@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
@@ -1,89 +0,0 @@
1
- import { describe, expect, test } from 'bun:test'
2
- import {
3
- resolveFloorplanAnnotationLabelTransform,
4
- resolveFloorplanAnnotationUpdate,
5
- resolveFloorplanLabelAngle,
6
- shouldUpdateFloorplanLabelRotation,
7
- updateSvgFloorplanLabelOrientations,
8
- } from './floorplan-label-angle'
9
-
10
- describe('resolveFloorplanLabelAngle', () => {
11
- test('keeps segment labels readable while preserving their screen direction', () => {
12
- expect(resolveFloorplanLabelAngle(0, 0)).toBe(0)
13
- expect(resolveFloorplanLabelAngle(Math.PI, 0)).toBe(0)
14
- expect(resolveFloorplanLabelAngle(Math.PI / 2, 90)).toBe(-90)
15
- })
16
-
17
- test('counter-rotates aggregate labels to remain horizontal', () => {
18
- expect(resolveFloorplanLabelAngle(0, 90, true)).toBe(-90)
19
- expect(resolveFloorplanLabelAngle(Math.PI / 3, -35, true)).toBe(35)
20
- })
21
-
22
- test('ignores sub-degree rotation changes for annotation layout', () => {
23
- expect(shouldUpdateFloorplanLabelRotation(30, 30.9)).toBe(false)
24
- expect(shouldUpdateFloorplanLabelRotation(30, 31)).toBe(true)
25
- expect(shouldUpdateFloorplanLabelRotation(359.5, 0.25)).toBe(false)
26
- })
27
-
28
- test('updates only label orientation while preserving its layout shift', () => {
29
- expect(
30
- resolveFloorplanAnnotationLabelTransform({
31
- afterRotation: 'translate(0 -0.2)',
32
- angleRadians: 0,
33
- beforeRotation: 'translate(4 6)',
34
- layoutDx: 0.5,
35
- layoutDy: -0.25,
36
- sceneRotationDeg: 90,
37
- screenUpright: true,
38
- }),
39
- ).toEqual({
40
- defaultTransform: 'translate(4 6) rotate(-90) translate(0 -0.2)',
41
- transform: 'translate(4 6) rotate(-90) translate(0 -0.2) translate(0.5 -0.25)',
42
- })
43
- })
44
-
45
- test('keeps a rotation-only update out of the full collision layout path', () => {
46
- expect(
47
- resolveFloorplanAnnotationUpdate({
48
- layoutInputsChanged: false,
49
- nextRotationDeg: 45,
50
- previousRotationDeg: 30,
51
- }),
52
- ).toEqual({
53
- resolveCollisions: false,
54
- updateLabelPresentation: true,
55
- })
56
- })
57
-
58
- test('runs collision layout when floor-plan scene inputs change', () => {
59
- expect(
60
- resolveFloorplanAnnotationUpdate({
61
- layoutInputsChanged: true,
62
- nextRotationDeg: 30,
63
- previousRotationDeg: 30,
64
- }),
65
- ).toEqual({
66
- resolveCollisions: true,
67
- updateLabelPresentation: true,
68
- })
69
- })
70
-
71
- test('updates captured label references without rediscovering the DOM', () => {
72
- const attributes = new Map<string, string>([['transform', 'translate(4 6) rotate(0)']])
73
- const label = {
74
- dataset: {
75
- floorplanAnnotationAngleRadians: '0',
76
- floorplanAnnotationLayoutDx: '0.5',
77
- floorplanAnnotationLayoutDy: '-0.25',
78
- floorplanAnnotationScreenUpright: 'true',
79
- floorplanAnnotationTransformAfterRotation: '',
80
- floorplanAnnotationTransformBeforeRotation: 'translate(4 6)',
81
- },
82
- getAttribute: (name: string) => attributes.get(name) ?? null,
83
- setAttribute: (name: string, value: string) => attributes.set(name, value),
84
- } as unknown as SVGGElement
85
-
86
- expect(updateSvgFloorplanLabelOrientations([label], 90)).toBe(1)
87
- expect(attributes.get('transform')).toBe('translate(4 6) rotate(-90) translate(0.5 -0.25)')
88
- })
89
- })
@@ -1,106 +0,0 @@
1
- export function resolveFloorplanLabelAngle(
2
- angleRadians: number,
3
- sceneRotationDeg: number,
4
- screenUpright = false,
5
- ): number {
6
- if (screenUpright) return -sceneRotationDeg
7
-
8
- let localAngleDeg = (angleRadians * 180) / Math.PI
9
- let screenAngleDeg = localAngleDeg + sceneRotationDeg
10
- screenAngleDeg = ((((screenAngleDeg + 180) % 360) + 360) % 360) - 180
11
- if (screenAngleDeg > 90) localAngleDeg -= 180
12
- else if (screenAngleDeg <= -90) localAngleDeg += 180
13
- return ((((localAngleDeg + 180) % 360) + 360) % 360) - 180
14
- }
15
-
16
- export function shouldUpdateFloorplanLabelRotation(
17
- previousRotationDeg: number | null,
18
- nextRotationDeg: number,
19
- minimumDeltaDeg = 1,
20
- ): boolean {
21
- if (previousRotationDeg === null) return true
22
- const deltaDeg = ((((nextRotationDeg - previousRotationDeg + 180) % 360) + 360) % 360) - 180
23
- return Math.abs(deltaDeg) >= minimumDeltaDeg
24
- }
25
-
26
- export function resolveFloorplanAnnotationUpdate({
27
- layoutInputsChanged,
28
- previousRotationDeg,
29
- nextRotationDeg,
30
- }: {
31
- layoutInputsChanged: boolean
32
- previousRotationDeg: number | null
33
- nextRotationDeg: number
34
- }): {
35
- resolveCollisions: boolean
36
- updateLabelPresentation: boolean
37
- } {
38
- return {
39
- resolveCollisions: layoutInputsChanged,
40
- updateLabelPresentation:
41
- layoutInputsChanged ||
42
- shouldUpdateFloorplanLabelRotation(previousRotationDeg, nextRotationDeg),
43
- }
44
- }
45
-
46
- export function resolveFloorplanAnnotationLabelTransform({
47
- angleRadians,
48
- sceneRotationDeg,
49
- screenUpright,
50
- beforeRotation,
51
- afterRotation,
52
- layoutDx = 0,
53
- layoutDy = 0,
54
- }: {
55
- angleRadians: number
56
- sceneRotationDeg: number
57
- screenUpright: boolean
58
- beforeRotation: string
59
- afterRotation: string
60
- layoutDx?: number
61
- layoutDy?: number
62
- }): {
63
- defaultTransform: string
64
- transform: string
65
- } {
66
- const degrees = resolveFloorplanLabelAngle(angleRadians, sceneRotationDeg, screenUpright)
67
- const defaultTransform = [beforeRotation, `rotate(${degrees})`, afterRotation]
68
- .filter(Boolean)
69
- .join(' ')
70
- return {
71
- defaultTransform,
72
- transform:
73
- layoutDx === 0 && layoutDy === 0
74
- ? defaultTransform
75
- : `${defaultTransform} translate(${layoutDx} ${layoutDy})`,
76
- }
77
- }
78
-
79
- export function updateSvgFloorplanLabelOrientations(
80
- labels: Iterable<SVGGElement>,
81
- sceneRotationDeg: number,
82
- ): number {
83
- let updated = 0
84
-
85
- for (const label of labels) {
86
- const angleRadians = Number(label.dataset.floorplanAnnotationAngleRadians)
87
- if (!Number.isFinite(angleRadians)) continue
88
-
89
- const { defaultTransform, transform } = resolveFloorplanAnnotationLabelTransform({
90
- angleRadians,
91
- sceneRotationDeg,
92
- screenUpright: label.dataset.floorplanAnnotationScreenUpright === 'true',
93
- beforeRotation: label.dataset.floorplanAnnotationTransformBeforeRotation ?? '',
94
- afterRotation: label.dataset.floorplanAnnotationTransformAfterRotation ?? '',
95
- layoutDx: Number(label.dataset.floorplanAnnotationLayoutDx ?? 0),
96
- layoutDy: Number(label.dataset.floorplanAnnotationLayoutDy ?? 0),
97
- })
98
- if (label.getAttribute('transform') === transform) continue
99
-
100
- label.dataset.floorplanAnnotationDefaultTransform = defaultTransform
101
- label.setAttribute('transform', transform)
102
- updated += 1
103
- }
104
-
105
- return updated
106
- }
@@ -1,124 +0,0 @@
1
- 'use client'
2
-
3
- import type { Point2D } from '@pascal-app/core'
4
-
5
- function toSvgX(value: number) {
6
- return value
7
- }
8
-
9
- function toSvgY(value: number) {
10
- return value
11
- }
12
-
13
- function toSvgPoint(point: Point2D) {
14
- return {
15
- x: toSvgX(point.x),
16
- y: toSvgY(point.y),
17
- }
18
- }
19
-
20
- export function formatPolygonPath(points: Point2D[], holes: Point2D[][] = []) {
21
- const formatSubpath = (subpathPoints: Point2D[]) => {
22
- const [firstPoint, ...restPoints] = subpathPoints
23
- if (!firstPoint) {
24
- return null
25
- }
26
-
27
- const firstSvgPoint = toSvgPoint(firstPoint)
28
-
29
- return [
30
- `M ${firstSvgPoint.x} ${firstSvgPoint.y}`,
31
- ...restPoints.map((point) => {
32
- const svgPoint = toSvgPoint(point)
33
- return `L ${svgPoint.x} ${svgPoint.y}`
34
- }),
35
- 'Z',
36
- ].join(' ')
37
- }
38
-
39
- return [points, ...holes].map(formatSubpath).filter(Boolean).join(' ')
40
- }
41
-
42
- export function buildSvgPolylinePath(points: Point2D[]) {
43
- if (points.length < 2) {
44
- return null
45
- }
46
-
47
- return points
48
- .map((point, index) => {
49
- const svgPoint = toSvgPoint(point)
50
- return `${index === 0 ? 'M' : 'L'} ${svgPoint.x} ${svgPoint.y}`
51
- })
52
- .join(' ')
53
- }
54
-
55
- export function getArcPlanPoint(center: Point2D, radius: number, angle: number): Point2D {
56
- return {
57
- x: center.x + Math.cos(angle) * radius,
58
- y: center.y + Math.sin(angle) * radius,
59
- }
60
- }
61
-
62
- export function buildSvgArcPath(
63
- center: Point2D,
64
- radius: number,
65
- startAngle: number,
66
- endAngle: number,
67
- ) {
68
- const start = getArcPlanPoint(center, radius, startAngle)
69
- return `M ${toSvgX(start.x)} ${toSvgY(start.y)} ${svgArcCommands(center, radius, startAngle, endAngle)}`
70
- }
71
-
72
- export function buildSvgAnnularSectorPath(
73
- center: Point2D,
74
- innerRadius: number,
75
- outerRadius: number,
76
- startAngle: number,
77
- endAngle: number,
78
- ) {
79
- const outerStart = getArcPlanPoint(center, outerRadius, startAngle)
80
- const innerEnd = getArcPlanPoint(center, innerRadius, endAngle)
81
- return [
82
- `M ${toSvgX(outerStart.x)} ${toSvgY(outerStart.y)}`,
83
- svgArcCommands(center, outerRadius, startAngle, endAngle),
84
- `L ${toSvgX(innerEnd.x)} ${toSvgY(innerEnd.y)}`,
85
- svgArcCommands(center, innerRadius, endAngle, startAngle),
86
- 'Z',
87
- ].join(' ')
88
- }
89
-
90
- function svgArcCommands(center: Point2D, radius: number, startAngle: number, endAngle: number) {
91
- const delta = endAngle - startAngle
92
- const count = Math.max(1, Math.ceil(Math.abs(delta) / Math.PI))
93
- return Array.from({ length: count }, (_, index) => {
94
- const end = getArcPlanPoint(center, radius, startAngle + (delta * (index + 1)) / count)
95
- return `A ${radius} ${radius} 0 0 ${delta >= 0 ? 1 : 0} ${toSvgX(end.x)} ${toSvgY(end.y)}`
96
- }).join(' ')
97
- }
98
-
99
- export function formatSvgPolygonPoints(points: Point2D[]) {
100
- return points.map((point) => `${toSvgX(point.x)},${toSvgY(point.y)}`).join(' ')
101
- }
102
-
103
- /**
104
- * Three points defining an arrow head — tip + two trailing barbs.
105
- * Returned as plain `Point2D` objects so consumers can either feed them
106
- * straight into `formatSvgPolygonPoints` (for SVG `points=""`) or push
107
- * them onto a `FloorplanGeometry.polygon.points` array. Mixing both
108
- * downstream paths through a string-returning helper was awkward — see
109
- * `nodes/src/stair/floorplan.ts` which needs the points as objects.
110
- */
111
- export function buildSvgArrowHeadPoints(point: Point2D, angle: number, size: number): Point2D[] {
112
- const left = {
113
- x: point.x - size * Math.cos(angle - Math.PI / 6),
114
- y: point.y - size * Math.sin(angle - Math.PI / 6),
115
- }
116
- const right = {
117
- x: point.x - size * Math.cos(angle + Math.PI / 6),
118
- y: point.y - size * Math.sin(angle + Math.PI / 6),
119
- }
120
-
121
- return [point, left, right]
122
- }
123
-
124
- export { toSvgPoint, toSvgX, toSvgY }
@@ -1,89 +0,0 @@
1
- import * as React from 'react'
2
- import { Button } from './../../../components/ui/primitives/button'
3
- import {
4
- Tooltip,
5
- TooltipContent,
6
- TooltipTrigger,
7
- } from './../../../components/ui/primitives/tooltip'
8
- import { triggerSFX } from './../../../lib/sfx-bus'
9
- import { cn } from './../../../lib/utils'
10
- import { useActionMenuPopupSide } from './placement'
11
-
12
- interface ActionButtonProps extends React.ComponentProps<typeof Button> {
13
- label: string
14
- shortcut?: string
15
- isActive?: boolean
16
- tooltipContent?: React.ReactNode
17
- tooltipSide?: 'top' | 'right' | 'bottom' | 'left'
18
- /** The tooltip portals to `<body>`, so a button in a themed scope passes the scope here. */
19
- tooltipClassName?: string
20
- }
21
-
22
- export const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProps>(
23
- (
24
- {
25
- className,
26
- children,
27
- label,
28
- shortcut,
29
- isActive,
30
- tooltipContent,
31
- tooltipSide,
32
- tooltipClassName,
33
- onClick,
34
- onMouseEnter,
35
- ...props
36
- },
37
- ref,
38
- ) => {
39
- const popupSide = useActionMenuPopupSide()
40
- return (
41
- <Tooltip>
42
- <TooltipTrigger asChild>
43
- <Button
44
- // These are toggles, not commands: the armed/unarmed state has to be
45
- // readable without decoding the active-state styling.
46
- aria-pressed={isActive}
47
- // Icon-only: the tooltip text is the button's name.
48
- aria-label={label}
49
- className={cn('relative h-11 w-11 transition-all', className)}
50
- onClick={(event) => {
51
- triggerSFX('sfx:menu-click')
52
- onClick?.(event)
53
- }}
54
- onMouseEnter={(event) => {
55
- triggerSFX('sfx:menu-hover')
56
- onMouseEnter?.(event)
57
- }}
58
- ref={ref}
59
- {...props}
60
- >
61
- <div
62
- className={cn(
63
- 'flex h-full w-full items-center justify-center transition-transform',
64
- shortcut && '-translate-x-0.5 -translate-y-0.5',
65
- )}
66
- >
67
- {children}
68
- </div>
69
- {shortcut && (
70
- <div className="absolute right-1 bottom-1 rounded border border-border/40 bg-background/40 px-1 py-[2px] backdrop-blur-md">
71
- <span className="block font-medium font-mono text-[9px] text-muted-foreground/70 leading-none">
72
- {shortcut}
73
- </span>
74
- </div>
75
- )}
76
- </Button>
77
- </TooltipTrigger>
78
- <TooltipContent className={tooltipClassName} side={tooltipSide ?? popupSide}>
79
- {tooltipContent || (
80
- <p>
81
- {label} {shortcut && `(${shortcut})`}
82
- </p>
83
- )}
84
- </TooltipContent>
85
- </Tooltip>
86
- )
87
- },
88
- )
89
- ActionButton.displayName = 'ActionButton'
@@ -1,42 +0,0 @@
1
- 'use client'
2
-
3
- import { usePascalI18n } from '@pascal-app/core'
4
- import { Video } from 'lucide-react'
5
- import { cn } from '../../../lib/utils'
6
- import useFollowPascal from '../../../store/use-follow-pascal'
7
- import { ActionButton } from './action-button'
8
-
9
- /** The toolbar's switch for Follow Pascal: the camera follows what the agent builds. */
10
- export function FollowPascalToggle({
11
- className,
12
- tooltipClassName,
13
- }: {
14
- className?: string
15
- tooltipClassName?: string
16
- }) {
17
- const i18n = usePascalI18n()
18
- const on = useFollowPascal((state) => state.on)
19
- const setOn = useFollowPascal((state) => state.setOn)
20
- return (
21
- <ActionButton
22
- aria-pressed={on}
23
- className={cn(
24
- on
25
- ? 'bg-violet-500/20 text-violet-600 dark:text-violet-300 dark:hover:text-accent-foreground'
26
- : 'hover:bg-black/4 hover:text-violet-600 dark:hover:text-violet-300',
27
- className,
28
- )}
29
- label={
30
- on
31
- ? i18n._({ id: 'editor.actionMenu.followPascalToggle.on', message: 'Follow Pascal: On' })
32
- : i18n._({ id: 'editor.actionMenu.followPascalToggle.off', message: 'Follow Pascal: Off' })
33
- }
34
- onClick={() => setOn(!on)}
35
- size="icon"
36
- tooltipClassName={tooltipClassName}
37
- variant="ghost"
38
- >
39
- <Video className="h-6 w-6" />
40
- </ActionButton>
41
- )
42
- }
@@ -1,69 +0,0 @@
1
- import { Slot } from '@radix-ui/react-slot'
2
- import { cva, type VariantProps } from 'class-variance-authority'
3
- import type * as React from 'react'
4
-
5
- import { cn } from '../../../lib/utils'
6
-
7
- const buttonVariants = cva(
8
- "inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md font-barlow font-medium text-sm outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
9
- {
10
- variants: {
11
- variant: {
12
- default: 'bg-primary text-primary-foreground hover:bg-primary/90',
13
- destructive:
14
- 'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40',
15
- outline:
16
- 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50',
17
- secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
18
- ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
19
- link: 'text-primary underline-offset-4 hover:underline',
20
- },
21
- size: {
22
- default: 'h-9 px-4 py-2 has-[>svg]:px-3',
23
- sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',
24
- lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
25
- icon: 'size-9',
26
- 'icon-sm': 'size-8',
27
- 'icon-lg': 'size-10',
28
- },
29
- },
30
- defaultVariants: {
31
- variant: 'default',
32
- size: 'default',
33
- },
34
- },
35
- )
36
-
37
- function Button({
38
- className,
39
- variant,
40
- size,
41
- asChild = false,
42
- ref,
43
- ...props
44
- }: React.ComponentProps<'button'> &
45
- VariantProps<typeof buttonVariants> & {
46
- asChild?: boolean
47
- }) {
48
- if (asChild) {
49
- return (
50
- <Slot
51
- className={cn(buttonVariants({ variant, size, className }))}
52
- data-slot="button"
53
- ref={ref as never}
54
- {...props}
55
- />
56
- )
57
- }
58
-
59
- return (
60
- <button
61
- className={cn(buttonVariants({ variant, size, className }))}
62
- data-slot="button"
63
- ref={ref}
64
- {...props}
65
- />
66
- )
67
- }
68
-
69
- export { Button, buttonVariants }