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 /// Diff a file from a commit against the working tree.
101 DiffFileWithWorkingTree(String, String), // (oid, file_path)
102 /// Result of diffing a file with the working tree.
103 DiffWithWorkingTreeLoaded(Result<DiffInfo, String>),
104 /// The commit log scrollable was scrolled.
105 /// Carries `(absolute_y, relative_y)` — absolute for virtual-window
106 /// positioning, relative (0.0 = top, 1.0 = bottom) for load-more trigger.
107 CommitLogScrolled(f32, f32),
108 /// The diff viewer scrollable was scrolled — carries `absolute_y`.
109 DiffViewScrolled(f32),
110 /// A lazy-loaded page of additional commits was fetched from the background.
111 MoreCommitsLoaded(Result<CommitPage, String>),
112
113 // ── Staging ───────────────────────────────────────────────────────────
114 /// Stage a single file by its path.
115 StageFile(String),
116 /// Unstage a single file by its path.
117 UnstageFile(String),
118 /// Stage all unstaged files.
119 StageAll,
120 /// Unstage all staged files.
121 UnstageAll,
122 /// Discard working-directory changes for a file.
123 DiscardFile(String),
124 /// User confirmed the discard for a file.
125 ConfirmDiscard(String),
126 /// User cancelled a pending discard.
127 CancelDiscard,
128 /// Toggle selection of an unstaged file (Shift+Click).
129 ToggleSelectUnstaged(String),
130 /// Toggle selection of a staged file (Shift+Click).
131 ToggleSelectStaged(String),
132 /// Stage all currently selected unstaged files.
133 StageSelected,
134 /// Unstage all currently selected staged files.
135 UnstageSelected,
136 /// Discard all currently selected unstaged files.
137 DiscardSelected,
138 /// Discard a staged file (unstage + discard working dir changes).
139 DiscardStagedFile(String),
140 /// Async staging operation completed.
141 StagingUpdated(Result<StagingPayload, String>),
142
143 // ── Commit creation ───────────────────────────────────────────────────
144 /// User is typing in the commit-message input.
145 CommitMessageChanged(String),
146 /// User clicked "Commit".
147 CreateCommit,
148 /// Async commit creation completed.
149 CommitCreated(Result<(), String>),
150
151 // ── Stash ─────────────────────────────────────────────────────────────
152 /// Save the current working state as a stash.
153 StashSave,
154 /// Pop (apply + drop) a stash by index.
155 StashPop(usize),
156 /// Drop (delete) a stash by index without applying.
157 StashDrop(usize),
158 /// Async stash operation completed.
159 StashUpdated(Result<Vec<StashEntry>, String>),
160 /// User is typing in the stash-message input.
161 StashMessageChanged(String),
162 /// User right-clicked a stash entry.
163 OpenStashContextMenu(usize),
164 /// User right-clicked a file in the commit diff file list.
165 OpenCommitFileContextMenu(String, String), // (oid, file_path)
166 /// User right-clicked an unstaged file.
167 OpenUnstagedFileContextMenu(String),
168 /// User right-clicked a staged file.
169 OpenStagedFileContextMenu(String),
170 /// User wants to view the diff of a stash entry.
171 ViewStashDiff(usize),
172 /// Stash diff loaded.
173 StashDiffLoaded(Result<Vec<DiffInfo>, String>),
174 /// Apply a stash (like pop but keeps the stash).
175 StashApply(usize),
176
177 // ── Remotes ───────────────────────────────────────────────────────────
178 /// Fetch from the first configured remote.
179 Fetch,
180 /// Async fetch completed.
181 FetchCompleted(Result<(), String>),
182
183 // ── UI ────────────────────────────────────────────────────────────────
184 /// User clicked a file in the commit-diff file list (by index into `commit_files`).
185 SelectDiffByIndex(usize),
186 /// User clicked a file in the staging area to view its diff.
187 SelectDiff(DiffInfo),
188 /// Dismiss the current error banner.
189 DismissError,
190 /// Zoom in (increase UI scale).
191 ZoomIn,
192 /// Zoom out (decrease UI scale).
193 ZoomOut,
194 /// Reset zoom to 100%.
195 ZoomReset,
196 /// Toggle the left sidebar.
197 ToggleSidebar,
198 /// Close the current repository and return to the welcome screen.
199 CloseRepo,
200
201 // ── Pane resize ───────────────────────────────────────────────────────
202 /// User pressed the mouse button on a vertical divider to start dragging.
203 PaneDragStart(crate::state::DragTarget, f32),
204 /// User pressed the mouse button on the horizontal staging divider.
205 PaneDragStartH(crate::state::DragTargetH, f32),
206 /// Mouse moved during a drag — `(x, y)` in window coordinates.
207 PaneDragMove(f32, f32),
208 /// Mouse button released — stop dragging.
209 PaneDragEnd,
210 // ── Async persistence results ─────────────────────────────────────
211 /// Background `record_repo_opened` + `load_settings` completed.
212 /// Carries the refreshed recent-repos list (or an error string).
213 RepoRecorded(Result<Vec<gitkraft_core::RepoHistoryEntry>, String>),
214 /// Background `load_settings` completed (e.g. after closing a repo).
215 SettingsLoaded(Result<Vec<gitkraft_core::RepoHistoryEntry>, String>),
216 /// Background `save_theme` completed (fire-and-forget, errors logged).
217 ThemeSaved(Result<(), String>),
218 /// Background layout save completed (fire-and-forget, errors logged).
219 LayoutSaved(Result<(), String>),
220 /// Layout loaded from persisted settings on startup.
221 LayoutLoaded(Result<Option<gitkraft_core::LayoutSettings>, String>),
222 /// Background session save completed (fire-and-forget).
223 SessionSaved(Result<(), String>),
224 /// Async restore of a specific tab (by index) completed on startup.
225 RepoRestoredAt(usize, Result<RepoPayload, String>),
226
227 // ── Context menus ─────────────────────────────────────────────────────────────
228 /// User right-clicked a local branch.
229 /// Payload: (branch_name, index_in_local_list, is_current_branch).
230 OpenBranchContextMenu(String, usize, bool),
231
232 /// User right-clicked a remote branch.
233 OpenRemoteBranchContextMenu(String),
234
235 /// Checkout a remote branch (creates local tracking branch).
236 CheckoutRemoteBranch(String),
237
238 /// Delete a remote branch.
239 DeleteRemoteBranch(String),
240 /// User right-clicked a commit row.
241 OpenCommitContextMenu(usize),
242
243 /// Dismiss the context menu without taking an action.
244 CloseContextMenu,
245
246 // ── Branch actions ────────────────────────────────────────────────────────────
247 /// Push the named branch to its default remote.
248 PushBranch(String),
249
250 /// Pull the current branch from its remote, rebasing local commits on top.
251 PullBranch(String),
252
253 /// Rebase the current HEAD onto `target` (a branch name or OID string).
254 RebaseOnto(String),
255
256 /// Begin an inline rename: record the branch being renamed and pre-fill input.
257 BeginRenameBranch(String),
258
259 /// User is typing in the rename input.
260 RenameBranchInputChanged(String),
261
262 /// User confirmed the rename.
263 ConfirmRenameBranch,
264
265 /// User cancelled the rename.
266 CancelRename,
267
268 /// Merge a named branch into the current HEAD branch.
269 MergeBranch(String),
270
271 /// Begin an inline tag-creation form at the given commit OID.
272 /// The bool indicates whether this is an annotated tag (true) or lightweight (false).
273 BeginCreateTag(String, bool),
274
275 /// User is typing in the tag name input.
276 TagNameChanged(String),
277
278 /// User is typing in the annotated tag message input.
279 TagMessageChanged(String),
280
281 /// User confirmed tag creation.
282 ConfirmCreateTag,
283
284 /// User cancelled tag creation.
285 CancelCreateTag,
286
287 // ── Commit actions ────────────────────────────────────────────────────────────
288 /// Checkout a specific commit in detached HEAD mode.
289 CheckoutCommitDetached(String),
290
291 /// Rebase the current branch on top of a specific commit.
292 RebaseOntoCommit(String),
293
294 /// Revert a specific commit (creates a revert commit).
295 RevertCommit(String),
296
297 /// git reset --soft `oid` — move HEAD, keep staged + working changes.
298 ResetSoft(String),
299 /// git reset --mixed `oid` — move HEAD and unstage; keep working directory.
300 ResetMixed(String),
301 /// git reset --hard `oid` — move HEAD and discard all uncommitted changes.
302 ResetHard(String),
303
304 // ── Shared ───────────────────────────────────────────────────────────────────
305 /// Copy a string to the system clipboard.
306 CopyText(String),
307
308 /// Generic result for any git operation that produces a full repo refresh.
309 /// The operation itself is responsible for a descriptive error string.
310 GitOperationResult(Result<RepoPayload, String>),
311
312 /// User selected a different theme from the picker (by index into
313 /// `gitkraft_core::THEME_NAMES`).
314 ThemeChanged(usize),
315
316 /// User selected a different editor from the picker.
317 EditorChanged(gitkraft_core::Editor),
318 /// Background `save_editor` completed (fire-and-forget, errors logged).
319 EditorSaved(Result<(), String>),
320 /// User clicked a recent repository entry on the welcome screen.
321 OpenRecentRepo(PathBuf),
322 // ── Search ────────────────────────────────────────────────────────────
323 /// Toggle the search overlay.
324 ToggleSearch,
325 /// User typed in the search input.
326 SearchQueryChanged(String),
327 /// Search results arrived from the background.
328 SearchResultsLoaded(Result<Vec<gitkraft_core::CommitInfo>, String>),
329 /// User selected a search result (by index).
330 SelectSearchResult(usize),
331 /// User confirmed the selected search result (Enter).
332 ConfirmSearchResult,
333 /// File list for commit-vs-workdir diff loaded.
334 SearchDiffFilesLoaded(Result<Vec<gitkraft_core::DiffFileEntry>, String>),
335 /// User toggled selection of a file in the search diff file list.
336 ToggleSearchDiffFile(usize),
337 /// User requested to view diff of selected search file against working tree.
338 ViewSearchDiffFile(usize),
339 /// The diff content for a search file loaded.
340 SearchFileDiffLoaded(Result<gitkraft_core::DiffInfo, String>),
341 /// User clicked "Select All" / "Deselect All" in search diff.
342 ToggleSearchDiffSelectAll,
343 /// Diff all selected files against working tree (combined view).
344 DiffSelectedFiles,
345 /// Combined diffs for selected files loaded.
346 SearchMultiDiffLoaded(Result<Vec<gitkraft_core::DiffInfo>, String>),
347 /// Go back from file diff view to file list in search.
348 SearchDiffBack,
349 /// User right-clicked a search result — open commit context menu.
350 OpenSearchResultContextMenu(usize),
351
352 /// File system change detected — auto-refresh staging area.
353 FileSystemChanged,
354
355 /// Open a file in the configured editor.
356 OpenInEditor(String),
357 /// Open a file in the system's default program.
358 OpenInDefaultProgram(String),
359 /// Show a file in the system file manager.
360 ShowInFolder(String),
361
362 /// No-op (used for disabled buttons, etc.).
363 Noop,
364}