@marimo-team/frontend 0.25.2 → 0.25.3-dev100

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 (476) hide show
  1. package/dist/assets/CellStatus-fK4n2NPc.js +1 -0
  2. package/dist/assets/{ConnectedDataExplorerComponent-t8fH_TNc.js → ConnectedDataExplorerComponent-Dft8R6l5.js} +1 -1
  3. package/dist/assets/{ErrorBoundary-Gl5nCA8C.js → ErrorBoundary-CAEGdoFW.js} +2 -2
  4. package/dist/assets/JsonOutput-CyAKmZDD.js +11 -0
  5. package/dist/assets/{LazyAnyLanguageCodeMirror-CKcCbG2G.js → LazyAnyLanguageCodeMirror-BPYE76jR.js} +2 -2
  6. package/dist/assets/MarimoErrorOutput-CgZvP0p0.js +10 -0
  7. package/dist/assets/{RenderHTML-BbOqmHy3.js → RenderHTML-DQvxDDNS.js} +1 -1
  8. package/dist/assets/RunButton-BBDnDL6G.js +1 -0
  9. package/dist/assets/__vite-browser-external-8vDMR0Kk.js +1 -0
  10. package/dist/assets/__vite-browser-external-mKcnhO20.js +1 -0
  11. package/dist/assets/{add-cell-with-ai-8UBC0xwR.js → add-cell-with-ai-Clg5FELx.js} +12 -12
  12. package/dist/assets/{add-connection-dialog-DXQnd6eB.js → add-connection-dialog-BqdWmD-7.js} +2 -2
  13. package/dist/assets/{agent-panel-D7kMZLG-.js → agent-panel-5RxG5Y82.js} +5 -5
  14. package/dist/assets/ai-model-dropdown-Bvl1ktQq.js +13 -0
  15. package/dist/assets/{alert-dialog-BaPGzeOK.js → alert-dialog-C-sWmKva.js} +1 -1
  16. package/dist/assets/any-language-editor-DUMe1UCk.js +1 -0
  17. package/dist/assets/{app-config-button-BKEG4x9u.js → app-config-button-0QTeQ2Mv.js} +1 -1
  18. package/dist/assets/arrays-0R2reF2y.js +1 -0
  19. package/dist/assets/{blob-D5nfNHOo.js → blob-5VcB8K_t.js} +1 -1
  20. package/dist/assets/{button-COI-NGrx.js → button-D8gVKwe-.js} +1 -1
  21. package/dist/assets/{cache-panel-DTY1dqvi.js → cache-panel-BV-c7ed0.js} +1 -1
  22. package/dist/assets/{capabilities-BJvf-MD4.js → capabilities-Pq1YNPlK.js} +1 -1
  23. package/dist/assets/cell-editor-Byw5y-oI.js +42 -0
  24. package/dist/assets/cell-link-C5yYyzuF.js +1 -0
  25. package/dist/assets/{cells-BRkCaSsh.js → cells-D_11MjVB.js} +60 -60
  26. package/dist/assets/{chat-display-Dnagy4Nv.js → chat-display-BCR_ySiV.js} +1 -1
  27. package/dist/assets/chat-panel-RPRvBsaU.js +4 -0
  28. package/dist/assets/chat-ui-CGviVaRA.js +4 -0
  29. package/dist/assets/{circle-DXf-VWZG.js → circle-B6D_6D8w.js} +1 -1
  30. package/dist/assets/code-visibility-DYDagVy7.js +1 -0
  31. package/dist/assets/coerce-DWjFeXOY.js +1 -0
  32. package/dist/assets/column-preview-xe533-Cn.js +1 -0
  33. package/dist/assets/command-palette-BB9bWdWp.js +1 -0
  34. package/dist/assets/{common-Cu6CDbuw.js → common-CaeItbcI.js} +1 -1
  35. package/dist/assets/components-CBE8cOLK.js +1 -0
  36. package/dist/assets/{components-CP_UoFtT.js → components-DCo7mAWO.js} +1 -1
  37. package/dist/assets/{components-CGw16R_N.js → components-G6frbw0c.js} +1 -1
  38. package/dist/assets/config-CD1IuZFJ.js +1 -0
  39. package/dist/assets/config-DMz7oN0r.js +1 -0
  40. package/dist/assets/config-DpY86_Yq.js +1 -0
  41. package/dist/assets/{copy-D8O_CYCG.js → copy-KlhazlhB.js} +1 -1
  42. package/dist/assets/{copy-icon-BIiRXh_3.js → copy-icon-8-a5vovc.js} +1 -1
  43. package/dist/assets/{createReducer-DgRODTW6.js → createReducer-CaI_Oa0v.js} +1 -1
  44. package/dist/assets/{datasource-Cq6x_5ML.js → datasource-deuXnlU-.js} +1 -1
  45. package/dist/assets/{dates-BbFQ2oE4.js → dates-CQMwFko2.js} +1 -1
  46. package/dist/assets/{dependency-graph-panel--Kgtktcw.js → dependency-graph-panel-CIykLnJe.js} +2 -2
  47. package/dist/assets/{dist-iaThVDkA.js → dist-jlhMTKo_.js} +1 -1
  48. package/dist/assets/{documentation-panel-BHmbeznw.js → documentation-panel-2PQ8kQLa.js} +1 -1
  49. package/dist/assets/{download-DCJXy66H.js → download-dK52bLxZ.js} +1 -1
  50. package/dist/assets/{dropdown-menu-C1rnl-0o.js → dropdown-menu-DS6umAnH.js} +1 -1
  51. package/dist/assets/edit-page-DrElzw10.js +9 -0
  52. package/dist/assets/{error-banner-Cd69vvyX.js → error-banner-B5kzVf_0.js} +1 -1
  53. package/dist/assets/{error-entries-Dk3VZ-OK.js → error-entries-3GVk2KjS.js} +1 -1
  54. package/dist/assets/{error-panel-BPRYmC5J.js → error-panel-CzDRGPeC.js} +1 -1
  55. package/dist/assets/{errors-DCrsa1nH.js → errors-CNsgAw6m.js} +1 -1
  56. package/dist/assets/{es-Cqst59WX.js → es-N9SHpmG7.js} +1 -1
  57. package/dist/assets/{feedback-button-Bik2-e33.js → feedback-button-C5NgWpFg.js} +2 -2
  58. package/dist/assets/feedback-button-CONwLFpw.js +1 -0
  59. package/dist/assets/file-explorer-panel-B50xDnTZ.js +1 -0
  60. package/dist/assets/{file-name-input-Cdc-TVqO.js → file-name-input-DqwMSgGU.js} +1 -1
  61. package/dist/assets/{floating-outline-XD9FAV-f.js → floating-outline-ChmAuvxz.js} +1 -1
  62. package/dist/assets/{formats-yZ06h1Pl.js → formats-zylfwejq.js} +1 -1
  63. package/dist/assets/{formatting-BqrP-n49.js → formatting-uom1es4b.js} +1 -1
  64. package/dist/assets/{fullscreen-B9tHcfpV.js → fullscreen-BWTs8pYZ.js} +1 -1
  65. package/dist/assets/{gallery-page-Dm6mGwBi.js → gallery-page-CiL8aMS1.js} +1 -1
  66. package/dist/assets/{ganttDiagram-EL5Y4UJY-2n7Ig5Jm.js → ganttDiagram-EL5Y4UJY-CYqkCfFG.js} +1 -1
  67. package/dist/assets/{glide-data-editor-J9CAb70d.js → glide-data-editor-CqRclBkp.js} +2 -2
  68. package/dist/assets/{glide-utils-CIrAxdjv.js → glide-utils-HFqhvWHI.js} +2 -2
  69. package/dist/assets/home-page-BgZVl4dP.js +1 -0
  70. package/dist/assets/{hooks-93IwHd2u.js → hooks-DM5n7Osw.js} +1 -1
  71. package/dist/assets/{html-to-image-DK9Zl1lU.js → html-to-image-D_0rd3_B.js} +1 -1
  72. package/dist/assets/index-BnhSnwvB.css +2 -0
  73. package/dist/assets/index-EG2n8Oht.js +30 -0
  74. package/dist/assets/{input-BoBxR50a.js → input-DmHMEZGh.js} +1 -1
  75. package/dist/assets/kiosk-mode-DFS6HopF.js +1 -0
  76. package/dist/assets/layout-BpP8nTjb.js +9 -0
  77. package/dist/assets/linear-Bma8J3iM.js +1 -0
  78. package/dist/assets/links-DX2w97N9.js +1 -0
  79. package/dist/assets/{logs-panel-ABM16htT.js → logs-panel-B5eVAZtH.js} +1 -1
  80. package/dist/assets/{markdown-renderer-BG3sp2rs.js → markdown-renderer-BWUzqkJM.js} +1 -1
  81. package/dist/assets/{menu-items-BrwnTgyK.js → menu-items-BgUyNUpu.js} +1 -1
  82. package/dist/assets/{mermaid-a3xYymTy.js → mermaid-BKXlWk4F.js} +2 -2
  83. package/dist/assets/{name-cell-input-BgEBmqyL.js → name-cell-input-DXW68gRW.js} +1 -1
  84. package/dist/assets/numbers-CsZVw3Dc.js +1 -0
  85. package/dist/assets/{objects-snlMn4yk.js → objects-rLPlQ3X_.js} +1 -1
  86. package/dist/assets/{once-Haz_uxEL.js → once-6KNTRW0u.js} +1 -1
  87. package/dist/assets/{outline-panel-DCXQ3_F6.js → outline-panel-DPxJhJ_A.js} +1 -1
  88. package/dist/assets/{package-data-dv4FBHxR.js → package-data-CKPmBDtg.js} +1 -1
  89. package/dist/assets/packages-panel-gBE2FWQr.js +1 -0
  90. package/dist/assets/{panel-accordion-state-C1sdPp0m.js → panel-accordion-state-CCF5Gxnc.js} +1 -1
  91. package/dist/assets/panels-Dr6qMCvU.js +105 -0
  92. package/dist/assets/precisionRound-BxlUs8G4.js +1 -0
  93. package/dist/assets/process-output-Cw9IDcKQ.js +1 -0
  94. package/dist/assets/{quadrantDiagram-AXDQQJYC-yBhCDI7E.js → quadrantDiagram-AXDQQJYC-D9KH5Zge.js} +1 -1
  95. package/dist/assets/react-vega-BTNeboR2.js +189 -0
  96. package/dist/assets/{readonly-code-display-Do50PHI8.js → readonly-code-display-DYcZQ28T.js} +1 -1
  97. package/dist/assets/{readonly-python-code-DCSMIsTX.js → readonly-python-code-RFuQP50v.js} +1 -1
  98. package/dist/assets/{renderShortcut-BkVJQ_Zh.js → renderShortcut-rUwGqKgC.js} +1 -1
  99. package/dist/assets/{requests-lt0JK8Jt.js → requests-BIFR-h4M.js} +1 -1
  100. package/dist/assets/requests-DuLJN9Tf.js +1 -0
  101. package/dist/assets/{reveal-component-ltaepAqw.js → reveal-component-yRrDmCQz.js} +37 -37
  102. package/dist/assets/run-page-B69M4-zE.js +1 -0
  103. package/dist/assets/{runs-BLp5jezv.js → runs-C49hleBy.js} +1 -1
  104. package/dist/assets/{runtime-details-state-CSPobEdR.js → runtime-details-state-pobtu6TO.js} +1 -1
  105. package/dist/assets/{save-worker-BbUSwZRG.js → save-worker-pCkSHhuz.js} +1 -1
  106. package/dist/assets/{schemas-Dv42wLFq.js → schemas-ChRqMOTL.js} +1 -1
  107. package/dist/assets/scratchpad-panel-H44_pvb_.js +1 -0
  108. package/dist/assets/{scroll-to-bottom-button-1FyowpWV.js → scroll-to-bottom-button-CIkON2WJ.js} +1 -1
  109. package/dist/assets/{secrets-panel-Bctbtkrp.js → secrets-panel-bcfEKxRx.js} +1 -1
  110. package/dist/assets/{select-DTBn5eTb.js → select-3CVGlYrC.js} +1 -1
  111. package/dist/assets/{session-DqoWmb7X.js → session-CMLIzZKw.js} +1 -1
  112. package/dist/assets/session-panel-Bnd1iXkO.js +1 -0
  113. package/dist/assets/{snippets-panel-Bh1oTw4m.js → snippets-panel-DKApxKx9.js} +1 -1
  114. package/dist/assets/{spec-BfSBcO5B.js → spec-uCpVvaAR.js} +1 -1
  115. package/dist/assets/{state-uwRvJLb8.js → state-3mcyTS9q.js} +1 -1
  116. package/dist/assets/state-B_MCKzBL.js +1 -0
  117. package/dist/assets/state-Bl9BiY_s.js +3 -0
  118. package/dist/assets/{state-C3oDoyFe.js → state-C5WAeuTu.js} +1 -1
  119. package/dist/assets/{state-DxQnC_1I.js → state-Cv2JW3l9.js} +1 -1
  120. package/dist/assets/{state-XsPO8hNi.js → state-Dv059B3A.js} +1 -1
  121. package/dist/assets/{strings-B2vv-ENv.js → strings-C25fhMR_.js} +1 -1
  122. package/dist/assets/{swiper-component-a07domrZ.js → swiper-component-CRGRy5_O.js} +1 -1
  123. package/dist/assets/{switch-CTaiAjrz.js → switch-DYjBfpNJ.js} +1 -1
  124. package/dist/assets/{tabs-BuQlZFbt.js → tabs-CY8E4r9U.js} +1 -1
  125. package/dist/assets/{terminal-Ck_vK-34.js → terminal-Ca-kubKb.js} +1 -1
  126. package/dist/assets/{time-MgToa8VM.js → time-DswSXBDk.js} +1 -1
  127. package/dist/assets/{toast-BGzLGnvw.js → toast-Gzxi-le_.js} +1 -1
  128. package/dist/assets/tooltip-Brya4Ghn.js +61 -0
  129. package/dist/assets/{tooltip-C9TuY18o.js → tooltip-CkGm8X7m.js} +1 -1
  130. package/dist/assets/tracing-Clwy4SlW.js +1 -0
  131. package/dist/assets/tracing-panel-BwjLG_aC.js +2 -0
  132. package/dist/assets/{tree-actions-GM5E_bUs.js → tree-actions-BZNTQ12N.js} +1 -1
  133. package/dist/assets/{use-restore-focus-BxEGwIbk.js → use-restore-focus-DdS7Tl0F.js} +1 -1
  134. package/dist/assets/{useAddCell-GR7eEQrT.js → useAddCell-Cjuqk2YR.js} +1 -1
  135. package/dist/assets/useAsyncData-qvoPYHtx.js +1 -0
  136. package/dist/assets/useBoolean-Dd2nWXFJ.js +1 -0
  137. package/dist/assets/useCellActionButton-MoWLvuGe.js +1 -0
  138. package/dist/assets/useDebounce-DwTQ_1Pu.js +1 -0
  139. package/dist/assets/{useDeepCompareMemoize-DFIVN9dn.js → useDeepCompareMemoize-B2DVK2De.js} +1 -1
  140. package/dist/assets/useDelayElapsed-BlsDT4TV.js +1 -0
  141. package/dist/assets/{useDeleteCell-2_bMClHd.js → useDeleteCell-BC-MW7d1.js} +1 -1
  142. package/dist/assets/{useDependencyPanelTab-k3C3M18S.js → useDependencyPanelTab-CZU7i-B3.js} +1 -1
  143. package/dist/assets/{useEvent-Dfcbgjhg.js → useEvent-BDjgaBtz.js} +1 -1
  144. package/dist/assets/{useEventListener-D9cftcHj.js → useEventListener-CjDeLqFC.js} +1 -1
  145. package/dist/assets/{useIframeCapabilities-CwTkczVN.js → useIframeCapabilities-Czr9e4uG.js} +1 -1
  146. package/dist/assets/{useInstallPackage-iX1eufdl.js → useInstallPackage-wcDq9hYT.js} +1 -1
  147. package/dist/assets/{useInterval--x3s1VpI.js → useInterval-DJdQmXnq.js} +1 -1
  148. package/dist/assets/useNotebookActions-wFxVdRXQ.js +1 -0
  149. package/dist/assets/{useRunCells-B2vUhREg.js → useRunCells-ByaWxoIt.js} +1 -1
  150. package/dist/assets/{useSplitCell-DEb03htp.js → useSplitCell-YJIystwv.js} +1 -1
  151. package/dist/assets/useStateForKey-CGMJQT8x.js +1 -0
  152. package/dist/assets/{useTheme-COhSHkCj.js → useTheme-BAA27lh7.js} +1 -1
  153. package/dist/assets/{utils-CaQYvazl.js → utils-DfElfBpz.js} +1 -1
  154. package/dist/assets/vega-component-BeyxAJad.js +1 -0
  155. package/dist/assets/{vega-embed-container-DJPNkHPb.js → vega-embed-container-DtArSJz1.js} +1 -1
  156. package/dist/assets/vega-loader.browser-Bu25TKax.js +6 -0
  157. package/dist/assets/{worker-DTQ7oH2A.js → worker-bSCjCvwz.js} +1 -1
  158. package/dist/assets/{write-secret-modal-DzKAInL2.js → write-secret-modal-BEx-EUlh.js} +1 -1
  159. package/dist/assets/{xychartDiagram-S5SC5T6Z-KbGzG6-u.js → xychartDiagram-S5SC5T6Z-CeRnBS5e.js} +1 -1
  160. package/dist/index.html +80 -79
  161. package/package.json +8 -5
  162. package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
  163. package/src/__tests__/host-imports.test.ts +101 -0
  164. package/src/__tests__/mount.test.ts +8 -8
  165. package/src/components/app-config/ai-config.tsx +7 -2
  166. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  167. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  168. package/src/components/chat/acp/blocks.tsx +2 -2
  169. package/src/components/chat/chat-components.tsx +2 -0
  170. package/src/components/chat/chat-panel.tsx +9 -8
  171. package/src/components/chat/chat-utils.ts +4 -8
  172. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  173. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  174. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  175. package/src/components/data-table/__tests__/context-menu.test.ts +176 -0
  176. package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +24 -1
  177. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  178. package/src/components/data-table/__tests__/export-actions.test.tsx +434 -6
  179. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  180. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  181. package/src/components/data-table/cell-selection/feature.ts +2 -2
  182. package/src/components/data-table/cell-selection/types.ts +2 -2
  183. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  184. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  185. package/src/components/data-table/charts/charts.tsx +35 -25
  186. package/src/components/data-table/charts/storage.ts +16 -5
  187. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  188. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  189. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  190. package/src/components/data-table/columns.tsx +45 -46
  191. package/src/components/data-table/context-menu.tsx +161 -46
  192. package/src/components/data-table/data-table.tsx +7 -1
  193. package/src/components/data-table/date-filter-inputs.tsx +15 -42
  194. package/src/components/data-table/date-popover.tsx +6 -4
  195. package/src/components/data-table/export-actions.tsx +267 -163
  196. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +2 -2
  197. package/src/components/data-table/schemas.ts +14 -36
  198. package/src/components/data-table/search-highlight.tsx +79 -0
  199. package/src/components/data-table/types.ts +1 -0
  200. package/src/components/data-table/url-detector.tsx +49 -9
  201. package/src/components/data-table/utils.ts +18 -0
  202. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  203. package/src/components/datasources/__tests__/column-preview.test.tsx +63 -2
  204. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  205. package/src/components/datasources/column-preview.tsx +8 -2
  206. package/src/components/datasources/datasources.tsx +47 -35
  207. package/src/components/debugger/debugger-code.tsx +5 -0
  208. package/src/components/dependency-graph/panels.tsx +2 -1
  209. package/src/components/editor/Output.tsx +9 -2
  210. package/src/components/editor/SortableCell.tsx +1 -3
  211. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  212. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
  213. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +210 -34
  214. package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
  215. package/src/components/editor/actions/pair-with-agent-modal.tsx +100 -37
  216. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  217. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  218. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  219. package/src/components/editor/cell/CreateCellButton.tsx +8 -10
  220. package/src/components/editor/cell/DeleteButton.tsx +1 -0
  221. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  222. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  223. package/src/components/editor/cell/code/cell-editor.tsx +1 -1
  224. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  225. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  226. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +30 -1
  227. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  228. package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
  229. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  230. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +366 -0
  231. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
  232. package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
  233. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
  234. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  235. package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
  236. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  237. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  238. package/src/components/editor/connections/components.tsx +1 -1
  239. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  240. package/src/components/editor/controls/command-palette.tsx +22 -10
  241. package/src/components/editor/dynamic-favicon.tsx +21 -40
  242. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +537 -0
  243. package/src/components/editor/errors/send-error-report-button.tsx +69 -0
  244. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  245. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  246. package/src/components/editor/file-tree/file-explorer.tsx +10 -5
  247. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  248. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  249. package/src/components/editor/notebook-banner.tsx +1 -0
  250. package/src/components/editor/output/MarimoErrorOutput.tsx +21 -5
  251. package/src/components/editor/output/MarimoTracebackOutput.tsx +37 -22
  252. package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
  253. package/src/components/editor/output/ansi-reduce.ts +12 -0
  254. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  255. package/src/components/editor/package-alert.tsx +6 -4
  256. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  257. package/src/components/home/components.tsx +17 -31
  258. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  259. package/src/components/icons/large-spinner.tsx +3 -14
  260. package/src/components/icons/spinner.tsx +1 -1
  261. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  262. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  263. package/src/components/pages/home-page.tsx +164 -91
  264. package/src/components/scratchpad/scratchpad.tsx +2 -2
  265. package/src/components/slides/reveal-component.tsx +4 -2
  266. package/src/components/slides/slide-cell-view.tsx +1 -1
  267. package/src/components/slides/slide-form.tsx +1 -0
  268. package/src/components/tracing/tracing.tsx +2 -2
  269. package/src/components/ui/alert-dialog.tsx +1 -1
  270. package/src/core/ai/context/providers/datasource.ts +6 -2
  271. package/src/core/attachments/__tests__/toast.test.tsx +58 -0
  272. package/src/core/attachments/state.ts +18 -0
  273. package/src/core/attachments/toast.ts +15 -0
  274. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  275. package/src/core/cells/__tests__/cell.test.ts +39 -0
  276. package/src/core/cells/__tests__/cells.test.ts +55 -1
  277. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  278. package/src/core/cells/__tests__/error-report.test.ts +139 -0
  279. package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
  280. package/src/core/cells/__tests__/session.test.ts +50 -1
  281. package/src/core/cells/cell.ts +14 -3
  282. package/src/core/cells/cells.ts +3 -1
  283. package/src/core/cells/error-report.ts +63 -0
  284. package/src/core/cells/pending-delete-service.ts +2 -6
  285. package/src/core/cells/session.ts +29 -2
  286. package/src/core/cells/types.ts +10 -1
  287. package/src/core/codemirror/__tests__/setup.test.ts +1 -1
  288. package/src/core/codemirror/keymaps/vim.ts +2 -6
  289. package/src/core/codemirror/language/languages/markdown.ts +1 -1
  290. package/src/core/codemirror/language/languages/python.ts +3 -3
  291. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  292. package/src/core/config/pair.ts +5 -3
  293. package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
  294. package/src/core/datasets/data-source-discovery.ts +4 -6
  295. package/src/core/document/__tests__/draft.test.ts +322 -0
  296. package/src/core/document/__tests__/fixtures.ts +92 -0
  297. package/src/core/document/__tests__/model.test.ts +483 -0
  298. package/src/core/document/__tests__/state.test.ts +446 -0
  299. package/src/core/document/__tests__/submission.test.ts +346 -0
  300. package/src/core/document/draft.ts +472 -0
  301. package/src/core/document/index.ts +10 -0
  302. package/src/core/document/model.ts +53 -0
  303. package/src/core/document/state.ts +401 -0
  304. package/src/core/document/sync.ts +359 -0
  305. package/src/core/edit-app.tsx +2 -2
  306. package/src/core/host/AGENTS.md +16 -0
  307. package/src/core/host/__tests__/attachments.test.ts +56 -0
  308. package/src/core/host/__tests__/document.test.ts +126 -0
  309. package/src/core/host/__tests__/fixtures.ts +41 -0
  310. package/src/core/host/__tests__/notebook-client.test.ts +570 -0
  311. package/src/core/host/connection.ts +42 -0
  312. package/src/core/host/notebook-client.ts +297 -0
  313. package/src/core/host/resources/attachments.ts +22 -0
  314. package/src/core/host/resources/document.ts +101 -0
  315. package/src/core/hotkeys/actions.ts +2 -4
  316. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  317. package/src/core/network/connection.ts +4 -2
  318. package/src/core/network/useConnectionNotice.ts +8 -2
  319. package/src/core/run-app.tsx +2 -2
  320. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  321. package/src/core/runtime/__tests__/runtime.test.ts +18 -17
  322. package/src/core/runtime/runtime.ts +26 -30
  323. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  324. package/src/core/state/jotai.ts +1 -1
  325. package/src/core/static/files.ts +2 -0
  326. package/src/core/wasm/bridge.ts +2 -10
  327. package/src/core/wasm/store.ts +3 -3
  328. package/src/core/wasm/worker/bootstrap.ts +1 -1
  329. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  330. package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -29
  331. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +223 -0
  332. package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +194 -184
  333. package/src/core/websocket/connection-state.ts +60 -0
  334. package/src/core/websocket/state.ts +7 -0
  335. package/src/core/websocket/transports/__tests__/notebook.test.ts +502 -0
  336. package/src/core/websocket/transports/notebook.ts +213 -0
  337. package/src/core/websocket/types.ts +9 -39
  338. package/src/core/websocket/useNotebookClient.ts +89 -0
  339. package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +53 -224
  340. package/src/css/globals.css +5 -0
  341. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  342. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  343. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  344. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  345. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  346. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  347. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  348. package/src/hooks/useAsyncData.ts +3 -3
  349. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  350. package/src/hooks/useDebounce.ts +5 -8
  351. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  352. package/src/hooks/useDelayElapsed.ts +14 -10
  353. package/src/hooks/useElapsedTime.ts +3 -3
  354. package/src/hooks/useInputHistory.ts +4 -7
  355. package/src/hooks/useInternalStateWithSync.ts +6 -6
  356. package/src/hooks/useResizeObserver.ts +4 -7
  357. package/src/hooks/useScript.ts +67 -70
  358. package/src/hooks/useStateForKey.ts +93 -0
  359. package/src/mount.tsx +3 -3
  360. package/src/plugins/core/registerReactComponent.tsx +1 -1
  361. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  362. package/src/plugins/impl/DataTablePlugin.tsx +14 -15
  363. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  364. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  365. package/src/plugins/impl/FormPlugin.tsx +1 -1
  366. package/src/plugins/impl/RangeSliderPlugin.tsx +0 -1
  367. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  368. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  369. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +201 -1
  370. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  371. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  372. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
  373. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  374. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  375. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  376. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  377. package/src/plugins/impl/chat/chat-ui.tsx +12 -10
  378. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  379. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  380. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  381. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  382. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  383. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +0 -1
  384. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  385. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  386. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  387. package/src/plugins/impl/panel/utils.ts +17 -0
  388. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  389. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  390. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  391. package/src/stories/button.stories.tsx +1 -0
  392. package/src/stories/editor.stories.tsx +24 -17
  393. package/src/stories/form-wrapper.stories.tsx +2 -2
  394. package/src/stories/select.stories.tsx +2 -2
  395. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  396. package/src/utils/__tests__/arrays.test.ts +35 -0
  397. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  398. package/src/utils/__tests__/dates.test.ts +116 -0
  399. package/src/utils/__tests__/numbers.test.ts +48 -1
  400. package/src/utils/__tests__/objects.test.ts +21 -0
  401. package/src/utils/arrays.ts +10 -0
  402. package/src/utils/blob.ts +1 -1
  403. package/src/utils/dates.ts +94 -0
  404. package/src/utils/numbers.ts +28 -17
  405. package/src/utils/objects.ts +3 -7
  406. package/src/utils/once.ts +19 -19
  407. package/src/utils/zod-utils.ts +1 -3
  408. package/dist/assets/CellStatus-BP2mavAq.js +0 -1
  409. package/dist/assets/JsonOutput-D6HIIsCb.js +0 -11
  410. package/dist/assets/MarimoErrorOutput-D60XkCny.js +0 -6
  411. package/dist/assets/RunButton-CK3LhnP5.js +0 -1
  412. package/dist/assets/__vite-browser-external-CYO_P3S8.js +0 -1
  413. package/dist/assets/__vite-browser-external-FeDVGZU8.js +0 -1
  414. package/dist/assets/ai-model-dropdown-B0z6-zhp.js +0 -5
  415. package/dist/assets/any-language-editor-BLCAD3yx.js +0 -1
  416. package/dist/assets/api-BeC_VSGv.js +0 -1
  417. package/dist/assets/arrays-BqUx5USx.js +0 -1
  418. package/dist/assets/cell-editor-ADVfycpN.js +0 -21
  419. package/dist/assets/cell-link-LSth_8hT.js +0 -1
  420. package/dist/assets/chat-panel-DQ6HkWEF.js +0 -4
  421. package/dist/assets/chat-ui-Df8Vcj6n.js +0 -4
  422. package/dist/assets/circle-play-CYIb0qT6.js +0 -1
  423. package/dist/assets/code-visibility-B-_GvwR5.js +0 -1
  424. package/dist/assets/coerce-CpE6kUdX.js +0 -1
  425. package/dist/assets/column-preview-Cm2D6vyX.js +0 -1
  426. package/dist/assets/command-palette-DTSQ7gOC.js +0 -1
  427. package/dist/assets/components-CXfO-F47.js +0 -1
  428. package/dist/assets/config-BOKzA3tF.js +0 -1
  429. package/dist/assets/config-DX8Y2WbJ.js +0 -1
  430. package/dist/assets/config-VPSq73n8.js +0 -1
  431. package/dist/assets/edit-page-ChcSgpke.js +0 -9
  432. package/dist/assets/feedback-button-B2gKC5cn.js +0 -1
  433. package/dist/assets/file-explorer-panel-BEEK5lcl.js +0 -1
  434. package/dist/assets/home-page-DMHnsRxm.js +0 -4
  435. package/dist/assets/index-Bcdzex-3.css +0 -2
  436. package/dist/assets/index-s-M1OXjs.js +0 -29
  437. package/dist/assets/kiosk-mode-BZECfg9k.js +0 -1
  438. package/dist/assets/layout-DmcNEu3P.js +0 -9
  439. package/dist/assets/linear-0eij_aAG.js +0 -1
  440. package/dist/assets/link-NuFVR7G9.js +0 -1
  441. package/dist/assets/links-DkkL9DVa.js +0 -1
  442. package/dist/assets/numbers-Mk3-CcoU.js +0 -1
  443. package/dist/assets/packages-panel-BMGOmZkR.js +0 -1
  444. package/dist/assets/pair-CaQyK6En.js +0 -1
  445. package/dist/assets/panels-CxVG8-v2.js +0 -4
  446. package/dist/assets/precisionRound-Bq_nmf5u.js +0 -1
  447. package/dist/assets/process-output-D0KGq04R.js +0 -1
  448. package/dist/assets/react-vega-C3rYGtP6.js +0 -189
  449. package/dist/assets/requests-CgSY1X4a.js +0 -1
  450. package/dist/assets/run-page-DF3c5cDk.js +0 -1
  451. package/dist/assets/scratchpad-panel-Dwd6W5PE.js +0 -1
  452. package/dist/assets/session-panel-VdDhEBa-.js +0 -1
  453. package/dist/assets/state-BAJYRhmO.js +0 -1
  454. package/dist/assets/state-_Qai_QyX.js +0 -3
  455. package/dist/assets/tooltip-mT3CGest.js +0 -61
  456. package/dist/assets/tracing-Di6pUmX8.js +0 -1
  457. package/dist/assets/tracing-panel-PeRXHD8_.js +0 -2
  458. package/dist/assets/useAsyncData-C7aJJz0Y.js +0 -1
  459. package/dist/assets/useBoolean-BDD6_xQQ.js +0 -1
  460. package/dist/assets/useCellActionButton-B-9eG5z_.js +0 -1
  461. package/dist/assets/useDebounce-B_ccBZTp.js +0 -1
  462. package/dist/assets/useNotebookActions-NHN6A-cN.js +0 -1
  463. package/dist/assets/vega-component-CnadTgoE.js +0 -1
  464. package/dist/assets/vega-loader.browser-CLRkNgm7.js +0 -6
  465. package/dist/assets/ws-BXWY541V.js +0 -22
  466. package/src/core/state/__tests__/jotai.test.ts +0 -25
  467. package/src/core/websocket/__tests__/useWebSocket.test.ts +0 -49
  468. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  469. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -338
  470. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  471. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  472. package/src/core/websocket/transports/basic.ts +0 -102
  473. package/src/core/websocket/transports/sse.ts +0 -308
  474. package/src/core/websocket/transports/transport.ts +0 -61
  475. package/src/core/websocket/transports/ws.ts +0 -98
  476. package/src/core/websocket/useWebSocket.tsx +0 -88
@@ -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>
@@ -716,14 +721,10 @@ const ChatPanelBody = () => {
716
721
  );
717
722
 
718
723
  const isLoading = status === "submitted" || status === "streaming";
719
- // Read via a ref so the queue-vs-send decision stays correct even when it is
720
- // made after an `await` (e.g. resolving @-context), by which point the render
721
- // closure's `isLoading` may be stale.
722
- const isLoadingRef = useRef(isLoading);
723
- isLoadingRef.current = isLoading;
724
-
724
+ // useEvent reads the latest loading state, even when called after an await
725
+ // (e.g. resolving @-context) that outlives the original render's closure.
725
726
  const submitOrQueue = useEvent((parts: ChatMessagePart[]) => {
726
- if (isLoadingRef.current || hasQueuedRef.current) {
727
+ if (isLoading || hasQueuedRef.current) {
727
728
  enqueueUserMessage(parts);
728
729
  } else {
729
730
  sendUserMessage(parts);
@@ -294,16 +294,12 @@ export function useMessageQueue() {
294
294
  // invoked from a callback (e.g. `onFinish`) in the same tick as `enqueue`.
295
295
  const messagesRef = useRef<QueuedUserMessage[]>([]);
296
296
  const hasQueuedRef = useRef(false);
297
- messagesRef.current = messages;
298
- hasQueuedRef.current = messages.length > 0;
299
297
 
300
298
  const enqueue = useEvent((parts: ChatMessagePart[]) => {
301
- setMessages((prev) => {
302
- const next = [...prev, { id: generateUUID(), parts }];
303
- messagesRef.current = next;
304
- hasQueuedRef.current = true;
305
- return next;
306
- });
299
+ const next = [...messagesRef.current, { id: generateUUID(), parts }];
300
+ messagesRef.current = next;
301
+ hasQueuedRef.current = true;
302
+ setMessages(next);
307
303
  });
308
304
 
309
305
  const flushNext = useEvent((send: (parts: ChatMessagePart[]) => void) => {
@@ -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
@@ -14,6 +14,7 @@ import {
14
14
  renderCellValue,
15
15
  } from "../columns";
16
16
  import { getMimeValues, isMimeValue, MimeCell } from "../mime-cell";
17
+ import { buildHighlightPattern } from "../search-highlight";
17
18
  import type { FieldTypesWithExternalType } from "../types";
18
19
  import { uniformSample } from "../uniformSample";
19
20
  import { UrlDetector } from "../url-detector";
@@ -240,6 +241,7 @@ describe("generateColumns", () => {
240
241
  // "age" is a number column — should auto right-align
241
242
  // oxlint-disable-next-line typescript/no-explicit-any
242
243
  const cell = (columns[1].cell as any)({
244
+ table: { options: { meta: {} } },
243
245
  column: {
244
246
  columnDef: columns[1],
245
247
  },
@@ -251,6 +253,7 @@ describe("generateColumns", () => {
251
253
  // "name" is a string column — should remain left-aligned
252
254
  // oxlint-disable-next-line typescript/no-explicit-any
253
255
  const nameCell = (columns[0].cell as any)({
256
+ table: { options: { meta: {} } },
254
257
  column: {
255
258
  columnDef: columns[0],
256
259
  },
@@ -271,6 +274,7 @@ describe("generateColumns", () => {
271
274
  // "age" is numeric but explicitly set to left
272
275
  // oxlint-disable-next-line typescript/no-explicit-any
273
276
  const cell = (columns[1].cell as any)({
277
+ table: { options: { meta: {} } },
274
278
  column: {
275
279
  columnDef: columns[1],
276
280
  },
@@ -311,6 +315,7 @@ describe("generateColumns", () => {
311
315
  // Assuming getCellStyleClass is a function that returns a class name
312
316
  // oxlint-disable-next-line typescript/no-explicit-any
313
317
  const cell = (columns[0].cell as any)({
318
+ table: { options: { meta: {} } },
314
319
  column: {
315
320
  columnDef: columns[0],
316
321
  },
@@ -1359,3 +1364,103 @@ test("column_widths sets fixed size and meta.width", () => {
1359
1364
  expect(b?.size).toBeUndefined();
1360
1365
  expect(b?.meta?.width).toBeUndefined();
1361
1366
  });
1367
+
1368
+ describe("renderCellValue with highlightPattern", () => {
1369
+ const createColumn = (
1370
+ dataType: "string" | "number" | "boolean" | "date",
1371
+ dtype = "object",
1372
+ ) =>
1373
+ ({
1374
+ id: "col",
1375
+ columnDef: { meta: { dataType, dtype } },
1376
+ getColumnFormatting: () => undefined,
1377
+ getColumnWrapping: () => undefined,
1378
+ applyColumnFormatting: (value: unknown) => value,
1379
+ }) as unknown as Column<unknown>;
1380
+
1381
+ const renderCell = (
1382
+ column: Column<unknown>,
1383
+ value: unknown,
1384
+ highlightQuery: string,
1385
+ ) =>
1386
+ render(
1387
+ <I18nProvider locale="en-US">
1388
+ <TooltipProvider>
1389
+ {renderCellValue({
1390
+ column,
1391
+ renderValue: () => value,
1392
+ getValue: () => value,
1393
+ selectCell: undefined,
1394
+ cellStyles: "",
1395
+ highlightPattern: buildHighlightPattern(highlightQuery),
1396
+ })}
1397
+ </TooltipProvider>
1398
+ </I18nProvider>,
1399
+ );
1400
+
1401
+ const markTexts = (container: HTMLElement) =>
1402
+ [...container.querySelectorAll("mark")].map((m) => m.textContent);
1403
+
1404
+ it("marks a match in a string cell", () => {
1405
+ const { container } = renderCell(
1406
+ createColumn("string"),
1407
+ "Alice Smith",
1408
+ "smi",
1409
+ );
1410
+ expect(markTexts(container)).toEqual(["Smi"]);
1411
+ });
1412
+
1413
+ it("marks only the middle text when the string has edge whitespace", () => {
1414
+ const { container } = renderCell(createColumn("string"), " a b ", " ");
1415
+ expect(markTexts(container)).toEqual([" "]);
1416
+ const edgeMarkers = container.querySelectorAll("span[aria-label]");
1417
+ expect(edgeMarkers).toHaveLength(2);
1418
+ for (const marker of edgeMarkers) {
1419
+ expect(marker.querySelector("mark")).toBeNull();
1420
+ }
1421
+ });
1422
+
1423
+ it("marks a match in the locale display of a number", () => {
1424
+ const { container } = renderCell(createColumn("number"), 1234.5, "234");
1425
+ expect(markTexts(container)).toEqual(["234"]);
1426
+ expect(container.textContent).toBe("1,234.5");
1427
+ });
1428
+
1429
+ it("does not mark a number when the locale display hides the raw match", () => {
1430
+ const { container } = renderCell(createColumn("number"), 1234, "1234");
1431
+ expect(markTexts(container)).toEqual([]);
1432
+ expect(container.textContent).toBe("1,234");
1433
+ });
1434
+
1435
+ it("marks a boolean cell", () => {
1436
+ const { container } = renderCell(createColumn("boolean"), false, "false");
1437
+ expect(markTexts(container)).toEqual(["False"]);
1438
+ });
1439
+
1440
+ it("marks a date cell by its display string", () => {
1441
+ const { container } = renderCell(
1442
+ createColumn("date", "date"),
1443
+ new Date(2026, 0, 15),
1444
+ "2026",
1445
+ );
1446
+ expect(markTexts(container)).toEqual(["2026"]);
1447
+ });
1448
+
1449
+ it("renders no mark for an empty query", () => {
1450
+ for (const [column, value] of [
1451
+ [createColumn("string"), "Alice"],
1452
+ [createColumn("number"), 12],
1453
+ [createColumn("boolean"), false],
1454
+ [createColumn("date", "date"), new Date(2026, 0, 15)],
1455
+ ] as const) {
1456
+ const { container } = renderCell(column, value, "");
1457
+ expect(markTexts(container)).toEqual([]);
1458
+ }
1459
+ });
1460
+
1461
+ it("renders no mark in the trigger of a long-string popout", () => {
1462
+ const value = "x".repeat(60);
1463
+ const { container } = renderCell(createColumn("string"), value, "xxx");
1464
+ expect(markTexts(container)).toEqual([]);
1465
+ });
1466
+ });
@@ -0,0 +1,130 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { type Cell, createTable, getCoreRowModel } from "@tanstack/react-table";
4
+ import { fireEvent, render, screen } from "@testing-library/react";
5
+ import { useRef } from "react";
6
+ import { expect, it, vi } from "vitest";
7
+ import { SetupMocks } from "@/__mocks__/common";
8
+ import { DataTableContextMenu } from "../context-menu";
9
+
10
+ SetupMocks.resizeObserver();
11
+
12
+ const rows = [{ value: "first" }, { value: "second" }];
13
+ const columns = [
14
+ { accessorKey: "value", meta: { filterType: "text" as const } },
15
+ ];
16
+
17
+ function createCells() {
18
+ const table = createTable({
19
+ data: rows,
20
+ columns,
21
+ locale: "en-US",
22
+ getCoreRowModel: getCoreRowModel(),
23
+ onColumnFiltersChange: () => undefined,
24
+ state: {},
25
+ onStateChange: () => undefined,
26
+ renderFallbackValue: null,
27
+ });
28
+ return table.getRowModel().rows.map((row) => row.getAllCells()[0]);
29
+ }
30
+
31
+ function Harness({ cells }: { cells: Cell<(typeof rows)[number], unknown>[] }) {
32
+ const contextMenuRef = useRef<Cell<(typeof rows)[number], unknown> | null>(
33
+ null,
34
+ );
35
+ const tableRef = useRef<HTMLTableSectionElement>(null);
36
+ const tableBody = (
37
+ <tbody ref={tableRef}>
38
+ <tr>
39
+ <td>empty</td>
40
+ </tr>
41
+ {cells.map((cell) => {
42
+ return (
43
+ <tr key={cell.id}>
44
+ <td
45
+ data-cell-id={cell.id}
46
+ onContextMenu={() => {
47
+ contextMenuRef.current = cell;
48
+ }}
49
+ >
50
+ {String(cell.getValue())}
51
+ </td>
52
+ </tr>
53
+ );
54
+ })}
55
+ </tbody>
56
+ );
57
+ return (
58
+ <table>
59
+ <DataTableContextMenu
60
+ contextMenuRef={contextMenuRef}
61
+ tableRef={tableRef}
62
+ tableBody={tableBody}
63
+ copyAllCells={() => undefined}
64
+ />
65
+ </table>
66
+ );
67
+ }
68
+
69
+ it("filters the latest right-clicked cell when the menu is already open", () => {
70
+ const onFilter = vi.fn();
71
+ const table = createTable({
72
+ data: rows,
73
+ columns,
74
+ locale: "en-US",
75
+ getCoreRowModel: getCoreRowModel(),
76
+ onColumnFiltersChange: onFilter,
77
+ state: {},
78
+ onStateChange: () => undefined,
79
+ renderFallbackValue: null,
80
+ });
81
+ render(
82
+ <Harness
83
+ cells={table.getRowModel().rows.map((row) => row.getAllCells()[0])}
84
+ />,
85
+ );
86
+
87
+ const firstCell = screen.getByText("first");
88
+ const secondCell = screen.getByText("second");
89
+ fireEvent.contextMenu(firstCell);
90
+ expect(firstCell).toHaveClass("bg-(--green-4)");
91
+ expect(
92
+ screen.getByRole("menuitem", { name: "Filter by this value" }),
93
+ ).toBeInTheDocument();
94
+ fireEvent.contextMenu(secondCell);
95
+ expect(firstCell).not.toHaveClass("bg-(--green-4)");
96
+ expect(secondCell).toHaveClass("bg-(--green-4)");
97
+ fireEvent.click(
98
+ screen.getByRole("menuitem", { name: "Filter by this value" }),
99
+ );
100
+ expect(firstCell).not.toHaveClass("bg-(--green-4)");
101
+ expect(secondCell).not.toHaveClass("bg-(--green-4)");
102
+
103
+ const update = onFilter.mock.calls.at(-1)?.[0];
104
+ expect(update([])).toEqual([
105
+ {
106
+ id: "value",
107
+ value: { type: "text", operator: "equals", text: "second" },
108
+ },
109
+ ]);
110
+ });
111
+
112
+ it("does not reuse a previous cell after the menu closes", () => {
113
+ render(<Harness cells={createCells()} />);
114
+
115
+ const firstCell = screen.getByText("first");
116
+ fireEvent.contextMenu(firstCell);
117
+ expect(firstCell).toHaveClass("bg-(--green-4)");
118
+ fireEvent.keyDown(document.activeElement ?? document.body, {
119
+ key: "Escape",
120
+ });
121
+ expect(firstCell).not.toHaveClass("bg-(--green-4)");
122
+ expect(screen.queryByRole("menu")).not.toBeInTheDocument();
123
+
124
+ // Right-clicking outside any data cell must not resurrect the old one
125
+ fireEvent.contextMenu(screen.getByText("empty"));
126
+ expect(firstCell).not.toHaveClass("bg-(--green-4)");
127
+ expect(
128
+ screen.queryByRole("menuitem", { name: "Filter by this value" }),
129
+ ).not.toBeInTheDocument();
130
+ });
@@ -0,0 +1,176 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
4
+ import { buildCellFilter } from "../context-menu";
5
+
6
+ describe("buildCellFilter", () => {
7
+ // Use a non-UTC, non-DST-ambiguous offset so date-only parsing bugs
8
+ // (UTC vs. local midnight) can't hide behind a host machine that
9
+ // happens to run in UTC.
10
+ let originalTimezone: string | undefined;
11
+ beforeAll(() => {
12
+ originalTimezone = process.env.TZ;
13
+ process.env.TZ = "America/New_York";
14
+ });
15
+ afterAll(() => {
16
+ process.env.TZ = originalTimezone;
17
+ });
18
+
19
+ it("builds an equality filter for a normal date value", () => {
20
+ const filter = buildCellFilter({
21
+ type: "date",
22
+ value: "2024-01-15",
23
+ comparison: "eq",
24
+ dataType: "date",
25
+ dtype: "date",
26
+ });
27
+ expect(filter).toEqual({
28
+ type: "date",
29
+ operator: "==",
30
+ // Must be local midnight, not `new Date("2024-01-15")` (UTC
31
+ // midnight), or the filter shifts by a day in UTC- timezones.
32
+ value: new Date(2024, 0, 15),
33
+ });
34
+ });
35
+
36
+ it("returns undefined for an empty-string date value in a non-temporal-looking context", () => {
37
+ const filter = buildCellFilter({
38
+ type: "date",
39
+ value: "",
40
+ comparison: "eq",
41
+ dataType: "date",
42
+ dtype: "date",
43
+ });
44
+ expect(filter).toBeUndefined();
45
+ });
46
+
47
+ it("returns undefined for an unparsable date value instead of an Invalid Date filter", () => {
48
+ const filter = buildCellFilter({
49
+ type: "date",
50
+ value: "not-a-date",
51
+ comparison: "eq",
52
+ dataType: "date",
53
+ dtype: "date",
54
+ });
55
+ expect(filter).toBeUndefined();
56
+ });
57
+
58
+ it("builds an equality filter for a normal datetime value", () => {
59
+ const filter = buildCellFilter({
60
+ type: "datetime",
61
+ value: "2024-01-15 08:30:00",
62
+ comparison: "eq",
63
+ dataType: "datetime",
64
+ dtype: "datetime64[ns]",
65
+ });
66
+ expect(filter).toEqual({
67
+ type: "datetime",
68
+ operator: "==",
69
+ value: new Date(2024, 0, 15, 8, 30, 0),
70
+ });
71
+ });
72
+
73
+ it("returns undefined for an unparsable datetime value", () => {
74
+ const filter = buildCellFilter({
75
+ type: "datetime",
76
+ value: "not-a-datetime",
77
+ comparison: "eq",
78
+ dataType: "datetime",
79
+ dtype: "datetime64[ns]",
80
+ });
81
+ expect(filter).toBeUndefined();
82
+ });
83
+
84
+ it("builds an equality filter for a normal time value", () => {
85
+ const filter = buildCellFilter({
86
+ type: "time",
87
+ value: "08:30:00",
88
+ comparison: "eq",
89
+ dataType: "time",
90
+ });
91
+ expect(filter).toEqual({
92
+ type: "time",
93
+ operator: "==",
94
+ value: new Date(1970, 0, 1, 8, 30, 0),
95
+ });
96
+ });
97
+
98
+ it("returns undefined instead of an unparsable time value", () => {
99
+ const filter = buildCellFilter({
100
+ type: "time",
101
+ value: "25:00:00",
102
+ comparison: "eq",
103
+ dataType: "time",
104
+ });
105
+ expect(filter).toBeUndefined();
106
+ });
107
+
108
+ it("maps a NaT date value to is_null instead of an invalid Date filter", () => {
109
+ const filter = buildCellFilter({
110
+ type: "date",
111
+ value: "NaT",
112
+ comparison: "eq",
113
+ dataType: "date",
114
+ dtype: "date",
115
+ });
116
+ expect(filter).toEqual({ type: "date", operator: "is_null" });
117
+ });
118
+
119
+ it("maps a NaT datetime value to is_not_null for the neq comparison", () => {
120
+ const filter = buildCellFilter({
121
+ type: "datetime",
122
+ value: "NaT",
123
+ comparison: "neq",
124
+ dataType: "datetime",
125
+ dtype: "datetime64[ns]",
126
+ });
127
+ expect(filter).toEqual({ type: "datetime", operator: "is_not_null" });
128
+ });
129
+
130
+ it("does not treat the string 'NaT' in a non-temporal column as a sentinel", () => {
131
+ const filter = buildCellFilter({
132
+ type: "text",
133
+ value: "NaT",
134
+ comparison: "eq",
135
+ dataType: "string",
136
+ });
137
+ expect(filter).toEqual({ type: "text", operator: "equals", text: "NaT" });
138
+ });
139
+
140
+ it("maps an actual null value to is_null", () => {
141
+ const filter = buildCellFilter({
142
+ type: "number",
143
+ value: null,
144
+ comparison: "eq",
145
+ dataType: "number",
146
+ });
147
+ expect(filter).toEqual({ type: "number", operator: "is_null" });
148
+ });
149
+
150
+ it("builds a literal (always-false) equality filter for a NaN number value", () => {
151
+ // NaN is a real float value, not a missing-value marker like null/NaT,
152
+ // and the backend's is_null() (narwhals/polars semantics) doesn't match
153
+ // it, so it falls through to a normal (if unsatisfiable) == filter.
154
+ const filter = buildCellFilter({
155
+ type: "number",
156
+ value: Number.NaN,
157
+ comparison: "eq",
158
+ dataType: "number",
159
+ });
160
+ expect(filter).toEqual({
161
+ type: "number",
162
+ operator: "==",
163
+ value: Number.NaN,
164
+ });
165
+ });
166
+
167
+ it("builds a literal equality filter for a normal number value", () => {
168
+ const filter = buildCellFilter({
169
+ type: "number",
170
+ value: 42,
171
+ comparison: "eq",
172
+ dataType: "number",
173
+ });
174
+ expect(filter).toEqual({ type: "number", operator: "==", value: 42 });
175
+ });
176
+ });
@@ -1,7 +1,30 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
  import { describe, expect, it } from "vitest";
3
3
  import { dateToLocalISODate } from "@/utils/dates";
4
- import { parsePastedRange } from "../date-filter-inputs";
4
+ import { parsePastedDate, parsePastedRange } from "../date-filter-inputs";
5
+
6
+ describe("parsePastedDate", () => {
7
+ it("parses a time-only string for the time filter type", () => {
8
+ const result = parsePastedDate("time", "2:30 PM");
9
+ expect(result).toEqual(new Date(1970, 0, 1, 14, 30));
10
+ });
11
+
12
+ it("parses a local ISO datetime for the datetime filter type", () => {
13
+ const result = parsePastedDate("datetime", "2026-02-01T08:30:00");
14
+ expect(result).toEqual(new Date(2026, 1, 1, 8, 30, 0));
15
+ });
16
+
17
+ it("falls back to the Date constructor for other formats", () => {
18
+ const result = parsePastedDate("datetime", "Feb 1, 2026 08:30:00");
19
+ expect(result).toBeDefined();
20
+ expect(result?.getFullYear()).toBe(2026);
21
+ });
22
+
23
+ it("returns undefined for an empty or unparsable string", () => {
24
+ expect(parsePastedDate("date", "")).toBeUndefined();
25
+ expect(parsePastedDate("time", "not-a-time")).toBeUndefined();
26
+ });
27
+ });
5
28
 
6
29
  describe("parsePastedRange", () => {
7
30
  it.each([
@@ -0,0 +1,72 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { render, screen } from "@testing-library/react";
3
+ import type { ReactNode } from "react";
4
+ import { I18nProvider } from "react-aria";
5
+ import { afterEach, describe, expect, it, vi } from "vitest";
6
+ import { DatePopover } from "../date-popover";
7
+
8
+ vi.mock("@/components/ui/tooltip", () => ({
9
+ Tooltip: ({ content }: { content: ReactNode }) => content,
10
+ }));
11
+
12
+ afterEach(() => {
13
+ vi.restoreAllMocks();
14
+ });
15
+
16
+ describe("DatePopover", () => {
17
+ it.each([0, 123])(
18
+ "updates the local timezone and time on rerender with %i milliseconds",
19
+ (milliseconds) => {
20
+ const NativeDateTimeFormat = Intl.DateTimeFormat;
21
+ let systemTimezone = "America/Los_Angeles";
22
+ vi.spyOn(Intl, "DateTimeFormat").mockImplementation(
23
+ function (locales, options) {
24
+ return new NativeDateTimeFormat(locales, {
25
+ ...options,
26
+ timeZone: options?.timeZone ?? systemTimezone,
27
+ });
28
+ },
29
+ );
30
+
31
+ const date = new Date("2026-01-15T12:00:00Z");
32
+ date.setUTCMilliseconds(milliseconds);
33
+ const content = () => (
34
+ <I18nProvider locale="en-US">
35
+ <DatePopover date={date} type="datetime">
36
+ timestamp
37
+ </DatePopover>
38
+ </I18nProvider>
39
+ );
40
+ const { rerender } = render(content());
41
+ const previousTime =
42
+ screen.getByText(systemTimezone).nextElementSibling?.textContent;
43
+ const utcTime = screen.getByText("UTC").nextElementSibling?.textContent;
44
+
45
+ systemTimezone = "Asia/Tokyo";
46
+ rerender(content());
47
+
48
+ const expected = new NativeDateTimeFormat("en-US", {
49
+ timeZone: systemTimezone,
50
+ ...(milliseconds
51
+ ? {
52
+ year: "numeric",
53
+ month: "2-digit",
54
+ day: "2-digit",
55
+ hour: "2-digit",
56
+ minute: "2-digit",
57
+ second: "2-digit",
58
+ fractionalSecondDigits: 3,
59
+ }
60
+ : { dateStyle: "long", timeStyle: "medium" }),
61
+ }).format(date);
62
+ expect(screen.queryByText("America/Los_Angeles")).toBeNull();
63
+ expect(
64
+ screen.getByText(systemTimezone).nextElementSibling?.textContent,
65
+ ).toBe(expected);
66
+ expect(expected).not.toBe(previousTime);
67
+ expect(screen.getByText("UTC").nextElementSibling?.textContent).toBe(
68
+ utcTime,
69
+ );
70
+ },
71
+ );
72
+ });