feat(tui): implement dark mode with runtime /theme switching

- Paint dark background at app shell level via appStyle() helper
- Add /theme slash command for runtime light/dark switching
- Add comprehensive dark-mode style tests
- Document --theme dark in README Quick Start
This commit is contained in:
loveuer
2026-06-24 20:45:06 -07:00
parent 3d403bd685
commit 9abd5c3b5f
4 changed files with 158 additions and 4 deletions
+34 -2
View File
@@ -145,7 +145,7 @@ var slashCommands = []slashCommand{
{Name: "/new", Description: "new session"},
{Name: "/status", Description: "show changed files"},
{Name: "/diff", Description: "show unstaged diff"},
{Name: "/test", Description: "run project tests"},
{Name: "/theme", Description: "switch theme light|dark"},
}
func newModel(ctx context.Context, assistant *agent.Agent, opts Options) model {
@@ -389,7 +389,7 @@ func (m *model) submit() (tea.Model, tea.Cmd) {
m.status = "ready"
m.refreshViewport(true)
return *m, nil
case "/model", "/compact":
case "/model", "/compact", "/theme":
return m.runLocalCommand(input)
}
@@ -1090,6 +1090,9 @@ func fitLine(text string, width int) string {
}
func (m *model) runLocalCommand(input string) (tea.Model, tea.Cmd) {
if strings.HasPrefix(input, "/theme") {
return m.handleThemeCommand(input)
}
output, err := m.handleLocalCommand(input)
if err == errOpenPicker {
m.openSessionPicker()
@@ -1114,6 +1117,35 @@ func (m *model) runLocalCommand(input string) (tea.Model, tea.Cmd) {
return *m, nil
}
func (m *model) handleThemeCommand(input string) (tea.Model, tea.Cmd) {
fields := strings.Fields(input)
if len(fields) < 2 {
m.input.SetValue("")
m.afterInputChanged()
m.messages = append(m.messages, message{
role: roleSystem, content: "theme: " + string(m.theme.Mode) + "\nusage: /theme light|dark",
})
m.refreshViewport(true)
return *m, nil
}
mode, err := ParseThemeMode(fields[1])
if err != nil {
m.input.SetValue("")
m.afterInputChanged()
m.messages = append(m.messages, message{role: roleError, content: err.Error()})
m.refreshViewport(true)
return *m, nil
}
m.theme = themeForMode(mode)
m.styles = m.theme.styles()
applyTextareaTheme(&m.input, m.theme)
m.input.SetValue("")
m.afterInputChanged()
m.messages = append(m.messages, message{role: roleSystem, content: "theme: " + string(mode)})
m.refreshViewport(true)
return *m, nil
}
func (m model) handleLocalCommand(input string) (string, error) {
fields := strings.Fields(input)
if len(fields) == 0 {