add opus
ober
779a600f908737f41904d052d1266a4dce2b51c1
new file mode 100644 --- /dev/null +++ b/opus-plan.md @@ -0,0 +1,467 @@ +# jcode → opencode UX Parity — Plan v2 + +**Date:** 2026-05-05 +**Goal:** Mirror the genuinely-useful UX patterns from opencode, adapted to jcode's +Scheme + termbox2 stack. Parity by deliberate translation, not by line-by-line copy. +**Reference:** `~/mine/opencode/packages/opencode/src/cli/cmd/tui/` + +--- + +## Premise + +opencode has a polished TUI. Some of that polish is foundational (theme system depth, +message visual hierarchy, ephemeral feedback). Some of it is chrome (logo, rotating +tips, agent-colored accents). A good parity plan distinguishes between the two and +sequences foundations before features. + +This plan is that pass over `qwen-plan.md`. Same goal, different sequencing, harsher +choices about what tree-sitter-shaped things are worth building. + +--- + +## What opencode does, ranked by actual UX value + +| Pattern | Value | Verdict | +|---------|-------|---------| +| Colored left borders on messages | Real (P1: messages blur in long sessions) | **Mirror** | +| Toast notifications for ephemeral state | Real (P2: silent state changes confuse) | **Mirror** | +| Syntax highlighting in code blocks | Real (P3: walls of green) | **Mirror, lightweight** | +| Theme variety + custom theme JSON | Real (P4: users have strong preferences) | **Mirror** | +| Stacked dialog system | Real for permission flows | **Mirror** | +| Dimmed thinking display | Real with reasoning models | **Mirror** | +| Auto dark/light detection | Real (config friction) | **Mirror** | +| Command palette (Ctrl+K) | Real for power users | **Mirror** | +| Leader-key chords | Real for vim users | **Mirror** | +| ASCII logo + home screen | Aesthetic (visual identity) | **Mirror, minimal** | +| Mouse text selection | Real (table stakes in 2026) | **Mirror** | +| Agent-colored accent | Aesthetic | **Defer** — interacts badly with Chez parameter inheritance (see risks) | +| Session timeline / fork UI | Niche | **Defer** — no observed demand | +| Richer sidebar (file tree, MCP, LSP) | Real, but coupled to other work | **Defer** — fold in with each integration, not as UI exercise | +| Plugin UI slots | Speculative | **Defer** — no plugins exist yet | +| Client/server split | Architectural | **Out of scope** — separate decision | + +--- + +## Sequencing principle: foundations before features + +Order matters because later items depend on earlier ones. Specifically: + +- **Face slots first**: every theme, toast, thinking block, and syntax token resolves + through the face system. Adding a slot retroactively means every theme has to gain + it. Front-load this. +- **Theme JSON loader before themes**: each new theme is ~150 lines of Scheme or one + JSON file. Build the loader, then drop in JSON files. +- **Stacked dialogs before command palette**: the palette renders as a dialog. +- **Borders before logo**: borders set the visual language for messages; logo is + pure intro chrome that follows. + +`qwen-plan.md` ordered by perceived effort. This plan orders by dependency. + +--- + +## Phase 1 — Foundation (≈2 weeks) + +Nothing user-visible blocks on these in isolation, but everything else needs them. + +### 1.1 Expand face system + +**File:** `src/jcode/ui/tui-theme.ss` +**Effort:** ~200 LOC across all 3 existing themes + +Add the slots that later phases will use, applied to dark/light/gruvbox in one pass: + +``` +;; Toast (Phase 2.1) +toast-error-border toast-warning-border toast-success-border toast-info-border +toast-bg toast-title toast-message + +;; Thinking (Phase 2.4) +thinking-text thinking-border thinking-header + +;; Syntax (Phase 2.2) +syntax-comment syntax-keyword syntax-function syntax-variable +syntax-string syntax-number syntax-type syntax-operator +syntax-punctuation + +;; Selection (Phase 3.5) +selection-bg + +;; Leader / palette (Phase 3.2, 3.3) +leader-indicator leader-dim +palette-match palette-keybind palette-category + +;; Home (Phase 3.1) +home-logo-fg home-logo-shadow home-tip-text home-tip-icon + +;; Borders (Phase 1.2) +msg-border-user msg-border-assistant msg-border-tool msg-border-error +``` + +This is mechanical and repetitive. Do it in one go, not piecemeal. + +### 1.2 Colored left borders on message blocks + +**File:** `src/jcode/ui/tui-message.ss` +**Effort:** ~50 LOC + +In `render-msg-block!`: before rendering content, draw a 1-cell vertical border +from `y` to `y + height - 1` at column `x - 1`, using `tb-change-cell!` to set +fg/bg from the role-appropriate `msg-border-*` face. Shift content right by 2 +columns (1 border + 1 padding). + +This is the single highest signal-to-LOC visual change in the plan. Ship it +early; subsequent items build on the visual language it establishes. + +### 1.3 opencode-compatible theme JSON loader + +**File:** `src/jcode/ui/tui-theme.ss` (extend), `src/jcode/core/config.ss` +**Effort:** ~150 LOC + +Parse opencode's `{ "$schema", "defs", "theme" }` schema. Resolve `defs`, then +walk `theme` slots. For slots with `{ "dark": ..., "light": ... }` variants, +emit two jcode themes (`name-dark`, `name-light`) so users get both. + +Use the slot mapping table in the appendix below. Imports `(std text json)` — +pattern is already established elsewhere in the codebase. + +Scan `~/.jcode/themes/*.json` and `./.jcode/themes/*.json` at startup. Register +loaded themes alongside built-ins. + +### 1.4 Import 8 themes from opencode + +**Files:** drop into `themes/` directory (new); register at startup +**Effort:** ~50 LOC for the registration; theme content is JSON files copied verbatim + +Pull these from `~/mine/opencode/.../context/theme/` — most-requested first: + +- dracula +- catppuccin (becomes catppuccin-mocha + catppuccin-latte via dark/light split) +- tokyonight (becomes tokyonight + tokyonight-day) +- nord +- gruvbox (already have ours, but theirs may differ — compare) +- monokai + +Eight themes ≠ thirty. Eight covers ~90% of the "I want my favorite theme" +audience without bloating the binary. + +--- + +## Phase 2 — Visible UX patterns (≈3 weeks) + +What users will actually notice. Foundation from Phase 1 makes each of these +small. + +### 2.1 Toast notifications + +**File:** new `src/jcode/ui/tui-toast.ss`, modified `src/jcode/ui/tui.ss` +**Effort:** ~200 LOC + +```scheme +(defstruct toast (variant title message dismiss-at)) +``` + +State on the app: a list of active toasts. Render top-right at +`(width - toast-width - 2, 2 + offset)` per toast, stacked downward. 3s default +auto-dismiss. Any keypress dismisses the oldest. Variant maps to +`toast-{error,warning,success,info}-border`. + +Wire to: +- agent errors (currently silent or end up in message thread) +- `/save` and session checkpoints +- theme switch confirmation +- config reload +- tool completion when long-running (>5s) + +**Pitfall:** toasts run a dismiss timer on a green thread. Capture and +re-parameterize `current-error-port` and `log-level` per +`feedback_chez_parameter_thread_inheritance.md` or the toast thread will +deadlock the log mutex. + +### 2.2 Lightweight syntax highlighting + +**File:** new `src/jcode/ui/tui-syntax.ss`, modified `src/jcode/ui/tui-markdown.ss` +**Effort:** ~300 LOC + +Per-language tokenizers (regex-based). Languages, in priority order: + +1. **scheme** — we're a Scheme tool, our own code blocks should look right +2. **bash** — agent runs lots of shell +3. **python** — common request language +4. **javascript / typescript** — same +5. **json** — config files +6. **rust, go, c** — fill out the long tail + +Each tokenizer is a list of `(regex face-name)` pairs scanned in order, longest +match wins. Tokens map to `syntax-*` faces. + +**Why not tree-sitter:** +- FFI to a C++ library, with parser binaries per language +- Blocking parse calls would need `__collect_safe` declarations + (`feedback_ffi_collect_safe.md`) +- Streaming code blocks during LLM output would need incremental reparsing +- Maintenance: tree-sitter parsers change; we'd track upstream forever +- Accuracy advantage over keyword scanning is marginal for short code blocks + +Approach 2 from `qwen-plan.md` was right; "Approach 1" was a footnote that should +have been the recommendation. + +### 2.3 Stacked dialog system + +**File:** `src/jcode/ui/tui-dialog.ss` +**Effort:** ~150 LOC modified + +```scheme +(defstruct dialog-stack (dialogs backdrop?)) +``` + +Replace single active dialog with a stack. Push opens, Escape pops. Backdrop is +a one-pass overlay rendering `░` characters with dimmed fg over the entire +non-dialog screen, then the top dialog at full brightness. + +Slight visual offset for stacked dialogs (1-2 cells down/right per layer) so +the stack is visible. + +Enables: permission prompt → confirmation → result alert without losing the +parent. Also a prerequisite for the command palette in 3.2. + +### 2.4 Dimmed thinking display + +**File:** `src/jcode/ui/tui-message.ss`, `src/jcode/ui/tui-markdown.ss` +**Effort:** ~80 LOC + +Detect thinking content from provider metadata (Anthropic extended thinking, +or `<thinking>` tags from other providers). Render the block with: + +- `thinking-text` face (dim + italic) +- `thinking-border` left border (muted) +- Header line `⚙ Thinking…` collapsed by default; expand on click/keypress +- Show elapsed time when collapsed + +Faces already added in 1.1, so this is just rendering logic. + +--- + +## Phase 3 — Power features (≈4 weeks) + +Items individual users will love but aren't universal needs. + +### 3.1 Home screen + ASCII logo + +**Files:** new `src/jcode/ui/tui-home.ss`, new `src/jcode/ui/tui-logo.ss` +**Effort:** ~250 LOC total + +Half-block logo using `▀` (U+2580): fg = letter color, bg = shadow tint. One +ASCII grid for the left half (shadow), one for the right (bold). Render with +`tb-change-cell!` per glyph. + +Logo design: "jcode" in 4-row half-block typography. Small (≤40 cols wide) so +it works on narrow terminals. Skip rendering on terminals <60 cols. + +Home screen layout: +``` + [ logo ] + + ┌─────────────────────────┐ + │ > Try a TODO… │ + └─────────────────────────┘ + + 💡 Press Ctrl+B to toggle the sidebar +``` + +Tip rotates every 8s through ~6 messages (sidebar, /help, theme cycle, multi-line +input, plan/build mode, scrolling). + +State: add `home?` to app-state. True when message list is empty. Transition +to session view on first message submit. + +### 3.2 Command palette (Ctrl+K) + +**File:** new `src/jcode/ui/tui-command-palette.ss`, modified `src/jcode/ui/tui-keys.ss` +**Effort:** ~300 LOC + +Renders as a dialog (uses the stack from 2.3). Fuzzy match across: + +- Slash commands (existing registry — reuse, don't duplicate) +- Config toggles (`/sidebar`, `/theme`, `/display thinking`) +- Session ops (`/session new|list|rename`) +- Models, agents + +Categorized rendering. Keybind on right side of each entry. Recent/frecency +ranking at top. + +Critical: this must be a *view* over the existing slash command registry, not +a parallel data source. Discoverability is a UI concern, not a data concern. + +### 3.3 Leader-key chord system + +**File:** `src/jcode/ui/tui-keys.ss` +**Effort:** ~200 LOC + +Default leader: Space. 2s chord window. Indicator shown in status bar during +the window. ESC or timeout cancels. + +Bindings tree: + +``` +<leader> s n → /session new +<leader> s l → /session list +<leader> m c → /model cycle +<leader> a c → /agent cycle +<leader> t → /theme cycle +<leader> b → /sidebar toggle +<leader> k → command palette +<leader> ? → leader help overlay +``` + +Configurable from `jcode.json`. Implementation: a key-tree walker with state. + +**Pitfall:** the 2s timeout runs on a green thread → parameter inheritance +applies (same as 2.1). + +### 3.4 Auto dark/light detection + +**File:** `src/jcode/ui/tui.ss`, `src/jcode/ui/tui-theme.ss` +**Effort:** ~80 LOC + +Send `\x1b]11;?\x07` at startup. Read response with a 100ms timeout. Parse +`rgb:RRRR/GGGG/BBBB` form. Compute luminance, choose variant. + +Add `theme: auto` config option. Default off — opt-in only, so it doesn't +surprise existing users. + +### 3.5 Mouse text selection + +**File:** `src/jcode/ui/tui.ss`, `src/jcode/ui/tui-ffi.ss`, `src/jcode/ui/tui-message.ss` +**Effort:** ~250 LOC + +termbox2 supports mouse via `TB_INPUT_MOUSE` — we may already enable it for +wheel scroll. Extend to capture press, drag, release events. + +Map screen coordinates to message-thread byte offsets (the existing rendered +line buffer needs to retain source positions). Highlight selection range with +`selection-bg`. Copy on Ctrl+C or right-click via OSC 52 (`\x1b]52;c;<base64>\x07`), +which works in most modern terminals without shell-out to pbcopy/xclip. + +Most expensive item in this phase. Schedule last. + +--- + +## Phase 4 — Deferred + +Listed for completeness; revisit only on real signal. + +- **Agent-colored accent** — every agent switch rebinds a parameter; tied to + the parameter-inheritance pitfall. Cute, not worth the stability cost. +- **Session timeline / fork** — significant new UI, no observed demand. +- **Richer sidebar panels** — should ship alongside MCP/LSP integration work, + not as standalone UI. +- **Plugin UI slots** — no plugins exist; building a slot framework before + there's content to fill it is speculative. +- **Client/server split** — architectural, separate decision, out of UX scope. +- **Scroll acceleration** — minor; defer until a user complains. + +--- + +## Codebase-specific risks + +Read before starting any item. These have all bitten before, per memory. + +1. **`.ss` editing**: ALWAYS use `jerboa_balanced_replace`, ALWAYS run + `jerboa_check_balance` after. Never touch `.ss` files with `Edit`. +2. **Parameter inheritance**: any green thread for timers (toast dismiss, + leader timeout, tip rotation, theme watch) must capture + `current-error-port` and `log-level` and re-parameterize on the spawned + thread. Otherwise the TUI deadlocks the log mutex. + Ref: `feedback_chez_parameter_thread_inheritance.md`. +3. **Plugin boot deps**: every new module added to the binary's import graph + must be listed in `build-binary.ss external-libs`. WPO inlining is not + enough. + Ref: `feedback_chez_plugin_boot_deps.md`. +4. **Build before commit**: `make binary && ./jcode --help` must succeed + before any commit. The user has flagged this 3+ times. + Ref: `feedback_always_build_test.md`. +5. **Forward refs**: jerbuild `.sls` output doesn't tolerate forward + references between top-level forms within a file. If a new module starts + throwing unbound-identifier errors, check definition order. + Ref: `feedback_build_gotchas.md`. + +--- + +## Appendix — opencode → jcode slot mapping + +For the JSON loader (1.3). Same content as `qwen-plan.md` but condensed. + +``` +opencode → jcode face(s) +───────────────────────────────────────────────────────────── +primary → user-label, input-prompt +secondary → status-mode-plan +accent → spinner, tool-name, heading, status-mode-build +error → error +warning → status-mode-plan, toast-warning-border +success → status-mode-build, toast-success-border +info → tool-name, toast-info-border +text → default, assistant-text, user-text +textMuted → dim, tool-result, status-dim +background → default (bg) +backgroundPanel → sidebar-bg, completion-item (bg), toast-bg +backgroundElement → completion-selected (bg), code-inline (bg) +border → tool-border, divider, sidebar-divider, msg-border-* +borderActive → dialog-border, sidebar-selected (border) +borderSubtle → horizontal-rule, blockquote-border, thinking-border +diffAdded/Removed/Context → diff-added / diff-removed / diff-context +diffHunkHeader → diff-hunk +markdownHeading → heading +markdownLink → link +markdownCode → code-block, code-inline +markdownBlockQuote → blockquote +markdownEmph → italic +markdownStrong → bold +markdownHorizontalRule → horizontal-rule +markdownListItem → list-bullet +syntax{Comment,Keyword,…} → syntax-{comment,keyword,…} +``` + +Slots with `{ dark, light }` variants → emit a paired `<theme>-dark` / `<theme>-light` +in jcode's theme registry, not a single theme that picks one. + +--- + +## File-by-file summary + +**New (10 files)** +- `themes/*.json` — 8 imported theme files +- `src/jcode/ui/tui-toast.ss` +- `src/jcode/ui/tui-syntax.ss` +- `src/jcode/ui/tui-home.ss` +- `src/jcode/ui/tui-logo.ss` +- `src/jcode/ui/tui-command-palette.ss` + +**Modified** +- `src/jcode/ui/tui-theme.ss` — face slots, JSON loader (Phase 1.1, 1.3) +- `src/jcode/ui/tui-message.ss` — borders, thinking display (1.2, 2.4) +- `src/jcode/ui/tui-markdown.ss` — syntax hookup, thinking blocks (2.2, 2.4) +- `src/jcode/ui/tui-dialog.ss` — stacked dialogs (2.3) +- `src/jcode/ui/tui-keys.ss` — leader system, palette keybind (3.2, 3.3) +- `src/jcode/ui/tui-ffi.ss` — mouse drag events (3.5) +- `src/jcode/ui/tui.ss` — integration glue throughout +- `src/jcode/core/config.ss` — theme dir scan, `theme: auto`, leader bindings +- `build-binary.ss` — register every new module under `external-libs` + +--- + +## Differences from qwen-plan + +| | qwen-plan | this plan | +|---|---|---| +| Sequencing | By perceived effort | By dependency | +| Theme expansion | Add 10 themes by hand-coding faces | Add JSON loader, drop in 8 JSON files | +| Syntax highlighting | Tree-sitter (recommended) + keyword (footnote) | Keyword only; tree-sitter explicitly rejected | +| Face expansion | Spread across phases as features land | Front-loaded in Phase 1 | +| Phase 4 (architectural) | Listed with effort estimates | Marked out-of-scope for UX work | +| Risks section | Absent | Lists the 5 jcode-specific gotchas with memory refs | +| Agent-colored accent | Phase 3, with code sketch | Deferred — flagged as parameter-inheritance hazard | +| Logo / home screen | Phase 1 quick win + Phase 2 home | Combined in Phase 3; not a foundation item | + +Same destination, but with the work ordered so each item can land cleanly without +forcing later items to rewrite earlier ones.