@chrtco/sdk 0.10.41 → 0.10.43

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.cjs CHANGED
@@ -85,6 +85,8 @@ __export(index_exports, {
85
85
  NAUTICAL_SYMBOLS: () => NAUTICAL_SYMBOLS,
86
86
  NAV_AID_LAYER_GROUPS: () => NAV_AID_LAYER_GROUPS,
87
87
  NAV_AID_QUERY_LAYERS: () => NAV_AID_QUERY_LAYERS,
88
+ NIGHT_INK: () => NIGHT_INK,
89
+ NIGHT_MUTED_INK: () => NIGHT_MUTED_INK,
88
90
  NON_SOLAS_DISCLAIMER: () => NON_SOLAS_DISCLAIMER,
89
91
  NON_SOLAS_DISCLAIMER_SHORT: () => NON_SOLAS_DISCLAIMER_SHORT,
90
92
  OfflineError: () => OfflineError,
@@ -155,13 +157,16 @@ __export(index_exports, {
155
157
  buildSectorsFromFeatures: () => buildSectorsFromFeatures,
156
158
  buildSources: () => buildSources,
157
159
  candidateRank: () => candidateRank,
160
+ capLuminance: () => capLuminance,
158
161
  cellBandCharacters: () => cellBandCharacters,
159
162
  cellBandMatch: () => cellBandMatch,
160
163
  chrtAtonTintExpression: () => chrtAtonTintExpression,
164
+ chrtHouseColor: () => chrtHouseColor,
161
165
  chrtLightTintExpression: () => chrtLightTintExpression,
162
166
  collectCandidates: () => collectCandidates,
163
167
  collectClickCandidates: () => collectClickCandidates,
164
168
  combineWithBandFilter: () => combineWithBandFilter,
169
+ contrastRatio: () => contrastRatio,
165
170
  daymarkColor: () => daymarkColor,
166
171
  decimateFeatures: () => decimateFeatures,
167
172
  decodeAreaFeatureProps: () => decodeAreaFeatureProps,
@@ -233,6 +238,7 @@ __export(index_exports, {
233
238
  preprocessSoundings: () => preprocessSoundings,
234
239
  preprocessWaterBodies: () => preprocessWaterBodies,
235
240
  rasterAtonIconExpression: () => rasterAtonIconExpression,
241
+ relativeLuminance: () => relativeLuminance,
236
242
  resolveColor: () => resolveColor,
237
243
  resolveCustomTheme: () => resolveCustomTheme,
238
244
  resolveRasterImage: () => resolveRasterImage,
@@ -242,11 +248,13 @@ __export(index_exports, {
242
248
  rhythmProperties: () => rhythmProperties,
243
249
  s101ArtworkProvenance: () => s101ArtworkProvenance,
244
250
  sanitizeCustomStyleConfig: () => sanitizeCustomStyleConfig,
251
+ scaleLinearLight: () => scaleLinearLight,
245
252
  scaleSizeValue: () => scaleSizeValue,
246
253
  scaminToMinZoom: () => scaminToMinZoom,
247
254
  sectorLimitToArcBearing: () => sectorLimitToArcBearing,
248
255
  styleModeBand: () => styleModeBand,
249
256
  terrainStackCount: () => terrainStackCount,
257
+ themeChrtGlyph: () => themeChrtGlyph,
250
258
  tileTemplate: () => tileTemplate,
251
259
  uncoveredSectorArcs: () => uncoveredSectorArcs,
252
260
  withinBboxFilter: () => withinBboxFilter,
@@ -1363,6 +1371,14 @@ function requireManifestPath(manifestPath) {
1363
1371
  // src/endpoints/offline.ts
1364
1372
  var PROGRESS_MIN_BYTES = 1024 * 1024;
1365
1373
  var PROGRESS_MIN_FRACTION = 0.01;
1374
+ function noStoreInit() {
1375
+ try {
1376
+ new Request("http://localhost/", { cache: "no-store" });
1377
+ return { cache: "no-store" };
1378
+ } catch {
1379
+ return {};
1380
+ }
1381
+ }
1366
1382
  var OfflineEndpoint = class {
1367
1383
  constructor(http, deviceId, manifestPath) {
1368
1384
  this.http = http;
@@ -1649,7 +1665,7 @@ var OfflineEndpoint = class {
1649
1665
  resumed = true;
1650
1666
  }
1651
1667
  }
1652
- const fetchInit = { method: "GET", signal };
1668
+ const fetchInit = { method: "GET", signal, ...noStoreInit() };
1653
1669
  if (startByte > 0) {
1654
1670
  fetchInit.headers = { Range: `bytes=${startByte}-` };
1655
1671
  }
@@ -2428,7 +2444,12 @@ var S52_DUSK = {
2428
2444
  CHMGF: "#905090",
2429
2445
  CHWHT: "#90a0a0",
2430
2446
  CHCOR: "#a06030",
2431
- SNDG1: "#606060",
2447
+ // Deeper-than-safety soundings. Was #606060, which measured 2.39:1 on this
2448
+ // palette's DEPMD (#7ba6b8). DUSK water is LIGHT, so the only way to 3:1 is
2449
+ // darker: #505050 = 3.07:1 on DEPMD, 4.09:1 on DEPDW (spec: #292e2e). The
2450
+ // shoaler SNDG2 stays the louder numeral through its UIBCK halo cut-out.
2451
+ // Guarded with NIGHT's by `sounding-legibility.test.ts`; see S52_NIGHT.SNDG1.
2452
+ SNDG1: "#505050",
2432
2453
  SNDG2: "#b0b0b0",
2433
2454
  DEPSC: "#606060",
2434
2455
  DEPCN: "#2E2E2E",
@@ -2471,6 +2492,22 @@ var S52_NIGHT = {
2471
2492
  CHBRN: "#382410",
2472
2493
  CHGRF: "#2a2a22",
2473
2494
  CHGRD: "#1e1e18",
2495
+ // The NIGHT ink — linework, symbol outlines, navigation text, the shoal
2496
+ // soundings (SNDG2 below). Light warm on dark water: a deliberate deviation
2497
+ // from the spec's near-black (#1f2223), so a NIGHT chart is legible on a
2498
+ // screen nobody has dimmed. It is the brightest thing NIGHT draws on purpose,
2499
+ // and `night-luminance.test.ts` holds everything else under its muted level.
2500
+ //
2501
+ // ⚠ RED NIGHT DOES NOT DIM IT, AND THAT WAS DECIDED, NOT MISSED (2026-10-01).
2502
+ // Red night composes this same NIGHT chart with red chrome, as iOS does
2503
+ // (ContentView `effectiveChartTheme`). Dimming the ink for red night alone
2504
+ // would (1) drop SNDG2 out of the deeper soundings' legibility window — SNDG1
2505
+ // is pinned to 43% of this light by `sounding-legibility.test.ts` — and the
2506
+ // owner's red-night report of 2026-09-30 asked for MORE sounding legibility,
2507
+ // not less; and (2) dim the hazard outlines (OBSTRN, WRECKS) and coastline
2508
+ // that a night chart must keep. What red night was missing was everything
2509
+ // that did NOT follow this palette: labels, house marks, hatches, sector
2510
+ // bearings. Those follow it now.
2474
2511
  CHBLK: "#c8aa78",
2475
2512
  CSTLN: "#c8aa78",
2476
2513
  OUTLW: "#c8aa78",
@@ -2499,7 +2536,24 @@ var S52_NIGHT = {
2499
2536
  CHMGF: "#482848",
2500
2537
  CHWHT: "#484840",
2501
2538
  CHCOR: "#683818",
2502
- SNDG1: "#383028",
2539
+ // ⚠ DELIBERATE DEVIATION from the published S-52 NIGHT value (#1f2223),
2540
+ // for the reason DEPCN's below gives, on the deeper-than-safety soundings.
2541
+ //
2542
+ // Reported 2026-09-30 at night on CHRTdesktop (Raspberry Pi 4): "we mute
2543
+ // out the ones that are 'deep' but I need to still be able to see them."
2544
+ // The previous value, #383028, measured 1.37:1 on DEPDW and 1.08:1 on
2545
+ // DEPMD: the muted soundings had disappeared entirely.
2546
+ //
2547
+ // #897350 is SNDG2 (#c8aa78) scaled to 43% of its linear light, so it keeps
2548
+ // SNDG2's hue (the NIGHT table's warm linework family, kind to a
2549
+ // dark-adapted eye and to red night). Against this palette's water it
2550
+ // measures 3.10:1 on DEPMD and 3.91:1 on DEPDW, which clears the 3:1 floor.
2551
+ // It is 0.49x SNDG2's contrast on any of them, so the eye still goes to
2552
+ // the shoal soundings first. On the lighter DEPMS/DEPVS water the UIBCK
2553
+ // halo carries it: fill against halo is 4.37:1.
2554
+ // The window is narrow (luminance 0.175-0.187 meets both conditions), and
2555
+ // `sounding-legibility.test.ts` pins both ends for every symbol family.
2556
+ SNDG1: "#897350",
2503
2557
  SNDG2: "#c8aa78",
2504
2558
  DEPSC: "#383028",
2505
2559
  // ⚠ DELIBERATE DEVIATION from the published S-52 NIGHT value (#1f2223),
@@ -5198,6 +5252,45 @@ var InteractClient = class {
5198
5252
  }
5199
5253
  };
5200
5254
 
5255
+ // src/atlas/palette-light.ts
5256
+ var HEX6 = /^#?([0-9a-f]{6})$/i;
5257
+ function channels(hex) {
5258
+ const m = HEX6.exec(hex);
5259
+ if (!m) throw new Error(`palette-light: "${hex}" is not a #rrggbb colour`);
5260
+ const h = m[1];
5261
+ return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16));
5262
+ }
5263
+ function toLinear(c) {
5264
+ const v = c / 255;
5265
+ return v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
5266
+ }
5267
+ function toEncoded(v) {
5268
+ const c = v <= 31308e-7 ? v * 12.92 : 1.055 * v ** (1 / 2.4) - 0.055;
5269
+ return Math.round(Math.min(1, Math.max(0, c)) * 255);
5270
+ }
5271
+ function hex2(n) {
5272
+ return n.toString(16).padStart(2, "0");
5273
+ }
5274
+ function relativeLuminance(hex) {
5275
+ const [r, g2, b] = channels(hex).map(toLinear);
5276
+ return 0.2126 * r + 0.7152 * g2 + 0.0722 * b;
5277
+ }
5278
+ function contrastRatio(a, b) {
5279
+ const [hi, lo] = [relativeLuminance(a), relativeLuminance(b)].sort((x, y) => y - x);
5280
+ return (hi + 0.05) / (lo + 0.05);
5281
+ }
5282
+ function scaleLinearLight(hex, factor) {
5283
+ const [r, g2, b] = channels(hex).map((c) => toEncoded(toLinear(c) * factor));
5284
+ return `#${hex2(r)}${hex2(g2)}${hex2(b)}`;
5285
+ }
5286
+ function capLuminance(hex, ceiling) {
5287
+ const l = relativeLuminance(hex);
5288
+ if (l <= ceiling) return scaleLinearLight(hex, 1);
5289
+ return scaleLinearLight(hex, ceiling / l);
5290
+ }
5291
+ var NIGHT_INK = S52_NIGHT.CHBLK;
5292
+ var NIGHT_MUTED_INK = scaleLinearLight(S52_NIGHT.CHBLK, 0.43);
5293
+
5201
5294
  // src/atlas/s52-linestyles-generated.ts
5202
5295
  var S52_LINESTYLES = {
5203
5296
  ACHARE51: {
@@ -23008,14 +23101,60 @@ var NATQUA_ABBR = [
23008
23101
  // hard
23009
23102
  ];
23010
23103
  var DARK_BG = "#1a1d23";
23011
- var NAV_HALO = {
23012
- WHITE: { bright: "#ffffff", night: "#b8b0a0" },
23013
- CREAM: { bright: "#fdf0b0", night: "#a89868" },
23014
- YELLOW: { bright: "#eab308", night: "#806020" },
23015
- BLUE: { bright: "#b0c8e8", night: "#7088a0" }
23104
+ var NIGHT_MUTED = NIGHT_MUTED_INK;
23105
+ var NIGHT_KNOCKOUT = resolveColor("UIBCK", "NIGHT");
23106
+ var LABEL_TINT = {
23107
+ WATER: { day: "#9ec1d8", night: "muted" },
23108
+ // halo — lakes, rivers, canals
23109
+ WATER_DEEP: { day: "#8fb8d0", night: "muted" },
23110
+ // halo — sea areas, water place names
23111
+ LAND_REGION: { day: "#e8d8b8", night: "muted" },
23112
+ // halo — land regions, land place names
23113
+ LAND_NATURAL: { day: "#e0d8c8", night: "muted" },
23114
+ // halo — islands
23115
+ CITY: { day: "#f1e7d2", night: "muted" },
23116
+ // halo — built-up areas
23117
+ AIRPORT: { day: "#d4c9b0", night: "muted" },
23118
+ // halo — airports, landmarks, harbour facilities
23119
+ STRUCTURE: { day: "#bfa88a", night: "muted" },
23120
+ // glyph — bridges, shoreline constructions
23121
+ STRUCTURE_LIGHT: { day: "#c8c0b0", night: "muted" },
23122
+ // glyph — piles, moorings; halo — single buildings
23123
+ SOUNDING: { day: "#c8c8c8", night: "muted" },
23124
+ // halo — seabed, contour values; glyph — variation
23125
+ MAGENTA: { day: "#c545c3", night: "hue" },
23126
+ // glyph — production areas, overhead cables
23127
+ PIPELINE: { day: "#7fbf3f", night: "hue" },
23128
+ // glyph — overhead pipelines
23129
+ KELP: { day: "#3f8a34", night: "hue" },
23130
+ // halo — weed / kelp
23131
+ NAV_WHITE: { day: "#ffffff", night: "ink" },
23132
+ // halo — lateral, isolated danger, safe water
23133
+ NAV_CREAM: { day: "#fdf0b0", night: "ink" },
23134
+ // halo — cardinals, light characteristics
23135
+ NAV_YELLOW: { day: "#eab308", night: "hue" },
23136
+ // halo — special-purpose marks
23137
+ NAV_BLUE: { day: "#b0c8e8", night: "muted" }
23138
+ // halo — fog signals
23016
23139
  };
23017
- function navHalo(palette, ct) {
23018
- return ct === "NIGHT" ? NAV_HALO[palette].night : NAV_HALO[palette].bright;
23140
+ function tintFor(tint, ct) {
23141
+ const day = LABEL_TINT[tint].day;
23142
+ if (ct === "DAY") return day;
23143
+ return capLuminance(day, relativeLuminance(resolveColor("CHBLK", ct)));
23144
+ }
23145
+ function nightText(tint) {
23146
+ const role = LABEL_TINT[tint].night;
23147
+ if (role === "ink") return NIGHT_INK;
23148
+ if (role === "muted") return NIGHT_MUTED;
23149
+ return capLuminance(LABEL_TINT[tint].day, relativeLuminance(NIGHT_MUTED));
23150
+ }
23151
+ function onHalo(tint, ct) {
23152
+ if (ct === "NIGHT") return { textColor: nightText(tint), textHaloColor: NIGHT_KNOCKOUT };
23153
+ return { textColor: DARK_BG, textHaloColor: tintFor(tint, ct) };
23154
+ }
23155
+ function onKnockout(tint, ct) {
23156
+ if (ct === "NIGHT") return { textColor: nightText(tint), textHaloColor: NIGHT_KNOCKOUT };
23157
+ return { textColor: tintFor(tint, ct), textHaloColor: DARK_BG };
23019
23158
  }
23020
23159
  var NAV_AID_LABEL_TEXT_SIZE = [
23021
23160
  "interpolate",
@@ -23297,21 +23436,6 @@ function buildAirportTierExpression() {
23297
23436
  2
23298
23437
  ];
23299
23438
  }
23300
- var COLOR = {
23301
- WATER: "#9ec1d8",
23302
- WATER_DEEP: "#8fb8d0",
23303
- LAND_REGION: "#e8d8b8",
23304
- LAND_NATURAL: "#e0d8c8",
23305
- CITY: "#f1e7d2",
23306
- AIRPORT: "#d4c9b0",
23307
- STRUCTURE: "#bfa88a",
23308
- STRUCTURE_LIGHT: "#c8c0b0",
23309
- SOUNDING: "#c8c8c8",
23310
- NAV_NUMBER: "#ffffff",
23311
- NAV_CARDINAL: "#fdf0b0",
23312
- NAV_SPECIAL: "#eab308",
23313
- LIGHT: "#fdf0b0"
23314
- };
23315
23439
  var ORIENTATION_BAND_SORT_KEY = [
23316
23440
  "to-number",
23317
23441
  ["slice", ["coalesce", ["get", "_cell_id"], "??9???"], 2, 3],
@@ -23656,8 +23780,7 @@ function getLabelConfig(layerName, opts = {}) {
23656
23780
  [13, [11.5, 11.5]],
23657
23781
  [16, [10.5, 11.5]]
23658
23782
  ]),
23659
- textColor: DARK_BG,
23660
- textHaloColor: COLOR.LAND_REGION,
23783
+ ...onHalo("LAND_REGION", ct),
23661
23784
  // Land halo, not the shared taper — see LAND_HALO_WIDTH.
23662
23785
  textHaloWidth: LAND_HALO_WIDTH,
23663
23786
  textOffset: [0, 0],
@@ -23671,8 +23794,7 @@ function getLabelConfig(layerName, opts = {}) {
23671
23794
  type: "symbol",
23672
23795
  textField: buildLandmarkLabelExpression(),
23673
23796
  textSize: 9,
23674
- textColor: DARK_BG,
23675
- textHaloColor: "#d4c9b0",
23797
+ ...onHalo("AIRPORT", ct),
23676
23798
  textHaloWidth: 1.5,
23677
23799
  textOffset: [0, 0],
23678
23800
  minzoom: 9,
@@ -23692,8 +23814,7 @@ function getLabelConfig(layerName, opts = {}) {
23692
23814
  type: "symbol",
23693
23815
  textField: buildHarbourFacilityLabelExpression(),
23694
23816
  textSize: 9,
23695
- textColor: DARK_BG,
23696
- textHaloColor: "#d4c9b0",
23817
+ ...onHalo("AIRPORT", ct),
23697
23818
  textHaloWidth: 1.5,
23698
23819
  textOffset: [0, 0],
23699
23820
  minzoom: 12,
@@ -23710,8 +23831,7 @@ function getLabelConfig(layerName, opts = {}) {
23710
23831
  textField: ["coalesce", ["get", "_navAidLabel"], OBJNAM_TEXT],
23711
23832
  textSize: NAV_AID_LABEL_TEXT_SIZE,
23712
23833
  textFont: NAV_AID_LABEL_FONT,
23713
- textColor: DARK_BG,
23714
- textHaloColor: navHalo("WHITE", ct),
23834
+ ...onHalo("NAV_WHITE", ct),
23715
23835
  textHaloWidth: 1.5,
23716
23836
  textOffset: [0, 1.5],
23717
23837
  minzoom: 10,
@@ -23723,8 +23843,7 @@ function getLabelConfig(layerName, opts = {}) {
23723
23843
  textField: ["coalesce", ["get", "_navAidLabel"], OBJNAM_TEXT],
23724
23844
  textSize: NAV_AID_LABEL_TEXT_SIZE,
23725
23845
  textFont: NAV_AID_LABEL_FONT,
23726
- textColor: DARK_BG,
23727
- textHaloColor: navHalo("WHITE", ct),
23846
+ ...onHalo("NAV_WHITE", ct),
23728
23847
  textHaloWidth: 1.5,
23729
23848
  textOffset: [0, 1.5],
23730
23849
  minzoom: 10,
@@ -23737,8 +23856,7 @@ function getLabelConfig(layerName, opts = {}) {
23737
23856
  textField: ["coalesce", ["get", "_navAidLabel"], OBJNAM_TEXT],
23738
23857
  textSize: NAV_AID_LABEL_TEXT_SIZE,
23739
23858
  textFont: NAV_AID_LABEL_FONT,
23740
- textColor: DARK_BG,
23741
- textHaloColor: navHalo("CREAM", ct),
23859
+ ...onHalo("NAV_CREAM", ct),
23742
23860
  textHaloWidth: 1.5,
23743
23861
  textOffset: [0, 1.5],
23744
23862
  minzoom: 10,
@@ -23751,8 +23869,7 @@ function getLabelConfig(layerName, opts = {}) {
23751
23869
  textField: ["coalesce", ["get", "_navAidLabel"], OBJNAM_TEXT],
23752
23870
  textSize: NAV_AID_LABEL_TEXT_SIZE,
23753
23871
  textFont: NAV_AID_LABEL_FONT,
23754
- textColor: DARK_BG,
23755
- textHaloColor: navHalo("YELLOW", ct),
23872
+ ...onHalo("NAV_YELLOW", ct),
23756
23873
  textHaloWidth: 1.5,
23757
23874
  textOffset: [0, 1.5],
23758
23875
  minzoom: 10,
@@ -23765,8 +23882,7 @@ function getLabelConfig(layerName, opts = {}) {
23765
23882
  textField: ["coalesce", ["get", "_navAidLabel"], OBJNAM_TEXT],
23766
23883
  textSize: NAV_AID_LABEL_TEXT_SIZE,
23767
23884
  textFont: NAV_AID_LABEL_FONT,
23768
- textColor: DARK_BG,
23769
- textHaloColor: navHalo("WHITE", ct),
23885
+ ...onHalo("NAV_WHITE", ct),
23770
23886
  textHaloWidth: 1.5,
23771
23887
  textOffset: [0, 1.5],
23772
23888
  minzoom: 10,
@@ -23778,8 +23894,7 @@ function getLabelConfig(layerName, opts = {}) {
23778
23894
  textField: ["get", "_lightLabel"],
23779
23895
  textSize: NAV_AID_LABEL_TEXT_SIZE,
23780
23896
  textFont: NAV_AID_LABEL_FONT,
23781
- textColor: DARK_BG,
23782
- textHaloColor: navHalo("CREAM", ct),
23897
+ ...onHalo("NAV_CREAM", ct),
23783
23898
  textHaloWidth: 1.5,
23784
23899
  textOffset: [2, 0],
23785
23900
  minzoom: 9,
@@ -23803,8 +23918,7 @@ function getLabelConfig(layerName, opts = {}) {
23803
23918
  textField: catfogExpr,
23804
23919
  textSize: NAV_AID_LABEL_TEXT_SIZE,
23805
23920
  textFont: NAV_AID_LABEL_FONT,
23806
- textColor: DARK_BG,
23807
- textHaloColor: navHalo("BLUE", ct),
23921
+ ...onHalo("NAV_BLUE", ct),
23808
23922
  textHaloWidth: 1.5,
23809
23923
  textOffset: [2, 1.2],
23810
23924
  minzoom: 11,
@@ -23879,8 +23993,7 @@ function getLabelConfig(layerName, opts = {}) {
23879
23993
  [13, [0, 0, 13, 10, 11]],
23880
23994
  [16, [0, 0, 12, 11, 10]]
23881
23995
  ]),
23882
- textColor: DARK_BG,
23883
- textHaloColor: COLOR.WATER_DEEP,
23996
+ ...onHalo("WATER_DEEP", ct),
23884
23997
  textHaloWidth: ORIENTATION_HALO_WIDTH,
23885
23998
  textOffset: [0, 0],
23886
23999
  textFont: ["Open Sans Italic"],
@@ -23950,8 +24063,7 @@ function getLabelConfig(layerName, opts = {}) {
23950
24063
  [13, [10, 11]],
23951
24064
  [16, [9, 10]]
23952
24065
  ]),
23953
- textColor: DARK_BG,
23954
- textHaloColor: COLOR.WATER,
24066
+ ...onHalo("WATER", ct),
23955
24067
  textHaloWidth: ORIENTATION_HALO_WIDTH,
23956
24068
  textOffset: [0, 0],
23957
24069
  textFont: ["Open Sans Italic"],
@@ -23971,8 +24083,7 @@ function getLabelConfig(layerName, opts = {}) {
23971
24083
  [13, [10, 10]],
23972
24084
  [16, [9, 10]]
23973
24085
  ]),
23974
- textColor: DARK_BG,
23975
- textHaloColor: COLOR.WATER,
24086
+ ...onHalo("WATER", ct),
23976
24087
  textHaloWidth: ORIENTATION_HALO_WIDTH,
23977
24088
  textOffset: [0, 0],
23978
24089
  textFont: ["Open Sans Italic"],
@@ -23995,8 +24106,7 @@ function getLabelConfig(layerName, opts = {}) {
23995
24106
  16,
23996
24107
  9
23997
24108
  ],
23998
- textColor: DARK_BG,
23999
- textHaloColor: COLOR.WATER,
24109
+ ...onHalo("WATER", ct),
24000
24110
  textHaloWidth: ORIENTATION_HALO_WIDTH,
24001
24111
  textOffset: [0, 0],
24002
24112
  textFont: ["Open Sans Italic"],
@@ -24025,8 +24135,7 @@ function getLabelConfig(layerName, opts = {}) {
24025
24135
  [13, [11.5, 11.5]],
24026
24136
  [16, [10.5, 11.5]]
24027
24137
  ]),
24028
- textColor: DARK_BG,
24029
- textHaloColor: COLOR.LAND_NATURAL,
24138
+ ...onHalo("LAND_NATURAL", ct),
24030
24139
  // Land halo, not the shared taper — see LAND_HALO_WIDTH.
24031
24140
  textHaloWidth: LAND_HALO_WIDTH,
24032
24141
  textOffset: [0, 0],
@@ -24049,8 +24158,7 @@ function getLabelConfig(layerName, opts = {}) {
24049
24158
  [13, [10, 11, 12]],
24050
24159
  [16, [10, 10, 11]]
24051
24160
  ]),
24052
- textColor: DARK_BG,
24053
- textHaloColor: COLOR.CITY,
24161
+ ...onHalo("CITY", ct),
24054
24162
  textHaloWidth: ORIENTATION_HALO_WIDTH,
24055
24163
  textOffset: [0, 0],
24056
24164
  textFont: ["Open Sans Regular"],
@@ -24065,8 +24173,7 @@ function getLabelConfig(layerName, opts = {}) {
24065
24173
  type: "symbol",
24066
24174
  textField: OBJNAM_TEXT,
24067
24175
  textSize: 9,
24068
- textColor: "#bfa88a",
24069
- textHaloColor: DARK_BG,
24176
+ ...onKnockout("STRUCTURE", ct),
24070
24177
  textHaloWidth: 1.5,
24071
24178
  textOffset: [0, 0],
24072
24179
  minzoom: 12,
@@ -24084,8 +24191,7 @@ function getLabelConfig(layerName, opts = {}) {
24084
24191
  type: "symbol",
24085
24192
  textField: OBJNAM_TEXT,
24086
24193
  textSize: 8,
24087
- textColor: "#c8c0b0",
24088
- textHaloColor: DARK_BG,
24194
+ ...onKnockout("STRUCTURE_LIGHT", ct),
24089
24195
  textHaloWidth: 1.2,
24090
24196
  textOffset: [0, 1.2],
24091
24197
  minzoom: 13
@@ -24095,8 +24201,7 @@ function getLabelConfig(layerName, opts = {}) {
24095
24201
  type: "symbol",
24096
24202
  textField: OBJNAM_TEXT,
24097
24203
  textSize: 8,
24098
- textColor: "#c8c0b0",
24099
- textHaloColor: DARK_BG,
24204
+ ...onKnockout("STRUCTURE_LIGHT", ct),
24100
24205
  textHaloWidth: 1.2,
24101
24206
  textOffset: [0, 1.2],
24102
24207
  minzoom: 13
@@ -24106,8 +24211,7 @@ function getLabelConfig(layerName, opts = {}) {
24106
24211
  type: "symbol",
24107
24212
  textField: OBJNAM_TEXT,
24108
24213
  textSize: 8,
24109
- textColor: "#bfa88a",
24110
- textHaloColor: DARK_BG,
24214
+ ...onKnockout("STRUCTURE", ct),
24111
24215
  textHaloWidth: 1.2,
24112
24216
  textOffset: [0, 1],
24113
24217
  minzoom: 13,
@@ -24121,8 +24225,7 @@ function getLabelConfig(layerName, opts = {}) {
24121
24225
  type: "symbol",
24122
24226
  textField: OBJNAM_TEXT,
24123
24227
  textSize: 8,
24124
- textColor: DARK_BG,
24125
- textHaloColor: "#c8c0b0",
24228
+ ...onHalo("STRUCTURE_LIGHT", ct),
24126
24229
  textHaloWidth: 1.2,
24127
24230
  textOffset: [0, 1.2],
24128
24231
  minzoom: 14,
@@ -24143,8 +24246,7 @@ function getLabelConfig(layerName, opts = {}) {
24143
24246
  [13, [10, 10]],
24144
24247
  [16, [9, 9]]
24145
24248
  ]),
24146
- textColor: DARK_BG,
24147
- textHaloColor: COLOR.AIRPORT,
24249
+ ...onHalo("AIRPORT", ct),
24148
24250
  textHaloWidth: ORIENTATION_HALO_WIDTH,
24149
24251
  textOffset: [0, 0],
24150
24252
  textFont: ["Open Sans Regular"],
@@ -24165,8 +24267,7 @@ function getLabelConfig(layerName, opts = {}) {
24165
24267
  16,
24166
24268
  11
24167
24269
  ],
24168
- textColor: DARK_BG,
24169
- textHaloColor: COLOR.SOUNDING,
24270
+ ...onHalo("SOUNDING", ct),
24170
24271
  textHaloWidth: 1,
24171
24272
  textOffset: [0, 0],
24172
24273
  textFont: ["Open Sans Italic"],
@@ -24200,8 +24301,7 @@ function getLabelConfig(layerName, opts = {}) {
24200
24301
  14,
24201
24302
  11
24202
24303
  ],
24203
- textColor: COLOR.SOUNDING,
24204
- textHaloColor: DARK_BG,
24304
+ ...onKnockout("SOUNDING", ct),
24205
24305
  textHaloWidth: 1,
24206
24306
  textOffset: [0, 0],
24207
24307
  textFont: ["Open Sans Italic"],
@@ -24231,8 +24331,7 @@ function getLabelConfig(layerName, opts = {}) {
24231
24331
  16,
24232
24332
  8
24233
24333
  ],
24234
- textColor: "#c545c3",
24235
- textHaloColor: DARK_BG,
24334
+ ...onKnockout("MAGENTA", ct),
24236
24335
  textHaloWidth: ORIENTATION_HALO_WIDTH,
24237
24336
  textOffset: [0, 0],
24238
24337
  textFont: ["Open Sans Italic"],
@@ -24252,8 +24351,7 @@ function getLabelConfig(layerName, opts = {}) {
24252
24351
  16,
24253
24352
  11
24254
24353
  ],
24255
- textColor: DARK_BG,
24256
- textHaloColor: "#3f8a34",
24354
+ ...onHalo("KELP", ct),
24257
24355
  textHaloWidth: 1,
24258
24356
  textOffset: [0, 0],
24259
24357
  textFont: ["Open Sans Italic"],
@@ -24345,8 +24443,7 @@ function getLabelConfig(layerName, opts = {}) {
24345
24443
  17,
24346
24444
  11
24347
24445
  ],
24348
- textColor: isCBL ? "#c545c3" : "#7fbf3f",
24349
- textHaloColor: DARK_BG,
24446
+ ...onKnockout(isCBL ? "MAGENTA" : "PIPELINE", ct),
24350
24447
  textHaloWidth: 1.4,
24351
24448
  textOffset: [0, 0],
24352
24449
  textFont: ["Open Sans Regular"],
@@ -24381,8 +24478,7 @@ function getLabelConfig(layerName, opts = {}) {
24381
24478
  14,
24382
24479
  10
24383
24480
  ],
24384
- textColor: DARK_BG,
24385
- textHaloColor: "#c8c8c8",
24481
+ ...onHalo("SOUNDING", ct),
24386
24482
  textHaloWidth: 1.2,
24387
24483
  textOffset: [0, 0],
24388
24484
  textFont: ["Open Sans Regular"],
@@ -24687,9 +24783,9 @@ var PLACENAME_LAND_SIZE_STOPS = [
24687
24783
  [13, [0, 0, 10, 11, 10, 11, 10, 10, 10, 10]],
24688
24784
  [16, [0, 0, 10, 10, 9, 10, 9, 9, 9, 9]]
24689
24785
  ];
24690
- function buildPlaceNameLayers(sourceId) {
24786
+ function buildPlaceNameLayers(sourceId, ct = "DAY") {
24691
24787
  const sortKey = ["to-number", ["coalesce", ["get", "rank"], 9999999]];
24692
- const make = (idSuffix, classes, font, haloColor, letterSpacing, stops) => ({
24788
+ const make = (idSuffix, classes, font, tint, letterSpacing, stops) => ({
24693
24789
  id: `chrt-placename-${idSuffix}`,
24694
24790
  type: "symbol",
24695
24791
  source: sourceId,
@@ -24717,8 +24813,8 @@ function buildPlaceNameLayers(sourceId) {
24717
24813
  "symbol-sort-key": sortKey
24718
24814
  },
24719
24815
  paint: {
24720
- "text-color": DARK_BG,
24721
- "text-halo-color": haloColor,
24816
+ "text-color": onHalo(tint, ct).textColor,
24817
+ "text-halo-color": onHalo(tint, ct).textHaloColor,
24722
24818
  "text-halo-width": ORIENTATION_HALO_WIDTH,
24723
24819
  "text-opacity": PLACENAME_TEXT_OPACITY
24724
24820
  },
@@ -24728,8 +24824,8 @@ function buildPlaceNameLayers(sourceId) {
24728
24824
  }
24729
24825
  });
24730
24826
  return [
24731
- make("water", PLACENAME_WATER_CLASSES, ["Open Sans Italic"], COLOR.WATER_DEEP, 0.05, PLACENAME_WATER_SIZE_STOPS),
24732
- make("land", PLACENAME_LAND_CLASSES, ["Open Sans Regular"], COLOR.LAND_REGION, 0.02, PLACENAME_LAND_SIZE_STOPS)
24827
+ make("water", PLACENAME_WATER_CLASSES, ["Open Sans Italic"], "WATER_DEEP", 0.05, PLACENAME_WATER_SIZE_STOPS),
24828
+ make("land", PLACENAME_LAND_CLASSES, ["Open Sans Regular"], "LAND_REGION", 0.02, PLACENAME_LAND_SIZE_STOPS)
24733
24829
  ];
24734
24830
  }
24735
24831
  var DRGNOTE_SOURCE_LAYER = "DRGNOTE";
@@ -27453,37 +27549,58 @@ var CHRT_ATON_COLORS = {
27453
27549
  // pink (magenta-ish)
27454
27550
  };
27455
27551
  var CHRT_UNKNOWN_TINT = "#8B9298";
27456
- function chrtAtonTintExpression() {
27552
+ var HOUSE_BLACK = "#101418";
27553
+ function chrtHouseColor(dayHex, palette = "DAY") {
27554
+ if (palette === "DAY") return dayHex;
27555
+ const isBlack = dayHex.toLowerCase() === HOUSE_BLACK.toLowerCase();
27556
+ if (palette === "DUSK") {
27557
+ return isBlack ? dayHex : capLuminance(dayHex, relativeLuminance(resolveColor("CHBLK", "DUSK")));
27558
+ }
27559
+ return isBlack ? NIGHT_INK : capLuminance(dayHex, relativeLuminance(NIGHT_MUTED_INK));
27560
+ }
27561
+ function themeChrtGlyph(svg3, palette = "DAY") {
27562
+ if (palette === "DAY") return svg3;
27563
+ return svg3.replace(
27564
+ /(fill|stroke)="(#[0-9a-fA-F]{3}|#[0-9a-fA-F]{6})"/g,
27565
+ (_m, attr, hex) => {
27566
+ const six = hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;
27567
+ return `${attr}="${chrtHouseColor(six, palette)}"`;
27568
+ }
27569
+ );
27570
+ }
27571
+ function chrtAtonTintExpression(palette = "DAY") {
27572
+ const c = (code) => chrtHouseColor(CHRT_ATON_COLORS[code], palette);
27457
27573
  return colourCodeCase(
27458
27574
  [
27459
- ["3", CHRT_ATON_COLORS["3"]],
27460
- ["4", CHRT_ATON_COLORS["4"]],
27461
- ["6", CHRT_ATON_COLORS["6"]],
27462
- ["11", CHRT_ATON_COLORS["11"]],
27463
- ["9", CHRT_ATON_COLORS["9"]],
27464
- ["5", CHRT_ATON_COLORS["5"]],
27465
- ["1", CHRT_ATON_COLORS["1"]],
27466
- ["2", CHRT_ATON_COLORS["2"]],
27467
- ["8", CHRT_ATON_COLORS["8"]],
27468
- ["10", CHRT_ATON_COLORS["10"]],
27469
- ["12", CHRT_ATON_COLORS["12"]],
27470
- ["7", CHRT_ATON_COLORS["7"]]
27575
+ ["3", c("3")],
27576
+ ["4", c("4")],
27577
+ ["6", c("6")],
27578
+ ["11", c("11")],
27579
+ ["9", c("9")],
27580
+ ["5", c("5")],
27581
+ ["1", c("1")],
27582
+ ["2", c("2")],
27583
+ ["8", c("8")],
27584
+ ["10", c("10")],
27585
+ ["12", c("12")],
27586
+ ["7", c("7")]
27471
27587
  ],
27472
- CHRT_UNKNOWN_TINT
27588
+ chrtHouseColor(CHRT_UNKNOWN_TINT, palette)
27473
27589
  );
27474
27590
  }
27475
- function chrtLightTintExpression() {
27591
+ function chrtLightTintExpression(palette = "DAY") {
27592
+ const c = (code) => chrtHouseColor(CHRT_ATON_COLORS[code], palette);
27476
27593
  return colourCodeCase(
27477
27594
  [
27478
- ["3", CHRT_ATON_COLORS["3"]],
27479
- ["4", CHRT_ATON_COLORS["4"]],
27480
- ["6", CHRT_ATON_COLORS["6"]],
27481
- ["11", CHRT_ATON_COLORS["11"]],
27482
- ["9", CHRT_ATON_COLORS["9"]],
27483
- ["5", CHRT_ATON_COLORS["5"]],
27484
- ["1", CHRT_ATON_COLORS["1"]]
27595
+ ["3", c("3")],
27596
+ ["4", c("4")],
27597
+ ["6", c("6")],
27598
+ ["11", c("11")],
27599
+ ["9", c("9")],
27600
+ ["5", c("5")],
27601
+ ["1", c("1")]
27485
27602
  ],
27486
- CHRT_ATON_COLORS["1"]
27603
+ c("1")
27487
27604
  );
27488
27605
  }
27489
27606
  function scalar(attr) {
@@ -27585,7 +27702,7 @@ var TINTED_CLASSES = /* @__PURE__ */ new Set([
27585
27702
  "BCNSPP",
27586
27703
  "DAYMAR"
27587
27704
  ]);
27588
- var CHRT_HALO = { color: "#101418", width: 1 };
27705
+ var CHRT_HALO = { color: HOUSE_BLACK, width: 1 };
27589
27706
  var CHRT_LINE_DASH = {
27590
27707
  CBLSUB: { color: "CHMGD", width: 0.8, dash: [6, 3] },
27591
27708
  PIPSOL: { color: "PIPLN", width: 0.8, dash: [6, 3] }
@@ -27613,11 +27730,11 @@ function applyChrtSymbolSet(style, palette = "DAY") {
27613
27730
  layout["icon-image"] = CHRT_ICON_EXPRESSIONS[objectClass]();
27614
27731
  const paint = layer.paint ?? {};
27615
27732
  if (TINTED_CLASSES.has(objectClass)) {
27616
- paint["icon-color"] = chrtAtonTintExpression();
27733
+ paint["icon-color"] = chrtAtonTintExpression(palette);
27617
27734
  paint["icon-halo-color"] = CHRT_HALO.color;
27618
27735
  paint["icon-halo-width"] = CHRT_HALO.width;
27619
27736
  } else if (objectClass === "LIGHTS") {
27620
- paint["icon-color"] = chrtLightTintExpression();
27737
+ paint["icon-color"] = chrtLightTintExpression(palette);
27621
27738
  paint["icon-halo-color"] = CHRT_HALO.color;
27622
27739
  paint["icon-halo-width"] = CHRT_HALO.width;
27623
27740
  }
@@ -30769,7 +30886,7 @@ function generateStyle(palette = "DAY", opts = {}) {
30769
30886
  )
30770
30887
  );
30771
30888
  if (opts.labels === "placename") {
30772
- labelLayers.push(...buildPlaceNameLayers(sourceId));
30889
+ labelLayers.push(...buildPlaceNameLayers(sourceId, palette));
30773
30890
  }
30774
30891
  for (const { label } of chartLabelBundles) {
30775
30892
  if (bandFan === "collapsed") {
@@ -30859,7 +30976,7 @@ function generateStyle(palette = "DAY", opts = {}) {
30859
30976
  }
30860
30977
  }
30861
30978
  if (opts.labels === "placename") {
30862
- labelLayers.push(...buildPlaceNameLayers(sourceId));
30979
+ labelLayers.push(...buildPlaceNameLayers(sourceId, palette));
30863
30980
  } else {
30864
30981
  for (const { objectClass, label } of bundles) {
30865
30982
  if (!label) continue;
@@ -32032,6 +32149,8 @@ var Chrt = class {
32032
32149
  NAUTICAL_SYMBOLS,
32033
32150
  NAV_AID_LAYER_GROUPS,
32034
32151
  NAV_AID_QUERY_LAYERS,
32152
+ NIGHT_INK,
32153
+ NIGHT_MUTED_INK,
32035
32154
  NON_SOLAS_DISCLAIMER,
32036
32155
  NON_SOLAS_DISCLAIMER_SHORT,
32037
32156
  OfflineError,
@@ -32102,13 +32221,16 @@ var Chrt = class {
32102
32221
  buildSectorsFromFeatures,
32103
32222
  buildSources,
32104
32223
  candidateRank,
32224
+ capLuminance,
32105
32225
  cellBandCharacters,
32106
32226
  cellBandMatch,
32107
32227
  chrtAtonTintExpression,
32228
+ chrtHouseColor,
32108
32229
  chrtLightTintExpression,
32109
32230
  collectCandidates,
32110
32231
  collectClickCandidates,
32111
32232
  combineWithBandFilter,
32233
+ contrastRatio,
32112
32234
  daymarkColor,
32113
32235
  decimateFeatures,
32114
32236
  decodeAreaFeatureProps,
@@ -32180,6 +32302,7 @@ var Chrt = class {
32180
32302
  preprocessSoundings,
32181
32303
  preprocessWaterBodies,
32182
32304
  rasterAtonIconExpression,
32305
+ relativeLuminance,
32183
32306
  resolveColor,
32184
32307
  resolveCustomTheme,
32185
32308
  resolveRasterImage,
@@ -32189,11 +32312,13 @@ var Chrt = class {
32189
32312
  rhythmProperties,
32190
32313
  s101ArtworkProvenance,
32191
32314
  sanitizeCustomStyleConfig,
32315
+ scaleLinearLight,
32192
32316
  scaleSizeValue,
32193
32317
  scaminToMinZoom,
32194
32318
  sectorLimitToArcBearing,
32195
32319
  styleModeBand,
32196
32320
  terrainStackCount,
32321
+ themeChrtGlyph,
32197
32322
  tileTemplate,
32198
32323
  uncoveredSectorArcs,
32199
32324
  withinBboxFilter,