feat: Implemented syntax styles
Treesitter integration implemented! But tests are failing, need to resolve that.
This commit is contained in:
parent
77416bc0a4
commit
1c2585b8d9
@ -15,6 +15,7 @@ type ModelBuilder struct {
|
|||||||
// NewModelBuilder: Builds and returns a new model, using the default color scheme (kanagawa-wave).
|
// NewModelBuilder: Builds and returns a new model, using the default color scheme (kanagawa-wave).
|
||||||
func NewModelBuilder() *ModelBuilder {
|
func NewModelBuilder() *ModelBuilder {
|
||||||
editorStyles := style.DefaultStyles()
|
editorStyles := style.DefaultStyles()
|
||||||
|
editorTheme := themes.NewDefaultTheme()
|
||||||
|
|
||||||
return &ModelBuilder{
|
return &ModelBuilder{
|
||||||
model: Model{
|
model: Model{
|
||||||
@ -34,8 +35,8 @@ func NewModelBuilder() *ModelBuilder {
|
|||||||
settings: core.NewDefaultSettings(),
|
settings: core.NewDefaultSettings(),
|
||||||
registers: core.DefaultRegisters(),
|
registers: core.DefaultRegisters(),
|
||||||
styles: editorStyles,
|
styles: editorStyles,
|
||||||
syntax: syntax.NewTreeSitterEngine(editorStyles),
|
syntax: syntax.NewTreeSitterEngine(editorTheme),
|
||||||
theme: themes.NewDefaultTheme(),
|
theme: editorTheme,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,8 +31,8 @@ func (m Model) View() string {
|
|||||||
// Draw window
|
// Draw window
|
||||||
view := viewWindow(win, t, options, m.Mode(), m.Syntax())
|
view := viewWindow(win, t, options, m.Mode(), m.Syntax())
|
||||||
|
|
||||||
// Command bar is seperate
|
// Command bar is separate
|
||||||
cmdBar := drawCommandBar(m)
|
cmdBar := drawCommandBar(m, t)
|
||||||
view += cmdBar
|
view += cmdBar
|
||||||
|
|
||||||
// Handle command output, draw on top
|
// Handle command output, draw on top
|
||||||
@ -225,38 +225,37 @@ func rightBar(w *core.Window, mode core.Mode, t theme.EditorTheme) (bar string)
|
|||||||
|
|
||||||
// drawCommandBar: Renders the command line showing command input, errors, or
|
// drawCommandBar: Renders the command line showing command input, errors, or
|
||||||
// output depending on the current mode and state.
|
// output depending on the current mode and state.
|
||||||
func drawCommandBar(m Model) string {
|
func drawCommandBar(m Model, t theme.EditorTheme) string {
|
||||||
styles := m.Styles()
|
|
||||||
|
|
||||||
// Compute left bar (command side)
|
// Compute left bar (command side)
|
||||||
var leftBar string
|
var leftBar string
|
||||||
if m.Mode() == core.CommandMode {
|
if m.Mode() == core.CommandMode {
|
||||||
leftBar = styles.LineStyle.Render(":")
|
leftBar = t.Line.Render(":")
|
||||||
cmd := []rune(m.Command())
|
cmd := []rune(m.Command())
|
||||||
cur := m.CommandCursor()
|
cur := m.CommandCursor()
|
||||||
for i, r := range cmd {
|
for i, r := range cmd {
|
||||||
if i == cur {
|
if i == cur {
|
||||||
leftBar += styles.DefaultCursorStyle(m.Mode()).Render(string(r))
|
leftBar += t.DefaultCursor(m.Mode()).Render(string(r))
|
||||||
} else {
|
} else {
|
||||||
leftBar += styles.LineStyle.Render(string(r))
|
leftBar += t.Line.Render(string(r))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cursor at end of command
|
// Cursor at end of command
|
||||||
if cur >= len(cmd) {
|
if cur >= len(cmd) {
|
||||||
leftBar += styles.DefaultCursorStyle(m.Mode()).Render(" ")
|
leftBar += t.DefaultCursor(m.Mode()).Render(" ")
|
||||||
}
|
}
|
||||||
// bar = fmt.Sprintf("%s %d", bar, cur)
|
// bar = fmt.Sprintf("%s %d", bar, cur)
|
||||||
} else if out := m.CommandOutput(); out != nil && len(out.Lines) > 0 && out.Inline {
|
} else if out := m.CommandOutput(); out != nil && len(out.Lines) > 0 && out.Inline {
|
||||||
// TODO: This is not perfect, temporary
|
// TODO: This is not perfect, temporary
|
||||||
text := strings.Join(out.Lines, " ")
|
text := strings.Join(out.Lines, " ")
|
||||||
if out.IsError {
|
if out.IsError {
|
||||||
leftBar = styles.CommandError.Render(text)
|
leftBar = t.CommandLine.Error.Render(text)
|
||||||
} else {
|
} else {
|
||||||
leftBar = styles.LineStyle.Render(text)
|
leftBar = t.Line.Render(text)
|
||||||
}
|
}
|
||||||
} else if strings.TrimSpace(m.Command()) != "" {
|
} else if strings.TrimSpace(m.Command()) != "" {
|
||||||
content := fmt.Sprintf(":%s", m.Command())
|
content := fmt.Sprintf(":%s", m.Command())
|
||||||
leftBar = styles.LineStyle.Render(content) //
|
leftBar = t.Line.Render(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute right bar
|
// Compute right bar
|
||||||
@ -265,12 +264,12 @@ func drawCommandBar(m Model) string {
|
|||||||
if len(m.input.Pending()) > 0 {
|
if len(m.input.Pending()) > 0 {
|
||||||
width := 10 // Size of the block to display
|
width := 10 // Size of the block to display
|
||||||
content := fmt.Sprintf("%-*s", width, m.input.Pending())
|
content := fmt.Sprintf("%-*s", width, m.input.Pending())
|
||||||
rightBar = styles.LineStyle.Render(content)
|
rightBar = t.Line.Render(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
dif := m.termWidth - (lipgloss.Width(leftBar) + lipgloss.Width(rightBar))
|
dif := m.termWidth - (lipgloss.Width(leftBar) + lipgloss.Width(rightBar))
|
||||||
|
|
||||||
bar := leftBar + strings.Repeat(styles.BackgroundStyle.Render(" "), max(0, dif)) + rightBar
|
bar := leftBar + strings.Repeat(t.Background.Render(" "), max(0, dif)) + rightBar
|
||||||
return bar
|
return bar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||||
"git.gophernest.net/azpect/TextEditor/internal/style"
|
"git.gophernest.net/azpect/TextEditor/internal/theme"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
sitter "github.com/tree-sitter/go-tree-sitter"
|
sitter "github.com/tree-sitter/go-tree-sitter"
|
||||||
)
|
)
|
||||||
@ -20,7 +20,7 @@ import (
|
|||||||
//
|
//
|
||||||
// Cached styles are represented as one style per rune for each line.
|
// Cached styles are represented as one style per rune for each line.
|
||||||
type TreeSitterEngine struct {
|
type TreeSitterEngine struct {
|
||||||
styles style.Styles
|
editorTheme theme.EditorTheme
|
||||||
registry *languageRegistry
|
registry *languageRegistry
|
||||||
|
|
||||||
cache map[*core.Buffer]*bufferCache
|
cache map[*core.Buffer]*bufferCache
|
||||||
@ -72,9 +72,9 @@ type captureRange struct {
|
|||||||
//
|
//
|
||||||
// Language support is resolved through the language registry, so the engine can
|
// Language support is resolved through the language registry, so the engine can
|
||||||
// work with any language/query pair registered there.
|
// work with any language/query pair registered there.
|
||||||
func NewTreeSitterEngine(styles style.Styles) *TreeSitterEngine {
|
func NewTreeSitterEngine(t theme.EditorTheme) *TreeSitterEngine {
|
||||||
return &TreeSitterEngine{
|
return &TreeSitterEngine{
|
||||||
styles: styles,
|
editorTheme: t,
|
||||||
registry: newLanguageRegistry(),
|
registry: newLanguageRegistry(),
|
||||||
cache: map[*core.Buffer]*bufferCache{},
|
cache: map[*core.Buffer]*bufferCache{},
|
||||||
}
|
}
|
||||||
@ -138,7 +138,7 @@ func (e *TreeSitterEngine) LineStyleMap(buf *core.Buffer, line int) []lipgloss.S
|
|||||||
runes := []rune(buf.Line(line))
|
runes := []rune(buf.Line(line))
|
||||||
out := make([]lipgloss.Style, len(runes))
|
out := make([]lipgloss.Style, len(runes))
|
||||||
for i := range out {
|
for i := range out {
|
||||||
out[i] = e.styles.LineStyle
|
out[i] = e.editorTheme.Line
|
||||||
}
|
}
|
||||||
bc.lines[line] = out
|
bc.lines[line] = out
|
||||||
return out
|
return out
|
||||||
@ -312,13 +312,13 @@ func (e *TreeSitterEngine) buildFullBuffer(buf *core.Buffer, bc *bufferCache) {
|
|||||||
if fullRebuild {
|
if fullRebuild {
|
||||||
bc.lines = map[int][]lipgloss.Style{}
|
bc.lines = map[int][]lipgloss.Style{}
|
||||||
for i := range lineCount {
|
for i := range lineCount {
|
||||||
bc.lines[i] = defaultLineStyles(lines[i], e.styles.LineStyle)
|
bc.lines[i] = defaultLineStyles(lines[i], e.editorTheme.Line)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dirty := normalizedDirtyRanges(bc.dirty, lineCount)
|
dirty := normalizedDirtyRanges(bc.dirty, lineCount)
|
||||||
for _, r := range dirty {
|
for _, r := range dirty {
|
||||||
for i := r.start; i <= r.end; i++ {
|
for i := r.start; i <= r.end; i++ {
|
||||||
bc.lines[i] = defaultLineStyles(lines[i], e.styles.LineStyle)
|
bc.lines[i] = defaultLineStyles(lines[i], e.editorTheme.Line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -397,7 +397,7 @@ func (e *TreeSitterEngine) buildFullBuffer(buf *core.Buffer, bc *bufferCache) {
|
|||||||
// rewrites.
|
// rewrites.
|
||||||
targetDirty := normalizedDirtyRanges(bc.dirty, lineCount)
|
targetDirty := normalizedDirtyRanges(bc.dirty, lineCount)
|
||||||
for _, c := range captures {
|
for _, c := range captures {
|
||||||
sty := style.CaptureStyle(e.styles.LineStyle, c.name)
|
sty := e.editorTheme.CaptureStyle(c.name)
|
||||||
for row := c.startRow; row <= c.endRow; row++ {
|
for row := c.startRow; row <= c.endRow; row++ {
|
||||||
if int(row) >= len(lines) {
|
if int(row) >= len(lines) {
|
||||||
break
|
break
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package theme
|
package theme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
)
|
)
|
||||||
@ -13,6 +15,7 @@ type EditorTheme struct {
|
|||||||
CommandLine CommandLineTheme
|
CommandLine CommandLineTheme
|
||||||
Line lipgloss.Style
|
Line lipgloss.Style
|
||||||
Background lipgloss.Style
|
Background lipgloss.Style
|
||||||
|
Syntax SyntaxTheme
|
||||||
}
|
}
|
||||||
|
|
||||||
type CursorTheme struct {
|
type CursorTheme struct {
|
||||||
@ -37,6 +40,11 @@ type CommandLineTheme struct {
|
|||||||
ContinueMessage lipgloss.Style
|
ContinueMessage lipgloss.Style
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SyntaxTheme struct {
|
||||||
|
Exact map[string]lipgloss.Style
|
||||||
|
Group map[string]lipgloss.Style
|
||||||
|
}
|
||||||
|
|
||||||
func (t EditorTheme) Cursor(mode core.Mode, textStyle lipgloss.Style) lipgloss.Style {
|
func (t EditorTheme) Cursor(mode core.Mode, textStyle lipgloss.Style) lipgloss.Style {
|
||||||
bg := textStyle.GetBackground()
|
bg := textStyle.GetBackground()
|
||||||
fg := textStyle.GetForeground()
|
fg := textStyle.GetForeground()
|
||||||
@ -75,3 +83,23 @@ func (t EditorTheme) VisualHighlightWithTextColor(textStyle lipgloss.Style) lipg
|
|||||||
return t.VisualHightlight.
|
return t.VisualHightlight.
|
||||||
Foreground(textStyle.GetForeground())
|
Foreground(textStyle.GetForeground())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use base (Line) as fallback. Every style will use the background from the base (Line).
|
||||||
|
//
|
||||||
|
// NOTE: Maybe we keep background on the mapping? Not sure for now
|
||||||
|
func (t EditorTheme) CaptureStyle(capture string) lipgloss.Style {
|
||||||
|
base := t.Line
|
||||||
|
|
||||||
|
exact := strings.ToLower(strings.TrimSpace(capture))
|
||||||
|
group := strings.Split(exact, ".")[0]
|
||||||
|
|
||||||
|
if sty, ok := t.Syntax.Exact[exact]; ok {
|
||||||
|
return sty.Background(base.GetBackground())
|
||||||
|
}
|
||||||
|
|
||||||
|
if sty, ok := t.Syntax.Group[group]; ok {
|
||||||
|
return sty.Background(base.GetBackground())
|
||||||
|
}
|
||||||
|
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
package themes
|
package themes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.gophernest.net/azpect/TextEditor/internal/theme"
|
"git.gophernest.net/azpect/TextEditor/internal/theme"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
)
|
)
|
||||||
|
|
||||||
const background = lipgloss.Color("#1f2335")
|
const background = lipgloss.Color("#111418")
|
||||||
const foreground = lipgloss.Color("#dcd7ba")
|
const foreground = lipgloss.Color("#d4d8e1")
|
||||||
|
|
||||||
func NewDefaultTheme() theme.EditorTheme {
|
func NewDefaultTheme() theme.EditorTheme {
|
||||||
hightlight := lipgloss.NewStyle().
|
hightlight := lipgloss.NewStyle().
|
||||||
@ -27,6 +29,7 @@ func NewDefaultTheme() theme.EditorTheme {
|
|||||||
CommandLine: newDefaultCommandLineTheme(),
|
CommandLine: newDefaultCommandLineTheme(),
|
||||||
Line: line,
|
Line: line,
|
||||||
Background: background,
|
Background: background,
|
||||||
|
Syntax: newDefaultSyntaxTheme(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,19 +54,19 @@ func newDefaultCursorTheme() theme.CursorTheme {
|
|||||||
|
|
||||||
func newDefaultGutterTheme() theme.GutterTheme {
|
func newDefaultGutterTheme() theme.GutterTheme {
|
||||||
base := lipgloss.NewStyle().
|
base := lipgloss.NewStyle().
|
||||||
Background(lipgloss.Color("#181b2a")).
|
Background(lipgloss.Color("#0d1014")).
|
||||||
Foreground(lipgloss.Color("#7e8399"))
|
Foreground(lipgloss.Color("#6b7280"))
|
||||||
|
|
||||||
return theme.GutterTheme{
|
return theme.GutterTheme{
|
||||||
Default: base,
|
Default: base,
|
||||||
CurrentLine: base.Foreground(lipgloss.Color("#f6c384")),
|
CurrentLine: base.Foreground(lipgloss.Color("#c0c8d8")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDefaultStatusBarTheme() theme.StatusBarTheme {
|
func newDefaultStatusBarTheme() theme.StatusBarTheme {
|
||||||
bar := lipgloss.NewStyle().
|
bar := lipgloss.NewStyle().
|
||||||
Background(lipgloss.Color("#181b2a")).
|
Background(lipgloss.Color("#0d1014")).
|
||||||
Foreground(lipgloss.Color("#8ea4a2"))
|
Foreground(lipgloss.Color("#8f99aa"))
|
||||||
|
|
||||||
return theme.StatusBarTheme{
|
return theme.StatusBarTheme{
|
||||||
Default: bar,
|
Default: bar,
|
||||||
@ -76,8 +79,118 @@ func newDefaultCommandLineTheme() theme.CommandLineTheme {
|
|||||||
Background(background)
|
Background(background)
|
||||||
|
|
||||||
return theme.CommandLineTheme{
|
return theme.CommandLineTheme{
|
||||||
Error: base.Foreground(lipgloss.Color("#e82424")),
|
Error: base.Foreground(lipgloss.Color("#bf616a")),
|
||||||
OutputBorder: base.Background(lipgloss.Color("#11131d")),
|
OutputBorder: base.Background(lipgloss.Color("#0d1014")),
|
||||||
ContinueMessage: base.Foreground(lipgloss.Color("#7aa2f7")),
|
ContinueMessage: base.Foreground(lipgloss.Color("#81a1c1")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newDefaultSyntaxTheme() theme.SyntaxTheme {
|
||||||
|
exact := map[string]lipgloss.Style{
|
||||||
|
"attribute.builtin": color("#ebcb8b"),
|
||||||
|
"character.special": color("#d08770"),
|
||||||
|
"comment.documentation": color("#8f99aa"),
|
||||||
|
"constant.builtin": color("#88c0d0"),
|
||||||
|
"constant.macro": color("#d08770"),
|
||||||
|
"function.builtin": color("#88c0d0"),
|
||||||
|
"function.call": color("#81a1c1"),
|
||||||
|
"function.macro": color("#d08770"),
|
||||||
|
"function.method": color("#81a1c1"),
|
||||||
|
"function.method.call": color("#81a1c1"),
|
||||||
|
"keyword.conditional": color("#b48ead"),
|
||||||
|
"keyword.conditional.ternary": color("#b48ead"),
|
||||||
|
"keyword.coroutine": color("#b48ead"),
|
||||||
|
"keyword.debug": color("#b48ead"),
|
||||||
|
"keyword.directive": color("#b48ead"),
|
||||||
|
"keyword.directive.define": color("#b48ead"),
|
||||||
|
"keyword.exception": color("#b48ead"),
|
||||||
|
"keyword.function": color("#b48ead"),
|
||||||
|
"keyword.import": color("#b48ead"),
|
||||||
|
"keyword.modifier": color("#b48ead"),
|
||||||
|
"keyword.operator": color("#d08770"),
|
||||||
|
"keyword.repeat": color("#b48ead"),
|
||||||
|
"keyword.return": color("#b48ead"),
|
||||||
|
"keyword.type": color("#ebcb8b"),
|
||||||
|
"markup.heading": color("#ebcb8b"),
|
||||||
|
"markup.heading.1": color("#ebcb8b"),
|
||||||
|
"markup.heading.2": color("#e5c68a"),
|
||||||
|
"markup.heading.3": color("#ddbe88"),
|
||||||
|
"markup.heading.4": color("#d5b686"),
|
||||||
|
"markup.heading.5": color("#cdaf84"),
|
||||||
|
"markup.heading.6": color("#c5a883"),
|
||||||
|
"markup.italic": color("#c0c8d8"),
|
||||||
|
"markup.link.label": color("#81a1c1"),
|
||||||
|
"markup.raw": color("#a3be8c"),
|
||||||
|
"markup.strikethrough": color("#7f8795"),
|
||||||
|
"markup.strong": color("#ebcb8b"),
|
||||||
|
"markup.underline": color("#88c0d0"),
|
||||||
|
"module.builtin": color("#88c0d0"),
|
||||||
|
"number.float": color("#88c0d0"),
|
||||||
|
"punctuation.bracket": color("#9aa4b2"),
|
||||||
|
"punctuation.delimiter": color("#9aa4b2"),
|
||||||
|
"punctuation.special": color("#d08770"),
|
||||||
|
"string.documentation": color("#a3be8c"),
|
||||||
|
"string.escape": color("#d08770"),
|
||||||
|
"string.regexp": color("#88c0d0"),
|
||||||
|
"string.special.path": color("#a3be8c"),
|
||||||
|
"string.special.symbol": color("#88c0d0"),
|
||||||
|
"string.special.url": color("#88c0d0"),
|
||||||
|
"tag.attribute": color("#ebcb8b"),
|
||||||
|
"tag.attribute.url": color("#88c0d0"),
|
||||||
|
"tag.builtin": color("#81a1c1"),
|
||||||
|
"tag.delimiter": color("#9aa4b2"),
|
||||||
|
"type.builtin": color("#ebcb8b"),
|
||||||
|
"type.definition": color("#ebcb8b"),
|
||||||
|
"variable.builtin": color("#8fbcbb"),
|
||||||
|
"variable.member": color("#c0c8d8"),
|
||||||
|
"variable.parameter": color("#c0c8d8"),
|
||||||
|
}
|
||||||
|
|
||||||
|
group := map[string]lipgloss.Style{
|
||||||
|
"attribute": color("#ebcb8b"),
|
||||||
|
"boolean": color("#88c0d0"),
|
||||||
|
"character": color("#a3be8c"),
|
||||||
|
"charset": color("#ebcb8b"),
|
||||||
|
"comment": color("#7f8795"),
|
||||||
|
"conceal": color("#7f8795"),
|
||||||
|
"constant": color("#88c0d0"),
|
||||||
|
"constructor": color("#ebcb8b"),
|
||||||
|
"error": color("#bf616a"),
|
||||||
|
"function": color("#81a1c1"),
|
||||||
|
"import": color("#b48ead"),
|
||||||
|
"interface": color("#ebcb8b"),
|
||||||
|
"keyframes": color("#d08770"),
|
||||||
|
"keyword": color("#b48ead"),
|
||||||
|
"label": color("#d08770"),
|
||||||
|
"media": color("#d08770"),
|
||||||
|
"module": color("#81a1c1"),
|
||||||
|
"namespace": color("#81a1c1"),
|
||||||
|
"none": color("#d4d8e1"),
|
||||||
|
"nospell": color("#d4d8e1"),
|
||||||
|
"number": color("#88c0d0"),
|
||||||
|
"operator": color("#9aa4b2"),
|
||||||
|
"property": color("#c0c8d8"),
|
||||||
|
"spell": color("#d4d8e1"),
|
||||||
|
"string": color("#a3be8c"),
|
||||||
|
"supports": color("#d08770"),
|
||||||
|
"tag": color("#81a1c1"),
|
||||||
|
"type": color("#ebcb8b"),
|
||||||
|
"variable": color("#d4d8e1"),
|
||||||
|
}
|
||||||
|
|
||||||
|
return theme.SyntaxTheme{
|
||||||
|
Exact: exact,
|
||||||
|
Group: group,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple helper to create a lipgloss style with the provided foreground
|
||||||
|
func color(c string) lipgloss.Style {
|
||||||
|
col := foreground
|
||||||
|
if strings.TrimSpace(c) != "" {
|
||||||
|
col = lipgloss.Color(c)
|
||||||
|
}
|
||||||
|
return lipgloss.NewStyle().
|
||||||
|
Background(background).
|
||||||
|
Foreground(col)
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user