@tileflow/dom 0.0.0-bootstrap.0 → 0.1.0-alpha.0

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.js ADDED
@@ -0,0 +1,1768 @@
1
+ // src/map.ts
2
+ import {
3
+ attachTileflowAtmosphere,
4
+ attachTileflowBranding,
5
+ attachTileflowFairUseNotice,
6
+ attachTileflowHorizonQueries,
7
+ attachTileflowLabelPlacement,
8
+ attachTileflowMap3D,
9
+ attachTileflowMapLifecycle,
10
+ attachTileflowNauticalLights,
11
+ attachTileflowNotices,
12
+ createTileflowAppearanceBinding,
13
+ createTileflowContourRequestScope,
14
+ createTileflowNavigationControlBinding,
15
+ createTileflowSessionStarter,
16
+ createTileflowThemeController,
17
+ createTileflowTransformRequest,
18
+ getTileflowSystemColorScheme,
19
+ loadTileflowRuntimeStyle,
20
+ loadTileflowStyleFonts,
21
+ registerTileflowContourProtocol,
22
+ registerTileflowPathGroundProtocol,
23
+ registerTileflowPmtilesProtocol,
24
+ registerTileflowWorldRequestBridge,
25
+ reportTileflowMapError,
26
+ resolveTileflowCameraFit,
27
+ subscribeTileflowSystemColorScheme,
28
+ tileflowCanvasContextAttributes,
29
+ tileflowMaxPitch
30
+ } from "@tileflow/core/browser";
31
+ import { normalizeTileflowCaptureId } from "@tileflow/core/capture";
32
+ import {
33
+ createTileflowSessionController,
34
+ defaultTileflowManifestUrl,
35
+ defaultTileflowRuntimeView,
36
+ loadTileflowManifest,
37
+ mergeTileflowAnalytics,
38
+ nearestTileflowBlendTheme,
39
+ normalizeTileflowRuntimeCenter,
40
+ normalizeTileflowStaticImageSize,
41
+ resolveTileflowManifestMap,
42
+ resolveTileflowRuntimeStyle,
43
+ resolveTileflowRuntimeTheme,
44
+ resolveTileflowRuntimeThemeBlend,
45
+ resolveTileflowRuntimeView,
46
+ resolveTileflowStaticImageUrl
47
+ } from "@tileflow/core/runtime";
48
+ import {
49
+ tileflowInteractionStateSchema as tileflowInteractionStateSchema2,
50
+ unusedTileflowRendererDiagnostics
51
+ } from "@tileflow/interactions";
52
+ import {
53
+ createTileflowMapLibreDataRuntime,
54
+ createTileflowMapLibreDomRuntime,
55
+ createTileflowMapLibreInteractionCoordinator,
56
+ createTileflowMapLibreSemanticDomRuntime
57
+ } from "@tileflow/interactions/maplibre";
58
+
59
+ // src/inputs.ts
60
+ import { validateTileflowBrowserThemeBlendSelection } from "@tileflow/core/browser";
61
+ import {
62
+ isTileflowTimeOfDay,
63
+ validateTileflowRuntimeSource,
64
+ validateTileflowThemeSelection
65
+ } from "@tileflow/core/runtime";
66
+ import {
67
+ initialTileflowInteractionState,
68
+ tileflowInteractionStateSchema,
69
+ validateTileflowAnnotations,
70
+ validateTileflowInteractionBindings
71
+ } from "@tileflow/interactions";
72
+ function prepareTileflowAnnotations(annotations) {
73
+ const candidates = annotations ?? [];
74
+ const titles = new Map(candidates.map((annotation) => [annotation.id, annotation.ariaLabel]));
75
+ const validation = validateTileflowAnnotations(candidates);
76
+ if (!validation.ok) {
77
+ return { annotations: [], diagnostics: validation.diagnostics, ok: false, titles };
78
+ }
79
+ return { annotations: candidates, diagnostics: [], ok: true, titles };
80
+ }
81
+ function prepareTileflowInteractionBindings(interactions) {
82
+ const validation = validateTileflowInteractionBindings(interactions ?? []);
83
+ return validation.ok ? { bindings: validation.bindings, diagnostics: [], ok: true } : { diagnostics: validation.diagnostics, ok: false };
84
+ }
85
+ function prepareTileflowInteractionState(interactionState, defaultInteractionState, controlledOwnership = interactionState !== void 0) {
86
+ const controlled = interactionState !== void 0;
87
+ if (controlled && defaultInteractionState !== void 0) {
88
+ return invalidState(
89
+ controlled,
90
+ "Tileflow Map accepts interactionState or defaultInteractionState, not both."
91
+ );
92
+ }
93
+ if (controlled !== controlledOwnership) {
94
+ return invalidState(
95
+ controlled,
96
+ "Tileflow interaction state ownership cannot switch between controlled and uncontrolled."
97
+ );
98
+ }
99
+ const statePath = controlled ? "/interactionState" : "/defaultInteractionState";
100
+ const parsed = tileflowInteractionStateSchema.safeParse(
101
+ interactionState ?? defaultInteractionState ?? initialTileflowInteractionState
102
+ );
103
+ if (parsed.success) return { controlled, diagnostics: [], ok: true, state: parsed.data };
104
+ return {
105
+ controlled,
106
+ diagnostics: parsed.error.issues.map((issue) => ({
107
+ code: "INVALID_DOCUMENT",
108
+ level: "error",
109
+ message: issue.message,
110
+ path: `${statePath}${issue.path.map((segment) => String(segment).replaceAll("~", "~0").replaceAll("/", "~1")).map((segment) => `/${segment}`).join("")}`
111
+ })),
112
+ ok: false,
113
+ state: initialTileflowInteractionState
114
+ };
115
+ }
116
+ function invalidState(controlled, message) {
117
+ return {
118
+ controlled,
119
+ diagnostics: [{ code: "INVALID_DOCUMENT", level: "error", message, path: "" }],
120
+ ok: false,
121
+ state: initialTileflowInteractionState
122
+ };
123
+ }
124
+ function assertTileflowMapStyleOptions(input) {
125
+ const error = styleOptionsError(input);
126
+ if (error) throw new TypeError(`Invalid Tileflow map options: ${error}`);
127
+ }
128
+ function styleOptionsError(input) {
129
+ const sourceValidation = validateTileflowRuntimeSource(input.source);
130
+ if (!sourceValidation.ok) return sourceValidation.error;
131
+ if (input.theme !== void 0 && !validateTileflowThemeSelection(input.theme)) {
132
+ return 'theme must be a concrete portable theme name or "system"';
133
+ }
134
+ if (input.timeOfDay !== void 0 && !isTileflowTimeOfDay(input.timeOfDay)) {
135
+ return "timeOfDay must be day, dawn, dusk, or night";
136
+ }
137
+ if (input.themeBlend !== void 0 && !validateTileflowBrowserThemeBlendSelection(input.themeBlend)) {
138
+ return "themeBlend must name two to eight concrete themes, a position from 0 to themes.length - 1, and an optional boolean hold";
139
+ }
140
+ if (input.themeTransition !== void 0 && !isThemeTransition(input.themeTransition)) {
141
+ return "themeTransition.duration must be a number of milliseconds from 0 to 5000";
142
+ }
143
+ return void 0;
144
+ }
145
+ function isThemeTransition(value) {
146
+ if (!value || typeof value !== "object") return false;
147
+ const { duration } = value;
148
+ return duration === void 0 || typeof duration === "number" && Number.isFinite(duration) && duration >= 0 && duration <= 5e3;
149
+ }
150
+ function createOverlayFailureDiagnostic(message) {
151
+ return { code: "OVERLAY_FAILURE", level: "error", message, path: "" };
152
+ }
153
+ function tileflowInteractionDiagnosticKey(diagnostic) {
154
+ return [
155
+ diagnostic.code,
156
+ diagnostic.level,
157
+ diagnostic.path ?? "",
158
+ diagnostic.target ? JSON.stringify(diagnostic.target) : "",
159
+ diagnostic.message
160
+ ].join("\0");
161
+ }
162
+ function imageModeAnnotationDiagnostic(annotationCount) {
163
+ return annotationCount > 0 ? {
164
+ code: "UNSUPPORTED_MODE",
165
+ level: "error",
166
+ message: 'Tileflow annotations require mode="interactive"; image mode cannot render interactive overlays.',
167
+ path: ""
168
+ } : void 0;
169
+ }
170
+ function imageModeMapInteractionDiagnostic(annotationCount, bindingCount, hasAdditionalInteractionConfiguration = false) {
171
+ if (bindingCount === 0 && !hasAdditionalInteractionConfiguration) {
172
+ return imageModeAnnotationDiagnostic(annotationCount);
173
+ }
174
+ return {
175
+ code: "UNSUPPORTED_MODE",
176
+ level: "error",
177
+ message: annotationCount > 0 && bindingCount > 0 ? 'Tileflow annotations and interactions require mode="interactive"; image mode cannot render interactive overlays.' : annotationCount > 0 ? 'Tileflow annotations require mode="interactive"; image mode cannot render interactive overlays.' : bindingCount > 0 ? 'Tileflow interactions require mode="interactive"; image mode cannot render interactive overlays.' : 'Tileflow interaction state, callbacks, and renderers require mode="interactive"; image mode cannot render interactive overlays.',
178
+ path: ""
179
+ };
180
+ }
181
+
182
+ // src/maplibre.ts
183
+ var bundlerWorkerUrlKey = /* @__PURE__ */ Symbol.for("tileflow.maplibre.workerUrl");
184
+ var mapLibrePromise;
185
+ var mapLibre;
186
+ var workerUrl;
187
+ var appliedWorkerUrl;
188
+ function configureTileflowMapLibre(configuration) {
189
+ const nextWorkerUrl = configuration.workerUrl;
190
+ if (!isWorkerUrl(nextWorkerUrl)) {
191
+ throw new TypeError("Tileflow MapLibre workerUrl must be a non-empty string.");
192
+ }
193
+ if (workerUrl !== void 0 && workerUrl !== nextWorkerUrl) {
194
+ throw new Error("Tileflow MapLibre workerUrl is already configured.");
195
+ }
196
+ if (appliedWorkerUrl !== void 0 && appliedWorkerUrl !== nextWorkerUrl) {
197
+ throw new Error(
198
+ "Tileflow MapLibre workerUrl is already configured: a map started with the bundler-provided worker. Configure the worker before the first map."
199
+ );
200
+ }
201
+ workerUrl = nextWorkerUrl;
202
+ if (mapLibre) applyWorkerUrl(mapLibre);
203
+ }
204
+ function loadTileflowMapLibre() {
205
+ mapLibrePromise ??= import("maplibre-gl").then(resolveMapLibreModule).then(configureTileflowMapLibreModule).catch((error) => {
206
+ mapLibrePromise = void 0;
207
+ throw error;
208
+ });
209
+ return mapLibrePromise;
210
+ }
211
+ function resolveMapLibreModule(module) {
212
+ const defaultExport = module.default;
213
+ return defaultExport ?? module;
214
+ }
215
+ function configureTileflowMapLibreModule(maplibregl) {
216
+ applyWorkerUrl(maplibregl);
217
+ mapLibre = maplibregl;
218
+ return maplibregl;
219
+ }
220
+ function applyWorkerUrl(maplibregl) {
221
+ const nextWorkerUrl = workerUrl ?? readBundlerWorkerUrl();
222
+ if (nextWorkerUrl === void 0 || nextWorkerUrl === appliedWorkerUrl) return;
223
+ maplibregl.setWorkerUrl(nextWorkerUrl);
224
+ appliedWorkerUrl = nextWorkerUrl;
225
+ }
226
+ function readBundlerWorkerUrl() {
227
+ const value = globalThis[bundlerWorkerUrlKey];
228
+ return isWorkerUrl(value) ? value : void 0;
229
+ }
230
+ function isWorkerUrl(value) {
231
+ return typeof value === "string" && value.trim().length > 0;
232
+ }
233
+
234
+ // src/reconcile.ts
235
+ function createTileflowReconciler() {
236
+ const memos = /* @__PURE__ */ new Map();
237
+ const effects = /* @__PURE__ */ new Map();
238
+ const stableValues = /* @__PURE__ */ new Map();
239
+ let pending = [];
240
+ return {
241
+ commit() {
242
+ const batch = pending;
243
+ pending = [];
244
+ const errors = [];
245
+ for (const { key } of batch) {
246
+ const slot = effects.get(key);
247
+ const cleanup = slot?.cleanup;
248
+ if (slot) slot.cleanup = void 0;
249
+ if (!cleanup) continue;
250
+ try {
251
+ cleanup();
252
+ } catch (error) {
253
+ errors.push(error);
254
+ }
255
+ }
256
+ for (const { key, run } of batch) {
257
+ const slot = effects.get(key);
258
+ if (!slot) continue;
259
+ try {
260
+ const cleanup = run();
261
+ slot.cleanup = typeof cleanup === "function" ? cleanup : void 0;
262
+ } catch (error) {
263
+ errors.push(error);
264
+ }
265
+ }
266
+ throwCollected(errors, "Tileflow map update failed.");
267
+ },
268
+ dispose() {
269
+ pending = [];
270
+ const cleanups = [...effects.values()].map((slot) => slot.cleanup).filter((cleanup) => cleanup !== void 0).reverse();
271
+ effects.clear();
272
+ memos.clear();
273
+ stableValues.clear();
274
+ runTileflowCleanups(cleanups);
275
+ },
276
+ effect(key, deps, run) {
277
+ const slot = effects.get(key);
278
+ if (slot && sameDependencies(slot.deps, deps)) return;
279
+ if (slot) slot.deps = deps;
280
+ else effects.set(key, { deps });
281
+ pending = pending.filter((entry) => entry.key !== key);
282
+ pending.push({ key, run });
283
+ },
284
+ memo(key, deps, compute) {
285
+ const slot = memos.get(key);
286
+ if (slot && sameDependencies(slot.deps, deps)) return slot.value;
287
+ const value = compute();
288
+ memos.set(key, { deps, value });
289
+ return value;
290
+ },
291
+ stable(key, value) {
292
+ if (stableValues.has(key)) {
293
+ const previous = stableValues.get(key);
294
+ if (areEquivalentTileflowOptionValues(previous, value)) return previous;
295
+ }
296
+ stableValues.set(key, value);
297
+ return value;
298
+ }
299
+ };
300
+ }
301
+ function sameDependencies(left, right) {
302
+ return left.length === right.length && left.every((value, index) => Object.is(value, right[index]));
303
+ }
304
+ function areEquivalentTileflowOptionValues(left, right, seen = /* @__PURE__ */ new WeakMap()) {
305
+ if (Object.is(left, right)) return true;
306
+ if (!left || !right || typeof left !== "object" || typeof right !== "object") return false;
307
+ const seenRight = seen.get(left);
308
+ if (seenRight) return seenRight === right;
309
+ seen.set(left, right);
310
+ if (Array.isArray(left) || Array.isArray(right)) {
311
+ return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => areEquivalentTileflowOptionValues(value, right[index], seen));
312
+ }
313
+ if (!isPlainObject(left) || !isPlainObject(right)) return false;
314
+ const leftKeys = Object.keys(left);
315
+ const rightKeys = Object.keys(right);
316
+ return leftKeys.length === rightKeys.length && leftKeys.every(
317
+ (key) => Object.hasOwn(right, key) && areEquivalentTileflowOptionValues(left[key], right[key], seen)
318
+ );
319
+ }
320
+ function isPlainObject(value) {
321
+ const prototype = Object.getPrototypeOf(value);
322
+ return prototype === Object.prototype || prototype === null;
323
+ }
324
+ function runTileflowCleanups(cleanups) {
325
+ const errors = [];
326
+ for (const cleanup of cleanups) {
327
+ try {
328
+ cleanup();
329
+ } catch (error) {
330
+ errors.push(error);
331
+ }
332
+ }
333
+ throwCollected(errors, "Tileflow map cleanup failed.");
334
+ }
335
+ function throwCollected(errors, message) {
336
+ if (errors.length === 1) throw errors[0];
337
+ if (errors.length > 1) throw new AggregateError(errors, message);
338
+ }
339
+
340
+ // src/views.ts
341
+ function tileflowAnnotationViewContext(target, state) {
342
+ const { annotation } = target;
343
+ const content = target.kind === "marker" ? annotation.marker?.content : target.kind === "tooltip" ? annotation.tooltip?.content : annotation.popup?.content;
344
+ return {
345
+ annotation,
346
+ close: target.close,
347
+ ...content ? { content } : {},
348
+ target: { annotation, coordinate: annotation.coordinate, kind: "annotation" },
349
+ ...content?.kind === "view" ? { viewName: content.name } : {},
350
+ ...target.kind === "marker" && state ? { state } : {}
351
+ };
352
+ }
353
+ function tileflowSemanticViewContext(target) {
354
+ return {
355
+ close: target.close,
356
+ content: target.content,
357
+ target: target.target,
358
+ ...target.viewName ? { viewName: target.viewName } : {}
359
+ };
360
+ }
361
+ function createTileflowMapViewHost(options) {
362
+ const mounted = /* @__PURE__ */ new Map();
363
+ let signature = "[]";
364
+ let settledSignature = "[]";
365
+ let frames = [];
366
+ let disposed = false;
367
+ const cancelFrames = () => {
368
+ for (const frame of frames) options.window.cancelAnimationFrame(frame);
369
+ frames = [];
370
+ };
371
+ const draw = (view, context) => {
372
+ view.context = context;
373
+ try {
374
+ if (view.instance && view.instance.update) {
375
+ view.instance.update(context);
376
+ return;
377
+ }
378
+ view.instance?.destroy?.();
379
+ view.element.replaceChildren();
380
+ view.instance = view.renderer(view.element, context);
381
+ } catch (error) {
382
+ view.instance = void 0;
383
+ options.onError(error);
384
+ }
385
+ };
386
+ const unmount = (view) => {
387
+ view.detachPointer?.();
388
+ try {
389
+ view.instance?.destroy?.();
390
+ } catch (error) {
391
+ options.onError(error);
392
+ }
393
+ };
394
+ const markerContext = (view) => tileflowAnnotationViewContext(view.target, {
395
+ ...view.pointer,
396
+ selected: view.selected
397
+ });
398
+ const watchPointer = (view) => {
399
+ const element = view.element.closest('[data-tileflow-interaction="marker"]');
400
+ if (!element) return void 0;
401
+ const set = (patch) => {
402
+ const next = { ...view.pointer, ...patch };
403
+ if (next.focused === view.pointer.focused && next.hovered === view.pointer.hovered) return;
404
+ view.pointer = next;
405
+ draw(view, markerContext(view));
406
+ };
407
+ const enter = () => set({ hovered: true });
408
+ const leave = () => set({ hovered: false });
409
+ const focus = () => set({ focused: true });
410
+ const blur = () => set({ focused: false });
411
+ element.addEventListener("pointerenter", enter);
412
+ element.addEventListener("pointerleave", leave);
413
+ element.addEventListener("focusin", focus);
414
+ element.addEventListener("focusout", blur);
415
+ return () => {
416
+ element.removeEventListener("pointerenter", enter);
417
+ element.removeEventListener("pointerleave", leave);
418
+ element.removeEventListener("focusin", focus);
419
+ element.removeEventListener("focusout", blur);
420
+ };
421
+ };
422
+ return {
423
+ dispose() {
424
+ disposed = true;
425
+ cancelFrames();
426
+ for (const view of mounted.values()) unmount(view);
427
+ mounted.clear();
428
+ },
429
+ isSettled: () => settledSignature === signature,
430
+ sync(targets, views) {
431
+ if (disposed) return;
432
+ const selected = new Set(
433
+ targets.annotations.filter((target) => target.kind === "popup").map((target) => target.annotation.id)
434
+ );
435
+ const next = /* @__PURE__ */ new Map();
436
+ for (const target of targets.annotations) {
437
+ next.set(`annotation:${target.key}`, { kind: "annotation", target });
438
+ }
439
+ for (const target of targets.semantic) {
440
+ next.set(`semantic:${target.key}`, { kind: "semantic", target });
441
+ }
442
+ for (const [key, view] of mounted) {
443
+ if (next.has(key)) continue;
444
+ unmount(view);
445
+ mounted.delete(key);
446
+ }
447
+ for (const [key, entry] of next) {
448
+ const viewKind = entry.target.kind;
449
+ const renderer = views[viewKind];
450
+ const isMarker = entry.kind === "annotation" && viewKind === "marker";
451
+ const isSelected = isMarker && selected.has(
452
+ entry.target.annotation.id
453
+ );
454
+ const existing = mounted.get(key);
455
+ if (!renderer) {
456
+ if (existing) {
457
+ unmount(existing);
458
+ mounted.delete(key);
459
+ }
460
+ continue;
461
+ }
462
+ if (existing && existing.renderer === renderer && existing.target === entry.target && existing.selected === isSelected) {
463
+ continue;
464
+ }
465
+ if (existing && existing.renderer !== renderer) {
466
+ unmount(existing);
467
+ mounted.delete(key);
468
+ }
469
+ const view = mounted.get(key) ?? {
470
+ context: void 0,
471
+ element: entry.target.container,
472
+ instance: void 0,
473
+ pointer: { focused: false, hovered: false },
474
+ renderer,
475
+ selected: isSelected,
476
+ target: entry.target
477
+ };
478
+ view.element = entry.target.container;
479
+ view.selected = isSelected;
480
+ view.target = entry.target;
481
+ if (!mounted.has(key)) {
482
+ mounted.set(key, view);
483
+ if (isMarker) view.detachPointer = watchPointer(view);
484
+ }
485
+ draw(
486
+ view,
487
+ entry.kind === "semantic" ? tileflowSemanticViewContext(entry.target) : isMarker ? markerContext(view) : tileflowAnnotationViewContext(
488
+ entry.target
489
+ )
490
+ );
491
+ }
492
+ const nextSignature = JSON.stringify([...mounted.keys()].sort());
493
+ if (nextSignature === signature) return;
494
+ signature = nextSignature;
495
+ cancelFrames();
496
+ const first = options.window.requestAnimationFrame(() => {
497
+ const second = options.window.requestAnimationFrame(() => {
498
+ frames = [];
499
+ if (disposed) return;
500
+ settledSignature = nextSignature;
501
+ options.onChange();
502
+ });
503
+ frames = [second];
504
+ });
505
+ frames = [first];
506
+ }
507
+ };
508
+ }
509
+
510
+ // src/map.ts
511
+ var noInteractions = [];
512
+ var statusAttributes = [
513
+ "data-tileflow-capture-id",
514
+ "data-tileflow-map",
515
+ "data-tileflow-state",
516
+ "data-tileflow-theme"
517
+ ];
518
+ function createTileflowMap(container, initialOptions, createOptions = {}) {
519
+ if (!container || typeof container !== "object" || !container.ownerDocument) {
520
+ throw new TypeError("createTileflowMap needs an HTML element to mount the map in.");
521
+ }
522
+ const ownerWindow = container.ownerDocument.defaultView;
523
+ if (!ownerWindow) throw new Error("A Tileflow map needs a browser document.");
524
+ const browserWindow = ownerWindow;
525
+ assertTileflowMapStyleOptions(initialOptions);
526
+ if (initialOptions.views && initialOptions.renderTargets) {
527
+ throw new TypeError("A Tileflow map takes views or renderTargets, not both.");
528
+ }
529
+ const sessionSource = createOptions.sessionSource ?? "dom";
530
+ const statusElement = createOptions.statusElement === void 0 ? container : createOptions.statusElement;
531
+ const captureId = normalizeTileflowCaptureId(createOptions.captureId);
532
+ const controlledInteractionOwnership = initialOptions.interactionState !== void 0;
533
+ let options = { ...initialOptions };
534
+ let destroyed = false;
535
+ let reconciling = false;
536
+ let dirty = false;
537
+ let scheduled = false;
538
+ let manifestResolution = { key: "", map: null, state: "loading" };
539
+ let systemColorScheme = "light";
540
+ let captureState = "loading";
541
+ let themeTransitionState = "idle";
542
+ let activeRuntimeResource;
543
+ let annotationRenderTargets = [];
544
+ let semanticRenderTargets = [];
545
+ let annotationBridgeDiagnostic;
546
+ let semanticBridgeDiagnostic;
547
+ let stateBridgeDiagnostic;
548
+ let rendererBridgeDiagnostic;
549
+ let viewBridgeDiagnostic;
550
+ let annotationDiagnostics = [];
551
+ let semanticDiagnostics = [];
552
+ let reportedDeclarativeDiagnosticKeys = /* @__PURE__ */ new Set();
553
+ let imageSize = null;
554
+ let managedImage = null;
555
+ let imageReadinessRun = 0;
556
+ let map = null;
557
+ let scene;
558
+ let themeController;
559
+ let appearanceBinding;
560
+ let navigationBinding;
561
+ let interactionRuntime;
562
+ let semanticRuntime;
563
+ let interactionCoordinator;
564
+ let dataRuntime;
565
+ let appliedCameraFitKey;
566
+ let lastStatus;
567
+ let current;
568
+ let annotationsForRuntime = [];
569
+ let annotationTitles = /* @__PURE__ */ new Map();
570
+ let coordinatedInteractionState = prepareTileflowInteractionState(
571
+ initialOptions.interactionState,
572
+ initialOptions.defaultInteractionState
573
+ ).state;
574
+ let dataInput = {};
575
+ let resolveReady;
576
+ let rejectReady;
577
+ let readySettled = false;
578
+ const ready = new Promise((resolve, reject) => {
579
+ resolveReady = resolve;
580
+ rejectReady = reject;
581
+ });
582
+ ready.catch(() => void 0);
583
+ const settleReady = (outcome) => {
584
+ if (readySettled) return;
585
+ readySettled = true;
586
+ if ("map" in outcome) resolveReady(outcome.map);
587
+ else rejectReady(outcome.error);
588
+ };
589
+ const reconciler = createTileflowReconciler();
590
+ const reportError = (code, cause) => {
591
+ reportTileflowMapError(options.onError, code, cause);
592
+ if (code === "manifest" || code === "runtime") {
593
+ settleReady({ error: cause instanceof Error ? cause : new Error(String(cause)) });
594
+ }
595
+ };
596
+ const reportDiagnostic = (diagnostic) => options.onInteractionDiagnostic?.(diagnostic);
597
+ const viewHost = createTileflowMapViewHost({
598
+ onChange: () => invalidate(),
599
+ onError(error) {
600
+ console.error("Failed to render a Tileflow map view", error);
601
+ viewBridgeDiagnostic = createOverlayFailureDiagnostic(
602
+ "A Tileflow map view could not be rendered."
603
+ );
604
+ reportDiagnostic(viewBridgeDiagnostic);
605
+ invalidate();
606
+ },
607
+ window: browserWindow
608
+ });
609
+ function invalidate() {
610
+ if (destroyed) return;
611
+ if (reconciling) {
612
+ dirty = true;
613
+ return;
614
+ }
615
+ if (scheduled) return;
616
+ scheduled = true;
617
+ queueMicrotask(() => {
618
+ scheduled = false;
619
+ reconcile();
620
+ });
621
+ }
622
+ function derive() {
623
+ const memo = reconciler.memo;
624
+ const stable = reconciler.stable;
625
+ const {
626
+ annotationClusters,
627
+ annotations,
628
+ bounds,
629
+ dataOverlays,
630
+ dataSources,
631
+ defaultInteractionState,
632
+ fitAnnotations,
633
+ fitBoundsOptions,
634
+ interactionState,
635
+ source,
636
+ theme,
637
+ themeBlend,
638
+ timeOfDay
639
+ } = options;
640
+ const interactions = options.interactions ?? noInteractions;
641
+ const runtimeSource = memo("runtimeSource", [source.manifestUrl, source.map], () => ({
642
+ manifestUrl: source.manifestUrl,
643
+ map: source.map
644
+ }));
645
+ const mapName = runtimeSource.map;
646
+ const manifestUrl = runtimeSource.manifestUrl ?? defaultTileflowManifestUrl;
647
+ const preparedAnnotations = memo(
648
+ "annotations",
649
+ [annotations],
650
+ () => prepareTileflowAnnotations(annotations)
651
+ );
652
+ const preparedInteractions = memo(
653
+ "interactions",
654
+ [interactions],
655
+ () => prepareTileflowInteractionBindings(interactions)
656
+ );
657
+ const preparedInteractionState = memo(
658
+ "interactionState",
659
+ [defaultInteractionState, interactionState],
660
+ () => prepareTileflowInteractionState(
661
+ interactionState,
662
+ defaultInteractionState,
663
+ controlledInteractionOwnership
664
+ )
665
+ );
666
+ const cameraFit = memo(
667
+ "cameraFit",
668
+ [annotations, bounds, fitAnnotations, fitBoundsOptions],
669
+ () => resolveTileflowCameraFit({
670
+ annotations: annotations ?? [],
671
+ bounds,
672
+ fitAnnotations,
673
+ fitBoundsOptions
674
+ })
675
+ );
676
+ const mapOptions = stable("mapOptions", options.mapOptions);
677
+ const manifestRequestKey = JSON.stringify([manifestUrl, mapName]);
678
+ const currentManifest = manifestResolution.key === manifestRequestKey ? manifestResolution : { key: manifestRequestKey, map: null, state: "loading" };
679
+ const manifestMap = currentManifest.state === "ready" ? currentManifest.map : null;
680
+ const themeBlendKey = themeBlend ? JSON.stringify(themeBlend.themes) : void 0;
681
+ const blendThemeName = themeBlend ? nearestTileflowBlendTheme(themeBlend) : void 0;
682
+ const themeResolution = memo(
683
+ "themeResolution",
684
+ [blendThemeName, manifestMap, systemColorScheme, theme, themeBlendKey, timeOfDay],
685
+ () => {
686
+ const selection = blendThemeName ?? theme;
687
+ if (!manifestMap) {
688
+ return {
689
+ error: false,
690
+ name: selection && selection !== "system" ? selection : void 0
691
+ };
692
+ }
693
+ try {
694
+ if (themeBlendKey) {
695
+ for (const name of JSON.parse(themeBlendKey)) {
696
+ resolveTileflowRuntimeTheme(manifestMap, name);
697
+ }
698
+ }
699
+ return {
700
+ error: false,
701
+ name: resolveTileflowRuntimeTheme(
702
+ manifestMap,
703
+ selection,
704
+ systemColorScheme,
705
+ themeBlendKey ? void 0 : timeOfDay
706
+ ).name
707
+ };
708
+ } catch (cause) {
709
+ return { cause, error: true, name: void 0 };
710
+ }
711
+ }
712
+ );
713
+ const manifestView = resolveTileflowRuntimeView({ manifestMap });
714
+ const manifestCenter = stable(
715
+ "manifestCenter",
716
+ normalizeTileflowRuntimeCenter(manifestView.center)
717
+ );
718
+ const resolvedCenter = stable(
719
+ "center",
720
+ options.center ?? mapOptions?.center ?? manifestCenter
721
+ );
722
+ const resolvedZoom = options.zoom ?? mapOptions?.zoom ?? manifestView.zoom ?? defaultTileflowRuntimeView.zoom;
723
+ const resolvedBearing = mapOptions?.bearing ?? manifestView.bearing;
724
+ const resolvedPitch = mapOptions?.pitch ?? manifestView.pitch;
725
+ const resolvedInteractive = options.interactive ?? mapOptions?.interactive ?? true;
726
+ const isImageMode = options.mode === "image";
727
+ const renderers = tileflowMapRenderers(options);
728
+ const { hasMarkerRenderer, hasPopupRenderer, hasTooltipRenderer } = renderers;
729
+ const hasInteractionConfiguration = hasTileflowMapInteractionConfiguration(options, renderers);
730
+ const declarativeDiagnostics = memo(
731
+ "declarativeDiagnostics",
732
+ [
733
+ hasInteractionConfiguration,
734
+ hasPopupRenderer,
735
+ hasTooltipRenderer,
736
+ interactions,
737
+ isImageMode,
738
+ preparedAnnotations,
739
+ preparedInteractions,
740
+ preparedInteractionState
741
+ ],
742
+ () => tileflowMapDeclarativeDiagnostics({
743
+ hasInteractionConfiguration,
744
+ hasPopupRenderer,
745
+ hasTooltipRenderer,
746
+ interactions,
747
+ isImageMode,
748
+ preparedAnnotations,
749
+ preparedInteractions,
750
+ preparedInteractionState
751
+ })
752
+ );
753
+ const runtimeStyle = memo(
754
+ "runtimeStyle",
755
+ [isImageMode, manifestMap, runtimeSource, systemColorScheme, themeResolution],
756
+ () => isImageMode || themeResolution.error ? null : resolveTileflowRuntimeStyle({
757
+ colorScheme: systemColorScheme,
758
+ manifestMap,
759
+ source: runtimeSource,
760
+ theme: themeResolution.name
761
+ })
762
+ );
763
+ const blendResolution = memo(
764
+ "blendResolution",
765
+ [isImageMode, manifestMap, runtimeSource, themeBlendKey],
766
+ () => {
767
+ if (isImageMode || !manifestMap || !themeBlendKey) return { styles: null };
768
+ try {
769
+ return {
770
+ styles: resolveTileflowRuntimeThemeBlend({
771
+ blend: { position: 0, themes: JSON.parse(themeBlendKey) },
772
+ manifestMap,
773
+ source: runtimeSource
774
+ })
775
+ };
776
+ } catch (cause) {
777
+ return { cause, styles: null };
778
+ }
779
+ }
780
+ );
781
+ const resolvedAnalytics = memo(
782
+ "analytics",
783
+ [options.analytics, runtimeStyle],
784
+ () => mergeTileflowAnalytics(options.analytics, runtimeStyle?.analytics)
785
+ );
786
+ const staticImageCenter = stable(
787
+ "staticImageCenter",
788
+ normalizeTileflowRuntimeCenter(options.center ?? mapOptions?.center, manifestCenter)
789
+ );
790
+ const runtimeImageUrl = memo(
791
+ "runtimeImageUrl",
792
+ [
793
+ imageSize,
794
+ options.imageUrl,
795
+ isImageMode,
796
+ manifestMap,
797
+ staticImageCenter,
798
+ resolvedZoom,
799
+ systemColorScheme,
800
+ themeResolution
801
+ ],
802
+ () => options.imageUrl ?? (isImageMode && !themeResolution.error ? resolveTileflowStaticImageUrl({
803
+ center: staticImageCenter,
804
+ colorScheme: systemColorScheme,
805
+ imageSize,
806
+ manifestMap,
807
+ theme: themeResolution.name,
808
+ zoom: resolvedZoom
809
+ }) : void 0)
810
+ );
811
+ const runtimeResolutionState = currentManifest.state !== "ready" ? currentManifest.state === "error" ? "error" : "loading" : themeResolution.error ? "error" : isImageMode ? runtimeImageUrl ? "idle" : options.imageUrl === void 0 && imageSize === null ? "loading" : "error" : runtimeStyle ? "idle" : "error";
812
+ return {
813
+ annotationClusters,
814
+ blendResolution,
815
+ isImageMode,
816
+ runtimeImageUrl,
817
+ cameraFit,
818
+ dataOverlays,
819
+ dataSources,
820
+ declarativeDiagnostics,
821
+ hasMarkerRenderer,
822
+ hasPopupRenderer,
823
+ hasTooltipRenderer,
824
+ manifestRequestKey,
825
+ manifestUrl,
826
+ mapName,
827
+ mapOptions,
828
+ preparedAnnotations,
829
+ preparedInteractions,
830
+ preparedInteractionState,
831
+ resolvedAnalytics,
832
+ resolvedBearing,
833
+ resolvedCenter,
834
+ resolvedInteractive,
835
+ resolvedPitch,
836
+ resolvedZoom,
837
+ runtimeMapIdentity: `${manifestUrl}\0${runtimeSource.map}`,
838
+ runtimeResolutionState,
839
+ runtimeSource,
840
+ runtimeStyle,
841
+ themeBlendHold: themeBlend?.hold === true,
842
+ themeBlendKey,
843
+ themeBlendPosition: themeBlend?.position,
844
+ themeResolution
845
+ };
846
+ }
847
+ function reconcile() {
848
+ if (destroyed) return;
849
+ reconciling = true;
850
+ try {
851
+ do {
852
+ dirty = false;
853
+ pass();
854
+ } while (dirty && !destroyed);
855
+ } finally {
856
+ reconciling = false;
857
+ }
858
+ emitStatus();
859
+ }
860
+ function pass() {
861
+ const effect = reconciler.effect;
862
+ const d = derive();
863
+ current = d;
864
+ if (d.preparedAnnotations.ok) {
865
+ annotationsForRuntime = d.preparedAnnotations.annotations;
866
+ annotationTitles = d.preparedAnnotations.titles;
867
+ }
868
+ dataInput = {
869
+ annotationClusters: d.annotationClusters,
870
+ annotations: d.preparedAnnotations.annotations,
871
+ dataOverlays: d.dataOverlays,
872
+ dataSources: d.dataSources
873
+ };
874
+ effect("scene", [options.vegetation, options.viewMode ?? "3d"], () => {
875
+ scene?.setVegetation(options.vegetation);
876
+ scene?.setViewMode(options.viewMode ?? "3d");
877
+ });
878
+ effect("imageSize", [d.isImageMode, options.imageUrl], () => {
879
+ imageSize = null;
880
+ invalidate();
881
+ if (!d.isImageMode || options.imageUrl) return;
882
+ const updateImageSize = () => {
883
+ const next = normalizeTileflowStaticImageSize({
884
+ height: container.clientHeight,
885
+ width: container.clientWidth
886
+ });
887
+ if (imageSize?.height === next.height && imageSize.width === next.width) return;
888
+ imageSize = next;
889
+ invalidate();
890
+ };
891
+ const resizeObserver = new ResizeObserver(updateImageSize);
892
+ resizeObserver.observe(container);
893
+ updateImageSize();
894
+ return () => resizeObserver.disconnect();
895
+ });
896
+ const ownsImage = d.isImageMode && options.image === void 0;
897
+ effect("managedImage", [ownsImage], () => {
898
+ if (!ownsImage) return;
899
+ const image = container.ownerDocument.createElement("img");
900
+ image.decoding = "async";
901
+ image.style.cssText = "display: block; height: 100%; object-fit: cover; width: 100%;";
902
+ managedImage = image;
903
+ invalidate();
904
+ return () => {
905
+ image.remove();
906
+ if (managedImage === image) managedImage = null;
907
+ };
908
+ });
909
+ const imageElement = ownsImage ? managedImage : options.image ?? null;
910
+ const alt = options.alt ?? "";
911
+ const imageLoading = options.imageLoading ?? "eager";
912
+ effect("managedImageAttributes", [managedImage, d.runtimeImageUrl, alt, imageLoading], () => {
913
+ const image = managedImage;
914
+ if (!image) return;
915
+ image.alt = alt;
916
+ image.loading = imageLoading;
917
+ if (!d.runtimeImageUrl) {
918
+ image.remove();
919
+ image.removeAttribute("src");
920
+ return;
921
+ }
922
+ if (image.getAttribute("src") !== d.runtimeImageUrl) image.src = d.runtimeImageUrl;
923
+ if (!image.isConnected) container.append(image);
924
+ });
925
+ effect("imageReadiness", [d.isImageMode, d.runtimeImageUrl, imageElement], () => {
926
+ if (!d.isImageMode) return;
927
+ imageReadinessRun += 1;
928
+ activeRuntimeResource = d.runtimeImageUrl;
929
+ captureState = "loading";
930
+ invalidate();
931
+ const image = imageElement;
932
+ if (!image) return;
933
+ const markReady = async () => {
934
+ const run = ++imageReadinessRun;
935
+ try {
936
+ if (typeof image.decode === "function") await image.decode();
937
+ } catch {
938
+ if (!image.complete || image.naturalWidth === 0) {
939
+ if (imageReadinessRun === run) {
940
+ captureState = "error";
941
+ invalidate();
942
+ }
943
+ return;
944
+ }
945
+ }
946
+ if (imageReadinessRun !== run) return;
947
+ captureState = "idle";
948
+ invalidate();
949
+ };
950
+ const fail = () => {
951
+ imageReadinessRun += 1;
952
+ captureState = "error";
953
+ invalidate();
954
+ };
955
+ const load = () => void markReady();
956
+ image.addEventListener("load", load);
957
+ image.addEventListener("error", fail);
958
+ if (image.complete && image.getAttribute("src")) {
959
+ if (image.naturalWidth === 0) fail();
960
+ else void markReady();
961
+ }
962
+ return () => {
963
+ image.removeEventListener("load", load);
964
+ image.removeEventListener("error", fail);
965
+ };
966
+ });
967
+ effect("systemColorScheme", [options.theme], () => {
968
+ if (options.theme !== "system") return;
969
+ systemColorScheme = getTileflowSystemColorScheme();
970
+ invalidate();
971
+ return subscribeTileflowSystemColorScheme((scheme) => {
972
+ systemColorScheme = scheme;
973
+ invalidate();
974
+ });
975
+ });
976
+ effect("declarativeDiagnostics", [d.declarativeDiagnostics], () => {
977
+ const previousKeys = reportedDeclarativeDiagnosticKeys;
978
+ const nextKeys = /* @__PURE__ */ new Set();
979
+ for (const diagnostic of d.declarativeDiagnostics) {
980
+ const key = tileflowInteractionDiagnosticKey(diagnostic);
981
+ nextKeys.add(key);
982
+ if (!previousKeys.has(key)) reportDiagnostic(diagnostic);
983
+ }
984
+ reportedDeclarativeDiagnosticKeys = nextKeys;
985
+ });
986
+ effect("manifest", [d.manifestRequestKey, d.manifestUrl, d.mapName], () => {
987
+ let cancelled = false;
988
+ const { manifestRequestKey, manifestUrl, mapName } = d;
989
+ manifestResolution = { key: manifestRequestKey, map: null, state: "loading" };
990
+ invalidate();
991
+ loadTileflowManifest(manifestUrl).then((manifest) => {
992
+ if (cancelled) return;
993
+ const resolvedMap = manifest && mapName ? resolveTileflowManifestMap(manifest, mapName) : null;
994
+ if (!resolvedMap) {
995
+ reportError(
996
+ "manifest",
997
+ new Error(
998
+ manifest ? `The Tileflow manifest at ${manifestUrl} has no map named "${mapName}".` : `No Tileflow manifest was found at ${manifestUrl}.`
999
+ )
1000
+ );
1001
+ }
1002
+ manifestResolution = {
1003
+ key: manifestRequestKey,
1004
+ map: resolvedMap,
1005
+ state: resolvedMap ? "ready" : "error"
1006
+ };
1007
+ invalidate();
1008
+ }).catch((error) => {
1009
+ if (cancelled) return;
1010
+ reportError("manifest", error);
1011
+ manifestResolution = { key: manifestRequestKey, map: null, state: "error" };
1012
+ invalidate();
1013
+ });
1014
+ return () => {
1015
+ cancelled = true;
1016
+ };
1017
+ });
1018
+ effect("camera", [d.resolvedBearing, d.resolvedCenter, d.resolvedPitch, d.resolvedZoom], () => {
1019
+ map?.jumpTo({
1020
+ bearing: d.resolvedBearing,
1021
+ center: d.resolvedCenter,
1022
+ pitch: d.resolvedPitch,
1023
+ zoom: d.resolvedZoom
1024
+ });
1025
+ });
1026
+ effect("navigationControl", [options.navigationControl, d.resolvedInteractive], () => {
1027
+ navigationBinding?.update(d.resolvedInteractive ? options.navigationControl : false);
1028
+ });
1029
+ effect("cameraFit", [d.cameraFit], () => {
1030
+ if (!map || appliedCameraFitKey === d.cameraFit.key) return;
1031
+ appliedCameraFitKey = d.cameraFit.key;
1032
+ if (d.cameraFit.error) reportError("camera", new Error(d.cameraFit.error));
1033
+ if (d.cameraFit.fit) fitCameraTo(map, d.cameraFit.fit);
1034
+ });
1035
+ effect("reconcileAnnotations", [d.preparedAnnotations], () => {
1036
+ if (!interactionRuntime || !d.preparedAnnotations.ok) return;
1037
+ reconcileAnnotations(interactionRuntime, d.preparedAnnotations.annotations);
1038
+ });
1039
+ effect(
1040
+ "reconcileData",
1041
+ [d.annotationClusters, d.dataOverlays, d.dataSources, d.preparedAnnotations],
1042
+ () => {
1043
+ dataRuntime?.reconcile(dataInput);
1044
+ }
1045
+ );
1046
+ effect("reconcileInteractions", [d.preparedInteractions], () => {
1047
+ if (!semanticRuntime || !d.preparedInteractions.ok) return;
1048
+ reconcileInteractions(semanticRuntime, d.preparedInteractions.bindings);
1049
+ });
1050
+ effect("controlledInteractionState", [d.preparedInteractionState], () => {
1051
+ const prepared = d.preparedInteractionState;
1052
+ if (!prepared.controlled || !prepared.ok) return;
1053
+ try {
1054
+ stateBridgeDiagnostic = void 0;
1055
+ interactionCoordinator?.setInteractionState(prepared.state);
1056
+ coordinatedInteractionState = prepared.state;
1057
+ } catch (error) {
1058
+ console.error("Failed to update Tileflow interaction state", error);
1059
+ stateBridgeDiagnostic = createOverlayFailureDiagnostic(
1060
+ "Tileflow interaction state could not be updated."
1061
+ );
1062
+ reportDiagnostic(stateBridgeDiagnostic);
1063
+ }
1064
+ invalidate();
1065
+ });
1066
+ effect(
1067
+ "customRenderers",
1068
+ [d.hasMarkerRenderer, d.hasPopupRenderer, d.hasTooltipRenderer],
1069
+ () => {
1070
+ if (!interactionRuntime && !semanticRuntime) return;
1071
+ try {
1072
+ rendererBridgeDiagnostic = void 0;
1073
+ interactionRuntime?.setCustomRenderers({
1074
+ marker: d.hasMarkerRenderer,
1075
+ popup: d.hasPopupRenderer,
1076
+ tooltip: d.hasTooltipRenderer
1077
+ });
1078
+ semanticRuntime?.setCustomRenderers({
1079
+ popup: d.hasPopupRenderer,
1080
+ tooltip: d.hasTooltipRenderer
1081
+ });
1082
+ } catch (error) {
1083
+ console.error("Failed to update Tileflow interaction renderers", error);
1084
+ rendererBridgeDiagnostic = createOverlayFailureDiagnostic(
1085
+ "Tileflow interaction renderers could not be updated."
1086
+ );
1087
+ reportDiagnostic(rendererBridgeDiagnostic);
1088
+ }
1089
+ invalidate();
1090
+ }
1091
+ );
1092
+ effect(
1093
+ "mount",
1094
+ [
1095
+ d.isImageMode,
1096
+ d.resolvedInteractive,
1097
+ d.runtimeMapIdentity,
1098
+ d.runtimeStyle !== null || d.themeResolution.error && map !== null,
1099
+ d.mapOptions
1100
+ ],
1101
+ () => d.isImageMode ? void 0 : mountMap()
1102
+ );
1103
+ effect("theme", [d.runtimeStyle, d.themeBlendKey, d.themeResolution.error], () => {
1104
+ const controller = themeController;
1105
+ if (!controller || d.themeBlendKey) return;
1106
+ if (d.themeResolution.error) {
1107
+ void controller.setTheme(controller.getCurrent());
1108
+ return;
1109
+ }
1110
+ if (!d.runtimeStyle || controller.getCurrent() === d.runtimeStyle && !controller.getBlend()) {
1111
+ return;
1112
+ }
1113
+ activeRuntimeResource = d.runtimeStyle;
1114
+ invalidate();
1115
+ void controller.setTheme(d.runtimeStyle, { transition: options.themeTransition }).then((result) => {
1116
+ if (result.status === "failed") {
1117
+ reportError("theme", result.error ?? new Error("The map theme could not be changed."));
1118
+ }
1119
+ });
1120
+ });
1121
+ const themeResolutionCause = d.themeResolution.error ? d.themeResolution.cause : void 0;
1122
+ effect("themeError", [d.themeResolution.error, themeResolutionCause], () => {
1123
+ if (d.themeResolution.error) reportError("theme", themeResolutionCause);
1124
+ });
1125
+ effect("blendError", [d.blendResolution.cause], () => {
1126
+ if (d.blendResolution.cause !== void 0) reportError("blend", d.blendResolution.cause);
1127
+ });
1128
+ effect("appearance", [options.colors, options.emphasis], () => {
1129
+ appearanceBinding?.update(
1130
+ { colors: options.colors, emphasis: options.emphasis },
1131
+ { transition: options.themeTransition }
1132
+ );
1133
+ });
1134
+ effect(
1135
+ "blend",
1136
+ [d.blendResolution.styles, d.runtimeStyle, d.themeBlendHold, d.themeBlendPosition],
1137
+ () => {
1138
+ const controller = themeController;
1139
+ const styles = d.blendResolution.styles;
1140
+ const position = d.themeBlendPosition;
1141
+ if (!controller || !styles || position === void 0) return;
1142
+ if (d.runtimeStyle) {
1143
+ activeRuntimeResource = d.runtimeStyle;
1144
+ invalidate();
1145
+ }
1146
+ void controller.setBlend(
1147
+ { hold: d.themeBlendHold, position, themes: styles },
1148
+ { transition: options.themeTransition }
1149
+ ).then((result) => {
1150
+ if (result.status === "failed") {
1151
+ reportError(
1152
+ "blend",
1153
+ result.error ?? new Error("The map themes could not be blended.")
1154
+ );
1155
+ }
1156
+ });
1157
+ }
1158
+ );
1159
+ reconciler.commit();
1160
+ syncViews();
1161
+ }
1162
+ function reconcileAnnotations(runtime, annotations) {
1163
+ try {
1164
+ annotationBridgeDiagnostic = void 0;
1165
+ runtime.reconcile(annotations);
1166
+ } catch (error) {
1167
+ console.error("Failed to reconcile Tileflow annotations", error);
1168
+ annotationBridgeDiagnostic = createOverlayFailureDiagnostic(
1169
+ "Tileflow annotations could not be reconciled."
1170
+ );
1171
+ reportDiagnostic(annotationBridgeDiagnostic);
1172
+ }
1173
+ invalidate();
1174
+ }
1175
+ function reconcileInteractions(runtime, bindings) {
1176
+ try {
1177
+ semanticBridgeDiagnostic = void 0;
1178
+ runtime.reconcile(bindings);
1179
+ } catch (error) {
1180
+ console.error("Failed to reconcile Tileflow semantic interactions", error);
1181
+ semanticBridgeDiagnostic = createOverlayFailureDiagnostic(
1182
+ "Tileflow semantic interactions could not be reconciled."
1183
+ );
1184
+ reportDiagnostic(semanticBridgeDiagnostic);
1185
+ }
1186
+ invalidate();
1187
+ }
1188
+ let lastRenderTargets = {
1189
+ annotations: annotationRenderTargets,
1190
+ semantic: semanticRenderTargets
1191
+ };
1192
+ function syncViews() {
1193
+ if (lastRenderTargets.annotations !== annotationRenderTargets || lastRenderTargets.semantic !== semanticRenderTargets) {
1194
+ lastRenderTargets = { annotations: annotationRenderTargets, semantic: semanticRenderTargets };
1195
+ options.renderTargets?.onChange(lastRenderTargets);
1196
+ }
1197
+ viewHost.sync(lastRenderTargets, options.renderTargets ? {} : options.views ?? {});
1198
+ }
1199
+ function mountMap() {
1200
+ themeTransitionState = "idle";
1201
+ invalidate();
1202
+ const initialRuntimeStyle = current.runtimeStyle;
1203
+ if (!initialRuntimeStyle) return void 0;
1204
+ activeRuntimeResource = initialRuntimeStyle;
1205
+ captureState = "loading";
1206
+ let cancelled = false;
1207
+ let disposeMap;
1208
+ const { mapOptions, resolvedInteractive } = current;
1209
+ const resourceLifetime = new AbortController();
1210
+ const session = createTileflowSessionController({ source: sessionSource });
1211
+ const resourceTransform = createTileflowTransformRequest({
1212
+ getAnalytics: () => current.resolvedAnalytics,
1213
+ sessionController: session,
1214
+ sessionId: session.sessionId,
1215
+ transformRequest: mapOptions?.transformRequest ?? void 0
1216
+ });
1217
+ const styleLoadOptions = {
1218
+ signal: resourceLifetime.signal,
1219
+ transformRequest: (url, type) => resourceTransform(
1220
+ url,
1221
+ type
1222
+ )
1223
+ };
1224
+ void Promise.all([
1225
+ loadTileflowMapLibre(),
1226
+ loadTileflowRuntimeStyle(initialRuntimeStyle, styleLoadOptions).then(async (prepared) => {
1227
+ await loadTileflowStyleFonts(prepared.style, { fontFaces: prepared.fontFaces });
1228
+ return prepared;
1229
+ })
1230
+ ]).then(([maplibregl, prepared]) => {
1231
+ if (cancelled || destroyed) return;
1232
+ const setupCleanups = [];
1233
+ let setupDisposed = false;
1234
+ const registerCleanup = (cleanup) => setupCleanups.push(cleanup);
1235
+ disposeMap = () => {
1236
+ if (setupDisposed) return;
1237
+ setupDisposed = true;
1238
+ try {
1239
+ runTileflowCleanups([...setupCleanups].reverse());
1240
+ } finally {
1241
+ map = null;
1242
+ interactionRuntime = void 0;
1243
+ semanticRuntime = void 0;
1244
+ interactionCoordinator = void 0;
1245
+ annotationDiagnostics = [];
1246
+ annotationRenderTargets = [];
1247
+ annotationBridgeDiagnostic = void 0;
1248
+ semanticDiagnostics = [];
1249
+ semanticRenderTargets = [];
1250
+ semanticBridgeDiagnostic = void 0;
1251
+ stateBridgeDiagnostic = void 0;
1252
+ rendererBridgeDiagnostic = void 0;
1253
+ invalidate();
1254
+ }
1255
+ };
1256
+ const sessionStarter = createTileflowSessionStarter({
1257
+ getSessionId: () => session.sessionId,
1258
+ sessionId: session.sessionId,
1259
+ source: sessionSource
1260
+ });
1261
+ const fairUseNotice = attachTileflowFairUseNotice(container);
1262
+ registerCleanup(() => fairUseNotice.dispose());
1263
+ registerTileflowContourProtocol({ addProtocol: maplibregl.addProtocol });
1264
+ registerTileflowPathGroundProtocol({ addProtocol: maplibregl.addProtocol });
1265
+ registerTileflowPmtilesProtocol({ addProtocol: maplibregl.addProtocol });
1266
+ const worldRequestBridge = registerTileflowWorldRequestBridge({
1267
+ addProtocol: maplibregl.addProtocol,
1268
+ onNotice: fairUseNotice.update
1269
+ });
1270
+ registerCleanup(() => worldRequestBridge.dispose());
1271
+ const contourRequestScope = createTileflowContourRequestScope({
1272
+ transformRequest: (url, resourceType) => transformRequest(
1273
+ url,
1274
+ resourceType
1275
+ )
1276
+ });
1277
+ registerCleanup(() => contourRequestScope.dispose());
1278
+ const transformRequest = createTileflowTransformRequest({
1279
+ contourRequestScope,
1280
+ getAnalytics: () => current.resolvedAnalytics,
1281
+ sessionController: session,
1282
+ sessionId: session.sessionId,
1283
+ transformRequest: mapOptions?.transformRequest ?? void 0,
1284
+ worldRequestBridge
1285
+ });
1286
+ const mapLibreMap = new maplibregl.Map({
1287
+ ...mapOptions,
1288
+ canvasContextAttributes: tileflowCanvasContextAttributes(
1289
+ mapOptions?.canvasContextAttributes
1290
+ ),
1291
+ container,
1292
+ style: prepared.style,
1293
+ bearing: current.resolvedBearing,
1294
+ center: current.resolvedCenter,
1295
+ pitch: current.resolvedPitch,
1296
+ zoom: current.resolvedZoom,
1297
+ interactive: resolvedInteractive,
1298
+ attributionControl: mapOptions?.attributionControl ?? { compact: true },
1299
+ // Tileflow's compiled styles are validated when they are built.
1300
+ validateStyle: mapOptions?.validateStyle ?? false,
1301
+ maxPitch: mapOptions?.maxPitch ?? tileflowMaxPitch,
1302
+ transformRequest
1303
+ });
1304
+ map = mapLibreMap;
1305
+ registerCleanup(() => mapLibreMap.remove());
1306
+ registerCleanup(attachTileflowAtmosphere(mapLibreMap));
1307
+ registerCleanup(attachTileflowBranding(mapLibreMap));
1308
+ registerCleanup(attachTileflowNotices(mapLibreMap));
1309
+ registerCleanup(attachTileflowNauticalLights(mapLibreMap));
1310
+ registerCleanup(attachTileflowLabelPlacement(mapLibreMap));
1311
+ registerCleanup(attachTileflowHorizonQueries(mapLibreMap));
1312
+ const controller = initialRuntimeStyle.theme === void 0 ? void 0 : createTileflowThemeController({
1313
+ initial: prepared.family ? prepared : initialRuntimeStyle,
1314
+ initialStyle: prepared.style,
1315
+ styleLoadOptions,
1316
+ map: mapLibreMap,
1317
+ onTransition(transition) {
1318
+ options.onThemeChange?.(transition);
1319
+ themeTransitionState = transition.phase === "preloading" || transition.phase === "applying" ? "loading" : transition.phase === "error" ? "error" : "idle";
1320
+ invalidate();
1321
+ }
1322
+ });
1323
+ if (controller) {
1324
+ themeController = controller;
1325
+ registerCleanup(() => {
1326
+ controller.dispose();
1327
+ if (themeController === controller) themeController = void 0;
1328
+ });
1329
+ const appearance = createTileflowAppearanceBinding({
1330
+ controller,
1331
+ // CSS variables in `colors` are read where the map sits.
1332
+ element: container,
1333
+ map: mapLibreMap,
1334
+ onError: (error, part) => reportError(part, error)
1335
+ });
1336
+ appearanceBinding = appearance;
1337
+ registerCleanup(() => {
1338
+ appearance.dispose();
1339
+ if (appearanceBinding === appearance) appearanceBinding = void 0;
1340
+ });
1341
+ appearance.update(
1342
+ { colors: options.colors, emphasis: options.emphasis },
1343
+ { transition: { duration: 0 } }
1344
+ );
1345
+ }
1346
+ const mapScene = attachTileflowMap3D({
1347
+ map: mapLibreMap,
1348
+ ...controller ? { controller } : {},
1349
+ vegetation: options.vegetation,
1350
+ viewMode: options.viewMode ?? "3d",
1351
+ load: () => import("@tileflow/core/3d"),
1352
+ onError: (error) => reportError("scene", error),
1353
+ // Direct tile readers use HTTP, not MapLibre's custom World protocol.
1354
+ transformRequest: createTileflowTransformRequest({
1355
+ getAnalytics: () => current.resolvedAnalytics,
1356
+ sessionController: session,
1357
+ sessionId: session.sessionId,
1358
+ transformRequest: (url) => mapOptions?.transformRequest?.(
1359
+ url,
1360
+ "Tile"
1361
+ )
1362
+ })
1363
+ });
1364
+ scene = mapScene;
1365
+ registerCleanup(() => {
1366
+ mapScene.dispose();
1367
+ if (scene === mapScene) scene = void 0;
1368
+ });
1369
+ const coordinator = createTileflowMapLibreInteractionCoordinator({
1370
+ ...controlledInteractionOwnership ? { interactionState: coordinatedInteractionState } : { defaultInteractionState: coordinatedInteractionState },
1371
+ onInteractionStateChange(state) {
1372
+ if (!controlledInteractionOwnership) coordinatedInteractionState = state;
1373
+ options.onInteractionStateChange?.(state);
1374
+ }
1375
+ });
1376
+ interactionCoordinator = coordinator;
1377
+ registerCleanup(() => coordinator.dispose());
1378
+ const createOverlay = ({
1379
+ container: overlayContainer,
1380
+ kind
1381
+ }) => wrapMapLibrePositioned(
1382
+ new maplibregl.Popup({
1383
+ closeButton: false,
1384
+ closeOnClick: false,
1385
+ closeOnMove: false,
1386
+ focusAfterOpen: false,
1387
+ offset: kind === "popup" ? 24 : 18
1388
+ }).setDOMContent(overlayContainer)
1389
+ );
1390
+ const annotationRuntime = createTileflowMapLibreDomRuntime({
1391
+ createMarker({ annotation, element }) {
1392
+ const marker = new maplibregl.Marker({ element });
1393
+ element.title = annotationTitles.get(annotation.id) ?? annotation.ariaLabel;
1394
+ return wrapMapLibrePositioned(marker);
1395
+ },
1396
+ createOverlay,
1397
+ customMarker: current.hasMarkerRenderer,
1398
+ customPopup: current.hasPopupRenderer,
1399
+ customTooltip: current.hasTooltipRenderer,
1400
+ document: container.ownerDocument,
1401
+ interactionState: coordinator.getInteractionState(),
1402
+ map: mapLibreMap,
1403
+ onInteractionStateChange: coordinator.requestInteractionState,
1404
+ onDiagnostic: reportDiagnostic,
1405
+ updateMarker(_marker, { annotation, element }) {
1406
+ element.title = annotationTitles.get(annotation.id) ?? annotation.ariaLabel;
1407
+ }
1408
+ });
1409
+ interactionRuntime = annotationRuntime;
1410
+ registerCleanup(() => annotationRuntime.dispose());
1411
+ const mapDataRuntime = createTileflowMapLibreDataRuntime({
1412
+ createMarker: ({ element }) => wrapMapLibrePositioned(new maplibregl.Marker({ element })),
1413
+ document: container.ownerDocument,
1414
+ map: mapLibreMap,
1415
+ onDiagnostic: reportDiagnostic,
1416
+ onInteractionEvent(event) {
1417
+ options.onInteractionEvent?.(event);
1418
+ },
1419
+ setHiddenAnnotations: annotationRuntime.setHiddenAnnotations
1420
+ });
1421
+ dataRuntime = mapDataRuntime;
1422
+ registerCleanup(() => {
1423
+ mapDataRuntime.dispose();
1424
+ if (dataRuntime === mapDataRuntime) dataRuntime = void 0;
1425
+ });
1426
+ const featureRuntime = createTileflowMapLibreSemanticDomRuntime({
1427
+ cancelFrame: (frame) => browserWindow.cancelAnimationFrame(frame),
1428
+ createOverlay,
1429
+ customPopup: current.hasPopupRenderer,
1430
+ customTooltip: current.hasTooltipRenderer,
1431
+ document: container.ownerDocument,
1432
+ interactionState: coordinator.getInteractionState(),
1433
+ map: mapLibreMap,
1434
+ onDiagnostic: reportDiagnostic,
1435
+ onInteractionStateChange: coordinator.requestInteractionState,
1436
+ poiMap: createTileflowMapLibrePoiMap(mapLibreMap),
1437
+ requestFrame: (callback) => browserWindow.requestAnimationFrame(() => callback())
1438
+ });
1439
+ semanticRuntime = featureRuntime;
1440
+ registerCleanup(() => featureRuntime.dispose());
1441
+ registerCleanup(coordinator.attach("annotation", annotationRuntime));
1442
+ registerCleanup(coordinator.attach("semantic", featureRuntime));
1443
+ registerCleanup(
1444
+ annotationRuntime.subscribeEvents((event) => options.onInteractionEvent?.(event))
1445
+ );
1446
+ registerCleanup(
1447
+ annotationRuntime.subscribeDiagnostics((diagnostics) => {
1448
+ annotationDiagnostics = diagnostics;
1449
+ invalidate();
1450
+ })
1451
+ );
1452
+ registerCleanup(
1453
+ annotationRuntime.subscribeRenderTargets((targets) => {
1454
+ annotationRenderTargets = targets;
1455
+ invalidate();
1456
+ })
1457
+ );
1458
+ registerCleanup(
1459
+ featureRuntime.subscribeEvents((event) => {
1460
+ if (event.target.kind === "annotation") return;
1461
+ options.onInteractionEvent?.({ ...event, target: event.target });
1462
+ })
1463
+ );
1464
+ registerCleanup(
1465
+ featureRuntime.subscribeDiagnostics((diagnostics) => {
1466
+ semanticDiagnostics = diagnostics;
1467
+ invalidate();
1468
+ })
1469
+ );
1470
+ registerCleanup(
1471
+ featureRuntime.subscribeRenderTargets((targets) => {
1472
+ semanticRenderTargets = targets;
1473
+ invalidate();
1474
+ })
1475
+ );
1476
+ annotationDiagnostics = annotationRuntime.getDiagnostics();
1477
+ annotationRenderTargets = annotationRuntime.getRenderTargets();
1478
+ semanticDiagnostics = featureRuntime.getDiagnostics();
1479
+ semanticRenderTargets = featureRuntime.getRenderTargets();
1480
+ reconcileAnnotations(annotationRuntime, annotationsForRuntime);
1481
+ mapDataRuntime.reconcile(dataInput);
1482
+ const initialInteractions = current.preparedInteractions;
1483
+ if (initialInteractions.ok)
1484
+ reconcileInteractions(featureRuntime, initialInteractions.bindings);
1485
+ const navigation = createTileflowNavigationControlBinding({
1486
+ createControl: (controlOptions) => new maplibregl.NavigationControl(controlOptions),
1487
+ map: mapLibreMap
1488
+ });
1489
+ navigationBinding = navigation;
1490
+ registerCleanup(() => {
1491
+ navigation.dispose();
1492
+ if (navigationBinding === navigation) navigationBinding = void 0;
1493
+ });
1494
+ navigation.update(current.resolvedInteractive ? options.navigationControl : false);
1495
+ const initialFit = current.cameraFit;
1496
+ appliedCameraFitKey = initialFit.key;
1497
+ if (initialFit.error) reportError("camera", new Error(initialFit.error));
1498
+ if (initialFit.fit) fitCameraTo(mapLibreMap, initialFit.fit, 0);
1499
+ const resizeObserver = new ResizeObserver(() => mapLibreMap.resize());
1500
+ registerCleanup(() => resizeObserver.disconnect());
1501
+ resizeObserver.observe(container);
1502
+ const lifecycle = attachTileflowMapLifecycle({
1503
+ getSession: () => {
1504
+ const analyticsForLoad = current.resolvedAnalytics;
1505
+ return {
1506
+ analytics: analyticsForLoad,
1507
+ styleId: analyticsForLoad?.styleId ?? (typeof initialRuntimeStyle.style === "string" ? initialRuntimeStyle.style : current.mapName)
1508
+ };
1509
+ },
1510
+ map: mapLibreMap,
1511
+ onLoad: (loadedMap) => {
1512
+ settleReady({ map: loadedMap });
1513
+ options.onLoad?.(loadedMap);
1514
+ },
1515
+ scheduler: {
1516
+ cancelFrame: (frame) => browserWindow.cancelAnimationFrame(frame),
1517
+ requestFrame: (callback) => browserWindow.requestAnimationFrame(callback)
1518
+ },
1519
+ sessionStarter,
1520
+ setState: (state) => {
1521
+ captureState = state;
1522
+ invalidate();
1523
+ },
1524
+ subscribe: (subscribedMap, event, listener) => {
1525
+ const subscription = subscribedMap.on(event, listener);
1526
+ return () => subscription.unsubscribe();
1527
+ }
1528
+ });
1529
+ registerCleanup(() => lifecycle.dispose());
1530
+ const latestRuntimeStyle = current.runtimeStyle;
1531
+ const latestBlend = current.blendResolution.styles && current.themeBlendPosition !== void 0 ? {
1532
+ hold: current.themeBlendHold,
1533
+ position: current.themeBlendPosition,
1534
+ themes: current.blendResolution.styles
1535
+ } : null;
1536
+ if (controller && latestBlend) {
1537
+ if (latestRuntimeStyle) activeRuntimeResource = latestRuntimeStyle;
1538
+ void controller.setBlend(latestBlend, { transition: options.themeTransition });
1539
+ } else if (controller && latestRuntimeStyle && latestRuntimeStyle !== initialRuntimeStyle) {
1540
+ activeRuntimeResource = latestRuntimeStyle;
1541
+ void controller.setTheme(latestRuntimeStyle, { transition: options.themeTransition });
1542
+ }
1543
+ invalidate();
1544
+ }).catch((error) => {
1545
+ resourceLifetime.abort();
1546
+ try {
1547
+ disposeMap?.();
1548
+ } catch (cleanupError) {
1549
+ reportError("runtime", cleanupError);
1550
+ }
1551
+ if (!cancelled && !destroyed) {
1552
+ reportError("runtime", error);
1553
+ captureState = "error";
1554
+ invalidate();
1555
+ }
1556
+ });
1557
+ return () => {
1558
+ cancelled = true;
1559
+ resourceLifetime.abort();
1560
+ disposeMap?.();
1561
+ };
1562
+ }
1563
+ function computeStatus() {
1564
+ const d = current;
1565
+ const diagnostics = [
1566
+ ...d.declarativeDiagnostics,
1567
+ ...annotationBridgeDiagnostic ? [annotationBridgeDiagnostic] : [],
1568
+ ...semanticBridgeDiagnostic ? [semanticBridgeDiagnostic] : [],
1569
+ ...stateBridgeDiagnostic ? [stateBridgeDiagnostic] : [],
1570
+ ...rendererBridgeDiagnostic ? [rendererBridgeDiagnostic] : [],
1571
+ ...viewBridgeDiagnostic ? [viewBridgeDiagnostic] : [],
1572
+ ...annotationDiagnostics,
1573
+ ...semanticDiagnostics
1574
+ ];
1575
+ const interactionReadiness = diagnostics.some(
1576
+ (diagnostic) => diagnostic.level === "error"
1577
+ ) ? "error" : viewHost.isSettled() ? "idle" : "loading";
1578
+ const currentCaptureState = activeRuntimeResource === (d.isImageMode ? d.runtimeImageUrl : d.runtimeStyle) ? captureState : "loading";
1579
+ const parts = [
1580
+ currentCaptureState,
1581
+ interactionReadiness,
1582
+ d.runtimeResolutionState,
1583
+ themeTransitionState
1584
+ ];
1585
+ return {
1586
+ diagnostics,
1587
+ readiness: parts.includes("error") ? "error" : parts.every((part) => part === "idle") ? "idle" : "loading",
1588
+ theme: d.themeResolution.name,
1589
+ imageUrl: d.isImageMode ? d.runtimeImageUrl : void 0
1590
+ };
1591
+ }
1592
+ function emitStatus() {
1593
+ if (destroyed) return;
1594
+ const status = computeStatus();
1595
+ if (lastStatus && lastStatus.readiness === status.readiness && lastStatus.theme === status.theme && lastStatus.imageUrl === status.imageUrl && lastStatus.diagnostics.length === status.diagnostics.length && lastStatus.diagnostics.every((diagnostic, index) => diagnostic === status.diagnostics[index])) {
1596
+ return;
1597
+ }
1598
+ lastStatus = status;
1599
+ if (current.isImageMode && status.readiness === "idle") settleReady({ map: null });
1600
+ if (statusElement) {
1601
+ setAttribute(statusElement, "data-tileflow-capture-id", captureId);
1602
+ setAttribute(statusElement, "data-tileflow-map", current.mapName);
1603
+ setAttribute(statusElement, "data-tileflow-theme", status.theme);
1604
+ setAttribute(statusElement, "data-tileflow-state", status.readiness);
1605
+ }
1606
+ options.onStatusChange?.(status);
1607
+ }
1608
+ function update(next) {
1609
+ if (destroyed) return;
1610
+ const merged = { ...options, ...next };
1611
+ assertTileflowMapStyleOptions(merged);
1612
+ if (merged.views && merged.renderTargets) {
1613
+ throw new TypeError("A Tileflow map takes views or renderTargets, not both.");
1614
+ }
1615
+ options = merged;
1616
+ if (reconciling) {
1617
+ dirty = true;
1618
+ return;
1619
+ }
1620
+ reconcile();
1621
+ }
1622
+ reconcile();
1623
+ return {
1624
+ container,
1625
+ destroy() {
1626
+ if (destroyed) return;
1627
+ destroyed = true;
1628
+ settleReady({ error: new DOMException("The Tileflow map was destroyed.", "AbortError") });
1629
+ try {
1630
+ viewHost.dispose();
1631
+ reconciler.dispose();
1632
+ } finally {
1633
+ if (statusElement) {
1634
+ for (const attribute of statusAttributes) statusElement.removeAttribute(attribute);
1635
+ }
1636
+ }
1637
+ },
1638
+ getInteractionState: () => interactionCoordinator?.getInteractionState() ?? coordinatedInteractionState,
1639
+ getMap: () => map,
1640
+ getRenderTargets: () => lastRenderTargets,
1641
+ getStatus: () => lastStatus ?? computeStatus(),
1642
+ getThemeController: () => themeController ?? null,
1643
+ ready,
1644
+ setAnnotations: (annotations) => update({ annotations }),
1645
+ setBounds: (bounds, fitBoundsOptions) => update(fitBoundsOptions === void 0 ? { bounds } : { bounds, fitBoundsOptions }),
1646
+ setColors: (colors) => update({ colors }),
1647
+ setEmphasis: (emphasis) => update({ emphasis }),
1648
+ setInteractions: (interactions) => update({ interactions }),
1649
+ setInteractionState(state) {
1650
+ if (controlledInteractionOwnership) {
1651
+ update({ interactionState: state });
1652
+ return;
1653
+ }
1654
+ const parsed = tileflowInteractionStateSchema2.parse(state);
1655
+ coordinatedInteractionState = parsed;
1656
+ interactionCoordinator?.setInteractionState(parsed);
1657
+ },
1658
+ setTheme: (theme) => update({ theme }),
1659
+ setThemeBlend: (themeBlend) => update({ themeBlend }),
1660
+ setView: (view) => update(view),
1661
+ setViews: (views) => update({ views }),
1662
+ update
1663
+ };
1664
+ }
1665
+ function tileflowMapRenderers(options) {
1666
+ const external = options.renderTargets;
1667
+ return external ? {
1668
+ hasMarkerRenderer: external.marker === true,
1669
+ hasPopupRenderer: external.popup === true,
1670
+ hasTooltipRenderer: external.tooltip === true
1671
+ } : {
1672
+ hasMarkerRenderer: options.views?.marker !== void 0,
1673
+ hasPopupRenderer: options.views?.popup !== void 0,
1674
+ hasTooltipRenderer: options.views?.tooltip !== void 0
1675
+ };
1676
+ }
1677
+ function hasTileflowMapInteractionConfiguration(options, renderers) {
1678
+ return options.annotationClusters !== void 0 || options.annotations !== void 0 || options.dataOverlays !== void 0 || options.dataSources !== void 0 || options.defaultInteractionState !== void 0 || options.interactionState !== void 0 || options.interactions !== void 0 || options.onInteractionDiagnostic !== void 0 || options.onInteractionEvent !== void 0 || options.onInteractionStateChange !== void 0 || renderers.hasMarkerRenderer || renderers.hasPopupRenderer || renderers.hasTooltipRenderer;
1679
+ }
1680
+ function tileflowMapDeclarativeDiagnostics(input) {
1681
+ const modeDiagnostic = input.isImageMode ? imageModeMapInteractionDiagnostic(
1682
+ input.preparedAnnotations.annotations.length,
1683
+ input.interactions.length,
1684
+ input.hasInteractionConfiguration
1685
+ ) : void 0;
1686
+ return [
1687
+ ...modeDiagnostic ? [modeDiagnostic] : [],
1688
+ ...input.preparedAnnotations.diagnostics,
1689
+ ...input.preparedInteractions.diagnostics,
1690
+ ...input.preparedInteractionState.diagnostics,
1691
+ ...input.isImageMode ? [] : unusedTileflowRendererDiagnostics({
1692
+ annotations: input.preparedAnnotations.annotations,
1693
+ bindings: input.interactions,
1694
+ hasPopupRenderer: input.hasPopupRenderer,
1695
+ hasTooltipRenderer: input.hasTooltipRenderer
1696
+ })
1697
+ ];
1698
+ }
1699
+ function getTileflowMapInitialStatus(options, controlledInteractionOwnership = options.interactionState !== void 0) {
1700
+ const interactions = options.interactions ?? noInteractions;
1701
+ const renderers = tileflowMapRenderers(options);
1702
+ const isImageMode = options.mode === "image";
1703
+ const diagnostics = tileflowMapDeclarativeDiagnostics({
1704
+ hasInteractionConfiguration: hasTileflowMapInteractionConfiguration(options, renderers),
1705
+ hasPopupRenderer: renderers.hasPopupRenderer,
1706
+ hasTooltipRenderer: renderers.hasTooltipRenderer,
1707
+ interactions,
1708
+ isImageMode,
1709
+ preparedAnnotations: prepareTileflowAnnotations(options.annotations),
1710
+ preparedInteractions: prepareTileflowInteractionBindings(interactions),
1711
+ preparedInteractionState: prepareTileflowInteractionState(
1712
+ options.interactionState,
1713
+ options.defaultInteractionState,
1714
+ controlledInteractionOwnership
1715
+ )
1716
+ });
1717
+ const selection = options.themeBlend ? nearestTileflowBlendTheme(options.themeBlend) : options.theme;
1718
+ return {
1719
+ diagnostics,
1720
+ imageUrl: isImageMode ? options.imageUrl : void 0,
1721
+ readiness: diagnostics.some((diagnostic) => diagnostic.level === "error") ? "error" : "loading",
1722
+ theme: selection && selection !== "system" ? selection : void 0
1723
+ };
1724
+ }
1725
+ function setAttribute(element, name, value) {
1726
+ if (value === void 0) element.removeAttribute(name);
1727
+ else if (element.getAttribute(name) !== value) element.setAttribute(name, value);
1728
+ }
1729
+ function fitCameraTo(map, fit, duration) {
1730
+ map.fitBounds(fit.bounds, {
1731
+ ...fit.options.maxZoom === void 0 ? {} : { maxZoom: fit.options.maxZoom },
1732
+ ...fit.options.padding === void 0 ? {} : { padding: fit.options.padding },
1733
+ ...duration !== void 0 ? { duration } : fit.options.duration === void 0 ? {} : { duration: fit.options.duration }
1734
+ });
1735
+ }
1736
+ function wrapMapLibrePositioned(positioned) {
1737
+ return {
1738
+ addTo(map) {
1739
+ positioned.addTo(map);
1740
+ },
1741
+ remove() {
1742
+ positioned.remove();
1743
+ },
1744
+ setLngLat(coordinate) {
1745
+ positioned.setLngLat([coordinate[0], coordinate[1]]);
1746
+ }
1747
+ };
1748
+ }
1749
+ function createTileflowMapLibrePoiMap(map) {
1750
+ const poiMap = map;
1751
+ return {
1752
+ getStyle: () => map.getStyle(),
1753
+ on(event, listener) {
1754
+ return poiMap.on(event, listener);
1755
+ },
1756
+ queryRenderedFeatures(point, queryOptions) {
1757
+ return map.queryRenderedFeatures(point, {
1758
+ layers: [...queryOptions.layers]
1759
+ });
1760
+ }
1761
+ };
1762
+ }
1763
+ export {
1764
+ configureTileflowMapLibre,
1765
+ createTileflowMap,
1766
+ getTileflowMapInitialStatus,
1767
+ loadTileflowMapLibre
1768
+ };