contactsheet 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.
@@ -0,0 +1,1189 @@
1
+ /* ============================================================
2
+ contactsheet 画布样式
3
+ 颜色/尺寸的唯一出处 = 下面的 :root 变量,组件里不写死色值
4
+ ============================================================ */
5
+
6
+ :root {
7
+ color-scheme: dark; /* 原生控件(checkbox/select/滚动条)跟随暗色,否则暗面板上一块纯白 */
8
+
9
+ /* ---------- 1. 表面(不透明) ----------
10
+ 判据用 L* 差而不是 WCAG 对比度:近黑区的对比度公式被 +0.05 常数压平,
11
+ 四级表面算出来全是 1.07,看着"都是同一个黑"却"达标"。这里按相邻 ΔL*≈6 排,
12
+ 才是真的能看出层次(原值与设计稿给的值都只有 ΔL*≈3.5,等于没分层)。 */
13
+ --cs-bg: #09090b; /* 画布底(zinc-950) L* 3 */
14
+ --cs-bg-sunken: #050506; /* 走查底 */
15
+ --cs-s1: #18181b; /* 一级:顶栏/HUD/面板/卡片 L* 10 */
16
+ --cs-s2: #232327; /* 二级:输入框/行/chip L* 15 */
17
+ --cs-s3: #2e2e33; /* 三级:行 hover/键帽 L* 21 */
18
+ --cs-s4: #3b3b41; /* 四级:按下/选中行 L* 27 */
19
+
20
+ /* ---------- 2. 描边(一律 alpha-white:浮层压在用户的白页面上也成立) ---------- */
21
+ --cs-line: rgba(255, 255, 255, 0.07);
22
+ --cs-line-2: rgba(255, 255, 255, 0.12);
23
+ --cs-line-3: rgba(255, 255, 255, 0.20);
24
+ --cs-line-opaque: #23262c; /* 必须不透明处:画板 frame */
25
+ --cs-scrim: rgba(6, 7, 9, 0.72);
26
+ --cs-scrim-strong:rgba(6, 7, 9, 0.88);
27
+
28
+ /* ---------- 3. 文字 ---------- */
29
+ --cs-fg: #fafafa; /* zinc-50 */
30
+ --cs-fg-2: #a1a1aa; /* zinc-400 */
31
+ --cs-fg-3: #8b8b93; /* 元信息,对 --cs-s1 有 5.1:1 */
32
+ --cs-fg-4: #52525b; /* zinc-600,禁用 */
33
+
34
+ /* ---------- 4. 语义色 ----------
35
+ shadcn 的暗色主题是中性灰阶 + 白色主操作,没有品牌彩色。这里只保留必需的状态色:
36
+ 选中/主操作用近白(不是紫),hover 描边用中性亮线(不是青),
37
+ 状态色只在真的表示状态时出现(open/已解决/危险),且都压低饱和度。 */
38
+ --cs-accent: #fafafa; /* 选中 / 主操作:白,shadcn 的 primary */
39
+ --cs-accent-hi: #ffffff;
40
+ --cs-accent-solid: #fafafa; /* 实心主按钮底(配深色文字) */
41
+ --cs-fg-on-accent: #0a0a0a;
42
+ --cs-hover: #a1a1aa; /* 反查 hover 描边:中性亮灰 */
43
+ --cs-warn: #d4d4d8; /* pin(open):中性,靠形状而不是颜色说话 */
44
+ --cs-ok: #71717a; /* 已解决:更暗的灰,自然退到背景里 */
45
+ --cs-danger: #ef4444; /* 只有"删除/错误"才配用红 —— 全站唯一的彩色 */
46
+ --cs-kind-component:#71717a; /* kind 圆点:灰阶区分,不用彩色编码 */
47
+ --cs-kind-screen: #d4d4d8;
48
+ --cs-accent-08: rgba(250, 250, 250, 0.08);
49
+ --cs-accent-16: rgba(250, 250, 250, 0.14);
50
+ --cs-accent-32: rgba(250, 250, 250, 0.28);
51
+ --cs-hover-12: rgba(161, 161, 170, 0.14);
52
+ --cs-warn-16: rgba(212, 212, 216, 0.16);
53
+ --cs-ok-16: rgba(113, 113, 122, 0.18);
54
+ --cs-danger-16: rgba(239, 68, 68, 0.16);
55
+
56
+ /* ---------- 5. 字体:mono 只给机器串(selector/id/尺寸),人读的一律 sans ---------- */
57
+ --cs-ff-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter",
58
+ "Helvetica Neue", "PingFang SC", "Microsoft YaHei", sans-serif;
59
+ --cs-ff-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
60
+ --cs-fs-micro: 10px;
61
+ --cs-fs-xs: 11px;
62
+ --cs-fs-sm: 12px;
63
+ --cs-fs-md: 13px;
64
+ --cs-fs-lg: 15px;
65
+ --cs-lh-tight: 1.2;
66
+ --cs-lh: 1.45;
67
+ --cs-fw-medium: 500;
68
+ --cs-fw-bold: 600;
69
+ --cs-track-tight: -0.011em;
70
+ --cs-track-wide: 0.06em;
71
+
72
+ /* ---------- 6. 间距(4 基) ---------- */
73
+ --cs-sp-1: 2px; --cs-sp-2: 4px; --cs-sp-3: 6px; --cs-sp-4: 8px;
74
+ --cs-sp-5: 12px; --cs-sp-6: 16px; --cs-sp-7: 20px; --cs-sp-8: 24px;
75
+
76
+ /* ---------- 7. 圆角 ---------- */
77
+ --cs-r-1: 4px; --cs-r-2: 6px; --cs-r-3: 8px; --cs-r-4: 12px; --cs-r-full: 999px;
78
+
79
+ /* ---------- 8. 阴影:环境光 + 主光 + 顶缘高光(暗色 UI"有厚度"的关键) ---------- */
80
+ --cs-e1: 0 1px 2px rgba(0, 0, 0, 0.40);
81
+ --cs-e2: 0 2px 6px rgba(0, 0, 0, 0.35), 0 8px 24px -6px rgba(0, 0, 0, 0.50);
82
+ --cs-e3: 0 4px 12px rgba(0, 0, 0, 0.40), 0 24px 48px -12px rgba(0, 0, 0, 0.60);
83
+ --cs-edge: inset 0 1px 0 rgba(255, 255, 255, 0.06);
84
+ --cs-focus: 0 0 0 2px var(--cs-bg), 0 0 0 4px var(--cs-accent-32);
85
+
86
+ /* ---------- 9. 动效 ---------- */
87
+ --cs-dur-1: 90ms;
88
+ --cs-dur-2: 160ms;
89
+ --cs-dur-3: 240ms;
90
+ --cs-ease: cubic-bezier(0.2, 0, 0, 1);
91
+ --cs-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
92
+
93
+ /* ---------- 10. 层级 ---------- */
94
+ --cs-z-pins: 2; --cs-z-board-up: 5; --cs-z-fx: 20;
95
+ --cs-z-chrome: 30; --cs-z-panel: 40; --cs-z-overlay: 60; --cs-z-boot: 70;
96
+
97
+ /* ---------- 11. 布局常量(JS 侧 wall.ts 的 TITLE_H 必须等于 --cs-title-h) ---------- */
98
+ --cs-title-h: 24px;
99
+
100
+ /* ---------- 12. 运行时(JS 写入) ---------- */
101
+ --cs-inv: 1;
102
+ }
103
+
104
+ /* 兼容别名:老选择器一行不动就能跑,后续增量迁移 */
105
+ :root {
106
+ --cs-bg-dot: rgba(255, 255, 255, 0.055);
107
+ --cs-surface: var(--cs-s1);
108
+ --cs-surface-2: var(--cs-s2);
109
+ --cs-surface-3: var(--cs-s3);
110
+ --cs-line-strong: var(--cs-line-2);
111
+ --cs-text: var(--cs-fg);
112
+ --cs-text-dim: var(--cs-fg-2);
113
+ --cs-text-faint: var(--cs-fg-3);
114
+ --cs-accent-weak: var(--cs-accent-16);
115
+ --cs-hover-weak: var(--cs-hover-12);
116
+ --cs-pin: var(--cs-warn);
117
+ --cs-badge-component: var(--cs-kind-component);
118
+ --cs-badge-screen: var(--cs-kind-screen);
119
+ --cs-radius: var(--cs-r-4);
120
+ --cs-radius-sm: var(--cs-r-2);
121
+ --cs-shadow: var(--cs-e3);
122
+ --cs-shadow-sm: var(--cs-e1);
123
+ --cs-font: var(--cs-ff-sans);
124
+ --cs-mono: var(--cs-ff-mono);
125
+ }
126
+
127
+ @media (prefers-reduced-motion: reduce) {
128
+ *, *::before, *::after {
129
+ transition-duration: 1ms !important;
130
+ animation-duration: 1ms !important;
131
+ }
132
+ }
133
+
134
+ * { box-sizing: border-box; }
135
+
136
+ /* 全站唯一焦点环。透明 outline 是给 Windows 强制颜色模式的回退——那种模式会抹掉
137
+ box-shadow,只有 outline 会被系统赋色顶上 */
138
+ :where(button, select, input, textarea, [tabindex]):focus-visible {
139
+ outline: 2px solid transparent;
140
+ outline-offset: 2px;
141
+ box-shadow: var(--cs-focus);
142
+ }
143
+
144
+ /* 滚动条外观:只用标准属性,颜色取自变量(不与 ::-webkit-scrollbar 混用) */
145
+ .cs-args-body, #cs-refs, .cs-field textarea {
146
+ scrollbar-width: thin;
147
+ scrollbar-color: var(--cs-line-2) transparent;
148
+ overscroll-behavior: contain; /* 滚到底不把画布/页面一起带走 */
149
+ }
150
+
151
+ html, body {
152
+ height: 100%;
153
+ margin: 0;
154
+ overflow: hidden;
155
+ background: var(--cs-bg);
156
+ color: var(--cs-fg);
157
+ font: var(--cs-fs-md) / var(--cs-lh) var(--cs-ff-sans);
158
+ letter-spacing: var(--cs-track-tight);
159
+ -webkit-font-smoothing: antialiased;
160
+ text-rendering: optimizeLegibility;
161
+ }
162
+
163
+ /* ---------- 视口与世界 ---------- */
164
+
165
+ #cs-viewport {
166
+ position: fixed;
167
+ inset: 0;
168
+ overflow: hidden;
169
+ background-image: radial-gradient(var(--cs-bg-dot) 1px, transparent 1px);
170
+ background-size: 24px 24px;
171
+ cursor: default;
172
+ }
173
+ #cs-viewport.is-panning { cursor: grabbing; }
174
+ body[data-pin="on"] #cs-viewport { cursor: crosshair; }
175
+
176
+ #cs-world {
177
+ position: absolute;
178
+ top: 0;
179
+ left: 0;
180
+ transform-origin: 0 0;
181
+ will-change: transform;
182
+ }
183
+
184
+ /* ---------- 分组(一个文件 = 一列) ---------- */
185
+
186
+ .cs-group {
187
+ position: absolute;
188
+ top: 0;
189
+ left: 0;
190
+ }
191
+
192
+ .cs-group-title {
193
+ display: flex;
194
+ align-items: baseline;
195
+ gap: 8px;
196
+ height: 22px;
197
+ padding: 0 2px;
198
+ color: var(--cs-text-dim);
199
+ font: 600 12px/22px var(--cs-mono);
200
+ letter-spacing: 0.04em;
201
+ white-space: nowrap;
202
+ }
203
+ .cs-group-title .cs-group-count {
204
+ color: var(--cs-text-faint);
205
+ font-weight: 400;
206
+ }
207
+
208
+ /* ---------- 画板 ---------- */
209
+
210
+ .cs-board {
211
+ position: absolute;
212
+ left: 0;
213
+ }
214
+
215
+ /* 标题条按内容收缩,不再拉满画板宽——否则名字在最左、"3 args"在最右,
216
+ 中间 400px 真空,看起来像两个无关的东西 */
217
+ .cs-board-title {
218
+ display: inline-flex;
219
+ align-items: center;
220
+ gap: var(--cs-sp-3);
221
+ width: fit-content;
222
+ max-width: 100%;
223
+ height: var(--cs-title-h);
224
+ padding: 0 var(--cs-sp-3) 0 var(--cs-sp-2);
225
+ border-radius: var(--cs-r-1) var(--cs-r-1) 0 0;
226
+ background: transparent;
227
+ cursor: pointer;
228
+ user-select: none;
229
+ white-space: nowrap;
230
+ transition: background var(--cs-dur-1) var(--cs-ease);
231
+ }
232
+ .cs-board-title:hover { background: var(--cs-s2); }
233
+ .cs-board-title .cs-name {
234
+ flex: 1 1 auto; /* 名字优先占位;被裁的该是徽标不是名字 */
235
+ min-width: 0;
236
+ color: var(--cs-fg-2);
237
+ font: var(--cs-fw-medium) var(--cs-fs-sm)/1 var(--cs-ff-sans);
238
+ text-overflow: ellipsis;
239
+ overflow: hidden;
240
+ transition: color var(--cs-dur-1) var(--cs-ease);
241
+ }
242
+ .cs-board:hover .cs-name,
243
+ .cs-board.is-active .cs-name { color: var(--cs-fg); }
244
+ /* 次要元信息:平时几乎隐形,hover 才现身 */
245
+ .cs-board-title .cs-args-n {
246
+ flex: none;
247
+ color: var(--cs-fg-3);
248
+ font: var(--cs-fs-micro)/1 var(--cs-ff-mono);
249
+ opacity: 0;
250
+ transition: opacity var(--cs-dur-1) var(--cs-ease);
251
+ }
252
+ .cs-board:hover .cs-args-n, .cs-board.is-active .cs-args-n { opacity: 1; }
253
+
254
+ /* kind 徽标降级成 6px 圆点:"这是组件"是零信息量内容,不该是全屏最亮的东西 */
255
+ .cs-badge {
256
+ flex: none;
257
+ width: 6px;
258
+ height: 6px;
259
+ padding: 0;
260
+ border-radius: var(--cs-r-full);
261
+ background: currentColor;
262
+ font-size: 0; /* 文字留给 title 属性,不显示 */
263
+ overflow: hidden;
264
+ }
265
+ .cs-badge[data-kind="component"] { color: var(--cs-kind-component); }
266
+ .cs-badge[data-kind="screen"] { color: var(--cs-kind-screen); }
267
+
268
+ /* 画板是"标本卡":iframe 的画布底在浏览器里必然不透明(把 html/body 设成 transparent
269
+ 也没用,计算值透明、渲染仍是白),所以不装作它能裸放——给它边框、圆角、阴影,
270
+ 让那块白读作有意为之的展台,而不是一块漂在暗底上没轮廓的色块。 */
271
+ .cs-frame {
272
+ position: relative;
273
+ /* 用 outline 不用 border:border 会吃掉 2px 内容宽,env.width:390 实得 388,
274
+ 媒体查询会误命中更窄的断点 —— 声明宽度必须就是 iframe 视口宽度 */
275
+ outline: 1px solid var(--cs-line-opaque);
276
+ /* 不加圆角也不加阴影:带这两样,白底读起来是"又浮了一张卡片",而高亮框是直角矩形,
277
+ 形状对不上。画板的底就该读作画板本身的面,边界交给那条 1px outline */
278
+ background: #fff;
279
+ overflow: hidden;
280
+ transition: outline-color var(--cs-dur-1) var(--cs-ease);
281
+ }
282
+ .cs-board:hover .cs-frame {
283
+ outline-color: var(--cs-line-3);
284
+ }
285
+ .cs-frame iframe {
286
+ display: block;
287
+ width: 100%;
288
+ height: 100%;
289
+ border: 0;
290
+ background: transparent; /* 画布底由 .cs-frame 决定,iframe 自己不再叠一层 */
291
+ }
292
+
293
+ /* 未挂载占位 */
294
+ .cs-ph[hidden] { display: none; }
295
+ .cs-ph {
296
+ display: flex;
297
+ align-items: center;
298
+ justify-content: center;
299
+ width: 100%;
300
+ height: 100%;
301
+ background: var(--cs-surface-2);
302
+ color: var(--cs-text-faint);
303
+ font: 12px var(--cs-mono);
304
+ text-align: center;
305
+ padding: 8px;
306
+ }
307
+
308
+ /* 事件拦截层:浏览模式吃掉鼠标,反查 iframe 内元素 */
309
+ .cs-ovl {
310
+ position: absolute;
311
+ inset: 0;
312
+ pointer-events: auto;
313
+ }
314
+
315
+ /* 交互模式:当前板放行事件 + 亮边框,其余压暗 */
316
+ body[data-mode="interact"] .cs-board.is-active .cs-ovl { pointer-events: none; }
317
+ /* 交互模式:整块画板亮起来 —— 可交互的是整个页面,不是某个元素。
318
+ 压在白色页面上时 1px 细线几乎看不见,所以用 2px 外环 + 光晕。 */
319
+ body[data-mode="interact"] .cs-board.is-active .cs-frame {
320
+ outline: 2px solid var(--cs-accent);
321
+ outline-offset: 1px;
322
+ box-shadow: 0 0 0 6px var(--cs-accent-16), var(--cs-e3);
323
+ }
324
+ body[data-mode="interact"] .cs-board.is-active .cs-board-title {
325
+ background: var(--cs-accent-16);
326
+ }
327
+ body[data-mode="interact"] .cs-board.is-active .cs-name { color: var(--cs-accent-hi); }
328
+ /* 交互/走查模式下 pin 只展示不拦截:别挡住用户要点的元素(批注恰恰钉在最常点的地方) */
329
+ body[data-mode="interact"] .cs-pin,
330
+ body[data-mode="review"] .cs-pin {
331
+ pointer-events: none;
332
+ opacity: 0.35;
333
+ }
334
+ body[data-mode="interact"] .cs-board:not(.is-active) { opacity: 0.4; }
335
+ body[data-mode="interact"] .cs-section-title { opacity: 0.3; }
336
+
337
+ /* 走查模式:只留当前板,世界变换已由 JS 缩放到铺满 */
338
+ body[data-mode="review"] .cs-board:not(.is-active),
339
+ body[data-mode="review"] .cs-section-title { visibility: hidden; }
340
+ body[data-mode="review"] .cs-board.is-active .cs-board-title { display: none; }
341
+ body[data-mode="review"] .cs-board.is-active .cs-ovl { pointer-events: none; }
342
+ body[data-mode="review"] .cs-board.is-active .cs-frame {
343
+ border-radius: var(--cs-radius-sm);
344
+ }
345
+
346
+ /* 浏览模式下,当前活动板给个低调的提示边(Enter 会走查它) */
347
+ body[data-mode="browse"] .cs-board.is-active .cs-board-title {
348
+ border-color: var(--cs-line-strong);
349
+ }
350
+
351
+ /* ---------- pin 批注 ---------- */
352
+
353
+ .cs-pins {
354
+ /* 挂在 board 层(不进 overflow:hidden 的 frame),对齐到 frame 区域:标题条以下 */
355
+ position: absolute;
356
+ inset: var(--cs-title-h) 0 0 0;
357
+ pointer-events: none;
358
+ z-index: 2; /* 盖住标题条与 frame,气泡向上弹时不被裁、不被截击 */
359
+ }
360
+
361
+ .cs-pin {
362
+ position: absolute;
363
+ width: 18px;
364
+ height: 18px;
365
+ margin: 0;
366
+ border: 2px solid #0008;
367
+ border-radius: 50%;
368
+ background: var(--cs-pin);
369
+ color: var(--cs-fg-on-accent);
370
+ font: 700 10px/14px var(--cs-mono);
371
+ text-align: center;
372
+ cursor: pointer;
373
+ pointer-events: auto;
374
+ transform: translate(-50%, -50%) scale(var(--cs-inv));
375
+ transform-origin: 50% 50%;
376
+ }
377
+ /* 用 color-mix 调暗圆点自身,不能用 opacity——父节点透明会把里面的气泡一起弄糊 */
378
+ .cs-pin.is-resolved { background: color-mix(in srgb, var(--cs-ok) 62%, var(--cs-bg)); }
379
+ .cs-pin.is-orphan { background: var(--cs-danger); }
380
+
381
+ .cs-pin-bubble {
382
+ position: absolute;
383
+ bottom: 22px;
384
+ left: 50%;
385
+ width: 268px; /* 220 太窄:4 个中文按钮会被压成「删/除」竖排 */
386
+ padding: 8px 9px;
387
+ border: 1px solid var(--cs-line-strong);
388
+ border-radius: var(--cs-radius-sm);
389
+ background: var(--cs-surface);
390
+ color: var(--cs-text);
391
+ font: 12px/1.45 var(--cs-font);
392
+ text-align: left;
393
+ box-shadow: var(--cs-shadow);
394
+ transform: translateX(-50%);
395
+ display: none;
396
+ z-index: 3;
397
+ white-space: pre-wrap;
398
+ }
399
+ .cs-pin:hover .cs-pin-bubble { display: block; }
400
+ .cs-pin-bubble .cs-pin-meta {
401
+ display: flex;
402
+ flex-wrap: wrap; /* 放不下就换行,而不是把按钮压成竖排 */
403
+ align-items: center;
404
+ gap: var(--cs-sp-2);
405
+ margin-top: var(--cs-sp-4);
406
+ padding-top: var(--cs-sp-3);
407
+ border-top: 1px solid var(--cs-line);
408
+ color: var(--cs-fg-3);
409
+ font: var(--cs-fs-micro)/1 var(--cs-ff-sans);
410
+ letter-spacing: var(--cs-track-wide);
411
+ text-transform: uppercase;
412
+ }
413
+ .cs-pin-meta button {
414
+ flex: 0 0 auto; /* 不许被压缩 */
415
+ white-space: nowrap; /* 不许逐字换行 */
416
+ min-width: 48px; /* 两字中文按钮也有像样的点击区 */
417
+ padding: var(--cs-sp-2) var(--cs-sp-5);
418
+ border: 1px solid var(--cs-line-2);
419
+ border-radius: var(--cs-r-1);
420
+ background: var(--cs-s2);
421
+ color: var(--cs-fg-2);
422
+ font: var(--cs-fw-medium) var(--cs-fs-xs)/1.1 var(--cs-ff-sans);
423
+ letter-spacing: var(--cs-track-tight);
424
+ text-transform: none;
425
+ cursor: pointer;
426
+ transition: background var(--cs-dur-1) var(--cs-ease),
427
+ color var(--cs-dur-1) var(--cs-ease), border-color var(--cs-dur-1) var(--cs-ease);
428
+ }
429
+ .cs-pin-meta button:first-of-type { margin-left: auto; }
430
+ .cs-pin-meta button:hover {
431
+ background: var(--cs-s3);
432
+ border-color: var(--cs-line-3);
433
+ color: var(--cs-fg);
434
+ }
435
+ .cs-pin-meta button.cs-x-danger:hover {
436
+ background: var(--cs-danger-16);
437
+ border-color: var(--cs-danger);
438
+ color: var(--cs-danger);
439
+ }
440
+
441
+ /* ---------- 屏幕坐标层:高亮框 / pin 输入框 ---------- */
442
+
443
+ #cs-fx {
444
+ position: fixed;
445
+ inset: 0;
446
+ pointer-events: none;
447
+ z-index: 20;
448
+ }
449
+
450
+ .cs-box {
451
+ position: absolute;
452
+ border: 1px solid var(--cs-hover);
453
+ background: var(--cs-hover-weak);
454
+ border-radius: 2px;
455
+ display: none;
456
+ }
457
+ .cs-box.is-on { display: block; }
458
+ .cs-box[data-kind="select"] {
459
+ border-color: var(--cs-accent);
460
+ border-style: solid;
461
+ background: var(--cs-accent-weak);
462
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
463
+ }
464
+ .cs-box .cs-box-tag {
465
+ /* 位置由 select.ts 的 placeTag() 内联写入(要按画板边界翻边/夹边),这里不给 top/left */
466
+ position: absolute;
467
+ padding: 1px 5px;
468
+ border-radius: 3px 3px 0 0;
469
+ background: var(--cs-hover);
470
+ color: var(--cs-fg-on-accent);
471
+ font: var(--cs-fw-bold) var(--cs-fs-micro)/14px var(--cs-ff-mono);
472
+ white-space: nowrap;
473
+ }
474
+ .cs-box[data-kind="select"] .cs-box-tag {
475
+ background: var(--cs-accent);
476
+ color: var(--cs-fg-on-accent);
477
+ }
478
+
479
+ .cs-pin-input {
480
+ position: absolute;
481
+ width: 232px;
482
+ padding: 7px;
483
+ border: 1px solid var(--cs-pin);
484
+ border-radius: var(--cs-radius-sm);
485
+ background: var(--cs-surface);
486
+ box-shadow: var(--cs-shadow);
487
+ pointer-events: auto;
488
+ }
489
+ .cs-pin-input textarea {
490
+ display: block;
491
+ width: 100%;
492
+ height: 54px;
493
+ padding: 5px;
494
+ border: 1px solid var(--cs-line);
495
+ border-radius: 4px;
496
+ background: var(--cs-surface-2);
497
+ color: var(--cs-text);
498
+ font: 12px/1.4 var(--cs-font);
499
+ resize: none;
500
+ }
501
+ .cs-pin-input textarea:focus-visible {
502
+ border-color: var(--cs-pin);
503
+ outline: 2px solid var(--cs-pin);
504
+ outline-offset: 1px;
505
+ }
506
+ .cs-pin-input .cs-pin-tip {
507
+ margin-top: 5px;
508
+ color: var(--cs-text-faint);
509
+ font: 10px/1 var(--cs-mono);
510
+ }
511
+
512
+ /* ---------- HUD ---------- */
513
+
514
+ #cs-hud {
515
+ position: fixed;
516
+ left: 14px;
517
+ bottom: 14px;
518
+ z-index: 30;
519
+ /* 纯信息层:绝不拦事件。它是固定浮层,盖住的画板本来就该照样能拖能点 */
520
+ pointer-events: none;
521
+ user-select: none;
522
+ min-width: 260px;
523
+ max-width: 46vw;
524
+ padding: 8px 10px;
525
+ border: 1px solid var(--cs-line-2);
526
+ border-radius: var(--cs-r-3);
527
+ background: color-mix(in srgb, var(--cs-s1) 92%, transparent);
528
+ backdrop-filter: blur(8px);
529
+ box-shadow: var(--cs-e1), var(--cs-edge);
530
+ user-select: none;
531
+ }
532
+ .cs-hud-row {
533
+ display: flex;
534
+ align-items: center;
535
+ gap: 9px;
536
+ }
537
+ #cs-mode {
538
+ padding: 2px var(--cs-sp-4);
539
+ border: 1px solid var(--cs-line-2);
540
+ border-radius: var(--cs-r-full);
541
+ background: var(--cs-s2);
542
+ color: var(--cs-fg-2);
543
+ font: var(--cs-fw-medium) var(--cs-fs-xs)/1.6 var(--cs-ff-sans);
544
+ }
545
+ body[data-mode="interact"] #cs-mode,
546
+ body[data-mode="review"] #cs-mode,
547
+ body[data-pin="on"] #cs-mode {
548
+ border-color: transparent;
549
+ background: var(--cs-accent);
550
+ color: var(--cs-fg-on-accent);
551
+ }
552
+ .cs-hud-hint {
553
+ color: var(--cs-fg-4);
554
+ font: var(--cs-fs-xs)/1.7 var(--cs-ff-sans);
555
+ }
556
+ .cs-hud-hint b {
557
+ display: inline-block;
558
+ min-width: 15px;
559
+ padding: 0 4px;
560
+ border: 1px solid var(--cs-line-2);
561
+ border-radius: var(--cs-r-1);
562
+ background: var(--cs-s2);
563
+ color: var(--cs-fg-2);
564
+ font: var(--cs-fw-medium) var(--cs-fs-micro)/1.5 var(--cs-ff-mono);
565
+ text-align: center;
566
+ }
567
+ #cs-probe {
568
+ margin-top: 6px;
569
+ color: var(--cs-fg-2);
570
+ font: var(--cs-fs-xs)/1.4 var(--cs-ff-mono);
571
+ overflow: hidden;
572
+ text-overflow: ellipsis;
573
+ white-space: nowrap;
574
+ }
575
+
576
+ /* ---------- 顶部信息条 ---------- */
577
+
578
+ #cs-topbar {
579
+ position: fixed;
580
+ left: 14px;
581
+ top: 12px;
582
+ z-index: 30;
583
+ display: flex;
584
+ align-items: center;
585
+ gap: 10px;
586
+ padding: 6px 10px;
587
+ border: 1px solid var(--cs-line-2);
588
+ border-radius: var(--cs-r-3);
589
+ background: color-mix(in srgb, var(--cs-s1) 92%, transparent);
590
+ backdrop-filter: blur(8px);
591
+ box-shadow: var(--cs-e1), var(--cs-edge);
592
+ color: var(--cs-fg-4);
593
+ font: var(--cs-fs-xs)/1.6 var(--cs-ff-sans);
594
+ user-select: none;
595
+ }
596
+ /* logo:全息玻璃小标,高度对齐文字行;不透明度略压,别抢注意力 */
597
+ #cs-topbar .cs-logo { height: 18px; width: auto; display: block; }
598
+ #cs-topbar .cs-brand { color: var(--cs-fg-2); font-weight: var(--cs-fw-medium); }
599
+ #cs-topbar .cs-zoom { color: var(--cs-fg-3); font: var(--cs-fs-micro)/1.6 var(--cs-ff-mono); }
600
+ #cs-topbar #cs-target { color: var(--cs-fg-4); font: var(--cs-fs-micro)/1.6 var(--cs-ff-mono); }
601
+
602
+ /* ---------- args 面板 ---------- */
603
+
604
+ #cs-args {
605
+ position: fixed;
606
+ top: 0;
607
+ right: 0;
608
+ bottom: 0;
609
+ z-index: 40;
610
+ width: 300px;
611
+ display: flex;
612
+ flex-direction: column;
613
+ border-left: 1px solid var(--cs-line-2);
614
+ background: var(--cs-s1);
615
+ box-shadow: var(--cs-e3);
616
+ }
617
+ #cs-args[hidden] { display: none; }
618
+ .cs-args-head {
619
+ display: flex;
620
+ align-items: center;
621
+ gap: 8px;
622
+ padding: 11px 12px;
623
+ border-bottom: 1px solid var(--cs-line);
624
+ }
625
+ .cs-args-head .cs-args-title {
626
+ min-width: 0;
627
+ font: var(--cs-fw-bold) var(--cs-fs-md)/1.2 var(--cs-ff-sans);
628
+ letter-spacing: var(--cs-track-tight);
629
+ overflow: hidden;
630
+ text-overflow: ellipsis;
631
+ white-space: nowrap;
632
+ }
633
+ .cs-args-head .cs-x {
634
+ margin-left: auto;
635
+ padding: 2px 7px;
636
+ border: 0;
637
+ border-radius: var(--cs-r-1);
638
+ background: transparent;
639
+ color: var(--cs-fg-3);
640
+ font: var(--cs-fs-sm)/1 var(--cs-ff-sans);
641
+ cursor: pointer;
642
+ }
643
+ .cs-args-head .cs-x:hover { background: var(--cs-s3); color: var(--cs-fg); }
644
+ .cs-args-sub {
645
+ padding: 8px 12px;
646
+ border-bottom: 1px solid var(--cs-line);
647
+ color: var(--cs-fg-3);
648
+ font: var(--cs-fs-micro)/1.5 var(--cs-ff-mono);
649
+ white-space: pre-line;
650
+ overflow-wrap: anywhere;
651
+ }
652
+ .cs-args-body {
653
+ flex: 1;
654
+ min-height: 0; /* flex 子项要能收缩,内部滚动才激活 */
655
+ overflow: auto;
656
+ overscroll-behavior: contain;
657
+ padding: 10px 12px;
658
+ }
659
+ .cs-field { margin-bottom: 12px; }
660
+ .cs-field > label {
661
+ display: block;
662
+ margin-bottom: 4px;
663
+ color: var(--cs-fg-2);
664
+ font: var(--cs-fw-medium) var(--cs-fs-xs)/1.4 var(--cs-ff-sans);
665
+ }
666
+ .cs-field input[type="text"],
667
+ .cs-field input[type="number"],
668
+ .cs-field textarea {
669
+ width: 100%;
670
+ padding: var(--cs-sp-3) var(--cs-sp-4);
671
+ border: 1px solid var(--cs-line-2);
672
+ border-radius: var(--cs-r-2);
673
+ background: var(--cs-s2);
674
+ color: var(--cs-fg);
675
+ font: var(--cs-fs-sm)/1.4 var(--cs-ff-mono);
676
+ }
677
+ .cs-field textarea { height: 76px; resize: vertical; }
678
+ /* 焦点环:只在键盘聚焦时补 outline,不一刀切 outline:none */
679
+ .cs-field input:focus-visible,
680
+ .cs-field textarea:focus-visible {
681
+ border-color: var(--cs-line-3);
682
+ outline: 2px solid var(--cs-accent-32);
683
+ outline-offset: 0;
684
+ }
685
+ .cs-field.is-bool > label {
686
+ display: flex;
687
+ align-items: center;
688
+ gap: 7px;
689
+ color: var(--cs-fg);
690
+ font: var(--cs-fs-sm)/1 var(--cs-ff-sans);
691
+ cursor: pointer;
692
+ }
693
+ .cs-field input[type="checkbox"] { accent-color: var(--cs-accent); width: 14px; height: 14px; }
694
+ .cs-args-empty { color: var(--cs-fg-3); font: var(--cs-fs-sm)/1.6 var(--cs-ff-sans); }
695
+ .cs-args-foot {
696
+ padding: 10px 12px;
697
+ border-top: 1px solid var(--cs-line);
698
+ }
699
+ .cs-args-foot button {
700
+ width: 100%;
701
+ padding: var(--cs-sp-3);
702
+ border: 1px solid var(--cs-line-2);
703
+ border-radius: var(--cs-r-2);
704
+ background: transparent;
705
+ color: var(--cs-fg-2);
706
+ font: var(--cs-fw-medium) var(--cs-fs-sm)/1.4 var(--cs-ff-sans);
707
+ }
708
+ .cs-args-foot button[disabled] { cursor: not-allowed; opacity: 0.5; }
709
+
710
+ /* ---------- 参考图 ---------- */
711
+
712
+ #cs-refs {
713
+ position: fixed;
714
+ right: 14px;
715
+ bottom: 14px;
716
+ z-index: 30;
717
+ display: flex;
718
+ flex-direction: column-reverse;
719
+ gap: 8px;
720
+ max-height: 70vh;
721
+ overflow: auto;
722
+ overscroll-behavior: contain;
723
+ }
724
+ /* args 面板开着时让开,别被压在下面 */
725
+ body[data-args="open"] #cs-refs { right: 314px; }
726
+ #cs-refs .cs-ref {
727
+ width: 84px;
728
+ border: 1px solid var(--cs-line);
729
+ border-radius: var(--cs-radius-sm);
730
+ background: var(--cs-surface);
731
+ padding: 4px;
732
+ cursor: zoom-in;
733
+ box-shadow: var(--cs-shadow-sm);
734
+ }
735
+ #cs-refs .cs-ref img { display: block; width: 100%; border-radius: 3px; }
736
+ #cs-refs .cs-ref span {
737
+ display: block;
738
+ margin-top: 3px;
739
+ color: var(--cs-text-faint);
740
+ font: 9px/1.2 var(--cs-mono);
741
+ overflow: hidden;
742
+ text-overflow: ellipsis;
743
+ white-space: nowrap;
744
+ }
745
+
746
+ #cs-lightbox {
747
+ position: fixed;
748
+ inset: 0;
749
+ z-index: 60;
750
+ display: flex;
751
+ align-items: center;
752
+ justify-content: center;
753
+ flex-direction: column;
754
+ gap: 10px;
755
+ background: rgba(0, 0, 0, 0.82);
756
+ cursor: zoom-out;
757
+ }
758
+ #cs-lightbox[hidden] { display: none; }
759
+ #cs-lightbox img {
760
+ max-width: 88vw;
761
+ max-height: 82vh;
762
+ border-radius: var(--cs-r-3);
763
+ box-shadow: var(--cs-e3);
764
+ }
765
+ #cs-lightbox span { color: var(--cs-fg-2); font: var(--cs-fs-xs) var(--cs-ff-mono); }
766
+
767
+ /* ---------- 启动失败提示卡 ---------- */
768
+
769
+ #cs-boot {
770
+ position: fixed;
771
+ inset: 0;
772
+ z-index: 70;
773
+ display: flex;
774
+ align-items: center;
775
+ justify-content: center;
776
+ background: rgba(13, 14, 17, 0.86);
777
+ }
778
+ #cs-boot[hidden] { display: none; }
779
+ .cs-card {
780
+ max-width: 420px;
781
+ padding: 22px 24px;
782
+ border: 1px solid var(--cs-line-2);
783
+ border-radius: var(--cs-r-4);
784
+ background: var(--cs-s1);
785
+ box-shadow: var(--cs-e3), var(--cs-edge);
786
+ text-align: left;
787
+ }
788
+ .cs-card h2 { margin: 0 0 var(--cs-sp-3); font: var(--cs-fw-bold) var(--cs-fs-lg)/1.4 var(--cs-ff-sans); letter-spacing: var(--cs-track-tight); }
789
+ .cs-card p { margin: 0 0 var(--cs-sp-3); color: var(--cs-fg-2); font: var(--cs-fs-sm)/1.6 var(--cs-ff-sans); }
790
+ .cs-card code {
791
+ padding: 1px 5px;
792
+ border-radius: var(--cs-r-1);
793
+ background: var(--cs-s2);
794
+ color: var(--cs-fg);
795
+ font: var(--cs-fs-xs) var(--cs-ff-mono);
796
+ }
797
+ .cs-card .cs-err { color: var(--cs-danger); font: var(--cs-fs-micro)/1.5 var(--cs-ff-mono); word-break: break-all; }
798
+ /* 只作用于卡片的直接子按钮(重试/确认这类主操作)。以前写成 `.cs-card button` 会连
799
+ picker 的会话行一起染成蓝色主 CTA——选会话是中性选择,不该长得像"即将执行" */
800
+ .cs-card > button {
801
+ margin-top: var(--cs-sp-6);
802
+ padding: var(--cs-sp-4) var(--cs-sp-7);
803
+ border: 1px solid transparent;
804
+ border-radius: var(--cs-r-2);
805
+ background: var(--cs-accent);
806
+ color: var(--cs-fg-on-accent);
807
+ font: var(--cs-fw-medium) var(--cs-fs-sm)/1 var(--cs-ff-sans);
808
+ cursor: pointer;
809
+ transition: opacity var(--cs-dur-1) var(--cs-ease);
810
+ }
811
+ .cs-card > button:hover { opacity: 0.85; }
812
+
813
+ /* pins.ts / app.ts 在用但 CSS 里一直缺的类 */
814
+ .cs-dim { color: var(--cs-fg-3); font-size: var(--cs-fs-xs); }
815
+
816
+ /* ---------- 批注编辑 + 复制批注按钮 ---------- */
817
+
818
+ /* 编辑中气泡常显,不再依赖 hover(鼠标要进 textarea) */
819
+ .cs-pin-bubble.is-editing { display: block; }
820
+
821
+ .cs-pin-edit {
822
+ width: 100%;
823
+ min-height: 56px;
824
+ padding: 6px 7px;
825
+ border: 1px solid var(--cs-line-strong);
826
+ border-radius: 4px;
827
+ background: var(--cs-surface-2);
828
+ color: var(--cs-text);
829
+ font: 12px/1.45 var(--cs-font);
830
+ resize: vertical;
831
+ box-sizing: border-box;
832
+ }
833
+
834
+ #cs-copy {
835
+ padding: var(--cs-sp-2) var(--cs-sp-5);
836
+ border: 1px solid var(--cs-line-2);
837
+ border-radius: var(--cs-r-2);
838
+ background: transparent;
839
+ color: var(--cs-fg-2);
840
+ font: var(--cs-fw-medium) var(--cs-fs-xs)/1.6 var(--cs-ff-sans);
841
+ cursor: pointer;
842
+ transition: background var(--cs-dur-1) var(--cs-ease), color var(--cs-dur-1) var(--cs-ease);
843
+ }
844
+ #cs-copy:hover { background: var(--cs-s2); color: var(--cs-fg); }
845
+
846
+ /* hover/编辑中的画板整体抬到邻居之上:pin 气泡朝上弹时不被相邻画板的标题条盖住 */
847
+ .cs-board:hover { z-index: 5; }
848
+ .cs-board:has(.cs-pin-bubble.is-editing) { z-index: 5; }
849
+
850
+ /* 走查模式标题条隐藏,frame 顶到 board 顶,pins 层同步对齐 */
851
+ body[data-mode="review"] .cs-board.is-active .cs-pins { top: 0; }
852
+
853
+ /* JS 宽限开合:is-open 时气泡常显(与 :hover 并存,穿过死区不闪没) */
854
+ .cs-pin.is-open .cs-pin-bubble { display: block; }
855
+
856
+ /* screen 画板是"一整个页面",比组件标本更重:边框更实一点,把两类画板区分开 */
857
+ .cs-board[data-kind="screen"] .cs-frame {
858
+ outline-color: var(--cs-line-2);
859
+ box-shadow: var(--cs-e2);
860
+ }
861
+
862
+ /* screen 画板的视口宽度下拉 */
863
+ .cs-wsel {
864
+ flex: none;
865
+ max-width: 96px;
866
+ padding: 1px 3px;
867
+ border: 1px solid var(--cs-line);
868
+ border-radius: 3px;
869
+ background: var(--cs-surface-2);
870
+ color: var(--cs-text-dim);
871
+ font: 10px/1.3 var(--cs-mono);
872
+ }
873
+ .cs-wsel:hover { color: var(--cs-text); border-color: var(--cs-line-strong); }
874
+
875
+ /* ---------- 拖拽调尺寸手柄 + 底色开关 ---------- */
876
+
877
+ .cs-rz { position: absolute; z-index: 3; }
878
+ .cs-rz-e { top: var(--cs-title-h); right: -4px; bottom: 8px; width: 8px; cursor: ew-resize; }
879
+ .cs-rz-s { left: 8px; right: 8px; bottom: -4px; height: 8px; cursor: ns-resize; }
880
+ .cs-rz-se { right: -5px; bottom: -5px; width: 14px; height: 14px; cursor: nwse-resize; }
881
+ /* 手柄平时无形,hover 画板时右/下缘现出细提示线 */
882
+ .cs-board:hover .cs-rz-e { border-right: 2px solid var(--cs-line-strong); }
883
+ .cs-board:hover .cs-rz-s { border-bottom: 2px solid var(--cs-line-strong); }
884
+ .cs-rz:hover { border-color: var(--cs-accent) !important; }
885
+
886
+ .cs-bgbtn {
887
+ flex: none;
888
+ padding: 1px 4px;
889
+ border: none;
890
+ background: transparent;
891
+ color: var(--cs-text-faint);
892
+ font: 11px/1 var(--cs-mono);
893
+ cursor: pointer;
894
+ }
895
+ .cs-bgbtn:hover { color: var(--cs-text); }
896
+ .cs-bgbtn.is-on { color: var(--cs-pin); }
897
+
898
+ #cs-push {
899
+ padding: var(--cs-sp-2) var(--cs-sp-5);
900
+ border: 1px solid transparent;
901
+ border-radius: var(--cs-r-2);
902
+ background: var(--cs-accent);
903
+ color: var(--cs-fg-on-accent);
904
+ font: var(--cs-fw-medium) var(--cs-fs-xs)/1.6 var(--cs-ff-sans);
905
+ cursor: pointer;
906
+ transition: opacity var(--cs-dur-1) var(--cs-ease);
907
+ }
908
+ #cs-push:hover { opacity: 0.85; }
909
+
910
+ /* ---------- 推送会话选择器 ---------- */
911
+
912
+ .cs-picker {
913
+ position: fixed;
914
+ inset: 0;
915
+ z-index: 60;
916
+ display: flex;
917
+ align-items: center;
918
+ justify-content: center;
919
+ background: rgba(0, 0, 0, 0.45);
920
+ }
921
+ .cs-picker .cs-card { min-width: 320px; }
922
+
923
+ .cs-picker-row {
924
+ display: flex;
925
+ align-items: center;
926
+ gap: 10px;
927
+ width: 100%;
928
+ margin: 4px 0;
929
+ padding: 8px 10px;
930
+ border: 1px solid var(--cs-line);
931
+ border-radius: var(--cs-radius-sm);
932
+ background: var(--cs-surface-2);
933
+ color: var(--cs-text);
934
+ font: 13px/1 var(--cs-mono);
935
+ text-align: left;
936
+ cursor: pointer;
937
+ }
938
+ .cs-picker-row:hover { border-color: var(--cs-accent); background: var(--cs-surface-3); }
939
+
940
+ .cs-picker-status {
941
+ margin-left: auto;
942
+ padding: 2px 6px;
943
+ border-radius: 3px;
944
+ font: 600 9px/1 var(--cs-mono);
945
+ text-transform: uppercase;
946
+ background: var(--cs-surface-3);
947
+ color: var(--cs-text-faint);
948
+ }
949
+ .cs-picker-status.is-idle { color: var(--cs-ok); }
950
+ .cs-picker-status.is-busy { color: var(--cs-pin); }
951
+
952
+ /* ---------- toast:一次性回执(不再被 hover 反查擦掉) ---------- */
953
+
954
+ #cs-toasts {
955
+ position: fixed;
956
+ left: 50%;
957
+ bottom: 26px;
958
+ z-index: var(--cs-z-overlay);
959
+ display: flex;
960
+ flex-direction: column-reverse;
961
+ align-items: center;
962
+ gap: var(--cs-sp-2);
963
+ transform: translateX(-50%);
964
+ pointer-events: none;
965
+ }
966
+
967
+ .cs-toast {
968
+ max-width: min(560px, 70vw);
969
+ padding: var(--cs-sp-4) var(--cs-sp-6);
970
+ border: 1px solid var(--cs-line-2);
971
+ border-radius: var(--cs-r-3);
972
+ background: var(--cs-s1);
973
+ color: var(--cs-fg);
974
+ font: var(--cs-fw-medium) var(--cs-fs-sm)/1.4 var(--cs-ff-sans);
975
+ box-shadow: var(--cs-e2), var(--cs-edge);
976
+ opacity: 0;
977
+ transform: translateY(6px);
978
+ transition: opacity var(--cs-dur-2) var(--cs-ease-out),
979
+ transform var(--cs-dur-2) var(--cs-ease-out);
980
+ }
981
+ .cs-toast.is-in { opacity: 1; transform: translateY(0); }
982
+
983
+ /* 左侧色条编码状态,不给整条上色(整条上色在暗底上太吵) */
984
+ .cs-toast.is-ok { border-left: 3px solid var(--cs-ok); }
985
+ .cs-toast.is-warn { border-left: 3px solid var(--cs-warn); }
986
+ .cs-toast.is-error { border-left: 3px solid var(--cs-danger); }
987
+
988
+ /* ---------- 左侧图层列表(替代小地图:能搜、能显隐、能看路由) ---------- */
989
+
990
+ :root { --cs-sb-w: 236px; }
991
+
992
+ #cs-sidebar {
993
+ position: fixed;
994
+ left: 0;
995
+ top: 0;
996
+ bottom: 0;
997
+ width: var(--cs-sb-w);
998
+ z-index: var(--cs-z-chrome);
999
+ display: flex;
1000
+ flex-direction: column; /* 头固定、列表自己滚 */
1001
+ min-height: 0;
1002
+ border-right: 1px solid var(--cs-line);
1003
+ background: var(--cs-s1);
1004
+ box-shadow: var(--cs-e2);
1005
+ transition: transform var(--cs-dur-3) var(--cs-ease);
1006
+ }
1007
+ body[data-sidebar="off"] #cs-sidebar { transform: translateX(-100%); }
1008
+ /* 侧栏占位:画布内容整体右移,免得被压住 */
1009
+ body:not([data-sidebar="off"]) #cs-topbar { left: calc(var(--cs-sb-w) + 14px); }
1010
+ body:not([data-sidebar="off"]) #cs-hud { left: calc(var(--cs-sb-w) + 16px); }
1011
+
1012
+ .cs-sb-head {
1013
+ flex: none;
1014
+ display: flex;
1015
+ flex-direction: column;
1016
+ gap: var(--cs-sp-3);
1017
+ padding: var(--cs-sp-5) var(--cs-sp-5) var(--cs-sp-4);
1018
+ border-bottom: 1px solid var(--cs-line);
1019
+ }
1020
+ .cs-sb-title {
1021
+ color: var(--cs-fg-3);
1022
+ font: var(--cs-fw-bold) var(--cs-fs-micro)/1 var(--cs-ff-sans);
1023
+ letter-spacing: var(--cs-track-wide);
1024
+ text-transform: uppercase;
1025
+ }
1026
+ .cs-sb-search {
1027
+ width: 100%;
1028
+ padding: var(--cs-sp-3) var(--cs-sp-4);
1029
+ border: 1px solid var(--cs-line-2);
1030
+ border-radius: var(--cs-r-2);
1031
+ background: var(--cs-s2);
1032
+ color: var(--cs-fg);
1033
+ font: var(--cs-fs-sm)/1.2 var(--cs-ff-sans);
1034
+ }
1035
+ .cs-sb-search::placeholder { color: var(--cs-fg-4); }
1036
+
1037
+ /* 导航容器:自成有界滚动区,滚动条隐形但滚得动,滚到底不带走画布 */
1038
+ .cs-sb-list {
1039
+ flex: 1 1 auto;
1040
+ min-height: 0;
1041
+ overflow-y: auto;
1042
+ overscroll-behavior: contain;
1043
+ padding: var(--cs-sp-3) var(--cs-sp-3) var(--cs-sp-7);
1044
+ scrollbar-width: none;
1045
+ }
1046
+ .cs-sb-list::-webkit-scrollbar { display: none; }
1047
+
1048
+ .cs-sb-section {
1049
+ display: flex;
1050
+ align-items: center;
1051
+ gap: var(--cs-sp-3);
1052
+ margin: var(--cs-sp-5) var(--cs-sp-2) var(--cs-sp-2);
1053
+ color: var(--cs-fg-2);
1054
+ font: var(--cs-fw-bold) var(--cs-fs-xs)/1 var(--cs-ff-sans);
1055
+ }
1056
+ .cs-sb-section:first-child { margin-top: var(--cs-sp-2); }
1057
+ .cs-sb-count {
1058
+ padding: 1px var(--cs-sp-2);
1059
+ border-radius: var(--cs-r-full);
1060
+ background: var(--cs-s3);
1061
+ color: var(--cs-fg-3);
1062
+ font: var(--cs-fs-micro)/1.5 var(--cs-ff-mono);
1063
+ }
1064
+ .cs-sb-file {
1065
+ margin: var(--cs-sp-4) var(--cs-sp-2) var(--cs-sp-1);
1066
+ color: var(--cs-fg-3);
1067
+ font: var(--cs-fs-micro)/1.4 var(--cs-ff-mono);
1068
+ }
1069
+
1070
+ .cs-sb-row {
1071
+ display: flex;
1072
+ align-items: center;
1073
+ border-radius: var(--cs-r-2);
1074
+ }
1075
+ .cs-sb-row:hover { background: var(--cs-s2); }
1076
+ .cs-sb-row.is-active { background: var(--cs-s3); }
1077
+ .cs-sb-row.is-hidden-board .cs-sb-name { color: var(--cs-fg-4); text-decoration: line-through; }
1078
+
1079
+ .cs-sb-go {
1080
+ flex: 1 1 auto;
1081
+ min-width: 0; /* 名字要能收缩,省略号才生效 */
1082
+ display: flex;
1083
+ align-items: center;
1084
+ gap: var(--cs-sp-3);
1085
+ padding: var(--cs-sp-3) var(--cs-sp-2);
1086
+ border: 0;
1087
+ background: none;
1088
+ color: inherit;
1089
+ text-align: left;
1090
+ cursor: pointer;
1091
+ }
1092
+ .cs-sb-dot {
1093
+ flex: none;
1094
+ width: 6px;
1095
+ height: 6px;
1096
+ border-radius: var(--cs-r-full);
1097
+ background: currentColor;
1098
+ }
1099
+ .cs-sb-go[data-kind="screen"] .cs-sb-dot { color: var(--cs-kind-screen); }
1100
+ .cs-sb-go[data-kind="component"] .cs-sb-dot { color: var(--cs-kind-component); }
1101
+ .cs-sb-name {
1102
+ flex: 0 1 auto;
1103
+ min-width: 0;
1104
+ overflow: hidden;
1105
+ text-overflow: ellipsis;
1106
+ white-space: nowrap;
1107
+ color: var(--cs-fg);
1108
+ font: var(--cs-fs-sm)/1.3 var(--cs-ff-sans);
1109
+ }
1110
+ .cs-sb-route {
1111
+ flex: 0 1 auto;
1112
+ min-width: 0;
1113
+ margin-left: auto;
1114
+ overflow: hidden;
1115
+ text-overflow: ellipsis;
1116
+ white-space: nowrap;
1117
+ color: var(--cs-fg-3);
1118
+ font: var(--cs-fs-micro)/1.3 var(--cs-ff-mono);
1119
+ }
1120
+ .cs-sb-eye {
1121
+ flex: none;
1122
+ width: 26px;
1123
+ padding: var(--cs-sp-2) 0;
1124
+ border: 0;
1125
+ background: none;
1126
+ color: var(--cs-fg-4);
1127
+ font: var(--cs-fs-xs)/1 var(--cs-ff-sans);
1128
+ cursor: pointer;
1129
+ opacity: 0;
1130
+ transition: opacity var(--cs-dur-1) var(--cs-ease), color var(--cs-dur-1) var(--cs-ease);
1131
+ }
1132
+ .cs-sb-row:hover .cs-sb-eye, .cs-sb-row.is-hidden-board .cs-sb-eye { opacity: 1; }
1133
+ .cs-sb-eye:hover { color: var(--cs-fg); }
1134
+ .cs-sb-empty { padding: var(--cs-sp-6) var(--cs-sp-4); color: var(--cs-fg-4); font-size: var(--cs-fs-sm); }
1135
+
1136
+ #cs-sb-toggle {
1137
+ padding: 2px var(--cs-sp-3);
1138
+ border: 0;
1139
+ border-radius: var(--cs-r-1);
1140
+ background: none;
1141
+ color: var(--cs-fg-3);
1142
+ font-size: var(--cs-fs-md);
1143
+ cursor: pointer;
1144
+ }
1145
+ #cs-sb-toggle:hover { color: var(--cs-fg); background: var(--cs-s3); }
1146
+
1147
+ /* ---------- 世界里的分区标题 ---------- */
1148
+
1149
+ .cs-section-title {
1150
+ position: absolute;
1151
+ display: flex;
1152
+ align-items: baseline;
1153
+ gap: var(--cs-sp-3);
1154
+ color: var(--cs-fg-2);
1155
+ font: var(--cs-fw-bold) var(--cs-fs-md)/1 var(--cs-ff-sans);
1156
+ white-space: nowrap;
1157
+ pointer-events: none;
1158
+ }
1159
+ .cs-section-title[hidden] { display: none; }
1160
+ .cs-section-count {
1161
+ color: var(--cs-fg-4);
1162
+ font: var(--cs-fs-xs)/1 var(--cs-ff-mono);
1163
+ }
1164
+
1165
+ /* 拖动中的画板:抬起来 + 跟手 */
1166
+ .cs-board.is-dragging {
1167
+ z-index: var(--cs-z-board-up);
1168
+ cursor: grabbing;
1169
+ }
1170
+ .cs-board.is-dragging .cs-frame { box-shadow: var(--cs-e3); outline-color: var(--cs-accent); }
1171
+ .cs-board[hidden] { display: none; }
1172
+
1173
+ /* 页面画板标题条上的路由 */
1174
+ .cs-board-title .cs-args-n.cs-route {
1175
+ opacity: 1; /* 路由是主信息,不跟着 args 计数一起淡出 */
1176
+ padding: 1px var(--cs-sp-3);
1177
+ border-radius: var(--cs-r-1);
1178
+ background: var(--cs-s2);
1179
+ color: var(--cs-kind-screen);
1180
+ max-width: 180px;
1181
+ overflow: hidden;
1182
+ text-overflow: ellipsis;
1183
+ }
1184
+
1185
+ /* 选中的 pin:环起来,表明它现在听键盘 */
1186
+ .cs-pin.is-selected {
1187
+ box-shadow: 0 0 0 3px var(--cs-accent), 0 0 0 6px var(--cs-accent-32);
1188
+ z-index: 4;
1189
+ }