render every conversation turn through one card primitive
test / unit + widget + golden + a11y (push) Failing after 24s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 25s
test / unit + widget + golden + a11y (push) Failing after 24s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 25s
The Claude pane's conversation view hand-rolled a separate card layout per message kind (user/assistant/thinking/tool-use/tool-result), so any shared chrome had to be added five times. ConversationCard is one template with three variants (stripe/bordered/bare) that wires the chrome once: a copy button revealed on hover (yielding the turn's raw text), an always-visible collapse/expand caret for collapsible turns, and an extensible MessageAction list. It's decoupled from ConversationItem — the view maps each item to (variant, accent, label, body, copyText, actions) — so the typed event cards coming with the stream-json work reuse the same chrome with a different body. T-173. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2222,3 +2222,4 @@ INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by,
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-159', 'status', 'backlog', 'done', NULL, '2026-05-24 08:54:30', '2026-05-24 08:54:30', '2026-05-24 08:54:30', NULL, '478956f24d051f603aab2c7ceec328b2', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-161', 'status', 'backlog', 'done', NULL, '2026-05-24 09:45:07', '2026-05-24 09:45:07', '2026-05-24 09:45:07', NULL, 'd7b99c3dff59fc5a70a7866567fbfe0f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-165', 'status', 'backlog', 'in_progress', NULL, '2026-05-24 16:32:59', '2026-05-24 16:32:59', '2026-05-24 16:32:59', NULL, '1dbcae2366b429ad26e65f1a9cfebc33', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-173', 'status', 'backlog', 'in_progress', NULL, '2026-05-24 17:00:14', '2026-05-24 17:00:14', '2026-05-24 17:00:14', NULL, 'c0306e7ca9f755e69c76cdba93bca934', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
|
||||
@@ -2707,3 +2707,19 @@ INSERT INTO tickets (id, type, parent_id, title, description, status, priority,
|
||||
INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-171', 'task', 'T-164', 'P2: sidebar cockpit — task list + messages + roster controls', 'Evolve the Claude meta sidebar (T-141/T-157) from a read-only roster into the CONTROL surface for clide-managed agents. Because clide owns the broker, the task list and inter-agent messages are local data — so the sidebar both shows them live and lets the user ACT: spawn / show / hide / mute an agent, reassign a task, inject or redirect a message. Roster rows keep the live per-member status (T-157) but gain controls. Acceptance: from the sidebar the user can spawn/show/hide an agent, watch the shared task list update, see the message flow, and intervene (reassign/inject); widget tests for the controls wired to the orchestrator + broker. Blocked by T-169 (orchestrator) and T-170 (broker).', 'backlog', 'medium', NULL, NULL, 'D-77', '2026-05-24 16:27:09', '2026-05-24 16:27:09', NULL, '1134d3b5bbef2b2017d43a56b9fdac39', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||
INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-172', 'task', 'T-164', 'P2: fork-into-a-pane (--fork-session)', 'Branch a conversation into a new managed session/pane, reusing the orchestrator''s spawn plumbing. ''Fork'' = spawn a managed session seeded from another session''s context via --resume + --fork-session (resume the source, but create a new session-id so the branch diverges without touching the original). Surface it as a slash action and a sidebar/roster button on a session. Acceptance: forking an active session opens a new pane continuing from its context, independent thereafter; the original is unaffected; tests for the fork argv + the new managed session appearing in the roster. Blocked by T-169 (orchestrator).', 'backlog', 'low', NULL, NULL, 'D-77', '2026-05-24 16:27:16', '2026-05-24 16:27:16', NULL, 'e27d5e85bb9a9ce26ae8756b36448d44', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||
INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-165', 'task', 'T-164', 'P1: stream-json transport — drive the Claude pane via the control protocol', 'Spawn the Claude pane as ''claude --input-format stream-json --output-format stream-json --verbose'' (+ --include-partial-messages later) instead of an interactive TUI in tmux. Multiplex its stdin/stdout; parse the event stream (system/init, assistant, tool_use, tool_result, result, rate_limit_event); feed the existing ConversationController/MessageBus from events rather than tailing the transcript JSONL. Isolate the protocol behind one module (the drift-containment point, like the transcript reader). Acceptance: a Claude pane converses end-to-end over stream-json (send a prompt, stream the reply) with no tmux session; events drive the native conversation view; unit tests against canned event fixtures. Blocks the rest of phase 1. Evolves D-75 (rendering now sourced from events).', 'in_progress', 'high', NULL, NULL, 'D-77', '2026-05-24 16:26:15', '2026-05-24 16:32:59', NULL, '60aad91b8c9af23881c71ffc6c3082a8', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||
INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-173', 'task', 'T-164', 'Conversation message-card primitive: hover copy + collapse/expand, extensible base', 'Replace the three ad-hoc card shapes in conversation_view.dart (_messageCard stripe / _labelled / _card bordered) with ONE base primitive every turn renders through, with hover affordances wired into the base so all message types (now + future) get them consistently.
|
||||
|
||||
PRIMITIVE — ConversationCard (lib/builtin/claude/src/conversation_card.dart):
|
||||
- Props: variant (stripe | bordered | bare — preserves the user/claude vs tool/thinking visual distinction), accent (Color), label (String), body (Widget content slot), copyText (raw text the copy action yields), actions (List<MessageAction> — extensible), collapsible (bool) + collapsedByDefault (bool).
|
||||
- Chrome: the label row + a hover-revealed action bar (top-right): a copy button (ClideClipboard, copies copyText), a collapse/expand toggle when collapsible, and an extensible slot for future per-message actions. Body sits below.
|
||||
- Behaviors: actions reveal on hover (MouseRegion) but are ALWAYS reachable for keyboard/a11y (Semantics button + focusable, per the keyboard-operability work — never hover-only); collapse renders a short preview + an expand affordance; default-collapsed for noisy types (tool results, thinking, very long bodies); coexists with the surrounding SelectionArea (button-copy AND drag-select both work — no nested SelectionArea).
|
||||
|
||||
FUTURE-PROOFING (the point):
|
||||
- MessageAction { icon, label, onInvoke } is an open list — copy is just the first. Future actions (fork-from-here, retry, edit, jump-to, save) drop in without touching the template; ties directly into D-77''s unified-session model (e.g. fork-from-here = spawn a managed session seeded from this turn).
|
||||
- The card is DECOUPLED from ConversationItem — _ConversationTurn maps each item to (variant, accent, label, body, copyText, actions). So T-168''s richer typed cards (Edit/Write diff, Bash command+output, Read/Grep) reuse the SAME chrome with a different body + copyText + maybe extra actions; nothing in the template hardcodes a message kind.
|
||||
- Per-card collapse policy (not global) so prose stays open while tool noise collapses.
|
||||
- Variant enum keeps today''s look while unifying the implementation.
|
||||
|
||||
MIGRATION: route the existing switch in _ConversationTurn through the primitive; the three shapes become variants. Lands independently of the stream-json transport (works on the current conversation_view) but is the base T-168 builds on.
|
||||
|
||||
Acceptance: every turn type renders through the one primitive; hover-copy yields the right raw text per type; collapsible types expand/collapse (tool-result/thinking default-collapsed); actions are keyboard-focusable + Semantics-labelled (a11y); widget tests for copy, collapse, each variant, and an injected custom action; coverage >= floor. Feeds T-168.', 'in_progress', 'medium', NULL, NULL, 'D-77', '2026-05-24 16:45:40', '2026-05-24 17:00:14', NULL, '315d3cc6c134fc5a48a8527e62290554', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||
|
||||
Reference in New Issue
Block a user