@code2rich/jpage 1.5.0

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.
Files changed (143) hide show
  1. package/.claude/settings.local.json +68 -0
  2. package/.dockerignore +8 -0
  3. package/.env.example +56 -0
  4. package/.github/workflows/ci.yml +43 -0
  5. package/CLAUDE.md +280 -0
  6. package/Dockerfile +44 -0
  7. package/LICENSE +21 -0
  8. package/README.md +433 -0
  9. package/README_EN.md +399 -0
  10. package/bin/args.js +64 -0
  11. package/bin/client.js +93 -0
  12. package/bin/commands/_shared.js +54 -0
  13. package/bin/commands/cat.js +23 -0
  14. package/bin/commands/ls.js +44 -0
  15. package/bin/commands/mv.js +20 -0
  16. package/bin/commands/rm.js +22 -0
  17. package/bin/commands/skills.js +70 -0
  18. package/bin/commands/star.js +23 -0
  19. package/bin/commands/tags.js +97 -0
  20. package/bin/commands/upload.js +84 -0
  21. package/bin/commands/url.js +25 -0
  22. package/bin/commands/whoami.js +29 -0
  23. package/bin/config.js +85 -0
  24. package/bin/jpage.js +168 -0
  25. package/build.js +112 -0
  26. package/docker-compose.yml +26 -0
  27. package/docs/api.md +438 -0
  28. package/docs/design/005-custom-modal.md +296 -0
  29. package/docs/design/013-file-version-history.md +324 -0
  30. package/docs/design/billing-system.md +600 -0
  31. package/docs/design/db-index-and-healthcheck.md +176 -0
  32. package/docs/design/loading-states.md +209 -0
  33. package/docs/virtual-hosting-feasibility.md +453 -0
  34. package/eslint.config.mjs +172 -0
  35. package/lib/auth-state.js +15 -0
  36. package/lib/categories.js +20 -0
  37. package/lib/crypto.js +85 -0
  38. package/lib/csp.js +66 -0
  39. package/lib/db.js +53 -0
  40. package/lib/dispatch.js +103 -0
  41. package/lib/fts.js +81 -0
  42. package/lib/middleware/auth.js +114 -0
  43. package/lib/middleware/files.js +42 -0
  44. package/lib/paths.js +9 -0
  45. package/lib/render-cache.js +48 -0
  46. package/lib/render.js +157 -0
  47. package/lib/templates.js +149 -0
  48. package/lib/util.js +66 -0
  49. package/lib/view-counts.js +59 -0
  50. package/lib/zip.js +192 -0
  51. package/logger.js +16 -0
  52. package/mailer.js +34 -0
  53. package/mcp/constants.js +16 -0
  54. package/mcp/resources.js +74 -0
  55. package/mcp/server.js +43 -0
  56. package/mcp/tools-categories.js +56 -0
  57. package/mcp/tools-content-templates.js +59 -0
  58. package/mcp/tools-files.js +245 -0
  59. package/mcp/tools-tags.js +41 -0
  60. package/mcp/tools-versions.js +57 -0
  61. package/mcp/transport.js +183 -0
  62. package/mcp/util.js +63 -0
  63. package/mcp-server.js +20 -0
  64. package/migrations/001_init_schema.js +25 -0
  65. package/migrations/002_add_share_key.js +33 -0
  66. package/migrations/003_add_roles_and_tokens.js +28 -0
  67. package/migrations/004_add_version_history.js +32 -0
  68. package/migrations/005_tags_starred_categories.js +49 -0
  69. package/migrations/006_zip_bundle.js +17 -0
  70. package/migrations/007_add_file_type_uploaded_by_indexes.js +7 -0
  71. package/migrations/008_add_fts5.js +6 -0
  72. package/migrations/009_add_link_visits.js +20 -0
  73. package/migrations/010_add_templates_system.js +34 -0
  74. package/migrations/011_content_templates.js +233 -0
  75. package/migrations/012_add_email_and_verification.js +35 -0
  76. package/migrations/013_add_token_encrypted.js +14 -0
  77. package/migrations.js +65 -0
  78. package/package.json +63 -0
  79. package/public/css/style.css +2915 -0
  80. package/public/index.html +855 -0
  81. package/public/js/api.js +22 -0
  82. package/public/js/app.js +94 -0
  83. package/public/js/components/dialog.js +106 -0
  84. package/public/js/components/toast.js +13 -0
  85. package/public/js/pages/content-templates.js +330 -0
  86. package/public/js/pages/home.js +1903 -0
  87. package/public/js/pages/landing.js +158 -0
  88. package/public/js/pages/login.js +175 -0
  89. package/public/js/pages/preview.js +713 -0
  90. package/public/js/theme.js +44 -0
  91. package/public/js/utils.js +67 -0
  92. package/routes/admin.js +136 -0
  93. package/routes/auth.js +365 -0
  94. package/routes/categories.js +90 -0
  95. package/routes/content-templates.js +215 -0
  96. package/routes/files/_shared.js +112 -0
  97. package/routes/files/associations.js +94 -0
  98. package/routes/files/crud.js +139 -0
  99. package/routes/files/detail-serve.js +178 -0
  100. package/routes/files/index.js +38 -0
  101. package/routes/files/list.js +200 -0
  102. package/routes/files/overwrite.js +114 -0
  103. package/routes/files/upload.js +204 -0
  104. package/routes/files/versions.js +166 -0
  105. package/routes/files.js +16 -0
  106. package/routes/skills.js +93 -0
  107. package/routes/tags.js +65 -0
  108. package/routes/tokens.js +110 -0
  109. package/routes/users.js +120 -0
  110. package/server.js +372 -0
  111. package/skills/jpage-content-template/SKILL.md +98 -0
  112. package/skills/jpage-upload/SKILL.md +247 -0
  113. package/skills-registry.js +135 -0
  114. package/templates/academic.html +41 -0
  115. package/templates/dark-pro.html +41 -0
  116. package/templates/default.html +56 -0
  117. package/templates/github.html +67 -0
  118. package/test/browser-harness.js +125 -0
  119. package/test/dispatch-bench.js +74 -0
  120. package/test/helpers/setup.js +45 -0
  121. package/test/integration/admin.test.js +108 -0
  122. package/test/integration/auth.test.js +93 -0
  123. package/test/integration/categories.test.js +103 -0
  124. package/test/integration/cli.test.js +310 -0
  125. package/test/integration/content-templates.test.js +147 -0
  126. package/test/integration/files-security.test.js +248 -0
  127. package/test/integration/files.test.js +139 -0
  128. package/test/integration/share.test.js +79 -0
  129. package/test/integration/skills.test.js +104 -0
  130. package/test/integration/tags.test.js +84 -0
  131. package/test/integration/tokens.test.js +89 -0
  132. package/test/integration/users.test.js +138 -0
  133. package/test/mcp-harness.js +152 -0
  134. package/test/perf-bench.js +108 -0
  135. package/test/perf-harness.js +198 -0
  136. package/test/run-server.sh +15 -0
  137. package/test/unit/cli-args.test.js +88 -0
  138. package/test/unit/cli-config.test.js +89 -0
  139. package/test/unit/crypto.test.js +100 -0
  140. package/test/unit/fts.test.js +52 -0
  141. package/test/unit/render-cache.test.js +76 -0
  142. package/test/unit/util.test.js +81 -0
  143. package/test/unit/zip.test.js +164 -0
@@ -0,0 +1,2915 @@
1
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
2
+
3
+ :root {
4
+ --primary: #6366f1;
5
+ --primary-hover: #818cf8;
6
+ --primary-soft: rgba(99,102,241,0.12);
7
+ --danger: #f87171;
8
+ --danger-hover: #fca5a5;
9
+ --bg: #08080f;
10
+ --card: rgba(16,16,32,0.75);
11
+ --border: rgba(99,102,241,0.12);
12
+ --text: #e4e4f0;
13
+ --text-secondary: #9898b8;
14
+ --radius: 10px;
15
+ --radius-sm: 6px;
16
+ --shadow: 0 1px 2px rgba(0,0,0,0.3), 0 8px 24px rgba(0,0,0,0.3);
17
+ --header-bg: rgba(8,8,15,0.88);
18
+ --glow: 0 0 24px rgba(99,102,241,0.25);
19
+ }
20
+
21
+ html.light {
22
+ --primary: #6366f1;
23
+ --primary-hover: #4f46e5;
24
+ --primary-soft: rgba(99,102,241,0.06);
25
+ --danger: #dc2626;
26
+ --danger-hover: #b91c1c;
27
+ --bg: #f6f6fc;
28
+ --card: rgba(255,255,255,0.9);
29
+ --border: rgba(99,102,241,0.15);
30
+ --text: #18182e;
31
+ --text-secondary: #5a5a78;
32
+ --shadow: 0 1px 2px rgba(15,23,42,0.06), 0 8px 24px rgba(15,23,42,0.06);
33
+ --header-bg: rgba(246,246,252,0.88);
34
+ --glow: none;
35
+ }
36
+
37
+ /* 暗色主题组件覆盖 */
38
+ html:not(.light) .upload-area:hover,
39
+ html:not(.light) .upload-area.dragover {
40
+ background: var(--primary-soft);
41
+ }
42
+
43
+ html:not(.light) .source-view {
44
+ background: #0d1117;
45
+ color: #c9d1d9;
46
+ }
47
+
48
+ html:not(.light) .btn-small {
49
+ background: rgba(99,102,241,0.08);
50
+ color: var(--text);
51
+ border-color: rgba(99,102,241,0.15);
52
+ }
53
+ html:not(.light) .btn-small:hover {
54
+ background: rgba(99,102,241,0.15);
55
+ }
56
+ html:not(.light) .btn-small.active {
57
+ background: var(--primary);
58
+ color: #fff;
59
+ border-color: var(--primary);
60
+ }
61
+
62
+ html:not(.light) .btn-danger {
63
+ background: rgba(248, 113, 113, 0.12);
64
+ color: var(--danger);
65
+ border-color: rgba(248, 113, 113, 0.35);
66
+ }
67
+ html:not(.light) .btn-danger:hover {
68
+ background: rgba(248, 113, 113, 0.2);
69
+ }
70
+
71
+ html:not(.light) #preview-iframe {
72
+ background: #fff;
73
+ }
74
+
75
+ html:not(.light) .preview-title-strip:hover {
76
+ border-color: rgba(99,102,241,0.25);
77
+ }
78
+
79
+ body {
80
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans SC', 'PingFang SC', sans-serif;
81
+ background: var(--bg);
82
+ color: var(--text);
83
+ min-height: 100vh;
84
+ line-height: 1.5;
85
+ -webkit-font-smoothing: antialiased;
86
+ transition: background .4s, color .4s;
87
+ }
88
+
89
+ /* 主题切换按钮 */
90
+ .theme-toggle {
91
+ width: 36px;
92
+ height: 36px;
93
+ border-radius: 50%;
94
+ background: var(--card);
95
+ border: 1px solid var(--border);
96
+ backdrop-filter: blur(10px);
97
+ cursor: pointer;
98
+ display: inline-flex;
99
+ align-items: center;
100
+ justify-content: center;
101
+ transition: all .3s;
102
+ flex-shrink: 0;
103
+ padding: 0;
104
+ }
105
+ .theme-toggle:hover {
106
+ border-color: var(--primary);
107
+ box-shadow: var(--glow);
108
+ }
109
+ .theme-toggle svg {
110
+ width: 16px;
111
+ height: 16px;
112
+ fill: var(--text-secondary);
113
+ transition: fill .3s;
114
+ }
115
+ .theme-toggle:hover svg { fill: var(--primary); }
116
+ html.light .theme-toggle .icon-moon { display: none; }
117
+ html.light .theme-toggle .icon-sun { display: block; }
118
+ .theme-toggle .icon-sun { display: none; }
119
+
120
+ /* Auth */
121
+ .auth-container {
122
+ display: flex;
123
+ align-items: center;
124
+ justify-content: center;
125
+ min-height: 100vh;
126
+ padding: 20px;
127
+ }
128
+
129
+ .auth-box {
130
+ background: var(--card);
131
+ border-radius: var(--radius);
132
+ box-shadow: var(--shadow);
133
+ padding: 40px;
134
+ width: 100%;
135
+ max-width: 400px;
136
+ }
137
+
138
+ .auth-box h1 {
139
+ font-size: 24px;
140
+ margin-bottom: 4px;
141
+ text-align: center;
142
+ }
143
+
144
+ .subtitle {
145
+ color: var(--text-secondary);
146
+ font-size: 14px;
147
+ text-align: center;
148
+ margin-bottom: 24px;
149
+ }
150
+
151
+ .auth-tabs {
152
+ display: flex;
153
+ gap: 8px;
154
+ margin-bottom: 20px;
155
+ border-bottom: 1px solid var(--border);
156
+ }
157
+
158
+ .auth-tab {
159
+ flex: 1;
160
+ padding: 10px;
161
+ border: none;
162
+ background: none;
163
+ color: var(--text-secondary);
164
+ font-size: 15px;
165
+ cursor: pointer;
166
+ border-bottom: 2px solid transparent;
167
+ margin-bottom: -1px;
168
+ transition: all .2s;
169
+ }
170
+
171
+ .auth-tab.active {
172
+ color: var(--primary);
173
+ border-bottom-color: var(--primary);
174
+ }
175
+
176
+ .auth-panel { display: none; }
177
+ .auth-panel.active { display: block; }
178
+
179
+ .form-group { margin-bottom: 16px; }
180
+ .form-group label {
181
+ display: block;
182
+ font-size: 14px;
183
+ margin-bottom: 6px;
184
+ color: var(--text);
185
+ }
186
+ .form-group input {
187
+ width: 100%;
188
+ padding: 10px 12px;
189
+ border: 1px solid var(--border);
190
+ border-radius: var(--radius);
191
+ font-size: 15px;
192
+ transition: border-color .2s;
193
+ }
194
+ .form-group input:focus {
195
+ outline: none;
196
+ border-color: var(--primary);
197
+ }
198
+
199
+ .btn {
200
+ padding: 10px 18px;
201
+ border: none;
202
+ border-radius: var(--radius);
203
+ font-size: 15px;
204
+ cursor: pointer;
205
+ text-decoration: none;
206
+ transition: background-color .2s, border-color .2s, color .2s, box-shadow .2s;
207
+ display: inline-flex;
208
+ align-items: center;
209
+ gap: 6px;
210
+ }
211
+
212
+ .btn:focus-visible {
213
+ outline: 2px solid var(--primary);
214
+ outline-offset: 2px;
215
+ }
216
+
217
+ .btn svg { width: 16px; height: 16px; flex-shrink: 0; }
218
+
219
+ .btn-primary {
220
+ width: 100%;
221
+ background: var(--primary);
222
+ color: #fff;
223
+ }
224
+ .btn-primary:hover { background: var(--primary-hover); }
225
+
226
+ .btn-small {
227
+ padding: 6px 14px;
228
+ font-size: 13px;
229
+ background: var(--bg);
230
+ color: var(--text);
231
+ border: 1px solid var(--border);
232
+ }
233
+ .btn-small:hover { background: var(--border); }
234
+ .btn-small.active {
235
+ background: var(--primary);
236
+ color: #fff;
237
+ border-color: var(--primary);
238
+ }
239
+ .btn-copy-link { color: var(--primary); border-color: var(--primary); }
240
+ .btn-copy-link:hover { background: var(--primary); color: #fff; }
241
+
242
+ .auth-message {
243
+ margin-top: 12px;
244
+ font-size: 14px;
245
+ text-align: center;
246
+ min-height: 20px;
247
+ }
248
+ .auth-message.error { color: var(--danger); }
249
+ .auth-message.success { color: #16a34a; }
250
+
251
+ /* Layout */
252
+ .layout { min-height: 100vh; }
253
+
254
+ .header {
255
+ display: flex;
256
+ align-items: center;
257
+ justify-content: space-between;
258
+ flex-wrap: wrap;
259
+ gap: 12px;
260
+ padding: 14px 32px;
261
+ background: var(--header-bg);
262
+ backdrop-filter: saturate(180%) blur(12px);
263
+ -webkit-backdrop-filter: saturate(180%) blur(12px);
264
+ border-bottom: 1px solid var(--border);
265
+ position: sticky;
266
+ top: 0;
267
+ z-index: 10;
268
+ }
269
+
270
+ .header-left, .header-right {
271
+ display: flex;
272
+ align-items: center;
273
+ gap: 12px;
274
+ min-width: 0;
275
+ }
276
+
277
+ .brand-mark {
278
+ width: 40px;
279
+ height: 40px;
280
+ border-radius: var(--radius-sm);
281
+ background: linear-gradient(135deg, var(--primary) 0%, #7c3aed 100%);
282
+ color: #fff;
283
+ font-size: 18px;
284
+ font-weight: 700;
285
+ letter-spacing: -0.02em;
286
+ display: flex;
287
+ align-items: center;
288
+ justify-content: center;
289
+ flex-shrink: 0;
290
+ box-shadow: 0 2px 8px rgba(37, 99, 235, 0.35);
291
+ }
292
+
293
+ .brand-text { min-width: 0; }
294
+
295
+ .header h1 { font-size: 1.125rem; font-weight: 600; letter-spacing: -0.02em; }
296
+ .header-tagline {
297
+ margin-top: 2px;
298
+ font-size: 13px;
299
+ color: var(--text-secondary);
300
+ font-weight: 400;
301
+ }
302
+
303
+ .header h2 { font-size: 16px; font-weight: 500; }
304
+
305
+ .user-name {
306
+ color: var(--text-secondary);
307
+ font-size: 14px;
308
+ }
309
+
310
+ .main { max-width: 960px; margin: 0 auto; padding: 28px 32px 48px; }
311
+
312
+ /* Upload */
313
+ .toolbar { margin-bottom: 24px; }
314
+
315
+ .upload-area {
316
+ border: 2px dashed var(--border);
317
+ border-radius: var(--radius);
318
+ padding: 36px 28px;
319
+ text-align: center;
320
+ cursor: pointer;
321
+ transition: border-color .2s, background-color .2s, box-shadow .2s;
322
+ background: var(--card);
323
+ box-shadow: var(--shadow);
324
+ }
325
+
326
+ .upload-area:hover, .upload-area.dragover {
327
+ border-color: var(--primary);
328
+ background: var(--primary-soft);
329
+ box-shadow: 0 4px 20px rgba(37, 99, 235, 0.12);
330
+ }
331
+
332
+ .upload-area:focus-visible {
333
+ outline: none;
334
+ border-color: var(--primary);
335
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.25);
336
+ }
337
+
338
+ .upload-area svg {
339
+ width: 44px;
340
+ height: 44px;
341
+ color: var(--primary);
342
+ opacity: 0.85;
343
+ margin-bottom: 10px;
344
+ }
345
+
346
+ .upload-title { font-size: 16px; font-weight: 600; margin-bottom: 6px; letter-spacing: -0.01em; }
347
+
348
+ .upload-hint {
349
+ font-size: 13px;
350
+ color: var(--text-secondary);
351
+ }
352
+
353
+ /* File list */
354
+ .file-list-header {
355
+ display: flex;
356
+ align-items: center;
357
+ gap: 8px;
358
+ margin-bottom: 12px;
359
+ }
360
+
361
+ .file-list-header h2 { font-size: 16px; flex: 1; }
362
+
363
+ .select-all-wrap { flex-shrink: 0; }
364
+
365
+ .file-count {
366
+ font-size: 13px;
367
+ color: var(--text-secondary);
368
+ }
369
+
370
+ /* File filter bar */
371
+ .file-filter-bar {
372
+ display: flex;
373
+ flex-wrap: wrap;
374
+ align-items: center;
375
+ gap: 12px;
376
+ margin-bottom: 12px;
377
+ }
378
+
379
+ .search-input-wrap {
380
+ position: relative;
381
+ flex: 1;
382
+ min-width: 200px;
383
+ }
384
+
385
+ .search-icon {
386
+ position: absolute;
387
+ left: 12px;
388
+ top: 50%;
389
+ transform: translateY(-50%);
390
+ width: 18px;
391
+ height: 18px;
392
+ color: var(--text-secondary);
393
+ pointer-events: none;
394
+ }
395
+
396
+ #search-input {
397
+ width: 100%;
398
+ padding: 9px 36px 9px 38px;
399
+ border: 1px solid var(--border);
400
+ border-radius: var(--radius);
401
+ font-size: 14px;
402
+ background: var(--card);
403
+ color: var(--text);
404
+ transition: border-color .2s, box-shadow .2s;
405
+ }
406
+
407
+ #search-input:focus {
408
+ outline: none;
409
+ border-color: var(--primary);
410
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.18);
411
+ }
412
+
413
+ #search-input::placeholder {
414
+ color: var(--text-secondary);
415
+ }
416
+
417
+ .search-clear {
418
+ position: absolute;
419
+ right: 8px;
420
+ top: 50%;
421
+ transform: translateY(-50%);
422
+ width: 22px;
423
+ height: 22px;
424
+ padding: 0;
425
+ border: none;
426
+ border-radius: 50%;
427
+ background: var(--border);
428
+ color: var(--text-secondary);
429
+ font-size: 14px;
430
+ line-height: 1;
431
+ cursor: pointer;
432
+ display: flex;
433
+ align-items: center;
434
+ justify-content: center;
435
+ transition: background .15s;
436
+ }
437
+
438
+ .search-clear:hover {
439
+ background: var(--text-secondary);
440
+ color: var(--card);
441
+ }
442
+
443
+ .search-kbd {
444
+ position: absolute;
445
+ right: 10px;
446
+ top: 50%;
447
+ transform: translateY(-50%);
448
+ padding: 1px 6px;
449
+ border: 1px solid var(--border);
450
+ border-radius: 4px;
451
+ background: var(--bg);
452
+ color: var(--text-secondary);
453
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
454
+ font-size: 11px;
455
+ line-height: 1.6;
456
+ pointer-events: none;
457
+ transition: opacity .15s;
458
+ }
459
+
460
+ .filter-chips {
461
+ display: flex;
462
+ align-items: center;
463
+ gap: 6px;
464
+ flex-wrap: wrap;
465
+ }
466
+
467
+ .filter-chip {
468
+ padding: 5px 14px;
469
+ border: 1px solid var(--border);
470
+ border-radius: 999px;
471
+ background: var(--bg);
472
+ color: var(--text-secondary);
473
+ font-size: 12px;
474
+ font-weight: 500;
475
+ cursor: pointer;
476
+ transition: all .15s;
477
+ white-space: nowrap;
478
+ }
479
+
480
+ .filter-chip:hover {
481
+ border-color: var(--text-secondary);
482
+ color: var(--text);
483
+ }
484
+
485
+ .filter-chip.active {
486
+ background: var(--primary);
487
+ color: #fff;
488
+ border-color: var(--primary);
489
+ }
490
+
491
+ .filter-divider {
492
+ width: 1px;
493
+ height: 18px;
494
+ background: var(--border);
495
+ margin: 0 2px;
496
+ }
497
+
498
+ html:not(.light) {
499
+ .search-clear {
500
+ background: #475569;
501
+ color: var(--text-secondary);
502
+ }
503
+ .search-clear:hover {
504
+ background: var(--text-secondary);
505
+ color: var(--card);
506
+ }
507
+ .filter-chip:hover {
508
+ border-color: #64748b;
509
+ }
510
+ }
511
+
512
+ @media (max-width: 640px) {
513
+ .file-filter-bar {
514
+ flex-direction: column;
515
+ align-items: stretch;
516
+ }
517
+ .search-input-wrap {
518
+ min-width: 0;
519
+ }
520
+ }
521
+
522
+ .file-list {
523
+ display: flex;
524
+ flex-direction: column;
525
+ gap: 8px;
526
+ }
527
+
528
+ .pagination {
529
+ display: flex;
530
+ align-items: center;
531
+ justify-content: center;
532
+ gap: 6px;
533
+ padding: 16px 0 8px;
534
+ flex-wrap: wrap;
535
+ }
536
+ .pagination-btn {
537
+ min-width: 36px;
538
+ height: 36px;
539
+ padding: 0 10px;
540
+ border: 1px solid var(--border);
541
+ border-radius: var(--radius);
542
+ background: var(--card);
543
+ color: var(--text);
544
+ font-size: 14px;
545
+ cursor: pointer;
546
+ transition: background .15s, border-color .15s;
547
+ }
548
+ .pagination-btn:hover:not(:disabled) {
549
+ background: var(--border);
550
+ }
551
+ .pagination-btn:disabled {
552
+ opacity: .4;
553
+ cursor: default;
554
+ }
555
+ .pagination-btn.active {
556
+ background: var(--primary);
557
+ color: #fff;
558
+ border-color: var(--primary);
559
+ }
560
+ .pagination-ellipsis {
561
+ padding: 0 6px;
562
+ color: var(--text-secondary);
563
+ user-select: none;
564
+ }
565
+
566
+ /* Custom Checkbox */
567
+ .file-checkbox-wrap {
568
+ display: inline-flex;
569
+ align-items: center;
570
+ cursor: pointer;
571
+ flex-shrink: 0;
572
+ -webkit-user-select: none;
573
+ user-select: none;
574
+ }
575
+ .file-checkbox-wrap input { position: absolute; opacity: 0; width: 0; height: 0; }
576
+ .file-checkbox-visual {
577
+ width: 18px; height: 18px;
578
+ border: 2px solid #cbd5e1;
579
+ border-radius: 4px;
580
+ display: flex; align-items: center; justify-content: center;
581
+ transition: background .15s, border-color .15s;
582
+ position: relative;
583
+ }
584
+ .file-checkbox-wrap input:checked + .file-checkbox-visual {
585
+ background: var(--primary);
586
+ border-color: var(--primary);
587
+ }
588
+ .file-checkbox-wrap input:checked + .file-checkbox-visual::after {
589
+ content: '';
590
+ width: 5px; height: 9px;
591
+ border: solid #fff;
592
+ border-width: 0 2px 2px 0;
593
+ transform: rotate(45deg);
594
+ margin-top: -2px;
595
+ }
596
+ .file-checkbox-wrap input:focus-visible + .file-checkbox-visual {
597
+ outline: 2px solid var(--primary);
598
+ outline-offset: 2px;
599
+ }
600
+ html:not(.light) {
601
+ .file-checkbox-visual { border-color: #475569; }
602
+ }
603
+
604
+ .file-item.selected {
605
+ background: rgba(37, 99, 235, 0.06);
606
+ border-color: var(--primary);
607
+ }
608
+ html:not(.light) {
609
+ .file-item.selected {
610
+ background: rgba(37, 99, 235, 0.15);
611
+ }
612
+ }
613
+
614
+ /* Batch Toolbar */
615
+ .batch-toolbar {
616
+ position: fixed;
617
+ bottom: 24px;
618
+ left: 50%;
619
+ transform: translateX(-50%);
620
+ z-index: 100;
621
+ display: flex;
622
+ align-items: center;
623
+ gap: 8px;
624
+ padding: 10px 16px;
625
+ background: rgba(255, 255, 255, 0.85);
626
+ backdrop-filter: blur(12px);
627
+ -webkit-backdrop-filter: blur(12px);
628
+ border: 1px solid var(--border);
629
+ border-radius: 12px;
630
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12);
631
+ }
632
+ html:not(.light) {
633
+ .batch-toolbar {
634
+ background: rgba(30, 41, 59, 0.9);
635
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
636
+ }
637
+ }
638
+ .batch-toolbar[hidden] {
639
+ display: none;
640
+ }
641
+ .batch-category-select {
642
+ padding: 5px 8px;
643
+ font-size: 12px;
644
+ border: 1px solid var(--border);
645
+ border-radius: var(--radius-sm);
646
+ background: var(--card);
647
+ color: inherit;
648
+ cursor: pointer;
649
+ }
650
+
651
+ .file-item {
652
+ display: flex;
653
+ align-items: center;
654
+ justify-content: space-between;
655
+ gap: 12px;
656
+ padding: 14px 18px;
657
+ background: var(--card);
658
+ border: 1px solid var(--border);
659
+ border-radius: var(--radius);
660
+ transition: box-shadow .2s, border-color .2s, transform .15s ease;
661
+ }
662
+
663
+ .file-item:hover {
664
+ box-shadow: var(--shadow);
665
+ border-color: #cbd5e1;
666
+ }
667
+
668
+ html:not(.light) {
669
+ .file-item:hover {
670
+ border-color: #475569;
671
+ }
672
+ }
673
+
674
+ .file-info {
675
+ display: flex;
676
+ align-items: center;
677
+ gap: 12px;
678
+ flex: 1;
679
+ cursor: pointer;
680
+ min-width: 0;
681
+ border-radius: var(--radius-sm);
682
+ padding: 2px 4px 2px 0;
683
+ margin: -2px 0;
684
+ }
685
+
686
+ .file-info:focus-visible {
687
+ outline: 2px solid var(--primary);
688
+ outline-offset: 2px;
689
+ }
690
+
691
+ .file-icon {
692
+ width: 40px;
693
+ height: 40px;
694
+ border-radius: var(--radius);
695
+ display: flex;
696
+ align-items: center;
697
+ justify-content: center;
698
+ font-size: 12px;
699
+ font-weight: 600;
700
+ flex-shrink: 0;
701
+ }
702
+
703
+ .file-icon.html { background: linear-gradient(145deg, #fef3c7, #fde68a); color: #b45309; }
704
+ .file-icon.md { background: linear-gradient(145deg, #dbeafe, #bfdbfe); color: #1d4ed8; }
705
+
706
+ html:not(.light) {
707
+ .file-icon.html { background: rgba(251, 191, 36, 0.2); color: #fcd34d; }
708
+ .file-icon.md { background: rgba(96, 165, 250, 0.2); color: #93c5fd; }
709
+ }
710
+
711
+ .file-meta { min-width: 0; }
712
+
713
+ .file-name {
714
+ font-size: 15px;
715
+ font-weight: 500;
716
+ white-space: nowrap;
717
+ overflow: hidden;
718
+ text-overflow: ellipsis;
719
+ }
720
+
721
+ .file-subline {
722
+ display: flex;
723
+ align-items: center;
724
+ gap: 6px;
725
+ flex-wrap: wrap;
726
+ margin-top: 2px;
727
+ }
728
+
729
+ .file-snippet {
730
+ font-size: 12px;
731
+ color: var(--text-secondary);
732
+ margin-top: 2px;
733
+ line-height: 1.4;
734
+ overflow: hidden;
735
+ text-overflow: ellipsis;
736
+ display: -webkit-box;
737
+ -webkit-line-clamp: 2;
738
+ -webkit-box-orient: vertical;
739
+ }
740
+ .file-snippet mark {
741
+ background: #fff3b0;
742
+ color: inherit;
743
+ padding: 0 2px;
744
+ border-radius: 2px;
745
+ }
746
+ html:not(.light) {
747
+ .file-snippet mark {
748
+ background: #5c4b00;
749
+ }
750
+ }
751
+
752
+ .file-detail {
753
+ font-size: 12px;
754
+ color: var(--text-secondary);
755
+ }
756
+
757
+ .file-badge {
758
+ display: inline-flex;
759
+ align-items: center;
760
+ padding: 0 7px;
761
+ border-radius: 999px;
762
+ font-size: 11px;
763
+ font-weight: 500;
764
+ line-height: 1.7;
765
+ white-space: nowrap;
766
+ }
767
+
768
+ .file-badge-type {
769
+ background: var(--bg);
770
+ color: var(--text-secondary);
771
+ border: 1px solid var(--border);
772
+ }
773
+
774
+ .file-badge-public {
775
+ background: rgba(22, 163, 74, 0.1);
776
+ color: #16a34a;
777
+ }
778
+
779
+ .file-badge-private {
780
+ background: rgba(220, 38, 38, 0.08);
781
+ color: var(--danger);
782
+ }
783
+
784
+ html:not(.light) {
785
+ .file-badge-public { background: rgba(22, 163, 74, 0.15); }
786
+ .file-badge-private { background: rgba(248, 113, 113, 0.12); }
787
+ }
788
+
789
+ .file-actions {
790
+ display: flex;
791
+ gap: 6px;
792
+ flex-shrink: 0;
793
+ align-items: center;
794
+ }
795
+
796
+ .file-actions .btn {
797
+ padding: 5px 10px;
798
+ font-size: 12px;
799
+ }
800
+
801
+ /* 文件项更多操作下拉 */
802
+ .file-more-dropdown {
803
+ position: relative;
804
+ display: inline-flex;
805
+ }
806
+
807
+ .file-more-trigger {
808
+ min-width: 32px;
809
+ text-align: center;
810
+ font-size: 16px;
811
+ line-height: 1;
812
+ letter-spacing: 1px;
813
+ }
814
+
815
+ .file-more-menu {
816
+ display: none;
817
+ position: absolute;
818
+ top: calc(100% + 6px);
819
+ right: 0;
820
+ min-width: 140px;
821
+ background: var(--card);
822
+ border: 1px solid var(--border);
823
+ border-radius: var(--radius);
824
+ box-shadow: 0 8px 30px rgba(15, 23, 42, 0.15);
825
+ z-index: 100;
826
+ padding: 4px;
827
+ }
828
+
829
+ .file-more-dropdown.open .file-more-menu {
830
+ display: block;
831
+ }
832
+
833
+ .file-more-item {
834
+ display: block;
835
+ width: 100%;
836
+ text-align: left;
837
+ padding: 8px 12px;
838
+ border: none;
839
+ background: none;
840
+ color: var(--text);
841
+ font-size: 13px;
842
+ border-radius: var(--radius-sm);
843
+ cursor: pointer;
844
+ }
845
+
846
+ .file-more-item:hover {
847
+ background: var(--border);
848
+ }
849
+
850
+ .file-more-danger {
851
+ color: var(--danger);
852
+ }
853
+
854
+ .file-more-danger:hover {
855
+ background: rgba(248, 113, 113, 0.1);
856
+ }
857
+
858
+ .file-more-divider {
859
+ border: none;
860
+ border-top: 1px solid var(--border);
861
+ margin: 4px 0;
862
+ }
863
+
864
+ .btn-danger {
865
+ background: #fef2f2;
866
+ color: var(--danger);
867
+ border-color: #fecaca;
868
+ }
869
+ .btn-danger:hover { background: #fee2e2; }
870
+
871
+ .empty-state {
872
+ text-align: center;
873
+ padding: 60px 20px;
874
+ color: var(--text-secondary);
875
+ }
876
+
877
+ .empty-state svg {
878
+ width: 48px;
879
+ height: 48px;
880
+ margin-bottom: 12px;
881
+ opacity: .5;
882
+ }
883
+
884
+ .empty-state-cta {
885
+ display: inline-flex;
886
+ align-items: center;
887
+ gap: 6px;
888
+ margin-top: 16px;
889
+ padding: 8px 20px;
890
+ background: var(--primary);
891
+ color: #fff;
892
+ border: none;
893
+ border-radius: var(--radius);
894
+ font-size: 14px;
895
+ font-weight: 500;
896
+ cursor: pointer;
897
+ transition: background .2s;
898
+ }
899
+
900
+ .empty-state-cta:hover { background: var(--primary-hover); }
901
+ .empty-state-cta svg { width: 16px; height: 16px; }
902
+
903
+ /* Preview */
904
+ .preview-layout {
905
+ position: relative;
906
+ display: flex;
907
+ flex-direction: column;
908
+ height: 100vh;
909
+ }
910
+
911
+ .preview-layout .header { flex-shrink: 0; }
912
+
913
+ .header.header-preview {
914
+ padding: 6px max(12px, env(safe-area-inset-left, 0px)) 8px max(12px, env(safe-area-inset-right, 0px));
915
+ align-items: stretch;
916
+ }
917
+
918
+ .header-preview {
919
+ transition: max-height .32s ease, opacity .22s ease, padding .22s ease, border-width .22s ease;
920
+ max-height: 200px;
921
+ opacity: 1;
922
+ overflow: visible;
923
+ }
924
+
925
+ .preview-topbar {
926
+ width: 100%;
927
+ max-width: 960px;
928
+ margin: 0 auto;
929
+ }
930
+
931
+ /* 仅标题行:点标题展开完整工具栏 */
932
+ .preview-title-strip {
933
+ display: none;
934
+ align-items: center;
935
+ gap: 8px;
936
+ width: 100%;
937
+ padding: 6px 12px;
938
+ border: 1px solid var(--border);
939
+ border-radius: var(--radius);
940
+ background: var(--card);
941
+ color: var(--text);
942
+ font: inherit;
943
+ text-align: left;
944
+ cursor: pointer;
945
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
946
+ transition: border-color .15s ease, box-shadow .15s ease;
947
+ }
948
+
949
+ .preview-title-strip:hover {
950
+ border-color: #cbd5e1;
951
+ box-shadow: 0 2px 8px rgba(15, 23, 42, 0.06);
952
+ }
953
+
954
+ .preview-title-strip:focus-visible {
955
+ outline: 2px solid var(--primary);
956
+ outline-offset: 2px;
957
+ }
958
+
959
+ .preview-title-text {
960
+ flex: 1;
961
+ min-width: 0;
962
+ overflow: hidden;
963
+ text-overflow: ellipsis;
964
+ white-space: nowrap;
965
+ font-size: 14px;
966
+ font-weight: 600;
967
+ letter-spacing: -0.02em;
968
+ }
969
+
970
+ .preview-title-strip-hint {
971
+ flex-shrink: 0;
972
+ width: 18px;
973
+ height: 18px;
974
+ opacity: 0.55;
975
+ transition: transform .2s ease, opacity .2s ease;
976
+ }
977
+
978
+ .preview-title-strip:hover .preview-title-strip-hint {
979
+ transform: translateY(2px);
980
+ opacity: 0.8;
981
+ }
982
+
983
+ .preview-toolbar-expanded {
984
+ display: block;
985
+ }
986
+
987
+ .preview-expanded-row {
988
+ display: flex;
989
+ flex-wrap: wrap;
990
+ align-items: center;
991
+ justify-content: space-between;
992
+ gap: 10px;
993
+ }
994
+
995
+ .preview-expanded-left {
996
+ display: flex;
997
+ align-items: center;
998
+ gap: 10px;
999
+ flex: 1;
1000
+ min-width: 0;
1001
+ }
1002
+
1003
+ .preview-expanded-right {
1004
+ display: flex;
1005
+ flex-wrap: wrap;
1006
+ align-items: center;
1007
+ gap: 6px;
1008
+ flex-shrink: 0;
1009
+ }
1010
+
1011
+ .header-preview #btn-back {
1012
+ position: relative;
1013
+ }
1014
+
1015
+ .preview-heading-truncate {
1016
+ min-width: 0;
1017
+ flex: 1;
1018
+ overflow: hidden;
1019
+ text-overflow: ellipsis;
1020
+ white-space: nowrap;
1021
+ font-size: 15px;
1022
+ font-weight: 600;
1023
+ letter-spacing: -0.02em;
1024
+ }
1025
+
1026
+ .btn-header-fold {
1027
+ padding: 6px 10px;
1028
+ min-width: 36px;
1029
+ justify-content: center;
1030
+ }
1031
+
1032
+ .btn-header-fold .icon-fold-chevron {
1033
+ width: 18px;
1034
+ height: 18px;
1035
+ display: block;
1036
+ }
1037
+
1038
+ .btn-header-fold .icon-fold-chevron--down { display: none; }
1039
+
1040
+ .preview-layout.preview-header-collapsed .btn-header-fold .icon-fold-chevron--up {
1041
+ display: none;
1042
+ }
1043
+
1044
+ .preview-layout.preview-header-collapsed .btn-header-fold .icon-fold-chevron--down {
1045
+ display: block;
1046
+ }
1047
+
1048
+ .view-toggle { display: flex; gap: 4px; }
1049
+
1050
+ /* Preview more dropdown */
1051
+ .preview-more-dropdown {
1052
+ position: relative;
1053
+ display: inline-flex;
1054
+ }
1055
+
1056
+ .preview-more-trigger {
1057
+ display: inline-flex;
1058
+ align-items: center;
1059
+ justify-content: center;
1060
+ padding: 4px 8px;
1061
+ min-width: 32px;
1062
+ }
1063
+
1064
+ .preview-more-menu {
1065
+ display: none;
1066
+ position: absolute;
1067
+ top: calc(100% + 6px);
1068
+ right: 0;
1069
+ min-width: 160px;
1070
+ background: var(--card);
1071
+ border: 1px solid var(--border);
1072
+ border-radius: var(--radius);
1073
+ box-shadow: 0 8px 30px rgba(15, 23, 42, 0.15);
1074
+ z-index: 100;
1075
+ padding: 4px;
1076
+ }
1077
+
1078
+ .preview-more-dropdown.open .preview-more-menu {
1079
+ display: block;
1080
+ }
1081
+
1082
+ /* 收起:顶栏完全折叠不占高度,仅保留屏幕顶部悬浮「展开」 */
1083
+ .preview-layout.preview-header-collapsed .header.header-preview {
1084
+ flex-shrink: 1;
1085
+ flex-grow: 0;
1086
+ flex-basis: 0;
1087
+ min-height: 0;
1088
+ max-height: 0;
1089
+ padding-top: 0;
1090
+ padding-bottom: 0;
1091
+ margin: 0;
1092
+ border-bottom-width: 0;
1093
+ border-bottom-color: transparent;
1094
+ opacity: 0;
1095
+ pointer-events: none;
1096
+ overflow: hidden;
1097
+ }
1098
+
1099
+ .preview-expand-floating {
1100
+ display: none;
1101
+ position: fixed;
1102
+ top: max(6px, env(safe-area-inset-top, 0px));
1103
+ left: 50%;
1104
+ transform: translateX(-50%);
1105
+ z-index: 1000;
1106
+ align-items: center;
1107
+ justify-content: center;
1108
+ width: 36px;
1109
+ height: 28px;
1110
+ padding: 0;
1111
+ border: 1px solid var(--border);
1112
+ border-radius: 0 0 10px 10px;
1113
+ border-top: none;
1114
+ background: var(--header-bg);
1115
+ backdrop-filter: saturate(180%) blur(12px);
1116
+ -webkit-backdrop-filter: saturate(180%) blur(12px);
1117
+ color: var(--text);
1118
+ cursor: pointer;
1119
+ box-shadow: 0 2px 10px rgba(15, 23, 42, 0.08);
1120
+ }
1121
+
1122
+ .preview-expand-floating svg {
1123
+ width: 16px;
1124
+ height: 16px;
1125
+ opacity: 0.8;
1126
+ }
1127
+
1128
+ .preview-expand-floating:focus-visible {
1129
+ outline: 2px solid var(--primary);
1130
+ outline-offset: 2px;
1131
+ }
1132
+
1133
+ .preview-layout.preview-header-collapsed .preview-expand-floating {
1134
+ display: flex;
1135
+ }
1136
+
1137
+ .preview-container {
1138
+ flex: 1;
1139
+ position: relative;
1140
+ overflow: hidden;
1141
+ }
1142
+
1143
+ #preview-iframe {
1144
+ width: 100%;
1145
+ height: 100%;
1146
+ border: none;
1147
+ background: #fff;
1148
+ }
1149
+
1150
+ .source-view {
1151
+ display: none;
1152
+ width: 100%;
1153
+ height: 100%;
1154
+ margin: 0;
1155
+ padding: 20px 32px;
1156
+ background: #1e1e1e;
1157
+ color: #d4d4d4;
1158
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
1159
+ font-size: 14px;
1160
+ line-height: 1.6;
1161
+ overflow: auto;
1162
+ white-space: pre-wrap;
1163
+ word-break: break-all;
1164
+ }
1165
+
1166
+ .source-view.active { display: block; }
1167
+
1168
+ /* Bundle 源码视图:左侧文件树 + 右侧代码 */
1169
+ .source-wrap {
1170
+ width: 100%;
1171
+ height: 100%;
1172
+ }
1173
+ .source-wrap.bundle-layout {
1174
+ display: flex;
1175
+ align-items: stretch;
1176
+ }
1177
+ .source-wrap.bundle-layout .source-view {
1178
+ width: auto;
1179
+ flex: 1;
1180
+ min-width: 0;
1181
+ }
1182
+
1183
+ .bundle-tree {
1184
+ display: flex;
1185
+ flex-direction: column;
1186
+ width: 240px;
1187
+ flex-shrink: 0;
1188
+ background: #252526;
1189
+ border-right: 1px solid #3c3c3c;
1190
+ color: #cccccc;
1191
+ font-size: 13px;
1192
+ overflow: hidden;
1193
+ }
1194
+ .bundle-tree[hidden] { display: none; }
1195
+
1196
+ .bundle-tree-header {
1197
+ display: flex;
1198
+ align-items: center;
1199
+ justify-content: space-between;
1200
+ padding: 10px 12px;
1201
+ border-bottom: 1px solid #3c3c3c;
1202
+ flex-shrink: 0;
1203
+ }
1204
+ .bundle-tree-title {
1205
+ font-weight: 600;
1206
+ color: #d4d4d4;
1207
+ }
1208
+ .bundle-tree-count {
1209
+ font-size: 11px;
1210
+ color: #858585;
1211
+ }
1212
+
1213
+ .bundle-tree-body {
1214
+ flex: 1;
1215
+ overflow: auto;
1216
+ padding: 6px 0;
1217
+ }
1218
+
1219
+ .bundle-node { user-select: none; }
1220
+ .bundle-children { padding-left: 14px; }
1221
+ .bundle-node-row {
1222
+ display: flex;
1223
+ align-items: center;
1224
+ gap: 6px;
1225
+ width: 100%;
1226
+ padding: 4px 12px;
1227
+ border: none;
1228
+ background: none;
1229
+ color: inherit;
1230
+ font: inherit;
1231
+ text-align: left;
1232
+ cursor: pointer;
1233
+ border-radius: 0;
1234
+ }
1235
+ .bundle-node-row:hover { background: rgba(255, 255, 255, 0.06); }
1236
+ .bundle-node-row:focus-visible {
1237
+ outline: 2px solid var(--primary);
1238
+ outline-offset: -2px;
1239
+ }
1240
+ .bundle-file-row.active {
1241
+ background: rgba(56, 139, 253, 0.18);
1242
+ color: #ffffff;
1243
+ }
1244
+ .bundle-chevron {
1245
+ width: 12px;
1246
+ height: 12px;
1247
+ flex-shrink: 0;
1248
+ transition: transform 0.15s ease;
1249
+ }
1250
+ .bundle-dir:not(.expanded) .bundle-chevron { transform: rotate(-90deg); }
1251
+ .bundle-dir:not(.expanded) > .bundle-children { display: none; }
1252
+ .bundle-file-ic { font-size: 12px; flex-shrink: 0; }
1253
+ .bundle-name {
1254
+ overflow: hidden;
1255
+ text-overflow: ellipsis;
1256
+ white-space: nowrap;
1257
+ }
1258
+
1259
+ @media (max-width: 768px) {
1260
+ .bundle-tree { width: 180px; }
1261
+ }
1262
+
1263
+ /* Toast */
1264
+ .toast {
1265
+ position: fixed;
1266
+ top: 20px;
1267
+ right: 20px;
1268
+ padding: 12px 20px;
1269
+ border-radius: var(--radius);
1270
+ background: var(--card);
1271
+ box-shadow: var(--shadow);
1272
+ font-size: 14px;
1273
+ z-index: 100;
1274
+ animation: slideIn .3s ease;
1275
+ border-left: 4px solid var(--primary);
1276
+ max-width: min(360px, calc(100vw - 40px));
1277
+ }
1278
+
1279
+ @media (prefers-reduced-motion: reduce) {
1280
+ .toast { animation: none; }
1281
+ .file-item { transition: none; }
1282
+ .upload-area { transition: none; }
1283
+ .header-preview { transition: none; }
1284
+ }
1285
+
1286
+ .toast.error { border-left-color: var(--danger); }
1287
+
1288
+ @media (max-width: 600px) {
1289
+ .toast {
1290
+ top: auto;
1291
+ right: auto;
1292
+ bottom: 24px;
1293
+ left: 50%;
1294
+ transform: translateX(-50%);
1295
+ border-left: none;
1296
+ border-radius: 20px;
1297
+ text-align: center;
1298
+ }
1299
+ .toast.error { border-left: none; border-bottom: 3px solid var(--danger); background: rgba(220, 38, 38, 0.08); }
1300
+ }
1301
+
1302
+ @keyframes slideIn {
1303
+ from { transform: translateX(100%); opacity: 0; }
1304
+ to { transform: translateX(0); opacity: 1; }
1305
+ }
1306
+
1307
+ /* List loading */
1308
+ .file-list.is-loading {
1309
+ min-height: 120px;
1310
+ display: flex;
1311
+ flex-direction: column;
1312
+ gap: 10px;
1313
+ padding: 16px;
1314
+ background: var(--card);
1315
+ border: 1px dashed var(--border);
1316
+ border-radius: var(--radius);
1317
+ }
1318
+
1319
+ /* Skeleton */
1320
+ .skeleton-item {
1321
+ display: flex;
1322
+ align-items: center;
1323
+ gap: 12px;
1324
+ padding: 14px 18px;
1325
+ border-radius: var(--radius);
1326
+ }
1327
+
1328
+ .skeleton-icon {
1329
+ width: 40px;
1330
+ height: 40px;
1331
+ border-radius: var(--radius);
1332
+ background: linear-gradient(90deg, var(--border) 25%, rgba(0,0,0,.06) 50%, var(--border) 75%);
1333
+ background-size: 200% 100%;
1334
+ animation: shimmer 1.5s infinite;
1335
+ flex-shrink: 0;
1336
+ }
1337
+
1338
+ .skeleton-lines { flex: 1; }
1339
+
1340
+ .skeleton-line {
1341
+ height: 14px;
1342
+ border-radius: 4px;
1343
+ background: linear-gradient(90deg, var(--border) 25%, rgba(0,0,0,.06) 50%, var(--border) 75%);
1344
+ background-size: 200% 100%;
1345
+ animation: shimmer 1.5s infinite;
1346
+ margin-bottom: 8px;
1347
+ }
1348
+
1349
+ .skeleton-line:last-child { margin-bottom: 0; }
1350
+ .skeleton-w60 { width: 60%; }
1351
+ .skeleton-w40 { width: 40%; }
1352
+
1353
+ @keyframes shimmer {
1354
+ 0% { background-position: 200% 0; }
1355
+ 100% { background-position: -200% 0; }
1356
+ }
1357
+
1358
+ /* Upload progress */
1359
+ .upload-progress {
1360
+ margin-top: 10px;
1361
+ height: 24px;
1362
+ position: relative;
1363
+ background: var(--bg);
1364
+ border-radius: 999px;
1365
+ overflow: hidden;
1366
+ border: 1px solid var(--border);
1367
+ }
1368
+
1369
+ .upload-progress-bar {
1370
+ height: 100%;
1371
+ background: linear-gradient(90deg, var(--primary), #7c3aed);
1372
+ border-radius: 999px;
1373
+ width: 0%;
1374
+ transition: width .2s ease;
1375
+ }
1376
+
1377
+ .upload-progress-text {
1378
+ position: absolute;
1379
+ inset: 0;
1380
+ display: flex;
1381
+ align-items: center;
1382
+ justify-content: center;
1383
+ font-size: 12px;
1384
+ font-weight: 600;
1385
+ color: var(--text);
1386
+ mix-blend-mode: difference;
1387
+ }
1388
+
1389
+ /* Preview spinner */
1390
+ .preview-spinner {
1391
+ position: absolute;
1392
+ inset: 0;
1393
+ display: flex;
1394
+ flex-direction: column;
1395
+ align-items: center;
1396
+ justify-content: center;
1397
+ gap: 12px;
1398
+ background: var(--card);
1399
+ z-index: 5;
1400
+ color: var(--text-secondary);
1401
+ font-size: 14px;
1402
+ }
1403
+
1404
+ .spinner {
1405
+ width: 32px;
1406
+ height: 32px;
1407
+ border: 3px solid var(--border);
1408
+ border-top-color: var(--primary);
1409
+ border-radius: 50%;
1410
+ animation: spin .8s linear infinite;
1411
+ }
1412
+
1413
+ @keyframes spin {
1414
+ to { transform: rotate(360deg); }
1415
+ }
1416
+
1417
+ @media (prefers-reduced-motion: reduce) {
1418
+ .skeleton-icon, .skeleton-line { animation: none; }
1419
+ .spinner { animation: none; }
1420
+ }
1421
+
1422
+ /* Responsive */
1423
+ @media (max-width: 768px) {
1424
+ .file-actions { flex-wrap: wrap; }
1425
+ .file-item { flex-wrap: wrap; }
1426
+ }
1427
+
1428
+ @media (max-width: 640px) {
1429
+ .header { padding: 12px 16px; }
1430
+ .main { padding: 16px 16px 40px; }
1431
+ .auth-box { padding: 28px 20px; }
1432
+ .source-view { padding: 16px; }
1433
+ .brand-mark { width: 36px; height: 36px; font-size: 12px; }
1434
+ .upload-area { padding: 28px 18px; }
1435
+ .preview-expanded-left {
1436
+ width: 100%;
1437
+ }
1438
+ .preview-expanded-right {
1439
+ width: 100%;
1440
+ justify-content: flex-end;
1441
+ }
1442
+ .batch-toolbar {
1443
+ left: 12px;
1444
+ right: 12px;
1445
+ transform: none;
1446
+ flex-wrap: wrap;
1447
+ justify-content: center;
1448
+ bottom: 12px;
1449
+ }
1450
+ }
1451
+
1452
+ /* Login */
1453
+ .login-layout {
1454
+ min-height: 100vh;
1455
+ display: flex;
1456
+ align-items: center;
1457
+ justify-content: center;
1458
+ padding: 24px 16px;
1459
+ }
1460
+
1461
+ .login-main {
1462
+ width: 100%;
1463
+ max-width: 400px;
1464
+ background: var(--card);
1465
+ border-radius: var(--radius);
1466
+ box-shadow: var(--shadow);
1467
+ padding: 32px 28px;
1468
+ }
1469
+
1470
+ .login-form {
1471
+ display: flex;
1472
+ flex-direction: column;
1473
+ gap: 14px;
1474
+ }
1475
+
1476
+ .login-form.auth-panel:not(.active) { display: none; }
1477
+ .login-form.auth-panel.active { display: flex; }
1478
+
1479
+ .login-brand {
1480
+ text-align: center;
1481
+ margin-bottom: 4px;
1482
+ display: flex;
1483
+ flex-direction: column;
1484
+ align-items: center;
1485
+ gap: 8px;
1486
+ }
1487
+
1488
+ .login-brand .brand-mark {
1489
+ width: 40px;
1490
+ height: 40px;
1491
+ font-size: 18px;
1492
+ }
1493
+
1494
+ .login-title {
1495
+ font-size: 22px;
1496
+ font-weight: 600;
1497
+ letter-spacing: -0.02em;
1498
+ margin: 0;
1499
+ }
1500
+
1501
+ .login-subtitle {
1502
+ font-size: 13px;
1503
+ color: var(--text-secondary);
1504
+ margin: 0;
1505
+ }
1506
+
1507
+ .login-field {
1508
+ display: flex;
1509
+ flex-direction: column;
1510
+ gap: 6px;
1511
+ }
1512
+
1513
+ .login-field span {
1514
+ font-size: 13px;
1515
+ color: var(--text-secondary);
1516
+ }
1517
+
1518
+ .login-field input {
1519
+ width: 100%;
1520
+ padding: 10px 12px;
1521
+ border: 1px solid var(--border);
1522
+ border-radius: var(--radius);
1523
+ font-size: 15px;
1524
+ background: var(--card);
1525
+ color: var(--text);
1526
+ transition: border-color .2s, box-shadow .2s;
1527
+ }
1528
+
1529
+ .login-field select,
1530
+ .login-field textarea {
1531
+ width: 100%;
1532
+ padding: 10px 12px;
1533
+ border: 1px solid var(--border);
1534
+ border-radius: var(--radius);
1535
+ font-size: 15px;
1536
+ background: var(--card);
1537
+ color: var(--text);
1538
+ transition: border-color .2s, box-shadow .2s;
1539
+ }
1540
+
1541
+ .login-field select:focus,
1542
+ .login-field textarea:focus {
1543
+ outline: none;
1544
+ border-color: var(--primary);
1545
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.18);
1546
+ }
1547
+
1548
+ .login-error {
1549
+ background: rgba(220, 38, 38, 0.08);
1550
+ color: var(--danger);
1551
+ border: 1px solid rgba(220, 38, 38, 0.25);
1552
+ border-radius: var(--radius-sm);
1553
+ padding: 8px 12px;
1554
+ font-size: 13px;
1555
+ }
1556
+
1557
+ .register-code-row {
1558
+ display: flex;
1559
+ gap: 8px;
1560
+ align-items: center;
1561
+ }
1562
+ .register-code-row input {
1563
+ flex: 1;
1564
+ padding: 10px 12px;
1565
+ border: 1px solid var(--border);
1566
+ border-radius: var(--radius);
1567
+ font-size: 15px;
1568
+ background: var(--card);
1569
+ color: var(--text);
1570
+ letter-spacing: 4px;
1571
+ text-align: center;
1572
+ transition: border-color .2s, box-shadow .2s;
1573
+ }
1574
+ .register-code-row input:focus {
1575
+ outline: none;
1576
+ border-color: var(--primary);
1577
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.18);
1578
+ }
1579
+ .register-code-row .btn-small {
1580
+ white-space: nowrap;
1581
+ min-width: 100px;
1582
+ }
1583
+ .register-code-tip {
1584
+ font-size: 13px;
1585
+ color: var(--muted);
1586
+ margin-top: 4px;
1587
+ animation: tipFadeIn .3s ease;
1588
+ }
1589
+ .register-username-hint {
1590
+ font-size: 13px;
1591
+ color: var(--danger);
1592
+ margin-top: 4px;
1593
+ animation: tipFadeIn .3s ease;
1594
+ }
1595
+ @keyframes tipFadeIn {
1596
+ from { opacity: 0; transform: translateY(-4px); }
1597
+ to { opacity: 1; transform: translateY(0); }
1598
+ }
1599
+
1600
+ .login-submit {
1601
+ margin-top: 4px;
1602
+ width: 100%;
1603
+ background: var(--primary);
1604
+ color: #fff;
1605
+ border: none;
1606
+ padding: 11px 18px;
1607
+ font-size: 15px;
1608
+ font-weight: 500;
1609
+ justify-content: center;
1610
+ }
1611
+
1612
+ .login-submit:hover { background: var(--primary-hover); }
1613
+ .login-submit:disabled { opacity: 0.6; cursor: not-allowed; }
1614
+
1615
+ html:not(.light) {
1616
+ .login-error {
1617
+ background: rgba(248, 113, 113, 0.12);
1618
+ border-color: rgba(248, 113, 113, 0.35);
1619
+ }
1620
+ }
1621
+
1622
+ /* Header right (user + logout) */
1623
+ .header-right {
1624
+ display: flex;
1625
+ align-items: center;
1626
+ gap: 10px;
1627
+ flex-shrink: 0;
1628
+ }
1629
+
1630
+ /* Settings dropdown */
1631
+ .settings-dropdown {
1632
+ position: relative;
1633
+ display: inline-flex;
1634
+ }
1635
+
1636
+ .btn-settings {
1637
+ display: inline-flex;
1638
+ align-items: center;
1639
+ gap: 5px;
1640
+ }
1641
+
1642
+ .btn-settings svg {
1643
+ width: 16px;
1644
+ height: 16px;
1645
+ transition: transform .3s ease;
1646
+ }
1647
+
1648
+ .settings-dropdown.open .btn-settings svg {
1649
+ transform: rotate(60deg);
1650
+ }
1651
+
1652
+ .settings-menu {
1653
+ display: none;
1654
+ position: absolute;
1655
+ top: calc(100% + 6px);
1656
+ right: 0;
1657
+ min-width: 160px;
1658
+ background: var(--card);
1659
+ border: 1px solid var(--border);
1660
+ border-radius: var(--radius);
1661
+ box-shadow: 0 8px 30px rgba(15, 23, 42, 0.15);
1662
+ z-index: 100;
1663
+ padding: 4px;
1664
+ }
1665
+
1666
+ .settings-dropdown.open .settings-menu {
1667
+ display: block;
1668
+ }
1669
+
1670
+ .settings-menu-item {
1671
+ display: flex;
1672
+ align-items: center;
1673
+ gap: 8px;
1674
+ width: 100%;
1675
+ padding: 9px 12px;
1676
+ border: none;
1677
+ border-radius: var(--radius-sm);
1678
+ background: none;
1679
+ color: var(--text);
1680
+ font-size: 13px;
1681
+ cursor: pointer;
1682
+ transition: background .15s;
1683
+ }
1684
+
1685
+ .settings-menu-item:hover {
1686
+ background: var(--bg);
1687
+ }
1688
+
1689
+ .settings-menu-item svg {
1690
+ width: 15px;
1691
+ height: 15px;
1692
+ opacity: 0.6;
1693
+ flex-shrink: 0;
1694
+ }
1695
+
1696
+ .settings-menu-item:hover svg {
1697
+ opacity: 1;
1698
+ }
1699
+
1700
+ .header-user {
1701
+ font-size: 13px;
1702
+ color: var(--text-secondary);
1703
+ padding: 2px 8px;
1704
+ background: var(--bg);
1705
+ border-radius: 999px;
1706
+ border: 1px solid var(--border);
1707
+ }
1708
+
1709
+ html:not(.light) {
1710
+ .header-user {
1711
+ background: rgba(255, 255, 255, 0.05);
1712
+ }
1713
+ }
1714
+
1715
+ /* Upload privacy checkbox row */
1716
+ .upload-privacy {
1717
+ display: flex;
1718
+ align-items: center;
1719
+ gap: 8px;
1720
+ margin-top: 10px;
1721
+ padding: 8px 12px;
1722
+ font-size: 13px;
1723
+ color: var(--text-secondary);
1724
+ cursor: pointer;
1725
+ user-select: none;
1726
+ }
1727
+
1728
+ .upload-privacy input {
1729
+ width: 16px;
1730
+ height: 16px;
1731
+ cursor: pointer;
1732
+ accent-color: var(--primary);
1733
+ }
1734
+
1735
+ .upload-privacy:hover { color: var(--text); }
1736
+
1737
+ /* Lock badge */
1738
+ .file-lock {
1739
+ display: inline-block;
1740
+ margin-right: 4px;
1741
+ font-size: 0.9em;
1742
+ filter: saturate(0.9);
1743
+ }
1744
+
1745
+ /* Skills section */
1746
+ .skills-section {
1747
+ margin-top: 40px;
1748
+ padding-top: 24px;
1749
+ border-top: 1px solid var(--border);
1750
+ }
1751
+
1752
+ .skills-header {
1753
+ display: flex;
1754
+ align-items: center;
1755
+ justify-content: space-between;
1756
+ margin-bottom: 6px;
1757
+ }
1758
+
1759
+ .skills-header h2 { font-size: 16px; }
1760
+
1761
+ .skills-count { font-size: 13px; color: var(--text-secondary); }
1762
+
1763
+ .skills-hint {
1764
+ font-size: 13px;
1765
+ color: var(--text-secondary);
1766
+ margin: 0 0 14px;
1767
+ line-height: 1.6;
1768
+ }
1769
+
1770
+ .skills-list {
1771
+ display: flex;
1772
+ flex-direction: column;
1773
+ gap: 10px;
1774
+ }
1775
+
1776
+ .skills-list.is-loading {
1777
+ color: var(--text-secondary);
1778
+ font-size: 13px;
1779
+ padding: 12px 0;
1780
+ }
1781
+
1782
+ .skill-card {
1783
+ display: flex;
1784
+ align-items: center;
1785
+ justify-content: space-between;
1786
+ gap: 16px;
1787
+ padding: 14px 16px;
1788
+ background: var(--card);
1789
+ border: 1px solid var(--border);
1790
+ border-radius: var(--radius);
1791
+ box-shadow: var(--shadow);
1792
+ }
1793
+
1794
+ .skill-card-info { flex: 1; min-width: 0; }
1795
+
1796
+ .skill-card-title {
1797
+ display: flex;
1798
+ align-items: baseline;
1799
+ gap: 10px;
1800
+ margin-bottom: 4px;
1801
+ }
1802
+
1803
+ .skill-name { font-weight: 600; font-size: 15px; color: var(--text); }
1804
+
1805
+ .skill-version {
1806
+ font-size: 12px;
1807
+ color: var(--text-secondary);
1808
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1809
+ }
1810
+
1811
+ .skill-card-desc {
1812
+ font-size: 13px;
1813
+ color: var(--text-secondary);
1814
+ line-height: 1.5;
1815
+ margin: 0 0 6px;
1816
+ display: -webkit-box;
1817
+ -webkit-line-clamp: 2;
1818
+ -webkit-box-orient: vertical;
1819
+ overflow: hidden;
1820
+ }
1821
+
1822
+ .skill-card-meta { font-size: 12px; color: var(--text-secondary); }
1823
+
1824
+ .skill-card-actions { display: flex; gap: 8px; flex-shrink: 0; }
1825
+
1826
+ .empty-state-sm { padding: 24px 16px; }
1827
+ .empty-state-sm svg { width: 28px; height: 28px; }
1828
+ .empty-state-sm p { font-size: 13px; }
1829
+
1830
+ /* Modal */
1831
+ .modal-backdrop[hidden] { display: none; }
1832
+
1833
+ .modal-backdrop {
1834
+ position: fixed;
1835
+ inset: 0;
1836
+ background: rgba(15, 23, 42, 0.5);
1837
+ display: flex;
1838
+ align-items: center;
1839
+ justify-content: center;
1840
+ z-index: 1000;
1841
+ padding: 24px;
1842
+ }
1843
+
1844
+ .modal-panel {
1845
+ background: var(--card);
1846
+ border-radius: var(--radius);
1847
+ box-shadow: 0 20px 60px rgba(15, 23, 42, 0.25);
1848
+ max-width: 720px;
1849
+ width: 100%;
1850
+ max-height: 80vh;
1851
+ display: flex;
1852
+ flex-direction: column;
1853
+ overflow: hidden;
1854
+ }
1855
+
1856
+ .modal-header {
1857
+ display: flex;
1858
+ align-items: center;
1859
+ justify-content: space-between;
1860
+ padding: 16px 20px;
1861
+ border-bottom: 1px solid var(--border);
1862
+ }
1863
+
1864
+ .modal-header h2 { font-size: 16px; margin: 0; }
1865
+
1866
+ .modal-close {
1867
+ width: 28px;
1868
+ height: 28px;
1869
+ padding: 0;
1870
+ font-size: 18px;
1871
+ line-height: 28px;
1872
+ justify-content: center;
1873
+ }
1874
+
1875
+ .modal-body {
1876
+ padding: 20px;
1877
+ overflow-y: auto;
1878
+ flex: 1;
1879
+ }
1880
+
1881
+ .modal-body h3 {
1882
+ font-size: 13px;
1883
+ font-weight: 600;
1884
+ color: var(--text-secondary);
1885
+ text-transform: uppercase;
1886
+ letter-spacing: 0.04em;
1887
+ margin: 18px 0 8px;
1888
+ }
1889
+
1890
+ .modal-body h3:first-child { margin-top: 0; }
1891
+
1892
+ .skill-modal-meta {
1893
+ display: flex;
1894
+ flex-direction: column;
1895
+ gap: 4px;
1896
+ font-size: 13px;
1897
+ line-height: 1.6;
1898
+ }
1899
+
1900
+ .skill-modal-meta code {
1901
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1902
+ font-size: 12px;
1903
+ background: var(--bg);
1904
+ padding: 1px 6px;
1905
+ border-radius: 4px;
1906
+ }
1907
+
1908
+ .skill-file-tree {
1909
+ list-style: none;
1910
+ padding: 10px 14px;
1911
+ margin: 0;
1912
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1913
+ font-size: 12px;
1914
+ background: var(--bg);
1915
+ border-radius: var(--radius-sm);
1916
+ }
1917
+
1918
+ .skill-file-tree li { padding: 2px 0; }
1919
+
1920
+ .skill-modal-source {
1921
+ background: var(--bg);
1922
+ border-radius: var(--radius-sm);
1923
+ padding: 12px 14px;
1924
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1925
+ font-size: 12px;
1926
+ line-height: 1.55;
1927
+ white-space: pre-wrap;
1928
+ word-break: break-word;
1929
+ max-height: 320px;
1930
+ overflow-y: auto;
1931
+ margin: 0;
1932
+ }
1933
+
1934
+ .skill-modal-source code { font-family: inherit; }
1935
+
1936
+ .skill-install-rendered {
1937
+ font-size: 13px;
1938
+ line-height: 1.65;
1939
+ color: var(--text);
1940
+ }
1941
+
1942
+ .skill-install-rendered h3 { margin-top: 0; }
1943
+ .skill-install-rendered h4,
1944
+ .skill-install-rendered h5 {
1945
+ font-size: 14px;
1946
+ font-weight: 600;
1947
+ margin: 16px 0 8px;
1948
+ color: var(--text);
1949
+ text-transform: none;
1950
+ letter-spacing: 0;
1951
+ }
1952
+
1953
+ .skill-install-rendered h5 { font-size: 13px; color: var(--text-secondary); }
1954
+
1955
+ .skill-install-rendered p { margin: 0 0 10px; }
1956
+
1957
+ .skill-install-rendered ul,
1958
+ .skill-install-rendered ol { margin: 0 0 12px; padding-left: 22px; }
1959
+
1960
+ .skill-install-rendered li { margin: 3px 0; }
1961
+
1962
+ .skill-install-rendered pre {
1963
+ background: var(--bg);
1964
+ border-radius: var(--radius-sm);
1965
+ padding: 10px 12px;
1966
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1967
+ font-size: 12px;
1968
+ line-height: 1.55;
1969
+ white-space: pre-wrap;
1970
+ word-break: break-word;
1971
+ overflow-x: auto;
1972
+ margin: 0 0 12px;
1973
+ }
1974
+
1975
+ .skill-install-rendered code {
1976
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1977
+ font-size: 0.92em;
1978
+ background: var(--bg);
1979
+ padding: 1px 5px;
1980
+ border-radius: 3px;
1981
+ }
1982
+
1983
+ .skill-install-rendered pre code { background: none; padding: 0; font-size: inherit; }
1984
+
1985
+ .modal-footer {
1986
+ display: flex;
1987
+ justify-content: flex-end;
1988
+ gap: 8px;
1989
+ padding: 14px 20px;
1990
+ border-top: 1px solid var(--border);
1991
+ background: var(--bg);
1992
+ }
1993
+ .modal-footer .btn-primary { width: auto; }
1994
+ .modal-footer .btn { flex-shrink: 0; }
1995
+
1996
+ /* Modal wide variant */
1997
+ .modal-panel-wide {
1998
+ max-width: 860px;
1999
+ }
2000
+
2001
+ /* Modal small variant (prompt / confirm / alert) */
2002
+ .modal-panel-sm {
2003
+ max-width: 440px;
2004
+ }
2005
+
2006
+ .dialog-message {
2007
+ font-size: 15px;
2008
+ line-height: 1.6;
2009
+ margin: 0;
2010
+ }
2011
+
2012
+ .dialog-field {
2013
+ margin-top: 12px;
2014
+ }
2015
+
2016
+ .dialog-field[hidden] {
2017
+ display: none;
2018
+ }
2019
+
2020
+ /* Version panel */
2021
+ .version-panel {
2022
+ position: absolute;
2023
+ top: 0;
2024
+ right: 0;
2025
+ width: 320px;
2026
+ height: 100%;
2027
+ background: var(--card);
2028
+ border-left: 1px solid var(--border);
2029
+ box-shadow: -4px 0 20px rgba(0, 0, 0, 0.1);
2030
+ z-index: 20;
2031
+ display: flex;
2032
+ flex-direction: column;
2033
+ transform: translateX(100%);
2034
+ transition: transform 0.25s ease;
2035
+ }
2036
+
2037
+ .version-panel.open {
2038
+ transform: translateX(0);
2039
+ }
2040
+
2041
+ .version-panel-header {
2042
+ display: flex;
2043
+ align-items: center;
2044
+ justify-content: space-between;
2045
+ padding: 14px 16px;
2046
+ border-bottom: 1px solid var(--border);
2047
+ flex-shrink: 0;
2048
+ }
2049
+
2050
+ .version-panel-header h3 {
2051
+ font-size: 15px;
2052
+ font-weight: 600;
2053
+ margin: 0;
2054
+ }
2055
+
2056
+ .version-panel-body {
2057
+ overflow-y: auto;
2058
+ flex: 1;
2059
+ padding: 12px 0;
2060
+ }
2061
+
2062
+ .version-item {
2063
+ padding: 10px 16px;
2064
+ border-bottom: 1px solid var(--border);
2065
+ }
2066
+
2067
+ .version-item:last-child {
2068
+ border-bottom: none;
2069
+ }
2070
+
2071
+ .version-item-current {
2072
+ background: var(--primary-soft);
2073
+ }
2074
+
2075
+ .version-item-row {
2076
+ display: flex;
2077
+ align-items: center;
2078
+ gap: 8px;
2079
+ margin-bottom: 4px;
2080
+ }
2081
+
2082
+ .version-item-dot {
2083
+ width: 8px;
2084
+ height: 8px;
2085
+ border-radius: 50%;
2086
+ background: var(--border);
2087
+ flex-shrink: 0;
2088
+ }
2089
+
2090
+ .version-item-current .version-item-dot {
2091
+ background: var(--primary);
2092
+ }
2093
+
2094
+ .version-item-label {
2095
+ font-size: 13px;
2096
+ font-weight: 600;
2097
+ color: var(--text);
2098
+ }
2099
+
2100
+ .version-item-current .version-item-label {
2101
+ color: var(--primary);
2102
+ }
2103
+
2104
+ .version-item-meta {
2105
+ font-size: 12px;
2106
+ color: var(--text-secondary);
2107
+ margin-left: 16px;
2108
+ margin-bottom: 6px;
2109
+ }
2110
+
2111
+ .version-item-actions {
2112
+ display: flex;
2113
+ gap: 6px;
2114
+ margin-left: 16px;
2115
+ flex-wrap: wrap;
2116
+ }
2117
+
2118
+ .version-item-actions .btn {
2119
+ font-size: 11px;
2120
+ padding: 3px 10px;
2121
+ }
2122
+
2123
+ .version-empty {
2124
+ padding: 32px 16px;
2125
+ text-align: center;
2126
+ color: var(--text-secondary);
2127
+ font-size: 13px;
2128
+ }
2129
+
2130
+ .file-badge-version {
2131
+ background: rgba(37, 99, 235, 0.1);
2132
+ color: var(--primary);
2133
+ }
2134
+
2135
+ html:not(.light) {
2136
+ .version-panel {
2137
+ box-shadow: -4px 0 20px rgba(0, 0, 0, 0.35);
2138
+ }
2139
+
2140
+ .file-badge-version {
2141
+ background: rgba(96, 165, 250, 0.15);
2142
+ }
2143
+
2144
+ .version-item-current {
2145
+ background: rgba(37, 99, 235, 0.12);
2146
+ }
2147
+
2148
+ .version-item-dot {
2149
+ background: #475569;
2150
+ }
2151
+
2152
+ .version-item-current .version-item-dot {
2153
+ background: var(--primary);
2154
+ }
2155
+ }
2156
+
2157
+ @media (max-width: 640px) {
2158
+ .version-panel {
2159
+ width: 100%;
2160
+ }
2161
+ }
2162
+
2163
+ @media (prefers-reduced-motion: reduce) {
2164
+ .version-panel {
2165
+ transition: none;
2166
+ }
2167
+ }
2168
+
2169
+ /* MCP Config Modal */
2170
+ .mcp-status {
2171
+ margin-bottom: 16px;
2172
+ }
2173
+
2174
+ .mcp-status-badge {
2175
+ display: inline-flex;
2176
+ align-items: center;
2177
+ gap: 8px;
2178
+ padding: 6px 14px;
2179
+ border-radius: 999px;
2180
+ font-size: 13px;
2181
+ font-weight: 600;
2182
+ margin-bottom: 14px;
2183
+ }
2184
+
2185
+ .mcp-status-on {
2186
+ background: rgba(22, 163, 74, 0.1);
2187
+ color: #16a34a;
2188
+ border: 1px solid rgba(22, 163, 74, 0.25);
2189
+ }
2190
+
2191
+ .mcp-status-off {
2192
+ background: rgba(220, 38, 38, 0.08);
2193
+ color: var(--danger);
2194
+ border: 1px solid rgba(220, 38, 38, 0.2);
2195
+ }
2196
+
2197
+ .mcp-status-dot {
2198
+ width: 8px;
2199
+ height: 8px;
2200
+ border-radius: 50%;
2201
+ flex-shrink: 0;
2202
+ }
2203
+
2204
+ .mcp-status-on .mcp-status-dot { background: #16a34a; }
2205
+ .mcp-status-off .mcp-status-dot { background: var(--danger); }
2206
+
2207
+ .mcp-info-row {
2208
+ display: flex;
2209
+ align-items: baseline;
2210
+ gap: 12px;
2211
+ padding: 8px 0;
2212
+ border-bottom: 1px solid var(--border);
2213
+ font-size: 13px;
2214
+ }
2215
+
2216
+ .mcp-info-row:last-child { border-bottom: none; }
2217
+
2218
+ .mcp-label {
2219
+ flex-shrink: 0;
2220
+ width: 64px;
2221
+ color: var(--text-secondary);
2222
+ font-weight: 500;
2223
+ }
2224
+
2225
+ .mcp-value {
2226
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
2227
+ font-size: 12px;
2228
+ background: var(--bg);
2229
+ padding: 2px 8px;
2230
+ border-radius: 4px;
2231
+ word-break: break-all;
2232
+ user-select: all;
2233
+ }
2234
+
2235
+ .mcp-detail h3 {
2236
+ font-size: 13px;
2237
+ font-weight: 600;
2238
+ color: var(--text-secondary);
2239
+ text-transform: uppercase;
2240
+ letter-spacing: 0.04em;
2241
+ margin: 20px 0 8px;
2242
+ }
2243
+
2244
+ .mcp-config-hint {
2245
+ font-size: 13px;
2246
+ color: var(--text-secondary);
2247
+ margin: 0 0 10px;
2248
+ line-height: 1.6;
2249
+ }
2250
+
2251
+ .mcp-config-hint code {
2252
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
2253
+ font-size: 12px;
2254
+ background: var(--bg);
2255
+ padding: 1px 5px;
2256
+ border-radius: 3px;
2257
+ }
2258
+
2259
+ .mcp-config-block {
2260
+ position: relative;
2261
+ }
2262
+
2263
+ .mcp-config-code {
2264
+ background: var(--bg);
2265
+ border-radius: var(--radius-sm);
2266
+ padding: 14px 16px;
2267
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
2268
+ font-size: 12px;
2269
+ line-height: 1.55;
2270
+ white-space: pre-wrap;
2271
+ word-break: break-word;
2272
+ margin: 0;
2273
+ overflow-x: auto;
2274
+ }
2275
+
2276
+ .mcp-config-code code { font-family: inherit; }
2277
+
2278
+ .mcp-copy-btn {
2279
+ position: absolute;
2280
+ top: 8px;
2281
+ right: 8px;
2282
+ z-index: 1;
2283
+ }
2284
+
2285
+ .mcp-config-path {
2286
+ min-height: 18px;
2287
+ }
2288
+
2289
+ .mcp-tabs {
2290
+ display: flex;
2291
+ flex-wrap: wrap;
2292
+ gap: 4px;
2293
+ border-bottom: 1px solid var(--border);
2294
+ margin: 0 0 12px;
2295
+ }
2296
+
2297
+ .mcp-tab {
2298
+ padding: 8px 12px;
2299
+ font-size: 13px;
2300
+ font-family: inherit;
2301
+ background: none;
2302
+ color: var(--text-secondary);
2303
+ border: none;
2304
+ border-bottom: 2px solid transparent;
2305
+ cursor: pointer;
2306
+ transition: color .2s, border-color .2s;
2307
+ margin-bottom: -1px;
2308
+ }
2309
+
2310
+ .mcp-tab:hover {
2311
+ color: var(--text);
2312
+ }
2313
+
2314
+ .mcp-tab.active {
2315
+ color: var(--primary);
2316
+ border-bottom-color: var(--primary);
2317
+ font-weight: 600;
2318
+ }
2319
+
2320
+ html:not(.light) {
2321
+ .mcp-status-on {
2322
+ background: rgba(22, 163, 74, 0.15);
2323
+ border-color: rgba(22, 163, 74, 0.35);
2324
+ }
2325
+ .mcp-status-off {
2326
+ background: rgba(248, 113, 113, 0.12);
2327
+ border-color: rgba(248, 113, 113, 0.3);
2328
+ }
2329
+ }
2330
+
2331
+
2332
+ /* --- Admin-only elements --- */
2333
+ .admin-only { display: none; }
2334
+
2335
+ /* --- Settings menu divider --- */
2336
+ .settings-menu-divider {
2337
+ height: 1px;
2338
+ background: var(--border);
2339
+ margin: 4px 8px;
2340
+ }
2341
+
2342
+ .settings-menu-label {
2343
+ font-size: 11px;
2344
+ font-weight: 600;
2345
+ color: var(--text-secondary);
2346
+ text-transform: uppercase;
2347
+ letter-spacing: 0.05em;
2348
+ padding: 6px 12px 2px;
2349
+ user-select: none;
2350
+ }
2351
+
2352
+ /* --- Users table --- */
2353
+ .users-table { width: 100%; border-collapse: collapse; }
2354
+ .users-table th, .users-table td { padding: 8px 12px; text-align: left; border-bottom: 1px solid var(--border); }
2355
+ .users-table th { font-size: 12px; font-weight: 600; color: var(--text-secondary); text-transform: uppercase; }
2356
+ .users-table td { font-size: 14px; }
2357
+ .users-actions { display: flex; gap: 6px; }
2358
+
2359
+ .role-badge {
2360
+ display: inline-block;
2361
+ padding: 2px 8px;
2362
+ border-radius: 4px;
2363
+ font-size: 12px;
2364
+ font-weight: 500;
2365
+ }
2366
+ .role-admin { background: #dbeafe; color: #1e40af; }
2367
+ .role-user { background: #f3f4f6; color: #6b7280; }
2368
+ html:not(.light) {
2369
+ .role-admin { background: #1e3a5f; color: #93c5fd; }
2370
+ .role-user { background: #374151; color: #9ca3af; }
2371
+ }
2372
+
2373
+ /* --- Token list --- */
2374
+ .token-item {
2375
+ display: flex;
2376
+ justify-content: space-between;
2377
+ align-items: center;
2378
+ padding: 10px 0;
2379
+ border-bottom: 1px solid var(--border);
2380
+ }
2381
+ .token-info { display: flex; flex-direction: column; gap: 2px; }
2382
+ .token-info strong { font-size: 14px; }
2383
+ .token-prefix {
2384
+ display: inline-block;
2385
+ font-family: monospace;
2386
+ font-size: 12px;
2387
+ padding: 1px 6px;
2388
+ background: var(--card);
2389
+ border: 1px solid var(--border);
2390
+ border-radius: 3px;
2391
+ width: fit-content;
2392
+ }
2393
+ .token-time { font-size: 12px; color: var(--text-secondary); }
2394
+ .token-actions { display: flex; gap: 6px; flex-shrink: 0; }
2395
+ .token-reveal-input {
2396
+ width: 100%;
2397
+ font-family: monospace;
2398
+ font-size: 13px;
2399
+ padding: 8px 10px;
2400
+ background: var(--card);
2401
+ border: 1px solid var(--border);
2402
+ border-radius: var(--radius-sm);
2403
+ color: var(--text);
2404
+ }
2405
+ .btn-danger-outline {
2406
+ color: var(--danger);
2407
+ border: 1px solid var(--danger);
2408
+ background: transparent;
2409
+ }
2410
+ .btn-danger-outline:hover { background: var(--danger); color: white; }
2411
+
2412
+ /* --- Modal hint --- */
2413
+ .modal-hint { font-size: 13px; color: var(--text-secondary); margin: 0; }
2414
+
2415
+ /* Star button */
2416
+ .btn-star {
2417
+ font-size: 14px;
2418
+ color: var(--text-secondary);
2419
+ border-color: var(--border);
2420
+ min-width: 32px;
2421
+ justify-content: center;
2422
+ }
2423
+ .btn-star:hover { color: #f59e0b; border-color: #f59e0b; }
2424
+ .btn-star.starred { color: #f59e0b; border-color: #f59e0b; background: rgba(245, 158, 11, 0.08); }
2425
+
2426
+ html:not(.light) {
2427
+ .btn-star:hover { color: #fbbf24; border-color: #fbbf24; }
2428
+ .btn-star.starred { color: #fbbf24; border-color: #fbbf24; background: rgba(251, 191, 36, 0.12); }
2429
+ }
2430
+
2431
+ /* Tag badge */
2432
+ .file-badge-tag {
2433
+ background: rgba(37, 99, 235, 0.08);
2434
+ color: var(--primary);
2435
+ border: 1px solid rgba(37, 99, 235, 0.2);
2436
+ cursor: pointer;
2437
+ }
2438
+ .file-badge-tag:hover { background: rgba(37, 99, 235, 0.15); }
2439
+
2440
+ html:not(.light) {
2441
+ .file-badge-tag { background: rgba(96, 165, 250, 0.12); border-color: rgba(96, 165, 250, 0.25); }
2442
+ .file-badge-tag:hover { background: rgba(96, 165, 250, 0.2); }
2443
+ }
2444
+
2445
+ /* Category badge */
2446
+ .file-badge-category {
2447
+ background: rgba(124, 58, 237, 0.08);
2448
+ color: #7c3aed;
2449
+ border: 1px solid rgba(124, 58, 237, 0.2);
2450
+ }
2451
+
2452
+ html:not(.light) {
2453
+ .file-badge-category { background: rgba(167, 139, 250, 0.12); color: #a78bfa; border-color: rgba(167, 139, 250, 0.25); }
2454
+ }
2455
+
2456
+ /* Filter dropdown */
2457
+ .filter-dropdown { position: relative; display: inline-flex; }
2458
+ .filter-dropdown-menu {
2459
+ display: none;
2460
+ position: absolute;
2461
+ top: calc(100% + 6px);
2462
+ left: 0;
2463
+ min-width: 160px;
2464
+ background: var(--card);
2465
+ border: 1px solid var(--border);
2466
+ border-radius: var(--radius);
2467
+ box-shadow: 0 8px 30px rgba(15, 23, 42, 0.15);
2468
+ z-index: 100;
2469
+ padding: 4px;
2470
+ max-height: 240px;
2471
+ overflow-y: auto;
2472
+ }
2473
+ .filter-dropdown.open .filter-dropdown-menu { display: block; }
2474
+
2475
+ .filter-dropdown-item {
2476
+ display: block;
2477
+ width: 100%;
2478
+ padding: 7px 12px;
2479
+ border: none;
2480
+ border-radius: var(--radius-sm);
2481
+ background: none;
2482
+ color: var(--text);
2483
+ font-size: 13px;
2484
+ text-align: left;
2485
+ cursor: pointer;
2486
+ transition: background .15s;
2487
+ white-space: nowrap;
2488
+ }
2489
+ .filter-dropdown-item:hover { background: var(--bg); }
2490
+ .filter-dropdown-item.active { background: var(--primary-soft); color: var(--primary); font-weight: 500; }
2491
+
2492
+ /* Tag editor modal */
2493
+ .tag-editor-selected { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; min-height: 28px; }
2494
+ .tag-editor-chip {
2495
+ display: inline-flex;
2496
+ align-items: center;
2497
+ gap: 4px;
2498
+ padding: 3px 10px;
2499
+ border-radius: 999px;
2500
+ font-size: 12px;
2501
+ font-weight: 500;
2502
+ background: var(--primary-soft);
2503
+ color: var(--primary);
2504
+ border: 1px solid rgba(37, 99, 235, 0.2);
2505
+ }
2506
+ .tag-editor-chip-remove { cursor: pointer; opacity: 0.6; font-size: 14px; line-height: 1; }
2507
+ .tag-editor-chip-remove:hover { opacity: 1; }
2508
+
2509
+ .tag-editor-input-wrap { position: relative; }
2510
+ #tag-editor-input {
2511
+ width: 100%;
2512
+ padding: 9px 12px;
2513
+ border: 1px solid var(--border);
2514
+ border-radius: var(--radius);
2515
+ font-size: 14px;
2516
+ background: var(--card);
2517
+ color: var(--text);
2518
+ }
2519
+ #tag-editor-input:focus { outline: none; border-color: var(--primary); }
2520
+
2521
+ /* Editor */
2522
+ .editor-container {
2523
+ width: 100%;
2524
+ height: 100%;
2525
+ display: flex;
2526
+ flex-direction: column;
2527
+ background: var(--card);
2528
+ }
2529
+
2530
+ .editor-wrap {
2531
+ flex: 1;
2532
+ display: flex;
2533
+ overflow: hidden;
2534
+ }
2535
+
2536
+ .editor-gutter {
2537
+ width: 48px;
2538
+ padding: 12px 8px 12px 12px;
2539
+ text-align: right;
2540
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Menlo', 'Consolas', monospace;
2541
+ font-size: 13px;
2542
+ line-height: 1.6;
2543
+ color: var(--text-secondary);
2544
+ background: var(--bg);
2545
+ border-right: 1px solid var(--border);
2546
+ overflow: hidden;
2547
+ white-space: pre;
2548
+ user-select: none;
2549
+ opacity: 0.7;
2550
+ }
2551
+
2552
+ .editor-textarea {
2553
+ flex: 1;
2554
+ padding: 12px 16px;
2555
+ border: none;
2556
+ outline: none;
2557
+ resize: none;
2558
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Menlo', 'Consolas', monospace;
2559
+ font-size: 13px;
2560
+ line-height: 1.6;
2561
+ color: var(--text);
2562
+ background: var(--card);
2563
+ tab-size: 2;
2564
+ white-space: pre;
2565
+ overflow: auto;
2566
+ }
2567
+
2568
+ .editor-statusbar {
2569
+ padding: 4px 16px;
2570
+ font-size: 12px;
2571
+ color: var(--text-secondary);
2572
+ background: var(--bg);
2573
+ border-top: 1px solid var(--border);
2574
+ text-align: right;
2575
+ }
2576
+
2577
+ html:not(.light) {
2578
+ .editor-container {
2579
+ background: #0d1117;
2580
+ }
2581
+ .editor-textarea {
2582
+ background: #0d1117;
2583
+ color: #c9d1d9;
2584
+ }
2585
+ .editor-gutter {
2586
+ background: #161b22;
2587
+ color: #484f58;
2588
+ border-right-color: #30363d;
2589
+ }
2590
+ .editor-statusbar {
2591
+ background: #161b22;
2592
+ border-top-color: #30363d;
2593
+ }
2594
+ }
2595
+
2596
+ .tag-editor-suggestions {
2597
+ display: none;
2598
+ position: absolute;
2599
+ top: 100%;
2600
+ left: 0;
2601
+ right: 0;
2602
+ background: var(--card);
2603
+ border: 1px solid var(--border);
2604
+ border-radius: 0 0 var(--radius) var(--radius);
2605
+ border-top: none;
2606
+ max-height: 160px;
2607
+ overflow-y: auto;
2608
+ z-index: 10;
2609
+ list-style: none;
2610
+ padding: 4px;
2611
+ margin: 0;
2612
+ }
2613
+ .tag-editor-suggestions.visible { display: block; }
2614
+ .tag-editor-suggestions li { padding: 6px 12px; font-size: 13px; cursor: pointer; border-radius: var(--radius-sm); }
2615
+ .tag-editor-suggestions li:hover { background: var(--bg); }
2616
+ .tag-create-new { color: var(--primary); font-weight: 500; }
2617
+
2618
+ /* Category list */
2619
+ .category-list-item {
2620
+ display: flex;
2621
+ align-items: center;
2622
+ justify-content: space-between;
2623
+ padding: 10px 12px;
2624
+ border-radius: var(--radius-sm);
2625
+ cursor: pointer;
2626
+ transition: background .15s;
2627
+ font-size: 14px;
2628
+ }
2629
+ .category-list-item:hover { background: var(--bg); }
2630
+ .category-list-item.selected { background: var(--primary-soft); color: var(--primary); font-weight: 500; }
2631
+ .category-item-actions { display: flex; gap: 4px; }
2632
+ .category-item-actions .btn { font-size: 11px; padding: 2px 8px; }
2633
+
2634
+ /* Template preview card */
2635
+ .tpl-card {
2636
+ display: flex; flex-direction: column; border: 2px solid var(--border);
2637
+ border-radius: var(--radius-md); overflow: hidden; cursor: pointer;
2638
+ transition: border-color .15s, box-shadow .15s;
2639
+ }
2640
+ .tpl-card:hover { border-color: var(--text-secondary); box-shadow: 0 2px 8px rgba(0,0,0,.08); }
2641
+ .tpl-card.selected { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary); }
2642
+ .tpl-preview {
2643
+ height: 72px; padding: 10px 12px 8px; display: flex; flex-direction: column; gap: 5px;
2644
+ font-size: 0; line-height: 1;
2645
+ }
2646
+ .tpl-preview-heading { height: 7px; width: 55%; border-radius: 2px; }
2647
+ .tpl-preview-line { height: 4px; border-radius: 1px; }
2648
+ .tpl-preview-line:nth-child(2) { width: 80%; }
2649
+ .tpl-preview-line:nth-child(3) { width: 60%; }
2650
+ .tpl-preview-code { height: 14px; width: 40%; border-radius: 2px; margin-top: 2px; }
2651
+ .tpl-card-label {
2652
+ display: flex; align-items: center; justify-content: space-between;
2653
+ padding: 6px 12px; font-size: 13px; color: var(--text);
2654
+ border-top: 1px solid var(--border); background: var(--surface);
2655
+ }
2656
+ .tpl-card.selected .tpl-card-label { font-weight: 600; color: var(--primary); }
2657
+ .tpl-card-check { font-size: 15px; color: var(--primary); display: none; }
2658
+ .tpl-card.selected .tpl-card-check { display: inline; }
2659
+ #template-select-list {
2660
+ display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; padding: 4px 0;
2661
+ }
2662
+
2663
+ /* View badge */
2664
+ .file-badge-views { color: var(--text-secondary); background: transparent; font-size: 11px; padding: 0 4px; }
2665
+
2666
+ /* Stats dialog */
2667
+ .stats-summary { text-align: center; margin-bottom: 12px; }
2668
+ .stats-total { font-size: 15px; }
2669
+ .stats-total strong { font-size: 24px; color: var(--primary); margin: 0 4px; }
2670
+ .stats-section { margin-bottom: 16px; }
2671
+ .stats-section h4 { font-size: 13px; color: var(--text-secondary); margin-bottom: 8px; font-weight: 500; }
2672
+ .stats-empty { color: var(--text-secondary); font-size: 13px; text-align: center; padding: 12px 0; }
2673
+ .stats-chart { display: flex; align-items: flex-end; gap: 2px; height: 80px; border-bottom: 1px solid var(--border); padding-bottom: 18px; }
2674
+ .stats-bar-group { flex: 1; display: flex; flex-direction: column; align-items: center; height: 100%; justify-content: flex-end; min-width: 0; position: relative; }
2675
+ .stats-bar { width: 100%; max-width: 20px; background: var(--primary); border-radius: 2px 2px 0 0; min-height: 2px; transition: height .3s; }
2676
+ .stats-bar-val { font-size: 9px; color: var(--text-secondary); line-height: 1; margin-bottom: 2px; }
2677
+ .stats-bar-label { font-size: 9px; color: var(--text-secondary); position: absolute; bottom: -16px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
2678
+
2679
+ /* --- 内容模板市场 --- */
2680
+ .ct-filter-bar { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
2681
+ .ct-filter-bar .search-input-wrap { width: 100%; }
2682
+ .ct-filter-bar .search-input-wrap input { width: 100%; padding: 8px 12px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text); font-size: 14px; }
2683
+ .ct-filter-bar .filter-chips { display: flex; flex-wrap: wrap; gap: 6px; }
2684
+ .ct-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
2685
+ .ct-card { background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 14px; cursor: pointer; transition: box-shadow .2s, border-color .2s; display: flex; flex-direction: column; gap: 6px; }
2686
+ .ct-card:hover { box-shadow: 0 2px 8px var(--shadow); border-color: var(--primary); }
2687
+ .ct-card-header { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
2688
+ .ct-card-title { font-size: 14px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
2689
+ .ct-badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; line-height: 1.4; }
2690
+ .ct-badge-html { background: #e0f2fe; color: #0369a1; }
2691
+ .ct-badge-md { background: #dbeafe; color: #1d4ed8; }
2692
+ .ct-badge-scene { background: var(--primary-light, #ede9fe); color: var(--primary); }
2693
+ .ct-badge-tag { background: var(--bg-secondary, #f1f5f9); color: var(--text-secondary); }
2694
+ .ct-card-desc { font-size: 12px; color: var(--text-secondary); line-height: 1.5; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; margin: 0; }
2695
+ .ct-card-footer { display: flex; justify-content: space-between; align-items: center; font-size: 11px; color: var(--text-secondary); margin-top: auto; }
2696
+ .ct-use-count { color: var(--primary); font-weight: 500; }
2697
+ .ct-owner-mark { color: var(--success, #10b981); font-weight: 500; }
2698
+ .ct-loading, .ct-empty { text-align: center; padding: 40px; color: var(--text-secondary); font-size: 14px; grid-column: 1 / -1; }
2699
+ .ct-pagination { display: flex; align-items: center; justify-content: center; gap: 12px; grid-column: 1 / -1; padding: 12px 0; }
2700
+ .ct-page-info { font-size: 13px; color: var(--text-secondary); }
2701
+ .ct-detail-meta { margin-bottom: 16px; }
2702
+ .ct-meta-row { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
2703
+ .ct-desc { font-size: 13px; color: var(--text-secondary); line-height: 1.5; margin: 8px 0; }
2704
+ .ct-meta-info { font-size: 12px; color: var(--text-secondary); }
2705
+ /* 卡片缩略图 */
2706
+ .ct-card-thumb { height: 160px; position: relative; overflow: hidden; background: linear-gradient(135deg, var(--bg-secondary, #f1f5f9), var(--border)); margin: -14px -14px 10px; border-radius: 6px 6px 0 0; }
2707
+ .ct-card-thumb-wrap { width: 1024px; height: 640px; transform: scale(0.25); transform-origin: top left; pointer-events: none; }
2708
+ .ct-thumb-iframe { width: 100%; height: 100%; border: none; background: #fff; }
2709
+ .ct-card-thumb-loading { position: absolute; inset: 0; background: linear-gradient(135deg, var(--bg-secondary, #f1f5f9), var(--border)); animation: ct-thumb-pulse 1.5s ease-in-out infinite; }
2710
+ @keyframes ct-thumb-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }
2711
+ html:not(.light) {
2712
+ .ct-badge-html { background: #172554; color: #7dd3fc; }
2713
+ .ct-badge-md { background: #1e3a5f; color: #93c5fd; }
2714
+ .ct-badge-scene { background: #2e1065; color: #c4b5fd; }
2715
+ .ct-badge-tag { background: #1e293b; color: #94a3b8; }
2716
+ .ct-card-thumb { background: linear-gradient(135deg, #1e293b, #334155); }
2717
+ }
2718
+
2719
+ /* --- 落地页 --- */
2720
+ .landing-page { min-height: 100vh; background: var(--bg); display: flex; flex-direction: column; }
2721
+ .landing-nav { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: transparent; transition: background .3s, border-color .3s, box-shadow .3s; border-bottom: 1px solid transparent; }
2722
+ .landing-nav.scrolled { background: var(--header-bg); backdrop-filter: saturate(180%) blur(12px); -webkit-backdrop-filter: saturate(180%) blur(12px); border-bottom-color: var(--border); box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
2723
+ .landing-nav-inner { max-width: 1200px; margin: 0 auto; padding: 0 24px; height: 56px; display: flex; align-items: center; justify-content: space-between; }
2724
+ .landing-nav-brand { display: flex; align-items: center; gap: 10px; }
2725
+ .landing-nav-title { font-size: 18px; font-weight: 700; color: var(--text); }
2726
+ .landing-nav-actions { display: flex; gap: 8px; }
2727
+
2728
+ .btn-ghost { background: transparent; color: var(--primary); border: 1px solid var(--primary); }
2729
+ .btn-ghost:hover { background: var(--primary); color: #fff; }
2730
+ .btn-lg { padding: 12px 28px; font-size: 15px; font-weight: 600; }
2731
+
2732
+ /* ===== Hero ===== */
2733
+ .landing-hero {
2734
+ flex: 1; min-height: 100vh; display: flex; flex-direction: column;
2735
+ align-items: center; justify-content: center; text-align: center;
2736
+ padding: 80px 24px 40px; position: relative;
2737
+ }
2738
+ .landing-hero-eyebrow {
2739
+ font-family: ui-monospace, "SF Mono", monospace; font-size: 12px;
2740
+ letter-spacing: 3px; color: var(--primary); margin-bottom: 28px;
2741
+ display: flex; align-items: center; gap: 8px;
2742
+ }
2743
+ .landing-hero-eyebrow span {
2744
+ display: inline-block; width: 5px; height: 5px; border-radius: 50%;
2745
+ background: var(--primary); animation: pulse-dot 2s ease-in-out infinite;
2746
+ }
2747
+ .landing-hero-title {
2748
+ font-size: clamp(52px, 10vw, 88px); font-weight: 800;
2749
+ letter-spacing: -2px; line-height: 1.05; margin: 0 0 8px;
2750
+ }
2751
+ .landing-hero-title em {
2752
+ font-style: normal;
2753
+ background: linear-gradient(135deg, var(--primary), #8b5cf6 45%, #06b6d4);
2754
+ -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
2755
+ }
2756
+ .landing-hero-title small {
2757
+ font-weight: 300; font-size: .38em; color: var(--text-secondary); letter-spacing: 2px;
2758
+ }
2759
+ .landing-hero-sub {
2760
+ font-size: clamp(15px, 2.2vw, 20px); color: var(--text-secondary);
2761
+ font-weight: 300; letter-spacing: 6px; margin: 0 0 36px;
2762
+ }
2763
+ .landing-hero-slogan {
2764
+ font-size: clamp(16px, 2.5vw, 22px); font-weight: 400;
2765
+ color: var(--text); margin: 0 0 40px; line-height: 1.6;
2766
+ }
2767
+ .landing-hero-actions { display: flex; gap: 12px; margin-bottom: 36px; }
2768
+ .landing-hero-actions .btn-primary { width: auto; }
2769
+ .landing-hero-tags { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
2770
+ .landing-tag {
2771
+ padding: 5px 12px; border-radius: 6px; background: var(--card);
2772
+ border: 1px solid var(--border); font-family: ui-monospace, "SF Mono", monospace;
2773
+ font-size: 11px; color: var(--text-secondary); transition: all .3s;
2774
+ }
2775
+ .landing-tag:hover { border-color: var(--primary); color: var(--primary); transform: translateY(-2px); }
2776
+
2777
+ .badge-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--primary); animation: pulse-dot 2s ease-in-out infinite; }
2778
+ @keyframes pulse-dot {
2779
+ 0%, 100% { opacity: 1; }
2780
+ 50% { opacity: 0.4; }
2781
+ }
2782
+
2783
+ /* ===== 模板展示(保留给弹窗) ===== */
2784
+ .landing-template-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 20px; }
2785
+ .landing-template-card { background: var(--card); border: 1px solid var(--border); border-radius: 12px; overflow: hidden; cursor: pointer; transition: box-shadow .2s, border-color .2s, transform .2s; }
2786
+ .landing-template-card:hover { box-shadow: 0 8px 24px var(--shadow); border-color: var(--primary); transform: translateY(-3px); }
2787
+ .landing-template-thumb { height: 150px; background: linear-gradient(135deg, var(--bg-secondary, #f1f5f9), var(--border)); display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden; }
2788
+ .landing-template-thumb::before { content: ''; position: absolute; inset: 0; background: linear-gradient(135deg, transparent 40%, rgba(37, 99, 235, 0.06) 100%); }
2789
+ .landing-template-thumb-placeholder { width: 60px; height: 60px; border-radius: 14px; background: linear-gradient(135deg, var(--primary), #7c3aed); color: #fff; font-size: 22px; font-weight: 700; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3); position: relative; z-index: 1; }
2790
+ .landing-template-info { padding: 14px 16px; }
2791
+ .landing-template-info h4 { margin: 0 0 10px; font-size: 14px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2792
+ .landing-template-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
2793
+ .landing-template-uses { font-size: 11px; color: var(--text-secondary); }
2794
+ .landing-template-thumb .ct-card-thumb-wrap { position: relative; z-index: 2; }
2795
+ .landing-template-thumb .ct-card-thumb-loading { position: absolute; inset: 0; z-index: 1; background: linear-gradient(135deg, var(--bg-secondary, #f1f5f9), var(--border)); animation: ct-thumb-pulse 1.5s ease-in-out infinite; }
2796
+ .landing-template-empty { text-align: center; padding: 48px; color: var(--text-secondary); font-size: 14px; }
2797
+ .ct-badge-type { background: #e0f2fe; color: #0369a1; }
2798
+
2799
+ /* 模板预览弹窗 */
2800
+ .modal-panel-lg { max-width: 900px; width: 90vw; max-height: 85vh; display: flex; flex-direction: column; }
2801
+ .template-preview-header { padding: 16px 20px; border-bottom: 1px solid var(--border); }
2802
+ .template-preview-header h3 { margin: 0 0 6px; font-size: 16px; color: var(--text); }
2803
+ .template-preview-meta { display: flex; gap: 6px; }
2804
+ .template-preview-body { flex: 1; overflow: hidden; }
2805
+ .template-preview-body iframe { width: 100%; height: 60vh; border: none; background: #fff; }
2806
+
2807
+ /* ===== 四次进化 + 设计理念 ===== */
2808
+ .landing-sec { padding: 90px 24px; }
2809
+ .landing-sec-inner { max-width: 900px; margin: 0 auto; }
2810
+ .landing-sec-tag { font-family: ui-monospace, "SF Mono", monospace; font-size: 11px; letter-spacing: 3px; text-transform: uppercase; color: var(--primary); margin-bottom: 10px; }
2811
+ .landing-sec-title { font-size: clamp(28px, 5vw, 44px); font-weight: 700; letter-spacing: -1px; margin: 0 0 48px; line-height: 1.2; }
2812
+
2813
+ /* 版本时间线 */
2814
+ .landing-versions { display: flex; flex-direction: column; gap: 0; position: relative; }
2815
+ .landing-versions::before { content: ''; position: absolute; left: 28px; top: 0; bottom: 0; width: 2px; background: linear-gradient(to bottom, var(--primary), #8b5cf6 50%, #06b6d4); opacity: .2; }
2816
+ .ver { position: relative; padding-left: 68px; padding-bottom: 48px; }
2817
+ .ver:last-child { padding-bottom: 0; }
2818
+ .ver-dot { position: absolute; left: 20px; top: 4px; width: 18px; height: 18px; border-radius: 50%; border: 2.5px solid var(--primary); background: var(--bg); z-index: 2; transition: all .4s; }
2819
+ .ver:hover .ver-dot { background: var(--primary); box-shadow: 0 0 18px rgba(99,102,241,.5); transform: scale(1.2); }
2820
+ .ver:nth-child(2) .ver-dot { border-color: #8b5cf6; }
2821
+ .ver:nth-child(2):hover .ver-dot { background: #8b5cf6; box-shadow: 0 0 18px rgba(139,92,246,.5); }
2822
+ .ver:nth-child(3) .ver-dot { border-color: #06b6d4; }
2823
+ .ver:nth-child(3):hover .ver-dot { background: #06b6d4; box-shadow: 0 0 18px rgba(6,182,212,.5); }
2824
+ .ver:nth-child(4) .ver-dot { border-color: #f59e0b; }
2825
+ .ver:nth-child(4):hover .ver-dot { background: #f59e0b; box-shadow: 0 0 18px rgba(245,158,11,.5); }
2826
+ .ver-card { background: var(--card); border: 1px solid var(--border); border-radius: 14px; padding: 28px 28px 24px; backdrop-filter: blur(8px); transition: all .4s; }
2827
+ .ver:hover .ver-card { border-color: var(--primary); box-shadow: var(--glow); }
2828
+ .ver:nth-child(2):hover .ver-card { border-color: #8b5cf6; box-shadow: 0 0 24px rgba(139,92,246,.2); }
2829
+ .ver:nth-child(3):hover .ver-card { border-color: #06b6d4; box-shadow: 0 0 24px rgba(6,182,212,.2); }
2830
+ .ver:nth-child(4):hover .ver-card { border-color: #f59e0b; box-shadow: 0 0 24px rgba(245,158,11,.2); }
2831
+ .ver-head { display: flex; align-items: center; gap: 14px; margin-bottom: 16px; flex-wrap: wrap; }
2832
+ .ver-num { font-family: ui-monospace, "SF Mono", monospace; font-size: 13px; font-weight: 700; letter-spacing: 1px; padding: 3px 10px; border-radius: 6px; }
2833
+ .ver:nth-child(1) .ver-num { color: var(--primary); background: rgba(99,102,241,.12); }
2834
+ .ver:nth-child(2) .ver-num { color: #8b5cf6; background: rgba(139,92,246,.10); }
2835
+ .ver:nth-child(3) .ver-num { color: #06b6d4; background: rgba(6,182,212,.10); }
2836
+ .ver:nth-child(4) .ver-num { color: #f59e0b; background: rgba(245,158,11,.10); }
2837
+ .ver-date { font-family: ui-monospace, "SF Mono", monospace; font-size: 12px; color: var(--text-secondary); }
2838
+ .ver-title { font-size: 20px; font-weight: 700; flex: 1; }
2839
+ .ver-theme { font-size: 13px; color: var(--text-secondary); font-style: italic; padding: 2px 10px; border-radius: 4px; background: rgba(255,255,255,.04); }
2840
+ .ver-desc { font-size: 15px; color: var(--text-secondary); line-height: 1.75; margin-bottom: 18px; }
2841
+ .ver-highlights { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; }
2842
+ .hl { display: flex; align-items: flex-start; gap: 8px; font-size: 13px; color: var(--text-secondary); padding: 8px 12px; border-radius: 8px; background: rgba(255,255,255,.02); border: 1px solid transparent; transition: all .3s; }
2843
+ .hl:hover { border-color: var(--border); background: rgba(255,255,255,.04); }
2844
+ .hl-icon { flex-shrink: 0; font-size: 15px; margin-top: 1px; }
2845
+
2846
+ /* 设计理念 */
2847
+ .landing-philos { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
2848
+ .phi { background: var(--card); border: 1px solid var(--border); border-radius: 14px; padding: 28px 24px; backdrop-filter: blur(8px); transition: all .4s; position: relative; overflow: hidden; }
2849
+ .phi::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; opacity: 0; transition: opacity .4s; }
2850
+ .phi:nth-child(1)::before { background: linear-gradient(90deg, var(--primary), #06b6d4); }
2851
+ .phi:nth-child(2)::before { background: linear-gradient(90deg, #8b5cf6, #f59e0b); }
2852
+ .phi:nth-child(3)::before { background: linear-gradient(90deg, #06b6d4, var(--primary)); }
2853
+ .phi:nth-child(4)::before { background: linear-gradient(90deg, #f59e0b, #8b5cf6); }
2854
+ .phi:hover::before { opacity: 1; }
2855
+ .phi:hover { transform: translateY(-3px); border-color: var(--primary); box-shadow: var(--glow); }
2856
+ .phi-ico { width: 44px; height: 44px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 20px; margin-bottom: 16px; }
2857
+ .phi:nth-child(1) .phi-ico { background: rgba(99,102,241,.12); }
2858
+ .phi:nth-child(2) .phi-ico { background: rgba(139,92,246,.10); }
2859
+ .phi:nth-child(3) .phi-ico { background: rgba(6,182,212,.10); }
2860
+ .phi:nth-child(4) .phi-ico { background: rgba(245,158,11,.10); }
2861
+ .phi h3 { font-size: 17px; font-weight: 600; margin-bottom: 6px; }
2862
+ .phi p { font-size: 14px; color: var(--text-secondary); line-height: 1.7; }
2863
+
2864
+ /* ===== 页脚 ===== */
2865
+ .landing-footer {
2866
+ text-align: center; padding: 32px 24px; font-size: 13px;
2867
+ color: var(--text-secondary); border-top: 1px solid var(--border);
2868
+ }
2869
+ .landing-footer p { margin: 0; }
2870
+ .landing-footer a { color: var(--text-secondary); text-decoration: none; transition: color .2s; }
2871
+ .landing-footer a:hover { color: var(--primary); }
2872
+
2873
+ /* ===== 响应式 ===== */
2874
+ @media (max-width: 768px) {
2875
+ .landing-hero { padding: 100px 20px 40px; }
2876
+ .landing-hero-sub { letter-spacing: 3px; }
2877
+ .landing-philos { grid-template-columns: 1fr; }
2878
+ .ver-highlights { grid-template-columns: 1fr; }
2879
+ .landing-sec { padding: 60px 20px; }
2880
+ }
2881
+ @media (max-width: 520px) {
2882
+ .landing-hero { padding: 90px 16px 32px; }
2883
+ .landing-hero-actions { flex-direction: column; align-items: stretch; }
2884
+ .landing-hero-actions .btn { width: 100%; justify-content: center; }
2885
+ .landing-nav-inner { padding: 0 16px; }
2886
+ .landing-versions::before { left: 18px; }
2887
+ .ver { padding-left: 52px; }
2888
+ .ver-dot { left: 10px; }
2889
+ .ver-head { gap: 8px; }
2890
+ .ver-title { font-size: 17px; }
2891
+ }
2892
+
2893
+ /* ===== 落地页深色模式 ===== */
2894
+ html:not(.light) {
2895
+ .ct-badge-type { background: #172554; color: #7dd3fc; }
2896
+ .template-preview-body iframe { background: #1e293b; }
2897
+ .landing-tag { background: rgba(16, 16, 32, .75); }
2898
+ }
2899
+
2900
+ /* 邮箱验证提示条 */
2901
+ .email-verify-banner[hidden] { display: none; }
2902
+ .email-verify-banner:not([hidden]) {
2903
+ display: flex;
2904
+ align-items: center;
2905
+ justify-content: center;
2906
+ gap: 12px;
2907
+ padding: 8px 16px;
2908
+ background: #fef3c7;
2909
+ color: #92400e;
2910
+ font-size: 14px;
2911
+ }
2912
+ .email-verify-banner .btn { font-size: 12px; }
2913
+ @media (prefers-color-scheme: dark) {
2914
+ .email-verify-banner:not([hidden]) { background: #422006; color: #fbbf24; }
2915
+ }