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::{DiffFileEntry, DiffInfo, StashEntry};
10
11// ── Payload types ─────────────────────────────────────────────────────────────
12
13/// Payload returned after successfully opening / refreshing a repository.
14///
15/// This is now a re-export of the shared core type so both GUI and TUI use
16/// the same struct definition.
17pub type RepoPayload = gitkraft_core::RepoSnapshot;
18
19/// Payload returned after a staging operation (stage / unstage / discard).
20#[derive(Debug, Clone)]
21pub struct StagingPayload {
22 pub unstaged: Vec<DiffInfo>,
23 pub staged: Vec<DiffInfo>,
24}
25
26/// Payload returned by a lazy-load page request.
27#[derive(Debug, Clone)]
28pub struct CommitPage {
29 pub commits: Vec<gitkraft_core::CommitInfo>,
30 pub graph_rows: Vec<gitkraft_core::GraphRow>,
31}
32
33// ── Message enum ──────────────────────────────────────────────────────────────
34
35#[derive(Debug, Clone)]
36pub enum Message {
37 // ── Tabs ──────────────────────────────────────────────────────────────
38 /// User clicked a tab in the tab bar.
39 SwitchTab(usize),
40 /// User clicked the "+" button to open a new empty tab.
41 NewTab,
42 /// User clicked the close (×) button on a tab.
43 CloseTab(usize),
44
45 // ── Repository ────────────────────────────────────────────────────────
46 /// User clicked "Open Repository" — launch the folder picker.
47 OpenRepo,
48 /// User clicked "Init Repository" — launch the folder picker for init.
49 InitRepo,
50 /// Folder picker returned a path (or was cancelled → `None`).
51 RepoSelected(Option<PathBuf>),
52 /// Folder picker returned a path for init (or was cancelled → `None`).
53 RepoInitSelected(Option<PathBuf>),
54 /// Async repo-open completed.
55 RepoOpened(Result<RepoPayload, String>),
56 /// User requested a full refresh of the current repo.
57 RefreshRepo,
58 /// Async refresh completed.
59 RepoRefreshed(Result<RepoPayload, String>),
60
61 // ── Branches ──────────────────────────────────────────────────────────
62 /// User clicked a branch name → checkout that branch.
63 CheckoutBranch(String),
64 /// Async checkout completed.
65 BranchCheckedOut(Result<(), String>),
66 /// User submitted the new-branch form.
67 CreateBranch,
68 /// User is typing a new branch name.
69 NewBranchNameChanged(String),
70 /// Async branch creation completed.
71 BranchCreated(Result<(), String>),
72 /// User clicked the delete button next to a branch.
73 DeleteBranch(String),
74 /// Async branch deletion completed.
75 BranchDeleted(Result<(), String>),
76 /// Toggle visibility of the new-branch inline form.
77 ToggleBranchCreate,
78 /// Collapse or expand the Local branches section.
79 ToggleLocalBranches,
80 /// Collapse or expand the Remote branches section.
81 ToggleRemoteBranches,
82
83 // ── Commits ───────────────────────────────────────────────────────────
84 /// User clicked a commit row in the log.
85 SelectCommit(usize),
86 /// Async commit file-list load completed (lightweight — no diff content).
87 CommitFileListLoaded(Result<Vec<DiffFileEntry>, String>),
88 /// Async single-file diff load completed.
89 SingleFileDiffLoaded(Result<DiffInfo, String>),
90 /// Diff a file from a commit against the working tree.
91 DiffFileWithWorkingTree(String, String), // (oid, file_path)
92 /// Result of diffing a file with the working tree.
93 DiffWithWorkingTreeLoaded(Result<DiffInfo, String>),
94 /// The commit log scrollable was scrolled.
95 /// Carries `(absolute_y, relative_y)` — absolute for virtual-window
96 /// positioning, relative (0.0 = top, 1.0 = bottom) for load-more trigger.
97 CommitLogScrolled(f32, f32),
98 /// The diff viewer scrollable was scrolled — carries `absolute_y`.
99 DiffViewScrolled(f32),
100 /// A lazy-loaded page of additional commits was fetched from the background.
101 MoreCommitsLoaded(Result<CommitPage, String>),
102
103 // ── Staging ───────────────────────────────────────────────────────────
104 /// Stage a single file by its path.
105 StageFile(String),
106 /// Unstage a single file by its path.
107 UnstageFile(String),
108 /// Stage all unstaged files.
109 StageAll,
110 /// Unstage all staged files.
111 UnstageAll,
112 /// Discard working-directory changes for a file.
113 DiscardFile(String),
114 /// User confirmed the discard for a file.
115 ConfirmDiscard(String),
116 /// User cancelled a pending discard.
117 CancelDiscard,
118 /// Toggle selection of an unstaged file (Shift+Click).
119 ToggleSelectUnstaged(String),
120 /// Toggle selection of a staged file (Shift+Click).
121 ToggleSelectStaged(String),
122 /// Stage all currently selected unstaged files.
123 StageSelected,
124 /// Unstage all currently selected staged files.
125 UnstageSelected,
126 /// Discard all currently selected unstaged files.
127 DiscardSelected,
128 /// Discard a staged file (unstage + discard working dir changes).
129 DiscardStagedFile(String),
130 /// Async staging operation completed.
131 StagingUpdated(Result<StagingPayload, String>),
132
133 // ── Commit creation ───────────────────────────────────────────────────
134 /// User is typing in the commit-message input.
135 CommitMessageChanged(String),
136 /// User clicked "Commit".
137 CreateCommit,
138 /// Async commit creation completed.
139 CommitCreated(Result<(), String>),
140
141 // ── Stash ─────────────────────────────────────────────────────────────
142 /// Save the current working state as a stash.
143 StashSave,
144 /// Pop (apply + drop) a stash by index.
145 StashPop(usize),
146 /// Drop (delete) a stash by index without applying.
147 StashDrop(usize),
148 /// Async stash operation completed.
149 StashUpdated(Result<Vec<StashEntry>, String>),
150 /// User is typing in the stash-message input.
151 StashMessageChanged(String),
152 /// User right-clicked a stash entry.
153 OpenStashContextMenu(usize),
154 /// User right-clicked a file in the commit diff file list.
155 OpenCommitFileContextMenu(String, String), // (oid, file_path)
156 /// User right-clicked an unstaged file.
157 OpenUnstagedFileContextMenu(String),
158 /// User right-clicked a staged file.
159 OpenStagedFileContextMenu(String),
160 /// User wants to view the diff of a stash entry.
161 ViewStashDiff(usize),
162 /// Stash diff loaded.
163 StashDiffLoaded(Result<Vec<DiffInfo>, String>),
164 /// Apply a stash (like pop but keeps the stash).
165 StashApply(usize),
166
167 // ── Remotes ───────────────────────────────────────────────────────────
168 /// Fetch from the first configured remote.
169 Fetch,
170 /// Async fetch completed.
171 FetchCompleted(Result<(), String>),
172
173 // ── UI ────────────────────────────────────────────────────────────────
174 /// User clicked a file in the commit-diff file list (by index into `commit_files`).
175 SelectDiffByIndex(usize),
176 /// User clicked a file in the staging area to view its diff.
177 SelectDiff(DiffInfo),
178 /// Dismiss the current error banner.
179 DismissError,
180 /// Zoom in (increase UI scale).
181 ZoomIn,
182 /// Zoom out (decrease UI scale).
183 ZoomOut,
184 /// Reset zoom to 100%.
185 ZoomReset,
186 /// Toggle the left sidebar.
187 ToggleSidebar,
188 /// Close the current repository and return to the welcome screen.
189 CloseRepo,
190
191 // ── Pane resize ───────────────────────────────────────────────────────
192 /// User pressed the mouse button on a vertical divider to start dragging.
193 PaneDragStart(crate::state::DragTarget, f32),
194 /// User pressed the mouse button on the horizontal staging divider.
195 PaneDragStartH(crate::state::DragTargetH, f32),
196 /// Mouse moved during a drag — `(x, y)` in window coordinates.
197 PaneDragMove(f32, f32),
198 /// Mouse button released — stop dragging.
199 PaneDragEnd,
200 // ── Async persistence results ─────────────────────────────────────
201 /// Background `record_repo_opened` + `load_settings` completed.
202 /// Carries the refreshed recent-repos list (or an error string).
203 RepoRecorded(Result<Vec<gitkraft_core::RepoHistoryEntry>, String>),
204 /// Background `load_settings` completed (e.g. after closing a repo).
205 SettingsLoaded(Result<Vec<gitkraft_core::RepoHistoryEntry>, String>),
206 /// Background `save_theme` completed (fire-and-forget, errors logged).
207 ThemeSaved(Result<(), String>),
208 /// Background layout save completed (fire-and-forget, errors logged).
209 LayoutSaved(Result<(), String>),
210 /// Layout loaded from persisted settings on startup.
211 LayoutLoaded(Result<Option<gitkraft_core::LayoutSettings>, String>),
212 /// Background session save completed (fire-and-forget).
213 SessionSaved(Result<(), String>),
214 /// Async restore of a specific tab (by index) completed on startup.
215 RepoRestoredAt(usize, Result<RepoPayload, String>),
216
217 // ── Context menus ─────────────────────────────────────────────────────────────
218 /// User right-clicked a local branch.
219 /// Payload: (branch_name, index_in_local_list, is_current_branch).
220 OpenBranchContextMenu(String, usize, bool),
221
222 /// User right-clicked a remote branch.
223 OpenRemoteBranchContextMenu(String),
224
225 /// Checkout a remote branch (creates local tracking branch).
226 CheckoutRemoteBranch(String),
227
228 /// Delete a remote branch.
229 DeleteRemoteBranch(String),
230 /// User right-clicked a commit row.
231 OpenCommitContextMenu(usize),
232
233 /// Dismiss the context menu without taking an action.
234 CloseContextMenu,
235
236 // ── Branch actions ────────────────────────────────────────────────────────────
237 /// Push the named branch to its default remote.
238 PushBranch(String),
239
240 /// Pull the current branch from its remote, rebasing local commits on top.
241 PullBranch(String),
242
243 /// Rebase the current HEAD onto `target` (a branch name or OID string).
244 RebaseOnto(String),
245
246 /// Begin an inline rename: record the branch being renamed and pre-fill input.
247 BeginRenameBranch(String),
248
249 /// User is typing in the rename input.
250 RenameBranchInputChanged(String),
251
252 /// User confirmed the rename.
253 ConfirmRenameBranch,
254
255 /// User cancelled the rename.
256 CancelRename,
257
258 /// Merge a named branch into the current HEAD branch.
259 MergeBranch(String),
260
261 /// Begin an inline tag-creation form at the given commit OID.
262 /// The bool indicates whether this is an annotated tag (true) or lightweight (false).
263 BeginCreateTag(String, bool),
264
265 /// User is typing in the tag name input.
266 TagNameChanged(String),
267
268 /// User is typing in the annotated tag message input.
269 TagMessageChanged(String),
270
271 /// User confirmed tag creation.
272 ConfirmCreateTag,
273
274 /// User cancelled tag creation.
275 CancelCreateTag,
276
277 /// Cherry-pick a commit by OID onto the current branch.
278 CherryPickCommit(String),
279
280 /// Begin an inline "create branch at this commit" form.
281 BeginCreateBranchAtCommit(String),
282
283 /// User confirmed branch creation at a specific commit.
284 ConfirmCreateBranchAtCommit,
285
286 /// User cancelled branch creation at a specific commit.
287 CancelCreateBranchAtCommit,
288
289 /// Execute a commit action that requires no further user input.
290 /// Actions that need input (branch name, tag name) continue to use the
291 /// existing `BeginCreate*` messages.
292 ExecuteCommitAction(String, gitkraft_core::CommitAction),
293
294 // ── Commit actions ────────────────────────────────────────────────────────────
295 /// Checkout a specific commit in detached HEAD mode.
296 CheckoutCommitDetached(String),
297
298 /// Rebase the current branch on top of a specific commit.
299 RebaseOntoCommit(String),
300
301 /// Revert a specific commit (creates a revert commit).
302 RevertCommit(String),
303
304 /// git reset --soft `oid` — move HEAD, keep staged + working changes.
305 ResetSoft(String),
306 /// git reset --mixed `oid` — move HEAD and unstage; keep working directory.
307 ResetMixed(String),
308 /// git reset --hard `oid` — move HEAD and discard all uncommitted changes.
309 ResetHard(String),
310
311 // ── Shared ───────────────────────────────────────────────────────────────────
312 /// Copy a string to the system clipboard.
313 CopyText(String),
314
315 /// Generic result for any git operation that produces a full repo refresh.
316 /// The operation itself is responsible for a descriptive error string.
317 GitOperationResult(Result<RepoPayload, String>),
318
319 /// User selected a different theme from the picker (by index into
320 /// `gitkraft_core::THEME_NAMES`).
321 ThemeChanged(usize),
322
323 /// User selected a different editor from the picker.
324 EditorChanged(gitkraft_core::Editor),
325 /// Background `save_editor` completed (fire-and-forget, errors logged).
326 EditorSaved(Result<(), String>),
327 /// User clicked a recent repository entry on the welcome screen.
328 OpenRecentRepo(PathBuf),
329 // ── Search ────────────────────────────────────────────────────────────
330 /// Toggle the search overlay.
331 ToggleSearch,
332 /// User typed in the search input.
333 SearchQueryChanged(String),
334 /// Search results arrived from the background.
335 SearchResultsLoaded(Result<Vec<gitkraft_core::CommitInfo>, String>),
336 /// User selected a search result (by index).
337 SelectSearchResult(usize),
338 /// User confirmed the selected search result (Enter).
339 ConfirmSearchResult,
340 /// File list for commit-vs-workdir diff loaded.
341 SearchDiffFilesLoaded(Result<Vec<gitkraft_core::DiffFileEntry>, String>),
342 /// User toggled selection of a file in the search diff file list.
343 ToggleSearchDiffFile(usize),
344 /// User requested to view diff of selected search file against working tree.
345 ViewSearchDiffFile(usize),
346 /// The diff content for a search file loaded.
347 SearchFileDiffLoaded(Result<gitkraft_core::DiffInfo, String>),
348 /// User clicked "Select All" / "Deselect All" in search diff.
349 ToggleSearchDiffSelectAll,
350 /// Diff all selected files against working tree (combined view).
351 DiffSelectedFiles,
352 /// Combined diffs for selected files loaded.
353 SearchMultiDiffLoaded(Result<Vec<gitkraft_core::DiffInfo>, String>),
354 /// Go back from file diff view to file list in search.
355 SearchDiffBack,
356 /// User right-clicked a search result — open commit context menu.
357 OpenSearchResultContextMenu(usize),
358
359 /// File system change detected — auto-refresh staging area.
360 FileSystemChanged,
361
362 /// Open a file in the configured editor.
363 OpenInEditor(String),
364 /// Open a file in the system's default program.
365 OpenInDefaultProgram(String),
366 /// Show a file in the system file manager.
367 ShowInFolder(String),
368
369 /// Keyboard modifier state changed (e.g. Shift pressed/released).
370 ModifiersChanged(iced::keyboard::Modifiers),
371 /// Multiple commit file diffs loaded for a multi-file selection.
372 CommitMultiDiffLoaded(Result<Vec<gitkraft_core::DiffInfo>, String>),
373
374 // ── File history overlay ──────────────────────────────────────────────────
375 /// Open the file-history overlay for a specific repo-relative path.
376 ViewFileHistory(String),
377 /// Background load of file-history commits completed.
378 FileHistoryLoaded(Result<(String, Vec<gitkraft_core::CommitInfo>), String>),
379 /// Close the file-history overlay without taking an action.
380 CloseFileHistory,
381 /// User scrolled the file-history list.
382 FileHistoryScrolled(f32),
383 /// User selected a commit in file history — load its diff and close overlay.
384 SelectFileHistoryCommit(String),
385
386 // ── Blame overlay ─────────────────────────────────────────────────────────
387 /// Open the blame overlay for a specific repo-relative path.
388 ViewFileBlame(String),
389 /// Background load of blame lines completed.
390 FileBlameLoaded(Result<(String, Vec<gitkraft_core::BlameLine>), String>),
391 /// Close the blame overlay.
392 CloseFileBlame,
393 /// User scrolled the blame view.
394 BlameScrolled(f32),
395
396 // ── File deletion ─────────────────────────────────────────────────────────
397 /// Show delete-confirmation banner for a working-tree file.
398 DeleteFile(String),
399 /// User confirmed the deletion.
400 ConfirmDeleteFile,
401 /// User cancelled the deletion.
402 CancelDeleteFile,
403
404 /// Diff multiple files from a specific commit against the current working tree.
405 DiffMultiWithWorkingTree(String, Vec<String>), // (oid, file_paths)
406
407 /// Restore a single file from a specific commit to the working directory.
408 CheckoutFileAtCommit(String, String), // (oid, file_path)
409 /// Restore multiple files from a specific commit to the working directory.
410 CheckoutMultiFilesAtCommit(String, Vec<String>), // (oid, file_paths)
411
412 /// Cherry-pick a list of commits (by OID) onto the current branch.
413 CherryPickCommits(Vec<String>),
414 /// Revert a list of commits (by OID) in order.
415 RevertCommits(Vec<String>),
416
417 /// Combined range diff across multiple selected commits was loaded.
418 CommitRangeDiffLoaded(Result<Vec<gitkraft_core::DiffInfo>, String>),
419
420 /// The application window was resized.
421 WindowResized(f32, f32), // (width, height)
422 /// The application window was moved.
423 WindowMoved(f32, f32), // (x, y)
424
425 /// Open the GUI settings file (settings.json) in the configured editor.
426 /// Triggered by Ctrl/Cmd + , (like Zed).
427 OpenSettingsFile,
428
429 /// Shift+Down arrow — extends range selection in the file list (if files are
430 /// loaded) or in the commit log (fallback).
431 ShiftArrowDown,
432 /// Shift+Up arrow — same as above but upward.
433 ShiftArrowUp,
434
435 /// No-op (used for disabled buttons, etc.).
436 Noop,
437}