@jesonliu/lark-claudecode-bridge 1.4.3 → 2.0.1

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 (56) hide show
  1. package/assets/web/css/main.css +774 -574
  2. package/assets/web/index.html +37 -17
  3. package/assets/web/js/auth.js +218 -0
  4. package/assets/web/js/bootstrap.js +36 -34
  5. package/assets/web/js/core.js +4 -3
  6. package/assets/web/js/main.js +79 -20
  7. package/assets/web/js/markdown.js +64 -0
  8. package/assets/web/js/pages/apps.js +338 -251
  9. package/assets/web/js/pages/chats.js +481 -0
  10. package/assets/web/js/pages/claude.js +243 -180
  11. package/assets/web/js/pages/mcp.js +59 -43
  12. package/assets/web/js/pages/memory.js +43 -46
  13. package/assets/web/js/pages/overview.js +426 -88
  14. package/assets/web/js/pages/permissions.js +121 -34
  15. package/assets/web/js/pages/plugins.js +51 -37
  16. package/assets/web/js/pages/skills.js +68 -92
  17. package/assets/web/js/pages/slash.js +31 -23
  18. package/assets/web/js/pages/workspaces.js +114 -73
  19. package/assets/web/js/theme.js +21 -18
  20. package/assets/web/js/ui.js +75 -32
  21. package/dist/access/access-control.d.ts +18 -0
  22. package/dist/access/access-control.js +29 -0
  23. package/dist/access/access-control.js.map +1 -1
  24. package/dist/bin/lcb.js +15 -0
  25. package/dist/bin/lcb.js.map +1 -1
  26. package/dist/feishu/name-cache.d.ts +15 -0
  27. package/dist/feishu/name-cache.js +82 -0
  28. package/dist/feishu/name-cache.js.map +1 -0
  29. package/dist/feishu/sibling-names.d.ts +1 -0
  30. package/dist/feishu/sibling-names.js +63 -0
  31. package/dist/feishu/sibling-names.js.map +1 -0
  32. package/dist/gateway/feishu-gateway.d.ts +24 -0
  33. package/dist/gateway/feishu-gateway.js +132 -0
  34. package/dist/gateway/feishu-gateway.js.map +1 -1
  35. package/dist/gateway/gateway-registry.d.ts +18 -0
  36. package/dist/gateway/gateway-registry.js +14 -0
  37. package/dist/gateway/gateway-registry.js.map +1 -0
  38. package/dist/index.d.ts +4 -0
  39. package/dist/index.js +52 -9
  40. package/dist/index.js.map +1 -1
  41. package/dist/types.d.ts +2 -0
  42. package/dist/web/chats-api.d.ts +93 -0
  43. package/dist/web/chats-api.js +603 -0
  44. package/dist/web/chats-api.js.map +1 -0
  45. package/dist/web/lifecycle.d.ts +21 -0
  46. package/dist/web/lifecycle.js +80 -3
  47. package/dist/web/lifecycle.js.map +1 -1
  48. package/dist/web/logs-api.d.ts +37 -0
  49. package/dist/web/logs-api.js +141 -0
  50. package/dist/web/logs-api.js.map +1 -0
  51. package/dist/web/server.js +196 -13
  52. package/dist/web/server.js.map +1 -1
  53. package/dist/web/webauth.d.ts +44 -0
  54. package/dist/web/webauth.js +221 -0
  55. package/dist/web/webauth.js.map +1 -0
  56. package/package.json +1 -1
@@ -1,574 +1,774 @@
1
- /* ============================================================
2
- Web 配置页样式 · v2 视觉升级
3
- 约定:所有选择器与旧版保持兼容(JS 大量依赖 class/ID 钩子,勿改名)
4
- —— 卡片 / 按钮 / 输入 / 表格 / 标签 / 抽屉 / 弹层 / Toast
5
- —— 自动暗色模式(prefers-color-scheme)+ 减弱动效偏好
6
- ============================================================ */
7
- :root {
8
- color-scheme: light;
9
- /* 色板(飞书系) */
10
- --bg: #eef1f6;
11
- --card: #ffffff;
12
- --field: #ffffff;
13
- --border: #e4e7ee;
14
- --border-strong: #d3d9e4;
15
- --text: #1f2329;
16
- --muted: #646a73;
17
- --primary: #3370ff;
18
- --primary-strong: #2b5fe3;
19
- --primary-weak: #eef3ff;
20
- --primary-ring: rgba(51, 112, 255, .16);
21
- --danger: #f54f4f;
22
- --ok: #2ea121;
23
- --warn: #d97a00;
24
- /* 状态标签底/字色 */
25
- --ok-bg: #e9f7e5;
26
- --warn-bg: #fff3e0;
27
- --err-bg: #feecec;
28
- --off-bg: #f0f1f5;
29
- --ok-tx: #2ea121;
30
- --warn-tx: #d97a00;
31
- --err-tx: #f54f4f;
32
- --off-tx: #8a9099;
33
- /* 未保存条(warn 弱化底) */
34
- --warn-weak: #fff8ef;
35
- --warn-weak-border: #ffe1bb;
36
- /* 代码 / 等宽 */
37
- --code-bg: rgba(31, 35, 41, .06);
38
- --mono: Consolas, "SF Mono", "Cascadia Mono", Menlo, monospace;
39
- /* 圆角与阴影 */
40
- --radius: 14px;
41
- --radius-sm: 9px;
42
- --shadow-1: 0 1px 2px rgba(31, 35, 41, .04), 0 1px 6px rgba(31, 35, 41, .03);
43
- --shadow-2: 0 8px 28px rgba(31, 35, 41, .12);
44
- --shadow-pop: 0 12px 40px rgba(31, 35, 41, .16);
45
- /* 头部 */
46
- --header-bg: rgba(255, 255, 255, .82);
47
- --spin-track: #d9e2f5;
48
- --toast-bg: rgba(31, 35, 41, .93);
49
- }
50
-
51
- @media (prefers-color-scheme: dark) {
52
- :root:not([data-theme="light"]) {
53
- color-scheme: dark;
54
- --bg: #0e1117;
55
- --card: #161a23;
56
- --field: #10141d;
57
- --border: #262c3a;
58
- --border-strong: #363e50;
59
- --text: #e7eaf1;
60
- --muted: #97a0b2;
61
- --primary: #4d80ff;
62
- --primary-strong: #6996ff;
63
- --primary-weak: rgba(77, 128, 255, .14);
64
- --primary-ring: rgba(77, 128, 255, .28);
65
- --danger: #ff7373;
66
- --ok: #5ad24a;
67
- --warn: #ffa23e;
68
- --ok-bg: rgba(52, 199, 36, .13);
69
- --warn-bg: rgba(255, 136, 0, .13);
70
- --err-bg: rgba(245, 79, 79, .13);
71
- --off-bg: rgba(255, 255, 255, .06);
72
- --ok-tx: #5ad24a;
73
- --warn-tx: #ffa23e;
74
- --err-tx: #ff7373;
75
- --off-tx: #97a0b2;
76
- --warn-weak: rgba(255, 136, 0, .09);
77
- --warn-weak-border: rgba(255, 136, 0, .32);
78
- --code-bg: rgba(255, 255, 255, .09);
79
- --shadow-1: 0 1px 2px rgba(0, 0, 0, .3), 0 1px 8px rgba(0, 0, 0, .18);
80
- --shadow-2: 0 8px 28px rgba(0, 0, 0, .45);
81
- --shadow-pop: 0 12px 40px rgba(0, 0, 0, .55);
82
- --header-bg: rgba(19, 23, 31, .82);
83
- --spin-track: rgba(255, 255, 255, .16);
84
- --toast-bg: rgba(58, 64, 78, .95);
85
- }
86
- }
87
-
88
- /* ---------- 基础 ---------- */
89
- * { box-sizing: border-box; }
90
- body {
91
- margin: 0;
92
- font: 14px/1.65 -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
93
- background:
94
- radial-gradient(1000px 480px at 6% -120px, rgba(51, 112, 255, .09), transparent 62%),
95
- radial-gradient(820px 420px at 100% -160px, rgba(150, 110, 255, .06), transparent 58%),
96
- var(--bg);
97
- color: var(--text);
98
- }
99
- ::selection { background: var(--primary-weak); }
100
-
101
- /* 滚动条(Chromium/Safari + Firefox) */
102
- ::-webkit-scrollbar { width: 10px; height: 10px; }
103
- ::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 8px; border: 2px solid transparent; background-clip: padding-box; }
104
- ::-webkit-scrollbar-thumb:hover { background-color: var(--muted); }
105
- ::-webkit-scrollbar-track { background: transparent; }
106
- * { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
107
-
108
- /* ---------- 顶栏 ---------- */
109
- header {
110
- background: var(--header-bg);
111
- -webkit-backdrop-filter: blur(14px) saturate(1.5);
112
- backdrop-filter: blur(14px) saturate(1.5);
113
- border-bottom: 1px solid var(--border);
114
- padding: 0 24px;
115
- height: 56px;
116
- display: flex;
117
- align-items: center;
118
- gap: 14px;
119
- position: sticky;
120
- top: 0;
121
- z-index: 10;
122
- }
123
- .brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
124
- .logo {
125
- width: 30px; height: 30px; border-radius: 9px; flex: 0 0 auto;
126
- display: inline-flex; align-items: center; justify-content: center;
127
- background: linear-gradient(135deg, #3370ff 0%, #5b8cff 60%, #7c5bff 130%);
128
- box-shadow: 0 2px 8px rgba(51, 112, 255, .35);
129
- }
130
- header h1 { font-size: 15.5px; font-weight: 650; margin: 0; letter-spacing: .01em; white-space: nowrap; }
131
- header .meta {
132
- color: var(--muted); font-size: 12px;
133
- background: var(--card); border: 1px solid var(--border);
134
- border-radius: 999px; padding: 2px 11px;
135
- font-variant-numeric: tabular-nums; white-space: nowrap;
136
- }
137
- header .meta:empty { display: none; }
138
- header .spacer { flex: 1; }
139
-
140
- .stale-bar {
141
- background: var(--warn-bg); color: var(--warn-tx);
142
- padding: 9px 24px; font-size: 13px; border-bottom: 1px solid var(--border);
143
- }
144
- .stale-bar[hidden] { display: none; }
145
-
146
- /* ---------- 布局 / 侧栏 ---------- */
147
- .layout { display: flex; min-height: calc(100vh - 56px); }
148
- nav {
149
- width: 196px; flex: 0 0 auto;
150
- padding: 16px 10px 24px;
151
- position: sticky; top: 56px; align-self: flex-start;
152
- max-height: calc(100vh - 56px); overflow-y: auto;
153
- }
154
- nav button {
155
- display: flex; align-items: center; gap: 10px;
156
- width: 100%; padding: 8px 12px; margin: 2px 0;
157
- border: 0; background: none; border-radius: var(--radius-sm);
158
- cursor: pointer; font-size: 13.5px; color: var(--text);
159
- transition: background .15s, color .15s;
160
- position: relative;
161
- }
162
- nav button .ico { display: flex; align-items: center; color: var(--muted); transition: color .15s; }
163
- nav button:hover { background: rgba(127, 140, 170, .10); }
164
- nav button:hover .ico { color: var(--text); }
165
- nav button.active { background: var(--primary-weak); color: var(--primary); font-weight: 600; }
166
- nav button.active .ico { color: var(--primary); }
167
- nav button.active::before {
168
- content: ''; position: absolute; left: -10px; top: 50%; transform: translateY(-50%);
169
- width: 3px; height: 18px; border-radius: 0 3px 3px 0; background: var(--primary);
170
- }
171
- nav button:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
172
-
173
- main { flex: 1; min-width: 0; padding: 24px 36px 56px; }
174
- @media (min-width: 1800px) { main { max-width: 1560px; } }
175
- @media (max-width: 860px) {
176
- header { padding: 0 16px; }
177
- header h1 { font-size: 14px; }
178
- .layout { flex-direction: column; }
179
- nav {
180
- width: auto; max-height: none; position: sticky; top: 56px; z-index: 9;
181
- display: flex; flex-wrap: nowrap; overflow-x: auto; gap: 4px;
182
- padding: 8px 12px; background: var(--header-bg);
183
- -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
184
- border-right: 0; border-bottom: 1px solid var(--border);
185
- }
186
- nav button { width: auto; flex: 0 0 auto; padding: 6px 12px; }
187
- nav button.active::before { display: none; }
188
- main { padding: 16px 16px 40px; }
189
- }
190
-
191
- /* ---------- 页头(main.js 注入:图标 + 标题) ---------- */
192
- .page-head { display: flex; align-items: center; gap: 12px; margin: 2px 0 20px; }
193
- .page-head .ico {
194
- width: 36px; height: 36px; border-radius: 11px; flex: 0 0 auto;
195
- display: inline-flex; align-items: center; justify-content: center;
196
- background: var(--primary-weak); color: var(--primary);
197
- }
198
- .page-head h2 { margin: 0; font-size: 18px; font-weight: 650; letter-spacing: .01em; }
199
- #pageBody { animation: lcb-fade .18s ease; }
200
- @keyframes lcb-fade { from { opacity: 0; transform: translateY(4px); } }
201
-
202
- /* ---------- 卡片 ---------- */
203
- .card {
204
- background: var(--card); border: 1px solid var(--border);
205
- border-radius: var(--radius); padding: 20px 22px; margin-bottom: 18px;
206
- box-shadow: var(--shadow-1);
207
- transition: box-shadow .2s;
208
- }
209
- .card:hover { box-shadow: var(--shadow-2); }
210
- .card h3 { margin: 0 0 4px; font-size: 15px; font-weight: 650; }
211
- .card h3 .dirty-dot { display: none; margin-left: 6px; font-size: 11.5px; color: var(--warn); font-weight: 500; }
212
- .card .desc { color: var(--muted); font-size: 12.5px; margin-bottom: 12px; line-height: 1.7; }
213
- /* 卡内小节(概览「版本与更新」双区块):相邻小节间分隔线,首节不加(免疫未来插入其它 div) */
214
- .card-sub + .card-sub { border-top: 1px solid var(--border); margin-top: 14px; padding-top: 14px; }
215
- .card-sub-title { margin: 0 0 8px; font-size: 13.5px; font-weight: 650; }
216
- main h4 { font-size: 13.5px; font-weight: 600; }
217
-
218
- /* ---------- 表单 ---------- */
219
- label { display: block; font-size: 12px; color: var(--muted); margin: 10px 0 5px; }
220
- input[type=text], input[type=password], input[type=number], input[type=search], select, textarea {
221
- width: 100%; padding: 8px 12px;
222
- border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
223
- font: inherit; background: var(--field); color: var(--text);
224
- transition: border-color .15s, box-shadow .15s;
225
- }
226
- input:hover, select:hover, textarea:hover { border-color: var(--muted); }
227
- input:focus, select:focus, textarea:focus {
228
- outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-ring);
229
- }
230
- input, select, textarea { accent-color: var(--primary); }
231
- textarea { font-family: var(--mono); font-size: 12.5px; min-height: 96px; line-height: 1.6; resize: vertical; }
232
- select { cursor: pointer; }
233
-
234
- .row { display: flex; gap: 12px; }
235
- .row > * { flex: 1; min-width: 0; }
236
- /* 两列布局:配置项多的区块(如全局默认配置)一行 2 个,避免 4 列挤压 */
237
- .row-2col { display: grid; grid-template-columns: repeat(2, 1fr); gap: 4px 16px; }
238
- /* .row 内按钮按内容宽:内联 flex:0 展开会把按钮压到单字宽,一律走类规则 */
239
- .row > .btn, .row > .btn-wrap { flex: 0 0 auto; align-self: end; }
240
- .btn-wrap { display: flex; }
241
- @media (max-width: 860px) { .row { flex-direction: column; } .row-2col { grid-template-columns: 1fr; } }
242
-
243
- /* 输入框 + 行内按钮组合(路径框带「浏览」) */
244
- .input-btn { display: flex; gap: 8px; }
245
- .input-btn > input { flex: 1; min-width: 0; }
246
- .input-btn > .btn { flex: 0 0 auto; align-self: center; }
247
-
248
- /* ---------- 按钮 ---------- */
249
- .btn, nav button, .savebar .btn, .subtabs button, .chip { white-space: nowrap; }
250
- .btn {
251
- padding: 7px 16px; border-radius: var(--radius-sm);
252
- border: 1px solid var(--border-strong); background: var(--card); color: var(--text);
253
- cursor: pointer; font: inherit; font-size: 13.5px;
254
- transition: background .15s, border-color .15s, color .15s, box-shadow .15s, transform .06s;
255
- }
256
- .btn:hover { border-color: var(--primary); color: var(--primary); background: var(--primary-weak); }
257
- .btn:active { transform: translateY(1px); }
258
- .btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--primary-ring); }
259
- .btn:disabled { opacity: .55; cursor: not-allowed; transform: none; }
260
- .btn.primary {
261
- background: var(--primary); border-color: var(--primary); color: #fff;
262
- box-shadow: 0 2px 8px rgba(51, 112, 255, .32);
263
- }
264
- .btn.primary:hover { background: var(--primary-strong); border-color: var(--primary-strong); color: #fff; }
265
- .btn.danger { color: var(--danger); }
266
- .btn.danger:hover { border-color: var(--danger); background: var(--err-bg); color: var(--danger); }
267
- .btn.sm { padding: 4px 11px; font-size: 12.5px; border-radius: 8px; }
268
-
269
- /* ---------- Chips / 代码 ---------- */
270
- .chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0; }
271
- .chip {
272
- display: inline-flex; align-items: center; gap: 6px;
273
- background: var(--card); border: 1px solid var(--border);
274
- padding: 3px 11px; border-radius: 999px;
275
- font-size: 12.5px; font-family: var(--mono); color: var(--text);
276
- }
277
- .chip button { border: 0; background: none; color: var(--muted); cursor: pointer; padding: 0; font-size: 13px; line-height: 1; }
278
- .chip button:hover { color: var(--danger); }
279
- code {
280
- font-family: var(--mono); font-size: .92em;
281
- background: var(--code-bg); border-radius: 5px; padding: 1px 5px;
282
- word-break: break-all;
283
- }
284
- pre.report {
285
- background: var(--field); border: 1px solid var(--border); border-radius: var(--radius-sm);
286
- padding: 12px 14px; font-size: 12px; overflow: auto; white-space: pre-wrap;
287
- }
288
-
289
- /* ---------- 表格 ---------- */
290
- table { width: 100%; border-collapse: collapse; font-size: 13px; }
291
- /* 列表操作列:按钮一律横排(.btn 自身 nowrap,但 td 内模板换行空白给了折行点 → 竖排) */
292
- td.ops { white-space: nowrap; }
293
- th, td { text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--border); vertical-align: top; }
294
- thead th {
295
- color: var(--muted); font-weight: 500; font-size: 12px; letter-spacing: .03em;
296
- border-bottom: 1px solid var(--border-strong);
297
- }
298
- tbody tr { transition: background .12s; }
299
- tbody tr:hover { background: rgba(127, 140, 170, .05); }
300
- tbody tr:last-child td { border-bottom: 0; }
301
- tr.empty-row td { text-align: center; padding: 22px 10px; color: var(--muted); }
302
-
303
- /* ---------- 状态标签 ---------- */
304
- .tag {
305
- display: inline-flex; align-items: center; gap: 5px;
306
- padding: 2px 10px; border-radius: 999px; font-size: 11.5px; font-weight: 500;
307
- white-space: nowrap; /* 状态标签(如「未随本页启动」)不折行 */
308
- }
309
- .tag::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }
310
- .tag.ok { background: var(--ok-bg); color: var(--ok-tx); }
311
- .tag.off { background: var(--off-bg); color: var(--off-tx); }
312
- .tag.off::before { opacity: .5; }
313
- .tag.warn { background: var(--warn-bg); color: var(--warn-tx); }
314
- .tag.err { background: var(--err-bg); color: var(--err-tx); }
315
- /* chip / 按钮内嵌徽标(模型 1M 标记等):紧凑变体,去状态圆点 */
316
- .chip .tag, .btn .tag { font-size: 10px; padding: 1px 6px; gap: 0; }
317
- .chip .tag::before, .btn .tag::before { display: none; }
318
-
319
- /* ---------- 单选行 ---------- */
320
- .radio-row { display: flex; gap: 22px; margin: 8px 0; }
321
- .radio-row label { display: flex; align-items: center; gap: 6px; margin: 0; color: var(--text); font-size: 13.5px; cursor: pointer; }
322
- .radio-row input { accent-color: var(--primary); }
323
-
324
- /* ---------- 开关(原生 checkbox + 伪元素轨道;覆盖全局 label 的 block/margin) ---------- */
325
- .switch-row { display: flex; align-items: center; gap: 10px; margin: 10px 0 8px; }
326
- /* 表格单元格等紧凑容器用:去掉表单区默认的上下边距,避免与同行左侧文字错位 */
327
- .switch-row.tight { margin: 0; }
328
- .switch {
329
- position: relative; display: inline-flex; flex: 0 0 auto;
330
- width: 42px; height: 24px; margin: 0; cursor: pointer;
331
- }
332
- .switch input { position: absolute; width: 0; height: 0; opacity: 0; }
333
- .switch .track {
334
- position: absolute; inset: 0; border-radius: 999px;
335
- background: var(--border-strong); transition: background .16s;
336
- }
337
- .switch .track::after {
338
- content: ''; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px;
339
- border-radius: 50%; background: #fff; box-shadow: var(--shadow-1); transition: transform .16s;
340
- }
341
- /* 开启 = 全部放行(放手模式),用 warn 色提示当前处于高风险态 */
342
- .switch input:checked + .track { background: var(--warn); }
343
- .switch input:checked + .track::after { transform: translateX(18px); }
344
- .switch input:focus-visible + .track { box-shadow: 0 0 0 3px var(--primary-ring); }
345
- .switch-label { font-size: 13.5px; color: var(--text); }
346
- /* 开关开启时名单区暂不生效:整体置灰且不可交互(顺带让「恢复默认」等按钮不可点) */
347
- .block-disabled { opacity: .45; pointer-events: none; }
348
-
349
- /* ---------- 保存条 / 未保存标记 ---------- */
350
- .savebar {
351
- display: flex; gap: 10px; margin-top: 14px; align-items: center;
352
- padding: 9px 12px; border-radius: var(--radius-sm);
353
- background: var(--warn-weak); border: 1px solid var(--warn-weak-border);
354
- }
355
- .savebar .state { font-size: 12px; color: var(--warn-tx); }
356
- .savebar .state::before { content: '● '; font-size: 9px; vertical-align: 1px; }
357
-
358
- /* ---------- 应用子卡片 / 提示 ---------- */
359
- .app-card { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 4px 16px 14px; margin-bottom: 12px; position: relative; }
360
- .app-card .del { position: absolute; top: 10px; right: 12px; }
361
- .hint { font-size: 12px; color: var(--muted); }
362
- .footer-note { color: var(--muted); font-size: 12px; margin-top: 8px; }
363
-
364
- /* ---------- 子页签 ---------- */
365
- .subtabs { display: flex; gap: 8px; margin-bottom: 14px; }
366
- .subtabs button {
367
- padding: 6px 15px; border-radius: 999px; border: 1px solid var(--border-strong);
368
- background: none; cursor: pointer; font: inherit; font-size: 13px; color: var(--text);
369
- transition: background .15s, color .15s, border-color .15s;
370
- }
371
- .subtabs button:hover { border-color: var(--primary); color: var(--primary); }
372
- .subtabs button.active { background: var(--primary-weak); color: var(--primary); border-color: var(--primary); font-weight: 600; }
373
-
374
- /* ---------- Toast ---------- */
375
- #toast {
376
- position: fixed; top: 22px; left: 50%;
377
- transform: translateX(-50%) translateY(-14px);
378
- background: var(--toast-bg); color: #fff;
379
- -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
380
- padding: 10px 22px; border-radius: 999px; font-size: 13px;
381
- box-shadow: var(--shadow-2);
382
- opacity: 0; transition: opacity .25s, transform .25s;
383
- pointer-events: none; max-width: 80vw; z-index: 100;
384
- }
385
- #toast.show { opacity: .97; transform: translateX(-50%) translateY(0); }
386
- #toast.err { background: var(--danger); }
387
-
388
- /* ---------- 桥接器重启 / 离线全页遮罩 ---------- */
389
- .br-mask { position: fixed; inset: 0; background: rgba(15, 19, 24, .5); z-index: 90; display: flex; align-items: center; justify-content: center; animation: lcb-fade .18s ease; }
390
- .br-mask-card {
391
- background: var(--card); border-radius: 16px; padding: 28px 36px; text-align: center;
392
- box-shadow: var(--shadow-pop); max-width: 440px; font-size: 14px;
393
- border: 1px solid var(--border);
394
- }
395
- .br-spin, .br-spin-sm { border-radius: 50%; border-style: solid; border-color: var(--spin-track); border-top-color: var(--primary); animation: brspin .9s linear infinite; }
396
- .br-spin { width: 26px; height: 26px; margin: 0 auto 12px; border-width: 3px; }
397
- /* 表格内小号 spinner(斜杠命令远端拉取等异步加载占位) */
398
- .br-spin-sm { width: 18px; height: 18px; margin: 0 auto 10px; border-width: 2px; }
399
- @keyframes brspin { to { transform: rotate(360deg); } }
400
-
401
- /* ---------- 右侧抽屉(应用 / 斜杠命令 / 档案编辑共用) ---------- */
402
- .drawer-mask { position: fixed; inset: 0; background: rgba(15, 19, 24, .38); z-index: 50; display: flex; justify-content: flex-end; animation: lcb-fade .18s ease; }
403
- .drawer-mask[hidden] { display: none; }
404
- .drawer {
405
- position: relative; /* 拖拽手柄的定位基准 */
406
- width: min(980px, 94vw); height: 100%;
407
- background: var(--card); border-left: 1px solid var(--border);
408
- display: flex; flex-direction: column;
409
- box-shadow: -12px 0 40px rgba(15, 19, 24, .18);
410
- animation: lcb-drawer .22s cubic-bezier(.22, .8, .36, 1);
411
- }
412
- @keyframes lcb-drawer { from { transform: translateX(48px); opacity: .4; } }
413
- /* 左边缘拖拽调宽手柄:8px 命中区(半出界更易命中),视觉条仅 2px、悬停/拖拽时显主题色 */
414
- .drawer-resize { position: absolute; left: -4px; top: 0; bottom: 0; width: 8px; cursor: ew-resize; z-index: 2; touch-action: none; }
415
- .drawer-resize::after { content: ''; position: absolute; left: 3px; top: 0; bottom: 0; width: 2px; background: transparent; transition: background .15s; }
416
- .drawer-resize:hover::after, .drawer-resize.dragging::after { background: var(--primary); }
417
- /* 拖拽期间全局改光标 + 禁选中(防正文文字反白跟随) */
418
- body.drawer-resizing { cursor: ew-resize; user-select: none; }
419
- .drawer > header { display: flex; align-items: center; justify-content: space-between; padding: 0 20px; height: 52px; border-bottom: 1px solid var(--border); position: static; background: none; -webkit-backdrop-filter: none; backdrop-filter: none; }
420
- .drawer > header h3 { margin: 0; font-size: 15px; font-weight: 650; }
421
- .drawer-body { flex: 1; overflow-y: auto; padding: 18px 22px; }
422
- .drawer-foot { padding: 12px 20px; border-top: 1px solid var(--border); display: flex; gap: 10px; align-items: center; }
423
- .drawer-foot .spacer { flex: 1; }
424
-
425
- /* ---------- 居中 modal(目录选择等轻量交互;z-index 低于断连遮罩 br-mask=90) ---------- */
426
- .modal-mask { position: fixed; inset: 0; background: rgba(15, 19, 24, .42); z-index: 80; display: flex; align-items: center; justify-content: center; animation: lcb-fade .16s ease; }
427
- .modal {
428
- width: min(560px, 92vw); background: var(--card); border-radius: 16px; padding: 18px 22px;
429
- box-shadow: var(--shadow-pop); border: 1px solid var(--border);
430
- animation: lcb-pop .18s cubic-bezier(.22, .8, .36, 1);
431
- }
432
- @keyframes lcb-pop { from { transform: scale(.965); opacity: .5; } }
433
- .dir-list { border: 1px solid var(--border); border-radius: var(--radius-sm); max-height: 46vh; overflow-y: auto; margin-top: 10px; }
434
- .dir-list button {
435
- display: block; width: 100%; text-align: left; padding: 8px 12px;
436
- border: 0; border-bottom: 1px solid var(--border); background: none; cursor: pointer; font: inherit;
437
- transition: background .12s, color .12s;
438
- }
439
- .dir-list button:last-child { border-bottom: 0; }
440
- .dir-list button:hover { background: var(--primary-weak); color: var(--primary); }
441
- .dir-list .empty { padding: 14px 12px; color: var(--muted); font-size: 12.5px; text-align: center; }
442
-
443
- /* ---------- 可搜索 combobox(安装插件选择器) ---------- */
444
- .combo { position: relative; }
445
- .combo-panel {
446
- position: absolute; top: calc(100% + 5px); left: 0; right: 0;
447
- background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-sm);
448
- box-shadow: var(--shadow-2); max-height: 320px; overflow-y: auto; z-index: 40;
449
- }
450
- .combo-panel[hidden] { display: none; }
451
- .combo-item { padding: 8px 12px; border-bottom: 1px solid var(--border); cursor: pointer; transition: background .12s; }
452
- .combo-item:last-child { border-bottom: 0; }
453
- .combo-item:hover { background: var(--primary-weak); }
454
- .combo-item b { margin-right: 6px; }
455
- .combo-empty { padding: 12px; color: var(--muted); font-size: 12.5px; }
456
-
457
- /* ---------- 可搜索多选字段(分身白名单):外框对齐输入框观感,chips + 搜索行 ---------- */
458
- .msel { position: relative; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--field); padding: 2px 10px; transition: border-color .15s, box-shadow .15s; }
459
- .msel:focus-within { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-ring); }
460
- .msel-chips { margin: 4px 0; }
461
- .msel-chips:empty { display: none; }
462
- .msel .msel-input { border: 0; background: none; box-shadow: none; width: 100%; padding: 6px 2px; font-size: 13.5px; }
463
- .msel .msel-input:focus { border: 0; box-shadow: none; } /* 覆盖全局 input:focus(焦点态由 .msel:focus-within 呈现);带 .msel 前缀提特异性,防被 input[type=text] (0,1,1) 压制 */
464
- .combo-item.active { background: var(--primary-weak); } /* 键盘高亮(:hover 已有) */
465
- .msel-check { float: right; color: var(--primary); font-weight: 650; }
466
- .chip.ghost { border-style: dashed; color: var(--muted); }
467
-
468
- /* ---------- 操作进度弹框(update-all 真实 i/N,单操作时间缓动模拟) ---------- */
469
- .prog { height: 8px; border-radius: 999px; background: var(--off-bg); border: 1px solid var(--border); overflow: hidden; margin-top: 12px; }
470
- .prog-bar { height: 100%; width: 0%; background: linear-gradient(90deg, var(--primary), var(--primary-strong)); border-radius: 999px; transition: width .3s; }
471
- .prog-bar.done { background: var(--ok); }
472
-
473
- /* ---------- 减弱动效偏好 ---------- */
474
- @media (prefers-reduced-motion: reduce) {
475
- *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
476
- }
477
-
478
- /* ---------- 手动主题(顶栏切换写入 data-theme;auto = 不设属性,走上方系统媒体查询) ---------- */
479
- :root[data-theme="dark"] {
480
- color-scheme: dark;
481
- --bg: #0e1117;
482
- --card: #161a23;
483
- --field: #10141d;
484
- --border: #262c3a;
485
- --border-strong: #363e50;
486
- --text: #e7eaf1;
487
- --muted: #97a0b2;
488
- --primary: #4d80ff;
489
- --primary-strong: #6996ff;
490
- --primary-weak: rgba(77, 128, 255, .14);
491
- --primary-ring: rgba(77, 128, 255, .28);
492
- --danger: #ff7373;
493
- --ok: #5ad24a;
494
- --warn: #ffa23e;
495
- --ok-bg: rgba(52, 199, 36, .13);
496
- --warn-bg: rgba(255, 136, 0, .13);
497
- --err-bg: rgba(245, 79, 79, .13);
498
- --off-bg: rgba(255, 255, 255, .06);
499
- --ok-tx: #5ad24a;
500
- --warn-tx: #ffa23e;
501
- --err-tx: #ff7373;
502
- --off-tx: #97a0b2;
503
- --warn-weak: rgba(255, 136, 0, .09);
504
- --warn-weak-border: rgba(255, 136, 0, .32);
505
- --code-bg: rgba(255, 255, 255, .09);
506
- --shadow-1: 0 1px 2px rgba(0, 0, 0, .3), 0 1px 8px rgba(0, 0, 0, .18);
507
- --shadow-2: 0 8px 28px rgba(0, 0, 0, .45);
508
- --shadow-pop: 0 12px 40px rgba(0, 0, 0, .55);
509
- --header-bg: rgba(19, 23, 31, .82);
510
- --spin-track: rgba(255, 255, 255, .16);
511
- --toast-bg: rgba(58, 64, 78, .95);
512
- }
513
-
514
- /* ---------- 主题切换分段控件(顶栏) ---------- */
515
- .theme-switch {
516
- display: inline-flex; gap: 2px; padding: 3px;
517
- background: var(--card); border: 1px solid var(--border); border-radius: 999px;
518
- }
519
- .theme-switch button {
520
- display: inline-flex; align-items: center; justify-content: center;
521
- width: 30px; height: 24px; border: 0; background: none; border-radius: 999px;
522
- cursor: pointer; color: var(--muted);
523
- transition: background .15s, color .15s;
524
- }
525
- .theme-switch button:hover { color: var(--text); background: rgba(127, 140, 170, .12); }
526
- .theme-switch button.active { background: var(--primary-weak); color: var(--primary); }
527
- .theme-switch svg { display: block; }
528
-
529
- /* ---------- 列表工具栏(各列表页顶部按钮统一形态) ---------- */
530
- .list-toolbar { display: flex; gap: 8px; align-items: center; justify-content: flex-end; margin-bottom: 12px; }
531
- .list-toolbar .spacer { flex: 1; }
532
- .list-search { margin-bottom: 10px; }
533
-
534
- /* ---------- 页内一级 tab(下划线式;权限页等) ---------- */
535
- .tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin-bottom: 18px; }
536
- .tabs button {
537
- padding: 9px 18px; border: 0; background: none; cursor: pointer;
538
- font: inherit; font-size: 14px; color: var(--muted);
539
- border-bottom: 2px solid transparent; margin-bottom: -1px;
540
- transition: color .15s, border-color .15s;
541
- }
542
- .tabs button:hover { color: var(--text); }
543
- .tabs button.active { color: var(--primary); border-bottom-color: var(--primary); font-weight: 600; }
544
-
545
- /* ---------- 风格化确认 / 输入弹窗(替代浏览器原生 confirm / prompt) ---------- */
546
- .dlg { width: min(430px, 92vw); padding: 22px 24px 18px; }
547
- .dlg-title { margin: 0 0 10px; font-size: 15.5px; font-weight: 650; display: flex; align-items: center; gap: 8px; }
548
- .dlg-title .dlg-ico {
549
- width: 26px; height: 26px; border-radius: 8px; flex: 0 0 auto;
550
- display: inline-flex; align-items: center; justify-content: center;
551
- background: var(--err-bg); color: var(--err-tx); font-size: 14px;
552
- }
553
- .dlg-title.danger { color: var(--danger); }
554
- .dlg-msg { font-size: 13.5px; line-height: 1.75; color: var(--text); white-space: pre-line; word-break: break-word; }
555
- .dlg-msg code { font-size: 12.5px; }
556
- .dlg-input { margin-top: 12px; }
557
- .dlg-foot { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }
558
-
559
- /* 实心危险按钮(弹窗确认键):与 .btn.danger(描边)区分 */
560
- .btn.danger-solid { background: var(--danger); border-color: var(--danger); color: #fff; }
561
- .btn.danger-solid:hover { background: var(--danger); border-color: var(--danger); color: #fff; filter: brightness(1.07); }
562
-
563
- /* ---------- 扫码授权二维码(设备流) ---------- */
564
- /* 白底是刻意的:lark-cli 生成的 PNG 本身是白底黑码,容器若跟随暗色主题就会在图片
565
- 周围露出一圈深色,看着像二维码被裁掉。固定白底在任何主题下都与图片本身一致,
566
- 也保证手机扫得出来。 */
567
- .qr-box {
568
- display: flex; justify-content: center; align-items: center;
569
- margin-top: 12px; padding: 12px; background: #fff;
570
- border: 1px solid var(--border); border-radius: 12px; color-scheme: light;
571
- }
572
- .qr-box img { display: block; width: 220px; height: 220px; }
573
- /* 链接可整段选中复制;长 URL 必须能折行,否则会把弹窗撑爆 */
574
- .qr-url { margin-top: 8px; line-height: 1.6; word-break: break-all; user-select: all; }
1
+ /* ═══════════════════════════════════════════════════════════════
2
+ 配置台设计系统(prototype/index.html 重构原型 1:1 落地)
3
+ 结构:Tokens → Base → Topbar → Layout/Sidenav → PageHead → Panel
4
+ → Tiles → Dot/Tag → Buttons → Forms → RCards → Switch → Tables
5
+ → Chips → Tabs → Drawer → Dlg/ModalMask → Toast → Tip → Savebar
6
+ → Banner/Misc → 卡片网格 → 厂商档案 → 工作区 → 会话 → 锁屏
7
+ → UserRow → 现有组件保留区(msel/目录选择/进度/二维码…)→ Responsive
8
+ ═══════════════════════════════════════════════════════════════ */
9
+
10
+ /* ═══ Tokens ═══ */
11
+ :root{
12
+ color-scheme:light;
13
+ --bg:#f3f5f9; --card:#fff; --field:#fff; --sunk:#f6f8fb;
14
+ --border:#e4e8f0; --border2:#cfd6e4;
15
+ --ink:#1c2333; --mut:#697180; --faint:#9aa3b4;
16
+ --pri:#3370ff; --pri-h:#2b5fe3; --pri-w:#edf3ff; --pri-ring:rgba(51,112,255,.18);
17
+ --ok:#1f9d55; --ok-bg:#e7f6ec; --ok-tx:#177c43;
18
+ --warn:#d97706; --warn-bg:#fdf3e2; --warn-tx:#a85d03;
19
+ --err:#e5484d; --err-bg:#fdecec; --err-tx:#c7363b;
20
+ --off-bg:#eef0f5; --off-tx:#7a8294;
21
+ --code-bg:rgba(28,35,51,.06);
22
+ --spin-track:var(--border2);
23
+ --mono:"JetBrains Mono","SF Mono",Consolas,"Cascadia Mono",Menlo,monospace;
24
+ --r:12px; --r-s:8px;
25
+ --shadow:0 1px 2px rgba(16,24,40,.05);
26
+ --shadow-2:0 10px 32px rgba(16,24,40,.14);
27
+ --header-bg:rgba(255,255,255,.86);
28
+ --boot-veil:rgba(255,255,255,.82);
29
+ --nav-w:212px;
30
+ /* 旧变量别名:页面内联样式/历史组件仍引用旧名(--primary/--muted/--danger…),统一桥接到新 tokens */
31
+ --primary:var(--pri); --primary-strong:var(--pri-h); --primary-weak:var(--pri-w); --primary-ring:var(--pri-ring);
32
+ --text:var(--ink); --muted:var(--mut); --border-strong:var(--border2); --danger:var(--err);
33
+ --radius:var(--r); --radius-sm:var(--r-s); --shadow-pop:var(--shadow-2); --accent:var(--pri);
34
+ }
35
+ :root[data-theme="dark"]{
36
+ color-scheme:dark;
37
+ --bg:#0f1218; --card:#161b26; --field:#10141d; --sunk:#1a2030;
38
+ --border:#272e3f; --border2:#3a4358;
39
+ --ink:#e8ebf2; --mut:#98a1b3; --faint:#6b7488;
40
+ --pri:#4d80ff; --pri-h:#6996ff; --pri-w:rgba(77,128,255,.15); --pri-ring:rgba(77,128,255,.3);
41
+ --ok:#43c072; --ok-bg:rgba(46,161,33,.14); --ok-tx:#5ad286;
42
+ --warn:#f09d3e; --warn-bg:rgba(255,136,0,.12); --warn-tx:#ffb45e;
43
+ --err:#ff7076; --err-bg:rgba(229,72,77,.14); --err-tx:#ff8f94;
44
+ --off-bg:rgba(255,255,255,.06); --off-tx:#98a1b3;
45
+ --code-bg:rgba(255,255,255,.09);
46
+ --shadow:0 1px 2px rgba(0,0,0,.3);
47
+ --shadow-2:0 10px 32px rgba(0,0,0,.5);
48
+ --header-bg:rgba(17,21,29,.86);
49
+ --boot-veil:rgba(17,21,29,.82);
50
+ }
51
+
52
+ /* ═══ Base ═══ */
53
+ *{box-sizing:border-box}
54
+ [hidden]{display:none!important} /* 组件类 display 会覆盖 UA 的 [hidden] 样式,此处强制兜底 */
55
+ html{-webkit-text-size-adjust:100%}
56
+ body{
57
+ margin:0;
58
+ font:13.5px/1.65 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;
59
+ background:var(--bg); color:var(--ink);
60
+ }
61
+ ::selection{background:var(--pri-w)}
62
+ ::-webkit-scrollbar{width:10px;height:10px}
63
+ ::-webkit-scrollbar-thumb{background:var(--border2);border-radius:8px;border:2px solid transparent;background-clip:padding-box}
64
+ ::-webkit-scrollbar-track{background:transparent}
65
+ *{scrollbar-width:thin;scrollbar-color:var(--border2) transparent}
66
+ a{color:var(--pri);text-decoration:none}
67
+ a:hover{text-decoration:underline}
68
+ code{font-family:var(--mono);font-size:.9em;background:var(--code-bg);border-radius:5px;padding:1px 5px;word-break:break-all}
69
+ h1,h2,h3,h4{margin:0;font-weight:650}
70
+ button{font:inherit}
71
+ :focus-visible{outline:2px solid var(--pri);outline-offset:2px;border-radius:4px}
72
+
73
+ /* ═══ Topbar ═══ */
74
+ .topbar{
75
+ height:54px; padding:0 20px;
76
+ display:flex; align-items:center; gap:12px;
77
+ background:var(--header-bg);
78
+ -webkit-backdrop-filter:blur(14px) saturate(1.4); backdrop-filter:blur(14px) saturate(1.4);
79
+ border-bottom:1px solid var(--border);
80
+ position:sticky; top:0; z-index:30;
81
+ }
82
+ .brand{display:flex;align-items:center;gap:10px;min-width:0}
83
+ .brand .logo{
84
+ width:28px;height:28px;border-radius:8px;flex:0 0 auto;
85
+ display:inline-flex;align-items:center;justify-content:center;
86
+ background:linear-gradient(135deg,#3370ff,#7c5bff 130%);
87
+ box-shadow:0 2px 8px rgba(51,112,255,.32);
88
+ }
89
+ .brand h1{font-size:14.5px;letter-spacing:.01em;white-space:nowrap}
90
+ .pill{
91
+ font-size:11.5px;color:var(--mut);background:var(--card);
92
+ border:1px solid var(--border);border-radius:999px;padding:1px 10px;
93
+ font-variant-numeric:tabular-nums;white-space:nowrap;
94
+ }
95
+ .pill.accent{background:var(--pri-w);border-color:transparent;color:var(--pri)}
96
+ .topbar .sp{flex:1}
97
+ .theme-btn{
98
+ width:32px;height:30px;border-radius:8px;border:1px solid var(--border);
99
+ background:var(--card);color:var(--mut);cursor:pointer;
100
+ display:inline-flex;align-items:center;justify-content:center;
101
+ transition:color .15s,border-color .15s;
102
+ }
103
+ .theme-btn:hover{color:var(--ink);border-color:var(--border2)}
104
+
105
+ /* ═══ Layout / Sidenav ═══ */
106
+ .layout{display:flex;min-height:calc(100vh - 54px)}
107
+ .sidenav{
108
+ width:var(--nav-w);flex:0 0 auto;
109
+ padding:14px 10px 28px;
110
+ position:sticky;top:54px;align-self:flex-start;
111
+ max-height:calc(100vh - 54px);overflow-y:auto;
112
+ }
113
+ .nav-search{position:relative;margin:0 4px 12px}
114
+ .nav-search svg{position:absolute;left:9px;top:50%;transform:translateY(-50%);color:var(--faint);pointer-events:none}
115
+ .nav-search input{
116
+ width:100%;padding:7px 10px 7px 30px;font:inherit;font-size:12.5px;
117
+ border:1px solid var(--border);border-radius:var(--r-s);
118
+ background:var(--field);color:var(--ink);
119
+ }
120
+ .nav-search input:focus{outline:none;border-color:var(--pri);box-shadow:0 0 0 3px var(--pri-ring)}
121
+ .nav-group{margin-bottom:6px}
122
+ .nav-group-label{
123
+ font-size:11px;color:var(--faint);font-weight:600;letter-spacing:.06em;
124
+ padding:10px 12px 4px;white-space:nowrap;
125
+ }
126
+ .nav-item{
127
+ display:flex;align-items:center;gap:9px;width:100%;
128
+ padding:7px 12px;margin:1px 0;border:0;background:none;border-radius:var(--r-s);
129
+ cursor:pointer;font-size:13px;color:var(--ink);text-align:left;
130
+ transition:background .14s,color .14s;position:relative;
131
+ }
132
+ .nav-item .ico{display:flex;color:var(--mut);transition:color .14s;flex:0 0 auto}
133
+ .nav-item:hover{background:rgba(127,140,170,.1)}
134
+ .nav-item:hover .ico{color:var(--ink)}
135
+ .nav-item.active{background:var(--pri-w);color:var(--pri);font-weight:600}
136
+ .nav-item.active .ico{color:var(--pri)}
137
+ .nav-item .nav-badge{
138
+ margin-left:auto;min-width:16px;height:16px;padding:0 5px;border-radius:999px;
139
+ background:var(--err-bg);color:var(--err-tx);font-size:10px;font-weight:600;
140
+ display:inline-flex;align-items:center;justify-content:center;
141
+ }
142
+ .nav-item.nav-hide{display:none}
143
+
144
+ main{flex:1;min-width:0;padding:22px 30px 90px}
145
+ @media (min-width:1760px){main{max-width:1400px}}
146
+
147
+ /* ═══ Page head ═══ */
148
+ .page-head{display:flex;align-items:flex-start;gap:13px;margin:2px 0 20px;flex-wrap:wrap}
149
+ .page-head .ico{
150
+ width:38px;height:38px;border-radius:11px;flex:0 0 auto;
151
+ display:inline-flex;align-items:center;justify-content:center;
152
+ background:var(--pri-w);color:var(--pri);
153
+ }
154
+ .page-head h2{font-size:18px;letter-spacing:.01em}
155
+ .page-head .page-desc{color:var(--mut);font-size:12.5px;margin-top:2px}
156
+ .page-head .page-actions{margin-left:auto;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
157
+ #view{animation:fadein .18s ease}
158
+ @keyframes fadein{from{opacity:0;transform:translateY(4px)}}
159
+ #pageLegend{margin-top:26px}
160
+
161
+ /* ═══ Panel(统一卡片) ═══ */
162
+ /* overflow:visible:表头 ⓘ 的悬浮提示不能被卡片裁掉(主人反馈 #9);卡内内容自带 padding 不贴角,圆角不受影响 */
163
+ .panel{
164
+ background:var(--card);border:1px solid var(--border);border-radius:var(--r);
165
+ box-shadow:var(--shadow);margin-bottom:16px;overflow:visible;
166
+ }
167
+ .panel-head{
168
+ display:flex;align-items:center;gap:12px;flex-wrap:wrap;
169
+ padding:14px 18px 0;
170
+ }
171
+ .panel-head h3{font-size:14.5px}
172
+ .panel-sub{color:var(--mut);font-size:12px;margin-top:1px}
173
+ .panel-actions{margin-left:auto;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
174
+ .panel-body{padding:14px 18px 18px}
175
+ .panel-foot{
176
+ padding:10px 18px;border-top:1px solid var(--border);
177
+ color:var(--faint);font-size:12px;display:flex;gap:8px;align-items:center;flex-wrap:wrap;
178
+ }
179
+ .panel > hr{border:0;border-top:1px solid var(--border);margin:14px 0}
180
+ .sub-title{font-size:12.5px;font-weight:650;margin:0 0 10px;display:flex;align-items:center;gap:8px}
181
+ /* 兼容别名:首装向导 / 兜底提示等少量历史标记仍用 .card,观感对齐 .panel */
182
+ .card{background:var(--card);border:1px solid var(--border);border-radius:var(--r);box-shadow:var(--shadow);margin-bottom:16px;padding:16px 18px}
183
+ .card > h3{font-size:14.5px}
184
+ .card .desc{color:var(--mut);font-size:12.5px;margin-bottom:12px;line-height:1.7}
185
+
186
+ /* ═══ Status tiles(概览看板) ═══ */
187
+ .tiles{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px;margin-bottom:16px}
188
+ @media (max-width:1280px){.tiles{grid-template-columns:repeat(2,1fr)}}
189
+ @media (max-width:640px){.tiles{grid-template-columns:1fr}}
190
+ .tile{
191
+ background:var(--card);border:1px solid var(--border);border-radius:var(--r);
192
+ box-shadow:var(--shadow);padding:14px 16px 12px;
193
+ display:flex;flex-direction:column;gap:3px;min-width:0;
194
+ }
195
+ .tile-top{display:flex;align-items:center;gap:7px}
196
+ .tile-top .tile-ico{margin-left:auto;color:var(--faint);display:flex}
197
+ .tile-state{font-size:12.5px;font-weight:650}
198
+ .tile-name{font-size:15px;font-weight:650;margin-top:2px}
199
+ .tile-sub{color:var(--mut);font-size:12px;min-height:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
200
+ .tile-actions{display:flex;gap:6px;margin-top:8px}
201
+ .tile-actions .btn{padding:4px 10px;font-size:12px}
202
+
203
+ /* ═══ Dot / Tag ═══ */
204
+ .dot{width:8px;height:8px;border-radius:50%;flex:0 0 auto;display:inline-block}
205
+ .dot.ok{background:var(--ok);box-shadow:0 0 0 3px var(--ok-bg)}
206
+ .dot.off{background:var(--faint)}
207
+ .dot.warn{background:var(--warn);box-shadow:0 0 0 3px var(--warn-bg)}
208
+ .dot.err{background:var(--err);box-shadow:0 0 0 3px var(--err-bg)}
209
+ .tag{
210
+ display:inline-flex;align-items:center;gap:5px;white-space:nowrap;
211
+ padding:1px 9px;border-radius:999px;font-size:11px;font-weight:550;vertical-align:1px;
212
+ }
213
+ .tag.ok{background:var(--ok-bg);color:var(--ok-tx)}
214
+ .tag.warn{background:var(--warn-bg);color:var(--warn-tx)}
215
+ .tag.err{background:var(--err-bg);color:var(--err-tx)}
216
+ .tag.off{background:var(--off-bg);color:var(--off-tx)}
217
+ .tag.info{background:var(--pri-w);color:var(--pri)}
218
+ .tag.ghost{background:none;border:1px dashed var(--border2);color:var(--mut)}
219
+
220
+ /* ═══ Buttons ═══ */
221
+ .btn{
222
+ display:inline-flex;align-items:center;gap:6px;white-space:nowrap;
223
+ padding:6px 14px;border-radius:var(--r-s);
224
+ border:1px solid var(--border2);background:var(--card);color:var(--ink);
225
+ cursor:pointer;font-size:13px;line-height:1.5;
226
+ transition:background .15s,border-color .15s,color .15s,transform .06s;
227
+ }
228
+ .btn:hover{border-color:var(--pri);color:var(--pri);background:var(--pri-w)}
229
+ .btn:active{transform:translateY(1px)}
230
+ .btn:disabled{opacity:.5;cursor:not-allowed}
231
+ .btn.primary{background:var(--pri);border-color:var(--pri);color:#fff;box-shadow:0 1px 6px rgba(51,112,255,.3)}
232
+ .btn.primary:hover{background:var(--pri-h);border-color:var(--pri-h);color:#fff}
233
+ .btn.danger{color:var(--err)}
234
+ .btn.danger:hover{border-color:var(--err);background:var(--err-bg);color:var(--err)}
235
+ .btn.danger-solid{background:var(--err);border-color:var(--err);color:#fff}
236
+ .btn.danger-solid:hover{background:#d6383d;border-color:#d6383d;color:#fff;filter:brightness(1.05)}
237
+ .btn.sm{padding:3px 10px;font-size:12px;border-radius:7px}
238
+ .btn.ghost{border-color:transparent;background:none;color:var(--mut)}
239
+ .btn.ghost:hover{color:var(--pri);background:var(--pri-w);border-color:transparent}
240
+ .btn .spin{
241
+ width:13px;height:13px;border-radius:50%;flex:0 0 auto;
242
+ border:2px solid var(--border2);border-top-color:var(--pri);
243
+ animation:spin .8s linear infinite;
244
+ }
245
+ @keyframes spin{to{transform:rotate(360deg)}}
246
+
247
+ /* ═══ Forms ═══ */
248
+ label.fld{display:block;font-size:12px;color:var(--mut);margin:12px 0 5px}
249
+ label.fld:first-child{margin-top:0}
250
+ input[type=text],input[type=password],input[type=number],input[type=search],input[type=date],select,textarea{
251
+ width:100%;padding:7px 11px;
252
+ border:1px solid var(--border2);border-radius:var(--r-s);
253
+ font:inherit;font-size:13px;background:var(--field);color:var(--ink);
254
+ transition:border-color .15s,box-shadow .15s;
255
+ }
256
+ input:hover,select:hover,textarea:hover{border-color:var(--faint)}
257
+ input:focus,select:focus,textarea:focus{outline:none;border-color:var(--pri);box-shadow:0 0 0 3px var(--pri-ring)}
258
+ textarea{font-family:var(--mono);font-size:12.5px;min-height:90px;line-height:1.6;resize:vertical}
259
+ textarea[style*="font-family:inherit"],textarea.plain{font-family:inherit;font-size:13px}
260
+ select{cursor:pointer}
261
+ input[type=checkbox],input[type=radio]{accent-color:var(--pri)}
262
+ .grid2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2px 18px}
263
+ @media (max-width:860px){.grid2{grid-template-columns:1fr}}
264
+ .grid3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:2px 16px}
265
+ @media (max-width:1100px){.grid3{grid-template-columns:1fr}}
266
+ .frow{display:flex;gap:12px;align-items:flex-start}
267
+ .frow>*{flex:1;min-width:0}
268
+ .frow .btn,.frow .btn-wrap{flex:0 0 auto;align-self:flex-end}
269
+ .input-btn{display:flex;gap:8px}
270
+ .input-btn>input{flex:1;min-width:0}
271
+ .input-btn>.btn{flex:0 0 auto;align-self:center}
272
+ /* 历史表单行(bootstrap 向导 / pickDirectory 顶部) */
273
+ .row{display:flex;gap:12px}
274
+ .row>*{flex:1;min-width:0}
275
+ .row>.btn,.row>.btn-wrap{flex:0 0 auto;align-self:end}
276
+ .row-2col{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 16px;align-items:start}
277
+ .btn-wrap{display:flex}
278
+ @media (max-width:860px){.row{flex-direction:column}.row-2col{grid-template-columns:1fr}}
279
+ .hint{font-size:12px;color:var(--mut)}
280
+ .footer-note{color:var(--mut);font-size:12px;margin-top:8px}
281
+
282
+ /* ═══ Radio cards ═══ */
283
+ .rcards{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;margin:10px 0}
284
+ .rcard{
285
+ border:1px solid var(--border2);border-radius:10px;padding:12px 14px;cursor:pointer;
286
+ display:flex;gap:10px;align-items:flex-start;background:var(--field);
287
+ transition:border-color .15s,background .15s;
288
+ }
289
+ .rcard:hover{border-color:var(--pri)}
290
+ .rcard input{margin-top:3px}
291
+ .rcard.on{border-color:var(--pri);background:var(--pri-w)}
292
+ .rcard b{font-size:13px;display:block}
293
+ .rcard .hint{margin-top:2px}
294
+ /* 历史单选行(bootstrap 向导) */
295
+ .radio-row{display:flex;gap:22px;margin:8px 0;flex-wrap:wrap}
296
+ .radio-row label{display:flex;align-items:center;gap:6px;margin:0;color:var(--ink);font-size:13.5px;cursor:pointer}
297
+
298
+ /* ═══ Switch ═══ */
299
+ .switch-row{display:flex;align-items:center;gap:10px;margin:8px 0}
300
+ .switch{position:relative;display:inline-flex;flex:0 0 auto;width:40px;height:23px;margin:0;cursor:pointer}
301
+ .switch input{position:absolute;width:0;height:0;opacity:0}
302
+ .switch .track{position:absolute;inset:0;border-radius:999px;background:var(--border2);transition:background .16s}
303
+ .switch .track::after{
304
+ content:'';position:absolute;top:2px;left:2px;width:19px;height:19px;border-radius:50%;
305
+ background:#fff;box-shadow:var(--shadow);transition:transform .16s;
306
+ }
307
+ .switch input:checked+.track{background:var(--pri)}
308
+ .switch input:checked+.track::after{transform:translateX(17px)}
309
+ .switch input:focus-visible+.track{box-shadow:0 0 0 3px var(--pri-ring)}
310
+ .switch-label{font-size:13px}
311
+ .switch.tight{width:34px;height:20px}
312
+ .switch.tight .track::after{width:16px;height:16px}
313
+ .switch.tight input:checked+.track::after{transform:translateX(14px)}
314
+
315
+ /* ═══ Tables ═══ */
316
+ table{width:100%;border-collapse:collapse;font-size:13px}
317
+ th,td{text-align:left;padding:9px 10px;border-bottom:1px solid var(--border);vertical-align:top}
318
+ thead th{color:var(--mut);font-weight:550;font-size:11.5px;letter-spacing:.04em;border-bottom:1px solid var(--border2)}
319
+ tbody tr{transition:background .12s}
320
+ tbody tr:hover{background:rgba(127,140,170,.05)}
321
+ tbody tr:last-child td{border-bottom:0}
322
+ tr.empty td{text-align:center;padding:26px 10px;color:var(--mut)}
323
+ td.ops{white-space:nowrap}
324
+ tr.rowlink{cursor:pointer}
325
+ .kv td:first-child{width:150px;color:var(--mut);font-size:12.5px}
326
+
327
+ /* ═══ Chips(可编辑名单) ═══ */
328
+ .chips{display:flex;flex-wrap:wrap;gap:6px;margin:6px 0}
329
+ .echips{display:flex;flex-wrap:wrap;gap:7px;align-items:center;padding:9px 11px;min-height:40px}
330
+ .echips.on-box{border:1px solid var(--border2);border-radius:var(--r-s);background:var(--field)}
331
+ .chip{
332
+ display:inline-flex;align-items:center;gap:6px;
333
+ background:var(--card);border:1px solid var(--border);
334
+ padding:2px 6px 2px 11px;border-radius:999px;
335
+ font-size:12px;font-family:var(--mono);color:var(--ink);white-space:nowrap;
336
+ }
337
+ .chip .x,.chip>button{
338
+ border:0;background:none;color:var(--faint);cursor:pointer;
339
+ font-size:13px;line-height:1;padding:1px 4px;border-radius:50%;
340
+ }
341
+ .chip .x:hover,.chip>button:hover{color:var(--err);background:var(--err-bg)}
342
+ .chip.src{font-family:inherit;background:var(--off-bg);border-color:transparent;color:var(--mut)}
343
+ .chip.ghost{border-style:dashed;color:var(--mut)}
344
+ .chip .tag,.btn .tag{font-size:10px;padding:1px 6px;gap:0}
345
+
346
+ /* ═══ Tabs ═══ */
347
+ .tabs{display:flex;gap:2px;border-bottom:1px solid var(--border);margin-bottom:16px;flex-wrap:wrap}
348
+ .tabs button{
349
+ padding:8px 16px;border:0;background:none;cursor:pointer;font:inherit;font-size:13.5px;
350
+ color:var(--mut);border-bottom:2px solid transparent;margin-bottom:-1px;
351
+ transition:color .15s,border-color .15s;
352
+ }
353
+ .tabs button:hover{color:var(--ink)}
354
+ .tabs button.active{color:var(--pri);border-bottom-color:var(--pri);font-weight:600}
355
+ .tabs .dot{margin-left:5px;vertical-align:1px}
356
+ .subtabs{display:flex;gap:7px;margin-bottom:14px;flex-wrap:wrap}
357
+ .subtabs button{
358
+ padding:5px 14px;border-radius:999px;border:1px solid var(--border2);
359
+ background:none;cursor:pointer;font:inherit;font-size:12.5px;color:var(--ink);
360
+ transition:all .15s;
361
+ }
362
+ .subtabs button:hover{border-color:var(--pri);color:var(--pri)}
363
+ .subtabs button.active{background:var(--pri-w);color:var(--pri);border-color:var(--pri);font-weight:600}
364
+
365
+ /* ═══ Drawer ═══ */
366
+ .mask{position:fixed;inset:0;background:rgba(13,17,26,.42);z-index:50;display:flex;justify-content:flex-end;animation:fadein .16s ease}
367
+ .mask.center{justify-content:center;align-items:center}
368
+ .mask[hidden]{display:none}
369
+ .drawer{
370
+ position:relative; /* 左缘拖拽手柄的定位基准 */
371
+ width:min(960px,94vw);height:100%;background:var(--card);
372
+ border-left:1px solid var(--border);display:flex;flex-direction:column;
373
+ box-shadow:-12px 0 40px rgba(13,17,26,.2);
374
+ animation:slidein .22s cubic-bezier(.22,.8,.36,1);
375
+ }
376
+ .drawer.narrow{width:min(560px,94vw)}
377
+ @keyframes slidein{from{transform:translateX(40px);opacity:.4}}
378
+ /* 左边缘拖拽调宽手柄:8px 命中区,视觉条 2px、悬停/拖拽显主题色(localStorage 记忆 · 双击恢复默认) */
379
+ .drawer-resize{position:absolute;left:-4px;top:0;bottom:0;width:8px;cursor:ew-resize;z-index:2;touch-action:none}
380
+ .drawer-resize::after{content:'';position:absolute;left:3px;top:0;bottom:0;width:2px;background:transparent;transition:background .15s}
381
+ .drawer-resize:hover::after,.drawer-resize.dragging::after{background:var(--pri)}
382
+ body.drawer-resizing{cursor:ew-resize;user-select:none}
383
+ .drawer>header{
384
+ display:flex;align-items:center;gap:12px;padding:0 20px;height:52px;
385
+ border-bottom:1px solid var(--border);flex:0 0 auto;
386
+ }
387
+ .drawer>header h3{font-size:14.5px;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
388
+ .drawer-body{flex:1;overflow-y:auto;padding:18px 22px}
389
+ .drawer-foot{
390
+ padding:12px 20px;border-top:1px solid var(--border);
391
+ display:flex;gap:10px;align-items:center;flex:0 0 auto;
392
+ }
393
+ .drawer-foot .sp{flex:1}
394
+ /* 抽屉内左侧子导航(skill 文件浏览等) */
395
+ .dw-layout{display:flex;gap:20px;align-items:flex-start}
396
+ .dw-nav{width:150px;flex:0 0 auto;position:sticky;top:0;display:flex;flex-direction:column;gap:2px}
397
+ .dw-nav button{
398
+ display:flex;align-items:center;gap:8px;width:100%;padding:7px 11px;
399
+ border:0;background:none;border-radius:var(--r-s);cursor:pointer;
400
+ font:inherit;font-size:12.5px;color:var(--mut);text-align:left;transition:all .14s;
401
+ }
402
+ .dw-nav button:hover{background:rgba(127,140,170,.09);color:var(--ink)}
403
+ .dw-nav button.active{background:var(--pri-w);color:var(--pri);font-weight:600}
404
+ .dw-nav button .cnt{margin-left:auto;font-size:11px;color:var(--faint)}
405
+ .dw-pane{flex:1;min-width:0}
406
+ @media (max-width:760px){.dw-layout{flex-direction:column}.dw-nav{flex-direction:row;flex-wrap:wrap;width:100%;position:static}}
407
+
408
+ /* ═══ Dlg(confirmDialog/promptDialog 自挂载弹窗) ═══ */
409
+ .modal-mask{position:fixed;inset:0;background:rgba(13,17,26,.42);z-index:80;display:flex;align-items:center;justify-content:center;animation:fadein .16s ease}
410
+ .dlg{
411
+ width:min(430px,92vw);background:var(--card);border-radius:14px;padding:20px 22px 16px;
412
+ box-shadow:var(--shadow-2);border:1px solid var(--border);
413
+ animation:pop .16s cubic-bezier(.22,.8,.36,1);
414
+ }
415
+ @keyframes pop{from{transform:scale(.96);opacity:.5}}
416
+ .dlg-title{font-size:15px;display:flex;align-items:center;gap:8px;margin-bottom:10px}
417
+ .dlg-title.danger{color:var(--err)}
418
+ .dlg-msg{font-size:13px;line-height:1.75;white-space:pre-line;word-break:break-word;color:var(--ink)}
419
+ .dlg-input{width:100%;padding:7px 11px;border:1px solid var(--border2);border-radius:var(--r-s);font:inherit;font-size:13px;background:var(--field);color:var(--ink)}
420
+ .dlg-foot{display:flex;justify-content:flex-end;gap:10px;margin-top:18px}
421
+
422
+ /* ═══ Modal(通用居中弹窗:openModal() 用,宽度可配) ═══ */
423
+ .modal{
424
+ background:var(--card);border-radius:14px;border:1px solid var(--border);
425
+ box-shadow:var(--shadow-2);width:min(520px,92vw);max-height:88vh;
426
+ display:flex;flex-direction:column;
427
+ animation:pop .16s cubic-bezier(.22,.8,.36,1);
428
+ }
429
+ .modal-head{
430
+ display:flex;align-items:center;gap:10px;flex:0 0 auto;
431
+ padding:16px 20px 12px;border-bottom:1px solid var(--border);
432
+ }
433
+ .modal-head h3{font-size:14.5px;flex:1;min-width:0}
434
+ .modal-body{padding:16px 20px;overflow-y:auto}
435
+ .modal-foot{
436
+ padding:12px 20px 16px;border-top:1px solid var(--border);flex:0 0 auto;
437
+ display:flex;gap:10px;align-items:center;justify-content:flex-end;
438
+ }
439
+ .modal-foot .sp{flex:1}
440
+
441
+ /* ═══ Toast ═══ */
442
+ #toast{
443
+ position:fixed;top:20px;left:50%;transform:translateX(-50%) translateY(-12px);
444
+ background:rgba(28,35,51,.94);color:#fff;
445
+ -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
446
+ padding:9px 20px;border-radius:999px;font-size:13px;
447
+ box-shadow:var(--shadow-2);opacity:0;transition:opacity .25s,transform .25s;
448
+ pointer-events:none;max-width:80vw;z-index:300;
449
+ }
450
+ #toast.show{opacity:.97;transform:translateX(-50%) translateY(0)}
451
+ #toast.err{background:var(--err)}
452
+
453
+ /* ═══ Tooltip(信息收纳:ⓘ 悬浮显示) ═══ */
454
+ .tip{position:relative;display:inline-flex;color:var(--faint);cursor:help;vertical-align:-2px}
455
+ .tip:hover{color:var(--pri)}
456
+ .tip::after{
457
+ content:attr(data-tip);
458
+ position:absolute;left:50%;bottom:calc(100% + 7px);transform:translateX(-50%) translateY(4px);
459
+ background:rgba(28,35,51,.95);color:#fff;font-size:11.5px;font-weight:400;line-height:1.6;
460
+ padding:7px 11px;border-radius:8px;width:max-content;max-width:280px;white-space:normal;
461
+ opacity:0;pointer-events:none;transition:opacity .15s,transform .15s;z-index:60;
462
+ text-align:left;font-family:inherit;
463
+ }
464
+ .tip:hover::after{opacity:1;transform:translateX(-50%) translateY(0)}
465
+ /* 卡片头部的 ⓘ 向下弹:贴近视口顶部时向上弹会被裁掉(主人反馈 #9) */
466
+ .panel-actions .tip::after{bottom:auto;top:calc(100% + 7px);transform:translateX(-50%) translateY(-4px)}
467
+ .panel-actions .tip:hover::after{transform:translateX(-50%) translateY(0)}
468
+
469
+ /* ═══ Savebar(未保存条;页内卡级保存行复用同类名) ═══ */
470
+ .savebar{
471
+ position:fixed;bottom:22px;left:50%;transform:translateX(-50%);
472
+ background:var(--card);border:1px solid var(--warn);border-radius:999px;
473
+ box-shadow:var(--shadow-2);padding:8px 10px 8px 18px;
474
+ display:flex;align-items:center;gap:12px;z-index:40;
475
+ animation:rise .2s ease;
476
+ }
477
+ @keyframes rise{from{transform:translate(-50%,10px);opacity:0}}
478
+ .savebar .state{font-size:12.5px;color:var(--warn-tx);display:flex;align-items:center;gap:7px}
479
+ .savebar[hidden]{display:none}
480
+ .savebar[style*="display:none"],.savebar[style*="display: none"]{position:static;transform:none;box-shadow:none;border:0;padding:10px 0 0;animation:none;background:none}
481
+ .savebar[style*="display:none"] .state,.savebar[style*="display: none"] .state{color:var(--warn-tx)}
482
+ .dirty-dot{display:none;margin-left:6px;font-size:11.5px;color:var(--warn-tx);font-weight:500}
483
+ /* 区域整体置灰禁用(记忆页/权限页联动开关用) */
484
+ .block-disabled{opacity:.45;pointer-events:none}
485
+
486
+ /* ═══ Banner / Empty / Misc ═══ */
487
+ .stale-bar{background:var(--warn-bg);color:var(--warn-tx);padding:9px 24px;font-size:13px;border-bottom:1px solid var(--border)}
488
+ .stale-bar[hidden]{display:none}
489
+ .banner{border-radius:10px;padding:9px 13px;font-size:12.5px;line-height:1.7;display:flex;gap:8px;align-items:flex-start}
490
+ .banner.warn{background:var(--warn-bg);color:var(--warn-tx)}
491
+ .banner.err{background:var(--err-bg);color:var(--err-tx)}
492
+ .banner.info{background:var(--pri-w);color:var(--pri)}
493
+ .banner b{font-weight:650}
494
+ .empty{text-align:center;padding:30px 16px;color:var(--mut)}
495
+ .empty .btn{margin-top:10px}
496
+ .mono{font-family:var(--mono);font-size:12px}
497
+ .mut{color:var(--mut)}
498
+ .dim{color:var(--faint)}
499
+ details.adv{border:1px solid var(--border);border-radius:10px;padding:0 14px;background:var(--sunk)}
500
+ details.adv>summary{cursor:pointer;font-size:12.5px;color:var(--mut);padding:10px 0;list-style:none;display:flex;align-items:center;gap:6px}
501
+ details.adv>summary::before{content:'▸';transition:transform .15s;font-size:10px}
502
+ details.adv[open]>summary::before{transform:rotate(90deg)}
503
+ details.adv>.adv-body{padding:2px 2px 14px}
504
+ .mini-note{font-size:11.5px;color:var(--faint);line-height:1.7}
505
+
506
+ /* ═══ App 卡片网格 ═══ */
507
+ .cardgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:14px}
508
+ .acard{
509
+ background:var(--card);border:1px solid var(--border);border-radius:var(--r);
510
+ box-shadow:var(--shadow);padding:16px;cursor:pointer;
511
+ transition:border-color .15s,box-shadow .15s;display:flex;flex-direction:column;gap:9px;
512
+ }
513
+ .acard:hover{border-color:var(--pri);box-shadow:var(--shadow-2)}
514
+ .acard-head{display:flex;align-items:center;gap:8px}
515
+ .acard-head b{font-size:14.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
516
+ .acard-head .dot{margin-left:auto}
517
+ .acard-meta{display:flex;flex-wrap:wrap;gap:6px 14px;color:var(--mut);font-size:12px}
518
+ .acard-meta span{display:inline-flex;align-items:center;gap:5px;min-width:0}
519
+ .acard-foot{display:flex;gap:6px;margin-top:2px}
520
+ .acard.newcard{
521
+ border-style:dashed;border-color:var(--border2);background:none;
522
+ align-items:center;justify-content:center;color:var(--mut);min-height:130px;
523
+ box-shadow:none;gap:6px;
524
+ }
525
+ .acard.newcard:hover{color:var(--pri);border-color:var(--pri);background:var(--pri-w)}
526
+
527
+ /* ═══ pickChips 弹层(供沿用 combo 结构的组件用) ═══ */
528
+ .pick{position:relative}
529
+ .pick .pick-pop{
530
+ position:absolute;left:0;right:0;top:calc(100% + 4px);z-index:30;
531
+ background:var(--card);border:1px solid var(--border2);border-radius:10px;
532
+ box-shadow:var(--shadow-2);max-height:220px;overflow-y:auto;padding:4px;
533
+ }
534
+ .pick .pick-pop[hidden]{display:none}
535
+ .pick .pick-opt{
536
+ display:flex;align-items:center;gap:8px;width:100%;text-align:left;
537
+ border:0;background:none;cursor:pointer;font:inherit;font-size:12.5px;color:var(--ink);
538
+ padding:6px 9px;border-radius:7px;transition:background .12s;
539
+ }
540
+ .pick .pick-opt:hover{background:var(--pri-w)}
541
+ .pick .pick-opt .sub{color:var(--faint);font-size:11px;margin-left:auto;font-family:var(--mono)}
542
+ .pick .pick-empty{padding:10px;color:var(--faint);font-size:12px;text-align:center}
543
+
544
+ /* ═══ 可搜索多选/单选字段(multiSelectField / singleSelectField) ═══ */
545
+ .msel{position:relative;border:1px solid var(--border2);border-radius:var(--r-s);background:var(--field);padding:2px 10px;transition:border-color .15s,box-shadow .15s}
546
+ .msel:focus-within{border-color:var(--pri);box-shadow:0 0 0 3px var(--pri-ring)}
547
+ .msel-chips{margin:4px 0}
548
+ .msel-chips:empty{display:none}
549
+ .msel .msel-input{border:0;background:none;box-shadow:none;width:100%;padding:6px 2px;font-size:13px}
550
+ .msel .msel-input:focus{border:0;box-shadow:none}
551
+ .combo{position:relative}
552
+ .combo-panel,.msel .combo-panel{
553
+ position:absolute;left:-1px;right:-1px;top:calc(100% + 5px);
554
+ background:var(--card);border:1px solid var(--border2);border-radius:10px;
555
+ box-shadow:var(--shadow-2);max-height:280px;overflow-y:auto;z-index:40;
556
+ }
557
+ .combo-panel[hidden]{display:none}
558
+ .combo-item{padding:8px 12px;border-bottom:1px solid var(--border);cursor:pointer;transition:background .12s}
559
+ .combo-item:last-child{border-bottom:0}
560
+ .combo-item:hover{background:var(--pri-w)}
561
+ .combo-item.active{background:var(--pri-w)}
562
+ .combo-item b{margin-right:6px}
563
+ .combo-empty{padding:12px;color:var(--mut);font-size:12.5px}
564
+ .msel-check{float:right;color:var(--pri);font-weight:650}
565
+
566
+ /* ═══ 目录选择弹层(pickDirectory,文件管理器风格) ═══ */
567
+ .modal.dp{width:min(680px,94vw)}
568
+ .dp-head{display:flex;align-items:center;gap:12px;margin-bottom:12px}
569
+ .dp-head b{font-size:15px;flex:0 0 auto}
570
+ .dp-path{flex:1;min-width:0;text-align:left;color:var(--mut);font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
571
+ .dp-toolbar{display:flex;gap:8px;margin-bottom:10px}
572
+ .dp-toolbar input{flex:1;min-width:0}
573
+ .dp-toolbar .btn{flex:0 0 auto}
574
+ .dp-grid{
575
+ display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:6px;
576
+ max-height:52vh;overflow-y:auto;padding:10px;
577
+ border:1px solid var(--border);border-radius:10px;background:var(--sunk);
578
+ }
579
+ .dp-item{
580
+ display:flex;align-items:center;gap:8px;min-width:0;
581
+ padding:8px 10px;border:1px solid transparent;border-radius:8px;
582
+ background:var(--card);color:var(--ink);cursor:pointer;font:inherit;font-size:13px;text-align:left;
583
+ transition:border-color .12s,color .12s;
584
+ }
585
+ .dp-item svg{flex:0 0 auto;color:var(--warn)}
586
+ .dp-item span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
587
+ .dp-item:hover{border-color:var(--pri);color:var(--pri)}
588
+ .dp-empty{padding:22px 12px;color:var(--mut);font-size:12.5px;text-align:center;grid-column:1/-1}
589
+ .dp-foot{display:flex;align-items:center;gap:10px;margin-top:12px}
590
+ /* 会话页 AI 气泡:Markdown 渲染后的内容块间距(p/pre 等自带 inline style)与原文纯文本 */
591
+ .m-bubble .m-raw{white-space:pre-wrap}
592
+ .m-bubble pre{max-width:100%;overflow-x:auto}
593
+
594
+ /* ═══ 厂商档案卡(加宽版网格) ═══ */
595
+ .cardgrid.wide{grid-template-columns:repeat(auto-fill,minmax(420px,1fr))}
596
+ @media (max-width:960px){.cardgrid.wide{grid-template-columns:1fr}}
597
+ /* 档案单选小卡(生效配置 Tab) */
598
+ .pfcards{display:flex;flex-wrap:wrap;gap:10px}
599
+ .pfcard{
600
+ border:1px solid var(--border2);border-radius:10px;padding:10px 14px;cursor:pointer;
601
+ background:var(--field);min-width:180px;transition:border-color .15s,background .15s;
602
+ }
603
+ .pfcard:hover{border-color:var(--pri)}
604
+ .pfcard.on{border-color:var(--pri);background:var(--pri-w);box-shadow:0 0 0 3px var(--pri-ring)}
605
+ .pfcard b{font-size:13px;display:flex;align-items:center;gap:7px}
606
+ .pfcard .hint{margin-top:3px;font-size:11.5px}
607
+
608
+ /* ═══ 工作区卡片列表 ═══ */
609
+ .ws-list{display:flex;flex-direction:column}
610
+ .ws-item{
611
+ display:flex;align-items:center;gap:13px;padding:12px 4px;
612
+ border-bottom:1px solid var(--border);
613
+ }
614
+ .ws-item:last-child{border-bottom:0}
615
+ .ws-item .ws-ico{
616
+ width:36px;height:36px;border-radius:10px;flex:0 0 auto;
617
+ display:inline-flex;align-items:center;justify-content:center;
618
+ background:var(--warn-bg);color:var(--warn);
619
+ }
620
+ .ws-item .ws-main{flex:1;min-width:120px}
621
+ .ws-item .ws-name{font-size:13.5px;font-weight:600;display:flex;align-items:center;gap:8px}
622
+ .ws-item .ws-path{color:var(--mut);font-size:11.5px;font-family:var(--mono);margin-top:2px;word-break:break-all}
623
+ .ws-item .ws-ops{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}
624
+ /* 选目录弹窗内已选路径卡 */
625
+ .picked-path{
626
+ display:flex;align-items:center;gap:11px;border:1px solid var(--border);
627
+ border-radius:10px;padding:11px 14px;background:var(--sunk);
628
+ }
629
+ .picked-path .ws-ico{width:32px;height:32px;border-radius:9px}
630
+ .picked-path b{font-size:13px}
631
+ .picked-path .hint{font-size:11.5px;color:var(--mut)}
632
+
633
+ /* ═══ 会话页(查询条件 + 会话列表 + 消息流) ═══ */
634
+ .chats-layout{display:grid;grid-template-columns:290px minmax(0,1fr);gap:14px;align-items:start;position:relative}
635
+ @media (max-width:1080px){.chats-layout{grid-template-columns:1fr}}
636
+ /* 整页加载层:进页初始链路(树→话题→时间线)期间盖住列表+详情区,完成/出错即被 JS 移除 */
637
+ .chats-boot{position:absolute;inset:0;z-index:5;display:flex;align-items:center;justify-content:center;background:var(--boot-veil);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:fadein .16s ease}
638
+ .chats-boot-box{display:flex;flex-direction:column;align-items:center;color:var(--mut);font-size:12.5px}
639
+ .chat-list-panel{padding:0!important;max-height:calc(100vh - 278px);overflow-y:auto}
640
+ @media (max-width:1080px){.chat-list-panel{max-height:340px}}
641
+ /* 消息流固定高度滚动(主人反馈 #4c:不随内容无限拉高) */
642
+ .chats-scroll{max-height:calc(100vh - 330px);overflow-y:auto;padding:2px 6px 2px 2px}
643
+ /* 任务块(关键词定位的锚点):默认透明,命中高亮时描边 */
644
+ .chat-task{padding:4px 6px;border-radius:10px;transition:background .15s}
645
+ .chat-task.kw-active{background:var(--pri-w);outline:1.5px solid var(--pri)}
646
+ mark.kw-mark{background:var(--warn-bg);color:var(--warn-tx);border-radius:3px;padding:0 2px}
647
+ .chat-obj{
648
+ display:flex;gap:10px;padding:11px 14px;cursor:pointer;
649
+ border-bottom:1px solid var(--border);transition:background .12s;align-items:flex-start;
650
+ }
651
+ .chat-obj:last-child{border-bottom:0}
652
+ .chat-obj:hover{background:rgba(127,140,170,.06)}
653
+ .chat-obj.active{background:var(--pri-w)}
654
+ .chat-obj .c-ava{
655
+ width:34px;height:34px;border-radius:9px;flex:0 0 auto;
656
+ display:inline-flex;align-items:center;justify-content:center;
657
+ background:var(--pri-w);color:var(--pri);font-size:12.5px;font-weight:650;
658
+ }
659
+ /* 群聊会话头像:中性底与私聊(品牌色)区分(原型 chats-redesign 同款) */
660
+ .chat-obj .c-ava.group{background:var(--off-bg);color:var(--mut)}
661
+ .chat-obj .c-main{flex:1;min-width:0}
662
+ .chat-obj .c-top{display:flex;align-items:center;gap:6px}
663
+ .chat-obj .c-name{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
664
+ .chat-obj .c-time{margin-left:auto;color:var(--faint);font-size:11px;flex:0 0 auto}
665
+ .chat-obj .c-prev{color:var(--mut);font-size:11.5px;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
666
+ .chat-obj .c-topic{font-size:12px;font-weight:600;color:var(--ink);margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
667
+ .chat-obj .c-badges{display:flex;gap:5px;margin-top:4px;align-items:center}
668
+ .cmsg-day{display:flex;align-items:center;gap:12px;color:var(--faint);font-size:11.5px;margin:16px 0 10px}
669
+ .cmsg-day::before,.cmsg-day::after{content:'';flex:1;border-top:1px solid var(--border)}
670
+ .cmsg{display:flex;margin:8px 0;gap:9px}
671
+ .cmsg .m-ava{
672
+ width:28px;height:28px;border-radius:8px;flex:0 0 auto;margin-top:2px;
673
+ display:inline-flex;align-items:center;justify-content:center;
674
+ background:var(--off-bg);color:var(--mut);font-size:11px;font-weight:650;
675
+ }
676
+ .cmsg .m-body{max-width:min(76%,740px)}
677
+ .cmsg .m-meta{font-size:11px;color:var(--faint);margin-bottom:3px;display:flex;gap:8px;align-items:center}
678
+ .cmsg .m-bubble{
679
+ background:var(--card);border:1px solid var(--border);border-radius:11px;
680
+ padding:8px 13px;font-size:13px;line-height:1.7;word-break:break-word;
681
+ }
682
+ .cmsg.me{flex-direction:row-reverse}
683
+ .cmsg.me .m-body{display:flex;flex-direction:column;align-items:flex-end}
684
+ .cmsg.me .m-bubble{background:var(--pri-w);border-color:transparent}
685
+ .cmsg .m-link{font-size:11.5px;margin-top:4px}
686
+ .cmsg .m-full{margin-top:6px;font-size:12.5px;color:var(--mut);background:var(--sunk);border:1px dashed var(--border2);border-radius:9px;padding:9px 12px;line-height:1.7;white-space:pre-wrap}
687
+ .cmsg .m-full[hidden]{display:none}
688
+ .csum{
689
+ border:1px solid var(--border);border-radius:11px;background:var(--card);
690
+ padding:11px 14px;margin:10px 0 10px 37px;max-width:min(76%,640px);
691
+ }
692
+ .csum-top{display:flex;align-items:center;gap:8px}
693
+ .csum-top b{font-size:13px}
694
+ .csum-top .tag{margin-left:auto}
695
+ .csum-stats{display:flex;flex-wrap:wrap;gap:6px 18px;margin-top:8px;font-size:12px;color:var(--mut)}
696
+ .csum-stats b{color:var(--ink);font-variant-numeric:tabular-nums}
697
+
698
+ /* ═══ 设置 / 密码锁屏 ═══ */
699
+ .lockmask{
700
+ position:fixed;inset:0;z-index:200;display:flex;align-items:center;justify-content:center;
701
+ background:rgba(13,17,26,.55);
702
+ -webkit-backdrop-filter:blur(18px) saturate(1.2);backdrop-filter:blur(18px) saturate(1.2);
703
+ animation:fadein .18s ease;
704
+ }
705
+ .lockmask[hidden]{display:none}
706
+ .lockcard{
707
+ width:min(360px,92vw);background:var(--card);border:1px solid var(--border);
708
+ border-radius:18px;box-shadow:var(--shadow-2);padding:30px 28px 24px;text-align:center;
709
+ animation:pop .18s cubic-bezier(.22,.8,.36,1);
710
+ }
711
+ .lockcard .lock-logo{
712
+ width:46px;height:46px;border-radius:13px;margin:0 auto 14px;
713
+ display:inline-flex;align-items:center;justify-content:center;
714
+ background:linear-gradient(135deg,#3370ff,#7c5bff 130%);color:#fff;
715
+ box-shadow:0 4px 14px rgba(51,112,255,.35);
716
+ }
717
+ .lockcard h3{font-size:16px}
718
+ .lockcard .lock-sub{color:var(--mut);font-size:12.5px;margin:5px 0 18px}
719
+ .lockcard input{margin-bottom:10px;text-align:center;letter-spacing:.2em}
720
+ .lock-err{color:var(--err-tx);font-size:12px;min-height:18px;margin-bottom:6px}
721
+ .lock-tip{color:var(--faint);font-size:11px;margin-top:12px;line-height:1.7}
722
+ @keyframes shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-7px)}75%{transform:translateX(7px)}}
723
+ .shake{animation:shake .28s ease}
724
+
725
+ /* ═══ User row(记忆用户 / 机器人行) ═══ */
726
+ .urow{display:flex;align-items:center;gap:12px;padding:11px 0;border-bottom:1px solid var(--border);flex-wrap:wrap}
727
+ .urow:last-child{border-bottom:0}
728
+ .urow .u-ava{
729
+ width:32px;height:32px;border-radius:9px;flex:0 0 auto;
730
+ display:inline-flex;align-items:center;justify-content:center;
731
+ background:var(--pri-w);color:var(--pri);font-size:13px;font-weight:650;
732
+ }
733
+ .urow .u-main{flex:1;min-width:180px}
734
+ .urow .u-name{font-size:13px;font-weight:600;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
735
+ .urow .u-sub{color:var(--mut);font-size:11.5px;margin-top:1px}
736
+ .urow .u-ops{display:flex;gap:6px;flex-wrap:wrap}
737
+
738
+ /* ═══ 现有组件保留区(进度 / 二维码 / 运行遮罩) ═══ */
739
+ .prog{height:8px;border-radius:999px;background:var(--off-bg);border:1px solid var(--border);overflow:hidden;margin-top:12px}
740
+ .prog-bar{height:100%;width:0%;background:linear-gradient(90deg,var(--pri),var(--pri-h));border-radius:999px;transition:width .3s}
741
+ .prog-bar.done{background:var(--ok)}
742
+ pre.report{background:var(--field);border:1px solid var(--border);border-radius:var(--r-s);padding:12px 14px;font-size:12px;overflow:auto;white-space:pre-wrap;margin:0}
743
+ .br-spin,.br-spin-sm{border-radius:50%;border-style:solid;border-color:var(--spin-track);border-top-color:var(--pri);animation:brspin .9s linear infinite}
744
+ .br-spin{width:26px;height:26px;margin:0 auto 12px;border-width:3px}
745
+ .br-spin-sm{width:18px;height:18px;margin:0 auto 10px;border-width:2px}
746
+ @keyframes brspin{to{transform:rotate(360deg)}}
747
+ /* 扫码授权二维码:固定白底(lark-cli 生成的 PNG 本身白底黑码,主题跟随会在暗色下露边) */
748
+ .qr-box{
749
+ display:flex;justify-content:center;align-items:center;
750
+ margin-top:12px;padding:12px;background:#fff;
751
+ border:1px solid var(--border);border-radius:12px;color-scheme:light;
752
+ }
753
+ .qr-box img{display:block;width:220px;height:220px}
754
+ .qr-url{margin-top:8px;line-height:1.6;word-break:break-all;user-select:all}
755
+
756
+ /* ═══ Responsive ═══ */
757
+ @media (max-width:900px){
758
+ .layout{flex-direction:column}
759
+ .sidenav{
760
+ width:auto;max-height:none;position:sticky;top:54px;z-index:20;
761
+ display:flex;gap:4px;overflow-x:auto;padding:8px 12px;
762
+ background:var(--header-bg);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);
763
+ border-bottom:1px solid var(--border);
764
+ }
765
+ .nav-search{display:none}
766
+ .nav-group{display:contents}
767
+ .nav-group-label{display:none}
768
+ .nav-item{width:auto;flex:0 0 auto;padding:6px 12px}
769
+ main{padding:16px 16px 90px}
770
+ .page-head .page-actions{margin-left:0;width:100%}
771
+ }
772
+ @media (prefers-reduced-motion:reduce){
773
+ *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}
774
+ }