@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,294 +0,0 @@
1
- 'use client'
2
-
3
- import { type PascalI18n, usePascalI18n, useScene } from '@pascal-app/core'
4
- import {
5
- type RenderQuality,
6
- requestDeviceLocation,
7
- resolveLocation,
8
- type TimeOfDay,
9
- useViewer,
10
- type Weather,
11
- } from '@pascal-app/viewer'
12
- import {
13
- ArrowDownLeft,
14
- Check,
15
- Cloud,
16
- CloudSun,
17
- Gauge,
18
- LocateFixed,
19
- Moon,
20
- PenLine,
21
- Sun,
22
- Sunrise,
23
- Sunset,
24
- } from 'lucide-react'
25
- import { useState } from 'react'
26
- import {
27
- DropdownMenuItem,
28
- DropdownMenuSub,
29
- DropdownMenuSubContent,
30
- DropdownMenuSubTrigger,
31
- } from '../ui/primitives/dropdown-menu'
32
- import { openSkyPanel, useSkyPanel, weatherIcon } from './sky-control'
33
-
34
- /**
35
- * Render settings shared by every display dropdown:
36
- * - `QualityMenu` (both appearances): Low / Medium / High.
37
- * - `DaylightMenu` (colored): time-of-day and weather presets, plus "Use my location" when the
38
- * site has no geocoded point — or, while the sky chip is in the viewport, a row opening it.
39
- * - `EdgesToggleItem` (monochrome): ink edges on or off.
40
- */
41
-
42
- function timeOptions(i18n: PascalI18n) {
43
- return [
44
- {
45
- id: 'golden',
46
- icon: Sunrise,
47
- name: i18n._({ id: 'viewer.daylight.time.golden', message: 'Golden hour' }),
48
- },
49
- {
50
- id: 'midday',
51
- icon: Sun,
52
- name: i18n._({ id: 'viewer.daylight.time.midday', message: 'Midday' }),
53
- },
54
- {
55
- id: 'blue',
56
- icon: Sunset,
57
- name: i18n._({ id: 'viewer.daylight.time.blue', message: 'Blue hour' }),
58
- },
59
- {
60
- id: 'night',
61
- icon: Moon,
62
- name: i18n._({ id: 'viewer.daylight.time.night', message: 'Night' }),
63
- },
64
- ] as const satisfies readonly { id: TimeOfDay; icon: typeof Sun; name: string }[]
65
- }
66
-
67
- function weatherOptions(i18n: PascalI18n) {
68
- return [
69
- { id: 'clear', name: i18n._({ id: 'viewer.daylight.weather.clear', message: 'Clear' }) },
70
- { id: 'fair', name: i18n._({ id: 'viewer.daylight.weather.fair', message: 'Fair' }) },
71
- { id: 'cloudy', name: i18n._({ id: 'viewer.daylight.weather.cloudy', message: 'Cloudy' }) },
72
- {
73
- id: 'overcast',
74
- name: i18n._({ id: 'viewer.daylight.weather.overcast', message: 'Overcast' }),
75
- },
76
- ] as const satisfies readonly { id: Weather; name: string }[]
77
- }
78
-
79
- function qualityOptions(i18n: PascalI18n) {
80
- return [
81
- {
82
- id: 'low',
83
- name: i18n._({ id: 'viewer.daylight.quality.low', message: 'Low' }),
84
- detail: i18n._({
85
- id: 'viewer.daylight.quality.lowDetail',
86
- message: 'Fastest — no ambient occlusion or bloom',
87
- }),
88
- },
89
- {
90
- id: 'medium',
91
- name: i18n._({ id: 'viewer.daylight.quality.medium', message: 'Medium' }),
92
- detail: i18n._({
93
- id: 'viewer.daylight.quality.mediumDetail',
94
- message: 'Half-resolution ambient occlusion',
95
- }),
96
- },
97
- {
98
- id: 'high',
99
- name: i18n._({ id: 'viewer.daylight.quality.high', message: 'High' }),
100
- detail: i18n._({
101
- id: 'viewer.daylight.quality.highDetail',
102
- message: 'Lighting-aware ambient occlusion and bloom',
103
- }),
104
- },
105
- ] as const satisfies readonly { id: RenderQuality; name: string; detail: string }[]
106
- }
107
-
108
- export function QualityMenu({
109
- subContentClassName = 'dark min-w-56',
110
- }: {
111
- subContentClassName?: string
112
- }) {
113
- const i18n = usePascalI18n()
114
- const quality = useViewer((s) => s.renderQuality)
115
- const choice = useViewer((s) => s.renderQualityChoice)
116
- const qualities = qualityOptions(i18n)
117
- const active = qualities.find((o) => o.id === quality) ?? qualities[2]
118
- const autoName = i18n._({ id: 'viewer.daylight.quality.auto', message: 'Auto' })
119
- return (
120
- <DropdownMenuSub>
121
- <DropdownMenuSubTrigger>
122
- <Gauge className="h-4 w-4" />
123
- <span>{i18n._({ id: 'viewer.daylight.quality.title', message: 'Quality' })}</span>
124
- <span className="ml-auto text-muted-foreground text-xs">
125
- {choice === 'auto' ? `${autoName} (${active.name})` : active.name}
126
- </span>
127
- </DropdownMenuSubTrigger>
128
- <DropdownMenuSubContent className={subContentClassName}>
129
- <DropdownMenuItem onSelect={() => useViewer.getState().setRenderQuality('auto')}>
130
- <div className="flex flex-col">
131
- <span className="text-foreground">{autoName}</span>
132
- <span className="text-muted-foreground text-xs">
133
- {i18n._({
134
- id: 'viewer.daylight.quality.autoDetail',
135
- message: 'Picked for this device: {tier}',
136
- values: { tier: active.name },
137
- })}
138
- </span>
139
- </div>
140
- {choice === 'auto' ? <Check className="ml-auto h-4 w-4 text-foreground" /> : null}
141
- </DropdownMenuItem>
142
- {qualities.map((option) => (
143
- <DropdownMenuItem
144
- key={option.id}
145
- onSelect={() => useViewer.getState().setRenderQuality(option.id)}
146
- >
147
- <div className="flex flex-col">
148
- <span className="text-foreground">{option.name}</span>
149
- <span className="text-muted-foreground text-xs">{option.detail}</span>
150
- </div>
151
- {choice === option.id ? <Check className="ml-auto h-4 w-4 text-foreground" /> : null}
152
- </DropdownMenuItem>
153
- ))}
154
- </DropdownMenuSubContent>
155
- </DropdownMenuSub>
156
- )
157
- }
158
-
159
- export function DaylightMenu({
160
- subContentClassName = 'dark min-w-48',
161
- }: {
162
- subContentClassName?: string
163
- }) {
164
- const i18n = usePascalI18n()
165
- const timeOfDay = useViewer((s) => s.timeOfDay)
166
- const weather = useViewer((s) => s.weather)
167
- const deviceLocation = useViewer((s) => s.deviceLocation)
168
- const siteHasLocation = useScene((state) => {
169
- for (const id of state.rootNodeIds) {
170
- const node = state.nodes[id] as
171
- | { type?: string; parcel?: { originLngLat?: unknown }; dossier?: { point?: unknown } }
172
- | undefined
173
- if (node?.type === 'site') return Boolean(node.parcel?.originLngLat || node.dossier?.point)
174
- }
175
- return false
176
- })
177
- const [locating, setLocating] = useState(false)
178
- // The sky chip beside the compass carries time and weather wherever it is mounted.
179
- const skyChip = useSkyPanel((s) => s.hosts > 0)
180
- const times = timeOptions(i18n)
181
- const weathers = weatherOptions(i18n)
182
- const activeTime = times.find((o) => o.id === timeOfDay) ?? times[1]
183
- const activeWeather = weathers.find((o) => o.id === weather) ?? weathers[1]
184
- const source = siteHasLocation ? 'site' : resolveLocation(null, deviceLocation).source
185
-
186
- if (skyChip) {
187
- const WeatherIcon = weatherIcon(weather)
188
- return (
189
- <DropdownMenuItem onSelect={openSkyPanel}>
190
- <WeatherIcon className="h-4 w-4" />
191
- <span>{i18n._({ id: 'viewer.daylight.sky.menuItem', message: 'Time & weather' })}</span>
192
- <span className="ml-auto flex items-center gap-1 text-violet-600 text-xs dark:text-violet-300">
193
- {i18n._({ id: 'viewer.daylight.sky.inViewport', message: 'In viewport' })}
194
- <ArrowDownLeft className="h-3.5 w-3.5" />
195
- </span>
196
- </DropdownMenuItem>
197
- )
198
- }
199
-
200
- return (
201
- <>
202
- <DropdownMenuSub>
203
- <DropdownMenuSubTrigger>
204
- <activeTime.icon className="h-4 w-4" />
205
- <span>{i18n._({ id: 'viewer.daylight.time.title', message: 'Time of day' })}</span>
206
- <span className="ml-auto text-muted-foreground text-xs">{activeTime.name}</span>
207
- </DropdownMenuSubTrigger>
208
- <DropdownMenuSubContent className={subContentClassName}>
209
- {times.map((option) => (
210
- <DropdownMenuItem
211
- key={option.id}
212
- onSelect={() => useViewer.getState().setTimeOfDay(option.id)}
213
- >
214
- <option.icon className="h-4 w-4" />
215
- <span>{option.name}</span>
216
- {timeOfDay === option.id ? <Check className="ml-auto h-4 w-4" /> : null}
217
- </DropdownMenuItem>
218
- ))}
219
- {source !== 'site' ? (
220
- <DropdownMenuItem
221
- disabled={locating}
222
- onSelect={(event) => {
223
- event.preventDefault()
224
- setLocating(true)
225
- requestDeviceLocation()
226
- .then((location) => useViewer.getState().setDeviceLocation(location))
227
- .catch(() => {})
228
- .finally(() => setLocating(false))
229
- }}
230
- >
231
- <LocateFixed className="h-4 w-4" />
232
- <div className="flex flex-col">
233
- <span>
234
- {i18n._({ id: 'viewer.daylight.location.use', message: 'Use my location' })}
235
- </span>
236
- <span className="text-muted-foreground text-xs">
237
- {source === 'device'
238
- ? i18n._({
239
- id: 'viewer.daylight.location.device',
240
- message: 'Sun follows your location',
241
- })
242
- : i18n._({
243
- id: 'viewer.daylight.location.approximate',
244
- message: 'Sun position is approximate',
245
- })}
246
- </span>
247
- </div>
248
- </DropdownMenuItem>
249
- ) : null}
250
- </DropdownMenuSubContent>
251
- </DropdownMenuSub>
252
- <DropdownMenuSub>
253
- <DropdownMenuSubTrigger>
254
- {weather === 'clear' ? <CloudSun className="h-4 w-4" /> : <Cloud className="h-4 w-4" />}
255
- <span>{i18n._({ id: 'viewer.daylight.weather.title', message: 'Weather' })}</span>
256
- <span className="ml-auto text-muted-foreground text-xs">{activeWeather.name}</span>
257
- </DropdownMenuSubTrigger>
258
- <DropdownMenuSubContent className={subContentClassName}>
259
- {weathers.map((option) => (
260
- <DropdownMenuItem
261
- key={option.id}
262
- onSelect={() => useViewer.getState().setWeather(option.id)}
263
- >
264
- <span>{option.name}</span>
265
- {weather === option.id ? <Check className="ml-auto h-4 w-4" /> : null}
266
- </DropdownMenuItem>
267
- ))}
268
- </DropdownMenuSubContent>
269
- </DropdownMenuSub>
270
- </>
271
- )
272
- }
273
-
274
- /** Ink edges on (soft) or off. The menu stays open on toggle. */
275
- export function EdgesToggleItem() {
276
- const i18n = usePascalI18n()
277
- const on = useViewer((s) => s.edges !== 'off')
278
- return (
279
- <DropdownMenuItem
280
- onSelect={(event) => {
281
- event.preventDefault()
282
- useViewer.getState().setEdges(on ? 'off' : 'soft')
283
- }}
284
- >
285
- <PenLine className="h-4 w-4" />
286
- <span>{i18n._({ id: 'viewer.display.edges.title', message: 'Edges' })}</span>
287
- <span className="ml-auto text-muted-foreground text-xs">
288
- {on
289
- ? i18n._({ id: 'viewer.common.on', message: 'On' })
290
- : i18n._({ id: 'viewer.common.off', message: 'Off' })}
291
- </span>
292
- </DropdownMenuItem>
293
- )
294
- }