@tileflow/core 0.1.0-alpha.3 → 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: {
@@ -2282,1072 +3506,310 @@ var style_default = {
2282
3506
  visibility: "visible"
2283
3507
  },
2284
3508
  paint: {
2285
- "text-color": "#666",
2286
- "text-halo-blur": 0.5,
2287
- "text-halo-color": "#ffffff",
2288
- "text-halo-width": 1
2289
- }
2290
- },
2291
- {
2292
- id: "place-other",
2293
- type: "symbol",
2294
- metadata: { "mapbox:group": "1444849242106.713" },
2295
- source: "openmaptiles",
2296
- "source-layer": "place",
2297
- filter: [
2298
- "!in",
2299
- "class",
2300
- "city",
2301
- "town",
2302
- "village",
2303
- "state",
2304
- "country",
2305
- "continent"
2306
- ],
2307
- layout: {
2308
- "text-field": "{name:latin}\n{name:nonlatin}",
2309
- "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]] },
2526
- "text-transform": "uppercase",
2527
- visibility: "visible"
2528
- },
2529
- paint: {
2530
- "text-color": "#334",
2531
- "text-halo-blur": 1,
2532
- "text-halo-color": "rgba(255,255,255,0.8)",
2533
- "text-halo-width": 2
3509
+ "text-color": "#666",
3510
+ "text-halo-blur": 0.5,
3511
+ "text-halo-color": "#ffffff",
3512
+ "text-halo-width": 1
2534
3513
  }
2535
3514
  },
2536
3515
  {
2537
- id: "place-continent",
3516
+ id: "place-other",
2538
3517
  type: "symbol",
2539
3518
  metadata: { "mapbox:group": "1444849242106.713" },
2540
3519
  source: "openmaptiles",
2541
3520
  "source-layer": "place",
2542
- maxzoom: 1,
2543
- filter: ["==", "class", "continent"],
3521
+ filter: [
3522
+ "!in",
3523
+ "class",
3524
+ "city",
3525
+ "town",
3526
+ "village",
3527
+ "state",
3528
+ "country",
3529
+ "continent"
3530
+ ],
2544
3531
  layout: {
2545
- "text-field": "{name:latin}",
3532
+ "text-field": "{name:latin}\n{name:nonlatin}",
2546
3533
  "text-font": ["Noto Sans Bold"],
2547
- "text-max-width": 6.25,
2548
- "text-size": 14,
3534
+ "text-letter-spacing": 0.1,
3535
+ "text-max-width": 9,
3536
+ "text-size": { base: 1.2, stops: [[12, 10], [15, 14]] },
2549
3537
  "text-transform": "uppercase",
2550
3538
  visibility: "visible"
2551
3539
  },
2552
3540
  paint: {
2553
- "text-color": "#334",
2554
- "text-halo-blur": 1,
3541
+ "text-color": "#633",
2555
3542
  "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 themeConfigSchema = z.object({
2595
- mode: z.enum(["light", "dark"]).optional(),
2596
- colors: colorConfigSchema.optional(),
2597
- typography: typographySchema.optional()
2598
- });
2599
- var sourceLayersSchema = z.object({
2600
- landuse: z.string().optional(),
2601
- park: z.string().optional(),
2602
- water: z.string().optional(),
2603
- road: z.string().optional(),
2604
- building: z.string().optional(),
2605
- boundary: z.string().optional(),
2606
- place: z.string().optional(),
2607
- poi: z.string().optional()
2608
- });
2609
- var basemapSchema = z.object({
2610
- type: z.literal("osm"),
2611
- attribution: z.string().optional(),
2612
- glyphs: z.string().min(1).optional(),
2613
- sourceId: z.string().min(1).optional(),
2614
- sourceLayers: sourceLayersSchema.optional(),
2615
- sprite: z.string().min(1).optional(),
2616
- tileset: z.string().trim().min(1).optional(),
2617
- url: z.url().optional()
2618
- });
2619
- var roadsModuleSchema = z.object({
2620
- type: z.literal("roads"),
2621
- density: z.enum(["clean", "balanced", "dense"]).optional(),
2622
- preset: z.enum(["modern", "minimal"]).optional()
2623
- });
2624
- var moduleSchema = z.discriminatedUnion("type", [roadsModuleSchema]);
2625
- var viewSchema = z.object({
2626
- bearing: z.number().finite().min(-180).max(180).optional(),
2627
- center: z.tuple([z.number().finite(), z.number().finite()]).refine(([lng, lat]) => lng >= -180 && lng <= 180 && lat >= -90 && lat <= 90, {
2628
- message: "Expected center as [lng, lat] within world bounds"
2629
- }).optional(),
2630
- zoom: z.number().finite().min(0).max(24).optional()
2631
- });
2632
- var configSchema = z.object({
2633
- basemap: basemapSchema.optional(),
2634
- name: z.string().min(1).optional(),
2635
- tileset: z.string().trim().min(1).optional(),
2636
- theme: z.union([z.string().trim().min(1), themeConfigSchema]).optional(),
2637
- modules: z.array(moduleSchema).optional(),
2638
- colors: colorConfigSchema.optional(),
2639
- typography: typographySchema.optional(),
2640
- density: z.enum(["clean", "balanced", "dense"]).optional(),
2641
- roads: z.enum(["hidden", "soft", "standard", "detailed"]).optional(),
2642
- labels: z.enum(["none", "essential", "balanced", "full"]).optional(),
2643
- poi: z.enum(["none", "minimal", "balanced", "full"]).optional(),
2644
- buildings: z.enum(["hidden", "flat", "3d"]).optional(),
2645
- terrain: z.union([
2646
- z.enum(["none", "hillshade", "3d"]),
2647
- z.object({
2648
- attribution: z.string().optional(),
2649
- encoding: z.enum(["mapbox", "terrarium"]).optional(),
2650
- exaggeration: z.number().min(0).max(10).optional(),
2651
- mode: z.enum(["none", "hillshade", "3d"]).optional(),
2652
- sourceId: z.string().min(1).optional(),
2653
- tileset: z.string().trim().min(1).optional(),
2654
- url: z.url().optional()
2655
- })
2656
- ]).optional(),
2657
- glyphs: z.string().min(1).optional(),
2658
- sprite: z.string().min(1).optional(),
2659
- tiles: z.object({
2660
- sourceId: z.string().min(1).optional(),
2661
- tileset: z.string().trim().min(1).optional(),
2662
- url: z.url().optional(),
2663
- attribution: z.string().optional(),
2664
- sourceLayers: sourceLayersSchema.optional()
2665
- }).optional(),
2666
- view: viewSchema.optional(),
2667
- allowedOrigins: z.array(z.string().trim().min(1).max(253)).max(20).optional()
2668
- });
2669
- var tilesetConfigSchema = z.object({
2670
- id: z.string().trim().min(1).optional(),
2671
- name: z.string().trim().min(1).optional(),
2672
- attribution: z.string().optional(),
2673
- sourceLayers: sourceLayersSchema.optional()
2674
- });
2675
- var projectConfigSchema = z.object({
2676
- themes: z.record(z.string(), themeConfigSchema).optional(),
2677
- tilesets: z.record(z.string(), tilesetConfigSchema).optional(),
2678
- maps: z.record(z.string(), configSchema).refine((maps) => {
2679
- return Object.keys(maps).length > 0;
2680
- }, "Expected at least one map")
2681
- });
2682
- function validateConfig(input) {
2683
- if (isProjectConfigLike(input)) {
2684
- const parsed2 = projectConfigSchema.safeParse(input);
2685
- if (parsed2.success) {
2686
- const messages = validateProjectThemeReferences(parsed2.data);
2687
- if (messages.length > 0) {
2688
- return { valid: false, messages };
3543
+ "text-halo-width": 1.2
2689
3544
  }
2690
- return { valid: true, messages: [] };
2691
- }
2692
- return {
2693
- valid: false,
2694
- messages: parsed2.error.issues.map((issue) => ({
2695
- level: "error",
2696
- path: issue.path.join(".") || "config",
2697
- message: issue.message
2698
- }))
2699
- };
2700
- }
2701
- const parsed = configSchema.safeParse(input);
2702
- if (parsed.success) {
2703
- const messages = validateMapThemeReference(parsed.data, void 0, "theme");
2704
- if (messages.length > 0) {
2705
- return { valid: false, messages };
2706
- }
2707
- return { valid: true, messages: [] };
2708
- }
2709
- return {
2710
- valid: false,
2711
- messages: parsed.error.issues.map((issue) => ({
2712
- level: "error",
2713
- path: issue.path.join(".") || "config",
2714
- message: issue.message
2715
- }))
2716
- };
2717
- }
2718
- function isProjectConfigLike(input) {
2719
- return Boolean(
2720
- input && typeof input === "object" && "maps" in input && typeof input.maps === "object"
2721
- );
2722
- }
2723
- function validateProjectThemeReferences(project) {
2724
- const themeNames = new Set(Object.keys(project.themes ?? {}));
2725
- return Object.entries(project.maps).flatMap(
2726
- ([mapName, mapConfig]) => validateMapThemeReference(mapConfig, themeNames, `maps.${mapName}.theme`)
2727
- );
2728
- }
2729
- function validateMapThemeReference(config, themeNames, path) {
2730
- if (typeof config.theme !== "string") {
2731
- return [];
2732
- }
2733
- if (legacyThemeNames.has(config.theme) || themeNames?.has(config.theme)) {
2734
- return [];
2735
- }
2736
- return [
2737
- {
2738
- level: "error",
2739
- path,
2740
- message: `Unknown Tileflow theme "${config.theme}"`
2741
- }
2742
- ];
2743
- }
2744
-
2745
- // src/compiler.ts
2746
- var defaultSourceLayers = {
2747
- landuse: "landuse",
2748
- park: "park",
2749
- water: "water",
2750
- road: "transportation",
2751
- building: "building",
2752
- boundary: "boundary",
2753
- place: "place",
2754
- poi: "poi"
2755
- };
2756
- var defaultColors = {
2757
- canvas: "#F6F7F3",
2758
- land: "#F1F3ED",
2759
- water: "#A9D3F5",
2760
- nature: "#CDE8B5",
2761
- road: "#FFFFFF",
2762
- text: "#3C4043",
2763
- accent: "#F4C95D"
2764
- };
2765
- var defaultConfig = {
2766
- density: "balanced",
2767
- roads: "standard",
2768
- labels: "balanced",
2769
- poi: "minimal"
2770
- };
2771
- var themePresets = {
2772
- standard: {
2773
- colors: defaultColors,
2774
- density: "balanced",
2775
- labels: "balanced",
2776
- poi: "minimal",
2777
- roads: "standard"
2778
- },
2779
- light: {
2780
- colors: {
2781
- canvas: "#F9FAF8",
2782
- land: "#F6F7F3",
2783
- water: "#C5E1F5",
2784
- nature: "#DDEED2",
2785
- road: "#FFFFFF",
2786
- building: "#ECEAE4",
2787
- text: "#5F6368",
2788
- accent: "#C9D1D9"
2789
- },
2790
- density: "clean",
2791
- labels: "essential",
2792
- poi: "minimal",
2793
- roads: "standard"
2794
- },
2795
- dark: {
2796
- colors: {
2797
- canvas: "#1C2228",
2798
- land: "#232B32",
2799
- water: "#18384D",
2800
- nature: "#274230",
2801
- road: "#39434D",
2802
- building: "#2F363D",
2803
- text: "#D7DEE6",
2804
- accent: "#E4B857"
2805
3545
  },
2806
- density: "balanced",
2807
- labels: "balanced",
2808
- poi: "minimal",
2809
- roads: "standard"
2810
- },
2811
- minimal: {
2812
- colors: {
2813
- canvas: "#FAFAF8",
2814
- land: "#F7F7F2",
2815
- water: "#D6EAF6",
2816
- nature: "#E5EEDB",
2817
- road: "#FFFFFF",
2818
- building: "#F0EEE8",
2819
- text: "#697079",
2820
- accent: "#B8C0CA"
3546
+ {
3547
+ id: "place-village",
3548
+ type: "symbol",
3549
+ metadata: { "mapbox:group": "1444849242106.713" },
3550
+ source: "openmaptiles",
3551
+ "source-layer": "place",
3552
+ filter: ["==", "class", "village"],
3553
+ layout: {
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
+ }
2821
3565
  },
2822
- density: "clean",
2823
- labels: "essential",
2824
- poi: "none",
2825
- roads: "soft"
2826
- },
2827
- // "OSM Bright 2" — Google Maps-inspired palette. Warm-neutral land, soft
2828
- // blue water, muted green parks, white roads on a light gray canvas, and a
2829
- // warm yellow accent that drives the highway color once the class-based road
2830
- // engine lands. Tuned to read as a familiar navigation basemap.
2831
- "osm-bright-2": {
2832
- colors: {
2833
- canvas: "#EBE9E4",
2834
- land: "#F5F4F1",
2835
- water: "#AADAFF",
2836
- nature: "#C7E6A8",
2837
- road: "#FFFFFF",
2838
- building: "#EFEDE7",
2839
- text: "#5F6368",
2840
- 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
+ }
2841
3585
  },
2842
- density: "balanced",
2843
- labels: "balanced",
2844
- poi: "balanced",
2845
- roads: "detailed"
2846
- }
2847
- };
2848
- var defaultTypography = {
2849
- font: "Noto Sans",
2850
- weight: "regular"
2851
- };
2852
- var fontWeightLabels = {
2853
- regular: "Regular",
2854
- medium: "Medium",
2855
- semibold: "Semibold",
2856
- bold: "Bold"
2857
- };
2858
- var defaultTileBaseUrl = "https://api.tileflow.dev";
2859
- var defaultTileset = "world";
2860
- function createStyle(input = {}, options = {}) {
2861
- const parsed = configSchema.safeParse(input);
2862
- if (!parsed.success) {
2863
- const message = parsed.error.issues.map((issue) => `${issue.path.join(".") || "config"}: ${issue.message}`).join("; ");
2864
- throw new Error(`Invalid Tileflow config. ${message}`);
2865
- }
2866
- const config = parsed.data;
2867
- const basemap = config.basemap ?? osm();
2868
- const theme = resolveTheme(config.theme, options.themes);
2869
- const density = config.density ?? theme.preset.density ?? defaultConfig.density;
2870
- const roadModule = config.modules?.find(
2871
- (moduleConfig) => moduleConfig.type === "roads"
2872
- );
2873
- const roads2 = resolveRoads(config.roads, roadModule, theme.preset.roads ?? defaultConfig.roads);
2874
- const labels = config.labels ?? theme.preset.labels ?? defaultConfig.labels;
2875
- const poi = config.poi ?? theme.preset.poi ?? defaultConfig.poi;
2876
- const buildings = config.buildings ?? "flat";
2877
- const colors = resolveColors(config.colors, theme.colors);
2878
- const sourceId = config.tiles?.sourceId ?? basemap.sourceId ?? "tileflow";
2879
- const tileBaseUrl = normalizeBaseUrl(options.tileBaseUrl);
2880
- const tileset = options.tileset ?? config.tileset ?? config.tiles?.tileset ?? basemap.tileset ?? defaultTileset;
2881
- const tilesUrl = config.tiles?.url ?? basemap.url ?? `${tileBaseUrl}/tiles/${tileset}/tiles.json`;
2882
- const typography = resolveTypography(config.typography, theme.typography);
2883
- const terrain = resolveTerrain(config.terrain, tileBaseUrl);
2884
- const glyphs = config.glyphs ?? basemap.glyphs ?? `${tileBaseUrl}/fonts/{fontstack}/{range}.pbf`;
2885
- const sprite = config.sprite ?? basemap.sprite;
2886
- const attribution = config.tiles?.attribution ?? basemap.attribution ?? "\xA9 OpenMapTiles \xA9 OpenStreetMap contributors";
2887
- const metadata = {
2888
- "tileflow:basemap": basemap.type,
2889
- "tileflow:theme": theme.name,
2890
- "tileflow:themeMode": theme.mode,
2891
- "tileflow:density": density,
2892
- "tileflow:roads": roads2,
2893
- "tileflow:labels": labels,
2894
- "tileflow:poi": poi,
2895
- "tileflow:buildings": buildings,
2896
- "tileflow:modules": config.modules?.map((moduleConfig) => moduleConfig.type) ?? [],
2897
- "tileflow:terrain": terrain ? {
2898
- encoding: terrain.source.encoding,
2899
- mode: terrain.mode,
2900
- sourceId: terrain.sourceId
2901
- } : void 0,
2902
- "tileflow:typography": typography,
2903
- "tileflow:colors": colors,
2904
- "tileflow:tileBaseUrl": tileBaseUrl,
2905
- "tileflow:tileset": tileset,
2906
- "tileflow:tiles": "tileflow",
2907
- "tileflow:view": config.view
2908
- };
2909
- if (shouldUseOsmBrightTemplate(config, basemap, theme)) {
2910
- return createOsmBrightStyle({
2911
- attribution,
2912
- colors,
2913
- glyphs,
2914
- metadata,
2915
- name: config.name ?? "OSM Bright",
2916
- sourceId,
2917
- sprite,
2918
- terrain,
2919
- theme,
2920
- tilesUrl,
2921
- typography
2922
- });
2923
- }
2924
- const sourceLayers = resolveSourceLayers(
2925
3586
  {
2926
- ...defaultSourceLayers,
2927
- ...basemap.sourceLayers,
2928
- ...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
+ }
2929
3605
  },
2930
- void 0
2931
- );
2932
- const layers = openMapTilesLayers(sourceId, sourceLayers, colors, {
2933
- buildings,
2934
- density,
2935
- labels,
2936
- poi,
2937
- roads: roads2,
2938
- typography
2939
- });
2940
- const resolvedLayers = terrain ? withTerrainLayers(layers, terrain) : layers;
2941
- const style = {
2942
- version: 8,
2943
- name: config.name ?? "Tileflow",
2944
- glyphs,
2945
- ...sprite ? { sprite } : {},
2946
- sources: {
2947
- [sourceId]: {
2948
- type: "vector",
2949
- url: tilesUrl,
2950
- 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"
2951
3623
  },
2952
- ...terrain ? {
2953
- [terrain.sourceId]: terrain.source
2954
- } : {}
3624
+ paint: {
3625
+ "text-color": "#333",
3626
+ "text-halo-color": "rgba(255,255,255,0.8)",
3627
+ "text-halo-width": 1.2
3628
+ }
2955
3629
  },
2956
- layers: resolvedLayers,
2957
- ...terrain?.mode === "3d" ? {
2958
- terrain: {
2959
- exaggeration: terrain.exaggeration,
2960
- 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
2961
3650
  }
2962
- } : {},
2963
- metadata
2964
- };
2965
- return style;
2966
- }
2967
- function createStyleFromProject(projectConfig, mapName, options = {}) {
2968
- const config = projectConfig.maps[mapName];
2969
- if (!config) {
2970
- throw new Error(`Unknown Tileflow map: ${mapName}`);
2971
- }
2972
- const tilesetName = options.tileset ?? config.tileset ?? config.tiles?.tileset ?? config.basemap?.tileset;
2973
- const tilesetConfig = tilesetName ? projectConfig.tilesets?.[tilesetName] : void 0;
2974
- const resolvedTileset = tilesetConfig?.id ?? tilesetName ?? defaultTileset;
2975
- const baseBasemap = config.basemap ?? osm();
2976
- const { sourceLayers: tileSourceLayers, ...tileOptions } = config.tiles ?? {};
2977
- const resolvedBasemapSourceLayers = baseBasemap.sourceLayers || tilesetConfig?.sourceLayers ? resolveSourceLayers(baseBasemap.sourceLayers, tilesetConfig?.sourceLayers) : void 0;
2978
- const resolvedTileSourceLayers = baseBasemap.sourceLayers || !tileSourceLayers ? void 0 : resolveSourceLayers(tileSourceLayers, tilesetConfig?.sourceLayers);
2979
- return createStyle(
3651
+ },
2980
3652
  {
2981
- ...config,
2982
- tileset: resolvedTileset,
2983
- basemap: {
2984
- ...baseBasemap,
2985
- tileset: resolvedTileset,
2986
- attribution: baseBasemap.attribution ?? tilesetConfig?.attribution,
2987
- ...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"
2988
3671
  },
2989
- tiles: {
2990
- ...tileOptions,
2991
- tileset: resolvedTileset,
2992
- ...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
2993
3677
  }
2994
3678
  },
2995
3679
  {
2996
- ...options,
2997
- themes: projectConfig.themes,
2998
- tileset: resolvedTileset
2999
- }
3000
- );
3001
- }
3002
- function createManifest(projectConfig, options = {}) {
3003
- const styleBaseUrl = normalizeStyleBaseUrl(options.styleBaseUrl);
3004
- const tileBaseUrl = normalizeBaseUrl(options.tileBaseUrl);
3005
- const maps = Object.keys(projectConfig.maps);
3006
- return {
3007
- version: 1,
3008
- tileBaseUrl,
3009
- styles: Object.fromEntries(
3010
- maps.map((mapName) => [mapName, `${styleBaseUrl}/styles/${mapName}.json`])
3011
- ),
3012
- maps: Object.fromEntries(
3013
- maps.map((mapName) => [mapName, `${styleBaseUrl}/styles/${mapName}.json`])
3014
- )
3015
- };
3016
- }
3017
- function getDefaultColors() {
3018
- return defaultColors;
3019
- }
3020
- function resolveTheme(theme, projectThemes) {
3021
- if (!theme) {
3022
- return resolveLegacyTheme("light");
3023
- }
3024
- if (typeof theme === "string") {
3025
- const projectTheme = projectThemes?.[theme];
3026
- if (projectTheme) {
3027
- return resolveThemeConfig(theme, projectTheme);
3028
- }
3029
- if (isLegacyTheme(theme)) {
3030
- return resolveLegacyTheme(theme);
3031
- }
3032
- throw new Error(`Unknown Tileflow theme: ${theme}`);
3033
- }
3034
- return resolveThemeConfig("inline", theme);
3035
- }
3036
- function resolveLegacyTheme(theme) {
3037
- const preset = themePresets[theme];
3038
- return {
3039
- colors: preset.colors,
3040
- custom: false,
3041
- mode: theme === "dark" ? "dark" : "light",
3042
- name: theme,
3043
- preset,
3044
- typography: defaultTypography
3045
- };
3046
- }
3047
- function resolveThemeConfig(name, theme) {
3048
- const colors = normalizeColorAliases(theme.colors);
3049
- const preset = theme.mode === "dark" ? themePresets.dark : theme.mode === "light" ? themePresets.light : themePresets.standard;
3050
- return {
3051
- colors: {
3052
- ...preset.colors,
3053
- ...colors
3054
- },
3055
- custom: true,
3056
- mode: theme.mode,
3057
- name,
3058
- preset,
3059
- typography: resolveTypography(theme.typography, defaultTypography)
3060
- };
3061
- }
3062
- function isLegacyTheme(value) {
3063
- return value in themePresets;
3064
- }
3065
- function normalizeColorAliases(colors) {
3066
- if (!colors) {
3067
- return {};
3068
- }
3069
- return {
3070
- ...colors,
3071
- canvas: colors.canvas ?? colors.background,
3072
- nature: colors.nature ?? colors.park
3073
- };
3074
- }
3075
- function resolveRoads(legacyRoads, moduleConfig, fallback) {
3076
- if (!moduleConfig) {
3077
- return legacyRoads ?? fallback;
3078
- }
3079
- if (moduleConfig.preset === "minimal" || moduleConfig.density === "clean") {
3080
- return "soft";
3081
- }
3082
- if (moduleConfig.density === "dense") {
3083
- return "detailed";
3084
- }
3085
- return "standard";
3086
- }
3087
- function resolveColors(overrides = {}, defaults = defaultColors) {
3088
- const colors = {
3089
- ...defaults,
3090
- canvas: overrides.canvas ?? overrides.background ?? defaults.canvas ?? defaultColors.canvas,
3091
- land: overrides.land ?? defaults.land ?? defaultColors.land,
3092
- water: overrides.water ?? defaults.water ?? defaultColors.water,
3093
- nature: overrides.nature ?? overrides.park ?? defaults.nature ?? defaults.park ?? defaultColors.nature,
3094
- road: overrides.road ?? defaults.road ?? defaultColors.road,
3095
- text: overrides.text ?? defaults.text ?? defaultColors.text,
3096
- accent: overrides.accent ?? defaults.accent ?? defaultColors.accent,
3097
- building: overrides.building ?? defaults.building,
3098
- roadCasing: overrides.roadCasing ?? defaults.roadCasing
3099
- };
3100
- const building = colors.building ?? mix(colors.land, colors.text, 0.08);
3101
- const roadCasing = colors.roadCasing ?? mix(colors.road, colors.text, 0.2);
3102
- return {
3103
- canvas: cssColor(colors.canvas),
3104
- land: cssColor(colors.land),
3105
- water: cssColor(colors.water),
3106
- nature: cssColor(colors.nature),
3107
- road: cssColor(colors.road),
3108
- text: cssColor(colors.text),
3109
- accent: cssColor(colors.accent),
3110
- building: cssColor(building),
3111
- roadCasing: cssColor(roadCasing),
3112
- roadMajor: mix(colors.road, colors.accent, 0.16),
3113
- roadMinor: cssColor(colors.road),
3114
- textHalo: alpha(colors.canvas, 0.84),
3115
- textMuted: mix(colors.text, colors.canvas, 0.42)
3116
- };
3117
- }
3118
- function resolveTerrain(terrain, tileBaseUrl) {
3119
- if (!terrain || terrain === "none") {
3120
- return void 0;
3121
- }
3122
- const terrainConfig = typeof terrain === "string" ? { mode: terrain } : terrain;
3123
- const mode = terrainConfig.mode ?? "hillshade";
3124
- if (mode === "none") {
3125
- return void 0;
3126
- }
3127
- const sourceId = terrainConfig.sourceId ?? "tileflow-terrain";
3128
- const tileset = terrainConfig.tileset ?? "terrain";
3129
- const url = terrainConfig.url ?? `${tileBaseUrl}/tiles/${tileset}/tiles.json`;
3130
- const encoding = terrainConfig.encoding ?? "terrarium";
3131
- return {
3132
- exaggeration: terrainConfig.exaggeration ?? 1.2,
3133
- mode,
3134
- sourceId,
3135
- source: {
3136
- attribution: terrainConfig.attribution,
3137
- encoding,
3138
- type: "raster-dem",
3139
- url
3140
- }
3141
- };
3142
- }
3143
- function resolveTypography(overrides = {}, defaults = defaultTypography) {
3144
- return {
3145
- ...defaults,
3146
- ...overrides
3147
- };
3148
- }
3149
- function resolveSourceLayers(mapLayers, tilesetLayers) {
3150
- return {
3151
- ...defaultSourceLayers,
3152
- ...tilesetLayers,
3153
- ...mapLayers
3154
- };
3155
- }
3156
- function shouldUseOsmBrightTemplate(config, basemap, theme) {
3157
- const templateTheme = theme.custom || theme.name === "light" || theme.name === "dark";
3158
- 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;
3159
- }
3160
- function createOsmBrightStyle(options) {
3161
- const template = cloneJson(style_default);
3162
- const layers = template.layers.map((layer) => remapOsmBrightSource(layer, options.sourceId));
3163
- const themedLayers = applyOsmBrightTheme(
3164
- layers,
3165
- options.colors,
3166
- options.theme,
3167
- options.typography
3168
- );
3169
- const resolvedLayers = options.terrain ? withTerrainLayers(themedLayers, options.terrain) : themedLayers;
3170
- return {
3171
- ...template,
3172
- name: options.name,
3173
- glyphs: options.glyphs,
3174
- ...options.sprite || template.sprite ? { sprite: options.sprite ?? template.sprite } : {},
3175
- sources: {
3176
- [options.sourceId]: {
3177
- type: "vector",
3178
- url: options.tilesUrl,
3179
- 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"
3180
3698
  },
3181
- ...options.terrain ? {
3182
- [options.terrain.sourceId]: options.terrain.source
3183
- } : {}
3184
- },
3185
- layers: resolvedLayers,
3186
- ...options.terrain?.mode === "3d" ? {
3187
- terrain: {
3188
- exaggeration: options.terrain.exaggeration,
3189
- source: options.terrain.sourceId
3190
- }
3191
- } : {},
3192
- metadata: {
3193
- ...template.metadata ?? {},
3194
- ...options.metadata,
3195
- "tileflow:template": "openmaptiles-osm-bright"
3196
- }
3197
- };
3198
- }
3199
- function cloneJson(value) {
3200
- return JSON.parse(JSON.stringify(value));
3201
- }
3202
- function remapOsmBrightSource(layer, sourceId) {
3203
- if (layer.source !== "openmaptiles") {
3204
- return layer;
3205
- }
3206
- return {
3207
- ...layer,
3208
- source: sourceId
3209
- };
3210
- }
3211
- function applyOsmBrightTheme(layers, colors, theme, typography) {
3212
- if (!theme.custom && theme.name === "light") {
3213
- return layers;
3214
- }
3215
- return layers.map((layer) => {
3216
- const type = stringValue(layer.type);
3217
- const sourceLayer = stringValue(layer["source-layer"]);
3218
- const id = stringValue(layer.id);
3219
- const nextLayer = { ...layer };
3220
- const paint = isRecord(layer.paint) ? { ...layer.paint } : {};
3221
- const layout = isRecord(layer.layout) ? { ...layer.layout } : {};
3222
- let paintChanged = false;
3223
- let layoutChanged = false;
3224
- if (type === "background") {
3225
- paint["background-color"] = colors.canvas;
3226
- paintChanged = true;
3227
- }
3228
- if (type === "fill") {
3229
- const fillColor = osmBrightFillColor(id, sourceLayer, colors);
3230
- if (fillColor) {
3231
- paint["fill-color"] = fillColor;
3232
- paintChanged = true;
3233
- }
3234
- }
3235
- if (type === "line") {
3236
- const lineColor = osmBrightLineColor(id, sourceLayer, colors);
3237
- if (lineColor) {
3238
- paint["line-color"] = lineColor;
3239
- 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
3240
3704
  }
3241
- }
3242
- if (type === "symbol") {
3243
- const textColor = osmBrightTextColor(id, sourceLayer, colors);
3244
- if (textColor && ("text-color" in paint || id.includes("label") || id.includes("name"))) {
3245
- paint["text-color"] = textColor;
3246
- 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
3247
3731
  }
3248
- if ("text-halo-color" in paint) {
3249
- paint["text-halo-color"] = colors.textHalo;
3250
- 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
3251
3758
  }
3252
- if (theme.custom && "text-font" in layout) {
3253
- layout["text-font"] = textFont(typography);
3254
- 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
3255
3781
  }
3256
3782
  }
3257
- if (paintChanged) {
3258
- nextLayer.paint = paint;
3259
- }
3260
- if (layoutChanged) {
3261
- nextLayer.layout = layout;
3262
- }
3263
- return nextLayer;
3264
- });
3265
- }
3266
- function osmBrightFillColor(id, sourceLayer, colors) {
3267
- if (sourceLayer === "water" || id.includes("water")) {
3268
- return colors.water;
3269
- }
3270
- if (sourceLayer === "building") {
3271
- return colors.building;
3272
- }
3273
- if (sourceLayer === "landcover" || id.includes("park") || id.includes("wood")) {
3274
- return colors.nature;
3275
- }
3276
- if (sourceLayer === "landuse" || id.includes("landuse")) {
3277
- return colors.land;
3278
- }
3279
- return void 0;
3280
- }
3281
- function osmBrightLineColor(id, sourceLayer, colors) {
3282
- if (sourceLayer === "transportation") {
3283
- return id.includes("case") || id.includes("casing") ? colors.roadCasing : colors.road;
3284
- }
3285
- if (sourceLayer === "waterway") {
3286
- return colors.water;
3287
- }
3288
- if (sourceLayer === "boundary") {
3289
- return colors.textMuted;
3290
- }
3291
- return void 0;
3292
- }
3293
- function osmBrightTextColor(id, sourceLayer, colors) {
3294
- if (sourceLayer === "water_name" || id.includes("water")) {
3295
- 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;
3296
3791
  }
3297
- if (sourceLayer === "transportation_name") {
3298
- 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;
3299
3796
  }
3300
- return colors.text;
3301
- }
3302
- function isRecord(value) {
3303
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
3304
- }
3305
- function stringValue(value) {
3306
- return typeof value === "string" ? value : "";
3307
- }
3308
- function normalizeBaseUrl(value) {
3309
- return normalizeUrl(value ?? defaultTileBaseUrl);
3310
- }
3311
- function normalizeStyleBaseUrl(value) {
3312
- return normalizeUrl(value ?? "");
3313
- }
3314
- function normalizeUrl(value) {
3315
- return value.replace(/\/+$/, "");
3316
- }
3317
- function textFont(typography) {
3318
- return [`${typography.font} ${fontWeightLabels[typography.weight]}`];
3319
- }
3320
- 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";
3321
3801
  return {
3322
- id,
3323
- type: "fill",
3324
- source,
3325
- "source-layer": sourceLayer,
3326
- paint: {
3327
- "fill-color": color,
3328
- "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
3329
3810
  }
3330
3811
  };
3331
3812
  }
3332
- function openMapTilesLayers(source, sourceLayers, colors, options) {
3333
- return [
3334
- {
3335
- id: "background",
3336
- type: "background",
3337
- paint: {
3338
- "background-color": colors.canvas
3339
- }
3340
- },
3341
- fillLayer("landuse", source, sourceLayers.landuse, colors.land, 0.42),
3342
- fillLayer("parks", source, sourceLayers.park, colors.nature, parkOpacity(options.density)),
3343
- fillLayer("water", source, sourceLayers.water, colors.water, 1),
3344
- ...buildingLayers(source, sourceLayers.building, colors, options.density, options.buildings),
3345
- ...roadLayers(source, sourceLayers.road, colors, options.roads),
3346
- ...boundaryLayers(source, sourceLayers.boundary, colors),
3347
- ...labelLayers(source, sourceLayers.place, colors, options.labels, options.typography),
3348
- ...poiLayers(source, sourceLayers.poi, colors, options.poi, options.typography)
3349
- ];
3350
- }
3351
3813
  function withTerrainLayers(layers, terrain) {
3352
3814
  const hillshadeLayer = {
3353
3815
  id: "terrain-hillshade",
@@ -3371,335 +3833,559 @@ function withTerrainLayers(layers, terrain) {
3371
3833
  ...layers.slice(insertionIndex + 1)
3372
3834
  ];
3373
3835
  }
3374
- function buildingLayers(source, sourceLayer, colors, density, buildings) {
3375
- if (buildings === "hidden") {
3376
- return [];
3377
- }
3378
- if (buildings === "3d") {
3379
- return [
3380
- {
3381
- id: "buildings-3d",
3382
- type: "fill-extrusion",
3383
- source,
3384
- "source-layer": sourceLayer,
3385
- minzoom: 14,
3386
- filter: ["!=", ["get", "hide_3d"], true],
3387
- paint: {
3388
- "fill-extrusion-base": [
3389
- "case",
3390
- ["has", "render_min_height"],
3391
- ["to-number", ["get", "render_min_height"]],
3392
- ["has", "min_height"],
3393
- ["to-number", ["get", "min_height"]],
3394
- 0
3395
- ],
3396
- "fill-extrusion-color": colors.building,
3397
- "fill-extrusion-height": [
3398
- "interpolate",
3399
- ["linear"],
3400
- ["zoom"],
3401
- 14,
3402
- 0,
3403
- 15,
3404
- [
3405
- "case",
3406
- ["has", "render_height"],
3407
- ["to-number", ["get", "render_height"]],
3408
- ["has", "height"],
3409
- ["to-number", ["get", "height"]],
3410
- 18
3411
- ]
3412
- ],
3413
- "fill-extrusion-opacity": 0.72
3414
- }
3415
- }
3416
- ];
3417
- }
3418
- if (density === "clean") {
3419
- return [
3420
- {
3421
- id: "buildings-soft",
3422
- type: "fill",
3423
- source,
3424
- "source-layer": sourceLayer,
3425
- minzoom: 14,
3426
- paint: {
3427
- "fill-color": colors.building,
3428
- "fill-opacity": 0.38
3429
- }
3430
- }
3431
- ];
3432
- }
3433
- return [
3434
- {
3435
- id: "buildings",
3436
- type: "fill",
3437
- source,
3438
- "source-layer": sourceLayer,
3439
- minzoom: 13,
3440
- paint: {
3441
- "fill-color": colors.building,
3442
- "fill-opacity": density === "dense" ? 0.62 : 0.5
3443
- }
3444
- },
3445
- {
3446
- id: "buildings-outline",
3447
- type: "line",
3448
- source,
3449
- "source-layer": sourceLayer,
3450
- minzoom: 15,
3451
- paint: {
3452
- "line-color": mix(colors.building, colors.text, 0.2),
3453
- "line-opacity": 0.24,
3454
- "line-width": 0.6
3455
- }
3456
- }
3457
- ];
3458
- }
3459
- function roadLayers(source, sourceLayer, colors, roads2) {
3460
- if (roads2 === "hidden") {
3461
- return [];
3462
- }
3463
- const opacity = roads2 === "soft" ? 0.72 : roads2 === "standard" ? 0.84 : 0.96;
3464
- const casingOpacity = roads2 === "detailed" ? 0.3 : 0.16;
3465
- const majorRoadFilter = [
3466
- "match",
3467
- ["get", "class"],
3468
- ["motorway", "trunk", "primary", "secondary"],
3469
- true,
3470
- false
3471
- ];
3472
- const minorRoadFilter = ["!", majorRoadFilter];
3473
- const nonTunnelRoadFilter = ["!=", ["get", "brunnel"], "tunnel"];
3474
- return [
3475
- {
3476
- id: "roads-tunnels-casing",
3477
- type: "line",
3478
- source,
3479
- "source-layer": sourceLayer,
3480
- minzoom: 10,
3481
- filter: ["==", ["get", "brunnel"], "tunnel"],
3482
- paint: {
3483
- "line-color": colors.roadCasing,
3484
- "line-dasharray": [0.6, 0.8],
3485
- "line-opacity": 0.28,
3486
- "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;
3487
3860
  }
3488
- },
3489
- {
3490
- id: "roads-tunnels",
3491
- type: "line",
3492
- source,
3493
- "source-layer": sourceLayer,
3494
- minzoom: 10,
3495
- filter: ["==", ["get", "brunnel"], "tunnel"],
3496
- paint: {
3497
- "line-color": colors.roadMinor,
3498
- "line-dasharray": [0.6, 0.8],
3499
- "line-opacity": 0.2,
3500
- "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;
3501
3867
  }
3502
- },
3503
- {
3504
- id: "roads-casing",
3505
- type: "line",
3506
- source,
3507
- "source-layer": sourceLayer,
3508
- minzoom: 6,
3509
- filter: nonTunnelRoadFilter,
3510
- paint: {
3511
- "line-color": colors.roadCasing,
3512
- "line-opacity": casingOpacity,
3513
- "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;
3514
3874
  }
3515
- },
3516
- {
3517
- id: "roads-minor",
3518
- type: "line",
3519
- source,
3520
- "source-layer": sourceLayer,
3521
- minzoom: 6,
3522
- filter: ["all", nonTunnelRoadFilter, minorRoadFilter],
3523
- paint: {
3524
- "line-color": colors.roadMinor,
3525
- "line-opacity": opacity * 0.88,
3526
- "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;
3527
3878
  }
3528
- },
3529
- {
3530
- id: "roads-major",
3531
- type: "line",
3532
- source,
3533
- "source-layer": sourceLayer,
3534
- minzoom: 5,
3535
- filter: ["all", nonTunnelRoadFilter, majorRoadFilter],
3536
- paint: {
3537
- "line-color": colors.roadMajor,
3538
- "line-opacity": opacity,
3539
- "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;
3540
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
+ } : {}
3541
4117
  },
3542
- {
3543
- id: "roads-bridges",
3544
- type: "line",
3545
- source,
3546
- "source-layer": sourceLayer,
3547
- minzoom: 10,
3548
- filter: ["==", ["get", "brunnel"], "bridge"],
3549
- paint: {
3550
- "line-color": mix(colors.roadMajor, colors.canvas, 0.14),
3551
- "line-opacity": opacity,
3552
- "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
3553
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
3554
4178
  }
3555
- ];
3556
- }
3557
- function boundaryLayers(source, sourceLayer, colors) {
3558
- return [
4179
+ ] : [];
4180
+ const mapLayerOverrideModules = config.layers ? [
3559
4181
  {
3560
- id: "boundaries",
3561
- type: "line",
3562
- source,
3563
- "source-layer": sourceLayer,
3564
- minzoom: 2,
3565
- paint: {
3566
- "line-color": mix(colors.text, colors.canvas, 0.7),
3567
- "line-opacity": 0.35,
3568
- "line-width": ["interpolate", ["linear"], ["zoom"], 2, 0.35, 8, 0.8]
3569
- }
4182
+ type: "styleOverride",
4183
+ layers: config.layers
3570
4184
  }
4185
+ ] : [];
4186
+ const styleOverrideModules = [
4187
+ ...themeLayerOverrideModules,
4188
+ ...mapLayerOverrideModules,
4189
+ ...config.modules?.filter(
4190
+ (moduleConfig) => moduleConfig.type === "styleOverride"
4191
+ ) ?? []
3571
4192
  ];
3572
- }
3573
- function labelLayers(source, sourceLayer, colors, labels, typography) {
3574
- if (labels === "none") {
3575
- 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
+ };
3576
4251
  }
3577
- const maxRank = labels === "essential" ? 3 : labels === "balanced" ? 7 : 12;
3578
- return [
4252
+ const sourceLayers = resolveSourceLayers(
3579
4253
  {
3580
- id: "place-labels",
3581
- type: "symbol",
3582
- source,
3583
- "source-layer": sourceLayer,
3584
- minzoom: 3,
3585
- filter: ["<=", ["coalesce", ["get", "rank"], 10], maxRank],
3586
- layout: {
3587
- "text-field": ["coalesce", ["get", "name:en"], ["get", "name"]],
3588
- "text-font": textFont(typography),
3589
- "text-size": ["interpolate", ["linear"], ["zoom"], 3, 10, 8, 13, 14, 17],
3590
- "text-letter-spacing": 0,
3591
- "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
3592
4280
  },
3593
- paint: {
3594
- "text-color": colors.text,
3595
- "text-halo-color": colors.textHalo,
3596
- "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
3597
4290
  }
3598
- }
3599
- ];
4291
+ } : {},
4292
+ metadata
4293
+ };
4294
+ return style;
3600
4295
  }
3601
- function poiLayers(source, sourceLayer, colors, poi, typography) {
3602
- if (poi === "none") {
3603
- return [];
4296
+ function createStyleFromProject(projectConfig, mapName, options = {}) {
4297
+ const config = projectConfig.maps[mapName];
4298
+ if (!config) {
4299
+ throw new Error(`Unknown Tileflow map: ${mapName}`);
3604
4300
  }
3605
- const minzoom = poi === "minimal" ? 15 : poi === "balanced" ? 13 : 11;
3606
- 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(
3607
4309
  {
3608
- id: "poi-labels",
3609
- type: "symbol",
3610
- source,
3611
- "source-layer": sourceLayer,
3612
- minzoom,
3613
- layout: {
3614
- "text-field": ["coalesce", ["get", "name:en"], ["get", "name"]],
3615
- "text-font": textFont(typography),
3616
- "text-size": ["interpolate", ["linear"], ["zoom"], minzoom, 10, 17, 12],
3617
- "text-letter-spacing": 0,
3618
- "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 } : {}
3619
4317
  },
3620
- paint: {
3621
- "text-color": mix(colors.textMuted, colors.accent, 0.22),
3622
- "text-halo-color": alpha(colors.canvas, 0.78),
3623
- "text-halo-width": 1
4318
+ tiles: {
4319
+ ...tileOptions,
4320
+ tileset: resolvedTileset,
4321
+ ...resolvedTileSourceLayers ? { sourceLayers: resolvedTileSourceLayers } : {}
3624
4322
  }
4323
+ },
4324
+ {
4325
+ ...options,
4326
+ themes: projectConfig.themes,
4327
+ tileset: resolvedTileset
3625
4328
  }
3626
- ];
3627
- }
3628
- function parkOpacity(density) {
3629
- return density === "clean" ? 0.64 : density === "balanced" ? 0.78 : 0.9;
3630
- }
3631
- function alpha(color, opacity) {
3632
- const { a, b, g, r } = hexToRgba(color);
3633
- return `rgba(${r}, ${g}, ${b}, ${roundAlpha(a * opacity)})`;
4329
+ );
3634
4330
  }
3635
- function mix(hexA, hexB, amount) {
3636
- const a = hexToRgb(hexA);
3637
- const b = hexToRgb(hexB);
3638
- const r = Math.round(a.r + (b.r - a.r) * amount);
3639
- const g = Math.round(a.g + (b.g - a.g) * amount);
3640
- const bValue = Math.round(a.b + (b.b - a.b) * amount);
3641
- 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
+ };
3642
4345
  }
3643
- function hexToRgb(hex) {
3644
- const { b, g, r } = hexToRgba(hex);
3645
- return { b, g, r };
4346
+ function getDefaultColors2() {
4347
+ return getDefaultColors();
3646
4348
  }
3647
- function hexToRgba(hex) {
3648
- const rgbMatch = hex.match(
3649
- /^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)(?:\s*,\s*(\d+(?:\.\d+)?))?\s*\)$/
3650
- );
3651
- if (rgbMatch) {
3652
- return {
3653
- a: roundAlpha(Number(rgbMatch[4] ?? 1)),
3654
- r: Math.round(Number(rgbMatch[1])),
3655
- g: Math.round(Number(rgbMatch[2])),
3656
- b: Math.round(Number(rgbMatch[3]))
3657
- };
3658
- }
3659
- const value = hex.replace("#", "");
3660
- const normalized = value.length === 3 || value.length === 4 ? value.split("").map((character) => character + character).join("") : value;
4349
+ function resolveSourceLayers(mapLayers, tilesetLayers) {
3661
4350
  return {
3662
- a: normalized.length === 8 ? roundAlpha(Number.parseInt(normalized.slice(6, 8), 16) / 255) : 1,
3663
- r: Number.parseInt(normalized.slice(0, 2), 16),
3664
- g: Number.parseInt(normalized.slice(2, 4), 16),
3665
- b: Number.parseInt(normalized.slice(4, 6), 16)
4351
+ ...defaultSourceLayers,
4352
+ ...tilesetLayers,
4353
+ ...mapLayers
3666
4354
  };
3667
4355
  }
3668
- function cssColor(hex) {
3669
- const value = hex.replace("#", "");
3670
- if (value.length !== 4 && value.length !== 8) {
3671
- return hex;
3672
- }
3673
- const { a, b, g, r } = hexToRgba(hex);
3674
- 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;
3675
4359
  }
3676
- function roundAlpha(value) {
3677
- return Math.round(value * 1e3) / 1e3;
4360
+ function normalizeBaseUrl(value) {
4361
+ return normalizeUrl(value ?? defaultTileBaseUrl);
3678
4362
  }
3679
- function rgbToHex(r, g, b) {
3680
- 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(/\/+$/, "");
3681
4368
  }
3682
4369
 
3683
4370
  // src/config.ts
3684
4371
  function defineTileflow(config) {
3685
4372
  return config;
3686
4373
  }
3687
-
3688
- // src/modules.ts
3689
- function roads(options = {}) {
3690
- return {
3691
- type: "roads",
3692
- preset: options.preset ?? "modern",
3693
- ...options.density ? { density: options.density } : {}
3694
- };
3695
- }
3696
4374
  export {
4375
+ configSchema,
3697
4376
  createManifest,
3698
4377
  createStyle,
3699
4378
  createStyleFromProject,
3700
4379
  defineTileflow,
3701
- getDefaultColors,
4380
+ getDefaultColors2 as getDefaultColors,
3702
4381
  osm,
4382
+ parseTileflowMap,
4383
+ parseTileflowProject,
3703
4384
  roads,
3704
- validateConfig
4385
+ styleOverride,
4386
+ tileflowMapSchema,
4387
+ tileflowProjectSchema,
4388
+ tileflowThemeSchema,
4389
+ validateConfig,
4390
+ validateTileflowConfig
3705
4391
  };