Skip to content

Commit 119b77a

Browse files
authored
Merge pull request #9 from InterestingSoftware/settings-and-ui
Settings and UI
2 parents a89093c + dc8af82 commit 119b77a

20 files changed

Lines changed: 1603 additions & 696 deletions

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
}
2828
},
2929
"dependencies": {
30+
"@fontsource/fira-code": "^5.2.7",
31+
"@fontsource/plus-jakarta-sans": "^5.2.8",
3032
"@fontsource/space-grotesk": "^5.2.10",
3133
"@monaco-editor/loader": "^1.7.0",
3234
"@tauri-apps/api": "^2",

pnpm-lock.yaml

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/src/editor.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ pub struct EditorInfo {
1717
pub installed: bool,
1818
}
1919

20+
#[derive(Serialize, Clone)]
21+
#[serde(rename_all = "camelCase")]
22+
pub struct GitToolInfo {
23+
pub id: String,
24+
pub name: String,
25+
pub command: String,
26+
pub installed: bool,
27+
pub supports_diff: bool,
28+
pub supports_merge: bool,
29+
}
30+
2031
// ── Known Editors ──
2132
// Each entry: (id, display_name, cli_command, optional macOS app bundle path).
2233

@@ -27,6 +38,15 @@ struct EditorCandidate {
2738
mac_bundle_bin: Option<&'static str>,
2839
}
2940

41+
struct GitToolCandidate {
42+
id: &'static str,
43+
name: &'static str,
44+
command: &'static str,
45+
mac_bundle_bin: Option<&'static str>,
46+
supports_diff: bool,
47+
supports_merge: bool,
48+
}
49+
3050
fn known_editors() -> Vec<EditorCandidate> {
3151
vec![
3252
EditorCandidate {
@@ -94,6 +114,113 @@ fn known_editors() -> Vec<EditorCandidate> {
94114
]
95115
}
96116

117+
fn known_git_tools() -> Vec<GitToolCandidate> {
118+
vec![
119+
GitToolCandidate {
120+
id: "vscode",
121+
name: "VS Code",
122+
command: "code",
123+
mac_bundle_bin: Some(
124+
"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code",
125+
),
126+
supports_diff: true,
127+
supports_merge: true,
128+
},
129+
GitToolCandidate {
130+
id: "cursor",
131+
name: "Cursor",
132+
command: "cursor",
133+
mac_bundle_bin: Some("/Applications/Cursor.app/Contents/Resources/app/bin/cursor"),
134+
supports_diff: true,
135+
supports_merge: true,
136+
},
137+
GitToolCandidate {
138+
id: "windsurf",
139+
name: "Windsurf",
140+
command: "windsurf",
141+
mac_bundle_bin: Some(
142+
"/Applications/Windsurf.app/Contents/Resources/app/bin/windsurf",
143+
),
144+
supports_diff: true,
145+
supports_merge: true,
146+
},
147+
GitToolCandidate {
148+
id: "zed",
149+
name: "Zed",
150+
command: "zed",
151+
mac_bundle_bin: Some("/Applications/Zed.app/Contents/MacOS/cli"),
152+
supports_diff: true,
153+
supports_merge: true,
154+
},
155+
GitToolCandidate {
156+
id: "sublime",
157+
name: "Sublime Text",
158+
command: "subl",
159+
mac_bundle_bin: Some(
160+
"/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl",
161+
),
162+
supports_diff: true,
163+
supports_merge: true,
164+
},
165+
GitToolCandidate {
166+
id: "vimdiff",
167+
name: "Vimdiff",
168+
command: "vimdiff",
169+
mac_bundle_bin: None,
170+
supports_diff: true,
171+
supports_merge: true,
172+
},
173+
GitToolCandidate {
174+
id: "nvimdiff",
175+
name: "Neovim diff",
176+
command: "nvimdiff",
177+
mac_bundle_bin: None,
178+
supports_diff: true,
179+
supports_merge: true,
180+
},
181+
GitToolCandidate {
182+
id: "meld",
183+
name: "Meld",
184+
command: "meld",
185+
mac_bundle_bin: None,
186+
supports_diff: true,
187+
supports_merge: true,
188+
},
189+
GitToolCandidate {
190+
id: "kdiff3",
191+
name: "KDiff3",
192+
command: "kdiff3",
193+
mac_bundle_bin: None,
194+
supports_diff: true,
195+
supports_merge: true,
196+
},
197+
GitToolCandidate {
198+
id: "bcompare",
199+
name: "Beyond Compare",
200+
command: "bcompare",
201+
mac_bundle_bin: None,
202+
supports_diff: true,
203+
supports_merge: true,
204+
},
205+
GitToolCandidate {
206+
id: "p4merge",
207+
name: "P4Merge",
208+
command: "p4merge",
209+
mac_bundle_bin: None,
210+
supports_diff: true,
211+
supports_merge: true,
212+
},
213+
GitToolCandidate {
214+
id: "opendiff",
215+
name: "FileMerge (opendiff)",
216+
command: "opendiff",
217+
mac_bundle_bin: None,
218+
supports_diff: true,
219+
supports_merge: true,
220+
},
221+
]
222+
}
223+
97224
fn is_command_available(cmd: &str) -> bool {
98225
command_exists(cmd)
99226
}
@@ -112,6 +239,18 @@ fn resolve_editor(editor: &EditorCandidate) -> Option<String> {
112239
None
113240
}
114241

242+
fn resolve_git_tool(tool: &GitToolCandidate) -> Option<String> {
243+
if is_command_available(tool.command) {
244+
return Some(tool.command.to_string());
245+
}
246+
if let Some(bundle_path) = tool.mac_bundle_bin {
247+
if Path::new(bundle_path).exists() {
248+
return Some(bundle_path.to_string());
249+
}
250+
}
251+
None
252+
}
253+
115254
/// Parse a shell-like command string, respecting double and single quotes.
116255
/// e.g. `"/path/with spaces/code" --wait` → ["/path/with spaces/code", "--wait"]
117256
/// Also handles unquoted paths by probing the filesystem for known editors.
@@ -231,6 +370,26 @@ pub fn detect_editors() -> Vec<EditorInfo> {
231370
.collect()
232371
}
233372

373+
#[tauri::command]
374+
pub fn detect_git_tools() -> Vec<GitToolInfo> {
375+
known_git_tools()
376+
.into_iter()
377+
.map(|tool| {
378+
let resolved = resolve_git_tool(&tool);
379+
GitToolInfo {
380+
id: tool.id.to_string(),
381+
name: tool.name.to_string(),
382+
command: resolved
383+
.clone()
384+
.unwrap_or_else(|| tool.command.to_string()),
385+
installed: resolved.is_some(),
386+
supports_diff: tool.supports_diff,
387+
supports_merge: tool.supports_merge,
388+
}
389+
})
390+
.collect()
391+
}
392+
234393
#[tauri::command]
235394
pub fn get_git_config(key: String) -> Result<String, String> {
236395
let key = validate_git_config_key(&key)?;

src-tauri/src/git/helpers.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub enum GitAction {
2121
WorktreeList,
2222
ListRefs,
2323
CommitGraph,
24+
CountCommits,
2425
CreateManagedWorktree,
2526
DeleteManagedWorktree,
2627
PruneWorktrees,
@@ -45,11 +46,12 @@ pub enum GitAction {
4546

4647
impl GitAction {
4748
#[cfg(test)]
48-
pub const ALL: [GitAction; 24] = [
49+
pub const ALL: [GitAction; 25] = [
4950
GitAction::GitInfo,
5051
GitAction::WorktreeList,
5152
GitAction::ListRefs,
5253
GitAction::CommitGraph,
54+
GitAction::CountCommits,
5355
GitAction::CreateManagedWorktree,
5456
GitAction::DeleteManagedWorktree,
5557
GitAction::PruneWorktrees,
@@ -78,6 +80,7 @@ impl GitAction {
7880
GitAction::WorktreeList => "worktree_list",
7981
GitAction::ListRefs => "list_refs",
8082
GitAction::CommitGraph => "commit_graph",
83+
GitAction::CountCommits => "count_commits",
8184
GitAction::CreateManagedWorktree => "create_managed_worktree",
8285
GitAction::DeleteManagedWorktree => "delete_managed_worktree",
8386
GitAction::PruneWorktrees => "prune_worktrees",

src-tauri/src/git/operations.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,27 @@ pub async fn list_refs(repo_path: String) -> Result<RefsResult, String> {
280280
})
281281
}
282282

283+
#[tauri::command]
284+
pub async fn count_commits(repo_path: String) -> Result<u64, String> {
285+
let canonical = normalize_existing_path(&repo_path)?;
286+
let output = run_git(
287+
GitAction::CountCommits,
288+
&[
289+
"-C",
290+
&canonical.to_string_lossy(),
291+
"rev-list",
292+
"--count",
293+
"--all",
294+
],
295+
)?;
296+
let output = ensure_git_success(output, "Failed to count commits")?;
297+
let stdout = String::from_utf8_lossy(&output.stdout);
298+
stdout
299+
.trim()
300+
.parse::<u64>()
301+
.map_err(|_| "Failed to parse commit count".to_string())
302+
}
303+
283304
#[tauri::command]
284305
pub async fn get_commit_graph(
285306
repo_path: String,

0 commit comments

Comments
 (0)