1import { resolveFilesystemReadAccess } from "@t3tools/client-runtime/state/filesystem";
2import { useEnvironmentPresentation } from "../state/presentation";
3import { useEnvironmentQuery } from "../state/query";
4import { resolveMobileMarkdownMediaSource } from "../lib/markdownMediaSource";
5import { resolveAssetUrl } from "@t3tools/client-runtime/state/assets";
6import type { EnvironmentId, ThreadId } from "@t3tools/contracts";
7import type { MarkdownImageSourceResolver } from "./SelectableMarkdownText.types";
8import { normalizeNativeMarkdownUrl } from "../lib/markdownLinks";
9import { useCallback } from "react";
10import * as Option from "effect/Option";
11
12import { assetEnvironment } from "../state/assets";
13import { environmentSession, usePreparedConnection } from "../state/session";
14import { useAtomQueryRunner } from "../state/use-atom-query-runner";
15
16/** Native nested images use the same signed asset queries as app-owned media. */
17export function useMarkdownImageSource(props: {
18 readonly environmentId: EnvironmentId;
19 readonly threadId: ThreadId | null;
20 readonly workspaceRoot: string | null;
21 readonly captured?: boolean;
22}): MarkdownImageSourceResolver {
23 const { environmentId, threadId, workspaceRoot, captured } = props;
24 const connection = usePreparedConnection(environmentId);
25 const environment = useEnvironmentPresentation(environmentId);
26 const connectionPhase = environment.presentation?.connection.phase;
27 const session = useEnvironmentQuery(environmentSession.sessionStateAtom(environmentId));
28 const fileAccess = resolveFilesystemReadAccess({
29 isCatalogReady: environment.isReady,
30 connection: environment.presentation?.connection ?? null,
31 session: session.data,
32 sessionError: session.error,
33 });
34 const httpBaseUrl = Option.isSome(connection) ? connection.value.httpBaseUrl : null;
35 const createUrl = useAtomQueryRunner(assetEnvironment.createUrl, { reportFailure: false });
36 return useCallback(
37 async (image) => {
38 const media = resolveMobileMarkdownMediaSource(image.href, {
39 threadId: threadId ?? undefined,
40 workspaceRoot,
41 imageEmbed: true,
42 });
43 if (media?.access === "direct") return { uri: normalizeNativeMarkdownUrl(media.uri) };
44 if (
45 captured ||
46 !fileAccess.canReadFiles ||
47 media === null ||
48 media.access === "unavailable" ||
49 httpBaseUrl === null ||
50 connectionPhase === "offline" ||
51 connectionPhase === "error" ||
52 connectionPhase === "unsupported" ||
53 connectionPhase === "reconnecting"
54 )
55 throw new Error("Markdown image is unavailable");
56 const result = await createUrl({ environmentId, input: { resource: media.resource } });
57 if (result._tag !== "Success") throw new Error("Could not authorize Markdown image");
58 const uri = resolveAssetUrl(httpBaseUrl, result.value.relativeUrl);
59 if (uri === null) throw new Error("Markdown image URL is invalid");
60 return { uri };
61 },
62 [
63 captured,
64 fileAccess.canReadFiles,
65 connectionPhase,
66 createUrl,
67 environmentId,
68 httpBaseUrl,
69 threadId,
70 workspaceRoot,
71 ],
72 );
73}