dsh-usage-badge 0.1.8 → 0.1.9
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/docs/design-notes.md +17 -1
- package/lib/client.js +101 -51
- package/package.json +1 -1
package/docs/design-notes.md
CHANGED
|
@@ -267,6 +267,22 @@ dsh-usage-badge
|
|
|
267
267
|
2. 折叠结果交给 `cache-store.js`:默认写进 `$DSH_HOME/storages/usage-badge/cache.sqlite`,**一次只写变化的那几天**。只重读 size / mtime / 路径变化的日志;日志被删除时那条记录再也不会被写,于是缓存里的历史贡献原样留下(与桌面壳一致)。
|
|
268
268
|
3. 浏览器半轮询 `GET /usage-badge/summary`,本地完成区间聚合与筛选;切到「单价配置」时才去抓官方定价;切到「按年」时按年向 `GET /usage-badge/year` 取那一年的 12 个月(快照里没有更早的年份)。
|
|
269
269
|
|
|
270
|
+
### 弹窗不读主题令牌:它自带一套调色板
|
|
271
|
+
|
|
272
|
+
`.dub-pill`(侧边栏那一行)仍然用 DSH 的颜色令牌 —— 它要混在宿主自己的行里,跟着主题走才对。**弹窗是另一回事**:`.dub-box` 与图表 / 热力图的悬停卡片(`.dub-tip`)现在**不引用任何 `--dsw-*` 令牌**,颜色全部写死,挂在插件自己的 `--dub-*` 名下,深色变体跟着宿主的主题开关(`body[data-ds-dark-theme]`)切换;字体、字号、间距仍然继承宿主(面板该看起来像这个界面的一部分)。
|
|
273
|
+
|
|
274
|
+
为什么走到这一步,有两层原因:
|
|
275
|
+
|
|
276
|
+
1. **令牌的语义会变**。DSH 0.1.7 把 `--dsw-specific-menu` 从「不透明层」改成磨砂 + `backdrop-filter`,只填令牌、不加 blur 的规则就读成半透明 —— 当时改成直接填 `--dsw-alias-bg-layer-3`(那个 token 当时是不透明的),并由 `test/client.render.mjs` 钉住。
|
|
277
|
+
2. **令牌是任何人都能重定义的值**。open-sea-skin 把 `bg-base` / `bg-layer-1/2/3` 全部改写成 `rgba(…, 40%–90%)` 并带 `!important`,于是弹窗跟着整个界面一起变半透明:背后的对话与代码直接透到折线图上,数字没法读。先试过「只取令牌的颜色、把 alpha 钉成 1」(`rgb(from … r g b / 1)`),但那种修法仍然是**依赖**:下一个被皮肤改掉的令牌会再次失效。所以改成本插件自己拥有一套颜色。
|
|
278
|
+
|
|
279
|
+
代价与边界,写清楚:
|
|
280
|
+
|
|
281
|
+
- **不再跟随宿主的配色**:自定义主题(暖色、彩色)下弹窗仍是中性的白/深灰,不会跟着变色 —— 这正是「不受别的插件影响」的代价,也是要的。
|
|
282
|
+
- **只挡令牌**:如果某个插件直接对本插件的元素写 `style`/`!important`(例如 `* { background: … !important }`),或者给**祖先**加 `opacity`/`filter`,那仍然会影响到它 —— CSS 层面没有更硬的隔离。实测过这三种:前两种正常,第三种(全局 `!important` 填充、祖先 opacity)能穿透,属于「有插件故意针对」的情形。
|
|
283
|
+
- 断言见 `test/client.render.mjs`:**弹窗那段 CSS 里不许出现任何 `--dsw-`**(先剥掉注释再测,注释里允许点名令牌)、`.dub-box` / `.dub-tip` 必须填 `var(--dub-surface)`、浅色与深色两套调色板都在。
|
|
284
|
+
- 验证方式:把插件真实的样式表抽出来,在 headless Chromium 里把**所有** `--dsw-*` 令牌改成半透明/洋红,再读计算值 —— 浅色 `rgb(255,255,255)` / `rgb(17,20,24)`,深色 `rgb(22,26,33)` / `rgb(232,236,241)`,与令牌无关。
|
|
285
|
+
|
|
270
286
|
### 缓存为什么不是「一个 JSON 文件」
|
|
271
287
|
|
|
272
288
|
历史是不删的,所以「每次保存重写全部历史」这件事迟早会变成主问题。实测(把真实的一天桶形状铺成两年、761 天):
|
|
@@ -355,7 +371,7 @@ npm test
|
|
|
355
371
|
| `test/holidays.verify.mjs` | 节假日日历的行为:工作日节假日全天空闲、**调休周末仍按空闲价**(政策原文说周末全天空闲)、旧配置里的 `holidays.workdays` 被忽略、关闭日历时回到旧的按星期行为、自定义 `extra`、以及同一个月内不同日子的分类互不干扰 |
|
|
356
372
|
| `test/official-pricing.verify.mjs` | 解析器对**中英文两个页面快照**的逐字段断言(含旧模型别名、时区识别与换算);页面结构变化时会抛错而不是静默返回错价;`应用` 的合并语义(未知字段存活、已有的 `default` 行不动、没有 `default` 行时按官方第一条补一个并报告 `defaultFrom`、页面第一条确实是最便宜那档);并用**被 stub 掉的 fetch** 跑通两个路由,最后验证应用后的价格真的参与计费(含未列出模型按生成的 default 行计价),以及兜底行的完整生命周期:删除 → 回到内置模板并把 `defaultSource` 报成 `template` → 再次应用从官方第一条生成回来 → 手改过的兜底行不会被应用覆盖 |
|
|
357
373
|
| `test/cache.verify.mjs` | 缓存存储层:**两个后端跑同一套契约断言**(会话游标与桶的往返、`hourly` 槽位与逐请求记录、重写会话即替换它的日期集合、`years()` 的年份列表与有界区间读),以及只有跨重启才看得见的行为 —— **删掉会话日志后历史仍在**(新挂载的宿主照旧报出同样的金额)、**没有变化的一轮刷新不写文件**、**超出加载窗口的那天留在库里只是不读进来**(换个更宽的窗口还能取到)、旧 `cache.json` **迁移一次**并把旧文件改名为 `.migrated`(且 `.migrated` 之后**不再被读**)、**读不出来的缓存文件被改名保留并新建一个**、**更新版本的库只读使用且绝不改名**、加载窗口必须大于快照窗口这条不变量,以及 `按年` 路由(三年以前那一年照样读得到、12 个月恒定、`daily` 带节假日标记与 provider 拆分、没有数据的年份是 12 个空月而不是报错、非法年份 400)。 |
|
|
358
|
-
| `test/client.render.mjs` | 用替身 Loader 加载浏览器 bundle,检查 slot 注册形状与 `id`,再用 `react-dom/server` 渲染两个面板,断言金额格式、区间页签(含 `按年`:下拉只列出宿主报的那几年、选中的那一年被选中、月份轴恒为 12、点数是 12 × 序列数、四张卡片是那一年的合计、这一档不画热力图、没有年份时不渲染空下拉)、四序列折线的条数与配色、图例开关(单独显示某一条时的折线条数,以及面积填充有没有跟着走)、悬停 tooltip 的装配、provider 页签、热力图的格数(371 − 今天之后的那几天)与分档、官方价格表内容、两个开关(前缀回退与未匹配模型计价:开/关两种状态各自渲染出的说明与动作、default 行的来源标注、以及「哪些模型没价格」的提示)、**覆盖行编辑器**(每行的编辑/删除动作、倍率与峰谷列恒定存在、表单用被编辑行的真实数值预填而不是空模板、自定义规则才展开规则字段、新增行不给删除按钮、**兜底行同样可编辑可删除且不提供那些不生效的字段**、**重名键与「改过的行」会被保存拦下**、**旧宿主不会被说成「兜底行还没有」**、以及「保留未显示字段」与拒绝写 NaN
|
|
374
|
+
| `test/client.render.mjs` | 用替身 Loader 加载浏览器 bundle,检查 slot 注册形状与 `id`,再用 `react-dom/server` 渲染两个面板,断言金额格式、区间页签(含 `按年`:下拉只列出宿主报的那几年、选中的那一年被选中、月份轴恒为 12、点数是 12 × 序列数、四张卡片是那一年的合计、这一档不画热力图、没有年份时不渲染空下拉)、四序列折线的条数与配色、图例开关(单独显示某一条时的折线条数,以及面积填充有没有跟着走)、悬停 tooltip 的装配、provider 页签、热力图的格数(371 − 今天之后的那几天)与分档、官方价格表内容、两个开关(前缀回退与未匹配模型计价:开/关两种状态各自渲染出的说明与动作、default 行的来源标注、以及「哪些模型没价格」的提示)、**覆盖行编辑器**(每行的编辑/删除动作、倍率与峰谷列恒定存在、表单用被编辑行的真实数值预填而不是空模板、自定义规则才展开规则字段、新增行不给删除按钮、**兜底行同样可编辑可删除且不提供那些不生效的字段**、**重名键与「改过的行」会被保存拦下**、**旧宿主不会被说成「兜底行还没有」**、以及「保留未显示字段」与拒绝写 NaN 的源码断言)、节假日徽标与日历区块、展开/收起两种行形态、**弹窗那一段 CSS 里不许出现任何宿主颜色令牌(`--dsw-`),必须自带浅色/深色两套 `--dub-*` 调色板**,以及空数据 / 稀疏 provider 两个边界不崩 |
|
|
359
375
|
| `test/smoke.mjs` | 对着**本机真实**的 `$DSH_HOME` 跑一遍,打印徽标金额、今日 token 构成、当前峰谷档位、节假日日历状态、最近 7 天,以及滚动热力图那 371 天的分档分布(有用量的天数与四档各多少天,都挤在一档说明这张图会显得很平) |
|
|
360
376
|
| `test/official-pricing.live.mjs` | **联网**核对:抓真实页面并与 `test/fixtures/` 的快照逐字段比对,确认官方页面仍能解析、价格未变 |
|
|
361
377
|
|
package/lib/client.js
CHANGED
|
@@ -77,81 +77,131 @@ window.__ModuleLoader__.load({
|
|
|
77
77
|
.dub-dot-peak{background:var(--dsw-static-amber-500,#f59e0b)}
|
|
78
78
|
.dub-pill-tokens{margin-left:auto;flex:none;color:var(--dsw-alias-label-tertiary,#999);font-size:11px;font-variant-numeric:tabular-nums}
|
|
79
79
|
.dub-pill-amount{flex:none;margin-left:6px;color:var(--dsw-alias-label-primary,#111);font-variant-numeric:tabular-nums;font-weight:500}
|
|
80
|
-
|
|
81
|
-
.dub-
|
|
80
|
+
/* ── 弹窗自带一套样式,不读宿主的颜色令牌 ─────────────────────────────────────────────────────
|
|
81
|
+
.dub-pill(侧边栏那一行)仍然用 DSH 的令牌 —— 它要混在宿主自己的行里,跟着主题走才是对的。
|
|
82
|
+
弹窗是另一回事:它是**读数字**的地方,而令牌是任何人都能重新定义的值。皮肤完全可以把
|
|
83
|
+
--dsw-alias-bg-layer-3 改成玻璃(open-sea-skin 就是,40%–90% 可调),于是背后的对话直接透到
|
|
84
|
+
折线图上。所以弹窗改用自己的一套调色板:写死的颜色、挂在插件自己的 --dub-* 名下,深色变体
|
|
85
|
+
跟着宿主的主题开关走。这样任何插件靠重定义令牌都改不动它的观感 —— 要的就是这个独立性。
|
|
86
|
+
布局、间距和字体仍然继承宿主(字体故意不写死:面板该看起来像这个界面的一部分,只是不能透明)。 */
|
|
87
|
+
.dub-mask{
|
|
88
|
+
/* Light palette. This element also owns the scrim, so it is the natural place for it. */
|
|
89
|
+
color-scheme:light;
|
|
90
|
+
--dub-surface:#fff;
|
|
91
|
+
--dub-surface-soft:#f3f5f8;
|
|
92
|
+
--dub-ink:#111418;
|
|
93
|
+
--dub-ink-2:#5b6472;
|
|
94
|
+
--dub-ink-3:#8a929e;
|
|
95
|
+
--dub-line:rgba(15,23,42,.14);
|
|
96
|
+
--dub-line-soft:rgba(15,23,42,.08);
|
|
97
|
+
--dub-line-strong:rgba(15,23,42,.26);
|
|
98
|
+
--dub-grid:rgba(15,23,42,.10);
|
|
99
|
+
--dub-guide:rgba(15,23,42,.45);
|
|
100
|
+
--dub-fill:rgba(15,23,42,.07);
|
|
101
|
+
--dub-fill-strong:rgba(15,23,42,.18);
|
|
102
|
+
--dub-hover:rgba(15,23,42,.07);
|
|
103
|
+
--dub-link:#2f6fed;
|
|
104
|
+
--dub-warn:#a8590a;
|
|
105
|
+
--dub-mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
|
|
106
|
+
--dub-shadow:0 14px 44px rgba(15,23,42,.22);
|
|
107
|
+
position:fixed;inset:0;z-index:2147483600;pointer-events:auto;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.34)}
|
|
108
|
+
/* Dark palette. Keyed on the host's theme switch, which is the signal every DSH skin uses too; the
|
|
109
|
+
data-theme spellings are hedges for a future host that renames it (a selector that never matches
|
|
110
|
+
costs nothing, while a panel left light in a dark window would be a real regression). */
|
|
111
|
+
body[data-ds-dark-theme] .dub-mask,
|
|
112
|
+
body[data-theme="dark"] .dub-mask,
|
|
113
|
+
html[data-theme="dark"] .dub-mask{
|
|
114
|
+
color-scheme:dark;
|
|
115
|
+
--dub-surface:#161a21;
|
|
116
|
+
--dub-surface-soft:#1d222b;
|
|
117
|
+
--dub-ink:#e8ecf1;
|
|
118
|
+
--dub-ink-2:#aab3bf;
|
|
119
|
+
--dub-ink-3:#7c8695;
|
|
120
|
+
--dub-line:rgba(255,255,255,.16);
|
|
121
|
+
--dub-line-soft:rgba(255,255,255,.09);
|
|
122
|
+
--dub-line-strong:rgba(255,255,255,.26);
|
|
123
|
+
--dub-grid:rgba(255,255,255,.12);
|
|
124
|
+
--dub-guide:rgba(255,255,255,.45);
|
|
125
|
+
--dub-fill:rgba(255,255,255,.10);
|
|
126
|
+
--dub-fill-strong:rgba(255,255,255,.22);
|
|
127
|
+
--dub-hover:rgba(255,255,255,.10);
|
|
128
|
+
--dub-link:#8ab4ff;
|
|
129
|
+
--dub-warn:#f0b45f;
|
|
130
|
+
--dub-shadow:0 14px 44px rgba(0,0,0,.55)}
|
|
131
|
+
.dub-box{width:min(720px,100vw - 48px);max-height:min(680px,100vh - 80px);display:flex;flex-direction:column;overflow:hidden;border-radius:14px;border:.5px solid var(--dub-line);background:var(--dub-surface);color:var(--dub-ink);box-shadow:var(--dub-shadow)}
|
|
82
132
|
.dub-head{display:flex;align-items:center;gap:10px;padding:14px 18px 10px}
|
|
83
133
|
.dub-title{font-size:14px;font-weight:600;flex:1}
|
|
84
|
-
.dub-chip{font-size:11px;padding:1px 7px;border-radius:9px;background:var(--
|
|
85
|
-
.dub-chip-peak{background
|
|
134
|
+
.dub-chip{font-size:11px;padding:1px 7px;border-radius:9px;background:var(--dub-fill);color:var(--dub-ink-2);white-space:nowrap}
|
|
135
|
+
.dub-chip-peak{background:#f59e0b;color:#111}
|
|
86
136
|
.dub-chip-holiday{background:#16a34a;color:#fff}
|
|
87
137
|
.dub-x{border:0;background:0 0;color:inherit;font-size:18px;line-height:1;cursor:pointer;padding:2px 6px;border-radius:6px;opacity:.7}
|
|
88
|
-
.dub-x:hover{opacity:1;background:var(--
|
|
138
|
+
.dub-x:hover{opacity:1;background:var(--dub-hover)}
|
|
89
139
|
.dub-tabs{display:flex;gap:6px;padding:0 18px 10px;flex-wrap:wrap;align-items:center}
|
|
90
|
-
.dub-tab{font-size:12px;padding:3px 10px;border-radius:8px;border:.5px solid var(--
|
|
91
|
-
.dub-tab-on{background:var(--
|
|
140
|
+
.dub-tab{font-size:12px;padding:3px 10px;border-radius:8px;border:.5px solid var(--dub-line);background:0 0;color:inherit;cursor:pointer}
|
|
141
|
+
.dub-tab-on{background:var(--dub-fill);border-color:transparent;font-weight:600}
|
|
92
142
|
/* The year picker sits in the range row, so it is styled as one more tab. */
|
|
93
|
-
.dub-year{font:inherit;font-size:12px;padding:3px 6px;border-radius:8px;border:.5px solid var(--
|
|
143
|
+
.dub-year{font:inherit;font-size:12px;padding:3px 6px;border-radius:8px;border:.5px solid var(--dub-line);background:var(--dub-surface);color:inherit;cursor:pointer}
|
|
94
144
|
.dub-panetabs{padding:0 18px 8px;gap:0}
|
|
95
145
|
.dub-panetabs .dub-tab{border:0;border-bottom:2px solid transparent;border-radius:0;padding:4px 12px;font-size:13px;opacity:.7}
|
|
96
|
-
.dub-panetabs .dub-tab-on{background:0 0;border-bottom-color:var(--
|
|
97
|
-
.dub-sep{width:1px;height:16px;background:var(--
|
|
146
|
+
.dub-panetabs .dub-tab-on{background:0 0;border-bottom-color:var(--dub-ink);font-weight:600;opacity:1}
|
|
147
|
+
.dub-sep{width:1px;height:16px;background:var(--dub-line);margin:0 4px}
|
|
98
148
|
.dub-body{padding:0 18px 14px;overflow:auto}
|
|
99
|
-
.dub-stats{display:flex;gap:22px;flex-wrap:wrap;padding:10px 0 14px;border-bottom:.5px solid var(--
|
|
149
|
+
.dub-stats{display:flex;gap:22px;flex-wrap:wrap;padding:10px 0 14px;border-bottom:.5px solid var(--dub-line)}
|
|
100
150
|
.dub-stat{display:flex;flex-direction:column;gap:2px}
|
|
101
|
-
.dub-stat-k{font-size:11px;color:var(--
|
|
151
|
+
.dub-stat-k{font-size:11px;color:var(--dub-ink-3)}
|
|
102
152
|
.dub-stat-v{font-size:15px;font-weight:600;font-variant-numeric:tabular-nums}
|
|
103
153
|
.dub-chartwrap{padding-top:6px}
|
|
104
154
|
.dub-plot{position:relative}
|
|
105
|
-
.dub-legend{display:flex;gap:14px;flex-wrap:wrap;padding:2px 0 6px;font-size:11px;color:var(--
|
|
155
|
+
.dub-legend{display:flex;gap:14px;flex-wrap:wrap;padding:2px 0 6px;font-size:11px;color:var(--dub-ink-2)}
|
|
106
156
|
/* Legend entries are toggles, so a series can be read on its own — a lone line at
|
|
107
157
|
its own scale says more than four lines sharing one plot. The swatch of a hidden
|
|
108
158
|
series loses its colour rather than being restyled, so the inline background stays
|
|
109
159
|
the only place a series colour is written. */
|
|
110
160
|
.dub-legend-item{display:inline-flex;align-items:center;gap:5px;font:inherit;font-size:11px;color:inherit;background:0 0;border:0;padding:0;cursor:pointer}
|
|
111
|
-
.dub-legend-item:hover{color:var(--
|
|
161
|
+
.dub-legend-item:hover{color:var(--dub-ink)}
|
|
112
162
|
.dub-legend-off{opacity:.5}
|
|
113
|
-
.dub-legend-off .dub-legend-swatch{background:var(--
|
|
114
|
-
.dub-legend-reset{font:inherit;font-size:11px;background:0 0;border:0;padding:0;cursor:pointer;color:var(--
|
|
163
|
+
.dub-legend-off .dub-legend-swatch{background:var(--dub-fill-strong)}
|
|
164
|
+
.dub-legend-reset{font:inherit;font-size:11px;background:0 0;border:0;padding:0;cursor:pointer;color:var(--dub-link);text-decoration:underline}
|
|
115
165
|
.dub-legend-swatch{flex:none;width:11px;height:11px;border-radius:3px}
|
|
116
166
|
.dub-svg{display:block;width:100%;height:auto;overflow:visible}
|
|
117
|
-
.dub-grid{stroke:var(--
|
|
118
|
-
.dub-guide{stroke:var(--
|
|
167
|
+
.dub-grid{stroke:var(--dub-grid);stroke-width:1}
|
|
168
|
+
.dub-guide{stroke:var(--dub-guide);stroke-width:1;stroke-dasharray:3 3}
|
|
119
169
|
.dub-band-holiday{fill:rgba(22,163,74,.10)}
|
|
120
|
-
.dub-axis{fill:var(--
|
|
121
|
-
.dub-tip{position:absolute;top:18px;transform:translateX(-50%);pointer-events:none;box-sizing:border-box;width:172px;padding:8px 10px;border-radius:8px;border:.5px solid var(--
|
|
122
|
-
.dub-tip-title{font-weight:600;color:var(--
|
|
170
|
+
.dub-axis{fill:var(--dub-ink-3);font-size:10px;text-anchor:middle}
|
|
171
|
+
.dub-tip{position:absolute;top:18px;transform:translateX(-50%);pointer-events:none;box-sizing:border-box;width:172px;padding:8px 10px;border-radius:8px;border:.5px solid var(--dub-line);background:var(--dub-surface);box-shadow:0 6px 20px rgba(0,0,0,.18);font-size:11px;line-height:1.6;color:var(--dub-ink-2);z-index:2}
|
|
172
|
+
.dub-tip-title{font-weight:600;color:var(--dub-ink);margin-bottom:2px}
|
|
123
173
|
.dub-tip-row{display:flex;align-items:center;gap:6px;white-space:nowrap}
|
|
124
174
|
.dub-tip-label{flex:1}
|
|
125
|
-
.dub-tip-value{font-variant-numeric:tabular-nums;color:var(--
|
|
126
|
-
.dub-empty{padding:34px 0;text-align:center;font-size:12px;color:var(--
|
|
127
|
-
.dub-block{border:.5px solid var(--
|
|
175
|
+
.dub-tip-value{font-variant-numeric:tabular-nums;color:var(--dub-ink)}
|
|
176
|
+
.dub-empty{padding:34px 0;text-align:center;font-size:12px;color:var(--dub-ink-3)}
|
|
177
|
+
.dub-block{border:.5px solid var(--dub-line);border-radius:10px;padding:12px 14px;margin:12px 0}
|
|
128
178
|
.dub-block-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:8px}
|
|
129
179
|
.dub-block-title{font-size:13px;font-weight:600;flex:1}
|
|
130
|
-
.dub-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12px;color:var(--
|
|
180
|
+
.dub-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12px;color:var(--dub-ink-2);padding:3px 0}
|
|
131
181
|
.dub-table{width:100%;border-collapse:collapse;font-size:12px;margin:6px 0}
|
|
132
|
-
.dub-table th{text-align:left;font-weight:600;color:var(--
|
|
133
|
-
.dub-table td{padding:4px 8px 4px 0;border-bottom:.5px solid var(--
|
|
134
|
-
.dub-table td.dub-model{font-family:var(--
|
|
135
|
-
.dub-note{color:var(--
|
|
136
|
-
.dub-warn{color
|
|
182
|
+
.dub-table th{text-align:left;font-weight:600;color:var(--dub-ink-2);padding:4px 8px 4px 0;border-bottom:.5px solid var(--dub-line);white-space:nowrap}
|
|
183
|
+
.dub-table td{padding:4px 8px 4px 0;border-bottom:.5px solid var(--dub-line-soft);font-variant-numeric:tabular-nums;white-space:nowrap}
|
|
184
|
+
.dub-table td.dub-model{font-family:var(--dub-mono);color:var(--dub-ink)}
|
|
185
|
+
.dub-note{color:var(--dub-ink-3);font-size:11px;line-height:1.5}
|
|
186
|
+
.dub-warn{color:var(--dub-warn);font-size:11px}
|
|
137
187
|
/* The version handshake lives at the top of the dialog, above the tabs, so a mismatch is stated
|
|
138
188
|
on whichever tab the reader opened. */
|
|
139
|
-
.dub-version{padding:8px 18px;border-bottom:.5px solid var(--
|
|
189
|
+
.dub-version{padding:8px 18px;border-bottom:.5px solid var(--dub-line);background:var(--dub-surface-soft)}
|
|
140
190
|
/* Explanatory prose lives in these tooltips instead of under every block: the panel is a table of
|
|
141
191
|
numbers, and a wall of notes under each one is what made it hard to scan. */
|
|
142
192
|
.dub-info{flex:none;display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;border-radius:50%;border:1px solid currentColor;font-size:9px;font-style:normal;line-height:1;opacity:.5;cursor:help;user-select:none}
|
|
143
193
|
.dub-info:hover{opacity:.9}
|
|
144
|
-
.dub-foot{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 18px;border-top:.5px solid var(--
|
|
145
|
-
.dub-foot input,.dub-foot select,.dub-block input,.dub-block select{font:inherit;font-size:12px;padding:2px 6px;border-radius:6px;border:.5px solid var(--
|
|
146
|
-
.dub-btn{font:inherit;font-size:12px;padding:3px 10px;border-radius:7px;border:.5px solid var(--
|
|
147
|
-
.dub-btn:hover{background:var(--
|
|
194
|
+
.dub-foot{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 18px;border-top:.5px solid var(--dub-line);font-size:12px;color:var(--dub-ink-2)}
|
|
195
|
+
.dub-foot input,.dub-foot select,.dub-block input,.dub-block select{font:inherit;font-size:12px;padding:2px 6px;border-radius:6px;border:.5px solid var(--dub-line-strong);background:var(--dub-surface);color:inherit;width:88px}
|
|
196
|
+
.dub-btn{font:inherit;font-size:12px;padding:3px 10px;border-radius:7px;border:.5px solid var(--dub-line-strong);background:0 0;color:inherit;cursor:pointer;white-space:nowrap}
|
|
197
|
+
.dub-btn:hover{background:var(--dub-hover)}
|
|
148
198
|
.dub-btn:disabled{opacity:.5;cursor:default}
|
|
149
|
-
/* The one filled action in the plugin. --dsw-alias-brand-primary is a
|
|
150
|
-
token in this design system (DSH uses it for text, outlines and the switch track),
|
|
151
|
-
so filling with it left white text on near-white in the dark theme. The DeepSeek
|
|
152
|
-
|
|
153
|
-
.dub-btn-primary{border-color:transparent;background
|
|
154
|
-
.dub-btn-primary:hover{opacity:.9;background
|
|
199
|
+
/* The one filled action in the plugin. Not a theme token: --dsw-alias-brand-primary is a
|
|
200
|
+
*foreground* token in this design system (DSH uses it for text, outlines and the switch track),
|
|
201
|
+
so filling with it left white text on near-white in the dark theme. The DeepSeek blue is written
|
|
202
|
+
out here for the same reason the rest of the panel's palette is. */
|
|
203
|
+
.dub-btn-primary{border-color:transparent;background:#4176e6;color:#fff}
|
|
204
|
+
.dub-btn-primary:hover{opacity:.9;background:#4176e6}
|
|
155
205
|
/* Contributions heatmap: one square per calendar day of the last year. The five
|
|
156
206
|
steps are the same rgba green at rising opacity, so the ramp reads correctly on
|
|
157
207
|
both a light and a dark panel without a theme query. Each step sets fill for the
|
|
@@ -160,30 +210,30 @@ window.__ModuleLoader__.load({
|
|
|
160
210
|
.dub-hm-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:6px}
|
|
161
211
|
.dub-hm-title{flex:1;font-size:12px;font-weight:600}
|
|
162
212
|
.dub-hm-metrics{display:flex;gap:4px}
|
|
163
|
-
.dub-hm-metric{font:inherit;font-size:11px;padding:2px 8px;border-radius:7px;border:.5px solid var(--
|
|
164
|
-
.dub-hm-metric:hover{background:var(--
|
|
165
|
-
.dub-hm-metric-on{background:var(--
|
|
213
|
+
.dub-hm-metric{font:inherit;font-size:11px;padding:2px 8px;border-radius:7px;border:.5px solid var(--dub-line);background:0 0;color:inherit;cursor:pointer}
|
|
214
|
+
.dub-hm-metric:hover{background:var(--dub-hover)}
|
|
215
|
+
.dub-hm-metric-on{background:var(--dub-fill);border-color:transparent;font-weight:600}
|
|
166
216
|
/* The tooltip's percentages are squares of the grid, so it hangs off a wrapper that
|
|
167
217
|
is exactly as tall as the svg rather than off the block (title and legend included). */
|
|
168
218
|
.dub-hm-plot{position:relative}
|
|
169
219
|
.dub-hm-cell{stroke:none}
|
|
170
|
-
.dub-hm-l0{fill:var(--
|
|
220
|
+
.dub-hm-l0{fill:var(--dub-fill);background:var(--dub-fill)}
|
|
171
221
|
.dub-hm-l1{fill:rgba(45,164,78,.25);background:rgba(45,164,78,.25)}
|
|
172
222
|
.dub-hm-l2{fill:rgba(45,164,78,.45);background:rgba(45,164,78,.45)}
|
|
173
223
|
.dub-hm-l3{fill:rgba(45,164,78,.7);background:rgba(45,164,78,.7)}
|
|
174
224
|
.dub-hm-l4{fill:rgba(45,164,78,.95);background:rgba(45,164,78,.95)}
|
|
175
|
-
.dub-hm-month,.dub-hm-wd{fill:var(--
|
|
225
|
+
.dub-hm-month,.dub-hm-wd{fill:var(--dub-ink-3);font-size:9px}
|
|
176
226
|
/* The hover card is the chart's .dub-tip, so this only re-anchors it: the style is
|
|
177
227
|
shared, the position is a fraction of the grid. */
|
|
178
228
|
.dub-hm-tip{margin-top:5px}
|
|
179
229
|
.dub-hm-tip-up{margin-top:-5px;transform:translate(-50%,-100%)}
|
|
180
|
-
.dub-hm-foot{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding-top:6px;font-size:11px;color:var(--
|
|
230
|
+
.dub-hm-foot{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding-top:6px;font-size:11px;color:var(--dub-ink-3)}
|
|
181
231
|
.dub-hm-legend{margin-left:auto;display:inline-flex;align-items:center;gap:4px}
|
|
182
232
|
.dub-hm-swatch{display:inline-block;width:10px;height:10px;border-radius:2px}
|
|
183
233
|
/* The price-row editor. The block already styles its inputs at 88px wide, so the fields
|
|
184
234
|
that need another width are written as a two-class selector to outrank that rule. */
|
|
185
|
-
.dub-form{border-top:.5px solid var(--
|
|
186
|
-
.dub-form-row{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:12px;color:var(--
|
|
235
|
+
.dub-form{border-top:.5px solid var(--dub-line);margin-top:10px;padding-top:10px}
|
|
236
|
+
.dub-form-row{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:12px;color:var(--dub-ink-2);padding:3px 0}
|
|
187
237
|
.dub-form-row label{white-space:nowrap}
|
|
188
238
|
.dub-block .dub-form-key{width:250px}
|
|
189
239
|
.dub-block .dub-form-num{width:74px}
|
|
@@ -191,8 +241,8 @@ window.__ModuleLoader__.load({
|
|
|
191
241
|
.dub-form-check{display:inline-flex;align-items:center;gap:4px}
|
|
192
242
|
.dub-form-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end;padding-top:8px}
|
|
193
243
|
.dub-table-ops{display:flex;gap:6px}
|
|
194
|
-
.dub-op{font:inherit;font-size:11px;padding:1px 7px;border-radius:6px;border:.5px solid var(--
|
|
195
|
-
.dub-op:hover{background:var(--
|
|
244
|
+
.dub-op{font:inherit;font-size:11px;padding:1px 7px;border-radius:6px;border:.5px solid var(--dub-line-strong);background:0 0;color:inherit;cursor:pointer;white-space:nowrap}
|
|
245
|
+
.dub-op:hover{background:var(--dub-hover)}
|
|
196
246
|
`
|
|
197
247
|
|
|
198
248
|
/** Inject the stylesheet once per document, guarded the way shipped plugins do. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-usage-badge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "DeepSeek Harness 的用量徽标插件:侧边栏底部一行显示当天的 token 总量与估算费用,点开可看 24 小时 / 近 7 日 / 近 30 日 / 近 12 个月 / 按年用量,按 provider 筛选,并按 DeepSeek 官方定价与中国法定节假日计价。A usage badge plugin for DeepSeek Harness: today's tokens and estimated spend in the sidebar, with 24h / 7d / 30d / 12-month and per-year charts, official DeepSeek pricing and Chinese public holidays.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|