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
use crate::git::graph::{DiffStat, GraphRow};
use crate::git::status::RepoStatus;
use crate::repo_id::RepoId;
#[derive(Clone, Debug)]
#[allow(dead_code)]
pub(crate) enum Action {
Tick,
Render,
Quit,
Resize(u16, u16),
SelectNextRepo,
SelectPrevRepo,
SelectRepo(RepoId),
/// Update the file list and graph to a repo's data without moving the
/// list-row selection. Used when a child row (stash entry) is highlighted.
FocusRepoDetails(RepoId),
SelectWorktree {
repo_id: RepoId,
worktree_path: std::path::PathBuf,
worktree_branch: String,
},
/// Carries the result of a worktree status query back to the UI.
WorktreeFilesLoaded {
repo_id: RepoId,
worktree_path: std::path::PathBuf,
name: String,
files: Vec<crate::git::status::FileEntry>,
},
RepoStatusUpdated {
id: RepoId,
status: RepoStatus,
},
RefreshAll,
RefreshRepo(RepoId),
/// Deferred watcher refresh fired after the per-repo cooldown expires.
RefreshRepoAfterCooldown(RepoId),
/// Fast local status poll (no spinner, no fetch)
PollLocal,
/// Remote fetch poll (no spinner)
PollFetch,
ShowGitGraph,
ShowFileList,
/// Open the highlighted repo (or worktree) in a new tmux pane, or the
/// configured `[open] command`. Resolves the selection in-app, like
/// `ShowGitGraph`, so it carries no payload.
OpenSelected,
/// Open a specific repo/worktree by id (from the context menu), bound to the
/// right-clicked row so an async row re-sort can't retarget it.
OpenAt(RepoId),
/// Review the highlighted repo/worktree's diff vs its base branch in a new
/// tmux window, via the `[review] command` (default `git diff {base}...HEAD`).
/// Resolves the selection in-app, so it carries no payload.
ReviewSelected,
/// Review a specific repo/worktree by id (from the context menu), bound to
/// the right-clicked row.
ReviewAt(RepoId),
/// Open the branch-name input to create a new worktree for the given repo.
OpenNewWorktree(RepoId),
/// Create a worktree for `repo` on a new branch `branch`
/// (`git worktree add <dir> -b <branch>`).
CreateWorktree {
repo: std::path::PathBuf,
branch: String,
},
/// Resolve the highlighted worktree and open a confirmation to remove it.
/// Sent by the `d` key on a worktree row.
RemoveWorktreeSelected,
/// Resolve a specific worktree by id (from the context menu) and confirm
/// removal, bound to the right-clicked row rather than the selection.
RemoveWorktreeAt(RepoId),
/// Remove `worktree_path` from `repo` (`git worktree remove <path>`).
RemoveWorktree {
repo: std::path::PathBuf,
worktree_path: std::path::PathBuf,
},
/// `(session, pane_cwd)` from the latest liveness probe; repos/worktrees
/// whose path contains a pane cwd are marked live with that session's name.
LiveSessionsLoaded(Vec<(String, std::path::PathBuf)>),
/// The picker chose this value (a placement string, or a session name);
/// the app routes it by its pending picker purpose.
PickerChose(String),
/// The picker was dismissed; drop the pending action.
PickerCancel,
/// Attach the tmux session(s) live in the selected repo/worktree via the
/// `[goto] command` (default `tmux switch-client -t {session}`). One session
/// goes directly; several open the picker. Triggered by the `G` key.
GotoSessionSelected,
/// Attach a named tmux session directly (from a context-menu "Attach
/// <session>" item) via the `[goto] command`.
GotoSession(String),
/// Open the picker for the tmux sessions live in this repo/worktree path
/// (from the context-menu "Attach session…" item). Path-bound so it targets
/// the clicked row, not the current selection.
GotoSessionPicker(RepoId),
GraphLoaded {
generation: u64,
rows: Vec<GraphRow>,
},
DiffStatsLoaded {
generation: u64,
stats: Vec<(git2::Oid, DiffStat)>,
},
ShowContextMenu {
id: RepoId,
row: u16,
col: u16,
/// True when the right-clicked row is a worktree (vs a top-level repo),
/// so the menu can offer worktree-specific items.
is_worktree: bool,
},
HideContextMenu,
CopyPath(RepoId),
/// Right-click on a changed-file row: open the file context menu. `id` is the
/// changed-files box's repo id (the parent repo, even for worktree files).
ShowFileContextMenu {
id: RepoId,
path: std::path::PathBuf,
row: u16,
col: u16,
staged: bool,
unstaged: bool,
is_untracked: bool,
is_submodule: bool,
},
/// Open a file in the configured `[open]` command, or the OS default app.
OpenFile(RepoId, std::path::PathBuf),
/// Reveal a file in the OS file manager (Finder on macOS).
RevealFile(RepoId, std::path::PathBuf),
StageFile(RepoId, std::path::PathBuf),
UnstageFile(RepoId, std::path::PathBuf),
/// Prompt before discarding. The `bool` is `is_untracked` (delete vs restore).
DiscardFile(RepoId, std::path::PathBuf, bool),
/// Run the discard after the confirm dialog accepts.
DiscardFileConfirmed(RepoId, std::path::PathBuf, bool),
GitPush(RepoId),
GitPull(RepoId),
GitPullRebase(RepoId),
GitPullSubmodules(RepoId),
GitSubmoduleUpdate(RepoId),
GitSubmoduleSync(RepoId),
GitSubmoduleUpdateLatest(RepoId),
GitOpComplete {
id: RepoId,
message: String,
},
ShowDiff(RepoId, std::path::PathBuf),
DiffLoaded {
generation: u64,
content: String,
},
GraphError {
generation: u64,
message: String,
},
/// A background graph build exited without sending `GraphLoaded`/`GraphError`
/// (e.g. it panicked). Releases the `load_in_flight` latch for `generation`
/// so the graph can rebuild on the next change instead of freezing.
GraphLoadAborted {
generation: u64,
},
ShowCommitFiles {
repo_path: std::path::PathBuf,
oid: String,
},
CommitFilesLoaded {
generation: u64,
oid: String,
message: String,
files: Vec<(String, String)>,
},
ShowCommitDiff {
repo_path: std::path::PathBuf,
oid: String,
file_path: String,
},
CommitDiffLoaded {
generation: u64,
content: String,
},
OpenAddRepo,
AddRepo(std::path::PathBuf),
RemoveRepo(RepoId),
CycleSortOrder,
RescanRepos,
/// Idempotent rescan triggered by FS events. Compares the discovered set
/// against the current set and only mutates state on a diff. Unlike
/// [`Action::RescanRepos`] this preserves `excluded_repos`, in-flight
/// status queries, dirty-repo tracking, and the user's selection.
DiscoverNewRepos,
Error(String),
UpdateAvailable(String),
/// Clears the pending_status flag for a repo (sent on error paths)
StatusQueryDone(RepoId),
/// Open the in-app theme picker overlay.
OpenThemePicker,
/// Apply a theme transiently (live preview), no persistence.
PreviewTheme(String),
/// Apply a theme and write `theme = "<name>"` to the active config file.
CommitTheme(String),
/// Re-apply the theme that was active when the picker opened, then close.
CancelThemePreview,
}