agent-purge 0.2.10 → 0.2.11

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 (75) hide show
  1. package/CONTRIBUTING.md +64 -0
  2. package/LICENSE +21 -21
  3. package/README.md +169 -138
  4. package/backends/index.mjs +204 -244
  5. package/bench/bench-armor.mjs +844 -832
  6. package/bench/capture-hot.mjs +58 -0
  7. package/bench/capture-provider.mjs +66 -0
  8. package/bench/capture-report.mjs +15 -0
  9. package/bench/capture-server-hot.mjs +55 -0
  10. package/bench/capture-system.mjs +36 -0
  11. package/bench/judge-armor.mjs +119 -119
  12. package/bin/agent-purge.mjs +906 -780
  13. package/docs/LEGAL.md +27 -0
  14. package/docs/architecture.md +39 -0
  15. package/docs/compatibility.md +78 -0
  16. package/docs/images/console-desktop.png +0 -0
  17. package/docs/images/console-mobile.png +0 -0
  18. package/docs/releasing.md +87 -0
  19. package/docs/usage.md +158 -0
  20. package/lib/activation.mjs +38 -0
  21. package/lib/apply.mjs +227 -0
  22. package/lib/armed.mjs +38 -38
  23. package/lib/armor.mjs +225 -222
  24. package/lib/caps.mjs +182 -173
  25. package/lib/capture.mjs +324 -0
  26. package/lib/clean.mjs +124 -0
  27. package/lib/control.mjs +60 -0
  28. package/lib/detect.mjs +74 -70
  29. package/lib/doctor.mjs +7 -7
  30. package/lib/dsh.mjs +495 -453
  31. package/lib/environment.mjs +36 -0
  32. package/lib/evidence.mjs +41 -0
  33. package/lib/extension.mjs +140 -0
  34. package/lib/footprints.mjs +59 -0
  35. package/lib/home.mjs +47 -47
  36. package/lib/launch-conflicts.mjs +24 -0
  37. package/lib/launch.mjs +57 -0
  38. package/lib/opencode.mjs +62 -0
  39. package/lib/paths.mjs +25 -0
  40. package/lib/persist.mjs +439 -280
  41. package/lib/personas.mjs +17 -7
  42. package/lib/private-server.mjs +54 -0
  43. package/lib/project.mjs +134 -0
  44. package/lib/restore.mjs +131 -114
  45. package/lib/rewrite.mjs +124 -0
  46. package/lib/root.mjs +37 -0
  47. package/lib/shim.mjs +203 -160
  48. package/lib/slashes.mjs +227 -628
  49. package/lib/spawn.mjs +182 -126
  50. package/lib/state.mjs +85 -137
  51. package/lib/templates/personas/neutral.md +30 -30
  52. package/lib/update.mjs +137 -0
  53. package/lib/verification.mjs +23 -0
  54. package/lib/window.mjs +58 -0
  55. package/lib/wrappers.mjs +49 -0
  56. package/magpie-plugin/README.md +44 -0
  57. package/matrix.json +325 -341
  58. package/package.json +30 -4
  59. package/web/app.js +525 -0
  60. package/web/assets/agent-purge.ico +0 -0
  61. package/web/assets/claude.ico +0 -0
  62. package/web/assets/codebuddy.png +0 -0
  63. package/web/assets/codex.webp +0 -0
  64. package/web/assets/dsh.ico +0 -0
  65. package/web/assets/grok.ico +0 -0
  66. package/web/assets/hermes.ico +0 -0
  67. package/web/assets/omp.png +0 -0
  68. package/web/assets/opencode.ico +0 -0
  69. package/web/assets/opencode2.ico +0 -0
  70. package/web/assets/pi.png +0 -0
  71. package/web/assets/zcode.png +0 -0
  72. package/web/model.mjs +49 -0
  73. package/web/server.mjs +309 -194
  74. package/web/styles.css +917 -0
  75. package/web/ui.html +64 -1163
package/web/styles.css ADDED
@@ -0,0 +1,917 @@
1
+ /* ============================================================
2
+ agent-purge · Web Console — Design System v2
3
+ 方向:严谨工程 / Linear-Vercel 级工艺。浅色中性底 + 品牌蓝,
4
+ 4/8px 间距韵律,hairline 边框 + 分层阴影,克制的微交互。
5
+ 无外部字体请求;所有状态都有文字,不只靠色。
6
+ ============================================================ */
7
+
8
+ :root {
9
+ /* Surfaces */
10
+ --bg: #f6f7f9;
11
+ --panel: #ffffff;
12
+ --panel-muted: #f1f3f6;
13
+ --panel-hover: #f8fafc;
14
+
15
+ /* Ink */
16
+ --ink: #101828;
17
+ --text-secondary: #344054;
18
+ --muted: #5d6b81;
19
+ --faint: #667085;
20
+
21
+ /* Hairlines */
22
+ --line: #e4e8ee;
23
+ --line-subtle: #eef1f5;
24
+ --line-strong: #cdd5df;
25
+
26
+ /* Brand blue */
27
+ --blue: #2459cf;
28
+ --blue-hover: #1d4ab2;
29
+ --blue-soft: #eff4ff;
30
+ --blue-border: #c3d4f8;
31
+ --blue-subtle: #e8effd;
32
+ --blue-glow: rgba(36, 89, 207, 0.14);
33
+
34
+ /* Status (AA on their soft backgrounds, always paired with text) */
35
+ --green: #067647;
36
+ --green-soft: #ecfdf3;
37
+ --green-border: #abefc6;
38
+ --green-subtle: #d1fadf;
39
+
40
+ --amber: #93370d;
41
+ --amber-soft: #fffaeb;
42
+ --amber-border: #fedf89;
43
+ --amber-subtle: #fef0c7;
44
+
45
+ --red: #b42318;
46
+ --red-soft: #fef3f2;
47
+ --red-border: #fecdca;
48
+ --red-subtle: #fee4e2;
49
+
50
+ /* Radii */
51
+ --radius-sm: 6px;
52
+ --radius: 8px;
53
+ --radius-lg: 12px;
54
+
55
+ /* Elevation — soft, layered, low-spread */
56
+ --shadow-xs: 0 1px 2px 0 rgb(16 24 40 / 0.05);
57
+ --shadow-sm: 0 1px 3px 0 rgb(16 24 40 / 0.07), 0 1px 2px -1px rgb(16 24 40 / 0.05);
58
+ --shadow-md: 0 4px 8px -2px rgb(16 24 40 / 0.09), 0 2px 4px -2px rgb(16 24 40 / 0.05);
59
+ --shadow-lg: 0 12px 20px -6px rgb(16 24 40 / 0.12), 0 4px 8px -4px rgb(16 24 40 / 0.05);
60
+
61
+ /* Focus & motion */
62
+ --ring: 0 0 0 3px var(--blue-glow);
63
+ --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
64
+
65
+ font: 14px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "PingFang SC", "Noto Sans SC", "Microsoft YaHei", system-ui, sans-serif;
66
+ color: var(--ink);
67
+ background: var(--bg);
68
+ color-scheme: light;
69
+ }
70
+
71
+ *, *::before, *::after { box-sizing: border-box; }
72
+ body {
73
+ margin: 0;
74
+ min-height: 100vh;
75
+ background:
76
+ radial-gradient(1200px 400px at 50% -160px, rgba(36, 89, 207, 0.045), transparent 70%),
77
+ var(--bg);
78
+ -webkit-font-smoothing: antialiased;
79
+ }
80
+ button, input, select, textarea { font: inherit; }
81
+ button, a, input, select, textarea { touch-action: manipulation; }
82
+ button {
83
+ cursor: pointer;
84
+ color: inherit;
85
+ border: 1px solid transparent;
86
+ background: transparent;
87
+ padding: 9px 16px;
88
+ min-height: 40px;
89
+ border-radius: var(--radius-sm);
90
+ font-weight: 550;
91
+ transition: background 150ms var(--ease-out), border-color 150ms var(--ease-out),
92
+ color 150ms var(--ease-out), box-shadow 150ms var(--ease-out), transform 120ms var(--ease-out);
93
+ }
94
+ button:disabled { cursor: not-allowed; opacity: 0.5; }
95
+ a { color: var(--blue); text-underline-offset: 3px; text-decoration: none; transition: color 140ms var(--ease-out); }
96
+ a:hover { color: var(--blue-hover); text-decoration: underline; }
97
+
98
+ :focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
99
+ ::selection { background: #d6e2fb; color: #173fa5; }
100
+ * { scrollbar-color: var(--line-strong) transparent; scrollbar-width: thin; }
101
+ @media (prefers-reduced-motion: reduce) {
102
+ *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; }
103
+ }
104
+
105
+ /* ---------- Buttons ---------- */
106
+
107
+ button.primary {
108
+ background: var(--blue);
109
+ color: #fff;
110
+ font-weight: 600;
111
+ box-shadow: var(--shadow-xs), inset 0 1px 0 0 rgba(255, 255, 255, 0.12);
112
+ }
113
+ button.primary:hover:not(:disabled) {
114
+ background: var(--blue-hover);
115
+ box-shadow: var(--shadow-sm);
116
+ }
117
+ button.primary:active:not(:disabled) { transform: translateY(1px); box-shadow: none; }
118
+
119
+ button.secondary {
120
+ background: var(--panel);
121
+ border-color: var(--line);
122
+ color: var(--text-secondary);
123
+ box-shadow: var(--shadow-xs);
124
+ }
125
+ button.secondary:hover:not(:disabled) {
126
+ background: var(--panel-hover);
127
+ border-color: var(--line-strong);
128
+ color: var(--ink);
129
+ }
130
+ button.secondary:active:not(:disabled) { background: var(--panel-muted); }
131
+
132
+ button.danger { color: var(--red); }
133
+ button.danger:hover:not(:disabled) { background: var(--red-soft); border-color: var(--red-border); }
134
+
135
+ button.quiet { color: var(--muted); }
136
+ button.quiet:hover:not(:disabled) { background: var(--panel-muted); color: var(--ink); }
137
+
138
+ button.text-button {
139
+ color: var(--blue);
140
+ padding: 4px 8px;
141
+ min-height: auto;
142
+ font-size: 13px;
143
+ font-weight: 600;
144
+ display: inline-flex;
145
+ align-items: center;
146
+ gap: 4px;
147
+ border-radius: var(--radius-sm);
148
+ }
149
+ button.text-button:hover:not(:disabled) { background: var(--blue-soft); }
150
+
151
+ /* ---------- Forms ---------- */
152
+
153
+ input:not([type="checkbox"]):not([type="radio"]), select, textarea {
154
+ border: 1px solid var(--line);
155
+ background: var(--panel);
156
+ color: var(--ink);
157
+ padding: 9px 12px;
158
+ width: 100%;
159
+ min-width: 0;
160
+ border-radius: var(--radius-sm);
161
+ caret-color: var(--blue);
162
+ box-shadow: var(--shadow-xs);
163
+ transition: border-color 150ms var(--ease-out), box-shadow 150ms var(--ease-out);
164
+ }
165
+ input:not([type="checkbox"]):not([type="radio"]):hover, select:hover, textarea:hover { border-color: var(--line-strong); }
166
+ input:not([type="checkbox"]):not([type="radio"]):focus, select:focus, textarea:focus {
167
+ outline: none;
168
+ border-color: var(--blue);
169
+ box-shadow: var(--ring);
170
+ }
171
+ input:not([type="checkbox"]):not([type="radio"])::placeholder, textarea::placeholder { color: var(--faint); }
172
+ textarea { resize: vertical; line-height: 1.6; }
173
+ input:not([type="checkbox"]):not([type="radio"]):disabled, select:disabled, textarea:disabled {
174
+ background: var(--panel-muted);
175
+ color: var(--muted);
176
+ cursor: not-allowed;
177
+ box-shadow: none;
178
+ }
179
+ input[type="checkbox"], input[type="radio"] {
180
+ width: 16px;
181
+ height: 16px;
182
+ margin: 0;
183
+ flex: none;
184
+ cursor: pointer;
185
+ accent-color: var(--blue);
186
+ }
187
+
188
+ label, legend { display: block; font-weight: 600; font-size: 13.5px; color: var(--ink); margin-bottom: 7px; }
189
+ fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
190
+ legend { padding: 0; }
191
+ h1, h2, h3, p { margin: 0; }
192
+ h1 { font-size: 22px; line-height: 1.3; font-weight: 700; letter-spacing: -0.02em; color: var(--ink); }
193
+ h2 { font-size: 15.5px; font-weight: 650; line-height: 1.4; letter-spacing: -0.01em; color: var(--ink); }
194
+ h3 { font-size: 14px; font-weight: 600; }
195
+ p { max-width: 80ch; }
196
+ small, .help { font-size: 12.5px; color: var(--muted); line-height: 1.55; }
197
+ .muted { color: var(--muted); font-size: 12px; font-weight: 400; }
198
+ .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
199
+ [hidden] { display: none !important; }
200
+ code, pre { font-family: "Cascadia Code", "Fira Code", Consolas, monospace; font-size: 12px; }
201
+ code { background: var(--panel-muted); color: #1d2939; padding: 2px 5px; border-radius: 4px; }
202
+ pre { white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.65; }
203
+
204
+ /* Skip Link */
205
+ .skip {
206
+ position: fixed; top: -100px; left: 14px; z-index: 100;
207
+ background: white; padding: 10px 16px; border-radius: var(--radius);
208
+ box-shadow: var(--shadow-md); border: 1px solid var(--blue);
209
+ }
210
+ .skip:focus { top: 14px; }
211
+
212
+ /* ---------- Shell Layout ---------- */
213
+
214
+ .app-shell { display: grid; grid-template-columns: 228px minmax(0, 1fr); min-height: 100vh; }
215
+
216
+ /* Sidebar */
217
+ .sidebar {
218
+ position: sticky;
219
+ top: 0;
220
+ height: 100vh;
221
+ border-right: 1px solid var(--line);
222
+ background: linear-gradient(180deg, #ffffff 0%, #fbfcfd 100%);
223
+ display: flex;
224
+ flex-direction: column;
225
+ padding: 22px 14px 18px;
226
+ }
227
+ .brand {
228
+ display: flex;
229
+ gap: 12px;
230
+ align-items: center;
231
+ text-decoration: none;
232
+ color: var(--ink);
233
+ margin: 0 6px 26px;
234
+ }
235
+ .brand svg {
236
+ width: 34px;
237
+ height: 34px;
238
+ flex: none;
239
+ fill: var(--blue);
240
+ border-radius: 8px;
241
+ box-shadow: 0 2px 6px rgba(36, 89, 207, 0.32), inset 0 1px 0 rgba(255, 255, 255, 0.2);
242
+ }
243
+ .brand svg path { stroke: #fff; fill: none; stroke-width: 2.4; }
244
+ .brand strong { font-size: 15.5px; font-weight: 700; letter-spacing: -0.02em; display: block; color: var(--ink); }
245
+ .brand small { display: block; font-size: 11px; color: var(--muted); margin-top: 1px; font-weight: 500; }
246
+
247
+ .sidebar nav { display: grid; gap: 3px; }
248
+ .sidebar nav button {
249
+ position: relative;
250
+ display: flex;
251
+ align-items: center;
252
+ gap: 11px;
253
+ padding: 10px 14px;
254
+ text-align: left;
255
+ color: var(--text-secondary);
256
+ font-weight: 550;
257
+ font-size: 13.5px;
258
+ border-radius: var(--radius);
259
+ min-height: 40px;
260
+ transition: background 140ms var(--ease-out), color 140ms var(--ease-out);
261
+ }
262
+ .sidebar nav button:hover:not([aria-current=page]) { background: var(--line-subtle); color: var(--ink); }
263
+ .sidebar nav svg {
264
+ width: 17px;
265
+ height: 17px;
266
+ fill: none;
267
+ stroke: currentColor;
268
+ stroke-width: 1.8;
269
+ stroke-linecap: round;
270
+ stroke-linejoin: round;
271
+ flex-shrink: 0;
272
+ }
273
+ .sidebar nav [aria-current=page] {
274
+ color: var(--blue);
275
+ background: var(--blue-soft);
276
+ font-weight: 600;
277
+ }
278
+ .sidebar nav [aria-current=page]::before {
279
+ content: "";
280
+ position: absolute;
281
+ left: 0;
282
+ top: 9px;
283
+ bottom: 9px;
284
+ width: 3px;
285
+ border-radius: 0 3px 3px 0;
286
+ background: var(--blue);
287
+ }
288
+
289
+ .sidebar-foot {
290
+ margin-top: auto;
291
+ font-size: 11.5px;
292
+ color: var(--muted);
293
+ padding: 12px 14px;
294
+ background: var(--bg);
295
+ border: 1px solid var(--line);
296
+ border-radius: var(--radius);
297
+ }
298
+ .sidebar-foot small { display: block; margin-top: 3px; font-size: 11px; color: var(--faint); }
299
+ .dot {
300
+ display: inline-block;
301
+ width: 7px;
302
+ height: 7px;
303
+ background: var(--green);
304
+ border-radius: 50%;
305
+ margin-right: 6px;
306
+ box-shadow: 0 0 0 2px var(--green-subtle);
307
+ }
308
+
309
+ /* Workspace */
310
+ .workspace { min-width: 0; display: flex; flex-direction: column; background: transparent; }
311
+
312
+ /* Topbar */
313
+ .topbar {
314
+ display: flex;
315
+ align-items: center;
316
+ justify-content: space-between;
317
+ padding: 0 32px;
318
+ height: 54px;
319
+ border-bottom: 1px solid var(--line);
320
+ background: rgba(255, 255, 255, 0.82);
321
+ backdrop-filter: blur(10px);
322
+ font-size: 12.5px;
323
+ color: var(--muted);
324
+ position: sticky;
325
+ top: 0;
326
+ z-index: 10;
327
+ }
328
+ #connection { display: inline-flex; align-items: center; gap: 7px; font-weight: 550; color: var(--text-secondary); }
329
+ #connection::before {
330
+ content: "";
331
+ display: inline-block;
332
+ width: 7px;
333
+ height: 7px;
334
+ border-radius: 50%;
335
+ background: var(--green);
336
+ box-shadow: 0 0 0 2px var(--green-subtle);
337
+ }
338
+ .topbar-actions { display: inline-flex; align-items: center; gap: 10px; }
339
+ #version-badge { cursor: pointer; }
340
+
341
+ /* Main Content */
342
+ main { width: 100%; max-width: 1160px; margin: 0 auto; padding: 30px 32px 56px; flex: 1; min-width: 0; }
343
+ footer {
344
+ padding: 14px 32px;
345
+ font-size: 11.5px;
346
+ color: var(--faint);
347
+ border-top: 1px solid var(--line);
348
+ background: rgba(255, 255, 255, 0.9);
349
+ text-align: center;
350
+ }
351
+
352
+ /* Page headings */
353
+ .page-heading {
354
+ display: flex;
355
+ align-items: flex-end;
356
+ justify-content: space-between;
357
+ gap: 16px;
358
+ margin-bottom: 24px;
359
+ }
360
+ .page-heading p { margin-top: 4px; color: var(--muted); font-size: 13.5px; }
361
+
362
+ /* ---------- Panels ---------- */
363
+
364
+ .panel {
365
+ border: 1px solid var(--line);
366
+ border-radius: var(--radius-lg);
367
+ background: var(--panel);
368
+ padding: 24px;
369
+ margin-top: 16px;
370
+ min-width: 0;
371
+ box-shadow: var(--shadow-xs);
372
+ }
373
+ .settings { margin-top: 8px; }
374
+ .section-heading {
375
+ display: flex;
376
+ align-items: flex-start;
377
+ justify-content: space-between;
378
+ gap: 16px;
379
+ margin-bottom: 18px;
380
+ }
381
+
382
+ /* ---------- Hero Status Band ---------- */
383
+
384
+ .current-strip {
385
+ display: grid;
386
+ grid-template-columns: auto minmax(0, 1fr) auto;
387
+ align-items: center;
388
+ gap: 18px;
389
+ padding: 16px 22px;
390
+ background:
391
+ linear-gradient(180deg, rgba(36, 89, 207, 0.03), transparent 55%),
392
+ var(--panel);
393
+ border: 1px solid var(--line);
394
+ border-radius: var(--radius-lg);
395
+ box-shadow: var(--shadow-sm);
396
+ margin-bottom: 8px;
397
+ }
398
+ .current-strip > div {
399
+ display: flex;
400
+ gap: 12px;
401
+ align-items: baseline;
402
+ min-width: 0;
403
+ flex-wrap: wrap;
404
+ }
405
+ .current-strip strong {
406
+ font-size: 17px;
407
+ font-weight: 700;
408
+ color: var(--ink);
409
+ overflow-wrap: anywhere;
410
+ font-family: "Cascadia Code", "Fira Code", Consolas, monospace;
411
+ letter-spacing: -0.01em;
412
+ font-variant-numeric: tabular-nums;
413
+ }
414
+ .current-strip span:not(.badge) { font-size: 12.5px; color: var(--muted); }
415
+ .current-strip a {
416
+ font-size: 12.5px;
417
+ font-weight: 600;
418
+ display: inline-flex;
419
+ align-items: center;
420
+ gap: 4px;
421
+ white-space: nowrap;
422
+ }
423
+ .current-strip a::after { content: "→"; font-size: 13px; transition: transform 140ms var(--ease-out); }
424
+ .current-strip a:hover::after { transform: translateX(2px); }
425
+
426
+ /* ---------- Badges ---------- */
427
+
428
+ .badge {
429
+ display: inline-flex;
430
+ align-items: center;
431
+ gap: 5px;
432
+ border: 1px solid var(--line);
433
+ border-radius: 999px;
434
+ padding: 2px 10px;
435
+ font-size: 11px;
436
+ font-weight: 600;
437
+ line-height: 1.5;
438
+ white-space: nowrap;
439
+ color: var(--muted);
440
+ background: var(--bg);
441
+ }
442
+ .badge.good { background: var(--green-soft); color: var(--green); border-color: var(--green-border); }
443
+ .badge.warn { background: var(--amber-soft); color: var(--amber); border-color: var(--amber-border); }
444
+ .badge.bad { background: var(--red-soft); color: var(--red); border-color: var(--red-border); }
445
+ .badge.blue { background: var(--blue-soft); color: var(--blue); border-color: var(--blue-border); }
446
+ /* 内联 SVG 图标(热切/重启):与文字同行对齐,不随 emoji 字形跳基线 */
447
+ .badge svg { display: block; flex-shrink: 0; }
448
+
449
+ /* ---------- Config Grid & Mode Cards ---------- */
450
+
451
+ .config-grid {
452
+ display: grid;
453
+ grid-template-columns: minmax(260px, 1fr) minmax(320px, 1.25fr);
454
+ gap: 32px;
455
+ }
456
+ #persona-description {
457
+ margin: 10px 0;
458
+ background: var(--bg);
459
+ border: 1px solid var(--line-subtle);
460
+ border-radius: var(--radius-sm);
461
+ padding: 10px 14px;
462
+ font-size: 12.5px;
463
+ color: var(--text-secondary);
464
+ line-height: 1.6;
465
+ }
466
+
467
+ .mode-options { display: grid; gap: 10px; }
468
+ .mode-option {
469
+ margin: 0;
470
+ display: flex;
471
+ align-items: flex-start;
472
+ gap: 12px;
473
+ border: 1px solid var(--line);
474
+ padding: 13px 15px;
475
+ border-radius: var(--radius);
476
+ background: var(--panel);
477
+ cursor: pointer;
478
+ transition: border-color 150ms var(--ease-out), background 150ms var(--ease-out), box-shadow 150ms var(--ease-out);
479
+ }
480
+ .mode-option input[type=radio] { margin-top: 2px; accent-color: var(--blue); width: 16px; height: 16px; }
481
+ .mode-option:hover { border-color: var(--line-strong); background: var(--panel-hover); }
482
+ .mode-option:has(input:checked) {
483
+ border-color: var(--blue);
484
+ background: var(--blue-soft);
485
+ box-shadow: 0 0 0 1px var(--blue), var(--shadow-xs);
486
+ }
487
+ .mode-option:has(input:focus-visible) { outline: 2px solid var(--blue); outline-offset: 2px; }
488
+ .mode-option strong { font-size: 13.5px; font-weight: 650; color: var(--ink); display: block; }
489
+ .mode-option small { font-weight: 400; font-size: 12px; color: var(--muted); display: block; margin-top: 2px; }
490
+
491
+ /* Apply Bar */
492
+ .apply-bar {
493
+ display: flex;
494
+ align-items: center;
495
+ justify-content: space-between;
496
+ gap: 16px;
497
+ margin-top: 24px;
498
+ padding-top: 18px;
499
+ border-top: 1px solid var(--line-subtle);
500
+ }
501
+ .actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; flex-shrink: 0; }
502
+ .check-label { display: flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 500; color: var(--text-secondary); margin: 0; cursor: pointer; }
503
+
504
+ /* ---------- Harness Capsule Grid ---------- */
505
+
506
+ #harness-list { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
507
+
508
+ .harness-grid {
509
+ display: grid;
510
+ grid-template-columns: repeat(auto-fill, minmax(205px, 1fr));
511
+ gap: 10px;
512
+ }
513
+
514
+ .harness-capsule {
515
+ display: flex;
516
+ align-items: center;
517
+ justify-content: space-between;
518
+ flex-wrap: wrap;
519
+ gap: 8px;
520
+ padding: 10px 12px;
521
+ background: var(--panel);
522
+ border: 1px solid var(--line);
523
+ border-radius: var(--radius);
524
+ box-shadow: var(--shadow-xs);
525
+ cursor: pointer;
526
+ user-select: none;
527
+ transition: border-color 150ms var(--ease-out), background 150ms var(--ease-out),
528
+ box-shadow 150ms var(--ease-out), transform 150ms var(--ease-out);
529
+ }
530
+ .harness-capsule:hover {
531
+ border-color: var(--line-strong);
532
+ background: var(--panel-hover);
533
+ box-shadow: var(--shadow-sm);
534
+ transform: translateY(-1px);
535
+ }
536
+ .harness-capsule:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
537
+ .harness-capsule.is-on { border-color: var(--blue-border); }
538
+ .harness-capsule.is-selected {
539
+ border-color: var(--blue);
540
+ box-shadow: 0 0 0 1px var(--blue), var(--shadow-sm);
541
+ background: #f7faff;
542
+ }
543
+ .harness-capsule.is-selected .capsule-title { color: var(--blue); }
544
+ .harness-capsule.is-uninstalled {
545
+ opacity: 0.6;
546
+ border-style: dashed;
547
+ background: transparent;
548
+ box-shadow: none;
549
+ }
550
+ .harness-capsule.is-uninstalled:hover { opacity: 0.9; }
551
+ .harness-capsule.is-bad { border-color: var(--red-border); }
552
+
553
+ .capsule-body { display: flex; align-items: center; gap: 9px; flex: 1; min-width: 0; padding: 0; text-align: left; }
554
+ .capsule-body:hover:not(:disabled) { background: transparent; }
555
+ .capsule-info { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
556
+ .capsule-name-row { display: flex; align-items: center; gap: 6px; min-width: 0; }
557
+ .capsule-title {
558
+ font-size: 13.5px;
559
+ font-weight: 650;
560
+ color: var(--ink);
561
+ letter-spacing: -0.01em;
562
+ white-space: nowrap;
563
+ overflow: hidden;
564
+ text-overflow: ellipsis;
565
+ font-family: "Cascadia Code", "Fira Code", Consolas, monospace;
566
+ }
567
+ .capsule-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--line-strong); flex-shrink: 0; }
568
+ .capsule-dot.is-on { background: var(--green); box-shadow: 0 0 0 2px var(--green-subtle); }
569
+ .capsule-badge-default {
570
+ font-size: 10px;
571
+ padding: 0 5px;
572
+ border-radius: 4px;
573
+ background: var(--blue-subtle);
574
+ color: var(--blue);
575
+ font-weight: 650;
576
+ line-height: 1.5;
577
+ flex-shrink: 0;
578
+ }
579
+ .capsule-status-text { font-size: 12px; color: var(--muted); line-height: 1.3; }
580
+
581
+ /* 行内人设/模式下拉:小而安静,单独指定时才染蓝 */
582
+ .capsule-config {
583
+ display: flex;
584
+ align-items: center;
585
+ gap: 4px;
586
+ flex-wrap: nowrap;
587
+ /* 独占一行:宽度不够时宁可换行,也不把下拉挤成“人设·” */
588
+ flex: 1 0 100%;
589
+ margin-top: 4px;
590
+ }
591
+ .drawer-config { margin: 0 0 12px; }
592
+ .mini-select {
593
+ font: inherit;
594
+ font-size: 11.5px;
595
+ line-height: 1.2;
596
+ color: var(--text-secondary);
597
+ background: #fff;
598
+ border: 1px solid var(--line);
599
+ border-radius: 5px;
600
+ padding: 3px 4px;
601
+ /* 两者平分这一行,绝不把胶囊挤破 */
602
+ flex: 1 1 0;
603
+ min-width: 0;
604
+ cursor: pointer;
605
+ transition: border-color 120ms var(--ease-out), color 120ms var(--ease-out), background-color 120ms var(--ease-out);
606
+ }
607
+ .mini-select:hover { border-color: var(--line-strong); color: var(--ink); }
608
+ .mini-select:focus-visible { outline: 2px solid var(--blue); outline-offset: 1px; }
609
+ .mini-select.is-override {
610
+ color: var(--blue);
611
+ border-color: var(--blue-border);
612
+ background: var(--blue-subtle);
613
+ font-weight: 600;
614
+ }
615
+
616
+ .harness-avatar {
617
+ border-radius: 7px;
618
+ display: block;
619
+ flex-shrink: 0;
620
+ box-shadow: 0 1px 2px rgba(16, 24, 40, 0.14);
621
+ object-fit: contain;
622
+ }
623
+ .harness-avatar.fallback {
624
+ display: grid;
625
+ place-items: center;
626
+ background: linear-gradient(180deg, #f8fafc, #eef1f5);
627
+ border: 1px solid var(--line);
628
+ color: var(--text-secondary);
629
+ font-size: 12px;
630
+ font-weight: 700;
631
+ box-shadow: none;
632
+ }
633
+ /* 抽屉头 34px 档位:CSP 禁内联 style,用类名切换尺寸 */
634
+ .harness-avatar-lg { width: 34px; height: 34px; }
635
+ .harness-avatar-md { width: 28px; height: 28px; }
636
+
637
+ /* ---------- Drawer (expanded harness) ---------- */
638
+
639
+ .harness-drawer {
640
+ margin-top: 6px;
641
+ background: var(--panel);
642
+ border: 1px solid var(--blue-border);
643
+ border-radius: var(--radius-lg);
644
+ box-shadow: var(--shadow-md), 0 0 0 1px var(--blue-glow);
645
+ overflow: hidden;
646
+ animation: drawerSlide 180ms var(--ease-out);
647
+ }
648
+ @keyframes drawerSlide {
649
+ from { opacity: 0; transform: translateY(-5px); }
650
+ to { opacity: 1; transform: translateY(0); }
651
+ }
652
+
653
+ .drawer-header {
654
+ display: flex;
655
+ justify-content: space-between;
656
+ align-items: center;
657
+ gap: 16px;
658
+ padding: 13px 18px;
659
+ background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
660
+ border-bottom: 1px solid var(--line);
661
+ }
662
+ .drawer-title-group { display: flex; align-items: center; gap: 12px; min-width: 0; }
663
+ .drawer-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
664
+ .drawer-name-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
665
+ .drawer-title { font-size: 15.5px; font-weight: 700; color: var(--ink); letter-spacing: -0.01em; font-family: "Cascadia Code", "Fira Code", Consolas, monospace; }
666
+ .drawer-effect { font-size: 12px; color: var(--text-secondary); line-height: 1.4; }
667
+ .drawer-actions { display: flex; align-items: center; gap: 12px; flex-shrink: 0; }
668
+ .drawer-close {
669
+ width: 28px;
670
+ height: 28px;
671
+ display: grid;
672
+ place-items: center;
673
+ border: 1px solid var(--line);
674
+ background: var(--panel);
675
+ color: var(--muted);
676
+ border-radius: var(--radius-sm);
677
+ cursor: pointer;
678
+ font-size: 12px;
679
+ font-weight: bold;
680
+ transition: background 140ms var(--ease-out), color 140ms var(--ease-out), border-color 140ms var(--ease-out);
681
+ min-height: auto;
682
+ padding: 0;
683
+ }
684
+ .drawer-close:hover { background: var(--line-subtle); color: var(--ink); border-color: var(--line-strong); }
685
+
686
+ .drawer-body { padding: 14px 18px 16px; background: var(--bg); }
687
+
688
+ /* ---------- Switch ---------- */
689
+
690
+ .switch { position: relative; display: inline-flex; flex: none; width: 38px; height: 22px; cursor: pointer; }
691
+ .switch input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: inherit; z-index: 1; }
692
+ .switch-track {
693
+ position: relative;
694
+ flex: 1;
695
+ border-radius: 999px;
696
+ background: var(--line-strong);
697
+ transition: background 180ms var(--ease-out);
698
+ }
699
+ .switch-track::after {
700
+ content: "";
701
+ position: absolute;
702
+ top: 2.5px;
703
+ left: 2.5px;
704
+ width: 17px;
705
+ height: 17px;
706
+ border-radius: 50%;
707
+ background: #fff;
708
+ box-shadow: 0 1px 3px rgba(16, 24, 40, 0.25);
709
+ transition: transform 180ms var(--ease-out);
710
+ }
711
+ .switch input:checked + .switch-track { background: var(--blue); }
712
+ .switch input:checked + .switch-track::after { transform: translateX(16px); }
713
+ .switch input:focus-visible + .switch-track { outline: 2px solid var(--blue); outline-offset: 2px; }
714
+ .switch input:disabled { cursor: not-allowed; }
715
+ .switch input:disabled + .switch-track { opacity: 0.45; }
716
+
717
+ .switch.switch-sm { width: 32px; height: 18px; }
718
+ .switch.switch-sm .switch-track::after { top: 2px; left: 2px; width: 14px; height: 14px; }
719
+ .switch.switch-sm input:checked + .switch-track::after { transform: translateX(14px); }
720
+
721
+ /* ---------- Drawer Detail Rows ---------- */
722
+
723
+ .channel-row {
724
+ display: flex;
725
+ justify-content: space-between;
726
+ align-items: center;
727
+ gap: 16px;
728
+ padding: 10px 0;
729
+ border-top: 1px dashed var(--line);
730
+ }
731
+ .channel-row:first-of-type { border-top: none; }
732
+ .channel-row strong { font-size: 13.5px; font-weight: 600; color: var(--ink); }
733
+ .channel-row .help { font-size: 12px; margin-top: 2px; }
734
+ .channel-row button { min-width: 64px; text-align: center; }
735
+
736
+ /* 实测证据行:一个按钮 + 结论,放在通道列表之前 */
737
+ .verify-row {
738
+ display: flex;
739
+ align-items: center;
740
+ gap: 12px;
741
+ padding: 10px 0;
742
+ border-bottom: 1px dashed var(--line);
743
+ margin-bottom: 6px;
744
+ }
745
+ .verify-row button { flex: none; min-width: 88px; text-align: center; }
746
+ .verify-row .help { font-size: 12px; margin-top: 3px; }
747
+ .verify-row .badge { margin-bottom: 2px; }
748
+
749
+ .detail-note {
750
+ font-size: 12px;
751
+ color: var(--muted);
752
+ padding: 10px 13px;
753
+ background: var(--panel);
754
+ border: 1px solid var(--line);
755
+ border-radius: var(--radius-sm);
756
+ margin-bottom: 10px;
757
+ }
758
+ .advanced { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line); }
759
+ summary { cursor: pointer; font-weight: 550; font-size: 12.5px; color: var(--text-secondary); outline: none; }
760
+ summary:hover { color: var(--ink); }
761
+ .profile-options {
762
+ display: flex;
763
+ flex-wrap: wrap;
764
+ gap: 6px 14px;
765
+ margin: 10px 0;
766
+ padding: 10px 13px;
767
+ background: var(--panel);
768
+ border: 1px solid var(--line);
769
+ border-radius: var(--radius-sm);
770
+ }
771
+ .profile-options label { display: inline-flex; align-items: center; gap: 6px; margin: 0; font-size: 12.5px; font-weight: 500; color: var(--text-secondary); cursor: pointer; }
772
+
773
+ /* ---------- Notices ---------- */
774
+
775
+ .notice {
776
+ margin-bottom: 20px;
777
+ background: var(--blue-soft);
778
+ border: 1px solid var(--blue-border);
779
+ border-radius: var(--radius);
780
+ padding: 12px 16px;
781
+ color: #173fa5;
782
+ font-size: 13px;
783
+ font-weight: 550;
784
+ box-shadow: var(--shadow-xs);
785
+ }
786
+ .notice.error { background: var(--red-soft); border-color: var(--red-border); color: var(--red); }
787
+ .notice.warn { background: var(--amber-soft); border-color: var(--amber-border); color: var(--amber); }
788
+
789
+ /* ---------- Empty State ---------- */
790
+
791
+ .empty {
792
+ display: grid;
793
+ place-items: center;
794
+ text-align: center;
795
+ padding: 26px 20px;
796
+ border: 1px dashed var(--line);
797
+ border-radius: var(--radius);
798
+ color: var(--muted);
799
+ font-size: 13px;
800
+ line-height: 1.6;
801
+ background: rgba(255, 255, 255, 0.5);
802
+ }
803
+
804
+ /* ---------- Launch Settings ---------- */
805
+
806
+ .launch-settings { padding: 0; }
807
+ .launch-settings > summary { padding: 15px 20px; font-size: 13.5px; }
808
+ .launch-settings[open] > summary { border-bottom: 1px solid var(--line-subtle); }
809
+ .launch-settings .detail-body { padding: 14px 20px 16px; }
810
+ .inline-form { display: flex; align-items: center; gap: 12px; margin-top: 10px; flex-wrap: wrap; }
811
+ .inline-form select { max-width: 260px; }
812
+
813
+ /* ---------- Library Page ---------- */
814
+
815
+ .library-layout { display: grid; grid-template-columns: 248px minmax(0, 1fr); gap: 16px; }
816
+ .library-layout > .panel { margin-top: 0; }
817
+ .persona-sidebar { padding: 14px; align-self: start; }
818
+ .persona-sidebar input { margin-bottom: 12px; }
819
+ .persona-item {
820
+ display: block;
821
+ text-align: left;
822
+ width: 100%;
823
+ padding: 10px 12px;
824
+ border-radius: var(--radius);
825
+ border: 1px solid transparent;
826
+ background: transparent;
827
+ margin-bottom: 4px;
828
+ min-height: auto;
829
+ transition: background 140ms var(--ease-out), border-color 140ms var(--ease-out);
830
+ }
831
+ .persona-item:hover { background: var(--bg); border-color: var(--line); }
832
+ .persona-item.selected { background: var(--blue-soft); border-color: var(--blue-border); color: var(--blue); }
833
+ .persona-item strong { font-weight: 600; display: block; font-size: 13.5px; }
834
+ .persona-item small { display: block; margin-top: 2px; font-size: 11.5px; color: var(--muted); }
835
+ .persona-item.selected small { color: var(--blue); }
836
+ .two-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
837
+ .editor-panel label { font-size: 12.5px; margin-top: 14px; }
838
+ .two-fields label { margin-top: 0; }
839
+ .editor-panel textarea {
840
+ min-height: 320px;
841
+ font-family: "Cascadia Code", "Fira Code", Consolas, monospace;
842
+ font-size: 13px;
843
+ line-height: 1.65;
844
+ }
845
+ .editor-actions {
846
+ display: flex;
847
+ justify-content: space-between;
848
+ align-items: center;
849
+ gap: 12px;
850
+ margin-top: 22px;
851
+ padding-top: 18px;
852
+ border-top: 1px solid var(--line);
853
+ }
854
+
855
+ /* ---------- Diagnostics ---------- */
856
+
857
+ .table-scroll { overflow-x: auto; margin: 0 -24px; padding: 0 24px; }
858
+ .capture-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
859
+ .capture-table th, .capture-table td {
860
+ padding: 11px 14px;
861
+ text-align: left;
862
+ border-bottom: 1px solid var(--line);
863
+ vertical-align: top;
864
+ }
865
+ .capture-table th {
866
+ font-weight: 600;
867
+ font-size: 11.5px;
868
+ text-transform: none;
869
+ letter-spacing: 0.01em;
870
+ background: var(--bg);
871
+ color: var(--muted);
872
+ border-bottom: 1px solid var(--line-strong);
873
+ }
874
+ .capture-table tbody tr:hover td { background: var(--panel-hover); }
875
+ .result-row {
876
+ display: grid;
877
+ grid-template-columns: 140px 120px minmax(0, 1fr);
878
+ gap: 16px;
879
+ padding: 12px 0;
880
+ border-top: 1px solid var(--line);
881
+ font-size: 12.5px;
882
+ align-items: start;
883
+ }
884
+ .result-row:first-child { border-top: none; }
885
+ .result-row strong { font-size: 13.5px; }
886
+ .result-row .error-text { color: var(--red); }
887
+
888
+ /* ---------- Responsive ---------- */
889
+
890
+ @media (max-width: 1100px) {
891
+ .app-shell { grid-template-columns: 196px minmax(0, 1fr); }
892
+ main { padding: 22px 20px 40px; }
893
+ .config-grid { grid-template-columns: minmax(0, 1fr); gap: 20px; }
894
+ .library-layout { grid-template-columns: minmax(0, 1fr); }
895
+ .table-scroll { margin: 0; padding: 0; }
896
+ }
897
+ @media (max-width: 760px) {
898
+ .app-shell { display: block; }
899
+ .sidebar { position: static; height: auto; padding: 14px; border-right: 0; border-bottom: 1px solid var(--line); }
900
+ .brand { margin: 0 0 12px; }
901
+ .sidebar nav { display: flex; gap: 4px; }
902
+ .sidebar nav button { flex: 1; padding: 8px 6px; justify-content: center; font-size: 12px; }
903
+ .sidebar nav [aria-current=page]::before { display: none; }
904
+ .sidebar-foot { display: none; }
905
+ .topbar { padding: 0 16px; }
906
+ main { padding: 16px; }
907
+ .config-grid { grid-template-columns: 1fr; gap: 16px; }
908
+ .library-layout { grid-template-columns: 1fr; }
909
+ .current-strip { grid-template-columns: 1fr; gap: 10px; padding: 14px 16px; }
910
+ .result-row { grid-template-columns: 110px 100px minmax(0, 1fr); gap: 10px; }
911
+ .page-heading { flex-direction: column; align-items: stretch; }
912
+ .page-heading .actions { justify-content: flex-start; }
913
+ .current-strip > .badge { justify-self: start; }
914
+ .apply-bar { flex-direction: column; align-items: stretch; gap: 12px; }
915
+ .section-heading { flex-wrap: wrap; }
916
+ .section-heading .check-label { flex-shrink: 0; margin-left: auto; }
917
+ }