diff --git a/src/components/FileBrowser.tsx b/src/components/FileBrowser.tsx
index 4896917fc..e653518bb 100644
--- a/src/components/FileBrowser.tsx
+++ b/src/components/FileBrowser.tsx
@@ -96,6 +96,8 @@ import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { Tabs, TabsList, TabsTrigger } from "./ui/tabs";
import { Textarea } from "./ui/textarea";
import { DirectoryBatchLoader } from "./file-browser/directoryBatchLoader";
+import { REMOTE_FILE_TREE_REASON } from "../lib/remote-capabilities";
+import { remoteRepositoryContaining } from "../lib/repository-adapter";
import { flattenExpandedTree } from "./file-browser/flattenTree";
// Helper to check if file is binary
@@ -1204,8 +1206,9 @@ export const FileBrowser = ({
})();
const workspacePath = workspace ? getFullWorkspacePath(workspace) : basePath;
+ const isRemoteTree = remoteRepositoryContaining(basePath) !== null;
useSWR(
- repoPath
+ repoPath && !isRemoteTree
? [
"ensure-workspace-indexed",
repoPath,
@@ -1221,8 +1224,9 @@ export const FileBrowser = ({
data: loadedRootEntries,
isLoading: isLoadingDir,
mutate: mutateRootEntries,
- } = useSWR(basePath ? ["list-directory", basePath] : null, async () =>
- filterHiddenEntries(await listDirectory(basePath)),
+ } = useSWR(
+ basePath && !isRemoteTree ? ["list-directory", basePath] : null,
+ async () => filterHiddenEntries(await listDirectory(basePath)),
);
const rootEntries = loadedRootEntries ?? EMPTY_DIRECTORY_ENTRIES;
@@ -2016,7 +2020,14 @@ export const FileBrowser = ({
{/* File Tree */}
- {isLoadingDir && rootEntries.length === 0 ? (
+ {isRemoteTree ? (
+
+ {REMOTE_FILE_TREE_REASON}
+
+ ) : isLoadingDir && rootEntries.length === 0 ? (
diff --git a/src/components/ShowWorkspace.tsx b/src/components/ShowWorkspace.tsx
index 2bbfab0e4..e2a8d3dfc 100644
--- a/src/components/ShowWorkspace.tsx
+++ b/src/components/ShowWorkspace.tsx
@@ -77,6 +77,8 @@ import {
} from "../lib/commitsTabLabel";
import { cn, getFullWorkspacePath, resolveReadmeImageSrc } from "../lib/utils";
import { sumWorkspaceLocFromLog } from "../lib/workspace-stack";
+import { REMOTE_FILE_TREE_REASON } from "../lib/remote-capabilities";
+import { RemoteOperationUnsupportedError } from "../lib/repository-adapter";
import type { AgentReviewComment } from "../lib/api-types-review";
import type { SessionCreationInfo } from "../types/sessions";
import { ChangesDiffViewer } from "./ChangesDiffViewer";
@@ -476,8 +478,11 @@ export const ShowWorkspace = ({
]);
return { entries, readme };
} catch (error) {
- console.error("Failed to load workspace overview:", error);
- return { entries: [], readme: null as string | null };
+ const unavailable = error instanceof RemoteOperationUnsupportedError;
+ if (!unavailable) {
+ console.error("Failed to load workspace overview:", error);
+ }
+ return { entries: [], readme: null as string | null, unavailable };
}
},
);
@@ -1290,7 +1295,9 @@ export const ShowWorkspace = ({
})}
{!overviewPending && rootEntries.length === 0 && (
- No files found
+ {overviewData?.unavailable
+ ? REMOTE_FILE_TREE_REASON
+ : "No files found"}
)}
diff --git a/src/lib/api-extra.ts b/src/lib/api-extra.ts
index e28ee05d8..4201cb89b 100644
--- a/src/lib/api-extra.ts
+++ b/src/lib/api-extra.ts
@@ -284,11 +284,21 @@ export const writeAgentCliFiles = (
export const cleanupAgentCliFiles = (paths: string[]): Promise
=>
invoke("cleanup_agent_cli_files", { paths });
-export const getFileModifiedAt = (path: string): Promise =>
- invoke("get_file_modified_at", { path });
+const FILE_TREE = "Browsing the file tree";
-export const listDirectory = (path: string): Promise =>
- invoke("list_directory", { path });
+export const getFileModifiedAt = async (
+ path: string,
+): Promise => {
+ assertLocalOperation(path, FILE_TREE);
+ return invoke("get_file_modified_at", { path });
+};
+
+export const listDirectory = async (
+ path: string,
+): Promise => {
+ assertLocalOperation(path, FILE_TREE);
+ return invoke("list_directory", { path });
+};
export interface DirectoryBatchResult {
path: string;
@@ -296,25 +306,31 @@ export interface DirectoryBatchResult {
error?: string;
}
-export const listDirectoriesBatch = (
+export const listDirectoriesBatch = async (
paths: string[],
-): Promise =>
- invoke("list_directories_batch", { paths });
+): Promise => {
+ for (const path of paths) assertLocalOperation(path, FILE_TREE);
+ return invoke("list_directories_batch", { paths });
+};
-export const lsWorkspaceWithStatus = (
+export const lsWorkspaceWithStatus = async (
repoPath: string,
workspaceId: number | null,
-): Promise =>
- invoke("ls_workspace_with_status", { repoPath, workspaceId });
+): Promise => {
+ assertLocalOperation(repoPath, FILE_TREE);
+ return invoke("ls_workspace_with_status", { repoPath, workspaceId });
+};
// Kept as the typed frontend counterpart of the registered Tauri command.
// eslint-disable-next-line local/no-unused-exported-ts-functions, local/require-tauri-api-exports-used
-export const listDirectoryCached = (
+export const listDirectoryCached = async (
repoPath: string,
workspaceId: number | null,
parentPath: string,
-): Promise =>
- invoke("list_directory_cached", { repoPath, workspaceId, parentPath });
+): Promise => {
+ assertLocalOperation(repoPath, FILE_TREE);
+ return invoke("list_directory_cached", { repoPath, workspaceId, parentPath });
+};
export interface SendArtifactRecord {
id: string;
diff --git a/src/lib/api.ts b/src/lib/api.ts
index 24add155a..a61822c08 100644
--- a/src/lib/api.ts
+++ b/src/lib/api.ts
@@ -124,16 +124,18 @@ export const archiveWorkspace = async (
});
};
-export const ensureWorkspaceIndexed = (
+export const ensureWorkspaceIndexed = async (
repoPath: string,
workspaceId: number | null,
workspacePath: string,
-): Promise =>
- invoke("ensure_workspace_indexed", {
+): Promise => {
+ assertLocalOperation(repoPath, "Indexing workspace files");
+ return invoke("ensure_workspace_indexed", {
repoPath,
workspaceId,
workspacePath,
});
+};
export const getSetting = (key: string): Promise =>
invoke("get_setting", { key });
@@ -247,11 +249,13 @@ export const listGitignoredPathSuggestions = (
): Promise =>
invoke("list_gitignored_path_suggestions", { repoPath });
-export const getWorkspaceReadme = (
+export const getWorkspaceReadme = async (
repoPath: string,
workspaceId: number | null,
-): Promise =>
- invoke("get_workspace_readme", { repoPath, workspaceId });
+): Promise => {
+ assertLocalOperation(repoPath, "Reading the workspace README");
+ return invoke("get_workspace_readme", { repoPath, workspaceId });
+};
export const getWorkspaceFileHunks = (
repoPath: string,
diff --git a/src/lib/remote-capabilities.ts b/src/lib/remote-capabilities.ts
index 44964b00c..aa085cd64 100644
--- a/src/lib/remote-capabilities.ts
+++ b/src/lib/remote-capabilities.ts
@@ -9,6 +9,9 @@
export const REMOTE_MERGE_REASON =
"Merging a remote workspace is not available yet. Push the branch and merge it from your hosting provider instead.";
+export const REMOTE_FILE_TREE_REASON =
+ "Browsing the file tree is not available for remote repositories yet. Use Go to file to open a file.";
+
export interface ActionCapability {
supported: boolean;
reason?: string;
diff --git a/src/lib/remote-path-guards.test.ts b/src/lib/remote-path-guards.test.ts
index 0191c639b..57a67002d 100644
--- a/src/lib/remote-path-guards.test.ts
+++ b/src/lib/remote-path-guards.test.ts
@@ -25,18 +25,15 @@ const NOT_REPOSITORY_PATHS = new Set(["cleanupAgentCliFiles"]);
const KNOWN_UNGUARDED: string[] = [
"apply_stash",
"delete_stash",
- "ensure_workspace_indexed",
"export_stash_git_patch",
"get_cached_pr_ci_status",
"get_cached_pr_info",
- "get_file_modified_at",
"get_git_remote_url",
"get_log_timeseries",
"get_pr_checks_via_gh",
"get_pr_info_via_gh",
"get_repo_logs",
"get_stash_diff",
- "get_workspace_readme",
"get_workspace_setup_status",
"github_open_or_create_workspace_from_issue",
"init_repo",
@@ -44,9 +41,6 @@ const KNOWN_UNGUARDED: string[] = [
"jj_check_branch_exists",
"list_cached_pr_ci_statuses",
"list_cached_pr_statuses",
- "list_directories_batch",
- "list_directory",
- "list_directory_cached",
"list_gitignored_path_suggestions",
"list_installed_skills",
"list_send_artifacts",
@@ -55,7 +49,6 @@ const KNOWN_UNGUARDED: string[] = [
"list_workflow_runs",
"list_workflows",
"load_repo_yaml_config",
- "ls_workspace_with_status",
"open_or_create_workspace_from_pr",
"refresh_pr_branch_status",
"refresh_pr_statuses",