cardbuddy 0.9.1 → 0.11.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 (64) hide show
  1. package/README.md +21 -6
  2. package/dist/cli.js +17 -6
  3. package/dist/config.js +20 -2
  4. package/dist/portGuard.js +293 -0
  5. package/dist/preview-editor/assets/index-B4VZgNSK.css +1 -0
  6. package/dist/preview-editor/assets/index-jl4YJzKv.js +180 -0
  7. package/dist/preview-editor/index.html +2 -2
  8. package/dist/server-core/_vendor/finmall-env.mjs +4 -4
  9. package/dist/server-core/ai-chat.js +3710 -0
  10. package/dist/server-core/assetProxy.js +3 -22
  11. package/dist/server-core/buildDataFromSchema.js +36 -4
  12. package/dist/server-core/cardFiles.js +95 -2
  13. package/dist/server-core/cardImage.js +14 -0
  14. package/dist/server-core/cardListCache.js +0 -17
  15. package/dist/server-core/cardSave.js +2 -0
  16. package/dist/server-core/cardValidate.js +3 -0
  17. package/dist/server-core/index.js +6 -5
  18. package/dist/server-core/logStream.js +1 -6
  19. package/dist/server-core/sliceUpload.js +8 -1
  20. package/dist/standalone.js +24 -8
  21. package/dist/vendored/App.vue +405 -125
  22. package/dist/vendored/api.js +4 -4
  23. package/dist/vendored/assets/svgIcons/icons.svg +28 -0
  24. package/dist/vendored/components/AiSettings.vue +392 -0
  25. package/dist/vendored/components/CardGallery.vue +51 -5
  26. package/dist/vendored/components/ChatComposer.vue +413 -0
  27. package/dist/vendored/components/ChatMessage.vue +383 -0
  28. package/dist/vendored/components/ChatPanel.vue +937 -0
  29. package/dist/vendored/components/ComponentPalette.vue +1 -1
  30. package/dist/vendored/components/DebugPanel.vue +732 -90
  31. package/dist/vendored/components/Dropdown.vue +4 -2
  32. package/dist/vendored/components/MarkdownBody.vue +64 -0
  33. package/dist/vendored/components/NewCardDialog.vue +132 -0
  34. package/dist/vendored/components/NumberStepper.vue +125 -0
  35. package/dist/vendored/components/ProgressSlider.vue +9 -1
  36. package/dist/vendored/components/RemotePreview.vue +1 -1
  37. package/dist/vendored/components/StructureTree.vue +2 -2
  38. package/dist/vendored/main.js +1 -0
  39. package/dist/vendored/runtime/adapter.js +1 -1
  40. package/dist/vendored/runtime/ai-api.js +165 -0
  41. package/dist/vendored/runtime/clipboard-node.js +0 -5
  42. package/dist/vendored/runtime/markdown.js +140 -0
  43. package/dist/vendored/skills/.manifest.json +1101 -0
  44. package/dist/vendored/skills/dsl-card-generator/scripts/autofix_schema.py +249 -0
  45. package/dist/vendored/skills/dsl-card-generator/scripts/check_topology.py +678 -0
  46. package/dist/vendored/skills/dsl-card-generator/scripts/derive_data_json.py +81 -0
  47. package/dist/vendored/skills/dsl-card-generator/scripts/lib/paths.py +105 -0
  48. package/dist/vendored/skills/dsl-card-generator/scripts/validate_schema.py +624 -0
  49. package/dist/vendored/skills/finmall-card-sync/scripts/api_batch_sync.mjs +900 -0
  50. package/dist/vendored/skills/finmall-card-sync/scripts/slice_upload.mjs +699 -0
  51. package/dist/vendored/skills/shared/finmall-config.mjs +124 -0
  52. package/dist/vendored/styles/ai-chat.css +1850 -0
  53. package/dist/vendored/styles/app.css +725 -213
  54. package/dist/vendored/styles/card-gallery.css +48 -10
  55. package/dist/vendored/styles/card-select.css +13 -6
  56. package/dist/vendored/styles/dropdown.css +1 -2
  57. package/dist/vendored/styles/json-tree.css +3 -3
  58. package/dist/vendored/styles/node-inspector.css +13 -289
  59. package/dist/vendored/styles/setters.css +48 -49
  60. package/dist/vendored/styles/structure-tree.css +6 -11
  61. package/package.json +1 -1
  62. package/dist/preview-editor/assets/index-B9MOriNv.js +0 -80
  63. package/dist/preview-editor/assets/index-CwD14gm3.css +0 -1
  64. package/dist/vendored/assets/logo.png +0 -0
@@ -0,0 +1,1850 @@
1
+ /* ===== AI 开发助手(悬浮入口 + 右侧对话面板)=====
2
+ 尺寸 / 节奏全部取自《CardBuddy UI 尺寸与节奏规范手册》Token(间距 8pt 网格、控件高度阶梯、
3
+ 圆角阶梯、字号阶梯、图标尺寸、z-index 阶梯、发丝线 --hairline),颜色取自《深浅主题配色规范手册》。
4
+ 交互对齐 Codex:思考过程折叠成一行(进行中给计时 + 最新一行预览),工具调用一行摘要可展开,
5
+ 正文走 Markdown,输入框自适应高度,生成中发送键切成终止键。 */
6
+
7
+ /* ---- 悬浮入口(FAB)---- */
8
+ .ai-fab {
9
+ position: fixed;
10
+ right: var(--space-6);
11
+ bottom: var(--space-8);
12
+ width: var(--ctrl-xl);
13
+ height: var(--ctrl-xl);
14
+ aspect-ratio: 1;
15
+ padding: 0;
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ border: 1px solid var(--accent-border);
20
+ border-radius: var(--radius-pill);
21
+ background: var(--accent);
22
+ color: var(--on-accent);
23
+ cursor: pointer;
24
+ box-shadow: var(--shadow-3);
25
+ z-index: var(--z-panel);
26
+ transition: background 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;
27
+ }
28
+ .ai-fab:hover {
29
+ background: var(--accent-hover);
30
+ transform: translateY(-1px);
31
+ box-shadow: var(--shadow-4);
32
+ }
33
+ .ai-fab:active {
34
+ transform: translateY(0);
35
+ }
36
+ .ai-fab__icon {
37
+ width: var(--icon-lg);
38
+ height: var(--icon-lg);
39
+ flex-shrink: 0;
40
+ }
41
+ /* 面板打开时让位:淡出并禁用命中,避免与面板右缘重叠 */
42
+ .ai-fab--active {
43
+ opacity: 0;
44
+ pointer-events: none;
45
+ transform: scale(0.8);
46
+ }
47
+
48
+ /* ---- 面板骨架 ---- */
49
+ .aichat {
50
+ position: fixed;
51
+ top: 0;
52
+ right: 0;
53
+ bottom: 0;
54
+ min-width: 320px;
55
+ max-width: 100vw;
56
+ display: flex;
57
+ flex-direction: column;
58
+ background: var(--bg-panel);
59
+ border-left: var(--hairline) solid var(--border);
60
+ box-shadow: var(--shadow-5);
61
+ z-index: var(--z-appmodal);
62
+ transform: translateX(100%);
63
+ transition: transform 0.2s ease, width 0.12s ease;
64
+ pointer-events: none;
65
+ }
66
+ .aichat.is-open {
67
+ transform: translateX(0);
68
+ pointer-events: auto;
69
+ }
70
+
71
+ /* 左缘拖拽把手:静止淡显,hover / 拖拽上色(与 .splitter 同一语言) */
72
+ .aichat__grip {
73
+ position: absolute;
74
+ left: -3px;
75
+ top: 0;
76
+ bottom: 0;
77
+ width: 6px;
78
+ cursor: col-resize;
79
+ background: transparent;
80
+ z-index: var(--z-base);
81
+ transition: background 0.15s ease;
82
+ }
83
+ .aichat__grip:hover,
84
+ .aichat__grip.is-dragging {
85
+ background: var(--splitter);
86
+ }
87
+
88
+ /* ---- 头部 ---- */
89
+ .aichat__head {
90
+ flex-shrink: 0;
91
+ display: flex;
92
+ align-items: center;
93
+ gap: var(--space-2);
94
+ padding: var(--space-3) var(--space-4);
95
+ border-bottom: var(--hairline) solid var(--border);
96
+ background: var(--bg-bar);
97
+ }
98
+ .aichat__logo {
99
+ width: var(--ctrl-sm);
100
+ height: var(--ctrl-sm);
101
+ aspect-ratio: 1;
102
+ flex-shrink: 0;
103
+ display: flex;
104
+ align-items: center;
105
+ justify-content: center;
106
+ border-radius: var(--radius-sm);
107
+ background: var(--accent-soft);
108
+ color: var(--accent);
109
+ }
110
+ .aichat__logo svg {
111
+ width: var(--icon-md);
112
+ height: var(--icon-md);
113
+ }
114
+ .aichat__titles {
115
+ flex: 1;
116
+ min-width: 0;
117
+ display: flex;
118
+ flex-direction: column;
119
+ gap: 0;
120
+ }
121
+ .aichat__title {
122
+ font-size: var(--fs-md);
123
+ line-height: var(--lh-md);
124
+ font-weight: 600;
125
+ color: var(--text);
126
+ }
127
+ .aichat__sub {
128
+ font-size: var(--fs-sm);
129
+ line-height: var(--lh-sm);
130
+ color: var(--text-faint);
131
+ overflow: hidden;
132
+ text-overflow: ellipsis;
133
+ white-space: nowrap;
134
+ }
135
+ .aichat__acts {
136
+ display: flex;
137
+ align-items: center;
138
+ gap: var(--space-1);
139
+ flex-shrink: 0;
140
+ }
141
+
142
+ /* 面板内图标按钮:正方 28(--ctrl-sm),hover 给 --bg-hover + 文本色 */
143
+ .aichat__iconbtn {
144
+ width: var(--ctrl-sm);
145
+ height: var(--ctrl-sm);
146
+ aspect-ratio: 1;
147
+ padding: 0;
148
+ display: inline-flex;
149
+ align-items: center;
150
+ justify-content: center;
151
+ border: none;
152
+ border-radius: var(--radius-sm);
153
+ background: transparent;
154
+ color: var(--text-dim);
155
+ cursor: pointer;
156
+ transition: background 0.15s ease, color 0.15s ease;
157
+ }
158
+ .aichat__iconbtn svg {
159
+ width: var(--icon-md);
160
+ height: var(--icon-md);
161
+ }
162
+ .aichat__iconbtn:hover:not(:disabled) {
163
+ background: var(--bg-hover);
164
+ color: var(--text);
165
+ }
166
+ .aichat__iconbtn:disabled {
167
+ opacity: 0.4;
168
+ cursor: not-allowed;
169
+ }
170
+
171
+ /* ---- 未配置提示条 ---- */
172
+ .aichat__banner {
173
+ flex-shrink: 0;
174
+ display: flex;
175
+ align-items: center;
176
+ gap: var(--space-2);
177
+ padding: var(--space-2) var(--space-4);
178
+ background: var(--warn-soft);
179
+ border-bottom: var(--hairline) solid var(--border);
180
+ color: var(--text);
181
+ font-size: var(--fs-sm);
182
+ line-height: var(--lh-sm);
183
+ }
184
+ .aichat__banner svg {
185
+ width: var(--icon-sm);
186
+ height: var(--icon-sm);
187
+ color: var(--warn);
188
+ flex-shrink: 0;
189
+ }
190
+ .aichat__banner-btn {
191
+ margin-left: auto;
192
+ height: var(--ctrl-xs);
193
+ padding: 0 var(--space-2);
194
+ border: 1px solid var(--warn-border);
195
+ border-radius: var(--radius-sm);
196
+ background: transparent;
197
+ color: var(--warn);
198
+ font-size: var(--fs-sm);
199
+ cursor: pointer;
200
+ }
201
+ .aichat__banner-btn:hover {
202
+ background: var(--bg-hover);
203
+ }
204
+
205
+ /* ---- 消息区 ---- */
206
+ /* 滚动容器:为悬浮「回到底部」提供定位上下文,按钮底缘只跟消息区走,
207
+ 输入区拖高、反馈条出现、窗口缩放都无需 JS 重算偏移 */
208
+ .aichat__scroll {
209
+ position: relative;
210
+ flex: 1;
211
+ min-height: 0;
212
+ display: flex;
213
+ flex-direction: column;
214
+ }
215
+ .aichat__list {
216
+ flex: 1;
217
+ min-height: 0;
218
+ overflow-y: auto;
219
+ padding: var(--space-4);
220
+ display: flex;
221
+ flex-direction: column;
222
+ gap: var(--space-4);
223
+ scrollbar-width: thin;
224
+ scrollbar-color: var(--scroll-thumb) transparent;
225
+ }
226
+ .aichat__list::-webkit-scrollbar {
227
+ width: 8px;
228
+ }
229
+ .aichat__list::-webkit-scrollbar-thumb {
230
+ background: var(--scroll-thumb);
231
+ border-radius: var(--radius-pill);
232
+ }
233
+ .aichat__list::-webkit-scrollbar-thumb:hover {
234
+ background: var(--scroll-thumb-hover);
235
+ }
236
+ .aichat__loading {
237
+ font-size: var(--fs-sm);
238
+ color: var(--text-faint);
239
+ text-align: center;
240
+ padding: var(--space-4) 0;
241
+ }
242
+ .aichat__live {
243
+ font-size: var(--fs-xs);
244
+ line-height: var(--lh-xs);
245
+ color: var(--text-faint);
246
+ padding-left: var(--space-1);
247
+ }
248
+
249
+ /* 空态 */
250
+ .aichat__empty {
251
+ margin: auto;
252
+ max-width: 360px;
253
+ display: flex;
254
+ flex-direction: column;
255
+ align-items: center;
256
+ gap: var(--space-3);
257
+ text-align: center;
258
+ padding: var(--space-6) 0;
259
+ }
260
+ .aichat__empty-logo {
261
+ width: var(--space-9);
262
+ height: var(--space-9);
263
+ display: flex;
264
+ align-items: center;
265
+ justify-content: center;
266
+ border-radius: var(--radius-pill);
267
+ background: var(--accent-soft);
268
+ color: var(--accent);
269
+ }
270
+ .aichat__empty-logo svg {
271
+ width: var(--icon-lg);
272
+ height: var(--icon-lg);
273
+ }
274
+ .aichat__empty-title {
275
+ margin: 0;
276
+ font-size: var(--fs-lg);
277
+ line-height: var(--lh-lg);
278
+ font-weight: 600;
279
+ color: var(--text);
280
+ }
281
+ .aichat__empty-desc {
282
+ margin: 0;
283
+ font-size: var(--fs-sm);
284
+ line-height: var(--lh-sm);
285
+ color: var(--text-dim);
286
+ }
287
+ .aichat__suggests {
288
+ display: flex;
289
+ flex-direction: column;
290
+ gap: var(--space-2);
291
+ width: 100%;
292
+ margin-top: var(--space-1);
293
+ }
294
+ .aichat__suggest {
295
+ display: flex;
296
+ align-items: center;
297
+ gap: var(--space-2);
298
+ width: 100%;
299
+ min-height: var(--ctrl-md);
300
+ padding: var(--space-1) var(--space-3);
301
+ border: 1px solid var(--border);
302
+ border-radius: var(--radius-md);
303
+ background: var(--bg-input);
304
+ color: var(--text-dim);
305
+ font-size: var(--fs-sm);
306
+ line-height: var(--lh-sm);
307
+ text-align: left;
308
+ cursor: pointer;
309
+ transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
310
+ }
311
+ .aichat__suggest svg {
312
+ width: var(--icon-sm);
313
+ height: var(--icon-sm);
314
+ flex-shrink: 0;
315
+ color: var(--text-faint);
316
+ }
317
+ .aichat__suggest:hover {
318
+ border-color: var(--accent-border);
319
+ background: var(--bg-hover);
320
+ color: var(--text);
321
+ }
322
+
323
+ /* 回到底部悬浮按钮:贴消息区底缘(不是面板底缘),随输入区高度自适应 */
324
+ .aichat__jump {
325
+ position: absolute;
326
+ left: 50%;
327
+ bottom: var(--space-3);
328
+ transform: translateX(-50%);
329
+ width: var(--ctrl-sm);
330
+ height: var(--ctrl-sm);
331
+ aspect-ratio: 1;
332
+ padding: 0;
333
+ display: flex;
334
+ align-items: center;
335
+ justify-content: center;
336
+ border: 1px solid var(--border-strong);
337
+ border-radius: var(--radius-pill);
338
+ background: var(--bg-pop);
339
+ color: var(--text-dim);
340
+ cursor: pointer;
341
+ box-shadow: var(--shadow-2);
342
+ z-index: var(--z-pop);
343
+ }
344
+ .aichat__jump svg {
345
+ width: var(--icon-sm);
346
+ height: var(--icon-sm);
347
+ }
348
+ .aichat__jump:hover {
349
+ color: var(--text);
350
+ border-color: var(--accent-border);
351
+ }
352
+
353
+ /* 操作反馈条 */
354
+ .aichat__notice {
355
+ flex-shrink: 0;
356
+ display: flex;
357
+ align-items: center;
358
+ gap: var(--space-2);
359
+ padding: var(--space-2) var(--space-4);
360
+ border-top: var(--hairline) solid var(--border);
361
+ font-size: var(--fs-sm);
362
+ line-height: var(--lh-sm);
363
+ }
364
+ .aichat__notice--ok {
365
+ background: var(--ok-soft);
366
+ color: var(--ok);
367
+ }
368
+ .aichat__notice--warn {
369
+ background: var(--warn-soft);
370
+ color: var(--warn);
371
+ }
372
+ .aichat__notice span {
373
+ flex: 1;
374
+ min-width: 0;
375
+ overflow: hidden;
376
+ text-overflow: ellipsis;
377
+ white-space: nowrap;
378
+ }
379
+ .aichat__notice-x {
380
+ width: var(--ctrl-xs);
381
+ height: var(--ctrl-xs);
382
+ aspect-ratio: 1;
383
+ padding: 0;
384
+ display: inline-flex;
385
+ align-items: center;
386
+ justify-content: center;
387
+ border: none;
388
+ border-radius: var(--radius-xs);
389
+ background: transparent;
390
+ color: inherit;
391
+ cursor: pointer;
392
+ opacity: 0.7;
393
+ }
394
+ .aichat__notice-x svg {
395
+ width: var(--icon-xs);
396
+ height: var(--icon-xs);
397
+ }
398
+ .aichat__notice-x:hover {
399
+ opacity: 1;
400
+ background: var(--bg-hover);
401
+ }
402
+
403
+ /* ---- 消息 ---- */
404
+ .aimsg {
405
+ display: flex;
406
+ flex-direction: column;
407
+ gap: var(--space-2);
408
+ min-width: 0;
409
+ }
410
+ .aimsg--user {
411
+ align-self: flex-end;
412
+ max-width: 88%;
413
+ align-items: flex-end;
414
+ }
415
+ .aimsg--assistant {
416
+ align-self: stretch;
417
+ }
418
+ .aimsg__bubble {
419
+ padding: var(--space-2) var(--space-3);
420
+ border: 1px solid var(--border);
421
+ border-radius: var(--radius-lg);
422
+ background: var(--bg-hover);
423
+ color: var(--text);
424
+ font-size: var(--fs-base);
425
+ line-height: var(--lh-base);
426
+ white-space: pre-wrap;
427
+ word-break: break-word;
428
+ }
429
+ .aimsg__files {
430
+ display: flex;
431
+ flex-wrap: wrap;
432
+ gap: var(--space-2);
433
+ justify-content: flex-end;
434
+ }
435
+ .aifile {
436
+ display: flex;
437
+ align-items: center;
438
+ gap: var(--space-2);
439
+ max-width: 240px;
440
+ padding: var(--space-1) var(--space-2) var(--space-1) var(--space-1);
441
+ border: 1px solid var(--border);
442
+ border-radius: var(--radius-md);
443
+ background: var(--bg-input);
444
+ }
445
+ .aifile__thumb {
446
+ width: var(--ctrl-md);
447
+ height: var(--ctrl-md);
448
+ object-fit: cover;
449
+ border-radius: var(--radius-xs);
450
+ background: var(--bg-soft);
451
+ flex-shrink: 0;
452
+ }
453
+ .aifile__icon {
454
+ width: var(--ctrl-md);
455
+ height: var(--ctrl-md);
456
+ padding: var(--space-2);
457
+ box-sizing: border-box;
458
+ color: var(--text-dim);
459
+ flex-shrink: 0;
460
+ }
461
+ .aifile__meta {
462
+ display: flex;
463
+ flex-direction: column;
464
+ min-width: 0;
465
+ }
466
+ .aifile__name {
467
+ font-size: var(--fs-sm);
468
+ line-height: var(--lh-sm);
469
+ color: var(--text);
470
+ overflow: hidden;
471
+ text-overflow: ellipsis;
472
+ white-space: nowrap;
473
+ }
474
+ .aifile__sub {
475
+ font-size: var(--fs-xs);
476
+ line-height: var(--lh-xs);
477
+ color: var(--text-faint);
478
+ }
479
+ .aifile--missing {
480
+ border-color: var(--danger);
481
+ }
482
+ .aifile--missing .aifile__name {
483
+ color: var(--danger);
484
+ }
485
+
486
+ /* 思考过程(Codex 式折叠) */
487
+ .aithink,
488
+ .aitool {
489
+ border: 1px solid var(--border);
490
+ border-radius: var(--radius-md);
491
+ background: var(--bg-soft);
492
+ overflow: hidden;
493
+ }
494
+ .aithink__head,
495
+ .aitool__head {
496
+ width: 100%;
497
+ display: flex;
498
+ align-items: center;
499
+ gap: var(--space-2);
500
+ min-height: var(--ctrl-sm);
501
+ padding: var(--space-1) var(--space-3);
502
+ border: none;
503
+ background: transparent;
504
+ color: var(--text-dim);
505
+ font-size: var(--fs-sm);
506
+ line-height: var(--lh-sm);
507
+ text-align: left;
508
+ cursor: pointer;
509
+ transition: background 0.15s ease;
510
+ }
511
+ .aithink__head:hover,
512
+ .aitool__head:hover {
513
+ background: var(--bg-hover);
514
+ }
515
+ .aithink__chev,
516
+ .aitool__chev {
517
+ width: var(--icon-sm);
518
+ height: var(--icon-sm);
519
+ flex-shrink: 0;
520
+ color: var(--text-faint);
521
+ transition: transform 0.15s ease;
522
+ }
523
+ .aithink.is-open .aithink__chev,
524
+ .aitool.is-open .aitool__chev {
525
+ transform: rotate(90deg);
526
+ }
527
+ .aithink__icon,
528
+ .aitool__status {
529
+ width: var(--icon-sm);
530
+ height: var(--icon-sm);
531
+ flex-shrink: 0;
532
+ color: var(--text-faint);
533
+ }
534
+ .aithink__spin,
535
+ .aitool__spin {
536
+ width: var(--icon-sm);
537
+ height: var(--icon-sm);
538
+ flex-shrink: 0;
539
+ color: var(--accent);
540
+ animation: aichat-spin 0.9s linear infinite;
541
+ }
542
+ .aithink__label {
543
+ font-weight: 500;
544
+ color: var(--text-dim);
545
+ white-space: nowrap;
546
+ }
547
+ .aithink.is-live .aithink__label {
548
+ color: var(--accent);
549
+ }
550
+ /* 折叠态的「最新一行」预览:右侧渐隐,形成流动感 */
551
+ .aithink__peek {
552
+ flex: 1;
553
+ min-width: 0;
554
+ overflow: hidden;
555
+ white-space: nowrap;
556
+ text-overflow: ellipsis;
557
+ font-size: var(--fs-xs);
558
+ line-height: var(--lh-xs);
559
+ color: var(--text-faint);
560
+ mask-image: linear-gradient(90deg, #000 60%, transparent);
561
+ -webkit-mask-image: linear-gradient(90deg, #000 60%, transparent);
562
+ }
563
+ .aithink__body {
564
+ max-height: 240px;
565
+ overflow-y: auto;
566
+ border-top: var(--hairline) solid var(--border);
567
+ }
568
+ /* 截断态:超出 240px 后底部渐变遮罩 */
569
+ .aithink__body.is-truncated {
570
+ position: relative;
571
+ max-height: 260px;
572
+ overflow: hidden;
573
+ }
574
+ .aithink__body.is-truncated::after {
575
+ content: '';
576
+ position: absolute;
577
+ bottom: 0; left: 0; right: 0;
578
+ height: 48px;
579
+ background: linear-gradient(transparent, var(--bg-soft));
580
+ pointer-events: none;
581
+ }
582
+ /* 展开全文按钮 */
583
+ .aithink__more {
584
+ display: block;
585
+ width: 100%;
586
+ padding: var(--space-1) var(--space-3);
587
+ border: none;
588
+ border-top: var(--hairline) solid var(--border);
589
+ background: var(--bg-soft);
590
+ color: var(--accent);
591
+ font-size: var(--fs-xs);
592
+ cursor: pointer;
593
+ text-align: center;
594
+ }
595
+ .aithink__more:hover {
596
+ background: var(--bg-hover);
597
+ }
598
+ .aithink__body pre {
599
+ margin: 0;
600
+ padding: var(--space-3);
601
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
602
+ font-size: var(--fs-sm);
603
+ line-height: var(--lh-sm);
604
+ color: var(--text-dim);
605
+ white-space: pre-wrap;
606
+ word-break: break-word;
607
+ }
608
+
609
+ /* 工具调用行 */
610
+ .aitool--done .aitool__status {
611
+ color: var(--ok);
612
+ }
613
+ /* 参数仍在流式拼装的占位态:用强调色描边,和「已执行完」区分开 */
614
+ .aitool--pending {
615
+ border-color: var(--accent-border);
616
+ }
617
+ .aitool--pending .aitool__name {
618
+ color: var(--accent);
619
+ }
620
+ .aitool--pending .aitool__args {
621
+ color: var(--text-faint);
622
+ }
623
+ .aitool--error {
624
+ border-color: var(--danger);
625
+ }
626
+ .aitool--error .aitool__status {
627
+ color: var(--danger);
628
+ }
629
+ .aitool__name {
630
+ font-weight: 600;
631
+ color: var(--text);
632
+ white-space: nowrap;
633
+ }
634
+ .aitool__args {
635
+ flex: 1;
636
+ min-width: 0;
637
+ overflow: hidden;
638
+ text-overflow: ellipsis;
639
+ white-space: nowrap;
640
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
641
+ font-size: var(--fs-xs);
642
+ color: var(--text-faint);
643
+ }
644
+ .aitool__ms {
645
+ flex-shrink: 0;
646
+ font-size: var(--fs-xs);
647
+ color: var(--text-faint);
648
+ }
649
+ /* 高危工具审批条:命令原文 + 允许 / 拒绝 */
650
+ .aitool__approve {
651
+ display: flex;
652
+ align-items: center;
653
+ flex-wrap: wrap;
654
+ gap: var(--space-2);
655
+ padding: var(--space-2) var(--space-3);
656
+ border-top: var(--hairline) solid var(--accent-border);
657
+ background: var(--bg-hover);
658
+ }
659
+ .aitool__approve-icon {
660
+ width: var(--icon-sm);
661
+ height: var(--icon-sm);
662
+ flex-shrink: 0;
663
+ color: var(--warn, var(--accent));
664
+ }
665
+ .aitool__approve-cmd {
666
+ flex: 1;
667
+ min-width: 0;
668
+ overflow: hidden;
669
+ text-overflow: ellipsis;
670
+ white-space: nowrap;
671
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
672
+ font-size: var(--fs-xs);
673
+ color: var(--text);
674
+ }
675
+ .aitool__approve-hint {
676
+ font-size: var(--fs-xs);
677
+ color: var(--text-faint);
678
+ }
679
+ .aitool__approve-btn {
680
+ flex-shrink: 0;
681
+ min-height: var(--ctrl-xs, 24px);
682
+ padding: 0 var(--space-3);
683
+ border: 1px solid var(--accent-border);
684
+ border-radius: var(--radius-sm);
685
+ background: var(--accent);
686
+ color: var(--on-accent);
687
+ font-size: var(--fs-xs);
688
+ cursor: pointer;
689
+ }
690
+ .aitool__approve-btn--ghost {
691
+ background: transparent;
692
+ color: var(--accent);
693
+ }
694
+ .aitool__approve-btn--ghost:hover {
695
+ background: var(--bg-hover);
696
+ }
697
+ .aitool__approve-btn--deny {
698
+ background: transparent;
699
+ color: var(--text-dim);
700
+ }
701
+ .aitool__approve-btn--deny:hover {
702
+ background: var(--bg-hover);
703
+ }
704
+ .aitool__approve-note {
705
+ padding: var(--space-1) var(--space-3);
706
+ border-top: var(--hairline) solid var(--border);
707
+ font-size: var(--fs-xs);
708
+ color: var(--text-faint);
709
+ }
710
+ /* 免确认执行的说明行:说明 + 命令原文一行摆开,命令过长省略号截断(复用 .aitool__approve-cmd 的字体 Token) */
711
+ .aitool__approve-note.is-auto {
712
+ display: flex;
713
+ align-items: center;
714
+ gap: var(--space-2);
715
+ min-width: 0;
716
+ }
717
+ .aitool__body {
718
+ max-height: 320px;
719
+ overflow-y: auto;
720
+ padding: var(--space-3);
721
+ border-top: var(--hairline) solid var(--border);
722
+ background: var(--bg-panel);
723
+ }
724
+ .aitool__shot {
725
+ display: block;
726
+ max-width: 100%;
727
+ margin-top: var(--space-2);
728
+ border: 1px solid var(--border);
729
+ border-radius: var(--radius-sm);
730
+ background: #fff;
731
+ }
732
+ .aitool__error {
733
+ padding: var(--space-2);
734
+ border-radius: var(--radius-sm);
735
+ background: var(--danger-soft);
736
+ color: var(--danger);
737
+ font-size: var(--fs-sm);
738
+ line-height: var(--lh-sm);
739
+ white-space: pre-wrap;
740
+ word-break: break-word;
741
+ }
742
+
743
+ /* 等待首 token */
744
+ .aiwait {
745
+ display: flex;
746
+ align-items: center;
747
+ gap: var(--space-1);
748
+ color: var(--text-faint);
749
+ font-size: var(--fs-sm);
750
+ line-height: var(--lh-sm);
751
+ }
752
+ .aiwait__dot {
753
+ width: var(--space-1);
754
+ height: var(--space-1);
755
+ border-radius: var(--radius-pill);
756
+ background: var(--text-faint);
757
+ animation: aichat-bounce 1.2s infinite ease-in-out;
758
+ }
759
+ .aiwait__dot:nth-child(2) {
760
+ animation-delay: 0.15s;
761
+ }
762
+ .aiwait__dot:nth-child(3) {
763
+ animation-delay: 0.3s;
764
+ }
765
+ .aiwait__text {
766
+ margin-left: var(--space-1);
767
+ }
768
+ @keyframes aichat-bounce {
769
+ 0%,
770
+ 80%,
771
+ 100% {
772
+ opacity: 0.3;
773
+ transform: scale(0.8);
774
+ }
775
+ 40% {
776
+ opacity: 1;
777
+ transform: scale(1);
778
+ }
779
+ }
780
+ @keyframes aichat-spin {
781
+ to {
782
+ transform: rotate(360deg);
783
+ }
784
+ }
785
+
786
+ /* 错误 / 终止标记 */
787
+ .aimsg__error {
788
+ display: flex;
789
+ align-items: flex-start;
790
+ gap: var(--space-2);
791
+ padding: var(--space-2) var(--space-3);
792
+ border: 1px solid var(--danger);
793
+ border-radius: var(--radius-md);
794
+ background: var(--danger-soft);
795
+ color: var(--danger);
796
+ font-size: var(--fs-sm);
797
+ line-height: var(--lh-sm);
798
+ white-space: pre-wrap;
799
+ word-break: break-word;
800
+ }
801
+ .aimsg__error svg {
802
+ width: var(--icon-sm);
803
+ height: var(--icon-sm);
804
+ flex-shrink: 0;
805
+ margin-top: 2px;
806
+ }
807
+ .aimsg__aborted {
808
+ font-size: var(--fs-xs);
809
+ line-height: var(--lh-xs);
810
+ color: var(--text-faint);
811
+ }
812
+
813
+ /* 熔断收尾原因(死循环 / 墙钟 / 自设步数硬顶):比「已终止生成」更该被看见 —— 警告色底 + 一键续跑 */
814
+ .aimsg__budget {
815
+ display: flex;
816
+ align-items: center;
817
+ gap: var(--space-2);
818
+ padding: var(--space-2) var(--space-3);
819
+ border: 1px solid var(--warn-border);
820
+ border-radius: var(--radius-sm);
821
+ background: var(--warn-soft);
822
+ }
823
+ .aimsg__budget-icon {
824
+ width: var(--icon-sm);
825
+ height: var(--icon-sm);
826
+ flex-shrink: 0;
827
+ color: var(--warn);
828
+ }
829
+ .aimsg__budget-text {
830
+ flex: 1;
831
+ min-width: 0;
832
+ font-size: var(--fs-xs);
833
+ line-height: var(--lh-xs);
834
+ color: var(--text-dim);
835
+ }
836
+ .aimsg__budget-text strong {
837
+ color: var(--warn);
838
+ font-weight: 500;
839
+ }
840
+ /* 与审批条的主按钮同一套 Token,视觉语言不另起 */
841
+ .aimsg__budget-go {
842
+ flex-shrink: 0;
843
+ min-height: var(--ctrl-xs, 24px);
844
+ padding: 0 var(--space-3);
845
+ border: 1px solid var(--accent-border);
846
+ border-radius: var(--radius-sm);
847
+ background: var(--accent);
848
+ color: var(--on-accent);
849
+ font-size: var(--fs-xs);
850
+ cursor: pointer;
851
+ }
852
+ .aimsg__budget-go:hover {
853
+ background: var(--accent-hover, var(--accent));
854
+ }
855
+
856
+ /* 悬停操作(复制 / 重新生成) */
857
+ .aimsg__acts {
858
+ display: flex;
859
+ gap: var(--space-1);
860
+ opacity: 0;
861
+ transition: opacity 0.15s ease;
862
+ }
863
+ .aimsg:hover .aimsg__acts,
864
+ .aimsg__acts:focus-within {
865
+ opacity: 1;
866
+ }
867
+ .aimsg__act {
868
+ height: var(--ctrl-xs);
869
+ display: inline-flex;
870
+ align-items: center;
871
+ gap: var(--space-1);
872
+ padding: 0 var(--space-2);
873
+ border: 1px solid transparent;
874
+ border-radius: var(--radius-sm);
875
+ background: transparent;
876
+ color: var(--text-faint);
877
+ font-size: var(--fs-xs);
878
+ cursor: pointer;
879
+ transition: background 0.15s ease, color 0.15s ease;
880
+ }
881
+ .aimsg__act svg {
882
+ width: var(--icon-xs);
883
+ height: var(--icon-xs);
884
+ }
885
+ .aimsg__act:hover {
886
+ background: var(--bg-hover);
887
+ color: var(--text);
888
+ }
889
+
890
+ /* ---- Markdown 正文 ---- */
891
+ .md-body {
892
+ font-size: var(--fs-base);
893
+ line-height: var(--lh-base);
894
+ color: var(--text);
895
+ margin-bottom: var(--space-3);
896
+ word-break: break-word;
897
+ }
898
+ .md-body > :first-child {
899
+ margin-top: 0;
900
+ }
901
+ .md-body > :last-child {
902
+ margin-bottom: 0;
903
+ }
904
+ /* 流式光标:贴在正文最后一个块的末尾,让「正在输出」始终可见(对齐 Codex 体感) */
905
+ .aimsg__md.is-streaming > :last-child::after {
906
+ content: '';
907
+ display: inline-block;
908
+ width: 0.5em;
909
+ height: 1.05em;
910
+ margin-left: 2px;
911
+ vertical-align: -0.18em;
912
+ border-radius: 1px;
913
+ background: var(--accent);
914
+ animation: aichat-caret 1s steps(1, end) infinite;
915
+ }
916
+ @keyframes aichat-caret {
917
+ 0%,
918
+ 50% {
919
+ opacity: 1;
920
+ }
921
+ 50.01%,
922
+ 100% {
923
+ opacity: 0;
924
+ }
925
+ }
926
+ /* 流式高光:一条亮带沿横向扫过字形(background-clip: text),对齐 Codex 的 streaming shimmer。
927
+ 只作用于 aithink 折叠态的「最新一行」预览(.aithink__peek,仅思考进行中渲染):
928
+ 以其静态色 --text-faint 为底,扫过处提亮到 --stream-shine;思考结束节点收起即消失。
929
+ background-size 200% + 位移一个整 tile(-200% ≡ 0)保证循环无缝。 */
930
+ @supports ((-webkit-background-clip: text) or (background-clip: text)) {
931
+ .aithink__peek {
932
+ background-image: linear-gradient(
933
+ 100deg,
934
+ var(--text-faint) 0%,
935
+ var(--text-faint) 38%,
936
+ var(--stream-shine) 50%,
937
+ var(--text-faint) 62%,
938
+ var(--text-faint) 100%
939
+ );
940
+ background-size: 200% 100%;
941
+ -webkit-background-clip: text;
942
+ background-clip: text;
943
+ color: transparent;
944
+ animation: aichat-shine 2.2s linear infinite;
945
+ }
946
+ }
947
+ @keyframes aichat-shine {
948
+ from {
949
+ background-position: 0% 0;
950
+ }
951
+ to {
952
+ background-position: -200% 0;
953
+ }
954
+ }
955
+ /* 动效敏感用户:关掉横扫,回到各自静态色 */
956
+ @media (prefers-reduced-motion: reduce) {
957
+ .aithink__peek {
958
+ animation: none;
959
+ background-image: none;
960
+ color: var(--text-faint);
961
+ }
962
+ }
963
+ .md-body p {
964
+ margin: 0 0 var(--space-2);
965
+ }
966
+ .md-body h1,
967
+ .md-body h2,
968
+ .md-body h3,
969
+ .md-body h4 {
970
+ margin: var(--space-3) 0 var(--space-2);
971
+ font-weight: 600;
972
+ line-height: 1.4;
973
+ }
974
+ .md-body h1 {
975
+ font-size: var(--fs-xl);
976
+ }
977
+ .md-body h2 {
978
+ font-size: var(--fs-lg);
979
+ }
980
+ .md-body h3,
981
+ .md-body h4 {
982
+ font-size: var(--fs-md);
983
+ }
984
+ .md-body ul,
985
+ .md-body ol {
986
+ margin: 0 0 var(--space-2);
987
+ padding-left: var(--space-5);
988
+ }
989
+ .md-body li {
990
+ margin: var(--space-1) 0;
991
+ }
992
+ .md-body li > ul,
993
+ .md-body li > ol {
994
+ margin-bottom: 0;
995
+ }
996
+ .md-body a {
997
+ color: var(--accent);
998
+ text-decoration: none;
999
+ }
1000
+ .md-body a:hover {
1001
+ text-decoration: underline;
1002
+ }
1003
+ .md-body strong {
1004
+ font-weight: 600;
1005
+ color: var(--text);
1006
+ }
1007
+ .md-body blockquote {
1008
+ margin: 0 0 var(--space-2);
1009
+ padding: var(--space-1) 0 var(--space-1) var(--space-3);
1010
+ border-left: 2px solid var(--border-strong);
1011
+ color: var(--text-dim);
1012
+ }
1013
+ .md-body hr {
1014
+ margin: var(--space-3) 0;
1015
+ border: none;
1016
+ border-top: var(--hairline) solid var(--border);
1017
+ }
1018
+ .md-body img {
1019
+ max-width: 100%;
1020
+ border-radius: var(--radius-sm);
1021
+ }
1022
+ .md-body table {
1023
+ width: 100%;
1024
+ margin: 0 0 var(--space-2);
1025
+ border-collapse: collapse;
1026
+ font-size: var(--fs-sm);
1027
+ }
1028
+ .md-body th,
1029
+ .md-body td {
1030
+ padding: var(--space-1) var(--space-2);
1031
+ border: 1px solid var(--border);
1032
+ text-align: left;
1033
+ }
1034
+ .md-body th {
1035
+ background: var(--bg-soft);
1036
+ font-weight: 600;
1037
+ }
1038
+ .md-body code {
1039
+ padding: 0 var(--space-1);
1040
+ border: 1px solid var(--border);
1041
+ border-radius: var(--radius-xs);
1042
+ background: var(--bg-hover);
1043
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1044
+ font-size: var(--fs-sm);
1045
+ }
1046
+ .md-body pre {
1047
+ margin: 0;
1048
+ padding: var(--space-3);
1049
+ overflow-x: auto;
1050
+ background: var(--bg-soft);
1051
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1052
+ font-size: var(--fs-sm);
1053
+ line-height: var(--lh-sm);
1054
+ }
1055
+ .md-body pre code {
1056
+ padding: 0;
1057
+ border: none;
1058
+ background: transparent;
1059
+ font-size: inherit;
1060
+ }
1061
+ /* 代码块外框(由 enhanceCodeBlocks 注入):语言标签 + 复制 */
1062
+ .md-code {
1063
+ margin: 0 0 var(--space-2);
1064
+ border: 1px solid var(--border);
1065
+ border-radius: var(--radius-md);
1066
+ overflow: hidden;
1067
+ }
1068
+ .md-code__bar {
1069
+ display: flex;
1070
+ align-items: center;
1071
+ justify-content: space-between;
1072
+ gap: var(--space-2);
1073
+ padding: var(--space-1) var(--space-2);
1074
+ border-bottom: var(--hairline) solid var(--border);
1075
+ background: var(--bg-bar);
1076
+ }
1077
+ .md-code__lang {
1078
+ font-size: var(--fs-xs);
1079
+ line-height: var(--lh-xs);
1080
+ color: var(--text-faint);
1081
+ text-transform: uppercase;
1082
+ letter-spacing: 0.04em;
1083
+ }
1084
+ .md-code__copy {
1085
+ height: var(--ctrl-xs);
1086
+ padding: 0 var(--space-2);
1087
+ border: 1px solid var(--border);
1088
+ border-radius: var(--radius-xs);
1089
+ background: var(--bg-input);
1090
+ color: var(--text-dim);
1091
+ font-size: var(--fs-xs);
1092
+ cursor: pointer;
1093
+ }
1094
+ .md-code__copy:hover {
1095
+ border-color: var(--accent-border);
1096
+ color: var(--text);
1097
+ }
1098
+ .md-code__copy.is-ok {
1099
+ border-color: var(--ok-border);
1100
+ color: var(--ok);
1101
+ }
1102
+ /* Prism token 配色:复用编辑器既有 --code-* 语义色,与 JSON 树 / 代码编辑器同源 */
1103
+ .md-body .token.comment,
1104
+ .md-body .token.prolog,
1105
+ .md-body .token.doctype,
1106
+ .md-body .token.cdata {
1107
+ color: var(--code-comment);
1108
+ }
1109
+ .md-body .token.punctuation {
1110
+ color: var(--code-punc);
1111
+ }
1112
+ .md-body .token.property,
1113
+ .md-body .token.tag,
1114
+ .md-body .token.symbol,
1115
+ .md-body .token.deleted {
1116
+ color: var(--code-prop);
1117
+ }
1118
+ .md-body .token.string,
1119
+ .md-body .token.char,
1120
+ .md-body .token.attr-value,
1121
+ .md-body .token.inserted {
1122
+ color: var(--code-str);
1123
+ }
1124
+ .md-body .token.number,
1125
+ .md-body .token.boolean,
1126
+ .md-body .token.constant {
1127
+ color: var(--code-num);
1128
+ }
1129
+ .md-body .token.keyword,
1130
+ .md-body .token.rule,
1131
+ .md-body .token.important {
1132
+ color: var(--code-kw);
1133
+ }
1134
+ .md-body .token.function,
1135
+ .md-body .token.class-name {
1136
+ color: var(--code-fn);
1137
+ }
1138
+ .md-body .token.variable,
1139
+ .md-body .token.operator,
1140
+ .md-body .token.entity,
1141
+ .md-body .token.url {
1142
+ color: var(--code-var);
1143
+ }
1144
+
1145
+ /* ---- 输入区 ---- */
1146
+ .aicomp {
1147
+ position: relative;
1148
+ flex-shrink: 0;
1149
+ display: flex;
1150
+ flex-direction: column;
1151
+ gap: var(--space-2);
1152
+ padding: var(--space-3) var(--space-4) var(--space-4);
1153
+ border-top: var(--hairline) solid var(--border);
1154
+ background: var(--bg-bar);
1155
+ }
1156
+
1157
+ /* 上缘拖拽把手:与 .aichat__grip 同一语言(6px 透明条,hover / 拖拽上 --splitter 色) */
1158
+ .aicomp__grip {
1159
+ position: absolute;
1160
+ top: -3px;
1161
+ left: 0;
1162
+ right: 0;
1163
+ height: 6px;
1164
+ cursor: row-resize;
1165
+ background: transparent;
1166
+ z-index: var(--z-base);
1167
+ transition: background 0.15s ease;
1168
+ }
1169
+ .aicomp__grip:hover,
1170
+ .aicomp__grip.is-dragging {
1171
+ background: var(--splitter);
1172
+ }
1173
+
1174
+ /* 手动高度态:容器定高、输入框撑满剩余空间(内容超出自身滚动),附件行照旧占自己的高度 */
1175
+ .aicomp.is-manual .aicomp__box {
1176
+ flex: 1;
1177
+ min-height: 0;
1178
+ }
1179
+ .aicomp.is-manual .aicomp__ta {
1180
+ flex: 1;
1181
+ min-height: 0;
1182
+ max-height: none;
1183
+ }
1184
+ /* 拖拽中别把指针抢进文本选择 / 光标 */
1185
+ .aicomp.is-resizing .aicomp__ta {
1186
+ pointer-events: none;
1187
+ user-select: none;
1188
+ }
1189
+ .aicomp__files {
1190
+ display: flex;
1191
+ flex-wrap: wrap;
1192
+ gap: var(--space-2);
1193
+ }
1194
+ .aichip {
1195
+ display: inline-flex;
1196
+ align-items: center;
1197
+ gap: var(--space-2);
1198
+ max-width: 100%;
1199
+ height: var(--ctrl-sm);
1200
+ padding: 0 var(--space-1) 0 var(--space-2);
1201
+ border: 1px solid var(--border);
1202
+ border-radius: var(--radius-md);
1203
+ background: var(--bg-input);
1204
+ font-size: var(--fs-sm);
1205
+ }
1206
+ .aichip__thumb {
1207
+ width: var(--ctrl-xs);
1208
+ height: var(--ctrl-xs);
1209
+ object-fit: cover;
1210
+ border-radius: var(--radius-xs);
1211
+ background: var(--bg-soft);
1212
+ flex-shrink: 0;
1213
+ }
1214
+ .aichip__icon {
1215
+ width: var(--icon-sm);
1216
+ height: var(--icon-sm);
1217
+ color: var(--text-dim);
1218
+ flex-shrink: 0;
1219
+ }
1220
+ .aichip__name {
1221
+ max-width: 160px;
1222
+ overflow: hidden;
1223
+ text-overflow: ellipsis;
1224
+ white-space: nowrap;
1225
+ color: var(--text);
1226
+ }
1227
+ .aichip__sub {
1228
+ font-size: var(--fs-xs);
1229
+ color: var(--text-faint);
1230
+ }
1231
+ .aichip__rm {
1232
+ width: var(--ctrl-xs);
1233
+ height: var(--ctrl-xs);
1234
+ aspect-ratio: 1;
1235
+ padding: 0;
1236
+ display: inline-flex;
1237
+ align-items: center;
1238
+ justify-content: center;
1239
+ border: none;
1240
+ border-radius: var(--radius-xs);
1241
+ background: transparent;
1242
+ color: var(--text-faint);
1243
+ cursor: pointer;
1244
+ }
1245
+ .aichip__rm svg {
1246
+ width: var(--icon-xs);
1247
+ height: var(--icon-xs);
1248
+ }
1249
+ .aichip__rm:hover {
1250
+ background: var(--danger-soft);
1251
+ color: var(--danger);
1252
+ }
1253
+ .aichip--pending {
1254
+ opacity: 0.7;
1255
+ }
1256
+ .aichip--pending .aichip__icon {
1257
+ color: var(--accent);
1258
+ animation: aichat-spin 0.9s linear infinite;
1259
+ }
1260
+ .aichip--missing {
1261
+ border-color: var(--danger);
1262
+ }
1263
+ .aichip--missing .aichip__name {
1264
+ color: var(--danger);
1265
+ }
1266
+
1267
+ /* 粘贴路径行 */
1268
+ .aicomp__path {
1269
+ display: flex;
1270
+ gap: var(--space-2);
1271
+ }
1272
+ .aicomp__path-input {
1273
+ flex: 1;
1274
+ min-width: 0;
1275
+ height: var(--ctrl-sm);
1276
+ padding: 0 var(--space-3);
1277
+ border: 1px solid var(--border-strong);
1278
+ border-radius: var(--radius-sm);
1279
+ background: var(--bg-input);
1280
+ color: var(--text);
1281
+ font-size: var(--fs-sm);
1282
+ font-family: inherit;
1283
+ outline: none;
1284
+ }
1285
+ .aicomp__path-input:focus {
1286
+ border-color: var(--accent-border);
1287
+ }
1288
+ .aicomp__path-ok,
1289
+ .aicomp__path-no {
1290
+ height: var(--ctrl-sm);
1291
+ padding: 0 var(--space-3);
1292
+ border-radius: var(--radius-sm);
1293
+ font-size: var(--fs-sm);
1294
+ cursor: pointer;
1295
+ border: 1px solid var(--border-strong);
1296
+ background: transparent;
1297
+ color: var(--text-dim);
1298
+ }
1299
+ .aicomp__path-ok {
1300
+ border-color: var(--accent-border);
1301
+ background: var(--accent);
1302
+ color: var(--on-accent);
1303
+ }
1304
+ .aicomp__path-ok:disabled {
1305
+ opacity: 0.4;
1306
+ cursor: not-allowed;
1307
+ }
1308
+
1309
+ /* 输入框主体 */
1310
+ .aicomp__box {
1311
+ display: flex;
1312
+ flex-direction: column;
1313
+ border: 1px solid var(--border-strong);
1314
+ border-radius: var(--radius-lg);
1315
+ background: var(--bg-input);
1316
+ transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
1317
+ }
1318
+ .aicomp__box:focus-within {
1319
+ border-color: var(--accent-border);
1320
+ box-shadow: 0 0 0 2px var(--accent-soft);
1321
+ }
1322
+ .aicomp.is-drag .aicomp__box {
1323
+ border-color: var(--accent);
1324
+ background: var(--accent-soft);
1325
+ }
1326
+ .aicomp__ta {
1327
+ width: 100%;
1328
+ max-height: 168px;
1329
+ padding: var(--space-3) var(--space-3) var(--space-1);
1330
+ border: none;
1331
+ background: transparent;
1332
+ color: var(--text);
1333
+ font-family: inherit;
1334
+ font-size: var(--fs-base);
1335
+ line-height: var(--lh-base);
1336
+ resize: none;
1337
+ outline: none;
1338
+ overflow-y: auto;
1339
+ }
1340
+ .aicomp__ta::placeholder {
1341
+ color: var(--text-faint);
1342
+ }
1343
+ .aicomp__bar {
1344
+ display: flex;
1345
+ align-items: center;
1346
+ gap: var(--space-2);
1347
+ padding: var(--space-1) var(--space-2) var(--space-2);
1348
+ }
1349
+ .aicomp__tools {
1350
+ flex: 1;
1351
+ min-width: 0;
1352
+ display: flex;
1353
+ align-items: center;
1354
+ gap: var(--space-2);
1355
+ }
1356
+ .aicomp__attach {
1357
+ position: relative;
1358
+ flex-shrink: 0;
1359
+ }
1360
+ .aicomp__iconbtn {
1361
+ width: var(--ctrl-sm);
1362
+ height: var(--ctrl-sm);
1363
+ aspect-ratio: 1;
1364
+ padding: 0;
1365
+ display: inline-flex;
1366
+ align-items: center;
1367
+ justify-content: center;
1368
+ border: 1px solid transparent;
1369
+ border-radius: var(--radius-sm);
1370
+ background: transparent;
1371
+ color: var(--text-dim);
1372
+ cursor: pointer;
1373
+ }
1374
+ .aicomp__iconbtn svg {
1375
+ width: var(--icon-md);
1376
+ height: var(--icon-md);
1377
+ }
1378
+ .aicomp__iconbtn:hover {
1379
+ background: var(--bg-hover);
1380
+ color: var(--text);
1381
+ }
1382
+ .aicomp__iconbtn.is-open {
1383
+ background: var(--bg-active);
1384
+ color: var(--text);
1385
+ }
1386
+ .aicomp__hint {
1387
+ min-width: 0;
1388
+ overflow: hidden;
1389
+ text-overflow: ellipsis;
1390
+ white-space: nowrap;
1391
+ font-size: var(--fs-xs);
1392
+ line-height: var(--lh-xs);
1393
+ color: var(--text-faint);
1394
+ }
1395
+ .aicomp__menu {
1396
+ position: absolute;
1397
+ left: 0;
1398
+ bottom: calc(100% + var(--space-1));
1399
+ min-width: 176px;
1400
+ padding: var(--space-1);
1401
+ display: flex;
1402
+ flex-direction: column;
1403
+ border: 1px solid var(--border-strong);
1404
+ border-radius: var(--radius-md);
1405
+ background: var(--bg-pop);
1406
+ box-shadow: var(--shadow-3);
1407
+ z-index: var(--z-pop);
1408
+ }
1409
+ .aicomp__menu button {
1410
+ display: flex;
1411
+ align-items: center;
1412
+ gap: var(--space-2);
1413
+ height: var(--ctrl-sm);
1414
+ padding: 0 var(--space-2);
1415
+ border: none;
1416
+ border-radius: var(--radius-sm);
1417
+ background: transparent;
1418
+ color: var(--text);
1419
+ font-size: var(--fs-sm);
1420
+ text-align: left;
1421
+ cursor: pointer;
1422
+ }
1423
+ .aicomp__menu button svg {
1424
+ width: var(--icon-sm);
1425
+ height: var(--icon-sm);
1426
+ color: var(--text-dim);
1427
+ }
1428
+ .aicomp__menu button:hover {
1429
+ background: var(--bg-hover);
1430
+ }
1431
+ .aicomp__menu-sep {
1432
+ height: var(--hairline);
1433
+ margin: var(--space-1) 0;
1434
+ background: var(--border);
1435
+ }
1436
+ .aicomp__right {
1437
+ display: flex;
1438
+ align-items: center;
1439
+ gap: var(--space-2);
1440
+ flex-shrink: 0;
1441
+ }
1442
+ .aicomp__model {
1443
+ max-width: 120px;
1444
+ overflow: hidden;
1445
+ text-overflow: ellipsis;
1446
+ white-space: nowrap;
1447
+ height: var(--ctrl-xs);
1448
+ display: inline-flex;
1449
+ align-items: center;
1450
+ padding: 0 var(--space-2);
1451
+ border: 1px solid var(--border);
1452
+ border-radius: var(--radius-pill);
1453
+ background: var(--bg-soft);
1454
+ color: var(--text-faint);
1455
+ font-size: var(--fs-xs);
1456
+ }
1457
+ .aicomp__send {
1458
+ width: var(--ctrl-sm);
1459
+ height: var(--ctrl-sm);
1460
+ aspect-ratio: 1;
1461
+ padding: 0;
1462
+ display: inline-flex;
1463
+ align-items: center;
1464
+ justify-content: center;
1465
+ border: 1px solid var(--accent-border);
1466
+ border-radius: var(--radius-sm);
1467
+ background: var(--accent);
1468
+ color: var(--on-accent);
1469
+ cursor: pointer;
1470
+ transition: background 0.15s ease, opacity 0.15s ease;
1471
+ }
1472
+ .aicomp__send svg {
1473
+ width: var(--icon-md);
1474
+ height: var(--icon-md);
1475
+ }
1476
+ .aicomp__send:hover:not(:disabled) {
1477
+ background: var(--accent-hover);
1478
+ }
1479
+ .aicomp__send:disabled {
1480
+ opacity: 0.4;
1481
+ cursor: not-allowed;
1482
+ }
1483
+ /* 终止态:优先级要压过 .aicomp__send:hover:not(:disabled)(否则悬停时又变回主色底) */
1484
+ .aicomp__send--stop,
1485
+ .aicomp__send--stop:hover:not(:disabled) {
1486
+ border-color: var(--danger);
1487
+ background: var(--danger-soft);
1488
+ color: var(--danger);
1489
+ }
1490
+ .aicomp__send--stop:hover:not(:disabled) {
1491
+ background: var(--danger);
1492
+ color: var(--on-accent);
1493
+ }
1494
+
1495
+ /* ---- 模型设置浮层(面板内,不额外抬 z-index)---- */
1496
+ .aiset__mask {
1497
+ position: absolute;
1498
+ inset: 0;
1499
+ display: flex;
1500
+ align-items: center;
1501
+ justify-content: center;
1502
+ padding: var(--space-4);
1503
+ background: var(--scrim-modal);
1504
+ backdrop-filter: blur(var(--scrim-modal-blur));
1505
+ z-index: var(--z-modal);
1506
+ }
1507
+ .aiset {
1508
+ width: 100%;
1509
+ max-width: 420px;
1510
+ max-height: 100%;
1511
+ display: flex;
1512
+ flex-direction: column;
1513
+ overflow: hidden;
1514
+ border: 1px solid var(--border-strong);
1515
+ border-radius: var(--radius-xl);
1516
+ background: var(--bg-panel);
1517
+ box-shadow: var(--shadow-4);
1518
+ }
1519
+ .aiset__head {
1520
+ display: flex;
1521
+ align-items: center;
1522
+ gap: var(--space-2);
1523
+ padding: var(--space-3) var(--space-4);
1524
+ border-bottom: var(--hairline) solid var(--border);
1525
+ background: var(--bg-bar);
1526
+ }
1527
+ .aiset__head-icon {
1528
+ width: var(--icon-md);
1529
+ height: var(--icon-md);
1530
+ color: var(--accent);
1531
+ }
1532
+ .aiset__title {
1533
+ flex: 1;
1534
+ font-size: var(--fs-md);
1535
+ line-height: var(--lh-md);
1536
+ font-weight: 600;
1537
+ color: var(--text);
1538
+ }
1539
+ .aiset__x {
1540
+ width: var(--ctrl-sm);
1541
+ height: var(--ctrl-sm);
1542
+ aspect-ratio: 1;
1543
+ padding: 0;
1544
+ display: inline-flex;
1545
+ align-items: center;
1546
+ justify-content: center;
1547
+ border: none;
1548
+ border-radius: var(--radius-sm);
1549
+ background: transparent;
1550
+ color: var(--text-dim);
1551
+ cursor: pointer;
1552
+ }
1553
+ .aiset__x svg {
1554
+ width: var(--icon-md);
1555
+ height: var(--icon-md);
1556
+ }
1557
+ .aiset__x:hover {
1558
+ background: var(--bg-hover);
1559
+ color: var(--text);
1560
+ }
1561
+ .aiset__body {
1562
+ padding: var(--space-4);
1563
+ display: flex;
1564
+ flex-direction: column;
1565
+ gap: var(--space-4);
1566
+ overflow-y: auto;
1567
+ }
1568
+ .aiset__field {
1569
+ display: flex;
1570
+ flex-direction: column;
1571
+ gap: var(--space-1);
1572
+ }
1573
+ .aiset__label {
1574
+ font-size: var(--fs-sm);
1575
+ line-height: var(--lh-sm);
1576
+ font-weight: 500;
1577
+ color: var(--text-dim);
1578
+ }
1579
+ /* 字段标题行:标题(+ 可选的接口字段名)靠左,问号帮助按钮靠右。
1580
+ 长说明一律收进点击展开的浮层,面板里不再铺大段文字。 */
1581
+ .aiset__labelrow {
1582
+ display: flex;
1583
+ align-items: center;
1584
+ gap: var(--space-1);
1585
+ min-height: var(--ctrl-xs);
1586
+ }
1587
+ .aiset__unit {
1588
+ font-size: var(--fs-xs);
1589
+ line-height: var(--lh-xs);
1590
+ color: var(--text-faint);
1591
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1592
+ }
1593
+ .aiset__help {
1594
+ width: var(--ctrl-xs);
1595
+ height: var(--ctrl-xs);
1596
+ aspect-ratio: 1;
1597
+ padding: 0;
1598
+ display: inline-flex;
1599
+ align-items: center;
1600
+ justify-content: center;
1601
+ border: none;
1602
+ border-radius: var(--radius-pill);
1603
+ background: transparent;
1604
+ color: var(--text-faint);
1605
+ cursor: pointer;
1606
+ transition: background 0.15s ease, color 0.15s ease;
1607
+ }
1608
+ .aiset__help svg {
1609
+ width: var(--icon-sm);
1610
+ height: var(--icon-sm);
1611
+ }
1612
+ .aiset__help:hover {
1613
+ background: var(--bg-hover);
1614
+ color: var(--text);
1615
+ }
1616
+ .aiset__help.is-on {
1617
+ background: var(--accent-soft);
1618
+ color: var(--accent);
1619
+ }
1620
+ /* 行尾控件靠右:输入行的「?」与开关行的 Switch 各自顶到行尾 */
1621
+ .aiset__labelrow > .aiset__help:last-child,
1622
+ .aiset__switch {
1623
+ margin-left: auto;
1624
+ }
1625
+ .aiset__switch {
1626
+ flex: 0 0 auto;
1627
+ }
1628
+ /* 步进器 + 探测按钮一行:步进器吃满剩余宽度,按钮定宽靠右 */
1629
+ .aiset__stepperrow {
1630
+ display: flex;
1631
+ align-items: center;
1632
+ gap: var(--space-2);
1633
+ }
1634
+ .aiset__stepperrow .aiset__stepper {
1635
+ flex: 1 1 auto;
1636
+ min-width: 0;
1637
+ }
1638
+ .aiset__stepperrow .aiset__btn {
1639
+ flex: 0 0 auto;
1640
+ }
1641
+ .aiset__input {
1642
+ width: 100%;
1643
+ height: var(--ctrl-sm);
1644
+ padding: 0 var(--space-3);
1645
+ border: 1px solid var(--border-strong);
1646
+ border-radius: var(--radius-sm);
1647
+ background: var(--bg-input);
1648
+ color: var(--text);
1649
+ font-size: var(--fs-sm);
1650
+ font-family: inherit;
1651
+ outline: none;
1652
+ transition: border-color 0.15s ease;
1653
+ }
1654
+ .aiset__input:focus {
1655
+ border-color: var(--accent-border);
1656
+ }
1657
+ /* 密钥输入框:小眼睛内嵌在输入框尾部(掩码 / 明文切换),不再是外挂的独立按钮 */
1658
+ .aiset__keywrap {
1659
+ position: relative;
1660
+ display: flex;
1661
+ }
1662
+ .aiset__input--key {
1663
+ padding-right: calc(var(--ctrl-sm) + var(--space-2));
1664
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1665
+ }
1666
+ .aiset__eye {
1667
+ position: absolute;
1668
+ top: 50%;
1669
+ right: var(--space-1);
1670
+ transform: translateY(-50%);
1671
+ width: var(--ctrl-sm);
1672
+ height: var(--ctrl-sm);
1673
+ aspect-ratio: 1;
1674
+ padding: 0;
1675
+ display: inline-flex;
1676
+ align-items: center;
1677
+ justify-content: center;
1678
+ border: none;
1679
+ border-radius: var(--radius-sm);
1680
+ background: transparent;
1681
+ color: var(--text-faint);
1682
+ cursor: pointer;
1683
+ }
1684
+ .aiset__eye svg {
1685
+ width: var(--icon-sm);
1686
+ height: var(--icon-sm);
1687
+ }
1688
+ .aiset__eye:hover {
1689
+ background: var(--bg-hover);
1690
+ color: var(--text);
1691
+ }
1692
+ /* 字段说明浮层:就地展开在控件下方(面板可滚动,不用绝对定位以免被 overflow 裁切) */
1693
+ /* 手填值超网关已知上限的红线提示:warn 色小字,贴输入行下方 */
1694
+ .aiset__warnline {
1695
+ margin: var(--space-1) 0 0;
1696
+ font-size: var(--fs-xs);
1697
+ line-height: var(--lh-xs);
1698
+ color: var(--warn);
1699
+ }
1700
+ .aiset__pop {
1701
+ margin: 0;
1702
+ padding: var(--space-2) var(--space-3);
1703
+ border: 1px solid var(--border);
1704
+ border-left: 2px solid var(--accent-border);
1705
+ border-radius: var(--radius-sm);
1706
+ background: var(--bg-bar);
1707
+ font-size: var(--fs-xs);
1708
+ line-height: var(--lh-xs);
1709
+ color: var(--text-faint);
1710
+ }
1711
+ .aiset__pop strong {
1712
+ color: var(--text-dim);
1713
+ font-weight: 600;
1714
+ }
1715
+ .aiset__pop code {
1716
+ padding: 0 var(--space-1);
1717
+ border-radius: var(--radius-xs);
1718
+ background: var(--bg-hover);
1719
+ color: var(--text-dim);
1720
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1721
+ }
1722
+ .aiset__ping {
1723
+ display: flex;
1724
+ align-items: flex-start;
1725
+ gap: var(--space-2);
1726
+ padding: var(--space-2) var(--space-3);
1727
+ border-radius: var(--radius-sm);
1728
+ font-size: var(--fs-sm);
1729
+ line-height: var(--lh-sm);
1730
+ }
1731
+ .aiset__ping svg {
1732
+ width: var(--icon-sm);
1733
+ height: var(--icon-sm);
1734
+ flex-shrink: 0;
1735
+ margin-top: 2px;
1736
+ }
1737
+ .aiset__ping.is-ok {
1738
+ background: var(--ok-soft);
1739
+ color: var(--ok);
1740
+ }
1741
+ .aiset__ping.is-bad {
1742
+ background: var(--danger-soft);
1743
+ color: var(--danger);
1744
+ }
1745
+ .aiset__foot {
1746
+ display: flex;
1747
+ align-items: center;
1748
+ gap: var(--space-2);
1749
+ padding: var(--space-3) var(--space-4);
1750
+ border-top: var(--hairline) solid var(--border);
1751
+ background: var(--bg-bar);
1752
+ }
1753
+ .aiset__foot-gap {
1754
+ flex: 1;
1755
+ }
1756
+ .aiset__btn {
1757
+ display: inline-flex;
1758
+ align-items: center;
1759
+ justify-content: center;
1760
+ gap: var(--space-1);
1761
+ height: var(--ctrl-sm);
1762
+ padding: 0 var(--space-3);
1763
+ border: 1px solid var(--border-strong);
1764
+ border-radius: var(--radius-sm);
1765
+ background: transparent;
1766
+ color: var(--text-dim);
1767
+ font-size: var(--fs-sm);
1768
+ cursor: pointer;
1769
+ transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
1770
+ }
1771
+ .aiset__btn:hover:not(:disabled) {
1772
+ background: var(--bg-hover);
1773
+ color: var(--text);
1774
+ }
1775
+ .aiset__btn--primary {
1776
+ border-color: var(--accent-border);
1777
+ background: var(--accent);
1778
+ color: var(--on-accent);
1779
+ font-weight: 500;
1780
+ }
1781
+ .aiset__btn--primary:hover:not(:disabled) {
1782
+ background: var(--accent-hover);
1783
+ color: var(--on-accent);
1784
+ }
1785
+ .aiset__btn:disabled {
1786
+ opacity: 0.4;
1787
+ cursor: not-allowed;
1788
+ }
1789
+ /* 按钮内图标(测试连接的心跳线):与文本同一 currentColor,尺寸走图标阶梯 */
1790
+ .aiset__btn-icon {
1791
+ width: var(--icon-sm);
1792
+ height: var(--icon-sm);
1793
+ flex-shrink: 0;
1794
+ }
1795
+
1796
+ /* 上下文占比圆环(Codex 同款):轨道发丝色,弧长按占比走语义色三档 */
1797
+ .aicomp__ctx {
1798
+ display: inline-flex;
1799
+ align-items: center;
1800
+ gap: var(--space-1);
1801
+ flex-shrink: 0;
1802
+ }
1803
+ .aicomp__ctx-ring {
1804
+ width: 14px;
1805
+ height: 14px;
1806
+ flex-shrink: 0;
1807
+ }
1808
+ .aicomp__ctx-track,
1809
+ .aicomp__ctx-arc {
1810
+ fill: none;
1811
+ stroke-width: 2.5;
1812
+ }
1813
+ .aicomp__ctx-track {
1814
+ stroke: var(--border-strong);
1815
+ }
1816
+ .aicomp__ctx-arc {
1817
+ stroke: var(--accent);
1818
+ stroke-linecap: round;
1819
+ transition: stroke-dasharray 0.3s ease, stroke 0.3s ease;
1820
+ }
1821
+ .aicomp__ctx.is-warn .aicomp__ctx-arc {
1822
+ stroke: var(--warn);
1823
+ }
1824
+ .aicomp__ctx.is-danger .aicomp__ctx-arc {
1825
+ stroke: var(--danger);
1826
+ }
1827
+ .aicomp__ctx-pct {
1828
+ font-size: var(--fs-xs);
1829
+ color: var(--text-faint);
1830
+ font-variant-numeric: tabular-nums;
1831
+ }
1832
+ .aicomp__ctx.is-warn .aicomp__ctx-pct {
1833
+ color: var(--warn);
1834
+ }
1835
+ .aicomp__ctx.is-danger .aicomp__ctx-pct {
1836
+ color: var(--danger);
1837
+ }
1838
+
1839
+ /* 窄面板(拖到接近 320px)时收敛文案,保证控件不挤 */
1840
+ @container (max-width: 380px) {
1841
+ .aicomp__hint {
1842
+ display: none;
1843
+ }
1844
+ .aicomp__ctx-pct {
1845
+ display: none;
1846
+ }
1847
+ }
1848
+ .aichat {
1849
+ container-type: inline-size;
1850
+ }