Commit 08f23fd
feat(init): replace OpenTUI with Ink for the wizard UI (#885)
> **Stacked on top of #862** (OpenTUI / WizardUI scaffold). Once #862
merges, GitHub auto-retargets this PR to `main`.
## Summary
Replaces the OpenTUI implementation (Zig-compiled native binary, ~10.7
MB of binary cost, Bun-only) with
[Ink](https://github.com/vadimdemedes/ink) — pure JS + React, no native
bindings. Same `WizardUI` surface, same `WizardStore`, same sidebar
layout, same step checklist — different render primitives.
## Why
- **No native binary cost.** OpenTUI ships per-platform
`.so`/`.dylib`/`.dll` files via `bun:ffi`, inflating the binary by ~10.7
MB. Ink is pure JS, so the binary drops from ~118 MB → ~109 MB (-9.4
MB).
- **No alternate-screen flicker.** OpenTUI took over the
alternate-screen buffer; on dispose every trace of the run was wiped. We
had to replay a stripped-down transcript to stderr so users had any
scrollback. Ink renders inline.
- **Mature ecosystem.** `ink` + `ink-spinner` cover most of what we
hand-rolled in OpenTUI. Used by Wrangler, Gatsby, GitHub Copilot CLI,
and others.
## What's in this PR
Four commits, in order:
1. **`d1ca5f7a` feat(init): replace OpenTUI with Ink** — initial port.
Includes the `with { type: "file" }` workaround for Bun.compile bundling
React's CJS dev wrappers (otherwise hits a `__promiseAll` SyntaxError at
startup).
2. **`59900dbd` fix(init): make Ink select prompt actually respond to
arrow keys** — replaced `ink-select-input` with a hand-rolled `useInput`
implementation. The third-party component races with our store-driven
re-renders.
3. **`b4a591e2` fix(init): make Ink useInput actually deliver keystrokes
in Bun** — pass a fresh `/dev/tty` `ReadStream` to Ink's `stdin` option
to work around
[oven-sh/bun#6862](oven-sh/bun#6862) +
[vadimdemedes/ink#636](vadimdemedes/ink#636)
(Bun's `process.stdin` doesn't deliver `readable` events).
4. **`4a3e8354` fix(init): clear screen on dispose + tighten sidebar
layout** — `instance.clear()` before unmount so the wizard chrome
doesn't linger above the post-dispose summary; removed wasted rows
between sidebar panels.
## Things that stayed the same
- `WizardUI` interface (banner / intro / log / spinner / select /
multiselect / confirm / summary / cancel / outro / setStep /
recordFilesReading / markFilesAnalyzed)
- The external `WizardStore` + `useSyncExternalStore` subscription
pattern (renamed `opentui-store.ts` → `wizard-store.ts`)
- `file-tree.ts`, `sentry-tips.ts`, `types.ts` (unchanged in shape)
- Sidebar layout: tip card on top, step checklist in the middle,
files-read tree on the bottom
- Step progress checklist with implicit-skip back-fill
- Post-dispose chalk summary echoed after Ink unmounts
## Things that changed
- **Sidebar tree window vs. scrollbox.** Ink doesn't ship a scrollbox
primitive. The files-read panel now shows the *last* N rows that fit,
with a `↑ N earlier (scrolled)` hint when truncated. The tail-`f` UX
(newly-read files always visible) comes for free since the panel
re-renders to the bottom.
- **Multi-select.** Built directly on Ink's `useInput` (no third-party
multiselect component).
- **Cancellation.** OpenTUI's `keyHandler` was global; Ink's `useInput`
is per-component. Cancellation now hooks into:
- Each prompt's own `useInput` (handles `key.escape` and `key.ctrl &&
input === "c"` in raw mode where Node doesn't emit SIGINT).
- A top-level App-component `useInput` that intercepts Ctrl+C during
spinners (no prompt mounted).
- A `process.on("SIGINT", …)` fallback inside `InkUI` for the brief
window where raw mode flickers off.
## Bun-binary-only (same as OpenTUI was)
Ink's reconciler and the `yoga-layout` dependency use top-level await,
which esbuild can't emit in our CJS npm bundle. So Ink is bundled into
the Bun binary via the `with { type: "file" }` trick (same as OpenTUI
used) but excluded from `dist/index.cjs` entirely. Node users continue
to get `LoggingUI` — unchanged from before. This preserves AGENTS.md's
"no runtime dependencies" rule. `bun run check:deps` passes.
## Bun.compile workarounds (unavoidable)
- **`with { type: "file" }`** keeps `ink-app.tsx` out of esbuild's and
Bun.compile's static bundle graph. Without this, Bun.compile mangles
Ink's and React's CJS dev wrappers (it injects `__promiseAll` runtime
helpers in positions the IIFEs can't parse, producing `SyntaxError:
Unexpected identifier '__promiseAll'` at startup inside
`parse-keypress.js` or `react-jsx-runtime.development.js`).
- **`?bridge=1` query string** on the dynamic import bypasses Bun's
module-cache collision between the file-resource import and `await
import(path)` of the same absolute path.
- **`define: { 'process.env.NODE_ENV': '"production"' }`** on
`Bun.build` forces React to use its production builds.
- **`react-devtools-core`** installed as a devDep so Bun.compile can
resolve Ink's static reference (gated behind `process.env.DEV ===
"true"` at runtime → dead code in production).
## Files changed
**Added**
- `src/lib/init/ui/ink-app.tsx` — Ink React tree
- `src/lib/init/ui/ink-ui.ts` — `InkUI` bridge class
**Renamed**
- `src/lib/init/ui/opentui-store.ts` → `wizard-store.ts` (no logic
changes)
- `test/lib/init/ui/opentui-store.test.ts` → `wizard-store.test.ts`
**Deleted**
- `src/lib/init/ui/opentui-app.tsx`
- `src/lib/init/ui/opentui-ui.ts`
**Dep changes**
- Removed: `@opentui/core`, `@opentui/react`
- Added: `ink`, `ink-spinner`, `react-devtools-core` (all
devDependencies)
## Verification
- `bun run typecheck` (clean)
- `bun x ultracite check` (1 pre-existing warning, no new ones)
- `bun test --isolate test/lib/init/` (227 pass)
- `bun run check:deps` (no runtime dependencies)
- `SENTRY_CLIENT_ID=test bun run build` (binary 108.79 MB, **-9.4 MB**
vs OpenTUI's 118.23 MB)
- `SENTRY_CLIENT_ID=test bun run bundle` (npm 3.29 MB, unchanged)
- `./dist-bin/sentry-linux-x64 init --help` (renders cleanly)
- `node ./dist/bin.cjs init --help` (Node path renders cleanly)
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Sentry Bot <bot@sentry.io>
Co-authored-by: Miguel Betegón <miguelbetegongarcia@gmail.com>
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>1 parent 0307aad commit 08f23fd
50 files changed
Lines changed: 8996 additions & 846 deletions
File tree
- docs/src/fragments/commands
- plugins/sentry-cli/skills/sentry-cli/references
- script
- src
- commands
- lib/init
- ui
- test
- commands
- lib/init
- ui
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
36 | 56 | | |
37 | 57 | | |
38 | 58 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
14 | | - | |
| 13 | + | |
| 14 | + | |
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
13 | 12 | | |
14 | 13 | | |
15 | 14 | | |
| |||
21 | 20 | | |
22 | 21 | | |
23 | 22 | | |
| 23 | + | |
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| 33 | + | |
| 34 | + | |
33 | 35 | | |
34 | 36 | | |
35 | 37 | | |
36 | 38 | | |
37 | 39 | | |
38 | 40 | | |
| 41 | + | |
| 42 | + | |
39 | 43 | | |
40 | 44 | | |
41 | 45 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
20 | 20 | | |
21 | | - | |
| 21 | + | |
22 | 22 | | |
| 23 | + | |
23 | 24 | | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
30 | | - | |
31 | | - | |
| 31 | + | |
| 32 | + | |
32 | 33 | | |
33 | 34 | | |
34 | 35 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
124 | 124 | | |
125 | 125 | | |
126 | 126 | | |
127 | | - | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
128 | 148 | | |
129 | 149 | | |
130 | 150 | | |
| |||
295 | 315 | | |
296 | 316 | | |
297 | 317 | | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
298 | 337 | | |
299 | 338 | | |
300 | 339 | | |
| |||
480 | 519 | | |
481 | 520 | | |
482 | 521 | | |
483 | | - | |
484 | | - | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
485 | 528 | | |
486 | 529 | | |
487 | 530 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
215 | 215 | | |
216 | 216 | | |
217 | 217 | | |
218 | | - | |
219 | | - | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
220 | 242 | | |
221 | 243 | | |
222 | 244 | | |
| |||
278 | 300 | | |
279 | 301 | | |
280 | 302 | | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
281 | 317 | | |
282 | 318 | | |
283 | 319 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
3 | | - | |
4 | | - | |
5 | | - | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
6 | 16 | | |
7 | 17 | | |
8 | 18 | | |
9 | 19 | | |
10 | | - | |
| 20 | + | |
| 21 | + | |
11 | 22 | | |
12 | 23 | | |
13 | | - | |
14 | | - | |
| 24 | + | |
| 25 | + | |
15 | 26 | | |
16 | 27 | | |
17 | 28 | | |
| |||
21 | 32 | | |
22 | 33 | | |
23 | 34 | | |
24 | | - | |
25 | | - | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
26 | 40 | | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | | - | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
31 | 77 | | |
32 | 78 | | |
33 | 79 | | |
| |||
0 commit comments