Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 1 addition & 41 deletions Backend/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,44 +18,6 @@ use serde::{Deserialize, Serialize};
/// Default number of recent repositories stored when settings are missing or invalid.
pub const MAX_RECENTS: usize = 10;

/// Applies Git SSH-related environment variables from current settings.
///
/// # Parameters
/// - `cfg`: Current app configuration.
///
/// # Returns
/// - `()`.
fn apply_git_ssh_env(cfg: &AppConfig) {
// Prefer config-driven runtime env so the VCS backend (in another crate) can read it.
// Keep env var names stable for packaging and troubleshooting.
unsafe {
// Safety: OpenVCS sets these env vars during startup/config updates and treats them as
// process-wide configuration for child processes (e.g. `git`).
std::env::set_var(
"OPENVCS_SSH_MODE",
match cfg.git.ssh_binary {
crate::settings::GitSshBinary::Auto => "auto",
crate::settings::GitSshBinary::Host => "host",
crate::settings::GitSshBinary::Bundled => "bundled",
crate::settings::GitSshBinary::Custom => "custom",
},
);
}
if cfg.git.ssh_binary == crate::settings::GitSshBinary::Custom
&& !cfg.git.ssh_path.trim().is_empty()
{
unsafe {
// Safety: see comment above.
std::env::set_var("OPENVCS_SSH", cfg.git.ssh_path.trim());
}
} else {
unsafe {
// Safety: see comment above.
std::env::remove_var("OPENVCS_SSH");
}
}
}

/// Central application state.
/// Keeps track of the currently open repo and MRU recents.
/// Backend choice is tied to each repo (via `Repo::id()`), not stored globally.
Expand Down Expand Up @@ -84,10 +46,9 @@ impl AppState {
/// Creates app state by loading persisted settings and recent repositories.
///
/// # Returns
/// - A fully initialized [`AppState`] with config, recents, and runtime env applied.
/// - A fully initialized [`AppState`] with config and recent repositories loaded.
pub fn new_with_config() -> Self {
let cfg = AppConfig::load_or_default(); // reads ~/.config/openvcs/openvcs.conf
apply_git_ssh_env(&cfg);
let s = Self {
config: RwLock::new(cfg),
repo_config: RwLock::new(RepoConfig::default()),
Expand Down Expand Up @@ -125,7 +86,6 @@ impl AppState {
next.migrate();
next.validate();
next.save().map_err(|e| e.to_string())?;
apply_git_ssh_env(&next);
crate::monitoring::sync_backend_monitoring(&next);
*self.config.write() = next;
self.enforce_recents_limit_and_persist();
Expand Down
62 changes: 33 additions & 29 deletions Frontend/src/scripts/features/diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,37 +50,41 @@ export function bindCommit() {
const selLines = linesMap[path] || {};
combinedPatch += buildPatchForSelected(path, lines, selHunks, selLines) + '\n';
}
if (TAURI.has) {
if (combinedPatch.trim().length > 0 || selectedFiles.length > 0) {
const hookData = {
summary,
description,
branch: state.branch,
files: selectedFiles,
stagedFiles: stagePaths,
partialFiles,
patch: combinedPatch,
};
const pre = await runHook('preCommit', hookData);
if (pre.cancelled) {
notify(pre.reason || 'Commit cancelled');
clearBusy('Ready');
return;
}
summary = String(hookData.summary || '').trim() || summary;
description = String(hookData.description || '');
await TAURI.invoke('commit_patch_and_files', {
summary,
description,
patch: combinedPatch,
files: selectedFiles,
stagePaths,
});
await runHook('onCommit', hookData);
} else {
notify('Select files or hunks to commit');
if (!TAURI.has) {
notify('Commit is available in the desktop app');
clearBusy('Ready');
return;
}
Comment thread
Jordonbc marked this conversation as resolved.
Outdated
if (combinedPatch.trim().length > 0 || selectedFiles.length > 0) {
const hookData = {
summary,
description,
branch: state.branch,
files: selectedFiles,
stagedFiles: stagePaths,
partialFiles,
patch: combinedPatch,
};
const pre = await runHook('preCommit', hookData);
if (pre.cancelled) {
notify(pre.reason || 'Commit cancelled');
clearBusy('Ready');
return;
}
summary = String(hookData.summary || '').trim() || summary;
description = String(hookData.description || '');
await TAURI.invoke('commit_patch_and_files', {
summary,
description,
patch: combinedPatch,
files: selectedFiles,
stagePaths,
});
await runHook('onCommit', hookData);
}
else {
notify('Select files or hunks to commit');
return;
}
notify(`Committed to ${state.branch}: ${summary}`);
if (commitSummary) commitSummary.value = '';
Expand Down
26 changes: 16 additions & 10 deletions Frontend/src/scripts/features/repo/interactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,11 @@ export function toggleSelectAll(on: boolean, visible: FileStatus[]) {
export async function onFileContextMenu(ev: MouseEvent, f: FileStatus) {
ev.preventDefault();
const x = ev.clientX, y = ev.clientY;
const selectedPaths = Array.from(state.selectedFiles || []).filter(Boolean);
const clickedInSelection = !!f.path && (state.selectedFiles?.has(f.path) ?? false);
const selectedPaths = Array.from(state.selectedFiles || [])
.map((path) => path.trim())
.filter(Boolean);
const clickedPath = (f.path || '').trim();
const clickedInSelection = !!clickedPath && (state.selectedFiles?.has(clickedPath) ?? false);
const explicitMultiSelection =
clickedInSelection &&
selectedPaths.length > 1 &&
Expand All @@ -217,11 +220,12 @@ export async function onFileContextMenu(ev: MouseEvent, f: FileStatus) {
const items: CtxItem[] = [];
/** Opens the stash modal pre-filled for the provided paths. */
const openStashForPaths = (paths: string[], defaultMessage: string) => {
if (!paths.length) return;
const normalizedPaths = paths.map((path) => path.trim()).filter(Boolean);
if (!normalizedPaths.length) return;
openStashConfirm({
defaultMessage,
includeUntracked: false,
paths,
paths: normalizedPaths,
onSuccess: async () => {
await Promise.allSettled([hydrateStatus(), hydrateStash()]);
renderListCallback?.();
Expand All @@ -234,7 +238,7 @@ export async function onFileContextMenu(ev: MouseEvent, f: FileStatus) {
notify('Open is available in the desktop app');
return;
}
const target = (hasSingleSelection ? selectedPaths[0] : f.path) || '';
const target = (hasSingleSelection ? selectedPaths[0] : clickedPath) || '';
if (!target) return;
try {
await TAURI.invoke('open_repo_file', { path: target });
Expand All @@ -249,11 +253,13 @@ export async function onFileContextMenu(ev: MouseEvent, f: FileStatus) {
openStashForPaths(selectedPaths.slice(), 'WIP selection');
}});
}
const singleTarget = hasSingleSelection ? selectedPaths[0] : f.path;
const defaultMsg = `WIP ${singleTarget}`;
items.push({ label: 'Create stash for this file…', action: () => {
openStashForPaths([singleTarget], defaultMsg);
}});
const singleTarget = (hasSingleSelection ? selectedPaths[0] : clickedPath) || '';
if (singleTarget) {
const defaultMsg = `WIP ${singleTarget}`;
items.push({ label: 'Create stash for this file…', action: () => {
openStashForPaths([singleTarget], defaultMsg);
}});
}
items.push({ label: '---' });
items.push({ label: 'Add to .gitignore', action: async () => {
if (!TAURI.has) {
Expand Down
Loading