diff options
| author | adamdottv <[email protected]> | 2025-06-12 05:35:40 -0500 |
|---|---|---|
| committer | adamdottv <[email protected]> | 2025-06-12 16:00:24 -0500 |
| commit | cce2e4ad754479917fc8f6f24c1421cf19c04573 (patch) | |
| tree | 62c0ec32358932a468af73b44fd28e7de7859bbe /packages/tui/internal/styles | |
| parent | a1ce35c208bf9ebca37f722e845035bd7fd5e801 (diff) | |
| download | opencode-cce2e4ad754479917fc8f6f24c1421cf19c04573.tar.gz opencode-cce2e4ad754479917fc8f6f24c1421cf19c04573.zip | |
wip: refactoring tui
Diffstat (limited to 'packages/tui/internal/styles')
| -rw-r--r-- | packages/tui/internal/styles/background.go | 124 | ||||
| -rw-r--r-- | packages/tui/internal/styles/markdown.go | 175 | ||||
| -rw-r--r-- | packages/tui/internal/styles/styles.go | 33 |
3 files changed, 130 insertions, 202 deletions
diff --git a/packages/tui/internal/styles/background.go b/packages/tui/internal/styles/background.go index 2fbb34efb..6db130c0e 100644 --- a/packages/tui/internal/styles/background.go +++ b/packages/tui/internal/styles/background.go @@ -1,123 +1,13 @@ package styles -import ( - "fmt" - "regexp" - "strings" - - "github.com/charmbracelet/lipgloss" -) - -var ansiEscape = regexp.MustCompile("\x1b\\[[0-9;]*m") - -func getColorRGB(c lipgloss.TerminalColor) (uint8, uint8, uint8) { - r, g, b, a := c.RGBA() - - // Un-premultiply alpha if needed - if a > 0 && a < 0xffff { - r = (r * 0xffff) / a - g = (g * 0xffff) / a - b = (b * 0xffff) / a - } - - // Convert from 16-bit to 8-bit color - return uint8(r >> 8), uint8(g >> 8), uint8(b >> 8) +type TerminalInfo struct { + BackgroundIsDark bool } -// ForceReplaceBackgroundWithLipgloss replaces any ANSI background color codes -// in `input` with a single 24‑bit background (48;2;R;G;B). -func ForceReplaceBackgroundWithLipgloss(input string, newBgColor lipgloss.TerminalColor) string { - // Precompute our new-bg sequence once - r, g, b := getColorRGB(newBgColor) - newBg := fmt.Sprintf("48;2;%d;%d;%d", r, g, b) - - return ansiEscape.ReplaceAllStringFunc(input, func(seq string) string { - const ( - escPrefixLen = 2 // "\x1b[" - escSuffixLen = 1 // "m" - ) - - raw := seq - start := escPrefixLen - end := len(raw) - escSuffixLen +var Terminal *TerminalInfo - var sb strings.Builder - // reserve enough space: original content minus bg codes + our newBg - sb.Grow((end - start) + len(newBg) + 2) - - // scan from start..end, token by token - for i := start; i < end; { - // find the next ';' or end - j := i - for j < end && raw[j] != ';' { - j++ - } - token := raw[i:j] - - // fast‑path: skip "48;5;N" or "48;2;R;G;B" - if len(token) == 2 && token[0] == '4' && token[1] == '8' { - k := j + 1 - if k < end { - // find next token - l := k - for l < end && raw[l] != ';' { - l++ - } - next := raw[k:l] - if next == "5" { - // skip "48;5;N" - m := l + 1 - for m < end && raw[m] != ';' { - m++ - } - i = m + 1 - continue - } else if next == "2" { - // skip "48;2;R;G;B" - m := l + 1 - for count := 0; count < 3 && m < end; count++ { - for m < end && raw[m] != ';' { - m++ - } - m++ - } - i = m - continue - } - } - } - - // decide whether to keep this token - // manually parse ASCII digits to int - isNum := true - val := 0 - for p := i; p < j; p++ { - c := raw[p] - if c < '0' || c > '9' { - isNum = false - break - } - val = val*10 + int(c-'0') - } - keep := !isNum || - ((val < 40 || val > 47) && (val < 100 || val > 107) && val != 49) - - if keep { - if sb.Len() > 0 { - sb.WriteByte(';') - } - sb.WriteString(token) - } - // advance past this token (and the semicolon) - i = j + 1 - } - - // append our new background - if sb.Len() > 0 { - sb.WriteByte(';') - } - sb.WriteString(newBg) - - return "\x1b[" + sb.String() + "m" - }) +func init() { + Terminal = &TerminalInfo{ + BackgroundIsDark: false, + } } diff --git a/packages/tui/internal/styles/markdown.go b/packages/tui/internal/styles/markdown.go index bacd063cc..a8681e191 100644 --- a/packages/tui/internal/styles/markdown.go +++ b/packages/tui/internal/styles/markdown.go @@ -3,7 +3,8 @@ package styles import ( "github.com/charmbracelet/glamour" "github.com/charmbracelet/glamour/ansi" - "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/lipgloss/v2/compat" + "github.com/lucasb-eyer/go-colorful" "github.com/sst/opencode/internal/theme" ) @@ -15,117 +16,120 @@ func stringPtr(s string) *string { return &s } func uintPtr(u uint) *uint { return &u } // returns a glamour TermRenderer configured with the current theme -func GetMarkdownRenderer(width int) *glamour.TermRenderer { +func GetMarkdownRenderer(width int, backgroundColor compat.AdaptiveColor) *glamour.TermRenderer { r, _ := glamour.NewTermRenderer( - glamour.WithStyles(generateMarkdownStyleConfig()), + glamour.WithStyles(generateMarkdownStyleConfig(backgroundColor)), glamour.WithWordWrap(width), + glamour.WithChromaFormatter("terminal16m"), ) return r } // creates an ansi.StyleConfig for markdown rendering // using adaptive colors from the provided theme. -func generateMarkdownStyleConfig() ansi.StyleConfig { +func generateMarkdownStyleConfig(backgroundColor compat.AdaptiveColor) ansi.StyleConfig { t := theme.CurrentTheme() + background := stringPtr(AdaptiveColorToString(backgroundColor)) return ansi.StyleConfig{ Document: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{ - BlockPrefix: "", - BlockSuffix: "", - Color: stringPtr(adaptiveColorToString(t.MarkdownText())), + BlockPrefix: "", + BlockSuffix: "", + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.MarkdownText())), }, }, BlockQuote: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownBlockQuote())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownBlockQuote())), Italic: boolPtr(true), Prefix: "┃ ", }, Indent: uintPtr(1), - IndentToken: stringPtr(BaseStyle().Render(" ")), + IndentToken: stringPtr(" "), }, List: ansi.StyleList{ LevelIndent: defaultMargin, StyleBlock: ansi.StyleBlock{ - IndentToken: stringPtr(BaseStyle().Render(" ")), + IndentToken: stringPtr(" "), StylePrimitive: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownText())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownText())), }, }, }, Heading: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{ BlockSuffix: "\n", - Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())), Bold: boolPtr(true), }, }, H1: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{ Prefix: "# ", - Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())), Bold: boolPtr(true), }, }, H2: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{ Prefix: "## ", - Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())), Bold: boolPtr(true), }, }, H3: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{ Prefix: "### ", - Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())), Bold: boolPtr(true), }, }, H4: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{ Prefix: "#### ", - Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())), Bold: boolPtr(true), }, }, H5: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{ Prefix: "##### ", - Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())), Bold: boolPtr(true), }, }, H6: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{ Prefix: "###### ", - Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())), Bold: boolPtr(true), }, }, Strikethrough: ansi.StylePrimitive{ CrossedOut: boolPtr(true), - Color: stringPtr(adaptiveColorToString(t.TextMuted())), + Color: stringPtr(AdaptiveColorToString(t.TextMuted())), }, Emph: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownEmph())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownEmph())), Italic: boolPtr(true), }, Strong: ansi.StylePrimitive{ Bold: boolPtr(true), - Color: stringPtr(adaptiveColorToString(t.MarkdownStrong())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownStrong())), }, HorizontalRule: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownHorizontalRule())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownHorizontalRule())), Format: "\n─────────────────────────────────────────\n", }, Item: ansi.StylePrimitive{ BlockPrefix: "• ", - Color: stringPtr(adaptiveColorToString(t.MarkdownListItem())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownListItem())), }, Enumeration: ansi.StylePrimitive{ BlockPrefix: ". ", - Color: stringPtr(adaptiveColorToString(t.MarkdownListEnumeration())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownListEnumeration())), }, Task: ansi.StyleTask{ StylePrimitive: ansi.StylePrimitive{}, @@ -133,116 +137,147 @@ func generateMarkdownStyleConfig() ansi.StyleConfig { Unticked: "[ ] ", }, Link: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownLink())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownLink())), Underline: boolPtr(true), }, LinkText: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownLinkText())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownLinkText())), Bold: boolPtr(true), }, Image: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownImage())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownImage())), Underline: boolPtr(true), Format: "🖼 {{.text}}", }, ImageText: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownImageText())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownImageText())), Format: "{{.text}}", }, Code: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownCode())), - Prefix: "", - Suffix: "", + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.MarkdownCode())), + Prefix: "", + Suffix: "", }, }, CodeBlock: ansi.StyleCodeBlock{ StyleBlock: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{ - Prefix: " ", - Color: stringPtr(adaptiveColorToString(t.MarkdownCodeBlock())), + BackgroundColor: background, + Prefix: " ", + Color: stringPtr(AdaptiveColorToString(t.MarkdownCodeBlock())), }, }, Chroma: &ansi.Chroma{ + Background: ansi.StylePrimitive{ + BackgroundColor: background, + }, Text: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownText())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.MarkdownText())), }, Error: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.Error())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.Error())), }, Comment: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxComment())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxComment())), }, CommentPreproc: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())), }, Keyword: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())), }, KeywordReserved: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())), }, KeywordNamespace: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())), }, KeywordType: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxType())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxType())), }, Operator: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxOperator())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxOperator())), }, Punctuation: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxPunctuation())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxPunctuation())), }, Name: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxVariable())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxVariable())), }, NameBuiltin: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxVariable())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxVariable())), }, NameTag: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())), }, NameAttribute: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxFunction())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxFunction())), }, NameClass: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxType())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxType())), }, NameConstant: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxVariable())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxVariable())), }, NameDecorator: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxFunction())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxFunction())), }, NameFunction: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxFunction())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxFunction())), }, LiteralNumber: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxNumber())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxNumber())), }, LiteralString: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxString())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxString())), }, LiteralStringEscape: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())), }, GenericDeleted: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.DiffRemoved())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.DiffRemoved())), }, GenericEmph: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownEmph())), - Italic: boolPtr(true), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.MarkdownEmph())), + Italic: boolPtr(true), }, GenericInserted: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.DiffAdded())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.DiffAdded())), }, GenericStrong: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownStrong())), - Bold: boolPtr(true), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.MarkdownStrong())), + Bold: boolPtr(true), }, GenericSubheading: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), + BackgroundColor: background, + Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())), }, }, }, @@ -259,24 +294,26 @@ func generateMarkdownStyleConfig() ansi.StyleConfig { }, DefinitionDescription: ansi.StylePrimitive{ BlockPrefix: "\n ❯ ", - Color: stringPtr(adaptiveColorToString(t.MarkdownLinkText())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownLinkText())), }, Text: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownText())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownText())), }, Paragraph: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{ - Color: stringPtr(adaptiveColorToString(t.MarkdownText())), + Color: stringPtr(AdaptiveColorToString(t.MarkdownText())), }, }, } } -// adaptiveColorToString converts a lipgloss.AdaptiveColor to the appropriate +// AdaptiveColorToString converts a compat.AdaptiveColor to the appropriate // hex color string based on the current terminal background -func adaptiveColorToString(color lipgloss.AdaptiveColor) string { - if lipgloss.HasDarkBackground() { - return color.Dark +func AdaptiveColorToString(color compat.AdaptiveColor) string { + if Terminal.BackgroundIsDark { + c1, _ := colorful.MakeColor(color.Dark) + return c1.Hex() } - return color.Light + c1, _ := colorful.MakeColor(color.Light) + return c1.Hex() } diff --git a/packages/tui/internal/styles/styles.go b/packages/tui/internal/styles/styles.go index 3916ea12c..4b29091c3 100644 --- a/packages/tui/internal/styles/styles.go +++ b/packages/tui/internal/styles/styles.go @@ -1,7 +1,8 @@ package styles import ( - "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/lipgloss/v2" + "github.com/charmbracelet/lipgloss/v2/compat" "github.com/sst/opencode/internal/theme" ) @@ -83,76 +84,76 @@ func DimBorder() lipgloss.Style { } // PrimaryColor returns the primary color from the current theme -func PrimaryColor() lipgloss.AdaptiveColor { +func PrimaryColor() compat.AdaptiveColor { return theme.CurrentTheme().Primary() } // SecondaryColor returns the secondary color from the current theme -func SecondaryColor() lipgloss.AdaptiveColor { +func SecondaryColor() compat.AdaptiveColor { return theme.CurrentTheme().Secondary() } // AccentColor returns the accent color from the current theme -func AccentColor() lipgloss.AdaptiveColor { +func AccentColor() compat.AdaptiveColor { return theme.CurrentTheme().Accent() } // ErrorColor returns the error color from the current theme -func ErrorColor() lipgloss.AdaptiveColor { +func ErrorColor() compat.AdaptiveColor { return theme.CurrentTheme().Error() } // WarningColor returns the warning color from the current theme -func WarningColor() lipgloss.AdaptiveColor { +func WarningColor() compat.AdaptiveColor { return theme.CurrentTheme().Warning() } // SuccessColor returns the success color from the current theme -func SuccessColor() lipgloss.AdaptiveColor { +func SuccessColor() compat.AdaptiveColor { return theme.CurrentTheme().Success() } // InfoColor returns the info color from the current theme -func InfoColor() lipgloss.AdaptiveColor { +func InfoColor() compat.AdaptiveColor { return theme.CurrentTheme().Info() } // TextColor returns the text color from the current theme -func TextColor() lipgloss.AdaptiveColor { +func TextColor() compat.AdaptiveColor { return theme.CurrentTheme().Text() } // TextMutedColor returns the muted text color from the current theme -func TextMutedColor() lipgloss.AdaptiveColor { +func TextMutedColor() compat.AdaptiveColor { return theme.CurrentTheme().TextMuted() } // BackgroundColor returns the background color from the current theme -func BackgroundColor() lipgloss.AdaptiveColor { +func BackgroundColor() compat.AdaptiveColor { return theme.CurrentTheme().Background() } // BackgroundSubtleColor returns the subtle background color from the current theme -func BackgroundSubtleColor() lipgloss.AdaptiveColor { +func BackgroundSubtleColor() compat.AdaptiveColor { return theme.CurrentTheme().BackgroundSubtle() } // BackgroundElementColor returns the darker background color from the current theme -func BackgroundElementColor() lipgloss.AdaptiveColor { +func BackgroundElementColor() compat.AdaptiveColor { return theme.CurrentTheme().BackgroundElement() } // BorderColor returns the border color from the current theme -func BorderColor() lipgloss.AdaptiveColor { +func BorderColor() compat.AdaptiveColor { return theme.CurrentTheme().Border() } // BorderActiveColor returns the active border color from the current theme -func BorderActiveColor() lipgloss.AdaptiveColor { +func BorderActiveColor() compat.AdaptiveColor { return theme.CurrentTheme().BorderActive() } // BorderSubtleColor returns the subtle border color from the current theme -func BorderSubtleColor() lipgloss.AdaptiveColor { +func BorderSubtleColor() compat.AdaptiveColor { return theme.CurrentTheme().BorderSubtle() } |
