@databricks/appkit-ui 0.20.2 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/CLAUDE.md +5 -13
  2. package/NOTICE.md +3 -6
  3. package/dist/cli/commands/plugin/add-resource/add-resource.js.map +1 -1
  4. package/dist/js/arrow/lazy-arrow.d.ts +8 -0
  5. package/dist/js/arrow/lazy-arrow.d.ts.map +1 -1
  6. package/dist/js/arrow/lazy-arrow.js +8 -0
  7. package/dist/js/arrow/lazy-arrow.js.map +1 -1
  8. package/dist/react/charts/area/index.d.ts +0 -1
  9. package/dist/react/charts/area/index.d.ts.map +1 -1
  10. package/dist/react/charts/area/index.js.map +1 -1
  11. package/dist/react/charts/bar/index.d.ts +0 -1
  12. package/dist/react/charts/bar/index.d.ts.map +1 -1
  13. package/dist/react/charts/bar/index.js.map +1 -1
  14. package/dist/react/charts/constants.d.ts +1 -3
  15. package/dist/react/charts/constants.d.ts.map +1 -1
  16. package/dist/react/charts/constants.js +1 -3
  17. package/dist/react/charts/constants.js.map +1 -1
  18. package/dist/react/charts/heatmap/index.d.ts +0 -1
  19. package/dist/react/charts/heatmap/index.d.ts.map +1 -1
  20. package/dist/react/charts/heatmap/index.js.map +1 -1
  21. package/dist/react/charts/index.d.ts +1 -1
  22. package/dist/react/charts/index.js +1 -1
  23. package/dist/react/charts/line/index.d.ts +0 -1
  24. package/dist/react/charts/line/index.d.ts.map +1 -1
  25. package/dist/react/charts/line/index.js.map +1 -1
  26. package/dist/react/charts/options.d.ts.map +1 -1
  27. package/dist/react/charts/options.js +3 -1
  28. package/dist/react/charts/options.js.map +1 -1
  29. package/dist/react/charts/pie/index.d.ts +0 -1
  30. package/dist/react/charts/pie/index.d.ts.map +1 -1
  31. package/dist/react/charts/pie/index.js.map +1 -1
  32. package/dist/react/charts/radar/index.d.ts +0 -1
  33. package/dist/react/charts/radar/index.d.ts.map +1 -1
  34. package/dist/react/charts/radar/index.js.map +1 -1
  35. package/dist/react/charts/scatter/index.d.ts +0 -1
  36. package/dist/react/charts/scatter/index.d.ts.map +1 -1
  37. package/dist/react/charts/scatter/index.js.map +1 -1
  38. package/dist/react/genie/genie-chart-inference.d.ts +6 -1
  39. package/dist/react/genie/genie-chart-inference.d.ts.map +1 -1
  40. package/dist/react/genie/genie-chart-inference.js +60 -6
  41. package/dist/react/genie/genie-chart-inference.js.map +1 -1
  42. package/dist/react/genie/genie-chat-input.d.ts.map +1 -1
  43. package/dist/react/genie/genie-chat-input.js.map +1 -1
  44. package/dist/react/genie/genie-chat-message-list.d.ts.map +1 -1
  45. package/dist/react/genie/genie-chat-message-list.js.map +1 -1
  46. package/dist/react/genie/genie-chat-message.d.ts.map +1 -1
  47. package/dist/react/genie/genie-chat-message.js.map +1 -1
  48. package/dist/react/genie/genie-query-visualization.d.ts.map +1 -1
  49. package/dist/react/genie/genie-query-visualization.js +63 -13
  50. package/dist/react/genie/genie-query-visualization.js.map +1 -1
  51. package/dist/react/genie/index.d.ts +1 -1
  52. package/dist/react/genie/index.js +1 -1
  53. package/dist/react/index.d.ts +3 -3
  54. package/dist/react/index.js +5 -5
  55. package/dist/react/table/data-table.js +1 -1
  56. package/dist/react/ui/index.js +1 -1
  57. package/dist/schemas/plugin-manifest.generated.d.ts +178 -0
  58. package/dist/schemas/plugin-manifest.generated.d.ts.map +1 -0
  59. package/dist/schemas/plugin-manifest.schema.json +2 -0
  60. package/docs/api/appkit/Interface.CacheConfig.md +1 -1
  61. package/docs/api/appkit/Interface.PluginManifest.md +104 -6
  62. package/docs/api/appkit/Interface.ResourceEntry.md +5 -7
  63. package/docs/api/appkit/Interface.ResourceFieldEntry.md +2 -0
  64. package/docs/api/appkit/Interface.ResourceRequirement.md +63 -7
  65. package/docs/api/appkit/Interface.StreamExecutionSettings.md +1 -1
  66. package/docs/api/appkit/TypeAlias.PluginData.md +2 -0
  67. package/docs/api/appkit/TypeAlias.ToPlugin.md +2 -0
  68. package/docs/api/appkit.md +6 -6
  69. package/llms.txt +5 -13
  70. package/package.json +2 -5
  71. package/docs/api/appkit-ui/data/AreaChart.md +0 -79
  72. package/docs/api/appkit-ui/data/BarChart.md +0 -74
  73. package/docs/api/appkit-ui/data/DonutChart.md +0 -72
  74. package/docs/api/appkit-ui/data/HeatmapChart.md +0 -91
  75. package/docs/api/appkit-ui/data/LineChart.md +0 -77
  76. package/docs/api/appkit-ui/data/PieChart.md +0 -72
  77. package/docs/api/appkit-ui/data/RadarChart.md +0 -74
  78. package/docs/api/appkit-ui/data/ScatterChart.md +0 -76
@@ -5,6 +5,18 @@ const INFERENCE_CONFIG = {
5
5
  barMaxCategories: 100,
6
6
  groupedBarMaxCategories: 50
7
7
  };
8
+ function classifyColumns(rows, columns) {
9
+ if (rows.length < INFERENCE_CONFIG.minRows || columns.length < 2) return null;
10
+ const dateCols = columns.filter((c) => c.category === "date");
11
+ const numericCols = columns.filter((c) => c.category === "numeric");
12
+ const stringCols = columns.filter((c) => c.category === "string");
13
+ if (numericCols.length === 0) return null;
14
+ return {
15
+ dateCols,
16
+ numericCols,
17
+ stringCols
18
+ };
19
+ }
8
20
  function countUnique(rows, key) {
9
21
  const seen = /* @__PURE__ */ new Set();
10
22
  for (const row of rows) seen.add(row[key]);
@@ -19,11 +31,9 @@ function hasNegativeValues(rows, key) {
19
31
  * Returns `null` when the data is not suitable for charting.
20
32
  */
21
33
  function inferChartType(rows, columns) {
22
- if (rows.length < INFERENCE_CONFIG.minRows || columns.length < 2) return null;
23
- const dateCols = columns.filter((c) => c.category === "date");
24
- const numericCols = columns.filter((c) => c.category === "numeric");
25
- const stringCols = columns.filter((c) => c.category === "string");
26
- if (numericCols.length === 0) return null;
34
+ const classified = classifyColumns(rows, columns);
35
+ if (!classified) return null;
36
+ const { dateCols, numericCols, stringCols } = classified;
27
37
  if (dateCols.length > 0 && numericCols.length >= 1) return {
28
38
  chartType: "line",
29
39
  xKey: dateCols[0].name,
@@ -69,7 +79,51 @@ function inferChartType(rows, columns) {
69
79
  };
70
80
  return null;
71
81
  }
82
+ /**
83
+ * Return the chart types that are compatible with the given data shape.
84
+ * Used to populate the chart-type selector dropdown.
85
+ */
86
+ function getCompatibleChartTypes(rows, columns) {
87
+ const classified = classifyColumns(rows, columns);
88
+ if (!classified) return [];
89
+ const { dateCols, numericCols, stringCols } = classified;
90
+ if (dateCols.length > 0 && numericCols.length >= 1) return [
91
+ "line",
92
+ "bar",
93
+ "area"
94
+ ];
95
+ if (stringCols.length > 0 && numericCols.length >= 1) {
96
+ const xKey = stringCols[0].name;
97
+ const uniqueCategories = countUnique(rows, xKey);
98
+ if (numericCols.length === 1) {
99
+ const yKey = numericCols[0].name;
100
+ if (uniqueCategories <= INFERENCE_CONFIG.pieMaxCategories && !hasNegativeValues(rows, yKey)) return [
101
+ "pie",
102
+ "donut",
103
+ "bar",
104
+ "line",
105
+ "area"
106
+ ];
107
+ return [
108
+ "bar",
109
+ "line",
110
+ "area"
111
+ ];
112
+ }
113
+ return [
114
+ "bar",
115
+ "line",
116
+ "area"
117
+ ];
118
+ }
119
+ if (numericCols.length >= 2 && stringCols.length === 0 && dateCols.length === 0) return [
120
+ "scatter",
121
+ "line",
122
+ "area"
123
+ ];
124
+ return [];
125
+ }
72
126
 
73
127
  //#endregion
74
- export { inferChartType };
128
+ export { getCompatibleChartTypes, inferChartType };
75
129
  //# sourceMappingURL=genie-chart-inference.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"genie-chart-inference.js","names":[],"sources":["../../../src/react/genie/genie-chart-inference.ts"],"sourcesContent":["/**\n * ┌─────────────────────────────────────────────────────────────────────┐\n * │ CHART INFERENCE RULES │\n * │ │\n * │ These rules determine what chart type is shown for Genie query │\n * │ results. Modify thresholds and chart type mappings here. │\n * │ │\n * │ Column types are classified from SQL type_name: │\n * │ DATE: DATE, TIMESTAMP, TIMESTAMP_NTZ │\n * │ NUMERIC: DECIMAL, INT, DOUBLE, FLOAT, LONG, etc. │\n * │ STRING: STRING, VARCHAR, CHAR │\n * │ │\n * │ Rules (applied in priority order): │\n * │ │\n * │ SKIP (return null): │\n * │ - < 2 rows │\n * │ - < 2 columns │\n * │ - No numeric columns │\n * │ │\n * │ MATCH: │\n * │ 1. DATE + numeric(s) → line (timeseries) │\n * │ 2. STRING + 1 numeric, ≤7 cats → pie │\n * │ 3. STRING + 1 numeric, ≤100 cats → bar │\n * │ 4. STRING + 1 numeric, >100 cats → line │\n * │ 5. STRING + N numerics, ≤50 cats → bar (grouped) │\n * │ 6. STRING + N numerics, >50 cats → line (multi-series) │\n * │ 7. 2+ numerics only → scatter │\n * │ 8. Otherwise → null (skip) │\n * │ │\n * │ KNOWN LIMITATIONS: │\n * │ - First-column heuristic: picks first string col as category │\n * │ - No semantic understanding (can't tell ID from meaningful val) │\n * └─────────────────────────────────────────────────────────────────────┘\n */\n\nimport type { ChartType } from \"../charts/types\";\nimport type { GenieColumnMeta } from \"./genie-query-transform\";\n\n// ---------------------------------------------------------------------------\n// Configuration — edit thresholds here\n// ---------------------------------------------------------------------------\n\nconst INFERENCE_CONFIG = {\n /** Min rows required to show any chart */\n minRows: 2,\n /** Max unique categories for pie chart */\n pieMaxCategories: 7,\n /** Max unique categories for bar chart (single series) */\n barMaxCategories: 100,\n /** Max unique categories for grouped bar chart (multi series) */\n groupedBarMaxCategories: 50,\n} as const;\n\nexport interface ChartInference {\n chartType: ChartType;\n xKey: string;\n yKey: string | string[];\n}\n\nfunction countUnique(rows: Record<string, unknown>[], key: string): number {\n const seen = new Set<unknown>();\n for (const row of rows) {\n seen.add(row[key]);\n }\n return seen.size;\n}\n\nfunction hasNegativeValues(\n rows: Record<string, unknown>[],\n key: string,\n): boolean {\n for (const row of rows) {\n if (Number(row[key]) < 0) return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Main inference function\n// ---------------------------------------------------------------------------\n\n/**\n * Infer the best chart type for the given Genie query result.\n * Returns `null` when the data is not suitable for charting.\n */\nexport function inferChartType(\n rows: Record<string, unknown>[],\n columns: GenieColumnMeta[],\n): ChartInference | null {\n // Guard: need at least minRows and 2 columns\n if (rows.length < INFERENCE_CONFIG.minRows || columns.length < 2) {\n return null;\n }\n\n const dateCols = columns.filter((c) => c.category === \"date\");\n const numericCols = columns.filter((c) => c.category === \"numeric\");\n const stringCols = columns.filter((c) => c.category === \"string\");\n\n // Guard: must have at least one numeric column\n if (numericCols.length === 0) return null;\n\n // Rule 1: DATE + numeric(s) → line (timeseries)\n if (dateCols.length > 0 && numericCols.length >= 1) {\n return {\n chartType: \"line\",\n xKey: dateCols[0].name,\n yKey:\n numericCols.length === 1\n ? numericCols[0].name\n : numericCols.map((c) => c.name),\n };\n }\n\n // Rules 2–6: STRING + numeric(s)\n if (stringCols.length > 0 && numericCols.length >= 1) {\n const xKey = stringCols[0].name;\n const uniqueCategories = countUnique(rows, xKey);\n\n if (numericCols.length === 1) {\n const yKey = numericCols[0].name;\n\n // Rule 2: few categories → pie (skip if negative values)\n if (\n uniqueCategories <= INFERENCE_CONFIG.pieMaxCategories &&\n !hasNegativeValues(rows, yKey)\n ) {\n return { chartType: \"pie\", xKey, yKey };\n }\n // Rule 3: moderate categories → bar\n if (uniqueCategories <= INFERENCE_CONFIG.barMaxCategories) {\n return { chartType: \"bar\", xKey, yKey };\n }\n // Rule 4: many categories → line\n return { chartType: \"line\", xKey, yKey };\n }\n\n // Multiple numerics\n const yKey = numericCols.map((c) => c.name);\n\n // Rule 5: moderate categories → bar (grouped)\n if (uniqueCategories <= INFERENCE_CONFIG.groupedBarMaxCategories) {\n return { chartType: \"bar\", xKey, yKey };\n }\n // Rule 6: many categories → line (multi-series)\n return { chartType: \"line\", xKey, yKey };\n }\n\n // Rule 7: 2+ numerics only (no string, no date) → scatter\n if (\n numericCols.length >= 2 &&\n stringCols.length === 0 &&\n dateCols.length === 0\n ) {\n return {\n chartType: \"scatter\",\n xKey: numericCols[0].name,\n yKey: numericCols[1].name,\n };\n }\n\n // Rule 8: fallback — no chart\n return null;\n}\n"],"mappings":";AA0CA,MAAM,mBAAmB;CAEvB,SAAS;CAET,kBAAkB;CAElB,kBAAkB;CAElB,yBAAyB;CAC1B;AAQD,SAAS,YAAY,MAAiC,KAAqB;CACzE,MAAM,uBAAO,IAAI,KAAc;AAC/B,MAAK,MAAM,OAAO,KAChB,MAAK,IAAI,IAAI,KAAK;AAEpB,QAAO,KAAK;;AAGd,SAAS,kBACP,MACA,KACS;AACT,MAAK,MAAM,OAAO,KAChB,KAAI,OAAO,IAAI,KAAK,GAAG,EAAG,QAAO;AAEnC,QAAO;;;;;;AAWT,SAAgB,eACd,MACA,SACuB;AAEvB,KAAI,KAAK,SAAS,iBAAiB,WAAW,QAAQ,SAAS,EAC7D,QAAO;CAGT,MAAM,WAAW,QAAQ,QAAQ,MAAM,EAAE,aAAa,OAAO;CAC7D,MAAM,cAAc,QAAQ,QAAQ,MAAM,EAAE,aAAa,UAAU;CACnE,MAAM,aAAa,QAAQ,QAAQ,MAAM,EAAE,aAAa,SAAS;AAGjE,KAAI,YAAY,WAAW,EAAG,QAAO;AAGrC,KAAI,SAAS,SAAS,KAAK,YAAY,UAAU,EAC/C,QAAO;EACL,WAAW;EACX,MAAM,SAAS,GAAG;EAClB,MACE,YAAY,WAAW,IACnB,YAAY,GAAG,OACf,YAAY,KAAK,MAAM,EAAE,KAAK;EACrC;AAIH,KAAI,WAAW,SAAS,KAAK,YAAY,UAAU,GAAG;EACpD,MAAM,OAAO,WAAW,GAAG;EAC3B,MAAM,mBAAmB,YAAY,MAAM,KAAK;AAEhD,MAAI,YAAY,WAAW,GAAG;GAC5B,MAAM,OAAO,YAAY,GAAG;AAG5B,OACE,oBAAoB,iBAAiB,oBACrC,CAAC,kBAAkB,MAAM,KAAK,CAE9B,QAAO;IAAE,WAAW;IAAO;IAAM;IAAM;AAGzC,OAAI,oBAAoB,iBAAiB,iBACvC,QAAO;IAAE,WAAW;IAAO;IAAM;IAAM;AAGzC,UAAO;IAAE,WAAW;IAAQ;IAAM;IAAM;;EAI1C,MAAM,OAAO,YAAY,KAAK,MAAM,EAAE,KAAK;AAG3C,MAAI,oBAAoB,iBAAiB,wBACvC,QAAO;GAAE,WAAW;GAAO;GAAM;GAAM;AAGzC,SAAO;GAAE,WAAW;GAAQ;GAAM;GAAM;;AAI1C,KACE,YAAY,UAAU,KACtB,WAAW,WAAW,KACtB,SAAS,WAAW,EAEpB,QAAO;EACL,WAAW;EACX,MAAM,YAAY,GAAG;EACrB,MAAM,YAAY,GAAG;EACtB;AAIH,QAAO"}
1
+ {"version":3,"file":"genie-chart-inference.js","names":[],"sources":["../../../src/react/genie/genie-chart-inference.ts"],"sourcesContent":["/**\n * ┌─────────────────────────────────────────────────────────────────────┐\n * │ CHART INFERENCE RULES │\n * │ │\n * │ These rules determine what chart type is shown for Genie query │\n * │ results. Modify thresholds and chart type mappings here. │\n * │ │\n * │ Column types are classified from SQL type_name: │\n * │ DATE: DATE, TIMESTAMP, TIMESTAMP_NTZ │\n * │ NUMERIC: DECIMAL, INT, DOUBLE, FLOAT, LONG, etc. │\n * │ STRING: STRING, VARCHAR, CHAR │\n * │ │\n * │ Rules (applied in priority order): │\n * │ │\n * │ SKIP (return null): │\n * │ - < 2 rows │\n * │ - < 2 columns │\n * │ - No numeric columns │\n * │ │\n * │ MATCH: │\n * │ 1. DATE + numeric(s) → line (timeseries) │\n * │ 2. STRING + 1 numeric, ≤7 cats → pie │\n * │ 3. STRING + 1 numeric, ≤100 cats → bar │\n * │ 4. STRING + 1 numeric, >100 cats → line │\n * │ 5. STRING + N numerics, ≤50 cats → bar (grouped) │\n * │ 6. STRING + N numerics, >50 cats → line (multi-series) │\n * │ 7. 2+ numerics only → scatter │\n * │ 8. Otherwise → null (skip) │\n * │ │\n * │ KNOWN LIMITATIONS: │\n * │ - First-column heuristic: picks first string col as category │\n * │ - No semantic understanding (can't tell ID from meaningful val) │\n * └─────────────────────────────────────────────────────────────────────┘\n */\n\nimport type { ChartType } from \"../charts/types\";\nimport type { GenieColumnMeta } from \"./genie-query-transform\";\n\n// ---------------------------------------------------------------------------\n// Configuration — edit thresholds here\n// ---------------------------------------------------------------------------\n\nconst INFERENCE_CONFIG = {\n /** Min rows required to show any chart */\n minRows: 2,\n /** Max unique categories for pie chart */\n pieMaxCategories: 7,\n /** Max unique categories for bar chart (single series) */\n barMaxCategories: 100,\n /** Max unique categories for grouped bar chart (multi series) */\n groupedBarMaxCategories: 50,\n} as const;\n\nexport interface ChartInference {\n chartType: ChartType;\n xKey: string;\n yKey: string | string[];\n}\n\ninterface ClassifiedColumns {\n dateCols: GenieColumnMeta[];\n numericCols: GenieColumnMeta[];\n stringCols: GenieColumnMeta[];\n}\n\nfunction classifyColumns(\n rows: Record<string, unknown>[],\n columns: GenieColumnMeta[],\n): ClassifiedColumns | null {\n if (rows.length < INFERENCE_CONFIG.minRows || columns.length < 2) {\n return null;\n }\n\n const dateCols = columns.filter((c) => c.category === \"date\");\n const numericCols = columns.filter((c) => c.category === \"numeric\");\n const stringCols = columns.filter((c) => c.category === \"string\");\n\n if (numericCols.length === 0) return null;\n\n return { dateCols, numericCols, stringCols };\n}\n\nfunction countUnique(rows: Record<string, unknown>[], key: string): number {\n const seen = new Set<unknown>();\n for (const row of rows) {\n seen.add(row[key]);\n }\n return seen.size;\n}\n\nfunction hasNegativeValues(\n rows: Record<string, unknown>[],\n key: string,\n): boolean {\n for (const row of rows) {\n if (Number(row[key]) < 0) return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Main inference function\n// ---------------------------------------------------------------------------\n\n/**\n * Infer the best chart type for the given Genie query result.\n * Returns `null` when the data is not suitable for charting.\n */\nexport function inferChartType(\n rows: Record<string, unknown>[],\n columns: GenieColumnMeta[],\n): ChartInference | null {\n const classified = classifyColumns(rows, columns);\n if (!classified) return null;\n\n const { dateCols, numericCols, stringCols } = classified;\n\n // Rule 1: DATE + numeric(s) → line (timeseries)\n if (dateCols.length > 0 && numericCols.length >= 1) {\n return {\n chartType: \"line\",\n xKey: dateCols[0].name,\n yKey:\n numericCols.length === 1\n ? numericCols[0].name\n : numericCols.map((c) => c.name),\n };\n }\n\n // Rules 2–6: STRING + numeric(s)\n if (stringCols.length > 0 && numericCols.length >= 1) {\n const xKey = stringCols[0].name;\n const uniqueCategories = countUnique(rows, xKey);\n\n if (numericCols.length === 1) {\n const yKey = numericCols[0].name;\n\n // Rule 2: few categories → pie (skip if negative values)\n if (\n uniqueCategories <= INFERENCE_CONFIG.pieMaxCategories &&\n !hasNegativeValues(rows, yKey)\n ) {\n return { chartType: \"pie\", xKey, yKey };\n }\n // Rule 3: moderate categories → bar\n if (uniqueCategories <= INFERENCE_CONFIG.barMaxCategories) {\n return { chartType: \"bar\", xKey, yKey };\n }\n // Rule 4: many categories → line\n return { chartType: \"line\", xKey, yKey };\n }\n\n // Multiple numerics\n const yKey = numericCols.map((c) => c.name);\n\n // Rule 5: moderate categories → bar (grouped)\n if (uniqueCategories <= INFERENCE_CONFIG.groupedBarMaxCategories) {\n return { chartType: \"bar\", xKey, yKey };\n }\n // Rule 6: many categories → line (multi-series)\n return { chartType: \"line\", xKey, yKey };\n }\n\n // Rule 7: 2+ numerics only (no string, no date) → scatter\n if (\n numericCols.length >= 2 &&\n stringCols.length === 0 &&\n dateCols.length === 0\n ) {\n return {\n chartType: \"scatter\",\n xKey: numericCols[0].name,\n yKey: numericCols[1].name,\n };\n }\n\n // Rule 8: fallback — no chart\n return null;\n}\n\n/**\n * Return the chart types that are compatible with the given data shape.\n * Used to populate the chart-type selector dropdown.\n */\nexport function getCompatibleChartTypes(\n rows: Record<string, unknown>[],\n columns: GenieColumnMeta[],\n): ChartType[] {\n const classified = classifyColumns(rows, columns);\n if (!classified) return [];\n\n const { dateCols, numericCols, stringCols } = classified;\n\n // DATE + numeric(s) → timeseries-compatible types\n if (dateCols.length > 0 && numericCols.length >= 1) {\n return [\"line\", \"bar\", \"area\"];\n }\n\n // STRING + numeric(s)\n if (stringCols.length > 0 && numericCols.length >= 1) {\n const xKey = stringCols[0].name;\n const uniqueCategories = countUnique(rows, xKey);\n\n if (numericCols.length === 1) {\n const yKey = numericCols[0].name;\n // Few categories and no negatives → pie/donut are viable\n if (\n uniqueCategories <= INFERENCE_CONFIG.pieMaxCategories &&\n !hasNegativeValues(rows, yKey)\n ) {\n return [\"pie\", \"donut\", \"bar\", \"line\", \"area\"];\n }\n return [\"bar\", \"line\", \"area\"];\n }\n\n // Multiple numerics\n return [\"bar\", \"line\", \"area\"];\n }\n\n // 2+ numerics only → scatter-compatible\n if (\n numericCols.length >= 2 &&\n stringCols.length === 0 &&\n dateCols.length === 0\n ) {\n return [\"scatter\", \"line\", \"area\"];\n }\n\n return [];\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"genie-chat-input.d.ts","names":[],"sources":["../../../src/react/genie/genie-chat-input.tsx"],"mappings":";;;UAIiB,mBAAA;;EAEf,MAAA,GAAS,OAAA;EAFM;EAIf,QAAA;;EAEA,WAAA;EAJA;EAMA,SAAA;AAAA;;iBAIc,cAAA,CAAA;EACd,MAAA;EACA,QAAA;EACA,WAAA;EACA;AAAA,GACC,mBAAA,GAAmB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"genie-chat-input.d.ts","names":[],"sources":["../../../src/react/genie/genie-chat-input.tsx"],"mappings":";;;UAIU,mBAAA;;EAER,MAAA,GAAS,OAAA;EAFD;EAIR,QAAA;;EAEA,WAAA;EAJA;EAMA,SAAA;AAAA;;iBAIc,cAAA,CAAA;EACd,MAAA;EACA,QAAA;EACA,WAAA;EACA;AAAA,GACC,mBAAA,GAAmB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"genie-chat-input.js","names":[],"sources":["../../../src/react/genie/genie-chat-input.tsx"],"sourcesContent":["import { type KeyboardEvent, useRef, useState } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\nexport interface GenieChatInputProps {\n /** Callback fired when the user submits a message */\n onSend: (content: string) => void;\n /** Disable the input and send button */\n disabled?: boolean;\n /** Placeholder text shown in the textarea */\n placeholder?: string;\n /** Additional CSS class for the container */\n className?: string;\n}\n\n/** Auto-expanding textarea input with a send button for chat messages. Submits on Enter (Shift+Enter for newline). */\nexport function GenieChatInput({\n onSend,\n disabled = false,\n placeholder = \"Ask a question...\",\n className,\n}: GenieChatInputProps) {\n const [value, setValue] = useState(\"\");\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n const handleSubmit = () => {\n const trimmed = value.trim();\n if (!trimmed || disabled) return;\n onSend(trimmed);\n setValue(\"\");\n if (textareaRef.current) {\n textareaRef.current.style.height = \"auto\";\n }\n };\n\n const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n };\n\n const MAX_HEIGHT = 200;\n\n const handleInput = () => {\n const textarea = textareaRef.current;\n if (textarea) {\n textarea.style.height = \"auto\";\n const clamped = Math.min(textarea.scrollHeight, MAX_HEIGHT);\n textarea.style.height = `${clamped}px`;\n textarea.style.overflowY =\n textarea.scrollHeight > MAX_HEIGHT ? \"auto\" : \"hidden\";\n }\n };\n\n return (\n <div className={cn(\"flex gap-2 p-4 border-t shrink-0\", className)}>\n <textarea\n ref={textareaRef}\n value={value}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={handleKeyDown}\n onInput={handleInput}\n placeholder={placeholder}\n disabled={disabled}\n rows={1}\n className={cn(\n \"flex-1 resize-none overflow-hidden rounded-md border border-input bg-background px-3 py-2\",\n \"text-sm placeholder:text-muted-foreground\",\n \"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n )}\n />\n <Button\n onClick={handleSubmit}\n disabled={disabled || !value.trim()}\n size=\"default\"\n className=\"self-end\"\n >\n Send\n </Button>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAgBA,SAAgB,eAAe,EAC7B,QACA,WAAW,OACX,cAAc,qBACd,aACsB;CACtB,MAAM,CAAC,OAAO,YAAY,SAAS,GAAG;CACtC,MAAM,cAAc,OAA4B,KAAK;CAErD,MAAM,qBAAqB;EACzB,MAAM,UAAU,MAAM,MAAM;AAC5B,MAAI,CAAC,WAAW,SAAU;AAC1B,SAAO,QAAQ;AACf,WAAS,GAAG;AACZ,MAAI,YAAY,QACd,aAAY,QAAQ,MAAM,SAAS;;CAIvC,MAAM,iBAAiB,MAA0C;AAC/D,MAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;AACpC,KAAE,gBAAgB;AAClB,iBAAc;;;CAIlB,MAAM,aAAa;CAEnB,MAAM,oBAAoB;EACxB,MAAM,WAAW,YAAY;AAC7B,MAAI,UAAU;AACZ,YAAS,MAAM,SAAS;GACxB,MAAM,UAAU,KAAK,IAAI,SAAS,cAAc,WAAW;AAC3D,YAAS,MAAM,SAAS,GAAG,QAAQ;AACnC,YAAS,MAAM,YACb,SAAS,eAAe,aAAa,SAAS;;;AAIpD,QACE,qBAAC;EAAI,WAAW,GAAG,oCAAoC,UAAU;aAC/D,oBAAC;GACC,KAAK;GACE;GACP,WAAW,MAAM,SAAS,EAAE,OAAO,MAAM;GACzC,WAAW;GACX,SAAS;GACI;GACH;GACV,MAAM;GACN,WAAW,GACT,6FACA,6CACA,2EACA,kDACD;IACD,EACF,oBAAC;GACC,SAAS;GACT,UAAU,YAAY,CAAC,MAAM,MAAM;GACnC,MAAK;GACL,WAAU;aACX;IAEQ;GACL"}
1
+ {"version":3,"file":"genie-chat-input.js","names":[],"sources":["../../../src/react/genie/genie-chat-input.tsx"],"sourcesContent":["import { type KeyboardEvent, useRef, useState } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\ninterface GenieChatInputProps {\n /** Callback fired when the user submits a message */\n onSend: (content: string) => void;\n /** Disable the input and send button */\n disabled?: boolean;\n /** Placeholder text shown in the textarea */\n placeholder?: string;\n /** Additional CSS class for the container */\n className?: string;\n}\n\n/** Auto-expanding textarea input with a send button for chat messages. Submits on Enter (Shift+Enter for newline). */\nexport function GenieChatInput({\n onSend,\n disabled = false,\n placeholder = \"Ask a question...\",\n className,\n}: GenieChatInputProps) {\n const [value, setValue] = useState(\"\");\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n const handleSubmit = () => {\n const trimmed = value.trim();\n if (!trimmed || disabled) return;\n onSend(trimmed);\n setValue(\"\");\n if (textareaRef.current) {\n textareaRef.current.style.height = \"auto\";\n }\n };\n\n const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n };\n\n const MAX_HEIGHT = 200;\n\n const handleInput = () => {\n const textarea = textareaRef.current;\n if (textarea) {\n textarea.style.height = \"auto\";\n const clamped = Math.min(textarea.scrollHeight, MAX_HEIGHT);\n textarea.style.height = `${clamped}px`;\n textarea.style.overflowY =\n textarea.scrollHeight > MAX_HEIGHT ? \"auto\" : \"hidden\";\n }\n };\n\n return (\n <div className={cn(\"flex gap-2 p-4 border-t shrink-0\", className)}>\n <textarea\n ref={textareaRef}\n value={value}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={handleKeyDown}\n onInput={handleInput}\n placeholder={placeholder}\n disabled={disabled}\n rows={1}\n className={cn(\n \"flex-1 resize-none overflow-hidden rounded-md border border-input bg-background px-3 py-2\",\n \"text-sm placeholder:text-muted-foreground\",\n \"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n )}\n />\n <Button\n onClick={handleSubmit}\n disabled={disabled || !value.trim()}\n size=\"default\"\n className=\"self-end\"\n >\n Send\n </Button>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAgBA,SAAgB,eAAe,EAC7B,QACA,WAAW,OACX,cAAc,qBACd,aACsB;CACtB,MAAM,CAAC,OAAO,YAAY,SAAS,GAAG;CACtC,MAAM,cAAc,OAA4B,KAAK;CAErD,MAAM,qBAAqB;EACzB,MAAM,UAAU,MAAM,MAAM;AAC5B,MAAI,CAAC,WAAW,SAAU;AAC1B,SAAO,QAAQ;AACf,WAAS,GAAG;AACZ,MAAI,YAAY,QACd,aAAY,QAAQ,MAAM,SAAS;;CAIvC,MAAM,iBAAiB,MAA0C;AAC/D,MAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;AACpC,KAAE,gBAAgB;AAClB,iBAAc;;;CAIlB,MAAM,aAAa;CAEnB,MAAM,oBAAoB;EACxB,MAAM,WAAW,YAAY;AAC7B,MAAI,UAAU;AACZ,YAAS,MAAM,SAAS;GACxB,MAAM,UAAU,KAAK,IAAI,SAAS,cAAc,WAAW;AAC3D,YAAS,MAAM,SAAS,GAAG,QAAQ;AACnC,YAAS,MAAM,YACb,SAAS,eAAe,aAAa,SAAS;;;AAIpD,QACE,qBAAC;EAAI,WAAW,GAAG,oCAAoC,UAAU;aAC/D,oBAAC;GACC,KAAK;GACE;GACP,WAAW,MAAM,SAAS,EAAE,OAAO,MAAM;GACzC,WAAW;GACX,SAAS;GACI;GACH;GACV,MAAM;GACN,WAAW,GACT,6FACA,6CACA,2EACA,kDACD;IACD,EACF,oBAAC;GACC,SAAS;GACT,UAAU,YAAY,CAAC,MAAM,MAAM;GACnC,MAAK;GACL,WAAU;aACX;IAEQ;GACL"}
@@ -1 +1 @@
1
- {"version":3,"file":"genie-chat-message-list.d.ts","names":[],"sources":["../../../src/react/genie/genie-chat-message-list.tsx"],"mappings":";;;;UAQiB,yBAAA;;EAEf,QAAA,EAAU,gBAAA;EAFK;EAIf,MAAA,EAAQ,eAAA;;EAER,SAAA;EAJA;EAMA,eAAA;EAJA;EAMA,mBAAA;AAAA;;iBAkIc,oBAAA,CAAA;EACd,QAAA;EACA,MAAA;EACA,SAAA;EACA,eAAA;EACA;AAAA,GACC,yBAAA,GAAyB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"genie-chat-message-list.d.ts","names":[],"sources":["../../../src/react/genie/genie-chat-message-list.tsx"],"mappings":";;;;UAQU,yBAAA;;EAER,QAAA,EAAU,gBAAA;EAFF;EAIR,MAAA,EAAQ,eAAA;;EAER,SAAA;EAJA;EAMA,eAAA;EAJA;EAMA,mBAAA;AAAA;;iBAkIc,oBAAA,CAAA;EACd,QAAA;EACA,MAAA;EACA,SAAA;EACA,eAAA;EACA;AAAA,GACC,yBAAA,GAAyB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"genie-chat-message-list.js","names":[],"sources":["../../../src/react/genie/genie-chat-message-list.tsx"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { ScrollArea } from \"../ui/scroll-area\";\nimport { Skeleton } from \"../ui/skeleton\";\nimport { Spinner } from \"../ui/spinner\";\nimport { GenieChatMessage } from \"./genie-chat-message\";\nimport type { GenieChatStatus, GenieMessageItem } from \"./types\";\n\nexport interface GenieChatMessageListProps {\n /** Array of messages to display */\n messages: GenieMessageItem[];\n /** Current chat status (controls loading indicators and skeleton placeholders) */\n status: GenieChatStatus;\n /** Additional CSS class for the scroll area */\n className?: string;\n /** Whether a previous page of older messages exists */\n hasPreviousPage?: boolean;\n /** Callback to fetch the previous page of messages */\n onFetchPreviousPage?: () => void;\n}\n\nconst STATUS_LABELS: Record<string, string> = {\n ASKING_AI: \"Asking AI...\",\n EXECUTING_QUERY: \"Executing query...\",\n FILTERING_RESULTS: \"Filtering results...\",\n COMPLETED: \"Done\",\n};\n\nfunction formatStatus(status: string): string {\n return STATUS_LABELS[status] ?? status.replace(/_/g, \" \").toLowerCase();\n}\n\nfunction getViewport(scrollRef: React.RefObject<HTMLDivElement | null>) {\n return scrollRef.current?.querySelector<HTMLElement>(\n '[data-slot=\"scroll-area-viewport\"]',\n );\n}\n\n/**\n * Manages scroll position: scrolls to bottom on append/initial load,\n * preserves position when older messages are prepended.\n */\nfunction useScrollManagement(\n scrollRef: React.RefObject<HTMLDivElement | null>,\n messages: GenieMessageItem[],\n status: GenieChatStatus,\n) {\n const prevFirstMessageIdRef = useRef<string | null>(null);\n const prevScrollHeightRef = useRef(0);\n const prevMessageCountRef = useRef(0);\n\n // Keep prevScrollHeightRef fresh when async content (images, embeds)\n // changes the viewport height between renders.\n useEffect(() => {\n const viewport = getViewport(scrollRef);\n if (!viewport) return;\n\n const observer = new ResizeObserver(() => {\n prevScrollHeightRef.current = viewport.scrollHeight;\n });\n observer.observe(viewport);\n return () => observer.disconnect();\n }, [scrollRef]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: react to message count AND status so prevScrollHeightRef stays accurate when the loading indicator appears/disappears\n useLayoutEffect(() => {\n const viewport = getViewport(scrollRef);\n if (!viewport) return;\n\n const count = messages.length;\n const countChanged = count !== prevMessageCountRef.current;\n prevMessageCountRef.current = count;\n\n // Nothing to do if message count didn't change (e.g. status-only transition)\n if (!countChanged) {\n prevScrollHeightRef.current = viewport.scrollHeight;\n return;\n }\n\n const firstMessageId = messages[0]?.id ?? null;\n const wasPrepend =\n prevFirstMessageIdRef.current !== null &&\n firstMessageId !== prevFirstMessageIdRef.current;\n\n if (wasPrepend && prevScrollHeightRef.current > 0) {\n // Older messages prepended — preserve scroll position\n const delta = viewport.scrollHeight - prevScrollHeightRef.current;\n viewport.scrollTop += delta;\n } else {\n // Messages appended or initial load — scroll to bottom\n viewport.scrollTop = viewport.scrollHeight;\n }\n\n prevFirstMessageIdRef.current = firstMessageId;\n prevScrollHeightRef.current = viewport.scrollHeight;\n }, [messages.length, status]);\n}\n\n/**\n * Observes a sentinel element at the top of the scroll area and triggers\n * `onFetchPreviousPage` when the user scrolls to the top (only if content overflows).\n * Returns a ref to attach to the sentinel element.\n */\nfunction useLoadOlderOnScroll(\n scrollRef: React.RefObject<HTMLDivElement | null>,\n shouldObserve: boolean,\n onFetchPreviousPage?: () => void,\n) {\n const sentinelRef = useRef<HTMLDivElement>(null);\n const onFetchPreviousPageRef = useRef(onFetchPreviousPage);\n onFetchPreviousPageRef.current = onFetchPreviousPage;\n\n useEffect(() => {\n const sentinel = sentinelRef.current;\n const viewport = getViewport(scrollRef);\n if (!sentinel || !viewport || !shouldObserve) return;\n\n // The observer fires synchronously on observe() if the sentinel is\n // already visible. We arm it on the next frame so that synchronous\n // initial fire is ignored, but a real intersection (user genuinely\n // at the top on a short conversation) triggers on subsequent frames.\n let armed = false;\n const frameId = requestAnimationFrame(() => {\n armed = true;\n });\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (!armed) return;\n const isScrollable = viewport.scrollHeight > viewport.clientHeight;\n if (entries[0]?.isIntersecting && isScrollable) {\n onFetchPreviousPageRef.current?.();\n }\n },\n { root: viewport, threshold: 0 },\n );\n\n observer.observe(sentinel);\n return () => {\n cancelAnimationFrame(frameId);\n observer.disconnect();\n };\n }, [scrollRef, shouldObserve]);\n\n return sentinelRef;\n}\n\n/** Scrollable message list that renders Genie chat messages with auto-scroll, skeleton loaders, and a streaming indicator. */\nexport function GenieChatMessageList({\n messages,\n status,\n className,\n hasPreviousPage = false,\n onFetchPreviousPage,\n}: GenieChatMessageListProps) {\n const scrollRef = useRef<HTMLDivElement>(null);\n\n const sentinelRef = useLoadOlderOnScroll(\n scrollRef,\n hasPreviousPage && status !== \"loading-older\",\n onFetchPreviousPage,\n );\n useScrollManagement(scrollRef, messages, status);\n\n const lastMessage = messages[messages.length - 1];\n const showStreamingIndicator =\n status === \"streaming\" &&\n lastMessage?.role === \"assistant\" &&\n lastMessage.id === \"\";\n\n return (\n <ScrollArea ref={scrollRef} className={cn(\"flex-1 min-h-0 p-4\", className)}>\n <div className=\"flex flex-col gap-4\">\n {hasPreviousPage && <div ref={sentinelRef} className=\"h-px\" />}\n\n {status === \"loading-older\" && (\n <div className=\"flex items-center justify-center gap-2 py-2\">\n <Spinner className=\"h-3 w-3\" />\n <span className=\"text-sm text-muted-foreground\">\n Loading older messages...\n </span>\n </div>\n )}\n\n {status === \"loading-history\" && messages.length === 0 && (\n <div className=\"flex flex-col gap-4\">\n <Skeleton className=\"h-12 w-3/4\" />\n <Skeleton className=\"h-20 w-4/5 self-start\" />\n <Skeleton className=\"h-12 w-2/3 self-end\" />\n </div>\n )}\n\n {messages\n .filter(\n (msg) => msg.role !== \"assistant\" || msg.id !== \"\" || msg.content,\n )\n .map((msg) => (\n <GenieChatMessage key={msg.id} message={msg} />\n ))}\n\n {showStreamingIndicator && (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground px-11\">\n <Spinner className=\"h-3 w-3\" />\n <span>{formatStatus(lastMessage.status)}</span>\n </div>\n )}\n\n {messages.length === 0 && status === \"idle\" && (\n <div className=\"flex items-center justify-center h-full text-muted-foreground text-sm py-12\">\n Start a conversation by typing a question below.\n </div>\n )}\n </div>\n </ScrollArea>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"genie-chat-message-list.js","names":[],"sources":["../../../src/react/genie/genie-chat-message-list.tsx"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { ScrollArea } from \"../ui/scroll-area\";\nimport { Skeleton } from \"../ui/skeleton\";\nimport { Spinner } from \"../ui/spinner\";\nimport { GenieChatMessage } from \"./genie-chat-message\";\nimport type { GenieChatStatus, GenieMessageItem } from \"./types\";\n\ninterface GenieChatMessageListProps {\n /** Array of messages to display */\n messages: GenieMessageItem[];\n /** Current chat status (controls loading indicators and skeleton placeholders) */\n status: GenieChatStatus;\n /** Additional CSS class for the scroll area */\n className?: string;\n /** Whether a previous page of older messages exists */\n hasPreviousPage?: boolean;\n /** Callback to fetch the previous page of messages */\n onFetchPreviousPage?: () => void;\n}\n\nconst STATUS_LABELS: Record<string, string> = {\n ASKING_AI: \"Asking AI...\",\n EXECUTING_QUERY: \"Executing query...\",\n FILTERING_RESULTS: \"Filtering results...\",\n COMPLETED: \"Done\",\n};\n\nfunction formatStatus(status: string): string {\n return STATUS_LABELS[status] ?? status.replace(/_/g, \" \").toLowerCase();\n}\n\nfunction getViewport(scrollRef: React.RefObject<HTMLDivElement | null>) {\n return scrollRef.current?.querySelector<HTMLElement>(\n '[data-slot=\"scroll-area-viewport\"]',\n );\n}\n\n/**\n * Manages scroll position: scrolls to bottom on append/initial load,\n * preserves position when older messages are prepended.\n */\nfunction useScrollManagement(\n scrollRef: React.RefObject<HTMLDivElement | null>,\n messages: GenieMessageItem[],\n status: GenieChatStatus,\n) {\n const prevFirstMessageIdRef = useRef<string | null>(null);\n const prevScrollHeightRef = useRef(0);\n const prevMessageCountRef = useRef(0);\n\n // Keep prevScrollHeightRef fresh when async content (images, embeds)\n // changes the viewport height between renders.\n useEffect(() => {\n const viewport = getViewport(scrollRef);\n if (!viewport) return;\n\n const observer = new ResizeObserver(() => {\n prevScrollHeightRef.current = viewport.scrollHeight;\n });\n observer.observe(viewport);\n return () => observer.disconnect();\n }, [scrollRef]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: react to message count AND status so prevScrollHeightRef stays accurate when the loading indicator appears/disappears\n useLayoutEffect(() => {\n const viewport = getViewport(scrollRef);\n if (!viewport) return;\n\n const count = messages.length;\n const countChanged = count !== prevMessageCountRef.current;\n prevMessageCountRef.current = count;\n\n // Nothing to do if message count didn't change (e.g. status-only transition)\n if (!countChanged) {\n prevScrollHeightRef.current = viewport.scrollHeight;\n return;\n }\n\n const firstMessageId = messages[0]?.id ?? null;\n const wasPrepend =\n prevFirstMessageIdRef.current !== null &&\n firstMessageId !== prevFirstMessageIdRef.current;\n\n if (wasPrepend && prevScrollHeightRef.current > 0) {\n // Older messages prepended — preserve scroll position\n const delta = viewport.scrollHeight - prevScrollHeightRef.current;\n viewport.scrollTop += delta;\n } else {\n // Messages appended or initial load — scroll to bottom\n viewport.scrollTop = viewport.scrollHeight;\n }\n\n prevFirstMessageIdRef.current = firstMessageId;\n prevScrollHeightRef.current = viewport.scrollHeight;\n }, [messages.length, status]);\n}\n\n/**\n * Observes a sentinel element at the top of the scroll area and triggers\n * `onFetchPreviousPage` when the user scrolls to the top (only if content overflows).\n * Returns a ref to attach to the sentinel element.\n */\nfunction useLoadOlderOnScroll(\n scrollRef: React.RefObject<HTMLDivElement | null>,\n shouldObserve: boolean,\n onFetchPreviousPage?: () => void,\n) {\n const sentinelRef = useRef<HTMLDivElement>(null);\n const onFetchPreviousPageRef = useRef(onFetchPreviousPage);\n onFetchPreviousPageRef.current = onFetchPreviousPage;\n\n useEffect(() => {\n const sentinel = sentinelRef.current;\n const viewport = getViewport(scrollRef);\n if (!sentinel || !viewport || !shouldObserve) return;\n\n // The observer fires synchronously on observe() if the sentinel is\n // already visible. We arm it on the next frame so that synchronous\n // initial fire is ignored, but a real intersection (user genuinely\n // at the top on a short conversation) triggers on subsequent frames.\n let armed = false;\n const frameId = requestAnimationFrame(() => {\n armed = true;\n });\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (!armed) return;\n const isScrollable = viewport.scrollHeight > viewport.clientHeight;\n if (entries[0]?.isIntersecting && isScrollable) {\n onFetchPreviousPageRef.current?.();\n }\n },\n { root: viewport, threshold: 0 },\n );\n\n observer.observe(sentinel);\n return () => {\n cancelAnimationFrame(frameId);\n observer.disconnect();\n };\n }, [scrollRef, shouldObserve]);\n\n return sentinelRef;\n}\n\n/** Scrollable message list that renders Genie chat messages with auto-scroll, skeleton loaders, and a streaming indicator. */\nexport function GenieChatMessageList({\n messages,\n status,\n className,\n hasPreviousPage = false,\n onFetchPreviousPage,\n}: GenieChatMessageListProps) {\n const scrollRef = useRef<HTMLDivElement>(null);\n\n const sentinelRef = useLoadOlderOnScroll(\n scrollRef,\n hasPreviousPage && status !== \"loading-older\",\n onFetchPreviousPage,\n );\n useScrollManagement(scrollRef, messages, status);\n\n const lastMessage = messages[messages.length - 1];\n const showStreamingIndicator =\n status === \"streaming\" &&\n lastMessage?.role === \"assistant\" &&\n lastMessage.id === \"\";\n\n return (\n <ScrollArea ref={scrollRef} className={cn(\"flex-1 min-h-0 p-4\", className)}>\n <div className=\"flex flex-col gap-4\">\n {hasPreviousPage && <div ref={sentinelRef} className=\"h-px\" />}\n\n {status === \"loading-older\" && (\n <div className=\"flex items-center justify-center gap-2 py-2\">\n <Spinner className=\"h-3 w-3\" />\n <span className=\"text-sm text-muted-foreground\">\n Loading older messages...\n </span>\n </div>\n )}\n\n {status === \"loading-history\" && messages.length === 0 && (\n <div className=\"flex flex-col gap-4\">\n <Skeleton className=\"h-12 w-3/4\" />\n <Skeleton className=\"h-20 w-4/5 self-start\" />\n <Skeleton className=\"h-12 w-2/3 self-end\" />\n </div>\n )}\n\n {messages\n .filter(\n (msg) => msg.role !== \"assistant\" || msg.id !== \"\" || msg.content,\n )\n .map((msg) => (\n <GenieChatMessage key={msg.id} message={msg} />\n ))}\n\n {showStreamingIndicator && (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground px-11\">\n <Spinner className=\"h-3 w-3\" />\n <span>{formatStatus(lastMessage.status)}</span>\n </div>\n )}\n\n {messages.length === 0 && status === \"idle\" && (\n <div className=\"flex items-center justify-center h-full text-muted-foreground text-sm py-12\">\n Start a conversation by typing a question below.\n </div>\n )}\n </div>\n </ScrollArea>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"genie-chat-message.d.ts","names":[],"sources":["../../../src/react/genie/genie-chat-message.tsx"],"mappings":";;;;UA0BiB,qBAAA;;EAEf,OAAA,EAAS,gBAAA;EAFM;EAIf,SAAA;AAAA;;iBAQc,gBAAA,CAAA;EACd,OAAA;EACA;AAAA,GACC,qBAAA,GAAqB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"genie-chat-message.d.ts","names":[],"sources":["../../../src/react/genie/genie-chat-message.tsx"],"mappings":";;;;UA0BU,qBAAA;;EAER,OAAA,EAAS,gBAAA;EAFD;EAIR,SAAA;AAAA;;iBAQc,gBAAA,CAAA;EACd,OAAA;EACA;AAAA,GACC,qBAAA,GAAqB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"genie-chat-message.js","names":[],"sources":["../../../src/react/genie/genie-chat-message.tsx"],"sourcesContent":["import { marked } from \"marked\";\nimport { useMemo } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Avatar, AvatarFallback } from \"../ui/avatar\";\nimport { Card } from \"../ui/card\";\nimport { GenieQueryVisualization } from \"./genie-query-visualization\";\nimport type { GenieAttachmentResponse, GenieMessageItem } from \"./types\";\n\n/**\n * Using `marked` instead of `react-markdown` because `react-markdown` depends on\n * `micromark-util-symbol` which has broken ESM exports with `rolldown-vite`.\n * Content comes from our own Genie API so `dangerouslySetInnerHTML` is safe.\n */\nmarked.setOptions({ breaks: true, gfm: true });\n\nconst markdownStyles = cn(\n \"text-sm\",\n \"[&_p]:my-1 [&_ul]:my-1 [&_ol]:my-1 [&_li]:my-0\",\n \"[&_pre]:bg-background/50 [&_pre]:p-2 [&_pre]:rounded [&_pre]:text-xs [&_pre]:overflow-x-auto\",\n \"[&_code]:text-xs [&_code]:bg-background/50 [&_code]:px-1 [&_code]:rounded\",\n \"[&_table]:text-xs [&_th]:px-2 [&_th]:py-1 [&_td]:px-2 [&_td]:py-1\",\n \"[&_table]:border-collapse [&_th]:border [&_td]:border\",\n \"[&_th]:border-border [&_td]:border-border\",\n \"[&_a]:underline\",\n);\n\nexport interface GenieChatMessageProps {\n /** The message object to render */\n message: GenieMessageItem;\n /** Additional CSS class */\n className?: string;\n}\n\nfunction isQueryAttachment(att: GenieAttachmentResponse): boolean {\n return !!(att.query?.title || att.query?.query);\n}\n\n/** Renders a single Genie message bubble with optional expandable SQL query attachments. */\nexport function GenieChatMessage({\n message,\n className,\n}: GenieChatMessageProps) {\n const isUser = message.role === \"user\";\n const queryAttachments = message.attachments.filter(isQueryAttachment);\n const html = useMemo(\n () => (message.content ? (marked.parse(message.content) as string) : \"\"),\n [message.content],\n );\n\n return (\n <div\n className={cn(\n \"flex gap-3\",\n isUser ? \"flex-row-reverse\" : \"flex-row\",\n className,\n )}\n >\n <Avatar className=\"h-8 w-8 shrink-0 mt-1\">\n <AvatarFallback\n className={cn(\n \"text-xs font-medium\",\n isUser ? \"bg-primary text-primary-foreground\" : \"bg-muted\",\n )}\n >\n {isUser ? \"You\" : \"AI\"}\n </AvatarFallback>\n </Avatar>\n\n <div\n className={cn(\n \"flex flex-col gap-2 max-w-[80%] min-w-0 overflow-hidden\",\n isUser ? \"items-end\" : \"items-start\",\n )}\n >\n <Card\n className={cn(\n \"px-4 py-3 max-w-full overflow-hidden\",\n isUser\n ? \"bg-primary text-primary-foreground [&_*::selection]:bg-primary-foreground/30 [&::selection]:bg-primary-foreground/30\"\n : \"bg-muted\",\n )}\n >\n {html && (\n <div\n className={markdownStyles}\n dangerouslySetInnerHTML={{ __html: html }}\n />\n )}\n\n {message.error && (\n <p className=\"text-sm text-destructive mt-1\">{message.error}</p>\n )}\n </Card>\n\n {queryAttachments.length > 0 && (\n <div className=\"flex flex-col gap-2 w-full min-w-0\">\n {queryAttachments.map((att) => {\n const key = att.attachmentId ?? \"query\";\n const queryResult = att.attachmentId\n ? message.queryResults.get(att.attachmentId)\n : undefined;\n\n return (\n <div key={key} className=\"flex flex-col gap-2\">\n <Card className=\"px-4 py-3 text-xs overflow-hidden shadow-none\">\n <details>\n <summary className=\"cursor-pointer select-none font-medium\">\n {att.query?.title ?? \"SQL Query\"}\n </summary>\n <div className=\"mt-2 flex flex-col gap-1\">\n {att.query?.description && (\n <span className=\"text-muted-foreground\">\n {att.query.description}\n </span>\n )}\n {att.query?.query && (\n <pre className=\"mt-1 p-2 rounded bg-background text-[11px] whitespace-pre-wrap break-all\">\n {att.query.query}\n </pre>\n )}\n </div>\n </details>\n </Card>\n {queryResult != null && (\n <Card className=\"px-4 py-3 overflow-hidden\">\n <GenieQueryVisualization data={queryResult} />\n </Card>\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,OAAO,WAAW;CAAE,QAAQ;CAAM,KAAK;CAAM,CAAC;AAE9C,MAAM,iBAAiB,GACrB,WACA,kDACA,gGACA,6EACA,qEACA,yDACA,6CACA,kBACD;AASD,SAAS,kBAAkB,KAAuC;AAChE,QAAO,CAAC,EAAE,IAAI,OAAO,SAAS,IAAI,OAAO;;;AAI3C,SAAgB,iBAAiB,EAC/B,SACA,aACwB;CACxB,MAAM,SAAS,QAAQ,SAAS;CAChC,MAAM,mBAAmB,QAAQ,YAAY,OAAO,kBAAkB;CACtE,MAAM,OAAO,cACJ,QAAQ,UAAW,OAAO,MAAM,QAAQ,QAAQ,GAAc,IACrE,CAAC,QAAQ,QAAQ,CAClB;AAED,QACE,qBAAC;EACC,WAAW,GACT,cACA,SAAS,qBAAqB,YAC9B,UACD;aAED,oBAAC;GAAO,WAAU;aAChB,oBAAC;IACC,WAAW,GACT,uBACA,SAAS,uCAAuC,WACjD;cAEA,SAAS,QAAQ;KACH;IACV,EAET,qBAAC;GACC,WAAW,GACT,2DACA,SAAS,cAAc,cACxB;cAED,qBAAC;IACC,WAAW,GACT,wCACA,SACI,yHACA,WACL;eAEA,QACC,oBAAC;KACC,WAAW;KACX,yBAAyB,EAAE,QAAQ,MAAM;MACzC,EAGH,QAAQ,SACP,oBAAC;KAAE,WAAU;eAAiC,QAAQ;MAAU;KAE7D,EAEN,iBAAiB,SAAS,KACzB,oBAAC;IAAI,WAAU;cACZ,iBAAiB,KAAK,QAAQ;KAC7B,MAAM,MAAM,IAAI,gBAAgB;KAChC,MAAM,cAAc,IAAI,eACpB,QAAQ,aAAa,IAAI,IAAI,aAAa,GAC1C;AAEJ,YACE,qBAAC;MAAc,WAAU;iBACvB,oBAAC;OAAK,WAAU;iBACd,qBAAC,wBACC,oBAAC;QAAQ,WAAU;kBAChB,IAAI,OAAO,SAAS;SACb,EACV,qBAAC;QAAI,WAAU;mBACZ,IAAI,OAAO,eACV,oBAAC;SAAK,WAAU;mBACb,IAAI,MAAM;UACN,EAER,IAAI,OAAO,SACV,oBAAC;SAAI,WAAU;mBACZ,IAAI,MAAM;UACP;SAEJ,IACE;QACL,EACN,eAAe,QACd,oBAAC;OAAK,WAAU;iBACd,oBAAC,2BAAwB,MAAM,cAAe;QACzC;QAvBD,IAyBJ;MAER;KACE;IAEJ;GACF"}
1
+ {"version":3,"file":"genie-chat-message.js","names":[],"sources":["../../../src/react/genie/genie-chat-message.tsx"],"sourcesContent":["import { marked } from \"marked\";\nimport { useMemo } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Avatar, AvatarFallback } from \"../ui/avatar\";\nimport { Card } from \"../ui/card\";\nimport { GenieQueryVisualization } from \"./genie-query-visualization\";\nimport type { GenieAttachmentResponse, GenieMessageItem } from \"./types\";\n\n/**\n * Using `marked` instead of `react-markdown` because `react-markdown` depends on\n * `micromark-util-symbol` which has broken ESM exports with `rolldown-vite`.\n * Content comes from our own Genie API so `dangerouslySetInnerHTML` is safe.\n */\nmarked.setOptions({ breaks: true, gfm: true });\n\nconst markdownStyles = cn(\n \"text-sm\",\n \"[&_p]:my-1 [&_ul]:my-1 [&_ol]:my-1 [&_li]:my-0\",\n \"[&_pre]:bg-background/50 [&_pre]:p-2 [&_pre]:rounded [&_pre]:text-xs [&_pre]:overflow-x-auto\",\n \"[&_code]:text-xs [&_code]:bg-background/50 [&_code]:px-1 [&_code]:rounded\",\n \"[&_table]:text-xs [&_th]:px-2 [&_th]:py-1 [&_td]:px-2 [&_td]:py-1\",\n \"[&_table]:border-collapse [&_th]:border [&_td]:border\",\n \"[&_th]:border-border [&_td]:border-border\",\n \"[&_a]:underline\",\n);\n\ninterface GenieChatMessageProps {\n /** The message object to render */\n message: GenieMessageItem;\n /** Additional CSS class */\n className?: string;\n}\n\nfunction isQueryAttachment(att: GenieAttachmentResponse): boolean {\n return !!(att.query?.title || att.query?.query);\n}\n\n/** Renders a single Genie message bubble with optional expandable SQL query attachments. */\nexport function GenieChatMessage({\n message,\n className,\n}: GenieChatMessageProps) {\n const isUser = message.role === \"user\";\n const queryAttachments = message.attachments.filter(isQueryAttachment);\n const html = useMemo(\n () => (message.content ? (marked.parse(message.content) as string) : \"\"),\n [message.content],\n );\n\n return (\n <div\n className={cn(\n \"flex gap-3\",\n isUser ? \"flex-row-reverse\" : \"flex-row\",\n className,\n )}\n >\n <Avatar className=\"h-8 w-8 shrink-0 mt-1\">\n <AvatarFallback\n className={cn(\n \"text-xs font-medium\",\n isUser ? \"bg-primary text-primary-foreground\" : \"bg-muted\",\n )}\n >\n {isUser ? \"You\" : \"AI\"}\n </AvatarFallback>\n </Avatar>\n\n <div\n className={cn(\n \"flex flex-col gap-2 max-w-[80%] min-w-0 overflow-hidden\",\n isUser ? \"items-end\" : \"items-start\",\n )}\n >\n <Card\n className={cn(\n \"px-4 py-3 max-w-full overflow-hidden\",\n isUser\n ? \"bg-primary text-primary-foreground [&_*::selection]:bg-primary-foreground/30 [&::selection]:bg-primary-foreground/30\"\n : \"bg-muted\",\n )}\n >\n {html && (\n <div\n className={markdownStyles}\n dangerouslySetInnerHTML={{ __html: html }}\n />\n )}\n\n {message.error && (\n <p className=\"text-sm text-destructive mt-1\">{message.error}</p>\n )}\n </Card>\n\n {queryAttachments.length > 0 && (\n <div className=\"flex flex-col gap-2 w-full min-w-0\">\n {queryAttachments.map((att) => {\n const key = att.attachmentId ?? \"query\";\n const queryResult = att.attachmentId\n ? message.queryResults.get(att.attachmentId)\n : undefined;\n\n return (\n <div key={key} className=\"flex flex-col gap-2\">\n <Card className=\"px-4 py-3 text-xs overflow-hidden shadow-none\">\n <details>\n <summary className=\"cursor-pointer select-none font-medium\">\n {att.query?.title ?? \"SQL Query\"}\n </summary>\n <div className=\"mt-2 flex flex-col gap-1\">\n {att.query?.description && (\n <span className=\"text-muted-foreground\">\n {att.query.description}\n </span>\n )}\n {att.query?.query && (\n <pre className=\"mt-1 p-2 rounded bg-background text-[11px] whitespace-pre-wrap break-all\">\n {att.query.query}\n </pre>\n )}\n </div>\n </details>\n </Card>\n {queryResult != null && (\n <Card className=\"px-4 py-3 overflow-hidden\">\n <GenieQueryVisualization data={queryResult} />\n </Card>\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,OAAO,WAAW;CAAE,QAAQ;CAAM,KAAK;CAAM,CAAC;AAE9C,MAAM,iBAAiB,GACrB,WACA,kDACA,gGACA,6EACA,qEACA,yDACA,6CACA,kBACD;AASD,SAAS,kBAAkB,KAAuC;AAChE,QAAO,CAAC,EAAE,IAAI,OAAO,SAAS,IAAI,OAAO;;;AAI3C,SAAgB,iBAAiB,EAC/B,SACA,aACwB;CACxB,MAAM,SAAS,QAAQ,SAAS;CAChC,MAAM,mBAAmB,QAAQ,YAAY,OAAO,kBAAkB;CACtE,MAAM,OAAO,cACJ,QAAQ,UAAW,OAAO,MAAM,QAAQ,QAAQ,GAAc,IACrE,CAAC,QAAQ,QAAQ,CAClB;AAED,QACE,qBAAC;EACC,WAAW,GACT,cACA,SAAS,qBAAqB,YAC9B,UACD;aAED,oBAAC;GAAO,WAAU;aAChB,oBAAC;IACC,WAAW,GACT,uBACA,SAAS,uCAAuC,WACjD;cAEA,SAAS,QAAQ;KACH;IACV,EAET,qBAAC;GACC,WAAW,GACT,2DACA,SAAS,cAAc,cACxB;cAED,qBAAC;IACC,WAAW,GACT,wCACA,SACI,yHACA,WACL;eAEA,QACC,oBAAC;KACC,WAAW;KACX,yBAAyB,EAAE,QAAQ,MAAM;MACzC,EAGH,QAAQ,SACP,oBAAC;KAAE,WAAU;eAAiC,QAAQ;MAAU;KAE7D,EAEN,iBAAiB,SAAS,KACzB,oBAAC;IAAI,WAAU;cACZ,iBAAiB,KAAK,QAAQ;KAC7B,MAAM,MAAM,IAAI,gBAAgB;KAChC,MAAM,cAAc,IAAI,eACpB,QAAQ,aAAa,IAAI,IAAI,aAAa,GAC1C;AAEJ,YACE,qBAAC;MAAc,WAAU;iBACvB,oBAAC;OAAK,WAAU;iBACd,qBAAC,wBACC,oBAAC;QAAQ,WAAU;kBAChB,IAAI,OAAO,SAAS;SACb,EACV,qBAAC;QAAI,WAAU;mBACZ,IAAI,OAAO,eACV,oBAAC;SAAK,WAAU;mBACb,IAAI,MAAM;UACN,EAER,IAAI,OAAO,SACV,oBAAC;SAAI,WAAU;mBACZ,IAAI,MAAM;UACP;SAEJ,IACE;QACL,EACN,eAAe,QACd,oBAAC;OAAK,WAAU;iBACd,oBAAC,2BAAwB,MAAM,cAAe;QACzC;QAvBD,IAyBJ;MAER;KACE;IAEJ;GACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"genie-query-visualization.d.ts","names":[],"sources":["../../../src/react/genie/genie-query-visualization.tsx"],"mappings":";;;;;UAmBiB,4BAAA;;EAEf,IAAA,EAAM,sBAAA;;EAEN,SAAA;AAAA;;;;;;;;iBAUc,uBAAA,CAAA;EACd,IAAA;EACA;AAAA,GACC,4BAAA,GAA4B,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"genie-query-visualization.d.ts","names":[],"sources":["../../../src/react/genie/genie-query-visualization.tsx"],"mappings":";;;;;UA4CU,4BAAA;;EAER,IAAA,EAAM,sBAAA;;EAEN,SAAA;AAAA;;;;;;;;iBAUc,uBAAA,CAAA;EACd,IAAA;EACA;AAAA,GACC,4BAAA,GAA4B,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -1,15 +1,28 @@
1
1
  import { BaseChart } from "../charts/base.js";
2
2
  import { ChartErrorBoundary } from "../charts/chart-error-boundary.js";
3
- import { inferChartType } from "./genie-chart-inference.js";
3
+ import { Button } from "../ui/button.js";
4
+ import { getCompatibleChartTypes, inferChartType } from "./genie-chart-inference.js";
5
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuTrigger } from "../ui/dropdown-menu.js";
4
6
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table.js";
5
7
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs.js";
6
8
  import { transformGenieData } from "./genie-query-transform.js";
7
- import { useMemo } from "react";
9
+ import { useMemo, useState } from "react";
8
10
  import { jsx, jsxs } from "react/jsx-runtime";
11
+ import { BarChart3Icon, ChevronDownIcon } from "lucide-react";
9
12
 
10
13
  //#region src/react/genie/genie-query-visualization.tsx
11
14
  const TABLE_ROW_LIMIT = 50;
12
15
  const CHART_HEIGHT = 250;
16
+ const CHART_TYPE_LABELS = {
17
+ bar: "Bar",
18
+ line: "Line",
19
+ area: "Area",
20
+ pie: "Pie",
21
+ donut: "Donut",
22
+ scatter: "Scatter",
23
+ radar: "Radar",
24
+ heatmap: "Heatmap"
25
+ };
13
26
  /**
14
27
  * Renders a chart + data table for a Genie query result.
15
28
  *
@@ -19,11 +32,23 @@ const CHART_HEIGHT = 250;
19
32
  */
20
33
  function GenieQueryVisualization({ data, className }) {
21
34
  const transformed = useMemo(() => transformGenieData(data), [data]);
22
- const inference = useMemo(() => transformed ? inferChartType(transformed.rows, transformed.columns) : null, [transformed]);
35
+ const { inference, compatibleTypes } = useMemo(() => {
36
+ if (!transformed) return {
37
+ inference: null,
38
+ compatibleTypes: []
39
+ };
40
+ const { rows, columns } = transformed;
41
+ return {
42
+ inference: inferChartType(rows, columns),
43
+ compatibleTypes: getCompatibleChartTypes(rows, columns)
44
+ };
45
+ }, [transformed]);
46
+ const [chartTypeOverride, setChartTypeOverride] = useState(null);
23
47
  if (!transformed || transformed.rows.length === 0) return null;
24
48
  const { rows, columns } = transformed;
25
49
  const truncated = rows.length > TABLE_ROW_LIMIT;
26
50
  const displayRows = truncated ? rows.slice(0, TABLE_ROW_LIMIT) : rows;
51
+ const activeChartType = chartTypeOverride && compatibleTypes.includes(chartTypeOverride) ? chartTypeOverride : inference?.chartType ?? null;
27
52
  const dataTable = /* @__PURE__ */ jsxs("div", {
28
53
  className: "overflow-auto max-h-[300px]",
29
54
  children: [/* @__PURE__ */ jsxs(Table, { children: [/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsx(TableRow, { children: columns.map((col) => /* @__PURE__ */ jsx(TableHead, { children: col.name }, col.name)) }) }), /* @__PURE__ */ jsx(TableBody, { children: displayRows.map((row, i) => /* @__PURE__ */ jsx(TableRow, { children: columns.map((col) => /* @__PURE__ */ jsx(TableCell, { children: row[col.name] != null ? String(row[col.name]) : "" }, col.name)) }, i)) })] }), truncated && /* @__PURE__ */ jsxs("p", {
@@ -37,40 +62,65 @@ function GenieQueryVisualization({ data, className }) {
37
62
  ]
38
63
  })]
39
64
  });
40
- if (!inference) return /* @__PURE__ */ jsx("div", {
65
+ if (!inference || !activeChartType) return /* @__PURE__ */ jsx("div", {
41
66
  className,
42
67
  children: dataTable
43
68
  });
44
69
  return /* @__PURE__ */ jsxs(Tabs, {
45
70
  defaultValue: "chart",
46
71
  className,
47
- children: [
48
- /* @__PURE__ */ jsxs(TabsList, { children: [/* @__PURE__ */ jsx(TabsTrigger, {
72
+ children: [/* @__PURE__ */ jsxs("div", {
73
+ className: "flex items-center justify-between",
74
+ children: [/* @__PURE__ */ jsxs(TabsList, { children: [/* @__PURE__ */ jsx(TabsTrigger, {
49
75
  value: "chart",
50
76
  children: "Chart"
51
77
  }), /* @__PURE__ */ jsx(TabsTrigger, {
52
78
  value: "table",
53
79
  children: "Table"
54
- })] }),
55
- /* @__PURE__ */ jsx(TabsContent, {
80
+ })] }), compatibleTypes.length > 1 && /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
81
+ asChild: true,
82
+ children: /* @__PURE__ */ jsxs(Button, {
83
+ variant: "ghost",
84
+ size: "icon-sm",
85
+ "aria-label": "Change chart type",
86
+ className: "gap-0.5",
87
+ children: [/* @__PURE__ */ jsx(BarChart3Icon, { className: "size-3.5" }), /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3" })]
88
+ })
89
+ }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
90
+ align: "end",
91
+ children: [/* @__PURE__ */ jsx(DropdownMenuLabel, { children: "Chart type" }), /* @__PURE__ */ jsx(DropdownMenuRadioGroup, {
92
+ value: activeChartType,
93
+ onValueChange: (v) => setChartTypeOverride(v),
94
+ children: compatibleTypes.map((type) => /* @__PURE__ */ jsx(DropdownMenuRadioItem, {
95
+ value: type,
96
+ children: CHART_TYPE_LABELS[type]
97
+ }, type))
98
+ })]
99
+ })] })]
100
+ }), /* @__PURE__ */ jsxs("div", {
101
+ className: "grid [&>*]:col-start-1 [&>*]:row-start-1",
102
+ children: [/* @__PURE__ */ jsx(TabsContent, {
56
103
  value: "chart",
104
+ forceMount: true,
105
+ className: "data-[state=inactive]:invisible",
57
106
  children: /* @__PURE__ */ jsx(ChartErrorBoundary, {
58
107
  fallback: dataTable,
59
108
  children: /* @__PURE__ */ jsx(BaseChart, {
60
109
  data: rows,
61
- chartType: inference.chartType,
110
+ chartType: activeChartType,
62
111
  xKey: inference.xKey,
63
112
  yKey: inference.yKey,
64
113
  height: CHART_HEIGHT,
65
114
  showLegend: Array.isArray(inference.yKey)
66
115
  })
67
116
  })
68
- }),
69
- /* @__PURE__ */ jsx(TabsContent, {
117
+ }), /* @__PURE__ */ jsx(TabsContent, {
70
118
  value: "table",
119
+ forceMount: true,
120
+ className: "data-[state=inactive]:invisible",
71
121
  children: dataTable
72
- })
73
- ]
122
+ })]
123
+ })]
74
124
  });
75
125
  }
76
126
 
@@ -1 +1 @@
1
- {"version":3,"file":"genie-query-visualization.js","names":[],"sources":["../../../src/react/genie/genie-query-visualization.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport type { GenieStatementResponse } from \"shared\";\nimport { BaseChart } from \"../charts/base\";\nimport { ChartErrorBoundary } from \"../charts/chart-error-boundary\";\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"../ui/table\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"../ui/tabs\";\nimport { inferChartType } from \"./genie-chart-inference\";\nimport { transformGenieData } from \"./genie-query-transform\";\n\nconst TABLE_ROW_LIMIT = 50;\nconst CHART_HEIGHT = 250;\n\nexport interface GenieQueryVisualizationProps {\n /** Raw statement_response from the Genie API */\n data: GenieStatementResponse;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Renders a chart + data table for a Genie query result.\n *\n * - When a chart type can be inferred: shows Tabs with \"Chart\" (default) and \"Table\"\n * - When no chart fits: shows only the data table\n * - When data is empty/malformed: renders nothing\n */\nexport function GenieQueryVisualization({\n data,\n className,\n}: GenieQueryVisualizationProps) {\n const transformed = useMemo(() => transformGenieData(data), [data]);\n const inference = useMemo(\n () =>\n transformed\n ? inferChartType(transformed.rows, transformed.columns)\n : null,\n [transformed],\n );\n\n if (!transformed || transformed.rows.length === 0) return null;\n\n const { rows, columns } = transformed;\n const truncated = rows.length > TABLE_ROW_LIMIT;\n const displayRows = truncated ? rows.slice(0, TABLE_ROW_LIMIT) : rows;\n\n const dataTable = (\n <div className=\"overflow-auto max-h-[300px]\">\n <Table>\n <TableHeader>\n <TableRow>\n {columns.map((col) => (\n <TableHead key={col.name}>{col.name}</TableHead>\n ))}\n </TableRow>\n </TableHeader>\n <TableBody>\n {displayRows.map((row, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: tabular data rows have no unique identifier\n <TableRow key={i}>\n {columns.map((col) => (\n <TableCell key={col.name}>\n {row[col.name] != null ? String(row[col.name]) : \"\"}\n </TableCell>\n ))}\n </TableRow>\n ))}\n </TableBody>\n </Table>\n {truncated && (\n <p className=\"text-xs text-muted-foreground px-2 py-1\">\n Showing {TABLE_ROW_LIMIT} of {rows.length} rows\n </p>\n )}\n </div>\n );\n\n if (!inference) {\n return <div className={className}>{dataTable}</div>;\n }\n\n return (\n <Tabs defaultValue=\"chart\" className={className}>\n <TabsList>\n <TabsTrigger value=\"chart\">Chart</TabsTrigger>\n <TabsTrigger value=\"table\">Table</TabsTrigger>\n </TabsList>\n <TabsContent value=\"chart\">\n <ChartErrorBoundary fallback={dataTable}>\n <BaseChart\n data={rows}\n chartType={inference.chartType}\n xKey={inference.xKey}\n yKey={inference.yKey}\n height={CHART_HEIGHT}\n showLegend={Array.isArray(inference.yKey)}\n />\n </ChartErrorBoundary>\n </TabsContent>\n <TabsContent value=\"table\">{dataTable}</TabsContent>\n </Tabs>\n );\n}\n"],"mappings":";;;;;;;;;;AAgBA,MAAM,kBAAkB;AACxB,MAAM,eAAe;;;;;;;;AAgBrB,SAAgB,wBAAwB,EACtC,MACA,aAC+B;CAC/B,MAAM,cAAc,cAAc,mBAAmB,KAAK,EAAE,CAAC,KAAK,CAAC;CACnE,MAAM,YAAY,cAEd,cACI,eAAe,YAAY,MAAM,YAAY,QAAQ,GACrD,MACN,CAAC,YAAY,CACd;AAED,KAAI,CAAC,eAAe,YAAY,KAAK,WAAW,EAAG,QAAO;CAE1D,MAAM,EAAE,MAAM,YAAY;CAC1B,MAAM,YAAY,KAAK,SAAS;CAChC,MAAM,cAAc,YAAY,KAAK,MAAM,GAAG,gBAAgB,GAAG;CAEjE,MAAM,YACJ,qBAAC;EAAI,WAAU;aACb,qBAAC,oBACC,oBAAC,yBACC,oBAAC,sBACE,QAAQ,KAAK,QACZ,oBAAC,uBAA0B,IAAI,QAAf,IAAI,KAA4B,CAChD,GACO,GACC,EACd,oBAAC,uBACE,YAAY,KAAK,KAAK,MAErB,oBAAC,sBACE,QAAQ,KAAK,QACZ,oBAAC,uBACE,IAAI,IAAI,SAAS,OAAO,OAAO,IAAI,IAAI,MAAM,GAAG,MADnC,IAAI,KAER,CACZ,IALW,EAMJ,CACX,GACQ,IACN,EACP,aACC,qBAAC;GAAE,WAAU;;IAA0C;IAC5C;IAAgB;IAAK,KAAK;IAAO;;IACxC;GAEF;AAGR,KAAI,CAAC,UACH,QAAO,oBAAC;EAAe;YAAY;GAAgB;AAGrD,QACE,qBAAC;EAAK,cAAa;EAAmB;;GACpC,qBAAC,uBACC,oBAAC;IAAY,OAAM;cAAQ;KAAmB,EAC9C,oBAAC;IAAY,OAAM;cAAQ;KAAmB,IACrC;GACX,oBAAC;IAAY,OAAM;cACjB,oBAAC;KAAmB,UAAU;eAC5B,oBAAC;MACC,MAAM;MACN,WAAW,UAAU;MACrB,MAAM,UAAU;MAChB,MAAM,UAAU;MAChB,QAAQ;MACR,YAAY,MAAM,QAAQ,UAAU,KAAK;OACzC;MACiB;KACT;GACd,oBAAC;IAAY,OAAM;cAAS;KAAwB;;GAC/C"}
1
+ {"version":3,"file":"genie-query-visualization.js","names":[],"sources":["../../../src/react/genie/genie-query-visualization.tsx"],"sourcesContent":["import { BarChart3Icon, ChevronDownIcon } from \"lucide-react\";\nimport { useMemo, useState } from \"react\";\nimport type { GenieStatementResponse } from \"shared\";\nimport { BaseChart } from \"../charts/base\";\nimport { ChartErrorBoundary } from \"../charts/chart-error-boundary\";\nimport type { ChartType } from \"../charts/types\";\nimport { Button } from \"../ui/button\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuTrigger,\n} from \"../ui/dropdown-menu\";\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"../ui/table\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"../ui/tabs\";\nimport {\n getCompatibleChartTypes,\n inferChartType,\n} from \"./genie-chart-inference\";\nimport { transformGenieData } from \"./genie-query-transform\";\n\nconst TABLE_ROW_LIMIT = 50;\nconst CHART_HEIGHT = 250;\n\nconst CHART_TYPE_LABELS: Record<ChartType, string> = {\n bar: \"Bar\",\n line: \"Line\",\n area: \"Area\",\n pie: \"Pie\",\n donut: \"Donut\",\n scatter: \"Scatter\",\n radar: \"Radar\",\n heatmap: \"Heatmap\",\n};\n\ninterface GenieQueryVisualizationProps {\n /** Raw statement_response from the Genie API */\n data: GenieStatementResponse;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Renders a chart + data table for a Genie query result.\n *\n * - When a chart type can be inferred: shows Tabs with \"Chart\" (default) and \"Table\"\n * - When no chart fits: shows only the data table\n * - When data is empty/malformed: renders nothing\n */\nexport function GenieQueryVisualization({\n data,\n className,\n}: GenieQueryVisualizationProps) {\n const transformed = useMemo(() => transformGenieData(data), [data]);\n const { inference, compatibleTypes } = useMemo(() => {\n if (!transformed)\n return { inference: null, compatibleTypes: [] as ChartType[] };\n const { rows, columns } = transformed;\n return {\n inference: inferChartType(rows, columns),\n compatibleTypes: getCompatibleChartTypes(rows, columns),\n };\n }, [transformed]);\n\n const [chartTypeOverride, setChartTypeOverride] = useState<ChartType | null>(\n null,\n );\n\n if (!transformed || transformed.rows.length === 0) return null;\n\n const { rows, columns } = transformed;\n const truncated = rows.length > TABLE_ROW_LIMIT;\n const displayRows = truncated ? rows.slice(0, TABLE_ROW_LIMIT) : rows;\n\n const activeChartType =\n chartTypeOverride && compatibleTypes.includes(chartTypeOverride)\n ? chartTypeOverride\n : (inference?.chartType ?? null);\n\n const dataTable = (\n <div className=\"overflow-auto max-h-[300px]\">\n <Table>\n <TableHeader>\n <TableRow>\n {columns.map((col) => (\n <TableHead key={col.name}>{col.name}</TableHead>\n ))}\n </TableRow>\n </TableHeader>\n <TableBody>\n {displayRows.map((row, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: tabular data rows have no unique identifier\n <TableRow key={i}>\n {columns.map((col) => (\n <TableCell key={col.name}>\n {row[col.name] != null ? String(row[col.name]) : \"\"}\n </TableCell>\n ))}\n </TableRow>\n ))}\n </TableBody>\n </Table>\n {truncated && (\n <p className=\"text-xs text-muted-foreground px-2 py-1\">\n Showing {TABLE_ROW_LIMIT} of {rows.length} rows\n </p>\n )}\n </div>\n );\n\n if (!inference || !activeChartType) {\n return <div className={className}>{dataTable}</div>;\n }\n\n return (\n <Tabs defaultValue=\"chart\" className={className}>\n <div className=\"flex items-center justify-between\">\n <TabsList>\n <TabsTrigger value=\"chart\">Chart</TabsTrigger>\n <TabsTrigger value=\"table\">Table</TabsTrigger>\n </TabsList>\n {compatibleTypes.length > 1 && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label=\"Change chart type\"\n className=\"gap-0.5\"\n >\n <BarChart3Icon className=\"size-3.5\" />\n <ChevronDownIcon className=\"size-3\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuLabel>Chart type</DropdownMenuLabel>\n <DropdownMenuRadioGroup\n value={activeChartType}\n onValueChange={(v) => setChartTypeOverride(v as ChartType)}\n >\n {compatibleTypes.map((type) => (\n <DropdownMenuRadioItem key={type} value={type}>\n {CHART_TYPE_LABELS[type]}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </div>\n <div className=\"grid [&>*]:col-start-1 [&>*]:row-start-1\">\n <TabsContent\n value=\"chart\"\n forceMount\n className=\"data-[state=inactive]:invisible\"\n >\n <ChartErrorBoundary fallback={dataTable}>\n <BaseChart\n data={rows}\n chartType={activeChartType}\n xKey={inference.xKey}\n yKey={inference.yKey}\n height={CHART_HEIGHT}\n showLegend={Array.isArray(inference.yKey)}\n />\n </ChartErrorBoundary>\n </TabsContent>\n <TabsContent\n value=\"table\"\n forceMount\n className=\"data-[state=inactive]:invisible\"\n >\n {dataTable}\n </TabsContent>\n </div>\n </Tabs>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AA8BA,MAAM,kBAAkB;AACxB,MAAM,eAAe;AAErB,MAAM,oBAA+C;CACnD,KAAK;CACL,MAAM;CACN,MAAM;CACN,KAAK;CACL,OAAO;CACP,SAAS;CACT,OAAO;CACP,SAAS;CACV;;;;;;;;AAgBD,SAAgB,wBAAwB,EACtC,MACA,aAC+B;CAC/B,MAAM,cAAc,cAAc,mBAAmB,KAAK,EAAE,CAAC,KAAK,CAAC;CACnE,MAAM,EAAE,WAAW,oBAAoB,cAAc;AACnD,MAAI,CAAC,YACH,QAAO;GAAE,WAAW;GAAM,iBAAiB,EAAE;GAAiB;EAChE,MAAM,EAAE,MAAM,YAAY;AAC1B,SAAO;GACL,WAAW,eAAe,MAAM,QAAQ;GACxC,iBAAiB,wBAAwB,MAAM,QAAQ;GACxD;IACA,CAAC,YAAY,CAAC;CAEjB,MAAM,CAAC,mBAAmB,wBAAwB,SAChD,KACD;AAED,KAAI,CAAC,eAAe,YAAY,KAAK,WAAW,EAAG,QAAO;CAE1D,MAAM,EAAE,MAAM,YAAY;CAC1B,MAAM,YAAY,KAAK,SAAS;CAChC,MAAM,cAAc,YAAY,KAAK,MAAM,GAAG,gBAAgB,GAAG;CAEjE,MAAM,kBACJ,qBAAqB,gBAAgB,SAAS,kBAAkB,GAC5D,oBACC,WAAW,aAAa;CAE/B,MAAM,YACJ,qBAAC;EAAI,WAAU;aACb,qBAAC,oBACC,oBAAC,yBACC,oBAAC,sBACE,QAAQ,KAAK,QACZ,oBAAC,uBAA0B,IAAI,QAAf,IAAI,KAA4B,CAChD,GACO,GACC,EACd,oBAAC,uBACE,YAAY,KAAK,KAAK,MAErB,oBAAC,sBACE,QAAQ,KAAK,QACZ,oBAAC,uBACE,IAAI,IAAI,SAAS,OAAO,OAAO,IAAI,IAAI,MAAM,GAAG,MADnC,IAAI,KAER,CACZ,IALW,EAMJ,CACX,GACQ,IACN,EACP,aACC,qBAAC;GAAE,WAAU;;IAA0C;IAC5C;IAAgB;IAAK,KAAK;IAAO;;IACxC;GAEF;AAGR,KAAI,CAAC,aAAa,CAAC,gBACjB,QAAO,oBAAC;EAAe;YAAY;GAAgB;AAGrD,QACE,qBAAC;EAAK,cAAa;EAAmB;aACpC,qBAAC;GAAI,WAAU;cACb,qBAAC,uBACC,oBAAC;IAAY,OAAM;cAAQ;KAAmB,EAC9C,oBAAC;IAAY,OAAM;cAAQ;KAAmB,IACrC,EACV,gBAAgB,SAAS,KACxB,qBAAC,2BACC,oBAAC;IAAoB;cACnB,qBAAC;KACC,SAAQ;KACR,MAAK;KACL,cAAW;KACX,WAAU;gBAEV,oBAAC,iBAAc,WAAU,aAAa,EACtC,oBAAC,mBAAgB,WAAU,WAAW;MAC/B;KACW,EACtB,qBAAC;IAAoB,OAAM;eACzB,oBAAC,+BAAkB,eAA8B,EACjD,oBAAC;KACC,OAAO;KACP,gBAAgB,MAAM,qBAAqB,EAAe;eAEzD,gBAAgB,KAAK,SACpB,oBAAC;MAAiC,OAAO;gBACtC,kBAAkB;QADO,KAEJ,CACxB;MACqB;KACL,IACT;IAEb,EACN,qBAAC;GAAI,WAAU;cACb,oBAAC;IACC,OAAM;IACN;IACA,WAAU;cAEV,oBAAC;KAAmB,UAAU;eAC5B,oBAAC;MACC,MAAM;MACN,WAAW;MACX,MAAM,UAAU;MAChB,MAAM,UAAU;MAChB,QAAQ;MACR,YAAY,MAAM,QAAQ,UAAU,KAAK;OACzC;MACiB;KACT,EACd,oBAAC;IACC,OAAM;IACN;IACA,WAAU;cAET;KACW;IACV;GACD"}
@@ -1,6 +1,6 @@
1
1
  import { GenieAttachmentResponse, GenieMessageResponse, GenieStatementResponse, GenieStreamEvent } from "../../shared/src/genie.js";
2
2
  import { ColumnCategory, GenieColumnMeta, TransformedGenieData, transformGenieData } from "./genie-query-transform.js";
3
- import { ChartInference, inferChartType } from "./genie-chart-inference.js";
3
+ import { ChartInference, getCompatibleChartTypes, inferChartType } from "./genie-chart-inference.js";
4
4
  import { GenieChatProps, GenieChatStatus, GenieMessageItem, UseGenieChatOptions, UseGenieChatReturn } from "./types.js";
5
5
  import { GenieChat } from "./genie-chat.js";
6
6
  import { GenieChatInput } from "./genie-chat-input.js";
@@ -1,4 +1,4 @@
1
- import { inferChartType } from "./genie-chart-inference.js";
1
+ import { getCompatibleChartTypes, inferChartType } from "./genie-chart-inference.js";
2
2
  import { GenieChatInput } from "./genie-chat-input.js";
3
3
  import { transformGenieData } from "./genie-query-transform.js";
4
4
  import { GenieQueryVisualization } from "./genie-query-visualization.js";
@@ -13,7 +13,7 @@ import { BaseChart, BaseChartProps } from "./charts/base.js";
13
13
  import { createChart } from "./charts/create-chart.js";
14
14
  import { ChartWrapper, ChartWrapperProps } from "./charts/wrapper.js";
15
15
  import { NormalizedHeatmapData, normalizeChartData, normalizeHeatmapData } from "./charts/normalize.js";
16
- import { CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, FALLBACK_COLORS, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL } from "./charts/constants.js";
16
+ import { CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL } from "./charts/constants.js";
17
17
  import { useAllThemeColors, useThemeColors } from "./charts/theme.js";
18
18
  import { createTimeSeriesData, formatLabel, sortTimeSeriesAscending, toChartArray, toChartValue, truncateLabel } from "./charts/utils.js";
19
19
  import { CartesianContext, HeatmapContext, OptionBuilderContext, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption } from "./charts/options.js";
@@ -27,7 +27,7 @@ import { FilePreviewPanel, FilePreviewPanelProps } from "./file-browser/file-pre
27
27
  import { NewFolderInput, NewFolderInputProps } from "./file-browser/new-folder-input.js";
28
28
  import "./file-browser/index.js";
29
29
  import { ColumnCategory, GenieColumnMeta, TransformedGenieData, transformGenieData } from "./genie/genie-query-transform.js";
30
- import { ChartInference, inferChartType } from "./genie/genie-chart-inference.js";
30
+ import { ChartInference, getCompatibleChartTypes, inferChartType } from "./genie/genie-chart-inference.js";
31
31
  import { GenieChatProps, GenieChatStatus, GenieMessageItem, UseGenieChatOptions, UseGenieChatReturn } from "./genie/types.js";
32
32
  import { GenieChat } from "./genie/genie-chat.js";
33
33
  import { GenieChatInput } from "./genie/genie-chat-input.js";
@@ -97,4 +97,4 @@ import { Textarea } from "./ui/textarea.js";
97
97
  import { Toggle, toggleVariants } from "./ui/toggle.js";
98
98
  import { ToggleGroup, ToggleGroupItem } from "./ui/toggle-group.js";
99
99
  import "./ui/index.js";
100
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AnalyticsFormat, AreaChart, AreaChartProps, AreaChartSpecificProps, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, BarChart, BarChartProps, BarChartSpecificProps, BaseChart, BaseChartProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselApi, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, CartesianContext, ChartBaseProps, ChartColorPalette, ChartConfig, ChartContainer, ChartData, ChartInference, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, ChartType, ChartWrapper, ChartWrapperProps, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, ColumnCategory, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DATE_FIELD_PATTERNS, DataFormat, DataProps, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectoryEntry, DirectoryList, DirectoryListProps, DonutChart, DonutChartProps, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, FALLBACK_COLORS, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, FileBreadcrumb, FileBreadcrumbProps, FileBrowserLabels, FileEntry, FileEntryProps, FilePreview, FilePreviewPanel, FilePreviewPanelProps, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, GenieAttachmentResponse, GenieChat, GenieChatInput, GenieChatMessage, GenieChatMessageList, GenieChatProps, GenieChatStatus, GenieColumnMeta, GenieMessageItem, GenieMessageResponse, GenieQueryVisualization, GenieStatementResponse, GenieStreamEvent, HeatmapChart, HeatmapChartProps, HeatmapChartSpecificProps, HeatmapContext, HoverCard, HoverCardContent, HoverCardTrigger, InferResultByFormat, InferRowType, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, LineChart, LineChartProps, LineChartSpecificProps, METADATA_DATE_PATTERNS, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NAME_FIELD_PATTERNS, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NewFolderInput, NewFolderInputProps, NormalizedChartData, NormalizedChartDataBase, NormalizedHeatmapData, OptionBuilderContext, Orientation, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PieChart, PieChartProps, PieChartSpecificProps, PluginRegistry, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalContainerContext, PortalContainerProvider, Progress, QueryProps, QueryRegistry, RadarChart, RadarChartProps, RadarChartSpecificProps, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScatterChart, ScatterChartProps, ScatterChartSpecificProps, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TransformedGenieData, TypedArrowTable, UnifiedChartProps, UseAnalyticsQueryOptions, UseAnalyticsQueryResult, UseChartDataOptions, UseChartDataResult, UseGenieChatOptions, UseGenieChatReturn, badgeVariants, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption, buttonGroupVariants, buttonVariants, cn, createChart, createTimeSeriesData, formatFileSize, formatLabel, inferChartType, isArrowTable, isDataProps, isQueryProps, navigationMenuTriggerStyle, normalizeChartData, normalizeHeatmapData, sortTimeSeriesAscending, toChartArray, toChartValue, toggleVariants, transformGenieData, truncateLabel, useAllThemeColors, useAnalyticsQuery, useChartData, useFormField, useGenieChat, usePortalContainer, useResolvedPortalContainer, useSidebar, useThemeColors };
100
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AnalyticsFormat, AreaChart, AreaChartProps, AreaChartSpecificProps, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, BarChart, BarChartProps, BarChartSpecificProps, BaseChart, BaseChartProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselApi, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, CartesianContext, ChartBaseProps, ChartColorPalette, ChartConfig, ChartContainer, ChartData, ChartInference, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, ChartType, ChartWrapper, ChartWrapperProps, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, ColumnCategory, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DATE_FIELD_PATTERNS, DataFormat, DataProps, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectoryEntry, DirectoryList, DirectoryListProps, DonutChart, DonutChartProps, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, FileBreadcrumb, FileBreadcrumbProps, FileBrowserLabels, FileEntry, FileEntryProps, FilePreview, FilePreviewPanel, FilePreviewPanelProps, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, GenieAttachmentResponse, GenieChat, GenieChatInput, GenieChatMessage, GenieChatMessageList, GenieChatProps, GenieChatStatus, GenieColumnMeta, GenieMessageItem, GenieMessageResponse, GenieQueryVisualization, GenieStatementResponse, GenieStreamEvent, HeatmapChart, HeatmapChartProps, HeatmapChartSpecificProps, HeatmapContext, HoverCard, HoverCardContent, HoverCardTrigger, InferResultByFormat, InferRowType, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, LineChart, LineChartProps, LineChartSpecificProps, METADATA_DATE_PATTERNS, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NAME_FIELD_PATTERNS, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NewFolderInput, NewFolderInputProps, NormalizedChartData, NormalizedChartDataBase, NormalizedHeatmapData, OptionBuilderContext, Orientation, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PieChart, PieChartProps, PieChartSpecificProps, PluginRegistry, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalContainerContext, PortalContainerProvider, Progress, QueryProps, QueryRegistry, RadarChart, RadarChartProps, RadarChartSpecificProps, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScatterChart, ScatterChartProps, ScatterChartSpecificProps, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TransformedGenieData, TypedArrowTable, UnifiedChartProps, UseAnalyticsQueryOptions, UseAnalyticsQueryResult, UseChartDataOptions, UseChartDataResult, UseGenieChatOptions, UseGenieChatReturn, badgeVariants, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption, buttonGroupVariants, buttonVariants, cn, createChart, createTimeSeriesData, formatFileSize, formatLabel, getCompatibleChartTypes, inferChartType, isArrowTable, isDataProps, isQueryProps, navigationMenuTriggerStyle, normalizeChartData, normalizeHeatmapData, sortTimeSeriesAscending, toChartArray, toChartValue, toggleVariants, transformGenieData, truncateLabel, useAllThemeColors, useAnalyticsQuery, useChartData, useFormField, useGenieChat, usePortalContainer, useResolvedPortalContainer, useSidebar, useThemeColors };
@@ -1,5 +1,5 @@
1
1
  import { DATE_FIELD_PATTERNS, METADATA_DATE_PATTERNS, NAME_FIELD_PATTERNS } from "../js/constants.js";
2
- import { CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, FALLBACK_COLORS, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL } from "./charts/constants.js";
2
+ import { CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL } from "./charts/constants.js";
3
3
  import { isArrowTable, isDataProps, isQueryProps } from "./charts/types.js";
4
4
  import { createTimeSeriesData, formatLabel, sortTimeSeriesAscending, toChartArray, toChartValue, truncateLabel } from "./charts/utils.js";
5
5
  import { normalizeChartData, normalizeHeatmapData } from "./charts/normalize.js";
@@ -30,11 +30,13 @@ import { FileBreadcrumb } from "./file-browser/file-breadcrumb.js";
30
30
  import { FilePreviewPanel } from "./file-browser/file-preview-panel.js";
31
31
  import { NewFolderInput } from "./file-browser/new-folder-input.js";
32
32
  import "./file-browser/index.js";
33
- import { inferChartType } from "./genie/genie-chart-inference.js";
33
+ import { getCompatibleChartTypes, inferChartType } from "./genie/genie-chart-inference.js";
34
34
  import { GenieChatInput } from "./genie/genie-chat-input.js";
35
35
  import { ScrollArea, ScrollBar } from "./ui/scroll-area.js";
36
36
  import { Spinner } from "./ui/spinner.js";
37
37
  import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar.js";
38
+ import { PortalContainerContext, PortalContainerProvider, usePortalContainer, useResolvedPortalContainer } from "./portal-container-context.js";
39
+ import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "./ui/dropdown-menu.js";
38
40
  import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "./ui/table.js";
39
41
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs.js";
40
42
  import { transformGenieData } from "./genie/genie-query-transform.js";
@@ -45,8 +47,6 @@ import { useGenieChat } from "./genie/use-genie-chat.js";
45
47
  import { GenieChat } from "./genie/genie-chat.js";
46
48
  import "./genie/index.js";
47
49
  import "./hooks/index.js";
48
- import { PortalContainerContext, PortalContainerProvider, usePortalContainer, useResolvedPortalContainer } from "./portal-container-context.js";
49
- import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "./ui/dropdown-menu.js";
50
50
  import { Input } from "./ui/input.js";
51
51
  import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue } from "./ui/select.js";
52
52
  import { Checkbox } from "./ui/checkbox.js";
@@ -94,4 +94,4 @@ import { Toggle, toggleVariants } from "./ui/toggle.js";
94
94
  import { ToggleGroup, ToggleGroupItem } from "./ui/toggle-group.js";
95
95
  import "./ui/index.js";
96
96
 
97
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AreaChart, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, BarChart, BaseChart, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, ChartWrapper, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DATE_FIELD_PATTERNS, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectoryList, DonutChart, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, FALLBACK_COLORS, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, FileBreadcrumb, FileEntry, FilePreviewPanel, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, GenieChat, GenieChatInput, GenieChatMessage, GenieChatMessageList, GenieQueryVisualization, HeatmapChart, HoverCard, HoverCardContent, HoverCardTrigger, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, LineChart, METADATA_DATE_PATTERNS, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NAME_FIELD_PATTERNS, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NewFolderInput, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PieChart, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalContainerContext, PortalContainerProvider, Progress, RadarChart, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScatterChart, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption, buttonGroupVariants, buttonVariants, cn, createChart, createTimeSeriesData, formatFileSize, formatLabel, inferChartType, isArrowTable, isDataProps, isQueryProps, navigationMenuTriggerStyle, normalizeChartData, normalizeHeatmapData, sortTimeSeriesAscending, toChartArray, toChartValue, toggleVariants, transformGenieData, truncateLabel, useAllThemeColors, useAnalyticsQuery, useChartData, useFormField, useGenieChat, usePortalContainer, useResolvedPortalContainer, useSidebar, useThemeColors };
97
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AreaChart, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, BarChart, BaseChart, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, ChartWrapper, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DATE_FIELD_PATTERNS, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectoryList, DonutChart, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, FileBreadcrumb, FileEntry, FilePreviewPanel, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, GenieChat, GenieChatInput, GenieChatMessage, GenieChatMessageList, GenieQueryVisualization, HeatmapChart, HoverCard, HoverCardContent, HoverCardTrigger, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, LineChart, METADATA_DATE_PATTERNS, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NAME_FIELD_PATTERNS, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NewFolderInput, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PieChart, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalContainerContext, PortalContainerProvider, Progress, RadarChart, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScatterChart, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption, buttonGroupVariants, buttonVariants, cn, createChart, createTimeSeriesData, formatFileSize, formatLabel, getCompatibleChartTypes, inferChartType, isArrowTable, isDataProps, isQueryProps, navigationMenuTriggerStyle, normalizeChartData, normalizeHeatmapData, sortTimeSeriesAscending, toChartArray, toChartValue, toggleVariants, transformGenieData, truncateLabel, useAllThemeColors, useAnalyticsQuery, useChartData, useFormField, useGenieChat, usePortalContainer, useResolvedPortalContainer, useSidebar, useThemeColors };
@@ -1,7 +1,7 @@
1
1
  import { formatFieldLabel } from "../lib/format.js";
2
2
  import { Button } from "../ui/button.js";
3
- import { Table as Table$1, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table.js";
4
3
  import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuTrigger } from "../ui/dropdown-menu.js";
4
+ import { Table as Table$1, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table.js";
5
5
  import { Input } from "../ui/input.js";
6
6
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select.js";
7
7
  import { TableWrapper } from "./table-wrapper.js";
@@ -5,9 +5,9 @@ import { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, Breadcr
5
5
  import { ScrollArea, ScrollBar } from "./scroll-area.js";
6
6
  import { Spinner } from "./spinner.js";
7
7
  import { Avatar, AvatarFallback, AvatarImage } from "./avatar.js";
8
+ import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "./dropdown-menu.js";
8
9
  import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "./table.js";
9
10
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs.js";
10
- import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "./dropdown-menu.js";
11
11
  import { Input } from "./input.js";
12
12
  import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue } from "./select.js";
13
13
  import { Checkbox } from "./checkbox.js";