dsh-plugin-thinking-size 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.
- package/README.i18n.yaml +7 -0
- package/README.md +53 -0
- package/README.zh.md +65 -0
- package/cordis.patch.yml +4 -0
- package/lib/client.js +322 -0
- package/lib/index.js +9 -0
- package/package.json +80 -0
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Bilingual-pair consistency record: the git blob hash of each side as of the last
|
|
2
|
+
# confirmed-consistent state (convention from the dsh core repo, docs/i18n/README.md).
|
|
3
|
+
# Both languages carry equal authority; after editing either side, bring the other
|
|
4
|
+
# along and re-record with:
|
|
5
|
+
# git hash-object README.md README.zh.md
|
|
6
|
+
README.md: a60a5d2e42f935f4f2912e8db12512d6cdda576c
|
|
7
|
+
README.zh.md: e9c0ce1bc84cc03fbb25c96e318522a4dcce066a
|
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# dsh-plugin-thinking-size
|
|
2
|
+
|
|
3
|
+
A live reasoning-token badge for DSH web. It appends a compact `Think(128)` / `Think(5.2K)` marker to the title of every "Think" disclosure row in the conversation — ticking in real time while the model is still thinking, and staying put on history messages afterwards.
|
|
4
|
+
|
|
5
|
+
English | [中文](README.zh.md)
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
## The badge
|
|
11
|
+
|
|
12
|
+
| Format | Rule |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `Think(128)` | Below 1000 tokens: plain integer |
|
|
15
|
+
| `Think(5.2K)` | 1000 – 999 499: one decimal + K, trailing zeros dropped |
|
|
16
|
+
| `Think(1.2M)` | A million or more: one decimal + M |
|
|
17
|
+
|
|
18
|
+
- **Live** — the number ticks up while the reasoning is streaming.
|
|
19
|
+
- **Persistent** — every historical Think row keeps its badge too, across expand/collapse and session switches.
|
|
20
|
+
- **Subtle** — a 12px caption-colored span in tabular figures, appended inside the row title and marked `aria-hidden`, so the accessible title still reads just "Think".
|
|
21
|
+
|
|
22
|
+
## 雷霆大思考, measured
|
|
23
|
+
|
|
24
|
+
Some LLMs are famous for their 雷霆大思考 — thunder-length thinking that opens with several thousand tokens of pure reasoning. With this plugin you can watch the badge sprint in real time and see, at a glance, just how long each burst of thinking really is. No more wondering what the model was doing up there: the length is right on the row.
|
|
25
|
+
|
|
26
|
+
## How it works
|
|
27
|
+
|
|
28
|
+
- **Data** comes from the current session snapshot (`sessions` service → `chat.nodes`): the full text of every `reasoning` block of each `assistant-step` node. The collapsed DOM never carries the full reasoning text, so counting must read the snapshot, not the page.
|
|
29
|
+
- **Estimation** follows DeepSeek's official approximation — 1 English character ≈ 0.3 tokens, 1 CJK character ≈ 0.6 tokens, the sum rounded up. Counts are cached per reasoning-block object (its reference is immutable), so a streaming chunk only re-counts the one block that changed.
|
|
30
|
+
- **Writing** appends the badge span into each row's title span. Think rows (`data-variant="think"`) are matched to reasoning blocks by conversation anchor key plus in-anchor index. Snapshot subscription and a `MutationObserver` only set a dirty flag; a 100ms throttled sweep does the work — and if a React re-render wipes a badge, the observer flags it and the sweep puts it back.
|
|
31
|
+
- **Silent by design** — no host code is touched. If the `sessions` service is missing, the DOM contract changes, or a snapshot looks malformed, the plugin idles or skips rows quietly; it can never break the host.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
dsh plugin --profile web add dsh-plugin-thinking-size
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Restart `dsh web` to take effect.
|
|
40
|
+
|
|
41
|
+
### From source (local development)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pnpm install
|
|
45
|
+
pnpm run build
|
|
46
|
+
pnpm test
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Then add this package to `~/.dsh/profiles/web/package.json` as a `link:` dependency, register it in `dsh.profile.bundles`, and restart `dsh web`.
|
|
50
|
+
|
|
51
|
+
## Keywords
|
|
52
|
+
|
|
53
|
+
dsh · deepseek harness · dsh plugin · web plugin · client plugin · chat ui · thinking · deep thinking · reasoning · reasoning tokens · reasoning length · token counter · token estimator · token usage · think badge · badge · streaming · 思考 · 深度思考 · 长思考 · 思考长度 · 思考时长 · 思考过程 · 推理 · 推理 token · 思维链 · token 统计 · token 估算 · 徽标 · 实时显示 · 聊天界面 · 雷霆大思考
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# dsh-plugin-thinking-size
|
|
2
|
+
|
|
3
|
+
在 DeepSeek Harness web 界面的 "Think" 折叠行标题后实时显示思考内容的 token 量:
|
|
4
|
+
`Think(128)`、`Think(5.2K)`。思考流式输出时数字实时跳动,历史消息同样显示,
|
|
5
|
+
展开/折叠、切换会话后徽标仍在。
|
|
6
|
+
|
|
7
|
+
[English](README.md) | 中文
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
## 徽标
|
|
13
|
+
|
|
14
|
+
| 形态 | 规则 |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `Think(128)` | 不足 1000:整数 |
|
|
17
|
+
| `Think(5.2K)` | 1000 – 999 499:一位小数 + K,去尾 0 |
|
|
18
|
+
| `Think(1.2M)` | 百万以上:一位小数 + M |
|
|
19
|
+
|
|
20
|
+
- **实时** —— 思考流式输出期间数字随之跳动。
|
|
21
|
+
- **持久** —— 历史消息的每个 Think 行都保留徽标,展开/折叠、切换会话均不丢失。
|
|
22
|
+
- **克制** —— 12px、说明文字色、等宽数字的 `aria-hidden` span,追加在行标题内;
|
|
23
|
+
标题的可读文本仍只有 "Think"。
|
|
24
|
+
|
|
25
|
+
## 亲眼看看「雷霆大思考」有多长
|
|
26
|
+
|
|
27
|
+
部分 LLM 模型素有「雷霆大思考」之名——一个问题下去,先来几千上万 token 的纯思考。
|
|
28
|
+
装上本插件,你可以亲眼看着徽标里的数字一路狂飙,每一次思考到底有多长,看一眼便知。
|
|
29
|
+
模型在上面究竟想了多久,答案就写在行标题上。
|
|
30
|
+
|
|
31
|
+
## 原理
|
|
32
|
+
|
|
33
|
+
- **数据**:订阅当前会话快照(`sessions` 服务 → `chat.nodes`),从 `assistant-step`
|
|
34
|
+
节点的 `reasoning` 块取思考全文——折叠态 DOM 中没有全文,计数必须来自快照而非页面。
|
|
35
|
+
- **估算**:DeepSeek 官方近似口径——1 个英文字符 ≈ 0.3 token,1 个中文字符 ≈ 0.6 token,
|
|
36
|
+
合计向上取整。计数按 reasoning block 对象引用缓存(引用不可变),流式增量只需重算
|
|
37
|
+
当前变化的块。
|
|
38
|
+
- **写入**:把徽标 span 追加进行标题 span。Think 行(`data-variant="think"`)按会话流
|
|
39
|
+
锚点 key + 锚点内序号与 reasoning 块一一对应。快照订阅与 `MutationObserver` 只置脏
|
|
40
|
+
标记,100ms 节流 sweep 实际干活——React 重渲染抹掉徽标时,Observer 置脏、sweep 补回。
|
|
41
|
+
- **静默降级**:零侵入宿主。缺失 `sessions` 服务、宿主 DOM 契约改名、快照畸形时,
|
|
42
|
+
插件闲置或逐行跳过,绝不影响宿主运行。
|
|
43
|
+
|
|
44
|
+
## 安装
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
dsh plugin --profile web add dsh-plugin-thinking-size
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
重启 `dsh web` 生效。
|
|
51
|
+
|
|
52
|
+
### 从源码接入(本机开发)
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pnpm install
|
|
56
|
+
pnpm run build
|
|
57
|
+
pnpm test
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
然后在 `~/.dsh/profiles/web/package.json` 中加入本包(`link:` 依赖)并登记进
|
|
61
|
+
`dsh.profile.bundles`,重启 `dsh web`。
|
|
62
|
+
|
|
63
|
+
## 关键词
|
|
64
|
+
|
|
65
|
+
dsh · deepseek harness · dsh 插件 · web 插件 · 客户端插件 · 聊天界面 · 思考 · 深度思考 · 长思考 · 思考长度 · 思考时长 · 思考过程 · 推理 · 推理 token · 思维链 · token 统计 · token 估算 · 徽标 · 实时显示 · 雷霆大思考
|
package/cordis.patch.yml
ADDED
package/lib/client.js
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "dsh-plugin-thinking-size",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
|
|
7
|
+
"use strict";
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
10
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
11
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name2 in all)
|
|
14
|
+
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from))
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
25
|
+
|
|
26
|
+
// src/client/index.ts
|
|
27
|
+
var index_exports = {};
|
|
28
|
+
__export(index_exports, {
|
|
29
|
+
apply: () => apply,
|
|
30
|
+
inject: () => inject,
|
|
31
|
+
name: () => name
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(index_exports);
|
|
34
|
+
|
|
35
|
+
// src/client/dom.ts
|
|
36
|
+
var BADGE_CLASS = "dsh-thinking-size-badge";
|
|
37
|
+
var STYLE_ID = "dsh-thinking-size-style";
|
|
38
|
+
var BADGE_ATTR = "data-dsh-thinking-size";
|
|
39
|
+
function injectStyle(doc = document) {
|
|
40
|
+
if (doc.getElementById(STYLE_ID) !== null) return;
|
|
41
|
+
const tag = doc.createElement("style");
|
|
42
|
+
tag.id = STYLE_ID;
|
|
43
|
+
tag.textContent = `.${BADGE_CLASS}{color:var(--dsw-alias-label-caption,#8a8a8a);font-size:12px;font-weight:400;margin-left:2px;font-variant-numeric:tabular-nums;user-select:none}`;
|
|
44
|
+
doc.head.appendChild(tag);
|
|
45
|
+
}
|
|
46
|
+
function anchorKeyOf(row) {
|
|
47
|
+
const anchor = row.closest("[data-chat-anchor-key]");
|
|
48
|
+
return anchor?.getAttribute("data-chat-anchor-key") ?? null;
|
|
49
|
+
}
|
|
50
|
+
function titleSpanOf(row) {
|
|
51
|
+
const rowEl = row.querySelector("[data-disclosure-row]");
|
|
52
|
+
if (rowEl === null) return null;
|
|
53
|
+
for (const span of rowEl.querySelectorAll(":scope > span")) {
|
|
54
|
+
if (span.textContent === "Think" || span.querySelector(`:scope > [${BADGE_ATTR}]`) !== null) {
|
|
55
|
+
return span;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
function writeBadge(titleSpan, text) {
|
|
61
|
+
const doc = titleSpan.ownerDocument;
|
|
62
|
+
let badge = titleSpan.querySelector(`:scope > [${BADGE_ATTR}]`);
|
|
63
|
+
if (badge === null) {
|
|
64
|
+
badge = doc.createElement("span");
|
|
65
|
+
badge.className = BADGE_CLASS;
|
|
66
|
+
badge.setAttribute(BADGE_ATTR, "");
|
|
67
|
+
badge.setAttribute("aria-hidden", "true");
|
|
68
|
+
titleSpan.appendChild(badge);
|
|
69
|
+
}
|
|
70
|
+
if (badge.textContent === text) return false;
|
|
71
|
+
badge.textContent = text;
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
function removeBadge(row) {
|
|
75
|
+
const badges = row.querySelectorAll(`[${BADGE_ATTR}]`);
|
|
76
|
+
if (badges.length === 0) return false;
|
|
77
|
+
for (const badge of badges) badge.remove();
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
function queryThinkRows(root = document) {
|
|
81
|
+
return Array.from(root.querySelectorAll('[data-variant="think"]'));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/client/snapshot.ts
|
|
85
|
+
function collectReasoning(snapshot) {
|
|
86
|
+
const out = /* @__PURE__ */ new Map();
|
|
87
|
+
try {
|
|
88
|
+
const order = snapshot?.chat?.order;
|
|
89
|
+
const nodes = snapshot?.chat?.nodes;
|
|
90
|
+
if (order === void 0 || nodes === void 0) return out;
|
|
91
|
+
if (typeof nodes.get !== "function") return out;
|
|
92
|
+
for (const key of order) {
|
|
93
|
+
const node = nodes.get(key);
|
|
94
|
+
if (node?.kind !== "assistant-step") continue;
|
|
95
|
+
const blocks = node.data?.blocks;
|
|
96
|
+
if (!Array.isArray(blocks)) continue;
|
|
97
|
+
const reasoning = [];
|
|
98
|
+
for (const block of blocks) {
|
|
99
|
+
const candidate = block;
|
|
100
|
+
if (candidate !== null && candidate !== void 0 && candidate.kind === "reasoning" && typeof candidate.text === "string") {
|
|
101
|
+
reasoning.push(candidate);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (reasoning.length > 0) out.set(key, reasoning);
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
function createReasoningSource(sessions) {
|
|
111
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
112
|
+
let currentId;
|
|
113
|
+
let unsubscribeSession = null;
|
|
114
|
+
let unsubscribeList = null;
|
|
115
|
+
let disposed = false;
|
|
116
|
+
const notify = () => {
|
|
117
|
+
if (disposed) return;
|
|
118
|
+
for (const listener of listeners) {
|
|
119
|
+
try {
|
|
120
|
+
listener();
|
|
121
|
+
} catch {
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const sessionOf = (id) => {
|
|
126
|
+
if (id === void 0 || typeof sessions.binding !== "function") return void 0;
|
|
127
|
+
try {
|
|
128
|
+
return sessions.binding(id)?.session;
|
|
129
|
+
} catch {
|
|
130
|
+
return void 0;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const rebind = () => {
|
|
134
|
+
if (disposed) return;
|
|
135
|
+
let next;
|
|
136
|
+
try {
|
|
137
|
+
next = sessions.list?.getSnapshot?.().current;
|
|
138
|
+
} catch {
|
|
139
|
+
next = void 0;
|
|
140
|
+
}
|
|
141
|
+
if (next === currentId) return;
|
|
142
|
+
currentId = next;
|
|
143
|
+
try {
|
|
144
|
+
unsubscribeSession?.();
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
unsubscribeSession = null;
|
|
148
|
+
const session = sessionOf(currentId);
|
|
149
|
+
if (session !== void 0 && typeof session.subscribe === "function") {
|
|
150
|
+
try {
|
|
151
|
+
unsubscribeSession = session.subscribe(notify) ?? null;
|
|
152
|
+
} catch {
|
|
153
|
+
unsubscribeSession = null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
notify();
|
|
157
|
+
};
|
|
158
|
+
try {
|
|
159
|
+
unsubscribeList = sessions.list?.subscribe?.(rebind) ?? null;
|
|
160
|
+
} catch {
|
|
161
|
+
unsubscribeList = null;
|
|
162
|
+
}
|
|
163
|
+
rebind();
|
|
164
|
+
return {
|
|
165
|
+
collect() {
|
|
166
|
+
try {
|
|
167
|
+
const snapshot = sessionOf(currentId)?.getSnapshot?.();
|
|
168
|
+
return collectReasoning(snapshot);
|
|
169
|
+
} catch {
|
|
170
|
+
return /* @__PURE__ */ new Map();
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
subscribe(fn) {
|
|
174
|
+
listeners.add(fn);
|
|
175
|
+
return () => {
|
|
176
|
+
listeners.delete(fn);
|
|
177
|
+
};
|
|
178
|
+
},
|
|
179
|
+
dispose() {
|
|
180
|
+
disposed = true;
|
|
181
|
+
listeners.clear();
|
|
182
|
+
try {
|
|
183
|
+
unsubscribeSession?.();
|
|
184
|
+
} catch {
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
unsubscribeList?.();
|
|
188
|
+
} catch {
|
|
189
|
+
}
|
|
190
|
+
unsubscribeSession = null;
|
|
191
|
+
unsubscribeList = null;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// src/client/tokens.ts
|
|
197
|
+
var CJK = /[\u4E00-\u9FFF\u3400-\u4DBF\u3000-\u303F\uFF00-\uFFEF]/;
|
|
198
|
+
var TOKEN_PER_OTHER_CHAR = 0.3;
|
|
199
|
+
var TOKEN_PER_CJK_CHAR = 0.6;
|
|
200
|
+
function estimateTokens(text) {
|
|
201
|
+
if (!text) return 0;
|
|
202
|
+
let cjk = 0;
|
|
203
|
+
let other = 0;
|
|
204
|
+
for (const ch of text) {
|
|
205
|
+
if (CJK.test(ch)) cjk += 1;
|
|
206
|
+
else other += 1;
|
|
207
|
+
}
|
|
208
|
+
return Math.ceil(cjk * TOKEN_PER_CJK_CHAR + other * TOKEN_PER_OTHER_CHAR);
|
|
209
|
+
}
|
|
210
|
+
var blockCache = /* @__PURE__ */ new WeakMap();
|
|
211
|
+
function tokensOfBlock(block) {
|
|
212
|
+
const hit = blockCache.get(block);
|
|
213
|
+
if (hit !== void 0) return hit;
|
|
214
|
+
const tokens = estimateTokens(block.text);
|
|
215
|
+
blockCache.set(block, tokens);
|
|
216
|
+
return tokens;
|
|
217
|
+
}
|
|
218
|
+
function formatTokens(n) {
|
|
219
|
+
if (!Number.isFinite(n) || n <= 0) return "0";
|
|
220
|
+
if (n < 1e3) return String(Math.floor(n));
|
|
221
|
+
if (n < 999500) {
|
|
222
|
+
let s2 = (n / 1e3).toFixed(1);
|
|
223
|
+
if (s2.endsWith(".0")) s2 = s2.slice(0, -2);
|
|
224
|
+
return s2 + "K";
|
|
225
|
+
}
|
|
226
|
+
let s = (n / 1e6).toFixed(1);
|
|
227
|
+
if (s.endsWith(".0")) s = s.slice(0, -2);
|
|
228
|
+
return s + "M";
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/client/controller.ts
|
|
232
|
+
function thinkIndexOf(row) {
|
|
233
|
+
const anchor = row.closest("[data-chat-anchor-key]");
|
|
234
|
+
if (anchor === null) return -1;
|
|
235
|
+
let index = 0;
|
|
236
|
+
for (const candidate of anchor.querySelectorAll('[data-variant="think"]')) {
|
|
237
|
+
if (candidate === row) return index;
|
|
238
|
+
index += 1;
|
|
239
|
+
}
|
|
240
|
+
return -1;
|
|
241
|
+
}
|
|
242
|
+
function createController(deps) {
|
|
243
|
+
const doc = deps.doc ?? document;
|
|
244
|
+
let disposed = false;
|
|
245
|
+
let dirty = true;
|
|
246
|
+
const disposers = [];
|
|
247
|
+
injectStyle(doc);
|
|
248
|
+
disposers.push(
|
|
249
|
+
deps.source.subscribe(() => {
|
|
250
|
+
dirty = true;
|
|
251
|
+
})
|
|
252
|
+
);
|
|
253
|
+
const Observer = doc.defaultView?.MutationObserver ?? globalThis.MutationObserver;
|
|
254
|
+
const observer = new Observer(() => {
|
|
255
|
+
dirty = true;
|
|
256
|
+
});
|
|
257
|
+
observer.observe(doc.body ?? doc.documentElement, { childList: true, subtree: true });
|
|
258
|
+
disposers.push(
|
|
259
|
+
deps.interval(() => {
|
|
260
|
+
if (disposed || !dirty) return;
|
|
261
|
+
dirty = false;
|
|
262
|
+
sweep();
|
|
263
|
+
}, 100)
|
|
264
|
+
);
|
|
265
|
+
function sweep() {
|
|
266
|
+
if (disposed) return;
|
|
267
|
+
const byKey = deps.source.collect();
|
|
268
|
+
for (const row of queryThinkRows(doc)) {
|
|
269
|
+
const key = anchorKeyOf(row);
|
|
270
|
+
const index = thinkIndexOf(row);
|
|
271
|
+
const blocks = key !== null ? byKey.get(key) : void 0;
|
|
272
|
+
const block = blocks !== void 0 && index >= 0 ? blocks[index] : void 0;
|
|
273
|
+
if (block === void 0) {
|
|
274
|
+
removeBadge(row);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
const title = titleSpanOf(row);
|
|
278
|
+
if (title === null) continue;
|
|
279
|
+
writeBadge(title, `(${formatTokens(tokensOfBlock(block))})`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
sweep,
|
|
284
|
+
dispose() {
|
|
285
|
+
if (disposed) return;
|
|
286
|
+
disposed = true;
|
|
287
|
+
for (const disposer of disposers) {
|
|
288
|
+
try {
|
|
289
|
+
disposer();
|
|
290
|
+
} catch {
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
disposers.length = 0;
|
|
294
|
+
observer.disconnect();
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// src/client/index.ts
|
|
300
|
+
var name = "dsh-plugin-thinking-size";
|
|
301
|
+
var inject = ["timer", "sessions"];
|
|
302
|
+
function apply(ctx) {
|
|
303
|
+
try {
|
|
304
|
+
injectStyle();
|
|
305
|
+
} catch {
|
|
306
|
+
}
|
|
307
|
+
const sessions = ctx.get("sessions");
|
|
308
|
+
if (sessions === void 0) return;
|
|
309
|
+
const source = createReasoningSource(sessions);
|
|
310
|
+
const controller = createController({
|
|
311
|
+
source,
|
|
312
|
+
interval: (fn, ms) => ctx.interval(fn, ms)
|
|
313
|
+
});
|
|
314
|
+
return () => {
|
|
315
|
+
controller.dispose();
|
|
316
|
+
source.dispose();
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return module.exports;
|
|
321
|
+
}
|
|
322
|
+
});
|
package/lib/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-plugin-thinking-size",
|
|
3
|
+
"description": "Live reasoning-token badge — Think(5.2K) — on every Think disclosure row of the DSH web UI, ticking while streaming · 在 web 界面 Think 行标题后实时显示思考内容 token 数的 DeepSeek Harness 插件",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"dsh",
|
|
6
|
+
"deepseek-harness",
|
|
7
|
+
"deepseek",
|
|
8
|
+
"dsh-plugin",
|
|
9
|
+
"web-plugin",
|
|
10
|
+
"client-plugin",
|
|
11
|
+
"chat-ui",
|
|
12
|
+
"thinking",
|
|
13
|
+
"deep-thinking",
|
|
14
|
+
"reasoning",
|
|
15
|
+
"reasoning-tokens",
|
|
16
|
+
"reasoning-length",
|
|
17
|
+
"token-counter",
|
|
18
|
+
"token-estimator",
|
|
19
|
+
"token-usage",
|
|
20
|
+
"think-badge",
|
|
21
|
+
"badge",
|
|
22
|
+
"streaming",
|
|
23
|
+
"思考",
|
|
24
|
+
"深度思考",
|
|
25
|
+
"长思考",
|
|
26
|
+
"思考长度",
|
|
27
|
+
"思考时长",
|
|
28
|
+
"思考过程",
|
|
29
|
+
"推理",
|
|
30
|
+
"推理token",
|
|
31
|
+
"思维链",
|
|
32
|
+
"token统计",
|
|
33
|
+
"token估算",
|
|
34
|
+
"徽标",
|
|
35
|
+
"实时显示",
|
|
36
|
+
"聊天界面",
|
|
37
|
+
"雷霆大思考"
|
|
38
|
+
],
|
|
39
|
+
"version": "0.1.0",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/focksor/dsh-plugin-thinking-size.git"
|
|
44
|
+
},
|
|
45
|
+
"packageManager": "pnpm@11.7.0",
|
|
46
|
+
"files": [
|
|
47
|
+
"lib",
|
|
48
|
+
"cordis.patch.yml",
|
|
49
|
+
"README.md"
|
|
50
|
+
],
|
|
51
|
+
"type": "module",
|
|
52
|
+
"main": "./lib/index.js",
|
|
53
|
+
"exports": {
|
|
54
|
+
".": "./lib/index.js",
|
|
55
|
+
"./client": "./lib/client.js",
|
|
56
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
57
|
+
"./package.json": "./package.json"
|
|
58
|
+
},
|
|
59
|
+
"dsh": {
|
|
60
|
+
"bundle": {
|
|
61
|
+
"patch": "./cordis.patch.yml"
|
|
62
|
+
},
|
|
63
|
+
"client": {
|
|
64
|
+
"platform": "web",
|
|
65
|
+
"inject": []
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build": "node scripts/build.mjs && node scripts/check-build.mjs",
|
|
70
|
+
"pretest": "node scripts/build.mjs && tsc --noEmit",
|
|
71
|
+
"test": "node --test --experimental-strip-types",
|
|
72
|
+
"typecheck": "tsc --noEmit"
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@types/node": "^22.15.0",
|
|
76
|
+
"esbuild": "^0.25.0",
|
|
77
|
+
"jsdom": "^29.1.1",
|
|
78
|
+
"typescript": "^5.7.0"
|
|
79
|
+
}
|
|
80
|
+
}
|