feat(release): v0.3.0

commit 533ac4e58256e6520a86af964fcf4c2f9a98d4ba
Author: adminoo <git@kadath.corp>
Date:   Mon Feb 23 18:52:59 2026 +0100

    feat: freebsd release tarball generator

commit 874fb63fd0
Author: adminoo <git@kadath.corp>
Date:   Mon Feb 23 14:05:24 2026 +0100

    feat: bump changelog

commit 46ab7e2911
Author: adminoo <git@kadath.corp>
Date:   Mon Feb 23 13:58:14 2026 +0100

    feat: margin and page breaks

commit 44751a808a
Author: adminoo <git@kadath.corp>
Date:   Mon Feb 23 13:57:56 2026 +0100

    feat: picture are worth thousand words

commit a5683428e0
Author: adminoo <git@kadath.corp>
Date:   Mon Feb 23 13:39:00 2026 +0100

    feat: navigate individual sections

commit 0d9b7c4e7b
Author: adminoo <git@kadath.corp>
Date:   Mon Feb 23 13:38:19 2026 +0100

    feat: make use of vendoring
This commit is contained in:
2026-02-23 19:17:17 +01:00
parent b571588b15
commit 5fdcede6f8
11 changed files with 507 additions and 11 deletions

103
internal/note/sections.go Normal file
View File

@ -0,0 +1,103 @@
package note
import (
"strconv"
"strings"
"unicode"
)
type Section struct {
ID string
Heading string
Content string
}
// ParseH2Heading returns the heading text for a level-two markdown heading.
func ParseH2Heading(line string) (string, bool) {
trimmed := strings.TrimLeft(line, " \t")
if !strings.HasPrefix(trimmed, "## ") {
return "", false
}
return strings.TrimSpace(strings.TrimPrefix(trimmed, "## ")), true
}
// ParseSections splits markdown into level-two heading sections.
func ParseSections(content string) []Section {
if content == "" {
return nil
}
lines := strings.Split(content, "\n")
var sections []Section
var current *Section
var builder strings.Builder
counts := make(map[string]int)
flush := func() {
if current == nil {
return
}
current.Content = builder.String()
sections = append(sections, *current)
current = nil
builder.Reset()
}
for i, line := range lines {
heading, ok := ParseH2Heading(line)
if ok {
flush()
base := slugifyHeading(heading)
if base == "" {
base = "section"
}
counts[base]++
id := base
if counts[base] > 1 {
id = base + "-" + strconv.Itoa(counts[base])
}
current = &Section{
ID: id,
Heading: heading,
}
}
if current != nil {
builder.WriteString(line)
if i < len(lines)-1 {
builder.WriteString("\n")
}
}
}
flush()
return sections
}
func slugifyHeading(input string) string {
in := strings.TrimSpace(strings.ToLower(input))
if in == "" {
return ""
}
var b strings.Builder
prevDash := false
for _, r := range in {
if unicode.IsLetter(r) || unicode.IsDigit(r) {
b.WriteRune(r)
prevDash = false
continue
}
if !prevDash {
b.WriteByte('-')
prevDash = true
}
}
out := b.String()
out = strings.Trim(out, "-")
return out
}