1. wheel-down jumped to the bottom because max_scroll() derived the limit from
geometry, which only covers expandable blocks — a text-only conversation
left it at 0, so one down-notch clamped scroll to 0 with follow=true and
the next draw snapped to the real bottom. The limit now uses
content_lines (the full rendered line count, stored per draw).
2. tool request and result are now one block ('requested where it returns'):
UiBlock::ToolCall/ToolResult/ToolExec are unified into UiBlock::Tool, built
by merging each assistant tool call with its matching tool_result message
(looked up by call_id). Collapsed shows the concrete summary + duration +
status; expanded shows human-readable args, then '── 结果 ──', then the
output (two-level: partial preview with remaining-line hint, then full).
Live running executions reuse the same block. ToolResult messages are no
longer rendered standalone.
tests updated for the merged model; wheel test now drives content_lines.