Add Git worktree pane
This commit is contained in:
85
src/app.rs
85
src/app.rs
@@ -1,4 +1,5 @@
|
||||
mod generation;
|
||||
mod git;
|
||||
mod model_manager;
|
||||
mod preferences;
|
||||
mod projects;
|
||||
@@ -7,6 +8,7 @@ mod view;
|
||||
pub(crate) use view::app_theme;
|
||||
|
||||
use generation::ChatMessage;
|
||||
use git::{ActiveGitOperation, GitDiff, GitDiffMode, GitWorktree};
|
||||
use model_manager::{ActiveDownload, ModelDownload, ModelOperation};
|
||||
use preferences::PreferenceDraft;
|
||||
#[cfg(test)]
|
||||
@@ -41,6 +43,7 @@ const APP_ID: &str = "de.rfc1437.ds4server";
|
||||
const METRICS_SAMPLE_INTERVAL: Duration = Duration::from_millis(200);
|
||||
/// Disc scans are cheap but not free; the explorer does not need 5 Hz.
|
||||
const CACHE_SCAN_INTERVAL: Duration = Duration::from_secs(2);
|
||||
const GIT_SCAN_INTERVAL: Duration = Duration::from_secs(2);
|
||||
pub(super) const MIN_SIDEBAR_WIDTH: i32 = 180;
|
||||
pub(super) const MAX_SIDEBAR_WIDTH: i32 = 520;
|
||||
|
||||
@@ -67,6 +70,15 @@ pub(crate) struct App {
|
||||
/// when its first chat turn is stored, so empty ones vanish on restart.
|
||||
drafts: HashMap<i32, String>,
|
||||
git_states: HashMap<i32, GitState>,
|
||||
git_worktrees: HashMap<i32, GitWorktree>,
|
||||
git_selected_project: Option<i32>,
|
||||
git_selected_files: HashSet<PathBuf>,
|
||||
git_commit_message: String,
|
||||
git_diff: Option<GitDiff>,
|
||||
git_diff_mode: GitDiffMode,
|
||||
git_commit_all_confirmation: bool,
|
||||
git_operation: Option<ActiveGitOperation>,
|
||||
last_git_scan: Instant,
|
||||
#[cfg(target_os = "macos")]
|
||||
background_chats: HashMap<i32, ChatSnapshot>,
|
||||
/// Session whose quick-actions menu is open.
|
||||
@@ -227,6 +239,7 @@ pub(super) enum DetailTab {
|
||||
#[default]
|
||||
Chat,
|
||||
A2ui,
|
||||
Git,
|
||||
Stats,
|
||||
}
|
||||
|
||||
@@ -401,6 +414,18 @@ pub(crate) enum Message {
|
||||
CreateSession(i32),
|
||||
DraftProjectChanged(i32),
|
||||
SwitchGitBranch(String),
|
||||
ToggleGitFile(PathBuf),
|
||||
OpenGitDiff(PathBuf),
|
||||
SetGitDiffMode(GitDiffMode),
|
||||
GitCommitMessageChanged(String),
|
||||
GitStageSelected,
|
||||
GitUnstageSelected,
|
||||
GitCommit,
|
||||
ConfirmGitCommitAll,
|
||||
GitFetch,
|
||||
GitPull,
|
||||
GitPush,
|
||||
GitOperationTick,
|
||||
DiscardSession(i32),
|
||||
SelectSession(i32, i32),
|
||||
RequestDeleteSession(i32),
|
||||
@@ -416,6 +441,7 @@ pub(crate) enum Message {
|
||||
ToggleArchivedSessions(i32),
|
||||
ShowChat,
|
||||
ShowA2ui,
|
||||
ShowGit,
|
||||
ShowStats,
|
||||
ToggleSidebar,
|
||||
StartSidebarDrag,
|
||||
@@ -479,6 +505,15 @@ impl App {
|
||||
selected_session: None,
|
||||
drafts,
|
||||
git_states: HashMap::new(),
|
||||
git_worktrees: HashMap::new(),
|
||||
git_selected_project: None,
|
||||
git_selected_files: HashSet::new(),
|
||||
git_commit_message: String::new(),
|
||||
git_diff: None,
|
||||
git_diff_mode: GitDiffMode::default(),
|
||||
git_commit_all_confirmation: false,
|
||||
git_operation: None,
|
||||
last_git_scan: Instant::now() - GIT_SCAN_INTERVAL,
|
||||
#[cfg(target_os = "macos")]
|
||||
background_chats: HashMap::new(),
|
||||
session_menu: None,
|
||||
@@ -612,6 +647,15 @@ impl App {
|
||||
selected_session: None,
|
||||
drafts: HashMap::new(),
|
||||
git_states: HashMap::new(),
|
||||
git_worktrees: HashMap::new(),
|
||||
git_selected_project: None,
|
||||
git_selected_files: HashSet::new(),
|
||||
git_commit_message: String::new(),
|
||||
git_diff: None,
|
||||
git_diff_mode: GitDiffMode::default(),
|
||||
git_commit_all_confirmation: false,
|
||||
git_operation: None,
|
||||
last_git_scan: Instant::now() - GIT_SCAN_INTERVAL,
|
||||
#[cfg(target_os = "macos")]
|
||||
background_chats: HashMap::new(),
|
||||
session_menu: None,
|
||||
@@ -987,6 +1031,10 @@ impl App {
|
||||
Message::DismissPanel => {
|
||||
if self.quit_confirmation {
|
||||
self.quit_confirmation = false;
|
||||
} else if self.git_diff.is_some() {
|
||||
self.git_diff = None;
|
||||
} else if self.git_commit_all_confirmation {
|
||||
self.git_commit_all_confirmation = false;
|
||||
} else if self.pending_session_delete.is_some() {
|
||||
self.pending_session_delete = None;
|
||||
} else if self.pending_a2ui_dismissal.is_some() {
|
||||
@@ -1012,6 +1060,13 @@ impl App {
|
||||
return scroll_chat_to_end();
|
||||
}
|
||||
Message::ShowA2ui => self.detail_tab = DetailTab::A2ui,
|
||||
Message::ShowGit => {
|
||||
self.detail_tab = DetailTab::Git;
|
||||
if let Some(project_id) = self.selected_project {
|
||||
self.refresh_git_state();
|
||||
self.refresh_git_worktree(project_id);
|
||||
}
|
||||
}
|
||||
Message::ShowStats => {
|
||||
self.detail_tab = DetailTab::Stats;
|
||||
self.scan_kv_cache();
|
||||
@@ -1625,6 +1680,11 @@ impl App {
|
||||
}
|
||||
self.drafts.remove(&project_id);
|
||||
self.git_states.remove(&project_id);
|
||||
self.git_worktrees.remove(&project_id);
|
||||
if self.git_selected_project == Some(project_id) {
|
||||
self.git_selected_project = None;
|
||||
self.git_selected_files.clear();
|
||||
}
|
||||
if self.config.interface.last_project_id == Some(project_id) {
|
||||
self.config.interface.last_project_id = None;
|
||||
let _ = self.config.save(&config_path());
|
||||
@@ -1656,6 +1716,18 @@ impl App {
|
||||
}
|
||||
Message::DraftProjectChanged(project_id) => self.move_draft_to_project(project_id),
|
||||
Message::SwitchGitBranch(branch) => self.switch_git_branch(&branch),
|
||||
Message::ToggleGitFile(path) => self.toggle_git_file(path),
|
||||
Message::OpenGitDiff(path) => self.open_git_diff(&path),
|
||||
Message::SetGitDiffMode(mode) => self.git_diff_mode = mode,
|
||||
Message::GitCommitMessageChanged(message) => self.git_commit_message = message,
|
||||
Message::GitStageSelected => self.stage_selected_git_files(),
|
||||
Message::GitUnstageSelected => self.unstage_selected_git_files(),
|
||||
Message::GitCommit => self.commit_git(),
|
||||
Message::ConfirmGitCommitAll => self.confirm_commit_all_git(),
|
||||
Message::GitFetch => self.start_git_remote("fetch"),
|
||||
Message::GitPull => self.start_git_remote("pull"),
|
||||
Message::GitPush => self.start_git_remote("push"),
|
||||
Message::GitOperationTick => self.poll_git_operation(),
|
||||
Message::DiscardSession(project_id) => self.discard_session(project_id),
|
||||
Message::OpenSessionMenu(session_id) => {
|
||||
self.session_rename = None;
|
||||
@@ -1961,6 +2033,11 @@ impl App {
|
||||
iced::time::every(Duration::from_secs(1)).map(|_| Message::DownloadProgressTick),
|
||||
);
|
||||
}
|
||||
if self.git_operation.is_some() {
|
||||
subscriptions.push(
|
||||
iced::time::every(Duration::from_millis(100)).map(|_| Message::GitOperationTick),
|
||||
);
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
let titling = self.active_titling.is_some();
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
@@ -2054,6 +2131,14 @@ impl App {
|
||||
{
|
||||
self.scan_kv_cache();
|
||||
}
|
||||
if self.detail_tab == DetailTab::Git
|
||||
&& self.git_operation.is_none()
|
||||
&& self.last_git_scan.elapsed() >= GIT_SCAN_INTERVAL
|
||||
&& let Some(project_id) = self.selected_project
|
||||
{
|
||||
self.refresh_git_state();
|
||||
self.refresh_git_worktree(project_id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes one cache file. Checkpoints are disposable: the next turn on that
|
||||
|
||||
Reference in New Issue
Block a user