From 2e1bdaec79c15d7f2e4be77386ca295e5a57e444 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Sun, 7 Jun 2026 10:32:36 +0200 Subject: [PATCH] close T-252 (image lightbox) Co-Authored-By: Claude Opus 4.8 (1M context) --- .pql/changelog/ticket_history/2026-06.sql | 2 ++ .pql/changelog/tickets/2026-06.sql | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/.pql/changelog/ticket_history/2026-06.sql b/.pql/changelog/ticket_history/2026-06.sql index 91357660..76a4a4e2 100644 --- a/.pql/changelog/ticket_history/2026-06.sql +++ b/.pql/changelog/ticket_history/2026-06.sql @@ -515,3 +515,5 @@ 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-36', 'status', 'review', 'backlog', NULL, '2026-06-06 21:14:22', '2026-06-06 21:14:22', '2026-06-06 21:14:22', NULL, 'cbecd31c7d0a269f97351da4c1a6e691', 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-255', 'status', 'backlog', 'ready', NULL, '2026-06-06 21:43:40', '2026-06-06 21:43:40', '2026-06-06 21:43:40', NULL, 'bc4878fd6f920def39986ab0c9b2c34e', 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-54', 'status', 'in_progress', 'done', NULL, '2026-06-07 08:03:17', '2026-06-07 08:03:17', '2026-06-07 08:03:17', NULL, 'e166c11b3839c37954e7e3c21194994a', 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-252', 'status', 'ready', 'in_progress', NULL, '2026-06-07 08:07:42', '2026-06-07 08:07:42', '2026-06-07 08:07:42', NULL, 'ecff1f56ea618c88ac4f2dd9e5f53e41', 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-252', 'status', 'in_progress', 'done', NULL, '2026-06-07 08:32:36', '2026-06-07 08:32:36', '2026-06-07 08:32:36', NULL, '7f66c2872874836d984b5e7f516ba3ff', 1) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/tickets/2026-06.sql b/.pql/changelog/tickets/2026-06.sql index ec2e0d13..0da02ef4 100644 --- a/.pql/changelog/tickets/2026-06.sql +++ b/.pql/changelog/tickets/2026-06.sql @@ -1193,3 +1193,13 @@ Edge cases to handle: 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); +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 --fullscreen, or clide image open ), 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.', 'done', 'medium', NULL, NULL, 'D-78', '2026-06-06 08:28:43', '2026-06-07 08:32:36', NULL, 'ed2e2cec91fa720e170d287db445fdaa', 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);