1import { useAtomValue } from "@effect/atom-react";
2import type { EnvironmentId, PreviewRuntime, PreviewSessionSnapshot } from "@t3tools/contracts";
3
4import { isElectron } from "~/env";
5import { isPreviewSupportedInRuntime } from "~/previewStateStore";
6import { appAtomRegistry } from "~/rpc/atomRegistry";
7import { primaryEnvironmentIdAtom } from "~/state/primaryEnvironment";
8import {
9 readEnvironmentSupportsServerBrowser,
10 useEnvironmentSupportsServerBrowser,
11} from "~/state/entities";
12
13/**
14 * Where a tab the user opens should run. The desktop app draws its own
15 * server's tabs natively, so those stay agent-drivable at no cost. A remote
16 * environment's tabs would stream, so the desktop opens them in its own
17 * browser instead: no latency, and it reaches what this computer reaches. The
18 * user can move a tab to the environment when only it can reach the page.
19 */
20export function previewRuntimeFor(environmentId: EnvironmentId): PreviewRuntime | undefined {
21 if (!readEnvironmentSupportsServerBrowser(environmentId)) return undefined;
22 if (
23 isPreviewSupportedInRuntime() &&
24 environmentId !== appAtomRegistry.get(primaryEnvironmentIdAtom)
25 ) {
26 return undefined;
27 }
28 return "server";
29}
30
31/**
32 * The other browser a tab can move to, or null when it has none: a desktop
33 * tab of a remote environment can move to that environment's browser, and back.
34 */
35export function alternatePreviewRuntime(
36 environmentId: EnvironmentId,
37 primaryEnvironmentId: EnvironmentId | null,
38 serverBrowser: boolean,
39 snapshot: Pick<PreviewSessionSnapshot, "runtime"> | null | undefined,
40): PreviewRuntime | null {
41 if (!snapshot || !serverBrowser || !isPreviewSupportedInRuntime()) return null;
42 if (environmentId === primaryEnvironmentId) return null;
43 return snapshot.runtime === "server" ? "desktop" : "server";
44}
45
46/** Electron hosts its own browser tabs; other clients need the environment to host them. */
47export function isPreviewAvailableFor(environmentId: EnvironmentId): boolean {
48 return isPreviewSupportedInRuntime() || readEnvironmentSupportsServerBrowser(environmentId);
49}
50
51export function usePreviewAvailable(environmentId: EnvironmentId | null): boolean {
52 const serverBrowser = useEnvironmentSupportsServerBrowser(environmentId);
53 return isPreviewSupportedInRuntime() || serverBrowser;
54}
55
56/**
57 * Whether this client draws a server tab with its own `<webview>`. The desktop
58 * app renders tabs of the server it launched, which drives them over the
59 * desktop browser channel; every other client and environment streams them.
60 */
61export function rendersServerTabNatively(
62 environmentId: EnvironmentId,
63 primaryEnvironmentId: EnvironmentId | null,
64 snapshot: Pick<PreviewSessionSnapshot, "runtime"> | null | undefined,
65): boolean {
66 return (
67 isElectron &&
68 snapshot?.runtime === "server" &&
69 primaryEnvironmentId !== null &&
70 environmentId === primaryEnvironmentId
71 );
72}
73
74export function useRendersServerTabNatively(
75 environmentId: EnvironmentId,
76 snapshot: Pick<PreviewSessionSnapshot, "runtime"> | null | undefined,
77): boolean {
78 return rendersServerTabNatively(environmentId, useAtomValue(primaryEnvironmentIdAtom), snapshot);
79}