ui: detect the terminal background and pick a light or dark palette - #115
Conversation
There was a problem hiding this comment.
Caution
Changes requested ❌ — 2 issues
Reviewed 473813c in 12 minutes, 7 seconds.
- Reviewed
1commit with278lines of code in5files - Ran
1review agent producing2comments where2were posted - This pipeline runs no gatekeeper, so findings are posted as written.
- View full details on ellipsis.dev
This review was created by . You can tag
@ellipsis in this pull request.
| const finish = (mode: ThemeMode | null): void => { | ||
| if (settled) return | ||
| settled = true | ||
| clearTimeout(timer) | ||
| stdin.off('data', onData) | ||
| stdin.setRawMode(wasRaw) | ||
| // Ink attaches its own stdin handling after this; leave the stream | ||
| // paused so a bare detection doesn't hold the process open. | ||
| stdin.pause() | ||
| resolve(mode) | ||
| } | ||
| const onData = (chunk: Buffer): void => { | ||
| buffer += chunk.toString('latin1') | ||
| const mode = modeFromOscReply(buffer) | ||
| if (mode) finish(mode) | ||
| else if (buffer.length > 64) finish(null) | ||
| } | ||
| const timer = setTimeout(() => finish(null), timeoutMs) | ||
| stdin.setRawMode(true) | ||
| stdin.resume() | ||
| stdin.on('data', onData) |
There was a problem hiding this comment.
finish() detaches the reader and pauses stdin without draining it, so any part of the OSC 11 reply that has not arrived yet is buffered and replayed into Ink as typed text.
The regex matches before the reply's BEL/ST terminator, and the 200ms timeout can fire before the reply arrives at all. I ran queryOsc11 against a fake TTY: with the reply split so the terminator lands in a second chunk, stdin still holds "\x1b\" after resolve; with the reply arriving at 260ms, stdin holds the entire "\x1b]11;rgb:fdfd/f6f6/e3e3\x1b\". Ink resumes that same stream, and its parser turns those bytes into keypresses with ctrl/meta/escape all false ("\x1b]", "11;rgb:fdfd/f6f6/e3e3", "\x1b\"), which ConnectApp.tsx:1467 feeds to insertAtCursor — the composer opens pre-filled with raw ESC bytes and hex garbage, and enter POSTs it as a message. Requiring the terminator in OSC_REPLY ((?:\x07|\x1b\\)) fixes the split-chunk half deterministically; the late-reply half needs the stdin reader to stay attached and discarding until just before render, or a \x1b[6n sent after the OSC query as an ordering barrier so a terminal that will not answer OSC 11 is detected by its CPR reply instead of by a timeout that races the answer.
| foreground: '#1c1b17', | ||
| muted: '#706f66', | ||
| cursor: '#175173', | ||
| success: '#10b981', |
There was a problem hiding this comment.
Light-mode success green is 2.5:1 on white — below the 3:1 floor every other value in this palette clears; darken it.
Contrast against #ffffff: cursor 8.5, foreground 17.2, syntaxString 7.6, muted 5.1, active 4.9, error 4.8, success 2.5. It is used as a foreground, not a fill — the completed-step gutter (transcriptRows.ts:675) and the closed-session dot in sessions.ts:71, which additionally applies dim on top.
| success: '#10b981', | |
| success: '#047857', |
Important
Detects the terminal's background color at startup and applies either a light or dark color palette accordingly. Uses OSC 11 (the standard terminal background query), falls back to the
COLORFGBGenvironment variable, and defaults to dark mode if neither method works.src/lib/terminalBackground.tswith detection logic — queries the terminal and interprets its response or environment hints to determine if a light or dark palette should render.src/lib/theme.tswithlightPaletteanddarkPaletteconstants; the livethemeobject is now mutated in place byapplyThemeMode()so existing call sites automatically follow the detected mode without code changes.connect.tsandlaunch.tsx), BEFORE the first Ink frame renders, so no palette switching or re-renders are needed.COLORFGBGparser, and theme application.This description was created by
for 473813c. It will automatically update as commits are pushed.