@minmaps-dev/mm-web-sdk 1.0.0-rc.3 → 1.0.0-rc.30

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.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { IControl, ControlPosition, Map } from 'maplibre-gl';
1
+ import { IControl, ControlPosition, StyleSpecification, Map } from 'maplibre-gl';
2
2
 
3
3
  /**
4
4
  * Represents a floor in a building
@@ -6,6 +6,12 @@ import { IControl, ControlPosition, Map } from 'maplibre-gl';
6
6
  interface Floor {
7
7
  /** Unique floor identifier */
8
8
  id: string | number;
9
+ /**
10
+ * Underlying map entity id. Separate from `id` (the floor entity id) — used
11
+ * by waypoints (`wp.mapId`) and floor-geojson endpoints. Optional because
12
+ * not all providers populate it.
13
+ */
14
+ mapId?: string | number;
9
15
  /** Display name of the floor */
10
16
  name: string;
11
17
  /** Floor sequence/order (e.g., -1 for basement, 0 for ground, 1 for first floor) */
@@ -32,7 +38,7 @@ interface POI {
32
38
  /** Unique identifier */
33
39
  id: string | number;
34
40
  /** POI type */
35
- type: 'amenity' | 'destination';
41
+ type: 'amenity' | 'destination' | 'kiosk';
36
42
  /** Display name */
37
43
  name: string;
38
44
  /** Geographic coordinates [longitude, latitude] */
@@ -55,6 +61,9 @@ interface POI {
55
61
  waypoint?: Waypoint;
56
62
  /** Marks this POI as the "You are here" kiosk */
57
63
  isYouAreHere?: boolean;
64
+ /** For kiosk POIs only: compass heading the device physically faces, in
65
+ * degrees clockwise from north. Sourced from JACS `Device.heading`. */
66
+ heading?: number | null;
58
67
  }
59
68
  /**
60
69
  * Amenity - a specific type of POI (facilities, services)
@@ -64,8 +73,25 @@ interface Amenity {
64
73
  id: string | number;
65
74
  /** Display name */
66
75
  name: string;
67
- /** SVG icon data */
76
+ /** Inline SVG markup (legacy fullcall DTO). When absent, icon is resolved from `uris`. */
68
77
  svg?: string;
78
+ /**
79
+ * Hosted icon descriptors from JACS (`uris[].locales[].uriPath`). Used by
80
+ * the SDK's runtime icon fetcher to register a map image at init time.
81
+ */
82
+ uris?: Array<{
83
+ mimeType?: string;
84
+ resourceType?: string;
85
+ locales?: Array<{
86
+ locale?: string;
87
+ uriPath?: string;
88
+ }>;
89
+ }>;
90
+ /**
91
+ * Map-image id used as `icon-image` in the POI style layer. Decorated
92
+ * in-place by AmenityIcons.register once the SVG is fetched and registered.
93
+ */
94
+ iconId?: string;
69
95
  /** Search keywords */
70
96
  keywords?: string[];
71
97
  /** Associated waypoints */
@@ -89,6 +115,20 @@ interface Destination {
89
115
  category?: string;
90
116
  /** Additional properties */
91
117
  properties?: Record<string, unknown>;
118
+ /** Uploaded image (e.g. brand/store logo). JACS `/all` fullcall serves this
119
+ * as `{ items: [{ resourceType, mimeType, path }] }`; the legacy/basic shape
120
+ * is `[{ locales: [{ uriPath }] }]`. `DestinationIcons.register` handles both. */
121
+ uris?: unknown;
122
+ /** Free-form CMS metadata (reserved for future destination icon options). */
123
+ extensors?: Record<string, unknown>;
124
+ /** Map-manager "Label Display" mode (JACS `displayMode`):
125
+ * `0` Hidden (dot only) · `1` Label (dot + text) · `2` Image (uploaded logo).
126
+ * Undefined → treated as Label (backwards-compatible default). */
127
+ displayMode?: number;
128
+ /** Runtime-assigned map-image id, decorated in place by
129
+ * `DestinationIcons.register` (only for `displayMode === 2`).
130
+ * Read by the POI builder + theme layer. */
131
+ iconId?: string;
92
132
  }
93
133
  /**
94
134
  * Waypoint - a specific point location
@@ -140,6 +180,83 @@ type ViewOptions = {
140
180
  /** SW/NE bounding box */
141
181
  type Bounds = [[number, number], [number, number]];
142
182
 
183
+ /** How to center the map after computing a route */
184
+ type WayfindCenterMode = 'none' | 'destination' | 'route';
185
+ /**
186
+ * Per-call routing preferences forwarded to the wayfinding provider.
187
+ *
188
+ * - `accessible` — prefer accessible paths; providers should drop or heavily
189
+ * penalize edges marked inaccessible (stairs by default, anything else
190
+ * the provider's data flags).
191
+ * - `avoidStairs` — hard-filter stairs from the graph.
192
+ *
193
+ * The bundled stub provider doesn't compute routes; consumers wire a real
194
+ * provider (see `mm-web-sdk-example/lib/wayfinding`) that reads these
195
+ * flags when building edge weights.
196
+ */
197
+ type RoutingOptions = {
198
+ accessible?: boolean;
199
+ avoidStairs?: boolean;
200
+ };
201
+
202
+ /**
203
+ * A single point along the rendered route. `mapId` lets the renderer
204
+ * split the polyline by floor (so other-floor segments don't paint
205
+ * through the visible floor's geometry); `waypointId` and `pathTypeId`
206
+ * let downstream layers (text-directions, step UI) classify
207
+ * transitions — e.g. "Take the elevator" vs "Walk straight". The two
208
+ * endpoint points are the kiosk and destination anchors (`isEndpoint`
209
+ * marks them so they can render endpoint pins). All fields except
210
+ * `coordinates` are optional for backward-compat with the straight-line
211
+ * fallback.
212
+ */
213
+ type RoutePoint = {
214
+ coordinates: [number, number];
215
+ /** Floor mapId this point sits on. Missing on the straight-line fallback. */
216
+ mapId?: number;
217
+ /** JACS waypoint id this point resolved to, if any. */
218
+ waypointId?: number;
219
+ /** Path type of the edge that LEADS INTO this point (i.e. the edge
220
+ * whose `toKey` is this node). Used to detect elevator / stair /
221
+ * escalator transitions. Missing on the start point and on the
222
+ * straight-line fallback. */
223
+ pathTypeId?: number;
224
+ /** True for the snapped kiosk + destination anchors. */
225
+ isEndpoint?: boolean;
226
+ };
227
+
228
+ type WayfindStep = DepartStep | TurnStep | ContinueStep | TransitionStep$1 | ArriveStep;
229
+ type StepBase = {
230
+ /** Indices into the input `points` array that this step spans. */
231
+ pointRange: [startInclusive: number, endInclusive: number];
232
+ /** Floor this step is on. `null` for cross-floor transitions. */
233
+ floorId: number | null;
234
+ /** Localized instruction text. */
235
+ text: string;
236
+ /** Total walking distance within this step, in meters (rounded). */
237
+ distanceMeters?: number;
238
+ /** Landmark name used for anchoring, if any. */
239
+ landmark?: string;
240
+ };
241
+ type DepartStep = StepBase & {
242
+ type: 'depart';
243
+ };
244
+ type TurnStep = StepBase & {
245
+ type: 'turn-left' | 'turn-right' | 'u-turn';
246
+ };
247
+ type ContinueStep = StepBase & {
248
+ type: 'continue';
249
+ };
250
+ type TransitionStep$1 = StepBase & {
251
+ type: 'transition';
252
+ transition: 'elevator' | 'stairs' | 'escalator';
253
+ fromFloorId: number | null;
254
+ toFloorId: number | null;
255
+ };
256
+ type ArriveStep = StepBase & {
257
+ type: 'arrive';
258
+ };
259
+
143
260
  interface MapEvent {
144
261
  floor?: Floor;
145
262
  poi?: POI;
@@ -147,18 +264,156 @@ interface MapEvent {
147
264
  error?: any;
148
265
  venue?: any;
149
266
  camera?: CameraState;
267
+ /** Emitted by `themeChanged`; `'custom'` when consumer passed a raw style. */
268
+ theme?: 'default' | 'high-contrast' | 'custom';
269
+ /** Emitted by `localeChanged` after `setLocale()` finishes patching the
270
+ * venue model. BCP-47 code (`'en'`, `'es'`, `'es-MX'`). */
271
+ locale?: string;
272
+ /** Emitted by `routeReady` after a successful `navigateFromKioskToPOI`.
273
+ * Carries the raw geometry (`points`) and the human-readable
274
+ * step-by-step directions. Subscribe to drive a turn-by-turn UI. */
275
+ route?: {
276
+ points: RoutePoint[];
277
+ steps: WayfindStep[];
278
+ /** Floor mapId of each segment, in render order. `null` for
279
+ * segments emitted by the straight-line fallback. */
280
+ floorIds: Array<number | null>;
281
+ };
150
282
  }
151
283
  type EventCallback = (event: MapEvent) => void;
152
284
 
285
+ /**
286
+ * Padding kept clear inside the viewport when the SDK frames bounds —
287
+ * the initial venue/floor fit and the wayfinding route fit. A plain
288
+ * number pads all sides equally; the object form lets a kiosk reserve
289
+ * space for fixed UI overlays (header, dock, side rails) so framed
290
+ * content is never hidden behind them.
291
+ */
292
+ type BoundsPadding = number | {
293
+ top: number;
294
+ bottom: number;
295
+ left: number;
296
+ right: number;
297
+ };
298
+ /**
299
+ * Built-in theme names accepted by `MinuteMaps.setTheme()` and the
300
+ * `options.theme` init option. Pass a `StyleSpecification` directly for
301
+ * custom themes.
302
+ */
303
+ type ThemeName = 'default' | 'high-contrast';
304
+ /**
305
+ * Style of the disc + ring rendered behind each amenity icon. The SDK
306
+ * composites this into the icon bitmap at registration time, so badge +
307
+ * icon participate in symbol collision as one unit.
308
+ */
309
+ type AmenityBadgeStyle = {
310
+ /** Fill colour of the badge disc. Default `'#fdb81e'`. */
311
+ color?: string;
312
+ /** Stroke colour of the ring around the disc. Default `'#FFFFFF'`. */
313
+ ringColor?: string;
314
+ /** Ring width in logical px. Default `2`. */
315
+ ringWidth?: number;
316
+ };
317
+ type DestinationChipStyle = {
318
+ /** Chip fill colour. Default `'#FFFFFF'`. */
319
+ color?: string;
320
+ /** Chip border colour. Default navy `'#162e51'`. */
321
+ borderColor?: string;
322
+ /** Chip border width in logical px. Default `2`. */
323
+ borderWidth?: number;
324
+ };
153
325
  interface SDKOptions {
154
326
  debug?: boolean;
155
- theme?: 'light' | 'dark' | 'hybrid' | any;
327
+ /**
328
+ * Initial theme. `'default'` uses the bundled hybrid 3D theme.
329
+ * `'high-contrast'` uses the WCAG-AA tuned theme. Pass a
330
+ * `StyleSpecification` for fully custom styling.
331
+ */
332
+ theme?: ThemeName | any;
333
+ /**
334
+ * When true, the SDK skips animations on imperative camera calls
335
+ * (wayfinding fits, `setView`, idle re-frames). Consumers should
336
+ * mirror their app's `prefers-reduced-motion` state into this.
337
+ */
338
+ reducedMotion?: boolean;
339
+ /**
340
+ * Whether a freshly computed route auto-highlights its first step's
341
+ * segment (the brighter `route-line-active` overlay). Defaults to
342
+ * `true`, preserving the turn-by-turn segment highlight that
343
+ * `setActiveStep` drives.
344
+ *
345
+ * Set `false` for kiosk-style "show the whole route at once" UIs:
346
+ * the entire route line is shown without singling out one segment,
347
+ * which reads more clearly at a glance and avoids implying the visitor
348
+ * must step through the route. `setActiveStep` still works when called
349
+ * explicitly (e.g. a paginated QR-handoff / mobile flow) — this only
350
+ * controls the automatic highlight on `routeReady`.
351
+ */
352
+ routeStepHighlight?: boolean;
156
353
  initialFloor?: string | number;
157
354
  enableInteractions?: boolean;
158
355
  customSprite?: string;
159
356
  minIndoorZoom?: number;
160
357
  wallThickness?: number;
161
- boundsPadding?: number;
358
+ boundsPadding?: BoundsPadding;
359
+ /**
360
+ * Pitch (deg) for the opening view, applied centred on the kiosk ("You
361
+ * are here"). Omit for top-down.
362
+ */
363
+ initialPitch?: number;
364
+ /**
365
+ * Zoom for the opening view, centred on the kiosk. Set this above the
366
+ * theme's unit-walls breakpoint (~16.5 in the bundled hybrid theme) so the
367
+ * kiosk opens on the building's interior floor plan (its contents) rather
368
+ * than a zoomed-out 3D massing outline. Omit to keep the post-floor-fit
369
+ * zoom.
370
+ */
371
+ initialZoom?: number;
372
+ /**
373
+ * Clamp zoom-out relative to the opening view. When set, the map's
374
+ * `minZoom` becomes `(initialZoom − this)`, so visitors can nudge out by
375
+ * this many zoom levels but never pull back below the interior into the
376
+ * massing/region. `0` locks zoom-out exactly to the opening view.
377
+ */
378
+ minZoomBelowInitialFit?: number;
379
+ /**
380
+ * Override the colour the SDK recolours every amenity SVG to before it
381
+ * composites the badge. `none` / `transparent` fills are preserved so
382
+ * cut-outs stay. When the badge is enabled (the default), this defaults
383
+ * to navy (`#162e51`) so icons read on gold. Set explicitly for a
384
+ * different look, or set `amenityBadge: false` to disable recolouring
385
+ * altogether and keep the CMS-uploaded colours.
386
+ */
387
+ amenityIconColor?: string;
388
+ /**
389
+ * Badge composited behind each amenity icon. Pass `false` to render the
390
+ * icon alone (no badge — useful for high-contrast or 2D themes where the
391
+ * gold disc would compete with the floor). Pass an object to tune the
392
+ * disc / ring style. Defaults to a VA-gold disc with a 2px white ring.
393
+ *
394
+ * The badge is baked into the icon bitmap (canvas composite) rather than
395
+ * drawn as a separate circle layer, so badge + icon participate in symbol
396
+ * collision together — overlapping amenities hide as one unit instead of
397
+ * the icon hiding while the disc stays painted.
398
+ */
399
+ amenityBadge?: AmenityBadgeStyle | false;
400
+ /**
401
+ * Badge for vertical-circulation connectors (elevator / stairs / escalator).
402
+ * These render as their own visual class — a navy disc with a white glyph —
403
+ * so circulation reads distinct from the gold service amenities. Pass `false`
404
+ * to fall them back into the regular gold badge, or an object to tune the
405
+ * disc / ring. When `amenityBadge` is `false`, connectors are badge-less too
406
+ * unless this is set explicitly.
407
+ */
408
+ connectorBadge?: AmenityBadgeStyle | false;
409
+ /**
410
+ * Chip composited behind each destination's uploaded logo. Destinations are
411
+ * full-colour brand images (not recoloured), so they get a neutral white
412
+ * rounded chip for legibility instead of the amenity gold badge. Pass `false`
413
+ * to render the logo bare, or an object to tune the chip fill / border.
414
+ * Defaults to a white chip with a 2px navy border.
415
+ */
416
+ destinationChip?: DestinationChipStyle | false;
162
417
  styleMode?: 'venueStyleUrl' | 'sdkTemplate';
163
418
  templateOverrideMode?: 'colorsOnly' | 'colorsAndConstants' | 'all';
164
419
  }
@@ -190,6 +445,13 @@ type SDKConfig = {
190
445
  venueId: number;
191
446
  locale?: string;
192
447
  auth?: JMapAuth;
448
+ /**
449
+ * Identifies which physical kiosk this instance is, so the SDK can pin
450
+ * the "You are here" marker. Matched against either the numeric device
451
+ * `id` or the device `uuid` from the venue's `devices`. The device's
452
+ * attached waypoint (`waypoint.deviceIds`) is the kiosk's location.
453
+ */
454
+ deviceId?: string | number;
193
455
  };
194
456
  jacs: {
195
457
  mode: 'proxy' | 'direct';
@@ -204,9 +466,6 @@ type SDKConfig = {
204
466
  options?: SDKOptions;
205
467
  };
206
468
 
207
- /** How to center the map after computing a route */
208
- type WayfindCenterMode = 'none' | 'destination' | 'route';
209
-
210
469
  type LoggerFn = (...args: unknown[]) => void;
211
470
  type AmenityManagerDeps = {
212
471
  getVenue: () => any;
@@ -224,6 +483,19 @@ declare class AmenityManager {
224
483
  private log;
225
484
  private loadForFloor;
226
485
  getAll(): AmenityWithFloor[];
486
+ /**
487
+ * Venue-wide amenities, one entry per id. `getAll()` returns each amenity
488
+ * once per floor it appears on (and so renders a multi-floor "Bathroom"
489
+ * three times in a venue list); this collapses those into a single record
490
+ * with all its waypoints merged across floors.
491
+ *
492
+ * Also folds any duplicate-id records from the provider (a CMS data bug)
493
+ * into a single entry so React lists don't trip on dup keys.
494
+ *
495
+ * Use this for venue-wide browse UIs; pair it with `findClosestWaypoint`
496
+ * (on the SDK) to resolve a tap to the nearest physical instance.
497
+ */
498
+ getDistinct(): Amenity[];
227
499
  getByFloorId(floorId: Floor['id']): AmenityWithFloor[];
228
500
  getAllKiosks(): AmenityWithFloor[];
229
501
  getKioskForFloor(floorId: Floor['id']): AmenityWithFloor | null;
@@ -243,8 +515,25 @@ declare class MinuteMaps {
243
515
  private readonly logger;
244
516
  private defaultCamera;
245
517
  private viewModes;
518
+ /** Mirror of `config.options.reducedMotion`, mutable via `setReducedMotion`. */
519
+ private reducedMotion;
520
+ /** Tracks the active theme name so `setTheme()` is a no-op when re-requested. */
521
+ private activeThemeName;
522
+ /** Tracks the active locale so `setLocale()` is a no-op when re-requested.
523
+ * Seeded from `config.jmap.locale` at construction; `undefined` means
524
+ * whatever the customer's default locale is on the JACS side. */
525
+ private currentLocale;
246
526
  private amenityManager;
247
527
  private wayfinding;
528
+ private highlightManager;
529
+ private youAreHerePulse;
530
+ /** Categorical POI layers in the bundled theme that `setPOIFilter`
531
+ * toggles. Layers absent from this list — `poi-you-are-here-*`, the
532
+ * `poi-accessibility-icons` badge, and the `poi-highlight-*` layers —
533
+ * are intentionally unaffected; they're either anchors, status
534
+ * indicators, or transient focus state, not categorical content. */
535
+ private static readonly POI_CATEGORY_LAYERS;
536
+ private poiVisibleTypes;
248
537
  constructor(config: SDKConfig);
249
538
  init(): Promise<void>;
250
539
  on(event: string, cb: (e: MapEvent) => void): void;
@@ -256,9 +545,69 @@ declare class MinuteMaps {
256
545
  animate?: boolean;
257
546
  duration?: number;
258
547
  }): void;
548
+ /**
549
+ * Content-aware re-frame: fit an active route if one is displayed,
550
+ * otherwise fit the current floor's geojson. Unlike `resetView()`, this
551
+ * preserves the user's pitch and bearing — it only adjusts center/zoom
552
+ * to bring the scene back into view. Intended use is after the map
553
+ * container resizes (e.g. a side drawer opens beside the map and the
554
+ * pane gets narrower), so what matters stays framed without a yank.
555
+ */
556
+ refit(opts?: {
557
+ animate?: boolean;
558
+ duration?: number;
559
+ }): void;
560
+ /**
561
+ * Update the bounds padding (camera insets) used by route + floor fits.
562
+ * Consumers measure their on-screen chrome (panels, rails) and pass the
563
+ * insets so a fit frames the path in the visible, unobscured area rather
564
+ * than under the UI. Stored; the next fit / `refit()` uses it (clamped so
565
+ * a fit always lands — see `clampPadding`).
566
+ */
567
+ setBoundsPadding(p: BoundsPadding): void;
568
+ private fitToBounds;
259
569
  set3dEnabled(enabled: boolean): void;
260
570
  toggle3d(): void;
261
571
  getIs3dEnabled(): boolean;
572
+ /**
573
+ * Set the active map theme by name (`'default'` / `'high-contrast'`) or by
574
+ * passing a custom `StyleSpecification`. The current floor, route, POIs
575
+ * and camera are preserved across the swap — only layer styling changes.
576
+ *
577
+ * Used by kiosk consumers to wire the OS-level / popover high-contrast
578
+ * toggle to the map. See `KIOSK.md` for the pattern.
579
+ */
580
+ setTheme(theme: ThemeName | StyleSpecification): Promise<void>;
581
+ /** Currently active theme name (or `'custom'` when a raw style was passed). */
582
+ getActiveTheme(): 'default' | 'high-contrast' | 'custom';
583
+ /**
584
+ * Update the SDK's reduced-motion preference at runtime. When true,
585
+ * imperative camera animations (route fits, idle re-frames) and the
586
+ * route line-draw animation run with `duration: 0`.
587
+ */
588
+ setReducedMotion(enabled: boolean): void;
589
+ getReducedMotion(): boolean;
590
+ /**
591
+ * Switch the locale used for POI / amenity / destination / floor names at
592
+ * runtime. The SDK refetches localized names from JACS (per-entity
593
+ * endpoints — `/all` does not honor `?locale=`), patches the in-memory
594
+ * venue model in place, then redraws the POI source for the current floor
595
+ * so map labels render in the new language.
596
+ *
597
+ * Structural data (waypoints, coordinates, iconIds) is preserved — only
598
+ * the localized fields (`name`, `description`) move.
599
+ *
600
+ * Emits a `localeChanged` event with the new code after the patch
601
+ * completes. No-op when the code matches the current locale.
602
+ *
603
+ * Throws if the underlying data provider doesn't implement locale support
604
+ * (only `JacsDataProvider` does today), or if the JACS fetch fails — the
605
+ * prior locale is left intact in that case.
606
+ */
607
+ setLocale(locale: string): Promise<void>;
608
+ /** Currently active locale (BCP-47), or `undefined` when no locale has
609
+ * been set — meaning JACS resolves names to the customer's default. */
610
+ getActiveLocale(): string | undefined;
262
611
  setUnits2dEnabled(enabled: boolean): void;
263
612
  toggleUnits2d(): void;
264
613
  getIsUnits2dEnabled(): boolean;
@@ -274,21 +623,239 @@ declare class MinuteMaps {
274
623
  getAllPOIs(floor?: Floor): POI[];
275
624
  getYouAreHerePOI(floor?: Floor): POI | null;
276
625
  getYouAreHereCoordinates(floor?: Floor): [number, number] | null;
626
+ /**
627
+ * Compass heading the kiosk's physical hardware faces, in degrees
628
+ * clockwise from north. Sourced from JACS `Device.heading`. `null` when
629
+ * the venue's device record has no heading set.
630
+ *
631
+ * In order to rotate the map so it is oriented towards the user's gaze,
632
+ * 180 is subtracted from `Device.heading`.
633
+ *
634
+ * Drives `recenterOnKiosk({ useHeading: true })` and the directional
635
+ * cone rendered next to the "You are here" marker.
636
+ */
637
+ getKioskHeading(): number | null;
638
+ /**
639
+ * Display name of the JACS device the kiosk is anchored to (e.g.
640
+ * "Main Lobby Kiosk", "Information Desk Kiosk"). `null` when the venue
641
+ * has no kiosk anchor or the device record carries no `name`.
642
+ *
643
+ * Distinct from `getYouAreHerePOI()?.name`, which is always the
644
+ * literal "You are here" label rendered on the map. Use this for chrome
645
+ * that needs the kiosk's identity — e.g. the directions overlay's
646
+ * "From <kiosk>" header.
647
+ */
648
+ getKioskName(): string | null;
649
+ /**
650
+ * Distance + walking time from the kiosk's anchor waypoint to a target,
651
+ * computed over the JACS path graph (the same graph the routing engine
652
+ * uses). Designed for "Closest: 30 sec walk" subtitles in the consumer
653
+ * UI — no second graph build, no second Dijkstra implementation.
654
+ *
655
+ * Accepts a raw waypoint id, an amenity / destination object (the first
656
+ * entry of its `waypoints` array is treated as the entry point), or any
657
+ * `{ id, mapId? }` shape. Returns `null` when the kiosk isn't anchored,
658
+ * the target waypoint isn't on the graph, or no path resolves.
659
+ *
660
+ * Walking speed defaults to 1.2 m/s (indoor wayfinding norm). Pass
661
+ * `walkingSpeedMps` to estimate for accessibility (e.g. 0.9).
662
+ */
663
+ getWalkTimeFromKiosk(target: number | string | {
664
+ id?: number | string;
665
+ mapId?: number;
666
+ } | {
667
+ waypoints?: Array<number | string | {
668
+ id?: number | string;
669
+ }>;
670
+ }, opts?: {
671
+ walkingSpeedMps?: number;
672
+ }): {
673
+ meters: number;
674
+ seconds: number;
675
+ pathNodeCount: number;
676
+ } | null;
677
+ /**
678
+ * Center the camera on the kiosk's "You are here" position. Use this
679
+ * (not `resetView`) for a chrome "Recenter" button — `resetView` snaps
680
+ * to the SDK's captured default camera, which may have been a venue
681
+ * overview rather than the kiosk's spot.
682
+ *
683
+ * When `useHeading` is true (default) and the kiosk device has a
684
+ * `heading` on file, the camera's bearing is rotated so the direction
685
+ * the kiosk physically faces ends up at the top of the screen. That
686
+ * way "what the user sees in front of them" matches "what's up on the
687
+ * map" — the single largest readability win for stressed indoor users.
688
+ */
689
+ recenterOnKiosk(opts?: {
690
+ useHeading?: boolean;
691
+ zoom?: number;
692
+ pitch?: number;
693
+ animate?: boolean;
694
+ duration?: number;
695
+ }): void;
277
696
  searchPOIs(query: string, floor?: Floor): POI[];
697
+ /**
698
+ * Search POIs across every floor in the venue. Returns raw matches —
699
+ * an amenity that exists on multiple floors appears multiple times,
700
+ * once per instance. Consumers that need one row per logical amenity
701
+ * should dedupe by id (and, for "nearest", pick the instance closest
702
+ * to the kiosk via planar distance on `coordinates`).
703
+ */
704
+ searchAllPOIs(query: string): POI[];
278
705
  wayfindBetweenWaypoints(fromWaypoint: any, toWaypoint: any, options?: {
279
706
  centerMode?: 'none' | 'destination' | 'route';
280
707
  zoom?: number;
708
+ /** Prefer accessible paths (drops or penalizes stairs / inaccessible edges). */
709
+ accessible?: boolean;
710
+ /** Hard-filter stairs from the graph. */
711
+ avoidStairs?: boolean;
281
712
  }): Promise<any>;
282
713
  navigateFromKioskToDestination(destination: any): Promise<any>;
714
+ /**
715
+ * Route from the kiosk to a specific POI's waypoint. Use this when you
716
+ * already hold a resolved POI (e.g. from `searchPOIs` / `searchAllPOIs`).
717
+ * For a venue-wide amenity record with multiple instances, resolve via
718
+ * `findClosestWaypoint` first — or use `highlightAmenity` instead, if
719
+ * you want a visual focus rather than a route.
720
+ */
721
+ navigateFromKioskToPOI(poi: POI, options?: {
722
+ accessible?: boolean;
723
+ avoidStairs?: boolean;
724
+ }): Promise<any>;
725
+ /** Build the landmark / floor-name context the directions module needs.
726
+ * Pulled out so `setLocale()` or `setActiveStep()` can rebuild it on
727
+ * demand if we ever surface a locale-aware variant. */
728
+ private buildDirectionsContext;
729
+ /**
730
+ * Drive the turn-by-turn UI. Two effects:
731
+ *
732
+ * 1. When the step's `floorId` differs from the active floor, switch
733
+ * floors so the segment for the step's leg becomes visible.
734
+ * 2. Highlight the step's slice of the route line by writing the
735
+ * point range to the `route-active-step` source; the bundled
736
+ * theme's `route-line-active` layer paints it in gold over the
737
+ * muted base route.
738
+ *
739
+ * Transition steps (cross-floor elevator / stair hops) clear the
740
+ * highlight — the overlay text carries the action, and there's no
741
+ * meaningful on-floor segment to paint.
742
+ *
743
+ * Pass `null` to clear the highlight without changing the floor or
744
+ * tearing down the route.
745
+ */
746
+ setActiveStep(step: WayfindStep | null): Promise<void>;
747
+ private writeActiveStepHighlight;
748
+ private clearActiveStepHighlight;
283
749
  clearRoute(): void;
750
+ /**
751
+ * Highlight a single POI on the map — a pulsing ring — and bring it into
752
+ * view. Resolves the POI across all floors, switching the active floor if
753
+ * it lives on another one. Pass a POI / amenity / destination id.
754
+ */
755
+ highlightPOI(id: string | number): Promise<void>;
756
+ /** Remove the POI highlight set by `highlightPOI`. */
757
+ clearHighlight(): void;
758
+ /**
759
+ * Show only the named POI categories on the map. Pass `['amenity']` to
760
+ * emphasize amenities (e.g. while an Amenities drawer is open) — destination
761
+ * POIs hide. Call `clearPOIFilter()` to restore the default (all categories).
762
+ *
763
+ * The "You are here" marker, the wheelchair-accessibility badge, and any
764
+ * active highlight are not affected and always render.
765
+ */
766
+ setPOIFilter(types: Array<'amenity' | 'destination'>): void;
767
+ /** Restore the default — every POI category renders. */
768
+ clearPOIFilter(): void;
769
+ private applyPOIFilter;
770
+ /**
771
+ * Return the waypoint in `waypoints` closest to `from`. Useful for
772
+ * "route to the nearest X" against a venue-wide amenity record that has
773
+ * multiple physical instances. Defaults `from` to the kiosk's
774
+ * "You are here" coordinates.
775
+ *
776
+ * Distance is planar Euclidean on lng/lat — sufficient for ordering at
777
+ * single-venue scale, and avoids a turf dependency on the hot path.
778
+ */
779
+ findClosestWaypoint(waypoints: Waypoint[], from?: [number, number]): Waypoint | null;
780
+ /**
781
+ * Route from the kiosk to the closest physical instance of an amenity.
782
+ * Resolves the amenity venue-wide, picks the instance with the smallest
783
+ * path-graph walk time from the kiosk (falling back to planar Euclidean
784
+ * when the walk-time graph can't resolve any waypoint), switches the
785
+ * active floor if needed, and routes via the same plumbing as
786
+ * `navigateFromKioskToPOI` — so step-by-step directions, the
787
+ * `routeReady` event, and the active-floor camera fit all "just work."
788
+ *
789
+ * Pass `accessible` / `avoidStairs` to weight the *route* (the SDK's
790
+ * routing engine reads these per call). Note: closest-instance picking
791
+ * itself is currently distance-based and does not yet honor those
792
+ * weights — the underlying walk-time graph applies pixel-length only.
793
+ * In practice this matters when an amenity has multiple instances and
794
+ * the geometrically closest is reachable only via stairs; the picked
795
+ * instance won't change today, but the *route* to it will avoid stairs
796
+ * (or fail gracefully) if `accessible`/`avoidStairs` is set.
797
+ */
798
+ navigateFromKioskToClosestAmenity(amenityId: string | number, options?: {
799
+ accessible?: boolean;
800
+ avoidStairs?: boolean;
801
+ }): Promise<any>;
802
+ /**
803
+ * Highlight + center on the closest physical instance of an amenity.
804
+ * The amenity is resolved against `amenities.getDistinct()` (venue-wide,
805
+ * deduped); the closest waypoint to the kiosk is picked; the active floor
806
+ * is switched if that instance lives on another one.
807
+ *
808
+ * This is the right call from a venue-wide list ("here are all the
809
+ * bathrooms in the building, take me to the closest one"). For taps on
810
+ * a specific resolved POI (e.g. a search result that already names one
811
+ * instance), use `highlightPOI` instead.
812
+ */
813
+ highlightAmenity(amenityId: string | number): Promise<void>;
814
+ /** Which floor owns this waypoint, by matching mapId against the floor's
815
+ * `mapId` (or `id` as fallback). Used to switch floors when routing /
816
+ * highlighting hits an instance on a different one. */
817
+ private findFloorForWaypoint;
284
818
  getCameraPosition(): CameraState | null;
285
819
  getMap(): Map | null;
286
820
  destroy(): void;
287
821
  setCurrentFloor(floor: Floor): Promise<void>;
822
+ /**
823
+ * Filter the route-line / route-halo layers to only render segments
824
+ * whose `floorId` matches the active floor (or features that carry no
825
+ * `floorId` at all — the straight-line fallback, which we want visible
826
+ * on every floor since it has no floor membership to filter against).
827
+ *
828
+ * Features are tagged with the JACS pixel `mapId` (that's what flows
829
+ * through the route point's `mapId`), so the filter compares against
830
+ * the active floor's `mapId`, NOT its `id` — those are two different
831
+ * JACS identifiers (`floor.id` is the building-floor record; `mapId`
832
+ * is the SVG asset). We accept the floor's `id` here for convenience
833
+ * and resolve to `mapId` via `getFloors()`.
834
+ *
835
+ * Applied imperatively rather than baked into the theme JSON so the
836
+ * filter tracks runtime floor changes without restyling the map.
837
+ */
838
+ private updateRouteFloorFilter;
288
839
  isReady(): boolean;
289
840
  private setFloorLayerVisibility;
290
841
  private getBoundsPadding;
842
+ /**
843
+ * Shrink per-side padding if the requested insets would leave no room.
844
+ * `cameraForBounds` / `fitBounds` silently fail ("Map cannot fit within
845
+ * canvas") when an axis's total padding meets or exceeds that axis — cap
846
+ * each axis's total at 80% so a fit always has space to land.
847
+ */
848
+ private clampPadding;
291
849
  private getVenueBounds;
850
+ /**
851
+ * Opening-framing policy applied once after the initial floor fit.
852
+ * - `initialPitch`: tilt to this and re-fit the building footprint so the
853
+ * kiosk opens on a tilted building instead of a flat top-down plan.
854
+ * - `minZoomBelowInitialFit`: clamp the map's minZoom to
855
+ * `(fitZoom − margin)` so visitors can't pull back to the empty region.
856
+ * Both are no-ops when their option is unset.
857
+ */
858
+ private applyInitialFraming;
292
859
  private applyInitialViewFromVenue;
293
860
  private loadAndPatchVenueStyle;
294
861
  private ensureFloorLayersFromStyle;
@@ -296,5 +863,42 @@ declare class MinuteMaps {
296
863
  }
297
864
  declare function createMinuteMapsSDK(config: SDKConfig): MinuteMaps;
298
865
 
299
- export { MinuteMaps, createMinuteMapsSDK };
300
- export type { Amenity, AmenityWithFloor, Bounds, CameraState, Destination, EventCallback, Floor, FloorMetadata, JMapAuth, JMapConfig, JacsAuth, JacsConfig, MapEvent, POI, POISearchResult, SDKConfig, SDKOptions, ViewOptions, WayfindCenterMode, Waypoint };
866
+ type TransitionStep = Extract<WayfindStep, {
867
+ type: 'transition';
868
+ }>;
869
+ type RouteFloorSection = {
870
+ /** Floor (JACS `mapId`) this section is on. `null` only for a degenerate
871
+ * floorless route (straight-line fallback with no `mapId`). */
872
+ floorId: number | null;
873
+ /** This floor's steps, each with its index into the original flat array
874
+ * so the consumer can call `setActiveStep` / highlight by index. */
875
+ steps: Array<{
876
+ step: WayfindStep;
877
+ index: number;
878
+ }>;
879
+ /** Index of this section's first step in the original array — the target
880
+ * for `setActiveStep` when advancing INTO this section. */
881
+ firstStepIndex: number;
882
+ /** The transition step that leaves this floor for the next section, if
883
+ * any. Absent on the final section. Its `text` ("Take the elevator to
884
+ * Basement") makes a good "continue" button label. */
885
+ exit?: {
886
+ step: TransitionStep;
887
+ index: number;
888
+ };
889
+ };
890
+ /**
891
+ * Group steps into per-floor sections. Single-floor routes return one
892
+ * section (so a consumer can keep its whole-route view for that case).
893
+ */
894
+ declare function groupStepsIntoFloorSections(steps: WayfindStep[]): RouteFloorSection[];
895
+ /**
896
+ * Which section is "active" given the active step index — the last section
897
+ * whose `firstStepIndex` is at or before `activeStepIndex`. Returns 0 when
898
+ * nothing matches (e.g. index points at the leading transition, which
899
+ * shouldn't happen since routes start with a depart step).
900
+ */
901
+ declare function activeSectionIndex(sections: RouteFloorSection[], activeStepIndex: number): number;
902
+
903
+ export { MinuteMaps, activeSectionIndex, createMinuteMapsSDK, groupStepsIntoFloorSections };
904
+ export type { Amenity, AmenityBadgeStyle, AmenityWithFloor, Bounds, BoundsPadding, CameraState, Destination, DestinationChipStyle, EventCallback, Floor, FloorMetadata, JMapAuth, JMapConfig, JacsAuth, JacsConfig, MapEvent, POI, POISearchResult, RouteFloorSection, RoutePoint, RoutingOptions, SDKConfig, SDKOptions, ThemeName, TransitionStep, ViewOptions, WayfindCenterMode, WayfindStep, Waypoint };