From 8907d98bb22e3f0d632aa0e3cc83136407450257 Mon Sep 17 00:00:00 2001 From: jay23606 <4602056+jay23606@users.noreply.github.com> Date: Sat, 10 Oct 2026 18:08:51 -0400 Subject: [PATCH] Fix image links with optional titles --- main.go | 7 +++--- main_test.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 main_test.go diff --git a/main.go b/main.go index 7250eb7..9bfb2ec 100644 --- a/main.go +++ b/main.go @@ -230,8 +230,8 @@ func isSnap() bool { // processMarkdownImages processes markdown source and converts relative image paths to data URIs func processMarkdownImages(markdown string, baseDir string) string { - // Process markdown image syntax: ![alt](path) - imgMarkdownRegex := regexp.MustCompile(`!\[([^\]]*)\]\(([^)]+)\)`) + // Process markdown image syntax: ![alt](path "optional title") + imgMarkdownRegex := regexp.MustCompile(`!\[([^\]]*)\]\(([^)\s]+)(\s+(?:"[^"]*"|'[^']*'|\([^)]*\)))?\)`) markdown = imgMarkdownRegex.ReplaceAllStringFunc(markdown, func(match string) string { parts := imgMarkdownRegex.FindStringSubmatch(match) if len(parts) < 3 { @@ -239,10 +239,11 @@ func processMarkdownImages(markdown string, baseDir string) string { } alt := parts[1] imgPath := parts[2] + title := parts[3] if isRelativePath(imgPath) { if dataURI := imageToDataURI(imgPath, baseDir); dataURI != "" { - return fmt.Sprintf("![%s](%s)", alt, dataURI) + return fmt.Sprintf("![%s](%s%s)", alt, dataURI, title) } } return match diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..a104021 --- /dev/null +++ b/main_test.go @@ -0,0 +1,65 @@ +package main + +import ( + "bytes" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/yuin/goldmark" +) + +func TestProcessMarkdownImages(t *testing.T) { + baseDir := t.TempDir() + imagePath := filepath.Join(baseDir, "images", "diagram.png") + if err := os.MkdirAll(filepath.Dir(imagePath), 0755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(imagePath, []byte("image data"), 0644); err != nil { + t.Fatal(err) + } + + tests := []struct { + name string + markdown string + wantTitleHTML string + }{ + { + name: "double-quoted title", + markdown: `![Architecture diagram](images/diagram.png "Architecture diagram")`, + wantTitleHTML: `title="Architecture diagram"`, + }, + { + name: "single-quoted title", + markdown: `![Architecture diagram](images/diagram.png 'Architecture diagram')`, + wantTitleHTML: `title="Architecture diagram"`, + }, + { + name: "parenthesized title", + markdown: `![Architecture diagram](images/diagram.png (Architecture diagram))`, + wantTitleHTML: `title="Architecture diagram"`, + }, + { + name: "without title", + markdown: `![Architecture diagram](images/diagram.png)`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + processed := processMarkdownImages(tt.markdown, baseDir) + if !strings.Contains(processed, `data:image/png;base64,aW1hZ2UgZGF0YQ==`) { + t.Fatalf("image was not embedded: %s", processed) + } + + var rendered bytes.Buffer + if err := goldmark.New().Convert([]byte(processed), &rendered); err != nil { + t.Fatal(err) + } + if tt.wantTitleHTML != "" && !strings.Contains(rendered.String(), tt.wantTitleHTML) { + t.Fatalf("rendered image lost its title attribute: %s", rendered.String()) + } + }) + } +}