mirror of
https://github.com/delibae/claude-prism.git
synced 2026-08-28 05:14:59 +00:00
fix: expand registry PATH vars and fix debug window sync
- Expand %USERPROFILE% etc. via ExpandEnvironmentStringsW when reading registry PATH entries - Use is_file() instead of exists() for binary detection - Fix debug window showing blank: use emitTo() for cross-window events instead of emit() which only targets the current window - Update step numbering and doc comments - Improve error message to avoid double-wrapping Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e37cf55367
commit
dd6ca0cb54
3 changed files with 67 additions and 13 deletions
|
|
@ -54,6 +54,7 @@ objc2-foundation = { version = "0.3", features = ["NSData"] }
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
winreg = "0.55"
|
winreg = "0.55"
|
||||||
|
windows-sys = { version = "0.52", features = ["Win32_System_Environment"] }
|
||||||
|
|
||||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||||
tauri-plugin-updater = "2"
|
tauri-plugin-updater = "2"
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,8 @@ impl Default for ClaudeProcessState {
|
||||||
/// On Windows, read User + System PATH from the registry and search for claude.
|
/// On Windows, read User + System PATH from the registry and search for claude.
|
||||||
/// This catches cases where claude was installed after the GUI app launched,
|
/// This catches cases where claude was installed after the GUI app launched,
|
||||||
/// since the process PATH is stale but the registry PATH is up to date.
|
/// since the process PATH is stale but the registry PATH is up to date.
|
||||||
|
/// Registry values may contain unexpanded variables like `%USERPROFILE%`,
|
||||||
|
/// so we expand them via `ExpandEnvironmentStringsW` before searching.
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
fn find_claude_in_registry_path() -> Option<String> {
|
fn find_claude_in_registry_path() -> Option<String> {
|
||||||
use winreg::enums::{HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
|
use winreg::enums::{HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
|
||||||
|
|
@ -69,7 +71,12 @@ fn find_claude_in_registry_path() -> Option<String> {
|
||||||
// User PATH
|
// User PATH
|
||||||
if let Ok(env) = RegKey::predef(HKEY_CURRENT_USER).open_subkey("Environment") {
|
if let Ok(env) = RegKey::predef(HKEY_CURRENT_USER).open_subkey("Environment") {
|
||||||
if let Ok(user_path) = env.get_value::<String, _>("Path") {
|
if let Ok(user_path) = env.get_value::<String, _>("Path") {
|
||||||
dirs.extend(user_path.split(';').filter(|s| !s.is_empty()).map(String::from));
|
dirs.extend(
|
||||||
|
user_path
|
||||||
|
.split(';')
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.map(|s| expand_env_vars(s)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,7 +85,12 @@ fn find_claude_in_registry_path() -> Option<String> {
|
||||||
.open_subkey(r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")
|
.open_subkey(r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")
|
||||||
{
|
{
|
||||||
if let Ok(sys_path) = env.get_value::<String, _>("Path") {
|
if let Ok(sys_path) = env.get_value::<String, _>("Path") {
|
||||||
dirs.extend(sys_path.split(';').filter(|s| !s.is_empty()).map(String::from));
|
dirs.extend(
|
||||||
|
sys_path
|
||||||
|
.split(';')
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.map(|s| expand_env_vars(s)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,7 +98,7 @@ fn find_claude_in_registry_path() -> Option<String> {
|
||||||
for dir in &dirs {
|
for dir in &dirs {
|
||||||
for name in &candidates {
|
for name in &candidates {
|
||||||
let p = PathBuf::from(dir).join(name);
|
let p = PathBuf::from(dir).join(name);
|
||||||
if p.exists() {
|
if p.is_file() {
|
||||||
return Some(p.to_string_lossy().to_string());
|
return Some(p.to_string_lossy().to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -95,8 +107,49 @@ fn find_claude_in_registry_path() -> Option<String> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Expand Windows environment variables like `%USERPROFILE%` in a string.
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn expand_env_vars(s: &str) -> String {
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::os::windows::ffi::{OsStrExt, OsStringExt};
|
||||||
|
|
||||||
|
let wide: Vec<u16> = OsString::from(s).encode_wide().chain(std::iter::once(0)).collect();
|
||||||
|
|
||||||
|
// First call to get required buffer size
|
||||||
|
let size = unsafe {
|
||||||
|
windows_sys::Win32::System::Environment::ExpandEnvironmentStringsW(
|
||||||
|
wide.as_ptr(),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if size == 0 {
|
||||||
|
return s.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf: Vec<u16> = vec![0u16; size as usize];
|
||||||
|
let result = unsafe {
|
||||||
|
windows_sys::Win32::System::Environment::ExpandEnvironmentStringsW(
|
||||||
|
wide.as_ptr(),
|
||||||
|
buf.as_mut_ptr(),
|
||||||
|
size,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if result == 0 {
|
||||||
|
return s.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim trailing null
|
||||||
|
if let Some(pos) = buf.iter().position(|&c| c == 0) {
|
||||||
|
buf.truncate(pos);
|
||||||
|
}
|
||||||
|
OsString::from_wide(&buf).to_string_lossy().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
/// Discover the claude binary on the system.
|
/// Discover the claude binary on the system.
|
||||||
/// Checks: ~/.local/bin (native install) → which → NVM paths → standard paths → bare fallback.
|
/// Search order: ~/.local/bin → NVM_BIN → which → registry PATH (Windows) →
|
||||||
|
/// login shell (Unix) → npm/nvm global → standard paths → user-specific paths.
|
||||||
|
/// Returns Err if not found.
|
||||||
fn find_claude_binary() -> Result<String, String> {
|
fn find_claude_binary() -> Result<String, String> {
|
||||||
// 1. Check the native installer's default location first
|
// 1. Check the native installer's default location first
|
||||||
// (GUI apps often don't have ~/.local/bin in PATH)
|
// (GUI apps often don't have ~/.local/bin in PATH)
|
||||||
|
|
@ -155,7 +208,7 @@ fn find_claude_binary() -> Result<String, String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Check NVM directories (Unix) or npm global (Windows)
|
// 6. Check NVM directories (Unix) or npm/nvm global (Windows)
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
if let Some(home) = dirs::home_dir() {
|
if let Some(home) = dirs::home_dir() {
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
|
@ -213,7 +266,7 @@ fn find_claude_binary() -> Result<String, String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Check standard paths (Unix only)
|
// 7. Check standard paths (Unix only)
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
{
|
{
|
||||||
let standard_paths = [
|
let standard_paths = [
|
||||||
|
|
@ -229,7 +282,7 @@ fn find_claude_binary() -> Result<String, String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Check user-specific paths
|
// 8. Check user-specific paths
|
||||||
if let Some(home) = dirs::home_dir() {
|
if let Some(home) = dirs::home_dir() {
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
let user_paths = vec![
|
let user_paths = vec![
|
||||||
|
|
@ -273,7 +326,7 @@ fn find_claude_binary() -> Result<String, String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err("Claude CLI not found. Install it from https://claude.ai".to_string())
|
Err("Not found in any known location. Install from https://claude.ai".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Strip ANSI escape sequences from CLI output before sending to the frontend.
|
/// Strip ANSI escape sequences from CLI output before sending to the frontend.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { emit, listen } from "@tauri-apps/api/event";
|
import { emitTo, listen } from "@tauri-apps/api/event";
|
||||||
|
|
||||||
export type LogLevel = "debug" | "info" | "warn" | "error";
|
export type LogLevel = "debug" | "info" | "warn" | "error";
|
||||||
|
|
||||||
|
|
@ -93,7 +93,7 @@ export const useLogStore = create<LogStore>((set) => ({
|
||||||
|
|
||||||
// Only emit to debug window if one is connected
|
// Only emit to debug window if one is connected
|
||||||
if (_debugWindowConnected && !_isDebugWindow) {
|
if (_debugWindowConnected && !_isDebugWindow) {
|
||||||
emit("debug-log-entry", entry).catch(() => {});
|
emitTo("debug", "debug-log-entry", entry).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward warn/error to Rust stderr via existing js_log command
|
// Forward warn/error to Rust stderr via existing js_log command
|
||||||
|
|
@ -143,8 +143,8 @@ if (_isDebugWindow) {
|
||||||
useLogStore.setState({ version: ++_version });
|
useLogStore.setState({ version: ++_version });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Request bulk sync on open
|
// Request bulk sync on open — send to main window
|
||||||
emit("debug-log-sync-request").catch(() => {});
|
emitTo("main", "debug-log-sync-request").catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main window responds to sync requests by sending the full buffer.
|
// Main window responds to sync requests by sending the full buffer.
|
||||||
|
|
@ -152,7 +152,7 @@ if (!_isDebugWindow) {
|
||||||
listen("debug-log-sync-request", () => {
|
listen("debug-log-sync-request", () => {
|
||||||
_debugWindowConnected = true;
|
_debugWindowConnected = true;
|
||||||
if (_buffer.length > 0) {
|
if (_buffer.length > 0) {
|
||||||
emit("debug-log-sync", _buffer).catch(() => {});
|
emitTo("debug", "debug-log-sync", _buffer).catch(() => {});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue