@oh-my-sidebar/opencode-context-progress 0.1.0

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/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # @oh-my-sidebar/opencode-context-progress
2
+
3
+ OpenCode TUI sidebar plugin that displays the current session's context usage progress.
4
+
5
+ ## Features
6
+
7
+ - Real-time context window usage bar
8
+ - Token count and context window limit display
9
+ - Session cost tracking
10
+ - Color-coded warnings (yellow at 70%, red at 90%)
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install @oh-my-sidebar/opencode-context-progress
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ Register the plugin in your OpenCode configuration:
21
+
22
+ ```json
23
+ {
24
+ "plugins": {
25
+ "oh-my-sidebar.context-progress": {
26
+ "tui": "@oh-my-sidebar/opencode-context-progress/tui"
27
+ }
28
+ }
29
+ }
30
+ ```
31
+
32
+ ## License
33
+
34
+ MIT
package/dist/tui.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { TuiPluginModule } from '@opencode-ai/plugin/tui';
2
+
3
+ declare const plugin: TuiPluginModule & {
4
+ id: string;
5
+ };
6
+
7
+ export { plugin as default };
package/dist/tui.js ADDED
@@ -0,0 +1,111 @@
1
+ // src/index.tsx
2
+ import { TextAttributes } from "@opentui/core";
3
+ import { createMemo } from "solid-js";
4
+ import { jsx, jsxs } from "@opentui/solid/jsx-runtime";
5
+ var BAR_WIDTH = 24;
6
+ function formatInt(value) {
7
+ return new Intl.NumberFormat("en-US").format(Math.max(0, Math.round(value)));
8
+ }
9
+ function formatMoney(value) {
10
+ return `$${value.toFixed(2)}`;
11
+ }
12
+ function safeNumber(value) {
13
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
14
+ }
15
+ function messageTokenCount(message) {
16
+ const input = safeNumber(message?.tokens?.input);
17
+ const output = safeNumber(message?.tokens?.output);
18
+ const reasoning = safeNumber(message?.tokens?.reasoning);
19
+ const cacheRead = safeNumber(message?.tokens?.cache?.read);
20
+ const cacheWrite = safeNumber(message?.tokens?.cache?.write);
21
+ return input + output + reasoning + cacheRead + cacheWrite;
22
+ }
23
+ function buildBar(percent) {
24
+ const clamped = Math.max(0, Math.min(100, percent));
25
+ const filled = Math.max(0, Math.min(BAR_WIDTH, Math.round(clamped / 100 * BAR_WIDTH)));
26
+ return {
27
+ bar: `${"\u2588".repeat(filled)}${"\u2591".repeat(BAR_WIDTH - filled)}`,
28
+ clamped
29
+ };
30
+ }
31
+ function View(props) {
32
+ const messages = createMemo(() => props.api.state.session.messages(props.sessionID));
33
+ const sessionCost = createMemo(() => {
34
+ const sessionState = props.api.state?.session;
35
+ const fromState = safeNumber(sessionState?.get?.(props.sessionID)?.cost);
36
+ if (fromState > 0) return fromState;
37
+ return messages().filter((m) => (m?.role ?? m?.info?.role) === "assistant").reduce((sum, m) => sum + safeNumber(m?.cost), 0);
38
+ });
39
+ const usage = createMemo(() => {
40
+ const lastAssistant = messages().findLast((m) => {
41
+ const role = m?.role ?? m?.info?.role;
42
+ const output = safeNumber(m?.tokens?.output);
43
+ return role === "assistant" && output > 0;
44
+ });
45
+ if (!lastAssistant) {
46
+ return {
47
+ tokens: 0,
48
+ contextWindow: 0,
49
+ percent: 0
50
+ };
51
+ }
52
+ const tokens = messageTokenCount(lastAssistant);
53
+ const providerID = lastAssistant?.providerID ?? lastAssistant?.info?.providerID;
54
+ const modelID = lastAssistant?.modelID ?? lastAssistant?.info?.modelID;
55
+ const model = props.api.state.provider.find((item) => item.id === providerID)?.models?.[modelID];
56
+ const contextWindow = safeNumber(model?.limit?.context);
57
+ const percent = contextWindow > 0 ? Math.round(tokens / contextWindow * 100) : 0;
58
+ return {
59
+ tokens,
60
+ contextWindow,
61
+ percent
62
+ };
63
+ });
64
+ const detailLine = createMemo(() => {
65
+ const state = usage();
66
+ const limitText = state.contextWindow > 0 ? formatInt(state.contextWindow) : "--";
67
+ return `${formatInt(state.tokens)} / ${limitText} / ${formatMoney(sessionCost())}`;
68
+ });
69
+ const theme = () => props.api.theme.current;
70
+ const progress = createMemo(() => {
71
+ const percent = usage().percent;
72
+ const bar = buildBar(percent);
73
+ const color = percent >= 90 ? theme().error : percent >= 70 ? theme().warning : theme().accent;
74
+ return {
75
+ bar: bar.bar,
76
+ color,
77
+ percent: bar.clamped
78
+ };
79
+ });
80
+ return /* @__PURE__ */ jsxs("box", { children: [
81
+ /* @__PURE__ */ jsx("text", { fg: theme().text, attributes: TextAttributes.BOLD, children: "Context" }),
82
+ /* @__PURE__ */ jsxs("box", { flexDirection: "row", gap: 1, children: [
83
+ /* @__PURE__ */ jsx("text", { fg: progress().color, children: progress().bar }),
84
+ /* @__PURE__ */ jsxs("text", { fg: progress().color, children: [
85
+ " ",
86
+ progress().percent,
87
+ "%"
88
+ ] })
89
+ ] }),
90
+ /* @__PURE__ */ jsx("text", { fg: theme().textMuted, children: detailLine() })
91
+ ] });
92
+ }
93
+ var tui = async (api) => {
94
+ const { slots } = api;
95
+ slots.register({
96
+ order: 100,
97
+ slots: {
98
+ sidebar_content(_ctx, props) {
99
+ return /* @__PURE__ */ jsx(View, { api, sessionID: props.session_id });
100
+ }
101
+ }
102
+ });
103
+ };
104
+ var plugin = {
105
+ id: "oh-my-sidebar.context-progress",
106
+ tui
107
+ };
108
+ var src_default = plugin;
109
+ export {
110
+ src_default as default
111
+ };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@oh-my-sidebar/opencode-context-progress",
3
+ "version": "0.1.0",
4
+ "description": "OpenCode TUI sidebar plugin for context usage progress",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/edso404/opencode-zanbo-sidebar",
8
+ "directory": "packages/context-progress"
9
+ },
10
+ "homepage": "https://github.com/edso404/opencode-zanbo-sidebar/tree/master/packages/context-progress",
11
+ "bugs": {
12
+ "url": "https://github.com/edso404/opencode-zanbo-sidebar/issues"
13
+ },
14
+ "type": "module",
15
+ "exports": {
16
+ "./tui": {
17
+ "types": "./dist/tui.d.ts",
18
+ "import": "./dist/tui.js",
19
+ "default": "./dist/tui.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "README.md"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsup",
28
+ "dev": "tsup --watch",
29
+ "prepublishOnly": "pnpm build"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "keywords": [
35
+ "opencode",
36
+ "opencode-plugin",
37
+ "sidebar",
38
+ "context"
39
+ ],
40
+ "license": "MIT",
41
+ "engines": {
42
+ "opencode": ">=1.4.3"
43
+ },
44
+ "dependencies": {
45
+ "@opencode-ai/plugin": "catalog:",
46
+ "@opentui/core": "catalog:",
47
+ "@opentui/solid": "catalog:",
48
+ "solid-js": "catalog:"
49
+ }
50
+ }