1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
//! Central action enum — components emit these, the root dispatcher routes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Action {
Noop,
Quit,
// Navigation
MoveUp,
MoveDown,
DrillIn,
DrillOut,
PreviewLineUp,
PreviewLineDown,
// Pane filter (SEARCHING mode)
EnterSearch,
CommitFilter,
ClearFilter,
// Find-in-file (plans/0007 §3): `␣ /` opens FIND over the preview;
// n/N cycle a committed search.
LeaderFindInFile,
/// FIND-mode keystroke changed the query — recompute matches live.
UpdateFind,
CommitFind,
CancelFind,
FindNext,
FindPrev,
// Leader
Leader,
LeaderSearch,
LeaderFileFind,
LeaderGrep,
/// `␣ b` — revision switcher (branches/tags), plans/0016 M1a.
LeaderRefs,
/// `␣ p` — the preview pane takes the keyboard (plans/0016 M1).
LeaderPreview,
/// Esc/q out of the preview submode.
ExitPreview,
/// `␣ p h` — file history lens over the preview, plans/0016 M1b.
LeaderHistory,
/// `␣ p b` — blame lens over the preview (margin runs), M1c.
BlameToggle,
/// `Y` in a file pane — copy content (the visual selection, else
/// the cursor line) to the clipboard (GitHub's copy button).
PreviewCopy,
/// Enter in the preview submode: editor — or the line's commit in
/// the history lens while blaming.
PreviewEnter,
LeaderQuit,
// Revision mocks (plans/0016 M1): the refs popup live-previews the
// crumb, Enter commits, Esc reverts; the history lens navigates
// commits and "opens" a revision (mock: status line, no fetch).
RefsPreview(String),
RefsCommit(String),
HistoryUp,
HistoryDown,
HistoryOpen,
HistoryClose,
/// `/` in the history lens: commit-list filter session.
HistoryFilterBegin,
/// `y` in the history lens: yank the file URL anchored to the
/// commit — the permalink that never rots (plans/0016 M1b).
HistoryYank,
// Popup
ClosePopup,
// Overlays & command layer (plans/0003, plans/0004)
KeybindsPopup,
CommandLine,
RunCommand(String),
Visual,
ExitVisual,
ToggleSelect,
/// `␣ y` — yank the context's remote URL (plans/0003 §1).
LeaderYank,
/// `␣ c` — clear all VISUAL marks.
ClearMarks,
/// `␣ d` — delete marked orgs from the browser (+ state).
DeleteMarked,
/// `␣ r` — reload the open repo's tree (or the org's repos).
LeaderReload,
/// Settings popup closed with edits: persist + hot reload.
ApplySettings(crate::config::Config),
/// Clone wizard: run the clones (repos + destination).
RunClone {
repos: Vec<String>,
dest: std::path::PathBuf,
},
// Global search view (plans/0002-v0.2)
CloseSearchView,
GlobalSearchSubmitted {
kind: crate::components::global_search::SearchKind,
query: String,
scope: String,
extension: String,
},
GlobalSearchResults {
hits: Vec<crate::components::global_search::SearchHit>,
/// Provider-truncated or client-capped result set (plans/0008
/// §4) — complete and clipped sets are distinguishable.
clipped: bool,
/// v1.3: index freshness for indexed backends; `None` = live
/// or unknown.
index: Option<String>,
/// plans/0012 M1: hits the client-side grammar filter removed.
client_filtered: usize,
/// Grammar tokens rootle couldn't express anywhere.
unfiltered: Vec<String>,
},
/// Streamed batch (v1.3, plans/0011): merged into the result set
/// by file identity; selection survives.
GlobalSearchDelta {
hits: Vec<crate::components::global_search::SearchHit>,
},
GlobalSearchFailed {
error: crate::provider::ProviderError,
},
/// Cursor landed on a hit without preview lines but with a sha —
/// fetch its blob and locate the context lazily (plans/0006 §1).
LoadHitContext {
hit: Box<crate::components::global_search::SearchHit>,
query: String,
},
HitContextLoaded {
repo: String,
path: String,
sha: String,
line: u32,
preview: Vec<(u32, ratatui::text::Line<'static>)>,
match_count: u32,
},
OpenSearchHit(crate::components::global_search::SearchHit),
/// Expand the hit into the full-file pane (plans/0012 M2): fetch
/// the whole blob by repo+sha — cache-first, so a hit whose
/// context the lazy locate already fetched is free.
LoadHitFile {
hit: Box<crate::components::global_search::SearchHit>,
},
/// The expanded pane's blob landed, styled on the UI thread like
/// every other blob (sanitize + highlight at the boundary).
HitFileLoaded {
repo: String,
path: String,
sha: String,
lang: String,
lines: Vec<ratatui::text::Line<'static>>,
},
/// The expanded pane's blob fetch failed (or the blob is binary).
HitFileFailed {
sha: String,
error: crate::provider::ProviderError,
},
// Search popup ↔ GitHub backend
SearchSubmitted(String),
SearchResults {
items: Vec<crate::provider::SearchItem>,
},
SearchFailed {
error: crate::provider::ProviderError,
},
// Org loading
OrgSelected(String),
LoadOrgRepos(String),
OrgReposLoaded {
org: String,
repos: Vec<crate::provider::RepoInfo>,
},
OrgReposFailed {
org: String,
error: crate::provider::ProviderError,
},
// Repo tree loading
LoadRepoTree {
owner: String,
name: String,
},
TreeLoaded {
owner: String,
name: String,
entries: Vec<crate::provider::TreeNode>,
truncated: bool,
branch: String,
},
TreeFailed {
owner: String,
name: String,
error: crate::provider::ProviderError,
},
// Blob preview
LoadBlob {
sha: String,
name: String,
},
BlobLoaded {
sha: String,
name: String,
bytes: Vec<u8>,
},
BlobFailed {
sha: String,
error: crate::provider::ProviderError,
},
/// Lazy hit-context fetch failed (plans/0008 §2).
HitContextFailed {
sha: String,
error: crate::provider::ProviderError,
},
/// Blob fetched but the match text isn't in it (plans/0008 §4).
HitContextMissing {
sha: String,
},
/// Cursor-rest debounce fired (plans/0008 §3): dispatch the lazy
/// context fetch for the hit the cursor finally rested on.
HitContextDebounceFired {
timer_gen: u64,
hit: crate::components::global_search::SearchHit,
query: String,
},
// Selection outcomes
RepoSelected {
owner: String,
name: String,
},
OpenSelected,
}