okami.mom/albedo

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

dawntui: tree and context inspector on listView, shared confirm for agent delete78011da

main
4.6 KiB123 linesraw
1package tui
2
3import (
4	"albedo/cli/internal/daemon"
5	"errors"
6
7	tea "charm.land/bubbletea/v2"
8)
9
10func contextSnapshot() *daemon.ContextSnapshot {
11	window := 200000
12	return &daemon.ContextSnapshot{
13		State: "ready", Provider: "claude", Model: "claude-sonnet", Protocol: "messages",
14		ContextWindowTokens: &window,
15		Sections: []daemon.ContextSection{
16			{ID: "system", Label: "System instructions", Kind: "system", Source: "AGENTS.md + workspace", ItemCount: 3, ByteCount: 9120, Pages: 1, Preview: "You are albedo, a coding agent..."},
17			{ID: "tools", Label: "Tool definitions", Kind: "tools", Source: "extensions", ItemCount: 14, ByteCount: 22040, Pages: 2},
18			{ID: "history", Label: "Conversation history", Kind: "history", Source: "transcript", ItemCount: 58, ByteCount: 184320, Pages: 9, Preview: "user: add a retry to the webhook sender"},
19			{ID: "input", Label: "Current input", Kind: "input", Source: "this turn", ItemCount: 1, ByteCount: 212},
20		},
21	}
22}
23
24// contextModel is the inspector with a ready snapshot, sized to the shot.
25func contextModel(width, height int) ContextInspectorModel {
26	m := NewContextInspectorModel(nil, "s")
27	m.SetSize(width, height)
28	m, _ = m.Update(contextSnapshotLoadedMsg{Gen: m.Generation, Snapshot: contextSnapshot()})
29	return m
30}
31
32func contextShot(keys ...tea.Msg) func(width, height int) string {
33	return func(width, height int) string {
34		m := contextModel(width, height)
35		for _, key := range keys {
36			m, _ = m.Update(key)
37		}
38		return m.View()
39	}
40}
41
42// contextReader opens the history section with its first page loaded.
43func contextReader(width, height int, page daemon.ContextPage) ContextInspectorModel {
44	m := contextModel(width, height)
45	m, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyDown})
46	m, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyDown})
47	m, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
48	m, _ = m.Update(contextPageLoadedMsg{Gen: m.Generation, SectionID: "history", Data: &page})
49	return m
50}
51
52func historyPage() daemon.ContextPage {
53	content := "user: add a retry to the webhook sender\n"
54	for range 40 {
55		content += "assistant: looked at internal/hooks/sender.go and the queue it drains into\n"
56	}
57	return daemon.ContextPage{Content: content, Pages: 9}
58}
59
60func init() {
61	enter := tea.KeyPressMsg{Code: tea.KeyEnter}
62	down := tea.KeyPressMsg{Code: tea.KeyDown}
63	registerShots("context",
64		shotState{"list", contextShot(down, down)},
65		shotState{"no-content", contextShot(down, down, down)},
66		shotState{"filtering", contextShot(shotTyped("hist")...)},
67		shotState{"no-match", contextShot(shotTyped("zzz")...)},
68		shotState{"reader", func(width, height int) string {
69			return contextReader(width, height, historyPage()).View()
70		}},
71		shotState{"reader-scrolled", func(width, height int) string {
72			m := contextReader(width, height, historyPage())
73			m.View()
74			for range 12 {
75				m, _ = m.Update(down)
76			}
77			return m.View()
78		}},
79		shotState{"reader-loading", func(width, height int) string {
80			m := contextModel(width, height)
81			m, _ = m.Update(down)
82			m, _ = m.Update(down)
83			m, _ = m.Update(enter)
84			return m.View()
85		}},
86		shotState{"reader-failed", func(width, height int) string {
87			m := contextModel(width, height)
88			m, _ = m.Update(down)
89			m, _ = m.Update(down)
90			m, _ = m.Update(enter)
91			m, _ = m.Update(contextPageLoadedMsg{Gen: m.Generation, SectionID: "history", Err: errors.New("page 1 is no longer available")})
92			return m.View()
93		}},
94		shotState{"reader-omitted", func(width, height int) string {
95			page := historyPage()
96			page.Omitted = "earlier turns were dropped from this request"
97			return contextReader(width, height, page).View()
98		}},
99		shotState{"pending", func(width, height int) string {
100			m := NewContextInspectorModel(nil, "s")
101			m.SetSize(width, height)
102			m, _ = m.Update(contextSnapshotLoadedMsg{Gen: m.Generation, Snapshot: &daemon.ContextSnapshot{State: "pending", Reason: "the session has not sent a request yet"}})
103			return m.View()
104		}},
105		shotState{"empty", func(width, height int) string {
106			m := NewContextInspectorModel(nil, "s")
107			m.SetSize(width, height)
108			m, _ = m.Update(contextSnapshotLoadedMsg{Gen: m.Generation, Snapshot: &daemon.ContextSnapshot{State: "ready", Provider: "claude", Model: "claude-sonnet"}})
109			return m.View()
110		}},
111		shotState{"loading", func(width, height int) string {
112			m := NewContextInspectorModel(nil, "s")
113			m.SetSize(width, height)
114			return m.View()
115		}},
116		shotState{"load-failed", func(width, height int) string {
117			m := NewContextInspectorModel(nil, "s")
118			m.SetSize(width, height)
119			m, _ = m.Update(contextSnapshotLoadedMsg{Gen: m.Generation, Err: errors.New("daemon connection unavailable")})
120			return m.View()
121		}},
122	)
123}