From 1feb3743f6a5b2ee70958ff5f48d49f4dff7e30f Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Fri, 14 Aug 2026 01:44:37 +0530 Subject: [PATCH] dedupe help center template partials and reuse the markdown link helper --- internal/stringutil/htmlchunker.go | 2 +- internal/stringutil/htmlembedprep.go | 16 +--- internal/stringutil/htmlembedprep_test.go | 87 +++++++++++++++++++ .../web-templates/help/classic/article.html | 17 +--- .../web-templates/help/classic/layout.html | 2 +- .../web-templates/help/docs/article.html | 17 +--- .../public/web-templates/help/docs/home.html | 2 +- .../web-templates/help/docs/layout.html | 4 +- .../web-templates/help/docs/notfound.html | 2 +- .../web-templates/help/docs/search.html | 2 +- .../web-templates/help/shared/partials.html | 23 +++++ 11 files changed, 122 insertions(+), 52 deletions(-) create mode 100644 internal/stringutil/htmlembedprep_test.go diff --git a/internal/stringutil/htmlchunker.go b/internal/stringutil/htmlchunker.go index 9efd76b6..178e83fc 100644 --- a/internal/stringutil/htmlchunker.go +++ b/internal/stringutil/htmlchunker.go @@ -11,7 +11,7 @@ import ( ) // Bump whenever ChunkHTMLContent changes the text it emits for unchanged input; it feeds reindex fingerprints. -const ChunkerVersion = 2 +const ChunkerVersion = 3 var ( sentenceRegex = regexp.MustCompile(`[.!?]+[\s]+`) diff --git a/internal/stringutil/htmlembedprep.go b/internal/stringutil/htmlembedprep.go index e96d2c5d..f39838bb 100644 --- a/internal/stringutil/htmlembedprep.go +++ b/internal/stringutil/htmlembedprep.go @@ -12,7 +12,7 @@ func prepareHTMLForEmbedding(htmlContent string) string { if err != nil { return htmlContent } - inlineLinkHrefs(doc) + inlineAnchorMarkdown(doc) flattenTables(doc) var b strings.Builder @@ -22,20 +22,6 @@ func prepareHTMLForEmbedding(htmlContent string) string { return b.String() } -// inlineLinkHrefs appends each link's destination after its text so the flattened text keeps the URL. -func inlineLinkHrefs(n *html.Node) { - if n.Type == html.ElementNode && n.Data == "a" { - href := strings.TrimSpace(attrValue(n, "href")) - text := strings.TrimSpace(nodeText(n)) - if href != "" && !strings.HasPrefix(href, "#") && !strings.HasPrefix(href, "cid:") && text != href { - n.AppendChild(&html.Node{Type: html.TextNode, Data: " (" + href + ")"}) - } - } - for c := n.FirstChild; c != nil; c = c.NextSibling { - inlineLinkHrefs(c) - } -} - // flattenTables turns each table into a
 of one header-labelled line per row, which also keeps it unsplittable.
 func flattenTables(n *html.Node) {
 	var tables []*html.Node
diff --git a/internal/stringutil/htmlembedprep_test.go b/internal/stringutil/htmlembedprep_test.go
new file mode 100644
index 00000000..5366eeb1
--- /dev/null
+++ b/internal/stringutil/htmlembedprep_test.go
@@ -0,0 +1,87 @@
+package stringutil
+
+import (
+	"strings"
+	"testing"
+)
+
+func TestPrepareHTMLForEmbedding(t *testing.T) {
+	tests := []struct {
+		name        string
+		in          string
+		contains    []string
+		notContains []string
+	}{
+		{
+			name:        "link becomes markdown",
+			in:          `

See the guide for steps.

`, + contains: []string{"See [the guide](https://example.com/guide) for steps."}, + notContains: []string{"https://example.com

`, + contains: []string{">https://example.com<"}, + notContains: []string{ + "[https://example.com](https://example.com)", + "Setup inline

`, + contains: []string{`Setup`, `inline`}, + notContains: []string{"[Setup]"}, + }, + { + name: "table flattens to header labelled lines", + in: ` + + + +
PlanPrice
Basic$5
Pro$10
`, + contains: []string{"
", "Plan: Basic | Price: $5", "Plan: Pro | Price: $10"},
+			notContains: []string{""},
+		},
+		{
+			name:        "header only table keeps the header line",
+			in:          `
PlanPrice
`, + contains: []string{"Plan | Price"}, + notContains: []string{""}, + }, + { + name: "caption leads the table text", + in: `
Pricing
Basic$5
`, + contains: []string{ + "Pricing\nBasic | $5", + }, + }, + { + name: "link inside a table cell survives as markdown", + in: `
Doc
Pay now
`, + contains: []string{"Doc: [Pay now](https://example.com/x)"}, + notContains: []string{"{{ if $showAuthor }}{{ .Data.Article.AuthorName }} · {{ end }}{{ $.L.T "globals.terms.lastUpdated" }}
- {{ if not .Data.HelpCenter.Theme.Article.HideToc }} - - {{ end }} + {{ template "hc-article-toc" . }}
{{ .Data.Content }}
-
- {{ $.L.T "helpCenter.wasHelpful" }} - - - -
+{{ template "hc-helpful" . }} {{ if and .Data.Related (not .Data.HelpCenter.Theme.Article.HideRelated) }}
- {{ if not .Data.HelpCenter.Theme.Article.HideToc }} - - {{ end }} + {{ template "hc-article-toc" . }}
{{ .Data.Content }}
-
- {{ .L.T "helpCenter.wasHelpful" }} - - - -
+ {{ template "hc-helpful" . }} {{ if and .Data.Related (not .Data.HelpCenter.Theme.Article.HideRelated) }}
- + {{ template "hc-search-icon" }} {{ .L.T "globals.terms.search" }} @@ -33,7 +33,7 @@
{{ end }} +{{ define "hc-search-icon" }}