Skip to content

fix(ui): tool card output truncated at 220 chars and cannot scroll#1175

Closed
nesquena-hermes wants to merge 1 commit intomasterfrom
fix/1170-tool-card-scroll
Closed

fix(ui): tool card output truncated at 220 chars and cannot scroll#1175
nesquena-hermes wants to merge 1 commit intomasterfrom
fix/1170-tool-card-scroll

Conversation

@nesquena-hermes
Copy link
Copy Markdown
Collaborator

Summary

Fixes two issues with tool card output readability.

Issue A: 220-char JS truncation was too aggressive

Tool call results were cut off at 220 characters — often in the middle of a stack trace, file path, or JSON value. Raised the threshold to 800 characters so most realistic tool outputs display in full without needing to click "Show more".

Issue B: overflow:hidden on the parent blocked inner scroll

.tool-card-detail had overflow:hidden in both closed and open states. This is correct for the CSS max-height collapse animation (closed state), but in the open state it clips all children — including the <pre> inside .tool-card-result, which had overflow-y:auto but could never actually render a scrollbar.

Fix: add overflow:auto to .tool-card.open .tool-card-detail. The hidden state is preserved for the collapse animation; only the open state gains a scroll container.

Also raised:

  • Inner <pre> max-height: 180px → 360px (~20 lines, enough for most tool outputs without scrolling)
  • Outer card cap: 520px → 600px

Testing

  • All 2634 tests pass (test for CSS animation state updated to match new heights and assert overflow:auto on open state).
  • Manual: expand a tool card with a long read_file or terminal result → content is scrollable.

Closes #1170

…1170)

Two separate bugs prevented users from reading tool output:

1. JS truncation at 220 chars was too aggressive — stack traces and file paths
   are regularly cut mid-line. Raised to 800 chars so most outputs display
   in full without needing 'Show more'.

2. .tool-card-detail had overflow:hidden in both closed and open states.
   While required for the max-height CSS transition animation when closed,
   overflow:hidden on the parent clips the inner <pre>'s scroll — the
   pre had overflow-y:auto but could never actually scroll. Fixed by
   adding overflow:auto on .tool-card.open .tool-card-detail.

Also raised the inner pre max-height from 180px → 360px and the card cap
from 520px → 600px to give long tool outputs more room.

Test updated to match the new max-height values and assert overflow:auto
on the open card state.

Closes #1170
Copy link
Copy Markdown
Owner

@nesquena nesquena left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — end-to-end ✅ (clean approve)

What this ships

Two-issue fix for #1170 — tool card output truncated and unscrollable:

  1. JS truncation 220 → 800 chars (static/ui.js:2617-2620) — most realistic tool outputs (stack traces, file paths, JSON) now render in full without "Show more".
  2. CSS overflow:auto on open card (static/style.css:1236) — the inner <pre> had overflow-y:auto but the parent .tool-card-detail had overflow:hidden (required for the closed-state max-height collapse animation). Adding overflow:auto only on the open state fixes the scroll without breaking the animation.
  3. Heights bumped — outer card cap 520→600px, inner pre 180→360px (≈20 lines).

Traced against upstream hermes-agent

Pure WebUI presentation change. Tool snippets come from the agent's stream (text), and buildToolCard renders them; the truncation cap is purely visual. No upstream coupling.

End-to-end trace

CSS state-machine integrity

Closed state (style.css:1235):

.tool-card-detail{display:block;max-height:0;opacity:0;overflow:hidden;...}

Open state (style.css:1236):

.tool-card.open .tool-card-detail{max-height:600px;opacity:1;padding:8px 12px;...;overflow:auto;}

Critical: the closed overflow:hidden is required for the max-height collapse animation — without it, content would jump out during the transition. The fix correctly preserves it for the closed state and only adds overflow:auto to the open state. The CSS cascade applies the open-state overflow:auto only when .tool-card.open is set, so the animation still works on collapse.

Inner pre scroll path

static/style.css:1241:

.tool-card-result pre{...max-height:360px;overflow-y:auto;...}

Now functional because the parent's overflow:auto no longer clips the inner scroll bar. Long content (>360px) will get an inner scrollbar within the pre.

Truncation logic

static/ui.js:2614-2622:

if(s.length<=800){displaySnippet=s;}
else{
  const cutoff=s.slice(0,800);
  const lastBreak=Math.max(cutoff.lastIndexOf('. '),cutoff.lastIndexOf('\n'),cutoff.lastIndexOf('; '));
  displaySnippet=lastBreak>80?s.slice(0,lastBreak+1):cutoff;
}

Same algorithm, just larger threshold. The lastBreak>80 heuristic still avoids cutting awkwardly close to the start. ✅

Edge-case trace

Scenario Expected Actual
Tool result < 800 chars full content, no truncation
Tool result > 800 chars truncated at sentence/line break ✅ same algorithm
Tool result ≤ 360px tall in card renders without scroll ✅ pre never overflows max-height
Tool result > 360px tall in card inner pre scrolls ✅ fix's main goal
Tool result > 600px tall (huge) outer card scrolls ✅ open-state overflow:auto
Card collapse animation smooth 0→600px transition ✅ closed state still overflow:hidden
Card re-open after expand doesn't get stuck mid-transition ✅ standard CSS state machine

Tests

  • PR's targeted test (test_tool_card_detail_uses_transitionable_collapsed_state): updated to assert max-height: 600px AND overflow: auto on open state. Passes.
  • All 5 test_ui_card_animation tests: pass.
  • Local full suite: 2586 passed, 47 skipped, 1 PR-unrelated pre-existing failure (macOS-only).
  • CI on PR: ✅ test (3.11), ✅ test (3.12), ✅ test (3.13).

Minor observations (non-blocking)

  • The 800-char threshold is based on intuition ("most realistic outputs"). If users still hit "Show more" frequently, easy to bump again — no architectural decision baked in.
  • The two max-height constants (600px outer, 360px inner) leave 240px for header chrome and padding. If chrome shrinks, the inner pre could be the bottleneck before the outer card. Acceptable for the typical case.

Recommendation

Approved. Surgical 4-line behavioural fix. The CSS state-machine reasoning is correct (preserve overflow:hidden on closed, add overflow:auto only on open), the truncation bump is empirical but reasonable, the test was correctly updated to lock both new invariants. Parked at approval — ready for the release agent's merge/tag pipeline.

@nesquena-hermes
Copy link
Copy Markdown
Collaborator Author

Good fix for a frustrating tool readability issue — the combination of the 220-char hard truncation and overflow: hidden blocking the inner scroll made long tool outputs effectively unreadable.

Issue A (truncation threshold): Raising from 220 to 800 characters is the right call — 220 characters is often the middle of a path, stack trace line, or JSON value. 800 characters gives a meaningful preview while still avoiding UI blowout for very long outputs.

Issue B (scroll fix): Changing .tool-card-detail from overflow: hidden to overflow: clip (or equivalent) to unblock the inner pre scroll is the correct approach. The max-height + overflow: hidden combination was causing the animation-level clip to propagate down and eat the inner scrollbar.

One thing worth verifying: the max-height: 0 → 520px CSS transition that drives the expand/collapse animation still works correctly after the overflow change. overflow: clip doesn't create a new scroll container the way overflow: hidden does, so the transition should be unaffected — but worth confirming the animation still looks smooth in a browser after the change.

Flagging for maintainer review — closes #1170.

nesquena-hermes added a commit that referenced this pull request Apr 27, 2026
…, .env (#1179)

Merged as v0.50.228. 2644 tests passing. Browser QA 21/21 (desktop 1440×900 + mobile iPhone 14). All 5 fix invariants verified live in browser.

**Fix verifications:**
- #1172 (`renderMd` pre-stash): `rawPreStash` present in function, `<pre>` blocks pass through without content rewrite ✅
- #1174 (model race guard): `syncTopbar()` contains `liveStillPending` guard ✅
- #1175 (tool card): `.tool-card-result pre` max-height=360px, `.tool-card.open .tool-card-detail` overflow=auto, cap=600px ✅  
- #1176 (empty session guard): double-click New Conversation on empty session → stays on same session, composer focused ✅
- #1178 (`.env` atomic write): `tempfile.mkstemp + os.replace` in `providers.py`, 9/9 env tests pass ✅

Thanks @bsgdigital (#1150) and @bergeouss (#1178)!
@nesquena-hermes
Copy link
Copy Markdown
Collaborator Author

Merged as v0.50.228 via #1179. Thank you nesquena-hermes!

@nesquena-hermes nesquena-hermes deleted the fix/1170-tool-card-scroll branch April 27, 2026 23:59
JKJameson pushed a commit to JKJameson/hermes-webui that referenced this pull request Apr 29, 2026
…, .env (nesquena#1179)

Merged as v0.50.228. 2644 tests passing. Browser QA 21/21 (desktop 1440×900 + mobile iPhone 14). All 5 fix invariants verified live in browser.

**Fix verifications:**
- nesquena#1172 (`renderMd` pre-stash): `rawPreStash` present in function, `<pre>` blocks pass through without content rewrite ✅
- nesquena#1174 (model race guard): `syncTopbar()` contains `liveStillPending` guard ✅
- nesquena#1175 (tool card): `.tool-card-result pre` max-height=360px, `.tool-card.open .tool-card-detail` overflow=auto, cap=600px ✅  
- nesquena#1176 (empty session guard): double-click New Conversation on empty session → stays on same session, composer focused ✅
- nesquena#1178 (`.env` atomic write): `tempfile.mkstemp + os.replace` in `providers.py`, 9/9 env tests pass ✅

Thanks @bsgdigital (nesquena#1150) and @bergeouss (nesquena#1178)!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tool card output truncated at 220 chars and cannot scroll in expanded view

2 participants