session_ops_dialog.go RawHistoryFolder
1package modes
2
3import (
4 "github.com/bnema/zut/packages/tui"
5)
6
7// sessionOpsDialog is the picker shown when the user runs `/session`
8// without an argument. Offers the two portable-file operations on
9// the current conversation: export (write the in-memory transcript
10// plus meta to a .zutsession file) and import (load a .zutsession
11// from another machine and swap it in as the active session).
12//
13// Shape mirrors telegramDialog and logoutDialog: tiny list, arrow
14// keys to move, enter to pick, esc to cancel.
15type sessionOpsDialog struct {
16 active bool
17 items []sessionOpsItem
18 cursor int
19}
20
21type sessionOpsItem struct {
22 label string
23 action string // "export" | "import"
24 hint string
25}
26
27type sessionOpsAction struct {
28 Select bool
29 Action string
30 Close bool
31}
32
33func newSessionOpsDialog() *sessionOpsDialog { return &sessionOpsDialog{} }
34
35// Open shows the picker. Items are usually both "export" and
36// "import" but the caller can suppress either (e.g. hide export
37// when the session is empty).
38func (d *sessionOpsDialog) Open(items []sessionOpsItem) bool {
39 if len(items) == 0 {
40 return false
41 }
42 d.items = items
43 d.cursor = 0
44 d.active = true
45 return true
46}
47
48// Close hides the dialog.
49func (d *sessionOpsDialog) Close() { d.active = false }
50
51// Active reports whether the dialog is consuming input.
52func (d *sessionOpsDialog) Active() bool { return d != nil && d.active }
53
54// HandleKey advances the selection or resolves the dialog.
55func (d *sessionOpsDialog) HandleKey(k tui.Key) sessionOpsAction {
56 switch k.Kind {
57 case tui.KeyUp:
58 if d.cursor > 0 {
59 d.cursor--
60 }
61 case tui.KeyDown:
62 if d.cursor < len(d.items)-1 {
63 d.cursor++
64 }
65 case tui.KeyEsc:
66 d.Close()
67 return sessionOpsAction{Close: true}
68 case tui.KeyEnter:
69 if len(d.items) == 0 {
70 d.Close()
71 return sessionOpsAction{Close: true}
72 }
73 it := d.items[d.cursor]
74 d.Close()
75 return sessionOpsAction{Select: true, Action: it.action}
76 }
77 return sessionOpsAction{}
78}
79
80// Render returns the dialog lines.
81func (d *sessionOpsDialog) Render(th tui.Theme, width int) []string {
82 if !d.Active() {
83 return nil
84 }
85 var lines []string
86 lines = append(lines, frameHeader(th, "session", width))
87 lines = append(lines, th.FGColor(th.Muted, "pick an action (\u2191/\u2193, enter, esc to cancel):"))
88 for i, it := range d.items {
89 text := " " + it.label
90 if it.hint != "" {
91 text += " (" + it.hint + ")"
92 }
93 if i == d.cursor {
94 lines = append(lines, th.PadHighlight(text, width))
95 } else {
96 lines = append(lines, th.FGColor(th.Muted, text))
97 }
98 }
99 lines = append(lines, frameRule(th, width))
100 return lines
101}