@tileflow/svelte 0.1.0-alpha.23 → 0.1.0-alpha.25

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
@@ -1,211 +1,199 @@
1
1
  # @tileflow/svelte
2
2
 
3
- Svelte component for rendering Tileflow maps with MapLibre.
3
+ Svelte components for interactive Tileflow maps, annotations, and existing-image display.
4
+ The component consumes prepared assets; it does not compile executable map configuration in the
5
+ browser.
4
6
 
5
- Install `maplibre-gl` alongside the package and import its CSS once in your app.
7
+ > Related packages and guides: [documentation index](https://raw.githubusercontent.com/tileflow/tileflow-sdk/main/llms.txt).
6
8
 
7
- ```svelte
8
- <script lang="ts">
9
- import {TileflowMap} from '@tileflow/svelte';
10
- import 'maplibre-gl/dist/maplibre-gl.css';
11
- </script>
9
+ ## Install
12
10
 
13
- <TileflowMap
14
- source={{kind: 'tileflow', map: 'madrid'}}
15
- theme="system"
16
- center={[-3.7038, 40.4168]}
17
- zoom={12}
18
- mapOptions={{
19
- cooperativeGestures: true,
20
- maxZoom: 18,
21
- }}
22
- />
11
+ Use an existing Svelte 5 application. Supported peers are Svelte `>=5 <6` and MapLibre GL JS
12
+ `>=6.4.1 <7`. The local build workflow below requires Node.js 22 or newer.
13
+
14
+ ```sh
15
+ npm install @tileflow/svelte@alpha @tileflow/core@alpha @tileflow/maps@alpha "maplibre-gl@^6.4.1"
16
+ npm install --save-dev --save-exact tileflow@alpha
23
17
  ```
24
18
 
25
- ## MapLibre 6 worker
19
+ ## Prepare a map
26
20
 
27
- With MapLibre GL JS 6, configure the application-owned worker once before mounting an interactive
28
- map. The worker URL must come from the same `maplibre-gl` package installed by the application.
21
+ Create `tileflow.config.ts` at the application root:
22
+
23
+ <!-- docs:check -->
29
24
 
30
25
  ```ts
31
- // main.ts
32
- import workerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url';
33
- import {configureTileflowMapLibre} from '@tileflow/svelte';
26
+ import {defineMap} from '@tileflow/core';
27
+ import {streets} from '@tileflow/maps';
28
+
29
+ export default defineMap({
30
+ id: 'madrid',
31
+ version: 1,
32
+ extends: streets,
33
+ view: {center: [-3.7038, 40.4168], zoom: 12},
34
+ });
35
+ ```
34
36
 
35
- configureTileflowMapLibre({workerUrl});
37
+ For a Vite application serving `public/` at the URL root:
38
+
39
+ ```sh
40
+ npx tileflow build --out public/tileflow
36
41
  ```
37
42
 
38
- This is the Vite setup. Run it before `mount(...)`; `mode="image"` remains MapLibre-free. MapLibre
39
- GL JS 6.4.1-6.x is supported. Use the `@tileflow/next` or `@tileflow/webpack` README for their
40
- worker-delivery recipes.
43
+ SvelteKit serves static files from `static/` by default; for that setup, build to `static/tileflow`
44
+ instead. Set the component's manifest URL to the final public URL when a base path changes it.
45
+ Rebuild after config/asset changes or use the
46
+ [Vite integration](https://github.com/tileflow/tileflow-sdk/blob/main/packages/vite/README.md)
47
+ for watched development and production output. Local preparation needs no API key; rendering can
48
+ still fetch configured remote resources.
49
+
50
+ ## Configure the worker
41
51
 
42
- `mapOptions` accepts native MapLibre options except `container` and `style`, which
43
- Tileflow resolves from `source`. Direct
44
- Tileflow props such as `center`, `zoom`, and `interactive` take priority when
45
- provided. Camera resolution is the same in interactive and image modes: direct props, then
46
- `mapOptions`, then the published manifest view, then Tileflow's shared runtime defaults.
52
+ In a Vite client entry, add the following before mounting the application:
53
+
54
+ ```ts
55
+ import workerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url';
56
+ import {configureTileflowMapLibre} from '@tileflow/svelte';
47
57
 
48
- Every map has exactly one discriminated `source`. Use `kind: 'tileflow'` with `map` and an optional
49
- `manifestUrl` for published delivery. `kind: 'maplibre'` is an unmanaged escape hatch for one direct
50
- style object or URL; it has no Tileflow theme identity, `system` selection, switching, or manifest
51
- traceability. Browser components do not compile `tileflow.config.ts`.
58
+ configureTileflowMapLibre({workerUrl});
59
+ ```
52
60
 
53
- Without `manifestUrl`, the exact default is `/tileflow/manifest.json`. If a bundler, framework base
54
- path, reverse proxy, or Tileflow plugin publishes it elsewhere, set the final public URL explicitly;
55
- the component does not guess it. Manifest 404s, unknown map IDs, unresolved styles, and unresolved
56
- image URLs enter `data-tileflow-state="error"`.
61
+ Configure once before an interactive map mounts. In SvelteKit, place client-only setup in the
62
+ application's client initialization path rather than running browser setup in a server load
63
+ function. The `?worker&url` syntax is Vite-specific. Webpack users need the
64
+ [Webpack worker recipe](https://github.com/tileflow/tileflow-sdk/blob/main/packages/webpack/README.md).
65
+ The worker and shared module must match the application's installed MapLibre version.
57
66
 
58
- `theme` selects a published theme name. Omission uses `defaultTheme`; `"system"` requires the map's
59
- explicit light/dark mapping. Switching preserves the MapLibre instance, camera, and interactions,
60
- and rolls back a failed style. `onThemeChange` receives transition state.
67
+ ## Render a map
61
68
 
62
69
  ```svelte
63
- <TileflowMap
64
- source={{
65
- kind: 'tileflow',
66
- map: 'madrid',
67
- manifestUrl: 'https://cdn.example.com/tileflow/manifest.json',
68
- }}
69
- />
70
+ <script lang="ts">
71
+ import 'maplibre-gl/dist/maplibre-gl.css';
72
+ import {TileflowMap} from '@tileflow/svelte';
73
+ </script>
70
74
 
71
75
  <TileflowMap
72
- source={{
73
- kind: 'maplibre',
74
- style: 'https://cdn.example.com/tileflow/styles/madrid/dark.json',
75
- }}
76
+ source={{map: 'madrid'}}
77
+ theme="system"
78
+ center={[-3.7038, 40.4168]}
79
+ zoom={12}
80
+ mapOptions={{cooperativeGestures: true, maxZoom: 18}}
76
81
  />
77
82
  ```
78
83
 
79
- `mode="image"` resolves an explicit or published image URL and renders an `<img>` without loading
80
- or evaluating MapLibre. All environments follow the same published manifest contract.
84
+ Coordinates are `[longitude, latitude]`. `source` is one object, `{map, manifestUrl?}`.
85
+ `source.map` matches the config's portable ID, not a hosted `map_...` identifier. The default
86
+ manifest URL is exactly `/tileflow/manifest.json`. Set `source.manifestUrl` for a subpath or external
87
+ host; the browser does not infer bundler configuration.
88
+
89
+ Omitting `theme` uses `defaultTheme`. `system` needs an explicit light/dark mapping, which Streets
90
+ supplies. Theme changes keep the MapLibre instance, camera, and interactions, and roll back on
91
+ failure. `onThemeChange` reports transitions.
92
+
93
+ `mapOptions` accepts native MapLibre options except `container` and `style`. Direct props win over
94
+ those options, then the manifest view, then shared defaults. `TileflowMap` always selects a Tileflow
95
+ map through its manifest; it has no renderer discriminator or direct-style mode. The obsolete
96
+ source fields `kind` and `style` are rejected. For a completely unmanaged map, use `maplibre-gl`
97
+ directly rather than a Tileflow component.
81
98
 
82
- Hosted maps automatically preflight a short-lived commercial session grant before eligible
83
- resources. Setting `analytics={{enabled: false}}` disables the optional beacon only; it does not
84
- remove hosted authorization or override a user `mapOptions.transformRequest` callback.
85
- Use `analytics={{surfaceId: 'store-locator'}}` when the same Map is embedded in several stable
86
- product locations. Missing or invalid Surface IDs become `default`; do not use URLs, branches,
87
- random instance IDs, or user IDs.
88
- Direct Tileflow World maps keep early `GRACE` silent, show a compact accessible owner-action pill
89
- when late `GRACE` is activated, and show a stronger banner in `MANAGED_REQUIRED`. Missing tiles and
90
- MapLibre failures do not remove that recovery path.
99
+ ## Add annotations and native popup UI
91
100
 
92
- ## Annotations and overlays
101
+ Install the contract types used below:
93
102
 
94
- Use `annotations` for keyed markers with optional tooltips and popups. Tileflow supplies accessible,
95
- text-only defaults; Svelte snippets can replace any surface and are mounted into the DOM containers
96
- owned by the shared MapLibre interaction runtime.
103
+ ```sh
104
+ npm install @tileflow/interactions@alpha
105
+ ```
106
+
107
+ This Svelte 5 component uses the worker and CSS setup above:
97
108
 
98
109
  ```svelte
99
110
  <script lang="ts">
100
- import {TileflowMap} from '@tileflow/svelte';
101
111
  import type {
102
112
  TileflowAnnotation,
103
- TileflowInteractionBinding,
104
113
  TileflowInteractionState,
114
+ TileflowInteractionViewContext,
105
115
  } from '@tileflow/interactions';
116
+ import {TileflowMap} from '@tileflow/svelte';
106
117
 
107
- const annotations: TileflowAnnotation[] = [
118
+ const annotations = [
108
119
  {
109
- ariaLabel: 'Madrid',
110
- coordinate: [-3.7038, 40.4168],
111
120
  id: 'madrid',
112
121
  kind: 'marker',
113
- marker: {content: {kind: 'text', text: 'Madrid'}},
122
+ coordinate: [-3.7038, 40.4168],
123
+ ariaLabel: 'Madrid',
124
+ tooltip: {content: {kind: 'text', text: 'View Madrid'}},
114
125
  popup: {content: {kind: 'view', name: 'city-card'}},
115
- tooltip: {content: {kind: 'text', text: 'Open Madrid'}},
116
- },
117
- ];
118
-
119
- const interactions: TileflowInteractionBinding[] = [
120
- {
121
- id: 'poi-details',
122
- popup: {content: {kind: 'view', name: 'poi-card'}},
123
- target: {categories: ['food-drink'], domain: 'poi', kind: 'semantic-feature'},
124
- tooltip: {content: {field: 'name', fallback: 'Restaurant', kind: 'field'}},
125
126
  },
126
- ];
127
+ ] satisfies readonly TileflowAnnotation[];
127
128
 
128
- let interactionState: TileflowInteractionState = {popup: null};
129
+ let state = $state<TileflowInteractionState>({popup: null});
129
130
  </script>
130
131
 
131
- {#snippet marker(context)}
132
- <span>{context.annotation.ariaLabel}</span>
133
- {/snippet}
134
-
135
- {#snippet tooltip(context)}
132
+ {#snippet popup(context: TileflowInteractionViewContext)}
136
133
  {#if context.target.kind === 'annotation'}
137
- <span>{context.target.annotation.ariaLabel}</span>
138
- {:else}
139
- <span>Point of interest</span>
140
- {/if}
141
- {/snippet}
142
-
143
- {#snippet popup(context)}
144
- <article>
145
- {#if context.target.kind === 'annotation'}
134
+ <article>
146
135
  <h2>{context.target.annotation.ariaLabel}</h2>
147
- {:else if context.target.kind === 'semantic-feature'}
148
- <h2>POI details</h2>
149
- {/if}
150
- <button type="button" onclick={context.close}>Close</button>
151
- </article>
136
+ <button type="button" onclick={context.close}>Close</button>
137
+ </article>
138
+ {/if}
152
139
  {/snippet}
153
140
 
154
141
  <TileflowMap
155
- source={{kind: 'tileflow', map: 'madrid'}}
142
+ source={{map: 'madrid'}}
156
143
  {annotations}
157
- {interactions}
158
- {interactionState}
159
- onInteractionStateChange={(next) => (interactionState = next)}
160
- onInteractionEvent={(event) => console.log(event.type)}
161
- onInteractionDiagnostic={(diagnostic) => console.error(diagnostic)}
162
- {marker}
163
- {tooltip}
144
+ interactionState={state}
145
+ onInteractionStateChange={(next) => (state = next)}
146
+ onInteractionDiagnostic={(diagnostic) => console.error(diagnostic.code)}
164
147
  {popup}
165
148
  />
166
149
  ```
167
150
 
168
- `annotations` is the only application-owned marker input. Controlled `interactionState` and
169
- uncontrolled `defaultInteractionState` are mutually exclusive. `interactions` binds tooltips and
170
- popups to semantic POIs exposed by the compiled style metadata; applications select the stable
171
- `poi` domain and optional categories rather than physical style-layer IDs.
151
+ Annotations need a stable `id`, singular `coordinate`, and non-empty `ariaLabel`; optional data must
152
+ be JSON-safe. `marker` receives annotation-only context. `tooltip` and `popup` receive the shared
153
+ interaction context; narrow `target.kind` before reading annotation or semantic feature data.
172
154
 
173
- Snippet callbacks are read at the time an interaction occurs, and adding or removing a snippet
174
- switches between custom and default rendering without recreating the map or its keyed markers.
175
- `marker` receives the annotation-only context. `tooltip` and `popup` receive the general interaction
176
- context and therefore handle both annotation and semantic-feature targets. Each context includes
177
- resolved content, the target, optional view name, and `close()`.
155
+ Use `interactions` with `target: {kind: 'semantic-feature', domain: 'poi'}` for POIs already rendered
156
+ by the style. Optional categories use Tileflow's taxonomy, such as `food-drink` or `retail`, not raw
157
+ source classes. Semantic bindings require compatible style metadata and avoid one DOM marker per
158
+ feature. Annotation and semantic overlays share one popup state.
178
159
 
179
- Both runtimes are controlled by one interaction-state coordinator. Replacing an annotation popup
180
- with a POI popup (or the reverse) closes the previous owner before opening the next one, so event
181
- order remains `popup:close` followed by `popup:open`.
160
+ Controlled `interactionState` and uncontrolled `defaultInteractionState` are mutually exclusive.
161
+ Do not change ownership during the component's lifetime. Updating annotations, bindings, state, or
162
+ snippets reconciles existing runtimes rather than recreating MapLibre.
182
163
 
183
- Annotations and semantic interactions are interactive-only. The TypeScript API excludes
184
- annotations, interactions, interaction state, snippets, and interaction callbacks from the
185
- `mode="image"` branch. Untyped JavaScript that supplies them still receives an
186
- `UNSUPPORTED_MODE` diagnostic and capture readiness `error`, rather than pretending the static image
187
- contains live overlays.
164
+ Text/field descriptors render as text. A `view` names an application snippet; it is not serialized
165
+ HTML. Keep tooltips non-interactive and put buttons, links, and forms in popups.
188
166
 
189
- ## Headless capture readiness
167
+ ## Display an existing image
190
168
 
191
- Use `captureId` to disambiguate repeated named maps:
169
+ `mode="image"` displays an explicit `imageUrl` or derives an image URL from the selected manifest
170
+ map's `mapId`, `apiUrl`, concrete theme and view, without loading MapLibre. It still requires a valid
171
+ Tileflow source. A local style build does not generate that image. This mode does not submit or poll
172
+ a Static Maps operation; use the server-side
173
+ [Static Maps client](https://github.com/tileflow/tileflow-sdk/blob/main/packages/static/README.md)
174
+ for new renders, keeping privileged credentials off the browser.
192
175
 
193
- ```svelte
194
- <TileflowMap source={{kind: 'tileflow', map: 'madrid'}} captureId="checkout-map" />
195
- ```
176
+ Annotations, semantic bindings, interaction state, and interaction snippets are interactive-only.
177
+ Untyped callers that pass them to image mode receive `UNSUPPORTED_MODE` and capture readiness
178
+ `error`, not simulated live overlays.
179
+
180
+ ## Capture, authorization, and troubleshooting
196
181
 
197
- The root exposes `data-tileflow-map`, optional `data-tileflow-capture-id`, and
198
- `data-tileflow-state="loading|idle|error"`. It becomes idle only after MapLibre idle plus two
199
- animation frames and, for custom interaction snippets, the Svelte DOM commit plus two current
200
- animation frames. It returns to loading when either surface changes and marks map or interaction
201
- errors. In image mode, readiness follows image decode/load when no interaction configuration is
202
- present. Application capture selects exactly one ready target.
182
+ Use `captureId` to disambiguate repeated maps. The root exposes `data-tileflow-map`, resolved
183
+ `data-tileflow-theme`, optional `data-tileflow-capture-id`, and
184
+ `data-tileflow-state="loading|idle|error"`. Readiness includes MapLibre/image loading and committed
185
+ Svelte interaction views. Application capture needs exactly one ready target and the application's
186
+ normal loopback server.
203
187
 
204
- ## Compatibility
188
+ For an error or blank map, check the manifest URL, map/theme IDs, imported CSS, and matching
189
+ worker/shared assets. The component does not guess a new manifest location after a 404.
205
190
 
206
- The supported peer window is Svelte 5.x and MapLibre GL JS 6.4.1-6.x. Compatibility smoke tests
207
- install Svelte 5.0.0 with the exact lower bound and selected current release from packed Tileflow
208
- tarballs, typecheck the public declarations, and compile real Svelte consumers. Future Svelte majors
209
- stay outside the peer range until that matrix passes.
191
+ Hosted maps acquire a short-lived session grant before eligible resource requests.
192
+ `analytics={{enabled: false}}` disables the optional beacon, not authorization. Use a stable product
193
+ `surfaceId`, not a URL or user identifier. Direct World maps can show owner-action notices when
194
+ managed delivery is required. Imports and the image path remain SSR-safe; interactive rendering
195
+ requires a browser with WebGL.
210
196
 
211
- Docs: https://tileflow.dev/docs
197
+ See the [interaction guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/interactions/README.md)
198
+ and [browser runtime contract](https://github.com/tileflow/tileflow-sdk/blob/main/docs/contracts/framework-browser-runtime.md).
199
+ Use the installed declarations and README for release-specific behavior; `main` may be newer than npm.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/svelte",
3
- "version": "0.1.0-alpha.23",
3
+ "version": "0.1.0-alpha.25",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Svelte component for rendering Tileflow maps with MapLibre.",
6
6
  "type": "module",
@@ -44,8 +44,8 @@
44
44
  "access": "public"
45
45
  },
46
46
  "dependencies": {
47
- "@tileflow/core": ">=0.1.0-alpha.24 <0.1.0-beta.0",
48
- "@tileflow/interactions": ">=0.1.0-alpha.3 <0.1.0-beta.0"
47
+ "@tileflow/core": ">=0.1.0-alpha.27 <0.1.0-beta.0",
48
+ "@tileflow/interactions": ">=0.1.0-alpha.4 <0.1.0-beta.0"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "maplibre-gl": ">=6.4.1 <7",
@@ -23,7 +23,6 @@
23
23
  resolveTileflowRuntimeTheme,
24
24
  resolveTileflowRuntimeView,
25
25
  resolveTileflowStaticImageUrl,
26
- shouldLoadTileflowManifest,
27
26
  type TileflowRuntimeManifestMap,
28
27
  } from '@tileflow/core/runtime';
29
28
  import {
@@ -144,7 +143,7 @@
144
143
  let manifestMap: TileflowRuntimeManifestMap | null = null;
145
144
  let manifestLoadId = 0;
146
145
  let manifestResolutionKey = '';
147
- let manifestResolutionState: 'error' | 'loading' | 'not-needed' | 'ready' = 'loading';
146
+ let manifestResolutionState: 'error' | 'loading' | 'ready' = 'loading';
148
147
  let mapLoadId = 0;
149
148
  let mounted = false;
150
149
  let refreshRunId = 0;
@@ -170,23 +169,11 @@
170
169
  $: assertTileflowMapStyleInputs({source, theme});
171
170
  $: resolvedCaptureId = normalizeTileflowCaptureId(captureId);
172
171
  $: isImageMode = resolvedMode === 'image';
173
- $: mapName = source?.kind === 'tileflow' ? source.map : undefined;
174
- $: manifestUrl =
175
- source?.kind === 'tileflow'
176
- ? (source.manifestUrl ?? defaultTileflowManifestUrl)
177
- : defaultTileflowManifestUrl;
178
- $: shouldLoadManifest = shouldLoadTileflowManifest({
179
- source,
180
- });
181
- $: manifestRequestKey = shouldLoadManifest
182
- ? JSON.stringify([manifestUrl, mapName])
183
- : 'not-needed';
172
+ $: mapName = source.map;
173
+ $: manifestUrl = source.manifestUrl ?? defaultTileflowManifestUrl;
174
+ $: manifestRequestKey = JSON.stringify([manifestUrl, mapName]);
184
175
  $: currentManifestResolutionState =
185
- manifestResolutionKey === manifestRequestKey
186
- ? manifestResolutionState
187
- : shouldLoadManifest
188
- ? 'loading'
189
- : 'not-needed';
176
+ manifestResolutionKey === manifestRequestKey ? manifestResolutionState : 'loading';
190
177
  $: manifestMap =
191
178
  manifestResolutionKey === manifestRequestKey && manifestResolutionState === 'ready'
192
179
  ? loadedManifestMap
@@ -194,7 +181,6 @@
194
181
  $: themeResolution = resolveThemeSelection({
195
182
  colorScheme: systemColorScheme,
196
183
  manifestMap,
197
- source,
198
184
  theme,
199
185
  });
200
186
  $: resolvedThemeName = themeResolution.name;
@@ -237,7 +223,6 @@
237
223
  isImageMode,
238
224
  runtimeImageUrl,
239
225
  runtimeStyle,
240
- shouldLoadManifest,
241
226
  themeResolutionError: themeResolution.error,
242
227
  });
243
228
  $: currentMapCaptureState =
@@ -384,17 +369,7 @@
384
369
  }
385
370
 
386
371
  async function refreshManifest() {
387
- const shouldLoad = shouldLoadManifest;
388
372
  const requestKey = manifestRequestKey;
389
-
390
- if (!shouldLoad) {
391
- manifestLoadId += 1;
392
- loadedManifestMap = null;
393
- manifestResolutionKey = requestKey;
394
- manifestResolutionState = 'not-needed';
395
- return;
396
- }
397
-
398
373
  const loadId = ++manifestLoadId;
399
374
  loadedManifestMap = null;
400
375
  manifestResolutionKey = requestKey;
@@ -1228,16 +1203,12 @@
1228
1203
  function resolveThemeSelection(input: {
1229
1204
  colorScheme: 'dark' | 'light';
1230
1205
  manifestMap: TileflowRuntimeManifestMap | null;
1231
- source: TileflowMapProps['source'];
1232
1206
  theme: TileflowMapProps['theme'];
1233
1207
  }): Readonly<{error: boolean; name: string | undefined}> {
1234
- if (!input.manifestMap || input.source.kind !== 'tileflow') {
1208
+ if (!input.manifestMap) {
1235
1209
  return {
1236
1210
  error: false,
1237
- name:
1238
- input.source.kind === 'tileflow' && input.theme && input.theme !== 'system'
1239
- ? input.theme
1240
- : undefined,
1211
+ name: input.theme && input.theme !== 'system' ? input.theme : undefined,
1241
1212
  };
1242
1213
  }
1243
1214
 
@@ -1252,16 +1223,15 @@
1252
1223
  }
1253
1224
 
1254
1225
  function resolveRuntimeResolutionState(input: {
1255
- currentManifestResolutionState: 'error' | 'loading' | 'not-needed' | 'ready';
1226
+ currentManifestResolutionState: 'error' | 'loading' | 'ready';
1256
1227
  imageSize: {height: number; width: number} | null;
1257
1228
  imageUrl: string | undefined;
1258
1229
  isImageMode: boolean;
1259
1230
  runtimeImageUrl: string | undefined;
1260
1231
  runtimeStyle: ReturnType<typeof resolveTileflowRuntimeStyle>;
1261
- shouldLoadManifest: boolean;
1262
1232
  themeResolutionError: boolean;
1263
1233
  }): 'error' | 'idle' | 'loading' {
1264
- if (input.shouldLoadManifest && input.currentManifestResolutionState !== 'ready') {
1234
+ if (input.currentManifestResolutionState !== 'ready') {
1265
1235
  return input.currentManifestResolutionState === 'error' ? 'error' : 'loading';
1266
1236
  }
1267
1237
  if (input.themeResolutionError) return 'error';
@@ -16,12 +16,6 @@ export function validateTileflowMapStyleInputs(input) {
16
16
  if (input.theme !== undefined && !validateTileflowThemeSelection(input.theme)) {
17
17
  return {error: 'theme must be a concrete portable theme name or "system"', ok: false};
18
18
  }
19
- if (
20
- input.theme !== undefined &&
21
- /** @type {{kind?: unknown}} */ (input.source).kind !== 'tileflow'
22
- ) {
23
- return {error: 'theme is only valid with a tileflow source', ok: false};
24
- }
25
19
  return {ok: true};
26
20
  }
27
21