Merge branch 'worktree-agent-adaf01c0' into feat/phase1-foundation
This commit is contained in:
commit
3f8035ac72
94
internal/theme/builtins.go
Normal file
94
internal/theme/builtins.go
Normal file
@ -0,0 +1,94 @@
|
||||
package theme
|
||||
|
||||
type Theme struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Foreground string `json:"foreground"`
|
||||
Background string `json:"background"`
|
||||
Cursor string `json:"cursor"`
|
||||
Black string `json:"black"`
|
||||
Red string `json:"red"`
|
||||
Green string `json:"green"`
|
||||
Yellow string `json:"yellow"`
|
||||
Blue string `json:"blue"`
|
||||
Magenta string `json:"magenta"`
|
||||
Cyan string `json:"cyan"`
|
||||
White string `json:"white"`
|
||||
BrightBlack string `json:"brightBlack"`
|
||||
BrightRed string `json:"brightRed"`
|
||||
BrightGreen string `json:"brightGreen"`
|
||||
BrightYellow string `json:"brightYellow"`
|
||||
BrightBlue string `json:"brightBlue"`
|
||||
BrightMagenta string `json:"brightMagenta"`
|
||||
BrightCyan string `json:"brightCyan"`
|
||||
BrightWhite string `json:"brightWhite"`
|
||||
SelectionBg string `json:"selectionBg,omitempty"`
|
||||
SelectionFg string `json:"selectionFg,omitempty"`
|
||||
IsBuiltin bool `json:"isBuiltin"`
|
||||
}
|
||||
|
||||
var BuiltinThemes = []Theme{
|
||||
{
|
||||
Name: "Dracula", IsBuiltin: true,
|
||||
Foreground: "#f8f8f2", Background: "#282a36", Cursor: "#f8f8f2",
|
||||
Black: "#21222c", Red: "#ff5555", Green: "#50fa7b", Yellow: "#f1fa8c",
|
||||
Blue: "#bd93f9", Magenta: "#ff79c6", Cyan: "#8be9fd", White: "#f8f8f2",
|
||||
BrightBlack: "#6272a4", BrightRed: "#ff6e6e", BrightGreen: "#69ff94",
|
||||
BrightYellow: "#ffffa5", BrightBlue: "#d6acff", BrightMagenta: "#ff92df",
|
||||
BrightCyan: "#a4ffff", BrightWhite: "#ffffff",
|
||||
},
|
||||
{
|
||||
Name: "Nord", IsBuiltin: true,
|
||||
Foreground: "#d8dee9", Background: "#2e3440", Cursor: "#d8dee9",
|
||||
Black: "#3b4252", Red: "#bf616a", Green: "#a3be8c", Yellow: "#ebcb8b",
|
||||
Blue: "#81a1c1", Magenta: "#b48ead", Cyan: "#88c0d0", White: "#e5e9f0",
|
||||
BrightBlack: "#4c566a", BrightRed: "#bf616a", BrightGreen: "#a3be8c",
|
||||
BrightYellow: "#ebcb8b", BrightBlue: "#81a1c1", BrightMagenta: "#b48ead",
|
||||
BrightCyan: "#8fbcbb", BrightWhite: "#eceff4",
|
||||
},
|
||||
{
|
||||
Name: "Monokai", IsBuiltin: true,
|
||||
Foreground: "#f8f8f2", Background: "#272822", Cursor: "#f8f8f0",
|
||||
Black: "#272822", Red: "#f92672", Green: "#a6e22e", Yellow: "#f4bf75",
|
||||
Blue: "#66d9ef", Magenta: "#ae81ff", Cyan: "#a1efe4", White: "#f8f8f2",
|
||||
BrightBlack: "#75715e", BrightRed: "#f92672", BrightGreen: "#a6e22e",
|
||||
BrightYellow: "#f4bf75", BrightBlue: "#66d9ef", BrightMagenta: "#ae81ff",
|
||||
BrightCyan: "#a1efe4", BrightWhite: "#f9f8f5",
|
||||
},
|
||||
{
|
||||
Name: "One Dark", IsBuiltin: true,
|
||||
Foreground: "#abb2bf", Background: "#282c34", Cursor: "#528bff",
|
||||
Black: "#282c34", Red: "#e06c75", Green: "#98c379", Yellow: "#e5c07b",
|
||||
Blue: "#61afef", Magenta: "#c678dd", Cyan: "#56b6c2", White: "#abb2bf",
|
||||
BrightBlack: "#545862", BrightRed: "#e06c75", BrightGreen: "#98c379",
|
||||
BrightYellow: "#e5c07b", BrightBlue: "#61afef", BrightMagenta: "#c678dd",
|
||||
BrightCyan: "#56b6c2", BrightWhite: "#c8ccd4",
|
||||
},
|
||||
{
|
||||
Name: "Solarized Dark", IsBuiltin: true,
|
||||
Foreground: "#839496", Background: "#002b36", Cursor: "#839496",
|
||||
Black: "#073642", Red: "#dc322f", Green: "#859900", Yellow: "#b58900",
|
||||
Blue: "#268bd2", Magenta: "#d33682", Cyan: "#2aa198", White: "#eee8d5",
|
||||
BrightBlack: "#002b36", BrightRed: "#cb4b16", BrightGreen: "#586e75",
|
||||
BrightYellow: "#657b83", BrightBlue: "#839496", BrightMagenta: "#6c71c4",
|
||||
BrightCyan: "#93a1a1", BrightWhite: "#fdf6e3",
|
||||
},
|
||||
{
|
||||
Name: "Gruvbox Dark", IsBuiltin: true,
|
||||
Foreground: "#ebdbb2", Background: "#282828", Cursor: "#ebdbb2",
|
||||
Black: "#282828", Red: "#cc241d", Green: "#98971a", Yellow: "#d79921",
|
||||
Blue: "#458588", Magenta: "#b16286", Cyan: "#689d6a", White: "#a89984",
|
||||
BrightBlack: "#928374", BrightRed: "#fb4934", BrightGreen: "#b8bb26",
|
||||
BrightYellow: "#fabd2f", BrightBlue: "#83a598", BrightMagenta: "#d3869b",
|
||||
BrightCyan: "#8ec07c", BrightWhite: "#ebdbb2",
|
||||
},
|
||||
{
|
||||
Name: "MobaXTerm Classic", IsBuiltin: true,
|
||||
Foreground: "#ececec", Background: "#242424", Cursor: "#b4b4c0",
|
||||
Black: "#000000", Red: "#aa4244", Green: "#7e8d53", Yellow: "#e4b46d",
|
||||
Blue: "#6e9aba", Magenta: "#9e5085", Cyan: "#80d5cf", White: "#cccccc",
|
||||
BrightBlack: "#808080", BrightRed: "#cc7b7d", BrightGreen: "#a5b17c",
|
||||
BrightYellow: "#ecc995", BrightBlue: "#96b6cd", BrightMagenta: "#c083ac",
|
||||
BrightCyan: "#a9e2de", BrightWhite: "#cccccc",
|
||||
},
|
||||
}
|
||||
85
internal/theme/service.go
Normal file
85
internal/theme/service.go
Normal file
@ -0,0 +1,85 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ThemeService struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewThemeService(db *sql.DB) *ThemeService {
|
||||
return &ThemeService{db: db}
|
||||
}
|
||||
|
||||
func (s *ThemeService) SeedBuiltins() error {
|
||||
for _, t := range BuiltinThemes {
|
||||
_, err := s.db.Exec(
|
||||
`INSERT OR IGNORE INTO themes (name, foreground, background, cursor,
|
||||
black, red, green, yellow, blue, magenta, cyan, white,
|
||||
bright_black, bright_red, bright_green, bright_yellow, bright_blue,
|
||||
bright_magenta, bright_cyan, bright_white, selection_bg, selection_fg, is_builtin)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,1)`,
|
||||
t.Name, t.Foreground, t.Background, t.Cursor,
|
||||
t.Black, t.Red, t.Green, t.Yellow, t.Blue, t.Magenta, t.Cyan, t.White,
|
||||
t.BrightBlack, t.BrightRed, t.BrightGreen, t.BrightYellow, t.BrightBlue,
|
||||
t.BrightMagenta, t.BrightCyan, t.BrightWhite, t.SelectionBg, t.SelectionFg,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("seed theme %s: %w", t.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ThemeService) List() ([]Theme, error) {
|
||||
rows, err := s.db.Query(
|
||||
`SELECT id, name, foreground, background, cursor,
|
||||
black, red, green, yellow, blue, magenta, cyan, white,
|
||||
bright_black, bright_red, bright_green, bright_yellow, bright_blue,
|
||||
bright_magenta, bright_cyan, bright_white,
|
||||
COALESCE(selection_bg,''), COALESCE(selection_fg,''), is_builtin
|
||||
FROM themes ORDER BY is_builtin DESC, name`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var themes []Theme
|
||||
for rows.Next() {
|
||||
var t Theme
|
||||
if err := rows.Scan(&t.ID, &t.Name, &t.Foreground, &t.Background, &t.Cursor,
|
||||
&t.Black, &t.Red, &t.Green, &t.Yellow, &t.Blue, &t.Magenta, &t.Cyan, &t.White,
|
||||
&t.BrightBlack, &t.BrightRed, &t.BrightGreen, &t.BrightYellow, &t.BrightBlue,
|
||||
&t.BrightMagenta, &t.BrightCyan, &t.BrightWhite,
|
||||
&t.SelectionBg, &t.SelectionFg, &t.IsBuiltin); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
themes = append(themes, t)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return themes, nil
|
||||
}
|
||||
|
||||
func (s *ThemeService) GetByName(name string) (*Theme, error) {
|
||||
var t Theme
|
||||
err := s.db.QueryRow(
|
||||
`SELECT id, name, foreground, background, cursor,
|
||||
black, red, green, yellow, blue, magenta, cyan, white,
|
||||
bright_black, bright_red, bright_green, bright_yellow, bright_blue,
|
||||
bright_magenta, bright_cyan, bright_white,
|
||||
COALESCE(selection_bg,''), COALESCE(selection_fg,''), is_builtin
|
||||
FROM themes WHERE name = ?`, name,
|
||||
).Scan(&t.ID, &t.Name, &t.Foreground, &t.Background, &t.Cursor,
|
||||
&t.Black, &t.Red, &t.Green, &t.Yellow, &t.Blue, &t.Magenta, &t.Cyan, &t.White,
|
||||
&t.BrightBlack, &t.BrightRed, &t.BrightGreen, &t.BrightYellow, &t.BrightBlue,
|
||||
&t.BrightMagenta, &t.BrightCyan, &t.BrightWhite,
|
||||
&t.SelectionBg, &t.SelectionFg, &t.IsBuiltin)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get theme %s: %w", name, err)
|
||||
}
|
||||
return &t, nil
|
||||
}
|
||||
60
internal/theme/service_test.go
Normal file
60
internal/theme/service_test.go
Normal file
@ -0,0 +1,60 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/vstockwell/wraith/internal/db"
|
||||
)
|
||||
|
||||
func setupTestDB(t *testing.T) *ThemeService {
|
||||
t.Helper()
|
||||
d, err := db.Open(filepath.Join(t.TempDir(), "test.db"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Migrate(d); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { d.Close() })
|
||||
return NewThemeService(d)
|
||||
}
|
||||
|
||||
func TestSeedBuiltins(t *testing.T) {
|
||||
svc := setupTestDB(t)
|
||||
if err := svc.SeedBuiltins(); err != nil {
|
||||
t.Fatalf("SeedBuiltins() error: %v", err)
|
||||
}
|
||||
|
||||
themes, err := svc.List()
|
||||
if err != nil {
|
||||
t.Fatalf("List() error: %v", err)
|
||||
}
|
||||
if len(themes) != len(BuiltinThemes) {
|
||||
t.Errorf("len(themes) = %d, want %d", len(themes), len(BuiltinThemes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeedBuiltinsIdempotent(t *testing.T) {
|
||||
svc := setupTestDB(t)
|
||||
svc.SeedBuiltins()
|
||||
svc.SeedBuiltins()
|
||||
|
||||
themes, _ := svc.List()
|
||||
if len(themes) != len(BuiltinThemes) {
|
||||
t.Errorf("len(themes) = %d after double seed, want %d", len(themes), len(BuiltinThemes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetByName(t *testing.T) {
|
||||
svc := setupTestDB(t)
|
||||
svc.SeedBuiltins()
|
||||
|
||||
theme, err := svc.GetByName("Dracula")
|
||||
if err != nil {
|
||||
t.Fatalf("GetByName() error: %v", err)
|
||||
}
|
||||
if theme.Background != "#282a36" {
|
||||
t.Errorf("Background = %q, want %q", theme.Background, "#282a36")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user