okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

Aly Raffauffeat(api)!: migrate daemon and clients to protocol 361c43ca

main
2.6 KiB92 linesraw
1package tui
2
3import (
4	"strings"
5
6	"albedo/cli/internal/daemon"
7
8	"charm.land/bubbles/v2/textinput"
9	tea "charm.land/bubbletea/v2"
10)
11
12// SessionRenameMsg asks the app to save a session's name. A blank name hands
13// the title back to the daemon, which takes it from the latest message.
14type SessionRenameMsg struct{ ID, Name, ETag string }
15
16// sessionRenamedMsg is the daemon's answer to a SessionRenameMsg: the
17// session as it is now listed.
18type sessionRenamedMsg struct {
19	Err     error
20	Session daemon.Session
21	SessionRenameMsg
22}
23
24// renameField edits one session's name in place. While it is open its owner
25// hands it every key, so typing never reaches search or navigation.
26type renameField struct {
27	id, was, etag string
28	input         textinput.Model
29	// selected holds until the first key: typing replaces the opening
30	// draft, which a message title can fill to the limit; other keys edit it.
31	selected bool
32}
33
34// renameLimit matches the daemon, which keeps 80 characters of a name.
35const renameLimit = 80
36
37// open starts a draft of current, prompting with placeholder once emptied.
38func (f *renameField) open(id, current, placeholder string) {
39	in := newField()
40	in.CharLimit = renameLimit
41	in.Placeholder = placeholder
42	st := in.Styles()
43	st.Focused.Text = DefaultStyles.Bold
44	st.Focused.Placeholder = DefaultStyles.Faint
45	in.SetStyles(st)
46	in.SetValue(current)
47	in.CursorEnd()
48	in.Focus()
49	f.id, f.was, f.input, f.selected = id, current, in, current != ""
50}
51
52func (f renameField) active() bool { return f.id != "" }
53
54// key edits the draft. Enter saves a changed name and esc drops it; both
55// close the field.
56func (f *renameField) key(msg tea.KeyPressMsg) tea.Cmd {
57	switch msg.String() {
58	case "esc", "ctrl+c":
59		f.id = ""
60		return nil
61	case "enter":
62		id, name := f.id, strings.TrimSpace(f.input.Value())
63		f.id = ""
64		if name == strings.TrimSpace(f.was) {
65			return nil
66		}
67		return func() tea.Msg { return SessionRenameMsg{ID: id, Name: name, ETag: f.etag} }
68	}
69	if f.selected && msg.Text != "" {
70		f.input.SetValue("")
71	}
72	f.selected = false
73	var cmd tea.Cmd
74	f.input, cmd = f.input.Update(msg)
75	return cmd
76}
77
78// view is the draft in exactly width cells.
79func (f renameField) view(width int) string {
80	f.input.SetWidth(max(1, width-1))
81	if f.selected {
82		st := f.input.Styles()
83		st.Focused.Text = DefaultStyles.Cursor
84		f.input.SetStyles(st)
85	}
86	return svFit(f.input.View(), width)
87}
88
89// renameHints stand in for a screen's key hints while a name is being edited.
90func renameHints(blank string) string {
91	return keyHints(hint{"enter", "save"}, hint{"esc", "cancel"}, hint{"", "empty " + blank})
92}