@tileflow/core 0.1.0-alpha.4 → 0.1.0-alpha.5

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,199 +1,1423 @@
1
- // ../../apps/web/public/openmaptiles-osm-bright-style/style.json
2
- var style_default = {
3
- version: 8,
4
- name: "Bright",
5
- metadata: {
6
- "mapbox:autocomposite": false,
7
- "mapbox:groups": {
8
- "1444849242106.713": { collapsed: false, name: "Places" },
9
- "1444849334699.1902": { collapsed: true, name: "Bridges" },
10
- "1444849345966.4436": { collapsed: false, name: "Roads" },
11
- "1444849354174.1904": { collapsed: true, name: "Tunnels" },
12
- "1444849364238.8171": { collapsed: false, name: "Buildings" },
13
- "1444849382550.77": { collapsed: false, name: "Water" },
14
- "1444849388993.3071": { collapsed: false, name: "Land" }
15
- },
16
- "mapbox:type": "template",
17
- "openmaptiles:mapbox:owner": "openmaptiles",
18
- "openmaptiles:mapbox:source:url": "mapbox://openmaptiles.4qljc88t",
19
- "openmaptiles:version": "3.x"
20
- },
21
- center: [0, 0],
22
- zoom: 1,
23
- bearing: 0,
24
- pitch: 0,
25
- sources: {
26
- openmaptiles: {
27
- type: "vector",
28
- url: "https://api.maptiler.com/tiles/v3-openmaptiles/tiles.json?key={key}"
29
- }
30
- },
31
- sprite: "https://openmaptiles.github.io/osm-bright-gl-style/sprite",
32
- glyphs: "https://api.maptiler.com/fonts/{fontstack}/{range}.pbf?key={key}",
33
- layers: [
34
- {
35
- id: "background",
36
- type: "background",
37
- paint: { "background-color": "#f8f4f0" }
38
- },
1
+ // src/basemaps/osm.ts
2
+ function osm(options = {}) {
3
+ return {
4
+ type: "osm",
5
+ ...options
6
+ };
7
+ }
8
+
9
+ // src/modules/roads/index.ts
10
+ function roads(options = {}) {
11
+ return {
12
+ type: "roads",
13
+ preset: options.preset ?? "modern",
14
+ ...options.density ? { density: options.density } : {}
15
+ };
16
+ }
17
+ function resolveRoads(legacyRoads, moduleConfig, fallback) {
18
+ if (!moduleConfig) {
19
+ return legacyRoads ?? fallback;
20
+ }
21
+ if (moduleConfig.preset === "minimal" || moduleConfig.density === "clean") {
22
+ return "soft";
23
+ }
24
+ if (moduleConfig.density === "dense") {
25
+ return "detailed";
26
+ }
27
+ return "standard";
28
+ }
29
+ function roadLayers(source, sourceLayer, colors, roadsMode) {
30
+ if (roadsMode === "hidden") {
31
+ return [];
32
+ }
33
+ const opacity = roadsMode === "soft" ? 0.72 : roadsMode === "standard" ? 0.84 : 0.96;
34
+ const casingOpacity = roadsMode === "detailed" ? 0.3 : 0.16;
35
+ const majorRoadFilter = [
36
+ "match",
37
+ ["get", "class"],
38
+ ["motorway", "trunk", "primary", "secondary"],
39
+ true,
40
+ false
41
+ ];
42
+ const minorRoadFilter = ["!", majorRoadFilter];
43
+ const nonTunnelRoadFilter = ["!=", ["get", "brunnel"], "tunnel"];
44
+ return [
39
45
  {
40
- id: "landcover-glacier",
41
- type: "fill",
42
- metadata: { "mapbox:group": "1444849388993.3071" },
43
- source: "openmaptiles",
44
- "source-layer": "landcover",
45
- filter: ["==", "subclass", "glacier"],
46
- layout: { visibility: "visible" },
46
+ id: "roads-tunnels-casing",
47
+ type: "line",
48
+ source,
49
+ "source-layer": sourceLayer,
50
+ minzoom: 10,
51
+ filter: ["==", ["get", "brunnel"], "tunnel"],
47
52
  paint: {
48
- "fill-color": "#fff",
49
- "fill-opacity": { base: 1, stops: [[0, 0.9], [10, 0.3]] }
53
+ "line-color": colors.roads.tunnel,
54
+ "line-dasharray": [0.6, 0.8],
55
+ "line-opacity": 0.28,
56
+ "line-width": ["interpolate", ["linear"], ["zoom"], 10, 0.5, 14, 1.7, 16, 3.8]
50
57
  }
51
58
  },
52
59
  {
53
- id: "landuse-residential",
54
- type: "fill",
55
- metadata: { "mapbox:group": "1444849388993.3071" },
56
- source: "openmaptiles",
57
- "source-layer": "landuse",
58
- filter: [
59
- "all",
60
- ["in", "class", "residential", "suburb", "neighbourhood"]
61
- ],
62
- layout: { visibility: "visible" },
60
+ id: "roads-tunnels",
61
+ type: "line",
62
+ source,
63
+ "source-layer": sourceLayer,
64
+ minzoom: 10,
65
+ filter: ["==", ["get", "brunnel"], "tunnel"],
63
66
  paint: {
64
- "fill-color": {
65
- base: 1,
66
- stops: [
67
- [12, "hsla(30, 19%, 90%, 0.4)"],
68
- [16, "hsla(30, 19%, 90%, 0.2)"]
69
- ]
70
- }
67
+ "line-color": colors.roads.minor,
68
+ "line-dasharray": [0.6, 0.8],
69
+ "line-opacity": 0.2,
70
+ "line-width": ["interpolate", ["linear"], ["zoom"], 10, 0.25, 14, 1, 16, 2.4]
71
71
  }
72
72
  },
73
73
  {
74
- id: "landuse-commercial",
75
- type: "fill",
76
- metadata: { "mapbox:group": "1444849388993.3071" },
77
- source: "openmaptiles",
78
- "source-layer": "landuse",
79
- filter: [
80
- "all",
81
- ["==", "$type", "Polygon"],
82
- ["==", "class", "commercial"]
83
- ],
84
- layout: { visibility: "visible" },
85
- paint: { "fill-color": "hsla(0, 60%, 87%, 0.23)" }
86
- },
87
- {
88
- id: "landuse-industrial",
89
- type: "fill",
90
- source: "openmaptiles",
91
- "source-layer": "landuse",
92
- filter: [
93
- "all",
94
- ["==", "$type", "Polygon"],
95
- ["in", "class", "industrial", "garages", "dam"]
96
- ],
97
- layout: { visibility: "visible" },
98
- paint: { "fill-color": "hsla(49, 100%, 88%, 0.34)" }
99
- },
100
- {
101
- id: "landuse-cemetery",
102
- type: "fill",
103
- metadata: { "mapbox:group": "1444849388993.3071" },
104
- source: "openmaptiles",
105
- "source-layer": "landuse",
106
- filter: ["==", "class", "cemetery"],
107
- paint: { "fill-color": "#e0e4dd" }
108
- },
109
- {
110
- id: "landuse-hospital",
111
- type: "fill",
112
- metadata: { "mapbox:group": "1444849388993.3071" },
113
- source: "openmaptiles",
114
- "source-layer": "landuse",
115
- filter: ["==", "class", "hospital"],
116
- paint: { "fill-color": "#fde" }
117
- },
118
- {
119
- id: "landuse-school",
120
- type: "fill",
121
- metadata: { "mapbox:group": "1444849388993.3071" },
122
- source: "openmaptiles",
123
- "source-layer": "landuse",
124
- filter: ["==", "class", "school"],
125
- paint: { "fill-color": "#f0e8f8" }
126
- },
127
- {
128
- id: "landuse-railway",
129
- type: "fill",
130
- metadata: { "mapbox:group": "1444849388993.3071" },
131
- source: "openmaptiles",
132
- "source-layer": "landuse",
133
- filter: ["==", "class", "railway"],
134
- layout: { visibility: "visible" },
135
- paint: { "fill-color": "hsla(30, 19%, 90%, 0.4)" }
136
- },
137
- {
138
- id: "landcover-wood",
139
- type: "fill",
140
- metadata: { "mapbox:group": "1444849388993.3071" },
141
- source: "openmaptiles",
142
- "source-layer": "landcover",
143
- filter: ["==", "class", "wood"],
74
+ id: "roads-casing",
75
+ type: "line",
76
+ source,
77
+ "source-layer": sourceLayer,
78
+ minzoom: 6,
79
+ filter: nonTunnelRoadFilter,
144
80
  paint: {
145
- "fill-antialias": { base: 1, stops: [[0, false], [9, true]] },
146
- "fill-color": "#6a4",
147
- "fill-opacity": 0.1,
148
- "fill-outline-color": "hsla(0, 0%, 0%, 0.03)"
81
+ "line-color": colors.roads.casing,
82
+ "line-opacity": casingOpacity,
83
+ "line-width": ["interpolate", ["linear"], ["zoom"], 6, 0.4, 12, 1.2, 16, 4]
149
84
  }
150
85
  },
151
86
  {
152
- id: "landcover-grass",
153
- type: "fill",
154
- metadata: { "mapbox:group": "1444849388993.3071" },
155
- source: "openmaptiles",
156
- "source-layer": "landcover",
157
- filter: ["==", "class", "grass"],
158
- paint: { "fill-color": "#d8e8c8", "fill-opacity": 1 }
159
- },
160
- {
161
- id: "landcover-grass-park",
162
- type: "fill",
163
- metadata: { "mapbox:group": "1444849388993.3071" },
164
- source: "openmaptiles",
165
- "source-layer": "park",
166
- filter: ["==", "class", "public_park"],
167
- paint: { "fill-color": "#d8e8c8", "fill-opacity": 0.8 }
87
+ id: "roads-minor",
88
+ type: "line",
89
+ source,
90
+ "source-layer": sourceLayer,
91
+ minzoom: 6,
92
+ filter: ["all", nonTunnelRoadFilter, minorRoadFilter],
93
+ paint: {
94
+ "line-color": colors.roads.minor,
95
+ "line-opacity": opacity * 0.88,
96
+ "line-width": ["interpolate", ["linear"], ["zoom"], 6, 0.2, 12, 0.8, 16, 3]
97
+ }
168
98
  },
169
99
  {
170
- id: "waterway_tunnel",
100
+ id: "roads-major",
171
101
  type: "line",
172
- source: "openmaptiles",
173
- "source-layer": "waterway",
174
- minzoom: 14,
175
- filter: [
176
- "all",
177
- ["in", "class", "river", "stream", "canal"],
178
- ["==", "brunnel", "tunnel"]
179
- ],
180
- layout: { "line-cap": "round", visibility: "visible" },
102
+ source,
103
+ "source-layer": sourceLayer,
104
+ minzoom: 5,
105
+ filter: ["all", nonTunnelRoadFilter, majorRoadFilter],
181
106
  paint: {
182
- "line-color": "#a0c8f0",
183
- "line-dasharray": [2, 4],
184
- "line-width": { base: 1.3, stops: [[13, 0.5], [20, 6]] }
107
+ "line-color": colors.roads.primary,
108
+ "line-opacity": opacity,
109
+ "line-width": ["interpolate", ["linear"], ["zoom"], 5, 0.35, 12, 1.2, 16, 4]
185
110
  }
186
111
  },
187
112
  {
188
- id: "waterway-other",
113
+ id: "roads-bridges",
189
114
  type: "line",
190
- metadata: { "mapbox:group": "1444849382550.77" },
191
- source: "openmaptiles",
192
- "source-layer": "waterway",
193
- filter: [
194
- "all",
195
- ["!in", "class", "canal", "river", "stream"],
196
- ["==", "intermittent", 0]
115
+ source,
116
+ "source-layer": sourceLayer,
117
+ minzoom: 10,
118
+ filter: ["==", ["get", "brunnel"], "bridge"],
119
+ paint: {
120
+ "line-color": colors.roads.bridge,
121
+ "line-opacity": opacity,
122
+ "line-width": ["interpolate", ["linear"], ["zoom"], 10, 0.45, 14, 1.6, 16, 4.4]
123
+ }
124
+ }
125
+ ];
126
+ }
127
+
128
+ // src/themes/index.ts
129
+ var defaultColors = {
130
+ background: "#F6F7F3",
131
+ canvas: "#F6F7F3",
132
+ land: "#F1F3ED",
133
+ water: "#A9D3F5",
134
+ park: "#CDE8B5",
135
+ greenspace: "#CDE8B5",
136
+ nature: "#CDE8B5",
137
+ building: "#E6E3DA",
138
+ road: "#FFFFFF",
139
+ roadMajor: "#F4C95D",
140
+ boundary: "#C9D1D9",
141
+ text: "#3C4043",
142
+ textMuted: "#727B84",
143
+ textHalo: "#FFFFFF"
144
+ };
145
+ var themePresets = {
146
+ standard: {
147
+ colors: defaultColors,
148
+ density: "balanced",
149
+ labels: "balanced",
150
+ poi: "minimal",
151
+ roads: "standard"
152
+ },
153
+ light: {
154
+ colors: {
155
+ background: "#F9FAF8",
156
+ land: "#F6F7F3",
157
+ water: "#C5E1F5",
158
+ park: "#DDEED2",
159
+ greenspace: "#DDEED2",
160
+ building: "#ECEAE4",
161
+ road: "#FFFFFF",
162
+ roadMajor: "#F7D58A",
163
+ boundary: "#C9D1D9",
164
+ text: "#5F6368",
165
+ textMuted: "#87909A",
166
+ textHalo: "#FFFFFF"
167
+ },
168
+ density: "clean",
169
+ labels: "essential",
170
+ poi: "minimal",
171
+ roads: "standard"
172
+ },
173
+ dark: {
174
+ colors: {
175
+ background: "#1C2228",
176
+ land: "#232B32",
177
+ water: "#18384D",
178
+ park: "#274230",
179
+ greenspace: "#274230",
180
+ building: "#2F363D",
181
+ road: "#39434D",
182
+ roadMajor: "#6E7580",
183
+ boundary: "#53606B",
184
+ text: "#D7DEE6",
185
+ textMuted: "#A3AFBA",
186
+ textHalo: "#1C2228"
187
+ },
188
+ density: "balanced",
189
+ labels: "balanced",
190
+ poi: "minimal",
191
+ roads: "standard"
192
+ },
193
+ minimal: {
194
+ colors: {
195
+ background: "#FAFAF8",
196
+ land: "#F7F7F2",
197
+ water: "#D6EAF6",
198
+ park: "#E5EEDB",
199
+ greenspace: "#E5EEDB",
200
+ building: "#F0EEE8",
201
+ road: "#FFFFFF",
202
+ roadMajor: "#E5E0D7",
203
+ boundary: "#D4D9DD",
204
+ text: "#697079",
205
+ textMuted: "#8E969F",
206
+ textHalo: "#FFFFFF"
207
+ },
208
+ density: "clean",
209
+ labels: "essential",
210
+ poi: "none",
211
+ roads: "soft"
212
+ },
213
+ "osm-bright-2": {
214
+ colors: {
215
+ background: "#EBE9E4",
216
+ land: "#F5F4F1",
217
+ water: "#AADAFF",
218
+ park: "#C7E6A8",
219
+ greenspace: "#C7E6A8",
220
+ building: "#EFEDE7",
221
+ road: "#FFFFFF",
222
+ roadMajor: "#F9C97C",
223
+ boundary: "#B8C0CA",
224
+ text: "#5F6368",
225
+ textMuted: "#87909A",
226
+ textHalo: "#FFFFFF"
227
+ },
228
+ density: "balanced",
229
+ labels: "balanced",
230
+ poi: "balanced",
231
+ roads: "detailed"
232
+ }
233
+ };
234
+ var defaultTypography = {
235
+ font: "Noto Sans",
236
+ fontFamily: "Noto Sans",
237
+ weight: "regular"
238
+ };
239
+ var fontWeightLabels = {
240
+ regular: "Regular",
241
+ medium: "Medium",
242
+ semibold: "Semibold",
243
+ bold: "Bold"
244
+ };
245
+ function getDefaultColors() {
246
+ return { ...defaultColors };
247
+ }
248
+ function resolveTheme(theme, projectThemes) {
249
+ if (!theme) {
250
+ return resolveLegacyTheme("light");
251
+ }
252
+ if (typeof theme === "string") {
253
+ if (projectThemes?.[theme]) {
254
+ return resolveProjectTheme(theme, projectThemes);
255
+ }
256
+ if (isLegacyTheme(theme)) {
257
+ return resolveLegacyTheme(theme);
258
+ }
259
+ throw new Error(`Unknown Tileflow theme: ${theme}`);
260
+ }
261
+ return resolveInlineTheme(theme, projectThemes);
262
+ }
263
+ function resolveColors(overrides = {}, defaults = defaultColors, moduleOverrides = {}, moduleDefaults = {}) {
264
+ const defaultInput = normalizeColorAliases(defaults);
265
+ const overrideInput = normalizeColorAliases(overrides);
266
+ const defaultModules = mergeThemeModules(extractLegacyModuleColors(defaults), moduleDefaults);
267
+ const overrideModules = mergeThemeModules(extractLegacyModuleColors(overrides), moduleOverrides);
268
+ const base = resolveBaseColors(overrideInput, defaultInput);
269
+ const roadCasing = overrideModules.roads?.casing ?? defaultModules.roads?.casing ?? overrideInput.roadCasing ?? defaultInput.roadCasing ?? mix(base.road, base.text, 0.2);
270
+ const baseResolved = {
271
+ ...base,
272
+ canvas: base.background,
273
+ greenspace: base.park,
274
+ nature: base.park,
275
+ roadCasing,
276
+ roadMinor: base.road
277
+ };
278
+ const roadDefaults = {
279
+ bridge: base.roadMajor,
280
+ casing: roadCasing,
281
+ ferry: base.water,
282
+ minor: base.road,
283
+ motorway: base.roadMajor,
284
+ path: mix(base.road, base.park, 0.2),
285
+ primary: base.roadMajor,
286
+ rail: base.textMuted,
287
+ secondary: base.roadMajor,
288
+ trunk: base.roadMajor,
289
+ tunnel: mix(roadCasing, base.background, 0.35)
290
+ };
291
+ const labelDefaults = {
292
+ country: base.text,
293
+ halo: base.textHalo,
294
+ muted: base.textMuted,
295
+ poi: base.textMuted,
296
+ primary: base.text,
297
+ road: base.textMuted,
298
+ settlement: base.text,
299
+ water: mix(base.water, base.text, 0.42)
300
+ };
301
+ const landuseDefaults = {
302
+ cemetery: mix(base.park, base.land, 0.35),
303
+ civic: mix(base.land, base.textMuted, 0.08),
304
+ commercial: mix(base.land, base.textMuted, 0.05),
305
+ industrial: mix(base.land, base.text, 0.04),
306
+ residential: mix(base.land, base.background, 0.35)
307
+ };
308
+ const landcoverDefaults = {
309
+ grass: mix(base.park, base.land, 0.25),
310
+ ice: mix(base.water, base.background, 0.65),
311
+ park: base.park,
312
+ protected: mix(base.park, base.textMuted, 0.08),
313
+ sand: mix(base.land, base.roadMajor, 0.1),
314
+ wood: mix(base.park, base.text, 0.08)
315
+ };
316
+ const hydroDefaults = {
317
+ ferry: mix(base.water, base.text, 0.24),
318
+ label: labelDefaults.water,
319
+ water: base.water,
320
+ waterway: mix(base.water, base.text, 0.08)
321
+ };
322
+ const buildingDefaults = {
323
+ extrusion: mix(base.building, base.text, 0.08),
324
+ fill: base.building,
325
+ outline: mix(base.building, base.text, 0.2)
326
+ };
327
+ const boundaryDefaults = {
328
+ admin: base.boundary,
329
+ disputed: alpha(base.boundary, 0.65),
330
+ major: mix(base.boundary, base.text, 0.2),
331
+ maritime: mix(base.water, base.text, 0.25)
332
+ };
333
+ return {
334
+ ...mapColorRecord(baseResolved),
335
+ boundaries: mapColorRecord(
336
+ resolveGroup(boundaryDefaults, defaultModules.boundaries, overrideModules.boundaries)
337
+ ),
338
+ buildings: mapColorRecord(
339
+ resolveGroup(buildingDefaults, defaultModules.buildings, overrideModules.buildings)
340
+ ),
341
+ hydro: mapColorRecord(resolveGroup(hydroDefaults, defaultModules.hydro, overrideModules.hydro)),
342
+ labels: mapColorRecord(
343
+ resolveGroup(labelDefaults, defaultModules.labels, overrideModules.labels)
344
+ ),
345
+ landcover: mapColorRecord(
346
+ resolveGroup(landcoverDefaults, defaultModules.landcover, overrideModules.landcover)
347
+ ),
348
+ landuse: mapColorRecord(
349
+ resolveGroup(landuseDefaults, defaultModules.landuse, overrideModules.landuse)
350
+ ),
351
+ roads: mapColorRecord(resolveGroup(roadDefaults, defaultModules.roads, overrideModules.roads))
352
+ };
353
+ }
354
+ function resolveTypography(overrides = {}, defaults = defaultTypography) {
355
+ const font = overrides.font ?? overrides.fontFamily ?? defaults.font;
356
+ return {
357
+ font,
358
+ fontFamily: font,
359
+ weight: overrides.weight ?? defaults.weight
360
+ };
361
+ }
362
+ function textFont(typography) {
363
+ return [`${typography.font} ${fontWeightLabels[typography.weight]}`];
364
+ }
365
+ function resolveLegacyTheme(theme) {
366
+ const preset = themePresets[theme];
367
+ const mode = theme === "dark" ? "dark" : "light";
368
+ return {
369
+ colorOverrides: theme === "light" ? {} : preset.colors,
370
+ colors: preset.colors,
371
+ custom: false,
372
+ layerOverrides: {},
373
+ mode,
374
+ moduleOverrides: {},
375
+ modules: {},
376
+ name: theme,
377
+ preset,
378
+ typography: defaultTypography,
379
+ typographyOverridden: false
380
+ };
381
+ }
382
+ function resolveProjectTheme(name, projectThemes, seen = []) {
383
+ if (seen.includes(name)) {
384
+ throw new Error(`Circular Tileflow theme extends: ${[...seen, name].join(" -> ")}`);
385
+ }
386
+ const theme = projectThemes[name];
387
+ if (!theme) {
388
+ throw new Error(`Unknown Tileflow theme: ${name}`);
389
+ }
390
+ const base = resolveThemeBase(theme, name, projectThemes, [...seen, name]);
391
+ return resolveThemeConfig(name, theme, base, true);
392
+ }
393
+ function resolveInlineTheme(theme, projectThemes) {
394
+ const base = resolveThemeBase(theme, "inline", projectThemes, ["inline"]);
395
+ return resolveThemeConfig("inline", theme, base, true);
396
+ }
397
+ function resolveThemeBase(theme, name, projectThemes, seen) {
398
+ if (theme.extends) {
399
+ if (projectThemes?.[theme.extends]) {
400
+ return resolveProjectTheme(theme.extends, projectThemes, seen);
401
+ }
402
+ if (isLegacyTheme(theme.extends)) {
403
+ return resolveLegacyTheme(theme.extends);
404
+ }
405
+ throw new Error(`Unknown Tileflow theme: ${theme.extends}`);
406
+ }
407
+ if (theme.mode === "dark" || name === "dark") {
408
+ return resolveLegacyTheme("dark");
409
+ }
410
+ return resolveLegacyTheme("light");
411
+ }
412
+ function resolveThemeConfig(name, theme, base, custom) {
413
+ const themeColorOverrides = normalizeColorAliases(theme.colors);
414
+ const themeModuleOverrides = mergeThemeModules(
415
+ extractLegacyModuleColors(theme.colors),
416
+ theme.modules
417
+ );
418
+ const colorOverrides = mergeColorConfigs(base.colorOverrides, themeColorOverrides);
419
+ const colors = mergeColorConfigs(base.colors, themeColorOverrides);
420
+ const moduleOverrides = mergeThemeModules(base.moduleOverrides, themeModuleOverrides);
421
+ const modules = mergeThemeModules(base.modules, themeModuleOverrides);
422
+ const mode = theme.mode ?? base.mode;
423
+ const preset = {
424
+ ...base.preset,
425
+ colors
426
+ };
427
+ return {
428
+ colorOverrides,
429
+ colors,
430
+ custom,
431
+ layerOverrides: mergeStyleLayerOverrides(base.layerOverrides, theme.layers),
432
+ mode,
433
+ moduleOverrides,
434
+ modules,
435
+ name,
436
+ preset,
437
+ typography: resolveTypography(resolveThemeTypography(theme), base.typography),
438
+ typographyOverridden: base.typographyOverridden || hasTypographyConfig(theme)
439
+ };
440
+ }
441
+ function isLegacyTheme(value) {
442
+ return value in themePresets;
443
+ }
444
+ function resolveThemeTypography(theme) {
445
+ const font = theme.typography?.font ?? theme.typography?.fontFamily ?? theme.fonts?.labels ?? theme.fonts?.body;
446
+ return {
447
+ ...theme.typography?.weight ? { weight: theme.typography.weight } : {},
448
+ ...font ? { font } : {}
449
+ };
450
+ }
451
+ function hasTypographyConfig(theme) {
452
+ return Boolean(
453
+ theme.typography?.font || theme.typography?.fontFamily || theme.typography?.weight || theme.fonts?.labels || theme.fonts?.body
454
+ );
455
+ }
456
+ function normalizeColorAliases(colors) {
457
+ if (!colors) {
458
+ return {};
459
+ }
460
+ return {
461
+ ...colors,
462
+ background: colors.background ?? colors.canvas,
463
+ park: colors.park ?? colors.greenspace ?? colors.nature,
464
+ greenspace: colors.greenspace ?? colors.park ?? colors.nature,
465
+ roadMajor: colors.roadMajor ?? colors.accent
466
+ };
467
+ }
468
+ function resolveBaseColors(overrides, defaults) {
469
+ const background = overrides.background ?? defaults.background ?? defaults.canvas ?? defaultColors.background;
470
+ const land = overrides.land ?? defaults.land ?? defaultColors.land;
471
+ const water = overrides.water ?? defaults.water ?? defaultColors.water;
472
+ const park = overrides.park ?? overrides.greenspace ?? overrides.nature ?? defaults.park ?? defaults.greenspace ?? defaults.nature ?? defaultColors.park;
473
+ const road = overrides.road ?? defaults.road ?? defaultColors.road;
474
+ const text = overrides.text ?? defaults.text ?? defaultColors.text;
475
+ const textMuted = overrides.textMuted ?? defaults.textMuted ?? mix(text, background, 0.42);
476
+ const textHalo = overrides.textHalo ?? defaults.textHalo ?? background;
477
+ return {
478
+ background,
479
+ boundary: overrides.boundary ?? defaults.boundary ?? mix(text, background, 0.72),
480
+ building: overrides.building ?? defaults.building ?? mix(land, text, 0.08),
481
+ land,
482
+ park,
483
+ road,
484
+ roadMajor: overrides.roadMajor ?? defaults.roadMajor ?? mix(road, textMuted, 0.16),
485
+ text,
486
+ textHalo,
487
+ textMuted,
488
+ water
489
+ };
490
+ }
491
+ function mergeColorConfigs(defaults, overrides = {}) {
492
+ return {
493
+ ...stripLegacyModuleColors(defaults),
494
+ ...stripLegacyModuleColors(overrides)
495
+ };
496
+ }
497
+ function stripLegacyModuleColors(colors) {
498
+ const {
499
+ boundaries: _boundaries,
500
+ buildings: _buildings,
501
+ hydro: _hydro,
502
+ labels: _labels,
503
+ landcover: _landcover,
504
+ landuse: _landuse,
505
+ roads: _roads,
506
+ ...baseColors
507
+ } = colors;
508
+ return baseColors;
509
+ }
510
+ function extractLegacyModuleColors(colors) {
511
+ if (!colors) {
512
+ return {};
513
+ }
514
+ return {
515
+ ...colors.boundaries ? { boundaries: colors.boundaries } : {},
516
+ ...colors.buildings ? { buildings: colors.buildings } : {},
517
+ ...colors.hydro ? { hydro: colors.hydro } : {},
518
+ ...colors.labels ? { labels: colors.labels } : {},
519
+ ...colors.landcover ? { landcover: colors.landcover } : {},
520
+ ...colors.landuse ? { landuse: colors.landuse } : {},
521
+ ...colors.roads ? { roads: colors.roads } : {}
522
+ };
523
+ }
524
+ function mergeThemeModules(defaults = {}, overrides = {}) {
525
+ return {
526
+ ...mergeThemeModuleGroup("boundaries", defaults, overrides),
527
+ ...mergeThemeModuleGroup("buildings", defaults, overrides),
528
+ ...mergeThemeModuleGroup("hydro", defaults, overrides),
529
+ ...mergeThemeModuleGroup("labels", defaults, overrides),
530
+ ...mergeThemeModuleGroup("landcover", defaults, overrides),
531
+ ...mergeThemeModuleGroup("landuse", defaults, overrides),
532
+ ...mergeThemeModuleGroup("roads", defaults, overrides)
533
+ };
534
+ }
535
+ function mergeThemeModuleGroup(key, defaults, overrides) {
536
+ const value = {
537
+ ...isRecord(defaults[key]) ? defaults[key] : {},
538
+ ...isRecord(overrides[key]) ? overrides[key] : {}
539
+ };
540
+ return Object.keys(value).length > 0 ? { [key]: value } : {};
541
+ }
542
+ function mergeStyleLayerOverrides(defaults = {}, overrides = {}) {
543
+ return {
544
+ ...defaults,
545
+ ...overrides
546
+ };
547
+ }
548
+ function isRecord(value) {
549
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
550
+ }
551
+ function resolveGroup(defaults, themeDefaults, overrides) {
552
+ return {
553
+ ...defaults,
554
+ ...themeDefaults,
555
+ ...overrides
556
+ };
557
+ }
558
+ function mapColorRecord(colors) {
559
+ return Object.fromEntries(
560
+ Object.entries(colors).map(([key, value]) => [key, cssColor(value)])
561
+ );
562
+ }
563
+ function alpha(color, opacity) {
564
+ const { a, b, g, r } = hexToRgba(color);
565
+ return `rgba(${r}, ${g}, ${b}, ${roundAlpha(a * opacity)})`;
566
+ }
567
+ function mix(hexA, hexB, amount) {
568
+ const a = hexToRgb(hexA);
569
+ const b = hexToRgb(hexB);
570
+ const r = Math.round(a.r + (b.r - a.r) * amount);
571
+ const g = Math.round(a.g + (b.g - a.g) * amount);
572
+ const bValue = Math.round(a.b + (b.b - a.b) * amount);
573
+ return rgbToHex(r, g, bValue);
574
+ }
575
+ function hexToRgb(hex) {
576
+ const { b, g, r } = hexToRgba(hex);
577
+ return { b, g, r };
578
+ }
579
+ function hexToRgba(hex) {
580
+ const rgbMatch = hex.match(
581
+ /^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)(?:\s*,\s*(\d+(?:\.\d+)?))?\s*\)$/
582
+ );
583
+ if (rgbMatch) {
584
+ return {
585
+ a: roundAlpha(Number(rgbMatch[4] ?? 1)),
586
+ r: Math.round(Number(rgbMatch[1])),
587
+ g: Math.round(Number(rgbMatch[2])),
588
+ b: Math.round(Number(rgbMatch[3]))
589
+ };
590
+ }
591
+ const value = hex.replace("#", "");
592
+ const normalized = value.length === 3 || value.length === 4 ? value.split("").map((character) => character + character).join("") : value;
593
+ return {
594
+ a: normalized.length === 8 ? roundAlpha(Number.parseInt(normalized.slice(6, 8), 16) / 255) : 1,
595
+ r: Number.parseInt(normalized.slice(0, 2), 16),
596
+ g: Number.parseInt(normalized.slice(2, 4), 16),
597
+ b: Number.parseInt(normalized.slice(4, 6), 16)
598
+ };
599
+ }
600
+ function cssColor(hex) {
601
+ const value = hex.replace("#", "");
602
+ if (value.length !== 4 && value.length !== 8) {
603
+ return hex;
604
+ }
605
+ const { a, b, g, r } = hexToRgba(hex);
606
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
607
+ }
608
+ function roundAlpha(value) {
609
+ return Math.round(value * 1e3) / 1e3;
610
+ }
611
+ function rgbToHex(r, g, b) {
612
+ return `#${[r, g, b].map((value) => value.toString(16).padStart(2, "0")).join("")}`;
613
+ }
614
+
615
+ // src/basemaps/osm/layers.ts
616
+ function openMapTilesLayers(source, sourceLayers, colors, options) {
617
+ return [
618
+ {
619
+ id: "background",
620
+ type: "background",
621
+ paint: {
622
+ "background-color": colors.background
623
+ }
624
+ },
625
+ fillLayer("landuse", source, sourceLayers.landuse, colors.landuse.residential, 0.42),
626
+ fillLayer("parks", source, sourceLayers.park, colors.landcover.park, parkOpacity(options.density)),
627
+ fillLayer("water", source, sourceLayers.water, colors.hydro.water, 1),
628
+ ...buildingLayers(source, sourceLayers.building, colors, options.density, options.buildings),
629
+ ...roadLayers(source, sourceLayers.road, colors, options.roads),
630
+ ...boundaryLayers(source, sourceLayers.boundary, colors),
631
+ ...labelLayers(source, sourceLayers.place, colors, options.labels, options.typography),
632
+ ...poiLayers(source, sourceLayers.poi, colors, options.poi, options.typography)
633
+ ];
634
+ }
635
+ function fillLayer(id, source, sourceLayer, color, opacity) {
636
+ return {
637
+ id,
638
+ type: "fill",
639
+ source,
640
+ "source-layer": sourceLayer,
641
+ paint: {
642
+ "fill-color": color,
643
+ "fill-opacity": opacity
644
+ }
645
+ };
646
+ }
647
+ function buildingLayers(source, sourceLayer, colors, density, buildings) {
648
+ if (buildings === "hidden") {
649
+ return [];
650
+ }
651
+ if (buildings === "3d") {
652
+ return [
653
+ {
654
+ id: "buildings-3d",
655
+ type: "fill-extrusion",
656
+ source,
657
+ "source-layer": sourceLayer,
658
+ minzoom: 14,
659
+ filter: ["!=", ["get", "hide_3d"], true],
660
+ paint: {
661
+ "fill-extrusion-base": [
662
+ "case",
663
+ ["has", "render_min_height"],
664
+ ["to-number", ["get", "render_min_height"]],
665
+ ["has", "min_height"],
666
+ ["to-number", ["get", "min_height"]],
667
+ 0
668
+ ],
669
+ "fill-extrusion-color": colors.buildings.extrusion,
670
+ "fill-extrusion-height": [
671
+ "interpolate",
672
+ ["linear"],
673
+ ["zoom"],
674
+ 14,
675
+ 0,
676
+ 15,
677
+ [
678
+ "case",
679
+ ["has", "render_height"],
680
+ ["to-number", ["get", "render_height"]],
681
+ ["has", "height"],
682
+ ["to-number", ["get", "height"]],
683
+ 18
684
+ ]
685
+ ],
686
+ "fill-extrusion-opacity": 0.72
687
+ }
688
+ }
689
+ ];
690
+ }
691
+ if (density === "clean") {
692
+ return [
693
+ {
694
+ id: "buildings-soft",
695
+ type: "fill",
696
+ source,
697
+ "source-layer": sourceLayer,
698
+ minzoom: 14,
699
+ paint: {
700
+ "fill-color": colors.buildings.fill,
701
+ "fill-opacity": 0.38
702
+ }
703
+ }
704
+ ];
705
+ }
706
+ return [
707
+ {
708
+ id: "buildings",
709
+ type: "fill",
710
+ source,
711
+ "source-layer": sourceLayer,
712
+ minzoom: 13,
713
+ paint: {
714
+ "fill-color": colors.buildings.fill,
715
+ "fill-opacity": density === "dense" ? 0.62 : 0.5
716
+ }
717
+ },
718
+ {
719
+ id: "buildings-outline",
720
+ type: "line",
721
+ source,
722
+ "source-layer": sourceLayer,
723
+ minzoom: 15,
724
+ paint: {
725
+ "line-color": colors.buildings.outline,
726
+ "line-opacity": 0.24,
727
+ "line-width": 0.6
728
+ }
729
+ }
730
+ ];
731
+ }
732
+ function boundaryLayers(source, sourceLayer, colors) {
733
+ return [
734
+ {
735
+ id: "boundaries",
736
+ type: "line",
737
+ source,
738
+ "source-layer": sourceLayer,
739
+ minzoom: 2,
740
+ paint: {
741
+ "line-color": colors.boundaries.admin,
742
+ "line-opacity": 0.35,
743
+ "line-width": ["interpolate", ["linear"], ["zoom"], 2, 0.35, 8, 0.8]
744
+ }
745
+ }
746
+ ];
747
+ }
748
+ function labelLayers(source, sourceLayer, colors, labels, typography) {
749
+ if (labels === "none") {
750
+ return [];
751
+ }
752
+ const maxRank = labels === "essential" ? 3 : labels === "balanced" ? 7 : 12;
753
+ return [
754
+ {
755
+ id: "place-labels",
756
+ type: "symbol",
757
+ source,
758
+ "source-layer": sourceLayer,
759
+ minzoom: 3,
760
+ filter: ["<=", ["coalesce", ["get", "rank"], 10], maxRank],
761
+ layout: {
762
+ "text-field": ["coalesce", ["get", "name:en"], ["get", "name"]],
763
+ "text-font": textFont(typography),
764
+ "text-size": ["interpolate", ["linear"], ["zoom"], 3, 10, 8, 13, 14, 17],
765
+ "text-letter-spacing": 0,
766
+ "text-padding": 4
767
+ },
768
+ paint: {
769
+ "text-color": colors.labels.primary,
770
+ "text-halo-color": colors.labels.halo,
771
+ "text-halo-width": 1.2
772
+ }
773
+ }
774
+ ];
775
+ }
776
+ function poiLayers(source, sourceLayer, colors, poi, typography) {
777
+ if (poi === "none") {
778
+ return [];
779
+ }
780
+ const minzoom = poi === "minimal" ? 15 : poi === "balanced" ? 13 : 11;
781
+ return [
782
+ {
783
+ id: "poi-labels",
784
+ type: "symbol",
785
+ source,
786
+ "source-layer": sourceLayer,
787
+ minzoom,
788
+ layout: {
789
+ "text-field": ["coalesce", ["get", "name:en"], ["get", "name"]],
790
+ "text-font": textFont(typography),
791
+ "text-size": ["interpolate", ["linear"], ["zoom"], minzoom, 10, 17, 12],
792
+ "text-letter-spacing": 0,
793
+ "text-padding": 2
794
+ },
795
+ paint: {
796
+ "text-color": colors.labels.poi,
797
+ "text-halo-color": alpha(colors.background, 0.78),
798
+ "text-halo-width": 1
799
+ }
800
+ }
801
+ ];
802
+ }
803
+ function parkOpacity(density) {
804
+ return density === "clean" ? 0.64 : density === "balanced" ? 0.78 : 0.9;
805
+ }
806
+
807
+ // src/modules/style-override/index.ts
808
+ function styleOverride(options = {}) {
809
+ return {
810
+ type: "styleOverride",
811
+ ...options.layers ? { layers: options.layers } : {},
812
+ ...options.removeLayers ? { removeLayers: options.removeLayers } : {}
813
+ };
814
+ }
815
+ function applyStyleOverrides(layers, modules = []) {
816
+ return modules.reduce(applyStyleOverride, layers);
817
+ }
818
+ function applyStyleOverride(layers, moduleConfig) {
819
+ const removeLayers = new Set(moduleConfig.removeLayers ?? []);
820
+ let nextLayers = layers.filter((layer) => !removeLayers.has(String(layer.id)));
821
+ for (const [layerId, override] of Object.entries(moduleConfig.layers ?? {})) {
822
+ nextLayers = applyLayerOverride(nextLayers, layerId, override);
823
+ }
824
+ return nextLayers;
825
+ }
826
+ function applyLayerOverride(layers, layerId, override) {
827
+ const index = layers.findIndex((layer) => layer.id === layerId);
828
+ if (index === -1) {
829
+ const layer = stripControlFields({
830
+ id: layerId,
831
+ ...override
832
+ });
833
+ return insertLayer(layers, layer, override.before);
834
+ }
835
+ const previousLayer = layers[index];
836
+ const patchedLayer = stripControlFields({
837
+ ...previousLayer,
838
+ ...override,
839
+ layout: mergeRecords(previousLayer.layout, override.layout),
840
+ paint: mergeRecords(previousLayer.paint, override.paint),
841
+ metadata: mergeRecords(previousLayer.metadata, override.metadata)
842
+ });
843
+ const nextLayers = [...layers];
844
+ nextLayers[index] = patchedLayer;
845
+ return nextLayers;
846
+ }
847
+ function insertLayer(layers, layer, before) {
848
+ if (!before) {
849
+ return [...layers, layer];
850
+ }
851
+ const index = layers.findIndex((candidate) => candidate.id === before);
852
+ if (index === -1) {
853
+ return [...layers, layer];
854
+ }
855
+ return [...layers.slice(0, index), layer, ...layers.slice(index)];
856
+ }
857
+ function mergeRecords(previous, next) {
858
+ if (!isRecord2(previous) && !isRecord2(next)) {
859
+ return void 0;
860
+ }
861
+ return {
862
+ ...isRecord2(previous) ? previous : {},
863
+ ...isRecord2(next) ? next : {}
864
+ };
865
+ }
866
+ function stripControlFields(layer) {
867
+ const { before, ...styleLayer } = layer;
868
+ return styleLayer;
869
+ }
870
+ function isRecord2(value) {
871
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
872
+ }
873
+
874
+ // src/schema.ts
875
+ import { z } from "zod";
876
+ var legacyThemeNames = /* @__PURE__ */ new Set(["standard", "light", "dark", "minimal", "osm-bright-2"]);
877
+ var identifierSchema = z.string().trim().regex(/^[A-Za-z0-9_-]+$/, {
878
+ message: "Expected letters, numbers, underscores, or hyphens"
879
+ });
880
+ var hexColorSchema = z.string().regex(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, {
881
+ message: "Expected a hex color like #F6F7F3 or #F6F7F3FF"
882
+ });
883
+ function colorGroupSchema(keys) {
884
+ return z.object(Object.fromEntries(keys.map((key) => [key, hexColorSchema.optional()]))).strict();
885
+ }
886
+ var colorConfigSchema = z.object({
887
+ accent: hexColorSchema.optional(),
888
+ background: hexColorSchema.optional(),
889
+ boundary: hexColorSchema.optional(),
890
+ building: hexColorSchema.optional(),
891
+ canvas: hexColorSchema.optional(),
892
+ greenspace: hexColorSchema.optional(),
893
+ land: hexColorSchema.optional(),
894
+ nature: hexColorSchema.optional(),
895
+ park: hexColorSchema.optional(),
896
+ road: hexColorSchema.optional(),
897
+ roadCasing: hexColorSchema.optional(),
898
+ roadMajor: hexColorSchema.optional(),
899
+ text: hexColorSchema.optional(),
900
+ textHalo: hexColorSchema.optional(),
901
+ textMuted: hexColorSchema.optional(),
902
+ water: hexColorSchema.optional(),
903
+ boundaries: colorGroupSchema(["admin", "disputed", "major", "maritime"]).optional(),
904
+ buildings: colorGroupSchema(["extrusion", "fill", "outline"]).optional(),
905
+ hydro: colorGroupSchema(["ferry", "label", "water", "waterway"]).optional(),
906
+ labels: colorGroupSchema([
907
+ "country",
908
+ "halo",
909
+ "muted",
910
+ "poi",
911
+ "primary",
912
+ "road",
913
+ "settlement",
914
+ "water"
915
+ ]).optional(),
916
+ landcover: colorGroupSchema(["grass", "ice", "park", "protected", "sand", "wood"]).optional(),
917
+ landuse: colorGroupSchema([
918
+ "cemetery",
919
+ "civic",
920
+ "commercial",
921
+ "industrial",
922
+ "residential"
923
+ ]).optional(),
924
+ roads: colorGroupSchema([
925
+ "bridge",
926
+ "casing",
927
+ "ferry",
928
+ "minor",
929
+ "motorway",
930
+ "path",
931
+ "primary",
932
+ "rail",
933
+ "secondary",
934
+ "trunk",
935
+ "tunnel"
936
+ ]).optional()
937
+ }).strict();
938
+ var themeModulesSchema = z.object({
939
+ boundaries: colorGroupSchema(["admin", "disputed", "major", "maritime"]).optional(),
940
+ buildings: colorGroupSchema(["extrusion", "fill", "outline"]).optional(),
941
+ hydro: colorGroupSchema(["ferry", "label", "water", "waterway"]).optional(),
942
+ labels: colorGroupSchema([
943
+ "country",
944
+ "halo",
945
+ "muted",
946
+ "poi",
947
+ "primary",
948
+ "road",
949
+ "settlement",
950
+ "water"
951
+ ]).optional(),
952
+ landcover: colorGroupSchema(["grass", "ice", "park", "protected", "sand", "wood"]).optional(),
953
+ landuse: colorGroupSchema([
954
+ "cemetery",
955
+ "civic",
956
+ "commercial",
957
+ "industrial",
958
+ "residential"
959
+ ]).optional(),
960
+ roads: colorGroupSchema([
961
+ "bridge",
962
+ "casing",
963
+ "ferry",
964
+ "minor",
965
+ "motorway",
966
+ "path",
967
+ "primary",
968
+ "rail",
969
+ "secondary",
970
+ "trunk",
971
+ "tunnel"
972
+ ]).optional()
973
+ }).strict();
974
+ var styleLayerOverrideSchema = z.object({
975
+ before: z.string().min(1).optional(),
976
+ filter: z.unknown().optional(),
977
+ layout: z.record(z.string(), z.unknown()).optional(),
978
+ maxzoom: z.number().finite().min(0).max(24).optional(),
979
+ metadata: z.record(z.string(), z.unknown()).optional(),
980
+ minzoom: z.number().finite().min(0).max(24).optional(),
981
+ paint: z.record(z.string(), z.unknown()).optional(),
982
+ source: z.string().min(1).optional(),
983
+ "source-layer": z.string().min(1).optional(),
984
+ type: z.string().min(1).optional()
985
+ }).catchall(z.unknown());
986
+ var typographySchema = z.object({
987
+ font: z.string().trim().min(1).optional(),
988
+ fontFamily: z.string().trim().min(1).optional(),
989
+ weight: z.enum(["regular", "medium", "semibold", "bold"]).optional()
990
+ }).strict();
991
+ var fontsSchema = z.object({
992
+ body: z.string().trim().min(1).optional(),
993
+ labels: z.string().trim().min(1).optional()
994
+ }).strict();
995
+ var tileflowThemeSchema = z.object({
996
+ colors: colorConfigSchema.optional(),
997
+ extends: z.string().trim().min(1).optional(),
998
+ fonts: fontsSchema.optional(),
999
+ layers: z.record(z.string(), styleLayerOverrideSchema).optional(),
1000
+ mode: z.enum(["light", "dark"]).optional(),
1001
+ modules: themeModulesSchema.optional(),
1002
+ typography: typographySchema.optional()
1003
+ }).strict();
1004
+ var themeConfigSchema = tileflowThemeSchema;
1005
+ var sourceLayersSchema = z.object({
1006
+ landuse: z.string().optional(),
1007
+ park: z.string().optional(),
1008
+ water: z.string().optional(),
1009
+ road: z.string().optional(),
1010
+ building: z.string().optional(),
1011
+ boundary: z.string().optional(),
1012
+ place: z.string().optional(),
1013
+ poi: z.string().optional()
1014
+ }).strict();
1015
+ var basemapSchema = z.object({
1016
+ type: z.literal("osm"),
1017
+ attribution: z.string().optional(),
1018
+ glyphs: z.string().min(1).optional(),
1019
+ sourceId: z.string().min(1).optional(),
1020
+ sourceLayers: sourceLayersSchema.optional(),
1021
+ sprite: z.string().min(1).optional(),
1022
+ tileset: z.string().trim().min(1).optional(),
1023
+ url: z.url().optional()
1024
+ }).strict();
1025
+ var roadsModuleSchema = z.object({
1026
+ type: z.literal("roads"),
1027
+ density: z.enum(["clean", "balanced", "dense"]).optional(),
1028
+ preset: z.enum(["modern", "minimal"]).optional()
1029
+ }).strict();
1030
+ var styleOverrideModuleSchema = z.object({
1031
+ type: z.literal("styleOverride"),
1032
+ layers: z.record(z.string(), styleLayerOverrideSchema).optional(),
1033
+ removeLayers: z.array(z.string().min(1)).optional()
1034
+ }).strict();
1035
+ var moduleSchema = z.discriminatedUnion("type", [roadsModuleSchema, styleOverrideModuleSchema]);
1036
+ var viewSchema = z.object({
1037
+ bearing: z.number().finite().min(-180).max(180).optional(),
1038
+ center: z.tuple([z.number().finite(), z.number().finite()]).refine(([lng, lat]) => lng >= -180 && lng <= 180 && lat >= -90 && lat <= 90, {
1039
+ message: "Expected center as [lng, lat] within world bounds"
1040
+ }).optional(),
1041
+ zoom: z.number().finite().min(0).max(24).optional()
1042
+ }).strict();
1043
+ var terrainConfigSchema = z.object({
1044
+ attribution: z.string().optional(),
1045
+ encoding: z.enum(["mapbox", "terrarium"]).optional(),
1046
+ exaggeration: z.number().min(0).max(10).optional(),
1047
+ mode: z.enum(["none", "hillshade", "3d"]).optional(),
1048
+ sourceId: z.string().min(1).optional(),
1049
+ tileset: z.string().trim().min(1).optional(),
1050
+ url: z.url().optional()
1051
+ }).strict();
1052
+ var tilesSchema = z.object({
1053
+ sourceId: z.string().min(1).optional(),
1054
+ tileset: z.string().trim().min(1).optional(),
1055
+ url: z.url().optional(),
1056
+ attribution: z.string().optional(),
1057
+ sourceLayers: sourceLayersSchema.optional()
1058
+ }).strict();
1059
+ var tileflowMapSchema = z.object({
1060
+ basemap: basemapSchema.optional(),
1061
+ name: z.string().min(1).optional(),
1062
+ tileset: z.string().trim().min(1).optional(),
1063
+ theme: z.union([z.string().trim().min(1), themeConfigSchema]).optional(),
1064
+ modules: z.array(moduleSchema).optional(),
1065
+ layers: z.record(z.string(), styleLayerOverrideSchema).optional(),
1066
+ colors: colorConfigSchema.optional(),
1067
+ typography: typographySchema.optional(),
1068
+ density: z.enum(["clean", "balanced", "dense"]).optional(),
1069
+ roads: z.enum(["hidden", "soft", "standard", "detailed"]).optional(),
1070
+ labels: z.enum(["none", "essential", "balanced", "full"]).optional(),
1071
+ poi: z.enum(["none", "minimal", "balanced", "full"]).optional(),
1072
+ buildings: z.enum(["hidden", "flat", "3d"]).optional(),
1073
+ terrain: z.union([z.enum(["none", "hillshade", "3d"]), terrainConfigSchema]).optional(),
1074
+ glyphs: z.string().min(1).optional(),
1075
+ sprite: z.string().min(1).optional(),
1076
+ tiles: tilesSchema.optional(),
1077
+ view: viewSchema.optional(),
1078
+ allowedOrigins: z.array(z.string().trim().min(1).max(253)).max(20).optional()
1079
+ }).strict();
1080
+ var configSchema = tileflowMapSchema;
1081
+ var tilesetConfigSchema = z.object({
1082
+ id: z.string().trim().min(1).optional(),
1083
+ name: z.string().trim().min(1).optional(),
1084
+ attribution: z.string().optional(),
1085
+ sourceLayers: sourceLayersSchema.optional()
1086
+ }).strict();
1087
+ var tileflowProjectSchema = z.object({
1088
+ themes: z.record(identifierSchema, themeConfigSchema).optional(),
1089
+ tilesets: z.record(identifierSchema, tilesetConfigSchema).optional(),
1090
+ maps: z.record(identifierSchema, configSchema).refine((maps) => {
1091
+ return Object.keys(maps).length > 0;
1092
+ }, "Expected at least one map")
1093
+ }).strict();
1094
+ var projectConfigSchema = tileflowProjectSchema;
1095
+ function parseTileflowMap(input) {
1096
+ const parsed = configSchema.parse(input);
1097
+ const messages = validateMapThemeReference(parsed, void 0, "theme");
1098
+ if (messages.length > 0) {
1099
+ throw new Error(messages.map((message) => `${message.path}: ${message.message}`).join("; "));
1100
+ }
1101
+ return parsed;
1102
+ }
1103
+ function parseTileflowProject(input) {
1104
+ const parsed = projectConfigSchema.parse(input);
1105
+ const messages = validateProjectThemeReferences(parsed);
1106
+ if (messages.length > 0) {
1107
+ throw new Error(messages.map((message) => `${message.path}: ${message.message}`).join("; "));
1108
+ }
1109
+ return parsed;
1110
+ }
1111
+ function validateConfig(input) {
1112
+ if (isProjectConfigLike(input)) {
1113
+ const parsed2 = projectConfigSchema.safeParse(input);
1114
+ if (parsed2.success) {
1115
+ const messages = validateProjectThemeReferences(parsed2.data);
1116
+ if (messages.length > 0) {
1117
+ return { valid: false, messages };
1118
+ }
1119
+ return { valid: true, messages: [] };
1120
+ }
1121
+ return {
1122
+ valid: false,
1123
+ messages: parsed2.error.issues.map((issue) => ({
1124
+ level: "error",
1125
+ path: issue.path.join(".") || "config",
1126
+ message: issue.message
1127
+ }))
1128
+ };
1129
+ }
1130
+ const parsed = configSchema.safeParse(input);
1131
+ if (parsed.success) {
1132
+ const messages = validateMapThemeReference(parsed.data, void 0, "theme");
1133
+ if (messages.length > 0) {
1134
+ return { valid: false, messages };
1135
+ }
1136
+ return { valid: true, messages: [] };
1137
+ }
1138
+ return {
1139
+ valid: false,
1140
+ messages: parsed.error.issues.map((issue) => ({
1141
+ level: "error",
1142
+ path: issue.path.join(".") || "config",
1143
+ message: issue.message
1144
+ }))
1145
+ };
1146
+ }
1147
+ var validateTileflowConfig = validateConfig;
1148
+ function isProjectConfigLike(input) {
1149
+ return Boolean(
1150
+ input && typeof input === "object" && "maps" in input && typeof input.maps === "object"
1151
+ );
1152
+ }
1153
+ function validateProjectThemeReferences(project) {
1154
+ const themeNames = new Set(Object.keys(project.themes ?? {}));
1155
+ const themeMessages = validateThemeExtends(project.themes, themeNames);
1156
+ const mapMessages = Object.entries(project.maps).flatMap(
1157
+ ([mapName, mapConfig]) => validateMapThemeReference(mapConfig, themeNames, `maps.${mapName}.theme`)
1158
+ );
1159
+ return [...themeMessages, ...mapMessages];
1160
+ }
1161
+ function validateMapThemeReference(config, themeNames, path) {
1162
+ if (!config.theme) {
1163
+ return [];
1164
+ }
1165
+ if (typeof config.theme === "string") {
1166
+ if (legacyThemeNames.has(config.theme) || themeNames?.has(config.theme)) {
1167
+ return [];
1168
+ }
1169
+ return [
1170
+ {
1171
+ level: "error",
1172
+ path,
1173
+ message: `Unknown Tileflow theme "${config.theme}"`
1174
+ }
1175
+ ];
1176
+ }
1177
+ if (!config.theme.extends) {
1178
+ return [];
1179
+ }
1180
+ if (legacyThemeNames.has(config.theme.extends) || themeNames?.has(config.theme.extends)) {
1181
+ return [];
1182
+ }
1183
+ return [
1184
+ {
1185
+ level: "error",
1186
+ path: `${path}.extends`,
1187
+ message: `Unknown Tileflow theme "${config.theme.extends}"`
1188
+ }
1189
+ ];
1190
+ }
1191
+ function validateThemeExtends(themes, themeNames) {
1192
+ const messages = [];
1193
+ for (const [themeName, theme] of Object.entries(themes ?? {})) {
1194
+ if (theme.extends && !legacyThemeNames.has(theme.extends) && !themeNames.has(theme.extends)) {
1195
+ messages.push({
1196
+ level: "error",
1197
+ path: `themes.${themeName}.extends`,
1198
+ message: `Unknown Tileflow theme "${theme.extends}"`
1199
+ });
1200
+ }
1201
+ }
1202
+ for (const themeName of themeNames) {
1203
+ const cycle = findThemeCycle(themeName, themes, []);
1204
+ if (cycle) {
1205
+ messages.push({
1206
+ level: "error",
1207
+ path: `themes.${themeName}.extends`,
1208
+ message: `Circular Tileflow theme extends: ${cycle.join(" -> ")}`
1209
+ });
1210
+ }
1211
+ }
1212
+ return messages;
1213
+ }
1214
+ function findThemeCycle(themeName, themes, path) {
1215
+ if (path.includes(themeName)) {
1216
+ return [...path, themeName];
1217
+ }
1218
+ const nextThemeName = themes?.[themeName]?.extends;
1219
+ if (!nextThemeName || legacyThemeNames.has(nextThemeName)) {
1220
+ return void 0;
1221
+ }
1222
+ return findThemeCycle(nextThemeName, themes, [...path, themeName]);
1223
+ }
1224
+
1225
+ // src/templates/osm-bright/style.json
1226
+ var style_default = {
1227
+ version: 8,
1228
+ name: "Bright",
1229
+ metadata: {
1230
+ "mapbox:autocomposite": false,
1231
+ "mapbox:groups": {
1232
+ "1444849242106.713": { collapsed: false, name: "Places" },
1233
+ "1444849334699.1902": { collapsed: true, name: "Bridges" },
1234
+ "1444849345966.4436": { collapsed: false, name: "Roads" },
1235
+ "1444849354174.1904": { collapsed: true, name: "Tunnels" },
1236
+ "1444849364238.8171": { collapsed: false, name: "Buildings" },
1237
+ "1444849382550.77": { collapsed: false, name: "Water" },
1238
+ "1444849388993.3071": { collapsed: false, name: "Land" }
1239
+ },
1240
+ "mapbox:type": "template",
1241
+ "openmaptiles:mapbox:owner": "openmaptiles",
1242
+ "openmaptiles:mapbox:source:url": "mapbox://openmaptiles.4qljc88t",
1243
+ "openmaptiles:version": "3.x"
1244
+ },
1245
+ center: [0, 0],
1246
+ zoom: 1,
1247
+ bearing: 0,
1248
+ pitch: 0,
1249
+ sources: {
1250
+ openmaptiles: {
1251
+ type: "vector",
1252
+ url: "https://api.maptiler.com/tiles/v3-openmaptiles/tiles.json?key={key}"
1253
+ }
1254
+ },
1255
+ sprite: "https://openmaptiles.github.io/osm-bright-gl-style/sprite",
1256
+ glyphs: "https://api.maptiler.com/fonts/{fontstack}/{range}.pbf?key={key}",
1257
+ layers: [
1258
+ {
1259
+ id: "background",
1260
+ type: "background",
1261
+ paint: { "background-color": "#f8f4f0" }
1262
+ },
1263
+ {
1264
+ id: "landcover-glacier",
1265
+ type: "fill",
1266
+ metadata: { "mapbox:group": "1444849388993.3071" },
1267
+ source: "openmaptiles",
1268
+ "source-layer": "landcover",
1269
+ filter: ["==", "subclass", "glacier"],
1270
+ layout: { visibility: "visible" },
1271
+ paint: {
1272
+ "fill-color": "#fff",
1273
+ "fill-opacity": { base: 1, stops: [[0, 0.9], [10, 0.3]] }
1274
+ }
1275
+ },
1276
+ {
1277
+ id: "landuse-residential",
1278
+ type: "fill",
1279
+ metadata: { "mapbox:group": "1444849388993.3071" },
1280
+ source: "openmaptiles",
1281
+ "source-layer": "landuse",
1282
+ filter: [
1283
+ "all",
1284
+ ["in", "class", "residential", "suburb", "neighbourhood"]
1285
+ ],
1286
+ layout: { visibility: "visible" },
1287
+ paint: {
1288
+ "fill-color": {
1289
+ base: 1,
1290
+ stops: [
1291
+ [12, "hsla(30, 19%, 90%, 0.4)"],
1292
+ [16, "hsla(30, 19%, 90%, 0.2)"]
1293
+ ]
1294
+ }
1295
+ }
1296
+ },
1297
+ {
1298
+ id: "landuse-commercial",
1299
+ type: "fill",
1300
+ metadata: { "mapbox:group": "1444849388993.3071" },
1301
+ source: "openmaptiles",
1302
+ "source-layer": "landuse",
1303
+ filter: [
1304
+ "all",
1305
+ ["==", "$type", "Polygon"],
1306
+ ["==", "class", "commercial"]
1307
+ ],
1308
+ layout: { visibility: "visible" },
1309
+ paint: { "fill-color": "hsla(0, 60%, 87%, 0.23)" }
1310
+ },
1311
+ {
1312
+ id: "landuse-industrial",
1313
+ type: "fill",
1314
+ source: "openmaptiles",
1315
+ "source-layer": "landuse",
1316
+ filter: [
1317
+ "all",
1318
+ ["==", "$type", "Polygon"],
1319
+ ["in", "class", "industrial", "garages", "dam"]
1320
+ ],
1321
+ layout: { visibility: "visible" },
1322
+ paint: { "fill-color": "hsla(49, 100%, 88%, 0.34)" }
1323
+ },
1324
+ {
1325
+ id: "landuse-cemetery",
1326
+ type: "fill",
1327
+ metadata: { "mapbox:group": "1444849388993.3071" },
1328
+ source: "openmaptiles",
1329
+ "source-layer": "landuse",
1330
+ filter: ["==", "class", "cemetery"],
1331
+ paint: { "fill-color": "#e0e4dd" }
1332
+ },
1333
+ {
1334
+ id: "landuse-hospital",
1335
+ type: "fill",
1336
+ metadata: { "mapbox:group": "1444849388993.3071" },
1337
+ source: "openmaptiles",
1338
+ "source-layer": "landuse",
1339
+ filter: ["==", "class", "hospital"],
1340
+ paint: { "fill-color": "#fde" }
1341
+ },
1342
+ {
1343
+ id: "landuse-school",
1344
+ type: "fill",
1345
+ metadata: { "mapbox:group": "1444849388993.3071" },
1346
+ source: "openmaptiles",
1347
+ "source-layer": "landuse",
1348
+ filter: ["==", "class", "school"],
1349
+ paint: { "fill-color": "#f0e8f8" }
1350
+ },
1351
+ {
1352
+ id: "landuse-railway",
1353
+ type: "fill",
1354
+ metadata: { "mapbox:group": "1444849388993.3071" },
1355
+ source: "openmaptiles",
1356
+ "source-layer": "landuse",
1357
+ filter: ["==", "class", "railway"],
1358
+ layout: { visibility: "visible" },
1359
+ paint: { "fill-color": "hsla(30, 19%, 90%, 0.4)" }
1360
+ },
1361
+ {
1362
+ id: "landcover-wood",
1363
+ type: "fill",
1364
+ metadata: { "mapbox:group": "1444849388993.3071" },
1365
+ source: "openmaptiles",
1366
+ "source-layer": "landcover",
1367
+ filter: ["==", "class", "wood"],
1368
+ paint: {
1369
+ "fill-antialias": { base: 1, stops: [[0, false], [9, true]] },
1370
+ "fill-color": "#6a4",
1371
+ "fill-opacity": 0.1,
1372
+ "fill-outline-color": "hsla(0, 0%, 0%, 0.03)"
1373
+ }
1374
+ },
1375
+ {
1376
+ id: "landcover-grass",
1377
+ type: "fill",
1378
+ metadata: { "mapbox:group": "1444849388993.3071" },
1379
+ source: "openmaptiles",
1380
+ "source-layer": "landcover",
1381
+ filter: ["==", "class", "grass"],
1382
+ paint: { "fill-color": "#d8e8c8", "fill-opacity": 1 }
1383
+ },
1384
+ {
1385
+ id: "landcover-grass-park",
1386
+ type: "fill",
1387
+ metadata: { "mapbox:group": "1444849388993.3071" },
1388
+ source: "openmaptiles",
1389
+ "source-layer": "park",
1390
+ filter: ["==", "class", "public_park"],
1391
+ paint: { "fill-color": "#d8e8c8", "fill-opacity": 0.8 }
1392
+ },
1393
+ {
1394
+ id: "waterway_tunnel",
1395
+ type: "line",
1396
+ source: "openmaptiles",
1397
+ "source-layer": "waterway",
1398
+ minzoom: 14,
1399
+ filter: [
1400
+ "all",
1401
+ ["in", "class", "river", "stream", "canal"],
1402
+ ["==", "brunnel", "tunnel"]
1403
+ ],
1404
+ layout: { "line-cap": "round", visibility: "visible" },
1405
+ paint: {
1406
+ "line-color": "#a0c8f0",
1407
+ "line-dasharray": [2, 4],
1408
+ "line-width": { base: 1.3, stops: [[13, 0.5], [20, 6]] }
1409
+ }
1410
+ },
1411
+ {
1412
+ id: "waterway-other",
1413
+ type: "line",
1414
+ metadata: { "mapbox:group": "1444849382550.77" },
1415
+ source: "openmaptiles",
1416
+ "source-layer": "waterway",
1417
+ filter: [
1418
+ "all",
1419
+ ["!in", "class", "canal", "river", "stream"],
1420
+ ["==", "intermittent", 0]
197
1421
  ],
198
1422
  layout: { "line-cap": "round", visibility: "visible" },
199
1423
  paint: {
@@ -2307,1064 +3531,285 @@ var style_default = {
2307
3531
  layout: {
2308
3532
  "text-field": "{name:latin}\n{name:nonlatin}",
2309
3533
  "text-font": ["Noto Sans Bold"],
2310
- "text-letter-spacing": 0.1,
2311
- "text-max-width": 9,
2312
- "text-size": { base: 1.2, stops: [[12, 10], [15, 14]] },
2313
- "text-transform": "uppercase",
2314
- visibility: "visible"
2315
- },
2316
- paint: {
2317
- "text-color": "#633",
2318
- "text-halo-color": "rgba(255,255,255,0.8)",
2319
- "text-halo-width": 1.2
2320
- }
2321
- },
2322
- {
2323
- id: "place-village",
2324
- type: "symbol",
2325
- metadata: { "mapbox:group": "1444849242106.713" },
2326
- source: "openmaptiles",
2327
- "source-layer": "place",
2328
- filter: ["==", "class", "village"],
2329
- layout: {
2330
- "text-field": "{name:latin}\n{name:nonlatin}",
2331
- "text-font": ["Noto Sans Regular"],
2332
- "text-max-width": 8,
2333
- "text-size": { base: 1.2, stops: [[10, 12], [15, 22]] },
2334
- visibility: "visible"
2335
- },
2336
- paint: {
2337
- "text-color": "#333",
2338
- "text-halo-color": "rgba(255,255,255,0.8)",
2339
- "text-halo-width": 1.2
2340
- }
2341
- },
2342
- {
2343
- id: "place-town",
2344
- type: "symbol",
2345
- metadata: { "mapbox:group": "1444849242106.713" },
2346
- source: "openmaptiles",
2347
- "source-layer": "place",
2348
- filter: ["==", "class", "town"],
2349
- layout: {
2350
- "text-field": "{name:latin}\n{name:nonlatin}",
2351
- "text-font": ["Noto Sans Regular"],
2352
- "text-max-width": 8,
2353
- "text-size": { base: 1.2, stops: [[10, 14], [15, 24]] },
2354
- visibility: "visible"
2355
- },
2356
- paint: {
2357
- "text-color": "#333",
2358
- "text-halo-color": "rgba(255,255,255,0.8)",
2359
- "text-halo-width": 1.2
2360
- }
2361
- },
2362
- {
2363
- id: "place-city",
2364
- type: "symbol",
2365
- metadata: { "mapbox:group": "1444849242106.713" },
2366
- source: "openmaptiles",
2367
- "source-layer": "place",
2368
- filter: ["all", ["!=", "capital", 2], ["==", "class", "city"]],
2369
- layout: {
2370
- "text-field": "{name:latin}\n{name:nonlatin}",
2371
- "text-font": ["Noto Sans Regular"],
2372
- "text-max-width": 8,
2373
- "text-size": { base: 1.2, stops: [[7, 14], [11, 24]] },
2374
- visibility: "visible"
2375
- },
2376
- paint: {
2377
- "text-color": "#333",
2378
- "text-halo-color": "rgba(255,255,255,0.8)",
2379
- "text-halo-width": 1.2
2380
- }
2381
- },
2382
- {
2383
- id: "place-city-capital",
2384
- type: "symbol",
2385
- metadata: { "mapbox:group": "1444849242106.713" },
2386
- source: "openmaptiles",
2387
- "source-layer": "place",
2388
- filter: ["all", ["==", "capital", 2], ["==", "class", "city"]],
2389
- layout: {
2390
- "icon-image": "star_11",
2391
- "icon-size": 0.8,
2392
- "text-anchor": "left",
2393
- "text-field": "{name:latin}\n{name:nonlatin}",
2394
- "text-font": ["Noto Sans Regular"],
2395
- "text-max-width": 8,
2396
- "text-offset": [0.4, 0],
2397
- "text-size": { base: 1.2, stops: [[7, 14], [11, 24]] },
2398
- visibility: "visible"
2399
- },
2400
- paint: {
2401
- "text-color": "#333",
2402
- "text-halo-color": "rgba(255,255,255,0.8)",
2403
- "text-halo-width": 1.2
2404
- }
2405
- },
2406
- {
2407
- id: "place-state",
2408
- type: "symbol",
2409
- metadata: { "mapbox:group": "1444849242106.713" },
2410
- source: "openmaptiles",
2411
- "source-layer": "place",
2412
- filter: ["in", "class", "state"],
2413
- layout: {
2414
- "text-field": "{name:latin}",
2415
- "text-font": ["Noto Sans Bold"],
2416
- "text-letter-spacing": 0.1,
2417
- "text-max-width": 9,
2418
- "text-size": { base: 1.2, stops: [[12, 10], [15, 14]] },
2419
- "text-transform": "uppercase",
2420
- visibility: "visible"
2421
- },
2422
- paint: {
2423
- "text-color": "#633",
2424
- "text-halo-color": "rgba(255,255,255,0.8)",
2425
- "text-halo-width": 1.2
2426
- }
2427
- },
2428
- {
2429
- id: "place-country-other",
2430
- type: "symbol",
2431
- metadata: { "mapbox:group": "1444849242106.713" },
2432
- source: "openmaptiles",
2433
- "source-layer": "place",
2434
- filter: [
2435
- "all",
2436
- ["==", "class", "country"],
2437
- [">=", "rank", 3],
2438
- ["!has", "iso_a2"]
2439
- ],
2440
- layout: {
2441
- "text-field": "{name:latin}",
2442
- "text-font": ["Noto Sans Italic"],
2443
- "text-max-width": 6.25,
2444
- "text-size": { stops: [[3, 11], [7, 17]] },
2445
- "text-transform": "uppercase",
2446
- visibility: "visible"
2447
- },
2448
- paint: {
2449
- "text-color": "#334",
2450
- "text-halo-blur": 1,
2451
- "text-halo-color": "rgba(255,255,255,0.8)",
2452
- "text-halo-width": 2
2453
- }
2454
- },
2455
- {
2456
- id: "place-country-3",
2457
- type: "symbol",
2458
- metadata: { "mapbox:group": "1444849242106.713" },
2459
- source: "openmaptiles",
2460
- "source-layer": "place",
2461
- filter: [
2462
- "all",
2463
- ["==", "class", "country"],
2464
- [">=", "rank", 3],
2465
- ["has", "iso_a2"]
2466
- ],
2467
- layout: {
2468
- "text-field": "{name:latin}",
2469
- "text-font": ["Noto Sans Bold"],
2470
- "text-max-width": 6.25,
2471
- "text-size": { stops: [[3, 11], [7, 17]] },
2472
- "text-transform": "uppercase",
2473
- visibility: "visible"
2474
- },
2475
- paint: {
2476
- "text-color": "#334",
2477
- "text-halo-blur": 1,
2478
- "text-halo-color": "rgba(255,255,255,0.8)",
2479
- "text-halo-width": 2
2480
- }
2481
- },
2482
- {
2483
- id: "place-country-2",
2484
- type: "symbol",
2485
- metadata: { "mapbox:group": "1444849242106.713" },
2486
- source: "openmaptiles",
2487
- "source-layer": "place",
2488
- filter: [
2489
- "all",
2490
- ["==", "class", "country"],
2491
- ["==", "rank", 2],
2492
- ["has", "iso_a2"]
2493
- ],
2494
- layout: {
2495
- "text-field": "{name:latin}",
2496
- "text-font": ["Noto Sans Bold"],
2497
- "text-max-width": 6.25,
2498
- "text-size": { stops: [[2, 11], [5, 17]] },
2499
- "text-transform": "uppercase",
2500
- visibility: "visible"
2501
- },
2502
- paint: {
2503
- "text-color": "#334",
2504
- "text-halo-blur": 1,
2505
- "text-halo-color": "rgba(255,255,255,0.8)",
2506
- "text-halo-width": 2
2507
- }
2508
- },
2509
- {
2510
- id: "place-country-1",
2511
- type: "symbol",
2512
- metadata: { "mapbox:group": "1444849242106.713" },
2513
- source: "openmaptiles",
2514
- "source-layer": "place",
2515
- filter: [
2516
- "all",
2517
- ["==", "class", "country"],
2518
- ["==", "rank", 1],
2519
- ["has", "iso_a2"]
2520
- ],
2521
- layout: {
2522
- "text-field": "{name:latin}",
2523
- "text-font": ["Noto Sans Bold"],
2524
- "text-max-width": 6.25,
2525
- "text-size": { stops: [[1, 11], [4, 17]] },
3534
+ "text-letter-spacing": 0.1,
3535
+ "text-max-width": 9,
3536
+ "text-size": { base: 1.2, stops: [[12, 10], [15, 14]] },
2526
3537
  "text-transform": "uppercase",
2527
3538
  visibility: "visible"
2528
3539
  },
2529
3540
  paint: {
2530
- "text-color": "#334",
2531
- "text-halo-blur": 1,
3541
+ "text-color": "#633",
2532
3542
  "text-halo-color": "rgba(255,255,255,0.8)",
2533
- "text-halo-width": 2
3543
+ "text-halo-width": 1.2
2534
3544
  }
2535
3545
  },
2536
3546
  {
2537
- id: "place-continent",
3547
+ id: "place-village",
2538
3548
  type: "symbol",
2539
3549
  metadata: { "mapbox:group": "1444849242106.713" },
2540
3550
  source: "openmaptiles",
2541
3551
  "source-layer": "place",
2542
- maxzoom: 1,
2543
- filter: ["==", "class", "continent"],
3552
+ filter: ["==", "class", "village"],
2544
3553
  layout: {
2545
- "text-field": "{name:latin}",
2546
- "text-font": ["Noto Sans Bold"],
2547
- "text-max-width": 6.25,
2548
- "text-size": 14,
2549
- "text-transform": "uppercase",
2550
- visibility: "visible"
2551
- },
2552
- paint: {
2553
- "text-color": "#334",
2554
- "text-halo-blur": 1,
2555
- "text-halo-color": "rgba(255,255,255,0.8)",
2556
- "text-halo-width": 2
2557
- }
2558
- }
2559
- ],
2560
- id: "bright"
2561
- };
2562
-
2563
- // src/basemaps.ts
2564
- function osm(options = {}) {
2565
- return {
2566
- type: "osm",
2567
- ...options
2568
- };
2569
- }
2570
-
2571
- // src/schema.ts
2572
- import { z } from "zod";
2573
- var legacyThemeNames = /* @__PURE__ */ new Set(["standard", "light", "dark", "minimal", "osm-bright-2"]);
2574
- var hexColorSchema = z.string().regex(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, {
2575
- message: "Expected a hex color like #F6F7F3 or #F6F7F3FF"
2576
- });
2577
- var colorConfigSchema = z.object({
2578
- canvas: hexColorSchema.optional(),
2579
- land: hexColorSchema.optional(),
2580
- background: hexColorSchema.optional(),
2581
- water: hexColorSchema.optional(),
2582
- nature: hexColorSchema.optional(),
2583
- text: hexColorSchema.optional(),
2584
- accent: hexColorSchema.optional(),
2585
- park: hexColorSchema.optional(),
2586
- road: hexColorSchema.optional(),
2587
- roadCasing: hexColorSchema.optional(),
2588
- building: hexColorSchema.optional()
2589
- });
2590
- var typographySchema = z.object({
2591
- font: z.string().trim().min(1).optional(),
2592
- weight: z.enum(["regular", "medium", "semibold", "bold"]).optional()
2593
- });
2594
- var fontsSchema = z.object({
2595
- body: z.string().trim().min(1).optional(),
2596
- labels: z.string().trim().min(1).optional()
2597
- }).strict();
2598
- var themeConfigSchema = z.object({
2599
- colors: colorConfigSchema.optional(),
2600
- fonts: fontsSchema.optional(),
2601
- typography: typographySchema.optional()
2602
- }).strict();
2603
- var sourceLayersSchema = z.object({
2604
- landuse: z.string().optional(),
2605
- park: z.string().optional(),
2606
- water: z.string().optional(),
2607
- road: z.string().optional(),
2608
- building: z.string().optional(),
2609
- boundary: z.string().optional(),
2610
- place: z.string().optional(),
2611
- poi: z.string().optional()
2612
- });
2613
- var basemapSchema = z.object({
2614
- type: z.literal("osm"),
2615
- attribution: z.string().optional(),
2616
- glyphs: z.string().min(1).optional(),
2617
- sourceId: z.string().min(1).optional(),
2618
- sourceLayers: sourceLayersSchema.optional(),
2619
- sprite: z.string().min(1).optional(),
2620
- tileset: z.string().trim().min(1).optional(),
2621
- url: z.url().optional()
2622
- });
2623
- var roadsModuleSchema = z.object({
2624
- type: z.literal("roads"),
2625
- density: z.enum(["clean", "balanced", "dense"]).optional(),
2626
- preset: z.enum(["modern", "minimal"]).optional()
2627
- });
2628
- var moduleSchema = z.discriminatedUnion("type", [roadsModuleSchema]);
2629
- var viewSchema = z.object({
2630
- bearing: z.number().finite().min(-180).max(180).optional(),
2631
- center: z.tuple([z.number().finite(), z.number().finite()]).refine(([lng, lat]) => lng >= -180 && lng <= 180 && lat >= -90 && lat <= 90, {
2632
- message: "Expected center as [lng, lat] within world bounds"
2633
- }).optional(),
2634
- zoom: z.number().finite().min(0).max(24).optional()
2635
- });
2636
- var configSchema = z.object({
2637
- basemap: basemapSchema.optional(),
2638
- name: z.string().min(1).optional(),
2639
- tileset: z.string().trim().min(1).optional(),
2640
- theme: z.union([z.string().trim().min(1), themeConfigSchema]).optional(),
2641
- modules: z.array(moduleSchema).optional(),
2642
- colors: colorConfigSchema.optional(),
2643
- typography: typographySchema.optional(),
2644
- density: z.enum(["clean", "balanced", "dense"]).optional(),
2645
- roads: z.enum(["hidden", "soft", "standard", "detailed"]).optional(),
2646
- labels: z.enum(["none", "essential", "balanced", "full"]).optional(),
2647
- poi: z.enum(["none", "minimal", "balanced", "full"]).optional(),
2648
- buildings: z.enum(["hidden", "flat", "3d"]).optional(),
2649
- terrain: z.union([
2650
- z.enum(["none", "hillshade", "3d"]),
2651
- z.object({
2652
- attribution: z.string().optional(),
2653
- encoding: z.enum(["mapbox", "terrarium"]).optional(),
2654
- exaggeration: z.number().min(0).max(10).optional(),
2655
- mode: z.enum(["none", "hillshade", "3d"]).optional(),
2656
- sourceId: z.string().min(1).optional(),
2657
- tileset: z.string().trim().min(1).optional(),
2658
- url: z.url().optional()
2659
- })
2660
- ]).optional(),
2661
- glyphs: z.string().min(1).optional(),
2662
- sprite: z.string().min(1).optional(),
2663
- tiles: z.object({
2664
- sourceId: z.string().min(1).optional(),
2665
- tileset: z.string().trim().min(1).optional(),
2666
- url: z.url().optional(),
2667
- attribution: z.string().optional(),
2668
- sourceLayers: sourceLayersSchema.optional()
2669
- }).optional(),
2670
- view: viewSchema.optional(),
2671
- allowedOrigins: z.array(z.string().trim().min(1).max(253)).max(20).optional()
2672
- });
2673
- var tilesetConfigSchema = z.object({
2674
- id: z.string().trim().min(1).optional(),
2675
- name: z.string().trim().min(1).optional(),
2676
- attribution: z.string().optional(),
2677
- sourceLayers: sourceLayersSchema.optional()
2678
- });
2679
- var projectConfigSchema = z.object({
2680
- themes: z.object({
2681
- light: themeConfigSchema.optional(),
2682
- dark: themeConfigSchema.optional()
2683
- }).strict().optional(),
2684
- tilesets: z.record(z.string(), tilesetConfigSchema).optional(),
2685
- maps: z.record(z.string(), configSchema).refine((maps) => {
2686
- return Object.keys(maps).length > 0;
2687
- }, "Expected at least one map")
2688
- });
2689
- function validateConfig(input) {
2690
- if (isProjectConfigLike(input)) {
2691
- const parsed2 = projectConfigSchema.safeParse(input);
2692
- if (parsed2.success) {
2693
- const messages = validateProjectThemeReferences(parsed2.data);
2694
- if (messages.length > 0) {
2695
- return { valid: false, messages };
2696
- }
2697
- return { valid: true, messages: [] };
2698
- }
2699
- return {
2700
- valid: false,
2701
- messages: parsed2.error.issues.map((issue) => ({
2702
- level: "error",
2703
- path: issue.path.join(".") || "config",
2704
- message: issue.message
2705
- }))
2706
- };
2707
- }
2708
- const parsed = configSchema.safeParse(input);
2709
- if (parsed.success) {
2710
- const messages = validateMapThemeReference(parsed.data, void 0, "theme");
2711
- if (messages.length > 0) {
2712
- return { valid: false, messages };
2713
- }
2714
- return { valid: true, messages: [] };
2715
- }
2716
- return {
2717
- valid: false,
2718
- messages: parsed.error.issues.map((issue) => ({
2719
- level: "error",
2720
- path: issue.path.join(".") || "config",
2721
- message: issue.message
2722
- }))
2723
- };
2724
- }
2725
- function isProjectConfigLike(input) {
2726
- return Boolean(
2727
- input && typeof input === "object" && "maps" in input && typeof input.maps === "object"
2728
- );
2729
- }
2730
- function validateProjectThemeReferences(project) {
2731
- const themeNames = new Set(Object.keys(project.themes ?? {}));
2732
- return Object.entries(project.maps).flatMap(
2733
- ([mapName, mapConfig]) => validateMapThemeReference(mapConfig, themeNames, `maps.${mapName}.theme`)
2734
- );
2735
- }
2736
- function validateMapThemeReference(config, themeNames, path) {
2737
- if (typeof config.theme !== "string") {
2738
- return [];
2739
- }
2740
- if (legacyThemeNames.has(config.theme) || themeNames?.has(config.theme)) {
2741
- return [];
2742
- }
2743
- return [
2744
- {
2745
- level: "error",
2746
- path,
2747
- message: `Unknown Tileflow theme "${config.theme}"`
2748
- }
2749
- ];
2750
- }
2751
-
2752
- // src/compiler.ts
2753
- var defaultSourceLayers = {
2754
- landuse: "landuse",
2755
- park: "park",
2756
- water: "water",
2757
- road: "transportation",
2758
- building: "building",
2759
- boundary: "boundary",
2760
- place: "place",
2761
- poi: "poi"
2762
- };
2763
- var defaultColors = {
2764
- canvas: "#F6F7F3",
2765
- land: "#F1F3ED",
2766
- water: "#A9D3F5",
2767
- nature: "#CDE8B5",
2768
- road: "#FFFFFF",
2769
- text: "#3C4043",
2770
- accent: "#F4C95D"
2771
- };
2772
- var defaultConfig = {
2773
- density: "balanced",
2774
- roads: "standard",
2775
- labels: "balanced",
2776
- poi: "minimal"
2777
- };
2778
- var themePresets = {
2779
- standard: {
2780
- colors: defaultColors,
2781
- density: "balanced",
2782
- labels: "balanced",
2783
- poi: "minimal",
2784
- roads: "standard"
2785
- },
2786
- light: {
2787
- colors: {
2788
- canvas: "#F9FAF8",
2789
- land: "#F6F7F3",
2790
- water: "#C5E1F5",
2791
- nature: "#DDEED2",
2792
- road: "#FFFFFF",
2793
- building: "#ECEAE4",
2794
- text: "#5F6368",
2795
- accent: "#C9D1D9"
2796
- },
2797
- density: "clean",
2798
- labels: "essential",
2799
- poi: "minimal",
2800
- roads: "standard"
2801
- },
2802
- dark: {
2803
- colors: {
2804
- canvas: "#1C2228",
2805
- land: "#232B32",
2806
- water: "#18384D",
2807
- nature: "#274230",
2808
- road: "#39434D",
2809
- building: "#2F363D",
2810
- text: "#D7DEE6",
2811
- accent: "#E4B857"
2812
- },
2813
- density: "balanced",
2814
- labels: "balanced",
2815
- poi: "minimal",
2816
- roads: "standard"
2817
- },
2818
- minimal: {
2819
- colors: {
2820
- canvas: "#FAFAF8",
2821
- land: "#F7F7F2",
2822
- water: "#D6EAF6",
2823
- nature: "#E5EEDB",
2824
- road: "#FFFFFF",
2825
- building: "#F0EEE8",
2826
- text: "#697079",
2827
- accent: "#B8C0CA"
3554
+ "text-field": "{name:latin}\n{name:nonlatin}",
3555
+ "text-font": ["Noto Sans Regular"],
3556
+ "text-max-width": 8,
3557
+ "text-size": { base: 1.2, stops: [[10, 12], [15, 22]] },
3558
+ visibility: "visible"
3559
+ },
3560
+ paint: {
3561
+ "text-color": "#333",
3562
+ "text-halo-color": "rgba(255,255,255,0.8)",
3563
+ "text-halo-width": 1.2
3564
+ }
2828
3565
  },
2829
- density: "clean",
2830
- labels: "essential",
2831
- poi: "none",
2832
- roads: "soft"
2833
- },
2834
- // "OSM Bright 2" — Google Maps-inspired palette. Warm-neutral land, soft
2835
- // blue water, muted green parks, white roads on a light gray canvas, and a
2836
- // warm yellow accent that drives the highway color once the class-based road
2837
- // engine lands. Tuned to read as a familiar navigation basemap.
2838
- "osm-bright-2": {
2839
- colors: {
2840
- canvas: "#EBE9E4",
2841
- land: "#F5F4F1",
2842
- water: "#AADAFF",
2843
- nature: "#C7E6A8",
2844
- road: "#FFFFFF",
2845
- building: "#EFEDE7",
2846
- text: "#5F6368",
2847
- accent: "#F9C97C"
3566
+ {
3567
+ id: "place-town",
3568
+ type: "symbol",
3569
+ metadata: { "mapbox:group": "1444849242106.713" },
3570
+ source: "openmaptiles",
3571
+ "source-layer": "place",
3572
+ filter: ["==", "class", "town"],
3573
+ layout: {
3574
+ "text-field": "{name:latin}\n{name:nonlatin}",
3575
+ "text-font": ["Noto Sans Regular"],
3576
+ "text-max-width": 8,
3577
+ "text-size": { base: 1.2, stops: [[10, 14], [15, 24]] },
3578
+ visibility: "visible"
3579
+ },
3580
+ paint: {
3581
+ "text-color": "#333",
3582
+ "text-halo-color": "rgba(255,255,255,0.8)",
3583
+ "text-halo-width": 1.2
3584
+ }
2848
3585
  },
2849
- density: "balanced",
2850
- labels: "balanced",
2851
- poi: "balanced",
2852
- roads: "detailed"
2853
- }
2854
- };
2855
- var defaultTypography = {
2856
- font: "Noto Sans",
2857
- weight: "regular"
2858
- };
2859
- var fontWeightLabels = {
2860
- regular: "Regular",
2861
- medium: "Medium",
2862
- semibold: "Semibold",
2863
- bold: "Bold"
2864
- };
2865
- var defaultTileBaseUrl = "https://api.tileflow.dev";
2866
- var defaultTileset = "world";
2867
- function createStyle(input = {}, options = {}) {
2868
- const parsed = configSchema.safeParse(input);
2869
- if (!parsed.success) {
2870
- const message = parsed.error.issues.map((issue) => `${issue.path.join(".") || "config"}: ${issue.message}`).join("; ");
2871
- throw new Error(`Invalid Tileflow config. ${message}`);
2872
- }
2873
- const config = parsed.data;
2874
- const basemap = config.basemap ?? osm();
2875
- const theme = resolveTheme(config.theme, options.themes);
2876
- const density = config.density ?? theme.preset.density ?? defaultConfig.density;
2877
- const roadModule = config.modules?.find(
2878
- (moduleConfig) => moduleConfig.type === "roads"
2879
- );
2880
- const roads2 = resolveRoads(config.roads, roadModule, theme.preset.roads ?? defaultConfig.roads);
2881
- const labels = config.labels ?? theme.preset.labels ?? defaultConfig.labels;
2882
- const poi = config.poi ?? theme.preset.poi ?? defaultConfig.poi;
2883
- const buildings = config.buildings ?? "flat";
2884
- const colors = resolveColors(config.colors, theme.colors);
2885
- const sourceId = config.tiles?.sourceId ?? basemap.sourceId ?? "tileflow";
2886
- const tileBaseUrl = normalizeBaseUrl(options.tileBaseUrl);
2887
- const tileset = options.tileset ?? config.tileset ?? config.tiles?.tileset ?? basemap.tileset ?? defaultTileset;
2888
- const tilesUrl = config.tiles?.url ?? basemap.url ?? `${tileBaseUrl}/tiles/${tileset}/tiles.json`;
2889
- const typography = resolveTypography(config.typography, theme.typography);
2890
- const terrain = resolveTerrain(config.terrain, tileBaseUrl);
2891
- const glyphs = config.glyphs ?? basemap.glyphs ?? `${tileBaseUrl}/fonts/{fontstack}/{range}.pbf`;
2892
- const sprite = config.sprite ?? basemap.sprite;
2893
- const attribution = config.tiles?.attribution ?? basemap.attribution ?? "\xA9 OpenMapTiles \xA9 OpenStreetMap contributors";
2894
- const metadata = {
2895
- "tileflow:basemap": basemap.type,
2896
- "tileflow:theme": theme.name,
2897
- "tileflow:themeMode": theme.mode,
2898
- "tileflow:density": density,
2899
- "tileflow:roads": roads2,
2900
- "tileflow:labels": labels,
2901
- "tileflow:poi": poi,
2902
- "tileflow:buildings": buildings,
2903
- "tileflow:modules": config.modules?.map((moduleConfig) => moduleConfig.type) ?? [],
2904
- "tileflow:terrain": terrain ? {
2905
- encoding: terrain.source.encoding,
2906
- mode: terrain.mode,
2907
- sourceId: terrain.sourceId
2908
- } : void 0,
2909
- "tileflow:typography": typography,
2910
- "tileflow:colors": colors,
2911
- "tileflow:tileBaseUrl": tileBaseUrl,
2912
- "tileflow:tileset": tileset,
2913
- "tileflow:tiles": "tileflow",
2914
- "tileflow:view": config.view
2915
- };
2916
- if (shouldUseOsmBrightTemplate(config, basemap, theme)) {
2917
- return createOsmBrightStyle({
2918
- attribution,
2919
- colors,
2920
- glyphs,
2921
- metadata,
2922
- name: config.name ?? "OSM Bright",
2923
- sourceId,
2924
- sprite,
2925
- terrain,
2926
- theme,
2927
- tilesUrl,
2928
- typography
2929
- });
2930
- }
2931
- const sourceLayers = resolveSourceLayers(
2932
3586
  {
2933
- ...defaultSourceLayers,
2934
- ...basemap.sourceLayers,
2935
- ...config.tiles?.sourceLayers
3587
+ id: "place-city",
3588
+ type: "symbol",
3589
+ metadata: { "mapbox:group": "1444849242106.713" },
3590
+ source: "openmaptiles",
3591
+ "source-layer": "place",
3592
+ filter: ["all", ["!=", "capital", 2], ["==", "class", "city"]],
3593
+ layout: {
3594
+ "text-field": "{name:latin}\n{name:nonlatin}",
3595
+ "text-font": ["Noto Sans Regular"],
3596
+ "text-max-width": 8,
3597
+ "text-size": { base: 1.2, stops: [[7, 14], [11, 24]] },
3598
+ visibility: "visible"
3599
+ },
3600
+ paint: {
3601
+ "text-color": "#333",
3602
+ "text-halo-color": "rgba(255,255,255,0.8)",
3603
+ "text-halo-width": 1.2
3604
+ }
2936
3605
  },
2937
- void 0
2938
- );
2939
- const layers = openMapTilesLayers(sourceId, sourceLayers, colors, {
2940
- buildings,
2941
- density,
2942
- labels,
2943
- poi,
2944
- roads: roads2,
2945
- typography
2946
- });
2947
- const resolvedLayers = terrain ? withTerrainLayers(layers, terrain) : layers;
2948
- const style = {
2949
- version: 8,
2950
- name: config.name ?? "Tileflow",
2951
- glyphs,
2952
- ...sprite ? { sprite } : {},
2953
- sources: {
2954
- [sourceId]: {
2955
- type: "vector",
2956
- url: tilesUrl,
2957
- attribution
3606
+ {
3607
+ id: "place-city-capital",
3608
+ type: "symbol",
3609
+ metadata: { "mapbox:group": "1444849242106.713" },
3610
+ source: "openmaptiles",
3611
+ "source-layer": "place",
3612
+ filter: ["all", ["==", "capital", 2], ["==", "class", "city"]],
3613
+ layout: {
3614
+ "icon-image": "star_11",
3615
+ "icon-size": 0.8,
3616
+ "text-anchor": "left",
3617
+ "text-field": "{name:latin}\n{name:nonlatin}",
3618
+ "text-font": ["Noto Sans Regular"],
3619
+ "text-max-width": 8,
3620
+ "text-offset": [0.4, 0],
3621
+ "text-size": { base: 1.2, stops: [[7, 14], [11, 24]] },
3622
+ visibility: "visible"
2958
3623
  },
2959
- ...terrain ? {
2960
- [terrain.sourceId]: terrain.source
2961
- } : {}
3624
+ paint: {
3625
+ "text-color": "#333",
3626
+ "text-halo-color": "rgba(255,255,255,0.8)",
3627
+ "text-halo-width": 1.2
3628
+ }
2962
3629
  },
2963
- layers: resolvedLayers,
2964
- ...terrain?.mode === "3d" ? {
2965
- terrain: {
2966
- exaggeration: terrain.exaggeration,
2967
- source: terrain.sourceId
3630
+ {
3631
+ id: "place-state",
3632
+ type: "symbol",
3633
+ metadata: { "mapbox:group": "1444849242106.713" },
3634
+ source: "openmaptiles",
3635
+ "source-layer": "place",
3636
+ filter: ["in", "class", "state"],
3637
+ layout: {
3638
+ "text-field": "{name:latin}",
3639
+ "text-font": ["Noto Sans Bold"],
3640
+ "text-letter-spacing": 0.1,
3641
+ "text-max-width": 9,
3642
+ "text-size": { base: 1.2, stops: [[12, 10], [15, 14]] },
3643
+ "text-transform": "uppercase",
3644
+ visibility: "visible"
3645
+ },
3646
+ paint: {
3647
+ "text-color": "#633",
3648
+ "text-halo-color": "rgba(255,255,255,0.8)",
3649
+ "text-halo-width": 1.2
2968
3650
  }
2969
- } : {},
2970
- metadata
2971
- };
2972
- return style;
2973
- }
2974
- function createStyleFromProject(projectConfig, mapName, options = {}) {
2975
- const config = projectConfig.maps[mapName];
2976
- if (!config) {
2977
- throw new Error(`Unknown Tileflow map: ${mapName}`);
2978
- }
2979
- const tilesetName = options.tileset ?? config.tileset ?? config.tiles?.tileset ?? config.basemap?.tileset;
2980
- const tilesetConfig = tilesetName ? projectConfig.tilesets?.[tilesetName] : void 0;
2981
- const resolvedTileset = tilesetConfig?.id ?? tilesetName ?? defaultTileset;
2982
- const baseBasemap = config.basemap ?? osm();
2983
- const { sourceLayers: tileSourceLayers, ...tileOptions } = config.tiles ?? {};
2984
- const resolvedBasemapSourceLayers = baseBasemap.sourceLayers || tilesetConfig?.sourceLayers ? resolveSourceLayers(baseBasemap.sourceLayers, tilesetConfig?.sourceLayers) : void 0;
2985
- const resolvedTileSourceLayers = baseBasemap.sourceLayers || !tileSourceLayers ? void 0 : resolveSourceLayers(tileSourceLayers, tilesetConfig?.sourceLayers);
2986
- return createStyle(
3651
+ },
2987
3652
  {
2988
- ...config,
2989
- tileset: resolvedTileset,
2990
- basemap: {
2991
- ...baseBasemap,
2992
- tileset: resolvedTileset,
2993
- attribution: baseBasemap.attribution ?? tilesetConfig?.attribution,
2994
- ...resolvedBasemapSourceLayers ? { sourceLayers: resolvedBasemapSourceLayers } : {}
3653
+ id: "place-country-other",
3654
+ type: "symbol",
3655
+ metadata: { "mapbox:group": "1444849242106.713" },
3656
+ source: "openmaptiles",
3657
+ "source-layer": "place",
3658
+ filter: [
3659
+ "all",
3660
+ ["==", "class", "country"],
3661
+ [">=", "rank", 3],
3662
+ ["!has", "iso_a2"]
3663
+ ],
3664
+ layout: {
3665
+ "text-field": "{name:latin}",
3666
+ "text-font": ["Noto Sans Italic"],
3667
+ "text-max-width": 6.25,
3668
+ "text-size": { stops: [[3, 11], [7, 17]] },
3669
+ "text-transform": "uppercase",
3670
+ visibility: "visible"
2995
3671
  },
2996
- tiles: {
2997
- ...tileOptions,
2998
- tileset: resolvedTileset,
2999
- ...resolvedTileSourceLayers ? { sourceLayers: resolvedTileSourceLayers } : {}
3672
+ paint: {
3673
+ "text-color": "#334",
3674
+ "text-halo-blur": 1,
3675
+ "text-halo-color": "rgba(255,255,255,0.8)",
3676
+ "text-halo-width": 2
3000
3677
  }
3001
3678
  },
3002
3679
  {
3003
- ...options,
3004
- themes: projectConfig.themes,
3005
- tileset: resolvedTileset
3006
- }
3007
- );
3008
- }
3009
- function createManifest(projectConfig, options = {}) {
3010
- const styleBaseUrl = normalizeStyleBaseUrl(options.styleBaseUrl);
3011
- const tileBaseUrl = normalizeBaseUrl(options.tileBaseUrl);
3012
- const maps = Object.keys(projectConfig.maps);
3013
- return {
3014
- version: 1,
3015
- tileBaseUrl,
3016
- styles: Object.fromEntries(
3017
- maps.map((mapName) => [mapName, `${styleBaseUrl}/styles/${mapName}.json`])
3018
- ),
3019
- maps: Object.fromEntries(
3020
- maps.map((mapName) => [mapName, `${styleBaseUrl}/styles/${mapName}.json`])
3021
- )
3022
- };
3023
- }
3024
- function getDefaultColors() {
3025
- return defaultColors;
3026
- }
3027
- function resolveTheme(theme, projectThemes) {
3028
- if (!theme) {
3029
- return resolveLegacyTheme("light");
3030
- }
3031
- if (typeof theme === "string") {
3032
- const projectTheme = isProjectThemeName(theme) ? projectThemes?.[theme] : void 0;
3033
- if (projectTheme) {
3034
- return resolveThemeConfig(theme, projectTheme);
3035
- }
3036
- if (isLegacyTheme(theme)) {
3037
- return resolveLegacyTheme(theme);
3038
- }
3039
- throw new Error(`Unknown Tileflow theme: ${theme}`);
3040
- }
3041
- return resolveThemeConfig("inline", theme);
3042
- }
3043
- function resolveLegacyTheme(theme) {
3044
- const preset = themePresets[theme];
3045
- return {
3046
- colors: preset.colors,
3047
- custom: false,
3048
- mode: theme === "dark" ? "dark" : "light",
3049
- name: theme,
3050
- preset,
3051
- typography: defaultTypography
3052
- };
3053
- }
3054
- function resolveThemeConfig(name, theme) {
3055
- const colors = normalizeColorAliases(theme.colors);
3056
- const themeName = isProjectThemeName(name) ? name : "light";
3057
- const preset = themePresets[themeName];
3058
- return {
3059
- colors: {
3060
- ...preset.colors,
3061
- ...colors
3062
- },
3063
- custom: true,
3064
- mode: themeName,
3065
- name,
3066
- preset,
3067
- typography: resolveTypography(resolveThemeTypography(theme), defaultTypography)
3068
- };
3069
- }
3070
- function isLegacyTheme(value) {
3071
- return value in themePresets;
3072
- }
3073
- function isProjectThemeName(value) {
3074
- return value === "light" || value === "dark";
3075
- }
3076
- function resolveThemeTypography(theme) {
3077
- return {
3078
- ...theme.typography,
3079
- font: theme.typography?.font ?? theme.fonts?.labels ?? theme.fonts?.body
3080
- };
3081
- }
3082
- function normalizeColorAliases(colors) {
3083
- if (!colors) {
3084
- return {};
3085
- }
3086
- return {
3087
- ...colors,
3088
- canvas: colors.canvas ?? colors.background,
3089
- nature: colors.nature ?? colors.park
3090
- };
3091
- }
3092
- function resolveRoads(legacyRoads, moduleConfig, fallback) {
3093
- if (!moduleConfig) {
3094
- return legacyRoads ?? fallback;
3095
- }
3096
- if (moduleConfig.preset === "minimal" || moduleConfig.density === "clean") {
3097
- return "soft";
3098
- }
3099
- if (moduleConfig.density === "dense") {
3100
- return "detailed";
3101
- }
3102
- return "standard";
3103
- }
3104
- function resolveColors(overrides = {}, defaults = defaultColors) {
3105
- const colors = {
3106
- ...defaults,
3107
- canvas: overrides.canvas ?? overrides.background ?? defaults.canvas ?? defaultColors.canvas,
3108
- land: overrides.land ?? defaults.land ?? defaultColors.land,
3109
- water: overrides.water ?? defaults.water ?? defaultColors.water,
3110
- nature: overrides.nature ?? overrides.park ?? defaults.nature ?? defaults.park ?? defaultColors.nature,
3111
- road: overrides.road ?? defaults.road ?? defaultColors.road,
3112
- text: overrides.text ?? defaults.text ?? defaultColors.text,
3113
- accent: overrides.accent ?? defaults.accent ?? defaultColors.accent,
3114
- building: overrides.building ?? defaults.building,
3115
- roadCasing: overrides.roadCasing ?? defaults.roadCasing
3116
- };
3117
- const building = colors.building ?? mix(colors.land, colors.text, 0.08);
3118
- const roadCasing = colors.roadCasing ?? mix(colors.road, colors.text, 0.2);
3119
- return {
3120
- canvas: cssColor(colors.canvas),
3121
- land: cssColor(colors.land),
3122
- water: cssColor(colors.water),
3123
- nature: cssColor(colors.nature),
3124
- road: cssColor(colors.road),
3125
- text: cssColor(colors.text),
3126
- accent: cssColor(colors.accent),
3127
- building: cssColor(building),
3128
- roadCasing: cssColor(roadCasing),
3129
- roadMajor: mix(colors.road, colors.accent, 0.16),
3130
- roadMinor: cssColor(colors.road),
3131
- textHalo: alpha(colors.canvas, 0.84),
3132
- textMuted: mix(colors.text, colors.canvas, 0.42)
3133
- };
3134
- }
3135
- function resolveTerrain(terrain, tileBaseUrl) {
3136
- if (!terrain || terrain === "none") {
3137
- return void 0;
3138
- }
3139
- const terrainConfig = typeof terrain === "string" ? { mode: terrain } : terrain;
3140
- const mode = terrainConfig.mode ?? "hillshade";
3141
- if (mode === "none") {
3142
- return void 0;
3143
- }
3144
- const sourceId = terrainConfig.sourceId ?? "tileflow-terrain";
3145
- const tileset = terrainConfig.tileset ?? "terrain";
3146
- const url = terrainConfig.url ?? `${tileBaseUrl}/tiles/${tileset}/tiles.json`;
3147
- const encoding = terrainConfig.encoding ?? "terrarium";
3148
- return {
3149
- exaggeration: terrainConfig.exaggeration ?? 1.2,
3150
- mode,
3151
- sourceId,
3152
- source: {
3153
- attribution: terrainConfig.attribution,
3154
- encoding,
3155
- type: "raster-dem",
3156
- url
3157
- }
3158
- };
3159
- }
3160
- function resolveTypography(overrides = {}, defaults = defaultTypography) {
3161
- return {
3162
- ...defaults,
3163
- ...overrides
3164
- };
3165
- }
3166
- function resolveSourceLayers(mapLayers, tilesetLayers) {
3167
- return {
3168
- ...defaultSourceLayers,
3169
- ...tilesetLayers,
3170
- ...mapLayers
3171
- };
3172
- }
3173
- function shouldUseOsmBrightTemplate(config, basemap, theme) {
3174
- const templateTheme = theme.custom || theme.name === "light" || theme.name === "dark";
3175
- return basemap.type === "osm" && templateTheme && !config.colors && !config.typography && !config.density && !config.roads && !config.labels && !config.poi && !config.buildings && !config.modules?.length && !basemap.sourceLayers && !config.tiles?.sourceLayers;
3176
- }
3177
- function createOsmBrightStyle(options) {
3178
- const template = cloneJson(style_default);
3179
- const layers = template.layers.map((layer) => remapOsmBrightSource(layer, options.sourceId));
3180
- const themedLayers = applyOsmBrightTheme(
3181
- layers,
3182
- options.colors,
3183
- options.theme,
3184
- options.typography
3185
- );
3186
- const resolvedLayers = options.terrain ? withTerrainLayers(themedLayers, options.terrain) : themedLayers;
3187
- return {
3188
- ...template,
3189
- name: options.name,
3190
- glyphs: options.glyphs,
3191
- ...options.sprite || template.sprite ? { sprite: options.sprite ?? template.sprite } : {},
3192
- sources: {
3193
- [options.sourceId]: {
3194
- type: "vector",
3195
- url: options.tilesUrl,
3196
- attribution: options.attribution
3680
+ id: "place-country-3",
3681
+ type: "symbol",
3682
+ metadata: { "mapbox:group": "1444849242106.713" },
3683
+ source: "openmaptiles",
3684
+ "source-layer": "place",
3685
+ filter: [
3686
+ "all",
3687
+ ["==", "class", "country"],
3688
+ [">=", "rank", 3],
3689
+ ["has", "iso_a2"]
3690
+ ],
3691
+ layout: {
3692
+ "text-field": "{name:latin}",
3693
+ "text-font": ["Noto Sans Bold"],
3694
+ "text-max-width": 6.25,
3695
+ "text-size": { stops: [[3, 11], [7, 17]] },
3696
+ "text-transform": "uppercase",
3697
+ visibility: "visible"
3197
3698
  },
3198
- ...options.terrain ? {
3199
- [options.terrain.sourceId]: options.terrain.source
3200
- } : {}
3201
- },
3202
- layers: resolvedLayers,
3203
- ...options.terrain?.mode === "3d" ? {
3204
- terrain: {
3205
- exaggeration: options.terrain.exaggeration,
3206
- source: options.terrain.sourceId
3207
- }
3208
- } : {},
3209
- metadata: {
3210
- ...template.metadata ?? {},
3211
- ...options.metadata,
3212
- "tileflow:template": "openmaptiles-osm-bright"
3213
- }
3214
- };
3215
- }
3216
- function cloneJson(value) {
3217
- return JSON.parse(JSON.stringify(value));
3218
- }
3219
- function remapOsmBrightSource(layer, sourceId) {
3220
- if (layer.source !== "openmaptiles") {
3221
- return layer;
3222
- }
3223
- return {
3224
- ...layer,
3225
- source: sourceId
3226
- };
3227
- }
3228
- function applyOsmBrightTheme(layers, colors, theme, typography) {
3229
- if (!theme.custom && theme.name === "light") {
3230
- return layers;
3231
- }
3232
- return layers.map((layer) => {
3233
- const type = stringValue(layer.type);
3234
- const sourceLayer = stringValue(layer["source-layer"]);
3235
- const id = stringValue(layer.id);
3236
- const nextLayer = { ...layer };
3237
- const paint = isRecord(layer.paint) ? { ...layer.paint } : {};
3238
- const layout = isRecord(layer.layout) ? { ...layer.layout } : {};
3239
- let paintChanged = false;
3240
- let layoutChanged = false;
3241
- if (type === "background") {
3242
- paint["background-color"] = colors.canvas;
3243
- paintChanged = true;
3244
- }
3245
- if (type === "fill") {
3246
- const fillColor = osmBrightFillColor(id, sourceLayer, colors);
3247
- if (fillColor) {
3248
- paint["fill-color"] = fillColor;
3249
- paintChanged = true;
3250
- }
3251
- }
3252
- if (type === "line") {
3253
- const lineColor = osmBrightLineColor(id, sourceLayer, colors);
3254
- if (lineColor) {
3255
- paint["line-color"] = lineColor;
3256
- paintChanged = true;
3699
+ paint: {
3700
+ "text-color": "#334",
3701
+ "text-halo-blur": 1,
3702
+ "text-halo-color": "rgba(255,255,255,0.8)",
3703
+ "text-halo-width": 2
3257
3704
  }
3258
- }
3259
- if (type === "symbol") {
3260
- const textColor = osmBrightTextColor(id, sourceLayer, colors);
3261
- if (textColor && ("text-color" in paint || id.includes("label") || id.includes("name"))) {
3262
- paint["text-color"] = textColor;
3263
- paintChanged = true;
3705
+ },
3706
+ {
3707
+ id: "place-country-2",
3708
+ type: "symbol",
3709
+ metadata: { "mapbox:group": "1444849242106.713" },
3710
+ source: "openmaptiles",
3711
+ "source-layer": "place",
3712
+ filter: [
3713
+ "all",
3714
+ ["==", "class", "country"],
3715
+ ["==", "rank", 2],
3716
+ ["has", "iso_a2"]
3717
+ ],
3718
+ layout: {
3719
+ "text-field": "{name:latin}",
3720
+ "text-font": ["Noto Sans Bold"],
3721
+ "text-max-width": 6.25,
3722
+ "text-size": { stops: [[2, 11], [5, 17]] },
3723
+ "text-transform": "uppercase",
3724
+ visibility: "visible"
3725
+ },
3726
+ paint: {
3727
+ "text-color": "#334",
3728
+ "text-halo-blur": 1,
3729
+ "text-halo-color": "rgba(255,255,255,0.8)",
3730
+ "text-halo-width": 2
3264
3731
  }
3265
- if ("text-halo-color" in paint) {
3266
- paint["text-halo-color"] = colors.textHalo;
3267
- paintChanged = true;
3732
+ },
3733
+ {
3734
+ id: "place-country-1",
3735
+ type: "symbol",
3736
+ metadata: { "mapbox:group": "1444849242106.713" },
3737
+ source: "openmaptiles",
3738
+ "source-layer": "place",
3739
+ filter: [
3740
+ "all",
3741
+ ["==", "class", "country"],
3742
+ ["==", "rank", 1],
3743
+ ["has", "iso_a2"]
3744
+ ],
3745
+ layout: {
3746
+ "text-field": "{name:latin}",
3747
+ "text-font": ["Noto Sans Bold"],
3748
+ "text-max-width": 6.25,
3749
+ "text-size": { stops: [[1, 11], [4, 17]] },
3750
+ "text-transform": "uppercase",
3751
+ visibility: "visible"
3752
+ },
3753
+ paint: {
3754
+ "text-color": "#334",
3755
+ "text-halo-blur": 1,
3756
+ "text-halo-color": "rgba(255,255,255,0.8)",
3757
+ "text-halo-width": 2
3268
3758
  }
3269
- if (theme.custom && "text-font" in layout) {
3270
- layout["text-font"] = textFont(typography);
3271
- layoutChanged = true;
3759
+ },
3760
+ {
3761
+ id: "place-continent",
3762
+ type: "symbol",
3763
+ metadata: { "mapbox:group": "1444849242106.713" },
3764
+ source: "openmaptiles",
3765
+ "source-layer": "place",
3766
+ maxzoom: 1,
3767
+ filter: ["==", "class", "continent"],
3768
+ layout: {
3769
+ "text-field": "{name:latin}",
3770
+ "text-font": ["Noto Sans Bold"],
3771
+ "text-max-width": 6.25,
3772
+ "text-size": 14,
3773
+ "text-transform": "uppercase",
3774
+ visibility: "visible"
3775
+ },
3776
+ paint: {
3777
+ "text-color": "#334",
3778
+ "text-halo-blur": 1,
3779
+ "text-halo-color": "rgba(255,255,255,0.8)",
3780
+ "text-halo-width": 2
3272
3781
  }
3273
3782
  }
3274
- if (paintChanged) {
3275
- nextLayer.paint = paint;
3276
- }
3277
- if (layoutChanged) {
3278
- nextLayer.layout = layout;
3279
- }
3280
- return nextLayer;
3281
- });
3282
- }
3283
- function osmBrightFillColor(id, sourceLayer, colors) {
3284
- if (sourceLayer === "water" || id.includes("water")) {
3285
- return colors.water;
3286
- }
3287
- if (sourceLayer === "building") {
3288
- return colors.building;
3289
- }
3290
- if (sourceLayer === "landcover" || id.includes("park") || id.includes("wood")) {
3291
- return colors.nature;
3292
- }
3293
- if (sourceLayer === "landuse" || id.includes("landuse")) {
3294
- return colors.land;
3295
- }
3296
- return void 0;
3297
- }
3298
- function osmBrightLineColor(id, sourceLayer, colors) {
3299
- if (sourceLayer === "transportation") {
3300
- return id.includes("case") || id.includes("casing") ? colors.roadCasing : colors.road;
3301
- }
3302
- if (sourceLayer === "waterway") {
3303
- return colors.water;
3304
- }
3305
- if (sourceLayer === "boundary") {
3306
- return colors.textMuted;
3307
- }
3308
- return void 0;
3309
- }
3310
- function osmBrightTextColor(id, sourceLayer, colors) {
3311
- if (sourceLayer === "water_name" || id.includes("water")) {
3312
- return colors.water;
3783
+ ],
3784
+ id: "bright"
3785
+ };
3786
+
3787
+ // src/terrain/index.ts
3788
+ function resolveTerrain(terrain, tileBaseUrl) {
3789
+ if (!terrain || terrain === "none") {
3790
+ return void 0;
3313
3791
  }
3314
- if (sourceLayer === "transportation_name") {
3315
- return colors.textMuted;
3792
+ const terrainConfig = typeof terrain === "string" ? { mode: terrain } : terrain;
3793
+ const mode = terrainConfig.mode ?? "hillshade";
3794
+ if (mode === "none") {
3795
+ return void 0;
3316
3796
  }
3317
- return colors.text;
3318
- }
3319
- function isRecord(value) {
3320
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
3321
- }
3322
- function stringValue(value) {
3323
- return typeof value === "string" ? value : "";
3324
- }
3325
- function normalizeBaseUrl(value) {
3326
- return normalizeUrl(value ?? defaultTileBaseUrl);
3327
- }
3328
- function normalizeStyleBaseUrl(value) {
3329
- return normalizeUrl(value ?? "");
3330
- }
3331
- function normalizeUrl(value) {
3332
- return value.replace(/\/+$/, "");
3333
- }
3334
- function textFont(typography) {
3335
- return [`${typography.font} ${fontWeightLabels[typography.weight]}`];
3336
- }
3337
- function fillLayer(id, source, sourceLayer, color, opacity) {
3797
+ const sourceId = terrainConfig.sourceId ?? "tileflow-terrain";
3798
+ const tileset = terrainConfig.tileset ?? "terrain";
3799
+ const url = terrainConfig.url ?? `${tileBaseUrl}/tiles/${tileset}/tiles.json`;
3800
+ const encoding = terrainConfig.encoding ?? "terrarium";
3338
3801
  return {
3339
- id,
3340
- type: "fill",
3341
- source,
3342
- "source-layer": sourceLayer,
3343
- paint: {
3344
- "fill-color": color,
3345
- "fill-opacity": opacity
3802
+ exaggeration: terrainConfig.exaggeration ?? 1.2,
3803
+ mode,
3804
+ sourceId,
3805
+ source: {
3806
+ ...terrainConfig.attribution ? { attribution: terrainConfig.attribution } : {},
3807
+ encoding,
3808
+ type: "raster-dem",
3809
+ url
3346
3810
  }
3347
3811
  };
3348
3812
  }
3349
- function openMapTilesLayers(source, sourceLayers, colors, options) {
3350
- return [
3351
- {
3352
- id: "background",
3353
- type: "background",
3354
- paint: {
3355
- "background-color": colors.canvas
3356
- }
3357
- },
3358
- fillLayer("landuse", source, sourceLayers.landuse, colors.land, 0.42),
3359
- fillLayer("parks", source, sourceLayers.park, colors.nature, parkOpacity(options.density)),
3360
- fillLayer("water", source, sourceLayers.water, colors.water, 1),
3361
- ...buildingLayers(source, sourceLayers.building, colors, options.density, options.buildings),
3362
- ...roadLayers(source, sourceLayers.road, colors, options.roads),
3363
- ...boundaryLayers(source, sourceLayers.boundary, colors),
3364
- ...labelLayers(source, sourceLayers.place, colors, options.labels, options.typography),
3365
- ...poiLayers(source, sourceLayers.poi, colors, options.poi, options.typography)
3366
- ];
3367
- }
3368
3813
  function withTerrainLayers(layers, terrain) {
3369
3814
  const hillshadeLayer = {
3370
3815
  id: "terrain-hillshade",
@@ -3388,335 +3833,559 @@ function withTerrainLayers(layers, terrain) {
3388
3833
  ...layers.slice(insertionIndex + 1)
3389
3834
  ];
3390
3835
  }
3391
- function buildingLayers(source, sourceLayer, colors, density, buildings) {
3392
- if (buildings === "hidden") {
3393
- return [];
3394
- }
3395
- if (buildings === "3d") {
3396
- return [
3397
- {
3398
- id: "buildings-3d",
3399
- type: "fill-extrusion",
3400
- source,
3401
- "source-layer": sourceLayer,
3402
- minzoom: 14,
3403
- filter: ["!=", ["get", "hide_3d"], true],
3404
- paint: {
3405
- "fill-extrusion-base": [
3406
- "case",
3407
- ["has", "render_min_height"],
3408
- ["to-number", ["get", "render_min_height"]],
3409
- ["has", "min_height"],
3410
- ["to-number", ["get", "min_height"]],
3411
- 0
3412
- ],
3413
- "fill-extrusion-color": colors.building,
3414
- "fill-extrusion-height": [
3415
- "interpolate",
3416
- ["linear"],
3417
- ["zoom"],
3418
- 14,
3419
- 0,
3420
- 15,
3421
- [
3422
- "case",
3423
- ["has", "render_height"],
3424
- ["to-number", ["get", "render_height"]],
3425
- ["has", "height"],
3426
- ["to-number", ["get", "height"]],
3427
- 18
3428
- ]
3429
- ],
3430
- "fill-extrusion-opacity": 0.72
3431
- }
3432
- }
3433
- ];
3434
- }
3435
- if (density === "clean") {
3436
- return [
3437
- {
3438
- id: "buildings-soft",
3439
- type: "fill",
3440
- source,
3441
- "source-layer": sourceLayer,
3442
- minzoom: 14,
3443
- paint: {
3444
- "fill-color": colors.building,
3445
- "fill-opacity": 0.38
3446
- }
3447
- }
3448
- ];
3449
- }
3450
- return [
3451
- {
3452
- id: "buildings",
3453
- type: "fill",
3454
- source,
3455
- "source-layer": sourceLayer,
3456
- minzoom: 13,
3457
- paint: {
3458
- "fill-color": colors.building,
3459
- "fill-opacity": density === "dense" ? 0.62 : 0.5
3460
- }
3461
- },
3462
- {
3463
- id: "buildings-outline",
3464
- type: "line",
3465
- source,
3466
- "source-layer": sourceLayer,
3467
- minzoom: 15,
3468
- paint: {
3469
- "line-color": mix(colors.building, colors.text, 0.2),
3470
- "line-opacity": 0.24,
3471
- "line-width": 0.6
3472
- }
3473
- }
3474
- ];
3475
- }
3476
- function roadLayers(source, sourceLayer, colors, roads2) {
3477
- if (roads2 === "hidden") {
3478
- return [];
3479
- }
3480
- const opacity = roads2 === "soft" ? 0.72 : roads2 === "standard" ? 0.84 : 0.96;
3481
- const casingOpacity = roads2 === "detailed" ? 0.3 : 0.16;
3482
- const majorRoadFilter = [
3483
- "match",
3484
- ["get", "class"],
3485
- ["motorway", "trunk", "primary", "secondary"],
3486
- true,
3487
- false
3488
- ];
3489
- const minorRoadFilter = ["!", majorRoadFilter];
3490
- const nonTunnelRoadFilter = ["!=", ["get", "brunnel"], "tunnel"];
3491
- return [
3492
- {
3493
- id: "roads-tunnels-casing",
3494
- type: "line",
3495
- source,
3496
- "source-layer": sourceLayer,
3497
- minzoom: 10,
3498
- filter: ["==", ["get", "brunnel"], "tunnel"],
3499
- paint: {
3500
- "line-color": colors.roadCasing,
3501
- "line-dasharray": [0.6, 0.8],
3502
- "line-opacity": 0.28,
3503
- "line-width": ["interpolate", ["linear"], ["zoom"], 10, 0.5, 14, 1.7, 16, 3.8]
3836
+
3837
+ // src/templates/osm-bright/theme.ts
3838
+ function applyOsmBrightTheme(layers, colors, theme, typography) {
3839
+ if (!theme.custom && theme.name === "light") {
3840
+ return layers;
3841
+ }
3842
+ return layers.map((layer) => {
3843
+ const type = stringValue(layer.type);
3844
+ const sourceLayer = stringValue(layer["source-layer"]);
3845
+ const id = stringValue(layer.id);
3846
+ const nextLayer = { ...layer };
3847
+ const paint = isRecord3(layer.paint) ? { ...layer.paint } : {};
3848
+ const layout = isRecord3(layer.layout) ? { ...layer.layout } : {};
3849
+ let paintChanged = false;
3850
+ let layoutChanged = false;
3851
+ if (type === "background" && hasBaseColor(theme, ["background", "canvas"])) {
3852
+ paint["background-color"] = colors.background;
3853
+ paintChanged = true;
3854
+ }
3855
+ if (type === "fill") {
3856
+ const fillColor = osmBrightFillColor(id, sourceLayer, colors, theme);
3857
+ if (fillColor) {
3858
+ paint["fill-color"] = fillColor;
3859
+ paintChanged = true;
3504
3860
  }
3505
- },
3506
- {
3507
- id: "roads-tunnels",
3508
- type: "line",
3509
- source,
3510
- "source-layer": sourceLayer,
3511
- minzoom: 10,
3512
- filter: ["==", ["get", "brunnel"], "tunnel"],
3513
- paint: {
3514
- "line-color": colors.roadMinor,
3515
- "line-dasharray": [0.6, 0.8],
3516
- "line-opacity": 0.2,
3517
- "line-width": ["interpolate", ["linear"], ["zoom"], 10, 0.25, 14, 1, 16, 2.4]
3861
+ }
3862
+ if (type === "line") {
3863
+ const lineColor = osmBrightLineColor(id, sourceLayer, colors, theme);
3864
+ if (lineColor) {
3865
+ paint["line-color"] = lineColor;
3866
+ paintChanged = true;
3518
3867
  }
3519
- },
3520
- {
3521
- id: "roads-casing",
3522
- type: "line",
3523
- source,
3524
- "source-layer": sourceLayer,
3525
- minzoom: 6,
3526
- filter: nonTunnelRoadFilter,
3527
- paint: {
3528
- "line-color": colors.roadCasing,
3529
- "line-opacity": casingOpacity,
3530
- "line-width": ["interpolate", ["linear"], ["zoom"], 6, 0.4, 12, 1.2, 16, 4]
3868
+ }
3869
+ if (type === "symbol") {
3870
+ const textColor = osmBrightTextColor(id, sourceLayer, colors, theme);
3871
+ if (textColor && ("text-color" in paint || id.includes("label") || id.includes("name"))) {
3872
+ paint["text-color"] = textColor;
3873
+ paintChanged = true;
3531
3874
  }
3532
- },
3533
- {
3534
- id: "roads-minor",
3535
- type: "line",
3536
- source,
3537
- "source-layer": sourceLayer,
3538
- minzoom: 6,
3539
- filter: ["all", nonTunnelRoadFilter, minorRoadFilter],
3540
- paint: {
3541
- "line-color": colors.roadMinor,
3542
- "line-opacity": opacity * 0.88,
3543
- "line-width": ["interpolate", ["linear"], ["zoom"], 6, 0.2, 12, 0.8, 16, 3]
3875
+ if ("text-halo-color" in paint && shouldThemeLabelHalo(theme)) {
3876
+ paint["text-halo-color"] = colors.labels.halo;
3877
+ paintChanged = true;
3544
3878
  }
3545
- },
3546
- {
3547
- id: "roads-major",
3548
- type: "line",
3549
- source,
3550
- "source-layer": sourceLayer,
3551
- minzoom: 5,
3552
- filter: ["all", nonTunnelRoadFilter, majorRoadFilter],
3553
- paint: {
3554
- "line-color": colors.roadMajor,
3555
- "line-opacity": opacity,
3556
- "line-width": ["interpolate", ["linear"], ["zoom"], 5, 0.35, 12, 1.2, 16, 4]
3879
+ if (theme.typographyOverridden && "text-font" in layout) {
3880
+ layout["text-font"] = textFont(typography);
3881
+ layoutChanged = true;
3557
3882
  }
3883
+ }
3884
+ if (paintChanged) {
3885
+ nextLayer.paint = paint;
3886
+ }
3887
+ if (layoutChanged) {
3888
+ nextLayer.layout = layout;
3889
+ }
3890
+ return nextLayer;
3891
+ });
3892
+ }
3893
+ function osmBrightFillColor(id, sourceLayer, colors, theme) {
3894
+ if (sourceLayer === "water" || id.includes("water")) {
3895
+ return shouldThemeHydro(theme) ? colors.hydro.water : void 0;
3896
+ }
3897
+ if (sourceLayer === "building") {
3898
+ return shouldThemeBuildings(theme) ? colors.buildings.fill : void 0;
3899
+ }
3900
+ if (sourceLayer === "landcover" || id.includes("park") || id.includes("wood")) {
3901
+ if (!shouldThemeLandcover(theme)) {
3902
+ return void 0;
3903
+ }
3904
+ if (id.includes("wood") || id.includes("forest")) {
3905
+ return colors.landcover.wood;
3906
+ }
3907
+ if (id.includes("grass")) {
3908
+ return colors.landcover.grass;
3909
+ }
3910
+ if (id.includes("sand") || id.includes("beach")) {
3911
+ return colors.landcover.sand;
3912
+ }
3913
+ if (id.includes("ice") || id.includes("glacier")) {
3914
+ return colors.landcover.ice;
3915
+ }
3916
+ return colors.landcover.park;
3917
+ }
3918
+ if (sourceLayer === "landuse" || id.includes("landuse")) {
3919
+ if (!shouldThemeLanduse(theme)) {
3920
+ return void 0;
3921
+ }
3922
+ if (id.includes("commercial") || id.includes("retail")) {
3923
+ return colors.landuse.commercial;
3924
+ }
3925
+ if (id.includes("industrial")) {
3926
+ return colors.landuse.industrial;
3927
+ }
3928
+ if (id.includes("cemetery")) {
3929
+ return colors.landuse.cemetery;
3930
+ }
3931
+ return colors.landuse.residential;
3932
+ }
3933
+ return void 0;
3934
+ }
3935
+ function osmBrightLineColor(id, sourceLayer, colors, theme) {
3936
+ if (sourceLayer === "transportation") {
3937
+ return osmBrightRoadLineColor(id, colors, theme);
3938
+ }
3939
+ if (sourceLayer === "waterway") {
3940
+ return shouldThemeHydro(theme) ? colors.hydro.waterway : void 0;
3941
+ }
3942
+ if (sourceLayer === "boundary") {
3943
+ if (!shouldThemeBoundaries(theme)) {
3944
+ return void 0;
3945
+ }
3946
+ return id.includes("country") || id.includes("admin") ? colors.boundaries.major : colors.boundaries.admin;
3947
+ }
3948
+ return void 0;
3949
+ }
3950
+ function osmBrightTextColor(id, sourceLayer, colors, theme) {
3951
+ if (sourceLayer === "water_name" || id.includes("water")) {
3952
+ return shouldThemeWaterLabels(theme) ? colors.labels.water : void 0;
3953
+ }
3954
+ if (sourceLayer === "transportation_name") {
3955
+ return shouldThemeRoadLabels(theme) ? colors.labels.road : void 0;
3956
+ }
3957
+ if (sourceLayer === "poi" || id.includes("poi")) {
3958
+ return shouldThemePoiLabels(theme) ? colors.labels.poi : void 0;
3959
+ }
3960
+ if (id.includes("country")) {
3961
+ return shouldThemeLabels(theme) ? colors.labels.country : void 0;
3962
+ }
3963
+ if (sourceLayer === "place" || id.includes("place")) {
3964
+ return shouldThemeLabels(theme) ? colors.labels.settlement : void 0;
3965
+ }
3966
+ return shouldThemeLabels(theme) ? colors.labels.primary : void 0;
3967
+ }
3968
+ function shouldThemeHydro(theme) {
3969
+ return hasBaseColor(theme, ["water"]) || hasModuleColorGroup(theme, "hydro");
3970
+ }
3971
+ function shouldThemeWaterLabels(theme) {
3972
+ return shouldThemeHydro(theme) || shouldThemeLabels(theme) || hasNestedModuleColor(theme, "labels", "water");
3973
+ }
3974
+ function osmBrightRoadLineColor(id, colors, theme) {
3975
+ if (hasModuleColorGroup(theme, "roads")) {
3976
+ return osmBrightModuleRoadLineColor(id, colors);
3977
+ }
3978
+ if (isNonRoadThemeTarget(id)) {
3979
+ return void 0;
3980
+ }
3981
+ if (isRoadCasing(id)) {
3982
+ return hasBaseColor(theme, ["roadCasing"]) ? colors.roads.casing : void 0;
3983
+ }
3984
+ if (isMajorRoad(id)) {
3985
+ return hasBaseColor(theme, ["roadMajor"]) ? colors.roads.primary : void 0;
3986
+ }
3987
+ if (isMinorRoad(id)) {
3988
+ return hasBaseColor(theme, ["road"]) ? colors.roads.minor : void 0;
3989
+ }
3990
+ return void 0;
3991
+ }
3992
+ function osmBrightModuleRoadLineColor(id, colors) {
3993
+ if (isRoadCasing(id)) {
3994
+ return colors.roads.casing;
3995
+ }
3996
+ if (id.includes("ferry")) {
3997
+ return colors.roads.ferry;
3998
+ }
3999
+ if (id.includes("rail")) {
4000
+ return colors.roads.rail;
4001
+ }
4002
+ if (id.includes("path") || id.includes("track") || id.includes("steps")) {
4003
+ return colors.roads.path;
4004
+ }
4005
+ if (id.includes("bridge")) {
4006
+ return colors.roads.bridge;
4007
+ }
4008
+ if (id.includes("tunnel")) {
4009
+ return colors.roads.tunnel;
4010
+ }
4011
+ if (id.includes("motorway")) {
4012
+ return colors.roads.motorway;
4013
+ }
4014
+ if (id.includes("trunk")) {
4015
+ return colors.roads.trunk;
4016
+ }
4017
+ if (id.includes("primary")) {
4018
+ return colors.roads.primary;
4019
+ }
4020
+ if (id.includes("secondary") || id.includes("tertiary")) {
4021
+ return colors.roads.secondary;
4022
+ }
4023
+ if (id.includes("link")) {
4024
+ return colors.roads.primary;
4025
+ }
4026
+ if (id.includes("minor") || id.includes("service")) {
4027
+ return colors.roads.minor;
4028
+ }
4029
+ return void 0;
4030
+ }
4031
+ function isRoadCasing(id) {
4032
+ return id.includes("case") || id.includes("casing");
4033
+ }
4034
+ function isMajorRoad(id) {
4035
+ return id.includes("motorway") || id.includes("trunk") || id.includes("primary") || id.includes("secondary") || id.includes("tertiary") || id.includes("highway-link") || id.includes("bridge-link") || id.includes("tunnel-link");
4036
+ }
4037
+ function isMinorRoad(id) {
4038
+ return id.includes("minor") || id.includes("service") || id.includes("track");
4039
+ }
4040
+ function isNonRoadThemeTarget(id) {
4041
+ return id.includes("rail") || id.includes("ferry") || id.includes("cablecar") || id.includes("path") || id.includes("steps") || id.includes("pier") || id.includes("area");
4042
+ }
4043
+ function shouldThemeLandcover(theme) {
4044
+ return hasBaseColor(theme, ["park", "greenspace", "nature"]) || hasModuleColorGroup(theme, "landcover");
4045
+ }
4046
+ function shouldThemeLanduse(theme) {
4047
+ return hasBaseColor(theme, ["land"]) || hasModuleColorGroup(theme, "landuse");
4048
+ }
4049
+ function shouldThemeBuildings(theme) {
4050
+ return hasBaseColor(theme, ["building"]) || hasModuleColorGroup(theme, "buildings");
4051
+ }
4052
+ function shouldThemeBoundaries(theme) {
4053
+ return hasBaseColor(theme, ["boundary"]) || hasModuleColorGroup(theme, "boundaries");
4054
+ }
4055
+ function shouldThemeLabels(theme) {
4056
+ return hasBaseColor(theme, ["text", "textMuted"]) || hasModuleColorGroup(theme, "labels");
4057
+ }
4058
+ function shouldThemeLabelHalo(theme) {
4059
+ return hasBaseColor(theme, ["background", "canvas", "textHalo"]) || hasNestedModuleColor(theme, "labels", "halo");
4060
+ }
4061
+ function shouldThemeRoadLabels(theme) {
4062
+ return shouldThemeLabels(theme) || hasNestedModuleColor(theme, "labels", "road");
4063
+ }
4064
+ function shouldThemePoiLabels(theme) {
4065
+ return shouldThemeLabels(theme) || hasNestedModuleColor(theme, "labels", "poi");
4066
+ }
4067
+ function hasBaseColor(theme, keys) {
4068
+ return keys.some(
4069
+ (key) => typeof theme.colorOverrides[key] === "string"
4070
+ );
4071
+ }
4072
+ function hasModuleColorGroup(theme, group) {
4073
+ const value = theme.moduleOverrides[group];
4074
+ return Boolean(
4075
+ value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length > 0
4076
+ );
4077
+ }
4078
+ function hasNestedModuleColor(theme, group, key) {
4079
+ const value = theme.moduleOverrides[group];
4080
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
4081
+ return false;
4082
+ }
4083
+ return typeof value[key] === "string";
4084
+ }
4085
+ function isRecord3(value) {
4086
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
4087
+ }
4088
+ function stringValue(value) {
4089
+ return typeof value === "string" ? value : "";
4090
+ }
4091
+
4092
+ // src/templates/osm-bright/index.ts
4093
+ function createOsmBrightStyle(options) {
4094
+ const template = cloneJson(style_default);
4095
+ const layers = template.layers.map((layer) => remapOsmBrightSource(layer, options.sourceId));
4096
+ const themedLayers = applyOsmBrightTheme(
4097
+ layers,
4098
+ options.colors,
4099
+ options.theme,
4100
+ options.typography
4101
+ );
4102
+ const resolvedLayers = options.terrain ? withTerrainLayers(themedLayers, options.terrain) : themedLayers;
4103
+ return {
4104
+ ...template,
4105
+ name: options.name,
4106
+ glyphs: options.glyphs,
4107
+ ...options.sprite || template.sprite ? { sprite: options.sprite ?? template.sprite } : {},
4108
+ sources: {
4109
+ [options.sourceId]: {
4110
+ type: "vector",
4111
+ url: options.tilesUrl,
4112
+ attribution: options.attribution
4113
+ },
4114
+ ...options.terrain ? {
4115
+ [options.terrain.sourceId]: options.terrain.source
4116
+ } : {}
3558
4117
  },
3559
- {
3560
- id: "roads-bridges",
3561
- type: "line",
3562
- source,
3563
- "source-layer": sourceLayer,
3564
- minzoom: 10,
3565
- filter: ["==", ["get", "brunnel"], "bridge"],
3566
- paint: {
3567
- "line-color": mix(colors.roadMajor, colors.canvas, 0.14),
3568
- "line-opacity": opacity,
3569
- "line-width": ["interpolate", ["linear"], ["zoom"], 10, 0.45, 14, 1.6, 16, 4.4]
4118
+ layers: resolvedLayers,
4119
+ ...options.terrain?.mode === "3d" ? {
4120
+ terrain: {
4121
+ exaggeration: options.terrain.exaggeration,
4122
+ source: options.terrain.sourceId
3570
4123
  }
4124
+ } : {},
4125
+ metadata: {
4126
+ ...template.metadata ?? {},
4127
+ ...options.metadata,
4128
+ "tileflow:template": "openmaptiles-osm-bright"
4129
+ }
4130
+ };
4131
+ }
4132
+ function cloneJson(value) {
4133
+ return JSON.parse(JSON.stringify(value));
4134
+ }
4135
+ function remapOsmBrightSource(layer, sourceId) {
4136
+ if (layer.source !== "openmaptiles") {
4137
+ return layer;
4138
+ }
4139
+ return {
4140
+ ...layer,
4141
+ source: sourceId
4142
+ };
4143
+ }
4144
+
4145
+ // src/compiler.ts
4146
+ var defaultSourceLayers = {
4147
+ landuse: "landuse",
4148
+ park: "park",
4149
+ water: "water",
4150
+ road: "transportation",
4151
+ building: "building",
4152
+ boundary: "boundary",
4153
+ place: "place",
4154
+ poi: "poi"
4155
+ };
4156
+ var defaultConfig = {
4157
+ density: "balanced",
4158
+ roads: "standard",
4159
+ labels: "balanced",
4160
+ poi: "minimal"
4161
+ };
4162
+ var defaultTileBaseUrl = "https://api.tileflow.dev";
4163
+ var defaultTileset = "world";
4164
+ function createStyle(input = {}, options = {}) {
4165
+ const parsed = configSchema.safeParse(input);
4166
+ if (!parsed.success) {
4167
+ const message = parsed.error.issues.map((issue) => `${issue.path.join(".") || "config"}: ${issue.message}`).join("; ");
4168
+ throw new Error(`Invalid Tileflow config. ${message}`);
4169
+ }
4170
+ const config = parsed.data;
4171
+ const basemap = config.basemap ?? osm();
4172
+ const theme = resolveTheme(config.theme, options.themes);
4173
+ const density = config.density ?? theme.preset.density ?? defaultConfig.density;
4174
+ const themeLayerOverrideModules = Object.keys(theme.layerOverrides).length > 0 ? [
4175
+ {
4176
+ type: "styleOverride",
4177
+ layers: theme.layerOverrides
3571
4178
  }
3572
- ];
3573
- }
3574
- function boundaryLayers(source, sourceLayer, colors) {
3575
- return [
4179
+ ] : [];
4180
+ const mapLayerOverrideModules = config.layers ? [
3576
4181
  {
3577
- id: "boundaries",
3578
- type: "line",
3579
- source,
3580
- "source-layer": sourceLayer,
3581
- minzoom: 2,
3582
- paint: {
3583
- "line-color": mix(colors.text, colors.canvas, 0.7),
3584
- "line-opacity": 0.35,
3585
- "line-width": ["interpolate", ["linear"], ["zoom"], 2, 0.35, 8, 0.8]
3586
- }
4182
+ type: "styleOverride",
4183
+ layers: config.layers
3587
4184
  }
4185
+ ] : [];
4186
+ const styleOverrideModules = [
4187
+ ...themeLayerOverrideModules,
4188
+ ...mapLayerOverrideModules,
4189
+ ...config.modules?.filter(
4190
+ (moduleConfig) => moduleConfig.type === "styleOverride"
4191
+ ) ?? []
3588
4192
  ];
3589
- }
3590
- function labelLayers(source, sourceLayer, colors, labels, typography) {
3591
- if (labels === "none") {
3592
- return [];
4193
+ const roadModule = config.modules?.find(
4194
+ (moduleConfig) => moduleConfig.type === "roads"
4195
+ );
4196
+ const roads2 = resolveRoads(config.roads, roadModule, theme.preset.roads ?? defaultConfig.roads);
4197
+ const labels = config.labels ?? theme.preset.labels ?? defaultConfig.labels;
4198
+ const poi = config.poi ?? theme.preset.poi ?? defaultConfig.poi;
4199
+ const buildings = config.buildings ?? "flat";
4200
+ const colors = resolveColors(config.colors, theme.colors, {}, theme.modules);
4201
+ const sourceId = config.tiles?.sourceId ?? basemap.sourceId ?? "tileflow";
4202
+ const tileBaseUrl = normalizeBaseUrl(options.tileBaseUrl);
4203
+ const tileset = options.tileset ?? config.tileset ?? config.tiles?.tileset ?? basemap.tileset ?? defaultTileset;
4204
+ const tilesUrl = config.tiles?.url ?? basemap.url ?? `${tileBaseUrl}/tiles/${tileset}/tiles.json`;
4205
+ const typography = resolveTypography(config.typography, theme.typography);
4206
+ const terrain = resolveTerrain(config.terrain, tileBaseUrl);
4207
+ const glyphs = config.glyphs ?? basemap.glyphs ?? `${tileBaseUrl}/fonts/{fontstack}/{range}.pbf`;
4208
+ const sprite = config.sprite ?? basemap.sprite;
4209
+ const attribution = config.tiles?.attribution ?? basemap.attribution ?? "\xA9 OpenMapTiles \xA9 OpenStreetMap contributors";
4210
+ const metadata = {
4211
+ "tileflow:basemap": basemap.type,
4212
+ "tileflow:theme": theme.name,
4213
+ "tileflow:themeMode": theme.mode,
4214
+ "tileflow:density": density,
4215
+ "tileflow:roads": roads2,
4216
+ "tileflow:labels": labels,
4217
+ "tileflow:poi": poi,
4218
+ "tileflow:buildings": buildings,
4219
+ "tileflow:modules": config.modules?.map((moduleConfig) => moduleConfig.type) ?? [],
4220
+ "tileflow:themeModules": Object.keys(theme.moduleOverrides),
4221
+ "tileflow:terrain": terrain ? {
4222
+ encoding: terrain.source.encoding,
4223
+ mode: terrain.mode,
4224
+ sourceId: terrain.sourceId
4225
+ } : void 0,
4226
+ "tileflow:typography": typography,
4227
+ "tileflow:colors": colors,
4228
+ "tileflow:tileBaseUrl": tileBaseUrl,
4229
+ "tileflow:tileset": tileset,
4230
+ "tileflow:tiles": "tileflow",
4231
+ "tileflow:view": config.view
4232
+ };
4233
+ if (shouldUseOsmBrightTemplate(config, basemap, theme)) {
4234
+ const style2 = createOsmBrightStyle({
4235
+ attribution,
4236
+ colors,
4237
+ glyphs,
4238
+ metadata,
4239
+ name: config.name ?? "OSM Bright",
4240
+ sourceId,
4241
+ sprite,
4242
+ terrain,
4243
+ theme,
4244
+ tilesUrl,
4245
+ typography
4246
+ });
4247
+ return {
4248
+ ...style2,
4249
+ layers: applyStyleOverrides(style2.layers, styleOverrideModules)
4250
+ };
3593
4251
  }
3594
- const maxRank = labels === "essential" ? 3 : labels === "balanced" ? 7 : 12;
3595
- return [
4252
+ const sourceLayers = resolveSourceLayers(
3596
4253
  {
3597
- id: "place-labels",
3598
- type: "symbol",
3599
- source,
3600
- "source-layer": sourceLayer,
3601
- minzoom: 3,
3602
- filter: ["<=", ["coalesce", ["get", "rank"], 10], maxRank],
3603
- layout: {
3604
- "text-field": ["coalesce", ["get", "name:en"], ["get", "name"]],
3605
- "text-font": textFont(typography),
3606
- "text-size": ["interpolate", ["linear"], ["zoom"], 3, 10, 8, 13, 14, 17],
3607
- "text-letter-spacing": 0,
3608
- "text-padding": 4
4254
+ ...defaultSourceLayers,
4255
+ ...basemap.sourceLayers,
4256
+ ...config.tiles?.sourceLayers
4257
+ },
4258
+ void 0
4259
+ );
4260
+ const layers = openMapTilesLayers(sourceId, sourceLayers, colors, {
4261
+ buildings,
4262
+ density,
4263
+ labels,
4264
+ poi,
4265
+ roads: roads2,
4266
+ typography
4267
+ });
4268
+ const terrainLayers = terrain ? withTerrainLayers(layers, terrain) : layers;
4269
+ const resolvedLayers = applyStyleOverrides(terrainLayers, styleOverrideModules);
4270
+ const style = {
4271
+ version: 8,
4272
+ name: config.name ?? "Tileflow",
4273
+ glyphs,
4274
+ ...sprite ? { sprite } : {},
4275
+ sources: {
4276
+ [sourceId]: {
4277
+ type: "vector",
4278
+ url: tilesUrl,
4279
+ attribution
3609
4280
  },
3610
- paint: {
3611
- "text-color": colors.text,
3612
- "text-halo-color": colors.textHalo,
3613
- "text-halo-width": 1.2
4281
+ ...terrain ? {
4282
+ [terrain.sourceId]: terrain.source
4283
+ } : {}
4284
+ },
4285
+ layers: resolvedLayers,
4286
+ ...terrain?.mode === "3d" ? {
4287
+ terrain: {
4288
+ exaggeration: terrain.exaggeration,
4289
+ source: terrain.sourceId
3614
4290
  }
3615
- }
3616
- ];
4291
+ } : {},
4292
+ metadata
4293
+ };
4294
+ return style;
3617
4295
  }
3618
- function poiLayers(source, sourceLayer, colors, poi, typography) {
3619
- if (poi === "none") {
3620
- return [];
4296
+ function createStyleFromProject(projectConfig, mapName, options = {}) {
4297
+ const config = projectConfig.maps[mapName];
4298
+ if (!config) {
4299
+ throw new Error(`Unknown Tileflow map: ${mapName}`);
3621
4300
  }
3622
- const minzoom = poi === "minimal" ? 15 : poi === "balanced" ? 13 : 11;
3623
- return [
4301
+ const tilesetName = options.tileset ?? config.tileset ?? config.tiles?.tileset ?? config.basemap?.tileset;
4302
+ const tilesetConfig = tilesetName ? projectConfig.tilesets?.[tilesetName] : void 0;
4303
+ const resolvedTileset = tilesetConfig?.id ?? tilesetName ?? defaultTileset;
4304
+ const baseBasemap = config.basemap ?? osm();
4305
+ const { sourceLayers: tileSourceLayers, ...tileOptions } = config.tiles ?? {};
4306
+ const resolvedBasemapSourceLayers = baseBasemap.sourceLayers || tilesetConfig?.sourceLayers ? resolveSourceLayers(baseBasemap.sourceLayers, tilesetConfig?.sourceLayers) : void 0;
4307
+ const resolvedTileSourceLayers = baseBasemap.sourceLayers || !tileSourceLayers ? void 0 : resolveSourceLayers(tileSourceLayers, tilesetConfig?.sourceLayers);
4308
+ return createStyle(
3624
4309
  {
3625
- id: "poi-labels",
3626
- type: "symbol",
3627
- source,
3628
- "source-layer": sourceLayer,
3629
- minzoom,
3630
- layout: {
3631
- "text-field": ["coalesce", ["get", "name:en"], ["get", "name"]],
3632
- "text-font": textFont(typography),
3633
- "text-size": ["interpolate", ["linear"], ["zoom"], minzoom, 10, 17, 12],
3634
- "text-letter-spacing": 0,
3635
- "text-padding": 2
4310
+ ...config,
4311
+ tileset: resolvedTileset,
4312
+ basemap: {
4313
+ ...baseBasemap,
4314
+ tileset: resolvedTileset,
4315
+ attribution: baseBasemap.attribution ?? tilesetConfig?.attribution,
4316
+ ...resolvedBasemapSourceLayers ? { sourceLayers: resolvedBasemapSourceLayers } : {}
3636
4317
  },
3637
- paint: {
3638
- "text-color": mix(colors.textMuted, colors.accent, 0.22),
3639
- "text-halo-color": alpha(colors.canvas, 0.78),
3640
- "text-halo-width": 1
4318
+ tiles: {
4319
+ ...tileOptions,
4320
+ tileset: resolvedTileset,
4321
+ ...resolvedTileSourceLayers ? { sourceLayers: resolvedTileSourceLayers } : {}
3641
4322
  }
4323
+ },
4324
+ {
4325
+ ...options,
4326
+ themes: projectConfig.themes,
4327
+ tileset: resolvedTileset
3642
4328
  }
3643
- ];
3644
- }
3645
- function parkOpacity(density) {
3646
- return density === "clean" ? 0.64 : density === "balanced" ? 0.78 : 0.9;
3647
- }
3648
- function alpha(color, opacity) {
3649
- const { a, b, g, r } = hexToRgba(color);
3650
- return `rgba(${r}, ${g}, ${b}, ${roundAlpha(a * opacity)})`;
4329
+ );
3651
4330
  }
3652
- function mix(hexA, hexB, amount) {
3653
- const a = hexToRgb(hexA);
3654
- const b = hexToRgb(hexB);
3655
- const r = Math.round(a.r + (b.r - a.r) * amount);
3656
- const g = Math.round(a.g + (b.g - a.g) * amount);
3657
- const bValue = Math.round(a.b + (b.b - a.b) * amount);
3658
- return rgbToHex(r, g, bValue);
4331
+ function createManifest(projectConfig, options = {}) {
4332
+ const styleBaseUrl = normalizeStyleBaseUrl(options.styleBaseUrl);
4333
+ const tileBaseUrl = normalizeBaseUrl(options.tileBaseUrl);
4334
+ const maps = Object.keys(projectConfig.maps);
4335
+ return {
4336
+ version: 1,
4337
+ tileBaseUrl,
4338
+ styles: Object.fromEntries(
4339
+ maps.map((mapName) => [mapName, `${styleBaseUrl}/styles/${mapName}.json`])
4340
+ ),
4341
+ maps: Object.fromEntries(
4342
+ maps.map((mapName) => [mapName, `${styleBaseUrl}/styles/${mapName}.json`])
4343
+ )
4344
+ };
3659
4345
  }
3660
- function hexToRgb(hex) {
3661
- const { b, g, r } = hexToRgba(hex);
3662
- return { b, g, r };
4346
+ function getDefaultColors2() {
4347
+ return getDefaultColors();
3663
4348
  }
3664
- function hexToRgba(hex) {
3665
- const rgbMatch = hex.match(
3666
- /^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)(?:\s*,\s*(\d+(?:\.\d+)?))?\s*\)$/
3667
- );
3668
- if (rgbMatch) {
3669
- return {
3670
- a: roundAlpha(Number(rgbMatch[4] ?? 1)),
3671
- r: Math.round(Number(rgbMatch[1])),
3672
- g: Math.round(Number(rgbMatch[2])),
3673
- b: Math.round(Number(rgbMatch[3]))
3674
- };
3675
- }
3676
- const value = hex.replace("#", "");
3677
- const normalized = value.length === 3 || value.length === 4 ? value.split("").map((character) => character + character).join("") : value;
4349
+ function resolveSourceLayers(mapLayers, tilesetLayers) {
3678
4350
  return {
3679
- a: normalized.length === 8 ? roundAlpha(Number.parseInt(normalized.slice(6, 8), 16) / 255) : 1,
3680
- r: Number.parseInt(normalized.slice(0, 2), 16),
3681
- g: Number.parseInt(normalized.slice(2, 4), 16),
3682
- b: Number.parseInt(normalized.slice(4, 6), 16)
4351
+ ...defaultSourceLayers,
4352
+ ...tilesetLayers,
4353
+ ...mapLayers
3683
4354
  };
3684
4355
  }
3685
- function cssColor(hex) {
3686
- const value = hex.replace("#", "");
3687
- if (value.length !== 4 && value.length !== 8) {
3688
- return hex;
3689
- }
3690
- const { a, b, g, r } = hexToRgba(hex);
3691
- return `rgba(${r}, ${g}, ${b}, ${a})`;
4356
+ function shouldUseOsmBrightTemplate(config, basemap, theme) {
4357
+ const templateTheme = theme.custom || theme.name === "light" || theme.name === "dark";
4358
+ return basemap.type === "osm" && templateTheme && !config.colors && !config.typography && !config.density && !config.roads && !config.labels && !config.poi && !config.buildings && !config.modules?.some((moduleConfig) => moduleConfig.type !== "styleOverride") && !basemap.sourceLayers && !config.tiles?.sourceLayers;
3692
4359
  }
3693
- function roundAlpha(value) {
3694
- return Math.round(value * 1e3) / 1e3;
4360
+ function normalizeBaseUrl(value) {
4361
+ return normalizeUrl(value ?? defaultTileBaseUrl);
3695
4362
  }
3696
- function rgbToHex(r, g, b) {
3697
- return `#${[r, g, b].map((value) => value.toString(16).padStart(2, "0")).join("")}`;
4363
+ function normalizeStyleBaseUrl(value) {
4364
+ return normalizeUrl(value ?? "");
4365
+ }
4366
+ function normalizeUrl(value) {
4367
+ return value.replace(/\/+$/, "");
3698
4368
  }
3699
4369
 
3700
4370
  // src/config.ts
3701
4371
  function defineTileflow(config) {
3702
4372
  return config;
3703
4373
  }
3704
-
3705
- // src/modules.ts
3706
- function roads(options = {}) {
3707
- return {
3708
- type: "roads",
3709
- preset: options.preset ?? "modern",
3710
- ...options.density ? { density: options.density } : {}
3711
- };
3712
- }
3713
4374
  export {
4375
+ configSchema,
3714
4376
  createManifest,
3715
4377
  createStyle,
3716
4378
  createStyleFromProject,
3717
4379
  defineTileflow,
3718
- getDefaultColors,
4380
+ getDefaultColors2 as getDefaultColors,
3719
4381
  osm,
4382
+ parseTileflowMap,
4383
+ parseTileflowProject,
3720
4384
  roads,
3721
- validateConfig
4385
+ styleOverride,
4386
+ tileflowMapSchema,
4387
+ tileflowProjectSchema,
4388
+ tileflowThemeSchema,
4389
+ validateConfig,
4390
+ validateTileflowConfig
3722
4391
  };