Skip to main content

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    /// Pop (apply + drop) the most recent stash — shortcut from the staging area.
149    StashPopFirst,
150    /// Async stash operation completed.
151    StashUpdated(Result<Vec<StashEntry>, String>),
152    /// User is typing in the stash-message input.
153    StashMessageChanged(String),
154    /// User right-clicked a stash entry.
155    OpenStashContextMenu(usize),
156    /// User right-clicked a file in the commit diff file list.
157    OpenCommitFileContextMenu(String, String), // (oid, file_path)
158    /// User right-clicked an unstaged file.
159    OpenUnstagedFileContextMenu(String),
160    /// User right-clicked a staged file.
161    OpenStagedFileContextMenu(String),
162    /// User wants to view the diff of a stash entry.
163    ViewStashDiff(usize),
164    /// Stash diff loaded.
165    StashDiffLoaded(Result<Vec<DiffInfo>, String>),
166    /// Apply a stash (like pop but keeps the stash).
167    StashApply(usize),
168
169    // ── Remotes ───────────────────────────────────────────────────────────
170    /// Fetch from the first configured remote.
171    Fetch,
172    /// Async fetch completed.
173    FetchCompleted(Result<(), String>),
174
175    // ── UI ────────────────────────────────────────────────────────────────
176    /// User clicked a file in the commit-diff file list (by index into `commit_files`).
177    SelectDiffByIndex(usize),
178    /// User clicked a file in the staging area to view its diff.
179    SelectDiff(DiffInfo),
180    /// Dismiss the current error banner.
181    DismissError,
182    /// Zoom in (increase UI scale).
183    ZoomIn,
184    /// Zoom out (decrease UI scale).
185    ZoomOut,
186    /// Reset zoom to 100%.
187    ZoomReset,
188    /// Toggle the left sidebar.
189    ToggleSidebar,
190    /// Close the current repository and return to the welcome screen.
191    CloseRepo,
192
193    // ── Pane resize ───────────────────────────────────────────────────────
194    /// User pressed the mouse button on a vertical divider to start dragging.
195    PaneDragStart(crate::state::DragTarget, f32),
196    /// User pressed the mouse button on the horizontal staging divider.
197    PaneDragStartH(crate::state::DragTargetH, f32),
198    /// Mouse moved during a drag — `(x, y)` in window coordinates.
199    PaneDragMove(f32, f32),
200    /// Mouse button released — stop dragging.
201    PaneDragEnd,
202    // ── Async persistence results ─────────────────────────────────────
203    /// Background `record_repo_opened` + `load_settings` completed.
204    /// Carries the refreshed recent-repos list (or an error string).
205    RepoRecorded(Result<Vec<gitkraft_core::RepoHistoryEntry>, String>),
206    /// Background `load_settings` completed (e.g. after closing a repo).
207    SettingsLoaded(Result<Vec<gitkraft_core::RepoHistoryEntry>, String>),
208    /// Background `save_theme` completed (fire-and-forget, errors logged).
209    ThemeSaved(Result<(), String>),
210    /// Background layout save completed (fire-and-forget, errors logged).
211    LayoutSaved(Result<(), String>),
212    /// Layout loaded from persisted settings on startup.
213    LayoutLoaded(Result<Option<gitkraft_core::LayoutSettings>, String>),
214    /// Background session save completed (fire-and-forget).
215    SessionSaved(Result<(), String>),
216    /// Async restore of a specific tab (by index) completed on startup.
217    RepoRestoredAt(usize, Result<RepoPayload, String>),
218
219    // ── Context menus ─────────────────────────────────────────────────────────────
220    /// User right-clicked a local branch.
221    /// Payload: (branch_name, index_in_local_list, is_current_branch).
222    OpenBranchContextMenu(String, usize, bool),
223
224    /// User right-clicked a remote branch.
225    OpenRemoteBranchContextMenu(String),
226
227    /// Checkout a remote branch (creates local tracking branch).
228    CheckoutRemoteBranch(String),
229
230    /// Delete a remote branch.
231    DeleteRemoteBranch(String),
232    /// User right-clicked a commit row.
233    OpenCommitContextMenu(usize),
234
235    /// Dismiss the context menu without taking an action.
236    CloseContextMenu,
237
238    // ── Branch actions ────────────────────────────────────────────────────────────
239    /// Push the named branch to its default remote.
240    PushBranch(String),
241
242    /// Force-push the current branch to its remote (with --force-with-lease).
243    ForcePushBranch,
244
245    /// Pull the current branch from its remote, rebasing local commits on top.
246    PullBranch(String),
247
248    /// Rebase the current HEAD onto `target` (a branch name or OID string).
249    RebaseOnto(String),
250
251    /// Begin an inline rename: record the branch being renamed and pre-fill input.
252    BeginRenameBranch(String),
253
254    /// User is typing in the rename input.
255    RenameBranchInputChanged(String),
256
257    /// User confirmed the rename.
258    ConfirmRenameBranch,
259
260    /// User cancelled the rename.
261    CancelRename,
262
263    /// Merge a named branch into the current HEAD branch.
264    MergeBranch(String),
265
266    /// Begin an inline tag-creation form at the given commit OID.
267    /// The bool indicates whether this is an annotated tag (true) or lightweight (false).
268    BeginCreateTag(String, bool),
269
270    /// User is typing in the tag name input.
271    TagNameChanged(String),
272
273    /// User is typing in the annotated tag message input.
274    TagMessageChanged(String),
275
276    /// User confirmed tag creation.
277    ConfirmCreateTag,
278
279    /// User cancelled tag creation.
280    CancelCreateTag,
281
282    /// Cherry-pick a commit by OID onto the current branch.
283    CherryPickCommit(String),
284
285    /// Begin an inline "create branch at this commit" form.
286    BeginCreateBranchAtCommit(String),
287
288    /// User confirmed branch creation at a specific commit.
289    ConfirmCreateBranchAtCommit,
290
291    /// User cancelled branch creation at a specific commit.
292    CancelCreateBranchAtCommit,
293
294    /// Execute a commit action that requires no further user input.
295    /// Actions that need input (branch name, tag name) continue to use the
296    /// existing `BeginCreate*` messages.
297    ExecuteCommitAction(String, gitkraft_core::CommitAction),
298
299    // ── Commit actions ────────────────────────────────────────────────────────────
300    /// Checkout a specific commit in detached HEAD mode.
301    CheckoutCommitDetached(String),
302
303    /// Rebase the current branch on top of a specific commit.
304    RebaseOntoCommit(String),
305
306    /// Revert a specific commit (creates a revert commit).
307    RevertCommit(String),
308
309    /// git reset --soft `oid` — move HEAD, keep staged + working changes.
310    ResetSoft(String),
311    /// git reset --mixed `oid` — move HEAD and unstage; keep working directory.
312    ResetMixed(String),
313    /// git reset --hard `oid` — move HEAD and discard all uncommitted changes.
314    ResetHard(String),
315
316    // ── Shared ───────────────────────────────────────────────────────────────────
317    /// Copy a string to the system clipboard.
318    CopyText(String),
319
320    /// Generic result for any git operation that produces a full repo refresh.
321    /// The operation itself is responsible for a descriptive error string.
322    GitOperationResult(Result<RepoPayload, String>),
323
324    /// User selected a different theme from the picker (by index into
325    /// `gitkraft_core::THEME_NAMES`).
326    ThemeChanged(usize),
327
328    /// User selected a different editor from the picker.
329    EditorChanged(gitkraft_core::Editor),
330    /// Background `save_editor` completed (fire-and-forget, errors logged).
331    EditorSaved(Result<(), String>),
332    /// User clicked a recent repository entry on the welcome screen.
333    OpenRecentRepo(PathBuf),
334    // ── Search ────────────────────────────────────────────────────────────
335    /// Toggle the search overlay.
336    ToggleSearch,
337    /// User typed in the search input.
338    SearchQueryChanged(String),
339    /// Search results arrived from the background.
340    SearchResultsLoaded(Result<Vec<gitkraft_core::CommitInfo>, String>),
341    /// User selected a search result (by index).
342    SelectSearchResult(usize),
343    /// User confirmed the selected search result (Enter).
344    ConfirmSearchResult,
345    /// File list for commit-vs-workdir diff loaded.
346    SearchDiffFilesLoaded(Result<Vec<gitkraft_core::DiffFileEntry>, String>),
347    /// User toggled selection of a file in the search diff file list.
348    ToggleSearchDiffFile(usize),
349    /// User requested to view diff of selected search file against working tree.
350    ViewSearchDiffFile(usize),
351    /// The diff content for a search file loaded.
352    SearchFileDiffLoaded(Result<gitkraft_core::DiffInfo, String>),
353    /// User clicked "Select All" / "Deselect All" in search diff.
354    ToggleSearchDiffSelectAll,
355    /// Diff all selected files against working tree (combined view).
356    DiffSelectedFiles,
357    /// Combined diffs for selected files loaded.
358    SearchMultiDiffLoaded(Result<Vec<gitkraft_core::DiffInfo>, String>),
359    /// Go back from file diff view to file list in search.
360    SearchDiffBack,
361    /// User right-clicked a search result — open commit context menu.
362    OpenSearchResultContextMenu(usize),
363
364    /// File system change detected — auto-refresh staging area.
365    FileSystemChanged,
366
367    /// Open a file in the configured editor.
368    OpenInEditor(String),
369    /// Open multiple files in the configured editor.
370    OpenFilesInEditor(Vec<String>),
371    /// Open a file in the system's default program.
372    OpenInDefaultProgram(String),
373    /// Open multiple files in the system's default program.
374    OpenFilesInDefaultProgram(Vec<String>),
375    /// Preview a file's content in the diff pane (read-only).
376    PreviewFile(String),
377    /// Result of reading a file for preview.
378    FilePreviewLoaded(Result<(String, String), String>),
379    /// Show a file in the system file manager.
380    ShowInFolder(String),
381
382    /// Keyboard modifier state changed (e.g. Shift pressed/released).
383    ModifiersChanged(iced::keyboard::Modifiers),
384    /// Multiple commit file diffs loaded for a multi-file selection.
385    CommitMultiDiffLoaded(Result<Vec<gitkraft_core::DiffInfo>, String>),
386
387    // ── File history overlay ──────────────────────────────────────────────────
388    /// Open the file-history overlay for a specific repo-relative path.
389    ViewFileHistory(String),
390    /// Background load of file-history commits completed.
391    FileHistoryLoaded(Result<(String, Vec<gitkraft_core::CommitInfo>), String>),
392    /// Close the file-history overlay without taking an action.
393    CloseFileHistory,
394    /// User scrolled the file-history list.
395    FileHistoryScrolled(f32),
396    /// User selected a commit in file history — load its diff and close overlay.
397    SelectFileHistoryCommit(String),
398
399    // ── Blame overlay ─────────────────────────────────────────────────────────
400    /// Open the blame overlay for a specific repo-relative path.
401    ViewFileBlame(String),
402    /// Background load of blame lines completed.
403    FileBlameLoaded(Result<(String, Vec<gitkraft_core::BlameLine>), String>),
404    /// Close the blame overlay.
405    CloseFileBlame,
406    /// User scrolled the blame view.
407    BlameScrolled(f32),
408
409    // ── File deletion ─────────────────────────────────────────────────────────
410    /// Show delete-confirmation banner for a working-tree file.
411    DeleteFile(String),
412    /// User confirmed the deletion.
413    ConfirmDeleteFile,
414    /// User cancelled the deletion.
415    CancelDeleteFile,
416
417    /// Diff multiple files from a specific commit against the current working tree.
418    DiffMultiWithWorkingTree(String, Vec<String>), // (oid, file_paths)
419
420    /// Restore a single file from a specific commit to the working directory.
421    CheckoutFileAtCommit(String, String), // (oid, file_path)
422    /// Restore multiple files from a specific commit to the working directory.
423    CheckoutMultiFilesAtCommit(String, Vec<String>), // (oid, file_paths)
424
425    /// Cherry-pick a list of commits (by OID) onto the current branch.
426    CherryPickCommits(Vec<String>),
427    /// Revert a list of commits (by OID) in order.
428    RevertCommits(Vec<String>),
429
430    /// Combined range diff across multiple selected commits was loaded.
431    CommitRangeDiffLoaded(Result<Vec<gitkraft_core::DiffInfo>, String>),
432
433    /// The application window was resized.
434    WindowResized(f32, f32), // (width, height)
435    /// The application window was moved.
436    WindowMoved(f32, f32), // (x, y)
437
438    /// Open the GUI settings file (settings.json) in the configured editor.
439    /// Triggered by Ctrl/Cmd + , (like Zed).
440    OpenSettingsFile,
441
442    /// Shift+Down arrow — extends range selection in the file list (if files are
443    /// loaded) or in the commit log (fallback).
444    ShiftArrowDown,
445    /// Shift+Up arrow — same as above but upward.
446    ShiftArrowUp,
447
448    /// Jump to the first (newest) commit in the log.
449    JumpToFirstCommit,
450    /// Jump to the last (oldest) commit in the log.
451    JumpToLastCommit,
452
453    /// Toggle whether the currently selected commit is in the multi-selection set.
454    ToggleCommitSelection,
455
456    /// Fired ~10× per second to advance loading-spinner animation frames.
457    /// Only emitted while at least one tab is loading.
458    AnimationTick,
459
460    /// No-op (used for disabled buttons, etc.).
461    Noop,
462}