feat(theme): token semantik Catppuccin (Mocha/Latte/terminal) + mode auto via tea.BackgroundColorMsg + tombol T
CI / test (push) Successful in 11s

This commit is contained in:
oxpoe committed 2026-10-15 13:32:55 +07:00
1 parent c65d2cb75d
commit 8b2a86877e
13 files changed
+428 -37

No files matched your search

+38 -22
View File
@@ -17,6 +17,7 @@ import (
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/sysinfo"
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/ui/animation"
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/ui/components"
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/ui/theme"
)
type viewKind int
@@ -72,6 +73,7 @@ func New() Model {
cfg.UI.Language = l
}
i18n.SetLang(cfg.UI.Language)
theme.Init(theme.Mode(cfg.UI.ThemeMode), cfg.UI.ThemeAccent)
cl, err := docker.New()
host, _ := os.Hostname()
@@ -91,7 +93,7 @@ func New() Model {
loading: true,
}
if cfgErr != nil {
m.toast.Show(i18n.T("toast.configError"), cfgErr.Error())
m.toast.Show(components.LevelWarning, i18n.T("toast.configError"), cfgErr.Error())
}
if err != nil {
m.loadErr = err
@@ -102,7 +104,7 @@ func New() Model {
}
func (m Model) Init() tea.Cmd {
var cmds []tea.Cmd
cmds := []tea.Cmd{tea.RequestBackgroundColor} // untuk mode tema "auto"
if m.animating {
cmds = append(cmds, animation.Next(m.tickID))
}
@@ -154,7 +156,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.loading = false
if msg.err != nil {
m.loadErr = msg.err
m.toast.Show(i18n.T("toast.dockerError"), msg.err.Error())
m.toast.Show(components.LevelError, i18n.T("toast.dockerError"), msg.err.Error())
m.sound.Play(audio.NotifyError)
} else {
m.loadErr = nil
@@ -175,6 +177,11 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
return m, nil
case tea.BackgroundColorMsg:
// Mode tema "auto": Mocha vs Latte sesuai background terminal.
theme.SetDetectedDark(msg.IsDark())
return m, nil
case tea.WindowSizeMsg:
m.width, m.height = msg.Width, msg.Height
@@ -209,8 +216,16 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.loading = true
m.sound.Play(audio.Click)
return m, tea.Batch(loadContainers(m.cl), loadInfo(m.cl))
case "T": // tombol cepat tema (PRD §5.4): auto → dark → light → terminal
cur := theme.Current()
next := theme.CycleMode(cur.Mode)
theme.Set(next, cur.AccentName)
m.toast.Show(components.LevelInfo,
i18n.Tf("toast.themeMode", i18n.T("theme."+string(next))),
i18n.T("theme.switchHint"))
m.sound.Play(audio.Click)
case "t": // demo toast (sampai modal asli datang)
m.toast.Show(i18n.T("toast.demoTitle"), i18n.T("toast.demoBody"))
m.toast.Show(components.LevelSuccess, i18n.T("toast.demoTitle"), i18n.T("toast.demoBody"))
m.sound.Play(audio.NotifySuccess)
case "j", "down":
if len(m.containers) > 0 && m.cursor < len(m.containers)-1 {
@@ -285,37 +300,38 @@ func (m Model) scrollStart() int {
return m.cursor - rows + 1
}
func stateStyle(state string) (glyph, color string) {
func stateStyle(state string) (glyph string, c theme.Color) {
th := theme.Current()
switch state {
case "running":
return "●", "#a6e3a1"
return "●", th.Success
case "paused":
return "⏸", "#f9e2af"
return "⏸", th.Warning
case "restarting", "created":
return "◌", "#89b4fa"
return "◌", th.Info
case "exited", "dead":
return "○", "#f38ba8"
return "○", th.Danger
default:
return "○", "#6c7086"
return "○", th.Overlay
}
}
// --- styling helpers ---
// --- styling helpers (semua via token tema — F-asi §5.4) ---
func dim(s string) string {
return lipgloss.NewStyle().Foreground(lipgloss.Color("#6c7086")).Render(s)
return theme.Style(theme.Current().Subtext).Render(s)
}
func text(s string) string {
return lipgloss.NewStyle().Foreground(lipgloss.Color("#cdd6f4")).Render(s)
return theme.Style(theme.Current().Text).Render(s)
}
func red(s string) string {
return lipgloss.NewStyle().Foreground(lipgloss.Color("#f38ba8")).Render(s)
return theme.Style(theme.Current().Danger).Render(s)
}
func styled(color, s string) string {
return lipgloss.NewStyle().Foreground(lipgloss.Color(color)).Render(s)
func styled(c theme.Color, s string) string {
return theme.Style(c).Render(s)
}
func truncate(s string, w int) string {
@@ -339,11 +355,12 @@ func panel(title, meta string, lines []string, w, h int, focused bool) string {
if w < 6 || h < 2 {
return ""
}
bc := "#6c7086"
th := theme.Current()
bc := th.Overlay
if focused {
bc = "#cba6f7"
bc = th.Accent
}
border := lipgloss.NewStyle().Foreground(lipgloss.Color(bc))
border := lipgloss.NewStyle().Foreground(lipgloss.Color(string(bc)))
titleS := text(title)
left := border.Render("╭─ ") + titleS
@@ -382,8 +399,7 @@ func panel(title, meta string, lines []string, w, h int, focused bool) string {
// --- views ---
func (m Model) View() tea.View {
header := lipgloss.NewStyle().Bold(true).
Foreground(lipgloss.Color("#cba6f7")).
header := theme.Style(theme.Current().Accent).Bold(true).
Render("tia — Terminal Is A My Way")
var body, footer string
@@ -442,7 +458,7 @@ func (m Model) sidebarView() string {
glyph, color := stateStyle(c.State)
mark := " "
if i == m.cursor {
mark = styled("#cba6f7", "▶ ")
mark = styled(theme.Current().Accent, "▶ ")
}
lines = append(lines, mark+styled(color, glyph)+" "+text(truncate(c.Name, innerW-4)))
}