Skip to content

Commit 753d4d1

Browse files
author
SqlRush
committed
Normalize image hint metadata keys
1 parent cd174ac commit 753d4d1

3 files changed

Lines changed: 36 additions & 118 deletions

File tree

‎docs/first-second-parity-audit.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ M7 progress now includes:
411411
- `internal/session`: image-cache now supports session-scoped path caching, base64 image writes, image-only bulk storage, stored-path lookup, cache clearing, and cleanup of non-current session cache directories.
412412
- `internal/session`/`internal/contracts`: prompt pasted-content conversion can now build Anthropic image `source` content blocks from image paste metadata, expand text paste refs, and append source-path meta messages for cached images.
413413
- `internal/session`/`internal/tui`: pasted image metadata now carries `dimensions` and `sourcePath`, accepts common source/dimension aliases, and renders official-style image metadata text with source path plus original/display dimensions and coordinate scale.
414-
- `internal/tui`: image hint parsing now reads iTerm2 OSC File `width`/`height`, original/display dimension aliases, and `sourcePath`/`source_path`/`path`, then preserves those fields through prompt pasted image metadata.
414+
- `internal/tui`: image hint parsing now reads iTerm2 OSC File `width`/`height`, original/display dimension aliases, and `sourcePath`/`source_path`/`path`, normalizing metadata key case plus `_`/`-` separators, then preserves those fields through prompt pasted image metadata.
415415
- `internal/tui`: PromptInput/REPL screen can enable a session-scoped image-cache so image hint paste caches the image path and writes the base64 image file while inserting the `[Image #N]` prompt reference.
416416
- `internal/tui`: image paste cache now writes generated cache paths back into `PastedContent.SourcePath` when no original source path exists, so prompt metadata/history restore paths do not depend only on global image-id cache lookup.
417417
- `internal/tui`: PromptInput paste now strips ANSI, normalizes carriage returns, expands tabs, and uses the official 800-character plus `min(rows-10, 2)` visible-line threshold to decide whether pasted text stays inline or becomes a pasted-content reference.

‎internal/tui/input.go‎

Lines changed: 25 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,131 +1240,24 @@ func parseImageHint(seq string) (imageHint, bool) {
12401240
sourcePath := ""
12411241
dimensions := session.ImageDimensions{}
12421242
for _, field := range strings.Split(metadata, ";") {
1243-
if raw, ok := strings.CutPrefix(field, "name="); ok {
1244-
name = decodeImageName(raw)
1245-
continue
1246-
}
1247-
if raw, ok := strings.CutPrefix(field, "filename="); ok {
1248-
name = decodeImageName(raw)
1249-
continue
1250-
}
1251-
if raw, ok := strings.CutPrefix(field, "fileName="); ok {
1252-
name = decodeImageName(raw)
1243+
key, raw, ok := parseImageHintMetadataField(field)
1244+
if !ok {
12531245
continue
12541246
}
1255-
if raw, ok := strings.CutPrefix(field, "file_name="); ok {
1247+
switch key {
1248+
case "name", "filename", "file":
12561249
name = decodeImageName(raw)
1257-
continue
1258-
}
1259-
if raw, ok := strings.CutPrefix(field, "type="); ok {
1260-
mediaType = strings.TrimSpace(raw)
1261-
continue
1262-
}
1263-
if raw, ok := strings.CutPrefix(field, "mediaType="); ok {
1264-
mediaType = strings.TrimSpace(raw)
1265-
continue
1266-
}
1267-
if raw, ok := strings.CutPrefix(field, "media_type="); ok {
1268-
mediaType = strings.TrimSpace(raw)
1269-
continue
1270-
}
1271-
if raw, ok := strings.CutPrefix(field, "mimeType="); ok {
1272-
mediaType = strings.TrimSpace(raw)
1273-
continue
1274-
}
1275-
if raw, ok := strings.CutPrefix(field, "mime="); ok {
1250+
case "type", "mediatype", "mimetype", "mime", "contenttype":
12761251
mediaType = strings.TrimSpace(raw)
1277-
continue
1278-
}
1279-
if raw, ok := strings.CutPrefix(field, "mime_type="); ok {
1280-
mediaType = strings.TrimSpace(raw)
1281-
continue
1282-
}
1283-
if raw, ok := strings.CutPrefix(field, "contentType="); ok {
1284-
mediaType = strings.TrimSpace(raw)
1285-
continue
1286-
}
1287-
if raw, ok := strings.CutPrefix(field, "content_type="); ok {
1288-
mediaType = strings.TrimSpace(raw)
1289-
continue
1290-
}
1291-
if raw, ok := strings.CutPrefix(field, "sourcePath="); ok {
1292-
sourcePath = strings.TrimSpace(raw)
1293-
continue
1294-
}
1295-
if raw, ok := strings.CutPrefix(field, "source_path="); ok {
1252+
case "sourcepath", "path", "filepath", "imagepath", "source", "sourceurl", "fileurl", "imageurl", "url", "sourceuri", "fileuri", "imageuri", "uri":
12961253
sourcePath = strings.TrimSpace(raw)
1297-
continue
1298-
}
1299-
if raw, ok := strings.CutPrefix(field, "path="); ok {
1300-
sourcePath = strings.TrimSpace(raw)
1301-
continue
1302-
}
1303-
if raw, ok := strings.CutPrefix(field, "filePath="); ok {
1304-
sourcePath = strings.TrimSpace(raw)
1305-
continue
1306-
}
1307-
if raw, ok := strings.CutPrefix(field, "file_path="); ok {
1308-
sourcePath = strings.TrimSpace(raw)
1309-
continue
1310-
}
1311-
if raw, ok := strings.CutPrefix(field, "source="); ok {
1312-
sourcePath = strings.TrimSpace(raw)
1313-
continue
1314-
}
1315-
if raw, ok := strings.CutPrefix(field, "sourceURL="); ok {
1316-
sourcePath = strings.TrimSpace(raw)
1317-
continue
1318-
}
1319-
if raw, ok := strings.CutPrefix(field, "source_url="); ok {
1320-
sourcePath = strings.TrimSpace(raw)
1321-
continue
1322-
}
1323-
if raw, ok := strings.CutPrefix(field, "url="); ok {
1324-
sourcePath = strings.TrimSpace(raw)
1325-
continue
1326-
}
1327-
if raw, ok := strings.CutPrefix(field, "uri="); ok {
1328-
sourcePath = strings.TrimSpace(raw)
1329-
continue
1330-
}
1331-
if raw, ok := strings.CutPrefix(field, "width="); ok {
1332-
dimensions.OriginalWidth = parseImageDimension(raw)
1333-
continue
1334-
}
1335-
if raw, ok := strings.CutPrefix(field, "height="); ok {
1336-
dimensions.OriginalHeight = parseImageDimension(raw)
1337-
continue
1338-
}
1339-
if raw, ok := strings.CutPrefix(field, "originalWidth="); ok {
1340-
dimensions.OriginalWidth = parseImageDimension(raw)
1341-
continue
1342-
}
1343-
if raw, ok := strings.CutPrefix(field, "original_width="); ok {
1254+
case "width", "originalwidth":
13441255
dimensions.OriginalWidth = parseImageDimension(raw)
1345-
continue
1346-
}
1347-
if raw, ok := strings.CutPrefix(field, "originalHeight="); ok {
1348-
dimensions.OriginalHeight = parseImageDimension(raw)
1349-
continue
1350-
}
1351-
if raw, ok := strings.CutPrefix(field, "original_height="); ok {
1256+
case "height", "originalheight":
13521257
dimensions.OriginalHeight = parseImageDimension(raw)
1353-
continue
1354-
}
1355-
if raw, ok := strings.CutPrefix(field, "displayWidth="); ok {
1356-
dimensions.DisplayWidth = parseImageDimension(raw)
1357-
continue
1358-
}
1359-
if raw, ok := strings.CutPrefix(field, "display_width="); ok {
1258+
case "displaywidth":
13601259
dimensions.DisplayWidth = parseImageDimension(raw)
1361-
continue
1362-
}
1363-
if raw, ok := strings.CutPrefix(field, "displayHeight="); ok {
1364-
dimensions.DisplayHeight = parseImageDimension(raw)
1365-
continue
1366-
}
1367-
if raw, ok := strings.CutPrefix(field, "display_height="); ok {
1260+
case "displayheight":
13681261
dimensions.DisplayHeight = parseImageDimension(raw)
13691262
}
13701263
}
@@ -1377,6 +1270,21 @@ func parseImageHint(seq string) (imageHint, bool) {
13771270
return imageHint{Display: display, Content: content, MediaType: mediaType, Filename: name, Dimensions: dimensionPtr, SourcePath: sourcePath}, true
13781271
}
13791272

1273+
func parseImageHintMetadataField(field string) (string, string, bool) {
1274+
name, value, ok := strings.Cut(field, "=")
1275+
if !ok {
1276+
return "", "", false
1277+
}
1278+
return normalizeImageHintMetadataKey(name), value, true
1279+
}
1280+
1281+
func normalizeImageHintMetadataKey(name string) string {
1282+
name = strings.ToLower(strings.TrimSpace(name))
1283+
name = strings.ReplaceAll(name, "_", "")
1284+
name = strings.ReplaceAll(name, "-", "")
1285+
return name
1286+
}
1287+
13801288
func splitImageHintPayload(payload string) (string, string) {
13811289
if index := strings.LastIndex(payload, ":"); index >= 0 {
13821290
return payload[:index], payload[index+1:]

‎internal/tui/tui_test.go‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,16 @@ func TestParseImageHintAcceptsMetadataAliases(t *testing.T) {
912912
}
913913
}
914914

915+
func TestParseImageHintNormalizesMetadataKeys(t *testing.T) {
916+
key := ParseKey("\x1b]1337;File=Name=case.png;MIME_TYPE=image/png;source-url=file:///tmp/case.png;original-width=200;Original_Height=100;display-width=50;Display-Height=25;inline=1:CCCC\a")
917+
if key.Type != KeyImageHint || key.Text != "[Image: case.png]" || key.Filename != "case.png" || key.MediaType != "image/png" || key.Content != "CCCC" || key.SourcePath != "file:///tmp/case.png" {
918+
t.Fatalf("key = %#v", key)
919+
}
920+
if key.Dimensions == nil || key.Dimensions.OriginalWidth != 200 || key.Dimensions.OriginalHeight != 100 || key.Dimensions.DisplayWidth != 50 || key.Dimensions.DisplayHeight != 25 {
921+
t.Fatalf("dimensions = %#v", key.Dimensions)
922+
}
923+
}
924+
915925
func TestParseAlternateTerminalNavigationSequences(t *testing.T) {
916926
cases := []struct {
917927
seq string

0 commit comments

Comments
 (0)