@slkiser/opencode-quota 4.10.0 → 4.10.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 (76) hide show
  1. package/README.md +16 -15
  2. package/dist/lib/alibaba-token-plan.d.ts +88 -0
  3. package/dist/lib/alibaba-token-plan.d.ts.map +1 -0
  4. package/dist/lib/alibaba-token-plan.js +505 -0
  5. package/dist/lib/alibaba-token-plan.js.map +1 -0
  6. package/dist/lib/atomic-json.d.ts +3 -0
  7. package/dist/lib/atomic-json.d.ts.map +1 -1
  8. package/dist/lib/atomic-json.js +16 -4
  9. package/dist/lib/atomic-json.js.map +1 -1
  10. package/dist/lib/config-write-target.d.ts +28 -0
  11. package/dist/lib/config-write-target.d.ts.map +1 -0
  12. package/dist/lib/config-write-target.js +159 -0
  13. package/dist/lib/config-write-target.js.map +1 -0
  14. package/dist/lib/init-installer.d.ts.map +1 -1
  15. package/dist/lib/init-installer.js +2 -2
  16. package/dist/lib/init-installer.js.map +1 -1
  17. package/dist/lib/opencode-config-editor.d.ts +3 -0
  18. package/dist/lib/opencode-config-editor.d.ts.map +1 -1
  19. package/dist/lib/opencode-config-editor.js +71 -10
  20. package/dist/lib/opencode-config-editor.js.map +1 -1
  21. package/dist/lib/opencode-go.d.ts.map +1 -1
  22. package/dist/lib/opencode-go.js +3 -2
  23. package/dist/lib/opencode-go.js.map +1 -1
  24. package/dist/lib/openrouter.d.ts +2 -1
  25. package/dist/lib/openrouter.d.ts.map +1 -1
  26. package/dist/lib/openrouter.js +3 -4
  27. package/dist/lib/openrouter.js.map +1 -1
  28. package/dist/lib/provider-registration.d.ts +11 -0
  29. package/dist/lib/provider-registration.d.ts.map +1 -1
  30. package/dist/lib/provider-registration.js +12 -0
  31. package/dist/lib/provider-registration.js.map +1 -1
  32. package/dist/lib/quota-status.d.ts.map +1 -1
  33. package/dist/lib/quota-status.js +18 -0
  34. package/dist/lib/quota-status.js.map +1 -1
  35. package/dist/lib/scoped-update.d.ts +2 -0
  36. package/dist/lib/scoped-update.d.ts.map +1 -1
  37. package/dist/lib/scoped-update.js +43 -7
  38. package/dist/lib/scoped-update.js.map +1 -1
  39. package/dist/lib/synthetic.d.ts.map +1 -1
  40. package/dist/lib/synthetic.js +9 -0
  41. package/dist/lib/synthetic.js.map +1 -1
  42. package/dist/lib/tui-compact-format.d.ts.map +1 -1
  43. package/dist/lib/tui-compact-format.js +79 -5
  44. package/dist/lib/tui-compact-format.js.map +1 -1
  45. package/dist/lib/tui-panel-state.d.ts +5 -1
  46. package/dist/lib/tui-panel-state.d.ts.map +1 -1
  47. package/dist/lib/tui-panel-state.js.map +1 -1
  48. package/dist/lib/tui-prompt-bar-format.d.ts +25 -0
  49. package/dist/lib/tui-prompt-bar-format.d.ts.map +1 -0
  50. package/dist/lib/tui-prompt-bar-format.js +206 -0
  51. package/dist/lib/tui-prompt-bar-format.js.map +1 -0
  52. package/dist/lib/tui-runtime.d.ts.map +1 -1
  53. package/dist/lib/tui-runtime.js +5 -93
  54. package/dist/lib/tui-runtime.js.map +1 -1
  55. package/dist/lib/tui-sidebar-content.d.ts +32 -0
  56. package/dist/lib/tui-sidebar-content.d.ts.map +1 -0
  57. package/dist/lib/tui-sidebar-content.js +52 -0
  58. package/dist/lib/tui-sidebar-content.js.map +1 -0
  59. package/dist/providers/alibaba-token-plan.d.ts +3 -0
  60. package/dist/providers/alibaba-token-plan.d.ts.map +1 -0
  61. package/dist/providers/alibaba-token-plan.js +89 -0
  62. package/dist/providers/alibaba-token-plan.js.map +1 -0
  63. package/dist/providers/cache-policies.d.ts +3 -0
  64. package/dist/providers/cache-policies.d.ts.map +1 -1
  65. package/dist/providers/cache-policies.js +1 -0
  66. package/dist/providers/cache-policies.js.map +1 -1
  67. package/dist/providers/openrouter.d.ts.map +1 -1
  68. package/dist/providers/openrouter.js +11 -4
  69. package/dist/providers/openrouter.js.map +1 -1
  70. package/dist/providers/registry.d.ts.map +1 -1
  71. package/dist/providers/registry.js +2 -0
  72. package/dist/providers/registry.js.map +1 -1
  73. package/dist/tui.d.ts.map +1 -1
  74. package/dist/tui.js +226 -219
  75. package/dist/tui.tsx +57 -49
  76. package/package.json +5 -1
package/dist/tui.tsx CHANGED
@@ -7,9 +7,8 @@ import type {
7
7
  TuiPromptRef,
8
8
  } from "@opencode-ai/plugin/tui";
9
9
  import type { JSX } from "@opentui/solid";
10
- import { createEffect, createSignal, onCleanup, Show } from "solid-js";
10
+ import { createEffect, createSignal, Index, onCleanup, Show } from "solid-js";
11
11
  import {
12
- formatDisplayedPercentLabel,
13
12
  formatQuotaModeHeading,
14
13
  formatResetCountdown,
15
14
  isResetTimeDecimals,
@@ -21,8 +20,6 @@ import {
21
20
  type QuotaDialogCommandId,
22
21
  type QuotaDialogCommandSpec,
23
22
  } from "./lib/quota-dialog-commands.js";
24
- import { extractSingleWindowWindowLabel } from "./lib/quota-entry-display.js";
25
- import { formatQuotaRunway } from "./lib/quota-exhaustion-projection.js";
26
23
  import type { SessionTokenError } from "./lib/quota-status.js";
27
24
  import { disposeQuotaTelemetryOwner } from "./lib/quota-telemetry.js";
28
25
  import { getSidebarBodyLineColor } from "./lib/tui-line-style.js";
@@ -41,6 +38,11 @@ import {
41
38
  shouldRenderHomeBottom,
42
39
  shouldRenderSidebarPanel,
43
40
  } from "./lib/tui-panel-state.js";
41
+ import {
42
+ formatPromptBarPercentMeta,
43
+ PROMPT_BAR_WIDTH,
44
+ resolvePromptBarLabel,
45
+ } from "./lib/tui-prompt-bar-format.js";
44
46
  import { createTuiRefreshLifecycle } from "./lib/tui-refresh-lifecycle.js";
45
47
  import {
46
48
  createTuiQuotaClient,
@@ -54,6 +56,7 @@ import {
54
56
  type TuiSurfaceRegistration,
55
57
  writeTuiQuotaExportIfEnabled,
56
58
  } from "./lib/tui-runtime.js";
59
+ import { buildSidebarContentRows, type SidebarContentRow } from "./lib/tui-sidebar-content.js";
57
60
  import type { TuiCommandDisplay } from "./lib/types.js";
58
61
 
59
62
  const id = "@slkiser/opencode-quota";
@@ -378,33 +381,46 @@ function SidebarContentView(props: {
378
381
  return collapsed() ? lines() : getSidebarPanelLinesExpanded(panel());
379
382
  };
380
383
 
381
- const toggleIcon = () => (collapsed() ? "▶" : "▼");
382
- const providerCount = () => panel().providerCount ?? 0;
383
- const headerText = () => {
384
+ const contentRows = (): SidebarContentRow[] => {
384
385
  const heading = panel().headerPercentMode
385
386
  ? formatQuotaModeHeading(panel().headerPercentMode)
386
387
  : "Quota";
387
- return hasDetailLines() ? `${toggleIcon()} ${heading}` : heading;
388
+ return buildSidebarContentRows({
389
+ collapsed: collapsed(),
390
+ heading,
391
+ hasDetailLines: hasDetailLines(),
392
+ providerCount: panel().providerCount ?? 0,
393
+ lines: displayLines(),
394
+ });
388
395
  };
389
396
 
390
397
  return (
391
398
  <Show when={shouldRenderSidebarPanel(panel())}>
392
- <box gap={0}>
393
- <box flexDirection="row">
394
- <text fg={props.api.theme.current.text} onMouseDown={toggleCollapsed}>
395
- <b>{headerText()}</b>
396
- </text>
397
- <Show when={collapsed() && providerCount() > 0}>
398
- <text fg={props.api.theme.current.textMuted}> ({providerCount()} providers)</text>
399
- </Show>
400
- </box>
401
- <box gap={0}>
402
- {displayLines().map((line) => (
403
- <text fg={getSidebarBodyLineColor(line, props.api.theme.current)} wrapMode="none">
404
- {line || " "}
399
+ <box gap={0} width="100%">
400
+ <Index each={contentRows()}>
401
+ {(row: () => SidebarContentRow) => (
402
+ <text
403
+ fg={
404
+ row().kind === "header"
405
+ ? props.api.theme.current.text
406
+ : getSidebarBodyLineColor(row().text, props.api.theme.current)
407
+ }
408
+ wrapMode="none"
409
+ width="100%"
410
+ onMouseDown={row().kind === "header" ? toggleCollapsed : undefined}
411
+ >
412
+ {row().kind === "header"
413
+ ? row().segments.map((segment) =>
414
+ segment.style === "muted" ? (
415
+ <span fg={props.api.theme.current.textMuted}>{segment.text}</span>
416
+ ) : (
417
+ <b>{segment.text}</b>
418
+ ),
419
+ )
420
+ : row().text || " "}
405
421
  </text>
406
- ))}
407
- </box>
422
+ )}
423
+ </Index>
408
424
  </box>
409
425
  </Show>
410
426
  );
@@ -468,8 +484,6 @@ function SessionPromptWithCompactStatus(props: {
468
484
  );
469
485
  }
470
486
 
471
- const PROMPT_BAR_WIDTH = 12;
472
-
473
487
  function shouldRenderPromptBar(
474
488
  bar: PromptBarState,
475
489
  ): bar is Extract<PromptBarState, { status: "ready" }> {
@@ -528,31 +542,20 @@ function buildPromptBarParts(params: {
528
542
  if (!shouldRenderPromptBar(bar)) return undefined;
529
543
  const entry = bar.entry;
530
544
  if (!entry) return undefined;
531
- const reset = entry.resetTimeIso
532
- ? formatResetCountdown(
533
- entry.resetTimeIso,
534
- isResetTimeDecimals(bar.resetTimeDecimals)
535
- ? { compactRounded: true, decimals: bar.resetTimeDecimals }
536
- : { spaced: bar.resetTimeSpaced },
537
- )
538
- : "";
539
- const runway = formatQuotaRunway(entry.runway);
540
545
 
541
546
  const hasPercent = Number.isFinite(entry.percentRemaining);
542
547
  if (entry.semanticSegment && !hasPercent) {
543
- return { label: entry.semanticSegment, barText: "", meta: reset };
548
+ const reset = entry.resetTimeIso
549
+ ? formatResetCountdown(
550
+ entry.resetTimeIso,
551
+ isResetTimeDecimals(bar.resetTimeDecimals)
552
+ ? { compactRounded: true, decimals: bar.resetTimeDecimals }
553
+ : { spaced: bar.resetTimeSpaced },
554
+ )
555
+ : "";
556
+ return { label: resolvePromptBarLabel(entry), barText: "", meta: reset };
544
557
  }
545
558
 
546
- const windowLabel =
547
- entry.semanticSegment ??
548
- extractSingleWindowWindowLabel(entry.label ?? "") ??
549
- extractSingleWindowWindowLabel(entry.name ?? "") ??
550
- "Quota";
551
- const percent = formatDisplayedPercentLabel(
552
- entry.percentRemaining ?? 0,
553
- bar.percentDisplayMode ?? "remaining",
554
- "bare",
555
- );
556
559
  const p = Math.min(
557
560
  100,
558
561
  resolveDisplayedPercent(entry.percentRemaining ?? 0, bar.percentDisplayMode ?? "remaining"),
@@ -571,11 +574,16 @@ function buildPromptBarParts(params: {
571
574
  barText = cells.join("") + "░".repeat(empty);
572
575
  }
573
576
  return {
574
- label: windowLabel,
577
+ label: resolvePromptBarLabel(entry),
575
578
  barText,
576
- meta: entry.semanticSegment
577
- ? [reset, runway ? `r/o ${runway}` : ""].filter(Boolean).join(" | ")
578
- : [percent, reset, runway ? `r/o ${runway}` : ""].filter(Boolean).join(" | "),
579
+ meta: formatPromptBarPercentMeta({
580
+ percentRemaining: entry.percentRemaining ?? 0,
581
+ percentDisplayMode: bar.percentDisplayMode,
582
+ resetTimeIso: entry.resetTimeIso,
583
+ resetTimeDecimals: bar.resetTimeDecimals,
584
+ resetTimeSpaced: bar.resetTimeSpaced,
585
+ runway: entry.runway,
586
+ }),
579
587
  };
580
588
  }
581
589
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slkiser/opencode-quota",
3
- "version": "4.10.0",
3
+ "version": "4.10.1",
4
4
  "description": "OpenCode quota & tokens usage with zero context window pollution. Supports GitHub Copilot, OpenAI (Plus/Pro), Qwen Code, Chutes AI, Synthetic, Google Antigravity, Z.ai coding plan and more.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -109,6 +109,10 @@
109
109
  "typecheck": "tsc --noEmit",
110
110
  "test": "vitest run",
111
111
  "test:watch": "vitest",
112
+ "test:stabilization:tui": "node scripts/test-stabilization-connected.mjs --tui",
113
+ "test:stabilization:tui:prompt-bar": "node scripts/test-stabilization-connected.mjs --tui --prompt-bar",
114
+ "test:stabilization:web": "node scripts/test-stabilization-connected.mjs --web",
115
+ "test:stabilization:fixtures": "node scripts/test-stabilization-fixtures.mjs",
112
116
  "verify": "pnpm run check && pnpm run verify:typescript-version && pnpm run verify:v4-history && pnpm run typecheck && pnpm run build && pnpm test && pnpm run test:four-surfaces && pnpm run verify:package-contents"
113
117
  }
114
118
  }