143 lines
2.8 KiB
Go
143 lines
2.8 KiB
Go
package render
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Builder accumulates markdown output.
|
|
type Builder struct {
|
|
sb strings.Builder
|
|
}
|
|
|
|
// New creates a new markdown builder.
|
|
func New() *Builder {
|
|
return &Builder{}
|
|
}
|
|
|
|
// H1 writes a level-1 heading.
|
|
func (b *Builder) H1(text string) {
|
|
fmt.Fprintf(&b.sb, "# %s\n\n", text)
|
|
}
|
|
|
|
// H2 writes a level-2 heading.
|
|
func (b *Builder) H2(text string) {
|
|
fmt.Fprintf(&b.sb, "## %s\n\n", text)
|
|
}
|
|
|
|
// H3 writes a level-3 heading.
|
|
func (b *Builder) H3(text string) {
|
|
fmt.Fprintf(&b.sb, "### %s\n\n", text)
|
|
}
|
|
|
|
// P writes a paragraph.
|
|
func (b *Builder) P(text string) {
|
|
fmt.Fprintf(&b.sb, "%s\n\n", text)
|
|
}
|
|
|
|
// Bold writes bold text inline (no newline).
|
|
func (b *Builder) Bold(text string) {
|
|
fmt.Fprintf(&b.sb, "**%s**", text)
|
|
}
|
|
|
|
// KV writes a key: value line.
|
|
func (b *Builder) KV(key, value string) {
|
|
if value == "" {
|
|
return
|
|
}
|
|
fmt.Fprintf(&b.sb, "- **%s:** %s\n", key, value)
|
|
}
|
|
|
|
// Bullet writes a bullet point.
|
|
func (b *Builder) Bullet(text string) {
|
|
fmt.Fprintf(&b.sb, "- %s\n", text)
|
|
}
|
|
|
|
// NumberedItem writes a numbered list item.
|
|
func (b *Builder) NumberedItem(n int, text string) {
|
|
fmt.Fprintf(&b.sb, "%d. %s\n", n, text)
|
|
}
|
|
|
|
// Table writes a markdown table.
|
|
func (b *Builder) Table(headers []string, rows [][]string) {
|
|
if len(headers) == 0 {
|
|
return
|
|
}
|
|
|
|
// Header row
|
|
fmt.Fprintf(&b.sb, "| %s |\n", strings.Join(headers, " | "))
|
|
|
|
// Separator
|
|
seps := make([]string, len(headers))
|
|
for i := range seps {
|
|
seps[i] = "---"
|
|
}
|
|
fmt.Fprintf(&b.sb, "| %s |\n", strings.Join(seps, " | "))
|
|
|
|
// Data rows
|
|
for _, row := range rows {
|
|
// Pad row to match header length
|
|
for len(row) < len(headers) {
|
|
row = append(row, "")
|
|
}
|
|
fmt.Fprintf(&b.sb, "| %s |\n", strings.Join(row[:len(headers)], " | "))
|
|
}
|
|
b.sb.WriteString("\n")
|
|
}
|
|
|
|
// Line writes a raw line.
|
|
func (b *Builder) Line(text string) {
|
|
fmt.Fprintf(&b.sb, "%s\n", text)
|
|
}
|
|
|
|
// Newline writes an empty line.
|
|
func (b *Builder) Newline() {
|
|
b.sb.WriteString("\n")
|
|
}
|
|
|
|
// HR writes a horizontal rule.
|
|
func (b *Builder) HR() {
|
|
b.sb.WriteString("---\n\n")
|
|
}
|
|
|
|
// String returns the accumulated markdown.
|
|
func (b *Builder) String() string {
|
|
return b.sb.String()
|
|
}
|
|
|
|
// FormatGP formats a coin amount with commas (e.g., 1,234,567 gp).
|
|
func FormatGP(amount int) string {
|
|
if amount < 0 {
|
|
return fmt.Sprintf("-%s gp", addCommas(-amount))
|
|
}
|
|
return fmt.Sprintf("%s gp", addCommas(amount))
|
|
}
|
|
|
|
// FormatNumber formats a number with commas.
|
|
func FormatNumber(n int) string {
|
|
return addCommas(n)
|
|
}
|
|
|
|
func addCommas(n int) string {
|
|
s := fmt.Sprintf("%d", n)
|
|
if len(s) <= 3 {
|
|
return s
|
|
}
|
|
|
|
var result strings.Builder
|
|
remainder := len(s) % 3
|
|
if remainder > 0 {
|
|
result.WriteString(s[:remainder])
|
|
if len(s) > remainder {
|
|
result.WriteString(",")
|
|
}
|
|
}
|
|
for i := remainder; i < len(s); i += 3 {
|
|
if i > remainder {
|
|
result.WriteString(",")
|
|
}
|
|
result.WriteString(s[i : i+3])
|
|
}
|
|
return result.String()
|
|
}
|