@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,286 @@
1
+ import { jsx as e, jsxs as a, Fragment as A } from "react/jsx-runtime";
2
+ import { lazy as T, Suspense as B, useState as H, useMemo as V } from "react";
3
+ import { isSpace as W } from "../model/types.js";
4
+ import { zoneSpaces as D, addZone as j, perimeter as G, captive as J, setZoneMembers as K, removeZone as Q, entryInto as L } from "../model/ontology.js";
5
+ import { refreshPortals as X } from "../model/inference.js";
6
+ import { Plus as Y, RefreshCw as _, Shapes as q, ChevronDown as E, ChevronRight as ee, LocateFixed as R, Trash2 as ne, DoorOpen as te, MoveHorizontal as se, Layers as ae } from "lucide-react";
7
+ const ie = T(
8
+ () => import("./StructureExplorer.js").then((s) => ({ default: s.StructureExplorer }))
9
+ ), le = "Outdoor site";
10
+ function re(s, p) {
11
+ const g = L(s, p) !== null, N = L(s, s.a === p ? s.b : s.a) !== null;
12
+ return g && N ? "both ways" : g ? "in only" : N ? "out only" : "sealed";
13
+ }
14
+ function be(s) {
15
+ return /* @__PURE__ */ e(
16
+ B,
17
+ {
18
+ fallback: /* @__PURE__ */ e("p", { className: "structure-empty", role: "status", children: "Loading structure…" }),
19
+ children: /* @__PURE__ */ e(ie, { ...s, renderZones: (p) => /* @__PURE__ */ e(ce, { ...s, ...p }) })
20
+ }
21
+ );
22
+ }
23
+ function ce({
24
+ project: s,
25
+ selected: p,
26
+ onSelect: g,
27
+ onFloorChange: N,
28
+ onEdit: h,
29
+ onLocate: y,
30
+ query: $,
31
+ floor: S
32
+ }) {
33
+ var F;
34
+ const [P, U] = H(null), c = p && s.objects.find((r) => r.id === p && W(r.kind)), I = V(() => {
35
+ const r = new Map(s.objects.map((i) => [i.id, i])), u = new Map(s.floors.map((i) => [i.id, i.name])), t = s.zones ?? [], m = /* @__PURE__ */ new Map();
36
+ for (const i of t) {
37
+ const b = D(s, i).map((o) => r.get(o)).filter(Boolean), z = [i.name, i.id, i.purpose, ...b.map((o) => o == null ? void 0 : o.name)].join(" ").toLowerCase();
38
+ if ($.trim() && !z.includes($.trim().toLowerCase()) || S !== "all" && !b.some((o) => {
39
+ var n;
40
+ return ((o == null ? void 0 : o.floorId) ?? "outdoors") === S || ((n = o == null ? void 0 : o.servedFloorIds) == null ? void 0 : n.includes(S));
41
+ }))
42
+ continue;
43
+ const f = i.purpose ?? "other", k = m.get(f);
44
+ k ? k.push(i) : m.set(f, [i]);
45
+ }
46
+ return { byId: r, floors: u, groups: m };
47
+ }, [s, $, S]), w = (r) => {
48
+ var u;
49
+ return ((u = I.byId.get(r)) == null ? void 0 : u.name) ?? "Unknown";
50
+ }, x = (r) => {
51
+ var t;
52
+ const u = ((t = I.byId.get(r)) == null ? void 0 : t.floorId) ?? null;
53
+ return { floorId: u, label: u === null ? le : I.floors.get(u) ?? "Unknown level" };
54
+ }, Z = (r) => {
55
+ g == null || g(r), N == null || N(x(r).floorId);
56
+ }, M = h ? /* @__PURE__ */ a("div", { className: "structure-actions", children: [
57
+ /* @__PURE__ */ a(
58
+ "button",
59
+ {
60
+ type: "button",
61
+ className: "text-button",
62
+ disabled: !c,
63
+ title: c ? `New zone containing ${c.name}` : "Select an area on the plan first",
64
+ onClick: () => {
65
+ c && h((r) => j(r, `${c.name} zone`, [c.id], "security"));
66
+ },
67
+ children: [
68
+ /* @__PURE__ */ e(Y, { size: 13 }),
69
+ " New zone"
70
+ ]
71
+ }
72
+ ),
73
+ /* @__PURE__ */ a(
74
+ "button",
75
+ {
76
+ type: "button",
77
+ className: "text-button",
78
+ title: "Re-read portals from the plan, keeping any you have edited",
79
+ onClick: () => h((r) => X(r)),
80
+ children: [
81
+ /* @__PURE__ */ e(_, { size: 13 }),
82
+ " Re-read portals"
83
+ ]
84
+ }
85
+ )
86
+ ] }) : null;
87
+ return I.groups.size ? /* @__PURE__ */ a("div", { className: "structure-zone-list", children: [
88
+ M,
89
+ /* @__PURE__ */ e("div", { className: "inspector-scroll structure-scroll", children: [...I.groups].map(([r, u]) => /* @__PURE__ */ a("section", { className: "structure-group", children: [
90
+ /* @__PURE__ */ e("h3", { children: r }),
91
+ u.map((t) => {
92
+ var k, o;
93
+ const m = P === t.id, i = D(s, t), b = G(s, t), z = m ? J(s, t) : [], f = new Set(i);
94
+ return /* @__PURE__ */ a("div", { className: `structure-zone ${m ? "open" : ""}`, children: [
95
+ /* @__PURE__ */ a("div", { className: "structure-zone-toolbar", children: [
96
+ /* @__PURE__ */ a(
97
+ "button",
98
+ {
99
+ type: "button",
100
+ className: "structure-zone-head",
101
+ "aria-expanded": m,
102
+ onClick: () => U(m ? null : t.id),
103
+ children: [
104
+ m ? /* @__PURE__ */ e(E, { size: 14 }) : /* @__PURE__ */ e(ee, { size: 14 }),
105
+ /* @__PURE__ */ e("strong", { children: t.name }),
106
+ /* @__PURE__ */ a("span", { className: "structure-counts", children: [
107
+ i.length,
108
+ " ",
109
+ i.length === 1 ? "space" : "spaces",
110
+ b.length > 0 && ` · ${b.length} in`,
111
+ t.connects && ` · ${{ all: "lift", adjacent: "stairs", up: "escalator up", down: "escalator down" }[t.connects]}`
112
+ ] })
113
+ ]
114
+ }
115
+ ),
116
+ /* @__PURE__ */ e(
117
+ "button",
118
+ {
119
+ className: "structure-locate",
120
+ "aria-label": `Find ${t.name} on map`,
121
+ title: "Find on map",
122
+ disabled: !i.length,
123
+ onClick: () => y ? y({ objectIds: i }) : Z(i[0]),
124
+ children: /* @__PURE__ */ e(R, { size: 13 })
125
+ }
126
+ )
127
+ ] }),
128
+ m && /* @__PURE__ */ a("div", { className: "structure-detail", children: [
129
+ h && /* @__PURE__ */ a("div", { className: "structure-edit", children: [
130
+ /* @__PURE__ */ e(
131
+ "input",
132
+ {
133
+ "aria-label": "Zone name",
134
+ value: t.name,
135
+ onChange: (n) => {
136
+ const l = n.target.value;
137
+ h((d) => {
138
+ var C;
139
+ const v = (C = d.zones) == null ? void 0 : C.find((O) => O.id === t.id);
140
+ v && (v.name = l);
141
+ });
142
+ }
143
+ }
144
+ ),
145
+ /* @__PURE__ */ a(
146
+ "select",
147
+ {
148
+ "aria-label": "Connects",
149
+ value: t.connects ?? "",
150
+ onChange: (n) => {
151
+ const l = n.target.value;
152
+ h((d) => {
153
+ var C;
154
+ const v = (C = d.zones) == null ? void 0 : C.find((O) => O.id === t.id);
155
+ v && (v.connects = l || void 0);
156
+ });
157
+ },
158
+ children: [
159
+ /* @__PURE__ */ e("option", { value: "", children: "No implied route" }),
160
+ /* @__PURE__ */ e("option", { value: "all", children: "Lift · any landing to any" }),
161
+ /* @__PURE__ */ e("option", { value: "adjacent", children: "Stair · level by level" }),
162
+ /* @__PURE__ */ e("option", { value: "up", children: "Escalator · up only" }),
163
+ /* @__PURE__ */ e("option", { value: "down", children: "Escalator · down only" })
164
+ ]
165
+ }
166
+ ),
167
+ /* @__PURE__ */ e(
168
+ "button",
169
+ {
170
+ type: "button",
171
+ className: "text-button",
172
+ disabled: !c,
173
+ title: c ? f.has(c.id) ? `Remove ${c.name}` : `Add ${c.name}` : "Select an area on the plan first",
174
+ onClick: () => {
175
+ if (!c) return;
176
+ const n = !f.has(c.id);
177
+ h((l) => K(l, t.id, [c.id], n));
178
+ },
179
+ children: c && f.has(c.id) ? "Remove selected" : "Add selected"
180
+ }
181
+ ),
182
+ /* @__PURE__ */ e(
183
+ "button",
184
+ {
185
+ type: "button",
186
+ className: "icon-button",
187
+ "aria-label": `Delete ${t.name}`,
188
+ onClick: () => h((n) => Q(n, t.id)),
189
+ children: /* @__PURE__ */ e(ne, { size: 14 })
190
+ }
191
+ )
192
+ ] }),
193
+ b.length > 0 && /* @__PURE__ */ a(A, { children: [
194
+ /* @__PURE__ */ a("h4", { children: [
195
+ /* @__PURE__ */ e(te, { size: 12 }),
196
+ " Ways in"
197
+ ] }),
198
+ /* @__PURE__ */ e("ul", { children: b.map((n) => {
199
+ const l = f.has(n.a) ? n.a : n.b, d = l === n.a ? n.b : n.a;
200
+ return /* @__PURE__ */ a("li", { className: "structure-member", children: [
201
+ /* @__PURE__ */ a("span", { className: "structure-member-title", children: [
202
+ /* @__PURE__ */ e("span", { className: "structure-from", children: w(d) }),
203
+ /* @__PURE__ */ e(se, { size: 11 }),
204
+ /* @__PURE__ */ e("span", { children: w(l) }),
205
+ /* @__PURE__ */ e("em", { children: re(n, l) })
206
+ ] }),
207
+ /* @__PURE__ */ e(
208
+ "button",
209
+ {
210
+ type: "button",
211
+ className: "structure-locate",
212
+ title: "Find on map",
213
+ "aria-label": `Find passage from ${w(d)} to ${w(l)} on map`,
214
+ onClick: () => y ? y({
215
+ objectIds: n.openingId ? [n.openingId] : [l, d],
216
+ floorId: x(d).floorId
217
+ }) : Z(n.openingId ?? l),
218
+ children: /* @__PURE__ */ e(R, { size: 13 })
219
+ }
220
+ )
221
+ ] }, n.id);
222
+ }) })
223
+ ] }),
224
+ !!((k = t.childZoneIds) != null && k.length) && /* @__PURE__ */ a("div", { className: "structure-note", children: [
225
+ "Contains zones:",
226
+ " ",
227
+ t.childZoneIds.map((n) => {
228
+ var l, d;
229
+ return ((d = (l = s.zones) == null ? void 0 : l.find((v) => v.id === n)) == null ? void 0 : d.name) ?? n;
230
+ }).join(", ")
231
+ ] }),
232
+ ((o = s.zones) == null ? void 0 : o.some((n) => {
233
+ var l;
234
+ return (l = n.childZoneIds) == null ? void 0 : l.includes(t.id);
235
+ })) && /* @__PURE__ */ a("div", { className: "structure-note", children: [
236
+ "Part of:",
237
+ " ",
238
+ s.zones.filter((n) => {
239
+ var l;
240
+ return (l = n.childZoneIds) == null ? void 0 : l.includes(t.id);
241
+ }).map((n) => n.name).join(", ")
242
+ ] }),
243
+ /* @__PURE__ */ a("h4", { children: [
244
+ /* @__PURE__ */ e(ae, { size: 12 }),
245
+ " Spaces"
246
+ ] }),
247
+ /* @__PURE__ */ e("ul", { children: i.map((n) => /* @__PURE__ */ a("li", { className: "structure-member", children: [
248
+ /* @__PURE__ */ a("span", { className: "structure-member-title", children: [
249
+ /* @__PURE__ */ e("span", { children: w(n) }),
250
+ /* @__PURE__ */ e("em", { children: x(n).label })
251
+ ] }),
252
+ /* @__PURE__ */ e(
253
+ "button",
254
+ {
255
+ type: "button",
256
+ className: "structure-locate",
257
+ title: "Find on map",
258
+ "aria-label": `Find ${w(n)} on map`,
259
+ onClick: () => y ? y({ objectIds: [n] }) : Z(n),
260
+ children: /* @__PURE__ */ e(R, { size: 13 })
261
+ }
262
+ )
263
+ ] }, n)) }),
264
+ z.length > 0 && /* @__PURE__ */ a("p", { className: "structure-note", children: [
265
+ z.length,
266
+ " portal",
267
+ z.length > 1 ? "s" : "",
268
+ " inside this zone — crossing one does not leave it."
269
+ ] })
270
+ ] })
271
+ ] }, t.id);
272
+ })
273
+ ] }, r)) })
274
+ ] }) : /* @__PURE__ */ a("div", { className: "structure-zone-list", children: [
275
+ M,
276
+ /* @__PURE__ */ a("div", { className: "structure-empty", children: [
277
+ /* @__PURE__ */ e(q, { size: 22 }),
278
+ /* @__PURE__ */ e("strong", { children: (F = s.zones) != null && F.length ? "No zones match these filters" : "No semantic zones yet" }),
279
+ /* @__PURE__ */ e("p", { children: "Spaces and portals are available in their own tabs. Select a space on the plan to create a named zone." })
280
+ ] })
281
+ ] });
282
+ }
283
+ export {
284
+ be as StructureView
285
+ };
286
+ //# sourceMappingURL=StructureView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StructureView.js","sources":["../../../../src/components/StructureView.tsx"],"sourcesContent":["// Browsing the building as a structure rather than a picture.\n//\n// A map is the wrong instrument for most of what the ontology says. A zone spanning eleven floors, a\n// lift shaft, \"everywhere a contractor may go\" — none of those have a shape, and drawing them on a\n// plan either lies about them or hides them. This view reads the same document as a list of what\n// groups with what and what opens onto what, which is the form those questions actually take.\nimport { lazy, Suspense, useMemo, useState } from 'react';\nimport { isSpace, type Point, type Portal, type ProjectDocument, type SiteObject, type Zone } from '../model/types';\nimport { addZone, captive, entryInto, perimeter, removeZone, setZoneMembers, zoneSpaces } from '../model/ontology';\nimport { refreshPortals } from '../model/inference';\nconst StructureExplorer = lazy(() =>\n import('./StructureExplorer').then(module => ({ default: module.StructureExplorer })),\n);\nimport {\n ChevronDown,\n ChevronRight,\n DoorOpen,\n Layers,\n LocateFixed,\n MoveHorizontal,\n Plus,\n RefreshCw,\n Shapes,\n Trash2,\n} from 'lucide-react';\n\nexport interface StructureViewProps {\n project: ProjectDocument;\n selected?: string | null;\n /** Fires when a space or opening is picked, so a host can follow along on the plan. */\n onSelect?: (id: string) => void;\n /** Fires alongside onSelect when the pick lives on a particular floor. */\n onFloorChange?: (floorId: string | null) => void;\n onLocate?: (target: StructureTarget) => void;\n onGraphView?: (floorId?: string | null) => void;\n /** Omit to keep the panel read-only. Given, the panel can author zones: the host applies the\n * mutation to a draft and owns undo, exactly as the map tools do. */\n onEdit?: (change: (draft: ProjectDocument) => void) => void;\n}\nexport interface StructureTarget {\n objectIds?: string[];\n floorId?: string | null;\n position?: Point;\n}\n\nconst OUTDOORS = 'Outdoor site';\n/** Direction, said the way a person would: which way this portal lets you go, relative to a zone. */\nfunction passageLabel(portal: Portal, insideId: string): string {\n const inward = entryInto(portal, insideId) !== null;\n const outward = entryInto(portal, portal.a === insideId ? portal.b : portal.a) !== null;\n if (inward && outward) return 'both ways';\n if (inward) return 'in only';\n if (outward) return 'out only';\n return 'sealed';\n}\n\nexport function StructureView(props: StructureViewProps) {\n return (\n <Suspense\n fallback={\n <p className=\"structure-empty\" role=\"status\">\n Loading structure…\n </p>\n }\n >\n <StructureExplorer {...props} renderZones={filter => <ZoneList {...props} {...filter} />} />\n </Suspense>\n );\n}\n\nfunction ZoneList({\n project,\n selected,\n onSelect,\n onFloorChange,\n onEdit,\n onLocate,\n query,\n floor,\n}: StructureViewProps & { query: string; floor: string }) {\n const [open, setOpen] = useState<string | null>(null);\n const selectedSpace = selected && project.objects.find(o => o.id === selected && isSpace(o.kind));\n const model = useMemo(() => {\n const byId = new Map(project.objects.map(o => [o.id, o]));\n const floors = new Map(project.floors.map(f => [f.id, f.name]));\n const zones = project.zones ?? [];\n const groups = new Map<string, Zone[]>();\n for (const z of zones) {\n const members = zoneSpaces(project, z)\n .map(id => byId.get(id))\n .filter(Boolean);\n const text = [z.name, z.id, z.purpose, ...members.map(o => o?.name)].join(' ').toLowerCase();\n if (query.trim() && !text.includes(query.trim().toLowerCase())) continue;\n if (\n floor !== 'all' &&\n !members.some(o => (o?.floorId ?? 'outdoors') === floor || o?.servedFloorIds?.includes(floor))\n )\n continue;\n const key = z.purpose ?? 'other';\n const list = groups.get(key);\n if (list) list.push(z);\n else groups.set(key, [z]);\n }\n return { byId, floors, groups };\n }, [project, query, floor]);\n\n const name = (id: string) => model.byId.get(id)?.name ?? 'Unknown';\n const floorOf = (id: string) => {\n const floorId = model.byId.get(id)?.floorId ?? null;\n return { floorId, label: floorId === null ? OUTDOORS : (model.floors.get(floorId) ?? 'Unknown level') };\n };\n const pick = (id: string) => {\n onSelect?.(id);\n onFloorChange?.(floorOf(id).floorId);\n };\n\n const actions = onEdit ? (\n <div className=\"structure-actions\">\n <button\n type=\"button\"\n className=\"text-button\"\n disabled={!selectedSpace}\n title={selectedSpace ? `New zone containing ${selectedSpace.name}` : 'Select an area on the plan first'}\n onClick={() => {\n if (!selectedSpace) return;\n onEdit(d => addZone(d, `${selectedSpace.name} zone`, [selectedSpace.id], 'security'));\n }}\n >\n <Plus size={13} /> New zone\n </button>\n <button\n type=\"button\"\n className=\"text-button\"\n title=\"Re-read portals from the plan, keeping any you have edited\"\n onClick={() => onEdit(d => refreshPortals(d))}\n >\n <RefreshCw size={13} /> Re-read portals\n </button>\n </div>\n ) : null;\n\n if (!model.groups.size)\n return (\n <div className=\"structure-zone-list\">\n {actions}\n <div className=\"structure-empty\">\n <Shapes size={22} />\n <strong>{project.zones?.length ? 'No zones match these filters' : 'No semantic zones yet'}</strong>\n <p>Spaces and portals are available in their own tabs. Select a space on the plan to create a named zone.</p>\n </div>\n </div>\n );\n\n return (\n <div className=\"structure-zone-list\">\n {actions}\n <div className=\"inspector-scroll structure-scroll\">\n {[...model.groups].map(([purpose, zones]) => (\n <section key={purpose} className=\"structure-group\">\n <h3>{purpose}</h3>\n {zones.map(zone => {\n const isOpen = open === zone.id;\n const members = zoneSpaces(project, zone);\n const ways = perimeter(project, zone);\n const inner = isOpen ? captive(project, zone) : [];\n const inside = new Set(members);\n return (\n <div key={zone.id} className={`structure-zone ${isOpen ? 'open' : ''}`}>\n <div className=\"structure-zone-toolbar\">\n <button\n type=\"button\"\n className=\"structure-zone-head\"\n aria-expanded={isOpen}\n onClick={() => setOpen(isOpen ? null : zone.id)}\n >\n {isOpen ? <ChevronDown size={14} /> : <ChevronRight size={14} />}\n <strong>{zone.name}</strong>\n <span className=\"structure-counts\">\n {members.length} {members.length === 1 ? 'space' : 'spaces'}\n {ways.length > 0 && ` · ${ways.length} in`}\n {zone.connects &&\n ` · ${{ all: 'lift', adjacent: 'stairs', up: 'escalator up', down: 'escalator down' }[zone.connects]}`}\n </span>\n </button>\n <button\n className=\"structure-locate\"\n aria-label={`Find ${zone.name} on map`}\n title=\"Find on map\"\n disabled={!members.length}\n onClick={() => (onLocate ? onLocate({ objectIds: members }) : pick(members[0]))}\n >\n <LocateFixed size={13} />\n </button>\n </div>\n {isOpen && (\n <div className=\"structure-detail\">\n {onEdit && (\n <div className=\"structure-edit\">\n <input\n aria-label=\"Zone name\"\n value={zone.name}\n onChange={e => {\n const name = e.target.value;\n onEdit(d => {\n const z = d.zones?.find(x => x.id === zone.id);\n if (z) z.name = name;\n });\n }}\n />\n <select\n aria-label=\"Connects\"\n value={zone.connects ?? ''}\n onChange={e => {\n const v = e.target.value;\n onEdit(d => {\n const z = d.zones?.find(x => x.id === zone.id);\n if (z) z.connects = (v || undefined) as Zone['connects'];\n });\n }}\n >\n <option value=\"\">No implied route</option>\n <option value=\"all\">Lift · any landing to any</option>\n <option value=\"adjacent\">Stair · level by level</option>\n <option value=\"up\">Escalator · up only</option>\n <option value=\"down\">Escalator · down only</option>\n </select>\n <button\n type=\"button\"\n className=\"text-button\"\n disabled={!selectedSpace}\n title={\n !selectedSpace\n ? 'Select an area on the plan first'\n : inside.has(selectedSpace.id)\n ? `Remove ${selectedSpace.name}`\n : `Add ${selectedSpace.name}`\n }\n onClick={() => {\n if (!selectedSpace) return;\n const add = !inside.has(selectedSpace.id);\n onEdit(d => setZoneMembers(d, zone.id, [selectedSpace.id], add));\n }}\n >\n {selectedSpace && inside.has(selectedSpace.id) ? 'Remove selected' : 'Add selected'}\n </button>\n <button\n type=\"button\"\n className=\"icon-button\"\n aria-label={`Delete ${zone.name}`}\n onClick={() => onEdit(d => removeZone(d, zone.id))}\n >\n <Trash2 size={14} />\n </button>\n </div>\n )}\n {ways.length > 0 && (\n <>\n <h4>\n <DoorOpen size={12} /> Ways in\n </h4>\n <ul>\n {ways.map(portal => {\n const insideId = inside.has(portal.a) ? portal.a : portal.b;\n const outsideId = insideId === portal.a ? portal.b : portal.a;\n return (\n <li key={portal.id} className=\"structure-member\">\n <span className=\"structure-member-title\">\n <span className=\"structure-from\">{name(outsideId)}</span>\n <MoveHorizontal size={11} />\n <span>{name(insideId)}</span>\n <em>{passageLabel(portal, insideId)}</em>\n </span>\n <button\n type=\"button\"\n className=\"structure-locate\"\n title=\"Find on map\"\n aria-label={`Find passage from ${name(outsideId)} to ${name(insideId)} on map`}\n onClick={() =>\n onLocate\n ? onLocate({\n objectIds: portal.openingId ? [portal.openingId] : [insideId, outsideId],\n floorId: floorOf(outsideId).floorId,\n })\n : pick(portal.openingId ?? insideId)\n }\n >\n <LocateFixed size={13} />\n </button>\n </li>\n );\n })}\n </ul>\n </>\n )}\n {!!zone.childZoneIds?.length && (\n <div className=\"structure-note\">\n Contains zones:{' '}\n {zone.childZoneIds.map(id => project.zones?.find(z => z.id === id)?.name ?? id).join(', ')}\n </div>\n )}\n {project.zones?.some(z => z.childZoneIds?.includes(zone.id)) && (\n <div className=\"structure-note\">\n Part of:{' '}\n {project.zones\n .filter(z => z.childZoneIds?.includes(zone.id))\n .map(z => z.name)\n .join(', ')}\n </div>\n )}\n <h4>\n <Layers size={12} /> Spaces\n </h4>\n <ul>\n {members.map(id => (\n <li key={id} className=\"structure-member\">\n <span className=\"structure-member-title\">\n <span>{name(id)}</span>\n <em>{floorOf(id).label}</em>\n </span>\n <button\n type=\"button\"\n className=\"structure-locate\"\n title=\"Find on map\"\n aria-label={`Find ${name(id)} on map`}\n onClick={() => (onLocate ? onLocate({ objectIds: [id] }) : pick(id))}\n >\n <LocateFixed size={13} />\n </button>\n </li>\n ))}\n </ul>\n {inner.length > 0 && (\n <p className=\"structure-note\">\n {inner.length} portal{inner.length > 1 ? 's' : ''} inside this zone — crossing one does not\n leave it.\n </p>\n )}\n </div>\n )}\n </div>\n );\n })}\n </section>\n ))}\n </div>\n </div>\n );\n}\n\n/** Exported for hosts that want the same grouping without the chrome. */\nexport const zoneMembers = (project: ProjectDocument, zone: Zone): SiteObject[] =>\n zoneSpaces(project, zone)\n .map(id => project.objects.find(o => o.id === id))\n .filter((o): o is SiteObject => !!o);\n"],"names":["StructureExplorer","lazy","module","OUTDOORS","passageLabel","portal","insideId","inward","entryInto","outward","StructureView","props","jsx","Suspense","filter","ZoneList","project","selected","onSelect","onFloorChange","onEdit","onLocate","query","floor","open","setOpen","useState","selectedSpace","o","isSpace","model","useMemo","byId","floors","f","zones","groups","z","members","zoneSpaces","id","text","_a","key","list","name","floorOf","floorId","pick","actions","jsxs","d","addZone","Plus","refreshPortals","RefreshCw","purpose","zone","isOpen","ways","perimeter","inner","captive","inside","ChevronDown","ChevronRight","LocateFixed","e","x","v","add","setZoneMembers","removeZone","Trash2","Fragment","DoorOpen","outsideId","MoveHorizontal","_b","Layers","Shapes"],"mappings":"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"}
@@ -0,0 +1,209 @@
1
+ import { jsxs as t, jsx as r, Fragment as D } from "react/jsx-runtime";
2
+ import { X as S, ChevronDown as E, Search as x } from "lucide-react";
3
+ import { useRef as k, useEffect as v, useState as b } from "react";
4
+ function $({
5
+ title: c,
6
+ subtitle: l,
7
+ onClose: s,
8
+ children: i,
9
+ wide: a = !1,
10
+ fullscreen: o = !1
11
+ }) {
12
+ const f = k(null);
13
+ return v(() => {
14
+ var m;
15
+ const n = f.current;
16
+ n == null || n.showModal();
17
+ const d = n == null ? void 0 : n.querySelector("input, select, textarea"), u = n == null ? void 0 : n.querySelectorAll("footer button");
18
+ return (m = d ?? (u == null ? void 0 : u[u.length - 1])) == null || m.focus(), () => n == null ? void 0 : n.close();
19
+ }, []), /* @__PURE__ */ t(
20
+ "dialog",
21
+ {
22
+ className: `modal ${a ? "wide" : ""} ${o ? "fullscreen" : ""}`,
23
+ "aria-label": c,
24
+ ref: f,
25
+ onCancel: s,
26
+ onClick: (n) => {
27
+ n.target === n.currentTarget && s();
28
+ },
29
+ children: [
30
+ /* @__PURE__ */ t("header", { children: [
31
+ /* @__PURE__ */ t("div", { children: [
32
+ /* @__PURE__ */ r("h2", { children: c }),
33
+ l && /* @__PURE__ */ r("p", { children: l })
34
+ ] }),
35
+ /* @__PURE__ */ r("button", { className: "icon-button", "aria-label": "Close dialog", onClick: s, children: /* @__PURE__ */ r(S, { size: 20 }) })
36
+ ] }),
37
+ i
38
+ ]
39
+ }
40
+ );
41
+ }
42
+ function q({
43
+ label: c,
44
+ value: l,
45
+ onChange: s,
46
+ type: i = "text",
47
+ suffix: a,
48
+ min: o,
49
+ max: f,
50
+ step: n
51
+ }) {
52
+ return /* @__PURE__ */ t("label", { className: "field", children: [
53
+ /* @__PURE__ */ r("span", { children: c }),
54
+ /* @__PURE__ */ t("div", { className: "input-wrap", children: [
55
+ /* @__PURE__ */ r(
56
+ "input",
57
+ {
58
+ "aria-label": c,
59
+ type: i,
60
+ defaultValue: l,
61
+ min: o,
62
+ max: f,
63
+ step: n ?? (i === "number" ? 0.1 : void 0),
64
+ onBlur: (d) => {
65
+ String(l) !== d.currentTarget.value && s(d.currentTarget.value);
66
+ },
67
+ onKeyDown: (d) => {
68
+ d.key === "Enter" && d.currentTarget.blur();
69
+ }
70
+ },
71
+ String(l)
72
+ ),
73
+ a && /* @__PURE__ */ r("em", { children: a })
74
+ ] })
75
+ ] });
76
+ }
77
+ function z({
78
+ label: c,
79
+ value: l,
80
+ onChange: s,
81
+ description: i
82
+ }) {
83
+ return /* @__PURE__ */ t("button", { className: "toggle-row", role: "switch", "aria-checked": l, onClick: s, children: [
84
+ /* @__PURE__ */ t("span", { children: [
85
+ c,
86
+ i && /* @__PURE__ */ r("small", { children: i })
87
+ ] }),
88
+ /* @__PURE__ */ r("i", { className: `toggle ${l ? "on" : ""}` })
89
+ ] });
90
+ }
91
+ function A({
92
+ label: c,
93
+ value: l,
94
+ options: s,
95
+ onChange: i,
96
+ description: a
97
+ }) {
98
+ return /* @__PURE__ */ t("div", { className: "toggle-row choice-row", children: [
99
+ /* @__PURE__ */ t("div", { className: "choice-head", children: [
100
+ /* @__PURE__ */ r("span", { children: c }),
101
+ /* @__PURE__ */ r("div", { className: "choice", role: "radiogroup", "aria-label": c, children: s.map((o) => /* @__PURE__ */ r(
102
+ "button",
103
+ {
104
+ role: "radio",
105
+ "aria-checked": l === o.value,
106
+ className: l === o.value ? "active" : "",
107
+ title: o.title,
108
+ onClick: () => i(o.value),
109
+ children: o.label
110
+ },
111
+ o.value
112
+ )) })
113
+ ] }),
114
+ a && /* @__PURE__ */ r("small", { children: a })
115
+ ] });
116
+ }
117
+ function F({
118
+ entries: c,
119
+ value: l,
120
+ onPick: s,
121
+ ariaLabel: i
122
+ }) {
123
+ const [a, o] = b(!1), [f, n] = b(""), [d, u] = b(0), m = k(null), C = k(null), N = c.find((e) => e.id === l), p = f.trim().toLowerCase(), g = a ? c.filter(
124
+ (e) => !p || e.name.toLowerCase().includes(p) || e.code.toLowerCase().includes(p) || (e.hint ?? "").toLowerCase().includes(p)
125
+ ).slice(0, 60) : [];
126
+ v(() => {
127
+ const e = (h) => {
128
+ var w;
129
+ (w = m.current) != null && w.contains(h.target) || o(!1);
130
+ };
131
+ return window.addEventListener("pointerdown", e), () => window.removeEventListener("pointerdown", e);
132
+ }, []), v(() => {
133
+ u(0);
134
+ }, [p, a]), v(() => {
135
+ var e;
136
+ a && ((e = C.current) == null || e.focus());
137
+ }, [a]);
138
+ const y = (e) => {
139
+ s(e), o(!1), n("");
140
+ };
141
+ return /* @__PURE__ */ t("div", { className: "floor-select", ref: m, children: [
142
+ /* @__PURE__ */ t(
143
+ "button",
144
+ {
145
+ "aria-label": i,
146
+ "data-value": l,
147
+ className: "floor-select-value",
148
+ onClick: () => {
149
+ o(!a), n("");
150
+ },
151
+ children: [
152
+ N ? /* @__PURE__ */ t(D, { children: [
153
+ /* @__PURE__ */ r("b", { children: N.code }),
154
+ /* @__PURE__ */ r("span", { children: N.name })
155
+ ] }) : i,
156
+ /* @__PURE__ */ r(E, { size: 14 })
157
+ ]
158
+ }
159
+ ),
160
+ a && /* @__PURE__ */ t("div", { className: "place-options floor-select-options", role: "listbox", children: [
161
+ /* @__PURE__ */ t("div", { className: "place-input", children: [
162
+ /* @__PURE__ */ r(x, { size: 13 }),
163
+ /* @__PURE__ */ r(
164
+ "input",
165
+ {
166
+ ref: C,
167
+ "aria-label": "Search floors",
168
+ placeholder: "Search floors…",
169
+ value: f,
170
+ onChange: (e) => n(e.target.value),
171
+ onBlur: () => setTimeout(() => o(!1), 120),
172
+ onKeyDown: (e) => {
173
+ e.key === "ArrowDown" ? (e.preventDefault(), u((h) => Math.min(h + 1, g.length - 1))) : e.key === "ArrowUp" ? (e.preventDefault(), u((h) => Math.max(h - 1, 0))) : e.key === "Enter" ? (e.preventDefault(), g[d] && y(g[d].id)) : e.key === "Escape" && (e.stopPropagation(), o(!1));
174
+ }
175
+ }
176
+ )
177
+ ] }),
178
+ g.map((e, h) => /* @__PURE__ */ t(
179
+ "button",
180
+ {
181
+ role: "option",
182
+ "aria-selected": e.id === l,
183
+ className: `place-option ${h === d ? "cursor" : ""} ${e.id === l ? "chosen" : ""}`,
184
+ onPointerDown: (w) => {
185
+ w.preventDefault(), y(e.id);
186
+ },
187
+ onMouseEnter: () => u(h),
188
+ children: [
189
+ /* @__PURE__ */ r("span", { className: "floor-number", children: e.code }),
190
+ /* @__PURE__ */ t("span", { children: [
191
+ e.name,
192
+ e.hint && /* @__PURE__ */ r("small", { children: e.hint })
193
+ ] })
194
+ ]
195
+ },
196
+ e.id
197
+ )),
198
+ !g.length && /* @__PURE__ */ r("p", { className: "place-none", children: "No floors match." })
199
+ ] })
200
+ ] });
201
+ }
202
+ export {
203
+ A as Choice,
204
+ q as Field,
205
+ F as FloorSelect,
206
+ $ as Modal,
207
+ z as Toggle
208
+ };
209
+ //# sourceMappingURL=controls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controls.js","sources":["../../../../src/components/controls.tsx"],"sourcesContent":["import { ChevronDown, Search, X } from 'lucide-react';\nimport { useEffect, useRef, useState, type ReactNode } from 'react';\nexport function Modal({\n title,\n subtitle,\n onClose,\n children,\n wide = false,\n fullscreen = false,\n}: {\n title: string;\n subtitle?: string;\n onClose: () => void;\n children: ReactNode;\n wide?: boolean;\n fullscreen?: boolean;\n}) {\n const dialog = useRef<HTMLDialogElement>(null);\n useEffect(() => {\n const el = dialog.current;\n el?.showModal();\n // Put the keyboard where the dialog is asking you to act.\n //\n // Left alone, a dialog focuses its first focusable child, which here is the X in the corner: the\n // one control that throws the dialog away. Enter then dismisses the question instead of\n // answering it, which is the opposite of what pressing Enter on a confirmation should do.\n //\n // A dialog that wants typing wants its first field — opening \"Add a floor\" and having to reach\n // for the mouse to click into the name is the wrong first move. Anything else wants the last\n // button in its footer, which is where the action sits.\n const field = el?.querySelector<HTMLElement>('input, select, textarea');\n const actions = el?.querySelectorAll<HTMLElement>('footer button');\n (field ?? actions?.[actions.length - 1])?.focus();\n return () => el?.close();\n }, []);\n return (\n <dialog\n className={`modal ${wide ? 'wide' : ''} ${fullscreen ? 'fullscreen' : ''}`}\n aria-label={title}\n ref={dialog}\n onCancel={onClose}\n onClick={e => {\n if (e.target === e.currentTarget) onClose();\n }}\n >\n <header>\n <div>\n <h2>{title}</h2>\n {subtitle && <p>{subtitle}</p>}\n </div>\n <button className=\"icon-button\" aria-label=\"Close dialog\" onClick={onClose}>\n <X size={20} />\n </button>\n </header>\n {children}\n </dialog>\n );\n}\nexport function Field({\n label,\n value,\n onChange,\n type = 'text',\n suffix,\n min,\n max,\n step,\n}: {\n label: string;\n value: string | number;\n onChange: (value: string) => void;\n type?: string;\n suffix?: string;\n min?: number;\n max?: number;\n step?: number;\n}) {\n return (\n <label className=\"field\">\n <span>{label}</span>\n <div className=\"input-wrap\">\n <input\n key={String(value)}\n aria-label={label}\n type={type}\n defaultValue={value}\n min={min}\n max={max}\n step={step ?? (type === 'number' ? 0.1 : undefined)}\n onBlur={e => {\n if (String(value) !== e.currentTarget.value) onChange(e.currentTarget.value);\n }}\n onKeyDown={e => {\n if (e.key === 'Enter') e.currentTarget.blur();\n }}\n />\n {suffix && <em>{suffix}</em>}\n </div>\n </label>\n );\n}\nexport function Toggle({\n label,\n value,\n onChange,\n description,\n}: {\n label: string;\n value: boolean;\n onChange: () => void;\n description?: string;\n}) {\n return (\n <button className=\"toggle-row\" role=\"switch\" aria-checked={value} onClick={onChange}>\n <span>\n {label}\n {description && <small>{description}</small>}\n </span>\n <i className={`toggle ${value ? 'on' : ''}`} />\n </button>\n );\n}\n\n/** A row of mutually exclusive choices, for a setting that has three answers rather than two.\n * Same shape as `Toggle` — label, optional description, the control on the right — so a panel of\n * switches does not acquire a second visual language for the one setting that needs a third state. */\nexport function Choice<T extends string>({\n label,\n value,\n options,\n onChange,\n description,\n}: {\n label: string;\n value: T;\n options: { value: T; label: string; title?: string }[];\n onChange: (value: T) => void;\n description?: string;\n}) {\n return (\n <div className=\"toggle-row choice-row\">\n <div className=\"choice-head\">\n <span>{label}</span>\n <div className=\"choice\" role=\"radiogroup\" aria-label={label}>\n {options.map(o => (\n <button\n key={o.value}\n role=\"radio\"\n aria-checked={value === o.value}\n className={value === o.value ? 'active' : ''}\n title={o.title}\n onClick={() => onChange(o.value)}\n >\n {o.label}\n </button>\n ))}\n </div>\n </div>\n {description && <small>{description}</small>}\n </div>\n );\n}\n\n/** Searchable dropdown for level selection: projects like the Silo carry a hundred floors. */\nexport interface FloorEntry {\n id: string;\n code: string;\n name: string;\n hint?: string;\n}\nexport function FloorSelect({\n entries,\n value,\n onPick,\n ariaLabel,\n}: {\n entries: FloorEntry[];\n value: string;\n onPick: (id: string) => void;\n ariaLabel: string;\n}) {\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n const [cursor, setCursor] = useState(0);\n const wrap = useRef<HTMLDivElement>(null);\n const input = useRef<HTMLInputElement>(null);\n const current = entries.find(e => e.id === value);\n const q = query.trim().toLowerCase();\n const matches = open\n ? entries\n .filter(\n e =>\n !q ||\n e.name.toLowerCase().includes(q) ||\n e.code.toLowerCase().includes(q) ||\n (e.hint ?? '').toLowerCase().includes(q),\n )\n .slice(0, 60)\n : [];\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 }, [q, open]);\n useEffect(() => {\n if (open) input.current?.focus();\n }, [open]);\n const pick = (id: string) => {\n onPick(id);\n setOpen(false);\n setQuery('');\n };\n return (\n <div className=\"floor-select\" ref={wrap}>\n <button\n aria-label={ariaLabel}\n data-value={value}\n className=\"floor-select-value\"\n onClick={() => {\n setOpen(!open);\n setQuery('');\n }}\n >\n {current ? (\n <>\n <b>{current.code}</b>\n <span>{current.name}</span>\n </>\n ) : (\n ariaLabel\n )}\n <ChevronDown size={14} />\n </button>\n {open && (\n <div className=\"place-options floor-select-options\" role=\"listbox\">\n <div className=\"place-input\">\n <Search size={13} />\n <input\n ref={input}\n aria-label=\"Search floors\"\n placeholder=\"Search floors…\"\n value={query}\n onChange={e => setQuery(e.target.value)}\n onBlur={() => setTimeout(() => setOpen(false), 120)}\n onKeyDown={e => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setCursor(v => Math.min(v + 1, matches.length - 1));\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setCursor(v => Math.max(v - 1, 0));\n } else if (e.key === 'Enter') {\n e.preventDefault();\n if (matches[cursor]) pick(matches[cursor].id);\n } else if (e.key === 'Escape') {\n e.stopPropagation();\n setOpen(false);\n }\n }}\n />\n </div>\n {matches.map((e, i) => (\n <button\n key={e.id}\n role=\"option\"\n aria-selected={e.id === value}\n className={`place-option ${i === cursor ? 'cursor' : ''} ${e.id === value ? 'chosen' : ''}`}\n onPointerDown={ev => {\n ev.preventDefault();\n pick(e.id);\n }}\n onMouseEnter={() => setCursor(i)}\n >\n <span className=\"floor-number\">{e.code}</span>\n <span>\n {e.name}\n {e.hint && <small>{e.hint}</small>}\n </span>\n </button>\n ))}\n {!matches.length && <p className=\"place-none\">No floors match.</p>}\n </div>\n )}\n </div>\n );\n}\n"],"names":["Modal","title","subtitle","onClose","children","wide","fullscreen","dialog","useRef","useEffect","el","field","actions","_a","jsxs","e","jsx","X","Field","label","value","onChange","type","suffix","min","max","step","Toggle","description","Choice","options","FloorSelect","entries","onPick","ariaLabel","open","setOpen","useState","query","setQuery","cursor","setCursor","wrap","input","current","q","matches","away","pick","id","Fragment","ChevronDown","Search","v","i","ev"],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ function r(n, l, M) {
2
+ const v = Math.max(1, Math.ceil(n.length / 80)), y = Math.max(0.08, 1 - M / 360);
3
+ for (let t = 0; t < n.length; t++) {
4
+ const a = n[t];
5
+ a.vx -= a.x * 1e-3 * y, a.vy -= a.y * 1e-3 * y;
6
+ for (let x = (t + M) % v; x < n.length; x += v) {
7
+ if (x === t) continue;
8
+ const c = n[x];
9
+ let e = a.x - c.x, h = a.y - c.y;
10
+ Math.abs(e) + Math.abs(h) < 0.01 && (e = t < x ? -0.1 : 0.1, h = 0.1);
11
+ const o = 90 * v * y / Math.max(25, e * e + h * h);
12
+ a.vx += e * o, a.vy += h * o;
13
+ }
14
+ }
15
+ for (const [t, a] of l) {
16
+ const x = n[t], c = n[a];
17
+ if (!x || !c || t === a) continue;
18
+ const e = c.x - x.x, h = c.y - x.y, o = Math.hypot(e, h) || 1, f = (o - 50) / o * 0.018 * y;
19
+ x.vx += e * f, x.vy += h * f, c.vx -= e * f, c.vy -= h * f;
20
+ }
21
+ for (const t of n)
22
+ t.pinned || (t.x += Math.max(-6, Math.min(6, t.vx)), t.y += Math.max(-6, Math.min(6, t.vy))), t.vx *= 0.7, t.vy *= 0.7;
23
+ }
24
+ export {
25
+ r as balanceGraph
26
+ };
27
+ //# sourceMappingURL=graphLayout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphLayout.js","sources":["../../../../src/components/graphLayout.ts"],"sourcesContent":["export interface GraphPoint {\n x: number;\n y: number;\n vx: number;\n vy: number;\n pinned?: boolean;\n}\n/** Bounded sampled repulsion keeps large navigation graphs out of quadratic per-frame work. */\nexport function balanceGraph(points: GraphPoint[], links: [number, number][], tick: number): void {\n const stride = Math.max(1, Math.ceil(points.length / 80));\n const alpha = Math.max(0.08, 1 - tick / 360);\n for (let i = 0; i < points.length; i++) {\n const p = points[i];\n p.vx -= p.x * 0.001 * alpha;\n p.vy -= p.y * 0.001 * alpha;\n for (let j = (i + tick) % stride; j < points.length; j += stride) {\n if (j === i) continue;\n const q = points[j];\n let dx = p.x - q.x,\n dy = p.y - q.y;\n if (Math.abs(dx) + Math.abs(dy) < 0.01) {\n dx = i < j ? -0.1 : 0.1;\n dy = 0.1;\n }\n const force = (90 * stride * alpha) / Math.max(25, dx * dx + dy * dy);\n p.vx += dx * force;\n p.vy += dy * force;\n }\n }\n for (const [a, b] of links) {\n const p = points[a],\n q = points[b];\n if (!p || !q || a === b) continue;\n const dx = q.x - p.x,\n dy = q.y - p.y,\n length = Math.hypot(dx, dy) || 1;\n const force = ((length - 50) / length) * 0.018 * alpha;\n p.vx += dx * force;\n p.vy += dy * force;\n q.vx -= dx * force;\n q.vy -= dy * force;\n }\n for (const p of points) {\n if (!p.pinned) {\n p.x += Math.max(-6, Math.min(6, p.vx));\n p.y += Math.max(-6, Math.min(6, p.vy));\n }\n p.vx *= 0.7;\n p.vy *= 0.7;\n }\n}\n"],"names":["balanceGraph","points","links","tick","stride","alpha","i","p","j","q","dx","dy","force","a","b","length"],"mappings":"AAQO,SAASA,EAAaC,GAAsBC,GAA2BC,GAAoB;AAChG,QAAMC,IAAS,KAAK,IAAI,GAAG,KAAK,KAAKH,EAAO,SAAS,EAAE,CAAC,GAClDI,IAAQ,KAAK,IAAI,MAAM,IAAIF,IAAO,GAAG;AAC3C,WAASG,IAAI,GAAGA,IAAIL,EAAO,QAAQK,KAAK;AACtC,UAAMC,IAAIN,EAAOK,CAAC;AAClB,IAAAC,EAAE,MAAMA,EAAE,IAAI,OAAQF,GACtBE,EAAE,MAAMA,EAAE,IAAI,OAAQF;AACtB,aAASG,KAAKF,IAAIH,KAAQC,GAAQI,IAAIP,EAAO,QAAQO,KAAKJ,GAAQ;AAChE,UAAII,MAAMF,EAAG;AACb,YAAMG,IAAIR,EAAOO,CAAC;AAClB,UAAIE,IAAKH,EAAE,IAAIE,EAAE,GACfE,IAAKJ,EAAE,IAAIE,EAAE;AACf,MAAI,KAAK,IAAIC,CAAE,IAAI,KAAK,IAAIC,CAAE,IAAI,SAChCD,IAAKJ,IAAIE,IAAI,OAAO,KACpBG,IAAK;AAEP,YAAMC,IAAS,KAAKR,IAASC,IAAS,KAAK,IAAI,IAAIK,IAAKA,IAAKC,IAAKA,CAAE;AACpE,MAAAJ,EAAE,MAAMG,IAAKE,GACbL,EAAE,MAAMI,IAAKC;AAAA,IACf;AAAA,EACF;AACA,aAAW,CAACC,GAAGC,CAAC,KAAKZ,GAAO;AAC1B,UAAMK,IAAIN,EAAOY,CAAC,GAChBJ,IAAIR,EAAOa,CAAC;AACd,QAAI,CAACP,KAAK,CAACE,KAAKI,MAAMC,EAAG;AACzB,UAAMJ,IAAKD,EAAE,IAAIF,EAAE,GACjBI,IAAKF,EAAE,IAAIF,EAAE,GACbQ,IAAS,KAAK,MAAML,GAAIC,CAAE,KAAK,GAC3BC,KAAUG,IAAS,MAAMA,IAAU,QAAQV;AACjD,IAAAE,EAAE,MAAMG,IAAKE,GACbL,EAAE,MAAMI,IAAKC,GACbH,EAAE,MAAMC,IAAKE,GACbH,EAAE,MAAME,IAAKC;AAAA,EACf;AACA,aAAWL,KAAKN;AACd,IAAKM,EAAE,WACLA,EAAE,KAAK,KAAK,IAAI,IAAI,KAAK,IAAI,GAAGA,EAAE,EAAE,CAAC,GACrCA,EAAE,KAAK,KAAK,IAAI,IAAI,KAAK,IAAI,GAAGA,EAAE,EAAE,CAAC,IAEvCA,EAAE,MAAM,KACRA,EAAE,MAAM;AAEZ;"}