Fixes: theme picker crash, dialog ErrorBoundary, control-server hardening, lazy themes, stderr styling, /new race#76
Merged
Conversation
Error-ish gateway.stderr lines now render as styled error rows instead of plain system lines, and the 200-char truncation on the transcript row is gone — full line passes through. The gw.logs ring buffer already captures every stderr line untruncated via GatewayClient.log(), so /logs (gw.tail) still offers the complete traceback regardless of what the transcript surfaces.
newSession mirrored switchProfile's cleanup except for the gateway sid
clear. Without gw.setSession("") up front, any RPC or event emitted in
the window between reset() and setSession(new) carried the outgoing
session_id via gateway-client's auto-injection — a narrow race where
stale-sid events can be misattributed to the new session.
Adds a MockGateway-backed regression test: asserts the second
session.create (from /new) has no auto-injected session_id, and that
session.close still receives prev explicitly.
…l.md Bun.serve without hostname binds 0.0.0.0 — CONTROL=1 was silently network-reachable. Default CONTROL_BIND=127.0.0.1. If the user overrides to a non-loopback host, start() writes a yellow stderr banner and AppInner raises a 15s warning toast so the exposure is never silent. warningFor(on, bind, port) factored out so tests exercise the decision without re-importing the module under different env (BIND is captured at module load, mirroring the existing `enabled` pattern). docs/control.md covers what it is, env vars, endpoints, security posture, and the ptyrun/e2e use case.
…Boundary CommandProvider.register used to bump a state counter on add and on cleanup. Nothing downstream actually read the counter — open() and the palette's useKeyboard read the registry lazily at press time, so the re-render did no work. But when a caller's register effect had a dep that flipped mid-commit (slash.tsx lists themeCtx; the theme picker fires ctx.set on every arrow via onMove), the cleanup's setState rescheduled CommandProvider during its own passive-unmount phase and the nested-update limit tripped. Switch the registry to a pure ref; (un)register is now a plain Map write. Wrap the dialog overlay in a class Boundary so the next latent dialog loop (Rollback, Branch, Eikon Generate and the theme picker's own DialogSelect current/onMove interplay) degrades to a dismissed dialog + toast instead of killing the TUI. Boundary sits inside DialogProvider so it can call clear() + toast.error() directly. Tests: test/command-registry.test.tsx covers the register contract under rapid mount/unmount bursts; test/dialog-boundary.test.tsx mounts a dialog whose body throws and asserts the Boundary catches, dismisses, and the error surfaces via toast.
Drop the static import of every theme body from src/theme/builtin.ts.
Names + a few preview colors live in a generated manifest; theme
bodies load on first set() via dynamic import(`./themes/${name}.json`)
and are memoized in src/theme/load.ts.
src/index.tsx primes the active theme before render() so the first
frame paints without a fallback flicker. test/preload.ts does the
same for every bun test mount.
New: scripts/gen-theme-manifest.ts regenerates src/theme/manifest.ts
from the JSONs on disk — run when a theme is added, removed, or its
primary/accent/background changes.
…et no-ops on unchanged value The registry setRevision removal was necessary but not sufficient: the remaining loop was DialogSelect's cursor-sync effect (cursor <- index of props.current) ping-ponging with the move-notify effect (onMove -> ctx.set -> props.current). Gate onMove behind a moved ref that only keyboard nav and mouse hover set. prefs.set also returns early when the value is unchanged so redundant preview writes don't notify subscribers.
Contributor
|
🎉 This PR is included in version 1.7.1-dev.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Merged
Contributor
|
🎉 This PR is included in version 1.7.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Batch of small fixes from the wiki-driven audit (herm-wiki debt register), merged per-fix branches plus two follow-ups found during integration review.
Fixes
/themeand navigating blew up withMaximum update depth exceededand killed the TUI.CommandProvider.registerno longer bumps state on (un)register; the registry is a pure ref read lazily at keypress time.DialogHostis wrapped in an ErrorBoundary: a throw inside any dialog dismisses it and toasts the error instead of crashing the app.DialogSelect: the cursor-sync effect (cursor ← index(props.current)) and the live-preview effect (onMove → ctx.set → props.current) fed each other.onMovenow fires only for user-driven moves (keyboard nav / mouse hover), andprefs.setno-ops when the value is unchanged./new—newSessionnow clears the gateway's active sid beforesession.create, mirroringswitchProfile, so events in the gap aren't attributed to the outgoing session.kind: "error"in the transcript (previously a plain system line truncated at 200 chars); the full line is kept, and/logsstill carries the untruncated ring.127.0.0.1by default, warns (stderr + toast) whenCONTROL_BINDpoints at a non-loopback host, and the control server is now documented indocs/control.md.name+ preview colors) backs the picker, and the active theme is primed before first render.terminal.resizeRPC is now documented as intentional insrc/app/useSession.ts.dev.Tests
bunx tsc --noEmitclean,bun test1050 pass / 0 fail,bun run buildOK./themeopens, navigates with live preview, selects, and reverts on Esc with no error toast.Notes
dev. Individual fix branches are pushed astask/t_*if separate review is preferred.