mirror of https://github.com/antonmedv/fx
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
353 lines
8.3 KiB
Go
353 lines
8.3 KiB
Go
6 months ago
|
package theme
|
||
1 year ago
|
|
||
|
import (
|
||
1 year ago
|
"encoding/json"
|
||
1 year ago
|
"fmt"
|
||
1 year ago
|
"os"
|
||
1 year ago
|
"regexp"
|
||
1 year ago
|
"sort"
|
||
6 months ago
|
"strings"
|
||
1 year ago
|
|
||
|
"github.com/charmbracelet/lipgloss"
|
||
1 year ago
|
"github.com/muesli/termenv"
|
||
6 months ago
|
|
||
|
"github.com/antonmedv/fx/internal/utils"
|
||
1 year ago
|
)
|
||
|
|
||
6 months ago
|
type Theme struct {
|
||
|
Cursor Color
|
||
|
Syntax Color
|
||
|
Preview Color
|
||
|
StatusBar Color
|
||
|
Search Color
|
||
|
Key Color
|
||
|
String Color
|
||
|
Null Color
|
||
|
Boolean Color
|
||
|
Number Color
|
||
|
Size Color
|
||
1 year ago
|
}
|
||
|
|
||
6 months ago
|
type Color func(s []byte) []byte
|
||
1 year ago
|
|
||
6 months ago
|
func Value(b []byte, selected, chunk bool) Color {
|
||
1 year ago
|
if selected {
|
||
6 months ago
|
return CurrentTheme.Cursor
|
||
1 year ago
|
} else if chunk {
|
||
6 months ago
|
return CurrentTheme.String
|
||
1 year ago
|
} else {
|
||
|
switch b[0] {
|
||
|
case '"':
|
||
6 months ago
|
return CurrentTheme.String
|
||
1 year ago
|
case 't', 'f':
|
||
6 months ago
|
return CurrentTheme.Boolean
|
||
1 year ago
|
case 'n':
|
||
6 months ago
|
return CurrentTheme.Null
|
||
1 year ago
|
case '{', '[', '}', ']':
|
||
6 months ago
|
return CurrentTheme.Syntax
|
||
1 year ago
|
default:
|
||
6 months ago
|
if utils.IsDigit(b[0]) || b[0] == '-' {
|
||
|
return CurrentTheme.Number
|
||
1 year ago
|
}
|
||
|
return noColor
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
7 months ago
|
var (
|
||
6 months ago
|
TermOutput = termenv.NewOutput(os.Stderr)
|
||
7 months ago
|
)
|
||
|
|
||
1 year ago
|
func init() {
|
||
1 year ago
|
themeNames = make([]string, 0, len(themes))
|
||
|
for name := range themes {
|
||
|
themeNames = append(themeNames, name)
|
||
|
}
|
||
|
sort.Strings(themeNames)
|
||
|
|
||
1 year ago
|
themeId, ok := os.LookupEnv("FX_THEME")
|
||
|
if !ok {
|
||
|
themeId = "1"
|
||
|
}
|
||
1 year ago
|
|
||
6 months ago
|
showSizesValue, ok := os.LookupEnv("FX_SHOW_SIZE")
|
||
6 months ago
|
if ok {
|
||
|
showSizesValue := strings.ToLower(showSizesValue)
|
||
6 months ago
|
ShowSizes = showSizesValue == "true" || showSizesValue == "yes" || showSizesValue == "on" || showSizesValue == "1"
|
||
6 months ago
|
}
|
||
|
|
||
6 months ago
|
CurrentTheme, ok = themes[themeId]
|
||
1 year ago
|
if !ok {
|
||
1 year ago
|
_, _ = fmt.Fprintf(os.Stderr, "fx: unknown theme %q, available themes: %v\n", themeId, themeNames)
|
||
|
os.Exit(1)
|
||
1 year ago
|
}
|
||
1 year ago
|
|
||
6 months ago
|
if TermOutput.ColorProfile() == termenv.Ascii {
|
||
|
CurrentTheme = themes["0"]
|
||
1 year ago
|
}
|
||
1 year ago
|
|
||
6 months ago
|
Colon = CurrentTheme.Syntax([]byte{':', ' '})
|
||
|
ColonPreview = CurrentTheme.Preview([]byte{':'})
|
||
|
Comma = CurrentTheme.Syntax([]byte{','})
|
||
|
Empty = CurrentTheme.Preview([]byte{'~'})
|
||
|
Dot3 = CurrentTheme.Preview([]byte("…"))
|
||
|
CloseCurlyBracket = CurrentTheme.Syntax([]byte{'}'})
|
||
|
CloseSquareBracket = CurrentTheme.Syntax([]byte{']'})
|
||
1 year ago
|
}
|
||
|
|
||
|
var (
|
||
1 year ago
|
themeNames []string
|
||
6 months ago
|
CurrentTheme Theme
|
||
1 year ago
|
defaultCursor = toColor(lipgloss.NewStyle().Reverse(true).Render)
|
||
1 year ago
|
defaultPreview = toColor(lipgloss.NewStyle().Foreground(lipgloss.Color("8")).Render)
|
||
1 year ago
|
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)
|
||
1 year ago
|
defaultNull = fg("243")
|
||
6 months ago
|
defaultSize = toColor(lipgloss.NewStyle().Foreground(lipgloss.Color("8")).Render)
|
||
6 months ago
|
ShowSizes = false
|
||
1 year ago
|
)
|
||
|
|
||
1 year ago
|
var (
|
||
6 months ago
|
Colon []byte
|
||
|
ColonPreview []byte
|
||
|
Comma []byte
|
||
|
Empty []byte
|
||
|
Dot3 []byte
|
||
|
CloseCurlyBracket []byte
|
||
|
CloseSquareBracket []byte
|
||
1 year ago
|
)
|
||
|
|
||
6 months ago
|
var themes = map[string]Theme{
|
||
1 year ago
|
"0": {
|
||
|
Cursor: defaultCursor,
|
||
|
Syntax: noColor,
|
||
|
Preview: noColor,
|
||
|
StatusBar: noColor,
|
||
|
Search: defaultSearch,
|
||
|
Key: noColor,
|
||
|
String: noColor,
|
||
|
Null: noColor,
|
||
|
Boolean: noColor,
|
||
|
Number: noColor,
|
||
6 months ago
|
Size: noColor,
|
||
1 year ago
|
},
|
||
|
"1": {
|
||
|
Cursor: defaultCursor,
|
||
|
Syntax: noColor,
|
||
|
Preview: defaultPreview,
|
||
|
StatusBar: defaultStatusBar,
|
||
|
Search: defaultSearch,
|
||
|
Key: boldFg("4"),
|
||
1 year ago
|
String: fg("2"),
|
||
1 year ago
|
Null: defaultNull,
|
||
1 year ago
|
Boolean: fg("5"),
|
||
|
Number: fg("6"),
|
||
6 months ago
|
Size: defaultSize,
|
||
1 year ago
|
},
|
||
|
"2": {
|
||
1 year ago
|
Cursor: defaultCursor,
|
||
|
Syntax: noColor,
|
||
|
Preview: defaultPreview,
|
||
|
StatusBar: defaultStatusBar,
|
||
|
Search: defaultSearch,
|
||
|
Key: fg("2"),
|
||
|
String: fg("4"),
|
||
|
Null: defaultNull,
|
||
|
Boolean: fg("5"),
|
||
|
Number: fg("6"),
|
||
6 months ago
|
Size: defaultSize,
|
||
1 year ago
|
},
|
||
|
"3": {
|
||
1 year ago
|
Cursor: defaultCursor,
|
||
|
Syntax: noColor,
|
||
|
Preview: defaultPreview,
|
||
|
StatusBar: defaultStatusBar,
|
||
|
Search: defaultSearch,
|
||
|
Key: fg("13"),
|
||
|
String: fg("11"),
|
||
|
Null: defaultNull,
|
||
|
Boolean: fg("1"),
|
||
|
Number: fg("14"),
|
||
6 months ago
|
Size: defaultSize,
|
||
1 year ago
|
},
|
||
|
"4": {
|
||
1 year ago
|
Cursor: defaultCursor,
|
||
|
Syntax: noColor,
|
||
|
Preview: defaultPreview,
|
||
|
StatusBar: defaultStatusBar,
|
||
|
Search: defaultSearch,
|
||
|
Key: fg("#00F5D4"),
|
||
|
String: fg("#00BBF9"),
|
||
|
Null: defaultNull,
|
||
|
Boolean: fg("#F15BB5"),
|
||
|
Number: fg("#9B5DE5"),
|
||
6 months ago
|
Size: defaultSize,
|
||
1 year ago
|
},
|
||
1 year ago
|
"5": {
|
||
1 year ago
|
Cursor: defaultCursor,
|
||
|
Syntax: noColor,
|
||
|
Preview: defaultPreview,
|
||
|
StatusBar: defaultStatusBar,
|
||
|
Search: defaultSearch,
|
||
|
Key: fg("#faf0ca"),
|
||
|
String: fg("#f4d35e"),
|
||
|
Null: defaultNull,
|
||
|
Boolean: fg("#ee964b"),
|
||
|
Number: fg("#ee964b"),
|
||
6 months ago
|
Size: defaultSize,
|
||
1 year ago
|
},
|
||
1 year ago
|
"6": {
|
||
1 year ago
|
Cursor: defaultCursor,
|
||
|
Syntax: noColor,
|
||
|
Preview: defaultPreview,
|
||
|
StatusBar: defaultStatusBar,
|
||
|
Search: defaultSearch,
|
||
|
Key: fg("#4D96FF"),
|
||
|
String: fg("#6BCB77"),
|
||
|
Null: defaultNull,
|
||
|
Boolean: fg("#FF6B6B"),
|
||
|
Number: fg("#FFD93D"),
|
||
6 months ago
|
Size: defaultSize,
|
||
1 year ago
|
},
|
||
1 year ago
|
"7": {
|
||
1 year ago
|
Cursor: defaultCursor,
|
||
|
Syntax: noColor,
|
||
|
Preview: defaultPreview,
|
||
|
StatusBar: defaultStatusBar,
|
||
|
Search: defaultSearch,
|
||
|
Key: boldFg("42"),
|
||
|
String: boldFg("213"),
|
||
|
Null: defaultNull,
|
||
|
Boolean: boldFg("201"),
|
||
|
Number: boldFg("201"),
|
||
6 months ago
|
Size: defaultSize,
|
||
1 year ago
|
},
|
||
1 year ago
|
"8": {
|
||
1 year ago
|
Cursor: defaultCursor,
|
||
|
Syntax: noColor,
|
||
|
Preview: defaultPreview,
|
||
|
StatusBar: defaultStatusBar,
|
||
|
Search: defaultSearch,
|
||
1 year ago
|
Key: boldFg("51"),
|
||
1 year ago
|
String: fg("195"),
|
||
|
Null: defaultNull,
|
||
1 year ago
|
Boolean: fg("50"),
|
||
|
Number: fg("123"),
|
||
6 months ago
|
Size: defaultSize,
|
||
1 year ago
|
},
|
||
1 year ago
|
"🔵": {
|
||
1 year ago
|
Cursor: toColor(lipgloss.NewStyle().
|
||
|
Foreground(lipgloss.Color("15")).
|
||
|
Background(lipgloss.Color("33")).
|
||
|
Render),
|
||
1 year ago
|
Syntax: boldFg("33"),
|
||
|
Preview: defaultPreview,
|
||
|
StatusBar: defaultStatusBar,
|
||
|
Search: defaultSearch,
|
||
|
Key: fg("33"),
|
||
|
String: noColor,
|
||
|
Null: noColor,
|
||
|
Boolean: noColor,
|
||
|
Number: noColor,
|
||
6 months ago
|
Size: defaultSize,
|
||
1 year ago
|
},
|
||
1 year ago
|
"🥝": {
|
||
|
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"),
|
||
6 months ago
|
Size: defaultSize,
|
||
1 year ago
|
},
|
||
1 year ago
|
}
|
||
|
|
||
|
func noColor(s []byte) []byte {
|
||
|
return s
|
||
|
}
|
||
|
|
||
6 months ago
|
func toColor(f func(s ...string) string) Color {
|
||
1 year ago
|
return func(s []byte) []byte {
|
||
|
return []byte(f(string(s)))
|
||
|
}
|
||
|
}
|
||
|
|
||
6 months ago
|
func fg(color string) Color {
|
||
1 year ago
|
return toColor(lipgloss.NewStyle().Foreground(lipgloss.Color(color)).Render)
|
||
|
}
|
||
|
|
||
6 months ago
|
func boldFg(color string) Color {
|
||
1 year ago
|
return toColor(lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color(color)).Render)
|
||
|
}
|
||
|
|
||
6 months ago
|
func ThemeTester() {
|
||
1 year ago
|
title := lipgloss.NewStyle().Bold(true)
|
||
|
for _, name := range themeNames {
|
||
1 year ago
|
t := themes[name]
|
||
|
comma := string(t.Syntax([]byte{','}))
|
||
|
colon := string(t.Syntax([]byte{':'}))
|
||
1 year ago
|
|
||
|
fmt.Println(title.Render(fmt.Sprintf("Theme %q", name)))
|
||
1 year ago
|
fmt.Println(string(t.Syntax([]byte("{"))))
|
||
1 year ago
|
|
||
|
fmt.Printf(" %v%v %v%v\n",
|
||
1 year ago
|
string(t.Key([]byte("\"string\""))),
|
||
1 year ago
|
colon,
|
||
1 year ago
|
string(t.String([]byte("\"Fox jumps over the lazy dog\""))),
|
||
1 year ago
|
comma)
|
||
|
|
||
|
fmt.Printf(" %v%v %v%v\n",
|
||
1 year ago
|
string(t.Key([]byte("\"number\""))),
|
||
1 year ago
|
colon,
|
||
1 year ago
|
string(t.Number([]byte("1234567890"))),
|
||
1 year ago
|
comma)
|
||
|
|
||
|
fmt.Printf(" %v%v %v%v\n",
|
||
1 year ago
|
string(t.Key([]byte("\"boolean\""))),
|
||
1 year ago
|
colon,
|
||
1 year ago
|
string(t.Boolean([]byte("true"))),
|
||
1 year ago
|
comma)
|
||
|
fmt.Printf(" %v%v %v%v\n",
|
||
1 year ago
|
string(t.Key([]byte("\"null\""))),
|
||
1 year ago
|
colon,
|
||
1 year ago
|
string(t.Null([]byte("null"))),
|
||
1 year ago
|
comma)
|
||
1 year ago
|
fmt.Println(string(t.Syntax([]byte("}"))))
|
||
1 year ago
|
println()
|
||
|
}
|
||
|
}
|
||
1 year ago
|
|
||
6 months ago
|
func ExportThemes() {
|
||
1 year ago
|
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]
|
||
1 year ago
|
}
|
||
|
}
|
||
1 year ago
|
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))
|
||
1 year ago
|
}
|