Files
esengine bf2984ae8c refactor(termrender): the terminal visual layer is its own package
The theme, colour profile, Markdown/math renderer, tool cards, diff view,
assistant block and clipboard writes move out of internal/frontend/cli into
internal/frontend/termrender, so a terminal frontend other than cli can draw
the same bytes without importing cli. cli now calls termrender for every
style, width, box and Markdown helper; view_helpers.go stays in cli.

termrender exports the surface a frontend draws with: Bold/Dim/Green/Red/
Yellow/Accent/Reverse, VisibleWidth/PadRight/Boxed, ConfigureTheme,
ConfigureThemeFromConfig(ForTTYOutput), ActiveTheme/ThemeName and the
ThemeStyle/ThemeFg/ThemeLipColor/ApplyTextareaTheme helpers over an exported
Palette/Color, RenderMarkdown and MarkdownRenderer, AssistantBlock, ToolCard/
ToolDot/ToolHead/ToolArg/ToolDisplayName, DiffBlock/DiffStat, CopyToClipboard
with ClipboardCopyMsg, ANSIConsoleReady and SetColorProfile.

Transcript helpers nothing calls (edgeScrollDir, selSpan, scrollbarThumb)
are dropped with their tests, and the clipboard message loses the seq and
status-hint fields no caller set. The metadata-command test now asserts that
version/help never reach the TTY theme setup, through a cli-side seam, since
the terminal probe it used to swap is termrender's.

The repolint baseline diff is the moved files' prior essay debt carried to
their new paths (mathnode.go, md.go, width_test.go; transcript.go's is paid
off), plus orphan budget for the eight exports whose consumer, the new terminal
UI, is not in the tree yet (ToolCard, DiffBlock, AssistantBlock,
RenderMarkdown, CopyToClipboard, ApplyTextareaTheme, ThemeName) or which only
a cli test drives (SetColorProfile). Wiring the UI reclaims that budget.
2026-09-24 23:45:52 -07:00

69 lines
1.7 KiB
Go

package termrender
import (
"bytes"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/text"
)
var kindMath = ast.NewNodeKind("Math")
type mathNode struct {
ast.BaseInline
value string // Unicode rendering for terminal display
source string // original LaTeX source (e.g. "\alpha", "\frac{1}{2}")
display bool
}
func (n *mathNode) Kind() ast.NodeKind { return kindMath }
func (n *mathNode) Dump(src []byte, level int) { ast.DumpHelper(n, src, level, nil, nil) }
type mathParser struct{}
func (p *mathParser) Trigger() []byte { return []byte{'$'} }
func (p *mathParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
line, _ := block.PeekLine()
if len(line) == 0 || line[0] != '$' {
return nil
}
display := len(line) >= 2 && line[1] == '$'
delim := 1
if display {
delim = 2
}
rest := line[delim:]
var closeAt int
if display {
closeAt = bytes.Index(rest, []byte("$$"))
} else {
closeAt = bytes.IndexByte(rest, '$')
}
if closeAt < 0 {
return nil
}
inner := rest[:closeAt]
if len(bytes.TrimSpace(inner)) == 0 {
return nil
}
// Currency guard (markdown-it-texmath rule): a single-$ span only counts as
// math when the open isn't followed by space, the close isn't preceded by
// space, and the char after the close isn't a digit — so "$5 and $10" stays
// prose. Display $$ is unambiguous and skips the check.
if !display {
after := closeAt + 1
if inner[0] == ' ' || inner[len(inner)-1] == ' ' ||
(after < len(rest) && rest[after] >= '0' && rest[after] <= '9') {
return nil
}
}
block.Advance(delim + closeAt + delim)
src := string(bytes.TrimSpace(inner))
return &mathNode{value: latexToUnicode(src), source: src, display: display}
}