@pascal-app/viewer 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 (224) hide show
  1. package/README.md +157 -0
  2. package/dist/components/viewer/drone-controller.d.ts.map +1 -1
  3. package/dist/components/viewer/drone-controller.js +1 -5
  4. package/dist/components/viewer/first-person/build-collider-world.d.ts +14 -0
  5. package/dist/components/viewer/first-person/build-collider-world.d.ts.map +1 -0
  6. package/dist/components/viewer/first-person/build-collider-world.js +426 -0
  7. package/dist/components/viewer/first-person/ground-holes.d.ts +22 -0
  8. package/dist/components/viewer/first-person/ground-holes.d.ts.map +1 -0
  9. package/dist/components/viewer/first-person/ground-holes.js +116 -0
  10. package/dist/components/viewer/first-person/mechanism-targets.d.ts +27 -0
  11. package/dist/components/viewer/first-person/mechanism-targets.d.ts.map +1 -0
  12. package/dist/components/viewer/first-person/mechanism-targets.js +79 -0
  13. package/dist/components/viewer/first-person/terrain-collider.d.ts +32 -0
  14. package/dist/components/viewer/first-person/terrain-collider.d.ts.map +1 -0
  15. package/dist/components/viewer/first-person/terrain-collider.js +175 -0
  16. package/dist/components/viewer/glb-walkthrough-controller.d.ts +1 -1
  17. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
  18. package/dist/components/viewer/glb-walkthrough-controller.js +3 -4
  19. package/dist/components/viewer/live-walkthrough-controller.d.ts +17 -0
  20. package/dist/components/viewer/live-walkthrough-controller.d.ts.map +1 -0
  21. package/dist/components/viewer/live-walkthrough-controller.js +1449 -0
  22. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  23. package/dist/components/viewer/post-processing.js +22 -1
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +3 -0
  27. package/dist/lib/daylight/room-map.d.ts +29 -4
  28. package/dist/lib/daylight/room-map.d.ts.map +1 -1
  29. package/dist/lib/daylight/room-map.js +219 -36
  30. package/dist/lib/daylight/room-scene.d.ts.map +1 -1
  31. package/dist/lib/daylight/room-scene.js +9 -3
  32. package/dist/lib/daylight/rooms.d.ts +15 -6
  33. package/dist/lib/daylight/rooms.d.ts.map +1 -1
  34. package/dist/lib/daylight/rooms.js +49 -9
  35. package/dist/lib/measurement-presentation.d.ts +2 -0
  36. package/dist/lib/measurement-presentation.d.ts.map +1 -1
  37. package/dist/lib/measurement-presentation.js +6 -0
  38. package/dist/lib/typing-target.d.ts +2 -0
  39. package/dist/lib/typing-target.d.ts.map +1 -0
  40. package/dist/lib/typing-target.js +7 -0
  41. package/dist/lib/viewer-config.d.ts +6 -0
  42. package/dist/lib/viewer-config.d.ts.map +1 -1
  43. package/dist/lib/viewer-config.js +2 -0
  44. package/dist/sfx/audio-settings.d.ts +81 -0
  45. package/dist/sfx/audio-settings.d.ts.map +1 -0
  46. package/dist/sfx/audio-settings.js +166 -0
  47. package/dist/sfx/index.d.ts +6 -0
  48. package/dist/sfx/index.d.ts.map +1 -0
  49. package/dist/sfx/index.js +5 -0
  50. package/dist/sfx/interaction-sounds.d.ts +7 -0
  51. package/dist/sfx/interaction-sounds.d.ts.map +1 -0
  52. package/dist/sfx/interaction-sounds.js +39 -0
  53. package/dist/sfx/player.d.ts +31 -0
  54. package/dist/sfx/player.d.ts.map +1 -0
  55. package/dist/sfx/player.js +255 -0
  56. package/dist/sfx/sounds.d.ts +60 -0
  57. package/dist/sfx/sounds.d.ts.map +1 -0
  58. package/dist/sfx/sounds.js +25 -0
  59. package/dist/sfx/use-viewer-ui-sounds.d.ts +8 -0
  60. package/dist/sfx/use-viewer-ui-sounds.d.ts.map +1 -0
  61. package/dist/sfx/use-viewer-ui-sounds.js +42 -0
  62. package/dist/store/use-first-person-hud.d.ts +13 -0
  63. package/dist/store/use-first-person-hud.d.ts.map +1 -0
  64. package/dist/store/use-first-person-hud.js +21 -0
  65. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  66. package/dist/systems/roof/roof-system.js +27 -13
  67. package/dist/ui/components/action-button.d.ts +14 -0
  68. package/dist/ui/components/action-button.d.ts.map +1 -0
  69. package/dist/ui/components/action-button.js +21 -0
  70. package/dist/ui/components/action-menu-placement.d.ts +7 -0
  71. package/dist/ui/components/action-menu-placement.d.ts.map +1 -0
  72. package/dist/ui/components/action-menu-placement.js +10 -0
  73. package/dist/ui/components/floorplan-compass-button.d.ts +10 -0
  74. package/dist/ui/components/floorplan-compass-button.d.ts.map +1 -0
  75. package/dist/ui/components/floorplan-compass-button.js +22 -0
  76. package/dist/ui/components/follow-pascal-toggle.d.ts +6 -0
  77. package/dist/ui/components/follow-pascal-toggle.d.ts.map +1 -0
  78. package/dist/ui/components/follow-pascal-toggle.js +21 -0
  79. package/dist/ui/components/localized-html.d.ts +9 -0
  80. package/dist/ui/components/localized-html.d.ts.map +1 -0
  81. package/dist/ui/components/localized-html.js +16 -0
  82. package/dist/ui/components/render-settings-menu.d.ts +9 -0
  83. package/dist/ui/components/render-settings-menu.d.ts.map +1 -0
  84. package/dist/ui/components/render-settings-menu.js +145 -0
  85. package/dist/ui/components/rooms/room-highlight.d.ts +10 -0
  86. package/dist/ui/components/rooms/room-highlight.d.ts.map +1 -0
  87. package/dist/ui/components/rooms/room-highlight.js +56 -0
  88. package/dist/ui/components/rooms/room-labels.d.ts +278 -0
  89. package/dist/ui/components/rooms/room-labels.d.ts.map +1 -0
  90. package/dist/ui/components/rooms/room-labels.js +1295 -0
  91. package/dist/ui/components/rooms/viewer-rooms.d.ts +14 -0
  92. package/dist/ui/components/rooms/viewer-rooms.d.ts.map +1 -0
  93. package/dist/ui/components/rooms/viewer-rooms.js +123 -0
  94. package/dist/ui/components/scene-loader.d.ts +18 -0
  95. package/dist/ui/components/scene-loader.d.ts.map +1 -0
  96. package/dist/ui/components/scene-loader.js +29 -0
  97. package/dist/ui/components/sky-control.d.ts +37 -0
  98. package/dist/ui/components/sky-control.d.ts.map +1 -0
  99. package/dist/ui/components/sky-control.js +382 -0
  100. package/dist/ui/components/use-viewer-camera-navigation-sync.d.ts +4 -0
  101. package/dist/ui/components/use-viewer-camera-navigation-sync.d.ts.map +1 -0
  102. package/dist/ui/components/use-viewer-camera-navigation-sync.js +117 -0
  103. package/dist/ui/components/viewer-controls-bar.d.ts +20 -0
  104. package/dist/ui/components/viewer-controls-bar.d.ts.map +1 -0
  105. package/dist/ui/components/viewer-controls-bar.js +130 -0
  106. package/dist/ui/components/viewer-hover-label.d.ts +14 -0
  107. package/dist/ui/components/viewer-hover-label.d.ts.map +1 -0
  108. package/dist/ui/components/viewer-hover-label.js +28 -0
  109. package/dist/ui/components/viewer-scene-header.d.ts +34 -0
  110. package/dist/ui/components/viewer-scene-header.d.ts.map +1 -0
  111. package/dist/ui/components/viewer-scene-header.js +92 -0
  112. package/dist/ui/components/viewer-selection-manager.d.ts +7 -0
  113. package/dist/ui/components/viewer-selection-manager.d.ts.map +1 -0
  114. package/dist/ui/components/viewer-selection-manager.js +116 -0
  115. package/dist/ui/components/viewer-stage-modes.d.ts +7 -0
  116. package/dist/ui/components/viewer-stage-modes.d.ts.map +1 -0
  117. package/dist/ui/components/viewer-stage-modes.js +42 -0
  118. package/dist/ui/components/viewer-stage-switcher.d.ts +10 -0
  119. package/dist/ui/components/viewer-stage-switcher.d.ts.map +1 -0
  120. package/dist/ui/components/viewer-stage-switcher.js +16 -0
  121. package/dist/ui/components/viewer-stage.d.ts +25 -0
  122. package/dist/ui/components/viewer-stage.d.ts.map +1 -0
  123. package/dist/ui/components/viewer-stage.js +107 -0
  124. package/dist/ui/components/walkthrough-hud.d.ts +29 -0
  125. package/dist/ui/components/walkthrough-hud.d.ts.map +1 -0
  126. package/dist/ui/components/walkthrough-hud.js +55 -0
  127. package/dist/ui/floorplan/floorplan-camera-sync.d.ts +22 -0
  128. package/dist/ui/floorplan/floorplan-camera-sync.d.ts.map +1 -0
  129. package/dist/ui/floorplan/floorplan-camera-sync.js +157 -0
  130. package/dist/ui/floorplan/floorplan-dimension-renderer.d.ts +61 -0
  131. package/dist/ui/floorplan/floorplan-dimension-renderer.d.ts.map +1 -0
  132. package/dist/ui/floorplan/floorplan-dimension-renderer.js +280 -0
  133. package/dist/ui/floorplan/floorplan-geometry-renderer.d.ts +54 -0
  134. package/dist/ui/floorplan/floorplan-geometry-renderer.d.ts.map +1 -0
  135. package/dist/ui/floorplan/floorplan-geometry-renderer.js +317 -0
  136. package/dist/ui/floorplan/floorplan-label-angle.d.ts +24 -0
  137. package/dist/ui/floorplan/floorplan-label-angle.d.ts.map +1 -0
  138. package/dist/ui/floorplan/floorplan-label-angle.js +60 -0
  139. package/dist/ui/floorplan/floorplan-navigation-sync.d.ts +18 -0
  140. package/dist/ui/floorplan/floorplan-navigation-sync.d.ts.map +1 -0
  141. package/dist/ui/floorplan/floorplan-navigation-sync.js +41 -0
  142. package/dist/ui/floorplan/floorplan-preview-geometry.d.ts +26 -0
  143. package/dist/ui/floorplan/floorplan-preview-geometry.d.ts.map +1 -0
  144. package/dist/ui/floorplan/floorplan-preview-geometry.js +173 -0
  145. package/dist/ui/floorplan/floorplan-preview-navigation.d.ts +22 -0
  146. package/dist/ui/floorplan/floorplan-preview-navigation.d.ts.map +1 -0
  147. package/dist/ui/floorplan/floorplan-preview-navigation.js +41 -0
  148. package/dist/ui/floorplan/floorplan-preview-visibility.d.ts +8 -0
  149. package/dist/ui/floorplan/floorplan-preview-visibility.d.ts.map +1 -0
  150. package/dist/ui/floorplan/floorplan-preview-visibility.js +21 -0
  151. package/dist/ui/floorplan/floorplan-preview.d.ts +19 -0
  152. package/dist/ui/floorplan/floorplan-preview.d.ts.map +1 -0
  153. package/dist/ui/floorplan/floorplan-preview.js +700 -0
  154. package/dist/ui/hooks/use-mobile.d.ts +2 -0
  155. package/dist/ui/hooks/use-mobile.d.ts.map +1 -0
  156. package/dist/ui/hooks/use-mobile.js +14 -0
  157. package/dist/ui/index.d.ts +49 -0
  158. package/dist/ui/index.d.ts.map +1 -0
  159. package/dist/ui/index.js +48 -0
  160. package/dist/ui/lib/below-grade.d.ts +38 -0
  161. package/dist/ui/lib/below-grade.d.ts.map +1 -0
  162. package/dist/ui/lib/below-grade.js +102 -0
  163. package/dist/ui/lib/camera-pose.d.ts +19 -0
  164. package/dist/ui/lib/camera-pose.d.ts.map +1 -0
  165. package/dist/ui/lib/camera-pose.js +113 -0
  166. package/dist/ui/lib/cn.d.ts +3 -0
  167. package/dist/ui/lib/cn.d.ts.map +1 -0
  168. package/dist/ui/lib/cn.js +5 -0
  169. package/dist/ui/lib/level-name.d.ts +10 -0
  170. package/dist/ui/lib/level-name.d.ts.map +1 -0
  171. package/dist/ui/lib/level-name.js +33 -0
  172. package/dist/ui/lib/node-display-label.d.ts +8 -0
  173. package/dist/ui/lib/node-display-label.d.ts.map +1 -0
  174. package/dist/ui/lib/node-display-label.js +36 -0
  175. package/dist/ui/lib/room-area.d.ts +3 -0
  176. package/dist/ui/lib/room-area.d.ts.map +1 -0
  177. package/dist/ui/lib/room-area.js +8 -0
  178. package/dist/ui/lib/room-assembly-overlay.d.ts +46 -0
  179. package/dist/ui/lib/room-assembly-overlay.d.ts.map +1 -0
  180. package/dist/ui/lib/room-assembly-overlay.js +222 -0
  181. package/dist/ui/lib/room-name-messages.d.ts +28 -0
  182. package/dist/ui/lib/room-name-messages.d.ts.map +1 -0
  183. package/dist/ui/lib/room-name-messages.js +112 -0
  184. package/dist/ui/lib/room-selection.d.ts +58 -0
  185. package/dist/ui/lib/room-selection.d.ts.map +1 -0
  186. package/dist/ui/lib/room-selection.js +297 -0
  187. package/dist/ui/lib/scene-theme-name.d.ts +4 -0
  188. package/dist/ui/lib/scene-theme-name.d.ts.map +1 -0
  189. package/dist/ui/lib/scene-theme-name.js +22 -0
  190. package/dist/ui/lib/selection-modifiers.d.ts +20 -0
  191. package/dist/ui/lib/selection-modifiers.d.ts.map +1 -0
  192. package/dist/ui/lib/selection-modifiers.js +15 -0
  193. package/dist/ui/lib/viewer-selection.d.ts +132 -0
  194. package/dist/ui/lib/viewer-selection.d.ts.map +1 -0
  195. package/dist/ui/lib/viewer-selection.js +462 -0
  196. package/dist/ui/lib/zone-content.d.ts +6 -0
  197. package/dist/ui/lib/zone-content.d.ts.map +1 -0
  198. package/dist/ui/lib/zone-content.js +96 -0
  199. package/dist/ui/primitives/button.d.ts +11 -0
  200. package/dist/ui/primitives/button.d.ts.map +1 -0
  201. package/dist/ui/primitives/button.js +35 -0
  202. package/dist/ui/primitives/dropdown-menu.d.ts +26 -0
  203. package/dist/ui/primitives/dropdown-menu.d.ts.map +1 -0
  204. package/dist/ui/primitives/dropdown-menu.js +53 -0
  205. package/dist/ui/primitives/popover.d.ts +8 -0
  206. package/dist/ui/primitives/popover.d.ts.map +1 -0
  207. package/dist/ui/primitives/popover.js +17 -0
  208. package/dist/ui/primitives/switch.d.ts +5 -0
  209. package/dist/ui/primitives/switch.d.ts.map +1 -0
  210. package/dist/ui/primitives/switch.js +8 -0
  211. package/dist/ui/primitives/tooltip.d.ts +8 -0
  212. package/dist/ui/primitives/tooltip.d.ts.map +1 -0
  213. package/dist/ui/primitives/tooltip.js +17 -0
  214. package/dist/ui/store/camera-pose-store.d.ts +17 -0
  215. package/dist/ui/store/camera-pose-store.d.ts.map +1 -0
  216. package/dist/ui/store/camera-pose-store.js +15 -0
  217. package/dist/ui/store/navigation-sync-pose-store.d.ts +26 -0
  218. package/dist/ui/store/navigation-sync-pose-store.d.ts.map +1 -0
  219. package/dist/ui/store/navigation-sync-pose-store.js +16 -0
  220. package/dist/ui/store/use-follow-pascal.d.ts +30 -0
  221. package/dist/ui/store/use-follow-pascal.d.ts.map +1 -0
  222. package/dist/ui/store/use-follow-pascal.js +28 -0
  223. package/dist/ui/theme.css +339 -0
  224. package/package.json +34 -7
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from 'react';
3
+ import { playSound } from '../../sfx/player';
4
+ import { cn } from '../lib/cn';
5
+ import { Button } from '../primitives/button';
6
+ import { Tooltip, TooltipContent, TooltipTrigger } from '../primitives/tooltip';
7
+ import { useActionMenuPopupSide } from './action-menu-placement';
8
+ export const ActionButton = React.forwardRef(({ className, children, label, shortcut, isActive, tooltipContent, tooltipSide, tooltipClassName, onClick, onMouseEnter, ...props }, ref) => {
9
+ const popupSide = useActionMenuPopupSide();
10
+ return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(Button
11
+ // These are toggles, not commands: the armed/unarmed state has to be
12
+ // readable without decoding the active-state styling.
13
+ , { "aria-pressed": isActive, "aria-label": label, className: cn('relative h-11 w-11 transition-all', className), onClick: (event) => {
14
+ playSound('menuClick');
15
+ onClick?.(event);
16
+ }, onMouseEnter: (event) => {
17
+ playSound('menuHover');
18
+ onMouseEnter?.(event);
19
+ }, ref: ref, ...props, children: [_jsx("div", { className: cn('flex h-full w-full items-center justify-center transition-transform', shortcut && '-translate-x-0.5 -translate-y-0.5'), children: children }), shortcut && (_jsx("div", { className: "absolute right-1 bottom-1 rounded border border-border/40 bg-background/40 px-1 py-[2px] backdrop-blur-md", children: _jsx("span", { className: "block font-medium font-mono text-[9px] text-muted-foreground/70 leading-none", children: shortcut }) }))] }) }), _jsx(TooltipContent, { className: tooltipClassName, side: tooltipSide ?? popupSide, children: tooltipContent || (_jsxs("p", { children: [label, " ", shortcut && `(${shortcut})`] })) })] }));
20
+ });
21
+ ActionButton.displayName = 'ActionButton';
@@ -0,0 +1,7 @@
1
+ /** Viewport edge the action menu docks to. */
2
+ export type ActionMenuPlacement = 'bottom' | 'top';
3
+ export declare const ActionMenuPlacementProvider: import("react").Provider<ActionMenuPlacement>;
4
+ export declare function useActionMenuPlacement(): ActionMenuPlacement;
5
+ /** Side tooltips and popovers open toward, away from the docked edge. */
6
+ export declare function useActionMenuPopupSide(): 'top' | 'bottom';
7
+ //# sourceMappingURL=action-menu-placement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-menu-placement.d.ts","sourceRoot":"","sources":["../../../src/ui/components/action-menu-placement.ts"],"names":[],"mappings":"AAEA,8CAA8C;AAC9C,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,KAAK,CAAA;AAIlD,eAAO,MAAM,2BAA2B,+CAAsC,CAAA;AAE9E,wBAAgB,sBAAsB,IAAI,mBAAmB,CAE5D;AAED,yEAAyE;AACzE,wBAAgB,sBAAsB,IAAI,KAAK,GAAG,QAAQ,CAEzD"}
@@ -0,0 +1,10 @@
1
+ import { createContext, useContext } from 'react';
2
+ const ActionMenuPlacementContext = createContext('bottom');
3
+ export const ActionMenuPlacementProvider = ActionMenuPlacementContext.Provider;
4
+ export function useActionMenuPlacement() {
5
+ return useContext(ActionMenuPlacementContext);
6
+ }
7
+ /** Side tooltips and popovers open toward, away from the docked edge. */
8
+ export function useActionMenuPopupSide() {
9
+ return useActionMenuPlacement() === 'top' ? 'bottom' : 'top';
10
+ }
@@ -0,0 +1,10 @@
1
+ import type React from 'react';
2
+ export type FloorplanCompassButtonProps = {
3
+ northRotationDeg: number;
4
+ onAlignNorth: () => void;
5
+ needleRef?: React.RefObject<SVGSVGElement | null>;
6
+ /** Horizontal anchor; defaults to the bottom-left corner. */
7
+ style?: React.CSSProperties;
8
+ };
9
+ export declare function FloorplanCompassButton({ northRotationDeg, onAlignNorth, needleRef, style, }: FloorplanCompassButtonProps): React.JSX.Element;
10
+ //# sourceMappingURL=floorplan-compass-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floorplan-compass-button.d.ts","sourceRoot":"","sources":["../../../src/ui/components/floorplan-compass-button.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,MAAM,MAAM,2BAA2B,GAAG;IACxC,gBAAgB,EAAE,MAAM,CAAA;IACxB,YAAY,EAAE,MAAM,IAAI,CAAA;IACxB,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,aAAa,GAAG,IAAI,CAAC,CAAA;IACjD,6DAA6D;IAC7D,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;CAC5B,CAAA;AAED,wBAAgB,sBAAsB,CAAC,EACrC,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACT,KAAK,GACN,EAAE,2BAA2B,qBA6C7B"}
@@ -0,0 +1,22 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { usePascalI18n } from '@pascal-app/core';
4
+ import { cn } from '../lib/cn';
5
+ import { Tooltip, TooltipContent, TooltipTrigger } from '../primitives/tooltip';
6
+ import { SKY_COMPASS_BOTTOM, useSkyPanel } from './sky-control';
7
+ export function FloorplanCompassButton({ northRotationDeg, onAlignNorth, needleRef, style, }) {
8
+ const i18n = usePascalI18n();
9
+ // Beside the sky chip (not when moved to the plan's right corner), centre on the docks' row.
10
+ const besideSky = useSkyPanel((s) => s.hosts > 0) && style?.right === undefined;
11
+ const label = i18n._({
12
+ id: 'viewer.floorplan.alignNorth',
13
+ message: 'Align view to north',
14
+ });
15
+ return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { "aria-label": label, className: cn('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', !style && 'left-3'), style: besideSky ? { bottom: SKY_COMPASS_BOTTOM, ...style } : style, onClick: (event) => {
16
+ event.preventDefault();
17
+ event.stopPropagation();
18
+ onAlignNorth();
19
+ }, onPointerDown: (event) => {
20
+ event.stopPropagation();
21
+ }, type: "button", children: _jsx("span", { className: "relative flex h-6 w-6 items-center justify-center rounded-full bg-[#b8b8b8] shadow-inner dark:bg-neutral-700", children: _jsxs("svg", { "aria-hidden": "true", className: "h-6 w-6", ref: needleRef, style: { transform: `rotate(${northRotationDeg}deg)` }, viewBox: "0 0 48 48", children: [_jsx("path", { d: "M24 4.5 31.5 25 24 21.5 16.5 25Z", fill: "#f15b5b" }), _jsx("path", { d: "M24 43.5 16.5 23 24 26.5 31.5 23Z", fill: "#ffffff" })] }) }) }) }), _jsx(TooltipContent, { side: "right", children: label })] }));
22
+ }
@@ -0,0 +1,6 @@
1
+ /** The toolbar's switch for Follow Pascal: the camera follows what the agent builds. */
2
+ export declare function FollowPascalToggle({ className, tooltipClassName, }: {
3
+ className?: string;
4
+ tooltipClassName?: string;
5
+ }): import("react").JSX.Element;
6
+ //# sourceMappingURL=follow-pascal-toggle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"follow-pascal-toggle.d.ts","sourceRoot":"","sources":["../../../src/ui/components/follow-pascal-toggle.tsx"],"names":[],"mappings":"AAQA,wFAAwF;AACxF,wBAAgB,kBAAkB,CAAC,EACjC,SAAS,EACT,gBAAgB,GACjB,EAAE;IACD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B,+BA6BA"}
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { usePascalI18n } from '@pascal-app/core';
4
+ import { Video } from 'lucide-react';
5
+ import { cn } from '../lib/cn';
6
+ import { useFollowPascal } from '../store/use-follow-pascal';
7
+ import { ActionButton } from './action-button';
8
+ /** The toolbar's switch for Follow Pascal: the camera follows what the agent builds. */
9
+ export function FollowPascalToggle({ className, tooltipClassName, }) {
10
+ const i18n = usePascalI18n();
11
+ const on = useFollowPascal((state) => state.on);
12
+ const setOn = useFollowPascal((state) => state.setOn);
13
+ return (_jsx(ActionButton, { "aria-pressed": on, className: cn(on
14
+ ? 'bg-violet-500/20 text-violet-600 dark:text-violet-300 dark:hover:text-accent-foreground'
15
+ : 'hover:bg-black/4 hover:text-violet-600 dark:hover:text-violet-300', className), label: on
16
+ ? i18n._({ id: 'editor.actionMenu.followPascalToggle.on', message: 'Follow Pascal: On' })
17
+ : i18n._({
18
+ id: 'editor.actionMenu.followPascalToggle.off',
19
+ message: 'Follow Pascal: Off',
20
+ }), onClick: () => setOn(!on), size: "icon", tooltipClassName: tooltipClassName, variant: "ghost", children: _jsx(Video, { className: "h-6 w-6" }) }));
21
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * drei's `Html`, keeping the surrounding i18n scope. `Html` renders its
3
+ * children in a separate React root, which sees none of the surrounding
4
+ * context, so a component inside it would translate and format with the
5
+ * default (English) scope instead of the host's (the editor's `?lang=` or
6
+ * `<Editor locale>` one).
7
+ */
8
+ export declare const LocalizedHtml: import("react").ForwardRefExoticComponent<Omit<import("@react-three/drei/web/Html").HtmlProps & import("react").RefAttributes<HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
9
+ //# sourceMappingURL=localized-html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localized-html.d.ts","sourceRoot":"","sources":["../../../src/ui/components/localized-html.tsx"],"names":[],"mappings":"AAMA;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,wMAUxB,CAAA"}
@@ -0,0 +1,16 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { PascalI18nProvider, usePascalI18n } from '@pascal-app/core';
4
+ import { Html as DreiHtml } from '@react-three/drei';
5
+ import { forwardRef } from 'react';
6
+ /**
7
+ * drei's `Html`, keeping the surrounding i18n scope. `Html` renders its
8
+ * children in a separate React root, which sees none of the surrounding
9
+ * context, so a component inside it would translate and format with the
10
+ * default (English) scope instead of the host's (the editor's `?lang=` or
11
+ * `<Editor locale>` one).
12
+ */
13
+ export const LocalizedHtml = forwardRef(function LocalizedHtml({ children, ...props }, ref) {
14
+ const i18n = usePascalI18n();
15
+ return (_jsx(DreiHtml, { ...props, ref: ref, children: _jsx(PascalI18nProvider, { i18n: i18n, children: children }) }));
16
+ });
@@ -0,0 +1,9 @@
1
+ export declare function QualityMenu({ subContentClassName, }: {
2
+ subContentClassName?: string;
3
+ }): import("react").JSX.Element;
4
+ export declare function DaylightMenu({ subContentClassName, }: {
5
+ subContentClassName?: string;
6
+ }): import("react").JSX.Element;
7
+ /** Ink edges on (soft) or off. The menu stays open on toggle. */
8
+ export declare function EdgesToggleItem(): import("react").JSX.Element;
9
+ //# sourceMappingURL=render-settings-menu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-settings-menu.d.ts","sourceRoot":"","sources":["../../../src/ui/components/render-settings-menu.tsx"],"names":[],"mappings":"AAuGA,wBAAgB,WAAW,CAAC,EAC1B,mBAAqC,GACtC,EAAE;IACD,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B,+BA6CA;AAED,wBAAgB,YAAY,CAAC,EAC3B,mBAAqC,GACtC,EAAE;IACD,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B,+BA6GA;AAED,iEAAiE;AACjE,wBAAgB,eAAe,gCAmB9B"}
@@ -0,0 +1,145 @@
1
+ 'use client';
2
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { usePascalI18n, useScene } from '@pascal-app/core';
4
+ import { ArrowDownLeft, Check, Cloud, CloudSun, Gauge, LocateFixed, Moon, PenLine, Sun, Sunrise, Sunset, } from 'lucide-react';
5
+ import { useState } from 'react';
6
+ import { requestDeviceLocation, resolveLocation } from '../../lib/daylight/location';
7
+ import useViewer, {} from '../../store/use-viewer';
8
+ import { DropdownMenuItem, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, } from '../primitives/dropdown-menu';
9
+ import { openSkyPanel, useSkyPanel, weatherIcon } from './sky-control';
10
+ /**
11
+ * Render settings shared by every display dropdown:
12
+ * - `QualityMenu` (both appearances): Low / Medium / High.
13
+ * - `DaylightMenu` (colored): time-of-day and weather presets, plus "Use my location" when the
14
+ * site has no geocoded point — or, while the sky chip is in the viewport, a row opening it.
15
+ * - `EdgesToggleItem` (monochrome): ink edges on or off.
16
+ */
17
+ function timeOptions(i18n) {
18
+ return [
19
+ {
20
+ id: 'golden',
21
+ icon: Sunrise,
22
+ name: i18n._({ id: 'viewer.daylight.time.golden', message: 'Golden hour' }),
23
+ },
24
+ {
25
+ id: 'midday',
26
+ icon: Sun,
27
+ name: i18n._({ id: 'viewer.daylight.time.midday', message: 'Midday' }),
28
+ },
29
+ {
30
+ id: 'blue',
31
+ icon: Sunset,
32
+ name: i18n._({ id: 'viewer.daylight.time.blue', message: 'Blue hour' }),
33
+ },
34
+ {
35
+ id: 'night',
36
+ icon: Moon,
37
+ name: i18n._({ id: 'viewer.daylight.time.night', message: 'Night' }),
38
+ },
39
+ ];
40
+ }
41
+ function weatherOptions(i18n) {
42
+ return [
43
+ { id: 'clear', name: i18n._({ id: 'viewer.daylight.weather.clear', message: 'Clear' }) },
44
+ { id: 'fair', name: i18n._({ id: 'viewer.daylight.weather.fair', message: 'Fair' }) },
45
+ { id: 'cloudy', name: i18n._({ id: 'viewer.daylight.weather.cloudy', message: 'Cloudy' }) },
46
+ {
47
+ id: 'overcast',
48
+ name: i18n._({ id: 'viewer.daylight.weather.overcast', message: 'Overcast' }),
49
+ },
50
+ ];
51
+ }
52
+ function qualityOptions(i18n) {
53
+ return [
54
+ {
55
+ id: 'low',
56
+ name: i18n._({ id: 'viewer.daylight.quality.low', message: 'Low' }),
57
+ detail: i18n._({
58
+ id: 'viewer.daylight.quality.lowDetail',
59
+ message: 'Fastest — no ambient occlusion or bloom',
60
+ }),
61
+ },
62
+ {
63
+ id: 'medium',
64
+ name: i18n._({ id: 'viewer.daylight.quality.medium', message: 'Medium' }),
65
+ detail: i18n._({
66
+ id: 'viewer.daylight.quality.mediumDetail',
67
+ message: 'Half-resolution ambient occlusion',
68
+ }),
69
+ },
70
+ {
71
+ id: 'high',
72
+ name: i18n._({ id: 'viewer.daylight.quality.high', message: 'High' }),
73
+ detail: i18n._({
74
+ id: 'viewer.daylight.quality.highDetail',
75
+ message: 'Lighting-aware ambient occlusion and bloom',
76
+ }),
77
+ },
78
+ ];
79
+ }
80
+ export function QualityMenu({ subContentClassName = 'dark min-w-56', }) {
81
+ const i18n = usePascalI18n();
82
+ const quality = useViewer((s) => s.renderQuality);
83
+ const choice = useViewer((s) => s.renderQualityChoice);
84
+ const qualities = qualityOptions(i18n);
85
+ const active = qualities.find((o) => o.id === quality) ?? qualities[2];
86
+ const autoName = i18n._({ id: 'viewer.daylight.quality.auto', message: 'Auto' });
87
+ return (_jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { children: [_jsx(Gauge, { className: "h-4 w-4" }), _jsx("span", { children: i18n._({ id: 'viewer.daylight.quality.title', message: 'Quality' }) }), _jsx("span", { className: "ml-auto text-muted-foreground text-xs", children: choice === 'auto' ? `${autoName} (${active.name})` : active.name })] }), _jsxs(DropdownMenuSubContent, { className: subContentClassName, children: [_jsxs(DropdownMenuItem, { onSelect: () => useViewer.getState().setRenderQuality('auto'), children: [_jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "text-foreground", children: autoName }), _jsx("span", { className: "text-muted-foreground text-xs", children: i18n._({
88
+ id: 'viewer.daylight.quality.autoDetail',
89
+ message: 'Picked for this device: {tier}',
90
+ values: { tier: active.name },
91
+ }) })] }), choice === 'auto' ? _jsx(Check, { className: "ml-auto h-4 w-4 text-foreground" }) : null] }), qualities.map((option) => (_jsxs(DropdownMenuItem, { onSelect: () => useViewer.getState().setRenderQuality(option.id), children: [_jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "text-foreground", children: option.name }), _jsx("span", { className: "text-muted-foreground text-xs", children: option.detail })] }), choice === option.id ? _jsx(Check, { className: "ml-auto h-4 w-4 text-foreground" }) : null] }, option.id)))] })] }));
92
+ }
93
+ export function DaylightMenu({ subContentClassName = 'dark min-w-48', }) {
94
+ const i18n = usePascalI18n();
95
+ const timeOfDay = useViewer((s) => s.timeOfDay);
96
+ const weather = useViewer((s) => s.weather);
97
+ const deviceLocation = useViewer((s) => s.deviceLocation);
98
+ const siteHasLocation = useScene((state) => {
99
+ for (const id of state.rootNodeIds) {
100
+ const node = state.nodes[id];
101
+ if (node?.type === 'site')
102
+ return Boolean(node.parcel?.originLngLat || node.dossier?.point);
103
+ }
104
+ return false;
105
+ });
106
+ const [locating, setLocating] = useState(false);
107
+ // The sky chip beside the compass carries time and weather wherever it is mounted.
108
+ const skyChip = useSkyPanel((s) => s.hosts > 0);
109
+ const times = timeOptions(i18n);
110
+ const weathers = weatherOptions(i18n);
111
+ const activeTime = times.find((o) => o.id === timeOfDay) ?? times[1];
112
+ const activeWeather = weathers.find((o) => o.id === weather) ?? weathers[1];
113
+ const source = siteHasLocation ? 'site' : resolveLocation(null, deviceLocation).source;
114
+ if (skyChip) {
115
+ const WeatherIcon = weatherIcon(weather);
116
+ return (_jsxs(DropdownMenuItem, { onSelect: openSkyPanel, children: [_jsx(WeatherIcon, { className: "h-4 w-4" }), _jsx("span", { children: i18n._({ id: 'viewer.daylight.sky.menuItem', message: 'Time & weather' }) }), _jsxs("span", { className: "ml-auto flex items-center gap-1 text-violet-600 text-xs dark:text-violet-300", children: [i18n._({ id: 'viewer.daylight.sky.inViewport', message: 'In viewport' }), _jsx(ArrowDownLeft, { className: "h-3.5 w-3.5" })] })] }));
117
+ }
118
+ return (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { children: [_jsx(activeTime.icon, { className: "h-4 w-4" }), _jsx("span", { children: i18n._({ id: 'viewer.daylight.time.title', message: 'Time of day' }) }), _jsx("span", { className: "ml-auto text-muted-foreground text-xs", children: activeTime.name })] }), _jsxs(DropdownMenuSubContent, { className: subContentClassName, children: [times.map((option) => (_jsxs(DropdownMenuItem, { onSelect: () => useViewer.getState().setTimeOfDay(option.id), children: [_jsx(option.icon, { className: "h-4 w-4" }), _jsx("span", { children: option.name }), timeOfDay === option.id ? _jsx(Check, { className: "ml-auto h-4 w-4" }) : null] }, option.id))), source !== 'site' ? (_jsxs(DropdownMenuItem, { disabled: locating, onSelect: (event) => {
119
+ event.preventDefault();
120
+ setLocating(true);
121
+ requestDeviceLocation()
122
+ .then((location) => useViewer.getState().setDeviceLocation(location))
123
+ .catch(() => { })
124
+ .finally(() => setLocating(false));
125
+ }, children: [_jsx(LocateFixed, { className: "h-4 w-4" }), _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { children: i18n._({ id: 'viewer.daylight.location.use', message: 'Use my location' }) }), _jsx("span", { className: "text-muted-foreground text-xs", children: source === 'device'
126
+ ? i18n._({
127
+ id: 'viewer.daylight.location.device',
128
+ message: 'Sun follows your location',
129
+ })
130
+ : i18n._({
131
+ id: 'viewer.daylight.location.approximate',
132
+ message: 'Sun position is approximate',
133
+ }) })] })] })) : null] })] }), _jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { children: [weather === 'clear' ? _jsx(CloudSun, { className: "h-4 w-4" }) : _jsx(Cloud, { className: "h-4 w-4" }), _jsx("span", { children: i18n._({ id: 'viewer.daylight.weather.title', message: 'Weather' }) }), _jsx("span", { className: "ml-auto text-muted-foreground text-xs", children: activeWeather.name })] }), _jsx(DropdownMenuSubContent, { className: subContentClassName, children: weathers.map((option) => (_jsxs(DropdownMenuItem, { onSelect: () => useViewer.getState().setWeather(option.id), children: [_jsx("span", { children: option.name }), weather === option.id ? _jsx(Check, { className: "ml-auto h-4 w-4" }) : null] }, option.id))) })] })] }));
134
+ }
135
+ /** Ink edges on (soft) or off. The menu stays open on toggle. */
136
+ export function EdgesToggleItem() {
137
+ const i18n = usePascalI18n();
138
+ const on = useViewer((s) => s.edges !== 'off');
139
+ return (_jsxs(DropdownMenuItem, { onSelect: (event) => {
140
+ event.preventDefault();
141
+ useViewer.getState().setEdges(on ? 'off' : 'soft');
142
+ }, children: [_jsx(PenLine, { className: "h-4 w-4" }), _jsx("span", { children: i18n._({ id: 'viewer.display.edges.title', message: 'Edges' }) }), _jsx("span", { className: "ml-auto text-muted-foreground text-xs", children: on
143
+ ? i18n._({ id: 'viewer.common.on', message: 'On' })
144
+ : i18n._({ id: 'viewer.common.off', message: 'Off' }) })] }));
145
+ }
@@ -0,0 +1,10 @@
1
+ import { type Object3D } from 'three';
2
+ import { type RoomAssemblyHeights } from '../../lib/room-assembly-overlay';
3
+ import type { RoomSelectionRecord } from '../../lib/room-selection';
4
+ /** A room's floor, wall faces and outline in the highlight colour, riding its level's object. */
5
+ export declare function RoomAssemblyMesh({ room, heights, levelObject, }: {
6
+ room: RoomSelectionRecord;
7
+ heights: RoomAssemblyHeights;
8
+ levelObject: () => Object3D | undefined;
9
+ }): import("react").JSX.Element;
10
+ //# sourceMappingURL=room-highlight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-highlight.d.ts","sourceRoot":"","sources":["../../../../src/ui/components/rooms/room-highlight.tsx"],"names":[],"mappings":"AAIA,OAAO,EAKL,KAAK,QAAQ,EACd,MAAM,OAAO,CAAA;AAGd,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,iCAAiC,CAAA;AAC3F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AA2BnE,iGAAiG;AACjG,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,OAAO,EACP,WAAW,GACZ,EAAE;IACD,IAAI,EAAE,mBAAmB,CAAA;IACzB,OAAO,EAAE,mBAAmB,CAAA;IAC5B,WAAW,EAAE,MAAM,QAAQ,GAAG,SAAS,CAAA;CACxC,+BAqDA"}
@@ -0,0 +1,56 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useFrame } from '@react-three/fiber';
4
+ import { useEffect, useMemo, useRef } from 'react';
5
+ import { BufferGeometry, DoubleSide, Float32BufferAttribute, } from 'three';
6
+ import { LineBasicNodeMaterial, MeshBasicNodeMaterial } from 'three/webgpu';
7
+ import { OVERLAY_LAYER } from '../../../lib/layers';
8
+ import { getRoomAssembly } from '../../lib/room-assembly-overlay';
9
+ const noRaycast = () => { };
10
+ const outlineMaterial = new LineBasicNodeMaterial({
11
+ color: '#818cf8',
12
+ depthTest: false,
13
+ depthWrite: false,
14
+ });
15
+ const fillMaterial = (opacity) => new MeshBasicNodeMaterial({
16
+ color: '#818cf8',
17
+ depthTest: false,
18
+ depthWrite: false,
19
+ opacity,
20
+ side: DoubleSide,
21
+ transparent: true,
22
+ });
23
+ const floorMaterial = fillMaterial(0.18);
24
+ // Walls and the ceiling stack over the floor when seen from above.
25
+ const surfaceMaterial = fillMaterial(0.1);
26
+ function positions(array) {
27
+ const geometry = new BufferGeometry();
28
+ geometry.setAttribute('position', new Float32BufferAttribute(array, 3));
29
+ return geometry;
30
+ }
31
+ /** A room's floor, wall faces and outline in the highlight colour, riding its level's object. */
32
+ export function RoomAssemblyMesh({ room, heights, levelObject, }) {
33
+ const ref = useRef(null);
34
+ const assembly = useMemo(() => getRoomAssembly(room.geometry, heights), [room, heights]);
35
+ const geometry = useMemo(() => ({
36
+ floor: positions(assembly.floor),
37
+ surfaces: positions(assembly.surfaces),
38
+ outline: positions(assembly.outline),
39
+ }), [assembly]);
40
+ useEffect(() => () => {
41
+ geometry.floor.dispose();
42
+ geometry.surfaces.dispose();
43
+ geometry.outline.dispose();
44
+ }, [geometry]);
45
+ useFrame(() => {
46
+ const level = levelObject();
47
+ if (!ref.current)
48
+ return;
49
+ ref.current.visible = !!level;
50
+ if (level) {
51
+ level.updateWorldMatrix(true, false);
52
+ ref.current.matrix.copy(level.matrixWorld);
53
+ }
54
+ });
55
+ return (_jsxs("group", { matrixAutoUpdate: false, ref: ref, children: [_jsx("mesh", { geometry: geometry.floor, layers: OVERLAY_LAYER, material: floorMaterial, raycast: noRaycast, renderOrder: 100 }), _jsx("mesh", { geometry: geometry.surfaces, layers: OVERLAY_LAYER, material: surfaceMaterial, raycast: noRaycast, renderOrder: 100 }), _jsx("lineSegments", { geometry: geometry.outline, layers: OVERLAY_LAYER, material: outlineMaterial, raycast: noRaycast, renderOrder: 101 })] }));
56
+ }