diff --git a/src-tauri/src/codex.rs b/src-tauri/src/codex.rs index 347bd6063..c27fbc466 100644 --- a/src-tauri/src/codex.rs +++ b/src-tauri/src/codex.rs @@ -255,6 +255,12 @@ pub(crate) async fn send_user_message( app: AppHandle, ) -> Result { if remote_backend::is_remote_mode(&*state).await { + let images = images.map(|paths| { + paths + .into_iter() + .map(remote_backend::normalize_path_for_remote) + .collect::>() + }); return remote_backend::call_remote( &*state, app, diff --git a/src-tauri/src/remote_backend.rs b/src-tauri/src/remote_backend.rs index a46f6b839..a844f0fff 100644 --- a/src-tauri/src/remote_backend.rs +++ b/src-tauri/src/remote_backend.rs @@ -16,6 +16,40 @@ const DISCONNECTED_MESSAGE: &str = "remote backend disconnected"; type PendingMap = HashMap>>; +pub(crate) fn normalize_path_for_remote(path: String) -> String { + let trimmed = path.trim(); + if trimmed.is_empty() { + return path; + } + + if let Some(normalized) = normalize_wsl_unc_path(trimmed) { + return normalized; + } + + path +} + +fn normalize_wsl_unc_path(path: &str) -> Option { + let lower = path.to_ascii_lowercase(); + let (prefix_len, raw) = if lower.starts_with("\\\\wsl$\\") { + (7, path) + } else if lower.starts_with("\\\\wsl.localhost\\") { + (16, path) + } else { + return None; + }; + + let remainder = raw.get(prefix_len..)?; + let mut segments = remainder.split('\\').filter(|segment| !segment.is_empty()); + segments.next()?; + let joined = segments.collect::>().join("/"); + Some(if joined.is_empty() { + "/".to_string() + } else { + format!("/{joined}") + }) +} + #[derive(Clone)] pub(crate) struct RemoteBackend { inner: Arc, diff --git a/src-tauri/src/workspaces.rs b/src-tauri/src/workspaces.rs index 10e365853..3c1faa2ae 100644 --- a/src-tauri/src/workspaces.rs +++ b/src-tauri/src/workspaces.rs @@ -516,6 +516,8 @@ pub(crate) async fn add_workspace( app: AppHandle, ) -> Result { if remote_backend::is_remote_mode(&*state).await { + let path = remote_backend::normalize_path_for_remote(path); + let codex_bin = codex_bin.map(remote_backend::normalize_path_for_remote); let response = remote_backend::call_remote( &*state, app, @@ -719,6 +721,17 @@ pub(crate) async fn add_worktree( state: State<'_, AppState>, app: AppHandle, ) -> Result { + if remote_backend::is_remote_mode(&*state).await { + let response = remote_backend::call_remote( + &*state, + app, + "add_worktree", + json!({ "parentId": parent_id, "branch": branch }), + ) + .await?; + return serde_json::from_value(response).map_err(|err| err.to_string()); + } + let branch = branch.trim(); if branch.is_empty() { return Err("Branch name is required.".to_string()); @@ -812,7 +825,13 @@ pub(crate) async fn add_worktree( pub(crate) async fn remove_workspace( id: String, state: State<'_, AppState>, + app: AppHandle, ) -> Result<(), String> { + if remote_backend::is_remote_mode(&*state).await { + remote_backend::call_remote(&*state, app, "remove_workspace", json!({ "id": id })).await?; + return Ok(()); + } + let (entry, child_worktrees) = { let workspaces = state.workspaces.lock().await; let entry = workspaces @@ -880,7 +899,13 @@ pub(crate) async fn remove_workspace( pub(crate) async fn remove_worktree( id: String, state: State<'_, AppState>, + app: AppHandle, ) -> Result<(), String> { + if remote_backend::is_remote_mode(&*state).await { + remote_backend::call_remote(&*state, app, "remove_worktree", json!({ "id": id })).await?; + return Ok(()); + } + let (entry, parent) = { let workspaces = state.workspaces.lock().await; let entry = workspaces @@ -1333,7 +1358,19 @@ pub(crate) async fn update_workspace_settings( id: String, settings: WorkspaceSettings, state: State<'_, AppState>, + app: AppHandle, ) -> Result { + if remote_backend::is_remote_mode(&*state).await { + let response = remote_backend::call_remote( + &*state, + app, + "update_workspace_settings", + json!({ "id": id, "settings": settings }), + ) + .await?; + return serde_json::from_value(response).map_err(|err| err.to_string()); + } + let (entry_snapshot, list) = { let mut workspaces = state.workspaces.lock().await; let entry_snapshot = apply_workspace_settings_update(&mut workspaces, &id, settings)?; @@ -1361,7 +1398,20 @@ pub(crate) async fn update_workspace_codex_bin( id: String, codex_bin: Option, state: State<'_, AppState>, + app: AppHandle, ) -> Result { + if remote_backend::is_remote_mode(&*state).await { + let codex_bin = codex_bin.map(remote_backend::normalize_path_for_remote); + let response = remote_backend::call_remote( + &*state, + app, + "update_workspace_codex_bin", + json!({ "id": id, "codex_bin": codex_bin }), + ) + .await?; + return serde_json::from_value(response).map_err(|err| err.to_string()); + } + let (entry_snapshot, list) = { let mut workspaces = state.workspaces.lock().await; let entry_snapshot = match workspaces.get_mut(&id) {