dsh-router-core 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 (42) hide show
  1. package/README.md +151 -0
  2. package/cordis.patch.yml +9 -0
  3. package/lib/client-registry.js +3006 -0
  4. package/lib/client.js +3006 -0
  5. package/lib/index.js +1756 -0
  6. package/lib/types/credential-store.d.ts +15 -0
  7. package/lib/types/index.d.ts +6 -0
  8. package/lib/types/keys.d.ts +39 -0
  9. package/lib/types/llm/adapter.d.ts +31 -0
  10. package/lib/types/router/index.d.ts +73 -0
  11. package/lib/types/router/types.d.ts +80 -0
  12. package/lib/types/shared.d.ts +138 -0
  13. package/lib/types/supplier-config.d.ts +28 -0
  14. package/lib/types/suppliers/contract.d.ts +98 -0
  15. package/lib/types/suppliers/loader.d.ts +41 -0
  16. package/lib/types/suppliers/registry.d.ts +33 -0
  17. package/package.json +86 -0
  18. package/src/client/CombosTab.tsx +458 -0
  19. package/src/client/EndpointTab.tsx +360 -0
  20. package/src/client/Modal.tsx +46 -0
  21. package/src/client/RouterView.tsx +186 -0
  22. package/src/client/SupplierDetail.tsx +943 -0
  23. package/src/client/index.tsx +18 -0
  24. package/src/client/router.css +2096 -0
  25. package/src/client/sidebar-entry.ts +167 -0
  26. package/src/client/workspace-controller.ts +26 -0
  27. package/src/client/workspace-mount.tsx +117 -0
  28. package/src/credential-store.ts +64 -0
  29. package/src/index.ts +468 -0
  30. package/src/keys.ts +143 -0
  31. package/src/llm/adapter.ts +273 -0
  32. package/src/router/index.ts +273 -0
  33. package/src/router/types.ts +85 -0
  34. package/src/shared.ts +152 -0
  35. package/src/supplier-config.ts +144 -0
  36. package/src/suppliers/contract.ts +73 -0
  37. package/src/suppliers/loader.ts +191 -0
  38. package/src/suppliers/nvidia/plugin.ts +268 -0
  39. package/src/suppliers/opencode/plugin.ts +200 -0
  40. package/src/suppliers/openrouter/plugin.ts +282 -0
  41. package/src/suppliers/registry.ts +365 -0
  42. package/src/types.d.ts +45 -0
@@ -0,0 +1,2096 @@
1
+ /* ============================================================
2
+ dsh-router — sidebar entry
3
+ ============================================================ */
4
+
5
+ .dshr-entry {
6
+ box-sizing: border-box;
7
+ display: flex;
8
+ width: 100%;
9
+ min-height: 36px;
10
+ align-items: center;
11
+ gap: 10px;
12
+ border: none;
13
+ border-radius: 8px;
14
+ padding: 0 10px;
15
+ color: var(--dsw-alias-label-secondary);
16
+ background: transparent;
17
+ font-size: 13px;
18
+ white-space: nowrap;
19
+ cursor: pointer;
20
+ }
21
+
22
+ .dshr-entry:hover {
23
+ color: var(--dsw-alias-label-primary);
24
+ background: var(--dsw-alias-interactive-bg-hover);
25
+ }
26
+
27
+ .dshr-entry[data-active] {
28
+ color: var(--dsw-alias-label-primary);
29
+ background: var(--dsw-alias-interactive-bg-active);
30
+ font-weight: 600;
31
+ }
32
+
33
+ .dshr-entryIcon {
34
+ display: inline-flex;
35
+ flex: none;
36
+ align-items: center;
37
+ justify-content: center;
38
+ width: 24px;
39
+ height: 24px;
40
+ }
41
+
42
+ .dshr-entryIcon svg {
43
+ display: block;
44
+ width: 18px;
45
+ height: 18px;
46
+ }
47
+
48
+ .dshr-entryLabel {
49
+ overflow: hidden;
50
+ text-overflow: ellipsis;
51
+ }
52
+
53
+ [data-dsh-frame][data-sidebar-collapsed] .dshr-entry {
54
+ width: 36px;
55
+ min-height: 36px;
56
+ justify-content: center;
57
+ margin: 0 auto 12px;
58
+ padding: 0;
59
+ border-radius: 50%;
60
+ }
61
+
62
+ [data-dsh-frame][data-sidebar-collapsed] .dshr-entryLabel {
63
+ display: none;
64
+ }
65
+
66
+ /* ============================================================
67
+ dsh-router — center-column panel
68
+ ============================================================ */
69
+
70
+ [data-pane='conversation'] {
71
+ position: relative;
72
+ }
73
+
74
+ .dshDesktopConversationSurface {
75
+ position: relative;
76
+ }
77
+
78
+ [data-dsh-router-view] {
79
+ position: absolute;
80
+ z-index: 60;
81
+ inset: 0;
82
+ display: none;
83
+ min-width: 0;
84
+ min-height: 0;
85
+ overflow: hidden;
86
+ background: var(--dsw-alias-bg-overlay, var(--dsw-alias-bg-base));
87
+ }
88
+
89
+ .dshr-panelView {
90
+ isolation: isolate;
91
+ }
92
+
93
+ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]):not([data-dsh-mnemon-active]) [data-dsh-router-view] {
94
+ display: block;
95
+ }
96
+
97
+ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]):not([data-dsh-mnemon-active]) [data-pane='conversation'] > :not([data-dsh-router-view]),
98
+ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]):not([data-dsh-mnemon-active]) [class*='centerCol'] > :not([data-dsh-router-view]),
99
+ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]):not([data-dsh-mnemon-active]) .dshDesktopConversationSurface > :not([data-dsh-router-view]) {
100
+ display: none !important;
101
+ }
102
+
103
+ /* ============================================================
104
+ dsh-router — panel shell
105
+ ============================================================ */
106
+
107
+ .dshr-shell {
108
+ --rs-bg: var(--dsw-alias-bg-overlay, var(--dsw-alias-bg-base));
109
+ --rs-layer-1: var(--dsw-alias-bg-layer-1);
110
+ --rs-layer-2: var(--dsw-alias-bg-layer-2);
111
+ --rs-input: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-2));
112
+ --rs-text: var(--dsw-alias-label-primary);
113
+ --rs-muted: var(--dsw-alias-label-secondary);
114
+ --rs-faint: var(--dsw-alias-label-tertiary);
115
+ --rs-line: var(--dsw-alias-border-l1);
116
+ --rs-line-strong: var(--dsw-alias-border-l2);
117
+ --rs-accent: var(--dsw-alias-state-business-primary);
118
+ --rs-hover: var(--dsw-alias-interactive-bg-hover);
119
+ --rs-danger: var(--dsw-alias-state-error-primary);
120
+ --rs-success: var(--dsw-alias-state-success-primary);
121
+ --rs-ok: var(--rs-success);
122
+ --rs-warn: #9a6a18;
123
+ --rs-mono: var(--ds-font-family-code, 'SFMono-Regular', Consolas, monospace);
124
+ --rs-sans: var(--dsw-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
125
+ box-sizing: border-box;
126
+ display: flex;
127
+ height: 100%;
128
+ min-width: 0;
129
+ min-height: 0;
130
+ flex-direction: column;
131
+ overflow: hidden;
132
+ color: var(--rs-text);
133
+ background: var(--rs-bg);
134
+ font: 13px/1.55 var(--rs-sans);
135
+ -webkit-tap-highlight-color: transparent;
136
+ }
137
+
138
+ .dshr-shell *,
139
+ .dshr-shell *::before,
140
+ .dshr-shell *::after { box-sizing: border-box; }
141
+
142
+ .dshr-shell button,
143
+ .dshr-shell input,
144
+ .dshr-shell select,
145
+ .dshr-shell textarea { color: inherit; font: inherit; }
146
+
147
+ .dshr-shell button:focus-visible,
148
+ .dshr-shell input:focus-visible,
149
+ .dshr-shell select:focus-visible,
150
+ .dshr-shell textarea:focus-visible {
151
+ outline: 2px solid var(--rs-accent);
152
+ outline-offset: 2px;
153
+ }
154
+
155
+ /* Masthead */
156
+ .dshr-masthead {
157
+ display: flex;
158
+ min-height: 56px;
159
+ flex: 0 0 auto;
160
+ align-items: center;
161
+ gap: 16px;
162
+ padding: 0 18px;
163
+ border-bottom: 1px solid var(--rs-line);
164
+ }
165
+
166
+ .dshr-brand {
167
+ display: grid;
168
+ min-width: 0;
169
+ flex: 0 1 auto;
170
+ align-content: center;
171
+ gap: 1px 9px;
172
+ }
173
+
174
+ .dshr-brandTitle {
175
+ overflow: hidden;
176
+ font-size: 15px;
177
+ font-weight: 650;
178
+ text-overflow: ellipsis;
179
+ white-space: nowrap;
180
+ }
181
+
182
+ .dshr-brandSub {
183
+ overflow: hidden;
184
+ color: var(--rs-faint);
185
+ font-size: 10px;
186
+ text-overflow: ellipsis;
187
+ white-space: nowrap;
188
+ }
189
+
190
+ .dshr-statusCluster {
191
+ display: inline-flex;
192
+ min-width: 0;
193
+ flex: 0 0 auto;
194
+ align-items: center;
195
+ gap: 7px;
196
+ margin-left: auto;
197
+ color: var(--rs-muted);
198
+ font-size: 12px;
199
+ white-space: nowrap;
200
+ }
201
+
202
+ .dshr-statusEndpoint {
203
+ overflow: hidden;
204
+ max-width: 220px;
205
+ color: var(--rs-faint);
206
+ font: 11px var(--rs-mono);
207
+ text-overflow: ellipsis;
208
+ }
209
+
210
+ .dshr-headerActions {
211
+ display: inline-flex;
212
+ flex: 0 0 auto;
213
+ align-items: center;
214
+ gap: 10px;
215
+ }
216
+
217
+ .dshr-refreshedAt {
218
+ color: var(--rs-faint);
219
+ font-size: 11px;
220
+ white-space: nowrap;
221
+ }
222
+
223
+ .dshr-iconButton {
224
+ display: inline-flex;
225
+ width: 28px;
226
+ height: 28px;
227
+ align-items: center;
228
+ justify-content: center;
229
+ border: 1px solid var(--rs-line);
230
+ border-radius: 6px;
231
+ color: var(--rs-muted);
232
+ background: var(--rs-layer-1);
233
+ cursor: pointer;
234
+ }
235
+
236
+ .dshr-iconButton:hover { color: var(--rs-text); background: var(--rs-hover); }
237
+ .dshr-iconButton:disabled { opacity: 0.5; cursor: default; }
238
+
239
+ .dshr-backButton {
240
+ display: inline-flex;
241
+ height: 28px;
242
+ align-items: center;
243
+ gap: 5px;
244
+ padding: 0 10px;
245
+ border: 1px solid var(--rs-line);
246
+ border-radius: 6px;
247
+ color: var(--rs-text);
248
+ background: transparent;
249
+ font-size: 12px;
250
+ cursor: pointer;
251
+ white-space: nowrap;
252
+ }
253
+
254
+ .dshr-backButton:hover { background: var(--rs-hover); }
255
+ .dshr-backButton svg { display: block; }
256
+
257
+ /* Alert banner */
258
+ .dshr-alert {
259
+ display: flex;
260
+ flex: 0 0 auto;
261
+ align-items: baseline;
262
+ gap: 10px;
263
+ margin: 12px 18px 0;
264
+ padding: 10px 12px;
265
+ border: 1px solid var(--rs-line-strong);
266
+ border-radius: 8px;
267
+ background: var(--rs-layer-1);
268
+ }
269
+
270
+ .dshr-alert strong { color: var(--rs-danger); font-size: 12px; }
271
+ .dshr-alert span { color: var(--rs-muted); font-size: 12px; }
272
+
273
+ /* Nav tabs */
274
+ .dshr-nav {
275
+ display: flex;
276
+ flex: 0 0 auto;
277
+ gap: 2px;
278
+ padding: 0 14px;
279
+ border-bottom: 1px solid var(--rs-line);
280
+ }
281
+
282
+ .dshr-nav button {
283
+ position: relative;
284
+ min-height: 42px;
285
+ padding: 0 12px;
286
+ border: none;
287
+ color: var(--rs-muted);
288
+ background: transparent;
289
+ font-size: 13px;
290
+ cursor: pointer;
291
+ }
292
+
293
+ .dshr-nav button:hover { color: var(--rs-text); }
294
+
295
+ .dshr-nav button[aria-current='page'] {
296
+ color: var(--rs-text);
297
+ font-weight: 600;
298
+ }
299
+
300
+ .dshr-nav button[aria-current='page']::after {
301
+ position: absolute;
302
+ right: 0;
303
+ bottom: -1px;
304
+ left: 0;
305
+ height: 2px;
306
+ border-radius: 2px 2px 0 0;
307
+ content: '';
308
+ background: var(--rs-accent);
309
+ }
310
+
311
+ .dshr-navCount {
312
+ display: inline-block;
313
+ margin-left: 5px;
314
+ padding: 0 6px;
315
+ border-radius: 9px;
316
+ color: var(--rs-faint);
317
+ background: var(--rs-layer-2);
318
+ font-size: 11px;
319
+ line-height: 16px;
320
+ }
321
+
322
+ /* Content */
323
+ .dshr-content {
324
+ display: flex;
325
+ min-width: 0;
326
+ min-height: 0;
327
+ flex: 1 1 auto;
328
+ flex-direction: column;
329
+ overflow: auto;
330
+ }
331
+
332
+ .dshr-empty {
333
+ display: flex;
334
+ min-height: 120px;
335
+ align-items: center;
336
+ justify-content: center;
337
+ color: var(--rs-faint);
338
+ font-size: 13px;
339
+ }
340
+
341
+ /* Accounts table */
342
+ .dshr-tableWrap {
343
+ overflow: auto;
344
+ padding: 12px 18px;
345
+ }
346
+
347
+ .dshr-table {
348
+ width: 100%;
349
+ border-collapse: collapse;
350
+ font-size: 12px;
351
+ }
352
+
353
+ .dshr-table th {
354
+ position: sticky;
355
+ top: 0;
356
+ padding: 8px 10px;
357
+ border-bottom: 1px solid var(--rs-line-strong);
358
+ color: var(--rs-faint);
359
+ background: var(--rs-bg);
360
+ font-size: 11px;
361
+ font-weight: 600;
362
+ text-align: left;
363
+ white-space: nowrap;
364
+ }
365
+
366
+ .dshr-table td {
367
+ padding: 7px 10px;
368
+ border-bottom: 1px solid var(--rs-line);
369
+ vertical-align: middle;
370
+ }
371
+
372
+ .dshr-table tbody tr:hover { background: var(--rs-hover); }
373
+
374
+ .dshr-mono { font-family: var(--rs-mono); font-size: 11px; }
375
+ .dshr-num { text-align: right; }
376
+ .dshr-muted { color: var(--rs-faint); }
377
+ .dshr-reason { color: var(--rs-muted); font-size: 11px; }
378
+
379
+ .dshr-badge {
380
+ display: inline-block;
381
+ padding: 1px 8px;
382
+ border-radius: 9px;
383
+ font-size: 11px;
384
+ line-height: 16px;
385
+ }
386
+
387
+ .dshr-badge-ok { color: var(--rs-success); background: color-mix(in srgb, var(--rs-success) 12%, transparent); }
388
+ .dshr-badge-warn { color: var(--rs-warn); background: color-mix(in srgb, var(--rs-warn) 14%, transparent); }
389
+ .dshr-badge-danger { color: var(--rs-danger); background: color-mix(in srgb, var(--rs-danger) 12%, transparent); }
390
+
391
+ /* Models grid */
392
+ .dshr-modelGrid {
393
+ display: grid;
394
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
395
+ gap: 10px;
396
+ padding: 14px 18px;
397
+ }
398
+
399
+ .dshr-modelCard {
400
+ display: flex;
401
+ min-width: 0;
402
+ align-items: center;
403
+ justify-content: space-between;
404
+ gap: 10px;
405
+ padding: 10px 12px;
406
+ border: 1px solid var(--rs-line);
407
+ border-radius: 8px;
408
+ background: var(--rs-layer-1);
409
+ }
410
+
411
+ .dshr-modelCard:hover { border-color: var(--rs-line-strong); }
412
+
413
+ .dshr-modelId {
414
+ overflow: hidden;
415
+ font-family: var(--rs-mono);
416
+ font-size: 12px;
417
+ text-overflow: ellipsis;
418
+ white-space: nowrap;
419
+ }
420
+
421
+ .dshr-modelCtx {
422
+ flex: none;
423
+ color: var(--rs-faint);
424
+ font-size: 10px;
425
+ }
426
+
427
+ /* ============================================================
428
+ dsh-router — 9router 风格 tab 内容(供应商 / 组合 / 端点与密钥)
429
+ ============================================================ */
430
+
431
+ .dshr-tabBody {
432
+ display: flex;
433
+ min-width: 0;
434
+ min-height: 0;
435
+ flex-direction: column;
436
+ gap: 14px;
437
+ padding: 14px 18px;
438
+ }
439
+
440
+ .dshr-card {
441
+ min-width: 0;
442
+ border: 1px solid var(--rs-line);
443
+ border-radius: 10px;
444
+ background: var(--rs-layer-1);
445
+ overflow: hidden;
446
+ }
447
+
448
+ .dshr-cardHead {
449
+ display: flex;
450
+ min-height: 44px;
451
+ align-items: center;
452
+ gap: 10px;
453
+ padding: 10px 14px;
454
+ border-bottom: 1px solid var(--rs-line);
455
+ }
456
+
457
+ .dshr-cardTitle {
458
+ font-size: 13px;
459
+ font-weight: 650;
460
+ }
461
+
462
+ .dshr-cardMeta {
463
+ margin-left: auto;
464
+ font-size: 11px;
465
+ white-space: nowrap;
466
+ }
467
+
468
+ /* 组合链 */
469
+ .dshr-comboChain {
470
+ display: flex;
471
+ flex-wrap: wrap;
472
+ align-items: center;
473
+ gap: 8px;
474
+ padding: 14px;
475
+ }
476
+
477
+ .dshr-comboStep {
478
+ display: inline-flex;
479
+ align-items: center;
480
+ gap: 6px;
481
+ }
482
+
483
+ .dshr-comboArrow {
484
+ color: var(--rs-faint);
485
+ margin-right: 8px;
486
+ }
487
+
488
+ .dshr-comboSupplier {
489
+ font-family: var(--rs-mono);
490
+ font-size: 12px;
491
+ }
492
+
493
+ /* 端点与密钥 */
494
+ .dshr-endpointRow {
495
+ display: flex;
496
+ align-items: center;
497
+ gap: 8px;
498
+ padding: 12px 14px;
499
+ }
500
+
501
+ .dshr-endpointCode {
502
+ min-width: 0;
503
+ flex: 1 1 auto;
504
+ overflow: hidden;
505
+ padding: 8px 10px;
506
+ border: 1px solid var(--rs-line);
507
+ border-radius: 6px;
508
+ background: var(--rs-layer-2);
509
+ color: var(--rs-text);
510
+ font-size: 12px;
511
+ text-overflow: ellipsis;
512
+ white-space: nowrap;
513
+ }
514
+
515
+ .dshr-miniButton {
516
+ display: inline-flex;
517
+ flex: none;
518
+ height: 28px;
519
+ align-items: center;
520
+ justify-content: center;
521
+ padding: 0 10px;
522
+ border: 1px solid var(--rs-line);
523
+ border-radius: 6px;
524
+ color: var(--rs-muted);
525
+ background: var(--rs-layer-1);
526
+ font-size: 12px;
527
+ cursor: pointer;
528
+ white-space: nowrap;
529
+ }
530
+
531
+ .dshr-miniButton:hover { color: var(--rs-text); background: var(--rs-hover); }
532
+
533
+ .dshr-code {
534
+ margin: 0;
535
+ padding: 12px 14px;
536
+ overflow-x: auto;
537
+ color: var(--rs-text);
538
+ background: var(--rs-layer-2);
539
+ font: 12px/1.6 var(--rs-mono);
540
+ white-space: pre-wrap;
541
+ word-break: break-all;
542
+ }
543
+
544
+ /* ============================================================
545
+ dsh-router — 供应商详情 / 端点与密钥 / 通用控件
546
+ ============================================================ */
547
+
548
+ /* 详情页头 */
549
+ .dshr-detailHead {
550
+ display: flex;
551
+ align-items: center;
552
+ gap: 10px;
553
+ min-height: 36px;
554
+ }
555
+
556
+ .dshr-detailTitle {
557
+ font-size: 14px;
558
+ font-weight: 650;
559
+ }
560
+
561
+ /* 可点击卡片 */
562
+ .dshr-card-click {
563
+ cursor: pointer;
564
+ transition: border-color .12s ease;
565
+ }
566
+
567
+ .dshr-card-click:hover { border-color: var(--rs-accent); }
568
+
569
+ .dshr-cardHint {
570
+ padding: 0 14px 12px;
571
+ }
572
+
573
+ /* 供应商小卡片(grid 布局,替代整行大卡片) */
574
+ .dshr-supplierGrid {
575
+ display: grid;
576
+ grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
577
+ gap: 12px;
578
+ }
579
+
580
+ /* 供应商分组(内置 / 插件分开) */
581
+ .dshr-supplierGroup {
582
+ display: flex;
583
+ flex-direction: column;
584
+ gap: 10px;
585
+ }
586
+
587
+ .dshr-supplierGroup + .dshr-supplierGroup {
588
+ margin-top: 22px;
589
+ }
590
+
591
+ .dshr-supplierGroupTitle {
592
+ font-size: 12px;
593
+ font-weight: 600;
594
+ color: var(--rs-muted);
595
+ letter-spacing: .04em;
596
+ padding-left: 2px;
597
+ }
598
+
599
+ .dshr-supplierCard {
600
+ display: flex;
601
+ flex-direction: column;
602
+ gap: 8px;
603
+ min-width: 0;
604
+ padding: 16px 18px;
605
+ border: 1px solid var(--rs-line);
606
+ border-radius: 12px;
607
+ background: var(--rs-layer-1);
608
+ cursor: pointer;
609
+ transition: border-color .12s ease;
610
+ }
611
+
612
+ .dshr-supplierCard:hover { border-color: var(--rs-accent); }
613
+
614
+ .dshr-supplierName {
615
+ font-size: 14px;
616
+ font-weight: 650;
617
+ white-space: nowrap;
618
+ overflow: hidden;
619
+ text-overflow: ellipsis;
620
+ }
621
+
622
+ .dshr-supplierRow {
623
+ display: flex;
624
+ align-items: center;
625
+ gap: 9px;
626
+ min-width: 0;
627
+ }
628
+
629
+ .dshr-supplierIcon {
630
+ width: 24px;
631
+ height: 24px;
632
+ flex: none;
633
+ border-radius: 5px;
634
+ object-fit: contain;
635
+ color: var(--rs-faint);
636
+ }
637
+
638
+ .dshr-supplierMeta {
639
+ display: flex;
640
+ align-items: center;
641
+ gap: 8px;
642
+ font-size: 11px;
643
+ white-space: nowrap;
644
+ }
645
+
646
+ .dshr-chevron {
647
+ color: var(--rs-faint);
648
+ font-size: 18px;
649
+ line-height: 1;
650
+ margin-left: auto;
651
+ }
652
+
653
+ /* 登录框 */
654
+ .dshr-loginBox {
655
+ display: flex;
656
+ flex-direction: column;
657
+ gap: 8px;
658
+ padding: 12px 14px;
659
+ border-bottom: 1px solid var(--rs-line);
660
+ background: var(--rs-layer-2);
661
+ }
662
+
663
+ .dshr-loginUrl {
664
+ display: block;
665
+ overflow: hidden;
666
+ padding: 8px 10px;
667
+ border: 1px solid var(--rs-line);
668
+ border-radius: 6px;
669
+ background: var(--rs-layer-1);
670
+ color: var(--rs-accent);
671
+ font: 11px/1.5 var(--rs-mono);
672
+ text-overflow: ellipsis;
673
+ white-space: nowrap;
674
+ word-break: break-all;
675
+ }
676
+
677
+ /* 输入框 */
678
+ .dshr-input {
679
+ flex: 1 1 auto;
680
+ min-width: 0;
681
+ height: 30px;
682
+ padding: 0 10px;
683
+ border: 1px solid var(--rs-line);
684
+ border-radius: 6px;
685
+ background: var(--rs-layer-1);
686
+ color: var(--rs-text);
687
+ font-size: 12px;
688
+ }
689
+
690
+ .dshr-input:focus {
691
+ outline: none;
692
+ border-color: var(--rs-accent);
693
+ }
694
+
695
+ /* 成功提示 */
696
+ .dshr-alert-ok {
697
+ border-color: color-mix(in srgb, var(--rs-ok) 45%, transparent);
698
+ background: color-mix(in srgb, var(--rs-ok) 10%, transparent);
699
+ }
700
+
701
+ .dshr-createdKey {
702
+ display: block;
703
+ margin: 6px 0;
704
+ padding: 8px 10px;
705
+ border-radius: 6px;
706
+ background: var(--rs-layer-1);
707
+ word-break: break-all;
708
+ }
709
+
710
+ /* 模型卡片行 */
711
+ .dshr-modelRow {
712
+ display: flex;
713
+ align-items: center;
714
+ gap: 8px;
715
+ min-width: 0;
716
+ }
717
+
718
+ .dshr-modelCard-off {
719
+ opacity: .55;
720
+ }
721
+
722
+ /* 开关 */
723
+ .dshr-toggle {
724
+ position: relative;
725
+ flex: none;
726
+ width: 30px;
727
+ height: 16px;
728
+ border: none;
729
+ border-radius: 8px;
730
+ background: var(--rs-line);
731
+ cursor: pointer;
732
+ padding: 0;
733
+ transition: background .15s ease;
734
+ }
735
+
736
+ .dshr-toggle-on { background: var(--rs-ok); }
737
+
738
+ .dshr-toggleKnob {
739
+ position: absolute;
740
+ top: 2px;
741
+ left: 2px;
742
+ width: 12px;
743
+ height: 12px;
744
+ border-radius: 50%;
745
+ background: #fff;
746
+ transition: transform .15s ease;
747
+ }
748
+
749
+ .dshr-toggle-on .dshr-toggleKnob { transform: translateX(14px); }
750
+
751
+ /* 操作按钮组 */
752
+ .dshr-ops {
753
+ display: flex;
754
+ gap: 6px;
755
+ white-space: nowrap;
756
+ }
757
+
758
+ .dshr-danger { color: var(--rs-danger); }
759
+ .dshr-danger:hover { color: var(--rs-danger); border-color: var(--rs-danger); }
760
+
761
+ /* ============================================================
762
+ dsh-router — 弹窗 + 按钮 + 整体打磨
763
+ ============================================================ */
764
+
765
+ /* 弹窗 */
766
+ .dshr-modalOverlay {
767
+ position: fixed;
768
+ z-index: 1000;
769
+ inset: 0;
770
+ display: flex;
771
+ align-items: center;
772
+ justify-content: center;
773
+ padding: 24px;
774
+ background: color-mix(in srgb, #000 42%, transparent);
775
+ backdrop-filter: blur(2px);
776
+ animation: dshrFadeIn .12s ease;
777
+ }
778
+
779
+ .dshr-modal {
780
+ width: min(560px, 100%);
781
+ max-height: min(80vh, 640px);
782
+ display: flex;
783
+ flex-direction: column;
784
+ overflow: hidden;
785
+ border: 1px solid var(--rs-line-strong);
786
+ border-radius: 12px;
787
+ background: var(--rs-layer-1);
788
+ box-shadow: 0 20px 50px rgb(0 0 0 / .35), 0 0 0 1px rgb(0 0 0 / .05);
789
+ animation: dshrModalIn .16s ease;
790
+ }
791
+
792
+ .dshr-modalHead {
793
+ display: flex;
794
+ min-height: 48px;
795
+ flex: 0 0 auto;
796
+ align-items: center;
797
+ gap: 10px;
798
+ padding: 0 16px;
799
+ border-bottom: 1px solid var(--rs-line);
800
+ }
801
+
802
+ .dshr-modalTitle {
803
+ min-width: 0;
804
+ flex: 1 1 auto;
805
+ overflow: hidden;
806
+ font-size: 14px;
807
+ font-weight: 650;
808
+ text-overflow: ellipsis;
809
+ white-space: nowrap;
810
+ }
811
+
812
+ .dshr-modalClose {
813
+ display: inline-flex;
814
+ flex: none;
815
+ width: 26px;
816
+ height: 26px;
817
+ align-items: center;
818
+ justify-content: center;
819
+ border: none;
820
+ border-radius: 6px;
821
+ color: var(--rs-muted);
822
+ background: transparent;
823
+ cursor: pointer;
824
+ }
825
+
826
+ .dshr-modalClose:hover { color: var(--rs-text); background: var(--rs-hover); }
827
+
828
+ .dshr-modalBody {
829
+ min-height: 0;
830
+ overflow: auto;
831
+ padding: 16px;
832
+ }
833
+
834
+ .dshr-modalActions {
835
+ display: flex;
836
+ justify-content: flex-end;
837
+ gap: 8px;
838
+ margin-top: 18px;
839
+ }
840
+
841
+ @keyframes dshrFadeIn { from { opacity: 0; } to { opacity: 1; } }
842
+ @keyframes dshrModalIn { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: none; } }
843
+
844
+ /* 按钮 */
845
+ .dshr-shell .dshr-primaryButton {
846
+ display: inline-flex;
847
+ height: 30px;
848
+ align-items: center;
849
+ justify-content: center;
850
+ padding: 0 14px;
851
+ border: none;
852
+ border-radius: 7px;
853
+ color: #fff;
854
+ background: var(--rs-accent);
855
+ font-size: 12px;
856
+ font-weight: 600;
857
+ cursor: pointer;
858
+ white-space: nowrap;
859
+ transition: filter .12s ease;
860
+ }
861
+
862
+ .dshr-shell .dshr-primaryButton:hover { filter: brightness(1.08); }
863
+ .dshr-shell .dshr-primaryButton:disabled { opacity: .55; cursor: default; }
864
+
865
+ .dshr-shell .dshr-dangerButton {
866
+ display: inline-flex;
867
+ height: 30px;
868
+ align-items: center;
869
+ justify-content: center;
870
+ padding: 0 14px;
871
+ border: none;
872
+ border-radius: 7px;
873
+ color: #fff;
874
+ background: var(--rs-danger);
875
+ font-size: 12px;
876
+ font-weight: 600;
877
+ cursor: pointer;
878
+ white-space: nowrap;
879
+ transition: filter .12s ease;
880
+ }
881
+
882
+ .dshr-shell .dshr-dangerButton:hover { filter: brightness(1.1); }
883
+
884
+ /* 登录步骤 */
885
+ .dshr-loginBox {
886
+ display: flex;
887
+ flex-direction: column;
888
+ gap: 10px;
889
+ }
890
+
891
+ .dshr-loginStep {
892
+ display: flex;
893
+ align-items: flex-start;
894
+ gap: 8px;
895
+ color: var(--rs-muted);
896
+ font-size: 12px;
897
+ line-height: 1.6;
898
+ }
899
+
900
+ .dshr-loginNum {
901
+ display: inline-flex;
902
+ flex: none;
903
+ width: 18px;
904
+ height: 18px;
905
+ align-items: center;
906
+ justify-content: center;
907
+ border-radius: 50%;
908
+ color: var(--rs-accent);
909
+ background: color-mix(in srgb, var(--rs-accent) 14%, transparent);
910
+ font-size: 11px;
911
+ font-weight: 700;
912
+ }
913
+
914
+ .dshr-loginUrl {
915
+ display: block;
916
+ overflow: hidden;
917
+ padding: 9px 11px;
918
+ border: 1px solid var(--rs-line);
919
+ border-radius: 8px;
920
+ background: var(--rs-layer-2);
921
+ color: var(--rs-accent);
922
+ font: 11px/1.5 var(--rs-mono);
923
+ text-overflow: ellipsis;
924
+ white-space: nowrap;
925
+ word-break: break-all;
926
+ }
927
+
928
+ /* 新 Key 弹窗 */
929
+ .dshr-createdKeyBox {
930
+ display: flex;
931
+ flex-direction: column;
932
+ gap: 10px;
933
+ }
934
+
935
+ .dshr-createdKey {
936
+ padding: 10px 12px;
937
+ border-radius: 8px;
938
+ background: var(--rs-layer-2);
939
+ word-break: break-all;
940
+ user-select: all;
941
+ }
942
+
943
+ /* ============================================================
944
+ dsh-router — 整体打磨
945
+ ============================================================ */
946
+
947
+ /* Masthead: 细腻渐变 + 底部微阴影 */
948
+ .dshr-masthead {
949
+ background: linear-gradient(to bottom, color-mix(in srgb, var(--rs-layer-1) 62%, transparent), transparent);
950
+ }
951
+
952
+ /* Brand 标题更突出 */
953
+ .dshr-brandTitle {
954
+ letter-spacing: .01em;
955
+ }
956
+
957
+ /* 卡片:阴影 + hover 微抬升 */
958
+ .dshr-card {
959
+ box-shadow: 0 1px 2px rgb(0 0 0 / .04);
960
+ transition: box-shadow .15s ease, transform .15s ease, border-color .15s ease;
961
+ }
962
+
963
+ .dshr-card-click:hover {
964
+ transform: translateY(-1px);
965
+ border-color: color-mix(in srgb, var(--rs-accent) 55%, var(--rs-line));
966
+ box-shadow: 0 6px 18px rgb(0 0 0 / .10);
967
+ }
968
+
969
+ /* Tab:悬停背景 + 计数徽标配色 */
970
+ .dshr-nav button {
971
+ transition: color .12s ease, background .12s ease;
972
+ }
973
+
974
+ .dshr-nav button:hover {
975
+ background: var(--rs-hover);
976
+ border-radius: 6px 6px 0 0;
977
+ }
978
+
979
+ .dshr-navCount {
980
+ background: color-mix(in srgb, var(--rs-accent) 12%, transparent);
981
+ color: var(--rs-accent);
982
+ font-weight: 600;
983
+ }
984
+
985
+ /* 徽标:更柔和 */
986
+ .dshr-badge {
987
+ box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 12%, transparent);
988
+ }
989
+
990
+ /* 表格行悬停 + 表头层级 */
991
+ .dshr-table tbody tr { transition: background .1s ease; }
992
+ .dshr-table th { letter-spacing: .02em; }
993
+
994
+ /* 输入框聚焦 */
995
+ .dshr-input:focus {
996
+ border-color: var(--rs-accent);
997
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--rs-accent) 18%, transparent);
998
+ }
999
+
1000
+ /* 开关微过渡 */
1001
+ .dshr-toggle { transition: background .15s ease, box-shadow .15s ease; }
1002
+ .dshr-toggle:hover { box-shadow: 0 0 0 3px color-mix(in srgb, var(--rs-accent) 18%, transparent); }
1003
+
1004
+ /* 内容区细滚动条 */
1005
+ .dshr-content::-webkit-scrollbar,
1006
+ .dshr-tableWrap::-webkit-scrollbar,
1007
+ .dshr-modalBody::-webkit-scrollbar {
1008
+ width: 8px;
1009
+ height: 8px;
1010
+ }
1011
+
1012
+ .dshr-content::-webkit-scrollbar-thumb,
1013
+ .dshr-tableWrap::-webkit-scrollbar-thumb,
1014
+ .dshr-modalBody::-webkit-scrollbar-thumb {
1015
+ border-radius: 4px;
1016
+ background: var(--rs-line-strong);
1017
+ }
1018
+
1019
+ .dshr-content::-webkit-scrollbar-track,
1020
+ .dshr-tableWrap::-webkit-scrollbar-track,
1021
+ .dshr-modalBody::-webkit-scrollbar-track {
1022
+ background: transparent;
1023
+ }
1024
+
1025
+ /* ============================================================
1026
+ dsh-router — 端点与密钥(9router 风格布局)
1027
+ ============================================================ */
1028
+
1029
+ /* 卡片头图标 */
1030
+ .dshr-cardIcon {
1031
+ display: inline-flex;
1032
+ flex: none;
1033
+ align-items: center;
1034
+ justify-content: center;
1035
+ width: 24px;
1036
+ height: 24px;
1037
+ border-radius: 6px;
1038
+ color: var(--rs-accent);
1039
+ background: color-mix(in srgb, var(--rs-accent) 12%, transparent);
1040
+ }
1041
+
1042
+ .dshr-cardAction {
1043
+ margin-left: auto;
1044
+ }
1045
+
1046
+ /* Endpoint 列表 */
1047
+ .dshr-endpointList {
1048
+ display: flex;
1049
+ flex-direction: column;
1050
+ gap: 4px;
1051
+ padding: 12px 14px;
1052
+ }
1053
+
1054
+ .dshr-endpointRow {
1055
+ display: flex;
1056
+ align-items: center;
1057
+ gap: 8px;
1058
+ }
1059
+
1060
+ .dshr-endpointLabel {
1061
+ flex: none;
1062
+ min-width: 88px;
1063
+ padding: 3px 8px;
1064
+ border-radius: 6px;
1065
+ color: var(--rs-muted);
1066
+ background: var(--rs-layer-2);
1067
+ font: 11px var(--rs-mono);
1068
+ text-align: center;
1069
+ }
1070
+
1071
+ .dshr-endpointRow .dshr-input {
1072
+ flex: 1 1 auto;
1073
+ min-width: 0;
1074
+ color: var(--rs-muted);
1075
+ }
1076
+
1077
+ .dshr-endpointNote {
1078
+ padding: 2px 2px 0;
1079
+ }
1080
+
1081
+ /* 图标按钮 */
1082
+ .dshr-iconBtn {
1083
+ display: inline-flex;
1084
+ flex: none;
1085
+ width: 30px;
1086
+ height: 30px;
1087
+ align-items: center;
1088
+ justify-content: center;
1089
+ border: none;
1090
+ border-radius: 6px;
1091
+ color: var(--rs-muted);
1092
+ background: transparent;
1093
+ cursor: pointer;
1094
+ transition: color .12s ease, background .12s ease;
1095
+ }
1096
+
1097
+ .dshr-iconBtn:hover { color: var(--rs-accent); background: var(--rs-hover); }
1098
+
1099
+ .dshr-iconBtn svg { display: block; }
1100
+
1101
+ .dshr-iconBtn-sm { width: 26px; height: 26px; }
1102
+
1103
+ /* Require API key 行 */
1104
+ .dshr-requireRow {
1105
+ display: flex;
1106
+ align-items: center;
1107
+ justify-content: space-between;
1108
+ gap: 12px;
1109
+ padding: 12px 14px;
1110
+ border-bottom: 1px solid var(--rs-line);
1111
+ }
1112
+
1113
+ .dshr-requireTitle {
1114
+ margin: 0;
1115
+ font-size: 13px;
1116
+ font-weight: 600;
1117
+ }
1118
+
1119
+ .dshr-requireDesc {
1120
+ margin: 2px 0 0;
1121
+ color: var(--rs-faint);
1122
+ font-size: 12px;
1123
+ }
1124
+
1125
+ /* 空状态 */
1126
+ .dshr-keyEmpty {
1127
+ display: flex;
1128
+ flex-direction: column;
1129
+ align-items: center;
1130
+ gap: 8px;
1131
+ padding: 40px 16px 44px;
1132
+ }
1133
+
1134
+ .dshr-keyEmptyIcon {
1135
+ display: inline-flex;
1136
+ align-items: center;
1137
+ justify-content: center;
1138
+ width: 56px;
1139
+ height: 56px;
1140
+ margin-bottom: 6px;
1141
+ border-radius: 50%;
1142
+ color: var(--rs-accent);
1143
+ background: color-mix(in srgb, var(--rs-accent) 10%, transparent);
1144
+ }
1145
+
1146
+ .dshr-keyEmptyTitle {
1147
+ margin: 0;
1148
+ font-size: 14px;
1149
+ font-weight: 600;
1150
+ }
1151
+
1152
+ .dshr-keyEmptyDesc {
1153
+ margin: 0 0 8px;
1154
+ color: var(--rs-faint);
1155
+ font-size: 12px;
1156
+ }
1157
+
1158
+ /* key 列表(flex 行,非表格) */
1159
+ .dshr-keyList {
1160
+ display: flex;
1161
+ flex-direction: column;
1162
+ }
1163
+
1164
+ .dshr-keyRow {
1165
+ display: flex;
1166
+ align-items: center;
1167
+ justify-content: space-between;
1168
+ gap: 12px;
1169
+ padding: 12px 14px;
1170
+ border-bottom: 1px solid var(--rs-line);
1171
+ transition: opacity .12s ease;
1172
+ }
1173
+
1174
+ .dshr-keyRow:last-child { border-bottom: none; }
1175
+ .dshr-keyRow-paused { opacity: .6; }
1176
+
1177
+ .dshr-keyInfo {
1178
+ min-width: 0;
1179
+ flex: 1 1 auto;
1180
+ }
1181
+
1182
+ .dshr-keyNameRow {
1183
+ display: flex;
1184
+ align-items: center;
1185
+ gap: 4px;
1186
+ }
1187
+
1188
+ .dshr-keyName {
1189
+ margin: 0;
1190
+ font-size: 13px;
1191
+ font-weight: 600;
1192
+ }
1193
+
1194
+ .dshr-keyValue {
1195
+ display: flex;
1196
+ align-items: center;
1197
+ gap: 2px;
1198
+ margin-top: 2px;
1199
+ }
1200
+
1201
+ .dshr-keyValue code {
1202
+ overflow: hidden;
1203
+ color: var(--rs-muted);
1204
+ text-overflow: ellipsis;
1205
+ white-space: nowrap;
1206
+ }
1207
+
1208
+ .dshr-keyCreated {
1209
+ margin: 3px 0 0;
1210
+ color: var(--rs-faint);
1211
+ font-size: 11px;
1212
+ }
1213
+
1214
+ .dshr-keyPaused { color: var(--rs-warn); }
1215
+
1216
+ .dshr-keyOps {
1217
+ display: flex;
1218
+ flex: none;
1219
+ align-items: center;
1220
+ gap: 4px;
1221
+ }
1222
+
1223
+ /* 删除按钮 hover 才明显(9router:group-hover 显示) */
1224
+ .dshr-keyDelete {
1225
+ color: var(--rs-muted);
1226
+ opacity: 0;
1227
+ transition: opacity .12s ease, color .12s ease, background .12s ease;
1228
+ }
1229
+
1230
+ .dshr-keyRow:hover .dshr-keyDelete { opacity: 1; }
1231
+ .dshr-keyDelete:focus-visible { opacity: 1; }
1232
+ .dshr-keyDelete:hover {
1233
+ color: var(--rs-danger);
1234
+ background: color-mix(in srgb, var(--rs-danger) 14%, transparent);
1235
+ }
1236
+
1237
+ /* 弹窗表单 */
1238
+ .dshr-modalForm {
1239
+ display: flex;
1240
+ flex-direction: column;
1241
+ gap: 14px;
1242
+ }
1243
+
1244
+ .dshr-fieldLabel {
1245
+ font-size: 12px;
1246
+ font-weight: 650;
1247
+ color: var(--rs-text);
1248
+ margin-top: 4px;
1249
+ }
1250
+
1251
+ .dshr-warnBox {
1252
+ padding: 12px 14px;
1253
+ border: 1px solid color-mix(in srgb, var(--rs-warn) 40%, transparent);
1254
+ border-radius: 8px;
1255
+ background: color-mix(in srgb, var(--rs-warn) 8%, transparent);
1256
+ }
1257
+
1258
+ .dshr-warnTitle {
1259
+ margin: 0 0 4px;
1260
+ color: var(--rs-warn);
1261
+ font-size: 13px;
1262
+ font-weight: 700;
1263
+ }
1264
+
1265
+ .dshr-warnDesc {
1266
+ margin: 0;
1267
+ color: var(--rs-muted);
1268
+ font-size: 12px;
1269
+ }
1270
+
1271
+ .dshr-createdRow { padding: 0; }
1272
+
1273
+ .dshr-createdRow .dshr-input { color: var(--rs-text); }
1274
+
1275
+ .dshr-fullButton { width: 100%; }
1276
+
1277
+ /* ============================================================
1278
+ dsh-router — 供应商详情(9router 风格)
1279
+ ============================================================ */
1280
+
1281
+ /* Header */
1282
+ .dshr-providerHead {
1283
+ display: flex;
1284
+ flex-direction: column;
1285
+ gap: 10px;
1286
+ }
1287
+
1288
+ .dshr-backLink {
1289
+ display: inline-flex;
1290
+ align-items: center;
1291
+ align-self: flex-start;
1292
+ gap: 4px;
1293
+ border: none;
1294
+ padding: 2px 0;
1295
+ color: var(--rs-muted);
1296
+ background: transparent;
1297
+ font-size: 12px;
1298
+ cursor: pointer;
1299
+ transition: color .12s ease;
1300
+ }
1301
+
1302
+ .dshr-backLink:hover { color: var(--rs-accent); }
1303
+
1304
+ .dshr-providerTitleRow {
1305
+ display: flex;
1306
+ align-items: center;
1307
+ gap: 12px;
1308
+ }
1309
+
1310
+ .dshr-providerIcon {
1311
+ display: inline-flex;
1312
+ flex: none;
1313
+ width: 44px;
1314
+ height: 44px;
1315
+ align-items: center;
1316
+ justify-content: center;
1317
+ border-radius: 10px;
1318
+ }
1319
+
1320
+ .dshr-providerImg {
1321
+ width: 100%;
1322
+ height: 100%;
1323
+ object-fit: contain;
1324
+ }
1325
+
1326
+ .dshr-providerName {
1327
+ margin: 0;
1328
+ font-size: 20px;
1329
+ font-weight: 700;
1330
+ letter-spacing: -.01em;
1331
+ }
1332
+
1333
+ .dshr-providerCount {
1334
+ margin: 2px 0 0;
1335
+ color: var(--rs-muted);
1336
+ font-size: 12px;
1337
+ }
1338
+
1339
+ /* 链接池空状态 */
1340
+ .dshr-linkEmpty {
1341
+ display: flex;
1342
+ flex-direction: column;
1343
+ align-items: center;
1344
+ gap: 10px;
1345
+ padding: 36px 16px 40px;
1346
+ }
1347
+
1348
+ .dshr-linkEmptyIcon {
1349
+ display: inline-flex;
1350
+ align-items: center;
1351
+ justify-content: center;
1352
+ width: 52px;
1353
+ height: 52px;
1354
+ border-radius: 50%;
1355
+ color: var(--rs-accent);
1356
+ background: color-mix(in srgb, var(--rs-accent) 10%, transparent);
1357
+ }
1358
+
1359
+ .dshr-linkEmptyText { text-align: center; }
1360
+
1361
+ .dshr-linkEmptyTitle {
1362
+ margin: 0;
1363
+ font-size: 14px;
1364
+ font-weight: 600;
1365
+ }
1366
+
1367
+ .dshr-linkEmptyDesc {
1368
+ margin: 2px 0 0;
1369
+ color: var(--rs-faint);
1370
+ font-size: 12px;
1371
+ }
1372
+
1373
+ /* 链接列表(9router ConnectionRow 风格行) */
1374
+ .dshr-linkList {
1375
+ display: flex;
1376
+ flex-direction: column;
1377
+ }
1378
+
1379
+ .dshr-linkRow {
1380
+ display: flex;
1381
+ align-items: center;
1382
+ justify-content: space-between;
1383
+ gap: 12px;
1384
+ padding: 12px 14px;
1385
+ border-bottom: 1px solid var(--rs-line);
1386
+ transition: background .1s ease;
1387
+ }
1388
+
1389
+ .dshr-linkRow:last-child { border-bottom: none; }
1390
+ .dshr-linkRow:hover { background: var(--rs-hover); }
1391
+ .dshr-linkRow[draggable='true'] { cursor: grab; }
1392
+ .dshr-linkRow[draggable='true']:active { cursor: grabbing; }
1393
+ .dshr-linkRowDragging { opacity: .45; background: var(--rs-hover); }
1394
+
1395
+ .dshr-linkGrip {
1396
+ display: inline-flex;
1397
+ align-items: center;
1398
+ justify-content: center;
1399
+ color: var(--rs-muted);
1400
+ flex: 0 0 auto;
1401
+ cursor: grab;
1402
+ padding: 4px 2px;
1403
+ user-select: none;
1404
+ }
1405
+ .dshr-linkGrip:active { cursor: grabbing; }
1406
+ .dshr-linkGrip svg { opacity: .65; }
1407
+ .dshr-linkRow:hover .dshr-linkGrip svg { opacity: 1; }
1408
+
1409
+ .dshr-linkMain {
1410
+ min-width: 0;
1411
+ flex: 1 1 auto;
1412
+ }
1413
+
1414
+ .dshr-linkTitleLine {
1415
+ display: flex;
1416
+ align-items: center;
1417
+ gap: 8px;
1418
+ }
1419
+
1420
+ .dshr-linkName {
1421
+ overflow: hidden;
1422
+ font-size: 13px;
1423
+ font-weight: 600;
1424
+ text-overflow: ellipsis;
1425
+ white-space: nowrap;
1426
+ }
1427
+
1428
+ .dshr-linkSub {
1429
+ display: flex;
1430
+ flex-wrap: wrap;
1431
+ align-items: center;
1432
+ gap: 6px;
1433
+ margin-top: 3px;
1434
+ color: var(--rs-faint);
1435
+ font-size: 11px;
1436
+ }
1437
+
1438
+ .dshr-linkUid { color: var(--rs-faint); }
1439
+ .dshr-linkCredits { color: var(--rs-muted); }
1440
+ .dshr-linkErr { color: var(--rs-warn); }
1441
+ .dshr-linkCooling { color: var(--rs-warn); }
1442
+ .dshr-linkDot { color: var(--rs-faint); opacity: .6; }
1443
+
1444
+ .dshr-linkOps {
1445
+ display: flex;
1446
+ flex: none;
1447
+ align-items: center;
1448
+ }
1449
+
1450
+ /* 删除链接 hover 显示 + 红色 */
1451
+ .dshr-linkDelete {
1452
+ color: var(--rs-muted);
1453
+ opacity: 0;
1454
+ transition: opacity .12s ease, color .12s ease, background .12s ease;
1455
+ }
1456
+
1457
+ .dshr-linkRow:hover .dshr-linkDelete { opacity: 1; }
1458
+ .dshr-linkDelete:focus-visible { opacity: 1; }
1459
+ .dshr-linkDelete:hover {
1460
+ color: var(--rs-danger);
1461
+ background: color-mix(in srgb, var(--rs-danger) 14%, transparent);
1462
+ }
1463
+
1464
+ /* 模型 chips(9router ModelRow 风格) */
1465
+ .dshr-modelChips {
1466
+ display: flex;
1467
+ flex-wrap: wrap;
1468
+ gap: 8px;
1469
+ padding: 14px;
1470
+ }
1471
+
1472
+ .dshr-modelChip {
1473
+ display: inline-flex;
1474
+ align-items: center;
1475
+ gap: 6px;
1476
+ padding: 6px 10px;
1477
+ border: 1px solid var(--rs-line);
1478
+ border-radius: 8px;
1479
+ color: var(--rs-text);
1480
+ background: var(--rs-layer-1);
1481
+ font-size: 12px;
1482
+ cursor: pointer;
1483
+ transition: border-color .12s ease, background .12s ease, color .12s ease;
1484
+ }
1485
+
1486
+ .dshr-modelChip:hover {
1487
+ border-color: var(--rs-accent);
1488
+ background: color-mix(in srgb, var(--rs-accent) 6%, transparent);
1489
+ }
1490
+
1491
+ .dshr-modelChip:disabled { opacity: .6; cursor: default; }
1492
+
1493
+ .dshr-modelChipId {
1494
+ font-family: var(--rs-mono);
1495
+ font-size: 11px;
1496
+ }
1497
+
1498
+ .dshr-modelChipCtx {
1499
+ flex: none;
1500
+ color: var(--rs-faint);
1501
+ font-size: 10px;
1502
+ }
1503
+
1504
+ .dshr-modelChip-off {
1505
+ border-style: dashed;
1506
+ color: var(--rs-faint);
1507
+ opacity: .8;
1508
+ }
1509
+
1510
+ .dshr-modelChip-off:hover {
1511
+ color: var(--rs-accent);
1512
+ border-color: var(--rs-accent);
1513
+ }
1514
+
1515
+ .dshr-modelChipRestore {
1516
+ color: var(--rs-accent);
1517
+ font-size: 10px;
1518
+ }
1519
+
1520
+ /* 已禁用区 */
1521
+ .dshr-disabledModels {
1522
+ width: 100%;
1523
+ }
1524
+
1525
+ .dshr-disabledLabel {
1526
+ margin: 4px 0 0;
1527
+ color: var(--rs-faint);
1528
+ font-size: 11px;
1529
+ }
1530
+
1531
+ .dshr-disabledModels .dshr-modelChips {
1532
+ padding: 8px 0 0;
1533
+ }
1534
+
1535
+ /* ============================================================
1536
+ dsh-router — 模型 chips 升级(9router 测试/复制 + 批量按钮)
1537
+ ============================================================ */
1538
+
1539
+ /* 卡片头操作组 */
1540
+ .dshr-cardActions {
1541
+ display: flex;
1542
+ align-items: center;
1543
+ gap: 6px;
1544
+ margin-left: auto;
1545
+ }
1546
+
1547
+ /* 连接池策略选择 */
1548
+ .dshr-poolStrategy {
1549
+ display: inline-flex;
1550
+ align-items: center;
1551
+ gap: 6px;
1552
+ font-size: 12px;
1553
+ }
1554
+ .dshr-select {
1555
+ appearance: none;
1556
+ border: 1px solid var(--rs-line);
1557
+ border-radius: 6px;
1558
+ background: var(--rs-surface, transparent);
1559
+ color: inherit;
1560
+ font-size: 12px;
1561
+ padding: 3px 22px 3px 8px;
1562
+ cursor: pointer;
1563
+ background-image: linear-gradient(45deg, transparent 50%, var(--rs-muted) 50%), linear-gradient(135deg, var(--rs-muted) 50%, transparent 50%);
1564
+ background-position: calc(100% - 12px) 55%, calc(100% - 7px) 55%;
1565
+ background-size: 5px 5px;
1566
+ background-repeat: no-repeat;
1567
+ }
1568
+ .dshr-select:focus { outline: none; border-color: var(--rs-accent, #4c7dff); }
1569
+
1570
+
1571
+ /* 模型 chip 包装(状态边框) */
1572
+ .dshr-modelChipWrap {
1573
+ position: relative;
1574
+ display: inline-flex;
1575
+ align-items: center;
1576
+ border: 1px solid var(--rs-line);
1577
+ border-radius: 8px;
1578
+ background: var(--rs-layer-1);
1579
+ transition: border-color .12s ease, background .12s ease;
1580
+ }
1581
+
1582
+ .dshr-modelChipWrap:hover {
1583
+ border-color: var(--rs-accent);
1584
+ background: color-mix(in srgb, var(--rs-accent) 6%, transparent);
1585
+ }
1586
+
1587
+ .dshr-modelChipWrap.dshr-modelChip-ok { border-color: var(--rs-ok); }
1588
+ .dshr-modelChipWrap.dshr-modelChip-err { border-color: var(--rs-danger); }
1589
+
1590
+ /* chip 本体(点击禁用) */
1591
+ .dshr-modelChipWrap .dshr-modelChip {
1592
+ display: inline-flex;
1593
+ align-items: center;
1594
+ gap: 6px;
1595
+ padding: 6px 8px 6px 6px;
1596
+ border: none;
1597
+ border-radius: 8px;
1598
+ color: var(--rs-text);
1599
+ background: transparent;
1600
+ font-size: 12px;
1601
+ cursor: pointer;
1602
+ }
1603
+
1604
+ .dshr-modelChipStatus {
1605
+ display: inline-flex;
1606
+ flex: none;
1607
+ align-items: center;
1608
+ justify-content: center;
1609
+ color: var(--rs-faint);
1610
+ }
1611
+
1612
+ .dshr-modelChipWrap.dshr-modelChip-ok .dshr-modelChipStatus { color: var(--rs-ok); }
1613
+ .dshr-modelChipWrap.dshr-modelChip-err .dshr-modelChipStatus { color: var(--rs-danger); }
1614
+
1615
+ /* chip 右侧操作(hover 显示) */
1616
+ .dshr-modelChipOps {
1617
+ display: inline-flex;
1618
+ align-items: center;
1619
+ gap: 0;
1620
+ padding-right: 4px;
1621
+ opacity: 0;
1622
+ transition: opacity .12s ease;
1623
+ }
1624
+
1625
+ .dshr-modelChipWrap:hover .dshr-modelChipOps,
1626
+ .dshr-modelChipOps:focus-within { opacity: 1; }
1627
+
1628
+ /* 旧 .dshr-modelChip 独立样式(disabled 区仍用) */
1629
+ .dshr-modelChip {
1630
+ display: inline-flex;
1631
+ align-items: center;
1632
+ gap: 6px;
1633
+ padding: 6px 10px;
1634
+ border: 1px solid var(--rs-line);
1635
+ border-radius: 8px;
1636
+ color: var(--rs-text);
1637
+ background: var(--rs-layer-1);
1638
+ font-size: 12px;
1639
+ cursor: pointer;
1640
+ transition: border-color .12s ease, background .12s ease, color .12s ease;
1641
+ }
1642
+
1643
+ .dshr-modelChip:hover {
1644
+ border-color: var(--rs-accent);
1645
+ background: color-mix(in srgb, var(--rs-accent) 6%, transparent);
1646
+ }
1647
+
1648
+ .dshr-modelChip:disabled { opacity: .6; cursor: default; }
1649
+
1650
+ .dshr-modelChipId {
1651
+ font-family: var(--rs-mono);
1652
+ font-size: 11px;
1653
+ }
1654
+
1655
+ .dshr-modelChipCtx {
1656
+ flex: none;
1657
+ color: var(--rs-faint);
1658
+ font-size: 10px;
1659
+ }
1660
+
1661
+ .dshr-modelChip-off {
1662
+ border-style: dashed;
1663
+ color: var(--rs-faint);
1664
+ opacity: .8;
1665
+ }
1666
+
1667
+ .dshr-modelChip-off:hover {
1668
+ color: var(--rs-accent);
1669
+ border-color: var(--rs-accent);
1670
+ }
1671
+
1672
+ .dshr-modelChipRestore {
1673
+ color: var(--rs-accent);
1674
+ font-size: 10px;
1675
+ }
1676
+
1677
+ /* 计数徽标跟在标题/操作按钮右侧(模型卡片) */
1678
+ .dshr-cardMetaAfter {
1679
+ margin-left: 0;
1680
+ }
1681
+
1682
+ /* 测试 loading 转圈 */
1683
+ .dshr-spin {
1684
+ display: inline-flex;
1685
+ animation: dshrSpin .8s linear infinite;
1686
+ }
1687
+
1688
+ @keyframes dshrSpin {
1689
+ to { transform: rotate(360deg); }
1690
+ }
1691
+
1692
+ /* 供应商标题行编辑按钮(靠右) */
1693
+ .dshr-providerEdit {
1694
+ margin-left: auto;
1695
+ }
1696
+
1697
+ /* 模型搜索栏 */
1698
+ .dshr-modelSearch {
1699
+ display: flex;
1700
+ align-items: center;
1701
+ gap: 6px;
1702
+ padding: 8px 14px 0;
1703
+ }
1704
+
1705
+ .dshr-modelSearchIcon {
1706
+ display: inline-flex;
1707
+ flex: none;
1708
+ align-items: center;
1709
+ justify-content: center;
1710
+ color: var(--rs-faint);
1711
+ }
1712
+
1713
+ .dshr-modelSearch .dshr-input {
1714
+ flex: 1 1 auto;
1715
+ min-width: 0;
1716
+ }
1717
+
1718
+ /* 自定义模型删除按钮(hover 红色) */
1719
+ .dshr-modelDelete {
1720
+ color: var(--rs-muted);
1721
+ transition: color .12s ease, background .12s ease;
1722
+ }
1723
+
1724
+ .dshr-modelDelete:hover {
1725
+ color: var(--rs-danger);
1726
+ background: color-mix(in srgb, var(--rs-danger) 14%, transparent);
1727
+ }
1728
+
1729
+ /* 全局浮窗提示(toast) */
1730
+ .dshr-toast {
1731
+ position: fixed;
1732
+ z-index: 2000;
1733
+ left: 50%;
1734
+ bottom: 28px;
1735
+ transform: translateX(-50%);
1736
+ padding: 8px 16px;
1737
+ border-radius: 8px;
1738
+ color: var(--rs-text);
1739
+ background: var(--rs-layer-2);
1740
+ border: 1px solid var(--rs-line-strong);
1741
+ box-shadow: 0 6px 20px rgb(0 0 0 / .18);
1742
+ font-size: 12px;
1743
+ font-weight: 500;
1744
+ white-space: nowrap;
1745
+ animation: dshrToastIn .18s ease;
1746
+ }
1747
+
1748
+ @keyframes dshrToastIn {
1749
+ from { opacity: 0; transform: translateX(-50%) translateY(8px); }
1750
+ to { opacity: 1; transform: translateX(-50%) translateY(0); }
1751
+ }
1752
+
1753
+ /* ============ 组合(9router combos 布局交互) ============ */
1754
+ .dshr-comboIntro { overflow: visible; }
1755
+
1756
+ .dshr-comboIntroDesc {
1757
+ font-size: 12px;
1758
+ white-space: nowrap;
1759
+ overflow: hidden;
1760
+ text-overflow: ellipsis;
1761
+ }
1762
+
1763
+ .dshr-comboIntroBody {
1764
+ display: flex;
1765
+ flex-wrap: wrap;
1766
+ align-items: center;
1767
+ gap: 8px 14px;
1768
+ padding: 10px 14px;
1769
+ font-size: 12px;
1770
+ color: var(--rs-muted);
1771
+ border-top: 1px solid var(--rs-line);
1772
+ }
1773
+
1774
+ .dshr-comboHeaderKey {
1775
+ font-weight: 600;
1776
+ color: var(--rs-text);
1777
+ background: color-mix(in srgb, var(--rs-success) 14%, transparent);
1778
+ border-radius: 4px;
1779
+ padding: 1px 6px;
1780
+ font-size: 11px;
1781
+ }
1782
+
1783
+ .dshr-comboHeaderKey-2 {
1784
+ margin-left: 4px;
1785
+ background: color-mix(in srgb, var(--rs-accent) 14%, transparent);
1786
+ }
1787
+
1788
+ .dshr-comboList { display: flex; flex-direction: column; gap: 10px; }
1789
+
1790
+ .dshr-comboCard { display: flex; flex-direction: column; }
1791
+
1792
+ .dshr-comboName {
1793
+ font-family: var(--rs-mono);
1794
+ font-size: 13px;
1795
+ font-weight: 600;
1796
+ color: var(--rs-text);
1797
+ white-space: nowrap;
1798
+ overflow: hidden;
1799
+ text-overflow: ellipsis;
1800
+ max-width: 200px;
1801
+ }
1802
+
1803
+ .dshr-comboOps {
1804
+ display: flex;
1805
+ align-items: center;
1806
+ gap: 2px;
1807
+ flex-shrink: 0;
1808
+ margin-left: 10px;
1809
+ }
1810
+
1811
+ .dshr-comboOpBtn-danger { color: var(--rs-danger); }
1812
+ .dshr-comboOpBtn-danger:hover {
1813
+ color: var(--rs-danger);
1814
+ background: color-mix(in srgb, var(--rs-danger) 12%, transparent);
1815
+ }
1816
+
1817
+ .dshr-comboCardBody {
1818
+ display: flex;
1819
+ flex-direction: column;
1820
+ gap: 8px;
1821
+ padding: 12px 14px;
1822
+ border-top: 1px solid var(--rs-line);
1823
+ }
1824
+
1825
+ .dshr-comboChips {
1826
+ display: flex;
1827
+ flex-wrap: wrap;
1828
+ align-items: center;
1829
+ gap: 6px;
1830
+ min-width: 0;
1831
+ }
1832
+
1833
+ .dshr-comboChip {
1834
+ display: inline-flex;
1835
+ align-items: center;
1836
+ gap: 5px;
1837
+ background: color-mix(in srgb, var(--rs-text) 6%, transparent);
1838
+ border: 1px solid var(--rs-line);
1839
+ border-radius: 6px;
1840
+ padding: 3px 8px 3px 5px;
1841
+ }
1842
+
1843
+ .dshr-comboChipNum {
1844
+ width: 15px;
1845
+ height: 15px;
1846
+ border-radius: 50%;
1847
+ display: inline-flex;
1848
+ align-items: center;
1849
+ justify-content: center;
1850
+ font-size: 10px;
1851
+ font-weight: 600;
1852
+ color: var(--rs-primary);
1853
+ background: color-mix(in srgb, var(--rs-primary) 12%, transparent);
1854
+ }
1855
+
1856
+ .dshr-comboChipSupplier {
1857
+ font-family: var(--rs-mono);
1858
+ font-size: 11px;
1859
+ color: var(--rs-text);
1860
+ }
1861
+
1862
+ .dshr-comboEmptySteps { font-style: italic; font-size: 12px; }
1863
+
1864
+ .dshr-comboMore { font-size: 10px; color: var(--rs-muted); }
1865
+
1866
+ .dshr-comboStrategySelect {
1867
+ width: 84px;
1868
+ height: 26px;
1869
+ padding: 0 6px;
1870
+ font-size: 12px;
1871
+ }
1872
+
1873
+ .dshr-comboStrategySelect:disabled {
1874
+ opacity: .55;
1875
+ cursor: not-allowed;
1876
+ }
1877
+
1878
+ /* 表单 */
1879
+ .dshr-formLabel {
1880
+ font-size: 12px;
1881
+ font-weight: 600;
1882
+ color: var(--rs-text);
1883
+ margin: 0 0 6px;
1884
+ }
1885
+
1886
+ .dshr-formHint {
1887
+ font-size: 10px;
1888
+ color: var(--rs-muted);
1889
+ margin: 4px 0 0;
1890
+ }
1891
+
1892
+ .dshr-comboEmptyStepsBox {
1893
+ display: flex;
1894
+ flex-direction: column;
1895
+ align-items: center;
1896
+ gap: 6px;
1897
+ padding: 20px 0;
1898
+ border: 1px dashed color-mix(in srgb, var(--rs-text) 16%, transparent);
1899
+ border-radius: 9px;
1900
+ color: var(--rs-muted);
1901
+ }
1902
+
1903
+ .dshr-comboStepList {
1904
+ display: flex;
1905
+ flex-direction: column;
1906
+ gap: 6px;
1907
+ max-height: 260px;
1908
+ overflow-y: auto;
1909
+ }
1910
+
1911
+ .dshr-comboStepRow {
1912
+ display: flex;
1913
+ align-items: center;
1914
+ gap: 8px;
1915
+ padding: 3px 4px;
1916
+ border: 1px solid transparent;
1917
+ border-radius: 7px;
1918
+ cursor: grab;
1919
+ }
1920
+
1921
+ .dshr-comboStepRow:active { cursor: grabbing; }
1922
+
1923
+ .dshr-comboStepRow-dragging {
1924
+ opacity: .45;
1925
+ border-color: var(--rs-accent);
1926
+ }
1927
+
1928
+ .dshr-comboStepRow-over {
1929
+ border-color: var(--rs-accent);
1930
+ background: color-mix(in srgb, var(--rs-accent) 8%, transparent);
1931
+ }
1932
+
1933
+ .dshr-comboGrip {
1934
+ flex: none;
1935
+ display: inline-flex;
1936
+ align-items: center;
1937
+ justify-content: center;
1938
+ color: var(--rs-faint);
1939
+ cursor: grab;
1940
+ }
1941
+
1942
+ .dshr-comboStepNum {
1943
+ width: 20px;
1944
+ height: 20px;
1945
+ border-radius: 50%;
1946
+ flex-shrink: 0;
1947
+ display: flex;
1948
+ align-items: center;
1949
+ justify-content: center;
1950
+ font-size: 11px;
1951
+ font-weight: 600;
1952
+ color: var(--rs-primary);
1953
+ background: color-mix(in srgb, var(--rs-primary) 12%, transparent);
1954
+ }
1955
+
1956
+ .dshr-comboStepSelect { flex: 1; min-width: 0; }
1957
+
1958
+ .dshr-comboStepRemove:hover {
1959
+ color: var(--rs-danger);
1960
+ background: color-mix(in srgb, var(--rs-danger) 14%, transparent);
1961
+ }
1962
+
1963
+ .dshr-comboAddStep {
1964
+ width: 100%;
1965
+ margin-top: 8px;
1966
+ padding: 8px 0;
1967
+ border: 1px dashed color-mix(in srgb, var(--rs-text) 20%, transparent);
1968
+ border-radius: 9px;
1969
+ background: transparent;
1970
+ color: var(--rs-primary);
1971
+ font-size: 12px;
1972
+ font-weight: 500;
1973
+ cursor: pointer;
1974
+ display: flex;
1975
+ align-items: center;
1976
+ justify-content: center;
1977
+ gap: 6px;
1978
+ transition: border-color .12s ease, background .12s ease;
1979
+ }
1980
+
1981
+ .dshr-comboAddStep:hover {
1982
+ border-color: color-mix(in srgb, var(--rs-primary) 50%, transparent);
1983
+ background: color-mix(in srgb, var(--rs-primary) 5%, transparent);
1984
+ }
1985
+
1986
+ /* 组合:已选模型行 + 添加模型挑选列表 */
1987
+ .dshr-comboSelectedModel {
1988
+ flex: 1;
1989
+ min-width: 0;
1990
+ font-family: var(--rs-mono);
1991
+ font-size: 12px;
1992
+ color: var(--rs-text);
1993
+ overflow: hidden;
1994
+ text-overflow: ellipsis;
1995
+ white-space: nowrap;
1996
+ }
1997
+
1998
+ .dshr-comboPickLabel { margin-top: 12px; }
1999
+
2000
+ .dshr-comboModelSearch { margin-bottom: 6px; }
2001
+
2002
+ .dshr-comboPickList {
2003
+ display: flex;
2004
+ flex-direction: column;
2005
+ gap: 2px;
2006
+ max-height: 180px;
2007
+ overflow-y: auto;
2008
+ }
2009
+
2010
+ .dshr-comboPickItem {
2011
+ display: flex;
2012
+ align-items: center;
2013
+ gap: 8px;
2014
+ min-width: 0;
2015
+ padding: 6px 8px;
2016
+ border: none;
2017
+ border-radius: 6px;
2018
+ background: transparent;
2019
+ color: var(--rs-text);
2020
+ font-size: 12px;
2021
+ text-align: left;
2022
+ cursor: pointer;
2023
+ transition: background .12s ease;
2024
+ }
2025
+
2026
+ .dshr-comboPickItem:hover { background: var(--rs-hover); }
2027
+
2028
+ .dshr-comboPickName {
2029
+ flex: 1;
2030
+ min-width: 0;
2031
+ font-family: var(--rs-mono);
2032
+ overflow: hidden;
2033
+ text-overflow: ellipsis;
2034
+ white-space: nowrap;
2035
+ }
2036
+
2037
+ .dshr-comboPickEmpty {
2038
+ font-size: 12px;
2039
+ font-style: italic;
2040
+ padding: 8px 2px;
2041
+ }
2042
+
2043
+ /* 组合:添加模型按供应商分组 */
2044
+ .dshr-comboPickGroup { margin-bottom: 6px; }
2045
+
2046
+ .dshr-comboPickGroupTitle {
2047
+ margin: 0 0 2px;
2048
+ padding: 0 8px;
2049
+ font-size: 11px;
2050
+ font-weight: 600;
2051
+ color: var(--rs-faint);
2052
+ }
2053
+
2054
+ /* 组合表单:左右分栏(左=组合名/策略/已选,右=添加模型) */
2055
+ .dshr-comboFormSplit {
2056
+ position: relative;
2057
+ display: grid;
2058
+ grid-template-columns: 1fr 1fr;
2059
+ gap: 24px;
2060
+ align-items: start;
2061
+ }
2062
+
2063
+ .dshr-comboFormLeft {
2064
+ display: flex;
2065
+ flex-direction: column;
2066
+ gap: 12px;
2067
+ min-width: 0;
2068
+ }
2069
+
2070
+ .dshr-comboFormRight {
2071
+ display: flex;
2072
+ flex-direction: column;
2073
+ gap: 6px;
2074
+ min-width: 0;
2075
+ max-height: 340px;
2076
+ }
2077
+
2078
+ .dshr-comboFormRight .dshr-comboPickList {
2079
+ flex: 1;
2080
+ min-height: 0;
2081
+ }
2082
+
2083
+ /* 组合编辑弹窗:加宽 */
2084
+ .dshr-modal-wide {
2085
+ width: min(760px, 100%);
2086
+ }
2087
+
2088
+ /* 左右分栏之间的分割线 */
2089
+ .dshr-comboFormDivider {
2090
+ position: absolute;
2091
+ top: 12px;
2092
+ bottom: 12px;
2093
+ left: 50%;
2094
+ width: 1px;
2095
+ background: var(--rs-line);
2096
+ }