@pascal-app/editor 1.0.6 → 1.0.7

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 (317) 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 +20 -8
  6. package/src/components/editor/elevation-3d-guide-layer.tsx +1 -1
  7. package/src/components/editor/first-person-controls.tsx +72 -61
  8. package/src/components/editor/floating-action-menu.tsx +27 -9
  9. package/src/components/editor/floating-building-action-menu.tsx +1 -1
  10. package/src/components/editor/floorplan-panel.test.ts +220 -0
  11. package/src/components/editor/floorplan-panel.tsx +285 -132
  12. package/src/components/editor/footprint-height-handle.tsx +1 -1
  13. package/src/components/editor/grid.tsx +24 -15
  14. package/src/components/editor/group-actions.ts +19 -1
  15. package/src/components/editor/group-floating-action-menu.tsx +1 -1
  16. package/src/components/editor/group-move-3d.ts +27 -2
  17. package/src/components/editor/group-selection-box-3d.tsx +4 -1
  18. package/src/components/editor/group-transform-shared.ts +20 -0
  19. package/src/components/editor/index.tsx +130 -20
  20. package/src/components/editor/inspector-extension-actions.tsx +36 -0
  21. package/src/components/editor/localized-html.tsx +22 -0
  22. package/src/components/editor/node-action-menu.tsx +46 -12
  23. package/src/components/editor/node-arrow-handles.tsx +1 -2
  24. package/src/components/editor/open-wall-ends-3d-layer.tsx +1 -1
  25. package/src/components/editor/opening-guides-3d-layer.tsx +1 -1
  26. package/src/components/editor/registry-action-contributions.test.tsx +67 -6
  27. package/src/components/editor/registry-action-contributions.tsx +115 -15
  28. package/src/components/editor/riser-diagram-panel.tsx +59 -9
  29. package/src/components/editor/room-controls.tsx +1 -1
  30. package/src/components/editor/room-handles-2d.tsx +156 -6
  31. package/src/components/editor/room-handles.tsx +241 -51
  32. package/src/components/editor/room-highlight.tsx +2 -0
  33. package/src/components/editor/room-labels.tsx +29 -15
  34. package/src/components/editor/selection-manager.tsx +15 -102
  35. package/src/components/editor/separator-end-grip.tsx +74 -0
  36. package/src/components/editor/site-edge-labels.tsx +1 -1
  37. package/src/components/editor/thumbnail-generator.test.ts +15 -6
  38. package/src/components/editor/thumbnail-generator.tsx +26 -1
  39. package/src/components/editor/use-floorplan-background-placement.ts +55 -52
  40. package/src/components/editor/views/view-bar.tsx +111 -26
  41. package/src/components/editor/wall-measurement-label.tsx +1 -1
  42. package/src/components/editor/wall-move-side-handles.tsx +5 -1
  43. package/src/components/editor/wall-pull-grip.tsx +193 -0
  44. package/src/components/editor/wall-snap-beacon-layer.tsx +81 -1
  45. package/src/components/editor-2d/floorplan-adjacent-walls-layer.test.ts +76 -0
  46. package/src/components/editor-2d/floorplan-adjacent-walls-layer.tsx +186 -0
  47. package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +63 -3
  48. package/src/components/editor-2d/floorplan-group-move.tsx +31 -3
  49. package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +6 -0
  50. package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +22 -13
  51. package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +11 -3
  52. package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +7 -1
  53. package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +96 -4
  54. package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +52 -12
  55. package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +12 -1
  56. package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +197 -37
  57. package/src/components/procedural-items/inspector.tsx +59 -12
  58. package/src/components/systems/ceiling/ceiling-bracket-batch.test.ts +2 -0
  59. package/src/components/systems/ceiling/ceiling-system.test.ts +141 -0
  60. package/src/components/systems/ceiling/ceiling-system.tsx +53 -18
  61. package/src/components/tools/distribution/distribution-run-tool.tsx +50 -26
  62. package/src/components/tools/item/placement-history.test.tsx +1 -1
  63. package/src/components/tools/item/placement-strategies.test.ts +64 -1
  64. package/src/components/tools/item/placement-strategies.ts +5 -9
  65. package/src/components/tools/item/use-placement-coordinator.tsx +30 -34
  66. package/src/components/tools/registry/item-surface-move.test.ts +11 -0
  67. package/src/components/tools/select/select-candidates.test.ts +45 -0
  68. package/src/components/tools/select/select-candidates.ts +13 -2
  69. package/src/components/tools/shared/cursor-sphere.tsx +10 -3
  70. package/src/components/tools/shared/draft-measurement-label.tsx +1 -1
  71. package/src/components/tools/shared/placement-box.tsx +1 -1
  72. package/src/components/tools/shared/placement-dimension-guides.tsx +1 -1
  73. package/src/components/tools/site/terrain-brush-cursor.tsx +1 -1
  74. package/src/components/tools/wall/wall-drafting.test.ts +299 -23
  75. package/src/components/tools/wall/wall-drafting.ts +132 -24
  76. package/src/components/tools/wall/wall-pull-snap.test.ts +274 -0
  77. package/src/components/tools/wall/wall-pull-snap.ts +563 -0
  78. package/src/components/tools/wall/wall-snap-geometry.test.ts +113 -1
  79. package/src/components/tools/wall/wall-snap-geometry.ts +137 -0
  80. package/src/components/tools/zone/zone-tool.tsx +3 -3
  81. package/src/components/ui/action-menu/action-button.tsx +2 -0
  82. package/src/components/ui/action-menu/camera-actions.tsx +8 -7
  83. package/src/components/ui/action-menu/control-modes.tsx +9 -5
  84. package/src/components/ui/action-menu/follow-pascal-pill.tsx +3 -1
  85. package/src/components/ui/action-menu/follow-pascal-toggle.tsx +7 -1
  86. package/src/components/ui/action-menu/furnish-tools.tsx +7 -6
  87. package/src/components/ui/action-menu/index.tsx +1 -0
  88. package/src/components/ui/action-menu/measurement-control.tsx +35 -29
  89. package/src/components/ui/action-menu/point-ask-button.tsx +4 -3
  90. package/src/components/ui/action-menu/structure-tools.tsx +25 -23
  91. package/src/components/ui/action-menu/view-toggles.test.ts +18 -0
  92. package/src/components/ui/action-menu/view-toggles.tsx +256 -51
  93. package/src/components/ui/build-panel/build-panel.tsx +163 -25
  94. package/src/components/ui/command-palette/editor-commands.tsx +38 -11
  95. package/src/components/ui/controls/finish-swatch.tsx +45 -19
  96. package/src/components/ui/controls/material-picker.tsx +24 -17
  97. package/src/components/ui/controls/metric-control.test.ts +52 -0
  98. package/src/components/ui/controls/metric-control.tsx +62 -24
  99. package/src/components/ui/controls/segmented-control.tsx +5 -2
  100. package/src/components/ui/controls/slider-control.test.tsx +23 -0
  101. package/src/components/ui/controls/slider-control.tsx +30 -14
  102. package/src/components/ui/floating-level-selector.tsx +100 -48
  103. package/src/components/ui/helpers/building-helper.tsx +5 -3
  104. package/src/components/ui/helpers/contextual-helper-panel.tsx +97 -37
  105. package/src/components/ui/helpers/helper-manager.test.tsx +53 -3
  106. package/src/components/ui/helpers/helper-manager.tsx +82 -57
  107. package/src/components/ui/helpers/item-helper.tsx +6 -4
  108. package/src/components/ui/helpers/registered-tool-helper.tsx +3 -1
  109. package/src/components/ui/level-duplicate-dialog.tsx +32 -23
  110. package/src/components/ui/level-wall-copy-prompt.tsx +124 -0
  111. package/src/components/ui/panels/collections/collections-popover.tsx +11 -11
  112. package/src/components/ui/panels/deck-boards-fields.tsx +136 -0
  113. package/src/components/ui/panels/floor-foundation-panel.tsx +113 -38
  114. package/src/components/ui/panels/floor-opening-panel.tsx +73 -25
  115. package/src/components/ui/panels/inspector-extensions.tsx +66 -0
  116. package/src/components/ui/panels/mobile-panel-sheet.tsx +3 -1
  117. package/src/components/ui/panels/mobile-selection-bar.tsx +8 -6
  118. package/src/components/ui/panels/multi-field-value.test.ts +27 -0
  119. package/src/components/ui/panels/multi-field-value.ts +20 -1
  120. package/src/components/ui/panels/multi-height-mode.tsx +8 -7
  121. package/src/components/ui/panels/multi-parametric-inspector.tsx +122 -34
  122. package/src/components/ui/panels/multi-selection-panel.tsx +75 -35
  123. package/src/components/ui/panels/node-display.ts +35 -24
  124. package/src/components/ui/panels/panel-manager.tsx +5 -3
  125. package/src/components/ui/panels/panel-wrapper.tsx +22 -109
  126. package/src/components/ui/panels/parametric-field-control.tsx +13 -10
  127. package/src/components/ui/panels/parametric-inspector.tsx +18 -12
  128. package/src/components/ui/panels/reference-panel.tsx +51 -35
  129. package/src/components/ui/panels/room-construction-rows.tsx +464 -65
  130. package/src/components/ui/panels/room-name-options.test.ts +36 -3
  131. package/src/components/ui/panels/room-name-options.ts +110 -11
  132. package/src/components/ui/panels/room-openings-field.tsx +21 -12
  133. package/src/components/ui/panels/room-panel.tsx +206 -99
  134. package/src/components/ui/panels/shape-choice.tsx +7 -5
  135. package/src/components/ui/panels/wall-region-list.tsx +7 -7
  136. package/src/components/ui/primitives/shortcut-token.tsx +10 -6
  137. package/src/components/ui/sidebar/icon-rail.tsx +11 -9
  138. package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +10 -10
  139. package/src/components/ui/sidebar/panels/items-panel/index.tsx +10 -9
  140. package/src/components/ui/sidebar/panels/settings-panel/index.tsx +3 -0
  141. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +102 -32
  142. package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +4 -3
  143. package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +4 -2
  144. package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +4 -2
  145. package/src/components/ui/sidebar/panels/site-panel/collection-tree-node.tsx +7 -5
  146. package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +4 -2
  147. package/src/components/ui/sidebar/panels/site-panel/default-names.ts +75 -0
  148. package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +4 -2
  149. package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +4 -2
  150. package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +4 -2
  151. package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +4 -2
  152. package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +4 -2
  153. package/src/components/ui/sidebar/panels/site-panel/index.tsx +117 -64
  154. package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +36 -3
  155. package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +4 -2
  156. package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +7 -4
  157. package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +8 -2
  158. package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +8 -5
  159. package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +4 -2
  160. package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +8 -3
  161. package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +4 -2
  162. package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +4 -2
  163. package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +8 -7
  164. package/src/components/ui/sidebar/panels/site-panel/tree-node-actions.tsx +7 -6
  165. package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +16 -1
  166. package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +15 -8
  167. package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +4 -2
  168. package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +4 -2
  169. package/src/components/ui/sidebar/panels/site-panel/zone-membership-checkbox.tsx +3 -1
  170. package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +18 -8
  171. package/src/components/ui/sidebar/panels/zone-panel/index.tsx +25 -14
  172. package/src/components/ui/sidebar/tab-bar.tsx +4 -1
  173. package/src/components/ui/sidebar/use-plugin-panels.tsx +19 -10
  174. package/src/components/ui/snap-target-badge.tsx +7 -6
  175. package/src/components/ui/wall-type/wall-type-editor-dialog.tsx +721 -0
  176. package/src/components/ui/wall-type/wall-type-menu.tsx +167 -0
  177. package/src/components/viewer/floorplan-compass-button.tsx +12 -3
  178. package/src/components/viewer/floorplan-preview.tsx +27 -19
  179. package/src/components/viewer/render-settings-menu.tsx +294 -0
  180. package/src/components/viewer/sky-control.tsx +710 -0
  181. package/src/components/viewer/use-viewer-ui-sounds.test.ts +15 -0
  182. package/src/components/viewer/use-viewer-ui-sounds.ts +46 -0
  183. package/src/components/viewer/viewer-controls-bar.tsx +278 -260
  184. package/src/components/viewer/viewer-hover-label.tsx +12 -4
  185. package/src/components/viewer/viewer-rooms.tsx +61 -4
  186. package/src/components/viewer/viewer-scene-header.tsx +28 -17
  187. package/src/components/viewer/viewer-stage-switcher.tsx +4 -2
  188. package/src/components/viewer-overlay.tsx +8 -0
  189. package/src/components/walkthrough-hud.tsx +75 -2
  190. package/src/hooks/use-keyboard.test.ts +85 -4
  191. package/src/hooks/use-keyboard.ts +65 -7
  192. package/src/hooks/use-selected-room.ts +12 -1
  193. package/src/i18n/floor-footprint-name.ts +30 -0
  194. package/src/i18n/level-name.ts +39 -0
  195. package/src/i18n/locales/en/messages.po +15117 -0
  196. package/src/i18n/locales/en/messages.ts +1 -0
  197. package/src/i18n/locales/en-XA/messages.ts +1 -0
  198. package/src/i18n/locales/es/messages.po +37501 -0
  199. package/src/i18n/locales/es/messages.ts +1 -0
  200. package/src/i18n/locales/fr/messages.po +37501 -0
  201. package/src/i18n/locales/fr/messages.ts +1 -0
  202. package/src/i18n/locales/ja/messages.po +37501 -0
  203. package/src/i18n/locales/ja/messages.ts +1 -0
  204. package/src/i18n/locales/zh-CN/messages.po +37501 -0
  205. package/src/i18n/locales/zh-CN/messages.ts +1 -0
  206. package/src/i18n/locales.test.ts +63 -0
  207. package/src/i18n/locales.ts +32 -0
  208. package/src/i18n/message-slot.tsx +25 -0
  209. package/src/i18n/node-messages.ts +169 -0
  210. package/src/i18n/pseudo-locale.ts +17 -0
  211. package/src/i18n/published-entry.test.ts +17 -0
  212. package/src/i18n/scene-theme-name.ts +16 -0
  213. package/src/index.tsx +156 -4
  214. package/src/lib/add-storey.test.ts +3 -3
  215. package/src/lib/contextual-help.test.ts +20 -1
  216. package/src/lib/contextual-help.ts +95 -19
  217. package/src/lib/continuation.ts +38 -11
  218. package/src/lib/floor-footprints.test.ts +2 -1
  219. package/src/lib/floor-foundation-support.test.ts +7 -3
  220. package/src/lib/floorplan/dimension-drive.test.ts +36 -2
  221. package/src/lib/floorplan/dimension-drive.ts +68 -3
  222. package/src/lib/floorplan/floorplan-export.test.ts +16 -5
  223. package/src/lib/floorplan/floorplan-export.tsx +13 -16
  224. package/src/lib/floorplan/floorplan-extension.test.ts +11 -12
  225. package/src/lib/floorplan/floorplan-extension.ts +37 -18
  226. package/src/lib/floorplan/floorplan-mode.test.ts +13 -6
  227. package/src/lib/floorplan/floorplan-mode.ts +14 -12
  228. package/src/lib/floorplan/floorplan-plugin-install.test.tsx +1 -1
  229. package/src/lib/floorplan/floorplan-readonly.ts +3 -3
  230. package/src/lib/floorplan/schedules.ts +10 -6
  231. package/src/lib/floorplan/site-plan/site-annotations.test.ts +46 -1
  232. package/src/lib/floorplan/site-plan/site-annotations.ts +3 -0
  233. package/src/lib/floorplan/site-plan/site-parts.ts +6 -3
  234. package/src/lib/geometry-script/author.test.ts +114 -0
  235. package/src/lib/geometry-script/author.ts +55 -23
  236. package/src/lib/geometry-script/client.ts +3 -0
  237. package/src/lib/geometry-script/finish-scripts.ts +7 -7
  238. package/src/lib/glb-export.test.ts +327 -0
  239. package/src/lib/glb-export.ts +70 -2
  240. package/src/lib/grid-frame.test.ts +56 -3
  241. package/src/lib/grid-frame.ts +28 -1
  242. package/src/lib/history.test.ts +2 -1
  243. package/src/lib/hud-title.ts +48 -24
  244. package/src/lib/ifc-export.test.ts +41 -1
  245. package/src/lib/inspector-card-mode.test.ts +3 -31
  246. package/src/lib/inspector-card-mode.ts +5 -10
  247. package/src/lib/inspector-expanded.test.ts +13 -0
  248. package/src/lib/inspector-expanded.ts +13 -1
  249. package/src/lib/keyboard-pan.test.ts +7 -0
  250. package/src/lib/keyboard-pan.ts +9 -2
  251. package/src/lib/layer-thickness.test.ts +22 -0
  252. package/src/lib/layer-thickness.ts +79 -0
  253. package/src/lib/lot/drop-in.test.ts +59 -1
  254. package/src/lib/lot/drop-in.ts +42 -50
  255. package/src/lib/material-swatch-style.ts +24 -0
  256. package/src/lib/measurement-parser.ts +7 -3
  257. package/src/lib/measurements.test.ts +17 -0
  258. package/src/lib/measurements.ts +20 -5
  259. package/src/lib/mezzanine-selection.test.ts +3 -1
  260. package/src/lib/orbit-near-plane.test.ts +12 -0
  261. package/src/lib/orbit-near-plane.ts +13 -0
  262. package/src/lib/paint-regions.test.ts +8 -5
  263. package/src/lib/paint-scope.test.ts +53 -0
  264. package/src/lib/paint-scope.ts +18 -4
  265. package/src/lib/panel-rows.ts +17 -8
  266. package/src/lib/reference-scale.ts +83 -0
  267. package/src/lib/room-built-on.test.ts +26 -12
  268. package/src/lib/room-built-on.ts +20 -2
  269. package/src/lib/room-construction.test.ts +28 -1
  270. package/src/lib/room-construction.ts +14 -0
  271. package/src/lib/room-controls-phase6.test.ts +140 -0
  272. package/src/lib/room-handle-drag.ts +557 -42
  273. package/src/lib/room-name-catalog.test.ts +8 -0
  274. package/src/lib/room-name-catalog.ts +8 -1
  275. package/src/lib/room-name-messages.test.ts +132 -0
  276. package/src/lib/room-name-messages.ts +147 -0
  277. package/src/lib/room-name-storage.test.ts +334 -0
  278. package/src/lib/room-push-dimensions.test.ts +37 -0
  279. package/src/lib/room-push-dimensions.ts +226 -2
  280. package/src/lib/room-selection.test.ts +23 -6
  281. package/src/lib/scene-clipboard.test.ts +70 -49
  282. package/src/lib/scene-clipboard.ts +9 -4
  283. package/src/lib/selection-cycle.ts +63 -0
  284. package/src/lib/separator-edit.test.ts +431 -0
  285. package/src/lib/separator-edit.ts +179 -0
  286. package/src/lib/separator-lines.ts +38 -0
  287. package/src/lib/sfx-bus.ts +27 -1
  288. package/src/lib/sfx-player.ts +16 -0
  289. package/src/lib/tool-cursor-icon.ts +5 -1
  290. package/src/lib/tool-shortcuts.ts +130 -0
  291. package/src/lib/viewer-selection.test.ts +28 -24
  292. package/src/lib/viewer-selection.ts +20 -5
  293. package/src/lib/wall-draw-variant.test.ts +7 -6
  294. package/src/lib/wall-draw-variant.ts +107 -6
  295. package/src/lib/wall-nudge.test.ts +44 -0
  296. package/src/lib/wall-nudge.ts +131 -0
  297. package/src/lib/wall-parallel-snap.test.ts +83 -0
  298. package/src/lib/wall-parallel-snap.ts +176 -0
  299. package/src/lib/wall-placement-pointer.test.ts +23 -0
  300. package/src/lib/wall-placement-pointer.ts +18 -0
  301. package/src/lib/wall-pull.test.ts +245 -0
  302. package/src/lib/wall-pull.ts +209 -0
  303. package/src/lib/wall-push-line-up.test.ts +132 -0
  304. package/src/lib/wall-region-list.ts +14 -5
  305. package/src/lib/wall-run-snap.test.ts +188 -0
  306. package/src/lib/wall-run-snap.ts +83 -0
  307. package/src/lib/wall-type.test.ts +125 -0
  308. package/src/lib/wall-type.ts +119 -0
  309. package/src/lib/window-interaction.test.ts +42 -0
  310. package/src/lib/window-interaction.ts +47 -5
  311. package/src/lib/world-grid-snap.test.ts +139 -2
  312. package/src/lib/world-grid-snap.ts +102 -105
  313. package/src/store/use-alignment-guides.ts +27 -1
  314. package/src/store/use-editor.tsx +48 -0
  315. package/src/store/use-floorplan-annotation-visibility.test.ts +64 -0
  316. package/src/store/use-floorplan-annotation-visibility.ts +47 -22
  317. package/src/store/use-wall-snap-indicator.ts +15 -2
@@ -1,4 +1,4 @@
1
- import { type BuildingNode, useScene } from '@pascal-app/core'
1
+ import { type BuildingNode, useScene, usePascalI18n } from '@pascal-app/core'
2
2
  import { useViewer } from '@pascal-app/viewer'
3
3
  import { Building2, Plus } from 'lucide-react'
4
4
  import { memo, useState } from 'react'
@@ -23,6 +23,7 @@ export const BuildingTreeNode = memo(function BuildingTreeNode({
23
23
  depth,
24
24
  isLast,
25
25
  }: BuildingTreeNodeProps) {
26
+ const i18n = usePascalI18n()
26
27
  const [expanded, setExpanded] = useState(true)
27
28
  const isVisible = useScene((s) => s.nodes[nodeId]?.visible !== false)
28
29
  const name = useScene((s) => s.nodes[nodeId]?.name)
@@ -63,7 +64,7 @@ export const BuildingTreeNode = memo(function BuildingTreeNode({
63
64
  <Plus className="h-3 w-3" />
64
65
  </button>
65
66
  </TooltipTrigger>
66
- <TooltipContent side="right">Add new level</TooltipContent>
67
+ <TooltipContent side="right">{i18n._({ id: 'editor.sidebar.panels.sitePanel.buildingTreeNode.addNewLevel', message: 'Add new level' })}</TooltipContent>
67
68
  </Tooltip>
68
69
  </div>
69
70
  }
@@ -75,7 +76,7 @@ export const BuildingTreeNode = memo(function BuildingTreeNode({
75
76
  isLast={isLast}
76
77
  isSelected={isSelected}
77
78
  isVisible={isVisible}
78
- label={name || 'Building'}
79
+ label={name || i18n._({ id: 'editor.sidebar.panels.sitePanel.buildingTreeNode.building', message: 'Building' })}
79
80
  onClick={handleClick}
80
81
  onDoubleClick={() => focusTreeNode(nodeId)}
81
82
  onToggle={() => setExpanded(!expanded)}
@@ -1,4 +1,4 @@
1
- import { type AnyNodeId, type CeilingNode, useScene } from '@pascal-app/core'
1
+ import { type AnyNodeId, type CeilingNode, useScene, usePascalI18n} 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, useState } from 'react'
@@ -8,6 +8,7 @@ import { formatAreaLabel } from './../../../../../lib/measurements'
8
8
  import { InlineRenameInput } from './inline-rename-input'
9
9
  import { focusTreeNode, handleTreeSelection, TreeNode, TreeNodeWrapper } from './tree-node'
10
10
  import { TreeNodeActions } from './tree-node-actions'
11
+ import { ceilingDefaultName } from './default-names'
11
12
 
12
13
  interface CeilingTreeNodeProps {
13
14
  nodeId: AnyNodeId
@@ -20,6 +21,7 @@ export const CeilingTreeNode = memo(function CeilingTreeNode({
20
21
  depth,
21
22
  isLast,
22
23
  }: CeilingTreeNodeProps) {
24
+ const i18n = usePascalI18n()
23
25
  const [expanded, setExpanded] = useState(false)
24
26
  const [isEditing, setIsEditing] = useState(false)
25
27
  const isVisible = useScene((s) => s.nodes[nodeId as AnyNodeId]?.visible !== false)
@@ -77,7 +79,7 @@ export const CeilingTreeNode = memo(function CeilingTreeNode({
77
79
  const handleStartEditing = useCallback(() => setIsEditing(true), [])
78
80
  const handleStopEditing = useCallback(() => setIsEditing(false), [])
79
81
 
80
- const defaultName = `Ceiling (${formatAreaLabel(calculatePolygonArea(polygon), unit)})`
82
+ const defaultName = ceilingDefaultName(i18n, formatAreaLabel(calculatePolygonArea(polygon), unit))
81
83
 
82
84
  return (
83
85
  <TreeNodeWrapper
@@ -1,4 +1,4 @@
1
- import { type AnyNodeId, type ChimneyNode, useScene } from '@pascal-app/core'
1
+ import { type AnyNodeId, type ChimneyNode, useScene, usePascalI18n } from '@pascal-app/core'
2
2
  import { useViewer } from '@pascal-app/viewer'
3
3
  import Image from 'next/image'
4
4
  import { memo, useCallback, useState } from 'react'
@@ -6,6 +6,7 @@ import { SnapTargetIcon } from '../../../snap-target-badge'
6
6
  import { InlineRenameInput } from './inline-rename-input'
7
7
  import { focusTreeNode, handleTreeSelection, TreeNodeWrapper } from './tree-node'
8
8
  import { TreeNodeActions } from './tree-node-actions'
9
+ import { nodeLabelDescriptor } from '../../../../../i18n/node-messages'
9
10
 
10
11
  interface ChimneyTreeNodeProps {
11
12
  nodeId: AnyNodeId
@@ -18,6 +19,7 @@ export const ChimneyTreeNode = memo(function ChimneyTreeNode({
18
19
  depth,
19
20
  isLast,
20
21
  }: ChimneyTreeNodeProps) {
22
+ const i18n = usePascalI18n()
21
23
  const [isEditing, setIsEditing] = useState(false)
22
24
  const isVisible = useScene((s) => s.nodes[nodeId]?.visible !== false)
23
25
  const node = useScene((s) => s.nodes[nodeId] as ChimneyNode | undefined)
@@ -39,7 +41,7 @@ export const ChimneyTreeNode = memo(function ChimneyTreeNode({
39
41
  [nodeId, setSelection],
40
42
  )
41
43
 
42
- const defaultName = node?.name || 'Chimney'
44
+ const defaultName = node?.name || i18n._(nodeLabelDescriptor('chimney', 'Chimney'))
43
45
 
44
46
  return (
45
47
  <TreeNodeWrapper
@@ -6,7 +6,7 @@ import {
6
6
  findLevelAncestorId,
7
7
  type LevelNode,
8
8
  resolveBuildingForLevel,
9
- useScene,
9
+ useScene, usePascalI18n,
10
10
  } from '@pascal-app/core'
11
11
  import { useViewer } from '@pascal-app/viewer'
12
12
  import { Layers, Trash2 } from 'lucide-react'
@@ -44,6 +44,7 @@ function useLiveMembers(collectionId: CollectionId) {
44
44
 
45
45
  /** Collections, listed under the level tree. Absent when the scene has none. */
46
46
  export const CollectionsSection = memo(function CollectionsSection() {
47
+ const i18n = usePascalI18n()
47
48
  const collectionIds = useScene(useShallow((s) => Object.keys(s.collections) as CollectionId[]))
48
49
  const [expanded, setExpanded] = useState(true)
49
50
 
@@ -58,7 +59,7 @@ export const CollectionsSection = memo(function CollectionsSection() {
58
59
  icon={<Layers className="h-3.5 w-3.5" />}
59
60
  label={
60
61
  <span className="flex items-center gap-1.5">
61
- Collections
62
+ {i18n._({ id: 'editor.sidebar.panels.sitePanel.collectionTreeNode.collections', message: 'Collections' })}
62
63
  <span className="text-muted-foreground text-xs">{collectionIds.length}</span>
63
64
  </span>
64
65
  }
@@ -90,6 +91,7 @@ const CollectionTreeNode = memo(function CollectionTreeNode({
90
91
  depth,
91
92
  isLast,
92
93
  }: CollectionTreeNodeProps) {
94
+ const i18n = usePascalI18n()
93
95
  const [isEditing, setIsEditing] = useState(false)
94
96
  const [expanded, setExpanded] = useState(false)
95
97
  const name = useScene((s) => s.collections[collectionId]?.name)
@@ -147,7 +149,7 @@ const CollectionTreeNode = memo(function CollectionTreeNode({
147
149
  event.stopPropagation()
148
150
  deleteCollection(collectionId)
149
151
  }}
150
- title="Delete collection"
152
+ title={i18n._({ id: 'editor.sidebar.panels.sitePanel.collectionTreeNode.deleteCollection', message: 'Delete collection' })}
151
153
  type="button"
152
154
  >
153
155
  <Trash2 className="h-3 w-3" />
@@ -159,7 +161,7 @@ const CollectionTreeNode = memo(function CollectionTreeNode({
159
161
  icon={
160
162
  <ColorDot
161
163
  color={color ?? '#6366f1'}
162
- label="Collection colour"
164
+ label={i18n._({ id: 'editor.sidebar.panels.sitePanel.collectionTreeNode.collectionColour', message: 'Collection colour' })}
163
165
  onChange={(next) => updateCollection(collectionId, { color: next })}
164
166
  />
165
167
  }
@@ -169,7 +171,7 @@ const CollectionTreeNode = memo(function CollectionTreeNode({
169
171
  label={
170
172
  <span className="flex min-w-0 items-center gap-1.5">
171
173
  <InlineRenameField
172
- defaultName="Collection"
174
+ defaultName={i18n._({ id: 'editor.layers.defaultName.collection', message: 'Collection' })}
173
175
  isEditing={isEditing}
174
176
  name={name}
175
177
  onRename={handleRename}
@@ -1,10 +1,11 @@
1
- import { type AnyNodeId, type ColumnNode, useScene } from '@pascal-app/core'
1
+ import { type AnyNodeId, type ColumnNode, useScene, usePascalI18n } from '@pascal-app/core'
2
2
  import { useViewer } from '@pascal-app/viewer'
3
3
  import Image from 'next/image'
4
4
  import { memo, useCallback, useState } from 'react'
5
5
  import { InlineRenameInput } from './inline-rename-input'
6
6
  import { focusTreeNode, handleTreeSelection, TreeNodeWrapper } from './tree-node'
7
7
  import { TreeNodeActions } from './tree-node-actions'
8
+ import { nodeLabelDescriptor } from '../../../../../i18n/node-messages'
8
9
 
9
10
  interface ColumnTreeNodeProps {
10
11
  nodeId: AnyNodeId
@@ -17,6 +18,7 @@ export const ColumnTreeNode = memo(function ColumnTreeNode({
17
18
  depth,
18
19
  isLast,
19
20
  }: ColumnTreeNodeProps) {
21
+ const i18n = usePascalI18n()
20
22
  const [isEditing, setIsEditing] = useState(false)
21
23
  const isVisible = useScene((s) => s.nodes[nodeId]?.visible !== false)
22
24
  const node = useScene((s) => s.nodes[nodeId] as ColumnNode | undefined)
@@ -38,7 +40,7 @@ export const ColumnTreeNode = memo(function ColumnTreeNode({
38
40
  [nodeId, setSelection],
39
41
  )
40
42
 
41
- const defaultName = node?.name || 'Column'
43
+ const defaultName = node?.name || i18n._(nodeLabelDescriptor('column', 'Column'))
42
44
 
43
45
  return (
44
46
  <TreeNodeWrapper
@@ -0,0 +1,75 @@
1
+ import type { PascalI18n, RoofType, StairSegmentNode, ZoneNode } from '@pascal-app/core'
2
+
3
+ /** Names a layer row shows (and its drag preview) until the user renames it. */
4
+
5
+ export function zoneDefaultName(
6
+ i18n: PascalI18n,
7
+ zone: { spaceRole?: ZoneNode['spaceRole'] } | undefined,
8
+ area: string,
9
+ ): string {
10
+ return zone?.spaceRole === 'room'
11
+ ? i18n._({ id: 'editor.layers.defaultName.room', message: 'Room ({area})', values: { area } })
12
+ : i18n._({ id: 'editor.layers.defaultName.zone', message: 'Zone ({area})', values: { area } })
13
+ }
14
+
15
+ export function slabDefaultName(i18n: PascalI18n, area: string): string {
16
+ return i18n._({ id: 'editor.layers.defaultName.slab', message: 'Slab ({area})', values: { area } })
17
+ }
18
+
19
+ export function ceilingDefaultName(i18n: PascalI18n, area: string): string {
20
+ return i18n._({
21
+ id: 'editor.layers.defaultName.ceiling',
22
+ message: 'Ceiling ({area})',
23
+ values: { area },
24
+ })
25
+ }
26
+
27
+ export function roofDefaultName(i18n: PascalI18n, count: number): string {
28
+ return i18n._({
29
+ id: 'editor.layers.defaultName.roof',
30
+ message: 'Roof ({count, plural, one {# segment} other {# segments}})',
31
+ values: { count },
32
+ })
33
+ }
34
+
35
+ export function staircaseDefaultName(i18n: PascalI18n, count: number): string {
36
+ return i18n._({
37
+ id: 'editor.layers.defaultName.staircase',
38
+ message: 'Staircase ({count, plural, one {# segment} other {# segments}})',
39
+ values: { count },
40
+ })
41
+ }
42
+
43
+ export function roofSegmentDefaultName(
44
+ i18n: PascalI18n,
45
+ roofType: RoofType,
46
+ width: number,
47
+ depth: number,
48
+ ): string {
49
+ return i18n._({
50
+ id: 'editor.layers.defaultName.roofSegment',
51
+ message:
52
+ '{roofType, select, hip {Hip} gable {Gable} shed {Shed} gambrel {Gambrel} dutch {Dutch} mansard {Mansard} flat {Flat} conical {Conical} barrel {Barrel} other {Roof}} ({width}×{depth}m)',
53
+ values: { roofType, width: width.toFixed(1), depth: depth.toFixed(1) },
54
+ })
55
+ }
56
+
57
+ export function stairSegmentDefaultName(
58
+ i18n: PascalI18n,
59
+ segmentType: StairSegmentNode['segmentType'],
60
+ width: number,
61
+ length: number,
62
+ ): string {
63
+ const values = { width: width.toFixed(1), length: length.toFixed(1) }
64
+ return segmentType === 'stair'
65
+ ? i18n._({
66
+ id: 'editor.layers.defaultName.flight',
67
+ message: 'Flight ({width}×{length}m)',
68
+ values,
69
+ })
70
+ : i18n._({
71
+ id: 'editor.layers.defaultName.landing',
72
+ message: 'Landing ({width}×{length}m)',
73
+ values,
74
+ })
75
+ }
@@ -1,6 +1,6 @@
1
1
  'use client'
2
2
 
3
- import { type AnyNodeId, type DoorNode, useScene } from '@pascal-app/core'
3
+ import { type AnyNodeId, type DoorNode, 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 DoorTreeNodeProps {
13
14
  nodeId: AnyNodeId
@@ -20,6 +21,7 @@ export const DoorTreeNode = memo(function DoorTreeNode({
20
21
  depth,
21
22
  isLast,
22
23
  }: DoorTreeNodeProps) {
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 DoorNode | undefined)
@@ -62,7 +64,7 @@ export const DoorTreeNode = memo(function DoorTreeNode({
62
64
  isVisible={isVisible}
63
65
  label={
64
66
  <InlineRenameInput
65
- defaultName="Door"
67
+ defaultName={i18n._(nodeLabelDescriptor('door', 'Door'))}
66
68
  isEditing={isEditing}
67
69
  nodeId={nodeId as AnyNodeId}
68
70
  onStartEditing={handleStartEditing}
@@ -1,4 +1,4 @@
1
- import { type AnyNodeId, type DormerNode, useScene } from '@pascal-app/core'
1
+ import { type AnyNodeId, type DormerNode, useScene, usePascalI18n } from '@pascal-app/core'
2
2
  import { useViewer } from '@pascal-app/viewer'
3
3
  import Image from 'next/image'
4
4
  import { memo, useCallback, useState } from 'react'
@@ -7,6 +7,7 @@ import { SnapTargetIcon } from '../../../snap-target-badge'
7
7
  import { InlineRenameInput } from './inline-rename-input'
8
8
  import { focusTreeNode, handleTreeSelection, TreeNode, TreeNodeWrapper } from './tree-node'
9
9
  import { TreeNodeActions } from './tree-node-actions'
10
+ import { nodeLabelDescriptor } from '../../../../../i18n/node-messages'
10
11
 
11
12
  interface DormerTreeNodeProps {
12
13
  nodeId: AnyNodeId
@@ -25,6 +26,7 @@ export const DormerTreeNode = memo(function DormerTreeNode({
25
26
  depth,
26
27
  isLast,
27
28
  }: DormerTreeNodeProps) {
29
+ const i18n = usePascalI18n()
28
30
  const [isEditing, setIsEditing] = useState(false)
29
31
  const [expanded, setExpanded] = useState(true)
30
32
  const isVisible = useScene((s) => s.nodes[nodeId]?.visible !== false)
@@ -50,7 +52,7 @@ export const DormerTreeNode = memo(function DormerTreeNode({
50
52
  [nodeId, setSelection],
51
53
  )
52
54
 
53
- const defaultName = node?.name || 'Dormer'
55
+ const defaultName = node?.name || i18n._(nodeLabelDescriptor('dormer', 'Dormer'))
54
56
 
55
57
  return (
56
58
  <TreeNodeWrapper
@@ -1,10 +1,11 @@
1
- import { type AnyNodeId, type ElevatorNode, useScene } from '@pascal-app/core'
1
+ import { type AnyNodeId, type ElevatorNode, useScene, usePascalI18n } from '@pascal-app/core'
2
2
  import { useViewer } from '@pascal-app/viewer'
3
3
  import Image from 'next/image'
4
4
  import { memo, useCallback, useState } from 'react'
5
5
  import { InlineRenameInput } from './inline-rename-input'
6
6
  import { focusTreeNode, handleTreeSelection, TreeNodeWrapper } from './tree-node'
7
7
  import { TreeNodeActions } from './tree-node-actions'
8
+ import { nodeLabelDescriptor } from '../../../../../i18n/node-messages'
8
9
 
9
10
  interface ElevatorTreeNodeProps {
10
11
  nodeId: AnyNodeId
@@ -17,6 +18,7 @@ export const ElevatorTreeNode = memo(function ElevatorTreeNode({
17
18
  depth,
18
19
  isLast,
19
20
  }: ElevatorTreeNodeProps) {
21
+ const i18n = usePascalI18n()
20
22
  const [isEditing, setIsEditing] = useState(false)
21
23
  const isVisible = useScene((s) => s.nodes[nodeId]?.visible !== false)
22
24
  const node = useScene((s) => s.nodes[nodeId] as ElevatorNode | undefined)
@@ -53,7 +55,7 @@ export const ElevatorTreeNode = memo(function ElevatorTreeNode({
53
55
  isVisible={isVisible}
54
56
  label={
55
57
  <InlineRenameInput
56
- defaultName={node?.name || 'Elevator'}
58
+ defaultName={node?.name || i18n._(nodeLabelDescriptor('elevator', 'Elevator'))}
57
59
  isEditing={isEditing}
58
60
  nodeId={nodeId}
59
61
  onStartEditing={() => setIsEditing(true)}
@@ -1,10 +1,11 @@
1
- import { type AnyNodeId, type FenceNode, useScene } from '@pascal-app/core'
1
+ import { type AnyNodeId, type FenceNode, useScene, usePascalI18n } from '@pascal-app/core'
2
2
  import { useViewer } from '@pascal-app/viewer'
3
3
  import Image from 'next/image'
4
4
  import { memo, useEffect, useState } from 'react'
5
5
  import { InlineRenameInput } from './inline-rename-input'
6
6
  import { focusTreeNode, handleTreeSelection, TreeNode, TreeNodeWrapper } from './tree-node'
7
7
  import { TreeNodeActions } from './tree-node-actions'
8
+ import { nodeLabelDescriptor } from '../../../../../i18n/node-messages'
8
9
 
9
10
  interface FenceTreeNodeProps {
10
11
  nodeId: AnyNodeId
@@ -17,6 +18,7 @@ export const FenceTreeNode = memo(function FenceTreeNode({
17
18
  depth,
18
19
  isLast,
19
20
  }: FenceTreeNodeProps) {
21
+ const i18n = usePascalI18n()
20
22
  const node = useScene((state) => state.nodes[nodeId]) as FenceNode | undefined
21
23
  const [isEditing, setIsEditing] = useState(false)
22
24
  const [expanded, setExpanded] = useState(true)
@@ -55,7 +57,7 @@ export const FenceTreeNode = memo(function FenceTreeNode({
55
57
  isVisible={node.visible !== false}
56
58
  label={
57
59
  <InlineRenameInput
58
- defaultName="Fence"
60
+ defaultName={i18n._(nodeLabelDescriptor('fence', 'Fence'))}
59
61
  isEditing={isEditing}
60
62
  nodeId={node.id}
61
63
  onStartEditing={() => setIsEditing(true)}
@@ -1,4 +1,4 @@
1
- import { type AnyNodeId, type GutterNode, useScene } from '@pascal-app/core'
1
+ import { type AnyNodeId, type GutterNode, useScene, usePascalI18n } from '@pascal-app/core'
2
2
  import { useViewer } from '@pascal-app/viewer'
3
3
  import Image from 'next/image'
4
4
  import { memo, useCallback, useState } from 'react'
@@ -6,6 +6,7 @@ import { SnapTargetIcon } from '../../../snap-target-badge'
6
6
  import { InlineRenameInput } from './inline-rename-input'
7
7
  import { focusTreeNode, handleTreeSelection, TreeNodeWrapper } from './tree-node'
8
8
  import { TreeNodeActions } from './tree-node-actions'
9
+ import { nodeLabelDescriptor } from '../../../../../i18n/node-messages'
9
10
 
10
11
  interface GutterTreeNodeProps {
11
12
  nodeId: AnyNodeId
@@ -18,6 +19,7 @@ export const GutterTreeNode = memo(function GutterTreeNode({
18
19
  depth,
19
20
  isLast,
20
21
  }: GutterTreeNodeProps) {
22
+ const i18n = usePascalI18n()
21
23
  const [isEditing, setIsEditing] = useState(false)
22
24
  const isVisible = useScene((s) => s.nodes[nodeId]?.visible !== false)
23
25
  const node = useScene((s) => s.nodes[nodeId] as GutterNode | undefined)
@@ -39,7 +41,7 @@ export const GutterTreeNode = memo(function GutterTreeNode({
39
41
  [nodeId, setSelection],
40
42
  )
41
43
 
42
- const defaultName = node?.name || 'Gutter'
44
+ const defaultName = node?.name || i18n._(nodeLabelDescriptor('gutter', 'Gutter'))
43
45
 
44
46
  return (
45
47
  <TreeNodeWrapper