@tileflow/vue 0.1.0-alpha.19 → 0.1.0-alpha.21

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 CHANGED
@@ -1,5 +1,4 @@
1
1
  // src/index.ts
2
- import maplibregl from "maplibre-gl";
3
2
  import {
4
3
  computed,
5
4
  defineComponent,
@@ -7,44 +6,177 @@ import {
7
6
  nextTick,
8
7
  onBeforeUnmount,
9
8
  onMounted,
9
+ onUpdated,
10
10
  ref,
11
11
  shallowRef,
12
- watch
12
+ Teleport,
13
+ watch,
14
+ watchEffect
13
15
  } from "vue";
16
+ import {
17
+ attachTileflowFairUseNotice,
18
+ attachTileflowMapLifecycle,
19
+ createTileflowSessionStarter,
20
+ createTileflowThemeController,
21
+ createTileflowTransformRequest,
22
+ getTileflowSystemColorScheme,
23
+ loadTileflowStyleFonts,
24
+ registerTileflowContourProtocol,
25
+ registerTileflowPmtilesProtocol,
26
+ registerTileflowWorldRequestBridge,
27
+ subscribeTileflowSystemColorScheme
28
+ } from "@tileflow/core/browser";
29
+ import { normalizeTileflowCaptureId } from "@tileflow/core/capture";
14
30
  import {
15
31
  createTileflowSessionController,
16
32
  defaultTileflowManifestUrl,
33
+ defaultTileflowRuntimeView,
17
34
  loadTileflowManifest,
18
35
  mergeTileflowAnalytics,
19
- normalizeTileflowCaptureId,
36
+ normalizeTileflowRuntimeCenter,
20
37
  normalizeTileflowStaticImageSize,
21
38
  resolveTileflowManifestMap,
22
39
  resolveTileflowMapMode,
23
40
  resolveTileflowRuntimeStyle,
41
+ resolveTileflowRuntimeTheme,
42
+ resolveTileflowRuntimeView,
24
43
  resolveTileflowStaticImageUrl,
25
44
  shouldLoadTileflowManifest
26
- } from "@tileflow/core";
45
+ } from "@tileflow/core/runtime";
27
46
  import {
28
- attachTileflowMapLifecycle,
29
- createTileflowMarkerController,
30
- createTileflowSessionStarter,
31
- createTileflowTransformRequest
32
- } from "@tileflow/core/browser";
33
- var defaultCenter = [0, 20];
34
- var defaultZoom = 2;
47
+ initialTileflowInteractionState
48
+ } from "@tileflow/interactions";
49
+ import {
50
+ createTileflowMapLibreDomRuntime,
51
+ createTileflowMapLibreInteractionCoordinator,
52
+ createTileflowMapLibreSemanticDomRuntime
53
+ } from "@tileflow/interactions/maplibre";
54
+
55
+ // src/interactions.ts
56
+ import {
57
+ tileflowInteractionStateSchema,
58
+ validateTileflowAnnotations,
59
+ validateTileflowInteractionBindings
60
+ } from "@tileflow/interactions";
61
+ function resolveTileflowVueAnnotations({ annotations }) {
62
+ const candidates = annotations ?? [];
63
+ const validation = validateTileflowAnnotations(candidates);
64
+ if (!validation.ok) {
65
+ return {
66
+ annotations: [],
67
+ diagnostics: validation.diagnostics,
68
+ ok: false
69
+ };
70
+ }
71
+ return {
72
+ // Validation deliberately does not replace application data with a schema clone. Slot
73
+ // contexts keep the caller's typed annotation and data identities.
74
+ annotations: candidates,
75
+ diagnostics: [],
76
+ ok: true
77
+ };
78
+ }
79
+ function resolveTileflowVueInteractionBindings(interactions) {
80
+ const candidates = interactions ?? [];
81
+ const validation = validateTileflowInteractionBindings(candidates);
82
+ if (!validation.ok) {
83
+ return { bindings: [], diagnostics: validation.diagnostics, ok: false };
84
+ }
85
+ return {
86
+ // As with annotations, validation must not replace caller-owned objects: bindings can be
87
+ // compared by identity by framework consumers even though their runtime shape is JSON-safe.
88
+ bindings: candidates,
89
+ diagnostics: [],
90
+ ok: true
91
+ };
92
+ }
93
+ function validateTileflowVueInteractionState(interactionState, defaultInteractionState) {
94
+ if (interactionState !== void 0 && defaultInteractionState !== void 0) {
95
+ return [
96
+ createTileflowVueInteractionDiagnostic(
97
+ "INVALID_DOCUMENT",
98
+ "interactionState and defaultInteractionState are mutually exclusive."
99
+ )
100
+ ];
101
+ }
102
+ const candidate = interactionState ?? defaultInteractionState;
103
+ if (candidate === void 0) return [];
104
+ const statePath = interactionState === void 0 ? "/defaultInteractionState" : "/interactionState";
105
+ const parsed = tileflowInteractionStateSchema.safeParse(candidate);
106
+ if (parsed.success) return [];
107
+ return parsed.error.issues.map(
108
+ (issue) => createTileflowVueInteractionDiagnostic(
109
+ "INVALID_DOCUMENT",
110
+ issue.message,
111
+ `${statePath}${jsonPointer(issue.path)}`
112
+ )
113
+ );
114
+ }
115
+ function createTileflowVueInteractionDiagnostic(code, message, path) {
116
+ return {
117
+ code,
118
+ level: "error",
119
+ message,
120
+ ...path === void 0 ? {} : { path }
121
+ };
122
+ }
123
+ function jsonPointer(path) {
124
+ return path.map((segment) => String(segment).replaceAll("~", "~0").replaceAll("/", "~1")).map((segment) => `/${segment}`).join("");
125
+ }
126
+
127
+ // src/maplibre.ts
128
+ var mapLibrePromise;
129
+ function loadTileflowMapLibre() {
130
+ mapLibrePromise ??= import("maplibre-gl").then(resolveMapLibreModule).catch((error) => {
131
+ mapLibrePromise = void 0;
132
+ throw error;
133
+ });
134
+ return mapLibrePromise;
135
+ }
136
+ function resolveMapLibreModule(module) {
137
+ const defaultExport = module.default;
138
+ return defaultExport ?? module;
139
+ }
140
+
141
+ // src/style-source.ts
142
+ import {
143
+ validateTileflowRuntimeSource,
144
+ validateTileflowThemeSelection
145
+ } from "@tileflow/core/runtime";
146
+ function validateTileflowMapStyleInputs(input) {
147
+ const sourceValidation = validateTileflowRuntimeSource(input.source);
148
+ if (!sourceValidation.ok) return sourceValidation;
149
+ if (input.theme !== void 0 && !validateTileflowThemeSelection(input.theme)) {
150
+ return { error: 'theme must be a concrete portable theme name or "system"', ok: false };
151
+ }
152
+ if (input.theme !== void 0 && input.source.kind !== "tileflow") {
153
+ return { error: "theme is only valid with a tileflow source", ok: false };
154
+ }
155
+ return { ok: true };
156
+ }
157
+ function assertTileflowMapStyleInputs(input) {
158
+ const validation = validateTileflowMapStyleInputs(input);
159
+ if (!validation.ok) {
160
+ throw new TypeError(`Invalid TileflowMap source: ${validation.error}`);
161
+ }
162
+ }
163
+
164
+ // src/index.ts
35
165
  var TileflowMap = defineComponent({
36
166
  name: "TileflowMap",
37
167
  inheritAttrs: false,
168
+ slots: Object,
38
169
  props: {
39
170
  alt: {
40
171
  default: "",
41
172
  type: String
42
173
  },
43
174
  analytics: Object,
175
+ annotations: Array,
44
176
  center: Array,
45
177
  captureId: String,
46
178
  className: String,
47
- config: Object,
179
+ defaultInteractionState: Object,
48
180
  height: {
49
181
  default: 420,
50
182
  type: [Number, String]
@@ -58,127 +190,361 @@ var TileflowMap = defineComponent({
58
190
  default: void 0,
59
191
  type: Boolean
60
192
  },
61
- manifestUrl: {
62
- default: defaultTileflowManifestUrl,
63
- type: String
64
- },
65
- map: String,
193
+ interactions: Array,
194
+ interactionState: Object,
66
195
  mapOptions: Object,
67
- mapStyle: Object,
68
- markers: {
69
- default: () => [],
70
- type: Array
71
- },
72
196
  mode: {
73
197
  default: "interactive",
74
198
  type: String
75
199
  },
76
- preferLocalDev: {
77
- default: true,
78
- type: Boolean
200
+ source: {
201
+ required: true,
202
+ type: Object
79
203
  },
80
- styleBaseUrl: String,
81
- styleUrl: String,
82
- themes: Object,
204
+ theme: String,
83
205
  zoom: Number
84
206
  },
85
207
  emits: {
86
- load: (_map) => true
208
+ interactionDiagnostic: (_diagnostic) => true,
209
+ interactionEvent: (_event) => true,
210
+ load: (_map) => true,
211
+ themeChange: (_transition) => true,
212
+ "update:interactionState": (_state) => true
87
213
  },
88
- setup(props, { attrs, emit }) {
214
+ setup(props, { attrs, emit, slots }) {
89
215
  const containerRef = ref(null);
90
- const manifestMap = shallowRef(null);
216
+ const loadedManifestMap = shallowRef(null);
217
+ const manifestResolutionKey = ref("");
218
+ const manifestResolutionState = ref("loading");
91
219
  const imageSize = shallowRef(null);
92
220
  const mapRef = shallowRef(null);
93
- const captureState = ref("loading");
94
- const markerController = createTileflowMarkerController({
95
- attach(markerInstance, map, marker) {
96
- markerInstance.setLngLat(marker.coordinates).addTo(map);
97
- markerInstance.getElement().title = marker.label ?? marker.id;
98
- },
99
- create: (marker) => new maplibregl.Marker({
100
- color: marker.color ?? "#C6A15B"
101
- }),
102
- remove: (marker) => marker.remove()
103
- });
221
+ const systemColorScheme = ref("light");
222
+ const mapCaptureState = ref("loading");
223
+ const themeTransitionState = ref("idle");
224
+ const activeRuntimeResource = shallowRef(void 0);
225
+ const interactionCaptureState = ref("idle");
226
+ const annotationRenderTargets = shallowRef([]);
227
+ const semanticRenderTargets = shallowRef(
228
+ []
229
+ );
230
+ const controlledInteractionOwnership = props.interactionState !== void 0;
231
+ let annotationRuntime = null;
232
+ let semanticRuntime = null;
233
+ let interactionRuntimesDisposing = false;
234
+ let initializeSemanticRuntime = null;
235
+ let annotationRuntimeDiagnosticsUnsubscribe = null;
236
+ let annotationRuntimeCoordinatorDetach = null;
237
+ let annotationRuntimeEventsUnsubscribe = null;
238
+ let annotationRuntimeTargetsUnsubscribe = null;
239
+ let semanticRuntimeDiagnosticsUnsubscribe = null;
240
+ let semanticRuntimeCoordinatorDetach = null;
241
+ let semanticRuntimeEventsUnsubscribe = null;
242
+ let semanticRuntimeTargetsUnsubscribe = null;
243
+ let activeAnnotationDiagnosticKeys = /* @__PURE__ */ new Set();
244
+ let activeSemanticDiagnosticKeys = /* @__PURE__ */ new Set();
245
+ let annotationRuntimeDiagnostics = [];
246
+ let semanticRuntimeDiagnostics = [];
104
247
  let imageResizeObserver = null;
248
+ let mapFairUseNotice = null;
105
249
  let mapLifecycle = null;
106
250
  let mapResizeObserver = null;
251
+ let mapWorldRequestBridge = null;
252
+ let themeController = null;
253
+ let unsubscribeSystemColorScheme = null;
254
+ let mapLoadId = 0;
107
255
  let manifestLoadId = 0;
108
256
  let readinessRunId = 0;
109
- const resolvedMode = computed(
110
- () => resolveTileflowMapMode({
111
- imageUrl: props.imageUrl,
112
- mode: props.mode,
113
- preferLocalDev: props.preferLocalDev
257
+ let interactionReadinessRunId = 0;
258
+ let interactionReadinessFrames = [];
259
+ let interactionOverlayFailed = false;
260
+ let interactionInputDiagnosticSignature = "";
261
+ const resolvedMode = computed(() => resolveTileflowMapMode({ mode: props.mode }));
262
+ const isImageMode = computed(() => resolvedMode.value === "image");
263
+ const annotationResolution = computed(
264
+ () => resolveTileflowVueAnnotations({
265
+ annotations: props.annotations
114
266
  })
115
267
  );
116
- const isImageMode = computed(() => resolvedMode.value === "image");
117
- const imageCenter = computed(() => props.center ?? defaultCenter);
118
- const imageZoom = computed(() => props.zoom ?? defaultZoom);
268
+ const interactionBindingResolution = computed(
269
+ () => resolveTileflowVueInteractionBindings(props.interactions)
270
+ );
271
+ const interactionStateDiagnostics = computed(() => {
272
+ const diagnostics = [
273
+ ...validateTileflowVueInteractionState(
274
+ props.interactionState,
275
+ props.defaultInteractionState
276
+ )
277
+ ];
278
+ if (props.interactionState !== void 0 !== controlledInteractionOwnership) {
279
+ diagnostics.push(
280
+ createTileflowVueInteractionDiagnostic(
281
+ "INVALID_DOCUMENT",
282
+ "Tileflow interaction state ownership cannot switch between controlled and uncontrolled."
283
+ )
284
+ );
285
+ }
286
+ return diagnostics;
287
+ });
288
+ const hasInteractionConfiguration = computed(
289
+ () => props.annotations !== void 0 || props.interactions !== void 0 || props.interactionState !== void 0 || props.defaultInteractionState !== void 0 || slots.marker !== void 0 || slots.popup !== void 0 || slots.tooltip !== void 0
290
+ );
291
+ const interactionInputDiagnostics = computed(() => {
292
+ const diagnostics = [
293
+ ...annotationResolution.value.diagnostics,
294
+ ...interactionBindingResolution.value.diagnostics,
295
+ ...interactionStateDiagnostics.value
296
+ ];
297
+ if (isImageMode.value && hasInteractionConfiguration.value) {
298
+ diagnostics.push(
299
+ createTileflowVueInteractionDiagnostic(
300
+ "UNSUPPORTED_MODE",
301
+ "Annotations, semantic interactions, and interaction state are unavailable in image mode."
302
+ )
303
+ );
304
+ }
305
+ return diagnostics;
306
+ });
307
+ const initialInteractionState = validateTileflowVueInteractionState(props.interactionState, props.defaultInteractionState).length === 0 ? props.interactionState ?? props.defaultInteractionState ?? initialTileflowInteractionState : initialTileflowInteractionState;
308
+ const interactionCoordinator = createTileflowMapLibreInteractionCoordinator({
309
+ onInteractionStateChange(nextState) {
310
+ if (interactionRuntimesDisposing) return;
311
+ emit("update:interactionState", nextState);
312
+ },
313
+ ...controlledInteractionOwnership ? { interactionState: initialInteractionState } : { defaultInteractionState: initialInteractionState }
314
+ });
315
+ const mapName = computed(
316
+ () => props.source.kind === "tileflow" ? props.source.map : void 0
317
+ );
318
+ const manifestUrl = computed(
319
+ () => props.source.kind === "tileflow" ? props.source.manifestUrl ?? defaultTileflowManifestUrl : defaultTileflowManifestUrl
320
+ );
321
+ const shouldLoadManifest = computed(
322
+ () => shouldLoadTileflowManifest({
323
+ source: props.source
324
+ })
325
+ );
326
+ const manifestRequestKey = computed(
327
+ () => shouldLoadManifest.value ? JSON.stringify([manifestUrl.value, mapName.value]) : "not-needed"
328
+ );
329
+ const currentManifestResolutionState = computed(
330
+ () => manifestResolutionKey.value === manifestRequestKey.value ? manifestResolutionState.value : shouldLoadManifest.value ? "loading" : "not-needed"
331
+ );
332
+ const manifestMap = computed(
333
+ () => manifestResolutionKey.value === manifestRequestKey.value && manifestResolutionState.value === "ready" ? loadedManifestMap.value : null
334
+ );
335
+ const themeResolution = computed(() => {
336
+ if (!manifestMap.value || props.source.kind !== "tileflow") {
337
+ return {
338
+ error: false,
339
+ name: props.source.kind === "tileflow" && props.theme && props.theme !== "system" ? props.theme : void 0
340
+ };
341
+ }
342
+ try {
343
+ return {
344
+ error: false,
345
+ name: resolveTileflowRuntimeTheme(manifestMap.value, props.theme, systemColorScheme.value).name
346
+ };
347
+ } catch {
348
+ return { error: true, name: void 0 };
349
+ }
350
+ });
351
+ const resolvedThemeName = computed(() => themeResolution.value.name);
352
+ const manifestView = computed(
353
+ () => resolveTileflowRuntimeView({ manifestMap: manifestMap.value })
354
+ );
355
+ const manifestCenter = computed(
356
+ () => normalizeTileflowRuntimeCenter(manifestView.value.center)
357
+ );
358
+ const imageCenter = computed(
359
+ () => normalizeTileflowRuntimeCenter(
360
+ props.center ?? props.mapOptions?.center,
361
+ manifestCenter.value
362
+ )
363
+ );
364
+ const imageZoom = computed(
365
+ () => props.zoom ?? props.mapOptions?.zoom ?? manifestView.value.zoom ?? defaultTileflowRuntimeView.zoom
366
+ );
119
367
  const resolvedCenter = computed(
120
- () => props.center ?? props.mapOptions?.center ?? defaultCenter
368
+ () => props.center ?? props.mapOptions?.center ?? manifestCenter.value
369
+ );
370
+ const resolvedZoom = computed(
371
+ () => props.zoom ?? props.mapOptions?.zoom ?? manifestView.value.zoom ?? defaultTileflowRuntimeView.zoom
121
372
  );
122
- const resolvedZoom = computed(() => props.zoom ?? props.mapOptions?.zoom ?? defaultZoom);
373
+ const resolvedBearing = computed(() => props.mapOptions?.bearing ?? manifestView.value.bearing);
374
+ const resolvedPitch = computed(() => props.mapOptions?.pitch ?? manifestView.value.pitch);
123
375
  const resolvedInteractive = computed(
124
376
  () => props.interactive ?? props.mapOptions?.interactive ?? true
125
377
  );
126
378
  const runtimeStyle = computed(
127
- () => isImageMode.value ? null : resolveTileflowRuntimeStyle({
128
- config: props.config,
379
+ () => isImageMode.value || themeResolution.value.error ? null : resolveTileflowRuntimeStyle({
380
+ colorScheme: systemColorScheme.value,
129
381
  manifestMap: manifestMap.value,
130
- map: props.map,
131
- preferLocalDev: props.preferLocalDev,
132
- style: props.mapStyle,
133
- styleBaseUrl: props.styleBaseUrl,
134
- styleUrl: props.styleUrl,
135
- themes: props.themes
382
+ source: props.source,
383
+ theme: themeResolution.value.name
136
384
  })
137
385
  );
138
386
  const resolvedAnalytics = computed(
139
387
  () => mergeTileflowAnalytics(props.analytics, runtimeStyle.value?.analytics)
140
388
  );
141
389
  const runtimeImageUrl = computed(
142
- () => props.imageUrl ?? (isImageMode.value ? resolveTileflowStaticImageUrl({
390
+ () => props.imageUrl ?? (isImageMode.value && !themeResolution.value.error ? resolveTileflowStaticImageUrl({
143
391
  center: imageCenter.value,
392
+ colorScheme: systemColorScheme.value,
144
393
  imageSize: imageSize.value,
145
394
  manifestMap: manifestMap.value,
395
+ theme: themeResolution.value.name,
146
396
  zoom: imageZoom.value
147
397
  }) : void 0)
148
398
  );
399
+ const runtimeResolutionState = computed(() => {
400
+ if (shouldLoadManifest.value && currentManifestResolutionState.value !== "ready") {
401
+ return currentManifestResolutionState.value === "error" ? "error" : "loading";
402
+ }
403
+ if (themeResolution.value.error) return "error";
404
+ if (isImageMode.value) {
405
+ if (runtimeImageUrl.value) return "idle";
406
+ return props.imageUrl === void 0 && imageSize.value === null ? "loading" : "error";
407
+ }
408
+ return runtimeStyle.value ? "idle" : "error";
409
+ });
410
+ const currentMapCaptureState = computed(
411
+ () => activeRuntimeResource.value === (isImageMode.value ? runtimeImageUrl.value : runtimeStyle.value) ? mapCaptureState.value : "loading"
412
+ );
413
+ const captureState = computed(() => {
414
+ if (currentMapCaptureState.value === "error" || interactionCaptureState.value === "error" || runtimeResolutionState.value === "error" || themeTransitionState.value === "error") {
415
+ return "error";
416
+ }
417
+ if (currentMapCaptureState.value === "loading" || interactionCaptureState.value === "loading" || runtimeResolutionState.value === "loading" || themeTransitionState.value === "loading") {
418
+ return "loading";
419
+ }
420
+ return "idle";
421
+ });
149
422
  const frameStyle = computed(() => ({
150
- height: formatHeight(props.height),
423
+ height: formatHeight(props.height ?? 420),
151
424
  minHeight: "240px",
152
425
  overflow: "hidden",
153
426
  position: "relative",
154
427
  width: "100%"
155
428
  }));
156
429
  const resolvedCaptureId = computed(() => normalizeTileflowCaptureId(props.captureId));
157
- const refreshManifest = async () => {
158
- const shouldLoad = shouldLoadTileflowManifest({
159
- config: props.config,
160
- imageMode: isImageMode.value,
161
- imageUrl: props.imageUrl,
162
- map: props.map,
163
- style: props.mapStyle,
164
- styleBaseUrl: props.styleBaseUrl,
165
- styleUrl: props.styleUrl
430
+ const emitInteractionDiagnostics = (diagnostics) => {
431
+ for (const diagnostic of diagnostics) emit("interactionDiagnostic", diagnostic);
432
+ };
433
+ const hasInteractionErrors = () => interactionOverlayFailed || interactionInputDiagnostics.value.some(({ level }) => level === "error") || annotationRuntimeDiagnostics.some(({ level }) => level === "error") || semanticRuntimeDiagnostics.some(({ level }) => level === "error");
434
+ const invalidateInteractionReadiness = () => {
435
+ interactionReadinessRunId += 1;
436
+ if (typeof cancelAnimationFrame === "function") {
437
+ for (const frame of interactionReadinessFrames) cancelAnimationFrame(frame);
438
+ }
439
+ interactionReadinessFrames = [];
440
+ };
441
+ const settleInteractionReadiness = () => {
442
+ invalidateInteractionReadiness();
443
+ if (hasInteractionErrors()) {
444
+ interactionCaptureState.value = "error";
445
+ return;
446
+ }
447
+ const runId = interactionReadinessRunId;
448
+ interactionCaptureState.value = "loading";
449
+ void nextTick().then(() => {
450
+ if (runId !== interactionReadinessRunId || hasInteractionErrors()) return;
451
+ const view = containerRef.value?.ownerDocument.defaultView;
452
+ if (!view) {
453
+ interactionCaptureState.value = "idle";
454
+ return;
455
+ }
456
+ const firstFrame = view.requestAnimationFrame(() => {
457
+ interactionReadinessFrames = interactionReadinessFrames.filter(
458
+ (frame) => frame !== firstFrame
459
+ );
460
+ if (runId !== interactionReadinessRunId || hasInteractionErrors()) return;
461
+ const secondFrame = view.requestAnimationFrame(() => {
462
+ interactionReadinessFrames = interactionReadinessFrames.filter(
463
+ (frame) => frame !== secondFrame
464
+ );
465
+ if (runId !== interactionReadinessRunId || hasInteractionErrors()) return;
466
+ interactionCaptureState.value = "idle";
467
+ });
468
+ interactionReadinessFrames.push(secondFrame);
469
+ });
470
+ interactionReadinessFrames.push(firstFrame);
166
471
  });
472
+ };
473
+ let renderTargetKeySignature = "";
474
+ const syncInteractionRenderTargetReadiness = () => {
475
+ const nextSignature = JSON.stringify([
476
+ ...annotationRenderTargets.value.map(({ key }) => `annotation:${key}`),
477
+ ...semanticRenderTargets.value.map(({ key }) => `semantic:${key}`)
478
+ ]);
479
+ if (nextSignature === renderTargetKeySignature) return;
480
+ renderTargetKeySignature = nextSignature;
481
+ settleInteractionReadiness();
482
+ };
483
+ const syncRuntimeDiagnostics = (source, diagnostics) => {
484
+ const previouslyActiveKeys = /* @__PURE__ */ new Set([
485
+ ...activeAnnotationDiagnosticKeys,
486
+ ...activeSemanticDiagnosticKeys
487
+ ]);
488
+ const nextKeys = new Set(diagnostics.map(interactionDiagnosticKey));
489
+ if (source === "annotation") {
490
+ activeAnnotationDiagnosticKeys = nextKeys;
491
+ annotationRuntimeDiagnostics = diagnostics;
492
+ } else {
493
+ activeSemanticDiagnosticKeys = nextKeys;
494
+ semanticRuntimeDiagnostics = diagnostics;
495
+ }
496
+ emitInteractionDiagnostics(
497
+ diagnostics.filter(
498
+ (diagnostic) => !previouslyActiveKeys.has(interactionDiagnosticKey(diagnostic))
499
+ )
500
+ );
501
+ settleInteractionReadiness();
502
+ };
503
+ const reportOverlayFailure = (message) => {
504
+ interactionOverlayFailed = true;
505
+ interactionCaptureState.value = "error";
506
+ emitInteractionDiagnostics([
507
+ createTileflowVueInteractionDiagnostic("OVERLAY_FAILURE", message)
508
+ ]);
509
+ };
510
+ watchEffect(() => {
511
+ assertTileflowMapStyleInputs({ source: props.source, theme: props.theme });
512
+ });
513
+ watchEffect(() => {
514
+ const diagnostics = interactionInputDiagnostics.value;
515
+ const signature = JSON.stringify(diagnostics);
516
+ if (signature !== interactionInputDiagnosticSignature) {
517
+ interactionInputDiagnosticSignature = signature;
518
+ emitInteractionDiagnostics(diagnostics);
519
+ }
520
+ settleInteractionReadiness();
521
+ });
522
+ const refreshManifest = async () => {
523
+ const shouldLoad = shouldLoadManifest.value;
524
+ const requestKey = manifestRequestKey.value;
167
525
  if (!shouldLoad) {
168
- manifestMap.value = null;
526
+ manifestLoadId += 1;
527
+ loadedManifestMap.value = null;
528
+ manifestResolutionKey.value = requestKey;
529
+ manifestResolutionState.value = "not-needed";
169
530
  return;
170
531
  }
171
532
  const loadId = ++manifestLoadId;
172
- manifestMap.value = null;
533
+ loadedManifestMap.value = null;
534
+ manifestResolutionKey.value = requestKey;
535
+ manifestResolutionState.value = "loading";
173
536
  try {
174
- const manifest = await loadTileflowManifest(props.manifestUrl);
537
+ const manifest = await loadTileflowManifest(manifestUrl.value);
175
538
  if (loadId === manifestLoadId) {
176
- manifestMap.value = manifest && props.map ? resolveTileflowManifestMap(manifest, props.map) : null;
539
+ const resolvedMap = manifest && mapName.value ? resolveTileflowManifestMap(manifest, mapName.value) : null;
540
+ loadedManifestMap.value = resolvedMap;
541
+ manifestResolutionState.value = resolvedMap ? "ready" : "error";
177
542
  }
178
543
  } catch (error) {
179
544
  if (loadId === manifestLoadId) {
180
545
  console.error("Failed to load Tileflow manifest", error);
181
- manifestMap.value = null;
546
+ loadedManifestMap.value = null;
547
+ manifestResolutionState.value = "error";
182
548
  }
183
549
  }
184
550
  };
@@ -202,70 +568,303 @@ var TileflowMap = defineComponent({
202
568
  updateImageSize();
203
569
  };
204
570
  const destroyMap = () => {
571
+ mapLoadId += 1;
205
572
  readinessRunId += 1;
573
+ invalidateInteractionReadiness();
206
574
  const lifecycle = mapLifecycle;
575
+ const fairUseNotice = mapFairUseNotice;
207
576
  const resizeObserver = mapResizeObserver;
208
577
  const map = mapRef.value;
578
+ const controller = themeController;
579
+ const annotationInteractionsForMap = annotationRuntime;
580
+ const semanticInteractionsForMap = semanticRuntime;
581
+ const worldRequestBridge = mapWorldRequestBridge;
582
+ const teardownSteps = [
583
+ annotationRuntimeCoordinatorDetach,
584
+ semanticRuntimeCoordinatorDetach,
585
+ annotationRuntimeTargetsUnsubscribe,
586
+ annotationRuntimeDiagnosticsUnsubscribe,
587
+ annotationRuntimeEventsUnsubscribe,
588
+ semanticRuntimeTargetsUnsubscribe,
589
+ semanticRuntimeDiagnosticsUnsubscribe,
590
+ semanticRuntimeEventsUnsubscribe,
591
+ () => annotationInteractionsForMap?.dispose(),
592
+ () => semanticInteractionsForMap?.dispose(),
593
+ () => worldRequestBridge?.dispose(),
594
+ () => fairUseNotice?.dispose(),
595
+ () => lifecycle?.dispose(),
596
+ () => controller?.dispose(),
597
+ () => resizeObserver?.disconnect(),
598
+ () => map?.remove()
599
+ ];
600
+ annotationRuntime = null;
601
+ semanticRuntime = null;
602
+ initializeSemanticRuntime = null;
603
+ annotationRuntimeCoordinatorDetach = null;
604
+ annotationRuntimeDiagnosticsUnsubscribe = null;
605
+ annotationRuntimeEventsUnsubscribe = null;
606
+ annotationRuntimeTargetsUnsubscribe = null;
607
+ semanticRuntimeDiagnosticsUnsubscribe = null;
608
+ semanticRuntimeCoordinatorDetach = null;
609
+ semanticRuntimeEventsUnsubscribe = null;
610
+ semanticRuntimeTargetsUnsubscribe = null;
611
+ annotationRenderTargets.value = [];
612
+ semanticRenderTargets.value = [];
613
+ activeAnnotationDiagnosticKeys = /* @__PURE__ */ new Set();
614
+ activeSemanticDiagnosticKeys = /* @__PURE__ */ new Set();
615
+ annotationRuntimeDiagnostics = [];
616
+ semanticRuntimeDiagnostics = [];
617
+ renderTargetKeySignature = "";
618
+ mapFairUseNotice = null;
209
619
  mapLifecycle = null;
210
620
  mapResizeObserver = null;
211
621
  mapRef.value = null;
212
- try {
213
- lifecycle?.dispose();
214
- } finally {
622
+ mapWorldRequestBridge = null;
623
+ themeController = null;
624
+ interactionRuntimesDisposing = true;
625
+ let teardownError;
626
+ for (const teardown of teardownSteps) {
215
627
  try {
216
- resizeObserver?.disconnect();
217
- } finally {
218
- try {
219
- markerController.clear();
220
- } finally {
221
- map?.remove();
222
- }
628
+ teardown?.();
629
+ } catch (error) {
630
+ teardownError ??= error;
223
631
  }
224
632
  }
633
+ interactionRuntimesDisposing = false;
634
+ interactionCaptureState.value = hasInteractionErrors() ? "error" : "idle";
635
+ if (teardownError) {
636
+ console.error("Failed to fully dispose the Tileflow map runtime", teardownError);
637
+ }
638
+ };
639
+ const syncAnnotationRuntime = () => {
640
+ const resolution = annotationResolution.value;
641
+ if (!annotationRuntime) return;
642
+ try {
643
+ annotationRuntime.reconcile(
644
+ resolution.ok && !isImageMode.value ? resolution.annotations : []
645
+ );
646
+ } catch {
647
+ reportOverlayFailure("Unable to reconcile the Tileflow annotation runtime.");
648
+ }
649
+ };
650
+ const syncSemanticRuntime = () => {
651
+ const resolution = interactionBindingResolution.value;
652
+ const bindings = resolution.ok && !isImageMode.value ? resolution.bindings : [];
653
+ if (bindings.length > 0 && !semanticRuntime) initializeSemanticRuntime?.();
654
+ if (!semanticRuntime) return;
655
+ try {
656
+ semanticRuntime.reconcile(bindings);
657
+ } catch {
658
+ reportOverlayFailure("Unable to reconcile the Tileflow semantic interaction runtime.");
659
+ }
660
+ };
661
+ const syncCustomRenderers = () => {
662
+ try {
663
+ annotationRuntime?.setCustomRenderers({
664
+ marker: slots.marker !== void 0,
665
+ popup: slots.popup !== void 0,
666
+ tooltip: slots.tooltip !== void 0
667
+ });
668
+ semanticRuntime?.setCustomRenderers({
669
+ popup: slots.popup !== void 0,
670
+ tooltip: slots.tooltip !== void 0
671
+ });
672
+ } catch {
673
+ reportOverlayFailure("Unable to update the Tileflow interaction renderers.");
674
+ }
225
675
  };
226
- const recreateMap = () => {
676
+ const recreateMap = async () => {
227
677
  destroyMap();
228
- captureState.value = "loading";
229
- if (!containerRef.value || !runtimeStyle.value || isImageMode.value) {
678
+ interactionOverlayFailed = false;
679
+ settleInteractionReadiness();
680
+ mapCaptureState.value = "loading";
681
+ themeTransitionState.value = "idle";
682
+ if (isImageMode.value) {
683
+ await resetImageReadiness();
230
684
  return;
231
685
  }
686
+ activeRuntimeResource.value = runtimeStyle.value;
687
+ if (!containerRef.value || !runtimeStyle.value) {
688
+ return;
689
+ }
690
+ const loadId = mapLoadId;
691
+ const container = containerRef.value;
232
692
  const runtime = runtimeStyle.value;
233
- const analyticsForMap = resolvedAnalytics.value;
693
+ let maplibregl;
694
+ try {
695
+ [maplibregl] = await Promise.all([
696
+ loadTileflowMapLibre(),
697
+ loadTileflowStyleFonts(runtime.style, {
698
+ fontFaces: runtime.fontFaces
699
+ })
700
+ ]);
701
+ } catch (error) {
702
+ if (loadId === mapLoadId) {
703
+ console.error("Failed to load the Tileflow map runtime", error);
704
+ mapCaptureState.value = "error";
705
+ }
706
+ return;
707
+ }
708
+ if (loadId !== mapLoadId || containerRef.value !== container || isImageMode.value) {
709
+ return;
710
+ }
234
711
  const session = createTileflowSessionController({ source: "vue" });
235
712
  const sessionStarter = createTileflowSessionStarter({
236
713
  getSessionId: () => session.sessionId,
237
714
  sessionId: session.sessionId,
238
715
  source: "vue"
239
716
  });
717
+ mapFairUseNotice = attachTileflowFairUseNotice(container);
718
+ registerTileflowContourProtocol({ addProtocol: maplibregl.addProtocol });
719
+ registerTileflowPmtilesProtocol({ addProtocol: maplibregl.addProtocol });
720
+ mapWorldRequestBridge = registerTileflowWorldRequestBridge({
721
+ addProtocol: maplibregl.addProtocol,
722
+ onNotice: mapFairUseNotice.update
723
+ });
240
724
  const map = new maplibregl.Map({
241
725
  ...props.mapOptions,
242
- attributionControl: props.mapOptions?.attributionControl ?? { compact: true },
726
+ attributionControl: props.mapOptions?.attributionControl ?? {
727
+ compact: true
728
+ },
729
+ bearing: resolvedBearing.value,
243
730
  center: resolvedCenter.value,
244
- container: containerRef.value,
731
+ container,
245
732
  interactive: resolvedInteractive.value,
733
+ pitch: resolvedPitch.value,
246
734
  style: runtime.style,
247
735
  transformRequest: createTileflowTransformRequest({
248
- getAnalytics: () => analyticsForMap,
736
+ getAnalytics: () => resolvedAnalytics.value,
249
737
  sessionController: session,
250
738
  sessionId: session.sessionId,
251
- transformRequest: props.mapOptions?.transformRequest ?? void 0
739
+ transformRequest: props.mapOptions?.transformRequest ?? void 0,
740
+ worldRequestBridge: mapWorldRequestBridge
252
741
  }),
253
742
  zoom: resolvedZoom.value
254
743
  });
255
744
  mapRef.value = map;
745
+ themeController = runtime.theme === void 0 ? null : createTileflowThemeController({
746
+ initial: runtime,
747
+ map,
748
+ onTransition(transition) {
749
+ emit("themeChange", transition);
750
+ if (transition.phase === "preloading" || transition.phase === "applying") {
751
+ themeTransitionState.value = "loading";
752
+ } else if (transition.phase === "error") {
753
+ themeTransitionState.value = "error";
754
+ } else {
755
+ themeTransitionState.value = "idle";
756
+ }
757
+ }
758
+ });
759
+ try {
760
+ const mapInteractions = createTileflowMapLibreDomRuntime({
761
+ createMarker({ annotation, element }) {
762
+ element.title = annotation.ariaLabel;
763
+ return new maplibregl.Marker({ element });
764
+ },
765
+ customMarker: slots.marker !== void 0,
766
+ customPopup: slots.popup !== void 0,
767
+ customTooltip: slots.tooltip !== void 0,
768
+ createOverlay({ container: overlayContainer, kind }) {
769
+ return new maplibregl.Popup({
770
+ closeButton: false,
771
+ closeOnClick: false,
772
+ closeOnMove: false,
773
+ focusAfterOpen: false,
774
+ offset: kind === "popup" ? 24 : 18
775
+ }).setDOMContent(overlayContainer);
776
+ },
777
+ document: container.ownerDocument,
778
+ interactionState: interactionCoordinator.getInteractionState(),
779
+ map,
780
+ onInteractionStateChange: interactionCoordinator.requestInteractionState,
781
+ updateMarker(_marker, { annotation, element }) {
782
+ element.title = annotation.ariaLabel;
783
+ }
784
+ });
785
+ annotationRuntime = mapInteractions;
786
+ annotationRuntimeCoordinatorDetach = interactionCoordinator.attach(
787
+ "annotation",
788
+ mapInteractions
789
+ );
790
+ annotationRuntimeDiagnosticsUnsubscribe = mapInteractions.subscribeDiagnostics(
791
+ (diagnostics) => syncRuntimeDiagnostics("annotation", diagnostics)
792
+ );
793
+ annotationRuntimeEventsUnsubscribe = mapInteractions.subscribeEvents((event) => {
794
+ if (!interactionRuntimesDisposing) emit("interactionEvent", event);
795
+ });
796
+ syncRuntimeDiagnostics("annotation", mapInteractions.getDiagnostics());
797
+ annotationRuntimeTargetsUnsubscribe = mapInteractions.subscribeRenderTargets((targets) => {
798
+ annotationRenderTargets.value = targets;
799
+ syncInteractionRenderTargetReadiness();
800
+ });
801
+ annotationRenderTargets.value = mapInteractions.getRenderTargets();
802
+ syncInteractionRenderTargetReadiness();
803
+ syncAnnotationRuntime();
804
+ } catch {
805
+ reportOverlayFailure("Unable to initialize the Tileflow annotation runtime.");
806
+ }
807
+ initializeSemanticRuntime = () => {
808
+ if (semanticRuntime || interactionRuntimesDisposing || mapRef.value !== map) return;
809
+ try {
810
+ const semanticInteractions = createTileflowMapLibreSemanticDomRuntime({
811
+ cancelFrame: (frame) => cancelAnimationFrame(frame),
812
+ createOverlay({ container: overlayContainer, kind }) {
813
+ return new maplibregl.Popup({
814
+ closeButton: false,
815
+ closeOnClick: false,
816
+ closeOnMove: false,
817
+ focusAfterOpen: false,
818
+ offset: kind === "popup" ? 24 : 18
819
+ }).setDOMContent(overlayContainer);
820
+ },
821
+ customPopup: slots.popup !== void 0,
822
+ customTooltip: slots.tooltip !== void 0,
823
+ document: container.ownerDocument,
824
+ interactionState: interactionCoordinator.getInteractionState(),
825
+ map,
826
+ onInteractionStateChange: interactionCoordinator.requestInteractionState,
827
+ poiMap: createTileflowVuePoiMap(map),
828
+ requestFrame: (callback) => requestAnimationFrame(callback)
829
+ });
830
+ semanticRuntime = semanticInteractions;
831
+ semanticRuntimeCoordinatorDetach = interactionCoordinator.attach(
832
+ "semantic",
833
+ semanticInteractions
834
+ );
835
+ semanticRuntimeDiagnosticsUnsubscribe = semanticInteractions.subscribeDiagnostics(
836
+ (diagnostics) => syncRuntimeDiagnostics("semantic", diagnostics)
837
+ );
838
+ semanticRuntimeEventsUnsubscribe = semanticInteractions.subscribeEvents((event) => {
839
+ if (!interactionRuntimesDisposing) emit("interactionEvent", event);
840
+ });
841
+ syncRuntimeDiagnostics("semantic", semanticInteractions.getDiagnostics());
842
+ semanticRuntimeTargetsUnsubscribe = semanticInteractions.subscribeRenderTargets(
843
+ (targets) => {
844
+ semanticRenderTargets.value = targets;
845
+ syncInteractionRenderTargetReadiness();
846
+ }
847
+ );
848
+ semanticRenderTargets.value = semanticInteractions.getRenderTargets();
849
+ syncInteractionRenderTargetReadiness();
850
+ } catch {
851
+ reportOverlayFailure("Unable to initialize the Tileflow semantic interaction runtime.");
852
+ }
853
+ };
854
+ syncSemanticRuntime();
256
855
  if (resolvedInteractive.value) {
257
856
  map.addControl(new maplibregl.NavigationControl(), "top-right");
258
857
  }
259
858
  mapResizeObserver = new ResizeObserver(() => {
260
859
  map.resize();
261
860
  });
262
- mapResizeObserver.observe(containerRef.value);
861
+ mapResizeObserver.observe(container);
263
862
  mapLifecycle = attachTileflowMapLifecycle({
264
863
  getSession: () => {
265
864
  const analyticsForLoad = resolvedAnalytics.value;
266
865
  return {
267
866
  analytics: analyticsForLoad,
268
- styleId: analyticsForLoad?.styleId ?? (typeof runtime.style === "string" ? runtime.style : props.map)
867
+ styleId: analyticsForLoad?.styleId ?? (typeof runtime.style === "string" ? runtime.style : mapName.value)
269
868
  };
270
869
  },
271
870
  map,
@@ -276,120 +875,174 @@ var TileflowMap = defineComponent({
276
875
  },
277
876
  sessionStarter,
278
877
  setState: (state) => {
279
- captureState.value = state;
878
+ mapCaptureState.value = state;
280
879
  },
281
880
  subscribe: (subscribedMap, event, listener) => {
282
881
  const subscription = subscribedMap.on(event, listener);
283
882
  return () => subscription.unsubscribe();
284
883
  }
285
884
  });
286
- syncMarkers();
885
+ if (themeController && runtimeStyle.value && runtimeStyle.value !== runtime) {
886
+ activeRuntimeResource.value = runtimeStyle.value;
887
+ void themeController.setTheme(runtimeStyle.value);
888
+ }
889
+ };
890
+ const switchTheme = () => {
891
+ if (!themeController) return;
892
+ if (themeResolution.value.error) {
893
+ void themeController.setTheme(themeController.getCurrent());
894
+ return;
895
+ }
896
+ const next = runtimeStyle.value;
897
+ if (!next) return;
898
+ activeRuntimeResource.value = next;
899
+ void themeController.setTheme(next).then((result) => {
900
+ if (result.status === "failed") {
901
+ console.error("Failed to change the Tileflow map theme", result.error);
902
+ }
903
+ });
904
+ };
905
+ const syncSystemColorSchemeSubscription = () => {
906
+ unsubscribeSystemColorScheme?.();
907
+ unsubscribeSystemColorScheme = null;
908
+ if (props.theme !== "system") return;
909
+ systemColorScheme.value = getTileflowSystemColorScheme();
910
+ unsubscribeSystemColorScheme = subscribeTileflowSystemColorScheme((scheme) => {
911
+ systemColorScheme.value = scheme;
912
+ });
287
913
  };
288
914
  const syncView = () => {
289
915
  mapRef.value?.jumpTo({
916
+ bearing: resolvedBearing.value,
290
917
  center: resolvedCenter.value,
918
+ pitch: resolvedPitch.value,
291
919
  zoom: resolvedZoom.value
292
920
  });
293
921
  };
294
- const syncMarkers = () => {
295
- const map = mapRef.value;
296
- if (!map) {
297
- markerController.clear();
298
- return;
299
- }
300
- markerController.replace(map, props.markers);
301
- };
302
922
  const markImageReady = async (image) => {
303
923
  const runId = ++readinessRunId;
304
924
  try {
305
925
  if (typeof image.decode === "function") await image.decode();
306
926
  } catch {
307
927
  if (!image.complete || image.naturalWidth === 0) {
308
- if (runId === readinessRunId) captureState.value = "error";
928
+ if (runId === readinessRunId) mapCaptureState.value = "error";
309
929
  return;
310
930
  }
311
931
  }
312
- if (runId === readinessRunId) captureState.value = "idle";
932
+ if (runId === readinessRunId) {
933
+ mapCaptureState.value = "idle";
934
+ }
313
935
  };
314
936
  const resetImageReadiness = async () => {
315
937
  if (!isImageMode.value) return;
316
938
  const runId = ++readinessRunId;
317
- captureState.value = "loading";
939
+ activeRuntimeResource.value = runtimeImageUrl.value;
940
+ if (hasInteractionConfiguration.value) {
941
+ mapCaptureState.value = "idle";
942
+ return;
943
+ }
944
+ mapCaptureState.value = "loading";
318
945
  await nextTick();
319
946
  if (runId !== readinessRunId) return;
320
947
  const image = containerRef.value?.querySelector("img");
321
948
  if (!image?.complete) return;
322
949
  if (image.naturalWidth === 0) {
323
- captureState.value = "error";
950
+ mapCaptureState.value = "error";
324
951
  return;
325
952
  }
326
953
  await markImageReady(image);
327
954
  };
328
955
  onMounted(() => {
956
+ syncSystemColorSchemeSubscription();
329
957
  void refreshManifest();
330
958
  updateImageResizeObserver();
331
- recreateMap();
959
+ void recreateMap();
332
960
  void resetImageReadiness();
333
961
  });
334
962
  onBeforeUnmount(() => {
963
+ unsubscribeSystemColorScheme?.();
335
964
  imageResizeObserver?.disconnect();
336
965
  destroyMap();
966
+ interactionCoordinator.dispose();
967
+ });
968
+ onUpdated(() => {
969
+ syncCustomRenderers();
337
970
  });
338
971
  watch(
339
- () => [
340
- props.config,
341
- props.imageUrl,
342
- props.manifestUrl,
343
- props.map,
344
- props.mapStyle,
345
- props.mode,
346
- props.preferLocalDev,
347
- props.styleBaseUrl,
348
- props.styleUrl
349
- ],
972
+ () => [props.imageUrl, props.mode, props.source],
350
973
  () => {
351
974
  void refreshManifest();
352
975
  }
353
976
  );
354
977
  watch(
355
- () => [isImageMode.value, runtimeImageUrl.value],
978
+ () => props.theme,
979
+ () => syncSystemColorSchemeSubscription()
980
+ );
981
+ watch(
982
+ () => runtimeStyle.value,
983
+ () => switchTheme(),
984
+ { flush: "post" }
985
+ );
986
+ watch(
987
+ () => [hasInteractionConfiguration.value, isImageMode.value, runtimeImageUrl.value],
356
988
  () => {
357
989
  void resetImageReadiness();
358
990
  },
359
991
  { flush: "post" }
360
992
  );
361
993
  watch(
362
- () => [props.imageUrl, props.mode, props.preferLocalDev],
994
+ () => [props.imageUrl, props.mode],
363
995
  () => updateImageResizeObserver(),
364
996
  { flush: "post" }
365
997
  );
366
998
  watch(
367
999
  () => [
368
- props.config,
369
1000
  props.analytics,
370
1001
  props.interactive,
371
- props.map,
372
1002
  props.mapOptions,
373
- props.mapStyle,
374
1003
  props.mode,
375
- props.preferLocalDev,
376
- props.styleBaseUrl,
377
- props.styleUrl,
378
- props.themes,
1004
+ props.source,
379
1005
  manifestMap.value
380
1006
  ],
381
- () => recreateMap(),
1007
+ () => void recreateMap(),
382
1008
  { flush: "post" }
383
1009
  );
384
1010
  watch(
385
- () => [props.center, props.zoom, props.mapOptions?.center, props.mapOptions?.zoom],
1011
+ () => [
1012
+ props.center,
1013
+ props.zoom,
1014
+ props.mapOptions?.bearing,
1015
+ props.mapOptions?.center,
1016
+ props.mapOptions?.pitch,
1017
+ props.mapOptions?.zoom,
1018
+ manifestMap.value?.view
1019
+ ],
386
1020
  () => syncView(),
387
1021
  { flush: "post" }
388
1022
  );
389
1023
  watch(
390
- () => props.markers,
391
- () => syncMarkers(),
392
- { flush: "post" }
1024
+ () => props.annotations,
1025
+ () => syncAnnotationRuntime(),
1026
+ { deep: true, flush: "post" }
1027
+ );
1028
+ watch(
1029
+ () => props.interactions,
1030
+ () => syncSemanticRuntime(),
1031
+ { deep: true, flush: "post" }
1032
+ );
1033
+ watch(
1034
+ () => props.interactionState,
1035
+ (nextState) => {
1036
+ if (!controlledInteractionOwnership || nextState === void 0 || interactionStateDiagnostics.value.length > 0) {
1037
+ return;
1038
+ }
1039
+ try {
1040
+ interactionCoordinator.setInteractionState(nextState);
1041
+ } catch {
1042
+ reportOverlayFailure("Unable to apply Tileflow interaction state.");
1043
+ }
1044
+ },
1045
+ { deep: true, flush: "post" }
393
1046
  );
394
1047
  return () => {
395
1048
  const rootAttrs = { ...attrs };
@@ -397,34 +1050,66 @@ var TileflowMap = defineComponent({
397
1050
  const styleValue = [attrs.style, frameStyle.value];
398
1051
  delete rootAttrs.class;
399
1052
  delete rootAttrs.style;
1053
+ const image = isImageMode.value && runtimeImageUrl.value ? h("img", {
1054
+ alt: props.alt,
1055
+ decoding: "async",
1056
+ loading: props.imageLoading,
1057
+ onError: () => {
1058
+ readinessRunId += 1;
1059
+ mapCaptureState.value = "error";
1060
+ },
1061
+ onLoad: (event) => void markImageReady(event.currentTarget),
1062
+ src: runtimeImageUrl.value,
1063
+ style: {
1064
+ display: "block",
1065
+ height: "100%",
1066
+ objectFit: "cover",
1067
+ width: "100%"
1068
+ }
1069
+ }) : void 0;
1070
+ const annotationTeleports = annotationRenderTargets.value.flatMap((target) => {
1071
+ const slot = target.kind === "marker" ? slots.marker : target.kind === "tooltip" ? slots.tooltip : slots.popup;
1072
+ if (!slot) return [];
1073
+ const content = target.annotation[target.kind]?.content;
1074
+ const context = {
1075
+ annotation: target.annotation,
1076
+ close: target.close,
1077
+ content,
1078
+ target: {
1079
+ annotation: target.annotation,
1080
+ coordinate: target.annotation.coordinate,
1081
+ kind: "annotation"
1082
+ },
1083
+ viewName: content?.kind === "view" ? content.name : void 0
1084
+ };
1085
+ return [
1086
+ h(Teleport, { key: `annotation:${target.key}`, to: target.container }, slot(context))
1087
+ ];
1088
+ });
1089
+ const semanticTeleports = semanticRenderTargets.value.flatMap((target) => {
1090
+ const slot = target.kind === "tooltip" ? slots.tooltip : slots.popup;
1091
+ if (!slot) return [];
1092
+ const context = {
1093
+ close: target.close,
1094
+ content: target.content,
1095
+ target: target.target,
1096
+ viewName: target.viewName
1097
+ };
1098
+ return [h(Teleport, { key: `semantic:${target.key}`, to: target.container }, slot(context))];
1099
+ });
400
1100
  return h(
401
1101
  "div",
402
1102
  {
403
1103
  ...rootAttrs,
404
1104
  class: classValue,
405
1105
  "data-tileflow-capture-id": resolvedCaptureId.value,
406
- "data-tileflow-map": props.map,
1106
+ "data-tileflow-map": mapName.value,
1107
+ "data-tileflow-theme": resolvedThemeName.value,
407
1108
  "data-tileflow-state": captureState.value,
408
1109
  ref: containerRef,
409
1110
  style: styleValue
410
1111
  },
411
- isImageMode.value && runtimeImageUrl.value ? h("img", {
412
- alt: props.alt,
413
- decoding: "async",
414
- loading: props.imageLoading,
415
- onError: () => {
416
- readinessRunId += 1;
417
- captureState.value = "error";
418
- },
419
- onLoad: (event) => void markImageReady(event.currentTarget),
420
- src: runtimeImageUrl.value,
421
- style: {
422
- display: "block",
423
- height: "100%",
424
- objectFit: "cover",
425
- width: "100%"
426
- }
427
- }) : void 0
1112
+ [image, ...annotationTeleports, ...semanticTeleports]
428
1113
  );
429
1114
  };
430
1115
  }
@@ -433,6 +1118,26 @@ var index_default = TileflowMap;
433
1118
  function formatHeight(height) {
434
1119
  return typeof height === "number" ? `${height}px` : height;
435
1120
  }
1121
+ function interactionDiagnosticKey(diagnostic) {
1122
+ return JSON.stringify([diagnostic.code, diagnostic.message, diagnostic.path, diagnostic.target]);
1123
+ }
1124
+ function createTileflowVuePoiMap(map) {
1125
+ return {
1126
+ getStyle: () => map.getStyle(),
1127
+ on: (event, listener) => map.on(event, listener),
1128
+ queryRenderedFeatures(point, options) {
1129
+ return map.queryRenderedFeatures(point, {
1130
+ layers: [...options.layers]
1131
+ }).map((feature) => ({
1132
+ ...feature.id === void 0 ? {} : { id: feature.id },
1133
+ layer: { id: feature.layer.id },
1134
+ ...feature.properties === null ? {} : { properties: feature.properties },
1135
+ ...feature.source === void 0 ? {} : { source: feature.source },
1136
+ ...feature.sourceLayer === void 0 ? {} : { sourceLayer: feature.sourceLayer }
1137
+ }));
1138
+ }
1139
+ };
1140
+ }
436
1141
  export {
437
1142
  TileflowMap,
438
1143
  index_default as default