@kerros/viewer 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 (264) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -2
  3. package/dist/adapters/basemap.js +11 -0
  4. package/dist/adapters/basemap.js.map +1 -0
  5. package/dist/adapters/persistence.js +121 -0
  6. package/dist/adapters/persistence.js.map +1 -0
  7. package/dist/adapters/status.js +18 -0
  8. package/dist/adapters/status.js.map +1 -0
  9. package/dist/components/ElevatorPanel.js +120 -0
  10. package/dist/components/ElevatorPanel.js.map +1 -0
  11. package/dist/components/Icons.js +57 -0
  12. package/dist/components/Icons.js.map +1 -0
  13. package/dist/components/Inspector.js +1184 -0
  14. package/dist/components/Inspector.js.map +1 -0
  15. package/dist/components/NavigatePanel.js +301 -0
  16. package/dist/components/NavigatePanel.js.map +1 -0
  17. package/dist/components/NavigationGraph.js +234 -0
  18. package/dist/components/NavigationGraph.js.map +1 -0
  19. package/dist/components/StructureExplorer.js +510 -0
  20. package/dist/components/StructureExplorer.js.map +1 -0
  21. package/dist/components/StructureView.js +286 -0
  22. package/dist/components/StructureView.js.map +1 -0
  23. package/dist/components/controls.js +170 -0
  24. package/dist/components/controls.js.map +1 -0
  25. package/dist/components/graphLayout.js +27 -0
  26. package/dist/components/graphLayout.js.map +1 -0
  27. package/dist/host.js +169 -0
  28. package/dist/host.js.map +1 -0
  29. package/dist/i18n.js +88 -0
  30. package/dist/i18n.js.map +1 -0
  31. package/dist/index.js +171 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/map/CabinMaterials.js +81 -0
  34. package/dist/map/CabinMaterials.js.map +1 -0
  35. package/dist/map/CabinMirror.js +43 -0
  36. package/dist/map/CabinMirror.js.map +1 -0
  37. package/dist/map/Doors.js +80 -0
  38. package/dist/map/Doors.js.map +1 -0
  39. package/dist/map/ElevatorCabin.js +93 -0
  40. package/dist/map/ElevatorCabin.js.map +1 -0
  41. package/dist/map/FixtureLights.js +117 -0
  42. package/dist/map/FixtureLights.js.map +1 -0
  43. package/dist/map/MapCanvas.js +2161 -0
  44. package/dist/map/MapCanvas.js.map +1 -0
  45. package/dist/map/Passenger.js +69 -0
  46. package/dist/map/Passenger.js.map +1 -0
  47. package/dist/map/SceneLayer.js +1534 -0
  48. package/dist/map/SceneLayer.js.map +1 -0
  49. package/dist/map/UndergroundContext.js +153 -0
  50. package/dist/map/UndergroundContext.js.map +1 -0
  51. package/dist/map/ambience.js +194 -0
  52. package/dist/map/ambience.js.map +1 -0
  53. package/dist/map/architecture.js +305 -0
  54. package/dist/map/architecture.js.map +1 -0
  55. package/dist/map/bathMusic.js +92 -0
  56. package/dist/map/bathMusic.js.map +1 -0
  57. package/dist/map/cameraAim.js +33 -0
  58. package/dist/map/cameraAim.js.map +1 -0
  59. package/dist/map/elevators.js +56 -0
  60. package/dist/map/elevators.js.map +1 -0
  61. package/dist/map/excavation.js +52 -0
  62. package/dist/map/excavation.js.map +1 -0
  63. package/dist/map/exteriors.js +25 -0
  64. package/dist/map/exteriors.js.map +1 -0
  65. package/dist/map/features.js +218 -0
  66. package/dist/map/features.js.map +1 -0
  67. package/dist/map/fences.js +29 -0
  68. package/dist/map/fences.js.map +1 -0
  69. package/dist/map/journey.js +112 -0
  70. package/dist/map/journey.js.map +1 -0
  71. package/dist/map/levels.js +6 -0
  72. package/dist/map/levels.js.map +1 -0
  73. package/dist/map/lighting.js +142 -0
  74. package/dist/map/lighting.js.map +1 -0
  75. package/dist/map/loadBasemap.js +26 -0
  76. package/dist/map/loadBasemap.js.map +1 -0
  77. package/dist/map/materials.js +183 -0
  78. package/dist/map/materials.js.map +1 -0
  79. package/dist/map/poolLighting.js +39 -0
  80. package/dist/map/poolLighting.js.map +1 -0
  81. package/dist/map/projection.js +24 -0
  82. package/dist/map/projection.js.map +1 -0
  83. package/dist/map/rampGeometry.js +38 -0
  84. package/dist/map/rampGeometry.js.map +1 -0
  85. package/dist/map/route.js +107 -0
  86. package/dist/map/route.js.map +1 -0
  87. package/dist/map/stairSurfaces.js +115 -0
  88. package/dist/map/stairSurfaces.js.map +1 -0
  89. package/dist/map/surfaces.js +136 -0
  90. package/dist/map/surfaces.js.map +1 -0
  91. package/dist/map/textures.js +95 -0
  92. package/dist/map/textures.js.map +1 -0
  93. package/dist/map/underground.js +34 -0
  94. package/dist/map/underground.js.map +1 -0
  95. package/dist/map/walk.js +433 -0
  96. package/dist/map/walk.js.map +1 -0
  97. package/dist/map/walkContext.js +46 -0
  98. package/dist/map/walkContext.js.map +1 -0
  99. package/dist/map/walkDimensions.js +7 -0
  100. package/dist/map/walkDimensions.js.map +1 -0
  101. package/dist/map/walkJourney.js +85 -0
  102. package/dist/map/walkJourney.js.map +1 -0
  103. package/dist/map/walkSurfaces.js +106 -0
  104. package/dist/map/walkSurfaces.js.map +1 -0
  105. package/dist/map/wallJoins.js +70 -0
  106. package/dist/map/wallJoins.js.map +1 -0
  107. package/dist/map/water.js +274 -0
  108. package/dist/map/water.js.map +1 -0
  109. package/dist/model/authoring.js +80 -0
  110. package/dist/model/authoring.js.map +1 -0
  111. package/dist/model/boundaries.js +548 -0
  112. package/dist/model/boundaries.js.map +1 -0
  113. package/dist/model/coverage.js +30 -0
  114. package/dist/model/coverage.js.map +1 -0
  115. package/dist/model/document.js +52 -0
  116. package/dist/model/document.js.map +1 -0
  117. package/dist/model/doors.js +47 -0
  118. package/dist/model/doors.js.map +1 -0
  119. package/dist/model/factory.js +56 -0
  120. package/dist/model/factory.js.map +1 -0
  121. package/dist/model/geometry.js +394 -0
  122. package/dist/model/geometry.js.map +1 -0
  123. package/dist/model/inference.js +182 -0
  124. package/dist/model/inference.js.map +1 -0
  125. package/dist/model/materials.js +11 -0
  126. package/dist/model/materials.js.map +1 -0
  127. package/dist/model/mutations.js +157 -0
  128. package/dist/model/mutations.js.map +1 -0
  129. package/dist/model/navigation.js +275 -0
  130. package/dist/model/navigation.js.map +1 -0
  131. package/dist/model/ontology.js +109 -0
  132. package/dist/model/ontology.js.map +1 -0
  133. package/dist/model/passages.js +5 -0
  134. package/dist/model/passages.js.map +1 -0
  135. package/dist/model/portals.js +42 -0
  136. package/dist/model/portals.js.map +1 -0
  137. package/dist/model/precision.js +11 -0
  138. package/dist/model/precision.js.map +1 -0
  139. package/dist/model/project.js +17 -0
  140. package/dist/model/project.js.map +1 -0
  141. package/dist/model/ramps.js +10 -0
  142. package/dist/model/ramps.js.map +1 -0
  143. package/dist/model/roomNavigation.js +87 -0
  144. package/dist/model/roomNavigation.js.map +1 -0
  145. package/dist/model/routeClearance.js +72 -0
  146. package/dist/model/routeClearance.js.map +1 -0
  147. package/dist/model/spaces.js +104 -0
  148. package/dist/model/spaces.js.map +1 -0
  149. package/dist/model/stairGeometry.js +40 -0
  150. package/dist/model/stairGeometry.js.map +1 -0
  151. package/dist/model/stairTraversal.js +48 -0
  152. package/dist/model/stairTraversal.js.map +1 -0
  153. package/dist/model/topology.js +63 -0
  154. package/dist/model/topology.js.map +1 -0
  155. package/dist/model/types.js +35 -0
  156. package/dist/model/types.js.map +1 -0
  157. package/dist/model/validate.js +240 -0
  158. package/dist/model/validate.js.map +1 -0
  159. package/dist/model/vertical.js +129 -0
  160. package/dist/model/vertical.js.map +1 -0
  161. package/dist/model/walls.js +71 -0
  162. package/dist/model/walls.js.map +1 -0
  163. package/dist/styles.css +4931 -0
  164. package/dist/theme/index.js +34 -0
  165. package/dist/theme/index.js.map +1 -0
  166. package/dist/types/adapters/basemap.d.ts +2 -0
  167. package/dist/types/adapters/persistence.d.ts +29 -0
  168. package/dist/types/adapters/status.d.ts +4 -0
  169. package/dist/types/components/ElevatorPanel.d.ts +18 -0
  170. package/dist/types/components/Icons.d.ts +8 -0
  171. package/dist/types/components/Inspector.d.ts +38 -0
  172. package/dist/types/components/NavigatePanel.d.ts +27 -0
  173. package/dist/types/components/NavigationGraph.d.ts +10 -0
  174. package/dist/types/components/StructureExplorer.d.ts +8 -0
  175. package/dist/types/components/StructureView.d.ts +22 -0
  176. package/dist/types/components/controls.d.ts +52 -0
  177. package/dist/types/components/graphLayout.d.ts +9 -0
  178. package/dist/types/i18n.d.ts +85 -0
  179. package/dist/types/import/analysisPreview.d.ts +27 -0
  180. package/dist/types/import/brief.d.ts +22 -0
  181. package/dist/types/import/checkpoint.d.ts +49 -0
  182. package/dist/types/import/instructions.d.ts +7 -0
  183. package/dist/types/import/usage.d.ts +10 -0
  184. package/dist/types/map/CabinMaterials.d.ts +18 -0
  185. package/dist/types/map/CabinMirror.d.ts +13 -0
  186. package/dist/types/map/Doors.d.ts +11 -0
  187. package/dist/types/map/ElevatorCabin.d.ts +20 -0
  188. package/dist/types/map/FixtureLights.d.ts +35 -0
  189. package/dist/types/map/MapCanvas.d.ts +87 -0
  190. package/dist/types/map/Passenger.d.ts +18 -0
  191. package/dist/types/map/SceneLayer.d.ts +306 -0
  192. package/dist/types/map/UndergroundContext.d.ts +18 -0
  193. package/dist/types/map/ambience.d.ts +29 -0
  194. package/dist/types/map/architecture.d.ts +8 -0
  195. package/dist/types/map/bathMusic.d.ts +14 -0
  196. package/dist/types/map/cameraAim.d.ts +20 -0
  197. package/dist/types/map/elevators.d.ts +16 -0
  198. package/dist/types/map/excavation.d.ts +9 -0
  199. package/dist/types/map/exteriors.d.ts +3 -0
  200. package/dist/types/map/features.d.ts +33 -0
  201. package/dist/types/map/fences.d.ts +4 -0
  202. package/dist/types/map/journey.d.ts +36 -0
  203. package/dist/types/map/levels.d.ts +4 -0
  204. package/dist/types/map/lighting.d.ts +103 -0
  205. package/dist/types/map/loadBasemap.d.ts +4 -0
  206. package/dist/types/map/materials.d.ts +38 -0
  207. package/dist/types/map/poolLighting.d.ts +11 -0
  208. package/dist/types/map/projection.d.ts +9 -0
  209. package/dist/types/map/rampGeometry.d.ts +8 -0
  210. package/dist/types/map/route.d.ts +59 -0
  211. package/dist/types/map/stairSurfaces.d.ts +29 -0
  212. package/dist/types/map/surfaces.d.ts +36 -0
  213. package/dist/types/map/textures.d.ts +8 -0
  214. package/dist/types/map/underground.d.ts +30 -0
  215. package/dist/types/map/walk.d.ts +232 -0
  216. package/dist/types/map/walkContext.d.ts +11 -0
  217. package/dist/types/map/walkDimensions.d.ts +5 -0
  218. package/dist/types/map/walkJourney.d.ts +33 -0
  219. package/dist/types/map/walkSurfaces.d.ts +27 -0
  220. package/dist/types/map/wallJoins.d.ts +6 -0
  221. package/dist/types/map/water.d.ts +12 -0
  222. package/dist/types/model/authoring.d.ts +38 -0
  223. package/dist/types/model/boundaries.d.ts +41 -0
  224. package/dist/types/model/coverage.d.ts +8 -0
  225. package/dist/types/model/document.d.ts +9 -0
  226. package/dist/types/model/doors.d.ts +19 -0
  227. package/dist/types/model/factory.d.ts +5 -0
  228. package/dist/types/model/geometry.d.ts +99 -0
  229. package/dist/types/model/host.d.ts +154 -0
  230. package/dist/types/model/inference.d.ts +46 -0
  231. package/dist/types/model/live.d.ts +60 -0
  232. package/dist/types/model/materials.d.ts +28 -0
  233. package/dist/types/model/mutations.d.ts +178 -0
  234. package/dist/types/model/navigation.d.ts +70 -0
  235. package/dist/types/model/ontology.d.ts +43 -0
  236. package/dist/types/model/passages.d.ts +2 -0
  237. package/dist/types/model/portals.d.ts +9 -0
  238. package/dist/types/model/precision.d.ts +11 -0
  239. package/dist/types/model/project.d.ts +8 -0
  240. package/dist/types/model/ramps.d.ts +7 -0
  241. package/dist/types/model/roomNavigation.d.ts +15 -0
  242. package/dist/types/model/routeClearance.d.ts +11 -0
  243. package/dist/types/model/spaces.d.ts +46 -0
  244. package/dist/types/model/stairGeometry.d.ts +27 -0
  245. package/dist/types/model/stairTraversal.d.ts +8 -0
  246. package/dist/types/model/topology.d.ts +14 -0
  247. package/dist/types/model/types.d.ts +369 -0
  248. package/dist/types/model/validate.d.ts +38 -0
  249. package/dist/types/model/vertical.d.ts +145 -0
  250. package/dist/types/model/walls.d.ts +71 -0
  251. package/dist/types/schema/index.d.ts +25 -0
  252. package/dist/types/theme/index.d.ts +55 -0
  253. package/dist/types/viewer/FloorViewer.d.ts +76 -0
  254. package/dist/types/viewer/ViewerWorkspace.d.ts +2 -0
  255. package/dist/types/viewer/host.d.ts +11 -0
  256. package/dist/types/viewer/index.d.ts +2 -0
  257. package/dist/types/viewer/panels.d.ts +12 -0
  258. package/dist/viewer/FloorViewer.js +104 -0
  259. package/dist/viewer/FloorViewer.js.map +1 -0
  260. package/dist/viewer/ViewerWorkspace.js +370 -0
  261. package/dist/viewer/ViewerWorkspace.js.map +1 -0
  262. package/dist/viewer/panels.js +35 -0
  263. package/dist/viewer/panels.js.map +1 -0
  264. package/package.json +58 -4
@@ -0,0 +1,34 @@
1
+ import { jsx as n } from "react/jsx-runtime";
2
+ import { createContext as u, useMemo as i, useContext as m, useState as f, useEffect as d } from "react";
3
+ import { en as s } from "../i18n.js";
4
+ function h() {
5
+ const [e, r] = f(() => localStorage.getItem("kerros:dark") === "1");
6
+ return d(() => {
7
+ document.documentElement.classList.toggle("dark", e), localStorage.setItem("kerros:dark", e ? "1" : "0");
8
+ }, [e]), [e, () => r((o) => !o)];
9
+ }
10
+ const c = u({});
11
+ function j({ theme: e = {}, children: r }) {
12
+ const o = Object.fromEntries(
13
+ Object.entries(e.tokens ?? {}).map(([t, a]) => [t.startsWith("--") ? t : `--${t}`, a])
14
+ );
15
+ return /* @__PURE__ */ n(c.Provider, { value: e, children: /* @__PURE__ */ n("div", { className: "kerros-root kerros-passthrough", style: o, children: r }) });
16
+ }
17
+ const l = () => m(c);
18
+ function v() {
19
+ const { strings: e } = l();
20
+ return i(() => {
21
+ if (!e) return s;
22
+ const r = { ...s };
23
+ for (const [o, t] of Object.entries(e))
24
+ r[o] = t && typeof t == "object" && !Array.isArray(t) ? { ...s[o], ...t } : t;
25
+ return r;
26
+ }, [e]);
27
+ }
28
+ export {
29
+ j as KerrosThemeProvider,
30
+ h as useDarkMode,
31
+ l as useKerrosTheme,
32
+ v as useStrings
33
+ };
34
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../../../src/theme/index.tsx"],"sourcesContent":["import {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ComponentType,\n type CSSProperties,\n type ReactNode,\n} from 'react';\nimport type { SiteObject } from '../model/types';\nimport type { StatusTone } from '../model/live';\nimport { en, type En } from '../i18n';\n\ntype DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K] };\n\n/** App-wide dark mode: a `dark` class on the root element, remembered per browser. */\nexport function useDarkMode(): [boolean, () => void] {\n const [dark, setDark] = useState(() => localStorage.getItem('kerros:dark') === '1');\n useEffect(() => {\n document.documentElement.classList.toggle('dark', dark);\n localStorage.setItem('kerros:dark', dark ? '1' : '0');\n }, [dark]);\n return [dark, () => setDark(d => !d)];\n}\n\n// ——— Theming & pluggable chrome. Hosts wrap the viewer/editor in <KerrosThemeProvider> to override\n// design tokens, supply custom icons, and route notifications/confirms into their own UI. Every\n// field is optional; unset falls back to the built-in default, so the components work with no theme.\nexport interface ConfirmOptions {\n title: string;\n body?: string;\n danger?: boolean;\n confirmLabel?: string;\n}\n/** Plan/route/device rendering colours, per deployment (\"operation model\"). Basemap is separate (BasemapConfig). */\nexport interface MapStyleOptions {\n /** Plan area fill and wall colour (top-down 2D). */\n room?: string;\n wall?: string;\n route?: string;\n routeActive?: string;\n /** Selection/hover/draft highlight colours, applied on both the 2D plan and the 3D scene. */\n selection?: string;\n /** Soft (translucent) selection fill for areas; defaults to a light tint of the selection colour. */\n selectionFill?: string;\n hover?: string;\n draft?: string;\n statusTones?: Partial<Record<StatusTone, string>>;\n /** Last-word override for any area/object fill and marker accent; return undefined to fall through. */\n objectColor?: (object: SiteObject) => string | undefined;\n}\nexport interface KerrosTheme {\n /** CSS custom-property overrides (e.g. { '--accent': '#e8562f' }); applied to the theme wrapper. */\n tokens?: Record<string, string>;\n /** Per kind/tool/POI-symbol icon overrides; keys match EntityIcon's `kind`/`symbol`. */\n icons?: Record<string, ComponentType<{ size?: number }>>;\n /** Show a transient notification. Unset → the built-in toast. */\n notify?: (message: string, tone?: 'info' | 'error') => void;\n /** Ask the user to confirm a destructive action. Unset → the built-in dialog. */\n confirm?: (options: ConfirmOptions) => Promise<boolean>;\n /** Plan/route/device rendering colours. */\n mapStyle?: MapStyleOptions;\n /** Override any library UI strings (deep-merged over the built-in English); read via useStrings(). */\n strings?: DeepPartial<En>;\n}\nconst ThemeContext = createContext<KerrosTheme>({});\nexport function KerrosThemeProvider({ theme = {}, children }: { theme?: KerrosTheme; children: ReactNode }) {\n // Only the tokens are inline — they are the one genuinely dynamic part. The wrapper's layout role\n // (none: display:contents) is .kerros-passthrough in the stylesheet, kept apart from .kerros-root so\n // that \"scope for the resets\" and \"box in the layout\" stay two separate, legible ideas.\n const style = Object.fromEntries(\n Object.entries(theme.tokens ?? {}).map(([k, v]) => [k.startsWith('--') ? k : `--${k}`, v]),\n ) as CSSProperties;\n return (\n <ThemeContext.Provider value={theme}>\n <div className=\"kerros-root kerros-passthrough\" style={style}>\n {children}\n </div>\n </ThemeContext.Provider>\n );\n}\nexport const useKerrosTheme = () => useContext(ThemeContext);\n\n// Library UI strings with the host's overrides deep-merged over the built-in English (one level of\n// nesting — tools/navigate/map — is enough for the current dictionary).\nexport function useStrings(): En {\n const { strings } = useKerrosTheme();\n return useMemo(() => {\n if (!strings) return en;\n const out = { ...en } as Record<string, unknown>;\n for (const [k, v] of Object.entries(strings))\n out[k] =\n v && typeof v === 'object' && !Array.isArray(v)\n ? { ...((en as Record<string, unknown>)[k] as object), ...v }\n : v;\n return out as En;\n }, [strings]);\n}\n"],"names":["useDarkMode","dark","setDark","useState","useEffect","d","ThemeContext","createContext","KerrosThemeProvider","theme","children","style","k","v","jsx","useKerrosTheme","useContext","useStrings","strings","useMemo","en","out"],"mappings":";;;AAiBO,SAASA,IAAqC;AACnD,QAAM,CAACC,GAAMC,CAAO,IAAIC,EAAS,MAAM,aAAa,QAAQ,aAAa,MAAM,GAAG;AAClF,SAAAC,EAAU,MAAM;AACd,aAAS,gBAAgB,UAAU,OAAO,QAAQH,CAAI,GACtD,aAAa,QAAQ,eAAeA,IAAO,MAAM,GAAG;AAAA,EACtD,GAAG,CAACA,CAAI,CAAC,GACF,CAACA,GAAM,MAAMC,EAAQ,CAAAG,MAAK,CAACA,CAAC,CAAC;AACtC;AA0CA,MAAMC,IAAeC,EAA2B,EAAE;AAC3C,SAASC,EAAoB,EAAE,OAAAC,IAAQ,CAAA,GAAI,UAAAC,KAA0D;AAI1G,QAAMC,IAAQ,OAAO;AAAA,IACnB,OAAO,QAAQF,EAAM,UAAU,CAAA,CAAE,EAAE,IAAI,CAAC,CAACG,GAAGC,CAAC,MAAM,CAACD,EAAE,WAAW,IAAI,IAAIA,IAAI,KAAKA,CAAC,IAAIC,CAAC,CAAC;AAAA,EAAA;AAE3F,SACE,gBAAAC,EAACR,EAAa,UAAb,EAAsB,OAAOG,GAC5B,UAAA,gBAAAK,EAAC,OAAA,EAAI,WAAU,kCAAiC,OAAAH,GAC7C,UAAAD,EAAA,CACH,GACF;AAEJ;AACO,MAAMK,IAAiB,MAAMC,EAAWV,CAAY;AAIpD,SAASW,IAAiB;AAC/B,QAAM,EAAE,SAAAC,EAAA,IAAYH,EAAA;AACpB,SAAOI,EAAQ,MAAM;AACnB,QAAI,CAACD,EAAS,QAAOE;AACrB,UAAMC,IAAM,EAAE,GAAGD,EAAA;AACjB,eAAW,CAACR,GAAGC,CAAC,KAAK,OAAO,QAAQK,CAAO;AACzC,MAAAG,EAAIT,CAAC,IACHC,KAAK,OAAOA,KAAM,YAAY,CAAC,MAAM,QAAQA,CAAC,IAC1C,EAAE,GAAKO,EAA+BR,CAAC,GAAc,GAAGC,MACxDA;AACR,WAAOQ;AAAA,EACT,GAAG,CAACH,CAAO,CAAC;AACd;"}
@@ -0,0 +1,2 @@
1
+ import type { BasemapConfig } from '../model/host';
2
+ export declare const neutralBasemap: BasemapConfig;
@@ -0,0 +1,29 @@
1
+ import type { AssetRepository, ProjectDocument, ProjectRepository, ProjectSummary } from '../model/types';
2
+ export { validateProject } from '../model/validate';
3
+ export { exportProject, importProject, blobDataUrl } from '../model/document';
4
+ export declare class LocalProjectRepository implements ProjectRepository {
5
+ list(): Promise<ProjectSummary[]>;
6
+ load(id: string): Promise<ProjectDocument | null>;
7
+ save(project: ProjectDocument): Promise<void>;
8
+ delete(id: string): Promise<void>;
9
+ }
10
+ /** Large documents belong in IndexedDB rather than localStorage's small synchronous quota.
11
+ * An optional previous repository keeps existing browser saves readable; new saves take precedence. */
12
+ export declare class IndexedProjectRepository implements ProjectRepository {
13
+ private previous?;
14
+ private db?;
15
+ constructor(previous?: ProjectRepository | undefined);
16
+ private open;
17
+ list(): Promise<ProjectSummary[]>;
18
+ load(id: string): Promise<ProjectDocument | null>;
19
+ private write;
20
+ save(project: ProjectDocument): Promise<void>;
21
+ delete(id: string): Promise<void>;
22
+ }
23
+ export declare class IndexedAssetRepository implements AssetRepository {
24
+ private db?;
25
+ private open;
26
+ get(id: string): Promise<Blob | undefined>;
27
+ put(id: string, asset: Blob): Promise<void>;
28
+ delete(id: string): Promise<void>;
29
+ }
@@ -0,0 +1,4 @@
1
+ import type { StatusReading, StatusTone } from '../model/live';
2
+ export declare const unknownStatus: (feedId: string) => StatusReading;
3
+ export declare function statusTone(status?: StatusReading, now?: number): StatusTone;
4
+ export declare function statusLabel(status?: StatusReading, now?: number): string;
@@ -0,0 +1,18 @@
1
+ import type { ElevatorControls } from '../model/host';
2
+ import type { ProjectDocument } from '../model/types';
3
+ import type { WalkController } from '../map/walk';
4
+ export declare function ElevatorPanel({ project, floorId, controls, walker, riding, onRide, onFloor, requested, onWave, soundOn, onSound, }: {
5
+ project: ProjectDocument;
6
+ floorId: string | null;
7
+ controls: ElevatorControls;
8
+ walker: {
9
+ current: WalkController | null;
10
+ };
11
+ riding: string | null;
12
+ onRide(id: string | null): void;
13
+ onFloor?(id: string): void;
14
+ requested: number;
15
+ onWave(): void;
16
+ soundOn: boolean;
17
+ onSound(): void;
18
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,8 @@
1
+ import type { ObjectKind, SiteObject, Tool } from '../model/types';
2
+ export declare function EntityIcon({ kind, size, symbol, travel, }: {
3
+ kind: ObjectKind | Tool | 'wall' | 'fence';
4
+ size?: number;
5
+ symbol?: SiteObject['symbol'];
6
+ /** An escalator's direction, so a descending one is not drawn with an arrow pointing up. */
7
+ travel?: SiteObject['travel'];
8
+ }): import("react").JSX.Element;
@@ -0,0 +1,38 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { Barrier, Drawing, Floor, ProjectDocument, SiteObject } from '../model/types';
3
+ import type { StatusReading } from '../model/live';
4
+ import type { StatusPanelContext } from '../model/host';
5
+ export interface InspectorProps {
6
+ project: ProjectDocument;
7
+ floorId: string | null;
8
+ selected: string | null;
9
+ statuses: Map<string, StatusReading>;
10
+ /** Whether the host supplied a StatusFeed at all. False hides every live-status surface — a
11
+ * rollup of bindings that can only ever read "unknown" is worse than no rollup. */
12
+ monitoring: boolean;
13
+ editing: boolean;
14
+ live: boolean;
15
+ renderStatusPanel?: (ctx: StatusPanelContext) => ReactNode;
16
+ onClose: () => void;
17
+ onUpdateObject: (id: string, patch: Partial<SiteObject>) => void;
18
+ /** Apply an arbitrary change through the host's commit/undo path — used for the ontology, which
19
+ * edits entities beside the object rather than fields on it. */
20
+ onEdit?: (change: (draft: ProjectDocument) => void) => void;
21
+ /** Editing operations are supplied by the editor, so read-only consumers need no authoring engine. */
22
+ onSpaceGeometry?: (id: string, source: 'outline' | 'walls' | 'independent') => void;
23
+ onShiftOrigin?: (east: number, north: number) => void;
24
+ onUpdateBarrier: (id: string, patch: Partial<Barrier>) => void;
25
+ onUpdateDrawing: (id: string, patch: Partial<Drawing>) => void;
26
+ onUpdateFloor: (id: string, patch: Partial<Floor>) => void;
27
+ /** Set (or clear, with null) the floor a viewer opens this document on. */
28
+ onSetInitialFloor: (id: string | null) => void;
29
+ /** Remove a floor and everything on it (confirms first). */
30
+ onDeleteFloor: (id: string) => void;
31
+ onDuplicate: () => void;
32
+ onDelete: () => void;
33
+ onSelect: (id: string) => void;
34
+ /** Open a floor — a lift's levels are the one relation that crosses them. */
35
+ onFloor: (id: string) => void;
36
+ onTraceFootprint: (id: string) => void;
37
+ }
38
+ export declare function Inspector(props: InspectorProps): import("react").JSX.Element;
@@ -0,0 +1,27 @@
1
+ import type { Point, ProjectDocument } from '../model/types';
2
+ import type { Route } from '../model/navigation';
3
+ export interface NavigatePanelProps {
4
+ project: ProjectDocument;
5
+ /** Where the person is — the walk's avatar — offered as "Where you are" and proposed as the
6
+ * start whenever nothing else has been chosen. Null when nobody has walked yet. */
7
+ here?: {
8
+ floorId: string | null;
9
+ position: Point;
10
+ } | null;
11
+ from: string | null;
12
+ to: string | null;
13
+ route: Route | null;
14
+ playing: boolean;
15
+ activeStep: number | null;
16
+ editing: boolean;
17
+ onFrom: (id: string | null) => void;
18
+ onTo: (id: string | null) => void;
19
+ onSwap: () => void;
20
+ onClear: () => void;
21
+ onPlay: () => void;
22
+ onPause: () => void;
23
+ onStep: (delta: 1 | -1) => void;
24
+ onSeek: (index: number) => void;
25
+ onClose: () => void;
26
+ }
27
+ export declare function NavigatePanel({ project, here, from, to, route, playing, activeStep, editing, onFrom, onTo, onSwap, onClear, onPlay, onPause, onStep, onSeek, onClose, }: NavigatePanelProps): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import type { ProjectDocument } from '../model/types';
2
+ import type { StructureTarget } from './StructureView';
3
+ export interface NavigationGraphProps {
4
+ project: ProjectDocument;
5
+ initialFloor?: string | null;
6
+ dark: boolean;
7
+ onLocate: (target: StructureTarget) => void;
8
+ onClose: () => void;
9
+ }
10
+ export declare function NavigationGraph({ project, initialFloor, dark, onLocate, onClose }: NavigationGraphProps): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { StructureViewProps } from './StructureView';
3
+ export declare function StructureExplorer({ project, selected, onSelect, onFloorChange, onLocate, onGraphView, renderZones, }: StructureViewProps & {
4
+ renderZones: (filter: {
5
+ query: string;
6
+ floor: string;
7
+ }) => ReactNode;
8
+ }): import("react").JSX.Element;
@@ -0,0 +1,22 @@
1
+ import { type Point, type ProjectDocument, type SiteObject, type Zone } from '../model/types';
2
+ export interface StructureViewProps {
3
+ project: ProjectDocument;
4
+ selected?: string | null;
5
+ /** Fires when a space or opening is picked, so a host can follow along on the plan. */
6
+ onSelect?: (id: string) => void;
7
+ /** Fires alongside onSelect when the pick lives on a particular floor. */
8
+ onFloorChange?: (floorId: string | null) => void;
9
+ onLocate?: (target: StructureTarget) => void;
10
+ onGraphView?: (floorId?: string | null) => void;
11
+ /** Omit to keep the panel read-only. Given, the panel can author zones: the host applies the
12
+ * mutation to a draft and owns undo, exactly as the map tools do. */
13
+ onEdit?: (change: (draft: ProjectDocument) => void) => void;
14
+ }
15
+ export interface StructureTarget {
16
+ objectIds?: string[];
17
+ floorId?: string | null;
18
+ position?: Point;
19
+ }
20
+ export declare function StructureView(props: StructureViewProps): import("react").JSX.Element;
21
+ /** Exported for hosts that want the same grouping without the chrome. */
22
+ export declare const zoneMembers: (project: ProjectDocument, zone: Zone) => SiteObject[];
@@ -0,0 +1,52 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare function Modal({ title, subtitle, onClose, children, wide, fullscreen, }: {
3
+ title: string;
4
+ subtitle?: string;
5
+ onClose: () => void;
6
+ children: ReactNode;
7
+ wide?: boolean;
8
+ fullscreen?: boolean;
9
+ }): import("react").JSX.Element;
10
+ export declare function Field({ label, value, onChange, type, suffix, min, max, step, }: {
11
+ label: string;
12
+ value: string | number;
13
+ onChange: (value: string) => void;
14
+ type?: string;
15
+ suffix?: string;
16
+ min?: number;
17
+ max?: number;
18
+ step?: number;
19
+ }): import("react").JSX.Element;
20
+ export declare function Toggle({ label, value, onChange, description, }: {
21
+ label: string;
22
+ value: boolean;
23
+ onChange: () => void;
24
+ description?: string;
25
+ }): import("react").JSX.Element;
26
+ /** A row of mutually exclusive choices, for a setting that has three answers rather than two.
27
+ * Same shape as `Toggle` — label, optional description, the control on the right — so a panel of
28
+ * switches does not acquire a second visual language for the one setting that needs a third state. */
29
+ export declare function Choice<T extends string>({ label, value, options, onChange, description, }: {
30
+ label: string;
31
+ value: T;
32
+ options: {
33
+ value: T;
34
+ label: string;
35
+ title?: string;
36
+ }[];
37
+ onChange: (value: T) => void;
38
+ description?: string;
39
+ }): import("react").JSX.Element;
40
+ /** Searchable dropdown for level selection: projects like the Silo carry a hundred floors. */
41
+ export interface FloorEntry {
42
+ id: string;
43
+ code: string;
44
+ name: string;
45
+ hint?: string;
46
+ }
47
+ export declare function FloorSelect({ entries, value, onPick, ariaLabel, }: {
48
+ entries: FloorEntry[];
49
+ value: string;
50
+ onPick: (id: string) => void;
51
+ ariaLabel: string;
52
+ }): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ export interface GraphPoint {
2
+ x: number;
3
+ y: number;
4
+ vx: number;
5
+ vy: number;
6
+ pinned?: boolean;
7
+ }
8
+ /** Bounded sampled repulsion keeps large navigation graphs out of quadratic per-frame work. */
9
+ export declare function balanceGraph(points: GraphPoint[], links: [number, number][], tick: number): void;
@@ -0,0 +1,85 @@
1
+ export declare const en: {
2
+ app: string;
3
+ brand: string;
4
+ viewer: string;
5
+ editor: string;
6
+ monitor: string;
7
+ overview: string;
8
+ floors: string;
9
+ drawings: string;
10
+ objects: string;
11
+ import: string;
12
+ export: string;
13
+ saved: string;
14
+ saving: string;
15
+ saveError: string;
16
+ outdoors: string;
17
+ tools: {
18
+ select: string;
19
+ pan: string;
20
+ wall: string;
21
+ boundary: string;
22
+ partition: string;
23
+ fence: string;
24
+ zone: string;
25
+ room: string;
26
+ rectangle: string;
27
+ hole: string;
28
+ door: string;
29
+ window: string;
30
+ gate: string;
31
+ turnstile: string;
32
+ reader: string;
33
+ camera: string;
34
+ elevator: string;
35
+ stairs: string;
36
+ office: string;
37
+ container: string;
38
+ storage: string;
39
+ poi: string;
40
+ measure: string;
41
+ adopt: string;
42
+ split: string;
43
+ enclose: string;
44
+ route: string;
45
+ sensor: string;
46
+ light: string;
47
+ alarm: string;
48
+ equipment: string;
49
+ evacuation: string;
50
+ };
51
+ navigate: {
52
+ title: string;
53
+ from: string;
54
+ to: string;
55
+ choose: string;
56
+ swap: string;
57
+ clear: string;
58
+ play: string;
59
+ pause: string;
60
+ prev: string;
61
+ next: string;
62
+ walking: string;
63
+ floors: string;
64
+ steps: string;
65
+ noGraph: string;
66
+ noGraphHintEdit: string;
67
+ noGraphHintView: string;
68
+ unreachable: string;
69
+ unreachableHint: string;
70
+ pick: string;
71
+ here: string;
72
+ hereHint: string;
73
+ levels: string;
74
+ allLevels: string;
75
+ places: string;
76
+ more: string;
77
+ };
78
+ map: {
79
+ basemapUnavailable: string;
80
+ basemapUnavailablePlan: string;
81
+ adoptMiss: string;
82
+ adoptFail: string;
83
+ };
84
+ };
85
+ export type En = typeof en;
@@ -0,0 +1,27 @@
1
+ import type { AssetRepository } from '../model/types';
2
+ /** Human-only vector evidence. Never part of the model checkpoint or its conversation. */
3
+ export interface ImportAnalysisPreview {
4
+ artifactId: string;
5
+ page: number;
6
+ units: 'm' | 'px' | 'pt';
7
+ transformId?: string;
8
+ metresPerUnit?: number;
9
+ calibration?: string;
10
+ svg: string;
11
+ /** Human-only source raster, located in the SVG display coordinate frame. */
12
+ background?: {
13
+ pngBase64: string;
14
+ bounds: [number, number, number, number];
15
+ matrix: [number, number, number, number, number, number];
16
+ };
17
+ shown: number;
18
+ total: number;
19
+ counts: Record<string, number>;
20
+ warnings: string[];
21
+ }
22
+ export declare function readAnalysisPreview(value: unknown): ImportAnalysisPreview;
23
+ export declare function saveAnalysisPreview(assets: AssetRepository, sourceId: string, preview: ImportAnalysisPreview): Promise<void>;
24
+ export declare function loadAnalysisPreview(assets: AssetRepository, sourceId: string): Promise<ImportAnalysisPreview | undefined>;
25
+ export declare const deleteAnalysisPreview: (assets: AssetRepository, sourceId: string) => Promise<void>;
26
+ /** Compose only validated local evidence. Render as an inert image, never innerHTML. */
27
+ export declare function analysisPreviewSvg(preview: ImportAnalysisPreview, hidden?: string[], showBackground?: boolean, opacity?: number): string;
@@ -0,0 +1,22 @@
1
+ /** User-supplied import scope and whole-floor exterior scale references. */
2
+ export interface ImportBrief {
3
+ buildingType?: 'apartment' | 'house' | 'office' | 'retail' | 'other';
4
+ floorCount?: number;
5
+ widthMetres?: number;
6
+ depthMetres?: number;
7
+ footprintAreaM2?: number;
8
+ }
9
+ export declare function readImportBrief(value: unknown): ImportBrief | undefined;
10
+ export declare function importBriefInstructions(brief?: ImportBrief): string;
11
+ export interface SourceCalibration {
12
+ drawingWidth?: number;
13
+ drawingDepth?: number;
14
+ drawingArea?: number;
15
+ drawingLength?: number;
16
+ realLengthMetres?: number;
17
+ basis: string;
18
+ metresPerUnit: number;
19
+ }
20
+ /** Compute scale from the user's references; never let the model silently substitute a smaller
21
+ * real-world width. Multiple references must agree within 10% in linear scale. */
22
+ export declare function calibrateImport(value: unknown, brief?: ImportBrief): SourceCalibration;
@@ -0,0 +1,49 @@
1
+ import { type ImportBrief, type SourceCalibration } from './brief';
2
+ import { type ImportInstruction } from './instructions';
3
+ /** Serializable continuation state, never a second copy of project geometry or source pixels.
4
+ * Extraction recipes restore cached backend artifacts after a worker/container restart. */
5
+ export interface ImportCheckpoint {
6
+ version: 1;
7
+ projectId: string;
8
+ brief?: ImportBrief;
9
+ phase: 'inspect' | 'build' | 'review';
10
+ notes: string;
11
+ sourcePlan: string;
12
+ calibration?: SourceCalibration;
13
+ mutationKinds: string[];
14
+ lastAssistantText: string;
15
+ lastToolReport: string;
16
+ analysis: {
17
+ artifactId: string;
18
+ sourceHash: string;
19
+ request: Record<string, unknown>;
20
+ calibration?: {
21
+ input: Record<string, unknown>;
22
+ revision: number;
23
+ };
24
+ }[];
25
+ decisions?: CandidateDecision[];
26
+ steering?: ImportInstruction[];
27
+ }
28
+ export interface CandidateDecision {
29
+ artifactId: string;
30
+ transformId: string;
31
+ floorId: string;
32
+ candidateId: string;
33
+ status: 'accepted' | 'rejected' | 'needs-review';
34
+ modelIds: string[];
35
+ }
36
+ export declare function readImportCheckpoint(value: unknown): ImportCheckpoint | undefined;
37
+ export interface ImportBudget {
38
+ inputTokens: number;
39
+ outputTokens: number;
40
+ /** Maximum provider turns in this run. Omitted by older hosts: 40. */
41
+ maxTurns?: number;
42
+ }
43
+ export declare const DEFAULT_IMPORT_BUDGET: ImportBudget;
44
+ export declare function readImportBudget(value: unknown): ImportBudget;
45
+ export interface ImportPause {
46
+ reason: 'input-budget' | 'output-budget' | 'turn-limit' | 'refusals' | 'repeated-tools';
47
+ message: string;
48
+ }
49
+ export declare function readImportPause(value: unknown): ImportPause | undefined;
@@ -0,0 +1,7 @@
1
+ export interface ImportInstruction {
2
+ id: string;
3
+ text: string;
4
+ }
5
+ export declare function readImportInstruction(value: unknown): ImportInstruction;
6
+ /** Keep the latest human steering separately from model-authored notes. */
7
+ export declare function mergeImportInstructions(...groups: ImportInstruction[][]): ImportInstruction[];
@@ -0,0 +1,10 @@
1
+ /** Disjoint provider-reported token counts. Input excludes cache reads and writes. */
2
+ export interface AiTokenUsage {
3
+ inputTokens: number;
4
+ outputTokens: number;
5
+ cacheReadTokens: number;
6
+ cacheWriteTokens: number;
7
+ }
8
+ export declare const emptyTokenUsage: () => AiTokenUsage;
9
+ export declare function addTokenUsage(a: AiTokenUsage, b: AiTokenUsage): AiTokenUsage;
10
+ export declare function readTokenUsage(value: unknown): AiTokenUsage;
@@ -0,0 +1,18 @@
1
+ import * as THREE from 'three';
2
+ /** Private cabin finishes: their lighting environment is the ceiling diffuser, even in a dark
3
+ * basement. Metals reflect this environment; the cabin walls do not glow like the old grey box. */
4
+ export declare class CabinMaterials {
5
+ readonly steel: THREE.MeshPhysicalMaterial;
6
+ readonly trim: THREE.MeshStandardMaterial;
7
+ readonly wall: THREE.MeshStandardMaterial;
8
+ readonly floor: THREE.MeshStandardMaterial;
9
+ readonly dark: THREE.MeshStandardMaterial;
10
+ readonly lamp: THREE.MeshBasicMaterial;
11
+ readonly display: THREE.MeshBasicMaterial;
12
+ readonly environment: THREE.Texture;
13
+ private target;
14
+ private textures;
15
+ private owned;
16
+ constructor(renderer: THREE.WebGLRenderer);
17
+ dispose(): void;
18
+ }
@@ -0,0 +1,13 @@
1
+ import * as THREE from 'three';
2
+ import { Reflector } from 'three/addons/objects/Reflector.js';
3
+ /** The head is visible in the mirror, but never surrounds the first-person camera. */
4
+ export declare const REFLECTION_BODY = 29;
5
+ /** MapLibre's camera encodes rotation in its projection matrix. Give the mirror a conventional
6
+ * view frame while preserving the exact world-to-clip mapping, including the chosen POV lens. */
7
+ export declare function mirrorViewCamera(source: THREE.Camera, target: THREE.PerspectiveCamera): void;
8
+ /** One live planar reflection, rendered only near a visible cabin. No recursive mirror passes. */
9
+ export declare class CabinMirror extends Reflector {
10
+ private view;
11
+ private frames;
12
+ constructor(width: number, height: number);
13
+ }
@@ -0,0 +1,11 @@
1
+ import * as THREE from 'three';
2
+ import type { SiteObject } from '../model/types';
3
+ import type { MaterialLibrary } from './materials';
4
+ /** Local metres: X along the opening, Y toward side A, Z up. Hardware moves with its leaf. */
5
+ export declare function makeDoor(door: SiteObject, wallDepth: number, height: number, materials: MaterialLibrary): {
6
+ frame: THREE.Group<THREE.Object3DEventMap>;
7
+ panels: {
8
+ group: THREE.Group;
9
+ setOpen: (value: number) => void;
10
+ }[];
11
+ };
@@ -0,0 +1,20 @@
1
+ import * as THREE from 'three';
2
+ import type { SiteObject } from '../model/types';
3
+ import type { CabinMaterials } from './CabinMaterials';
4
+ export declare const FACE_TURN: {
5
+ front: number;
6
+ right: number;
7
+ back: number;
8
+ left: number;
9
+ };
10
+ export declare function makeElevatorCabin(o: SiteObject, height: number, finish: CabinMaterials, floorCode: string, moving: boolean): THREE.Group<THREE.Object3DEventMap>;
11
+ /** The visible portion of a leaf as it retracts behind the jamb. Clipping at the opening keeps
12
+ * the concealed leaf inside its pocket even on shafts narrower than a full exposed door travel. */
13
+ export declare function doorLeafSlice(gap: number, sign: number, progress: number): {
14
+ width: number;
15
+ center: number;
16
+ };
17
+ export declare function makeCabinDoor(gap: number, height: number, sign: number, finish: CabinMaterials): {
18
+ group: THREE.Group<THREE.Object3DEventMap>;
19
+ update: (progress: number) => void;
20
+ };
@@ -0,0 +1,35 @@
1
+ import * as THREE from 'three';
2
+ import type { Point, SiteObject } from '../model/types';
3
+ import type { MaterialLibrary } from './materials';
4
+ /** Occasional ballast dropouts, not a regular strobe. Stable per fixture and elapsed time. */
5
+ export declare function fixtureOutput(id: string, seconds: number, flicker?: number): number;
6
+ export declare const MAX_FIXTURE_LIGHTS = 4;
7
+ /** How many of those throw shadows. A shadowed lamp is six shadow passes when it moves and a cube
8
+ * of shadow taps on every pixel every frame; the two nearest carry the room, and the two beyond
9
+ * them light without shadow, which nobody notices past the first two. The count is held constant
10
+ * — an empty slot keeps its flag — because the shader is compiled for a number of shadowed lights
11
+ * and changing that number recompiles every material in the scene. */
12
+ export declare const SHADOWED_FIXTURE_LIGHTS = 2;
13
+ /** All fittings are two instanced draws. Only the nearest few cast light/shadows, keeping the
14
+ * shader and shadow-map budget constant whether the document contains ten lamps or ten thousand. */
15
+ export declare class FixtureLights {
16
+ private fixtures;
17
+ private points;
18
+ private colors;
19
+ private emitters;
20
+ private lights;
21
+ private active;
22
+ private lastFlicker;
23
+ private flickering;
24
+ constructor(scene: THREE.Scene, fixtures: SiteObject[], xy: (p: Point) => Point, base: number, materials: MaterialLibrary);
25
+ /** The shadows go to the nearest lamps, and stay with them until a lamp without one is clearly
26
+ * nearer — a swap redraws a shadow, so a walker straddling two lamps must not swap on every step.
27
+ * Always exactly SHADOWED_FIXTURE_LIGHTS flags are set, empty slots included: see the constant. */
28
+ private assignShadows;
29
+ /** Every lamp's shadow is stale — the scene changed under them. */
30
+ invalidate(): void;
31
+ update(eye: THREE.Vector3, seconds: number): {
32
+ shadowsChanged: boolean;
33
+ animate: boolean;
34
+ };
35
+ }