Skip to content

Fresh-agent transcript UX: glom chip, stable streaming, tool-notice polish, scroll keys, focus-on-switch#471

Merged
danshapiro merged 5 commits into
mainfrom
fresh-agent-ux-katas
Jun 22, 2026
Merged

Fresh-agent transcript UX: glom chip, stable streaming, tool-notice polish, scroll keys, focus-on-switch#471
danshapiro merged 5 commits into
mainfrom
fresh-agent-ux-katas

Conversation

@danshapiro

Copy link
Copy Markdown
Owner

Fresh-agent transcript UX batch

Lands 5 katas filed in the "filing katas thread", implemented via Red-Green-Refactor TDD, one commit per kata. All build on the same worktree/branch sequentially.

Katas & commits

Kata Commit Summary
cer9 1ec8ac6f Sticky "glom" chip at the top of the transcript showing the most-recent offscreen-above user statement (one line, ellipsis, native title tooltip). Click jumps to it (scrollIntoView({block:'start'}) and leaves autoscroll paused — new agent output does not resnap. Appears any time ≥1 user turn is above the viewport (not just while streaming).
jp70 5a021838 Stable live activity-strip height during streaming. Fixes the blank line that flickered at the bottom while working: the streaming last turn is no longer dropped by filterTurnsForDisplay, the live strip renders a stable placeholder (same my-0.5 footprint) instead of null when displayRows is empty, and selectLiveActivityBlockId returns null to avoid a double spinner. transcriptSignature is unchanged by the placeholder so no extra autoscroll frames fire.
5kxd b9b8aa69 Drops the colored border-l-2 vertical line from tool-notification headers (activity-summary, FreshAgentToolBlock, FreshAgentThinkingRow, FreshAgentThinkingDisclosure, tool_result card) while keeping the px-2/pl-* indentation. Error state preserved via existing X icon + text-destructive + a subtle bg-destructive/10 tint. Single-tool activity strips now expand the only tool's body in one click (initialExpanded={true} when exactly 1 tool row, 0 thinking); multi-tool and thinking-only strips unchanged.
faz3 f07572ee ArrowUp/Down, PageUp/Down, Home, End scroll the transcript when the composer isn't focused (falls through unchanged when it is). FreshAgentTranscript converted to forwardRef with an imperative scroll handle; pane-root handlePaneKeyDown extended to dispatch nav keys via an isInteractiveTarget guard. End resumes autoscroll / dismisses the scroll-to-bottom button; Home/PageUp pause it. Scoped to fresh-agent panes.
0bc6 1cc1c1be Focus the composer when switching to a tab whose active pane is a fresh-agent pane. Replaced the hidden-based focus effect with one keyed on isActivePane (!hidden && activeTabId === tabId && activePaneId === paneId); bails only when an editable inside this pane already has focus (no clobber); focuses the pane root (tabIndex={-1}) when the composer is disabled. Removed the redundant focusOnReady path so the effect is the single owner of activation + session-ready focus. Also fixes a pre-existing order-dependent flake (storage clear in beforeEach).

Verification

  • npm run test:vitest -- run (full default-config suite): 3818 passed (356 files)
  • npm run lint: 0 errors (11 pre-existing warnings, none in changed files)
  • npm run typecheck (client + server): clean

Each kata's acceptance criteria are covered by unit tests in test/unit/client/components/fresh-agent/. E2E criteria noted as follow-ups in the individual katas.

codex added 5 commits June 22, 2026 04:41
…ser turn (kata cer9)

Add a sticky chip anchored to the top of the transcript scroller that shows
the most-recent user turn scrolled above the viewport. Clicking scrolls that
turn into view (block:start) without re-enabling autoscroll — new agent
output does not resnap. Recomputes on scroll and transcript-signature change
via getBoundingClientRect comparison, no polling.
…(kata jp70)

While a fresh-agent turn streams, the activity strip's bottom edge jumped
up/down because several paths momentarily contributed zero height:

- filterTurnsForDisplay dropped the last turn when all items were filtered
  out (e.g. showThinking=false with only a thinking item), so the turn's
  mt-3/mt-1.5 margin blinked on/off.
- FreshAgentActivityStrip returned null when displayRows was empty,
  collapsing the my-0.5 footprint.
- selectLiveActivityBlockId kept pointing at an earlier turn's strip when
  the streaming last turn had no displayable items, producing a second
  running indicator.

Fix:
- filterTurnsForDisplay now takes isStreaming and keeps the last turn
  (with empty items) when streaming, preserving the article footprint.
- FreshAgentActivityStrip renders a stable spinner-only placeholder
  (same my-0.5 + py-0.5 + text-xs footprint) instead of null when live
  and displayRows is empty.
- FreshAgentTurnArticle renders a trailing live placeholder strip when
  isStreamingLastTurn and no displayable blocks exist.
- selectLiveActivityBlockId returns null when the streaming last turn has
  no displayable items, so only the placeholder is live (no double spinner).

The transcriptSignature memo is unchanged — the placeholder is not an item,
so it does not fire extra autoscroll frames. Settled (non-streaming)
behavior is unchanged.
@danshapiro danshapiro merged commit 99ee54d into main Jun 22, 2026
1 check passed
@danshapiro danshapiro deleted the fresh-agent-ux-katas branch June 22, 2026 22:37
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.

2 participants