131 lines
3.3 KiB
TypeScript
131 lines
3.3 KiB
TypeScript
import {
|
|
useMutation,
|
|
useQuery,
|
|
useQueryClient,
|
|
type UseMutationResult,
|
|
type UseQueryResult,
|
|
} from "@tanstack/react-query";
|
|
|
|
import { apiClient } from "@/lib/api/client";
|
|
|
|
export interface FileEntry {
|
|
name: string;
|
|
path: string;
|
|
isDir: boolean;
|
|
size?: number;
|
|
modTime?: string;
|
|
language?: string;
|
|
}
|
|
|
|
const LANGUAGE_BY_EXTENSION: Record<string, string> = {
|
|
css: "css",
|
|
go: "go",
|
|
html: "html",
|
|
js: "javascript",
|
|
jsx: "javascript",
|
|
json: "json",
|
|
md: "markdown",
|
|
py: "python",
|
|
ts: "typescript",
|
|
tsx: "typescript",
|
|
yaml: "yaml",
|
|
yml: "yaml",
|
|
};
|
|
|
|
export function inferLanguage(name: string): string | undefined {
|
|
const ext = name.split(".").pop()?.toLowerCase();
|
|
return ext ? LANGUAGE_BY_EXTENSION[ext] : undefined;
|
|
}
|
|
|
|
export interface ReadFileResponse {
|
|
path: string;
|
|
content: string;
|
|
}
|
|
|
|
function fileListQueryKey(workspaceId: string, path: string) {
|
|
return ["workspaces", workspaceId, "files", path || "."] as const;
|
|
}
|
|
|
|
async function fetchFileList(
|
|
workspaceId: string,
|
|
path: string,
|
|
): Promise<FileEntry[]> {
|
|
const queryPath = path || ".";
|
|
const url =
|
|
`/api/workspaces/${encodeURIComponent(workspaceId)}/files?` +
|
|
new URLSearchParams({ path: queryPath }).toString();
|
|
const res = await apiClient(url);
|
|
if (!res.ok) {
|
|
throw new Error(`Failed to list files: ${res.status}`);
|
|
}
|
|
return (await res.json()) as FileEntry[];
|
|
}
|
|
|
|
export function fileReadQueryKey(workspaceId: string, path: string) {
|
|
return ["workspaces", workspaceId, "files", "read", path] as const;
|
|
}
|
|
|
|
async function fetchFileRead(
|
|
workspaceId: string,
|
|
path: string,
|
|
): Promise<ReadFileResponse> {
|
|
const url =
|
|
`/api/workspaces/${encodeURIComponent(workspaceId)}/files/read?` +
|
|
new URLSearchParams({ path }).toString();
|
|
const res = await apiClient(url);
|
|
if (!res.ok) {
|
|
throw new Error(`Failed to read file: ${res.status}`);
|
|
}
|
|
return (await res.json()) as ReadFileResponse;
|
|
}
|
|
|
|
export function useFileRead(
|
|
workspaceId: string | null,
|
|
path: string | null,
|
|
): UseQueryResult<ReadFileResponse, Error> {
|
|
return useQuery({
|
|
queryKey: fileReadQueryKey(workspaceId ?? "", path ?? ""),
|
|
queryFn: () => fetchFileRead(workspaceId!, path!),
|
|
enabled: !!workspaceId && !!path,
|
|
});
|
|
}
|
|
|
|
export function useFileList(
|
|
workspaceId: string | null,
|
|
path: string,
|
|
enabled = true,
|
|
): UseQueryResult<FileEntry[], Error> {
|
|
const queryPath = path || ".";
|
|
return useQuery({
|
|
queryKey: fileListQueryKey(workspaceId ?? "", queryPath),
|
|
queryFn: () => fetchFileList(workspaceId!, queryPath),
|
|
enabled: !!workspaceId && enabled,
|
|
});
|
|
}
|
|
|
|
export function useFileWrite(): UseMutationResult<
|
|
void,
|
|
Error,
|
|
{ path: string; content: string; workspaceId: string }
|
|
> {
|
|
const queryClient = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async ({ workspaceId, path, content }) => {
|
|
const url = `/api/workspaces/${encodeURIComponent(workspaceId)}/files/write`;
|
|
const res = await apiClient(url, {
|
|
method: "PUT",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ path, content }),
|
|
});
|
|
if (!res.ok) {
|
|
throw new Error(`Failed to save file: ${res.status}`);
|
|
}
|
|
},
|
|
onSuccess: (_, variables) => {
|
|
queryClient.invalidateQueries({
|
|
queryKey: fileReadQueryKey(variables.workspaceId, variables.path),
|
|
});
|
|
},
|
|
});
|
|
}
|