feat(stacks): seksi Stacks di workspace (urutan PRD 1-5) — scan disk + rekonsiliasi compose ls
CI / test (push) Successful in 24s

This commit is contained in:
oxpoe committed 2026-10-16 01:50:16 +07:00
1 parent 8a3b92b155
commit f57d1cc29c
5 files changed
+199 -27

No files matched your search

+66
View File
@@ -0,0 +1,66 @@
package app
import (
"context"
"time"
tea "charm.land/bubbletea/v2"
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/docker"
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/i18n"
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/stack"
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/ui/theme"
)
// stacksMsg — hasil scan stack dari disk + rekonsiliasi compose ls.
type stacksMsg struct {
id int64
items []stack.Stack
err error
}
// fetchStacks — scan folder stack (synch cepat) lalu overlay status dari
// `compose ls`. Compose gagal → status disk tetap dipakai (degrade anggun).
func fetchStacks(id int64, cm *docker.ComposeClient, dir string) tea.Cmd {
return func() tea.Msg {
items, err := stack.ScanFS(dir)
if err != nil {
return stacksMsg{id: id, err: err}
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if projects, err := cm.List(ctx); err == nil {
items = stack.Reconcile(items, projects)
}
return stacksMsg{id: id, items: items}
}
}
// stackStyle — glyph + warna per status stack.
func stackStyle(st stack.Status) (string, theme.Color) {
th := theme.Current()
switch st {
case stack.StatusRunning:
return "●", th.Success
case stack.StatusExited:
return "○", th.Danger
case stack.StatusCreatedFile, stack.StatusCreatedStack:
return "◌", th.Info
default:
return "○", th.Overlay
}
}
// stackStatusText — label terjemahan status stack.
func stackStatusText(st stack.Status) string {
switch st {
case stack.StatusRunning:
return i18n.T("state.running")
case stack.StatusExited:
return i18n.T("state.exited")
case stack.StatusCreatedStack:
return i18n.T("state.created")
default:
return i18n.T("state.detected")
}
}