@marimo-team/islands 0.25.2 → 0.25.3-dev100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNSxdKyJ.js → ConnectedDataExplorerComponent-Dhf5EGs5.js} +14 -14
  2. package/dist/{ErrorBoundary-HusVNio-.js → ErrorBoundary-GScQVnJ8.js} +8 -8
  3. package/dist/{abnfDiagram-VCTEODGH-DQDjNXt3.js → abnfDiagram-VCTEODGH-Di400kYu.js} +2 -2
  4. package/dist/any-language-editor-k2B8YI3Y.js +75 -0
  5. package/dist/{architectureDiagram-5GKGNRK7-Nmls7Wxn.js → architectureDiagram-5GKGNRK7-BdTJcsNJ.js} +1 -1
  6. package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
  7. package/dist/assets/__vite-browser-external-35VXYmnw.js +1 -0
  8. package/dist/assets/{worker-HPY9fbhz.js → worker-Dq8y-WM5.js} +2 -2
  9. package/dist/{button-CvjkwdSk.js → button-BxB3RDjN.js} +1 -1
  10. package/dist/{capabilities-CQ5IjhzL.js → capabilities-BgR0osfN.js} +2 -2
  11. package/dist/{chat-ui-BDpymeoe.js → chat-ui-BIgPxfPP.js} +1193 -1194
  12. package/dist/{check-EDwSwP89.js → check-P35dc2dH.js} +1 -1
  13. package/dist/{chunk-IMKFNOWR-CoIFEo-1.js → chunk-IMKFNOWR-mtD7aJBw.js} +1 -1
  14. package/dist/{chunk-SHT3W25Y-lHiGx3ZC.js → chunk-SHT3W25Y-DybB1ShH.js} +1 -1
  15. package/dist/{chunk-SVP7TREG-BvsHIrY_.js → chunk-SVP7TREG-DNhhdCex.js} +1 -1
  16. package/dist/{chunk-TICWLB2K-BC8YIm_n.js → chunk-TICWLB2K-NhAQhlDf.js} +1 -1
  17. package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
  18. package/dist/{classDiagram-ZZMXUADV-Bt98lkkv.js → classDiagram-ZZMXUADV-GTL0l5xS.js} +2 -2
  19. package/dist/{classDiagram-v2-VYDZK3BY-D9bfbcC3.js → classDiagram-v2-VYDZK3BY-BWrM2ruI.js} +2 -2
  20. package/dist/{code-visibility-4aW5326K.js → code-visibility-DnqM5oW_.js} +17 -17
  21. package/dist/{common-Cjo7bjRK.js → common-D2HSo8Gy.js} +6091 -5697
  22. package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-CPoxUG7G.js} +3 -3
  23. package/dist/{cynefinDiagram-5FMLGOSQ-CgWra7wt.js → cynefinDiagram-5FMLGOSQ-CX22wa7c.js} +1 -1
  24. package/dist/{dagre-GXQ25YYZ-CFZ8-35c.js → dagre-GXQ25YYZ-Df6Pp-cl.js} +1 -1
  25. package/dist/{data-grid-overlay-editor-CvdQXfSk.js → data-grid-overlay-editor-BgIMQs9G.js} +5 -5
  26. package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
  27. package/dist/{diagram-S7CK7UJ4-lCNQrW7d.js → diagram-S7CK7UJ4-CctU9kVx.js} +1 -1
  28. package/dist/{diagram-UQ7AKVKN-CQPQ6pP_.js → diagram-UQ7AKVKN-FVHjCF9j.js} +1 -1
  29. package/dist/{diagram-VX7I27RA-Cf3b0llz.js → diagram-VX7I27RA-BSDbwFFv.js} +1 -1
  30. package/dist/{diagram-Z3DM3KII-Bt4QLrpn.js → diagram-Z3DM3KII-BX8kW7Nc.js} +1 -1
  31. package/dist/{dialog-CEBFZNyc.js → dialog-C1PcpCqX.js} +3 -3
  32. package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
  33. package/dist/{dropdown-menu-CP_qCYHC.js → dropdown-menu-B_7xD2X4.js} +177 -169
  34. package/dist/{ebnfDiagram-PWID7BFC-D3NAyPFY.js → ebnfDiagram-PWID7BFC-DtapdLet.js} +2 -2
  35. package/dist/{erDiagram-RLTQ6QDP-Tlu09_Nm.js → erDiagram-RLTQ6QDP-BAru9jHt.js} +1 -1
  36. package/dist/{error-banner-Da600cwz.js → error-banner-Sl2FlSJ4.js} +12 -7
  37. package/dist/{cells-C8iCYvLU.js → error-entries-TAO6kEhF.js} +1573 -1504
  38. package/dist/{feedback-button-ZuLnINhC.js → feedback-button-BXcB6llx.js} +122 -123
  39. package/dist/{flowDiagram-HODETNUW-CAYbpJmV.js → flowDiagram-HODETNUW-CjaeFeea.js} +2 -2
  40. package/dist/{formats-BGh2thRc.js → formats-V8ifA0Bk.js} +3 -3
  41. package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
  42. package/dist/{glide-data-editor-qM9FSJA9.js → glide-data-editor-S-_J-7SO.js} +370 -368
  43. package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Bt8_pBa2.js} +16 -16
  44. package/dist/{globals-Ci7B9t6C.js → globals-D4vBwcD6.js} +3 -3
  45. package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
  46. package/dist/{html-to-image-CZY1pIZH.js → html-to-image-CWdayGZU.js} +35 -35
  47. package/dist/{infoDiagram-27XIBGKW-XrDxhr0x.js → infoDiagram-27XIBGKW-B1hmUggC.js} +1 -1
  48. package/dist/{ishikawaDiagram-5VMMS53U-CD1muzov.js → ishikawaDiagram-5VMMS53U-DUeT3t-d.js} +1 -1
  49. package/dist/{kanban-definition-UXKFOSKX-Dg9W142_.js → kanban-definition-UXKFOSKX-DEUnNNgc.js} +1 -1
  50. package/dist/{label-BDKF3Afc.js → label-BRnUC0NX.js} +2 -2
  51. package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
  52. package/dist/{loader-BQOfjTIJ.js → loader-Bm8cy3e8.js} +12 -12
  53. package/dist/main.js +3262 -3044
  54. package/dist/{mermaid-CiAXjLpA.js → mermaid-B0fXpsNP.js} +38 -38
  55. package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
  56. package/dist/{mindmap-definition-YA3MSWOX-DgCidgYJ.js → mindmap-definition-YA3MSWOX-CPexlhDQ.js} +1 -1
  57. package/dist/{number-overlay-editor-DunDxAzh.js → number-overlay-editor-CUrUm7Np.js} +3 -3
  58. package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
  59. package/dist/{once-BtE-wKRL.js → once-eG8Vmvjd.js} +1 -1
  60. package/dist/{pegDiagram-XKGWAZYB-coOYYKB6.js → pegDiagram-XKGWAZYB-CMpp1OOQ.js} +2 -2
  61. package/dist/{pieDiagram-E7YTZNPT-BnXTc3bj.js → pieDiagram-E7YTZNPT-DlMWg5t1.js} +1 -1
  62. package/dist/precisionRound-DhAzeRcm.js +49 -0
  63. package/dist/{process-output-e4jj9Ijt.js → process-output-DD-oSvZk.js} +5 -5
  64. package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
  65. package/dist/{railroadDiagram-O6MQD6OU-B28-u83n.js → railroadDiagram-O6MQD6OU-DBJfseeO.js} +2 -2
  66. package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
  67. package/dist/{requirementDiagram-BXWQKSXE-CdYCTMu0.js → requirementDiagram-BXWQKSXE-BC7V639r.js} +1 -1
  68. package/dist/{reveal-component-A7HedxmE.js → reveal-component-CFzdsOg8.js} +23 -23
  69. package/dist/{select-BButFi-d.js → select-B0LRDH0g.js} +4 -4
  70. package/dist/{sequenceDiagram-WJ2MYXX4-CM3aRAe_.js → sequenceDiagram-WJ2MYXX4-CgesU6F-.js} +1 -1
  71. package/dist/{spec-BwarBpUJ.js → spec-CcT0slVc.js} +4 -4
  72. package/dist/{stateDiagram-D77RDMKH-DOZUMVj8.js → stateDiagram-D77RDMKH-lH_BUhs3.js} +2 -2
  73. package/dist/{stateDiagram-v2-MP3YSRHH-CISYxma6.js → stateDiagram-v2-MP3YSRHH-8LL6FrBe.js} +2 -2
  74. package/dist/style.css +1 -1
  75. package/dist/{swimlanes-42K2YHIH-BkSppxp8.js → swimlanes-42K2YHIH-Cuf9B9kE.js} +1 -1
  76. package/dist/{swimlanesDiagram-VR7AAH4N-c8G5pGxC.js → swimlanesDiagram-VR7AAH4N-BoMMtOPh.js} +2 -2
  77. package/dist/{swiper-component-DKntyn8p.js → swiper-component-DfhRvAFi.js} +2 -2
  78. package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
  79. package/dist/{timeline-definition-24CTP7MA-CaMQayqK.js → timeline-definition-24CTP7MA-yzzw4TF4.js} +1 -1
  80. package/dist/{toDate-BNJf01u1.js → toDate-DKH0_kMo.js} +53 -54
  81. package/dist/{toast-B9UMEi8u.js → toast-CFWpihJU.js} +4 -4
  82. package/dist/{tooltip-BO1REbqs.js → tooltip-CKO1pHJo.js} +2 -2
  83. package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-DBCITLeF.js} +1 -1
  84. package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-D0mLJstx.js} +15 -14
  85. package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-c_yJtkAe.js} +2 -2
  86. package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-BpPWi3G3.js} +1 -1
  87. package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-BkdeVdXC.js} +1 -1
  88. package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-Dum8WSXq.js} +3 -3
  89. package/dist/{useTheme-CRnjkku0.js → useTheme-CfeaVw5D.js} +6 -6
  90. package/dist/{vega-component-DG29TAB7.js → vega-component-DgytCKrL.js} +147 -147
  91. package/dist/{vega-embed-container-BNHE18G9.js → vega-embed-container--LYZqguO.js} +2 -2
  92. package/dist/{vega-loader.browser-DNST6uVf.js → vega-loader.browser-B9Pz0EGP.js} +298 -507
  93. package/dist/{vennDiagram-4TSXK5OY-CF8pfaK3.js → vennDiagram-4TSXK5OY-eJOrTqqC.js} +1 -1
  94. package/dist/{wardleyDiagram-VM6X3IG4-dGPu39d-.js → wardleyDiagram-VM6X3IG4-TILPvee6.js} +1 -1
  95. package/dist/{xychartDiagram-S5SC5T6Z-CP6-9q56.js → xychartDiagram-S5SC5T6Z-DUPvRwwJ.js} +2 -2
  96. package/package.json +8 -5
  97. package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
  98. package/src/__tests__/host-imports.test.ts +101 -0
  99. package/src/__tests__/mount.test.ts +8 -8
  100. package/src/components/app-config/ai-config.tsx +7 -2
  101. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  102. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  103. package/src/components/chat/acp/blocks.tsx +2 -2
  104. package/src/components/chat/chat-components.tsx +2 -0
  105. package/src/components/chat/chat-panel.tsx +9 -8
  106. package/src/components/chat/chat-utils.ts +4 -8
  107. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  108. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  109. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  110. package/src/components/data-table/__tests__/context-menu.test.ts +176 -0
  111. package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +24 -1
  112. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  113. package/src/components/data-table/__tests__/export-actions.test.tsx +434 -6
  114. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  115. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  116. package/src/components/data-table/cell-selection/feature.ts +2 -2
  117. package/src/components/data-table/cell-selection/types.ts +2 -2
  118. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  119. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  120. package/src/components/data-table/charts/charts.tsx +35 -25
  121. package/src/components/data-table/charts/storage.ts +16 -5
  122. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  123. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  124. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  125. package/src/components/data-table/columns.tsx +45 -46
  126. package/src/components/data-table/context-menu.tsx +161 -46
  127. package/src/components/data-table/data-table.tsx +7 -1
  128. package/src/components/data-table/date-filter-inputs.tsx +15 -42
  129. package/src/components/data-table/date-popover.tsx +6 -4
  130. package/src/components/data-table/export-actions.tsx +267 -163
  131. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +2 -2
  132. package/src/components/data-table/schemas.ts +14 -36
  133. package/src/components/data-table/search-highlight.tsx +79 -0
  134. package/src/components/data-table/types.ts +1 -0
  135. package/src/components/data-table/url-detector.tsx +49 -9
  136. package/src/components/data-table/utils.ts +18 -0
  137. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  138. package/src/components/datasources/__tests__/column-preview.test.tsx +63 -2
  139. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  140. package/src/components/datasources/column-preview.tsx +8 -2
  141. package/src/components/datasources/datasources.tsx +47 -35
  142. package/src/components/debugger/debugger-code.tsx +5 -0
  143. package/src/components/dependency-graph/panels.tsx +2 -1
  144. package/src/components/editor/Output.tsx +9 -2
  145. package/src/components/editor/SortableCell.tsx +1 -3
  146. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  147. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
  148. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +210 -34
  149. package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
  150. package/src/components/editor/actions/pair-with-agent-modal.tsx +100 -37
  151. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  152. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  153. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  154. package/src/components/editor/cell/CreateCellButton.tsx +8 -10
  155. package/src/components/editor/cell/DeleteButton.tsx +1 -0
  156. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  157. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  158. package/src/components/editor/cell/code/cell-editor.tsx +1 -1
  159. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  160. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  161. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +30 -1
  162. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  163. package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
  164. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  165. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +366 -0
  166. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
  167. package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
  168. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
  169. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  170. package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
  171. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  172. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  173. package/src/components/editor/connections/components.tsx +1 -1
  174. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  175. package/src/components/editor/controls/command-palette.tsx +22 -10
  176. package/src/components/editor/dynamic-favicon.tsx +21 -40
  177. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +537 -0
  178. package/src/components/editor/errors/send-error-report-button.tsx +69 -0
  179. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  180. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  181. package/src/components/editor/file-tree/file-explorer.tsx +10 -5
  182. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  183. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  184. package/src/components/editor/notebook-banner.tsx +1 -0
  185. package/src/components/editor/output/MarimoErrorOutput.tsx +21 -5
  186. package/src/components/editor/output/MarimoTracebackOutput.tsx +37 -22
  187. package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
  188. package/src/components/editor/output/ansi-reduce.ts +12 -0
  189. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  190. package/src/components/editor/package-alert.tsx +6 -4
  191. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  192. package/src/components/home/components.tsx +17 -31
  193. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  194. package/src/components/icons/large-spinner.tsx +3 -14
  195. package/src/components/icons/spinner.tsx +1 -1
  196. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  197. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  198. package/src/components/pages/home-page.tsx +164 -91
  199. package/src/components/scratchpad/scratchpad.tsx +2 -2
  200. package/src/components/slides/reveal-component.tsx +4 -2
  201. package/src/components/slides/slide-cell-view.tsx +1 -1
  202. package/src/components/slides/slide-form.tsx +1 -0
  203. package/src/components/tracing/tracing.tsx +2 -2
  204. package/src/components/ui/alert-dialog.tsx +1 -1
  205. package/src/core/ai/context/providers/datasource.ts +6 -2
  206. package/src/core/attachments/__tests__/toast.test.tsx +58 -0
  207. package/src/core/attachments/state.ts +18 -0
  208. package/src/core/attachments/toast.ts +15 -0
  209. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  210. package/src/core/cells/__tests__/cell.test.ts +39 -0
  211. package/src/core/cells/__tests__/cells.test.ts +55 -1
  212. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  213. package/src/core/cells/__tests__/error-report.test.ts +139 -0
  214. package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
  215. package/src/core/cells/__tests__/session.test.ts +50 -1
  216. package/src/core/cells/cell.ts +14 -3
  217. package/src/core/cells/cells.ts +3 -1
  218. package/src/core/cells/error-report.ts +63 -0
  219. package/src/core/cells/pending-delete-service.ts +2 -6
  220. package/src/core/cells/session.ts +29 -2
  221. package/src/core/cells/types.ts +10 -1
  222. package/src/core/codemirror/__tests__/setup.test.ts +1 -1
  223. package/src/core/codemirror/keymaps/vim.ts +2 -6
  224. package/src/core/codemirror/language/languages/markdown.ts +1 -1
  225. package/src/core/codemirror/language/languages/python.ts +3 -3
  226. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  227. package/src/core/config/pair.ts +5 -3
  228. package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
  229. package/src/core/datasets/data-source-discovery.ts +4 -6
  230. package/src/core/document/__tests__/draft.test.ts +322 -0
  231. package/src/core/document/__tests__/fixtures.ts +92 -0
  232. package/src/core/document/__tests__/model.test.ts +483 -0
  233. package/src/core/document/__tests__/state.test.ts +446 -0
  234. package/src/core/document/__tests__/submission.test.ts +346 -0
  235. package/src/core/document/draft.ts +472 -0
  236. package/src/core/document/index.ts +10 -0
  237. package/src/core/document/model.ts +53 -0
  238. package/src/core/document/state.ts +401 -0
  239. package/src/core/document/sync.ts +359 -0
  240. package/src/core/edit-app.tsx +2 -2
  241. package/src/core/host/AGENTS.md +16 -0
  242. package/src/core/host/__tests__/attachments.test.ts +56 -0
  243. package/src/core/host/__tests__/document.test.ts +126 -0
  244. package/src/core/host/__tests__/fixtures.ts +41 -0
  245. package/src/core/host/__tests__/notebook-client.test.ts +570 -0
  246. package/src/core/host/connection.ts +42 -0
  247. package/src/core/host/notebook-client.ts +297 -0
  248. package/src/core/host/resources/attachments.ts +22 -0
  249. package/src/core/host/resources/document.ts +101 -0
  250. package/src/core/hotkeys/actions.ts +2 -4
  251. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  252. package/src/core/network/connection.ts +4 -2
  253. package/src/core/network/useConnectionNotice.ts +8 -2
  254. package/src/core/run-app.tsx +2 -2
  255. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  256. package/src/core/runtime/__tests__/runtime.test.ts +18 -17
  257. package/src/core/runtime/runtime.ts +26 -30
  258. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  259. package/src/core/state/jotai.ts +1 -1
  260. package/src/core/static/files.ts +2 -0
  261. package/src/core/wasm/bridge.ts +2 -10
  262. package/src/core/wasm/store.ts +3 -3
  263. package/src/core/wasm/worker/bootstrap.ts +1 -1
  264. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  265. package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -29
  266. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +223 -0
  267. package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +194 -184
  268. package/src/core/websocket/connection-state.ts +60 -0
  269. package/src/core/websocket/state.ts +7 -0
  270. package/src/core/websocket/transports/__tests__/notebook.test.ts +502 -0
  271. package/src/core/websocket/transports/notebook.ts +213 -0
  272. package/src/core/websocket/types.ts +9 -39
  273. package/src/core/websocket/useNotebookClient.ts +89 -0
  274. package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +53 -224
  275. package/src/css/globals.css +5 -0
  276. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  277. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  278. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  279. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  280. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  281. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  282. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  283. package/src/hooks/useAsyncData.ts +3 -3
  284. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  285. package/src/hooks/useDebounce.ts +5 -8
  286. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  287. package/src/hooks/useDelayElapsed.ts +14 -10
  288. package/src/hooks/useElapsedTime.ts +3 -3
  289. package/src/hooks/useInputHistory.ts +4 -7
  290. package/src/hooks/useInternalStateWithSync.ts +6 -6
  291. package/src/hooks/useResizeObserver.ts +4 -7
  292. package/src/hooks/useScript.ts +67 -70
  293. package/src/hooks/useStateForKey.ts +93 -0
  294. package/src/mount.tsx +3 -3
  295. package/src/plugins/core/registerReactComponent.tsx +1 -1
  296. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  297. package/src/plugins/impl/DataTablePlugin.tsx +14 -15
  298. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  299. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  300. package/src/plugins/impl/FormPlugin.tsx +1 -1
  301. package/src/plugins/impl/RangeSliderPlugin.tsx +0 -1
  302. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  303. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  304. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +201 -1
  305. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  306. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  307. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
  308. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  309. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  310. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  311. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  312. package/src/plugins/impl/chat/chat-ui.tsx +12 -10
  313. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  314. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  315. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  316. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  317. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  318. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +0 -1
  319. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  320. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  321. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  322. package/src/plugins/impl/panel/utils.ts +17 -0
  323. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  324. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  325. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  326. package/src/stories/button.stories.tsx +1 -0
  327. package/src/stories/editor.stories.tsx +24 -17
  328. package/src/stories/form-wrapper.stories.tsx +2 -2
  329. package/src/stories/select.stories.tsx +2 -2
  330. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  331. package/src/utils/__tests__/arrays.test.ts +35 -0
  332. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  333. package/src/utils/__tests__/dates.test.ts +116 -0
  334. package/src/utils/__tests__/numbers.test.ts +48 -1
  335. package/src/utils/__tests__/objects.test.ts +21 -0
  336. package/src/utils/arrays.ts +10 -0
  337. package/src/utils/blob.ts +1 -1
  338. package/src/utils/dates.ts +94 -0
  339. package/src/utils/numbers.ts +28 -17
  340. package/src/utils/objects.ts +3 -7
  341. package/src/utils/once.ts +19 -19
  342. package/src/utils/zod-utils.ts +1 -3
  343. package/dist/any-language-editor-B9REGlD9.js +0 -76
  344. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  345. package/dist/error-entries-CzoYkIQZ.js +0 -56
  346. package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
  347. package/dist/precisionRound-DMIVQrt2.js +0 -97
  348. package/src/core/state/__tests__/jotai.test.ts +0 -25
  349. package/src/core/websocket/__tests__/useWebSocket.test.ts +0 -49
  350. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  351. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -338
  352. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  353. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  354. package/src/core/websocket/transports/basic.ts +0 -102
  355. package/src/core/websocket/transports/sse.ts +0 -308
  356. package/src/core/websocket/transports/transport.ts +0 -61
  357. package/src/core/websocket/transports/ws.ts +0 -98
  358. package/src/core/websocket/useWebSocket.tsx +0 -88
@@ -3,7 +3,7 @@ import { t } from "./src-CA_m4ONl.js";
3
3
  import { n } from "./chunk-Y2CYZVJY-DKKS1VyV.js";
4
4
  import { $ as r, H as i, K as a, U as o, a as s, b as c, c as l, et as u, f as d, nt as f, tt as p, v as m, w as h, y as g } from "./chunk-DU6HZSFF-BwiyaNpg.js";
5
5
  import { t as _ } from "./rough.esm-CtV9AnV1.js";
6
- import { p as v } from "./mermaid-CiAXjLpA.js";
6
+ import { p as v } from "./mermaid-B0fXpsNP.js";
7
7
  //#region ../node_modules/.pnpm/khroma@2.1.0/node_modules/khroma/dist/methods/transparentize.js
8
8
  var y = (e, t) => p(e, "a", -t), b = 1e-10;
9
9
  function x(e, t) {
@@ -2,7 +2,7 @@ import { i as e } from "./chunk-75Z2AOVW-D7gxIs2Q.js";
2
2
  import { n as t } from "./src-CA_m4ONl.js";
3
3
  import { n } from "./chunk-Y2CYZVJY-DKKS1VyV.js";
4
4
  import { D as r, H as i, K as a, U as o, a as s, b as c, c as l, v as u, w as d, x as f, y as p } from "./chunk-DU6HZSFF-BwiyaNpg.js";
5
- import { p as m } from "./mermaid-CiAXjLpA.js";
5
+ import { p as m } from "./mermaid-B0fXpsNP.js";
6
6
  import { n as h } from "./mermaid-parser.core-DxuSfYwJ.js";
7
7
  import { t as g } from "./chunk-JWPE2WC7-DJvv1t8U.js";
8
8
  //#region ../node_modules/.pnpm/mermaid@11.17.2/node_modules/mermaid/dist/chunks/mermaid.core/wardleyDiagram-VM6X3IG4.mjs
@@ -1,4 +1,4 @@
1
- import { t as e } from "./linear-Cnaq3Tfb.js";
1
+ import { t as e } from "./linear-DWERh7eI.js";
2
2
  import { n as t } from "./ordinal-c7efuCSS.js";
3
3
  import { t as n } from "./range-BAIvbVHp.js";
4
4
  import { q as r } from "./cubic-OaEBhQPK.js";
@@ -9,7 +9,7 @@ import { n as s } from "./chunk-Y2CYZVJY-DKKS1VyV.js";
9
9
  import { D as c, H as l, K as u, U as d, a as f, b as p, c as m, f as h, v as g, w as _, y as v, z as y } from "./chunk-DU6HZSFF-BwiyaNpg.js";
10
10
  import "./chunk-PWAF6VOD-CQw5qATZ.js";
11
11
  import { t as b } from "./chunk-GMAD6QVW-BhaJx0X3.js";
12
- import { p as x } from "./mermaid-CiAXjLpA.js";
12
+ import { p as x } from "./mermaid-B0fXpsNP.js";
13
13
  //#region ../node_modules/.pnpm/d3-scale@4.0.2/node_modules/d3-scale/src/band.js
14
14
  function S() {
15
15
  var e = t().unknown(void 0), r = e.domain, i = e.range, o = 0, s = 1, c, l, u = !1, d = 0, f = 0, p = .5;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.25.2",
3
+ "version": "0.25.3-dev100",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -57,6 +57,7 @@
57
57
  "@marimo-team/codemirror-mcp": "^0.1.7",
58
58
  "@marimo-team/codemirror-sql": "^0.3.0",
59
59
  "@marimo-team/llm-info": "workspace:*",
60
+ "@marimo-team/client": "workspace:*",
60
61
  "@marimo-team/marimo-api": "workspace:*",
61
62
  "@marimo-team/react-slotz": "^0.2.0",
62
63
  "@marimo-team/smart-cells": "workspace:*",
@@ -152,6 +153,7 @@
152
153
  "typescript-memoize": "^1.1.1",
153
154
  "use-acp": "0.2.6",
154
155
  "use-resize-observer": "^9.1.0",
156
+ "vega": "6.2.0",
155
157
  "vega-lite": "6.4.3",
156
158
  "vega-loader": "^5.1.3",
157
159
  "vega-parser": "^7.1.3",
@@ -183,7 +185,8 @@
183
185
  "preview:islands": "cross-env VITE_MARIMO_VERSION='0.4.6' VITE_MARIMO_ISLANDS=true vite --config islands/vite.config.mts build",
184
186
  "storybook": "storybook dev -p 6006",
185
187
  "build-storybook": "storybook build",
186
- "sync-katex-css": "node --experimental-strip-types scripts/sync-katex-css.ts"
188
+ "sync-katex-css": "node --experimental-strip-types scripts/sync-katex-css.ts",
189
+ "doctor": "npx react-doctor@latest"
187
190
  },
188
191
  "peerDependencies": {
189
192
  "react": "^17 || ^18 || ^19",
@@ -205,7 +208,7 @@
205
208
  "@types/react": "^19.3.0",
206
209
  "@types/react-dom": "^19.3.0",
207
210
  "@types/timestring": "^6.0.5",
208
- "@vitejs/plugin-react": "^6.1.1",
211
+ "@vitejs/plugin-react": "^6.1.2",
209
212
  "@vitest/coverage-v8": "4.1.11",
210
213
  "blob-polyfill": "^7.0.20220408",
211
214
  "cross-env": "^7.0.3",
@@ -215,7 +218,7 @@
215
218
  "oxc-transform-react": "0.145.0",
216
219
  "oxfmt": "^0.68.0",
217
220
  "oxlint": "^1.83.0",
218
- "postcss": "^8.5.28",
221
+ "postcss": "^8.5.29",
219
222
  "postcss-prefix-selector": "^2.2.1",
220
223
  "react": "^19.3.0",
221
224
  "react-dom": "^19.3.0",
@@ -224,7 +227,7 @@
224
227
  "stylelint-config-standard": "^36.0.1",
225
228
  "tailwindcss": "^4.3.3",
226
229
  "vega-typings": "^2.3.0",
227
- "vite": "^8.3.0",
230
+ "vite": "^8.3.4",
228
231
  "vite-plugin-wasm": "^3.6.0",
229
232
  "vitest": "^4.1.11",
230
233
  "yaml": "^2.9.1"
@@ -2,11 +2,12 @@
2
2
 
3
3
  import { readFileSync } from "node:fs";
4
4
  import path from "node:path";
5
- import { pairPreviewSchema } from "@/core/config/pair";
5
+ import { pairConfigSchema } from "@/core/config/pair";
6
6
 
7
7
  // Read the Python-owned resource only in tests to check the shared contract.
8
- export const PAIR_PREVIEW = pairPreviewSchema.parse({
8
+ export const PAIR_CONFIG = pairConfigSchema.parse({
9
9
  command: "uvx marimo@latest",
10
+ hostEnabled: true,
10
11
  templates: JSON.parse(
11
12
  readFileSync(
12
13
  path.resolve(
@@ -0,0 +1,101 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import NodeChildProcess from "node:child_process";
4
+ import NodeFs from "node:fs";
5
+ import NodeOs from "node:os";
6
+ import NodePath from "node:path";
7
+ import NodeUrl from "node:url";
8
+ import { expect, it } from "vitest";
9
+ import { z } from "zod";
10
+
11
+ it("enforces host dependencies for runtime and type imports", () => {
12
+ const frontend = NodeUrl.fileURLToPath(
13
+ new NodeUrl.URL("../../", import.meta.url),
14
+ );
15
+ const directory = NodeFs.mkdtempSync(
16
+ NodePath.join(NodeOs.tmpdir(), "host-imports-"),
17
+ );
18
+ const files = {
19
+ "src/core/host/allowed.ts": `
20
+ import { atom } from "jotai/vanilla";
21
+ import { z } from "zod";
22
+ import * as Protocol from "@marimo-team/client/protocol";
23
+ import { Logger } from "@/utils/Logger";
24
+ import type { Document } from "./document";
25
+ export * from "@/core/host/events";
26
+ type Event = import("./events").NotebookEvent;
27
+ void import("./protocol");
28
+ `,
29
+ "src/core/host/protocol.ts": `
30
+ import type { NotificationMessage as A } from "../kernel/messages";
31
+ import { type NotificationMessage as B } from "@/core/kernel/messages";
32
+ import { NotificationMessage as C } from "../kernel/messages";
33
+ import type { NotificationPayload } from "../kernel/messages";
34
+ `,
35
+ "src/core/host/forbidden.ts": `
36
+ import { useEffect } from "react";
37
+ import type { NotificationMessage } from "../kernel/messages";
38
+ export * from "../kernel/messages";
39
+ export { Logger } from "@/utils/Logger/../other";
40
+ void import("../kernel/messages");
41
+ type Message = import("../kernel/messages").NotificationMessage;
42
+ const module = require("../kernel/messages");
43
+ void import(module);
44
+ import "./__tests__/allowed";
45
+ import "./allowed.test";
46
+ `,
47
+ "src/core/host/__tests__/allowed.ts": `import { useEffect } from "react";`,
48
+ "src/core/host/allowed.test.ts": `import { useEffect } from "react";`,
49
+ "src/core/host/allowed.spec.ts": `import { useEffect } from "react";`,
50
+ "src/core/websocket/allowed.ts": `import { useEffect } from "react";`,
51
+ };
52
+ try {
53
+ for (const [path, source] of Object.entries(files)) {
54
+ const destination = NodePath.join(directory, path);
55
+ NodeFs.mkdirSync(NodePath.dirname(destination), { recursive: true });
56
+ NodeFs.writeFileSync(destination, source);
57
+ }
58
+ const result = NodeChildProcess.spawnSync(
59
+ NodePath.join(frontend, "node_modules/.bin/oxlint"),
60
+ [
61
+ "--config",
62
+ NodePath.join(frontend, ".oxlintrc.json"),
63
+ "--format",
64
+ "json",
65
+ directory,
66
+ ],
67
+ { cwd: frontend, encoding: "utf8" },
68
+ );
69
+ const report = z
70
+ .object({
71
+ diagnostics: z.array(
72
+ z.object({
73
+ severity: z.string(),
74
+ filename: z.string(),
75
+ labels: z.array(z.object({ span: z.object({ line: z.number() }) })),
76
+ }),
77
+ ),
78
+ })
79
+ .parse(JSON.parse(result.stdout));
80
+ const errors = report.diagnostics.filter(
81
+ (diagnostic) => diagnostic.severity === "error",
82
+ );
83
+ expect(
84
+ errors
85
+ .map((error) => ({
86
+ file: NodePath.basename(error.filename),
87
+ line: error.labels[0].span.line,
88
+ }))
89
+ .toSorted((a, b) => a.file.localeCompare(b.file) || a.line - b.line),
90
+ ).toEqual([
91
+ ...[2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map((line) => ({
92
+ file: "forbidden.ts",
93
+ line,
94
+ })),
95
+ ...[2, 3, 4, 5].map((line) => ({ file: "protocol.ts", line })),
96
+ ]);
97
+ expect(result.status).toBe(1);
98
+ } finally {
99
+ NodeFs.rmSync(directory, { recursive: true, force: true });
100
+ }
101
+ });
@@ -8,8 +8,8 @@ import { connectionAtom } from "@/core/network/connection";
8
8
  import { store } from "@/core/state/jotai";
9
9
  import { isStaticNotebook } from "@/core/static/static-state";
10
10
  import { ConnectionState } from "@/core/websocket/types";
11
- import { pairPreviewAtom } from "@/core/config/pair";
12
- import { PAIR_PREVIEW } from "./fixtures/pair-preview";
11
+ import { pairConfigAtom } from "@/core/config/pair";
12
+ import { PAIR_CONFIG } from "./fixtures/pair-config";
13
13
  import { mount, visibleForTesting } from "../mount";
14
14
 
15
15
  // Mock React DOM
@@ -84,20 +84,20 @@ describe("mount", () => {
84
84
  mockElement,
85
85
  );
86
86
 
87
- it("hydrates the server's Pair preview configuration", () => {
88
- const error = mountRead({ pairPreview: PAIR_PREVIEW });
87
+ it("hydrates the server's Pair configuration", () => {
88
+ const error = mountRead({ pairConfig: PAIR_CONFIG });
89
89
 
90
90
  expect(error).toBeUndefined();
91
- expect(store.get(pairPreviewAtom)).toEqual(PAIR_PREVIEW);
91
+ expect(store.get(pairConfigAtom)).toEqual(PAIR_CONFIG);
92
92
  });
93
93
 
94
- it("uses legacy Pair behavior when the host omits preview configuration", () => {
95
- store.set(pairPreviewAtom, PAIR_PREVIEW);
94
+ it("clears Pair configuration when the server omits it", () => {
95
+ store.set(pairConfigAtom, PAIR_CONFIG);
96
96
 
97
97
  const error = mountRead();
98
98
 
99
99
  expect(error).toBeUndefined();
100
- expect(store.get(pairPreviewAtom)).toBeUndefined();
100
+ expect(store.get(pairConfigAtom)).toBeUndefined();
101
101
  });
102
102
 
103
103
  describe("connection state initialization", () => {
@@ -48,6 +48,7 @@ import { DEFAULT_AI_MODEL, type UserConfig } from "@/core/config/config-schema";
48
48
  import { isWasm } from "@/core/wasm/utils";
49
49
  import { cn } from "@/utils/cn";
50
50
  import { Events } from "@/utils/events";
51
+ import { Objects } from "@/utils/objects";
51
52
  import { Strings } from "@/utils/strings";
52
53
  import { AIModelDropdown, getProviderLabel } from "../ai/ai-model-dropdown";
53
54
  import {
@@ -501,6 +502,7 @@ const ModelListItem: React.FC<ModelListItemProps> = ({
501
502
  <ModelInfoCard model={model} />
502
503
  {model.custom && (
503
504
  <Button
505
+ aria-label={`Delete model ${model.name}`}
504
506
  variant="ghost"
505
507
  size="icon"
506
508
  onClick={handleDelete}
@@ -681,7 +683,10 @@ export const CustomProvidersConfig: React.FC<AiConfigProps> = ({
681
683
  };
682
684
 
683
685
  const removeProvider = (providerName: string) => {
684
- const { [providerName]: _, ...rest } = customProviders;
686
+ const rest = Objects.filter(
687
+ customProviders,
688
+ (_, name) => name !== providerName,
689
+ );
685
690
  // Reset to clear nested dirty state, then set new value
686
691
  form.resetField("ai.custom_providers");
687
692
  form.setValue("ai.custom_providers", rest, { shouldDirty: true });
@@ -1846,7 +1851,7 @@ export const AddModelForm: React.FC<{
1846
1851
  id={modelNameInputId}
1847
1852
  value={modelName}
1848
1853
  onChange={(e) => setModelName(e.target.value)}
1849
- placeholder="gpt-4"
1854
+ placeholder="gpt-5.6-sol"
1850
1855
  className="text-xs mb-0"
1851
1856
  />
1852
1857
  </div>
@@ -77,6 +77,23 @@ describe("useMessageQueue", () => {
77
77
  expect(result.current.messages).toEqual([]);
78
78
  });
79
79
 
80
+ it("preserves multiple enqueues and flushes before React renders", () => {
81
+ const { result } = renderHook(() => useMessageQueue());
82
+ const send = vi.fn();
83
+
84
+ act(() => {
85
+ result.current.enqueue([textPart("first")]);
86
+ result.current.enqueue([textPart("second")]);
87
+ result.current.flushNext(send);
88
+ result.current.flushNext(send);
89
+ });
90
+
91
+ expect(send).toHaveBeenNthCalledWith(1, [textPart("first")]);
92
+ expect(send).toHaveBeenNthCalledWith(2, [textPart("second")]);
93
+ expect(result.current.messages).toEqual([]);
94
+ expect(result.current.hasQueuedRef.current).toBe(false);
95
+ });
96
+
80
97
  it("flushNext on an empty queue is a no-op", () => {
81
98
  const { result } = renderHook(() => useMessageQueue());
82
99
  const send = vi.fn();
@@ -74,7 +74,7 @@ describe("state utility functions", () => {
74
74
  const newState = addSession(initialState, session);
75
75
 
76
76
  // Remove the dynamic tabId for snapshot comparison
77
- const { tabId, ...sessionWithoutId } = newState.sessions[0];
77
+ const { tabId: _tabId, ...sessionWithoutId } = newState.sessions[0];
78
78
  expect({
79
79
  ...newState,
80
80
  sessions: [sessionWithoutId],
@@ -116,7 +116,7 @@ describe("state utility functions", () => {
116
116
  const newState = addSession(initialState, newSession);
117
117
 
118
118
  // Remove dynamic tabId for snapshot
119
- const { tabId, ...sessionWithoutId } = newState.sessions[0];
119
+ const { tabId: _tabId, ...sessionWithoutId } = newState.sessions[0];
120
120
  expect({
121
121
  ...newState,
122
122
  sessions: [sessionWithoutId],
@@ -328,9 +328,9 @@ export const PlansBlock = (props: { data: PlanNotificationEvent[] }) => {
328
328
  </span>
329
329
  </div>
330
330
  <ul className="flex flex-col gap-1">
331
- {plans.map((item, index) => (
331
+ {plans.map((item) => (
332
332
  <li
333
- key={`${item.status}-${index}`}
333
+ key={item.content}
334
334
  className={`
335
335
  flex items-center gap-2 px-2 py-1 rounded
336
336
  `}
@@ -124,6 +124,7 @@ export const AddContextButton = ({
124
124
  return (
125
125
  <Tooltip content="Add context">
126
126
  <Button
127
+ aria-label="Add context"
127
128
  variant="text"
128
129
  size="icon"
129
130
  onClick={handleAddContext}
@@ -148,6 +149,7 @@ export const AttachFileButton = ({
148
149
  <>
149
150
  <Tooltip content="Attach a file">
150
151
  <Button
152
+ aria-label="Attach a file"
151
153
  variant="text"
152
154
  size="icon"
153
155
  onClick={() => fileInputRef.current?.click()}
@@ -166,7 +166,12 @@ const ChatHeader: React.FC<ChatHeaderProps> = ({
166
166
  return (
167
167
  <div className="flex border-b px-2 py-1 justify-between shrink-0 items-center">
168
168
  <Tooltip content="New chat">
169
- <Button variant="text" size="icon" onClick={onNewChat}>
169
+ <Button
170
+ aria-label="New chat"
171
+ variant="text"
172
+ size="icon"
173
+ onClick={onNewChat}
174
+ >
170
175
  <PlusIcon className="h-4 w-4" />
171
176
  </Button>
172
177
  </Tooltip>
@@ -716,14 +721,10 @@ const ChatPanelBody = () => {
716
721
  );
717
722
 
718
723
  const isLoading = status === "submitted" || status === "streaming";
719
- // Read via a ref so the queue-vs-send decision stays correct even when it is
720
- // made after an `await` (e.g. resolving @-context), by which point the render
721
- // closure's `isLoading` may be stale.
722
- const isLoadingRef = useRef(isLoading);
723
- isLoadingRef.current = isLoading;
724
-
724
+ // useEvent reads the latest loading state, even when called after an await
725
+ // (e.g. resolving @-context) that outlives the original render's closure.
725
726
  const submitOrQueue = useEvent((parts: ChatMessagePart[]) => {
726
- if (isLoadingRef.current || hasQueuedRef.current) {
727
+ if (isLoading || hasQueuedRef.current) {
727
728
  enqueueUserMessage(parts);
728
729
  } else {
729
730
  sendUserMessage(parts);
@@ -294,16 +294,12 @@ export function useMessageQueue() {
294
294
  // invoked from a callback (e.g. `onFinish`) in the same tick as `enqueue`.
295
295
  const messagesRef = useRef<QueuedUserMessage[]>([]);
296
296
  const hasQueuedRef = useRef(false);
297
- messagesRef.current = messages;
298
- hasQueuedRef.current = messages.length > 0;
299
297
 
300
298
  const enqueue = useEvent((parts: ChatMessagePart[]) => {
301
- setMessages((prev) => {
302
- const next = [...prev, { id: generateUUID(), parts }];
303
- messagesRef.current = next;
304
- hasQueuedRef.current = true;
305
- return next;
306
- });
299
+ const next = [...messagesRef.current, { id: generateUUID(), parts }];
300
+ messagesRef.current = next;
301
+ hasQueuedRef.current = true;
302
+ setMessages(next);
307
303
  });
308
304
 
309
305
  const flushNext = useEvent((send: (parts: ChatMessagePart[]) => void) => {
@@ -8,6 +8,7 @@ import {
8
8
  import { fireEvent, render, screen, within } from "@testing-library/react";
9
9
  import { beforeAll, describe, expect, it, vi } from "vitest";
10
10
  import { TooltipProvider } from "@/components/ui/tooltip";
11
+ import type { PreviewColumn } from "@/plugins/impl/DataTablePlugin";
11
12
  import { ColumnExplorerPanel } from "../column-explorer-panel/column-explorer";
12
13
  import type { FieldTypesWithExternalType } from "../types";
13
14
 
@@ -36,11 +37,13 @@ const TEST_COLUMNS: ColumnDef<Row>[] = [
36
37
  interface HarnessProps {
37
38
  totalColumns?: number;
38
39
  initiallyHidden?: readonly string[];
40
+ previewColumn?: PreviewColumn;
39
41
  }
40
42
 
41
43
  function PanelHarness({
42
44
  totalColumns = 3,
43
45
  initiallyHidden = EMPTY_HIDDEN_COLUMNS,
46
+ previewColumn = vi.fn().mockResolvedValue({}),
44
47
  }: HarnessProps) {
45
48
  const table = useReactTable<Row>({
46
49
  data: [],
@@ -55,7 +58,7 @@ function PanelHarness({
55
58
  });
56
59
  return (
57
60
  <ColumnExplorerPanel
58
- previewColumn={vi.fn().mockResolvedValue({})}
61
+ previewColumn={previewColumn}
59
62
  fieldTypes={FIELD_TYPES}
60
63
  totalRows={3}
61
64
  totalColumns={totalColumns}
@@ -124,6 +127,29 @@ describe("ColumnExplorerPanel search", () => {
124
127
  });
125
128
  });
126
129
 
130
+ describe("ColumnExplorerPanel chart actions", () => {
131
+ it("names chart actions for each expanded column", async () => {
132
+ const previewColumn = vi.fn<PreviewColumn>().mockResolvedValue({
133
+ chart_code: "chart_code",
134
+ chart_spec: null,
135
+ error: null,
136
+ missing_packages: null,
137
+ stats: null,
138
+ });
139
+ renderPanel({ previewColumn });
140
+ fireEvent.click(screen.getByText("cust_age"));
141
+
142
+ for (const column of ["customer_name", "cust_age"]) {
143
+ expect(
144
+ await screen.findByRole("button", {
145
+ name: `Add chart to notebook for ${column}`,
146
+ }),
147
+ ).toBeInTheDocument();
148
+ expect(previewColumn).toHaveBeenCalledWith({ column });
149
+ }
150
+ });
151
+ });
152
+
127
153
  describe("ColumnExplorerPanel header counts", () => {
128
154
  it("uses rendered-subset total when a clipped column is hidden", () => {
129
155
  // Dataset has 100 columns server-side; only 3 are rendered into the
@@ -14,6 +14,7 @@ import {
14
14
  renderCellValue,
15
15
  } from "../columns";
16
16
  import { getMimeValues, isMimeValue, MimeCell } from "../mime-cell";
17
+ import { buildHighlightPattern } from "../search-highlight";
17
18
  import type { FieldTypesWithExternalType } from "../types";
18
19
  import { uniformSample } from "../uniformSample";
19
20
  import { UrlDetector } from "../url-detector";
@@ -240,6 +241,7 @@ describe("generateColumns", () => {
240
241
  // "age" is a number column — should auto right-align
241
242
  // oxlint-disable-next-line typescript/no-explicit-any
242
243
  const cell = (columns[1].cell as any)({
244
+ table: { options: { meta: {} } },
243
245
  column: {
244
246
  columnDef: columns[1],
245
247
  },
@@ -251,6 +253,7 @@ describe("generateColumns", () => {
251
253
  // "name" is a string column — should remain left-aligned
252
254
  // oxlint-disable-next-line typescript/no-explicit-any
253
255
  const nameCell = (columns[0].cell as any)({
256
+ table: { options: { meta: {} } },
254
257
  column: {
255
258
  columnDef: columns[0],
256
259
  },
@@ -271,6 +274,7 @@ describe("generateColumns", () => {
271
274
  // "age" is numeric but explicitly set to left
272
275
  // oxlint-disable-next-line typescript/no-explicit-any
273
276
  const cell = (columns[1].cell as any)({
277
+ table: { options: { meta: {} } },
274
278
  column: {
275
279
  columnDef: columns[1],
276
280
  },
@@ -311,6 +315,7 @@ describe("generateColumns", () => {
311
315
  // Assuming getCellStyleClass is a function that returns a class name
312
316
  // oxlint-disable-next-line typescript/no-explicit-any
313
317
  const cell = (columns[0].cell as any)({
318
+ table: { options: { meta: {} } },
314
319
  column: {
315
320
  columnDef: columns[0],
316
321
  },
@@ -1359,3 +1364,103 @@ test("column_widths sets fixed size and meta.width", () => {
1359
1364
  expect(b?.size).toBeUndefined();
1360
1365
  expect(b?.meta?.width).toBeUndefined();
1361
1366
  });
1367
+
1368
+ describe("renderCellValue with highlightPattern", () => {
1369
+ const createColumn = (
1370
+ dataType: "string" | "number" | "boolean" | "date",
1371
+ dtype = "object",
1372
+ ) =>
1373
+ ({
1374
+ id: "col",
1375
+ columnDef: { meta: { dataType, dtype } },
1376
+ getColumnFormatting: () => undefined,
1377
+ getColumnWrapping: () => undefined,
1378
+ applyColumnFormatting: (value: unknown) => value,
1379
+ }) as unknown as Column<unknown>;
1380
+
1381
+ const renderCell = (
1382
+ column: Column<unknown>,
1383
+ value: unknown,
1384
+ highlightQuery: string,
1385
+ ) =>
1386
+ render(
1387
+ <I18nProvider locale="en-US">
1388
+ <TooltipProvider>
1389
+ {renderCellValue({
1390
+ column,
1391
+ renderValue: () => value,
1392
+ getValue: () => value,
1393
+ selectCell: undefined,
1394
+ cellStyles: "",
1395
+ highlightPattern: buildHighlightPattern(highlightQuery),
1396
+ })}
1397
+ </TooltipProvider>
1398
+ </I18nProvider>,
1399
+ );
1400
+
1401
+ const markTexts = (container: HTMLElement) =>
1402
+ [...container.querySelectorAll("mark")].map((m) => m.textContent);
1403
+
1404
+ it("marks a match in a string cell", () => {
1405
+ const { container } = renderCell(
1406
+ createColumn("string"),
1407
+ "Alice Smith",
1408
+ "smi",
1409
+ );
1410
+ expect(markTexts(container)).toEqual(["Smi"]);
1411
+ });
1412
+
1413
+ it("marks only the middle text when the string has edge whitespace", () => {
1414
+ const { container } = renderCell(createColumn("string"), " a b ", " ");
1415
+ expect(markTexts(container)).toEqual([" "]);
1416
+ const edgeMarkers = container.querySelectorAll("span[aria-label]");
1417
+ expect(edgeMarkers).toHaveLength(2);
1418
+ for (const marker of edgeMarkers) {
1419
+ expect(marker.querySelector("mark")).toBeNull();
1420
+ }
1421
+ });
1422
+
1423
+ it("marks a match in the locale display of a number", () => {
1424
+ const { container } = renderCell(createColumn("number"), 1234.5, "234");
1425
+ expect(markTexts(container)).toEqual(["234"]);
1426
+ expect(container.textContent).toBe("1,234.5");
1427
+ });
1428
+
1429
+ it("does not mark a number when the locale display hides the raw match", () => {
1430
+ const { container } = renderCell(createColumn("number"), 1234, "1234");
1431
+ expect(markTexts(container)).toEqual([]);
1432
+ expect(container.textContent).toBe("1,234");
1433
+ });
1434
+
1435
+ it("marks a boolean cell", () => {
1436
+ const { container } = renderCell(createColumn("boolean"), false, "false");
1437
+ expect(markTexts(container)).toEqual(["False"]);
1438
+ });
1439
+
1440
+ it("marks a date cell by its display string", () => {
1441
+ const { container } = renderCell(
1442
+ createColumn("date", "date"),
1443
+ new Date(2026, 0, 15),
1444
+ "2026",
1445
+ );
1446
+ expect(markTexts(container)).toEqual(["2026"]);
1447
+ });
1448
+
1449
+ it("renders no mark for an empty query", () => {
1450
+ for (const [column, value] of [
1451
+ [createColumn("string"), "Alice"],
1452
+ [createColumn("number"), 12],
1453
+ [createColumn("boolean"), false],
1454
+ [createColumn("date", "date"), new Date(2026, 0, 15)],
1455
+ ] as const) {
1456
+ const { container } = renderCell(column, value, "");
1457
+ expect(markTexts(container)).toEqual([]);
1458
+ }
1459
+ });
1460
+
1461
+ it("renders no mark in the trigger of a long-string popout", () => {
1462
+ const value = "x".repeat(60);
1463
+ const { container } = renderCell(createColumn("string"), value, "xxx");
1464
+ expect(markTexts(container)).toEqual([]);
1465
+ });
1466
+ });