@pascal-app/editor 1.0.6 → 1.1.0

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 (382) hide show
  1. package/package.json +18 -15
  2. package/src/components/editor/action-menu-button.tsx +4 -1
  3. package/src/components/editor/alignment-3d-guide-layer.tsx +102 -11
  4. package/src/components/editor/bake-thumbnail.tsx +3 -2
  5. package/src/components/editor/custom-camera-controls.tsx +110 -42
  6. package/src/components/editor/duplicate-node.ts +2 -1
  7. package/src/components/editor/elevation-3d-guide-layer.tsx +1 -1
  8. package/src/components/editor/first-person-controls.tsx +77 -68
  9. package/src/components/editor/fit-scene-framing.test.ts +318 -0
  10. package/src/components/editor/fit-scene-framing.ts +142 -0
  11. package/src/components/editor/floating-action-menu.tsx +31 -11
  12. package/src/components/editor/floating-building-action-menu.tsx +1 -1
  13. package/src/components/editor/floor-openings-overlay.tsx +1 -1
  14. package/src/components/editor/floor-region-controls.tsx +2 -2
  15. package/src/components/editor/floorplan-panel.test.ts +220 -0
  16. package/src/components/editor/floorplan-panel.tsx +458 -140
  17. package/src/components/editor/floorplan-wall-typing-keys.test.tsx +152 -0
  18. package/src/components/editor/floorplan-wall-typing-keys.ts +79 -0
  19. package/src/components/editor/footprint-height-handle.tsx +2 -2
  20. package/src/components/editor/grid.tsx +24 -15
  21. package/src/components/editor/group-actions.ts +19 -1
  22. package/src/components/editor/group-floating-action-menu.tsx +1 -1
  23. package/src/components/editor/group-move-3d.ts +27 -2
  24. package/src/components/editor/group-selection-box-3d.tsx +4 -1
  25. package/src/components/editor/group-transform-shared.ts +23 -1
  26. package/src/components/editor/index.tsx +150 -20
  27. package/src/components/editor/inspector-extension-actions.tsx +36 -0
  28. package/src/components/editor/localized-html.tsx +22 -0
  29. package/src/components/editor/measurement-pill.tsx +1 -1
  30. package/src/components/editor/node-action-menu.tsx +46 -12
  31. package/src/components/editor/node-arrow-handles.tsx +2 -3
  32. package/src/components/editor/open-wall-ends-3d-layer.tsx +2 -3
  33. package/src/components/editor/open-wall-ends-hint.tsx +1 -1
  34. package/src/components/editor/opening-guides-3d-layer.tsx +1 -1
  35. package/src/components/editor/quick-measurement-card.tsx +1 -1
  36. package/src/components/editor/registry-action-contributions.test.tsx +67 -6
  37. package/src/components/editor/registry-action-contributions.tsx +115 -15
  38. package/src/components/editor/riser-diagram-panel.tsx +59 -9
  39. package/src/components/editor/room-controls.tsx +4 -3
  40. package/src/components/editor/room-handles-2d.tsx +158 -7
  41. package/src/components/editor/room-handles.tsx +244 -54
  42. package/src/components/editor/room-highlight.tsx +2 -0
  43. package/src/components/editor/room-labels.tsx +33 -17
  44. package/src/components/editor/selection-manager.tsx +15 -102
  45. package/src/components/editor/separator-end-grip.tsx +74 -0
  46. package/src/components/editor/site-edge-labels.tsx +7 -3
  47. package/src/components/editor/thumbnail-generator.test.ts +15 -6
  48. package/src/components/editor/thumbnail-generator.tsx +26 -1
  49. package/src/components/editor/use-floorplan-background-placement.ts +55 -52
  50. package/src/components/editor/views/view-bar.tsx +111 -26
  51. package/src/components/editor/wall-measurement-label.tsx +31 -15
  52. package/src/components/editor/wall-move-side-handles.tsx +15 -7
  53. package/src/components/editor/wall-pull-grip.tsx +193 -0
  54. package/src/components/editor/wall-region-handles.tsx +2 -2
  55. package/src/components/editor/wall-snap-beacon-layer.tsx +134 -33
  56. package/src/components/editor-2d/floorplan-adjacent-walls-layer.test.ts +76 -0
  57. package/src/components/editor-2d/floorplan-adjacent-walls-layer.tsx +186 -0
  58. package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +63 -3
  59. package/src/components/editor-2d/floorplan-group-move.tsx +32 -4
  60. package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +6 -0
  61. package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +33 -24
  62. package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +12 -5
  63. package/src/components/editor-2d/floorplan-registry-action-menu.tsx +2 -1
  64. package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +29 -5
  65. package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +96 -4
  66. package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +52 -12
  67. package/src/components/editor-2d/renderers/floorplan-draft-measurement.tsx +7 -1
  68. package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +13 -1
  69. package/src/components/editor-2d/renderers/floorplan-placement-typing-hud.test.tsx +35 -0
  70. package/src/components/editor-2d/renderers/floorplan-placement-typing-hud.tsx +65 -0
  71. package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +34 -1
  72. package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +211 -39
  73. package/src/components/procedural-items/inspector.tsx +59 -12
  74. package/src/components/systems/ceiling/ceiling-bracket-batch.test.ts +2 -0
  75. package/src/components/systems/ceiling/ceiling-system.test.ts +141 -0
  76. package/src/components/systems/ceiling/ceiling-system.tsx +53 -18
  77. package/src/components/systems/roof/roof-edit-system.test.tsx +15 -1
  78. package/src/components/systems/roof/roof-edit-system.tsx +30 -19
  79. package/src/components/tools/distribution/distribution-run-tool.test.tsx +371 -3
  80. package/src/components/tools/distribution/distribution-run-tool.tsx +200 -58
  81. package/src/components/tools/item/move-tool.tsx +1 -6
  82. package/src/components/tools/item/placement-history.test.tsx +1 -1
  83. package/src/components/tools/item/placement-strategies.test.ts +64 -1
  84. package/src/components/tools/item/placement-strategies.ts +5 -9
  85. package/src/components/tools/item/use-placement-coordinator.tsx +31 -35
  86. package/src/components/tools/paint-region/wall-paint-region-tool.tsx +1 -1
  87. package/src/components/tools/registry/item-surface-move.test.ts +11 -0
  88. package/src/components/tools/registry/move-registry-node-tool.test.ts +42 -1
  89. package/src/components/tools/registry/move-registry-node-tool.tsx +38 -14
  90. package/src/components/tools/select/select-candidates.test.ts +45 -0
  91. package/src/components/tools/select/select-candidates.ts +13 -2
  92. package/src/components/tools/shared/cursor-sphere.tsx +10 -3
  93. package/src/components/tools/shared/draft-measurement-label.tsx +1 -1
  94. package/src/components/tools/shared/placement-box.tsx +2 -6
  95. package/src/components/tools/shared/placement-coordinate-input.tsx +94 -0
  96. package/src/components/tools/shared/placement-dimension-guides.tsx +1 -1
  97. package/src/components/tools/shared/segment-angle.ts +0 -4
  98. package/src/components/tools/site/terrain-brush-cursor.tsx +1 -1
  99. package/src/components/tools/tool-manager.tsx +1 -24
  100. package/src/components/tools/wall/wall-drafting.test.ts +299 -23
  101. package/src/components/tools/wall/wall-drafting.ts +132 -24
  102. package/src/components/tools/wall/wall-pull-snap.test.ts +274 -0
  103. package/src/components/tools/wall/wall-pull-snap.ts +563 -0
  104. package/src/components/tools/wall/wall-snap-geometry.test.ts +113 -1
  105. package/src/components/tools/wall/wall-snap-geometry.ts +137 -0
  106. package/src/components/tools/wall/wall-typed-entry.test.ts +99 -0
  107. package/src/components/tools/wall/wall-typed-entry.ts +70 -0
  108. package/src/components/tools/zone/zone-tool.tsx +3 -3
  109. package/src/components/ui/action-menu/action-button.tsx +2 -0
  110. package/src/components/ui/action-menu/camera-actions.tsx +8 -7
  111. package/src/components/ui/action-menu/control-modes.tsx +9 -5
  112. package/src/components/ui/action-menu/follow-pascal-pill.tsx +3 -1
  113. package/src/components/ui/action-menu/follow-pascal-toggle.tsx +7 -1
  114. package/src/components/ui/action-menu/furnish-tools.tsx +7 -6
  115. package/src/components/ui/action-menu/index.tsx +1 -0
  116. package/src/components/ui/action-menu/measurement-control.tsx +35 -29
  117. package/src/components/ui/action-menu/point-ask-button.tsx +4 -3
  118. package/src/components/ui/action-menu/structure-tools.tsx +25 -23
  119. package/src/components/ui/action-menu/view-toggles.test.ts +18 -0
  120. package/src/components/ui/action-menu/view-toggles.tsx +256 -51
  121. package/src/components/ui/build-panel/build-panel.tsx +163 -25
  122. package/src/components/ui/command-palette/editor-commands.tsx +38 -11
  123. package/src/components/ui/controls/finish-swatch.tsx +45 -19
  124. package/src/components/ui/controls/material-picker.tsx +24 -17
  125. package/src/components/ui/controls/metric-control.test.ts +52 -0
  126. package/src/components/ui/controls/metric-control.tsx +62 -24
  127. package/src/components/ui/controls/segmented-control.tsx +5 -2
  128. package/src/components/ui/controls/slider-control.test.tsx +23 -0
  129. package/src/components/ui/controls/slider-control.tsx +47 -19
  130. package/src/components/ui/controls/tool-options-panel.tsx +74 -44
  131. package/src/components/ui/floating-level-selector.tsx +100 -48
  132. package/src/components/ui/helpers/building-helper.tsx +5 -3
  133. package/src/components/ui/helpers/contextual-helper-panel.tsx +97 -37
  134. package/src/components/ui/helpers/helper-manager.test.tsx +106 -3
  135. package/src/components/ui/helpers/helper-manager.tsx +136 -57
  136. package/src/components/ui/helpers/item-helper.tsx +6 -4
  137. package/src/components/ui/helpers/registered-tool-helper.tsx +3 -1
  138. package/src/components/ui/level-duplicate-dialog.tsx +32 -23
  139. package/src/components/ui/level-wall-copy-prompt.tsx +124 -0
  140. package/src/components/ui/panels/collections/collections-popover.tsx +11 -11
  141. package/src/components/ui/panels/deck-boards-fields.tsx +136 -0
  142. package/src/components/ui/panels/floor-foundation-panel.tsx +114 -39
  143. package/src/components/ui/panels/floor-opening-panel.tsx +74 -26
  144. package/src/components/ui/panels/inspector-extensions.tsx +66 -0
  145. package/src/components/ui/panels/mobile-panel-sheet.tsx +3 -1
  146. package/src/components/ui/panels/mobile-selection-bar.tsx +34 -28
  147. package/src/components/ui/panels/multi-field-value.test.ts +27 -0
  148. package/src/components/ui/panels/multi-field-value.ts +20 -1
  149. package/src/components/ui/panels/multi-height-mode.tsx +11 -10
  150. package/src/components/ui/panels/multi-parametric-inspector.tsx +122 -34
  151. package/src/components/ui/panels/multi-selection-panel.tsx +76 -36
  152. package/src/components/ui/panels/node-display.ts +35 -24
  153. package/src/components/ui/panels/panel-manager.tsx +9 -5
  154. package/src/components/ui/panels/panel-wrapper.tsx +63 -127
  155. package/src/components/ui/panels/parametric-field-control.tsx +13 -10
  156. package/src/components/ui/panels/parametric-inspector.tsx +18 -12
  157. package/src/components/ui/panels/reference-panel.tsx +51 -35
  158. package/src/components/ui/panels/room-construction-rows.tsx +465 -66
  159. package/src/components/ui/panels/room-name-options.test.ts +36 -3
  160. package/src/components/ui/panels/room-name-options.ts +110 -11
  161. package/src/components/ui/panels/room-openings-field.tsx +22 -13
  162. package/src/components/ui/panels/room-panel.tsx +207 -100
  163. package/src/components/ui/panels/shape-choice.tsx +7 -5
  164. package/src/components/ui/panels/wall-region-list.tsx +7 -7
  165. package/src/components/ui/primitives/shortcut-token.tsx +10 -6
  166. package/src/components/ui/right-stack.test.ts +13 -6
  167. package/src/components/ui/right-stack.tsx +21 -10
  168. package/src/components/ui/sidebar/icon-rail.tsx +11 -9
  169. package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +10 -10
  170. package/src/components/ui/sidebar/panels/items-panel/index.tsx +10 -9
  171. package/src/components/ui/sidebar/panels/settings-panel/index.tsx +3 -0
  172. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +102 -32
  173. package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +1 -5
  174. package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +4 -3
  175. package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +5 -3
  176. package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +4 -2
  177. package/src/components/ui/sidebar/panels/site-panel/collection-tree-node.tsx +7 -5
  178. package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +4 -2
  179. package/src/components/ui/sidebar/panels/site-panel/default-names.ts +75 -0
  180. package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +4 -2
  181. package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +4 -2
  182. package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +4 -2
  183. package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +4 -2
  184. package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +4 -2
  185. package/src/components/ui/sidebar/panels/site-panel/index.tsx +118 -72
  186. package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +36 -3
  187. package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +4 -2
  188. package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +7 -4
  189. package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +8 -2
  190. package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +8 -5
  191. package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +4 -2
  192. package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +9 -4
  193. package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +4 -2
  194. package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +4 -2
  195. package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +8 -7
  196. package/src/components/ui/sidebar/panels/site-panel/tree-node-actions.tsx +7 -6
  197. package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +16 -1
  198. package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +15 -8
  199. package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +4 -2
  200. package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +4 -2
  201. package/src/components/ui/sidebar/panels/site-panel/zone-membership-checkbox.tsx +3 -1
  202. package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +19 -9
  203. package/src/components/ui/sidebar/panels/zone-panel/index.tsx +25 -14
  204. package/src/components/ui/sidebar/tab-bar.tsx +4 -1
  205. package/src/components/ui/sidebar/use-plugin-panels.tsx +19 -10
  206. package/src/components/ui/snap-target-badge.tsx +7 -6
  207. package/src/components/ui/wall-type/wall-type-editor-dialog.tsx +721 -0
  208. package/src/components/ui/wall-type/wall-type-menu.tsx +167 -0
  209. package/src/components/viewer/floorplan-compass-button.tsx +12 -3
  210. package/src/components/viewer/floorplan-preview.tsx +27 -19
  211. package/src/components/viewer/render-settings-menu.tsx +294 -0
  212. package/src/components/viewer/sky-control.tsx +710 -0
  213. package/src/components/viewer/use-viewer-ui-sounds.test.ts +15 -0
  214. package/src/components/viewer/use-viewer-ui-sounds.ts +46 -0
  215. package/src/components/viewer/viewer-controls-bar.tsx +278 -260
  216. package/src/components/viewer/viewer-hover-label.tsx +12 -4
  217. package/src/components/viewer/viewer-rooms.tsx +62 -5
  218. package/src/components/viewer/viewer-scene-header.tsx +28 -17
  219. package/src/components/viewer/viewer-stage-switcher.tsx +4 -2
  220. package/src/components/viewer-overlay.tsx +8 -0
  221. package/src/components/walkthrough-hud.tsx +75 -2
  222. package/src/hooks/use-keyboard.test.ts +85 -4
  223. package/src/hooks/use-keyboard.ts +65 -7
  224. package/src/hooks/use-selected-room.ts +12 -1
  225. package/src/i18n/floor-footprint-name.ts +30 -0
  226. package/src/i18n/level-name.ts +39 -0
  227. package/src/i18n/locales/en/messages.po +15849 -0
  228. package/src/i18n/locales/en/messages.ts +1 -0
  229. package/src/i18n/locales/en-XA/messages.ts +1 -0
  230. package/src/i18n/locales/es/messages.po +39331 -0
  231. package/src/i18n/locales/es/messages.ts +1 -0
  232. package/src/i18n/locales/fr/messages.po +39331 -0
  233. package/src/i18n/locales/fr/messages.ts +1 -0
  234. package/src/i18n/locales/ja/messages.po +39331 -0
  235. package/src/i18n/locales/ja/messages.ts +1 -0
  236. package/src/i18n/locales/zh-CN/messages.po +39331 -0
  237. package/src/i18n/locales/zh-CN/messages.ts +1 -0
  238. package/src/i18n/locales.test.ts +63 -0
  239. package/src/i18n/locales.ts +32 -0
  240. package/src/i18n/message-slot.tsx +25 -0
  241. package/src/i18n/node-messages.ts +169 -0
  242. package/src/i18n/pseudo-locale.ts +17 -0
  243. package/src/i18n/published-entry.test.ts +17 -0
  244. package/src/i18n/scene-theme-name.ts +16 -0
  245. package/src/index.tsx +224 -45
  246. package/src/lib/add-storey.test.ts +3 -3
  247. package/src/lib/contextual-help.test.ts +20 -1
  248. package/src/lib/contextual-help.ts +101 -19
  249. package/src/lib/continuation.ts +38 -11
  250. package/src/lib/elevation-guides.ts +21 -6
  251. package/src/lib/elevator-support.ts +1 -72
  252. package/src/lib/floor-footprints.test.ts +3 -2
  253. package/src/lib/floor-footprints.ts +1 -9
  254. package/src/lib/floor-foundation-support.test.ts +15 -6
  255. package/src/lib/floor-opening-draft.ts +1 -1
  256. package/src/lib/floor-region-session.ts +1 -1
  257. package/src/lib/floorplan/dimension-drive.test.ts +36 -2
  258. package/src/lib/floorplan/dimension-drive.ts +68 -3
  259. package/src/lib/floorplan/floorplan-export.test.ts +16 -5
  260. package/src/lib/floorplan/floorplan-export.tsx +13 -16
  261. package/src/lib/floorplan/floorplan-extension.test.ts +11 -12
  262. package/src/lib/floorplan/floorplan-extension.ts +37 -18
  263. package/src/lib/floorplan/floorplan-mode.test.ts +13 -6
  264. package/src/lib/floorplan/floorplan-mode.ts +14 -12
  265. package/src/lib/floorplan/floorplan-plugin-install.test.tsx +1 -1
  266. package/src/lib/floorplan/floorplan-readonly.ts +3 -3
  267. package/src/lib/floorplan/open-wall-ends.ts +2 -1
  268. package/src/lib/floorplan/schedules.ts +10 -6
  269. package/src/lib/floorplan/site-plan/site-annotations.test.ts +46 -1
  270. package/src/lib/floorplan/site-plan/site-annotations.ts +3 -0
  271. package/src/lib/floorplan/site-plan/site-parts.ts +6 -3
  272. package/src/lib/geometry-script/author.test.ts +114 -0
  273. package/src/lib/geometry-script/author.ts +55 -23
  274. package/src/lib/geometry-script/client.ts +3 -0
  275. package/src/lib/geometry-script/finish-scripts.ts +7 -7
  276. package/src/lib/gesture-lifecycle.ts +3 -1
  277. package/src/lib/glb-export.test.ts +327 -0
  278. package/src/lib/glb-export.ts +70 -2
  279. package/src/lib/grid-frame.test.ts +56 -3
  280. package/src/lib/grid-frame.ts +28 -1
  281. package/src/lib/history.test.ts +2 -1
  282. package/src/lib/hud-title.ts +48 -24
  283. package/src/lib/ifc-export.test.ts +41 -1
  284. package/src/lib/inspector-card-mode.test.ts +3 -31
  285. package/src/lib/inspector-card-mode.ts +5 -10
  286. package/src/lib/inspector-expanded.test.ts +13 -0
  287. package/src/lib/inspector-expanded.ts +13 -1
  288. package/src/lib/interaction/scope.ts +1 -0
  289. package/src/lib/keyboard-pan.test.ts +7 -0
  290. package/src/lib/keyboard-pan.ts +9 -2
  291. package/src/lib/layer-thickness.test.ts +22 -0
  292. package/src/lib/layer-thickness.ts +79 -0
  293. package/src/lib/level-selection.test.ts +18 -2
  294. package/src/lib/level-selection.ts +31 -8
  295. package/src/lib/linear-display.ts +1 -1
  296. package/src/lib/live-gesture-round-trip.test.ts +1 -1
  297. package/src/lib/lot/drop-in.test.ts +59 -1
  298. package/src/lib/lot/drop-in.ts +42 -50
  299. package/src/lib/material-swatch-style.ts +24 -0
  300. package/src/lib/measurement-label.test.ts +1 -1
  301. package/src/lib/measurement-parser.ts +7 -3
  302. package/src/lib/measurements.test.ts +24 -6
  303. package/src/lib/measurements.ts +1 -203
  304. package/src/lib/mezzanine-selection.test.ts +3 -1
  305. package/src/lib/node-action-movement.test.tsx +115 -0
  306. package/src/lib/node-action-movement.ts +2 -0
  307. package/src/lib/orbit-near-plane.test.ts +12 -0
  308. package/src/lib/orbit-near-plane.ts +13 -0
  309. package/src/lib/paint-regions.test.ts +8 -5
  310. package/src/lib/paint-scope.test.ts +53 -0
  311. package/src/lib/paint-scope.ts +18 -4
  312. package/src/lib/panel-rows.ts +17 -8
  313. package/src/lib/placement-metadata.ts +4 -0
  314. package/src/lib/point-ask/describe-target.ts +6 -8
  315. package/src/lib/point-ask/region.ts +1 -1
  316. package/src/lib/print-shell-compiler.ts +12 -1
  317. package/src/lib/print-wall-solids.test.ts +11 -0
  318. package/src/lib/print-wall-solids.ts +9 -0
  319. package/src/lib/reference-scale.ts +83 -0
  320. package/src/lib/room-assembly-overlay.ts +1 -1
  321. package/src/lib/room-built-on.test.ts +28 -17
  322. package/src/lib/room-built-on.ts +21 -17
  323. package/src/lib/room-construction-commands.ts +3 -2
  324. package/src/lib/room-construction.test.ts +28 -1
  325. package/src/lib/room-construction.ts +14 -0
  326. package/src/lib/room-controls-phase6.test.ts +141 -1
  327. package/src/lib/room-handle-drag.ts +554 -58
  328. package/src/lib/room-name-catalog.test.ts +8 -0
  329. package/src/lib/room-name-catalog.ts +8 -1
  330. package/src/lib/room-name-messages.test.ts +132 -0
  331. package/src/lib/room-name-messages.ts +147 -0
  332. package/src/lib/room-name-storage.test.ts +334 -0
  333. package/src/lib/room-push-dimensions.test.ts +38 -1
  334. package/src/lib/room-push-dimensions.ts +226 -2
  335. package/src/lib/room-selection.test.ts +23 -6
  336. package/src/lib/scene-bounds.test.ts +144 -0
  337. package/src/lib/scene-bounds.ts +79 -15
  338. package/src/lib/scene-clipboard.test.ts +70 -49
  339. package/src/lib/scene-clipboard.ts +11 -5
  340. package/src/lib/selection-cycle.ts +63 -0
  341. package/src/lib/separator-edit.test.ts +431 -0
  342. package/src/lib/separator-edit.ts +179 -0
  343. package/src/lib/separator-lines.ts +38 -0
  344. package/src/lib/sfx-bus.ts +27 -1
  345. package/src/lib/sfx-player.ts +16 -0
  346. package/src/lib/snapping-mode.test.ts +23 -1
  347. package/src/lib/tool-cursor-icon.ts +5 -1
  348. package/src/lib/tool-shortcuts.ts +130 -0
  349. package/src/lib/viewer-selection.test.ts +28 -24
  350. package/src/lib/viewer-selection.ts +21 -6
  351. package/src/lib/wall-draw-variant.test.ts +7 -6
  352. package/src/lib/wall-draw-variant.ts +107 -6
  353. package/src/lib/wall-nudge.test.ts +44 -0
  354. package/src/lib/wall-nudge.ts +131 -0
  355. package/src/lib/wall-parallel-snap.test.ts +83 -0
  356. package/src/lib/wall-parallel-snap.ts +176 -0
  357. package/src/lib/wall-placement-pointer.test.ts +23 -0
  358. package/src/lib/wall-placement-pointer.ts +18 -0
  359. package/src/lib/wall-pull.test.ts +245 -0
  360. package/src/lib/wall-pull.ts +209 -0
  361. package/src/lib/wall-push-line-up.test.ts +132 -0
  362. package/src/lib/wall-region-list.ts +17 -6
  363. package/src/lib/wall-run-snap.test.ts +188 -0
  364. package/src/lib/wall-run-snap.ts +83 -0
  365. package/src/lib/wall-type.test.ts +125 -0
  366. package/src/lib/wall-type.ts +119 -0
  367. package/src/lib/window-interaction.test.ts +44 -0
  368. package/src/lib/world-grid-snap.test.ts +139 -2
  369. package/src/lib/world-grid-snap.ts +102 -105
  370. package/src/store/use-alignment-guides.ts +27 -1
  371. package/src/store/use-editor.tsx +53 -0
  372. package/src/store/use-floorplan-annotation-visibility.test.ts +64 -0
  373. package/src/store/use-floorplan-annotation-visibility.ts +47 -22
  374. package/src/store/use-placement-typing.test.ts +165 -0
  375. package/src/store/use-placement-typing.ts +186 -0
  376. package/src/store/use-wall-snap-indicator.ts +15 -2
  377. package/src/components/tools/elevator/elevator-defaults.ts +0 -8
  378. package/src/components/tools/elevator/elevator-tool.tsx +0 -372
  379. package/src/components/tools/elevator/move-elevator-tool.tsx +0 -262
  380. package/src/lib/door-interaction.ts +0 -102
  381. package/src/lib/measurement-label.ts +0 -68
  382. package/src/lib/window-interaction.ts +0 -100
@@ -1,4 +1,4 @@
1
- import { type AnyNodeId, useScene, type WallNode } from '@pascal-app/core'
1
+ import { type AnyNodeId, useScene, usePascalI18n, type WallNode } from '@pascal-app/core'
2
2
  import { useViewer } from '@pascal-app/viewer'
3
3
  import Image from 'next/image'
4
4
  import { memo, useCallback, useEffect, useRef, useState } from 'react'
@@ -6,6 +6,7 @@ import { useShallow } from 'zustand/react/shallow'
6
6
  import { InlineRenameInput } from './inline-rename-input'
7
7
  import { focusTreeNode, handleTreeSelection, TreeNode, TreeNodeWrapper } from './tree-node'
8
8
  import { TreeNodeActions } from './tree-node-actions'
9
+ import { nodeLabelDescriptor } from '../../../../../i18n/node-messages'
9
10
 
10
11
  interface WallTreeNodeProps {
11
12
  nodeId: AnyNodeId
@@ -18,6 +19,7 @@ export const WallTreeNode = memo(function WallTreeNode({
18
19
  depth,
19
20
  isLast,
20
21
  }: WallTreeNodeProps) {
22
+ const i18n = usePascalI18n()
21
23
  const [expanded, setExpanded] = useState(false)
22
24
  const [isEditing, setIsEditing] = useState(false)
23
25
  const isVisible = useScene((s) => s.nodes[nodeId as AnyNodeId]?.visible !== false)
@@ -83,7 +85,7 @@ export const WallTreeNode = memo(function WallTreeNode({
83
85
  isVisible={isVisible}
84
86
  label={
85
87
  <InlineRenameInput
86
- defaultName="Wall"
88
+ defaultName={i18n._(nodeLabelDescriptor('wall', 'Wall'))}
87
89
  isEditing={isEditing}
88
90
  nodeId={nodeId as AnyNodeId}
89
91
  onStartEditing={handleStartEditing}
@@ -1,6 +1,6 @@
1
1
  'use client'
2
2
 
3
- import { type AnyNodeId, type WindowNode, useScene } from '@pascal-app/core'
3
+ import { type AnyNodeId, type WindowNode, useScene, usePascalI18n } from '@pascal-app/core'
4
4
  import { useViewer } from '@pascal-app/viewer'
5
5
  import Image from 'next/image'
6
6
  import { memo, useCallback, useState } from 'react'
@@ -8,6 +8,7 @@ import { resolveNodeSnapTarget, SnapTargetIcon } from '../../../snap-target-badg
8
8
  import { InlineRenameInput } from './inline-rename-input'
9
9
  import { focusTreeNode, handleTreeSelection, TreeNodeWrapper } from './tree-node'
10
10
  import { TreeNodeActions } from './tree-node-actions'
11
+ import { nodeLabelDescriptor } from '../../../../../i18n/node-messages'
11
12
 
12
13
  interface WindowTreeNodeProps {
13
14
  nodeId: AnyNodeId
@@ -20,6 +21,7 @@ export const WindowTreeNode = memo(function WindowTreeNode({
20
21
  depth,
21
22
  isLast,
22
23
  }: WindowTreeNodeProps) {
24
+ const i18n = usePascalI18n()
23
25
  const [isEditing, setIsEditing] = useState(false)
24
26
  const isVisible = useScene((s) => s.nodes[nodeId as AnyNodeId]?.visible !== false)
25
27
  const node = useScene((s) => s.nodes[nodeId] as WindowNode | undefined)
@@ -62,7 +64,7 @@ export const WindowTreeNode = memo(function WindowTreeNode({
62
64
  isVisible={isVisible}
63
65
  label={
64
66
  <InlineRenameInput
65
- defaultName="Window"
67
+ defaultName={i18n._(nodeLabelDescriptor('window', 'Window'))}
66
68
  isEditing={isEditing}
67
69
  nodeId={nodeId as AnyNodeId}
68
70
  onStartEditing={handleStartEditing}
@@ -1,3 +1,4 @@
1
+ import { usePascalI18n } from '@pascal-app/core'
1
2
  import { Checkbox } from './../../../primitives/checkbox'
2
3
 
3
4
  /** Membership toggle shown on zone rows while a unit is focused. */
@@ -10,9 +11,10 @@ export function ZoneMembershipCheckbox({
10
11
  onToggle: () => void
11
12
  unitName: string
12
13
  }) {
14
+ const i18n = usePascalI18n()
13
15
  return (
14
16
  <Checkbox
15
- aria-label={`In ${unitName}`}
17
+ aria-label={i18n._({ id: 'editor.sidebar.panels.sitePanel.zoneMembershipCheckbox.inUnitname', message: 'In {unitName}', values: { unitName } })}
16
18
  checked={checked}
17
19
  className="mr-2"
18
20
  onCheckedChange={onToggle}
@@ -1,18 +1,19 @@
1
- import { useScene, type ZoneNode } from '@pascal-app/core'
1
+ import { useScene, type ZoneNode, usePascalI18n} from '@pascal-app/core'
2
2
  import { useViewer } from '@pascal-app/viewer'
3
3
  import { memo, useCallback, useState } from 'react'
4
4
  import { ColorDot } from './../../../../../components/ui/primitives/color-dot'
5
- import { formatAreaLabel } from './../../../../../lib/measurements'
5
+ import { formatAreaLabel } from '@pascal-app/core'
6
6
  import {
7
7
  selectZoneOrRoom,
8
8
  useZoneSelected,
9
- zoneKindLabel,
10
9
  } from './../../../../../lib/room-zone-routing'
11
10
  import { toggleZoneMembership } from './../../../../../lib/units'
12
- import { InlineRenameInput } from './inline-rename-input'
11
+ import { RoomRenameInput } from './inline-rename-input'
13
12
  import { focusTreeNode, TreeNodeWrapper } from './tree-node'
14
13
  import { TreeNodeActions } from './tree-node-actions'
15
14
  import { ZoneMembershipCheckbox } from './zone-membership-checkbox'
15
+ import { zoneDefaultName } from './default-names'
16
+ import { nodeLabelDescriptor } from '../../../../../i18n/node-messages'
16
17
 
17
18
  interface ZoneTreeNodeProps {
18
19
  nodeId: ZoneNode['id']
@@ -25,13 +26,14 @@ export const ZoneTreeNode = memo(function ZoneTreeNode({
25
26
  depth,
26
27
  isLast,
27
28
  }: ZoneTreeNodeProps) {
29
+ const i18n = usePascalI18n()
28
30
  const [isEditing, setIsEditing] = useState(false)
29
31
  const updateNode = useScene((state) => state.updateNode)
30
32
  const isVisible = useScene((s) => s.nodes[nodeId]?.visible !== false)
31
33
  const color = useScene((s) => (s.nodes[nodeId] as ZoneNode | undefined)?.color)
32
34
  const polygon = useScene((s) => (s.nodes[nodeId] as ZoneNode | undefined)?.polygon ?? [])
33
35
  const isSelected = useZoneSelected(nodeId)
34
- const kind = useScene((s) => zoneKindLabel(s.nodes[nodeId] as ZoneNode | undefined))
36
+ const spaceRole = useScene((s) => (s.nodes[nodeId] as ZoneNode | undefined)?.spaceRole)
35
37
  const isHovered = useViewer((state) => state.hoveredId === nodeId)
36
38
  const setHoveredId = useViewer((state) => state.setHoveredId)
37
39
  const unit = useViewer((state) => state.unit)
@@ -48,7 +50,7 @@ export const ZoneTreeNode = memo(function ZoneTreeNode({
48
50
  const handleStartEditing = useCallback(() => setIsEditing(true), [])
49
51
  const handleStopEditing = useCallback(() => setIsEditing(false), [])
50
52
 
51
- const defaultName = `${kind} (${formatAreaLabel(calculatePolygonArea(polygon), unit)})`
53
+ const defaultName = zoneDefaultName(i18n, { spaceRole }, formatAreaLabel(calculatePolygonArea(polygon), unit))
52
54
 
53
55
  return (
54
56
  <TreeNodeWrapper
@@ -57,7 +59,15 @@ export const ZoneTreeNode = memo(function ZoneTreeNode({
57
59
  expanded={false}
58
60
  hasChildren={false}
59
61
  icon={
60
- <ColorDot color={color ?? '#3b82f6'} onChange={(c) => updateNode(nodeId, { color: c })} />
62
+ <ColorDot
63
+ color={color ?? '#3b82f6'}
64
+ label={i18n._({
65
+ id: 'editor.layers.spaceColour',
66
+ message: '{role, select, room {Room colour} other {Zone colour}}',
67
+ values: { role: spaceRole ?? 'generic' },
68
+ })}
69
+ onChange={(c) => updateNode(nodeId, { color: c })}
70
+ />
61
71
  }
62
72
  isHovered={isHovered}
63
73
  isLast={isLast}
@@ -70,10 +80,10 @@ export const ZoneTreeNode = memo(function ZoneTreeNode({
70
80
  <ZoneMembershipCheckbox
71
81
  checked={focusedUnit.members.includes(nodeId)}
72
82
  onToggle={() => toggleZoneMembership(focusedUnit.id, nodeId)}
73
- unitName={focusedUnit.name || 'Unit'}
83
+ unitName={focusedUnit.name || i18n._(nodeLabelDescriptor('unit', 'Unit'))}
74
84
  />
75
85
  )}
76
- <InlineRenameInput
86
+ <RoomRenameInput
77
87
  defaultName={defaultName}
78
88
  isEditing={isEditing}
79
89
  nodeId={nodeId}
@@ -2,7 +2,7 @@ import {
2
2
  type AnyNodeId,
3
3
  emitter,
4
4
  useScene,
5
- type ZoneNode,
5
+ type ZoneNode, usePascalI18n,
6
6
  } from '@pascal-app/core'
7
7
  import { useViewer } from '@pascal-app/viewer'
8
8
  import { Camera, Hexagon, Save, Trash2 } from 'lucide-react'
@@ -19,8 +19,10 @@ import { cn } from './../../../../../lib/utils'
19
19
  import useEditor from './../../../../../store/use-editor'
20
20
  import { ActionButton } from '../../../controls/action-button'
21
21
  import { PanelSection } from '../../../controls/panel-section'
22
+ import { displayRoomName } from '../../../../../lib/room-name-messages'
22
23
 
23
24
  function ZoneItem({ zone }: { zone: ZoneNode }) {
25
+ const i18n = usePascalI18n()
24
26
  const [cameraPopoverOpen, setCameraPopoverOpen] = useState(false)
25
27
  const deleteNode = useScene((state) => state.deleteNode)
26
28
  const updateNode = useScene((state) => state.updateNode)
@@ -57,17 +59,25 @@ function ZoneItem({ zone }: { zone: ZoneNode }) {
57
59
  onClick={handleClick}
58
60
  >
59
61
  <span className="mr-2">
60
- <ColorDot color={zone.color} onChange={handleColorChange} />
62
+ <ColorDot
63
+ color={zone.color}
64
+ label={i18n._({
65
+ id: 'editor.layers.spaceColour',
66
+ message: '{role, select, room {Room colour} other {Zone colour}}',
67
+ values: { role: zone.spaceRole ?? 'generic' },
68
+ })}
69
+ onChange={handleColorChange}
70
+ />
61
71
  </span>
62
72
  <Hexagon className="mr-1.5 h-3.5 w-3.5 shrink-0" />
63
- <span className="flex-1 truncate">{zone.name}</span>
73
+ <span className="flex-1 truncate">{displayRoomName(zone.name ?? '', i18n)}</span>
64
74
  {/* Camera snapshot button */}
65
75
  <Popover onOpenChange={setCameraPopoverOpen} open={cameraPopoverOpen}>
66
76
  <PopoverTrigger asChild>
67
77
  <button
68
78
  className="relative flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground opacity-0 transition-colors hover:bg-black/5 hover:text-foreground group-hover/row:opacity-100 dark:hover:bg-white/10"
69
79
  onClick={(e) => e.stopPropagation()}
70
- title="Camera snapshot"
80
+ title={i18n._({ id: 'editor.sidebar.panels.zonePanel.cameraSnapshot', message: 'Camera snapshot' })}
71
81
  >
72
82
  <Camera className="h-3 w-3" />
73
83
  {zone.camera && (
@@ -92,7 +102,7 @@ function ZoneItem({ zone }: { zone: ZoneNode }) {
92
102
  }}
93
103
  >
94
104
  <Camera className="h-3.5 w-3.5" />
95
- View snapshot
105
+ {i18n._({ id: 'editor.sidebar.panels.zonePanel.viewSnapshot', message: 'View snapshot' })}
96
106
  </button>
97
107
  )}
98
108
  <button
@@ -104,7 +114,7 @@ function ZoneItem({ zone }: { zone: ZoneNode }) {
104
114
  }}
105
115
  >
106
116
  <Camera className="h-3.5 w-3.5" />
107
- {zone.camera ? 'Update snapshot' : 'Take snapshot'}
117
+ {zone.camera ? i18n._({ id: 'editor.sidebar.panels.zonePanel.updateSnapshot', message: 'Update snapshot' }) : i18n._({ id: 'editor.sidebar.panels.zonePanel.takeSnapshot', message: 'Take snapshot' })}
108
118
  </button>
109
119
  {zone.camera && (
110
120
  <button
@@ -116,7 +126,7 @@ function ZoneItem({ zone }: { zone: ZoneNode }) {
116
126
  }}
117
127
  >
118
128
  <Trash2 className="h-3.5 w-3.5" />
119
- Clear snapshot
129
+ {i18n._({ id: 'editor.sidebar.panels.zonePanel.clearSnapshot', message: 'Clear snapshot' })}
120
130
  </button>
121
131
  )}
122
132
  </div>
@@ -133,6 +143,7 @@ function ZoneItem({ zone }: { zone: ZoneNode }) {
133
143
  }
134
144
 
135
145
  export function ZonePanel() {
146
+ const i18n = usePascalI18n()
136
147
  const nodes = useScene((state) => state.nodes)
137
148
  const currentLevelId = useViewer((state) => state.selection.levelId)
138
149
  const selectedZoneId = useViewer((state) => state.selection.zoneId)
@@ -167,7 +178,7 @@ export function ZonePanel() {
167
178
  if (!currentLevelId) {
168
179
  return (
169
180
  <div className="px-3 py-4 text-muted-foreground text-sm">
170
- Select a level to view and create zones
181
+ {i18n._({ id: 'editor.sidebar.panels.zonePanel.selectALevelToViewAnd', message: 'Select a level to view and create zones' })}
171
182
  </div>
172
183
  )
173
184
  }
@@ -176,34 +187,34 @@ export function ZonePanel() {
176
187
  <div className="py-1">
177
188
  {levelZones.length === 0 ? (
178
189
  <div className="px-3 py-4 text-muted-foreground text-sm">
179
- No zones on this level.{' '}
190
+ {i18n._({ id: 'editor.sidebar.panels.zonePanel.noZonesOnThisLevel', message: 'No zones on this level.' })}{' '}
180
191
  <button className="cursor-pointer text-primary hover:underline" onClick={handleAddZone}>
181
- Add one
192
+ {i18n._({ id: 'editor.sidebar.panels.zonePanel.addOne', message: 'Add one' })}
182
193
  </button>
183
194
  </div>
184
195
  ) : (
185
196
  levelZones.map((zone) => <ZoneItem key={zone.id} zone={zone} />)
186
197
  )}
187
198
  {selectedZone ? (
188
- <PanelSection className="mt-2 border-t" title="Actions">
199
+ <PanelSection className="mt-2 border-t" title={i18n._({ id: 'editor.sidebar.panels.zonePanel.actions', message: 'Actions' })}>
189
200
  <ActionButton
190
201
  className="w-full flex-none"
191
202
  icon={<Save className="h-4 w-4" />}
192
- label="Save to catalog"
203
+ label={i18n._({ id: 'editor.sidebar.panels.zonePanel.saveToCatalog', message: 'Save to catalog' })}
193
204
  onClick={() => emitter.emit('room-preset:create', { zoneId: selectedZone.id })}
194
205
  type="button"
195
206
  />
196
207
  <ActionButton
197
208
  className="w-full flex-none"
198
209
  icon={<Trash2 className="h-4 w-4 text-red-600 dark:text-red-400" />}
199
- label="Delete"
210
+ label={i18n._({ id: 'editor.sidebar.panels.zonePanel.delete', message: 'Delete' })}
200
211
  onClick={() => deleteSelectedZone(false)}
201
212
  type="button"
202
213
  />
203
214
  <ActionButton
204
215
  className="w-full flex-none"
205
216
  icon={<Trash2 className="h-4 w-4 text-red-600 dark:text-red-400" />}
206
- label="Delete with contents"
217
+ label={i18n._({ id: 'editor.sidebar.panels.zonePanel.deleteWithContents', message: 'Delete with contents' })}
207
218
  onClick={() => deleteSelectedZone(true)}
208
219
  type="button"
209
220
  />
@@ -121,7 +121,10 @@ export function IconRail({ tabs, activeTab, collapsed, onIconClick }: IconRailPr
121
121
 
122
122
  return (
123
123
  <TooltipProvider delayDuration={0} disableHoverableContent>
124
- <div className="flex h-full w-14 shrink-0 flex-col items-center gap-1 border-border/50 border-r py-2">
124
+ <div
125
+ className="flex h-full w-14 shrink-0 flex-col items-center gap-1 border-border/50 border-r py-2"
126
+ data-surface="icon-rail"
127
+ >
125
128
  {defaultTabs.map(renderTab)}
126
129
  {pluginTabs.length > 0 && (
127
130
  <div className="mt-1 flex w-11 flex-col items-center gap-1 border-border/70 border-t pt-2">
@@ -1,7 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import { Icon } from '@iconify/react'
4
- import { type IconRef, useScene } from '@pascal-app/core'
4
+ import { type IconRef, useScene, usePascalI18n } from '@pascal-app/core'
5
5
  import { Plus } from 'lucide-react'
6
6
  import {
7
7
  type ComponentType,
@@ -28,9 +28,8 @@ import type { ExtraPanel } from './icon-rail'
28
28
  // The rail's "+" puts the plugin directory on the stage, as a view in the
29
29
  // focused pane, and stays lit while it is there instead of leaving an
30
30
  // unrelated panel open beside it. `component` serves the v1 sidebar.
31
- const pluginsManagerPanel: ExtraPanel = {
31
+ const pluginsManagerPanel: Omit<ExtraPanel, 'label'> = {
32
32
  id: 'plugins',
33
- label: 'Plugins',
34
33
  icon: <img alt="" className="size-8 object-contain" src="/icons/plugins.webp" />,
35
34
  component: PluginsView,
36
35
  noPanel: true,
@@ -63,12 +62,12 @@ function renderIconRef(ref: IconRef): ReactNode {
63
62
  }
64
63
 
65
64
  function PluginPanelCrashed({ label }: { label: string }) {
65
+ const i18n = usePascalI18n()
66
66
  return (
67
67
  <div className="flex flex-col gap-2 p-4 text-sm">
68
- <p className="font-medium text-sidebar-foreground">"{label}" plugin crashed</p>
68
+ <p className="font-medium text-sidebar-foreground">{i18n._({ id: 'editor.sidebar.usePluginPanels.labelPluginCrashed', message: '"{label}" plugin crashed', values: { label } })}</p>
69
69
  <p className="text-sidebar-foreground/50 text-xs">
70
- This panel hit an error and was unloaded for this session. The rest of the editor is
71
- unaffected — reload to try again.
70
+ {i18n._({ id: 'editor.sidebar.usePluginPanels.thisPanelHitAnErrorAnd', message: 'This panel hit an error and was unloaded for this session. The rest of the editor is unaffected — reload to try again.' })}
72
71
  </p>
73
72
  </div>
74
73
  )
@@ -83,13 +82,16 @@ function resolvePanelComponent(panel: EditorHostPanel): ComponentType {
83
82
  const cached = wrappedPanelCache.get(panel.component)
84
83
  if (cached) return cached
85
84
  const Lazy = lazy(panel.component)
86
- const Wrapped: ComponentType = () => (
85
+ const Wrapped: ComponentType = () => {
86
+ const i18n = usePascalI18n()
87
+ return ((
87
88
  <ErrorBoundary fallback={<PluginPanelCrashed label={panel.label} />}>
88
- <Suspense fallback={<div className="p-4 text-sidebar-foreground/50 text-sm">Loading…</div>}>
89
+ <Suspense fallback={<div className="p-4 text-sidebar-foreground/50 text-sm">{i18n._({ id: 'editor.sidebar.usePluginPanels.loading', message: 'Loading…' })}</div>}>
89
90
  <Lazy />
90
91
  </Suspense>
91
92
  </ErrorBoundary>
92
- )
93
+ ))
94
+ }
93
95
  Wrapped.displayName = `PluginPanel(${panel.id})`
94
96
  wrappedPanelCache.set(panel.component, Wrapped)
95
97
  return Wrapped
@@ -107,6 +109,7 @@ function resolvePanelComponent(panel: EditorHostPanel): ComponentType {
107
109
  * authoring panel like Nature doesn't ride into the studio rail.
108
110
  */
109
111
  export function useHostPanels(hostPanels?: ExtraPanel[]): ExtraPanel[] {
112
+ const i18n = usePascalI18n()
110
113
  const registered = useSyncExternalStore(
111
114
  editorHostPanelRegistry.subscribe,
112
115
  editorHostPanelRegistry.getSnapshot,
@@ -151,7 +154,13 @@ export function useHostPanels(hostPanels?: ExtraPanel[]): ExtraPanel[] {
151
154
  readOnly,
152
155
  workspaceMode,
153
156
  })
154
- ? [{ ...pluginsManagerPanel, stageActive: pluginsViewShown }]
157
+ ? [
158
+ {
159
+ ...pluginsManagerPanel,
160
+ label: i18n._({ id: 'editor.sidebar.usePluginPanels.plugins', message: 'Plugins' }),
161
+ stageActive: pluginsViewShown,
162
+ },
163
+ ]
155
164
  : []
156
165
  return [...(hostPanels ?? []), ...fromRegistry, ...manager]
157
166
  }
@@ -1,4 +1,4 @@
1
- import type { AnyNode, AssetInput } from '@pascal-app/core'
1
+ import { type AnyNode, type AssetInput, type MessageDescriptor, usePascalI18n } from '@pascal-app/core'
2
2
  import { isProceduralItem } from '@pascal-app/core/procedural-items'
3
3
  import type { ReactNode } from 'react'
4
4
  import { cn } from '../../lib/utils'
@@ -12,10 +12,10 @@ const SNAP_TARGET_ICONS: Record<SnapTarget, string> = {
12
12
  roof: '/icons/roof.webp',
13
13
  }
14
14
 
15
- const SNAP_TARGET_LABELS: Record<SnapTarget, string> = {
16
- wall: 'Wall attachment',
17
- ceiling: 'Ceiling attachment',
18
- roof: 'Roof attachment',
15
+ const SNAP_TARGET_LABELS: Record<SnapTarget, MessageDescriptor> = {
16
+ wall: /* i18n */ { id: 'editor.snapTarget.wall', message: 'Wall attachment' },
17
+ ceiling: /* i18n */ { id: 'editor.snapTarget.ceiling', message: 'Ceiling attachment' },
18
+ roof: /* i18n */ { id: 'editor.snapTarget.roof', message: 'Roof attachment' },
19
19
  }
20
20
 
21
21
  const SNAP_TARGET_BADGE_SIZE_CLASSES: Record<SnapTargetBadgeSize, string> = {
@@ -53,6 +53,7 @@ export function SnapTargetBadge({
53
53
  size?: SnapTargetBadgeSize
54
54
  target: SnapTarget
55
55
  }) {
56
+ const i18n = usePascalI18n()
56
57
  return (
57
58
  <span
58
59
  className={cn(
@@ -62,7 +63,7 @@ export function SnapTargetBadge({
62
63
  )}
63
64
  >
64
65
  <img
65
- alt={SNAP_TARGET_LABELS[target]}
66
+ alt={i18n._(SNAP_TARGET_LABELS[target])}
66
67
  className={cn('object-contain', SNAP_TARGET_ICON_SIZE_CLASSES[size])}
67
68
  src={SNAP_TARGET_ICONS[target]}
68
69
  />