import { type ComponentPropsWithRef, useEffect, useState } from "react"; import type { FileContents } from "@pierre/diffs"; import { File } from "@pierre/diffs/react"; import { cachedFile, fileOptions, highlightFile } from "../../lib/highlighter"; import { cn } from "../../lib/utils"; import { Button } from "../../ui/button"; import { Tooltip, TooltipContent, TooltipTrigger } from "../../ui/tooltip"; import { FilePath } from "../file-path/file-path"; import { FileTree } from "../file-tree/file-tree"; import { IconFileTree, IconLoader } from "../../icons"; type FileViewProps = Omit, "children"> & { path: string; source?: string; pending?: boolean; error?: string; treePaths?: string[]; treePending?: boolean; onFileSelect?: (path: string, option?: Select) => void; }; type Select = { tab?: boolean; }; function contents(path: string, source: string): FileContents { return { name: path, contents: source }; } function useFile(path: string, source: string | undefined, pending: boolean) { let [state, setState] = useState< { path: string; source: string; html: string; } | null >(() => { if (source === undefined) return null; let file = contents(path, source); let html = cachedFile(file); return html ? { path, source, html } : null; }); useEffect(() => { if (source === undefined) return; let cancelled = false; let file = contents(path, source); highlightFile(file).then(html => { if (!cancelled) { setState(prev => prev?.path === path && prev.source === source && prev.html === html ? prev : { path, source, html } ); } }); return () => { cancelled = true; }; }, [path, source]); let file = source === undefined ? pending && state ? contents(state.path, state.source) : undefined : contents(path, source); let html = source === undefined ? pending && state ? state.html : undefined : state?.path === path && state.source === source ? state.html : file ? cachedFile(file) : undefined; return { file, html }; } function FileContent( { path, source, pending = false, error }: Pick< FileViewProps, "path" | "source" | "pending" | "error" >, ) { let { file, html } = useFile(path, source, pending); let loading = pending && !file; let empty = !loading && !error && source === undefined && !file; return (loading ? (
) : error || empty ? (
{error ?? "No file selected"}
) : file ? ( ) : null); } function FileBrowser( { path, treePaths, treePending, onFileSelect }: Pick< FileViewProps, "path" | "treePaths" | "treePending" | "onFileSelect" >, ) { return (treePending && !treePaths?.length ? (
) : treePaths?.length ? ( onFileSelect?.(next)} onOpenTab={next => onFileSelect?.(next, { tab: true })} /> ) : (
No files
)); } function FileView( { path, source, pending = false, error, treePaths, treePending = false, onFileSelect, className, ref, ...props }: FileViewProps, ) { let [tree, setTree] = useState(() => !path); let hasTree = treePaths !== undefined || treePending; let open = tree && hasTree; return (
{hasTree && ( setTree(open => !open)} > } /> {tree ? "Hide file tree" : "Show file tree"} )}
); } export { FileView }; export type { FileViewProps };