diff --git a/app/client/components/__test__/restore-form.test.tsx b/app/client/components/__test__/restore-form.test.tsx
index 3638f1a5..463162ef 100644
--- a/app/client/components/__test__/restore-form.test.tsx
+++ b/app/client/components/__test__/restore-form.test.tsx
@@ -75,4 +75,51 @@ describe("RestoreForm", () => {
});
});
});
+
+ test("restores the selected full path when the display root is unrelated", async () => {
+ let restoreRequestBody: unknown;
+
+ server.use(
+ http.get("/api/v1/repositories/:shortId/snapshots/:snapshotId/files", () => {
+ return HttpResponse.json({
+ files: [
+ { name: "project", path: "/mnt/project", type: "dir" },
+ { name: "a.txt", path: "/mnt/project/a.txt", type: "file" },
+ ],
+ });
+ }),
+ http.post("/api/v1/repositories/:shortId/restore", async ({ request }) => {
+ restoreRequestBody = await request.json();
+ return HttpResponse.json({
+ success: true,
+ message: "Snapshot restored successfully",
+ filesRestored: 1,
+ filesSkipped: 0,
+ });
+ }),
+ );
+
+ render(
+ ,
+ );
+
+ const row = await screen.findByRole("button", { name: "mnt" });
+ await userEvent.click(within(row).getByRole("checkbox"));
+ await userEvent.click(screen.getByRole("button", { name: "Restore 1 item" }));
+
+ await waitFor(() => {
+ expect(restoreRequestBody).toEqual({
+ snapshotId: "snap-1",
+ include: ["/mnt"],
+ selectedItemKind: "dir",
+ overwrite: "always",
+ });
+ });
+ });
});
diff --git a/app/client/components/file-browsers/__tests__/snapshot-tree-browser.test.tsx b/app/client/components/file-browsers/__tests__/snapshot-tree-browser.test.tsx
index 3e8fc202..8fc91c01 100644
--- a/app/client/components/file-browsers/__tests__/snapshot-tree-browser.test.tsx
+++ b/app/client/components/file-browsers/__tests__/snapshot-tree-browser.test.tsx
@@ -134,6 +134,33 @@ describe("SnapshotTreeBrowser", () => {
expect(selectedKind === "dir").toBe(true);
});
+ test("keeps full paths when the display root does not contain the query root", async () => {
+ mockListSnapshotFiles();
+
+ let selectedPaths: Set | undefined;
+ let selectedKind: "file" | "dir" | null = null;
+
+ renderSnapshotTreeBrowser({
+ queryBasePath: "/mnt/project",
+ displayBasePath: "/other/root",
+ withCheckboxes: true,
+ onSelectionChange: (paths) => {
+ selectedPaths = paths;
+ },
+ onSingleSelectionKindChange: (kind) => {
+ selectedKind = kind;
+ },
+ });
+
+ const row = await screen.findByRole("button", { name: "mnt" });
+ const checkbox = within(row).getByRole("checkbox");
+
+ await userEvent.click(checkbox);
+
+ expect(selectedPaths ? Array.from(selectedPaths) : []).toEqual(["/mnt"]);
+ expect(selectedKind === "dir").toBe(true);
+ });
+
test("shows selected folder state when full paths are provided from the parent", async () => {
mockListSnapshotFiles();
diff --git a/app/client/components/file-browsers/snapshot-tree-browser.tsx b/app/client/components/file-browsers/snapshot-tree-browser.tsx
index 8a764415..6491e45f 100644
--- a/app/client/components/file-browsers/snapshot-tree-browser.tsx
+++ b/app/client/components/file-browsers/snapshot-tree-browser.tsx
@@ -4,7 +4,7 @@ import { listSnapshotFilesOptions } from "~/client/api-client/@tanstack/react-qu
import { FileBrowser, type FileBrowserUiProps } from "~/client/components/file-browsers/file-browser";
import { useFileBrowser } from "~/client/hooks/use-file-browser";
import { parseError } from "~/client/lib/errors";
-import { normalizeAbsolutePath } from "@zerobyte/core/utils";
+import { isPathWithin, normalizeAbsolutePath } from "@zerobyte/core/utils";
import { logger } from "~/client/lib/logger";
function createPathPrefixFns(basePath: string) {
@@ -48,6 +48,9 @@ export const SnapshotTreeBrowser = (props: SnapshotTreeBrowserProps) => {
const queryClient = useQueryClient();
const normalizedQueryBasePath = normalizeAbsolutePath(queryBasePath);
const normalizedDisplayBasePath = normalizeAbsolutePath(displayBasePath ?? "/");
+ const effectiveDisplayBasePath = isPathWithin(normalizedDisplayBasePath, normalizedQueryBasePath)
+ ? normalizedDisplayBasePath
+ : "/";
const { data, isLoading, error } = useQuery({
...listSnapshotFilesOptions({
@@ -57,7 +60,7 @@ export const SnapshotTreeBrowser = (props: SnapshotTreeBrowserProps) => {
enabled,
});
- const displayPathFns = useMemo(() => createPathPrefixFns(normalizedDisplayBasePath), [normalizedDisplayBasePath]);
+ const displayPathFns = useMemo(() => createPathPrefixFns(effectiveDisplayBasePath), [effectiveDisplayBasePath]);
const displaySelectedPaths = useMemo(() => {
if (!selectedPaths) return undefined;