package doccontent import ( "crypto/sha256" "encoding/hex" "strings" "golang.org/x/text/unicode/norm" ) // CanonicalizeMarkdown applies LF, UTF-8 NFC, and a single trailing newline. // It must not trim trailing spaces or tabs: two trailing spaces are Markdown hard breaks. func CanonicalizeMarkdown(raw string) string { s := strings.ReplaceAll(raw, "\r\n", "\n") s = strings.ReplaceAll(s, "\r", "\n") s = norm.NFC.String(s) if s != "" && !strings.HasSuffix(s, "\n") { s += "\n" } return s } // LegacyDestructiveCanonicalizeMarkdown reproduces the pre-fix canonicalizer that // stripped per-line trailing spaces/tabs. Used only to classify historical drift. func LegacyDestructiveCanonicalizeMarkdown(raw string) string { s := strings.ReplaceAll(raw, "\r\n", "\n") s = strings.ReplaceAll(s, "\r", "\n") s = norm.NFC.String(s) lines := strings.Split(s, "\n") for i, line := range lines { lines[i] = strings.TrimRightFunc(line, func(r rune) bool { return r == ' ' || r == '\t' }) } s = strings.Join(lines, "\n") if s != "" && !strings.HasSuffix(s, "\n") { s += "\n" } return s } func SHA256Hex(content string) string { sum := sha256.Sum256([]byte(content)) return hex.EncodeToString(sum[:]) }