@arandevcode/opencode-quota-extended 0.1.1 → 0.2.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.
@@ -1,4 +1,4 @@
1
- import { resolveOpenCodeGoConfigCached } from "../lib/opencode-go-config.js";
1
+ import { resolveOpenCodeGoInstanceCached } from "../lib/opencode-go-config.js";
2
2
  import { fetchWithTimeout } from "../lib/http.js";
3
3
  import { clampPercent } from "../lib/reset-format.js";
4
4
  const DASHBOARD_URL_PREFIX = "https://opencode.ai/workspace/";
@@ -22,81 +22,88 @@ function parseWindow(html, field) {
22
22
  }
23
23
  return null;
24
24
  }
25
- export const opencodeGoProvider = {
26
- id: "opencode-go",
27
- defaultLabel: "OpenCode Go",
28
- async isAvailable() {
29
- const r = await resolveOpenCodeGoConfigCached();
30
- return r.state === "configured";
31
- },
32
- async fetch(ctx) {
33
- const fetchedAt = new Date().toISOString();
34
- const base = {
35
- id: "opencode-go",
36
- label: "OpenCode Go",
37
- status: "no-auth",
38
- windows: [],
39
- fetchedAt,
40
- };
41
- const r = await resolveOpenCodeGoConfigCached();
42
- if (r.state === "none")
43
- return { ...base, status: "skipped" };
44
- if (r.state === "incomplete") {
45
- return {
46
- ...base,
47
- status: "error",
48
- error: `Missing ${r.missing} (source: ${r.source})`,
25
+ export function createOpenCodeGoProvider(opts) {
26
+ const { id, label } = opts;
27
+ return {
28
+ id,
29
+ defaultLabel: label,
30
+ async isAvailable() {
31
+ const r = await resolveOpenCodeGoInstanceCached(id);
32
+ return r.state === "configured";
33
+ },
34
+ async fetch(ctx) {
35
+ const fetchedAt = new Date().toISOString();
36
+ const base = {
37
+ id,
38
+ label,
39
+ status: "no-auth",
40
+ windows: [],
41
+ fetchedAt,
49
42
  };
50
- }
51
- if (r.state === "invalid") {
52
- return { ...base, status: "error", error: `Invalid config: ${r.error}` };
53
- }
54
- const { workspaceId, authCookie } = r.config;
55
- try {
56
- const url = `${DASHBOARD_URL_PREFIX}${encodeURIComponent(workspaceId)}${DASHBOARD_URL_SUFFIX}`;
57
- const res = await fetchWithTimeout(url, {
58
- method: "GET",
59
- headers: {
60
- "User-Agent": USER_AGENT,
61
- Accept: "text/html",
62
- Cookie: `auth=${authCookie}`,
63
- },
64
- }, ctx.requestTimeoutMs);
65
- if (!res.ok) {
66
- const text = (await res.text()).slice(0, 120);
43
+ const r = await resolveOpenCodeGoInstanceCached(id);
44
+ if (r.state === "none")
45
+ return { ...base, status: "skipped" };
46
+ if (r.state === "incomplete") {
67
47
  return {
68
48
  ...base,
69
49
  status: "error",
70
- error: `OpenCode Go dashboard error ${res.status}: ${text}`,
50
+ error: `Missing ${r.missing} (source: ${r.source})`,
71
51
  };
72
52
  }
73
- const html = await res.text();
74
- const now = Date.now();
75
- const windows = [];
76
- for (const spec of WINDOWS) {
77
- const parsed = parseWindow(html, spec.field);
78
- if (!parsed)
79
- continue;
80
- windows.push({
81
- id: spec.id,
82
- label: spec.label,
83
- percentRemaining: clampPercent(100 - parsed.usagePercent),
84
- resetTimeIso: new Date(now + parsed.resetInSec * 1000).toISOString(),
85
- });
53
+ if (r.state === "invalid") {
54
+ return { ...base, status: "error", error: `Invalid config: ${r.error}` };
86
55
  }
87
- if (windows.length === 0) {
88
- return {
89
- ...base,
90
- status: "error",
91
- error: "Could not parse any known OpenCode Go dashboard usage windows",
92
- };
56
+ const { workspaceId, authCookie } = r.config;
57
+ try {
58
+ const url = `${DASHBOARD_URL_PREFIX}${encodeURIComponent(workspaceId)}${DASHBOARD_URL_SUFFIX}`;
59
+ const res = await fetchWithTimeout(url, {
60
+ method: "GET",
61
+ headers: {
62
+ "User-Agent": USER_AGENT,
63
+ Accept: "text/html",
64
+ Cookie: `auth=${authCookie}`,
65
+ },
66
+ }, ctx.requestTimeoutMs);
67
+ if (!res.ok) {
68
+ const text = (await res.text()).slice(0, 120);
69
+ return {
70
+ ...base,
71
+ status: "error",
72
+ error: `OpenCode Go dashboard error ${res.status}: ${text}`,
73
+ };
74
+ }
75
+ const html = await res.text();
76
+ const now = Date.now();
77
+ const windows = [];
78
+ for (const spec of WINDOWS) {
79
+ const parsed = parseWindow(html, spec.field);
80
+ if (!parsed)
81
+ continue;
82
+ windows.push({
83
+ id: spec.id,
84
+ label: spec.label,
85
+ percentRemaining: clampPercent(100 - parsed.usagePercent),
86
+ resetTimeIso: new Date(now + parsed.resetInSec * 1000).toISOString(),
87
+ });
88
+ }
89
+ if (windows.length === 0) {
90
+ return {
91
+ ...base,
92
+ status: "error",
93
+ error: "Could not parse any known OpenCode Go dashboard usage windows",
94
+ };
95
+ }
96
+ return { ...base, status: "ok", windows };
97
+ }
98
+ catch (err) {
99
+ const message = err instanceof Error ? err.message : String(err);
100
+ return { ...base, status: "error", error: message.slice(0, 200) };
93
101
  }
94
- return { ...base, status: "ok", windows };
95
- }
96
- catch (err) {
97
- const message = err instanceof Error ? err.message : String(err);
98
- return { ...base, status: "error", error: message.slice(0, 200) };
99
- }
100
- },
101
- };
102
+ },
103
+ };
104
+ }
105
+ export const opencodeGoProvider = createOpenCodeGoProvider({
106
+ id: "opencode-go",
107
+ label: "OpenCode Go",
108
+ });
102
109
  //# sourceMappingURL=opencode-go.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"opencode-go.js","sourceRoot":"","sources":["../../src/providers/opencode-go.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"opencode-go.js","sourceRoot":"","sources":["../../src/providers/opencode-go.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,10 @@
4
4
  * Holds the list of available providers and exposes a single
5
5
  * `collectSnapshot()` that fans out fetches in parallel and returns
6
6
  * a CardsSnapshot suitable for the TUI sidebar and the server tool.
7
+ *
8
+ * OpenCode Go is dynamic: besides the base "opencode-go" instance, any
9
+ * opencode-go-<suffix>.json sidecar file adds an extra "Opencode Go <suffix>"
10
+ * provider.
7
11
  */
8
12
  import type { CardsPluginConfig } from "../config/defaults.js";
9
13
  import type { Provider, ProviderContext } from "./types.js";
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,iBAAiB,CAAC;AAYpE,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAE5D;AAED,wBAAgB,aAAa,IAAI,QAAQ,EAAE,CAE1C;AAED,0DAA0D;AAC1D,wBAAgB,YAAY,CAC1B,MAAM,EAAE,iBAAiB,EACzB,UAAU,EAAE,MAAM,GACjB,eAAe,CAMjB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,aAAa,CAAC,CAsDxB"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,iBAAiB,CAAC;AAgBpE,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAE5D;AAED,wBAAgB,aAAa,IAAI,QAAQ,EAAE,CAE1C;AAMD,0DAA0D;AAC1D,wBAAgB,YAAY,CAC1B,MAAM,EAAE,iBAAiB,EACzB,UAAU,EAAE,MAAM,GACjB,eAAe,CAKjB;AAmBD;;;GAGG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,aAAa,CAAC,CA8DxB"}
@@ -1,6 +1,7 @@
1
1
  import { openaiProvider } from "./openai.js";
2
2
  import { copilotProvider } from "./copilot.js";
3
- import { opencodeGoProvider } from "./opencode-go.js";
3
+ import { createOpenCodeGoProvider, opencodeGoProvider, } from "./opencode-go.js";
4
+ import { discoverOpenCodeGoInstances } from "../lib/opencode-go-config.js";
4
5
  const ALL_PROVIDERS = [
5
6
  openaiProvider,
6
7
  copilotProvider,
@@ -12,24 +13,52 @@ export function getProvider(id) {
12
13
  export function listProviders() {
13
14
  return ALL_PROVIDERS.slice();
14
15
  }
16
+ function isOpenCodeGoId(id) {
17
+ return id === "opencode-go" || id.startsWith("opencode-go-");
18
+ }
15
19
  /** Build a ProviderContext for a specific provider id. */
16
20
  export function buildContext(config, providerId) {
17
- const requestTimeoutMs = providerId === "opencode-go"
21
+ const requestTimeoutMs = isOpenCodeGoId(providerId)
18
22
  ? config.opencodeGoTimeoutMs
19
23
  : config.requestTimeoutMs;
20
24
  return { requestTimeoutMs };
21
25
  }
26
+ /**
27
+ * Stable ordering for providers. Base providers follow the configured
28
+ * `enabledProviders` order; extra OpenCode Go instances are appended right
29
+ * after the base "opencode-go" entry.
30
+ */
31
+ function providerOrder(config, id) {
32
+ const idx = config.enabledProviders.indexOf(id);
33
+ if (idx !== -1)
34
+ return idx;
35
+ if (isOpenCodeGoId(id)) {
36
+ const goIdx = config.enabledProviders.indexOf("opencode-go");
37
+ return goIdx !== -1 ? goIdx + 1 : config.enabledProviders.length;
38
+ }
39
+ return config.enabledProviders.length;
40
+ }
22
41
  /**
23
42
  * Collect a snapshot by fanning out fetches in parallel for all
24
43
  * enabled providers that are available.
25
44
  */
26
45
  export async function collectSnapshot(config) {
27
46
  const providers = [];
28
- const tasks = config.enabledProviders.map(async (id) => {
47
+ const defs = [];
48
+ for (const id of config.enabledProviders) {
29
49
  const provider = getProvider(id);
30
50
  if (!provider)
31
- return;
32
- const ctx = buildContext(config, id);
51
+ continue;
52
+ defs.push({ provider });
53
+ if (id === "opencode-go") {
54
+ const extras = await discoverOpenCodeGoInstances();
55
+ for (const instance of extras) {
56
+ defs.push({ provider: createOpenCodeGoProvider(instance) });
57
+ }
58
+ }
59
+ }
60
+ const tasks = defs.map(async ({ provider }) => {
61
+ const ctx = buildContext(config, provider.id);
33
62
  let available = false;
34
63
  try {
35
64
  available = await provider.isAvailable(ctx);
@@ -64,9 +93,11 @@ export async function collectSnapshot(config) {
64
93
  });
65
94
  await Promise.all(tasks);
66
95
  providers.sort((a, b) => {
67
- const order = config.enabledProviders.indexOf(a.id);
68
- const orderB = config.enabledProviders.indexOf(b.id);
69
- return order - orderB;
96
+ const orderA = providerOrder(config, a.id);
97
+ const orderB = providerOrder(config, b.id);
98
+ if (orderA !== orderB)
99
+ return orderA - orderB;
100
+ return a.id.localeCompare(b.id);
70
101
  });
71
102
  return {
72
103
  generatedAt: new Date().toISOString(),
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"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"}
package/dist/tui.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAgB,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAWxF,eAAO,MAAM,mBAAmB,EAAE,SAoGjC,CAAC;AAEF,QAAA,MAAM,YAAY,EAAE,eAAe,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAGjD,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAgB,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAWxF,eAAO,MAAM,mBAAmB,EAAE,SA2GjC,CAAC;AAEF,QAAA,MAAM,YAAY,EAAE,eAAe,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAGjD,CAAC;AAEF,eAAe,YAAY,CAAC"}
package/dist/tui.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  /** @jsxImportSource @opentui/solid */
2
- import { createSignal, onCleanup } from "solid-js";
2
+ import { createSignal, ErrorBoundary, onCleanup } from "solid-js";
3
3
  import type { TuiPlugin, TuiPluginApi, TuiPluginModule } from "@opencode-ai/plugin/tui";
4
4
  import { loadCardsConfig } from "./config/loader.js";
5
5
  import { collectSnapshot } from "./providers/registry.js";
@@ -49,7 +49,10 @@ export const quotaCardsTuiPlugin: TuiPlugin = async (api, _options, _meta) => {
49
49
  const v = ++version;
50
50
  collectSnapshot(config).then((next) => {
51
51
  if (v === version) setSnapshot(next);
52
- }).catch(() => {}).finally(() => {
52
+ }).catch((err) => {
53
+ const msg = err instanceof Error ? `${err.message}\n${err.stack}` : String(err);
54
+ try { process.stderr.write(`[quota-cards] snapshot refresh failed: ${msg}\n`); } catch {}
55
+ }).finally(() => {
53
56
  inFlight = false;
54
57
  if (queued) { queued = false; reload(); }
55
58
  });
@@ -68,10 +71,10 @@ export const quotaCardsTuiPlugin: TuiPlugin = async (api, _options, _meta) => {
68
71
 
69
72
  const interval = setInterval(reload, config.refreshIntervalMs ?? REFRESH_INTERVAL_MS);
70
73
  const unsubs = [
71
- api.event.on("session.updated", (e) => { if (e?.properties?.info?.id === sessionID) scheduleRefresh(); }),
72
- api.event.on("message.updated", (e) => { if (e?.properties?.info?.sessionID === sessionID) scheduleRefresh(); }),
73
- api.event.on("message.removed", (e) => { if (e?.properties?.sessionID === sessionID) scheduleRefresh(); }),
74
- api.event.on("tui.session.select", (e) => { if (e?.properties?.sessionID === sessionID) scheduleRefresh(); }),
74
+ api.event.on("session.updated", (e) => { try { if (e?.properties?.info?.id === sessionID) scheduleRefresh(); } catch {} }),
75
+ api.event.on("message.updated", (e) => { try { if (e?.properties?.info?.sessionID === sessionID) scheduleRefresh(); } catch {} }),
76
+ api.event.on("message.removed", (e) => { try { if (e?.properties?.sessionID === sessionID) scheduleRefresh(); } catch {} }),
77
+ api.event.on("tui.session.select", (e) => { try { if (e?.properties?.sessionID === sessionID) scheduleRefresh(); } catch {} }),
75
78
  ];
76
79
 
77
80
  reload();
@@ -103,7 +106,11 @@ export const quotaCardsTuiPlugin: TuiPlugin = async (api, _options, _meta) => {
103
106
  order: config.cardsSidebar.order,
104
107
  slots: {
105
108
  sidebar_content(_ctx: unknown, props: { session_id: string }) {
106
- return <SidebarContentView sessionID={props.session_id} />;
109
+ return (
110
+ <ErrorBoundary fallback={<text fg={api.theme.current.error}>Quota error</text>}>
111
+ <SidebarContentView sessionID={props.session_id} />
112
+ </ErrorBoundary>
113
+ );
107
114
  },
108
115
  },
109
116
  });
@@ -1 +1 @@
1
- {"version":3,"file":"SidebarPanel.d.ts","sourceRoot":"","sources":["../../src/ui/SidebarPanel.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrD,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,GAAG,EAAE,YAAY,CAAC;IAClB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,EAAE,MAAM,aAAa,CAAC;CAC/B,GAAG,GAAG,CAAC,OAAO,CAsBd"}
1
+ {"version":3,"file":"SidebarPanel.d.ts","sourceRoot":"","sources":["../../src/ui/SidebarPanel.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrD,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,GAAG,EAAE,YAAY,CAAC;IAClB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,EAAE,MAAM,aAAa,CAAC;CAC/B,GAAG,GAAG,CAAC,OAAO,CAiCd"}
@@ -19,6 +19,17 @@ export function SidebarPanel(props: {
19
19
  props.snapshot().providers.filter((p) => p.status === "ok").length;
20
20
  const totalCount = () => props.snapshot().providers.length;
21
21
 
22
+ const estimatedHeight = () => {
23
+ let total = 0
24
+ for (const p of props.snapshot().providers) {
25
+ if (p.status === "skipped") continue
26
+ if (p.status === "no-auth" || p.status === "error") { total += 1; continue }
27
+ const n = p.windows?.length ?? 1
28
+ total += 4 * n + 1 // title + gap + n*3 rows + (n-1)*gaps = 4n+1
29
+ }
30
+ return Math.min(total, 50)
31
+ }
32
+
22
33
  return (
23
34
  <box flexDirection="column" gap={0}>
24
35
  <box flexDirection="row" gap={1}>
@@ -26,11 +37,11 @@ export function SidebarPanel(props: {
26
37
  Quota ({providerCount()}/{totalCount()})
27
38
  </text>
28
39
  </box>
29
- <box flexDirection="column" gap={1}>
40
+ <scrollbox viewportCulling={true} height={estimatedHeight()} flexDirection="column" gap={1}>
30
41
  <For each={props.snapshot().providers}>
31
42
  {(p) => <ProviderCards api={props.api} provider={p} showOnlyMultiWindow={props.config.showOnlyMultiWindow} />}
32
43
  </For>
33
- </box>
44
+ </scrollbox>
34
45
  </box>
35
46
  );
36
47
  }
package/dist/ui/cards.tsx CHANGED
@@ -4,7 +4,7 @@ import { TextAttributes } from "@opentui/core";
4
4
  import type { JSX } from "@opentui/solid";
5
5
  import type { TuiThemeCurrent } from "@opencode-ai/plugin/tui";
6
6
  import type { ProviderQuota, QuotaWindow } from "../lib/types.js";
7
- import { trafficLight } from "./progress-bar.js";
7
+ import { trafficLight, trafficLightColorKey } from "./progress-bar.js";
8
8
  import { formatResetCombined } from "../lib/reset-format.js";
9
9
 
10
10
  export function ProviderCards(props: {
@@ -47,11 +47,11 @@ export function ProviderCards(props: {
47
47
  }
48
48
 
49
49
  return (
50
- <box flexDirection="column" gap={0}>
50
+ <box flexDirection="column" gap={0} width="100%">
51
51
  <text fg={theme().text} attributes={TextAttributes.BOLD}>
52
52
  {props.provider.label}
53
53
  </text>
54
- <box flexDirection="column" gap={1}>
54
+ <box flexDirection="column" gap={1} width="100%">
55
55
  <For each={props.provider.windows}>
56
56
  {(window) => <WindowRow api={props.api} window={window} />}
57
57
  </For>
@@ -66,39 +66,48 @@ function WindowRow(props: {
66
66
  }): JSX.Element {
67
67
  const theme = () => props.api.theme.current;
68
68
  const muted = () => theme().textMuted;
69
- const textColor = () => theme().text;
70
69
  const percent = () => props.window.percentRemaining;
71
70
  const light = () => trafficLight(percent());
72
71
  const barColor = () => {
73
- if (light() === "ok") return "#6b8e23";
74
- if (light() === "warning") return "#ffa500";
75
- return "#ff6b6b";
72
+ const key = trafficLightColorKey(light());
73
+ return theme()[key] ?? theme().text;
76
74
  };
77
75
  const label = () => windowDisplayLabel(props.window);
78
76
  const reset = () => formatReset(props.window.resetTimeIso);
79
- const usedPct = () => {
80
- const v = Math.round(100 - percent());
81
- return v >= 0 ? v : 0;
82
- };
83
77
 
84
78
  return (
85
- <box flexDirection="column" gap={0}>
79
+ <box flexDirection="column" gap={0} width="100%">
86
80
  <box flexDirection="row" gap={0} justifyContent="space-between">
87
81
  <text>{label()}</text>
88
82
  <text fg={light() === "error" ? barColor() : muted()}>{reset()}</text>
89
83
  </box>
90
- <box flexDirection="row" height={1}>
91
- <box flexGrow={percent()} backgroundColor={barColor()} />
92
- <box flexGrow={100 - percent()} backgroundColor={theme().backgroundElement} />
93
- </box>
94
- <box flexDirection="row" gap={0} justifyContent="flex-end">
95
- <text fg={textColor()}>{`${usedPct()}% used / `}</text>
96
- <text fg={barColor()}>{`${percent()}% left`}</text>
84
+ <GaugeBar percent={percent()} theme={theme()} barColor={barColor()} />
85
+ <box flexDirection="row" gap={0} justifyContent="space-between">
86
+ <text fg={barColor()}>{percent()}% left</text>
87
+ <text fg={muted()}>{100 - percent()}% used</text>
97
88
  </box>
98
89
  </box>
99
90
  );
100
91
  }
101
92
 
93
+ function GaugeBar(props: { percent: number; theme: any; barColor: any }): JSX.Element {
94
+ const clamped = Math.max(0, Math.min(100, props.percent));
95
+ const WIDTH = 38;
96
+ const filledCount = Math.round((clamped / 100) * WIDTH);
97
+ const emptyCount = WIDTH - filledCount;
98
+
99
+ return (
100
+ <box flexDirection="row" height={1}>
101
+ {filledCount > 0 ? (
102
+ <text fg={props.barColor}>{"▓".repeat(filledCount)}</text>
103
+ ) : null}
104
+ {emptyCount > 0 ? (
105
+ <text fg={props.barColor}>{"░".repeat(emptyCount)}</text>
106
+ ) : null}
107
+ </box>
108
+ );
109
+ }
110
+
102
111
  function windowDisplayLabel(window: QuotaWindow): string {
103
112
  if (
104
113
  typeof window.remaining === "number" &&
@@ -22,6 +22,6 @@ export declare function trafficLight(percentRemaining: number): TrafficLight;
22
22
  /**
23
23
  * Map a traffic-light state to the corresponding TuiThemeCurrent color key.
24
24
  */
25
- export declare function trafficLightColorKey(light: TrafficLight): "accent" | "warning" | "error";
25
+ export declare function trafficLightColorKey(light: TrafficLight): "success" | "warning" | "error";
26
26
  export declare function trafficLightIndicator(light: TrafficLight): string;
27
27
  //# sourceMappingURL=progress-bar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"progress-bar.d.ts","sourceRoot":"","sources":["../../src/ui/progress-bar.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAQA;AAED,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,gBAAgB,EAAE,MAAM,GAAG,YAAY,CAInE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAIxF;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAIjE"}
1
+ {"version":3,"file":"progress-bar.d.ts","sourceRoot":"","sources":["../../src/ui/progress-bar.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAQA;AAED,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,gBAAgB,EAAE,MAAM,GAAG,YAAY,CAInE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAIzF;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAIjE"}
@@ -33,7 +33,7 @@ export function trafficLight(percentRemaining) {
33
33
  */
34
34
  export function trafficLightColorKey(light) {
35
35
  if (light === "ok")
36
- return "accent";
36
+ return "success";
37
37
  if (light === "warning")
38
38
  return "warning";
39
39
  return "error";
@@ -1 +1 @@
1
- {"version":3,"file":"progress-bar.js","sourceRoot":"","sources":["../../src/ui/progress-bar.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,gBAAwB,EAAE,QAAgB,EAAE;IAK5E,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACtF,OAAO;QACL,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;QAC/B,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,WAAW,CAAC;QACtC,KAAK;KACN,CAAC;AACJ,CAAC;AAID;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,gBAAwB;IACnD,IAAI,gBAAgB,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,gBAAgB,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC;IAC7C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAmB;IACtD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC;IACpC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAmB;IACvD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,GAAG,CAAC;IAC/B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IACpC,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"progress-bar.js","sourceRoot":"","sources":["../../src/ui/progress-bar.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,gBAAwB,EAAE,QAAgB,EAAE;IAK5E,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACtF,OAAO;QACL,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;QAC/B,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,WAAW,CAAC;QACtC,KAAK;KACN,CAAC;AACJ,CAAC;AAID;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,gBAAwB;IACnD,IAAI,gBAAgB,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,gBAAgB,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC;IAC7C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAmB;IACtD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACrC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAmB;IACvD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,GAAG,CAAC;IAC/B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IACpC,OAAO,GAAG,CAAC;AACb,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arandevcode/opencode-quota-extended",
3
- "version": "0.1.1",
4
- "description": "OpenCode plugin: quota usage cards with progress bars (5h / weekly / monthly) for OpenAI, GitHub Copilot and OpenCode Go in the TUI sidebar.",
3
+ "version": "0.2.1",
4
+ "description": "OpenCode plugin: quota usage cards with theme-colored gauge progress bars (5h / weekly / monthly) for OpenAI, GitHub Copilot and OpenCode Go in the TUI sidebar.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -66,6 +66,9 @@
66
66
  "engines": {
67
67
  "node": ">=18.0.0"
68
68
  },
69
+ "publishConfig": {
70
+ "access": "public"
71
+ },
69
72
  "author": "arandevcode",
70
73
  "license": "MIT"
71
74
  }
@@ -1,26 +0,0 @@
1
- <!DOCTYPE HTML>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <title>Directory listing for /opencode-personal-projects/opencode-quota-extended/dist/config/</title>
6
- </head>
7
- <body>
8
- <h1>Directory listing for /opencode-personal-projects/opencode-quota-extended/dist/config/</h1>
9
- <hr>
10
- <ul>
11
- <li><a href="defaults.d.ts">defaults.d.ts</a></li>
12
- <li><a href="defaults.d.ts.map">defaults.d.ts.map</a></li>
13
- <li><a href="defaults.js">defaults.js</a></li>
14
- <li><a href="defaults.js.map">defaults.js.map</a></li>
15
- <li><a href="loader.d.ts">loader.d.ts</a></li>
16
- <li><a href="loader.d.ts.map">loader.d.ts.map</a></li>
17
- <li><a href="loader.js">loader.js</a></li>
18
- <li><a href="loader.js.map">loader.js.map</a></li>
19
- <li><a href="schema.d.ts">schema.d.ts</a></li>
20
- <li><a href="schema.d.ts.map">schema.d.ts.map</a></li>
21
- <li><a href="schema.js">schema.js</a></li>
22
- <li><a href="schema.js.map">schema.js.map</a></li>
23
- </ul>
24
- <hr>
25
- </body>
26
- </html>
package/dist/index.html DELETED
@@ -1,29 +0,0 @@
1
- <!DOCTYPE HTML>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <title>Directory listing for /opencode-personal-projects/opencode-quota-extended/dist/</title>
6
- </head>
7
- <body>
8
- <h1>Directory listing for /opencode-personal-projects/opencode-quota-extended/dist/</h1>
9
- <hr>
10
- <ul>
11
- <li><a href="config/">config/</a></li>
12
- <li><a href="index.d.ts">index.d.ts</a></li>
13
- <li><a href="index.d.ts.map">index.d.ts.map</a></li>
14
- <li><a href="index.js">index.js</a></li>
15
- <li><a href="index.js.map">index.js.map</a></li>
16
- <li><a href="lib/">lib/</a></li>
17
- <li><a href="providers/">providers/</a></li>
18
- <li><a href="server.d.ts">server.d.ts</a></li>
19
- <li><a href="server.d.ts.map">server.d.ts.map</a></li>
20
- <li><a href="server.js">server.js</a></li>
21
- <li><a href="server.js.map">server.js.map</a></li>
22
- <li><a href="tui.d.ts">tui.d.ts</a></li>
23
- <li><a href="tui.d.ts.map">tui.d.ts.map</a></li>
24
- <li><a href="tui.tsx">tui.tsx</a></li>
25
- <li><a href="ui/">ui/</a></li>
26
- </ul>
27
- <hr>
28
- </body>
29
- </html>