tavern-harness 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +161 -0
  2. package/README.zh.md +123 -0
  3. package/bin/cli.js +48 -0
  4. package/index.html +35 -0
  5. package/package.json +50 -0
  6. package/public/favicon.png +0 -0
  7. package/public/tavern-harness-logo.png +0 -0
  8. package/sandbox-server.mjs +997 -0
  9. package/src/App.tsx +268 -0
  10. package/src/assets/tavern-handbook.md +39 -0
  11. package/src/components/AchievementModal.tsx +158 -0
  12. package/src/components/CharactersView.tsx +904 -0
  13. package/src/components/ChatView.tsx +1639 -0
  14. package/src/components/ConfirmationDialog.tsx +56 -0
  15. package/src/components/Dashboard.tsx +180 -0
  16. package/src/components/DeleteConfirmDialog.tsx +48 -0
  17. package/src/components/FileDisplayModal.tsx +556 -0
  18. package/src/components/GameplayDialogs.tsx +191 -0
  19. package/src/components/NewSessionMenu.tsx +362 -0
  20. package/src/components/SettingsView.tsx +517 -0
  21. package/src/components/Sidebar.tsx +392 -0
  22. package/src/components/StatsView.tsx +189 -0
  23. package/src/components/Toasts.tsx +20 -0
  24. package/src/components/WorkspaceFileManagerModal.tsx +216 -0
  25. package/src/components/shared.tsx +369 -0
  26. package/src/core/achievements.ts +85 -0
  27. package/src/core/fileDownload.ts +67 -0
  28. package/src/core/gameplay.ts +521 -0
  29. package/src/core/horizontalWheel.ts +93 -0
  30. package/src/core/i18n.ts +1403 -0
  31. package/src/core/linkedScroll.ts +236 -0
  32. package/src/core/markdown.ts +170 -0
  33. package/src/core/openai.ts +451 -0
  34. package/src/core/openingGreeting.ts +41 -0
  35. package/src/core/prompts.ts +254 -0
  36. package/src/core/proxy.ts +58 -0
  37. package/src/core/sillyTavernImporter.ts +317 -0
  38. package/src/core/stats.ts +91 -0
  39. package/src/core/strings.ts +19 -0
  40. package/src/core/toolDefinitions.ts +400 -0
  41. package/src/core/tools/builtinTools.ts +38 -0
  42. package/src/core/tools/generatedSkillExecutor.ts +706 -0
  43. package/src/core/tools/generatedWorkspace.ts +13 -0
  44. package/src/core/tools/toolExecutor.ts +718 -0
  45. package/src/core/turnLoop.ts +211 -0
  46. package/src/db/database.ts +647 -0
  47. package/src/main.tsx +23 -0
  48. package/src/store/store.ts +2419 -0
  49. package/src/theme/chat.css +1227 -0
  50. package/src/theme/global.css +1377 -0
  51. package/src/theme/theme.ts +143 -0
  52. package/src/theme/views.css +1430 -0
  53. package/src/types/models.ts +364 -0
  54. package/src/vite-env.d.ts +1 -0
  55. package/tsconfig.json +21 -0
  56. package/vite.config.ts +273 -0
@@ -0,0 +1,1430 @@
1
+ /* ============================================================
2
+ 弹窗 / 覆盖层 / 视图页 / 卡片列表
3
+ ============================================================ */
4
+
5
+ .overlay {
6
+ position: fixed;
7
+ inset: 0;
8
+ background: rgba(0, 0, 0, 0.42);
9
+ backdrop-filter: blur(4px);
10
+ z-index: 900;
11
+ animation: fadeIn 0.18s ease;
12
+ }
13
+ @keyframes fadeIn {
14
+ from { opacity: 0; }
15
+ }
16
+ .modal-root {
17
+ position: fixed;
18
+ inset: 0;
19
+ z-index: 901;
20
+ display: grid;
21
+ place-items: center;
22
+ pointer-events: none;
23
+ }
24
+ .modal {
25
+ pointer-events: auto;
26
+ width: min(480px, calc(100vw - 32px));
27
+ max-height: calc(100dvh - 60px);
28
+ display: flex;
29
+ flex-direction: column;
30
+ background: var(--surface);
31
+ box-shadow: var(--shadow-lg);
32
+ }
33
+ .modal-head {
34
+ display: flex;
35
+ align-items: center;
36
+ justify-content: space-between;
37
+ padding: 14px 18px;
38
+ border-bottom: 1px solid var(--border);
39
+ }
40
+ .modal-body {
41
+ padding: 16px 18px;
42
+ overflow-y: auto;
43
+ }
44
+ .modal-foot {
45
+ display: flex;
46
+ justify-content: flex-end;
47
+ gap: 8px;
48
+ padding: 12px 18px;
49
+ border-top: 1px solid var(--border);
50
+ }
51
+
52
+ .modal.confirm {
53
+ width: min(420px, calc(100vw - 32px));
54
+ }
55
+
56
+ /* ---------- 大尺寸编辑弹窗(角色卡 / Lorebook) ---------- */
57
+ .modal-editor {
58
+ width: min(1040px, calc(100vw - 32px));
59
+ height: min(90dvh, 960px);
60
+ }
61
+ .modal-editor .modal-body {
62
+ display: flex;
63
+ flex-direction: column;
64
+ gap: 12px;
65
+ flex: 1;
66
+ min-height: 0;
67
+ }
68
+ .modal-editor .grow-textarea {
69
+ flex: 1;
70
+ min-height: 110px;
71
+ resize: vertical;
72
+ }
73
+ .modal-editor .preview-scroll {
74
+ max-height: 200px;
75
+ overflow: auto;
76
+ }
77
+ .modal-editor .wb-render-pane {
78
+ flex: 1;
79
+ min-height: 110px;
80
+ padding: 12px 14px;
81
+ max-height: none;
82
+ height: 100%;
83
+ }
84
+ /* 角色卡:人设 Prompt 所在的 field 容器弹性增长,textaarea 填满 */
85
+ .modal-editor .prompt-field {
86
+ flex: 3;
87
+ min-height: 220px;
88
+ display: flex;
89
+ flex-direction: column;
90
+ }
91
+ .modal-editor .prompt-field .grow-textarea {
92
+ flex: 1;
93
+ min-height: 0;
94
+ }
95
+ .modal-editor .greeting-grow {
96
+ min-height: 88px;
97
+ }
98
+ /* 开场白输入框:删除按钮悬浮于右上角 */
99
+ .modal-editor .greeting-input {
100
+ position: relative;
101
+ }
102
+ .modal-editor .greeting-input .greeting-del {
103
+ position: absolute;
104
+ top: 8px;
105
+ right: 8px;
106
+ width: 22px;
107
+ height: 22px;
108
+ border-radius: 6px;
109
+ background: color-mix(in srgb, var(--surface) 72%, transparent);
110
+ color: var(--text-dim);
111
+ opacity: 0.7;
112
+ z-index: 2;
113
+ }
114
+ .modal-editor .greeting-input .greeting-del:hover {
115
+ opacity: 1;
116
+ color: var(--danger);
117
+ }
118
+ .field-hint {
119
+ font-size: 11.5px;
120
+ color: var(--text-faint);
121
+ line-height: 1.4;
122
+ }
123
+ /* 技能清单:限制最大高度并滚动,避免挤压 Prompt 的弹性空间 */
124
+ .modal-editor .skill-list-scroll {
125
+ display: flex;
126
+ flex-wrap: wrap;
127
+ gap: 6px;
128
+ max-height: 108px;
129
+ overflow-y: auto;
130
+ align-content: flex-start;
131
+ }
132
+ .confirm-args {
133
+ background: var(--code-bg);
134
+ border: 1px solid var(--border);
135
+ border-radius: 10px;
136
+ padding: 10px 12px;
137
+ font-size: 11.5px;
138
+ max-height: 180px;
139
+ overflow: auto;
140
+ white-space: pre-wrap;
141
+ word-break: break-all;
142
+ }
143
+ .confirm-reason {
144
+ padding-top: 10px;
145
+ border-top: 1px solid var(--border);
146
+ }
147
+ .confirm-reason-title {
148
+ margin-bottom: 4px;
149
+ color: var(--text);
150
+ font-size: 13px;
151
+ font-weight: 700;
152
+ }
153
+ .confirm-inline-code {
154
+ display: inline;
155
+ margin-right: 0.25em;
156
+ padding: 2px 5px;
157
+ border: 1px solid var(--border);
158
+ border-radius: 4px;
159
+ background: var(--code-bg);
160
+ color: var(--text);
161
+ font-family: var(--font-mono);
162
+ font-size: 0.92em;
163
+ white-space: pre-wrap;
164
+ overflow-wrap: anywhere;
165
+ }
166
+
167
+ /* ---------- 视图页通用 ---------- */
168
+ .view-page {
169
+ flex: 1;
170
+ overflow-y: auto;
171
+ padding: 22px 28px;
172
+ }
173
+ .workshop-page {
174
+ /* Safari 尚未稳定支持 scrollbar-gutter;始终保留纵向滚动条槽,
175
+ 防止切换内容高度不同的 Tab 时居中的标题和导航横向跳动。 */
176
+ overflow-y: scroll;
177
+ scrollbar-gutter: stable;
178
+ }
179
+ .view-col {
180
+ max-width: 900px;
181
+ margin: 0 auto;
182
+ display: flex;
183
+ flex-direction: column;
184
+ gap: 16px;
185
+ }
186
+ .view-title {
187
+ font-family: var(--font-serif);
188
+ font-size: 24px;
189
+ font-weight: 700;
190
+ letter-spacing: 0.2px;
191
+ margin: 0;
192
+ }
193
+ .view-sub {
194
+ color: var(--text-dim);
195
+ font-size: 13px;
196
+ margin: 2px 0 0;
197
+ }
198
+
199
+ /* ---------- 卡片多列瀑布流(角色) ---------- */
200
+ .char-grid {
201
+ --masonry-column-min-width: 190px;
202
+ --masonry-gap: 14px;
203
+ display: flex;
204
+ gap: var(--masonry-gap);
205
+ align-items: flex-start;
206
+ }
207
+ .char-grid-column {
208
+ flex: 1 1 var(--masonry-column-min-width);
209
+ min-width: 0;
210
+ display: flex;
211
+ flex-direction: column;
212
+ gap: var(--masonry-gap);
213
+ }
214
+ .masonry-item {
215
+ min-width: 0;
216
+ }
217
+ .char-card {
218
+ border: 1px solid var(--border);
219
+ border-radius: var(--radius);
220
+ background: var(--surface);
221
+ padding: 16px;
222
+ display: flex;
223
+ width: 100%;
224
+ box-sizing: border-box;
225
+ flex-direction: column;
226
+ gap: 10px;
227
+ transition: border-color 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
228
+ position: relative;
229
+ }
230
+ .char-card:hover {
231
+ z-index: 1;
232
+ border-color: color-mix(in srgb, var(--primary) 32%, transparent);
233
+ background: color-mix(in srgb, var(--primary) 4%, var(--surface));
234
+ }
235
+ .char-card .cname {
236
+ font-family: var(--font-serif);
237
+ font-weight: 700;
238
+ font-size: 16px;
239
+ display: flex;
240
+ align-items: center;
241
+ gap: 8px;
242
+ /* 行内不换行:标签固定在右侧(flex-wrap:nowrap),名称在剩余宽度内完整显示、可换行 */
243
+ flex-wrap: nowrap;
244
+ min-width: 0;
245
+ }
246
+ .char-card .cname .cname-text {
247
+ min-width: 0;
248
+ flex: 1 1 auto;
249
+ overflow: hidden;
250
+ white-space: nowrap;
251
+ }
252
+ .char-card .cname .tag {
253
+ flex-shrink: 0;
254
+ }
255
+ .char-card .cgreet {
256
+ font-size: 12.5px;
257
+ color: var(--text-dim);
258
+ line-height: 1.5;
259
+ display: -webkit-box;
260
+ -webkit-line-clamp: 10; /* 人设最多展示 10 行 */
261
+ -webkit-box-orient: vertical;
262
+ overflow: hidden;
263
+ }
264
+ .char-card .cskil {
265
+ display: flex;
266
+ flex-wrap: wrap;
267
+ gap: 4px;
268
+ align-content: flex-start;
269
+ /* 技能最多展示 5 行(行高约21px,5行+4个4px间隙≈121px),超出可滚动 */
270
+ max-height: 121px;
271
+ overflow-y: auto;
272
+ /* 不设置 scrollbar-width/scrollbar-color:设置后会禁用全局 ::-webkit-scrollbar
273
+ 定制,退回浏览器默认滚动条(白色轨道,与卡片羊皮纸底色不协调);
274
+ 留空即沿用全局滚动条样式,与侧边栏会话列表一致 */
275
+ }
276
+ .char-card .cactions {
277
+ display: flex;
278
+ gap: 6px;
279
+ }
280
+ /* Lorebook卡片(复用 char-card 外壳,内容区预览更多行) */
281
+ .wb-card .cname {
282
+ font-family: var(--font-serif);
283
+ font-weight: 700;
284
+ font-size: 16px;
285
+ display: flex;
286
+ align-items: center;
287
+ gap: 8px;
288
+ flex-wrap: nowrap;
289
+ min-width: 0;
290
+ }
291
+ .wb-card .cname .cname-text {
292
+ min-width: 0;
293
+ flex: 1 1 auto;
294
+ overflow: hidden;
295
+ white-space: nowrap;
296
+ }
297
+ .card-name-scroll {
298
+ display: block;
299
+ }
300
+ .card-name-scroll-text {
301
+ display: inline-block;
302
+ }
303
+ .card-name-scroll.is-overflowing .card-name-scroll-text {
304
+ animation: card-name-marquee 8s linear infinite;
305
+ }
306
+ .card-name-scroll-copy {
307
+ margin-left: 2.5rem;
308
+ }
309
+ @keyframes card-name-marquee {
310
+ from { transform: translateX(0); }
311
+ to { transform: translateX(calc(-100% - 1.25rem)); }
312
+ }
313
+ @media (prefers-reduced-motion: reduce) {
314
+ .card-name-scroll.is-overflowing .card-name-scroll-text {
315
+ animation: none;
316
+ }
317
+ }
318
+ .wb-card .cname .tag {
319
+ flex-shrink: 0;
320
+ }
321
+ .wb-card .wcontent {
322
+ font-size: 12.5px;
323
+ color: var(--text-dim);
324
+ line-height: 1.5;
325
+ display: -webkit-box;
326
+ -webkit-line-clamp: 10; /* Lorebook内容最多展示 10 行 */
327
+ -webkit-box-orient: vertical;
328
+ overflow: hidden;
329
+ white-space: pre-line;
330
+ }
331
+ .wb-card .cmeta {
332
+ font-size: 11px;
333
+ color: var(--text-dim);
334
+ opacity: 0.7;
335
+ }
336
+ .skill-tag {
337
+ font-size: 10.5px;
338
+ padding: 2px 7px;
339
+ border-radius: 6px;
340
+ background: var(--surface-3);
341
+ color: var(--text-dim);
342
+ font-weight: 600;
343
+ }
344
+ .skill-tag.locked {
345
+ opacity: 0.5;
346
+ }
347
+
348
+ /* ---------- 列表(Lorebook / 技能 / 文件) ---------- */
349
+ .list-item {
350
+ display: flex;
351
+ align-items: center;
352
+ gap: 12px;
353
+ padding: 12px 14px;
354
+ border: 1px solid var(--border);
355
+ border-radius: 11px;
356
+ background: var(--surface);
357
+ transition: border-color 0.32s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.32s cubic-bezier(0.16, 1, 0.3, 1);
358
+ }
359
+ .list-item:hover {
360
+ position: relative;
361
+ z-index: 1;
362
+ border-color: color-mix(in srgb, var(--primary) 30%, transparent);
363
+ background: color-mix(in srgb, var(--primary) 3%, var(--surface));
364
+ }
365
+ .list-item .l-main {
366
+ flex: 1;
367
+ min-width: 0;
368
+ }
369
+ .list-item .l-title {
370
+ font-weight: 700;
371
+ font-size: 13.5px;
372
+ }
373
+ .list-item .l-sub {
374
+ font-size: 11.5px;
375
+ color: var(--text-faint);
376
+ font-family: var(--font-mono);
377
+ }
378
+
379
+ /* ---------- 技能表:拖拽排序 ---------- */
380
+ .skill-sort-hint {
381
+ display: flex;
382
+ align-items: center;
383
+ gap: 7px;
384
+ font-size: 11.5px;
385
+ color: var(--text-faint);
386
+ line-height: 1.5;
387
+ padding: 0 2px;
388
+ }
389
+ /* 技能卡片 = 现有 .list-item 样式 + 拖拽能力(无手柄,整卡可拖) */
390
+ .list-item.sortable-skill {
391
+ cursor: grab;
392
+ user-select: none;
393
+ touch-action: none; /* dnd-kit:允许触摸设备拖拽 */
394
+ }
395
+ .list-item.sortable-skill:active {
396
+ cursor: grabbing;
397
+ }
398
+ .list-item.sortable-skill.dragging {
399
+ box-shadow:
400
+ 0 0 0 1px color-mix(in srgb, var(--primary) 30%, transparent),
401
+ 0 0 10px color-mix(in srgb, var(--glow) 42%, transparent),
402
+ 0 0 24px color-mix(in srgb, var(--glow) 16%, transparent),
403
+ var(--shadow-lg);
404
+ border-color: color-mix(in srgb, var(--primary) 45%, transparent);
405
+ cursor: grabbing;
406
+ }
407
+ /* 右侧按钮区:右对齐;宽度自适应内容,但禁止被文字挤压变形 */
408
+ .skill-item-actions {
409
+ display: flex;
410
+ align-items: center;
411
+ gap: 8px;
412
+ flex-shrink: 0;
413
+ margin-left: auto;
414
+ }
415
+ .skill-item-actions .btn {
416
+ min-height: 26px;
417
+ justify-content: center;
418
+ box-sizing: border-box;
419
+ white-space: nowrap;
420
+ flex-shrink: 0;
421
+ }
422
+
423
+ /* ---------- 技能详情弹窗 ---------- */
424
+ .skill-detail-tag {
425
+ font-size: 10.5px;
426
+ font-family: var(--font-mono);
427
+ color: var(--warn);
428
+ background: var(--surface-2);
429
+ border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent);
430
+ padding: 2px 8px;
431
+ border-radius: 6px;
432
+ }
433
+ .skill-detail-text {
434
+ font-size: 13px;
435
+ color: var(--text);
436
+ background: var(--surface-2);
437
+ border: 1px solid var(--border-soft);
438
+ border-radius: var(--radius-sm);
439
+ padding: 10px 13px;
440
+ line-height: 1.55;
441
+ white-space: pre-wrap;
442
+ word-break: break-word;
443
+ }
444
+ .skill-detail-textarea,
445
+ .skill-detail-json {
446
+ overflow-wrap: anywhere;
447
+ word-break: break-word;
448
+ }
449
+ .skill-detail-json {
450
+ margin: 0;
451
+ max-height: 240px;
452
+ overflow-y: auto;
453
+ overflow-x: hidden;
454
+ font-family: var(--font-mono);
455
+ font-size: 11.5px;
456
+ line-height: 1.55;
457
+ color: var(--text-dim);
458
+ background: var(--surface-2);
459
+ border: 1px solid var(--border-soft);
460
+ border-radius: var(--radius-sm);
461
+ padding: 10px 13px;
462
+ white-space: pre-wrap;
463
+ }
464
+ .skill-detail-json.err {
465
+ color: var(--danger);
466
+ }
467
+
468
+ /* ---------- 设置分组 ---------- */
469
+ .set-grid {
470
+ display: grid;
471
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
472
+ gap: 16px;
473
+ }
474
+ .set-section {
475
+ border: 1px solid var(--border);
476
+ border-radius: var(--radius);
477
+ background: var(--surface);
478
+ padding: 18px;
479
+ display: flex;
480
+ flex-direction: column;
481
+ gap: 12px;
482
+ box-shadow: var(--shadow);
483
+ transition: border-color 0.32s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.32s cubic-bezier(0.16, 1, 0.3, 1);
484
+ }
485
+ .set-section:hover {
486
+ border-color: color-mix(in srgb, var(--primary) 24%, transparent);
487
+ }
488
+ .set-section h3 {
489
+ margin: 0;
490
+ font-family: var(--font-serif);
491
+ font-size: 15.5px;
492
+ font-weight: 700;
493
+ display: flex;
494
+ align-items: center;
495
+ gap: 8px;
496
+ }
497
+ .settings-reset-section {
498
+ align-items: flex-start;
499
+ border-color: color-mix(in srgb, var(--danger) 58%, var(--border));
500
+ background: color-mix(in srgb, var(--danger) 14%, var(--surface));
501
+ }
502
+ .settings-reset-section:hover {
503
+ border-color: color-mix(in srgb, var(--danger) 78%, var(--border));
504
+ background: color-mix(in srgb, var(--danger) 19%, var(--surface));
505
+ }
506
+ .settings-reset-desc {
507
+ margin: 5px 0 0;
508
+ color: var(--text-dim);
509
+ font-size: 12.5px;
510
+ line-height: 1.55;
511
+ }
512
+ .settings-reset-button {
513
+ margin-top: 2px;
514
+ }
515
+
516
+ /* ---------- 成就陈列柜 ---------- */
517
+ .ach-shelf {
518
+ display: grid;
519
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
520
+ gap: 12px;
521
+ }
522
+ .ach-card {
523
+ border: 1px solid var(--border);
524
+ border-radius: var(--radius);
525
+ background: var(--surface);
526
+ padding: 14px;
527
+ display: flex;
528
+ flex-direction: column;
529
+ gap: 8px;
530
+ transition: all 0.18s ease;
531
+ opacity: 0.72;
532
+ }
533
+ .ach-card:hover {
534
+ opacity: 1;
535
+ z-index: 1;
536
+ border-color: color-mix(in srgb, var(--primary) 30%, transparent);
537
+ }
538
+ .ach-card.unlocked {
539
+ opacity: 1;
540
+ border-color: color-mix(in srgb, var(--primary) 45%, transparent);
541
+ background: color-mix(in srgb, var(--primary-soft) 55%, var(--surface));
542
+ box-shadow:
543
+ inset 0 0 0 1px color-mix(in srgb, var(--primary) 14%, transparent),
544
+ inset 0 0 12px color-mix(in srgb, var(--glow) 22%, transparent),
545
+ 0 0 0 1px color-mix(in srgb, var(--primary) 22%, transparent),
546
+ 0 0 8px color-mix(in srgb, var(--glow) 40%, transparent),
547
+ 0 0 16px color-mix(in srgb, var(--glow) 18%, transparent);
548
+ }
549
+ .ach-card-head {
550
+ display: flex;
551
+ align-items: center;
552
+ justify-content: space-between;
553
+ }
554
+ .ach-card-icon {
555
+ font-size: 26px;
556
+ line-height: 1;
557
+ }
558
+ .ach-card-status {
559
+ font-size: 10.5px;
560
+ font-weight: 700;
561
+ letter-spacing: 0.4px;
562
+ color: var(--text-faint);
563
+ font-family: var(--font-mono);
564
+ }
565
+ .ach-card.unlocked .ach-card-status {
566
+ color: var(--success);
567
+ }
568
+ .ach-card-name {
569
+ font-family: var(--font-serif);
570
+ font-weight: 700;
571
+ font-size: 15.5px;
572
+ }
573
+ .ach-card-desc {
574
+ font-size: 11.5px;
575
+ color: var(--text-dim);
576
+ line-height: 1.4;
577
+ min-height: 32px;
578
+ }
579
+ .ach-card-time {
580
+ font-size: 10.5px;
581
+ color: var(--text-faint);
582
+ }
583
+ .ach-shelf-summary {
584
+ font-size: 12px;
585
+ color: var(--text-dim);
586
+ }
587
+ .ach-shelf-summary b {
588
+ color: var(--text);
589
+ }
590
+ .set-row {
591
+ display: flex;
592
+ align-items: center;
593
+ justify-content: space-between;
594
+ gap: 12px;
595
+ }
596
+ .set-row .s-label {
597
+ font-size: 12.5px;
598
+ font-weight: 600;
599
+ color: var(--text-dim);
600
+ }
601
+ .set-row .s-desc {
602
+ font-size: 11px;
603
+ color: var(--text-faint);
604
+ }
605
+ .range-row {
606
+ display: flex;
607
+ align-items: center;
608
+ gap: 10px;
609
+ }
610
+ .range-row input[type='range'] {
611
+ flex: 1;
612
+ accent-color: var(--primary);
613
+ }
614
+ .range-val {
615
+ font-family: var(--font-mono);
616
+ font-size: 12px;
617
+ color: var(--text-dim);
618
+ min-width: 48px;
619
+ text-align: right;
620
+ }
621
+
622
+ .provider-card {
623
+ border: 1px solid var(--border);
624
+ border-radius: 12px;
625
+ padding: 14px;
626
+ background: var(--surface);
627
+ display: flex;
628
+ flex-direction: column;
629
+ gap: 10px;
630
+ transition: border-color 0.32s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.32s cubic-bezier(0.16, 1, 0.3, 1);
631
+ }
632
+ .provider-card:hover {
633
+ border-color: color-mix(in srgb, var(--primary) 26%, transparent);
634
+ }
635
+ .provider-card .p-head {
636
+ display: flex;
637
+ align-items: center;
638
+ gap: 10px;
639
+ }
640
+ .provider-card .p-name {
641
+ font-weight: 700;
642
+ font-family: var(--font-serif);
643
+ font-size: 15px;
644
+ }
645
+ .provider-card .p-url {
646
+ font-size: 11px;
647
+ color: var(--text-faint);
648
+ font-family: var(--font-mono);
649
+ word-break: break-all;
650
+ }
651
+ .p-status {
652
+ font-size: 11px;
653
+ font-weight: 700;
654
+ padding: 2px 8px;
655
+ border-radius: 999px;
656
+ }
657
+ .p-status.on {
658
+ color: var(--success);
659
+ background: var(--success-soft);
660
+ }
661
+ .p-status.off {
662
+ color: var(--text-faint);
663
+ background: var(--surface-2);
664
+ }
665
+
666
+ .provider-card.disabled {
667
+ opacity: 0.55;
668
+ }
669
+ .provider-card.disabled .p-name {
670
+ color: var(--text-faint);
671
+ }
672
+
673
+ /* ---------- 开关 Switch ---------- */
674
+ .switch {
675
+ position: relative;
676
+ display: inline-block;
677
+ width: 38px;
678
+ height: 22px;
679
+ flex-shrink: 0;
680
+ }
681
+ .switch input {
682
+ opacity: 0;
683
+ width: 0;
684
+ height: 0;
685
+ }
686
+ .switch-slider {
687
+ position: absolute;
688
+ cursor: pointer;
689
+ inset: 0;
690
+ background: var(--surface-3);
691
+ border: 1px solid var(--border);
692
+ border-radius: 22px;
693
+ transition: background 0.2s, border-color 0.2s;
694
+ }
695
+ .switch-slider::before {
696
+ content: '';
697
+ position: absolute;
698
+ width: 16px;
699
+ height: 16px;
700
+ left: 2px;
701
+ top: 2px;
702
+ border-radius: 50%;
703
+ background: var(--text-faint);
704
+ transition: transform 0.2s, background 0.2s;
705
+ }
706
+ .switch input:checked + .switch-slider {
707
+ background: var(--grad-from);
708
+ border-color: transparent;
709
+ }
710
+ .switch input:checked + .switch-slider::before {
711
+ transform: translateX(16px);
712
+ background: #fff;
713
+ }
714
+
715
+ /* ---------- 参数表 ---------- */
716
+ .param-table td {
717
+ border-bottom: 1px solid var(--border-soft);
718
+ padding: 9px 4px;
719
+ }
720
+ .param-table .range-row {
721
+ display: grid;
722
+ grid-template-columns: 1fr 52px;
723
+ gap: 10px;
724
+ align-items: center;
725
+ }
726
+ .param-table .range-val {
727
+ min-width: 0;
728
+ }
729
+
730
+ /* ---------- 消息右键菜单 ---------- */
731
+ .overlay-msg {
732
+ position: fixed;
733
+ inset: 0;
734
+ z-index: 950;
735
+ }
736
+ .msg-menu {
737
+ position: fixed;
738
+ z-index: 951;
739
+ min-width: 172px;
740
+ padding: 6px;
741
+ display: flex;
742
+ flex-direction: column;
743
+ background: var(--surface);
744
+ border: 1px solid var(--border);
745
+ box-shadow: var(--shadow-lg);
746
+ border-radius: 12px;
747
+ }
748
+ .msg-menu-item {
749
+ display: flex;
750
+ align-items: center;
751
+ gap: 9px;
752
+ padding: 8px 12px;
753
+ border: none;
754
+ background: none;
755
+ border-radius: 8px;
756
+ color: var(--text);
757
+ font-size: 13px;
758
+ font-weight: 600;
759
+ cursor: pointer;
760
+ text-align: left;
761
+ width: 100%;
762
+ }
763
+ .msg-menu-item:hover {
764
+ background: var(--surface-2);
765
+ color: var(--text);
766
+ }
767
+ .msg-menu-item.danger {
768
+ color: var(--danger);
769
+ }
770
+ .msg-menu-item.danger:hover {
771
+ background: var(--danger-soft);
772
+ color: var(--danger);
773
+ }
774
+
775
+ /* ---------- 游戏导出/导入弹窗 ---------- */
776
+ .gameplay-history-row {
777
+ display: flex;
778
+ align-items: center;
779
+ justify-content: space-between;
780
+ gap: 12px;
781
+ padding: 2px 2px 0;
782
+ }
783
+ .gameplay-history-row .field {
784
+ flex: 1;
785
+ min-width: 0;
786
+ }
787
+ .gameplay-summary {
788
+ font-size: 12.5px;
789
+ line-height: 1.65;
790
+ color: var(--text-dim);
791
+ background: var(--surface-2);
792
+ border: 1px solid var(--border-soft);
793
+ border-radius: 10px;
794
+ padding: 10px 12px;
795
+ }
796
+ .gameplay-file-btn {
797
+ border-style: dashed;
798
+ border-color: color-mix(in srgb, var(--primary) 40%, var(--border));
799
+ color: var(--text);
800
+ background: var(--surface);
801
+ font-weight: 700;
802
+ min-height: 44px;
803
+ white-space: nowrap;
804
+ overflow: hidden;
805
+ text-overflow: ellipsis;
806
+ }
807
+ .gameplay-file-btn:hover {
808
+ border-color: var(--primary);
809
+ background: color-mix(in srgb, var(--primary) 7%, transparent);
810
+ }
811
+
812
+ /* ---------- 创建对话:角色槽位 ---------- */
813
+ .npc-slot-x {
814
+ position: absolute;
815
+ top: 50%;
816
+ right: 6px;
817
+ transform: translateY(-50%);
818
+ width: 20px;
819
+ height: 20px;
820
+ padding: 0;
821
+ border-radius: 7px;
822
+ border: 1px solid color-mix(in srgb, var(--danger) 26%, var(--border));
823
+ background: color-mix(in srgb, var(--danger-soft) 72%, var(--surface));
824
+ color: var(--danger);
825
+ display: flex;
826
+ align-items: center;
827
+ justify-content: center;
828
+ cursor: pointer;
829
+ box-shadow: none;
830
+ line-height: 1;
831
+ opacity: 0.92;
832
+ transition: background 0.16s, border-color 0.16s, opacity 0.16s;
833
+ }
834
+ .npc-slot-x:hover {
835
+ border-color: color-mix(in srgb, var(--danger) 55%, var(--border));
836
+ background: color-mix(in srgb, var(--danger-soft) 88%, var(--surface));
837
+ opacity: 1;
838
+ }
839
+ .npc-slot-x svg {
840
+ display: block;
841
+ margin: auto;
842
+ }
843
+ .npc-slot-add {
844
+ align-self: flex-start;
845
+ display: inline-flex;
846
+ align-items: center;
847
+ gap: 6px;
848
+ padding: 0;
849
+ border: none;
850
+ background: none;
851
+ color: var(--primary);
852
+ font-size: 13px;
853
+ font-weight: 700;
854
+ cursor: pointer;
855
+ transition: color 0.15s ease, opacity 0.15s ease;
856
+ }
857
+ .npc-slot-add:hover {
858
+ color: var(--primary-strong, var(--primary));
859
+ opacity: 0.82;
860
+ }
861
+ .new-session-section-label {
862
+ font-size: 12px;
863
+ font-weight: 700;
864
+ color: var(--text-dim);
865
+ letter-spacing: 0.3px;
866
+ text-transform: uppercase;
867
+ }
868
+ .new-session-random-row {
869
+ display: flex;
870
+ align-items: center;
871
+ justify-content: space-between;
872
+ gap: 12px;
873
+ padding: 4px 2px 0;
874
+ }
875
+ .new-session-modal {
876
+ overflow: hidden;
877
+ }
878
+
879
+ /* 「+」添加角色:内联选择小列表 */
880
+ .slot-picker {
881
+ position: relative;
882
+ }
883
+ .slot-picker-menu {
884
+ position: absolute;
885
+ left: 0;
886
+ top: calc(100% + 6px);
887
+ z-index: 30;
888
+ min-width: 160px;
889
+ max-height: 220px;
890
+ overflow-y: auto;
891
+ padding: 6px;
892
+ display: flex;
893
+ flex-direction: column;
894
+ gap: 2px;
895
+ }
896
+ .slot-picker-item {
897
+ display: flex;
898
+ align-items: center;
899
+ gap: 8px;
900
+ width: 100%;
901
+ padding: 6px 8px;
902
+ border: none;
903
+ background: none;
904
+ border-radius: 8px;
905
+ color: var(--text);
906
+ font-size: 13px;
907
+ font-weight: 600;
908
+ cursor: pointer;
909
+ text-align: left;
910
+ }
911
+ .slot-picker-item:hover {
912
+ background: var(--surface-2);
913
+ }
914
+
915
+ .model-option:hover {
916
+ background: var(--surface-2);
917
+ }
918
+
919
+ /* ---------- 分组标签 ---------- */
920
+ .group-label {
921
+ font-size: 12px;
922
+ font-weight: 800;
923
+ color: var(--text-dim);
924
+ letter-spacing: 0.6px;
925
+ text-transform: uppercase;
926
+ margin: 12px 0 8px;
927
+ display: flex;
928
+ align-items: center;
929
+ gap: 8px;
930
+ }
931
+ .group-label::after {
932
+ content: '';
933
+ flex: 1;
934
+ height: 1px;
935
+ background: var(--border-soft);
936
+ }
937
+
938
+ /* ---------- 原始日志弹窗 ---------- */
939
+ .raw-log-modal {
940
+ overflow: hidden;
941
+ }
942
+ .raw-modal-head {
943
+ display: grid;
944
+ grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
945
+ gap: 12px;
946
+ }
947
+ .raw-modal-title {
948
+ min-width: 0;
949
+ display: flex;
950
+ align-items: center;
951
+ gap: 8px;
952
+ overflow: hidden;
953
+ font-size: 14px;
954
+ font-weight: 800;
955
+ white-space: nowrap;
956
+ text-overflow: ellipsis;
957
+ }
958
+ .raw-head-actions {
959
+ min-width: 0;
960
+ display: flex;
961
+ align-items: center;
962
+ justify-content: flex-end;
963
+ gap: 8px;
964
+ }
965
+ .raw-head-actions > .icon-btn.active {
966
+ color: var(--primary);
967
+ background: var(--surface-2);
968
+ }
969
+ .raw-tabs {
970
+ display: flex;
971
+ justify-self: center;
972
+ gap: 3px;
973
+ background: var(--surface-2);
974
+ border: 1px solid var(--border-soft);
975
+ border-radius: 9px;
976
+ padding: 3px;
977
+ }
978
+ .raw-tab {
979
+ padding: 5px 12px;
980
+ border: 1px solid transparent;
981
+ border-radius: 6px;
982
+ background: none;
983
+ color: var(--text-dim);
984
+ font-size: 12px;
985
+ font-weight: 600;
986
+ cursor: pointer;
987
+ transition: all 0.32s cubic-bezier(0.16, 1, 0.3, 1);
988
+ }
989
+ .raw-tab:hover {
990
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
991
+ border-color: color-mix(in srgb, var(--primary) 25%, transparent);
992
+ color: var(--text);
993
+ }
994
+ .raw-tab.active {
995
+ position: relative;
996
+ z-index: 1;
997
+ border-color: color-mix(in srgb, var(--primary) 58%, transparent);
998
+ background: color-mix(in srgb, var(--primary) 15%, var(--surface));
999
+ color: var(--primary);
1000
+ font-weight: 800;
1001
+ box-shadow:
1002
+ inset 0 0 0 1px color-mix(in srgb, var(--primary) 14%, transparent),
1003
+ inset 0 0 10px color-mix(in srgb, var(--glow) 30%, transparent),
1004
+ 0 0 0 1px color-mix(in srgb, var(--primary) 26%, transparent),
1005
+ 0 0 8px color-mix(in srgb, var(--glow) 42%, transparent),
1006
+ 0 0 14px color-mix(in srgb, var(--glow) 20%, transparent);
1007
+ }
1008
+ @media (max-width: 680px) {
1009
+ .raw-modal-head {
1010
+ grid-template-columns: minmax(0, 1fr) auto;
1011
+ }
1012
+ .raw-tabs {
1013
+ grid-column: 1 / -1;
1014
+ grid-row: 2;
1015
+ }
1016
+ }
1017
+ .raw-pre {
1018
+ margin: 0;
1019
+ padding: 14px 16px;
1020
+ max-height: 60dvh;
1021
+ overflow: auto;
1022
+ font-size: 12px;
1023
+ line-height: 1.55;
1024
+ white-space: pre-wrap;
1025
+ word-break: break-all;
1026
+ background: var(--code-bg);
1027
+ }
1028
+ .raw-pre-nowrap {
1029
+ white-space: pre;
1030
+ word-break: normal;
1031
+ }
1032
+ .raw-wrap-toggle {
1033
+ display: inline-flex;
1034
+ align-items: center;
1035
+ gap: 8px;
1036
+ flex-shrink: 0;
1037
+ }
1038
+ .raw-wrap-label {
1039
+ color: var(--text-dim);
1040
+ font-size: 11.5px;
1041
+ font-weight: 600;
1042
+ white-space: nowrap;
1043
+ }
1044
+ .raw-merged {
1045
+ display: flex;
1046
+ flex-direction: column;
1047
+ max-height: 60dvh;
1048
+ }
1049
+ .raw-merged-head {
1050
+ display: flex;
1051
+ align-items: center;
1052
+ justify-content: space-between;
1053
+ gap: 10px;
1054
+ padding: 10px 16px;
1055
+ font-size: 12.5px;
1056
+ font-weight: 700;
1057
+ color: var(--text-dim);
1058
+ border-bottom: 1px solid var(--border-soft);
1059
+ }
1060
+ .raw-empty {
1061
+ padding: 30px;
1062
+ text-align: center;
1063
+ color: var(--text-faint);
1064
+ font-size: 13px;
1065
+ }
1066
+ /* ---------- 文件展示弹窗(file_display) ---------- */
1067
+ .display-modal {
1068
+ width: min(880px, calc(100vw - 32px));
1069
+ height: min(82dvh, 720px);
1070
+ }
1071
+ .display-pip {
1072
+ position: fixed;
1073
+ z-index: 950;
1074
+ min-width: 0;
1075
+ min-height: 0;
1076
+ display: flex;
1077
+ flex-direction: column;
1078
+ overflow: hidden;
1079
+ background: var(--surface);
1080
+ box-shadow: var(--shadow-lg);
1081
+ }
1082
+ .display-pip-drag-layer {
1083
+ min-height: 0;
1084
+ flex: 1;
1085
+ display: flex;
1086
+ flex-direction: column;
1087
+ }
1088
+ .display-pip .display-modal-head {
1089
+ padding: 9px 10px;
1090
+ flex-wrap: nowrap;
1091
+ cursor: move;
1092
+ touch-action: none;
1093
+ user-select: none;
1094
+ }
1095
+ .display-pip .display-modal-title > .mono {
1096
+ display: none;
1097
+ }
1098
+ .display-pip .display-title-text {
1099
+ font-size: 12.5px;
1100
+ }
1101
+ .display-pip .display-html-note {
1102
+ display: none;
1103
+ }
1104
+ .display-pip-resize {
1105
+ position: absolute;
1106
+ right: 0;
1107
+ bottom: 0;
1108
+ z-index: 2;
1109
+ width: 24px;
1110
+ height: 24px;
1111
+ border: 0;
1112
+ padding: 0;
1113
+ background: transparent;
1114
+ cursor: nwse-resize;
1115
+ touch-action: none;
1116
+ }
1117
+ .display-modal-head {
1118
+ gap: 12px;
1119
+ flex-wrap: wrap;
1120
+ }
1121
+ .display-modal-title {
1122
+ display: flex;
1123
+ align-items: center;
1124
+ gap: 8px;
1125
+ min-width: 0;
1126
+ font-weight: 800;
1127
+ font-size: 13.5px;
1128
+ }
1129
+ .display-title-text {
1130
+ white-space: nowrap;
1131
+ overflow: hidden;
1132
+ text-overflow: ellipsis;
1133
+ }
1134
+ .display-head-actions {
1135
+ display: flex;
1136
+ align-items: center;
1137
+ gap: 6px;
1138
+ margin-left: auto;
1139
+ }
1140
+ .display-zoom-group {
1141
+ display: flex;
1142
+ align-items: center;
1143
+ gap: 4px;
1144
+ background: var(--surface-2);
1145
+ border: 1px solid var(--border-soft);
1146
+ border-radius: 9px;
1147
+ padding: 2px;
1148
+ }
1149
+ .display-zoom-group .icon-btn {
1150
+ width: 26px;
1151
+ height: 26px;
1152
+ border-radius: 7px;
1153
+ }
1154
+ .display-head-actions > .icon-btn.active {
1155
+ color: var(--primary);
1156
+ background: var(--surface-2);
1157
+ }
1158
+ .display-modal-body {
1159
+ flex: 1;
1160
+ min-height: 0;
1161
+ padding: 0;
1162
+ display: flex;
1163
+ flex-direction: column;
1164
+ }
1165
+ .display-loading,
1166
+ .display-error {
1167
+ flex: 1;
1168
+ display: grid;
1169
+ place-items: center;
1170
+ gap: 10px;
1171
+ color: var(--text-dim);
1172
+ font-size: 13px;
1173
+ flex-direction: column;
1174
+ }
1175
+ .display-error {
1176
+ color: var(--danger);
1177
+ }
1178
+
1179
+ /* 图片 */
1180
+ .display-image-viewport {
1181
+ flex: 1;
1182
+ min-height: 0;
1183
+ overflow: hidden;
1184
+ display: grid;
1185
+ place-items: center;
1186
+ background:
1187
+ radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--border) 55%, transparent) 1px, transparent 0) 0 0 / 22px 22px,
1188
+ var(--surface-2);
1189
+ user-select: none;
1190
+ touch-action: none;
1191
+ }
1192
+ .display-image {
1193
+ max-width: none;
1194
+ max-height: none;
1195
+ object-fit: contain;
1196
+ transition: transform 0.12s ease-out;
1197
+ will-change: transform;
1198
+ }
1199
+
1200
+ /* HTML 沙箱 */
1201
+ .display-html-wrap {
1202
+ flex: 1;
1203
+ min-height: 0;
1204
+ display: flex;
1205
+ flex-direction: column;
1206
+ }
1207
+ .display-html-viewport {
1208
+ position: relative;
1209
+ flex: 1;
1210
+ min-height: 0;
1211
+ overflow: hidden;
1212
+ background: var(--surface-2);
1213
+ }
1214
+ .display-html-loading {
1215
+ position: absolute;
1216
+ inset: 0;
1217
+ z-index: 1;
1218
+ display: flex;
1219
+ flex-direction: column;
1220
+ align-items: center;
1221
+ justify-content: center;
1222
+ gap: 10px;
1223
+ color: var(--text-dim);
1224
+ font-size: 13px;
1225
+ background: var(--surface-2);
1226
+ opacity: 1;
1227
+ visibility: visible;
1228
+ transition: opacity 0.12s ease, visibility 0s linear 0s;
1229
+ }
1230
+ .display-html-loading.hidden {
1231
+ opacity: 0;
1232
+ visibility: hidden;
1233
+ transition: opacity 0.12s ease, visibility 0s linear 0.12s;
1234
+ }
1235
+ .display-iframe {
1236
+ position: absolute;
1237
+ inset: 0 auto auto 0;
1238
+ display: block;
1239
+ border: none;
1240
+ background: transparent;
1241
+ transform-origin: top left;
1242
+ opacity: 0;
1243
+ }
1244
+ .display-iframe.ready {
1245
+ opacity: 1;
1246
+ }
1247
+ .display-html-note {
1248
+ padding: 7px 14px;
1249
+ font-size: 11.5px;
1250
+ color: var(--text-faint);
1251
+ border-top: 1px solid var(--border-soft);
1252
+ }
1253
+
1254
+ /* 文本 / 代码 / 纯文本 */
1255
+ .display-md-scroll,
1256
+ .display-code-scroll,
1257
+ .display-plain-scroll {
1258
+ flex: 1;
1259
+ min-height: 0;
1260
+ overflow: auto;
1261
+ }
1262
+ .display-md-scroll .md {
1263
+ padding: 16px 20px;
1264
+ }
1265
+ .display-code,
1266
+ .display-plain {
1267
+ margin: 0;
1268
+ padding: 14px 16px;
1269
+ font-size: 12.5px;
1270
+ line-height: 1.55;
1271
+ white-space: pre;
1272
+ overflow-x: auto;
1273
+ min-height: 100%;
1274
+ background: var(--code-bg);
1275
+ }
1276
+ .display-code.wrapped,
1277
+ .display-plain.wrapped {
1278
+ white-space: pre-wrap;
1279
+ overflow-wrap: anywhere;
1280
+ }
1281
+ .display-code code {
1282
+ font-family: inherit;
1283
+ font-size: inherit;
1284
+ background: none;
1285
+ padding: 0;
1286
+ color: var(--text);
1287
+ }
1288
+
1289
+ /* 工具结果「查看」按钮 */
1290
+ .display-view-btn {
1291
+ margin-top: 6px;
1292
+ display: inline-flex;
1293
+ align-items: center;
1294
+ gap: 5px;
1295
+ }
1296
+ .tool-card-result-line .display-view-btn {
1297
+ margin-top: 0;
1298
+ margin-left: 8px;
1299
+ }
1300
+ /* 展开态工具结果:查看按钮固定在结果行右侧、垂直居中 */
1301
+ .tool-card-body .tool-result .display-view-btn {
1302
+ margin-top: 0;
1303
+ margin-left: auto;
1304
+ flex-shrink: 0;
1305
+ }
1306
+
1307
+ /* ---------- 会话工作区文件管理器(只读) ---------- */
1308
+ .ws-fm-modal {
1309
+ width: min(640px, calc(100vw - 32px));
1310
+ height: min(70dvh, 560px);
1311
+ }
1312
+ .ws-fm-head {
1313
+ gap: 10px;
1314
+ flex-wrap: wrap;
1315
+ }
1316
+ .ws-fm-title {
1317
+ display: flex;
1318
+ align-items: center;
1319
+ gap: 8px;
1320
+ min-width: 0;
1321
+ font-weight: 800;
1322
+ font-size: 13.5px;
1323
+ }
1324
+ .ws-fm-title .ws-fm-crumb {
1325
+ min-width: 0;
1326
+ overflow: hidden;
1327
+ text-overflow: ellipsis;
1328
+ white-space: nowrap;
1329
+ font-size: 11.5px;
1330
+ color: var(--text-dim);
1331
+ }
1332
+ .ws-fm-crumb-link {
1333
+ cursor: pointer;
1334
+ color: var(--primary);
1335
+ }
1336
+ .ws-fm-crumb-link:hover {
1337
+ text-decoration: underline;
1338
+ }
1339
+ .ws-fm-crumb-sep {
1340
+ margin: 0 3px;
1341
+ color: var(--text-faint);
1342
+ }
1343
+ .ws-fm-crumb-cur {
1344
+ color: var(--text);
1345
+ }
1346
+ .ws-fm-body {
1347
+ flex: 1;
1348
+ min-height: 0;
1349
+ display: flex;
1350
+ flex-direction: column;
1351
+ gap: 10px;
1352
+ padding: 14px 18px;
1353
+ }
1354
+ .ws-fm-note {
1355
+ display: flex;
1356
+ align-items: center;
1357
+ gap: 6px;
1358
+ font-size: 11.5px;
1359
+ color: var(--text-faint);
1360
+ }
1361
+ .ws-fm-list {
1362
+ flex: 1;
1363
+ min-height: 0;
1364
+ overflow-y: auto;
1365
+ border: 1px solid var(--border-soft);
1366
+ border-radius: 10px;
1367
+ background: var(--surface-2);
1368
+ padding: 4px;
1369
+ }
1370
+ .ws-fm-row {
1371
+ display: flex;
1372
+ align-items: center;
1373
+ gap: 8px;
1374
+ padding: 6px 10px;
1375
+ border-radius: 7px;
1376
+ cursor: pointer;
1377
+ color: var(--text-dim);
1378
+ font-size: 13px;
1379
+ user-select: none;
1380
+ }
1381
+ .ws-fm-row:hover {
1382
+ background: color-mix(in srgb, var(--primary) 10%, transparent);
1383
+ color: var(--text);
1384
+ }
1385
+ .ws-fm-arrow {
1386
+ width: 10px;
1387
+ flex-shrink: 0;
1388
+ }
1389
+ .ws-fm-name {
1390
+ flex: 1;
1391
+ min-width: 0;
1392
+ overflow: hidden;
1393
+ text-overflow: ellipsis;
1394
+ white-space: nowrap;
1395
+ }
1396
+ .ws-fm-actions {
1397
+ display: inline-flex;
1398
+ align-items: center;
1399
+ gap: 4px;
1400
+ }
1401
+ .ws-fm-open {
1402
+ display: inline-flex;
1403
+ align-items: center;
1404
+ opacity: 0.55;
1405
+ }
1406
+ .ws-fm-delete {
1407
+ width: 26px;
1408
+ height: 26px;
1409
+ color: var(--text-faint);
1410
+ opacity: 0;
1411
+ }
1412
+ .ws-fm-row:hover .ws-fm-open,
1413
+ .ws-fm-row:hover .ws-fm-delete,
1414
+ .ws-fm-delete:focus-visible {
1415
+ opacity: 1;
1416
+ }
1417
+ .ws-fm-delete:hover {
1418
+ color: var(--danger);
1419
+ }
1420
+ .ws-fm-empty {
1421
+ padding: 22px;
1422
+ text-align: center;
1423
+ color: var(--text-faint);
1424
+ font-size: 13px;
1425
+ display: flex;
1426
+ align-items: center;
1427
+ justify-content: center;
1428
+ gap: 10px;
1429
+ flex: 1;
1430
+ }