@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,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 }