@tileflow/vue 0.1.0-alpha.19 → 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,127 +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,
225
+ theme: String,
83
226
  zoom: Number
84
227
  },
85
228
  emits: {
86
- load: (_map) => true
229
+ interactionDiagnostic: (_diagnostic) => true,
230
+ interactionEvent: (_event) => true,
231
+ load: (_map) => true,
232
+ themeChange: (_transition) => true,
233
+ "update:interactionState": (_state) => true
87
234
  },
88
- setup(props, { attrs, emit }) {
235
+ setup(props, { attrs, emit, slots }) {
89
236
  const containerRef = ref(null);
90
- const manifestMap = shallowRef(null);
237
+ const loadedManifestMap = shallowRef(null);
238
+ const manifestResolutionKey = ref("");
239
+ const manifestResolutionState = ref("loading");
91
240
  const imageSize = shallowRef(null);
92
241
  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
- });
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();
104
269
  let imageResizeObserver = null;
270
+ let mapFairUseNotice = null;
105
271
  let mapLifecycle = null;
106
272
  let mapResizeObserver = null;
273
+ let mapWorldRequestBridge = null;
274
+ let themeController = null;
275
+ let unsubscribeSystemColorScheme = null;
276
+ let mapLoadId = 0;
107
277
  let manifestLoadId = 0;
108
278
  let readinessRunId = 0;
109
- const resolvedMode = computed(
110
- () => resolveTileflowMapMode({
111
- imageUrl: props.imageUrl,
112
- mode: props.mode,
113
- 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
114
289
  })
115
290
  );
116
- const isImageMode = computed(() => resolvedMode.value === "image");
117
- const imageCenter = computed(() => props.center ?? defaultCenter);
118
- 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
+ );
119
390
  const resolvedCenter = computed(
120
- () => props.center ?? props.mapOptions?.center ?? defaultCenter
391
+ () => props.center ?? props.mapOptions?.center ?? manifestCenter.value
121
392
  );
122
- 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);
123
398
  const resolvedInteractive = computed(
124
399
  () => props.interactive ?? props.mapOptions?.interactive ?? true
125
400
  );
126
401
  const runtimeStyle = computed(
127
- () => isImageMode.value ? null : resolveTileflowRuntimeStyle({
128
- config: props.config,
402
+ () => isImageMode.value || themeResolution.value.error ? null : resolveTileflowRuntimeStyle({
403
+ colorScheme: systemColorScheme.value,
129
404
  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
405
+ source: props.source,
406
+ theme: themeResolution.value.name
136
407
  })
137
408
  );
138
409
  const resolvedAnalytics = computed(
139
410
  () => mergeTileflowAnalytics(props.analytics, runtimeStyle.value?.analytics)
140
411
  );
141
412
  const runtimeImageUrl = computed(
142
- () => props.imageUrl ?? (isImageMode.value ? resolveTileflowStaticImageUrl({
413
+ () => props.imageUrl ?? (isImageMode.value && !themeResolution.value.error ? resolveTileflowStaticImageUrl({
143
414
  center: imageCenter.value,
415
+ colorScheme: systemColorScheme.value,
144
416
  imageSize: imageSize.value,
145
417
  manifestMap: manifestMap.value,
418
+ theme: themeResolution.value.name,
146
419
  zoom: imageZoom.value
147
420
  }) : void 0)
148
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
+ });
149
445
  const frameStyle = computed(() => ({
150
- height: formatHeight(props.height),
446
+ height: formatHeight(props.height ?? 420),
151
447
  minHeight: "240px",
152
448
  overflow: "hidden",
153
449
  position: "relative",
154
450
  width: "100%"
155
451
  }));
156
452
  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
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);
166
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;
167
548
  if (!shouldLoad) {
168
- manifestMap.value = null;
549
+ manifestLoadId += 1;
550
+ loadedManifestMap.value = null;
551
+ manifestResolutionKey.value = requestKey;
552
+ manifestResolutionState.value = "not-needed";
169
553
  return;
170
554
  }
171
555
  const loadId = ++manifestLoadId;
172
- manifestMap.value = null;
556
+ loadedManifestMap.value = null;
557
+ manifestResolutionKey.value = requestKey;
558
+ manifestResolutionState.value = "loading";
173
559
  try {
174
- const manifest = await loadTileflowManifest(props.manifestUrl);
560
+ const manifest = await loadTileflowManifest(manifestUrl.value);
175
561
  if (loadId === manifestLoadId) {
176
- 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";
177
565
  }
178
566
  } catch (error) {
179
567
  if (loadId === manifestLoadId) {
180
568
  console.error("Failed to load Tileflow manifest", error);
181
- manifestMap.value = null;
569
+ loadedManifestMap.value = null;
570
+ manifestResolutionState.value = "error";
182
571
  }
183
572
  }
184
573
  };
@@ -202,70 +591,303 @@ var TileflowMap = defineComponent({
202
591
  updateImageSize();
203
592
  };
204
593
  const destroyMap = () => {
594
+ mapLoadId += 1;
205
595
  readinessRunId += 1;
596
+ invalidateInteractionReadiness();
206
597
  const lifecycle = mapLifecycle;
598
+ const fairUseNotice = mapFairUseNotice;
207
599
  const resizeObserver = mapResizeObserver;
208
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;
209
642
  mapLifecycle = null;
210
643
  mapResizeObserver = null;
211
644
  mapRef.value = null;
212
- try {
213
- lifecycle?.dispose();
214
- } finally {
645
+ mapWorldRequestBridge = null;
646
+ themeController = null;
647
+ interactionRuntimesDisposing = true;
648
+ let teardownError;
649
+ for (const teardown of teardownSteps) {
215
650
  try {
216
- resizeObserver?.disconnect();
217
- } finally {
218
- try {
219
- markerController.clear();
220
- } finally {
221
- map?.remove();
222
- }
651
+ teardown?.();
652
+ } catch (error) {
653
+ teardownError ??= error;
223
654
  }
224
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
+ }
225
684
  };
226
- 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 () => {
227
701
  destroyMap();
228
- captureState.value = "loading";
229
- 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();
230
708
  return;
231
709
  }
710
+ activeRuntimeResource.value = runtimeStyle.value;
711
+ if (!containerRef.value || !runtimeStyle.value) {
712
+ return;
713
+ }
714
+ const loadId = mapLoadId;
715
+ const container = containerRef.value;
232
716
  const runtime = runtimeStyle.value;
233
- 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
+ }
234
735
  const session = createTileflowSessionController({ source: "vue" });
235
736
  const sessionStarter = createTileflowSessionStarter({
236
737
  getSessionId: () => session.sessionId,
237
738
  sessionId: session.sessionId,
238
739
  source: "vue"
239
740
  });
741
+ mapFairUseNotice = attachTileflowFairUseNotice(container);
742
+ registerTileflowContourProtocol({ addProtocol: maplibregl.addProtocol });
743
+ mapWorldRequestBridge = registerTileflowWorldRequestBridge({
744
+ addProtocol: maplibregl.addProtocol,
745
+ onNotice: mapFairUseNotice.update
746
+ });
240
747
  const map = new maplibregl.Map({
241
748
  ...props.mapOptions,
242
- attributionControl: props.mapOptions?.attributionControl ?? { compact: true },
749
+ attributionControl: props.mapOptions?.attributionControl ?? {
750
+ compact: true
751
+ },
752
+ bearing: resolvedBearing.value,
243
753
  center: resolvedCenter.value,
244
- container: containerRef.value,
754
+ container,
245
755
  interactive: resolvedInteractive.value,
756
+ pitch: resolvedPitch.value,
246
757
  style: runtime.style,
247
758
  transformRequest: createTileflowTransformRequest({
248
- getAnalytics: () => analyticsForMap,
759
+ getAnalytics: () => resolvedAnalytics.value,
249
760
  sessionController: session,
250
761
  sessionId: session.sessionId,
251
- transformRequest: props.mapOptions?.transformRequest ?? void 0
762
+ transformRequest: props.mapOptions?.transformRequest ?? void 0,
763
+ worldRequestBridge: mapWorldRequestBridge
252
764
  }),
253
765
  zoom: resolvedZoom.value
254
766
  });
255
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();
256
878
  if (resolvedInteractive.value) {
257
879
  map.addControl(new maplibregl.NavigationControl(), "top-right");
258
880
  }
259
881
  mapResizeObserver = new ResizeObserver(() => {
260
882
  map.resize();
261
883
  });
262
- mapResizeObserver.observe(containerRef.value);
884
+ mapResizeObserver.observe(container);
263
885
  mapLifecycle = attachTileflowMapLifecycle({
264
886
  getSession: () => {
265
887
  const analyticsForLoad = resolvedAnalytics.value;
266
888
  return {
267
889
  analytics: analyticsForLoad,
268
- styleId: analyticsForLoad?.styleId ?? (typeof runtime.style === "string" ? runtime.style : props.map)
890
+ styleId: analyticsForLoad?.styleId ?? (typeof runtime.style === "string" ? runtime.style : mapName.value)
269
891
  };
270
892
  },
271
893
  map,
@@ -276,120 +898,174 @@ var TileflowMap = defineComponent({
276
898
  },
277
899
  sessionStarter,
278
900
  setState: (state) => {
279
- captureState.value = state;
901
+ mapCaptureState.value = state;
280
902
  },
281
903
  subscribe: (subscribedMap, event, listener) => {
282
904
  const subscription = subscribedMap.on(event, listener);
283
905
  return () => subscription.unsubscribe();
284
906
  }
285
907
  });
286
- 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
+ });
287
936
  };
288
937
  const syncView = () => {
289
938
  mapRef.value?.jumpTo({
939
+ bearing: resolvedBearing.value,
290
940
  center: resolvedCenter.value,
941
+ pitch: resolvedPitch.value,
291
942
  zoom: resolvedZoom.value
292
943
  });
293
944
  };
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
945
  const markImageReady = async (image) => {
303
946
  const runId = ++readinessRunId;
304
947
  try {
305
948
  if (typeof image.decode === "function") await image.decode();
306
949
  } catch {
307
950
  if (!image.complete || image.naturalWidth === 0) {
308
- if (runId === readinessRunId) captureState.value = "error";
951
+ if (runId === readinessRunId) mapCaptureState.value = "error";
309
952
  return;
310
953
  }
311
954
  }
312
- if (runId === readinessRunId) captureState.value = "idle";
955
+ if (runId === readinessRunId) {
956
+ mapCaptureState.value = "idle";
957
+ }
313
958
  };
314
959
  const resetImageReadiness = async () => {
315
960
  if (!isImageMode.value) return;
316
961
  const runId = ++readinessRunId;
317
- captureState.value = "loading";
962
+ activeRuntimeResource.value = runtimeImageUrl.value;
963
+ if (hasInteractionConfiguration.value) {
964
+ mapCaptureState.value = "idle";
965
+ return;
966
+ }
967
+ mapCaptureState.value = "loading";
318
968
  await nextTick();
319
969
  if (runId !== readinessRunId) return;
320
970
  const image = containerRef.value?.querySelector("img");
321
971
  if (!image?.complete) return;
322
972
  if (image.naturalWidth === 0) {
323
- captureState.value = "error";
973
+ mapCaptureState.value = "error";
324
974
  return;
325
975
  }
326
976
  await markImageReady(image);
327
977
  };
328
978
  onMounted(() => {
979
+ syncSystemColorSchemeSubscription();
329
980
  void refreshManifest();
330
981
  updateImageResizeObserver();
331
- recreateMap();
982
+ void recreateMap();
332
983
  void resetImageReadiness();
333
984
  });
334
985
  onBeforeUnmount(() => {
986
+ unsubscribeSystemColorScheme?.();
335
987
  imageResizeObserver?.disconnect();
336
988
  destroyMap();
989
+ interactionCoordinator.dispose();
990
+ });
991
+ onUpdated(() => {
992
+ syncCustomRenderers();
337
993
  });
338
994
  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
- ],
995
+ () => [props.imageUrl, props.mode, props.source],
350
996
  () => {
351
997
  void refreshManifest();
352
998
  }
353
999
  );
354
1000
  watch(
355
- () => [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],
356
1011
  () => {
357
1012
  void resetImageReadiness();
358
1013
  },
359
1014
  { flush: "post" }
360
1015
  );
361
1016
  watch(
362
- () => [props.imageUrl, props.mode, props.preferLocalDev],
1017
+ () => [props.imageUrl, props.mode],
363
1018
  () => updateImageResizeObserver(),
364
1019
  { flush: "post" }
365
1020
  );
366
1021
  watch(
367
1022
  () => [
368
- props.config,
369
1023
  props.analytics,
370
1024
  props.interactive,
371
- props.map,
372
1025
  props.mapOptions,
373
- props.mapStyle,
374
1026
  props.mode,
375
- props.preferLocalDev,
376
- props.styleBaseUrl,
377
- props.styleUrl,
378
- props.themes,
1027
+ props.source,
379
1028
  manifestMap.value
380
1029
  ],
381
- () => recreateMap(),
1030
+ () => void recreateMap(),
382
1031
  { flush: "post" }
383
1032
  );
384
1033
  watch(
385
- () => [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
+ ],
386
1043
  () => syncView(),
387
1044
  { flush: "post" }
388
1045
  );
389
1046
  watch(
390
- () => props.markers,
391
- () => syncMarkers(),
392
- { 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" }
393
1069
  );
394
1070
  return () => {
395
1071
  const rootAttrs = { ...attrs };
@@ -397,34 +1073,66 @@ var TileflowMap = defineComponent({
397
1073
  const styleValue = [attrs.style, frameStyle.value];
398
1074
  delete rootAttrs.class;
399
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
+ });
400
1123
  return h(
401
1124
  "div",
402
1125
  {
403
1126
  ...rootAttrs,
404
1127
  class: classValue,
405
1128
  "data-tileflow-capture-id": resolvedCaptureId.value,
406
- "data-tileflow-map": props.map,
1129
+ "data-tileflow-map": mapName.value,
1130
+ "data-tileflow-theme": resolvedThemeName.value,
407
1131
  "data-tileflow-state": captureState.value,
408
1132
  ref: containerRef,
409
1133
  style: styleValue
410
1134
  },
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
1135
+ [image, ...annotationTeleports, ...semanticTeleports]
428
1136
  );
429
1137
  };
430
1138
  }
@@ -433,6 +1141,26 @@ var index_default = TileflowMap;
433
1141
  function formatHeight(height) {
434
1142
  return typeof height === "number" ? `${height}px` : height;
435
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
+ }
436
1164
  export {
437
1165
  TileflowMap,
438
1166
  index_default as default