package ui import ( "os" "os/exec" "path/filepath" tea "github.com/charmbracelet/bubbletea" "motd-assist/internal/motd" "motd-assist/internal/preset" ) type discoverMsg struct { scripts []motd.Script host motd.HostInfo err error } type presetsMsg struct { presets []preset.Preset err error } type previewMsg struct { version int text string } type applyDoneMsg struct{ err error } func discoverCmd(root string) tea.Cmd { return func() tea.Msg { scripts, err := motd.Discover(root) return discoverMsg{scripts: scripts, host: motd.DetectHost(root), err: err} } } func presetsCmd() tea.Cmd { return func() tea.Msg { presets, err := preset.LoadAll() return presetsMsg{presets: presets, err: err} } } // previewCmd rendert die Arbeitskopie als Live-Vorschau (asynchron, damit // langsame Scripte die TUI nicht blockieren). func (m *Model) previewCmd() tea.Cmd { m.previewBusy = true m.previewVersion++ version := m.previewVersion width := m.previewWidth() scripts := make([]motd.PreviewScript, 0, len(m.working)) for _, r := range m.working { if r.Remove { continue } ps := motd.PreviewScript{Name: r.Name, Enabled: r.Enabled} if r.Exists { ps.Path = filepath.Join(motd.MotdDir(m.root), r.Name) } else { ps.Source = r.Install } scripts = append(scripts, ps) } return func() tea.Msg { return previewMsg{version: version, text: motd.Render(scripts, width)} } } // previewWidth ist die nutzbare Breite innerhalb der Preview-Umrandung. func (m *Model) previewWidth() int { w := m.width - 4 // Rahmen (2) + Padding (2) if w < 40 { w = 40 } if w > 100 { w = 100 } return w } // applyExec handelt das Terminal an sudo weiter (interaktive // Passwortabfrage) und startet danach die TUI weiter. func applyExec(plan motd.Plan) tea.Cmd { planFile, err := plan.WriteFile() if err != nil { return func() tea.Msg { return applyDoneMsg{err: err} } } self, err := os.Executable() if err != nil { os.Remove(planFile) return func() tea.Msg { return applyDoneMsg{err: err} } } var cmd *exec.Cmd if motd.IsRoot() { cmd = exec.Command(self, "apply-state", planFile) } else { sudo, err := exec.LookPath("sudo") if err != nil { os.Remove(planFile) return func() tea.Msg { return applyDoneMsg{err: err} } } cmd = exec.Command(sudo, "-p", "[motd-assist] Passwort für %p: ", self, "apply-state", planFile) } return tea.ExecProcess(cmd, func(err error) tea.Msg { os.Remove(planFile) return applyDoneMsg{err: err} }) }