Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
26 changes: 26 additions & 0 deletions create_ico.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# PowerShell script to convert PNG to ICO format
$pngPath = "src-tauri\icons\icon.png"
$icoPath = "src-tauri\icons\icon.ico"

Add-Type -AssemblyName System.Drawing

# Load the PNG image
$png = [System.Drawing.Image]::FromFile((Resolve-Path $pngPath))

# Create a new bitmap with 32x32 size
$bitmap = New-Object System.Drawing.Bitmap(32, 32)
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
$graphics.DrawImage($png, 0, 0, 32, 32)

# Create ICO file
$icon = [System.Drawing.Icon]::FromHandle($bitmap.GetHicon())
$fileStream = [System.IO.File]::Create((Resolve-Path ".").Path + "\" + $icoPath)
$icon.Save($fileStream)

# Clean up
$fileStream.Close()
$graphics.Dispose()
$bitmap.Dispose()
$png.Dispose()

Write-Host "ICO file created successfully at $icoPath"
Binary file added src-tauri/icons/icon.ico
Binary file not shown.
71 changes: 61 additions & 10 deletions src-tauri/src/claude_binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,23 +168,37 @@ fn discover_system_installations() -> Vec<ClaudeInstallation> {
fn try_which_command() -> Option<ClaudeInstallation> {
debug!("Trying 'which claude' to find binary...");

match Command::new("which").arg("claude").output() {
// Use 'where' on Windows, 'which' on Unix systems
let (cmd, args) = if cfg!(windows) {
("where", vec!["claude"])
} else {
("which", vec!["claude"])
};

match Command::new(cmd).args(&args).output() {
Ok(output) if output.status.success() => {
let output_str = String::from_utf8_lossy(&output.stdout).trim().to_string();

if output_str.is_empty() {
return None;
}

// Parse aliased output: "claude: aliased to /path/to/claude"
let path = if output_str.starts_with("claude:") && output_str.contains("aliased to") {
output_str
.split("aliased to")
.nth(1)
.map(|s| s.trim().to_string())
// Handle Windows 'where' command which may return multiple paths
let path = if cfg!(windows) {
// Take the first line from Windows 'where' command output
output_str.lines().next().unwrap_or(&output_str).to_string()
} else {
Some(output_str)
}?;
// Parse aliased output on Unix: "claude: aliased to /path/to/claude"
if output_str.starts_with("claude:") && output_str.contains("aliased to") {
output_str
.split("aliased to")
.nth(1)
.map(|s| s.trim().to_string())
.unwrap_or(output_str)
} else {
output_str
}
};

debug!("'which' found claude at: {}", path);

Expand Down Expand Up @@ -264,7 +278,44 @@ fn find_standard_installations() -> Vec<ClaudeInstallation> {
("/bin/claude".to_string(), "system".to_string()),
];

// Also check user-specific paths
// Check Windows-specific paths first
if cfg!(windows) {
if let Ok(userprofile) = std::env::var("USERPROFILE") {
paths_to_check.extend(vec![
// NPM global installation (most common on Windows)
(
format!("{}\\AppData\\Roaming\\npm\\claude.cmd", userprofile),
"npm-global".to_string(),
),
(
format!("{}\\AppData\\Roaming\\npm\\claude", userprofile),
"npm-global".to_string(),
),
// Other Windows package managers
(
format!("{}\\AppData\\Local\\Yarn\\bin\\claude.cmd", userprofile),
"yarn-global".to_string(),
),
(
format!("{}\\.bun\\bin\\claude.exe", userprofile),
"bun".to_string(),
),
// Scoop installation
(
format!("{}\\scoop\\apps\\claude\\current\\claude.exe", userprofile),
"scoop".to_string(),
),
]);
}

// Check Program Files
paths_to_check.extend(vec![
("C:\\Program Files\\Claude\\claude.exe".to_string(), "system".to_string()),
("C:\\Program Files (x86)\\Claude\\claude.exe".to_string(), "system".to_string()),
]);
}

// Also check user-specific paths (Unix-style)
if let Ok(home) = std::env::var("HOME") {
paths_to_check.extend(vec![
(
Expand Down