@anglefeint/astro-theme 0.1.18 → 0.1.20

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/README.md CHANGED
@@ -59,4 +59,24 @@ In the starter/site project, map these aliases to `src/config/*` and `src/i18n/*
59
59
  - `anglefeint-new-post`
60
60
  - `anglefeint-new-page`
61
61
 
62
- Starter projects can invoke these directly (or wrap them in npm scripts).
62
+ Examples:
63
+
64
+ ```bash
65
+ # create one post slug in all default locales
66
+ anglefeint-new-post my-first-post
67
+
68
+ # create post only for selected locales
69
+ anglefeint-new-post my-first-post --locales en,ja
70
+
71
+ # or via environment variable
72
+ ANGLEFEINT_LOCALES=en,ja anglefeint-new-post my-first-post
73
+
74
+ # create a custom page with theme variant
75
+ anglefeint-new-page projects --theme base
76
+ anglefeint-new-page projects --theme ai
77
+ anglefeint-new-page projects --theme cyber
78
+ anglefeint-new-page projects --theme hacker
79
+ anglefeint-new-page projects --theme matrix
80
+ ```
81
+
82
+ Starter projects can invoke these directly (or wrap them in npm scripts). For most users, `#starter` is the recommended installation path.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anglefeint/astro-theme",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "type": "module",
5
5
  "description": "Anglefeint core theme package for Astro",
6
6
  "keywords": [
@@ -4,13 +4,17 @@ export function initHeroCanvas(prefersReducedMotion) {
4
4
 
5
5
  var canvas = shell.querySelector('.hero-canvas');
6
6
  var wrap = shell.querySelector('.hero-canvas-wrap');
7
- if (!canvas || !wrap) return;
7
+ var heroStack = shell.querySelector('.hero-stack');
8
+ if (!canvas || !wrap || !heroStack) return;
9
+ var ctx = canvas.getContext('2d');
10
+ if (!ctx) return;
8
11
 
9
12
  var src = canvas.getAttribute('data-hero-src');
10
13
  if (!src) return;
11
14
 
12
15
  var heroStart = 0;
13
16
  var heroRaf = 0;
17
+ var resizeTimer = 0;
14
18
  var baseCanvas = document.createElement('canvas');
15
19
  var baseCtx = baseCanvas.getContext('2d');
16
20
  var pixelCanvas = document.createElement('canvas');
@@ -19,14 +23,17 @@ export function initHeroCanvas(prefersReducedMotion) {
19
23
  var noiseCtx = noiseCanvas.getContext('2d');
20
24
  var edgeCanvas = document.createElement('canvas');
21
25
  var edgeCtx = edgeCanvas.getContext('2d');
26
+ var edgeWorkCanvas = document.createElement('canvas');
27
+ var edgeWorkCtx = edgeWorkCanvas.getContext('2d');
22
28
  var edgeReady = false;
29
+ var frameCount = 0;
23
30
 
24
31
  var EDGE_PHASE = 1.8;
25
32
  var REVEAL_PHASE = 2.5;
26
33
  var INTRO_END = EDGE_PHASE + REVEAL_PHASE;
27
34
 
28
35
  function sizeCanvas() {
29
- var rect = shell.querySelector('.hero-stack').getBoundingClientRect();
36
+ var rect = heroStack.getBoundingClientRect();
30
37
  var dpr = Math.min(window.devicePixelRatio || 1, 2);
31
38
  canvas.width = Math.max(2, Math.round(rect.width * dpr));
32
39
  canvas.height = Math.max(2, Math.round(rect.height * dpr));
@@ -55,28 +62,44 @@ export function initHeroCanvas(prefersReducedMotion) {
55
62
  baseCanvas.width = w;
56
63
  baseCanvas.height = h;
57
64
  drawBase(baseCtx, img, w, h);
58
- drawBase(edgeCtx, img, w, h);
65
+ var workW = Math.max(2, Math.round(w * 0.5));
66
+ var workH = Math.max(2, Math.round(h * 0.5));
67
+ edgeWorkCanvas.width = workW;
68
+ edgeWorkCanvas.height = workH;
69
+ drawBase(edgeWorkCtx, img, workW, workH);
59
70
 
60
- var srcImage = edgeCtx.getImageData(0, 0, w, h);
71
+ var srcImage = edgeWorkCtx.getImageData(0, 0, workW, workH);
61
72
  var d = srcImage.data;
62
- var out = edgeCtx.createImageData(w, h);
73
+ var out = edgeWorkCtx.createImageData(workW, workH);
63
74
  var od = out.data;
75
+ var rowStride = workW * 4;
64
76
 
65
- for (var y = 1; y < h - 1; y++) {
66
- for (var x = 1; x < w - 1; x++) {
67
- var idx = function(px, py) {
68
- return ((py * w) + px) * 4;
69
- };
70
- var i = idx(x, y);
71
- function luma(px, py) {
72
- var j = idx(px, py);
73
- return d[j] * 0.299 + d[j + 1] * 0.587 + d[j + 2] * 0.114;
74
- }
77
+ for (var y = 1; y < workH - 1; y++) {
78
+ var row = y * rowStride;
79
+ var rowAbove = row - rowStride;
80
+ var rowBelow = row + rowStride;
81
+ for (var x = 1; x < workW - 1; x++) {
82
+ var i = row + x * 4;
83
+ var iLeft = i - 4;
84
+ var iRight = i + 4;
85
+ var iAbove = rowAbove + x * 4;
86
+ var iAboveLeft = iAbove - 4;
87
+ var iAboveRight = iAbove + 4;
88
+ var iBelow = rowBelow + x * 4;
89
+ var iBelowLeft = iBelow - 4;
90
+ var iBelowRight = iBelow + 4;
91
+
92
+ var lumAboveLeft = d[iAboveLeft] * 0.299 + d[iAboveLeft + 1] * 0.587 + d[iAboveLeft + 2] * 0.114;
93
+ var lumAbove = d[iAbove] * 0.299 + d[iAbove + 1] * 0.587 + d[iAbove + 2] * 0.114;
94
+ var lumAboveRight = d[iAboveRight] * 0.299 + d[iAboveRight + 1] * 0.587 + d[iAboveRight + 2] * 0.114;
95
+ var lumLeft = d[iLeft] * 0.299 + d[iLeft + 1] * 0.587 + d[iLeft + 2] * 0.114;
96
+ var lumRight = d[iRight] * 0.299 + d[iRight + 1] * 0.587 + d[iRight + 2] * 0.114;
97
+ var lumBelowLeft = d[iBelowLeft] * 0.299 + d[iBelowLeft + 1] * 0.587 + d[iBelowLeft + 2] * 0.114;
98
+ var lumBelow = d[iBelow] * 0.299 + d[iBelow + 1] * 0.587 + d[iBelow + 2] * 0.114;
99
+ var lumBelowRight = d[iBelowRight] * 0.299 + d[iBelowRight + 1] * 0.587 + d[iBelowRight + 2] * 0.114;
75
100
 
76
- var gx = -luma(x - 1, y - 1) - 2 * luma(x - 1, y) - luma(x - 1, y + 1)
77
- + luma(x + 1, y - 1) + 2 * luma(x + 1, y) + luma(x + 1, y + 1);
78
- var gy = -luma(x - 1, y - 1) - 2 * luma(x, y - 1) - luma(x + 1, y - 1)
79
- + luma(x - 1, y + 1) + 2 * luma(x, y + 1) + luma(x + 1, y + 1);
101
+ var gx = -lumAboveLeft - 2 * lumLeft - lumBelowLeft + lumAboveRight + 2 * lumRight + lumBelowRight;
102
+ var gy = -lumAboveLeft - 2 * lumAbove - lumAboveRight + lumBelowLeft + 2 * lumBelow + lumBelowRight;
80
103
  var mag = Math.min(255, Math.sqrt(gx * gx + gy * gy));
81
104
 
82
105
  od[i] = Math.min(255, mag * 0.4);
@@ -86,15 +109,18 @@ export function initHeroCanvas(prefersReducedMotion) {
86
109
  }
87
110
  }
88
111
 
89
- edgeCtx.putImageData(out, 0, 0);
112
+ edgeWorkCtx.putImageData(out, 0, 0);
113
+ edgeCtx.clearRect(0, 0, w, h);
114
+ edgeCtx.imageSmoothingEnabled = true;
115
+ edgeCtx.drawImage(edgeWorkCanvas, 0, 0, workW, workH, 0, 0, w, h);
90
116
  edgeReady = true;
91
117
  }
92
118
 
93
119
  function heroRender(t) {
94
120
  if (!heroStart) heroStart = t;
95
121
  var elapsed = (t - heroStart) * 0.001;
96
- var ctx = canvas.getContext('2d');
97
- if (!ctx || !canvas.img) {
122
+ frameCount++;
123
+ if (!canvas.img) {
98
124
  heroRaf = requestAnimationFrame(heroRender);
99
125
  return;
100
126
  }
@@ -161,7 +187,7 @@ export function initHeroCanvas(prefersReducedMotion) {
161
187
  ctx.fillStyle = barGrad;
162
188
  ctx.fillRect(0, scanPos - 30, w, 60);
163
189
 
164
- if (Math.random() < 0.08) {
190
+ if ((frameCount % 2) === 0 && Math.random() < 0.08) {
165
191
  var glitchY = Math.random() * h;
166
192
  var glitchH = 2 + Math.random() * 12;
167
193
  var shiftX = (Math.random() - 0.5) * 12;
@@ -171,14 +197,14 @@ export function initHeroCanvas(prefersReducedMotion) {
171
197
  ctx.restore();
172
198
  }
173
199
 
174
- if (elapsed >= 6 && Math.random() < 0.025) {
200
+ if ((frameCount % 3) === 0 && elapsed >= 6 && Math.random() < 0.025) {
175
201
  var dropoutY = Math.floor(Math.random() * h);
176
202
  var dropoutH = 2 + Math.floor(Math.random() * 2);
177
203
  ctx.fillStyle = 'rgba(0, 0, 0, 0.85)';
178
204
  ctx.fillRect(0, dropoutY, w, dropoutH);
179
205
  }
180
206
 
181
- if (Math.random() < 0.03) {
207
+ if ((frameCount % 3) === 0 && Math.random() < 0.03) {
182
208
  var burstY = Math.random() * h * 0.8;
183
209
  var burstH = 4 + Math.random() * 20;
184
210
  if (noiseCanvas.width !== w) {
@@ -221,8 +247,7 @@ export function initHeroCanvas(prefersReducedMotion) {
221
247
  buildEdge(img);
222
248
  wrap.classList.add('ready');
223
249
  if (prefersReducedMotion) {
224
- var staticCtx = canvas.getContext('2d');
225
- if (staticCtx) staticCtx.drawImage(baseCanvas, 0, 0);
250
+ ctx.drawImage(baseCanvas, 0, 0);
226
251
  return;
227
252
  }
228
253
  heroRaf = requestAnimationFrame(heroRender);
@@ -230,10 +255,13 @@ export function initHeroCanvas(prefersReducedMotion) {
230
255
  img.src = new URL(src, window.location.href).href;
231
256
 
232
257
  window.addEventListener('resize', function() {
233
- if (canvas.img) {
258
+ if (!canvas.img) return;
259
+ if (resizeTimer) clearTimeout(resizeTimer);
260
+ resizeTimer = setTimeout(function() {
261
+ resizeTimer = 0;
234
262
  sizeCanvas();
235
263
  buildEdge(canvas.img);
236
- }
264
+ }, 180);
237
265
  }, { passive: true });
238
266
 
239
267
  function onHeroVisibilityChange() {
@@ -248,6 +276,7 @@ export function initHeroCanvas(prefersReducedMotion) {
248
276
 
249
277
  document.addEventListener('visibilitychange', onHeroVisibilityChange);
250
278
  window.addEventListener('beforeunload', function() {
279
+ if (resizeTimer) clearTimeout(resizeTimer);
251
280
  cancelAnimationFrame(heroRaf);
252
281
  }, { once: true });
253
282
  }
@@ -0,0 +1,57 @@
1
+ /* About background layer: terminal backdrop and scan/flicker ambience. */
2
+ /* ── terminal background ── */
3
+ .hacker-bg {
4
+ position: fixed;
5
+ inset: 0;
6
+ z-index: 0;
7
+ overflow: hidden;
8
+ pointer-events: none;
9
+ }
10
+ .hacker-bg-canvas {
11
+ position: absolute;
12
+ inset: 0;
13
+ width: 100%;
14
+ height: 100%;
15
+ }
16
+ .hacker-scanlines {
17
+ position: absolute;
18
+ inset: 0;
19
+ /* 浅色扫描线:在深色背景上可见,全屏 CRT 感 */
20
+ background: repeating-linear-gradient(
21
+ 180deg,
22
+ transparent 0px,
23
+ transparent 2px,
24
+ rgba(255, 255, 255, 0.03) 2px,
25
+ rgba(255, 255, 255, 0.03) 3px
26
+ );
27
+ pointer-events: none;
28
+ }
29
+ .hacker-vignette {
30
+ position: absolute;
31
+ inset: 0;
32
+ background: radial-gradient(
33
+ ellipse at center,
34
+ transparent 50%,
35
+ rgba(0, 0, 0, 0.5) 80%,
36
+ rgba(0, 0, 0, 0.85) 100%
37
+ );
38
+ pointer-events: none;
39
+ }
40
+ .hacker-flicker {
41
+ position: absolute;
42
+ inset: 0;
43
+ background: transparent;
44
+ animation: hacker-flicker-anim 0.08s infinite;
45
+ pointer-events: none;
46
+ }
47
+ @keyframes hacker-flicker-anim {
48
+ 0% { background: rgba(255, 255, 255, 0.012); }
49
+ 50% { background: transparent; }
50
+ 100% { background: rgba(255, 255, 255, 0.005); }
51
+ }
52
+ .hacker-glow {
53
+ position: absolute;
54
+ inset: 0;
55
+ box-shadow: inset 0 0 120px rgba(255, 255, 255, 0.03);
56
+ pointer-events: none;
57
+ }
@@ -0,0 +1,58 @@
1
+ /* About base layer: hacker-page chrome variables and shell baselines. */
2
+ body.hacker-page {
3
+ background: #000 !important;
4
+ background-image: none !important;
5
+ color: rgba(255, 255, 255, 0.88);
6
+ min-height: 100vh;
7
+ scrollbar-width: thin;
8
+ scrollbar-color: rgba(255, 255, 255, 0.25) transparent;
9
+ /* header: Anonymous 黑白灰 + 黄点缀 */
10
+ --chrome-bg: rgba(0, 0, 0, 0.42);
11
+ --chrome-border: rgba(255, 255, 255, 0.2);
12
+ --chrome-link: rgba(255, 255, 255, 0.9);
13
+ --chrome-link-hover: rgba(0, 255, 100, 0.95);
14
+ --chrome-active: rgba(255, 255, 255, 0.5);
15
+ --chrome-text-muted: rgba(255, 255, 255, 0.5);
16
+ }
17
+ body.hacker-page::-webkit-scrollbar { width: 6px; }
18
+ body.hacker-page::-webkit-scrollbar-track { background: transparent; }
19
+ body.hacker-page::-webkit-scrollbar-thumb {
20
+ background: rgba(255, 255, 255, 0.22);
21
+ border-radius: 3px;
22
+ }
23
+ /* fixed header + content offset (mirrors ai-page in global.css) */
24
+ body.hacker-page header {
25
+ position: fixed;
26
+ top: 0;
27
+ left: 0;
28
+ right: 0;
29
+ z-index: 100;
30
+ backdrop-filter: blur(3px);
31
+ -webkit-backdrop-filter: blur(3px);
32
+ }
33
+ body.hacker-page .hacker-content {
34
+ padding-top: calc(3em + 56px);
35
+ }
36
+ body.hacker-page .hacker-content,
37
+ body.hacker-page footer {
38
+ position: relative;
39
+ z-index: 10;
40
+ }
41
+ /* footer: Anonymous 灰白 */
42
+ body.hacker-page footer {
43
+ --chrome-bg: rgba(0, 0, 0, 0.85);
44
+ --chrome-border: rgba(255, 255, 255, 0.15);
45
+ border-top-color: rgba(255, 255, 255, 0.15) !important;
46
+ color: rgba(255, 255, 255, 0.55);
47
+ }
48
+ body.hacker-page footer a {
49
+ color: rgba(255, 255, 255, 0.7) !important;
50
+ }
51
+ body.hacker-page footer a:hover {
52
+ color: rgba(0, 255, 100, 0.9) !important;
53
+ }
54
+ @media (max-width: 720px) {
55
+ body.hacker-page .hacker-content {
56
+ padding-top: calc(1em + 56px);
57
+ }
58
+ }
@@ -0,0 +1,112 @@
1
+ /* About keyboard layer: virtual keyboard and helper interaction visuals. */
2
+ /* Help: 虚拟键盘 */
3
+ .hacker-vkeyboard {
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: 4px;
7
+ font-size: 0.7rem;
8
+ }
9
+ .hacker-vkeyboard-row {
10
+ display: flex;
11
+ justify-content: center;
12
+ gap: 3px;
13
+ }
14
+ .hacker-vkey {
15
+ min-width: 24px;
16
+ height: 28px;
17
+ padding: 0 6px;
18
+ display: inline-flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ background: rgba(255, 255, 255, 0.08);
22
+ border: 1px solid rgba(255, 255, 255, 0.2);
23
+ border-radius: 4px;
24
+ color: rgba(255, 255, 255, 0.9);
25
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
26
+ cursor: pointer;
27
+ transition: all 0.1s ease;
28
+ user-select: none;
29
+ }
30
+ .hacker-vkey:hover {
31
+ background: rgba(255, 255, 255, 0.15);
32
+ border-color: rgba(255, 255, 255, 0.35);
33
+ }
34
+ .hacker-vkey.highlight {
35
+ background: rgba(0, 255, 100, 0.35);
36
+ border-color: rgba(0, 255, 100, 0.7);
37
+ box-shadow: 0 0 12px rgba(0, 255, 100, 0.3);
38
+ }
39
+ .hacker-vkey.wide { min-width: 36px; }
40
+ .hacker-vkey.backspace { min-width: 72px; }
41
+ .hacker-vkey.space { min-width: 120px; }
42
+ .hacker-vkey.acc { background: rgba(255, 140, 0, 0.25); border-color: rgba(255, 140, 0, 0.5); }
43
+ .hacker-vkey.acc:hover { background: rgba(255, 140, 0, 0.35); }
44
+ .hacker-vkeyboard-wrap {
45
+ display: flex;
46
+ flex-wrap: wrap;
47
+ gap: 2rem;
48
+ align-items: stretch;
49
+ }
50
+ .hacker-vkeyboard-main {
51
+ flex: 1;
52
+ min-width: 0;
53
+ }
54
+ .hacker-vkeyboard-side {
55
+ display: flex;
56
+ flex-direction: column;
57
+ gap: 6px;
58
+ flex-shrink: 0;
59
+ min-width: 140px;
60
+ }
61
+ .hacker-vkeyboard-side-block {
62
+ display: flex;
63
+ flex-direction: column;
64
+ gap: 4px;
65
+ margin-bottom: 4px;
66
+ }
67
+ .hacker-vkeyboard-side-row {
68
+ display: flex;
69
+ gap: 3px;
70
+ }
71
+ .hacker-vkeyboard-side-block .hacker-vkey {
72
+ flex: 1;
73
+ min-width: 0;
74
+ }
75
+ .hacker-vkey.nav-home { background: rgba(66, 133, 244, 0.3); border-color: rgba(66, 133, 244, 0.5); }
76
+ .hacker-vkey.nav-home:hover { background: rgba(66, 133, 244, 0.4); }
77
+ .hacker-vkey.nav-end { background: rgba(234, 67, 53, 0.25); border-color: rgba(234, 67, 53, 0.5); }
78
+ .hacker-vkey.nav-end:hover { background: rgba(234, 67, 53, 0.35); }
79
+ .hacker-vkeyboard-arrows-wrap {
80
+ display: flex;
81
+ flex-direction: column;
82
+ align-items: center;
83
+ margin-top: 6px;
84
+ }
85
+ .hacker-vkeyboard-arrows {
86
+ display: grid;
87
+ grid-template: ". u ." 1fr "l d r" 1fr / 1fr 1fr 1fr;
88
+ gap: 2px;
89
+ height: 56px;
90
+ }
91
+ .hacker-vkeyboard-arrows .arr-u { grid-area: u; }
92
+ .hacker-vkeyboard-arrows .arr-l { grid-area: l; }
93
+ .hacker-vkeyboard-arrows .arr-r { grid-area: r; }
94
+ .hacker-vkeyboard-arrows .arr-d { grid-area: d; }
95
+ .hacker-vkeyboard-arrows .hacker-vkey { width: 50px; height: 22px; font-size: 0.65rem; padding: 0; }
96
+ .hacker-vkeyboard-stats {
97
+ width: 100%;
98
+ margin-top: 0.2rem;
99
+ font-size: 0.75em;
100
+ color: rgba(255, 255, 255, 0.6);
101
+ }
102
+ .hacker-vkeyboard-stats-label {
103
+ font-size: 0.65rem;
104
+ color: rgba(255, 255, 255, 0.5);
105
+ letter-spacing: 0.1em;
106
+ }
107
+ .hacker-modal-body.hacker-modal-keyboard {
108
+ overflow: visible;
109
+ }
110
+ @media (max-width: 900px) {
111
+ .hacker-sidebar { display: none; }
112
+ }
@@ -0,0 +1,159 @@
1
+ /* About modal layer: dialog shells and script explorer surfaces. */
2
+ /* ── 弹窗 (Anonymous 窗口) ── */
3
+ .hacker-modal-overlay {
4
+ position: fixed;
5
+ inset: 0;
6
+ z-index: 200;
7
+ background: rgba(0, 0, 0, 0.7);
8
+ backdrop-filter: blur(4px);
9
+ display: none;
10
+ align-items: center;
11
+ justify-content: center;
12
+ padding: 2rem;
13
+ }
14
+ .hacker-modal-overlay.open {
15
+ display: flex;
16
+ }
17
+ .hacker-modal {
18
+ width: min(420px, 100%);
19
+ max-height: 80vh;
20
+ }
21
+ .hacker-modal.hacker-modal-wide {
22
+ width: min(900px, calc(100vw - 2rem));
23
+ overflow: visible;
24
+ }
25
+ .hacker-modal-header {
26
+ display: flex;
27
+ align-items: center;
28
+ gap: 0.5rem;
29
+ padding: 0.5rem 0.9rem;
30
+ background: #fff;
31
+ border-bottom: 1px solid rgba(0, 0, 0, 0.15);
32
+ }
33
+ .hacker-modal-dots {
34
+ display: flex;
35
+ gap: 6px;
36
+ }
37
+ .hacker-modal-dots span {
38
+ width: 10px;
39
+ height: 10px;
40
+ border-radius: 50%;
41
+ }
42
+ .hacker-modal-dots span:nth-child(1) { background: #ff5f57; }
43
+ .hacker-modal-dots span:nth-child(2) { background: #febc2e; }
44
+ .hacker-modal-dots span:nth-child(3) { background: #28c840; }
45
+ .hacker-modal-title {
46
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
47
+ font-size: 0.75em;
48
+ color: #000;
49
+ font-weight: 600;
50
+ letter-spacing: 0.1em;
51
+ text-transform: uppercase;
52
+ }
53
+ .hacker-modal-close {
54
+ margin-left: auto;
55
+ width: 66px;
56
+ height: 66px;
57
+ border: none;
58
+ background: rgba(0, 0, 0, 0.06);
59
+ color: #000;
60
+ display: flex;
61
+ align-items: center;
62
+ justify-content: center;
63
+ cursor: pointer;
64
+ opacity: 0.9;
65
+ border-radius: 10px;
66
+ }
67
+ .hacker-modal-close .hacker-modal-close-icon {
68
+ flex-shrink: 0;
69
+ }
70
+ .hacker-modal-close:hover {
71
+ opacity: 1;
72
+ background: rgba(0, 0, 0, 0.14);
73
+ }
74
+ .hacker-modal-body {
75
+ padding: 1rem 0.9rem 1.2rem;
76
+ color: rgba(255, 255, 255, 0.88);
77
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
78
+ font-size: 0.82rem;
79
+ line-height: 1.6;
80
+ max-height: 60vh;
81
+ overflow-y: auto;
82
+ }
83
+ .hacker-modal-body pre {
84
+ margin: 0;
85
+ white-space: pre-wrap;
86
+ word-break: break-word;
87
+ }
88
+ /* DL Data: Downloading 进度条 (Anonymous 风格) */
89
+ .hacker-modal-body.hacker-modal-download .modal-subtitle {
90
+ font-size: 0.9em;
91
+ color: rgba(255, 255, 255, 0.7);
92
+ margin-bottom: 1rem;
93
+ }
94
+ .hacker-modal-progress {
95
+ display: flex;
96
+ gap: 2px;
97
+ flex-wrap: wrap;
98
+ margin-top: 0.5rem;
99
+ }
100
+ .hacker-modal-progress span {
101
+ width: 10px;
102
+ height: 12px;
103
+ background: rgba(255, 255, 255, 0.2);
104
+ transition: background 0.12s ease;
105
+ }
106
+ .hacker-modal-progress span.filled {
107
+ background: rgba(255, 255, 255, 0.6);
108
+ }
109
+ /* All Scripts: 文件夹网格 */
110
+ .hacker-modal-scripts {
111
+ padding: 0.5rem 0;
112
+ }
113
+ .hacker-modal-scripts-path {
114
+ font-size: 0.9em;
115
+ color: rgba(255, 255, 255, 0.9);
116
+ letter-spacing: 0.05em;
117
+ margin-bottom: 1rem;
118
+ border-bottom: 1px solid rgba(255, 255, 255, 0.15);
119
+ padding-bottom: 0.5rem;
120
+ }
121
+ .hacker-modal-scripts-grid {
122
+ display: grid;
123
+ grid-template-columns: repeat(5, 1fr);
124
+ gap: 1rem;
125
+ }
126
+ .hacker-script-folder {
127
+ display: flex;
128
+ flex-direction: column;
129
+ align-items: center;
130
+ gap: 0.5rem;
131
+ padding: 0.6rem 0.4rem;
132
+ background: rgba(255, 255, 255, 0.05);
133
+ border: 1px solid rgba(255, 255, 255, 0.12);
134
+ border-radius: 6px;
135
+ color: rgba(255, 255, 255, 0.9);
136
+ text-decoration: none;
137
+ transition: all 0.15s ease;
138
+ }
139
+ .hacker-script-folder:hover {
140
+ background: rgba(255, 255, 255, 0.12);
141
+ border-color: rgba(0, 255, 100, 0.4);
142
+ box-shadow: 0 0 12px rgba(0, 255, 100, 0.15);
143
+ }
144
+ .hacker-script-folder-icon {
145
+ width: 32px;
146
+ height: 28px;
147
+ color: rgba(255, 255, 255, 0.7);
148
+ }
149
+ .hacker-script-folder-label {
150
+ font-size: 0.72rem;
151
+ text-align: center;
152
+ word-break: break-word;
153
+ line-height: 1.3;
154
+ }
155
+ @media (max-width: 600px) {
156
+ .hacker-modal-scripts-grid {
157
+ grid-template-columns: repeat(3, 1fr);
158
+ }
159
+ }