Skip to content

feat(opencode): collapsible TUI sidebar#58

Merged
ualtinok merged 11 commits into
cortexkit:mainfrom
iceteaSA:feat/tui-sidebar-collapse
Jun 4, 2026
Merged

feat(opencode): collapsible TUI sidebar#58
ualtinok merged 11 commits into
cortexkit:mainfrom
iceteaSA:feat/tui-sidebar-collapse

Conversation

@iceteaSA
Copy link
Copy Markdown
Contributor

@iceteaSA iceteaSA commented Jun 4, 2026

Summary

Adds a click-to-collapse toggle to the OpenCode quota sidebar, mirroring cortexkit's aft plugin.

  • Local, non-persisted collapse state; onMouseDown on the header toggles it (resets to expanded on restart).
  • Header: /▶ CLAUDE glyph + plugin version (or the LIMITED badge when degraded).
  • Collapsed view: the active account's name + 5h usage % ● (usage-toned), plus a Mode fast row when fast mode is on. Em dash when no 5h window.
  • Expanded view unchanged, gated on !collapsed() || !hasData() so it never goes blank.
  • resolveActiveAccount pure helper (unit-tested) resolves the active account from activeId.

Changes

  • packages/opencode/src/sidebar-state.tsresolveActiveAccount helper.
  • packages/opencode/src/tui.tsx — collapse signal, header (glyph + version), collapsed render.
  • README.md — sidebar docs.

Tests

  • 6 unit tests for resolveActiveAccount. Full suite green (typecheck + test + build).

Dependency

Stacked on #57 (stale fallback quota / clobbered active account). The collapsed view shows the active account, which relies on the BUG 2 activeId fix in #57. Merge #57 first — until then this PR's diff includes #57's commits.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Adds a click-to-collapse toggle to the OpenCode quota sidebar with a compact view and versioned header. Preserves the active fallback during async quota refreshes and live-updates after background fallback storage changes.

  • New Features

    • Click the CLAUDE header to collapse/expand; shows ▼/▶ and v{version} (read from package.json) or LIMITED when degraded.
    • Collapsed view: active account name, 5h usage percent with tone dot, plus a Mode fast row when enabled. Collapse state is per session.
    • resolveActiveAccount selects the active account for the collapsed view (with a defensive enabled guard).
    • Sidebar stays visible when data clears while collapsed.
  • Bug Fixes

    • Prevent async main quota refresh from clobbering the active fallback by reusing the last routing via refreshSidebarQuota.
    • Refresh sidebar quota after background fallback storage changes via onFallbackStorageChanged, without a request.
    • Suppress a false-positive a11y lint warning on the interactive header box.

Written for commit 5959f45. Summary will update on new commits.

Review in cubic

Greptile Summary

This PR adds a click-to-collapse toggle to the OpenCode quota sidebar and fixes a bug where async background quota refreshes would clobber the active fallback account in the sidebar state. The collapse state is session-local, the header gains a ▼/▶ glyph plus plugin version (or LIMITED badge when degraded), and the collapsed view shows the active account's 5h usage.

  • Collapse toggle (tui.tsx): A collapsed Solid signal toggles on onMouseDown only when hasData() is true; collapsed and expanded views are gated with complementary Show predicates so the sidebar never goes blank when data clears while collapsed.
  • Clobber fix (index.ts): lastSidebarRouting captures the last explicit routing decision; refreshSidebarQuota() replays it during async quota refreshes instead of unconditionally resetting activeId to 'main'.
  • resolveActiveAccount (sidebar-state.ts): New pure helper with 6 unit tests mapping activeId{id, name, quota} for the collapsed view; onFallbackStorageChanged callback in accounts.ts triggers a sidebar re-render after background storage saves.

Confidence Score: 5/5

Safe to merge — changes are confined to TUI rendering and sidebar state plumbing, with no load-bearing auth or quota logic altered.

All new code paths are guarded (null checks on latestGetAuth, hasData() gate on collapse toggle, defensive enabled check in resolveActiveAccount), the clobber fix is straightforward and well-tested with a timing-sensitive integration test, and the reactive patterns in tui.tsx are idiomatic Solid.js. No data-loss or auth boundary concerns introduced.

No files require special attention.

Important Files Changed

Filename Overview
packages/opencode/src/tui.tsx Adds collapsed signal, CollapsedRow component, header glyph + version display, and conditional collapsed/expanded rendering; reactive patterns are correct for Solid.js.
packages/opencode/src/index.ts Introduces lastSidebarRouting and refreshSidebarQuota to prevent async quota refreshes from clobbering the active fallback account in the sidebar; logic and guards are sound.
packages/opencode/src/sidebar-state.ts Adds resolveActiveAccount pure helper; correctly handles main/undefined/unmatched/disabled-fallback cases with a well-placed defensive guard.
packages/core/src/accounts.ts Threads onFallbackStorageChanged callback through FallbackAccountManager; callback fires once after save() when storage changes, sequencing is correct.
packages/opencode/src/tests/index.test.ts Adds two integration tests covering the async-clobber regression and background-fallback-refresh-updates-sidebar scenarios; timing approach with Bun.sleep is reasonable.
packages/opencode/src/tests/sidebar-state.test.ts New unit test file covering all resolveActiveAccount branches including main, enabled/disabled fallback, undefined, and unmatched activeId.
packages/opencode/src/tests/accounts.test.ts Adds test verifying onFallbackStorageChanged fires exactly once when refreshQuotaForDueAccounts saves a changed storage; correctly validates the new callback contract.
README.md Single-sentence doc addition for the collapse toggle; accurate and concise.

Reviews (2): Last reviewed commit: "docs(opencode): clarify version path lay..." | Re-trigger Greptile

iceteaSA added 2 commits June 4, 2026 08:16
…nged

The callback fires on any persisted fallback storage change (token refresh,
quota update, or error recording), not only quota fetches. Rename per review
feedback for an accurate API name.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 8 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant TUI as OpenCode TUI
    participant Plugin as AnthropicAuthPlugin
    participant FAM as FallbackAccountManager
    participant QM as QuotaManager
    participant Storage as Account Storage
    participant Fetch as External APIs

    Note over TUI,Fetch: NEW: Collapsible Sidebar + Active Account Fix

    TUI->>TUI: onMouseDown on header
    alt Has Data
        TUI->>TUI: toggle collapsed() signal
    end

    TUI->>TUI: resolveActiveAccount(state)
    Note over TUI: NEW: Pure helper from sidebar-state.ts
    TUI->>TUI: Check activeId
    alt activeId === 'main' or unmatched/disabled
        TUI->>TUI: Return main account
    else activeId matches enabled fallback
        TUI->>TUI: Return fallback account
    end

    TUI->>TUI: Render collapsed view
    Note over TUI: NEW: Show active account name + 5h % ● or em dash

    Plugin->>FAM: new FallbackAccountManager({onFallbackQuotaFetched})
    Note over Plugin: NEW: register callback for background quota updates
    FAM->>FAM: refreshQuotaForDueAccounts()
    FAM->>Storage: Save changed quotas
    FAM-->>Plugin: onFallbackQuotaFetched()
    Plugin->>Plugin: refreshSidebarQuota()
    Note over Plugin: NEW: Re-write sidebar with last known routing
    Plugin->>Storage: loadAccounts()
    Plugin->>Storage: writeSidebarState(lastSidebarRouting)
    Plugin-->>TUI: Updated sidebar state

    Plugin->>Plugin: writeSidebarState()
    Plugin->>Plugin: Store lastSidebarRouting
    Note over Plugin: NEW: Remember last activeId/route

    Plugin->>QM: refreshMain(access)
    QM->>Fetch: GET /api/oauth/usage
    Fetch-->>QM: Quota data
    QM-->>Plugin: .then()
    Plugin->>Plugin: refreshSidebarQuota()
    Note over Plugin: CHANGED: Uses last routing instead of resetting to 'main'
    Plugin-->>TUI: Sidebar updated without clobbering active account

    alt Collapsed && Has Data
        TUI->>TUI: Show active account + 5h quota
        alt Fast Mode On
            TUI->>TUI: Show "Mode fast" row
        end
    else !Collapsed || !Has Data
        TUI->>TUI: Show expanded full sidebar
        TUI->>TUI: Show quota sections, routing, cache, health
    end

    Note over TUI: CHANGED: Header shows ▼/▶ + version or LIMITED badge
Loading

Re-trigger cubic

Comment thread packages/opencode/src/tui.tsx
Comment thread packages/opencode/src/tui.tsx
Comment thread packages/opencode/src/sidebar-state.ts
@ualtinok ualtinok merged commit 0ea0bcc into cortexkit:main Jun 4, 2026
4 checks passed
@iceteaSA iceteaSA deleted the feat/tui-sidebar-collapse branch June 4, 2026 07:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants