@pascal-app/editor 1.1.0 → 1.1.1

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 (260) hide show
  1. package/package.json +9 -15
  2. package/src/components/editor/action-menu-button.tsx +1 -1
  3. package/src/components/editor/alignment-3d-guide-layer.tsx +1 -1
  4. package/src/components/editor/custom-camera-controls.tsx +11 -11
  5. package/src/components/editor/editor-layout-v2.tsx +1 -1
  6. package/src/components/editor/elevation-3d-guide-layer.tsx +1 -1
  7. package/src/components/editor/first-person-controls.tsx +20 -1981
  8. package/src/components/editor/floating-action-menu.tsx +1 -1
  9. package/src/components/editor/floating-building-action-menu.tsx +1 -1
  10. package/src/components/editor/floor-region-controls.tsx +6 -2
  11. package/src/components/editor/floorplan-navigation-presentation.test.ts +0 -40
  12. package/src/components/editor/floorplan-navigation-presentation.ts +0 -66
  13. package/src/components/editor/floorplan-panel.tsx +26 -22
  14. package/src/components/editor/floorplan-wall-typing-keys.ts +1 -1
  15. package/src/components/editor/follow-pascal.test.tsx +1 -2
  16. package/src/components/editor/follow-pascal.tsx +1 -1
  17. package/src/components/editor/footprint-height-handle.tsx +1 -1
  18. package/src/components/editor/group-actions.ts +1 -2
  19. package/src/components/editor/group-floating-action-menu.tsx +1 -1
  20. package/src/components/editor/group-move-3d.ts +1 -2
  21. package/src/components/editor/group-rotate-handle.tsx +1 -2
  22. package/src/components/editor/handles/use-handle-drag.ts +1 -2
  23. package/src/components/editor/index.tsx +24 -14
  24. package/src/components/editor/inspector-extension-actions.tsx +1 -1
  25. package/src/components/editor/node-arrow-handles.tsx +1 -1
  26. package/src/components/editor/open-wall-ends-3d-layer.tsx +1 -1
  27. package/src/components/editor/opening-guides-3d-layer.tsx +1 -1
  28. package/src/components/editor/room-controls.tsx +7 -4
  29. package/src/components/editor/room-handles-2d.tsx +2 -3
  30. package/src/components/editor/room-handles.tsx +1 -2
  31. package/src/components/editor/room-highlight.tsx +6 -105
  32. package/src/components/editor/room-labels.test.ts +8 -822
  33. package/src/components/editor/room-labels.tsx +40 -1671
  34. package/src/components/editor/room-webgpu-materials.test.ts +1 -1
  35. package/src/components/editor/selection-manager.tsx +5 -3
  36. package/src/components/editor/site-edge-labels.tsx +1 -1
  37. package/src/components/editor/snapshot-capture-overlay.tsx +2 -5
  38. package/src/components/editor/use-floorplan-background-placement.ts +2 -1
  39. package/src/components/editor/views/plugins-view.tsx +1 -1
  40. package/src/components/editor/views/view-bar.tsx +9 -9
  41. package/src/components/editor/wall-measurement-label.tsx +1 -1
  42. package/src/components/editor/wall-move-side-handles.tsx +1 -1
  43. package/src/components/editor/wall-pull-grip.tsx +2 -2
  44. package/src/components/editor-2d/floorplan-group-move.tsx +2 -3
  45. package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +1 -1
  46. package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +1 -1
  47. package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +1 -2
  48. package/src/components/editor-2d/renderers/floorplan-annotation-layout.test.ts +0 -37
  49. package/src/components/editor-2d/renderers/floorplan-annotation-layout.ts +0 -30
  50. package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +13 -3
  51. package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +1 -2
  52. package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +8 -10
  53. package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +25 -33
  54. package/src/components/systems/roof/roof-edit-system.tsx +4 -3
  55. package/src/components/tools/distribution/distribution-run-tool.tsx +1 -1
  56. package/src/components/tools/item/placement-history.test.tsx +1 -1
  57. package/src/components/tools/item/use-placement-coordinator.tsx +2 -3
  58. package/src/components/tools/paint-region/wall-paint-region-tool.tsx +6 -2
  59. package/src/components/tools/registry/move-registry-node-tool.tsx +1 -2
  60. package/src/components/tools/shared/cursor-sphere.tsx +1 -1
  61. package/src/components/tools/shared/draft-measurement-label.tsx +1 -1
  62. package/src/components/tools/shared/placement-box.tsx +1 -1
  63. package/src/components/tools/shared/placement-dimension-guides.tsx +1 -1
  64. package/src/components/tools/site/terrain-brush-cursor.tsx +1 -1
  65. package/src/components/ui/action-menu/camera-actions.tsx +2 -2
  66. package/src/components/ui/action-menu/control-modes.tsx +1 -1
  67. package/src/components/ui/action-menu/follow-pascal-pill.tsx +1 -1
  68. package/src/components/ui/action-menu/index.tsx +3 -3
  69. package/src/components/ui/action-menu/measurement-control.tsx +3 -3
  70. package/src/components/ui/action-menu/placement.ts +0 -18
  71. package/src/components/ui/action-menu/point-ask-button.tsx +1 -1
  72. package/src/components/ui/action-menu/view-toggles.tsx +5 -5
  73. package/src/components/ui/controls/finish-swatch.tsx +1 -1
  74. package/src/components/ui/controls/material-paint-panel.tsx +2 -2
  75. package/src/components/ui/controls/scene-material-list.tsx +2 -2
  76. package/src/components/ui/controls/select-control.tsx +1 -1
  77. package/src/components/ui/controls/terrain-sculpt-panel.tsx +1 -1
  78. package/src/components/ui/floating-level-selector.tsx +3 -3
  79. package/src/components/ui/helpers/contextual-helper-panel.tsx +1 -1
  80. package/src/components/ui/helpers/helper-manager.test.tsx +1 -1
  81. package/src/components/ui/helpers/helper-manager.tsx +1 -1
  82. package/src/components/ui/level-duplicate-dialog.tsx +1 -1
  83. package/src/components/ui/panels/collections/collections-popover.tsx +2 -2
  84. package/src/components/ui/panels/floor-foundation-panel.tsx +2 -2
  85. package/src/components/ui/panels/floor-opening-panel.tsx +4 -4
  86. package/src/components/ui/panels/node-display.ts +1 -1
  87. package/src/components/ui/panels/panel-manager.tsx +1 -1
  88. package/src/components/ui/panels/panel-wrapper.tsx +1 -1
  89. package/src/components/ui/panels/parametric-inspector.tsx +1 -1
  90. package/src/components/ui/panels/room-construction-rows.tsx +6 -6
  91. package/src/components/ui/panels/room-name-options.ts +1 -5
  92. package/src/components/ui/panels/room-panel.tsx +6 -8
  93. package/src/components/ui/primitives/color-dot.tsx +2 -2
  94. package/src/components/ui/primitives/opacity-control.tsx +2 -2
  95. package/src/components/ui/primitives/sidebar.tsx +3 -3
  96. package/src/components/ui/right-stack.tsx +1 -1
  97. package/src/components/ui/sidebar/icon-rail.tsx +1 -1
  98. package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +1 -1
  99. package/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +2 -2
  100. package/src/components/ui/sidebar/panels/settings-panel/construction-animation-setting.tsx +1 -1
  101. package/src/components/ui/sidebar/panels/settings-panel/index.tsx +2 -2
  102. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +1 -1
  103. package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +1 -1
  104. package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +1 -1
  105. package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +1 -1
  106. package/src/components/ui/sidebar/panels/site-panel/index.tsx +2 -2
  107. package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +1 -1
  108. package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +1 -1
  109. package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +46 -7
  110. package/src/components/ui/sidebar/panels/site-panel/tree-node-actions.tsx +1 -1
  111. package/src/components/ui/sidebar/panels/site-panel/tree-node-drag.tsx +14 -5
  112. package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +2 -2
  113. package/src/components/ui/sidebar/panels/zone-panel/index.tsx +3 -3
  114. package/src/components/ui/sidebar/tab-bar.tsx +1 -1
  115. package/src/components/ui/wall-type/wall-type-editor-dialog.tsx +1 -1
  116. package/src/components/ui/wall-type/wall-type-menu.tsx +1 -1
  117. package/src/components/viewer-overlay.tsx +1 -2
  118. package/src/hooks/use-auto-frame.ts +1 -2
  119. package/src/hooks/use-keyboard.ts +1 -2
  120. package/src/hooks/use-selected-room.ts +5 -7
  121. package/src/i18n/floor-footprint-name.ts +1 -1
  122. package/src/i18n/locales/en/messages.po +180 -182
  123. package/src/i18n/locales/es/messages.po +180 -182
  124. package/src/i18n/locales/fr/messages.po +180 -182
  125. package/src/i18n/locales/ja/messages.po +180 -182
  126. package/src/i18n/locales/zh-CN/messages.po +180 -182
  127. package/src/i18n/locales.test.ts +1 -1
  128. package/src/index.tsx +109 -131
  129. package/src/lib/below-grade.test.tsx +4 -4
  130. package/src/lib/camera-facing.test.ts +1 -1
  131. package/src/lib/camera-facing.ts +2 -3
  132. package/src/lib/editor-level-display.ts +1 -1
  133. package/src/lib/floor-footprints.test.ts +2 -2
  134. package/src/lib/floor-opening-draft.ts +1 -1
  135. package/src/lib/floorplan/annotation-visibility.test.ts +1 -1
  136. package/src/lib/floorplan/annotation-visibility.ts +1 -1
  137. package/src/lib/floorplan/floorplan-export.test.ts +1 -1
  138. package/src/lib/floorplan/floorplan-export.tsx +8 -11
  139. package/src/lib/floorplan/floorplan-extension.ts +4 -157
  140. package/src/lib/floorplan/floorplan-mode.ts +2 -1
  141. package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +1 -1
  142. package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +8 -6
  143. package/src/lib/floorplan/floorplan-plugin-install.test.tsx +3 -4
  144. package/src/lib/floorplan/geometry.ts +2 -112
  145. package/src/lib/floorplan/index.ts +0 -9
  146. package/src/lib/floorplan/items.ts +2 -1
  147. package/src/lib/floorplan/schedules.ts +6 -3
  148. package/src/lib/floorplan/sheet-export.ts +7 -3
  149. package/src/lib/floorplan/site-plan/site-parts.ts +7 -1
  150. package/src/lib/floorplan/site-plan/site-plan-layer.tsx +1 -1
  151. package/src/lib/follow-pascal-presentation.ts +1 -1
  152. package/src/lib/glb-export.ts +1 -1
  153. package/src/lib/material-paint.ts +1 -1
  154. package/src/lib/measurements.test.ts +2 -1
  155. package/src/lib/mezzanine-selection.test.ts +1 -1
  156. package/src/lib/paint-region-mode.ts +1 -1
  157. package/src/lib/point-ask/session.ts +1 -2
  158. package/src/lib/roof-duplication.test.ts +100 -0
  159. package/src/lib/roof-duplication.ts +30 -5
  160. package/src/lib/room-handle-drag.ts +2 -12
  161. package/src/lib/room-name-catalog.test.ts +2 -1
  162. package/src/lib/room-name-messages.test.ts +2 -1
  163. package/src/lib/room-name-messages.ts +1 -118
  164. package/src/lib/room-name-storage.test.ts +2 -1
  165. package/src/lib/room-selection-commands.ts +2 -2
  166. package/src/lib/room-selection.test.ts +6 -6
  167. package/src/lib/room-transform-session.ts +1 -2
  168. package/src/lib/room-zone-routing.ts +2 -2
  169. package/src/lib/selection-routing.test.ts +0 -28
  170. package/src/lib/selection-routing.ts +1 -40
  171. package/src/lib/sfx-bus.ts +8 -32
  172. package/src/lib/sfx-player.test.ts +72 -1
  173. package/src/lib/sfx-player.ts +49 -337
  174. package/src/lib/viewer-selection.test.ts +38 -10
  175. package/src/lib/wall-pull.test.ts +1 -1
  176. package/src/lib/wall-pull.ts +1 -27
  177. package/src/store/use-editor.tsx +17 -26
  178. package/src/store/use-floorplan-annotation-visibility.ts +5 -5
  179. package/src/store/use-wall-region-handles-selection.ts +1 -2
  180. package/src/components/editor/__bench__/room-label-placement.bench.ts +0 -108
  181. package/src/components/editor/first-person/build-collider-world.test.ts +0 -596
  182. package/src/components/editor/first-person/build-collider-world.ts +0 -549
  183. package/src/components/editor/first-person/ground-holes.test.ts +0 -128
  184. package/src/components/editor/first-person/ground-holes.ts +0 -130
  185. package/src/components/editor/first-person/mechanism-targets.test.ts +0 -162
  186. package/src/components/editor/first-person/mechanism-targets.ts +0 -88
  187. package/src/components/editor/first-person/terrain-collider.test.ts +0 -132
  188. package/src/components/editor/first-person/terrain-collider.ts +0 -212
  189. package/src/components/editor/floorplan-camera-sync.test.ts +0 -249
  190. package/src/components/editor/floorplan-camera-sync.ts +0 -213
  191. package/src/components/editor/localized-html.tsx +0 -22
  192. package/src/components/editor-2d/renderers/floorplan-dimension-renderer.test.tsx +0 -236
  193. package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +0 -708
  194. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.test.tsx +0 -385
  195. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +0 -670
  196. package/src/components/editor-2d/renderers/floorplan-label-angle.test.ts +0 -89
  197. package/src/components/editor-2d/renderers/floorplan-label-angle.ts +0 -106
  198. package/src/components/editor-2d/svg-paths.ts +0 -124
  199. package/src/components/ui/action-menu/action-button.tsx +0 -89
  200. package/src/components/ui/action-menu/follow-pascal-toggle.tsx +0 -42
  201. package/src/components/ui/primitives/button.tsx +0 -69
  202. package/src/components/ui/primitives/dropdown-menu.tsx +0 -232
  203. package/src/components/ui/primitives/popover.tsx +0 -42
  204. package/src/components/ui/primitives/switch.tsx +0 -29
  205. package/src/components/ui/primitives/tooltip.tsx +0 -57
  206. package/src/components/ui/scene-loader.tsx +0 -73
  207. package/src/components/viewer/floorplan-compass-button.tsx +0 -67
  208. package/src/components/viewer/floorplan-preview-geometry.test.ts +0 -107
  209. package/src/components/viewer/floorplan-preview-geometry.ts +0 -281
  210. package/src/components/viewer/floorplan-preview-navigation.test.ts +0 -43
  211. package/src/components/viewer/floorplan-preview-navigation.ts +0 -62
  212. package/src/components/viewer/floorplan-preview-visibility.test.ts +0 -35
  213. package/src/components/viewer/floorplan-preview-visibility.ts +0 -19
  214. package/src/components/viewer/floorplan-preview.tsx +0 -1065
  215. package/src/components/viewer/render-settings-menu.tsx +0 -294
  216. package/src/components/viewer/sky-control.tsx +0 -710
  217. package/src/components/viewer/use-viewer-camera-navigation-sync.ts +0 -140
  218. package/src/components/viewer/use-viewer-ui-sounds.test.ts +0 -15
  219. package/src/components/viewer/use-viewer-ui-sounds.ts +0 -46
  220. package/src/components/viewer/viewer-controls-bar.tsx +0 -496
  221. package/src/components/viewer/viewer-hover-label.tsx +0 -42
  222. package/src/components/viewer/viewer-rooms.tsx +0 -248
  223. package/src/components/viewer/viewer-scene-header.tsx +0 -286
  224. package/src/components/viewer/viewer-selection-manager.tsx +0 -124
  225. package/src/components/viewer/viewer-stage-modes.test.ts +0 -38
  226. package/src/components/viewer/viewer-stage-modes.ts +0 -49
  227. package/src/components/viewer/viewer-stage-switcher.tsx +0 -97
  228. package/src/components/viewer/viewer-stage.test.tsx +0 -47
  229. package/src/components/viewer/viewer-stage.tsx +0 -221
  230. package/src/components/walkthrough-hud.tsx +0 -193
  231. package/src/hooks/use-mobile.ts +0 -19
  232. package/src/i18n/level-name.ts +0 -39
  233. package/src/i18n/scene-theme-name.ts +0 -16
  234. package/src/lib/below-grade.ts +0 -133
  235. package/src/lib/camera-pose.test.ts +0 -237
  236. package/src/lib/camera-pose.ts +0 -172
  237. package/src/lib/floorplan/floorplan-extension.test.ts +0 -42
  238. package/src/lib/floorplan/floorplan-readonly.test.ts +0 -47
  239. package/src/lib/floorplan/floorplan-readonly.ts +0 -95
  240. package/src/lib/floorplan/marks.test.ts +0 -213
  241. package/src/lib/floorplan/marks.ts +0 -238
  242. package/src/lib/measurements.ts +0 -8
  243. package/src/lib/node-display-label.ts +0 -37
  244. package/src/lib/room-assembly-overlay.test.ts +0 -275
  245. package/src/lib/room-assembly-overlay.ts +0 -265
  246. package/src/lib/room-selection.ts +0 -417
  247. package/src/lib/scene-bounds.test.ts +0 -327
  248. package/src/lib/scene-bounds.ts +0 -233
  249. package/src/lib/typing-target.ts +0 -9
  250. package/src/lib/viewer-selection.ts +0 -595
  251. package/src/lib/zone-content.ts +0 -133
  252. package/src/store/camera-pose-store.test.ts +0 -29
  253. package/src/store/camera-pose-store.ts +0 -27
  254. package/src/store/navigation-sync-pose-store.test.ts +0 -34
  255. package/src/store/navigation-sync-pose-store.ts +0 -27
  256. package/src/store/use-audio.test.ts +0 -146
  257. package/src/store/use-audio.tsx +0 -169
  258. package/src/store/use-first-person-hud.ts +0 -40
  259. package/src/store/use-follow-pascal.test.ts +0 -62
  260. package/src/store/use-follow-pascal.ts +0 -53
@@ -1,7 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import { createContext, type ReactNode, useContext, useLayoutEffect, useRef } from 'react'
4
- import { useIsMobile } from '../../hooks/use-mobile'
4
+ import { useIsMobile } from '@pascal-app/viewer/ui'
5
5
  import { cn } from '../../lib/utils'
6
6
  import { TOP_MENU_CLEARANCE } from './action-menu/placement'
7
7
 
@@ -7,7 +7,7 @@ import {
7
7
  Tooltip,
8
8
  TooltipContent,
9
9
  TooltipTrigger,
10
- } from './../../../components/ui/primitives/tooltip'
10
+ } from '@pascal-app/viewer/ui'
11
11
  import { cn } from './../../../lib/utils'
12
12
 
13
13
  export type PanelId = string
@@ -11,7 +11,7 @@ import {
11
11
  TooltipContent,
12
12
  TooltipProvider,
13
13
  TooltipTrigger,
14
- } from '../../../../../components/ui/primitives/tooltip'
14
+ } from '@pascal-app/viewer/ui'
15
15
 
16
16
  /** A function-axis taxonomy node, assembled into a tree by the embedder. */
17
17
  export type FunctionTreeNode = {
@@ -1,5 +1,5 @@
1
1
  import { Volume2, VolumeX } from 'lucide-react'
2
- import { Button } from '../../../../../components/ui/primitives/button'
2
+ import { Button } from '@pascal-app/viewer/ui'
3
3
  import {
4
4
  Dialog,
5
5
  DialogContent,
@@ -9,7 +9,7 @@ import {
9
9
  DialogTrigger,
10
10
  } from '../../../../../components/ui/primitives/dialog'
11
11
  import { Slider } from '../../../../../components/ui/slider'
12
- import useAudio from '../../../../../store/use-audio'
12
+ import { useAudioSettings as useAudio } from '@pascal-app/viewer/sfx'
13
13
 
14
14
  export function AudioSettingsDialog() {
15
15
  const {
@@ -8,7 +8,7 @@ import {
8
8
  } from '../../../../../store/use-construction-reveal'
9
9
  import { SegmentedControl } from '../../../controls/segmented-control'
10
10
  import { Badge } from '../../../primitives/badge'
11
- import { Tooltip, TooltipContent, TooltipTrigger } from '../../../primitives/tooltip'
11
+ import { Tooltip, TooltipContent, TooltipTrigger } from '@pascal-app/viewer/ui'
12
12
 
13
13
  /** Why Framing is greyed out. */
14
14
  export const FRAMING_NEEDS_ARCHITECT = 'Framing needs the Architect plugin.'
@@ -61,7 +61,7 @@ import {
61
61
  waitForLocalImport,
62
62
  } from '../../../../../lib/send-to-app'
63
63
  import { SegmentedControl } from './../../../../../components/ui/controls/segmented-control'
64
- import { Button } from './../../../../../components/ui/primitives/button'
64
+ import { Button } from '@pascal-app/viewer/ui'
65
65
  import {
66
66
  Dialog,
67
67
  DialogContent,
@@ -69,7 +69,7 @@ import {
69
69
  DialogTrigger,
70
70
  } from './../../../../../components/ui/primitives/dialog'
71
71
  import { Input } from './../../../../../components/ui/primitives/input'
72
- import { Switch } from './../../../../../components/ui/primitives/switch'
72
+ import { Switch } from '@pascal-app/viewer/ui'
73
73
  import { cn } from './../../../../../lib/utils'
74
74
  import { refusedObjectsNotice } from './../../../../../components/editor/group-actions'
75
75
  import { addStoreyBelow, addStoreyOnTop } from './../../../../../lib/add-storey'
@@ -1,6 +1,6 @@
1
1
  import { type MessageDescriptor, usePascalI18n } from '@pascal-app/core'
2
2
  import { Keyboard } from 'lucide-react'
3
- import { Button } from './../../../../../components/ui/primitives/button'
3
+ import { Button } from '@pascal-app/viewer/ui'
4
4
  import {
5
5
  Dialog,
6
6
  DialogContent,
@@ -14,7 +14,7 @@ import {
14
14
  XCircle,
15
15
  } from 'lucide-react'
16
16
  import { useState } from 'react'
17
- import { Button } from '../../../../../components/ui/primitives/button'
17
+ import { Button } from '@pascal-app/viewer/ui'
18
18
  import {
19
19
  Dialog,
20
20
  DialogContent,
@@ -2,7 +2,7 @@ import { getProjectFileSink } from '@pascal-app/core'
2
2
  import { downloadProjectFile } from '../../../../../lib/download-project-file'
3
3
  import { AlertTriangle, Printer } from 'lucide-react'
4
4
  import { useState } from 'react'
5
- import { Button } from '../../../../../components/ui/primitives/button'
5
+ import { Button } from '@pascal-app/viewer/ui'
6
6
  import {
7
7
  isPrintLevelBundleReport,
8
8
  type PrintLevelBundleReport,
@@ -8,7 +8,7 @@ import {
8
8
  Tooltip,
9
9
  TooltipContent,
10
10
  TooltipTrigger,
11
- } from './../../../../../components/ui/primitives/tooltip'
11
+ } from '@pascal-app/viewer/ui'
12
12
  import { focusTreeNode, TreeNode, TreeNodeWrapper } from './tree-node'
13
13
  import { TreeNodeActions } from './tree-node-actions'
14
14
 
@@ -52,7 +52,7 @@ import {
52
52
  Popover,
53
53
  PopoverContent,
54
54
  PopoverTrigger,
55
- } from './../../../../../components/ui/primitives/popover'
55
+ } from '@pascal-app/viewer/ui'
56
56
  import {
57
57
  buildLevelDuplicateCreateOps,
58
58
  type LevelDuplicatePreset,
@@ -81,7 +81,7 @@ import { focusTreeNode, TreeNode, TreeNodeWrapper } from './tree-node'
81
81
  import { TreeNodeDragProvider } from './tree-node-drag'
82
82
  import { zoneDefaultName } from './default-names'
83
83
  import { nodeLabelDescriptor } from '../../../../../i18n/node-messages'
84
- import { authoredLevelName, localizedLevelName } from '../../../../../i18n/level-name'
84
+ import { authoredLevelName, localizedLevelName } from '@pascal-app/viewer/ui'
85
85
 
86
86
  // ============================================================================
87
87
  // PROPERTY LINE SECTION
@@ -1,7 +1,7 @@
1
1
  import { type AnyNodeId, usePascalI18n, useScene } from '@pascal-app/core'
2
2
  import { Pencil } from 'lucide-react'
3
3
  import { memo, useCallback, useEffect, useRef, useState } from 'react'
4
- import { displayRoomName } from './../../../../../lib/room-name-messages'
4
+ import { displayRoomName } from '@pascal-app/viewer/ui'
5
5
  import { cn } from './../../../../../lib/utils'
6
6
  import { commitTreeRoomRename } from './../../../panels/room-name-options'
7
7
 
@@ -6,7 +6,7 @@ import { useShallow } from 'zustand/react/shallow'
6
6
  import { InlineRenameInput } from './inline-rename-input'
7
7
  import { focusTreeNode, TreeNode, TreeNodeWrapper } from './tree-node'
8
8
  import { TreeNodeActions } from './tree-node-actions'
9
- import { authoredLevelName, localizedLevelName } from '../../../../../i18n/level-name'
9
+ import { authoredLevelName, localizedLevelName } from '@pascal-app/viewer/ui'
10
10
 
11
11
  interface LevelTreeNodeProps {
12
12
  nodeId: LevelNode['id']
@@ -1,4 +1,12 @@
1
- import { type AnyNodeId, type RoofNode, type RoofSegmentNode, useScene, usePascalI18n} from '@pascal-app/core'
1
+ import {
2
+ type AnyNodeId,
3
+ type RoofNode,
4
+ type RoofSegmentNode,
5
+ roofFaceOf,
6
+ useRegistryVersion,
7
+ useScene,
8
+ usePascalI18n,
9
+ } from '@pascal-app/core'
2
10
  import { useViewer } from '@pascal-app/viewer'
3
11
  import { AnimatePresence } from 'motion/react'
4
12
  import Image from 'next/image'
@@ -10,6 +18,7 @@ import { focusTreeNode, handleTreeSelection, TreeNode, TreeNodeWrapper } from '.
10
18
  import { TreeNodeActions } from './tree-node-actions'
11
19
  import { DropIndicatorLine, useTreeNodeDrag } from './tree-node-drag'
12
20
  import { roofDefaultName, roofSegmentDefaultName } from './default-names'
21
+ import { nodeLabelDescriptor } from '../../../../../i18n/node-messages'
13
22
 
14
23
  interface RoofTreeNodeProps {
15
24
  nodeId: AnyNodeId
@@ -42,10 +51,23 @@ export const RoofTreeNode = memo(function RoofTreeNode({
42
51
  .filter((n): n is RoofSegmentNode => n?.type === 'roof-segment')
43
52
  }),
44
53
  )
54
+ // Its faces of other kinds (`capabilities.roofs`: a plugin's roof planes), listed after its
55
+ // segments as the rows their kinds draw. A plugin registers its kinds after the first mount.
56
+ useRegistryVersion()
57
+ const faceIds = useScene(
58
+ useShallow(
59
+ (s) =>
60
+ ((s.nodes[nodeId] as RoofNode | undefined)?.children ?? []).filter((childId) =>
61
+ roofFaceOf(s.nodes[childId as AnyNodeId]),
62
+ ) as AnyNodeId[],
63
+ ),
64
+ )
45
65
 
46
- // Targeted selector — only re-renders when a segment of THIS roof is selected/deselected
47
- const hasSelectedChild = useViewer((state) =>
48
- segments.some((seg) => state.selection.selectedIds.includes(seg.id)),
66
+ // Targeted selector — only re-renders when a segment or face of THIS roof is selected/deselected
67
+ const hasSelectedChild = useViewer(
68
+ (state) =>
69
+ segments.some((seg) => state.selection.selectedIds.includes(seg.id)) ||
70
+ faceIds.some((id) => state.selection.selectedIds.includes(id)),
49
71
  )
50
72
 
51
73
  const handleClick = useCallback(
@@ -86,7 +108,11 @@ export const RoofTreeNode = memo(function RoofTreeNode({
86
108
  }, [isDropTarget, expanded])
87
109
 
88
110
  const segmentCount = segments.length
89
- const defaultName = roofDefaultName(i18n, segmentCount)
111
+ // A roof of faces alone reads as a roof, not as one of no segments.
112
+ const defaultName =
113
+ segmentCount === 0 && faceIds.length > 0
114
+ ? i18n._(nodeLabelDescriptor('roof', 'Roof'))
115
+ : roofDefaultName(i18n, segmentCount)
90
116
 
91
117
  // Hide the dragged segment from every roof while dragging
92
118
  const visibleSegments = drag ? segments.filter((seg) => seg.id !== drag.nodeId) : segments
@@ -99,7 +125,7 @@ export const RoofTreeNode = memo(function RoofTreeNode({
99
125
  actions={<TreeNodeActions nodeId={nodeId} />}
100
126
  depth={depth}
101
127
  expanded={expanded}
102
- hasChildren={segments.length > 0}
128
+ hasChildren={segments.length + faceIds.length > 0}
103
129
  icon={
104
130
  <Image alt="" className="object-contain" height={14} src="/icons/roof.webp" width={14} />
105
131
  }
@@ -139,7 +165,12 @@ export const RoofTreeNode = memo(function RoofTreeNode({
139
165
  </AnimatePresence>
140
166
  <RoofSegmentTreeNode
141
167
  depth={depth + 1}
142
- isLast={isLast && i === visibleSegments.length - 1 && !showIndicatorAfter}
168
+ isLast={
169
+ isLast &&
170
+ faceIds.length === 0 &&
171
+ i === visibleSegments.length - 1 &&
172
+ !showIndicatorAfter
173
+ }
143
174
  node={seg}
144
175
  />
145
176
  <AnimatePresence>
@@ -151,6 +182,14 @@ export const RoofTreeNode = memo(function RoofTreeNode({
151
182
  <AnimatePresence>
152
183
  {isDropTarget && visibleSegments.length === 0 && <DropIndicatorLine />}
153
184
  </AnimatePresence>
185
+ {faceIds.map((faceId, index) => (
186
+ <TreeNode
187
+ depth={depth + 1}
188
+ isLast={isLast && index === faceIds.length - 1}
189
+ key={faceId}
190
+ nodeId={faceId}
191
+ />
192
+ ))}
154
193
  </TreeNodeWrapper>
155
194
  </div>
156
195
  )
@@ -6,7 +6,7 @@ import {
6
6
  Popover,
7
7
  PopoverContent,
8
8
  PopoverTrigger,
9
- } from './../../../../../components/ui/primitives/popover'
9
+ } from '@pascal-app/viewer/ui'
10
10
 
11
11
  interface TreeNodeActionsProps {
12
12
  nodeId: AnyNodeId
@@ -96,6 +96,11 @@ type DragState = {
96
96
  type DropTarget = {
97
97
  parentId: string
98
98
  insertIndex: number
99
+ /**
100
+ * The row the drop lands before; none at the end. The rows are not all of the parent's
101
+ * children (a roof's faces have no drop row), so the drop is placed by id.
102
+ */
103
+ beforeId?: string
99
104
  } | null
100
105
 
101
106
  type TreeNodeDragContextValue = {
@@ -143,6 +148,10 @@ export function TreeNodeDragProvider({ children }: { children: ReactNode }) {
143
148
  if (!(drag && dropTarget)) return
144
149
 
145
150
  const state = useScene.getState()
151
+ const place = (children: string[], id: string) => {
152
+ const before = dropTarget.beforeId ? children.indexOf(dropTarget.beforeId) : -1
153
+ children.splice(before === -1 ? children.length : before, 0, id)
154
+ }
146
155
 
147
156
  if (dropTarget.parentId === drag.sourceParentId) {
148
157
  // --- Reorder within same parent ---
@@ -152,8 +161,7 @@ export function TreeNodeDragProvider({ children }: { children: ReactNode }) {
152
161
  const fromIndex = currentChildren.indexOf(drag.nodeId)
153
162
  if (fromIndex === -1) return
154
163
  currentChildren.splice(fromIndex, 1)
155
- const toIndex = Math.min(dropTarget.insertIndex, currentChildren.length)
156
- currentChildren.splice(toIndex, 0, drag.nodeId)
164
+ place(currentChildren, drag.nodeId)
157
165
  state.updateNode(dropTarget.parentId as AnyNodeId, { children: currentChildren } as any)
158
166
  }
159
167
  } else {
@@ -185,8 +193,7 @@ export function TreeNodeDragProvider({ children }: { children: ReactNode }) {
185
193
  const idx = children.indexOf(drag.nodeId)
186
194
  if (idx !== -1) {
187
195
  children.splice(idx, 1)
188
- const toIndex = Math.min(dropTarget.insertIndex, children.length)
189
- children.splice(toIndex, 0, drag.nodeId)
196
+ place(children, drag.nodeId)
190
197
  state.updateNode(dropTarget.parentId as AnyNodeId, { children } as any)
191
198
  }
192
199
  }
@@ -259,6 +266,7 @@ export function TreeNodeDragProvider({ children }: { children: ReactNode }) {
259
266
  // Find child rows to determine insert index
260
267
  const childRows = targetEl.querySelectorAll<HTMLElement>('[data-drop-child]')
261
268
  let insertIndex = childRows.length
269
+ let beforeId: string | undefined
262
270
 
263
271
  for (let i = 0; i < childRows.length; i++) {
264
272
  const row = childRows[i]!
@@ -266,11 +274,12 @@ export function TreeNodeDragProvider({ children }: { children: ReactNode }) {
266
274
  const midY = rect.top + rect.height / 2
267
275
  if (e.clientY < midY) {
268
276
  insertIndex = i
277
+ beforeId = row.dataset.dropChild
269
278
  break
270
279
  }
271
280
  }
272
281
 
273
- foundTarget = { parentId, insertIndex }
282
+ foundTarget = { parentId, insertIndex, beforeId }
274
283
  break
275
284
  }
276
285
 
@@ -16,8 +16,8 @@ import { assignZoneToUnit, enterUnitFocus } from './../../../../../lib/units'
16
16
  import { InlineRenameInput } from './inline-rename-input'
17
17
  import { TreeNodeWrapper } from './tree-node'
18
18
  import { nodeLabelDescriptor } from '../../../../../i18n/node-messages'
19
- import { localizedLevelName } from '../../../../../i18n/level-name'
20
- import { displayRoomName } from '../../../../../lib/room-name-messages'
19
+ import { localizedLevelName } from '@pascal-app/viewer/ui'
20
+ import { displayRoomName } from '@pascal-app/viewer/ui'
21
21
 
22
22
  type UnitWarningCode = ReturnType<typeof unitWarnings>[number]['code']
23
23
 
@@ -8,18 +8,18 @@ import { useViewer } from '@pascal-app/viewer'
8
8
  import { Camera, Hexagon, Save, Trash2 } from 'lucide-react'
9
9
  import { useState } from 'react'
10
10
  import { sfxEmitter } from './../../../../../lib/sfx-bus'
11
- import { collectZoneContentIds } from './../../../../../lib/zone-content'
11
+ import { collectZoneContentIds } from '@pascal-app/viewer/ui'
12
12
  import { ColorDot } from './../../../../../components/ui/primitives/color-dot'
13
13
  import {
14
14
  Popover,
15
15
  PopoverContent,
16
16
  PopoverTrigger,
17
- } from './../../../../../components/ui/primitives/popover'
17
+ } from '@pascal-app/viewer/ui'
18
18
  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
+ import { displayRoomName } from '@pascal-app/viewer/ui'
23
23
 
24
24
  function ZoneItem({ zone }: { zone: ZoneNode }) {
25
25
  const i18n = usePascalI18n()
@@ -4,7 +4,7 @@ import type { ReactNode } from 'react'
4
4
  import { editorHostPanelRegistry } from '../../../lib/plugin-panels'
5
5
  import { triggerSFX } from './../../../lib/sfx-bus'
6
6
  import { cn } from './../../../lib/utils'
7
- import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../primitives/tooltip'
7
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@pascal-app/viewer/ui'
8
8
 
9
9
  export type SidebarTab = {
10
10
  id: string
@@ -36,7 +36,7 @@ import {
36
36
  type WallTypePlacement,
37
37
  type WallTypeSaveResult,
38
38
  } from '../../../lib/wall-type'
39
- import { Button } from '../primitives/button'
39
+ import { Button } from '@pascal-app/viewer/ui'
40
40
  import {
41
41
  Dialog,
42
42
  DialogContent,
@@ -23,7 +23,7 @@ import {
23
23
  DropdownMenuLabel,
24
24
  DropdownMenuSeparator,
25
25
  DropdownMenuTrigger,
26
- } from '../primitives/dropdown-menu'
26
+ } from '@pascal-app/viewer/ui'
27
27
  import { type WallTypeEditorRequest, WallTypeEditorDialog } from './wall-type-editor-dialog'
28
28
 
29
29
  /**
@@ -1,10 +1,9 @@
1
1
  'use client'
2
2
 
3
+ import { ViewerControlsBar, ViewerSceneHeader } from '@pascal-app/viewer/ui'
3
4
  import { flushSync } from 'react-dom'
4
5
  import { requestWalkthroughPointerLock } from '../lib/walkthrough-pointer-lock'
5
6
  import useEditor from '../store/use-editor'
6
- import { ViewerControlsBar } from './viewer/viewer-controls-bar'
7
- import { ViewerSceneHeader } from './viewer/viewer-scene-header'
8
7
 
9
8
  type ProjectOwner = {
10
9
  id: string
@@ -1,8 +1,7 @@
1
1
  'use client'
2
2
 
3
- import { emitter, useScene } from '@pascal-app/core'
3
+ import { computeSceneBoundsXZ, emitter, useScene } from '@pascal-app/core'
4
4
  import { useEffect, useRef } from 'react'
5
- import { computeSceneBoundsXZ } from '../lib/scene-bounds'
6
5
 
7
6
  /**
8
7
  * Auto-frame the camera onto a freshly loaded scene.
@@ -9,7 +9,7 @@ import {
9
9
  toggleNodeMechanism,
10
10
  useScene,
11
11
  } from '@pascal-app/core'
12
- import { cancelPerfAction, markPerfAction, useViewer } from '@pascal-app/viewer'
12
+ import { cancelPerfAction, isTypingTarget, markPerfAction, useViewer } from '@pascal-app/viewer'
13
13
  import { useEffect } from 'react'
14
14
  import {
15
15
  cutSelectionToEditorClipboard,
@@ -40,7 +40,6 @@ import { sfxEmitter } from '../lib/sfx-bus'
40
40
  import { openSidebarPanel } from '../lib/sidebar-panel'
41
41
  import { activeSiteNode, clampBrushRadius } from '../lib/terrain-sculpt'
42
42
  import { armToolShortcut, toolShortcutForKey } from '../lib/tool-shortcuts'
43
- import { isTypingTarget } from '../lib/typing-target'
44
43
  import { leaveUnitFocus } from '../lib/units'
45
44
  import { subscribeWallKindReset } from '../lib/wall-draw-variant'
46
45
  import { isWallNudgeKey, nudgeSelectedWall } from '../lib/wall-nudge'
@@ -2,17 +2,17 @@
2
2
 
3
3
  import { type AnyNode, type AnyNodeId, nodeRegistry, useScene } from '@pascal-app/core'
4
4
  import { useViewer } from '@pascal-app/viewer'
5
- import { useCallback, useSyncExternalStore } from 'react'
6
- import { getFloorplanNodeExtension } from '../lib/floorplan/floorplan-extension'
7
- import { selectionEnabled } from '../lib/interaction/scope'
5
+ import type { SelectionModifierKeys } from '@pascal-app/viewer/ui'
8
6
  import {
9
7
  type RoomKey,
10
8
  RoomSelectionIndex,
11
9
  type RoomSelectionRecord,
12
10
  resolveRoomHit,
13
- } from '../lib/room-selection'
11
+ } from '@pascal-app/viewer/ui'
12
+ import { useCallback, useSyncExternalStore } from 'react'
13
+ import { getFloorplanNodeExtension } from '../lib/floorplan/floorplan-extension'
14
+ import { selectionEnabled } from '../lib/interaction/scope'
14
15
  import { shouldInterceptRoom } from '../lib/room-selection-commands'
15
- import { type SelectionModifierKeys, selectionModifiersFromEvent } from '../lib/selection-routing'
16
16
  import useEditor from '../store/use-editor'
17
17
  import useInteractionScope from '../store/use-interaction-scope'
18
18
 
@@ -150,5 +150,3 @@ export function resolvePlanRoomHit(nodeId: string | null, point: readonly [numbe
150
150
  const levelId = node?.parentId ?? useViewer.getState().selection.levelId
151
151
  return levelId ? resolveEditorRoomHit(node ?? null, levelId, point, '2d') : null
152
152
  }
153
-
154
- export { selectionModifiersFromEvent }
@@ -4,7 +4,7 @@ import {
4
4
  type PascalI18n,
5
5
  type SlabNode,
6
6
  } from '@pascal-app/core'
7
- import { displayRoomName } from '../lib/room-name-messages'
7
+ import { displayRoomName } from '@pascal-app/viewer/ui'
8
8
 
9
9
  /** `floorFootprintName` in the UI language; MCP and agents keep core's English. */
10
10
  export function localizedFloorFootprintName(