@anglefeint/astro-theme 0.1.13 → 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 +2 -4
- package/src/layouts/BlogPost.astro +2 -1
- package/src/layouts/shells/AiShell.astro +2 -1
- package/src/layouts/shells/CyberShell.astro +4 -1
- package/src/layouts/shells/HackerShell.astro +2 -1
- package/src/layouts/shells/MatrixShell.astro +2 -1
- package/src/scripts/about/background.js +150 -0
- package/src/scripts/about/interactions.js +101 -0
- package/src/scripts/about/modals.js +347 -0
- package/src/scripts/about/reading-ui.js +60 -0
- package/src/scripts/about/runtime.js +20 -0
- package/src/scripts/about-effects.js +12 -551
- package/src/scripts/blogpost/hero-canvas.js +253 -0
- package/src/scripts/blogpost/interactions.js +73 -0
- package/src/scripts/blogpost/network-canvas.js +117 -0
- package/src/scripts/blogpost/read-progress.js +52 -0
- package/src/scripts/blogpost/red-queen-tv.js +604 -0
- package/src/scripts/blogpost-effects.js +20 -1084
- /package/{public → src}/styles/about-page.css +0 -0
- /package/{public → src}/styles/blog-list.css +0 -0
- /package/{public → src}/styles/blog-post.css +0 -0
- /package/{public → src}/styles/home-page.css +0 -0
- /package/{public → src}/styles/theme-ai.css +0 -0
- /package/{public → src}/styles/theme-cyber.css +0 -0
|
@@ -1,553 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
runtimeConfig = JSON.parse(runtimeConfigEl.textContent);
|
|
7
|
-
} catch (_err) {
|
|
8
|
-
runtimeConfig = {};
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
var prefersReducedMotion = false;
|
|
12
|
-
try {
|
|
13
|
-
prefersReducedMotion = !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches);
|
|
14
|
-
} catch (_e) {
|
|
15
|
-
prefersReducedMotion = false;
|
|
16
|
-
}
|
|
17
|
-
// ── Terminal background: dir + 可输入 (点击背景聚焦,回车仅换行) ──
|
|
18
|
-
var bgCanvas = document.querySelector('.hacker-bg-canvas');
|
|
19
|
-
if (bgCanvas) {
|
|
20
|
-
var bgCtx = bgCanvas.getContext('2d');
|
|
21
|
-
var fontSize = 13;
|
|
22
|
-
var lineHeight = 18;
|
|
23
|
-
var bgAnimationId = 0;
|
|
24
|
-
var fallbackDirLines = [
|
|
25
|
-
'~ $ ls -la',
|
|
26
|
-
'total 42',
|
|
27
|
-
'drwxr-xr-x 12 void staff 384 Jan 12 about blog projects',
|
|
28
|
-
'drwxr-xr-x 8 void staff 256 Jan 11 .config .ssh keys',
|
|
29
|
-
'-rw-r--r-- 1 void staff 2048 Jan 10 README.md .env.gpg',
|
|
30
|
-
'-rwxr-xr-x 1 void staff 512 Jan 9 deploy.sh hack',
|
|
31
|
-
'~ $ cat .motd',
|
|
32
|
-
'>> welcome to the void | access granted',
|
|
33
|
-
];
|
|
34
|
-
var dirLines = runtimeConfig.effects && Array.isArray(runtimeConfig.effects.backgroundLines) && runtimeConfig.effects.backgroundLines.length > 0
|
|
35
|
-
? runtimeConfig.effects.backgroundLines
|
|
36
|
-
: fallbackDirLines;
|
|
37
|
-
var hackerFocused = false;
|
|
38
|
-
var hackerInput = '';
|
|
39
|
-
var hackerHistory = [];
|
|
40
|
-
|
|
41
|
-
function sizeBackground() {
|
|
42
|
-
var dpr = Math.min(window.devicePixelRatio || 1, 2);
|
|
43
|
-
bgCanvas.width = window.innerWidth * dpr;
|
|
44
|
-
bgCanvas.height = window.innerHeight * dpr;
|
|
45
|
-
bgCanvas.style.width = window.innerWidth + 'px';
|
|
46
|
-
bgCanvas.style.height = window.innerHeight + 'px';
|
|
47
|
-
bgCtx.scale(dpr, dpr);
|
|
48
|
-
}
|
|
49
|
-
sizeBackground();
|
|
50
|
-
|
|
51
|
-
function renderBg(t) {
|
|
52
|
-
var w = window.innerWidth;
|
|
53
|
-
var h = window.innerHeight;
|
|
54
|
-
var dpr = Math.min(window.devicePixelRatio || 1, 2);
|
|
55
|
-
|
|
56
|
-
bgCtx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
57
|
-
bgCtx.clearRect(0, 0, w, h);
|
|
58
|
-
bgCtx.fillStyle = '#0a0a0a';
|
|
59
|
-
bgCtx.fillRect(0, 0, w, h);
|
|
60
|
-
|
|
61
|
-
bgCtx.font = fontSize + 'px ui-monospace, SFMono-Regular, Menlo, monospace';
|
|
62
|
-
var padX = 24;
|
|
63
|
-
var baseY = 22;
|
|
64
|
-
|
|
65
|
-
// directory listing
|
|
66
|
-
for (var i = 0; i < dirLines.length; i++) {
|
|
67
|
-
var line = dirLines[i];
|
|
68
|
-
if (line.indexOf('~ $') === 0) {
|
|
69
|
-
bgCtx.fillStyle = 'rgba(255, 255, 255, 0.9)';
|
|
70
|
-
} else if (line.indexOf('>>') === 0) {
|
|
71
|
-
bgCtx.fillStyle = 'rgba(255, 255, 255, 0.75)';
|
|
72
|
-
} else if (line.indexOf('total') === 0 || line.indexOf('drwx') === 0 || line.indexOf('-rw') === 0) {
|
|
73
|
-
bgCtx.fillStyle = 'rgba(255, 255, 255, 0.55)';
|
|
74
|
-
} else {
|
|
75
|
-
bgCtx.fillStyle = 'rgba(255, 255, 255, 0.6)';
|
|
76
|
-
}
|
|
77
|
-
bgCtx.fillText(line, padX, baseY + i * lineHeight);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
var promptY = baseY + dirLines.length * lineHeight + 10;
|
|
81
|
-
|
|
82
|
-
// 用户输入历史 (回车产生的行)
|
|
83
|
-
for (var i = 0; i < hackerHistory.length; i++) {
|
|
84
|
-
bgCtx.fillStyle = 'rgba(255, 255, 255, 0.9)';
|
|
85
|
-
bgCtx.fillText('~ $ ' + hackerHistory[i], padX, promptY + i * lineHeight);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// 当前行: prompt + 输入 + 光标
|
|
89
|
-
var currentY = promptY + hackerHistory.length * lineHeight;
|
|
90
|
-
bgCtx.fillStyle = 'rgba(255, 255, 255, 0.9)';
|
|
91
|
-
bgCtx.fillText('~ $ ', padX, currentY);
|
|
92
|
-
var promptW = bgCtx.measureText('~ $ ').width;
|
|
93
|
-
bgCtx.fillText(hackerInput, padX + promptW, currentY);
|
|
94
|
-
var inputW = bgCtx.measureText(hackerInput).width;
|
|
95
|
-
var blink = Math.floor(t / 530) % 2;
|
|
96
|
-
if (blink && hackerFocused) {
|
|
97
|
-
bgCtx.fillStyle = 'rgba(0, 255, 100, 0.9)';
|
|
98
|
-
bgCtx.fillRect(padX + promptW + inputW, currentY - fontSize + 4, 8, fontSize - 2);
|
|
99
|
-
} else if (!hackerFocused && blink) {
|
|
100
|
-
bgCtx.fillStyle = 'rgba(0, 255, 100, 0.9)';
|
|
101
|
-
bgCtx.fillRect(padX + promptW + inputW, currentY - fontSize + 4, 8, fontSize - 2);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
bgAnimationId = requestAnimationFrame(renderBg);
|
|
105
|
-
}
|
|
106
|
-
function startBackgroundLoop() {
|
|
107
|
-
if (bgAnimationId || document.hidden) return;
|
|
108
|
-
bgAnimationId = requestAnimationFrame(renderBg);
|
|
109
|
-
}
|
|
110
|
-
function stopBackgroundLoop() {
|
|
111
|
-
if (!bgAnimationId) return;
|
|
112
|
-
cancelAnimationFrame(bgAnimationId);
|
|
113
|
-
bgAnimationId = 0;
|
|
114
|
-
}
|
|
115
|
-
if (prefersReducedMotion) {
|
|
116
|
-
renderBg(performance.now());
|
|
117
|
-
stopBackgroundLoop();
|
|
118
|
-
} else {
|
|
119
|
-
startBackgroundLoop();
|
|
120
|
-
document.addEventListener('visibilitychange', function() {
|
|
121
|
-
if (document.hidden) {
|
|
122
|
-
stopBackgroundLoop();
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
startBackgroundLoop();
|
|
126
|
-
});
|
|
127
|
-
}
|
|
1
|
+
import { initTerminalBackground } from './about/background.js';
|
|
2
|
+
import { initAboutInteractions } from './about/interactions.js';
|
|
3
|
+
import { initAboutModals } from './about/modals.js';
|
|
4
|
+
import { initAboutReadingUi } from './about/reading-ui.js';
|
|
5
|
+
import { isReducedMotion, readAboutRuntimeConfig } from './about/runtime.js';
|
|
128
6
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
} else if (header && header.contains(e.target)) {
|
|
137
|
-
hackerFocused = false;
|
|
138
|
-
} else if (footer && footer.contains(e.target)) {
|
|
139
|
-
hackerFocused = false;
|
|
140
|
-
} else if (e.target.closest('.hacker-back-to-top, .hacker-regenerate')) {
|
|
141
|
-
hackerFocused = false;
|
|
142
|
-
} else {
|
|
143
|
-
hackerFocused = true;
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// 键盘输入 (仅当聚焦时)
|
|
148
|
-
document.addEventListener('keydown', function(e) {
|
|
149
|
-
if (!hackerFocused) return;
|
|
150
|
-
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
|
151
|
-
e.preventDefault();
|
|
152
|
-
if (e.key === 'Enter') {
|
|
153
|
-
hackerHistory.push(hackerInput);
|
|
154
|
-
hackerInput = '';
|
|
155
|
-
if (hackerHistory.length > 8) hackerHistory.shift();
|
|
156
|
-
} else if (e.key === 'Backspace') {
|
|
157
|
-
hackerInput = hackerInput.slice(0, -1);
|
|
158
|
-
} else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey && hackerInput.length < 80) {
|
|
159
|
-
hackerInput += e.key;
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
window.addEventListener('resize', function() {
|
|
164
|
-
sizeBackground();
|
|
165
|
-
}, { passive: true });
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// ── Progress bar + toasts + back-to-top ──
|
|
169
|
-
var progress = document.querySelector('.hacker-progress');
|
|
170
|
-
var toast = document.querySelector('.hacker-toast');
|
|
171
|
-
var fallbackToasts = {
|
|
172
|
-
p30: 'context parsed',
|
|
173
|
-
p60: 'inference stable',
|
|
174
|
-
p90: 'output finalized',
|
|
175
|
-
};
|
|
176
|
-
var toastMessages = runtimeConfig.effects && runtimeConfig.effects.scrollToasts
|
|
177
|
-
? runtimeConfig.effects.scrollToasts
|
|
178
|
-
: fallbackToasts;
|
|
179
|
-
var stageSeen = { p30: false, p60: false, p90: false };
|
|
180
|
-
var toastTimer = 0;
|
|
181
|
-
var hasScrolled = false;
|
|
182
|
-
function showToast(msg) {
|
|
183
|
-
if (!toast) return;
|
|
184
|
-
toast.textContent = '> ' + msg;
|
|
185
|
-
toast.classList.add('visible');
|
|
186
|
-
clearTimeout(toastTimer);
|
|
187
|
-
toastTimer = setTimeout(function() {
|
|
188
|
-
toast.classList.remove('visible');
|
|
189
|
-
}, 900);
|
|
190
|
-
}
|
|
191
|
-
if (progress) {
|
|
192
|
-
function onScroll() {
|
|
193
|
-
var scrollTop = window.scrollY || document.documentElement.scrollTop;
|
|
194
|
-
var scrollHeight = document.documentElement.scrollHeight - window.innerHeight;
|
|
195
|
-
var p = scrollHeight > 0 ? Math.min(1, scrollTop / scrollHeight) : 1;
|
|
196
|
-
progress.style.setProperty('--read-progress', String(p));
|
|
197
|
-
var btn = document.querySelector('.hacker-back-to-top');
|
|
198
|
-
if (btn) btn.classList.toggle('visible', scrollTop > 400);
|
|
199
|
-
if (!hasScrolled && scrollTop > 6) hasScrolled = true;
|
|
200
|
-
if (!hasScrolled) return;
|
|
201
|
-
if (!stageSeen.p30 && p >= 0.3) {
|
|
202
|
-
stageSeen.p30 = true;
|
|
203
|
-
showToast(toastMessages.p30 || fallbackToasts.p30);
|
|
204
|
-
}
|
|
205
|
-
if (!stageSeen.p60 && p >= 0.6) {
|
|
206
|
-
stageSeen.p60 = true;
|
|
207
|
-
showToast(toastMessages.p60 || fallbackToasts.p60);
|
|
208
|
-
}
|
|
209
|
-
if (!stageSeen.p90 && p >= 0.9) {
|
|
210
|
-
stageSeen.p90 = true;
|
|
211
|
-
showToast(toastMessages.p90 || fallbackToasts.p90);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
onScroll();
|
|
215
|
-
window.addEventListener('scroll', onScroll, { passive: true });
|
|
216
|
-
}
|
|
217
|
-
var backTop = document.querySelector('.hacker-back-to-top');
|
|
218
|
-
if (backTop) {
|
|
219
|
-
backTop.addEventListener('click', function() {
|
|
220
|
-
window.scrollTo({ top: 0, behavior: prefersReducedMotion ? 'auto' : 'smooth' });
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// ── 文件夹弹窗 ──
|
|
225
|
-
var modalOverlay = document.getElementById('hacker-modal');
|
|
226
|
-
var modalBody = document.getElementById('hacker-modal-body');
|
|
227
|
-
var modalTitle = document.querySelector('.hacker-modal-title');
|
|
228
|
-
var decryptorKeysLabel = runtimeConfig.decryptorKeysLabel || 'keys tested';
|
|
229
|
-
var decryptorInterval = null;
|
|
230
|
-
function randHex() {
|
|
231
|
-
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
232
|
-
return chars[Math.floor(Math.random() * chars.length)];
|
|
233
|
-
}
|
|
234
|
-
function randKeyLine(pairs) {
|
|
235
|
-
var s = [];
|
|
236
|
-
for (var i = 0; i < pairs; i++) {
|
|
237
|
-
s.push(randHex() + randHex());
|
|
238
|
-
}
|
|
239
|
-
return s.join(' ');
|
|
240
|
-
}
|
|
241
|
-
function randPass() {
|
|
242
|
-
var s = '';
|
|
243
|
-
for (var i = 0; i < 6; i++) s += randHex().toLowerCase();
|
|
244
|
-
s += '@' + randHex() + randHex() + randHex() + randHex() + randHex();
|
|
245
|
-
return s;
|
|
246
|
-
}
|
|
247
|
-
function startDecryptorFlash() {
|
|
248
|
-
if (prefersReducedMotion) return;
|
|
249
|
-
if (decryptorInterval) clearInterval(decryptorInterval);
|
|
250
|
-
var keys = 0, sec = 1;
|
|
251
|
-
decryptorInterval = setInterval(function() {
|
|
252
|
-
if (!modalOverlay.classList.contains('open')) {
|
|
253
|
-
clearInterval(decryptorInterval);
|
|
254
|
-
decryptorInterval = null;
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
keys += 1 + Math.floor(Math.random() * 3);
|
|
258
|
-
sec = Math.min(59, Math.floor(keys / 15) + 1);
|
|
259
|
-
var el = document.getElementById('dec-keys');
|
|
260
|
-
if (el) el.textContent = '[00:00:' + String(sec).padStart(2, '0') + '] ' + keys + ' ' + decryptorKeysLabel;
|
|
261
|
-
el = document.getElementById('dec-pass');
|
|
262
|
-
if (el) el.textContent = randPass();
|
|
263
|
-
el = document.getElementById('dec-master1');
|
|
264
|
-
if (el) el.textContent = randKeyLine(8);
|
|
265
|
-
el = document.getElementById('dec-master2');
|
|
266
|
-
if (el) el.textContent = randKeyLine(8);
|
|
267
|
-
el = document.getElementById('dec-trans1');
|
|
268
|
-
if (el) el.textContent = randKeyLine(7);
|
|
269
|
-
el = document.getElementById('dec-trans2');
|
|
270
|
-
if (el) el.textContent = randKeyLine(7);
|
|
271
|
-
el = document.getElementById('dec-trans3');
|
|
272
|
-
if (el) el.textContent = randKeyLine(8);
|
|
273
|
-
el = document.getElementById('dec-trans4');
|
|
274
|
-
if (el) el.textContent = randKeyLine(8);
|
|
275
|
-
}, 180);
|
|
276
|
-
}
|
|
277
|
-
var helpCharCount = 0;
|
|
278
|
-
function buildHelpKeyboard() {
|
|
279
|
-
var keyboardConfig = runtimeConfig.keyboard || {};
|
|
280
|
-
var statsLabel = keyboardConfig.statsLabel || 'Stats & Achievements';
|
|
281
|
-
var typedPrefix = keyboardConfig.typedPrefix || 'You typed:';
|
|
282
|
-
var typedSuffix = keyboardConfig.typedSuffix || 'characters';
|
|
283
|
-
var rows = [
|
|
284
|
-
['`','1','2','3','4','5','6','7','8','9','0','-','=','Backspace'],
|
|
285
|
-
['Tab','Q','W','E','R','T','Y','U','I','O','P','[',']'],
|
|
286
|
-
['CapsLock','A','S','D','F','G','H','J','K','L',';',"'",'Enter'],
|
|
287
|
-
['Shift','Z','X','C','V','B','N','M',',','.','/','ShiftRight'],
|
|
288
|
-
['Ctrl','Alt','Space','AltRight','CtrlRight']
|
|
289
|
-
];
|
|
290
|
-
var codeMap = { '`':'Backquote','1':'Digit1','2':'Digit2','3':'Digit3','4':'Digit4','5':'Digit5','6':'Digit6','7':'Digit7','8':'Digit8','9':'Digit9','0':'Digit0','-':'Minus','=':'Equal','Backspace':'Backspace','Tab':'Tab','Q':'KeyQ','W':'KeyW','E':'KeyE','R':'KeyR','T':'KeyT','Y':'KeyY','U':'KeyU','I':'KeyI','O':'KeyO','P':'KeyP','[':'BracketLeft',']':'BracketRight','CapsLock':'CapsLock','A':'KeyA','S':'KeyS','D':'KeyD','F':'KeyF','G':'KeyG','H':'KeyH','J':'KeyJ','K':'KeyK','L':'KeyL',';':'Semicolon',"'":'Quote','Enter':'Enter','Shift':'ShiftLeft','ShiftRight':'ShiftRight','Z':'KeyZ','X':'KeyX','C':'KeyC','V':'KeyV','B':'KeyB','N':'KeyN','M':'KeyM',',':'Comma','.':'Period','/':'Slash','Ctrl':'ControlLeft','CtrlRight':'ControlRight','Alt':'AltLeft','AltRight':'AltRight','Space':'Space' };
|
|
291
|
-
var html = '<div class="hacker-vkeyboard-wrap" id="help-keyboard">';
|
|
292
|
-
html += '<div class="hacker-vkeyboard hacker-vkeyboard-main">';
|
|
293
|
-
rows.forEach(function(row) {
|
|
294
|
-
html += '<div class="hacker-vkeyboard-row">';
|
|
295
|
-
row.forEach(function(k) {
|
|
296
|
-
var code = codeMap[k] || k;
|
|
297
|
-
var cls = 'hacker-vkey';
|
|
298
|
-
if (k === 'Tab' || k === 'CapsLock' || k === 'Enter') cls += ' wide';
|
|
299
|
-
if (k === 'Space') cls += ' space';
|
|
300
|
-
if (k === 'Backspace') cls += ' acc backspace';
|
|
301
|
-
var label = k === 'Space' ? ' ' : (k === 'ShiftRight' ? 'Shift' : k === 'CtrlRight' ? 'Ctrl' : k === 'AltRight' ? 'Alt' : k === 'Backspace' ? 'Back ⌫' : k);
|
|
302
|
-
html += '<span class="' + cls + '" data-code="' + code + '" data-key="' + (k === 'ShiftRight' ? 'Shift' : k === 'CtrlRight' ? 'Ctrl' : k === 'AltRight' ? 'Alt' : k === 'Backspace' ? 'Backspace' : k).replace("'", "\\'") + '"' + (k === 'Backspace' ? ' title="Backspace or ESC"' : '') + '>' + label + '</span>';
|
|
303
|
-
});
|
|
304
|
-
html += '</div>';
|
|
305
|
-
});
|
|
306
|
-
html += '</div>';
|
|
307
|
-
html += '<div class="hacker-vkeyboard-side">';
|
|
308
|
-
html += '<div class="hacker-vkeyboard-side-block">';
|
|
309
|
-
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>';
|
|
310
|
-
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>';
|
|
311
|
-
html += '</div>';
|
|
312
|
-
html += '<div class="hacker-vkeyboard-side-row"><span class="hacker-vkey" data-code="Purge" data-key="Purge">Purge</span></div>';
|
|
313
|
-
html += '<div class="hacker-vkeyboard-arrows-wrap">';
|
|
314
|
-
html += '<div class="hacker-vkeyboard-arrows">';
|
|
315
|
-
html += '<span class="hacker-vkey arr-u" data-code="ArrowUp" data-key="↑">↑</span>';
|
|
316
|
-
html += '<span class="hacker-vkey arr-l" data-code="ArrowLeft" data-key="←">←</span>';
|
|
317
|
-
html += '<span class="hacker-vkey arr-r" data-code="ArrowRight" data-key="→">→</span>';
|
|
318
|
-
html += '<span class="hacker-vkey arr-d" data-code="ArrowDown" data-key="↓">↓</span>';
|
|
319
|
-
html += '</div>';
|
|
320
|
-
html += '</div></div></div>';
|
|
321
|
-
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>';
|
|
322
|
-
return html;
|
|
323
|
-
}
|
|
324
|
-
function highlightKey(code) {
|
|
325
|
-
if (code === 'Escape') code = 'Backspace';
|
|
326
|
-
var el = modalBody.querySelector('.hacker-vkey[data-code="' + code + '"]');
|
|
327
|
-
if (!el) {
|
|
328
|
-
if (code === 'ShiftRight') el = modalBody.querySelector('.hacker-vkey[data-code="ShiftLeft"]');
|
|
329
|
-
else if (code === 'ControlRight') el = modalBody.querySelector('.hacker-vkey[data-code="ControlLeft"]');
|
|
330
|
-
else if (code === 'AltRight') el = modalBody.querySelector('.hacker-vkey[data-code="AltLeft"]');
|
|
331
|
-
}
|
|
332
|
-
if (el) {
|
|
333
|
-
el.classList.add('highlight');
|
|
334
|
-
setTimeout(function() { el.classList.remove('highlight'); }, 150);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
function initHelpKeyboard() {
|
|
338
|
-
helpCharCount = 0;
|
|
339
|
-
var charEl = document.getElementById('help-char-count');
|
|
340
|
-
if (charEl) charEl.textContent = '0';
|
|
341
|
-
modalBody.querySelectorAll('.hacker-vkey').forEach(function(k) {
|
|
342
|
-
k.addEventListener('click', function() {
|
|
343
|
-
var code = k.getAttribute('data-code');
|
|
344
|
-
highlightKey(code);
|
|
345
|
-
var key = k.getAttribute('data-key');
|
|
346
|
-
var navKeys = ['Shift','Ctrl','Alt','CapsLock','Tab','Enter','Backspace','Space','Ins','Home','PgUp','Del','End','PgDn','Purge','↑','↓','←','→'];
|
|
347
|
-
if (key && navKeys.indexOf(key) === -1) {
|
|
348
|
-
helpCharCount++;
|
|
349
|
-
charEl = document.getElementById('help-char-count');
|
|
350
|
-
if (charEl) charEl.textContent = helpCharCount;
|
|
351
|
-
} else if (key === 'Space') {
|
|
352
|
-
helpCharCount++;
|
|
353
|
-
charEl = document.getElementById('help-char-count');
|
|
354
|
-
if (charEl) charEl.textContent = helpCharCount;
|
|
355
|
-
}
|
|
356
|
-
});
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
function handleHelpKeydown(e) {
|
|
360
|
-
if (!modalOverlay.classList.contains('open') || !modalBody.classList.contains('hacker-modal-keyboard')) return;
|
|
361
|
-
if (e.key === 'Escape') return;
|
|
362
|
-
e.preventDefault();
|
|
363
|
-
highlightKey(e.code);
|
|
364
|
-
if (e.key.length === 1) {
|
|
365
|
-
helpCharCount++;
|
|
366
|
-
var charEl = document.getElementById('help-char-count');
|
|
367
|
-
if (charEl) charEl.textContent = helpCharCount;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
var scriptsTpl = document.getElementById('hacker-scripts-folders-tpl');
|
|
371
|
-
var fallbackModalContent = {
|
|
372
|
-
'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' },
|
|
373
|
-
'ai': { title: 'AI', body: '<pre>~ $ model --status\n\ninference: stable\ncontext: 8k tokens\nlatency: < 200ms\n\n>> system online</pre>' },
|
|
374
|
-
'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' },
|
|
375
|
-
'help': { title: 'Help', body: '', type: 'keyboard' },
|
|
376
|
-
'all-scripts': { title: '/root/bash/scripts', body: '', type: 'scripts' }
|
|
377
|
-
};
|
|
378
|
-
var modalContent = runtimeConfig.modalContent || fallbackModalContent;
|
|
379
|
-
document.querySelectorAll('.hacker-folder[data-modal]').forEach(function(btn) {
|
|
380
|
-
btn.addEventListener('click', function() {
|
|
381
|
-
var id = btn.getAttribute('data-modal');
|
|
382
|
-
var data = modalContent[id];
|
|
383
|
-
if (data) {
|
|
384
|
-
var modalEl = modalOverlay.querySelector('.hacker-modal');
|
|
385
|
-
if (modalEl) modalEl.classList.remove('hacker-modal-wide');
|
|
386
|
-
modalTitle.textContent = data.title;
|
|
387
|
-
modalBody.innerHTML = data.body;
|
|
388
|
-
modalBody.className = 'hacker-modal-body' + (data.type === 'progress' ? ' hacker-modal-download' : '') + (data.type === 'keyboard' ? ' hacker-modal-keyboard' : '') + (data.type === 'scripts' ? ' hacker-modal-scripts-wrap' : '');
|
|
389
|
-
if (data.type === 'progress') {
|
|
390
|
-
var bar = document.getElementById('dl-progress');
|
|
391
|
-
if (bar) {
|
|
392
|
-
bar.innerHTML = '';
|
|
393
|
-
for (var i = 0; i < 48; i++) bar.appendChild(document.createElement('span'));
|
|
394
|
-
var idx = 0;
|
|
395
|
-
function fillNext() {
|
|
396
|
-
if (idx < 48) {
|
|
397
|
-
bar.children[idx].classList.add('filled');
|
|
398
|
-
idx++;
|
|
399
|
-
setTimeout(fillNext, 80 + Math.random() * 60);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
fillNext();
|
|
403
|
-
}
|
|
404
|
-
} else if (data.type === 'decryptor') {
|
|
405
|
-
var el = document.getElementById('dec-keys');
|
|
406
|
-
if (el) el.textContent = '[00:00:01] 0 ' + decryptorKeysLabel;
|
|
407
|
-
el = document.getElementById('dec-pass');
|
|
408
|
-
if (el) el.textContent = randPass();
|
|
409
|
-
el = document.getElementById('dec-master1');
|
|
410
|
-
if (el) el.textContent = randKeyLine(8);
|
|
411
|
-
el = document.getElementById('dec-master2');
|
|
412
|
-
if (el) el.textContent = randKeyLine(8);
|
|
413
|
-
el = document.getElementById('dec-trans1');
|
|
414
|
-
if (el) el.textContent = randKeyLine(7);
|
|
415
|
-
el = document.getElementById('dec-trans2');
|
|
416
|
-
if (el) el.textContent = randKeyLine(7);
|
|
417
|
-
el = document.getElementById('dec-trans3');
|
|
418
|
-
if (el) el.textContent = randKeyLine(8);
|
|
419
|
-
el = document.getElementById('dec-trans4');
|
|
420
|
-
if (el) el.textContent = randKeyLine(8);
|
|
421
|
-
startDecryptorFlash();
|
|
422
|
-
} else if (data.type === 'keyboard') {
|
|
423
|
-
modalBody.innerHTML = buildHelpKeyboard();
|
|
424
|
-
if (modalEl) modalEl.classList.add('hacker-modal-wide');
|
|
425
|
-
initHelpKeyboard();
|
|
426
|
-
document.addEventListener('keydown', handleHelpKeydown);
|
|
427
|
-
} else if (data.type === 'scripts' && scriptsTpl && scriptsTpl.content) {
|
|
428
|
-
modalBody.innerHTML = '';
|
|
429
|
-
modalBody.appendChild(scriptsTpl.content.cloneNode(true));
|
|
430
|
-
if (modalEl) modalEl.classList.add('hacker-modal-wide');
|
|
431
|
-
}
|
|
432
|
-
modalOverlay.classList.add('open');
|
|
433
|
-
modalOverlay.setAttribute('aria-hidden', 'false');
|
|
434
|
-
}
|
|
435
|
-
});
|
|
436
|
-
});
|
|
437
|
-
function closeModal() {
|
|
438
|
-
if (decryptorInterval) {
|
|
439
|
-
clearInterval(decryptorInterval);
|
|
440
|
-
decryptorInterval = null;
|
|
441
|
-
}
|
|
442
|
-
document.removeEventListener('keydown', handleHelpKeydown);
|
|
443
|
-
var modalEl = modalOverlay.querySelector('.hacker-modal');
|
|
444
|
-
if (modalEl) modalEl.classList.remove('hacker-modal-wide');
|
|
445
|
-
modalOverlay.classList.remove('open');
|
|
446
|
-
modalOverlay.setAttribute('aria-hidden', 'true');
|
|
447
|
-
}
|
|
448
|
-
document.querySelector('.hacker-modal-close').addEventListener('click', closeModal);
|
|
449
|
-
modalOverlay.addEventListener('click', function(e) {
|
|
450
|
-
if (e.target === modalOverlay) closeModal();
|
|
451
|
-
});
|
|
452
|
-
document.addEventListener('keydown', function(e) {
|
|
453
|
-
if (e.key === 'Escape' && modalOverlay.classList.contains('open')) closeModal();
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
// ── Mouse glow ──
|
|
457
|
-
var glow = document.querySelector('.hacker-mouse-glow');
|
|
458
|
-
if (glow && !prefersReducedMotion) {
|
|
459
|
-
var glowRaf;
|
|
460
|
-
var mx = 0, my = 0;
|
|
461
|
-
document.addEventListener('mousemove', function(e) {
|
|
462
|
-
mx = e.clientX;
|
|
463
|
-
my = e.clientY;
|
|
464
|
-
if (!glowRaf) glowRaf = requestAnimationFrame(function() {
|
|
465
|
-
glow.style.setProperty('--mouse-x', mx + 'px');
|
|
466
|
-
glow.style.setProperty('--mouse-y', my + 'px');
|
|
467
|
-
glowRaf = 0;
|
|
468
|
-
});
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// ── Paragraph scroll reveal ──
|
|
473
|
-
var paras = document.querySelectorAll('.hacker-body p, .hacker-body h2, .hacker-body blockquote, .about-manifest');
|
|
474
|
-
if (window.IntersectionObserver) {
|
|
475
|
-
var io = new IntersectionObserver(function(entries) {
|
|
476
|
-
entries.forEach(function(e) {
|
|
477
|
-
if (e.isIntersecting) {
|
|
478
|
-
e.target.classList.add('hacker-visible');
|
|
479
|
-
io.unobserve(e.target);
|
|
480
|
-
}
|
|
481
|
-
});
|
|
482
|
-
}, { rootMargin: '0px 0px -60px 0px', threshold: 0.1 });
|
|
483
|
-
paras.forEach(function(p) { io.observe(p); });
|
|
484
|
-
} else {
|
|
485
|
-
paras.forEach(function(p) { p.classList.add('hacker-visible'); });
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// ── Typewriter section titles ──
|
|
489
|
-
var titles = document.querySelectorAll('.about-section-title');
|
|
490
|
-
if (prefersReducedMotion && titles.length) {
|
|
491
|
-
titles.forEach(function(el) {
|
|
492
|
-
var fullText = el.textContent || '';
|
|
493
|
-
el.setAttribute('data-full-text', fullText);
|
|
494
|
-
el.textContent = fullText;
|
|
495
|
-
el.style.minHeight = '1.2em';
|
|
496
|
-
});
|
|
497
|
-
} else if (window.IntersectionObserver && titles.length) {
|
|
498
|
-
titles.forEach(function(el) {
|
|
499
|
-
var fullText = el.textContent || '';
|
|
500
|
-
el.setAttribute('data-full-text', fullText);
|
|
501
|
-
el.textContent = '';
|
|
502
|
-
el.style.minHeight = '1.2em';
|
|
503
|
-
});
|
|
504
|
-
var tio = new IntersectionObserver(function(entries) {
|
|
505
|
-
entries.forEach(function(e) {
|
|
506
|
-
if (e.isIntersecting) {
|
|
507
|
-
tio.unobserve(e.target);
|
|
508
|
-
var text = e.target.getAttribute('data-full-text') || '';
|
|
509
|
-
var i = 0;
|
|
510
|
-
e.target.textContent = '';
|
|
511
|
-
function type() {
|
|
512
|
-
if (i <= text.length) {
|
|
513
|
-
e.target.textContent = text.slice(0, i);
|
|
514
|
-
i++;
|
|
515
|
-
setTimeout(type, 32);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
type();
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
}, { rootMargin: '0px 0px -40px 0px', threshold: 0.1 });
|
|
522
|
-
titles.forEach(function(el) { tio.observe(el); });
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// ── Regenerate button ──
|
|
526
|
-
var regen = document.querySelector('.hacker-regenerate');
|
|
527
|
-
var article = document.querySelector('.about-shell');
|
|
528
|
-
var scan = document.querySelector('.hacker-load-scan');
|
|
529
|
-
if (regen && article) {
|
|
530
|
-
regen.addEventListener('click', function() {
|
|
531
|
-
if (prefersReducedMotion) {
|
|
532
|
-
article.classList.add('hacker-flash');
|
|
533
|
-
setTimeout(function() {
|
|
534
|
-
article.classList.remove('hacker-flash');
|
|
535
|
-
}, 120);
|
|
536
|
-
return;
|
|
537
|
-
}
|
|
538
|
-
regen.disabled = true;
|
|
539
|
-
article.classList.add('hacker-flash');
|
|
540
|
-
if (scan) {
|
|
541
|
-
scan.style.animation = 'none';
|
|
542
|
-
scan.offsetHeight;
|
|
543
|
-
scan.style.animation = 'hacker-scan 0.8s ease-out forwards';
|
|
544
|
-
scan.style.top = '0';
|
|
545
|
-
scan.style.opacity = '1';
|
|
546
|
-
}
|
|
547
|
-
setTimeout(function() {
|
|
548
|
-
article.classList.remove('hacker-flash');
|
|
549
|
-
regen.disabled = false;
|
|
550
|
-
}, 1200);
|
|
551
|
-
});
|
|
552
|
-
}
|
|
7
|
+
export function initAboutEffects() {
|
|
8
|
+
var runtimeConfig = readAboutRuntimeConfig();
|
|
9
|
+
var prefersReducedMotion = isReducedMotion();
|
|
10
|
+
initTerminalBackground(runtimeConfig, prefersReducedMotion);
|
|
11
|
+
initAboutReadingUi(runtimeConfig, prefersReducedMotion);
|
|
12
|
+
initAboutModals(runtimeConfig, prefersReducedMotion);
|
|
13
|
+
initAboutInteractions(prefersReducedMotion);
|
|
553
14
|
}
|