@kerros/editor 0.0.0-stage → 0.1.0-rc.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 (345) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +35 -2
  3. package/dist/SitePlanner.js +1189 -0
  4. package/dist/SitePlanner.js.map +1 -0
  5. package/dist/adapters/basemap.js +11 -0
  6. package/dist/adapters/basemap.js.map +1 -0
  7. package/dist/adapters/persistence.js +121 -0
  8. package/dist/adapters/persistence.js.map +1 -0
  9. package/dist/adapters/status.js +11 -0
  10. package/dist/adapters/status.js.map +1 -0
  11. package/dist/adapters/useProjectPersistence.js +56 -0
  12. package/dist/adapters/useProjectPersistence.js.map +1 -0
  13. package/dist/components/AiImportPanel.js +635 -0
  14. package/dist/components/AiImportPanel.js.map +1 -0
  15. package/dist/components/AnalysisPreview.js +179 -0
  16. package/dist/components/AnalysisPreview.js.map +1 -0
  17. package/dist/components/ElevatorPanel.js +120 -0
  18. package/dist/components/ElevatorPanel.js.map +1 -0
  19. package/dist/components/Icons.js +57 -0
  20. package/dist/components/Icons.js.map +1 -0
  21. package/dist/components/ImportDialog.js +467 -0
  22. package/dist/components/ImportDialog.js.map +1 -0
  23. package/dist/components/Inspector.js +1184 -0
  24. package/dist/components/Inspector.js.map +1 -0
  25. package/dist/components/NavigatePanel.js +301 -0
  26. package/dist/components/NavigatePanel.js.map +1 -0
  27. package/dist/components/NavigationGraph.js +234 -0
  28. package/dist/components/NavigationGraph.js.map +1 -0
  29. package/dist/components/StructureExplorer.js +510 -0
  30. package/dist/components/StructureExplorer.js.map +1 -0
  31. package/dist/components/StructureView.js +286 -0
  32. package/dist/components/StructureView.js.map +1 -0
  33. package/dist/components/controls.js +209 -0
  34. package/dist/components/controls.js.map +1 -0
  35. package/dist/components/graphLayout.js +27 -0
  36. package/dist/components/graphLayout.js.map +1 -0
  37. package/dist/host.js +175 -0
  38. package/dist/host.js.map +1 -0
  39. package/dist/i18n.js +88 -0
  40. package/dist/i18n.js.map +1 -0
  41. package/dist/import/analysisPreview.js +41 -0
  42. package/dist/import/analysisPreview.js.map +1 -0
  43. package/dist/import/brief.js +29 -0
  44. package/dist/import/brief.js.map +1 -0
  45. package/dist/import/checkpoint.js +50 -0
  46. package/dist/import/checkpoint.js.map +1 -0
  47. package/dist/import/detect.js +120 -0
  48. package/dist/import/detect.js.map +1 -0
  49. package/dist/import/documentSvg.js +49 -0
  50. package/dist/import/documentSvg.js.map +1 -0
  51. package/dist/import/instructions.js +20 -0
  52. package/dist/import/instructions.js.map +1 -0
  53. package/dist/import/openings.js +61 -0
  54. package/dist/import/openings.js.map +1 -0
  55. package/dist/import/planImport.js +233 -0
  56. package/dist/import/planImport.js.map +1 -0
  57. package/dist/import/register.js +47 -0
  58. package/dist/import/register.js.map +1 -0
  59. package/dist/import/runs.js +67 -0
  60. package/dist/import/runs.js.map +1 -0
  61. package/dist/import/session.js +23 -0
  62. package/dist/import/session.js.map +1 -0
  63. package/dist/import/types.js +13 -0
  64. package/dist/import/types.js.map +1 -0
  65. package/dist/import/usage.js +29 -0
  66. package/dist/import/usage.js.map +1 -0
  67. package/dist/import/walls.js +112 -0
  68. package/dist/import/walls.js.map +1 -0
  69. package/dist/index.js +178 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/map/CabinMaterials.js +81 -0
  72. package/dist/map/CabinMaterials.js.map +1 -0
  73. package/dist/map/CabinMirror.js +43 -0
  74. package/dist/map/CabinMirror.js.map +1 -0
  75. package/dist/map/Doors.js +80 -0
  76. package/dist/map/Doors.js.map +1 -0
  77. package/dist/map/ElevatorCabin.js +93 -0
  78. package/dist/map/ElevatorCabin.js.map +1 -0
  79. package/dist/map/FixtureLights.js +117 -0
  80. package/dist/map/FixtureLights.js.map +1 -0
  81. package/dist/map/MapCanvas.js +2161 -0
  82. package/dist/map/MapCanvas.js.map +1 -0
  83. package/dist/map/Passenger.js +69 -0
  84. package/dist/map/Passenger.js.map +1 -0
  85. package/dist/map/SceneLayer.js +1534 -0
  86. package/dist/map/SceneLayer.js.map +1 -0
  87. package/dist/map/UndergroundContext.js +153 -0
  88. package/dist/map/UndergroundContext.js.map +1 -0
  89. package/dist/map/ambience.js +194 -0
  90. package/dist/map/ambience.js.map +1 -0
  91. package/dist/map/architecture.js +305 -0
  92. package/dist/map/architecture.js.map +1 -0
  93. package/dist/map/bathMusic.js +92 -0
  94. package/dist/map/bathMusic.js.map +1 -0
  95. package/dist/map/cameraAim.js +33 -0
  96. package/dist/map/cameraAim.js.map +1 -0
  97. package/dist/map/elevators.js +56 -0
  98. package/dist/map/elevators.js.map +1 -0
  99. package/dist/map/excavation.js +52 -0
  100. package/dist/map/excavation.js.map +1 -0
  101. package/dist/map/exteriors.js +25 -0
  102. package/dist/map/exteriors.js.map +1 -0
  103. package/dist/map/features.js +218 -0
  104. package/dist/map/features.js.map +1 -0
  105. package/dist/map/fences.js +29 -0
  106. package/dist/map/fences.js.map +1 -0
  107. package/dist/map/journey.js +112 -0
  108. package/dist/map/journey.js.map +1 -0
  109. package/dist/map/levels.js +6 -0
  110. package/dist/map/levels.js.map +1 -0
  111. package/dist/map/lighting.js +143 -0
  112. package/dist/map/lighting.js.map +1 -0
  113. package/dist/map/loadBasemap.js +26 -0
  114. package/dist/map/loadBasemap.js.map +1 -0
  115. package/dist/map/materials.js +183 -0
  116. package/dist/map/materials.js.map +1 -0
  117. package/dist/map/poolLighting.js +39 -0
  118. package/dist/map/poolLighting.js.map +1 -0
  119. package/dist/map/projection.js +24 -0
  120. package/dist/map/projection.js.map +1 -0
  121. package/dist/map/rampGeometry.js +38 -0
  122. package/dist/map/rampGeometry.js.map +1 -0
  123. package/dist/map/route.js +107 -0
  124. package/dist/map/route.js.map +1 -0
  125. package/dist/map/stairSurfaces.js +115 -0
  126. package/dist/map/stairSurfaces.js.map +1 -0
  127. package/dist/map/surfaces.js +136 -0
  128. package/dist/map/surfaces.js.map +1 -0
  129. package/dist/map/textures.js +95 -0
  130. package/dist/map/textures.js.map +1 -0
  131. package/dist/map/underground.js +34 -0
  132. package/dist/map/underground.js.map +1 -0
  133. package/dist/map/walk.js +433 -0
  134. package/dist/map/walk.js.map +1 -0
  135. package/dist/map/walkContext.js +46 -0
  136. package/dist/map/walkContext.js.map +1 -0
  137. package/dist/map/walkDimensions.js +7 -0
  138. package/dist/map/walkDimensions.js.map +1 -0
  139. package/dist/map/walkJourney.js +85 -0
  140. package/dist/map/walkJourney.js.map +1 -0
  141. package/dist/map/walkSurfaces.js +106 -0
  142. package/dist/map/walkSurfaces.js.map +1 -0
  143. package/dist/map/wallJoins.js +70 -0
  144. package/dist/map/wallJoins.js.map +1 -0
  145. package/dist/map/water.js +274 -0
  146. package/dist/map/water.js.map +1 -0
  147. package/dist/model/authoring.js +99 -0
  148. package/dist/model/authoring.js.map +1 -0
  149. package/dist/model/boundaries.js +548 -0
  150. package/dist/model/boundaries.js.map +1 -0
  151. package/dist/model/coverage.js +30 -0
  152. package/dist/model/coverage.js.map +1 -0
  153. package/dist/model/document.js +52 -0
  154. package/dist/model/document.js.map +1 -0
  155. package/dist/model/doors.js +47 -0
  156. package/dist/model/doors.js.map +1 -0
  157. package/dist/model/factory.js +56 -0
  158. package/dist/model/factory.js.map +1 -0
  159. package/dist/model/geometry.js +454 -0
  160. package/dist/model/geometry.js.map +1 -0
  161. package/dist/model/history.js +29 -0
  162. package/dist/model/history.js.map +1 -0
  163. package/dist/model/imports.js +45 -0
  164. package/dist/model/imports.js.map +1 -0
  165. package/dist/model/inference.js +182 -0
  166. package/dist/model/inference.js.map +1 -0
  167. package/dist/model/materials.js +11 -0
  168. package/dist/model/materials.js.map +1 -0
  169. package/dist/model/mutations.js +157 -0
  170. package/dist/model/mutations.js.map +1 -0
  171. package/dist/model/navigation.js +275 -0
  172. package/dist/model/navigation.js.map +1 -0
  173. package/dist/model/ontology.js +109 -0
  174. package/dist/model/ontology.js.map +1 -0
  175. package/dist/model/passages.js +5 -0
  176. package/dist/model/passages.js.map +1 -0
  177. package/dist/model/portals.js +42 -0
  178. package/dist/model/portals.js.map +1 -0
  179. package/dist/model/precision.js +11 -0
  180. package/dist/model/precision.js.map +1 -0
  181. package/dist/model/project.js +31 -0
  182. package/dist/model/project.js.map +1 -0
  183. package/dist/model/ramps.js +10 -0
  184. package/dist/model/ramps.js.map +1 -0
  185. package/dist/model/roomNavigation.js +87 -0
  186. package/dist/model/roomNavigation.js.map +1 -0
  187. package/dist/model/routeClearance.js +72 -0
  188. package/dist/model/routeClearance.js.map +1 -0
  189. package/dist/model/spaces.js +104 -0
  190. package/dist/model/spaces.js.map +1 -0
  191. package/dist/model/stairGeometry.js +40 -0
  192. package/dist/model/stairGeometry.js.map +1 -0
  193. package/dist/model/stairTraversal.js +48 -0
  194. package/dist/model/stairTraversal.js.map +1 -0
  195. package/dist/model/topology.js +63 -0
  196. package/dist/model/topology.js.map +1 -0
  197. package/dist/model/types.js +35 -0
  198. package/dist/model/types.js.map +1 -0
  199. package/dist/model/validate.js +240 -0
  200. package/dist/model/validate.js.map +1 -0
  201. package/dist/model/vertical.js +129 -0
  202. package/dist/model/vertical.js.map +1 -0
  203. package/dist/model/walls.js +261 -0
  204. package/dist/model/walls.js.map +1 -0
  205. package/dist/planner/DrawingDock.js +231 -0
  206. package/dist/planner/DrawingDock.js.map +1 -0
  207. package/dist/planner/FloorControls.js +93 -0
  208. package/dist/planner/FloorControls.js.map +1 -0
  209. package/dist/planner/MapSettings.js +143 -0
  210. package/dist/planner/MapSettings.js.map +1 -0
  211. package/dist/planner/PlannerHelp.js +57 -0
  212. package/dist/planner/PlannerHelp.js.map +1 -0
  213. package/dist/planner/ProjectSidebar.js +246 -0
  214. package/dist/planner/ProjectSidebar.js.map +1 -0
  215. package/dist/planner/floors.js +18 -0
  216. package/dist/planner/floors.js.map +1 -0
  217. package/dist/planner/useDrawingTools.js +341 -0
  218. package/dist/planner/useDrawingTools.js.map +1 -0
  219. package/dist/planner/useMapDisplay.js +51 -0
  220. package/dist/planner/useMapDisplay.js.map +1 -0
  221. package/dist/planner/usePlannerKeyboard.js +138 -0
  222. package/dist/planner/usePlannerKeyboard.js.map +1 -0
  223. package/dist/planner/useReferenceAlignment.js +50 -0
  224. package/dist/planner/useReferenceAlignment.js.map +1 -0
  225. package/dist/styles.css +4931 -0
  226. package/dist/theme/index.js +34 -0
  227. package/dist/theme/index.js.map +1 -0
  228. package/dist/types/SitePlanner.d.ts +5 -0
  229. package/dist/types/adapters/basemap.d.ts +2 -0
  230. package/dist/types/adapters/persistence.d.ts +29 -0
  231. package/dist/types/adapters/status.d.ts +4 -0
  232. package/dist/types/adapters/useProjectPersistence.d.ts +13 -0
  233. package/dist/types/components/AiImportPanel.d.ts +9 -0
  234. package/dist/types/components/AnalysisPreview.d.ts +4 -0
  235. package/dist/types/components/ElevatorPanel.d.ts +18 -0
  236. package/dist/types/components/Icons.d.ts +8 -0
  237. package/dist/types/components/ImportDialog.d.ts +42 -0
  238. package/dist/types/components/Inspector.d.ts +38 -0
  239. package/dist/types/components/NavigatePanel.d.ts +27 -0
  240. package/dist/types/components/NavigationGraph.d.ts +10 -0
  241. package/dist/types/components/StructureExplorer.d.ts +8 -0
  242. package/dist/types/components/StructureView.d.ts +22 -0
  243. package/dist/types/components/controls.d.ts +52 -0
  244. package/dist/types/components/graphLayout.d.ts +9 -0
  245. package/dist/types/editor/host.d.ts +9 -0
  246. package/dist/types/editor/index.d.ts +2 -0
  247. package/dist/types/i18n.d.ts +85 -0
  248. package/dist/types/import/analysisPreview.d.ts +27 -0
  249. package/dist/types/import/brief.d.ts +22 -0
  250. package/dist/types/import/checkpoint.d.ts +49 -0
  251. package/dist/types/import/detect.d.ts +34 -0
  252. package/dist/types/import/documentSvg.d.ts +2 -0
  253. package/dist/types/import/index.d.ts +10 -0
  254. package/dist/types/import/instructions.d.ts +7 -0
  255. package/dist/types/import/openings.d.ts +22 -0
  256. package/dist/types/import/planImport.d.ts +22 -0
  257. package/dist/types/import/register.d.ts +25 -0
  258. package/dist/types/import/runs.d.ts +31 -0
  259. package/dist/types/import/session.d.ts +33 -0
  260. package/dist/types/import/types.d.ts +56 -0
  261. package/dist/types/import/usage.d.ts +10 -0
  262. package/dist/types/import/walls.d.ts +37 -0
  263. package/dist/types/map/CabinMaterials.d.ts +18 -0
  264. package/dist/types/map/CabinMirror.d.ts +13 -0
  265. package/dist/types/map/Doors.d.ts +11 -0
  266. package/dist/types/map/ElevatorCabin.d.ts +20 -0
  267. package/dist/types/map/FixtureLights.d.ts +35 -0
  268. package/dist/types/map/MapCanvas.d.ts +87 -0
  269. package/dist/types/map/Passenger.d.ts +18 -0
  270. package/dist/types/map/SceneLayer.d.ts +306 -0
  271. package/dist/types/map/UndergroundContext.d.ts +18 -0
  272. package/dist/types/map/ambience.d.ts +29 -0
  273. package/dist/types/map/architecture.d.ts +8 -0
  274. package/dist/types/map/bathMusic.d.ts +14 -0
  275. package/dist/types/map/cameraAim.d.ts +20 -0
  276. package/dist/types/map/elevators.d.ts +16 -0
  277. package/dist/types/map/excavation.d.ts +9 -0
  278. package/dist/types/map/exteriors.d.ts +3 -0
  279. package/dist/types/map/features.d.ts +33 -0
  280. package/dist/types/map/fences.d.ts +4 -0
  281. package/dist/types/map/journey.d.ts +36 -0
  282. package/dist/types/map/levels.d.ts +4 -0
  283. package/dist/types/map/lighting.d.ts +103 -0
  284. package/dist/types/map/loadBasemap.d.ts +4 -0
  285. package/dist/types/map/materials.d.ts +38 -0
  286. package/dist/types/map/poolLighting.d.ts +11 -0
  287. package/dist/types/map/projection.d.ts +9 -0
  288. package/dist/types/map/rampGeometry.d.ts +8 -0
  289. package/dist/types/map/route.d.ts +59 -0
  290. package/dist/types/map/stairSurfaces.d.ts +29 -0
  291. package/dist/types/map/surfaces.d.ts +36 -0
  292. package/dist/types/map/textures.d.ts +8 -0
  293. package/dist/types/map/underground.d.ts +30 -0
  294. package/dist/types/map/walk.d.ts +232 -0
  295. package/dist/types/map/walkContext.d.ts +11 -0
  296. package/dist/types/map/walkDimensions.d.ts +5 -0
  297. package/dist/types/map/walkJourney.d.ts +33 -0
  298. package/dist/types/map/walkSurfaces.d.ts +27 -0
  299. package/dist/types/map/wallJoins.d.ts +6 -0
  300. package/dist/types/map/water.d.ts +12 -0
  301. package/dist/types/model/authoring.d.ts +38 -0
  302. package/dist/types/model/boundaries.d.ts +41 -0
  303. package/dist/types/model/coverage.d.ts +8 -0
  304. package/dist/types/model/document.d.ts +9 -0
  305. package/dist/types/model/doors.d.ts +19 -0
  306. package/dist/types/model/factory.d.ts +5 -0
  307. package/dist/types/model/geometry.d.ts +99 -0
  308. package/dist/types/model/history.d.ts +10 -0
  309. package/dist/types/model/host.d.ts +154 -0
  310. package/dist/types/model/imports.d.ts +7 -0
  311. package/dist/types/model/inference.d.ts +46 -0
  312. package/dist/types/model/live.d.ts +60 -0
  313. package/dist/types/model/materials.d.ts +28 -0
  314. package/dist/types/model/mutations.d.ts +178 -0
  315. package/dist/types/model/navigation.d.ts +70 -0
  316. package/dist/types/model/ontology.d.ts +43 -0
  317. package/dist/types/model/passages.d.ts +2 -0
  318. package/dist/types/model/portals.d.ts +9 -0
  319. package/dist/types/model/precision.d.ts +11 -0
  320. package/dist/types/model/project.d.ts +8 -0
  321. package/dist/types/model/ramps.d.ts +7 -0
  322. package/dist/types/model/roomNavigation.d.ts +15 -0
  323. package/dist/types/model/routeClearance.d.ts +11 -0
  324. package/dist/types/model/spaces.d.ts +46 -0
  325. package/dist/types/model/stairGeometry.d.ts +27 -0
  326. package/dist/types/model/stairTraversal.d.ts +8 -0
  327. package/dist/types/model/topology.d.ts +14 -0
  328. package/dist/types/model/types.d.ts +369 -0
  329. package/dist/types/model/validate.d.ts +38 -0
  330. package/dist/types/model/vertical.d.ts +145 -0
  331. package/dist/types/model/walls.d.ts +71 -0
  332. package/dist/types/planner/DrawingDock.d.ts +16 -0
  333. package/dist/types/planner/FloorControls.d.ts +17 -0
  334. package/dist/types/planner/MapSettings.d.ts +16 -0
  335. package/dist/types/planner/PlannerHelp.d.ts +3 -0
  336. package/dist/types/planner/ProjectSidebar.d.ts +32 -0
  337. package/dist/types/planner/floors.d.ts +3 -0
  338. package/dist/types/planner/types.d.ts +3 -0
  339. package/dist/types/planner/useDrawingTools.d.ts +52 -0
  340. package/dist/types/planner/useMapDisplay.d.ts +29 -0
  341. package/dist/types/planner/usePlannerKeyboard.d.ts +18 -0
  342. package/dist/types/planner/useReferenceAlignment.d.ts +30 -0
  343. package/dist/types/schema/index.d.ts +25 -0
  344. package/dist/types/theme/index.d.ts +55 -0
  345. package/package.json +58 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavigatePanel.js","sources":["../../../../src/components/NavigatePanel.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react';\nimport {\n ArrowUpDown,\n ArrowUpRight,\n ChevronLeft,\n ChevronRight,\n DoorOpen,\n Flag,\n Footprints,\n Layers3,\n MapPin,\n MoveUpRight,\n Pause,\n PersonStanding,\n Play,\n Search,\n Waypoints,\n X,\n} from 'lucide-react';\nimport type { Point, ProjectDocument, SiteObject } from '../model/types';\nimport type { Route, RouteStep } from '../model/navigation';\nimport { floorPhrase, HERE, navNodes } from '../model/navigation';\nimport { EntityIcon } from './Icons';\nimport { useStrings } from '../theme';\n\n/** Object kinds offered as route endpoints — everything routeAnchors can resolve meaningfully. */\nconst NAV_KINDS = ['room', 'zone', 'door', 'gate', 'poi', 'elevator', 'stairs', 'turnstile'];\nconst STEP_ICONS: Record<RouteStep['kind'], typeof MapPin> = {\n depart: MapPin,\n walk: Footprints,\n door: DoorOpen,\n stairs: MoveUpRight,\n elevator: ArrowUpRight,\n arrive: Flag,\n};\n\nexport interface NavigatePanelProps {\n project: ProjectDocument;\n /** Where the person is — the walk's avatar — offered as \"Where you are\" and proposed as the\n * start whenever nothing else has been chosen. Null when nobody has walked yet. */\n here?: { floorId: string | null; position: Point } | null;\n from: string | null;\n to: string | null;\n route: Route | null;\n playing: boolean;\n activeStep: number | null;\n editing: boolean;\n onFrom: (id: string | null) => void;\n onTo: (id: string | null) => void;\n onSwap: () => void;\n onClear: () => void;\n onPlay: () => void;\n onPause: () => void;\n onStep: (delta: 1 | -1) => void;\n onSeek: (index: number) => void;\n onClose: () => void;\n}\nexport function NavigatePanel({\n project,\n here,\n from,\n to,\n route,\n playing,\n activeStep,\n editing,\n onFrom,\n onTo,\n onSwap,\n onClear,\n onPlay,\n onPause,\n onStep,\n onSeek,\n onClose,\n}: NavigatePanelProps) {\n const en = useStrings();\n const t = en.navigate;\n // Every place a route can start or end, in level order and alphabetical within a level, each\n // knowing its level's name and the space it sits in (a door in a room, a room in a zone), which\n // is what the browse view groups by.\n const { places, levels } = useMemo(() => {\n const options = project.objects.filter(o => NAV_KINDS.includes(o.kind));\n const byId = new Map(project.objects.map(o => [o.id, o]));\n const levels: Level[] = [\n { id: null, name: en.outdoors, count: 0 },\n ...project.floors.map(f => ({ id: f.id as string | null, name: floorPhrase(project, f.id), count: 0 })),\n ];\n const places: Place[] = [];\n for (const level of levels) {\n const items = options.filter(o => o.floorId === level.id).sort((a, b) => a.name.localeCompare(b.name));\n level.count = items.length;\n for (const o of items) {\n const parent = o.parentId ? byId.get(o.parentId) : undefined;\n places.push({ id: o.id, o, floorId: level.id, floor: level.name, group: parent?.name ?? '' });\n }\n }\n return { places, levels: levels.filter(l => l.count) };\n }, [project, en.outdoors]);\n const hereLabel = here\n ? `${t.here} · ${here.floorId === null ? en.outdoors : floorPhrase(project, here.floorId)}`\n : '';\n const herePlace: Place | null = here\n ? {\n id: HERE,\n o: { id: HERE, kind: 'poi', name: t.here, floorId: here.floorId, position: here.position } as SiteObject,\n floorId: here.floorId,\n floor: hereLabel,\n group: '',\n }\n : null;\n // Propose where the person is as the start. Once, when there is one and nothing was chosen —\n // not on every render, or clearing the start would be undone at the next frame.\n const proposed = useRef(false);\n useEffect(() => {\n if (here && !from && !to && !proposed.current) {\n proposed.current = true;\n onFrom(HERE);\n }\n }, [here, from, to, onFrom]);\n const hasGraph = useMemo(() => navNodes(project).length > 0, [project]);\n const floorsCrossed = route ? new Set(route.steps.map(s => s.floorId)).size : 0;\n const picker = (\n label: string,\n value: string | null,\n other: string | null,\n set: (id: string | null) => void,\n first: Place | null,\n ) => (\n <PlacePicker\n label={label}\n places={places}\n levels={levels}\n first={first}\n value={value}\n other={other}\n onPick={set}\n placeholder={t.choose}\n strings={t}\n />\n );\n return (\n <aside className=\"inspector navigate-panel\">\n <div className=\"inspector-top\">\n <span>{t.title}</span>\n <button className=\"icon-button\" aria-label=\"Close navigation\" onClick={onClose}>\n <X size={17} />\n </button>\n </div>\n <div className=\"inspector-scroll\">\n <div className=\"nav-endpoints\">\n <div className=\"nav-endpoint-fields\">\n {picker(t.from, from, to, onFrom, herePlace)}\n {picker(t.to, to, from, onTo, null)}\n </div>\n <div className=\"nav-endpoint-actions\">\n <button className=\"icon-button\" aria-label={t.swap} title={t.swap} disabled={!from && !to} onClick={onSwap}>\n <ArrowUpDown size={16} />\n </button>\n {(from || to) && (\n <button className=\"text-button\" onClick={onClear}>\n {t.clear}\n </button>\n )}\n </div>\n </div>\n {!hasGraph ? (\n <div className=\"nav-empty\">\n <Waypoints size={22} />\n <strong>{t.noGraph}</strong>\n <p>{editing ? t.noGraphHintEdit : t.noGraphHintView}</p>\n </div>\n ) : from && to && !route ? (\n <div className=\"nav-empty\">\n <Waypoints size={22} />\n <strong>{t.unreachable}</strong>\n <p>{t.unreachableHint}</p>\n </div>\n ) : route ? (\n <>\n <div className=\"nav-summary\">\n <div>\n <strong>{Math.max(1, Math.round(route.distance))} m</strong>\n <span>{t.walking}</span>\n </div>\n <div>\n <strong>{floorsCrossed}</strong>\n <span>{t.floors}</span>\n </div>\n <div>\n <strong>{route.steps.length}</strong>\n <span>{t.steps}</span>\n </div>\n </div>\n <ol className=\"nav-steps\">\n {route.steps.map((s, i) => {\n const Icon = STEP_ICONS[s.kind];\n return (\n <li key={i}>\n <button className={`nav-step ${i === activeStep ? 'active' : ''}`} onClick={() => onSeek(i)}>\n <span className=\"nav-step-num\">{i + 1}</span>\n <Icon size={15} />\n <span className=\"nav-step-text\">\n {s.text}\n <small>\n {floorPhrase(project, s.floorId)}\n {s.distance !== undefined ? ` · ${Math.max(1, Math.round(s.distance))} m` : ''}\n </small>\n </span>\n </button>\n </li>\n );\n })}\n </ol>\n </>\n ) : (\n <p className=\"helper\">{t.pick}</p>\n )}\n </div>\n {route && (\n <div className=\"nav-controls\">\n <button\n className=\"icon-button\"\n aria-label={t.prev}\n title={t.prev}\n disabled={(activeStep ?? 0) <= 0}\n onClick={() => onStep(-1)}\n >\n <ChevronLeft size={18} />\n </button>\n <button className=\"button primary nav-play\" onClick={playing ? onPause : onPlay}>\n {playing ? (\n <>\n <Pause size={15} />\n {t.pause}\n </>\n ) : (\n <>\n <Play size={15} />\n {t.play}\n </>\n )}\n </button>\n <button\n className=\"icon-button\"\n aria-label={t.next}\n title={t.next}\n disabled={(activeStep ?? -1) >= route.steps.length - 1}\n onClick={() => onStep(1)}\n >\n <ChevronRight size={18} />\n </button>\n </div>\n )}\n </aside>\n );\n}\n\ninterface Place {\n id: string;\n o: SiteObject;\n floorId: string | null;\n floor: string;\n /** The space this place sits in, when the document says so. */\n group: string;\n}\ninterface Level {\n id: string | null;\n name: string;\n count: number;\n}\n/** How many rows a list shows before it asks to be scrolled, and how many more each scroll adds.\n * A big floor has hundreds of rooms, and all of them are one list — laid out a page at a time so\n * the first paint is instant and the last row is still reachable. */\nconst PAGE = 60;\n\n/** A searchable place picker with two ways in. Type, and it searches every place on every level;\n * leave it empty, and it browses — the levels first, then the places on the one you open, grouped\n * by the space they sit in. Both lists scroll to the end rather than stopping at a count. */\nfunction PlacePicker({\n label,\n places,\n levels,\n first,\n value,\n other,\n onPick,\n placeholder,\n strings,\n}: {\n label: string;\n places: Place[];\n levels: Level[];\n /** A place offered ahead of everything — \"Where you are\" — or null. */\n first: Place | null;\n value: string | null;\n other: string | null;\n onPick: (id: string | null) => void;\n placeholder: string;\n strings: { here: string; hereHint: string; levels: string; allLevels: string; places: string; more: string };\n}) {\n const [query, setQuery] = useState(''),\n [open, setOpen] = useState(false),\n [cursor, setCursor] = useState(0),\n // The level being browsed; undefined is the list of levels.\n [level, setLevel] = useState<string | null | undefined>(undefined),\n [limit, setLimit] = useState(PAGE);\n const wrap = useRef<HTMLDivElement>(null);\n const list = useRef<HTMLDivElement>(null);\n const selected = value === HERE ? first : value ? places.find(p => p.id === value) : undefined;\n const q = query.trim().toLowerCase();\n const browsing = open && !q;\n // Search mode: every place whose name or level matches. Browse mode on a level: that level's\n // places. Neither is cut off; the page limit only decides how many are laid out so far.\n const matches: Place[] = !open\n ? []\n : q\n ? places.filter(p => p.id !== other && (p.o.name.toLowerCase().includes(q) || p.floor.toLowerCase().includes(q)))\n : level !== undefined\n ? places.filter(p => p.id !== other && p.floorId === level)\n : [];\n const shown = matches.slice(0, limit);\n useEffect(() => {\n const away = (e: PointerEvent) => {\n if (!wrap.current?.contains(e.target as Node)) setOpen(false);\n };\n window.addEventListener('pointerdown', away);\n return () => window.removeEventListener('pointerdown', away);\n }, []);\n useEffect(() => {\n setCursor(0);\n setLimit(PAGE);\n if (list.current) list.current.scrollTop = 0;\n }, [q, open, level]);\n const pick = (id: string) => {\n onPick(id);\n setOpen(false);\n setQuery('');\n setLevel(undefined);\n };\n const more = () => {\n const el = list.current;\n if (el && el.scrollTop + el.clientHeight >= el.scrollHeight - 80 && limit < matches.length) setLimit(n => n + PAGE);\n };\n const option = (p: Place, i: number) => (\n <button\n key={p.id}\n role=\"option\"\n aria-selected={i === cursor}\n className={`place-option ${i === cursor ? 'cursor' : ''} ${p.id === value ? 'chosen' : ''} ${p.id === HERE ? 'here' : ''}`}\n onPointerDown={e => {\n e.preventDefault();\n pick(p.id);\n }}\n onMouseEnter={() => setCursor(i)}\n >\n {p.id === HERE ? <PersonStanding size={14} /> : <EntityIcon kind={p.o.kind} size={14} symbol={p.o.symbol} />}\n <span>\n {p.o.name}\n <small>{p.id === HERE ? `${strings.hereHint} · ${p.floor.split(' · ')[1] ?? ''}` : p.floor}</small>\n </span>\n </button>\n );\n // Rows of a browsed level, with a heading wherever the space changes: a room's doors under the\n // room, the rooms of a zone under the zone.\n const grouped = (rows: Place[]) => {\n const out: ReactNode[] = [];\n let group: string | undefined;\n rows.forEach((p, i) => {\n if (p.group !== group) {\n group = p.group;\n if (group)\n out.push(\n <p key={`g-${group}-${i}`} className=\"place-group\">\n {group}\n </p>,\n );\n }\n out.push(option(p, i));\n });\n return out;\n };\n return (\n <label className=\"field place-picker\" ref={wrap as never}>\n <span>{label}</span>\n <div className=\"place-input\">\n <Search size={13} />\n <input\n aria-label={label}\n placeholder={selected ? selected.o.name : placeholder}\n value={open ? query : (selected?.o.name ?? '')}\n onFocus={() => {\n setOpen(true);\n setQuery('');\n }}\n onBlur={() => setTimeout(() => setOpen(false), 120)}\n onChange={e => {\n setQuery(e.target.value);\n setOpen(true);\n }}\n onKeyDown={e => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setCursor(c => Math.min(c + 1, shown.length - 1));\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setCursor(c => Math.max(c - 1, 0));\n } else if (e.key === 'Enter') {\n e.preventDefault();\n if (shown[cursor]) pick(shown[cursor].id);\n } else if (e.key === 'Backspace' && !query && level !== undefined) {\n setLevel(undefined);\n } else if (e.key === 'Escape') {\n e.stopPropagation();\n setOpen(false);\n (e.target as HTMLInputElement).blur();\n }\n }}\n />\n {value && !open && (\n <button\n className=\"icon-button place-clear\"\n aria-label={`Clear ${label}`}\n onClick={e => {\n e.preventDefault();\n onPick(null);\n }}\n >\n <X size={13} />\n </button>\n )}\n </div>\n {open && (\n <div className=\"place-options\" role=\"listbox\" ref={list} onScroll={more}>\n {browsing && level === undefined ? (\n <>\n {first && first.id !== other && option(first, -1)}\n <p className=\"place-group\">{strings.levels}</p>\n {levels.map(l => (\n <button\n key={l.id ?? 'out'}\n className=\"place-level\"\n onPointerDown={e => {\n e.preventDefault();\n setLevel(l.id);\n }}\n >\n <Layers3 size={14} />\n {l.name}\n <small>\n {l.count} {strings.places}\n </small>\n </button>\n ))}\n </>\n ) : (\n <>\n {browsing && (\n <button\n className=\"place-back\"\n onPointerDown={e => {\n e.preventDefault();\n setLevel(undefined);\n }}\n >\n <ChevronLeft size={14} />\n {strings.allLevels}\n </button>\n )}\n {!browsing && first && first.id !== other && first.o.name.toLowerCase().includes(q) && option(first, -1)}\n {shown.length ? (\n browsing ? (\n grouped(shown)\n ) : (\n shown.map(option)\n )\n ) : (\n <p className=\"place-none\">No places match “{query}”.</p>\n )}\n {limit < matches.length && <p className=\"place-none\">{strings.more}</p>}\n </>\n )}\n </div>\n )}\n </label>\n );\n}\n"],"names":["NAV_KINDS","STEP_ICONS","MapPin","Footprints","DoorOpen","MoveUpRight","ArrowUpRight","Flag","NavigatePanel","project","here","from","to","route","playing","activeStep","editing","onFrom","onTo","onSwap","onClear","onPlay","onPause","onStep","onSeek","onClose","en","useStrings","t","places","levels","useMemo","options","byId","f","floorPhrase","level","items","o","a","b","parent","l","hereLabel","herePlace","HERE","proposed","useRef","useEffect","hasGraph","navNodes","floorsCrossed","s","picker","label","value","other","set","first","jsx","PlacePicker","jsxs","X","ArrowUpDown","Waypoints","Fragment","i","Icon","ChevronLeft","Pause","Play","ChevronRight","PAGE","onPick","placeholder","strings","query","setQuery","useState","open","setOpen","cursor","setCursor","setLevel","limit","setLimit","wrap","list","selected","p","q","browsing","matches","shown","away","e","_a","pick","id","more","el","n","option","PersonStanding","EntityIcon","grouped","rows","out","group","Search","c","Layers3"],"mappings":"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"}
@@ -0,0 +1,234 @@
1
+ import { jsxs as g, jsx as l } from "react/jsx-runtime";
2
+ import { useState as L, useRef as x, useMemo as he, useEffect as j } from "react";
3
+ import { LocateFixed as fe } from "lucide-react";
4
+ import { navNodes as pe, navEdges as ge } from "../model/navigation.js";
5
+ import { balanceGraph as me } from "./graphLayout.js";
6
+ const U = { walk: "#8393a9", door: "#c28332", stairs: "#2eaaa0", elevator: "#9777f1" }, se = ["#7367e6", "#2aabb3", "#d9a54b", "#ce6f91", "#5d9d6e", "#a67fc4"];
7
+ function we({ project: b, initialFloor: W, dark: q, onLocate: ie, onClose: K }) {
8
+ const [T, ce] = L(W === void 0 ? "all" : W ?? "outdoors"), [F, Q] = L(!0), [_, le] = L(""), [P, D] = L(""), [de, O] = L(!1), H = x(null), a = x({ x: 0, y: 0, scale: 1 }), M = x(() => {
9
+ }), J = x(() => {
10
+ }), w = x(0), V = x(P);
11
+ V.current = P;
12
+ const Z = x(F);
13
+ Z.current = F;
14
+ const N = x(!0), n = he(() => {
15
+ const e = pe(b).filter((s) => T === "all" || (s.floorId ?? "outdoors") === T), t = new Map(e.map((s, p) => [s.id, p])), r = ge(b).filter((s) => t.has(s.aId) && t.has(s.bId)), d = e.map((s, p) => {
16
+ const k = p * 2.399963, I = 15 * Math.sqrt(p + 1);
17
+ return { x: Math.cos(k) * I, y: Math.sin(k) * I, vx: 0, vy: 0 };
18
+ }), h = new Map(b.objects.map((s) => [s.id, s]));
19
+ return {
20
+ nodes: e,
21
+ edges: r,
22
+ points: d,
23
+ links: r.map((s) => [t.get(s.aId), t.get(s.bId)]),
24
+ objects: h
25
+ };
26
+ }, [b, T]), m = n.nodes.find((e) => e.id === P), ee = (e) => {
27
+ var r;
28
+ const t = n.nodes[e];
29
+ return ((r = n.objects.get(t.objectId ?? "")) == null ? void 0 : r.name) || t.id;
30
+ }, te = (e) => {
31
+ var t;
32
+ return e === null ? "Outdoor site" : ((t = b.floors.find((r) => r.id === e)) == null ? void 0 : t.name) ?? e;
33
+ }, ue = () => {
34
+ m && ie({
35
+ objectIds: m.objectId ? [m.objectId] : [],
36
+ floorId: m.floorId,
37
+ position: m.position
38
+ });
39
+ };
40
+ j(() => {
41
+ w.current = 0, O(!1), D(""), N.current = !0;
42
+ }, [n]), j(() => {
43
+ const e = H.current, t = e == null ? void 0 : e.getContext("2d");
44
+ if (!e || !t) return;
45
+ let r = 1, d = 1, h = 0, s = 0, p = !1;
46
+ const k = new Map(b.floors.map((o, c) => [o.id, se[c % se.length]])), I = () => {
47
+ const o = V.current, { x: c, y, scale: i } = a.current;
48
+ t.clearRect(0, 0, r, d), t.save(), t.translate(r / 2 + c, d / 2 + y), t.scale(i, i), n.edges.forEach((u, f) => {
49
+ const [C, S] = n.links[f], B = n.points[C], A = n.points[S], oe = u.aId === o || u.bId === o;
50
+ if (t.globalAlpha = o && !oe ? 0.18 : 0.65, t.strokeStyle = U[u.kind], t.lineWidth = (oe ? 2.5 : 1) / i, t.beginPath(), t.moveTo(B.x, B.y), t.lineTo(A.x, A.y), t.stroke(), u.directed) {
51
+ const R = Math.atan2(A.y - B.y, A.x - B.x), E = 7 / i, $ = A.x - Math.cos(R) * 7 / i, z = A.y - Math.sin(R) * 7 / i;
52
+ t.fillStyle = U[u.kind], t.beginPath(), t.moveTo($, z), t.lineTo($ - Math.cos(R - 0.5) * E, z - Math.sin(R - 0.5) * E), t.lineTo($ - Math.cos(R + 0.5) * E, z - Math.sin(R + 0.5) * E), t.closePath(), t.fill();
53
+ }
54
+ }), t.globalAlpha = 1, n.nodes.forEach((u, f) => {
55
+ const C = n.points[f], S = u.id === o;
56
+ t.beginPath(), t.arc(C.x, C.y, (S ? 7 : 4) / i, 0, Math.PI * 2), t.fillStyle = k.get(u.floorId ?? "") ?? "#8b94a4", t.fill(), S && (t.strokeStyle = q ? "#fff" : "#191c32", t.lineWidth = 2 / i, t.stroke()), (S || n.nodes.length < 65 && i > 0.45) && (t.font = `${12 / i}px system-ui`, t.fillStyle = q ? "#eeeefa" : "#24263a", t.fillText(ee(f).slice(0, 38), C.x + 9 / i, C.y - 7 / i));
57
+ }), t.restore();
58
+ }, Y = () => {
59
+ if (!n.points.length) return;
60
+ let o = 1 / 0, c = -1 / 0, y = 1 / 0, i = -1 / 0;
61
+ for (const f of n.points)
62
+ o = Math.min(o, f.x), c = Math.max(c, f.x), y = Math.min(y, f.y), i = Math.max(i, f.y);
63
+ const u = Math.max(
64
+ 0.05,
65
+ Math.min(3, (r - 90) / Math.max(1, c - o), (d - 90) / Math.max(1, i - y))
66
+ );
67
+ a.current = { x: -(o + c) * u / 2, y: -(y + i) * u / 2, scale: u }, I();
68
+ }, G = (o) => {
69
+ h = 0, !p && (Z.current && w.current < 360 ? (o - s > 30 && (me(n.points, n.links, w.current++), N.current && w.current % 20 === 0 ? Y() : I(), s = o), h = requestAnimationFrame(G)) : (I(), w.current >= 360 && O(!0)));
70
+ }, X = () => {
71
+ I(), !h && !p && (h = requestAnimationFrame(G));
72
+ };
73
+ M.current = X, J.current = Y;
74
+ const ae = new ResizeObserver(() => {
75
+ const o = e.getBoundingClientRect();
76
+ r = o.width, d = o.height;
77
+ const c = Math.min(2, window.devicePixelRatio || 1);
78
+ e.width = Math.round(r * c), e.height = Math.round(d * c), t.setTransform(c, 0, 0, c, 0, 0), Y(), X();
79
+ }), re = (o) => {
80
+ o.preventDefault(), N.current = !1;
81
+ const c = e.getBoundingClientRect(), y = o.clientX - c.left - c.width / 2, i = o.clientY - c.top - c.height / 2, u = a.current.scale, f = Math.max(0.03, Math.min(8, u * Math.exp(-o.deltaY * 1e-3)));
82
+ a.current = {
83
+ scale: f,
84
+ x: y - (y - a.current.x) * f / u,
85
+ y: i - (i - a.current.y) * f / u
86
+ }, X();
87
+ };
88
+ return e.addEventListener("wheel", re, { passive: !1 }), ae.observe(e), h = requestAnimationFrame(G), () => {
89
+ p = !0, cancelAnimationFrame(h), ae.disconnect(), e.removeEventListener("wheel", re);
90
+ };
91
+ }, [n, q, b]), j(() => M.current(), [F, P]);
92
+ const v = x(
93
+ null
94
+ ), ne = n.nodes.map((e, t) => ({ node: e, i: t, label: `${ee(t)} · ${te(e.floorId)}` })).filter((e) => e.label.toLowerCase().includes(_.toLowerCase())).slice(0, 100);
95
+ return /* @__PURE__ */ g("section", { className: "navigation-graph", "aria-label": "Navigation graph view", children: [
96
+ /* @__PURE__ */ g("div", { className: "navigation-graph-toolbar", children: [
97
+ /* @__PURE__ */ l("strong", { children: "Navigation graph" }),
98
+ /* @__PURE__ */ g("select", { "aria-label": "Graph floor", value: T, onChange: (e) => ce(e.target.value), children: [
99
+ /* @__PURE__ */ l("option", { value: "all", children: "All floors" }),
100
+ /* @__PURE__ */ l("option", { value: "outdoors", children: "Outdoor site" }),
101
+ b.floors.map((e) => /* @__PURE__ */ l("option", { value: e.id, children: te(e.id) }, e.id))
102
+ ] }),
103
+ /* @__PURE__ */ g("label", { children: [
104
+ /* @__PURE__ */ l("input", { type: "checkbox", checked: F, onChange: (e) => Q(e.target.checked) }),
105
+ "Auto balance"
106
+ ] }),
107
+ /* @__PURE__ */ l(
108
+ "button",
109
+ {
110
+ className: "button secondary small",
111
+ onClick: () => {
112
+ w.current = 0, O(!1), Q(!0), M.current();
113
+ },
114
+ children: "Rebalance"
115
+ }
116
+ ),
117
+ /* @__PURE__ */ l("button", { className: "button secondary small", onClick: () => J.current(), children: "Fit graph" }),
118
+ /* @__PURE__ */ l("button", { className: "button secondary small", onClick: K, children: "Back to map" })
119
+ ] }),
120
+ /* @__PURE__ */ g("div", { className: "navigation-graph-search", children: [
121
+ /* @__PURE__ */ l(
122
+ "input",
123
+ {
124
+ type: "search",
125
+ "aria-label": "Search graph nodes",
126
+ placeholder: "Find a room or transport node…",
127
+ value: _,
128
+ onChange: (e) => le(e.target.value)
129
+ }
130
+ ),
131
+ /* @__PURE__ */ g(
132
+ "select",
133
+ {
134
+ "aria-label": "Graph node",
135
+ value: P,
136
+ onChange: (e) => {
137
+ N.current = !1;
138
+ const t = n.nodes.findIndex((r) => r.id === e.target.value);
139
+ if (D(e.target.value), t >= 0) {
140
+ const r = n.points[t];
141
+ a.current.x = -r.x * a.current.scale, a.current.y = -r.y * a.current.scale, M.current();
142
+ }
143
+ },
144
+ children: [
145
+ /* @__PURE__ */ g("option", { value: "", children: [
146
+ "Select a node (",
147
+ n.nodes.length,
148
+ ")"
149
+ ] }),
150
+ m && !ne.some((e) => e.node.id === m.id) && /* @__PURE__ */ l("option", { value: m.id, children: m.id }),
151
+ ne.map((e) => /* @__PURE__ */ l("option", { value: e.node.id, children: e.label }, e.node.id))
152
+ ]
153
+ }
154
+ ),
155
+ /* @__PURE__ */ l(
156
+ "button",
157
+ {
158
+ className: "icon-button",
159
+ "aria-label": "Find node on map",
160
+ title: "Find on map",
161
+ disabled: !m,
162
+ onClick: ue,
163
+ children: /* @__PURE__ */ l(fe, { size: 16 })
164
+ }
165
+ )
166
+ ] }),
167
+ /* @__PURE__ */ g("div", { className: "navigation-graph-stage", children: [
168
+ /* @__PURE__ */ l(
169
+ "canvas",
170
+ {
171
+ ref: H,
172
+ role: "img",
173
+ "aria-label": `Navigation graph with ${n.nodes.length} nodes and ${n.edges.length} edges`,
174
+ tabIndex: 0,
175
+ onPointerDown: (e) => {
176
+ N.current = !1, e.currentTarget.setPointerCapture(e.pointerId);
177
+ const t = e.currentTarget.getBoundingClientRect(), r = e.clientX - t.left, d = e.clientY - t.top, h = (r - t.width / 2 - a.current.x) / a.current.scale, s = (d - t.height / 2 - a.current.y) / a.current.scale, p = n.points.findIndex((k) => Math.hypot(k.x - h, k.y - s) < 10 / a.current.scale);
178
+ v.current = { x: r, y: d, startX: r, startY: d, node: p, moved: !1 }, p >= 0 && (n.points[p].pinned = !0);
179
+ },
180
+ onPointerMove: (e) => {
181
+ const t = v.current;
182
+ if (!t) return;
183
+ const r = e.currentTarget.getBoundingClientRect(), d = e.clientX - r.left, h = e.clientY - r.top;
184
+ if (t.moved || (t.moved = Math.hypot(d - t.startX, h - t.startY) > 3), t.node >= 0) {
185
+ const s = n.points[t.node];
186
+ s.x += (d - t.x) / a.current.scale, s.y += (h - t.y) / a.current.scale;
187
+ } else
188
+ a.current.x += d - t.x, a.current.y += h - t.y;
189
+ t.x = d, t.y = h, M.current();
190
+ },
191
+ onPointerUp: () => {
192
+ const e = v.current;
193
+ v.current = null, e && e.node >= 0 && (n.points[e.node].pinned = !1, e.moved ? (w.current = 0, O(!1), M.current()) : D(n.nodes[e.node].id));
194
+ },
195
+ onPointerCancel: () => {
196
+ v.current && v.current.node >= 0 && (n.points[v.current.node].pinned = !1), v.current = null;
197
+ },
198
+ onKeyDown: (e) => {
199
+ if (e.stopPropagation(), N.current = !1, e.key === "+" || e.key === "=") a.current.scale = Math.min(8, a.current.scale * 1.2);
200
+ else if (e.key === "-") a.current.scale = Math.max(0.03, a.current.scale / 1.2);
201
+ else if (e.key === "ArrowLeft") a.current.x += 40;
202
+ else if (e.key === "ArrowRight") a.current.x -= 40;
203
+ else if (e.key === "ArrowUp") a.current.y += 40;
204
+ else if (e.key === "ArrowDown") a.current.y -= 40;
205
+ else if (e.key === "Escape") K();
206
+ else return;
207
+ e.preventDefault(), M.current();
208
+ }
209
+ }
210
+ ),
211
+ !n.nodes.length && /* @__PURE__ */ l("p", { className: "navigation-graph-empty", children: "No navigation nodes on this floor." })
212
+ ] }),
213
+ /* @__PURE__ */ g("div", { className: "navigation-graph-legend", children: [
214
+ Object.entries(U).map(([e, t]) => /* @__PURE__ */ g("span", { children: [
215
+ /* @__PURE__ */ l("i", { style: { background: t } }),
216
+ e === "stairs" ? "stairs / escalators" : e
217
+ ] }, e)),
218
+ /* @__PURE__ */ l("span", { children: "Arrows: one-way travel" }),
219
+ /* @__PURE__ */ g("span", { children: [
220
+ n.nodes.length,
221
+ " nodes · ",
222
+ n.edges.length,
223
+ " edges ·",
224
+ " ",
225
+ F ? de ? "Balanced" : "Balancing…" : "Layout paused"
226
+ ] })
227
+ ] }),
228
+ /* @__PURE__ */ l("p", { className: "navigation-graph-help", children: "Drag nodes to arrange · drag background to pan · scroll to zoom. Layout changes only this view." })
229
+ ] });
230
+ }
231
+ export {
232
+ we as NavigationGraph
233
+ };
234
+ //# sourceMappingURL=NavigationGraph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavigationGraph.js","sources":["../../../../src/components/NavigationGraph.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { LocateFixed } from 'lucide-react';\nimport { navEdges, navNodes } from '../model/navigation';\nimport type { ProjectDocument } from '../model/types';\nimport type { StructureTarget } from './StructureView';\nimport { balanceGraph, type GraphPoint } from './graphLayout';\n\nconst COLORS = { walk: '#8393a9', door: '#c28332', stairs: '#2eaaa0', elevator: '#9777f1' };\nconst FLOORS = ['#7367e6', '#2aabb3', '#d9a54b', '#ce6f91', '#5d9d6e', '#a67fc4'];\nexport interface NavigationGraphProps {\n project: ProjectDocument;\n initialFloor?: string | null;\n dark: boolean;\n onLocate: (target: StructureTarget) => void;\n onClose: () => void;\n}\nexport function NavigationGraph({ project, initialFloor, dark, onLocate, onClose }: NavigationGraphProps) {\n const [floor, setFloor] = useState(initialFloor === undefined ? 'all' : (initialFloor ?? 'outdoors'));\n const [auto, setAuto] = useState(true);\n const [query, setQuery] = useState('');\n const [selected, setSelected] = useState('');\n const [settled, setSettled] = useState(false);\n const canvas = useRef<HTMLCanvasElement>(null);\n const view = useRef({ x: 0, y: 0, scale: 1 });\n const redraw = useRef<() => void>(() => {});\n const fit = useRef<() => void>(() => {});\n const tick = useRef(0);\n const selectedRef = useRef(selected);\n selectedRef.current = selected;\n const autoRef = useRef(auto);\n autoRef.current = auto;\n const autoFit = useRef(true);\n const graph = useMemo(() => {\n const nodes = navNodes(project).filter(node => floor === 'all' || (node.floorId ?? 'outdoors') === floor);\n const byId = new Map(nodes.map((node, i) => [node.id, i]));\n const edges = navEdges(project).filter(edge => byId.has(edge.aId) && byId.has(edge.bId));\n const points: GraphPoint[] = nodes.map((_node, i) => {\n const angle = i * 2.399963,\n radius = 15 * Math.sqrt(i + 1);\n return { x: Math.cos(angle) * radius, y: Math.sin(angle) * radius, vx: 0, vy: 0 };\n });\n const objects = new Map(project.objects.map(object => [object.id, object]));\n return {\n nodes,\n edges,\n points,\n links: edges.map(edge => [byId.get(edge.aId)!, byId.get(edge.bId)!] as [number, number]),\n objects,\n };\n }, [project, floor]);\n const chosen = graph.nodes.find(node => node.id === selected);\n const nodeName = (index: number) => {\n const node = graph.nodes[index];\n return graph.objects.get(node.objectId ?? '')?.name || node.id;\n };\n const floorName = (id: string | null) =>\n id === null ? 'Outdoor site' : (project.floors.find(f => f.id === id)?.name ?? id);\n const locate = () => {\n if (chosen)\n onLocate({\n objectIds: chosen.objectId ? [chosen.objectId] : [],\n floorId: chosen.floorId,\n position: chosen.position,\n });\n };\n useEffect(() => {\n tick.current = 0;\n setSettled(false);\n setSelected('');\n autoFit.current = true;\n }, [graph]);\n useEffect(() => {\n const element = canvas.current,\n context = element?.getContext('2d');\n if (!element || !context) return;\n let width = 1,\n height = 1,\n frame = 0,\n lastFrame = 0,\n disposed = false;\n const colors = new Map(project.floors.map((f, i) => [f.id, FLOORS[i % FLOORS.length]]));\n const draw = () => {\n const selected = selectedRef.current;\n const { x, y, scale } = view.current;\n context.clearRect(0, 0, width, height);\n context.save();\n context.translate(width / 2 + x, height / 2 + y);\n context.scale(scale, scale);\n graph.edges.forEach((edge, i) => {\n const [a, b] = graph.links[i],\n p = graph.points[a],\n q = graph.points[b];\n const highlight = edge.aId === selected || edge.bId === selected;\n context.globalAlpha = selected && !highlight ? 0.18 : 0.65;\n context.strokeStyle = COLORS[edge.kind];\n context.lineWidth = (highlight ? 2.5 : 1) / scale;\n context.beginPath();\n context.moveTo(p.x, p.y);\n context.lineTo(q.x, q.y);\n context.stroke();\n if (edge.directed) {\n const angle = Math.atan2(q.y - p.y, q.x - p.x),\n length = 7 / scale;\n const ax = q.x - (Math.cos(angle) * 7) / scale,\n ay = q.y - (Math.sin(angle) * 7) / scale;\n context.fillStyle = COLORS[edge.kind];\n context.beginPath();\n context.moveTo(ax, ay);\n context.lineTo(ax - Math.cos(angle - 0.5) * length, ay - Math.sin(angle - 0.5) * length);\n context.lineTo(ax - Math.cos(angle + 0.5) * length, ay - Math.sin(angle + 0.5) * length);\n context.closePath();\n context.fill();\n }\n });\n context.globalAlpha = 1;\n graph.nodes.forEach((node, i) => {\n const p = graph.points[i],\n active = node.id === selected;\n context.beginPath();\n context.arc(p.x, p.y, (active ? 7 : 4) / scale, 0, Math.PI * 2);\n context.fillStyle = colors.get(node.floorId ?? '') ?? '#8b94a4';\n context.fill();\n if (active) {\n context.strokeStyle = dark ? '#fff' : '#191c32';\n context.lineWidth = 2 / scale;\n context.stroke();\n }\n if (active || (graph.nodes.length < 65 && scale > 0.45)) {\n context.font = `${12 / scale}px system-ui`;\n context.fillStyle = dark ? '#eeeefa' : '#24263a';\n context.fillText(nodeName(i).slice(0, 38), p.x + 9 / scale, p.y - 7 / scale);\n }\n });\n context.restore();\n };\n const fitGraph = () => {\n if (!graph.points.length) return;\n let left = Infinity,\n right = -Infinity,\n top = Infinity,\n bottom = -Infinity;\n for (const p of graph.points) {\n left = Math.min(left, p.x);\n right = Math.max(right, p.x);\n top = Math.min(top, p.y);\n bottom = Math.max(bottom, p.y);\n }\n const scale = Math.max(\n 0.05,\n Math.min(3, (width - 90) / Math.max(1, right - left), (height - 90) / Math.max(1, bottom - top)),\n );\n view.current = { x: (-(left + right) * scale) / 2, y: (-(top + bottom) * scale) / 2, scale };\n draw();\n };\n const animate = (time: number) => {\n frame = 0;\n if (disposed) return;\n if (autoRef.current && tick.current < 360) {\n if (time - lastFrame > 30) {\n balanceGraph(graph.points, graph.links, tick.current++);\n if (autoFit.current && tick.current % 20 === 0) fitGraph();\n else draw();\n lastFrame = time;\n }\n frame = requestAnimationFrame(animate);\n } else {\n draw();\n if (tick.current >= 360) setSettled(true);\n }\n };\n const wake = () => {\n draw();\n if (!frame && !disposed) frame = requestAnimationFrame(animate);\n };\n redraw.current = wake;\n fit.current = fitGraph;\n const resize = new ResizeObserver(() => {\n const box = element.getBoundingClientRect();\n width = box.width;\n height = box.height;\n const ratio = Math.min(2, window.devicePixelRatio || 1);\n element.width = Math.round(width * ratio);\n element.height = Math.round(height * ratio);\n context.setTransform(ratio, 0, 0, ratio, 0, 0);\n fitGraph();\n wake();\n });\n const wheel = (event: WheelEvent) => {\n event.preventDefault();\n autoFit.current = false;\n const box = element.getBoundingClientRect(),\n px = event.clientX - box.left - box.width / 2,\n py = event.clientY - box.top - box.height / 2;\n const old = view.current.scale,\n scale = Math.max(0.03, Math.min(8, old * Math.exp(-event.deltaY * 0.001)));\n view.current = {\n scale,\n x: px - ((px - view.current.x) * scale) / old,\n y: py - ((py - view.current.y) * scale) / old,\n };\n wake();\n };\n element.addEventListener('wheel', wheel, { passive: false });\n resize.observe(element);\n frame = requestAnimationFrame(animate);\n return () => {\n disposed = true;\n cancelAnimationFrame(frame);\n resize.disconnect();\n element.removeEventListener('wheel', wheel);\n };\n }, [graph, dark, project]);\n useEffect(() => redraw.current(), [auto, selected]);\n const drag = useRef<{ x: number; y: number; startX: number; startY: number; node: number; moved: boolean } | null>(\n null,\n );\n const controls = graph.nodes\n .map((node, i) => ({ node, i, label: `${nodeName(i)} · ${floorName(node.floorId)}` }))\n .filter(row => row.label.toLowerCase().includes(query.toLowerCase()))\n .slice(0, 100);\n return (\n <section className=\"navigation-graph\" aria-label=\"Navigation graph view\">\n <div className=\"navigation-graph-toolbar\">\n <strong>Navigation graph</strong>\n <select aria-label=\"Graph floor\" value={floor} onChange={event => setFloor(event.target.value)}>\n <option value=\"all\">All floors</option>\n <option value=\"outdoors\">Outdoor site</option>\n {project.floors.map(f => (\n <option key={f.id} value={f.id}>\n {floorName(f.id)}\n </option>\n ))}\n </select>\n <label>\n <input type=\"checkbox\" checked={auto} onChange={event => setAuto(event.target.checked)} />\n Auto balance\n </label>\n <button\n className=\"button secondary small\"\n onClick={() => {\n tick.current = 0;\n setSettled(false);\n setAuto(true);\n redraw.current();\n }}\n >\n Rebalance\n </button>\n <button className=\"button secondary small\" onClick={() => fit.current()}>\n Fit graph\n </button>\n <button className=\"button secondary small\" onClick={onClose}>\n Back to map\n </button>\n </div>\n <div className=\"navigation-graph-search\">\n <input\n type=\"search\"\n aria-label=\"Search graph nodes\"\n placeholder=\"Find a room or transport node…\"\n value={query}\n onChange={event => setQuery(event.target.value)}\n />\n <select\n aria-label=\"Graph node\"\n value={selected}\n onChange={event => {\n autoFit.current = false;\n const i = graph.nodes.findIndex(n => n.id === event.target.value);\n setSelected(event.target.value);\n if (i >= 0) {\n const p = graph.points[i];\n view.current.x = -p.x * view.current.scale;\n view.current.y = -p.y * view.current.scale;\n redraw.current();\n }\n }}\n >\n <option value=\"\">Select a node ({graph.nodes.length})</option>\n {chosen && !controls.some(row => row.node.id === chosen.id) && <option value={chosen.id}>{chosen.id}</option>}\n {controls.map(row => (\n <option value={row.node.id} key={row.node.id}>\n {row.label}\n </option>\n ))}\n </select>\n <button\n className=\"icon-button\"\n aria-label=\"Find node on map\"\n title=\"Find on map\"\n disabled={!chosen}\n onClick={locate}\n >\n <LocateFixed size={16} />\n </button>\n </div>\n <div className=\"navigation-graph-stage\">\n <canvas\n ref={canvas}\n role=\"img\"\n aria-label={`Navigation graph with ${graph.nodes.length} nodes and ${graph.edges.length} edges`}\n tabIndex={0}\n onPointerDown={event => {\n autoFit.current = false;\n event.currentTarget.setPointerCapture(event.pointerId);\n const box = event.currentTarget.getBoundingClientRect(),\n x = event.clientX - box.left,\n y = event.clientY - box.top;\n const gx = (x - box.width / 2 - view.current.x) / view.current.scale,\n gy = (y - box.height / 2 - view.current.y) / view.current.scale;\n const node = graph.points.findIndex(p => Math.hypot(p.x - gx, p.y - gy) < 10 / view.current.scale);\n drag.current = { x, y, startX: x, startY: y, node, moved: false };\n if (node >= 0) graph.points[node].pinned = true;\n }}\n onPointerMove={event => {\n const current = drag.current;\n if (!current) return;\n const box = event.currentTarget.getBoundingClientRect(),\n x = event.clientX - box.left,\n y = event.clientY - box.top;\n current.moved ||= Math.hypot(x - current.startX, y - current.startY) > 3;\n if (current.node >= 0) {\n const p = graph.points[current.node];\n p.x += (x - current.x) / view.current.scale;\n p.y += (y - current.y) / view.current.scale;\n } else {\n view.current.x += x - current.x;\n view.current.y += y - current.y;\n }\n current.x = x;\n current.y = y;\n redraw.current();\n }}\n onPointerUp={() => {\n const current = drag.current;\n drag.current = null;\n if (current && current.node >= 0) {\n graph.points[current.node].pinned = false;\n if (!current.moved) setSelected(graph.nodes[current.node].id);\n else {\n tick.current = 0;\n setSettled(false);\n redraw.current();\n }\n }\n }}\n onPointerCancel={() => {\n if (drag.current && drag.current.node >= 0) graph.points[drag.current.node].pinned = false;\n drag.current = null;\n }}\n onKeyDown={event => {\n event.stopPropagation();\n autoFit.current = false;\n if (event.key === '+' || event.key === '=') view.current.scale = Math.min(8, view.current.scale * 1.2);\n else if (event.key === '-') view.current.scale = Math.max(0.03, view.current.scale / 1.2);\n else if (event.key === 'ArrowLeft') view.current.x += 40;\n else if (event.key === 'ArrowRight') view.current.x -= 40;\n else if (event.key === 'ArrowUp') view.current.y += 40;\n else if (event.key === 'ArrowDown') view.current.y -= 40;\n else if (event.key === 'Escape') onClose();\n else return;\n event.preventDefault();\n redraw.current();\n }}\n />\n {!graph.nodes.length && <p className=\"navigation-graph-empty\">No navigation nodes on this floor.</p>}\n </div>\n <div className=\"navigation-graph-legend\">\n {Object.entries(COLORS).map(([kind, color]) => (\n <span key={kind}>\n <i style={{ background: color }} />\n {kind === 'stairs' ? 'stairs / escalators' : kind}\n </span>\n ))}\n <span>Arrows: one-way travel</span>\n <span>\n {graph.nodes.length} nodes · {graph.edges.length} edges ·{' '}\n {auto ? (settled ? 'Balanced' : 'Balancing…') : 'Layout paused'}\n </span>\n </div>\n <p className=\"navigation-graph-help\">\n Drag nodes to arrange · drag background to pan · scroll to zoom. Layout changes only this view.\n </p>\n </section>\n );\n}\n"],"names":["COLORS","FLOORS","NavigationGraph","project","initialFloor","dark","onLocate","onClose","floor","setFloor","useState","auto","setAuto","query","setQuery","selected","setSelected","settled","setSettled","canvas","useRef","view","redraw","fit","tick","selectedRef","autoRef","autoFit","graph","useMemo","nodes","navNodes","node","byId","i","edges","navEdges","edge","points","_node","angle","radius","objects","object","chosen","nodeName","index","_a","floorName","id","f","locate","useEffect","element","context","width","height","frame","lastFrame","disposed","colors","draw","x","scale","a","b","p","q","highlight","length","ax","ay","active","fitGraph","left","right","top","bottom","animate","time","balanceGraph","wake","resize","box","ratio","wheel","event","px","py","old","drag","controls","row","jsxs","jsx","n","LocateFixed","y","gx","gy","current","kind","color"],"mappings":"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"}