AI编码
GitHub ·
@pascalorg
pascalorg/editor 发布 v1.0.0 稳定版,并补齐 Agent 技能与多宿主插件分发
pascalorg/editor 发布 v1.0.0,将 beta 阶段稳定的场景模型、节点注册表和包边界以稳定版本发布到 npm latest,并把开放 capture 协议与 viewer runtime 作为 @pascal-app/core/capture 和 @pascal-app/viewer/capture 提供。该版本还完善 agent 分发面:公开 pascal-3d 和 furniture-fit 技能,提供 Claude Code、Codex、Cursor 和 Gemini 的插件清单,进入官方 MCP Registry,并提供可认领和验证托管 agent key 的 CLI。
AI推荐理由 官方 release notes 显示该项目从 1.0.0-beta 毕业到稳定版,npm latest 发布场景模型、节点注册表和包边界,并把开放 capture 协议与 viewer runtime 作为 @pascal-app/core/capture、@pascal-app/viewer/capture 发布;同时补齐 agent 分发面:pascal-3d 与 furniture-fit 公开技能、Claude Code/Codex/Cursor/Gemini 插件清单、MCP Registry 条目和可认领验证托管 agent key 的 CLI。具体新信息是稳定版发布与多宿主 agent 集成路径,局限是摘要未给出实际使用效果或性能数据,安装与注册表条目本身不能证明工具可靠性。
72
正文
Official release notes (https://github.com/pascalorg/editor/releases/tag/v1.0.0)
1.0.0 graduates the `1.0.0-beta` line opened by [v1.0.0-beta.1](https://github.com/pascalorg/editor/releases/tag/v1.0.0-beta.1): the scene model, node registry, and package boundaries that the beta stabilized now ship as stable versions on the npm `latest` tag, and the open capture protocol and viewer runtime ship inside them as `@pascal-app/core/capture` and `@pascal-app/viewer/capture` ([#713](https://github.com/pascalorg/editor/pull/713), [#851](https://github.com/pascalorg/editor/pull/851)). It also closes the agent distribution surface: public `pascal-3d` and `furniture-fit` skills, plugin manifests for Claude Code, Codex, Cursor and Gemini, an entry in the official MCP Registry, and a CLI that can claim and verify a hosted agent key ([#777](https://github.com/pascalorg/editor/pull/777), [#829](https://github.com/pascalorg/editor/pull/829), [#808](https://github.com/pascalorg/editor/pull/808), [#815](https://github.com/pascalorg/editor/pull/815)).
### ✨ Features
**Agent skills and distribution**
- **Public agent skills** — `pascal-3d` and `furniture-fit` teach MCP-capable agents to build, inspect, validate and hand off scenes, and to report measured furniture footprints with evidence-scoped conclusions, fail-closed input gates, blocker-aware next actions, and a no-sign-in footprint pre-check link ([#777](https://github.com/pascalorg/editor/pull/777), [#781](https://github.com/pascalorg/editor/pull/781), [#791](https://github.com/pascalorg/editor/pull/791), [#794](https://github.com/pascalorg/editor/pull/794), [#824](https://github.com/pascalorg/editor/pull/824)), with sharper skill discovery and MCP safety metadata across both bundles ([#812](https://github.com/pascalorg/editor/pull/812))
- **Plugin bundles 0.1.3 → 0.1.8** — one canonical `skills/` source ships as a single versioned plugin with a recorded validation ledger per bundle ([#782](https://github.com/pascalorg/editor/pull/782), [#795](https://github.com/pascalorg/editor/pull/795), [#802](https://github.com/pascalorg/editor/pull/802), [#811](https://github.com/pascalorg/editor/pull/811), [#825](https://github.com/pascalorg/editor/pull/825))
- **Claude Code plugin** — this repository installs as `pascal-agent-skills@pascal`, with a credential-free local `pascal mcp connect` server bundled ([#777](https://github.com/pascalorg/editor/pull/777), [#810](https://github.com/pascalorg/editor/pull/810)) and a hosted `pascal-hosted` Streamable HTTP server whose optional Pascal API key is collected as sensitive plugin user configuration and stored in the OS keychain rather than any file ([#835](https://github.com/pascalorg/editor/pull/835))
- **Codex, Cursor and Gemini manifests** — a portable root `mcp.json` (the spec reads `mcp.json`, not `.mcp.json`), `.cursor-plugin/plugin.json`, `gemini-extension.json`, plated marketplace logos, and `bun run skills:validate` asserting parity across every descriptor ([#829](https://github.com/pascalorg/editor/pull/829))
- **OpenAI Agent Plugins manifest** — root `plugin.json` with listing metadata, branding assets, a With MCP review packet, and per-tool annotation justifications for all 46 MCP tools ([#796](https://github.com/pascalorg/editor/pull/796), [#797](https://github.com/pascalorg/editor/pull/797), [#799](https://github.com/pascalorg/editor/pull/799), [#813](https://github.com/pascalorg/editor/pull/813))
- **ClawHub publication readiness** — scoped `.clawhubignore` policies with regression tests that reject re-inclusion and legacy-override rules ([#798](https://github.com/pascalorg/editor/pull/798), [#806](https://github.com/pascalorg/editor/pull/806))
- **Official MCP Registry entry** — `io.github.pascalorg/editor` publishes the hosted Streamable HTTP endpoint, with CI validating the manifest against the live API catalog ([#808](https://github.com/pascalorg/editor/pull/808), [#809](https://github.com/pascalorg/editor/pull/809))
**CLI**
- **`@pascal-app/cli`** — persistent local editor installation and lifecycle management over a packaged standalone runtime, with update rollback, damaged-runtime recovery, collision-safe startup, flat standalone dependencies, and first-run command setup ([#604](https://github.com/pascalorg/editor/pull/604), [#605](https://github.com/pascalorg/editor/pull/605), [#609](https://github.com/pascalorg/editor/pull/609), [#610](https://github.com/pascalorg/editor/pull/610), [#611](https://github.com/pascalorg/editor/pull/611))
- **Managed MCP alongside the local editor** — `npx @pascal-app/cli editor` starts the editor and an authenticated dynamic-port MCP service together, so `pascal mcp connect` needs neither the port nor the private token in client configuration ([#612](https://github.com/pascalorg/editor/pull/612))
- **`pascal agent claim` and `pascal agent status`** — an agent opens a prefilled 15-minute human handoff and verifies its hosted key without storing or printing it, shipped through the verified GitHub CLI preview channel ([#815](https://github.com/pascalorg/editor/pull/815), [#818](https://github.com/pascalorg/editor/pull/818), [#821](https://github.com/pascalorg/editor/pull/821), [#780](https://github.com/pascalorg/editor/pull/780), [#819](https://github.com/pascalorg/editor/pull/819), [#822](https://github.com/pascalorg/editor/pull/822))
**MCP server**
- **Layout clearance** for doors and item overlaps ([#569](https://github.com/pascalorg/editor/pull/569)); surface holes subtracted from measured area ([#563](https://github.com/pascalorg/editor/pull/563))
- **Tool execution middleware**, with cancellation propagated to host executors and callback updates kept inside them ([#801](https://github.com/pascalorg/editor/pull/801), [#803](https://github.com/pascalorg/editor/pull/803), [#804](https://github.com/pascalorg/editor/pull/804))
- **Honest tool contracts** — returned fields declared in tool output schemas ([#575](https://github.com/pascalorg/editor/pull/575)), and skipped persistence reported instead of silently dropping live sync ([#736](https://github.com/pascalorg/editor/pull/736))
**Capture**
- **Open capture protocol and viewer runtime** — `@pascal-app/core/capture` (formerly the beta-only `@pascal-app/capture-protocol`, folded in [#851](https://github.com/pascalorg/editor/pull/851)) covers extensible capture manifests, artifacts, coordinate frames and live streams; `@pascal-app/viewer/capture` renders room models, point clouds, surface meshes and animated device motion; scan nodes gain capture-session references, transforms and per-layer visibility, and capture layers mount as host-provided scene-tree children ([#713](https://github.com/pascalorg/editor/pull/713))
- **Manifest-first previews** — JSON preview artifacts hydrate through a single renderability predicate, so a stream can't be declared renderable without a hydration path ([#757](https://github.com/pascalorg/editor/pull/757)); shared local Clay previews and optional dollhouse rendering align validation with the recorder's native face budget ([#788](https://github.com/pascalorg/editor/pull/788))
- **Authored camera poses** — external camera tools request a still from an explicit world-space pose, including roll and output framing, without moving the working viewport, and receive correlated saved/failed events ([#768](https://github.com/pascalorg/editor/pull/768))
- **Capture-mode camera suite** — FOV lens, walk/drone framing and keycap hints ([#742](https://github.com/pascalorg/editor/pull/742)), walk/drone snapshot framing brought up to orbit's standard ([#743](https://github.com/pascalorg/editor/pull/743)), level-follow camera and snapshot suite ([#752](https://github.com/pascalorg/editor/pull/752)), WebP snapshot encoding ([#622](https://github.com/pascalorg/editor/pull/622)), and a 2048 px long-edge clamp on viewport/area captures ([#639](https://github.com/pascalorg/editor/pull/639))
**Editor and modeling**
- **Custom mesh editing** — Blender-style mesh editing, plus block mesh editing UX ([#638](https://github.com/pascalorg/editor/pull/638), [#691](https://github.com/pascalorg/editor/pull/691))
- **Roofs** — lean-to extensions with automatic drainage ([#651](https://github.com/pascalorg/editor/pull/651)), improved materials, drawing and intersections ([#690](https://github.com/pascalorg/editor/pull/690))
- **MEP** — duct and pipe fittings, routing and system checks ([#769](https://github.com/pascalorg/editor/pull/769)), and connected pipe/duct drafting ([#827](https://github.com/pascalorg/editor/pull/827))
- **Cabinets** — exact sizing with run-aware editing ([#753](https://github.com/pascalorg/editor/pull/753)), modular constraints and finishes ([#719](https://github.com/pascalorg/editor/pull/719))
- **Synchronized 2D viewer modes** ([#672](https://github.com/pascalorg/editor/pull/672))
- **Exports** — print-ready model exports ([#701](https://github.com/pascalorg/editor/pull/701), [#703](https://github.com/pascalorg/editor/pull/703)) and `exportFloorplanPdf` on the package entry ([#636](https://github.com/pascalorg/editor/pull/636))
- **Selection and inspector** — shared parameters editable on a homogeneous multi-selection ([#680](https://github.com/pascalorg/editor/pull/680)), session multi-select groups via Ctrl+G ([#571](https://github.com/pascalorg/editor/pull/571)), arbitrary maxes lifted from inspector dimension fields ([#721](https://github.com/pascalorg/editor/pull/721)), room-envelope height caps raised from 6 m to 20 m with the ceiling's level bound surfaced ([#642](https://github.com/pascalorg/editor/pull/642), [#650](https://github.com/pascalorg/editor/pull/650))
- **Host integration** — guided-tour hooks for targets, a success cue, snapping and camera hints ([#728](https://github.com/pascalorg/editor/pull/728), [#730](https://github.com/pascalorg/editor/pull/730)), `noPanel` sidebar tabs for stage-driving rail entries ([#641](https://github.com/pascalorg/editor/pull/641)), declarative `def.toolOptions` with a shared `ToolOptionsPanel` ([#750](https://github.com/pascalorg/editor/pull/750)), a `presentation.findInCatalog` opt-out for the action menu ([#756](https://github.com/pascalorg/editor/pull/756)), an exported `getWallHideState` ([#576](https://github.com/pascalorg/editor/pull/576)), a configurable `maxFps` render-loop cap ([#671](https://github.com/pascalorg/editor/pull/671)), and a single-writer ToolMode FSM for mode/tool ([#751](https://github.com/pascalorg/editor/pull/751))
- **Plugin ecosystem** — Mint ([#591](https://github.com/pascalorg/editor/pull/591)) and Streetscape ([#628](https://github.com/pascalorg/editor/pull/628)) plugins, the Bones structural X-ray plugin wired into the standalone editor ([#649](https://github.com/pascalorg/editor/pull/649)), and per-kind plugin inspector-card extensions on the floating inspector ([#667](https://github.com/pascalorg/editor/pull/667))
- **Light WebGPU preview and `/editor` scene redirect** ([#570](https://github.com/pascalorg/editor/pull/570))
- **Terrain and collaboration** — terrain elevation inherited by rooms, geometry builders and plugins ([#568](https://github.com/pascalorg/editor/pull/568)); realtime terrain and scene collaboration primitives repaired ([#573](https://github.com/pascalorg/editor/pull/573))
### 🐛 Fixes
- **Scene materials survive persistence** — custom materials are preserved across save, load, clone, fork and live sync, and collections are no longer dropped on MCP import ([#597](https://github.com/pascalorg/editor/pull/597)) by [@ShiroKSH](https://github.com/ShiroKSH); materials also carry through Load Build ([#729](https://github.com/pascalorg/editor/pull/729)), dangling library material refs re-resolve with the paint picker defaulting to Pascal ([#749](https://github.com/pascalorg/editor/pull/749)), and the material list has a stable test hook ([#724](https://github.com/pascalorg/editor/pull/724))
- **Deterministic wall junctions** — exactly-collinear walls miter in a stable order, so identical scenes produce identical geometry regardless of node iteration order ([#596](https://github.com/pascalorg/editor/pull/596)) by [@tomatotomata](https://github.com/tomatotomata); lean-to roof mitering fixed across connected runs ([#739](https://github.com/pascalorg/editor/pull/739)); `calculateLevelMiters` reachable through `core/wall` ([#732](https://github.com/pascalorg/editor/pull/732))
- **Wall drafts and previews** — plane-bound defaults restored for draft, panel and self-heal ([#630](https://github.com/pascalorg/editor/pull/630)), slab-sourced drafts never coerced to a ground base ([#702](https://github.com/pascalorg/editor/pull/702)), node tops stop lifting floor placements while batched walls stay pickable ([#686](https://github.com/pascalorg/editor/pull/686)), a hovered wall leaves its batch so it can outline ([#687](https://github.com/pascalorg/editor/pull/687)), wall item placement preview no longer drifts off the item ([#643](https://github.com/pascalorg/editor/pull/643)), draft preview geometry reused ([#550](https://github.com/pascalorg/editor/pull/550)), and wall load fixes for treatment fan-out, one CSG cut per wall, a capture draw leak and converter duplicates ([#762](https://github.com/pascalorg/editor/pull/762))
- **Hover, selection and hidden walls** — hidden walls stop swallowing pointer events ([#683](https://github.com/pascalorg/editor/pull/683)), stay ray targets while a door/window tool is active ([#689](https://github.com/pascalorg/editor/pull/689)), and lose to nearest-first hover against the furniture behind them ([#697](https://github.com/pascalorg/editor/pull/697)); opening MOVE drags ride the node's own wall with one undo entry per gesture ([#694](https://github.com/pascalorg/editor/pull/694))
- **Autosave and scene loading** — the autosave wipe guard stays armed after the scene loads ([#578](https://github.com/pascalorg/editor/pull/578), [#682](https://github.com/pascalorg/editor/pull/682)), a failed scene load shows an error with retry instead of an empty scene ([#759](https://github.com/pascalorg/editor/pull/759)), and null parent links in stored scenes are repaired so they pass authority validation ([#574](https://github.com/pascalorg/editor/pull/574))
- **History and dirty state** — history command ownership hardened ([#627](https://github.com/pascalorg/editor/pull/627)), placement drafts kept out of undo history ([#637](https://github.com/pascalorg/editor/pull/637)), a guarded dirty set with undo phantom sweep and animation split ([#758](https://github.com/pascalorg/editor/pull/758)), and elevator dirty tracking dropped ([#599](https://github.com/pascalorg/editor/pull/599))
- **Core robustness** — vertical scene canonicalization shared ([#626](https://github.com/pascalorg/editor/pull/626)), numeric sanitize diagnostics fail safe ([#615](https://github.com/pascalorg/editor/pull/615)), asset ids no longer depend on `crypto.randomUUID` ([#681](https://github.com/pascalorg/editor/pull/681)), and room surfaces reconcile after wall topology edits ([#624](https://github.com/pascalorg/editor/pull/624))
- **zod 4.5 compatibility** — `AnyNode` made discriminated-union-proof through bare-literal projection ([#745](https://github.com/pascalorg/editor/pull/745)), node metadata typed as a record instead of `z.json()` ([#746](https://github.com/pascalorg/editor/pull/746)), with the dependency range capped and then lifted to a working 4.5.x ([#744](https://github.com/pascalorg/editor/pull/744), [#747](https://github.com/pascalorg/editor/pull/747))
- **Sentry-reported crashes** — null WebGL context in the 3D viewer ([#455](https://github.com/pascalorg/editor/pull/455)), missing roof-segment trim in the ridge-vent geometry key ([#530](https://github.com/pascalorg/editor/pull/530)), and a `structuredClone` fallback where lingo is imported ([#577](https://github.com/pascalorg/editor/pull/577))
- **Claude Code plugin root** — the plugin root is `skills/` instead of the repository root, so installing `pascal-agent-skills@pascal` copies the two skill bundles and their MCP configuration instead of caching the whole monorepo and running `bun install` against the root lockfile ([#832](https://github.com/pascalorg/editor/pull/832))
- **Plugin integration** — plugin node kinds accepted by the scene save API ([#490](https://github.com/pascalorg/editor/pull/490)), hover/click subscriptions attached for kinds that register after mount ([#665](https://github.com/pascalorg/editor/pull/665)), inspector extension mode either/or with the regular controls ([#668](https://github.com/pascalorg/editor/pull/668)), and no empty plugin manager in an empty rail ([#741](https://github.com/pascalorg/editor/pull/741))
- **Roof, placement and level details** — roof and placement previews corrected ([#718](https://github.com/pascalorg/editor/pull/718)), wall editing and roof controls corrected ([#738](https://github.com/pascalorg/editor/pull/738)), slab room paint scoped to the clicked slab's level ([#592](https://github.com/pascalorg/editor/pull/592)), the function-tree category grid capped so the item list stays reachable ([#593](https://github.com/pascalorg/editor/pull/593)), level pill without ground-floor delete and no compass in studio ([#723](https://github.com/pascalorg/editor/pull/723)), stable level elevation smoothing ([#659](https://github.com/pascalorg/editor/pull/659)), isolation and solo no longer overwriting each other's layer masks ([#607](https://github.com/pascalorg/editor/pull/607)), millimetre notation respected in 3D labels ([#540](https://github.com/pascalorg/editor/pull/540)), and adjacent IFC wall assemblies no longer merged ([#603](https://github.com/pascalorg/editor/pull/603))
- **Experience fix pass** — Cmd/Ctrl+S saves instead of opening the browser's page-save dialog, plus placement, selection rotation, roof gable, stairs and capture fixes on three r186 ([#807](https://github.com/pascalorg/editor/pull/807))
### ⚡ Performance
- **Charter rows** — initial wall builds drain within the time budget (row 6) ([#800](https://github.com/pascalorg/editor/pull/800)), undo/redo invalidation scoped to changed geometry and cleared previews (row 7) ([#805](https://github.com/pascalorg/editor/pull/805)), ceiling undersides and slab bodies batched (row 16) ([#789](https://github.com/pascalorg/editor/pull/789))
- **Batching and instancing** — items, columns, doors and windows draw through BatchedMesh containers ([#760](https://github.com/pascalorg/editor/pull/760)), wall batching stays live in cutaway ([#761](https://github.com/pascalorg/editor/pull/761)), a level's walls are sewn into one mesh ([#608](https://github.com/pascalorg/editor/pull/608)), wall geometry is no longer rebuilt every frame ([#556](https://github.com/pascalorg/editor/pull/556)), ceiling corner brackets are instanced per level ([#784](https://github.com/pascalorg/editor/pull/784)), and animation mixers are skipped for items without clips ([#785](https://github.com/pascalorg/editor/pull/785))
- **Viewer passes** — outline reuses scene depth and renders masks from proxies ([#772](https://github.com/pascalorg/editor/pull/772)), pointer-event raycasts are cached with a camera-drag throttle ([#774](https://github.com/pascalorg/editor/pull/774)), wall cutout facing is cached instead of re-evaluated per camera move ([#775](https://github.com/pascalorg/editor/pull/775)), zone and overlay passes render from indexed roots ([#776](https://github.com/pascalorg/editor/pull/776)), viewer runtime ownership tightened ([#675](https://github.com/pascalorg/editor/pull/675)), and viewer stage mode sets stabilized ([#673](https://github.com/pascalorg/editor/pull/673))
- **Schema parsing** — lazy per-kind compiled node parsers behind a flag ([#748](https://github.com/pascalorg/editor/pull/748))
- **CLI download size** — the packed `@pascal-app/cli` drops from 107 MB to 65 MB by pruning build-only files from the portable runtime, with every dropped file printed so a future non-conventional asset is reported rather than shipped ([#838](https://github.com/pascalorg/editor/pull/838)), and then to 0.5 MB: the runtime ships as a checksummed release asset that the CLI fetches on first use instead of riding inside the npm tarball ([#845](https://github.com/pascalorg/editor/pull/845))
- **Instrumentation** — a `?perf` suite with real WebGPU timestamp queries, system tracks, action receipts and a DOM panel, at zero cost without the flag ([#755](https://github.com/pascalorg/editor/pull/755))
### 🧰 Tooling / infra
- **Release pipeline** — a stable bump now graduates a prerelease to its base version (`1.0.0-beta.N` → `1.0.0`) instead of producing `2.0.0` or publishing a beta to `latest` ([#837](https://github.com/pascalorg/editor/pull/837)); npm publishing moved to trusted publishing over GitHub OIDC after npm deprecated 2FA-bypass tokens ([#839](https://github.com/pascalorg/editor/pull/839), [#853](https://github.com/pascalorg/editor/pull/853), [#856](https://github.com/pascalorg/editor/pull/856)); trusted publishing for MCP and CLI ([#779](https://github.com/pascalorg/editor/pull/779)) with npm auth and OIDC repairs ([#816](https://github.com/pascalorg/editor/pull/816), [#817](https://github.com/pascalorg/editor/pull/817)); release package tag push fixed ([#565](https://github.com/pascalorg/editor/pull/565)); license metadata and `LICENSE` shipped in tarballs ([#732](https://github.com/pascalorg/editor/pull/732))
- **CI and tests** — the test suite runs in CI ([#548](https://github.com/pascalorg/editor/pull/548)), bun pinned to 1.3.14 in the quality gate ([#585](https://github.com/pascalorg/editor/pull/585)), `bun install` retried on transient GitHub 429s ([#674](https://github.com/pascalorg/editor/pull/674)), CI-sized timeout for the every-angle corner-joint sweep ([#693](https://github.com/pascalorg/editor/pull/693)) and a 30 s budget for rounded-opening CSG unions ([#764](https://github.com/pascalorg/editor/pull/764)), registry leaks across test files fixed ([#709](https://github.com/pascalorg/editor/pull/709)), the canopy regression matrix split into independent tests ([#790](https://github.com/pascalorg/editor/pull/790)), node source-system probes run from a package-local file with mocks established before any fiber/react import ([#792](https://github.com/pascalorg/editor/pull/792), [#793](https://github.com/pascalorg/editor/pull/793)), history probe modules resolved from the packages that depend on them under the isolated linker ([#828](https://github.com/pascalorg/editor/pull/828)), and wall batch tests driving the frame function directly ([#763](https://github.com/pascalorg/editor/pull/763))
- **Architecture** — runtime R3F evicted from the core barrel, with an architecture test to keep it out ([#692](https://github.com/pascalorg/editor/pull/692))
- **Platform** — Next.js 16.3 ([#579](https://github.com/pascalorg/editor/pull/579)), the Node runtime for standalone editor Vercel functions ([#580](https://github.com/pascalorg/editor/pull/580)), and `bun dev` working on Windows without dropping the PORT override ([#587](https://github.com/pascalorg/editor/pull/587))
- **Docs** — the docker workflow ([#590](https://github.com/pascalorg/editor/pull/590)), the plugin viewer appearance contract ([#625](https://github.com/pascalorg/editor/pull/625)), the MCP north convention in the garden-house template ([#584](https://github.com/pascalorg/editor/pull/584)), how `from_brief` binds a scene ([#595](https://github.com/pascalorg/editor/pull/595)), CLI package discoverability ([#606](https://github.com/pascalorg/editor/pull/606)), a security policy and code of conduct ([#575](https://github.com/pascalorg/editor/pull/575)), stale `NEXT_PUBLIC_GOOGLE_MAPS_API_KEY` docs dropped ([#589](https://github.com/pascalorg/editor/pull/589)), and changelog credit for the junction-determinism and scene-materials fixes ([#598](https://github.com/pascalorg/editor/pull/598))
- **Bones plugin pin** — the pinned structural X-ray plugin advanced 26 times across this window: camera-proof X-ray, dollhouse and bl
Repository README (current repository documentation, not release changes)
# Pascal Editor
An open-source, local-first 3D building editor built with React Three Fiber and
WebGPU. Run it in the browser or from the CLI, and connect AI agents through MCP.
[](LICENSE)
[](https://www.npmjs.com/package/@pascal-app/core)
[](https://www.npmjs.com/package/@pascal-app/viewer)
[](https://www.npmjs.com/package/@pascal-app/cli)
[](https://discord.gg/XRKsDcpqgS)
[](https://x.com/pascal_app)
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:
```bash
npx @pascal-app/cli editor
```
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`. The npm
package holds the CLI and that MCP service; the web editor runtime is downloaded once per
version on the first command that starts the editor and verified against a digest published
inside the package. Configure an agent to launch `pascal mcp connect`, which needs neither
the editor process nor that download. Install the `pascal` command with
`npm install --global @pascal-app/cli`. See [Run Pascal locally](https://editor.pascal.app/docs/developers/local-editor)
for pnpm/Bun commands, project management, MCP setup, updates, storage paths, and
troubleshooting.
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
[](https://skills.sh/pascalorg/editor)
Install Pascal's public agent workflows from this repository with [skills.sh](https://skills.sh):
```bash
npx skills add pascalorg/editor \
--skill pascal-3d \
--skill furniture-fit
```
Claude Code users can install the same canonical skill source as a plugin:
```text
/plugin marketplace add pascalorg/editor
/plugin install pascal-agent-skills@pascal
```
The Claude plugin also supplies the local `pascal mcp connect` server. Install and start the Pascal CLI first, and keep `pascal` on the `PATH` used to launch Claude Code. This local connector needs no Pascal account or API key and does not upload projects automatically. Its plugin root is this repository's `skills/` directory, so an install copies only the skill bundles and their plugin metadata rather than the repository.
The plugin bundles two servers: the local `pascal` connector above and a hosted `pascal-hosted` server for `https://editor.pascal.app/api/mcp`, which prompts for an optional Pascal API key at enable time and stores it in the OS keychain. Leave the key empty to run local-only.
Claude Code 2.1.258 loads both the user-scoped `pascal` server created by `pascal mcp setup claude` and the plugin-provided server. Remove the manual entry before reloading or restarting Claude Code so only the plugin owns the connection lifecycle:
```bash
claude mcp remove --scope user pascal
```
Use `/mcp` to remove or disable any project- or local-scoped Pascal connection too. Leaving both connections active violates the one-active-agent-client-per-local-service requirement. When the intended project is hosted in a Pascal account or organization, disable the plugin-provided local server in `/mcp` and configure the hosted endpoint from the skill setup guide instead.
Codex users can install the same plugin from the repository marketplace:
```bash
codex plugin marketplace add pascalorg/editor
codex plugin add pascal-agent-skills@pascal
```
OpenClaw installation becomes available after the skills are published under Pascal's ClawHub publisher. See [skills/README.md](skills/README.md) for the owner-qualified install and verification commands.
[`pascal-3d`](skills/pascal-3d/SKILL.md) covers safe local or hosted MCP setup and verified scene work. [`furniture-fit`](skills/furniture-fit/SKILL.md) produces a bounded, evidence-based footprint assessment without claiming unsupported height, swing, or delivery checks. See [skills/README.md](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.
These workflows require a connected Pascal MCP server for their tool-backed actions. An OpenAI directory submission must therefore use **With MCP** and submit the production hosted MCP endpoint together with the skills. The repository package does not prove that the endpoint, OAuth flow, reviewer credentials, domain verification, or portal scan is ready for review.
### MCP Registry
[`server.json`](server.json) is Pascal's manifest for the official MCP Registry. Its
version tracks the hosted MCP implementation independently of the npm package version.
Pull requests validate the manifest and production endpoint. A Pascal organization
owner publishes an approved version from `main` with the official registry publisher.
## 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 ``. Capture sessions are an
optional extension shipped inside those packages as the `@pascal-app/core/capture` and
`@pascal-app/viewer/capture` subpaths:
```bash
npm install @pascal-app/core @pascal-app/viewer @pascal-app/editor @pascal-app/nodes
```
```typescript
import { loadPlugin } from '@pascal-app/core'
import { builtinPlugin } from '@pascal-app/nodes'
await loadPlugin(builtinPlugin)
```
See the [`@pascal-app/viewer` quick start](packages/viewer/README.md#usage) 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:
```
editor/
├── apps/
│ └── editor/ # Next.js application
├── packages/
│ ├── core/ # Schemas, scene state, registry contracts, capture contracts
│ ├── viewer/ # 3D rendering runtime, shared systems, capture runtime
│ ├── editor/ # Editing tools and UI components
│ ├── nodes/ # Built-in node definitions, renderers, and systems
│ ├── cli/ # Persistent local editor installer and process manager
│ ├── mcp/ # Model Context Protocol server and scene storage
│ └── ui/ # Shared UI components
```
### Separation of Concerns
| Package | Responsibility |
|---------|---------------|
| **@pascal-app/core** | Node schemas, scene state (Zustand), registry contracts, spatial queries, and event bus. `core/capture` adds versioned capture manifests, normalized streams, and transport-neutral static/live sources |
| **@pascal-app/viewer** | 3D rendering via React Three Fiber, shared render systems, default camera/controls, and post-processing. `viewer/capture` adds the capture runtime and reference model, device-motion, point-cloud, and surface-mesh 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:**
```typescript
// Subscribe to state changes (React component)
const nodes = useScene((state) => state.nodes)
const levelId = useViewer((state) => state.selection.levelId)
const activeTool = useEditor((state) => state.tool)
// Access state outside React (callbacks, systems)
const node = useScene.getState().nodes[id]
useViewer.getState().setSelection({ levelId: 'level_123' })
```
---
## Core Concepts
### Nodes
Nodes are the data primitives that describe the 3D scene. All nodes extend `BaseNode`:
```typescript
BaseNode {
id: string // Auto-generated with type prefix (e.g., "wall_abc123")
type: string // Discriminator for type-safe handling
parentId: string | null // Parent node reference
visible: boolean
camera?: Camera // Optional saved camera position
metadata?: JSON // Arbitrary metadata (e.g., { isTransient: true })
}
```
**Node Hierarchy:**
```
Site
└── Building
└── Level
├── Wall → Item (doors, windows)
├── Slab
├── Ceiling → Item (lights)
├── Roof
├── Zone
├── Scan (3D reference)
└── Guide (2D reference)
```
Nodes are stored in a **flat dictionary** (`Record`), 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`:
```typescript
useScene.getState() = {
nodes: Record, // All nodes
rootNodeIds: string[], // Top-level nodes (sites)
dirtyNodes: Set, // Nodes pending system updates
createNode(node, parentId),
updateNode(id, updates),
deleteNode(id),
}
```
**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:
```typescript
sceneRegistry = {
nodes: Map, // ID → 3D object
byType: {
wall: Set,
item: Set,
zone: Set,
// ...
}
}
```
Renderers register their refs using the `useRegistry` hook:
```tsx
const ref = useRef(null!)
useRegistry(node.id, 'wall', ref)
```
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:
```
SceneRenderer
└── NodeRenderer (dispatches by type)
├── BuildingRenderer
├── LevelRenderer
├── WallRenderer
├── SlabRenderer
├── ZoneRenderer
├── ItemRenderer
└── ...
```
**Pattern:**
1. Renderer creates a placeholder mesh/group
2. Registers it with `useRegistry`
3. Systems update geometry based on node data
Example (simplified):
```tsx
const WallRenderer = ({ node }) => {
const ref = useRef(null!)
useRegistry(node.id, 'wall', ref)
return (
{/* Replaced by WallSystem */}
{node.children.map(id => )}
)
}
```
---
### 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:**
```typescript
useFrame(() => {
for (const id of dirtyNodes) {
const obj = sceneRegistry.nodes.get(id)
const node = useScene.getState().nodes[id]
// Update geometry, transforms, etc.
updateGeometry(obj, node)
dirtyNodes.delete(id)
}
})
```
---
### 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.
```typescript
// Automatic: createNode, updateNode, deleteNode mark nodes dirty
useScene.getState().updateNode(wallId, { thickness: 0.2 })
// → wallId added to dirtyNodes
// → WallSystem regenerates geometry next frame
// → wallId removed from dirtyNodes
```
**Manual marking:**
```typescript
useScene.getState().dirtyNodes.add(wallId)
```
---
### Event Bus
Inter-component communication uses a typed event emitter (mitt):
```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
}
```
---
### Spatial Grid Manager
Handles collision detection and placement validation:
```typescript
spatialGridManager.canPlaceOnFloor(levelId, position, dimensions, rotation)
spatialGridManager.canPlaceOnWall(wallId, t, height, dimensions)
spatialGridManager.getSlabElevationAt(levelId, x, z)
```
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:
```
Site → Building → Level → Zone → Items
```
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
```
User Action (click, drag)
↓
Tool Handler
↓
useScene.createNode() / updateNode()
↓
Node added/updated in store
Node marked dirty
↓
React re-renders NodeRenderer
useRegistry() registers 3D object
↓
System detects dirty node (useFrame)
Updates geometry via sceneRegistry
Clears dirty flag
```
---
## 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](https://editor.pascal.app/docs/developers/plugins): the `Plugin` shape, panel contributions, discovery, lifecycle, and what's in/out of v1.
- **Worked example** — [`pascalorg/plugin-trees`](https://github.com/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:
```bash
# Install dependencies
bun install
# Run development server (builds packages + starts editor with watch mode)
bun dev
# This will:
# 1. Build @pascal-app/core and @pascal-app/viewer
# 2. Start watching both packages for changes
# 3. Start the Next.js editor dev server
# Open http://localhost:3002
```
**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
```bash
# Build all packages
turbo build
# Build specific package
turbo build --filter=@pascal-app/core
```
### Publishing Packages
Releases run from `.github/workflows/release.yml` (`workflow_dispatch`, with
`package`, `bump`, and `dry-run` inputs). The workflow bumps versions, rewrites
the internal `@pascal-app/*` ranges, builds, publishes in dependency order
(`core` → `viewer` → `editor` → `nodes` → `mcp` → `ifc-converter` → `cli`),
then commits the release and pushes one tag per package. A dry run validates
the builds without touching the registry.
---
## 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](CONTRIBUTING.md) for setup, code style and the PR flow.
- New node kinds and sidebar panels ship as [plugins](https://editor.pascal.app/docs/developers/plugins) rather than edits to the built-ins — [`pascalorg/plugin-trees`](https://github.com/pascalorg/plugin-trees) is a worked example
- Questions and ideas go to [Discussions](https://github.com/pascalorg/editor/discussions); reproducible bugs go to [Issues](https://github.com/pascalorg/editor/issues)
- Participation is covered by our [Code of Conduct](CODE_OF_CONDUCT.md)
- Security problems go to [SECURITY.md](SECURITY.md), not a public issue
---
## Contributors
---