Skip to content

Commit b839274

Browse files
author
Ivan
committed
fix(escape): wrap PUA/Nerd Font icon glyphs with Roboto Mono Nerd Font
1 parent 1cc5be7 commit b839274

3 files changed

Lines changed: 58 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
Dates use YYYY-MM-DD.
44

5+
## [1.1.1] - 2026-09-06
6+
7+
### Fixed
8+
9+
- PUA/Nerd Font icon glyphs are now wrapped in a span with `font-family:'Roboto Mono Nerd Font',monospace` so they render correctly in browser WASM builds instead of falling back to a font that lacks the glyph.
10+
511
## [1.1.0] - 2026-09-05
612

713
### Added

micron/escape.go

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package micron
66
import (
77
"html"
88
"strings"
9+
"unicode/utf8"
910
)
1011

1112
// stripASCIIControls removes ASCII control characters (U+0000-U+001F).
@@ -33,32 +34,62 @@ func htmlText(s string) string {
3334
return html.EscapeString(stripASCIIControls(s))
3435
}
3536

37+
// isNerdIconRune returns true for runes in the Private Use Areas that
38+
// Nerd Font / patched icon fonts occupy. These glyphs need a font that
39+
// includes them, otherwise they render as replacement boxes.
40+
func isNerdIconRune(r rune) bool {
41+
return (r >= 0xE000 && r <= 0xF8FF) ||
42+
(r >= 0xF0000 && r <= 0xFFFFD) ||
43+
(r >= 0x100000 && r <= 0x10FFFD)
44+
}
45+
3646
func appendHTMLText(b *strings.Builder, s string) {
3747
s = stripASCIIControls(s)
48+
var icon bool
3849
start := 0
39-
for i := range len(s) {
40-
var esc string
41-
switch s[i] {
42-
case '&':
43-
esc = "&amp;"
44-
case '<':
45-
esc = "&lt;"
46-
case '>':
47-
esc = "&gt;"
48-
case '"':
49-
esc = "&#34;"
50-
case '\'':
51-
esc = "&#39;"
52-
default:
53-
continue
50+
for i := 0; i < len(s); {
51+
r, size := utf8.DecodeRuneInString(s[i:])
52+
nextIcon := isNerdIconRune(r)
53+
if nextIcon != icon {
54+
if start < i {
55+
b.WriteString(s[start:i])
56+
}
57+
if nextIcon {
58+
b.WriteString(`<span class="nf" style="font-family:'Roboto Mono Nerd Font',monospace">`)
59+
} else {
60+
b.WriteString(`</span>`)
61+
}
62+
icon = nextIcon
63+
start = i
5464
}
55-
if start < i {
56-
b.WriteString(s[start:i])
65+
if !nextIcon && size == 1 {
66+
var esc string
67+
switch s[i] {
68+
case '&':
69+
esc = "&amp;"
70+
case '<':
71+
esc = "&lt;"
72+
case '>':
73+
esc = "&gt;"
74+
case '"':
75+
esc = "&#34;"
76+
case '\'':
77+
esc = "&#39;"
78+
}
79+
if esc != "" {
80+
if start < i {
81+
b.WriteString(s[start:i])
82+
}
83+
b.WriteString(esc)
84+
start = i + 1
85+
}
5786
}
58-
b.WriteString(esc)
59-
start = i + 1
87+
i += size
6088
}
6189
if start < len(s) {
6290
b.WriteString(s[start:])
6391
}
92+
if icon {
93+
b.WriteString(`</span>`)
94+
}
6495
}

micron/html_build.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func (p *Parser) writeLink(b *strings.Builder, lk *Link, s *State) {
160160
b.WriteString(`"`)
161161
appendQuotedHTMLStyleAttr(b, lk.Style, s.DefaultBG)
162162
b.WriteString(`>`)
163-
b.WriteString(lk.Label)
163+
appendHTMLText(b, lk.Label)
164164
b.WriteString(`</a>`)
165165
return
166166
}
@@ -207,7 +207,7 @@ func (p *Parser) writeLink(b *strings.Builder, lk *Link, s *State) {
207207
b.WriteString(`"`)
208208
appendQuotedHTMLStyleAttr(b, lk.Style, s.DefaultBG)
209209
b.WriteString(`>`)
210-
b.WriteString(lk.Label)
210+
appendHTMLText(b, lk.Label)
211211
b.WriteString(`</a>`)
212212
}
213213

0 commit comments

Comments
 (0)