121 lines · 4.1 KB · TypeScript
RawDownload
import type {  AssetCreateUrlResult,  AssetResource,  EnvironmentId,  PreviewOpenInput,  PreviewSessionSnapshot,  ScopedThreadRef,} from "@t3tools/contracts";import { mediaFileReference } from "@t3tools/client-runtime/media-reference";import {  type AtomCommandResult,  mapAtomCommandResult,} from "@t3tools/client-runtime/state/runtime";import * as Cause from "effect/Cause";import * as Data from "effect/Data";import { AsyncResult } from "effect/unstable/reactivity"; import { resolveAssetUrl } from "~/assets/assetUrls";import {  applyPreviewServerSnapshot,  isPreviewSupportedInRuntime,  rememberPreviewUrl,} from "~/previewStateStore";import { useRightPanelStore } from "~/rightPanelStore"; import {  browserDefaultOpenProfileId,  browserDefaultOpenViewport,  resolveBrowserDefaults,} from "./browserDefaults"; export const isBrowserPreviewFile = (path: string): boolean =>  /\.(?:html?|pdf)$/i.test(path.split(/[?#]/, 1)[0] ?? ""); export class BrowserPreviewUnavailableError extends Data.TaggedError(  "BrowserPreviewUnavailableError",)<{  readonly message: string;}> {} export type OpenPreviewMutation<E = unknown> = (input: {  readonly environmentId: EnvironmentId;  readonly input: PreviewOpenInput;}) => Promise<AtomCommandResult<PreviewSessionSnapshot, E>>; export async function openUrlInPreview<E>(input: {  readonly threadRef: ScopedThreadRef;  readonly url: string;  readonly openPreview: OpenPreviewMutation<E>;}): Promise<AtomCommandResult<void, E>> {  const defaults = await resolveBrowserDefaults();  const result = await input.openPreview({    environmentId: input.threadRef.environmentId,    input: {      threadId: input.threadRef.threadId,      url: input.url,      // Built here rather than via `openPreviewSession` because this path      // maps the result differently, so the configured defaults have to be      // applied explicitly or file/link opens would ignore them.      viewport: browserDefaultOpenViewport(defaults),      profileId: browserDefaultOpenProfileId(defaults),    },  });  return mapAtomCommandResult(result, (snapshot) => {    applyPreviewServerSnapshot(input.threadRef, snapshot);    rememberPreviewUrl(input.threadRef, input.url);    useRightPanelStore.getState().openBrowser(input.threadRef, snapshot.tabId);  });} /** * Opens a browser document in the integrated browser. Inside the workspace the * page may load sibling assets; a file outside it is served on its own. */export async function openFileInPreview<AssetError, PreviewError>(input: {  readonly threadRef: ScopedThreadRef;  readonly filePath: string;  readonly workspaceRoot: string | undefined;  readonly httpBaseUrl: string;  readonly createAssetUrl: (input: {    readonly environmentId: EnvironmentId;    readonly input: { readonly resource: AssetResource };  }) => Promise<AtomCommandResult<AssetCreateUrlResult, AssetError>>;  readonly openPreview: OpenPreviewMutation<PreviewError>;}): Promise<AtomCommandResult<void, AssetError | PreviewError | BrowserPreviewUnavailableError>> {  if (!isPreviewSupportedInRuntime()) {    return AsyncResult.failure(      Cause.fail(        new BrowserPreviewUnavailableError({          message: "The integrated browser is unavailable in this runtime.",        }),      ),    );  }  const insideWorkspace =    mediaFileReference(input.filePath, input.workspaceRoot).relativePath !== undefined;  const assetResult = await input.createAssetUrl({    environmentId: input.threadRef.environmentId,    input: {      resource: {        _tag: insideWorkspace ? "workspace-file" : "media-file",        threadId: input.threadRef.threadId,        path: input.filePath,      },    },  });  if (assetResult._tag === "Failure") {    return AsyncResult.failure(assetResult.cause);  }  const assetUrl = resolveAssetUrl(input.httpBaseUrl, assetResult.value.relativeUrl);  if (assetUrl === null) {    return AsyncResult.failure(      Cause.die(new Error("The environment returned an invalid asset URL.")),    );  }  return openUrlInPreview({    threadRef: input.threadRef,    url: assetUrl,    openPreview: input.openPreview,  });}