@anglefeint/astro-theme 0.1.14 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anglefeint/astro-theme",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "type": "module",
5
5
  "description": "Anglefeint core theme package for Astro",
6
6
  "keywords": [
@@ -0,0 +1,150 @@
1
+ export function initTerminalBackground(runtimeConfig, prefersReducedMotion) {
2
+ var bgCanvas = document.querySelector('.hacker-bg-canvas');
3
+ if (!bgCanvas) return;
4
+
5
+ var bgCtx = bgCanvas.getContext('2d');
6
+ var fontSize = 13;
7
+ var lineHeight = 18;
8
+ var bgAnimationId = 0;
9
+ var fallbackDirLines = [
10
+ '~ $ ls -la',
11
+ 'total 42',
12
+ 'drwxr-xr-x 12 void staff 384 Jan 12 about blog projects',
13
+ 'drwxr-xr-x 8 void staff 256 Jan 11 .config .ssh keys',
14
+ '-rw-r--r-- 1 void staff 2048 Jan 10 README.md .env.gpg',
15
+ '-rwxr-xr-x 1 void staff 512 Jan 9 deploy.sh hack',
16
+ '~ $ cat .motd',
17
+ '>> welcome to the void | access granted',
18
+ ];
19
+ var dirLines = runtimeConfig.effects && Array.isArray(runtimeConfig.effects.backgroundLines) && runtimeConfig.effects.backgroundLines.length > 0
20
+ ? runtimeConfig.effects.backgroundLines
21
+ : fallbackDirLines;
22
+ var hackerFocused = false;
23
+ var hackerInput = '';
24
+ var hackerHistory = [];
25
+
26
+ function sizeBackground() {
27
+ var dpr = Math.min(window.devicePixelRatio || 1, 2);
28
+ bgCanvas.width = window.innerWidth * dpr;
29
+ bgCanvas.height = window.innerHeight * dpr;
30
+ bgCanvas.style.width = window.innerWidth + 'px';
31
+ bgCanvas.style.height = window.innerHeight + 'px';
32
+ bgCtx.scale(dpr, dpr);
33
+ }
34
+
35
+ function renderBg(t) {
36
+ var w = window.innerWidth;
37
+ var h = window.innerHeight;
38
+ var dpr = Math.min(window.devicePixelRatio || 1, 2);
39
+
40
+ bgCtx.setTransform(dpr, 0, 0, dpr, 0, 0);
41
+ bgCtx.clearRect(0, 0, w, h);
42
+ bgCtx.fillStyle = '#0a0a0a';
43
+ bgCtx.fillRect(0, 0, w, h);
44
+
45
+ bgCtx.font = fontSize + 'px ui-monospace, SFMono-Regular, Menlo, monospace';
46
+ var padX = 24;
47
+ var baseY = 22;
48
+
49
+ for (var i = 0; i < dirLines.length; i++) {
50
+ var line = dirLines[i];
51
+ if (line.indexOf('~ $') === 0) {
52
+ bgCtx.fillStyle = 'rgba(255, 255, 255, 0.9)';
53
+ } else if (line.indexOf('>>') === 0) {
54
+ bgCtx.fillStyle = 'rgba(255, 255, 255, 0.75)';
55
+ } else if (line.indexOf('total') === 0 || line.indexOf('drwx') === 0 || line.indexOf('-rw') === 0) {
56
+ bgCtx.fillStyle = 'rgba(255, 255, 255, 0.55)';
57
+ } else {
58
+ bgCtx.fillStyle = 'rgba(255, 255, 255, 0.6)';
59
+ }
60
+ bgCtx.fillText(line, padX, baseY + i * lineHeight);
61
+ }
62
+
63
+ var promptY = baseY + dirLines.length * lineHeight + 10;
64
+
65
+ for (var j = 0; j < hackerHistory.length; j++) {
66
+ bgCtx.fillStyle = 'rgba(255, 255, 255, 0.9)';
67
+ bgCtx.fillText('~ $ ' + hackerHistory[j], padX, promptY + j * lineHeight);
68
+ }
69
+
70
+ var currentY = promptY + hackerHistory.length * lineHeight;
71
+ bgCtx.fillStyle = 'rgba(255, 255, 255, 0.9)';
72
+ bgCtx.fillText('~ $ ', padX, currentY);
73
+ var promptW = bgCtx.measureText('~ $ ').width;
74
+ bgCtx.fillText(hackerInput, padX + promptW, currentY);
75
+ var inputW = bgCtx.measureText(hackerInput).width;
76
+ var blink = Math.floor(t / 530) % 2;
77
+ if (blink && hackerFocused) {
78
+ bgCtx.fillStyle = 'rgba(0, 255, 100, 0.9)';
79
+ bgCtx.fillRect(padX + promptW + inputW, currentY - fontSize + 4, 8, fontSize - 2);
80
+ } else if (!hackerFocused && blink) {
81
+ bgCtx.fillStyle = 'rgba(0, 255, 100, 0.9)';
82
+ bgCtx.fillRect(padX + promptW + inputW, currentY - fontSize + 4, 8, fontSize - 2);
83
+ }
84
+
85
+ bgAnimationId = requestAnimationFrame(renderBg);
86
+ }
87
+
88
+ function startBackgroundLoop() {
89
+ if (bgAnimationId || document.hidden) return;
90
+ bgAnimationId = requestAnimationFrame(renderBg);
91
+ }
92
+
93
+ function stopBackgroundLoop() {
94
+ if (!bgAnimationId) return;
95
+ cancelAnimationFrame(bgAnimationId);
96
+ bgAnimationId = 0;
97
+ }
98
+
99
+ sizeBackground();
100
+
101
+ if (prefersReducedMotion) {
102
+ renderBg(performance.now());
103
+ stopBackgroundLoop();
104
+ } else {
105
+ startBackgroundLoop();
106
+ document.addEventListener('visibilitychange', function() {
107
+ if (document.hidden) {
108
+ stopBackgroundLoop();
109
+ return;
110
+ }
111
+ startBackgroundLoop();
112
+ });
113
+ }
114
+
115
+ document.addEventListener('click', function(e) {
116
+ var content = document.querySelector('.about-shell');
117
+ var header = document.querySelector('header');
118
+ var footer = document.querySelector('footer');
119
+ if (content && content.contains(e.target)) {
120
+ hackerFocused = false;
121
+ } else if (header && header.contains(e.target)) {
122
+ hackerFocused = false;
123
+ } else if (footer && footer.contains(e.target)) {
124
+ hackerFocused = false;
125
+ } else if (e.target.closest('.hacker-back-to-top, .hacker-regenerate')) {
126
+ hackerFocused = false;
127
+ } else {
128
+ hackerFocused = true;
129
+ }
130
+ });
131
+
132
+ document.addEventListener('keydown', function(e) {
133
+ if (!hackerFocused) return;
134
+ if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
135
+ e.preventDefault();
136
+ if (e.key === 'Enter') {
137
+ hackerHistory.push(hackerInput);
138
+ hackerInput = '';
139
+ if (hackerHistory.length > 8) hackerHistory.shift();
140
+ } else if (e.key === 'Backspace') {
141
+ hackerInput = hackerInput.slice(0, -1);
142
+ } else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey && hackerInput.length < 80) {
143
+ hackerInput += e.key;
144
+ }
145
+ });
146
+
147
+ window.addEventListener('resize', function() {
148
+ sizeBackground();
149
+ }, { passive: true });
150
+ }
@@ -0,0 +1,101 @@
1
+ export function initAboutInteractions(prefersReducedMotion) {
2
+ var glow = document.querySelector('.hacker-mouse-glow');
3
+ if (glow && !prefersReducedMotion) {
4
+ var glowRaf;
5
+ var mx = 0;
6
+ var my = 0;
7
+ document.addEventListener('mousemove', function(e) {
8
+ mx = e.clientX;
9
+ my = e.clientY;
10
+ if (!glowRaf) {
11
+ glowRaf = requestAnimationFrame(function() {
12
+ glow.style.setProperty('--mouse-x', mx + 'px');
13
+ glow.style.setProperty('--mouse-y', my + 'px');
14
+ glowRaf = 0;
15
+ });
16
+ }
17
+ });
18
+ }
19
+
20
+ var paras = document.querySelectorAll('.hacker-body p, .hacker-body h2, .hacker-body blockquote, .about-manifest');
21
+ if (window.IntersectionObserver) {
22
+ var io = new IntersectionObserver(function(entries) {
23
+ entries.forEach(function(entry) {
24
+ if (entry.isIntersecting) {
25
+ entry.target.classList.add('hacker-visible');
26
+ io.unobserve(entry.target);
27
+ }
28
+ });
29
+ }, { rootMargin: '0px 0px -60px 0px', threshold: 0.1 });
30
+ paras.forEach(function(p) { io.observe(p); });
31
+ } else {
32
+ paras.forEach(function(p) { p.classList.add('hacker-visible'); });
33
+ }
34
+
35
+ var titles = document.querySelectorAll('.about-section-title');
36
+ if (prefersReducedMotion && titles.length) {
37
+ titles.forEach(function(el) {
38
+ var fullText = el.textContent || '';
39
+ el.setAttribute('data-full-text', fullText);
40
+ el.textContent = fullText;
41
+ el.style.minHeight = '1.2em';
42
+ });
43
+ } else if (window.IntersectionObserver && titles.length) {
44
+ titles.forEach(function(el) {
45
+ var fullText = el.textContent || '';
46
+ el.setAttribute('data-full-text', fullText);
47
+ el.textContent = '';
48
+ el.style.minHeight = '1.2em';
49
+ });
50
+
51
+ var tio = new IntersectionObserver(function(entries) {
52
+ entries.forEach(function(entry) {
53
+ if (entry.isIntersecting) {
54
+ tio.unobserve(entry.target);
55
+ var text = entry.target.getAttribute('data-full-text') || '';
56
+ var i = 0;
57
+ entry.target.textContent = '';
58
+ function type() {
59
+ if (i <= text.length) {
60
+ entry.target.textContent = text.slice(0, i);
61
+ i++;
62
+ setTimeout(type, 32);
63
+ }
64
+ }
65
+ type();
66
+ }
67
+ });
68
+ }, { rootMargin: '0px 0px -40px 0px', threshold: 0.1 });
69
+
70
+ titles.forEach(function(el) { tio.observe(el); });
71
+ }
72
+
73
+ var regen = document.querySelector('.hacker-regenerate');
74
+ var article = document.querySelector('.about-shell');
75
+ var scan = document.querySelector('.hacker-load-scan');
76
+ if (regen && article) {
77
+ regen.addEventListener('click', function() {
78
+ if (prefersReducedMotion) {
79
+ article.classList.add('hacker-flash');
80
+ setTimeout(function() {
81
+ article.classList.remove('hacker-flash');
82
+ }, 120);
83
+ return;
84
+ }
85
+
86
+ regen.disabled = true;
87
+ article.classList.add('hacker-flash');
88
+ if (scan) {
89
+ scan.style.animation = 'none';
90
+ scan.offsetHeight;
91
+ scan.style.animation = 'hacker-scan 0.8s ease-out forwards';
92
+ scan.style.top = '0';
93
+ scan.style.opacity = '1';
94
+ }
95
+ setTimeout(function() {
96
+ article.classList.remove('hacker-flash');
97
+ regen.disabled = false;
98
+ }, 1200);
99
+ });
100
+ }
101
+ }
@@ -0,0 +1,347 @@
1
+ function randHex() {
2
+ var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
3
+ return chars[Math.floor(Math.random() * chars.length)];
4
+ }
5
+
6
+ function randKeyLine(pairs) {
7
+ var s = [];
8
+ for (var i = 0; i < pairs; i++) s.push(randHex() + randHex());
9
+ return s.join(' ');
10
+ }
11
+
12
+ function randPass() {
13
+ var s = '';
14
+ for (var i = 0; i < 6; i++) s += randHex().toLowerCase();
15
+ s += '@' + randHex() + randHex() + randHex() + randHex() + randHex();
16
+ return s;
17
+ }
18
+
19
+ export function initAboutModals(runtimeConfig, prefersReducedMotion) {
20
+ var modalOverlay = document.getElementById('hacker-modal');
21
+ var modalBody = document.getElementById('hacker-modal-body');
22
+ var modalTitle = document.querySelector('.hacker-modal-title');
23
+ if (!modalOverlay || !modalBody || !modalTitle) return;
24
+
25
+ var decryptorKeysLabel = runtimeConfig.decryptorKeysLabel || 'keys tested';
26
+ var decryptorInterval = null;
27
+ var helpCharCount = 0;
28
+
29
+ function startDecryptorFlash() {
30
+ if (prefersReducedMotion) return;
31
+ if (decryptorInterval) clearInterval(decryptorInterval);
32
+ var keys = 0;
33
+ var sec = 1;
34
+ decryptorInterval = setInterval(function() {
35
+ if (!modalOverlay.classList.contains('open')) {
36
+ clearInterval(decryptorInterval);
37
+ decryptorInterval = null;
38
+ return;
39
+ }
40
+ keys += 1 + Math.floor(Math.random() * 3);
41
+ sec = Math.min(59, Math.floor(keys / 15) + 1);
42
+ var el = document.getElementById('dec-keys');
43
+ if (el) el.textContent = '[00:00:' + String(sec).padStart(2, '0') + '] ' + keys + ' ' + decryptorKeysLabel;
44
+ el = document.getElementById('dec-pass');
45
+ if (el) el.textContent = randPass();
46
+ el = document.getElementById('dec-master1');
47
+ if (el) el.textContent = randKeyLine(8);
48
+ el = document.getElementById('dec-master2');
49
+ if (el) el.textContent = randKeyLine(8);
50
+ el = document.getElementById('dec-trans1');
51
+ if (el) el.textContent = randKeyLine(7);
52
+ el = document.getElementById('dec-trans2');
53
+ if (el) el.textContent = randKeyLine(7);
54
+ el = document.getElementById('dec-trans3');
55
+ if (el) el.textContent = randKeyLine(8);
56
+ el = document.getElementById('dec-trans4');
57
+ if (el) el.textContent = randKeyLine(8);
58
+ }, 180);
59
+ }
60
+
61
+ function buildHelpKeyboard() {
62
+ var keyboardConfig = runtimeConfig.keyboard || {};
63
+ var statsLabel = keyboardConfig.statsLabel || 'Stats & Achievements';
64
+ var typedPrefix = keyboardConfig.typedPrefix || 'You typed:';
65
+ var typedSuffix = keyboardConfig.typedSuffix || 'characters';
66
+ var rows = [
67
+ ['`', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 'Backspace'],
68
+ ['Tab', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '[', ']'],
69
+ ['CapsLock', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', "'", 'Enter'],
70
+ ['Shift', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '/', 'ShiftRight'],
71
+ ['Ctrl', 'Alt', 'Space', 'AltRight', 'CtrlRight'],
72
+ ];
73
+ var codeMap = {
74
+ '`': 'Backquote',
75
+ '1': 'Digit1',
76
+ '2': 'Digit2',
77
+ '3': 'Digit3',
78
+ '4': 'Digit4',
79
+ '5': 'Digit5',
80
+ '6': 'Digit6',
81
+ '7': 'Digit7',
82
+ '8': 'Digit8',
83
+ '9': 'Digit9',
84
+ '0': 'Digit0',
85
+ '-': 'Minus',
86
+ '=': 'Equal',
87
+ Backspace: 'Backspace',
88
+ Tab: 'Tab',
89
+ Q: 'KeyQ',
90
+ W: 'KeyW',
91
+ E: 'KeyE',
92
+ R: 'KeyR',
93
+ T: 'KeyT',
94
+ Y: 'KeyY',
95
+ U: 'KeyU',
96
+ I: 'KeyI',
97
+ O: 'KeyO',
98
+ P: 'KeyP',
99
+ '[': 'BracketLeft',
100
+ ']': 'BracketRight',
101
+ CapsLock: 'CapsLock',
102
+ A: 'KeyA',
103
+ S: 'KeyS',
104
+ D: 'KeyD',
105
+ F: 'KeyF',
106
+ G: 'KeyG',
107
+ H: 'KeyH',
108
+ J: 'KeyJ',
109
+ K: 'KeyK',
110
+ L: 'KeyL',
111
+ ';': 'Semicolon',
112
+ "'": 'Quote',
113
+ Enter: 'Enter',
114
+ Shift: 'ShiftLeft',
115
+ ShiftRight: 'ShiftRight',
116
+ Z: 'KeyZ',
117
+ X: 'KeyX',
118
+ C: 'KeyC',
119
+ V: 'KeyV',
120
+ B: 'KeyB',
121
+ N: 'KeyN',
122
+ M: 'KeyM',
123
+ ',': 'Comma',
124
+ '.': 'Period',
125
+ '/': 'Slash',
126
+ Ctrl: 'ControlLeft',
127
+ CtrlRight: 'ControlRight',
128
+ Alt: 'AltLeft',
129
+ AltRight: 'AltRight',
130
+ Space: 'Space',
131
+ };
132
+
133
+ var html = '<div class="hacker-vkeyboard-wrap" id="help-keyboard">';
134
+ html += '<div class="hacker-vkeyboard hacker-vkeyboard-main">';
135
+ rows.forEach(function(row) {
136
+ html += '<div class="hacker-vkeyboard-row">';
137
+ row.forEach(function(k) {
138
+ var code = codeMap[k] || k;
139
+ var cls = 'hacker-vkey';
140
+ if (k === 'Tab' || k === 'CapsLock' || k === 'Enter') cls += ' wide';
141
+ if (k === 'Space') cls += ' space';
142
+ if (k === 'Backspace') cls += ' acc backspace';
143
+ var label =
144
+ k === 'Space'
145
+ ? '&nbsp;'
146
+ : k === 'ShiftRight'
147
+ ? 'Shift'
148
+ : k === 'CtrlRight'
149
+ ? 'Ctrl'
150
+ : k === 'AltRight'
151
+ ? 'Alt'
152
+ : k === 'Backspace'
153
+ ? 'Back ⌫'
154
+ : k;
155
+ html +=
156
+ '<span class="' +
157
+ cls +
158
+ '" data-code="' +
159
+ code +
160
+ '" data-key="' +
161
+ (k === 'ShiftRight' ? 'Shift' : k === 'CtrlRight' ? 'Ctrl' : k === 'AltRight' ? 'Alt' : k === 'Backspace' ? 'Backspace' : k).replace("'", "\\'") +
162
+ '"' +
163
+ (k === 'Backspace' ? ' title="Backspace or ESC"' : '') +
164
+ '>' +
165
+ label +
166
+ '</span>';
167
+ });
168
+ html += '</div>';
169
+ });
170
+ html += '</div>';
171
+ html += '<div class="hacker-vkeyboard-side">';
172
+ html += '<div class="hacker-vkeyboard-side-block">';
173
+ html += '<div class="hacker-vkeyboard-side-row"><span class="hacker-vkey" data-code="Insert" data-key="Ins">Insert</span><span class="hacker-vkey nav-home" data-code="Home" data-key="Home">Home</span><span class="hacker-vkey" data-code="PageUp" data-key="PgUp">PgUp</span></div>';
174
+ html += '<div class="hacker-vkeyboard-side-row"><span class="hacker-vkey" data-code="Delete" data-key="Del">Delete</span><span class="hacker-vkey nav-end" data-code="End" data-key="End">End</span><span class="hacker-vkey" data-code="PageDown" data-key="PgDn">PgDn</span></div>';
175
+ html += '</div>';
176
+ html += '<div class="hacker-vkeyboard-side-row"><span class="hacker-vkey" data-code="Purge" data-key="Purge">Purge</span></div>';
177
+ html += '<div class="hacker-vkeyboard-arrows-wrap">';
178
+ html += '<div class="hacker-vkeyboard-arrows">';
179
+ html += '<span class="hacker-vkey arr-u" data-code="ArrowUp" data-key="↑">↑</span>';
180
+ html += '<span class="hacker-vkey arr-l" data-code="ArrowLeft" data-key="←">←</span>';
181
+ html += '<span class="hacker-vkey arr-r" data-code="ArrowRight" data-key="→">→</span>';
182
+ html += '<span class="hacker-vkey arr-d" data-code="ArrowDown" data-key="↓">↓</span>';
183
+ html += '</div>';
184
+ html += '</div></div></div>';
185
+ html += '<div class="hacker-vkeyboard-stats"><span class="hacker-vkeyboard-stats-label">' + statsLabel + '</span><br>' + typedPrefix + ' <span id="help-char-count">0</span> ' + typedSuffix + '</div>';
186
+ return html;
187
+ }
188
+
189
+ function highlightKey(code) {
190
+ if (code === 'Escape') code = 'Backspace';
191
+ var el = modalBody.querySelector('.hacker-vkey[data-code="' + code + '"]');
192
+ if (!el) {
193
+ if (code === 'ShiftRight') el = modalBody.querySelector('.hacker-vkey[data-code="ShiftLeft"]');
194
+ else if (code === 'ControlRight') el = modalBody.querySelector('.hacker-vkey[data-code="ControlLeft"]');
195
+ else if (code === 'AltRight') el = modalBody.querySelector('.hacker-vkey[data-code="AltLeft"]');
196
+ }
197
+ if (el) {
198
+ el.classList.add('highlight');
199
+ setTimeout(function() {
200
+ el.classList.remove('highlight');
201
+ }, 150);
202
+ }
203
+ }
204
+
205
+ function initHelpKeyboard() {
206
+ helpCharCount = 0;
207
+ var charEl = document.getElementById('help-char-count');
208
+ if (charEl) charEl.textContent = '0';
209
+ modalBody.querySelectorAll('.hacker-vkey').forEach(function(k) {
210
+ k.addEventListener('click', function() {
211
+ var code = k.getAttribute('data-code');
212
+ highlightKey(code);
213
+ var key = k.getAttribute('data-key');
214
+ var navKeys = ['Shift', 'Ctrl', 'Alt', 'CapsLock', 'Tab', 'Enter', 'Backspace', 'Space', 'Ins', 'Home', 'PgUp', 'Del', 'End', 'PgDn', 'Purge', '↑', '↓', '←', '→'];
215
+ if (key && navKeys.indexOf(key) === -1) {
216
+ helpCharCount++;
217
+ charEl = document.getElementById('help-char-count');
218
+ if (charEl) charEl.textContent = helpCharCount;
219
+ } else if (key === 'Space') {
220
+ helpCharCount++;
221
+ charEl = document.getElementById('help-char-count');
222
+ if (charEl) charEl.textContent = helpCharCount;
223
+ }
224
+ });
225
+ });
226
+ }
227
+
228
+ function handleHelpKeydown(e) {
229
+ if (!modalOverlay.classList.contains('open') || !modalBody.classList.contains('hacker-modal-keyboard')) return;
230
+ if (e.key === 'Escape') return;
231
+ e.preventDefault();
232
+ highlightKey(e.code);
233
+ if (e.key.length === 1) {
234
+ helpCharCount++;
235
+ var charEl = document.getElementById('help-char-count');
236
+ if (charEl) charEl.textContent = helpCharCount;
237
+ }
238
+ }
239
+
240
+ var scriptsTpl = document.getElementById('hacker-scripts-folders-tpl');
241
+ var fallbackModalContent = {
242
+ 'dl-data': { title: 'Downloading...', body: '<div class="hacker-modal-download"><div class="modal-subtitle">Critical Data</div><div class="hacker-modal-progress" id="dl-progress"></div></div>', type: 'progress' },
243
+ ai: { title: 'AI', body: '<pre>~ $ model --status\n\ninference: stable\ncontext: 8k tokens\nlatency: &lt; 200ms\n\n&gt;&gt; system online</pre>' },
244
+ decryptor: { title: 'Password Decryptor', body: '<pre class="hacker-decryptor-pre">Calculating Hashes\n\n<span id="dec-keys">[00:00:01] 0 keys tested</span>\n\nCurrent passphrase: <span id="dec-pass">********</span>\n\nMaster key\n<span id="dec-master1"></span>\n<span id="dec-master2"></span>\n\nTransient key\n<span id="dec-trans1"></span>\n<span id="dec-trans2"></span>\n<span id="dec-trans3"></span>\n<span id="dec-trans4"></span></pre>', type: 'decryptor' },
245
+ help: { title: 'Help', body: '', type: 'keyboard' },
246
+ 'all-scripts': { title: '/root/bash/scripts', body: '', type: 'scripts' },
247
+ };
248
+
249
+ var modalContent = runtimeConfig.modalContent || fallbackModalContent;
250
+ document.querySelectorAll('.hacker-folder[data-modal]').forEach(function(btn) {
251
+ btn.addEventListener('click', function() {
252
+ var id = btn.getAttribute('data-modal');
253
+ var data = modalContent[id];
254
+ if (!data) return;
255
+
256
+ var modalEl = modalOverlay.querySelector('.hacker-modal');
257
+ if (modalEl) modalEl.classList.remove('hacker-modal-wide');
258
+ modalTitle.textContent = data.title;
259
+ modalBody.innerHTML = data.body;
260
+ modalBody.className =
261
+ 'hacker-modal-body' +
262
+ (data.type === 'progress' ? ' hacker-modal-download' : '') +
263
+ (data.type === 'keyboard' ? ' hacker-modal-keyboard' : '') +
264
+ (data.type === 'scripts' ? ' hacker-modal-scripts-wrap' : '');
265
+
266
+ if (data.type === 'progress') {
267
+ var bar = document.getElementById('dl-progress');
268
+ if (bar) {
269
+ bar.innerHTML = '';
270
+ for (var i = 0; i < 48; i++) bar.appendChild(document.createElement('span'));
271
+ var idx = 0;
272
+ function fillNext() {
273
+ if (idx < 48) {
274
+ bar.children[idx].classList.add('filled');
275
+ idx++;
276
+ setTimeout(fillNext, 80 + Math.random() * 60);
277
+ }
278
+ }
279
+ fillNext();
280
+ }
281
+ } else if (data.type === 'decryptor') {
282
+ var el = document.getElementById('dec-keys');
283
+ if (el) el.textContent = '[00:00:01] 0 ' + decryptorKeysLabel;
284
+ el = document.getElementById('dec-pass');
285
+ if (el) el.textContent = randPass();
286
+ el = document.getElementById('dec-master1');
287
+ if (el) el.textContent = randKeyLine(8);
288
+ el = document.getElementById('dec-master2');
289
+ if (el) el.textContent = randKeyLine(8);
290
+ el = document.getElementById('dec-trans1');
291
+ if (el) el.textContent = randKeyLine(7);
292
+ el = document.getElementById('dec-trans2');
293
+ if (el) el.textContent = randKeyLine(7);
294
+ el = document.getElementById('dec-trans3');
295
+ if (el) el.textContent = randKeyLine(8);
296
+ el = document.getElementById('dec-trans4');
297
+ if (el) el.textContent = randKeyLine(8);
298
+ startDecryptorFlash();
299
+ } else if (data.type === 'keyboard') {
300
+ modalBody.innerHTML = buildHelpKeyboard();
301
+ if (modalEl) modalEl.classList.add('hacker-modal-wide');
302
+ initHelpKeyboard();
303
+ document.addEventListener('keydown', handleHelpKeydown);
304
+ } else if (data.type === 'scripts' && scriptsTpl) {
305
+ modalBody.innerHTML = '';
306
+ if ('content' in scriptsTpl && scriptsTpl.content) {
307
+ modalBody.appendChild(scriptsTpl.content.cloneNode(true));
308
+ } else {
309
+ modalBody.appendChild(scriptsTpl.cloneNode(true));
310
+ var cloned = modalBody.querySelector('#hacker-scripts-folders-tpl');
311
+ if (cloned) {
312
+ cloned.removeAttribute('id');
313
+ cloned.hidden = false;
314
+ cloned.removeAttribute('aria-hidden');
315
+ }
316
+ }
317
+ if (modalEl) modalEl.classList.add('hacker-modal-wide');
318
+ }
319
+
320
+ modalOverlay.classList.add('open');
321
+ modalOverlay.setAttribute('aria-hidden', 'false');
322
+ });
323
+ });
324
+
325
+ function closeModal() {
326
+ if (decryptorInterval) {
327
+ clearInterval(decryptorInterval);
328
+ decryptorInterval = null;
329
+ }
330
+ document.removeEventListener('keydown', handleHelpKeydown);
331
+ var modalEl = modalOverlay.querySelector('.hacker-modal');
332
+ if (modalEl) modalEl.classList.remove('hacker-modal-wide');
333
+ modalOverlay.classList.remove('open');
334
+ modalOverlay.setAttribute('aria-hidden', 'true');
335
+ }
336
+
337
+ var closeButton = document.querySelector('.hacker-modal-close');
338
+ if (closeButton) closeButton.addEventListener('click', closeModal);
339
+
340
+ modalOverlay.addEventListener('click', function(e) {
341
+ if (e.target === modalOverlay) closeModal();
342
+ });
343
+
344
+ document.addEventListener('keydown', function(e) {
345
+ if (e.key === 'Escape' && modalOverlay.classList.contains('open')) closeModal();
346
+ });
347
+ }
@@ -0,0 +1,60 @@
1
+ export function initAboutReadingUi(runtimeConfig, prefersReducedMotion) {
2
+ var progress = document.querySelector('.hacker-progress');
3
+ var toast = document.querySelector('.hacker-toast');
4
+ var fallbackToasts = {
5
+ p30: 'context parsed',
6
+ p60: 'inference stable',
7
+ p90: 'output finalized',
8
+ };
9
+ var toastMessages = runtimeConfig.effects && runtimeConfig.effects.scrollToasts
10
+ ? runtimeConfig.effects.scrollToasts
11
+ : fallbackToasts;
12
+ var stageSeen = { p30: false, p60: false, p90: false };
13
+ var toastTimer = 0;
14
+ var hasScrolled = false;
15
+
16
+ function showToast(msg) {
17
+ if (!toast) return;
18
+ toast.textContent = '> ' + msg;
19
+ toast.classList.add('visible');
20
+ clearTimeout(toastTimer);
21
+ toastTimer = setTimeout(function() {
22
+ toast.classList.remove('visible');
23
+ }, 900);
24
+ }
25
+
26
+ if (progress) {
27
+ function onScroll() {
28
+ var scrollTop = window.scrollY || document.documentElement.scrollTop;
29
+ var scrollHeight = document.documentElement.scrollHeight - window.innerHeight;
30
+ var p = scrollHeight > 0 ? Math.min(1, scrollTop / scrollHeight) : 1;
31
+ progress.style.setProperty('--read-progress', String(p));
32
+ var btn = document.querySelector('.hacker-back-to-top');
33
+ if (btn) btn.classList.toggle('visible', scrollTop > 400);
34
+ if (!hasScrolled && scrollTop > 6) hasScrolled = true;
35
+ if (!hasScrolled) return;
36
+ if (!stageSeen.p30 && p >= 0.3) {
37
+ stageSeen.p30 = true;
38
+ showToast(toastMessages.p30 || fallbackToasts.p30);
39
+ }
40
+ if (!stageSeen.p60 && p >= 0.6) {
41
+ stageSeen.p60 = true;
42
+ showToast(toastMessages.p60 || fallbackToasts.p60);
43
+ }
44
+ if (!stageSeen.p90 && p >= 0.9) {
45
+ stageSeen.p90 = true;
46
+ showToast(toastMessages.p90 || fallbackToasts.p90);
47
+ }
48
+ }
49
+
50
+ onScroll();
51
+ window.addEventListener('scroll', onScroll, { passive: true });
52
+ }
53
+
54
+ var backTop = document.querySelector('.hacker-back-to-top');
55
+ if (backTop) {
56
+ backTop.addEventListener('click', function() {
57
+ window.scrollTo({ top: 0, behavior: prefersReducedMotion ? 'auto' : 'smooth' });
58
+ });
59
+ }
60
+ }