pascalorg/editor
Pascal Editor
A 3D building editor built with React Three Fiber and WebGPU.
https://github.com/user-attachments/assets/8b50e7cf-cebe-4579-9cf3-8786b35f7b6b
Run the Editor Locally
Node.js 22.13 or newer can create a persistent local Pascal installation without cloning this repository:
|
|
The CLI starts the editor and an authenticated MCP service in the background, selects
collision-free loopback ports, and keeps projects in ~/.pascal/data/pascal.db. Configure
an agent to launch pascal mcp connect. See Run Pascal locally
for pnpm/Bun commands, project management, MCP setup, updates, storage paths, and
troubleshooting. The npm release is the older runtime described below; use the verified
GitHub preview when a task needs the new read-only furniture candidate check.
Candidate-enabled CLI preview
The npm beta tag currently resolves to @pascal-app/cli@1.0.0-beta.1, which predates the read-only furniture candidate input in this repository. To use that capability before the next npm release, install the verified GitHub prerelease built from commit aa653f2f523f81f361ac20cb42b745faf7e46844:
|
|
The expected archive SHA-256 is 814ffa8c6f6a5fced73bf909c616d9a78feff18fd61fd0b4b7d65e74fad5a33d. The same-version update command installs and activates this CLI’s bundled runtime, restarting an older running service when necessary. Keep an existing PASCAL_HOME unchanged so stored projects remain in the same data directory; pascal editor alone reuses any healthy service, including an older one. Keep the preview prefix on the agent host’s PATH before running pascal mcp setup claude, pascal mcp setup codex, or configuring pascal mcp connect manually. This GitHub prerelease is not an npm version.
Use one active agent client per local CLI service. The standalone local HTTP runtime shares active scene state between clients; use separate PASCAL_HOME directories and service processes when independent concurrent work is required.
Agent skills
Install Pascal’s public agent workflows from this repository with skills.sh:
|
|
Claude Code users can install the same canonical skill source as a plugin:
|
|
Codex users can install the same plugin from the repository marketplace:
|
|
pascal-3d covers safe local or hosted MCP setup and verified scene work. furniture-fit produces a bounded, evidence-based footprint assessment without claiming unsupported height, swing, or delivery checks. See skills/README.md for package details and validation.
The skills inspect the connected MCP tool schemas before using optional fields. A capability present in this repository may be absent from an older installed or hosted release; the agent should report the narrower supported result instead of assuming source-only inputs are available.
Using Published Packages
The viewer runtime and built-in node definitions are separate packages. Install the full built-in
viewer set, then load the built-in plugin once before mounting <Viewer>. Capture sessions are an
optional transport-neutral extension:
|
|
|
|
See the @pascal-app/viewer quick start for a React example.
Repository Architecture
This is a Turborepo monorepo with the reusable editor packages, the standalone app, and the CLI that distributes it:
|
|
Separation of Concerns
| Package | Responsibility |
|---|---|
| @pascal-app/core | Node schemas, scene state (Zustand), registry contracts, spatial queries, and event bus |
| @pascal-app/viewer | 3D rendering via React Three Fiber, shared render systems, default camera/controls, and post-processing |
| @pascal-app/capture-protocol | Versioned capture manifests, normalized streams, and transport-neutral static/live sources |
| @pascal-app/capture-viewer | Viewer child runtime and reference model, device-motion, and point-cloud layers |
| @pascal-app/editor | Editing tools, panels, selection, and direct-manipulation UI |
| @pascal-app/nodes | Built-in registry plugin with node definitions, renderers, geometry, and systems |
| @pascal-app/cli | Installs and manages a versioned standalone editor runtime and persistent local data |
| @pascal-app/mcp | Exposes scene tools, resources, prompts, and local storage to MCP-compatible AI hosts |
| apps/editor | Standalone Next.js host for the editor packages |
The viewer renders the scene with sensible defaults. The editor extends it with interactive tools, selection management, and editing capabilities.
Stores
Each package has its own Zustand store for managing state:
| Store | Package | Responsibility |
|---|---|---|
useScene |
@pascal-app/core |
Scene data: nodes, root IDs, dirty nodes, CRUD operations. Persisted to IndexedDB with undo/redo via Zundo. |
useViewer |
@pascal-app/viewer |
Viewer state: current selection (building/level/zone IDs), level display mode (stacked/exploded/solo), camera mode. |
useEditor |
apps/editor |
Editor state: active tool, structure layer visibility, panel states, editor-specific preferences. |
Access patterns:
|
|
Core Concepts
Nodes
Nodes are the data primitives that describe the 3D scene. All nodes extend BaseNode:
|
|
Node Hierarchy:
|
|
Nodes are stored in a flat dictionary (Record<id, Node>), not a nested tree. Parent-child relationships are defined via parentId and children arrays.
Scene State (Zustand Store)
The scene is managed by a Zustand store in @pascal-app/core:
|
|
Middleware:
- Persist - Saves to IndexedDB (excludes transient nodes)
- Temporal (Zundo) - Undo/redo with 50-step history
Scene Registry
The registry maps node IDs to their Three.js objects for fast lookup:
|
|
Renderers register their refs using the useRegistry hook:
|
|
This allows systems to access 3D objects directly without traversing the scene graph.
Node Renderers
Renderers are React components that create Three.js objects for each node type:
|
|
Pattern:
- Renderer creates a placeholder mesh/group
- Registers it with
useRegistry - Systems update geometry based on node data
Example (simplified):
|
|
Systems
Systems are React components that run in the render loop (useFrame) to update geometry and transforms. They process dirty nodes marked by the store.
Core Systems (in @pascal-app/core):
| System | Responsibility |
|---|---|
WallSystem |
Generates wall geometry with mitering and CSG cutouts for doors/windows |
SlabSystem |
Generates floor geometry from polygons |
CeilingSystem |
Generates ceiling geometry |
RoofSystem |
Generates roof geometry |
ItemSystem |
Positions items on walls, ceilings, or floors (slab elevation) |
Viewer Systems (in @pascal-app/viewer):
| System | Responsibility |
|---|---|
LevelSystem |
Handles level visibility and vertical positioning (stacked/exploded/solo modes) |
ScanSystem |
Controls 3D scan visibility |
GuideSystem |
Controls guide image visibility |
Processing Pattern:
|
|
Dirty Nodes
When a node changes, it’s marked as dirty in useScene.getState().dirtyNodes. Systems check this set each frame and only recompute geometry for dirty nodes.
|
|
Manual marking:
|
|
Event Bus
Inter-component communication uses a typed event emitter (mitt):
|
|
Spatial Grid Manager
Handles collision detection and placement validation:
|
|
Used by item placement tools to validate positions and calculate slab elevations.
Editor Architecture
The editor extends the viewer with:
Tools
Tools are activated via the toolbar and handle user input for specific operations:
- SelectTool - Selection and manipulation
- WallTool - Draw walls
- ZoneTool - Create zones
- ItemTool - Place furniture/fixtures
- SlabTool - Create floor slabs
Selection Manager
The editor uses a custom selection manager with hierarchical navigation:
|
|
Each depth level has its own selection strategy for hover/click behavior.
Editor-Specific Systems
ZoneSystem- Controls zone visibility based on level mode- Custom camera controls with node focusing
Data Flow
|
|
Building a Plugin
The editor is extensible: a plugin ships node kinds (schema, 3D/2D rendering, placement tools, inspector parametrics) and left-rail panels through the same Plugin manifest the built-ins use — there is no separate internal API.
- Developer guide — Create a plugin: the
Pluginshape, panel contributions, discovery, lifecycle, and what’s in/out of v1. - Worked example —
pascalorg/plugin-trees: a standalone plugin with procedural trees, flowers, grass, and a presets panel. Clone it as a starting point.
Technology Stack
- React 19 + Next.js 16
- Three.js (WebGPU renderer)
- React Three Fiber + Drei
- Zustand (state management)
- Zod (schema validation)
- Zundo (undo/redo)
- three-bvh-csg (Boolean geometry operations)
- Turborepo (monorepo management)
- Bun (package manager)
Getting Started
Development
Run the development server from the root directory to enable hot reload for all packages:
|
|
Important: Always run bun dev from the root directory to ensure the package watchers are running. This enables hot reload when you edit files in packages/core/src/ or packages/viewer/src/.
Building for Production
|
|
Publishing Packages
|
|
Key Files
| Path | Description |
|---|---|
packages/core/src/schema/ |
Node type definitions (Zod schemas) |
packages/core/src/store/use-scene.ts |
Scene state store |
packages/core/src/hooks/scene-registry/ |
3D object registry |
packages/core/src/systems/ |
Geometry generation systems |
packages/viewer/src/components/renderers/ |
Node renderers |
packages/viewer/src/components/viewer/ |
Main Viewer component |
apps/editor/components/tools/ |
Editor tools |
apps/editor/store/ |
Editor-specific state |
Contributing
Bug fixes, features, docs and ideas are all welcome. Start with CONTRIBUTING.md for setup, code style and the PR flow.
- New node kinds and sidebar panels ship as plugins rather than edits to the built-ins —
pascalorg/plugin-treesis a worked example - Questions and ideas go to Discussions; reproducible bugs go to Issues
- Participation is covered by our Code of Conduct
- Security problems go to SECURITY.md, not a public issue