@kahwee/sf-map-svg 4.1.0 → 4.2.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/llms-full.txt CHANGED
@@ -2,19 +2,19 @@
2
2
 
3
3
  > Offline San Francisco SVG maps with explicit geographic imports, no runtime dependencies, static rendering, and a browser controller.
4
4
 
5
- @kahwee/sf-map-svg 4.1.0 · local working tree. Node >=24.0.0 for server rendering; browsers need a DOM and a JSON-capable bundler.
5
+ @kahwee/sf-map-svg 4.2.0 · local working tree. Node >=24.0.0 for server rendering; browsers need a DOM and a JSON-capable bundler.
6
6
 
7
7
  These are working-tree docs. Unreleased changelog entries may describe APIs absent from npm; verify the installed version before copying them.
8
8
 
9
9
  Use renderMap(data, options) for SVG strings; createMap(data, options) returns a controller. Mount map.element and destroy it on unmount. Choose mode explicitly: createMap defaults to basemap. Geography is separate; staticMapData and fullMapData have different shapes.
10
10
 
11
- <!-- Generated from README.md; @kahwee/sf-map-svg 4.1.0 · local working tree. -->
11
+ <!-- Generated from README.md; @kahwee/sf-map-svg 4.2.0 · local working tree. -->
12
12
 
13
13
  # SF Map SVG
14
14
 
15
15
  Offline, self-contained San Francisco SVG maps. The package has no runtime dependencies. Geography is always an explicit import; the root entry does not bundle data.
16
16
 
17
- [Live examples](https://kahwee.github.io/sf-map-svg/examples.html) · [Storybook source](https://github.com/kahwee/sf-map-svg/blob/main/stories/) · [Geographic sources](https://github.com/kahwee/sf-map-svg/blob/main/SOURCES.md) · [v3 migration](https://github.com/kahwee/sf-map-svg/blob/main/docs/migration-v3.md)
17
+ [Design playground](https://kahwee.github.io/sf-map-svg/playground.html) · [Live examples](https://kahwee.github.io/sf-map-svg/examples.html) · [Storybook source](https://github.com/kahwee/sf-map-svg/blob/main/stories/) · [Geographic sources](https://github.com/kahwee/sf-map-svg/blob/main/SOURCES.md) · [v3 migration](https://github.com/kahwee/sf-map-svg/blob/main/docs/migration-v3.md)
18
18
 
19
19
  ## Install
20
20
 
@@ -64,7 +64,7 @@ map.camera.zoom(1.5);
64
64
  map.destroy();
65
65
  ```
66
66
 
67
- `createMap(data, options)` returns a controller. Use `map.element` for mounting, `map.configure({ features, layers, controls })` for runtime switches, `map.camera` for pan/zoom/fit/reset, and `map.on()` for typed events. Appearance is set at construction. SFAR realtor neighborhoods are the default when supplied; SF Find and analysis are explicit alternate sources. The `/data/full` preset includes all three collections and historical districts.
67
+ `createMap(data, options)` returns a controller. Use `map.element` for mounting, `map.configure({ features, layers, controls, appearance, mode, source, year, labels })` for runtime switches, `map.camera` for pan/zoom/fit/reset, and `map.on()` for typed events. Appearance can change live while preserving camera, selection, and focus. `getResolvedConfiguration()` explains effective layers; `getCapabilities()` reports supplied geography. SFAR realtor neighborhoods are the default when supplied; SF Find and analysis are explicit alternate sources. The `/data/full` preset includes all three collections and historical districts.
68
68
 
69
69
  See [controller options, events, and lifecycle](https://github.com/kahwee/sf-map-svg/blob/main/docs/API.md#interactive-maps),
70
70
  [examples](https://github.com/kahwee/sf-map-svg/blob/main/docs/EXAMPLES.md), and [consumer integration](https://github.com/kahwee/sf-map-svg/blob/main/docs/consumer-integration.md).
@@ -91,7 +91,7 @@ The [contributing guide](https://github.com/kahwee/sf-map-svg/blob/main/CONTRIBU
91
91
 
92
92
  ---
93
93
 
94
- <!-- Generated from docs/developer-guide.md; @kahwee/sf-map-svg 4.1.0 · local working tree. -->
94
+ <!-- Generated from docs/developer-guide.md; @kahwee/sf-map-svg 4.2.0 · local working tree. -->
95
95
 
96
96
  # Developer guide
97
97
 
@@ -183,13 +183,13 @@ SFAR realtor neighborhoods are the default when supplied. If only an alternative
183
183
 
184
184
  | Change | Call |
185
185
  | --- | --- |
186
- | Layers, controls, behaviors | `map.configure({ layers, controls, features })` |
186
+ | Layers, controls, behaviors | `map.configure({ layers, controls, features, appearance, mode, source, year, labels })` |
187
187
  | Map mode or neighborhood source | `map.setMode(mode)`, `map.setSource(source)` |
188
188
  | District boundaries or choropleth | `map.setDistrictYear(year)`, `map.setDistrictStyle(callback)` |
189
189
  | Master text visibility | `map.setLabels(visible)` |
190
190
  | Markers or route overlays | `map.setMarkers(items)`, `map.setOverlays(items)` |
191
191
  | Camera | `map.camera.set/pan/zoom/fit/reset(...)` |
192
- | Palette, typography, control styling | Construction-only `appearance`; recreate the map to change it |
192
+ | Palette, typography, control styling | `map.configure({ appearance })`; retains camera, selection and focus |
193
193
 
194
194
  `configure` merges supplied keys; omitted keys keep their existing values. Set a layer or control key to `undefined` to remove its override. Set a whole group to `undefined` to reset that group. Set a feature to `false` to disable it. Explicit layer overrides keep winning after mode changes.
195
195
 
@@ -200,7 +200,7 @@ map.configure({ features: { motion: false } });
200
200
  map.configure({ layers: undefined }); // clear all explicit layer overrides
201
201
  ```
202
202
 
203
- `getConfiguration()` returns a detached snapshot of these groups, including explicit layer overrides. It is not a full serialization of mode, source, year, camera, selection, or construction appearance. Invalid configuration patches leave the current map unchanged.
203
+ `getConfiguration()` returns a detached snapshot of these groups, including explicit layer overrides. It is not a full serialization of mode, source, year, camera, or selection. Appearance overrides are included. Use `getResolvedConfiguration()` for current mode/source/year/labels and effective layer switches, and `getCapabilities()` for supplied geography. Invalid configuration patches leave the current map unchanged.
204
204
 
205
205
  ## Markers, routes and camera coordinates
206
206
 
@@ -231,9 +231,11 @@ The generated `llms.txt` index links to Markdown documentation; `llms-full.txt`
231
231
 
232
232
  See [API options](https://github.com/kahwee/sf-map-svg/blob/main/docs/API.md), [worked examples](https://github.com/kahwee/sf-map-svg/blob/main/docs/EXAMPLES.md), [consumer integration](https://github.com/kahwee/sf-map-svg/blob/main/docs/consumer-integration.md), and [v3 migration](https://github.com/kahwee/sf-map-svg/blob/main/docs/migration-v3.md).
233
233
 
234
+ Appearance token objects merge by key; `undefined` removes an override and `appearance: undefined` resets the group. Static maps accept grouped `layers` and the shared `appearance` keys (`theme`, `colors`, `districtStyle`) too. See [the API contract](https://github.com/kahwee/sf-map-svg/blob/main/docs/API.md#live-configuration-and-inspection) and [the design playground](https://kahwee.github.io/sf-map-svg/playground.html).
235
+
234
236
  ---
235
237
 
236
- <!-- Generated from docs/API.md; @kahwee/sf-map-svg 4.1.0 · local working tree. -->
238
+ <!-- Generated from docs/API.md; @kahwee/sf-map-svg 4.2.0 · local working tree. -->
237
239
 
238
240
  # Map options and lifecycle
239
241
 
@@ -243,7 +245,20 @@ and event types are exported by [src/api.ts](https://github.com/kahwee/sf-map-sv
243
245
 
244
246
  ## Static maps
245
247
 
246
- Static options include `theme`, `width`, `height`, `padding`, `year` (2002, 2012, 2022), `districtLines`, `districtFills`, `districtStyle`, `districtLabels`, `neighborhoodLines`, `labels`, `highways`, `keyRoads`, `roadLabels`, `landmarks`, `bartStations`, `markers`, `overlays`, `title`, `idPrefix`, `colors`, and `animation`. Each optional layer is independent. User-supplied text and attributes are escaped in SVG output.
248
+ Static maps accept the same grouped presentation vocabulary as interactive maps:
249
+
250
+ ```ts
251
+ const presentation = {
252
+ layers: { landmarks: true, bartStations: true },
253
+ appearance: { theme: 'districts' as const, colors: { water: '#e7f0f3' } },
254
+ };
255
+ const { svg } = renderMap(staticMapData, presentation);
256
+ const map = createMap(fullMapData, presentation);
257
+ ```
258
+
259
+ Static `layers` supports every interactive layer switch except `neighborhoodLabels`. Static `appearance` supports `theme`, `colors`, and `districtStyle`; browser typography, area interaction styles, and screen-space marker sizing remain interactive settings. Unknown grouped keys are rejected. Supplied grouped keys override flat compatibility keys; omitted or `undefined` grouped keys preserve the flat value or renderer default. The two renderers retain their existing defaults. Marker arrays accept readonly inputs.
260
+
261
+ Flat compatibility options include `theme`, `width`, `height`, `padding`, `year` (2002, 2012, 2022), `districtLines`, `districtFills`, `districtStyle`, `districtLabels`, `neighborhoodLines`, `labels`, `highways`, `keyRoads`, `roadLabels`, `landmarks`, `bartStations`, `markers`, `overlays`, `title`, `idPrefix`, `colors`, and `animation`. Each optional layer is independent. User-supplied text and attributes are escaped in SVG output.
247
262
 
248
263
  Custom district IDs must be finite numbers. An empty `labelPoints` array uses the district's primary `label` position.
249
264
 
@@ -269,9 +284,48 @@ Two opt-in features animate the map's lines and layers. `features.layerTransitio
269
284
 
270
285
  District style callbacks are evaluated once per district at construction and on style or year updates. Hover, selection, and layer toggles reuse the prepared styles; call `setDistrictStyle()` again when external styling data changes. Invalid styles leave the current map unchanged. District fades respect reduced motion and are removed on interruption or destruction.
271
286
 
287
+ ## Live configuration and inspection
288
+
289
+ `map.configure({ appearance, mode, source, year, labels, layers, features, controls })` updates an existing map. Appearance updates preserve the camera, selections, retained marker nodes, and keyboard focus. Mode/source changes through `configure` preserve the camera; existing `setMode` and `setSource` convenience methods retain their reset behavior. Switching neighborhood source clears the neighborhood selection. Changing district year retains a selected district when it exists in the new year. Unsupported datasets, invalid appearance values, and invalid district-style callback results fail during preparation before commit. A reentrant styling callback can supersede the pending patch.
290
+
291
+ Appearance token objects (`colors`, `style`, `labelStyle`, `labelSize`, `areaStyle`) merge supplied keys; other appearance properties replace. Omitted keys retain their value, an explicit `undefined` property removes that override, and `appearance: undefined` resets the whole appearance group. Feature objects retain their existing replacement semantics. Mode/source/year/labels ignore `undefined`; reset them with an explicit value. Patch types explicitly permit these resets with TypeScript’s `exactOptionalPropertyTypes` enabled.
292
+
293
+ ```ts
294
+ map.configure({
295
+ appearance: { colors: { water: '#142d45' }, labelStyle: { fontFamily: 'Georgia,serif' } },
296
+ layers: { bartStations: false },
297
+ });
298
+ map.configure({ appearance: undefined }); // restore construction defaults, not initial overrides
299
+
300
+ const overrides = map.getConfiguration();
301
+ const effective = map.getResolvedConfiguration();
302
+ const available = map.getCapabilities();
303
+ ```
304
+
305
+ `getConfiguration()` returns detached feature, layer, control, and appearance overrides; district style callbacks retain their function identity. `getResolvedConfiguration()` adds the current mode, source, year and master labels and resolves **layer switches** through mode defaults, available data, and the master label switch. Appearance/control fields remain overrides. It does not claim that every label is visible: zoom and collision filtering still apply. `getCapabilities()` returns supplied `sources`, usable district `years`, and layer data availability for the current source/year, independent of visibility overrides. These reads throw after destruction.
306
+
307
+ ## Common selection event
308
+
309
+ `selectionchange` adds a consistent envelope without changing existing event payloads:
310
+
311
+ ```ts
312
+ map.on('selectionchange', (event) => {
313
+ // kind narrows current and previous to the corresponding selection type.
314
+ if (event.kind === 'marker') console.log(event.current?.id, event.previous?.id);
315
+ });
316
+ ```
317
+
318
+ `kind` is `marker`, `neighborhood`, or `district`. `current` and `previous` are detached selection snapshots or `null`. The event follows committed selection changes, including clearing and a changed district vintage; it does not represent every activation. Existing `markerchange`, `neighborhoodchange`, and `districtchange` events remain supported.
319
+
320
+ ## Coordinates and units
321
+
322
+ GeoJSON and static projection inputs use `[longitude, latitude]` in WGS84; markers use `{ lng, lat }`. Static `project` returns SVG units. `projectToScreen(lng, lat)` returns pixels relative to the mounted canvas. Camera viewports and pan deltas use the fixed 800 × 800 projected map space, independent of canvas size. Marker radius uses screen pixels in browser maps and SVG units in static output.
323
+
324
+ Explore presentation options in the [live map design playground](https://kahwee.github.io/sf-map-svg/playground.html). The page identifies its package version; released builds use compatible options for that installed version.
325
+
272
326
  ---
273
327
 
274
- <!-- Generated from docs/EXAMPLES.md; @kahwee/sf-map-svg 4.1.0 · local working tree. -->
328
+ <!-- Generated from docs/EXAMPLES.md; @kahwee/sf-map-svg 4.2.0 · local working tree. -->
275
329
 
276
330
  # Examples
277
331
 
@@ -382,7 +436,7 @@ For the controller and explicit data imports, see [the v3 migration guide](https
382
436
 
383
437
  ---
384
438
 
385
- <!-- Generated from docs/consumer-integration.md; @kahwee/sf-map-svg 4.1.0 · local working tree. -->
439
+ <!-- Generated from docs/consumer-integration.md; @kahwee/sf-map-svg 4.2.0 · local working tree. -->
386
440
 
387
441
  # Consumer integration
388
442
 
@@ -410,7 +464,7 @@ For server rendering, import `renderMap` from the root and pass `guideMapData.ma
410
464
 
411
465
  ---
412
466
 
413
- <!-- Generated from docs/migration-v3.md; @kahwee/sf-map-svg 4.1.0 · local working tree. -->
467
+ <!-- Generated from docs/migration-v3.md; @kahwee/sf-map-svg 4.2.0 · local working tree. -->
414
468
 
415
469
  # Migrate to version 3
416
470
 
@@ -451,7 +505,7 @@ District styles are now prepared once per district on construction and style/yea
451
505
 
452
506
  ---
453
507
 
454
- <!-- Generated from SOURCES.md; @kahwee/sf-map-svg 4.1.0 · local working tree. -->
508
+ <!-- Generated from SOURCES.md; @kahwee/sf-map-svg 4.2.0 · local working tree. -->
455
509
 
456
510
  # Geometry and provenance
457
511
 
@@ -635,12 +689,26 @@ display geometry, and no precinct polygons or raw workbook data are shipped.
635
689
 
636
690
  ---
637
691
 
638
- <!-- Generated from CHANGELOG.md; @kahwee/sf-map-svg 4.1.0 · local working tree. -->
692
+ <!-- Generated from CHANGELOG.md; @kahwee/sf-map-svg 4.2.0 · local working tree. -->
639
693
 
640
694
  # Changelog
641
695
 
642
696
  User-visible changes are recorded here. Unreleased entries describe changes on `main` that are not part of a tagged package release.
643
697
 
698
+ ## Unreleased
699
+
700
+ ## 4.2.0 — 2026-10-01
701
+
702
+ - Reuse one native dropdown treatment across Pages, with an inset chevron, a reserved end gutter, and preserved labels, keyboard behavior, and mobile pickers.
703
+ - Strengthen the playground with strict TypeScript, checked JavaScript/TypeScript snippets, versioned design links, persistent explicit layer choices and independent static rendering. Keep mobile controls unobstructed and picker labels legible in dark mode.
704
+ - Express explicit configuration resets for consumers using `exactOptionalPropertyTypes`, including nested appearance tokens. Make Pages API and guide references describe their actual package version without internal preview warnings.
705
+
706
+ - Share grouped `layers` and supported `appearance` options between static and interactive maps, retaining flat static options and existing defaults; accept readonly static marker arrays.
707
+ - Update interactive appearance, mode, source, district year and labels through `configure`, preserving camera and retained marker nodes. Merge appearance tokens by key and validate combined patches before commit.
708
+ - Add `getResolvedConfiguration()` and `getCapabilities()` for effective layer switches and supplied geography, plus an additive `selectionchange` event with consistent current/previous snapshots.
709
+ - Add a live Pages map design playground with six editable palettes, layers, typography, sample pins/routes, browser/static previews, undo/redo, shareable designs, copyable code and full-city SVG export. Keep released-package previews compatible with their installed API.
710
+ - Refresh the Pages site: a new SF Map SVG mark traced from the real coastline, a reframed header with a release link, card-style reference tables that no longer collapse, a live filter for the API reference, an aligned playground with one-row-per-colour inks, and 12px minimum type for labels and badges.
711
+
644
712
  ## 4.1.0 — 2026-10-01
645
713
 
646
714
  - Validate canonical and generated GeoJSON during data checks, rejecting invalid WGS84 coordinates and malformed polygon rings before release.
@@ -932,7 +1000,7 @@ consistent home, and makes animation and subscription ownership predictable.
932
1000
 
933
1001
  # Published type contracts
934
1002
 
935
- @kahwee/sf-map-svg 4.1.0 · local working tree. These declarations are generated by TypeScript. Use package export paths, not the internal filenames below. Imported relative contracts are included for context, not as additional public entry points.
1003
+ @kahwee/sf-map-svg 4.2.0 · local working tree. These declarations are generated by TypeScript. Use package export paths, not the internal filenames below. Imported relative contracts are included for context, not as additional public entry points.
936
1004
 
937
1005
  | Public import | Declaration |
938
1006
  | --- | --- |
@@ -987,25 +1055,67 @@ export declare function createMap(data: InteractiveSFMapData, options?: MapOptio
987
1055
  ```ts
988
1056
  import type { Geometry, NeighborhoodSource } from '../data/types.js';
989
1057
  import type { CameraOptions, DistrictSelection, DistrictStyle, DistrictYear, InteractiveLayers, MapFeatures, MapMarker, MapOverlay, MapPadding, MapViewport, NeighborhoodExplorerOptions, NeighborhoodSelection } from './types.js';
990
- export type MapAppearance = Pick<NeighborhoodExplorerOptions, 'theme' | 'colors' | 'labelStyle' | 'areaStyle' | 'districtStyle' | 'labelSize' | 'style' | 'markerRadius' | 'markerHitSize' | 'markerColor' | 'selectedMarkerColor'>;
1058
+ /** Patch properties can explicitly remove overrides, including with exactOptionalPropertyTypes. */
1059
+ type Resettable<T> = {
1060
+ [K in keyof T]?: T[K] | undefined;
1061
+ };
1062
+ type AppearanceOptions = Pick<NeighborhoodExplorerOptions, 'theme' | 'colors' | 'labelStyle' | 'areaStyle' | 'districtStyle' | 'labelSize' | 'style' | 'markerRadius' | 'markerHitSize' | 'markerColor' | 'selectedMarkerColor'>;
1063
+ export type MapAppearance = {
1064
+ [K in keyof AppearanceOptions]?: K extends 'colors' | 'style' | 'labelStyle' | 'labelSize' | 'areaStyle' ? Resettable<NonNullable<AppearanceOptions[K]>> | undefined : AppearanceOptions[K] | undefined;
1065
+ };
991
1066
  export type MapControls = NonNullable<NeighborhoodExplorerOptions['controls']>;
992
- /** Omitted groups/keys retain their values; undefined groups reset all keys in that group. */
1067
+ /** Omitted keys retain values. Undefined presentation groups reset; undefined mode/source/year/labels retain their current value. */
993
1068
  export interface MapConfiguration {
994
- features?: MapFeatures;
995
- layers?: InteractiveLayers;
996
- controls?: MapControls;
1069
+ features?: Resettable<MapFeatures> | undefined;
1070
+ layers?: Resettable<InteractiveLayers> | undefined;
1071
+ controls?: Resettable<MapControls> | undefined;
1072
+ appearance?: MapAppearance | undefined;
1073
+ mode?: NonNullable<NeighborhoodExplorerOptions['mode']> | undefined;
1074
+ source?: NeighborhoodSource | undefined;
1075
+ year?: DistrictYear | undefined;
1076
+ labels?: boolean | undefined;
997
1077
  }
998
1078
  export interface MapOptions extends Omit<NeighborhoodExplorerOptions, keyof MapFeatures | keyof MapAppearance | 'interface' | 'onMarkerActivate' | 'onOverlayActivate'> {
999
1079
  features?: MapFeatures;
1000
- appearance?: MapAppearance;
1080
+ appearance?: MapAppearance | undefined;
1001
1081
  }
1002
1082
  export interface MapConfigurationSnapshot {
1003
1083
  features: MapFeatures;
1004
1084
  /** Explicit overrides; absent values continue to follow the current mode. */
1005
1085
  layers: InteractiveLayers;
1006
1086
  controls: MapControls;
1087
+ appearance: MapAppearance;
1088
+ }
1089
+ export interface MapCapabilities {
1090
+ sources: readonly NeighborhoodSource[];
1091
+ years: readonly DistrictYear[];
1092
+ /** Data availability, independent of requested layer visibility. */
1093
+ layers: Readonly<Record<keyof InteractiveLayers, boolean>>;
1094
+ }
1095
+ export interface ResolvedMapConfiguration extends MapConfigurationSnapshot {
1096
+ mode: NonNullable<MapOptions['mode']>;
1097
+ source: NeighborhoodSource;
1098
+ year: DistrictYear;
1099
+ labels: boolean;
1100
+ /** Effective switches after mode defaults and data availability; label switches honor labels. */
1101
+ layers: Record<keyof InteractiveLayers, boolean>;
1007
1102
  }
1103
+ export type MapSelectionChange = {
1104
+ kind: 'marker';
1105
+ current: MapMarker | null;
1106
+ previous: MapMarker | null;
1107
+ } | {
1108
+ kind: 'neighborhood';
1109
+ current: NeighborhoodSelection | null;
1110
+ previous: NeighborhoodSelection | null;
1111
+ } | {
1112
+ kind: 'district';
1113
+ current: DistrictSelection | null;
1114
+ previous: DistrictSelection | null;
1115
+ };
1008
1116
  export interface MapEvents {
1117
+ /** Common selection envelope; existing change events retain their payloads. */
1118
+ selectionchange: MapSelectionChange;
1009
1119
  districtchange: DistrictSelection | {
1010
1120
  id: null;
1011
1121
  year: DistrictYear;
@@ -1043,11 +1153,13 @@ export interface MapEvents {
1043
1153
  mapresize: undefined;
1044
1154
  }
1045
1155
  export interface MapCamera {
1156
+ /** Fixed 800 × 800 projected space, not longitude/latitude. */
1046
1157
  get(): MapViewport;
1047
1158
  set(view: MapViewport, options?: CameraOptions): void;
1048
1159
  fit(geometry: Geometry, options?: CameraOptions & {
1049
1160
  padding?: number | MapPadding;
1050
1161
  }): void;
1162
+ /** Delta in projected map units. */
1051
1163
  pan(x: number, y: number, options?: CameraOptions): void;
1052
1164
  zoom(factor: number, options?: CameraOptions): void;
1053
1165
  reset(options?: CameraOptions): void;
@@ -1061,6 +1173,8 @@ export interface MapController {
1061
1173
  readonly destroyed: boolean;
1062
1174
  configure(patch: MapConfiguration): void;
1063
1175
  getConfiguration(): MapConfigurationSnapshot;
1176
+ getResolvedConfiguration(): ResolvedMapConfiguration;
1177
+ getCapabilities(): MapCapabilities;
1064
1178
  on<K extends keyof MapEvents>(type: K, listener: (detail: MapEvents[K]) => void): () => void;
1065
1179
  /** Reconcile stable IDs; retained markers preserve nodes, focus, and entrance animations. */
1066
1180
  setMarkers(markers: readonly MapMarker[]): void;
@@ -1091,6 +1205,7 @@ export interface MapController {
1091
1205
  /** Idempotent; stops work and managed subscriptions. Does not remove host-owned DOM. */
1092
1206
  destroy(): void;
1093
1207
  }
1208
+ export {};
1094
1209
  ```
1095
1210
 
1096
1211
  ### dist/data/types.d.ts
@@ -1264,6 +1379,9 @@ export interface MapOverlay {
1264
1379
  label?: string;
1265
1380
  }
1266
1381
  export interface SFMapOptions {
1382
+ /** Grouped presentation; supplied keys take precedence over flat compatibility options. */
1383
+ layers?: StaticMapLayers;
1384
+ appearance?: StaticMapAppearance;
1267
1385
  theme?: 'districts' | 'transit';
1268
1386
  width?: number;
1269
1387
  height?: number;
@@ -1282,7 +1400,7 @@ export interface SFMapOptions {
1282
1400
  roadLabels?: boolean;
1283
1401
  landmarks?: boolean;
1284
1402
  bartStations?: boolean;
1285
- markers?: MapMarker[];
1403
+ markers?: readonly MapMarker[];
1286
1404
  overlays?: readonly MapOverlay[];
1287
1405
  title?: string;
1288
1406
  idPrefix?: string;
@@ -1298,6 +1416,9 @@ export interface SFMapOptions {
1298
1416
  };
1299
1417
  colors?: Partial<Record<'water' | 'land' | 'district' | 'neighborhood' | 'highway' | 'road' | 'park' | 'landmark' | 'bart' | 'label' | 'marker' | 'selected', string>>;
1300
1418
  }
1419
+ /** Presentation supported by both the static and interactive renderers. */
1420
+ export type StaticMapLayers = Omit<InteractiveLayers, 'neighborhoodLabels'>;
1421
+ export type StaticMapAppearance = Pick<SFMapOptions, 'theme' | 'colors' | 'districtStyle'>;
1301
1422
  /** Serializable viewport in the interactive map's fixed 800 × 800 projected space. */
1302
1423
  export type MapViewport = readonly [x: number, y: number, size: number];
1303
1424
  export type ExplorerMode = 'neighborhoods' | 'districts' | 'basemap';
@@ -1476,7 +1597,7 @@ export interface NeighborhoodExplorerElement extends HTMLElement {
1476
1597
  export interface TransitAnimationElement extends HTMLElement {
1477
1598
  destroy(): void;
1478
1599
  }
1479
- export type { MapAppearance, MapCamera, MapConfiguration, MapController, MapEvents, MapOptions, } from './controller-types.js';
1600
+ export type { MapAppearance, MapCamera, MapCapabilities, MapConfiguration, MapConfigurationSnapshot, MapController, MapControls, MapEvents, MapOptions, MapSelectionChange, ResolvedMapConfiguration, } from './controller-types.js';
1480
1601
  ```
1481
1602
 
1482
1603
  ### dist/src/explorer-data.d.ts
@@ -1578,6 +1699,20 @@ export declare function getLayerPathsWithData(options: SFMapOptions, data: SFMap
1578
1699
  point: [number, number];
1579
1700
  }[];
1580
1701
  };
1702
+ export declare function resolveMapColors(theme: SFMapOptions['theme'], colors?: SFMapOptions['colors']): {
1703
+ water: string;
1704
+ land: string;
1705
+ district: string;
1706
+ neighborhood: string;
1707
+ highway: string;
1708
+ road: string;
1709
+ park: string;
1710
+ landmark: string;
1711
+ bart: string;
1712
+ label: string;
1713
+ marker: string;
1714
+ selected: string;
1715
+ };
1581
1716
  /** Make an offline SVG and the matching longitude/latitude projection. */
1582
1717
  export declare function createSFMapWithData(options: SFMapOptions, data: SFMapData): {
1583
1718
  svg: string;
@@ -1592,7 +1727,7 @@ export declare function createSFMapWithData(options: SFMapOptions, data: SFMapDa
1592
1727
  import { type SFMapData } from './map-core.js';
1593
1728
  import type { SFMapOptions } from './types.js';
1594
1729
  export type { DistrictRowData, SFMapData as StaticMapData } from './map-core.js';
1595
- export type { DistrictStyle, DistrictYear, SFMapOptions as StaticMapOptions } from './types.js';
1730
+ export type { DistrictStyle, DistrictYear, SFMapOptions as StaticMapOptions, StaticMapAppearance, StaticMapLayers, } from './types.js';
1596
1731
  /** Server-safe SVG rendering with explicit data; returns SVG plus projection helpers. */
1597
1732
  export declare function renderMap(data: SFMapData, options?: SFMapOptions): {
1598
1733
  svg: string;
package/llms.txt CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Offline San Francisco SVG maps with explicit geographic imports, no runtime dependencies, static rendering, and a browser controller.
4
4
 
5
- @kahwee/sf-map-svg 4.1.0 · local working tree. Node >=24.0.0 for server rendering; browsers need a DOM and a JSON-capable bundler.
5
+ @kahwee/sf-map-svg 4.2.0 · local working tree. Node >=24.0.0 for server rendering; browsers need a DOM and a JSON-capable bundler.
6
6
 
7
7
  These are working-tree docs. Unreleased changelog entries may describe APIs absent from npm; verify the installed version before copying them.
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kahwee/sf-map-svg",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "Offline SVG maps of San Francisco with district boundaries, parks, landmarks, and BART stations.",
5
5
  "type": "module",
6
6
  "main": "./dist/src/api.js",
@@ -175,13 +175,14 @@
175
175
  "demo": "pnpm build && node examples/build.mjs",
176
176
  "format": "biome check --write .",
177
177
  "format:check": "biome format .",
178
- "check": "pnpm peers check && pnpm lint && pnpm check:docs && pnpm data:check && pnpm typecheck && pnpm typecheck:stories && pnpm test && pnpm test:bundle",
178
+ "check": "pnpm peers check && pnpm lint && pnpm check:docs && pnpm data:check && pnpm typecheck && pnpm typecheck:stories && pnpm typecheck:website && pnpm test && pnpm test:bundle",
179
179
  "check:docs": "pnpm build && node scripts/check-docs.mjs && node scripts/build-llm-docs.mjs --check",
180
180
  "storybook": "pnpm build && storybook dev -p 6006 --host 127.0.0.1 --no-open",
181
181
  "build-storybook": "pnpm build && storybook build",
182
182
  "data:catalog": "node scripts/build-data-catalog.mjs",
183
183
  "data:check": "node scripts/build-data-catalog.mjs --check",
184
184
  "typecheck": "pnpm build && tsc -p tsconfig.json",
185
+ "typecheck:website": "pnpm build && tsc -p tsconfig.website.json",
185
186
  "typecheck:stories": "tsc -p tsconfig.stories.json",
186
187
  "data:normalize-realtor": "node scripts/normalize-realtor.mjs",
187
188
  "data:guide": "node scripts/build-guide-data.mjs",