@marimo-team/frontend 0.16.1 → 0.16.2

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 (167) hide show
  1. package/dist/assets/{ConnectedDataExplorerComponent-2wVcyvDj.js → ConnectedDataExplorerComponent-B5cPvWoQ.js} +1 -1
  2. package/dist/assets/{ImageComparisonComponent-D2j6i0hv.js → ImageComparisonComponent-CqR26LSv.js} +1 -1
  3. package/dist/assets/{VegaLite-BckFaf2D.js → VegaLite-DvQDATwI.js} +1 -1
  4. package/dist/assets/_baseEach--KDTwKbG.js +1 -0
  5. package/dist/assets/_baseMap-Cu3o-eyO.js +1 -0
  6. package/dist/assets/{_baseUniq-BKktIGQ1.js → _baseUniq-y7ZXnMo1.js} +1 -1
  7. package/dist/assets/{_createAggregator-C5CVY-0t.js → _createAggregator-ZcHkHPNJ.js} +1 -1
  8. package/dist/assets/{agent-panel-RGLNjkYe.js → agent-panel-B91RoLct.js} +6 -6
  9. package/dist/assets/{any-language-editor-DjuXwGCA.js → any-language-editor-CxfHcm5h.js} +1 -1
  10. package/dist/assets/{architectureDiagram-W76B3OCA-Dyj4ds_R.js → architectureDiagram-W76B3OCA-BQsvK8uR.js} +1 -1
  11. package/dist/assets/{between-horizontal-start-Dt2aKpPf.js → between-horizontal-start-BmYToIaM.js} +1 -1
  12. package/dist/assets/{blockDiagram-QIGZ2CNN-o-i7DDvN.js → blockDiagram-QIGZ2CNN-r3HgCj4w.js} +1 -1
  13. package/dist/assets/{c4Diagram-FPNF74CW-DGHEwWrx.js → c4Diagram-FPNF74CW-BJbPNt41.js} +1 -1
  14. package/dist/assets/channel-DFaEx1fu.js +1 -0
  15. package/dist/assets/{chat-panel-9alr8FS4.js → chat-panel-IoPMv8e2.js} +3 -3
  16. package/dist/assets/{chunk-4BX2VUAB-BJecb-Ri.js → chunk-4BX2VUAB-Dv4MZ9Hj.js} +1 -1
  17. package/dist/assets/{chunk-55IACEB6-CAATkc4w.js → chunk-55IACEB6-CM4AHquB.js} +1 -1
  18. package/dist/assets/{chunk-FMBD7UC4-DPuNbQ-f.js → chunk-FMBD7UC4-C_Zz0ENB.js} +1 -1
  19. package/dist/assets/{chunk-K7UQS3LO-C8TWVLiH.js → chunk-K7UQS3LO-DYSmiXYq.js} +1 -1
  20. package/dist/assets/{chunk-QN33PNHL-DiZZ09q4.js → chunk-QN33PNHL-QM4OPuQP.js} +1 -1
  21. package/dist/assets/{chunk-QZHKN3VN-BIUM7usu.js → chunk-QZHKN3VN-CfAsGyeB.js} +1 -1
  22. package/dist/assets/{chunk-TVAH2DTR-vGTArPBG.js → chunk-TVAH2DTR-6j_Cpjsi.js} +1 -1
  23. package/dist/assets/{chunk-TZMSLE5B-D2KRqp_x.js → chunk-TZMSLE5B-BHslFJQE.js} +1 -1
  24. package/dist/assets/{circle-play-cjeNez0N.js → circle-play-CK3UZRYQ.js} +1 -1
  25. package/dist/assets/classDiagram-KNZD7YFC-BsZtvV5O.js +1 -0
  26. package/dist/assets/classDiagram-v2-RKCZMP56-BsZtvV5O.js +1 -0
  27. package/dist/assets/{clear-button-C97JtAez.js → clear-button-C4fDVSv8.js} +1 -1
  28. package/dist/assets/clone-YBEvPE-s.js +1 -0
  29. package/dist/assets/command-palette-D7hOfvf6.js +1 -0
  30. package/dist/assets/{common-Du9rSOwD.js → common-D-lbuUwz.js} +1 -1
  31. package/dist/assets/{compile-CZXqyOxa.js → compile-DVQe1Mzk.js} +1 -1
  32. package/dist/assets/{cose-bilkent-S5V4N54A-CqUN5Y9b.js → cose-bilkent-S5V4N54A-D-IS7WC8.js} +1 -1
  33. package/dist/assets/{dagre-5GWH7T2D-RJqTI9DM.js → dagre-5GWH7T2D-lYu-tEWT.js} +1 -1
  34. package/dist/assets/{data-grid-overlay-editor-DZN0q1LV.js → data-grid-overlay-editor-C5peOCit.js} +1 -1
  35. package/dist/assets/datasources-panel-D3NA20uZ.js +1 -0
  36. package/dist/assets/{dependency-graph-panel-CEog_O7V.js → dependency-graph-panel-BGVYOfkV.js} +1 -1
  37. package/dist/assets/{diagram-N5W7TBWH-D-l4zZ9d.js → diagram-N5W7TBWH-BnvIuYUp.js} +1 -1
  38. package/dist/assets/{diagram-QEK2KX5R-CCOmBUt-.js → diagram-QEK2KX5R-DemedRK3.js} +1 -1
  39. package/dist/assets/{diagram-S2PKOQOG-C_I_9jnZ.js → diagram-S2PKOQOG-iiY7AuyH.js} +1 -1
  40. package/dist/assets/{documentation-panel-C1BtMZ3M.js → documentation-panel-C3dSwOSQ.js} +1 -1
  41. package/dist/assets/{edit-page-B-oevUZ9.js → edit-page-C5TsEeSo.js} +17 -17
  42. package/dist/assets/{ellipsis-vertical-BEb-J8z6.js → ellipsis-vertical-CazJl8M7.js} +1 -1
  43. package/dist/assets/{empty-state-C99UyDE3.js → empty-state-DW308mFO.js} +1 -1
  44. package/dist/assets/{erDiagram-AWTI2OKA-BePOLi5M.js → erDiagram-AWTI2OKA-6wQ8Ugg0.js} +1 -1
  45. package/dist/assets/{error-panel-Bs34jXFh.js → error-panel-D1VnJ1yP.js} +1 -1
  46. package/dist/assets/{file-explorer-panel-Ck6UL861.js → file-explorer-panel-0oVd4t-D.js} +1 -1
  47. package/dist/assets/{flowDiagram-PVAE7QVJ-BgjFu5l7.js → flowDiagram-PVAE7QVJ-C55IUWjm.js} +1 -1
  48. package/dist/assets/{ganttDiagram-OWAHRB6G-YOPb3XSV.js → ganttDiagram-OWAHRB6G-DmqCM6ME.js} +1 -1
  49. package/dist/assets/{gitGraphDiagram-NY62KEGX-CGhqaDTy.js → gitGraphDiagram-NY62KEGX-DBvhAeM_.js} +1 -1
  50. package/dist/assets/{glide-data-editor-9QUH6iso.js → glide-data-editor-CHNuHidQ.js} +11 -11
  51. package/dist/assets/{graph-DQQFGrho.js → graph-CG6BgUWQ.js} +1 -1
  52. package/dist/assets/{home-page-DRKpPCrF.js → home-page-dgivXuSR.js} +3 -3
  53. package/dist/assets/{index-SGLNXrGP.js → index-BTGpssVX.js} +1 -1
  54. package/dist/assets/{index-Aeo6WiK7.js → index-BYVZlBF8.js} +1 -1
  55. package/dist/assets/{index-CUFv_thQ.js → index-BelfnXwL.js} +1 -1
  56. package/dist/assets/{index-DdnKZNxM.js → index-BneyUujp.js} +1 -1
  57. package/dist/assets/{index-BJNCMUmG.js → index-C02SqeRj.js} +1 -1
  58. package/dist/assets/{index-aE43R74q.js → index-C7dtgr9A.js} +1 -1
  59. package/dist/assets/{index-C2MD0vgD.js → index-CAQvMTzM.js} +1 -1
  60. package/dist/assets/index-CGDMlQfO.css +1 -0
  61. package/dist/assets/index-CelXfcd8.js +580 -0
  62. package/dist/assets/{index-C_tkBKNO.js → index-Csd6QrCV.js} +1 -1
  63. package/dist/assets/{index-BAbIIxHU.js → index-CtPksxf0.js} +1 -1
  64. package/dist/assets/{index-2252nrk6.js → index-Cxyk7pt-.js} +1 -1
  65. package/dist/assets/{index-BW3k9Gss.js → index-DAZ-9ri2.js} +1 -1
  66. package/dist/assets/{index-ClzeQrN7.js → index-DONRrmA2.js} +1 -1
  67. package/dist/assets/{index-B8jXZ12t.js → index-Db36XTG_.js} +1 -1
  68. package/dist/assets/{index-BprjMYH5.js → index-DdIhdEVw.js} +1 -1
  69. package/dist/assets/{index-CFKO7WXI.js → index-M_pBKDSe.js} +1 -1
  70. package/dist/assets/{index-CfaDbEdi.js → index-_luCZMLM.js} +1 -1
  71. package/dist/assets/{index-BjgnbONl.js → index-mkubqy9-.js} +1 -1
  72. package/dist/assets/{index-C1ez98sk.js → index-sbO9UaUU.js} +1 -1
  73. package/dist/assets/{index-G5QZppK2.js → index-z4krxQ4j.js} +1 -1
  74. package/dist/assets/infoDiagram-STP46IZ2-wTALjfPc.js +2 -0
  75. package/dist/assets/{isEmpty-D-4c7sMv.js → isEmpty-CqX_YTIf.js} +1 -1
  76. package/dist/assets/{journeyDiagram-BIP6EPQ6-C94u3Mv3.js → journeyDiagram-BIP6EPQ6-Y5w_Tqe_.js} +1 -1
  77. package/dist/assets/{kanban-definition-6OIFK2YF-BEXYFzz7.js → kanban-definition-6OIFK2YF-DbXs5Rxi.js} +1 -1
  78. package/dist/assets/{layout-Bz2BJ2ru.js → layout-BCNPDACj.js} +1 -1
  79. package/dist/assets/{linear-D8s7K76e.js → linear-uO6UVhXt.js} +1 -1
  80. package/dist/assets/{links-BpXlz1GG.js → links-Drv7cJgN.js} +3 -3
  81. package/dist/assets/{logs-panel-DC7wpmPz.js → logs-panel-BEQ1eRUp.js} +1 -1
  82. package/dist/assets/{markdown-renderer-DRdSWR9X.js → markdown-renderer-Dmzbb00W.js} +3 -3
  83. package/dist/assets/{mermaid-Y3x4hmD0.js → mermaid-qRc4MXIj.js} +1 -1
  84. package/dist/assets/{mermaid.core-DzthE35Y.js → mermaid.core-CvvJtCRj.js} +4 -4
  85. package/dist/assets/min-DYUOb1RR.js +1 -0
  86. package/dist/assets/{mindmap-definition-Q6HEUPPD-DktvuLe1.js → mindmap-definition-Q6HEUPPD-G5NognM-.js} +1 -1
  87. package/dist/assets/{number-overlay-editor-BEfwI1IT.js → number-overlay-editor-DPr5sHFu.js} +1 -1
  88. package/dist/assets/{outline-panel-CdsnAy2w.js → outline-panel-gxQXvVi4.js} +1 -1
  89. package/dist/assets/{packages-panel-DiTA-d_D.js → packages-panel-B1T0VPlg.js} +1 -1
  90. package/dist/assets/{pieDiagram-ADFJNKIX-DQDNQ-de.js → pieDiagram-ADFJNKIX-DK9SHkfc.js} +1 -1
  91. package/dist/assets/{quadrantDiagram-LMRXKWRM-0kgIXc2-.js → quadrantDiagram-LMRXKWRM-D1DdWF8C.js} +1 -1
  92. package/dist/assets/{react-plotly-DJqqfM7c.js → react-plotly-CTwajqCb.js} +1 -1
  93. package/dist/assets/{requirementDiagram-4UW4RH46-B5rb0ypd.js → requirementDiagram-4UW4RH46-DnjDAypr.js} +1 -1
  94. package/dist/assets/{run-page-CFmLrv1R.js → run-page-CQY9im22.js} +1 -1
  95. package/dist/assets/{sankeyDiagram-GR3RE2ED-Dom7IlnF.js → sankeyDiagram-GR3RE2ED-B67Va-ER.js} +1 -1
  96. package/dist/assets/{scratchpad-panel-CuHWpHO8.js → scratchpad-panel-DlDfcDtW.js} +1 -1
  97. package/dist/assets/{secrets-panel-CfHc5YD0.js → secrets-panel-BDGyuGZA.js} +1 -1
  98. package/dist/assets/{sequenceDiagram-C3RYC4MD-PNJWXQbw.js → sequenceDiagram-C3RYC4MD-DiWgZPtN.js} +1 -1
  99. package/dist/assets/{slides-component-CJgaTRZ0.js → slides-component-DhpPRtQp.js} +1 -1
  100. package/dist/assets/{snippets-panel-B2EC1txM.js → snippets-panel-CLkBXhJ2.js} +1 -1
  101. package/dist/assets/{sortBy-DZnlX29-.js → sortBy-D4OG7w4O.js} +1 -1
  102. package/dist/assets/{state-CWict9RU.js → state-Dz_3JyED.js} +1 -1
  103. package/dist/assets/{stateDiagram-KXAO66HF-BE58aJnr.js → stateDiagram-KXAO66HF-ByF2AULw.js} +1 -1
  104. package/dist/assets/stateDiagram-v2-UMBNRL4Z-CtBJqosP.js +1 -0
  105. package/dist/assets/{storage-DRaR04wR.js → storage-Dr0CC44z.js} +6 -6
  106. package/dist/assets/{terminal-BX3Su5q7.js → terminal-BtdissBf.js} +1 -1
  107. package/dist/assets/{time-hUzZfpNE.js → time-DKdOTnQg.js} +1 -1
  108. package/dist/assets/{timeline-definition-XQNQX7LJ-CqQP9t51.js → timeline-definition-XQNQX7LJ-DzER9bf6.js} +1 -1
  109. package/dist/assets/{tracing-B10Q1n-L.js → tracing-Dpx5M-u3.js} +2 -2
  110. package/dist/assets/{tracing-panel-Du8WCnno.js → tracing-panel-hCjBkSER.js} +2 -2
  111. package/dist/assets/{trash-B81GTiv6.js → trash-C6Ko-g5q.js} +1 -1
  112. package/dist/assets/{tree-6vW2ogkh.js → tree-BHN2gcCF.js} +6 -6
  113. package/dist/assets/{treemap-75Q7IDZK-CdwDwwsz.js → treemap-75Q7IDZK-DR79Mhzt.js} +1 -1
  114. package/dist/assets/{variable-panel-D5qgJI7k.js → variable-panel-PFBCFz36.js} +1 -1
  115. package/dist/assets/{vega-component-DJaJWMJM.js → vega-component-Db6-uY4C.js} +1 -1
  116. package/dist/assets/{xychartDiagram-6GGTOJPD-WFtXqaM9.js → xychartDiagram-6GGTOJPD-DWzBP3tZ.js} +1 -1
  117. package/dist/index.html +2 -2
  118. package/package.json +3 -3
  119. package/src/__mocks__/requests.ts +1 -0
  120. package/src/components/data-table/__tests__/columns.test.tsx +38 -0
  121. package/src/components/data-table/cell-hover-template/feature.ts +1 -1
  122. package/src/components/data-table/cell-hover-template/types.ts +1 -1
  123. package/src/components/data-table/columns.tsx +21 -2
  124. package/src/components/data-table/renderers.tsx +16 -8
  125. package/src/components/data-table/schemas.ts +16 -0
  126. package/src/components/editor/Cell.tsx +2 -0
  127. package/src/components/editor/errors/sql-validation-errors.tsx +34 -0
  128. package/src/components/editor/output/ConsoleOutput.tsx +13 -1
  129. package/src/components/editor/output/MarimoErrorOutput.tsx +60 -1
  130. package/src/core/ai/context/providers/cell-output.ts +1 -18
  131. package/src/core/codemirror/language/__tests__/extension.test.ts +24 -0
  132. package/src/core/codemirror/language/__tests__/sql-validation.test.ts +133 -0
  133. package/src/core/codemirror/language/languages/sql/sql-mode.ts +20 -0
  134. package/src/core/codemirror/language/languages/sql/sql.ts +90 -3
  135. package/src/core/codemirror/language/languages/sql/validation-errors.ts +79 -0
  136. package/src/core/codemirror/language/panel/panel.tsx +8 -2
  137. package/src/core/codemirror/language/panel/sql.tsx +81 -4
  138. package/src/core/config/feature-flag.tsx +3 -1
  139. package/src/core/datasets/request-registry.ts +17 -1
  140. package/src/core/islands/bridge.ts +1 -0
  141. package/src/core/islands/main.ts +1 -0
  142. package/src/core/kernel/messages.ts +1 -0
  143. package/src/core/network/requests-network.ts +7 -0
  144. package/src/core/network/requests-static.ts +1 -0
  145. package/src/core/network/requests-toasting.ts +1 -0
  146. package/src/core/network/types.ts +2 -0
  147. package/src/core/wasm/bridge.ts +5 -0
  148. package/src/core/websocket/useMarimoWebSocket.tsx +4 -0
  149. package/src/plugins/core/registerReactComponent.tsx +23 -19
  150. package/src/plugins/impl/DataTablePlugin.tsx +11 -4
  151. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +17 -5
  152. package/src/stories/dataframe.stories.tsx +2 -0
  153. package/src/utils/__tests__/dom.test.ts +167 -0
  154. package/src/utils/dom.ts +55 -0
  155. package/dist/assets/_baseEach-CvTX9w0Y.js +0 -1
  156. package/dist/assets/_baseMap-CtlwA90f.js +0 -1
  157. package/dist/assets/channel-Co6iMgWq.js +0 -1
  158. package/dist/assets/classDiagram-KNZD7YFC-BbJ0rY3y.js +0 -1
  159. package/dist/assets/classDiagram-v2-RKCZMP56-BbJ0rY3y.js +0 -1
  160. package/dist/assets/clone-BMP0PsTa.js +0 -1
  161. package/dist/assets/command-palette-B93Pjcky.js +0 -1
  162. package/dist/assets/datasources-panel-v7H3cR0p.js +0 -1
  163. package/dist/assets/index-C7CoaNFb.js +0 -578
  164. package/dist/assets/index-DadI618h.css +0 -1
  165. package/dist/assets/infoDiagram-STP46IZ2-CJLOpSAf.js +0 -2
  166. package/dist/assets/min-BBO3-1Hg.js +0 -1
  167. package/dist/assets/stateDiagram-v2-UMBNRL4Z-CdThjimL.js +0 -1
@@ -1,9 +1,16 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
+ import type { SelectTriggerProps } from "@radix-ui/react-select";
3
4
  import { useAtomValue } from "jotai";
4
- import { AlertCircle, CircleHelpIcon } from "lucide-react";
5
+ import {
6
+ AlertCircle,
7
+ CircleHelpIcon,
8
+ DatabaseBackup,
9
+ SearchCheck,
10
+ } from "lucide-react";
5
11
  import { transformDisplayName } from "@/components/databases/display";
6
12
  import { DatabaseLogo } from "@/components/databases/icon";
13
+ import { Button } from "@/components/ui/button";
7
14
  import {
8
15
  Select,
9
16
  SelectContent,
@@ -14,6 +21,7 @@ import {
14
21
  SelectTrigger,
15
22
  SelectValue,
16
23
  } from "@/components/ui/select";
24
+ import { Tooltip } from "@/components/ui/tooltip";
17
25
  import {
18
26
  dataConnectionsMapAtom,
19
27
  setLatestEngineSelected,
@@ -24,6 +32,7 @@ import {
24
32
  } from "@/core/datasets/engines";
25
33
  import type { DataSourceConnection } from "@/core/kernel/messages";
26
34
  import { useNonce } from "@/hooks/useNonce";
35
+ import { type SQLMode, useSQLMode } from "../languages/sql/sql-mode";
27
36
 
28
37
  interface SelectProps {
29
38
  selectedEngine: ConnectionName;
@@ -77,7 +86,7 @@ export const SQLEngineSelect: React.FC<SelectProps> = ({
77
86
  <SelectItem key={connection.name} value={connection.name}>
78
87
  <div className="flex items-center gap-1">
79
88
  <DatabaseLogo className="h-3 w-3" name={connection.dialect} />
80
- <span className="truncate">
89
+ <span className="truncate ml-0.5">
81
90
  {transformDisplayName(connection.display_name)}
82
91
  </span>
83
92
  </div>
@@ -88,9 +97,9 @@ export const SQLEngineSelect: React.FC<SelectProps> = ({
88
97
  return (
89
98
  <div className="flex flex-row gap-1 items-center">
90
99
  <Select value={selectedEngine} onValueChange={handleSelectEngine}>
91
- <SelectTrigger className="text-xs border-border shadow-none! ring-0! h-4.5 px-1.5">
100
+ <SQLSelectTrigger>
92
101
  <SelectValue placeholder="Select an engine" />
93
- </SelectTrigger>
102
+ </SQLSelectTrigger>
94
103
  <SelectContent>
95
104
  <SelectGroup>
96
105
  <SelectLabel>Database connections</SelectLabel>
@@ -130,3 +139,71 @@ export const SQLEngineSelect: React.FC<SelectProps> = ({
130
139
  const HELP_KEY = "__help__";
131
140
  const HELP_URL =
132
141
  "http://docs.marimo.io/guides/working_with_data/sql/#connecting-to-a-custom-database";
142
+
143
+ export const SQLModeSelect: React.FC = () => {
144
+ const { sqlMode, setSQLMode } = useSQLMode();
145
+
146
+ const handleToggleMode = () => {
147
+ setSQLMode(sqlMode === "validate" ? "default" : "validate");
148
+ };
149
+
150
+ const getModeIcon = (mode: SQLMode) => {
151
+ return mode === "validate" ? (
152
+ <SearchCheck className="h-3 w-3" />
153
+ ) : (
154
+ <DatabaseBackup className="h-3 w-3" />
155
+ );
156
+ };
157
+
158
+ const getTooltipContent = (mode: SQLMode) => {
159
+ return mode === "validate" ? (
160
+ <div className="text-xs">
161
+ <div className="font-semibold mb-1 flex flex-row items-center gap-1">
162
+ <SearchCheck className="h-3 w-3" />
163
+ Validate Mode
164
+ </div>
165
+ <p>Queries are validated as you write them</p>
166
+ </div>
167
+ ) : (
168
+ <div className="text-xs">
169
+ <div className="font-semibold mb-1 flex flex-row items-center gap-1">
170
+ <DatabaseBackup className="h-3 w-3" />
171
+ Default Mode
172
+ </div>
173
+ <p>Standard editing</p>
174
+ </div>
175
+ );
176
+ };
177
+
178
+ return (
179
+ <div className="flex flex-row gap-1 items-center">
180
+ <Tooltip delayDuration={300} content={getTooltipContent(sqlMode)}>
181
+ <Button
182
+ variant="ghost"
183
+ size="sm"
184
+ onClick={handleToggleMode}
185
+ className="h-5 px-1.5 text-xs border-border shadow-none hover:bg-accent"
186
+ >
187
+ {getModeIcon(sqlMode)}
188
+ <span className="ml-1">
189
+ {sqlMode === "validate" ? "Validate" : "Default"}
190
+ </span>
191
+ </Button>
192
+ </Tooltip>
193
+ </div>
194
+ );
195
+ };
196
+
197
+ const SQLSelectTrigger: React.FC<SelectTriggerProps> = ({
198
+ children,
199
+ ...props
200
+ }) => {
201
+ return (
202
+ <SelectTrigger
203
+ className="text-xs border-border shadow-none! ring-0! h-5 px-1.5 hover:bg-accent transition-colors"
204
+ {...props}
205
+ >
206
+ {children}
207
+ </SelectTrigger>
208
+ );
209
+ };
@@ -14,6 +14,7 @@ export interface ExperimentalFeatures {
14
14
  mcp_docs: boolean;
15
15
  sql_linter: boolean;
16
16
  external_agents: boolean;
17
+ sql_mode: boolean;
17
18
  // Add new feature flags here
18
19
  }
19
20
 
@@ -26,13 +27,14 @@ const defaultValues: ExperimentalFeatures = {
26
27
  mcp_docs: false,
27
28
  sql_linter: false,
28
29
  external_agents: import.meta.env.DEV,
30
+ sql_mode: false,
29
31
  };
30
32
 
31
33
  export function getFeatureFlag<T extends keyof ExperimentalFeatures>(
32
34
  feature: T,
33
35
  ): ExperimentalFeatures[T] {
34
36
  return (
35
- (getResolvedMarimoConfig().experimental?.[
37
+ (getResolvedMarimoConfig()?.experimental?.[
36
38
  feature
37
39
  ] as ExperimentalFeatures[T]) ?? defaultValues[feature]
38
40
  );
@@ -1,10 +1,15 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
- import type { SQLTableListPreview, SQLTablePreview } from "../kernel/messages";
2
+ import type {
3
+ SQLTableListPreview,
4
+ SQLTablePreview,
5
+ ValidateSQLResult,
6
+ } from "../kernel/messages";
3
7
  import { DeferredRequestRegistry } from "../network/DeferredRequestRegistry";
4
8
  import { getRequestClient } from "../network/requests";
5
9
  import type {
6
10
  PreviewSQLTableListRequest,
7
11
  PreviewSQLTableRequest,
12
+ ValidateSQLRequest,
8
13
  } from "../network/types";
9
14
 
10
15
  // We make a request to the backend to preview the table, passing in Engine, DB, Schema, and Table
@@ -32,3 +37,14 @@ export const PreviewSQLTableList = new DeferredRequestRegistry<
32
37
  ...req,
33
38
  });
34
39
  });
40
+
41
+ export const ValidateSQL = new DeferredRequestRegistry<
42
+ Omit<ValidateSQLRequest, "requestId">,
43
+ ValidateSQLResult
44
+ >("validate-sql", async (requestId, req) => {
45
+ const client = getRequestClient();
46
+ await client.validateSQL({
47
+ requestId: requestId,
48
+ ...req,
49
+ });
50
+ });
@@ -153,6 +153,7 @@ export class IslandsPyodideBridge implements RunRequests, EditRequests {
153
153
  previewSQLTable = throwNotImplemented;
154
154
  previewSQLTableList = throwNotImplemented;
155
155
  previewDataSourceConnection = throwNotImplemented;
156
+ validateSQL = throwNotImplemented;
156
157
  openFile = throwNotImplemented;
157
158
  sendListFiles = throwNotImplemented;
158
159
  sendSearchFiles = throwNotImplemented;
@@ -119,6 +119,7 @@ export async function initialize() {
119
119
  case "sql-table-list-preview":
120
120
  case "datasets":
121
121
  case "data-source-connections":
122
+ case "validate-sql-result":
122
123
  case "secret-keys-result":
123
124
  case "startup-logs":
124
125
  // Unsupported
@@ -38,6 +38,7 @@ export type DataColumnPreview = OperationMessageData<"data-column-preview">;
38
38
  export type SQLTablePreview = OperationMessageData<"sql-table-preview">;
39
39
  export type SQLTableListPreview =
40
40
  OperationMessageData<"sql-table-list-preview">;
41
+ export type ValidateSQLResult = OperationMessageData<"validate-sql-result">;
41
42
  export type SecretKeysResult = OperationMessageData<"secret-keys-result">;
42
43
  export type StartupLogs = OperationMessageData<"startup-logs">;
43
44
  export type CellMessage = OperationMessageData<"cell-op">;
@@ -207,6 +207,13 @@ export function createNetworkRequests(): EditRequests & RunRequests {
207
207
  })
208
208
  .then(handleResponseReturnNull);
209
209
  },
210
+ validateSQL: (request) => {
211
+ return getClient()
212
+ .POST("/api/sql/validate", {
213
+ body: request,
214
+ })
215
+ .then(handleResponseReturnNull);
216
+ },
210
217
  openFile: async (request) => {
211
218
  await waitForConnectionOpen();
212
219
  await getClient()
@@ -57,6 +57,7 @@ export function createStaticRequests(): EditRequests & RunRequests {
57
57
  previewSQLTable: throwNotInEditMode,
58
58
  previewSQLTableList: throwNotInEditMode,
59
59
  previewDataSourceConnection: throwNotInEditMode,
60
+ validateSQL: throwNotInEditMode,
60
61
  openFile: throwNotInEditMode,
61
62
  getUsageStats: throwNotInEditMode,
62
63
  sendListFiles: throwNotInEditMode,
@@ -35,6 +35,7 @@ export function createErrorToastingRequests(
35
35
  previewSQLTable: "Failed to fetch SQL table",
36
36
  previewSQLTableList: "Failed to fetch SQL table list",
37
37
  previewDataSourceConnection: "Failed to preview data source connection",
38
+ validateSQL: "Failed to validate SQL",
38
39
  openFile: "Failed to open file",
39
40
  getUsageStats: "", // No toast
40
41
  sendListFiles: "Failed to list files",
@@ -62,6 +62,7 @@ export type PreviewSQLTableRequest = schemas["PreviewSQLTableRequest"];
62
62
  export type PreviewSQLTableListRequest = schemas["PreviewSQLTableListRequest"];
63
63
  export type PreviewDataSourceConnectionRequest =
64
64
  schemas["PreviewDataSourceConnectionRequest"];
65
+ export type ValidateSQLRequest = schemas["ValidateSQLRequest"];
65
66
  export type PdbRequest = schemas["PdbRequest"];
66
67
  export type ReadCodeResponse = schemas["ReadCodeResponse"];
67
68
  export type RecentFilesResponse = schemas["RecentFilesResponse"];
@@ -140,6 +141,7 @@ export interface EditRequests {
140
141
  previewDataSourceConnection: (
141
142
  request: PreviewDataSourceConnectionRequest,
142
143
  ) => Promise<null>;
144
+ validateSQL: (request: ValidateSQLRequest) => Promise<null>;
143
145
  openFile: (request: { path: string }) => Promise<null>;
144
146
  getUsageStats: () => Promise<UsageResponse>;
145
147
  // Debugger
@@ -496,6 +496,11 @@ export class PyodideBridge implements RunRequests, EditRequests {
496
496
  return null;
497
497
  };
498
498
 
499
+ validateSQL: EditRequests["validateSQL"] = async (request) => {
500
+ await this.putControlRequest(request);
501
+ return null;
502
+ };
503
+
499
504
  sendModelValue: RunRequests["sendModelValue"] = async (request) => {
500
505
  await this.putControlRequest(request);
501
506
  return null;
@@ -32,6 +32,7 @@ import type { ConnectionName } from "../datasets/engines";
32
32
  import {
33
33
  PreviewSQLTable,
34
34
  PreviewSQLTableList,
35
+ ValidateSQL,
35
36
  } from "../datasets/request-registry";
36
37
  import { useDatasetsActions } from "../datasets/state";
37
38
  import { UI_ELEMENT_REGISTRY } from "../dom/uiregistry";
@@ -238,6 +239,9 @@ export function useMarimoWebSocket(opts: {
238
239
  case "sql-table-list-preview":
239
240
  PreviewSQLTableList.resolve(msg.data.request_id as RequestId, msg.data);
240
241
  return;
242
+ case "validate-sql-result":
243
+ ValidateSQL.resolve(msg.data.request_id as RequestId, msg.data);
244
+ return;
241
245
  case "secret-keys-result":
242
246
  SECRETS_REGISTRY.resolve(msg.data.request_id as RequestId, msg.data);
243
247
  return;
@@ -24,7 +24,7 @@ import ReactDOM, { type Root } from "react-dom/client";
24
24
  import useEvent from "react-use-event-hook";
25
25
  import type { ZodSchema } from "zod";
26
26
  import { notebookAtom } from "@/core/cells/cells.ts";
27
- import { findCellId } from "@/core/cells/ids.ts";
27
+ import { HTMLCellId } from "@/core/cells/ids.ts";
28
28
  import { isUninstantiated } from "@/core/cells/utils";
29
29
  import { createInputEvent, MarimoValueUpdateEvent } from "@/core/dom/events";
30
30
  import { getUIElementObjectId } from "@/core/dom/ui-element";
@@ -182,24 +182,28 @@ function PluginSlotInternal<T>(
182
182
  const objectId = getUIElementObjectId(hostElement);
183
183
  invariant(objectId, "Object ID should exist");
184
184
 
185
- const cellId = findCellId(hostElement);
186
- invariant(cellId, "Cell ID should exist");
187
-
188
- const notebookState = store.get(notebookAtom);
189
- const cellRuntime = notebookState.cellRuntime[cellId];
190
- const cellData = notebookState.cellData[cellId];
191
-
192
- const cellNotInitialized = isUninstantiated({
193
- executionTime:
194
- cellRuntime.runElapsedTimeMs ?? cellData.lastExecutionTime,
195
- status: cellRuntime.status,
196
- errored: cellRuntime.errored,
197
- interrupted: cellRuntime.interrupted,
198
- stopped: cellRuntime.stopped,
199
- });
200
-
201
- if (cellNotInitialized) {
202
- throw new CellNotInitializedError();
185
+ const htmlId = HTMLCellId.findElementThroughShadowDOMs(hostElement)?.id;
186
+ const cellId = htmlId ? HTMLCellId.parse(htmlId) : null;
187
+ if (cellId) {
188
+ // If the cell is not initialized, throw an error
189
+ const notebookState = store.get(notebookAtom);
190
+ const cellRuntime = notebookState.cellRuntime[cellId];
191
+ const cellData = notebookState.cellData[cellId];
192
+
193
+ const cellNotInitialized = isUninstantiated({
194
+ executionTime:
195
+ cellRuntime.runElapsedTimeMs ?? cellData.lastExecutionTime,
196
+ status: cellRuntime.status,
197
+ errored: cellRuntime.errored,
198
+ interrupted: cellRuntime.interrupted,
199
+ stopped: cellRuntime.stopped,
200
+ });
201
+
202
+ if (cellNotInitialized) {
203
+ throw new CellNotInitializedError();
204
+ }
205
+ } else {
206
+ Logger.warn(`Cell ID ${cellId} cannot be found`);
203
207
  }
204
208
 
205
209
  const response = await FUNCTIONS_REGISTRY.request({
@@ -36,6 +36,10 @@ import {
36
36
  import { usePanelOwnership } from "@/components/data-table/hooks/use-panel-ownership";
37
37
  import { LoadingTable } from "@/components/data-table/loading-table";
38
38
  import { RowViewerPanel } from "@/components/data-table/row-viewer-panel/row-viewer";
39
+ import {
40
+ type DownloadAsArgs,
41
+ DownloadAsSchema,
42
+ } from "@/components/data-table/schemas";
39
43
  import {
40
44
  type BinValues,
41
45
  type ColumnHeaderStats,
@@ -182,6 +186,7 @@ interface Data<T> {
182
186
  freezeColumnsRight?: string[];
183
187
  textJustifyColumns?: Record<string, "left" | "center" | "right">;
184
188
  wrappedColumns?: string[];
189
+ headerTooltip?: Record<string, string>;
185
190
  totalColumns: number;
186
191
  maxColumns: number | "all";
187
192
  hasStableRowId: boolean;
@@ -190,7 +195,7 @@ interface Data<T> {
190
195
 
191
196
  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
192
197
  type DataTableFunctions = {
193
- download_as: (req: { format: "csv" | "json" | "parquet" }) => Promise<string>;
198
+ download_as: DownloadAsArgs;
194
199
  get_column_summaries: <T>(
195
200
  opts: ColumnSummariesArgs,
196
201
  ) => Promise<ColumnSummaries<T>>;
@@ -249,6 +254,7 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
249
254
  .record(z.enum(["left", "center", "right"]))
250
255
  .optional(),
251
256
  wrappedColumns: z.array(z.string()).optional(),
257
+ headerTooltip: z.record(z.string()).optional(),
252
258
  fieldTypes: columnToFieldTypesSchema.nullish(),
253
259
  totalColumns: z.number(),
254
260
  maxColumns: z.union([z.number(), z.literal("all")]).default("all"),
@@ -263,9 +269,7 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
263
269
  }),
264
270
  )
265
271
  .withFunctions<DataTableFunctions>({
266
- download_as: rpc
267
- .input(z.object({ format: z.enum(["csv", "json", "parquet"]) }))
268
- .output(z.string()),
272
+ download_as: DownloadAsSchema,
269
273
  get_column_summaries: rpc
270
274
  .input(z.object({ precompute: z.boolean() }))
271
275
  .output(
@@ -706,6 +710,7 @@ const DataTableComponent = ({
706
710
  freezeColumnsRight,
707
711
  textJustifyColumns,
708
712
  wrappedColumns,
713
+ headerTooltip,
709
714
  totalColumns,
710
715
  get_row_ids,
711
716
  cellStyles,
@@ -779,6 +784,7 @@ const DataTableComponent = ({
779
784
  fieldTypes: memoizedClampedFieldTypes,
780
785
  textJustifyColumns: memoizedTextJustifyColumns,
781
786
  wrappedColumns: memoizedWrappedColumns,
787
+ headerTooltip: headerTooltip,
782
788
  // Only show data types if they are explicitly set
783
789
  showDataTypes: showDataTypes,
784
790
  calculateTopKRows: calculate_top_k_rows,
@@ -791,6 +797,7 @@ const DataTableComponent = ({
791
797
  memoizedClampedFieldTypes,
792
798
  memoizedTextJustifyColumns,
793
799
  memoizedWrappedColumns,
800
+ headerTooltip,
794
801
  calculate_top_k_rows,
795
802
  ],
796
803
  );
@@ -4,6 +4,10 @@ import { isEqual } from "lodash-es";
4
4
  import { Code2Icon, DatabaseIcon, FunctionSquareIcon } from "lucide-react";
5
5
  import { type JSX, memo, useEffect, useRef, useState } from "react";
6
6
  import { z } from "zod";
7
+ import {
8
+ type DownloadAsArgs,
9
+ DownloadAsSchema,
10
+ } from "@/components/data-table/schemas";
7
11
  import type { FieldTypesWithExternalType } from "@/components/data-table/types";
8
12
  import { ReadonlyCode } from "@/components/editor/code/readonly-python-code";
9
13
  import { Spinner } from "@/components/icons/spinner";
@@ -38,6 +42,7 @@ interface Data {
38
42
  label?: string | null;
39
43
  columns: ColumnDataTypes;
40
44
  pageSize: number;
45
+ showDownload: boolean;
41
46
  }
42
47
 
43
48
  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
@@ -67,6 +72,7 @@ type PluginFunctions = {
67
72
  data: TableData<T>;
68
73
  total_rows: number;
69
74
  }>;
75
+ download_as: DownloadAsArgs;
70
76
  };
71
77
 
72
78
  // Value is selection, but it is not currently exposed to the user
@@ -77,13 +83,14 @@ export const DataFramePlugin = createPlugin<S>("marimo-dataframe")
77
83
  z.object({
78
84
  label: z.string().nullish(),
79
85
  pageSize: z.number().default(5),
86
+ showDownload: z.boolean().default(true),
80
87
  columns: z
81
88
  .array(z.tuple([z.string().or(z.number()), z.string(), z.string()]))
82
89
  .transform((value) => {
83
90
  const map = new Map<ColumnId, string>();
84
- value.forEach(([key, dataType]) =>
85
- map.set(key as ColumnId, dataType as DataType),
86
- );
91
+ value.forEach(([key, dataType]) => {
92
+ map.set(key as ColumnId, dataType as DataType);
93
+ });
87
94
  return map;
88
95
  }),
89
96
  }),
@@ -124,6 +131,7 @@ export const DataFramePlugin = createPlugin<S>("marimo-dataframe")
124
131
  total_rows: z.number(),
125
132
  }),
126
133
  ),
134
+ download_as: DownloadAsSchema,
127
135
  })
128
136
  .renderer((props) => (
129
137
  <TableProviders>
@@ -141,6 +149,8 @@ interface DataTableProps extends Data, PluginFunctions {
141
149
  value: S;
142
150
  setValue: (value: S) => void;
143
151
  host: HTMLElement;
152
+ showDownload: boolean;
153
+ download_as: DownloadAsArgs;
144
154
  }
145
155
 
146
156
  const EMPTY: Transformations = {
@@ -151,11 +161,13 @@ export const DataFrameComponent = memo(
151
161
  ({
152
162
  columns,
153
163
  pageSize,
164
+ showDownload,
154
165
  value,
155
166
  setValue,
156
167
  get_dataframe,
157
168
  get_column_values,
158
169
  search,
170
+ download_as,
159
171
  host,
160
172
  }: DataTableProps): JSX.Element => {
161
173
  const { data, error, isPending } = useAsyncData(
@@ -270,8 +282,8 @@ export const DataFrameComponent = memo(
270
282
  pagination={true}
271
283
  fieldTypes={field_types}
272
284
  rowHeaders={row_headers || Arrays.EMPTY}
273
- showDownload={false}
274
- download_as={Functions.THROW}
285
+ showDownload={showDownload}
286
+ download_as={download_as}
275
287
  enableSearch={false}
276
288
  showFilters={false}
277
289
  search={search}
@@ -40,6 +40,8 @@ export const DataFrame: StoryObj = {
40
40
  get_dataframe={() => Promise.reject(new Error("not implemented"))}
41
41
  search={Functions.THROW}
42
42
  host={document.body}
43
+ showDownload={false}
44
+ download_as={async () => ""}
43
45
  />
44
46
  );
45
47
  },