feat(image): --file metadata payload for annotated image cards (T-316)
`clide image show --file meta.json` reads a {path,label,description,caption}
payload so an image can carry a title and a longer description, not just a
one-line caption. ImageMessage + the image card render the richer metadata;
the bare `image show <path> [--caption]` form is unchanged. Honest userError
on a malformed/missing payload. Text annotation only (option a) — visual
marker overlays stay a follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -749,6 +749,11 @@ class _ConversationTurn extends StatelessWidget {
|
||||
body: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Annotation title above the image (T-316), when a --file payload set it.
|
||||
if (m.label != null && m.label!.isNotEmpty) ...[
|
||||
ClideText(m.label!, fontSize: clideFontMeta, fontWeight: FontWeight.w600, color: tokens.globalForeground),
|
||||
const SizedBox(height: 4),
|
||||
],
|
||||
// The card stays display-only (D-78); the click is a navigation
|
||||
// gesture that opens the full-screen lightbox (T-252), not an inline
|
||||
// control.
|
||||
@@ -770,6 +775,10 @@ class _ConversationTurn extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (m.description != null && m.description!.isNotEmpty) ...[
|
||||
const SizedBox(height: 6),
|
||||
ClideText(m.description!, fontSize: clideFontCaption, color: tokens.globalTextMuted),
|
||||
],
|
||||
if (caption != null && caption.isNotEmpty) ...[const SizedBox(height: 4), ClideText(caption, fontSize: clideFontMeta, color: tokens.globalTextMuted)],
|
||||
],
|
||||
),
|
||||
|
||||
@@ -641,6 +641,8 @@ class ClaudeExtension extends ClideExtension {
|
||||
isSidechain: false,
|
||||
path: path,
|
||||
caption: m.data['caption'] as String?,
|
||||
label: m.data['label'] as String?,
|
||||
description: m.data['description'] as String?,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -178,7 +178,15 @@ final class AssistantToolUse extends ConversationItem {
|
||||
/// driver has already resolved (workspace-relative paths are resolved before
|
||||
/// injection); [caption] is an optional one-line label.
|
||||
final class ImageMessage extends ConversationItem {
|
||||
const ImageMessage({required super.uuid, required super.timestamp, required super.isSidechain, required this.path, this.caption});
|
||||
const ImageMessage({
|
||||
required super.uuid,
|
||||
required super.timestamp,
|
||||
required super.isSidechain,
|
||||
required this.path,
|
||||
this.caption,
|
||||
this.label,
|
||||
this.description,
|
||||
});
|
||||
|
||||
/// Absolute path to the image file on disk.
|
||||
final String path;
|
||||
@@ -186,6 +194,10 @@ final class ImageMessage extends ConversationItem {
|
||||
/// Optional caption shown under the image.
|
||||
final String? caption;
|
||||
|
||||
/// Optional richer annotations from a `--file` metadata payload (T-316): a
|
||||
/// title/label above the image and a longer description beneath it.
|
||||
final String? label, description;
|
||||
|
||||
@override
|
||||
String toString() => 'ImageMessage($path${caption != null ? ', "$caption"' : ''})';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user