diff --git a/apps/desktop/src/app/DesktopApp.ts b/apps/desktop/src/app/DesktopApp.ts index 214fd383e04..c1a88ef301a 100644 --- a/apps/desktop/src/app/DesktopApp.ts +++ b/apps/desktop/src/app/DesktopApp.ts @@ -170,12 +170,14 @@ const bootstrap = Effect.gen(function* () { const rendererTarget = environment.isDevelopment ? Option.getOrThrow(environment.devServerUrl) : backendConfig.httpBaseUrl; - yield* electronProtocol.registerDesktopProtocol({ - scheme: ElectronProtocol.getDesktopScheme(environment.isDevelopment), - targetOrigin: rendererTarget, - backendOrigin: backendConfig.httpBaseUrl, - clerkFrontendApiHostname: DesktopClerk.desktopClerkFrontendApiHostname, - }); + if (!environment.isDevelopment || DesktopClerk.isDesktopClerkBridgeEnabled()) { + yield* electronProtocol.registerDesktopProtocol({ + scheme: ElectronProtocol.getDesktopScheme(environment.isDevelopment), + targetOrigin: rendererTarget, + backendOrigin: backendConfig.httpBaseUrl, + clerkFrontendApiHostname: DesktopClerk.getDesktopClerkFrontendApiHostname(), + }); + } yield* logBootstrapInfo("bootstrap resolved backend endpoint", { baseUrl: backendConfig.httpBaseUrl.href, }); diff --git a/apps/desktop/src/app/DesktopClerk.test.ts b/apps/desktop/src/app/DesktopClerk.test.ts index 9b5ed56d1f3..6e8b7355d64 100644 --- a/apps/desktop/src/app/DesktopClerk.test.ts +++ b/apps/desktop/src/app/DesktopClerk.test.ts @@ -31,7 +31,7 @@ const makeDesktopClerkLayer = (isDevelopment = true) => { isDevelopment, } as unknown as DesktopEnvironment.DesktopEnvironment["Service"]); - return DesktopClerk.layer.pipe( + return DesktopClerk.makeDesktopClerkLayer(true).pipe( Layer.provide(Layer.succeed(DesktopEnvironment.DesktopEnvironment, environment)), ); }; @@ -76,6 +76,27 @@ describe("DesktopClerk", () => { }); }); + it.effect("skips the SDK bridge when the build has no Clerk publishable key", () => { + storageMock.mockReturnValue(storageAdapter); + createClerkBridgeMock.mockReturnValue({ cleanup: vi.fn() }); + + return Effect.gen(function* () { + yield* Effect.scoped( + Layer.build(DesktopClerk.makeDesktopClerkLayer(false)).pipe( + Effect.provide( + Layer.succeed(DesktopEnvironment.DesktopEnvironment, { + stateDir: "/tmp/t3-state", + isDevelopment: true, + } as unknown as DesktopEnvironment.DesktopEnvironment["Service"]), + ), + ), + ); + + assert.deepEqual(storageMock.mock.calls, []); + assert.deepEqual(createClerkBridgeMock.mock.calls, []); + }); + }); + it.effect("preserves bridge initialization failures", () => { const cause = new Error("bridge initialization failed"); storageMock.mockReturnValue(storageAdapter); diff --git a/apps/desktop/src/app/DesktopClerk.ts b/apps/desktop/src/app/DesktopClerk.ts index 0e283f8dd0c..8974fa9619b 100644 --- a/apps/desktop/src/app/DesktopClerk.ts +++ b/apps/desktop/src/app/DesktopClerk.ts @@ -7,13 +7,21 @@ import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; import * as Scope from "effect/Scope"; -import { clerkFrontendApiHostnameFromPublishableKey } from "@t3tools/shared/relayAuth"; import * as ElectronApp from "../electron/ElectronApp.ts"; import * as ElectronProtocol from "../electron/ElectronProtocol.ts"; import * as ElectronWindow from "../electron/ElectronWindow.ts"; +import { + getDesktopClerkFrontendApiHostname, + isDesktopClerkBridgeEnabled, + resolveDesktopClerkFrontendApiHostname, +} from "./DesktopClerkConfig.ts"; import * as DesktopEnvironment from "./DesktopEnvironment.ts"; -declare const __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__: string | undefined; +export { + getDesktopClerkFrontendApiHostname, + isDesktopClerkBridgeEnabled, + resolveDesktopClerkFrontendApiHostname, +} from "./DesktopClerkConfig.ts"; export class DesktopClerkBridgeInitializationError extends Schema.TaggedErrorClass()( "DesktopClerkBridgeInitializationError", @@ -52,25 +60,6 @@ export class DesktopClerk extends Context.Service< } >()("@t3tools/desktop/app/DesktopClerk") {} -export function resolveDesktopClerkFrontendApiHostname( - publishableKey: string | undefined, -): string | undefined { - const normalizedKey = publishableKey?.trim(); - if (!normalizedKey) return undefined; - - try { - return clerkFrontendApiHostnameFromPublishableKey(normalizedKey); - } catch { - return undefined; - } -} - -export const desktopClerkFrontendApiHostname = resolveDesktopClerkFrontendApiHostname( - typeof __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__ === "undefined" - ? undefined - : __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__, -); - export function createDesktopClerkBridge(stateDir: string, isDevelopment: boolean) { return createClerkBridge({ storage: storage({ path: stateDir }), @@ -82,54 +71,60 @@ export function createDesktopClerkBridge(stateDir: string, isDevelopment: boolea }); } -export const make = Effect.gen(function* () { - const environment = yield* DesktopEnvironment.DesktopEnvironment; - yield* Effect.acquireRelease( - Effect.try({ - try: () => createDesktopClerkBridge(environment.stateDir, environment.isDevelopment), - catch: (cause) => - new DesktopClerkBridgeInitializationError({ - stateDir: environment.stateDir, - isDevelopment: environment.isDevelopment, - cause, +export function makeDesktopClerkLayer(enabled = isDesktopClerkBridgeEnabled()) { + const make = Effect.gen(function* () { + const environment = yield* DesktopEnvironment.DesktopEnvironment; + if (enabled) { + yield* Effect.acquireRelease( + Effect.try({ + try: () => createDesktopClerkBridge(environment.stateDir, environment.isDevelopment), + catch: (cause) => + new DesktopClerkBridgeInitializationError({ + stateDir: environment.stateDir, + isDevelopment: environment.isDevelopment, + cause, + }), }), - }), - (bridge) => - Effect.try({ - try: () => bridge.cleanup(), - catch: (cause) => - new DesktopClerkBridgeCleanupError({ - stateDir: environment.stateDir, - isDevelopment: environment.isDevelopment, - cause, - }), - }).pipe(Effect.orDie), - ); + (bridge) => + Effect.try({ + try: () => bridge.cleanup(), + catch: (cause) => + new DesktopClerkBridgeCleanupError({ + stateDir: environment.stateDir, + isDevelopment: environment.isDevelopment, + cause, + }), + }).pipe(Effect.orDie), + ); + } - return DesktopClerk.of({ - configure: Effect.gen(function* () { - const electronApp = yield* ElectronApp.ElectronApp; - const electronWindow = yield* ElectronWindow.ElectronWindow; - const context = yield* Effect.context(); - const runPromise = Effect.runPromiseWith(context); + return DesktopClerk.of({ + configure: Effect.gen(function* () { + const electronApp = yield* ElectronApp.ElectronApp; + const electronWindow = yield* ElectronWindow.ElectronWindow; + const context = yield* Effect.context(); + const runPromise = Effect.runPromiseWith(context); - if (!(yield* electronApp.requestSingleInstanceLock)) { - yield* electronApp.quit; - return yield* Effect.interrupt; - } + if (!(yield* electronApp.requestSingleInstanceLock)) { + yield* electronApp.quit; + return yield* Effect.interrupt; + } - yield* electronApp.on("second-instance", () => { - void runPromise( - Effect.gen(function* () { - const mainWindow = yield* electronWindow.currentMainOrFirst; - if (Option.isSome(mainWindow)) { - yield* electronWindow.reveal(mainWindow.value); - } - }), - ); - }); - }).pipe(Effect.withSpan("desktop.clerk.configure")), + yield* electronApp.on("second-instance", () => { + void runPromise( + Effect.gen(function* () { + const mainWindow = yield* electronWindow.currentMainOrFirst; + if (Option.isSome(mainWindow)) { + yield* electronWindow.reveal(mainWindow.value); + } + }), + ); + }); + }).pipe(Effect.withSpan("desktop.clerk.configure")), + }); }); -}); -export const layer = Layer.effect(DesktopClerk, make); + return Layer.effect(DesktopClerk, make); +} + +export const layer = makeDesktopClerkLayer(); diff --git a/apps/desktop/src/app/DesktopClerkConfig.ts b/apps/desktop/src/app/DesktopClerkConfig.ts new file mode 100644 index 00000000000..548bd5ea3ac --- /dev/null +++ b/apps/desktop/src/app/DesktopClerkConfig.ts @@ -0,0 +1,32 @@ +declare const __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__: string | undefined; + +export function resolveDesktopClerkFrontendApiHostname( + publishableKey: string | undefined, +): string | undefined { + const normalizedKey = publishableKey?.trim(); + if (!normalizedKey) return undefined; + + try { + const encodedFrontendApi = normalizedKey.split("_").slice(2).join("_"); + const frontendApi = globalThis.atob(encodedFrontendApi).replace(/\$$/u, ""); + if (frontendApi.length === 0 || frontendApi.includes("/")) return undefined; + + return new URL(`https://${frontendApi}`).hostname; + } catch { + return undefined; + } +} + +const desktopClerkFrontendApiHostname = resolveDesktopClerkFrontendApiHostname( + typeof __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__ === "undefined" + ? undefined + : __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__, +); + +export function getDesktopClerkFrontendApiHostname(): string | undefined { + return desktopClerkFrontendApiHostname; +} + +export function isDesktopClerkBridgeEnabled(): boolean { + return Boolean(desktopClerkFrontendApiHostname); +} diff --git a/apps/desktop/src/preload.ts b/apps/desktop/src/preload.ts index 6f126f41334..71cd63f3f59 100644 --- a/apps/desktop/src/preload.ts +++ b/apps/desktop/src/preload.ts @@ -4,12 +4,16 @@ import type { DesktopPreviewRecordingFrame, DesktopPreviewTabState, } from "@t3tools/contracts"; -import { exposeClerkBridge } from "@clerk/electron/preload"; import { contextBridge, ipcRenderer } from "electron"; +import { isDesktopClerkBridgeEnabled } from "./app/DesktopClerkConfig.ts"; import * as IpcChannels from "./ipc/channels.ts"; -exposeClerkBridge({ passkeys: true }); +if (isDesktopClerkBridgeEnabled()) { + const { exposeClerkBridge } = + require("@clerk/electron/preload") as typeof import("@clerk/electron/preload"); + exposeClerkBridge({ passkeys: true }); +} function unwrapEnsureSshEnvironmentResult(result: unknown) { if ( diff --git a/apps/desktop/src/window/DesktopWindow.test.ts b/apps/desktop/src/window/DesktopWindow.test.ts index 76413dd0b55..efc57720b92 100644 --- a/apps/desktop/src/window/DesktopWindow.test.ts +++ b/apps/desktop/src/window/DesktopWindow.test.ts @@ -231,7 +231,7 @@ describe("DesktopWindow", () => { assert.equal(yield* Ref.get(createCount), 1); assert.isTrue(createdWindowOptions[0]?.disableAutoHideCursor); assert.deepEqual(fakeWindow.setAutoHideCursor.mock.calls, [[false]]); - assert.deepEqual(fakeWindow.loadURL.mock.calls[0], ["t3code-dev://app/"]); + assert.deepEqual(fakeWindow.loadURL.mock.calls[0], ["http://127.0.0.1:5733/"]); assert.equal(fakeWindow.openDevTools.mock.calls.length, 1); }).pipe(Effect.provide(layer)); }), diff --git a/apps/desktop/src/window/DesktopWindow.ts b/apps/desktop/src/window/DesktopWindow.ts index e6cfce3c54f..74228649817 100644 --- a/apps/desktop/src/window/DesktopWindow.ts +++ b/apps/desktop/src/window/DesktopWindow.ts @@ -7,6 +7,7 @@ import * as Ref from "effect/Ref"; import type * as Electron from "electron"; import * as DesktopAssets from "../app/DesktopAssets.ts"; +import * as DesktopClerk from "../app/DesktopClerk.ts"; import * as DesktopEnvironment from "../app/DesktopEnvironment.ts"; import { makeComponentLogger } from "../app/DesktopObservability.ts"; import * as DesktopState from "../app/DesktopState.ts"; @@ -159,7 +160,10 @@ export const make = Effect.gen(function* () { DesktopWindowError > { yield* previewManager.getBrowserSession(); - const applicationUrl = getDesktopUrl(environment.isDevelopment); + const applicationUrl = + environment.isDevelopment && !DesktopClerk.isDesktopClerkBridgeEnabled() + ? Option.getOrThrow(environment.devServerUrl).href + : getDesktopUrl(environment.isDevelopment); const iconPaths = yield* assets.iconPaths; const iconOption = getIconOption(iconPaths, environment.platform); const shouldUseDarkColors = yield* electronTheme.shouldUseDarkColors; diff --git a/apps/web/src/AppRoot.test.tsx b/apps/web/src/AppRoot.test.tsx deleted file mode 100644 index 9112e31cb86..00000000000 --- a/apps/web/src/AppRoot.test.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Children, isValidElement, type ReactElement, type ReactNode } from "react"; -import { RouterProvider } from "@tanstack/react-router"; -import { describe, expect, it } from "vite-plus/test"; - -import { ElectronBrowserHost } from "./browser/ElectronBrowserHost"; -import { AppAtomRegistryProvider } from "./rpc/atomRegistry"; -import type { AppRouter } from "./router"; -import { AppRoot } from "./AppRoot"; - -describe("AppRoot", () => { - it("shares the application atom registry with routed UI and the Electron browser host", () => { - const root = AppRoot({ router: {} as AppRouter }); - - expect(root.type).toBe(AppAtomRegistryProvider); - const children = Children.toArray( - (root as ReactElement<{ readonly children: ReactNode }>).props.children, - ); - expect(children).toHaveLength(2); - expect(isValidElement(children[0]) && children[0].type).toBe(RouterProvider); - expect(isValidElement(children[1]) && children[1].type).toBe(ElectronBrowserHost); - }); -}); diff --git a/apps/web/src/AppRoot.tsx b/apps/web/src/AppRoot.tsx deleted file mode 100644 index 1ecb9f6b7b6..00000000000 --- a/apps/web/src/AppRoot.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { RouterProvider } from "@tanstack/react-router"; - -import { ElectronBrowserHost } from "./browser/ElectronBrowserHost"; -import { AppAtomRegistryProvider } from "./rpc/atomRegistry"; -import type { AppRouter } from "./router"; - -/** - * Owns renderer-wide providers. The Electron browser host intentionally sits - * outside the router so its webviews survive route transitions, but it must - * share the same atom registry as routed UI. - */ -export function AppRoot({ router }: { readonly router: AppRouter }) { - return ( - - - - - ); -} diff --git a/apps/web/src/components/clerk/T3ConnectSidebarSignIn.tsx b/apps/web/src/components/clerk/T3ConnectSidebarSignIn.tsx deleted file mode 100644 index 45477ee1b7e..00000000000 --- a/apps/web/src/components/clerk/T3ConnectSidebarSignIn.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { UserButton, useAuth } from "@clerk/react"; -import { LogInIcon, SmartphoneIcon } from "lucide-react"; - -import { hasCloudPublicConfig } from "../../cloud/publicConfig"; -import { SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "../ui/sidebar"; -import { MobileClientsUserProfilePage } from "./MobileClientsUserProfilePage"; -import { useT3ConnectAuthPrompt } from "./useT3ConnectAuthPrompt"; - -export function T3ConnectSidebarSignIn() { - if (!hasCloudPublicConfig()) return null; - - return ; -} - -export function T3ConnectSidebarAvatar() { - if (!hasCloudPublicConfig()) return null; - - return ; -} - -function ConfiguredT3ConnectSidebarAvatar() { - const { isLoaded, isSignedIn } = useAuth(); - - if (!isLoaded || !isSignedIn) return null; - - return ( - - } - url="mobile-clients" - > - - - - ); -} - -function ConfiguredT3ConnectSidebarSignIn() { - const { isLoaded, isSignedIn } = useAuth(); - const { authPrompt, openAuthPrompt } = useT3ConnectAuthPrompt(); - - if (!isLoaded || isSignedIn) return null; - - return ( - <> - - - - - Sign in to T3 Connect - - - - {authPrompt} - - ); -} diff --git a/apps/web/src/components/clerk/useT3ConnectAuthPrompt.tsx b/apps/web/src/components/clerk/useT3ConnectAuthPrompt.tsx deleted file mode 100644 index 05fa8250b30..00000000000 --- a/apps/web/src/components/clerk/useT3ConnectAuthPrompt.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { useClerk } from "@clerk/react"; - -export function useT3ConnectAuthPrompt() { - const clerk = useClerk(); - const openAuthPrompt = () => { - clerk.openWaitlist(); - }; - return { authPrompt: null, openAuthPrompt }; -} diff --git a/apps/web/src/components/clerk/MobileClientsUserProfilePage.logic.test.ts b/apps/web/src/components/connect/MobileClientsUserProfilePage.logic.test.ts similarity index 100% rename from apps/web/src/components/clerk/MobileClientsUserProfilePage.logic.test.ts rename to apps/web/src/components/connect/MobileClientsUserProfilePage.logic.test.ts diff --git a/apps/web/src/components/clerk/MobileClientsUserProfilePage.logic.ts b/apps/web/src/components/connect/MobileClientsUserProfilePage.logic.ts similarity index 100% rename from apps/web/src/components/clerk/MobileClientsUserProfilePage.logic.ts rename to apps/web/src/components/connect/MobileClientsUserProfilePage.logic.ts diff --git a/apps/web/src/components/clerk/MobileClientsUserProfilePage.tsx b/apps/web/src/components/connect/MobileClientsUserProfilePage.tsx similarity index 100% rename from apps/web/src/components/clerk/MobileClientsUserProfilePage.tsx rename to apps/web/src/components/connect/MobileClientsUserProfilePage.tsx diff --git a/apps/web/src/components/cloud/RelayClientInstallDialog.tsx b/apps/web/src/components/connect/RelayClientInstallDialog.tsx similarity index 99% rename from apps/web/src/components/cloud/RelayClientInstallDialog.tsx rename to apps/web/src/components/connect/RelayClientInstallDialog.tsx index 78282f65915..5782978bec0 100644 --- a/apps/web/src/components/cloud/RelayClientInstallDialog.tsx +++ b/apps/web/src/components/connect/RelayClientInstallDialog.tsx @@ -18,6 +18,7 @@ import { DialogPopup, DialogTitle, } from "../ui/dialog"; + const installSteps: ReadonlyArray<{ readonly stage: RelayClientInstallProgressStage; readonly label: string; diff --git a/apps/web/src/components/connect/T3ConnectClerkProvider.electron.tsx b/apps/web/src/components/connect/T3ConnectClerkProvider.electron.tsx new file mode 100644 index 00000000000..0034efc9125 --- /dev/null +++ b/apps/web/src/components/connect/T3ConnectClerkProvider.electron.tsx @@ -0,0 +1,18 @@ +import { passkeys } from "@clerk/electron/passkeys"; +import { ClerkProvider as ClerkElectronProvider } from "@clerk/electron/react"; + +import { ManagedRelayAuthProvider } from "../../cloud/managedAuth"; + +export default function ClerkProvider({ + children, + publishableKey, +}: { + readonly children: React.ReactNode; + readonly publishableKey: string; +}) { + return ( + + {children} + + ); +} diff --git a/apps/web/src/components/connect/T3ConnectClerkProvider.web.tsx b/apps/web/src/components/connect/T3ConnectClerkProvider.web.tsx new file mode 100644 index 00000000000..fc9ff3e2ba1 --- /dev/null +++ b/apps/web/src/components/connect/T3ConnectClerkProvider.web.tsx @@ -0,0 +1,17 @@ +import { ClerkProvider as ClerkReactProvider } from "@clerk/react"; + +import { ManagedRelayAuthProvider } from "../../cloud/managedAuth"; + +export default function ClerkProvider({ + children, + publishableKey, +}: { + readonly children: React.ReactNode; + readonly publishableKey: string; +}) { + return ( + + {children} + + ); +} diff --git a/apps/web/src/components/connect/T3ConnectSidebarSignIn.configured.tsx b/apps/web/src/components/connect/T3ConnectSidebarSignIn.configured.tsx new file mode 100644 index 00000000000..331cd51ec45 --- /dev/null +++ b/apps/web/src/components/connect/T3ConnectSidebarSignIn.configured.tsx @@ -0,0 +1,52 @@ +import { UserButton, useAuth, useClerk } from "@clerk/react"; +import { LogInIcon, SmartphoneIcon } from "lucide-react"; + +import { SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "../ui/sidebar"; +import { MobileClientsUserProfilePage } from "./MobileClientsUserProfilePage"; + +export function ConfiguredT3ConnectSidebarAvatar() { + const { isLoaded, isSignedIn } = useAuth(); + + if (!isLoaded || !isSignedIn) return null; + + return ( + + } + url="mobile-clients" + > + + + + ); +} + +export function ConfiguredT3ConnectSidebarSignIn() { + const { isLoaded, isSignedIn } = useAuth(); + const clerk = useClerk(); + + if (!isLoaded || isSignedIn) return null; + + return ( + + + clerk.openWaitlist()} + > + + Sign in to T3 Connect + + + + ); +} diff --git a/apps/web/src/components/connect/T3ConnectSidebarSignIn.tsx b/apps/web/src/components/connect/T3ConnectSidebarSignIn.tsx new file mode 100644 index 00000000000..4aaae8ba8d1 --- /dev/null +++ b/apps/web/src/components/connect/T3ConnectSidebarSignIn.tsx @@ -0,0 +1,17 @@ +import { hasCloudPublicConfig } from "../../cloud/publicConfig"; +import { + ConfiguredT3ConnectSidebarAvatar, + ConfiguredT3ConnectSidebarSignIn, +} from "./T3ConnectSidebarSignIn.configured"; + +export function T3ConnectSidebarSignIn() { + if (!hasCloudPublicConfig()) return null; + + return ; +} + +export function T3ConnectSidebarAvatar() { + if (!hasCloudPublicConfig()) return null; + + return ; +} diff --git a/apps/web/src/components/settings/SettingsSidebarNav.tsx b/apps/web/src/components/settings/SettingsSidebarNav.tsx index 6774b6f333f..3759a6feb91 100644 --- a/apps/web/src/components/settings/SettingsSidebarNav.tsx +++ b/apps/web/src/components/settings/SettingsSidebarNav.tsx @@ -20,7 +20,7 @@ import { SidebarSeparator, useSidebar, } from "../ui/sidebar"; -import { T3ConnectSidebarAvatar, T3ConnectSidebarSignIn } from "../clerk/T3ConnectSidebarSignIn"; +import { T3ConnectSidebarAvatar, T3ConnectSidebarSignIn } from "../connect/T3ConnectSidebarSignIn"; export type SettingsSectionPath = | "/settings/general" diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index 453649bfdc5..749a00941b6 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -1,9 +1,9 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; +import * as React from "react"; +import * as ReactDOM from "react-dom/client"; +import { createHashHistory, createBrowserHistory, RouterProvider } from "@tanstack/react-router"; import { ClerkProvider } from "@clerk/react"; import { passkeys } from "@clerk/electron/passkeys"; import { ClerkProvider as ElectronClerkProvider } from "@clerk/electron/react"; -import { createHashHistory, createBrowserHistory } from "@tanstack/react-router"; import "@fontsource-variable/dm-sans/index.css"; import "@fontsource/jetbrains-mono/400.css"; @@ -16,7 +16,8 @@ import { ManagedRelayAuthProvider } from "./cloud/managedAuth"; import { hasCloudPublicConfig } from "./cloud/publicConfig"; import { getRouter } from "./router"; import { syncDocumentWindowControlsOverlayClass } from "./lib/windowControlsOverlay"; -import { AppRoot } from "./AppRoot"; +import { AppAtomRegistryProvider } from "./rpc/atomRegistry"; +import { ElectronBrowserHost } from "./browser/ElectronBrowserHost"; // Electron loads the app from a file-backed shell, so hash history avoids path resolution issues. const history = isElectron ? createHashHistory() : createBrowserHistory(); @@ -29,22 +30,30 @@ if (isElectron) { const clerkPublishableKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY as string | undefined; -const app = ; +const app = ( + + + + +); + +const AuthWrapper = (props: { children: React.ReactNode }) => + clerkPublishableKey && hasCloudPublicConfig() ? ( + isElectron ? ( + + {props.children} + + ) : ( + + {props.children} + + ) + ) : ( + props.children + ); ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - {clerkPublishableKey && hasCloudPublicConfig() ? ( - isElectron ? ( - - {app} - - ) : ( - - {app} - - ) - ) : ( - app - )} + {app} , ); diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx index 36de3b95706..4c1f9200051 100644 --- a/apps/web/src/routes/__root.tsx +++ b/apps/web/src/routes/__root.tsx @@ -14,7 +14,7 @@ import { APP_BASE_NAME, APP_DISPLAY_NAME, APP_STAGE_LABEL } from "../branding"; import { resolveServerBackedAppDisplayName } from "../branding.logic"; import { AppSidebarLayout } from "../components/AppSidebarLayout"; import { CommandPalette } from "../components/CommandPalette"; -import { RelayClientInstallDialog } from "../components/cloud/RelayClientInstallDialog"; +import { RelayClientInstallDialog } from "../components/connect/RelayClientInstallDialog"; import { SshPasswordPromptDialog } from "../components/desktop/SshPasswordPromptDialog"; import { ProviderUpdateLaunchNotification } from "../components/ProviderUpdateLaunchNotification"; import { SlowRpcRequestToastCoordinator } from "../components/SlowRpcRequestToastCoordinator";