add ClideLightbox — click image cards to enlarge (T-252)
The inline image cards (T-249) are often too small to read. Clicking one now opens a full-screen lightbox: zoom (scroll wheel / pinch), pan when zoomed, double-click to reset to fit, Esc / close button / backdrop click to dismiss. ClideLightbox is a reusable primitive (lib/widgets/) over Flutter's InteractiveViewer with clide-owned zoom gestures, shown via the DialogRouter (dimmed backdrop, single modal at a time, D-78). The card stays display-only; the click is a navigation gesture, not an inline control. CLI parity (D-6): `clide image show <path> --fullscreen` opens straight into the lightbox instead of injecting a card. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1155,3 +1155,41 @@ 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-54', 'story', NULL, 'output and log panel', 'Bottom panel or context tab showing daemon logs, build output, extension logs, and pql sync output. Filterable by source. Auto-scrolls to latest. Useful for debugging extension and IPC issues.
|
||||
|
||||
UX design (2026-06-06, D-87): the panel is a bottom OUTPUT DOCK, read-only, two tabs — Output (the Logger stream, filter by source/level/text, auto-scroll) + Problems (moved out of the sidebar; no duplication). Toggled by a single status-bar widget that REPLACES the app-status indicator (merged health+log: green check when clean, warn/error counts when not, chevron for open state) — opens with click or Cmd/Ctrl+J. Needs a bounded in-memory ring sink on the Logger (no history today). Layout amends D-47 (dock pushes Claude up, capped so Claude stays >=50%). Terminal is NOT in the dock — kept first-class in the editor pane, tracked by T-258. Resolves Q-28. Wireframe: docs/design/wireframes/output-dock/.', 'done', 'medium', NULL, NULL, NULL, '2026-04-23 20:32:06', '2026-06-07 08:03:17', NULL, 'ced24a85b437a7c7f91515dd0ddf86cc', 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-252', 'story', NULL, 'Full-screen lightbox overlay (zoom + pan) for image cards', 'Click an inline image card (T-249) to open it in a full-screen overlay with zoom + pan — the standard lightbox UX from other apps (dimmed backdrop, image fit-to-screen, scroll/pinch to zoom, drag to pan when zoomed past fit, Esc or backdrop-click to dismiss).
|
||||
|
||||
Build the overlay as a SHARED PRIMITIVE WIDGET, not image-specific. A reusable zoomable-viewer / lightbox primitive under lib/widgets/ (e.g. ClideLightbox + ClideZoomPanViewer) that takes any child/content; the image card is its first consumer, and the canvas, graph, and diff/image previews can adopt it later. Own-the-rendering-stack + prefer-zero-deps still apply: Flutter''s SDK InteractiveViewer is the SDK-first starting point (allowed), but keep the gesture/zoom logic in the primitive so we control the UX, not an opinionated package.
|
||||
|
||||
Scope:
|
||||
- Shared primitive (lib/widgets/): a full-screen overlay with a dimmed backdrop hosted via the existing DialogRouter (single-at-a-time modal, D-78 interaction surface). Content scales to fit on open; zoom via scroll wheel / pinch / +- ; pan by drag when zoomed in; double-click toggles fit<->100%/2x; Esc and backdrop-click dismiss. Min/max zoom clamp. Keyboard + AT reachable (ClideTappable / Semantics), theme tokens / ui-design for any chrome (close affordance, zoom hint).
|
||||
- Image card wiring (T-249): the inline card stays display-only per D-78 — the click is a navigation gesture that opens the lightbox, not an inline control. Render the same resolved file at full resolution in the overlay; missing-file degrades to the existing placeholder.
|
||||
- Parity (D-6): a CLI counterpart to open an image straight into the lightbox (e.g. clide image show <path> --fullscreen, or clide image open <path>), registered so it surfaces in clide capabilities (T-248).
|
||||
|
||||
Refs: D-78 (interaction zone / display-only conversation widgets), D-6 (CLI/UI parity), DialogRouter (lib/kernel/src/dialog.dart). Builds on T-249 (image card + clide image show). Related: T-248.', 'in_progress', 'medium', NULL, NULL, 'D-78', '2026-06-06 08:28:43', '2026-06-07 08:07:42', NULL, '4abfa2ec36914b4ffcb8115a154f12c4', 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-262', 'story', NULL, 'Merge tool-call + success result into one card with header status check', 'Today a Claude tool call renders as TWO stacked cards in the conversation log: the tool-use card (e.g. "Write <path>" with a collapsible call body) and a separate success/result card ("Write · result / File created successfully..."). Collapse the successful pair into ONE card; failure keeps the current two-card interaction.
|
||||
|
||||
Current code: lib/builtin/claude/src/conversation_view.dart — _toolUse (~L294) renders AssistantToolUse, _toolResult (~L329) renders ToolResultMessage; the two ConversationItems are linked by toolUseId (ToolResultMessage.isError marks failure). ConversationCard header is built in lib/builtin/claude/src/conversation_card.dart _header (~L200). Builds on T-168 (per-tool body rendering).
|
||||
|
||||
Desired behavior (success):
|
||||
- One card per successful tool call — the tool-use card. The standalone success result card is suppressed (folded in), not rendered separately.
|
||||
- A success-green check mark sits at the RIGHT END of the card header row (reuse tokens.statusSuccess + a check glyph: PhosphorIcons.check or CheckIcon). No new theme tokens.
|
||||
- Collapsed by default: header shows the existing summary + the check. Expanding the card reveals the CALL segment (the input — path / diff / command, as today) and, BELOW it, the RESULT segment (the swallowed output). This applies to ALL tools, not just Write/Edit: for Bash/Read/Grep/LS the real output relocates into the expandable body so nothing is lost; for Write/Edit the trivial confirmation folds in the same way.
|
||||
- Render the result segment as an inline ClideCodeBlock (lib/widgets/src/clide_code_block.dart) WITH editor syntax colorization. ClideCodeBlock already colorizes via TreeSitterService when handed a language — so the work is inferring/passing the right language per tool: Read → from the file extension/path (language_map.grammarForPath), Bash → bash, Grep/LS/others → text fallback. Where a path is available, prefer it so highlighting matches the editor.
|
||||
|
||||
Desired behavior (failure) — UNCHANGED:
|
||||
- Leave the current interaction as-is: the tool-use card plus the prominent red "· error" result card (borderColor statusError, expanded-by-default). Optionally show a red status mark on the tool-use header for symmetry, but do NOT fold the error into one card.
|
||||
|
||||
What to build:
|
||||
1. ConversationCard: a trailing header status slot (e.g. status: success|error|none) rendered in _header between the Spacer/summary and the hover action buttons.
|
||||
2. ConversationCard: support a second body segment so an expanded card can show CALL then RESULT with a clear visual separator / sub-label (e.g. a divider or a muted "result" label) so the user can tell the call from its output.
|
||||
3. conversation_view: a result-by-toolUseId lookup (reverse of the existing toolUseById) so a tool-use card knows its outcome; on a successful pair, stamp the check + fold the result body and SUPPRESS the standalone success ToolResultMessage from the rendered list. Errors render both cards as today.
|
||||
4. Per-tool result language inference for the folded code block.
|
||||
|
||||
Edge cases to handle:
|
||||
- In-flight tool-use with no result yet: render the call card as today (no check, no folded result); status/result appear once the result arrives.
|
||||
- Orphan result with no paired tool-use: keep rendering it standalone.
|
||||
- Permission-resolved tool-use cards (conversation_view.dart ~L297, already green/red bordered + collapsed): reconcile so the merged-card + check treatment is consistent and not duplicated with the existing border-outcome styling.
|
||||
- Result items may not be strictly adjacent to their tool-use in the list; suppression must be keyed by toolUseId, not list position.
|
||||
|
||||
Tests: unit/widget coverage for the merged success card (collapsed shows check; expanded shows call + colorized result), language inference per tool, the unchanged error path, and the in-flight/orphan cases. Add a golden for the merged success card.
|
||||
|
||||
Refs: D-78 (interaction zone / display-only conversation widgets). Builds on T-168 (per-tool tool-use/result body rendering).', 'backlog', 'medium', NULL, NULL, 'D-78', '2026-06-07 08:10:42', '2026-06-07 08:10:42', NULL, '3eec2008cc279ef886612e74651cb081', 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