brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
pascal-editorBuild and extend 3D building editor apps with Pascal Editor, an open-source React Three Fiber and WebGPU editor whose scene is a flat Zustand node store (site, building, level, wall, door, window, slab, zone). Use when building a 3D architectural tool or BIM-like editor, generating floor plans from code or an AI agent, extending Pascal with custom node plug…
Independent GitHub snapshot · Automated classification · Original documentation governs
Pascal Editor (pascalorg/editor, MIT) is a 3D building editor built on React Three Fiber with a WebGPU renderer. The scene is a flat dictionary of typed nodes held in a Zustand store (useScene); parent/child links are ids. It ships as npm packages: @pascal-app/core (zod node schemas, stores, registry), @pascal-app/viewer (3D rendering), @pascal-app/nodes (built-in node definitions, renderers and systems as a plugin), @pascal-app/editor (tools and panels) and @pascal-app/cli (local editor plus MCP server). There is no @pascal-app/ui package. Versions checked: core and viewer 1.0.3.
npm install --global @pascal-app/cli
npx @pascal-app/cli editor # editor + authenticated MCP service, projects in ~/.pascal/data/pascal.db
An agent can be pointed at pascal mcp connect (no browser needed). To embed instead of run, install into a Next.js app:
npm install @pascal-app/core @pascal-app/viewer @pascal-app/nodes @pascal-app/editor
npm install next react react-dom three @react-three/fiber @react-three/drei lucide-react zustand
Site -> Building -> Level -> Wall -> Door / Window / Item
-> Slab, Ceiling, Roof, Zone, Stair, Guide, Scan ...
Every node has id (prefixed, e.g. wall_4muwxrhv...), type, parentId, visible, optional metadata, and containers have children (ids). Properties are flat on the node: there is no props wrapper. Units are metres; plan coordinates are [x, z] pairs.
import { useScene } from '@pascal-app/core' // nodes, rootNodeIds, dirtyNodes, CRUD
import { useViewer } from '@pascal-app/viewer' // selection, levelMode, wallMode, camera mode
useScene actions include createNode(node, parentId?), createNodes, updateNode(id, data), updateNodes, deleteNode(s), markDirty, setScene, clearScene. The scene persists to IndexedDB with undo/redo (Zundo). useEditor (active tool, layers, panels) lives in @pascal-app/editor.
Schemas are exported from @pascal-app/core and fill defaults and ids. Verified in Node 24:
import { useScene, SiteNode, BuildingNode, LevelNode, WallNode, DoorNode, SlabNode, ZoneNode } from '@pascal-app/core'
const scene = useScene.getState()
const site = SiteNode.parse({ polygon: { type: 'polygon', points: [[0, 0], [30, 0], [30, 30], [0, 30]] } })
scene.createNode(site)
const building = BuildingNode.parse({}); scene.createNode(building, site.id)
const level = LevelNode.parse({ level: 0, height: 2.7 }); scene.createNode(level, building.id)
const wall = WallNode.parse({ start: [0, 0], end: [10, 0], thickness: 0.2, height: 2.7 })
scene.createNode(wall, level.id)
// Door position is the centre in wall-local coordinates; y = half the height
const door = DoorNode.parse({ position: [3.5, 1.05, 0], width: 1.0, height: 2.1, wallId: wall.id })
scene.createNode(door, wall.id)
The parent's children array is updated by createNode. Other useful fields: WindowNode has position, width and height (default 1.5), windowType, sill; SlabNode has polygon, holes, thickness, elevation; ZoneNode has name, polygon, ceilingHeight (2.7), color, spaceRole. LevelNode uses level (index), baseElevation and height, not an elevation per level. updateNode schedules work with requestAnimationFrame, so it only runs in a browser, not a bare Node script.
import { loadPlugin } from '@pascal-app/core'
import { builtinPlugin } from '@pascal-app/nodes'
import { Viewer } from '@pascal-app/viewer'
const registryReady = loadPlugin(builtinPlugin) // register node definitions before rendering
export function BuildingViewer({ ready }: { ready: boolean }) {
return ready ? <div style={{ width: '100vw', height: '100vh' }}><Viewer /></div> : null
}
Set ready after registryReady resolves. Viewer creates its own canvas and accepts children such as camera controls. Geometry is rebuilt by systems (WallSystem, ZoneSystem, ...) for nodes in dirtyNodes.
New node types are plugins registered with loadPlugin; the reference example is pascalorg/plugin-trees. Look up a node's 3D object through useRegistry; components talk through the exported emitter (mitt).
height: 2.7) as above.WallNode.parse({ start, end, thickness: 0.2, height: 2.7 }): [0,0]->[10,0], [10,0]->[10,8], [10,8]->[0,8], [0,8]->[0,0].SlabNode with polygon [[0,0],[10,0],[10,8],[0,8]], thickness 0.25.DoorNode (width 1.0, height 2.1) at wall-local x 3.5 on the first wall, and a WindowNode on the east wall.ZoneNodes named "Living Room" and "Bedrooms" for labels and area.Result: Object.keys(useScene.getState().nodes) lists the new ids, and the level's children holds walls, slab and zones.
npx @pascal-app/cli editor
The CLI prints the editor URL and starts the MCP service on free ports. Add an MCP server entry whose command is pascal mcp connect to the agent's configuration, then ask it to "add a 1.2 m window to the south wall of Ground Floor". The agent uses the exposed tools (see @pascal-app/core/agent-tools) and you see the change in the open editor.
position is relative to the wall, not the level.XNode.parse) so ids and defaults are valid; parent them with the second argument of createNode.height and thickness are optional with no schema default, so pass them explicitly.@pascal-app/ui, registerSystem or props: examples found in older posts; they do not exist in 1.0.x.scene-migrations (@pascal-app/core/scene-migrations).architectural-dimensions skill for real-world size checks.