@sqlrooms/deck 0.29.0-rc.10

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 (251) hide show
  1. package/LICENSE.md +10 -0
  2. package/README.md +654 -0
  3. package/dist/BlockMapSettings.d.ts +3 -0
  4. package/dist/BlockMapSettings.d.ts.map +1 -0
  5. package/dist/BlockMapSettings.js +57 -0
  6. package/dist/BlockMapSettings.js.map +1 -0
  7. package/dist/DashboardMapSettings.d.ts +7 -0
  8. package/dist/DashboardMapSettings.d.ts.map +1 -0
  9. package/dist/DashboardMapSettings.js +52 -0
  10. package/dist/DashboardMapSettings.js.map +1 -0
  11. package/dist/DeckJsonMap.d.ts +4 -0
  12. package/dist/DeckJsonMap.d.ts.map +1 -0
  13. package/dist/DeckJsonMap.js +370 -0
  14. package/dist/DeckJsonMap.js.map +1 -0
  15. package/dist/DeckJsonMapSpec.d.ts +7617 -0
  16. package/dist/DeckJsonMapSpec.d.ts.map +1 -0
  17. package/dist/DeckJsonMapSpec.js +82 -0
  18. package/dist/DeckJsonMapSpec.js.map +1 -0
  19. package/dist/DeckMapDefaultStylesProvider.d.ts +28 -0
  20. package/dist/DeckMapDefaultStylesProvider.d.ts.map +1 -0
  21. package/dist/DeckMapDefaultStylesProvider.js +23 -0
  22. package/dist/DeckMapDefaultStylesProvider.js.map +1 -0
  23. package/dist/DeckMapSurface.d.ts +39 -0
  24. package/dist/DeckMapSurface.d.ts.map +1 -0
  25. package/dist/DeckMapSurface.js +137 -0
  26. package/dist/DeckMapSurface.js.map +1 -0
  27. package/dist/DeckMapsSlice.d.ts +99 -0
  28. package/dist/DeckMapsSlice.d.ts.map +1 -0
  29. package/dist/DeckMapsSlice.js +89 -0
  30. package/dist/DeckMapsSlice.js.map +1 -0
  31. package/dist/MapSettings.d.ts +16 -0
  32. package/dist/MapSettings.d.ts.map +1 -0
  33. package/dist/MapSettings.js +292 -0
  34. package/dist/MapSettings.js.map +1 -0
  35. package/dist/MapSettingsControls.d.ts +40 -0
  36. package/dist/MapSettingsControls.d.ts.map +1 -0
  37. package/dist/MapSettingsControls.js +54 -0
  38. package/dist/MapSettingsControls.js.map +1 -0
  39. package/dist/ai.d.ts +261 -0
  40. package/dist/ai.d.ts.map +1 -0
  41. package/dist/ai.js +392 -0
  42. package/dist/ai.js.map +1 -0
  43. package/dist/block.d.ts +21 -0
  44. package/dist/block.d.ts.map +1 -0
  45. package/dist/block.js +105 -0
  46. package/dist/block.js.map +1 -0
  47. package/dist/blockDocumentRegistration.d.ts +45 -0
  48. package/dist/blockDocumentRegistration.d.ts.map +1 -0
  49. package/dist/blockDocumentRegistration.js +68 -0
  50. package/dist/blockDocumentRegistration.js.map +1 -0
  51. package/dist/createDeckJsonSpecFromDatasets.d.ts +11 -0
  52. package/dist/createDeckJsonSpecFromDatasets.d.ts.map +1 -0
  53. package/dist/createDeckJsonSpecFromDatasets.js +85 -0
  54. package/dist/createDeckJsonSpecFromDatasets.js.map +1 -0
  55. package/dist/createOrUpdateDeckMapResource.d.ts +81 -0
  56. package/dist/createOrUpdateDeckMapResource.d.ts.map +1 -0
  57. package/dist/createOrUpdateDeckMapResource.js +82 -0
  58. package/dist/createOrUpdateDeckMapResource.js.map +1 -0
  59. package/dist/dashboard.d.ts +6 -0
  60. package/dist/dashboard.d.ts.map +1 -0
  61. package/dist/dashboard.js +337 -0
  62. package/dist/dashboard.js.map +1 -0
  63. package/dist/dashboardConfig.d.ts +25 -0
  64. package/dist/dashboardConfig.d.ts.map +1 -0
  65. package/dist/dashboardConfig.js +82 -0
  66. package/dist/dashboardConfig.js.map +1 -0
  67. package/dist/dashboardIntegration.d.ts +3 -0
  68. package/dist/dashboardIntegration.d.ts.map +1 -0
  69. package/dist/dashboardIntegration.js +16 -0
  70. package/dist/dashboardIntegration.js.map +1 -0
  71. package/dist/datasetSourceUtils.d.ts +16 -0
  72. package/dist/datasetSourceUtils.d.ts.map +1 -0
  73. package/dist/datasetSourceUtils.js +25 -0
  74. package/dist/datasetSourceUtils.js.map +1 -0
  75. package/dist/datasets/PreparedDatasetStore.d.ts +120 -0
  76. package/dist/datasets/PreparedDatasetStore.d.ts.map +1 -0
  77. package/dist/datasets/PreparedDatasetStore.js +268 -0
  78. package/dist/datasets/PreparedDatasetStore.js.map +1 -0
  79. package/dist/datasets/helpers.d.ts +57 -0
  80. package/dist/datasets/helpers.d.ts.map +1 -0
  81. package/dist/datasets/helpers.js +155 -0
  82. package/dist/datasets/helpers.js.map +1 -0
  83. package/dist/datasets/normalizeDatasets.d.ts +11 -0
  84. package/dist/datasets/normalizeDatasets.d.ts.map +1 -0
  85. package/dist/datasets/normalizeDatasets.js +50 -0
  86. package/dist/datasets/normalizeDatasets.js.map +1 -0
  87. package/dist/datasets/tableAdapter.d.ts +11 -0
  88. package/dist/datasets/tableAdapter.d.ts.map +1 -0
  89. package/dist/datasets/tableAdapter.js +11 -0
  90. package/dist/datasets/tableAdapter.js.map +1 -0
  91. package/dist/datasets/tableDatasetSql.d.ts +16 -0
  92. package/dist/datasets/tableDatasetSql.d.ts.map +1 -0
  93. package/dist/datasets/tableDatasetSql.js +49 -0
  94. package/dist/datasets/tableDatasetSql.js.map +1 -0
  95. package/dist/datasets/types.d.ts +40 -0
  96. package/dist/datasets/types.d.ts.map +1 -0
  97. package/dist/datasets/types.js +2 -0
  98. package/dist/datasets/types.js.map +1 -0
  99. package/dist/datasets/usePreparedDatasetStates.d.ts +16 -0
  100. package/dist/datasets/usePreparedDatasetStates.d.ts.map +1 -0
  101. package/dist/datasets/usePreparedDatasetStates.js +59 -0
  102. package/dist/datasets/usePreparedDatasetStates.js.map +1 -0
  103. package/dist/index.d.ts +34 -0
  104. package/dist/index.d.ts.map +1 -0
  105. package/dist/index.js +27 -0
  106. package/dist/index.js.map +1 -0
  107. package/dist/json/aggregateWaypoints.d.ts +14 -0
  108. package/dist/json/aggregateWaypoints.d.ts.map +1 -0
  109. package/dist/json/aggregateWaypoints.js +190 -0
  110. package/dist/json/aggregateWaypoints.js.map +1 -0
  111. package/dist/json/colorScaleFunction.d.ts +27 -0
  112. package/dist/json/colorScaleFunction.d.ts.map +1 -0
  113. package/dist/json/colorScaleFunction.js +46 -0
  114. package/dist/json/colorScaleFunction.js.map +1 -0
  115. package/dist/json/compileColorScale.d.ts +12 -0
  116. package/dist/json/compileColorScale.d.ts.map +1 -0
  117. package/dist/json/compileColorScale.js +125 -0
  118. package/dist/json/compileColorScale.js.map +1 -0
  119. package/dist/json/compileGeoArrowAccessor.d.ts +18 -0
  120. package/dist/json/compileGeoArrowAccessor.d.ts.map +1 -0
  121. package/dist/json/compileGeoArrowAccessor.js +129 -0
  122. package/dist/json/compileGeoArrowAccessor.js.map +1 -0
  123. package/dist/json/createDeckJsonConfiguration.d.ts +9 -0
  124. package/dist/json/createDeckJsonConfiguration.d.ts.map +1 -0
  125. package/dist/json/createDeckJsonConfiguration.js +315 -0
  126. package/dist/json/createDeckJsonConfiguration.js.map +1 -0
  127. package/dist/json/defaultClasses.d.ts +12 -0
  128. package/dist/json/defaultClasses.d.ts.map +1 -0
  129. package/dist/json/defaultClasses.js +35 -0
  130. package/dist/json/defaultClasses.js.map +1 -0
  131. package/dist/json/extractColorScaleLegends.d.ts +8 -0
  132. package/dist/json/extractColorScaleLegends.d.ts.map +1 -0
  133. package/dist/json/extractColorScaleLegends.js +86 -0
  134. package/dist/json/extractColorScaleLegends.js.map +1 -0
  135. package/dist/json/heatmapDefaults.d.ts +8 -0
  136. package/dist/json/heatmapDefaults.d.ts.map +1 -0
  137. package/dist/json/heatmapDefaults.js +14 -0
  138. package/dist/json/heatmapDefaults.js.map +1 -0
  139. package/dist/json/layerCompatibility.d.ts +21 -0
  140. package/dist/json/layerCompatibility.d.ts.map +1 -0
  141. package/dist/json/layerCompatibility.js +168 -0
  142. package/dist/json/layerCompatibility.js.map +1 -0
  143. package/dist/json/layerConfig.d.ts +14 -0
  144. package/dist/json/layerConfig.d.ts.map +1 -0
  145. package/dist/json/layerConfig.js +54 -0
  146. package/dist/json/layerConfig.js.map +1 -0
  147. package/dist/json/layers/DeckColumnLayer.d.ts +10 -0
  148. package/dist/json/layers/DeckColumnLayer.d.ts.map +1 -0
  149. package/dist/json/layers/DeckColumnLayer.js +26 -0
  150. package/dist/json/layers/DeckColumnLayer.js.map +1 -0
  151. package/dist/json/layers/DeckH3HexagonLayer.d.ts +37 -0
  152. package/dist/json/layers/DeckH3HexagonLayer.d.ts.map +1 -0
  153. package/dist/json/layers/DeckH3HexagonLayer.js +118 -0
  154. package/dist/json/layers/DeckH3HexagonLayer.js.map +1 -0
  155. package/dist/json/layers/DeckTripsLayer.d.ts +46 -0
  156. package/dist/json/layers/DeckTripsLayer.d.ts.map +1 -0
  157. package/dist/json/layers/DeckTripsLayer.js +170 -0
  158. package/dist/json/layers/DeckTripsLayer.js.map +1 -0
  159. package/dist/json/layers/createTableToRecordBatchAdapter.d.ts +20 -0
  160. package/dist/json/layers/createTableToRecordBatchAdapter.d.ts.map +1 -0
  161. package/dist/json/layers/createTableToRecordBatchAdapter.js +81 -0
  162. package/dist/json/layers/createTableToRecordBatchAdapter.js.map +1 -0
  163. package/dist/json/layers/index.d.ts +5 -0
  164. package/dist/json/layers/index.d.ts.map +1 -0
  165. package/dist/json/layers/index.js +5 -0
  166. package/dist/json/layers/index.js.map +1 -0
  167. package/dist/json/rewriteGeoArrowAccessors.d.ts +7 -0
  168. package/dist/json/rewriteGeoArrowAccessors.d.ts.map +1 -0
  169. package/dist/json/rewriteGeoArrowAccessors.js +55 -0
  170. package/dist/json/rewriteGeoArrowAccessors.js.map +1 -0
  171. package/dist/mapAiConfig.d.ts +111 -0
  172. package/dist/mapAiConfig.d.ts.map +1 -0
  173. package/dist/mapAiConfig.js +69 -0
  174. package/dist/mapAiConfig.js.map +1 -0
  175. package/dist/mapConfig.d.ts +73 -0
  176. package/dist/mapConfig.d.ts.map +1 -0
  177. package/dist/mapConfig.js +47 -0
  178. package/dist/mapConfig.js.map +1 -0
  179. package/dist/mapConfigUtils.d.ts +111 -0
  180. package/dist/mapConfigUtils.d.ts.map +1 -0
  181. package/dist/mapConfigUtils.js +502 -0
  182. package/dist/mapConfigUtils.js.map +1 -0
  183. package/dist/mapDataPolicy.d.ts +9 -0
  184. package/dist/mapDataPolicy.d.ts.map +1 -0
  185. package/dist/mapDataPolicy.js +25 -0
  186. package/dist/mapDataPolicy.js.map +1 -0
  187. package/dist/mapFit.d.ts +62 -0
  188. package/dist/mapFit.d.ts.map +1 -0
  189. package/dist/mapFit.js +325 -0
  190. package/dist/mapFit.js.map +1 -0
  191. package/dist/mapLayerConfigUtils.d.ts +54 -0
  192. package/dist/mapLayerConfigUtils.d.ts.map +1 -0
  193. package/dist/mapLayerConfigUtils.js +296 -0
  194. package/dist/mapLayerConfigUtils.js.map +1 -0
  195. package/dist/mapResourceAuthoring.d.ts +40 -0
  196. package/dist/mapResourceAuthoring.d.ts.map +1 -0
  197. package/dist/mapResourceAuthoring.js +266 -0
  198. package/dist/mapResourceAuthoring.js.map +1 -0
  199. package/dist/mosaic.d.ts +9 -0
  200. package/dist/mosaic.d.ts.map +1 -0
  201. package/dist/mosaic.js +8 -0
  202. package/dist/mosaic.js.map +1 -0
  203. package/dist/prepare/detectGeometryColumn.d.ts +19 -0
  204. package/dist/prepare/detectGeometryColumn.d.ts.map +1 -0
  205. package/dist/prepare/detectGeometryColumn.js +126 -0
  206. package/dist/prepare/detectGeometryColumn.js.map +1 -0
  207. package/dist/prepare/geoarrow.d.ts +18 -0
  208. package/dist/prepare/geoarrow.d.ts.map +1 -0
  209. package/dist/prepare/geoarrow.js +114 -0
  210. package/dist/prepare/geoarrow.js.map +1 -0
  211. package/dist/prepare/geometryDecoder.d.ts +8 -0
  212. package/dist/prepare/geometryDecoder.d.ts.map +1 -0
  213. package/dist/prepare/geometryDecoder.js +2 -0
  214. package/dist/prepare/geometryDecoder.js.map +1 -0
  215. package/dist/prepare/prepareDeckDataset.d.ts +57 -0
  216. package/dist/prepare/prepareDeckDataset.d.ts.map +1 -0
  217. package/dist/prepare/prepareDeckDataset.js +112 -0
  218. package/dist/prepare/prepareDeckDataset.js.map +1 -0
  219. package/dist/prepare/toGeoJsonBinary.d.ts +9 -0
  220. package/dist/prepare/toGeoJsonBinary.d.ts.map +1 -0
  221. package/dist/prepare/toGeoJsonBinary.js +26 -0
  222. package/dist/prepare/toGeoJsonBinary.js.map +1 -0
  223. package/dist/prepare/types.d.ts +25 -0
  224. package/dist/prepare/types.d.ts.map +1 -0
  225. package/dist/prepare/types.js +2 -0
  226. package/dist/prepare/types.js.map +1 -0
  227. package/dist/prepare/wkbDecoder.d.ts +3 -0
  228. package/dist/prepare/wkbDecoder.d.ts.map +1 -0
  229. package/dist/prepare/wkbDecoder.js +419 -0
  230. package/dist/prepare/wkbDecoder.js.map +1 -0
  231. package/dist/prepare/wkbParser.d.ts +24 -0
  232. package/dist/prepare/wkbParser.d.ts.map +1 -0
  233. package/dist/prepare/wkbParser.js +139 -0
  234. package/dist/prepare/wkbParser.js.map +1 -0
  235. package/dist/types.d.ts +92 -0
  236. package/dist/types.d.ts.map +1 -0
  237. package/dist/types.js +11 -0
  238. package/dist/types.js.map +1 -0
  239. package/dist/useDeckMapDatasetSchema.d.ts +33 -0
  240. package/dist/useDeckMapDatasetSchema.d.ts.map +1 -0
  241. package/dist/useDeckMapDatasetSchema.js +138 -0
  242. package/dist/useDeckMapDatasetSchema.js.map +1 -0
  243. package/dist/useDeckMapDatasets.d.ts +17 -0
  244. package/dist/useDeckMapDatasets.d.ts.map +1 -0
  245. package/dist/useDeckMapDatasets.js +69 -0
  246. package/dist/useDeckMapDatasets.js.map +1 -0
  247. package/dist/useDeckMapFitToBounds.d.ts +31 -0
  248. package/dist/useDeckMapFitToBounds.d.ts.map +1 -0
  249. package/dist/useDeckMapFitToBounds.js +58 -0
  250. package/dist/useDeckMapFitToBounds.js.map +1 -0
  251. package/package.json +98 -0
package/dist/ai.js ADDED
@@ -0,0 +1,392 @@
1
+ import { tool } from 'ai';
2
+ import { z } from 'zod';
3
+ import { MAP_TOOL_KEY, createDashboardAgentTool, createDashboardAiTools as createMosaicDashboardAiTools, ensureTable, ensurePanel, } from '@sqlrooms/mosaic';
4
+ import { createDeckMapDashboardPanelConfig, DECK_MAP_DASHBOARD_PANEL_TYPE, } from './dashboardConfig';
5
+ import { DECK_TABLE_DATASET_SOURCE_RELATION } from './datasets/tableDatasetSql';
6
+ import { getFirstDatasetSourceTableName, hasSqlOnlyDatasetSource, } from './datasetSourceUtils';
7
+ import { quoteDeckMapSqlIdentifier } from './mapConfigUtils';
8
+ export { getFirstDatasetSourceTableName, hasSqlOnlyDatasetSource };
9
+ export const DECK_MAP_AI_INSTRUCTIONS = `
10
+ Deck map tools:
11
+ - create_deck_map_config validates and returns a reusable native Deck JSON map config without requiring a dashboard artifact.
12
+ - create_dashboard_map creates or updates an interactive map panel inside a dashboard from a native Deck JSON map config.
13
+ - Use map tools when the user asks for a map, geospatial/spatial visualization, locations, longitude/latitude data, or geometry columns.
14
+ - CONFIG MODE: Every map config must include a configMode field ("basic" or "custom") that determines how the map was authored and whether the UI settings panel is available.
15
+ - "basic" (default): Use for straightforward requests — single layer, standard color scale, simple geometry binding. Stick ONLY to properties that the UI configurator supports: layer @@type, visibility, color scale (@@function colorScale), point radius (numeric getRadius with radiusUnits), line width (numeric getWidth with widthUnits), geometry/H3/arc column bindings, extrusion with a single elevation column. Do NOT use string accessors (@@= expressions), custom extensions, multiple layers, or advanced deck.gl props in basic mode. The user can fine-tune these maps through the settings panel.
16
+ - "custom": Use when the request demands creative, complex, or advanced visualization — multiple layers, data-driven accessors (@@= expressions), custom color arrays, advanced deck.gl props (opacity, transitions, material, highlightColor, etc.), layer extensions, or any props not representable in the UI configurator. The UI settings panel will be disabled for custom configs; users edit via the JSON editor instead.
17
+ - Decision rule: If the map can be fully expressed with a single layer + basic color scale + simple numeric radius/width, use "basic". Otherwise use "custom".
18
+ - Author maps with config.spec.layers using Deck JSON layer classes in @@type, such as GeoArrowScatterplotLayer, GeoArrowHeatmapLayer, GeoArrowPolygonLayer, GeoArrowPathLayer, GeoArrowTripsLayer, GeoArrowArcLayer, or GeoArrowH3HexagonLayer.
19
+ - LAYER SELECTION: Choose the layer type based on the geometry type in the data.
20
+ IMPORTANT: Only create a layer if the table contains data suitable for that layer type, or if you can transform the data into the required format with transformSql or a standalone sqlQuery. Do NOT create a layer if the data is clearly incompatible (e.g. do not create a path layer from point-only data without aggregation, do not create a polygon layer from point coordinates, do not create an arc layer without origin-destination pairs).
21
+ - Point data (lon/lat coordinates, point geometry): GeoArrowScatterplotLayer (Point layer), GeoArrowHeatmapLayer, GeoArrowColumnLayer. Requires rows with individual point positions — either separate longitude/latitude numeric columns, or a point geometry column. Each row represents one point on the map.
22
+ - Polygon data (building footprints, boundaries, areas, parcels, zones): GeoArrowPolygonLayer or GeoArrowSolidPolygonLayer. Requires a geometry column containing polygon or multipolygon WKB/GeoArrow data. Typically loaded from GeoJSON/Shapefile/GeoParquet or produced by spatial queries. Do NOT use for point data.
23
+ - Line data (roads, routes, paths, rivers): GeoArrowPathLayer. CRITICAL: GeoArrowPathLayer requires LineString geometry, NOT individual point rows. If the table has one row per waypoint (indicated by columns like path_id/route_id + order/sequence + lat/lon), you MUST aggregate them with transformSql: "SELECT path_id, label, ST_AsWKB(ST_MakeLine(LIST(ST_Point(lon, lat) ORDER BY waypoint_order))) AS geom FROM ${DECK_TABLE_DATASET_SOURCE_RELATION} GROUP BY path_id, label". Set geometryColumn to "geom" and geometryEncodingHint to "wkb". If the table already has a geometry/geom column with linestring data, use it directly with tableName. NEVER pass raw waypoint rows to GeoArrowPathLayer — it will fail.
24
+ - Animated trip data (routes with timestamps): GeoArrowTripsLayer. Same geometry requirements as GeoArrowPathLayer (LineString), plus a timestamps column. The transformSql MUST aggregate both the geometry and timestamps: "SELECT path_id, label, ST_AsWKB(ST_MakeLine(LIST(ST_Point(lon, lat) ORDER BY waypoint_order))) AS geom, LIST(timestamp ORDER BY waypoint_order) AS timestamps FROM ${DECK_TABLE_DATASET_SOURCE_RELATION} GROUP BY path_id, label". Set geometryColumn to "geom", geometryEncodingHint to "wkb", and _sqlroomsBinding.timestampColumn to "timestamps". The timestamps column must be a list of numbers (seconds) matching the order of waypoints in the linestring. Also set currentTime on the layer to control animation position. Do NOT use unless the data has or can produce both paths and ordered timestamps.
25
+ - CRITICAL geometryColumn rule: The geometryColumn field (in datasets[id].geometryColumn, _sqlroomsBinding.geometryColumn, and fitToData.geometryColumn) MUST match the exact column alias that produces the WKB geometry in the final query output — typically the "AS geom" alias in ST_AsWKB(...) AS geom. It must NEVER be set to a GROUP BY key, an ID column, or any other non-geometry column. For example, if the transformSql is "SELECT path_id, ST_AsWKB(ST_MakeLine(...)) AS geom ... GROUP BY path_id", geometryColumn must be "geom" (the geometry output), NOT "path_id" (the grouping key). Setting geometryColumn to a non-geometry column will cause the layer to fail silently.
26
+ - Arc data (origin-destination pairs): GeoArrowArcLayer. Requires two sets of coordinates per row (source and target). The table must have source_lon/source_lat AND target_lon/target_lat columns (or equivalent). The dataset source MUST use transformSql that creates WKB geometry columns from lat/lon, for example: "SELECT *, ST_AsWKB(ST_Point(source_lon, source_lat)) AS source_geom, ST_AsWKB(ST_Point(target_lon, target_lat)) AS target_geom FROM ${DECK_TABLE_DATASET_SOURCE_RELATION}". Set sourceGeometryColumn to "source_geom" and targetGeometryColumn to "target_geom". Set geometryEncodingHint to "wkb". To render straight lines instead of arcs, set "getHeight": 0 on the layer. Do NOT use for data with only one set of coordinates per row. When the source data has H3 indices instead of lat/lon, convert H3 to coordinates using h3_cell_to_lng(h3_index) and h3_cell_to_lat(h3_index) (the H3 extension is pre-loaded at startup), for example: "SELECT *, ST_AsWKB(ST_Point(h3_cell_to_lng(source_h3), h3_cell_to_lat(source_h3))) AS source_geom, ST_AsWKB(ST_Point(h3_cell_to_lng(target_h3), h3_cell_to_lat(target_h3))) AS target_geom FROM ${DECK_TABLE_DATASET_SOURCE_RELATION}". Do NOT use h3_latlng() — it does not exist.
27
+ - H3 hexagon data (h3 index column): GeoArrowH3HexagonLayer. Requires a column containing H3 string indices. Bind to dataset with _sqlroomsBinding.dataset. Set "getHexagon": "@@=h3_column_name" where h3_column_name is the column containing H3 string indices. Always include "fitToData": {"dataset": "datasetId"} so the map can zoom to the data extent. Do NOT use unless the table has an H3 index column. DuckDB H3 extension functions: h3_cell_to_lat(index), h3_cell_to_lng(index), h3_cell_to_latlng(index). Do NOT use h3_latlng(), h3_to_lat(), or other non-existent function names.
28
+ - CRITICAL: The transformSql and sqlQuery fields must contain ONLY a single SELECT statement. NEVER put INSTALL, LOAD, CREATE, or other DDL/meta-commands in dataset SQL — they will fail because dataset SQL is wrapped in a subquery at runtime. Extensions like h3 and spatial are pre-loaded at startup.
29
+ - GeoJSON files typically contain polygon or multipolygon features (boundaries, buildings, parcels); use GeoArrowPolygonLayer for these. If a GeoJSON file contains point features, use GeoArrowScatterplotLayer (Point layer) instead.
30
+ - RADIUS AND WIDTH: For GeoArrowScatterplotLayer (Point layer) use getRadius with radiusUnits: "pixels" (typically 2–6 pixels); large radii cause overdraw and rendering lag, especially with many points. For GeoArrowColumnLayer use the "radius" property (NOT getRadius) — it sets column radius in meters; typical values are 20–200 for city-scale data or smaller for dense datasets. Do NOT use getRadius or radiusUnits on column layers. For GeoArrowArcLayer, GeoArrowPathLayer, and GeoArrowTripsLayer use getWidth with widthUnits: "pixels" (typically 1–3 pixels).
31
+ - HEATMAP: For GeoArrowHeatmapLayer, do NOT set colorRange manually. The UI provides a scheme selector that generates the correct color array. If you set colorRange to hand-picked RGB arrays, it will be out of sync with the scheme selector shown in the UI. Just omit colorRange entirely and let the default apply — users can change the scheme through the map settings panel.
32
+ - ARC vs LINE: GeoArrowArcLayer renders curved 3D arcs by default. If the user asks for "lines" or "straight connections" between origin-destination pairs (not arcs), set "getHeight": 0 on the layer to render flat straight lines. Use arcs for flight routes or connections where the curve adds clarity; use flat lines for direct relationships, edges, or when the user explicitly requests lines.
33
+ - ELEVATION: For extruded layers, getElevation with @@function "scale" passes the raw field value as meters. Use elevationScale on the layer to multiply values to a useful visual height. For example, if the field is "floors" (1-10), set elevationScale to 3 (meters per floor). Do NOT use negative values for elevation. Avoid using diverging scales for elevation. IMPORTANT: Keep elevation moderate — if extruded polygons or H3 hexagons are too tall, users can't see the tops when zoomed in. Prefer elevationScale values that produce heights of a few hundred meters at most for city-scale data. A good rule of thumb: the maximum elevation (field max × elevationScale) should not exceed ~500m for typical zoom levels.
34
+ - Bind layers to datasets with _sqlroomsBinding.dataset and put tableName, tableName+transformSql, or sqlQuery sources in config.datasets.
35
+ - Use source.tableName for direct table-backed datasets. Use source.tableName plus source.transformSql when the map needs generated geometry or aggregation but should still follow the dashboard selected table. transformSql must read from ${DECK_TABLE_DATASET_SOURCE_RELATION}, not from the authored table name.
36
+ - Use source.sqlQuery only for a standalone literal query that should remain pinned to the authored SQL. Dashboard selected table replacement applies only to structured tableName sources, not literal sqlQuery sources.
37
+ - IMPORTANT: Always pass tableName in the create_dashboard_map tool params (the top-level tableName field). Use the table currently selected in the dashboard (dashboard.selectedTable from list_dashboard_panels). At runtime, the dashboard's selected table overrides structured source.tableName values — this param seeds or changes that selection.
38
+ - IMPORTANT: If you are creating a structured table-backed map layer for a table that is NOT the currently selected dashboard table, you MUST switch the dashboard's selected table to that dataset BEFORE or WHEN calling create_dashboard_map (pass the correct tableName). Structured table-backed map panels resolve data from the dashboard's active table — if you don't switch it, the layer will query the wrong table and fail.
39
+ - IMPORTANT: When referencing tables in tableName or sqlQuery, use ONLY the bare table name (e.g. "my_table") or schema-qualified name (e.g. "main.my_table"). NEVER include the database/catalog prefix (e.g. do NOT use "sqlrooms-cli.main.my_table") — the catalog does not exist in the query execution context.
40
+ - IMPORTANT: For point data with longitude/latitude columns that should follow dashboard table switching, use source.tableName plus source.transformSql to create a geometry column, for example: "SELECT *, ST_AsWKB(ST_Point(\\"Longitude\\", \\"Latitude\\")) AS \\"__sqlrooms_geom\\" FROM ${DECK_TABLE_DATASET_SOURCE_RELATION} WHERE \\"Longitude\\" IS NOT NULL AND \\"Latitude\\" IS NOT NULL". Set geometryColumn to the same name used in the AS clause (e.g. "__sqlrooms_geom") and geometryEncodingHint to "wkb".
41
+ - IMPORTANT: When providing fitToData, it MUST be a flat object (NOT nested by dataset ID). Include either longitudeColumn+latitudeColumn (for point data with separate coordinate columns) OR geometryColumn (for data with a WKB geometry column like GeoJSON). For H3 hexagon layers, just specify the dataset: "fitToData": {"dataset": "datasetId"} — the H3 column is auto-detected from the layer binding. For GeoJSON/spatial files with a "geom" column, use: "fitToData": {"dataset": "datasetId", "geometryColumn": "geom"}. For point data use: "fitToData": {"dataset": "datasetId", "longitudeColumn": "lon", "latitudeColumn": "lat"}. NEVER nest fitToData as {"datasetId": {...}} — always use a flat object with "dataset" as a string field.
42
+ - IMPORTANT: For GeoJSON or spatial files that already have a native geometry column (e.g. "geometry", "geom"), use the table directly with source.tableName (no sqlQuery needed), set the dataset's geometryColumn to "geom", set geometryEncodingHint to "wkb", and use fitToData with geometryColumn: {"dataset": "datasetId", "geometryColumn": "geom"}.
43
+ - IMPORTANT: When a GeoJSON file (.geojson) is loaded as a table, DuckDB uses ST_Read to produce a table with a WKB "geom" column and all feature properties as columns. Use source.tableName, set geometryColumn to "geom" and geometryEncodingHint to "wkb". Use "fitToData": {"dataset": "datasetId", "geometryColumn": "geom"} to zoom to the data extent.
44
+ - For data-driven color, use native Deck JSON accessors with {"@@function":"colorScale", "field":"...", "type":"sequential"|"diverging"|"quantize"|"quantile"|"categorical", "scheme":"...", "domain":"auto"} on color properties such as getFillColor, getLineColor, getColor, getSourceColor, or getTargetColor. Valid schemes: for "categorical" type use one of Accent, Dark2, Paired, Pastel1, Pastel2, Set1, Set2, Set3, Tableau10, Observable10, Category10. For "sequential" use Viridis, Inferno, Magma, Plasma, Turbo, Blues, Greens, Oranges, Reds, Purples, etc. For "diverging" use RdBu, Spectral, RdYlGn, BrBG, PiYG, etc. IMPORTANT: The colorScale "field" must reference a column that exists in the FINAL query output (after any GROUP BY aggregation). Do not reference columns that are lost during aggregation.
45
+ - IMPORTANT: Enabling a color scale means adding a {"@@function":"colorScale", ...} accessor to a compatible layer color property. The top-level showLegends field only controls whether already-defined color scale legends are visible; showLegends by itself does NOT create or enable data-driven color.
46
+ - Map panels default to a 100000-row runtime data limit; use config.dataPolicy.maxRows only when the map genuinely needs a panel-specific limit.
47
+ - Create maps with a SINGLE layer unless the user explicitly asks for multiple layers. If you think multiple layers would better serve the user's request, ask the user for confirmation before adding them.
48
+ - IMPORTANT: Browsers limit the number of active WebGL contexts (typically 8–16 per page). Each map panel uses one context. Do NOT create more than 4–5 map panels in a single dashboard — exceeding the limit causes older maps to lose their rendering context and show errors. If the user asks for many datasets, prefer combining compatible layers into fewer maps rather than creating one map per dataset.
49
+ - After calling create_dashboard_map, call list_dashboard_panels before your final response and check the map panel issue. If it has a render-error, repair the map config in place instead of saying the map is complete.
50
+ - BASEMAPS: Omit mapStyle unless the user explicitly requests a custom basemap. Maps inherit the host application's theme-aware basemap when available, with a token-free fallback. Do NOT use Mapbox styles (mapbox://styles/...) because a Mapbox access token may not be available. For an explicitly requested custom basemap, use a token-free MapLibre-compatible style URL.
51
+ `;
52
+ function createDeckMapDashboardExtraTools(extraTools) {
53
+ return (params) => ({
54
+ ...createDeckMapDashboardAiTools(params),
55
+ ...(extraTools?.(params) ?? {}),
56
+ });
57
+ }
58
+ /**
59
+ * Returns AI instructions for dashboards with Deck.gl map support.
60
+ * Provides guidance on when and how to use map visualizations.
61
+ *
62
+ * @returns Instructions string for AI agents
63
+ */
64
+ export function getDashboardWithDeckMapAiInstructions() {
65
+ return `${DECK_MAP_AI_INSTRUCTIONS.trim()}`;
66
+ }
67
+ /**
68
+ * Creates dashboard AI tools with built-in Deck.gl map support.
69
+ * Extends standard dashboard tools with map visualization capabilities.
70
+ *
71
+ * @param options - Dashboard AI tools configuration options
72
+ * @returns Record mapping tool names to tool instances, including map tools
73
+ */
74
+ export function createDashboardWithDeckMapAiTools(options) {
75
+ return createMosaicDashboardAiTools({
76
+ ...options,
77
+ extraTools: createDeckMapDashboardExtraTools(options.extraTools),
78
+ });
79
+ }
80
+ /**
81
+ * Creates a dashboard agent tool with built-in Deck.gl map support.
82
+ * Extends the standard dashboard agent with map creation capabilities.
83
+ *
84
+ * @template TState - Store state type extending MosaicDashboardStoreState
85
+ * @param options - Dashboard agent configuration options
86
+ * @returns Dashboard agent tool with map support
87
+ */
88
+ export function createDashboardAgentToolWithDeckMaps(options) {
89
+ return createDashboardAgentTool({
90
+ ...options,
91
+ additionalInstructions: [
92
+ options.additionalInstructions,
93
+ DECK_MAP_AI_INSTRUCTIONS.trim(),
94
+ ]
95
+ .filter(Boolean)
96
+ .join('\n\n'),
97
+ extraTools: createDeckMapDashboardExtraTools(options.extraTools),
98
+ });
99
+ }
100
+ const DeckMapLayerBindingConfig = z.looseObject({
101
+ dataset: z.string().optional(),
102
+ geometryColumn: z.string().optional(),
103
+ geometryEncodingHint: z.enum(['geoarrow', 'wkb', 'wkt']).optional(),
104
+ sourceGeometryColumn: z.string().optional(),
105
+ targetGeometryColumn: z.string().optional(),
106
+ timestampColumn: z.string().optional(),
107
+ hexagonColumn: z.string().optional(),
108
+ });
109
+ const DeckMapLayerSpec = z.looseObject({
110
+ '@@type': z.string().optional(),
111
+ id: z.string().optional(),
112
+ _sqlroomsBinding: DeckMapLayerBindingConfig.optional(),
113
+ });
114
+ const DeckMapSpec = z.looseObject({
115
+ initialViewState: z.record(z.string(), z.unknown()).optional(),
116
+ viewState: z.record(z.string(), z.unknown()).optional(),
117
+ controller: z.unknown().optional(),
118
+ layers: z.array(DeckMapLayerSpec).optional(),
119
+ });
120
+ const DeckMapDatasetSource = z.looseObject({
121
+ tableName: z.string().optional(),
122
+ transformSql: z.string().optional(),
123
+ sqlQuery: z.string().optional(),
124
+ });
125
+ const DeckMapDatasetConfig = z.looseObject({
126
+ source: DeckMapDatasetSource.optional(),
127
+ geometryColumn: z.string().optional(),
128
+ geometryEncodingHint: z.enum(['geoarrow', 'wkb', 'wkt']).optional(),
129
+ });
130
+ const DeckMapDataPolicyConfig = z.looseObject({
131
+ disabled: z.boolean().optional(),
132
+ maxRows: z.number().int().min(1).optional(),
133
+ reason: z.string().optional(),
134
+ });
135
+ export const DeckMapDashboardConfigParameter = z.looseObject({
136
+ spec: DeckMapSpec.describe('Deck JSON map spec as an object. Use spec.layers[].@@type for layer classes such as GeoArrowScatterplotLayer (Point layer), GeoArrowHeatmapLayer, GeoArrowPolygonLayer, GeoArrowPathLayer, or GeoArrowArcLayer.'),
137
+ datasets: z
138
+ .record(z.string(), DeckMapDatasetConfig)
139
+ .describe('Datasets keyed by dataset id. Layers bind to these ids through _sqlroomsBinding.dataset. Each dataset source may use tableName, tableName+transformSql, or sqlQuery.'),
140
+ configMode: z
141
+ .enum(['basic', 'custom'])
142
+ .optional()
143
+ .describe('Config authoring mode. Use "basic" (default) for straightforward single-layer maps that the user can tweak via the UI settings panel. Use "custom" for complex, multi-layer, or creative maps that use advanced deck.gl props beyond what the UI configurator supports — the settings panel will be disabled for custom configs.'),
144
+ mapStyle: z.string().optional(),
145
+ mapProps: z.record(z.string(), z.unknown()).optional(),
146
+ showLegends: z
147
+ .boolean()
148
+ .optional()
149
+ .describe('Whether to show color scale legends on the map. Defaults to true; omit or set true unless the user explicitly asks to hide legends.'),
150
+ interaction: z.record(z.string(), z.unknown()).optional(),
151
+ fitToData: z
152
+ .object({
153
+ dataset: z.string().describe('Dataset id to compute bounds from.'),
154
+ longitudeColumn: z
155
+ .string()
156
+ .optional()
157
+ .describe('Longitude column name for point data.'),
158
+ latitudeColumn: z
159
+ .string()
160
+ .optional()
161
+ .describe('Latitude column name for point data.'),
162
+ geometryColumn: z
163
+ .string()
164
+ .optional()
165
+ .describe('WKB geometry column name for computing bounds.'),
166
+ h3Column: z
167
+ .string()
168
+ .optional()
169
+ .describe('H3 hex index column for computing bounds.'),
170
+ padding: z.number().optional(),
171
+ maxZoom: z.number().optional(),
172
+ })
173
+ .optional()
174
+ .describe('Fit map view to data bounds. Provide dataset plus either geometryColumn (for WKB geometry) or longitudeColumn+latitudeColumn (for separate coordinate columns). Example: {"dataset": "myDataset", "geometryColumn": "geom"}'),
175
+ dataPolicy: DeckMapDataPolicyConfig.optional().describe('Optional per-map runtime data policy. Maps default to 100000 rows; set maxRows for a panel-specific override or disabled=true to bypass row-count validation.'),
176
+ settingsOpen: z.boolean().optional(),
177
+ });
178
+ export const DeckMapConfigToolParameters = z.object({
179
+ title: z.string().optional().default('Map').describe('Map title.'),
180
+ config: DeckMapDashboardConfigParameter.describe('Native Deck JSON dashboard map config. This is the canonical map representation.'),
181
+ reasoning: z
182
+ .string()
183
+ .describe('Brief rationale for creating the map config.'),
184
+ });
185
+ export const DeckMapDashboardToolParameters = DeckMapConfigToolParameters.extend({
186
+ tableName: z
187
+ .string()
188
+ .optional()
189
+ .describe('Optional table name used only to select/resolve the target dashboard table. Data sources still come from config.datasets.'),
190
+ panelId: z
191
+ .string()
192
+ .optional()
193
+ .describe('Optional panel ID. If provided, updates the existing map panel instead of creating a new one.'),
194
+ reasoning: z
195
+ .string()
196
+ .describe('Brief rationale for creating the map panel.'),
197
+ });
198
+ const DEFAULT_AI_GEOMETRY_COLUMN = '__sqlrooms_geom';
199
+ /**
200
+ * Normalizes an AI-generated map config to ensure dataset sources produce
201
+ * the expected geometry column when fitToData specifies coordinate columns
202
+ * but the dataset only uses a tableName without a transformSql.
203
+ */
204
+ function normalizeAiMapConfig(config) {
205
+ const datasets = config.datasets;
206
+ let fitToData = config.fitToData;
207
+ // Fix common AI mistake: fitToData wrapped as { datasetId: { dataset, ... } }
208
+ // instead of the expected flat { dataset, longitudeColumn, ... }.
209
+ if (fitToData && !fitToData.dataset && typeof fitToData === 'object') {
210
+ const keys = Object.keys(fitToData);
211
+ if (keys.length === 1) {
212
+ const nested = fitToData[keys[0]];
213
+ if (nested && typeof nested === 'object' && nested.dataset) {
214
+ fitToData = nested;
215
+ config = { ...config, fitToData: fitToData };
216
+ }
217
+ }
218
+ }
219
+ if (!datasets || typeof datasets !== 'object' || !fitToData) {
220
+ return config;
221
+ }
222
+ const lonCol = fitToData.longitudeColumn;
223
+ const latCol = fitToData.latitudeColumn;
224
+ if (!lonCol || !latCol) {
225
+ return config;
226
+ }
227
+ const targetDatasetId = fitToData.dataset;
228
+ if (!targetDatasetId) {
229
+ return config;
230
+ }
231
+ const targetDataset = datasets[targetDatasetId];
232
+ if (!targetDataset) {
233
+ return config;
234
+ }
235
+ const source = targetDataset.source;
236
+ // Always normalize when using tableName without transformSql and fitToData
237
+ // provides coordinate columns — the geometry must be computed from them.
238
+ if (!source?.tableName || source.sqlQuery || source.transformSql) {
239
+ return config;
240
+ }
241
+ const geometryColumn = targetDataset.geometryColumn ||
242
+ DEFAULT_AI_GEOMETRY_COLUMN;
243
+ const quotedLon = quoteDeckMapSqlIdentifier(lonCol);
244
+ const quotedLat = quoteDeckMapSqlIdentifier(latCol);
245
+ const quotedGeom = quoteDeckMapSqlIdentifier(geometryColumn);
246
+ const transformSql = [
247
+ `SELECT *, ST_AsWKB(ST_Point(${quotedLon}, ${quotedLat})) AS ${quotedGeom}`,
248
+ `FROM ${DECK_TABLE_DATASET_SOURCE_RELATION}`,
249
+ `WHERE ${quotedLon} IS NOT NULL AND ${quotedLat} IS NOT NULL`,
250
+ ].join(' ');
251
+ return {
252
+ ...config,
253
+ datasets: {
254
+ ...datasets,
255
+ [targetDatasetId]: {
256
+ ...targetDataset,
257
+ source: { tableName: source.tableName, transformSql },
258
+ geometryColumn,
259
+ geometryEncodingHint: 'wkb',
260
+ },
261
+ },
262
+ };
263
+ }
264
+ function cloneConfig(config) {
265
+ const normalized = normalizeAiMapConfig(config);
266
+ return JSON.parse(JSON.stringify(normalized));
267
+ }
268
+ /**
269
+ * Creates a dashboard-compatible Deck map panel from the native map config
270
+ * used by AI tools and embeddable map surfaces.
271
+ */
272
+ export function createDeckMapPanelFromNativeConfig(params) {
273
+ return createDeckMapDashboardPanelConfig({
274
+ title: params.title || 'Map',
275
+ ...cloneConfig(params.config),
276
+ });
277
+ }
278
+ export function createDeckMapConfigTool() {
279
+ return tool({
280
+ description: `Deck map config: validates and returns a reusable native Deck JSON map configuration without requiring a dashboard artifact.
281
+
282
+ Use when: a chat, agent, or artifact outside a dashboard needs a geospatial map config. Author the map using native Deck JSON: put layer classes in spec.layers[].@@type, bind layers to datasets through _sqlroomsBinding.dataset, and put tableName, tableName+transformSql, or sqlQuery sources in config.datasets. For data-driven colors, use color accessors such as getFillColor, getLineColor, getColor, getSourceColor, or getTargetColor with {"@@function":"colorScale", "field":"...", "type":"...", "scheme":"...", "domain":"auto"}. For categorical fields use scheme from: Tableau10, Set2, Category10, etc. For numeric fields use sequential schemes like Viridis.`,
283
+ inputSchema: DeckMapConfigToolParameters,
284
+ execute: async (params) => {
285
+ try {
286
+ const panel = createDeckMapPanelFromNativeConfig(params);
287
+ return {
288
+ llmResult: {
289
+ success: true,
290
+ details: `Created deck map config "${panel.title}".`,
291
+ data: {
292
+ kind: 'deck-map-config',
293
+ title: panel.title,
294
+ type: DECK_MAP_DASHBOARD_PANEL_TYPE,
295
+ config: panel.config,
296
+ },
297
+ },
298
+ };
299
+ }
300
+ catch (error) {
301
+ return {
302
+ llmResult: {
303
+ success: false,
304
+ errorMessage: error instanceof Error ? error.message : String(error),
305
+ },
306
+ };
307
+ }
308
+ },
309
+ });
310
+ }
311
+ /**
312
+ * Creates AI tools for Deck.gl map configuration.
313
+ * Returns tools for creating and configuring Deck.gl map panels.
314
+ *
315
+ * @returns Record mapping tool names to map configuration tools
316
+ */
317
+ export function createDeckMapAiTools() {
318
+ return {
319
+ create_deck_map_config: createDeckMapConfigTool(),
320
+ };
321
+ }
322
+ /**
323
+ * Creates a tool for adding Deck.gl map panels to dashboards.
324
+ * Supports creating new map panels or updating existing ones with native Deck JSON configs.
325
+ *
326
+ * @param params - Parameters containing dashboard and database adapters
327
+ * @returns Tool instance for creating/updating Deck.gl map panels
328
+ */
329
+ export function createDeckMapDashboardTool({ dashboardAdapter, databaseAdapter, }) {
330
+ return tool({
331
+ description: `Deck map panel: creates or updates an interactive geospatial map panel in a Mosaic dashboard from a native Deck JSON config.
332
+
333
+ Use when: the user asks for a map in a dashboard. Author the map using native Deck JSON: choose layer classes with spec.layers[].@@type, bind layers to datasets through _sqlroomsBinding.dataset, and put tableName, tableName+transformSql, or sqlQuery sources in config.datasets. For data-driven colors, use color accessors such as getFillColor, getLineColor, getColor, getSourceColor, or getTargetColor with {"@@function":"colorScale", "field":"...", "type":"...", "scheme":"...", "domain":"auto"}. For categorical fields use scheme from: Tableau10, Set2, Category10, etc. For numeric fields use sequential schemes like Viridis.`,
334
+ inputSchema: DeckMapDashboardToolParameters,
335
+ execute: async (params) => {
336
+ try {
337
+ const tableName = params.tableName ?? getFirstDatasetSourceTableName(params.config);
338
+ if (tableName) {
339
+ ensureTable(databaseAdapter, tableName);
340
+ await dashboardAdapter.setSelectedTable(tableName);
341
+ }
342
+ const panel = createDeckMapPanelFromNativeConfig(params);
343
+ if (params.panelId) {
344
+ ensurePanel(dashboardAdapter, params.panelId, DECK_MAP_DASHBOARD_PANEL_TYPE);
345
+ await dashboardAdapter.updatePanel(params.panelId, {
346
+ title: panel.title,
347
+ config: panel.config,
348
+ });
349
+ return {
350
+ llmResult: {
351
+ success: true,
352
+ details: `Updated map panel "${panel.title}".`,
353
+ data: {
354
+ panelId: params.panelId,
355
+ title: panel.title,
356
+ type: DECK_MAP_DASHBOARD_PANEL_TYPE,
357
+ config: panel.config,
358
+ },
359
+ },
360
+ };
361
+ }
362
+ const panelId = await dashboardAdapter.addPanel(panel);
363
+ return {
364
+ llmResult: {
365
+ success: true,
366
+ details: `Created map panel "${panel.title}".`,
367
+ data: {
368
+ panelId,
369
+ title: panel.title,
370
+ type: DECK_MAP_DASHBOARD_PANEL_TYPE,
371
+ config: panel.config,
372
+ },
373
+ },
374
+ };
375
+ }
376
+ catch (error) {
377
+ return {
378
+ llmResult: {
379
+ success: false,
380
+ errorMessage: error instanceof Error ? error.message : String(error),
381
+ },
382
+ };
383
+ }
384
+ },
385
+ });
386
+ }
387
+ export function createDeckMapDashboardAiTools(params) {
388
+ return {
389
+ [MAP_TOOL_KEY]: createDeckMapDashboardTool(params),
390
+ };
391
+ }
392
+ //# sourceMappingURL=ai.js.map
package/dist/ai.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.js","sourceRoot":"","sources":["../src/ai.ts"],"names":[],"mappings":"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","sourcesContent":["import {tool, type Tool} from 'ai';\nimport {z} from 'zod';\nimport {\n DashboardAiAdapter,\n MAP_TOOL_KEY,\n createDashboardAgentTool,\n createDashboardAiTools as createMosaicDashboardAiTools,\n type CreateDashboardAgentToolOptions,\n type CreateDashboardAiToolsOptions,\n ensureTable,\n ensurePanel,\n DatabaseAiAdapter,\n ExtraDashboardAiToolsFactory,\n ExtraDashboardAiToolsParams,\n MosaicDashboardStoreState,\n} from '@sqlrooms/mosaic';\nimport {\n createDeckMapDashboardPanelConfig,\n DECK_MAP_DASHBOARD_PANEL_TYPE,\n type DeckMapDashboardPanelConfig,\n} from './dashboardConfig';\nimport {DECK_TABLE_DATASET_SOURCE_RELATION} from './datasets/tableDatasetSql';\nimport {\n getFirstDatasetSourceTableName,\n hasSqlOnlyDatasetSource,\n} from './datasetSourceUtils';\nimport {quoteDeckMapSqlIdentifier} from './mapConfigUtils';\n\nexport {getFirstDatasetSourceTableName, hasSqlOnlyDatasetSource};\n\nexport const DECK_MAP_AI_INSTRUCTIONS = `\nDeck map tools:\n- create_deck_map_config validates and returns a reusable native Deck JSON map config without requiring a dashboard artifact.\n- create_dashboard_map creates or updates an interactive map panel inside a dashboard from a native Deck JSON map config.\n- Use map tools when the user asks for a map, geospatial/spatial visualization, locations, longitude/latitude data, or geometry columns.\n- CONFIG MODE: Every map config must include a configMode field (\"basic\" or \"custom\") that determines how the map was authored and whether the UI settings panel is available.\n - \"basic\" (default): Use for straightforward requests — single layer, standard color scale, simple geometry binding. Stick ONLY to properties that the UI configurator supports: layer @@type, visibility, color scale (@@function colorScale), point radius (numeric getRadius with radiusUnits), line width (numeric getWidth with widthUnits), geometry/H3/arc column bindings, extrusion with a single elevation column. Do NOT use string accessors (@@= expressions), custom extensions, multiple layers, or advanced deck.gl props in basic mode. The user can fine-tune these maps through the settings panel.\n - \"custom\": Use when the request demands creative, complex, or advanced visualization — multiple layers, data-driven accessors (@@= expressions), custom color arrays, advanced deck.gl props (opacity, transitions, material, highlightColor, etc.), layer extensions, or any props not representable in the UI configurator. The UI settings panel will be disabled for custom configs; users edit via the JSON editor instead.\n - Decision rule: If the map can be fully expressed with a single layer + basic color scale + simple numeric radius/width, use \"basic\". Otherwise use \"custom\".\n- Author maps with config.spec.layers using Deck JSON layer classes in @@type, such as GeoArrowScatterplotLayer, GeoArrowHeatmapLayer, GeoArrowPolygonLayer, GeoArrowPathLayer, GeoArrowTripsLayer, GeoArrowArcLayer, or GeoArrowH3HexagonLayer.\n- LAYER SELECTION: Choose the layer type based on the geometry type in the data.\n IMPORTANT: Only create a layer if the table contains data suitable for that layer type, or if you can transform the data into the required format with transformSql or a standalone sqlQuery. Do NOT create a layer if the data is clearly incompatible (e.g. do not create a path layer from point-only data without aggregation, do not create a polygon layer from point coordinates, do not create an arc layer without origin-destination pairs).\n - Point data (lon/lat coordinates, point geometry): GeoArrowScatterplotLayer (Point layer), GeoArrowHeatmapLayer, GeoArrowColumnLayer. Requires rows with individual point positions — either separate longitude/latitude numeric columns, or a point geometry column. Each row represents one point on the map.\n - Polygon data (building footprints, boundaries, areas, parcels, zones): GeoArrowPolygonLayer or GeoArrowSolidPolygonLayer. Requires a geometry column containing polygon or multipolygon WKB/GeoArrow data. Typically loaded from GeoJSON/Shapefile/GeoParquet or produced by spatial queries. Do NOT use for point data.\n - Line data (roads, routes, paths, rivers): GeoArrowPathLayer. CRITICAL: GeoArrowPathLayer requires LineString geometry, NOT individual point rows. If the table has one row per waypoint (indicated by columns like path_id/route_id + order/sequence + lat/lon), you MUST aggregate them with transformSql: \"SELECT path_id, label, ST_AsWKB(ST_MakeLine(LIST(ST_Point(lon, lat) ORDER BY waypoint_order))) AS geom FROM ${DECK_TABLE_DATASET_SOURCE_RELATION} GROUP BY path_id, label\". Set geometryColumn to \"geom\" and geometryEncodingHint to \"wkb\". If the table already has a geometry/geom column with linestring data, use it directly with tableName. NEVER pass raw waypoint rows to GeoArrowPathLayer — it will fail.\n - Animated trip data (routes with timestamps): GeoArrowTripsLayer. Same geometry requirements as GeoArrowPathLayer (LineString), plus a timestamps column. The transformSql MUST aggregate both the geometry and timestamps: \"SELECT path_id, label, ST_AsWKB(ST_MakeLine(LIST(ST_Point(lon, lat) ORDER BY waypoint_order))) AS geom, LIST(timestamp ORDER BY waypoint_order) AS timestamps FROM ${DECK_TABLE_DATASET_SOURCE_RELATION} GROUP BY path_id, label\". Set geometryColumn to \"geom\", geometryEncodingHint to \"wkb\", and _sqlroomsBinding.timestampColumn to \"timestamps\". The timestamps column must be a list of numbers (seconds) matching the order of waypoints in the linestring. Also set currentTime on the layer to control animation position. Do NOT use unless the data has or can produce both paths and ordered timestamps.\n- CRITICAL geometryColumn rule: The geometryColumn field (in datasets[id].geometryColumn, _sqlroomsBinding.geometryColumn, and fitToData.geometryColumn) MUST match the exact column alias that produces the WKB geometry in the final query output — typically the \"AS geom\" alias in ST_AsWKB(...) AS geom. It must NEVER be set to a GROUP BY key, an ID column, or any other non-geometry column. For example, if the transformSql is \"SELECT path_id, ST_AsWKB(ST_MakeLine(...)) AS geom ... GROUP BY path_id\", geometryColumn must be \"geom\" (the geometry output), NOT \"path_id\" (the grouping key). Setting geometryColumn to a non-geometry column will cause the layer to fail silently.\n - Arc data (origin-destination pairs): GeoArrowArcLayer. Requires two sets of coordinates per row (source and target). The table must have source_lon/source_lat AND target_lon/target_lat columns (or equivalent). The dataset source MUST use transformSql that creates WKB geometry columns from lat/lon, for example: \"SELECT *, ST_AsWKB(ST_Point(source_lon, source_lat)) AS source_geom, ST_AsWKB(ST_Point(target_lon, target_lat)) AS target_geom FROM ${DECK_TABLE_DATASET_SOURCE_RELATION}\". Set sourceGeometryColumn to \"source_geom\" and targetGeometryColumn to \"target_geom\". Set geometryEncodingHint to \"wkb\". To render straight lines instead of arcs, set \"getHeight\": 0 on the layer. Do NOT use for data with only one set of coordinates per row. When the source data has H3 indices instead of lat/lon, convert H3 to coordinates using h3_cell_to_lng(h3_index) and h3_cell_to_lat(h3_index) (the H3 extension is pre-loaded at startup), for example: \"SELECT *, ST_AsWKB(ST_Point(h3_cell_to_lng(source_h3), h3_cell_to_lat(source_h3))) AS source_geom, ST_AsWKB(ST_Point(h3_cell_to_lng(target_h3), h3_cell_to_lat(target_h3))) AS target_geom FROM ${DECK_TABLE_DATASET_SOURCE_RELATION}\". Do NOT use h3_latlng() — it does not exist.\n - H3 hexagon data (h3 index column): GeoArrowH3HexagonLayer. Requires a column containing H3 string indices. Bind to dataset with _sqlroomsBinding.dataset. Set \"getHexagon\": \"@@=h3_column_name\" where h3_column_name is the column containing H3 string indices. Always include \"fitToData\": {\"dataset\": \"datasetId\"} so the map can zoom to the data extent. Do NOT use unless the table has an H3 index column. DuckDB H3 extension functions: h3_cell_to_lat(index), h3_cell_to_lng(index), h3_cell_to_latlng(index). Do NOT use h3_latlng(), h3_to_lat(), or other non-existent function names.\n- CRITICAL: The transformSql and sqlQuery fields must contain ONLY a single SELECT statement. NEVER put INSTALL, LOAD, CREATE, or other DDL/meta-commands in dataset SQL — they will fail because dataset SQL is wrapped in a subquery at runtime. Extensions like h3 and spatial are pre-loaded at startup.\n - GeoJSON files typically contain polygon or multipolygon features (boundaries, buildings, parcels); use GeoArrowPolygonLayer for these. If a GeoJSON file contains point features, use GeoArrowScatterplotLayer (Point layer) instead.\n- RADIUS AND WIDTH: For GeoArrowScatterplotLayer (Point layer) use getRadius with radiusUnits: \"pixels\" (typically 2–6 pixels); large radii cause overdraw and rendering lag, especially with many points. For GeoArrowColumnLayer use the \"radius\" property (NOT getRadius) — it sets column radius in meters; typical values are 20–200 for city-scale data or smaller for dense datasets. Do NOT use getRadius or radiusUnits on column layers. For GeoArrowArcLayer, GeoArrowPathLayer, and GeoArrowTripsLayer use getWidth with widthUnits: \"pixels\" (typically 1–3 pixels).\n- HEATMAP: For GeoArrowHeatmapLayer, do NOT set colorRange manually. The UI provides a scheme selector that generates the correct color array. If you set colorRange to hand-picked RGB arrays, it will be out of sync with the scheme selector shown in the UI. Just omit colorRange entirely and let the default apply — users can change the scheme through the map settings panel.\n- ARC vs LINE: GeoArrowArcLayer renders curved 3D arcs by default. If the user asks for \"lines\" or \"straight connections\" between origin-destination pairs (not arcs), set \"getHeight\": 0 on the layer to render flat straight lines. Use arcs for flight routes or connections where the curve adds clarity; use flat lines for direct relationships, edges, or when the user explicitly requests lines.\n- ELEVATION: For extruded layers, getElevation with @@function \"scale\" passes the raw field value as meters. Use elevationScale on the layer to multiply values to a useful visual height. For example, if the field is \"floors\" (1-10), set elevationScale to 3 (meters per floor). Do NOT use negative values for elevation. Avoid using diverging scales for elevation. IMPORTANT: Keep elevation moderate — if extruded polygons or H3 hexagons are too tall, users can't see the tops when zoomed in. Prefer elevationScale values that produce heights of a few hundred meters at most for city-scale data. A good rule of thumb: the maximum elevation (field max × elevationScale) should not exceed ~500m for typical zoom levels.\n- Bind layers to datasets with _sqlroomsBinding.dataset and put tableName, tableName+transformSql, or sqlQuery sources in config.datasets.\n- Use source.tableName for direct table-backed datasets. Use source.tableName plus source.transformSql when the map needs generated geometry or aggregation but should still follow the dashboard selected table. transformSql must read from ${DECK_TABLE_DATASET_SOURCE_RELATION}, not from the authored table name.\n- Use source.sqlQuery only for a standalone literal query that should remain pinned to the authored SQL. Dashboard selected table replacement applies only to structured tableName sources, not literal sqlQuery sources.\n- IMPORTANT: Always pass tableName in the create_dashboard_map tool params (the top-level tableName field). Use the table currently selected in the dashboard (dashboard.selectedTable from list_dashboard_panels). At runtime, the dashboard's selected table overrides structured source.tableName values — this param seeds or changes that selection.\n- IMPORTANT: If you are creating a structured table-backed map layer for a table that is NOT the currently selected dashboard table, you MUST switch the dashboard's selected table to that dataset BEFORE or WHEN calling create_dashboard_map (pass the correct tableName). Structured table-backed map panels resolve data from the dashboard's active table — if you don't switch it, the layer will query the wrong table and fail.\n- IMPORTANT: When referencing tables in tableName or sqlQuery, use ONLY the bare table name (e.g. \"my_table\") or schema-qualified name (e.g. \"main.my_table\"). NEVER include the database/catalog prefix (e.g. do NOT use \"sqlrooms-cli.main.my_table\") — the catalog does not exist in the query execution context.\n- IMPORTANT: For point data with longitude/latitude columns that should follow dashboard table switching, use source.tableName plus source.transformSql to create a geometry column, for example: \"SELECT *, ST_AsWKB(ST_Point(\\\\\"Longitude\\\\\", \\\\\"Latitude\\\\\")) AS \\\\\"__sqlrooms_geom\\\\\" FROM ${DECK_TABLE_DATASET_SOURCE_RELATION} WHERE \\\\\"Longitude\\\\\" IS NOT NULL AND \\\\\"Latitude\\\\\" IS NOT NULL\". Set geometryColumn to the same name used in the AS clause (e.g. \"__sqlrooms_geom\") and geometryEncodingHint to \"wkb\".\n- IMPORTANT: When providing fitToData, it MUST be a flat object (NOT nested by dataset ID). Include either longitudeColumn+latitudeColumn (for point data with separate coordinate columns) OR geometryColumn (for data with a WKB geometry column like GeoJSON). For H3 hexagon layers, just specify the dataset: \"fitToData\": {\"dataset\": \"datasetId\"} — the H3 column is auto-detected from the layer binding. For GeoJSON/spatial files with a \"geom\" column, use: \"fitToData\": {\"dataset\": \"datasetId\", \"geometryColumn\": \"geom\"}. For point data use: \"fitToData\": {\"dataset\": \"datasetId\", \"longitudeColumn\": \"lon\", \"latitudeColumn\": \"lat\"}. NEVER nest fitToData as {\"datasetId\": {...}} — always use a flat object with \"dataset\" as a string field.\n- IMPORTANT: For GeoJSON or spatial files that already have a native geometry column (e.g. \"geometry\", \"geom\"), use the table directly with source.tableName (no sqlQuery needed), set the dataset's geometryColumn to \"geom\", set geometryEncodingHint to \"wkb\", and use fitToData with geometryColumn: {\"dataset\": \"datasetId\", \"geometryColumn\": \"geom\"}.\n- IMPORTANT: When a GeoJSON file (.geojson) is loaded as a table, DuckDB uses ST_Read to produce a table with a WKB \"geom\" column and all feature properties as columns. Use source.tableName, set geometryColumn to \"geom\" and geometryEncodingHint to \"wkb\". Use \"fitToData\": {\"dataset\": \"datasetId\", \"geometryColumn\": \"geom\"} to zoom to the data extent.\n- For data-driven color, use native Deck JSON accessors with {\"@@function\":\"colorScale\", \"field\":\"...\", \"type\":\"sequential\"|\"diverging\"|\"quantize\"|\"quantile\"|\"categorical\", \"scheme\":\"...\", \"domain\":\"auto\"} on color properties such as getFillColor, getLineColor, getColor, getSourceColor, or getTargetColor. Valid schemes: for \"categorical\" type use one of Accent, Dark2, Paired, Pastel1, Pastel2, Set1, Set2, Set3, Tableau10, Observable10, Category10. For \"sequential\" use Viridis, Inferno, Magma, Plasma, Turbo, Blues, Greens, Oranges, Reds, Purples, etc. For \"diverging\" use RdBu, Spectral, RdYlGn, BrBG, PiYG, etc. IMPORTANT: The colorScale \"field\" must reference a column that exists in the FINAL query output (after any GROUP BY aggregation). Do not reference columns that are lost during aggregation.\n- IMPORTANT: Enabling a color scale means adding a {\"@@function\":\"colorScale\", ...} accessor to a compatible layer color property. The top-level showLegends field only controls whether already-defined color scale legends are visible; showLegends by itself does NOT create or enable data-driven color.\n- Map panels default to a 100000-row runtime data limit; use config.dataPolicy.maxRows only when the map genuinely needs a panel-specific limit.\n- Create maps with a SINGLE layer unless the user explicitly asks for multiple layers. If you think multiple layers would better serve the user's request, ask the user for confirmation before adding them.\n- IMPORTANT: Browsers limit the number of active WebGL contexts (typically 8–16 per page). Each map panel uses one context. Do NOT create more than 4–5 map panels in a single dashboard — exceeding the limit causes older maps to lose their rendering context and show errors. If the user asks for many datasets, prefer combining compatible layers into fewer maps rather than creating one map per dataset.\n- After calling create_dashboard_map, call list_dashboard_panels before your final response and check the map panel issue. If it has a render-error, repair the map config in place instead of saying the map is complete.\n- BASEMAPS: Omit mapStyle unless the user explicitly requests a custom basemap. Maps inherit the host application's theme-aware basemap when available, with a token-free fallback. Do NOT use Mapbox styles (mapbox://styles/...) because a Mapbox access token may not be available. For an explicitly requested custom basemap, use a token-free MapLibre-compatible style URL.\n`;\n\nfunction createDeckMapDashboardExtraTools(\n extraTools?: ExtraDashboardAiToolsFactory,\n) {\n return (params: ExtraDashboardAiToolsParams) => ({\n ...createDeckMapDashboardAiTools(params),\n ...(extraTools?.(params) ?? {}),\n });\n}\n\n/**\n * Returns AI instructions for dashboards with Deck.gl map support.\n * Provides guidance on when and how to use map visualizations.\n *\n * @returns Instructions string for AI agents\n */\nexport function getDashboardWithDeckMapAiInstructions() {\n return `${DECK_MAP_AI_INSTRUCTIONS.trim()}`;\n}\n\n/**\n * Creates dashboard AI tools with built-in Deck.gl map support.\n * Extends standard dashboard tools with map visualization capabilities.\n *\n * @param options - Dashboard AI tools configuration options\n * @returns Record mapping tool names to tool instances, including map tools\n */\nexport function createDashboardWithDeckMapAiTools(\n options: CreateDashboardAiToolsOptions,\n): Record<string, Tool> {\n return createMosaicDashboardAiTools({\n ...options,\n extraTools: createDeckMapDashboardExtraTools(options.extraTools),\n });\n}\n\n/**\n * Creates a dashboard agent tool with built-in Deck.gl map support.\n * Extends the standard dashboard agent with map creation capabilities.\n *\n * @template TState - Store state type extending MosaicDashboardStoreState\n * @param options - Dashboard agent configuration options\n * @returns Dashboard agent tool with map support\n */\nexport function createDashboardAgentToolWithDeckMaps<\n TState extends MosaicDashboardStoreState,\n>(options: CreateDashboardAgentToolOptions<TState>): Tool {\n return createDashboardAgentTool({\n ...options,\n additionalInstructions: [\n options.additionalInstructions,\n DECK_MAP_AI_INSTRUCTIONS.trim(),\n ]\n .filter(Boolean)\n .join('\\n\\n'),\n extraTools: createDeckMapDashboardExtraTools(options.extraTools),\n });\n}\n\nconst DeckMapLayerBindingConfig = z.looseObject({\n dataset: z.string().optional(),\n geometryColumn: z.string().optional(),\n geometryEncodingHint: z.enum(['geoarrow', 'wkb', 'wkt']).optional(),\n sourceGeometryColumn: z.string().optional(),\n targetGeometryColumn: z.string().optional(),\n timestampColumn: z.string().optional(),\n hexagonColumn: z.string().optional(),\n});\n\nconst DeckMapLayerSpec = z.looseObject({\n '@@type': z.string().optional(),\n id: z.string().optional(),\n _sqlroomsBinding: DeckMapLayerBindingConfig.optional(),\n});\n\nconst DeckMapSpec = z.looseObject({\n initialViewState: z.record(z.string(), z.unknown()).optional(),\n viewState: z.record(z.string(), z.unknown()).optional(),\n controller: z.unknown().optional(),\n layers: z.array(DeckMapLayerSpec).optional(),\n});\n\nconst DeckMapDatasetSource = z.looseObject({\n tableName: z.string().optional(),\n transformSql: z.string().optional(),\n sqlQuery: z.string().optional(),\n});\n\nconst DeckMapDatasetConfig = z.looseObject({\n source: DeckMapDatasetSource.optional(),\n geometryColumn: z.string().optional(),\n geometryEncodingHint: z.enum(['geoarrow', 'wkb', 'wkt']).optional(),\n});\n\nconst DeckMapDataPolicyConfig = z.looseObject({\n disabled: z.boolean().optional(),\n maxRows: z.number().int().min(1).optional(),\n reason: z.string().optional(),\n});\n\nexport const DeckMapDashboardConfigParameter = z.looseObject({\n spec: DeckMapSpec.describe(\n 'Deck JSON map spec as an object. Use spec.layers[].@@type for layer classes such as GeoArrowScatterplotLayer (Point layer), GeoArrowHeatmapLayer, GeoArrowPolygonLayer, GeoArrowPathLayer, or GeoArrowArcLayer.',\n ),\n datasets: z\n .record(z.string(), DeckMapDatasetConfig)\n .describe(\n 'Datasets keyed by dataset id. Layers bind to these ids through _sqlroomsBinding.dataset. Each dataset source may use tableName, tableName+transformSql, or sqlQuery.',\n ),\n configMode: z\n .enum(['basic', 'custom'])\n .optional()\n .describe(\n 'Config authoring mode. Use \"basic\" (default) for straightforward single-layer maps that the user can tweak via the UI settings panel. Use \"custom\" for complex, multi-layer, or creative maps that use advanced deck.gl props beyond what the UI configurator supports — the settings panel will be disabled for custom configs.',\n ),\n mapStyle: z.string().optional(),\n mapProps: z.record(z.string(), z.unknown()).optional(),\n showLegends: z\n .boolean()\n .optional()\n .describe(\n 'Whether to show color scale legends on the map. Defaults to true; omit or set true unless the user explicitly asks to hide legends.',\n ),\n interaction: z.record(z.string(), z.unknown()).optional(),\n fitToData: z\n .object({\n dataset: z.string().describe('Dataset id to compute bounds from.'),\n longitudeColumn: z\n .string()\n .optional()\n .describe('Longitude column name for point data.'),\n latitudeColumn: z\n .string()\n .optional()\n .describe('Latitude column name for point data.'),\n geometryColumn: z\n .string()\n .optional()\n .describe('WKB geometry column name for computing bounds.'),\n h3Column: z\n .string()\n .optional()\n .describe('H3 hex index column for computing bounds.'),\n padding: z.number().optional(),\n maxZoom: z.number().optional(),\n })\n .optional()\n .describe(\n 'Fit map view to data bounds. Provide dataset plus either geometryColumn (for WKB geometry) or longitudeColumn+latitudeColumn (for separate coordinate columns). Example: {\"dataset\": \"myDataset\", \"geometryColumn\": \"geom\"}',\n ),\n dataPolicy: DeckMapDataPolicyConfig.optional().describe(\n 'Optional per-map runtime data policy. Maps default to 100000 rows; set maxRows for a panel-specific override or disabled=true to bypass row-count validation.',\n ),\n settingsOpen: z.boolean().optional(),\n});\n\nexport type DeckMapDashboardConfigToolConfig = z.infer<\n typeof DeckMapDashboardConfigParameter\n>;\n\nexport const DeckMapConfigToolParameters = z.object({\n title: z.string().optional().default('Map').describe('Map title.'),\n config: DeckMapDashboardConfigParameter.describe(\n 'Native Deck JSON dashboard map config. This is the canonical map representation.',\n ),\n reasoning: z\n .string()\n .describe('Brief rationale for creating the map config.'),\n});\n\nexport type DeckMapConfigToolParams = z.infer<\n typeof DeckMapConfigToolParameters\n>;\n\nexport const DeckMapDashboardToolParameters =\n DeckMapConfigToolParameters.extend({\n tableName: z\n .string()\n .optional()\n .describe(\n 'Optional table name used only to select/resolve the target dashboard table. Data sources still come from config.datasets.',\n ),\n panelId: z\n .string()\n .optional()\n .describe(\n 'Optional panel ID. If provided, updates the existing map panel instead of creating a new one.',\n ),\n reasoning: z\n .string()\n .describe('Brief rationale for creating the map panel.'),\n });\n\nexport type DeckMapDashboardToolParams = z.infer<\n typeof DeckMapDashboardToolParameters\n>;\n\nconst DEFAULT_AI_GEOMETRY_COLUMN = '__sqlrooms_geom';\n\n/**\n * Normalizes an AI-generated map config to ensure dataset sources produce\n * the expected geometry column when fitToData specifies coordinate columns\n * but the dataset only uses a tableName without a transformSql.\n */\nfunction normalizeAiMapConfig(\n config: DeckMapDashboardConfigToolConfig,\n): DeckMapDashboardConfigToolConfig {\n const datasets = config.datasets;\n let fitToData = config.fitToData as\n | Record<string, unknown>\n | null\n | undefined;\n\n // Fix common AI mistake: fitToData wrapped as { datasetId: { dataset, ... } }\n // instead of the expected flat { dataset, longitudeColumn, ... }.\n if (fitToData && !fitToData.dataset && typeof fitToData === 'object') {\n const keys = Object.keys(fitToData);\n if (keys.length === 1) {\n const nested = fitToData[keys[0]!] as Record<string, unknown> | undefined;\n if (nested && typeof nested === 'object' && nested.dataset) {\n fitToData = nested;\n config = {...config, fitToData: fitToData as any};\n }\n }\n }\n\n if (!datasets || typeof datasets !== 'object' || !fitToData) {\n return config;\n }\n\n const lonCol = fitToData.longitudeColumn as string | undefined;\n const latCol = fitToData.latitudeColumn as string | undefined;\n if (!lonCol || !latCol) {\n return config;\n }\n\n const targetDatasetId = fitToData.dataset as string | undefined;\n if (!targetDatasetId) {\n return config;\n }\n\n const targetDataset = datasets[targetDatasetId] as\n | Record<string, unknown>\n | undefined;\n if (!targetDataset) {\n return config;\n }\n\n const source = targetDataset.source as\n | {tableName?: string; transformSql?: string; sqlQuery?: string}\n | undefined;\n\n // Always normalize when using tableName without transformSql and fitToData\n // provides coordinate columns — the geometry must be computed from them.\n if (!source?.tableName || source.sqlQuery || source.transformSql) {\n return config;\n }\n\n const geometryColumn =\n (targetDataset.geometryColumn as string | undefined) ||\n DEFAULT_AI_GEOMETRY_COLUMN;\n\n const quotedLon = quoteDeckMapSqlIdentifier(lonCol);\n const quotedLat = quoteDeckMapSqlIdentifier(latCol);\n const quotedGeom = quoteDeckMapSqlIdentifier(geometryColumn);\n const transformSql = [\n `SELECT *, ST_AsWKB(ST_Point(${quotedLon}, ${quotedLat})) AS ${quotedGeom}`,\n `FROM ${DECK_TABLE_DATASET_SOURCE_RELATION}`,\n `WHERE ${quotedLon} IS NOT NULL AND ${quotedLat} IS NOT NULL`,\n ].join(' ');\n\n return {\n ...config,\n datasets: {\n ...datasets,\n [targetDatasetId]: {\n ...targetDataset,\n source: {tableName: source.tableName, transformSql},\n geometryColumn,\n geometryEncodingHint: 'wkb',\n },\n },\n };\n}\n\nfunction cloneConfig(\n config: DeckMapDashboardConfigToolConfig,\n): DeckMapDashboardPanelConfig {\n const normalized = normalizeAiMapConfig(config);\n return JSON.parse(JSON.stringify(normalized)) as DeckMapDashboardPanelConfig;\n}\n\n/**\n * Creates a dashboard-compatible Deck map panel from the native map config\n * used by AI tools and embeddable map surfaces.\n */\nexport function createDeckMapPanelFromNativeConfig(\n params: Pick<DeckMapConfigToolParams, 'title' | 'config'>,\n) {\n return createDeckMapDashboardPanelConfig({\n title: params.title || 'Map',\n ...cloneConfig(params.config),\n });\n}\n\nexport function createDeckMapConfigTool(): Tool {\n return tool({\n description: `Deck map config: validates and returns a reusable native Deck JSON map configuration without requiring a dashboard artifact.\n\nUse when: a chat, agent, or artifact outside a dashboard needs a geospatial map config. Author the map using native Deck JSON: put layer classes in spec.layers[].@@type, bind layers to datasets through _sqlroomsBinding.dataset, and put tableName, tableName+transformSql, or sqlQuery sources in config.datasets. For data-driven colors, use color accessors such as getFillColor, getLineColor, getColor, getSourceColor, or getTargetColor with {\"@@function\":\"colorScale\", \"field\":\"...\", \"type\":\"...\", \"scheme\":\"...\", \"domain\":\"auto\"}. For categorical fields use scheme from: Tableau10, Set2, Category10, etc. For numeric fields use sequential schemes like Viridis.`,\n inputSchema: DeckMapConfigToolParameters,\n execute: async (params) => {\n try {\n const panel = createDeckMapPanelFromNativeConfig(params);\n return {\n llmResult: {\n success: true,\n details: `Created deck map config \"${panel.title}\".`,\n data: {\n kind: 'deck-map-config',\n title: panel.title,\n type: DECK_MAP_DASHBOARD_PANEL_TYPE,\n config: panel.config,\n },\n },\n };\n } catch (error) {\n return {\n llmResult: {\n success: false,\n errorMessage:\n error instanceof Error ? error.message : String(error),\n },\n };\n }\n },\n });\n}\n\n/**\n * Creates AI tools for Deck.gl map configuration.\n * Returns tools for creating and configuring Deck.gl map panels.\n *\n * @returns Record mapping tool names to map configuration tools\n */\nexport function createDeckMapAiTools(): Record<string, Tool> {\n return {\n create_deck_map_config: createDeckMapConfigTool(),\n };\n}\n\n/**\n * Parameters for creating a Deck.gl map dashboard tool.\n * Provides adapters for dashboard and database operations.\n */\nexport type CreateDeckMapDashboardToolParams = {\n /** Dashboard adapter for adding and updating map panels */\n dashboardAdapter: DashboardAiAdapter;\n /** Database adapter for table validation */\n databaseAdapter: DatabaseAiAdapter;\n};\n\n/**\n * Creates a tool for adding Deck.gl map panels to dashboards.\n * Supports creating new map panels or updating existing ones with native Deck JSON configs.\n *\n * @param params - Parameters containing dashboard and database adapters\n * @returns Tool instance for creating/updating Deck.gl map panels\n */\nexport function createDeckMapDashboardTool({\n dashboardAdapter,\n databaseAdapter,\n}: CreateDeckMapDashboardToolParams): Tool {\n return tool({\n description: `Deck map panel: creates or updates an interactive geospatial map panel in a Mosaic dashboard from a native Deck JSON config.\n\nUse when: the user asks for a map in a dashboard. Author the map using native Deck JSON: choose layer classes with spec.layers[].@@type, bind layers to datasets through _sqlroomsBinding.dataset, and put tableName, tableName+transformSql, or sqlQuery sources in config.datasets. For data-driven colors, use color accessors such as getFillColor, getLineColor, getColor, getSourceColor, or getTargetColor with {\"@@function\":\"colorScale\", \"field\":\"...\", \"type\":\"...\", \"scheme\":\"...\", \"domain\":\"auto\"}. For categorical fields use scheme from: Tableau10, Set2, Category10, etc. For numeric fields use sequential schemes like Viridis.`,\n inputSchema: DeckMapDashboardToolParameters,\n execute: async (params) => {\n try {\n const tableName =\n params.tableName ?? getFirstDatasetSourceTableName(params.config);\n\n if (tableName) {\n ensureTable(databaseAdapter, tableName);\n await dashboardAdapter.setSelectedTable(tableName);\n }\n\n const panel = createDeckMapPanelFromNativeConfig(params);\n\n if (params.panelId) {\n ensurePanel(\n dashboardAdapter,\n params.panelId,\n DECK_MAP_DASHBOARD_PANEL_TYPE,\n );\n\n await dashboardAdapter.updatePanel(params.panelId, {\n title: panel.title,\n config: panel.config,\n });\n\n return {\n llmResult: {\n success: true,\n details: `Updated map panel \"${panel.title}\".`,\n data: {\n panelId: params.panelId,\n title: panel.title,\n type: DECK_MAP_DASHBOARD_PANEL_TYPE,\n config: panel.config,\n },\n },\n };\n }\n\n const panelId = await dashboardAdapter.addPanel(panel);\n\n return {\n llmResult: {\n success: true,\n details: `Created map panel \"${panel.title}\".`,\n data: {\n panelId,\n title: panel.title,\n type: DECK_MAP_DASHBOARD_PANEL_TYPE,\n config: panel.config,\n },\n },\n };\n } catch (error) {\n return {\n llmResult: {\n success: false,\n errorMessage:\n error instanceof Error ? error.message : String(error),\n },\n };\n }\n },\n });\n}\n\nexport function createDeckMapDashboardAiTools(\n params: CreateDeckMapDashboardToolParams,\n): Record<string, Tool> {\n return {\n [MAP_TOOL_KEY]: createDeckMapDashboardTool(params),\n };\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import { type DuckDbSliceState } from '@sqlrooms/duckdb';
2
+ import { type ReactNode } from 'react';
3
+ import { type DeckMapDataAdapter } from './DeckMapSurface';
4
+ import { type DeckMapsSliceState } from './DeckMapsSlice';
5
+ type DeckMapResourceState = DeckMapsSliceState & DuckDbSliceState;
6
+ /** Ensures a first-class map resource, optionally seeded from geospatial data. */
7
+ export declare function ensureDeckMapResourceState(state: DeckMapResourceState, mapId: string, title?: string): void;
8
+ export type DeckMapBlockRendererProps = {
9
+ mapId: string;
10
+ title?: string;
11
+ caption?: string;
12
+ onCaptionChange?: (caption: string | undefined) => void;
13
+ selected?: boolean;
14
+ readOnly?: boolean;
15
+ headerActions?: ReactNode;
16
+ dataAdapter?: DeckMapDataAdapter;
17
+ };
18
+ /** Mosaic-free worksheet map renderer backed only by `deckMaps`. */
19
+ export declare function DeckMapBlockRenderer({ mapId, title, caption, onCaptionChange, selected, readOnly, headerActions, dataAdapter, }: DeckMapBlockRendererProps): import("react/jsx-runtime").JSX.Element;
20
+ export {};
21
+ //# sourceMappingURL=block.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block.d.ts","sourceRoot":"","sources":["../src/block.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAC,KAAK,kBAAkB,EAAuB,MAAM,iBAAiB,CAAC;AAQ9E,KAAK,oBAAoB,GAAG,kBAAkB,GAAG,gBAAgB,CAAC;AAmDlE,kFAAkF;AAClF,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,oBAAoB,EAC3B,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,QAoBf;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACxD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC,CAAC;AAEF,oEAAoE;AACpE,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,KAAK,EACL,OAAO,EACP,eAAe,EACf,QAAQ,EACR,QAAQ,EACR,aAAa,EACb,WAAsC,GACvC,EAAE,yBAAyB,2CA6K3B"}