| 1 | import { useAtomValue } from "@effect/atom-react"; |
| 2 | import type { EnvironmentId, PreviewRuntime, PreviewSessionSnapshot } from "@t3tools/contracts"; |
| 3 | |
| 4 | import { isElectron } from "~/env"; |
| 5 | import { isPreviewSupportedInRuntime } from "~/previewStateStore"; |
| 6 | import { appAtomRegistry } from "~/rpc/atomRegistry"; |
| 7 | import { primaryEnvironmentIdAtom } from "~/state/primaryEnvironment"; |
| 8 | import { |
| 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 | */ |
| 20 | export 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 | */ |
| 35 | export 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. */ |
| 47 | export function isPreviewAvailableFor(environmentId: EnvironmentId): boolean { |
| 48 | return isPreviewSupportedInRuntime() || readEnvironmentSupportsServerBrowser(environmentId); |
| 49 | } |
| 50 | |
| 51 | export 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 | */ |
| 61 | export 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 | |
| 74 | export function useRendersServerTabNatively( |
| 75 | environmentId: EnvironmentId, |
| 76 | snapshot: Pick<PreviewSessionSnapshot, "runtime"> | null | undefined, |
| 77 | ): boolean { |
| 78 | return rendersServerTabNatively(environmentId, useAtomValue(primaryEnvironmentIdAtom), snapshot); |
| 79 | } |