Technical Briefing – editor (Pascal Editor) Clone: https://github.com/moses-y/editor
Overview
This Turborepo monorepo (664 files) provides a browser‑based 3D building editor. The core stack is React Three Fiber + Next.js (apps/editor) with three packages: @pascal-app/core (schemas, Zustand scene store, spatial queries), @pascal-app/viewer (3D rendering), and apps/editor (UI, tools, behaviours). State is managed via three Zustand stores (useScene, useViewer, useEditor). Nodes are flat dictionary entries with parentId links; a scene‑registry map (Map<id, Object3D>) lets renderers pull refs without graph traversal.
The Problem
The codebase suffers from high‑radius instability and circular import chains that make safe modifications risky. The hub module packages/editor/src/store/use-editor is imported by 44 other modules (instability 0), and 31 files participate in circular dependencies (e.g., tree-node.tsx, use-scene.ts, node-renderer.tsx). Adding or refactoring a node type can ripple through these edges without compile‑time protection, and there are no test files to verify behaviour after change.
What This Does
- Schema & state –
packages/core/src/schema/index.tsdefines node types (Wall, Item, Zone, etc.) that extendBaseNode;packages/core/src/store/use-scene.tsprovides CRUD middleware (persist, undo/redo). - Rendering –
packages/viewer/src/components/renderers/node-renderer.tsxregisters refs viauseRegistry; renderers for walls, slabs, roofs etc. live underpackages/viewer/src/components/renderers/. - Editor UI –
packages/editor/src/components/editor/index.tsxcomposes the main toolbar;packages/editor/src/components/ui/action-menu/index.tsxandcommand-palette/index.tsxprovide tool selection. The oversizedsite-panel/index.tsx(1413 loc) anditem-catalog/catalog-items.tsxhouse bulk of the UI logic. - Import graph – 237 internal modules, 596 edges; 31 modules in cycles; hub
use-editor(Ca 44, Ce 0) and utilitylib/utils.ts(Ca 43, Ce 0) are the most connected points.
How It Is Wired
- Entry point –
apps/editor/app/page.tsxrenders the<Editor />component. - State bootstrap –
useEditor(packages/editor/src/store/use-editor.tsx) is initialized; UI components readactiveTool, panel visibility, etc. - Node creation – Calling
useEditor.getState().createNode(...)flows intopackages/core/src/store/use-scene.tswhich updates the flat node dictionary and triggers the persist middleware. - Render sync – The scene registry (
packages/core/src/scene-registry.ts) maps node IDs to Three.jsMeshrefs; when a node is updated, the registry is refreshed and the corresponding renderer re‑renders. - Cycle cost – Any change that touches a circular‑import member can silently break another branch; the 31‑member cycle includes
tree-node.tsx,use-scene.ts, andnode-renderer.tsx.
How To Use It
| Step | Command / File |
|---|---|
| Clone | git clone https://github.com/moses-y/editor |
| Install | pnpm install (root package.json + per‑package package.jsons; no lockfile is present, so a fresh pnpm install will generate one) |
| Env | Copy env.example → .env; required vars are documented there (currently none beyond default Next.js vars). |
| Dev server | pnpm dev -w apps/editor (starts Next.js on http://localhost:3000) |
| Build | pnpm build -w apps/editor (produces static output) |
Real‑World Use
A developer wants to add a new “window” item type. They:
- Define a
Windownode type inpackages/core/src/schema/types.ts(extendsBaseNode). - Register a renderer in
packages/viewer/src/components/renderers/window-renderer.tsxusinguseRegistry(windowId, 'item', ref). - Export the type from
packages/core/src/schema/index.tsso the scene store recognises it. - Add a UI button in
packages/editor/src/components/ui/item-catalog/catalog-items.tsxthat callsuseEditor.getState().createNode({type: 'window', ...}, 'building_1'). - Because
use-editoris the hub (44 importers), the new node instantly appears in the scene, the registry updates, and the window renders—no compile‑time breakage as long as no circular import is introduced.
Code Health & Issues
Static analysis (67 findings, 37 high, 30 medium, 0 low)
- [HIGH/clarity] Hub module x8 –
packages/editor/src/store/use-editor.tsx,packages/editor/src/lib/utils.ts,packages/editor/src/lib/sfx-bus.ts; 44 modules depend onuse-editor; churn radius is high. - [HIGH/soundness] Import cycle member x31 – files include
tree-node.tsx,use-scene.ts,node-renderer.tsx; mutually reachable imports. - [HIGH/cognitive_load] Oversized file x6 –
site-panel/index.tsx(1413 loc),catalog-items.tsx,use-placement-coordinator.tsx. - [HIGH/cognitive_load] Deep nesting x9 –
collections-popover.tsx,command-palette/index.tsx,item-catalog/item-catalog.tsx(max indent 9). - [MEDIUM/cognitive_load] High branching density x5 –
spatial-grid-manager.ts,spatial-grid-sync.ts,scene.ts(169 branch points over 555 loc). - [HIGH/clarity] Duplicated code blocks – 971 repeated 6‑line blocks across 74 files (e.g.,
apps/editor/app/privacy/page.tsx,apps/editor/app/terms/page.tsx,packages/editor/src/lib/utils.ts).
SDLC observations (code‑health audit, 8 findings)
- Pin third‑party GitHub Actions to a commit SHA (
.github/workflows–oven-sh/setup-bun@v2). - Add a test suite; repository has no test files.
- Open a pull request instead of pushing directly to the default branch (
.github/workflows/release.ymlusesgit push --follow-tags). - Enable Dependabot/Renovate (9 manifests, no update bot configured).
The Bottom Line
The editor is a well‑structured React‑Three‑Fiber monorepo with a clear separation of core schemas, viewer rendering, and editor UI. Its strength lies in the node‑registry pattern and Zustand stores that keep scene state predictable. However, the lack of test coverage, 31 circular import cycles, and several oversized, deeply‑nested files create a high risk for any change that touches the hub use-editor module or adds new node types. Teams comfortable with React Three Fiber who are prepared to invest in test infrastructure and cycle‑breaking refactors can benefit from the platform; others may find the initial ramp‑up costly.