dsh-router-core 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -11
- package/docs/screenshot.png +0 -0
- package/docs/suppliers.md +302 -0
- package/lib/client-registry.js +1119 -213
- package/lib/client.js +1119 -213
- package/lib/index.js +1066 -50
- package/lib/types/llm/adapter.d.ts +2 -0
- package/lib/types/router/index.d.ts +34 -5
- package/lib/types/router/usage-store.d.ts +93 -0
- package/lib/types/router/usage-tokens.d.ts +118 -0
- package/lib/types/shared.d.ts +86 -1
- package/lib/types/supplier-config.d.ts +15 -1
- package/package.json +21 -4
- package/src/client/CheckinCard.tsx +180 -0
- package/src/client/CombosTab.tsx +28 -18
- package/src/client/RouterView.tsx +44 -27
- package/src/client/StatsTab.tsx +531 -0
- package/src/client/index.tsx +61 -11
- package/src/client/router.css +688 -35
- package/src/client/settings-nav-icon.ts +55 -0
- package/src/client/settings-section.tsx +21 -0
- package/src/client/workspace-mount.tsx +2 -9
- package/src/index.ts +51 -3
- package/src/llm/adapter.test.ts +108 -0
- package/src/llm/adapter.ts +18 -6
- package/src/router/index.ts +461 -49
- package/src/router/models-cache.test.ts +269 -0
- package/src/router/precise-dispatch.test.ts +198 -0
- package/src/router/usage-store.ts +499 -0
- package/src/router/usage-tokens.ts +327 -0
- package/src/router/usage.test.ts +558 -0
- package/src/shared.ts +90 -1
- package/src/supplier-config.ts +30 -2
- package/src/suppliers/loader.ts +2 -1
- package/src/suppliers/nvidia/plugin.ts +2 -3
- package/src/suppliers/opencode/plugin.ts +2 -3
- package/src/suppliers/openrouter/plugin.ts +2 -3
- package/src/suppliers/registry.ts +7 -2
package/src/client/router.css
CHANGED
|
@@ -64,17 +64,176 @@
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
/* ============================================================
|
|
67
|
-
dsh-router —
|
|
67
|
+
dsh-router — 主题变量(设置页 .dshr-settings / 老中心栏 .dshr-shell 共用)
|
|
68
68
|
============================================================ */
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
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;
|
|
119
|
+
}
|
|
120
|
+
|
|
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;
|
|
72
142
|
}
|
|
73
143
|
|
|
74
|
-
.
|
|
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);
|
|
76
224
|
}
|
|
77
225
|
|
|
226
|
+
.dshr-settingsBody::-webkit-scrollbar-track {
|
|
227
|
+
background: transparent;
|
|
228
|
+
}
|
|
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
|
/* 登录步骤 */
|
|
@@ -2103,3 +2267,492 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-
|
|
|
2103
2267
|
width: 1px;
|
|
2104
2268
|
background: var(--rs-line);
|
|
2105
2269
|
}
|
|
2270
|
+
|
|
2271
|
+
/* ============================================================
|
|
2272
|
+
dsh-router — 概览(用量看板,9router Overview 风格)
|
|
2273
|
+
============================================================ */
|
|
2274
|
+
|
|
2275
|
+
/* 周期切换 + 操作条 */
|
|
2276
|
+
.dshr-statsBar {
|
|
2277
|
+
display: flex;
|
|
2278
|
+
flex: none;
|
|
2279
|
+
flex-wrap: wrap;
|
|
2280
|
+
align-items: center;
|
|
2281
|
+
gap: 10px;
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
.dshr-periods {
|
|
2285
|
+
display: inline-flex;
|
|
2286
|
+
gap: 2px;
|
|
2287
|
+
padding: 2px;
|
|
2288
|
+
border: 1px solid var(--rs-line);
|
|
2289
|
+
border-radius: 8px;
|
|
2290
|
+
background: var(--rs-layer-2);
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
/* 周期按钮三态(按钮组:一组互斥的筛选按钮,不是导航 tab)。
|
|
2294
|
+
特异性阶梯(类/伪类数 , 元素数):
|
|
2295
|
+
文件顶部有 `.dshr-settings button { color: inherit; font: inherit }` = (1,1),
|
|
2296
|
+
单类的 .dshr-period (1,0) 会被它打死(白字、600 字重全丢),所以下面每条
|
|
2297
|
+
都必须带挂载容器(.dshr-settings / .dshr-shell)抬到 (2,0) 以上。
|
|
2298
|
+
**两个容器都要写**:只写 .dshr-shell 的话设置页里这套样式全掉
|
|
2299
|
+
(踩过:周期按钮退化成浏览器默认按钮)。
|
|
2300
|
+
两种 hover 用 :not() 做成互斥,谁赢跟源码顺序无关(免得以后有人挪块就翻车)。 */
|
|
2301
|
+
.dshr-settings .dshr-period,
|
|
2302
|
+
.dshr-shell .dshr-period {
|
|
2303
|
+
min-width: 46px;
|
|
2304
|
+
height: 26px;
|
|
2305
|
+
padding: 0 10px;
|
|
2306
|
+
border: none;
|
|
2307
|
+
border-radius: 6px;
|
|
2308
|
+
color: var(--rs-muted);
|
|
2309
|
+
background: transparent;
|
|
2310
|
+
font-size: 12px;
|
|
2311
|
+
font-weight: 500;
|
|
2312
|
+
cursor: pointer;
|
|
2313
|
+
transition: color .12s ease, background .12s ease, filter .12s ease;
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
/* 未激活悬停:跟 .dshr-miniButton / .dshr-iconButton 一个套路(主文字色 + --rs-hover 底) */
|
|
2317
|
+
.dshr-settings .dshr-period:not(.dshr-period-on):hover,
|
|
2318
|
+
.dshr-shell .dshr-period:not(.dshr-period-on):hover {
|
|
2319
|
+
color: var(--rs-text);
|
|
2320
|
+
background: var(--rs-hover);
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
/* 激活:强调色块 + 白字。底色用 layer-1 时跟卡片同色,根本看不出选中了哪个 */
|
|
2324
|
+
.dshr-settings .dshr-period-on,
|
|
2325
|
+
.dshr-shell .dshr-period-on {
|
|
2326
|
+
color: #fff;
|
|
2327
|
+
background: var(--rs-accent);
|
|
2328
|
+
font-weight: 600;
|
|
2329
|
+
box-shadow: 0 1px 2px rgb(0 0 0 / .12);
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
/* 激活态被悬停:跟 .dshr-primaryButton 一样提亮,表示「它还是能点的」 */
|
|
2333
|
+
.dshr-settings .dshr-period-on:hover,
|
|
2334
|
+
.dshr-shell .dshr-period-on:hover {
|
|
2335
|
+
color: #fff;
|
|
2336
|
+
background: var(--rs-accent);
|
|
2337
|
+
filter: brightness(1.08);
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
.dshr-statsBarRight {
|
|
2341
|
+
display: flex;
|
|
2342
|
+
align-items: center;
|
|
2343
|
+
gap: 6px;
|
|
2344
|
+
margin-left: auto;
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
.dshr-lifetime {
|
|
2348
|
+
font-size: 11px;
|
|
2349
|
+
white-space: nowrap;
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
/* 汇总卡(照 9router OverviewCards 的自适应五宫格) */
|
|
2353
|
+
.dshr-statGrid {
|
|
2354
|
+
display: grid;
|
|
2355
|
+
flex: none;
|
|
2356
|
+
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
2357
|
+
gap: 10px;
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
.dshr-statCard {
|
|
2361
|
+
display: flex;
|
|
2362
|
+
min-width: 0;
|
|
2363
|
+
flex-direction: column;
|
|
2364
|
+
gap: 3px;
|
|
2365
|
+
padding: 12px 14px;
|
|
2366
|
+
border: 1px solid var(--rs-line);
|
|
2367
|
+
border-radius: 10px;
|
|
2368
|
+
background: var(--rs-layer-1);
|
|
2369
|
+
box-shadow: 0 1px 2px rgb(0 0 0 / .04);
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
.dshr-statLabel {
|
|
2373
|
+
overflow: hidden;
|
|
2374
|
+
color: var(--rs-faint);
|
|
2375
|
+
font-size: 11px;
|
|
2376
|
+
font-weight: 600;
|
|
2377
|
+
letter-spacing: .03em;
|
|
2378
|
+
text-overflow: ellipsis;
|
|
2379
|
+
text-transform: uppercase;
|
|
2380
|
+
white-space: nowrap;
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
.dshr-statValue {
|
|
2384
|
+
overflow: hidden;
|
|
2385
|
+
font-size: 22px;
|
|
2386
|
+
font-weight: 700;
|
|
2387
|
+
letter-spacing: -.01em;
|
|
2388
|
+
line-height: 1.2;
|
|
2389
|
+
text-overflow: ellipsis;
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
.dshr-statHint {
|
|
2393
|
+
color: var(--rs-faint);
|
|
2394
|
+
font-size: 11px;
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
.dshr-statCard-primary .dshr-statValue { color: var(--rs-accent); }
|
|
2398
|
+
.dshr-statCard-ok .dshr-statValue { color: var(--rs-success); }
|
|
2399
|
+
.dshr-statCard-warn .dshr-statValue { color: var(--rs-warn); }
|
|
2400
|
+
.dshr-statCard-danger .dshr-statValue { color: var(--rs-danger); }
|
|
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
|
+
|
|
2464
|
+
.dshr-estimateNote {
|
|
2465
|
+
margin: 0;
|
|
2466
|
+
color: var(--rs-faint);
|
|
2467
|
+
font-size: 11px;
|
|
2468
|
+
line-height: 1.6;
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
.dshr-estimateNote code {
|
|
2472
|
+
padding: 0 3px;
|
|
2473
|
+
border-radius: 3px;
|
|
2474
|
+
background: var(--rs-layer-2);
|
|
2475
|
+
font-family: var(--rs-mono);
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
/* 趋势折线图(纯 SVG + HTML 叠加层,不引图表库) */
|
|
2479
|
+
.dshr-trendWrap {
|
|
2480
|
+
position: relative;
|
|
2481
|
+
padding: 14px 14px 10px;
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
.dshr-trendBusy {
|
|
2485
|
+
position: absolute;
|
|
2486
|
+
inset: 0;
|
|
2487
|
+
background: color-mix(in srgb, var(--rs-layer-1) 55%, transparent);
|
|
2488
|
+
pointer-events: none;
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
/* 绘图区:高度跟 SVG 视口一致,叠加层用百分比定位才对得上 */
|
|
2492
|
+
.dshr-linePlot {
|
|
2493
|
+
position: relative;
|
|
2494
|
+
height: 150px;
|
|
2495
|
+
cursor: crosshair;
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
/* 键盘可达(←/→ 逐格):聚焦环跟面板其它控件一致 */
|
|
2499
|
+
.dshr-linePlot:focus-visible {
|
|
2500
|
+
outline: 2px solid var(--rs-accent);
|
|
2501
|
+
outline-offset: 2px;
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
/* 峰值:给读数一个量级参照;半透明底是怕压在折线上看不清 */
|
|
2505
|
+
.dshr-linePeak {
|
|
2506
|
+
position: absolute;
|
|
2507
|
+
top: 0;
|
|
2508
|
+
right: 0;
|
|
2509
|
+
padding: 0 4px;
|
|
2510
|
+
border-radius: 3px;
|
|
2511
|
+
color: var(--rs-faint);
|
|
2512
|
+
background: color-mix(in srgb, var(--rs-layer-1) 72%, transparent);
|
|
2513
|
+
font-size: 9px;
|
|
2514
|
+
font-variant-numeric: tabular-nums;
|
|
2515
|
+
pointer-events: none;
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
.dshr-lineSvg {
|
|
2519
|
+
position: absolute;
|
|
2520
|
+
display: block;
|
|
2521
|
+
width: 100%;
|
|
2522
|
+
height: 100%;
|
|
2523
|
+
inset: 0;
|
|
2524
|
+
overflow: visible;
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
.dshr-lineGrid {
|
|
2528
|
+
stroke: var(--rs-line);
|
|
2529
|
+
stroke-dasharray: 3 3;
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2532
|
+
.dshr-lineArea {
|
|
2533
|
+
fill: color-mix(in srgb, var(--rs-accent) 14%, transparent);
|
|
2534
|
+
stroke: none;
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
.dshr-linePath {
|
|
2538
|
+
fill: none;
|
|
2539
|
+
stroke: var(--rs-accent);
|
|
2540
|
+
stroke-width: 2;
|
|
2541
|
+
stroke-linejoin: round;
|
|
2542
|
+
stroke-linecap: round;
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
/* 数值点:空心小圆(SVG 会被横向拉伸,所以画在 HTML 层) */
|
|
2546
|
+
.dshr-linePoint {
|
|
2547
|
+
position: absolute;
|
|
2548
|
+
width: 5px;
|
|
2549
|
+
height: 5px;
|
|
2550
|
+
border: 1.5px solid var(--rs-accent);
|
|
2551
|
+
border-radius: 50%;
|
|
2552
|
+
background: var(--rs-layer-1);
|
|
2553
|
+
transform: translate(-50%, -50%);
|
|
2554
|
+
pointer-events: none;
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
.dshr-lineGuide {
|
|
2558
|
+
position: absolute;
|
|
2559
|
+
top: 0;
|
|
2560
|
+
bottom: 0;
|
|
2561
|
+
width: 1px;
|
|
2562
|
+
background: var(--rs-line-strong);
|
|
2563
|
+
pointer-events: none;
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
.dshr-lineDot {
|
|
2567
|
+
position: absolute;
|
|
2568
|
+
width: 9px;
|
|
2569
|
+
height: 9px;
|
|
2570
|
+
border: 2px solid var(--rs-layer-1);
|
|
2571
|
+
border-radius: 50%;
|
|
2572
|
+
background: var(--rs-accent);
|
|
2573
|
+
box-shadow: 0 0 0 1px var(--rs-accent);
|
|
2574
|
+
transform: translate(-50%, -50%);
|
|
2575
|
+
pointer-events: none;
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
.dshr-lineLabels {
|
|
2579
|
+
position: relative;
|
|
2580
|
+
height: 14px;
|
|
2581
|
+
margin-top: 5px;
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
.dshr-lineLabel {
|
|
2585
|
+
position: absolute;
|
|
2586
|
+
top: 0;
|
|
2587
|
+
overflow: visible;
|
|
2588
|
+
color: var(--rs-faint);
|
|
2589
|
+
font-size: 9px;
|
|
2590
|
+
font-variant-numeric: tabular-nums;
|
|
2591
|
+
white-space: nowrap;
|
|
2592
|
+
transform: translateX(-50%);
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
/* 选中的时段:标签跟着高亮,不用抬头对读数才知道看的是哪一段 */
|
|
2596
|
+
.dshr-lineLabel-on {
|
|
2597
|
+
color: var(--rs-accent);
|
|
2598
|
+
font-weight: 700;
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
.dshr-trendReadout {
|
|
2602
|
+
display: flex;
|
|
2603
|
+
align-items: center;
|
|
2604
|
+
gap: 2px;
|
|
2605
|
+
min-height: 18px;
|
|
2606
|
+
padding-top: 6px;
|
|
2607
|
+
font-size: 11px;
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
.dshr-trendEmpty {
|
|
2611
|
+
display: flex;
|
|
2612
|
+
height: 150px;
|
|
2613
|
+
flex-direction: column;
|
|
2614
|
+
align-items: center;
|
|
2615
|
+
justify-content: center;
|
|
2616
|
+
gap: 6px;
|
|
2617
|
+
color: var(--rs-faint);
|
|
2618
|
+
font-size: 12px;
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
.dshr-trendEmptyIcon {
|
|
2622
|
+
display: inline-flex;
|
|
2623
|
+
align-items: center;
|
|
2624
|
+
justify-content: center;
|
|
2625
|
+
width: 44px;
|
|
2626
|
+
height: 44px;
|
|
2627
|
+
border-radius: 50%;
|
|
2628
|
+
color: var(--rs-accent);
|
|
2629
|
+
background: color-mix(in srgb, var(--rs-accent) 10%, transparent);
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
/* Top 榜(供应商 / 模型并排,窄屏堆叠) */
|
|
2633
|
+
.dshr-rankPair {
|
|
2634
|
+
display: grid;
|
|
2635
|
+
flex: none;
|
|
2636
|
+
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
|
2637
|
+
gap: 12px;
|
|
2638
|
+
align-items: start;
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
.dshr-rankList,
|
|
2642
|
+
.dshr-recentList {
|
|
2643
|
+
display: flex;
|
|
2644
|
+
flex-direction: column;
|
|
2645
|
+
padding: 6px 14px 12px;
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
.dshr-rankRow,
|
|
2649
|
+
.dshr-recentRow {
|
|
2650
|
+
display: flex;
|
|
2651
|
+
align-items: center;
|
|
2652
|
+
gap: 10px;
|
|
2653
|
+
min-width: 0;
|
|
2654
|
+
padding: 7px 0;
|
|
2655
|
+
border-bottom: 1px solid var(--rs-line);
|
|
2656
|
+
font-size: 12px;
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
.dshr-rankRow:last-child,
|
|
2660
|
+
.dshr-recentRow:last-child { border-bottom: none; }
|
|
2661
|
+
|
|
2662
|
+
/* 表头:不参与 hover,且不画分隔线(视觉上属于卡片头) */
|
|
2663
|
+
.dshr-rankHead {
|
|
2664
|
+
border-bottom: 1px solid var(--rs-line);
|
|
2665
|
+
color: var(--rs-faint);
|
|
2666
|
+
font-size: 11px;
|
|
2667
|
+
font-weight: 600;
|
|
2668
|
+
letter-spacing: .03em;
|
|
2669
|
+
text-transform: uppercase;
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
.dshr-rankRow:not(.dshr-rankHead):hover { background: var(--rs-hover); }
|
|
2673
|
+
|
|
2674
|
+
.dshr-rankName {
|
|
2675
|
+
min-width: 0;
|
|
2676
|
+
flex: 1 1 auto;
|
|
2677
|
+
overflow: hidden;
|
|
2678
|
+
text-overflow: ellipsis;
|
|
2679
|
+
white-space: nowrap;
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
.dshr-rankNum {
|
|
2683
|
+
flex: none;
|
|
2684
|
+
min-width: 52px;
|
|
2685
|
+
text-align: right;
|
|
2686
|
+
font-variant-numeric: tabular-nums;
|
|
2687
|
+
white-space: nowrap;
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2690
|
+
.dshr-rankFail { color: var(--rs-danger); }
|
|
2691
|
+
|
|
2692
|
+
.dshr-tokenIn { color: var(--rs-accent); }
|
|
2693
|
+
.dshr-tokenOut { color: var(--rs-success); }
|
|
2694
|
+
|
|
2695
|
+
/* 最近请求列宽(比 Top 榜宽,要放得下模型名) */
|
|
2696
|
+
.dshr-recentTime {
|
|
2697
|
+
display: flex;
|
|
2698
|
+
flex: none;
|
|
2699
|
+
align-items: center;
|
|
2700
|
+
gap: 5px;
|
|
2701
|
+
width: 78px;
|
|
2702
|
+
color: var(--rs-muted);
|
|
2703
|
+
font-variant-numeric: tabular-nums;
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
.dshr-recentModel {
|
|
2707
|
+
min-width: 0;
|
|
2708
|
+
flex: 1 1 auto;
|
|
2709
|
+
overflow: hidden;
|
|
2710
|
+
text-overflow: ellipsis;
|
|
2711
|
+
white-space: nowrap;
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2714
|
+
.dshr-recentSupplier {
|
|
2715
|
+
flex: none;
|
|
2716
|
+
width: 92px;
|
|
2717
|
+
overflow: hidden;
|
|
2718
|
+
color: var(--rs-muted);
|
|
2719
|
+
text-overflow: ellipsis;
|
|
2720
|
+
white-space: nowrap;
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2723
|
+
.dshr-recentRow-fail .dshr-recentModel { color: var(--rs-danger); }
|
|
2724
|
+
|
|
2725
|
+
/* 状态点 */
|
|
2726
|
+
.dshr-dot {
|
|
2727
|
+
display: inline-block;
|
|
2728
|
+
flex: none;
|
|
2729
|
+
width: 6px;
|
|
2730
|
+
height: 6px;
|
|
2731
|
+
border-radius: 50%;
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2734
|
+
.dshr-dot-ok { background: var(--rs-success); }
|
|
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
|
+
}
|