From 18f4a329905376f5efd4aa918ab8aef323e998f9 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Thu, 25 Jun 2026 09:34:50 +0000 Subject: [PATCH] Harden HyperTwist route loading surfaces --- website/src/__tests__/app-route-tree.test.tsx | 4 ++ .../__tests__/public-route-registry.test.ts | 8 +++ website/src/__tests__/route-shells.test.tsx | 5 +- .../src/components/routes/ProtectedRoute.tsx | 3 + website/src/components/ui/Skeletons.tsx | 23 ++++++- website/src/public-route-registry.json | 30 +++++++++ website/src/public-route-registry.ts | 2 + website/src/router/AppRouteTree.tsx | 66 +++++++++++++++++-- website/src/router/PublicRoutes.tsx | 24 +++++-- website/src/styles/global.css | 57 +++++++++++++++- 10 files changed, 208 insertions(+), 14 deletions(-) diff --git a/website/src/__tests__/app-route-tree.test.tsx b/website/src/__tests__/app-route-tree.test.tsx index 9e4540a..aa61299 100644 --- a/website/src/__tests__/app-route-tree.test.tsx +++ b/website/src/__tests__/app-route-tree.test.tsx @@ -156,6 +156,8 @@ describe('AppRouteTree', () => { renderRoute('/') expect(screen.getByText('Loading HyperTwist...')).toBeTruthy() + expect(screen.getByText('Public release shell')).toBeTruthy() + expect(screen.getByText(/public product story, current launch posture, and browser-versus-desktop authority map/i)).toBeTruthy() expect(await screen.findByText('HyperTwist turns cube practice into a real operator-grade training stack.')).toBeTruthy() }) @@ -180,6 +182,8 @@ describe('AppRouteTree', () => { it('renders the login page through the real public route tree', async () => { renderRoute('/login?next=%2Fapp%2Fdownloads') + expect(screen.getByText('Browser account access')).toBeTruthy() + expect(screen.getByText(/safe next-step routing, and protected release continuity/i)).toBeTruthy() expect(await screen.findByText('Log in to HyperTwist')).toBeTruthy() expect(screen.getByRole('link', { name: /create an account/i }).getAttribute('href')).toBe('/register?next=%2Fapp%2Fdownloads') }) diff --git a/website/src/__tests__/public-route-registry.test.ts b/website/src/__tests__/public-route-registry.test.ts index 22cd67c..12b915e 100644 --- a/website/src/__tests__/public-route-registry.test.ts +++ b/website/src/__tests__/public-route-registry.test.ts @@ -35,6 +35,14 @@ describe('public route registry', () => { expect(crawlablePublicRoutes.map((route) => route.path)).not.toContain('/register') }) + it('carries non-empty loader metadata for every shared public-shell route', () => { + for (const route of publicRouteRegistry) { + expect(route.loaderTitle.trim().length).toBeGreaterThan(0) + expect((route.loaderDescription || '').trim().length).toBeGreaterThan(0) + expect((route.loaderEyebrow || '').trim().length).toBeGreaterThan(0) + } + }) + it('renders sitemap XML from the shared crawlable route registry', () => { const xml = buildPublicSitemapXml() diff --git a/website/src/__tests__/route-shells.test.tsx b/website/src/__tests__/route-shells.test.tsx index 6b4bd60..b7effd8 100644 --- a/website/src/__tests__/route-shells.test.tsx +++ b/website/src/__tests__/route-shells.test.tsx @@ -156,10 +156,13 @@ describe('website route and shell behavior', () => { isLoading: true, }) - renderProtectedRoute('/app/account') + const { container } = renderProtectedRoute('/app/account') expect(screen.getByText('Loading operator access...')).toBeTruthy() + expect(screen.getByText('Browser account access')).toBeTruthy() expect(screen.getByText(/Checking your saved browser session, protected release posture, and browser-to-desktop continuity/i)).toBeTruthy() + expect(container.querySelector('.general-page-loader-shell--fullscreen')).not.toBeNull() + expect(screen.getByAltText('HyperTwist')).toBeTruthy() expect(screen.getByRole('link', { name: 'Open sign-in' }).getAttribute('href')).toBe('/login?next=%2Fapp%2Faccount') expect(screen.getByRole('link', { name: 'Open support' }).getAttribute('href')).toBe('/support?topic=operator-access') }) diff --git a/website/src/components/routes/ProtectedRoute.tsx b/website/src/components/routes/ProtectedRoute.tsx index f987b15..b040f6f 100644 --- a/website/src/components/routes/ProtectedRoute.tsx +++ b/website/src/components/routes/ProtectedRoute.tsx @@ -13,7 +13,10 @@ export function ProtectedRoute() { return (
diff --git a/website/src/components/ui/Skeletons.tsx b/website/src/components/ui/Skeletons.tsx index 8255024..9c28a28 100644 --- a/website/src/components/ui/Skeletons.tsx +++ b/website/src/components/ui/Skeletons.tsx @@ -5,10 +5,16 @@ export function GeneralPageLoader({ title = 'Loading', description, children, + eyebrow, + fullscreen = false, + showBrand = false, }: { title?: string description?: string children?: ReactNode + eyebrow?: string + fullscreen?: boolean + showBrand?: boolean }) { const [isCompleting, setIsCompleting] = useState(false) @@ -29,8 +35,23 @@ export function GeneralPageLoader({ const spinnerColor = isCompleting ? 'var(--ht-amber)' : 'var(--ht-cyan)' return ( -
+
+ {showBrand ? ( +
+ HyperTwist +
+ ) : null} + {eyebrow ?

{eyebrow}

: null} import('../pages/app-pages').then((m) => ({ const AccountPage = lazy(() => import('../pages/app-pages').then((m) => ({ default: m.AccountPage }))) const NoticesPage = lazy(() => import('../pages/app-pages').then((m) => ({ default: m.NoticesPage }))) -function withAppLoader(title: string, element: React.ReactNode) { - return }>{element} +const protectedAppLoaders = { + dashboard: { + title: 'Loading dashboard...', + eyebrow: 'Protected operator shell', + description: + 'Restoring account posture, release authority, package proof, and browser-to-desktop pairing surfaces inside the signed-in operator lane.', + }, + downloads: { + title: 'Loading downloads...', + eyebrow: 'Protected release lane', + description: + 'Resolving entitled desktop targets, current package proof, and rollout references before the signed-in download surface opens.', + }, + browserAccess: { + title: 'Loading browser access...', + eyebrow: 'Protected browser shell', + description: + 'Restoring the signed-in browser posture, release guidance, and explicit browser-versus-desktop authority boundaries.', + }, + account: { + title: 'Loading account...', + eyebrow: 'Protected account lane', + description: + 'Refreshing the signed-in account view, current plan posture, and release-access continuity before desktop follow-through resumes.', + }, + notices: { + title: 'Loading notices...', + eyebrow: 'Protected distribution references', + description: + 'Restoring signed-in notices, corresponding-source posture, and download/distribution references inside the protected release shell.', + }, +} as const + +function withAppLoader( + loader: { + title: string + eyebrow: string + description: string + }, + element: React.ReactNode, +) { + return ( + + } + > + {element} + + ) } export function AppRouteTree() { @@ -22,11 +74,11 @@ export function AppRouteTree() { }> }> - )} /> - )} /> - )} /> - )} /> - )} /> + )} /> + )} /> + )} /> + )} /> + )} /> diff --git a/website/src/router/PublicRoutes.tsx b/website/src/router/PublicRoutes.tsx index a2e25a6..683eba4 100644 --- a/website/src/router/PublicRoutes.tsx +++ b/website/src/router/PublicRoutes.tsx @@ -1,7 +1,7 @@ import { Suspense, lazy, type ReactNode } from 'react' import { Route } from 'react-router-dom' import { GeneralPageLoader } from '../components/ui/Skeletons' -import { publicRouteRegistry } from '../public-route-registry' +import { publicRouteRegistry, type PublicRouteDefinition } from '../public-route-registry' const HomeLanding = lazy(() => import('../pages/public-pages-marketing').then((m) => ({ default: m.HomeLanding }))) const FeaturesPage = lazy(() => import('../pages/public-pages-features').then((m) => ({ default: m.FeaturesPage }))) @@ -37,8 +37,24 @@ const publicRouteElements: Record = { '/register': , } -function withGeneralLoader(title: string, element: ReactNode) { - return }>{element} +function withGeneralLoader(route: PublicRouteDefinition, element: ReactNode) { + const isAuthRoute = route.path === '/login' || route.path === '/register' + + return ( + + } + > + {element} + + ) } export function renderPublicRoutes() { @@ -48,7 +64,7 @@ export function renderPublicRoutes() { ))} diff --git a/website/src/styles/global.css b/website/src/styles/global.css index 7afe3e5..af803a4 100644 --- a/website/src/styles/global.css +++ b/website/src/styles/global.css @@ -664,7 +664,14 @@ code { place-items: center; overflow: hidden; padding: 1.5rem; - background: transparent; + background: + radial-gradient(circle at top, rgba(84, 203, 255, 0.12), transparent 40%), + radial-gradient(circle at bottom, rgba(247, 178, 103, 0.1), transparent 48%), + linear-gradient(180deg, rgba(6, 10, 20, 0.9), rgba(7, 11, 19, 0.96)); +} + +.general-page-loader-shell--fullscreen { + min-height: 100vh; } .general-page-loader-frame { @@ -684,7 +691,51 @@ code { backdrop-filter: blur(14px); } +.general-page-loader-frame::before { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + pointer-events: none; + background: radial-gradient(circle at top, rgba(255, 255, 255, 0.08), transparent 58%); +} + +.general-page-loader-brand { + position: relative; + z-index: 1; + display: grid; + place-items: center; + width: 4.5rem; + height: 4.5rem; + border-radius: 1.4rem; + border: 1px solid rgba(84, 203, 255, 0.24); + background: + radial-gradient(circle at top, rgba(84, 203, 255, 0.12), transparent 58%), + rgba(8, 13, 26, 0.9); + box-shadow: + inset 0 0 0 1px rgba(255, 255, 255, 0.04), + 0 0 22px rgba(84, 203, 255, 0.16); +} + +.general-page-loader-brand__image { + width: 2.4rem; + height: 2.4rem; + object-fit: contain; +} + +.general-page-loader-eyebrow { + position: relative; + z-index: 1; + margin: 0; + font-size: 0.73rem; + letter-spacing: 0.14em; + text-transform: uppercase; + color: rgba(255, 255, 255, 0.62); +} + .general-page-loader-text { + position: relative; + z-index: 1; display: inline-flex; font-size: 0.81rem; font-weight: 600; @@ -694,6 +745,8 @@ code { } .general-page-loader-body { + position: relative; + z-index: 1; margin: 0; max-width: 32rem; text-align: center; @@ -702,6 +755,8 @@ code { } .general-page-loader-extra { + position: relative; + z-index: 1; width: 100%; }