@sqlrooms/deck 0.29.0-rc.11 → 0.29.0-rc.13

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.
Files changed (154) hide show
  1. package/README.md +193 -35
  2. package/dist/BlockMapSettings.d.ts.map +1 -1
  3. package/dist/BlockMapSettings.js +4 -16
  4. package/dist/BlockMapSettings.js.map +1 -1
  5. package/dist/DashboardMapSettings.d.ts.map +1 -1
  6. package/dist/DashboardMapSettings.js +7 -10
  7. package/dist/DashboardMapSettings.js.map +1 -1
  8. package/dist/DeckJsonMap.d.ts.map +1 -1
  9. package/dist/DeckJsonMap.js +75 -23
  10. package/dist/DeckJsonMap.js.map +1 -1
  11. package/dist/DeckJsonMapSpec.d.ts +41 -0
  12. package/dist/DeckJsonMapSpec.d.ts.map +1 -1
  13. package/dist/DeckJsonMapSpec.js +2 -0
  14. package/dist/DeckJsonMapSpec.js.map +1 -1
  15. package/dist/DeckMapBasemapSelect.d.ts +9 -0
  16. package/dist/DeckMapBasemapSelect.d.ts.map +1 -0
  17. package/dist/DeckMapBasemapSelect.js +20 -0
  18. package/dist/DeckMapBasemapSelect.js.map +1 -0
  19. package/dist/DeckMapDefaultStylesProvider.d.ts +13 -19
  20. package/dist/DeckMapDefaultStylesProvider.d.ts.map +1 -1
  21. package/dist/DeckMapDefaultStylesProvider.js +11 -18
  22. package/dist/DeckMapDefaultStylesProvider.js.map +1 -1
  23. package/dist/DeckMapSurface.d.ts +1 -1
  24. package/dist/DeckMapSurface.js +1 -1
  25. package/dist/DeckMapSurface.js.map +1 -1
  26. package/dist/DeckMapsSlice.d.ts +5 -0
  27. package/dist/DeckMapsSlice.d.ts.map +1 -1
  28. package/dist/DeckMapsSlice.js +6 -3
  29. package/dist/DeckMapsSlice.js.map +1 -1
  30. package/dist/MapSettings.d.ts +14 -0
  31. package/dist/MapSettings.d.ts.map +1 -1
  32. package/dist/MapSettings.js +548 -192
  33. package/dist/MapSettings.js.map +1 -1
  34. package/dist/MapSettingsControls.d.ts +8 -1
  35. package/dist/MapSettingsControls.d.ts.map +1 -1
  36. package/dist/MapSettingsControls.js +24 -2
  37. package/dist/MapSettingsControls.js.map +1 -1
  38. package/dist/ai.d.ts.map +1 -1
  39. package/dist/ai.js +20 -12
  40. package/dist/ai.js.map +1 -1
  41. package/dist/aiNormalize.d.ts.map +1 -1
  42. package/dist/aiNormalize.js +2 -1
  43. package/dist/aiNormalize.js.map +1 -1
  44. package/dist/basemap.d.ts +23 -0
  45. package/dist/basemap.d.ts.map +1 -0
  46. package/dist/basemap.js +23 -0
  47. package/dist/basemap.js.map +1 -0
  48. package/dist/block.d.ts +1 -1
  49. package/dist/block.d.ts.map +1 -1
  50. package/dist/block.js +13 -9
  51. package/dist/block.js.map +1 -1
  52. package/dist/createOrUpdateDeckMapResource.d.ts +6 -2
  53. package/dist/createOrUpdateDeckMapResource.d.ts.map +1 -1
  54. package/dist/createOrUpdateDeckMapResource.js +57 -4
  55. package/dist/createOrUpdateDeckMapResource.js.map +1 -1
  56. package/dist/datasets/PreparedDatasetStore.d.ts +3 -4
  57. package/dist/datasets/PreparedDatasetStore.d.ts.map +1 -1
  58. package/dist/datasets/PreparedDatasetStore.js +4 -8
  59. package/dist/datasets/PreparedDatasetStore.js.map +1 -1
  60. package/dist/datasets/helpers.d.ts +39 -2
  61. package/dist/datasets/helpers.d.ts.map +1 -1
  62. package/dist/datasets/helpers.js +86 -3
  63. package/dist/datasets/helpers.js.map +1 -1
  64. package/dist/datasets/usePreparedDatasetStates.d.ts.map +1 -1
  65. package/dist/datasets/usePreparedDatasetStates.js +12 -8
  66. package/dist/datasets/usePreparedDatasetStates.js.map +1 -1
  67. package/dist/index.d.ts +6 -3
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +5 -2
  70. package/dist/index.js.map +1 -1
  71. package/dist/json/colorScaleFunction.d.ts +12 -4
  72. package/dist/json/colorScaleFunction.d.ts.map +1 -1
  73. package/dist/json/colorScaleFunction.js.map +1 -1
  74. package/dist/json/compileColorScale.d.ts +3 -2
  75. package/dist/json/compileColorScale.d.ts.map +1 -1
  76. package/dist/json/compileColorScale.js +16 -1
  77. package/dist/json/compileColorScale.js.map +1 -1
  78. package/dist/json/createDeckJsonConfiguration.d.ts.map +1 -1
  79. package/dist/json/createDeckJsonConfiguration.js +235 -175
  80. package/dist/json/createDeckJsonConfiguration.js.map +1 -1
  81. package/dist/json/defaultClasses.d.ts.map +1 -1
  82. package/dist/json/defaultClasses.js +3 -3
  83. package/dist/json/defaultClasses.js.map +1 -1
  84. package/dist/json/extractColorScaleLegends.d.ts.map +1 -1
  85. package/dist/json/extractColorScaleLegends.js +41 -26
  86. package/dist/json/extractColorScaleLegends.js.map +1 -1
  87. package/dist/json/heatmapDefaults.d.ts +17 -7
  88. package/dist/json/heatmapDefaults.d.ts.map +1 -1
  89. package/dist/json/heatmapDefaults.js +54 -12
  90. package/dist/json/heatmapDefaults.js.map +1 -1
  91. package/dist/json/layers/DeckHeatmapLayer.d.ts +32 -0
  92. package/dist/json/layers/DeckHeatmapLayer.d.ts.map +1 -0
  93. package/dist/json/layers/DeckHeatmapLayer.js +69 -0
  94. package/dist/json/layers/DeckHeatmapLayer.js.map +1 -0
  95. package/dist/json/layers/index.d.ts +1 -0
  96. package/dist/json/layers/index.d.ts.map +1 -1
  97. package/dist/json/layers/index.js +1 -0
  98. package/dist/json/layers/index.js.map +1 -1
  99. package/dist/json/rewriteGeoArrowAccessors.d.ts.map +1 -1
  100. package/dist/json/rewriteGeoArrowAccessors.js +13 -5
  101. package/dist/json/rewriteGeoArrowAccessors.js.map +1 -1
  102. package/dist/json/scaleFunction.d.ts +5 -8
  103. package/dist/json/scaleFunction.d.ts.map +1 -1
  104. package/dist/json/scaleFunction.js +30 -5
  105. package/dist/json/scaleFunction.js.map +1 -1
  106. package/dist/mapAiConfig.d.ts +6 -0
  107. package/dist/mapAiConfig.d.ts.map +1 -1
  108. package/dist/mapAiConfig.js +7 -0
  109. package/dist/mapAiConfig.js.map +1 -1
  110. package/dist/mapAiSharedInstructions.d.ts +1 -1
  111. package/dist/mapAiSharedInstructions.d.ts.map +1 -1
  112. package/dist/mapAiSharedInstructions.js +4 -3
  113. package/dist/mapAiSharedInstructions.js.map +1 -1
  114. package/dist/mapConfig.d.ts +6 -1
  115. package/dist/mapConfig.d.ts.map +1 -1
  116. package/dist/mapConfig.js +28 -3
  117. package/dist/mapConfig.js.map +1 -1
  118. package/dist/mapConfigUtils.d.ts +22 -0
  119. package/dist/mapConfigUtils.d.ts.map +1 -1
  120. package/dist/mapConfigUtils.js +152 -5
  121. package/dist/mapConfigUtils.js.map +1 -1
  122. package/dist/mapLayerConfigUtils.d.ts +55 -0
  123. package/dist/mapLayerConfigUtils.d.ts.map +1 -1
  124. package/dist/mapLayerConfigUtils.js +222 -11
  125. package/dist/mapLayerConfigUtils.js.map +1 -1
  126. package/dist/mapResourceAuthoring.d.ts +1 -1
  127. package/dist/mapResourceAuthoring.d.ts.map +1 -1
  128. package/dist/mapResourceAuthoring.js +14 -7
  129. package/dist/mapResourceAuthoring.js.map +1 -1
  130. package/dist/mapStyles.d.ts +16 -0
  131. package/dist/mapStyles.d.ts.map +1 -0
  132. package/dist/mapStyles.js +32 -0
  133. package/dist/mapStyles.js.map +1 -0
  134. package/dist/prepare/prepareDeckDataset.d.ts.map +1 -1
  135. package/dist/prepare/prepareDeckDataset.js +1 -0
  136. package/dist/prepare/prepareDeckDataset.js.map +1 -1
  137. package/dist/prepare/types.d.ts +3 -0
  138. package/dist/prepare/types.d.ts.map +1 -1
  139. package/dist/prepare/types.js.map +1 -1
  140. package/dist/prepare/wkbDecoder.d.ts.map +1 -1
  141. package/dist/prepare/wkbDecoder.js +62 -16
  142. package/dist/prepare/wkbDecoder.js.map +1 -1
  143. package/dist/prepare/wkbParser.d.ts +2 -0
  144. package/dist/prepare/wkbParser.d.ts.map +1 -1
  145. package/dist/prepare/wkbParser.js +1 -0
  146. package/dist/prepare/wkbParser.js.map +1 -1
  147. package/dist/protomapsStyles.d.ts +18 -0
  148. package/dist/protomapsStyles.d.ts.map +1 -0
  149. package/dist/protomapsStyles.js +38 -0
  150. package/dist/protomapsStyles.js.map +1 -0
  151. package/dist/types.d.ts +13 -0
  152. package/dist/types.d.ts.map +1 -1
  153. package/dist/types.js.map +1 -1
  154. package/package.json +11 -9
package/README.md CHANGED
@@ -1,10 +1,24 @@
1
+ # @sqlrooms/deck
2
+
1
3
  Deck.gl integration for SQLRooms with JSON-driven map specs, dataset registry
2
4
  binding, DuckDB-backed or in-memory Arrow datasets, and GeoArrow-first geometry
3
5
  preparation.
4
6
 
7
+ ## Package entry points
8
+
9
+ | Import | Use |
10
+ | ----------------------- | ----------------------------------------------------------------------------------------- |
11
+ | `@sqlrooms/deck` | Host-neutral maps, durable map resources, block-document integration, and authoring tools |
12
+ | `@sqlrooms/deck/mosaic` | Opt-in Mosaic dashboard renderers, configuration helpers, and AI tools |
13
+
14
+ The [Deck.gl example](https://github.com/sqlrooms/examples/tree/main/deckgl)
15
+ shows direct DuckDB-backed maps. The
16
+ [Deck.gl + Mosaic example](https://github.com/sqlrooms/examples/tree/main/deckgl-mosaic)
17
+ shows custom cross-filter integration.
18
+
5
19
  ## Map resources and dashboard adapters
6
20
 
7
- Worksheet maps are first-class `deckMaps` resources. The root package export
21
+ Document maps are first-class `deckMaps` resources. The root package export
8
22
  contains the resource slice, renderer, settings, direct DuckDB data adapter,
9
23
  and resource orchestration APIs. It does not require Mosaic.
10
24
 
@@ -14,14 +28,14 @@ selection, and issue translation inside that adapter boundary.
14
28
 
15
29
  `DeckMapSettingsPanel` is the shared host-neutral editor for both surfaces. It
16
30
  receives a map config, selected table, available tables, and edit callbacks;
17
- worksheet resources and Mosaic panels only adapt their respective stores to
31
+ document resources and Mosaic panels only adapt their respective stores to
18
32
  that contract. Layer, binding, style, extrusion, and code-view controls therefore
19
- stay consistent without putting Mosaic APIs in the worksheet settings path.
33
+ stay consistent without putting Mosaic APIs in the document settings path.
20
34
 
21
35
  Use `getDeckMapDataPolicy(...)` to resolve a map config into the exported
22
36
  `DeckMapDataPolicy` runtime row-limit policy.
23
37
 
24
- Worksheet map runtime issues distinguish dataset SQL failures (`sql-error`)
38
+ Document map runtime issues distinguish dataset SQL failures (`sql-error`)
25
39
  from fit-to-data bounds failures (`fit-error`), so each issue is cleared only
26
40
  after its corresponding operation recovers.
27
41
 
@@ -29,7 +43,7 @@ after its corresponding operation recovers.
29
43
  fit-to-data bounds queries. The direct adapter uses the authored source for
30
44
  bounds while applying the configured row-limit policy only to rendered rows.
31
45
 
32
- Worksheet maps deliberately use independent selection semantics. Their direct
46
+ Document maps deliberately use independent selection semantics. Their direct
33
47
  data adapter executes each configured SQL/table dataset through the room's
34
48
  DuckDB connector and neither reads nor publishes Mosaic selections. This drops
35
49
  the old incidental intra-map cross-filtering between datasets; a future
@@ -39,9 +53,13 @@ resource ownership.
39
53
  ## Installation
40
54
 
41
55
  ```bash
42
- npm install @sqlrooms/deck @sqlrooms/duckdb @sqlrooms/ui
56
+ npm install @sqlrooms/deck @sqlrooms/room-shell apache-arrow
43
57
  ```
44
58
 
59
+ For Mosaic dashboard integration, also install `@sqlrooms/mosaic`,
60
+ `@uwdata/mosaic-core`, and `@uwdata/mosaic-sql`, then import the adapter from
61
+ `@sqlrooms/deck/mosaic`.
62
+
45
63
  ## What This Package Does
46
64
 
47
65
  `@sqlrooms/deck` is the JSON-spec bridge between SQLRooms data and deck.gl:
@@ -60,6 +78,10 @@ instead of hand-constructing deck layer instances in React code.
60
78
 
61
79
  ## Quick Start
62
80
 
81
+ `DeckJsonMap` resolves SQL and table datasets through the current SQLRooms
82
+ store. Render it inside `RoomShell` or `RoomStateProvider` with a store that
83
+ includes DuckDB state. The example below assumes that host is already mounted.
84
+
63
85
  ```tsx
64
86
  import {DeckJsonMap} from '@sqlrooms/deck';
65
87
 
@@ -87,7 +109,8 @@ const spec = {
87
109
  scheme: 'YlOrRd',
88
110
  domain: 'auto',
89
111
  },
90
- getRadius: '@@=6',
112
+ getRadius: 6,
113
+ radiusUnits: 'pixels',
91
114
  radiusMinPixels: 2,
92
115
  },
93
116
  ],
@@ -105,12 +128,78 @@ export function AirportsMap() {
105
128
  geometryEncodingHint: 'wkb',
106
129
  },
107
130
  }}
108
- mapStyle="https://basemaps.cartocdn.com/gl/positron-gl-style/style.json"
109
131
  />
110
132
  );
111
133
  }
112
134
  ```
113
135
 
136
+ ## Basemaps
137
+
138
+ Deck maps use [OpenFreeMap](https://openfreemap.org/) vector tiles by default:
139
+ **Positron** for light mode and **Dark** for dark mode. No API key or registration
140
+ is required. The hosted styles include attribution, which MapLibre displays.
141
+ Maps work immediately with `createDeckMapsSlice()` or `DeckJsonMap`, without
142
+ additional configuration.
143
+
144
+ New map resources and dashboard panels save `mapStyle: 'light'` or
145
+ `'dark'` using the app theme at creation. Changing the app theme later
146
+ does not change existing maps. The **Basemap** dropdown in map settings selects
147
+ Light or Dark, including for maps with custom layer configurations. The selection
148
+ survives dataset changes, config updates, and saved-workspace reloads; keys and
149
+ generated style objects are not stored in map resources. Applications creating
150
+ configs outside the browser can use `getDefaultDeckMapStyle(theme)` explicitly.
151
+
152
+ Bare `DeckJsonMap` instances and older saved maps without a style continue to
153
+ follow the app theme. Choose a basemap in settings to persist it in an older map.
154
+ Explicit custom `mapStyle` URLs and `mapProps.mapStyle` objects remain supported;
155
+ the selector displays **Custom** until a built-in style is selected.
156
+
157
+ For custom basemaps, supply a `DeckMapBasemapProvider` callback:
158
+
159
+ ```tsx
160
+ createDeckMapsSlice({basemapProvider: (theme) => customStyles[theme]});
161
+ ```
162
+
163
+ Return stable MapLibre style objects or URLs. Direct `DeckJsonMap` callers
164
+ can pass `basemapProvider` as a prop, overriding the room's provider without
165
+ requiring the Deck maps slice. The existing room store is still required for
166
+ dataset preparation. Explicit custom map styles take precedence over
167
+ the provider.
168
+
169
+ `DeckMapDefaultStylesProvider` and `useDeckMapDefaultStyles` are deprecated.
170
+ To migrate an existing `styles={{light, dark}}` wrapper, keep the style objects
171
+ and pass `basemapProvider: (theme) => styles[theme]` to `createDeckMapsSlice`,
172
+ then remove the wrapper. For individual map overrides, pass the same callback
173
+ to `DeckJsonMap`. Code that reads `useDeckMapDefaultStyles` should use the room's
174
+ `deckMaps.basemapProvider` or the supplied callback instead.
175
+ The context API remains functional for backward compatibility, as a fallback
176
+ when no callback style is available. Removal is reserved for a future breaking
177
+ release.
178
+
179
+ Protomaps remains an optional provider for applications supplying their own key:
180
+
181
+ ```tsx
182
+ import {
183
+ createDeckMapsSlice,
184
+ createProtomapsBasemapProvider,
185
+ } from '@sqlrooms/deck';
186
+
187
+ createDeckMapsSlice({
188
+ basemapProvider: createProtomapsBasemapProvider(protomapsApiKey),
189
+ });
190
+ ```
191
+
192
+ The helper creates and reuses the light/dark style objects. Its callback lives
193
+ outside `deckMaps.config`, so its key is not persisted with maps. Obtain a
194
+ browser-visible key from [Protomaps](https://protomaps.com/api) and configure
195
+ allowed origins. A missing or blank key falls back to OpenFreeMap.
196
+ Document maps, dashboard maps, and `DeckJsonMap` inherit the room's provider.
197
+
198
+ `createProtomapsStyle(flavor, apiKey)` creates a MapLibre style object, and
199
+ `createProtomapsDefaultStyles(apiKey)` creates the Protomaps light/dark pair.
200
+ `DECK_MAP_BASEMAP_STYLES` lists the provider-neutral IDs and labels. Existing
201
+ `protomaps-light`/`protomaps-dark` selections are recognized as light/dark aliases.
202
+
114
203
  ## Auto Spec Generation
115
204
 
116
205
  If you want a starter JSON spec instead of writing every layer manually, use
@@ -164,10 +253,11 @@ const spec = createDeckJsonSpecFromDatasets({
164
253
 
165
254
  ## Mosaic Dashboard Renderer
166
255
 
167
- `@sqlrooms/deck` can contribute a `deck-json-map` panel renderer to
256
+ `@sqlrooms/deck/mosaic` contributes a `deck-json-map` panel renderer to
168
257
  `@sqlrooms/mosaic` dashboards without making the Mosaic package depend on
169
- deck.gl or MapLibre. Pass the renderer when creating the Mosaic dashboard
170
- slice.
258
+ deck.gl or MapLibre. `createDeckMapDashboardSliceOptions()` installs the map
259
+ renderer and add-panel action alongside the default Mosaic renderers, actions,
260
+ and chart types.
171
261
 
172
262
  The dashboard renderer exposes `DeckMapDashboardSettings` through its renderer
173
263
  definition. `DeckMapBlockSettings` is also exported for block-document hosts
@@ -176,20 +266,13 @@ that embed maps as stateful blocks.
176
266
  ```tsx
177
267
  import {
178
268
  createDeckMapDashboardPanelConfig,
179
- DECK_MAP_DASHBOARD_PANEL_TYPE,
180
- deckMapDashboardPanelRenderer,
181
- } from '@sqlrooms/deck';
182
- import {
183
- createDefaultMosaicDashboardPanelRenderers,
184
- createMosaicDashboardSlice,
185
- MosaicDashboard,
186
- } from '@sqlrooms/mosaic';
187
-
188
- const dashboardSlice = createMosaicDashboardSlice({
189
- panelRenderers: createDefaultMosaicDashboardPanelRenderers({
190
- [DECK_MAP_DASHBOARD_PANEL_TYPE]: deckMapDashboardPanelRenderer,
191
- }),
192
- });
269
+ createDeckMapDashboardSliceOptions,
270
+ } from '@sqlrooms/deck/mosaic';
271
+ import {createMosaicDashboardSlice, MosaicDashboard} from '@sqlrooms/mosaic';
272
+
273
+ const dashboardSlice = createMosaicDashboardSlice(
274
+ createDeckMapDashboardSliceOptions(),
275
+ );
193
276
 
194
277
  function Dashboard() {
195
278
  return <MosaicDashboard dashboardId="geo" />;
@@ -264,15 +347,19 @@ as durable resources without creating a dashboard. Compose
264
347
  `ensureDeckMapResourceState(...)` for a durable map id, and render it with
265
348
  `DeckMapBlockRenderer`.
266
349
 
350
+ See [Blocks and Block Documents](https://sqlrooms.org/blocks-and-documents.html)
351
+ for the surrounding artifact, renderer-provider, persistence, and ownership
352
+ setup.
353
+
267
354
  Runtime issue recovery can call `deckMaps.clearMapIssue(mapId, kind)` to clear
268
355
  only a matching issue kind; omit `kind` when the map state should clear any
269
356
  stale issue. Replacing a map config clears its prior render issue, while data
270
357
  issues remain until the corresponding dataset recovery is reported.
271
- Direct worksheet maps automatically fit the configured dataset when the map or
358
+ Direct document maps automatically fit the configured dataset when the map or
272
359
  its source first becomes ready; the header action remains available for manual
273
360
  refitting.
274
361
 
275
- Hosts that expose direct worksheet-map AI capability should include
362
+ Hosts that expose direct document-map AI capability should include
276
363
  `getDeckMapResourceAiInstructions()` in the responsible agent and tool
277
364
  instructions. `createOrUpdateDeckMapResource(...)` validates the fully merged
278
365
  resource before any durable block or map write: each dataset needs a
@@ -334,6 +421,11 @@ const result = await createOrUpdateDeckMapResource(
334
421
  blockDocumentId,
335
422
  mapId,
336
423
  config,
424
+ pointBinding: {
425
+ dataset: 'places',
426
+ longitudeColumn: 'longitude',
427
+ latitudeColumn: 'latitude',
428
+ },
337
429
  tableName,
338
430
  title,
339
431
  intent,
@@ -355,10 +447,22 @@ title and uses it as the default block caption. Block metadata is written only
355
447
  after the map write succeeds.
356
448
 
357
449
  Map authoring helpers such as `normalizeDeckMapPointConfig(...)`,
358
- `normalizeDeckMapFillColor(...)`, `regenerateMapConfigForTable(...)`, and
450
+ `applyDeckMapPointBinding(...)`, `normalizeDeckMapFillColor(...)`,
451
+ `regenerateMapConfigForTable(...)`, and
359
452
  dataset-source helpers such as `getFirstDatasetSourceTableName(...)` are
360
453
  exported so hosts can normalize AI-authored configs before calling
361
- `createOrUpdateDeckMapResource(...)`. `normalizeDeckMapPointConfig(...)` only adds
454
+ `createOrUpdateDeckMapResource(...)`. Passing structured `pointBinding` to the
455
+ resource helper applies `applyDeckMapPointBinding(...)`: it generates canonical
456
+ WKB point SQL through `createDeckMapPointTransformSql(...)` and aligns the target
457
+ dataset, point layers, brush interaction, and fit binding. The host table lookup
458
+ also supplies source columns so missing coordinate columns and a generated
459
+ geometry alias that would duplicate an existing column are rejected before
460
+ durable state is written. For a single table-backed dataset, its canonical table
461
+ identity must also match the selected table because that selection overrides the
462
+ authored dataset source at render time. This is the preferred path for standard
463
+ table-backed longitude/latitude maps; raw `transformSql` remains available for
464
+ custom spatial transforms.
465
+ `normalizeDeckMapPointConfig(...)` only adds
362
466
  the standard lon/lat point transform to table-backed datasets that do not
363
467
  already declare `geometryColumn`, `source.sqlQuery`, or `source.transformSql`
364
468
  and whose resolved table does not expose a native geometry column; native
@@ -392,6 +496,24 @@ layers (e.g. render points under map labels) and reduces WebGL context usage.
392
496
  Set `interleaved` to `false` to render deck layers in a separate overlay canvas
393
497
  on top of all basemap layers (uses an additional WebGL context per map).
394
498
 
499
+ MapLibre's drawing buffer is preserved by default so DOM image capture can
500
+ include the basemap and interleaved deck layers after a frame finishes.
501
+ In separate-overlay mode, deck.gl/luma.gl also preserves its drawing buffer by
502
+ default. Both map canvases are identified for capture validation, including
503
+ overlays with a custom deck ID. A lost context or disabled preservation on
504
+ either canvas causes the CLI rendering tools to return an actionable error.
505
+ Capture readiness also tracks dataset preparation, MapLibre tile rendering,
506
+ and deck layers' asynchronous resources. While any map in the requested surface
507
+ is still loading, the CLI rendering tools return an error asking the caller to
508
+ wait and retry, instead of returning an incomplete image.
509
+ Preserving the buffer can increase GPU memory use and reduce rendering
510
+ performance. Hosts that do not need image capture can opt out by setting
511
+ `mapProps.canvasContextAttributes.preserveDrawingBuffer` to `false`.
512
+ Changing this context option requires remounting the map.
513
+ To opt out for a separate deck overlay, set
514
+ `deckProps.deviceProps.webgl.preserveDrawingBuffer` to `false` and remount
515
+ the map; DOM image capture will then be unavailable.
516
+
395
517
  ```tsx
396
518
  {
397
519
  /* Default (interleaved): */
@@ -540,6 +662,21 @@ getFillColor: {
540
662
  }
541
663
  ```
542
664
 
665
+ Optional `opacity` (0–1) is a SQLRooms `colorScale` extension. The compiler
666
+ multiplies it into the color's alpha so fill, stroke, and arc endpoints can be
667
+ dimmed independently of deck.gl `layer.opacity`:
668
+
669
+ ```tsx
670
+ getFillColor: {
671
+ '@@function': 'colorScale',
672
+ field: 'Magnitude',
673
+ type: 'sequential',
674
+ scheme: 'YlOrRd',
675
+ domain: 'auto',
676
+ opacity: 0.6,
677
+ }
678
+ ```
679
+
543
680
  Discrete numeric palettes are supported too:
544
681
 
545
682
  ```tsx
@@ -597,6 +734,10 @@ scenes. It accepts resolved Arrow tables with geometry stored as:
597
734
  - WKB / GeoArrow WKB
598
735
  - WKT / GeoArrow WKT
599
736
 
737
+ The returned `PreparedDeckDataset` records the dataset-level
738
+ `datasetGeometryColumn` and `datasetGeometryEncodingHint` used to prepare
739
+ and identify that payload.
740
+
600
741
  It then produces canonical deck-facing geometry outputs for:
601
742
 
602
743
  - GeoArrow-native layers such as `GeoArrowScatterplotLayer`
@@ -617,24 +758,27 @@ Deck caches only the expensive geometry preparation layer on top.
617
758
 
618
759
  ## Supported Layers
619
760
 
620
- The current curated layer set is:
761
+ The map-resource validator and settings UI support this curated layer set:
621
762
 
622
763
  - `GeoArrowScatterplotLayer`
623
764
  - `GeoArrowHeatmapLayer`
624
765
  - `GeoArrowColumnLayer`
625
766
  - `GeoArrowPathLayer`
626
767
  - `GeoArrowPolygonLayer`
627
- - `GeoArrowSolidPolygonLayer`
628
768
  - `GeoArrowArcLayer`
629
769
  - `GeoArrowTripsLayer`
630
770
  - `GeoArrowH3HexagonLayer`
631
771
  - `GeoJsonLayer`
632
772
 
773
+ The lower-level `DeckJsonMap` JSON converter also registers
774
+ `GeoArrowSolidPolygonLayer`. Durable resource and AI authoring intentionally do
775
+ not accept it; use `GeoArrowPolygonLayer` for authored map resources.
776
+
633
777
  GeoArrow-native geometry columns are the efficient path. WKB/WKT geometry falls
634
778
  back to decoding and GeoJSON-binary preparation, with promotion available
635
779
  for point-focused GeoArrow layers such as `GeoArrowScatterplotLayer`,
636
780
  `GeoArrowHeatmapLayer`, and `GeoArrowColumnLayer`, plus Polygon promotion for
637
- `GeoArrowPolygonLayer` and `GeoArrowSolidPolygonLayer`. WKB/WKT MultiPolygon
781
+ `GeoArrowPolygonLayer`. WKB/WKT MultiPolygon
638
782
  uses the GeoJSON-binary path so separate polygon parts retain their nesting.
639
783
 
640
784
  The GeoArrow layer implementations themselves come from
@@ -649,7 +793,7 @@ not rewritten — the wrap is an outer pipeline query. Prefer writing
649
793
  `ST_AsWKB(...)` in transforms when you control the SQL; the pipeline covers
650
794
  bare `ST_Point(...)` / table `GEOMETRY` columns for every authoring surface.
651
795
 
652
- ## AI map config prepare
796
+ ## Prepare AI-authored map configs
653
797
 
654
798
  AI and host tooling should prepare authored Deck map configs through the shared
655
799
  helpers exported from `@sqlrooms/deck`:
@@ -673,9 +817,15 @@ helpers exported from `@sqlrooms/deck`:
673
817
  Skips unknown-field rejection when `transformSql` / `sqlQuery` is present
674
818
  (aliases may be transform-only).
675
819
 
820
+ Mosaic-specific AI helpers such as `createDeckMapDashboardTool()`,
821
+ `createDashboardWithDeckMapAiTools()`, and
822
+ `createDashboardAgentToolWithDeckMaps()` are exported from
823
+ `@sqlrooms/deck/mosaic`.
824
+
676
825
  Durable resource writes also run `getDeckMapResourceConfigIssues` /
677
- `assertDeckMapResourceConfig` for syntax and type/scheme compatibility
678
- (e.g. `quantile` + `Viridis` is rejected). Native `GEOMETRY` columns are
826
+ `assertDeckMapResourceConfig` for syntax, supported layer types, and type/scheme
827
+ compatibility (e.g. `quantile` + `Viridis` is rejected; layer `@@type` must be
828
+ one of the settings picker classes). Native `GEOMETRY` columns are
679
829
  normalized to WKB by the dataset pipeline (not by SQL-string validation).
680
830
 
681
831
  ## Runtime Props and Children
@@ -688,3 +838,11 @@ Keep the spec serializable, then pass runtime behavior separately:
688
838
 
689
839
  This lets the spec stay stable for storage, validation, and future AI-assisted
690
840
  generation while still supporting interactive React behavior at runtime.
841
+
842
+ ## More resources
843
+
844
+ - [`@sqlrooms/deck` API reference](https://sqlrooms.org/api/deck/)
845
+ - [Deck.gl example source](https://github.com/sqlrooms/examples/tree/main/deckgl)
846
+ - [Deck.gl + Mosaic example source](https://github.com/sqlrooms/examples/tree/main/deckgl-mosaic)
847
+ - [Blocks and Block Documents](https://sqlrooms.org/blocks-and-documents.html)
848
+ - [Commands](https://sqlrooms.org/commands.html)
@@ -1 +1 @@
1
- {"version":3,"file":"BlockMapSettings.d.ts","sourceRoot":"","sources":["../src/BlockMapSettings.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,2BAA2B,EAEjC,MAAM,qBAAqB,CAAC;AAS7B,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,WAAW,EACX,eAAe,EACf,OAAO,EACP,QAAQ,GACT,EAAE,2BAA2B,2CA0G7B"}
1
+ {"version":3,"file":"BlockMapSettings.d.ts","sourceRoot":"","sources":["../src/BlockMapSettings.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,2BAA2B,EAEjC,MAAM,qBAAqB,CAAC;AAM7B,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,WAAW,EACX,eAAe,EACf,OAAO,EACP,QAAQ,GACT,EAAE,2BAA2B,2CA2E7B"}
@@ -1,13 +1,10 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { getTableIdentity } from '@sqlrooms/duckdb';
3
3
  import { blockDocumentContentToBlocks, useStoreWithBlockDocuments, } from '@sqlrooms/documents';
4
- import { Button } from '@sqlrooms/ui';
5
- import { XIcon } from 'lucide-react';
6
4
  import { useCallback, useMemo } from 'react';
7
5
  import { useStoreWithDeckMaps } from './DeckMapsSlice';
8
6
  import { DeckMapSettingsPanel } from './MapSettings';
9
- import { regenerateMapConfigForTable } from './mapConfigUtils';
10
- import { getDeckMapResourceConfigIssues } from './mapResourceAuthoring';
7
+ import { applyDeckMapTableSelection } from './mapConfigUtils';
11
8
  export function DeckMapBlockSettings({ blockId, dashboardId, blockInstanceId, onClose, readOnly, }) {
12
9
  const mapId = blockInstanceId ?? blockId;
13
10
  const map = useStoreWithDeckMaps((state) => state.deckMaps.config.mapsById[mapId]);
@@ -22,7 +19,6 @@ export function DeckMapBlockSettings({ blockId, dashboardId, blockInstanceId, on
22
19
  candidate.type === 'statefulBlock' &&
23
20
  candidate.blockType === 'map')
24
21
  : undefined, [artifact, blockId]);
25
- const configIssues = useMemo(() => map ? getDeckMapResourceConfigIssues(map.config, { allowEmpty: true }) : [], [map]);
26
22
  const handleTitleChange = useCallback((title) => {
27
23
  if (readOnly)
28
24
  return;
@@ -37,21 +33,13 @@ export function DeckMapBlockSettings({ blockId, dashboardId, blockInstanceId, on
37
33
  const handleTableChange = useCallback((table) => {
38
34
  if (readOnly || !map)
39
35
  return;
40
- const config = regenerateMapConfigForTable({ config: map.config }, table);
41
- if (config === map.config)
42
- return;
43
36
  updateMap(mapId, {
44
37
  selectedTable: getTableIdentity(table.table),
45
- config: config,
38
+ config: applyDeckMapTableSelection(map.config, table),
46
39
  });
47
40
  }, [map, mapId, readOnly, updateMap]);
48
41
  if (!map)
49
42
  return (_jsx("div", { className: "text-muted-foreground p-4 text-sm", children: "Map settings not available" }));
50
- if (configIssues.length > 0 || map.config.configMode === 'custom') {
51
- return (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "flex items-center justify-between border-b px-3 py-1.5 text-xs font-medium", children: [_jsx("span", { children: "Map settings" }), onClose ? (_jsx(Button, { variant: "ghost", size: "icon", className: "h-5 w-5", onClick: onClose, "aria-label": "Close", children: _jsx(XIcon, { className: "h-3.5 w-3.5" }) })) : null] }), _jsx("div", { className: "text-muted-foreground flex flex-1 items-center justify-center p-4 text-center text-sm", children: configIssues.length > 0
52
- ? `Invalid map configuration: ${configIssues[0].path}: ${configIssues[0].message}`
53
- : 'This custom map configuration cannot be safely edited with the basic settings controls.' })] }));
54
- }
55
- return (_jsx(DeckMapSettingsPanel, { title: block?.caption ?? map.title, selectedTable: map.selectedTable, config: map.config, tables: tables.filter((table) => table.columns.length > 0), onClose: onClose, onTitleChange: handleTitleChange, onTableChange: handleTableChange, onConfigChange: (config) => updateMap(mapId, { config }), readOnly: readOnly }));
43
+ return (_jsx(DeckMapSettingsPanel, { title: block?.caption ?? map.title, selectedTable: map.selectedTable, config: map.config, tables: tables.filter((table) => table.columns.length > 0), onClose: onClose, onTitleChange: handleTitleChange, onTableChange: handleTableChange, onConfigChange: (config) => updateMap(mapId, { config }), readOnly: readOnly, customConfig: map.config.configMode === 'custom', preferDatasetSource: true }));
56
44
  }
57
45
  //# sourceMappingURL=BlockMapSettings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BlockMapSettings.js","sourceRoot":"","sources":["../src/BlockMapSettings.tsx"],"names":[],"mappings":"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","sourcesContent":["import {getTableIdentity, type DataTable} from '@sqlrooms/duckdb';\nimport {\n blockDocumentContentToBlocks,\n type BlockDocumentStatefulBlockBlock,\n type BlockSettingsComponentProps,\n useStoreWithBlockDocuments,\n} from '@sqlrooms/documents';\nimport {Button} from '@sqlrooms/ui';\nimport {XIcon} from 'lucide-react';\nimport {useCallback, useMemo} from 'react';\nimport {useStoreWithDeckMaps} from './DeckMapsSlice';\nimport {DeckMapSettingsPanel} from './MapSettings';\nimport {regenerateMapConfigForTable} from './mapConfigUtils';\nimport {getDeckMapResourceConfigIssues} from './mapResourceAuthoring';\n\nexport function DeckMapBlockSettings({\n blockId,\n dashboardId,\n blockInstanceId,\n onClose,\n readOnly,\n}: BlockSettingsComponentProps) {\n const mapId = blockInstanceId ?? blockId;\n const map = useStoreWithDeckMaps(\n (state) => state.deckMaps.config.mapsById[mapId],\n );\n const tables = useStoreWithDeckMaps((state) => state.db.tables);\n const updateMap = useStoreWithDeckMaps((state) => state.deckMaps.updateMap);\n const artifact = useStoreWithBlockDocuments((state) =>\n dashboardId\n ? state.blockDocuments.config.artifacts[dashboardId]\n : undefined,\n );\n const updateBlock = useStoreWithBlockDocuments(\n (state) => state.blockDocuments.updateBlock,\n );\n const block = useMemo(\n () =>\n artifact\n ? blockDocumentContentToBlocks(artifact.content).find(\n (candidate): candidate is BlockDocumentStatefulBlockBlock =>\n candidate.id === blockId &&\n candidate.type === 'statefulBlock' &&\n candidate.blockType === 'map',\n )\n : undefined,\n [artifact, blockId],\n );\n const configIssues = useMemo(\n () =>\n map ? getDeckMapResourceConfigIssues(map.config, {allowEmpty: true}) : [],\n [map],\n );\n\n const handleTitleChange = useCallback(\n (title: string) => {\n if (readOnly) return;\n updateMap(mapId, {title});\n if (dashboardId && block) {\n updateBlock(dashboardId, blockId, {\n ...block,\n caption: title || undefined,\n });\n }\n },\n [block, blockId, dashboardId, mapId, readOnly, updateBlock, updateMap],\n );\n\n const handleTableChange = useCallback(\n (table: DataTable) => {\n if (readOnly || !map) return;\n const config = regenerateMapConfigForTable({config: map.config}, table);\n if (config === map.config) return;\n updateMap(mapId, {\n selectedTable: getTableIdentity(table.table),\n config: config as typeof map.config,\n });\n },\n [map, mapId, readOnly, updateMap],\n );\n\n if (!map)\n return (\n <div className=\"text-muted-foreground p-4 text-sm\">\n Map settings not available\n </div>\n );\n\n if (configIssues.length > 0 || map.config.configMode === 'custom') {\n return (\n <div className=\"flex h-full flex-col\">\n <div className=\"flex items-center justify-between border-b px-3 py-1.5 text-xs font-medium\">\n <span>Map settings</span>\n {onClose ? (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-5 w-5\"\n onClick={onClose}\n aria-label=\"Close\"\n >\n <XIcon className=\"h-3.5 w-3.5\" />\n </Button>\n ) : null}\n </div>\n <div className=\"text-muted-foreground flex flex-1 items-center justify-center p-4 text-center text-sm\">\n {configIssues.length > 0\n ? `Invalid map configuration: ${configIssues[0]!.path}: ${configIssues[0]!.message}`\n : 'This custom map configuration cannot be safely edited with the basic settings controls.'}\n </div>\n </div>\n );\n }\n\n return (\n <DeckMapSettingsPanel\n title={block?.caption ?? map.title}\n selectedTable={map.selectedTable}\n config={map.config}\n tables={tables.filter((table) => table.columns.length > 0)}\n onClose={onClose}\n onTitleChange={handleTitleChange}\n onTableChange={handleTableChange}\n onConfigChange={(config) => updateMap(mapId, {config})}\n readOnly={readOnly}\n />\n );\n}\n"]}
1
+ {"version":3,"file":"BlockMapSettings.js","sourceRoot":"","sources":["../src/BlockMapSettings.tsx"],"names":[],"mappings":"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","sourcesContent":["import {getTableIdentity, type DataTable} from '@sqlrooms/duckdb';\nimport {\n blockDocumentContentToBlocks,\n type BlockDocumentStatefulBlockBlock,\n type BlockSettingsComponentProps,\n useStoreWithBlockDocuments,\n} from '@sqlrooms/documents';\nimport {useCallback, useMemo} from 'react';\nimport {useStoreWithDeckMaps} from './DeckMapsSlice';\nimport {DeckMapSettingsPanel} from './MapSettings';\nimport {applyDeckMapTableSelection} from './mapConfigUtils';\n\nexport function DeckMapBlockSettings({\n blockId,\n dashboardId,\n blockInstanceId,\n onClose,\n readOnly,\n}: BlockSettingsComponentProps) {\n const mapId = blockInstanceId ?? blockId;\n const map = useStoreWithDeckMaps(\n (state) => state.deckMaps.config.mapsById[mapId],\n );\n const tables = useStoreWithDeckMaps((state) => state.db.tables);\n const updateMap = useStoreWithDeckMaps((state) => state.deckMaps.updateMap);\n const artifact = useStoreWithBlockDocuments((state) =>\n dashboardId\n ? state.blockDocuments.config.artifacts[dashboardId]\n : undefined,\n );\n const updateBlock = useStoreWithBlockDocuments(\n (state) => state.blockDocuments.updateBlock,\n );\n const block = useMemo(\n () =>\n artifact\n ? blockDocumentContentToBlocks(artifact.content).find(\n (candidate): candidate is BlockDocumentStatefulBlockBlock =>\n candidate.id === blockId &&\n candidate.type === 'statefulBlock' &&\n candidate.blockType === 'map',\n )\n : undefined,\n [artifact, blockId],\n );\n\n const handleTitleChange = useCallback(\n (title: string) => {\n if (readOnly) return;\n updateMap(mapId, {title});\n if (dashboardId && block) {\n updateBlock(dashboardId, blockId, {\n ...block,\n caption: title || undefined,\n });\n }\n },\n [block, blockId, dashboardId, mapId, readOnly, updateBlock, updateMap],\n );\n\n const handleTableChange = useCallback(\n (table: DataTable) => {\n if (readOnly || !map) return;\n updateMap(mapId, {\n selectedTable: getTableIdentity(table.table),\n config: applyDeckMapTableSelection(map.config, table),\n });\n },\n [map, mapId, readOnly, updateMap],\n );\n\n if (!map)\n return (\n <div className=\"text-muted-foreground p-4 text-sm\">\n Map settings not available\n </div>\n );\n\n return (\n <DeckMapSettingsPanel\n title={block?.caption ?? map.title}\n selectedTable={map.selectedTable}\n config={map.config}\n tables={tables.filter((table) => table.columns.length > 0)}\n onClose={onClose}\n onTitleChange={handleTitleChange}\n onTableChange={handleTableChange}\n onConfigChange={(config) => updateMap(mapId, {config})}\n readOnly={readOnly}\n customConfig={map.config.configMode === 'custom'}\n preferDatasetSource\n />\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardMapSettings.d.ts","sourceRoot":"","sources":["../src/DashboardMapSettings.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,qBAAqB,CAAC;AASrE,OAAO,EAAC,KAAK,EAAE,EAAc,MAAM,OAAO,CAAC;AAQ3C;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,EAAE,CAAC,2BAA2B,CAoHpE,CAAC"}
1
+ {"version":3,"file":"DashboardMapSettings.d.ts","sourceRoot":"","sources":["../src/DashboardMapSettings.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,qBAAqB,CAAC;AAOrE,OAAO,EAAC,KAAK,EAAE,EAAc,MAAM,OAAO,CAAC;AAQ3C;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,EAAE,CAAC,2BAA2B,CA+FpE,CAAC"}
@@ -1,8 +1,6 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { getTableIdentity } from '@sqlrooms/duckdb';
3
3
  import { ConfirmDatasetChangeDialog, useConfirmDatasetChange, useStoreWithMosaicDashboard, useTablesWithColumns, } from '@sqlrooms/mosaic';
4
- import { Button } from '@sqlrooms/ui';
5
- import { XIcon } from 'lucide-react';
6
4
  import { useCallback } from 'react';
7
5
  import { DeckMapSettingsPanel } from './MapSettings';
8
6
  import { DECK_MAP_DASHBOARD_PANEL_TYPE, } from './dashboardConfig';
@@ -36,17 +34,16 @@ export const DeckMapDashboardSettings = ({ blockId, dashboardId, onClose, readOn
36
34
  });
37
35
  }, [blockId, dashboardId, readOnly, updatePanel]);
38
36
  const { handleChangeRequest, handleConfirm, handleCancel, isDialogOpen } = useConfirmDatasetChange(handleTableChange);
37
+ const panel = dashboard?.panels.find((candidate) => candidate.id === blockId);
38
+ const mapConfig = panel?.type === DECK_MAP_DASHBOARD_PANEL_TYPE
39
+ ? panel.config
40
+ : undefined;
39
41
  if (!dashboard) {
40
42
  return (_jsx("div", { className: "flex h-full items-center justify-center p-4", children: _jsx("p", { className: "text-muted-foreground text-sm", children: "Dashboard not found" }) }));
41
43
  }
42
- const panel = dashboard.panels.find((candidate) => candidate.id === blockId);
43
- if (!panel || panel.type !== DECK_MAP_DASHBOARD_PANEL_TYPE) {
44
+ if (!panel || !mapConfig) {
44
45
  return (_jsx("div", { className: "flex h-full items-center justify-center p-4", children: _jsx("p", { className: "text-muted-foreground text-sm", children: "Map panel not found" }) }));
45
46
  }
46
- const mapConfig = panel.config;
47
- if (mapConfig.configMode === 'custom') {
48
- return (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "flex items-center justify-between border-b px-3 py-1.5 text-xs font-medium", children: [_jsx("span", { children: "Map settings" }), onClose ? (_jsx(Button, { variant: "ghost", size: "icon", className: "h-5 w-5", onClick: onClose, "aria-label": "Close", children: _jsx(XIcon, { className: "h-3.5 w-3.5" }) })) : null] }), _jsx("div", { className: "text-muted-foreground flex flex-1 items-center justify-center p-4 text-center text-sm", children: "This custom map configuration cannot be safely edited with the basic settings controls." })] }));
49
- }
50
- return (_jsxs(_Fragment, { children: [_jsx(DeckMapSettingsPanel, { title: panel.title, selectedTable: dashboard.selectedTable, config: mapConfig, tables: tables, onClose: onClose, onTableChange: handleChangeRequest, onTitleChange: handleTitleChange, onConfigChange: handleConfigChange, readOnly: readOnly }), _jsx(ConfirmDatasetChangeDialog, { open: isDialogOpen, onConfirm: handleConfirm, onCancel: handleCancel })] }));
47
+ return (_jsxs(_Fragment, { children: [_jsx(DeckMapSettingsPanel, { title: panel.title, selectedTable: dashboard.selectedTable, config: mapConfig, tables: tables, onClose: onClose, onTableChange: handleChangeRequest, onTitleChange: handleTitleChange, onConfigChange: handleConfigChange, readOnly: readOnly, customConfig: mapConfig.configMode === 'custom' }), _jsx(ConfirmDatasetChangeDialog, { open: isDialogOpen, onConfirm: handleConfirm, onCancel: handleCancel })] }));
51
48
  };
52
49
  //# sourceMappingURL=DashboardMapSettings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardMapSettings.js","sourceRoot":"","sources":["../src/DashboardMapSettings.tsx"],"names":[],"mappings":"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","sourcesContent":["import {getTableIdentity, type DataTable} from '@sqlrooms/duckdb';\nimport type {BlockSettingsComponentProps} from '@sqlrooms/documents';\nimport {\n ConfirmDatasetChangeDialog,\n useConfirmDatasetChange,\n useStoreWithMosaicDashboard,\n useTablesWithColumns,\n} from '@sqlrooms/mosaic';\nimport {Button} from '@sqlrooms/ui';\nimport {XIcon} from 'lucide-react';\nimport {type FC, useCallback} from 'react';\nimport {DeckMapSettingsPanel} from './MapSettings';\nimport {\n DECK_MAP_DASHBOARD_PANEL_TYPE,\n type DeckMapDashboardPanelConfig,\n} from './dashboardConfig';\nimport type {DeckMapConfig} from './mapConfig';\n\n/**\n * Settings adapter for a Deck map panel inside a Mosaic dashboard.\n */\nexport const DeckMapDashboardSettings: FC<BlockSettingsComponentProps> = ({\n blockId,\n dashboardId,\n onClose,\n readOnly,\n}) => {\n const dashboard = useStoreWithMosaicDashboard((state) =>\n dashboardId ? state.mosaicDashboard.getDashboard(dashboardId) : undefined,\n );\n const updatePanel = useStoreWithMosaicDashboard(\n (state) => state.mosaicDashboard.updatePanel,\n );\n const setSelectedTable = useStoreWithMosaicDashboard(\n (state) => state.mosaicDashboard.setSelectedTable,\n );\n const tables = useTablesWithColumns();\n\n const handleTableChange = useCallback(\n (table: DataTable) => {\n if (readOnly) return;\n\n if (dashboardId) {\n setSelectedTable(dashboardId, getTableIdentity(table.table));\n }\n },\n [dashboardId, readOnly, setSelectedTable],\n );\n\n const handleTitleChange = useCallback(\n (title: string) => {\n if (readOnly) return;\n\n if (dashboardId) {\n updatePanel(dashboardId, blockId, {title: title || undefined});\n }\n },\n [dashboardId, blockId, readOnly, updatePanel],\n );\n\n const handleConfigChange = useCallback(\n (config: DeckMapConfig) => {\n if (readOnly || !dashboardId) return;\n updatePanel(dashboardId, blockId, {\n config: config as unknown as Record<string, unknown>,\n });\n },\n [blockId, dashboardId, readOnly, updatePanel],\n );\n\n const {handleChangeRequest, handleConfirm, handleCancel, isDialogOpen} =\n useConfirmDatasetChange(handleTableChange);\n\n if (!dashboard) {\n return (\n <div className=\"flex h-full items-center justify-center p-4\">\n <p className=\"text-muted-foreground text-sm\">Dashboard not found</p>\n </div>\n );\n }\n\n const panel = dashboard.panels.find((candidate) => candidate.id === blockId);\n\n if (!panel || panel.type !== DECK_MAP_DASHBOARD_PANEL_TYPE) {\n return (\n <div className=\"flex h-full items-center justify-center p-4\">\n <p className=\"text-muted-foreground text-sm\">Map panel not found</p>\n </div>\n );\n }\n\n const mapConfig = panel.config as DeckMapDashboardPanelConfig;\n if (mapConfig.configMode === 'custom') {\n return (\n <div className=\"flex h-full flex-col\">\n <div className=\"flex items-center justify-between border-b px-3 py-1.5 text-xs font-medium\">\n <span>Map settings</span>\n {onClose ? (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-5 w-5\"\n onClick={onClose}\n aria-label=\"Close\"\n >\n <XIcon className=\"h-3.5 w-3.5\" />\n </Button>\n ) : null}\n </div>\n <div className=\"text-muted-foreground flex flex-1 items-center justify-center p-4 text-center text-sm\">\n This custom map configuration cannot be safely edited with the basic\n settings controls.\n </div>\n </div>\n );\n }\n\n return (\n <>\n <DeckMapSettingsPanel\n title={panel.title}\n selectedTable={dashboard.selectedTable}\n config={mapConfig}\n tables={tables}\n onClose={onClose}\n onTableChange={handleChangeRequest}\n onTitleChange={handleTitleChange}\n onConfigChange={handleConfigChange}\n readOnly={readOnly}\n />\n <ConfirmDatasetChangeDialog\n open={isDialogOpen}\n onConfirm={handleConfirm}\n onCancel={handleCancel}\n />\n </>\n );\n};\n"]}
1
+ {"version":3,"file":"DashboardMapSettings.js","sourceRoot":"","sources":["../src/DashboardMapSettings.tsx"],"names":[],"mappings":"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","sourcesContent":["import {getTableIdentity, type DataTable} from '@sqlrooms/duckdb';\nimport type {BlockSettingsComponentProps} from '@sqlrooms/documents';\nimport {\n ConfirmDatasetChangeDialog,\n useConfirmDatasetChange,\n useStoreWithMosaicDashboard,\n useTablesWithColumns,\n} from '@sqlrooms/mosaic';\nimport {type FC, useCallback} from 'react';\nimport {DeckMapSettingsPanel} from './MapSettings';\nimport {\n DECK_MAP_DASHBOARD_PANEL_TYPE,\n type DeckMapDashboardPanelConfig,\n} from './dashboardConfig';\nimport type {DeckMapConfig} from './mapConfig';\n\n/**\n * Settings adapter for a Deck map panel inside a Mosaic dashboard.\n */\nexport const DeckMapDashboardSettings: FC<BlockSettingsComponentProps> = ({\n blockId,\n dashboardId,\n onClose,\n readOnly,\n}) => {\n const dashboard = useStoreWithMosaicDashboard((state) =>\n dashboardId ? state.mosaicDashboard.getDashboard(dashboardId) : undefined,\n );\n const updatePanel = useStoreWithMosaicDashboard(\n (state) => state.mosaicDashboard.updatePanel,\n );\n const setSelectedTable = useStoreWithMosaicDashboard(\n (state) => state.mosaicDashboard.setSelectedTable,\n );\n const tables = useTablesWithColumns();\n\n const handleTableChange = useCallback(\n (table: DataTable) => {\n if (readOnly) return;\n\n if (dashboardId) {\n setSelectedTable(dashboardId, getTableIdentity(table.table));\n }\n },\n [dashboardId, readOnly, setSelectedTable],\n );\n\n const handleTitleChange = useCallback(\n (title: string) => {\n if (readOnly) return;\n\n if (dashboardId) {\n updatePanel(dashboardId, blockId, {title: title || undefined});\n }\n },\n [dashboardId, blockId, readOnly, updatePanel],\n );\n\n const handleConfigChange = useCallback(\n (config: DeckMapConfig) => {\n if (readOnly || !dashboardId) return;\n updatePanel(dashboardId, blockId, {\n config: config as unknown as Record<string, unknown>,\n });\n },\n [blockId, dashboardId, readOnly, updatePanel],\n );\n\n const {handleChangeRequest, handleConfirm, handleCancel, isDialogOpen} =\n useConfirmDatasetChange(handleTableChange);\n\n const panel = dashboard?.panels.find((candidate) => candidate.id === blockId);\n const mapConfig =\n panel?.type === DECK_MAP_DASHBOARD_PANEL_TYPE\n ? (panel.config as DeckMapDashboardPanelConfig)\n : undefined;\n\n if (!dashboard) {\n return (\n <div className=\"flex h-full items-center justify-center p-4\">\n <p className=\"text-muted-foreground text-sm\">Dashboard not found</p>\n </div>\n );\n }\n\n if (!panel || !mapConfig) {\n return (\n <div className=\"flex h-full items-center justify-center p-4\">\n <p className=\"text-muted-foreground text-sm\">Map panel not found</p>\n </div>\n );\n }\n\n return (\n <>\n <DeckMapSettingsPanel\n title={panel.title}\n selectedTable={dashboard.selectedTable}\n config={mapConfig}\n tables={tables}\n onClose={onClose}\n onTableChange={handleChangeRequest}\n onTitleChange={handleTitleChange}\n onConfigChange={handleConfigChange}\n readOnly={readOnly}\n customConfig={mapConfig.configMode === 'custom'}\n />\n <ConfirmDatasetChangeDialog\n open={isDialogOpen}\n onConfirm={handleConfirm}\n onCancel={handleCancel}\n />\n </>\n );\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"DeckJsonMap.d.ts","sourceRoot":"","sources":["../src/DeckJsonMap.tsx"],"names":[],"mappings":"AAIA,OAAO,kCAAkC,CAAC;AAyB1C,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AAgPjB,eAAO,MAAM,WAAW,gHA0VvB,CAAC"}
1
+ {"version":3,"file":"DeckJsonMap.d.ts","sourceRoot":"","sources":["../src/DeckJsonMap.tsx"],"names":[],"mappings":"AAaA,OAAO,kCAAkC,CAAC;AAyB1C,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AA8SjB,eAAO,MAAM,WAAW,gHAsXvB,CAAC"}