Skip to content

fix(tui): explicitly register spinner to survive Windows tree-shaking - #91

Merged
LeXwDeX merged 1 commit into
devfrom
fix/windows-spinner-register
Jul 9, 2026
Merged

fix(tui): explicitly register spinner to survive Windows tree-shaking#91
LeXwDeX merged 1 commit into
devfrom
fix/windows-spinner-register

Conversation

@LeXwDeX

@LeXwDeX LeXwDeX commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Problem

The Windows release binary (opencode.exe) crashes at runtime with [Reconciler] Unknown component type: spinner the first time <spinner> renders. The prior fix (#90, lockfile dedupe) did not stop it.

Root cause

On a Windows host, bun build drops the side-effect-only import "opentui-spinner/solid". That package declares sideEffects: ["./dist/solid.mjs"] as a forward-slash glob, which Bun does not match against backslash paths on a Windows builder. The extend({ spinner }) call inside it never executes, so the reconciler has no spinner component registered. macOS/Linux match fine (forward-slash paths), which is why only Windows crashes.

Verified against the released v1.17.11-dev.10 artifacts:

Binary Spinner interval must be a finite createPulse requires at least one color
opencode.exe (Windows) 0 0
opencode (macOS) 1 1

The Windows binary contains zero spinner runtime code — the registration was tree-shaken out.

Fix

Register the spinner explicitly via extend({ spinner: SpinnerRenderable }) in a local module imported by every <spinner> render path. The explicit extend(...) carries a real value dependency the bundler cannot drop.

  • packages/tui/src/ui/spinner-register.ts (new) — used by the TUI
  • packages/opencode/src/cli/cmd/run/spinner-register.ts (new) — used by the opencode run footer
  • 4 call sites swap import "opentui-spinner/solid" → the local registration module

The retained opentui-spinner/solid side-effect import is not redundant: it provides JSX type augmentation for <spinner>. At runtime it re-registers too, but extend is Object.assign over a flat catalogue, so double registration is harmless. This mirrors the existing extend({ go_upsell_art }) pattern in packages/tui/src/component/bg-pulse.tsx.

Verification

  • bun typecheck in packages/tui — PASS
  • bun typecheck in packages/opencode — PASS
  • Local ./packages/opencode/script/build.ts --single --skip-install --skip-embed-web-ui — PASS, smoke test --version PASS
  • Local compiled binary string inspection: spinner runtime strings present (were absent in the failing Windows release)

Follow-up (not in this PR)

A real end-to-end fix confirmation requires the Windows CI build. Suggested: add a post-build check on the Windows artifact that greps opencode.exe for Spinner interval must be a finite (>0) so this regression cannot recur silently.

Checklist

  • Branch cut from main, PR targets dev
  • Conventional commit title
  • Only spinner-related files changed (6 files, no unrelated diff)

On a Windows host, `bun build` drops the side-effect-only
`import "opentui-spinner/solid"` because the package's `sideEffects`
path glob does not match backslash paths. The `extend({ spinner })`
call inside it never runs, so the bundled Windows binary crashes at
runtime with `[Reconciler] Unknown component type: spinner` the first
time `<spinner>` renders. macOS/Linux are unaffected (forward-slash
paths match), which is why the prior lockfile dedupe fix did not stop
the Windows crash.

Verified the root cause against the released v1.17.11-dev.10 artifacts:
the Windows `opencode.exe` contains zero spinner runtime strings
("Spinner interval must be a finite", "createPulse requires at least
one color"), while the macOS binary from the same release contains
them — confirming the registration code was tree-shaken out on Windows.

Fix: register the spinner explicitly via `extend({ spinner:
SpinnerRenderable })` in a local module that each `<spinner>` render
path imports. This carries a real value dependency the bundler cannot
drop. The retained `opentui-spinner/solid` side-effect import is kept
for JSX type augmentation; `extend` is `Object.assign` over a flat
catalogue, so the resulting double registration on macOS/Linux is
harmless.

This mirrors the existing `extend({ go_upsell_art })` pattern in
packages/tui/src/component/bg-pulse.tsx.
@LeXwDeX
LeXwDeX enabled auto-merge July 9, 2026 03:42
@LeXwDeX
LeXwDeX merged commit 4d70746 into dev Jul 9, 2026
4 checks passed
@LeXwDeX
LeXwDeX deleted the fix/windows-spinner-register branch July 9, 2026 03:43
LeXwDeX added a commit that referenced this pull request Jul 10, 2026
Three improvements for offline / restricted-network startup that are not
covered by the already-merged offline-catalog PRs (#88-#91):

1. Plugin SDK offline bundling: build.ts copies the plugin source into a
   vendored directory next to each binary. At runtime npm.install resolves
   @opencode-ai/plugin via three-tier fallback — project-local > bundled
   copy > registry — so plugin installs succeed without network access.

2. dependencyVersion(): only pin the version on stable latest releases
   (channel=latest + pure semver x.y.z). Fork/prerelease/local builds get
   undefined so the install doesn't request a non-existent registry version.

3. Reference offline degradation: only register a Git reference when its
   cache directory actually exists, and update the entry after a successful
   refresh. Missing references are omitted instead of carrying a stale path.
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.

1 participant