1import type { AssetResource, EnvironmentId, ThreadId } from "@t3tools/contracts";
2import { useMemo } from "react";
3
4import { useAssetUrlState, useRefreshAssetUrl } from "../../state/assets";
5import {
6 isAbsolutePath,
7 isAudioPreviewFile,
8 isVideoPreviewFile,
9 resolveWorkspaceFilePath,
10} from "./filePath";
11
12export function useWorkspaceFileAssetUrlState(props: {
13 readonly cwd: string | null;
14 readonly environmentId: EnvironmentId | null;
15 readonly relativePath: string | null;
16 readonly threadId: ThreadId | null;
17 /** A draft's workspace root, used only when there is no thread to resolve one from. */
18 readonly draftCwd?: string | null;
19}) {
20 const absolutePath = useMemo(
21 () =>
22 props.cwd !== null && props.relativePath !== null
23 ? resolveWorkspaceFilePath(props.cwd, props.relativePath)
24 : null,
25 [props.cwd, props.relativePath],
26 );
27
28 // Video and audio stream from an exact-file URL, and so does anything outside
29 // the workspace, where no workspace-scoped URL can exist.
30 const relativePath = props.relativePath;
31 const draftCwd = props.draftCwd ?? null;
32 const resource = useMemo<AssetResource | null>(() => {
33 if (absolutePath === null || relativePath === null) return null;
34 if (props.threadId !== null) {
35 return {
36 _tag:
37 isVideoPreviewFile(absolutePath) ||
38 isAudioPreviewFile(absolutePath) ||
39 isAbsolutePath(relativePath)
40 ? "media-file"
41 : "workspace-file",
42 threadId: props.threadId,
43 path: absolutePath,
44 };
45 }
46 // A project draft has no thread, so it names its workspace root explicitly.
47 if (draftCwd === null) return null;
48 return { _tag: "draft-workspace-file", cwd: draftCwd, path: relativePath };
49 }, [absolutePath, relativePath, props.threadId, draftCwd]);
50 const state = useAssetUrlState(props.environmentId, resource);
51 const refresh = useRefreshAssetUrl(props.environmentId, resource);
52 return { ...state, resource, refresh };
53}