@marimo-team/frontend 0.25.3-dev95 → 0.25.3-dev97

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 (118) hide show
  1. package/dist/assets/{CellStatus-CplYFNTy.js → CellStatus-fK4n2NPc.js} +1 -1
  2. package/dist/assets/{ErrorBoundary-CdGuz4a_.js → ErrorBoundary-CAEGdoFW.js} +2 -2
  3. package/dist/assets/{JsonOutput-Csbov8Fx.js → JsonOutput-CyAKmZDD.js} +1 -1
  4. package/dist/assets/{MarimoErrorOutput-czclLlOn.js → MarimoErrorOutput-CgZvP0p0.js} +1 -1
  5. package/dist/assets/{RenderHTML-CNDedpS4.js → RenderHTML-DQvxDDNS.js} +1 -1
  6. package/dist/assets/{RunButton-CRfdOxVd.js → RunButton-BBDnDL6G.js} +1 -1
  7. package/dist/assets/{add-cell-with-ai-BAIX7r0P.js → add-cell-with-ai-Clg5FELx.js} +2 -2
  8. package/dist/assets/{add-connection-dialog-DrDuM-tI.js → add-connection-dialog-BqdWmD-7.js} +1 -1
  9. package/dist/assets/{agent-panel-CoXq_rlA.js → agent-panel-5RxG5Y82.js} +3 -3
  10. package/dist/assets/{ai-model-dropdown-COvWn46z.js → ai-model-dropdown-Bvl1ktQq.js} +2 -2
  11. package/dist/assets/{app-config-button-CtxoYG4F.js → app-config-button-0QTeQ2Mv.js} +1 -1
  12. package/dist/assets/{blob-DCjrjZKW.js → blob-5VcB8K_t.js} +1 -1
  13. package/dist/assets/{cache-panel-CbWZcBu0.js → cache-panel-BV-c7ed0.js} +1 -1
  14. package/dist/assets/{cell-editor-BnL0Sorl.js → cell-editor-Byw5y-oI.js} +2 -2
  15. package/dist/assets/{cell-link-BFbgKiRH.js → cell-link-C5yYyzuF.js} +1 -1
  16. package/dist/assets/{cells-yOs72eZm.js → cells-D_11MjVB.js} +13 -13
  17. package/dist/assets/{chat-display-DwRM41t_.js → chat-display-BCR_ySiV.js} +1 -1
  18. package/dist/assets/{chat-panel-DeTEdjxF.js → chat-panel-RPRvBsaU.js} +2 -2
  19. package/dist/assets/{chat-ui-D6XucD07.js → chat-ui-CGviVaRA.js} +3 -3
  20. package/dist/assets/column-preview-xe533-Cn.js +1 -0
  21. package/dist/assets/{command-palette-CA6yfNLb.js → command-palette-BB9bWdWp.js} +1 -1
  22. package/dist/assets/{common-B-ZYnO1x.js → common-CaeItbcI.js} +1 -1
  23. package/dist/assets/{components-vu2jLG08.js → components-G6frbw0c.js} +1 -1
  24. package/dist/assets/{datasource-BakEOWsf.js → datasource-deuXnlU-.js} +1 -1
  25. package/dist/assets/{dependency-graph-panel-DCGB3_id.js → dependency-graph-panel-CIykLnJe.js} +2 -2
  26. package/dist/assets/{documentation-panel-CpGZ2Dfn.js → documentation-panel-2PQ8kQLa.js} +1 -1
  27. package/dist/assets/{download-CV1k4XQ4.js → download-dK52bLxZ.js} +1 -1
  28. package/dist/assets/{edit-page-By7n2M84.js → edit-page-C2phWLKH.js} +6 -6
  29. package/dist/assets/{error-entries-JgrZYMbe.js → error-entries-3GVk2KjS.js} +1 -1
  30. package/dist/assets/{error-panel-DS7HWph-.js → error-panel-CzDRGPeC.js} +1 -1
  31. package/dist/assets/{feedback-button-eYsTCK-g.js → feedback-button-C5NgWpFg.js} +1 -1
  32. package/dist/assets/feedback-button-CONwLFpw.js +1 -0
  33. package/dist/assets/{file-explorer-panel-BTLwwwD4.js → file-explorer-panel-B50xDnTZ.js} +1 -1
  34. package/dist/assets/{file-name-input-BaR4h2Ud.js → file-name-input-DqwMSgGU.js} +1 -1
  35. package/dist/assets/{floating-outline-Drd73Quo.js → floating-outline-ChmAuvxz.js} +1 -1
  36. package/dist/assets/{gallery-page-DXUD11Rm.js → gallery-page-CiL8aMS1.js} +1 -1
  37. package/dist/assets/{glide-data-editor-DqBBoQBl.js → glide-data-editor-CqRclBkp.js} +1 -1
  38. package/dist/assets/{home-page-pgFFsmga.js → home-page-BgZVl4dP.js} +1 -1
  39. package/dist/assets/{hooks-D25kP4Sr.js → hooks-DM5n7Osw.js} +1 -1
  40. package/dist/assets/index-vCxv6WVQ.js +30 -0
  41. package/dist/assets/{layout-DLHIGZTK.js → layout-BpP8nTjb.js} +4 -4
  42. package/dist/assets/{logs-panel-D5AAD2s9.js → logs-panel-B5eVAZtH.js} +1 -1
  43. package/dist/assets/{markdown-renderer--CwVm4kJ.js → markdown-renderer-BWUzqkJM.js} +1 -1
  44. package/dist/assets/{name-cell-input-BFKZaNI5.js → name-cell-input-DXW68gRW.js} +1 -1
  45. package/dist/assets/{outline-panel-qcwRnl0R.js → outline-panel-DPxJhJ_A.js} +1 -1
  46. package/dist/assets/{packages-panel-DnSryNcz.js → packages-panel-gBE2FWQr.js} +1 -1
  47. package/dist/assets/{panel-accordion-state-BwJpDMVt.js → panel-accordion-state-CCF5Gxnc.js} +1 -1
  48. package/dist/assets/{panels-Aqzwb3UR.js → panels-BQYMF6p_.js} +1 -1
  49. package/dist/assets/{process-output-DuUfHYSr.js → process-output-Cw9IDcKQ.js} +1 -1
  50. package/dist/assets/{readonly-code-display-BY-9I_Yk.js → readonly-code-display-DYcZQ28T.js} +1 -1
  51. package/dist/assets/{readonly-python-code-DSyr-yE2.js → readonly-python-code-RFuQP50v.js} +1 -1
  52. package/dist/assets/{reveal-component-C_Lb7Mom.js → reveal-component-yRrDmCQz.js} +2 -2
  53. package/dist/assets/{run-page-ZTw-kCU-.js → run-page-CY65WUOu.js} +1 -1
  54. package/dist/assets/{runtime-details-state-B7gpnuXx.js → runtime-details-state-pobtu6TO.js} +1 -1
  55. package/dist/assets/scratchpad-panel-H44_pvb_.js +1 -0
  56. package/dist/assets/session-panel-Bnd1iXkO.js +1 -0
  57. package/dist/assets/{snippets-panel-BNadUJLU.js → snippets-panel-DKApxKx9.js} +1 -1
  58. package/dist/assets/{state-BaYudep3.js → state-Bl9BiY_s.js} +1 -1
  59. package/dist/assets/{state-_1LTGJQM.js → state-Dv059B3A.js} +1 -1
  60. package/dist/assets/{tracing-DbTgS4u9.js → tracing-Clwy4SlW.js} +1 -1
  61. package/dist/assets/{tracing-panel-CjlrpXPy.js → tracing-panel-BwjLG_aC.js} +2 -2
  62. package/dist/assets/{tree-actions-C_KYlaSv.js → tree-actions-BZNTQ12N.js} +1 -1
  63. package/dist/assets/{useAddCell-C0J8quGE.js → useAddCell-Cjuqk2YR.js} +1 -1
  64. package/dist/assets/{useCellActionButton-CbkOj6BE.js → useCellActionButton-MoWLvuGe.js} +1 -1
  65. package/dist/assets/{useDeleteCell-DHkF5wgS.js → useDeleteCell-BC-MW7d1.js} +1 -1
  66. package/dist/assets/{useDependencyPanelTab--yXJW6X5.js → useDependencyPanelTab-CZU7i-B3.js} +1 -1
  67. package/dist/assets/{useNotebookActions-dJQalORg.js → useNotebookActions-wFxVdRXQ.js} +1 -1
  68. package/dist/assets/{useRunCells-C_IwVJR_.js → useRunCells-ByaWxoIt.js} +1 -1
  69. package/dist/assets/{useSplitCell-Ci4MEUj8.js → useSplitCell-YJIystwv.js} +1 -1
  70. package/dist/index.html +20 -20
  71. package/package.json +1 -1
  72. package/src/components/app-config/ai-config.tsx +1 -0
  73. package/src/components/chat/acp/blocks.tsx +2 -2
  74. package/src/components/chat/chat-components.tsx +2 -0
  75. package/src/components/chat/chat-panel.tsx +6 -1
  76. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  77. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  78. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  79. package/src/components/data-table/charts/charts.tsx +21 -6
  80. package/src/components/data-table/charts/storage.ts +16 -5
  81. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  82. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  83. package/src/components/datasources/__tests__/column-preview.test.tsx +63 -2
  84. package/src/components/datasources/column-preview.tsx +8 -2
  85. package/src/components/datasources/datasources.tsx +12 -14
  86. package/src/components/debugger/debugger-code.tsx +5 -0
  87. package/src/components/dependency-graph/panels.tsx +2 -1
  88. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  89. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  90. package/src/components/editor/cell/DeleteButton.tsx +1 -0
  91. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  92. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
  93. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  94. package/src/components/editor/notebook-banner.tsx +1 -0
  95. package/src/components/editor/output/ansi-reduce.ts +6 -0
  96. package/src/components/editor/output/console/ConsoleOutput.tsx +11 -7
  97. package/src/components/editor/package-alert.tsx +6 -4
  98. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  99. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  100. package/src/components/scratchpad/scratchpad.tsx +2 -2
  101. package/src/components/slides/reveal-component.tsx +4 -2
  102. package/src/components/slides/slide-form.tsx +1 -0
  103. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  104. package/src/core/cells/__tests__/cell.test.ts +39 -0
  105. package/src/core/cells/__tests__/cells.test.ts +3 -1
  106. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  107. package/src/core/cells/cell.ts +14 -3
  108. package/src/core/cells/cells.ts +1 -0
  109. package/src/core/cells/types.ts +10 -1
  110. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  111. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  112. package/src/plugins/impl/chat/chat-ui.tsx +2 -1
  113. package/src/stories/button.stories.tsx +1 -0
  114. package/dist/assets/column-preview-DXM8SqGY.js +0 -1
  115. package/dist/assets/feedback-button-Dgs_HOOC.js +0 -1
  116. package/dist/assets/index-DNG4qQ-E.js +0 -30
  117. package/dist/assets/scratchpad-panel-D2IHkKoG.js +0 -1
  118. package/dist/assets/session-panel-cOSO3WCX.js +0 -1
package/dist/index.html CHANGED
@@ -66,7 +66,7 @@
66
66
  <marimo-server-token data-token="{{ server_token }}" hidden></marimo-server-token>
67
67
  <!-- /TODO -->
68
68
  <title>{{ title }}</title>
69
- <script type="module" crossorigin src="./assets/index-DNG4qQ-E.js"></script>
69
+ <script type="module" crossorigin src="./assets/index-vCxv6WVQ.js"></script>
70
70
  <link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-B0Z9INg1.js">
71
71
  <link rel="modulepreload" crossorigin href="./assets/react-DVplv7_D.js">
72
72
  <link rel="modulepreload" crossorigin href="./assets/compiler-runtime-BrajqUja.js">
@@ -139,7 +139,7 @@
139
139
  <link rel="modulepreload" crossorigin href="./assets/useTheme-BAA27lh7.js">
140
140
  <link rel="modulepreload" crossorigin href="./assets/useAsyncData-qvoPYHtx.js">
141
141
  <link rel="modulepreload" crossorigin href="./assets/purify.es--06brOip.js">
142
- <link rel="modulepreload" crossorigin href="./assets/cells-yOs72eZm.js">
142
+ <link rel="modulepreload" crossorigin href="./assets/cells-D_11MjVB.js">
143
143
  <link rel="modulepreload" crossorigin href="./assets/coerce-DWjFeXOY.js">
144
144
  <link rel="modulepreload" crossorigin href="./assets/get-DjNf4SIX.js">
145
145
  <link rel="modulepreload" crossorigin href="./assets/_hasPath-BmRQipI0.js">
@@ -154,40 +154,40 @@
154
154
  <link rel="modulepreload" crossorigin href="./assets/copy-KlhazlhB.js">
155
155
  <link rel="modulepreload" crossorigin href="./assets/micromark-factory-space-D9Dr-k4J.js">
156
156
  <link rel="modulepreload" crossorigin href="./assets/chunk-YOKDWASO-BIpRENEC.js">
157
- <link rel="modulepreload" crossorigin href="./assets/markdown-renderer--CwVm4kJ.js">
157
+ <link rel="modulepreload" crossorigin href="./assets/markdown-renderer-BWUzqkJM.js">
158
158
  <link rel="modulepreload" crossorigin href="./assets/copy-Djr5m5ac.js">
159
159
  <link rel="modulepreload" crossorigin href="./assets/copy-icon-8-a5vovc.js">
160
- <link rel="modulepreload" crossorigin href="./assets/useRunCells-C_IwVJR_.js">
161
- <link rel="modulepreload" crossorigin href="./assets/RenderHTML-CNDedpS4.js">
162
- <link rel="modulepreload" crossorigin href="./assets/blob-DCjrjZKW.js">
160
+ <link rel="modulepreload" crossorigin href="./assets/useRunCells-ByaWxoIt.js">
161
+ <link rel="modulepreload" crossorigin href="./assets/RenderHTML-DQvxDDNS.js">
162
+ <link rel="modulepreload" crossorigin href="./assets/blob-5VcB8K_t.js">
163
163
  <link rel="modulepreload" crossorigin href="./assets/kbd-pdKBv96t.js">
164
164
  <link rel="modulepreload" crossorigin href="./assets/useInstallPackage-wcDq9hYT.js">
165
165
  <link rel="modulepreload" crossorigin href="./assets/share-BZW3tzEX.js">
166
166
  <link rel="modulepreload" crossorigin href="./assets/events-CbeVJ6OD.js">
167
- <link rel="modulepreload" crossorigin href="./assets/state-BaYudep3.js">
168
- <link rel="modulepreload" crossorigin href="./assets/ErrorBoundary-CdGuz4a_.js">
167
+ <link rel="modulepreload" crossorigin href="./assets/state-Bl9BiY_s.js">
168
+ <link rel="modulepreload" crossorigin href="./assets/ErrorBoundary-CAEGdoFW.js">
169
169
  <link rel="modulepreload" crossorigin href="./assets/chevron-right-eAtVhbNZ.js">
170
170
  <link rel="modulepreload" crossorigin href="./assets/dropdown-menu-DS6umAnH.js">
171
171
  <link rel="modulepreload" crossorigin href="./assets/html-to-image-D_0rd3_B.js">
172
- <link rel="modulepreload" crossorigin href="./assets/download-CV1k4XQ4.js">
172
+ <link rel="modulepreload" crossorigin href="./assets/download-dK52bLxZ.js">
173
173
  <link rel="modulepreload" crossorigin href="./assets/switch-DYjBfpNJ.js">
174
174
  <link rel="modulepreload" crossorigin href="./assets/tabs-CY8E4r9U.js">
175
175
  <link rel="modulepreload" crossorigin href="./assets/table-DG7Ap4H4.js">
176
176
  <link rel="modulepreload" crossorigin href="./assets/eye-off-CxVozGAC.js">
177
177
  <link rel="modulepreload" crossorigin href="./assets/refresh-cw-Qw1f8Jhy.js">
178
- <link rel="modulepreload" crossorigin href="./assets/tree-actions-C_KYlaSv.js">
178
+ <link rel="modulepreload" crossorigin href="./assets/tree-actions-BZNTQ12N.js">
179
179
  <link rel="modulepreload" crossorigin href="./assets/ellipsis-63DyBLHQ.js">
180
180
  <link rel="modulepreload" crossorigin href="./assets/external-link-UrX9KO36.js">
181
181
  <link rel="modulepreload" crossorigin href="./assets/spinner-DyNE-o8o.js">
182
182
  <link rel="modulepreload" crossorigin href="./assets/message-circle-C3rKm8_a.js">
183
183
  <link rel="modulepreload" crossorigin href="./assets/hat-glasses-C8RhSVii.js">
184
184
  <link rel="modulepreload" crossorigin href="./assets/package-GSoSTiIK.js">
185
- <link rel="modulepreload" crossorigin href="./assets/datasource-BakEOWsf.js">
186
- <link rel="modulepreload" crossorigin href="./assets/error-entries-JgrZYMbe.js">
185
+ <link rel="modulepreload" crossorigin href="./assets/datasource-deuXnlU-.js">
186
+ <link rel="modulepreload" crossorigin href="./assets/error-entries-3GVk2KjS.js">
187
187
  <link rel="modulepreload" crossorigin href="./assets/alert-DTaqCu_d.js">
188
- <link rel="modulepreload" crossorigin href="./assets/state-_1LTGJQM.js">
189
- <link rel="modulepreload" crossorigin href="./assets/cell-link-BFbgKiRH.js">
190
- <link rel="modulepreload" crossorigin href="./assets/MarimoErrorOutput-czclLlOn.js">
188
+ <link rel="modulepreload" crossorigin href="./assets/state-Dv059B3A.js">
189
+ <link rel="modulepreload" crossorigin href="./assets/cell-link-C5yYyzuF.js">
190
+ <link rel="modulepreload" crossorigin href="./assets/MarimoErrorOutput-CgZvP0p0.js">
191
191
  <link rel="modulepreload" crossorigin href="./assets/useDateFormatter-DA-62E-Y.js">
192
192
  <link rel="modulepreload" crossorigin href="./assets/en-US-Buu61juQ.js">
193
193
  <link rel="modulepreload" crossorigin href="./assets/isValid-q_Ad80ug.js">
@@ -207,7 +207,7 @@
207
207
  <link rel="modulepreload" crossorigin href="./assets/table-vrvqpuKJ.js">
208
208
  <link rel="modulepreload" crossorigin href="./assets/react-resizable-panels.browser.esm-Dbe0MrFr.js">
209
209
  <link rel="modulepreload" crossorigin href="./assets/useStateForKey-CGMJQT8x.js">
210
- <link rel="modulepreload" crossorigin href="./assets/JsonOutput-Csbov8Fx.js">
210
+ <link rel="modulepreload" crossorigin href="./assets/JsonOutput-CyAKmZDD.js">
211
211
  <link rel="modulepreload" crossorigin href="./assets/chart-no-axes-column-C4_v6fQW.js">
212
212
  <link rel="modulepreload" crossorigin href="./assets/square-function-_W_Lo0P4.js">
213
213
  <link rel="modulepreload" crossorigin href="./assets/spec-uCpVvaAR.js">
@@ -221,11 +221,11 @@
221
221
  <link rel="modulepreload" crossorigin href="./assets/file-icons-CdJgUy-I.js">
222
222
  <link rel="modulepreload" crossorigin href="./assets/globe-UdPdIbF7.js">
223
223
  <link rel="modulepreload" crossorigin href="./assets/esm-CI_o7NhW.js">
224
- <link rel="modulepreload" crossorigin href="./assets/useAddCell-C0J8quGE.js">
225
- <link rel="modulepreload" crossorigin href="./assets/readonly-python-code-DSyr-yE2.js">
224
+ <link rel="modulepreload" crossorigin href="./assets/useAddCell-Cjuqk2YR.js">
225
+ <link rel="modulepreload" crossorigin href="./assets/readonly-python-code-RFuQP50v.js">
226
226
  <link rel="modulepreload" crossorigin href="./assets/data-grid-types-DgHVN_Zz.js">
227
227
  <link rel="modulepreload" crossorigin href="./assets/glide-utils-HFqhvWHI.js">
228
- <link rel="modulepreload" crossorigin href="./assets/column-preview-DXM8SqGY.js">
228
+ <link rel="modulepreload" crossorigin href="./assets/column-preview-xe533-Cn.js">
229
229
  <link rel="modulepreload" crossorigin href="./assets/square-BPmgQFnh.js">
230
230
  <link rel="modulepreload" crossorigin href="./assets/triangle-alert-DmIqR8sY.js">
231
231
  <link rel="modulepreload" crossorigin href="./assets/prop-types-Djf4O40e.js">
@@ -238,7 +238,7 @@
238
238
  <link rel="modulepreload" crossorigin href="./assets/useDeepCompareMemoize-B2DVK2De.js">
239
239
  <link rel="modulepreload" crossorigin href="./assets/useDelayElapsed-BlsDT4TV.js">
240
240
  <link rel="modulepreload" crossorigin href="./assets/pathUtils-DPmlLd-r.js">
241
- <link rel="modulepreload" crossorigin href="./assets/floating-outline-Drd73Quo.js">
241
+ <link rel="modulepreload" crossorigin href="./assets/floating-outline-ChmAuvxz.js">
242
242
  <link rel="stylesheet" crossorigin href="./assets/cells-0PFKL3SH.css">
243
243
  <link rel="stylesheet" crossorigin href="./assets/markdown-renderer-DjIHoF9Z.css">
244
244
  <link rel="stylesheet" crossorigin href="./assets/JsonOutput-B0GmlMnR.css">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/frontend",
3
- "version": "0.25.3-dev95",
3
+ "version": "0.25.3-dev97",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -502,6 +502,7 @@ const ModelListItem: React.FC<ModelListItemProps> = ({
502
502
  <ModelInfoCard model={model} />
503
503
  {model.custom && (
504
504
  <Button
505
+ aria-label={`Delete model ${model.name}`}
505
506
  variant="ghost"
506
507
  size="icon"
507
508
  onClick={handleDelete}
@@ -328,9 +328,9 @@ export const PlansBlock = (props: { data: PlanNotificationEvent[] }) => {
328
328
  </span>
329
329
  </div>
330
330
  <ul className="flex flex-col gap-1">
331
- {plans.map((item, index) => (
331
+ {plans.map((item) => (
332
332
  <li
333
- key={`${item.status}-${index}`}
333
+ key={item.content}
334
334
  className={`
335
335
  flex items-center gap-2 px-2 py-1 rounded
336
336
  `}
@@ -124,6 +124,7 @@ export const AddContextButton = ({
124
124
  return (
125
125
  <Tooltip content="Add context">
126
126
  <Button
127
+ aria-label="Add context"
127
128
  variant="text"
128
129
  size="icon"
129
130
  onClick={handleAddContext}
@@ -148,6 +149,7 @@ export const AttachFileButton = ({
148
149
  <>
149
150
  <Tooltip content="Attach a file">
150
151
  <Button
152
+ aria-label="Attach a file"
151
153
  variant="text"
152
154
  size="icon"
153
155
  onClick={() => fileInputRef.current?.click()}
@@ -166,7 +166,12 @@ const ChatHeader: React.FC<ChatHeaderProps> = ({
166
166
  return (
167
167
  <div className="flex border-b px-2 py-1 justify-between shrink-0 items-center">
168
168
  <Tooltip content="New chat">
169
- <Button variant="text" size="icon" onClick={onNewChat}>
169
+ <Button
170
+ aria-label="New chat"
171
+ variant="text"
172
+ size="icon"
173
+ onClick={onNewChat}
174
+ >
170
175
  <PlusIcon className="h-4 w-4" />
171
176
  </Button>
172
177
  </Tooltip>
@@ -8,6 +8,7 @@ import {
8
8
  import { fireEvent, render, screen, within } from "@testing-library/react";
9
9
  import { beforeAll, describe, expect, it, vi } from "vitest";
10
10
  import { TooltipProvider } from "@/components/ui/tooltip";
11
+ import type { PreviewColumn } from "@/plugins/impl/DataTablePlugin";
11
12
  import { ColumnExplorerPanel } from "../column-explorer-panel/column-explorer";
12
13
  import type { FieldTypesWithExternalType } from "../types";
13
14
 
@@ -36,11 +37,13 @@ const TEST_COLUMNS: ColumnDef<Row>[] = [
36
37
  interface HarnessProps {
37
38
  totalColumns?: number;
38
39
  initiallyHidden?: readonly string[];
40
+ previewColumn?: PreviewColumn;
39
41
  }
40
42
 
41
43
  function PanelHarness({
42
44
  totalColumns = 3,
43
45
  initiallyHidden = EMPTY_HIDDEN_COLUMNS,
46
+ previewColumn = vi.fn().mockResolvedValue({}),
44
47
  }: HarnessProps) {
45
48
  const table = useReactTable<Row>({
46
49
  data: [],
@@ -55,7 +58,7 @@ function PanelHarness({
55
58
  });
56
59
  return (
57
60
  <ColumnExplorerPanel
58
- previewColumn={vi.fn().mockResolvedValue({})}
61
+ previewColumn={previewColumn}
59
62
  fieldTypes={FIELD_TYPES}
60
63
  totalRows={3}
61
64
  totalColumns={totalColumns}
@@ -124,6 +127,29 @@ describe("ColumnExplorerPanel search", () => {
124
127
  });
125
128
  });
126
129
 
130
+ describe("ColumnExplorerPanel chart actions", () => {
131
+ it("names chart actions for each expanded column", async () => {
132
+ const previewColumn = vi.fn<PreviewColumn>().mockResolvedValue({
133
+ chart_code: "chart_code",
134
+ chart_spec: null,
135
+ error: null,
136
+ missing_packages: null,
137
+ stats: null,
138
+ });
139
+ renderPanel({ previewColumn });
140
+ fireEvent.click(screen.getByText("cust_age"));
141
+
142
+ for (const column of ["customer_name", "cust_age"]) {
143
+ expect(
144
+ await screen.findByRole("button", {
145
+ name: `Add chart to notebook for ${column}`,
146
+ }),
147
+ ).toBeInTheDocument();
148
+ expect(previewColumn).toHaveBeenCalledWith({ column });
149
+ }
150
+ });
151
+ });
152
+
127
153
  describe("ColumnExplorerPanel header counts", () => {
128
154
  it("uses rendered-subset total when a clipped column is hidden", () => {
129
155
  // Dataset has 100 columns server-side; only 3 are rendered into the
@@ -10,13 +10,27 @@ import { cellId } from "@/__tests__/branded";
10
10
  import { LazyVegaEmbed } from "@/components/charts/lazy";
11
11
  import { vegaLoader } from "@/plugins/impl/vega/loader";
12
12
  import { ChartPanel, TablePanel, type TablePanelProps } from "../charts";
13
- import { getChartTabName, tabsStorageAtom } from "../storage";
13
+ import { createTabId, getChartTabName, tabsStorageAtom } from "../storage";
14
14
  import { ChartType, NONE_VALUE } from "../types";
15
+ import { TOO_MANY_ROWS } from "../../types";
15
16
 
16
17
  vi.mock("@/components/charts/lazy", () => ({
17
18
  LazyVegaEmbed: vi.fn(() => null),
18
19
  }));
19
20
 
21
+ vi.mock("../components/chart-items", async (importOriginal) => ({
22
+ ...(await importOriginal<typeof import("../components/chart-items")>()),
23
+ ChartTypeSelect: ({
24
+ onValueChange,
25
+ }: {
26
+ onValueChange: (value: string) => void;
27
+ }) => (
28
+ <button type="button" onClick={() => onValueChange("line")}>
29
+ Change chart type
30
+ </button>
31
+ ),
32
+ }));
33
+
20
34
  beforeAll(() => {
21
35
  SetupMocks.resizeObserver();
22
36
  });
@@ -38,6 +52,7 @@ describe("ChartPanel", () => {
38
52
  id,
39
53
  [
40
54
  {
55
+ id: createTabId(),
41
56
  tabName,
42
57
  chartType: ChartType.BAR,
43
58
  config: {
@@ -283,4 +298,62 @@ describe("ChartPanel", () => {
283
298
  });
284
299
  },
285
300
  );
301
+ it("keeps panel state when a chart type change renames the tab", async () => {
302
+ const store = createStore();
303
+ const id = cellId("rename-keeps-state");
304
+ const tabName = getChartTabName(0, ChartType.BAR);
305
+ store.set(
306
+ tabsStorageAtom,
307
+ new Map([
308
+ [
309
+ id,
310
+ [
311
+ {
312
+ id: createTabId(),
313
+ tabName,
314
+ chartType: ChartType.BAR,
315
+ config: {
316
+ general: {
317
+ xColumn: { field: "value", type: "number" },
318
+ yColumn: {
319
+ field: "value",
320
+ type: "integer",
321
+ aggregate: NONE_VALUE,
322
+ },
323
+ },
324
+ },
325
+ },
326
+ ],
327
+ ],
328
+ ]),
329
+ );
330
+ const sample = [{ value: 1 }];
331
+ const props: TablePanelProps = {
332
+ cellId: id,
333
+ data: sample,
334
+ dataTable: <div />,
335
+ totalRows: TOO_MANY_ROWS,
336
+ columns: 1,
337
+ displayHeader: true,
338
+ fieldTypes: [["value", ["number", "float64"]]],
339
+ };
340
+ const { getByRole, findByRole, queryByRole } = render(
341
+ <Provider store={store}>
342
+ <Tooltip.Provider>
343
+ <TablePanel {...props} />
344
+ </Tooltip.Provider>
345
+ </Provider>,
346
+ );
347
+ fireEvent.click(getByRole("tab", { name: tabName }));
348
+ fireEvent.click(await findByRole("button", { name: "Proceed" }));
349
+ await waitFor(() => {
350
+ expect(queryByRole("button", { name: "Proceed" })).toBeNull();
351
+ });
352
+
353
+ fireEvent.click(getByRole("button", { name: "Change chart type" }));
354
+
355
+ const renamed = getChartTabName(0, ChartType.LINE);
356
+ await findByRole("tab", { name: renamed });
357
+ expect(queryByRole("button", { name: "Proceed" })).toBeNull();
358
+ });
286
359
  });
@@ -16,7 +16,7 @@ import { cellId } from "@/__tests__/branded";
16
16
  import { availableStorage } from "@/utils/storage/storage";
17
17
  import { ChartSchema } from "../schemas";
18
18
  import type { TabName } from "../storage";
19
- import { KEY, tabsStorageAtom } from "../storage";
19
+ import { createTabId, KEY, tabsStorageAtom } from "../storage";
20
20
  import { ChartType } from "../types";
21
21
 
22
22
  describe("Chart Transforms Storage", () => {
@@ -39,6 +39,7 @@ describe("Chart Transforms Storage", () => {
39
39
  const store = getDefaultStore();
40
40
  const cid = cellId("cell-1");
41
41
  const tabData = {
42
+ id: createTabId(),
42
43
  tabName: "Tab 1" as TabName,
43
44
  chartType: ChartType.LINE,
44
45
  config: ChartSchema.parse({
@@ -63,6 +64,7 @@ describe("Chart Transforms Storage", () => {
63
64
  const store = getDefaultStore();
64
65
  const cid = cellId("cell-1");
65
66
  const tabData1 = {
67
+ id: createTabId(),
66
68
  tabName: "Tab 1" as TabName,
67
69
  chartType: ChartType.LINE,
68
70
  config: ChartSchema.parse({
@@ -73,6 +75,7 @@ describe("Chart Transforms Storage", () => {
73
75
  }),
74
76
  };
75
77
  const tabData2 = {
78
+ id: createTabId(),
76
79
  tabName: "Tab 2" as TabName,
77
80
  chartType: ChartType.BAR,
78
81
  config: ChartSchema.parse({
@@ -48,7 +48,12 @@ import { HeatmapForm } from "./forms/heatmap";
48
48
  import { PieForm } from "./forms/pie";
49
49
  import { LazyChart } from "./lazy-chart";
50
50
  import { ChartSchema, type ChartSchemaType, getChartDefaults } from "./schemas";
51
- import { getChartTabName, type TabName, tabsStorageAtom } from "./storage";
51
+ import {
52
+ createTabId,
53
+ getChartTabName,
54
+ type TabName,
55
+ tabsStorageAtom,
56
+ } from "./storage";
52
57
  import { ChartType } from "./types";
53
58
 
54
59
  const NEW_CHART_TYPE = "bar" as ChartType;
@@ -114,7 +119,12 @@ export const TablePanel: React.FC<TablePanelProps> = ({
114
119
  const tabName = getChartTabName(0, NEW_CHART_TYPE);
115
120
  const newTabs = new Map(tabsMap);
116
121
  newTabs.set(cellId, [
117
- { tabName, chartType: NEW_CHART_TYPE, config: getChartDefaults() },
122
+ {
123
+ id: createTabId(),
124
+ tabName,
125
+ chartType: NEW_CHART_TYPE,
126
+ config: getChartDefaults(),
127
+ },
118
128
  ]);
119
129
  saveTabsMap(newTabs);
120
130
  setTabCounter(1);
@@ -135,6 +145,7 @@ export const TablePanel: React.FC<TablePanelProps> = ({
135
145
  newTabs.set(cellId, [
136
146
  ...tabs,
137
147
  {
148
+ id: createTabId(),
138
149
  tabName,
139
150
  chartType: NEW_CHART_TYPE,
140
151
  config: getChartDefaults(),
@@ -233,9 +244,9 @@ export const TablePanel: React.FC<TablePanelProps> = ({
233
244
  >
234
245
  Table
235
246
  </TabsTrigger>
236
- {tabs.map((tab, idx) => (
247
+ {tabs.map((tab) => (
237
248
  <TabsTrigger
238
- key={idx}
249
+ key={tab.id}
239
250
  className="text-xs"
240
251
  value={tab.tabName}
241
252
  onClick={() => setSelectedTab(tab.tabName)}
@@ -263,7 +274,7 @@ export const TablePanel: React.FC<TablePanelProps> = ({
263
274
  <TabsContent className="mt-1 overflow-hidden" value={DEFAULT_TAB_NAME}>
264
275
  {dataTable}
265
276
  </TabsContent>
266
- {tabs.map((tab, idx) => {
277
+ {tabs.map((tab) => {
267
278
  const saveChart = (formValues: ChartSchemaType) => {
268
279
  saveTabChart({
269
280
  tabName: tab.tabName,
@@ -275,7 +286,11 @@ export const TablePanel: React.FC<TablePanelProps> = ({
275
286
  saveTabChartType(tab.tabName, chartType);
276
287
  };
277
288
  return (
278
- <TabsContent key={idx} value={tab.tabName} className="h-[400px] mt-0">
289
+ <TabsContent
290
+ key={tab.id}
291
+ value={tab.tabName}
292
+ className="h-[400px] mt-0"
293
+ >
279
294
  <ChartPanel
280
295
  tableData={data}
281
296
  chartConfig={tab.config}
@@ -7,13 +7,16 @@ import { Logger } from "@/utils/Logger";
7
7
  import { NotebookScopedLocalStorage } from "@/utils/storage/typed";
8
8
  import { capitalize } from "@/utils/strings";
9
9
  import type { TypedString } from "@/utils/typed";
10
+ import { generateUUID } from "@/utils/uuid";
10
11
  import { ChartSchema, type ChartSchemaType } from "./schemas";
11
12
  import type { ChartType } from "./types";
12
13
 
13
14
  export type TabName = TypedString<"TabName">;
15
+ export type TabId = TypedString<"TabId">;
14
16
  export const KEY = "marimo:charts:v2";
15
17
 
16
18
  interface TabStorage {
19
+ id: TabId;
17
20
  tabName: TabName; // unique within cell
18
21
  chartType: ChartType;
19
22
  config: ChartSchemaType;
@@ -21,11 +24,15 @@ interface TabStorage {
21
24
 
22
25
  type TabStorageMap = Map<CellId, TabStorage[]>;
23
26
 
24
- const TabStorageSchema = z.object({
25
- tabName: z.string().transform((name) => name as TabName),
26
- chartType: z.string().transform((type) => type as ChartType),
27
- config: ChartSchema,
28
- });
27
+ const TabStorageSchema = z
28
+ .object({
29
+ id: z.string().optional(),
30
+ tabName: z.string().transform((name) => name as TabName),
31
+ chartType: z.string().transform((type) => type as ChartType),
32
+ config: ChartSchema,
33
+ })
34
+ // Stored tabs without an id use their name, which is unique within a cell.
35
+ .transform((tab) => ({ ...tab, id: (tab.id ?? tab.tabName) as TabId }));
29
36
  const TabStorageEntriesSchema = z.array(
30
37
  z.tuple([
31
38
  z.string().transform((name) => name as CellId),
@@ -64,6 +71,10 @@ export const tabsStorageAtom = atomWithStorage<TabStorageMap>(
64
71
  mapStorage,
65
72
  );
66
73
 
74
+ export function createTabId(): TabId {
75
+ return generateUUID() as TabId;
76
+ }
77
+
67
78
  /**
68
79
  * Convenience function to get the tab name for a given tab number and chart type
69
80
  */
@@ -345,7 +345,7 @@ const ColumnPreview = ({
345
345
  const chart = chart_spec && renderChart(chart_spec, theme);
346
346
 
347
347
  const addDataframeChart = chart_code && (
348
- <AddDataframeChart chartCode={chart_code} />
348
+ <AddDataframeChart chartCode={chart_code} columnName={columnName} />
349
349
  );
350
350
 
351
351
  return (
@@ -0,0 +1,97 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import {
4
+ fireEvent,
5
+ render,
6
+ screen,
7
+ waitFor,
8
+ within,
9
+ } from "@testing-library/react";
10
+ import { createStore, Provider } from "jotai";
11
+ import { beforeAll, describe, expect, it, vi } from "vitest";
12
+ import { MockRequestClient } from "@/__mocks__/requests";
13
+ import { Command, CommandList } from "@/components/ui/command";
14
+ import { TooltipProvider } from "@/components/ui/tooltip";
15
+ import type { DataTable } from "@/core/kernel/messages";
16
+ import { requestClientAtom } from "@/core/network/requests";
17
+ import { copyToClipboard } from "@/utils/copy";
18
+ import { TableList } from "../datasources";
19
+
20
+ vi.mock("@/utils/copy", () => ({
21
+ copyToClipboard: vi.fn().mockResolvedValue(undefined),
22
+ }));
23
+
24
+ beforeAll(() => {
25
+ HTMLElement.prototype.scrollIntoView = vi.fn();
26
+ });
27
+
28
+ const table: DataTable = {
29
+ name: "users",
30
+ columns: [
31
+ {
32
+ name: "email",
33
+ type: "string",
34
+ external_type: "VARCHAR",
35
+ sample_values: [],
36
+ },
37
+ {
38
+ name: "name",
39
+ type: "string",
40
+ external_type: "VARCHAR",
41
+ sample_values: [],
42
+ },
43
+ ],
44
+ source: "memory",
45
+ source_type: "local",
46
+ type: "table",
47
+ engine: null,
48
+ indexes: null,
49
+ num_columns: 2,
50
+ num_rows: null,
51
+ variable_name: null,
52
+ primary_keys: null,
53
+ };
54
+
55
+ describe("Column actions", () => {
56
+ it("copies each column through one named button without expanding its preview", async () => {
57
+ const store = createStore();
58
+ const client = MockRequestClient.create();
59
+ store.set(requestClientAtom, client);
60
+ render(
61
+ <Provider store={store}>
62
+ <TooltipProvider>
63
+ <Command>
64
+ <CommandList>
65
+ <TableList tables={[table]} />
66
+ </CommandList>
67
+ </Command>
68
+ </TooltipProvider>
69
+ </Provider>,
70
+ );
71
+
72
+ fireEvent.click(await screen.findByText("users"));
73
+
74
+ for (const column of table.columns) {
75
+ const copyButton = await screen.findByRole("button", {
76
+ name: `Copy column name ${column.name}`,
77
+ });
78
+ const row = screen.getByRole("option", {
79
+ name: `${column.name} ${column.external_type}`,
80
+ });
81
+ expect(within(row).getAllByRole("button")).toEqual([copyButton]);
82
+ expect(copyButton.querySelector("button")).toBeNull();
83
+
84
+ fireEvent.focus(copyButton);
85
+ expect(
86
+ await screen.findByRole("tooltip", { name: "Copy column name" }),
87
+ ).toBeInTheDocument();
88
+
89
+ fireEvent.click(copyButton);
90
+ await waitFor(() => {
91
+ expect(copyToClipboard).toHaveBeenLastCalledWith(column.name);
92
+ });
93
+ expect(client.previewDatasetColumn).not.toHaveBeenCalled();
94
+ }
95
+ expect(copyToClipboard).toHaveBeenCalledTimes(2);
96
+ });
97
+ });