gitkraft_gui/message.rs
1//! Unified message type for the entire GitKraft GUI application.
2//!
3//! Every user interaction, async result callback, and internal event is
4//! represented as a variant of [`Message`]. The top-level `update` function
5//! pattern-matches on these and delegates to the appropriate feature handler.
6
7use std::path::PathBuf;
8
9use gitkraft_core::{
10 BranchInfo, CommitInfo, DiffFileEntry, DiffInfo, GraphRow, RemoteInfo, RepoInfo, StashEntry,
11};
12
13// ── Payload types ─────────────────────────────────────────────────────────────
14
15/// Payload returned after successfully opening / refreshing a repository.
16/// Bundles every piece of state the UI needs so we can update in one shot.
17#[derive(Debug, Clone)]
18pub struct RepoPayload {
19 pub info: RepoInfo,
20 pub branches: Vec<BranchInfo>,
21 pub commits: Vec<CommitInfo>,
22 pub graph_rows: Vec<GraphRow>,
23 pub unstaged: Vec<DiffInfo>,
24 pub staged: Vec<DiffInfo>,
25 pub stashes: Vec<StashEntry>,
26 pub remotes: Vec<RemoteInfo>,
27}
28
29/// Payload returned after a staging operation (stage / unstage / discard).
30#[derive(Debug, Clone)]
31pub struct StagingPayload {
32 pub unstaged: Vec<DiffInfo>,
33 pub staged: Vec<DiffInfo>,
34}
35
36/// Payload returned by a lazy-load page request.
37#[derive(Debug, Clone)]
38pub struct CommitPage {
39 pub commits: Vec<gitkraft_core::CommitInfo>,
40 pub graph_rows: Vec<gitkraft_core::GraphRow>,
41}
42
43// ── Message enum ──────────────────────────────────────────────────────────────
44
45#[derive(Debug, Clone)]
46pub enum Message {
47 // ── Tabs ──────────────────────────────────────────────────────────────
48 /// User clicked a tab in the tab bar.
49 SwitchTab(usize),
50 /// User clicked the "+" button to open a new empty tab.
51 NewTab,
52 /// User clicked the close (×) button on a tab.
53 CloseTab(usize),
54
55 // ── Repository ────────────────────────────────────────────────────────
56 /// User clicked "Open Repository" — launch the folder picker.
57 OpenRepo,
58 /// User clicked "Init Repository" — launch the folder picker for init.
59 InitRepo,
60 /// Folder picker returned a path (or was cancelled → `None`).
61 RepoSelected(Option<PathBuf>),
62 /// Folder picker returned a path for init (or was cancelled → `None`).
63 RepoInitSelected(Option<PathBuf>),
64 /// Async repo-open completed.
65 RepoOpened(Result<RepoPayload, String>),
66 /// User requested a full refresh of the current repo.
67 RefreshRepo,
68 /// Async refresh completed.
69 RepoRefreshed(Result<RepoPayload, String>),
70
71 // ── Branches ──────────────────────────────────────────────────────────
72 /// User clicked a branch name → checkout that branch.
73 CheckoutBranch(String),
74 /// Async checkout completed.
75 BranchCheckedOut(Result<(), String>),
76 /// User submitted the new-branch form.
77 CreateBranch,
78 /// User is typing a new branch name.
79 NewBranchNameChanged(String),
80 /// Async branch creation completed.
81 BranchCreated(Result<(), String>),
82 /// User clicked the delete button next to a branch.
83 DeleteBranch(String),
84 /// Async branch deletion completed.
85 BranchDeleted(Result<(), String>),
86 /// Toggle visibility of the new-branch inline form.
87 ToggleBranchCreate,
88 /// Collapse or expand the Local branches section.
89 ToggleLocalBranches,
90 /// Collapse or expand the Remote branches section.
91 ToggleRemoteBranches,
92
93 // ── Commits ───────────────────────────────────────────────────────────
94 /// User clicked a commit row in the log.
95 SelectCommit(usize),
96 /// Async commit file-list load completed (lightweight — no diff content).
97 CommitFileListLoaded(Result<Vec<DiffFileEntry>, String>),
98 /// Async single-file diff load completed.
99 SingleFileDiffLoaded(Result<DiffInfo, String>),
100 /// The commit log scrollable was scrolled.
101 /// Carries `(absolute_y, relative_y)` — absolute for virtual-window
102 /// positioning, relative (0.0 = top, 1.0 = bottom) for load-more trigger.
103 CommitLogScrolled(f32, f32),
104 /// The diff viewer scrollable was scrolled — carries `absolute_y`.
105 DiffViewScrolled(f32),
106 /// A lazy-loaded page of additional commits was fetched from the background.
107 MoreCommitsLoaded(Result<CommitPage, String>),
108
109 // ── Staging ───────────────────────────────────────────────────────────
110 /// Stage a single file by its path.
111 StageFile(String),
112 /// Unstage a single file by its path.
113 UnstageFile(String),
114 /// Stage all unstaged files.
115 StageAll,
116 /// Unstage all staged files.
117 UnstageAll,
118 /// Discard working-directory changes for a file.
119 DiscardFile(String),
120 /// User confirmed the discard for a file.
121 ConfirmDiscard(String),
122 /// User cancelled a pending discard.
123 CancelDiscard,
124 /// Async staging operation completed.
125 StagingUpdated(Result<StagingPayload, String>),
126
127 // ── Commit creation ───────────────────────────────────────────────────
128 /// User is typing in the commit-message input.
129 CommitMessageChanged(String),
130 /// User clicked "Commit".
131 CreateCommit,
132 /// Async commit creation completed.
133 CommitCreated(Result<(), String>),
134
135 // ── Stash ─────────────────────────────────────────────────────────────
136 /// Save the current working state as a stash.
137 StashSave,
138 /// Pop (apply + drop) a stash by index.
139 StashPop(usize),
140 /// Drop (delete) a stash by index without applying.
141 StashDrop(usize),
142 /// Async stash operation completed.
143 StashUpdated(Result<Vec<StashEntry>, String>),
144 /// User is typing in the stash-message input.
145 StashMessageChanged(String),
146
147 // ── Remotes ───────────────────────────────────────────────────────────
148 /// Fetch from the first configured remote.
149 Fetch,
150 /// Async fetch completed.
151 FetchCompleted(Result<(), String>),
152
153 // ── UI ────────────────────────────────────────────────────────────────
154 /// User clicked a file in the commit-diff file list (by index into `commit_files`).
155 SelectDiffByIndex(usize),
156 /// User clicked a file in the staging area to view its diff.
157 SelectDiff(DiffInfo),
158 /// Dismiss the current error banner.
159 DismissError,
160 /// Zoom in (increase UI scale).
161 ZoomIn,
162 /// Zoom out (decrease UI scale).
163 ZoomOut,
164 /// Reset zoom to 100%.
165 ZoomReset,
166 /// Toggle the left sidebar.
167 ToggleSidebar,
168 /// Close the current repository and return to the welcome screen.
169 CloseRepo,
170
171 // ── Pane resize ───────────────────────────────────────────────────────
172 /// User pressed the mouse button on a vertical divider to start dragging.
173 PaneDragStart(crate::state::DragTarget, f32),
174 /// User pressed the mouse button on the horizontal staging divider.
175 PaneDragStartH(crate::state::DragTargetH, f32),
176 /// Mouse moved during a drag — `(x, y)` in window coordinates.
177 PaneDragMove(f32, f32),
178 /// Mouse button released — stop dragging.
179 PaneDragEnd,
180 // ── Async persistence results ─────────────────────────────────────
181 /// Background `record_repo_opened` + `load_settings` completed.
182 /// Carries the refreshed recent-repos list (or an error string).
183 RepoRecorded(Result<Vec<gitkraft_core::RepoHistoryEntry>, String>),
184 /// Background `load_settings` completed (e.g. after closing a repo).
185 SettingsLoaded(Result<Vec<gitkraft_core::RepoHistoryEntry>, String>),
186 /// Background `save_theme` completed (fire-and-forget, errors logged).
187 ThemeSaved(Result<(), String>),
188 /// Background layout save completed (fire-and-forget, errors logged).
189 LayoutSaved(Result<(), String>),
190 /// Layout loaded from persisted settings on startup.
191 LayoutLoaded(Result<Option<gitkraft_core::LayoutSettings>, String>),
192 /// Background session save completed (fire-and-forget).
193 SessionSaved(Result<(), String>),
194 /// Async restore of a specific tab (by index) completed on startup.
195 RepoRestoredAt(usize, Result<RepoPayload, String>),
196
197 // ── Context menus ─────────────────────────────────────────────────────────────
198 /// User right-clicked a local branch.
199 /// Payload: (branch_name, index_in_local_list, is_current_branch).
200 OpenBranchContextMenu(String, usize, bool),
201
202 /// User right-clicked a remote branch.
203 OpenRemoteBranchContextMenu(String),
204
205 /// Checkout a remote branch (creates local tracking branch).
206 CheckoutRemoteBranch(String),
207
208 /// Delete a remote branch.
209 DeleteRemoteBranch(String),
210 /// User right-clicked a commit row.
211 OpenCommitContextMenu(usize),
212
213 /// Dismiss the context menu without taking an action.
214 CloseContextMenu,
215
216 // ── Branch actions ────────────────────────────────────────────────────────────
217 /// Push the named branch to its default remote.
218 PushBranch(String),
219
220 /// Pull the current branch from its remote, rebasing local commits on top.
221 PullBranch(String),
222
223 /// Rebase the current HEAD onto `target` (a branch name or OID string).
224 RebaseOnto(String),
225
226 /// Begin an inline rename: record the branch being renamed and pre-fill input.
227 BeginRenameBranch(String),
228
229 /// User is typing in the rename input.
230 RenameBranchInputChanged(String),
231
232 /// User confirmed the rename.
233 ConfirmRenameBranch,
234
235 /// User cancelled the rename.
236 CancelRename,
237
238 /// Merge a named branch into the current HEAD branch.
239 MergeBranch(String),
240
241 /// Begin an inline tag-creation form at the given commit OID.
242 /// The bool indicates whether this is an annotated tag (true) or lightweight (false).
243 BeginCreateTag(String, bool),
244
245 /// User is typing in the tag name input.
246 TagNameChanged(String),
247
248 /// User is typing in the annotated tag message input.
249 TagMessageChanged(String),
250
251 /// User confirmed tag creation.
252 ConfirmCreateTag,
253
254 /// User cancelled tag creation.
255 CancelCreateTag,
256
257 // ── Commit actions ────────────────────────────────────────────────────────────
258 /// Checkout a specific commit in detached HEAD mode.
259 CheckoutCommitDetached(String),
260
261 /// Rebase the current branch on top of a specific commit.
262 RebaseOntoCommit(String),
263
264 /// Revert a specific commit (creates a revert commit).
265 RevertCommit(String),
266
267 /// git reset --soft `oid` — move HEAD, keep staged + working changes.
268 ResetSoft(String),
269 /// git reset --mixed `oid` — move HEAD and unstage; keep working directory.
270 ResetMixed(String),
271 /// git reset --hard `oid` — move HEAD and discard all uncommitted changes.
272 ResetHard(String),
273
274 // ── Shared ───────────────────────────────────────────────────────────────────
275 /// Copy a string to the system clipboard.
276 CopyText(String),
277
278 /// Generic result for any git operation that produces a full repo refresh.
279 /// The operation itself is responsible for a descriptive error string.
280 GitOperationResult(Result<RepoPayload, String>),
281
282 /// User selected a different theme from the picker (by index into
283 /// `gitkraft_core::THEME_NAMES`).
284 ThemeChanged(usize),
285 /// User clicked a recent repository entry on the welcome screen.
286 OpenRecentRepo(PathBuf),
287 /// No-op (used for disabled buttons, etc.).
288 Noop,
289}