@pascal-app/editor 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) 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/node-icon-image.test.tsx +46 -0
  84. package/src/components/ui/node-icon-image.tsx +33 -1
  85. package/src/components/ui/panels/collections/collections-popover.tsx +2 -2
  86. package/src/components/ui/panels/floor-foundation-panel.tsx +2 -2
  87. package/src/components/ui/panels/floor-opening-panel.tsx +4 -4
  88. package/src/components/ui/panels/node-display.ts +1 -1
  89. package/src/components/ui/panels/panel-manager.tsx +1 -1
  90. package/src/components/ui/panels/panel-wrapper.tsx +1 -1
  91. package/src/components/ui/panels/parametric-inspector.tsx +1 -1
  92. package/src/components/ui/panels/room-construction-rows.tsx +6 -6
  93. package/src/components/ui/panels/room-name-options.ts +1 -5
  94. package/src/components/ui/panels/room-panel.tsx +6 -8
  95. package/src/components/ui/primitives/color-dot.tsx +2 -2
  96. package/src/components/ui/primitives/opacity-control.tsx +2 -2
  97. package/src/components/ui/primitives/sidebar.tsx +3 -3
  98. package/src/components/ui/right-stack.tsx +1 -1
  99. package/src/components/ui/sidebar/icon-rail.tsx +1 -1
  100. package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +1 -1
  101. package/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +2 -2
  102. package/src/components/ui/sidebar/panels/settings-panel/construction-animation-setting.tsx +1 -1
  103. package/src/components/ui/sidebar/panels/settings-panel/index.tsx +2 -2
  104. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +1 -1
  105. package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +1 -1
  106. package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +1 -1
  107. package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +1 -1
  108. package/src/components/ui/sidebar/panels/site-panel/index.tsx +2 -2
  109. package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +1 -1
  110. package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +1 -1
  111. package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +46 -7
  112. package/src/components/ui/sidebar/panels/site-panel/tree-node-actions.tsx +1 -1
  113. package/src/components/ui/sidebar/panels/site-panel/tree-node-drag.tsx +14 -5
  114. package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +2 -2
  115. package/src/components/ui/sidebar/panels/zone-panel/index.tsx +3 -3
  116. package/src/components/ui/sidebar/tab-bar.tsx +1 -1
  117. package/src/components/ui/wall-type/wall-type-editor-dialog.tsx +1 -1
  118. package/src/components/ui/wall-type/wall-type-menu.tsx +1 -1
  119. package/src/components/viewer-overlay.tsx +1 -2
  120. package/src/hooks/use-auto-frame.ts +1 -2
  121. package/src/hooks/use-keyboard.ts +1 -2
  122. package/src/hooks/use-selected-room.ts +5 -7
  123. package/src/i18n/floor-footprint-name.ts +1 -1
  124. package/src/i18n/locales/en/messages.po +180 -182
  125. package/src/i18n/locales/es/messages.po +180 -182
  126. package/src/i18n/locales/fr/messages.po +180 -182
  127. package/src/i18n/locales/ja/messages.po +180 -182
  128. package/src/i18n/locales/zh-CN/messages.po +180 -182
  129. package/src/i18n/locales.test.ts +1 -1
  130. package/src/index.tsx +109 -131
  131. package/src/lib/below-grade.test.tsx +4 -4
  132. package/src/lib/camera-facing.test.ts +1 -1
  133. package/src/lib/camera-facing.ts +2 -3
  134. package/src/lib/editor-level-display.ts +1 -1
  135. package/src/lib/floor-footprints.test.ts +2 -2
  136. package/src/lib/floor-opening-draft.ts +1 -1
  137. package/src/lib/floorplan/annotation-visibility.test.ts +1 -1
  138. package/src/lib/floorplan/annotation-visibility.ts +1 -1
  139. package/src/lib/floorplan/floorplan-export.test.ts +1 -1
  140. package/src/lib/floorplan/floorplan-export.tsx +8 -11
  141. package/src/lib/floorplan/floorplan-extension.ts +4 -157
  142. package/src/lib/floorplan/floorplan-mode.ts +2 -1
  143. package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +1 -1
  144. package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +8 -6
  145. package/src/lib/floorplan/floorplan-plugin-install.test.tsx +3 -4
  146. package/src/lib/floorplan/geometry.ts +2 -112
  147. package/src/lib/floorplan/index.ts +0 -9
  148. package/src/lib/floorplan/items.ts +2 -1
  149. package/src/lib/floorplan/schedules.ts +6 -3
  150. package/src/lib/floorplan/sheet-export.ts +7 -3
  151. package/src/lib/floorplan/site-plan/site-parts.ts +7 -1
  152. package/src/lib/floorplan/site-plan/site-plan-layer.tsx +1 -1
  153. package/src/lib/follow-pascal-presentation.ts +1 -1
  154. package/src/lib/glb-export.ts +1 -1
  155. package/src/lib/material-paint.ts +1 -1
  156. package/src/lib/measurements.test.ts +2 -1
  157. package/src/lib/mezzanine-selection.test.ts +1 -1
  158. package/src/lib/paint-region-mode.ts +1 -1
  159. package/src/lib/point-ask/session.ts +1 -2
  160. package/src/lib/roof-duplication.test.ts +100 -0
  161. package/src/lib/roof-duplication.ts +30 -5
  162. package/src/lib/room-handle-drag.ts +2 -12
  163. package/src/lib/room-name-catalog.test.ts +2 -1
  164. package/src/lib/room-name-messages.test.ts +2 -1
  165. package/src/lib/room-name-messages.ts +1 -118
  166. package/src/lib/room-name-storage.test.ts +2 -1
  167. package/src/lib/room-selection-commands.ts +2 -2
  168. package/src/lib/room-selection.test.ts +6 -6
  169. package/src/lib/room-transform-session.ts +1 -2
  170. package/src/lib/room-zone-routing.ts +2 -2
  171. package/src/lib/selection-routing.test.ts +0 -28
  172. package/src/lib/selection-routing.ts +1 -40
  173. package/src/lib/sfx-bus.ts +8 -32
  174. package/src/lib/sfx-player.test.ts +72 -1
  175. package/src/lib/sfx-player.ts +49 -337
  176. package/src/lib/viewer-selection.test.ts +38 -10
  177. package/src/lib/wall-pull.test.ts +1 -1
  178. package/src/lib/wall-pull.ts +1 -27
  179. package/src/store/use-editor.tsx +17 -26
  180. package/src/store/use-floorplan-annotation-visibility.ts +5 -5
  181. package/src/store/use-wall-region-handles-selection.ts +1 -2
  182. package/src/components/editor/__bench__/room-label-placement.bench.ts +0 -108
  183. package/src/components/editor/first-person/build-collider-world.test.ts +0 -596
  184. package/src/components/editor/first-person/build-collider-world.ts +0 -549
  185. package/src/components/editor/first-person/ground-holes.test.ts +0 -128
  186. package/src/components/editor/first-person/ground-holes.ts +0 -130
  187. package/src/components/editor/first-person/mechanism-targets.test.ts +0 -162
  188. package/src/components/editor/first-person/mechanism-targets.ts +0 -88
  189. package/src/components/editor/first-person/terrain-collider.test.ts +0 -132
  190. package/src/components/editor/first-person/terrain-collider.ts +0 -212
  191. package/src/components/editor/floorplan-camera-sync.test.ts +0 -249
  192. package/src/components/editor/floorplan-camera-sync.ts +0 -213
  193. package/src/components/editor/localized-html.tsx +0 -22
  194. package/src/components/editor-2d/renderers/floorplan-dimension-renderer.test.tsx +0 -236
  195. package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +0 -708
  196. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.test.tsx +0 -385
  197. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +0 -670
  198. package/src/components/editor-2d/renderers/floorplan-label-angle.test.ts +0 -89
  199. package/src/components/editor-2d/renderers/floorplan-label-angle.ts +0 -106
  200. package/src/components/editor-2d/svg-paths.ts +0 -124
  201. package/src/components/ui/action-menu/action-button.tsx +0 -89
  202. package/src/components/ui/action-menu/follow-pascal-toggle.tsx +0 -42
  203. package/src/components/ui/primitives/button.tsx +0 -69
  204. package/src/components/ui/primitives/dropdown-menu.tsx +0 -232
  205. package/src/components/ui/primitives/popover.tsx +0 -42
  206. package/src/components/ui/primitives/switch.tsx +0 -29
  207. package/src/components/ui/primitives/tooltip.tsx +0 -57
  208. package/src/components/ui/scene-loader.tsx +0 -73
  209. package/src/components/viewer/floorplan-compass-button.tsx +0 -67
  210. package/src/components/viewer/floorplan-preview-geometry.test.ts +0 -107
  211. package/src/components/viewer/floorplan-preview-geometry.ts +0 -281
  212. package/src/components/viewer/floorplan-preview-navigation.test.ts +0 -43
  213. package/src/components/viewer/floorplan-preview-navigation.ts +0 -62
  214. package/src/components/viewer/floorplan-preview-visibility.test.ts +0 -35
  215. package/src/components/viewer/floorplan-preview-visibility.ts +0 -19
  216. package/src/components/viewer/floorplan-preview.tsx +0 -1065
  217. package/src/components/viewer/render-settings-menu.tsx +0 -294
  218. package/src/components/viewer/sky-control.tsx +0 -710
  219. package/src/components/viewer/use-viewer-camera-navigation-sync.ts +0 -140
  220. package/src/components/viewer/use-viewer-ui-sounds.test.ts +0 -15
  221. package/src/components/viewer/use-viewer-ui-sounds.ts +0 -46
  222. package/src/components/viewer/viewer-controls-bar.tsx +0 -496
  223. package/src/components/viewer/viewer-hover-label.tsx +0 -42
  224. package/src/components/viewer/viewer-rooms.tsx +0 -248
  225. package/src/components/viewer/viewer-scene-header.tsx +0 -286
  226. package/src/components/viewer/viewer-selection-manager.tsx +0 -124
  227. package/src/components/viewer/viewer-stage-modes.test.ts +0 -38
  228. package/src/components/viewer/viewer-stage-modes.ts +0 -49
  229. package/src/components/viewer/viewer-stage-switcher.tsx +0 -97
  230. package/src/components/viewer/viewer-stage.test.tsx +0 -47
  231. package/src/components/viewer/viewer-stage.tsx +0 -221
  232. package/src/components/walkthrough-hud.tsx +0 -193
  233. package/src/hooks/use-mobile.ts +0 -19
  234. package/src/i18n/level-name.ts +0 -39
  235. package/src/i18n/scene-theme-name.ts +0 -16
  236. package/src/lib/below-grade.ts +0 -133
  237. package/src/lib/camera-pose.test.ts +0 -237
  238. package/src/lib/camera-pose.ts +0 -172
  239. package/src/lib/floorplan/floorplan-extension.test.ts +0 -42
  240. package/src/lib/floorplan/floorplan-readonly.test.ts +0 -47
  241. package/src/lib/floorplan/floorplan-readonly.ts +0 -95
  242. package/src/lib/floorplan/marks.test.ts +0 -213
  243. package/src/lib/floorplan/marks.ts +0 -238
  244. package/src/lib/measurements.ts +0 -8
  245. package/src/lib/node-display-label.ts +0 -37
  246. package/src/lib/room-assembly-overlay.test.ts +0 -275
  247. package/src/lib/room-assembly-overlay.ts +0 -265
  248. package/src/lib/room-selection.ts +0 -417
  249. package/src/lib/scene-bounds.test.ts +0 -327
  250. package/src/lib/scene-bounds.ts +0 -233
  251. package/src/lib/typing-target.ts +0 -9
  252. package/src/lib/viewer-selection.ts +0 -595
  253. package/src/lib/zone-content.ts +0 -133
  254. package/src/store/camera-pose-store.test.ts +0 -29
  255. package/src/store/camera-pose-store.ts +0 -27
  256. package/src/store/navigation-sync-pose-store.test.ts +0 -34
  257. package/src/store/navigation-sync-pose-store.ts +0 -27
  258. package/src/store/use-audio.test.ts +0 -146
  259. package/src/store/use-audio.tsx +0 -169
  260. package/src/store/use-first-person-hud.ts +0 -40
  261. package/src/store/use-follow-pascal.test.ts +0 -62
  262. package/src/store/use-follow-pascal.ts +0 -53
@@ -4,11 +4,7 @@ import {
4
4
  numberedRoomName,
5
5
  type RoomNameSection,
6
6
  } from '../../../lib/room-name-catalog'
7
- import {
8
- type DescriptorTranslator,
9
- displayRoomName,
10
- editedRoomName,
11
- } from '../../../lib/room-name-messages'
7
+ import { type DescriptorTranslator, displayRoomName, editedRoomName } from '@pascal-app/viewer/ui'
12
8
  import { renameRoom } from '../../../lib/room-structure-commands'
13
9
 
14
10
  export type RoomNameOption = {
@@ -21,7 +21,7 @@ import { formatAreaLabel, formatLinearMeasurement } from '@pascal-app/core'
21
21
  import { addMezzanineStairs } from '../../../lib/mezzanine-stairs'
22
22
  import { roomFloorBase, roomRelativeFloorHeight } from '../../../lib/room-construction-commands'
23
23
  import { mezzanineElevationBounds } from '../../../lib/room-handle-drag'
24
- import type { RoomSelectionRecord } from '../../../lib/room-selection'
24
+ import type { RoomSelectionRecord } from '@pascal-app/viewer/ui'
25
25
  import {
26
26
  baseRoomName,
27
27
  type RoomNameSection,
@@ -39,9 +39,9 @@ import {
39
39
  DropdownMenuItem,
40
40
  DropdownMenuSeparator,
41
41
  DropdownMenuTrigger,
42
- } from '../primitives/dropdown-menu'
42
+ } from '@pascal-app/viewer/ui'
43
43
  import { ColorDot } from '../primitives/color-dot'
44
- import { Popover, PopoverAnchor, PopoverContent } from '../primitives/popover'
44
+ import { Popover, PopoverAnchor, PopoverContent } from '@pascal-app/viewer/ui'
45
45
  import { ShortcutToken } from '../primitives/shortcut-token'
46
46
  import { PanelWrapper } from './panel-wrapper'
47
47
  import { RoomOpeningsField } from './room-openings-field'
@@ -59,12 +59,10 @@ import {
59
59
  type RoomNameOption,
60
60
  roomNameOptions,
61
61
  } from './room-name-options'
62
- import { localizedLevelName } from '../../../i18n/level-name'
62
+ import { localizedLevelName } from '@pascal-app/viewer/ui'
63
63
  import { messageWithSlot } from '../../../i18n/message-slot'
64
- import {
65
- displayRoomName,
66
- localizedRoomNameSections,
67
- } from '../../../lib/room-name-messages'
64
+ import { displayRoomName } from '@pascal-app/viewer/ui'
65
+ import { localizedRoomNameSections } from '../../../lib/room-name-messages'
68
66
 
69
67
  /** "Room · Ground floor", "Mezzanine · in Kitchen", "Room · not enclosed". */
70
68
  export function roomKicker(
@@ -3,8 +3,8 @@
3
3
  import { ZONE_COLOR_PALETTE } from '@pascal-app/core'
4
4
  import { useState } from 'react'
5
5
  import { cn } from '../../../lib/utils'
6
- import { Popover, PopoverContent, PopoverTrigger } from './popover'
7
- import { Tooltip, TooltipContent, TooltipTrigger } from './tooltip'
6
+ import { Popover, PopoverContent, PopoverTrigger } from '@pascal-app/viewer/ui'
7
+ import { Tooltip, TooltipContent, TooltipTrigger } from '@pascal-app/viewer/ui'
8
8
 
9
9
  export const PALETTE_COLORS = ZONE_COLOR_PALETTE
10
10
 
@@ -2,8 +2,8 @@
2
2
 
3
3
  import { Eye, EyeOff } from 'lucide-react'
4
4
  import { useState } from 'react'
5
- import { Button } from '../../../components/ui/primitives/button'
6
- import { Popover, PopoverContent, PopoverTrigger } from '../../../components/ui/primitives/popover'
5
+ import { Button } from '@pascal-app/viewer/ui'
6
+ import { Popover, PopoverContent, PopoverTrigger } from '@pascal-app/viewer/ui'
7
7
  import { Slider } from '../../../components/ui/primitives/slider'
8
8
  import { cn } from '../../../lib/utils'
9
9
 
@@ -6,7 +6,7 @@ import { PanelLeftIcon } from 'lucide-react'
6
6
  import * as React from 'react'
7
7
  import { create } from 'zustand'
8
8
  import { persist } from 'zustand/middleware'
9
- import { Button } from './../../../components/ui/primitives/button'
9
+ import { Button } from '@pascal-app/viewer/ui'
10
10
  import { Input } from './../../../components/ui/primitives/input'
11
11
  import { Separator } from './../../../components/ui/primitives/separator'
12
12
  import {
@@ -22,8 +22,8 @@ import {
22
22
  TooltipContent,
23
23
  TooltipProvider,
24
24
  TooltipTrigger,
25
- } from './../../../components/ui/primitives/tooltip'
26
- import { useIsMobile } from './../../../hooks/use-mobile'
25
+ } from '@pascal-app/viewer/ui'
26
+ import { useIsMobile } from '@pascal-app/viewer/ui'
27
27
  import { hydrationGatedStorage } from './../../../lib/hydration-gated-storage'
28
28
  import { cn } from './../../../lib/utils'
29
29
 
@@ -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(