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    /// 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}