### Setup Pascal Editor Development Environment Source: https://github.com/pascalorg/editor/blob/main/CONTRIBUTING.md Clone the repository, install dependencies with Bun, and start the development server. The editor will be accessible at http://localhost:3000. ```bash git clone https://github.com/pascalorg/editor.git cd editor bun install bun dev ``` -------------------------------- ### Install and Run Development Server Source: https://github.com/pascalorg/editor/blob/main/apps/editor/README.md Commands to install dependencies and start the development server for the editor. ```bash # Install dependencies pnpm install # Run development server pnpm dev # Open http://localhost:3000 ``` -------------------------------- ### Development Server Setup Source: https://github.com/pascalorg/editor/blob/main/README.md Instructions for setting up the development environment. This involves installing dependencies using Bun and running the development server from the root directory to enable hot reloading across all packages. ```bash # Install dependencies bun install ``` -------------------------------- ### Install and Run Pascal Editor Source: https://github.com/pascalorg/editor/blob/main/SETUP.md Use these commands to install project dependencies and start the development server. The editor will be accessible at http://localhost:3000. ```bash bun install bun dev ``` -------------------------------- ### Install @pascal-app/core Source: https://github.com/pascalorg/editor/blob/main/packages/core/README.md Install the core library using npm. ```bash npm install @pascal-app/core ``` -------------------------------- ### Install @pascal-app/viewer Source: https://github.com/pascalorg/editor/blob/main/packages/viewer/README.md Install the viewer component and its core dependency using npm. ```bash npm install @pascal-app/viewer @pascal-app/core ``` -------------------------------- ### Install Peer Dependencies Source: https://github.com/pascalorg/editor/blob/main/packages/core/README.md Install necessary peer dependencies for @pascal-app/core. ```bash npm install react three @react-three/fiber @react-three/drei ``` -------------------------------- ### Install @pascal-app/mcp Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/README.md Install the MCP package using Bun. Ensure @pascal-app/core is available as a peer dependency. ```bash bun add @pascal-app/mcp ``` -------------------------------- ### Copy Environment Example File Source: https://github.com/pascalorg/editor/blob/main/SETUP.md Copy the example environment file to `.env` to configure optional settings. This is useful for enabling features like address search or customizing the development port. ```bash cp .env.example .env ``` -------------------------------- ### Install and Build MCP Package Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/README.md Commands to install dependencies and build the MCP package. ```bash bun install bun run --cwd packages/mcp build ``` -------------------------------- ### Launch MCP Server over stdio Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/README.md Start the MCP server using Bun, communicating over standard input/output. ```bash bunx pascal-mcp ``` -------------------------------- ### Run Development Server Source: https://github.com/pascalorg/editor/blob/main/README.md Starts the Next.js editor development server with hot reload enabled for package changes. Ensure you are in the root directory. ```bash bun dev ``` -------------------------------- ### Codex TOML Configuration Example Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/README.md Example of the TOML configuration file generated by Codex for an MCP server, detailing the command, arguments, and environment variables. ```toml [mcp_servers.pascal-dev] command = "bun" args = ["/absolute/path/to/editor/packages/mcp/dist/bin/pascal-mcp.js"] [mcp_servers.pascal-dev.env] PASCAL_DATA_DIR = "/Users/you/.pascal/data" ``` -------------------------------- ### Event Key Format Example Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/events.md Illustrates the structure of event keys used in the system, combining node types with specific action suffixes. ```plaintext : ``` -------------------------------- ### System Component Example Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/systems.md Illustrates a basic system component structure, including imports for `useFrame` and `useScene`. Systems should return null and perform computations within the `useFrame` hook. ```tsx import { useFrame } from '@react-three/fiber' import { useScene } from '../store/use-scene' export function MySystem() { const nodes = useScene(s => s.nodes) useFrame(() => { // compute and write back derived state }) return null } ``` -------------------------------- ### Usage Example for canPlaceOnWall Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/spatial-queries.md Shows how to use `canPlaceOnWall` and utilize the `adjustedY` value for precise placement on walls. The `adjustedY` ensures items sit flush with the slab. ```typescript const { valid, adjustedY } = canPlaceOnWall(levelId, wallId, x, y, dims, 'wall', undefined, [item.id]) if (valid) updateNode(item.id, { wallT: x, wallY: adjustedY }) ``` -------------------------------- ### Build Specific Package Source: https://github.com/pascalorg/editor/blob/main/README.md Builds a specific package, for example, '@pascal-app/core'. This is useful for targeted development or testing. ```bash turbo build --filter=@pascal-app/core ``` -------------------------------- ### Local Testing Commands for Pascal MCP Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/PR_DESCRIPTION.md A collection of commands for setting up, testing, and linting the Pascal MCP locally. Includes installation, building, unit/integration tests, smoke tests, and linting. ```bash # From the repo root bun install bun run --cwd packages/core build bun run --cwd packages/mcp build # Unit + integration tests (248 tests across 40 files) bun test --cwd packages/mcp # End-to-end smoke test (spawns stdio server and exercises 4 tools) bun run --cwd packages/mcp smoke # Biome lint bunx biome check packages/mcp # Turbo build bunx turbo build --filter=@pascal-app/mcp ``` -------------------------------- ### Usage Example for canPlaceOnFloor Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/spatial-queries.md Demonstrates how to use `canPlaceOnFloor` within a tool to validate placement before creating a node. Ensure to pass the item's ID to `ignoreIds` if it already exists. ```typescript const pos: [number, number, number] = [x, 0, z] const { valid } = canPlaceOnFloor(levelId, pos, getScaledDimensions(item), item.rotation, [item.id]) if (valid) createNode(item, levelId) ``` -------------------------------- ### Basic Node Renderer Structure Source: https://github.com/pascalorg/editor/blob/main/apps/editor/README.md Shows a simplified example of a `WallRenderer` component. It creates a placeholder mesh, registers it using `useRegistry`, and recursively renders child nodes. ```tsx const WallRenderer = ({ node }) => { const ref = useRef(null!) useRegistry(node.id, 'wall', ref) return ( {/* Replaced by WallSystem */} {node.children.map(id => )} ) } ``` -------------------------------- ### Codex CLI MCP Server Setup Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/PR_DESCRIPTION.md Use this command to add a development MCP server for Codex CLI. It sets the necessary environment variable and specifies the command to run the Pascal MCP. ```bash codex mcp add pascal-dev \ --env PASCAL_DATA_DIR="$HOME/.pascal/data" \ -- bun "$PWD/packages/mcp/dist/bin/pascal-mcp.js" ``` -------------------------------- ### Listen to and Emit Events with Pascal App Core Emitter Source: https://context7.com/pascalorg/editor/llms.txt Use the `emitter` for decoupled communication. Listen to events like 'wall:click' or 'item:enter' and emit control events for camera, guides, and animations. Remember to remove listeners when no longer needed. ```typescript import { emitter } from '@pascal-app/core/events/bus' // ----- Listen to pointer events ----- emitter.on('wall:click', ({ node, position, localPosition, stopPropagation }) => { console.log('Clicked wall:', node.id, 'at', position) stopPropagation() // prevent further handlers in the same frame }) emitter.on('item:enter', ({ node }) => { console.log('Hovering item:', node.asset.name) }) emitter.on('grid:click', ({ position, localPosition }) => { // position = world-space XYZ // localPosition = building-local XYZ (use this for placing nodes) console.log('Grid click at local', localPosition) }) // ----- Camera control events ----- emitter.emit('camera-controls:focus', { nodeId: 'wall_abc' }) emitter.emit('camera-controls:top-view', undefined) emitter.emit('camera-controls:fit-scene', { bounds: { min: [0,0], max: [10,8], center: [5,4], size: [10,8] } }) emitter.emit('camera-controls:generate-thumbnail', { projectId: 'proj_xyz', captureMode: 'standard', snapLevels: true, }) // ----- Guide events ----- emitter.emit('guide:set-reference-scale', { guideId: 'guide_abc' }) // ----- Door / window animation events ----- emitter.on('door:animation-completed', ({ doorId, field }) => { console.log(`Door ${doorId} finished animating field: ${field}`) }) // ----- Remove a listener ----- const handler = (e: { node: WallNode }) => {} emitter.on('wall:context-menu', handler) emitter.off('wall:context-menu', handler) ``` -------------------------------- ### Get Scene Summary Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/examples/generate-apartment.md Retrieve the current scene summary using the specified resource URI. This provides details about the layout, such as the number of sites, buildings, levels, walls, zones, doors, windows, usable area, and perimeter. ```jsonc // resource: pascal://scene/current/summary { "uri": "pascal://scene/current/summary" } ``` -------------------------------- ### Accessing and Filtering Scene Nodes with `useScene` Source: https://context7.com/pascalorg/editor/llms.txt Subscribe to scene node changes within a React component to display or process scene elements. This example shows how to get all nodes and filter for specific types. ```typescript import useScene from '@pascal-app/core/store/use-scene' import { WallNode, LevelNode } from '@pascal-app/core/schema' // --- Subscribe inside a React component --- function WallList() { const nodes = useScene((state) => state.nodes) const walls = Object.values(nodes).filter((n) => n.type === 'wall') return
    {walls.map((w) =>
  • {w.id}
  • )}
} ``` -------------------------------- ### Load Initial Scene with MCP Server Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/README.md Launch the MCP server and load a scene from a JSON file via stdio. ```bash pascal-mcp --stdio --scene ./my-scene.json ``` -------------------------------- ### Slab Elevation Utilities Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/spatial-queries.md Utilities for getting the correct Y coordinate when items rest on slabs. ```APIDOC ## Slab Elevation Utilities ### Description Provides functions to determine the correct Y coordinate for items placed on slabs. ### Functions #### getSlabElevationAt Gets the Y elevation at a single point on a slab. ```ts import { spatialGridManager } from '@pascal-app/core' const y = spatialGridManager.getSlabElevationAt(levelId, x, z) ``` * **levelId** (string) - The ID of the level. * **x** (number) - The X coordinate. * **z** (number) - The Z coordinate. * **Returns**: (number) The Y elevation at the specified point. #### getSlabElevationForItem Gets the Y elevation considering the item's full footprint (highest slab point under the item). ```ts import { spatialGridManager } from '@pascal-app/core' const y = spatialGridManager.getSlabElevationForItem(levelId, position, dimensions, rotation) ``` * **levelId** (string) - The ID of the level. * **position** (array) - The [x, y, z] coordinates of the item. * **dimensions** (array) - The [width, height, depth] of the item. * **rotation** (array) - The [x, y, z] rotation of the item. * **Returns**: (number) The highest Y elevation under the item's footprint. ``` -------------------------------- ### create_wall Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/README.md Adds a wall to a level with specified start and end points, and optional thickness and height. ```APIDOC ## create_wall ### Description Add a wall to a level. ### Input `{ levelId, start, end, thickness?, height? }` ### Output `{ wallId }` ``` -------------------------------- ### Build and Publish Packages Source: https://github.com/pascalorg/editor/blob/main/README.md Builds specified packages and then publishes them to npm. Ensure you have the necessary permissions and are logged into npm. ```bash turbo build --filter=@pascal-app/core --filter=@pascal-app/viewer npm publish --workspace=@pascal-app/core --access public npm publish --workspace=@pascal-app/viewer --access public ``` -------------------------------- ### Looking Up Objects in the Scene Registry Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/scene-registry.md Provides examples for looking up individual objects by ID or iterating through all objects of a specific type from the scene registry. ```typescript // Single lookup const obj = sceneRegistry.nodes.get(nodeId) if (obj) { /* use obj */ } // Iterate all walls for (const id of sceneRegistry.byType.wall) { const obj = sceneRegistry.nodes.get(id) } ``` -------------------------------- ### Emitting Node Events with useNodeEvents Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/events.md Example of how renderers should emit node events using the `useNodeEvents` hook, which abstracts the direct emitter calls. ```tsx // packages/viewer/src/hooks/use-node-events.ts const events = useNodeEvents(node, 'wall') return ``` -------------------------------- ### Build All Packages Source: https://github.com/pascalorg/editor/blob/main/README.md Builds all packages within the project using Turbo. This command should be run from the project root. ```bash turbo build ``` -------------------------------- ### Create Interior Partitions with apply_patch Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/examples/generate-apartment.md Add interior walls using `apply_patch` to divide the apartment into functional zones, including bedrooms, a bathroom, and an open-plan living/kitchen area. ```jsonc // tool: apply_patch { "name": "apply_patch", "arguments": { "patches": [ { "op": "create", "parentId": "level-1", "node": { "type": "wall", "start": [5.5, 0], "end": [5.5, 8], "thickness": 0.15, "height": 2.5 } }, { "op": "create", "parentId": "level-1", "node": { "type": "wall", "start": [5.5, 4], "end": [10, 4], "thickness": 0.15, "height": 2.5 } }, { "op": "create", "parentId": "level-1", "node": { "type": "wall", "start": [5.5, 5.5], "end": [8, 5.5], "thickness": 0.15, "height": 2.5 } }, { "op": "create", "parentId": "level-1", "node": { "type": "wall", "start": [8, 4], "end": [8, 5.5], "thickness": 0.15, "height": 2.5 } } ] } } ``` -------------------------------- ### Pascal MCP Package Directory Structure Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/PLAN.md Overview of the directory layout for the MCP package, detailing the location of source files, tools, resources, and examples. ```plaintext packages/mcp/ ├── PLAN.md (this file) ├── PR_DESCRIPTION.md (Phase 3 deliverable) ├── CROSS_CUTTING.md (any proposed upstream changes) ├── README.md ├── CHANGELOG.md ├── package.json ├── tsconfig.json ├── src/ │ ├── index.ts # programmatic API re-exports │ ├── server.ts # createPascalMcpServer() factory │ ├── bridge/ │ │ ├── node-shims.ts # RAF polyfill (load FIRST) │ │ ├── scene-bridge.ts # SceneBridge class │ │ └── scene-bridge.test.ts │ ├── tools/ │ │ ├── index.ts # registerTools(server, bridge) │ │ ├── schemas.ts # shared patch schemas │ │ ├── errors.ts # structured MCP error helpers │ │ ├── get-scene.ts │ │ ├── get-node.ts │ │ ├── describe-node.ts │ │ ├── find-nodes.ts │ │ ├── measure.ts │ │ ├── apply-patch.ts │ │ ├── create-level.ts │ │ ├── create-wall.ts │ │ ├── place-item.ts │ │ ├── cut-opening.ts │ │ ├── set-zone.ts │ │ ├── duplicate-level.ts │ │ ├── delete-node.ts │ │ ├── undo.ts │ │ ├── redo.ts │ │ ├── export-json.ts │ │ ├── export-glb.ts # stub: not_implemented │ │ ├── validate-scene.ts │ │ ├── check-collisions.ts │ │ ├── analyze-floorplan-image.ts │ │ ├── analyze-room-photo.ts │ │ └── *.test.ts (one per tool) │ ├── resources/ │ │ ├── index.ts │ │ ├── scene-current.ts │ │ ├── scene-summary.ts │ │ ├── catalog-items.ts │ │ ├── constraints.ts │ │ └── resources.test.ts │ ├── prompts/ │ │ ├── index.ts │ │ ├── from-brief.ts │ │ ├── iterate-on-feedback.ts │ │ ├── renovation-from-photos.ts │ │ └── prompts.test.ts │ ├── transports/ │ │ ├── stdio.ts │ │ └── http.ts │ └── bin/ │ └── pascal-mcp.ts # CLI entry; shebang #!/usr/bin/env node ├── scripts/ │ └── smoke.ts # end-to-end client test ├── examples/ │ ├── generate-apartment.md │ ├── renovate-from-photos.md │ └── embed-in-agent.ts └── dist/ # generated ``` -------------------------------- ### Cut Opening Tool for Windows Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/examples/renovate-from-photos.md This snippet demonstrates the `cut_opening` tool, used to create window openings in walls. It requires the wall ID, opening type, position, and dimensions. ```jsonc { "name": "cut_opening", "arguments": { "wallId": "wall-south", "type": "window", "position": 0.5, "width": 1.4, "height": 1.5 } } ``` -------------------------------- ### Run Editor and MCP Server with Shared Data Directory Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/README.md Configure both the editor and the MCP server to use the same data directory for seamless local development and data synchronization. ```bash # Terminal 1: run the editor PASCAL_DATA_DIR="$HOME/.pascal/data" bun run dev # Terminal 2 or an MCP host: run the server PASCAL_DATA_DIR="$HOME/.pascal/data" bun packages/mcp/dist/bin/pascal-mcp.js ``` -------------------------------- ### Create a Node using Zod Schema and Scene Tools Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/node-schemas.md Demonstrates how to create a new node instance by parsing data with the Zod schema and then inserting it into the scene using the `createNode` function. Always use `.parse()` for validation and ID generation. ```typescript import { WallNode } from '@pascal-app/core' import { useScene } from '@pascal-app/core' // 1. Parse validates and fills defaults (including auto-generated id) const wall = WallNode.parse({ name: 'Wall 1', start: [0, 0], end: [5, 0] }) // 2. createNode(node, parentId?) inserts it into the scene const { createNode } = useScene.getState() createNode(wall, levelId) ``` -------------------------------- ### Get Slab Elevation Functions Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/spatial-queries.md Provides functions to retrieve the Y coordinate of a slab at a specific point or considering an item's footprint. These are crucial for correct vertical placement on slabs. ```typescript import { spatialGridManager } from '@pascal-app/core' // Y at a single point const y = spatialGridManager.getSlabElevationAt(levelId, x, z) // Y considering the item's full footprint (highest slab point under item) const y = spatialGridManager.getSlabElevationForItem(levelId, position, dimensions, rotation) ``` -------------------------------- ### Photo to Scene Tool Call Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/examples/photo-to-scene.md This is how an agent would call the photo_to_scene tool, providing an image data URI, a scale hint, and a name for the scene. ```jsonc // tool: photo_to_scene { "name": "photo_to_scene", "arguments": { "image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD...", "scaleHint": "1 cm = 1 m, approx 20 m²", "name": "Weekend flat" } } ``` -------------------------------- ### Spread R3F Pointer Handlers onto Mesh Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/selection-managers.md Use `useNodeEvents` to get R3F pointer handlers and spread them onto a mesh component. This enables the mesh to emit selection events. ```tsx const events = useNodeEvents(node, 'wall') return ``` -------------------------------- ### Analyze Floorplan Image Tool Call Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/examples/renovate-from-photos.md This snippet shows the arguments passed to the `analyze_floorplan_image` tool, including the image data and a scale hint for better interpretation. ```jsonc // tool: analyze_floorplan_image { "name": "analyze_floorplan_image", "arguments": { "image": "data:image/png;base64,iVBORw0KGgoAAAANS...", "scaleHint": "1 m grid, total footprint ~9.5 m × 7 m" } } ``` -------------------------------- ### Minimal Renderer Implementation Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/renderers.md A basic example of a renderer component for a custom node type. It uses hooks to register the mesh with the registry and subscribe to pointer events. Ensure the mesh ref is correctly passed to `useRegistry`. ```tsx // packages/viewer/src/components/renderers/my-node/index.tsx import { useRegistry } from '@pascal-app/core' import { useNodeEvents } from '../../hooks/use-node-events' import { useScene } from '@pascal-app/core' export function MyNodeRenderer({ node }: { node: MyNode }) { const ref = useRef(null!) useRegistry(node.id, 'my-node', ref) // 3 args: id, type, ref — no return value const events = useNodeEvents(node, 'my-node') return ( ) } ``` -------------------------------- ### Basic Tool Component Structure Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/tools.md A typical tool component imports `useScene` and `useEditor` hooks. Pointer handlers should mutate the scene store directly or use `useLiveTransforms` for ephemeral state. Local geometry is for preview only. ```tsx import { useScene } from '@pascal-app/core' import { useEditor } from '../../store/use-editor' export function MyTool() { const createNode = useScene(s => s.createNode) const setTool = useEditor(s => s.setTool) // Pointer handlers mutate the scene store directly. // No local geometry — use a renderer for any preview mesh. return ( {/* ghost / preview geometry only */} ) } ``` -------------------------------- ### Place items and search assets with MCP Source: https://context7.com/pascalorg/editor/llms.txt Use `place_item` to add catalog items to levels, walls, or ceilings. First, use `search_assets` to find available items by query and category. ```typescript // Search the built-in catalog const found = await mcpClient.callTool('search_assets', { query: 'sofa', category: 'furniture', }) // → { results: [{ id: 'sofa', name: 'Sofa', dimensions: [2.2,0.9,0.95], … }], total: 1 } ``` ```typescript // Place on floor (level target) const placed = await mcpClient.callTool('place_item', { catalogItemId: 'sofa', targetNodeId: 'level_0', position: [3, 0, 2], rotation: Math.PI / 2, }) // → { itemId: 'item_sofa123', status: 'ok' } ``` ```typescript // Place on wall (wall target) await mcpClient.callTool('place_item', { catalogItemId: 'flat-screen-tv', targetNodeId: 'wall_kx9abc', position: [2.5, 1.2, 0], // localX, height, 0 }) ``` -------------------------------- ### Define a New Node Type with Zod Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/node-schemas.md Example of defining a custom node schema 'MyNode' by extending BaseNode and adding specific fields like 'width' and 'label'. Ensures IDs are prefixed correctly and type discriminators are set. ```typescript // packages/core/src/schema/nodes/my-node.ts import { z } from 'zod' import { BaseNode, objectId, nodeType } from '../base' export const MyNode = BaseNode.extend({ id: objectId('my-node'), // generates IDs like "my-node_abc123" type: nodeType('my-node'), // sets literal type discriminator // add node-specific fields: width: z.number().default(1), label: z.string().optional(), }).describe('My node — one-line description of what it represents') export type MyNode = z.infer export type MyNodeId = MyNode['id'] ``` -------------------------------- ### Event Bus for Inter-Component Communication Source: https://github.com/pascalorg/editor/blob/main/README.md Uses a typed event emitter for communication between components. Examples show listening to node-specific events like 'wall:click' and 'item:enter', as well as grid events. The `NodeEvent` payload structure is also defined. ```typescript // Node events emitter.on('wall:click', (event) => { ... }) emitter.on('item:enter', (event) => { ... }) emitter.on('zone:context-menu', (event) => { ... }) // Grid events (background) emitter.on('grid:click', (event) => { ... }) // Event payload NodeEvent { node: AnyNode position: [x, y, z] localPosition: [x, y, z] normal?: [x, y, z] stopPropagation: () => void } ``` -------------------------------- ### Import Zustand Store from @pascal-app/core/store Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/PLAN.md Import the default export, which is the Zustand store for managing scene state. Use `useScene.getState()` or `useScene.temporal.getState()` for accessing store data. ```typescript import useScene from '@pascal-app/core/store' // NOTE: useScene is the DEFAULT export from this subpath // Use useScene.getState() / useScene.temporal.getState() as usual. ``` -------------------------------- ### MCP CI Workflow Configuration Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/CROSS_CUTTING.md Defines a GitHub Actions CI workflow for the Pascal Editor project. This workflow runs on pushes to 'main' and PRs affecting specific packages, automating dependency installation, builds, tests, and code linting using Bun. ```yaml name: MCP CI on: push: branches: [ main ] pull_request: paths: - "packages/mcp/**" - "packages/core/**" - "packages/editor/src/scene-api/**" - ".github/workflows/mcp-ci.yml" - "bun.lock" env: BUN_VERSION: 1.0.34 jobs: build-and-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Bun uses: oven-sh/setup-bun@v1 with: bun-version: ${{ env.BUN_VERSION }} - name: Install dependencies run: bun install - name: Build core package run: bun run --filter @pascal-app/core build - name: Build MCP package run: bun run --filter @pascal-app/mcp build - name: Run MCP tests run: bun run --filter @pascal-app/mcp test - name: Run editor scene API tests run: bun run --filter @pascal-app/editor test:scene-api - name: Run Biome lint run: bun run --filter @pascal-app/mcp biome check --apply - name: Run Biome lint (editor scene API) run: bun run --filter @pascal-app/editor biome check --apply packages/editor/src/scene-api ``` -------------------------------- ### Listening to Multiple Node Types Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/events.md Shows how to set up a single handler for click events across multiple node types (wall, slab, door) and the corresponding cleanup. ```typescript // Multiple node types, same handler useEffect(() => { const types = ['wall', 'slab', 'door'] as const const handler = (e: NodeEvent) => { /* … */ } types.forEach(t => emitter.on(`${t}:click`, handler as any)) return () => types.forEach(t => emitter.off(`${t}:click`, handler as any)) }, []) ``` -------------------------------- ### Import Wall Helpers from @pascal-app/core/wall Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/PLAN.md Import pure functions and constants related to wall geometry calculations. These are safe for use in Node.js. ```typescript import { DEFAULT_WALL_HEIGHT, DEFAULT_WALL_THICKNESS, getWallPlanFootprint, getWallThickness, } from '@pascal-app/core/wall' ``` -------------------------------- ### Scene State Management with Zustand Source: https://github.com/pascalorg/editor/blob/main/README.md Illustrates the structure of the scene state managed by the Zustand store in @pascal-app/core, including nodes, root node IDs, dirty nodes, and core operations. ```typescript useScene.getState() = { nodes: Record, rootNodeIds: string[], dirtyNodes: Set, createNode(node, parentId), updateNode(id, updates), deleteNode(id), } ``` -------------------------------- ### Apply Patch Tool for Creating Walls Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/examples/renovate-from-photos.md This snippet shows how the `apply_patch` tool is used to create walls based on the floorplan analysis. It specifies the parent ID and node details for each wall. ```jsonc // tool: apply_patch { "name": "apply_patch", "arguments": { "patches": [ { "op": "create", "parentId": "level-1", "node": { "type": "wall", "start": [0, 0], "end": [9.5, 0], "thickness": 0.25, "height": 2.5 } }, /* ...remaining perimeter + partition walls from the vision result... */ ] } } ``` -------------------------------- ### Inspect Current Scene with get_scene Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/examples/generate-apartment.md Use the `get_scene` tool to retrieve the current state of the scene. This is typically the first step to understand the existing structure before applying modifications. ```jsonc // tool: get_scene { "name": "get_scene", "arguments": {} } ``` ```jsonc { "nodes": { "site-1": { "type": "site", "id": "site-1", "children": [/* ... */] }, "building-1": { "type": "building", "id": "building-1", "parentId": "site-1" }, "level-1": { "type": "level", "id": "level-1", "parentId": "building-1", "elevation": 0, "height": 2.5 } }, "rootNodeIds": ["site-1"] } ``` -------------------------------- ### Create Multiple Nodes in Batch Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/node-schemas.md Shows how to efficiently create multiple nodes by passing an array of node objects and their parent IDs to the `createNodes` function. Each node should be parsed with its respective schema first. ```typescript const { createNodes } = useScene.getState() createNodes([ { node: WallNode.parse({ start: [0, 0], end: [5, 0] }), parentId: levelId }, { node: WallNode.parse({ start: [5, 0], end: [5, 4] }), parentId: levelId }, ]) ``` -------------------------------- ### Create and Update Scene Nodes Source: https://github.com/pascalorg/editor/blob/main/packages/core/README.md Demonstrates creating a WallNode and adding it to the scene using the useScene hook. Also shows how to subscribe to scene changes to display the number of walls. ```typescript import { useScene, WallNode, ItemNode } from '@pascal-app/core' // Create a wall const wall = WallNode.parse({ points: [[0, 0], [5, 0]], height: 3, thickness: 0.2, }) useScene.getState().createNode(wall, parentLevelId) // Subscribe to scene changes function MyComponent() { const nodes = useScene((state) => state.nodes) const walls = Object.values(nodes).filter(n => n.type === 'wall') return
Total walls: {walls.length}
} ``` -------------------------------- ### useViewer Store Source: https://context7.com/pascalorg/editor/llms.txt Zustand store for viewer presentation state, including selection path, camera mode, level display mode, wall cutaway mode, theme, units, and export callbacks. It is persisted to localStorage as 'viewer-preferences'. ```APIDOC ## `@pascal-app/viewer` — `useViewer` Store Zustand store for viewer presentation state: selection path, camera mode, level display mode, wall cutaway mode, theme, units, and export callbacks. Persisted to `localStorage` as `viewer-preferences`. ### Subscribe (React) ```typescript import useViewer from '@pascal-app/viewer/store/use-viewer' function LevelModeToggle() { const levelMode = useViewer((state) => state.levelMode) const setLevelMode = useViewer((state) => state.setLevelMode) return ( ) } ``` ### Access outside React ```typescript const { selection, setSelection, resetSelection } = useViewer.getState() // Navigate the selection hierarchy (parent resets cascade down) setSelection({ buildingId: 'building_abc' }) // → levelId = null, zoneId = null, selectedIds = [] setSelection({ buildingId: 'building_abc', levelId: 'level_0' }) // → zoneId = null, selectedIds = [] setSelection({ levelId: 'level_0', selectedIds: ['item_sofa'] }) // Reset everything resetSelection() ``` ### Level display modes ```typescript useViewer.getState().setLevelMode('stacked') // all levels at true elevation useViewer.getState().setLevelMode('exploded') // levels fanned apart vertically useViewer.getState().setLevelMode('solo') // only selected level visible useViewer.getState().setLevelMode('manual') // levels positioned manually ``` ### Wall modes ```typescript useViewer.getState().setWallMode('up') // full walls useViewer.getState().setWallMode('cutaway') // walls cut at eye level useViewer.getState().setWallMode('down') // walls hidden ``` ### Camera mode ```typescript useViewer.getState().setCameraMode('perspective') useViewer.getState().setCameraMode('orthographic') ``` ### Theme / units ```typescript useViewer.getState().setTheme('dark') useViewer.getState().setUnit('imperial') ``` ### Register an export handler ```typescript useViewer.getState().setExportScene(async (format = 'glb') => { console.log('Exporting as', format) }) await useViewer.getState().exportScene?.('glb') ``` ``` -------------------------------- ### Injecting Editor-Specific Systems Source: https://github.com/pascalorg/editor/blob/main/wiki/architecture/systems.md Demonstrates how an editor application can inject its own custom systems as children of the `` component without modifying the viewer package itself. ```tsx // apps/editor — editor injects its own systems without modifying the viewer ``` -------------------------------- ### add_window Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/README.md Adds a window to a wall using parametric placement and sill height. ```APIDOC ## add_window ### Description Add a window to a wall using parametric placement and sill height. ### Input `{ wallId, t, width?, height?, sillHeight? }` ### Output `{ windowId, localX, sillHeight }` ``` -------------------------------- ### MCP Server Configuration for Claude Desktop Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/PR_DESCRIPTION.md Add this JSON configuration to your Claude Desktop settings to enable the Pascal MCP server. Ensure the command path points to your local `pascal-mcp.js` build. ```json { "mcpServers": { "pascal": { "command": "bun", "args": ["/absolute/path/to/editor/packages/mcp/dist/bin/pascal-mcp.js"], "env": { "PASCAL_DATA_DIR": "/Users/you/.pascal/data" } } } } ``` -------------------------------- ### Import Clone Helpers from @pascal-app/core/clone-scene-graph Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/PLAN.md Import functions and types for cloning and forking scene graphs. These utilities are essential for creating copies or modified versions of the scene data. ```typescript import { cloneLevelSubtree, cloneSceneGraph, forkSceneGraph, type SceneGraph, } from '@pascal-app/core/clone-scene-graph' ``` -------------------------------- ### Apply Renovation Patch - Pascal Editor Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/examples/renovate-from-photos.md This JSON object defines a patch operation to renovate a scene. It includes operations to delete a partition wall, update a living zone's label and polygon, and delete the kitchen zone. Use this to apply structural changes based on analysis. ```jsonc { "name": "apply_patch", "arguments": { "patches": [ { "op": "delete", "id": "wall-partition-living-kitchen", "cascade": false }, { "op": "update", "id": "zone-living", "data": { "label": "Open-Plan Living / Kitchen", "polygon": [[0, 0], [9.5, 0], [9.5, 3.5], [0, 3.5]] } }, { "op": "delete", "id": "zone-kitchen", "cascade": false } /* + item moves / deletes for the TV unit etc. */ ] } } ``` -------------------------------- ### Using the Pascal App Viewer Component Source: https://context7.com/pascalorg/editor/llms.txt The `` component provides a standalone 3D canvas. Customize hover and selection styles, performance monitoring, and BVH acceleration. Inject custom editor tools or overlays as children. ```tsx import Viewer from '@pascal-app/viewer' import useScene from '@pascal-app/core/store/use-scene' import { useEffect } from 'react' function App() { useEffect(() => { useScene.getState().loadScene() // initialises Site → Building → Level }, []) return (
{/* Inject custom editor tools, overlays, extra systems here */}
) } // Minimal read-only viewer (no selection) function ReadOnlyViewer() { return ( {/* No editor children */} ) } ``` -------------------------------- ### create_room Source: https://github.com/pascalorg/editor/blob/main/packages/mcp/README.md Creates a room, including its zone, slab, ceiling, and walls, from a given polygon. ```APIDOC ## create_room ### Description Create a zone, slab, ceiling, and walls from a polygon. ### Input `{ levelId, name, polygon, color?, wallHeight?, wallThickness? }` ### Output `{ zoneId, slabId, ceilingId, wallIds, areaSqMeters }` ```