From 275256cdb42cf050b68e6e2a349887a38e3006d4 Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Thu, 16 Apr 2026 15:50:28 -0500 Subject: [PATCH] feat(tui): uniform selection background instead of SGR inverse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Selection was falling back to SGR-7 inverse (fg ↔ bg per cell), which fragments over syntax-highlighted content — each amber/gold/dim/cornsilk fg turned into a different bg stripe, producing the staircase look. Now `useMainApp` calls `selection.setSelectionBgColor()` with a muted navy (`#3a3a55`) on theme change. `setSelectionBg` in screen.ts replaces just the bg cell-by-cell while preserving fg/bold/dim/italic, so the highlight is one solid color across the whole drag range and the text stays readable in its original color. Skins can override via `selection_bg` in their color map. --- ui-tui/src/app/useMainApp.ts | 7 +++++++ ui-tui/src/theme.ts | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/ui-tui/src/app/useMainApp.ts b/ui-tui/src/app/useMainApp.ts index c97fde79b4..d1ac42f5d0 100644 --- a/ui-tui/src/app/useMainApp.ts +++ b/ui-tui/src/app/useMainApp.ts @@ -130,6 +130,13 @@ export function useMainApp(gw: GatewayClient) { const hasSelection = useHasSelection() const selection = useSelection() + // Bind a uniform selection bg so drag-to-select shows one solid color + // across the whole range instead of SGR-inverse (which swaps each cell's + // fg → bg and fragments over amber/gold/dim text). Re-fires on skin swap. + useEffect(() => { + selection.setSelectionBgColor(ui.theme.color.selectionBg) + }, [selection, ui.theme.color.selectionBg]) + const composer = useComposerState({ gw, onClipboardPaste: quiet => clipboardPasteRef.current(quiet), diff --git a/ui-tui/src/theme.ts b/ui-tui/src/theme.ts index 48b7399723..d1f4aaa4b5 100644 --- a/ui-tui/src/theme.ts +++ b/ui-tui/src/theme.ts @@ -19,6 +19,8 @@ export interface ThemeColors { statusBad: string statusCritical: string + selectionBg: string + diffAdded: string diffRemoved: string diffAddedWord: string @@ -94,6 +96,11 @@ export const DEFAULT_THEME: Theme = { statusBad: '#FF8C00', statusCritical: '#FF6B6B', + // Uniform selection bg — matches the muted navy of the status bar so + // gold/amber fg stays readable and the highlight doesn't fragment per + // fg color the way SGR-inverse does. + selectionBg: '#3a3a55', + diffAdded: 'rgb(220,255,220)', diffRemoved: 'rgb(255,220,220)', diffAddedWord: 'rgb(36,138,61)', @@ -149,6 +156,8 @@ export function fromSkin( statusBad: d.color.statusBad, statusCritical: d.color.statusCritical, + selectionBg: c('selection_bg') ?? d.color.selectionBg, + diffAdded: d.color.diffAdded, diffRemoved: d.color.diffRemoved, diffAddedWord: d.color.diffAddedWord,