chore: jotts-go tui improvements
d0c90703
3 file(s) · +84 −15
| 4 | 4 | "fmt" |
|
| 5 | 5 | ||
| 6 | 6 | "github.com/charmbracelet/glamour" |
|
| 7 | + | "github.com/charmbracelet/glamour/ansi" |
|
| 7 | 8 | ) |
|
| 8 | 9 | ||
| 9 | 10 | type mdRenderer struct { |
|
| 12 | 13 | cache map[string]string |
|
| 13 | 14 | } |
|
| 14 | 15 | ||
| 16 | + | func sp(s string) *string { return &s } |
|
| 17 | + | func bp(b bool) *bool { return &b } |
|
| 18 | + | func up(u uint) *uint { return &u } |
|
| 19 | + | ||
| 20 | + | // ansiStyle uses only base ANSI palette indexes (0-7 for normal, 8-15 bright) |
|
| 21 | + | // so colors follow the terminal theme instead of hardcoded hex/256 values. |
|
| 22 | + | func ansiStyle() ansi.StyleConfig { |
|
| 23 | + | return ansi.StyleConfig{ |
|
| 24 | + | Document: ansi.StyleBlock{ |
|
| 25 | + | StylePrimitive: ansi.StylePrimitive{BlockPrefix: "\n", BlockSuffix: "\n"}, |
|
| 26 | + | Margin: up(2), |
|
| 27 | + | }, |
|
| 28 | + | BlockQuote: ansi.StyleBlock{ |
|
| 29 | + | Indent: up(1), |
|
| 30 | + | IndentToken: sp("│ "), |
|
| 31 | + | }, |
|
| 32 | + | Paragraph: ansi.StyleBlock{}, |
|
| 33 | + | List: ansi.StyleList{ |
|
| 34 | + | LevelIndent: 2, |
|
| 35 | + | }, |
|
| 36 | + | Heading: ansi.StyleBlock{ |
|
| 37 | + | StylePrimitive: ansi.StylePrimitive{BlockSuffix: "\n", Color: sp("4"), Bold: bp(true)}, |
|
| 38 | + | }, |
|
| 39 | + | H1: ansi.StyleBlock{StylePrimitive: ansi.StylePrimitive{Prefix: "# ", Color: sp("4"), Bold: bp(true)}}, |
|
| 40 | + | H2: ansi.StyleBlock{StylePrimitive: ansi.StylePrimitive{Prefix: "## ", Color: sp("4"), Bold: bp(true)}}, |
|
| 41 | + | H3: ansi.StyleBlock{StylePrimitive: ansi.StylePrimitive{Prefix: "### ", Color: sp("6"), Bold: bp(true)}}, |
|
| 42 | + | H4: ansi.StyleBlock{StylePrimitive: ansi.StylePrimitive{Prefix: "#### ", Color: sp("6")}}, |
|
| 43 | + | H5: ansi.StyleBlock{StylePrimitive: ansi.StylePrimitive{Prefix: "##### ", Color: sp("6")}}, |
|
| 44 | + | H6: ansi.StyleBlock{StylePrimitive: ansi.StylePrimitive{Prefix: "###### ", Color: sp("6")}}, |
|
| 45 | + | Strikethrough: ansi.StylePrimitive{CrossedOut: bp(true)}, |
|
| 46 | + | Emph: ansi.StylePrimitive{Italic: bp(true)}, |
|
| 47 | + | Strong: ansi.StylePrimitive{Bold: bp(true)}, |
|
| 48 | + | HorizontalRule: ansi.StylePrimitive{ |
|
| 49 | + | Color: sp("8"), |
|
| 50 | + | Format: "\n--------\n", |
|
| 51 | + | }, |
|
| 52 | + | Item: ansi.StylePrimitive{BlockPrefix: "• "}, |
|
| 53 | + | Enumeration: ansi.StylePrimitive{BlockPrefix: ". "}, |
|
| 54 | + | Task: ansi.StyleTask{ |
|
| 55 | + | Ticked: "[✓] ", |
|
| 56 | + | Unticked: "[ ] ", |
|
| 57 | + | }, |
|
| 58 | + | Link: ansi.StylePrimitive{Color: sp("6"), Underline: bp(true)}, |
|
| 59 | + | LinkText: ansi.StylePrimitive{Color: sp("2"), Bold: bp(true)}, |
|
| 60 | + | Image: ansi.StylePrimitive{Color: sp("5"), Underline: bp(true)}, |
|
| 61 | + | ImageText: ansi.StylePrimitive{ |
|
| 62 | + | Color: sp("8"), |
|
| 63 | + | Format: "Image: {{.text}} →", |
|
| 64 | + | }, |
|
| 65 | + | Code: ansi.StyleBlock{ |
|
| 66 | + | StylePrimitive: ansi.StylePrimitive{Color: sp("1"), Prefix: "`", Suffix: "`"}, |
|
| 67 | + | }, |
|
| 68 | + | CodeBlock: ansi.StyleCodeBlock{ |
|
| 69 | + | StyleBlock: ansi.StyleBlock{ |
|
| 70 | + | StylePrimitive: ansi.StylePrimitive{Color: sp("7")}, |
|
| 71 | + | Margin: up(2), |
|
| 72 | + | }, |
|
| 73 | + | }, |
|
| 74 | + | Table: ansi.StyleTable{ |
|
| 75 | + | CenterSeparator: sp("┼"), |
|
| 76 | + | ColumnSeparator: sp("│"), |
|
| 77 | + | RowSeparator: sp("─"), |
|
| 78 | + | }, |
|
| 79 | + | DefinitionDescription: ansi.StylePrimitive{BlockPrefix: "\n* "}, |
|
| 80 | + | } |
|
| 81 | + | } |
|
| 82 | + | ||
| 15 | 83 | func newRenderer(width int) *mdRenderer { |
|
| 16 | 84 | if width < 20 { |
|
| 17 | 85 | width = 80 |
|
| 18 | 86 | } |
|
| 87 | + | style := ansiStyle() |
|
| 19 | 88 | r, _ := glamour.NewTermRenderer( |
|
| 20 | - | glamour.WithAutoStyle(), |
|
| 89 | + | glamour.WithStyles(style), |
|
| 21 | 90 | glamour.WithWordWrap(width-2), |
|
| 22 | 91 | ) |
|
| 23 | 92 | return &mdRenderer{r: r, width: width, cache: map[string]string{}} |
|
| 27 | 96 | if width == m.width || width < 20 { |
|
| 28 | 97 | return |
|
| 29 | 98 | } |
|
| 99 | + | style := ansiStyle() |
|
| 30 | 100 | r, _ := glamour.NewTermRenderer( |
|
| 31 | - | glamour.WithAutoStyle(), |
|
| 101 | + | glamour.WithStyles(style), |
|
| 32 | 102 | glamour.WithWordWrap(width-2), |
|
| 33 | 103 | ) |
|
| 34 | 104 | m.r = r |
|
| 125 | 125 | m.contentVP.Width = maxInt(contentInnerW, 1) |
|
| 126 | 126 | m.contentVP.Height = maxInt(contentInnerH-1, 1) |
|
| 127 | 127 | ||
| 128 | - | m.titleInput.Width = maxInt(contentInnerW-2, 1) |
|
| 129 | - | m.contentArea.SetWidth(maxInt(contentInnerW, 1)) |
|
| 130 | - | m.contentArea.SetHeight(maxInt(contentInnerH-4, 1)) |
|
| 128 | + | m.titleInput.Width = maxInt(contentInnerW-4, 1) |
|
| 129 | + | m.contentArea.SetWidth(maxInt(contentInnerW-2, 1)) |
|
| 130 | + | m.contentArea.SetHeight(maxInt(contentInnerH-6, 1)) |
|
| 131 | 131 | ||
| 132 | 132 | listOuterW, _ := splitWidths(m.width) |
|
| 133 | 133 | listInnerW := maxInt(listOuterW-paneFrameWidth(), 1) |
| 10 | 10 | var ( |
|
| 11 | 11 | borderStyle = lipgloss.NewStyle(). |
|
| 12 | 12 | Border(lipgloss.NormalBorder()). |
|
| 13 | - | BorderForeground(lipgloss.Color("240")) |
|
| 13 | + | BorderForeground(lipgloss.Color("8")) |
|
| 14 | 14 | borderActive = lipgloss.NewStyle(). |
|
| 15 | 15 | Border(lipgloss.NormalBorder()). |
|
| 16 | - | BorderForeground(lipgloss.Color("214")) |
|
| 16 | + | BorderForeground(lipgloss.Color("3")) |
|
| 17 | 17 | titleStyle = lipgloss.NewStyle(). |
|
| 18 | 18 | Bold(true). |
|
| 19 | - | Foreground(lipgloss.Color("214")). |
|
| 19 | + | Foreground(lipgloss.Color("3")). |
|
| 20 | 20 | Padding(0, 1) |
|
| 21 | 21 | itemStyle = lipgloss.NewStyle().Padding(0, 1) |
|
| 22 | 22 | itemSelected = lipgloss.NewStyle(). |
|
| 23 | 23 | Padding(0, 1). |
|
| 24 | 24 | Bold(true). |
|
| 25 | - | Foreground(lipgloss.Color("214")) |
|
| 25 | + | Foreground(lipgloss.Color("3")) |
|
| 26 | 26 | statusOK = lipgloss.NewStyle(). |
|
| 27 | - | Foreground(lipgloss.Color("82")). |
|
| 27 | + | Foreground(lipgloss.Color("2")). |
|
| 28 | 28 | Bold(true) |
|
| 29 | 29 | statusErr = lipgloss.NewStyle(). |
|
| 30 | - | Foreground(lipgloss.Color("196")). |
|
| 30 | + | Foreground(lipgloss.Color("1")). |
|
| 31 | 31 | Bold(true) |
|
| 32 | 32 | hintStyle = lipgloss.NewStyle(). |
|
| 33 | - | Foreground(lipgloss.Color("244")) |
|
| 33 | + | Foreground(lipgloss.Color("8")) |
|
| 34 | 34 | modalStyle = lipgloss.NewStyle(). |
|
| 35 | 35 | Border(lipgloss.RoundedBorder()). |
|
| 36 | - | BorderForeground(lipgloss.Color("214")). |
|
| 37 | - | Padding(1, 2). |
|
| 38 | - | Background(lipgloss.Color("236")) |
|
| 36 | + | BorderForeground(lipgloss.Color("3")). |
|
| 37 | + | Padding(1, 2) |
|
| 39 | 38 | ) |
|
| 40 | 39 | ||
| 41 | 40 | func (m Model) View() string { |