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::{
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    /// Cherry-pick a commit by OID onto the current branch.
288    CherryPickCommit(String),
289
290    /// Begin an inline "create branch at this commit" form.
291    BeginCreateBranchAtCommit(String),
292
293    /// User confirmed branch creation at a specific commit.
294    ConfirmCreateBranchAtCommit,
295
296    /// User cancelled branch creation at a specific commit.
297    CancelCreateBranchAtCommit,
298
299    /// Execute a commit action that requires no further user input.
300    /// Actions that need input (branch name, tag name) continue to use the
301    /// existing `BeginCreate*` messages.
302    ExecuteCommitAction(String, gitkraft_core::CommitAction),
303
304    // ── Commit actions ────────────────────────────────────────────────────────────
305    /// Checkout a specific commit in detached HEAD mode.
306    CheckoutCommitDetached(String),
307
308    /// Rebase the current branch on top of a specific commit.
309    RebaseOntoCommit(String),
310
311    /// Revert a specific commit (creates a revert commit).
312    RevertCommit(String),
313
314    /// git reset --soft `oid` — move HEAD, keep staged + working changes.
315    ResetSoft(String),
316    /// git reset --mixed `oid` — move HEAD and unstage; keep working directory.
317    ResetMixed(String),
318    /// git reset --hard `oid` — move HEAD and discard all uncommitted changes.
319    ResetHard(String),
320
321    // ── Shared ───────────────────────────────────────────────────────────────────
322    /// Copy a string to the system clipboard.
323    CopyText(String),
324
325    /// Generic result for any git operation that produces a full repo refresh.
326    /// The operation itself is responsible for a descriptive error string.
327    GitOperationResult(Result<RepoPayload, String>),
328
329    /// User selected a different theme from the picker (by index into
330    /// `gitkraft_core::THEME_NAMES`).
331    ThemeChanged(usize),
332
333    /// User selected a different editor from the picker.
334    EditorChanged(gitkraft_core::Editor),
335    /// Background `save_editor` completed (fire-and-forget, errors logged).
336    EditorSaved(Result<(), String>),
337    /// User clicked a recent repository entry on the welcome screen.
338    OpenRecentRepo(PathBuf),
339    // ── Search ────────────────────────────────────────────────────────────
340    /// Toggle the search overlay.
341    ToggleSearch,
342    /// User typed in the search input.
343    SearchQueryChanged(String),
344    /// Search results arrived from the background.
345    SearchResultsLoaded(Result<Vec<gitkraft_core::CommitInfo>, String>),
346    /// User selected a search result (by index).
347    SelectSearchResult(usize),
348    /// User confirmed the selected search result (Enter).
349    ConfirmSearchResult,
350    /// File list for commit-vs-workdir diff loaded.
351    SearchDiffFilesLoaded(Result<Vec<gitkraft_core::DiffFileEntry>, String>),
352    /// User toggled selection of a file in the search diff file list.
353    ToggleSearchDiffFile(usize),
354    /// User requested to view diff of selected search file against working tree.
355    ViewSearchDiffFile(usize),
356    /// The diff content for a search file loaded.
357    SearchFileDiffLoaded(Result<gitkraft_core::DiffInfo, String>),
358    /// User clicked "Select All" / "Deselect All" in search diff.
359    ToggleSearchDiffSelectAll,
360    /// Diff all selected files against working tree (combined view).
361    DiffSelectedFiles,
362    /// Combined diffs for selected files loaded.
363    SearchMultiDiffLoaded(Result<Vec<gitkraft_core::DiffInfo>, String>),
364    /// Go back from file diff view to file list in search.
365    SearchDiffBack,
366    /// User right-clicked a search result — open commit context menu.
367    OpenSearchResultContextMenu(usize),
368
369    /// File system change detected — auto-refresh staging area.
370    FileSystemChanged,
371
372    /// Open a file in the configured editor.
373    OpenInEditor(String),
374    /// Open a file in the system's default program.
375    OpenInDefaultProgram(String),
376    /// Show a file in the system file manager.
377    ShowInFolder(String),
378
379    /// Keyboard modifier state changed (e.g. Shift pressed/released).
380    ModifiersChanged(iced::keyboard::Modifiers),
381    /// Multiple commit file diffs loaded for a multi-file selection.
382    CommitMultiDiffLoaded(Result<Vec<gitkraft_core::DiffInfo>, String>),
383
384    /// Diff multiple files from a specific commit against the current working tree.
385    DiffMultiWithWorkingTree(String, Vec<String>), // (oid, file_paths)
386
387    /// Restore a single file from a specific commit to the working directory.
388    CheckoutFileAtCommit(String, String), // (oid, file_path)
389    /// Restore multiple files from a specific commit to the working directory.
390    CheckoutMultiFilesAtCommit(String, Vec<String>), // (oid, file_paths)
391
392    /// Cherry-pick a list of commits (by OID) onto the current branch.
393    CherryPickCommits(Vec<String>),
394    /// Revert a list of commits (by OID) in order.
395    RevertCommits(Vec<String>),
396
397    /// Combined range diff across multiple selected commits was loaded.
398    CommitRangeDiffLoaded(Result<Vec<gitkraft_core::DiffInfo>, String>),
399
400    /// No-op (used for disabled buttons, etc.).
401    Noop,
402}