close T-236 + T-254 (inline pasted-image thumbnails, D-89)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 17:55:24 +02:00
co-authored by Claude Opus 4.8
parent 529f40996d
commit 7fb1a10982
2 changed files with 156 additions and 0 deletions
+76
View File
@@ -1753,3 +1753,79 @@ STATUS INDICATOR (live tick reuse): the bundled edits card carries ONE header st
- next edit starts -> back to spinner
- ...repeat, settling on check when the final edit in the run completes (or the error glyph if one fails).
So the collapsed card''s indicator animates spinner<->check as the run grows, rather than showing a static tick. Each individual edit keeps its own tick in the expanded list (unchanged); this is the aggregate indicator on the holder header/ticker. Mirror the same treatment for the existing "# steps" activity card if it doesn''t already do this.', NULL, '2026-06-09 15:46:02', '2026-06-09 15:46:02', '2026-06-09 15:46:02', NULL, '3967ce1c50019ad946ae4484f58209d3', 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-236', 'description', 'When a user message contains a pasted-screenshot reference — an @<path> token pointing at an image (the composer''s ComposerAttachment.pathToken format, e.g. @/home/<user>/.cache/clide/pasted/paste-<ts>.png, see screenshot) — the conversation log renders it as the raw path string. Show it as an inline THUMBNAIL instead.
The composer already renders pre-send attachments as Image.file thumbnail chips (clipboard_paste.dart + claude_composer _chip); this carries that into the post-send message log (UserMessage rendering in conversation_view/conversation_card).
Scope:
- When rendering UserMessage text, detect @<path> tokens; for image paths (reuse the extension check behind _looksLikeImage: .png/.jpg/.jpeg/.gif/.webp/.bmp), render a bounded Image.file thumbnail in place of the bare token. Surrounding prose still renders normally; a message can carry multiple tokens.
- Click/activate the thumbnail to open the full-size image (a preview dialog). Keyboard-operable + a11y label (filename).
- Graceful fallback: a missing/unreadable file (pasted temp files can be cleaned up) degrades to a small placeholder or the path text via Image.file errorBuilder never a crash/exception.
- Non-image @path tokens MAY render as a file chip (filename + icon) like the composer, but image thumbnails are the focus.
- Note: Image.file reads the path directly via dart:io, so this is NOT gated by the files.read allow-list (D-80) the cache dir is outside the workspace and that''s fine for display.
- Display-only: the text actually sent to Claude is unchanged; this only affects rendering.
Acceptance:
1. A user message with an @<path> image token shows an inline thumbnail in the log (not the raw path).
2. Clicking/activating the thumbnail opens the full image.
3. A missing/unreadable referenced file degrades to a placeholder (or the path text), no exception.
4. Prose and any non-image @path tokens around it still render readably; multiple tokens in one message all resolve.
5. The message content delivered to Claude is unchanged (render-only). Relates to T-142 (paste attachments).
MERGED T-254 (duplicate) into this ticket. Extra nuance carried over from T-254: keep the file path available (e.g. as a small caption/subtitle under the thumbnail, or via the lightbox) so it can still be copied/referenced. Reuse the existing inline image card + lightbox (T-252) rather than a new component.
RETRACTED the earlier ''merged T-254'' note T-236 and T-254 are distinct tickets (per user); disregard that note. T-236 stays scoped to its own description.', 'When a user message contains a pasted-screenshot reference an @<path> token pointing at an image (the composer''s ComposerAttachment.pathToken format, e.g. @/home/<user>/.cache/clide/pasted/paste-<ts>.png, see screenshot) the conversation log renders it as the raw path string. Show it as an inline THUMBNAIL instead.
The composer already renders pre-send attachments as Image.file thumbnail chips (clipboard_paste.dart + claude_composer _chip); this carries that into the post-send message log (UserMessage rendering in conversation_view/conversation_card).
Scope:
- When rendering UserMessage text, detect @<path> tokens; for image paths (reuse the extension check behind _looksLikeImage: .png/.jpg/.jpeg/.gif/.webp/.bmp), render a bounded Image.file thumbnail in place of the bare token. Surrounding prose still renders normally; a message can carry multiple tokens.
- Click/activate the thumbnail to open the full-size image (a preview dialog). Keyboard-operable + a11y label (filename).
- Graceful fallback: a missing/unreadable file (pasted temp files can be cleaned up) degrades to a small placeholder or the path text via Image.file errorBuilder never a crash/exception.
- Non-image @path tokens MAY render as a file chip (filename + icon) like the composer, but image thumbnails are the focus.
- Note: Image.file reads the path directly via dart:io, so this is NOT gated by the files.read allow-list (D-80) the cache dir is outside the workspace and that''s fine for display.
- Display-only: the text actually sent to Claude is unchanged; this only affects rendering.
Acceptance:
1. A user message with an @<path> image token shows an inline thumbnail in the log (not the raw path).
2. Clicking/activating the thumbnail opens the full image.
3. A missing/unreadable referenced file degrades to a placeholder (or the path text), no exception.
4. Prose and any non-image @path tokens around it still render readably; multiple tokens in one message all resolve.
5. The message content delivered to Claude is unchanged (render-only). Relates to T-142 (paste attachments).
MERGED T-254 (duplicate) into this ticket. Extra nuance carried over from T-254: keep the file path available (e.g. as a small caption/subtitle under the thumbnail, or via the lightbox) so it can still be copied/referenced. Reuse the existing inline image card + lightbox (T-252) rather than a new component.
RETRACTED the earlier ''merged T-254'' note T-236 and T-254 are distinct tickets (per user); disregard that note. T-236 stays scoped to its own description.
DONE 2026-06-09 implemented as the hybrid (D-89): inline thumbnail in the message prose that opens the lightbox on click/Enter. New ImageThumbnail + openImageLightbox (lib/builtin/claude/src/image_thumbnail.dart); ClideMarkdown gained an onImageToken WidgetSpan seam. Missing file placeholder; multiple tokens; non-image @path stays text; render-only (copyText unchanged). Composer chips reuse the same 44px thumbnail. Tests: image_thumbnail_test + conversation_view_test cases. Closes alongside T-254.', NULL, '2026-06-09 15:55:06', '2026-06-09 15:55:06', '2026-06-09 15:55:06', NULL, '14703b7b9644d40d524089077725d751', 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-236', 'status', 'ready', 'done', NULL, '2026-06-09 15:55:06', '2026-06-09 15:55:06', '2026-06-09 15:55:06', NULL, 'c0cf2740ab59fe5b3401c8a86d8a9f61', 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-254', 'description', 'When the user pastes an image, the conversation panel echoes back the file path as plain text (e.g. @/home/.../paste-<ts>.png). We now have an image viewer card — render the pasted image inline using that card instead of (or in addition to) the bare path.
## Behaviour
- Detect a pasted-image path in the conversation stream and render the image viewer card.
- Still surface the file path (e.g. as a caption / subtitle on the card) so it can be copied/referenced.
- Reuse the existing image viewer card component rather than building a new one.
## Notes
- Applies to the Claude conversation panel rendering path.
- Consider failure cases: missing/deleted file, non-image paste, very large images.
CANCELLED as a duplicate of T-236 (2026-06-09). T-236 is the concrete spec for rendering pasted @path images inline in the Claude conversation; its scope now includes T-254''s path-as-caption nuance. Implement under T-236.
RETRACTED the cancellation NOT a duplicate of T-236 (per user, 2026-06-09). Both stay open. T-254 remains its own ticket.', 'When the user pastes an image, the conversation panel echoes back the file path as plain text (e.g. @/home/.../paste-<ts>.png). We now have an image viewer card render the pasted image inline using that card instead of (or in addition to) the bare path.
## Behaviour
- Detect a pasted-image path in the conversation stream and render the image viewer card.
- Still surface the file path (e.g. as a caption / subtitle on the card) so it can be copied/referenced.
- Reuse the existing image viewer card component rather than building a new one.
## Notes
- Applies to the Claude conversation panel rendering path.
- Consider failure cases: missing/deleted file, non-image paste, very large images.
CANCELLED as a duplicate of T-236 (2026-06-09). T-236 is the concrete spec for rendering pasted @path images inline in the Claude conversation; its scope now includes T-254''s path-as-caption nuance. Implement under T-236.
RETRACTED the cancellation NOT a duplicate of T-236 (per user, 2026-06-09). Both stay open. T-254 remains its own ticket.
DONE 2026-06-09 satisfied by D-89 together with T-236. The lightbox-expansion + path-retention (filename a11y label / unchanged copyText) intent from T-254 is delivered by the shared ImageThumbnail lightbox; the chosen presentation is inline (not a separate card) per the user. See T-236 / D-89 / commit.', NULL, '2026-06-09 15:55:06', '2026-06-09 15:55:06', '2026-06-09 15:55:06', NULL, 'c6090b1f5fdbdee4b56e6fd1ac39a9e2', 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-254', 'status', 'ready', 'done', NULL, '2026-06-09 15:55:06', '2026-06-09 15:55:06', '2026-06-09 15:55:06', NULL, 'e07372e7e9662712183808199ac27210', 1) ON CONFLICT(hash) DO NOTHING;
+80
View File
@@ -3051,3 +3051,83 @@ STATUS INDICATOR (live tick reuse): the bundled edits card carries ONE header st
- next edit starts -> back to spinner
- ...repeat, settling on check when the final edit in the run completes (or the error glyph if one fails).
So the collapsed card''s indicator animates spinner<->check as the run grows, rather than showing a static tick. Each individual edit keeps its own tick in the expanded list (unchanged); this is the aggregate indicator on the holder header/ticker. Mirror the same treatment for the existing "# steps" activity card if it doesn''t already do this.', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-09 15:43:58', '2026-06-09 15:46:02', NULL, '21544d43b43b8da6a16a2a4bf0bc901d', 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-236', 'story', 'T-132', 'Render pasted-image @path tokens as thumbnails in the message log', 'When a user message contains a pasted-screenshot reference — an @<path> token pointing at an image (the composer''s ComposerAttachment.pathToken format, e.g. @/home/<user>/.cache/clide/pasted/paste-<ts>.png, see screenshot) — the conversation log renders it as the raw path string. Show it as an inline THUMBNAIL instead.
The composer already renders pre-send attachments as Image.file thumbnail chips (clipboard_paste.dart + claude_composer _chip); this carries that into the post-send message log (UserMessage rendering in conversation_view/conversation_card).
Scope:
- When rendering UserMessage text, detect @<path> tokens; for image paths (reuse the extension check behind _looksLikeImage: .png/.jpg/.jpeg/.gif/.webp/.bmp), render a bounded Image.file thumbnail in place of the bare token. Surrounding prose still renders normally; a message can carry multiple tokens.
- Click/activate the thumbnail to open the full-size image (a preview dialog). Keyboard-operable + a11y label (filename).
- Graceful fallback: a missing/unreadable file (pasted temp files can be cleaned up) degrades to a small placeholder or the path text via Image.file errorBuilder never a crash/exception.
- Non-image @path tokens MAY render as a file chip (filename + icon) like the composer, but image thumbnails are the focus.
- Note: Image.file reads the path directly via dart:io, so this is NOT gated by the files.read allow-list (D-80) the cache dir is outside the workspace and that''s fine for display.
- Display-only: the text actually sent to Claude is unchanged; this only affects rendering.
Acceptance:
1. A user message with an @<path> image token shows an inline thumbnail in the log (not the raw path).
2. Clicking/activating the thumbnail opens the full image.
3. A missing/unreadable referenced file degrades to a placeholder (or the path text), no exception.
4. Prose and any non-image @path tokens around it still render readably; multiple tokens in one message all resolve.
5. The message content delivered to Claude is unchanged (render-only). Relates to T-142 (paste attachments).
MERGED T-254 (duplicate) into this ticket. Extra nuance carried over from T-254: keep the file path available (e.g. as a small caption/subtitle under the thumbnail, or via the lightbox) so it can still be copied/referenced. Reuse the existing inline image card + lightbox (T-252) rather than a new component.
RETRACTED the earlier ''merged T-254'' note T-236 and T-254 are distinct tickets (per user); disregard that note. T-236 stays scoped to its own description.
DONE 2026-06-09 implemented as the hybrid (D-89): inline thumbnail in the message prose that opens the lightbox on click/Enter. New ImageThumbnail + openImageLightbox (lib/builtin/claude/src/image_thumbnail.dart); ClideMarkdown gained an onImageToken WidgetSpan seam. Missing file placeholder; multiple tokens; non-image @path stays text; render-only (copyText unchanged). Composer chips reuse the same 44px thumbnail. Tests: image_thumbnail_test + conversation_view_test cases. Closes alongside T-254.', 'ready', 'medium', NULL, NULL, NULL, '2026-06-03 15:31:01', '2026-06-09 15:55:06', NULL, 'a7f6817b8f2f1ce8130ad5429415c373', 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-236', 'story', 'T-132', 'Render pasted-image @path tokens as thumbnails in the message log', 'When a user message contains a pasted-screenshot reference — an @<path> token pointing at an image (the composer''s ComposerAttachment.pathToken format, e.g. @/home/<user>/.cache/clide/pasted/paste-<ts>.png, see screenshot) — the conversation log renders it as the raw path string. Show it as an inline THUMBNAIL instead.
The composer already renders pre-send attachments as Image.file thumbnail chips (clipboard_paste.dart + claude_composer _chip); this carries that into the post-send message log (UserMessage rendering in conversation_view/conversation_card).
Scope:
- When rendering UserMessage text, detect @<path> tokens; for image paths (reuse the extension check behind _looksLikeImage: .png/.jpg/.jpeg/.gif/.webp/.bmp), render a bounded Image.file thumbnail in place of the bare token. Surrounding prose still renders normally; a message can carry multiple tokens.
- Click/activate the thumbnail to open the full-size image (a preview dialog). Keyboard-operable + a11y label (filename).
- Graceful fallback: a missing/unreadable file (pasted temp files can be cleaned up) degrades to a small placeholder or the path text via Image.file errorBuilder never a crash/exception.
- Non-image @path tokens MAY render as a file chip (filename + icon) like the composer, but image thumbnails are the focus.
- Note: Image.file reads the path directly via dart:io, so this is NOT gated by the files.read allow-list (D-80) the cache dir is outside the workspace and that''s fine for display.
- Display-only: the text actually sent to Claude is unchanged; this only affects rendering.
Acceptance:
1. A user message with an @<path> image token shows an inline thumbnail in the log (not the raw path).
2. Clicking/activating the thumbnail opens the full image.
3. A missing/unreadable referenced file degrades to a placeholder (or the path text), no exception.
4. Prose and any non-image @path tokens around it still render readably; multiple tokens in one message all resolve.
5. The message content delivered to Claude is unchanged (render-only). Relates to T-142 (paste attachments).
MERGED T-254 (duplicate) into this ticket. Extra nuance carried over from T-254: keep the file path available (e.g. as a small caption/subtitle under the thumbnail, or via the lightbox) so it can still be copied/referenced. Reuse the existing inline image card + lightbox (T-252) rather than a new component.
RETRACTED the earlier ''merged T-254'' note T-236 and T-254 are distinct tickets (per user); disregard that note. T-236 stays scoped to its own description.
DONE 2026-06-09 implemented as the hybrid (D-89): inline thumbnail in the message prose that opens the lightbox on click/Enter. New ImageThumbnail + openImageLightbox (lib/builtin/claude/src/image_thumbnail.dart); ClideMarkdown gained an onImageToken WidgetSpan seam. Missing file placeholder; multiple tokens; non-image @path stays text; render-only (copyText unchanged). Composer chips reuse the same 44px thumbnail. Tests: image_thumbnail_test + conversation_view_test cases. Closes alongside T-254.', 'done', 'medium', NULL, NULL, NULL, '2026-06-03 15:31:01', '2026-06-09 15:55:06', NULL, '081e1a85313e3da1455cc931c500a934', 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-254', 'story', NULL, 'Render pasted images via the image viewer card in the Claude conversation panel instead of echoing only the file path', 'When the user pastes an image, the conversation panel echoes back the file path as plain text (e.g. @/home/.../paste-<ts>.png). We now have an image viewer card — render the pasted image inline using that card instead of (or in addition to) the bare path.
## Behaviour
- Detect a pasted-image path in the conversation stream and render the image viewer card.
- Still surface the file path (e.g. as a caption / subtitle on the card) so it can be copied/referenced.
- Reuse the existing image viewer card component rather than building a new one.
## Notes
- Applies to the Claude conversation panel rendering path.
- Consider failure cases: missing/deleted file, non-image paste, very large images.
CANCELLED as a duplicate of T-236 (2026-06-09). T-236 is the concrete spec for rendering pasted @path images inline in the Claude conversation; its scope now includes T-254''s path-as-caption nuance. Implement under T-236.
RETRACTED the cancellation NOT a duplicate of T-236 (per user, 2026-06-09). Both stay open. T-254 remains its own ticket.
DONE 2026-06-09 satisfied by D-89 together with T-236. The lightbox-expansion + path-retention (filename a11y label / unchanged copyText) intent from T-254 is delivered by the shared ImageThumbnail lightbox; the chosen presentation is inline (not a separate card) per the user. See T-236 / D-89 / commit.', 'ready', 'medium', NULL, NULL, NULL, '2026-06-06 09:32:54', '2026-06-09 15:55:06', NULL, 'cbc4c05333cd3cbec389030385541d17', 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-254', 'story', NULL, 'Render pasted images via the image viewer card in the Claude conversation panel instead of echoing only the file path', 'When the user pastes an image, the conversation panel echoes back the file path as plain text (e.g. @/home/.../paste-<ts>.png). We now have an image viewer card — render the pasted image inline using that card instead of (or in addition to) the bare path.
## Behaviour
- Detect a pasted-image path in the conversation stream and render the image viewer card.
- Still surface the file path (e.g. as a caption / subtitle on the card) so it can be copied/referenced.
- Reuse the existing image viewer card component rather than building a new one.
## Notes
- Applies to the Claude conversation panel rendering path.
- Consider failure cases: missing/deleted file, non-image paste, very large images.
CANCELLED as a duplicate of T-236 (2026-06-09). T-236 is the concrete spec for rendering pasted @path images inline in the Claude conversation; its scope now includes T-254''s path-as-caption nuance. Implement under T-236.
RETRACTED the cancellation NOT a duplicate of T-236 (per user, 2026-06-09). Both stay open. T-254 remains its own ticket.
DONE 2026-06-09 satisfied by D-89 together with T-236. The lightbox-expansion + path-retention (filename a11y label / unchanged copyText) intent from T-254 is delivered by the shared ImageThumbnail lightbox; the chosen presentation is inline (not a separate card) per the user. See T-236 / D-89 / commit.', 'done', 'medium', NULL, NULL, NULL, '2026-06-06 09:32:54', '2026-06-09 15:55:06', NULL, '0ed8b0f0f4012715eb1a62de2cae9ad6', 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);