Chat: inline question cards in the scrollable stream, house-style options, compact answered Q→A #56
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/coding-lab#56
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
When the agent asks a question (AskUserQuestion / plan approval), the chat web UI renders the picker inside the sticky bottom composer. A multi-question form is capped at half the viewport with its own inner scrollbar, so on mobile it effectively lays over the chat and the conversation behind it becomes unreachable. The pill-shaped option buttons also read poorly around two-line label + description content.
A second, related gap: once a question is answered, the transcript degrades it to a generic collapsed "AskUserQuestion" tool chip whose body is truncated raw JSON — what was asked and what was chosen is effectively unreadable in history, especially on mobile.
Reference experience: Claude Code's web interface (claude.ai/code), where questions are ordinary conversation content — inline in the one scrollable chat pane, options fully readable.
Decision record (grilled 2026-07-08)
Interviewed the maintainer through every design branch; all resolved:
max-heightscrollboxes on question/plan content are removed; the chat pane is the only scroll container.An earlier report that descriptions were "tooltip-only" traced to an old deployed build — not an issue on main.
Full specification in the agent brief comment below.
Agent Brief
Category: enhancement
Summary: Render agent questions as inline cards in the scrollable chat stream (Claude-Code-web style), restyle options as house-style cards, and show answered questions as compact Q→A summaries instead of raw tool chips.
Current behavior:
The chat view swaps the composer textarea for a question panel while a dialog is pending. Multi-question forms are capped at ~50% viewport height with a nested scrollbar; plan markdown is capped at ~40%. The panel sits pinned over the lower chat, which on mobile hides the conversation. Option buttons are pill-shaped (
radius-pill) segments; the flat multi-select path renders labels without their descriptions. Once a dialog tool acquires a result, the transcript translator stops emitting a dialog message and emits a generic tool message instead, so history shows only a collapsed chip named after the tool with truncated raw JSON input/output.Desired behavior:
Pending questions:
question,plan,approval) renders as an interactive card inside the chat stream, at the position of its dialog message in the transcript (in practice the last item — the agent is blocked on it). It must not render twice when both the live pending-dialog field and a dialog stream message describe the same tool id.questionbut no structured dialog is available) keeps its current locked note + Interrupt.Option cards (house style, not a claude.ai/code clone):
Answered questions in history:
Dialog/ chat message types, mirrored in the web API types) gains the answered/answer shape; conversation-state derivation must still treat only unanswered trailing dialogs as the question state.Key interfaces:
Dialogwire type — extend to represent a resolved dialog with its answer (per-question chosen indices/labels, other-text, dismissed flag). The web API mirror of these types follows.questiononly for pending) intact.--radius-control,--card,--accent, muted text).Acceptance criteria:
max-height/overflowscrollboxes inside the card).questiononly while unanswered.Out of scope:
Land-PR audit — PR #60 (
afk/56→main)Verdict: PASS.
Verification signal relied on: Forgejo Actions
ci / native— green on headde2ab20(4m32s). That is the repo's required check and covers the whole diff surface: Go tests,-tags uibuild+tests against the built dist, golangci-lint, and the SPA's eslint/prettier/vitest/vite build. The hermeticci-nixgate did not run — correctly: the diff touches no*.nix/flake.lock/go.mod/go.sumpaths (checked). Nothing was re-run locally.Checks:
feat(chat): ….afk/56, body carries a workingCloses #56.origin/main(git merge-tree), no conflict.tool_idagainst a matching stream message (card takes the transcript position when one matches, else appended).provider.Dialog.Outcome(nullable) derived in newdialogoutcome.gofrom the same compat §5toolUseResult/toolDenialKindground truth the intent backstop reads — no new Claude Code coupling; unreadable/denied/timeout resolutions degrade toDismissed, never to a pending-looking nil.deriveStatereportsquestiononly forOutcome == nil;internal/chat.lastDialogskips answered dialogs soAnswerDialogcan't target a resolved picker.planDialogdrops thetruncatecall; question prompts stay exempt too since the prompt is theanswersmap key).tool_idwhile following the tail; scrolled-up readers are never yanked;seenDialogToolIdresets on stream reset.--radius-control; descriptions now visible on all three picker paths incl. the flat multi-select that dropped them; innermax-heightscrollboxes removed.Non-blocking observation:
pendingCardFormatches bytool_idwithout checkingoutcome, so a transient race (answered stream message + stalepending_dialogfield in one refetch window) could momentarily show the interactive card instead of the summary — self-heals on the answer-triggered refetch; display-only.The PR body itself flags that no browser was available in the sandbox: a visual eyeball on the deployed build (mobile card arrival under the sticky header, option spacing, dark-mode tint) remains worthwhile after merge.
Merge is gated on the operator's explicit free-text go-ahead.