dsh-context 0.6.0 → 0.6.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.
Files changed (2) hide show
  1. package/lib/client.js +43 -37
  2. package/package.json +4 -1
package/lib/client.js CHANGED
@@ -24,10 +24,10 @@ const DICT_ZH = {
24
24
  "trend.title": "\u5386\u53F2\u8D8B\u52BF",
25
25
  "gran.step": "\u6B65\u9AA4",
26
26
  "gran.turn": "\u8F6E\u6B21",
27
- "trend.hint": "\u6BCF\u6B21\u6A21\u578B\u8BF7\u6C42\u4E00\u6BB5\uFF1B\u70B9\u51FB\u67F1\u5B50\u67E5\u770B\u8BE6\u60C5\uFF0C\u2702 \u8868\u793A\u538B\u7F29/\u526A\u679D\uFF1B\u5DE6\u53F3\u6ED1\u52A8\u53EF\u56DE\u770B\u66F4\u65E9",
27
+ "trend.hint": "\u60AC\u505C\u67F1\u5B50\u67E5\u770B\u8BE6\u60C5\uFF08\u70B9\u51FB\u56FA\u5B9A\uFF09\uFF0C\u2702 \u8868\u793A\u538B\u7F29/\u526A\u679D\uFF1B\u5DE6\u53F3\u6ED1\u52A8\u53EF\u56DE\u770B\u66F4\u65E9\uFF1BStep/Turn \u5207\u6362\u7C92\u5EA6",
28
28
  "trend.empty": "\u53D1\u8D77\u4E00\u8F6E\u5BF9\u8BDD\u540E\uFF0C\u8FD9\u91CC\u4F1A\u5C55\u793A\u6BCF\u6B21\u6A21\u578B\u8BF7\u6C42\u7684\u4E0A\u4E0B\u6587\u6784\u6210",
29
- "detail.step": "T{t} \xB7 \u7B2C {s} \u6B65",
30
- "detail.turn": "T{t} \xB7 \u5171 {n} \u6B65",
29
+ "detail.step": "\u7B2C {t} \u8F6E \xB7 \u7B2C {s} \u6B65",
30
+ "detail.turn": "\u7B2C {t} \u8F6E \xB7 \u5171 {n} \u6B65",
31
31
  "detail.lastStep": "\u672B\u6B65",
32
32
  "detail.estTotal": "\u4F30\u7B97\u5408\u8BA1 \u2248 {n}",
33
33
  "detail.actual": "\u5B9E\u9645 prompt {n}",
@@ -41,8 +41,8 @@ const DICT_ZH = {
41
41
  "loading": "\u6B63\u5728\u8BFB\u53D6\u4F1A\u8BDD\u65E5\u5FD7\u2026",
42
42
  "error": "\u4E0A\u4E0B\u6587\u6570\u636E\u8BFB\u53D6\u5931\u8D25\uFF1A",
43
43
  "footer": "\u4F30\u7B97\u53E3\u5F84\uFF1A\u4E0E dsh \u5185\u7F6E tokenMeter \u76F8\u540C\u7684\u56FA\u5B9A\u5BC6\u5EA6\u542F\u53D1\u5F0F\uFF08\u7EA6 4 \u5B57\u7B26 \u2248 1 token\uFF09\uFF1B\u300C\u5B9E\u9645\u300D\u4E3A\u4F9B\u5E94\u5546\u4E0A\u62A5\u7528\u91CF\u3002",
44
- "tip.step": "T{t} \xB7 \u7B2C{s}\u6B65",
45
- "tip.turn": "T{t} \xB7 \u5171 {n} \u6B65",
44
+ "tip.step": "\u7B2C {t} \u8F6E \xB7 \u7B2C{s}\u6B65",
45
+ "tip.turn": "\u7B2C {t} \u8F6E \xB7 \u5171 {n} \u6B65",
46
46
  "tip.total": "\u5408\u8BA1 \u2248 {n}",
47
47
  "tip.actual": "\uFF08\u5B9E\u9645 {n}\uFF09",
48
48
  "ev.compaction": "\u538B\u7F29\u4E0A\u4E0B\u6587\uFF08\u6458\u8981\u66FF\u6362 {n} \u6761\u6D88\u606F\uFF09",
@@ -79,10 +79,10 @@ const DICT_EN = {
79
79
  "trend.title": "History",
80
80
  "gran.step": "Step",
81
81
  "gran.turn": "Turn",
82
- "trend.hint": "one bar per model request; click a bar for details, \u2702 marks compaction/prune; scroll sideways to see earlier",
82
+ "trend.hint": "hover a bar for details (click to pin), \u2702 marks compaction/prune; scroll sideways to see earlier; Step/Turn switches granularity",
83
83
  "trend.empty": "Send a message and each model request\u2019s context makeup shows up here",
84
- "detail.step": "T{t} \xB7 step {s}",
85
- "detail.turn": "T{t} \xB7 {n} steps",
84
+ "detail.step": "Turn {t} \xB7 Step {s}",
85
+ "detail.turn": "Turn {t} \xB7 {n} steps",
86
86
  "detail.lastStep": "last step",
87
87
  "detail.estTotal": "estimated \u2248 {n}",
88
88
  "detail.actual": "actual prompt {n}",
@@ -96,8 +96,8 @@ const DICT_EN = {
96
96
  "loading": "Reading the session log\u2026",
97
97
  "error": "Failed to read context data: ",
98
98
  "footer": "Estimate: same fixed-density heuristic as dsh\u2019s built-in tokenMeter (~4 chars \u2248 1 token); \u201Cactual\u201D is provider-reported usage.",
99
- "tip.step": "T{t} \xB7 step {s}",
100
- "tip.turn": "T{t} \xB7 {n} steps",
99
+ "tip.step": "Turn {t} \xB7 Step {s}",
100
+ "tip.turn": "Turn {t} \xB7 {n} steps",
101
101
  "tip.total": "total \u2248 {n}",
102
102
  "tip.actual": " (actual {n})",
103
103
  "ev.compaction": "Context compacted (summary replaced {n} messages)",
@@ -186,23 +186,21 @@ function makeView(ctx, t) {
186
186
  let total = 0;
187
187
  for (const p of props.parts) total += p.value;
188
188
  const scale = props.max !== void 0 && props.max > total ? props.max : total;
189
- const wrapRef = React.useRef(null);
190
- const [tip, setTip] = React.useState(null);
191
- const showTip = (e, p) => {
192
- const rect = wrapRef.current?.getBoundingClientRect();
193
- const left = rect ? e.clientX - rect.left : 50;
194
- const width = rect?.width ?? 200;
195
- setTip({
196
- key: p.key,
197
- text: catLabel(p.key) + " " + fmt(p.value) + " (" + Math.round(p.value / total * 100) + "%)",
198
- left: Math.max(48, Math.min(left, width - 48))
199
- });
200
- if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
201
- };
202
- const clearHover = () => {
203
- setTip(null);
204
- if (props.onHoverKey !== void 0) props.onHoverKey(null);
205
- };
189
+ let tip = null;
190
+ if (props.hoverKey !== null && props.hoverKey !== void 0) {
191
+ let acc = 0;
192
+ for (const p of props.parts) {
193
+ const pct = scale > 0 ? p.value / scale * 100 : 0;
194
+ if (p.key === props.hoverKey && p.value > 0) {
195
+ tip = {
196
+ text: catLabel(p.key) + " " + fmt(p.value) + " (" + Math.round(p.value / total * 100) + "%)",
197
+ leftPct: Math.max(12, Math.min(acc + pct / 2, 88))
198
+ };
199
+ break;
200
+ }
201
+ acc += pct;
202
+ }
203
+ }
206
204
  return h(
207
205
  "div",
208
206
  { className: "lc-stacked-wrap" },
@@ -211,8 +209,9 @@ function makeView(ctx, t) {
211
209
  {
212
210
  className: "lc-stacked",
213
211
  style: { height: (props.height || 14) + "px" },
214
- ref: wrapRef,
215
- onMouseLeave: clearHover
212
+ onMouseLeave: () => {
213
+ if (props.onHoverKey !== void 0) props.onHoverKey(null);
214
+ }
216
215
  },
217
216
  total <= 0 ? null : props.parts.map((p) => {
218
217
  if (!p.value) return null;
@@ -221,16 +220,13 @@ function makeView(ctx, t) {
221
220
  key: p.key,
222
221
  className: "lc-stacked-seg" + (on ? " lc-stacked-seg-on" : ""),
223
222
  style: { width: p.value / scale * 100 + "%", background: p.color },
224
- onMouseEnter: (e) => {
225
- showTip(e, p);
226
- },
227
- onMouseMove: (e) => {
228
- showTip(e, p);
223
+ onMouseEnter: () => {
224
+ if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
229
225
  }
230
226
  });
231
227
  })
232
228
  ),
233
- tip ? h("div", { className: "lc-bar-tip", style: { left: tip.left + "px" } }, tip.text) : null
229
+ tip ? h("div", { className: "lc-bar-tip", style: { left: tip.leftPct + "%" } }, tip.text) : null
234
230
  );
235
231
  }
236
232
  function Legend(props) {
@@ -295,15 +291,24 @@ function makeView(ctx, t) {
295
291
  }
296
292
  const scrollRef = React.useRef(null);
297
293
  const scrolledOnce = React.useRef(false);
294
+ const lastGranRef = React.useRef(props.granularity);
298
295
  const [edges, setEdges] = React.useState({ left: false, right: false });
296
+ const edgesRef = React.useRef(edges);
299
297
  const updateEdges = (el) => {
300
298
  const left = el.scrollLeft > 4;
301
299
  const right = el.scrollLeft + el.clientWidth < el.scrollWidth - 4;
302
- setEdges((prev) => prev.left === left && prev.right === right ? prev : { left, right });
300
+ const prev = edgesRef.current;
301
+ if (prev.left === left && prev.right === right) return;
302
+ edgesRef.current = { left, right };
303
+ setEdges({ left, right });
303
304
  };
304
305
  React.useLayoutEffect(() => {
305
306
  const el = scrollRef.current;
306
307
  if (el === null) return;
308
+ if (props.granularity !== lastGranRef.current) {
309
+ lastGranRef.current = props.granularity;
310
+ scrolledOnce.current = false;
311
+ }
307
312
  if (!scrolledOnce.current) {
308
313
  scrolledOnce.current = true;
309
314
  el.scrollLeft = el.scrollWidth;
@@ -647,6 +652,7 @@ function makeView(ctx, t) {
647
652
  selectedSeq: pinnedReq ? pinnedReq.seq : null,
648
653
  hoveredSeq,
649
654
  activeTurn,
655
+ granularity,
650
656
  onSelect: setSelectedSeq,
651
657
  onHover: setHoveredSeq,
652
658
  onHoverTurn: setHoverTurn
@@ -689,7 +695,7 @@ const STYLES = [
689
695
  ".lc-gran { margin-left: auto; display: flex; gap: 2px; background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; padding: 1px; }",
690
696
  ".lc-gran-btn { border: 0; background: transparent; color: var(--dsw-alias-label-secondary); font-size: 11px; line-height: 1; padding: 3px 8px; border-radius: 5px; cursor: pointer; font-family: inherit; }",
691
697
  ".lc-gran-btn:hover { color: var(--dsw-alias-label-primary); }",
692
- ".lc-gran-on, .lc-gran-on:hover { background: var(--dsw-alias-brand-primary); color: #fff; }",
698
+ ".lc-gran-on, .lc-gran-on:hover { background: var(--dsw-alias-button-primary-fill); color: var(--dsw-alias-label-primary-foreground); }",
693
699
  ".lc-overview-num { margin-bottom: 8px; }",
694
700
  ".lc-overview-num b { font-size: 20px; }",
695
701
  ".lc-overview-num span { color: var(--dsw-alias-label-secondary); }",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Context insight panel for DeepSeek Harness: a Context tab (beside Chat/Trajectory) showing what the model's context window is made of and how it evolves — composition, per-request history, compactions, injections, and model switches.",
5
5
  "author": "bowenliang123",
6
6
  "repository": {
@@ -60,6 +60,9 @@
60
60
  "@types/react": "^18.3.31",
61
61
  "esbuild": "^0.28.2",
62
62
  "husky": "^9.1.7",
63
+ "jsdom": "^30.0.1",
64
+ "react": "^18.3.1",
65
+ "react-dom": "^18.3.1",
63
66
  "typescript": "^7.0.2"
64
67
  },
65
68
  "packageManager": "pnpm@11.9.0"