@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,232 +0,0 @@
1
- 'use client'
2
-
3
- import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
4
- import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react'
5
- import type * as React from 'react'
6
-
7
- import { cn } from '../../../lib/utils'
8
-
9
- function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
10
- return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
11
- }
12
-
13
- function DropdownMenuPortal({
14
- ...props
15
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
16
- return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
17
- }
18
-
19
- function DropdownMenuTrigger({
20
- ...props
21
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
22
- return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
23
- }
24
-
25
- function DropdownMenuContent({
26
- className,
27
- sideOffset = 4,
28
- ...props
29
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
30
- return (
31
- <DropdownMenuPrimitive.Portal>
32
- <DropdownMenuPrimitive.Content
33
- className={cn(
34
- 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-32 origin-(--radix-dropdown-menu-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=open]:animate-in',
35
- className,
36
- )}
37
- data-slot="dropdown-menu-content"
38
- sideOffset={sideOffset}
39
- {...props}
40
- />
41
- </DropdownMenuPrimitive.Portal>
42
- )
43
- }
44
-
45
- function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
46
- return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
47
- }
48
-
49
- function DropdownMenuItem({
50
- className,
51
- inset,
52
- variant = 'default',
53
- ...props
54
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
55
- inset?: boolean
56
- variant?: 'default' | 'destructive'
57
- }) {
58
- return (
59
- <DropdownMenuPrimitive.Item
60
- className={cn(
61
- "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 font-barlow text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-inset:pl-8 data-[variant=destructive]:text-destructive data-disabled:opacity-50 data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 data-[variant=destructive]:*:[svg]:text-destructive!",
62
- className,
63
- )}
64
- data-inset={inset}
65
- data-slot="dropdown-menu-item"
66
- data-variant={variant}
67
- {...props}
68
- />
69
- )
70
- }
71
-
72
- function DropdownMenuCheckboxItem({
73
- className,
74
- children,
75
- checked,
76
- ...props
77
- }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
78
- return (
79
- <DropdownMenuPrimitive.CheckboxItem
80
- checked={checked}
81
- className={cn(
82
- "relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 font-barlow text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
83
- className,
84
- )}
85
- data-slot="dropdown-menu-checkbox-item"
86
- {...props}
87
- >
88
- <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
89
- <DropdownMenuPrimitive.ItemIndicator>
90
- <CheckIcon className="size-4" />
91
- </DropdownMenuPrimitive.ItemIndicator>
92
- </span>
93
- {children}
94
- </DropdownMenuPrimitive.CheckboxItem>
95
- )
96
- }
97
-
98
- function DropdownMenuRadioGroup({
99
- ...props
100
- }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
101
- return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />
102
- }
103
-
104
- function DropdownMenuRadioItem({
105
- className,
106
- children,
107
- ...props
108
- }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
109
- return (
110
- <DropdownMenuPrimitive.RadioItem
111
- className={cn(
112
- "relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 font-barlow text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
113
- className,
114
- )}
115
- data-slot="dropdown-menu-radio-item"
116
- {...props}
117
- >
118
- <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
119
- <DropdownMenuPrimitive.ItemIndicator>
120
- <CircleIcon className="size-2 fill-current" />
121
- </DropdownMenuPrimitive.ItemIndicator>
122
- </span>
123
- {children}
124
- </DropdownMenuPrimitive.RadioItem>
125
- )
126
- }
127
-
128
- function DropdownMenuLabel({
129
- className,
130
- inset,
131
- ...props
132
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
133
- inset?: boolean
134
- }) {
135
- return (
136
- <DropdownMenuPrimitive.Label
137
- className={cn('px-2 py-1.5 font-barlow font-medium text-sm data-inset:pl-8', className)}
138
- data-inset={inset}
139
- data-slot="dropdown-menu-label"
140
- {...props}
141
- />
142
- )
143
- }
144
-
145
- function DropdownMenuSeparator({
146
- className,
147
- ...props
148
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
149
- return (
150
- <DropdownMenuPrimitive.Separator
151
- className={cn('-mx-1 my-1 h-px bg-border', className)}
152
- data-slot="dropdown-menu-separator"
153
- {...props}
154
- />
155
- )
156
- }
157
-
158
- function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {
159
- return (
160
- <span
161
- className={cn('ml-auto text-muted-foreground text-xs tracking-widest', className)}
162
- data-slot="dropdown-menu-shortcut"
163
- {...props}
164
- />
165
- )
166
- }
167
-
168
- function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
169
- return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
170
- }
171
-
172
- function DropdownMenuSubTrigger({
173
- className,
174
- inset,
175
- children,
176
- ...props
177
- }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
178
- inset?: boolean
179
- }) {
180
- return (
181
- <DropdownMenuPrimitive.SubTrigger
182
- className={cn(
183
- "flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 font-barlow text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-inset:pl-8 data-[state=open]:text-accent-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0",
184
- className,
185
- )}
186
- data-inset={inset}
187
- data-slot="dropdown-menu-sub-trigger"
188
- {...props}
189
- >
190
- {children}
191
- <ChevronRightIcon className="ml-auto size-4" />
192
- </DropdownMenuPrimitive.SubTrigger>
193
- )
194
- }
195
-
196
- function DropdownMenuSubContent({
197
- className,
198
- ...props
199
- }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
200
- // Portalled so the side-opening submenu escapes the parent content's
201
- // `overflow-x-hidden` clip (otherwise it renders but is invisible).
202
- return (
203
- <DropdownMenuPrimitive.Portal>
204
- <DropdownMenuPrimitive.SubContent
205
- className={cn(
206
- 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-32 origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=closed]:animate-out data-[state=open]:animate-in',
207
- className,
208
- )}
209
- data-slot="dropdown-menu-sub-content"
210
- {...props}
211
- />
212
- </DropdownMenuPrimitive.Portal>
213
- )
214
- }
215
-
216
- export {
217
- DropdownMenu,
218
- DropdownMenuPortal,
219
- DropdownMenuTrigger,
220
- DropdownMenuContent,
221
- DropdownMenuGroup,
222
- DropdownMenuLabel,
223
- DropdownMenuItem,
224
- DropdownMenuCheckboxItem,
225
- DropdownMenuRadioGroup,
226
- DropdownMenuRadioItem,
227
- DropdownMenuSeparator,
228
- DropdownMenuShortcut,
229
- DropdownMenuSub,
230
- DropdownMenuSubTrigger,
231
- DropdownMenuSubContent,
232
- }
@@ -1,42 +0,0 @@
1
- 'use client'
2
-
3
- import * as PopoverPrimitive from '@radix-ui/react-popover'
4
- import type * as React from 'react'
5
-
6
- import { cn } from '../../../lib/utils'
7
-
8
- function Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>) {
9
- return <PopoverPrimitive.Root data-slot="popover" {...props} />
10
- }
11
-
12
- function PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
13
- return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
14
- }
15
-
16
- function PopoverContent({
17
- className,
18
- align = 'center',
19
- sideOffset = 4,
20
- ...props
21
- }: React.ComponentProps<typeof PopoverPrimitive.Content>) {
22
- return (
23
- <PopoverPrimitive.Portal>
24
- <PopoverPrimitive.Content
25
- align={align}
26
- className={cn(
27
- 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[state=closed]:animate-out data-[state=open]:animate-in',
28
- className,
29
- )}
30
- data-slot="popover-content"
31
- sideOffset={sideOffset}
32
- {...props}
33
- />
34
- </PopoverPrimitive.Portal>
35
- )
36
- }
37
-
38
- function PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
39
- return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
40
- }
41
-
42
- export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
@@ -1,29 +0,0 @@
1
- 'use client'
2
-
3
- import * as SwitchPrimitives from '@radix-ui/react-switch'
4
- import * as React from 'react'
5
-
6
- import { cn } from './../../../lib/utils'
7
-
8
- const Switch = React.forwardRef<
9
- React.ElementRef<typeof SwitchPrimitives.Root>,
10
- React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
11
- >(({ className, ...props }, ref) => (
12
- <SwitchPrimitives.Root
13
- className={cn(
14
- 'peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-xs transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
15
- className,
16
- )}
17
- {...props}
18
- ref={ref}
19
- >
20
- <SwitchPrimitives.Thumb
21
- className={cn(
22
- 'pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0',
23
- )}
24
- />
25
- </SwitchPrimitives.Root>
26
- ))
27
- Switch.displayName = SwitchPrimitives.Root.displayName
28
-
29
- export { Switch }
@@ -1,57 +0,0 @@
1
- 'use client'
2
-
3
- import * as TooltipPrimitive from '@radix-ui/react-tooltip'
4
- import type * as React from 'react'
5
-
6
- import { cn } from '../../../lib/utils'
7
-
8
- function TooltipProvider({
9
- delayDuration = 0,
10
- ...props
11
- }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
12
- return (
13
- <TooltipPrimitive.Provider
14
- data-slot="tooltip-provider"
15
- delayDuration={delayDuration}
16
- {...props}
17
- />
18
- )
19
- }
20
-
21
- function Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
22
- return (
23
- <TooltipProvider>
24
- <TooltipPrimitive.Root data-slot="tooltip" {...props} />
25
- </TooltipProvider>
26
- )
27
- }
28
-
29
- function TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
30
- return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
31
- }
32
-
33
- function TooltipContent({
34
- className,
35
- sideOffset = 0,
36
- children,
37
- ...props
38
- }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
39
- return (
40
- <TooltipPrimitive.Portal>
41
- <TooltipPrimitive.Content
42
- className={cn(
43
- 'fade-in-0 zoom-in-95 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in text-balance rounded-md bg-foreground px-3 py-1.5 font-barlow text-background text-xs data-[state=closed]:animate-out',
44
- className,
45
- )}
46
- data-slot="tooltip-content"
47
- sideOffset={sideOffset}
48
- {...props}
49
- >
50
- {children}
51
- <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
52
- </TooltipPrimitive.Content>
53
- </TooltipPrimitive.Portal>
54
- )
55
- }
56
-
57
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
@@ -1,73 +0,0 @@
1
- 'use client'
2
-
3
- import { useEffect, useState } from 'react'
4
- import { cn } from '../../lib/utils'
5
- import { Button } from './primitives/button'
6
-
7
- const LOADERS = [
8
- 'pascal-loader-1',
9
- 'pascal-loader-2',
10
- 'pascal-loader-3',
11
- 'pascal-loader-4',
12
- 'pascal-loader-5',
13
- ]
14
-
15
- interface SceneLoaderProps {
16
- className?: string
17
- fullScreen?: boolean
18
- }
19
-
20
- export function SceneLoader({ className, fullScreen = false }: SceneLoaderProps) {
21
- const [loaderClass, setLoaderClass] = useState(LOADERS[0]!)
22
-
23
- useEffect(() => {
24
- // Pick a random loader on mount
25
- setLoaderClass(LOADERS[Math.floor(Math.random() * LOADERS.length)] ?? LOADERS[0]!)
26
- }, [])
27
-
28
- return (
29
- <div
30
- className={cn(
31
- 'z-100 flex items-center justify-center bg-background/80 backdrop-blur-md transition-opacity duration-300',
32
- fullScreen ? 'fixed inset-0' : 'absolute inset-0',
33
- className,
34
- )}
35
- >
36
- <div className={cn(loaderClass, 'text-foreground opacity-80')} />
37
- </div>
38
- )
39
- }
40
-
41
- interface SceneLoadFailedProps {
42
- className?: string
43
- onRetry: () => void
44
- }
45
-
46
- /**
47
- * Replaces the loader when the host could not deliver the scene. Rendered
48
- * INSTEAD of falling back to an empty default scene: a session that shows
49
- * scaffold nodes after a failed load autosaves that scaffold over the real
50
- * project (prod scene-wipe class, 2026-09-02).
51
- */
52
- export function SceneLoadFailed({ className, onRetry }: SceneLoadFailedProps) {
53
- return (
54
- <div
55
- className={cn(
56
- 'z-100 flex flex-col items-center justify-center gap-4 bg-background/90 px-6 text-center backdrop-blur-md',
57
- 'absolute inset-0',
58
- className,
59
- )}
60
- role="alert"
61
- >
62
- <div className="flex flex-col gap-1">
63
- <p className="font-medium text-foreground text-sm">This project couldn't be loaded</p>
64
- <p className="text-muted-foreground text-sm">
65
- Nothing was changed. Check your connection and try again.
66
- </p>
67
- </div>
68
- <Button className="rounded-full" onClick={onRetry} size="sm" type="button">
69
- Try again
70
- </Button>
71
- </div>
72
- )
73
- }
@@ -1,67 +0,0 @@
1
- 'use client'
2
-
3
- import { usePascalI18n } from '@pascal-app/core'
4
- import type React from 'react'
5
- import { cn } from '../../lib/utils'
6
- import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/primitives/tooltip'
7
- import { SKY_COMPASS_BOTTOM, useSkyPanel } from './sky-control'
8
-
9
- export type FloorplanCompassButtonProps = {
10
- northRotationDeg: number
11
- onAlignNorth: () => void
12
- needleRef?: React.RefObject<SVGSVGElement | null>
13
- /** Horizontal anchor; defaults to the bottom-left corner. */
14
- style?: React.CSSProperties
15
- }
16
-
17
- export function FloorplanCompassButton({
18
- northRotationDeg,
19
- onAlignNorth,
20
- needleRef,
21
- style,
22
- }: FloorplanCompassButtonProps) {
23
- const i18n = usePascalI18n()
24
- // Beside the sky chip (not when moved to the plan's right corner), centre on the docks' row.
25
- const besideSky = useSkyPanel((s) => s.hosts > 0) && style?.right === undefined
26
- const label = i18n._({
27
- id: 'viewer.floorplan.alignNorth',
28
- message: 'Align view to north',
29
- })
30
- return (
31
- <Tooltip>
32
- <TooltipTrigger asChild>
33
- <button
34
- aria-label={label}
35
- className={cn(
36
- 'group pointer-events-auto absolute bottom-3 z-30 flex h-8 w-8 items-center justify-center rounded-full border border-black/10 bg-white/85 shadow-sm backdrop-blur-md transition hover:bg-white hover:shadow-md dark:border-white/10 dark:bg-neutral-900/85 dark:hover:bg-neutral-900',
37
- !style && 'left-3',
38
- )}
39
- style={besideSky ? { bottom: SKY_COMPASS_BOTTOM, ...style } : style}
40
- onClick={(event) => {
41
- event.preventDefault()
42
- event.stopPropagation()
43
- onAlignNorth()
44
- }}
45
- onPointerDown={(event) => {
46
- event.stopPropagation()
47
- }}
48
- type="button"
49
- >
50
- <span className="relative flex h-6 w-6 items-center justify-center rounded-full bg-[#b8b8b8] shadow-inner dark:bg-neutral-700">
51
- <svg
52
- aria-hidden="true"
53
- className="h-6 w-6"
54
- ref={needleRef}
55
- style={{ transform: `rotate(${northRotationDeg}deg)` }}
56
- viewBox="0 0 48 48"
57
- >
58
- <path d="M24 4.5 31.5 25 24 21.5 16.5 25Z" fill="#f15b5b" />
59
- <path d="M24 43.5 16.5 23 24 26.5 31.5 23Z" fill="#ffffff" />
60
- </svg>
61
- </span>
62
- </button>
63
- </TooltipTrigger>
64
- <TooltipContent side="right">{label}</TooltipContent>
65
- </Tooltip>
66
- )
67
- }
@@ -1,107 +0,0 @@
1
- import { describe, expect, test } from 'bun:test'
2
- import type { FloorplanGeometry } from '@pascal-app/core'
3
- import {
4
- clientToFloorplanPoint,
5
- getFloorplanBounds,
6
- padFloorplanBounds,
7
- panFloorplanViewBox,
8
- scaleFloorplanViewBox,
9
- scaleFloorplanViewBoxBetweenClients,
10
- } from './floorplan-preview-geometry'
11
-
12
- describe('floorplan preview bounds', () => {
13
- test('composes nested translation and rotation', () => {
14
- const geometry: FloorplanGeometry = {
15
- kind: 'group',
16
- transform: { translate: [10, 5], rotate: Math.PI / 2 },
17
- children: [
18
- {
19
- kind: 'rect',
20
- x: 0,
21
- y: 0,
22
- width: 4,
23
- height: 2,
24
- },
25
- ],
26
- }
27
-
28
- expect(getFloorplanBounds([geometry])).toEqual({
29
- minX: 8,
30
- minY: 5,
31
- maxX: 10,
32
- maxY: 9,
33
- })
34
- })
35
-
36
- test('includes image rotation and dimension baselines', () => {
37
- const geometries: FloorplanGeometry[] = [
38
- {
39
- kind: 'image',
40
- url: '/symbol.png',
41
- center: [2, 3],
42
- width: 4,
43
- height: 2,
44
- rotation: Math.PI / 2,
45
- },
46
- {
47
- kind: 'dimension',
48
- start: [-3, -2],
49
- end: [3, -2],
50
- dimensionStart: [-4, -1],
51
- dimensionEnd: [4, -1],
52
- offsetNormal: [0, -1],
53
- offsetDistance: 1,
54
- extensionOvershoot: 0.2,
55
- text: '8 m',
56
- },
57
- ]
58
-
59
- expect(getFloorplanBounds(geometries)).toEqual({
60
- minX: -4,
61
- minY: -2,
62
- maxX: 4,
63
- maxY: 5,
64
- })
65
- })
66
-
67
- test('pads narrow plans by a usable minimum', () => {
68
- expect(padFloorplanBounds({ minX: 0, minY: 0, maxX: 0, maxY: 0 })).toEqual({
69
- minX: -0.75,
70
- minY: -0.75,
71
- maxX: 0.75,
72
- maxY: 0.75,
73
- })
74
- })
75
-
76
- test('maps clients through letterboxed SVG content', () => {
77
- const viewBox = { x: 0, y: 0, width: 10, height: 10 }
78
- const viewport = { left: 0, top: 0, width: 1000, height: 500 }
79
-
80
- expect(clientToFloorplanPoint(viewBox, viewport, 250, 0)).toEqual([0, 0])
81
- expect(clientToFloorplanPoint(viewBox, viewport, 750, 500)).toEqual([10, 10])
82
- expect(panFloorplanViewBox(viewBox, viewport, [500, 250], [550, 250])).toEqual({
83
- x: -1,
84
- y: 0,
85
- width: 10,
86
- height: 10,
87
- })
88
- })
89
-
90
- test('keeps the plan point beneath a moving pinch midpoint', () => {
91
- const viewBox = { x: 0, y: 0, width: 10, height: 10 }
92
- const viewport = { left: 0, top: 0, width: 1000, height: 500 }
93
- const next = scaleFloorplanViewBoxBetweenClients(viewBox, 0.5, viewport, [500, 250], [600, 250])
94
-
95
- expect(next).toEqual({ x: 1.5, y: 2.5, width: 5, height: 5 })
96
- expect(clientToFloorplanPoint(next, viewport, 600, 250)).toEqual([5, 5])
97
- })
98
-
99
- test('clamps zoom without changing the view box aspect ratio', () => {
100
- expect(scaleFloorplanViewBox({ x: 0, y: 0, width: 100, height: 1 }, 0.01)).toEqual({
101
- x: 37.5,
102
- y: 0.375,
103
- width: 25,
104
- height: 0.25,
105
- })
106
- })
107
- })