Skip to content

Raw process/agent output renders in unbounded pre blocks that the app shell silently clips instead of scrolling #5675

Description

@atomantic

ProblemLayout.jsx's root is w-full max-w-full overflow-x-hidden, so anything wider than the viewport is clipped, not scrollable. Two <pre> blocks render arbitrary-length machine output with the default white-space: pre, no wrap, and no overflow-x container: a self-update log line and a legacy shell command. On any viewport narrower than the longest line the tail of the text is unreachable — there is no horizontal scrollbar to find, because the shell ate it. 32 of the 63 <pre> elements in the tree already wrap or scroll correctly, so this is a two-file drift from an established idiom, not a missing convention.

Evidenceclient/src/components/apps/tabs/UpdateTab.jsx:681:

            {status.lastUpdateResult.log && (
              <pre className="text-xs text-gray-400 mt-2 font-mono">{status.lastUpdateResult.log}</pre>
            )}

client/src/components/cos/JobCard.jsx:533:

                  <pre className="w-full px-3 py-2 bg-port-bg border border-port-border rounded-lg text-gray-400 text-sm font-mono">{editData.command || 'No command'}</pre>

The shell that clips them, client/src/components/Layout.jsx:1173:

    <div className="h-dvh-screen print:h-auto print:min-h-screen w-full max-w-full overflow-x-hidden bg-port-bg flex">

The two correct idioms already in the tree: client/src/components/settings/LocalSetupPanel.jsx:269 (max-h-48 overflow-y-auto … whitespace-pre-wrap) and client/src/components/ui/ProcessLogLines.jsx:29 (whitespace-pre-wrap break-all). ProcessLogLines is the canonical renderer for streamed process output and is the pattern to match, since a wrapped log keeps every character reachable at 360px where a horizontal scroller does not.

Plan

  1. In client/src/components/apps/tabs/UpdateTab.jsx:681, change the class to text-xs text-gray-400 mt-2 font-mono whitespace-pre-wrap break-all max-h-48 overflow-y-auto. Decision: wrap + vertical scroll rather than overflow-x-auto, matching ProcessLogLines — an update log is prose-shaped output the user reads, and a horizontal scroller inside a settings tab is worse on touch.
  2. In client/src/components/cos/JobCard.jsx:533, add whitespace-pre-wrap break-all to the existing classes so a long legacy command is fully readable inside the card.
  3. Audit and fix the other <pre> elements whose opening tag has no overflow, whitespace-pre-wrap or break-* class: run grep -rn '<pre' client/src --include='*.jsx' | grep -v test | grep -vE 'overflow|whitespace-pre-wrap|break-' and apply the same class pair. As of this audit only the two above plus multi-line JSX openers remain.
  4. Do not remove overflow-x-hidden from Layout.jsx — it is deliberate (it stops any single wide child from giving the whole app a horizontal scrollbar). The fix belongs in the leaf.

Tests

  • Extend client/src/components/apps/tabs/UpdateTab.test.jsx with a render case supplying a 400-character single-line lastUpdateResult.log and asserting the <pre> element carries whitespace-pre-wrap. Uniquely catches removal of the wrap class, which is invisible in any test that only checks the text content.
  • Add the equivalent assertion to client/src/components/cos/JobCard.test.jsx for a long editData.command.

Acceptance criteria

  • grep -rn '<pre' client/src --include='*.jsx' | grep -v test | grep -vE 'overflow|whitespace-pre-wrap|break-' returns no single-line results.
  • A 300-character update log line is fully readable at a 360px viewport with no horizontal page scroll.

Out of scopeLayout.jsx's overflow-x-hidden, the ProcessLogModal / ProcessLogLines implementation, and <code> spans.


Filed by a /do:better --scan-only --issues audit (2026-09-01). Category: ux · Severity: medium · Files: client/src/components/apps/tabs/UpdateTab.jsx:681, client/src/components/cos/JobCard.jsx:533, client/src/components/Layout.jsx:1173, client/src/components/ui/ProcessLogLines.jsx:29

All labels already exist in the repo; do NOT create labels. Never add planner:* labels.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingeffort:lowEffort: lowmodel:lightModel size: lightplanTracked by /do:replanplanner:fable-5.1Plan authored by the Fable 5.1 modelseverity:mediumuxProposed from a UX/design audit

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions