From fa2decfce7fa359551634b4d24a2aecd9cef88c9 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 17 Sep 2026 20:26:01 -0700 Subject: [PATCH] refactor(web): share standalone page layout (#12354) --- .../src/components/auth/AuthSurfaceShell.tsx | 22 +- .../components/auth/PairingRouteSurface.tsx | 221 ++++++++---------- .../web/src/components/ui/standalone-page.tsx | 67 ++++++ apps/web/src/routes/__root.tsx | 54 ++--- 4 files changed, 194 insertions(+), 170 deletions(-) create mode 100644 apps/web/src/components/ui/standalone-page.tsx diff --git a/apps/web/src/components/auth/AuthSurfaceShell.tsx b/apps/web/src/components/auth/AuthSurfaceShell.tsx index 1c45c98a9e49120ea47a14cb353594d2330d68d2..ed27bd482a5d9fbbc1293c53fc2717cce945f854 100644 --- a/apps/web/src/components/auth/AuthSurfaceShell.tsx +++ b/apps/web/src/components/auth/AuthSurfaceShell.tsx @@ -2,22 +2,18 @@ import type { ReactNode } from "react"; import { APP_DISPLAY_NAME, APP_STAGE_LABEL } from "../../branding"; import { resolveSidebarStageBackdropVariant, StageBackdropArt } from "../SidebarStageBackdrop"; +import { StandalonePage } from "../ui/standalone-page"; /** - * Full-screen card for standalone auth pages, mirroring the pairing surface's - * treatment. Used by the CLI-connect authorize and callback surfaces. + * Branded masthead for the CLI-connect authorize and callback pages. */ export function AuthSurfaceShell({ children }: { readonly children: ReactNode }) { const stageVariant = resolveSidebarStageBackdropVariant(APP_STAGE_LABEL); return ( -
-
-
-
-
- -
+ {stageVariant ? (
@@ -36,9 +32,9 @@ export function AuthSurfaceShell({ children }: { readonly children: ReactNode })

- -
{children}
-
-
+ } + > + {children} + ); } diff --git a/apps/web/src/components/auth/PairingRouteSurface.tsx b/apps/web/src/components/auth/PairingRouteSurface.tsx index 59288506569c412db5e9a0a960fe43010c732c2c..cad11af57fc6418c29d18484cf472251d7cfa0c3 100644 --- a/apps/web/src/components/auth/PairingRouteSurface.tsx +++ b/apps/web/src/components/auth/PairingRouteSurface.tsx @@ -12,29 +12,18 @@ import { import { readHostedPairingRequest } from "../../hostedPairing"; import { Button } from "../ui/button"; import { Input } from "../ui/input"; +import { StandalonePage, StandalonePageHeader } from "../ui/standalone-page"; import { useAtomCommand } from "../../state/use-atom-command"; export function PairingPendingSurface() { return ( -
-
-
-
-
-
- -
-

- {APP_DISPLAY_NAME} -

-

- Pairing with this environment -

-

- Validating the pairing link and preparing your session. -

-
-
+ + + ); } @@ -97,69 +86,57 @@ export function PairingRouteSurface({ }, [submitCredential]); return ( -
-
-
-
-
+ + + +
void handleSubmit(event)}> +
+ + setCredential(event.currentTarget.value)} + placeholder="Paste a one-time token or pairing secret" + spellCheck={false} + value={credential} + /> +
+ + {errorMessage ? ( +
+ {errorMessage} +
+ ) : null} + +
+ + +
+
+ +
+ {describeSupportedMethods(auth.bootstrapMethods)}
- -
-

- {APP_DISPLAY_NAME} -

-

- Pair with this environment -

-

- {describeAuthGate(auth.bootstrapMethods)} -

- -
void handleSubmit(event)}> -
- - setCredential(event.currentTarget.value)} - placeholder="Paste a one-time token or pairing secret" - spellCheck={false} - value={credential} - /> -
- - {errorMessage ? ( -
- {errorMessage} -
- ) : null} - -
- - -
-
- -
- {describeSupportedMethods(auth.bootstrapMethods)} -
-
-
+ ); } @@ -233,57 +210,49 @@ export function HostedPairingRouteSurface() { const request = hostedPairingRequestRef.current; return ( -
-
-
-
-
-
- -
-

- {APP_DISPLAY_NAME} -

-

- {status === "paired" + + -

{message}

+ : "Pairing backend" + } + description={message} + /> - {request ? ( -
- Host: {request.host} -
- ) : null} - - {status === "error" ? ( -
- Verify the backend is reachable from this browser, supports CORS for hosted clients, and - is served over HTTPS when opening this page from HTTPS. -
- ) : null} + {request ? ( +
+ Host: {request.host} +
+ ) : null} -
- {status === "pairing" ? ( - - ) : canRetry ? ( - - ) : null} - {status === "paired" ? ( - - ) : null} + {status === "error" ? ( +
+ Verify the backend is reachable from this browser, supports CORS for hosted clients, and + is served over HTTPS when opening this page from HTTPS.
-

-
+ ) : null} + +
+ {status === "pairing" ? ( + + ) : canRetry ? ( + + ) : null} + {status === "paired" ? ( + + ) : null} +
+ ); } diff --git a/apps/web/src/components/ui/standalone-page.tsx b/apps/web/src/components/ui/standalone-page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f44ec18f6825a3bc893f1fe0dfdff17418e42aa3 --- /dev/null +++ b/apps/web/src/components/ui/standalone-page.tsx @@ -0,0 +1,67 @@ +import type { ReactNode } from "react"; + +const backdropClassNames = { + pairing: + "absolute inset-x-0 top-0 h-44 bg-[radial-gradient(44rem_16rem_at_top,color-mix(in_srgb,var(--color-emerald-500)_14%,transparent),transparent)]", + error: + "absolute inset-x-0 top-0 h-44 bg-[radial-gradient(44rem_16rem_at_top,color-mix(in_srgb,var(--color-red-500)_16%,transparent),transparent)]", + brand: + "absolute inset-x-0 top-0 h-72 bg-[radial-gradient(48rem_20rem_at_top,color-mix(in_srgb,var(--color-blue-500)_12%,transparent),transparent)]", +}; + +/** Shared page and card geometry for entry points outside the app shell. */ +export function StandalonePage({ + tone, + masthead, + children, +}: { + readonly tone: keyof typeof backdropClassNames; + readonly masthead?: ReactNode; + readonly children: ReactNode; +}) { + return ( +
+
+
+ {tone === "pairing" ? ( +
+ ) : null} +
+
+ +
+ {masthead} +
{children}
+
+
+ ); +} + +/** Entry-page headings keep their typography and spacing together. */ +export function StandalonePageHeader({ + eyebrow, + title, + description, +}: { + readonly eyebrow: ReactNode; + readonly title: ReactNode; + readonly description: ReactNode; +}) { + return ( + <> +

+ {eyebrow} +

+

{title}

+

{description}

+ + ); +} diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx index 42e40842a721cdd1074d0bb2861f19ea737f01ca..eee703ac3f4718e7e37f86c6c530b4c0ca536a01 100644 --- a/apps/web/src/routes/__root.tsx +++ b/apps/web/src/routes/__root.tsx @@ -35,6 +35,7 @@ import { useCopyToClipboard } from "../hooks/useCopyToClipboard"; import { useDefaultThemeAdoption } from "../hooks/useDefaultTheme"; import { useEnvironmentThemeSync } from "../hooks/useEnvironmentTheme"; import { Button } from "../components/ui/button"; +import { StandalonePage, StandalonePageHeader } from "../components/ui/standalone-page"; import { AnchoredToastProvider, stackedThreadToast, @@ -368,39 +369,30 @@ function RootRouteErrorView({ error }: ErrorComponentProps) { const report = useMemo(() => errorReport(error, pathname), [error, pathname]); return ( -
-
-
-
-
- -
-

- {APP_DISPLAY_NAME} -

-

- Something went wrong. -

-

{message}

+ + -
- - - -
+
+ + + +
-
-

Error report

-
-            {report}
-          
-
-
-
+
+

Error report

+
+          {report}
+        
+
+ ); } -- 2.54.0