[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-3860":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":18,"stars30d":19,"stars90d":16,"forks30d":16,"starsTrendScore":20,"compositeScore":21,"rankGlobal":10,"rankLanguage":10,"license":22,"archived":23,"fork":23,"defaultBranch":24,"hasWiki":23,"hasPages":23,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":26,"readmeContent":27,"aiSummary":28,"trendingCount":16,"starSnapshotCount":16,"syncStatus":29,"lastSyncTime":30,"discoverSource":31},3860,"editor","pascalorg\u002Feditor","pascalorg","Create and share 3D architectural projects.","https:\u002F\u002Feditor.pascal.app",null,"TypeScript",16650,2228,103,23,0,67,256,1274,325,120,"MIT License",false,"main",[],"2026-06-12 04:00:19","# Pascal Editor\n\nA 3D building editor built with React Three Fiber and WebGPU.\n\n[![MIT License](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT-blue.svg)](LICENSE)\n[![npm @pascal-app\u002Fcore](https:\u002F\u002Fimg.shields.io\u002Fnpm\u002Fv\u002F@pascal-app\u002Fcore?label=%40pascal-app%2Fcore)](https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@pascal-app\u002Fcore)\n[![npm @pascal-app\u002Fviewer](https:\u002F\u002Fimg.shields.io\u002Fnpm\u002Fv\u002F@pascal-app\u002Fviewer?label=%40pascal-app%2Fviewer)](https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@pascal-app\u002Fviewer)\n[![Discord](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FDiscord-Join%20Server-5865F2?logo=discord&logoColor=white)](https:\u002F\u002Fdiscord.gg\u002FSaBRA9t2)\n[![X (Twitter)](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Ffollow-%40pascal__app-black?logo=x&logoColor=white)](https:\u002F\u002Fx.com\u002Fpascal_app)\n\nhttps:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F8b50e7cf-cebe-4579-9cf3-8786b35f7b6b\n\n\n\n## Repository Architecture\n\nThis is a Turborepo monorepo with three main packages:\n\n```\neditor-v2\u002F\n├── apps\u002F\n│   └── editor\u002F          # Next.js application\n├── packages\u002F\n│   ├── core\u002F            # Schema definitions, state management, systems\n│   └── viewer\u002F          # 3D rendering components\n```\n\n### Separation of Concerns\n\n| Package | Responsibility |\n|---------|---------------|\n| **@pascal-app\u002Fcore** | Node schemas, scene state (Zustand), systems (geometry generation), spatial queries, event bus |\n| **@pascal-app\u002Fviewer** | 3D rendering via React Three Fiber, default camera\u002Fcontrols, post-processing |\n| **apps\u002Feditor** | UI components, tools, custom behaviors, editor-specific systems |\n\nThe **viewer** renders the scene with sensible defaults. The **editor** extends it with interactive tools, selection management, and editing capabilities.\n\n### Stores\n\nEach package has its own Zustand store for managing state:\n\n| Store | Package | Responsibility |\n|-------|---------|----------------|\n| `useScene` | `@pascal-app\u002Fcore` | Scene data: nodes, root IDs, dirty nodes, CRUD operations. Persisted to IndexedDB with undo\u002Fredo via Zundo. |\n| `useViewer` | `@pascal-app\u002Fviewer` | Viewer state: current selection (building\u002Flevel\u002Fzone IDs), level display mode (stacked\u002Fexploded\u002Fsolo), camera mode. |\n| `useEditor` | `apps\u002Feditor` | Editor state: active tool, structure layer visibility, panel states, editor-specific preferences. |\n\n**Access patterns:**\n\n```typescript\n\u002F\u002F Subscribe to state changes (React component)\nconst nodes = useScene((state) => state.nodes)\nconst levelId = useViewer((state) => state.selection.levelId)\nconst activeTool = useEditor((state) => state.tool)\n\n\u002F\u002F Access state outside React (callbacks, systems)\nconst node = useScene.getState().nodes[id]\nuseViewer.getState().setSelection({ levelId: 'level_123' })\n```\n\n---\n\n## Core Concepts\n\n### Nodes\n\nNodes are the data primitives that describe the 3D scene. All nodes extend `BaseNode`:\n\n```typescript\nBaseNode {\n  id: string              \u002F\u002F Auto-generated with type prefix (e.g., \"wall_abc123\")\n  type: string            \u002F\u002F Discriminator for type-safe handling\n  parentId: string | null \u002F\u002F Parent node reference\n  visible: boolean\n  camera?: Camera         \u002F\u002F Optional saved camera position\n  metadata?: JSON         \u002F\u002F Arbitrary metadata (e.g., { isTransient: true })\n}\n```\n\n**Node Hierarchy:**\n\n```\nSite\n└── Building\n    └── Level\n        ├── Wall → Item (doors, windows)\n        ├── Slab\n        ├── Ceiling → Item (lights)\n        ├── Roof\n        ├── Zone\n        ├── Scan (3D reference)\n        └── Guide (2D reference)\n```\n\nNodes are stored in a **flat dictionary** (`Record\u003Cid, Node>`), not a nested tree. Parent-child relationships are defined via `parentId` and `children` arrays.\n\n---\n\n### Scene State (Zustand Store)\n\nThe scene is managed by a Zustand store in `@pascal-app\u002Fcore`:\n\n```typescript\nuseScene.getState() = {\n  nodes: Record\u003Cid, AnyNode>,  \u002F\u002F All nodes\n  rootNodeIds: string[],       \u002F\u002F Top-level nodes (sites)\n  dirtyNodes: Set\u003Cstring>,     \u002F\u002F Nodes pending system updates\n\n  createNode(node, parentId),\n  updateNode(id, updates),\n  deleteNode(id),\n}\n```\n\n**Middleware:**\n- **Persist** - Saves to IndexedDB (excludes transient nodes)\n- **Temporal** (Zundo) - Undo\u002Fredo with 50-step history\n\n---\n\n### Scene Registry\n\nThe registry maps node IDs to their Three.js objects for fast lookup:\n\n```typescript\nsceneRegistry = {\n  nodes: Map\u003Cid, Object3D>,    \u002F\u002F ID → 3D object\n  byType: {\n    wall: Set\u003Cid>,\n    item: Set\u003Cid>,\n    zone: Set\u003Cid>,\n    \u002F\u002F ...\n  }\n}\n```\n\nRenderers register their refs using the `useRegistry` hook:\n\n```tsx\nconst ref = useRef\u003CMesh>(null!)\nuseRegistry(node.id, 'wall', ref)\n```\n\nThis allows systems to access 3D objects directly without traversing the scene graph.\n\n---\n\n### Node Renderers\n\nRenderers are React components that create Three.js objects for each node type:\n\n```\nSceneRenderer\n└── NodeRenderer (dispatches by type)\n    ├── BuildingRenderer\n    ├── LevelRenderer\n    ├── WallRenderer\n    ├── SlabRenderer\n    ├── ZoneRenderer\n    ├── ItemRenderer\n    └── ...\n```\n\n**Pattern:**\n1. Renderer creates a placeholder mesh\u002Fgroup\n2. Registers it with `useRegistry`\n3. Systems update geometry based on node data\n\nExample (simplified):\n```tsx\nconst WallRenderer = ({ node }) => {\n  const ref = useRef\u003CMesh>(null!)\n  useRegistry(node.id, 'wall', ref)\n\n  return (\n    \u003Cmesh ref={ref}>\n      \u003CboxGeometry args={[0, 0, 0]} \u002F>  {\u002F* Replaced by WallSystem *\u002F}\n      \u003CmeshStandardMaterial \u002F>\n      {node.children.map(id => \u003CNodeRenderer key={id} nodeId={id} \u002F>)}\n    \u003C\u002Fmesh>\n  )\n}\n```\n\n---\n\n### Systems\n\nSystems are React components that run in the render loop (`useFrame`) to update geometry and transforms. They process **dirty nodes** marked by the store.\n\n**Core Systems (in `@pascal-app\u002Fcore`):**\n\n| System | Responsibility |\n|--------|---------------|\n| `WallSystem` | Generates wall geometry with mitering and CSG cutouts for doors\u002Fwindows |\n| `SlabSystem` | Generates floor geometry from polygons |\n| `CeilingSystem` | Generates ceiling geometry |\n| `RoofSystem` | Generates roof geometry |\n| `ItemSystem` | Positions items on walls, ceilings, or floors (slab elevation) |\n\n**Viewer Systems (in `@pascal-app\u002Fviewer`):**\n\n| System | Responsibility |\n|--------|---------------|\n| `LevelSystem` | Handles level visibility and vertical positioning (stacked\u002Fexploded\u002Fsolo modes) |\n| `ScanSystem` | Controls 3D scan visibility |\n| `GuideSystem` | Controls guide image visibility |\n\n**Processing Pattern:**\n```typescript\nuseFrame(() => {\n  for (const id of dirtyNodes) {\n    const obj = sceneRegistry.nodes.get(id)\n    const node = useScene.getState().nodes[id]\n\n    \u002F\u002F Update geometry, transforms, etc.\n    updateGeometry(obj, node)\n\n    dirtyNodes.delete(id)\n  }\n})\n```\n\n---\n\n### Dirty Nodes\n\nWhen 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.\n\n```typescript\n\u002F\u002F Automatic: createNode, updateNode, deleteNode mark nodes dirty\nuseScene.getState().updateNode(wallId, { thickness: 0.2 })\n\u002F\u002F → wallId added to dirtyNodes\n\u002F\u002F → WallSystem regenerates geometry next frame\n\u002F\u002F → wallId removed from dirtyNodes\n```\n\n**Manual marking:**\n```typescript\nuseScene.getState().dirtyNodes.add(wallId)\n```\n\n---\n\n### Event Bus\n\nInter-component communication uses a typed event emitter (mitt):\n\n```typescript\n\u002F\u002F Node events\nemitter.on('wall:click', (event) => { ... })\nemitter.on('item:enter', (event) => { ... })\nemitter.on('zone:context-menu', (event) => { ... })\n\n\u002F\u002F Grid events (background)\nemitter.on('grid:click', (event) => { ... })\n\n\u002F\u002F Event payload\nNodeEvent {\n  node: AnyNode\n  position: [x, y, z]\n  localPosition: [x, y, z]\n  normal?: [x, y, z]\n  stopPropagation: () => void\n}\n```\n\n---\n\n### Spatial Grid Manager\n\nHandles collision detection and placement validation:\n\n```typescript\nspatialGridManager.canPlaceOnFloor(levelId, position, dimensions, rotation)\nspatialGridManager.canPlaceOnWall(wallId, t, height, dimensions)\nspatialGridManager.getSlabElevationAt(levelId, x, z)\n```\n\nUsed by item placement tools to validate positions and calculate slab elevations.\n\n---\n\n## Editor Architecture\n\nThe editor extends the viewer with:\n\n### Tools\n\nTools are activated via the toolbar and handle user input for specific operations:\n\n- **SelectTool** - Selection and manipulation\n- **WallTool** - Draw walls\n- **ZoneTool** - Create zones\n- **ItemTool** - Place furniture\u002Ffixtures\n- **SlabTool** - Create floor slabs\n\n### Selection Manager\n\nThe editor uses a custom selection manager with hierarchical navigation:\n\n```\nSite → Building → Level → Zone → Items\n```\n\nEach depth level has its own selection strategy for hover\u002Fclick behavior.\n\n### Editor-Specific Systems\n\n- `ZoneSystem` - Controls zone visibility based on level mode\n- Custom camera controls with node focusing\n\n---\n\n## Data Flow\n\n```\nUser Action (click, drag)\n       ↓\nTool Handler\n       ↓\nuseScene.createNode() \u002F updateNode()\n       ↓\nNode added\u002Fupdated in store\nNode marked dirty\n       ↓\nReact re-renders NodeRenderer\nuseRegistry() registers 3D object\n       ↓\nSystem detects dirty node (useFrame)\nUpdates geometry via sceneRegistry\nClears dirty flag\n```\n\n---\n\n## Technology Stack\n\n- **React 19** + **Next.js 16**\n- **Three.js** (WebGPU renderer)\n- **React Three Fiber** + **Drei**\n- **Zustand** (state management)\n- **Zod** (schema validation)\n- **Zundo** (undo\u002Fredo)\n- **three-bvh-csg** (Boolean geometry operations)\n- **Turborepo** (monorepo management)\n- **Bun** (package manager)\n\n---\n\n## Getting Started\n\n### Development\n\nRun the development server from the **root directory** to enable hot reload for all packages:\n\n```bash\n# Install dependencies\nbun install\n\n# Run development server (builds packages + starts editor with watch mode)\nbun dev\n\n# This will:\n# 1. Build @pascal-app\u002Fcore and @pascal-app\u002Fviewer\n# 2. Start watching both packages for changes\n# 3. Start the Next.js editor dev server\n# Open http:\u002F\u002Flocalhost:3000\n```\n\n**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\u002Fcore\u002Fsrc\u002F` or `packages\u002Fviewer\u002Fsrc\u002F`.\n\n### Building for Production\n\n```bash\n# Build all packages\nturbo build\n\n# Build specific package\nturbo build --filter=@pascal-app\u002Fcore\n```\n\n### Publishing Packages\n\n```bash\n# Build packages\nturbo build --filter=@pascal-app\u002Fcore --filter=@pascal-app\u002Fviewer\n\n# Publish to npm\nnpm publish --workspace=@pascal-app\u002Fcore --access public\nnpm publish --workspace=@pascal-app\u002Fviewer --access public\n```\n\n---\n\n## Key Files\n\n| Path | Description |\n|------|-------------|\n| `packages\u002Fcore\u002Fsrc\u002Fschema\u002F` | Node type definitions (Zod schemas) |\n| `packages\u002Fcore\u002Fsrc\u002Fstore\u002Fuse-scene.ts` | Scene state store |\n| `packages\u002Fcore\u002Fsrc\u002Fhooks\u002Fscene-registry\u002F` | 3D object registry |\n| `packages\u002Fcore\u002Fsrc\u002Fsystems\u002F` | Geometry generation systems |\n| `packages\u002Fviewer\u002Fsrc\u002Fcomponents\u002Frenderers\u002F` | Node renderers |\n| `packages\u002Fviewer\u002Fsrc\u002Fcomponents\u002Fviewer\u002F` | Main Viewer component |\n| `apps\u002Feditor\u002Fcomponents\u002Ftools\u002F` | Editor tools |\n| `apps\u002Feditor\u002Fstore\u002F` | Editor-specific state |\n\n---\n\n## Contributors\n\n\u003Ca href=\"https:\u002F\u002Fgithub.com\u002FAymericr\">\u003Cimg src=\"https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F4444492?v=4\" width=\"60\" height=\"60\" alt=\"Aymeric Rabot\" style=\"border-radius:50%\">\u003C\u002Fa>\n\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fwass08\">\u003Cimg src=\"https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6551176?v=4\" width=\"60\" height=\"60\" alt=\"Wassim Samad\" style=\"border-radius:50%\">\u003C\u002Fa>\n\n---\n\n\u003Ca href=\"https:\u002F\u002Ftrendshift.io\u002Frepositories\u002F23831\" target=\"_blank\">\u003Cimg src=\"https:\u002F\u002Ftrendshift.io\u002Fapi\u002Fbadge\u002Frepositories\u002F23831\" alt=\"pascalorg\u002Feditor | Trendshift\" width=\"250\" height=\"55\"\u002F>\u003C\u002Fa>\n","Pascal Editor 是一个用于创建和共享3D建筑项目的编辑器。该项目基于React Three Fiber和WebGPU技术构建，提供强大的3D渲染能力与直观的用户界面。它支持通过节点系统定义复杂的建筑结构，并利用Zustand进行高效的状态管理，确保了数据的一致性和可维护性。此外，Pascal Editor还提供了丰富的交互工具集，如选择、编辑等功能，使得用户能够轻松地对项目进行修改和完善。此工具非常适合建筑师、设计师以及任何需要创建或展示三维建筑设计方案的专业人士使用。",2,"2026-06-11 02:56:43","top_language"]