TUI floating overlay visibility and rendering fixes
Cluster 14467 · apps/desktop. Data as of
2 open items. Fix merged in #14895 on 24 Apr 2026. Check it covers your case.
The short answer
- Known?
- Yes. 8 related items grouped here, 2 issues already closed.
- Fix event
- A fix was merged to main in PR #14895. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
partially resolved, deciding PR #14895. That is separate from the fix event. - Still open
- 1 issue and 1 PR.
Next step: read #14895, then check whether your report is the one still open.
Read #14895Pick up a Lab taskMultiple bugs and fixes related to slash-command popups and floating overlays becoming invisible in the TUI, particularly when the composer input is blocked. Covers rendering logic for zero-height parents, spacer handling, and the /resume and /model picker overlays.
Open issues1
- Issue#14907TUI slash-command popups hidden by blocked composer guard
Open PRs1
- PR#59834fix(tui): render absolute-positioned children of a zero-height parent
Fixes on record
-
#14895 fix(tui): keep FloatingOverlays visible when input is blocked
files:
ui-tui/src/components/appLayout.tsxShow the diff
-
ui-tui/src/components/appLayout.tsx@@ -185,56 +185,58 @@ const ComposerPane = memo(function ComposerPane({ <StatusRulePane at="top" composer={composer} status={status} /> - {!isBlocked && ( - <Box flexDirection="column" marginTop={ui.statusBar === 'top' ? 0 : 1} position="relative"> - <FloatingOverlays - cols={composer.cols} - compIdx={composer.compIdx} - completions={composer.completions} - onModelSelect={actions.onModelSelect} - onPickerSelect={actions.resumeById} - pagerPageSize={composer.pagerPageSize} - /> + <Box flexDirection="column" marginTop={ui.statusBar === 'top' ? 0 : 1} position="relative"> + <FloatingOverlays + cols={composer.cols} + compIdx={composer.compIdx} + completions={composer.completions} + onModelSelect={actions.onModelSelect} + onPickerSelect={actions.resumeById} + pagerPageSize={composer.pagerPageSize} + /> - {composer.inputBuf.map((line, i) => ( - <Box key={i}> - <Box width={3}> - <Text color={ui.theme.color.dim}>{i === 0 ? `${ui.theme.brand.prompt} ` : ' '}</Text> - </Box> + {!isBlocked && ( + <> + {composer.inputBuf.map((line, i) => ( + <Box key={i}> + <Box width={3}> + <Text color={ui.theme.color.dim}>{i === 0 ? `${ui.theme.brand.prompt} ` : ' '}</Text> + </Box> - <Text color={ui.theme.color.cornsilk}>{line || ' '}</Text> - </Box> - ))} + <Text color={ui.theme.color.cornsilk}>{line || ' '}</Text> + </Box> + ))} + + <Box position="relative"> + <Box width={pw}> + {sh ? ( + <Text color={ui.theme.color.shellDollar}>$ </Text> + ) : ( + <Text bold color={ui.theme.color.prompt}> + {composer.inputBuf.length ? ' ' : `${ui.theme.brand.prompt} `} + </Text> + )} + </Box> - <Box position="relative"> - <Box width={pw}> - {sh ? ( - <Text color={ui.theme.color.shellDollar}>$ </Text> - ) : ( - <Text bold color={ui.theme.color.prompt}> - {composer.inputBuf.length ? ' ' : `${ui.theme.brand.prompt} `} - </Text> - )} - </Box> + <Box flexGrow={1} position="relative"> + {/* subtract NoSelect paddingX={1} (2 cols) + pw so wrap-ansi and cursorLayout agree */} + <TextInput + columns={Math.max(20, composer.cols - pw - 2)} + onChange={composer.updateInput} + onPaste={composer.handleTextPaste} + onSubmit={composer.submit} + placeholder={composer.empty ? PLACEHOLDER : ui.busy ? 'Ctrl+C to interrupt…' : ''} + value={composer.input} + /> - <Box flexGrow={1} position="relative"> - {/* subtract NoSelect paddingX={1} (2 cols) + pw so wrap-ansi and cursorLayout agree */} - <TextInput - columns={Math.max(20, composer.cols - pw - 2)} - onChange={composer.updateInput} - onPaste={composer.handleTextPaste} - onSubmit={composer.submit} - placeholder={composer.empty ? PLACEHOLDER : ui.busy ? 'Ctrl+C to interrupt…' : ''} - value={composer.input} - /> - - <Box position="absolute" right={0}> - <GoodVibesHeart t={ui.theme} tick={status.goodVibesTick} /> + <Box position="absolute" right={0}> + <GoodVibesHeart t={ui.theme} tick={status.goodVibesTick} /> + </Box> </Box> </Box> - </Box> - </Box> - )} + </> + )} + </Box> {!composer.empty && !ui.sid && <Text color={ui.theme.color.dim}>⚕ {ui.status}</Text>}
-
3 PRs closed without merging
From the close comments: done elsewhere 1 · superseded by another PR 1 · won't fix / by design 1
- #14509 fix(tui): remove picker from $isBlocked to fix /resume command - closes #14467 · superseded by another PR
- #15140 fix(tui): fix invisible slash-command overlays and FormattedText display serialization · won't fix / by design
- #27427 fix(tui): render spacer for FloatingOverlays when composer is blocked · done elsewhere
Closed issues · 2
How this cluster was grouped
-
Link evidence (
partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text. - Members outside that structural core may belong to a different underlying problem.
-
Pairwise link density
0.25: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Subsystem
- apps/desktop
- Merged PRs
- #14895
- Closed issues
- 2
- Attempts
- 3 PRs closed without merging
- Fix link checked
- 24 Apr 2026. This confirmation is old and may be out of date (a revert or reopen would not have invalidated it here); re-verify.