fx/theme.go

326 lines
7.6 KiB
Go
Raw Normal View History

2023-09-08 10:56:12 +03:00
package main
import (
2023-09-24 15:22:50 +03:00
"encoding/json"
2023-09-12 17:52:53 +03:00
"fmt"
2023-09-08 10:56:12 +03:00
"os"
2023-09-24 15:22:50 +03:00
"regexp"
2023-09-12 17:52:53 +03:00
"sort"
2023-09-08 10:56:12 +03:00
"github.com/charmbracelet/lipgloss"
2023-09-11 19:48:13 +03:00
"github.com/muesli/termenv"
2023-09-08 10:56:12 +03:00
)
type theme struct {
Cursor color
Syntax color
Preview color
StatusBar color
Search color
Key color
String color
Null color
Boolean color
Number color
}
type color func(s []byte) []byte
2023-09-24 15:22:50 +03:00
func valueStyle(b []byte, selected, chunk bool) color {
if selected {
return currentTheme.Cursor
} else if chunk {
return currentTheme.String
} else {
switch b[0] {
case '"':
return currentTheme.String
case 't', 'f':
return currentTheme.Boolean
case 'n':
return currentTheme.Null
case '{', '[', '}', ']':
return currentTheme.Syntax
default:
if isDigit(b[0]) || b[0] == '-' {
return currentTheme.Number
}
return noColor
}
}
}
2023-09-08 10:56:12 +03:00
func init() {
2023-09-22 15:40:01 +03:00
themeNames = make([]string, 0, len(themes))
for name := range themes {
themeNames = append(themeNames, name)
}
sort.Strings(themeNames)
2023-09-08 10:56:12 +03:00
themeId, ok := os.LookupEnv("FX_THEME")
if !ok {
themeId = "1"
}
2023-09-22 15:40:01 +03:00
2023-09-15 09:54:29 +03:00
currentTheme, ok = themes[themeId]
if !ok {
2023-09-22 15:40:01 +03:00
_, _ = fmt.Fprintf(os.Stderr, "fx: unknown theme %q, available themes: %v\n", themeId, themeNames)
os.Exit(1)
2023-09-15 09:54:29 +03:00
}
2023-09-22 15:40:01 +03:00
2023-09-11 19:48:13 +03:00
if termenv.ColorProfile() == termenv.Ascii {
currentTheme = themes["0"]
}
2023-09-11 14:16:10 +03:00
colon = currentTheme.Syntax([]byte{':', ' '})
2023-09-11 19:48:13 +03:00
colonPreview = currentTheme.Preview([]byte{':'})
2023-09-11 14:16:10 +03:00
comma = currentTheme.Syntax([]byte{','})
empty = currentTheme.Preview([]byte{'~'})
dot3 = currentTheme.Preview([]byte("…"))
closeCurlyBracket = currentTheme.Syntax([]byte{'}'})
closeSquareBracket = currentTheme.Syntax([]byte{']'})
2023-09-08 10:56:12 +03:00
}
var (
2023-09-22 15:40:01 +03:00
themeNames []string
2023-09-08 10:56:12 +03:00
currentTheme theme
defaultCursor = toColor(lipgloss.NewStyle().Reverse(true).Render)
2023-09-11 19:48:13 +03:00
defaultPreview = toColor(lipgloss.NewStyle().Foreground(lipgloss.Color("8")).Render)
2023-09-08 10:56:12 +03:00
defaultStatusBar = toColor(lipgloss.NewStyle().Background(lipgloss.Color("7")).Foreground(lipgloss.Color("0")).Render)
defaultSearch = toColor(lipgloss.NewStyle().Background(lipgloss.Color("11")).Foreground(lipgloss.Color("16")).Render)
2023-09-19 10:02:07 +03:00
defaultNull = fg("243")
2023-09-08 10:56:12 +03:00
)
2023-09-11 14:16:10 +03:00
var (
2023-09-11 14:57:09 +03:00
colon []byte
2023-09-11 19:48:13 +03:00
colonPreview []byte
2023-09-11 14:57:09 +03:00
comma []byte
empty []byte
dot3 []byte
closeCurlyBracket []byte
closeSquareBracket []byte
2023-09-11 14:16:10 +03:00
)
2023-09-08 10:56:12 +03:00
var themes = map[string]theme{
"0": {
Cursor: defaultCursor,
Syntax: noColor,
Preview: noColor,
StatusBar: noColor,
Search: defaultSearch,
Key: noColor,
String: noColor,
Null: noColor,
Boolean: noColor,
Number: noColor,
},
"1": {
Cursor: defaultCursor,
Syntax: noColor,
Preview: defaultPreview,
StatusBar: defaultStatusBar,
Search: defaultSearch,
Key: boldFg("4"),
2023-09-13 17:41:31 +03:00
String: fg("2"),
2023-09-08 10:56:12 +03:00
Null: defaultNull,
2023-09-13 17:41:31 +03:00
Boolean: fg("5"),
Number: fg("6"),
2023-09-08 10:56:12 +03:00
},
"2": {
2023-09-12 17:52:53 +03:00
Cursor: defaultCursor,
Syntax: noColor,
Preview: defaultPreview,
StatusBar: defaultStatusBar,
Search: defaultSearch,
Key: fg("2"),
String: fg("4"),
Null: defaultNull,
Boolean: fg("5"),
Number: fg("6"),
},
"3": {
2023-09-13 17:41:31 +03:00
Cursor: defaultCursor,
Syntax: noColor,
Preview: defaultPreview,
StatusBar: defaultStatusBar,
Search: defaultSearch,
Key: fg("13"),
String: fg("11"),
Null: defaultNull,
Boolean: fg("1"),
Number: fg("14"),
},
"4": {
2023-09-08 10:56:12 +03:00
Cursor: defaultCursor,
Syntax: noColor,
Preview: defaultPreview,
StatusBar: defaultStatusBar,
Search: defaultSearch,
Key: fg("#00F5D4"),
String: fg("#00BBF9"),
Null: defaultNull,
Boolean: fg("#F15BB5"),
Number: fg("#9B5DE5"),
},
2023-09-13 17:41:31 +03:00
"5": {
2023-09-08 10:56:12 +03:00
Cursor: defaultCursor,
Syntax: noColor,
Preview: defaultPreview,
StatusBar: defaultStatusBar,
Search: defaultSearch,
Key: fg("#faf0ca"),
String: fg("#f4d35e"),
Null: defaultNull,
Boolean: fg("#ee964b"),
Number: fg("#ee964b"),
},
2023-09-13 17:41:31 +03:00
"6": {
2023-09-08 10:56:12 +03:00
Cursor: defaultCursor,
Syntax: noColor,
Preview: defaultPreview,
StatusBar: defaultStatusBar,
Search: defaultSearch,
Key: fg("#4D96FF"),
String: fg("#6BCB77"),
Null: defaultNull,
Boolean: fg("#FF6B6B"),
Number: fg("#FFD93D"),
},
2023-09-13 17:41:31 +03:00
"7": {
2023-09-08 10:56:12 +03:00
Cursor: defaultCursor,
Syntax: noColor,
Preview: defaultPreview,
StatusBar: defaultStatusBar,
Search: defaultSearch,
Key: boldFg("42"),
String: boldFg("213"),
Null: defaultNull,
Boolean: boldFg("201"),
Number: boldFg("201"),
},
2023-09-13 17:41:31 +03:00
"8": {
2023-09-08 10:56:12 +03:00
Cursor: defaultCursor,
Syntax: noColor,
Preview: defaultPreview,
StatusBar: defaultStatusBar,
Search: defaultSearch,
2023-09-22 15:40:01 +03:00
Key: boldFg("51"),
2023-09-08 10:56:12 +03:00
String: fg("195"),
Null: defaultNull,
2023-09-22 15:40:01 +03:00
Boolean: fg("50"),
Number: fg("123"),
2023-09-08 10:56:12 +03:00
},
2023-09-13 17:41:31 +03:00
"🔵": {
2023-09-15 00:47:30 +03:00
Cursor: toColor(lipgloss.NewStyle().
Foreground(lipgloss.Color("15")).
Background(lipgloss.Color("33")).
Render),
2023-09-12 17:52:53 +03:00
Syntax: boldFg("33"),
Preview: defaultPreview,
StatusBar: defaultStatusBar,
Search: defaultSearch,
Key: fg("33"),
String: noColor,
Null: noColor,
Boolean: noColor,
Number: noColor,
},
2023-09-22 15:40:01 +03:00
"🥝": {
Cursor: defaultCursor,
Syntax: fg("179"),
Preview: defaultPreview,
StatusBar: defaultStatusBar,
Search: defaultSearch,
Key: boldFg("154"),
String: fg("82"),
Null: fg("230"),
Boolean: fg("226"),
Number: fg("226"),
},
2023-09-08 10:56:12 +03:00
}
func noColor(s []byte) []byte {
return s
}
func toColor(f func(s ...string) string) color {
return func(s []byte) []byte {
return []byte(f(string(s)))
}
}
func fg(color string) color {
return toColor(lipgloss.NewStyle().Foreground(lipgloss.Color(color)).Render)
}
func boldFg(color string) color {
return toColor(lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color(color)).Render)
}
2023-09-12 17:52:53 +03:00
func themeTester() {
title := lipgloss.NewStyle().Bold(true)
for _, name := range themeNames {
2023-09-24 15:22:50 +03:00
t := themes[name]
comma := string(t.Syntax([]byte{','}))
colon := string(t.Syntax([]byte{':'}))
2023-09-12 17:52:53 +03:00
fmt.Println(title.Render(fmt.Sprintf("Theme %q", name)))
2023-09-24 15:22:50 +03:00
fmt.Println(string(t.Syntax([]byte("{"))))
2023-09-12 17:52:53 +03:00
fmt.Printf(" %v%v %v%v\n",
2023-09-24 15:22:50 +03:00
string(t.Key([]byte("\"string\""))),
2023-09-12 17:52:53 +03:00
colon,
2023-09-24 15:22:50 +03:00
string(t.String([]byte("\"Fox jumps over the lazy dog\""))),
2023-09-12 17:52:53 +03:00
comma)
fmt.Printf(" %v%v %v%v\n",
2023-09-24 15:22:50 +03:00
string(t.Key([]byte("\"number\""))),
2023-09-12 17:52:53 +03:00
colon,
2023-09-24 15:22:50 +03:00
string(t.Number([]byte("1234567890"))),
2023-09-12 17:52:53 +03:00
comma)
fmt.Printf(" %v%v %v%v\n",
2023-09-24 15:22:50 +03:00
string(t.Key([]byte("\"boolean\""))),
2023-09-12 17:52:53 +03:00
colon,
2023-09-24 15:22:50 +03:00
string(t.Boolean([]byte("true"))),
2023-09-12 17:52:53 +03:00
comma)
fmt.Printf(" %v%v %v%v\n",
2023-09-24 15:22:50 +03:00
string(t.Key([]byte("\"null\""))),
2023-09-12 17:52:53 +03:00
colon,
2023-09-24 15:22:50 +03:00
string(t.Null([]byte("null"))),
2023-09-12 17:52:53 +03:00
comma)
2023-09-24 15:22:50 +03:00
fmt.Println(string(t.Syntax([]byte("}"))))
2023-09-12 17:52:53 +03:00
println()
}
}
2023-09-15 00:25:52 +03:00
2023-09-24 15:22:50 +03:00
func exportThemes() {
lipgloss.SetColorProfile(termenv.ANSI256) // Export in Terminal.app compatible colors
placeholder := []byte{'_'}
extract := func(b []byte) string {
matches := regexp.
MustCompile(`^\x1b\[(.+)m_`).
FindStringSubmatch(string(b))
if len(matches) == 0 {
return ""
} else {
return matches[1]
2023-09-15 00:25:52 +03:00
}
}
2023-09-24 15:22:50 +03:00
var export = map[string][]string{}
for _, name := range themeNames {
t := themes[name]
export[name] = append(export[name], extract(t.Syntax(placeholder)))
export[name] = append(export[name], extract(t.Key(placeholder)))
export[name] = append(export[name], extract(t.String(placeholder)))
export[name] = append(export[name], extract(t.Number(placeholder)))
export[name] = append(export[name], extract(t.Boolean(placeholder)))
export[name] = append(export[name], extract(t.Null(placeholder)))
}
data, err := json.Marshal(export)
if err != nil {
panic(err)
}
fmt.Println(string(data))
2023-09-15 00:25:52 +03:00
}