{"repo":"NousResearch/hermes-agent","public_id":"14467","display_label":"TUI floating overlay visibility and rendering fixes","summary":"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.","subsystem":"apps/desktop","counts":{"members":8,"open_issues":1,"open_issues_distinct":1,"open_prs":1,"closed_issues":2,"merged_prs":1,"attempt_prs":4},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":14895,"merged_at":"2026-04-24T04:44:51+00:00"},"resolution":{"state":"partially_resolved","by":"pr-14895"},"freshness":{"state":"stale","last_verified_at":"2026-04-24T04:44:51+00:00","is_stale":true},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.25285714285714284},"fix_edges":[{"issue_number":14467,"pr_number":14895,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true,"pr_merged_at":"2026-04-24T04:44:51+00:00"}],"members":[{"kind":"issue","number":14467,"title":"[Bug]: TUI `/resume` slash command does not open session picker","url":null,"state":"closed"},{"kind":"issue","number":14907,"title":"TUI slash-command popups hidden by blocked composer guard","url":null,"state":"open"},{"kind":"issue","number":15094,"title":"[TUI] /resume and /model picker overlay invisible due to rendering logic conflict (v0.11.0)","url":null,"state":"closed"},{"kind":"pr","number":14509,"title":"fix(tui): remove picker from $isBlocked to fix /resume command - closes #14467","url":null,"state":"closed","close_reason":{"category":"superseded_by_pr","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":14895,"title":"fix(tui): keep FloatingOverlays visible when input is blocked","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["ui-tui/src/components/appLayout.tsx"],"fix_hunks":[{"file":"ui-tui/src/components/appLayout.tsx","hunks":["@@ -185,56 +185,58 @@ const ComposerPane = memo(function ComposerPane({\n \n       <StatusRulePane at=\"top\" composer={composer} status={status} />\n \n-      {!isBlocked && (\n-        <Box flexDirection=\"column\" marginTop={ui.statusBar === 'top' ? 0 : 1} position=\"relative\">\n-          <FloatingOverlays\n-            cols={composer.cols}\n-            compIdx={composer.compIdx}\n-            completions={composer.completions}\n-            onModelSelect={actions.onModelSelect}\n-            onPickerSelect={actions.resumeById}\n-            pagerPageSize={composer.pagerPageSize}\n-          />\n+      <Box flexDirection=\"column\" marginTop={ui.statusBar === 'top' ? 0 : 1} position=\"relative\">\n+        <FloatingOverlays\n+          cols={composer.cols}\n+          compIdx={composer.compIdx}\n+          completions={composer.completions}\n+          onModelSelect={actions.onModelSelect}\n+          onPickerSelect={actions.resumeById}\n+          pagerPageSize={composer.pagerPageSize}\n+        />\n \n-          {composer.inputBuf.map((line, i) => (\n-            <Box key={i}>\n-              <Box width={3}>\n-                <Text color={ui.theme.color.dim}>{i === 0 ? `${ui.theme.brand.prompt} ` : '  '}</Text>\n-              </Box>\n+        {!isBlocked && (\n+          <>\n+            {composer.inputBuf.map((line, i) => (\n+              <Box key={i}>\n+                <Box width={3}>\n+                  <Text color={ui.theme.color.dim}>{i === 0 ? `${ui.theme.brand.prompt} ` : '  '}</Text>\n+                </Box>\n \n-              <Text color={ui.theme.color.cornsilk}>{line || ' '}</Text>\n-            </Box>\n-          ))}\n+                <Text color={ui.theme.color.cornsilk}>{line || ' '}</Text>\n+              </Box>\n+            ))}\n+\n+            <Box position=\"relative\">\n+              <Box width={pw}>\n+                {sh ? (\n+                  <Text color={ui.theme.color.shellDollar}>$ </Text>\n+                ) : (\n+                  <Text bold color={ui.theme.color.prompt}>\n+                    {composer.inputBuf.length ? '  ' : `${ui.theme.brand.prompt} `}\n+                  </Text>\n+                )}\n+              </Box>\n \n-          <Box position=\"relative\">\n-            <Box width={pw}>\n-              {sh ? (\n-                <Text color={ui.theme.color.shellDollar}>$ </Text>\n-              ) : (\n-                <Text bold color={ui.theme.color.prompt}>\n-                  {composer.inputBuf.length ? '  ' : `${ui.theme.brand.prompt} `}\n-                </Text>\n-              )}\n-            </Box>\n+              <Box flexGrow={1} position=\"relative\">\n+                {/* subtract NoSelect paddingX={1} (2 cols) + pw so wrap-ansi and cursorLayout agree */}\n+                <TextInput\n+                  columns={Math.max(20, composer.cols - pw - 2)}\n+                  onChange={composer.updateInput}\n+                  onPaste={composer.handleTextPaste}\n+                  onSubmit={composer.submit}\n+                  placeholder={composer.empty ? PLACEHOLDER : ui.busy ? 'Ctrl+C to interrupt…' : ''}\n+                  value={composer.input}\n+                />\n \n-            <Box flexGrow={1} position=\"relative\">\n-              {/* subtract NoSelect paddingX={1} (2 cols) + pw so wrap-ansi and cursorLayout agree */}\n-              <TextInput\n-                columns={Math.max(20, composer.cols - pw - 2)}\n-                onChange={composer.updateInput}\n-                onPaste={composer.handleTextPaste}\n-                onSubmit={composer.submit}\n-                placeholder={composer.empty ? PLACEHOLDER : ui.busy ? 'Ctrl+C to interrupt…' : ''}\n-                value={composer.input}\n-              />\n-\n-              <Box position=\"absolute\" right={0}>\n-                <GoodVibesHeart t={ui.theme} tick={status.goodVibesTick} />\n+                <Box position=\"absolute\" right={0}>\n+                  <GoodVibesHeart t={ui.theme} tick={status.goodVibesTick} />\n+                </Box>\n               </Box>\n             </Box>\n-          </Box>\n-        </Box>\n-      )}\n+          </>\n+        )}\n+      </Box>\n \n       {!composer.empty && !ui.sid && <Text color={ui.theme.color.dim}>⚕ {ui.status}</Text>}\n \n"]}]},{"kind":"pr","number":15140,"title":"fix(tui): fix invisible slash-command overlays and FormattedText display serialization","url":null,"state":"closed","close_reason":{"category":"wontfix_bydesign","implication":"wontfix"},"merged":false},{"kind":"pr","number":27427,"title":"fix(tui): render spacer for FloatingOverlays when composer is blocked","url":null,"state":"closed","close_reason":{"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":59834,"title":"fix(tui): render absolute-positioned children of a zero-height parent","url":null,"state":"open","close_reason":{"refs":[{"kind":"commit","value":"aec331899","number":0},{"kind":"pr","value":"#79268","number":79268},{"kind":"pr","value":"#59834","number":59834}],"category":"merged_via","implication":"superseded"},"merged":false}],"trust":{"default_tier":"verified","includes_suggested":false}}