Pascal Editor 是基于 React Three Fiber 和 WebGPU 的开源 3D 建筑编辑器,支持本地 CLI 启动和 MCP 协议,AI Agent 可通过标准化接口直接操作 3D 建模工作流。
一个开源、本地优先的 3D 建筑编辑器,基于 React Three Fiber 和 WebGPU 构建。可在浏览器中运行或通过 CLI 启动,并通过 MCP 连接 AI 智能体。
本地运行编辑器
Node.js 22.13 或更高版本可直接创建一个持久化的本地 Pascal 安装,无需克隆本仓库:
npx @pascal-app/cli editor
CLI 在后台启动编辑器和经过身份验证的 MCP 服务,选择无冲突的回环端口,并将项目存储在 ~/.pascal/data/pascal.db。配置一个 AI 智能体来启动 pascal mcp connect。关于 pnpm/Bun 命令、项目管理、MCP 设置、更新、存储路径和故障排除,详见 Run Pascal locally。npm 发布版是下方描述的旧版运行时;当某个任务需要新的只读家具候选检查功能时,使用经过验证的 GitHub 预览版。
npm beta 标签当前解析为 @pascal-app/cli@1.0.0-beta.1,早于本仓库中的只读家具候选输入和托管 AI 智能体 claim/status 命令。如需在这些 npm 发布前使用相关能力,请安装从 commit 5dabbc3b56109c9f79dc8a378443a4c520d9ee0a 构建的经过验证的 GitHub 预发布版:
PASCAL_PREVIEW_VERSION='1.0.0-beta.2.status.0'
PASCAL_PREVIEW_PREFIX="${XDG_DATA_HOME:-$HOME/.local/share}/pascal-preview"
PASCAL_PREVIEW_DOWNLOAD="$(mktemp -d)"
cd "$PASCAL_PREVIEW_DOWNLOAD"
curl --fail --location --remote-name \
"https://github.com/pascalorg/editor/releases/download/cli-v1.0.0-beta.2-status.0/pascal-app-cli-${PASCAL_PREVIEW_VERSION}.tgz"
curl --fail --location --remote-name \
"https://github.com/pascalorg/editor/releases/download/cli-v1.0.0-beta.2-status.0/SHA256SUMS.txt"
# macOS
shasum -a 256 -c SHA256SUMS.txt
# Linux: use `sha256sum -c SHA256SUMS.txt` instead.
npm install --global --prefix "$PASCAL_PREVIEW_PREFIX" --ignore-scripts \
"./pascal-app-cli-${PASCAL_PREVIEW_VERSION}.tgz"
export PATH="$PASCAL_PREVIEW_PREFIX/bin:$PATH"
pascal --version
pascal update --version "$PASCAL_PREVIEW_VERSION"
pascal editor --no-open
# For an existing hosted autonomous-agent key:
PASCAL_API_KEY='sk_live_...' pascal agent claim
PASCAL_API_KEY='sk_live_...' pascal agent status --json
预期归档 SHA-256 为 15628baeeb174fb7786a1643db08f0554bf6d18afaaa3979f01922c5cd40019a。同版本更新命令会安装并激活该 CLI 绑定的运行时,必要时重启正在运行的旧服务。保持现有 PASCAL_HOME 不变,以使存储的项目保留在同一数据目录中;单独运行 pascal editor 会复用任何健康的服务(包括旧版本)。在 Claude 插件提供的连接器之前,将预览版前缀保留在 AI 智能体主机的 PATH 上;也可运行 pascal mcp setup claude 或 pascal mcp setup codex 安装到其他路径,或手动配置 pascal mcp connect。pascal agent claim 会打开一个预填充的 15 分钟人工交接页面;pascal agent status 用于验证密钥并报告受限的声明状态。两个命令都不会存储或打印托管密钥。此 GitHub 预发布版不是 npm 版本。
每个本地 CLI 服务使用一个活动的 AI 智能体客户端。独立的本地 HTTP 运行时在线程间共享活动场景状态;如需独立的并发工作,请使用独立的 PASCAL_HOME 目录和服务进程。
通过 skills.sh 从本仓库安装 Pascal 的公共 AI 智能体工作流:
npx skills add pascalorg/editor \
--skill pascal-3d \
--skill furniture-fit
Claude Code 用户可将同一规范技能源作为插件安装:
/plugin marketplace add pascalorg/editor
/plugin install pascal-agent-skills@pascal
Claude 插件还提供了本地 pascal mcp connect 服务器。请先安装并启动 Pascal CLI,并保持 Pascal 在启动 Claude Code 所使用的 PATH 上。此本地连接器无需 Pascal 账户或 API 密钥,也不会自动上传项目。
Claude Code 2.1.258 会同时加载由 pascal mcp setup claude 创建的用户级 Pascal 服务器和插件提供的服务器。重新加载或重启 Claude Code 前,请先移除手动创建的条目,使插件全权管理连接生命周期:
claude mcp remove --scope user pascal
使用 /mcp 同样移除或禁用任何项目级或本地级的 Pascal 连接。同时保留两个连接会违反每个本地服务一个活动 AI 智能体客户端的要求。当目标项目托管在 Pascal 账户或组织中时,请在 /mcp 中禁用插件提供的本地服务器,并按照技能设置指南配置托管端点。
Codex 用户可从仓库 marketplace 安装同一插件:
codex plugin marketplace add pascalorg/editor
codex plugin add pascal-agent-skills@pascal
待技能在 Pascal 的 ClawHub 发布者下发布后,OpenClaw 安装将变为可用。参见 skills/README.md 获取所有者限定的安装和验证命令。
pascal-3d 涵盖安全的本地或托管 MCP 设置及经过验证的场景工作。furniture-fit 生成有界且基于证据的占用评估,不声称支持高度、摆动或配送检查。包详情和验证方法见 skills/README.md。
这些工作流在使用可选字段前会检查已连接 MCP 工具的模式。本仓库中存在的功能可能不存在于已安装的旧版或托管版中;AI 智能体应报告受限的支持结果,而不是假定源码中的输入均可用。
这些工作流需要连接 Pascal MCP 服务器以执行工具支持的操作。因此,OpenAI 目录提交必须使用 With MCP 并同时提交生产托管 MCP 端点以及相关技能。仓库包本身不能证明端点、OAuth 流程、审核员凭证、域名验证或门户扫描已就绪。
server.json 是 Pascal 官方 MCP Registry 的清单文件。其版本独立于 npm 包版本追踪托管 MCP 实现。Pull request 会对清单和生产端点进行验证。Pascal 组织所有者通过官方注册发布者从 main 分支发布已批准的版本。
使用已发布包
viewer 运行时和内置节点定义是独立的包。先安装完整的内置 viewer 集,然后在挂载 <Viewer> 之前加载一次内置插件。捕获会话是一个可选的传输层扩展:
npm install @pascal-app/core @pascal-app/viewer @pascal-app/editor @pascal-app/nodes
npm install @pascal-app/capture-protocol @pascal-app/capture-viewer
import { loadPlugin } from '@pascal-app/core'
import { builtinPlugin } from '@pascal-app/nodes'
await loadPlugin(builtinPlugin)
参见 @pascal-app/viewer quick start 的 React 示例。
仓库架构
这是一个 Turborepo 单仓库,包含可复用的编辑器包、独立应用以及分发它的 CLI:
Viewer 负责用合理的默认设置渲染场景。Editor 则在此基础上扩展了交互工具、选择管理和编辑能力。
每个包都有自己的 Zustand store 来管理状态:
// 订阅状态变更(React 组件)
const nodes = useScene((state) => state.nodes)
const levelId = useViewer((state) => state.selection.levelId)
const activeTool = useEditor((state) => state.tool)
// 在 React 外部访问状态(回调、systems)
const node = useScene.getState().nodes[id]
useViewer.getState().setSelection({ levelId: 'level_123' })
Node 是描述 3D 场景的数据原语。所有节点都继承自 BaseNode:
BaseNode {
id: string // 自动生成,带类型前缀(如 "wall_abc123")
type: string // 用于类型安全处理的判别器
parentId: string | null // 父节点引用
visible: boolean
camera?: Camera // 可选的已保存相机位置
metadata?: JSON // 任意元数据(如 { isTransient: true })
}
Site
└── Building
└── Level
├── Wall → Item(门、窗)
├── Slab
├── Ceiling → Item(灯具)
├── Roof
├── Zone
├── Scan(3D 参考)
└── Guide(2D 参考)
节点存储在扁平字典中(Record<id, Node>),而非嵌套树。父子关系通过 parentId 和 children 数组定义。
场景由 @pascal-app/core 中的 Zustand store 管理:
useScene.getState() = {
nodes: Record<id, AnyNode>, // 所有节点
rootNodeIds: string[], // 顶层节点(sites)
dirtyNodes: Set<string>, // 待系统更新的节点
createNode(node, parentId),
updateNode(id, updates),
deleteNode(id),
}
Persist - 保存到 IndexedDB(排除临时节点)
Temporal(Zundo)- 支持 50 步历史的撤销/重做
注册表通过节点 ID 到 Three.js 对象的映射实现快速查找:
sceneRegistry = {
nodes: Map<id, Object3D>, // ID → 3D 对象
byType: {
wall: Set<id>,
item: Set<id>,
zone: Set<id>,
// ...
}
}
渲染器使用 useRegistry hook 注册其引用:
const ref = useRef<Mesh>(null!)
useRegistry(node.id, 'wall', ref)
这使得系统可以直接访问 3D 对象,而无需遍历场景图。
渲染器是 React 组件,为每种节点类型创建 Three.js 对象:
SceneRenderer
└── NodeRenderer(按类型分发)
├── BuildingRenderer
├── LevelRenderer
├── WallRenderer
├── SlabRenderer
├── ZoneRenderer
├── ItemRenderer
└── ...
渲染器创建占位网格/Group → 通过 useRegistry() 注册 3D 对象 → 系统根据节点数据更新几何体
示例(简化版):
const WallRenderer = ({ node }) => {
const ref = useRef<Mesh>(null!)
useRegistry(node.id, 'wall', ref)
return (
<mesh ref={ref}>
<boxGeometry args={[0, 0, 0]} /> {/* 由 WallSystem 替换 */}
<meshStandardMaterial />
{node.children.map(id => <NodeRenderer key={id} nodeId={id} />)}
</mesh>
)
}
系统是运行在渲染循环中(useFrame)的 React 组件,用于更新几何体和变换。它们处理 store 中标记为 dirty 的节点。
核心系统(位于 @pascal-app/core):
Viewer 系统(位于 @pascal-app/viewer):
useFrame(() => {
for (const id of dirtyNodes) {
const obj = sceneRegistry.nodes.get(id)
const node = useScene.getState().nodes[id]
// 更新几何体、变换等
updateGeometry(obj, node)
dirtyNodes.delete(id)
}
})
当节点变更时,会在 useScene.getState().dirtyNodes 中被标记为 dirty。系统每帧检查这个集合,只对 dirty 节点重新计算几何体。
// 自动触发:createNode、updateNode、deleteNode 会标记节点为 dirty
useScene.getState().updateNode(wallId, { thickness: 0.2 })
// → wallId 被加入 dirtyNodes
// → WallSystem 下一帧重新生成几何体
// → wallId 从 dirtyNodes 中移除
useScene.getState().dirtyNodes.add(wallId)
组件间通信使用类型化事件发射器(mitt):
// 节点事件
emitter.on('wall:click', (event) => { ... })
emitter.on('item:enter', (event) => { ... })
emitter.on('zone:context-menu', (event) => { ... })
// 网格事件(背景)
emitter.on('grid:click', (event) => { ... })
// 事件载荷
NodeEvent {
node: AnyNode
position: [x, y, z]
localPosition: [x, y, z]
normal?: [x, y, z]
stopPropagation: () => void
}
处理碰撞检测和放置验证:
spatialGridManager.canPlaceOnFloor(levelId, position, dimensions, rotation)
spatialGridManager.canPlaceOnWall(wallId, t, height, dimensions)
spatialGridManager.getSlabElevationAt(levelId, x, z)
物品放置工具使用这些方法来验证位置并计算板面标高。
编辑器在 viewer 基础上扩展了以下功能:
工具通过工具栏激活,处理特定操作的用户输入:
SelectTool - 选择和操作
WallTool - 绘制墙体
ZoneTool - 创建区域
ItemTool - 放置家具/设备
SlabTool - 创建楼板
编辑器使用自定义选择管理器,支持层级导航:
Site → Building → Level → Zone → Items
每个深度级别都有其独立的悬停/点击行为选择策略。
ZoneSystem - 根据级别模式控制区域可见性
带节点聚焦功能的自定义相机控制
用户操作(点击、拖拽)
↓
工具处理器
↓
useScene.createNode() / updateNode()
↓
节点在 store 中被添加/更新
节点被标记为 dirty
↓
React 重新渲染 NodeRenderer
useRegistry() 注册 3D 对象
↓
系统检测到 dirty 节点(useFrame)
通过 sceneRegistry 更新几何体
清除 dirty 标记
编辑器支持扩展:插件通过与内置系统相同的 Plugin 清单发布节点类型(schema、3D/2D 渲染、放置工具、检查器参数化)和左侧面板——没有独立的内部 API。
开发者指南 — 创建插件:Plugin 形状、面板贡献、发现机制、生命周期,以及 v1 的包含/排除范围。
实战示例 — pascalorg/plugin-trees:一个独立插件,包含程序化树木、花卉、草地以及预设面板。以此为起点进行克隆。
React 19 + Next.js 16
Three.js(WebGPU 渲染器)
React Three Fiber + Drei
Zustand(状态管理)
Zod(schema 验证)
three-bvh-csg(布尔几何运算)
Turborepo(monorepo 管理)
Bun(包管理器)
从根目录运行开发服务器以启用所有包的热重载:
# 安装依赖
bun install
# 运行开发服务器(构建包 + 启动带监听模式的编辑器)
bun dev
# 这将:
# 1. 构建 @pascal-app/core 和 @pascal-app/viewer
# 2. 开始监听两个包的变更
# 3. 启动 Next.js 编辑器开发服务器
# 打开 http://localhost:3002
重要提示:始终从根目录运行 bun dev,以确保包监听器正在运行。这样在编辑 packages/core/src/ 或 packages/viewer/src/ 中的文件时就可以热重载。
# 构建所有包
turbo build
# 构建特定包
turbo build --filter=@pascal-app/core
# 构建包
turbo build --filter=@pascal-app/core --filter=@pascal-app/viewer
# 发布到 npm
npm publish --workspace=@pascal-app/core --access public
npm publish --workspace=@pascal-app/viewer --access public
欢迎提交 bug 修复、功能、文档和想法。先阅读 CONTRIBUTING.md 了解设置、代码风格和 PR 流程。
新的节点类型和侧边栏面板作为插件发布,而不是修改内置系统——pascalorg/plugin-trees 是一个实战示例。
问题和想法请到 Discussions;可复现的 bug 请到 Issues。
参与项目需遵守我们的行为准则。
安全问题请到 SECURITY.md,不要发公开 issue。