NousResearch/hermes-agent

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

1open issue
1open PR
8items grouped
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.

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.

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

Merged to main. A stamp counts the issues a fix is linked to.

  • #14895 fix(tui): keep FloatingOverlays visible when input is blocked

    merged to main

    files: ui-tui/src/components/appLayout.tsx

    Show 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
  • Closed#14467[Bug]: TUI `/resume` slash command does not open session picker
  • Closed#15094[TUI] /resume and /model picker overlay invisible due to rendering logic conflict (v0.11.0)
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.00 means 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 Lab

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

For your agent

The same facts, typed.