@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 +1 -1
- package/src/usage-model.ts +31 -9
- package/src/usage-panel.tsx +11 -2
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.
|
|
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",
|
package/src/usage-model.ts
CHANGED
|
@@ -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
|
|
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(
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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 {
|
package/src/usage-panel.tsx
CHANGED
|
@@ -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>
|