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