diff --git a/Makefile b/Makefile index 2f4f80b..bfb5659 100644 --- a/Makefile +++ b/Makefile @@ -14,3 +14,5 @@ run: fmt: go fmt ./... +theme-light: + go run ./cmd/diffium watch --theme light diff --git a/internal/cli/watch.go b/internal/cli/watch.go index b7ce3b6..bd39767 100644 --- a/internal/cli/watch.go +++ b/internal/cli/watch.go @@ -1,26 +1,66 @@ +// package cli +// +// import ( +// "fmt" +// +// "github.com/interpretive-systems/diffium/internal/gitx" +// "github.com/interpretive-systems/diffium/internal/tui" +// "github.com/spf13/cobra" +// ) +// +// func newWatchCmd() *cobra.Command { +// cmd := &cobra.Command{ +// Use: "watch", +// Short: "Open the TUI and watch for changes", +// RunE: func(cmd *cobra.Command, args []string) error { +// repoPath := mustGetStringFlag(cmd.Root(), "repo") +// root, err := gitx.RepoRoot(repoPath) +// if err != nil { +// return fmt.Errorf("not a git repo: %w", err) +// } +// return tui.Run(root) +// }, +// } +// return cmd +// } +// + + package cli import ( - "fmt" + "fmt" - "github.com/interpretive-systems/diffium/internal/gitx" - "github.com/interpretive-systems/diffium/internal/tui" - "github.com/spf13/cobra" + "github.com/interpretive-systems/diffium/internal/gitx" + "github.com/interpretive-systems/diffium/internal/tui" + "github.com/spf13/cobra" ) func newWatchCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "watch", - Short: "Open the TUI and watch for changes", - RunE: func(cmd *cobra.Command, args []string) error { - repoPath := mustGetStringFlag(cmd.Root(), "repo") - root, err := gitx.RepoRoot(repoPath) - if err != nil { - return fmt.Errorf("not a git repo: %w", err) - } - return tui.Run(root) - }, - } - return cmd -} + var theme string // 1. Define a variable to hold the flag value + + cmd := &cobra.Command{ + Use: "watch", + Short: "Open the TUI and watch for changes", + RunE: func(cmd *cobra.Command, args []string) error { + repoPath := mustGetStringFlag(cmd.Root(), "repo") + root, err := gitx.RepoRoot(repoPath) + if err != nil { + return fmt.Errorf("not a git repo: %w", err) + } + // 3. Pass the flag value to the updated tui.Run function + return tui.Run(root, theme) + }, + } + + // 2. Add the --theme flag to the command + cmd.Flags().StringVar( + &theme, + "theme", + "dark", // Set "dark" as the default theme + "The color theme to use: 'dark' or 'light'.", + ) + + return cmd +} diff --git a/internal/tui/program.go b/internal/tui/program.go index 1ac7bd7..661d4f7 100644 --- a/internal/tui/program.go +++ b/internal/tui/program.go @@ -62,7 +62,7 @@ type model struct { commitErr string commitDone bool lastCommit string - + currentBranch string // uncommit wizard state showUncommit bool @@ -106,7 +106,7 @@ type model struct { plErr string plDone bool plOutput string - + // search state searchActive bool searchInput textinput.Model @@ -130,8 +130,31 @@ type diffMsg struct { } // Run instantiates and runs the Bubble Tea program. -func Run(repoRoot string) error { - m := model{repoRoot: repoRoot, sideBySide: true, diffMode: "head", theme: loadThemeFromRepo(repoRoot)} +// func Run(repoRoot, themeName string) error { +// baseTheme := "dark" +// if themeName != "" { +// baseTheme = themeName +// +// } +// m := model{repoRoot: repoRoot, sideBySide: true, diffMode: "head", theme: loadThemeFromRepo(repoRoot, baseTheme)} +// p := tea.NewProgram(m, tea.WithAltScreen()) +// if _, err := p.Run(); err != nil { +// return err +// } +// return nil +// } + +// In /internal/tui/program.go + +// FIX: Change function signature to accept themeName +func Run(repoRoot string, themeName string) error { + m := model{ + repoRoot: repoRoot, + sideBySide: true, + diffMode: "head", + // FIX: Pass themeName to loadThemeFromRepo + theme: loadThemeFromRepo(repoRoot, themeName), + } p := tea.NewProgram(m, tea.WithAltScreen()) if _, err := p.Run(); err != nil { return err @@ -1982,7 +2005,7 @@ func (m model) rightBodyLinesAll(width int) []string { } } return lines - + } func (m *model) openSearch() { @@ -2017,20 +2040,20 @@ func (m model) handleSearchKeys(key tea.KeyMsg) (tea.Model, tea.Cmd) { m.closeSearch() return m, m.recalcViewport() case "ctrl+c": - return m, tea.Quit + return m, tea.Quit } - - + + // Navigation that does NOT leave input mode switch key.Type { case tea.KeyEnter: return m, (&m).advanceSearch(1) - case tea.KeyDown: + case tea.KeyDown: return m, (&m).advanceSearch(1) case tea.KeyUp: return m, (&m).advanceSearch(-1) } - + // Fallback: always let input handle it var cmd tea.Cmd diff --git a/internal/tui/theme.go b/internal/tui/theme.go index 88559cf..a4ddce1 100644 --- a/internal/tui/theme.go +++ b/internal/tui/theme.go @@ -1,67 +1,118 @@ package tui import ( - "encoding/json" - "os" - "path/filepath" + "encoding/json" + "os" + "path/filepath" + "fmt" // <--- FIXED: Added missing fmt import - "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/lipgloss" ) // Theme defines customizable colors for rendering. type Theme struct { - AddColor string `json:"addColor"` // e.g. "34" or "#22c55e" - DelColor string `json:"delColor"` // e.g. "196" or "#ef4444" - MetaColor string `json:"metaColor"` // optional, currently unused - DividerColor string `json:"dividerColor"` // e.g. "240" + AddColor string `json:"addColor"` + DelColor string `json:"delColor"` + MetaColor string `json:"metaColor"` + DividerColor string `json:"dividerColor"` + // NEW: Background colors + AddBgColor string `json:"addBgColor"` + DelBgColor string `json:"delBgColor"` } -func defaultTheme() Theme { - return Theme{ - AddColor: "34", - DelColor: "196", - MetaColor: "63", - DividerColor: "240", - } +// --- Theme Definitions --- + +func darkTheme() Theme { + return Theme{ + AddColor: "34", + DelColor: "196", + MetaColor: "63", + DividerColor: "240", + AddBgColor: "235", // Subtle dark background + DelBgColor: "235", // Subtle dark background + } +} + +func lightTheme() Theme { + return Theme{ + AddColor: "22", // Dark Green + DelColor: "9", // Dark Red + MetaColor: "27", // Dark Blue + DividerColor: "244", // Medium Gray + AddBgColor: "255", // Very light background (e.g., White-Green tint) + DelBgColor: "255", // Very light background (e.g., White-Red tint) + } +} + +// GetTheme returns the requested base theme. +func GetTheme(name string) Theme { + switch name { + case "light": + fmt.Printf("DEBUG: Loading Light Theme\n") // Temporary debug + return lightTheme() + default: // "dark" or any other value + fmt.Printf("DEBUG: Loading Dark Theme\n") // Temporary debug + return darkTheme() + } } // loadThemeFromRepo tries .diffium/theme.json at repoRoot. -func loadThemeFromRepo(repoRoot string) Theme { - t := defaultTheme() - path := filepath.Join(repoRoot, ".diffium", "theme.json") - b, err := os.ReadFile(path) - if err != nil { - return t - } - var u Theme - if err := json.Unmarshal(b, &u); err != nil { - return t - } - // Merge, keeping defaults for empty fields - if u.AddColor != "" { - t.AddColor = u.AddColor - } - if u.DelColor != "" { - t.DelColor = u.DelColor - } - if u.MetaColor != "" { - t.MetaColor = u.MetaColor - } - if u.DividerColor != "" { - t.DividerColor = u.DividerColor - } - return t +func loadThemeFromRepo(repoRoot, baseTheme string) Theme { + t := GetTheme(baseTheme) + path := filepath.Join(repoRoot, ".diffium", "theme.json") + b, err := os.ReadFile(path) + if err != nil { + return t + } + var u Theme + if err := json.Unmarshal(b, &u); err != nil { + return t + } + // Merge, keeping defaults for empty fields + if u.AddColor != "" { + t.AddColor = u.AddColor + } + if u.DelColor != "" { + t.DelColor = u.DelColor + } + if u.MetaColor != "" { + t.MetaColor = u.MetaColor + } + if u.DividerColor != "" { + t.DividerColor = u.DividerColor + } + if u.AddBgColor != "" { // <--- NEW: Merge background colors + t.AddBgColor = u.AddBgColor + } + if u.DelBgColor != "" { // <--- NEW: Merge background colors + t.DelBgColor = u.DelBgColor + } + return t } func (t Theme) AddText(s string) string { - return lipgloss.NewStyle().Foreground(lipgloss.Color(t.AddColor)).Render(s) + return lipgloss.NewStyle().Foreground(lipgloss.Color(t.AddColor)).Render(s) } func (t Theme) DelText(s string) string { - return lipgloss.NewStyle().Foreground(lipgloss.Color(t.DelColor)).Render(s) + return lipgloss.NewStyle().Foreground(lipgloss.Color(t.DelColor)).Render(s) } func (t Theme) DividerText(s string) string { - return lipgloss.NewStyle().Foreground(lipgloss.Color(t.DividerColor)).Render(s) + return lipgloss.NewStyle().Foreground(lipgloss.Color(t.DividerColor)).Render(s) +} + +// NEW: Methods to apply both foreground (text) and background color for entire lines +func (t Theme) AddLine(s string) string { + return lipgloss.NewStyle(). + Foreground(lipgloss.Color(t.AddColor)). + Background(lipgloss.Color(t.AddBgColor)). + Render(s) } +func (t Theme) DelLine(s string) string { + return lipgloss.NewStyle(). + Foreground(lipgloss.Color(t.DelColor)). + Background(lipgloss.Color(t.DelBgColor)). + Render(s) +} diff --git a/test b/test index e69de29..038d718 100644 --- a/test +++ b/test @@ -0,0 +1 @@ +testing