1import {
2 classifyMarkdownImageSource,
3 markdownImageSourceFragment,
4} from "@t3tools/client-runtime/markdown-images";
5import { splitFilePathPosition } from "@t3tools/shared/fileLinks";
6import {
7 mediaFileReference,
8 mediaUrlReference,
9 mediaReferenceFileName,
10} from "@t3tools/client-runtime/media-reference";
11import type { EnvironmentId, ThreadId } from "@t3tools/contracts";
12import { normalizeNativeMarkdownUrl } from "./markdownLinks";
13import { resolveMobileMarkdownMediaSource } from "./markdownMediaSource";
14
15import type { FilePreviewSource } from "../components/FilePreviewModal";
16import type { MediaVideoPreviewSource } from "./videoPreviewSource";
17import type { MediaActionsSource } from "./mediaActionsSource";
18
19/** Resolves only explicit media references. Ordinary links keep their existing navigation. */
20export function resolveMarkdownMediaPreview(
21 href: string,
22 input: {
23 readonly environmentId: EnvironmentId;
24 readonly threadId: ThreadId | undefined;
25 readonly workspaceRoot: string | null | undefined;
26 /** Captured markdown may only open media with a direct URL. */
27 readonly captured?: boolean;
28 /** Image syntax can target an endpoint without a recognizable extension. */
29 readonly imageEmbed?: boolean;
30 },
31):
32 | { readonly kind: "image"; readonly source: FilePreviewSource }
33 | { readonly kind: "pdf"; readonly source: FilePreviewSource }
34 | { readonly kind: "video"; readonly source: MediaVideoPreviewSource }
35 | null {
36 const classified = classifyMarkdownImageSource(href, input.workspaceRoot);
37 if (classified._tag !== "Blocked") {
38 const path =
39 classified._tag === "Direct" ? classified.uri : splitFilePathPosition(classified.path).path;
40 const reference =
41 classified._tag === "Direct"
42 ? mediaUrlReference(path)
43 : mediaFileReference(path, input.workspaceRoot);
44 const name = reference && mediaReferenceFileName(reference);
45 if (name && /\.pdf$/i.test(name)) {
46 const common = { kind: "pdf" as const, name, mimeType: "application/pdf" };
47 if (classified._tag === "Direct") {
48 const uri = normalizeNativeMarkdownUrl(path);
49 return {
50 kind: "pdf",
51 source: {
52 ...common,
53 uri,
54 actionsSource: {
55 ...(reference ? { reference } : {}),
56 uri,
57 name,
58 mimeType: common.mimeType,
59 },
60 },
61 };
62 }
63 if (input.captured) return null;
64 const resource = input.threadId
65 ? { _tag: "media-file" as const, threadId: input.threadId, path }
66 : input.workspaceRoot
67 ? { _tag: "draft-workspace-file" as const, cwd: input.workspaceRoot, path }
68 : null;
69 if (!resource) return null;
70 return {
71 kind: "pdf",
72 source: {
73 ...common,
74 environmentId: input.environmentId,
75 resource,
76 srcFragment: markdownImageSourceFragment(href),
77 actionsSource: {
78 ...(reference ? { reference } : {}),
79 environmentId: input.environmentId,
80 threadId: input.threadId,
81 resource,
82 name,
83 mimeType: common.mimeType,
84 },
85 },
86 };
87 }
88 }
89 const media = resolveMobileMarkdownMediaSource(href, input);
90 if (media === null || media.access === "unavailable") return null;
91 if (input.captured && media.access !== "direct") return null;
92 const { kind, name, mimeType, reference, srcFragment } = media;
93
94 const target =
95 media.access === "direct"
96 ? { uri: normalizeNativeMarkdownUrl(media.uri) }
97 : {
98 environmentId: input.environmentId,
99 resource: media.resource,
100 ...(srcFragment ? { srcFragment } : {}),
101 };
102 const actionsSource: MediaActionsSource =
103 media.access === "direct"
104 ? { reference, uri: media.uri, name, mimeType }
105 : {
106 reference,
107 environmentId: input.environmentId,
108 threadId: input.threadId,
109 resource: media.resource,
110 name,
111 mimeType,
112 };
113 return kind === "video"
114 ? { kind, source: { type: "media", name, mimeType, ...target, actionsSource } }
115 : { kind, source: { kind, name, ...target, actionsSource } };
116}