@glaicer/supercode-token-usage-panel 0.1.0 → 0.1.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@glaicer/supercode-token-usage-panel",
4
- "version": "0.1.0",
4
+ "version": "0.1.1",
5
5
  "description": "OpenCode TUI sidebar section: cumulative token usage and cost of the current session family.",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -5,7 +5,7 @@
5
5
  * folds completed steps/speed/TTFT, and estimates the current visible stream from
6
6
  * deltas. Exposes only ready-to-render rows plus a state flag.
7
7
  */
8
- import { createEffect, createMemo, createSignal, onCleanup, untrack } from "solid-js";
8
+ import type * as Solid from "solid-js";
9
9
  import type { AssistantMessage, Message, Part, Session } from "@opencode-ai/sdk/v2";
10
10
  import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
11
11
 
@@ -475,15 +475,37 @@ async function fetchFamily(
475
475
  return fetchBranch(client, root);
476
476
  }
477
477
 
478
+ /**
479
+ * Reactive primitives owned by the TUI entrypoint.
480
+ *
481
+ * The entry (usage-panel.tsx) imports these from "solid-js", where the host
482
+ * rewrites them to its own runtime. usage-model.ts must not value-import
483
+ * "solid-js" itself: as an npm-installed file under node_modules, the host's
484
+ * prescan can miss this sibling, leaving it bound to an isolated solid-js
485
+ * copy. Signals from two runtimes never notify each other's renderers, which
486
+ * freezes the panel at its first paint ("No usage yet.").
487
+ */
488
+ export interface SolidRuntime {
489
+ createSignal: typeof Solid.createSignal;
490
+ createMemo: typeof Solid.createMemo;
491
+ createEffect: typeof Solid.createEffect;
492
+ onCleanup: typeof Solid.onCleanup;
493
+ untrack: typeof Solid.untrack;
494
+ }
495
+
478
496
  /**
479
497
  * Usage Model over OpenCode's session aggregate. Totals cover the current
480
498
  * session plus all of its descendants (subagents); request sequencing keeps
481
499
  * slower responses from overwriting newer ones.
482
500
  */
483
- export function createUsageModel(api: TuiPluginApi, sessionId: () => string): UsageModel {
484
- const [remote, setRemote] = createSignal<RemoteState>();
485
- const [liveSpeed, setLiveSpeed] = createSignal<LiveSpeedState>();
486
- const [liveTtft, setLiveTtft] = createSignal<LiveTtftState>();
501
+ export function createUsageModel(
502
+ api: TuiPluginApi,
503
+ sessionId: () => string,
504
+ solid: SolidRuntime,
505
+ ): UsageModel {
506
+ const [remote, setRemote] = solid.createSignal<RemoteState>();
507
+ const [liveSpeed, setLiveSpeed] = solid.createSignal<LiveSpeedState>();
508
+ const [liveTtft, setLiveTtft] = solid.createSignal<LiveTtftState>();
487
509
  let request = 0;
488
510
  let nextAsyncRequest = 0;
489
511
  const memberRequests = new Map<string, number>();
@@ -650,7 +672,7 @@ export function createUsageModel(api: TuiPluginApi, sessionId: () => string): Us
650
672
  });
651
673
  };
652
674
 
653
- createEffect(() => {
675
+ solid.createEffect(() => {
654
676
  const sessionID = sessionId();
655
677
  clearProvisional();
656
678
  members = new Set([sessionID]);
@@ -662,7 +684,7 @@ export function createUsageModel(api: TuiPluginApi, sessionId: () => string): Us
662
684
  failedMembers.clear();
663
685
  tombstones.clear();
664
686
  setRemote(undefined);
665
- untrack(() => {
687
+ solid.untrack(() => {
666
688
  ttftTurns = completedTurns(sessionID);
667
689
  refresh(sessionID);
668
690
  });
@@ -939,7 +961,7 @@ export function createUsageModel(api: TuiPluginApi, sessionId: () => string): Us
939
961
  const offSessionIdle = api.event.on("session.idle", (event) => {
940
962
  if (event.properties.sessionID === sessionId()) clearProvisional();
941
963
  });
942
- onCleanup(() => {
964
+ solid.onCleanup(() => {
943
965
  request++;
944
966
  clearProvisional();
945
967
  offSessionCreated();
@@ -955,7 +977,7 @@ export function createUsageModel(api: TuiPluginApi, sessionId: () => string): Us
955
977
  offSessionIdle();
956
978
  });
957
979
 
958
- const snapshot = createMemo<
980
+ const snapshot = solid.createMemo<
959
981
  { status: UsageStatus; rows: readonly UsageRow[]; hasDescendants: boolean }
960
982
  >(() => {
961
983
  try {
@@ -13,19 +13,28 @@
13
13
  * memory, expanded by default and not persisted (spec: Out of Scope).
14
14
  */
15
15
  /** @jsxImportSource @opentui/solid */
16
- import { createSignal, For, Show } from "solid-js";
16
+ import { createEffect, createMemo, createSignal, For, onCleanup, Show, untrack } from "solid-js";
17
17
  import type { TuiPlugin, TuiPluginApi, TuiPluginModule } from "@opencode-ai/plugin/tui";
18
18
  import {
19
19
  USAGE_SECTION_TITLE,
20
20
  USAGE_SECTION_TITLE_WITH_SUBAGENTS,
21
21
  USAGE_STATUS_TEXT,
22
22
  createUsageModel,
23
+ type SolidRuntime,
23
24
  } from "./usage-model.ts";
24
25
 
26
+ /**
27
+ * The host rewrites this file's "solid-js" import to its own runtime. The
28
+ * model builds all of its signals on these exact primitives (see
29
+ * SolidRuntime), so the panel stays in the host's reactive graph even when
30
+ * installed as an npm package under node_modules.
31
+ */
32
+ const solid: SolidRuntime = { createSignal, createMemo, createEffect, onCleanup, untrack };
33
+
25
34
  function Section(props: { api: TuiPluginApi; session_id: string }) {
26
35
  const theme = () => props.api.theme.current;
27
36
  const [collapsed, setCollapsed] = createSignal(false);
28
- const model = createUsageModel(props.api, () => props.session_id);
37
+ const model = createUsageModel(props.api, () => props.session_id, solid);
29
38
 
30
39
  return (
31
40
  <box>