@tileflow/svelte 0.1.0-alpha.27 → 0.1.0-alpha.29

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/README.md CHANGED
@@ -90,12 +90,25 @@ Omitting `theme` uses `defaultTheme`. `system` needs an explicit light/dark mapp
90
90
  supplies. Theme changes keep the MapLibre instance, camera, and interactions, and roll back on
91
91
  failure. `onThemeChange` reports transitions.
92
92
 
93
+ Add `timeOfDay="dusk"` beside `theme` to select a map's optional `day`, `dawn`, `dusk`, or
94
+ `night` preset. For Streets, `theme="light"` with this preset selects the existing `dusk` palette.
95
+ Maps without this capability ignore the option; omitting it keeps the selected theme unchanged.
96
+ Changing it uses the existing theme transition. `themeBlend` takes precedence. See the
97
+ [authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/README.md#style-with-themes-and-semantic-modules).
98
+
93
99
  `themeTransition={{duration: 450}}` cross-fades theme changes over that many milliseconds.
94
100
  `themeBlend={{themes: ['night', 'dusk', 'day'], position}}` shows a continuous blend of the map's
95
101
  themes instead of `theme`: position 0 is the first theme and 1.5 is halfway from the second to the
96
102
  third. Moving the position updates the map at once. See
97
103
  [theme transitions and blends](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/theme-transitions-and-blends.md).
98
104
 
105
+ Interactive maps automatically render the map's vegetation. `vegetation` overrides visibility
106
+ (omitting it uses the map default); false hides models and flat artwork alike. `viewMode` is `2d`
107
+ by default and may be `3d`: only maps with `vegetation.mode: 'auto'` switch representation.
108
+ Streets uses model trees in either view. Changing either prop updates the existing map immediately.
109
+ These props do not change camera pitch, buildings or lighting and do not apply in image mode. See
110
+ [vegetation controls](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/trees-3d.md#visibility-and-representation).
111
+
99
112
  `mapOptions` accepts native MapLibre options except `container` and `style`. Direct props win over
100
113
  those options, then the manifest view, then shared defaults. `TileflowMap` always selects a Tileflow
101
114
  map through its manifest; it has no renderer discriminator or direct-style mode. The obsolete
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/svelte",
3
- "version": "0.1.0-alpha.27",
3
+ "version": "0.1.0-alpha.29",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Svelte component for rendering Tileflow maps with MapLibre.",
6
6
  "type": "module",
@@ -44,16 +44,16 @@
44
44
  "access": "public"
45
45
  },
46
46
  "dependencies": {
47
- "@tileflow/core": ">=0.1.0-alpha.30 <0.1.0-beta.0",
48
- "@tileflow/interactions": ">=0.1.0-alpha.4 <0.1.0-beta.0"
47
+ "@tileflow/core": ">=0.1.0-alpha.35 <0.1.0-beta.0",
48
+ "@tileflow/interactions": ">=0.1.0-alpha.5 <0.1.0-beta.0"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "maplibre-gl": ">=6.4.1 <7",
52
52
  "svelte": ">=5 <6"
53
53
  },
54
54
  "devDependencies": {
55
- "maplibre-gl": "6.9.0",
56
- "svelte": "^5.43.17",
55
+ "maplibre-gl": "6.13.0",
56
+ "svelte": "^5.57.2",
57
57
  "svelte-check": "^4.3.4"
58
58
  },
59
59
  "scripts": {
@@ -34,6 +34,8 @@
34
34
  attachTileflowMapLifecycle,
35
35
  createTileflowSessionStarter,
36
36
  createTileflowThemeController,
37
+ createTileflowVegetation,
38
+ type TileflowVegetationController,
37
39
  createTileflowTransformRequest,
38
40
  getTileflowSystemColorScheme,
39
41
  loadTileflowStyleFonts,
@@ -71,7 +73,7 @@
71
73
  type TileflowMapLibreSemanticDomRenderTarget,
72
74
  type TileflowMapLibreSemanticDomRuntime,
73
75
  } from '@tileflow/interactions/maplibre';
74
- import type {TileflowMapProps} from './index.js';
76
+ import type {TileflowMapOptions, TileflowMapProps} from './index.js';
75
77
  import {loadTileflowMapLibre} from './maplibre.js';
76
78
  import {assertTileflowMapStyleInputs} from './style-source.js';
77
79
 
@@ -99,7 +101,10 @@
99
101
  export let popup: TileflowMapProps['popup'] = undefined;
100
102
  export let source: TileflowMapProps['source'];
101
103
  export let theme: TileflowMapProps['theme'] = undefined;
104
+ export let timeOfDay: TileflowMapProps['timeOfDay'] = undefined;
102
105
  export let themeBlend: TileflowMapProps['themeBlend'] = undefined;
106
+ export let vegetation: TileflowMapProps['vegetation'] = undefined;
107
+ export let viewMode: NonNullable<TileflowMapProps['viewMode']> = '2d';
103
108
  export let themeTransition: TileflowMapProps['themeTransition'] = undefined;
104
109
  export let tooltip: TileflowMapProps['tooltip'] = undefined;
105
110
  export let zoom: TileflowMapProps['zoom'] = undefined;
@@ -142,6 +147,9 @@
142
147
  let interactionRuntimesDisposing = false;
143
148
  let mapResizeObserver: ResizeObserver | null = null;
144
149
  let mapWorldRequestBridge: TileflowWorldRequestBridge | null = null;
150
+ let vegetationController: TileflowVegetationController | null = null;
151
+ $: vegetationController?.setVisible(vegetation);
152
+ $: vegetationController?.setViewMode(viewMode);
145
153
  let themeController: TileflowThemeController | null = null;
146
154
  let systemColorScheme: 'dark' | 'light' = 'light';
147
155
  let unsubscribeSystemColorScheme: (() => void) | null = null;
@@ -172,7 +180,7 @@
172
180
  : {defaultInteractionState: initialInteractionState}),
173
181
  });
174
182
  $: resolvedMode = resolveTileflowMapMode({mode});
175
- $: assertTileflowMapStyleInputs({source, theme, themeBlend, themeTransition});
183
+ $: assertTileflowMapStyleInputs({source, theme, timeOfDay, themeBlend, themeTransition});
176
184
  $: resolvedCaptureId = normalizeTileflowCaptureId(captureId);
177
185
  $: isImageMode = resolvedMode === 'image';
178
186
  $: mapName = source.map;
@@ -188,10 +196,12 @@
188
196
  colorScheme: systemColorScheme,
189
197
  manifestMap,
190
198
  theme,
199
+ timeOfDay,
191
200
  themeBlend,
192
201
  });
193
202
  $: themeBlendKey = themeBlend ? JSON.stringify(themeBlend.themes) : undefined;
194
203
  $: themeBlendPosition = themeBlend?.position;
204
+ $: themeBlendHold = themeBlend?.hold === true;
195
205
  $: resolvedThemeName = themeResolution.name;
196
206
  $: manifestView = resolveTileflowRuntimeView({manifestMap});
197
207
  $: manifestCenter = normalizeTileflowRuntimeCenter(manifestView.center);
@@ -307,6 +317,7 @@
307
317
  $: if (mounted) {
308
318
  blendStyles;
309
319
  themeBlendPosition;
320
+ themeBlendHold;
310
321
  void switchBlend();
311
322
  }
312
323
 
@@ -448,6 +459,8 @@
448
459
  const unsubscribeSemanticTargets = semanticRuntimeTargetsUnsubscribe;
449
460
  const currentMap = mapInstance;
450
461
  const currentThemeController = themeController;
462
+ const currentVegetationController = vegetationController;
463
+ vegetationController = null;
451
464
  const worldRequestBridge = mapWorldRequestBridge;
452
465
  interactionRuntimesDisposing = true;
453
466
  mapFairUseNotice = null;
@@ -490,6 +503,7 @@
490
503
  () => semanticInteractionsForMap?.dispose(),
491
504
  () => annotationInteractionsForMap?.dispose(),
492
505
  () => lifecycleAttachment?.dispose(),
506
+ () => currentVegetationController?.dispose(),
493
507
  () => currentThemeController?.dispose(),
494
508
  () => worldRequestBridge?.dispose(),
495
509
  () => fairUseNotice?.dispose(),
@@ -579,8 +593,6 @@
579
593
  pitch: resolvedPitch,
580
594
  style: runtime.style as StyleSpecification | string,
581
595
  transformRequest: createTileflowTransformRequest({
582
- always: true,
583
- asyncAnalyticsTiming: 'request',
584
596
  getAnalytics: () => resolvedAnalytics,
585
597
  sessionController: session,
586
598
  sessionId: session.sessionId,
@@ -610,6 +622,23 @@
610
622
  },
611
623
  });
612
624
 
625
+ vegetationController = createTileflowVegetation({
626
+ map: maplibreMap,
627
+ ...(themeController ? {controller: themeController} : {}),
628
+ visible: vegetation,
629
+ viewMode,
630
+ transformRequest: createTileflowTransformRequest({
631
+ getAnalytics: () => resolvedAnalytics,
632
+ sessionController: session,
633
+ sessionId: session.sessionId,
634
+ transformRequest: (url) =>
635
+ mapOptions?.transformRequest?.(
636
+ url,
637
+ 'Tile' as Parameters<NonNullable<TileflowMapOptions['transformRequest']>>[1],
638
+ ),
639
+ }),
640
+ });
641
+
613
642
  setBridgeInteractionDiagnostic('annotation-runtime', null);
614
643
  try {
615
644
  const annotationInteractionsForMap = createTileflowMapLibreDomRuntime<
@@ -784,7 +813,7 @@
784
813
  if (!themeController || !blendStyles || position === undefined) return;
785
814
  if (runtimeStyle) activeRuntimeResource = runtimeStyle;
786
815
  const result = await themeController.setBlend(
787
- {position, themes: blendStyles},
816
+ {hold: themeBlendHold, position, themes: blendStyles},
788
817
  {transition: themeTransition},
789
818
  );
790
819
  if (result.status === 'failed') {
@@ -1035,11 +1064,7 @@
1035
1064
  }
1036
1065
 
1037
1066
  type BridgeInteractionDiagnosticSource =
1038
- | 'annotation-runtime'
1039
- | 'portal'
1040
- | 'renderers'
1041
- | 'semantic-runtime'
1042
- | 'state';
1067
+ 'annotation-runtime' | 'portal' | 'renderers' | 'semantic-runtime' | 'state';
1043
1068
 
1044
1069
  function setBridgeInteractionDiagnostic(
1045
1070
  source: BridgeInteractionDiagnosticSource,
@@ -1242,6 +1267,7 @@
1242
1267
  colorScheme: 'dark' | 'light';
1243
1268
  manifestMap: TileflowRuntimeManifestMap | null;
1244
1269
  theme: TileflowMapProps['theme'];
1270
+ timeOfDay: TileflowMapProps['timeOfDay'];
1245
1271
  themeBlend: TileflowMapProps['themeBlend'];
1246
1272
  }): Readonly<{error: boolean; name: string | undefined}> {
1247
1273
  // A blend shows its nearest theme until the blend style is ready, and names it while blending.
@@ -1259,7 +1285,12 @@
1259
1285
  }
1260
1286
  return {
1261
1287
  error: false,
1262
- name: resolveTileflowRuntimeTheme(input.manifestMap, selection, input.colorScheme).name,
1288
+ name: resolveTileflowRuntimeTheme(
1289
+ input.manifestMap,
1290
+ selection,
1291
+ input.colorScheme,
1292
+ input.themeBlend ? undefined : input.timeOfDay,
1293
+ ).name,
1263
1294
  };
1264
1295
  } catch {
1265
1296
  return {error: true, name: undefined};
package/src/index.d.ts CHANGED
@@ -1,10 +1,15 @@
1
+ import type {TileflowTimeOfDay} from '@tileflow/core/runtime';
1
2
  import type {Map as MapLibreMap, MapOptions as MapLibreMapOptions} from 'maplibre-gl';
2
3
  import type {Snippet, SvelteComponentTyped} from 'svelte';
3
- import type {TileflowThemeTransition, TileflowThemeTransitionOptions} from '@tileflow/core/browser';
4
+ import type {
5
+ TileflowBrowserThemeBlendSelection,
6
+ TileflowThemeTransition,
7
+ TileflowViewMode,
8
+ TileflowThemeTransitionOptions,
9
+ } from '@tileflow/core/browser';
4
10
  import type {
5
11
  TileflowAnalytics,
6
12
  TileflowRuntimeSource,
7
- TileflowThemeBlendSelection,
8
13
  TileflowThemeSelection,
9
14
  } from '@tileflow/core/runtime';
10
15
  import type {
@@ -34,6 +39,8 @@ export type TileflowMapInteractionSnippet<
34
39
  > = Snippet<[context: TileflowInteractionViewContext<TAnnotation>]>;
35
40
 
36
41
  type TileflowMapBaseProps = {
42
+ vegetation?: boolean;
43
+ viewMode?: TileflowViewMode;
37
44
  alt?: string;
38
45
  analytics?: TileflowAnalytics;
39
46
  center?: [number, number];
@@ -46,11 +53,13 @@ type TileflowMapBaseProps = {
46
53
  mapOptions?: TileflowMapOptions;
47
54
  onThemeChange?: (transition: TileflowThemeTransition) => void;
48
55
  theme?: TileflowThemeSelection;
56
+ timeOfDay?: TileflowTimeOfDay;
49
57
  /**
50
58
  * Shows a continuous blend of the map's themes instead of `theme`. Moving `position` changes the
51
- * map at once; changing `themes` prepares a new blend.
59
+ * map at once; changing `themes` prepares a new blend. `hold` keeps labels, icons, and mixed
60
+ * artwork back during a camera move; clearing it brings them in at once.
52
61
  */
53
- themeBlend?: TileflowThemeBlendSelection;
62
+ themeBlend?: TileflowBrowserThemeBlendSelection;
54
63
  /** Cross-fades theme changes, including entering and leaving a blend. */
55
64
  themeTransition?: TileflowThemeTransitionOptions;
56
65
  zoom?: number;
@@ -1,6 +1,7 @@
1
1
  type TileflowMapStyleInput = {
2
2
  source?: unknown;
3
3
  theme?: unknown;
4
+ timeOfDay?: unknown;
4
5
  themeBlend?: unknown;
5
6
  themeTransition?: unknown;
6
7
  };
@@ -1,11 +1,12 @@
1
+ import {validateTileflowBrowserThemeBlendSelection} from '@tileflow/core/browser';
1
2
  import {
3
+ isTileflowTimeOfDay,
2
4
  validateTileflowRuntimeSource,
3
- validateTileflowThemeBlendSelection,
4
5
  validateTileflowThemeSelection,
5
6
  } from '@tileflow/core/runtime';
6
7
 
7
8
  /**
8
- * @typedef {{source?: unknown, theme?: unknown, themeBlend?: unknown, themeTransition?: unknown}}
9
+ * @typedef {{source?: unknown, theme?: unknown, timeOfDay?: unknown, themeBlend?: unknown, themeTransition?: unknown}}
9
10
  * TileflowMapStyleInput
10
11
  */
11
12
  /** @typedef {{ok: true} | {error: string; ok: false}} TileflowMapStyleInputValidation */
@@ -20,10 +21,16 @@ export function validateTileflowMapStyleInputs(input) {
20
21
  if (input.theme !== undefined && !validateTileflowThemeSelection(input.theme)) {
21
22
  return {error: 'theme must be a concrete portable theme name or "system"', ok: false};
22
23
  }
23
- if (input.themeBlend !== undefined && !validateTileflowThemeBlendSelection(input.themeBlend)) {
24
+ if (input.timeOfDay !== undefined && !isTileflowTimeOfDay(input.timeOfDay)) {
25
+ return {error: 'timeOfDay must be day, dawn, dusk, or night', ok: false};
26
+ }
27
+ if (
28
+ input.themeBlend !== undefined &&
29
+ !validateTileflowBrowserThemeBlendSelection(input.themeBlend)
30
+ ) {
24
31
  return {
25
32
  error:
26
- 'themeBlend must name two to eight concrete themes and a position from 0 to themes.length - 1',
33
+ 'themeBlend must name two to eight concrete themes, a position from 0 to themes.length - 1, and an optional boolean hold',
27
34
  ok: false,
28
35
  };
29
36
  }