Wholesale move of every remaining kind into its own subdirectory under
`packages/nodes/src/`, finishing the registry-driven migration. Each
kind now ships its definition, schema (re-exported from core), and any
of `geometry` / `renderer` / `system` / `floorplan` / `tool` /
`move-tool` / `panel` / `floorplan-move` / `floorplan-affordances` /
`parametrics` / `preview` it needs — no per-kind code remains under
`packages/editor/src/components/tools/` or
`packages/viewer/src/components/renderers/`.
Deleted (replaced by registry-driven equivalents):
- `tools/{ceiling,column,door,fence,item,slab,spawn,wall,window}/...`
(boundary editors, hole editors, placement tools, move tools,
endpoint movers, curve tools, helpers, math libs)
- `ui/helpers/{ceiling,slab,wall}-helper.tsx`
- `ui/panels/{column,door,elevator,item,roof,roof-segment,spawn,
stair,stair-segment,wall,window}-panel.tsx`
- `viewer/src/components/renderers/{building,ceiling,column,door,
elevator,fence,guide,item,level,roof,roof-segment,scan,site,slab,
spawn,stair,stair-segment,wall,window,zone}-renderer.tsx`
- `viewer/src/components/viewer/legacy-system.tsx`
Added under `packages/nodes/src/`:
- `building/`, `column/`, `elevator/`, `guide/`, `level/`, `roof/`,
`roof-segment/`, `scan/`, `shared/`, `site/`, `stair/`,
`stair-segment/` packages with definition + schema + renderer / system
/ floorplan / panel as appropriate.
- New `floorplan-move.ts` for every kind that supports 2D moves
(ceiling, door, item, shelf, slab, window) — single registry-driven
dispatch path via `def.floorplanMoveTarget`.
- New `floorplan-affordances.ts` for kinds with polygon / endpoint
drags (ceiling, fence, slab, wall) — using the shared
`polygon-vertex-affordance` factories.
- New per-kind `panel.tsx` for kinds with custom inspector content
(door, item, shelf, spawn, wall, window).
- New per-kind `tool.tsx` for placement (door, item, shelf, window).
- New per-kind `move-tool.tsx` for kinds with custom 3D move flows
(door, item, slab, window).
Coordinator + manager updates in `packages/editor/`:
- `tool-manager.tsx` resolves tools from the registry only — no
hardcoded type→component map.
- `panel-manager.tsx` resolves inspector panels the same way.
- `placement-{coordinator,strategies,types}.ts` extended with
shelf-surface placement.
- `selection-manager.tsx` adds the registry-selectable fallback.
- `floorplan-panel.tsx`, `floorplan-background-placement.ts`,
`floorplan-render-context.tsx` updated for the registry layer's new
contract (props, affordance dispatch, render context).
Viewer updates:
- `viewer/index.tsx` drops legacy renderer mounts.
- `node-renderer.tsx` resolves by registry only.
- `scene-bvh.tsx`, `use-node-events.ts`, `level-system.tsx`,
`wall-cutout.tsx`, `zone-system.tsx`, `materials.ts` adjusted for
the registry-only world.
Sidebar tree nodes for ceiling / fence / slab / shelf / tree-node
updated to read from the registered nodes instead of the deleted
legacy renderer trees.
Wiki: new `plugin-authoring.md` page, README index updated.
Tests in `packages/nodes/src/index.test.ts` validate every registered
kind has the required shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verification anchor for "which path is running this kind?":
[pascal:registry] loaded pascal:core v1 (1 kinds: shelf)
prints in the browser dev console on app boot. Empty array means every
kind is on the legacy dispatch path. A kind in the array means the
registry-first NodeRenderer / ToolManager shims own it (legacy path is
short-circuited).
Also exposes `globalThis.__pascalNodeRegistry` in dev so you can run
ad-hoc inspections like `__pascalNodeRegistry.has('spawn')` from the
console. Production builds skip both.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds apps/editor/lib/bootstrap.ts that calls loadPlugin(builtinPlugin)
as a module-side-effect on first import. Imported from scene-loader.tsx
so it runs on the client side where the editor mounts.
Idempotency guard handles HMR re-execution (would otherwise throw on
duplicate registerNode). For the empty plugin this commit, the entire
call is a no-op — included now so future commits that add real node
kinds only need to push them onto builtinPlugin.nodes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-push audit agent A2 flagged two HIGH-severity security issues that
would have shipped in the PR had we not checked:
1. PUT /api/scenes/[id] URL-validation bypass. Phase 8 P4 fixed
the POST /api/scenes route by replacing a loose z.unknown() graph
schema with AnyNode superRefine. The fix never made it to the PUT
handler - an attacker could resubmit the same javascript:/file:///
payloads via PUT. Fixed by extracting the tight validator into
apps/editor/lib/graph-schema.ts and sharing it across both routes.
2. SSRF in photo_to_scene / analyze_floorplan_image /
analyze_room_photo. All three tools called raw fetch(image) on
user-supplied URLs with no validation - a direct
http://169.254.169.254/latest/meta-data/ exfil primitive on any
cloud host. Added packages/mcp/src/lib/safe-fetch.ts that:
- Blocks loopback (127.0.0.0/8, ::1)
- Blocks link-local incl. cloud metadata (169.254.0.0/16)
- Blocks private ranges (10/8, 172.16/12, 192.168/16, fc00::/7)
- Blocks .local/.internal/.corp hostnames + localhost variants
- Blocks v4-mapped IPv6 loopback (::ffff:127.0.0.1)
- Manual redirects (max 3), revalidating the allowlist per hop
- 20 MB response-size cap (streamed, enforced per-chunk)
- 10s timeout
- Optional PASCAL_ALLOWED_ASSET_ORIGINS env allowlist
Tests: 8 new SSRF guard tests, all vision tests still pass, full
suite 302/302.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Upload files directly to Supabase Storage via signed URLs, bypassing
the Next.js 100MB body size limit. Supports scans up to 200MB with
real-time progress tracking that persists across sidebar panel switches.
- Add Zustand upload store for persistent upload state
- Add createAssetUploadUrl/confirmAssetUpload server actions
- Add XHR-based upload orchestrator with progress events
- Scene node creation works even if sidebar panel unmounts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Removed 9 stale branch-specific env vars on Vercel that were locked
to the deleted feat/refactor-supabase-setup branch
- Added runtime warning when a preview deployment detects it's using
the production Supabase instance (branching skipped/misconfigured)
The root issue: Supabase branching only creates preview branches for
PRs that include migration changes. PRs without migrations get SKIPPED
and fall through to the generic env vars, which include 'preview' in
their targets and point to production Supabase.
To fully fix: configure Supabase branching to always create preview
branches, or remove 'preview' from generic Supabase env var targets
on Vercel (requires branching to be working first).
Co-authored-by: Anton Pascal <anton-pascal@users.noreply.github.com>
Validates all required env vars at runtime with zod schemas.
Skips validation during build via SKIP_ENV_VALIDATION flag.
Replaces scattered process.env access with typed env imports.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Turbo was stripping env vars during build on Vercel. Added all required
vars to turbo.json build task. Also hardened Supabase and BetterAuth
init with fallback placeholders for build-time safety.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Defer database client creation until first use via Proxy so Next.js
builds succeed without POSTGRES_URL and SUPABASE_URL env vars present.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add Google OAuth sign-in alongside magic link authentication
- Username onboarding: require username selection after first sign-in
- Public profile pages at /u/[username] with social links (GitHub, X)
- Settings page for managing username and social links
- Viewer header: unified floating card with project name, @username, breadcrumb
- Guest CTA on viewer page prompting sign-in
- Show owner avatar + username on community project cards
- Redesigned project cards (larger preview, avatar + stats row below)
- Consistent navbar across hub and profile pages with GitHub link
- Footer with links to GitHub repo and npm packages
- Borderless design with natural shadows throughout
- Apple corner smoothing (squircle) progressive enhancement
- cursor:pointer globally on buttons and links
- DB migrations: username, github_url, x_url columns on auth_users
- Root-level db:generate/migrate/push/studio scripts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>