@tileflow/vue 0.1.0-alpha.18 → 0.1.0-alpha.20

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