dsh-context 0.53.0 → 0.53.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 (3) hide show
  1. package/lib/client.js +432 -367
  2. package/lib/index.js +2 -1
  3. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -75,6 +75,9 @@ window.__ModuleLoader__.load({
75
75
  "placement.tab": "标签页",
76
76
  "placement.sidebar": "侧边栏",
77
77
  "placement.all": "全部",
78
+ "settings.insightsEntry": "上下文洞察入口",
79
+ "insightsEntry.show": "显示",
80
+ "insightsEntry.hide": "隐藏",
78
81
  "settings.gran": "趋势图 · 默认粒度",
79
82
  "settings.mode": "趋势图 · 默认展示方式",
80
83
  "settings.toolSort": "工具定义 · 默认排序",
@@ -323,17 +326,28 @@ window.__ModuleLoader__.load({
323
326
  "ov.kpi.sessions": "活跃会话",
324
327
  "ov.kpi.ofTotal": "共 {n} 个",
325
328
  "ov.kpi.tokens": "累计消耗",
326
- "ov.kpi.costSub": "models.dev 刊例价估算",
327
- "ov.kpi.cacheSub": "缓存读取 / 计费输入",
329
+ "ov.kpi.sessionsSub": "含 {n} 个会话",
328
330
  "ov.kpi.toolSub": "工具执行 {dur}",
329
331
  "ov.kpi.wallSub": "模型调用 {n}",
330
332
  "ov.heat.title": "活跃度",
331
- "ov.heat.sub": "点击按日期筛选",
333
+ "ov.heat.sub": "按日期筛选",
332
334
  "ov.heat.empty": "暂无活跃数据 — 会话产生模型请求后,这里会显示每日用量",
333
335
  "ov.heat.sessions": "{n} 个活跃会话",
334
336
  "ov.heat.wd.1": "一",
335
337
  "ov.heat.wd.3": "三",
336
338
  "ov.heat.wd.5": "五",
339
+ "ov.heat.mon.01": "1月",
340
+ "ov.heat.mon.02": "2月",
341
+ "ov.heat.mon.03": "3月",
342
+ "ov.heat.mon.04": "4月",
343
+ "ov.heat.mon.05": "5月",
344
+ "ov.heat.mon.06": "6月",
345
+ "ov.heat.mon.07": "7月",
346
+ "ov.heat.mon.08": "8月",
347
+ "ov.heat.mon.09": "9月",
348
+ "ov.heat.mon.10": "10月",
349
+ "ov.heat.mon.11": "11月",
350
+ "ov.heat.mon.12": "12月",
337
351
  "ov.list.title": "会话",
338
352
  "ov.list.search": "搜索标题或目录…",
339
353
  "ov.list.sortLabel": "排序",
@@ -427,6 +441,9 @@ window.__ModuleLoader__.load({
427
441
  "placement.tab": "Tab",
428
442
  "placement.sidebar": "Sidebar",
429
443
  "placement.all": "All",
444
+ "settings.insightsEntry": "Context Insights entry",
445
+ "insightsEntry.show": "Show",
446
+ "insightsEntry.hide": "Hide",
430
447
  "settings.gran": "Trend Chart · Default granularity",
431
448
  "settings.mode": "Trend Chart · Default display",
432
449
  "settings.toolSort": "Tool Schemas · Default sort",
@@ -675,17 +692,28 @@ window.__ModuleLoader__.load({
675
692
  "ov.kpi.sessions": "Active Sessions",
676
693
  "ov.kpi.ofTotal": "of {n} total",
677
694
  "ov.kpi.tokens": "Tokens Used",
678
- "ov.kpi.costSub": "estimated at models.dev list prices",
679
- "ov.kpi.cacheSub": "cache reads of billed input",
695
+ "ov.kpi.sessionsSub": "across {n} sessions",
680
696
  "ov.kpi.toolSub": "tool runs {dur}",
681
697
  "ov.kpi.wallSub": "{n} model calls",
682
698
  "ov.heat.title": "Activity",
683
- "ov.heat.sub": "Click to filter by day",
699
+ "ov.heat.sub": "Filter by date",
684
700
  "ov.heat.empty": "No activity yet — daily usage appears once sessions make model requests",
685
701
  "ov.heat.sessions": "{n} active sessions",
686
702
  "ov.heat.wd.1": "M",
687
703
  "ov.heat.wd.3": "W",
688
704
  "ov.heat.wd.5": "F",
705
+ "ov.heat.mon.01": "Jan",
706
+ "ov.heat.mon.02": "Feb",
707
+ "ov.heat.mon.03": "Mar",
708
+ "ov.heat.mon.04": "Apr",
709
+ "ov.heat.mon.05": "May",
710
+ "ov.heat.mon.06": "Jun",
711
+ "ov.heat.mon.07": "Jul",
712
+ "ov.heat.mon.08": "Aug",
713
+ "ov.heat.mon.09": "Sep",
714
+ "ov.heat.mon.10": "Oct",
715
+ "ov.heat.mon.11": "Nov",
716
+ "ov.heat.mon.12": "Dec",
689
717
  "ov.list.title": "Sessions",
690
718
  "ov.list.search": "Search title or path…",
691
719
  "ov.list.sortLabel": "Sort",
@@ -3372,11 +3400,12 @@ window.__ModuleLoader__.load({
3372
3400
  }
3373
3401
  //#endregion
3374
3402
  //#region src/client/format.ts
3375
- /** `fmt`: the k/M suffix style shared by bars/details/stats; `fmtTime`: local HH:MM:SS. */
3403
+ /** `fmt`: the k/M/B suffix style shared by bars/details/stats; `fmtTime`: local HH:MM:SS. */
3376
3404
  function fmt(n) {
3377
3405
  if (n === void 0 || n === null || isNaN(n)) return "—";
3378
3406
  const sign = n < 0 ? "-" : "";
3379
3407
  const a = Math.abs(n);
3408
+ if (a >= 1e9) return sign + (a / 1e9).toFixed(1) + "B";
3380
3409
  if (a >= 1e6) return sign + (a / 1e6).toFixed(1) + "M";
3381
3410
  if (a >= 1e3) return sign + (a / 1e3).toFixed(1) + "k";
3382
3411
  return sign + String(Math.round(a));
@@ -5265,77 +5294,67 @@ window.__ModuleLoader__.load({
5265
5294
  };
5266
5295
  }
5267
5296
  //#endregion
5268
- //#region node_modules/.pnpm/@deepseek-ai+dsh-util-workspace-path@0.1.5-rc.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-util-workspace-path/lib/index.js
5269
- /**
5270
- * The `dsh-resource://file/…` address grammar: how a file is named across the
5271
- * Sidebar and the resource model, built and parsed without touching a
5272
- * filesystem.
5273
- * @module
5274
- */
5275
- /** The scheme and type every file address opens with. */
5276
- const FILE_ADDRESS_PREFIX = "dsh-resource://file/";
5277
- /** Component-encode one id or path segment, keeping `:` literal for drive letters. */
5278
- function encodeSegment(segment) {
5279
- return encodeURIComponent(segment).replace(/%3A/gi, ":");
5280
- }
5281
- /** Encode a `/`-separated path segment by segment. */
5282
- function encodePath(path) {
5283
- return path.split("/").map(encodeSegment).join("/");
5284
- }
5285
- /**
5286
- * Build the address of a file read through one Session.
5287
- * @param sessionId - the Session whose Host workspace resolves the path.
5288
- * @param path - absolute or workspace-relative path; backslashes are normalized to `/`, and leading `./` prefixes are dropped.
5289
- * @returns the `dsh-resource://file/session/<sessionId>/<path>` address.
5290
- */
5291
- function sessionFileAddress(sessionId, path) {
5292
- const normalized = path.replace(/\\/g, "/").replace(/^(?:\.\/)+/, "");
5293
- return `${FILE_ADDRESS_PREFIX}session/${encodeSegment(sessionId)}/${encodePath(normalized)}`;
5294
- }
5295
- /**
5296
- * Browser-safe Workspace path and display helpers.
5297
- * @module @deepseek-ai/dsh-util-workspace-path
5298
- */
5299
- /** Whether a path uses a Windows drive or UNC prefix. */
5300
- function isWindowsStylePath(value) {
5301
- return /^[A-Za-z]:[/\\]/.test(value) || value.startsWith("\\\\");
5302
- }
5303
- /**
5304
- * Whether a path is absolute in either spelling the Host accepts: POSIX (`/a/b`) or Windows drive or UNC.
5305
- * @param path - the path to classify.
5306
- * @returns `true` for an absolute path; `false` for a Workspace-relative one.
5307
- */
5308
- function isAbsoluteWorkspacePath(path) {
5309
- return path.startsWith("/") || isWindowsStylePath(path);
5310
- }
5311
- /**
5312
- * Read the final non-empty segment of a Workspace path for display.
5313
- * Workspace-label surfaces use this helper instead of deriving another basename.
5314
- * @param path - Workspace directory path using POSIX or Windows separators.
5315
- * @returns the final segment, or an empty string for a separator-only path.
5316
- */
5317
- function workspaceTitleOf(path) {
5318
- const trimmed = path.replace(/[/\\]+$/, "");
5319
- const separator = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
5320
- return trimmed.slice(separator + 1);
5297
+ //#region src/client/overviewStore.ts
5298
+ function createStore() {
5299
+ let open = false;
5300
+ const listeners = /* @__PURE__ */ new Set();
5301
+ return {
5302
+ subscribe: (listener) => {
5303
+ listeners.add(listener);
5304
+ return () => {
5305
+ listeners.delete(listener);
5306
+ };
5307
+ },
5308
+ getSnapshot: () => open,
5309
+ set: (next) => {
5310
+ if (next === open) return;
5311
+ open = next;
5312
+ for (const listener of listeners) listener();
5313
+ }
5314
+ };
5321
5315
  }
5316
+ const overviewStore = createStore();
5317
+ //#endregion
5318
+ //#region src/client/components/overviewButton.tsx
5322
5319
  /**
5323
- * The address for a path as a caller holds it: a relative path, or an absolute
5324
- * path inside the Session's workspace, becomes a `session`-scoped address; an
5325
- * absolute path outside it, or one whose workspace root is unknown, keeps its
5326
- * absolute path in that Session's address.
5327
- * @param sessionId - the Session the path is read in.
5328
- * @param cwd - that Session's workspace root, when known.
5329
- * @param path - absolute or workspace-relative path, in either separator spelling.
5330
- * @returns the `dsh-resource://file/…` address.
5331
- */
5332
- function fileAddressFor(sessionId, cwd, path) {
5333
- const normalized = path.replace(/\\/g, "/");
5334
- if (!isAbsoluteWorkspacePath(normalized)) return sessionFileAddress(sessionId, normalized);
5335
- const root = cwd === void 0 ? "" : cwd.replace(/\\/g, "/").replace(/\/+$/, "");
5336
- if (root !== "" && normalized === root) return sessionFileAddress(sessionId, "");
5337
- if (root !== "" && normalized.startsWith(`${root}/`)) return sessionFileAddress(sessionId, normalized.slice(root.length + 1));
5338
- return sessionFileAddress(sessionId, normalized);
5320
+ * The Context Dashboard's sidebar entry: a footer action stacked directly
5321
+ * above Settings (the harness's own foot layout: footer actions, then the
5322
+ * settings row), mirroring the Settings trigger's geometry in both column
5323
+ * widths. The button carries the plugin emblem and, on the wide
5324
+ * column, its label. Clicking opens the overview overlay through the shared
5325
+ * module store (overviewStore.ts).
5326
+ *
5327
+ * The per-user `insightsEntry` preference takes the entry down (renders
5328
+ * null) without unregistering the seat. The subscription fails open: an
5329
+ * absent settings store, an unserved namespace, or a value the plugin
5330
+ * cannot understand all leave the entry visible.
5331
+ */
5332
+ /** Stable subscription faces for the settings-less degrade (no re-subscribes). */
5333
+ const subscribeNever = () => () => {};
5334
+ const entryShown = () => "show";
5335
+ function makeOverviewButton(kit, settings) {
5336
+ const { t } = kit;
5337
+ const subscribeEntry = settings === void 0 ? subscribeNever : (listener) => settings.store.subscribe(listener);
5338
+ const getEntry = settings === void 0 ? entryShown : () => settings.insightsEntry();
5339
+ return function OverviewButton(props) {
5340
+ if ((0, react.useSyncExternalStore)(subscribeEntry, getEntry) === "hide") return null;
5341
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
5342
+ type: "button",
5343
+ className: props.wide === true ? "lc-ov-entry" : "lc-ov-entry lc-ov-entry-rail",
5344
+ title: t("ov.entry"),
5345
+ "aria-label": t("ov.entry"),
5346
+ onClick: () => {
5347
+ overviewStore.set(true);
5348
+ },
5349
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextIcon, {
5350
+ size: props.wide === true ? 16 : 18,
5351
+ className: "lc-ov-entry-icon"
5352
+ }), props.wide === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5353
+ className: "lc-ov-entry-label",
5354
+ children: t("ov.entry")
5355
+ })]
5356
+ });
5357
+ };
5339
5358
  }
5340
5359
  //#endregion
5341
5360
  //#region src/shared/providers.ts
@@ -5515,6 +5534,258 @@ window.__ModuleLoader__.load({
5515
5534
  return formatCost(amount, currency).replace(/0+$/, "").replace(/\.$/, "");
5516
5535
  }
5517
5536
  //#endregion
5537
+ //#region node_modules/.pnpm/@opencode-ai+models@0.0.72/node_modules/@opencode-ai/models/dist/error.js
5538
+ /**
5539
+ * The only error thrown by the models.dev client.
5540
+ *
5541
+ * - `Transport` — the fetch itself failed (network, DNS, abort). `cause` is the underlying error.
5542
+ * - `UnexpectedStatus` — non-2xx response. `cause` is `{ status: number }`.
5543
+ * - `MalformedResponse` — the body was empty or not valid JSON. `cause` is the parse error, if any.
5544
+ */
5545
+ var ModelsDevError = class extends Error {
5546
+ reason;
5547
+ name = "ModelsDevError";
5548
+ constructor(reason, options) {
5549
+ super(reason, options);
5550
+ this.reason = reason;
5551
+ }
5552
+ };
5553
+ //#endregion
5554
+ //#region node_modules/.pnpm/@opencode-ai+models@0.0.72/node_modules/@opencode-ai/models/dist/client.js
5555
+ /**
5556
+ * Creates a stateless models.dev client. Every method performs exactly one
5557
+ * `GET` and nothing is ever cached — callers who want caching should wrap
5558
+ * calls with their own policy. For a no-network alternative, see the
5559
+ * `@opencode-ai/models/snapshot` entrypoint.
5560
+ */
5561
+ function make(options = {}) {
5562
+ const baseUrl = options.baseUrl ?? "https://models.dev";
5563
+ const base = baseUrl.endsWith("/") ? baseUrl : baseUrl + "/";
5564
+ const request = async (path, requestOptions) => {
5565
+ const fetch = options.fetch ?? globalThis.fetch;
5566
+ const headers = new Headers();
5567
+ for (const [key, value] of new Headers(options.headers)) headers.set(key, value);
5568
+ for (const [key, value] of new Headers(requestOptions?.headers)) headers.set(key, value);
5569
+ let response;
5570
+ try {
5571
+ response = await fetch(new URL(path, base), {
5572
+ method: "GET",
5573
+ headers,
5574
+ signal: requestOptions?.signal
5575
+ });
5576
+ } catch (cause) {
5577
+ throw new ModelsDevError("Transport", { cause });
5578
+ }
5579
+ if (!response.ok) {
5580
+ try {
5581
+ await response.body?.cancel();
5582
+ } catch {}
5583
+ throw new ModelsDevError("UnexpectedStatus", { cause: { status: response.status } });
5584
+ }
5585
+ let text;
5586
+ try {
5587
+ text = await response.text();
5588
+ } catch (cause) {
5589
+ throw new ModelsDevError("Transport", { cause });
5590
+ }
5591
+ if (text === "") throw new ModelsDevError("MalformedResponse");
5592
+ try {
5593
+ return JSON.parse(text);
5594
+ } catch (cause) {
5595
+ throw new ModelsDevError("MalformedResponse", { cause });
5596
+ }
5597
+ };
5598
+ return {
5599
+ /** All providers with their models, pricing, and limits (`/api.json`). */
5600
+ providers: (requestOptions) => request("api.json", requestOptions),
5601
+ /** Provider-agnostic model metadata (`/models.json`). */
5602
+ models: (requestOptions) => request("models.json", requestOptions),
5603
+ /** Providers and model metadata in a single request (`/catalog.json`). */
5604
+ catalog: (requestOptions) => request("catalog.json", requestOptions)
5605
+ };
5606
+ }
5607
+ //#endregion
5608
+ //#region src/client/modelPrices.ts
5609
+ /**
5610
+ * The client's model-price book (client/cost.ts prices from it): the
5611
+ * models.dev registry, fetched through the official @opencode-ai/models
5612
+ * SDK. The registry payload is untrusted wire input, so `pricesBookOf`
5613
+ * re-proves every field at the boundary — a non-conforming provider/model/
5614
+ * cost entry drops whole. The book keeps EVERY provider that prices (keyed
5615
+ * by the registry's own provider id), so a dsh provider id outside the
5616
+ * rename table still prices by direct passthrough. One fetch per page load,
5617
+ * kicked on first subscribe; a failure degrades to a visible state the cost
5618
+ * cell notes, with a backed-off automatic retry — never a spinner, and
5619
+ * never an unhandled rejection.
5620
+ */
5621
+ /** One finite non-negative registry figure, or null. */
5622
+ function rateOf(value) {
5623
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
5624
+ }
5625
+ /**
5626
+ * Extract the whole registry's per-model USD rates from a delivered
5627
+ * providers payload (`/api.json`), keyed by the registry's provider ids.
5628
+ * Any shape failure skips just that entry; a payload that is not a record
5629
+ * at all returns null (the store treats it as a failed fetch and retries).
5630
+ */
5631
+ function pricesBookOf(value) {
5632
+ const data = asRecord(value);
5633
+ if (data === null || Array.isArray(data)) return null;
5634
+ const book = {};
5635
+ for (const providerId of Object.keys(data)) {
5636
+ const provider = asRecord(data[providerId]);
5637
+ const modelList = provider !== null ? asRecord(provider.models) : null;
5638
+ if (modelList === null) continue;
5639
+ const models = {};
5640
+ for (const id of Object.keys(modelList)) {
5641
+ const model = asRecord(modelList[id]);
5642
+ const cost = model !== null ? asRecord(model.cost) : null;
5643
+ if (cost === null) continue;
5644
+ const miss = rateOf(cost.input);
5645
+ const out = rateOf(cost.output);
5646
+ if (miss === null || out === null) continue;
5647
+ models[id] = {
5648
+ hit: rateOf(cost.cache_read) ?? miss,
5649
+ miss,
5650
+ write: rateOf(cost.cache_write) ?? miss,
5651
+ out
5652
+ };
5653
+ }
5654
+ if (Object.keys(models).length > 0) book[providerId] = models;
5655
+ }
5656
+ return book;
5657
+ }
5658
+ const defaultLoader = () => make().providers();
5659
+ /** The retry backoff base; each consecutive failure doubles the wait, capped at 3 doublings. */
5660
+ const RETRY_BASE_MS = 3e4;
5661
+ let loader = defaultLoader;
5662
+ let snap = {
5663
+ prices: null,
5664
+ failed: false
5665
+ };
5666
+ let inFlight = false;
5667
+ let failures = 0;
5668
+ let timer = null;
5669
+ const listeners = /* @__PURE__ */ new Set();
5670
+ function fail() {
5671
+ snap = {
5672
+ ...snap,
5673
+ failed: true
5674
+ };
5675
+ failures++;
5676
+ timer = setTimeout(() => {
5677
+ timer = null;
5678
+ fire();
5679
+ }, RETRY_BASE_MS * 2 ** Math.min(failures - 1, 3));
5680
+ }
5681
+ async function fire() {
5682
+ inFlight = true;
5683
+ try {
5684
+ const book = pricesBookOf(await loader());
5685
+ if (book !== null) {
5686
+ snap = {
5687
+ prices: book,
5688
+ failed: false
5689
+ };
5690
+ failures = 0;
5691
+ } else fail();
5692
+ } catch {
5693
+ fail();
5694
+ }
5695
+ inFlight = false;
5696
+ for (const fn of listeners) fn();
5697
+ }
5698
+ function kick() {
5699
+ if (snap.prices !== null || inFlight || timer !== null) return;
5700
+ fire();
5701
+ }
5702
+ /** The useSyncExternalStore seam: subscribing also kicks the first fetch. */
5703
+ const subscribeModelPrices = (fn) => {
5704
+ listeners.add(fn);
5705
+ kick();
5706
+ return () => {
5707
+ listeners.delete(fn);
5708
+ };
5709
+ };
5710
+ const getModelPricesSnap = () => snap;
5711
+ /** The stats board's read of the price book (null until the fetch lands). */
5712
+ function useModelPrices() {
5713
+ return (0, react.useSyncExternalStore)(subscribeModelPrices, getModelPricesSnap);
5714
+ }
5715
+ //#endregion
5716
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-util-workspace-path@0.1.5-rc.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-util-workspace-path/lib/index.js
5717
+ /**
5718
+ * The `dsh-resource://file/…` address grammar: how a file is named across the
5719
+ * Sidebar and the resource model, built and parsed without touching a
5720
+ * filesystem.
5721
+ * @module
5722
+ */
5723
+ /** The scheme and type every file address opens with. */
5724
+ const FILE_ADDRESS_PREFIX = "dsh-resource://file/";
5725
+ /** Component-encode one id or path segment, keeping `:` literal for drive letters. */
5726
+ function encodeSegment(segment) {
5727
+ return encodeURIComponent(segment).replace(/%3A/gi, ":");
5728
+ }
5729
+ /** Encode a `/`-separated path segment by segment. */
5730
+ function encodePath(path) {
5731
+ return path.split("/").map(encodeSegment).join("/");
5732
+ }
5733
+ /**
5734
+ * Build the address of a file read through one Session.
5735
+ * @param sessionId - the Session whose Host workspace resolves the path.
5736
+ * @param path - absolute or workspace-relative path; backslashes are normalized to `/`, and leading `./` prefixes are dropped.
5737
+ * @returns the `dsh-resource://file/session/<sessionId>/<path>` address.
5738
+ */
5739
+ function sessionFileAddress(sessionId, path) {
5740
+ const normalized = path.replace(/\\/g, "/").replace(/^(?:\.\/)+/, "");
5741
+ return `${FILE_ADDRESS_PREFIX}session/${encodeSegment(sessionId)}/${encodePath(normalized)}`;
5742
+ }
5743
+ /**
5744
+ * Browser-safe Workspace path and display helpers.
5745
+ * @module @deepseek-ai/dsh-util-workspace-path
5746
+ */
5747
+ /** Whether a path uses a Windows drive or UNC prefix. */
5748
+ function isWindowsStylePath(value) {
5749
+ return /^[A-Za-z]:[/\\]/.test(value) || value.startsWith("\\\\");
5750
+ }
5751
+ /**
5752
+ * Whether a path is absolute in either spelling the Host accepts: POSIX (`/a/b`) or Windows drive or UNC.
5753
+ * @param path - the path to classify.
5754
+ * @returns `true` for an absolute path; `false` for a Workspace-relative one.
5755
+ */
5756
+ function isAbsoluteWorkspacePath(path) {
5757
+ return path.startsWith("/") || isWindowsStylePath(path);
5758
+ }
5759
+ /**
5760
+ * Read the final non-empty segment of a Workspace path for display.
5761
+ * Workspace-label surfaces use this helper instead of deriving another basename.
5762
+ * @param path - Workspace directory path using POSIX or Windows separators.
5763
+ * @returns the final segment, or an empty string for a separator-only path.
5764
+ */
5765
+ function workspaceTitleOf(path) {
5766
+ const trimmed = path.replace(/[/\\]+$/, "");
5767
+ const separator = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
5768
+ return trimmed.slice(separator + 1);
5769
+ }
5770
+ /**
5771
+ * The address for a path as a caller holds it: a relative path, or an absolute
5772
+ * path inside the Session's workspace, becomes a `session`-scoped address; an
5773
+ * absolute path outside it, or one whose workspace root is unknown, keeps its
5774
+ * absolute path in that Session's address.
5775
+ * @param sessionId - the Session the path is read in.
5776
+ * @param cwd - that Session's workspace root, when known.
5777
+ * @param path - absolute or workspace-relative path, in either separator spelling.
5778
+ * @returns the `dsh-resource://file/…` address.
5779
+ */
5780
+ function fileAddressFor(sessionId, cwd, path) {
5781
+ const normalized = path.replace(/\\/g, "/");
5782
+ if (!isAbsoluteWorkspacePath(normalized)) return sessionFileAddress(sessionId, normalized);
5783
+ const root = cwd === void 0 ? "" : cwd.replace(/\\/g, "/").replace(/\/+$/, "");
5784
+ if (root !== "" && normalized === root) return sessionFileAddress(sessionId, "");
5785
+ if (root !== "" && normalized.startsWith(`${root}/`)) return sessionFileAddress(sessionId, normalized.slice(root.length + 1));
5786
+ return sessionFileAddress(sessionId, normalized);
5787
+ }
5788
+ //#endregion
5518
5789
  //#region src/client/overview.ts
5519
5790
  /**
5520
5791
  * The Context Dashboard's data layer: everything the panel renders is derived
@@ -5548,21 +5819,6 @@ window.__ModuleLoader__.load({
5548
5819
  return null;
5549
5820
  }
5550
5821
  }
5551
- /** The running-session tally for the footer entry's badge (0 on any shape failure). */
5552
- function runningCountOf(snapshot) {
5553
- const state = asRecord(snapshot);
5554
- if (state === null) return 0;
5555
- const byId = asRecord(state.byId);
5556
- if (byId === null) return 0;
5557
- let count = 0;
5558
- try {
5559
- for (const id of Object.keys(byId)) {
5560
- const row = asRecord(byId[id]);
5561
- if (row !== null && row.running === true) count++;
5562
- }
5563
- } catch {}
5564
- return count;
5565
- }
5566
5822
  /**
5567
5823
  * The `useWorkspaces` standard prop read — same guarded-hook contract as
5568
5824
  * {@link sessionsSnapshotOf}. The overview joins its session → workspace
@@ -5829,7 +6085,12 @@ window.__ModuleLoader__.load({
5829
6085
  let toolsMs = 0;
5830
6086
  let calls = 0;
5831
6087
  let wallMs = 0;
6088
+ let costSessions = 0;
6089
+ let usageSessions = 0;
5832
6090
  for (const row of rows) {
6091
+ const cost = row.timeline?.cost;
6092
+ if (estimateSessionCost(cost, prices, currency) !== null) costSessions++;
6093
+ if (usageTotalsOf(cost) !== null) usageSessions++;
5833
6094
  turns += turnsOf(row.timeline);
5834
6095
  const timing = row.timeline?.timing;
5835
6096
  toolCalls += timing?.toolCalls ?? 0;
@@ -5843,7 +6104,9 @@ window.__ModuleLoader__.load({
5843
6104
  tokens: totals?.total ?? 0,
5844
6105
  turns,
5845
6106
  cost: estimateSessionCost(usage, prices, currency),
6107
+ costSessions,
5846
6108
  cacheHit: totals === null ? null : cacheHitPercent(totals.cacheRead, totals.input + totals.cacheRead + totals.cacheWrite),
6109
+ usageSessions,
5847
6110
  toolCalls,
5848
6111
  toolsMs,
5849
6112
  calls,
@@ -5920,238 +6183,6 @@ window.__ModuleLoader__.load({
5920
6183
  return t("ov.time.d", { n: Math.floor(hours / 24) });
5921
6184
  }
5922
6185
  //#endregion
5923
- //#region src/client/overviewStore.ts
5924
- function createStore() {
5925
- let open = false;
5926
- const listeners = /* @__PURE__ */ new Set();
5927
- return {
5928
- subscribe: (listener) => {
5929
- listeners.add(listener);
5930
- return () => {
5931
- listeners.delete(listener);
5932
- };
5933
- },
5934
- getSnapshot: () => open,
5935
- set: (next) => {
5936
- if (next === open) return;
5937
- open = next;
5938
- for (const listener of listeners) listener();
5939
- }
5940
- };
5941
- }
5942
- const overviewStore = createStore();
5943
- //#endregion
5944
- //#region src/client/components/overviewButton.tsx
5945
- function makeOverviewButton(kit) {
5946
- const { t } = kit;
5947
- return function OverviewButton(props) {
5948
- const running = runningCountOf(sessionsSnapshotOf(props));
5949
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
5950
- type: "button",
5951
- className: props.wide === true ? "lc-ov-entry" : "lc-ov-entry lc-ov-entry-rail",
5952
- title: t("ov.entry"),
5953
- "aria-label": t("ov.entry"),
5954
- onClick: () => {
5955
- overviewStore.set(true);
5956
- },
5957
- children: [
5958
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextIcon, {
5959
- size: props.wide === true ? 16 : 18,
5960
- className: "lc-ov-entry-icon"
5961
- }),
5962
- props.wide === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5963
- className: "lc-ov-entry-label",
5964
- children: t("ov.entry")
5965
- }),
5966
- running > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5967
- className: "lc-ov-badge",
5968
- "aria-hidden": "true",
5969
- children: running
5970
- })
5971
- ]
5972
- });
5973
- };
5974
- }
5975
- //#endregion
5976
- //#region node_modules/.pnpm/@opencode-ai+models@0.0.72/node_modules/@opencode-ai/models/dist/error.js
5977
- /**
5978
- * The only error thrown by the models.dev client.
5979
- *
5980
- * - `Transport` — the fetch itself failed (network, DNS, abort). `cause` is the underlying error.
5981
- * - `UnexpectedStatus` — non-2xx response. `cause` is `{ status: number }`.
5982
- * - `MalformedResponse` — the body was empty or not valid JSON. `cause` is the parse error, if any.
5983
- */
5984
- var ModelsDevError = class extends Error {
5985
- reason;
5986
- name = "ModelsDevError";
5987
- constructor(reason, options) {
5988
- super(reason, options);
5989
- this.reason = reason;
5990
- }
5991
- };
5992
- //#endregion
5993
- //#region node_modules/.pnpm/@opencode-ai+models@0.0.72/node_modules/@opencode-ai/models/dist/client.js
5994
- /**
5995
- * Creates a stateless models.dev client. Every method performs exactly one
5996
- * `GET` and nothing is ever cached — callers who want caching should wrap
5997
- * calls with their own policy. For a no-network alternative, see the
5998
- * `@opencode-ai/models/snapshot` entrypoint.
5999
- */
6000
- function make(options = {}) {
6001
- const baseUrl = options.baseUrl ?? "https://models.dev";
6002
- const base = baseUrl.endsWith("/") ? baseUrl : baseUrl + "/";
6003
- const request = async (path, requestOptions) => {
6004
- const fetch = options.fetch ?? globalThis.fetch;
6005
- const headers = new Headers();
6006
- for (const [key, value] of new Headers(options.headers)) headers.set(key, value);
6007
- for (const [key, value] of new Headers(requestOptions?.headers)) headers.set(key, value);
6008
- let response;
6009
- try {
6010
- response = await fetch(new URL(path, base), {
6011
- method: "GET",
6012
- headers,
6013
- signal: requestOptions?.signal
6014
- });
6015
- } catch (cause) {
6016
- throw new ModelsDevError("Transport", { cause });
6017
- }
6018
- if (!response.ok) {
6019
- try {
6020
- await response.body?.cancel();
6021
- } catch {}
6022
- throw new ModelsDevError("UnexpectedStatus", { cause: { status: response.status } });
6023
- }
6024
- let text;
6025
- try {
6026
- text = await response.text();
6027
- } catch (cause) {
6028
- throw new ModelsDevError("Transport", { cause });
6029
- }
6030
- if (text === "") throw new ModelsDevError("MalformedResponse");
6031
- try {
6032
- return JSON.parse(text);
6033
- } catch (cause) {
6034
- throw new ModelsDevError("MalformedResponse", { cause });
6035
- }
6036
- };
6037
- return {
6038
- /** All providers with their models, pricing, and limits (`/api.json`). */
6039
- providers: (requestOptions) => request("api.json", requestOptions),
6040
- /** Provider-agnostic model metadata (`/models.json`). */
6041
- models: (requestOptions) => request("models.json", requestOptions),
6042
- /** Providers and model metadata in a single request (`/catalog.json`). */
6043
- catalog: (requestOptions) => request("catalog.json", requestOptions)
6044
- };
6045
- }
6046
- //#endregion
6047
- //#region src/client/modelPrices.ts
6048
- /**
6049
- * The client's model-price book (client/cost.ts prices from it): the
6050
- * models.dev registry, fetched through the official @opencode-ai/models
6051
- * SDK. The registry payload is untrusted wire input, so `pricesBookOf`
6052
- * re-proves every field at the boundary — a non-conforming provider/model/
6053
- * cost entry drops whole. The book keeps EVERY provider that prices (keyed
6054
- * by the registry's own provider id), so a dsh provider id outside the
6055
- * rename table still prices by direct passthrough. One fetch per page load,
6056
- * kicked on first subscribe; a failure degrades to a visible state the cost
6057
- * cell notes, with a backed-off automatic retry — never a spinner, and
6058
- * never an unhandled rejection.
6059
- */
6060
- /** One finite non-negative registry figure, or null. */
6061
- function rateOf(value) {
6062
- return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
6063
- }
6064
- /**
6065
- * Extract the whole registry's per-model USD rates from a delivered
6066
- * providers payload (`/api.json`), keyed by the registry's provider ids.
6067
- * Any shape failure skips just that entry; a payload that is not a record
6068
- * at all returns null (the store treats it as a failed fetch and retries).
6069
- */
6070
- function pricesBookOf(value) {
6071
- const data = asRecord(value);
6072
- if (data === null || Array.isArray(data)) return null;
6073
- const book = {};
6074
- for (const providerId of Object.keys(data)) {
6075
- const provider = asRecord(data[providerId]);
6076
- const modelList = provider !== null ? asRecord(provider.models) : null;
6077
- if (modelList === null) continue;
6078
- const models = {};
6079
- for (const id of Object.keys(modelList)) {
6080
- const model = asRecord(modelList[id]);
6081
- const cost = model !== null ? asRecord(model.cost) : null;
6082
- if (cost === null) continue;
6083
- const miss = rateOf(cost.input);
6084
- const out = rateOf(cost.output);
6085
- if (miss === null || out === null) continue;
6086
- models[id] = {
6087
- hit: rateOf(cost.cache_read) ?? miss,
6088
- miss,
6089
- write: rateOf(cost.cache_write) ?? miss,
6090
- out
6091
- };
6092
- }
6093
- if (Object.keys(models).length > 0) book[providerId] = models;
6094
- }
6095
- return book;
6096
- }
6097
- const defaultLoader = () => make().providers();
6098
- /** The retry backoff base; each consecutive failure doubles the wait, capped at 3 doublings. */
6099
- const RETRY_BASE_MS = 3e4;
6100
- let loader = defaultLoader;
6101
- let snap = {
6102
- prices: null,
6103
- failed: false
6104
- };
6105
- let inFlight = false;
6106
- let failures = 0;
6107
- let timer = null;
6108
- const listeners = /* @__PURE__ */ new Set();
6109
- function fail() {
6110
- snap = {
6111
- ...snap,
6112
- failed: true
6113
- };
6114
- failures++;
6115
- timer = setTimeout(() => {
6116
- timer = null;
6117
- fire();
6118
- }, RETRY_BASE_MS * 2 ** Math.min(failures - 1, 3));
6119
- }
6120
- async function fire() {
6121
- inFlight = true;
6122
- try {
6123
- const book = pricesBookOf(await loader());
6124
- if (book !== null) {
6125
- snap = {
6126
- prices: book,
6127
- failed: false
6128
- };
6129
- failures = 0;
6130
- } else fail();
6131
- } catch {
6132
- fail();
6133
- }
6134
- inFlight = false;
6135
- for (const fn of listeners) fn();
6136
- }
6137
- function kick() {
6138
- if (snap.prices !== null || inFlight || timer !== null) return;
6139
- fire();
6140
- }
6141
- /** The useSyncExternalStore seam: subscribing also kicks the first fetch. */
6142
- const subscribeModelPrices = (fn) => {
6143
- listeners.add(fn);
6144
- kick();
6145
- return () => {
6146
- listeners.delete(fn);
6147
- };
6148
- };
6149
- const getModelPricesSnap = () => snap;
6150
- /** The stats board's read of the price book (null until the fetch lands). */
6151
- function useModelPrices() {
6152
- return (0, react.useSyncExternalStore)(subscribeModelPrices, getModelPricesSnap);
6153
- }
6154
- //#endregion
6155
6186
  //#region src/shared/days.ts
6156
6187
  /**
6157
6188
  * The local-day key (`YYYY-MM-DD`) shared by the activity fold (host) and the
@@ -6210,7 +6241,8 @@ window.__ModuleLoader__.load({
6210
6241
  //#region src/client/components/heatmap.tsx
6211
6242
  /**
6212
6243
  * The Context Dashboard's activity heatmap: a GitHub-style contribution grid
6213
- * (weeks as columns, Monday-first weekdays as rows) over the merged daily
6244
+ * (weeks as columns, Monday-first weekdays as rows, a month label over the
6245
+ * column where each month begins) over the merged daily
6214
6246
  * ledger (overview.ts). Cell depth is the day's billed-token share of the
6215
6247
  * window's maximum, in four steps; a day with data is a button whose click
6216
6248
  * pins the session list to that day (click again to release). Cells tip
@@ -6309,36 +6341,43 @@ window.__ModuleLoader__.load({
6309
6341
  }, row))
6310
6342
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6311
6343
  className: "lc-heat-cols",
6312
- children: columns.map((column, wi) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6313
- className: "lc-heat-col",
6314
- children: column.map((cell) => {
6315
- if (cell.future) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6316
- className: "lc-heat-cell lc-heat-future",
6317
- "aria-hidden": "true"
6318
- }, cell.key);
6319
- const level = cell.entry === void 0 ? 0 : levelOf(cell.entry.tokens, max);
6320
- if (cell.entry === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
6321
- label: cell.key,
6322
- side: "top",
6323
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "lc-heat-cell lc-heat-0" })
6324
- }, cell.key);
6325
- const picked = props.selected === cell.key;
6326
- const label = `${cell.key}\n${t("ov.heat.sessions", { n: cell.entry.sessions })}`;
6327
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
6328
- label,
6329
- side: "top",
6330
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6331
- type: "button",
6332
- className: `lc-heat-cell lc-heat-${String(level)}${picked ? " lc-heat-on" : ""}`,
6333
- "aria-label": label,
6334
- "aria-pressed": picked,
6335
- onClick: () => {
6336
- if (props.onSelect !== void 0) props.onSelect(picked ? null : cell.key);
6337
- }
6338
- })
6339
- }, cell.key);
6340
- })
6341
- }, wi))
6344
+ children: columns.map((column, wi) => {
6345
+ const opens = column[0].key.slice(0, 7) !== column[6].key.slice(0, 7) || column[0].key.slice(8, 10) === "01";
6346
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6347
+ className: "lc-heat-col",
6348
+ children: [opens && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6349
+ className: "lc-heat-mon",
6350
+ "aria-hidden": "true",
6351
+ children: t("ov.heat.mon." + column[6].key.slice(5, 7))
6352
+ }), column.map((cell) => {
6353
+ if (cell.future) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6354
+ className: "lc-heat-cell lc-heat-future",
6355
+ "aria-hidden": "true"
6356
+ }, cell.key);
6357
+ const level = cell.entry === void 0 ? 0 : levelOf(cell.entry.tokens, max);
6358
+ if (cell.entry === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
6359
+ label: cell.key,
6360
+ side: "top",
6361
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "lc-heat-cell lc-heat-0" })
6362
+ }, cell.key);
6363
+ const picked = props.selected === cell.key;
6364
+ const label = `${cell.key}\n${t("ov.heat.sessions", { n: cell.entry.sessions })}`;
6365
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
6366
+ label,
6367
+ side: "top",
6368
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6369
+ type: "button",
6370
+ className: `lc-heat-cell lc-heat-${String(level)}${picked ? " lc-heat-on" : ""}`,
6371
+ "aria-label": label,
6372
+ "aria-pressed": picked,
6373
+ onClick: () => {
6374
+ if (props.onSelect !== void 0) props.onSelect(picked ? null : cell.key);
6375
+ }
6376
+ })
6377
+ }, cell.key);
6378
+ })]
6379
+ }, wi);
6380
+ })
6342
6381
  })]
6343
6382
  });
6344
6383
  };
@@ -6768,7 +6807,7 @@ window.__ModuleLoader__.load({
6768
6807
  }),
6769
6808
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6770
6809
  className: "lc-stat-sub",
6771
- children: t("ov.kpi.costSub")
6810
+ children: t("ov.kpi.sessionsSub", { n: kpi.costSessions })
6772
6811
  })
6773
6812
  ]
6774
6813
  }),
@@ -6785,7 +6824,7 @@ window.__ModuleLoader__.load({
6785
6824
  }),
6786
6825
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6787
6826
  className: "lc-stat-sub",
6788
- children: t("ov.kpi.cacheSub")
6827
+ children: t("ov.kpi.sessionsSub", { n: kpi.usageSessions })
6789
6828
  })
6790
6829
  ]
6791
6830
  }),
@@ -7146,6 +7185,21 @@ window.__ModuleLoader__.load({
7146
7185
  props.set?.("defaultPlacement", id);
7147
7186
  }
7148
7187
  }),
7188
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
7189
+ label: t("settings.insightsEntry"),
7190
+ value: state.insightsEntry,
7191
+ disabled,
7192
+ options: [{
7193
+ id: "show",
7194
+ label: t("insightsEntry.show")
7195
+ }, {
7196
+ id: "hide",
7197
+ label: t("insightsEntry.hide")
7198
+ }],
7199
+ onPick: (id) => {
7200
+ props.set?.("insightsEntry", id);
7201
+ }
7202
+ }),
7149
7203
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
7150
7204
  label: t("settings.gran"),
7151
7205
  value: state.granularity,
@@ -7235,7 +7289,8 @@ window.__ModuleLoader__.load({
7235
7289
  ...v.defaultGranularity === "step" || v.defaultGranularity === "turn" ? { granularity: v.defaultGranularity } : {},
7236
7290
  ...v.defaultTrendMode === "total" || v.defaultTrendMode === "delta" ? { mode: v.defaultTrendMode } : {},
7237
7291
  ...v.defaultToolSort === "size" || v.defaultToolSort === "count" || v.defaultToolSort === "name" ? { toolSort: v.defaultToolSort } : {},
7238
- ...v.defaultFileSort === "count" || v.defaultFileSort === "latest" || v.defaultFileSort === "path" ? { fileSort: v.defaultFileSort } : {}
7292
+ ...v.defaultFileSort === "count" || v.defaultFileSort === "latest" || v.defaultFileSort === "path" ? { fileSort: v.defaultFileSort } : {},
7293
+ ...v.insightsEntry === "show" || v.insightsEntry === "hide" ? { insightsEntry: v.insightsEntry } : {}
7239
7294
  };
7240
7295
  }
7241
7296
  function createContextSettings() {
@@ -7246,29 +7301,34 @@ window.__ModuleLoader__.load({
7246
7301
  mode: "total",
7247
7302
  toolSort: "count",
7248
7303
  fileSort: "count",
7304
+ insightsEntry: "show",
7249
7305
  writable: false
7250
7306
  };
7251
7307
  let scope;
7252
7308
  const listeners = /* @__PURE__ */ new Set();
7253
7309
  const publish = (next) => {
7254
- if (next.status === state.status && next.placement === state.placement && next.granularity === state.granularity && next.mode === state.mode && next.toolSort === state.toolSort && next.fileSort === state.fileSort && next.writable === state.writable) return;
7310
+ if (next.status === state.status && next.placement === state.placement && next.granularity === state.granularity && next.mode === state.mode && next.toolSort === state.toolSort && next.fileSort === state.fileSort && next.insightsEntry === state.insightsEntry && next.writable === state.writable) return;
7255
7311
  state = next;
7256
7312
  for (const listener of listeners) listener();
7257
7313
  };
7258
7314
  const sync = (bound) => {
7259
7315
  const snap = bound.getSnapshot();
7260
7316
  const prefs = prefsOf(snap.value);
7261
- const rawPlacement = snap.value !== null && typeof snap.value === "object" ? snap.value.defaultPlacement : void 0;
7317
+ const raw = snap.value !== null && typeof snap.value === "object" ? snap.value : void 0;
7262
7318
  publish({
7263
7319
  status: snap.status === "ready" || snap.status === "unavailable" ? snap.status : "loading",
7264
- placement: prefs.placement ?? (rawPlacement === void 0 ? state.placement : "all"),
7320
+ placement: prefs.placement ?? (raw?.defaultPlacement === void 0 ? state.placement : "all"),
7265
7321
  granularity: prefs.granularity ?? state.granularity,
7266
7322
  mode: prefs.mode ?? state.mode,
7267
7323
  toolSort: prefs.toolSort ?? state.toolSort,
7268
7324
  fileSort: prefs.fileSort ?? state.fileSort,
7325
+ insightsEntry: prefs.insightsEntry ?? (raw?.insightsEntry === void 0 ? state.insightsEntry : "show"),
7269
7326
  writable: snap.writable
7270
7327
  });
7271
- return prefs.placement;
7328
+ return {
7329
+ placement: prefs.placement,
7330
+ insightsEntry: prefs.insightsEntry
7331
+ };
7272
7332
  };
7273
7333
  return {
7274
7334
  store: {
@@ -7285,6 +7345,7 @@ window.__ModuleLoader__.load({
7285
7345
  defaultTrendMode: () => state.mode,
7286
7346
  defaultToolSort: () => state.toolSort,
7287
7347
  defaultFileSort: () => state.fileSort,
7348
+ insightsEntry: () => state.insightsEntry,
7288
7349
  attach(bound) {
7289
7350
  scope = bound;
7290
7351
  sync(bound);
@@ -7301,10 +7362,14 @@ window.__ModuleLoader__.load({
7301
7362
  if (bound === void 0) return;
7302
7363
  bound.set(field, value).catch(() => {
7303
7364
  const truth = sync(bound);
7304
- if (field === "defaultPlacement" && truth === void 0) publish({
7365
+ if (field === "defaultPlacement" && truth.placement === void 0) publish({
7305
7366
  ...state,
7306
7367
  placement: "all"
7307
7368
  });
7369
+ if (field === "insightsEntry" && truth.insightsEntry === void 0) publish({
7370
+ ...state,
7371
+ insightsEntry: "show"
7372
+ });
7308
7373
  });
7309
7374
  }
7310
7375
  };
@@ -9410,7 +9475,7 @@ window.__ModuleLoader__.load({
9410
9475
  return function PluginInfo() {
9411
9476
  const [latest, setLatest] = (0, react.useState)(null);
9412
9477
  (0, react.useEffect)(() => {
9413
- if ("0.53.0".includes("-dev")) return;
9478
+ if ("0.53.2".includes("-dev")) return;
9414
9479
  let on = true;
9415
9480
  fetchLatestVersion().then((v) => {
9416
9481
  if (on && v) setLatest(v);
@@ -9419,8 +9484,8 @@ window.__ModuleLoader__.load({
9419
9484
  on = false;
9420
9485
  };
9421
9486
  }, []);
9422
- const update = latest !== null && isNewerVersion(latest, "0.53.0") ? latest : null;
9423
- const nameText = "dsh-context (v0.53.0)";
9487
+ const update = latest !== null && isNewerVersion(latest, "0.53.2") ? latest : null;
9488
+ const nameText = "dsh-context (v0.53.2)";
9424
9489
  const nameValue = [nameText];
9425
9490
  if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9426
9491
  className: "lc-pi-update",
@@ -11266,7 +11331,7 @@ window.__ModuleLoader__.load({
11266
11331
  }
11267
11332
  //#endregion
11268
11333
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/overview.css.mjs
11269
- const css = ".lc-ov-entry{box-sizing:border-box;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:8px;margin:0 -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;position:relative;overflow:hidden}.lc-ov-entry:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-ov-entry-rail{border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.lc-ov-entry-icon{flex:none}.lc-ov-entry-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-ov-badge{box-sizing:border-box;background:var(--color-green-500);color:#fff;pointer-events:none;border-radius:999px;justify-content:center;align-items:center;min-width:15px;height:15px;padding:0 4px;font-size:9px;font-weight:700;line-height:1;display:flex;position:absolute;top:1px;right:4px}.lc-ov-backdrop{z-index:200;background:var(--dsw-alias-bg-mask-1,#00000073);justify-content:center;align-items:center;display:flex;position:fixed;inset:0;container:lc-ov/inline-size}.lc-ov-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);width:min(1120px,100vw - 32px);max-height:min(86vh,900px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:16px 18px 18px;font-size:13px;display:flex;overflow:hidden}.lc-ov-head{flex-wrap:wrap;flex:none;align-items:center;gap:10px;margin-bottom:14px;display:flex}.lc-ov-head-icon{flex:none}.lc-ov-title{white-space:nowrap;font-size:15px;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-body{flex:auto;grid-template-columns:minmax(0,3fr) minmax(0,7fr);gap:10px;min-height:0;display:grid}.lc-ov-left{flex-direction:column;gap:10px;min-height:0;display:flex;overflow-y:auto}.lc-ov-right{min-height:0;overflow-y:auto}@container lc-ov (width<=932px){.lc-ov-card{overflow-y:auto}.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}.lc-ov-left,.lc-ov-right{min-height:auto;overflow:visible}.lc-ov-heat-card{flex:none}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-kpis{flex:none;grid-template-rows:repeat(3,1fr);grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.lc-ov-kpi{align-items:center;padding:10px 8px}.lc-ov-kpi .lc-stat-value{font-size:20px}.lc-ov-heat-card{flex-direction:column;flex:1 0 auto;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:4px;display:flex}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{width:10px;height:20px;color:var(--dsw-alias-label-secondary);text-align:center;flex:none;justify-content:center;align-items:center;font-size:9px;display:flex}.lc-heat-cols{gap:3px;display:flex}.lc-heat-col{flex-direction:column;gap:3px;display:flex}.lc-heat-cell{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);cursor:default;border:0;border-radius:20%;width:20px;height:20px;padding:0}button.lc-heat-cell{cursor:pointer}button.lc-heat-cell:hover{box-shadow:0 0 0 1.5px color-mix(in srgb, var(--color-blue-500) 70%, transparent)}.lc-heat-future{background:0 0}.lc-heat-0{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent)}.lc-heat-1{background:color-mix(in srgb, var(--color-blue-500) 28%, transparent)}.lc-heat-2{background:color-mix(in srgb, var(--color-blue-500) 50%, transparent)}.lc-heat-3{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-heat-4{background:color-mix(in srgb, var(--color-blue-500) 92%, transparent)}.lc-heat-on{box-shadow:0 0 0 1.5px var(--color-blue-500)}.lc-ov-list-head{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.lc-ov-list-title{white-space:nowrap;font-weight:600}.lc-ov-list-count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;background:var(--dsw-alias-bg-layer-2);border-radius:8px;padding:1px 7px;font-size:11px}.lc-ov-day-chip{border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);color:var(--color-blue-500);background:color-mix(in srgb, var(--color-blue-500) 10%, transparent);cursor:pointer;white-space:nowrap;border-radius:999px;padding:1px 10px;font-family:inherit;font-size:11px;line-height:1.4}.lc-ov-search{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:110px;font:inherit;color:var(--dsw-alias-label-primary);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;outline:none;flex:150px;padding:4px 10px;font-size:12px}.lc-ov-search:focus{border-color:var(--dsw-alias-label-secondary)}.lc-ov-search::placeholder{color:var(--dsw-alias-label-secondary)}.lc-ov-groups{flex-wrap:wrap;gap:6px;margin:-2px 0 10px;display:flex}.lc-ov-chip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;max-width:220px;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:999px;align-items:baseline;gap:5px;padding:2px 10px;font-size:11px;line-height:1.5;display:inline-flex;overflow:hidden}.lc-ov-chip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}.lc-ov-chip-on,.lc-ov-chip-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.lc-ov-chip-n{font-variant-numeric:tabular-nums;opacity:.65;font-size:10px}.lc-ov-chip-on .lc-ov-chip-n{opacity:.85}.lc-ov-grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px;display:grid}.lc-ov-session{text-align:left;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;font:inherit;color:inherit;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.lc-ov-session:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}.lc-ov-session-current{border-color:color-mix(in srgb, var(--color-blue-500) 65%, transparent)}.lc-ov-session-head{align-items:flex-start;gap:6px;min-width:0;display:flex}.lc-ov-running{background:var(--color-green-500);border-radius:50%;flex:none;width:7px;height:7px}.lc-ov-session-title{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;flex:auto;min-width:0;font-weight:600;display:-webkit-box;overflow:hidden}.lc-ov-current{color:var(--color-blue-500);border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);border-radius:4px;flex:none;padding:0 4px;font-size:10px;line-height:1.5}.lc-ov-session-times{flex-direction:column;flex:none;align-items:flex-end;gap:1px;display:flex}.lc-ov-session-time{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:11px}.lc-ov-session-created{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:10px;line-height:1.4}.lc-ov-session-crumb{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;align-items:center;gap:5px;font-size:11px;display:flex;overflow:hidden}.lc-ov-crumb-group{text-overflow:ellipsis;flex:none;max-width:55%;overflow:hidden}.lc-ov-crumb-sep{opacity:.55;flex:none}.lc-ov-crumb-project{text-overflow:ellipsis;min-width:0;font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent);border-radius:999px;flex:0 auto;padding:0 8px;font-size:10px;line-height:1.6;overflow:hidden}.lc-ov-session-body{align-items:center;gap:14px;min-width:0;display:flex}.lc-ov-mini-stats{flex-direction:column;flex:auto;gap:3px;min-width:0;display:flex}.lc-ov-mini-stat{justify-content:space-between;align-items:baseline;gap:10px;font-size:11px;display:flex}.lc-ov-mini-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.lc-ov-mini-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-session-empty{color:var(--dsw-alias-label-secondary);padding:6px 0;font-size:11px;line-height:1.5}.lc-ov-session-preview{border-top:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);align-items:baseline;gap:6px;min-width:0;margin-top:auto;padding-top:6px;display:flex}.lc-ov-session-preview-label{color:var(--dsw-alias-label-secondary);flex:none;font-size:10px}.lc-ov-session-preview-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);flex:0 auto;font-size:11px;line-height:1.5;overflow:hidden}.lc-ov-pager{justify-content:center;align-items:center;gap:10px;margin-top:12px;display:flex}.lc-ov-pager-btn{border:1px solid var(--dsw-alias-border-l2);width:26px;height:22px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:6px;flex:none;justify-content:center;align-items:center;line-height:1;display:flex}.lc-ov-pager-btn:hover:enabled{border-color:var(--dsw-alias-label-secondary)}.lc-ov-pager-btn:disabled{opacity:.4;cursor:default}.lc-ov-pager-n{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}";
11334
+ const css = ".lc-ov-entry{box-sizing:border-box;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:8px;margin:0 -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.lc-ov-entry:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-ov-entry-rail{border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.lc-ov-entry-icon{flex:none}.lc-ov-entry-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-ov-backdrop{z-index:200;background:var(--dsw-alias-bg-mask-1,#00000073);justify-content:center;align-items:center;display:flex;position:fixed;inset:0;container:lc-ov/inline-size}.lc-ov-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);width:min(1120px,100vw - 32px);max-height:min(86vh,900px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:16px 18px 18px;font-size:13px;display:flex;overflow:hidden}.lc-ov-head{flex-wrap:wrap;flex:none;align-items:center;gap:10px;margin-bottom:14px;display:flex}.lc-ov-head-icon{flex:none}.lc-ov-title{white-space:nowrap;font-size:15px;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-body{flex:auto;grid-template-columns:minmax(0,3fr) minmax(0,7fr);gap:10px;min-height:0;display:grid}.lc-ov-left{scrollbar-gutter:stable;flex-direction:column;gap:10px;min-height:0;padding-right:8px;display:flex;overflow-y:auto}.lc-ov-right{scrollbar-gutter:stable;min-height:0;padding-right:8px;overflow-y:auto}@container lc-ov (width<=932px){.lc-ov-card{overflow-y:auto}.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}.lc-ov-left,.lc-ov-right{min-height:auto;padding-right:0;overflow:visible}.lc-ov-heat-card{flex:none}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-kpis{flex:none;grid-template-rows:repeat(3,1fr);grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.lc-ov-kpi{align-items:center;padding:10px 8px}.lc-ov-kpi .lc-stat-value{font-size:20px}.lc-ov-heat-card{flex-direction:column;flex:1 0 auto;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:4px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:13px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{width:10px;height:20px;color:var(--dsw-alias-label-secondary);text-align:center;flex:none;justify-content:center;align-items:center;font-size:9px;display:flex}.lc-heat-cols{gap:3px;display:flex}.lc-heat-col{flex-direction:column;gap:3px;display:flex;position:relative}.lc-heat-mon{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:9px;line-height:13px;position:absolute;top:0;left:0}.lc-heat-cell{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);cursor:default;border:0;border-radius:20%;width:20px;height:20px;padding:0}button.lc-heat-cell{cursor:pointer}button.lc-heat-cell:hover{box-shadow:0 0 0 1.5px color-mix(in srgb, var(--color-blue-500) 70%, transparent)}.lc-heat-future{background:0 0}.lc-heat-0{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent)}.lc-heat-1{background:color-mix(in srgb, var(--color-blue-500) 28%, transparent)}.lc-heat-2{background:color-mix(in srgb, var(--color-blue-500) 50%, transparent)}.lc-heat-3{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-heat-4{background:color-mix(in srgb, var(--color-blue-500) 92%, transparent)}.lc-heat-on{box-shadow:0 0 0 1.5px var(--color-blue-500)}.lc-ov-list-head{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.lc-ov-list-title{white-space:nowrap;font-weight:600}.lc-ov-list-count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;background:var(--dsw-alias-bg-layer-2);border-radius:8px;padding:1px 7px;font-size:11px}.lc-ov-day-chip{border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);color:var(--color-blue-500);background:color-mix(in srgb, var(--color-blue-500) 10%, transparent);cursor:pointer;white-space:nowrap;border-radius:999px;padding:1px 10px;font-family:inherit;font-size:11px;line-height:1.4}.lc-ov-search{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:110px;font:inherit;color:var(--dsw-alias-label-primary);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;outline:none;flex:150px;padding:4px 10px;font-size:12px}.lc-ov-search:focus{border-color:var(--dsw-alias-label-secondary)}.lc-ov-search::placeholder{color:var(--dsw-alias-label-secondary)}.lc-ov-groups{flex-wrap:wrap;gap:6px;margin:-2px 0 10px;display:flex}.lc-ov-chip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;max-width:220px;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:999px;align-items:baseline;gap:5px;padding:2px 10px;font-size:11px;line-height:1.5;display:inline-flex;overflow:hidden}.lc-ov-chip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}.lc-ov-chip-on,.lc-ov-chip-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.lc-ov-chip-n{font-variant-numeric:tabular-nums;opacity:.65;font-size:10px}.lc-ov-chip-on .lc-ov-chip-n{opacity:.85}.lc-ov-grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px;display:grid}.lc-ov-session{text-align:left;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;font:inherit;color:inherit;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.lc-ov-session:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}.lc-ov-session-current{border-color:color-mix(in srgb, var(--color-blue-500) 65%, transparent)}.lc-ov-session-head{align-items:flex-start;gap:6px;min-width:0;display:flex}.lc-ov-running{background:var(--color-green-500);border-radius:50%;flex:none;width:7px;height:7px}.lc-ov-session-title{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;flex:auto;min-width:0;font-weight:600;display:-webkit-box;overflow:hidden}.lc-ov-current{color:var(--color-blue-500);border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);border-radius:4px;flex:none;padding:0 4px;font-size:10px;line-height:1.5}.lc-ov-session-times{flex-direction:column;flex:none;align-items:flex-end;gap:1px;display:flex}.lc-ov-session-time{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:11px}.lc-ov-session-created{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:10px;line-height:1.4}.lc-ov-session-crumb{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;align-items:center;gap:5px;font-size:11px;display:flex;overflow:hidden}.lc-ov-crumb-group{text-overflow:ellipsis;flex:none;max-width:55%;overflow:hidden}.lc-ov-crumb-sep{opacity:.55;flex:none}.lc-ov-crumb-project{text-overflow:ellipsis;min-width:0;font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent);border-radius:999px;flex:0 auto;padding:0 8px;font-size:10px;line-height:1.6;overflow:hidden}.lc-ov-session-body{align-items:center;gap:14px;min-width:0;display:flex}.lc-ov-mini-stats{flex-direction:column;flex:auto;gap:3px;min-width:0;display:flex}.lc-ov-mini-stat{justify-content:space-between;align-items:baseline;gap:10px;font-size:11px;display:flex}.lc-ov-mini-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.lc-ov-mini-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-session-empty{color:var(--dsw-alias-label-secondary);padding:6px 0;font-size:11px;line-height:1.5}.lc-ov-session-preview{border-top:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);align-items:baseline;gap:6px;min-width:0;margin-top:auto;padding-top:6px;display:flex}.lc-ov-session-preview-label{color:var(--dsw-alias-label-secondary);flex:none;font-size:10px}.lc-ov-session-preview-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);flex:0 auto;font-size:11px;line-height:1.5;overflow:hidden}.lc-ov-pager{justify-content:center;align-items:center;gap:10px;margin-top:12px;display:flex}.lc-ov-pager-btn{border:1px solid var(--dsw-alias-border-l2);width:26px;height:22px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:6px;flex:none;justify-content:center;align-items:center;line-height:1;display:flex}.lc-ov-pager-btn:hover:enabled{border-color:var(--dsw-alias-label-secondary)}.lc-ov-pager-btn:disabled{opacity:.4;cursor:default}.lc-ov-pager-n{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}";
11270
11335
  const tagId = "dsh-context/overview.css";
11271
11336
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
11272
11337
  const tag = document.createElement("style");
@@ -11345,7 +11410,7 @@ window.__ModuleLoader__.load({
11345
11410
  inject: (sessionId = "") => ({ hooks: { contextModal: modalStoreOf(sessionId) } })
11346
11411
  }, (props) => (0, react.createElement)(ContextModal, props));
11347
11412
  });
11348
- const OverviewButton = makeOverviewButton(kit);
11413
+ const OverviewButton = makeOverviewButton(kit, settings);
11349
11414
  ctx.slots.inject("sidebar.footer.action", () => {
11350
11415
  return ctx.slots.register({
11351
11416
  name: "sidebar.footer.action",
package/lib/index.js CHANGED
@@ -2943,7 +2943,8 @@ const SettingsSchema = z$1.object({
2943
2943
  "count",
2944
2944
  "latest",
2945
2945
  "path"
2946
- ]).default("count").loose()
2946
+ ]).default("count").loose(),
2947
+ insightsEntry: z$1.union(["show", "hide"]).default("show").loose()
2947
2948
  });
2948
2949
  /** Serve the namespace while a settings provider is composed; inert otherwise. */
2949
2950
  function installSettings(ctx) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.53.0",
3
+ "version": "0.53.2",
4
4
  "description": "A DeepSeek Harness plugin for context insight and management, with context dashboard and context command, for understanding how the context is made of, and how it evolves.",
5
5
  "author": "bowenliang123",
6
6
  "repository": {