@unmap/themes 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,8 +32,8 @@ const doc = await decodeTheme(code) // validated ThemeDoc, or th
32
32
  ## What is in a theme
33
33
 
34
34
  - `base` picks the style: `base`, `muted`, `outdoor`, `blueprint`, `blush`, `orchid`, `canopy`, `lagoon`, `tropic`, `sunset`, `bold`, `pastel`, `atlas` or `swiss`. Each of `light` and `dark` carries
35
- sparse overrides on that style, so a theme with no overrides renders exactly like its
36
- `flavor=` twin.
35
+ sparse overrides on that style, so a theme with no overrides renders exactly like the style
36
+ itself at `/styles/<style>.json`.
37
37
  - `basemap` overrides are colours on the style's recipe (land, water, vegetation, roads, labels,
38
38
  points of interest, landcover). `marker` styles the geocoding pin (`color`, `scale` 0.5 to 2).
39
39
  `route` styles the routing line (`color`, `width` 1 to 12, `opacity`, and a casing).
@@ -45,7 +45,9 @@ const doc = await decodeTheme(code) // validated ThemeDoc, or th
45
45
  - `encodeTheme(doc)` and `decodeTheme(code)`: async, validate on both sides, throw `ThemeError`
46
46
  with a `detail` array naming each problem. Simple themes encode as a short `u…` preset
47
47
  (three characters and up); advanced overrides stay `ut1.` + deflate. Codes are at most 4096
48
- characters. Existing `ut1.` codes keep decoding.
48
+ characters. The `ut1` prefix is the wire version and bumps on a breaking change to the
49
+ document format. The short codes pack a style slot and a preset index; both tables grow only
50
+ at the end and keep their positions, and a retired slot is a tombstone that does not decode.
49
51
  - `validateTheme(input)`: the synchronous validator, returns a typed `ThemeDoc`.
50
52
  - `resolveTheme(doc, mode)`: what the gateway calls. Returns the style's flavor name, the
51
53
  composed colour flavor, and the resolved `marker` and `route` styles with defaults filled in.
package/dist/index.d.ts CHANGED
@@ -174,9 +174,9 @@ declare const mist: {
174
174
  readonly 980: "#0D0D0D";
175
175
  };
176
176
  /**
177
- * Jade, hue 163. The `decimal` pair: one green family on near-black (dark) or near-white
178
- * (light), nothing else. Same lightness ladder as `ink`, so every label step the other styles
179
- * rely on clears the floors here too. Chroma rises with lightness up to the mid steps
177
+ * Jade, hue 163. The emerald and mint greens of `orchid`'s woods and of the parks and woods in
178
+ * `tropic`, `bold` and `pastel`. Same lightness ladder as `ink`, so every label step the other
179
+ * styles rely on clears the floors here too. Chroma rises with lightness up to the mid steps
180
180
  * (`0.16·L`) and eases off toward white (`0.42·(1−L)`), which is the profile of the
181
181
  * reference's own four greens (`#142a21` → `#50a882`).
182
182
  */
@@ -460,15 +460,11 @@ declare const swissRelief: {
460
460
  * The shipped styles. Appended, never inserted: this array's order drives the style picker in
461
461
  * the unmap.dev builder, so inserting a style mid-array silently reorders a control people have
462
462
  * already learned.
463
- *
464
- * `relief`, `street` and `decimal` were retired; they are not listed here. Short preset codes
465
- * still decode those slots via `normalizeThemeBase` / the slot table in `preset.ts`. `decimal`
466
- * (2026-09-09) maps onto `blueprint`: the two single-hue styles read as the same map.
467
463
  */
468
464
  declare const THEME_BASES: readonly ["base", "muted", "outdoor", "blueprint", "blush", "orchid", "canopy", "lagoon", "tropic", "sunset", "bold", "pastel", "atlas", "swiss"];
469
465
  type ThemeBase = (typeof THEME_BASES)[number];
470
- /** Map a theme `base` — living or retired — onto a shipped style. `null` if unknown. */
471
- declare function normalizeThemeBase(base: string): ThemeBase | null;
466
+ /** Whether `base` names a shipped style. */
467
+ declare function isThemeBase(base: unknown): base is ThemeBase;
472
468
  declare const MODES: readonly ["light", "dark"];
473
469
  type ThemeMode = (typeof MODES)[number];
474
470
  interface RoadRamp {
@@ -707,8 +703,8 @@ declare function validateTheme(input: unknown): ThemeDoc;
707
703
  * `deflate-raw`, which Node 20 — the repo floor — does not implement; the wrapper costs six
708
704
  * bytes and buys a checksum.
709
705
  *
710
- * `ut1` is the wire version. Bump it only for a breaking change, and keep decoding old
711
- * prefixes forever: codes get hardcoded into customers' pages.
706
+ * `ut1` is the wire version. A breaking change to the document format bumps it, and the decoder
707
+ * reads the current prefix only.
712
708
  */
713
709
  declare const THEME_CODE_PREFIX = "ut1.";
714
710
  declare const MAX_CODE_LENGTH = 4096;
@@ -726,8 +722,7 @@ declare function decodeTheme(code: string): Promise<ThemeDoc>;
726
722
  * enumerated choices that starts at three characters and grows only when the theme leaves the
727
723
  * curated set.
728
724
  *
729
- * u00 … u0C the style slots, in lineup order (`u03` was `decimal`, retired 2026-09-09;
730
- * it decodes to `blueprint`, and `blueprint` still encodes as `u04`)
725
+ * u00 … u0E the style slots, in lineup order (`u03` is a tombstone and does not decode)
731
726
  * u0W, u4e, … a named theme preset on a style (three characters for the first fifteen
732
727
  * presets; `tamarack`, the sixteenth, is the first to take four)
733
728
  * six to eight a style or preset with layer groups hidden or reordered
@@ -746,10 +741,10 @@ declare function decodeTheme(code: string): Promise<ThemeDoc>;
746
741
  * by a 13-bit Lehmer index of the draw order (0 = stock order)
747
742
  *
748
743
  * Anything left above the last field is an error, so a truncated or padded code fails rather
749
- * than decoding to a theme nobody asked for. Both lookup tables are append-only: a new style
750
- * or preset goes on the end, so a code already printed keeps meaning the same theme.
751
- * (The layout itself was re-cut on 2026-09-07 with no compatibility for the codes the earlier
752
- * region-and-lane scheme printed; that is the one time it happens.)
744
+ * than decoding to a theme nobody asked for. Both lookup tables only grow at the end and never
745
+ * reuse a position: the index is bit-packed into every code, so removing an entry would
746
+ * renumber every code after it. A retired entry stays as a `null` tombstone that fails to
747
+ * decode. (The layout itself was re-cut on 2026-09-07.)
753
748
  */
754
749
  declare const PRESET_CODE_PREFIX = "u";
755
750
  declare const PRESET_CODE_RE: RegExp;
@@ -867,7 +862,8 @@ declare function contrastWarnings(doc: ThemeDoc): ContrastWarning[];
867
862
 
868
863
  /**
869
864
  * The shipped styles as starting-point themes: zero overrides, so each is guaranteed identical
870
- * to its `flavor=` twin (asserted in the gateway theme tests). The builder opens on these.
865
+ * to the style it names, served at `/styles/<style>.json` (asserted in the gateway theme tests).
866
+ * The builder opens on these.
871
867
  *
872
868
  * Named after the style itself — `base`, `muted`, `outdoor`, `blueprint`, `blush`, … — the same word the homepage, the
873
869
  * SDK's `style` option and the gateway URL use. The theme presets (`process ink`, `boreal`, …)
@@ -956,4 +952,4 @@ interface PaletteColor {
956
952
  */
957
953
  declare function seedsFromPalette(colors: PaletteColor[]): Required<ThemeSeeds>;
958
954
 
959
- export { BUILDINGS_3D_LAYER_ID, type BasemapOverrides, type ContrastWarning, DEFAULT_LAYER_VISIBILITY, DEFAULT_MARKER, DEFAULT_ROUTE, DEFAULT_SEEDS, type DeepPartial, LABEL_PAIRS, LAYER_GROUPS, type LabelRamp, type LayerGroup, MAX_CODE_LENGTH, MODES, type MarkerTheme, type Oklch, PRESET_CODE_PREFIX, PRESET_CODE_RE, type PaletteColor, RECIPES, type RampSet, type Recipe, type ResolvedTheme, type RoadRamp, type RouteTheme, STOCK_RAMPS, STYLE_THEMES, THEME_BASES, THEME_CODE_PREFIX, THEME_PRESETS, type ThemeBase, type ThemeDoc, ThemeError, type ThemeHalf, type ThemeLayers, type ThemeMode, type ThemePreset, type ThemeSeeds, amber, applyThemeLayers, aqua, atlasGreen, atlasPaper, atlasRelief, atlasRoad, atlasWater, azure, buildRecipes, cobalt, contrast, contrastWarnings, coral, decodePreset, decodeTheme, effectiveLayerOrder, ember, encodePreset, encodeTheme, fern, generateTheme, graphite, hexToOklch, indigo, ink, isLayerVisible, isStockLayerOrder, isStockLayers, isThemePresetCode, jade, layerGroupOf, lilac, linen, makeFlavor, mergeRecipe, mist, normalizeThemeBase, oklchToHex, paper, peach, presetId, registryThemeKey, resolveTheme, rose, sandstone, seedsFromPalette, shuffleSeeds, sky, swissGreen, swissPaper, swissRelief, swissRoad, swissWater, validateTheme, verdant, wave };
955
+ export { BUILDINGS_3D_LAYER_ID, type BasemapOverrides, type ContrastWarning, DEFAULT_LAYER_VISIBILITY, DEFAULT_MARKER, DEFAULT_ROUTE, DEFAULT_SEEDS, type DeepPartial, LABEL_PAIRS, LAYER_GROUPS, type LabelRamp, type LayerGroup, MAX_CODE_LENGTH, MODES, type MarkerTheme, type Oklch, PRESET_CODE_PREFIX, PRESET_CODE_RE, type PaletteColor, RECIPES, type RampSet, type Recipe, type ResolvedTheme, type RoadRamp, type RouteTheme, STOCK_RAMPS, STYLE_THEMES, THEME_BASES, THEME_CODE_PREFIX, THEME_PRESETS, type ThemeBase, type ThemeDoc, ThemeError, type ThemeHalf, type ThemeLayers, type ThemeMode, type ThemePreset, type ThemeSeeds, amber, applyThemeLayers, aqua, atlasGreen, atlasPaper, atlasRelief, atlasRoad, atlasWater, azure, buildRecipes, cobalt, contrast, contrastWarnings, coral, decodePreset, decodeTheme, effectiveLayerOrder, ember, encodePreset, encodeTheme, fern, generateTheme, graphite, hexToOklch, indigo, ink, isLayerVisible, isStockLayerOrder, isStockLayers, isThemeBase, isThemePresetCode, jade, layerGroupOf, lilac, linen, makeFlavor, mergeRecipe, mist, oklchToHex, paper, peach, presetId, registryThemeKey, resolveTheme, rose, sandstone, seedsFromPalette, shuffleSeeds, sky, swissGreen, swissPaper, swissRelief, swissRoad, swissWater, validateTheme, verdant, wave };
package/dist/index.js CHANGED
@@ -366,14 +366,8 @@ var THEME_BASES = [
366
366
  "atlas",
367
367
  "swiss"
368
368
  ];
369
- var RETIRED_BASES = {
370
- relief: "outdoor",
371
- street: "base",
372
- decimal: "blueprint"
373
- };
374
- function normalizeThemeBase(base) {
375
- if (THEME_BASES.includes(base)) return base;
376
- return RETIRED_BASES[base] ?? null;
369
+ function isThemeBase(base) {
370
+ return typeof base === "string" && THEME_BASES.includes(base);
377
371
  }
378
372
  var MODES = ["light", "dark"];
379
373
  function makeFlavor(r) {
@@ -2506,8 +2500,7 @@ function validateTheme(input) {
2506
2500
  if (!DOC_KEYS.includes(key)) errors.push(`${key}: unknown field`);
2507
2501
  }
2508
2502
  if (input.v !== 1) errors.push("v: must be 1");
2509
- const base = typeof input.base === "string" ? normalizeThemeBase(input.base) : null;
2510
- if (!base) {
2503
+ if (!isThemeBase(input.base)) {
2511
2504
  errors.push(`base: must be one of ${THEME_BASES.join(", ")}`);
2512
2505
  }
2513
2506
  if (input.name !== void 0) {
@@ -2546,7 +2539,6 @@ function validateTheme(input) {
2546
2539
  }
2547
2540
  }
2548
2541
  if (errors.length > 0) throw new ThemeError("invalid theme", errors);
2549
- if (base && base !== input.base) return { ...input, base };
2550
2542
  return input;
2551
2543
  }
2552
2544
 
@@ -3032,7 +3024,8 @@ var STYLE_SLOTS = [
3032
3024
  "base",
3033
3025
  "muted",
3034
3026
  "outdoor",
3035
- "decimal",
3027
+ null,
3028
+ // u03, retired 2026-09-09
3036
3029
  "blueprint",
3037
3030
  "blush",
3038
3031
  "orchid",
@@ -3175,7 +3168,7 @@ function unpackLayers(bits) {
3175
3168
  return layers;
3176
3169
  }
3177
3170
  function encodePreset(doc) {
3178
- const slot = STYLE_SLOTS.indexOf(doc.base);
3171
+ const slot = isThemeBase(doc.base) ? STYLE_SLOTS.indexOf(doc.base) : -1;
3179
3172
  if (slot < 0) return null;
3180
3173
  let kind;
3181
3174
  let payload = 0n;
@@ -3217,9 +3210,10 @@ function decodePreset(code) {
3217
3210
  rest >>= bits;
3218
3211
  return value;
3219
3212
  };
3220
- const slot = STYLE_SLOTS[Number(take(STYLE_BITS))];
3221
- const base = slot ? normalizeThemeBase(slot) : null;
3222
- if (!base) throw new ThemeError("invalid theme", ["preset style is out of range"]);
3213
+ const slot = Number(take(STYLE_BITS));
3214
+ const base = STYLE_SLOTS[slot];
3215
+ if (base === void 0) throw new ThemeError("invalid theme", ["preset style is out of range"]);
3216
+ if (base === null) throw new ThemeError("invalid theme", [`preset style slot ${slot} is retired`]);
3223
3217
  const kind = take(KIND_BITS);
3224
3218
  const hasLayers = take(1n) === 1n;
3225
3219
  let doc;
@@ -3456,6 +3450,7 @@ export {
3456
3450
  isLayerVisible,
3457
3451
  isStockLayerOrder,
3458
3452
  isStockLayers,
3453
+ isThemeBase,
3459
3454
  isThemePresetCode,
3460
3455
  jade,
3461
3456
  layerGroupOf,
@@ -3464,7 +3459,6 @@ export {
3464
3459
  makeFlavor,
3465
3460
  mergeRecipe,
3466
3461
  mist,
3467
- normalizeThemeBase,
3468
3462
  oklchToHex,
3469
3463
  paper,
3470
3464
  peach,