# NousResearch/hermes-agent · TUI floating overlay visibility and rendering fixes

## Open now

- 1 issue, 1 PR open on GitHub.
- #14907: TUI slash-command popups hidden by blocked composer guard
- PR #59834: fix(tui): render absolute-positioned children of a zero-height parent

Multiple 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.

## TL;DR

- 8 member(s): 1 open issue(s), 2 closed issue(s), 1 open PR(s), 1 merged PR(s), 3 PR(s) closed without merging.
- A fix was merged to main in PR #14895. Verify it addresses your case.
- **Recommended:** read PR #14895 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `partially_resolved` · deciding PR(s): `pr-14895`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): 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 member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Open related issues

- #14907: TUI slash-command popups hidden by blocked composer guard

## Closed / duplicate issues

- #14467: \[Bug\]: TUI \`/resume\` slash command does not open session picker
- #15094: \[TUI\] /resume and /model picker overlay invisible due to rendering logic conflict (v0.11.0)

## Prior & competing PRs

- **#14509: fix(tui): remove picker from $isBlocked to fix /resume command - closes #14467**. closed without merge · fix tier `suggested`
  - maintainer direction: category `superseded_by_pr`; implication `superseded`
- **#14895: fix(tui): keep FloatingOverlays visible when input is blocked**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `ui-tui/src/components/appLayout.tsx`
  - fix hunks:
  - `ui-tui/src/components/appLayout.tsx`

    ```diff
    @@ -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>}
     
    ```
- **#27427: fix(tui): render spacer for FloatingOverlays when composer is blocked**. closed without merge · fix tier `suggested`
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#15140: fix(tui): fix invisible slash-command overlays and FormattedText display serialization**. closed without merge
  - maintainer direction: category `wontfix_bydesign`; implication `wontfix`
- **#59834: fix(tui): render absolute-positioned children of a zero-height parent**. open
  - maintainer direction: category `merged_via`; implication `superseded`; refs `aec331899`, `#79268`, `#59834`
  - files: `ui-tui/packages/hermes-ink/src/ink/render-node-to-output.ts`, `ui-tui/packages/hermes-ink/src/ink/zero-height-absolute-overlay.test.ts`

## Fix status

- Merged to main in PR #14895 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-04-24`. This confirmation is old and may be out of date (a revert/reopen would not have invalidated it here); re-verify.
- Subsystem: `apps/desktop`
