Add desktop HTTPS Git synchronization

This commit is contained in:
2026-08-10 19:53:00 +02:00
parent 8879df142d
commit 95bb10b4a1
6 changed files with 897 additions and 3 deletions

View File

@@ -32,6 +32,10 @@ pub enum UiAction {
CopyEntry,
DeleteEntry,
ToggleReveal,
GitStatus,
GitPull,
GitPush,
GitSync,
Lock,
Minimize,
Help,
@@ -68,6 +72,10 @@ impl UiAction {
Self::CopyEntry => "copy-entry",
Self::DeleteEntry => "delete-entry",
Self::ToggleReveal => "toggle-reveal",
Self::GitStatus => "git-status",
Self::GitPull => "git-pull",
Self::GitPush => "git-push",
Self::GitSync => "git-sync",
Self::Lock => "lock",
Self::Minimize => "minimize",
Self::Help => "help",
@@ -129,6 +137,7 @@ pub struct ActionContext {
pub focused_generatable: bool,
pub entry_path: bool,
pub selected_object: bool,
pub git_running: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -228,6 +237,10 @@ pub const ACTIONS: &[ActionSpec] = &[
"Reveal or Hide Field",
None,
),
spec(UiAction::GitStatus, MenuGroup::Tools, "Git Status…", None),
spec(UiAction::GitPull, MenuGroup::Tools, "Pull", None),
spec(UiAction::GitPush, MenuGroup::Tools, "Push", None),
spec(UiAction::GitSync, MenuGroup::Tools, "Synchronize", None),
spec(UiAction::Lock, MenuGroup::Tools, "Lock", Some("⌘L")),
spec(
UiAction::Minimize,
@@ -278,6 +291,9 @@ pub fn shortcut_label(action: UiAction) -> Option<String> {
}
pub fn enabled(action: UiAction, context: ActionContext) -> bool {
if context.git_running && !matches!(action, UiAction::Lock | UiAction::Minimize) {
return false;
}
match action {
UiAction::About | UiAction::Help => !context.modal_open,
UiAction::Settings => {
@@ -362,6 +378,13 @@ pub fn enabled(action: UiAction, context: ActionContext) -> bool {
&& !context.switching_vault
&& context.focused_sensitive
}
UiAction::GitStatus | UiAction::GitPull | UiAction::GitPush | UiAction::GitSync => {
context.storage_ready
&& !context.saving
&& !context.switching_vault
&& !context.git_running
&& !context.modal_open
}
UiAction::Lock => context.unlocked,
}
}
@@ -370,6 +393,9 @@ pub fn disabled_reason(action: UiAction, context: ActionContext) -> Option<&'sta
if enabled(action, context) {
return None;
}
if context.git_running && !matches!(action, UiAction::Lock | UiAction::Minimize) {
return Some("Cancel or wait for the active Git operation");
}
Some(match action {
UiAction::InitializeStore | UiAction::NewFolder | UiAction::NewEntry
if !context.storage_ready =>
@@ -471,6 +497,29 @@ pub fn disabled_reason(action: UiAction, context: ActionContext) -> Option<&'sta
UiAction::ToggleReveal if !context.document_open => "Open an entry first",
UiAction::ToggleReveal if !context.focused_sensitive => "Select a sensitive field first",
UiAction::ToggleReveal => "Wait for vault validation",
UiAction::GitStatus | UiAction::GitPull | UiAction::GitPush | UiAction::GitSync
if !context.storage_ready =>
{
"Shared configuration is unavailable"
}
UiAction::GitStatus | UiAction::GitPull | UiAction::GitPush | UiAction::GitSync
if context.saving =>
{
"Wait for the active save"
}
UiAction::GitStatus | UiAction::GitPull | UiAction::GitPush | UiAction::GitSync
if context.switching_vault =>
{
"Wait for vault validation"
}
UiAction::GitStatus | UiAction::GitPull | UiAction::GitPush | UiAction::GitSync
if context.git_running =>
{
"A Git operation is already running"
}
UiAction::GitStatus | UiAction::GitPull | UiAction::GitPush | UiAction::GitSync => {
"Close the current screen first"
}
UiAction::Lock => "The password store is already locked",
UiAction::CommandPalette => "Finish the current confirmation first",
UiAction::Settings if !context.storage_ready => "Shared configuration is unavailable",
@@ -512,6 +561,10 @@ pub const fn aliases(action: UiAction) -> &'static [&'static str] {
UiAction::CopyEntry => &["duplicate entry", "copy folder", "pass cp"],
UiAction::DeleteEntry => &["remove", "rm", "delete folder"],
UiAction::ToggleReveal => &["show password", "hide password", "reveal field"],
UiAction::GitStatus => &["repository status", "git history"],
UiAction::GitPull => &["fetch", "download changes"],
UiAction::GitPush => &["upload changes"],
UiAction::GitSync => &["synchronize", "pull and push"],
UiAction::Lock => &["secure", "log out", "relock"],
UiAction::Minimize => &["hide window"],
UiAction::Help => &["shortcuts", "documentation"],
@@ -572,6 +625,7 @@ mod tests {
focused_generatable: true,
entry_path: true,
selected_object: true,
git_running: false,
}
}
@@ -599,6 +653,14 @@ mod tests {
assert!(enabled(UiAction::GeneratePassword, ready));
assert!(!enabled(UiAction::CopyField, ready));
assert!(enabled(UiAction::ToggleReveal, ready));
for action in [
UiAction::GitStatus,
UiAction::GitPull,
UiAction::GitPush,
UiAction::GitSync,
] {
assert!(enabled(action, ready));
}
let viewing = ActionContext {
editing: false,
@@ -631,6 +693,9 @@ mod tests {
UiAction::MoveEntry,
UiAction::CopyEntry,
UiAction::DeleteEntry,
UiAction::GitPull,
UiAction::GitPush,
UiAction::GitSync,
] {
assert!(
enabled(action, locked),
@@ -708,6 +773,19 @@ mod tests {
),
Some("Finish the current confirmation first")
);
let git_running = ActionContext {
git_running: true,
..ready
};
assert!(enabled(UiAction::Lock, git_running));
assert!(enabled(UiAction::Minimize, git_running));
assert!(!enabled(UiAction::GitStatus, git_running));
assert!(!enabled(UiAction::CloseWindow, git_running));
assert_eq!(
disabled_reason(UiAction::CloseWindow, git_running),
Some("Cancel or wait for the active Git operation")
);
let modal = ActionContext {
modal_open: true,
..ready