1package modes
2
3import (
4 "context"
5 "fmt"
6 "strings"
7 "time"
8
9 "github.com/bnema/zut/packages/agent/extensions"
10 "github.com/bnema/zut/packages/tui"
11)
12
13const reloadStatusDuration = 5 * time.Second
14
15func formatReloadStatus(stats extensions.ReloadStats) (string, bool) {
16 msg := fmt.Sprintf("reloaded: %d stopped, %d loaded (%d ready)", stats.Stopped, stats.Loaded, stats.Ready)
17 if len(stats.Errors) == 0 {
18 return msg, false
19 }
20 details := make([]string, 0, len(stats.Errors))
21 for _, err := range stats.Errors {
22 if err != nil {
23 details = append(details, err.Error())
24 }
25 }
26 msg += fmt.Sprintf(", %d error(s)", len(stats.Errors))
27 if len(details) > 0 {
28 msg += ": " + strings.Join(details, "; ")
29 }
30 return msg, true
31}
32
33func (i *Interactive) setReloadStatus(msg string, failed bool) uint64 {
34 i.mu.Lock()
35 defer i.mu.Unlock()
36 i.reloadStatusSeq++
37 if failed {
38 i.statusOK = ""
39 i.statusErr = msg
40 i.reloadErrors = append(i.reloadErrors, msg)
41 return i.reloadStatusSeq
42 }
43 i.statusOK = msg
44 i.statusErr = ""
45 i.reloadErrors = nil
46 return i.reloadStatusSeq
47}
48
49func renderReloadErrors(theme tui.Theme, errors []string, width int) []string {
50 const marker = "✖ "
51 const indent = " "
52 wrapWidth := width - len([]rune(marker))
53 if wrapWidth < 8 {
54 wrapWidth = 8
55 }
56 var out []string
57 for _, msg := range errors {
58 wrapped := tui.WrapANSILine(msg, wrapWidth)
59 if len(wrapped) == 0 {
60 wrapped = []string{""}
61 }
62 for idx, line := range wrapped {
63 prefix := marker
64 if idx > 0 {
65 prefix = indent
66 }
67 out = append(out, theme.FGColor(theme.Error, prefix+line))
68 }
69 }
70 return out
71}
72
73func (i *Interactive) clearReloadStatus(seq uint64, msg string, failed bool) bool {
74 i.mu.Lock()
75 defer i.mu.Unlock()
76 if i.reloadStatusSeq != seq {
77 return false
78 }
79 if failed {
80 if i.statusErr != msg {
81 return false
82 }
83 i.statusErr = ""
84 return true
85 }
86 if i.statusOK != msg {
87 return false
88 }
89 i.statusOK = ""
90 return true
91}
92
93func (i *Interactive) dismissReloadStatus(ctx context.Context, seq uint64, msg string, failed bool) {
94 timer := time.NewTimer(reloadStatusDuration)
95 defer timer.Stop()
96 select {
97 case <-ctx.Done():
98 return
99 case <-timer.C:
100 if i.clearReloadStatus(seq, msg, failed) {
101 i.invalidate()
102 }
103 }
104}