dsh-context 0.2.2 → 0.3.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.
Files changed (2) hide show
  1. package/lib/client.js +70 -28
  2. package/package.json +10 -1
package/lib/client.js CHANGED
@@ -199,6 +199,11 @@ function makeView(ctx, t) {
199
199
 
200
200
  // Plot height in px (the marker lane above it is 18px).
201
201
  var CHART_H = 112
202
+ // Fixed column geometry: constant bar width keeps sparse histories from
203
+ // stretching bars, and dense histories scroll horizontally instead of
204
+ // compressing. The turn tick row below mirrors the same column grid.
205
+ var BAR_W = 14
206
+ var BAR_GAP = 2
202
207
 
203
208
  function TrendChart(props) {
204
209
  var requests = props.requests
@@ -218,36 +223,67 @@ function makeView(ctx, t) {
218
223
  }
219
224
  }
220
225
 
226
+ // Consecutive requests of the same turn collapse into one labeled range.
227
+ var groups = []
228
+ for (var g = 0; g < requests.length; g++) {
229
+ var grp = groups.length > 0 ? groups[groups.length - 1] : null
230
+ if (grp === null || grp.turn !== requests[g].turn) {
231
+ grp = { turn: requests[g].turn, count: 0 }
232
+ groups.push(grp)
233
+ }
234
+ grp.count++
235
+ }
236
+
237
+ // Stick to the newest bars unless the user scrolled away from the end.
238
+ var scrollRef = React.useRef(null)
239
+ React.useEffect(function () {
240
+ var el = scrollRef.current
241
+ if (el === null) return
242
+ if (el.scrollLeft + el.clientWidth >= el.scrollWidth - 24) el.scrollLeft = el.scrollWidth
243
+ })
244
+
221
245
  return h('div', { className: 'lc-chartrow' },
222
246
  h('div', { className: 'lc-axis' },
223
247
  h('span', { className: 'lc-axis-top' }, fmt(maxTotal)),
224
248
  h('span', { className: 'lc-axis-mid' }, fmt(Math.round(maxTotal / 2))),
225
249
  h('span', { className: 'lc-axis-bot' }, '0')),
226
- h('div', { className: 'lc-chart' },
227
- h('div', { className: 'lc-grid lc-grid-top' }),
228
- h('div', { className: 'lc-grid lc-grid-mid' }),
229
- requests.map(function (req, i) {
230
- var selected = props.selectedSeq === req.seq
231
- var tip = tr('tip.step', { t: req.turn, s: req.step }) + ' · ' + fmtTime(req.time) + '\n'
232
- + tr('tip.total', { n: fmt(req.total) })
233
- + (req.prompt !== undefined ? tr('tip.actual', { n: fmt(req.prompt) }) : '') + '\n'
234
- + CATS.map(function (c) { return catLabel(c.key) + ' ' + fmt(req[c.key] || 0) }).join(' / ')
235
- return h('div', {
236
- key: req.seq,
237
- className: 'lc-bar' + (selected ? ' lc-bar-selected' : ''),
238
- title: tip,
239
- onClick: function () { props.onSelect(selected ? null : req.seq) },
240
- },
241
- markers[i] ? h('span', { className: 'lc-bar-marker', title: eventLabel(markers[i]) }, '✂') : null,
242
- h('div', { className: 'lc-bar-stack' },
243
- CATS.map(function (c) {
244
- var v = req[c.key] || 0
245
- if (!v) return null
246
- // px heights: the stack's height is content-driven, so
247
- // percentage heights would collapse against an indefinite base.
248
- return h('div', { key: c.key, style: { height: Math.max(1, Math.round(v / maxTotal * CHART_H)) + 'px', background: c.color } })
249
- })))
250
- })))
250
+ h('div', { className: 'lc-chart-scroll', ref: scrollRef },
251
+ h('div', { className: 'lc-chart' },
252
+ h('div', { className: 'lc-grid lc-grid-top' }),
253
+ h('div', { className: 'lc-grid lc-grid-mid' }),
254
+ requests.map(function (req, i) {
255
+ var selected = props.selectedSeq === req.seq
256
+ var tip = tr('tip.step', { t: req.turn, s: req.step }) + ' · ' + fmtTime(req.time) + '\n'
257
+ + tr('tip.total', { n: fmt(req.total) })
258
+ + (req.prompt !== undefined ? tr('tip.actual', { n: fmt(req.prompt) }) : '') + '\n'
259
+ + CATS.map(function (c) { return catLabel(c.key) + ' ' + fmt(req[c.key] || 0) }).join(' / ')
260
+ return h('div', {
261
+ key: req.seq,
262
+ className: 'lc-bar' + (selected ? ' lc-bar-selected' : ''),
263
+ title: tip,
264
+ onClick: function () { props.onSelect(selected ? null : req.seq) },
265
+ },
266
+ markers[i] ? h('span', { className: 'lc-bar-marker', title: eventLabel(markers[i]) }, '✂') : null,
267
+ h('div', { className: 'lc-bar-stack' },
268
+ CATS.map(function (c) {
269
+ var v = req[c.key] || 0
270
+ if (!v) return null
271
+ // px heights: the stack's height is content-driven, so
272
+ // percentage heights would collapse against an indefinite base.
273
+ return h('div', { key: c.key, style: { height: Math.max(1, Math.round(v / maxTotal * CHART_H)) + 'px', background: c.color } })
274
+ })))
275
+ })),
276
+ h('div', { className: 'lc-turns' },
277
+ groups.map(function (grp, gi) {
278
+ // One column per bar plus the shared gap; the flex gap between
279
+ // ticks restores the inter-group gap, so tick spans line up with
280
+ // their bars exactly.
281
+ return h('span', {
282
+ key: 'turn-' + gi,
283
+ className: 'lc-turn',
284
+ style: { width: (grp.count * (BAR_W + BAR_GAP) - BAR_GAP) + 'px' },
285
+ }, 'T' + grp.turn)
286
+ }))))
251
287
  }
252
288
 
253
289
  function RequestDetail(props) {
@@ -433,21 +469,27 @@ var STYLES = [
433
469
  '.lc-tools { margin-top: 10px; color: var(--dsw-alias-label-secondary); display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }',
434
470
  '.lc-tool-chip { background: var(--dsw-alias-bg-layer-2); border-radius: 4px; padding: 1px 7px; font-size: 12px; color: var(--dsw-alias-label-primary); }',
435
471
  '.lc-chartrow { display: flex; gap: 6px; align-items: stretch; }',
436
- '.lc-axis { position: relative; width: 40px; height: 130px; padding-top: 18px; box-sizing: border-box; color: var(--dsw-alias-label-secondary); font-size: 11px; }',
472
+ '.lc-axis { position: relative; width: 40px; height: 150px; padding-top: 18px; box-sizing: border-box; color: var(--dsw-alias-label-secondary); font-size: 11px; }',
437
473
  '.lc-axis span { position: absolute; right: 0; line-height: 1; }',
438
474
  '.lc-axis-top { top: 13px; }',
439
475
  '.lc-axis-mid { top: 69px; }',
440
476
  '.lc-axis-bot { top: 125px; }',
441
- '.lc-chart { position: relative; flex: 1; display: flex; align-items: flex-end; gap: 2px; height: 130px; padding-top: 18px; box-sizing: border-box; }',
477
+ '.lc-chart-scroll { flex: 1; overflow-x: auto; overflow-y: hidden; min-width: 0; scrollbar-width: thin; }',
478
+ '.lc-chart-scroll::-webkit-scrollbar { height: 6px; }',
479
+ '.lc-chart-scroll::-webkit-scrollbar-thumb { background: var(--dsw-alias-border-l1); border-radius: 3px; }',
480
+ '.lc-chart-scroll::-webkit-scrollbar-track { background: transparent; }',
481
+ '.lc-chart { position: relative; display: flex; align-items: flex-end; gap: 2px; height: 130px; padding-top: 18px; box-sizing: border-box; width: max-content; min-width: 100%; }',
442
482
  '.lc-grid { position: absolute; left: 0; right: 0; border-top: 1px dashed var(--dsw-alias-border-l1); pointer-events: none; }',
443
483
  '.lc-grid-top { top: 18px; }',
444
484
  '.lc-grid-mid { top: 74px; }',
445
- '.lc-bar { position: relative; flex: 1; min-width: 5px; height: 100%; display: flex; align-items: flex-end; cursor: pointer; border-radius: 2px; }',
485
+ '.lc-bar { position: relative; width: 14px; flex: none; height: 100%; display: flex; align-items: flex-end; cursor: pointer; border-radius: 2px; }',
446
486
  '.lc-bar:hover { background: var(--dsw-alias-bg-layer-2); }',
447
487
  '.lc-bar-selected { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 1px; }',
448
488
  '.lc-bar-stack { display: flex; flex-direction: column-reverse; width: 100%; }',
449
489
  '.lc-bar-stack > div { width: 100%; }',
450
490
  '.lc-bar-marker { position: absolute; top: -16px; left: 50%; transform: translateX(-50%); font-size: 11px; color: var(--dsw-alias-state-warn-primary); }',
491
+ '.lc-turns { display: flex; gap: 2px; width: max-content; min-width: 100%; }',
492
+ '.lc-turn { flex: none; min-width: 24px; box-sizing: border-box; text-align: center; font-size: 11px; line-height: 18px; color: var(--dsw-alias-label-secondary); border-top: 1px solid var(--dsw-alias-border-l1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }',
451
493
  '.lc-detail { margin-top: 12px; border-top: 1px solid var(--dsw-alias-border-l1); padding-top: 12px; }',
452
494
  '.lc-detail-head { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-bottom: 8px; color: var(--dsw-alias-label-secondary); }',
453
495
  '.lc-detail-head b { color: var(--dsw-alias-label-primary); }',
package/package.json CHANGED
@@ -1,7 +1,16 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
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
+ "author": "bowenliang123",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/bowenliang123/dsh-context.git"
9
+ },
10
+ "homepage": "https://github.com/bowenliang123/dsh-context#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/bowenliang123/dsh-context/issues"
13
+ },
5
14
  "type": "module",
6
15
  "main": "lib/index.js",
7
16
  "exports": {