diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d872d97da..ad2dce3da1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,11 @@ file is the human-readable summary of what changed and why. the separation), and the status footer now sits on the terminal's last row instead of two rows above it. +- **Visible selection in the question panel** — the highlighted answer (and the + hovered question tab) used a background one shade off the panel, which was + effectively invisible in light themes; the highlight is now derived from the + panel and text colors so it reads in both light and dark. + - **No stray line on tool output** — expanded tool-output panels (bash output, generic tool results) no longer draw a background-colored left border glyph, which showed up as an odd thin vertical line along the panel's left edge on diff --git a/DIVERGENCES.md b/DIVERGENCES.md index 5a26bd2b92..3db36cb6b5 100644 --- a/DIVERGENCES.md +++ b/DIVERGENCES.md @@ -127,6 +127,7 @@ from the squash message first, or use a plain merge. | `packages/tui/src/component/spinner.tsx` | `SPINNER_FRAMES` swapped from cli-spinners "dots" (10 braille frames) to cli-spinners "sand" (35 braille frames, falling-sand animation); interval stays 80 ms | Fork visual identity for the shared `Spinner` component (in-progress tool/thinking rows, startup loading, session-list dialogs). | Keep our frames array; take upstream's surrounding changes. | | `packages/tui/src/component/prompt/index.tsx` | Busy footer spinner is cli-spinners "aesthetic" instead of the Knight Rider block scanner: module-level `FOOTER_FRAMES` (`▰▱` ramp, 7 chars, 8 frames), `spinnerDef` keeps the agent-color resolution but returns those frames plus a per-char color fn (`▰` → agent color, `▱` → `deriveInactiveColor(color, 0.3)`) instead of `createFrames`/`createColors` from `ui/spinner`, and the `` interval is 120 ms (was 40; cli-spinners' stock 80 felt too fast). `ui/spinner.ts` itself is untouched; only its `deriveInactiveColor` import remains. | Fork visual identity for the busy indicator next to `esc interrupt`; the agent accent color still drives it. | Keep `FOOTER_FRAMES`, our `spinnerDef` body, the `deriveInactiveColor` import, and the 120 ms interval; take upstream's surrounding changes. If upstream reworks `spinnerDef`, re-apply the aesthetic frames + per-char color fn on top. | | `packages/tui/src/component/prompt/index.tsx`, `packages/tui/src/routes/session/index.tsx` | Compact prompt/footer spacing: the input box's interior `paddingTop={1}` is replaced by a height-1 half-block top-edge row above the box (`border={["top"]}`, `horizontal: "▄"` when `backgroundElement` is opaque, `" "` otherwise — mirror of the existing `"▀"` bottom-edge row; the agent/model meta row keeps its `paddingTop={1}`), and the session content container loses its `paddingBottom={1}` and its `gap={1}` (the gap fired between every pair of children — transcript↔prompt, and prompt↔the trailing flexGrow spacer, where it left a phantom blank row under the footer even at zero spacer height; the half-block lip is now the only transcript↔prompt separation) so the prompt footer line sits on the terminal's last row | The prompt box was a row taller than its content needed, a full blank row sat between the transcript and the box, and the footer floated 2 rows above the bottom edge; the half-block lip keeps a visible box edge above the text at half the height. | Keep the top-edge lip row and the padding/gap removals; take upstream's surrounding changes. If upstream restyles the box edges, re-apply the symmetric half-block lips and keep the container un-padded. | +| `packages/tui/src/routes/session/question.tsx` | Selection/hover highlight color: a `highlight` memo (`tint(theme.backgroundPanel, theme.text, 0.1)`) replaces `theme.backgroundElement` on the active option row (number + label boxes, both the option list and the "Type your own answer" row) and on the question/Confirm tab hover state | `backgroundElement` sits ~1.04x off `backgroundPanel` in light themes (`#f5f5f5` on `#fafafa` for `codev`), so the selected answer had no visible highlight; blending the panel toward the theme's text color keeps a neutral band that is ~1.2x in both modes and works for generated/system themes too. | Keep the `highlight` memo and its four/two call sites; take upstream's surrounding changes. If upstream adds new `backgroundElement` highlights in this panel, point them at `highlight()` too. | | `packages/tui/src/app.tsx` | Mounted `` (fork-only `./context/editor-label`) immediately inside `` | The effective-editor-selection state is shared by the prompt and the sidebar footer, so it has to be provided above both. | Keep the import and the wrapper; take upstream's surrounding provider changes. If upstream moves `EditorContextProvider`, keep ours directly inside it. | | `packages/app/index.html` | `` → "CoDev Code" | Web UI tab title. | Keep our title. | | `packages/app/src/pages/layout.tsx`, `packages/app/src/pages/layout-new.tsx` | Dropped the `<TabsInfoPopup />` mount and its import from both layouts (added upstream in v1.17.18; since v1.18.3 upstream itself removed the sibling `HelpButton`, so nothing is imported from `help-button` anymore) | `TabsInfoPopup` is a dev-channel-only "Introducing Tabs" promo whose `muted autoplay loop` `<video>` mounts on every layout render. The fork's Windows e2e runs on GPU-less GitHub-hosted runners (software GPU — see the runner-swap row), where continuous video decode wedges the GPU command buffer (`kTransientFailure`), making the whole app-e2e suite flaky and ~3–5× slower. It never ships (release builds use a non-`dev` `CODEV_CHANNEL`), so unmounting it costs users nothing. | Keep ours: don't re-add the `<TabsInfoPopup />` mount or restore it to the import. | diff --git a/packages/tui/src/routes/session/question.tsx b/packages/tui/src/routes/session/question.tsx index 6eeb63786e..3621aec817 100644 --- a/packages/tui/src/routes/session/question.tsx +++ b/packages/tui/src/routes/session/question.tsx @@ -19,6 +19,9 @@ export function QuestionPrompt(props: { request: QuestionRequest; directory?: st const modeStack = useOpencodeModeStack() const questions = createMemo(() => props.request.questions) + // backgroundElement is nearly identical to backgroundPanel in light themes, so derive the + // highlight from the panel and the text color instead to keep it visible in both modes + const highlight = createMemo(() => tint(theme.backgroundPanel, theme.text, 0.1)) const single = createMemo(() => questions().length === 1 && questions()[0]?.multiple !== true) const tabs = createMemo(() => (single() ? 1 : questions().length + 1)) // questions + confirm tab (no confirm for single select) const [tabHover, setTabHover] = createSignal<number | "confirm" | null>(null) @@ -316,11 +319,7 @@ export function QuestionPrompt(props: { request: QuestionRequest; directory?: st paddingLeft={1} paddingRight={1} backgroundColor={ - isActive() - ? theme.accent - : tabHover() === index() - ? theme.backgroundElement - : theme.backgroundPanel + isActive() ? theme.accent : tabHover() === index() ? highlight() : theme.backgroundPanel } onMouseOver={() => setTabHover(index())} onMouseOut={() => setTabHover(null)} @@ -348,7 +347,7 @@ export function QuestionPrompt(props: { request: QuestionRequest; directory?: st paddingLeft={1} paddingRight={1} backgroundColor={ - confirm() ? theme.accent : tabHover() === "confirm" ? theme.backgroundElement : theme.backgroundPanel + confirm() ? theme.accent : tabHover() === "confirm" ? highlight() : theme.backgroundPanel } onMouseOver={() => setTabHover("confirm")} onMouseOut={() => setTabHover(null)} @@ -385,12 +384,12 @@ export function QuestionPrompt(props: { request: QuestionRequest; directory?: st }} > <box flexDirection="row"> - <box backgroundColor={active() ? theme.backgroundElement : undefined} paddingRight={1}> + <box backgroundColor={active() ? highlight() : undefined} paddingRight={1}> <text fg={active() ? tint(theme.textMuted, theme.secondary, 0.6) : theme.textMuted}> {`${i() + 1}.`} </text> </box> - <box backgroundColor={active() ? theme.backgroundElement : undefined}> + <box backgroundColor={active() ? highlight() : undefined}> <text fg={active() ? theme.secondary : picked() ? theme.success : theme.text}> {multi() ? `[${picked() ? "✓" : " "}] ${opt.label}` : opt.label} </text> @@ -417,12 +416,12 @@ export function QuestionPrompt(props: { request: QuestionRequest; directory?: st }} > <box flexDirection="row"> - <box backgroundColor={other() ? theme.backgroundElement : undefined} paddingRight={1}> + <box backgroundColor={other() ? highlight() : undefined} paddingRight={1}> <text fg={other() ? tint(theme.textMuted, theme.secondary, 0.6) : theme.textMuted}> {`${options().length + 1}.`} </text> </box> - <box backgroundColor={other() ? theme.backgroundElement : undefined}> + <box backgroundColor={other() ? highlight() : undefined}> <text fg={other() ? theme.secondary : customPicked() ? theme.success : theme.text}> {multi() ? `[${customPicked() ? "✓" : " "}] Type your own answer` : "Type your own answer"} </text>