dsh-router-core 0.3.0 → 0.3.2

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.
@@ -64,17 +64,176 @@
64
64
  }
65
65
 
66
66
  /* ============================================================
67
- dsh-router — center-column panel
67
+ dsh-router — 主题变量(设置页 .dshr-settings / 老中心栏 .dshr-shell 共用)
68
68
  ============================================================ */
69
69
 
70
- [data-pane='conversation'] {
71
- position: relative;
70
+ .dshr-settings,
71
+ .dshr-shell {
72
+ --rs-bg: var(--dsw-alias-bg-overlay, var(--dsw-alias-bg-base));
73
+ --rs-layer-1: var(--dsw-alias-bg-layer-1);
74
+ --rs-layer-2: var(--dsw-alias-bg-layer-2);
75
+ --rs-input: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-2));
76
+ --rs-text: var(--dsw-alias-label-primary);
77
+ --rs-muted: var(--dsw-alias-label-secondary);
78
+ --rs-faint: var(--dsw-alias-label-tertiary);
79
+ --rs-line: var(--dsw-alias-border-l1);
80
+ --rs-line-strong: var(--dsw-alias-border-l2);
81
+ --rs-accent: var(--dsw-alias-state-business-primary);
82
+ --rs-hover: var(--dsw-alias-interactive-bg-hover);
83
+ --rs-danger: var(--dsw-alias-state-error-primary);
84
+ --rs-success: var(--dsw-alias-state-success-primary);
85
+ --rs-ok: var(--rs-success);
86
+ --rs-warn: #9a6a18;
87
+ --rs-mono: var(--ds-font-family-code, 'SFMono-Regular', Consolas, monospace);
88
+ --rs-sans: var(--dsw-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
89
+ box-sizing: border-box;
90
+ min-width: 0;
91
+ color: var(--rs-text);
92
+ font: 13px/1.55 var(--rs-sans);
93
+ -webkit-tap-highlight-color: transparent;
94
+ }
95
+
96
+ /* ============================================================
97
+ dsh-router — 设置页容器(settings.section 座位)
98
+ ============================================================ */
99
+
100
+ /* 容器几何(浏览器实测,改之前先量别凭感觉):
101
+ - 内容宽 564px(面板 612 − 左右 padding 24)
102
+ - 外壳 .VOzbGW_options 是 `overflow-y:auto` + `flex:1 1 0%`,高度确定
103
+ (视口 813 时可视高 703~746)
104
+ - 根节点外面套了一层 display:contents(宿主 slot renderer 给的),**没有
105
+ 盒子** —— 但实测 `height:100%` 仍能穿过它解析到外壳高度
106
+ (验过:root 从 1332px 溢出态变成 722px,正好填满容器)
107
+
108
+ 布局规则:**tab 条固定,只有下面的内容区滚**。
109
+ 所以根节点必须限高(不限高内容会把外壳撑开,变成整页滚),内容区再
110
+ min-height:0 + overflow-y:auto 自己滚。
111
+ 关键字是 min-height:0 —— flex 子项默认 min-height:auto,不给 0 的话
112
+ 子项缩不小,overflow 永远不触发。 */
113
+ .dshr-settings {
114
+ display: flex;
115
+ height: 100%;
116
+ min-height: 0;
117
+ flex-direction: column;
118
+ gap: 14px;
72
119
  }
73
120
 
74
- .dshDesktopConversationSurface {
121
+ .dshr-settings *,
122
+ .dshr-settings *::before,
123
+ .dshr-settings *::after { box-sizing: border-box; }
124
+
125
+ /* 主导航 = tab 条。照 DSH 原生的内容区 tab(插件页那套 pbvGtq_tab):
126
+ 容器一条 1px 底部分隔线,激活项用 ::after 画 2px 下划线压在线上。
127
+ 刻意**不用**填充色块——那会跟下面的周期按钮组撞成同一种控件,
128
+ 层级也反了(导航是页面级,周期是筛选级)。 */
129
+ .dshr-tabs {
130
+ display: flex;
131
+ flex: none; /* 固定:内容区滚,tab 条不动 */
132
+ align-items: center;
133
+ gap: 4px;
134
+ border-bottom: 1px solid var(--rs-line);
135
+ /* 右端跟卡片对齐(实测数字,别凭感觉调):
136
+ root = 564;内容区因 padding-right 8px → 内容可视宽 556;
137
+ 卡片再贴着可视区排 → 548。
138
+ tab 条是 .dshr-settings 的直接子项,起点是 root(564),
139
+ 要落到 548 就得让掉 8(padding) + 8(滚动条) = 16px。
140
+ 写成 margin 而不用 padding,是为了不把分隔线画到 padding 区域外。 */
141
+ margin-right: 16px;
142
+ }
143
+
144
+ .dshr-tab {
75
145
  position: relative;
146
+ min-width: 0;
147
+ height: 36px;
148
+ padding: 0 12px;
149
+ border: none;
150
+ border-radius: 0;
151
+ color: var(--rs-muted);
152
+ background: transparent;
153
+ font-size: 13px;
154
+ font-weight: 400;
155
+ white-space: nowrap;
156
+ cursor: pointer;
157
+ transition: color .12s ease;
158
+ }
159
+
160
+ .dshr-tab:hover { color: var(--rs-text); }
161
+
162
+ /* 激活:主文字色 + 2px 下划线(bottom:-1px 压在容器的分隔线上)。
163
+ 双类是因为上面有 `.dshr-settings button { color: inherit; font: inherit }` (1,1),
164
+ 单类的 .dshr-tab-on 只有 (1,0),颜色和字号会被吃掉。 */
165
+ .dshr-settings .dshr-tab-on {
166
+ color: var(--rs-text);
167
+ font-size: 13px;
168
+ font-weight: 500;
169
+ }
170
+
171
+ .dshr-settings .dshr-tab-on::after {
172
+ position: absolute;
173
+ right: 0;
174
+ bottom: -1px;
175
+ left: 0;
176
+ height: 2px;
177
+ border-radius: 2px 2px 0 0;
178
+ content: '';
179
+ background: var(--rs-text);
180
+ }
181
+
182
+ .dshr-tabBack {
183
+ height: 26px;
184
+ padding: 0 10px;
185
+ border: 1px solid var(--rs-line);
186
+ border-radius: 6px;
187
+ color: var(--rs-muted);
188
+ background: transparent;
189
+ font-size: 12px;
190
+ white-space: nowrap;
191
+ cursor: pointer;
192
+ }
193
+
194
+ .dshr-tabBack:hover { color: var(--rs-text); background: var(--rs-hover); }
195
+
196
+ /* 内容区:吃掉剩余高度并**自己滚**,这样 tab 条永远留在顶部。
197
+ flex:1 + min-height:0 是必须的(见 .dshr-settings 的注释)。 */
198
+ .dshr-settingsBody {
199
+ display: flex;
200
+ min-width: 0;
201
+ min-height: 0;
202
+ flex: 1 1 auto;
203
+ flex-direction: column;
204
+ overflow-y: auto;
205
+ /* 恒定预留滚动条槽位。
206
+ 实测不设它时 clientWidth 在 564(内容短、无滚动条)和 556(内容长、有滚动条)
207
+ 之间跳,卡片宽也跟着 556/548 跳 —— 切 tab 时内容会横向抖一下。
208
+ 不支持 scrollbar-gutter 的浏览器只是退化成「照样占宽」,不会坏。 */
209
+ scrollbar-gutter: stable;
210
+ /* 右内边距:内容别贴到滚动条上。滚动条宽 8px 且占布局宽度
211
+ (offsetWidth 564 vs clientWidth 556),不撑开的话内容右边缘正好压在它上面。 */
212
+ padding-right: 8px;
213
+ }
214
+
215
+ /* 细滚动条:跟 .dshr-content / .dshr-modalBody 一个套路 */
216
+ .dshr-settingsBody::-webkit-scrollbar {
217
+ width: 8px;
218
+ height: 8px;
219
+ }
220
+
221
+ .dshr-settingsBody::-webkit-scrollbar-thumb {
222
+ border-radius: 4px;
223
+ background: var(--rs-line-strong);
224
+ }
225
+
226
+ .dshr-settingsBody::-webkit-scrollbar-track {
227
+ background: transparent;
76
228
  }
77
229
 
230
+ /* ============================================================
231
+ dsh-router — 老的中心栏劫持骨架(settings 不可用时的回退,非主路径)
232
+ ============================================================ */
233
+
234
+ [data-pane='conversation'] { position: relative; }
235
+ .dshDesktopConversationSurface { position: relative; }
236
+
78
237
  [data-dsh-router-view] {
79
238
  position: absolute;
80
239
  z-index: 60;
@@ -86,9 +245,7 @@
86
245
  background: var(--dsw-alias-bg-overlay, var(--dsw-alias-bg-base));
87
246
  }
88
247
 
89
- .dshr-panelView {
90
- isolation: isolate;
91
- }
248
+ .dshr-panelView { isolation: isolate; }
92
249
 
93
250
  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
251
  display: block;
@@ -100,50 +257,37 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-
100
257
  display: none !important;
101
258
  }
102
259
 
103
- /* ============================================================
104
- dsh-router — panel shell
105
- ============================================================ */
106
-
107
260
  .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
261
  display: flex;
127
262
  height: 100%;
128
- min-width: 0;
129
263
  min-height: 0;
130
264
  flex-direction: column;
131
265
  overflow: hidden;
132
- color: var(--rs-text);
133
266
  background: var(--rs-bg);
134
- font: 13px/1.55 var(--rs-sans);
135
- -webkit-tap-highlight-color: transparent;
136
267
  }
137
268
 
269
+ .dshr-settings *,
270
+ .dshr-settings *::before,
271
+ .dshr-settings *::after,
138
272
  .dshr-shell *,
139
273
  .dshr-shell *::before,
140
274
  .dshr-shell *::after { box-sizing: border-box; }
141
275
 
276
+ /* 按钮/表单重置 + 聚焦环:两个挂载容器都要覆盖。
277
+ 只写 .dshr-shell 的话设置页整套控件样式全掉(踩过:周期按钮变裸文字)。 */
278
+ .dshr-settings button,
279
+ .dshr-settings input,
280
+ .dshr-settings select,
281
+ .dshr-settings textarea,
142
282
  .dshr-shell button,
143
283
  .dshr-shell input,
144
284
  .dshr-shell select,
145
285
  .dshr-shell textarea { color: inherit; font: inherit; }
146
286
 
287
+ .dshr-settings button:focus-visible,
288
+ .dshr-settings input:focus-visible,
289
+ .dshr-settings select:focus-visible,
290
+ .dshr-settings textarea:focus-visible,
147
291
  .dshr-shell button:focus-visible,
148
292
  .dshr-shell input:focus-visible,
149
293
  .dshr-shell select:focus-visible,
@@ -396,6 +540,14 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-
396
540
  padding: 14px 18px;
397
541
  }
398
542
 
543
+ /* 设置页里横向 padding 归零:内容区已经贴着容器边,再加 18px 会把卡片挤成
544
+ 1 列、图表也跟着变窄。上下 padding 保留。 */
545
+ .dshr-settings .dshr-modelGrid,
546
+ .dshr-settings .dshr-tableWrap {
547
+ padding-right: 0;
548
+ padding-left: 0;
549
+ }
550
+
399
551
  .dshr-modelCard {
400
552
  display: flex;
401
553
  min-width: 0;
@@ -428,13 +580,20 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-
428
580
  dsh-router — 9router 风格 tab 内容(供应商 / 组合 / 端点与密钥)
429
581
  ============================================================ */
430
582
 
583
+ /* tab 内容:设置页里**不自己滚** —— 滚动交给 .dshr-settingsBody(tab 条下方
584
+ 那一块)。这里再给 overflow 就会出现嵌套滚动条(内容区一个、tabBody 一个)。
585
+ 老的 .dshr-shell 中心栏挂载方式没有 settingsBody 那层,滚动由它自己承担。 */
431
586
  .dshr-tabBody {
432
587
  display: flex;
433
- flex: 1 1 auto; /* 填满 content;自身滚动 */
434
588
  min-width: 0;
435
- min-height: 0;
436
589
  flex-direction: column;
437
590
  gap: 14px;
591
+ padding: 0;
592
+ }
593
+
594
+ .dshr-shell .dshr-tabBody {
595
+ flex: 1 1 auto;
596
+ min-height: 0;
438
597
  padding: 14px 18px;
439
598
  overflow-y: auto;
440
599
  }
@@ -845,6 +1004,7 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-
845
1004
  @keyframes dshrModalIn { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: none; } }
846
1005
 
847
1006
  /* 按钮 */
1007
+ .dshr-settings .dshr-primaryButton,
848
1008
  .dshr-shell .dshr-primaryButton {
849
1009
  display: inline-flex;
850
1010
  height: 30px;
@@ -862,9 +1022,12 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-
862
1022
  transition: filter .12s ease;
863
1023
  }
864
1024
 
1025
+ .dshr-settings .dshr-primaryButton:hover,
865
1026
  .dshr-shell .dshr-primaryButton:hover { filter: brightness(1.08); }
1027
+ .dshr-settings .dshr-primaryButton:disabled,
866
1028
  .dshr-shell .dshr-primaryButton:disabled { opacity: .55; cursor: default; }
867
1029
 
1030
+ .dshr-settings .dshr-dangerButton,
868
1031
  .dshr-shell .dshr-dangerButton {
869
1032
  display: inline-flex;
870
1033
  height: 30px;
@@ -882,6 +1045,7 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-
882
1045
  transition: filter .12s ease;
883
1046
  }
884
1047
 
1048
+ .dshr-settings .dshr-dangerButton:hover,
885
1049
  .dshr-shell .dshr-dangerButton:hover { filter: brightness(1.1); }
886
1050
 
887
1051
  /* 登录步骤 */
@@ -2126,11 +2290,15 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-
2126
2290
  background: var(--rs-layer-2);
2127
2291
  }
2128
2292
 
2129
- /* 周期按钮三态。特异性阶梯(类/伪类数 , 元素数):
2130
- 文件顶部有 `.dshr-shell button { color: inherit; font: inherit }` = (1,1),
2293
+ /* 周期按钮三态(按钮组:一组互斥的筛选按钮,不是导航 tab)。
2294
+ 特异性阶梯(类/伪类数 , 元素数):
2295
+ 文件顶部有 `.dshr-settings button { color: inherit; font: inherit }` = (1,1),
2131
2296
  单类的 .dshr-period (1,0) 会被它打死(白字、600 字重全丢),所以下面每条
2132
- 都必须带 .dshr-shell 抬到 (2,0) 以上。
2297
+ 都必须带挂载容器(.dshr-settings / .dshr-shell)抬到 (2,0) 以上。
2298
+ **两个容器都要写**:只写 .dshr-shell 的话设置页里这套样式全掉
2299
+ (踩过:周期按钮退化成浏览器默认按钮)。
2133
2300
  两种 hover 用 :not() 做成互斥,谁赢跟源码顺序无关(免得以后有人挪块就翻车)。 */
2301
+ .dshr-settings .dshr-period,
2134
2302
  .dshr-shell .dshr-period {
2135
2303
  min-width: 46px;
2136
2304
  height: 26px;
@@ -2146,12 +2314,14 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-
2146
2314
  }
2147
2315
 
2148
2316
  /* 未激活悬停:跟 .dshr-miniButton / .dshr-iconButton 一个套路(主文字色 + --rs-hover 底) */
2317
+ .dshr-settings .dshr-period:not(.dshr-period-on):hover,
2149
2318
  .dshr-shell .dshr-period:not(.dshr-period-on):hover {
2150
2319
  color: var(--rs-text);
2151
2320
  background: var(--rs-hover);
2152
2321
  }
2153
2322
 
2154
2323
  /* 激活:强调色块 + 白字。底色用 layer-1 时跟卡片同色,根本看不出选中了哪个 */
2324
+ .dshr-settings .dshr-period-on,
2155
2325
  .dshr-shell .dshr-period-on {
2156
2326
  color: #fff;
2157
2327
  background: var(--rs-accent);
@@ -2160,6 +2330,7 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-
2160
2330
  }
2161
2331
 
2162
2332
  /* 激活态被悬停:跟 .dshr-primaryButton 一样提亮,表示「它还是能点的」 */
2333
+ .dshr-settings .dshr-period-on:hover,
2163
2334
  .dshr-shell .dshr-period-on:hover {
2164
2335
  color: #fff;
2165
2336
  background: var(--rs-accent);
@@ -2228,6 +2399,68 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-
2228
2399
  .dshr-statCard-warn .dshr-statValue { color: var(--rs-warn); }
2229
2400
  .dshr-statCard-danger .dshr-statValue { color: var(--rs-danger); }
2230
2401
 
2402
+ /* 签到卡:跟统计卡同宽同高,但里面是按钮不是数字。
2403
+ gap 沿用卡片的 3px(不覆写)—— 之前改成 6px 让这一行比别的高 6px,
2404
+ 两行卡片因此不齐(92 vs 98px)。
2405
+ 今日已点过 → 边框转成功色,一眼看出「今天点过了」 */
2406
+ .dshr-statCard-checkin { gap: 3px; }
2407
+
2408
+ .dshr-statCard-done {
2409
+ border-color: color-mix(in srgb, var(--rs-success) 45%, transparent);
2410
+ }
2411
+
2412
+ /* 按钮撑满卡片宽度:卡片本来就窄,居左的小按钮看着像没对齐 */
2413
+ .dshr-checkinButton {
2414
+ display: inline-flex;
2415
+ width: 100%;
2416
+ /* 26px ≈ .dshr-statValue 的行高(22px × 1.2),让按钮那一行跟数字的
2417
+ 那一行一样高,卡片整体高度才跟邻卡一致 */
2418
+ height: 26px;
2419
+ align-items: center;
2420
+ justify-content: center;
2421
+ gap: 5px;
2422
+ border: 1px solid var(--rs-line);
2423
+ border-radius: 6px;
2424
+ color: var(--rs-text);
2425
+ background: var(--rs-layer-2);
2426
+ font-size: 12px;
2427
+ font-weight: 500;
2428
+ white-space: nowrap;
2429
+ cursor: pointer;
2430
+ transition: color .12s ease, background .12s ease, border-color .12s ease;
2431
+ }
2432
+
2433
+ .dshr-checkinButton:hover:not(:disabled) {
2434
+ border-color: var(--rs-accent);
2435
+ color: var(--rs-accent);
2436
+ background: color-mix(in srgb, var(--rs-accent) 8%, transparent);
2437
+ }
2438
+
2439
+ .dshr-checkinButton:disabled { opacity: .6; cursor: default; }
2440
+
2441
+ /* 已点过:按钮转成成功色柔和态(仍可再点) */
2442
+ .dshr-statCard-done .dshr-checkinButton {
2443
+ border-color: color-mix(in srgb, var(--rs-success) 40%, transparent);
2444
+ color: var(--rs-success);
2445
+ background: color-mix(in srgb, var(--rs-success) 8%, transparent);
2446
+ }
2447
+
2448
+ /* 结果/失败都压在 hint 那一行里(卡片行数必须跟邻卡一致):
2449
+ 失败时才转红,其余沿用 hint 的弱色,不成功后多出一行 */
2450
+ .dshr-statHint-err {
2451
+ overflow: hidden;
2452
+ color: var(--rs-danger);
2453
+ text-overflow: ellipsis;
2454
+ white-space: nowrap;
2455
+ }
2456
+
2457
+ /* 忙碌态:按钮转成弱色,跟「已点过」的成功色区分开 */
2458
+ .dshr-checkinButton-busy {
2459
+ color: var(--rs-faint);
2460
+ background: var(--rs-layer-2);
2461
+ border-color: var(--rs-line);
2462
+ }
2463
+
2231
2464
  .dshr-estimateNote {
2232
2465
  margin: 0;
2233
2466
  color: var(--rs-faint);
@@ -2500,3 +2733,26 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-
2500
2733
 
2501
2734
  .dshr-dot-ok { background: var(--rs-success); }
2502
2735
  .dshr-dot-fail { background: var(--rs-danger); }
2736
+
2737
+ /* ============================================================
2738
+ dsh-router — 设置导航图标(换掉宿主的默认齿轮)
2739
+ ============================================================ */
2740
+
2741
+ /* DSH 0.1.x 的 settings.section 注册只认 id / order / label,图标是宿主按
2742
+ 「内置 id 白名单」选的,插件传不了 —— 外部 section 一律拿到通用齿轮。
2743
+ settings-nav-icon.ts 给本插件那一行打上标记,这里用 currentColor mask
2744
+ 渲染路由字形:跟随宿主导航的 hover/active 配色,也保持 16px 图标节奏。
2745
+ 升级路径:等契约长出 icon 字段,删掉 settings-nav-icon.ts 和这一整块。 */
2746
+ [data-dsh-router-settings-nav] > svg:first-child {
2747
+ display: none;
2748
+ }
2749
+
2750
+ [data-dsh-router-settings-nav]::before {
2751
+ content: '';
2752
+ flex: none;
2753
+ width: 16px;
2754
+ height: 16px;
2755
+ background: currentColor;
2756
+ -webkit-mask: url("data:image/svg+xml,<svg%20xmlns='http://www.w3.org/2000/svg'%20width='24'%20height='24'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='black'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'><circle%20cx='5'%20cy='5'%20r='2.4'/><circle%20cx='19'%20cy='5'%20r='2.4'/><circle%20cx='12'%20cy='19'%20r='2.4'/><path%20d='M7.4%205h9.2'/><path%20d='M5%207.4v6.2c0%202.6%203%203.4%204.6%203.8'/><path%20d='M19%207.4v6.2c0%202.6-3%203.4-4.6%203.8'/></svg>") center / contain no-repeat;
2757
+ mask: url("data:image/svg+xml,<svg%20xmlns='http://www.w3.org/2000/svg'%20width='24'%20height='24'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='black'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'><circle%20cx='5'%20cy='5'%20r='2.4'/><circle%20cx='19'%20cy='5'%20r='2.4'/><circle%20cx='12'%20cy='19'%20r='2.4'/><path%20d='M7.4%205h9.2'/><path%20d='M5%207.4v6.2c0%202.6%203%203.4%204.6%203.8'/><path%20d='M19%207.4v6.2c0%202.6-3%203.4-4.6%203.8'/></svg>") center / contain no-repeat;
2758
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * 给设置导航里「路由」这一行换掉宿主的默认齿轮。
3
+ *
4
+ * 为什么需要这个:DSH 0.1.x 的 `settings.section` 注册**只认 id / order / label
5
+ * 三个字段**,图标是宿主内部按「内置 id 白名单」选的,插件传不了。所以外部
6
+ * section 一律拿到通用齿轮(跟「通用设置」「远程访问」同一个)。
7
+ * 在契约长出 icon 字段之前,只能注册后认领自己的那一行。
8
+ *
9
+ * 做法跟 dsh-better-sidebar 的 settings-nav-icon.ts 一致:给自己的行打一个
10
+ * 属性标记,再由 CSS 用 mask 渲染图标(见 router.css 的
11
+ * `[data-dsh-router-settings-nav]`)。用 mask + currentColor 是为了跟随
12
+ * 宿主导航的 hover/active 配色,也保持 16px 的图标节奏。
13
+ *
14
+ * 匹配方式:按行的**可见文案**认领(label 可能本地化),且只认 `nav button`,
15
+ * 碰到同名的别人家 section 不会误伤(我们同时校验它确实在设置面板里)。
16
+ * 卸载时断开观察并摘掉标记,HMR / 插件热卸载安全。
17
+ */
18
+
19
+ /** 打在设置导航按钮上的标记属性。 */
20
+ export const SETTINGS_NAV_MARKER = 'data-dsh-router-settings-nav'
21
+
22
+ /** 设置面板的导航按钮选择器(宿主结构:dialog 里的 nav)。 */
23
+ const NAV_BUTTON_SELECTOR = '[role="dialog"] nav button'
24
+
25
+ /**
26
+ * 持续把标记打在文案等于 `label` 的那一行的身上。
27
+ * @param label 当前本地化的 section 标题(跟注册时的 label 一致)
28
+ * @returns 清理函数:断开观察 + 摘掉所有本插件打的标记
29
+ */
30
+ export function registerSettingsNavIcon(label: string): () => void {
31
+ let disposed = false
32
+ const wanted = label.trim()
33
+
34
+ const sync = (): void => {
35
+ if (disposed || wanted === '') return
36
+ for (const button of document.querySelectorAll<HTMLElement>(NAV_BUTTON_SELECTOR)) {
37
+ const matches = button.textContent?.trim() === wanted
38
+ if (matches) button.setAttribute(SETTINGS_NAV_MARKER, '')
39
+ else button.removeAttribute(SETTINGS_NAV_MARKER)
40
+ }
41
+ }
42
+
43
+ sync()
44
+ // 设置面板是按需挂载的:body 子树变动时重新认领
45
+ const observer = new MutationObserver(sync)
46
+ observer.observe(document.body, { childList: true, subtree: true, characterData: true })
47
+
48
+ return () => {
49
+ disposed = true
50
+ observer.disconnect()
51
+ for (const el of document.querySelectorAll(`[${SETTINGS_NAV_MARKER}]`)) {
52
+ el.removeAttribute(SETTINGS_NAV_MARKER)
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * 「设置 → 路由」页 —— 走 DSH 官方 `settings.section` 座位。
3
+ *
4
+ * 容器几何(浏览器实测,不是估的)决定了这里的写法:
5
+ * - 内容宽 564px(面板 612 − 左右 padding 24)
6
+ * - 外壳 `.VOzbGW_options` 是 `overflow-y:auto` 且高度确定,所以**根节点
7
+ * 限高**(`height:100%`)+ 内容区自己滚,才能做到「tab 条固定、只有
8
+ * 内容区滚」。不限高就会退化成整页滚,tab 条跟着滚上去
9
+ * - 根节点外面套了一层 `display: contents`(宿主 slot renderer 给的),
10
+ * 但实测 `height:100%` 能穿过它解析到外壳高度
11
+ * - 横向会溢出:老布局是按整个中心栏宽做的,卡片 grid 必须按 564px 排
12
+ *
13
+ * 组件本身无状态、无 props:数据全在 RouterView 里自己 fetch。
14
+ */
15
+ import { RouterView } from './RouterView.tsx'
16
+
17
+ /** 设置页入口组件。 */
18
+ export function RouterSettingsSection(): JSX.Element {
19
+ // 不传 onBack:设置面板自带关闭,不需要「返回会话」
20
+ return <RouterView />
21
+ }
package/src/shared.ts CHANGED
@@ -15,7 +15,7 @@ export const ROUTER_VIEW_SELECTOR = '[data-dsh-router-view]'
15
15
  export const ROUTER_ACTIVE_ATTR = 'data-dsh-router-active'
16
16
 
17
17
  /** The panel's visible title (also the sidebar entry label). */
18
- export const ROUTER_TITLE = '路由系统'
18
+ export const ROUTER_TITLE = '路由'
19
19
 
20
20
  /** Same-origin API base the client panel fetches (host half serves it). */
21
21
  export const ROUTER_API_BASE = '/router/api'
@@ -51,6 +51,18 @@ export interface RouterModelsResponse {
51
51
  models?: Array<{ id: string; context_length?: number }>
52
52
  }
53
53
 
54
+ /** 一键签到:单个供应商的汇总结果(`/suppliers/:id/checkin` 的响应体)。 */
55
+ export interface RouterCheckinResponse {
56
+ ok: boolean
57
+ error?: string
58
+ total: number
59
+ /** 真正 claim 成功的链接数。 */
60
+ succeeded: number
61
+ /** 今日已签到的链接数(幂等成功)。 */
62
+ already: number
63
+ results?: Array<{ uid: string; ok: boolean; status?: string; message?: string }>
64
+ }
65
+
54
66
  /** `/router/api/health` response. */
55
67
  export interface RouterHealthResponse {
56
68
  ok: boolean
@@ -22,8 +22,14 @@ import type { SupplierEnv, SupplierModule } from '../contract.ts'
22
22
  export const id = 'nvidia'
23
23
  export const name = 'NVIDIA NIM'
24
24
  export const priority = 20 // 同 9Router:免费直连(opencode=0)之后
25
- /** 面板图标(9router 提供的 logo)。 */
26
- export const icon = 'http://localhost:20128/providers/nvidia.png'
25
+ /**
26
+ * 面板图标(NVIDIA 官方 logo,128×128 PNG,base64 内联)。
27
+ *
28
+ * **必须内联,不能放网络 URL**:之前是 `http://localhost:20128/...`(9router
29
+ * 的端口),9router 没跑时就是一张坏图 —— 面板图标不该依赖另一个服务活着,
30
+ * 更不该依赖特定端口。跟 opencode / openrouter / traework 的做法保持一致。
31
+ */
32
+ export const icon = 'data:image/png;base64,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'
27
33
 
28
34
  const BASE = 'https://integrate.api.nvidia.com/v1'
29
35
  const MODELS_URL = `${BASE}/models`