Implemented Markdown escaping for render output
All checks were successful
ci/woodpecker/tag/release Pipeline was successful

This commit is contained in:
2026-05-24 18:13:05 -05:00
parent 0dfd06c349
commit 0fc92f3643
2 changed files with 77 additions and 6 deletions

View File

@@ -17,13 +17,13 @@ func (MarkdownRenderer) Render(transcript Transcript, opts Options) (string, err
if title == "" {
title = "Transcript"
}
lines = append(lines, "# "+title, "")
lines = append(lines, "# "+escapeMarkdownInline(title), "")
if opts.IncludeMetadata {
lines = append(lines,
fmt.Sprintf("- Application: %s", transcript.Metadata.Application),
fmt.Sprintf("- Version: %s", transcript.Metadata.Version),
fmt.Sprintf("- Output schema: %s", transcript.Schema),
fmt.Sprintf("- Application: %s", escapeMarkdownInline(transcript.Metadata.Application)),
fmt.Sprintf("- Version: %s", escapeMarkdownInline(transcript.Metadata.Version)),
fmt.Sprintf("- Output schema: %s", escapeMarkdownInline(transcript.Schema)),
"",
)
}
@@ -37,11 +37,11 @@ func (MarkdownRenderer) Render(transcript Transcript, opts Options) (string, err
parts = append(parts, fmt.Sprintf("[#%d]", segment.ID))
}
text := segment.Text
text := escapeMarkdownInline(segment.Text)
if shouldItalicize(segment.Categories) {
text = "*" + text + "*"
}
parts = append(parts, fmt.Sprintf("**%s:** %s", segment.Speaker, text))
parts = append(parts, fmt.Sprintf("**%s:** %s", escapeMarkdownInline(segment.Speaker), text))
lines = append(lines, strings.Join(parts, " "))
lines = append(lines, "")
}
@@ -53,6 +53,28 @@ func (MarkdownRenderer) Render(transcript Transcript, opts Options) (string, err
return output, nil
}
func escapeMarkdownInline(value string) string {
replacer := strings.NewReplacer(
`\`, `\\`,
"`", "\\`",
"*", "\\*",
"_", "\\_",
"{", "\\{",
"}", "\\}",
"[", "\\[",
"]", "\\]",
"(", "\\(",
")", "\\)",
"#", "\\#",
"+", "\\+",
"!", "\\!",
"|", "\\|",
"<", "\\<",
">", "\\>",
)
return replacer.Replace(value)
}
func shouldItalicize(categories []string) bool {
for _, category := range categories {
switch category {

View File

@@ -108,6 +108,48 @@ func TestMarkdownRendererMetadataOnlyWhenRequested(t *testing.T) {
}
}
func TestMarkdownRendererEscapesUserProvidedMarkdown(t *testing.T) {
transcript := Transcript{
Schema: "seriatim-intermediate",
Metadata: Metadata{
Application: "seriatim *cli*",
Version: "v[test]",
},
Segments: []Segment{
{
ID: 1,
Start: 1,
End: 2,
Speaker: "Dr. *A_[1]",
Text: "Use *literal* [link](target) and `code` \\ slash!",
},
{
ID: 2,
Start: 2,
End: 3,
Speaker: "Narrator",
Text: "_aside_ with | pipe",
Categories: []string{"background"},
},
},
}
output, err := MarkdownRenderer{}.Render(transcript, Options{
Title: "# Planning [notes]",
IncludeTimestamps: false,
IncludeMetadata: true,
})
if err != nil {
t.Fatalf("render markdown: %v", err)
}
assertContains(t, output, "# \\# Planning \\[notes\\]")
assertContains(t, output, "- Application: seriatim \\*cli\\*")
assertContains(t, output, "- Version: v\\[test\\]")
assertContains(t, output, "**Dr. \\*A\\_\\[1\\]:** Use \\*literal\\* \\[link\\]\\(target\\) and \\`code\\` \\\\ slash\\!")
assertContains(t, output, "**Narrator:** *\\_aside\\_ with \\| pipe*")
}
func TestMarkdownRendererCategoryHintItalicsAndUnknownCategories(t *testing.T) {
transcript := Transcript{
Segments: []Segment{
@@ -176,3 +218,10 @@ func TestMarkdownRendererIsDeterministic(t *testing.T) {
t.Fatalf("expected HH:MM:SS timestamp formatting:\n%s", first)
}
}
func assertContains(t *testing.T, value string, want string) {
t.Helper()
if !strings.Contains(value, want) {
t.Fatalf("expected output to contain %q:\n%s", want, value)
}
}