@marimo-team/frontend 0.25.3-dev12 → 0.25.3-dev121
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-_Aw9_foI.js +1 -0
- package/dist/assets/{ConnectedDataExplorerComponent-TprnxZeJ.js → ConnectedDataExplorerComponent-BRgqbAsc.js} +1 -1
- package/dist/assets/{ErrorBoundary-CM13wrd8.js → ErrorBoundary-DxSzjvbx.js} +2 -2
- package/dist/assets/JsonOutput-B7pGj79O.js +11 -0
- package/dist/assets/{LazyAnyLanguageCodeMirror-B4fCknq1.js → LazyAnyLanguageCodeMirror-BPYE76jR.js} +2 -2
- package/dist/assets/MarimoErrorOutput-Bzo6Bkp5.js +10 -0
- package/dist/assets/{RenderHTML-DX6i99Op.js → RenderHTML-D6AC0IhC.js} +1 -1
- package/dist/assets/RunButton-BjYsPiKa.js +1 -0
- package/dist/assets/__vite-browser-external-BynxqS4w.js +1 -0
- package/dist/assets/__vite-browser-external-DTOT9NDN.js +1 -0
- package/dist/assets/{add-cell-with-ai-BpD_790A.js → add-cell-with-ai-CFrR1iW4.js} +6 -6
- package/dist/assets/{add-connection-dialog-B1BXCjex.js → add-connection-dialog-CyNAB_W2.js} +1 -1
- package/dist/assets/{agent-panel-CDFUc1uu.js → agent-panel-VOrccb5q.js} +3 -3
- package/dist/assets/ai-model-dropdown-Bfxv_zvu.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-AdV-QDD7.js → app-config-button-BRcjv8A_.js} +1 -1
- package/dist/assets/arrays-0R2reF2y.js +1 -0
- package/dist/assets/{blob-BG8yoQmx.js → blob-DTYOX-m5.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-eKgYK8Bd.js +42 -0
- package/dist/assets/{cell-link-8Auigi8P.js → cell-link-D3rkqgbU.js} +1 -1
- package/dist/assets/{cells-C6YGYGdi.js → cells-2HETnlvE.js} +18 -18
- package/dist/assets/{chat-display-DhgI2MS9.js → chat-display-BrT4VJJK.js} +1 -1
- package/dist/assets/chat-panel-qpPNEwxV.js +4 -0
- package/dist/assets/chat-ui-SAdJxg-k.js +4 -0
- package/dist/assets/{circle-DXf-VWZG.js → circle-B6D_6D8w.js} +1 -1
- package/dist/assets/{code-visibility-BJImdpDi.js → code-visibility-DYDagVy7.js} +1 -1
- package/dist/assets/column-preview-yI7-INkZ.js +1 -0
- package/dist/assets/command-palette-DF-B_YpW.js +1 -0
- package/dist/assets/{common-Sl3uthjg.js → common-DgXKwblm.js} +1 -1
- package/dist/assets/components-CBE8cOLK.js +1 -0
- package/dist/assets/{components-3cou6Mka.js → components-CuMhY4Sf.js} +1 -1
- package/dist/assets/{components-CskjK6xZ.js → components-DCo7mAWO.js} +1 -1
- package/dist/assets/config-CD1IuZFJ.js +1 -0
- package/dist/assets/{config-DRIpOKqh.js → config-DMz7oN0r.js} +1 -1
- package/dist/assets/{config-CA1V-wjh.js → config-DpY86_Yq.js} +1 -1
- 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-DfbNb83e.js → datasource-D9JMcEAP.js} +1 -1
- package/dist/assets/{dates-CfmdTL6B.js → dates-CQMwFko2.js} +1 -1
- package/dist/assets/{dependency-graph-panel-L5cBE5sl.js → dependency-graph-panel-BVJWPCvw.js} +3 -3
- package/dist/assets/{dist-iaThVDkA.js → dist-jlhMTKo_.js} +1 -1
- package/dist/assets/{documentation-panel-D7S8JIc2.js → documentation-panel--wI4XHso.js} +1 -1
- package/dist/assets/{download-C03FOElq.js → download-Dod42wfD.js} +1 -1
- package/dist/assets/{dropdown-menu-C1rnl-0o.js → dropdown-menu-DS6umAnH.js} +1 -1
- package/dist/assets/edit-page-Uj9YSRMX.js +9 -0
- package/dist/assets/{error-banner-COZ7SfYV.js → error-banner-B5kzVf_0.js} +1 -1
- package/dist/assets/{error-entries-CLnPDv7b.js → error-entries-D00nWhWE.js} +1 -1
- package/dist/assets/{error-panel-D2xrYLue.js → error-panel-CcN_1PWf.js} +1 -1
- package/dist/assets/{es-Cqst59WX.js → es-N9SHpmG7.js} +1 -1
- package/dist/assets/{feedback-button-DzPunIqU.js → feedback-button-ByGgqUsX.js} +1 -1
- package/dist/assets/feedback-button-tbWwJ_t6.js +1 -0
- package/dist/assets/file-explorer-panel-eTEqtMPW.js +1 -0
- package/dist/assets/{file-name-input-B4fRaQkI.js → file-name-input-D5SfTNWh.js} +1 -1
- package/dist/assets/{floating-outline-B5MaM1JS.js → floating-outline-B-k3P_AO.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-BefvdsM7.js → gallery-page-BzbMhDHi.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-2n7Ig5Jm.js → ganttDiagram-EL5Y4UJY-CYqkCfFG.js} +1 -1
- package/dist/assets/{glide-data-editor-C3eaYlXg.js → glide-data-editor-BMqhUrHI.js} +2 -2
- package/dist/assets/{glide-utils-CIrAxdjv.js → glide-utils-HFqhvWHI.js} +2 -2
- package/dist/assets/home-page-DJcYWiga.js +1 -0
- package/dist/assets/{hooks-DzML9nez.js → hooks-CGLb_CCz.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-bSIORW11.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-Ck1RoL1B.js → layout-C2XnLAfO.js} +5 -5
- package/dist/assets/linear-Bma8J3iM.js +1 -0
- package/dist/assets/links-DX2w97N9.js +1 -0
- package/dist/assets/{logs-panel-BRytaWaO.js → logs-panel-B3T1GFBk.js} +1 -1
- package/dist/assets/{markdown-renderer-DX0Xrrfy.js → markdown-renderer-BhiTvxtL.js} +1 -1
- package/dist/assets/{menu-items-BrwnTgyK.js → menu-items-BgUyNUpu.js} +1 -1
- package/dist/assets/{mermaid-C4EiVqcd.js → mermaid-BKXlWk4F.js} +2 -2
- package/dist/assets/{name-cell-input-DlpTvPLa.js → name-cell-input-CgAObR6N.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-B2WIpz-1.js → outline-panel-BhBb7Vqw.js} +1 -1
- package/dist/assets/{package-data-dv4FBHxR.js → package-data-CKPmBDtg.js} +1 -1
- package/dist/assets/packages-panel-Bt0xNpFl.js +1 -0
- package/dist/assets/{panel-accordion-state-BqSH5ttk.js → panel-accordion-state-ZsUdpAYn.js} +1 -1
- package/dist/assets/panels-DJghvIKo.js +1 -0
- package/dist/assets/precisionRound-BxlUs8G4.js +1 -0
- package/dist/assets/process-output-CeOMwE17.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-MnMEoYW5.js → readonly-code-display-TvOC7ESE.js} +1 -1
- package/dist/assets/{readonly-python-code-ggs4hAzC.js → readonly-python-code-Cqkfdeyo.js} +1 -1
- package/dist/assets/{renderShortcut-C1JwYcqW.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-PiV1eQk9.js → reveal-component-CwE6U9oJ.js} +37 -37
- package/dist/assets/run-page-CtonWTtJ.js +1 -0
- package/dist/assets/{runs-BLp5jezv.js → runs-C49hleBy.js} +1 -1
- package/dist/assets/{runtime-details-state-CoL-p0MC.js → runtime-details-state-CKqJSfSR.js} +1 -1
- package/dist/assets/{save-worker-BbUSwZRG.js → save-worker-UwdGcNK5.js} +2 -1
- package/dist/assets/scratchpad-panel-CwVBedfZ.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-D7KN4tVs.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-CdHdKUCF.js +1 -0
- package/dist/assets/{snippets-panel-ClGHtEKQ.js → snippets-panel-DYLWOLYw.js} +1 -1
- package/dist/assets/{spec-BfSBcO5B.js → spec-DlH4Mp7d.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-C3oDoyFe.js → state-C5WAeuTu.js} +1 -1
- package/dist/assets/{state-DxQnC_1I.js → state-Cv2JW3l9.js} +1 -1
- package/dist/assets/{state-DtGB7xeJ.js → state-DZN9vZ6z.js} +1 -1
- package/dist/assets/state-DgECPVFs.js +107 -0
- 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-wnSHYT8N.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-B0J2ctTm.js +1 -0
- package/dist/assets/tracing-panel-C1OCpoeE.js +2 -0
- package/dist/assets/{tree-actions-DnUokmva.js → tree-actions-sjBxIglc.js} +1 -1
- package/dist/assets/{use-restore-focus-BxEGwIbk.js → use-restore-focus-DdS7Tl0F.js} +1 -1
- package/dist/assets/{useAddCell-IN24thff.js → useAddCell-CaXnFGki.js} +1 -1
- package/dist/assets/useAsyncData-qvoPYHtx.js +1 -0
- package/dist/assets/useBoolean-Dd2nWXFJ.js +1 -0
- package/dist/assets/useCellActionButton-CJ90CMEj.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-fQlM46Y2.js → useDeleteCell-D9N85822.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-ImXYINm_.js → useDependencyPanelTab-CzAE0cm-.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-BW06eter.js → useInstallPackage-wcDq9hYT.js} +1 -1
- package/dist/assets/{useInterval--x3s1VpI.js → useInterval-DJdQmXnq.js} +1 -1
- package/dist/assets/useNotebookActions-CAMEQ38y.js +1 -0
- package/dist/assets/{useRunCells-DSJPXE-j.js → useRunCells-DGakx8hj.js} +1 -1
- package/dist/assets/{useSplitCell-DQyMF5gd.js → useSplitCell-AK5BKPGM.js} +1 -1
- package/dist/assets/useStateForKey-CGMJQT8x.js +1 -0
- package/dist/assets/{useTheme-n8Dj1zH7.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-jm2VC7pS.js +159 -0
- 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 +81 -79
- package/package.json +12 -12
- package/src/__tests__/host-imports.test.ts +1 -0
- package/src/__tests__/mount.test.ts +0 -20
- 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__/chart-spec-model.test.ts +195 -195
- 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__/date-popover.test.tsx +72 -0
- package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
- 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__/spec-snapshot.test.ts +20 -20
- package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
- package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
- package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
- package/src/components/data-table/charts/charts.tsx +40 -30
- 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/chart-spec-model.tsx +23 -16
- 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 +36 -20
- package/src/components/data-table/data-table.tsx +7 -1
- package/src/components/data-table/date-popover.tsx +6 -4
- package/src/components/data-table/export-actions.tsx +76 -48
- package/src/components/data-table/row-viewer-panel/row-viewer.tsx +2 -2
- 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/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/dependency-graph-tree.tsx +8 -8
- package/src/components/dependency-graph/elements.ts +24 -21
- package/src/components/dependency-graph/panels.tsx +2 -1
- package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
- 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-modal.test.tsx +24 -0
- package/src/components/editor/actions/pair-with-agent-modal.tsx +18 -15
- 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/packages-panel.tsx +4 -7
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +12 -18
- package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
- package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
- 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 +12 -23
- 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/MarimoTracebackOutput.tsx +2 -2
- package/src/components/editor/output/ansi-reduce.ts +33 -20
- package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
- package/src/components/editor/package-alert.tsx +6 -4
- package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
- 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/ai/tools/base.ts +16 -7
- package/src/core/ai/tools/edit-notebook-tool.ts +47 -45
- package/src/core/ai/tools/registry.ts +13 -8
- package/src/core/ai/tools/sample-tool.ts +6 -7
- package/src/core/attachments/state.ts +5 -8
- 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 +3 -1
- package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
- package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
- package/src/core/cells/__tests__/session.test.ts +117 -36
- package/src/core/cells/cell.ts +14 -3
- package/src/core/cells/cells.ts +3 -1
- package/src/core/cells/pending-delete-service.ts +2 -6
- package/src/core/cells/session.ts +1 -1
- package/src/core/cells/types.ts +10 -1
- package/src/core/codemirror/__tests__/setup.test.ts +29 -29
- package/src/core/codemirror/cells/__tests__/extensions.test.ts +15 -0
- package/src/core/codemirror/cells/extensions.ts +7 -3
- package/src/core/codemirror/keymaps/vim.ts +2 -6
- package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
- package/src/core/codemirror/language/extension.ts +8 -8
- package/src/core/codemirror/language/languages/markdown.ts +8 -6
- package/src/core/codemirror/language/languages/python.ts +16 -10
- package/src/core/codemirror/language/languages/sql/sql.ts +9 -7
- package/src/core/codemirror/language/panel/panel.tsx +1 -0
- package/src/core/codemirror/language/types.ts +7 -7
- package/src/core/codemirror/rtc/extension.ts +24 -12
- package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
- package/src/core/codemirror/rtc/loro/sync.ts +1 -0
- package/src/core/codemirror/utils.ts +1 -0
- 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__/codemirror.test.ts +483 -0
- package/src/core/document/__tests__/draft.test.ts +322 -0
- package/src/core/document/__tests__/fixtures.ts +92 -0
- package/src/core/document/__tests__/hooks.test.tsx +386 -0
- package/src/core/document/__tests__/lifecycle-scenarios.test.ts +61 -0
- package/src/core/document/__tests__/model.test.ts +158 -0
- package/src/core/document/__tests__/protocol-fixtures.ts +38 -0
- package/src/core/document/__tests__/recovery-scenarios.test.ts +230 -0
- package/src/core/document/__tests__/scenario-fixture.ts +42 -0
- package/src/core/document/__tests__/scenarios.test.ts +205 -0
- package/src/core/document/__tests__/state.test.ts +438 -0
- package/src/core/document/__tests__/submission.test.ts +203 -0
- package/src/core/document/__tests__/test-host.test.ts +92 -0
- package/src/core/document/__tests__/test-host.ts +247 -0
- package/src/core/document/__tests__/test-notebook.ts +139 -0
- package/src/core/document/__tests__/user.ts +61 -0
- package/src/core/document/codemirror.ts +311 -0
- package/src/core/document/draft.ts +472 -0
- package/src/core/document/hooks.tsx +118 -0
- package/src/core/document/index.ts +22 -0
- package/src/core/document/model.ts +52 -0
- package/src/core/document/select.ts +41 -0
- package/src/core/document/state.ts +374 -0
- package/src/core/document/sync.ts +363 -0
- package/src/core/host/AGENTS.md +12 -2
- 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 +511 -132
- package/src/core/host/__tests__/static.test.ts +167 -0
- package/src/core/host/__tests__/wasm.test.ts +152 -0
- package/src/core/host/backend.ts +10 -0
- package/src/core/host/connection.ts +1 -40
- package/src/core/host/notebook-client.ts +204 -133
- package/src/core/host/resources/attachments.ts +22 -0
- package/src/core/host/resources/document.ts +101 -0
- package/src/core/host/static.ts +70 -0
- package/src/core/host/wasm.ts +92 -0
- package/src/core/hotkeys/actions.ts +2 -4
- package/src/core/islands/__tests__/bootstrap.test.ts +0 -26
- package/src/core/islands/bootstrap.ts +0 -1
- package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
- 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/runtime/__tests__/createWsUrl.test.ts +77 -7
- package/src/core/runtime/__tests__/runtime.test.ts +15 -58
- package/src/core/runtime/runtime.ts +25 -42
- package/src/core/runtime/types.ts +0 -2
- package/src/core/state/__tests__/jotai.test.tsx +108 -0
- package/src/core/state/jotai.ts +1 -1
- package/src/core/static/__tests__/files.test.ts +38 -10
- package/src/core/static/files.ts +2 -0
- package/src/core/wasm/bridge.ts +38 -10
- package/src/core/wasm/store.ts +3 -3
- package/src/core/wasm/worker/bootstrap.ts +2 -1
- package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
- package/src/core/wasm/worker/types.ts +11 -0
- package/src/core/wasm/worker/worker.ts +48 -0
- package/src/core/websocket/__tests__/connection-state.test.ts +0 -12
- package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +153 -70
- package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +151 -428
- package/src/core/websocket/connection-state.ts +0 -10
- 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/useNotebookClient.ts +38 -29
- package/src/core/websocket/useNotebookSession.tsx +1 -14
- 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 +6 -10
- package/src/hooks/useDeepCompareMemoize.ts +1 -1
- package/src/hooks/useDelayElapsed.ts +14 -10
- package/src/hooks/useElapsedTime.ts +3 -3
- package/src/hooks/useEventListener.ts +1 -2
- 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 +0 -4
- package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
- package/src/plugins/core/registerReactComponent.tsx +1 -1
- package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
- package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
- package/src/plugins/impl/DataTablePlugin.tsx +29 -33
- 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 +117 -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 +1 -2
- 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/plotly/PlotlyPlugin.tsx +2 -5
- package/src/plugins/impl/plotly/selection.ts +25 -19
- package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
- 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__/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/numbers.ts +28 -17
- package/src/utils/objects.ts +3 -7
- package/src/utils/once.ts +19 -19
- package/src/utils/tracer.ts +9 -5
- package/src/utils/zod-utils.ts +1 -3
- package/dist/assets/CellStatus-DVOHpdeJ.js +0 -1
- package/dist/assets/JsonOutput-Cx1iyibB.js +0 -11
- package/dist/assets/MarimoErrorOutput-s_5GOX8k.js +0 -10
- package/dist/assets/RunButton-BKoh0zYG.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-i1vfGeVi.js +0 -13
- package/dist/assets/any-language-editor-BdTWdMCI.js +0 -1
- package/dist/assets/arrays-BqUx5USx.js +0 -1
- package/dist/assets/cell-editor-BKJ06H5F.js +0 -21
- package/dist/assets/chat-panel-Cau71Ygr.js +0 -4
- package/dist/assets/chat-ui-C5ql1Up-.js +0 -4
- package/dist/assets/circle-play-CYIb0qT6.js +0 -1
- package/dist/assets/column-preview-BZ_iNhtJ.js +0 -1
- package/dist/assets/command-palette-DDC3kBet.js +0 -1
- package/dist/assets/components-YBmtV0p6.js +0 -1
- package/dist/assets/config-Dy28fDwn.js +0 -1
- package/dist/assets/edit-page-D_Rg0gQO.js +0 -9
- package/dist/assets/feedback-button-Kla7i5RT.js +0 -1
- package/dist/assets/file-explorer-panel-Uk4_QIJQ.js +0 -1
- package/dist/assets/home-page-hmvKlc1w.js +0 -4
- package/dist/assets/index-6b4mf8Ai.css +0 -2
- package/dist/assets/index-ptK1gxmo.js +0 -30
- package/dist/assets/kiosk-mode-BZECfg9k.js +0 -1
- 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-CI5ff9b-.js +0 -1
- package/dist/assets/panels-BCcZ_yLK.js +0 -107
- package/dist/assets/precisionRound-Bq_nmf5u.js +0 -1
- package/dist/assets/process-output-f3C3PD0Z.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-AfZhtEh1.js +0 -1
- package/dist/assets/scratchpad-panel-D2rWg4k8.js +0 -1
- package/dist/assets/session-panel-CnANhcTo.js +0 -1
- package/dist/assets/state-BAJYRhmO.js +0 -1
- package/dist/assets/state-CbjBtkdd.js +0 -3
- package/dist/assets/tooltip-mT3CGest.js +0 -61
- package/dist/assets/tracing-Dz2rYOFL.js +0 -1
- package/dist/assets/tracing-panel-DqQY9V91.js +0 -2
- package/dist/assets/useAsyncData-C7aJJz0Y.js +0 -1
- package/dist/assets/useBoolean-d0Tty94N.js +0 -1
- package/dist/assets/useCellActionButton-DmaFZ4oF.js +0 -1
- package/dist/assets/useDebounce-B_ccBZTp.js +0 -1
- package/dist/assets/useNotebookActions-DHt3s-ia.js +0 -1
- package/dist/assets/vega-component-IouMe13t.js +0 -1
- package/dist/assets/vega-loader.browser-CLRkNgm7.js +0 -6
- package/dist/assets/worker-DTQ7oH2A.js +0 -158
- package/dist/assets/ws-BXWY541V.js +0 -22
- package/src/core/host/__tests__/events.test.ts +0 -46
- package/src/core/host/__tests__/protocol.test.ts +0 -214
- package/src/core/host/events.ts +0 -60
- package/src/core/host/protocol.ts +0 -1943
- package/src/core/state/__tests__/jotai.test.ts +0 -25
- package/src/core/websocket/__tests__/createConnectionTransport.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 -335
- 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/create.ts +0 -31
- package/src/core/websocket/transports/sse.ts +0 -308
- package/src/core/websocket/transports/transport.ts +0 -50
- package/src/core/websocket/transports/ws.ts +0 -98
package/src/hooks/useScript.ts
CHANGED
|
@@ -1,90 +1,87 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
import
|
|
2
|
+
import { useCallback, useSyncExternalStore } from "react";
|
|
3
3
|
|
|
4
4
|
type ScriptStatus = "loading" | "ready" | "error" | "unknown";
|
|
5
5
|
interface ScriptOptions {
|
|
6
6
|
removeOnUnmount?: boolean;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
interface ScriptEntry {
|
|
10
|
+
element: HTMLScriptElement;
|
|
11
|
+
listeners: Set<() => void>;
|
|
12
|
+
/** Whether useScript created the element, and so may remove it. */
|
|
13
|
+
owned: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
9
16
|
const defaultOptions = { removeOnUnmount: false };
|
|
10
17
|
|
|
18
|
+
// One entry per src, so reading the status on render is a map lookup
|
|
19
|
+
const scripts = new Map<string, ScriptEntry>();
|
|
20
|
+
|
|
21
|
+
function findScript(src: string): HTMLScriptElement | undefined {
|
|
22
|
+
// Compare attributes rather than building a selector, which `src` could break
|
|
23
|
+
return [...document.scripts].find(
|
|
24
|
+
(script) => script.getAttribute("src") === src,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function acquireScript(src: string): ScriptEntry {
|
|
29
|
+
const cached = scripts.get(src);
|
|
30
|
+
if (cached?.element.isConnected) {
|
|
31
|
+
return cached;
|
|
32
|
+
}
|
|
33
|
+
const existing = findScript(src);
|
|
34
|
+
const element = existing ?? document.createElement("script");
|
|
35
|
+
const entry: ScriptEntry = {
|
|
36
|
+
element,
|
|
37
|
+
listeners: new Set(),
|
|
38
|
+
owned: !existing,
|
|
39
|
+
};
|
|
40
|
+
const setStatus = (status: ScriptStatus) => {
|
|
41
|
+
element.dataset.status = status;
|
|
42
|
+
entry.listeners.forEach((listener) => listener());
|
|
43
|
+
};
|
|
44
|
+
// Listen for the script's lifetime rather than per subscriber, so the
|
|
45
|
+
// status still settles if the first subscriber unmounts mid-load.
|
|
46
|
+
element.addEventListener("load", () => setStatus("ready"), { once: true });
|
|
47
|
+
element.addEventListener("error", () => setStatus("error"), { once: true });
|
|
48
|
+
if (!existing) {
|
|
49
|
+
element.src = src;
|
|
50
|
+
element.async = true;
|
|
51
|
+
element.dataset.status = "loading";
|
|
52
|
+
document.body.append(element);
|
|
53
|
+
}
|
|
54
|
+
scripts.set(src, entry);
|
|
55
|
+
return entry;
|
|
56
|
+
}
|
|
57
|
+
|
|
11
58
|
export function useScript(
|
|
12
59
|
src: string,
|
|
13
60
|
options: ScriptOptions = defaultOptions,
|
|
14
61
|
) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
);
|
|
19
|
-
return (
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
React.useEffect(() => {
|
|
23
|
-
let script = document.querySelector<HTMLScriptElement>(
|
|
24
|
-
`script[src="${src}"]`,
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
const updateStatus = (newStatus: ScriptStatus) => {
|
|
28
|
-
if (script) {
|
|
29
|
-
script.dataset.status = newStatus;
|
|
30
|
-
setStatus(newStatus);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const handleScriptLoad = () => updateStatus("ready");
|
|
35
|
-
const handleScriptError = () => updateStatus("error");
|
|
36
|
-
|
|
37
|
-
// observes DOM changes and updates the status
|
|
38
|
-
const observeScript = (script: HTMLScriptElement) => {
|
|
39
|
-
const observer = new MutationObserver(() => {
|
|
40
|
-
const newStatus = script.dataset.status;
|
|
41
|
-
if (newStatus) {
|
|
42
|
-
setStatus(newStatus as ScriptStatus);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
observer.observe(script, {
|
|
46
|
-
attributes: true,
|
|
47
|
-
attributeFilter: ["data-status"],
|
|
48
|
-
});
|
|
49
|
-
return observer;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
if (!script) {
|
|
53
|
-
script = document.createElement("script");
|
|
54
|
-
script.src = src;
|
|
55
|
-
script.async = true;
|
|
56
|
-
updateStatus("loading");
|
|
57
|
-
document.body.append(script);
|
|
58
|
-
|
|
59
|
-
script.addEventListener("load", handleScriptLoad);
|
|
60
|
-
script.addEventListener("error", handleScriptError);
|
|
61
|
-
|
|
62
|
-
const observer = observeScript(script);
|
|
62
|
+
const { removeOnUnmount } = options;
|
|
63
|
+
const getSnapshot = useCallback((): ScriptStatus => {
|
|
64
|
+
const cached = scripts.get(src)?.element;
|
|
65
|
+
const element = cached?.isConnected ? cached : findScript(src);
|
|
66
|
+
return (element?.dataset.status as ScriptStatus) || "unknown";
|
|
67
|
+
}, [src]);
|
|
63
68
|
|
|
69
|
+
const subscribe = useCallback(
|
|
70
|
+
(onChange: () => void) => {
|
|
71
|
+
const entry = acquireScript(src);
|
|
72
|
+
entry.listeners.add(onChange);
|
|
64
73
|
return () => {
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (options.removeOnUnmount) {
|
|
71
|
-
script.remove();
|
|
74
|
+
entry.listeners.delete(onChange);
|
|
75
|
+
if (removeOnUnmount && entry.owned && entry.listeners.size === 0) {
|
|
76
|
+
entry.element.remove();
|
|
77
|
+
if (scripts.get(src) === entry) {
|
|
78
|
+
scripts.delete(src);
|
|
72
79
|
}
|
|
73
80
|
}
|
|
74
81
|
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (domStatus) {
|
|
79
|
-
if (domStatus === "ready" || domStatus === "error") {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
const observer = observeScript(script);
|
|
83
|
-
return () => observer.disconnect();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
setStatus("unknown");
|
|
87
|
-
}, [src, options.removeOnUnmount]);
|
|
82
|
+
},
|
|
83
|
+
[src, removeOnUnmount],
|
|
84
|
+
);
|
|
88
85
|
|
|
89
|
-
return
|
|
86
|
+
return useSyncExternalStore(subscribe, getSnapshot);
|
|
90
87
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { type SetStateAction, useCallback, useReducer } from "react";
|
|
3
|
+
|
|
4
|
+
interface KeyedState<T> {
|
|
5
|
+
key: unknown;
|
|
6
|
+
value: T;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type KeyedAction<T> =
|
|
10
|
+
| { type: "reset"; state: KeyedState<T> }
|
|
11
|
+
| { type: "update"; key: unknown; update: SetStateAction<T> };
|
|
12
|
+
|
|
13
|
+
function reduceKeyedState<T>(
|
|
14
|
+
previous: KeyedState<T>,
|
|
15
|
+
action: KeyedAction<T>,
|
|
16
|
+
): KeyedState<T> {
|
|
17
|
+
if (action.type === "reset") {
|
|
18
|
+
return action.state;
|
|
19
|
+
}
|
|
20
|
+
if (!Object.is(previous.key, action.key)) {
|
|
21
|
+
return previous;
|
|
22
|
+
}
|
|
23
|
+
const value =
|
|
24
|
+
typeof action.update === "function"
|
|
25
|
+
? (action.update as (previous: T) => T)(previous.value)
|
|
26
|
+
: action.update;
|
|
27
|
+
return Object.is(previous.value, value)
|
|
28
|
+
? previous
|
|
29
|
+
: { key: action.key, value };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Local state that resets when its owner key changes.
|
|
34
|
+
*
|
|
35
|
+
* The key uses Object.is. NaN matches NaN, 0 differs from -0, and objects match
|
|
36
|
+
* by reference. Keep the key stable between resets. An inline object such as
|
|
37
|
+
* `{ id }` creates a new key on each render and causes a render loop.
|
|
38
|
+
*
|
|
39
|
+
* On a key change, this hook calls initialize() and schedules a state update
|
|
40
|
+
* during render. React retries the calling component before rendering its
|
|
41
|
+
* children. Children and effects receive the reset value without an intervening
|
|
42
|
+
* commit of the old value. This resets only this hook's state. The component
|
|
43
|
+
* stays mounted, and its other hooks keep their state.
|
|
44
|
+
*
|
|
45
|
+
* initialize must be pure. React can call it more than once in Strict Mode or
|
|
46
|
+
* when retrying an interrupted render. A new initializer alone does not reset
|
|
47
|
+
* state. The next key change uses the initializer from that render. After
|
|
48
|
+
* committed key changes A -> B -> A, A starts fresh instead of restoring its
|
|
49
|
+
* earlier value. If React abandons a pending render for B, the visible state
|
|
50
|
+
* for A remains intact, including edits made while B was pending.
|
|
51
|
+
*
|
|
52
|
+
* The setter accepts values or pure updater functions, like useState. To store
|
|
53
|
+
* a function, use setValue(() => callback). Its identity is stable for the same
|
|
54
|
+
* key and changes with the key. Updates from a different key are ignored,
|
|
55
|
+
* including their updater functions. After A -> B -> A, an old setter for A
|
|
56
|
+
* can update A again. This hook does not cancel requests. For request results,
|
|
57
|
+
* use a distinct key for each lifetime so late results stay invalid.
|
|
58
|
+
*
|
|
59
|
+
* React can still call the component for ignored or same-value updates.
|
|
60
|
+
* Render counts do not indicate how many changes React commits to the screen.
|
|
61
|
+
*
|
|
62
|
+
* Use useInternalStateWithSync when the external value itself supplies the
|
|
63
|
+
* reset value. Use this hook when a separate key controls the reset.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* const [expanded, setExpanded] = useStateForKey(tree, () => new Set<string>());
|
|
67
|
+
*
|
|
68
|
+
* @see https://react.dev/reference/react/useState#storing-information-from-previous-renders
|
|
69
|
+
*/
|
|
70
|
+
export function useStateForKey<T>(key: unknown, initialize: () => T) {
|
|
71
|
+
// useState can eagerly evaluate an updater against a suspended render's
|
|
72
|
+
// state. A reducer checks the key when React processes each render's queue,
|
|
73
|
+
// so the still-visible owner can accept edits during a transition.
|
|
74
|
+
const [stored, dispatch] = useReducer(reduceKeyedState<T>, key, (key) => ({
|
|
75
|
+
key,
|
|
76
|
+
value: initialize(),
|
|
77
|
+
}));
|
|
78
|
+
const current = Object.is(stored.key, key)
|
|
79
|
+
? stored
|
|
80
|
+
: { key, value: initialize() };
|
|
81
|
+
if (current !== stored) {
|
|
82
|
+
dispatch({ type: "reset", state: current });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const setValue = useCallback(
|
|
86
|
+
(update: SetStateAction<T>) => {
|
|
87
|
+
dispatch({ type: "update", key, update });
|
|
88
|
+
},
|
|
89
|
+
[key],
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
return [current.value, setValue] as const;
|
|
93
|
+
}
|
package/src/mount.tsx
CHANGED
|
@@ -274,8 +274,6 @@ const mountOptionsSchema = z.object({
|
|
|
274
274
|
.transform((val) => val as api.Notebook["NotebookV1"]),
|
|
275
275
|
]),
|
|
276
276
|
|
|
277
|
-
notebookId: z.string().optional(),
|
|
278
|
-
|
|
279
277
|
/**
|
|
280
278
|
* Runtime configs
|
|
281
279
|
*/
|
|
@@ -286,7 +284,6 @@ const mountOptionsSchema = z.object({
|
|
|
286
284
|
// Lazy by default, but can be overridden by the runtime config
|
|
287
285
|
lazy: z.boolean().default(true),
|
|
288
286
|
authToken: z.string().nullish(),
|
|
289
|
-
notebookId: z.string().optional(),
|
|
290
287
|
}),
|
|
291
288
|
)
|
|
292
289
|
.nullish()
|
|
@@ -367,7 +364,6 @@ function initStore(options: unknown) {
|
|
|
367
364
|
} else {
|
|
368
365
|
store.set(runtimeConfigAtom, {
|
|
369
366
|
...DEFAULT_RUNTIME_CONFIG,
|
|
370
|
-
notebookId: parsedOptions.data.notebookId,
|
|
371
367
|
lazy: false,
|
|
372
368
|
serverToken: parsedOptions.data.serverToken,
|
|
373
369
|
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { DataEditorPlugin } from "../impl/DataEditorPlugin";
|
|
5
|
+
import { DataTablePlugin } from "../impl/DataTablePlugin";
|
|
6
|
+
import { DataFramePlugin } from "../impl/data-frames/DataFramePlugin";
|
|
7
|
+
import { PlotlyPlugin } from "../impl/plotly/PlotlyPlugin";
|
|
8
|
+
import { VegaPlugin } from "../impl/vega/VegaPlugin";
|
|
9
|
+
|
|
10
|
+
describe("plugin object schemas", () => {
|
|
11
|
+
const rows = [{ value: 1, extra: { nested: true } }];
|
|
12
|
+
const styles = { "0": { value: { color: "red", customProperty: 1 } } };
|
|
13
|
+
const tableData = {
|
|
14
|
+
initialValue: [],
|
|
15
|
+
label: null,
|
|
16
|
+
data: rows,
|
|
17
|
+
totalRows: 1,
|
|
18
|
+
totalColumns: 1,
|
|
19
|
+
rowHeaders: [],
|
|
20
|
+
cellStyles: styles,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
it("preserves table rows and arbitrary cell style fields", () => {
|
|
24
|
+
expect(DataTablePlugin.validator.parse(tableData)).toMatchObject({
|
|
25
|
+
data: rows,
|
|
26
|
+
cellStyles: styles,
|
|
27
|
+
pageSize: 10,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("preserves data editor row fields", () => {
|
|
32
|
+
expect(
|
|
33
|
+
DataEditorPlugin.validator.parse({
|
|
34
|
+
initialValue: { edits: [] },
|
|
35
|
+
label: null,
|
|
36
|
+
data: rows,
|
|
37
|
+
editableColumns: "all",
|
|
38
|
+
}),
|
|
39
|
+
).toMatchObject({ data: rows, columnNames: [] });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("preserves table RPC payload fields", () => {
|
|
43
|
+
const functions = DataTablePlugin.functions!;
|
|
44
|
+
const searchResult = {
|
|
45
|
+
data: rows,
|
|
46
|
+
total_rows: 1,
|
|
47
|
+
cell_styles: styles,
|
|
48
|
+
cell_hover_texts: null,
|
|
49
|
+
};
|
|
50
|
+
expect(functions.search.output.parse(searchResult)).toEqual(searchResult);
|
|
51
|
+
expect(functions.get_row_ids!.input.parse({ custom: true })).toEqual({
|
|
52
|
+
custom: true,
|
|
53
|
+
});
|
|
54
|
+
expect(functions.get_data_url!.input.parse({ custom: true })).toEqual({
|
|
55
|
+
custom: true,
|
|
56
|
+
});
|
|
57
|
+
expect(
|
|
58
|
+
functions.get_data_url!.output.parse({ data_url: rows, format: "json" }),
|
|
59
|
+
).toEqual({ data_url: rows, format: "json" });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("preserves dataframe search row fields", () => {
|
|
63
|
+
const result = { data: rows, total_rows: 1 };
|
|
64
|
+
expect(DataFramePlugin.functions!.search.output.parse(result)).toEqual(
|
|
65
|
+
result,
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("preserves Plotly figure and config fields", () => {
|
|
70
|
+
const data = {
|
|
71
|
+
figure: { data: [], layout: { title: "Chart" } },
|
|
72
|
+
config: { responsive: true },
|
|
73
|
+
};
|
|
74
|
+
expect(new PlotlyPlugin().validator.parse(data)).toEqual(data);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("preserves Vega spec and embed options fields and defaults", () => {
|
|
78
|
+
const spec = { mark: "bar", encoding: { x: { field: "value" } } };
|
|
79
|
+
const validator = new VegaPlugin().validator;
|
|
80
|
+
expect(validator.parse({ spec })).toEqual({
|
|
81
|
+
spec,
|
|
82
|
+
chartSelection: true,
|
|
83
|
+
fieldSelection: true,
|
|
84
|
+
embedOptions: {},
|
|
85
|
+
});
|
|
86
|
+
expect(
|
|
87
|
+
validator.parse({ spec, embedOptions: { actions: false } }).embedOptions,
|
|
88
|
+
).toEqual({ actions: false });
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("rejects scalar values where plugin objects are required", () => {
|
|
92
|
+
expect(
|
|
93
|
+
DataTablePlugin.validator.safeParse({ ...tableData, data: [42] }).success,
|
|
94
|
+
).toBe(false);
|
|
95
|
+
expect(
|
|
96
|
+
new PlotlyPlugin().validator.safeParse({ figure: 42, config: {} })
|
|
97
|
+
.success,
|
|
98
|
+
).toBe(false);
|
|
99
|
+
expect(
|
|
100
|
+
new VegaPlugin().validator.safeParse({ spec: {}, embedOptions: 42 })
|
|
101
|
+
.success,
|
|
102
|
+
).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -104,7 +104,7 @@ function PluginSlotInternal<T>(
|
|
|
104
104
|
ref: React.Ref<PluginSlotHandle>,
|
|
105
105
|
): JSX.Element {
|
|
106
106
|
const [childNodes, setChildNodes] = useState<ReactNode>(children);
|
|
107
|
-
const [value, setValue] = useState<T>(getInitialValue
|
|
107
|
+
const [value, setValue] = useState<T>(getInitialValue);
|
|
108
108
|
const { theme } = useTheme();
|
|
109
109
|
|
|
110
110
|
const [parsedResult, setParsedResult] = useState(() => {
|
|
@@ -71,7 +71,7 @@ const CodeEditorComponent = (props: CodeEditorComponentProps) => {
|
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
const handleChange = useEvent((newValue: string) => {
|
|
74
|
-
setLocalValue(
|
|
74
|
+
setLocalValue(newValue);
|
|
75
75
|
if (typeof props.debounce === "number") {
|
|
76
76
|
setValueDebounced(newValue);
|
|
77
77
|
} else if (!props.debounce) {
|
|
@@ -44,7 +44,7 @@ export const DataEditorPlugin = createPlugin<Edits>("marimo-data-editor", {
|
|
|
44
44
|
),
|
|
45
45
|
}),
|
|
46
46
|
label: z.string().nullable(),
|
|
47
|
-
data: z.union([z.string(), z.array(z.
|
|
47
|
+
data: z.union([z.string(), z.array(z.looseObject({}))]),
|
|
48
48
|
fieldTypes: columnToFieldTypesSchema.nullish(),
|
|
49
49
|
columnNames: z.array(z.string()).default([]),
|
|
50
50
|
editableColumns: z.union([z.array(z.string()), z.literal("all")]),
|
|
@@ -258,7 +258,7 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
|
|
|
258
258
|
z.array(z.object({ rowId: z.string(), columnName: z.string() })),
|
|
259
259
|
]),
|
|
260
260
|
label: z.string().nullable(),
|
|
261
|
-
data: z.union([z.string(), z.array(z.
|
|
261
|
+
data: z.union([z.string(), z.array(z.looseObject({}))]),
|
|
262
262
|
rawData: z.union([z.string(), z.array(z.looseObject({}))]).nullish(),
|
|
263
263
|
totalRows: z.union([z.number(), z.literal(TOO_MANY_ROWS)]),
|
|
264
264
|
pagination: z.boolean().default(false),
|
|
@@ -296,7 +296,7 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
|
|
|
296
296
|
hasStableRowId: z.boolean().default(false),
|
|
297
297
|
maxHeight: z.number().optional(),
|
|
298
298
|
cellStyles: z
|
|
299
|
-
.record(z.string(), z.record(z.string(), z.
|
|
299
|
+
.record(z.string(), z.record(z.string(), z.looseObject({})))
|
|
300
300
|
.optional(),
|
|
301
301
|
hoverTemplate: z.string().optional(),
|
|
302
302
|
cellHoverTexts: cellHoverTextSchema,
|
|
@@ -341,28 +341,25 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
|
|
|
341
341
|
)
|
|
342
342
|
.output(
|
|
343
343
|
z.object({
|
|
344
|
-
data: z.union([z.string(), z.array(z.
|
|
344
|
+
data: z.union([z.string(), z.array(z.looseObject({}))]),
|
|
345
345
|
total_rows: z.union([z.number(), z.literal(TOO_MANY_ROWS)]),
|
|
346
346
|
cell_styles: z
|
|
347
|
-
.record(
|
|
348
|
-
z.string(),
|
|
349
|
-
z.record(z.string(), z.object({}).passthrough()),
|
|
350
|
-
)
|
|
347
|
+
.record(z.string(), z.record(z.string(), z.looseObject({})))
|
|
351
348
|
.nullable(),
|
|
352
349
|
cell_hover_texts: cellHoverTextSchema.nullable(),
|
|
353
350
|
raw_data: z.union([z.string(), z.array(z.looseObject({}))]).nullish(),
|
|
354
351
|
}),
|
|
355
352
|
),
|
|
356
|
-
get_row_ids: rpc.input(z.
|
|
353
|
+
get_row_ids: rpc.input(z.looseObject({})).output(
|
|
357
354
|
z.object({
|
|
358
355
|
row_ids: z.array(z.number()),
|
|
359
356
|
all_rows: z.boolean(),
|
|
360
357
|
error: z.string().nullable(),
|
|
361
358
|
}),
|
|
362
359
|
),
|
|
363
|
-
get_data_url: rpc.input(z.
|
|
360
|
+
get_data_url: rpc.input(z.looseObject({})).output(
|
|
364
361
|
z.object({
|
|
365
|
-
data_url: z.union([z.string(), z.array(z.
|
|
362
|
+
data_url: z.union([z.string(), z.array(z.looseObject({}))]),
|
|
366
363
|
format: z.enum(["csv", "json", "arrow"]),
|
|
367
364
|
}),
|
|
368
365
|
),
|
|
@@ -546,10 +543,12 @@ export const LoadingDataTableComponent = memo(
|
|
|
546
543
|
}
|
|
547
544
|
}, [setValue, filters, searchQuery, sorting, props.hasStableRowId]);
|
|
548
545
|
|
|
549
|
-
|
|
550
|
-
|
|
546
|
+
const [previousPageSize, setPreviousPageSize] = useState(props.pageSize);
|
|
547
|
+
// Reconcile before requesting data for the updated table configuration.
|
|
548
|
+
if (previousPageSize !== props.pageSize) {
|
|
549
|
+
setPreviousPageSize(props.pageSize);
|
|
551
550
|
setPaginationState({ pageIndex: 0, pageSize: props.pageSize });
|
|
552
|
-
}
|
|
551
|
+
}
|
|
553
552
|
|
|
554
553
|
// Data loading
|
|
555
554
|
const { data, error, isPending, isFetching } = useAsyncData<{
|
|
@@ -697,12 +696,13 @@ export const LoadingDataTableComponent = memo(
|
|
|
697
696
|
[search, sorting, filters, searchQuery],
|
|
698
697
|
);
|
|
699
698
|
|
|
700
|
-
|
|
701
|
-
|
|
699
|
+
const [previousTotalRows, setPreviousTotalRows] = useState(data?.totalRows);
|
|
700
|
+
if (previousTotalRows !== data?.totalRows) {
|
|
701
|
+
setPreviousTotalRows(data?.totalRows);
|
|
702
702
|
setPaginationState((state) =>
|
|
703
703
|
state.pageIndex === 0 ? state : { ...state, pageIndex: 0 },
|
|
704
704
|
);
|
|
705
|
-
}
|
|
705
|
+
}
|
|
706
706
|
|
|
707
707
|
// Column summaries
|
|
708
708
|
const { data: columnSummaries, error: columnSummariesError } = useAsyncData<
|
|
@@ -835,7 +835,7 @@ const DataTableComponent = ({
|
|
|
835
835
|
sizeBytesIsLoading,
|
|
836
836
|
maxColumns,
|
|
837
837
|
pagination,
|
|
838
|
-
selection,
|
|
838
|
+
selection: requestedSelection,
|
|
839
839
|
value,
|
|
840
840
|
showFilters,
|
|
841
841
|
showDownload,
|
|
@@ -843,7 +843,7 @@ const DataTableComponent = ({
|
|
|
843
843
|
showColumnExplorer,
|
|
844
844
|
showRowExplorer,
|
|
845
845
|
showChartBuilder,
|
|
846
|
-
showDataTypes,
|
|
846
|
+
showDataTypes: requestedShowDataTypes,
|
|
847
847
|
rowHeaders,
|
|
848
848
|
fieldTypes,
|
|
849
849
|
paginationState,
|
|
@@ -916,16 +916,16 @@ const DataTableComponent = ({
|
|
|
916
916
|
}
|
|
917
917
|
const fieldTypesWithoutExternalTypes = toFieldTypes(fieldTypes);
|
|
918
918
|
|
|
919
|
-
return new ColumnChartSpecModel(
|
|
920
|
-
columnSummaries.data || [],
|
|
921
|
-
fieldTypesWithoutExternalTypes,
|
|
922
|
-
columnSummaries.stats,
|
|
923
|
-
columnSummaries.bin_values,
|
|
924
|
-
columnSummaries.value_counts,
|
|
925
|
-
{
|
|
919
|
+
return new ColumnChartSpecModel({
|
|
920
|
+
data: columnSummaries.data || [],
|
|
921
|
+
fieldTypes: fieldTypesWithoutExternalTypes,
|
|
922
|
+
stats: columnSummaries.stats,
|
|
923
|
+
binValues: columnSummaries.bin_values,
|
|
924
|
+
valueCounts: columnSummaries.value_counts,
|
|
925
|
+
opts: {
|
|
926
926
|
includeCharts: columnSummaries.show_charts,
|
|
927
927
|
},
|
|
928
|
-
);
|
|
928
|
+
});
|
|
929
929
|
}, [fieldTypes, columnSummaries]);
|
|
930
930
|
|
|
931
931
|
const rowHeaderNames = new Set(rowHeaders.map(([name]) => name));
|
|
@@ -975,15 +975,11 @@ const DataTableComponent = ({
|
|
|
975
975
|
const fractionDigitsByColumn = useDeepCompareMemoize(computedFractionDigits);
|
|
976
976
|
const shownColumns = memoizedClampedFieldTypes.length;
|
|
977
977
|
|
|
978
|
-
//
|
|
979
|
-
|
|
980
|
-
showDataTypes = false;
|
|
981
|
-
}
|
|
978
|
+
// Only show data types when they are explicitly supplied.
|
|
979
|
+
const showDataTypes = Boolean(fieldTypes && requestedShowDataTypes);
|
|
982
980
|
|
|
983
981
|
// Row/cell selection writes back to the kernel, absent in static exports.
|
|
984
|
-
|
|
985
|
-
selection = null;
|
|
986
|
-
}
|
|
982
|
+
const selection = isStatic ? null : requestedSelection;
|
|
987
983
|
|
|
988
984
|
const columns = useMemo(
|
|
989
985
|
() =>
|
|
@@ -97,13 +97,9 @@ const Dropdown = (props: DropdownProps): JSX.Element => {
|
|
|
97
97
|
id={id}
|
|
98
98
|
disabled={disabled}
|
|
99
99
|
>
|
|
100
|
-
{allowSelectNone ?
|
|
101
|
-
<option value={EMPTY_VALUE} selected={value.length === 0}>
|
|
102
|
-
--
|
|
103
|
-
</option>
|
|
104
|
-
) : null}
|
|
100
|
+
{allowSelectNone ? <option value={EMPTY_VALUE}>--</option> : null}
|
|
105
101
|
{options.map((option) => (
|
|
106
|
-
<option value={option}
|
|
102
|
+
<option value={option} key={option}>
|
|
107
103
|
{option}
|
|
108
104
|
</option>
|
|
109
105
|
))}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { type LucideIcon, ChevronRightIcon, CornerLeftUp } from "lucide-react";
|
|
4
|
-
import { type JSX,
|
|
4
|
+
import { type JSX, useLayoutEffect, useRef, useState } from "react";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import {
|
|
7
7
|
FILE_ICON as FILE_TYPE_ICONS,
|
|
@@ -16,6 +16,7 @@ import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
|
|
|
16
16
|
import { toast } from "@/components/ui/use-toast";
|
|
17
17
|
import { RANDOM_ID_ATTR } from "@/core/dom/ui-element-constants";
|
|
18
18
|
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
19
|
+
import { useDelayElapsed } from "@/hooks/useDelayElapsed";
|
|
19
20
|
import { useInternalStateWithSync } from "@/hooks/useInternalStateWithSync";
|
|
20
21
|
import { cn } from "@/utils/cn";
|
|
21
22
|
import { type FilePath, PathBuilder, Paths } from "@/utils/paths";
|
|
@@ -200,7 +201,6 @@ export const FileBrowser = ({
|
|
|
200
201
|
}: FileBrowserProps): JSX.Element | null => {
|
|
201
202
|
const [path, setPath] = useInternalStateWithSync(initialPath);
|
|
202
203
|
const [isUpdatingPath, setIsUpdatingPath] = useState(false);
|
|
203
|
-
const [showLoadingOverlay, setShowLoadingOverlay] = useState(false);
|
|
204
204
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
205
205
|
const rowRefs = useRef<(HTMLTableRowElement | null)[]>([]);
|
|
206
206
|
const gridRef = useRef<HTMLTableElement | null>(null);
|
|
@@ -219,20 +219,8 @@ export const FileBrowser = ({
|
|
|
219
219
|
return list_directory({ path: path });
|
|
220
220
|
}, [path, randomId, list_directory]);
|
|
221
221
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
setShowLoadingOverlay(false);
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const timeout = window.setTimeout(() => {
|
|
229
|
-
setShowLoadingOverlay(true);
|
|
230
|
-
}, 200);
|
|
231
|
-
|
|
232
|
-
return () => {
|
|
233
|
-
window.clearTimeout(timeout);
|
|
234
|
-
};
|
|
235
|
-
}, [isPending]);
|
|
222
|
+
const loadingDelayElapsed = useDelayElapsed(isPending ? 200 : 0);
|
|
223
|
+
const showLoadingOverlay = isPending && loadingDelayElapsed;
|
|
236
224
|
|
|
237
225
|
// Reset the roving tabindex whenever the listing reloads (a new path or a
|
|
238
226
|
// same-path refresh) so activeIndex never points past the current rows.
|
|
@@ -212,7 +212,7 @@ const Form = ({
|
|
|
212
212
|
// wrapped plugin; this buffered value is in contrast to the actual
|
|
213
213
|
// value of the plugin, which is the value of the wrapped plugin when
|
|
214
214
|
// the submit button was last clicked/activated.
|
|
215
|
-
const [internalValue, setInternalValue] = useState<T>(
|
|
215
|
+
const [internalValue, setInternalValue] = useState<T>(() =>
|
|
216
216
|
UI_ELEMENT_REGISTRY.lookupValue(elementId),
|
|
217
217
|
);
|
|
218
218
|
|
|
@@ -119,6 +119,7 @@ const RefreshComponent = ({ setValue, data }: IPluginProps<Value, Data>) => {
|
|
|
119
119
|
<Labeled label={data.label}>
|
|
120
120
|
<span className="inline-flex items-center text-secondary-foreground rounded shadow-sm-solid">
|
|
121
121
|
<Button
|
|
122
|
+
aria-label="Refresh"
|
|
122
123
|
data-testid="marimo-plugin-refresh-button"
|
|
123
124
|
variant="secondary"
|
|
124
125
|
size="icon"
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
import { type JSX,
|
|
2
|
+
import { type JSX, useId, useMemo } from "react";
|
|
3
3
|
import { useLocale } from "react-aria";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { NumberField } from "@/components/ui/number-field";
|
|
6
|
+
import { useInternalStateWithSync } from "@/hooks/useInternalStateWithSync";
|
|
6
7
|
import { cn } from "@/utils/cn";
|
|
7
8
|
import { clamp } from "@/utils/math";
|
|
8
9
|
import {
|
|
@@ -156,13 +157,10 @@ const SliderComponent = ({
|
|
|
156
157
|
const { locale } = useLocale();
|
|
157
158
|
|
|
158
159
|
// Hold internal value
|
|
159
|
-
const [internalValue, setInternalValue] =
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// oxlint-disable-next-line react/no-deriving-state-in-effects -- Synchronize the editable buffer when the controlled value changes.
|
|
164
|
-
setInternalValue(value);
|
|
165
|
-
}, [value]);
|
|
160
|
+
const [internalValue, setInternalValue] = useInternalStateWithSync(
|
|
161
|
+
value,
|
|
162
|
+
Object.is,
|
|
163
|
+
);
|
|
166
164
|
|
|
167
165
|
const stepsConfig = useMemo(() => computeStepsConfig(steps), [steps]);
|
|
168
166
|
|