@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.
- package/dist/assets/CellStatus-fK4n2NPc.js +1 -0
- package/dist/assets/{ConnectedDataExplorerComponent-t8fH_TNc.js → ConnectedDataExplorerComponent-Dft8R6l5.js} +1 -1
- package/dist/assets/{ErrorBoundary-Gl5nCA8C.js → ErrorBoundary-CAEGdoFW.js} +2 -2
- package/dist/assets/JsonOutput-CyAKmZDD.js +11 -0
- package/dist/assets/{LazyAnyLanguageCodeMirror-CKcCbG2G.js → LazyAnyLanguageCodeMirror-BPYE76jR.js} +2 -2
- package/dist/assets/MarimoErrorOutput-CgZvP0p0.js +10 -0
- package/dist/assets/{RenderHTML-BbOqmHy3.js → RenderHTML-DQvxDDNS.js} +1 -1
- package/dist/assets/RunButton-BBDnDL6G.js +1 -0
- package/dist/assets/__vite-browser-external-8vDMR0Kk.js +1 -0
- package/dist/assets/__vite-browser-external-mKcnhO20.js +1 -0
- package/dist/assets/{add-cell-with-ai-8UBC0xwR.js → add-cell-with-ai-Clg5FELx.js} +12 -12
- package/dist/assets/{add-connection-dialog-DXQnd6eB.js → add-connection-dialog-BqdWmD-7.js} +2 -2
- package/dist/assets/{agent-panel-D7kMZLG-.js → agent-panel-5RxG5Y82.js} +5 -5
- package/dist/assets/ai-model-dropdown-Bvl1ktQq.js +13 -0
- package/dist/assets/{alert-dialog-BaPGzeOK.js → alert-dialog-C-sWmKva.js} +1 -1
- package/dist/assets/any-language-editor-DUMe1UCk.js +1 -0
- package/dist/assets/{app-config-button-BKEG4x9u.js → app-config-button-0QTeQ2Mv.js} +1 -1
- package/dist/assets/arrays-0R2reF2y.js +1 -0
- package/dist/assets/{blob-D5nfNHOo.js → blob-5VcB8K_t.js} +1 -1
- package/dist/assets/{button-COI-NGrx.js → button-D8gVKwe-.js} +1 -1
- package/dist/assets/{cache-panel-DTY1dqvi.js → cache-panel-BV-c7ed0.js} +1 -1
- package/dist/assets/{capabilities-BJvf-MD4.js → capabilities-Pq1YNPlK.js} +1 -1
- package/dist/assets/cell-editor-Byw5y-oI.js +42 -0
- package/dist/assets/cell-link-C5yYyzuF.js +1 -0
- package/dist/assets/{cells-BRkCaSsh.js → cells-D_11MjVB.js} +60 -60
- package/dist/assets/{chat-display-Dnagy4Nv.js → chat-display-BCR_ySiV.js} +1 -1
- package/dist/assets/chat-panel-RPRvBsaU.js +4 -0
- package/dist/assets/chat-ui-CGviVaRA.js +4 -0
- package/dist/assets/{circle-DXf-VWZG.js → circle-B6D_6D8w.js} +1 -1
- package/dist/assets/code-visibility-DYDagVy7.js +1 -0
- package/dist/assets/coerce-DWjFeXOY.js +1 -0
- package/dist/assets/column-preview-xe533-Cn.js +1 -0
- package/dist/assets/command-palette-BB9bWdWp.js +1 -0
- package/dist/assets/{common-Cu6CDbuw.js → common-CaeItbcI.js} +1 -1
- package/dist/assets/components-CBE8cOLK.js +1 -0
- package/dist/assets/{components-CP_UoFtT.js → components-DCo7mAWO.js} +1 -1
- package/dist/assets/{components-CGw16R_N.js → components-G6frbw0c.js} +1 -1
- package/dist/assets/config-CD1IuZFJ.js +1 -0
- package/dist/assets/config-DMz7oN0r.js +1 -0
- package/dist/assets/config-DpY86_Yq.js +1 -0
- package/dist/assets/{copy-D8O_CYCG.js → copy-KlhazlhB.js} +1 -1
- package/dist/assets/{copy-icon-BIiRXh_3.js → copy-icon-8-a5vovc.js} +1 -1
- package/dist/assets/{createReducer-DgRODTW6.js → createReducer-CaI_Oa0v.js} +1 -1
- package/dist/assets/{datasource-Cq6x_5ML.js → datasource-deuXnlU-.js} +1 -1
- package/dist/assets/{dates-BbFQ2oE4.js → dates-CQMwFko2.js} +1 -1
- package/dist/assets/{dependency-graph-panel--Kgtktcw.js → dependency-graph-panel-CIykLnJe.js} +2 -2
- package/dist/assets/{dist-iaThVDkA.js → dist-jlhMTKo_.js} +1 -1
- package/dist/assets/{documentation-panel-BHmbeznw.js → documentation-panel-2PQ8kQLa.js} +1 -1
- package/dist/assets/{download-DCJXy66H.js → download-dK52bLxZ.js} +1 -1
- package/dist/assets/{dropdown-menu-C1rnl-0o.js → dropdown-menu-DS6umAnH.js} +1 -1
- package/dist/assets/edit-page-DrElzw10.js +9 -0
- package/dist/assets/{error-banner-Cd69vvyX.js → error-banner-B5kzVf_0.js} +1 -1
- package/dist/assets/{error-entries-Dk3VZ-OK.js → error-entries-3GVk2KjS.js} +1 -1
- package/dist/assets/{error-panel-BPRYmC5J.js → error-panel-CzDRGPeC.js} +1 -1
- package/dist/assets/{errors-DCrsa1nH.js → errors-CNsgAw6m.js} +1 -1
- package/dist/assets/{es-Cqst59WX.js → es-N9SHpmG7.js} +1 -1
- package/dist/assets/{feedback-button-Bik2-e33.js → feedback-button-C5NgWpFg.js} +2 -2
- package/dist/assets/feedback-button-CONwLFpw.js +1 -0
- package/dist/assets/file-explorer-panel-B50xDnTZ.js +1 -0
- package/dist/assets/{file-name-input-Cdc-TVqO.js → file-name-input-DqwMSgGU.js} +1 -1
- package/dist/assets/{floating-outline-XD9FAV-f.js → floating-outline-ChmAuvxz.js} +1 -1
- package/dist/assets/{formats-yZ06h1Pl.js → formats-zylfwejq.js} +1 -1
- package/dist/assets/{formatting-BqrP-n49.js → formatting-uom1es4b.js} +1 -1
- package/dist/assets/{fullscreen-B9tHcfpV.js → fullscreen-BWTs8pYZ.js} +1 -1
- package/dist/assets/{gallery-page-Dm6mGwBi.js → gallery-page-CiL8aMS1.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-2n7Ig5Jm.js → ganttDiagram-EL5Y4UJY-CYqkCfFG.js} +1 -1
- package/dist/assets/{glide-data-editor-J9CAb70d.js → glide-data-editor-CqRclBkp.js} +2 -2
- package/dist/assets/{glide-utils-CIrAxdjv.js → glide-utils-HFqhvWHI.js} +2 -2
- package/dist/assets/home-page-BgZVl4dP.js +1 -0
- package/dist/assets/{hooks-93IwHd2u.js → hooks-DM5n7Osw.js} +1 -1
- package/dist/assets/{html-to-image-DK9Zl1lU.js → html-to-image-D_0rd3_B.js} +1 -1
- package/dist/assets/index-BnhSnwvB.css +2 -0
- package/dist/assets/index-EG2n8Oht.js +30 -0
- package/dist/assets/{input-BoBxR50a.js → input-DmHMEZGh.js} +1 -1
- package/dist/assets/kiosk-mode-DFS6HopF.js +1 -0
- package/dist/assets/layout-BpP8nTjb.js +9 -0
- package/dist/assets/linear-Bma8J3iM.js +1 -0
- package/dist/assets/links-DX2w97N9.js +1 -0
- package/dist/assets/{logs-panel-ABM16htT.js → logs-panel-B5eVAZtH.js} +1 -1
- package/dist/assets/{markdown-renderer-BG3sp2rs.js → markdown-renderer-BWUzqkJM.js} +1 -1
- package/dist/assets/{menu-items-BrwnTgyK.js → menu-items-BgUyNUpu.js} +1 -1
- package/dist/assets/{mermaid-a3xYymTy.js → mermaid-BKXlWk4F.js} +2 -2
- package/dist/assets/{name-cell-input-BgEBmqyL.js → name-cell-input-DXW68gRW.js} +1 -1
- package/dist/assets/numbers-CsZVw3Dc.js +1 -0
- package/dist/assets/{objects-snlMn4yk.js → objects-rLPlQ3X_.js} +1 -1
- package/dist/assets/{once-Haz_uxEL.js → once-6KNTRW0u.js} +1 -1
- package/dist/assets/{outline-panel-DCXQ3_F6.js → outline-panel-DPxJhJ_A.js} +1 -1
- package/dist/assets/{package-data-dv4FBHxR.js → package-data-CKPmBDtg.js} +1 -1
- package/dist/assets/packages-panel-gBE2FWQr.js +1 -0
- package/dist/assets/{panel-accordion-state-C1sdPp0m.js → panel-accordion-state-CCF5Gxnc.js} +1 -1
- package/dist/assets/panels-Dr6qMCvU.js +105 -0
- package/dist/assets/precisionRound-BxlUs8G4.js +1 -0
- package/dist/assets/process-output-Cw9IDcKQ.js +1 -0
- package/dist/assets/{quadrantDiagram-AXDQQJYC-yBhCDI7E.js → quadrantDiagram-AXDQQJYC-D9KH5Zge.js} +1 -1
- package/dist/assets/react-vega-BTNeboR2.js +189 -0
- package/dist/assets/{readonly-code-display-Do50PHI8.js → readonly-code-display-DYcZQ28T.js} +1 -1
- package/dist/assets/{readonly-python-code-DCSMIsTX.js → readonly-python-code-RFuQP50v.js} +1 -1
- package/dist/assets/{renderShortcut-BkVJQ_Zh.js → renderShortcut-rUwGqKgC.js} +1 -1
- package/dist/assets/{requests-lt0JK8Jt.js → requests-BIFR-h4M.js} +1 -1
- package/dist/assets/requests-DuLJN9Tf.js +1 -0
- package/dist/assets/{reveal-component-ltaepAqw.js → reveal-component-yRrDmCQz.js} +37 -37
- package/dist/assets/run-page-B69M4-zE.js +1 -0
- package/dist/assets/{runs-BLp5jezv.js → runs-C49hleBy.js} +1 -1
- package/dist/assets/{runtime-details-state-CSPobEdR.js → runtime-details-state-pobtu6TO.js} +1 -1
- package/dist/assets/{save-worker-BbUSwZRG.js → save-worker-pCkSHhuz.js} +1 -1
- package/dist/assets/{schemas-Dv42wLFq.js → schemas-ChRqMOTL.js} +1 -1
- package/dist/assets/scratchpad-panel-H44_pvb_.js +1 -0
- package/dist/assets/{scroll-to-bottom-button-1FyowpWV.js → scroll-to-bottom-button-CIkON2WJ.js} +1 -1
- package/dist/assets/{secrets-panel-Bctbtkrp.js → secrets-panel-bcfEKxRx.js} +1 -1
- package/dist/assets/{select-DTBn5eTb.js → select-3CVGlYrC.js} +1 -1
- package/dist/assets/{session-DqoWmb7X.js → session-CMLIzZKw.js} +1 -1
- package/dist/assets/session-panel-Bnd1iXkO.js +1 -0
- package/dist/assets/{snippets-panel-Bh1oTw4m.js → snippets-panel-DKApxKx9.js} +1 -1
- package/dist/assets/{spec-BfSBcO5B.js → spec-uCpVvaAR.js} +1 -1
- package/dist/assets/{state-uwRvJLb8.js → state-3mcyTS9q.js} +1 -1
- package/dist/assets/state-B_MCKzBL.js +1 -0
- package/dist/assets/state-Bl9BiY_s.js +3 -0
- package/dist/assets/{state-C3oDoyFe.js → state-C5WAeuTu.js} +1 -1
- package/dist/assets/{state-DxQnC_1I.js → state-Cv2JW3l9.js} +1 -1
- package/dist/assets/{state-XsPO8hNi.js → state-Dv059B3A.js} +1 -1
- package/dist/assets/{strings-B2vv-ENv.js → strings-C25fhMR_.js} +1 -1
- package/dist/assets/{swiper-component-a07domrZ.js → swiper-component-CRGRy5_O.js} +1 -1
- package/dist/assets/{switch-CTaiAjrz.js → switch-DYjBfpNJ.js} +1 -1
- package/dist/assets/{tabs-BuQlZFbt.js → tabs-CY8E4r9U.js} +1 -1
- package/dist/assets/{terminal-Ck_vK-34.js → terminal-Ca-kubKb.js} +1 -1
- package/dist/assets/{time-MgToa8VM.js → time-DswSXBDk.js} +1 -1
- package/dist/assets/{toast-BGzLGnvw.js → toast-Gzxi-le_.js} +1 -1
- package/dist/assets/tooltip-Brya4Ghn.js +61 -0
- package/dist/assets/{tooltip-C9TuY18o.js → tooltip-CkGm8X7m.js} +1 -1
- package/dist/assets/tracing-Clwy4SlW.js +1 -0
- package/dist/assets/tracing-panel-BwjLG_aC.js +2 -0
- package/dist/assets/{tree-actions-GM5E_bUs.js → tree-actions-BZNTQ12N.js} +1 -1
- package/dist/assets/{use-restore-focus-BxEGwIbk.js → use-restore-focus-DdS7Tl0F.js} +1 -1
- package/dist/assets/{useAddCell-GR7eEQrT.js → useAddCell-Cjuqk2YR.js} +1 -1
- package/dist/assets/useAsyncData-qvoPYHtx.js +1 -0
- package/dist/assets/useBoolean-Dd2nWXFJ.js +1 -0
- package/dist/assets/useCellActionButton-MoWLvuGe.js +1 -0
- package/dist/assets/useDebounce-DwTQ_1Pu.js +1 -0
- package/dist/assets/{useDeepCompareMemoize-DFIVN9dn.js → useDeepCompareMemoize-B2DVK2De.js} +1 -1
- package/dist/assets/useDelayElapsed-BlsDT4TV.js +1 -0
- package/dist/assets/{useDeleteCell-2_bMClHd.js → useDeleteCell-BC-MW7d1.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-k3C3M18S.js → useDependencyPanelTab-CZU7i-B3.js} +1 -1
- package/dist/assets/{useEvent-Dfcbgjhg.js → useEvent-BDjgaBtz.js} +1 -1
- package/dist/assets/{useEventListener-D9cftcHj.js → useEventListener-CjDeLqFC.js} +1 -1
- package/dist/assets/{useIframeCapabilities-CwTkczVN.js → useIframeCapabilities-Czr9e4uG.js} +1 -1
- package/dist/assets/{useInstallPackage-iX1eufdl.js → useInstallPackage-wcDq9hYT.js} +1 -1
- package/dist/assets/{useInterval--x3s1VpI.js → useInterval-DJdQmXnq.js} +1 -1
- package/dist/assets/useNotebookActions-wFxVdRXQ.js +1 -0
- package/dist/assets/{useRunCells-B2vUhREg.js → useRunCells-ByaWxoIt.js} +1 -1
- package/dist/assets/{useSplitCell-DEb03htp.js → useSplitCell-YJIystwv.js} +1 -1
- package/dist/assets/useStateForKey-CGMJQT8x.js +1 -0
- package/dist/assets/{useTheme-COhSHkCj.js → useTheme-BAA27lh7.js} +1 -1
- package/dist/assets/{utils-CaQYvazl.js → utils-DfElfBpz.js} +1 -1
- package/dist/assets/vega-component-BeyxAJad.js +1 -0
- package/dist/assets/{vega-embed-container-DJPNkHPb.js → vega-embed-container-DtArSJz1.js} +1 -1
- package/dist/assets/vega-loader.browser-Bu25TKax.js +6 -0
- package/dist/assets/{worker-DTQ7oH2A.js → worker-bSCjCvwz.js} +1 -1
- package/dist/assets/{write-secret-modal-DzKAInL2.js → write-secret-modal-BEx-EUlh.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-KbGzG6-u.js → xychartDiagram-S5SC5T6Z-CeRnBS5e.js} +1 -1
- package/dist/index.html +80 -79
- package/package.json +8 -5
- package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
- package/src/__tests__/host-imports.test.ts +101 -0
- package/src/__tests__/mount.test.ts +8 -8
- package/src/components/app-config/ai-config.tsx +7 -2
- package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
- package/src/components/chat/acp/__tests__/state.test.ts +2 -2
- package/src/components/chat/acp/blocks.tsx +2 -2
- package/src/components/chat/chat-components.tsx +2 -0
- package/src/components/chat/chat-panel.tsx +9 -8
- package/src/components/chat/chat-utils.ts +4 -8
- package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
- package/src/components/data-table/__tests__/columns.test.tsx +105 -0
- package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
- package/src/components/data-table/__tests__/context-menu.test.ts +176 -0
- package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +24 -1
- package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
- package/src/components/data-table/__tests__/export-actions.test.tsx +434 -6
- package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
- package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
- package/src/components/data-table/cell-selection/feature.ts +2 -2
- package/src/components/data-table/cell-selection/types.ts +2 -2
- package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
- package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
- package/src/components/data-table/charts/charts.tsx +35 -25
- package/src/components/data-table/charts/storage.ts +16 -5
- package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
- package/src/components/data-table/column-summary/column-summary.tsx +2 -4
- package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
- package/src/components/data-table/columns.tsx +45 -46
- package/src/components/data-table/context-menu.tsx +161 -46
- package/src/components/data-table/data-table.tsx +7 -1
- package/src/components/data-table/date-filter-inputs.tsx +15 -42
- package/src/components/data-table/date-popover.tsx +6 -4
- package/src/components/data-table/export-actions.tsx +267 -163
- package/src/components/data-table/row-viewer-panel/row-viewer.tsx +2 -2
- package/src/components/data-table/schemas.ts +14 -36
- package/src/components/data-table/search-highlight.tsx +79 -0
- package/src/components/data-table/types.ts +1 -0
- package/src/components/data-table/url-detector.tsx +49 -9
- package/src/components/data-table/utils.ts +18 -0
- package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
- package/src/components/datasources/__tests__/column-preview.test.tsx +63 -2
- package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
- package/src/components/datasources/column-preview.tsx +8 -2
- package/src/components/datasources/datasources.tsx +47 -35
- package/src/components/debugger/debugger-code.tsx +5 -0
- package/src/components/dependency-graph/panels.tsx +2 -1
- package/src/components/editor/Output.tsx +9 -2
- package/src/components/editor/SortableCell.tsx +1 -3
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
- package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +210 -34
- package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
- package/src/components/editor/actions/pair-with-agent-modal.tsx +100 -37
- package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
- package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
- package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
- package/src/components/editor/cell/CreateCellButton.tsx +8 -10
- package/src/components/editor/cell/DeleteButton.tsx +1 -0
- package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
- package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
- package/src/components/editor/cell/code/cell-editor.tsx +1 -1
- package/src/components/editor/cell/code/language-toggle.tsx +8 -7
- package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
- package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +30 -1
- package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
- package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
- package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +366 -0
- package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
- package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
- package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
- package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
- package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
- package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
- package/src/components/editor/chrome/wrapper/storage.ts +1 -1
- package/src/components/editor/connections/components.tsx +1 -1
- package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
- package/src/components/editor/controls/command-palette.tsx +22 -10
- package/src/components/editor/dynamic-favicon.tsx +21 -40
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +537 -0
- package/src/components/editor/errors/send-error-report-button.tsx +69 -0
- package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
- package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
- package/src/components/editor/file-tree/file-explorer.tsx +10 -5
- package/src/components/editor/file-tree/file-viewer.tsx +4 -2
- package/src/components/editor/file-tree/use-file-search.ts +35 -21
- package/src/components/editor/notebook-banner.tsx +1 -0
- package/src/components/editor/output/MarimoErrorOutput.tsx +21 -5
- package/src/components/editor/output/MarimoTracebackOutput.tsx +37 -22
- package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
- package/src/components/editor/output/ansi-reduce.ts +12 -0
- package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
- package/src/components/editor/package-alert.tsx +6 -4
- package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
- package/src/components/home/components.tsx +17 -31
- package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
- package/src/components/icons/large-spinner.tsx +3 -14
- package/src/components/icons/spinner.tsx +1 -1
- package/src/components/mcp/mcp-status-indicator.tsx +1 -1
- package/src/components/pages/__tests__/home-page.test.tsx +116 -0
- package/src/components/pages/home-page.tsx +164 -91
- package/src/components/scratchpad/scratchpad.tsx +2 -2
- package/src/components/slides/reveal-component.tsx +4 -2
- package/src/components/slides/slide-cell-view.tsx +1 -1
- package/src/components/slides/slide-form.tsx +1 -0
- package/src/components/tracing/tracing.tsx +2 -2
- package/src/components/ui/alert-dialog.tsx +1 -1
- package/src/core/ai/context/providers/datasource.ts +6 -2
- package/src/core/attachments/__tests__/toast.test.tsx +58 -0
- package/src/core/attachments/state.ts +18 -0
- package/src/core/attachments/toast.ts +15 -0
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
- package/src/core/cells/__tests__/cell.test.ts +39 -0
- package/src/core/cells/__tests__/cells.test.ts +55 -1
- package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
- package/src/core/cells/__tests__/error-report.test.ts +139 -0
- package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
- package/src/core/cells/__tests__/session.test.ts +50 -1
- package/src/core/cells/cell.ts +14 -3
- package/src/core/cells/cells.ts +3 -1
- package/src/core/cells/error-report.ts +63 -0
- package/src/core/cells/pending-delete-service.ts +2 -6
- package/src/core/cells/session.ts +29 -2
- package/src/core/cells/types.ts +10 -1
- package/src/core/codemirror/__tests__/setup.test.ts +1 -1
- package/src/core/codemirror/keymaps/vim.ts +2 -6
- package/src/core/codemirror/language/languages/markdown.ts +1 -1
- package/src/core/codemirror/language/languages/python.ts +3 -3
- package/src/core/codemirror/language/panel/panel.tsx +1 -0
- package/src/core/config/pair.ts +5 -3
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
- package/src/core/datasets/data-source-discovery.ts +4 -6
- package/src/core/document/__tests__/draft.test.ts +322 -0
- package/src/core/document/__tests__/fixtures.ts +92 -0
- package/src/core/document/__tests__/model.test.ts +483 -0
- package/src/core/document/__tests__/state.test.ts +446 -0
- package/src/core/document/__tests__/submission.test.ts +346 -0
- package/src/core/document/draft.ts +472 -0
- package/src/core/document/index.ts +10 -0
- package/src/core/document/model.ts +53 -0
- package/src/core/document/state.ts +401 -0
- package/src/core/document/sync.ts +359 -0
- package/src/core/edit-app.tsx +2 -2
- package/src/core/host/AGENTS.md +16 -0
- package/src/core/host/__tests__/attachments.test.ts +56 -0
- package/src/core/host/__tests__/document.test.ts +126 -0
- package/src/core/host/__tests__/fixtures.ts +41 -0
- package/src/core/host/__tests__/notebook-client.test.ts +570 -0
- package/src/core/host/connection.ts +42 -0
- package/src/core/host/notebook-client.ts +297 -0
- package/src/core/host/resources/attachments.ts +22 -0
- package/src/core/host/resources/document.ts +101 -0
- package/src/core/hotkeys/actions.ts +2 -4
- package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
- package/src/core/network/connection.ts +4 -2
- package/src/core/network/useConnectionNotice.ts +8 -2
- package/src/core/run-app.tsx +2 -2
- package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
- package/src/core/runtime/__tests__/runtime.test.ts +18 -17
- package/src/core/runtime/runtime.ts +26 -30
- package/src/core/state/__tests__/jotai.test.tsx +108 -0
- package/src/core/state/jotai.ts +1 -1
- package/src/core/static/files.ts +2 -0
- package/src/core/wasm/bridge.ts +2 -10
- package/src/core/wasm/store.ts +3 -3
- package/src/core/wasm/worker/bootstrap.ts +1 -1
- package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
- package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -29
- package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +223 -0
- package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +194 -184
- package/src/core/websocket/connection-state.ts +60 -0
- package/src/core/websocket/state.ts +7 -0
- package/src/core/websocket/transports/__tests__/notebook.test.ts +502 -0
- package/src/core/websocket/transports/notebook.ts +213 -0
- package/src/core/websocket/types.ts +9 -39
- package/src/core/websocket/useNotebookClient.ts +89 -0
- package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +53 -224
- package/src/css/globals.css +5 -0
- package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
- package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
- package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
- package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
- package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
- package/src/hooks/__tests__/useScript.test.tsx +77 -0
- package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
- package/src/hooks/useAsyncData.ts +3 -3
- package/src/hooks/useDataSourceDiscovery.ts +6 -4
- package/src/hooks/useDebounce.ts +5 -8
- package/src/hooks/useDeepCompareMemoize.ts +1 -1
- package/src/hooks/useDelayElapsed.ts +14 -10
- package/src/hooks/useElapsedTime.ts +3 -3
- package/src/hooks/useInputHistory.ts +4 -7
- package/src/hooks/useInternalStateWithSync.ts +6 -6
- package/src/hooks/useResizeObserver.ts +4 -7
- package/src/hooks/useScript.ts +67 -70
- package/src/hooks/useStateForKey.ts +93 -0
- package/src/mount.tsx +3 -3
- package/src/plugins/core/registerReactComponent.tsx +1 -1
- package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
- package/src/plugins/impl/DataTablePlugin.tsx +14 -15
- package/src/plugins/impl/DropdownPlugin.tsx +2 -6
- package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
- package/src/plugins/impl/FormPlugin.tsx +1 -1
- package/src/plugins/impl/RangeSliderPlugin.tsx +0 -1
- package/src/plugins/impl/RefreshPlugin.tsx +1 -0
- package/src/plugins/impl/SliderPlugin.tsx +6 -8
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +201 -1
- package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
- package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
- package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
- package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
- package/src/plugins/impl/anywidget/runtime.ts +21 -9
- package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
- package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
- package/src/plugins/impl/chat/chat-ui.tsx +12 -10
- package/src/plugins/impl/code/any-language-editor.tsx +2 -2
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
- package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
- package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
- package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +0 -1
- package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
- package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
- package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
- package/src/plugins/impl/panel/utils.ts +17 -0
- package/src/plugins/impl/vega/make-selectable.ts +4 -7
- package/src/plugins/layout/RoutesPlugin.tsx +1 -1
- package/src/stories/__tests__/editor-story.test.tsx +33 -0
- package/src/stories/button.stories.tsx +1 -0
- package/src/stories/editor.stories.tsx +24 -17
- package/src/stories/form-wrapper.stories.tsx +2 -2
- package/src/stories/select.stories.tsx +2 -2
- package/src/stories/switchable-multi-select.stories.tsx +1 -1
- package/src/utils/__tests__/arrays.test.ts +35 -0
- package/src/utils/__tests__/batch-requests.test.ts +3 -3
- package/src/utils/__tests__/dates.test.ts +116 -0
- package/src/utils/__tests__/numbers.test.ts +48 -1
- package/src/utils/__tests__/objects.test.ts +21 -0
- package/src/utils/arrays.ts +10 -0
- package/src/utils/blob.ts +1 -1
- package/src/utils/dates.ts +94 -0
- package/src/utils/numbers.ts +28 -17
- package/src/utils/objects.ts +3 -7
- package/src/utils/once.ts +19 -19
- package/src/utils/zod-utils.ts +1 -3
- package/dist/assets/CellStatus-BP2mavAq.js +0 -1
- package/dist/assets/JsonOutput-D6HIIsCb.js +0 -11
- package/dist/assets/MarimoErrorOutput-D60XkCny.js +0 -6
- package/dist/assets/RunButton-CK3LhnP5.js +0 -1
- package/dist/assets/__vite-browser-external-CYO_P3S8.js +0 -1
- package/dist/assets/__vite-browser-external-FeDVGZU8.js +0 -1
- package/dist/assets/ai-model-dropdown-B0z6-zhp.js +0 -5
- package/dist/assets/any-language-editor-BLCAD3yx.js +0 -1
- package/dist/assets/api-BeC_VSGv.js +0 -1
- package/dist/assets/arrays-BqUx5USx.js +0 -1
- package/dist/assets/cell-editor-ADVfycpN.js +0 -21
- package/dist/assets/cell-link-LSth_8hT.js +0 -1
- package/dist/assets/chat-panel-DQ6HkWEF.js +0 -4
- package/dist/assets/chat-ui-Df8Vcj6n.js +0 -4
- package/dist/assets/circle-play-CYIb0qT6.js +0 -1
- package/dist/assets/code-visibility-B-_GvwR5.js +0 -1
- package/dist/assets/coerce-CpE6kUdX.js +0 -1
- package/dist/assets/column-preview-Cm2D6vyX.js +0 -1
- package/dist/assets/command-palette-DTSQ7gOC.js +0 -1
- package/dist/assets/components-CXfO-F47.js +0 -1
- package/dist/assets/config-BOKzA3tF.js +0 -1
- package/dist/assets/config-DX8Y2WbJ.js +0 -1
- package/dist/assets/config-VPSq73n8.js +0 -1
- package/dist/assets/edit-page-ChcSgpke.js +0 -9
- package/dist/assets/feedback-button-B2gKC5cn.js +0 -1
- package/dist/assets/file-explorer-panel-BEEK5lcl.js +0 -1
- package/dist/assets/home-page-DMHnsRxm.js +0 -4
- package/dist/assets/index-Bcdzex-3.css +0 -2
- package/dist/assets/index-s-M1OXjs.js +0 -29
- package/dist/assets/kiosk-mode-BZECfg9k.js +0 -1
- package/dist/assets/layout-DmcNEu3P.js +0 -9
- package/dist/assets/linear-0eij_aAG.js +0 -1
- package/dist/assets/link-NuFVR7G9.js +0 -1
- package/dist/assets/links-DkkL9DVa.js +0 -1
- package/dist/assets/numbers-Mk3-CcoU.js +0 -1
- package/dist/assets/packages-panel-BMGOmZkR.js +0 -1
- package/dist/assets/pair-CaQyK6En.js +0 -1
- package/dist/assets/panels-CxVG8-v2.js +0 -4
- package/dist/assets/precisionRound-Bq_nmf5u.js +0 -1
- package/dist/assets/process-output-D0KGq04R.js +0 -1
- package/dist/assets/react-vega-C3rYGtP6.js +0 -189
- package/dist/assets/requests-CgSY1X4a.js +0 -1
- package/dist/assets/run-page-DF3c5cDk.js +0 -1
- package/dist/assets/scratchpad-panel-Dwd6W5PE.js +0 -1
- package/dist/assets/session-panel-VdDhEBa-.js +0 -1
- package/dist/assets/state-BAJYRhmO.js +0 -1
- package/dist/assets/state-_Qai_QyX.js +0 -3
- package/dist/assets/tooltip-mT3CGest.js +0 -61
- package/dist/assets/tracing-Di6pUmX8.js +0 -1
- package/dist/assets/tracing-panel-PeRXHD8_.js +0 -2
- package/dist/assets/useAsyncData-C7aJJz0Y.js +0 -1
- package/dist/assets/useBoolean-BDD6_xQQ.js +0 -1
- package/dist/assets/useCellActionButton-B-9eG5z_.js +0 -1
- package/dist/assets/useDebounce-B_ccBZTp.js +0 -1
- package/dist/assets/useNotebookActions-NHN6A-cN.js +0 -1
- package/dist/assets/vega-component-CnadTgoE.js +0 -1
- package/dist/assets/vega-loader.browser-CLRkNgm7.js +0 -6
- package/dist/assets/ws-BXWY541V.js +0 -22
- package/src/core/state/__tests__/jotai.test.ts +0 -25
- package/src/core/websocket/__tests__/useWebSocket.test.ts +0 -49
- package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
- package/src/core/websocket/transports/__tests__/sse.test.ts +0 -338
- package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
- package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
- package/src/core/websocket/transports/basic.ts +0 -102
- package/src/core/websocket/transports/sse.ts +0 -308
- package/src/core/websocket/transports/transport.ts +0 -61
- package/src/core/websocket/transports/ws.ts +0 -98
- package/src/core/websocket/useWebSocket.tsx +0 -88
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
/** Half-open `[start, end)` offsets into a string. */
|
|
6
|
+
export type MatchRange = readonly [start: number, end: number];
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Build the pattern that finds `query` in a display string. Returns
|
|
10
|
+
* `undefined` for an empty query. The query is matched as a literal,
|
|
11
|
+
* case-insensitive substring, which mirrors how the backend selects rows.
|
|
12
|
+
* The `u` flag selects Unicode simple case folding, the same folding the
|
|
13
|
+
* backend regex engines use.
|
|
14
|
+
*/
|
|
15
|
+
export function buildHighlightPattern(query?: string): RegExp | undefined {
|
|
16
|
+
if (!query) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const escaped = query.replace(/[$()*+.?[\\\]^{|}]/g, "\\$&");
|
|
20
|
+
return new RegExp(escaped, "giu");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Offsets of every match of `pattern` in `text`. */
|
|
24
|
+
export function findMatchRanges(text: string, pattern?: RegExp): MatchRange[] {
|
|
25
|
+
if (!pattern) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
const ranges: MatchRange[] = [];
|
|
29
|
+
for (const match of text.matchAll(pattern)) {
|
|
30
|
+
ranges.push([match.index, match.index + match[0].length]);
|
|
31
|
+
}
|
|
32
|
+
return ranges;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Wrap the parts of `text` covered by `ranges` in `<mark>` elements.
|
|
37
|
+
* `ranges` are offsets into a larger string in which `text` starts at
|
|
38
|
+
* `offset`, so one set of ranges can be shared across adjacent segments.
|
|
39
|
+
* Returns `text` unchanged when no range overlaps it.
|
|
40
|
+
*/
|
|
41
|
+
export function highlightRanges(
|
|
42
|
+
text: string,
|
|
43
|
+
ranges: readonly MatchRange[],
|
|
44
|
+
offset = 0,
|
|
45
|
+
): ReactNode {
|
|
46
|
+
const segments: ReactNode[] = [];
|
|
47
|
+
let cursor = 0;
|
|
48
|
+
for (const [rangeStart, rangeEnd] of ranges) {
|
|
49
|
+
const start = Math.max(rangeStart - offset, cursor);
|
|
50
|
+
const end = Math.min(rangeEnd - offset, text.length);
|
|
51
|
+
if (end <= start) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (start > cursor) {
|
|
55
|
+
segments.push(text.slice(cursor, start));
|
|
56
|
+
}
|
|
57
|
+
segments.push(
|
|
58
|
+
<mark key={start} className="bg-search-highlight text-inherit">
|
|
59
|
+
{text.slice(start, end)}
|
|
60
|
+
</mark>,
|
|
61
|
+
);
|
|
62
|
+
cursor = end;
|
|
63
|
+
}
|
|
64
|
+
if (segments.length === 0) {
|
|
65
|
+
return text;
|
|
66
|
+
}
|
|
67
|
+
if (cursor < text.length) {
|
|
68
|
+
segments.push(text.slice(cursor));
|
|
69
|
+
}
|
|
70
|
+
return segments;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Wrap every match of `pattern` in `text` in a `<mark>` element. Returns
|
|
75
|
+
* `text` unchanged when `pattern` is absent or does not match.
|
|
76
|
+
*/
|
|
77
|
+
export function highlightMatches(text: string, pattern?: RegExp): ReactNode {
|
|
78
|
+
return highlightRanges(text, findMatchRanges(text, pattern));
|
|
79
|
+
}
|
|
@@ -6,6 +6,7 @@ import type { DataType } from "@/core/kernel/messages";
|
|
|
6
6
|
declare module "@tanstack/react-table" {
|
|
7
7
|
interface TableMeta<TData extends RowData> {
|
|
8
8
|
rawData?: TData[]; // raw data for filtering/copying (present only if format_mapping is provided)
|
|
9
|
+
highlightPattern?: RegExp; // built from the committed search query that produced the current rows
|
|
9
10
|
}
|
|
10
11
|
}
|
|
11
12
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { marked } from "marked";
|
|
4
|
-
import { useState } from "react";
|
|
4
|
+
import { Fragment, type ReactNode, useState } from "react";
|
|
5
5
|
import { MarkdownRenderer } from "@/components/markdown/markdown-renderer";
|
|
6
6
|
import {
|
|
7
7
|
Popover,
|
|
@@ -10,13 +10,20 @@ import {
|
|
|
10
10
|
} from "@/components/ui/popover";
|
|
11
11
|
import { Events } from "@/utils/events";
|
|
12
12
|
import type { ContentPart } from "@/utils/url-parser";
|
|
13
|
+
import { findMatchRanges, highlightRanges } from "./search-highlight";
|
|
13
14
|
|
|
14
|
-
const ImageWithFallback = ({
|
|
15
|
+
const ImageWithFallback = ({
|
|
16
|
+
url,
|
|
17
|
+
fallbackLabel,
|
|
18
|
+
}: {
|
|
19
|
+
url: string;
|
|
20
|
+
fallbackLabel?: ReactNode;
|
|
21
|
+
}) => {
|
|
15
22
|
const [error, setError] = useState(false);
|
|
16
23
|
const [open, setOpen] = useState(false);
|
|
17
24
|
|
|
18
25
|
if (error) {
|
|
19
|
-
return <URLAnchor url={url}
|
|
26
|
+
return <URLAnchor url={url}>{fallbackLabel}</URLAnchor>;
|
|
20
27
|
}
|
|
21
28
|
|
|
22
29
|
return (
|
|
@@ -93,21 +100,54 @@ export const MarkdownUrlDetector = ({
|
|
|
93
100
|
return <UrlDetector parts={parts} />;
|
|
94
101
|
};
|
|
95
102
|
|
|
96
|
-
export const UrlDetector = ({
|
|
103
|
+
export const UrlDetector = ({
|
|
104
|
+
parts,
|
|
105
|
+
highlightPattern,
|
|
106
|
+
}: {
|
|
107
|
+
parts: ContentPart[];
|
|
108
|
+
highlightPattern?: RegExp;
|
|
109
|
+
}) => {
|
|
110
|
+
// Match against the whole string so a query that spans a text part and
|
|
111
|
+
// a link is still marked. Each part then takes its slice of the ranges.
|
|
112
|
+
const ranges = highlightPattern
|
|
113
|
+
? findMatchRanges(parts.map(partText).join(""), highlightPattern)
|
|
114
|
+
: [];
|
|
115
|
+
|
|
116
|
+
let offset = 0;
|
|
97
117
|
const markup = parts.map((part, idx) => {
|
|
118
|
+
const text = partText(part);
|
|
119
|
+
const label = highlightRanges(text, ranges, offset);
|
|
120
|
+
offset += text.length;
|
|
121
|
+
|
|
98
122
|
if (part.type === "url") {
|
|
99
|
-
return
|
|
123
|
+
return (
|
|
124
|
+
<URLAnchor key={idx} url={part.url}>
|
|
125
|
+
{label}
|
|
126
|
+
</URLAnchor>
|
|
127
|
+
);
|
|
100
128
|
}
|
|
101
129
|
if (part.type === "image") {
|
|
102
|
-
return
|
|
130
|
+
return (
|
|
131
|
+
<ImageWithFallback key={idx} url={part.url} fallbackLabel={label} />
|
|
132
|
+
);
|
|
103
133
|
}
|
|
104
|
-
return
|
|
134
|
+
return <Fragment key={idx}>{label}</Fragment>;
|
|
105
135
|
});
|
|
106
136
|
|
|
107
137
|
return markup;
|
|
108
138
|
};
|
|
109
139
|
|
|
110
|
-
|
|
140
|
+
function partText(part: ContentPart): string {
|
|
141
|
+
return part.type === "text" ? part.value : part.url;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const URLAnchor = ({
|
|
145
|
+
url,
|
|
146
|
+
children,
|
|
147
|
+
}: {
|
|
148
|
+
url: string;
|
|
149
|
+
children?: ReactNode;
|
|
150
|
+
}) => {
|
|
111
151
|
return (
|
|
112
152
|
<a
|
|
113
153
|
href={url}
|
|
@@ -116,7 +156,7 @@ const URLAnchor = ({ url }: { url: string }) => {
|
|
|
116
156
|
onClick={Events.stopPropagation()}
|
|
117
157
|
className="text-link hover:underline"
|
|
118
158
|
>
|
|
119
|
-
{url}
|
|
159
|
+
{children ?? url}
|
|
120
160
|
</a>
|
|
121
161
|
);
|
|
122
162
|
};
|
|
@@ -228,6 +228,24 @@ export function detectSentinel(
|
|
|
228
228
|
return null;
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
+
type NullishSentinelType = Extract<CellValueSentinel["type"], "null" | "nat">;
|
|
232
|
+
|
|
233
|
+
const NULLISH_SENTINEL_TYPES: ReadonlySet<CellValueSentinel["type"]> =
|
|
234
|
+
new Set<NullishSentinelType>(["null", "nat"]);
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* True when the cell value represents a missing value (null, NaT)
|
|
238
|
+
* rather than a real value that happens to look special.
|
|
239
|
+
*/
|
|
240
|
+
export function isNullishSentinel(
|
|
241
|
+
value: unknown,
|
|
242
|
+
dataType: DataType | undefined,
|
|
243
|
+
dtype?: string,
|
|
244
|
+
): boolean {
|
|
245
|
+
const sentinel = detectSentinel(value, dataType, dtype);
|
|
246
|
+
return sentinel !== null && NULLISH_SENTINEL_TYPES.has(sentinel.type);
|
|
247
|
+
}
|
|
248
|
+
|
|
231
249
|
/**
|
|
232
250
|
* Stringify an unknown value. Converts objects to JSON strings.
|
|
233
251
|
* @param opts.value - The value to stringify.
|
|
@@ -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
|
+
});
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import { render } from "@testing-library/react";
|
|
4
|
-
import { Provider } from "jotai";
|
|
3
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
4
|
+
import { createStore, Provider } from "jotai";
|
|
5
5
|
import type React from "react";
|
|
6
6
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
7
7
|
import { MockRequestClient } from "@/__mocks__/requests";
|
|
8
|
+
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
8
9
|
import type { SQLTableContext } from "@/core/datasets/data-source-connections";
|
|
9
10
|
import type { DataTable, DataTableColumn } from "@/core/kernel/messages";
|
|
10
11
|
import { requestClientAtom } from "@/core/network/requests";
|
|
11
12
|
import { store } from "@/core/state/jotai";
|
|
12
13
|
import { DatasetColumnPreview } from "../column-preview";
|
|
13
14
|
|
|
15
|
+
const { createNewCell } = vi.hoisted(() => ({ createNewCell: vi.fn() }));
|
|
16
|
+
|
|
17
|
+
vi.mock("@/core/cells/cells", async (importOriginal) => ({
|
|
18
|
+
...(await importOriginal<typeof import("@/core/cells/cells")>()),
|
|
19
|
+
useCellActions: () => ({ createNewCell }),
|
|
20
|
+
}));
|
|
21
|
+
|
|
14
22
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
15
23
|
<Provider store={store}>{children}</Provider>
|
|
16
24
|
);
|
|
@@ -95,3 +103,56 @@ describe("DatasetColumnPreview fullyQualifiedTableName", () => {
|
|
|
95
103
|
);
|
|
96
104
|
});
|
|
97
105
|
});
|
|
106
|
+
|
|
107
|
+
describe("DatasetColumnPreview chart actions", () => {
|
|
108
|
+
it("identifies each column and inserts its corresponding chart", () => {
|
|
109
|
+
const store = createStore();
|
|
110
|
+
store.set(requestClientAtom, MockRequestClient.create());
|
|
111
|
+
const columns: DataTableColumn[] = [
|
|
112
|
+
{
|
|
113
|
+
name: "email",
|
|
114
|
+
type: "string",
|
|
115
|
+
external_type: "str",
|
|
116
|
+
sample_values: [],
|
|
117
|
+
},
|
|
118
|
+
{ name: "age", type: "integer", external_type: "int", sample_values: [] },
|
|
119
|
+
];
|
|
120
|
+
render(
|
|
121
|
+
<Provider store={store}>
|
|
122
|
+
<TooltipProvider>
|
|
123
|
+
{columns.map((column) => (
|
|
124
|
+
<DatasetColumnPreview
|
|
125
|
+
key={column.name}
|
|
126
|
+
table={{ ...table, source_type: "local", engine: null }}
|
|
127
|
+
column={column}
|
|
128
|
+
onAddColumnChart={vi.fn()}
|
|
129
|
+
preview={{
|
|
130
|
+
table_name: table.name,
|
|
131
|
+
column_name: column.name,
|
|
132
|
+
chart_code: `chart_${column.name}`,
|
|
133
|
+
chart_spec: null,
|
|
134
|
+
error: null,
|
|
135
|
+
missing_packages: null,
|
|
136
|
+
stats: {},
|
|
137
|
+
}}
|
|
138
|
+
/>
|
|
139
|
+
))}
|
|
140
|
+
</TooltipProvider>
|
|
141
|
+
</Provider>,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
for (const column of columns) {
|
|
145
|
+
fireEvent.click(
|
|
146
|
+
screen.getByRole("button", {
|
|
147
|
+
name: `Add chart to notebook for ${column.name}`,
|
|
148
|
+
}),
|
|
149
|
+
);
|
|
150
|
+
expect(createNewCell).toHaveBeenLastCalledWith({
|
|
151
|
+
code: `chart_${column.name}`,
|
|
152
|
+
before: false,
|
|
153
|
+
cellId: "__end__",
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
expect(createNewCell).toHaveBeenCalledTimes(2);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { render, screen, waitFor } from "@testing-library/react";
|
|
4
|
+
import { Provider } from "jotai";
|
|
5
|
+
import type React from "react";
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
import { MockRequestClient } from "@/__mocks__/requests";
|
|
8
|
+
import type { SQLTableContext } from "@/core/datasets/data-source-connections";
|
|
9
|
+
import { PreviewSQLTableList } from "@/core/datasets/request-registry";
|
|
10
|
+
import type { SQLTableListPreview } from "@/core/kernel/messages";
|
|
11
|
+
import type { RequestId } from "@/core/network/DeferredRequestRegistry";
|
|
12
|
+
import { requestClientAtom } from "@/core/network/requests";
|
|
13
|
+
import { store } from "@/core/state/jotai";
|
|
14
|
+
import { TableList } from "../datasources";
|
|
15
|
+
|
|
16
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
17
|
+
<Provider store={store}>{children}</Provider>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const sqlTableContext: SQLTableContext = {
|
|
21
|
+
engine: "my_engine",
|
|
22
|
+
database: "my_db",
|
|
23
|
+
schema: "public",
|
|
24
|
+
dialect: "postgres",
|
|
25
|
+
schemaPath: ["public"],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// The kernel answers table-list requests over the websocket, so the mock
|
|
29
|
+
// settles the deferred request instead of returning the response directly.
|
|
30
|
+
function renderDeferredTableList(settle?: (requestId: RequestId) => void) {
|
|
31
|
+
const client = MockRequestClient.create();
|
|
32
|
+
client.previewSQLTableList.mockImplementation(async ({ requestId }) => {
|
|
33
|
+
settle?.(requestId);
|
|
34
|
+
return null;
|
|
35
|
+
});
|
|
36
|
+
store.set(requestClientAtom, client);
|
|
37
|
+
render(
|
|
38
|
+
<TableList
|
|
39
|
+
tables={[]}
|
|
40
|
+
tablesResolved={false}
|
|
41
|
+
sqlTableContext={sqlTableContext}
|
|
42
|
+
/>,
|
|
43
|
+
{ wrapper },
|
|
44
|
+
);
|
|
45
|
+
return client;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
describe("TableList deferred discovery", () => {
|
|
49
|
+
it("shows the error when the request rejects", async () => {
|
|
50
|
+
renderDeferredTableList((requestId) =>
|
|
51
|
+
PreviewSQLTableList.reject(requestId, new Error("engine offline")),
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
expect(screen.getByText("Loading tables...")).toBeInTheDocument();
|
|
55
|
+
|
|
56
|
+
await waitFor(() => {
|
|
57
|
+
expect(screen.getByText("engine offline")).toBeInTheDocument();
|
|
58
|
+
});
|
|
59
|
+
expect(screen.queryByText("Loading tables...")).toBeNull();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("shows the error when the response has no tables field", async () => {
|
|
63
|
+
renderDeferredTableList((requestId) =>
|
|
64
|
+
PreviewSQLTableList.resolve(requestId, {} as SQLTableListPreview),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
await waitFor(() => {
|
|
68
|
+
expect(screen.getByText("No tables available")).toBeInTheDocument();
|
|
69
|
+
});
|
|
70
|
+
expect(screen.queryByText("Loading tables...")).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("shows the empty state for an empty table list", async () => {
|
|
74
|
+
renderDeferredTableList((requestId) =>
|
|
75
|
+
PreviewSQLTableList.resolve(requestId, {
|
|
76
|
+
tables: [],
|
|
77
|
+
} as unknown as SQLTableListPreview),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
await waitFor(() => {
|
|
81
|
+
expect(screen.getByText("No tables found")).toBeInTheDocument();
|
|
82
|
+
});
|
|
83
|
+
expect(screen.queryByText("Loading tables...")).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("requests the table list with the table context and waits for the kernel", async () => {
|
|
87
|
+
let pendingRequestId: RequestId | undefined;
|
|
88
|
+
const client = renderDeferredTableList((requestId) => {
|
|
89
|
+
pendingRequestId = requestId;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await waitFor(() => {
|
|
93
|
+
expect(client.previewSQLTableList).toHaveBeenCalledTimes(1);
|
|
94
|
+
});
|
|
95
|
+
expect(client.previewSQLTableList).toHaveBeenCalledWith(
|
|
96
|
+
expect.objectContaining({
|
|
97
|
+
engine: "my_engine",
|
|
98
|
+
database: "my_db",
|
|
99
|
+
schema: "public",
|
|
100
|
+
schemaPath: ["public"],
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
expect(screen.getByText("Loading tables...")).toBeInTheDocument();
|
|
104
|
+
|
|
105
|
+
// Settle the request so the process-wide registry does not keep the entry.
|
|
106
|
+
expect(pendingRequestId).toBeDefined();
|
|
107
|
+
PreviewSQLTableList.resolve(
|
|
108
|
+
pendingRequestId as RequestId,
|
|
109
|
+
{
|
|
110
|
+
tables: [],
|
|
111
|
+
} as unknown as SQLTableListPreview,
|
|
112
|
+
);
|
|
113
|
+
await waitFor(() => {
|
|
114
|
+
expect(screen.queryByText("Loading tables...")).toBeNull();
|
|
115
|
+
});
|
|
116
|
+
expect(PreviewSQLTableList.requests.size).toBe(0);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -112,12 +112,16 @@ export const DatasetColumnPreview: React.FC<{
|
|
|
112
112
|
|
|
113
113
|
const addDataframeChart = preview.chart_code &&
|
|
114
114
|
table.source_type === "local" && (
|
|
115
|
-
<AddDataframeChart
|
|
115
|
+
<AddDataframeChart
|
|
116
|
+
chartCode={preview.chart_code}
|
|
117
|
+
columnName={column.name}
|
|
118
|
+
/>
|
|
116
119
|
);
|
|
117
120
|
|
|
118
121
|
const addSQLChart = table.source_type === "duckdb" && (
|
|
119
122
|
<Tooltip content="Add SQL cell" delayDuration={400}>
|
|
120
123
|
<Button
|
|
124
|
+
aria-label={`Add SQL cell for ${column.name}`}
|
|
121
125
|
variant="outline"
|
|
122
126
|
size="icon"
|
|
123
127
|
className="z-10 bg-background absolute right-1 -top-1"
|
|
@@ -238,7 +242,8 @@ export function renderChart(chartSpec: string, theme: Theme) {
|
|
|
238
242
|
|
|
239
243
|
export const AddDataframeChart: React.FC<{
|
|
240
244
|
chartCode: string;
|
|
241
|
-
|
|
245
|
+
columnName: string;
|
|
246
|
+
}> = ({ chartCode, columnName }) => {
|
|
242
247
|
const autoInstantiate = useAtomValue(autoInstantiateAtom);
|
|
243
248
|
const lastFocusedCellId = useLastFocusedCellId();
|
|
244
249
|
const { createNewCell } = useCellActions();
|
|
@@ -261,6 +266,7 @@ export const AddDataframeChart: React.FC<{
|
|
|
261
266
|
return (
|
|
262
267
|
<Tooltip content="Add chart to notebook" delayDuration={400}>
|
|
263
268
|
<Button
|
|
269
|
+
aria-label={`Add chart to notebook for ${columnName}`}
|
|
264
270
|
variant="outline"
|
|
265
271
|
size="icon"
|
|
266
272
|
className="z-10 bg-background absolute right-1 -top-0.5"
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
VisibilityToggleButton,
|
|
14
14
|
} from "@/components/editor/file-tree/tree-actions";
|
|
15
15
|
import { CopyClipboardIcon } from "@/components/icons/copy-icon";
|
|
16
|
-
import { Button } from "@/components/ui/button";
|
|
16
|
+
import { Button, buttonVariants } from "@/components/ui/button";
|
|
17
17
|
import { Command, CommandInput, CommandItem } from "@/components/ui/command";
|
|
18
18
|
import { Tooltip } from "@/components/ui/tooltip";
|
|
19
19
|
import { maybeAddMarimoImport } from "@/core/cells/add-missing-import";
|
|
@@ -702,7 +702,40 @@ const SchemaNode: React.FC<SchemaNodeProps> = (props) => {
|
|
|
702
702
|
);
|
|
703
703
|
};
|
|
704
704
|
|
|
705
|
-
|
|
705
|
+
// Kept outside the component because the React Compiler does not yet
|
|
706
|
+
// support try/finally inside component bodies.
|
|
707
|
+
async function loadTableList(
|
|
708
|
+
sqlTableContext: SQLTableContext,
|
|
709
|
+
addTableList: ReturnType<typeof useDataSourceActions>["addTableList"],
|
|
710
|
+
setLoading: (loading: boolean) => void,
|
|
711
|
+
) {
|
|
712
|
+
setLoading(true);
|
|
713
|
+
try {
|
|
714
|
+
const { engine, database, schema, schemaPath } = sqlTableContext;
|
|
715
|
+
const previewTableList = await PreviewSQLTableList.request({
|
|
716
|
+
engine: engine,
|
|
717
|
+
database: database,
|
|
718
|
+
schema: schema,
|
|
719
|
+
schemaPath: schemaPath ?? [],
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
if (!previewTableList?.tables) {
|
|
723
|
+
throw new Error("No tables available");
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
addTableList({
|
|
727
|
+
tables: previewTableList.tables,
|
|
728
|
+
sqlTableContext: sqlTableContext,
|
|
729
|
+
});
|
|
730
|
+
} finally {
|
|
731
|
+
setLoading(false);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* This is exported for testing purposes only.
|
|
737
|
+
*/
|
|
738
|
+
export const TableList: React.FC<{
|
|
706
739
|
tables: DataTable[];
|
|
707
740
|
sqlTableContext?: SQLTableContext;
|
|
708
741
|
searchValue?: string;
|
|
@@ -731,26 +764,7 @@ const TableList: React.FC<{
|
|
|
731
764
|
// resets tablesResolved to false).
|
|
732
765
|
const { isPending, error } = useAsyncData(async () => {
|
|
733
766
|
if (!tablesResolved && sqlTableContext) {
|
|
734
|
-
setTablesLoading
|
|
735
|
-
|
|
736
|
-
const { engine, database, schema, schemaPath } = sqlTableContext;
|
|
737
|
-
const previewTableList = await PreviewSQLTableList.request({
|
|
738
|
-
engine: engine,
|
|
739
|
-
database: database,
|
|
740
|
-
schema: schema,
|
|
741
|
-
schemaPath: schemaPath ?? [],
|
|
742
|
-
});
|
|
743
|
-
|
|
744
|
-
if (!previewTableList?.tables) {
|
|
745
|
-
setTablesLoading(false);
|
|
746
|
-
throw new Error("No tables available");
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
addTableList({
|
|
750
|
-
tables: previewTableList.tables,
|
|
751
|
-
sqlTableContext: sqlTableContext,
|
|
752
|
-
});
|
|
753
|
-
setTablesLoading(false);
|
|
767
|
+
await loadTableList(sqlTableContext, addTableList, setTablesLoading);
|
|
754
768
|
}
|
|
755
769
|
}, [tablesResolved, sqlTableContext]);
|
|
756
770
|
|
|
@@ -964,6 +978,7 @@ const DatasetTableItem: React.FC<{
|
|
|
964
978
|
{renderRowsByColumns()}
|
|
965
979
|
<Tooltip content="Add table to notebook" delayDuration={400}>
|
|
966
980
|
<Button
|
|
981
|
+
aria-label={`Add table ${table.name} to notebook`}
|
|
967
982
|
className="group-hover:inline-flex hidden"
|
|
968
983
|
variant="text"
|
|
969
984
|
size="icon"
|
|
@@ -1054,19 +1069,16 @@ const DatasetColumnItem: React.FC<{
|
|
|
1054
1069
|
{isIndexed &&
|
|
1055
1070
|
renderItemSubtext({ tooltipContent: "Indexed", content: "IDX" })}
|
|
1056
1071
|
</div>
|
|
1057
|
-
<
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
/>
|
|
1068
|
-
</Button>
|
|
1069
|
-
</Tooltip>
|
|
1072
|
+
<CopyClipboardIcon
|
|
1073
|
+
tooltip="Copy column name"
|
|
1074
|
+
value={column.name}
|
|
1075
|
+
ariaLabel={`Copy column name ${column.name}`}
|
|
1076
|
+
className="h-3 w-3"
|
|
1077
|
+
buttonClassName={cn(
|
|
1078
|
+
buttonVariants({ variant: "text", size: "icon" }),
|
|
1079
|
+
"group-hover:opacity-100 opacity-0 hover:bg-muted text-muted-foreground hover:text-foreground",
|
|
1080
|
+
)}
|
|
1081
|
+
/>
|
|
1070
1082
|
<span className="text-xs text-muted-foreground">
|
|
1071
1083
|
{column.external_type}
|
|
1072
1084
|
</span>
|