@turingflow/agent-kit 0.0.0-stage → 0.1.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 (93) hide show
  1. package/README.md +546 -2
  2. package/dist/agent.d.ts +27 -0
  3. package/dist/agent.js +31 -0
  4. package/dist/cli.d.ts +5 -0
  5. package/dist/cli.js +105 -0
  6. package/dist/handlers.d.ts +92 -0
  7. package/dist/handlers.js +211 -0
  8. package/dist/index.d.ts +13 -0
  9. package/dist/index.js +10 -0
  10. package/dist/sdk.d.ts +106 -0
  11. package/dist/sdk.js +309 -0
  12. package/dist/server/agent.d.ts +27 -0
  13. package/dist/server/agent.js +31 -0
  14. package/dist/server/artifact-hooks.d.ts +6 -0
  15. package/dist/server/artifact-hooks.js +37 -0
  16. package/dist/server/artifact-tool-tracker.d.ts +24 -0
  17. package/dist/server/artifact-tool-tracker.js +347 -0
  18. package/dist/server/artifacts.d.ts +227 -0
  19. package/dist/server/artifacts.js +1061 -0
  20. package/dist/server/auth/config.d.ts +34 -0
  21. package/dist/server/auth/config.js +82 -0
  22. package/dist/server/auth/db.d.ts +159 -0
  23. package/dist/server/auth/db.js +680 -0
  24. package/dist/server/auth/handlers.d.ts +27 -0
  25. package/dist/server/auth/handlers.js +297 -0
  26. package/dist/server/auth/index.d.ts +15 -0
  27. package/dist/server/auth/index.js +14 -0
  28. package/dist/server/auth/login-options.d.ts +14 -0
  29. package/dist/server/auth/login-options.js +50 -0
  30. package/dist/server/auth/providers/dingtalk.d.ts +16 -0
  31. package/dist/server/auth/providers/dingtalk.js +177 -0
  32. package/dist/server/auth/providers/feishu.d.ts +15 -0
  33. package/dist/server/auth/providers/feishu.js +100 -0
  34. package/dist/server/auth/providers/index.d.ts +13 -0
  35. package/dist/server/auth/providers/index.js +24 -0
  36. package/dist/server/auth/session.d.ts +48 -0
  37. package/dist/server/auth/session.js +108 -0
  38. package/dist/server/auth/types.d.ts +118 -0
  39. package/dist/server/auth/types.js +7 -0
  40. package/dist/server/browser-agent-mcp.d.ts +67 -0
  41. package/dist/server/browser-agent-mcp.js +608 -0
  42. package/dist/server/cli.d.ts +6 -0
  43. package/dist/server/cli.js +112 -0
  44. package/dist/server/debug-log.d.ts +25 -0
  45. package/dist/server/debug-log.js +86 -0
  46. package/dist/server/handlers.d.ts +141 -0
  47. package/dist/server/handlers.js +523 -0
  48. package/dist/server/index.d.ts +30 -0
  49. package/dist/server/index.js +28 -0
  50. package/dist/server/kit-api.d.ts +23 -0
  51. package/dist/server/kit-api.js +153 -0
  52. package/dist/server/model-sources.d.ts +32 -0
  53. package/dist/server/model-sources.js +58 -0
  54. package/dist/server/oa.d.ts +87 -0
  55. package/dist/server/oa.js +397 -0
  56. package/dist/server/providers/openrouter.d.ts +51 -0
  57. package/dist/server/providers/openrouter.js +283 -0
  58. package/dist/server/render-markdown.d.ts +2 -0
  59. package/dist/server/render-markdown.js +50 -0
  60. package/dist/server/sdk.d.ts +259 -0
  61. package/dist/server/sdk.js +1991 -0
  62. package/dist/server/session-access.d.ts +5 -0
  63. package/dist/server/session-access.js +68 -0
  64. package/dist/server/session-fork.d.ts +10 -0
  65. package/dist/server/session-fork.js +142 -0
  66. package/dist/server/session-store.d.ts +56 -0
  67. package/dist/server/session-store.js +148 -0
  68. package/dist/server/sessions.d.ts +24 -0
  69. package/dist/server/sessions.js +541 -0
  70. package/dist/server/share.d.ts +15 -0
  71. package/dist/server/share.js +433 -0
  72. package/dist/server/tool-activity.d.ts +2 -0
  73. package/dist/server/tool-activity.js +26 -0
  74. package/dist/server/transcription/xiaomi-mimo.d.ts +8 -0
  75. package/dist/server/transcription/xiaomi-mimo.js +86 -0
  76. package/dist/server/upload.d.ts +13 -0
  77. package/dist/server/upload.js +81 -0
  78. package/dist/server/user-files.d.ts +28 -0
  79. package/dist/server/user-files.js +311 -0
  80. package/dist/server/voice.d.ts +25 -0
  81. package/dist/server/voice.js +61 -0
  82. package/dist/server/web-fetch.d.ts +71 -0
  83. package/dist/server/web-fetch.js +330 -0
  84. package/dist/styles.css +3354 -0
  85. package/dist/transcription/xiaomi-mimo.d.ts +8 -0
  86. package/dist/transcription/xiaomi-mimo.js +86 -0
  87. package/dist/ui/index.d.ts +352 -0
  88. package/dist/ui/index.js +7730 -0
  89. package/dist/upload.d.ts +13 -0
  90. package/dist/upload.js +81 -0
  91. package/dist/voice.d.ts +25 -0
  92. package/dist/voice.js +61 -0
  93. package/package.json +74 -4
@@ -0,0 +1,3354 @@
1
+ :root {
2
+ --bg: #f4f6fb;
3
+ --panel: #ffffff;
4
+ --ink: #1f2733;
5
+ --ink-soft: #5b6573;
6
+ /* Shimmer 扫光:亮色模式用「骨架式」——静止文字=浅灰(--shine-base),扫过高光(--shine)=深墨,
7
+ 高对比、一眼可见;深色调见下方 [data-theme=dark] 仍用白光。 */
8
+ --shine-base: #c2c9d4;
9
+ --shine: #1f2733;
10
+ --line: #e3e8f0;
11
+ --brand: #2f6bff;
12
+ --brand-dark: #1f4fd6;
13
+ --brand-soft: #eaf1ff;
14
+ --user-bubble: #2f6bff;
15
+ --bot-bubble: #f1f4fa;
16
+ --danger: #e5484d;
17
+ --ok: #1f9d55;
18
+ --radius: 14px;
19
+ --shadow: 0 10px 30px rgba(31, 39, 51, 0.08);
20
+ /* 中栏(聊天消息区)最小宽度:移动端视口小于该值时中栏不再压缩(保持此宽度,超出部分
21
+ 被壳层裁切 = 放弃适配,不显示提示页)。改这里即可整体调宽/调窄 */
22
+ --chat-min-w: 360px;
23
+ /* 输入框(单行态)卡片高度:textarea 最小高 60px(见 ChatWindow MIN_TEXTAREA_HEIGHT)
24
+ + composer 上下内边距 24px。思考直播小窗最大高度 = 该值 ×2,超出在框内滚动。 */
25
+ --chat-input-h: 84px;
26
+ --chat-thinking-max-h: calc(var(--chat-input-h) * 2);
27
+ /* 语义化扩展色(浅色默认值 = 原硬编码色,深色主题在下方 :root[data-theme="dark"] 整体换肤) */
28
+ --shell-bg: #ffffff;
29
+ --sidebar-bg: #f7f8fa;
30
+ --right-bg: #ffffff;
31
+ --hover: rgba(0,0,0,.05);
32
+ --hover-strong: #f0f1f4;
33
+ --active: #e8eaee;
34
+ --field-bg: #ffffff;
35
+ --field-soft: #fafbfc;
36
+ --overlay-bg: #ffffff;
37
+ --toast-bg: #ffffff;
38
+ --modal-mask: rgba(20, 28, 40, .45);
39
+ --fade-stop: rgba(255,255,255,.94);
40
+ --danger-soft: #fff0f0;
41
+ --ok-soft: #e9f8f0;
42
+ --warn-soft: #fef3e2;
43
+ --mark-bg: rgba(0, 0, 0, .1);
44
+ --user-bubble-soft: #f0f0f0;
45
+ --send-bg: #1f2733;
46
+ --send-bg-hover: #2f3b4a;
47
+ --send-ink: #ffffff;
48
+ --quote-bg: #f0f0f0;
49
+ --quote-line: #e0e0e0;
50
+ --brand-line: #cfe0ff;
51
+ --brand-line-strong: #b7d2ff;
52
+ --table-stripe: rgba(0,0,0,.02);
53
+ --scrollbar: rgba(31,39,51,.18);
54
+ --scrollbar-hover: rgba(31,39,51,.32);
55
+ --copied-bg: #e6f4ea;
56
+ --copied-ink: #1a7f37;
57
+ --drawer-shadow: -12px 0 40px rgba(31, 39, 51, .18);
58
+ --mobile-drawer-shadow: 0 8px 40px rgba(31, 39, 51, .22);
59
+ --spin-edge: rgba(0,0,0,.12);
60
+ --spin-top: rgba(0,0,0,.42);
61
+ /* 侧边消息滑块:亮色主题 —— 视口内可见=深墨(亮),滚出视口=浅灰 */
62
+ --slider-on: rgba(31, 39, 51, .52);
63
+ --slider-off: rgba(31, 39, 51, .16);
64
+ --slider-hover: #1f2733;
65
+ }
66
+
67
+ /* ============ 深色主题(参考 WorkBuddy 桌面端深色配色) ============
68
+ 通过 <html data-theme="dark"> 切换(用户菜单「外观」segment / 系统偏好)。
69
+ 核心语义变量换肤:炭黑底 + 白字 + 灰阶边框 + 品牌蓝点缀;组件硬编码色已收敛到上方变量。 */
70
+ :root[data-theme="dark"] {
71
+ /* 中性灰阶对齐 WorkBuddy 桌面端深色(去蓝绿偏色):页面 #1a1a1a / 主面 #1f1f1f / 卡片 #252525 / 描边 #333(白低透) */
72
+ --bg: #1a1a1a;
73
+ --panel: #252525;
74
+ --ink: #ededed;
75
+ --ink-soft: #9a9a9a;
76
+ /* Shimmer 扫光:暗色模式静止文字=浅灰(--shine-base),高光=白(深底上亮闪)。 */
77
+ --shine-base: #9a9a9a;
78
+ --shine: #ffffff;
79
+ --line: #333333;
80
+ --brand: #2f6bff;
81
+ --brand-dark: #7fa5ff;
82
+ --brand-soft: rgba(47, 107, 255, .16);
83
+ --user-bubble: #2f6bff;
84
+ --bot-bubble: #252525;
85
+ --danger: #f0616d;
86
+ --ok: #34b36b;
87
+ --radius: 14px;
88
+ --shadow: 0 10px 30px rgba(0, 0, 0, .45);
89
+ --chat-min-w: 360px;
90
+ --shell-bg: #1a1a1a;
91
+ --sidebar-bg: #1f1f1f;
92
+ --right-bg: #252525;
93
+ --hover: rgba(255,255,255,.07);
94
+ --hover-strong: #2a2a2a;
95
+ --active: #333333;
96
+ --field-bg: #1f1f1f;
97
+ --field-soft: #262626;
98
+ --overlay-bg: #252525;
99
+ --toast-bg: #252525;
100
+ --modal-mask: rgba(0, 0, 0, .55);
101
+ --fade-stop: rgba(26, 26, 26, .94);
102
+ --danger-soft: rgba(240, 97, 109, .14);
103
+ --ok-soft: rgba(52, 179, 107, .14);
104
+ --warn-soft: rgba(245, 166, 35, .16);
105
+ --user-bubble-soft: #2e2e2e;
106
+ --send-bg: #ededed;
107
+ --send-bg-hover: #ffffff;
108
+ --send-ink: #1a1a1a;
109
+ --quote-bg: #1f1f1f;
110
+ --quote-line: #333333;
111
+ --mark-bg: rgba(255, 255, 255, .18);
112
+ --brand-line: rgba(47, 107, 255, .4);
113
+ --brand-line-strong: rgba(47, 107, 255, .55);
114
+ --table-stripe: rgba(255,255,255,.03);
115
+ --scrollbar: rgba(255,255,255,.16);
116
+ --scrollbar-hover: rgba(255,255,255,.3);
117
+ --copied-bg: rgba(52, 179, 107, .16);
118
+ --copied-ink: #5fd693;
119
+ --drawer-shadow: -12px 0 40px rgba(0, 0, 0, .5);
120
+ --mobile-drawer-shadow: 0 8px 40px rgba(0, 0, 0, .55);
121
+ --spin-edge: rgba(255,255,255,.2);
122
+ --spin-top: rgba(255,255,255,.5);
123
+ /* 侧边消息滑块:暗色主题 —— 视口内可见=亮白,滚出视口=暗灰 */
124
+ --slider-on: rgba(255, 255, 255, .82);
125
+ --slider-off: rgba(255, 255, 255, .17);
126
+ --slider-hover: #ffffff;
127
+ }
128
+
129
+ * { box-sizing: border-box; }
130
+
131
+ /* 细滚动条:与 WorkBuddy 一致(4px 圆角浅灰) */
132
+ * { scrollbar-width: thin; scrollbar-color: var(--scrollbar) transparent; }
133
+ *::-webkit-scrollbar { width: 4px; height: 4px; }
134
+ *::-webkit-scrollbar-thumb { background: var(--scrollbar); border-radius: 999px; }
135
+ *::-webkit-scrollbar-track { background: transparent; }
136
+ /* 聊天消息区 + 右栏滚动条:自动隐藏(平时透明,滚动/悬停时显现,1.5s 无操作渐隐)。
137
+ webkit 的 ::-webkit-scrollbar 伪元素不支持 CSS 动画,由 JS(.sb-active)切换显示;
138
+ Firefox 用 scrollbar-color 同样随 .sb-active 切换(默认透明,滚动时显现) */
139
+ .chat-body, .chat-right-section-body { scrollbar-width: thin; scrollbar-color: rgba(31,39,51,0) transparent; }
140
+ .chat-body.sb-active, .chat-right-section-body.sb-active { scrollbar-color: var(--scrollbar) transparent; }
141
+ .chat-body::-webkit-scrollbar-thumb,
142
+ .chat-right-section-body::-webkit-scrollbar-thumb { background: rgba(31,39,51,0); }
143
+ .chat-body.sb-active::-webkit-scrollbar-thumb,
144
+ .chat-right-section-body.sb-active::-webkit-scrollbar-thumb { background: var(--scrollbar); }
145
+
146
+ html, body {
147
+ margin: 0;
148
+ padding: 0;
149
+ height: 100%;
150
+ overflow-x: hidden;
151
+ background: var(--bg);
152
+ color: var(--ink);
153
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", Roboto, Helvetica, Arial, sans-serif;
154
+ -webkit-font-smoothing: antialiased;
155
+ }
156
+
157
+ a { color: inherit; }
158
+
159
+ /* ============ 登录页(黑白灰色系) ============ */
160
+ .login-page {
161
+ min-height: 100vh;
162
+ display: flex;
163
+ flex-direction: column;
164
+ align-items: center;
165
+ justify-content: center;
166
+ background: radial-gradient(1200px 600px at 50% -10%, #eef0f4 0%, var(--bg) 55%);
167
+ padding: 24px;
168
+ }
169
+ .login-shell { width: 100%; max-width: 420px; }
170
+ .login-brand { display: flex; align-items: center; gap: 14px; margin-bottom: 22px; }
171
+ .login-brand-logo { height: 32px; width: auto; display: block; }
172
+ .login-brand-title h1 { margin: 0; font-size: 20px; letter-spacing: .3px; }
173
+ .login-brand-title span { font-size: 13px; color: var(--ink-soft); }
174
+
175
+ .login-card {
176
+ background: var(--panel);
177
+ border: 1px solid var(--line);
178
+ border-radius: var(--radius);
179
+ padding: 28px 26px 24px;
180
+ box-shadow: var(--shadow);
181
+ position: relative;
182
+ overflow: hidden;
183
+ }
184
+ .login-card-line {
185
+ position: absolute; top: 0; left: 0; right: 0; height: 4px;
186
+ background: linear-gradient(90deg, #2a3140, #8b93a3);
187
+ }
188
+ .login-dingtalk-heading { display: flex; flex-direction: column; gap: 4px; margin-bottom: 22px; }
189
+ .login-dingtalk-heading strong { font-size: 17px; }
190
+ .login-dingtalk-heading span { font-size: 13px; color: var(--ink-soft); }
191
+
192
+ /* 登录方式选择器(AUTH_PROVIDER 多个时渲染):黑白灰 */
193
+ .login-provider-list { display: flex; flex-direction: column; gap: 10px; }
194
+ .login-provider-button {
195
+ display: flex; align-items: center; justify-content: center; gap: 10px;
196
+ width: 100%; padding: 13px 16px; border-radius: 10px;
197
+ background: var(--panel);
198
+ border: 1px solid var(--ink);
199
+ color: var(--ink); font-size: 15px; font-weight: 600; text-decoration: none;
200
+ transition: background .15s ease, color .15s ease;
201
+ }
202
+ .login-provider-button svg { width: 22px; height: 22px; fill: currentColor; }
203
+
204
+ /* 兼容旧用法:单个登录按钮(同样黑白灰) */
205
+ .login-dingtalk-button {
206
+ display: flex; align-items: center; justify-content: center; gap: 10px;
207
+ width: 100%; padding: 13px 16px; border-radius: 10px;
208
+ background: var(--panel);
209
+ border: 1px solid var(--ink);
210
+ color: var(--ink); font-size: 15px; font-weight: 600; text-decoration: none;
211
+ transition: background .15s ease, color .15s ease;
212
+ }
213
+ .login-dingtalk-button svg { width: 22px; height: 22px; fill: currentColor; }
214
+
215
+ .login-auto-status {
216
+ display: flex; align-items: center; gap: 8px;
217
+ font-size: 13px; color: var(--ink-soft); margin: 12px 0 14px;
218
+ }
219
+ .login-auto-status i {
220
+ width: 8px; height: 8px; border-radius: 50%; background: var(--ink-soft);
221
+ animation: pulse 1s infinite ease-in-out;
222
+ }
223
+ @keyframes pulse { 0%,100%{opacity:.3} 50%{opacity:1} }
224
+
225
+ .login-dev-wrap { margin-top: 16px; }
226
+ .login-divider { display: flex; align-items: center; gap: 10px; color: var(--ink-soft); font-size: 12px; margin: 8px 0; }
227
+ .login-divider::before, .login-divider::after { content: ""; flex: 1; height: 1px; background: var(--line); }
228
+ .login-dev-button {
229
+ width: 100%; padding: 11px 16px; border-radius: 10px;
230
+ border: 1px solid var(--line); background: var(--panel); color: var(--ink);
231
+ font-size: 14px; font-weight: 600; cursor: pointer;
232
+ text-decoration: none; display: block; text-align: center;
233
+ transition: background .15s ease, color .15s ease;
234
+ }
235
+ .login-dev-button:disabled { opacity: .6; cursor: default; }
236
+
237
+ .login-error { color: var(--danger); font-size: 13px; margin-top: 14px; min-height: 18px; }
238
+ .login-dots { display: flex; gap: 6px; justify-content: center; margin: 18px 0; }
239
+ .login-dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-soft); }
240
+ .login-copyright { color: var(--ink-soft); font-size: 12px; margin-top: 6px; }
241
+
242
+ /* ============ 聊天页 ============ */
243
+ /* 三栏布局壳:左栏 / 中栏 / 右栏 横向铺满;左右默认收起(flex-basis 0),中栏全屏。
244
+ 应用窗口宽度由用户缩放控制:禁止窗口级水平滚动,右栏打开时压缩中栏而非横向溢出 */
245
+ .chat-shell { height: 100vh; display: flex; flex-direction: row; align-items: stretch; background: var(--shell-bg); overflow-x: hidden; overflow-y: hidden; }
246
+ .chat-topbar {
247
+ position: relative;
248
+ display: flex; align-items: center; gap: 12px;
249
+ flex: 0 0 auto;
250
+ height: 46px; padding: 0 16px;
251
+ border-bottom: 1px solid var(--line); background: var(--panel);
252
+ }
253
+ .chat-brand { display: flex; align-items: center; gap: 10px; }
254
+ .chat-logo { height: 26px; width: auto; display: block; flex: 0 0 auto; }
255
+ /* 顶栏左侧:侧边栏展开按钮 + 品牌 Logo + 会话标题。
256
+ flex:1 + min-width:0 → 空间不足时由它承担收缩(挤压标题),右侧按钮组始终完整。
257
+ overflow:hidden 防止长标题把 flex 项最小宽度撑死、把右侧按钮推出顶栏。 */
258
+ .chat-topbar-left { flex: 1 1 auto; min-width: 0; overflow: hidden; display: flex; align-items: center; gap: 6px; }
259
+ /* 按钮组固定不收缩:从右往左占据空间,空间不够时挤压左侧标题而不是被裁切 */
260
+ .chat-topbar-right { margin-left: auto; flex: 0 0 auto; display: flex; align-items: center; gap: 10px; }
261
+ .chat-sidebar-toggle {
262
+ display: inline-flex; align-items: center; justify-content: center;
263
+ width: 34px; height: 34px; border-radius: 8px;
264
+ border: none; background: transparent; color: var(--ink-soft); cursor: pointer;
265
+ transition: background .15s ease, color .15s ease;
266
+ }
267
+ .chat-sidebar-toggle svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
268
+ /* 中栏:全屏主体。真正的最小宽度 = 正常手机宽度(375px,与 JS 侧 CHAT_MIN_W 保持一致):
269
+ 即使 PC 上拖宽右侧边栏、或窗口收窄,中间 chat 区域也不会被挤压到 375px 以下。
270
+ min(375px, 100%) 保证窗口比手机还窄(如 360px 小屏)时回退为全宽,不产生水平溢出。 */
271
+ .chat-main { flex: 1 1 auto; min-width: min(375px, 100%); display: flex; flex-direction: column; height: 100vh; position: relative; }
272
+ .chat-main-inner {
273
+ flex: 0 1 auto;
274
+ min-height: 0;
275
+ display: flex; flex-direction: column;
276
+ justify-content: flex-end;
277
+ /* 输入区宽度 = min(中栏可用宽度的 80%, 1200px) 居中:
278
+ - 中栏 < 1200px/0.8 = 1500px 时,按 80% 缩放(随窗口/右栏自适应,两侧留白 10%);
279
+ - 中栏 ≥ 1500px(超大屏)时封顶 1200px,不再随百分比继续变宽。
280
+ 中栏 = 窗口 − 左栏 − 右栏;中栏最小宽度由 .chat-main 的 min-width 保证(375px)。 */
281
+ width: 100%; max-width: min(80%, 1200px); margin: 0 auto;
282
+ position: relative;
283
+ }
284
+ /* 新建任务空态(无消息):输入区与欢迎/提示一起垂直居中,参考 WB 新建任务界面;
285
+ 用户输入第一条消息后 is-empty 移除,chat-main-inner 自动回到底部常规布局 */
286
+ .chat-main.is-empty .chat-main-inner {
287
+ position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
288
+ width: min(680px, calc(100% - 32px)); max-width: none; margin: 0;
289
+ gap: 14px;
290
+ justify-content: center;
291
+ }
292
+ .chat-main.is-empty .chat-empty { margin: 0 auto 4px; max-width: 520px; }
293
+ .chat-main.is-empty .chat-suggestions { flex-wrap: wrap; justify-content: center; gap: 8px; }
294
+ .chat-main.is-empty .chat-composer-stack { margin: 0; width: 100%; box-sizing: border-box; }
295
+ /* 离开底部时:消息区与输入区交界的浮层(锚定输入区容器顶部 bottom:100%,滚动时不动)。
296
+ overlay 自身高度 0,fade 向上延伸进消息区、按钮浮在其上 */
297
+ .chat-body-overlay {
298
+ position: absolute; left: 0; right: 0; bottom: 100%; height: 0;
299
+ pointer-events: none; z-index: 5;
300
+ }
301
+ /* 消息底部渐隐 + 毛玻璃模糊 */
302
+ .chat-body-fade {
303
+ position: absolute; left: 0; right: 0; bottom: 0; height: 72px;
304
+ pointer-events: none;
305
+ background: linear-gradient(180deg, rgba(255,255,255,0) 0%, var(--fade-stop) 100%);
306
+ backdrop-filter: blur(6px);
307
+ -webkit-backdrop-filter: blur(6px);
308
+ }
309
+ /* Electron:backdrop-filter 合成会把覆盖区文字一起糊掉,仅保留渐变渐隐(浏览器保持毛玻璃) */
310
+ .env-electron .chat-body-fade {
311
+ backdrop-filter: none;
312
+ -webkit-backdrop-filter: none;
313
+ }
314
+ /* 居中「回到底部」按钮:一直悬浮在输入框上方 */
315
+ .chat-bottom-btn {
316
+ position: absolute; left: 50%; transform: translateX(-50%); bottom: 14px;
317
+ width: 34px; height: 34px; border-radius: 50%; z-index: 6;
318
+ border: 1px solid var(--line); background: var(--field-bg); color: var(--ink-soft);
319
+ display: inline-flex; align-items: center; justify-content: center;
320
+ cursor: pointer; pointer-events: auto;
321
+ box-shadow: 0 2px 8px rgba(31,39,51,.14);
322
+ transition: background .15s ease, color .15s ease, border-color .15s ease;
323
+ }
324
+ .chat-bottom-btn svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
325
+ /* 大模型输出中(busy)且用户离开底部:按钮细环转圈动画(subtle)。
326
+ 单段弧:起点最亮,沿圆周到尾端淡出为 0(conic 渐变),旋转产生流动感;
327
+ 环形 mask 保留 1px 细环。颜色用主题变量 --spin-edge(深色下为柔和的亮灰、
328
+ 浅色下为浅灰):对比足够但不过亮。 */
329
+ .chat-bottom-btn.is-live { border-color: transparent; }
330
+ .chat-bottom-btn.is-live::before {
331
+ content: ""; position: absolute; inset: 0; border-radius: 50%;
332
+ background: conic-gradient(from 0deg,
333
+ transparent 0deg,
334
+ var(--spin-edge) 60deg,
335
+ var(--spin-edge) 300deg,
336
+ transparent 360deg);
337
+ -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 1px), #000 calc(100% - 1px));
338
+ mask: radial-gradient(farthest-side, transparent calc(100% - 1px), #000 calc(100% - 1px));
339
+ animation: chatBottomLive 1.1s linear infinite;
340
+ }
341
+ @keyframes chatBottomLive { to { transform: rotate(360deg); } }
342
+ /* 左栏:会话列表 + 底部用户信息;默认收起(flex-basis 0 + overflow hidden + 内容 visibility hidden) */
343
+ .chat-pane-left { flex: 0 0 0; overflow: hidden; transition: flex-basis .18s ease; border-right: 1px solid var(--line); }
344
+ .chat-pane-left.is-open { flex-basis: 280px; }
345
+ .chat-pane-left:not(.is-open) .chat-sidebar-inner { visibility: hidden; }
346
+ .chat-sidebar-inner {
347
+ width: 280px; height: 100%;
348
+ display: flex; flex-direction: column;
349
+ background: var(--sidebar-bg);
350
+ }
351
+ /* 右栏:上半输入文件 / 下半输出产物;默认收起。拖动改宽时用 --right-w 覆盖基础宽度。
352
+ 展开默认与中间消息区平分剩余水平宽度(50%) */
353
+ .chat-pane-right { flex: 0 0 0; overflow: hidden; transition: flex-basis .18s ease; border-left: 1px solid var(--line); position: relative; }
354
+ /* 右栏宽上限 = 容器宽 − 左栏实际占用(var(--left-used):开 280px / 关 0px) − 中栏最小宽(375):
355
+ 拖宽右栏可以把中间 chat 一直压到 375px 最小宽;与 JS 拖拽侧(LEFT_PANE_W / CHAT_MIN_W)同源,双保险。
356
+ 窗口极窄时 calc 为负 → flex-basis 按 0 处理。 */
357
+ .chat-pane-right.is-open { flex-basis: min(var(--right-w, 50%), calc(100% - var(--left-used, 280px) - 375px)); }
358
+ .chat-pane-right.is-dragging { transition: none; }
359
+ .chat-pane-right:not(.is-open) .chat-right-inner { visibility: hidden; }
360
+ /* 产物预览全屏(toggle):右栏占满 app 整个宽度,左栏与中栏让位隐藏;再点还原。
361
+ 仅在右栏可见时生效(chat-shell 的 has-preview-full 类已带 rightShown 条件)。 */
362
+ .chat-shell.has-preview-full .chat-pane-left,
363
+ .chat-shell.has-preview-full .chat-main { display: none; }
364
+ .chat-shell.has-preview-full .chat-pane-right {
365
+ flex: 1 1 100% !important; min-width: 0 !important; max-width: 100% !important;
366
+ }
367
+ .chat-shell.has-preview-full .chat-resizer { display: none; }
368
+ /* 中/右栏之间的拖拽 resizer:hover 时 2px 竖条 dim-in 渐显;拖动中常显 */
369
+ .chat-resizer {
370
+ position: absolute; left: -5px; top: 0; bottom: 0; width: 10px;
371
+ cursor: col-resize; z-index: 40;
372
+ }
373
+ .chat-resizer::after {
374
+ content: ""; position: absolute; left: 50%; top: 0; bottom: 0; width: 2px;
375
+ transform: translateX(-50%);
376
+ background: rgba(31,39,51,.28);
377
+ opacity: 0; transition: opacity .18s ease;
378
+ }
379
+ .chat-pane-right:not(.is-open) .chat-right-inner { visibility: hidden; }
380
+ .chat-right-inner {
381
+ width: 100%; height: 100%;
382
+ display: flex; flex-direction: column;
383
+ background: var(--right-bg);
384
+ }
385
+ .chat-right-section { display: flex; flex-direction: column; min-height: 0; }
386
+ /* 输入区:排在输出区下方(视觉顺序:输出在上 / 输入在下);高度自适应内容(不强制 50%),
387
+ 上限 50%;文件项单列占满整行,长文件名允许换行,避免两列布局/省略号造成"被截断"的观感 */
388
+ .chat-right-section--inputs { flex: 0 1 auto; max-height: 50%; order: 2; border-top: 1px solid var(--line); }
389
+ .chat-right-section--inputs .chat-right-section-body { display: flex; flex-direction: column; gap: 4px; padding: 8px; overflow-y: auto; }
390
+ .chat-right-section--inputs .chat-right-file { flex: 0 0 auto; width: 100%; }
391
+ /* 输出区:排在输入区上方(视觉顺序:输出在上 / 输入在下);默认占满剩余高度(上限 50%);
392
+ 无输入文件时 is-full 占满 100% */
393
+ .chat-right-section--outputs { flex: 1 1 auto; max-height: 50%; order: 1; }
394
+ .chat-right-section--outputs.is-full { flex: 1 1 auto; max-height: 100%; }
395
+ .chat-right-section-head {
396
+ flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between;
397
+ height: 46px; padding: 0 12px; border-bottom: 1px solid var(--line);
398
+ font-size: 13px; font-weight: 700; color: var(--ink);
399
+ }
400
+ .chat-right-count {
401
+ font-size: 11px; color: var(--ink-soft); background: var(--line);
402
+ border-radius: 999px; padding: 1px 7px; font-weight: 600;
403
+ }
404
+ /* 标题 + 计数紧贴一行(计数在"输出产物"文字右边);"打开文件夹"按钮仍靠右 */
405
+ .chat-right-section-title {
406
+ display: inline-flex; align-items: center; gap: 6px; min-width: 0;
407
+ }
408
+ /* 打开产物文件夹(本地 app):section-head 内的小按钮,图标+文字,hover 仅变色不位移、不变边框 */
409
+ .chat-right-folder-btn {
410
+ display: inline-flex; align-items: center; gap: 4px; margin-left: 8px;
411
+ height: 24px; padding: 0 8px; border: 1px solid var(--line); border-radius: 6px;
412
+ background: transparent; color: var(--ink-soft); font-size: 11.5px; font-weight: 600;
413
+ cursor: pointer; transition: color .15s ease, background .15s ease;
414
+ }
415
+ .chat-right-folder-btn svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
416
+ .chat-right-folder-btn:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 8%, transparent); }
417
+ .chat-right-folder-btn:active { color: var(--ink); }
418
+ .chat-right-section-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 6px; }
419
+ /* 输出区:列表项垂直排列并拉开间距 */
420
+ .chat-right-section--outputs .chat-right-section-body { display: flex; flex-direction: column; gap: 8px; padding: 8px; }
421
+ .chat-right-empty { padding: 18px 10px; font-size: 12.5px; color: var(--ink-soft); text-align: center; }
422
+ .chat-right-file {
423
+ display: flex; align-items: center; gap: 6px;
424
+ padding: 6px 8px; border-radius: 8px;
425
+ }
426
+ .chat-right-file-main {
427
+ flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 8px;
428
+ text-decoration: none; color: inherit;
429
+ }
430
+ .chat-right-file-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
431
+ .chat-right-file-name {
432
+ font-size: 12.5px; color: var(--ink);
433
+ white-space: normal; word-break: break-all; line-height: 1.35;
434
+ }
435
+ .chat-right-file-meta { font-size: 11px; color: var(--ink-soft); }
436
+ .chat-right-file-dl {
437
+ flex: none; width: 26px; height: 26px;
438
+ display: inline-flex; align-items: center; justify-content: center;
439
+ border-radius: 6px; color: var(--ink-soft); text-decoration: none;
440
+ transition: background .15s ease, color .15s ease;
441
+ }
442
+ .chat-right-file-dl svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
443
+ /* 左栏底部用户信息(WorkBuddy 风格):用户区域朴素无边框无背景、点击也不改变外观;
444
+ 点击后用户菜单 popup(外观 segment + 退出登录)出现在 profile 上方;点菜单外任意处关闭 */
445
+ .chat-sidebar-user {
446
+ position: relative;
447
+ flex: 0 0 auto; padding: 10px 12px; border-top: 1px solid var(--line);
448
+ display: flex; flex-direction: column; gap: 8px;
449
+ }
450
+ .chat-sidebar-user-trigger {
451
+ display: block; width: 100%; border: none; background: none; padding: 6px 4px;
452
+ cursor: pointer; text-align: left; border-radius: 8px;
453
+ }
454
+ .chat-sidebar-user-row { display: flex; align-items: center; gap: 10px; }
455
+ .chat-sidebar-user-name {
456
+ font-size: 13.5px; font-weight: 600; color: var(--ink);
457
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
458
+ }
459
+ /* 用户菜单 popup:悬浮于用户块上方。不再使用全屏 backdrop(透明 fixed 层会拦截消息区滚轮,
460
+ 造成"菜单开着时悬停消息区无法滚动");关闭改由 ChatWindow 的 document pointerdown 监听处理,
461
+ 滚轮事件穿透到消息区,悬停即可滚动(WorkBuddy 同款行为)。 */
462
+ .chat-user-menu {
463
+ position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
464
+ width: 232px; z-index: 60;
465
+ background: var(--overlay-bg); border: 1px solid var(--line);
466
+ border-radius: 12px; box-shadow: var(--shadow);
467
+ padding: 6px; display: flex; flex-direction: column; gap: 2px;
468
+ animation: chatUserMenuIn .14s ease;
469
+ }
470
+ @keyframes chatUserMenuIn {
471
+ from { opacity: 0; transform: translate(-50%, 4px); }
472
+ to { opacity: 1; transform: translate(-50%, 0); }
473
+ }
474
+ .chat-user-menu-row {
475
+ display: flex; align-items: center; justify-content: space-between; gap: 10px;
476
+ padding: 8px 10px;
477
+ }
478
+ .chat-user-menu-label { font-size: 13px; color: var(--ink); }
479
+ /* segment:浅色 / 深色(外观切换) */
480
+ .chat-user-menu-segment {
481
+ display: inline-flex; background: var(--hover-strong); border-radius: 8px; padding: 2px; gap: 2px;
482
+ }
483
+ .chat-user-menu-seg {
484
+ border: none; background: transparent; cursor: pointer;
485
+ padding: 4px 12px; border-radius: 6px;
486
+ font-size: 12.5px; color: var(--ink-soft);
487
+ transition: background .15s ease, color .15s ease, box-shadow .15s ease;
488
+ }
489
+ .chat-user-menu-seg.is-on {
490
+ background: var(--overlay-bg); color: var(--ink); font-weight: 600;
491
+ box-shadow: 0 1px 3px rgba(0, 0, 0, .14);
492
+ }
493
+ .chat-user-menu-sep { height: 1px; background: var(--line); margin: 2px 0; }
494
+ /* 开关(显示上下文使用量 / 显示模型):track + knob 的两态滑块 */
495
+ .chat-user-menu-switch {
496
+ flex: 0 0 auto; position: relative; cursor: pointer;
497
+ width: 34px; height: 20px; padding: 0;
498
+ border: none; border-radius: 999px;
499
+ background: var(--hover-strong);
500
+ transition: background .16s ease;
501
+ }
502
+ .chat-user-menu-switch.is-on { background: var(--brand); }
503
+ .chat-user-menu-switch-knob {
504
+ position: absolute; top: 2px; left: 2px;
505
+ width: 16px; height: 16px; border-radius: 50%;
506
+ background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .25);
507
+ transition: transform .16s ease;
508
+ }
509
+ .chat-user-menu-switch.is-on .chat-user-menu-switch-knob { transform: translateX(14px); }
510
+ .chat-user-menu-item {
511
+ display: block; width: 100%; text-align: left;
512
+ border: none; background: transparent; cursor: pointer;
513
+ padding: 9px 10px; border-radius: 8px; font-size: 13px; color: var(--ink);
514
+ transition: background .15s ease, color .15s ease;
515
+ }
516
+ .chat-user-menu-item--danger { color: var(--danger); }
517
+ .chat-sidebar-head {
518
+ display: flex; align-items: center; justify-content: space-between; gap: 8px;
519
+ flex: 0 0 auto;
520
+ height: 46px; padding: 0 14px;
521
+ }
522
+ /* 头部右上角应用名:logo 在左、应用名靠右(space-between 下 flex:1 推右) */
523
+ .chat-sidebar-appname {
524
+ margin-left: auto; flex: 0 1 auto; min-width: 0;
525
+ font-size: 12.5px; color: var(--ink-soft);
526
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
527
+ }
528
+ .chat-sidebar-title { font-weight: 700; font-size: 15px; color: var(--ink); }
529
+ /* 左栏顶部 Logo(宿主传入):固定高度、保持比例,与「+新对话」按钮同行 */
530
+ .chat-sidebar-logo { height: 24px; width: auto; display: block; }
531
+ /* 「新建任务」独立固定行(不随会话列表滚动):头部下方一行,⊕ 圆圈加号 + 文字;
532
+ 与下方会话列表只留间距、不画分隔线 */
533
+ .chat-sidebar-newrow {
534
+ flex: 0 0 auto;
535
+ padding: 8px 8px 6px;
536
+ }
537
+ .chat-sidebar-new {
538
+ display: flex; align-items: center; gap: 7px; width: 100%;
539
+ border: none; background: transparent; color: var(--ink);
540
+ padding: 7px 10px; border-radius: 8px;
541
+ font-size: 13.5px; font-weight: 600; text-align: left; cursor: pointer;
542
+ transition: background .15s ease;
543
+ }
544
+ /* ⊕ 圆圈加号:整体外径约 14px,略小于 13.5px 文字高度(描边同步加粗以免缩小后发虚) */
545
+ .chat-sidebar-new-icon {
546
+ width: 8px; height: 8px; flex: 0 0 auto;
547
+ border: 1.2px solid currentColor; border-radius: 50%;
548
+ padding: 1.5px; box-sizing: content-box;
549
+ fill: none; stroke: currentColor; stroke-width: 3.2; stroke-linecap: round;
550
+ }
551
+ .chat-sidebar-list { flex: 1; overflow-y: auto; padding: 10px 8px 8px; }
552
+ .chat-sidebar-empty { padding: 24px 12px; font-size: 13px; color: var(--ink-soft); text-align: center; }
553
+ .chat-sidebar-item {
554
+ display: flex; align-items: center; gap: 8px;
555
+ padding: 6px 10px; border-radius: 9px; cursor: pointer;
556
+ transition: background .15s ease;
557
+ }
558
+ .chat-sidebar-item.is-active { background: var(--active); }
559
+ /* 会话项主体:标题 + 时间(hover 时时间隐藏、位置让给编辑/删除按钮)同行,标题单行。
560
+ main 固定 22px 行高、按钮与文字同高 → hover 前后 item 高度恒定、不抖动 */
561
+ .chat-sidebar-item-main {
562
+ flex: 1 1 auto; min-width: 0;
563
+ display: flex; flex-direction: row; align-items: center; gap: 6px;
564
+ height: 26px;
565
+ }
566
+ .chat-sidebar-item-title {
567
+ flex: 1 1 auto; min-width: 0; overflow: hidden; white-space: nowrap;
568
+ font-size: 13px; color: var(--ink);
569
+ }
570
+ .chat-sidebar-item-title:not(.is-marquee) .chat-sidebar-item-title-text {
571
+ display: block; width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
572
+ }
573
+ .chat-sidebar-item-title-text { display: inline-block; white-space: nowrap; width: max-content; min-width: 100%; line-height: 26px; }
574
+ /* 超长标题走马灯:默认不播放(transform 归零 = 标题开头),hover 播放;
575
+ 鼠标移走后 animation 移除 → 立即回到标题开始处 */
576
+ .chat-sidebar-item-title.is-marquee .chat-sidebar-item-title-text {
577
+ animation: none;
578
+ display: block; width: 100%; overflow: hidden; text-overflow: ellipsis;
579
+ }
580
+ /* 非 hover:只显示单份 t1 并省略,避免双份文本在裁切窗口里拼接成重复 */
581
+ .chat-sidebar-item-title.is-marquee .chat-sidebar-item-title-t2 { display: none; }
582
+ .chat-sidebar-item-title-t1, .chat-sidebar-item-title-t2 { display: inline-block; }
583
+ @keyframes chatTitleMarquee {
584
+ 0% { transform: translateX(0); }
585
+ 100% { transform: translateX(-50%); }
586
+ }
587
+ .chat-sidebar-item-time { flex: 0 0 auto; font-size: 11px; color: #9aa1ab; line-height: 26px; }
588
+ /* 会话运行中:时间位显示黑白灰转圈动画(颜色随主题变量 --spin-edge/--spin-top 明暗变化) */
589
+ .chat-sidebar-item-spinner {
590
+ display: inline-block; width: 12px; height: 12px; box-sizing: border-box;
591
+ border: 2px solid var(--spin-edge); border-top-color: var(--spin-top);
592
+ border-radius: 50%; animation: chatSpin .7s linear infinite;
593
+ vertical-align: -2px;
594
+ }
595
+ /* 后台跑完、当前未查看的会话:时间位显示小绿圈(点进该会话即消失)。
596
+ 实心绿点 + 轻微脉冲,在明暗主题下均醒目,但不喧宾夺主。 */
597
+ .chat-sidebar-item-done {
598
+ display: inline-block; width: 8px; height: 8px; box-sizing: border-box;
599
+ border-radius: 50%; background: #21b573; vertical-align: 0;
600
+ box-shadow: 0 0 0 0 rgba(33, 181, 115, .5);
601
+ animation: chatSidebarDonePulse 1.6s ease-out infinite;
602
+ }
603
+ @keyframes chatSidebarDonePulse {
604
+ 0% { box-shadow: 0 0 0 0 rgba(33, 181, 115, .5); }
605
+ 70% { box-shadow: 0 0 0 5px rgba(33, 181, 115, 0); }
606
+ 100% { box-shadow: 0 0 0 0 rgba(33, 181, 115, 0); }
607
+ }
608
+ /* hover 时时间隐藏,⋯ 按钮原位出现(点开重命名/删除菜单)。actions 不被标题挤压 */
609
+ .chat-sidebar-item-actions { display: none; align-items: center; gap: 0; flex: 0 0 auto; }
610
+ .chat-sidebar-item-act {
611
+ display: inline-flex; align-items: center; justify-content: center;
612
+ width: 26px; height: 26px; border-radius: 50%;
613
+ border: none; background: transparent; color: var(--ink-soft); cursor: pointer;
614
+ transition: color .15s ease;
615
+ }
616
+ .chat-sidebar-item-act svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
617
+ /* 会话条目 ⋯ 按钮:复用 msg-footer「更多」图标——实心圆点(fill 当前色、无描边),菜单打开时高亮 */
618
+ .chat-sidebar-item-act.chat-sidebar-item-more svg { width: 17px; height: 17px; }
619
+ .chat-sidebar-item-act.chat-sidebar-item-more svg circle { fill: currentColor; stroke: none; }
620
+ .chat-sidebar-item-act.chat-sidebar-item-more.is-open { color: var(--ink); background: var(--line); }
621
+ /* 侧栏分组 ⋯ 与条目 ⋯ 视觉统一:同为实心圆点(与 msg-footer「更多」一致) */
622
+ .chat-sidebar-group-more svg circle,
623
+ .chat-sidebar-item-more svg circle { fill: currentColor; stroke: none; }
624
+ .chat-sidebar-rename-input {
625
+ flex: 1 1 auto; min-width: 0;
626
+ border: 1px solid var(--line); border-radius: 6px; padding: 5px 8px;
627
+ font-size: 13px; background: var(--field-bg); color: var(--ink); outline: none;
628
+ }
629
+ .chat-sidebar-rename-input:focus { border-color: var(--brand); }
630
+ /* 结构化提问卡片(AskUserQuestion):模型需要用户确认/选择时,正文下方渲染的交互卡片 */
631
+ .chat-question-area { display: flex; flex-direction: column; gap: 12px; margin-top: 12px; margin-bottom: 4px; }
632
+ .chat-question-wrap { display: flex; flex-direction: column; gap: 10px; }
633
+ .chat-question-card {
634
+ border: 1px solid var(--line); border-radius: 12px; padding: 12px 14px;
635
+ background: var(--panel); display: flex; flex-direction: column; gap: 8px;
636
+ }
637
+ .chat-question-header {
638
+ align-self: flex-start; font-size: 11px; font-weight: 700; color: var(--ink-soft);
639
+ background: var(--hover); border-radius: 999px; padding: 2px 10px;
640
+ }
641
+ .chat-question-text { font-size: 15px; color: var(--ink); line-height: 1.5; }
642
+ .chat-question-options { display: flex; flex-wrap: wrap; gap: 8px; }
643
+ .chat-question-opt {
644
+ display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
645
+ border: 1px solid var(--line); background: var(--field-bg); border-radius: 9px;
646
+ padding: 8px 12px; cursor: pointer; font-size: 12.5px; color: var(--ink);
647
+ transition: border-color .15s ease, background .15s ease;
648
+ }
649
+ .chat-question-opt-label { font-weight: 600; }
650
+ .chat-question-opt-desc { color: var(--ink-soft); font-size: 12px; }
651
+ .chat-question-opt.is-selected {
652
+ border-color: var(--ink); background: var(--hover);
653
+ box-shadow: 0 0 0 1px var(--ink) inset;
654
+ }
655
+ .chat-question-opt.is-selected .chat-question-opt-label::after {
656
+ content: " ✓"; color: var(--ink); font-weight: 700;
657
+ }
658
+ .chat-question-multi {
659
+ margin-left: 6px; font-size: 11px; color: var(--ink-soft); font-weight: 400;
660
+ }
661
+ .chat-question-other { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
662
+ .chat-question-other-label { font-size: 11px; color: var(--ink-soft); }
663
+ .chat-question-other-input {
664
+ width: 100%; box-sizing: border-box;
665
+ border: 1px solid var(--line); border-radius: 8px;
666
+ background: var(--field-bg); color: var(--ink);
667
+ padding: 7px 10px; font-size: 13px;
668
+ }
669
+ .chat-question-other-input::placeholder { color: var(--ink-soft); opacity: .8; }
670
+ .chat-question-other-input:focus { outline: none; border-color: var(--ink); }
671
+ .chat-question-submit-row { display: flex; justify-content: flex-end; margin-top: 2px; }
672
+ .chat-question-submit {
673
+ border: none; border-radius: 999px; padding: 7px 18px; cursor: pointer;
674
+ font-size: 13px; font-weight: 600; color: var(--panel); background: var(--ink);
675
+ transition: opacity .15s ease, transform .1s ease;
676
+ }
677
+ .chat-question-submit:active:not(:disabled) { transform: scale(.98); }
678
+ .chat-question-submit:disabled {
679
+ background: var(--line); color: var(--ink-soft); cursor: not-allowed; font-weight: 500;
680
+ }
681
+ .chat-question-card--done { background: var(--hover); border-style: dashed; }
682
+ .chat-question-answered-badge {
683
+ align-self: flex-start; font-size: 12px; font-weight: 700; color: var(--ink);
684
+ }
685
+ .chat-question-answered-text {
686
+ font-size: 13px; color: var(--ink); line-height: 1.6; white-space: pre-wrap;
687
+ }
688
+ .chat-title { font-weight: 700; font-size: 15px; letter-spacing: .3px; }
689
+ .chat-title--center {
690
+ position: absolute; left: 50%; transform: translateX(-50%);
691
+ white-space: nowrap;
692
+ }
693
+ /* 顶栏左侧会话标题:单行省略,给右侧四个按钮让位。
694
+ 上限用 min(100%, 420px) 相对父容器而非 vw——vw 以视口计,右侧面板打开
695
+ 挤窄中栏时 32vw 仍按整窗计算,会把按钮组推出顶栏右缘(历史 bug)。 */
696
+ .chat-title--left {
697
+ flex: 0 1 auto; min-width: 0;
698
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
699
+ max-width: min(100%, 420px);
700
+ }
701
+ /* 顶栏图标按钮(search / 分享 / 历史):视觉与 .chat-sidebar-toggle 一致 */
702
+ .chat-topbar-btn {
703
+ display: inline-flex; align-items: center; justify-content: center;
704
+ width: 34px; height: 34px; border-radius: 8px;
705
+ border: none; background: transparent; color: var(--ink-soft); cursor: pointer;
706
+ transition: background .15s ease, color .15s ease;
707
+ }
708
+ .chat-topbar-btn.is-active { background: rgba(31,79,214,.10); color: var(--brand); }
709
+ .chat-topbar-btn svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
710
+ /* 分享图标:采用 Ionicons arrow-redo-outline(闭合轮廓的空心曲线箭头,箭头有身体、内部空心) */
711
+ /* 描边宽度跟随顶栏通用值 1.8,与 search/history 等图标保持一致(不再单独加粗) */
712
+ .chat-topbar-btn[aria-label="分享"] svg { stroke-width: 1.8; }
713
+ .chat-topbar-right { gap: 6px; }
714
+
715
+ /* 面板打开时顶栏保持在遮罩之上(点其它顶栏按钮可切换面板) */
716
+ .chat-topbar { z-index: 220; }
717
+ /* 面板点击关闭遮罩:盖住中栏内容区(顶栏 z 更高不受影响) */
718
+ .chat-panel-backdrop {
719
+ position: fixed; inset: 0; z-index: 200;
720
+ background: transparent;
721
+ }
722
+ /* ---- 对话内搜索条(顶栏下方) ---- */
723
+ .chat-search-bar {
724
+ flex: 0 0 auto;
725
+ display: flex; align-items: center; gap: 8px;
726
+ padding: 8px 16px;
727
+ border-bottom: 1px solid var(--line); background: var(--panel);
728
+ z-index: 210;
729
+ }
730
+ .chat-search-bar-icon { width: 16px; height: 16px; flex: 0 0 auto; fill: none; stroke: var(--ink-soft); stroke-width: 1.8; stroke-linecap: round; }
731
+ .chat-search-bar-input {
732
+ flex: 1 1 auto; min-width: 0; height: 30px;
733
+ border: 1px solid var(--line); border-radius: 8px; background: var(--field-bg);
734
+ padding: 0 10px; font-size: 13px; color: var(--ink); outline: none;
735
+ }
736
+ .chat-search-bar-input:focus { border-color: var(--brand); }
737
+ .chat-search-bar-count {
738
+ flex: 0 0 auto; font-size: 12px; color: var(--ink-soft);
739
+ font-variant-numeric: tabular-nums; min-width: 34px; text-align: center;
740
+ }
741
+ .chat-search-bar-btn {
742
+ flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
743
+ width: 30px; height: 30px; border-radius: 7px;
744
+ border: none; background: transparent; color: var(--ink-soft); cursor: pointer;
745
+ }
746
+ .chat-search-bar-btn svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
747
+ .chat-search-bar-close { color: var(--ink-soft); }
748
+
749
+ /* ---- 历史提问下拉 ---- */
750
+ .chat-history-pop,
751
+ .chat-share-pop {
752
+ position: absolute; top: 66px; right: 12px; z-index: 230;
753
+ width: min(360px, calc(100vw - 24px));
754
+ background: var(--overlay-bg); border: 1px solid var(--line); border-radius: 12px;
755
+ box-shadow: 0 12px 32px rgba(20, 27, 38, .16);
756
+ overflow: hidden;
757
+ }
758
+ .chat-history-head {
759
+ padding: 12px 14px; font-size: 13px; font-weight: 700; color: var(--ink);
760
+ border-bottom: 1px solid var(--line);
761
+ }
762
+ .chat-history-list { max-height: min(56vh, 420px); overflow-y: auto; padding: 6px; }
763
+ .chat-history-empty { padding: 22px 14px; text-align: center; font-size: 13px; color: var(--ink-soft); }
764
+ .chat-history-item {
765
+ display: flex; align-items: flex-start; width: 100%; text-align: left;
766
+ border: none; background: transparent; cursor: pointer;
767
+ padding: 9px 10px; border-radius: 8px; font-size: 13px; color: var(--ink);
768
+ line-height: 1.5;
769
+ }
770
+ .chat-history-item + .chat-history-item { margin-top: 2px; }
771
+ .chat-history-text {
772
+ display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
773
+ overflow: hidden; word-break: break-word;
774
+ }
775
+
776
+ /* ---- 分享面板:三选项(复制链接 / 浏览器打开 / 发送到 OA 聊天) ---- */
777
+ .chat-share-head {
778
+ display: flex; align-items: center; justify-content: space-between;
779
+ padding: 12px 14px; font-size: 13px; font-weight: 700; color: var(--ink);
780
+ border-bottom: 1px solid var(--line);
781
+ }
782
+ .chat-share-close {
783
+ flex: 0 0 auto; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center;
784
+ border: none; background: transparent; border-radius: 6px; cursor: pointer; color: var(--ink-soft);
785
+ }
786
+ .chat-share-close svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
787
+ .chat-share-loading,
788
+ .chat-share-error { padding: 16px 14px; font-size: 13px; color: var(--ink-soft); }
789
+ .chat-share-error { color: var(--danger); }
790
+ .chat-share-opt {
791
+ display: flex; align-items: center; gap: 11px; width: 100%;
792
+ padding: 11px 14px; border: none; background: transparent; cursor: pointer; text-align: left;
793
+ }
794
+ .chat-share-opt + .chat-share-opt { border-top: 1px solid var(--line); }
795
+ .chat-share-opt:disabled { opacity: .55; cursor: default; }
796
+ .chat-share-opt-icon {
797
+ flex: 0 0 auto; width: 34px; height: 34px; display: flex; align-items: center; justify-content: center;
798
+ background: var(--field-soft); border-radius: 9px; color: var(--ink);
799
+ }
800
+ .chat-share-opt-icon svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
801
+ .chat-share-opt-body { display: flex; flex-direction: column; min-width: 0; }
802
+ .chat-share-opt-title { font-size: 13.5px; font-weight: 600; color: var(--ink); }
803
+ .chat-share-opt-desc { font-size: 12px; color: var(--ink-soft); margin-top: 2px; }
804
+ .chat-share-opt-state { flex: 0 0 auto; margin-left: auto; font-size: 12px; color: #1f9d55; font-weight: 600; }
805
+ .chat-share-oa-msg { margin: 4px 14px 12px; font-size: 12px; color: #1f9d55; }
806
+ .chat-share-oa-msg.is-error { color: var(--danger); }
807
+ /* 飞书分享成功后的轻提示条:与上方操作项区分,作为"已分享"反馈,整条可点击跳转会话 */
808
+ .chat-share-oa-link {
809
+ display: flex; align-items: center; gap: 6px;
810
+ margin: 2px 14px 12px; padding: 7px 10px;
811
+ width: calc(100% - 28px); box-sizing: border-box;
812
+ border: 1px solid var(--line); border-radius: 8px;
813
+ background: var(--hover); cursor: pointer;
814
+ font-size: 12px; color: #1f9d55; font-weight: 600;
815
+ text-align: left; transition: background 0.15s ease;
816
+ }
817
+ .chat-share-oa-link svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; flex: 0 0 auto; }
818
+ /* 分享直达发送(未打开面板)的结果反馈:顶部居中 toast,黑白灰系 */
819
+ .chat-share-toast {
820
+ position: fixed;
821
+ top: 14px;
822
+ left: 50%;
823
+ transform: translateX(-50%);
824
+ z-index: 1000;
825
+ padding: 7px 16px;
826
+ border: 1px solid var(--line);
827
+ border-radius: 10px;
828
+ background: var(--toast-bg);
829
+ color: var(--ink);
830
+ font-size: 13px;
831
+ box-shadow: 0 6px 24px rgba(31, 39, 51, 0.12);
832
+ animation: chatShareToastIn 0.18s ease-out;
833
+ }
834
+ .chat-share-toast.is-error { color: var(--danger); border-color: #f1d6d6; }
835
+ @keyframes chatShareToastIn {
836
+ from { opacity: 0; transform: translateX(-50%) translateY(-6px); }
837
+ to { opacity: 1; transform: translateX(-50%) translateY(0); }
838
+ }
839
+
840
+ /* 搜索命中高亮(黑白灰系,参考 wb):淡灰边框 + 内边距让边框与内容留出呼吸感;
841
+ 命中关键字由正文里的 <mark> 背景高亮(见 Markdown.tsx / UserBubble 的 highlight) */
842
+ .msg.is-search-hit {
843
+ border: 1px solid var(--line);
844
+ border-radius: 12px;
845
+ padding: 8px 10px;
846
+ background: var(--field-soft);
847
+ }
848
+ /* 当前定位的命中:边框加深一档 + 背景更实 */
849
+ .msg.is-search-current {
850
+ border-color: var(--line);
851
+ background: var(--hover-strong);
852
+ }
853
+ mark {
854
+ background: var(--mark-bg);
855
+ color: inherit;
856
+ border-radius: 3px;
857
+ padding: 0 2px;
858
+ }
859
+ .chat-user-avatar {
860
+ width: 32px; height: 32px; border-radius: 50%; object-fit: cover;
861
+ border: 1px solid var(--line); display: block;
862
+ }
863
+ .chat-user-avatar--fallback {
864
+ background: linear-gradient(135deg, var(--brand), #6b8bff); color: #fff;
865
+ display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 13px;
866
+ }
867
+
868
+ /* 聊天滚动容器:全宽(滚动条贴最右缘,与 wb/豆包 work 一致),
869
+ 消息内容在内部 .chat-body-inner 限宽中栏 80% 居中。
870
+ scrollbar-gutter: stable:滚动条出现/消失(展开收起思考框导致溢出变化)时始终预留
871
+ 等宽 gutter,内容区宽度恒定——否则滚动条出现瞬间消息列表会整体左移抖动。 */
872
+ .chat-body {
873
+ flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; padding: 0; display: block; scrollbar-gutter: stable;
874
+ /* 滚动容器全宽:滚动条贴聊天区最右缘(右栏打开时 = 右栏左侧),
875
+ 消息内容限宽由 .chat-body-inner 承担(min(80%, 1200px) 居中) */
876
+ width: 100%; max-width: none; margin: 0;
877
+ }
878
+ .chat-body-inner {
879
+ width: 100%; max-width: min(80%, 1200px); margin: 0 auto;
880
+ box-sizing: border-box;
881
+ /* 底部留白(WorkBuddy 同款):消息区垂直高度略大于内容,滚动到底后还能再滚一段,
882
+ 让最后一条回复上方的「更多」菜单向上弹出时有足够空间,不会顶出视口 */
883
+ padding: 20px 26px 120px 18px;
884
+ display: flex; flex-direction: column; gap: 14px;
885
+ }
886
+ /* 流式置顶 reserve:消息列表底部的动态 spacer(纯占位,无内容、无背景)。
887
+ 高度由 useChatScroll 的 syncStreamScroll() 计算并实时写入:
888
+ reserve = max(0, 用户消息偏移 + 一屏 V − 真实内容高)
889
+ 设历史消息高 x、新发消息高 h、回复高 r、视口高 V:
890
+ - 置顶:reserve 补足「把用户消息顶到视口顶部」所需的滚动范围(否则 scrollTop 被浏览器 clamp)。
891
+ - 回复未填满(h+r ≤ V):reserve = V − h − r,总高度 = x + V 恒定 → scrollTop 纹丝不动,
892
+ 不抖动;此时「新消息下方到输入区之间的空白」= V − h,满足「空白 + h = 可见高度 V」。
893
+ - 回复写长:reserve 自动归零,内容自撑,一次性平滑切到底部跟随。
894
+
895
+ ★ 实现硬约束(踩坑记录,勿改):测「真实内容高」绝不能把 spacer 临时置 0 再读 scrollHeight。
896
+ scrollHeight 瞬间变短 → 浏览器立即 clamp scrollTop → spacer 恢复后 scrollTop 不回来,
897
+ 表现为「LLM 一开始输出,整条消息列表瞬间掉到下面」。必须用 scrollHeight − spacer 实际高度 反推。 */
898
+ .chat-stream-spacer { flex: 0 0 auto; width: 100%; }
899
+ .chat-empty { margin: auto; text-align: center; color: var(--ink-soft); max-width: 360px; }
900
+ .chat-empty h2 { color: var(--ink); font-size: 18px; margin: 0 0 8px; }
901
+ .chat-empty p { font-size: 14px; line-height: 1.6; }
902
+ .chat-loading-state { padding: 18px 12px; color: var(--ink-soft); font-size: 12.5px; }
903
+ .chat-loading-caption { display: flex; align-items: center; gap: 8px; justify-content: center; line-height: 20px; }
904
+ .chat-loading-spinner {
905
+ display: inline-block; flex: 0 0 16px; width: 16px; height: 16px; box-sizing: border-box;
906
+ border: 2px solid var(--spin-edge); border-top-color: var(--spin-top);
907
+ border-radius: 50%; animation: chatSpin .75s linear infinite;
908
+ }
909
+ .chat-loading-state--session { display: flex; align-items: center; justify-content: center; min-height: 160px; flex: 1; }
910
+ .chat-body.is-loading .chat-body-inner { min-height: 100%; padding-bottom: 20px; }
911
+ .chat-loading-state--sidebar { padding: 12px 8px; }
912
+ .chat-loading-state--sidebar .chat-loading-caption { justify-content: flex-start; padding: 0 4px 10px; }
913
+ .chat-loading-state--artifacts { padding: 16px 12px; }
914
+ .chat-loading-skeleton { display: flex; flex-direction: column; gap: 12px; margin-top: 14px; }
915
+ .chat-loading-skeleton-row { display: flex; align-items: center; gap: 10px; min-height: 25px; }
916
+ .chat-loading-state--artifacts .chat-loading-skeleton-row { min-height: 48px; padding: 8px; border: 1px solid var(--line); border-radius: 8px; }
917
+ .chat-loading-skeleton-lines { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
918
+ .chat-loading-skeleton-line, .chat-loading-skeleton-icon {
919
+ display: block; border-radius: 5px;
920
+ background: linear-gradient(90deg, var(--hover-strong) 20%, var(--line) 50%, var(--hover-strong) 80%);
921
+ background-size: 240% 100%; animation: chatSkeletonShine 1.6s ease-in-out infinite;
922
+ }
923
+ .chat-loading-skeleton-line { height: 11px; width: 86%; }
924
+ .chat-loading-skeleton-row:nth-child(2n) .chat-loading-skeleton-line { width: 67%; }
925
+ .chat-loading-skeleton-line.is-short { height: 8px; width: 36%; }
926
+ .chat-loading-skeleton-icon { flex: 0 0 28px; width: 28px; height: 32px; }
927
+ .chat-loading-error { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 22px 12px; color: var(--ink-soft); font-size: 12.5px; }
928
+ .chat-loading-error button { border: 1px solid var(--line); border-radius: 6px; background: transparent; color: var(--ink); cursor: pointer; padding: 4px 9px; }
929
+ @keyframes chatSkeletonShine { from { background-position: 100% 0; } to { background-position: -100% 0; } }
930
+ @media (prefers-reduced-motion: reduce) {
931
+ .chat-loading-spinner, .chat-loading-skeleton-line, .chat-loading-skeleton-icon { animation: none; }
932
+ }
933
+ .chat-suggestions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 16px; }
934
+ .chat-suggestions button {
935
+ border: 1px solid var(--line); background: var(--hover-strong); color: var(--ink);
936
+ border-radius: 999px; padding: 7px 13px; font-size: 13px; cursor: pointer;
937
+ }
938
+
939
+ .msg { display: flex; max-width: 100%; min-width: 0; }
940
+ /* 用户消息:整体右对齐(消息块靠右),气泡内部文字左对齐。
941
+ 宽屏时限制 860px 最大宽度(对标豆包 Work 不占满、右侧留白);中栏被挤压变窄时
942
+ min(100%, 860px) 自动取小 → 逐渐占满全部宽度(连续无断点)。答复保持全宽左对齐。 */
943
+ .msg.user {
944
+ align-self: flex-end; flex-direction: column; align-items: flex-end;
945
+ max-width: min(100%, 860px);
946
+ }
947
+ .msg.bot { align-self: flex-start; flex-direction: column; width: 100%; }
948
+ .msg.user .bubble { text-align: left; }
949
+ .msg.bot .bubble { text-align: left; }
950
+ .msg .bubble {
951
+ padding: 11px 14px; border-radius: 14px; font-size: 15.5px; line-height: 1.65;
952
+ white-space: pre-wrap; word-break: break-word; overflow-wrap: anywhere; min-width: 0;
953
+ max-width: 100%;
954
+ }
955
+ /* 用户消息里的文件部分:带背景的圆角矩形条,与输入文字明确区分。
956
+ .msg-attachment = 有附件元数据的消息(气泡上方可点链接);
957
+ .msg-inline-file = 恢复的历史消息里残留 "[附件] 文件名" 文本时解析出的文件条 */
958
+ .msg-attachment,
959
+ .msg-inline-file {
960
+ display: inline-flex; align-items: center; gap: 5px;
961
+ max-width: 100%; overflow-wrap: anywhere;
962
+ background: var(--brand-soft); border: 1px solid var(--brand-line); border-radius: 8px;
963
+ padding: 2px 8px; font-size: 12.5px; color: var(--brand-dark);
964
+ }
965
+ /* vertical-align: middle:附件条与同行正文垂直居中(缺省基线对齐会因 flex 盒/小字号
966
+ 文件名的合成基线不同而错位);上下对称 margin,避免不对称 margin 盒把居中点顶偏 */
967
+ .msg-attachment { text-decoration: none; margin: 2px 8px 2px 0; vertical-align: middle; }
968
+ .msg-inline-file { margin: 2px 8px 2px 0; vertical-align: middle; }
969
+ /* 附件条内图标:与文件名之间用 CSS gap 控制,替代 emoji 字符间距不可控的问题 */
970
+ .msg-attachment-icon,
971
+ .msg-inline-file-icon {
972
+ flex: none;
973
+ display: grid;
974
+ place-items: center;
975
+ width: 14px;
976
+ height: 14px;
977
+ color: currentColor;
978
+ }
979
+ .msg-attachment-icon svg,
980
+ .msg-inline-file-icon svg {
981
+ width: 14px;
982
+ height: 14px;
983
+ fill: none;
984
+ stroke: currentColor;
985
+ stroke-width: 2;
986
+ stroke-linecap: round;
987
+ stroke-linejoin: round;
988
+ }
989
+ .msg-attachment-name,
990
+ .msg-inline-file-name {
991
+ min-width: 0;
992
+ }
993
+ .msg.bot .bubble { align-self: flex-start; background: transparent; color: var(--ink); padding: 2px 0; border-bottom-left-radius: 4px; }
994
+ .msg.user .bubble { background: var(--user-bubble-soft); color: var(--ink); border-bottom-right-radius: 4px; }
995
+ .msg .typing { display: inline-flex; gap: 4px; align-items: center; }
996
+ .msg .typing i { width: 6px; height: 6px; border-radius: 50%; background: #9aa6b8; animation: blink 1.2s infinite; }
997
+ .msg .typing i:nth-child(2){ animation-delay: .2s } .msg .typing i:nth-child(3){ animation-delay: .4s }
998
+ @keyframes blink { 0%,80%,100%{opacity:.3} 40%{opacity:1} }
999
+
1000
+ /* ===== 负责人名片卡片(每条 AI 回答末尾,独占一行、左对齐)===== */
1001
+ .chat-owner-wrap { display: block; width: 100%; margin-top: 12px; clear: both; }
1002
+ /* 答案与「转人工」之间的过渡分隔:左线—文字—右线 */
1003
+ .chat-owner-divider {
1004
+ display: flex; align-items: center; gap: 10px;
1005
+ margin: 2px 0 10px;
1006
+ color: var(--ink-soft); font-size: 12px; letter-spacing: .3px; opacity: .85;
1007
+ }
1008
+ .chat-owner-divider::before,
1009
+ .chat-owner-divider::after {
1010
+ content: ""; flex: 1; height: 1px; background: var(--line);
1011
+ }
1012
+ .chat-owner-card {
1013
+ display: flex; align-items: center; gap: 10px;
1014
+ padding: 10px 12px 10px 8px;
1015
+ border: 1px solid var(--line); border-radius: 12px;
1016
+ background: var(--panel); cursor: pointer; text-align: left;
1017
+ box-shadow: 0 1px 4px rgba(31, 39, 51, .06);
1018
+ transition: border-color .15s, box-shadow .15s;
1019
+ flex: none;
1020
+ }
1021
+ /* 责任人卡片 + 评分卡片:同一行、两端对齐(责任人靠左 / 评分靠右);
1022
+ 宽度不足时自动换行(小手机评分卡片落到下一行)。
1023
+ align-items: stretch 让两张卡片等高(以较高者为准),内容各自垂直居中。 */
1024
+ .chat-owner-cards {
1025
+ display: flex; align-items: stretch; justify-content: space-between;
1026
+ gap: 12px; flex-wrap: wrap;
1027
+ }
1028
+ .chat-owner-avatar {
1029
+ width: 36px; height: 36px; border-radius: 50%; flex: none;
1030
+ display: grid; place-items: center; overflow: hidden;
1031
+ background: linear-gradient(135deg, var(--brand), #6b8bff); color: #fff;
1032
+ font-weight: 700; font-size: 15px;
1033
+ }
1034
+ .chat-owner-avatar img { width: 100%; height: 100%; object-fit: cover; }
1035
+ .chat-owner-meta { display: flex; flex-direction: column; line-height: 1.25; }
1036
+ .chat-owner-name { font-weight: 600; font-size: 13.5px; color: var(--ink); }
1037
+ .chat-owner-dept { font-size: 12px; color: var(--ink-soft); }
1038
+ .chat-owner-action { margin-left: 6px; font-size: 12.5px; color: var(--brand); white-space: nowrap; }
1039
+
1040
+ /* ===== Markdown 渲染(react-markdown + remark-gfm + rehype-highlight)===== */
1041
+ /* ═══ 流式正文揭示:块级渐入(fade-in,对标 WorkBuddy)═══
1042
+ 流式期间每个新到达的块级子元素(p / li / heading…)淡入一次,
1043
+ 文字全程全实心可读;末尾保留闪烁光标 ▍ 作为"正在输出"信号。
1044
+ 不再使用扫光(shimmer / mask-image),避免"前暗后亮"错位。 */
1045
+
1046
+ /* 流式「渐入」关键帧:每个新到达的文本块(p / li / heading…)淡入一次,
1047
+ 对标 WorkBuddy 的流式渐入观感;文字全程全实心,不会出现扫光那种前暗后亮的错位。 */
1048
+ @keyframes mdFadeIn {
1049
+ from { opacity: 0; transform: translateY(3px); }
1050
+ to { opacity: 1; transform: translateY(0); }
1051
+ }
1052
+ @keyframes mdCaretBlink {
1053
+ 50% { opacity: 0; }
1054
+ }
1055
+ /* 流式正文容器:定位上下文 + 逐块渐入。
1056
+ 只作用于「块级子元素」,已稳定显示的块不会重播动画,只有流式中新追加的块会淡入。
1057
+ .md-table-wrap 豁免:表格在流式期间频繁更新,重播渐入会造成持续闪烁。 */
1058
+ .md--streaming { position: relative; }
1059
+ .md--streaming > *:not(.md-table-wrap) {
1060
+ animation: mdFadeIn .32s ease-out both;
1061
+ }
1062
+ /* 尾部光标:流式期间正文末尾闪烁 ▍,强化"正在输出" */
1063
+ .md--streaming::after {
1064
+ content: "▍";
1065
+ display: inline-block;
1066
+ margin-left: 2px;
1067
+ color: var(--ink-soft);
1068
+ animation: mdCaretBlink 1s steps(1) infinite;
1069
+ }
1070
+ /* 无障碍:减少动效时停掉渐入与光标闪烁(文字保持全实心,语义不丢) */
1071
+ @media (prefers-reduced-motion: reduce) {
1072
+ .md--streaming > * { animation: none; transform: none; }
1073
+ .md--streaming::after { animation: none; opacity: 1; }
1074
+ }
1075
+ .bubble .md, .artifact-md .md{ font-size: 15.5px; line-height: 1.7; color: var(--ink); white-space: normal; overflow-wrap: anywhere; }
1076
+ .bubble .md > *:first-child, .artifact-md .md > *:first-child{ margin-top: 0; }
1077
+ .bubble .md > *:last-child, .artifact-md .md > *:last-child{ margin-bottom: 0; }
1078
+ .bubble .md p, .artifact-md .md p{ margin: 0 0 8px; }
1079
+ .bubble .md h1, .bubble .md h2, .bubble .md h3,
1080
+ .bubble .md h4, .bubble .md h5, .bubble .md h6, .artifact-md .md h1, .artifact-md .md h2, .artifact-md .md h3,
1081
+ .artifact-md .md h4, .artifact-md .md h5, .artifact-md .md h6{ margin: 12px 0 6px; line-height: 1.35; font-weight: 700; }
1082
+ .bubble .md h1, .artifact-md .md h1{ font-size: 20px; }
1083
+ .bubble .md h2, .artifact-md .md h2{ font-size: 18px; }
1084
+ .bubble .md h3, .artifact-md .md h3{ font-size: 16.5px; }
1085
+ .bubble .md h4, .bubble .md h5, .bubble .md h6, .artifact-md .md h4, .artifact-md .md h5, .artifact-md .md h6{ font-size: 15.5px; }
1086
+ .bubble .md ul, .bubble .md ol, .artifact-md .md ul, .artifact-md .md ol{ margin: 0 0 8px; padding-left: 22px; }
1087
+ .bubble .md li, .artifact-md .md li{ margin: 2px 0; }
1088
+ .bubble .md li.task-list-item, .artifact-md .md li.task-list-item{ list-style: none; margin-left: -22px; }
1089
+ .bubble .md li.task-list-item input[type="checkbox"], .artifact-md .md li.task-list-item input[type="checkbox"]{ margin-right: 6px; }
1090
+ .bubble .md a, .artifact-md .md a{ color: var(--brand); text-decoration: none; border-bottom: 1px solid rgba(47,107,255,.35); }
1091
+ .bubble .md strong, .artifact-md .md strong{ font-weight: 700; }
1092
+ .bubble .md em, .artifact-md .md em{ font-style: italic; }
1093
+ .bubble .md del, .artifact-md .md del{ color: var(--ink-soft); }
1094
+ /* 行内代码:非 pre 内 code */
1095
+ .bubble .md :not(pre) > code, .artifact-md .md :not(pre) > code{
1096
+ background: var(--quote-bg); border: 1px solid var(--quote-line); border-radius: 5px;
1097
+ padding: 1px 5px; font-size: 12.5px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1098
+ color: var(--ink);
1099
+ }
1100
+ /* 围栏代码块(react-markdown 输出 pre > code,rehype-highlight 加 hljs 类) */
1101
+ .bubble .md pre, .artifact-md .md pre{
1102
+ background: #0f172a; color: #e2e8f0; border-radius: 10px; padding: 12px 14px;
1103
+ overflow-x: auto; margin: 0 0 10px; font-size: 12.5px; line-height: 1.55;
1104
+ }
1105
+ .bubble .md pre code, .artifact-md .md pre code{ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; white-space: pre; background: none; border: none; padding: 0; color: inherit; }
1106
+ /* GFM 表格 */
1107
+ .bubble .md .md-table-wrap, .artifact-md .md .md-table-wrap{
1108
+ margin: 12px 0 16px; max-width: 100%; overflow-x: auto;
1109
+ border: 1px solid var(--line); border-radius: 10px;
1110
+ scrollbar-width: thin; scrollbar-color: var(--scrollbar) transparent;
1111
+ }
1112
+ .md-table-wrap:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }
1113
+ .bubble .md table, .artifact-md .md table{
1114
+ border-collapse: separate; border-spacing: 0; margin: 0; font-size: 14px; width: 100%;
1115
+ }
1116
+ .bubble .md th, .bubble .md td, .artifact-md .md th, .artifact-md .md td{
1117
+ border: 0; border-bottom: 1px solid var(--line); padding: 12px 16px;
1118
+ text-align: left; vertical-align: top; line-height: 1.6; min-width: 96px; max-width: 440px;
1119
+ overflow-wrap: break-word;
1120
+ }
1121
+ .bubble .md th, .artifact-md .md th{ background: var(--field-soft); font-weight: 600; }
1122
+ .bubble .md tbody tr:last-child td, .artifact-md .md tbody tr:last-child td{ border-bottom: 0; }
1123
+ .bubble .md td > :last-child, .artifact-md .md td > :last-child{ margin-bottom: 0; }
1124
+ @media (hover: hover) and (pointer: fine) {
1125
+ .bubble .md tbody tr:hover td, .artifact-md .md tbody tr:hover td{ background: var(--table-stripe); }
1126
+ }
1127
+ /* 引用 */
1128
+ .bubble .md blockquote, .artifact-md .md blockquote{
1129
+ margin: 0 0 10px; padding: 8px 12px; border-left: 3px solid var(--brand);
1130
+ background: var(--brand-soft); border-radius: 0 8px 8px 0; color: var(--ink-soft);
1131
+ }
1132
+ .bubble .md blockquote > *:last-child, .artifact-md .md blockquote > *:last-child{ margin-bottom: 0; }
1133
+ .bubble .md hr, .artifact-md .md hr{ border: none; border-top: 1px solid var(--line); margin: 12px 0; }
1134
+ .bubble .md img, .artifact-md .md img{ max-width: 100%; border-radius: 8px; }
1135
+ /* rehype-highlight(highlight.js)代码高亮配色:GitHub Dark 精简版 */
1136
+ .bubble .md .hljs-comment, .bubble .md .hljs-quote, .artifact-md .md .hljs-comment, .artifact-md .md .hljs-quote{ color: #8b949e; font-style: italic; }
1137
+ .bubble .md .hljs-keyword, .bubble .md .hljs-selector-tag, .bubble .md .hljs-literal,
1138
+ .bubble .md .hljs-doctag, .bubble .md .hljs-type, .artifact-md .md .hljs-keyword, .artifact-md .md .hljs-selector-tag, .artifact-md .md .hljs-literal,
1139
+ .artifact-md .md .hljs-doctag, .artifact-md .md .hljs-type{ color: #ff7b72; }
1140
+ .bubble .md .hljs-string, .bubble .md .hljs-regexp, .bubble .md .hljs-addition,
1141
+ .bubble .md .hljs-attribute, .bubble .md .hljs-meta .hljs-string, .artifact-md .md .hljs-string, .artifact-md .md .hljs-regexp, .artifact-md .md .hljs-addition,
1142
+ .artifact-md .md .hljs-attribute, .artifact-md .md .hljs-meta .hljs-string{ color: #a5d6ff; }
1143
+ .bubble .md .hljs-number, .bubble .md .hljs-symbol, .bubble .md .hljs-bullet,
1144
+ .bubble .md .hljs-variable, .bubble .md .hljs-template-variable, .artifact-md .md .hljs-number, .artifact-md .md .hljs-symbol, .artifact-md .md .hljs-bullet,
1145
+ .artifact-md .md .hljs-variable, .artifact-md .md .hljs-template-variable{ color: #79c0ff; }
1146
+ .bubble .md .hljs-title, .bubble .md .hljs-section, .bubble .md .hljs-name, .artifact-md .md .hljs-title, .artifact-md .md .hljs-section, .artifact-md .md .hljs-name{ color: #d2a8ff; }
1147
+ .bubble .md .hljs-built_in, .bubble .md .hljs-builtin-name, .bubble .md .hljs-params, .artifact-md .md .hljs-built_in, .artifact-md .md .hljs-builtin-name, .artifact-md .md .hljs-params{ color: #ffa657; }
1148
+ .bubble .md .hljs-function .hljs-title, .artifact-md .md .hljs-function .hljs-title{ color: #d2a8ff; }
1149
+ .bubble .md .hljs-emphasis, .artifact-md .md .hljs-emphasis{ font-style: italic; }
1150
+ .bubble .md .hljs-strong, .artifact-md .md .hljs-strong{ font-weight: 700; }
1151
+
1152
+ /* ===== 思考过程(thinking)区:思考中 = 小窗滚动直播;思考结束 = 收起为「已思考 X 秒」可再展开 ===== */
1153
+ .chat-thinking { margin: 0 0 8px; }
1154
+ .chat-thinking-head {
1155
+ display: inline-flex; align-items: center; gap: 7px;
1156
+ padding: 0;
1157
+ background: transparent; border: none;
1158
+ color: var(--ink-soft);
1159
+ }
1160
+ .chat-thinking-label { font-size: 12.5px; }
1161
+ /* 实时计时(活动步「已用时 X 秒」):与胶囊文字同字号同色,数字等宽避免每秒跳动宽度抖动 */
1162
+ .chat-thinking-timer {
1163
+ font-size: 12.5px; color: inherit;
1164
+ font-variant-numeric: tabular-nums;
1165
+ }
1166
+ .chat-thinking-spinner {
1167
+ width: 12px; height: 12px; border-radius: 50%;
1168
+ border: 2px solid var(--line); border-top-color: var(--brand);
1169
+ animation: chatVoiceSpin .7s linear infinite;
1170
+ }
1171
+ /* 思考直播(WorkBuddy 同款):无边框无背景,仅左侧一根竖条。
1172
+ 最大高度 = 输入框高度 ×2(--chat-thinking-max-h);超出在框内滚动,不再无限撑高消息流。
1173
+ 组件内 useEffect(ThinkingLiveBox)每次文本更新把 scrollTop 拉到底,持续显示最新思考。 */
1174
+ .chat-thinking-live {
1175
+ margin: 6px 0 0;
1176
+ padding: 2px 12px 2px 12px;
1177
+ background: transparent; border: none; border-radius: 0;
1178
+ border-left: 2px solid #c9cdd4;
1179
+ max-height: var(--chat-thinking-max-h);
1180
+ overflow-y: auto;
1181
+ font-size: 12.5px; line-height: 1.7; color: var(--ink-soft);
1182
+ white-space: pre-wrap; word-break: break-word;
1183
+ scrollbar-width: thin; scrollbar-color: #c9cdd4 transparent;
1184
+ }
1185
+ .chat-thinking-live::-webkit-scrollbar { width: 6px; }
1186
+ .chat-thinking-live::-webkit-scrollbar-track { background: transparent; }
1187
+ .chat-thinking-live::-webkit-scrollbar-thumb { background: #c9cdd4; border-radius: 3px; }
1188
+ /* 思考结束胶囊:无背景无边框纯文字,「思考 N · 用时 X 秒 · 工具名」,点击展开/收起完整思考过程 */
1189
+ .chat-thinking--done { margin: 0 0 8px; }
1190
+ .chat-thinking-done-toggle {
1191
+ display: inline-flex; align-items: center; gap: 7px;
1192
+ max-width: 100%; min-width: 0;
1193
+ padding: 2px 0;
1194
+ background: transparent; border: none;
1195
+ color: var(--ink-soft); font-size: 12.5px; cursor: pointer;
1196
+ transition: color .15s ease;
1197
+ }
1198
+ /* hover:仅字体颜色变化(含箭头,箭头颜色继承字体),边框不变 */
1199
+ /* 胶囊文字:继承按钮字体与颜色(与箭头一致,无 opacity 削弱);
1200
+ inline-flex + align-items:center 让工具 icon 与工具名垂直居中对齐(icon 复用 .chat-thinking-tool-icon) */
1201
+ .chat-thinking-done-label {
1202
+ display: inline-flex; align-items: center; gap: 6px;
1203
+ flex: 0 1 auto; min-width: 0; overflow: hidden; white-space: nowrap;
1204
+ font-size: 12.5px; color: inherit;
1205
+ }
1206
+ .chat-thinking-done-toggle .chat-thinking-tool-name,
1207
+ .chat-thinking-done-toggle .chat-thinking-browser-label {
1208
+ flex: 0 1 auto; min-width: 0;
1209
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
1210
+ }
1211
+ .chat-thinking-activity { display: block; }
1212
+ .chat-thinking-icon {
1213
+ display: inline-flex; align-items: center; justify-content: center;
1214
+ flex: 0 0 16px;
1215
+ width: 16px; height: 16px;
1216
+ background: transparent; color: inherit;
1217
+ }
1218
+ /* 展开/收起箭头:收起态向右(chevron-right)、展开态向下(chevron-down) */
1219
+ .chat-thinking-icon svg {
1220
+ width: 13px; height: 13px;
1221
+ fill: none; stroke: currentColor; stroke-width: 2.4;
1222
+ stroke-linecap: round; stroke-linejoin: round;
1223
+ }
1224
+ /* 展开后的完整思考正文(与直播小窗同款:左侧竖条、无边框、超高滚动) */
1225
+ .chat-thinking-body {
1226
+ margin: 6px 0 0; padding: 2px 12px 2px 12px;
1227
+ max-height: 320px; overflow-y: auto;
1228
+ background: transparent; border: none; border-radius: 0;
1229
+ border-left: 2px solid #c9cdd4;
1230
+ font-size: 13.5px; line-height: 1.7; color: var(--ink-soft);
1231
+ white-space: pre-wrap; word-break: break-word;
1232
+ scrollbar-width: thin; scrollbar-color: #c9cdd4 transparent;
1233
+ }
1234
+ .chat-thinking-body::-webkit-scrollbar { width: 6px; }
1235
+ .chat-thinking-body::-webkit-scrollbar-track { background: transparent; }
1236
+ .chat-thinking-body::-webkit-scrollbar-thumb { background: #c9cdd4; border-radius: 3px; }
1237
+ /* 展开详情顶部的该步用时(折叠胶囊不显示用时,展开后在此展示) */
1238
+ .chat-thinking-secs {
1239
+ margin: 0 0 6px;
1240
+ font-size: 14px; color: var(--ink-soft); line-height: 1.5;
1241
+ font-variant-numeric: tabular-nums;
1242
+ }
1243
+ /* WorkBuddy 式「动作 → 结果」:完成步胶囊下方常驻的结果摘要(2 行省略;展开时前端隐藏避免重复)。
1244
+ 每一步都保留(含工具步):取思考末段,line-clamp 2 控制消息流长度,长任务多步时也不会堆满。
1245
+ 颜色用正文色 --ink(而非灰 --ink-soft):摘要与过程白字同色,保持「一直可见」的连续性。
1246
+ 字号层级:正式答复 15.5 > 结果摘要 14.5 > 动作胶囊 14 > 状态徽标 12.5 */
1247
+ .chat-thinking-summary {
1248
+ margin: 3px 0 0 0;
1249
+ font-size: 14.5px; line-height: 1.6; color: var(--ink);
1250
+ display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
1251
+ overflow: hidden; overflow-wrap: anywhere;
1252
+ }
1253
+ /* 正文为空时的兜底回复(模型以 thinking/工具收尾、无最终 result):步骤折叠后
1254
+ 用最后一步思考尾段顶上,避免「已完成」卡片下只剩空气泡。展示为正式答复样式
1255
+ (非摘要的 2 行省略),仅在折叠态渲染;步骤未折叠时由胶囊摘要承担,不重复出现 */
1256
+ .msg-fallback-result {
1257
+ margin: 0;
1258
+ font-size: 15.5px; line-height: 1.65; color: var(--ink);
1259
+ overflow-wrap: anywhere;
1260
+ }
1261
+ /* 思考步内的工具调用行(WorkBuddy 同款清爽风格):无卡片无背景,语义图标 + 中文短词 +
1262
+ 入参摘要单行显示(超长省略),与左侧竖条形成统一的时间线观感 */
1263
+ .chat-thinking-tool {
1264
+ display: flex; align-items: baseline; gap: 6px; flex-wrap: nowrap;
1265
+ margin: 3px 0; padding: 0;
1266
+ background: transparent; border: none; border-radius: 0;
1267
+ max-width: 100%;
1268
+ }
1269
+ .chat-thinking-tool-icon {
1270
+ flex: 0 0 auto; align-self: center;
1271
+ display: inline-flex; align-items: center; justify-content: center;
1272
+ width: 14px; height: 14px;
1273
+ color: var(--ink-soft); opacity: .85;
1274
+ }
1275
+ .chat-thinking-tool-name {
1276
+ flex: 0 0 auto; color: var(--ink-soft);
1277
+ font-size: 14px; white-space: nowrap;
1278
+ }
1279
+ .chat-thinking-tool-input {
1280
+ flex: 1 1 auto; min-width: 0;
1281
+ font-size: 14px; color: var(--ink-soft);
1282
+ font-variant-numeric: tabular-nums;
1283
+ overflow-wrap: anywhere;
1284
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
1285
+ }
1286
+ /* 工具执行状态徽标(豆包 Work 式就地更新):✓ 成功 / ✗ 失败(保持全链最小层级) */
1287
+ .chat-thinking-tool-status {
1288
+ flex: 0 0 auto; font-size: 12.5px; font-weight: 500; color: var(--ok);
1289
+ }
1290
+ .chat-thinking-tool-status--error { color: var(--danger); }
1291
+ /* 工具结果预览(仅展开详情内显示,受限 3 行省略;超长完整内容放 title):
1292
+ 成功=灰、失败=红。收起态不再展示工具执行结果(SDK 无工具摘要字段) */
1293
+ .chat-thinking-tool-result {
1294
+ flex-basis: 100%; min-width: 0;
1295
+ font-size: 14px; line-height: 1.5; color: var(--ink-soft);
1296
+ overflow-wrap: anywhere;
1297
+ display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
1298
+ overflow: hidden;
1299
+ }
1300
+ .chat-thinking-tool-result.is-error { color: var(--danger); }
1301
+ /* 失败工具胶囊:整行文字标红(is-error 与 is-running 互斥) */
1302
+ .chat-thinking-done-toggle.is-error .chat-thinking-tool-name,
1303
+ .chat-thinking-done-toggle.is-error .chat-thinking-tool-file,
1304
+ .chat-thinking-done-toggle.is-error .chat-thinking-tool-status { color: var(--danger); }
1305
+ /* 文件编辑增删徽标(Claude Code 风格「已编辑文件 xxx +N -M」):+N 绿 / -M 红,
1306
+ 文件路径超长省略(完整路径放 title),徽标右对齐 */
1307
+ .chat-thinking-tool-file {
1308
+ flex: 0 1 auto; min-width: 0;
1309
+ font-size: 14px; color: var(--ink-soft);
1310
+ font-variant-numeric: tabular-nums;
1311
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
1312
+ }
1313
+ .chat-edit-badges {
1314
+ flex: 0 0 auto; display: inline-flex; align-items: center; gap: 4px;
1315
+ font-size: 14px; font-weight: 500; line-height: 1;
1316
+ font-variant-numeric: tabular-nums;
1317
+ }
1318
+ .chat-edit-add { color: var(--ok); }
1319
+ .chat-edit-del { color: #e5484d; }
1320
+ /* 思考步内的中间小结文本(工具调用前模型输出的该步小结):同样去掉卡片,纯文本行
1321
+ (内容层级,与结果摘要同级) */
1322
+ .chat-thinking-step-text {
1323
+ margin: 3px 0; padding: 0;
1324
+ background: transparent; border: none; border-radius: 0;
1325
+ font-size: 14.5px; color: var(--ink-soft); line-height: 1.6;
1326
+ overflow-wrap: anywhere;
1327
+ }
1328
+
1329
+ /* control_browser 过程面板(动作/思考实时进 thinking;截图仅在右侧边栏预览,不再渲染进消息列表) */
1330
+ .chat-browser {
1331
+ margin: 0 0 8px; padding: 8px;
1332
+ background: var(--table-stripe); border: 1px solid var(--line); border-radius: 8px;
1333
+ }
1334
+ .chat-browser-head {
1335
+ display: flex; align-items: center; justify-content: flex-end;
1336
+ margin-bottom: 6px;
1337
+ }
1338
+ .chat-browser-status {
1339
+ padding: 2px 8px; border-radius: 999px;
1340
+ font-size: 12px; line-height: 1.5; color: #fff;
1341
+ background: rgba(31,39,51,.72);
1342
+ pointer-events: none;
1343
+ white-space: nowrap;
1344
+ }
1345
+ .chat-browser-status--running { background: rgba(31,39,51,.72); }
1346
+ .chat-browser-status--done { background: rgba(23,126,63,.85); }
1347
+ .chat-browser-status--error { background: rgba(172,43,43,.85); }
1348
+ .chat-browser-status--ask { background: rgba(176,108,15,.9); }
1349
+ /* 模型决策思考文本(browser thinking 事件;hy3 默认不输出,有则显示) */
1350
+ .chat-browser-think {
1351
+ margin: 0 0 8px; padding: 6px 9px;
1352
+ background: var(--field-bg); border: 1px solid var(--line); border-radius: 6px;
1353
+ font-size: 12px; color: var(--ink); line-height: 1.6;
1354
+ white-space: pre-wrap; overflow-wrap: anywhere;
1355
+ }
1356
+ .chat-browser-actions {
1357
+ display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 8px;
1358
+ }
1359
+ .chat-browser-action {
1360
+ padding: 1px 7px; border-radius: 4px;
1361
+ background: var(--field-soft); border: 1px solid var(--line);
1362
+ font-size: 12px; color: var(--ink-soft); font-variant-numeric: tabular-nums;
1363
+ }
1364
+ /* 运行中的实时动作流(活跃步直播,只列最近 4 条) */
1365
+ .chat-browser-live {
1366
+ display: flex; flex-direction: column; gap: 3px;
1367
+ }
1368
+ .chat-browser-live-item {
1369
+ display: flex; align-items: baseline; gap: 7px;
1370
+ font-size: 12px; line-height: 1.6; color: var(--ink);
1371
+ }
1372
+ .chat-browser-live-step {
1373
+ flex: 0 0 auto; min-width: 24px; text-align: right;
1374
+ font-variant-numeric: tabular-nums; color: var(--ink-soft);
1375
+ font-size: 12px;
1376
+ }
1377
+ .chat-browser-live-action { min-width: 0; overflow-wrap: anywhere; }
1378
+ .chat-browser-live-empty {
1379
+ padding: 6px 0; font-size: 12px; color: var(--ink-soft);
1380
+ }
1381
+ .chat-browser-ask {
1382
+ margin-bottom: 8px; padding: 8px;
1383
+ background: var(--field-bg); border: 1px solid var(--line); border-radius: 6px;
1384
+ }
1385
+ .chat-browser-ask-q {
1386
+ margin-bottom: 6px; font-size: 12px; color: var(--ink); line-height: 1.6;
1387
+ }
1388
+ .chat-browser-ask-row { display: flex; gap: 6px; }
1389
+ .chat-browser-ask-input {
1390
+ flex: 1 1 auto; min-width: 0;
1391
+ padding: 5px 9px; border: 1px solid var(--line); border-radius: 6px;
1392
+ font-size: 12px; color: var(--ink); background: var(--field-bg);
1393
+ outline: none;
1394
+ }
1395
+ .chat-browser-ask-input:focus { border-color: var(--line); }
1396
+ .chat-browser-ask-btn {
1397
+ flex: 0 0 auto; padding: 4px 12px; border-radius: 6px;
1398
+ background: var(--hover-strong); border: 1px solid var(--line);
1399
+ font-size: 12px; color: var(--ink); cursor: pointer;
1400
+ }
1401
+ .chat-browser-ask-btn:disabled { opacity: .45; cursor: default; }
1402
+ .chat-browser-summary {
1403
+ padding: 6px 9px; border-radius: 6px;
1404
+ background: var(--field-soft); border: 1px solid var(--line);
1405
+ font-size: 12px; color: var(--ink); line-height: 1.6;
1406
+ overflow-wrap: anywhere;
1407
+ }
1408
+
1409
+ /* 底部输入区:与 WorkBuddy 一致的圆角卡片,和聊天内容用浅背景区分。
1410
+ 外层 stack 统管背景/边框/圆角/阴影;内部 composer 与 queue 只负责内容排布。
1411
+ stack 自身可收缩,queue 超出时内部滚动;composer 永远贴底,不会被 queue 顶出视口。 */
1412
+ .chat-composer-stack {
1413
+ margin: 0 14px 14px;
1414
+ background: var(--panel);
1415
+ border: 1px solid var(--line);
1416
+ border-radius: 20px;
1417
+ box-shadow: 0 2px 12px rgba(31, 39, 51, .04);
1418
+ display: flex;
1419
+ flex-direction: column;
1420
+ flex: 0 1 auto;
1421
+ min-height: 0;
1422
+ /* 作为容器:工具条按输入卡片自身宽度自适应(中栏随拖拽/右栏开合实时变化,
1423
+ 视口媒体查询感知不到,故用容器查询)。见下方 @container composer。 */
1424
+ container-type: inline-size;
1425
+ container-name: composer;
1426
+ }
1427
+ .chat-composer {
1428
+ flex: 0 0 auto;
1429
+ padding: 14px 16px 10px;
1430
+ }
1431
+ .chat-composer textarea {
1432
+ width: 100%; display: block; resize: none; border: 0; background: transparent;
1433
+ padding: 6px 2px; font-size: 15px; font-family: inherit; color: var(--ink);
1434
+ line-height: 1.55; overflow-y: hidden; min-height: 48px;
1435
+ }
1436
+ .chat-composer textarea:focus { outline: none; }
1437
+ .chat-composer textarea::placeholder { color: var(--ink-soft); opacity: .75; }
1438
+
1439
+ /* 输入区容器:内聚 composer + queue 的 stack,以及可选的 notify banner。
1440
+ 整体锚定在 chat-main-inner 底部:queue 向上生长时输入框不会被顶出视口,
1441
+ 消息列表(.chat-body)的可用高度会相应缩小。 */
1442
+ .chat-input-zone {
1443
+ position: relative;
1444
+ display: flex; flex-direction: column;
1445
+ justify-content: flex-end;
1446
+ flex: 0 1 auto;
1447
+ min-height: 0;
1448
+ }
1449
+
1450
+ /* ---- 选择工作空间(WB 风格,仅新建任务空态显示;进入对话后隐藏)---- */
1451
+ /* 定位上下文:按钮 + 向上弹出的项目菜单 */
1452
+ .chat-ws-anchor { position: relative; display: inline-flex; flex: 0 0 auto; }
1453
+ /* 下拉按钮:文件图标 + 当前项目名/提示 + 下拉箭头(与 WB 输入区左下角工具一致) */
1454
+ .chat-ws-btn {
1455
+ display: inline-flex; align-items: center; gap: 6px;
1456
+ border: 1px solid var(--line); background: transparent;
1457
+ color: var(--ink-soft); border-radius: 8px;
1458
+ font-size: 12.5px; line-height: 1; padding: 6px 10px;
1459
+ cursor: pointer; transition: all .15s ease;
1460
+ }
1461
+ .chat-ws-btn:hover, .chat-ws-btn.is-open {
1462
+ color: var(--ink); background: var(--hover);
1463
+ }
1464
+ .chat-ws-btn:disabled { opacity: .55; cursor: default; }
1465
+ .chat-ws-btn-icon { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
1466
+ .chat-ws-btn-label {
1467
+ max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
1468
+ font-weight: 500;
1469
+ }
1470
+ .chat-ws-btn-caret { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; opacity: .7; }
1471
+
1472
+ /* 项目菜单 popup:从按钮向上弹出;backdrop 承接外部点击关闭 */
1473
+ .chat-ws-pop {
1474
+ position: absolute; left: 0; bottom: calc(100% + 8px);
1475
+ z-index: 80;
1476
+ }
1477
+ .chat-ws-pop-backdrop { position: fixed; inset: 0; z-index: -1; }
1478
+ .chat-ws-pop-inner {
1479
+ width: 300px;
1480
+ background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
1481
+ box-shadow: var(--shadow);
1482
+ padding: 8px; display: flex; flex-direction: column;
1483
+ }
1484
+ /* 当前项目(顶部):路径 + 移除 */
1485
+ .chat-ws-current {
1486
+ display: flex; align-items: center; gap: 8px;
1487
+ padding: 8px 10px; margin-bottom: 4px;
1488
+ border: 1px dashed var(--line); border-radius: 8px; background: var(--field-soft, transparent);
1489
+ }
1490
+ .chat-ws-current-path {
1491
+ flex: 1 1 auto; min-width: 0;
1492
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
1493
+ font-size: 12px; color: var(--ink);
1494
+ font-family: ui-monospace, Consolas, "Courier New", monospace;
1495
+ }
1496
+ .chat-ws-act {
1497
+ flex: 0 0 auto;
1498
+ border: 1px solid var(--line); background: transparent;
1499
+ color: var(--ink-soft); border-radius: 7px;
1500
+ font-size: 12px; padding: 3px 10px; cursor: pointer;
1501
+ transition: all .15s ease;
1502
+ }
1503
+ .chat-ws-act:hover { color: var(--ink); background: var(--hover); }
1504
+ .chat-ws-act:disabled { opacity: .55; cursor: default; }
1505
+ .chat-ws-act--clear:hover { color: #d23f4c; border-color: rgba(210, 63, 76, .4); background: rgba(210, 63, 76, .08); }
1506
+ /* 分组标题 */
1507
+ .chat-ws-pop-label {
1508
+ padding: 6px 10px 4px; font-size: 11px; font-weight: 600;
1509
+ color: var(--ink-soft); letter-spacing: .04em; text-transform: uppercase;
1510
+ }
1511
+ .chat-ws-pop-empty { padding: 10px; font-size: 12.5px; color: var(--ink-soft); }
1512
+ /* 已有项目条目 */
1513
+ .chat-ws-pop-item {
1514
+ display: flex; align-items: center; gap: 8px;
1515
+ width: 100%; border: none; background: transparent;
1516
+ color: var(--ink); font-size: 13px; text-align: left;
1517
+ padding: 8px 10px; border-radius: 8px; cursor: pointer;
1518
+ transition: background .12s ease;
1519
+ }
1520
+ .chat-ws-pop-item:hover { background: var(--hover); }
1521
+ .chat-ws-pop-item.is-active { background: var(--brand-soft); color: var(--brand-dark); }
1522
+ .chat-ws-pop-item-icon { width: 15px; height: 15px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; opacity: .75; }
1523
+ .chat-ws-pop-item-name {
1524
+ flex: 1 1 auto; min-width: 0;
1525
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
1526
+ }
1527
+ .chat-ws-pop-item-check { flex: 0 0 auto; color: var(--brand); font-size: 13px; font-weight: 700; }
1528
+ .chat-ws-pop-divider { height: 1px; background: var(--line); margin: 6px 2px; }
1529
+ /* 底部操作:新建项目 / 打开本地文件夹 */
1530
+ .chat-ws-pop-act {
1531
+ display: flex; align-items: center; gap: 9px;
1532
+ width: 100%; border: none; background: transparent;
1533
+ color: var(--ink); font-size: 13px; text-align: left;
1534
+ padding: 8px 10px; border-radius: 8px; cursor: pointer;
1535
+ transition: background .12s ease;
1536
+ }
1537
+ .chat-ws-pop-act:hover { background: var(--hover); }
1538
+ .chat-ws-pop-act:disabled { opacity: .55; cursor: default; }
1539
+ .chat-ws-pop-act-icon {
1540
+ width: 17px; flex: 0 0 auto;
1541
+ display: inline-flex; align-items: center; justify-content: center;
1542
+ color: var(--ink-soft);
1543
+ }
1544
+ .chat-ws-pop-act-icon svg {
1545
+ width: 15px; height: 15px;
1546
+ fill: none; stroke: currentColor; stroke-width: 1.7;
1547
+ stroke-linecap: round; stroke-linejoin: round;
1548
+ }
1549
+
1550
+ /* ---- 侧边栏:会话按项目分组(WB 列表样式)---- */
1551
+ .chat-sidebar-group { margin-top: 2px; }
1552
+ .chat-sidebar-group + .chat-sidebar-group { margin-top: 10px; }
1553
+ /* 项目组标题:高度与会话 item 一致(padding 6px + 行高 26px + 6px = 38px),
1554
+ 左侧可折叠箭头,hover 时右侧出现「+ 新建会话」「⋯ 项目菜单」 */
1555
+ .chat-sidebar-group-title {
1556
+ display: flex; align-items: center; gap: 6px;
1557
+ padding: 6px 10px; box-sizing: border-box; height: 38px;
1558
+ font-size: 13px; font-weight: 600; color: var(--ink-soft);
1559
+ letter-spacing: .03em;
1560
+ border-radius: 9px; cursor: context-menu; user-select: none;
1561
+ transition: background .15s ease, color .15s ease;
1562
+ }
1563
+ .chat-sidebar-group-title:hover { background: var(--hover); color: var(--ink); }
1564
+ /* 折叠箭头:展开默认 ▼(可收起),折叠 is-collapsed 旋转回 ▶(可展开) */
1565
+ .chat-sidebar-group-chevron {
1566
+ flex: 0 0 auto; width: 18px; height: 18px;
1567
+ display: inline-flex; align-items: center; justify-content: center;
1568
+ padding: 0; border: none; border-radius: 5px; cursor: pointer;
1569
+ background: transparent; color: var(--ink-soft);
1570
+ transition: transform .15s ease, color .15s ease;
1571
+ }
1572
+ .chat-sidebar-group-chevron svg {
1573
+ width: 13px; height: 13px;
1574
+ fill: none; stroke: currentColor; stroke-width: 2;
1575
+ stroke-linecap: round; stroke-linejoin: round;
1576
+ transform: rotate(90deg);
1577
+ }
1578
+ .chat-sidebar-group-title.is-collapsed .chat-sidebar-group-chevron svg { transform: rotate(0deg); }
1579
+ .chat-sidebar-group-title:hover .chat-sidebar-group-chevron { color: var(--ink); }
1580
+ .chat-sidebar-group-chevron:hover { color: var(--ink); }
1581
+ /* 目录分组前的文件夹图标(WorkBuddy 同款:描边线性图标,与组标题文字同色) */
1582
+ .chat-sidebar-group-icon {
1583
+ flex: 0 0 auto; width: 14px; height: 14px;
1584
+ fill: none; stroke: currentColor; stroke-width: 1.8;
1585
+ stroke-linecap: round; stroke-linejoin: round;
1586
+ }
1587
+ /* 组内会话缩进对齐图标右侧(图标 14px + gap 6px + 标题 padding-left 10px ≈ 26px) */
1588
+ .chat-sidebar-group .chat-sidebar-item { padding-left: 26px; }
1589
+ .chat-sidebar-group-name {
1590
+ min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
1591
+ }
1592
+ .chat-sidebar-group-count {
1593
+ flex: 0 0 auto;
1594
+ font-size: 10.5px; font-weight: 700; color: var(--ink-soft);
1595
+ background: var(--hover, rgba(128, 128, 128, .15));
1596
+ border-radius: 99px; padding: 1px 7px;
1597
+ }
1598
+ /* hover 时计数隐藏、位置让给右侧操作区(+ / ⋯),避免挤压标题 */
1599
+ .chat-sidebar-group-title:hover .chat-sidebar-group-count { display: none; }
1600
+ .chat-sidebar-group-actions { display: none; align-items: center; gap: 2px; flex: 0 0 auto; margin-left: auto; }
1601
+ .chat-sidebar-group-title:hover .chat-sidebar-group-actions { display: inline-flex; }
1602
+ .chat-sidebar-group-act {
1603
+ display: inline-flex; align-items: center; justify-content: center;
1604
+ width: 22px; height: 22px; border-radius: 50%;
1605
+ border: none; background: transparent; color: var(--ink-soft); cursor: pointer;
1606
+ transition: color .15s ease, background .15s ease;
1607
+ }
1608
+ .chat-sidebar-group-act svg {
1609
+ width: 15px; height: 15px;
1610
+ fill: none; stroke: currentColor; stroke-width: 1.8;
1611
+ stroke-linecap: round; stroke-linejoin: round;
1612
+ }
1613
+ .chat-sidebar-group-act:hover { color: var(--ink); }
1614
+ .chat-sidebar-group-act.chat-sidebar-group-add {
1615
+ width: 26px; height: 26px;
1616
+ }
1617
+ .chat-sidebar-group-act.chat-sidebar-group-add svg {
1618
+ width: 17px; height: 17px;
1619
+ }
1620
+ .chat-sidebar-group-more.is-open { color: var(--ink); background: var(--line); }
1621
+ /* 项目分组菜单:固定定位浮层,锚定右键位置或 ⋯ 按钮下方;浅/深色主题自适应 */
1622
+ .chat-sidebar-project-menu {
1623
+ position: fixed; z-index: 300;
1624
+ min-width: 150px; padding: 5px;
1625
+ border: 1px solid var(--line); border-radius: 10px;
1626
+ background: var(--panel, var(--bg)); box-shadow: 0 8px 24px rgba(0,0,0,.22);
1627
+ display: flex; flex-direction: column; gap: 2px;
1628
+ }
1629
+ .chat-sidebar-project-menu button {
1630
+ display: flex; align-items: center; gap: 9px; width: 100%;
1631
+ padding: 7px 9px; border: none; border-radius: 7px;
1632
+ background: transparent; color: var(--ink); font-size: 13px; cursor: pointer; text-align: left;
1633
+ }
1634
+ .chat-sidebar-project-menu button:hover { background: var(--hover); }
1635
+ .chat-sidebar-project-menu button svg {
1636
+ width: 15px; height: 15px; flex: 0 0 auto;
1637
+ fill: none; stroke: var(--ink-soft); stroke-width: 1.8;
1638
+ stroke-linecap: round; stroke-linejoin: round;
1639
+ }
1640
+ .chat-sidebar-project-menu button:hover svg { stroke: var(--ink); }
1641
+ /* 「删除全部会话」危险项:文字/图标转危险色 */
1642
+ .chat-sidebar-project-menu button.is-danger,
1643
+ .chat-sidebar-project-menu button.is-danger:hover { color: var(--danger, #e5484d); }
1644
+ .chat-sidebar-project-menu button.is-danger svg,
1645
+ .chat-sidebar-project-menu button.is-danger:hover svg { stroke: var(--danger, #e5484d); }
1646
+
1647
+ /* 会话条目长按/⋯ 菜单:portal 到 body 渲染(脱离侧栏抽屉 overflow/层叠上下文);
1648
+ z-index 抬到 1100+,确保盖住移动端抽屉(200)、遮罩(150)、topbar(220) 与各类浮层 */
1649
+ .chat-sidebar-session-menu { z-index: 1100; }
1650
+
1651
+
1652
+
1653
+ /* Shimmer 扫光:核心规则 = 最后一个展示给用户的提示持续播放,表明 Agent 仍在干活、没有卡死。
1654
+ 命中两处(均由 ChatWindow 流式判定触发):
1655
+ ① 活动步「正在思考…」文字(.chat-thinking-head .chat-thinking-label);
1656
+ ② 正在运行的工具行:状态与命令在同一个文本元素中,共用一条扫光带,覆盖可见整行。
1657
+ 一条高光带沿文字从左往右循环掠过;基础色=--shine-base、高光=--shine(见 :root 与 [data-theme=dark])。
1658
+ 亮色模式:静止浅灰 → 扫过填深墨(高对比、明显);暗色模式:静止浅灰 → 扫过填白(深底亮闪)。 */
1659
+ /* 节奏对标 WorkBuddy:快速扫过 → 稍作停留 → 再重复(干脆利落、有顿挫)。
1660
+ 高光带收窄(占文字~0.6倍宽) + background-size:300% + no-repeat,使扫描能「彻底扫出文字」。
1661
+ 方向:左→右。0% 高光带在左侧外(文字零光);前 ~40% 线性匀速扫到右侧外(快速掠过整段文字);
1662
+ 40%~100% 定格(高光已完全离开文字,只显示纯文字=停留);循环回 0% 时两端文字均零光,无缝衔接。 */
1663
+ @keyframes chatTextShine {
1664
+ 0% { background-position: 90% 0; }
1665
+ 40% { background-position: 10% 0; }
1666
+ 100% { background-position: 10% 0; }
1667
+ }
1668
+ .chat-thinking-head .chat-thinking-label,
1669
+ .chat-thinking-done-toggle.is-running .chat-thinking-tool-name,
1670
+ .chat-thinking-done-toggle.is-running .chat-thinking-tool-file,
1671
+ .chat-thinking-done-toggle.is-running .chat-thinking-browser-label {
1672
+ position: relative;
1673
+ /* 收窄高光带(40%→60%,占文字~0.6倍宽),两侧留足余量,保证带子能完全扫出文字。
1674
+ 基础色用 --shine-base(浅色=浅灰、深色=浅灰),高光用 --shine(浅色=深墨、深色=白)。 */
1675
+ background: linear-gradient(
1676
+ 90deg,
1677
+ var(--shine-base) 0%,
1678
+ var(--shine-base) 40%,
1679
+ var(--shine) 50%,
1680
+ var(--shine-base) 60%,
1681
+ var(--shine-base) 100%
1682
+ );
1683
+ background-size: 300% 100%;
1684
+ background-repeat: no-repeat;
1685
+ background-position: 90% 0;
1686
+ -webkit-background-clip: text;
1687
+ background-clip: text;
1688
+ color: transparent;
1689
+ /* linear = 匀速干脆不拖泥带水;2.1s 内前 40%(~0.84s) 快速扫过、后 60%(~1.26s) 停留。
1690
+ 停点时高光已完全离开文字(文字零光),解决「停留发生在半途、文字还带光」的怪感。 */
1691
+ animation: chatTextShine 2.1s linear infinite;
1692
+ }
1693
+ /* 工具调用/浏览器胶囊文字:基础色与思考提示统一 --shine-base(见 :root 与 [data-theme=dark])。
1694
+ 注意:必须用 background-image(longhand)而非 background 简写——
1695
+ 简写会把 background-clip 重置为 border-box,导致上方规则的 background-clip:text 失效,
1696
+ 文字透明后渐变铺满元素矩形 → 变成「灰色渐变条」而非文字扫光。 */
1697
+ .chat-thinking-done-toggle.is-running .chat-thinking-tool-name,
1698
+ .chat-thinking-done-toggle.is-running .chat-thinking-tool-file,
1699
+ .chat-thinking-done-toggle.is-running .chat-thinking-browser-label {
1700
+ background-image: linear-gradient(
1701
+ 90deg,
1702
+ var(--shine-base) 0%,
1703
+ var(--shine-base) 40%,
1704
+ var(--shine) 50%,
1705
+ var(--shine-base) 60%,
1706
+ var(--shine-base) 100%
1707
+ );
1708
+ }
1709
+
1710
+ /* 通知权限引导条(仅 Web 且通知被浏览器拒绝时出现;悬浮于输入卡片上方) */
1711
+ .chat-notify-banner {
1712
+ position: absolute; left: 14px; right: 14px; bottom: calc(100% + 8px);
1713
+ display: flex; align-items: center; gap: 8px;
1714
+ padding: 8px 12px; border-radius: 10px;
1715
+ background: var(--panel); border: 1px solid var(--line);
1716
+ box-shadow: 0 4px 16px rgba(20, 27, 38, .10);
1717
+ z-index: 31;
1718
+ }
1719
+ .chat-notify-banner-icon { width: 15px; height: 15px; flex: 0 0 auto; fill: none; stroke: var(--brand); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
1720
+ .chat-notify-banner-text { flex: 1 1 auto; font-size: 12px; color: var(--ink); line-height: 1.5; }
1721
+ .chat-notify-banner-link {
1722
+ flex: 0 0 auto; font-size: 12px; font-weight: 600; color: var(--brand);
1723
+ text-decoration: none; white-space: nowrap;
1724
+ }
1725
+ .chat-notify-banner-close {
1726
+ flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
1727
+ width: 22px; height: 22px; border-radius: 6px;
1728
+ border: none; background: transparent; color: var(--ink-soft); cursor: pointer;
1729
+ }
1730
+ .chat-notify-banner-close svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
1731
+
1732
+ /* 待发消息队列(steer):与输入卡片共用 stack 面板,视觉上连成一体。
1733
+ 队列只保留顶部圆角(与外层 stack 一致),内部条目之间用 1px 分隔线
1734
+ (最后一条无线),条目本身不再带独立背景/边框/圆角。
1735
+ overflow 用于裁剪 item hover 背景,避免超出 stack 圆角。 */
1736
+ .chat-queue {
1737
+ flex: 0 1 auto;
1738
+ min-height: 0;
1739
+ display: flex; flex-direction: column; gap: 0;
1740
+ max-height: 42vh; overflow: hidden auto;
1741
+ border-radius: 20px 20px 0 0;
1742
+ /* 与 composer 同处一个 stack 面板,但用极微弱的深色叠加让 queue 区可辨识,
1743
+ 提示用户“上方是待插队消息,下方是主输入区”,对比度刻意保持克制 */
1744
+ background: rgba(0, 0, 0, 0.06);
1745
+ }
1746
+ .chat-queue-item {
1747
+ display: flex; align-items: center; gap: 8px;
1748
+ padding: 10px 12px; min-width: 0;
1749
+ border-bottom: 1px solid var(--line);
1750
+ cursor: grab;
1751
+ }
1752
+ .chat-queue-item:last-child { border-bottom: none; }
1753
+ .chat-queue-item:hover { background: var(--hover); }
1754
+ .chat-queue-item:active { cursor: grabbing; }
1755
+ .chat-queue-grip {
1756
+ flex: none; display: grid; place-items: center;
1757
+ width: 16px; height: 20px; color: var(--ink-soft);
1758
+ }
1759
+ .chat-queue-grip svg { width: 12px; height: 16px; fill: currentColor; }
1760
+ .chat-queue-text {
1761
+ flex: 1; min-width: 0; font-size: 13px; color: var(--ink);
1762
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
1763
+ }
1764
+ .chat-queue-act {
1765
+ flex: none; width: 26px; height: 26px; padding: 0; display: grid; place-items: center;
1766
+ border: none; border-radius: 7px; background: transparent; color: var(--ink-soft); cursor: pointer;
1767
+ }
1768
+ .chat-queue-act svg {
1769
+ width: 16px; height: 16px;
1770
+ fill: none; stroke: currentColor; stroke-width: 1.7;
1771
+ stroke-linecap: round; stroke-linejoin: round;
1772
+ }
1773
+ .chat-queue-notice { font-size: 12px; color: var(--ink-soft); padding: 7px 12px 9px; }
1774
+
1775
+ /* 有附件时:附件条在卡片内 textarea 上方;textarea 压缩为一行、卡片上内边距收窄,
1776
+ 合计与无附件时的总高度一致,聊天区高度与页面均不抖动。 */
1777
+ .chat-composer.has-attachment { padding-top: 8px; }
1778
+ .chat-composer.has-attachment textarea { min-height: 20px; }
1779
+ .chat-composer.has-attachment .chat-attachment-wrap { margin-bottom: 4px; }
1780
+
1781
+ /* 场景分类选择按钮组:输入卡片外部的上一行,按场景路由到不同钉钉文档 */
1782
+ .chat-scene-bar { padding: 10px 14px 4px; }
1783
+ .chat-scenes { display: flex; flex-wrap: wrap; gap: 8px; }
1784
+ .chat-scene {
1785
+ height: 30px; padding: 0 14px; border-radius: 999px;
1786
+ border: 1px solid var(--line); background: var(--field-bg); color: var(--ink-soft);
1787
+ font-size: 13px; cursor: pointer; line-height: 1; white-space: nowrap;
1788
+ transition: all .12s ease;
1789
+ }
1790
+ .chat-scene.is-active {
1791
+ border-color: var(--brand); background: var(--brand-soft); color: var(--brand-dark);
1792
+ font-weight: 600;
1793
+ }
1794
+
1795
+ .chat-toolbar { display: flex; align-items: center; gap: 4px; padding-top: 4px; }
1796
+
1797
+ /* 上传附件:仅 + 图标。hover 背景为圆形 */
1798
+ .chat-tool {
1799
+ width: 36px; height: 36px; padding: 0;
1800
+ display: flex; align-items: center; justify-content: center;
1801
+ border: none; border-radius: 50%; background: transparent;
1802
+ color: var(--ink-soft); cursor: pointer;
1803
+ }
1804
+ .chat-tool:disabled { opacity: .45; cursor: default; }
1805
+ .chat-tool svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
1806
+
1807
+ /* AgentApp 首屏加载占位:全屏居中黑白灰 spinner */
1808
+ .agent-kit-loading {
1809
+ position: fixed; inset: 0;
1810
+ display: flex; align-items: center; justify-content: center;
1811
+ background: var(--bg);
1812
+ }
1813
+ .agent-kit-loading::after {
1814
+ content: "";
1815
+ width: 22px; height: 22px; border-radius: 50%;
1816
+ border: 2px solid var(--spin-edge); border-top-color: var(--spin-top);
1817
+ animation: agentKitLoadingSpin .7s linear infinite;
1818
+ }
1819
+ @keyframes agentKitLoadingSpin { to { transform: rotate(360deg); } }
1820
+
1821
+ /* 语音输入:图标按钮,放在发送按钮旁边;录音时仅变为红色停止图标。hover 背景为圆形 */
1822
+ .chat-voice {
1823
+ width: 36px; height: 36px; padding: 0;
1824
+ display: flex; align-items: center; justify-content: center;
1825
+ border: none; border-radius: 50%; background: transparent;
1826
+ color: var(--ink-soft); cursor: pointer;
1827
+ }
1828
+ .chat-voice-icon { width: 22px; height: 22px; display: grid; place-items: center; color: inherit; }
1829
+ .chat-voice-icon svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
1830
+ .chat-voice-icon--stop svg { fill: currentColor; stroke: none; }
1831
+ .chat-voice:disabled { opacity: .45; cursor: default; }
1832
+ /* 录音进行中:按钮变红(停止态),时长提示在按钮左边 */
1833
+ .chat-voice.recording { background: var(--danger-soft); color: var(--danger); }
1834
+ .chat-voice.connecting { justify-content: center; }
1835
+ .chat-voice-spinner { width: 17px; height: 17px; box-sizing: border-box; border: 2px solid #c7d7ea; border-top-color: #2563eb; border-radius: 50%; animation: chatVoiceSpin .75s linear infinite; }
1836
+ /* 录音时长 / 10 分钟上限提示:位于语音按钮左侧 */
1837
+ .chat-voice-hint {
1838
+ display: inline-flex; align-items: center; gap: 6px; height: 36px; padding: 0 6px;
1839
+ font-size: 12px; font-weight: 500; color: var(--ink-soft);
1840
+ font-variant-numeric: tabular-nums; letter-spacing: .3px;
1841
+ }
1842
+ .chat-voice-dot { width: 8px; height: 8px; border-radius: 50%; background: #dc2626; animation: chatVoicePulse 1s ease-in-out infinite; }
1843
+ @keyframes chatVoicePulse { 50% { opacity: .25; } }
1844
+ @keyframes chatVoiceSpin { to { transform: rotate(360deg); } }
1845
+
1846
+ .chat-tool-spacer { flex: 1; }
1847
+ /* 上下文容量环形指示(输入框右侧、模型名左侧,对标 WorkBuddy):圆环 + 百分比,三档状态色 */
1848
+ .ctx-meter {
1849
+ display: inline-flex; align-items: center; gap: 5px;
1850
+ flex: 0 0 auto; padding: 4px 8px;
1851
+ border-radius: 8px; cursor: default;
1852
+ font-size: 12px; line-height: 1; color: var(--ink-soft);
1853
+ position: relative;
1854
+ }
1855
+ .ctx-meter svg { width: 16px; height: 16px; display: block; }
1856
+ .ctx-meter-track { fill: none; stroke: color-mix(in srgb, var(--line) 40%, transparent); stroke-width: 1.5; }
1857
+ .ctx-meter-bar {
1858
+ fill: none; stroke-width: 1.5; stroke-linecap: round;
1859
+ transform: rotate(-90deg); transform-origin: 50% 50%;
1860
+ transition: stroke-dashoffset .3s ease, stroke .3s ease;
1861
+ }
1862
+ /* 圆环只作容量提示:未用部分浅、已用部分深,但整体刻意降饱和,不抢主线视线 */
1863
+ .ctx-meter--ok .ctx-meter-bar { stroke: color-mix(in srgb, var(--ink-soft) 40%, transparent); }
1864
+ .ctx-meter--warn .ctx-meter-bar { stroke: color-mix(in srgb, var(--ink-soft) 70%, transparent); }
1865
+ .ctx-meter--danger .ctx-meter-bar { stroke: var(--ink-soft); }
1866
+ .ctx-meter--neutral .ctx-meter-bar { stroke: color-mix(in srgb, var(--ink-soft) 45%, transparent); }
1867
+ .ctx-meter.is-compacting { animation: ctx-meter-pulse 1s ease infinite; }
1868
+ @keyframes ctx-meter-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }
1869
+ /* hover 详情浮层(对标 WorkBuddy):已用百分比 · 已用/上限 tokens;深色浮层向上弹出 */
1870
+ .ctx-meter::after {
1871
+ content: attr(data-tip);
1872
+ position: absolute;
1873
+ bottom: calc(100% + 10px);
1874
+ left: 50%;
1875
+ transform: translateX(-50%);
1876
+ white-space: nowrap;
1877
+ padding: 5px 9px;
1878
+ border-radius: 7px;
1879
+ background: var(--bg-float, #fdfdfd);
1880
+ color: var(--ink, #1a1a1a);
1881
+ border: 1px solid var(--line, #e3e8f0);
1882
+ font-size: 12px;
1883
+ line-height: 1.5;
1884
+ box-shadow: 0 4px 14px rgb(0 0 0 / .15);
1885
+ opacity: 0;
1886
+ pointer-events: none;
1887
+ transition: opacity .12s ease;
1888
+ z-index: 40;
1889
+ }
1890
+ :root[data-theme="dark"] .ctx-meter::after {
1891
+ background: var(--bg-float, #212121);
1892
+ color: #f0f0f0;
1893
+ border-color: #3a3a3a;
1894
+ }
1895
+ .ctx-meter:hover::after { opacity: 1; }
1896
+ /* 中栏很窄时(窗口收窄 / 右栏拖宽挤压中栏):工具条放不下模型选择按钮,
1897
+ 隐藏它避免挤压导致输入卡片内容溢出、显示错乱。
1898
+ 用容器查询(容器 = .chat-composer-stack)而非视口媒体查询:
1899
+ 中栏宽度随右栏拖拽实时变化,视口宽度不变,媒体查询无法跟随。 */
1900
+ @container composer (max-width: 360px) {
1901
+ .chat-model-picker { display: none; }
1902
+ }
1903
+ /* 模型选择下拉(现代风格,向上弹出):触发按钮与 + 号/语音按钮一致——无背景无边框,
1904
+ hover 才出现浅灰胶囊圆角背景;菜单为圆角浮层,黑白灰系 */
1905
+ .chat-model-picker { position: relative; flex: 0 0 auto; }
1906
+ .chat-model-picker-btn {
1907
+ display: inline-flex; align-items: center; gap: 4px;
1908
+ height: 36px; padding: 0 12px;
1909
+ border: none; background: transparent; border-radius: 999px;
1910
+ color: var(--ink); font-size: 13px; font-family: inherit; cursor: pointer;
1911
+ transition: background .15s ease;
1912
+ }
1913
+ .chat-model-picker-btn.open { background: var(--hover); }
1914
+ .chat-model-picker-name {
1915
+ max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
1916
+ }
1917
+ .chat-model-picker-chev {
1918
+ width: 14px; height: 14px; flex: 0 0 auto;
1919
+ fill: none; stroke: var(--ink-soft); stroke-width: 1.6;
1920
+ stroke-linecap: round; stroke-linejoin: round;
1921
+ transition: transform .15s ease;
1922
+ }
1923
+ .chat-model-picker-btn.open .chat-model-picker-chev { transform: rotate(180deg); }
1924
+ .chat-model-picker-menu {
1925
+ position: absolute; right: 0; bottom: calc(100% + 6px);
1926
+ min-width: 200px; margin: 0; padding: 4px; list-style: none;
1927
+ background: var(--overlay-bg); border: 1px solid var(--line); border-radius: 12px;
1928
+ box-shadow: 0 8px 24px rgba(0, 0, 0, .08);
1929
+ z-index: 30;
1930
+ max-height: 240px; overflow-y: auto;
1931
+ }
1932
+ .chat-model-picker-item {
1933
+ display: flex; align-items: center; justify-content: space-between; gap: 12px;
1934
+ padding: 9px 12px; border-radius: 8px;
1935
+ font-size: 13px; line-height: 1.3; color: var(--ink);
1936
+ cursor: pointer; white-space: nowrap;
1937
+ transition: background .12s ease;
1938
+ }
1939
+ .chat-model-picker-item.selected { color: var(--ink); font-weight: 600; }
1940
+ /* 下拉项:名称(主)+ id(副,models.json 自定义模型时附显示) */
1941
+ .chat-model-picker-item-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
1942
+ .chat-model-picker-item-name { max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1943
+ .chat-model-picker-item-id {
1944
+ font-size: 11px; line-height: 1.2; color: var(--ink-soft);
1945
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1946
+ max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
1947
+ }
1948
+ /* 悬停信息卡:展示当前模型详情(provider/上下文窗口/能力),不展示 apiKey */
1949
+ .chat-model-picker-detail {
1950
+ position: absolute; right: 0; bottom: calc(100% + 8px);
1951
+ min-width: 260px; max-width: 340px; padding: 12px 14px;
1952
+ background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
1953
+ box-shadow: 0 10px 30px rgba(0,0,0,.20);
1954
+ pointer-events: none; z-index: 30;
1955
+ font-size: 12.5px; line-height: 1.4; color: var(--ink);
1956
+ }
1957
+ .chat-model-picker-detail-head { display: flex; flex-direction: column; gap: 3px; margin-bottom: 10px; }
1958
+ .chat-model-picker-detail-name { font-weight: 600; font-size: 13.5px; }
1959
+ .chat-model-picker-detail-id {
1960
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1961
+ font-size: 11px; color: var(--ink-soft); word-break: break-all;
1962
+ }
1963
+ .chat-model-picker-detail-rows { display: flex; flex-direction: column; gap: 7px; }
1964
+ .chat-model-picker-detail-row { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; }
1965
+ .chat-model-picker-detail-label { color: var(--ink-soft); flex: 0 0 auto; }
1966
+ .chat-model-picker-detail-value { text-align: right; word-break: break-all; }
1967
+ .chat-model-picker-check {
1968
+ width: 14px; height: 14px; flex: 0 0 auto;
1969
+ fill: none; stroke: currentColor; stroke-width: 2;
1970
+ stroke-linecap: round; stroke-linejoin: round;
1971
+ }
1972
+ /* 语音/附件报错:底部工具栏内、靠近右侧图标组 */
1973
+ .chat-hint--error {
1974
+ display: inline-flex; align-items: center;
1975
+ color: var(--danger); background: var(--danger-soft); border: 1px solid rgba(240, 97, 109, .35);
1976
+ border-radius: 8px; padding: 5px 8px; font-size: 11.5px; line-height: 1.3;
1977
+ max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
1978
+ }
1979
+
1980
+ .chat-send {
1981
+ width: 36px; height: 36px; border: none; border-radius: 50%;
1982
+ background: var(--send-bg); color: var(--send-ink); cursor: pointer;
1983
+ display: flex; align-items: center; justify-content: center;
1984
+ box-shadow: 0 4px 10px rgba(0,0,0,.15);
1985
+ }
1986
+ /* 无输入时发送按钮禁用:禁止光标;hover 不改变外观(避免与可发送态混淆) */
1987
+ .chat-send:disabled { opacity: .45; cursor: not-allowed; transform: none; }
1988
+ .chat-send svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
1989
+ /* 图片附件 + 疑似不支持图片的模型:输入卡片内的黄色提醒条(仅提示,不拦截发送) */
1990
+ .chat-vision-warning {
1991
+ margin-top: 6px; font-size: 12px; line-height: 1.55; color: #92400e;
1992
+ background: #fef3c7; border: 1px solid #fde68a; border-radius: 8px;
1993
+ padding: 6px 10px; max-width: 100%;
1994
+ }
1995
+ /* 附件条(输入卡片内、textarea 上方):默认显示类型图标(图片/文件),
1996
+ hover 时移除按钮替换图标位(与 WorkBuddy 一致) */
1997
+ .chat-attachment-wrap { position: relative; display: inline-block; max-width: fit-content; }
1998
+ .chat-attachment {
1999
+ display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--ink);
2000
+ background: var(--brand-soft); border: 1px solid var(--brand-line); border-radius: 8px;
2001
+ padding: 4px 8px; max-width: fit-content;
2002
+ }
2003
+ /* 附件图标位:默认显示类型图标(图片/文件),hover 时被移除按钮替换(与 WorkBuddy 一致) */
2004
+ .chat-attachment-icon-wrap { position: relative; width: 18px; height: 18px; flex: none; display: grid; place-items: center; }
2005
+ .chat-attachment-icon { display: grid; place-items: center; width: 18px; height: 18px; color: var(--brand-dark); transition: opacity .12s ease; }
2006
+ .chat-attachment-icon svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
2007
+ .chat-attachment-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2008
+ .chat-attachment-remove {
2009
+ position: absolute; inset: 0; display: grid; place-items: center;
2010
+ border: none; background: transparent; color: var(--danger); cursor: pointer;
2011
+ font-size: 17px; line-height: 1; padding: 0; border-radius: 50%;
2012
+ opacity: 0; transform: scale(.6); transition: opacity .12s ease, transform .12s ease;
2013
+ }
2014
+ /* 附件 hover 图片预览:浮层出现在附件条上方(仅图片附件渲染,纯 CSS 控制显隐) */
2015
+ .chat-attachment-preview {
2016
+ position: absolute; left: 0; bottom: calc(100% + 6px);
2017
+ padding: 8px; background: var(--overlay-bg); border: 1px solid var(--line); border-radius: 10px;
2018
+ box-shadow: 0 4px 16px rgba(15, 23, 42, .12);
2019
+ opacity: 0; visibility: hidden; transform: translateY(4px);
2020
+ transition: opacity .12s ease, transform .12s ease, visibility .12s;
2021
+ z-index: 30; pointer-events: none; max-width: 436px;
2022
+ }
2023
+ .chat-attachment-preview img {
2024
+ display: block; max-width: 420px; max-height: 320px;
2025
+ border-radius: 6px; object-fit: contain;
2026
+ }
2027
+
2028
+ /* 拖拽添加附件:文件拖入页面时的全屏蒙版(pointer-events 穿透,drop 仍落在 window 上)。
2029
+ 不使用 backdrop-filter:渲染开销会在大面积拖拽时造成界面抖动。 */
2030
+ .chat-drop-overlay {
2031
+ position: fixed; inset: 0; z-index: 100;
2032
+ display: flex; align-items: center; justify-content: center;
2033
+ background: rgba(47, 107, 255, .10);
2034
+ pointer-events: none;
2035
+ }
2036
+ .chat-drop-box {
2037
+ padding: 18px 34px; border-radius: 14px;
2038
+ background: var(--fade-stop); border: 2px dashed var(--brand);
2039
+ color: var(--brand-dark); font-size: 16px; font-weight: 600;
2040
+ box-shadow: 0 8px 30px rgba(31, 39, 51, .14);
2041
+ }
2042
+
2043
+ /* ============ 运营后台 admin ============ */
2044
+ .admin-page {
2045
+ min-height: 100vh;
2046
+ max-width: 1240px;
2047
+ margin: 0 auto;
2048
+ padding: 18px 22px 30px;
2049
+ display: flex;
2050
+ flex-direction: column;
2051
+ gap: 16px;
2052
+ }
2053
+ .admin-topbar {
2054
+ display: flex; align-items: center; gap: 12px;
2055
+ padding: 10px 2px 2px;
2056
+ }
2057
+ .admin-brand { display: flex; align-items: center; gap: 11px; }
2058
+ .admin-logo { height: 28px; width: auto; display: block; }
2059
+ .admin-title { margin: 0; font-size: 22px; font-weight: 800; letter-spacing: .3px; }
2060
+ .admin-range {
2061
+ display: flex; gap: 2px;
2062
+ margin-left: auto;
2063
+ background: var(--field-soft); border: 1px solid var(--line); border-radius: 10px;
2064
+ padding: 3px;
2065
+ }
2066
+ .admin-range button {
2067
+ border: none; background: transparent; padding: 5px 12px; border-radius: 8px;
2068
+ font-size: 12.5px; color: var(--ink-soft); cursor: pointer; white-space: nowrap;
2069
+ transition: all .12s ease;
2070
+ }
2071
+ .admin-range button.on {
2072
+ background: var(--overlay-bg); color: var(--brand-dark); font-weight: 600;
2073
+ box-shadow: 0 1px 4px rgba(31, 39, 51, .14);
2074
+ }
2075
+ .admin-clock {
2076
+ font-size: 13px; color: var(--ink-soft);
2077
+ font-variant-numeric: tabular-nums; letter-spacing: .4px;
2078
+ }
2079
+
2080
+ .admin-kpis {
2081
+ display: grid;
2082
+ grid-template-columns: repeat(6, 1fr);
2083
+ gap: 14px;
2084
+ }
2085
+ .admin-kpi {
2086
+ display: flex; align-items: flex-start; gap: 12px;
2087
+ background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
2088
+ padding: 16px 16px 14px; box-shadow: 0 2px 10px rgba(31, 39, 51, .04);
2089
+ }
2090
+ .admin-kpi-icon {
2091
+ flex: none; width: 40px; height: 40px; border-radius: 11px;
2092
+ display: grid; place-items: center; color: var(--brand); background: var(--brand-soft);
2093
+ }
2094
+ .admin-kpi--orange .admin-kpi-icon { color: #b45309; background: var(--warn-soft); }
2095
+ .admin-kpi--ok .admin-kpi-icon { color: var(--ok); background: var(--ok-soft); }
2096
+ .admin-kpi-body { min-width: 0; }
2097
+ .admin-kpi-label { font-size: 12.5px; color: var(--ink-soft); white-space: nowrap; }
2098
+ .admin-kpi-value {
2099
+ font-size: 25px; font-weight: 800; line-height: 1.25; margin: 2px 0 1px;
2100
+ font-variant-numeric: tabular-nums; white-space: nowrap;
2101
+ }
2102
+ .admin-kpi-unit { font-size: 13px; font-weight: 600; color: var(--ink-soft); margin-left: 2px; }
2103
+ .admin-kpi-sub {
2104
+ font-size: 11.5px; color: var(--ink-soft); line-height: 1.45;
2105
+ overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
2106
+ }
2107
+
2108
+ .admin-grid--pair { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
2109
+ .admin-card {
2110
+ background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
2111
+ padding: 16px 18px 12px; box-shadow: 0 2px 10px rgba(31, 39, 51, .04);
2112
+ min-width: 0;
2113
+ }
2114
+ .admin-card-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 6px; }
2115
+ .admin-card-head h3 { margin: 0; font-size: 14.5px; font-weight: 700; }
2116
+ .admin-card-hint { font-size: 11.5px; color: var(--ink-soft); }
2117
+
2118
+ .admin-chart-svg { width: 100%; height: auto; display: block; }
2119
+ .admin-gridline { stroke: var(--line); stroke-width: 1; }
2120
+ .admin-area { fill: rgba(47, 107, 255, .08); }
2121
+ .admin-line--ai { fill: none; stroke: var(--brand); stroke-width: 2.4; stroke-linejoin: round; }
2122
+ .admin-bar { fill: #c9dcff; }
2123
+ .admin-dot { fill: var(--panel); stroke: var(--brand); stroke-width: 2; }
2124
+ .admin-dot--today { fill: var(--brand); stroke: var(--panel); stroke-width: 2.4; }
2125
+ .admin-axis-x { font-size: 11px; fill: #8a94a6; }
2126
+ .admin-axis-y { font-size: 11px; fill: #8a94a6; }
2127
+
2128
+ .admin-pctbars { display: flex; flex-direction: column; gap: 9px; padding: 2px 0 6px; }
2129
+ .admin-pct-row { display: flex; align-items: center; gap: 10px; font-size: 12.5px; }
2130
+ .admin-pct-label { width: 64px; flex: none; color: var(--ink); }
2131
+ .admin-star { font-style: normal; color: #f5a623; }
2132
+ .admin-pct-track { flex: 1; height: 8px; border-radius: 999px; background: var(--field-soft); overflow: hidden; }
2133
+ .admin-pct-fill { display: block; height: 100%; border-radius: 999px; }
2134
+ .admin-pct-num { width: 40px; text-align: right; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
2135
+
2136
+ .admin-table-wrap { overflow-x: auto; }
2137
+ .admin-table { width: 100%; border-collapse: collapse; font-size: 13px; }
2138
+ .admin-table th {
2139
+ text-align: left; font-size: 12px; font-weight: 600; color: var(--ink-soft);
2140
+ padding: 8px 10px; border-bottom: 1px solid var(--line); white-space: nowrap;
2141
+ }
2142
+ .admin-table td { padding: 9px 10px; border-bottom: 1px solid var(--line); vertical-align: middle; }
2143
+ .admin-table tbody tr:last-child td { border-bottom: none; }
2144
+ .admin-name { font-weight: 600; }
2145
+ .admin-dept { margin-left: 8px; font-size: 11.5px; color: var(--ink-soft); }
2146
+ .admin-num { font-variant-numeric: tabular-nums; white-space: nowrap; }
2147
+ .admin-time { color: var(--ink-soft); }
2148
+ .admin-issue { max-width: 240px; }
2149
+ .admin-agent { font-size: 12.5px; color: var(--brand-dark); }
2150
+
2151
+ .admin-badge {
2152
+ display: inline-flex; align-items: center; gap: 5px;
2153
+ font-size: 12px; font-weight: 600; border-radius: 999px; padding: 3px 10px;
2154
+ white-space: nowrap;
2155
+ }
2156
+ .admin-badge i { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
2157
+ .admin-badge--ai { color: var(--brand-dark); background: var(--brand-soft); }
2158
+ .admin-badge--done { color: var(--ok); background: var(--ok-soft); }
2159
+ .admin-badge--active { color: var(--brand-dark); background: var(--brand-soft); }
2160
+ .admin-badge--escalated { color: #b45309; background: var(--warn-soft); }
2161
+ .admin-badge--queued { color: #a16207; background: var(--warn-soft); }
2162
+
2163
+ .admin-footer {
2164
+ text-align: center; font-size: 11.5px; color: var(--ink-soft);
2165
+ padding: 8px 0 2px;
2166
+ }
2167
+
2168
+ @media (max-width: 1100px) {
2169
+ .admin-kpis { grid-template-columns: repeat(3, 1fr); }
2170
+ .admin-grid--pair { grid-template-columns: 1fr; }
2171
+ }
2172
+ @media (max-width: 720px) {
2173
+ .admin-kpis { grid-template-columns: repeat(2, 1fr); }
2174
+ .admin-topbar { flex-wrap: wrap; }
2175
+ .admin-range { margin-left: 0; flex-wrap: wrap; }
2176
+ .admin-clock { margin-left: auto; }
2177
+ }
2178
+
2179
+ /* ============ 满意度评价(每条 AI 答复末尾) ============ */
2180
+ .chat-sat {
2181
+ display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 4px;
2182
+ flex: none;
2183
+ padding: 8px 12px;
2184
+ border: 1px solid var(--line);
2185
+ border-radius: 12px;
2186
+ background: var(--panel);
2187
+ }
2188
+ .chat-sat-title { font-size: 12px; font-weight: 600; line-height: 1.25; color: var(--ink); }
2189
+ .chat-sat-row { display: flex; align-items: center; gap: 10px; }
2190
+ .chat-sat-stars { display: inline-flex; gap: 2px; }
2191
+ .chat-sat-star {
2192
+ border: none; background: none; padding: 0; cursor: pointer;
2193
+ font-size: 18px; line-height: 1; color: var(--ink-soft);
2194
+ transition: transform .1s ease, color .1s ease;
2195
+ }
2196
+ .chat-sat-star.on { color: #f5a623; }
2197
+ .chat-sat-hint { font-size: 11.5px; color: var(--ink-soft); min-width: 68px; }
2198
+ .chat-sat-submit {
2199
+ margin-left: auto;
2200
+ border: none; border-radius: 8px; padding: 4px 14px;
2201
+ background: linear-gradient(135deg, var(--brand), var(--brand-dark)); color: #fff;
2202
+ font-size: 12px; font-weight: 600; cursor: pointer;
2203
+ }
2204
+ .chat-sat-submit:disabled { opacity: .45; cursor: default; }
2205
+ .chat-sat--done {
2206
+ flex-direction: row; align-items: center; gap: 8px;
2207
+ background: var(--ok-soft); border-color: rgba(52, 179, 107, .35);
2208
+ }
2209
+ .chat-sat--done .chat-sat-stars i { font-style: normal; font-size: 15px; color: var(--ink-soft); }
2210
+ .chat-sat--done .chat-sat-stars i.on { color: #f5a623; }
2211
+ .chat-sat-done-text { font-size: 12px; color: var(--ok); }
2212
+
2213
+ /* ============ 反馈操作条(点赞 / 点踩,描边图标按钮,仿 WorkBuddy 工具栏) ============ */
2214
+ .chat-fb {
2215
+ display: flex; align-items: center; gap: 4px;
2216
+ /* margin-top 由 .msg-footer 统一控制 */
2217
+ }
2218
+ .chat-fb-btn {
2219
+ width: 21px; height: 21px;
2220
+ display: inline-flex; align-items: center; justify-content: center;
2221
+ border: none; border-radius: 6px;
2222
+ background: transparent; cursor: pointer; padding: 0;
2223
+ color: var(--ink-soft);
2224
+ transition: color .15s, background .15s, transform .1s;
2225
+ }
2226
+ .chat-fb-btn:active { transform: scale(.92); }
2227
+ .chat-fb-btn svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
2228
+ .chat-fb-btn:hover, .chat-fb-vote.on { color: var(--ink); background: var(--hover); }
2229
+ .chat-fb-btn.is-copied { color: var(--ok); }
2230
+ .chat-fb-vote svg { width: 16px; height: 16px; }
2231
+ .chat-fb-menu-item svg { flex: 0 0 auto; width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
2232
+ .chat-fb-btn svg.chat-fb-icon-selected, .chat-fb-menu-item svg.chat-fb-icon-selected { fill: currentColor; stroke: none; }
2233
+ .chat-fb-menu-item:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
2234
+ .chat-fb-check { margin-left: auto; }
2235
+ .chat-fb-status { font-size: 11px; color: var(--ink-soft); }
2236
+ .chat-fb-status:empty { display: none; }
2237
+ .chat-fb-btn.on.up { color: var(--ok); border-color: var(--ok); background: rgba(35,162,111,.08); }
2238
+ .chat-fb-btn.on.down { color: #e5484d; border-color: #e5484d; background: rgba(229,72,77,.08); }
2239
+ /* 「更多」菜单(复制会话ID):WorkBuddy 风格悬浮小菜单,竖排选项;浮层用 CSS 变量随明暗主题 */
2240
+ .chat-fb-more-wrap { position: relative; display: inline-flex; }
2241
+ .chat-fb-more svg circle { fill: currentColor; stroke: none; }
2242
+ .chat-fb-more.on { color: var(--ink); border-color: var(--ink-soft); background: var(--line); }
2243
+ /* 「…」按钮作为菜单触发器,按下时不缩放,避免点击时的抖动;其 transform 过渡一并关闭 */
2244
+ .chat-fb-more:active { transform: none; }
2245
+ .chat-fb-more { transition: border-color .15s, color .15s, background .15s; }
2246
+ .chat-fb-menu-backdrop { position: fixed; inset: 0; z-index: 240; background: transparent; }
2247
+ .chat-fb-menu {
2248
+ /* z 高于聊天区所有浮层(输入区/待发队列 30、顶栏 220、历史/分享下拉 230):
2249
+ 菜单即使弹到输入卡片上方,也始终在输入区之上,用户总能点到菜单项。
2250
+ 定位由组件按按钮视口坐标 + 实测菜单高度计算(fixed,并在视口内 clamp):
2251
+ 保证在屏幕任何位置点「…」菜单都不会被滚动容器裁剪或顶出屏幕 */
2252
+ z-index: 241;
2253
+ min-width: 158px; padding: 5px;
2254
+ background: var(--overlay-bg); border: 1px solid var(--line);
2255
+ border-radius: 10px; box-shadow: var(--shadow);
2256
+ }
2257
+ .chat-fb-menu-item {
2258
+ display: flex; align-items: center; gap: 8px;
2259
+ width: 100%; padding: 8px 10px; border: none; background: none; cursor: pointer;
2260
+ font-size: 13px; color: var(--ink); border-radius: 7px; text-align: left;
2261
+ white-space: nowrap;
2262
+ transition: background .15s ease, color .15s ease;
2263
+ }
2264
+ .chat-fb-menu-item.is-copied { color: var(--ok); }
2265
+ /* hover 仅对可悬停设备生效(移动端点击不会粘滞) */
2266
+ @media (hover: hover) and (pointer: fine) {
2267
+ .chat-fb-menu-item:hover { background: var(--line); }
2268
+ }
2269
+
2270
+ /* ============ 产物(Artifact)卡片 ============ */
2271
+ .artifact-grid {
2272
+ display: flex; flex-wrap: wrap; gap: 10px;
2273
+ margin: 10px 0 2px;
2274
+ }
2275
+ .artifact-card {
2276
+ display: inline-flex; align-items: center; gap: 12px;
2277
+ width: min(340px, 100%);
2278
+ padding: 11px 14px;
2279
+ border: 1px solid var(--line); border-radius: 12px;
2280
+ background: var(--panel); color: var(--ink);
2281
+ cursor: pointer; text-align: left;
2282
+ transition: border-color .15s, box-shadow .15s, transform .1s;
2283
+ }
2284
+ .artifact-card:active { transform: scale(.98); }
2285
+ .artifact-card-icon { flex: 0 0 auto; display: inline-flex; }
2286
+ .artifact-card-icon svg { width: 26px; height: 26px; fill: none; stroke: var(--ink-soft); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
2287
+ /* PDF 图标:SVG Repo 标准图标(白底文档页 + 粗体 PDF),填充灰(与其它文件 icon 颜色统一)、无描边 */
2288
+ .artifact-card-icon svg.artifact-icon-pdf { fill: var(--ink-soft); stroke: none; }
2289
+ .artifact-card-main {
2290
+ flex: 1 1 auto; min-width: 0;
2291
+ display: flex; flex-direction: column; gap: 3px;
2292
+ }
2293
+ .artifact-card-name {
2294
+ font-size: 14px; font-weight: 600; color: var(--ink);
2295
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
2296
+ }
2297
+ .artifact-card-meta {
2298
+ display: inline-flex; align-items: center; gap: 8px;
2299
+ min-height: 18px;
2300
+ }
2301
+ .artifact-card-size {
2302
+ flex: 0 0 auto; font-size: 11.5px; color: var(--ink-soft);
2303
+ font-variant-numeric: tabular-nums;
2304
+ }
2305
+ /* 右侧「打开预览」箭头:无背景,中性色,hover 不强调 */
2306
+ .artifact-card-open {
2307
+ flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
2308
+ width: 22px; height: 22px; background: transparent; color: var(--ink-soft);
2309
+ }
2310
+ .artifact-card-open svg {
2311
+ width: 14px; height: 14px;
2312
+ fill: none; stroke: currentColor; stroke-width: 2.2;
2313
+ stroke-linecap: round; stroke-linejoin: round;
2314
+ }
2315
+
2316
+ /* ============ 产物预览抽屉(右侧) ============ */
2317
+ .artifact-drawer-backdrop {
2318
+ position: fixed; inset: 0; z-index: 40;
2319
+ background: var(--modal-mask);
2320
+ }
2321
+ .artifact-drawer {
2322
+ position: fixed; top: 0; right: 0; bottom: 0; z-index: 50;
2323
+ width: min(64vw, 880px); max-width: 100vw;
2324
+ display: flex; flex-direction: column;
2325
+ background: var(--panel);
2326
+ box-shadow: var(--drawer-shadow);
2327
+ border-left: 1px solid var(--line);
2328
+ animation: artifact-drawer-in .18s ease-out;
2329
+ }
2330
+ @keyframes artifact-drawer-in { from { transform: translateX(24px); opacity: .6; } to { transform: none; opacity: 1; } }
2331
+
2332
+ /* ============ 非免费模型(积分)提示 ============ */
2333
+ /* 位于回复底部「点赞/点踩」上方:本次对话走了 fallback 积分模型时的提示。
2334
+ 仅显示感叹号图标(黑白灰系),hover 出 tooltip 说明文字(data-tip,与 ctx-meter 同款)。 */
2335
+ .chat-model-hint {
2336
+ position: relative;
2337
+ display: inline-flex; align-items: center; justify-content: center;
2338
+ width: 20px; height: 20px; margin: 0;
2339
+ border-radius: 50%;
2340
+ color: var(--ink-soft);
2341
+ cursor: help;
2342
+ transition: background .15s ease, color .15s ease;
2343
+ }
2344
+ .chat-model-hint:hover { background: var(--hover); color: var(--ink); }
2345
+ .chat-model-hint-icon {
2346
+ display: inline-flex; align-items: center; justify-content: center;
2347
+ width: 13px; height: 13px;
2348
+ }
2349
+ .chat-model-hint-icon svg {
2350
+ width: 13px; height: 13px;
2351
+ fill: none; stroke: currentColor; stroke-width: 1.8;
2352
+ stroke-linecap: round; stroke-linejoin: round;
2353
+ }
2354
+ .chat-model-hint::after {
2355
+ content: attr(data-tip);
2356
+ position: absolute;
2357
+ bottom: calc(100% + 8px);
2358
+ left: 50%;
2359
+ transform: translateX(-50%);
2360
+ white-space: nowrap;
2361
+ padding: 5px 9px;
2362
+ border-radius: 7px;
2363
+ background: var(--bg-float, #fdfdfd);
2364
+ color: var(--ink, #1a1a1a);
2365
+ border: 1px solid var(--line, #e3e8f0);
2366
+ font-size: 12px; line-height: 1.5;
2367
+ box-shadow: 0 4px 14px rgb(0 0 0 / .15);
2368
+ opacity: 0;
2369
+ pointer-events: none;
2370
+ transition: opacity .12s ease;
2371
+ z-index: 40;
2372
+ }
2373
+ :root[data-theme="dark"] .chat-model-hint::after {
2374
+ background: var(--bg-float, #212121);
2375
+ color: #f0f0f0;
2376
+ border-color: #3a3a3a;
2377
+ }
2378
+ .chat-model-hint:hover::after { opacity: 1; }
2379
+ .artifact-drawer-head {
2380
+ flex: 0 0 auto;
2381
+ display: flex; align-items: center; justify-content: space-between;
2382
+ gap: 12px; padding: 14px 18px;
2383
+ border-bottom: 1px solid var(--line);
2384
+ background: var(--panel);
2385
+ }
2386
+ /* 预览窗口顶部菜单栏:标题 + 右侧操作区(下载图标 / 关闭)同行 */
2387
+ .artifact-menu-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
2388
+ .artifact-menu-bar .artifact-drawer-title { flex: 1 1 auto; min-width: 0; }
2389
+ /* 预览菜单操作按钮容器:右侧两个按钮并排 */
2390
+ .artifact-menu-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 4px; }
2391
+ /* 预览菜单操作按钮(下载 / 全屏 / 关闭):与输入区 +号、语音按钮一致——
2392
+ 无边框圆形,hover 仅黑白灰背景,自身颜色不变(不出现蓝色) */
2393
+ .artifact-menu-dl, .artifact-drawer-close, .artifact-drawer-full {
2394
+ flex: 0 0 auto; width: 32px; height: 32px;
2395
+ display: inline-flex; align-items: center; justify-content: center;
2396
+ border: none; border-radius: 50%; background: transparent;
2397
+ color: var(--ink-soft); text-decoration: none; cursor: pointer;
2398
+ transition: background .15s ease;
2399
+ }
2400
+ .artifact-menu-dl svg, .artifact-drawer-close svg, .artifact-drawer-full svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
2401
+ .artifact-drawer-title { font-size: 14px; font-weight: 700; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2402
+ .artifact-drawer-body { flex: 1 1 auto; min-height: 0; overflow: auto; background: var(--bg); }
2403
+ .artifact-frame { width: 100%; height: 100%; border: 0; background: var(--panel); display: block; }
2404
+ .artifact-img { display: block; max-width: 100%; margin: 0 auto; }
2405
+ .artifact-md { padding: 18px 22px; }
2406
+ .artifact-pre {
2407
+ margin: 0;
2408
+ font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, "Courier New", monospace);
2409
+ font-size: 13px;
2410
+ line-height: 1.6;
2411
+ white-space: pre;
2412
+ overflow: auto;
2413
+ color: var(--ink-soft);
2414
+ }
2415
+ /* Word(.docx) 内嵌预览(mammoth 转换的 HTML):文档流样式,随主题变量 */
2416
+ .artifact-word {
2417
+ padding: 14px 18px;
2418
+ font-size: 14px;
2419
+ line-height: 1.7;
2420
+ color: var(--ink);
2421
+ overflow: auto;
2422
+ }
2423
+ .artifact-word h1, .artifact-word h2, .artifact-word h3, .artifact-word h4 {
2424
+ margin: .7em 0 .35em;
2425
+ line-height: 1.35;
2426
+ color: var(--ink);
2427
+ }
2428
+ .artifact-word h1 { font-size: 20px; }
2429
+ .artifact-word h2 { font-size: 17px; }
2430
+ .artifact-word h3 { font-size: 15px; }
2431
+ .artifact-word h4 { font-size: 14px; }
2432
+ .artifact-word p { margin: .45em 0; }
2433
+ .artifact-word ul, .artifact-word ol { margin: .4em 0; padding-left: 22px; }
2434
+ .artifact-word table { border-collapse: collapse; margin: .6em 0; }
2435
+ .artifact-word td, .artifact-word th { border: 1px solid var(--line); padding: 4px 9px; }
2436
+ .artifact-word img { max-width: 100%; }
2437
+ /* Prism 语法高亮 token 色(浅色默认 + 深色覆盖):柔和、不抢视线 */
2438
+ .artifact-pre {
2439
+ --syn-comment: #8a919c;
2440
+ --syn-keyword: #8250df;
2441
+ --syn-string: #1a7f37;
2442
+ --syn-number: #0550ae;
2443
+ --syn-function: #6639ba;
2444
+ --syn-class: #953800;
2445
+ --syn-operator: #57606a;
2446
+ --syn-property: #0a3069;
2447
+ --syn-tag: #116329;
2448
+ --syn-regex: #cf222e;
2449
+ }
2450
+ :root[data-theme="dark"] .artifact-pre {
2451
+ --syn-comment: #8b949e;
2452
+ --syn-keyword: #ff7b72;
2453
+ --syn-string: #a5d6ff;
2454
+ --syn-number: #79c0ff;
2455
+ --syn-function: #d2a8ff;
2456
+ --syn-class: #ffa657;
2457
+ --syn-operator: #c9d1d9;
2458
+ --syn-property: #79c0ff;
2459
+ --syn-tag: #7ee787;
2460
+ --syn-regex: #ffa198;
2461
+ }
2462
+ .artifact-pre .token.comment, .artifact-pre .token.prolog, .artifact-pre .token.doctype, .artifact-pre .token.cdata { color: var(--syn-comment); font-style: italic; }
2463
+ .artifact-pre .token.keyword, .artifact-pre .token.atrule, .artifact-pre .token.rule, .artifact-pre .token.important { color: var(--syn-keyword); }
2464
+ .artifact-pre .token.string, .artifact-pre .token.char, .artifact-pre .token.attr-value { color: var(--syn-string); }
2465
+ .artifact-pre .token.number, .artifact-pre .token.boolean, .artifact-pre .token.constant { color: var(--syn-number); }
2466
+ .artifact-pre .token.function, .artifact-pre .token.method { color: var(--syn-function); }
2467
+ .artifact-pre .token.class-name, .artifact-pre .token.builtin, .artifact-pre .token.type { color: var(--syn-class); }
2468
+ .artifact-pre .token.operator, .artifact-pre .token.punctuation { color: var(--syn-operator); }
2469
+ .artifact-pre .token.property, .artifact-pre .token.attr-name, .artifact-pre .token.parameter { color: var(--syn-property); }
2470
+ .artifact-pre .token.variable, .artifact-pre .token.selector, .artifact-pre .token.tag { color: var(--syn-tag); }
2471
+ .artifact-pre .token.regex, .artifact-pre .token.deleted { color: var(--syn-regex); }
2472
+ .artifact-pre .token.inserted { color: var(--syn-string); }
2473
+ .artifact-other-hint { padding: 18px 22px; font-size: 13px; color: var(--ink-soft); line-height: 1.6; }
2474
+ .artifact-loading {
2475
+ display: flex; align-items: center; justify-content: center;
2476
+ height: 100%; min-height: 120px;
2477
+ }
2478
+ .artifact-loading::after {
2479
+ content: "";
2480
+ width: 24px; height: 24px; border-radius: 50%;
2481
+ border: 2.5px solid var(--spin-edge); border-top-color: var(--spin-top);
2482
+ animation: chatSpin .7s linear infinite;
2483
+ }
2484
+ .artifact-err {
2485
+ margin: 18px 22px; padding: 14px 16px; border-radius: 10px; font-size: 13px; line-height: 1.6;
2486
+ color: var(--danger); background: var(--panel); border: 1px solid var(--line);
2487
+ }
2488
+ /* 右栏内嵌预览:菜单栏 + 内容区(替换产物列表显示,不再悬浮) */
2489
+ .chat-right-preview { display: flex; flex-direction: column; height: 100%; }
2490
+ .chat-right-preview-head { flex: 0 0 auto; padding: 6px 8px; border-bottom: 1px solid var(--line); background: var(--panel); }
2491
+ .chat-right-preview-head .artifact-drawer-title { font-size: 13px; }
2492
+ .chat-right-preview-frame { flex: 1 1 auto; min-height: 0; background: var(--panel); overflow: auto; }
2493
+ .chat-right-preview-frame .artifact-img { max-height: 100%; object-fit: contain; }
2494
+ .chat-right-preview-frame .artifact-frame { height: 100%; }
2495
+ .chat-right-preview-frame .artifact-md { height: 100%; overflow: auto; box-sizing: border-box; }
2496
+ .chat-right-preview-frame .artifact-other-hint { height: 100%; box-sizing: border-box; display: flex; align-items: center; justify-content: center; text-align: center; }
2497
+ /* 右侧浏览器实时预览:截图可点击(坐标转发 AI 操作的同一浏览器),底部提示条 */
2498
+ .artifact-browser-live {
2499
+ display: flex; flex-direction: column; height: 100%; box-sizing: border-box;
2500
+ }
2501
+ .artifact-img--clickable { cursor: crosshair; flex: 1 1 auto; min-height: 0; max-height: none; object-fit: contain; }
2502
+ .artifact-browser-tip {
2503
+ flex: 0 0 auto; padding: 4px 10px; text-align: center;
2504
+ font-size: 11.5px; color: var(--ink-soft); background: var(--field-soft);
2505
+ border-top: 1px solid var(--line);
2506
+ }
2507
+
2508
+ /* ============ 「查看所有产物」按钮 + 右侧产物列表面板(WorkBuddy 风格) ============ */
2509
+ .artifact-viewall-row { margin: 8px 0 2px; display: flex; gap: 8px; flex-wrap: wrap; }
2510
+ .artifact-viewall {
2511
+ display: inline-flex; align-items: center; gap: 6px;
2512
+ padding: 6px 12px;
2513
+ border: 1px solid var(--line); border-radius: 999px;
2514
+ background: var(--field-bg); color: var(--ink-soft);
2515
+ font-size: 12.5px; font-weight: 600; line-height: 1.5;
2516
+ cursor: pointer; text-align: left;
2517
+ transition: border-color .15s, background .15s, box-shadow .15s;
2518
+ }
2519
+
2520
+ /* ============ 变更 diff 视图(对标 WorkBuddy「查看所有变更」) ============
2521
+ 右栏:顶部文件 tabs(+新增 / ~修改 / −删除),下方左右并排 diff(左=旧内容红底、右=新内容绿底) */
2522
+ .diff-panel {
2523
+ --diff-add-ink: #167346; --diff-del-ink: #b52b37; --diff-mod-ink: #2563c5;
2524
+ --diff-add-bg: rgba(40, 167, 92, .13); --diff-del-bg: rgba(220, 53, 69, .12);
2525
+ --diff-add-strong: rgba(40, 167, 92, .27); --diff-del-strong: rgba(220, 53, 69, .25);
2526
+ display: flex; flex-direction: column; height: 100%; min-height: 0;
2527
+ }
2528
+ :root[data-theme="dark"] .diff-panel {
2529
+ --diff-add-ink: #72dba0; --diff-del-ink: #ff959d; --diff-mod-ink: #89b8ff;
2530
+ --diff-add-bg: rgba(46, 160, 87, .19); --diff-del-bg: rgba(248, 81, 73, .17);
2531
+ --diff-add-strong: rgba(46, 160, 87, .37); --diff-del-strong: rgba(248, 81, 73, .34);
2532
+ }
2533
+ .diff-tabs {
2534
+ display: flex; align-items: center; gap: 4px;
2535
+ padding: 8px 10px 6px;
2536
+ border-bottom: 1px solid var(--line);
2537
+ flex-wrap: nowrap; overflow-x: auto; scrollbar-width: thin;
2538
+ }
2539
+ .diff-tab {
2540
+ display: inline-flex; align-items: center; gap: 5px;
2541
+ flex: 0 0 auto; max-width: 200px;
2542
+ padding: 4px 9px; border-radius: 8px;
2543
+ border: 1px solid transparent;
2544
+ background: transparent; color: var(--ink-soft);
2545
+ font-size: 12px; line-height: 1.4; cursor: pointer;
2546
+ transition: background .15s, border-color .15s;
2547
+ }
2548
+ .diff-tab:hover { background: var(--hover); }
2549
+ .diff-tab.is-active {
2550
+ background: var(--hover-strong); border-color: var(--line);
2551
+ color: var(--ink); font-weight: 600;
2552
+ }
2553
+ .diff-tab-mark { font-weight: 700; font-size: 12px; line-height: 1; }
2554
+ .diff-tab-mark--added { color: var(--diff-add-ink); }
2555
+ .diff-tab-mark--modified { color: var(--diff-mod-ink); }
2556
+ .diff-tab-mark--deleted { color: var(--diff-del-ink); }
2557
+ .diff-tab-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2558
+ .diff-tabs-spacer { flex: 1; min-width: 6px; }
2559
+ .diff-close {
2560
+ flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
2561
+ width: 26px; height: 26px; padding: 0; border-radius: 8px;
2562
+ border: none; background: transparent; color: var(--ink-soft); cursor: pointer;
2563
+ transition: background .15s, color .15s;
2564
+ }
2565
+ .diff-close:hover { background: var(--hover); color: var(--ink); }
2566
+ .diff-close svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
2567
+ .diff-file-head {
2568
+ display: flex; align-items: center; gap: 8px;
2569
+ padding: 6px 12px;
2570
+ border-bottom: 1px solid var(--line);
2571
+ background: var(--field-bg);
2572
+ }
2573
+ .diff-file-name { font-size: 12.5px; font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2574
+ .diff-file-size { font-size: 11.5px; color: var(--ink-soft); }
2575
+ .diff-change-tag {
2576
+ flex: 0 0 auto; padding: 1px 7px; border-radius: 999px;
2577
+ font-size: 11px; font-weight: 600; line-height: 1.5;
2578
+ background: var(--hover-strong); color: var(--ink-soft);
2579
+ }
2580
+ .diff-change-tag--added { color: var(--diff-add-ink); }
2581
+ .diff-change-tag--modified { color: var(--diff-mod-ink); }
2582
+ .diff-change-tag--deleted { color: var(--diff-del-ink); }
2583
+ .diff-stat { margin-left: auto; font-size: 11.5px; color: var(--ink-soft); white-space: nowrap; }
2584
+ .diff-view-controls { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; padding: 6px 12px; border-bottom: 1px solid var(--line); }
2585
+ .diff-view-controls button { border: 1px solid var(--line); background: transparent; color: var(--ink-soft); border-radius: 5px; padding: 4px 8px; cursor: pointer; font-size: 12px; }
2586
+ .diff-view-controls button[aria-pressed="true"] { background: var(--brand-soft); color: var(--brand); border-color: var(--brand); }
2587
+ .diff-view-controls > span { font-size: 11px; color: var(--ink-soft); }
2588
+ .diff-gap td { padding: 0; text-align: center; background: var(--hover); }
2589
+ .diff-gap button { display: block; width: 100%; padding: 9px 12px; border: 0; background: transparent; color: var(--ink-soft); cursor: pointer; font-size: 12px; }
2590
+ .diff-gap button:hover { color: var(--brand); background: var(--brand-soft); }
2591
+ .diff-stat-add { color: var(--diff-add-ink); }
2592
+ .diff-stat-del { color: var(--diff-del-ink); }
2593
+ .diff-stat-modified { color: var(--diff-mod-ink); }
2594
+ .diff-legend { display: flex; flex-wrap: wrap; gap: 12px; padding: 6px 12px; font-size: 11px; border-bottom: 1px solid var(--line); }
2595
+ .diff-notice { padding: 8px 12px; color: var(--ink-soft); font-size: 12px; background: var(--field-bg); }
2596
+ .diff-body { flex: 1; min-height: 0; overflow: auto; font-size: 12.5px; line-height: 1.5; }
2597
+ .diff-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
2598
+ .diff-lineno-col { width: 58px; }
2599
+ .diff-table th { position: sticky; top: 0; z-index: 1; padding: 7px 12px; text-align: left; font-weight: 600; font-size: 11px; color: var(--ink-soft); background: var(--field-bg); border-bottom: 1px solid var(--line); }
2600
+ .diff-table th + th, .diff-cell--left { border-right: 1px solid var(--line); }
2601
+ .diff-row td {
2602
+ padding: 0 8px; vertical-align: top;
2603
+ font-family: var(--mono, ui-monospace, "Cascadia Code", Consolas, monospace);
2604
+ font-size: 12px; white-space: pre-wrap; word-break: break-all;
2605
+ border-bottom: 1px solid color-mix(in srgb, var(--line) 50%, transparent);
2606
+ }
2607
+ .diff-lineno {
2608
+ width: 58px; min-width: 58px; text-align: right;
2609
+ color: var(--ink-soft);
2610
+ user-select: none; -webkit-user-select: none;
2611
+ background: color-mix(in srgb, var(--hover-strong) 40%, transparent);
2612
+ }
2613
+ .diff-cell { color: var(--ink); }
2614
+ .diff-cell--left.is-del, .diff-lineno.is-del { background: var(--diff-del-bg); }
2615
+ .diff-cell--right.is-add, .diff-lineno.is-add { background: var(--diff-add-bg); }
2616
+ .diff-lineno.is-del { color: var(--diff-del-ink); }
2617
+ .diff-lineno.is-add { color: var(--diff-add-ink); }
2618
+ .diff-line-mark { float: left; font-weight: 700; }
2619
+ .diff-row.is-modified .diff-line-mark { color: var(--diff-mod-ink); }
2620
+ .diff-row.is-modified .diff-lineno { box-shadow: inset 3px 0 0 var(--diff-mod-ink); }
2621
+ .diff-cell.is-empty { background: color-mix(in srgb, var(--hover-strong) 35%, transparent); }
2622
+ .diff-inline { color: inherit; border-radius: 2px; font-weight: 600; }
2623
+ .diff-inline--del { background: var(--diff-del-strong); }
2624
+ .diff-inline--add { background: var(--diff-add-strong); }
2625
+ .diff-no-newline { display: block; font: 10px/1.5 var(--mono, monospace); color: var(--ink-soft); user-select: none; }
2626
+ .diff-nocontent {
2627
+ display: flex; flex-direction: column; align-items: flex-start; gap: 8px;
2628
+ padding: 24px 16px; color: var(--ink-soft); font-size: 12.5px;
2629
+ }
2630
+ .diff-nocontent-name { font-weight: 600; color: var(--ink); }
2631
+
2632
+ /* 右侧产物列表 */
2633
+ .artifact-list { padding: 10px 14px; display: flex; flex-direction: column; gap: 8px; }
2634
+ /* 每行是一个圆角边框容器;⋯ 菜单展开时菜单成为行内第三行,
2635
+ 被行自身的圆角边框包住(不弹出到行外) */
2636
+ .artifact-list-item {
2637
+ display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
2638
+ width: 100%;
2639
+ position: relative;
2640
+ padding: 2px;
2641
+ border: 1px solid transparent; border-radius: 12px;
2642
+ background: transparent;
2643
+ transition: border-color .12s, background .12s;
2644
+ }
2645
+ .artifact-list-item.menu-open {
2646
+ border-color: var(--line);
2647
+ background: var(--panel);
2648
+ }
2649
+ .artifact-list-item-mainbtn {
2650
+ flex: 1 1 auto; min-width: 0;
2651
+ display: flex; align-items: center; gap: 12px;
2652
+ padding: 8px 10px;
2653
+ border: none; border-radius: 9px;
2654
+ background: transparent; color: var(--ink);
2655
+ cursor: pointer; text-align: left;
2656
+ transition: background .12s, transform .1s;
2657
+ }
2658
+ .artifact-list-item-mainbtn:active { transform: scale(.99); }
2659
+ /* 条目右侧「⋯」操作按钮(WorkBuddy 式菜单入口);点击在行内展开「打开所在文件夹 / 另存为」 */
2660
+ .artifact-list-actions { position: relative; flex: 0 0 auto; display: flex; align-items: center; }
2661
+ .artifact-list-more {
2662
+ display: inline-flex; align-items: center; justify-content: center;
2663
+ width: 30px; height: 30px; border: none; border-radius: 8px;
2664
+ background: transparent; color: var(--ink-soft); cursor: pointer; padding: 0;
2665
+ transition: background .12s, color .12s;
2666
+ }
2667
+ .artifact-list-more:hover, .artifact-list-more.is-open { background: var(--hover); color: var(--ink); }
2668
+ .artifact-list-more svg {
2669
+ width: 16px; height: 16px; fill: var(--ink-soft); stroke: none;
2670
+ }
2671
+ .artifact-list-more:hover svg, .artifact-list-more.is-open svg { fill: var(--ink); }
2672
+ /* 「⋯」菜单:行内展开(作为行容器的第三行,占满行宽,被行圆角边框包住) */
2673
+ .artifact-list-menu {
2674
+ flex: 1 1 100%; /* 换行到第二行,占满行宽 */
2675
+ display: flex; flex-direction: column; gap: 2px;
2676
+ padding: 4px 6px 6px;
2677
+ border: none; border-radius: 0; background: transparent; box-shadow: none;
2678
+ }
2679
+ .artifact-list-menu button {
2680
+ display: flex; align-items: center; gap: 9px; width: 100%;
2681
+ padding: 7px 9px; border: none; border-radius: 7px;
2682
+ background: transparent; color: var(--ink); font-size: 13px; cursor: pointer; text-align: left;
2683
+ }
2684
+ .artifact-list-menu button:hover { background: var(--hover); }
2685
+ .artifact-list-menu button svg {
2686
+ width: 15px; height: 15px; flex: 0 0 auto;
2687
+ fill: none; stroke: var(--ink-soft); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
2688
+ }
2689
+ .artifact-list-menu button:hover svg { stroke: var(--ink); }
2690
+ .artifact-list-item-main {
2691
+ flex: 1 1 auto; min-width: 0;
2692
+ display: flex; flex-direction: column; gap: 3px;
2693
+ }
2694
+ .artifact-list-item-name {
2695
+ font-size: 13.5px; font-weight: 600; color: var(--ink);
2696
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
2697
+ }
2698
+ .artifact-list-item-meta { font-size: 11.5px; color: var(--ink-soft); }
2699
+
2700
+ /* ============ 优雅提示与错误框 ============ */
2701
+ .chat-hint { margin: 6px auto 2px; max-width: 92%; padding: 8px 14px; border-radius: 10px; font-size: 12.5px; line-height: 1.5; }
2702
+ .chat-hint--slow {
2703
+ background: var(--brand-soft, rgba(31, 39, 51, .05));
2704
+ color: var(--ink-soft); border: 1px solid var(--line);
2705
+ }
2706
+ .chat-error {
2707
+ display: flex; align-items: center; gap: 10px; margin: 8px auto 2px; max-width: 92%;
2708
+ padding: 10px 14px; border-radius: 10px; font-size: 13px; line-height: 1.5;
2709
+ background: rgba(229, 72, 77, .08); border: 1px solid rgba(229, 72, 77, .35); color: var(--danger);
2710
+ }
2711
+ .chat-error-icon {
2712
+ flex: 0 0 auto; width: 18px; height: 18px; border-radius: 50%;
2713
+ background: var(--danger); color: #fff; font-weight: 700; font-size: 13px; line-height: 18px; text-align: center;
2714
+ }
2715
+ .chat-error-text { flex: 1 1 auto; word-break: break-word; }
2716
+
2717
+ /* 发送区:停止按钮(生成中显示)。灰色系。外部保持圆形(继承 .chat-send),
2718
+ 仅阴影收敛(区别于发送按钮的大阴影);中间的方形图标比发送图标略大 */
2719
+ .chat-send--stop {
2720
+ /* 参考截图:白色圆形 + 内部黑色实心方块(深色背景上高亮;浅色主题靠细边框区分) */
2721
+ background: #fff;
2722
+ border: 1px solid rgba(0, 0, 0, .08);
2723
+ color: #111;
2724
+ box-shadow: 0 2px 8px rgba(0, 0, 0, .28);
2725
+ }
2726
+ .chat-send--stop svg {
2727
+ width: 20px; height: 20px;
2728
+ fill: currentColor; stroke: none; /* 实心方块(覆盖 .chat-send svg 的描边模式) */
2729
+ animation: chatStopBreathe 2.2s ease-in-out infinite;
2730
+ }
2731
+ @keyframes chatStopBreathe {
2732
+ 0%, 100% { transform: scale(1); }
2733
+ 50% { transform: scale(.88); }
2734
+ }
2735
+
2736
+ /* 助手消息最上方的共用时提示:从用户发送消息开始计时,到全部答复完成/用户停止 */
2737
+ .msg-elapsed {
2738
+ margin: 0 0 8px;
2739
+ font-size: 12px;
2740
+ line-height: 1.4;
2741
+ color: var(--ink-soft, #5b6573);
2742
+ opacity: .8;
2743
+ user-select: none;
2744
+ }
2745
+
2746
+ /* 豆包 Work 式「已完成」折叠卡:代替 msg-elapsed 纯文本。流式中不显示(过程由下方思考/工具步直播);
2747
+ 完成后显示「已完成 X · ▸」,点击卡片展开/收起该消息的全部思考与工具调用。
2748
+ 纯文本行样式:无背景无边框,与下方内容天然左对齐 */
2749
+ .msg-process-card {
2750
+ display: inline-flex; align-items: center; gap: 7px;
2751
+ margin: 0 0 8px; padding: 0;
2752
+ border: none; background: transparent;
2753
+ font-size: 14px; line-height: 1.4; color: var(--ink-soft);
2754
+ cursor: pointer; user-select: none;
2755
+ transition: color .15s ease;
2756
+ }
2757
+ .msg-process-card:hover { color: var(--ink); }
2758
+ .msg-process-label { font-variant-numeric: tabular-nums; }
2759
+ .msg-process-chevron {
2760
+ flex: 0 0 auto;
2761
+ transition: transform .15s ease;
2762
+ }
2763
+ /* 与 tools 胶囊同款 SVG 描边 chevron:收起向右、展开向下(rotate 90deg) */
2764
+ .msg-process-chevron svg {
2765
+ width: 12px; height: 12px;
2766
+ fill: none; stroke: currentColor; stroke-width: 2;
2767
+ stroke-linecap: round; stroke-linejoin: round;
2768
+ }
2769
+ .msg-process-card.is-open .msg-process-chevron { transform: rotate(90deg); }
2770
+
2771
+ /* 助手消息底部操作行:消耗提示与点赞/点踩同一行(消耗在左,反馈紧跟其后) */
2772
+ .msg-footer {
2773
+ display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
2774
+ margin-top: 10px;
2775
+ }
2776
+ /* 非最后一条助手消息:footer 默认隐藏,hover 该消息时才显示(保留占位,避免布局抖动) */
2777
+ @media (hover: hover) and (pointer: fine) {
2778
+ .msg-footer:not(.msg-footer--last) {
2779
+ opacity: 0;
2780
+ pointer-events: none;
2781
+ transition: opacity .15s;
2782
+ }
2783
+ .msg:hover .msg-footer:not(.msg-footer--last) {
2784
+ opacity: 1;
2785
+ pointer-events: auto;
2786
+ }
2787
+ }
2788
+
2789
+ /* 助手消息底部消耗提示:本轮 token 用量与美元成本(与点赞点踩同一行) */
2790
+ .msg-cost {
2791
+ margin: 0;
2792
+ font-size: 11.5px;
2793
+ line-height: 1.4;
2794
+ color: var(--ink-soft, #5b6573);
2795
+ opacity: .75;
2796
+ user-select: none;
2797
+ }
2798
+
2799
+ /* 助手消息底部时间:最后一条始终显示,其他消息默认隐藏、hover 时显示 */
2800
+ .msg-footer-time {
2801
+ font-size: 11.5px;
2802
+ line-height: 1.4;
2803
+ color: var(--ink-soft);
2804
+ opacity: .75;
2805
+ user-select: none;
2806
+ }
2807
+ .msg-footer-time-hover {
2808
+ opacity: 0;
2809
+ transition: opacity 0.15s;
2810
+ }
2811
+ .msg-footer:hover .msg-footer-time-hover {
2812
+ opacity: .75;
2813
+ }
2814
+
2815
+ /* 删除确认对话框 + 全局 loading 遮罩 */
2816
+ .chat-modal-backdrop {
2817
+ position: fixed; inset: 0; background: var(--modal-mask);
2818
+ display: flex; align-items: center; justify-content: center; z-index: 1000;
2819
+ backdrop-filter: blur(2px);
2820
+ }
2821
+ /* Electron:关闭遮罩 blur(合成异常),保留半透明暗色 */
2822
+ .env-electron .chat-modal-backdrop { backdrop-filter: none; -webkit-backdrop-filter: none; }
2823
+ .chat-modal-backdrop--busy { pointer-events: none; }
2824
+ .chat-modal {
2825
+ background: var(--panel); border-radius: 14px; padding: 22px 24px; width: 320px;
2826
+ box-shadow: 0 12px 40px rgba(0,0,0,.18); text-align: center;
2827
+ }
2828
+ .chat-modal--loading { display: flex; flex-direction: column; align-items: center; gap: 12px; width: 200px; }
2829
+ .chat-modal-title { margin: 0 0 8px; font-size: 16px; font-weight: 700; }
2830
+ .chat-modal-desc { margin: 0 0 18px; font-size: 13px; color: var(--ink-soft); }
2831
+ .chat-modal-actions { display: flex; gap: 10px; justify-content: center; }
2832
+ .chat-modal-btn {
2833
+ padding: 8px 18px; border-radius: 8px; border: 1px solid var(--line); background: var(--field-bg);
2834
+ font-size: 13px; cursor: pointer; color: var(--ink);
2835
+ }
2836
+ .chat-modal-btn--danger { background: var(--danger); color: #fff; border-color: var(--danger); }
2837
+ .chat-spinner {
2838
+ width: 28px; height: 28px; border: 3px solid #e2e8f0; border-top-color: #2563eb;
2839
+ border-radius: 50%; animation: chatSpin .8s linear infinite;
2840
+ }
2841
+ @keyframes chatSpin { to { transform: rotate(360deg); } }
2842
+
2843
+ /* 输入图片全屏预览(应用大小浮层):点击右栏输入文件图片打开;深色底、顶栏文件名+下载/关闭,图片居中 contain */
2844
+ .chat-img-preview {
2845
+ position: fixed; inset: 0; z-index: 1100;
2846
+ background: rgba(12, 14, 18, .94);
2847
+ display: flex; flex-direction: column;
2848
+ }
2849
+ .chat-img-preview-head {
2850
+ flex: 0 0 auto; height: 52px; padding: 0 14px;
2851
+ display: flex; align-items: center; justify-content: space-between; gap: 12px;
2852
+ background: var(--panel);
2853
+ border-bottom: 1px solid var(--line);
2854
+ }
2855
+ .chat-img-preview-name {
2856
+ font-size: 13px; font-weight: 600; color: var(--ink);
2857
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
2858
+ }
2859
+ .chat-img-preview-actions { display: flex; gap: 8px; flex: 0 0 auto; }
2860
+ .chat-img-preview-btn {
2861
+ display: inline-flex; align-items: center; justify-content: center;
2862
+ width: 32px; height: 32px; border-radius: 8px;
2863
+ border: 1px solid var(--line); background: var(--field-bg); color: var(--ink);
2864
+ cursor: pointer; text-decoration: none;
2865
+ }
2866
+ .chat-img-preview-btn:hover { background: var(--hover-strong); }
2867
+ .chat-img-preview-btn svg {
2868
+ width: 16px; height: 16px; fill: none; stroke: currentColor;
2869
+ stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
2870
+ }
2871
+ .chat-img-preview-body {
2872
+ flex: 1 1 auto; min-height: 0;
2873
+ display: flex; align-items: center; justify-content: center; padding: 16px;
2874
+ }
2875
+ .chat-img-preview-img {
2876
+ max-width: 100%; max-height: 100%; object-fit: contain;
2877
+ border-radius: 4px; box-shadow: 0 8px 32px rgba(0, 0, 0, .35);
2878
+ }
2879
+
2880
+ /* 用户长输入折叠:折叠尾部渐隐罩 + 居中的"展开全部"按钮,与消息同属一个气泡 */
2881
+ /* 渐隐罩:盖在被截断文字最后几行上,从透明渐变到气泡背景色,配合极轻 blur 形成小毛玻璃过渡 */
2882
+ .chat-expand-fade {
2883
+ height: 26px;
2884
+ margin: -26px -14px 0 -14px; /* 覆盖气泡左右 padding(11px 14px),让渐变横跨内容宽度 */
2885
+ background: linear-gradient(180deg, rgba(240, 240, 240, 0), rgba(240, 240, 240, .96));
2886
+ pointer-events: none;
2887
+ }
2888
+ @supports (backdrop-filter: blur(1px)) {
2889
+ .chat-expand-fade { backdrop-filter: blur(1.2px); }
2890
+ }
2891
+ /* Electron 内核的 backdrop-filter 合成会把下方内容一起糊掉(历史问题),仅保留渐变 */
2892
+ .env-electron .chat-expand-fade { backdrop-filter: none; }
2893
+
2894
+ .chat-expand-toggle {
2895
+ margin-top: 8px;
2896
+ font-size: 12.5px;
2897
+ line-height: 1.4;
2898
+ color: var(--ink-soft);
2899
+ text-align: center;
2900
+ cursor: pointer;
2901
+ user-select: none;
2902
+ transition: color .15s ease;
2903
+ }
2904
+
2905
+ /* hover 用户消息时显示操作栏(消息底部,图标) */
2906
+ .msg.user .msg-hover-actions {
2907
+ opacity: 0;
2908
+ transition: opacity .15s;
2909
+ margin-top: 4px;
2910
+ display: flex;
2911
+ gap: 6px;
2912
+ align-items: center;
2913
+ justify-content: flex-end;
2914
+ }
2915
+ .msg-hover-actions .msg-time {
2916
+ font-size: 12px;
2917
+ color: var(--ink-soft);
2918
+ margin-right: 4px;
2919
+ }
2920
+ .msg-hover-actions button {
2921
+ width: 26px; height: 26px;
2922
+ display: flex; align-items: center; justify-content: center;
2923
+ border: none; background: transparent;
2924
+ color: var(--ink-soft);
2925
+ cursor: pointer;
2926
+ border-radius: 6px;
2927
+ }
2928
+ /* 复制成功反馈:绿色对勾 + 浅绿底,1.8s 后由组件恢复为普通态 */
2929
+ .msg-hover-actions button.is-copied { background: var(--copied-bg); color: var(--copied-ink); }
2930
+ .msg-hover-actions svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
2931
+
2932
+ /* 原地编辑用户消息(workbuddy 浅色风格) */
2933
+ /* 编辑态:原地右对齐(与已发送用户消息一致)。宽度由组件在进入编辑时量取「原气泡宽度」后
2934
+ 以行内样式写到 .msg.user 上——编辑框与编辑前的消息框同宽,而非撑满用户消息最大宽度。
2935
+ ★ .msg.user 必须保留 align-self: flex-end(不能对整个消息行 stretch)——否则消息行占满整行、
2936
+ 编辑框从最左起排,视觉上"闪到中间"。
2937
+ ★★ min-width 是必需的兜底:短消息(如"Hi")的原气泡本身很窄,量出来的宽度装不下
2938
+ 底部「提示语 + 取消/发送」按钮,flex-wrap 会把它们压成竖排一字一行(见 issue:编辑框被改坏)。
2939
+ 因此宽度取 max(原气泡宽, EDIT_MIN_WIDTH)——长消息仍与原消息同宽,短消息至少能放下底部元素。*/
2940
+ .msg.user.is-editing { align-self: flex-end; width: max-content; min-width: min(100%, 420px); max-width: min(100%, 860px); }
2941
+ /* .msg.user 编辑态已带确定的量取宽度,气泡撑满它即可复用自身底色/圆角/内边距,
2942
+ 使可见编辑框与已发送用户消息同宽同形,右缘原地不动、向左铺开(不换行、不跳位)。 */
2943
+ .msg.user.is-editing .bubble { width: 100%; max-width: 100%; }
2944
+ /* 编辑框仅作透明容器:底色/内边距由外层 .bubble 提供,避免双层背景与内边距叠加导致右缘内缩 */
2945
+ .msg-edit-box { width: 100%; box-sizing: border-box; }
2946
+ .msg-edit-textarea {
2947
+ width: 100%;
2948
+ background: transparent;
2949
+ border: none;
2950
+ outline: none;
2951
+ color: var(--ink);
2952
+ /* 与 .msg .bubble 完全一致:字号/行高随已发送用户消息,避免编辑时字体"变小" */
2953
+ font-size: 15.5px;
2954
+ line-height: 1.65;
2955
+ resize: none;
2956
+ max-height: 200px;
2957
+ overflow-y: auto;
2958
+ font-family: inherit;
2959
+ }
2960
+ .msg-edit-footer {
2961
+ margin-top: 10px;
2962
+ display: flex;
2963
+ align-items: center;
2964
+ justify-content: space-between;
2965
+ gap: 8px 12px;
2966
+ flex-wrap: wrap; /* 窄消息框内提示语与按钮换行,避免溢出编辑框 */
2967
+ }
2968
+ /* 提示语允许压缩并省略,优先保住「取消/发送」按钮不被压行 */
2969
+ .msg-edit-hint { font-size: 12px; color: var(--ink-soft); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2970
+ /* 按钮组不参与收缩:任何宽度下都保持「取消」「发送」并排、不换行、不竖排 */
2971
+ .msg-edit-btns { display: flex; gap: 8px; flex: none; white-space: nowrap; }
2972
+ .msg-edit-btns .chat-modal-btn { background: transparent; color: var(--ink-soft); border: 1px solid var(--line); padding: 4px 12px; }
2973
+ .chat-modal-btn--primary { background: var(--send-bg) !important; border-color: var(--send-bg) !important; color: var(--send-ink) !important; }
2974
+
2975
+ /* ============ 移动端适配 ============
2976
+ 断点策略:
2977
+ 1. > 767.98px —— 桌面三栏(左右栏 flex 占位,挤压中栏),维持原布局;
2978
+ 2. ≤ 767.98px —— 移动端:左右栏改为「悬浮抽屉」(fixed 覆盖在中栏之上,
2979
+ 不再占 flex 空间、不挤压中栏),中栏占满整宽;
2980
+ 3. 中栏最小宽度 --chat-min-w(360px):视口小于该值时中栏保持最小宽度不再压缩,
2981
+ 超出部分被壳层裁切(放弃适配,不显示提示)。 */
2982
+
2983
+ /* 移动端抽屉遮罩:默认隐藏(桌面无此元素语义);JS 在左/右抽屉打开时渲染,
2984
+ 点击关闭抽屉。仅在移动端媒体查询下可见 */
2985
+ .chat-mobile-backdrop { display: none; }
2986
+
2987
+ @media (max-width: 767.98px) {
2988
+ /* 移动端浏览器地址栏伸缩:用 dvh 兜底(不支持时回退 100vh) */
2989
+ .chat-shell, .chat-main { height: 100dvh; }
2990
+ /* 中栏最小宽度:视口 < --chat-min-w 时中栏保持最小宽度不再压缩,
2991
+ 超出部分由 .chat-shell 的 overflow-x:hidden 裁切(放弃适配,无提示) */
2992
+ .chat-main { min-width: var(--chat-min-w); }
2993
+
2994
+ /* ---- 左/右栏 → 悬浮抽屉 ---- */
2995
+ .chat-pane-left, .chat-pane-right {
2996
+ position: fixed; top: 0; bottom: 0; z-index: 200;
2997
+ flex: none; overflow: hidden;
2998
+ transition: transform .2s ease;
2999
+ box-shadow: var(--mobile-drawer-shadow);
3000
+ }
3001
+ .chat-pane-left {
3002
+ left: 0; width: 280px;
3003
+ transform: translateX(-100%);
3004
+ }
3005
+ .chat-pane-left.is-open { transform: translateX(0); }
3006
+ .chat-pane-right {
3007
+ right: 0;
3008
+ /* 默认 340px,但不超过视口 88%:窄屏下避免抽屉占满整屏 */
3009
+ width: min(var(--right-w, 340px), 88vw);
3010
+ transform: translateX(100%);
3011
+ }
3012
+ .chat-pane-right.is-open { transform: translateX(0); }
3013
+ .chat-pane-right.is-dragging { transition: none; }
3014
+
3015
+ /* 移动端抽屉无拖拽改宽语义,隐藏 resizer */
3016
+ .chat-resizer { display: none; }
3017
+
3018
+ /* 抽屉遮罩:盖住中栏,点击(JS onClick)关闭抽屉 */
3019
+ .chat-mobile-backdrop {
3020
+ display: block;
3021
+ position: fixed; inset: 0; z-index: 150;
3022
+ background: rgba(20, 27, 38, .32);
3023
+ }
3024
+
3025
+ /* ---- 中栏细节 ---- */
3026
+ .chat-topbar { height: 48px; padding: 0 10px; gap: 8px; }
3027
+ .chat-sidebar-toggle { width: 32px; height: 32px; }
3028
+ .chat-topbar-btn { width: 32px; height: 32px; }
3029
+ .chat-topbar-right { gap: 2px; }
3030
+ .chat-title--center {
3031
+ max-width: 42vw; overflow: hidden; text-overflow: ellipsis;
3032
+ }
3033
+ .chat-title--left { max-width: 34vw; }
3034
+ .chat-search-bar { padding: 6px 10px; gap: 6px; }
3035
+ .chat-search-bar-count { min-width: 28px; }
3036
+ .chat-history-pop,
3037
+ .chat-share-pop { top: 62px; right: 8px; width: min(340px, calc(100vw - 16px)); }
3038
+ .chat-history-list { max-height: 48vh; }
3039
+ .chat-body-inner { max-width: 100%; margin: 0; padding: 14px 14px; gap: 12px; }
3040
+ /* 移动端取消桌面端「内容区按中栏 80% 居中」的约束(否则窄屏左右各留 10% 大留白)。
3041
+ 空态 .is-empty .chat-main-inner 优先级更高,保持自身 32px 内边距不变。 */
3042
+ .chat-main-inner { max-width: 100%; margin: 0; }
3043
+ .msg { max-width: 100%; }
3044
+ .chat-scene-bar { padding: 8px 10px 2px; }
3045
+ .chat-composer-stack { margin: 0 10px 10px; border-radius: 16px; }
3046
+ .chat-composer { padding: 12px 12px 8px; }
3047
+ .chat-workspace { margin: 0 10px 10px; }
3048
+ /* ≥16px 防止 iOS Safari 聚焦输入框时自动放大页面 */
3049
+ .chat-composer textarea { font-size: 16px; }
3050
+ .chat-queue { border-radius: 16px 16px 0 0; }
3051
+
3052
+ /* ---- 右栏抽屉内部 ---- */
3053
+ .chat-right-section--inputs { max-height: 40%; }
3054
+ .chat-right-section--outputs { max-height: 60%; }
3055
+ .chat-right-section--outputs.is-full { max-height: 100%; }
3056
+
3057
+ /* 全局产物预览抽屉:窄屏下全屏铺开,便于阅读 */
3058
+ .artifact-drawer { width: 100vw; border-left: none; }
3059
+ }
3060
+
3061
+ /* ============ 深色主题定向覆盖(变量覆盖不到、需按 data-theme 单独写的几处) ============ */
3062
+ :root[data-theme="dark"] .login-page {
3063
+ background: radial-gradient(1200px 600px at 50% -10%, #2a2d34 0%, var(--bg) 55%);
3064
+ }
3065
+ /* 用户消息长输入折叠渐隐:深色下从气泡深灰渐变 */
3066
+ :root[data-theme="dark"] .chat-expand-fade {
3067
+ background: linear-gradient(180deg, rgba(58, 62, 70, 0), rgba(58, 62, 70, .96));
3068
+ }
3069
+ /* 移动端抽屉遮罩:深色下用更重的黑幕 */
3070
+ :root[data-theme="dark"] .chat-mobile-backdrop { background: rgba(0, 0, 0, .5); }
3071
+ /* 右栏拖拽分隔条:深色下黑条不可见,换白条 */
3072
+ :root[data-theme="dark"] .chat-resizer::after { background: rgba(255, 255, 255, .28); }
3073
+ /* 行内代码(暗色):对齐 WorkBuddy —— 灰色背景 + 近白文字,不用红色 */
3074
+ :root[data-theme="dark"] .bubble .md :not(pre) > code, :root[data-theme="dark"] .artifact-md .md :not(pre) > code{
3075
+ background: rgba(255, 255, 255, .08);
3076
+ border-color: rgba(255, 255, 255, .1);
3077
+ color: #e6e6e6;
3078
+ }
3079
+ /* 引用块文字:深色下浅色 #334155 看不清,提亮 */
3080
+ :root[data-theme="dark"] .bubble .md blockquote, :root[data-theme="dark"] .artifact-md .md blockquote{ color: #c3cfdf; }
3081
+
3082
+
3083
+ /* ============ 移动端 hover 粘滞修复 ============
3084
+ * 触摸设备(hover: none)点击后 :hover 状态会“粘住”,
3085
+ * 全部 :hover 视觉规则统一收敛到 (hover: hover) 设备才生效。
3086
+ * 注:移动端功能性 hover(如侧栏操作按钮、消息操作浮层)由 JS 以点击方式替代。 */
3087
+ @media (hover: hover) and (pointer: fine) {
3088
+ *::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-hover); }
3089
+ .chat-body.sb-active::-webkit-scrollbar-thumb:hover,
3090
+ .chat-right-section-body.sb-active::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-hover); }
3091
+ .login-provider-button:hover { background: var(--hover-strong); }
3092
+ .login-dingtalk-button:hover { background: var(--hover-strong); }
3093
+ .login-dev-button:hover { background: var(--hover-strong); }
3094
+ .chat-sidebar-toggle:hover { background: var(--hover); color: var(--ink); }
3095
+ .chat-bottom-btn:hover { background: var(--hover-strong); color: var(--ink); border-color: var(--line); }
3096
+ .chat-resizer:hover::after, .chat-resizer.is-dragging::after { opacity: 1; }
3097
+ .chat-right-file:hover { background: var(--hover); }
3098
+ .chat-right-file-dl:hover { background: var(--hover); color: var(--ink); }
3099
+ .chat-user-menu-item:hover { background: var(--hover); }
3100
+ .chat-user-menu-item--danger:hover { background: var(--danger-soft); color: var(--danger); }
3101
+ .chat-sidebar-new:hover { background: var(--hover-strong); }
3102
+ .chat-sidebar-item:hover { background: var(--hover-strong); }
3103
+ .chat-sidebar-item:hover .chat-sidebar-item-title.is-marquee .chat-sidebar-item-title-text,
3104
+ .chat-sidebar-item.is-active:hover .chat-sidebar-item-title.is-marquee .chat-sidebar-item-title-text {
3105
+ animation: chatTitleMarquee var(--marquee-dur, 4s) linear infinite;
3106
+ overflow: visible;
3107
+ }
3108
+ .chat-sidebar-item:hover .chat-sidebar-item-title.is-marquee .chat-sidebar-item-title-t2,
3109
+ .chat-sidebar-item.is-active:hover .chat-sidebar-item-title.is-marquee .chat-sidebar-item-title-t2 {
3110
+ display: inline-block;
3111
+ }
3112
+ .chat-sidebar-item:hover .chat-sidebar-item-actions { display: flex; }
3113
+ .chat-sidebar-item:hover .chat-sidebar-item-time { display: none; }
3114
+ .chat-sidebar-item-act:hover { color: var(--ink); }
3115
+ .chat-question-opt:hover { background: var(--hover); }
3116
+ .chat-question-submit:hover:not(:disabled) { opacity: .9; }
3117
+ .chat-topbar-btn:hover { background: var(--hover); color: var(--ink); }
3118
+ .chat-search-bar-btn:hover { background: var(--hover); color: var(--ink); }
3119
+ .chat-history-item:hover { background: var(--brand-soft); }
3120
+ .chat-share-close:hover { background: var(--hover-strong); color: var(--ink); }
3121
+ .chat-share-opt:hover:not(:disabled) { background: var(--hover-strong); }
3122
+ .chat-share-oa-link:hover { background: var(--hover-strong); }
3123
+ .chat-suggestions button:hover { background: var(--brand-soft); }
3124
+ .msg-attachment:hover { background: var(--brand-soft); border-color: var(--brand-line-strong); }
3125
+ .chat-owner-card:hover { border-color: #2563eb; box-shadow: 0 2px 10px rgba(47, 107, 255, .12); }
3126
+ .bubble .md a:hover, .artifact-md .md a:hover{ border-bottom-color: var(--brand); }
3127
+ .chat-thinking-done-toggle:hover { color: var(--ink); }
3128
+ .chat-browser-ask-btn:hover:not(:disabled) { background: var(--active); }
3129
+ .chat-notify-banner-link:hover { text-decoration: underline; }
3130
+ .chat-notify-banner-close:hover { background: var(--hover); color: var(--ink); }
3131
+ .chat-queue-act:hover { background: var(--hover); color: var(--ink); }
3132
+ .chat-queue-act--danger:hover { background: var(--danger-soft); color: var(--danger); }
3133
+ .chat-scene:hover { border-color: var(--brand); color: var(--brand-dark); }
3134
+ .chat-tool:hover { background: var(--hover); }
3135
+ .chat-voice:hover:not(:disabled) { background: var(--hover); }
3136
+ .chat-voice.recording:hover:not(:disabled) { background: var(--danger-soft); color: var(--danger); }
3137
+ .chat-model-picker-btn:hover { background: var(--hover); }
3138
+ .chat-model-picker-item:hover { background: var(--hover); }
3139
+ .chat-send:hover { background: var(--send-bg-hover); }
3140
+ .chat-send:disabled:hover { background: var(--send-bg); transform: none; }
3141
+ .chat-attachment:hover .chat-attachment-icon { opacity: 0; }
3142
+ .chat-attachment:hover .chat-attachment-remove { opacity: 1; transform: scale(1); }
3143
+ .chat-attachment-wrap:hover .chat-attachment-preview { opacity: 1; visibility: visible; transform: translateY(0); }
3144
+ .admin-range button:hover { color: var(--brand-dark); }
3145
+ .admin-table tbody tr:hover { background: var(--hover); }
3146
+ .chat-sat-star:hover { transform: scale(1.15); }
3147
+ .chat-fb-btn:hover { background: var(--hover); color: var(--ink); }
3148
+ .artifact-card:hover {
3149
+ border-color: var(--line);
3150
+ box-shadow: var(--shadow);
3151
+ background: var(--field-soft);
3152
+ }
3153
+ .artifact-menu-dl:hover, .artifact-drawer-close:hover { background: var(--hover); }
3154
+ .artifact-viewall:hover {
3155
+ border-color: var(--line); background: var(--hover);
3156
+ box-shadow: var(--shadow);
3157
+ }
3158
+ .artifact-list-item:hover .artifact-list-item-mainbtn {
3159
+ background: var(--hover);
3160
+ border-color: transparent;
3161
+ }
3162
+ .artifact-list-item:hover .artifact-list-item-name { color: var(--ink); }
3163
+ .artifact-list-item:hover .artifact-list-item-meta { color: var(--ink); }
3164
+ .chat-send--stop:hover { background: #f2f2f2; color: #111; transform: none; }
3165
+ .chat-modal-btn:hover { background: var(--hover-strong); }
3166
+ .chat-modal-btn--danger:hover { background: #d13438; }
3167
+ .chat-expand-toggle:hover { color: var(--ink); }
3168
+ .msg.user:hover .msg-hover-actions { opacity: 1; }
3169
+ .msg-hover-actions button:hover { background: var(--hover-strong); color: var(--ink); }
3170
+ .msg-hover-actions button.is-copied:hover { background: var(--copied-bg); color: var(--copied-ink); }
3171
+ .msg-edit-btns .chat-modal-btn:hover { background: var(--hover-strong); color: var(--ink); }
3172
+ .chat-modal-btn--primary:hover { background: var(--send-bg-hover) !important; }
3173
+ }
3174
+
3175
+ /* ============ 移动端交互体验补强 ============ */
3176
+ /* 触摸设备:点击闪动高亮去掉(黑白灰系) */
3177
+ * { -webkit-tap-highlight-color: transparent; }
3178
+ /* 按钮/链接:touch-action manipulation 消除双击缩放与点击延迟 */
3179
+ button, a, [role="button"], input, textarea, select {
3180
+ touch-action: manipulation;
3181
+ -webkit-touch-callout: none;
3182
+ }
3183
+ /* 滚动容器:overscroll contain 阻止移动端滚动链穿透(下拉刷新/回弹带动页面) */
3184
+ .chat-body, .chat-sidebar-list, .chat-history-list,
3185
+ .chat-right-section-body { overscroll-behavior: contain; }
3186
+
3187
+ /* 触摸设备(hover: none):会话条目不再常显操作按钮——改用长按/右键弹出操作菜单;
3188
+ 时间位恢复正常显示,并禁用长按选中文本 */
3189
+ @media (hover: none) {
3190
+ .chat-sidebar-item .chat-sidebar-item-actions { display: none; }
3191
+ .chat-sidebar-item { user-select: none; -webkit-user-select: none; }
3192
+ /* 用户消息操作浮层(复制/编辑等)常显 */
3193
+ .msg.user .msg-hover-actions { opacity: 1; }
3194
+ /* 附件移除按钮常显(图标隐藏让位) */
3195
+ .chat-attachment .chat-attachment-icon { opacity: 0; }
3196
+ .chat-attachment .chat-attachment-remove { opacity: 1; transform: scale(1); }
3197
+ }
3198
+ /* 触屏(含混合设备,即便被系统识别为可悬停):会话 ⋯ 按钮同样隐藏,长按弹菜单 */
3199
+ @media (pointer: coarse) {
3200
+ .chat-sidebar-item .chat-sidebar-item-actions { display: none; }
3201
+ }
3202
+
3203
+ /* ============ Admin 用量分析页(黑白灰系,无依赖 SVG 图表) ============ */
3204
+ .au-page {
3205
+ min-height: 100vh;
3206
+ background: var(--bg);
3207
+ color: var(--ink);
3208
+ padding: 0 24px 48px;
3209
+ font-size: 13.5px;
3210
+ }
3211
+ .au-topbar {
3212
+ display: flex; align-items: center; gap: 14px;
3213
+ padding: 16px 0 14px; margin-bottom: 18px;
3214
+ border-bottom: 1px solid var(--line);
3215
+ }
3216
+ .au-back {
3217
+ display: inline-flex; align-items: center; gap: 4px;
3218
+ padding: 6px 12px; border-radius: 8px;
3219
+ border: 1px solid var(--line); background: var(--field-bg); color: var(--ink);
3220
+ cursor: pointer; font-size: 13px;
3221
+ }
3222
+ .au-back:hover { background: var(--hover-strong); }
3223
+ .au-back svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
3224
+ .au-title { margin: 0; font-size: 18px; font-weight: 700; }
3225
+ .au-topbar-note { color: var(--ink-soft); font-size: 12px; margin-left: auto; }
3226
+ .au-error {
3227
+ padding: 12px 16px; border-radius: 10px;
3228
+ background: rgba(229, 72, 77, .08); border: 1px solid rgba(229, 72, 77, .35);
3229
+ color: var(--danger); font-size: 13px;
3230
+ }
3231
+ .au-loading {
3232
+ display: flex; align-items: center; gap: 10px;
3233
+ color: var(--ink-soft); font-size: 14px; padding: 40px 0;
3234
+ }
3235
+ /* admin 登录表单(黑白灰系):仅管理员可访问用量页 */
3236
+ .au-login-wrap { display: flex; justify-content: center; padding: 64px 0; }
3237
+ .au-login-card {
3238
+ width: 100%; max-width: 340px;
3239
+ display: flex; flex-direction: column; gap: 12px;
3240
+ background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
3241
+ padding: 24px 22px;
3242
+ }
3243
+ .au-login-title { font-size: 17px; font-weight: 700; }
3244
+ .au-login-sub { font-size: 12.5px; color: var(--ink-soft); margin-bottom: 4px; }
3245
+ .au-login-field { display: flex; flex-direction: column; gap: 5px; font-size: 12.5px; color: var(--ink-soft); }
3246
+ .au-login-field input {
3247
+ border: 1px solid var(--line); border-radius: 8px; background: var(--field-bg);
3248
+ color: var(--ink); padding: 8px 10px; font-size: 13.5px;
3249
+ }
3250
+ .au-login-field input:focus { outline: none; border-color: var(--ink); }
3251
+ .au-login-err { font-size: 12.5px; color: var(--danger); }
3252
+ .au-login-submit {
3253
+ margin-top: 6px; border: none; border-radius: 999px; padding: 9px 0;
3254
+ cursor: pointer; font-size: 13.5px; font-weight: 600;
3255
+ color: var(--panel); background: var(--ink);
3256
+ transition: opacity .15s ease, transform .1s ease;
3257
+ }
3258
+ .au-login-submit:hover:not(:disabled) { opacity: .9; }
3259
+ .au-login-submit:active:not(:disabled) { transform: scale(.98); }
3260
+ .au-login-submit:disabled { background: var(--line); color: var(--ink-soft); cursor: not-allowed; }
3261
+ .au-logout { margin-left: 0; }
3262
+ /* 无 cost_info 数据的空态:整页展示「暂无用量数据」,不报错 */
3263
+ .au-empty-page {
3264
+ padding: 64px 24px; text-align: center;
3265
+ background: var(--panel); border: 1px dashed var(--line); border-radius: 12px;
3266
+ }
3267
+ .au-empty-page-title { font-size: 17px; font-weight: 700; color: var(--ink); margin-bottom: 8px; }
3268
+ .au-empty-page-sub { font-size: 13px; color: var(--ink-soft); line-height: 1.6; }
3269
+ .au-cards { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 26px; }
3270
+ .au-card {
3271
+ flex: 1 1 150px; min-width: 140px;
3272
+ background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
3273
+ padding: 16px 18px;
3274
+ }
3275
+ .au-card-num { font-size: 22px; font-weight: 700; color: var(--ink); margin-bottom: 4px; }
3276
+ .au-card-cap { font-size: 12px; color: var(--ink-soft); }
3277
+ .au-sec { margin-bottom: 28px; }
3278
+ .au-sec-title { margin: 0 0 12px; font-size: 15px; font-weight: 600; }
3279
+ .au-empty { color: var(--ink-soft); font-size: 13px; }
3280
+ .au-hbar { width: 100%; max-width: 720px; height: auto; display: block; }
3281
+ .au-bar-label { fill: var(--ink-soft); }
3282
+ .au-bar-rect { fill: var(--ink); opacity: .82; }
3283
+ .au-bar-val { fill: var(--ink); font-weight: 600; }
3284
+ .au-table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: 12px; background: var(--panel); }
3285
+ .au-table { border-collapse: collapse; width: 100%; min-width: 820px; font-size: 12.5px; }
3286
+ .au-table th, .au-table td {
3287
+ padding: 8px 12px; text-align: left;
3288
+ border-bottom: 1px solid var(--line);
3289
+ white-space: nowrap;
3290
+ }
3291
+ .au-table th { background: var(--hover-strong); color: var(--ink-soft); font-weight: 600; position: sticky; top: 0; }
3292
+ .au-table tbody tr:hover { background: var(--hover); }
3293
+ .au-num { text-align: right; font-variant-numeric: tabular-nums; }
3294
+ .au-strong { font-weight: 700; }
3295
+ .au-mono { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 12px; }
3296
+ .au-preview { max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-soft); }
3297
+ /* 按会话分组合并单元格:第一列合并(Excel 合并样式),单元格内为会话名 + 该会话合计 */
3298
+ .au-col-session { min-width: 150px; }
3299
+ .au-group-cell {
3300
+ background: var(--hover); vertical-align: middle; min-width: 150px;
3301
+ border-top: 1px solid var(--line);
3302
+ }
3303
+ .au-group-title { font-weight: 700; color: var(--ink); font-size: 12.5px; line-height: 1.4; }
3304
+ .au-group-total { font-size: 11px; color: var(--ink-soft); margin-top: 3px; font-variant-numeric: tabular-nums; line-height: 1.4; }
3305
+ /* 会话组之间的分隔:组首行上方一条更明显的线 */
3306
+ .au-table tbody tr.au-row-first td { border-top: 1px solid var(--ink-soft); }
3307
+ .au-foot { color: var(--ink-soft); font-size: 12px; margin-top: 8px; }
3308
+
3309
+ /* ============ 侧边消息滑块(ChatGPT/豆包式 minimap,组件 MessageSlider.tsx) ============
3310
+ 挂在 .chat-main(position:relative)内、聊天区右缘;显隐由 JS 控制(宽度/可滚动判定)。
3311
+ 横线颜色:视口内=--slider-on(亮),滚出视口=--slider-off(灰);hover 加宽+高亮。 */
3312
+ .chat-slider {
3313
+ position: absolute; left: 10px; top: 90px; bottom: 190px; z-index: 30;
3314
+ display: flex; align-items: center;
3315
+ pointer-events: none; /* 容器不拦截,滚轮/点击穿透到聊天区;仅轨道可交互 */
3316
+ opacity: 0; visibility: hidden;
3317
+ transition: opacity .18s ease, visibility .18s;
3318
+ }
3319
+ .chat-slider.is-show { opacity: 1; visibility: visible; }
3320
+ .chat-slider-track { position: relative; width: 64px; pointer-events: auto; }
3321
+ /* 横线:宽度全部由 JS 经 --w 变量给出(非 hover 统一 10px;hover 时按与悬停横线的
3322
+ 距离指数衰减渐变——hover 最宽、相邻次之、远处钳回 10px,友商同款「波峰」过渡)。
3323
+ 3px 视觉高度 + 14px 命中区(background-clip: content-box 只画中间 3px) */
3324
+ .chat-slider-dash {
3325
+ position: absolute; left: 0; height: 14px; padding: 5.5px 0; box-sizing: border-box;
3326
+ border: none; border-radius: 2px; cursor: pointer;
3327
+ background: var(--slider-off); background-clip: content-box;
3328
+ width: var(--w, 10px);
3329
+ transition: width .14s ease, background-color .18s ease;
3330
+ }
3331
+ .chat-slider-dash.is-on { background-color: var(--slider-on); }
3332
+ .chat-slider-dash.is-hover { background-color: var(--slider-hover); }
3333
+ .chat-slider-dash:focus-visible { outline: 1px solid var(--brand); outline-offset: 1px; }
3334
+ /* 提问 tooltip:hover 横线右侧弹出,显示该消息所属轮次的用户提问(点击横线即跳到那个提问)。
3335
+ 定位模型:position: fixed(包含块=视口),left/top 由 JS 从横线按钮 getBoundingClientRect 算出。
3336
+ 这是竖条塌陷的根治方案——absolute + 64px track 包含块会把 shrink-to-fit 可用宽度压到 64px,
3337
+ 中文逐字断行一字一行;fixed 下可用宽度=视口,width:max-content 自然取内容真实宽度,
3338
+ max-width:340px 只在超宽时折行 → 短文字窄气泡、长文字 340px 换行。
3339
+ 最多 5 行:max-height 裁切 + JS 侧 120 字截断加「…」。 */
3340
+ .chat-slider-tip {
3341
+ position: fixed; transform: translateY(-50%);
3342
+ width: max-content; max-width: 340px;
3343
+ max-height: calc(5 * 1.5em + 16px); overflow: hidden;
3344
+ padding: 8px 12px; border-radius: 10px;
3345
+ background: var(--toast-bg); color: var(--ink);
3346
+ border: 1px solid var(--line); box-shadow: var(--shadow);
3347
+ font-size: 12.5px; line-height: 1.5;
3348
+ pointer-events: none; z-index: 31;
3349
+ }
3350
+ .chat-slider-tip-text {
3351
+ display: block;
3352
+ white-space: normal; word-break: break-word;
3353
+ max-width: 340px;
3354
+ }