dsh-xray 0.7.0 → 0.7.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 +29 -11
  2. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -17,19 +17,31 @@ window.__ModuleLoader__.load({
17
17
 
18
18
  //#region styles (host design tokens; auto-claimed by client-modules)
19
19
  const css = [
20
- '.xray-panel{max-width:920px;margin:0 auto;padding:16px 20px;font-size:13px;color:var(--dsw-alias-label-primary)}',
20
+ // The panel owns its scrolling: the tab fills the host view area
21
+ // (flex column, overflow on .xray-body) instead of growing inside the
22
+ // host scrollport. View switches then change only the inner scroll
23
+ // height — the page-level scroll position never jumps, however tall
24
+ // deps/cost render. .xray-stale dims outgoing content while the next
25
+ // payload is in flight.
26
+ '.xray-panel{flex:1;min-height:0;display:flex;flex-direction:column;max-width:920px;width:100%;margin:0 auto;padding:16px 20px;font-size:13px;color:var(--dsw-alias-label-primary)}',
27
+ '.xray-body{flex:1;min-height:0;overflow-y:auto}',
28
+ '.xray-stale{opacity:.45;transition:opacity .15s;pointer-events:none}',
21
29
  '.xray-sub{color:var(--dsw-alias-label-tertiary);margin:0 0 14px;font-size:12px}',
22
30
  '.xray-nav{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px}',
23
31
  '.xray-nav button{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:4px 10px;cursor:pointer}',
24
- '.xray-nav button.active{color:#fff;background:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}',
32
+ '.xray-nav button:hover{background:var(--dsw-alias-interactive-bg-hover)}',
33
+ // active view: the host's own tab-active family (state-business-*) —
34
+ // visible in both themes, unlike brand-primary which resolves to
35
+ // near-white in dark mode (white-on-white active buttons).
36
+ '.xray-nav button.active{color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-tertiary);border-color:var(--dsw-alias-state-business-primary);font-weight:600}',
25
37
  '.xray-table{border-collapse:collapse;width:100%;margin-top:6px}',
26
38
  '.xray-table th,.xray-table td{text-align:left;padding:4px 10px;border-bottom:1px solid var(--dsw-alias-border-l2);vertical-align:top}',
27
39
  '.xray-table th{color:var(--dsw-alias-label-tertiary);font-weight:normal}',
28
40
  '.xray-num{text-align:right}',
29
- '.xray-warn{color:var(--dsw-alias-label-error)}',
30
- '.xray-ok{color:var(--dsw-alias-label-success,#7ee787)}',
41
+ '.xray-warn{color:var(--dsw-alias-state-error-primary,#f85149)}',
42
+ '.xray-ok{color:var(--dsw-alias-state-success-primary,#3fb950)}',
31
43
  '.xray-muted{color:var(--dsw-alias-label-tertiary)}',
32
- '.xray-bar{background:var(--dsw-alias-brand-primary);height:10px;border-radius:2px;display:inline-block}',
44
+ '.xray-bar{background:var(--dsw-alias-state-business-primary);height:10px;border-radius:2px;display:inline-block}',
33
45
  '.xray-h3{font-size:13px;font-weight:600;margin:14px 0 4px}',
34
46
  ].join('\n');
35
47
  if (
@@ -260,11 +272,13 @@ window.__ModuleLoader__.load({
260
272
  // `for` stamps which view the payload belongs to: a click flips `view`
261
273
  // synchronously while `state` still carries the previous view's data —
262
274
  // rendering that mismatch with the new renderer throws and unmounts
263
- // the whole tab. Mismatch renders as loading instead.
275
+ // the whole tab. During the switch the previous view's content stays
276
+ // up (dimmed) and is replaced in one paint when the payload lands, so
277
+ // the layout changes once per click instead of collapsing to a
278
+ // one-line loading row and re-expanding.
264
279
  const [state, setState] = react.useState({ phase: 'loading', for: 'summary' });
265
280
  react.useEffect(() => {
266
281
  let alive = true;
267
- setState({ phase: 'loading', for: view });
268
282
  fetch(`/xray/api/${view}`)
269
283
  .then(async (res) => {
270
284
  if (!res.ok) throw new Error(await res.text());
@@ -285,15 +299,18 @@ window.__ModuleLoader__.load({
285
299
  alive = false;
286
300
  };
287
301
  }, [view]);
302
+ // Render whatever payload we HAVE (state.for), not the view the user
303
+ // just requested — the stale content keeps the scaffold stable while
304
+ // the fresh payload is in flight.
305
+ const settled = state.phase !== 'loading';
288
306
  let body;
289
- if (state.for !== view || state.phase === 'loading')
290
- body = h('p', { className: 'xray-muted' }, `loading ${view}…`);
307
+ if (!settled) body = h('p', { className: 'xray-muted' }, `loading ${view}…`);
291
308
  else if (state.phase === 'error') body = h('p', { className: 'xray-warn' }, state.message);
292
309
  else {
293
310
  // A diagnostic surface must never take itself down on one bad
294
311
  // payload: render the failure, keep the tab and its nav alive.
295
312
  try {
296
- body = renderers[view](state.data);
313
+ body = renderers[state.for](state.data);
297
314
  } catch (err) {
298
315
  body = h(
299
316
  'p',
@@ -302,6 +319,7 @@ window.__ModuleLoader__.load({
302
319
  );
303
320
  }
304
321
  }
322
+ const stale = settled && state.for !== view;
305
323
  return h(
306
324
  'div',
307
325
  { className: 'xray-panel' },
@@ -317,7 +335,7 @@ window.__ModuleLoader__.load({
317
335
  ),
318
336
  ),
319
337
  ),
320
- body,
338
+ h('div', { className: stale ? 'xray-body xray-stale' : 'xray-body' }, body),
321
339
  );
322
340
  }
323
341
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-xray",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
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",