@oh-my-sidebar/opencode-session-tokens 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.
Files changed (2) hide show
  1. package/dist/tui.tsx +66 -53
  2. package/package.json +9 -10
package/dist/tui.tsx CHANGED
@@ -1,79 +1,90 @@
1
1
  /** @jsxImportSource @opentui/solid */
2
- import { createMemo, createSignal, For, Show } from "solid-js"
3
- import type { TuiPlugin, TuiPluginApi, TuiPluginModule } from "@opencode-ai/plugin/tui"
4
2
 
5
- const MAX_MODEL_ROWS = 10
6
- const INT_FORMATTER = new Intl.NumberFormat("en-US")
3
+ import type { TuiPlugin, TuiPluginApi, TuiPluginModule } from "@opencode-ai/plugin/tui";
4
+ import { createMemo, createSignal, For, Show } from "solid-js";
5
+
6
+ const MAX_MODEL_ROWS = 10;
7
+ const INT_FORMATTER = new Intl.NumberFormat("en-US");
7
8
 
8
9
  function safeNumber(value: unknown): number {
9
- return typeof value === "number" && Number.isFinite(value) ? value : 0
10
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
10
11
  }
11
12
 
12
- function spentTokenCount(tokens: any): number {
13
- const input = safeNumber(tokens?.input)
14
- const output = safeNumber(tokens?.output)
15
- const reasoning = safeNumber(tokens?.reasoning)
16
- const cacheWrite = safeNumber(tokens?.cache?.write)
17
- return input + output + reasoning + cacheWrite
13
+ function spentTokenCount(tokens: {
14
+ input?: unknown;
15
+ output?: unknown;
16
+ reasoning?: unknown;
17
+ cache?: { write?: unknown };
18
+ }): number {
19
+ const input = safeNumber(tokens?.input);
20
+ const output = safeNumber(tokens?.output);
21
+ const reasoning = safeNumber(tokens?.reasoning);
22
+ const cacheWrite = safeNumber(tokens?.cache?.write);
23
+ return input + output + reasoning + cacheWrite;
18
24
  }
19
25
 
20
26
  function formatInt(value: number): string {
21
- return INT_FORMATTER.format(Math.max(0, Math.round(value)))
27
+ return INT_FORMATTER.format(Math.max(0, Math.round(value)));
22
28
  }
23
29
 
24
30
  function shortModelLabel(label: string): string {
25
- if (label.length <= 28) return label
26
- return `${label.slice(0, 25)}...`
31
+ if (label.length <= 28) return label;
32
+ return `${label.slice(0, 25)}...`;
27
33
  }
28
34
 
29
35
  function View(props: { api: TuiPluginApi; sessionID: string }) {
30
- const [open, setOpen] = createSignal(false)
31
- const theme = () => props.api.theme.current
32
- const messages = createMemo(() => props.api.state.session.messages(props.sessionID) as any[])
33
- const session = createMemo(() => props.api.state.session.get(props.sessionID) as any)
36
+ const [open, setOpen] = createSignal(false);
37
+ const theme = () => props.api.theme.current;
38
+ const messages = createMemo(() => props.api.state.session.messages(props.sessionID));
39
+ const session = createMemo(() => props.api.state.session.get(props.sessionID));
34
40
 
35
41
  const data = createMemo(() => {
36
- const totals = new Map<string, number>()
37
- let breakdownTotal = 0
38
- const seen = new Set<string>()
42
+ const totals = new Map<string, number>();
43
+ let breakdownTotal = 0;
44
+ const seen = new Set<string>();
39
45
 
40
46
  for (const message of messages()) {
41
- const role = message?.role ?? message?.info?.role
42
- if (role !== "assistant") continue
47
+ const role = message?.role ?? message?.info?.role;
48
+ if (role !== "assistant") continue;
43
49
 
44
- const messageID = message?.id
45
- if (typeof messageID === "string" && seen.has(messageID)) continue
46
- if (typeof messageID === "string") seen.add(messageID)
50
+ const messageID = message?.id;
51
+ if (typeof messageID === "string" && seen.has(messageID)) continue;
52
+ if (typeof messageID === "string") seen.add(messageID);
47
53
 
48
- const count = spentTokenCount(message?.tokens)
49
- if (count <= 0) continue
54
+ const count = spentTokenCount(message?.tokens);
55
+ if (count <= 0) continue;
50
56
 
51
- const modelID = message?.modelID ?? message?.info?.modelID ?? "unknown"
57
+ const modelID = message?.modelID ?? message?.info?.modelID ?? "unknown";
52
58
 
53
- breakdownTotal += count
54
- totals.set(modelID, (totals.get(modelID) ?? 0) + count)
59
+ breakdownTotal += count;
60
+ totals.set(modelID, (totals.get(modelID) ?? 0) + count);
55
61
  }
56
62
 
57
63
  const perModel = [...totals.entries()]
58
64
  .map(([model, tokens]) => ({ model, tokens }))
59
- .sort((a, b) => b.tokens - a.tokens)
65
+ .sort((a, b) => b.tokens - a.tokens);
60
66
 
61
- const sessionTotal = spentTokenCount(session()?.tokens)
62
- const total = breakdownTotal > 0 ? breakdownTotal : sessionTotal
67
+ const sessionTotal = spentTokenCount(session()?.tokens);
68
+ const total = breakdownTotal > 0 ? breakdownTotal : sessionTotal;
63
69
 
64
70
  return {
65
71
  total,
66
72
  perModel,
67
- }
68
- })
73
+ };
74
+ });
69
75
 
70
- const show = createMemo(() => data().total > 0)
71
- const canExpand = createMemo(() => data().perModel.length > 0)
76
+ const show = createMemo(() => data().total > 0);
77
+ const canExpand = createMemo(() => data().perModel.length > 0);
72
78
 
73
79
  return (
74
80
  <Show when={show()}>
75
81
  <box>
76
- <box flexDirection="row" gap={1} onMouseDown={() => canExpand() && setOpen((x) => !x)}>
82
+ <button
83
+ type="button"
84
+ flexDirection="row"
85
+ gap={1}
86
+ onMouseDown={() => canExpand() && setOpen((x) => !x)}
87
+ >
77
88
  <Show when={canExpand()}>
78
89
  <text fg={theme().text}>{open() ? "▼" : "▶"}</text>
79
90
  </Show>
@@ -81,23 +92,25 @@ function View(props: { api: TuiPluginApi; sessionID: string }) {
81
92
  <b>Session Tokens</b>
82
93
  </text>
83
94
  <text fg={theme().textMuted}>{formatInt(data().total)}</text>
84
- </box>
95
+ </button>
85
96
 
86
97
  <Show when={canExpand() && open()}>
87
- <For each={data().perModel.slice(0, MAX_MODEL_ROWS)}>{(row) => (
88
- <box flexDirection="row">
89
- <text fg={theme().textMuted}>{shortModelLabel(row.model)}</text>
90
- <box flexGrow={1} />
91
- <text fg={theme().textMuted}>{formatInt(row.tokens)}</text>
92
- </box>
93
- )}</For>
98
+ <For each={data().perModel.slice(0, MAX_MODEL_ROWS)}>
99
+ {(row) => (
100
+ <box flexDirection="row">
101
+ <text fg={theme().textMuted}>{shortModelLabel(row.model)}</text>
102
+ <box flexGrow={1} />
103
+ <text fg={theme().textMuted}>{formatInt(row.tokens)}</text>
104
+ </box>
105
+ )}
106
+ </For>
94
107
  <Show when={data().perModel.length > MAX_MODEL_ROWS}>
95
108
  <text fg={theme().textMuted}>+{data().perModel.length - MAX_MODEL_ROWS} more</text>
96
109
  </Show>
97
110
  </Show>
98
111
  </box>
99
112
  </Show>
100
- )
113
+ );
101
114
  }
102
115
 
103
116
  const tui: TuiPlugin = async (api) => {
@@ -105,15 +118,15 @@ const tui: TuiPlugin = async (api) => {
105
118
  order: 120,
106
119
  slots: {
107
120
  sidebar_content(_ctx, props) {
108
- return <View api={api} sessionID={props.session_id} />
121
+ return <View api={api} sessionID={props.session_id} />;
109
122
  },
110
123
  },
111
- })
112
- }
124
+ });
125
+ };
113
126
 
114
127
  const plugin: TuiPluginModule & { id: string } = {
115
128
  id: "oh-my-sidebar.session-tokens",
116
129
  tui,
117
- }
130
+ };
118
131
 
119
- export default plugin
132
+ export default plugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-sidebar/opencode-session-tokens",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "OpenCode TUI sidebar plugin for session token usage breakdown",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,10 +19,6 @@
19
19
  "dist",
20
20
  "README.md"
21
21
  ],
22
- "scripts": {
23
- "build": "rm -rf dist && mkdir -p dist && cp src/index.tsx dist/tui.tsx",
24
- "prepublishOnly": "pnpm build"
25
- },
26
22
  "publishConfig": {
27
23
  "access": "public"
28
24
  },
@@ -37,9 +33,12 @@
37
33
  "opencode": ">=1.4.3"
38
34
  },
39
35
  "dependencies": {
40
- "@opencode-ai/plugin": "catalog:",
41
- "@opentui/core": "catalog:",
42
- "@opentui/solid": "catalog:",
43
- "solid-js": "catalog:"
36
+ "@opencode-ai/plugin": "^1.17.10",
37
+ "@opentui/core": "^0.4.2",
38
+ "@opentui/solid": "^0.4.2",
39
+ "solid-js": "1.9.12"
40
+ },
41
+ "scripts": {
42
+ "build": "rm -rf dist && mkdir -p dist && cp src/index.tsx dist/tui.tsx"
44
43
  }
45
- }
44
+ }