-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
149 lines (133 loc) · 6.31 KB
/
Copy pathscript.js
File metadata and controls
149 lines (133 loc) · 6.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
const DATA = {
title: 'SkrobalaM',
stack: ['C', 'CUDA', 'VHDL', 'Python', 'React Native', 'ESP32', 'Arduino', 'WebSocket'],
projects: [
{
label: 'Ascii Shader',
img: 'images/shader.png',
desc: 'ASCII shader running on the GPU using CUDA and C. It maps downscaled pixel luminance from a video to ASCII characters.',
github: 'https://github.com/SkrobalaM/ascii-shader',
href: ''
},
{
label: 'DES on Logisim',
img: 'images/DES.png',
desc: 'Implementation of the Data Encryption Standard (DES) using digital logic blocks in Logisim, simulating real wire connections. This project showcases key scheduling and the standard DES encryption rounds—originally developed at IBM and standardized by NIST.',
github: 'https://github.com/SkrobalaM/DES_Logisim',
href: ''
},
{
label: 'SionnaRT guide',
img: 'images/SionnaRT.png',
desc: 'Guide to getting started with the SionnaRT library for wireless simulations, performing ray tracing on a custom 3D map built from OpenStreetMap data.',
github: 'https://github.com/SkrobalaM/SionnaRT_guide',
href: ''
},
{
label: 'Berghain challenge by ListenLab',
desc: 'Challenge brief:\nYou are the bouncer at a nightclub. Fill the venue with N=1000 people while satisfying constraints like "at least 40% Berlin locals" and "at least 80% wearing all black." People arrive one by one, and you must immediately decide whether to let them in or turn them away. The challenge is to meet all minimum requirements with as few rejections as possible.\n\nThis project uses stochastic decisions to find an optimal solution based on constraints, initial probabilities, and correlations.\n\nFinal ranking achieved: 68/1330.',
github: 'https://github.com/SkrobalaM/Berghain_Challenge',
href: ''
},
{
label: 'ESP32 Speech-to-Text',
img: 'images/microphone_wiring.png',
desc: 'Real-time speech-to-text solution using ESP32 microcontroller with I2S microphone, Python WebSocket server, and Google Cloud Speech-to-Text API. The system streams audio from the ESP32 to a local server for cloud-based transcription, eliminating the need for local memory storage on the microcontroller.\n\nFeatures:\n• I2S microphone integration on ESP32\n• Real-time audio streaming via WebSocket\n• Google Cloud Speech-to-Text API integration\n• Interim and final transcription results\n• Wi-Fi connectivity for remote operation',
github: 'https://github.com/SkrobalaM/ESP32_speech_to_text',
href: ''
},
],
working: [
{ label: 'Minimalist PDF scanner mobile app in React Native' },
],
};
// Render helpers
function el(tag, attrs = {}, children = []) {
const node = document.createElement(tag);
Object.entries(attrs).forEach(([k, v]) => node.setAttribute(k, v));
(Array.isArray(children) ? children : [children]).forEach(c => {
if (c == null) return;
node.appendChild(typeof c === 'string' ? document.createTextNode(c) : c);
});
return node;
}
let PROJECT_UID = 0;
function renderList(list, targetId) {
const ul = document.getElementById(targetId);
ul.innerHTML = '';
list.forEach(item => {
const li = el('li');
// If item has any extended content, build a collapsible block
if (item.img || item.desc) {
const wrapper = el('div', { class: 'project' });
// Toggle row (title)
const detailsId = `project-details-${PROJECT_UID++}`;
const toggle = el('button', {
class: 'project-toggle',
'aria-expanded': 'false',
'aria-controls': detailsId,
type: 'button'
});
const titleNode = item.href
? el('a', { href: item.href, target: '_blank', rel: 'noopener', class: 'project-title' }, item.label)
: el('span', { class: 'project-title' }, item.label);
toggle.appendChild(titleNode);
if (item.meta) toggle.appendChild(el('span', { class: 'meta' }, ` — ${item.meta}`));
// Subtle inline hint to educate the interaction (removed on first open)
const hint = el('span', { class: 'toggle-hint' }, 'Info');
toggle.appendChild(hint);
// Details area (hidden by default)
const details = el('div', { class: 'project-details', id: detailsId });
// Stack image and description under the title (indented by the tree li)
if (item.img) {
const img = el('img', {
class: 'project-thumb',
src: item.img,
alt: item.label
});
img.addEventListener('error', () => { img.style.display = 'none'; });
details.appendChild(img);
}
if (item.desc) details.appendChild(el('p', { class: 'project-desc' }, item.desc));
if (item.github) {
details.appendChild(
el('a', { class: 'project-link', href: item.github, target: '_blank', rel: 'noopener' }, 'View on GitHub')
);
}
toggle.addEventListener('click', () => {
const isOpen = wrapper.classList.toggle('open');
toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
if (hint && hint.parentNode) hint.remove();
});
wrapper.appendChild(toggle);
wrapper.appendChild(details);
li.appendChild(wrapper);
} else {
// Simple inline item without collapsible details
if (item.href) {
li.appendChild(el('a', { href: item.href, target: '_blank', rel: 'noopener' }, item.label));
if (item.meta) li.appendChild(el('span', { class: 'meta' }, ` — ${item.meta}`));
} else {
li.appendChild(document.createTextNode(item.label));
}
}
ul.appendChild(li);
});
}
// Apply content
document.getElementById('siteTitle').textContent = DATA.title;
document.getElementById('year').textContent = new Date().getFullYear();
const stack = document.getElementById('techStack');
DATA.stack.forEach(s => stack.appendChild(el('li', {}, s)));
renderList(DATA.projects, 'projects');
renderList(DATA.working, 'wip');
function updateHeaderOffset(){
const header = document.querySelector('.site-header');
if (!header) return;
const h = header.getBoundingClientRect().height;
document.documentElement.style.setProperty('--header-h', `${Math.ceil(h)}px`);
}
window.addEventListener('load', updateHeaderOffset);
window.addEventListener('resize', updateHeaderOffset);
// Small delay in case fonts load late and change height
setTimeout(updateHeaderOffset, 100);