Skip to content
Merged
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
29 changes: 28 additions & 1 deletion src-tauri/src/http_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1445,6 +1445,20 @@ async fn workspaces(State(state): State<HttpServerState>) -> Json<Vec<WorkspaceS
Json(workspace_summaries(&state.window_sessions).await)
}

fn browse_redirect_path(hash: &str, initial_file: Option<&str>) -> Result<String, String> {
let path = format!("/{hash}/");
let Some(initial_file) = initial_file else {
return Ok(path);
};
let mut url =
tauri::Url::parse(&format!("http://localhost{path}")).map_err(|error| error.to_string())?;
url.query_pairs_mut().append_pair("file", initial_file);
let query = url
.query()
.ok_or_else(|| "failed to encode browse file query".to_string())?;
Ok(format!("{path}?{query}"))
}

/// `GET /browse?path=<abs>` — the landing point for `ide browse`. Ensures a (possibly
/// hidden) session exists for `path`, then redirects into its scoped SPA, so a plain
/// `open <url>` from bash is enough to land a browser tab on the right workspace
Expand All @@ -1465,6 +1479,10 @@ async fn browse(app: tauri::AppHandle, state: HttpServerState, query: BrowseQuer
Err(error) => return ApiError::bad_request(error.to_string()).into_response(),
};
let hash = workspace_root_hash(&target.workspace_root);
let redirect_path = match browse_redirect_path(&hash, target.initial_file.as_deref()) {
Ok(path) => path,
Err(error) => return ApiError::internal(error).into_response(),
};

let already_open = resolve_workspace_by_hash(&state.window_sessions, &hash)
.await
Expand All @@ -1476,7 +1494,7 @@ async fn browse(app: tauri::AppHandle, state: HttpServerState, query: BrowseQuer
}
}

Redirect::to(&format!("/{hash}/")).into_response()
Redirect::to(&redirect_path).into_response()
}

/// Builds the hidden background window for a browse session on the main thread —
Expand Down Expand Up @@ -2794,6 +2812,15 @@ mod tests {
.is_none());
}

#[test]
fn browse_redirect_path_includes_an_encoded_file_target() {
assert_eq!(
browse_redirect_path("abc123", Some("docs/hello world#1.md")).unwrap(),
"/abc123/?file=docs%2Fhello+world%231.md"
);
assert_eq!(browse_redirect_path("abc123", None).unwrap(), "/abc123/");
}

#[tokio::test]
async fn workspace_summaries_dedupe_by_hash() {
let dir = tempdir().unwrap();
Expand Down
21 changes: 21 additions & 0 deletions src/tauri.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ describe("hosted Tauri API transport", () => {
vi.unstubAllGlobals();
vi.resetModules();
delete (window as Window & { __TAURI_INTERNALS__?: unknown }).__TAURI_INTERNALS__;
window.history.replaceState({}, "", "/");
});

it("adds the local bearer token to hosted write requests", async () => {
Expand Down Expand Up @@ -240,6 +241,26 @@ describe("hosted Tauri API transport", () => {
expect(fetchMock.mock.calls[1][0]).toBe("/api/agent-context");
});

it("reads the initial file from the scoped hosted URL", async () => {
const fetchMock = vi.fn();
vi.stubGlobal("fetch", fetchMock);
window.history.replaceState({}, "", "/abc123/?file=docs%2Fhello+world.md");
const { getInitialFile } = await import("./tauri");

await expect(getInitialFile()).resolves.toBe("docs/hello world.md");

expect(fetchMock).not.toHaveBeenCalled();
});

it("normalizes an empty native initial file to undefined", async () => {
(window as Window & { __TAURI_INTERNALS__?: unknown }).__TAURI_INTERNALS__ = {};
const { invoke } = await import("@tauri-apps/api/core");
vi.mocked(invoke).mockResolvedValueOnce(null);
const { getInitialFile } = await import("./tauri");

await expect(getInitialFile()).resolves.toBeUndefined();
});

it("does not persist recent files from hosted browser mode", async () => {
const fetchMock = vi.fn();
vi.stubGlobal("fetch", fetchMock);
Expand Down
6 changes: 4 additions & 2 deletions src/tauri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,8 +391,10 @@ export function getWorkspaceRoot() {
}

export function getInitialFile() {
if (!isNativeTauri()) return Promise.resolve(undefined);
return invoke<string | undefined>("get_initial_file");
if (!isNativeTauri()) {
return Promise.resolve(new URLSearchParams(window.location.search).get("file") ?? undefined);
}
return invoke<string | null>("get_initial_file").then((path) => path ?? undefined);
}
Comment thread
GordonBeeming marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

export function getAppInfo() {
Expand Down
Loading