dsh-xray 0.8.0 → 0.8.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.
Files changed (2) hide show
  1. package/lib/client.js +90 -25
  2. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -47,6 +47,7 @@ window.__ModuleLoader__.load({
47
47
  '.xray-entry-stats{color:var(--dsw-alias-label-tertiary);font-size:12px}',
48
48
  '.xray-entry-close{margin-left:auto;font:inherit;font-size:12px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:2px 10px;cursor:pointer}',
49
49
  '.xray-entry-text{flex:1;min-height:0;overflow:auto;margin:0;padding:12px;background:var(--dsw-alias-bg-layer-3);border-radius:8px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.5;white-space:pre-wrap;word-break:break-word}',
50
+ '.xray-owner-entry{display:inline-block;padding-left:22px}',
50
51
  '.xray-num{text-align:right}',
51
52
  '.xray-warn{color:var(--dsw-alias-state-error-primary,#f85149)}',
52
53
  '.xray-ok{color:var(--dsw-alias-state-success-primary,#3fb950)}',
@@ -72,6 +73,7 @@ window.__ModuleLoader__.load({
72
73
 
73
74
  /** English messages (also the key vocabulary; zh mirrors every key). */
74
75
  const en = {
76
+ 'view.xray': 'X-Ray',
75
77
  'panel.sub': 'composition X-ray — live from this harness',
76
78
  'panel.loading': 'loading {view}…',
77
79
  'panel.renderFailed': 'render failed: {message}',
@@ -152,12 +154,15 @@ window.__ModuleLoader__.load({
152
154
  'entry.stats': '{chars} chars · ~{tokens} tokens ({estimator})',
153
155
  'entry.close': 'close',
154
156
  'tip.clickEntry': 'Click to view the exact text this entry puts into every request',
157
+ 'tip.expandPlugin':
158
+ 'A plugin has no single text — its cost is the sum of the entries it registered; click to unfold them',
155
159
  'tip.contextTokens':
156
160
  'Estimated tokens every request carries before your message — see the cost view for the full breakdown',
157
161
  };
158
162
 
159
163
  /** Simplified Chinese mirror of every en key. */
160
164
  const zh = {
165
+ 'view.xray': 'X 光',
161
166
  'panel.sub': '组合 X 光——实时来自当前 harness',
162
167
  'panel.loading': '正在加载 {view}…',
163
168
  'panel.renderFailed': '渲染失败:{message}',
@@ -229,6 +234,7 @@ window.__ModuleLoader__.load({
229
234
  'entry.stats': '{chars} 字符 · 约 {tokens} tokens({estimator})',
230
235
  'entry.close': '关闭',
231
236
  'tip.clickEntry': '点击查看该条目每次请求实际注入的完整文本',
237
+ 'tip.expandPlugin': '插件本身没有单一文本——它的成本是其注册的全部条目之和;点击展开这些条目',
232
238
  'tip.contextTokens': '每次请求在你的消息之前携带的估算 tokens——完整明细见 cost 视图',
233
239
  };
234
240
 
@@ -290,6 +296,69 @@ window.__ModuleLoader__.load({
290
296
  );
291
297
  }
292
298
 
299
+ /** By-plugin rollup with expandable rows: a plugin has no single text —
300
+ * its cost is the SUM of the entries it registered, so clicking a row
301
+ * unfolds those entries (each linking to its raw text). */
302
+ function OwnersTable({ owners, onInspect, t }) {
303
+ const [open, setOpen] = react.useState(() => new Set());
304
+ const toggle = (plugin) =>
305
+ setOpen((current) => {
306
+ const next = new Set(current);
307
+ if (next.has(plugin)) next.delete(plugin);
308
+ else next.add(plugin);
309
+ return next;
310
+ });
311
+ const rows = [];
312
+ for (const o of owners) {
313
+ const expanded = open.has(o.plugin);
314
+ rows.push(
315
+ Row(o.plugin, [
316
+ h(
317
+ 'button',
318
+ {
319
+ className: 'xray-entry-link',
320
+ title: t('tip.expandPlugin'),
321
+ onClick: () => toggle(o.plugin),
322
+ },
323
+ `${expanded ? '▾' : '▸'} `,
324
+ o.plugin === 'unattributed'
325
+ ? h(
326
+ 'span',
327
+ { className: 'xray-muted', title: t('tip.unattributed') },
328
+ t('cost.unattributed'),
329
+ )
330
+ : o.plugin,
331
+ ),
332
+ { cls: 'xray-num', text: String(o.sections) },
333
+ { cls: 'xray-num', text: String(o.tools) },
334
+ { cls: 'xray-num', text: `~${o.tokens}` },
335
+ { cls: 'xray-num', text: `${o.share}%` },
336
+ h(Bar, { share: o.share }),
337
+ ]),
338
+ );
339
+ if (expanded) {
340
+ for (const entry of o.entries) {
341
+ rows.push(
342
+ Row(`${o.plugin}/${entry.kind}/${entry.name}`, [
343
+ h(
344
+ 'span',
345
+ { className: 'xray-owner-entry' },
346
+ h('span', { className: 'xray-muted' }, `${entry.kind} · `),
347
+ h(EntryLink, { kind: entry.kind, name: entry.name, onInspect, t }),
348
+ ),
349
+ '',
350
+ '',
351
+ { cls: 'xray-num', text: `~${entry.tokens}` },
352
+ '',
353
+ '',
354
+ ]),
355
+ );
356
+ }
357
+ }
358
+ }
359
+ return h(Table, { t, headers: ['plugin', 'sections', 'tools', 'tokens', 'share', ''], rows });
360
+ }
361
+
293
362
  /** Raw-text inspector: fetches one entry's live text on open. Fetched per
294
363
  * view, never cached — the text IS the audit artifact. */
295
364
  function EntryModal({ target, onClose, t }) {
@@ -354,7 +423,7 @@ window.__ModuleLoader__.load({
354
423
 
355
424
  //#region per-view renderers (mirror lib/panel.js, as components; all copy through t)
356
425
  const renderers = {
357
- summary: (d, t) =>
426
+ summary: (d, t, _onInspect, goToView) =>
358
427
  h(
359
428
  react.Fragment,
360
429
  null,
@@ -372,7 +441,15 @@ window.__ModuleLoader__.load({
372
441
  ]),
373
442
  Row('s', [t('row.services'), { cls: 'xray-num', text: String(d.services) }]),
374
443
  Row('t', [
375
- h('span', { title: t('tip.contextTokens') }, t('row.contextTokens')),
444
+ h(
445
+ 'button',
446
+ {
447
+ className: 'xray-entry-link',
448
+ title: t('tip.contextTokens'),
449
+ onClick: () => goToView('cost'),
450
+ },
451
+ t('row.contextTokens'),
452
+ ),
376
453
  { cls: 'xray-num', text: `~${d.toolSchemaTokens}` },
377
454
  ]),
378
455
  ],
@@ -486,26 +563,7 @@ window.__ModuleLoader__.load({
486
563
  react.Fragment,
487
564
  null,
488
565
  h('div', { className: 'xray-h3' }, t('h3.byPlugin')),
489
- h(Table, {
490
- t,
491
- headers: ['plugin', 'sections', 'tools', 'tokens', 'share', ''],
492
- rows: d.owners.map((o) =>
493
- Row(o.plugin, [
494
- o.plugin === 'unattributed'
495
- ? h(
496
- 'span',
497
- { className: 'xray-muted', title: t('tip.unattributed') },
498
- t('cost.unattributed'),
499
- )
500
- : o.plugin,
501
- { cls: 'xray-num', text: String(o.sections) },
502
- { cls: 'xray-num', text: String(o.tools) },
503
- { cls: 'xray-num', text: `~${o.tokens}` },
504
- { cls: 'xray-num', text: `${o.share}%` },
505
- h(Bar, { share: o.share }),
506
- ]),
507
- ),
508
- }),
566
+ h(OwnersTable, { owners: d.owners, onInspect, t }),
509
567
  )
510
568
  : null,
511
569
  d.sections.length
@@ -624,7 +682,7 @@ window.__ModuleLoader__.load({
624
682
  // A diagnostic surface must never take itself down on one bad
625
683
  // payload: render the failure, keep the tab and its nav alive.
626
684
  try {
627
- body = renderers[state.for](state.data, t, setInspecting);
685
+ body = renderers[state.for](state.data, t, setInspecting, setView);
628
686
  } catch (err) {
629
687
  body = h(
630
688
  'p',
@@ -661,12 +719,19 @@ window.__ModuleLoader__.load({
661
719
  //#endregion
662
720
 
663
721
  const inject = ['slots', 'locale'];
664
- /** Mount the X-ray tab into the conversation view ring (beside Chat / Trajectory). */
722
+ /** Mount the X-Ray tab into the conversation view ring (beside Chat / Trajectory). */
665
723
  function apply(ctx) {
666
724
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'xray: dictionaries');
725
+ const t = ctx.locale.bind(NS);
667
726
  ctx.slots.inject('conversation.view', () =>
668
727
  ctx.slots.register(
669
- { name: 'conversation.view', id: 'xray', order: 20, label: 'X-ray', locale: NS },
728
+ {
729
+ name: 'conversation.view',
730
+ id: 'xray',
731
+ order: 20,
732
+ label: () => t('view.xray'),
733
+ locale: NS,
734
+ },
670
735
  XrayPanel,
671
736
  ),
672
737
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-xray",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "X-ray for your DeepSeek Harness — see what's actually loaded, why, and what it costs you.",
5
5
  "repository": {
6
6
  "type": "git",