@pascal-app/editor 0.8.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/package.json +15 -13
  2. package/src/components/editor/alignment-3d-guide-layer.tsx +168 -0
  3. package/src/components/editor/bake-exporter.tsx +47 -0
  4. package/src/components/editor/custom-camera-controls.tsx +795 -28
  5. package/src/components/editor/editor-layout-mobile.tsx +8 -3
  6. package/src/components/editor/editor-layout-v2.tsx +76 -51
  7. package/src/components/editor/export-manager.tsx +69 -32
  8. package/src/components/editor/fence-tangent-lines-3d.tsx +87 -0
  9. package/src/components/editor/first-person/build-collider-world.test.ts +182 -0
  10. package/src/components/editor/first-person/build-collider-world.ts +228 -83
  11. package/src/components/editor/first-person-controls.tsx +943 -27
  12. package/src/components/editor/floating-action-menu.tsx +676 -208
  13. package/src/components/editor/floating-building-action-menu.tsx +2 -2
  14. package/src/components/editor/floorplan-background-selection.test.ts +58 -0
  15. package/src/components/editor/floorplan-background-selection.ts +3 -2
  16. package/src/components/editor/floorplan-panel.tsx +6916 -12137
  17. package/src/components/editor/grid.tsx +197 -36
  18. package/src/components/editor/group-actions.ts +457 -0
  19. package/src/components/editor/group-floating-action-menu.tsx +130 -0
  20. package/src/components/editor/group-move-3d.ts +397 -0
  21. package/src/components/editor/group-rotate-handle.tsx +420 -0
  22. package/src/components/editor/group-selection-box-3d.tsx +173 -0
  23. package/src/components/editor/group-transform-shared.test.ts +396 -0
  24. package/src/components/editor/group-transform-shared.ts +489 -0
  25. package/src/components/editor/handles/handle-arrow.tsx +581 -0
  26. package/src/components/editor/handles/use-handle-drag.ts +246 -0
  27. package/src/components/editor/index.tsx +412 -162
  28. package/src/components/editor/measurement-pill.tsx +115 -0
  29. package/src/components/editor/node-action-menu.tsx +14 -1
  30. package/src/components/editor/node-arrow-handles.tsx +1646 -0
  31. package/src/components/editor/opening-guides-3d-layer.tsx +144 -0
  32. package/src/components/editor/quick-measurement-card.tsx +70 -0
  33. package/src/components/editor/quick-measurement-hud.tsx +28 -0
  34. package/src/components/editor/riser-diagram-panel.tsx +137 -0
  35. package/src/components/editor/selection-manager.tsx +1106 -483
  36. package/src/components/editor/site-edge-labels.tsx +45 -21
  37. package/src/components/editor/slab-hole-highlights.tsx +474 -0
  38. package/src/components/editor/snapshot-capture-overlay.tsx +369 -155
  39. package/src/components/editor/three-context-bridge.ts +22 -0
  40. package/src/components/editor/thumbnail-generator.tsx +140 -41
  41. package/src/components/editor/use-floorplan-background-placement.ts +190 -35
  42. package/src/components/editor/use-floorplan-hit-testing.ts +24 -0
  43. package/src/components/editor/use-floorplan-scene-data.ts +19 -7
  44. package/src/components/editor/use-mesh-settle-epoch.ts +26 -0
  45. package/src/components/editor/wall-measurement-label.tsx +14 -19
  46. package/src/components/editor/wall-move-side-handles.tsx +870 -0
  47. package/src/components/editor/wall-opening-highlights.tsx +181 -0
  48. package/src/components/editor/wall-snap-beacon-layer.tsx +304 -0
  49. package/src/components/editor-2d/floorplan-action-menu-layer.tsx +15 -6
  50. package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +142 -0
  51. package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +5 -3
  52. package/src/components/editor-2d/floorplan-group-action-menu.tsx +87 -0
  53. package/src/components/editor-2d/floorplan-group-move.tsx +701 -0
  54. package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +113 -0
  55. package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +1656 -0
  56. package/src/components/editor-2d/floorplan-quick-measure-layer.tsx +212 -0
  57. package/src/components/editor-2d/floorplan-registry-action-menu.tsx +392 -0
  58. package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +857 -0
  59. package/src/components/editor-2d/floorplan-render-context.tsx +62 -0
  60. package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +77 -0
  61. package/src/components/editor-2d/floorplan-wall-move-ghost-layer.tsx +47 -0
  62. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +266 -0
  63. package/src/components/editor-2d/renderers/floorplan-label-angle.test.ts +15 -0
  64. package/src/components/editor-2d/renderers/floorplan-label-angle.ts +14 -0
  65. package/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx +108 -66
  66. package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +62 -0
  67. package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +308 -0
  68. package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +3325 -0
  69. package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +24 -9
  70. package/src/components/editor-2d/renderers/floorplan-voronoi-layer.tsx +128 -0
  71. package/src/components/editor-2d/svg-paths.ts +10 -2
  72. package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +439 -57
  73. package/src/components/systems/ceiling/ceiling-system.tsx +109 -12
  74. package/src/components/systems/roof/roof-edit-system.tsx +1851 -22
  75. package/src/components/systems/selection-affordance-manager.tsx +38 -0
  76. package/src/components/systems/stair/stair-edit-system.tsx +1 -1
  77. package/src/components/systems/zone/zone-label-editor-system.tsx +14 -1
  78. package/src/components/systems/zone/zone-system.tsx +42 -13
  79. package/src/components/tools/elevator/elevator-defaults.ts +8 -0
  80. package/src/components/tools/elevator/elevator-tool.tsx +375 -0
  81. package/src/components/tools/elevator/move-elevator-tool.tsx +263 -0
  82. package/src/components/tools/fence/fence-drafting.ts +141 -27
  83. package/src/components/tools/item/move-tool.tsx +45 -105
  84. package/src/components/tools/item/placement-math.test.ts +22 -0
  85. package/src/components/tools/item/placement-math.ts +59 -7
  86. package/src/components/tools/item/placement-strategies.ts +432 -16
  87. package/src/components/tools/item/placement-types.ts +22 -1
  88. package/src/components/tools/item/use-draft-node.ts +51 -2
  89. package/src/components/tools/item/use-placement-coordinator.tsx +1399 -256
  90. package/src/components/tools/registry/move-registry-node-tool.tsx +1064 -0
  91. package/src/components/tools/roof/roof-tool.tsx +350 -28
  92. package/src/components/tools/select/box-select-state.ts +88 -0
  93. package/src/components/tools/select/box-select-tool.tsx +442 -520
  94. package/src/components/tools/select/marquee-geometry.test.ts +161 -0
  95. package/src/components/tools/select/marquee-geometry.ts +155 -0
  96. package/src/components/tools/select/plane-box-select-tool.tsx +570 -0
  97. package/src/components/tools/select/screen-rectangle-selection.ts +84 -0
  98. package/src/components/tools/select/select-candidates.test.ts +109 -0
  99. package/src/components/tools/select/select-candidates.ts +85 -0
  100. package/src/components/tools/shared/affordance-dispatch.ts +30 -0
  101. package/src/components/tools/shared/cursor-sphere.tsx +62 -26
  102. package/src/components/tools/shared/drag-bounding-box.tsx +195 -0
  103. package/src/components/tools/shared/facing-indicator.tsx +87 -0
  104. package/src/components/tools/shared/facing-pose-indicator.tsx +54 -0
  105. package/src/components/tools/shared/floor-stack-preview.ts +25 -0
  106. package/src/components/tools/shared/fresh-placement-visibility.ts +61 -0
  107. package/src/components/tools/shared/placement-box-geometry.ts +123 -0
  108. package/src/components/tools/shared/placement-box.tsx +305 -0
  109. package/src/components/tools/shared/polygon-editor.tsx +866 -175
  110. package/src/components/tools/shared/segment-angle.ts +56 -0
  111. package/src/components/tools/site/site-boundary-editor.tsx +520 -16
  112. package/src/components/tools/site/site-flag-model.ts +18 -0
  113. package/src/components/tools/stair/stair-defaults.ts +2 -1
  114. package/src/components/tools/stair/stair-tool.tsx +342 -37
  115. package/src/components/tools/tool-manager.tsx +239 -62
  116. package/src/components/tools/wall/wall-drafting.test.ts +330 -0
  117. package/src/components/tools/wall/wall-drafting.ts +248 -171
  118. package/src/components/tools/wall/wall-snap-geometry.test.ts +123 -0
  119. package/src/components/tools/wall/wall-snap-geometry.ts +331 -0
  120. package/src/components/tools/zone/zone-boundary-editor.tsx +5 -1
  121. package/src/components/tools/zone/zone-tool.tsx +86 -82
  122. package/src/components/ui/action-menu/action-button.tsx +21 -1
  123. package/src/components/ui/action-menu/camera-actions.tsx +24 -17
  124. package/src/components/ui/action-menu/control-modes.tsx +40 -170
  125. package/src/components/ui/action-menu/furnish-tools.tsx +5 -5
  126. package/src/components/ui/action-menu/index.tsx +3 -117
  127. package/src/components/ui/action-menu/measurement-control.tsx +188 -0
  128. package/src/components/ui/action-menu/structure-tools.tsx +27 -93
  129. package/src/components/ui/action-menu/view-toggles.tsx +279 -90
  130. package/src/components/ui/command-palette/editor-commands.tsx +20 -15
  131. package/src/components/ui/command-palette/index.tsx +5 -3
  132. package/src/components/ui/controls/material-paint-panel.tsx +152 -0
  133. package/src/components/ui/controls/material-picker.tsx +87 -177
  134. package/src/components/ui/controls/material-properties-editor.tsx +108 -0
  135. package/src/components/ui/controls/metric-control.tsx +142 -46
  136. package/src/components/ui/controls/scene-material-list.tsx +247 -0
  137. package/src/components/ui/controls/slider-control.tsx +88 -38
  138. package/src/components/ui/floating-level-selector.tsx +47 -10
  139. package/src/components/ui/helpers/building-helper.tsx +10 -23
  140. package/src/components/ui/helpers/contextual-helper-panel.tsx +428 -0
  141. package/src/components/ui/helpers/helper-manager.tsx +235 -23
  142. package/src/components/ui/helpers/item-helper.tsx +28 -32
  143. package/src/components/ui/helpers/registered-tool-helper.tsx +59 -0
  144. package/src/components/ui/helpers/roof-helper.tsx +10 -12
  145. package/src/components/ui/icon-ref.tsx +47 -0
  146. package/src/components/ui/item-catalog/catalog-items.tsx +40 -1
  147. package/src/components/ui/item-catalog/item-catalog.tsx +16 -36
  148. package/src/components/ui/level-duplicate-dialog.tsx +2 -1
  149. package/src/components/ui/panels/node-display.ts +17 -16
  150. package/src/components/ui/panels/panel-manager.tsx +55 -58
  151. package/src/components/ui/panels/panel-wrapper.tsx +238 -12
  152. package/src/components/ui/panels/parametric-inspector.tsx +482 -0
  153. package/src/components/ui/panels/reference-panel.tsx +54 -19
  154. package/src/components/ui/primitives/dropdown-menu.tsx +12 -8
  155. package/src/components/ui/primitives/shortcut-token.tsx +39 -3
  156. package/src/components/ui/primitives/sidebar.tsx +1 -0
  157. package/src/components/ui/scene-loader.tsx +1 -3
  158. package/src/components/ui/sidebar/app-sidebar.tsx +5 -1
  159. package/src/components/ui/sidebar/icon-rail.tsx +50 -31
  160. package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +263 -0
  161. package/src/components/ui/sidebar/panels/items-panel/index.tsx +82 -8
  162. package/src/components/ui/sidebar/panels/plugins-panel.tsx +218 -0
  163. package/src/components/ui/sidebar/panels/settings-panel/index.tsx +122 -41
  164. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +66 -7
  165. package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +267 -0
  166. package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +3 -1
  167. package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +9 -5
  168. package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +86 -0
  169. package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -1
  170. package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +7 -2
  171. package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +92 -0
  172. package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +75 -0
  173. package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +3 -3
  174. package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +86 -0
  175. package/src/components/ui/sidebar/panels/site-panel/index.tsx +42 -21
  176. package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +31 -17
  177. package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +2 -1
  178. package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +140 -0
  179. package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +48 -8
  180. package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +132 -0
  181. package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +9 -5
  182. package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +86 -0
  183. package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +7 -1
  184. package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +2 -2
  185. package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +112 -39
  186. package/src/components/ui/sidebar/panels/site-panel/tree-structure.ts +36 -0
  187. package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -1
  188. package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +7 -2
  189. package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +3 -3
  190. package/src/components/ui/sidebar/panels/zone-panel/index.tsx +51 -2
  191. package/src/components/ui/sidebar/tab-bar.tsx +75 -1
  192. package/src/components/ui/sidebar/use-plugin-panels.tsx +132 -0
  193. package/src/components/ui/snap-target-badge.test.tsx +40 -0
  194. package/src/components/ui/snap-target-badge.tsx +88 -0
  195. package/src/components/viewer-overlay.tsx +253 -61
  196. package/src/components/viewer-zone-system.tsx +7 -2
  197. package/src/hooks/use-auto-save.test.ts +14 -0
  198. package/src/hooks/use-auto-save.ts +64 -10
  199. package/src/hooks/use-ceiling-events.ts +176 -0
  200. package/src/hooks/use-drag-action.ts +127 -0
  201. package/src/hooks/use-keyboard.ts +493 -95
  202. package/src/hooks/use-selection.ts +64 -0
  203. package/src/index.tsx +451 -6
  204. package/src/lib/active-placement-surface.ts +35 -0
  205. package/src/lib/ceiling-plan-snap.ts +24 -0
  206. package/src/lib/constants.ts +6 -2
  207. package/src/lib/contextual-help.test.ts +112 -0
  208. package/src/lib/contextual-help.ts +144 -0
  209. package/src/lib/continuation.ts +64 -0
  210. package/src/lib/direct-manipulation.test.ts +192 -0
  211. package/src/lib/direct-manipulation.ts +109 -0
  212. package/src/lib/editor-api.ts +50 -0
  213. package/src/lib/elevator-support.ts +96 -0
  214. package/src/lib/floorplan/apply-alignment.test.ts +25 -0
  215. package/src/lib/floorplan/apply-alignment.ts +117 -0
  216. package/src/lib/floorplan/floorplan-export.tsx +364 -0
  217. package/src/lib/floorplan/geometry.ts +53 -0
  218. package/src/lib/floorplan/index.ts +10 -0
  219. package/src/lib/floorplan/items.ts +5 -2
  220. package/src/lib/floorplan/plan-coords.ts +21 -0
  221. package/src/lib/floorplan/selection-tool.ts +40 -0
  222. package/src/lib/floorplan/stairs.ts +12 -1
  223. package/src/lib/fresh-planar-placement.test.ts +339 -0
  224. package/src/lib/fresh-planar-placement.ts +130 -0
  225. package/src/lib/glb-export.test.ts +441 -0
  226. package/src/lib/glb-export.ts +874 -0
  227. package/src/lib/history.ts +11 -1
  228. package/src/lib/interaction/hot-set.test.ts +133 -0
  229. package/src/lib/interaction/hot-set.ts +67 -0
  230. package/src/lib/interaction/overlay-policy.test.ts +51 -0
  231. package/src/lib/interaction/overlay-policy.ts +59 -0
  232. package/src/lib/interaction/scope.ts +211 -0
  233. package/src/lib/level-duplication.ts +54 -42
  234. package/src/lib/level-selection.ts +2 -2
  235. package/src/lib/material-paint.ts +227 -48
  236. package/src/lib/measurement-kind.test.ts +30 -0
  237. package/src/lib/measurement-kind.ts +19 -0
  238. package/src/lib/measurement-label.test.ts +47 -0
  239. package/src/lib/measurement-label.ts +68 -0
  240. package/src/lib/measurement-parser.ts +116 -0
  241. package/src/lib/measurements.test.ts +183 -0
  242. package/src/lib/measurements.ts +199 -0
  243. package/src/lib/paint-scope.test.ts +454 -0
  244. package/src/lib/paint-scope.ts +461 -0
  245. package/src/lib/placement-drag-release.ts +23 -0
  246. package/src/lib/placement-metadata.ts +29 -0
  247. package/src/lib/planar-cursor-placement.test.ts +100 -0
  248. package/src/lib/planar-cursor-placement.ts +42 -0
  249. package/src/lib/plugin-panels.test.ts +19 -0
  250. package/src/lib/plugin-panels.ts +91 -0
  251. package/src/lib/quick-measurement.test.ts +77 -0
  252. package/src/lib/quick-measurement.ts +109 -0
  253. package/src/lib/roof-duplication.ts +7 -7
  254. package/src/lib/roof-hover-outline-proxy.ts +22 -0
  255. package/src/lib/roof-wall-hit.ts +164 -0
  256. package/src/lib/scene-clipboard.test.ts +196 -0
  257. package/src/lib/scene-clipboard.ts +332 -0
  258. package/src/lib/scene.ts +40 -8
  259. package/src/lib/selection-routing.test.ts +298 -0
  260. package/src/lib/selection-routing.ts +175 -0
  261. package/src/lib/sfx/index.ts +1 -0
  262. package/src/lib/sfx/movement-tick.test.ts +65 -0
  263. package/src/lib/sfx/movement-tick.ts +18 -0
  264. package/src/lib/sfx-bus.ts +69 -10
  265. package/src/lib/sfx-player.test.ts +124 -0
  266. package/src/lib/sfx-player.ts +148 -55
  267. package/src/lib/site-boundary.ts +1 -0
  268. package/src/lib/slab-plan-snap.test.ts +93 -0
  269. package/src/lib/slab-plan-snap.ts +108 -0
  270. package/src/lib/snapping-mode.test.ts +138 -0
  271. package/src/lib/snapping-mode.ts +179 -0
  272. package/src/lib/stair-duplication.ts +4 -4
  273. package/src/lib/stair-levels.test.ts +146 -0
  274. package/src/lib/stair-levels.ts +177 -0
  275. package/src/lib/surface-plan-snap.test.ts +71 -0
  276. package/src/lib/surface-plan-snap.ts +256 -0
  277. package/src/lib/use-linear-display.ts +40 -0
  278. package/src/lib/world-grid-snap.ts +231 -0
  279. package/src/lib/zone-content.ts +130 -0
  280. package/src/store/use-alignment-guides.ts +21 -0
  281. package/src/store/use-direct-manipulation-feedback.ts +20 -0
  282. package/src/store/use-editor.tsx +767 -114
  283. package/src/store/use-facing-pose.ts +44 -0
  284. package/src/store/use-fence-curve-draft.ts +21 -0
  285. package/src/store/use-floorplan-draft-preview.ts +101 -0
  286. package/src/store/use-floorplan-marquee.ts +50 -0
  287. package/src/store/use-interaction-scope.test.ts +186 -0
  288. package/src/store/use-interaction-scope.ts +132 -0
  289. package/src/store/use-measurement-draft.test.ts +530 -0
  290. package/src/store/use-measurement-draft.ts +543 -0
  291. package/src/store/use-opening-guides.ts +41 -0
  292. package/src/store/use-placement-preview.ts +39 -0
  293. package/src/store/use-quick-measurement-hud.test.ts +53 -0
  294. package/src/store/use-quick-measurement-hud.ts +77 -0
  295. package/src/store/use-segment-draft-chain.ts +28 -0
  296. package/src/store/use-stair-build-preview.ts +43 -0
  297. package/src/store/use-wall-move-ghosts.ts +36 -0
  298. package/src/store/use-wall-snap-indicator.ts +34 -0
  299. package/src/components/editor/first-person/bvh-ecctrl.tsx +0 -860
  300. package/src/components/editor/preset-thumbnail-generator.tsx +0 -125
  301. package/src/components/editor-2d/renderers/floorplan-roof-layer.tsx +0 -113
  302. package/src/components/tools/building/move-building-tool.tsx +0 -157
  303. package/src/components/tools/ceiling/ceiling-boundary-editor.tsx +0 -43
  304. package/src/components/tools/ceiling/ceiling-hole-editor.tsx +0 -49
  305. package/src/components/tools/ceiling/ceiling-tool.tsx +0 -465
  306. package/src/components/tools/ceiling/move-ceiling-tool.tsx +0 -264
  307. package/src/components/tools/column/column-tool.tsx +0 -97
  308. package/src/components/tools/column/move-column-tool.tsx +0 -105
  309. package/src/components/tools/door/door-math.ts +0 -110
  310. package/src/components/tools/door/door-tool.tsx +0 -324
  311. package/src/components/tools/door/move-door-tool.tsx +0 -412
  312. package/src/components/tools/fence/curve-fence-tool.tsx +0 -178
  313. package/src/components/tools/fence/fence-tool.tsx +0 -346
  314. package/src/components/tools/fence/move-fence-endpoint-tool.tsx +0 -441
  315. package/src/components/tools/fence/move-fence-tool.tsx +0 -302
  316. package/src/components/tools/item/item-tool.tsx +0 -26
  317. package/src/components/tools/roof/move-roof-tool.tsx +0 -364
  318. package/src/components/tools/slab/move-slab-tool.tsx +0 -182
  319. package/src/components/tools/slab/slab-boundary-editor.tsx +0 -43
  320. package/src/components/tools/slab/slab-hole-editor.tsx +0 -49
  321. package/src/components/tools/slab/slab-tool.tsx +0 -322
  322. package/src/components/tools/spawn/move-spawn-tool.tsx +0 -101
  323. package/src/components/tools/spawn/spawn-tool.tsx +0 -130
  324. package/src/components/tools/wall/curve-wall-tool.tsx +0 -178
  325. package/src/components/tools/wall/move-wall-endpoint-tool.tsx +0 -426
  326. package/src/components/tools/wall/move-wall-tool.tsx +0 -358
  327. package/src/components/tools/wall/wall-tool.tsx +0 -332
  328. package/src/components/tools/window/move-window-tool.tsx +0 -447
  329. package/src/components/tools/window/window-math.ts +0 -117
  330. package/src/components/tools/window/window-tool.tsx +0 -332
  331. package/src/components/ui/helpers/ceiling-helper.tsx +0 -20
  332. package/src/components/ui/helpers/slab-helper.tsx +0 -20
  333. package/src/components/ui/helpers/wall-helper.tsx +0 -20
  334. package/src/components/ui/panels/ceiling-panel.tsx +0 -249
  335. package/src/components/ui/panels/column-panel.tsx +0 -759
  336. package/src/components/ui/panels/door-panel.tsx +0 -1299
  337. package/src/components/ui/panels/fence-panel.tsx +0 -222
  338. package/src/components/ui/panels/item-panel.tsx +0 -306
  339. package/src/components/ui/panels/paint-panel.tsx +0 -163
  340. package/src/components/ui/panels/presets/presets-popover.tsx +0 -511
  341. package/src/components/ui/panels/roof-panel.tsx +0 -280
  342. package/src/components/ui/panels/roof-segment-panel.tsx +0 -311
  343. package/src/components/ui/panels/slab-panel.tsx +0 -250
  344. package/src/components/ui/panels/spawn-panel.tsx +0 -161
  345. package/src/components/ui/panels/stair-panel.tsx +0 -577
  346. package/src/components/ui/panels/stair-segment-panel.tsx +0 -325
  347. package/src/components/ui/panels/wall-panel.tsx +0 -273
  348. package/src/components/ui/panels/window-panel.tsx +0 -968
  349. package/src/contexts/presets-context.tsx +0 -121
  350. package/src/hooks/use-contextual-tools.ts +0 -61
@@ -1,6 +1,5 @@
1
1
  'use client'
2
2
 
3
- import { Icon } from '@iconify/react'
4
3
  import {
5
4
  type AnyNodeId,
6
5
  type BuildingNode,
@@ -10,25 +9,22 @@ import {
10
9
  useScene,
11
10
  } from '@pascal-app/core'
12
11
  import { useViewer } from '@pascal-app/viewer'
13
- import { Check, ChevronDown, Eye, EyeOff, Layers2, Plus, Trash2 } from 'lucide-react'
12
+ import { Check, ChevronDown, Eye, EyeOff, Layers2, Plus, Trash2, Waypoints } from 'lucide-react'
14
13
  import { useCallback, useRef, useState } from 'react'
15
14
  import { useShallow } from 'zustand/react/shallow'
15
+ import { getLevelDisplayName } from '@pascal-app/core'
16
16
  import { createLocalGuideImage } from '../../../lib/local-guide-image'
17
17
  import { cn } from '../../../lib/utils'
18
- import useEditor, { type GridSnapStep } from '../../../store/use-editor'
18
+ import useEditor from '../../../store/use-editor'
19
19
  import { useUploadStore } from '../../../store/use-upload'
20
20
  import { SliderControl } from '../controls/slider-control'
21
21
  import { Popover, PopoverContent, PopoverTrigger } from '../primitives/popover'
22
- import { Tooltip, TooltipContent, TooltipTrigger } from '../primitives/tooltip'
23
22
  import { ActionButton } from './action-button'
24
23
 
25
24
  const MAX_FILE_SIZE = 200 * 1024 * 1024 // 200MB
26
25
  const ACCEPTED_FILE_TYPES = '.glb,.gltf,image/jpeg,image/png,image/webp,image/gif'
27
- const GRID_SNAP_STEPS: GridSnapStep[] = [0.5, 0.25, 0.1, 0.05]
28
-
29
- function formatGridSnapStep(step: GridSnapStep) {
30
- return step.toFixed(2)
31
- }
26
+ const REFERENCES_EMPTY_TEXT =
27
+ 'Upload GLB meshes as scan references or blueprint images as guide references.'
32
28
 
33
29
  // ── Helper: get guide images for the current level ──────────────────────────
34
30
 
@@ -84,10 +80,6 @@ function useLowerReferenceLevels(): LevelNode[] {
84
80
  )
85
81
  }
86
82
 
87
- function getLevelDisplayName(level: LevelNode) {
88
- return level.name || `Level ${level.level}`
89
- }
90
-
91
83
  // ── Shared upload button for dropdowns ──────────────────────────────────────
92
84
 
93
85
  function UploadButton({ onError }: { onError: (message: string | null) => void }) {
@@ -227,7 +219,7 @@ function GuidesControl() {
227
219
  <img
228
220
  alt="Guides"
229
221
  className="h-[28px] w-[28px] object-contain"
230
- src="/icons/floorplan.png"
222
+ src="/icons/floorplan.webp"
231
223
  />
232
224
  <span className="absolute -right-1.5 -bottom-1 min-w-[14px] rounded-full bg-white/20 px-[3px] text-center font-medium text-[9px] text-white/70 leading-[14px]">
233
225
  {guides.length}
@@ -266,7 +258,7 @@ function GuidesControl() {
266
258
  <div className="space-y-3">
267
259
  <div className="flex items-center gap-2">
268
260
  <span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-background/80">
269
- <img alt="" className="h-4 w-4 object-contain" src="/icons/floorplan.png" />
261
+ <img alt="" className="h-4 w-4 object-contain" src="/icons/floorplan.webp" />
270
262
  </span>
271
263
  <div className="min-w-0 flex-1">
272
264
  <p className="font-medium text-foreground text-sm">Guide images</p>
@@ -306,7 +298,7 @@ function GuidesControl() {
306
298
  <img
307
299
  alt=""
308
300
  className="h-3.5 w-3.5 shrink-0 object-contain opacity-70"
309
- src="/icons/floorplan.png"
301
+ src="/icons/floorplan.webp"
310
302
  />
311
303
  <p className="truncate font-medium text-foreground text-sm">
312
304
  {guide.name || `Guide image ${index + 1}`}
@@ -345,7 +337,7 @@ function GuidesControl() {
345
337
  </div>
346
338
  ) : (
347
339
  <div className="rounded-xl border border-border/45 border-dashed bg-background/60 px-3 py-4 text-muted-foreground text-sm">
348
- No guide images on this level yet.
340
+ {REFERENCES_EMPTY_TEXT}
349
341
  </div>
350
342
  )}
351
343
  </div>
@@ -354,70 +346,6 @@ function GuidesControl() {
354
346
  )
355
347
  }
356
348
 
357
- // ── Grid snap toggle ────────────────────────────────────────────────────────
358
-
359
- function GridSnapControl() {
360
- const [isOpen, setIsOpen] = useState(false)
361
- const gridSnapStep = useEditor((state) => state.gridSnapStep)
362
- const setGridSnapStep = useEditor((state) => state.setGridSnapStep)
363
-
364
- return (
365
- <Popover onOpenChange={setIsOpen} open={isOpen}>
366
- <Tooltip>
367
- <TooltipTrigger asChild>
368
- <PopoverTrigger asChild>
369
- <button
370
- aria-expanded={isOpen}
371
- aria-label={`Grid snap: ${formatGridSnapStep(gridSnapStep)}`}
372
- className={cn(
373
- 'flex h-11 w-11 flex-col items-center justify-center rounded-lg text-muted-foreground transition-all hover:bg-white/5 hover:text-foreground',
374
- isOpen && 'bg-white/10 text-foreground',
375
- )}
376
- type="button"
377
- >
378
- <Icon height={16} icon="lucide:grid-2x2" width={16} />
379
- <span className="mt-1 font-medium text-[9px] leading-none">
380
- {formatGridSnapStep(gridSnapStep)}
381
- </span>
382
- </button>
383
- </PopoverTrigger>
384
- </TooltipTrigger>
385
- <TooltipContent side="top">Grid snap: {formatGridSnapStep(gridSnapStep)}</TooltipContent>
386
- </Tooltip>
387
-
388
- <PopoverContent
389
- align="center"
390
- className="w-36 rounded-xl border-border/45 bg-background/96 p-2 shadow-elevation-3 backdrop-blur-xl"
391
- side="top"
392
- sideOffset={14}
393
- >
394
- <div className="space-y-1">
395
- {GRID_SNAP_STEPS.map((step) => {
396
- const isActive = step === gridSnapStep
397
- return (
398
- <button
399
- className={cn(
400
- 'flex w-full items-center justify-between rounded-lg px-2.5 py-2 text-left text-sm transition-colors hover:bg-white/8',
401
- isActive && 'bg-white/10 text-foreground',
402
- )}
403
- key={step}
404
- onClick={() => {
405
- setGridSnapStep(step)
406
- setIsOpen(false)
407
- }}
408
- type="button"
409
- >
410
- <span>{formatGridSnapStep(step)}</span>
411
- {isActive ? <Check className="h-3.5 w-3.5" /> : <span className="h-3.5 w-3.5" />}
412
- </button>
413
- )
414
- })}
415
- </div>
416
- </PopoverContent>
417
- </Popover>
418
- )
419
- }
420
-
421
349
  // ── Scans toggle + dropdown ─────────────────────────────────────────────────
422
350
 
423
351
  function ScansControl() {
@@ -467,7 +395,7 @@ function ScansControl() {
467
395
  variant="ghost"
468
396
  >
469
397
  <div className="relative">
470
- <img alt="Scans" className="h-[28px] w-[28px] object-contain" src="/icons/mesh.png" />
398
+ <img alt="Scans" className="h-[28px] w-[28px] object-contain" src="/icons/mesh.webp" />
471
399
  <span className="absolute -right-1.5 -bottom-1 min-w-[14px] rounded-full bg-white/20 px-[3px] text-center font-medium text-[9px] text-white/70 leading-[14px]">
472
400
  {scans.length}
473
401
  </span>
@@ -505,7 +433,7 @@ function ScansControl() {
505
433
  <div className="space-y-3">
506
434
  <div className="flex items-center gap-2">
507
435
  <span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-background/80">
508
- <img alt="" className="h-4 w-4 object-contain" src="/icons/mesh.png" />
436
+ <img alt="" className="h-4 w-4 object-contain" src="/icons/mesh.webp" />
509
437
  </span>
510
438
  <div className="min-w-0 flex-1">
511
439
  <p className="font-medium text-foreground text-sm">Scans</p>
@@ -545,7 +473,7 @@ function ScansControl() {
545
473
  <img
546
474
  alt=""
547
475
  className="h-3.5 w-3.5 shrink-0 object-contain opacity-70"
548
- src="/icons/mesh.png"
476
+ src="/icons/mesh.webp"
549
477
  />
550
478
  <p className="truncate font-medium text-foreground text-sm">
551
479
  {scan.name || `Scan ${index + 1}`}
@@ -584,7 +512,7 @@ function ScansControl() {
584
512
  </div>
585
513
  ) : (
586
514
  <div className="rounded-xl border border-border/45 border-dashed bg-background/60 px-3 py-4 text-muted-foreground text-sm">
587
- No scans on this level yet.
515
+ {REFERENCES_EMPTY_TEXT}
588
516
  </div>
589
517
  )}
590
518
  </div>
@@ -593,6 +521,247 @@ function ScansControl() {
593
521
  )
594
522
  }
595
523
 
524
+ // ── References (merged scans + guides) ──────────────────────────────────────
525
+ // Bottom-bar control that folds the separate Scans and Guides toggles into one
526
+ // "References" split button + a popover holding both, each with its own
527
+ // visibility toggle, upload, and per-item opacity/delete.
528
+
529
+ function ReferenceListSection({
530
+ title,
531
+ iconSrc,
532
+ noun,
533
+ emptyText,
534
+ nodes,
535
+ show,
536
+ setShow,
537
+ onError,
538
+ }: {
539
+ title: string
540
+ iconSrc: string
541
+ noun: string
542
+ emptyText: string
543
+ nodes: (GuideNode | ScanNode)[]
544
+ show: boolean
545
+ setShow: (show: boolean) => void
546
+ onError: (message: string | null) => void
547
+ }) {
548
+ const setSelection = useViewer((state) => state.setSelection)
549
+ const updateNode = useScene((state) => state.updateNode)
550
+ const deleteNode = useScene((state) => state.deleteNode)
551
+ const selectedReferenceId = useEditor((state) => state.selectedReferenceId)
552
+ const setSelectedReferenceId = useEditor((state) => state.setSelectedReferenceId)
553
+ const hasItems = nodes.length > 0
554
+
555
+ const handleSelect = useCallback(
556
+ (id: AnyNodeId) => {
557
+ setShow(true)
558
+ setSelectedReferenceId(id)
559
+ setSelection({ selectedIds: [], zoneId: null })
560
+ },
561
+ [setShow, setSelectedReferenceId, setSelection],
562
+ )
563
+
564
+ return (
565
+ <div className="space-y-2">
566
+ <div className="flex items-center gap-2">
567
+ <span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-background/80">
568
+ <img alt="" className="h-4 w-4 object-contain" src={iconSrc} />
569
+ </span>
570
+ <div className="min-w-0 flex-1">
571
+ <p className="font-medium text-foreground text-sm">{title}</p>
572
+ {hasItems && (
573
+ <p className="text-muted-foreground text-xs">
574
+ {nodes.length} {noun}
575
+ {nodes.length !== 1 ? 's' : ''} on this level
576
+ </p>
577
+ )}
578
+ </div>
579
+ <button
580
+ aria-label={show ? `Hide ${title.toLowerCase()}` : `Show ${title.toLowerCase()}`}
581
+ className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full border border-border/40 text-muted-foreground transition-colors hover:bg-white/10 hover:text-foreground"
582
+ onClick={() => setShow(!show)}
583
+ type="button"
584
+ >
585
+ {show ? <Eye className="h-3.5 w-3.5" /> : <EyeOff className="h-3.5 w-3.5" />}
586
+ </button>
587
+ <UploadButton onError={onError} />
588
+ </div>
589
+
590
+ {hasItems ? (
591
+ <div className="max-h-40 space-y-2 overflow-y-auto pr-1">
592
+ {nodes.map((node, index) => (
593
+ <div
594
+ className={cn(
595
+ 'group/item space-y-2 rounded-xl border bg-background/75 p-2.5 transition-colors',
596
+ selectedReferenceId === node.id
597
+ ? 'border-foreground/35 bg-white/10'
598
+ : 'border-border/45',
599
+ )}
600
+ key={node.id}
601
+ >
602
+ <div className="flex min-w-0 items-center gap-2">
603
+ <button
604
+ className="flex min-w-0 flex-1 items-center gap-2 text-left"
605
+ onClick={() => handleSelect(node.id)}
606
+ type="button"
607
+ >
608
+ <img
609
+ alt=""
610
+ className="h-3.5 w-3.5 shrink-0 object-contain opacity-70"
611
+ src={iconSrc}
612
+ />
613
+ <p className="truncate font-medium text-foreground text-sm">
614
+ {node.name || `${noun.charAt(0).toUpperCase()}${noun.slice(1)} ${index + 1}`}
615
+ </p>
616
+ {selectedReferenceId === node.id && (
617
+ <Check className="ml-auto h-3.5 w-3.5 shrink-0 text-foreground/80" />
618
+ )}
619
+ </button>
620
+ <button
621
+ aria-label={`Delete ${noun}`}
622
+ className="flex h-5 w-5 shrink-0 items-center justify-center rounded-md text-muted-foreground/50 opacity-0 transition-all hover:bg-destructive/10 hover:text-destructive group-hover/item:opacity-100"
623
+ onClick={(event) => {
624
+ event.stopPropagation()
625
+ deleteNode(node.id)
626
+ if (selectedReferenceId === node.id) {
627
+ setSelectedReferenceId(null)
628
+ }
629
+ }}
630
+ type="button"
631
+ >
632
+ <Trash2 className="h-3 w-3" />
633
+ </button>
634
+ </div>
635
+ <SliderControl
636
+ label="Opacity"
637
+ max={100}
638
+ min={0}
639
+ onChange={(value) =>
640
+ updateNode(node.id, { opacity: Math.round(Math.min(100, Math.max(0, value))) })
641
+ }
642
+ precision={0}
643
+ step={1}
644
+ unit="%"
645
+ value={node.opacity}
646
+ />
647
+ </div>
648
+ ))}
649
+ </div>
650
+ ) : (
651
+ <div className="rounded-xl border border-border/45 border-dashed bg-background/60 px-3 py-3 text-muted-foreground text-sm">
652
+ {emptyText}
653
+ </div>
654
+ )}
655
+ </div>
656
+ )
657
+ }
658
+
659
+ function ReferencesControl() {
660
+ const showScans = useViewer((state) => state.showScans)
661
+ const setShowScans = useViewer((state) => state.setShowScans)
662
+ const showGuides = useViewer((state) => state.showGuides)
663
+ const setShowGuides = useViewer((state) => state.setShowGuides)
664
+ const [isOpen, setIsOpen] = useState(false)
665
+ const [uploadError, setUploadError] = useState<string | null>(null)
666
+
667
+ const scans = useLevelScans()
668
+ const guides = useLevelGuides()
669
+ const total = scans.length + guides.length
670
+ const anyVisible = showScans || showGuides
671
+
672
+ const toggleAll = useCallback(() => {
673
+ const next = !anyVisible
674
+ setShowScans(next)
675
+ setShowGuides(next)
676
+ }, [anyVisible, setShowScans, setShowGuides])
677
+
678
+ return (
679
+ <Popover onOpenChange={setIsOpen} open={isOpen}>
680
+ <div className="flex items-center">
681
+ <ActionButton
682
+ className={cn(
683
+ 'rounded-r-none p-0',
684
+ anyVisible
685
+ ? 'bg-white/15'
686
+ : 'opacity-60 grayscale hover:bg-white/5 hover:opacity-100 hover:grayscale-0',
687
+ )}
688
+ label={`References: ${anyVisible ? 'Visible' : 'Hidden'}`}
689
+ onClick={toggleAll}
690
+ size="icon"
691
+ variant="ghost"
692
+ >
693
+ <div className="relative">
694
+ <img
695
+ alt="References"
696
+ className="h-[28px] w-[28px] object-contain"
697
+ src="/icons/floorplan.webp"
698
+ />
699
+ <span className="absolute -right-1.5 -bottom-1 min-w-[14px] rounded-full bg-white/20 px-[3px] text-center font-medium text-[9px] text-white/70 leading-[14px]">
700
+ {total}
701
+ </span>
702
+ </div>
703
+ </ActionButton>
704
+
705
+ <PopoverTrigger asChild>
706
+ <button
707
+ aria-expanded={isOpen}
708
+ aria-label="Reference settings"
709
+ className={cn(
710
+ 'flex h-11 w-6 items-center justify-center rounded-r-lg transition-colors',
711
+ anyVisible
712
+ ? isOpen
713
+ ? 'bg-white/10'
714
+ : 'bg-white/5 hover:bg-white/8'
715
+ : isOpen
716
+ ? 'bg-white/8'
717
+ : 'opacity-60 hover:bg-white/5 hover:opacity-100',
718
+ )}
719
+ type="button"
720
+ >
721
+ <ChevronDown className={cn('h-3 w-3 transition-transform', isOpen && 'rotate-180')} />
722
+ </button>
723
+ </PopoverTrigger>
724
+ </div>
725
+
726
+ <PopoverContent
727
+ align="center"
728
+ className="w-72 rounded-xl border-border/45 bg-background/96 p-3 shadow-elevation-3 backdrop-blur-xl"
729
+ side="top"
730
+ sideOffset={14}
731
+ >
732
+ <div className="space-y-3">
733
+ {uploadError && (
734
+ <div className="rounded-lg border border-destructive/30 bg-destructive/10 px-2.5 py-2 text-destructive text-xs">
735
+ {uploadError}
736
+ </div>
737
+ )}
738
+ <ReferenceListSection
739
+ emptyText={REFERENCES_EMPTY_TEXT}
740
+ iconSrc="/icons/mesh.webp"
741
+ nodes={scans}
742
+ noun="scan"
743
+ onError={setUploadError}
744
+ setShow={setShowScans}
745
+ show={showScans}
746
+ title="Scans"
747
+ />
748
+ <div className="h-px bg-border/45" />
749
+ <ReferenceListSection
750
+ emptyText={REFERENCES_EMPTY_TEXT}
751
+ iconSrc="/icons/floorplan.webp"
752
+ nodes={guides}
753
+ noun="guide image"
754
+ onError={setUploadError}
755
+ setShow={setShowGuides}
756
+ show={showGuides}
757
+ title="Guide images"
758
+ />
759
+ </div>
760
+ </PopoverContent>
761
+ </Popover>
762
+ )
763
+ }
764
+
596
765
  // ── Reference floor control ────────────────────────────────────────────────────────────────────
597
766
 
598
767
  function ReferenceFloorControl() {
@@ -749,15 +918,35 @@ function ReferenceFloorControl() {
749
918
  )
750
919
  }
751
920
 
752
- // ── Exports ─────────────────────────────────────────────────────────────────
921
+ // ── Riser diagram control ────────────────────────────────────────────────────
753
922
 
754
- export { GridSnapControl }
923
+ function RiserControl() {
924
+ const isRiserOpen = useEditor((state) => state.isRiserOpen)
925
+ const toggleRiserOpen = useEditor((state) => state.toggleRiserOpen)
926
+
927
+ return (
928
+ <ActionButton
929
+ className={cn(
930
+ isRiserOpen
931
+ ? 'bg-white/15'
932
+ : 'opacity-60 grayscale hover:bg-white/5 hover:opacity-100 hover:grayscale-0',
933
+ )}
934
+ label="Riser diagram"
935
+ onClick={toggleRiserOpen}
936
+ size="icon"
937
+ variant="ghost"
938
+ >
939
+ <Waypoints className="h-4 w-4" />
940
+ </ActionButton>
941
+ )
942
+ }
943
+
944
+ // ── Exports ─────────────────────────────────────────────────────────────────
755
945
 
756
946
  export function SecondaryToggles() {
757
947
  return (
758
948
  <div className="flex items-center gap-1">
759
- <ScansControl />
760
- <GuidesControl />
949
+ <ReferencesControl />
761
950
  </div>
762
951
  )
763
952
  }
@@ -765,10 +954,10 @@ export function SecondaryToggles() {
765
954
  export function ViewToggles() {
766
955
  return (
767
956
  <div className="flex items-center gap-1">
768
- <GridSnapControl />
769
957
  <ScansControl />
770
958
  <GuidesControl />
771
959
  <ReferenceFloorControl />
960
+ <RiserControl />
772
961
  </div>
773
962
  )
774
963
  }
@@ -27,6 +27,7 @@ import {
27
27
  PencilLine,
28
28
  Plus,
29
29
  Redo2,
30
+ Sparkles,
30
31
  Square,
31
32
  SquareStack,
32
33
  Sun,
@@ -190,8 +191,11 @@ export function EditorCommands() {
190
191
  const { nodes } = useScene.getState()
191
192
  const building = Object.values(nodes).find((n) => n.type === 'building')
192
193
  if (!building) return
194
+ const levelCount = building.children.filter(
195
+ (childId) => nodes[childId as keyof typeof nodes]?.type === 'level',
196
+ ).length
193
197
  const newLevel = LevelNode.parse({
194
- level: building.children.length,
198
+ level: levelCount,
195
199
  children: [],
196
200
  parentId: building.id,
197
201
  })
@@ -241,10 +245,10 @@ export function EditorCommands() {
241
245
  label: 'Wall Mode',
242
246
  group: 'Viewer Controls',
243
247
  icon: <Layers className="h-4 w-4" />,
244
- keywords: ['wall', 'cutaway', 'up', 'down', 'view'],
248
+ keywords: ['wall', 'cutaway', 'up', 'down', 'translucent', 'view'],
245
249
  badge: () => {
246
250
  const mode = useViewer.getState().wallMode
247
- return { cutaway: 'Cutaway', up: 'Up', down: 'Down' }[mode]
251
+ return { cutaway: 'Cutaway', up: 'Up', down: 'Down', translucent: 'Translucent' }[mode]
248
252
  },
249
253
  navigate: true,
250
254
  execute: () => navigateTo('wall-mode'),
@@ -278,19 +282,20 @@ export function EditorCommands() {
278
282
  }),
279
283
  },
280
284
  {
281
- id: 'editor.viewer.theme',
282
- label: () => {
283
- const theme = useViewer.getState().theme
284
- return theme === 'dark' ? 'Switch to Light Theme' : 'Switch to Dark Theme'
285
- },
285
+ id: 'editor.viewer.shading-solid',
286
+ label: 'Switch to Solid',
286
287
  group: 'Viewer Controls',
287
- icon: <Sun className="h-4 w-4" />, // icon is static; label conveys the action
288
- keywords: ['theme', 'dark', 'light', 'appearance', 'color'],
289
- execute: () =>
290
- run(() => {
291
- const { theme, setTheme } = useViewer.getState()
292
- setTheme(theme === 'dark' ? 'light' : 'dark')
293
- }),
288
+ icon: <Box className="h-4 w-4" />,
289
+ keywords: ['solid', 'shading', 'render', 'mode', 'performance'],
290
+ execute: () => run(() => useViewer.getState().setShading('solid')),
291
+ },
292
+ {
293
+ id: 'editor.viewer.shading-rendered',
294
+ label: 'Switch to Rendered',
295
+ group: 'Viewer Controls',
296
+ icon: <Sparkles className="h-4 w-4" />,
297
+ keywords: ['rendered', 'shading', 'render', 'mode', 'quality'],
298
+ execute: () => run(() => useViewer.getState().setShading('rendered')),
294
299
  },
295
300
  {
296
301
  id: 'editor.viewer.camera-snapshot',
@@ -10,6 +10,7 @@ import { useEffect, useState } from 'react'
10
10
  import { create } from 'zustand'
11
11
  import { useShallow } from 'zustand/shallow'
12
12
  import { Dialog, DialogContent, DialogTitle } from './../../../components/ui/primitives/dialog'
13
+ import { getLevelDisplayName } from '@pascal-app/core'
13
14
  import { useCommandRegistry } from '../../../store/use-command-registry'
14
15
  import { usePaletteViewRegistry } from '../../../store/use-palette-view-registry'
15
16
 
@@ -243,10 +244,11 @@ export function CommandPalette({ emptyAction }: { emptyAction?: CommandPaletteEm
243
244
  setOpen(false)
244
245
  }
245
246
 
246
- const wallModeLabel: Record<'cutaway' | 'up' | 'down', string> = {
247
+ const wallModeLabel: Record<'cutaway' | 'up' | 'down' | 'translucent', string> = {
247
248
  cutaway: 'Cutaway',
248
249
  up: 'Up',
249
250
  down: 'Down',
251
+ translucent: 'Translucent',
250
252
  }
251
253
  const levelModeLabel: Record<'manual' | 'stacked' | 'exploded' | 'solo', string> = {
252
254
  manual: 'Manual',
@@ -372,7 +374,7 @@ export function CommandPalette({ emptyAction }: { emptyAction?: CommandPaletteEm
372
374
  {/* ── Wall Mode sub-page ────────────────────────────────────── */}
373
375
  {page === 'wall-mode' && (
374
376
  <Command.Group heading="Wall Mode">
375
- {(['cutaway', 'up', 'down'] as const).map((mode) => (
377
+ {(['cutaway', 'up', 'down', 'translucent'] as const).map((mode) => (
376
378
  <OptionItem
377
379
  isActive={wallMode === mode}
378
380
  key={mode}
@@ -404,7 +406,7 @@ export function CommandPalette({ emptyAction }: { emptyAction?: CommandPaletteEm
404
406
  <OptionItem
405
407
  isActive={level.id === activeLevelId}
406
408
  key={level.id}
407
- label={level.name ?? `Level ${level.level}`}
409
+ label={getLevelDisplayName(level)}
408
410
  onSelect={() =>
409
411
  run(() => useViewer.getState().setSelection({ levelId: level.id }))
410
412
  }