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