opencode-codex-usage-tui 1.3.2 → 1.4.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.
Files changed (3) hide show
  1. package/README.md +3 -3
  2. package/dist/tui.js +19 -19
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # opencode-codex-usage
1
+ # opencode-codex-usage-tui
2
2
 
3
3
  OpenCode TUI plugin that shows remaining ChatGPT Codex usage limits in the sidebar.
4
4
 
@@ -23,7 +23,7 @@ Add the published package to OpenCode's TUI configuration:
23
23
  ```json
24
24
  {
25
25
  "$schema": "https://opencode.ai/tui.json",
26
- "plugin": ["opencode-codex-usage-tui@1.3.2"]
26
+ "plugin": ["opencode-codex-usage-tui@1.4.0"]
27
27
  }
28
28
  ```
29
29
 
@@ -44,7 +44,7 @@ For example, a local file URL can be used when supported:
44
44
  ```json
45
45
  {
46
46
  "$schema": "https://opencode.ai/tui.json",
47
- "plugin": ["file:///absolute/path/to/opencode-codex-usage-tui-1.3.2.tgz"]
47
+ "plugin": ["file:///absolute/path/to/opencode-codex-usage-tui-1.4.0.tgz"]
48
48
  }
49
49
  ```
50
50
 
package/dist/tui.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { TextAttributes } from "@opentui/core";
1
2
  import { createElement, insert, setProp } from "@opentui/solid";
2
3
  import { fetchUsage, normalizePayload } from "./core.js";
3
4
  const DASHBOARD_URL = "https://chatgpt.com/codex/settings/usage";
@@ -12,7 +13,9 @@ let collapsedInitialized = false;
12
13
  async function fetchOnce() {
13
14
  if (!fetchRequested || inflight) return;
14
15
  inflight = true;
15
- state = { ...state, status: "loading", message: void 0 };
16
+ if (state.status !== "ok" || state.windows.length === 0)
17
+ state = { ...state, status: "loading", message: void 0 };
18
+ else state = { ...state, message: void 0 };
16
19
  try {
17
20
  const result = await fetchUsage();
18
21
  if (!result.ok) {
@@ -62,7 +65,11 @@ function fmtReset(resetSec) {
62
65
  return `${mins}m`;
63
66
  }
64
67
  function fmtPct(pct) {
65
- return `${pct.toFixed(1).padStart(5)}%`;
68
+ return `${Math.round(pct)}%`;
69
+ }
70
+ function fmtCredits(balance) {
71
+ const amount = Number(balance);
72
+ return Number.isFinite(amount) ? amount.toFixed(2) : balance;
66
73
  }
67
74
  function liveResetSeconds(usage) {
68
75
  if (!state.lastFetch) return usage.resetInSec;
@@ -113,24 +120,19 @@ const tui = async (api) => {
113
120
  slots: {
114
121
  sidebar_content() {
115
122
  const theme = api.theme.current;
116
- const muted = theme.textMuted;
117
123
  const normal = theme.text;
118
- const valStyle = { fg: muted };
119
- const headerTitle = valNode({ bold: true, fg: normal });
124
+ const valStyle = { fg: normal };
125
+ const headerTitle = valNode({ attributes: TextAttributes.BOLD, fg: normal });
120
126
  const headerSummary = valNode(valStyle);
121
127
  const planVal = valNode(valStyle);
122
128
  const creditsVal = valNode(valStyle);
123
129
  const resetsVal = valNode(valStyle);
124
130
  const statusVal = valNode(valStyle);
125
131
  function usageBlock() {
126
- const label = valNode({ fg: muted });
132
+ const label = valNode({ fg: normal });
127
133
  const pct = valNode(valStyle);
128
- const reset = valNode(valStyle);
129
- const block = box({ flexDirection: "column", width: "100%" }, [
130
- box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [label, pct]),
131
- box({ flexDirection: "row", width: "100%", justifyContent: "flex-end" }, [reset])
132
- ]);
133
- return { block, label, pct, reset };
134
+ const block = box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [label, pct]);
135
+ return { block, label, pct };
134
136
  }
135
137
  const primary = usageBlock();
136
138
  const secondary = usageBlock();
@@ -141,13 +143,11 @@ const tui = async (api) => {
141
143
  if (!usage) {
142
144
  setNodeText(view.label, "");
143
145
  setNodeText(view.pct, "");
144
- setNodeText(view.reset, "");
145
146
  return;
146
147
  }
147
148
  const reset = fmtReset(liveResetSeconds(usage));
148
149
  setNodeText(view.label, usage.label);
149
- setNodeText(view.pct, `${fmtPct(remainingPercent(usage.usedPercent))} left${usage.limitReached ? " exhausted" : ""}`);
150
- setNodeText(view.reset, reset ? `reset ${reset}` : "");
150
+ setNodeText(view.pct, `${fmtPct(remainingPercent(usage.usedPercent))}${reset ? ` (${reset})` : ""}${usage.limitReached ? " exhausted" : ""}`);
151
151
  }
152
152
  function clearWindows() {
153
153
  setUsageWindow(primary, void 0);
@@ -170,7 +170,7 @@ const tui = async (api) => {
170
170
  setUsageWindow(extraTwo, windows[3]);
171
171
  setNodeText(
172
172
  creditsVal,
173
- state.credits ? state.credits.hasCredits ? state.credits.unlimited ? "unlimited" : state.credits.balance : "none" : ""
173
+ state.credits ? state.credits.hasCredits ? state.credits.unlimited ? "unlimited" : fmtCredits(state.credits.balance) : "none" : ""
174
174
  );
175
175
  setNodeText(resetsVal, state.resetCredits === void 0 ? "" : `${state.resetCredits} available`);
176
176
  setNodeText(statusVal, "");
@@ -210,13 +210,13 @@ ${DASHBOARD_URL}`);
210
210
  }
211
211
  const header = box({ flexDirection: "row", width: "100%", onMouseUp: () => toggle() }, [headerTitle, headerSummary]);
212
212
  const body = box({ flexDirection: "column", width: "100%", visible: !collapsed }, [
213
- box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [txt({ fg: muted }, ["Plan"]), planVal]),
213
+ box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [txt({ fg: normal }, ["Plan"]), planVal]),
214
214
  primary.block,
215
215
  secondary.block,
216
216
  extraOne.block,
217
217
  extraTwo.block,
218
- box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [txt({ fg: muted }, ["Credits"]), creditsVal]),
219
- box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [txt({ fg: muted }, ["Resets"]), resetsVal]),
218
+ box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [txt({ fg: normal }, ["Credits"]), creditsVal]),
219
+ box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [txt({ fg: normal }, ["Resets"]), resetsVal]),
220
220
  statusVal
221
221
  ]);
222
222
  const root = box({ flexDirection: "column", width: "100%" }, [header, body]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-codex-usage-tui",
3
- "version": "1.3.2",
3
+ "version": "1.4.0",
4
4
  "description": "OpenCode TUI plugin that shows remaining ChatGPT Codex usage limits in the sidebar",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -43,9 +43,9 @@
43
43
  "limits",
44
44
  "sidebar"
45
45
  ],
46
- "homepage": "https://github.com/ahmadmcer/opencode-codex-usage",
46
+ "homepage": "https://github.com/ahmadmcer/opencode-codex-usage-tui",
47
47
  "repository": {
48
48
  "type": "git",
49
- "url": "git+https://github.com/ahmadmcer/opencode-codex-usage.git"
49
+ "url": "git+https://github.com/ahmadmcer/opencode-codex-usage-tui.git"
50
50
  }
51
51
  }