From 83d62e6f40c7a391f36f853b177ec8ba5a5340fe Mon Sep 17 00:00:00 2001 From: Bryce Palmer Date: Thu, 4 Jan 2024 20:55:36 -0500 Subject: [PATCH] (feat): add theme customization (#47) Signed-off-by: everettraven --- README.md | 7 ++ docs/_sidebar.md | 1 + docs/features/themes.md | 33 +++++++++ test.json => examples/dashboards/test.json | 0 test.yaml => examples/dashboards/test.yaml | 0 examples/themes/theme.json | 16 +++++ internal/cli/root.go | 19 ++++-- pkg/charm/models/dashboard.go | 8 ++- pkg/charm/models/panels/logs.go | 31 +++++---- pkg/charm/models/panels/table.go | 10 +-- pkg/charm/models/tabber.go | 18 +++-- pkg/charm/styles/styles.go | 79 ++++++++++++++++++---- pkg/paneler/item.go | 9 ++- pkg/paneler/logs.go | 7 +- pkg/paneler/paneler.go | 9 +-- pkg/paneler/table.go | 7 +- 16 files changed, 199 insertions(+), 55 deletions(-) create mode 100644 docs/features/themes.md rename test.json => examples/dashboards/test.json (100%) rename test.yaml => examples/dashboards/test.yaml (100%) create mode 100644 examples/themes/theme.json diff --git a/README.md b/README.md index 2d6180c..e431b98 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,13 @@ [![asciicast](https://asciinema.org/a/625808.svg)](https://asciinema.org/a/625808) +## Motivation + +I created `buoy` because I do a lot of work on Kubernetes controllers. When I am making changes, I often find myself typing out a bunch of the same `kubectl ...` commands and switching between them. +Some of those commands are blocking (i.e `kubectl get logs -f ...`) and to keep them running while running other commands required opening a new terminal window and more typing. +Since I was running pretty repetitive commands I thought there had to be a better solution. I looked through existing CLI tooling around this space, but none had a simple interface that followed the pattern of +"define what you want to see and I'll show it to you". Thus `buoy` was created to fill this gap (and save me some time while delaying the inevitable arthritis). + ## Quickstart Install `buoy` by downloading one of the binaries from the [releases](https://github.com/everettraven/buoy/releases) or by running: diff --git a/docs/_sidebar.md b/docs/_sidebar.md index c3159dc..e703ddb 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -8,4 +8,5 @@ - Features - [Dot Notation Field Paths](features/dot-notation-paths.md) - [Remote Dashboard Configurations](features/remote-configs.md) + - [Theme Customization](features/themes.md) diff --git a/docs/features/themes.md b/docs/features/themes.md new file mode 100644 index 0000000..ab0bb90 --- /dev/null +++ b/docs/features/themes.md @@ -0,0 +1,33 @@ +# Customizing the theme of your dashboards + +`buoy` supports customizing the theme of your dashboard via a theme configuration file. There are two ways this can be done: +1. Specifying the path to the file via the `--theme` flag. Ex: `buoy dash.json --theme path/to/theme.json` +2. Creating a theme file in the default theme configuration file path (`~/.config/buoy/themes/default.json`) + +Currently, the theme configuration files must be JSON and looks like: +```json +{ + "tabColor": { + "light": "63", + "dark": "117" + }, + "selectedRowHighlightColor": { + "light": "63", + "dark": "117" + }, + "logSearchHighlightColor": { + "light": "63", + "dark": "117" + }, + "syntaxHighlightDarkColor": "nord", + "syntaxHighlightLightColor": "monokailight" +} +``` + +?> The example above is a representation of the default theme that is used when no theme configurations are found/specified + +- `tabColor` is the adaptive color scheme used when rendering each tab and the borders of the dashboard. `buoy` renders adaptively based on the color of the terminal background so the color code specified in `light` will be the color that is used when rendering on a light terminal background and `dark` will be used when rendering on a dark terminal background. `buoy` uses https://github.com/charmbracelet/lipgloss for styling and thus respects the same color values. The supported values for colors can be found at https://github.com/charmbracelet/lipgloss?tab=readme-ov-file#colors +- `selectedRowHighlightColor` is the adaptive color scheme used to highlight the currently selected row on a `table` panel. +- `logSearchHighlightColor` is the adaptive color scheme used to highlight search results when searching in a `log` panel. +- `syntaxHighlightDarkColor` is the color theme used for syntax highlighting against a dark terminal background. `buoy` uses https://github.com/alecthomas/chroma for syntax highlighting and thus the same color themes. The available color themes can be found at https://github.com/alecthomas/chroma/tree/master/styles +- `syntaxHighlightLightColor` is the color theme used for syntax highlighting against a light terminal background. diff --git a/test.json b/examples/dashboards/test.json similarity index 100% rename from test.json rename to examples/dashboards/test.json diff --git a/test.yaml b/examples/dashboards/test.yaml similarity index 100% rename from test.yaml rename to examples/dashboards/test.yaml diff --git a/examples/themes/theme.json b/examples/themes/theme.json new file mode 100644 index 0000000..291af3c --- /dev/null +++ b/examples/themes/theme.json @@ -0,0 +1,16 @@ +{ + "tabColor": { + "light": "236", + "dark": "220" + }, + "selectedRowHighlightColor": { + "light": "236", + "dark": "220" + }, + "logSearchHighlightColor": { + "light": "236", + "dark": "220" + }, + "syntaxHighlightDarkColor": "monokai", + "syntaxHighlightLightColor": "monokailight" +} \ No newline at end of file diff --git a/internal/cli/root.go b/internal/cli/root.go index f91764a..fb60820 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -12,6 +12,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/everettraven/buoy/pkg/charm/models" + "github.com/everettraven/buoy/pkg/charm/styles" "github.com/everettraven/buoy/pkg/paneler" "github.com/everettraven/buoy/pkg/types" "github.com/spf13/cobra" @@ -24,15 +25,20 @@ var rootCommand = &cobra.Command{ Short: "declarative kubernetes dashboard in the terminal", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return run(args[0]) + themePath, err := cmd.Flags().GetString("theme") + if err != nil { + return fmt.Errorf("getting theme flag: %w", err) + } + return run(args[0], themePath) }, } func init() { rootCommand.AddCommand(versionCommand) + rootCommand.Flags().String("theme", styles.DefaultThemePath, "path to theme file") } -func run(path string) error { +func run(path string, themePath string) error { var raw []byte var ext string u, err := url.ParseRequestURI(path) @@ -65,8 +71,13 @@ func run(path string) error { log.Fatalf("unmarshalling dashboard: %s", err) } + theme, err := styles.LoadTheme(themePath) + if err != nil { + log.Fatalf("loading theme: %s", err) + } + cfg := config.GetConfigOrDie() - p, err := paneler.NewDefaultPaneler(cfg) + p, err := paneler.NewDefaultPaneler(cfg, theme) if err != nil { log.Fatalf("configuring paneler: %s", err) } @@ -80,7 +91,7 @@ func run(path string) error { panelModels = append(panelModels, mod) } - m := models.NewDashboard(models.DefaultDashboardKeys, panelModels...) + m := models.NewDashboard(models.DefaultDashboardKeys, theme, panelModels...) if _, err := tea.NewProgram(m, tea.WithAltScreen()).Run(); err != nil { fmt.Println("Error running program:", err) os.Exit(1) diff --git a/pkg/charm/models/dashboard.go b/pkg/charm/models/dashboard.go index 3c29185..56df035 100644 --- a/pkg/charm/models/dashboard.go +++ b/pkg/charm/models/dashboard.go @@ -53,9 +53,10 @@ type Dashboard struct { width int help help.Model keys DashboardKeyMap + theme *styles.Theme } -func NewDashboard(keys DashboardKeyMap, panels ...tea.Model) *Dashboard { +func NewDashboard(keys DashboardKeyMap, theme *styles.Theme, panels ...tea.Model) *Dashboard { tabs := []Tab{} for _, panel := range panels { if namer, ok := panel.(Namer); ok { @@ -63,9 +64,10 @@ func NewDashboard(keys DashboardKeyMap, panels ...tea.Model) *Dashboard { } } return &Dashboard{ - tabber: NewTabber(DefaultTabberKeys, tabs...), + tabber: NewTabber(DefaultTabberKeys, theme, tabs...), help: help.New(), keys: keys, + theme: theme, } } @@ -96,7 +98,7 @@ func (d *Dashboard) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } func (d *Dashboard) View() string { - div := styles.TabGap().Render(strings.Repeat(" ", max(0, d.width-2))) + div := d.theme.TabGap().Render(strings.Repeat(" ", max(0, d.width-2))) return lipgloss.JoinVertical(0, d.tabber.View(), div, d.help.View(d.Help())) } diff --git a/pkg/charm/models/panels/logs.go b/pkg/charm/models/panels/logs.go index 762724e..6020885 100644 --- a/pkg/charm/models/panels/logs.go +++ b/pkg/charm/models/panels/logs.go @@ -72,9 +72,10 @@ type Logs struct { mode string keys LogsKeyMap strictSearch bool + theme *styles.Theme } -func NewLogs(keys LogsKeyMap, name string) *Logs { +func NewLogs(keys LogsKeyMap, name string, theme *styles.Theme) *Logs { searchbar := textinput.New() searchbar.Prompt = "> " searchbar.Placeholder = "search term" @@ -87,6 +88,7 @@ func NewLogs(keys LogsKeyMap, name string) *Logs { content: "", mode: modeLogs, keys: keys, + theme: theme, } } @@ -139,7 +141,7 @@ func (m *Logs) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } if m.mode == modeSearched { - m.viewport.SetContent(searchLogs(m.content, m.searchbar.Value(), m.viewport.Width, m.strictSearch)) + m.viewport.SetContent(m.searchLogs()) } m.viewport, cmd = m.viewport.Update(msg) @@ -151,7 +153,7 @@ func (m *Logs) View() string { if m.strictSearch { searchMode = "strict" } - searchModeOutput := styles.LogSearchModeStyle().Render(fmt.Sprintf("search mode: %s", searchMode)) + searchModeOutput := m.theme.LogSearchModeStyle().Render(fmt.Sprintf("search mode: %s", searchMode)) if m.mode == modeSearching { return lipgloss.JoinVertical(lipgloss.Top, @@ -185,19 +187,22 @@ func (m *Logs) Name() string { return m.name } -// searchLogs searches the logs for the given term +// searchLogs searches the logs for the term in the searchbar // and returns a string with the matching log lines // and the matched term highlighted. Uses fuzzy search -// if strict is false. Wraps logs to the given width if wrap > 0. -func searchLogs(logs, term string, wrap int, strict bool) string { - splitLogs := strings.Split(logs, "\n") +// if strict search is not enabled. Wraps logs to the width of the viewport. +func (m *Logs) searchLogs() string { + term := m.searchbar.Value() + wrap := m.viewport.Width + strict := m.strictSearch + splitLogs := strings.Split(m.content, "\n") if strict { - return strictMatchLogs(term, splitLogs, wrap) + return strictMatchLogs(term, splitLogs, m.viewport.Width, m.theme.LogSearchHighlightStyle()) } - return fuzzyMatchLogs(term, splitLogs, wrap) + return fuzzyMatchLogs(term, splitLogs, wrap, m.theme.LogSearchHighlightStyle()) } -func strictMatchLogs(searchTerm string, logLines []string, wrap int) string { +func strictMatchLogs(searchTerm string, logLines []string, wrap int, style lipgloss.Style) string { var results strings.Builder for _, log := range logLines { if wrap > 0 { @@ -207,7 +212,7 @@ func strictMatchLogs(searchTerm string, logLines []string, wrap int) string { highlighted := strings.Replace( log, searchTerm, - styles.LogSearchHighlightStyle().Render(searchTerm), -1, + style.Render(searchTerm), -1, ) results.WriteString(highlighted + "\n") } @@ -215,7 +220,7 @@ func strictMatchLogs(searchTerm string, logLines []string, wrap int) string { return results.String() } -func fuzzyMatchLogs(searchTerm string, logLines []string, wrap int) string { +func fuzzyMatchLogs(searchTerm string, logLines []string, wrap int, style lipgloss.Style) string { var matches []fuzzy.Match if wrap > 0 { wrappedLogs := []string{} @@ -231,7 +236,7 @@ func fuzzyMatchLogs(searchTerm string, logLines []string, wrap int) string { for _, match := range matches { for i := 0; i < len(match.Str); i++ { if matched(i, match.MatchedIndexes) { - results.WriteString(styles.LogSearchHighlightStyle().Render(string(match.Str[i]))) + results.WriteString(style.Render(string(match.Str[i]))) } else { results.WriteString(string(match.Str[i])) } diff --git a/pkg/charm/models/panels/table.go b/pkg/charm/models/panels/table.go index 0989abc..0104d8f 100644 --- a/pkg/charm/models/panels/table.go +++ b/pkg/charm/models/panels/table.go @@ -74,9 +74,10 @@ type Table struct { err error tempRows []tbl.Row keys TableKeyMap + theme *styles.Theme } -func NewTable(keys TableKeyMap, table *buoytypes.Table, lister cache.GenericLister, scope meta.RESTScopeName) *Table { +func NewTable(keys TableKeyMap, table *buoytypes.Table, lister cache.GenericLister, scope meta.RESTScopeName, theme *styles.Theme) *Table { tblColumns := []string{} width := 0 for _, column := range table.Columns { @@ -85,7 +86,7 @@ func NewTable(keys TableKeyMap, table *buoytypes.Table, lister cache.GenericList } tab := tbl.New(tblColumns, 100, 10) - tab.Styles.SelectedRow = styles.TableSelectedRowStyle() + tab.Styles.SelectedRow = theme.TableSelectedRowStyle() return &Table{ table: tab, @@ -98,6 +99,7 @@ func NewTable(keys TableKeyMap, table *buoytypes.Table, lister cache.GenericList rows: map[types.UID]*RowInfo{}, columns: table.Columns, keys: keys, + theme: theme, } } @@ -242,9 +244,9 @@ func (m *Table) FetchContentForIndex(index int) (string, error) { return "", fmt.Errorf("converting JSON to YAML for item %q: %w", name, err) } - theme := "nord" + theme := m.theme.SyntaxHighlightDarkTheme if !lipgloss.HasDarkBackground() { - theme = "monokailight" + theme = m.theme.SyntaxHighlightLightTheme } rw := &bytes.Buffer{} err = quick.Highlight(rw, string(itemYAML), "yaml", "terminal16m", theme) diff --git a/pkg/charm/models/tabber.go b/pkg/charm/models/tabber.go index f486741..cab40f1 100644 --- a/pkg/charm/models/tabber.go +++ b/pkg/charm/models/tabber.go @@ -24,12 +24,14 @@ type Tabber struct { selected int keyMap TabberKeyMap width int + theme *styles.Theme } -func NewTabber(keyMap TabberKeyMap, tabs ...Tab) *Tabber { +func NewTabber(keyMap TabberKeyMap, theme *styles.Theme, tabs ...Tab) *Tabber { return &Tabber{ tabs: tabs, keyMap: keyMap, + theme: theme, } } @@ -71,22 +73,23 @@ func (t *Tabber) Update(msg tea.Msg) (*Tabber, tea.Cmd) { } func (t *Tabber) View() string { - tabRightArrow := styles.TabGap().Render(" ▶ ") - tabLeftArrow := styles.TabGap().Render(" ◀ ") + tabRightArrow := t.theme.TabGap().Render(" ▶ ") + tabLeftArrow := t.theme.TabGap().Render(" ◀ ") pager := &pager{ tabRightArrow: tabRightArrow, tabLeftArrow: tabLeftArrow, pages: []page{}, + theme: t.theme, } pager.setPages(t.tabs, t.selected, t.width) tabBlock := pager.renderForSelectedTab(t.selected) // gap is a repeating of the spaces so that the bottom border continues the entire width // of the terminal. This allows it to look like a proper set of tabs - gap := styles.TabGap().Render(strings.Repeat(" ", max(0, t.width-lipgloss.Width(tabBlock)-2))) + gap := t.theme.TabGap().Render(strings.Repeat(" ", max(0, t.width-lipgloss.Width(tabBlock)-2))) tabsWithBorder := lipgloss.JoinHorizontal(lipgloss.Bottom, tabBlock, gap) - content := styles.ContentStyle().Render(t.tabs[t.selected].Model.View()) + content := t.theme.ContentStyle().Render(t.tabs[t.selected].Model.View()) return lipgloss.JoinVertical(0, tabsWithBorder, content) } @@ -141,6 +144,7 @@ type pager struct { pages []page tabRightArrow string tabLeftArrow string + theme *styles.Theme } func (p *pager) renderForSelectedTab(selected int) string { @@ -164,9 +168,9 @@ func (p *pager) setPages(tabs []Tab, selected int, width int) { tempTab := "" tempPage := page{start: 0, tabs: []string{}} for i, tab := range tabs { - renderedTab := styles.TabStyle().Render(tab.Name) + renderedTab := p.theme.TabStyle().Render(tab.Name) if i == selected { - renderedTab = styles.SelectedTabStyle().Render(tab.Name) + renderedTab = p.theme.SelectedTabStyle().Render(tab.Name) } tempTab = lipgloss.JoinHorizontal(lipgloss.Top, tempTab, renderedTab) joined := lipgloss.JoinHorizontal(lipgloss.Bottom, p.tabLeftArrow, tempTab, p.tabRightArrow) diff --git a/pkg/charm/styles/styles.go b/pkg/charm/styles/styles.go index 0ec5bed..56b72c8 100644 --- a/pkg/charm/styles/styles.go +++ b/pkg/charm/styles/styles.go @@ -1,41 +1,94 @@ package styles -import "github.com/charmbracelet/lipgloss" +import ( + "encoding/json" + "fmt" + "os" + + "github.com/charmbracelet/lipgloss" +) + +// Theme is a collection of adaptive colors to be used when rendering the UI. +// This is the basis for customizable themes +type Theme struct { + // TabColor is the color used to render the tabs and associated borders + TabColor lipgloss.AdaptiveColor + // SelectedRowHighlightColor is the color used to highlight the selected row in the table + SelectedRowHighlightColor lipgloss.AdaptiveColor + // LogSearchHighlightColor is the color used to highlight the search term in the log view + LogSearchHighlightColor lipgloss.AdaptiveColor + // SyntaxHighlightDarkTheme is the name of the syntax highlighting theme to use when the + // terminal has a dark background. Available themes can be found at + // https://github.com/alecthomas/chroma/tree/master/styles + SyntaxHighlightDarkTheme string + // SyntaxHighlightLightTheme is the name of the syntax highlighting theme to use when the + // terminal has a light background. Available themes can be found at + // https://github.com/alecthomas/chroma/tree/master/styles + SyntaxHighlightLightTheme string +} + +const DefaultThemePath = "~/.config/buoy/themes/default.json" var DefaultColor = lipgloss.AdaptiveColor{Light: "63", Dark: "117"} -func SelectedTabStyle() lipgloss.Style { +func LoadTheme(themePath string) (*Theme, error) { + t := &Theme{ + TabColor: DefaultColor, + SelectedRowHighlightColor: DefaultColor, + LogSearchHighlightColor: DefaultColor, + SyntaxHighlightDarkTheme: "nord", + SyntaxHighlightLightTheme: "monokailight", + } + // If the specified theme file doesn't exist, use the default theme + if _, err := os.Stat(themePath); err != nil { + return t, nil + } + + raw, err := os.ReadFile(themePath) + if err != nil { + return t, fmt.Errorf("reading theme file: %w", err) + } + + err = json.Unmarshal(raw, t) + if err != nil { + return t, fmt.Errorf("unmarshalling theme: %w", err) + } + + return t, nil +} + +func (t *Theme) SelectedTabStyle() lipgloss.Style { border := lipgloss.RoundedBorder() border.BottomLeft = "┘" border.Bottom = " " border.BottomRight = "└" - return lipgloss.NewStyle().Bold(true).Align(lipgloss.Center).Border(border).BorderForeground(DefaultColor).Padding(0, 1) + return lipgloss.NewStyle().Bold(true).Align(lipgloss.Center).Border(border).BorderForeground(t.TabColor).Padding(0, 1) } -func TabStyle() lipgloss.Style { +func (t *Theme) TabStyle() lipgloss.Style { border := lipgloss.RoundedBorder() border.BottomLeft = "┴" border.BottomRight = "┴" - return lipgloss.NewStyle().Bold(true).Align(lipgloss.Center).Border(border).BorderForeground(DefaultColor).Padding(0, 1) + return lipgloss.NewStyle().Bold(true).Align(lipgloss.Center).Border(border).BorderForeground(t.TabColor).Padding(0, 1) } -func TabGap() lipgloss.Style { +func (t *Theme) TabGap() lipgloss.Style { border := lipgloss.RoundedBorder() - return lipgloss.NewStyle().Bold(true).Align(lipgloss.Center).Border(border, false, false, true, false).BorderForeground(DefaultColor).Padding(0, 1) + return lipgloss.NewStyle().Bold(true).Align(lipgloss.Center).Border(border, false, false, true, false).BorderForeground(t.TabColor).Padding(0, 1) } -func ContentStyle() lipgloss.Style { +func (t *Theme) ContentStyle() lipgloss.Style { return lipgloss.NewStyle().Align(lipgloss.Center, lipgloss.Center) } -func TableSelectedRowStyle() lipgloss.Style { - return lipgloss.NewStyle().Foreground(DefaultColor) +func (t *Theme) TableSelectedRowStyle() lipgloss.Style { + return lipgloss.NewStyle().Foreground(t.SelectedRowHighlightColor) } -func LogSearchHighlightStyle() lipgloss.Style { - return lipgloss.NewStyle().Foreground(DefaultColor) +func (t *Theme) LogSearchHighlightStyle() lipgloss.Style { + return lipgloss.NewStyle().Foreground(t.LogSearchHighlightColor) } -func LogSearchModeStyle() lipgloss.Style { +func (t *Theme) LogSearchModeStyle() lipgloss.Style { return lipgloss.NewStyle().Italic(true).Faint(true) } diff --git a/pkg/paneler/item.go b/pkg/paneler/item.go index cf3cdb6..b38db7f 100644 --- a/pkg/paneler/item.go +++ b/pkg/paneler/item.go @@ -12,6 +12,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/everettraven/buoy/pkg/charm/models/panels" + "github.com/everettraven/buoy/pkg/charm/styles" "github.com/everettraven/buoy/pkg/types" "k8s.io/apimachinery/pkg/api/meta" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -30,13 +31,15 @@ type Item struct { dynamicClient dynamic.Interface discoveryClient *discovery.DiscoveryClient restMapper meta.RESTMapper + theme *styles.Theme } -func NewItem(dynamicClient dynamic.Interface, discoveryClient *discovery.DiscoveryClient, restMapper meta.RESTMapper) *Item { +func NewItem(dynamicClient dynamic.Interface, discoveryClient *discovery.DiscoveryClient, restMapper meta.RESTMapper, theme *styles.Theme) *Item { return &Item{ dynamicClient: dynamicClient, discoveryClient: discoveryClient, restMapper: restMapper, + theme: theme, } } @@ -56,9 +59,9 @@ func (t *Item) modelWrapperForItemPanel(itemPanel types.Item) *panels.Item { } func (t *Item) runInformerForItem(item types.Item, panel *panels.Item) error { - theme := "nord" + theme := t.theme.SyntaxHighlightDarkTheme if !lipgloss.HasDarkBackground() { - theme = "monokailight" + theme = t.theme.SyntaxHighlightLightTheme } // create informer and event handler infFact := dynamicinformer.NewFilteredDynamicSharedInformerFactory(t.dynamicClient, 1*time.Minute, item.Key.Namespace, func(lo *v1.ListOptions) { diff --git a/pkg/paneler/logs.go b/pkg/paneler/logs.go index bab017c..b35d6d7 100644 --- a/pkg/paneler/logs.go +++ b/pkg/paneler/logs.go @@ -9,6 +9,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/everettraven/buoy/pkg/charm/models/panels" + "github.com/everettraven/buoy/pkg/charm/styles" "github.com/everettraven/buoy/pkg/types" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" @@ -28,14 +29,16 @@ type Log struct { dynamicClient dynamic.Interface discoveryClient *discovery.DiscoveryClient restMapper meta.RESTMapper + theme *styles.Theme } -func NewLog(typedClient *kubernetes.Clientset, dynamicClient dynamic.Interface, discoveryClient *discovery.DiscoveryClient, restMapper meta.RESTMapper) *Log { +func NewLog(typedClient *kubernetes.Clientset, dynamicClient dynamic.Interface, discoveryClient *discovery.DiscoveryClient, restMapper meta.RESTMapper, theme *styles.Theme) *Log { return &Log{ typedClient: typedClient, dynamicClient: dynamicClient, discoveryClient: discoveryClient, restMapper: restMapper, + theme: theme, } } @@ -45,7 +48,7 @@ func (t *Log) Model(panel types.Panel) (tea.Model, error) { if err != nil { return nil, fmt.Errorf("unmarshalling panel to table type: %s", err) } - logPanel := panels.NewLogs(panels.DefaultLogsKeys, log.Name) + logPanel := panels.NewLogs(panels.DefaultLogsKeys, log.Name, t.theme) pod, err := t.getPodForObject(log) if err != nil { return nil, fmt.Errorf("error getting pod for object: %w", err) diff --git a/pkg/paneler/paneler.go b/pkg/paneler/paneler.go index a58b825..e438e1c 100644 --- a/pkg/paneler/paneler.go +++ b/pkg/paneler/paneler.go @@ -4,6 +4,7 @@ import ( "fmt" tea "github.com/charmbracelet/bubbletea" + "github.com/everettraven/buoy/pkg/charm/styles" "github.com/everettraven/buoy/pkg/types" "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" @@ -27,7 +28,7 @@ func (p *paneler) Model(panel types.Panel) (tea.Model, error) { return nil, fmt.Errorf("panel %q has unknown panel type: %q", panel.Name, panel.Type) } -func NewDefaultPaneler(cfg *rest.Config) (Paneler, error) { +func NewDefaultPaneler(cfg *rest.Config, theme *styles.Theme) (Paneler, error) { dClient, err := dynamic.NewForConfig(cfg) if err != nil { return nil, fmt.Errorf("error creating dynamic client: %w", err) @@ -51,9 +52,9 @@ func NewDefaultPaneler(cfg *rest.Config) (Paneler, error) { return &paneler{ panelerRegistry: map[string]Paneler{ - types.PanelTypeTable: NewTable(dClient, di, rm), - types.PanelTypeItem: NewItem(dClient, di, rm), - types.PanelTypeLogs: NewLog(kubeClient, dClient, di, rm), + types.PanelTypeTable: NewTable(dClient, di, rm, theme), + types.PanelTypeItem: NewItem(dClient, di, rm, theme), + types.PanelTypeLogs: NewLog(kubeClient, dClient, di, rm, theme), }, }, nil } diff --git a/pkg/paneler/table.go b/pkg/paneler/table.go index f1731ae..d0a6824 100644 --- a/pkg/paneler/table.go +++ b/pkg/paneler/table.go @@ -7,6 +7,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/everettraven/buoy/pkg/charm/models/panels" + "github.com/everettraven/buoy/pkg/charm/styles" buoytypes "github.com/everettraven/buoy/pkg/types" "k8s.io/apimachinery/pkg/api/meta" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -26,13 +27,15 @@ type Table struct { dynamicClient dynamic.Interface discoveryClient *discovery.DiscoveryClient restMapper meta.RESTMapper + theme *styles.Theme } -func NewTable(dynamicClient dynamic.Interface, discoveryClient *discovery.DiscoveryClient, restMapper meta.RESTMapper) *Table { +func NewTable(dynamicClient dynamic.Interface, discoveryClient *discovery.DiscoveryClient, restMapper meta.RESTMapper, theme *styles.Theme) *Table { return &Table{ dynamicClient: dynamicClient, discoveryClient: discoveryClient, restMapper: restMapper, + theme: theme, } } @@ -55,7 +58,7 @@ func (t *Table) modelForTablePanel(tablePanel *buoytypes.Table) (*panels.Table, if err != nil { return nil, nil, fmt.Errorf("creating informer for table: %w", err) } - table := panels.NewTable(panels.DefaultTableKeys, tablePanel, inf.Lister(), scope) + table := panels.NewTable(panels.DefaultTableKeys, tablePanel, inf.Lister(), scope, t.theme) _, err = setEventHandlerForTableInformer(inf, table) if err != nil { return nil, nil, fmt.Errorf("setting event handler for table informer: %w", err)