The standalone editor app loaded `lib/bootstrap.ts` as a side-effect
import only from `components/scene-loader.tsx`. That worked for the
`/edit/[sceneId]` route but left every other page (homepage, settings,
viewer-only routes) hitting `<Viewer>` with an empty client-side
registry — node materials resolved to `null` and React surfaced a
`<html>`-level hydration mismatch on first paint.
Fix mirrors the community-app side that landed in pascalorg/private-
editor#27:
- New `app/client-bootstrap.tsx` — thin client wrapper that imports
`../lib/bootstrap` and renders children.
- `app/layout.tsx` mounts `<ClientBootstrap>` around `{children}` so
every page in the standalone editor gets the registry populated
before its first `<Viewer>` / `<Editor>` mounts.
- `lib/bootstrap.ts` switched to **synchronous** built-in registration
via `registerNode(def)` per kind instead of `await loadPlugin(...)`.
The previous async kick-off only resolved in a microtask, letting
the first SSR / hydration pass see an empty registry. External
plugin discovery (`discoverPlugins()`) stays async and runs via its
own `loadExternalPlugins()` path, gated by `externalsKickedOff` so
HMR doesn't re-fetch.
- `components/scene-loader.tsx` drops the per-page side-effect import
— the root provider handles it now.
`bun.lock` syncs `@pascal-app/editor` into `@pascal-app/nodes`'s
peerDependencies + devDependencies (already declared in
`packages/nodes/package.json`; only the lockfile lagged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
51 lines
1.3 KiB
TypeScript
51 lines
1.3 KiB
TypeScript
import { Agentation } from 'agentation'
|
|
import { GeistPixelSquare } from 'geist/font/pixel'
|
|
import { Barlow } from 'next/font/google'
|
|
import localFont from 'next/font/local'
|
|
import Script from 'next/script'
|
|
import { ClientBootstrap } from './client-bootstrap'
|
|
import './globals.css'
|
|
|
|
const geistSans = localFont({
|
|
src: './fonts/GeistVF.woff',
|
|
variable: '--font-geist-sans',
|
|
})
|
|
const geistMono = localFont({
|
|
src: './fonts/GeistMonoVF.woff',
|
|
variable: '--font-geist-mono',
|
|
})
|
|
|
|
const barlow = Barlow({
|
|
subsets: ['latin'],
|
|
weight: ['400', '500', '600', '700'],
|
|
variable: '--font-barlow',
|
|
display: 'swap',
|
|
})
|
|
|
|
export default function RootLayout({
|
|
children,
|
|
}: Readonly<{
|
|
children: React.ReactNode
|
|
}>) {
|
|
return (
|
|
<html
|
|
className={`${geistSans.variable} ${geistMono.variable} ${GeistPixelSquare.variable} ${barlow.variable}`}
|
|
lang="en"
|
|
>
|
|
<head>
|
|
{process.env.NODE_ENV === 'development' && (
|
|
<Script
|
|
crossOrigin="anonymous"
|
|
src="//unpkg.com/react-scan/dist/auto.global.js"
|
|
strategy="beforeInteractive"
|
|
/>
|
|
)}
|
|
</head>
|
|
<body className="font-sans">
|
|
<ClientBootstrap>{children}</ClientBootstrap>
|
|
{process.env.NODE_ENV === 'development' && <Agentation />}
|
|
</body>
|
|
</html>
|
|
)
|
|
}
|