summaryrefslogtreecommitdiffhomepage
path: root/packages/tui/internal/components/dialog/theme.go
blob: 50472428b4f8590a6ee197bd6e248cd6865049ab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package dialog

import (
	tea "github.com/charmbracelet/bubbletea/v2"
	list "github.com/sst/opencode/internal/components/list"
	"github.com/sst/opencode/internal/components/modal"
	"github.com/sst/opencode/internal/layout"
	"github.com/sst/opencode/internal/styles"
	"github.com/sst/opencode/internal/theme"
	"github.com/sst/opencode/internal/util"
)

// ThemeChangedMsg is sent when the theme is changed
type ThemeChangedMsg struct {
	ThemeName string
}

// ThemeDialog interface for the theme switching dialog
type ThemeDialog interface {
	layout.Modal
}

type themeItem struct {
	name string
}

func (t themeItem) Render(selected bool, width int) string {
	th := theme.CurrentTheme()
	baseStyle := styles.BaseStyle().
		Width(width - 2).
		Background(th.BackgroundElement())

	if selected {
		baseStyle = baseStyle.
			Background(th.Primary()).
			Foreground(th.BackgroundElement()).
			Bold(true)
	} else {
		baseStyle = baseStyle.
			Foreground(th.Text())
	}

	return baseStyle.Padding(0, 1).Render(t.name)
}

type themeDialog struct {
	width  int
	height int

	modal *modal.Modal
	list  list.List[themeItem]
}

func (t *themeDialog) Init() tea.Cmd {
	return nil
}

func (t *themeDialog) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.WindowSizeMsg:
		t.width = msg.Width
		t.height = msg.Height
	case tea.KeyMsg:
		switch msg.String() {
		case "enter":
			if item, idx := t.list.GetSelectedItem(); idx >= 0 {
				previousTheme := theme.CurrentThemeName()
				selectedTheme := item.name
				if previousTheme == selectedTheme {
					return t, util.CmdHandler(modal.CloseModalMsg{})
				}
				if err := theme.SetTheme(selectedTheme); err != nil {
					// status.Error(err.Error())
					return t, nil
				}
				return t, tea.Sequence(
					util.CmdHandler(modal.CloseModalMsg{}),
					util.CmdHandler(ThemeChangedMsg{ThemeName: selectedTheme}),
				)
			}
		}
	}

	var cmd tea.Cmd
	listModel, cmd := t.list.Update(msg)
	t.list = listModel.(list.List[themeItem])
	return t, cmd
}

func (t *themeDialog) Render(background string) string {
	return t.modal.Render(t.list.View(), background)
}

func (t *themeDialog) Close() tea.Cmd {
	return nil
}

// NewThemeDialog creates a new theme switching dialog
func NewThemeDialog() ThemeDialog {
	themes := theme.AvailableThemes()
	currentTheme := theme.CurrentThemeName()

	var themeItems []themeItem
	var selectedIdx int
	for i, name := range themes {
		themeItems = append(themeItems, themeItem{name: name})
		if name == currentTheme {
			selectedIdx = i
		}
	}

	list := list.NewListComponent(
		themeItems,
		10, // maxVisibleThemes
		"No themes available",
		true,
	)

	// Set the initial selection to the current theme
	list.SetSelectedIndex(selectedIdx)

	return &themeDialog{
		list:  list,
		modal: modal.New(modal.WithTitle("Select Theme"), modal.WithMaxWidth(40)),
	}
}