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    // ── Commit actions ────────────────────────────────────────────────────────────
288    /// Checkout a specific commit in detached HEAD mode.
289    CheckoutCommitDetached(String),
290
291    /// Rebase the current branch on top of a specific commit.
292    RebaseOntoCommit(String),
293
294    /// Revert a specific commit (creates a revert commit).
295    RevertCommit(String),
296
297    /// git reset --soft `oid` — move HEAD, keep staged + working changes.
298    ResetSoft(String),
299    /// git reset --mixed `oid` — move HEAD and unstage; keep working directory.
300    ResetMixed(String),
301    /// git reset --hard `oid` — move HEAD and discard all uncommitted changes.
302    ResetHard(String),
303
304    // ── Shared ───────────────────────────────────────────────────────────────────
305    /// Copy a string to the system clipboard.
306    CopyText(String),
307
308    /// Generic result for any git operation that produces a full repo refresh.
309    /// The operation itself is responsible for a descriptive error string.
310    GitOperationResult(Result<RepoPayload, String>),
311
312    /// User selected a different theme from the picker (by index into
313    /// `gitkraft_core::THEME_NAMES`).
314    ThemeChanged(usize),
315
316    /// User selected a different editor from the picker.
317    EditorChanged(gitkraft_core::Editor),
318    /// Background `save_editor` completed (fire-and-forget, errors logged).
319    EditorSaved(Result<(), String>),
320    /// User clicked a recent repository entry on the welcome screen.
321    OpenRecentRepo(PathBuf),
322    // ── Search ────────────────────────────────────────────────────────────
323    /// Toggle the search overlay.
324    ToggleSearch,
325    /// User typed in the search input.
326    SearchQueryChanged(String),
327    /// Search results arrived from the background.
328    SearchResultsLoaded(Result<Vec<gitkraft_core::CommitInfo>, String>),
329    /// User selected a search result (by index).
330    SelectSearchResult(usize),
331    /// User confirmed the selected search result (Enter).
332    ConfirmSearchResult,
333    /// User right-clicked a search result — open commit context menu.
334    OpenSearchResultContextMenu(usize),
335
336    /// File system change detected — auto-refresh staging area.
337    FileSystemChanged,
338
339    /// Open a file in the configured editor.
340    OpenInEditor(String),
341    /// Open a file in the system's default program.
342    OpenInDefaultProgram(String),
343    /// Show a file in the system file manager.
344    ShowInFolder(String),
345
346    /// No-op (used for disabled buttons, etc.).
347    Noop,
348}