@marimo-team/islands 0.23.9-dev3 → 0.23.9-dev32

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 (289) hide show
  1. package/dist/{ConnectedDataExplorerComponent-2lBNiUv6.js → ConnectedDataExplorerComponent-MJy-Ll40.js} +20 -20
  2. package/dist/ErrorBoundary-rULOrC_p.js +175 -0
  3. package/dist/{ImageComparisonComponent-CNHIsPDj.js → ImageComparisonComponent-CHrI72em.js} +1 -1
  4. package/dist/{Plot-4wn-lMVn.js → Plot-CAYS29h9.js} +1 -1
  5. package/dist/{_baseUniq-CxZRxRRo.js → _baseUniq-B_2Hw7zG.js} +3 -3
  6. package/dist/{any-language-editor-VWs_7v27.js → any-language-editor-DfdpyDv_.js} +23 -23
  7. package/dist/architecture-7HQA4BMR-Kyc44TmC.js +6 -0
  8. package/dist/{architectureDiagram-VXUJARFQ-CXVJxFhH.js → architectureDiagram-VXUJARFQ-CT2SuxNw.js} +15 -15
  9. package/dist/{arrays-CldYf7p7.js → arrays-sEtDRoG4.js} +1 -1
  10. package/dist/assets/__vite-browser-external-BBEFRPue.js +1 -0
  11. package/dist/assets/{worker-CpBbwbQo.js → worker-BoAkAmaG.js} +2 -2
  12. package/dist/{blockDiagram-VD42YOAC-DGDaxR8I.js → blockDiagram-VD42YOAC-Dy7hlFla.js} +7 -7
  13. package/dist/{button-Dj4BTre0.js → button-C5K9fIPF.js} +2 -2
  14. package/dist/{c4Diagram-YG6GDRKO-C2hc6ne8.js → c4Diagram-YG6GDRKO-BXlAmZ8Z.js} +4 -4
  15. package/dist/{capabilities-C9rrYCzf.js → capabilities-BceAxrAW.js} +2 -2
  16. package/dist/{channel-BBoIVUrJ.js → channel-D_PHgcig.js} +1 -1
  17. package/dist/{chat-ui-D3XBept8.js → chat-ui-CpX2YcGy.js} +3084 -3057
  18. package/dist/{check-BcUIXnUT.js → check-DTbrK0zt.js} +1 -1
  19. package/dist/{chunk-4F5CHEZ2-BZq7Kom7.js → chunk-4F5CHEZ2-D9nGEHV8.js} +1 -1
  20. package/dist/{chunk-5FQGJX7Z-BOg95xG5.js → chunk-5FQGJX7Z-BNjes6Yx.js} +5 -5
  21. package/dist/{chunk-ABZYJK2D-D0cLy8Bb.js → chunk-ABZYJK2D-Dz0-H2B5.js} +1 -1
  22. package/dist/{chunk-ATLVNIR6-BXsEjlHF.js → chunk-ATLVNIR6-o0Z5MZLd.js} +1 -1
  23. package/dist/{chunk-B2363JML-D9-XOau1.js → chunk-B2363JML-KEJpLGGP.js} +1 -1
  24. package/dist/{chunk-B4BG7PRW-Q1usn6T3.js → chunk-B4BG7PRW-BL98U9B4.js} +4 -4
  25. package/dist/{chunk-DI55MBZ5-D1qLYNrb.js → chunk-DI55MBZ5-Dwkn0LWm.js} +4 -4
  26. package/dist/{chunk-EXTU4WIE-BKNXdLmD.js → chunk-EXTU4WIE-9sNjmQrB.js} +1 -1
  27. package/dist/{chunk-FRFDVMJY-BSBUAX7r.js → chunk-FRFDVMJY-DzQqMWrl.js} +1 -1
  28. package/dist/{chunk-JA3XYJ7Z-D6c6cOBG.js → chunk-JA3XYJ7Z-C32Y7Epf.js} +2 -2
  29. package/dist/{chunk-JZLCHNYA-BvsPHJmL.js → chunk-JZLCHNYA-C6ftyVMN.js} +4 -4
  30. package/dist/{chunk-N4CR4FBY-8ycT-O9a.js → chunk-N4CR4FBY-DUhGZhZs.js} +5 -5
  31. package/dist/{chunk-PL6DKKU2-B0MTXvyc.js → chunk-PL6DKKU2-D7km-08O.js} +1 -1
  32. package/dist/{chunk-QN33PNHL-Bb-eUBW3.js → chunk-QN33PNHL-0K6SDYn3.js} +1 -1
  33. package/dist/{chunk-QXUST7PY-DV8yRwBd.js → chunk-QXUST7PY-DMhsRpYK.js} +5 -5
  34. package/dist/{chunk-S3R3BYOJ-mQeCz5CE.js → chunk-S3R3BYOJ-oAe3dEbO.js} +3 -3
  35. package/dist/{chunk-SJTYNZTY-CEG4F0pB.js → chunk-SJTYNZTY-BkJrPRFC.js} +1 -1
  36. package/dist/{chunk-TCCFYFTB-d3HOqL2I.js → chunk-TCCFYFTB-D58KeXnC.js} +6 -6
  37. package/dist/{chunk-TQ3KTPDO-DiCtqVSi.js → chunk-TQ3KTPDO-D_yA_wAb.js} +1 -1
  38. package/dist/{chunk-TZMSLE5B-BqW10dHe.js → chunk-TZMSLE5B-yBKS_DQU.js} +1 -1
  39. package/dist/{chunk-UMXZTB3W-97iS1iEl.js → chunk-UMXZTB3W-D7uwvNjd.js} +1 -1
  40. package/dist/{classDiagram-2ON5EDUG--Yh__LHb.js → classDiagram-2ON5EDUG-QjoAcuFE.js} +10 -10
  41. package/dist/{classDiagram-v2-WZHVMYZB-BC7X7Xtc.js → classDiagram-v2-WZHVMYZB-bUCv4gu2.js} +10 -10
  42. package/dist/{clone-BuIIsfA8.js → clone-Q4Fqwn6q.js} +1 -1
  43. package/dist/{code-block-37QAKDTI-BsGy1AOJ.js → code-block-37QAKDTI-m92Yc8pv.js} +2 -2
  44. package/dist/{code-visibility-BKxrBMod.js → code-visibility-Bq6MzjHR.js} +8434 -8599
  45. package/dist/{constants-D0gkYoE2.js → constants-T20xxyNf.js} +2 -2
  46. package/dist/{copy-DLf4aN7I.js → copy-BuQpJEzp.js} +2 -2
  47. package/dist/{dagre-6UL2VRFP-DRBWoQUw.js → dagre-6UL2VRFP-J0JKgwOt.js} +11 -11
  48. package/dist/{dagre-VYEPqXIV.js → dagre-By_QsQgc.js} +11 -11
  49. package/dist/{data-grid-overlay-editor-efe5ZagF.js → data-grid-overlay-editor-mfEJ5475.js} +2 -2
  50. package/dist/{diagram-PSM6KHXK-H66ATWP2.js → diagram-PSM6KHXK-DYgJuNk9.js} +18 -18
  51. package/dist/{diagram-QEK2KX5R-DItl5Wns.js → diagram-QEK2KX5R-CKdBR2sb.js} +14 -14
  52. package/dist/{diagram-S2PKOQOG-CtuW_ZuL.js → diagram-S2PKOQOG-Dpi7mo5W.js} +14 -14
  53. package/dist/dist-0Fif7jnk.js +5 -0
  54. package/dist/{dist-Dh3wkoyH.js → dist-4j4c7bjm.js} +2 -2
  55. package/dist/{dist-CDFZi-QD.js → dist-B3P2fFpz.js} +1 -1
  56. package/dist/{dist-BNyrZfqT.js → dist-B3pZ0Ab6.js} +2 -2
  57. package/dist/dist-B5h_9sHB.js +6 -0
  58. package/dist/dist-B9M6R5ye.js +5 -0
  59. package/dist/dist-BCt3tnck.js +8 -0
  60. package/dist/{dist-BrBucRXs.js → dist-BTfv03uy.js} +2 -2
  61. package/dist/dist-BUIJwMwn.js +8 -0
  62. package/dist/{dist-CYEylvZA.js → dist-BbbIBDiQ.js} +1 -1
  63. package/dist/{dist-KnujRhFL.js → dist-BcuoonNH.js} +4 -4
  64. package/dist/{dist-DJ6zJQZ4.js → dist-Bde4a2kU.js} +2 -2
  65. package/dist/{dist-t_qL7eB8.js → dist-Bfwsv11D.js} +2 -2
  66. package/dist/{dist-CNtV21T_.js → dist-BhM8gdSO.js} +4 -4
  67. package/dist/{dist-nuW5EDYT.js → dist-BotSqB48.js} +2 -2
  68. package/dist/dist-BpquMd3k.js +5 -0
  69. package/dist/dist-BzJsqYfz.js +5 -0
  70. package/dist/{dist-D029TiHd.js → dist-Bz_sYWbr.js} +2 -2
  71. package/dist/{dist-D3ZI9nhS.js → dist-C1BYNeCR.js} +4 -4
  72. package/dist/{dist-Bc5pmZIw.js → dist-C5VC_yzu.js} +1 -1
  73. package/dist/dist-CA5ELXAf.js +6 -0
  74. package/dist/dist-CLBRs6Uv.js +5 -0
  75. package/dist/{dist-Dhk6FMb0.js → dist-CLJWPTX2.js} +3 -3
  76. package/dist/{dist-C34oIrQ9.js → dist-CLUtPrdy.js} +1 -1
  77. package/dist/dist-CStVCMbq.js +5 -0
  78. package/dist/{dist-B8RaFTRF.js → dist-CUCNs1ja.js} +2 -2
  79. package/dist/dist-CZRIEY3Y.js +8 -0
  80. package/dist/{dist-UcOPnRMa.js → dist-CcXxepx6.js} +3 -3
  81. package/dist/dist-CuUHbFD0.js +5 -0
  82. package/dist/{dist-B8BjrFUE.js → dist-Cy1WxgBD.js} +5 -5
  83. package/dist/{dist-WdPUFc56.js → dist-D4CewLk6.js} +1 -1
  84. package/dist/{dist-DMZNjfX4.js → dist-DRfcqpxJ.js} +2 -2
  85. package/dist/dist-DV7Iabxb.js +8 -0
  86. package/dist/{dist-usPCDYx8.js → dist-D_bzzWBm.js} +1 -1
  87. package/dist/{dist-BvCfQQQE.js → dist-DgnE8F-r.js} +1 -1
  88. package/dist/{dist-JEhxD_cn.js → dist-DhHh0jLg.js} +1 -1
  89. package/dist/{dist-DGAfI2rB.js → dist-DqAWR3CS.js} +2 -2
  90. package/dist/{dist--sWVZwjW.js → dist-Du8WkPuU.js} +1 -1
  91. package/dist/dist-DuEeHMvL.js +5 -0
  92. package/dist/{dist-BTyJtnNg.js → dist-DxvORzUR.js} +1 -1
  93. package/dist/{dist-B507mf_I.js → dist-RqXTaiir.js} +2 -2
  94. package/dist/{dist-Yrfc6L0I.js → dist-fQ0ViXGs.js} +3 -3
  95. package/dist/{dist-B4LJpMEg.js → dist-h2c8sZvT.js} +1 -1
  96. package/dist/{dist-C2ej4eOH.js → dist-luvabDEB.js} +2 -2
  97. package/dist/{dist-B52GXZbd.js → dist-p2qyWijU.js} +2 -2
  98. package/dist/{erDiagram-Q2GNP2WA--19X2kU5.js → erDiagram-Q2GNP2WA-BU-m41EQ.js} +10 -10
  99. package/dist/{error-banner-CVkfBUT3.js → error-banner-5bz0L9hS.js} +3 -3
  100. package/dist/{esm-CWp0KQeK.js → esm-BfhQmZjp.js} +4 -4
  101. package/dist/{esm-DjNnlmpf.js → esm-Duie8iU-.js} +23 -23
  102. package/dist/{extends-vAi97cpa.js → extends-BgdxCfYu.js} +6 -6
  103. package/dist/{flatten-CzBvFdvC.js → flatten-Bbw7g6-K.js} +1 -1
  104. package/dist/{flowDiagram-NV44I4VS-DQmWlo7f.js → flowDiagram-NV44I4VS-CRoXKjGq.js} +10 -10
  105. package/dist/{formats-Dsy9kkZu.js → formats-BIKFEOlR.js} +4 -4
  106. package/dist/{ganttDiagram-JELNMOA3-BOGXJ8Lk.js → ganttDiagram-JELNMOA3-7mq5f9cO.js} +7 -7
  107. package/dist/{gitGraph-G5XIXVHT-DGlbae5m.js → gitGraph-G5XIXVHT-DiniR35k.js} +3 -3
  108. package/dist/{gitGraphDiagram-V2S2FVAM-DjzxfF0P.js → gitGraphDiagram-V2S2FVAM-Dfuokq6w.js} +13 -13
  109. package/dist/{glide-data-editor-DucgdjRo.js → glide-data-editor-DjQd6fKp.js} +557 -557
  110. package/dist/{graphlib-CVPKjKCS.js → graphlib-Ns7y5crs.js} +5 -5
  111. package/dist/{hasIn-COs6vImh.js → hasIn-Deg7jl_j.js} +3 -3
  112. package/dist/{html-to-image-CpggM7u1.js → html-to-image-QL7QveRm.js} +115 -110
  113. package/dist/{info-VBDWY6EO-D2lvLLw5.js → info-VBDWY6EO-DVZvGhkQ.js} +3 -3
  114. package/dist/{infoDiagram-HS3SLOUP-ChNufFsP.js → infoDiagram-HS3SLOUP-CEnzWruK.js} +13 -13
  115. package/dist/{input-D4kjoQUB.js → input-Dh0iMVFM.js} +70 -67
  116. package/dist/{isEmpty-Dd8mx_WL.js → isEmpty-CJJMn-QP.js} +1 -1
  117. package/dist/{isSymbol-BvIfMnn6.js → isSymbol-CoUCgMCM.js} +1 -1
  118. package/dist/{journeyDiagram-XKPGCS4Q-BO_O4Ij1.js → journeyDiagram-XKPGCS4Q-8XYSU1GI.js} +3 -3
  119. package/dist/{kanban-definition-3W4ZIXB7-CPpiiiWk.js → kanban-definition-3W4ZIXB7--9pT9z1R.js} +7 -7
  120. package/dist/{label-BLqV33b1.js → label-LWtdw5i8.js} +3 -3
  121. package/dist/{linear-2NnK4cxi.js → linear-B5-AFRiR.js} +2 -2
  122. package/dist/{loader-Dr8Qem8p.js → loader-BWLPpjKK.js} +2 -2
  123. package/dist/main.js +1689 -1570
  124. package/dist/{memoize-C9ltv0Cw.js → memoize-BOtf2yFf.js} +1 -1
  125. package/dist/{merge-CHn7Yx0N.js → merge-Be1CqGnU.js} +1 -1
  126. package/dist/mermaid-4DMBBIKO-DIdL224_.js +6 -0
  127. package/dist/{mermaid-DO-Daq7u.js → mermaid-CAibas-0.js} +44 -44
  128. package/dist/{mermaid-parser.core-DreccfmS.js → mermaid-parser.core-C3XRsazI.js} +8 -8
  129. package/dist/{min-BNz2lZfk.js → min-Dtgc8txR.js} +4 -4
  130. package/dist/{mindmap-definition-VGOIOE7T-CC1_Vl0f.js → mindmap-definition-VGOIOE7T-B-4mnfFG.js} +9 -9
  131. package/dist/{now-Sgq5m3D-.js → now-Ch98bJO_.js} +2 -2
  132. package/dist/{number-overlay-editor-CpKi64Fy.js → number-overlay-editor-D-a0qCT8.js} +1 -1
  133. package/dist/{once-rJImu7SE.js → once-DPuqGUeo.js} +1 -1
  134. package/dist/{packet-DYOGHKS2-CmWtF3uO.js → packet-DYOGHKS2-34raHOiB.js} +3 -3
  135. package/dist/{pick-CRAXxDYn.js → pick-D1Qo8s2C.js} +4 -4
  136. package/dist/{pie-VRWISCQL-B6u8vus8.js → pie-VRWISCQL-BaLlzZa3.js} +3 -3
  137. package/dist/{pieDiagram-ADFJNKIX-Di34MOFQ.js → pieDiagram-ADFJNKIX-Cr3cNpZY.js} +15 -15
  138. package/dist/{precisionRound-CnHPY_5v.js → precisionRound-Tqb4mg-H.js} +1 -1
  139. package/dist/{process-output-X8TR20AK.js → process-output-C657UH7t.js} +36 -28
  140. package/dist/{quadrantDiagram-AYHSOK5B-B9kVk1ny.js → quadrantDiagram-AYHSOK5B-BuNL8Q93.js} +4 -4
  141. package/dist/{radar-ZZBFDIW7-XAmXSa8s.js → radar-ZZBFDIW7-Ci7bfoZa.js} +3 -3
  142. package/dist/{react-vega-Dh6-UKKe.js → react-vega-B0sAlDTL.js} +9 -9
  143. package/dist/react-vega-B6ncY2Tp.js +9 -0
  144. package/dist/{requirementDiagram-UZGBJVZJ-BxGfGYEx.js → requirementDiagram-UZGBJVZJ-BG2lLUN1.js} +9 -9
  145. package/dist/{reveal-component-BMyi2UMr.js → reveal-component-BQijUbYE.js} +33 -33
  146. package/dist/{sankeyDiagram-TZEHDZUN-D09PBJ-n.js → sankeyDiagram-TZEHDZUN-DMal8sps.js} +3 -3
  147. package/dist/{sequenceDiagram-WL72ISMW-t_Dpemj0.js → sequenceDiagram-WL72ISMW-DT6Tk-Eo.js} +4 -4
  148. package/dist/{spec-hVaaZsY5.js → spec-BKuFJIDz.js} +4 -4
  149. package/dist/{stateDiagram-FKZM4ZOC-B18gTP_j.js → stateDiagram-FKZM4ZOC-CB_lodq3.js} +12 -12
  150. package/dist/{stateDiagram-v2-4FDKWEC3-B6e_t14A.js → stateDiagram-v2-4FDKWEC3-E0RGjKsm.js} +10 -10
  151. package/dist/stex-KfRnSHzF.js +4 -0
  152. package/dist/{strings-BiIhGaI8.js → strings-Bu3vlb6W.js} +7 -7
  153. package/dist/style.css +1 -1
  154. package/dist/{swiper-component-DlD2GU2g.js → swiper-component-B2t5sN1q.js} +3 -3
  155. package/dist/{time-C1SGcFMH.js → time-CsmIF9YZ.js} +3 -3
  156. package/dist/{timeline-definition-IT6M3QCI-DJnh1ks5.js → timeline-definition-IT6M3QCI-NfSKRvH0.js} +2 -2
  157. package/dist/{toDate-CIpC_34u.js → toDate-BeKbrOvs.js} +5 -5
  158. package/dist/{tooltip-DRaMBu06.js → tooltip-C5FYOpQc.js} +4 -4
  159. package/dist/{treemap-GDKQZRPO-Du95DV6u.js → treemap-GDKQZRPO-Cl6OQh8D.js} +3 -3
  160. package/dist/{types-Dzuoc3LN.js → types-CVvp1fKr.js} +2 -9
  161. package/dist/{useAsyncData-C56Khv_R.js → useAsyncData-yp6n17kh.js} +2 -2
  162. package/dist/{useDateFormatter-B_9k85Ex.js → useDateFormatter-BA4FCquG.js} +2 -2
  163. package/dist/{useDeepCompareMemoize-Dt98v2ua.js → useDeepCompareMemoize-DJvAHUIC.js} +1 -1
  164. package/dist/{useIframeCapabilities-BkYHTrss.js → useIframeCapabilities-C4JTXTIh.js} +1 -1
  165. package/dist/{useLifecycle-BF6-z62y.js → useLifecycle-CsYXf0Ln.js} +4 -4
  166. package/dist/{useTheme-DykuNHR2.js → useTheme-CK_R9Mn8.js} +24 -21
  167. package/dist/{vega-component-cSdqoAxe.js → vega-component-ikfBfkZO.js} +18 -18
  168. package/dist/{vega-loader.browser-3_z8GoFC.js → vega-loader.browser-CZ-J8Py3.js} +3 -3
  169. package/dist/{xychartDiagram-PRI3JC2R-Dk2d_bX0.js → xychartDiagram-PRI3JC2R-BvwftqMA.js} +9 -9
  170. package/dist/{zod-BWkcDORu.js → zod-CoBiJ5v4.js} +3 -3
  171. package/package.json +1 -1
  172. package/src/components/ai/__tests__/ai-utils.test.ts +43 -38
  173. package/src/components/ai/ai-model-dropdown.tsx +2 -2
  174. package/src/components/app-config/ai-config.tsx +73 -1
  175. package/src/components/app-config/user-config-form.tsx +37 -1
  176. package/src/components/chat/__tests__/chat-utils.test.ts +269 -0
  177. package/src/components/chat/chat-panel.tsx +36 -3
  178. package/src/components/chat/chat-utils.ts +14 -58
  179. package/src/components/data-table/TableBottomBar.tsx +27 -6
  180. package/src/components/data-table/TableTopBar.tsx +7 -1
  181. package/src/components/data-table/__tests__/TableBottomBar.test.tsx +73 -0
  182. package/src/components/data-table/__tests__/column-explorer.test.tsx +128 -0
  183. package/src/components/data-table/__tests__/column-header.test.tsx +110 -277
  184. package/src/components/data-table/__tests__/data-table.test.tsx +52 -1
  185. package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +33 -0
  186. package/src/components/data-table/__tests__/filter-pill-editor.test.tsx +75 -38
  187. package/src/components/data-table/__tests__/filter-pills.test.tsx +287 -0
  188. package/src/components/data-table/__tests__/filter-test-utils.ts +47 -0
  189. package/src/components/data-table/__tests__/filters.test.ts +5 -5
  190. package/src/components/data-table/__tests__/header-items.test.tsx +47 -1
  191. package/src/components/data-table/__tests__/useColumnVisibility.test.ts +42 -0
  192. package/src/components/data-table/add-filter-button.tsx +85 -0
  193. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +98 -26
  194. package/src/components/data-table/column-header.tsx +94 -691
  195. package/src/components/data-table/columns.tsx +3 -4
  196. package/src/components/data-table/context-menu.tsx +26 -12
  197. package/src/components/data-table/data-table.tsx +125 -56
  198. package/src/components/data-table/date-filter-inputs.tsx +13 -10
  199. package/src/components/data-table/export-actions.tsx +17 -6
  200. package/src/components/data-table/filter-by-values-picker.tsx +13 -19
  201. package/src/components/data-table/filter-editor-context.tsx +34 -0
  202. package/src/components/data-table/filter-pill-editor.tsx +152 -175
  203. package/src/components/data-table/filter-pills.tsx +190 -153
  204. package/src/components/data-table/filters/builders.ts +102 -0
  205. package/src/components/data-table/filters/defaults.ts +31 -0
  206. package/src/components/data-table/filters/format.ts +131 -0
  207. package/src/components/data-table/filters/guards.ts +51 -0
  208. package/src/components/data-table/filters/index.ts +7 -0
  209. package/src/components/data-table/filters/operators.ts +76 -0
  210. package/src/components/data-table/filters/serialize.ts +186 -0
  211. package/src/components/data-table/filters/types.ts +33 -0
  212. package/src/components/data-table/header-items.tsx +25 -85
  213. package/src/components/data-table/hooks/use-column-visibility.ts +56 -0
  214. package/src/components/data-table/pagination.tsx +16 -3
  215. package/src/components/data-table/table-explorer-panel/table-explorer-panel.tsx +16 -6
  216. package/src/components/data-table/value-chips.tsx +52 -0
  217. package/src/components/databases/display.tsx +2 -0
  218. package/src/components/datasources/__tests__/utils.test.ts +82 -0
  219. package/src/components/datasources/utils.ts +16 -15
  220. package/src/components/editor/actions/pair-with-agent-modal.tsx +1 -0
  221. package/src/components/editor/errors/mangled-local-chip.tsx +50 -0
  222. package/src/components/editor/output/MarimoErrorOutput.tsx +110 -27
  223. package/src/components/editor/output/MarimoTracebackOutput.tsx +51 -34
  224. package/src/components/editor/renderers/slides-layout/slides-layout.tsx +1 -1
  225. package/src/components/slides/reveal-component.tsx +3 -3
  226. package/src/components/slides/slide-form.tsx +11 -3
  227. package/src/components/ui/number-field.tsx +13 -1
  228. package/src/core/ai/__tests__/model-registry.test.ts +72 -60
  229. package/src/core/ai/model-registry.ts +33 -28
  230. package/src/core/cells/__tests__/actions.test.ts +48 -0
  231. package/src/core/cells/actions.ts +5 -6
  232. package/src/core/codemirror/__tests__/setup.test.ts +29 -0
  233. package/src/core/codemirror/cells/traceback-decorations.ts +1 -1
  234. package/src/core/codemirror/cm.ts +3 -2
  235. package/src/core/codemirror/format.ts +1 -0
  236. package/src/core/codemirror/keymaps/vim.ts +63 -0
  237. package/src/core/codemirror/language/languages/sql/sql.ts +1 -0
  238. package/src/core/codemirror/language/languages/sql/utils.ts +2 -0
  239. package/src/core/config/__tests__/config-schema.test.ts +2 -0
  240. package/src/core/config/config-schema.ts +2 -0
  241. package/src/css/app/Cell.css +0 -1
  242. package/src/plugins/impl/DataTablePlugin.tsx +94 -33
  243. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +1 -0
  244. package/src/plugins/impl/chat/ChatPlugin.tsx +7 -1
  245. package/src/plugins/impl/chat/__tests__/chat-ui.test.ts +278 -0
  246. package/src/plugins/impl/chat/chat-ui.tsx +106 -59
  247. package/src/plugins/impl/chat/types.ts +5 -0
  248. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +8 -6
  249. package/src/stories/dataframe.stories.tsx +1 -0
  250. package/src/utils/__tests__/local-variables.test.ts +132 -0
  251. package/src/utils/dates.ts +39 -0
  252. package/src/utils/local-variables.ts +67 -0
  253. package/dist/ErrorBoundary-D3wrPNma.js +0 -167
  254. package/dist/architecture-7HQA4BMR-CS9jOrqM.js +0 -6
  255. package/dist/assets/__vite-browser-external-CAdMKBac.js +0 -1
  256. package/dist/dist-21ButRCu.js +0 -8
  257. package/dist/dist-B--tLnAP.js +0 -5
  258. package/dist/dist-BoHGySTM.js +0 -5
  259. package/dist/dist-ByAz19Qc.js +0 -5
  260. package/dist/dist-C1Ap5CYU.js +0 -5
  261. package/dist/dist-C93EysN4.js +0 -5
  262. package/dist/dist-CY-lVor6.js +0 -8
  263. package/dist/dist-CYDuv4bR.js +0 -8
  264. package/dist/dist-Cfo5EE2t.js +0 -6
  265. package/dist/dist-CjivSDvN.js +0 -5
  266. package/dist/dist-Cqwx-MH7.js +0 -5
  267. package/dist/dist-DbpcoFAV.js +0 -6
  268. package/dist/dist-FUNenbiQ.js +0 -5
  269. package/dist/dist-zhSud5X3.js +0 -8
  270. package/dist/mermaid-4DMBBIKO-B7VQMwJx.js +0 -6
  271. package/dist/react-vega-Cavbrg4l.js +0 -9
  272. package/dist/stex-ChDHQs3R.js +0 -4
  273. package/src/components/data-table/__tests__/column-header.test.ts +0 -65
  274. package/src/components/data-table/filters.ts +0 -386
  275. /package/dist/{_baseFor-BGiY-cm1.js → _baseFor-4jw-lnCC.js} +0 -0
  276. /package/dist/{clsx-CyyyQ8Ue.js → clsx-CIWA5tNO.js} +0 -0
  277. /package/dist/{defaultLocale-DoeErsX2.js → defaultLocale-BoHTsDG6.js} +0 -0
  278. /package/dist/{defaultLocale-BpsHxBd7.js → defaultLocale-u-3osm0P.js} +0 -0
  279. /package/dist/{dist-CCADb07R.js → dist-DNdhYsgW.js} +0 -0
  280. /package/dist/{emotion-is-prop-valid.esm-DtW2o230.js → emotion-is-prop-valid.esm-DzSb5hsH.js} +0 -0
  281. /package/dist/{invariant-UcGKQEhF.js → invariant-wRzNXIsJ.js} +0 -0
  282. /package/dist/{jsx-runtime-COBk7ree.js → jsx-runtime-DebpN0FN.js} +0 -0
  283. /package/dist/{main-CThhXnXU.js → main-Tj_-QTyF.js} +0 -0
  284. /package/dist/{micromark-factory-space-CwHmg6iz.js → micromark-factory-space-DF2w36zS.js} +0 -0
  285. /package/dist/{ordinal-B43ZeR68.js → ordinal-ArJavP1Q.js} +0 -0
  286. /package/dist/{purify.es-DT70lfR0.js → purify.es-H92eMd9-.js} +0 -0
  287. /package/dist/{range-BOiA8qqU.js → range-C-rmrM1O.js} +0 -0
  288. /package/dist/{react-dom-BWRJ_g_k.js → react-dom-BTJzcVJ9.js} +0 -0
  289. /package/dist/{stex-DrxP7bb3.js → stex-BIsgBmK4.js} +0 -0
@@ -5,17 +5,13 @@ import { useAtomValue } from "jotai";
5
5
  import {
6
6
  BugPlayIcon,
7
7
  ChevronDown,
8
+ ChevronRight,
8
9
  CopyIcon,
9
10
  ExternalLinkIcon,
10
11
  MessageCircleIcon,
11
12
  SearchIcon,
12
13
  } from "lucide-react";
13
14
  import { type JSX, useState } from "react";
14
- import {
15
- Accordion,
16
- AccordionContent,
17
- AccordionItem,
18
- } from "@/components/ui/accordion";
19
15
  import { Button } from "@/components/ui/button";
20
16
  import {
21
17
  DropdownMenu,
@@ -36,13 +32,17 @@ import { isWasm } from "@/core/wasm/utils";
36
32
  import { renderHTML } from "@/plugins/core/RenderHTML";
37
33
  import { sanitizeHtml } from "@/plugins/core/sanitize-html";
38
34
  import { copyToClipboard } from "@/utils/copy";
35
+ import {
36
+ containsMangledLocal,
37
+ splitMangledLocals,
38
+ } from "@/utils/local-variables";
39
39
  import {
40
40
  elementContainsMarimoCellFile,
41
41
  extractAllTracebackInfo,
42
42
  getTracebackInfo,
43
43
  } from "@/utils/traceback";
44
- import { cn } from "../../../utils/cn";
45
44
  import { AIFixButton } from "../errors/auto-fix";
45
+ import { MangledSegments } from "../errors/mangled-local-chip";
46
46
  import { CellLinkTraceback } from "../links/cell-link";
47
47
  import type { OnRefactorWithAI } from "../Output";
48
48
 
@@ -52,8 +52,6 @@ interface Props {
52
52
  onRefactorWithAI?: OnRefactorWithAI;
53
53
  }
54
54
 
55
- const KEY = "item";
56
-
57
55
  /**
58
56
  * List of errors due to violations of Marimo semantics.
59
57
  */
@@ -64,9 +62,12 @@ export const MarimoTracebackOutput = ({
64
62
  }: Props): JSX.Element => {
65
63
  const htmlTraceback = renderHTML({
66
64
  html: traceback,
67
- additionalReplacements: [replaceTracebackFilenames, replaceTracebackPrefix],
65
+ additionalReplacements: [
66
+ replaceTracebackFilenames,
67
+ replaceTracebackPrefix,
68
+ replaceMangledLocal,
69
+ ],
68
70
  });
69
- const [expanded, setExpanded] = useState(true);
70
71
 
71
72
  const lastTracebackLine = lastLine(traceback);
72
73
  const aiEnabled = useAtomValue(aiEnabledAtom);
@@ -86,6 +87,8 @@ export const MarimoTracebackOutput = ({
86
87
 
87
88
  const showSearch = !isStaticNotebook();
88
89
 
90
+ const [isOpen, setIsOpen] = useState(true);
91
+
89
92
  const handleRefactorWithAI = (triggerImmediately: boolean) => {
90
93
  onRefactorWithAI?.({
91
94
  prompt: `My code gives the following error:\n\n${lastTracebackLine}`,
@@ -93,32 +96,29 @@ export const MarimoTracebackOutput = ({
93
96
  });
94
97
  };
95
98
 
96
- const [error, errorMessage] = lastTracebackLine.split(":", 2);
97
-
98
99
  return (
99
100
  <div className="flex flex-col gap-2 min-w-full w-fit">
100
- <Accordion type="single" collapsible={true} value={expanded ? KEY : ""}>
101
- <AccordionItem value={KEY} className="border-none">
102
- <div
103
- className="flex gap-2 h-10 px-2 hover:bg-muted rounded-sm select-none items-center cursor-pointer transition-all"
104
- onClick={() => setExpanded((prev) => !prev)}
105
- >
106
- <ChevronDown
107
- className={cn(
108
- "h-4 w-4 text-muted-foreground transition-transform duration-200 shrink-0",
109
- expanded ? "rotate-180" : "rotate-0",
110
- )}
111
- />
112
- <div className="text-sm inline font-mono">
113
- <span className="text-destructive">{error || "Error"}:</span>{" "}
114
- {errorMessage}
115
- </div>
116
- </div>
117
- <AccordionContent className="text-muted-foreground px-4 pt-2 text-xs overflow-auto">
118
- {htmlTraceback}
119
- </AccordionContent>
120
- </AccordionItem>
121
- </Accordion>
101
+ <button
102
+ type="button"
103
+ onClick={() => setIsOpen(!isOpen)}
104
+ aria-expanded={isOpen}
105
+ aria-label={isOpen ? "Collapse traceback" : "Expand traceback"}
106
+ className="self-start flex items-center gap-1 pt-2 text-muted-foreground/70 hover:text-muted-foreground transition-colors"
107
+ >
108
+ {isOpen ? (
109
+ <ChevronDown className="h-3 w-3" />
110
+ ) : (
111
+ <ChevronRight className="h-3 w-3" />
112
+ )}
113
+ <span className="text-[0.6875rem] uppercase tracking-wider">
114
+ Traceback
115
+ </span>
116
+ </button>
117
+ {isOpen && (
118
+ <div className="text-muted-foreground pr-4 text-xs overflow-auto">
119
+ {htmlTraceback}
120
+ </div>
121
+ )}
122
122
  <div className="flex gap-2">
123
123
  {showAIFix && (
124
124
  <AIFixButton
@@ -249,6 +249,23 @@ export const replaceTracebackFilenames = (domNode: DOMNode) => {
249
249
  }
250
250
  };
251
251
 
252
+ /**
253
+ * Replace any cell-local mangled name (`_cell_<id>_<name>`) inside a text
254
+ * node with a {@link MangledLocalChip}. The mangled name appears in both
255
+ * the final `NameError:` line and inside compiled-cell source lines because
256
+ * the compiler rewrites underscore-prefixed references at AST-visit time.
257
+ */
258
+ export const replaceMangledLocal = (domNode: DOMNode) => {
259
+ if (!(domNode instanceof Text) || !domNode.nodeValue) {
260
+ return;
261
+ }
262
+ if (!containsMangledLocal(domNode.nodeValue)) {
263
+ return;
264
+ }
265
+ const segments = splitMangledLocals(domNode.nodeValue);
266
+ return <MangledSegments segments={segments} />;
267
+ };
268
+
252
269
  export const replaceTracebackPrefix = (domNode: DOMNode) => {
253
270
  if (
254
271
  domNode instanceof Text &&
@@ -55,7 +55,7 @@ export const SlidesLayoutRenderer: React.FC<Props> = ({
55
55
  setLayout={setLayout}
56
56
  activeIndex={resolvedIndex}
57
57
  onSlideChange={handleSlideChange}
58
- configWidth={300}
58
+ configWidth={280}
59
59
  mode={isReading ? "read" : mode}
60
60
  isEditable={!isReading}
61
61
  />
@@ -231,7 +231,7 @@ const RevealSlidesComponent = ({
231
231
  activeIndex,
232
232
  onSlideChange,
233
233
  mode,
234
- configWidth = 300, // px
234
+ configWidth, // px
235
235
  isEditable = false,
236
236
  }: {
237
237
  cellsWithOutput: RuntimeCell[];
@@ -240,7 +240,7 @@ const RevealSlidesComponent = ({
240
240
  activeIndex?: number;
241
241
  onSlideChange?: (index: number) => void;
242
242
  mode: AppMode;
243
- configWidth?: number;
243
+ configWidth: number;
244
244
  isEditable?: boolean;
245
245
  }) => {
246
246
  const containerRef = useRef<HTMLDivElement>(null);
@@ -439,7 +439,7 @@ const RevealSlidesComponent = ({
439
439
  <div className="group relative" style={{ width, height }}>
440
440
  <Deck
441
441
  deckRef={deckRef}
442
- className="aspect-video w-full overflow-hidden border rounded bg-background mo-slides-theme prose-slides"
442
+ className="aspect-video w-full overflow-hidden border rounded bg-background mo-slides-theme prose-slides focus:outline-none focus-visible:outline-none"
443
443
  config={revealConfig}
444
444
  onReady={handleDeckReady}
445
445
  onSlideChange={handleSlideChange}
@@ -26,15 +26,23 @@ import type {
26
26
  SlidesLayout,
27
27
  SlideType,
28
28
  } from "../editor/renderers/slides-layout/types";
29
- import { useState } from "react";
29
+ import { useAtom } from "jotai";
30
+ import { atomWithStorage } from "jotai/utils";
30
31
  import { Tooltip } from "../ui/tooltip";
31
32
  import { Button } from "../ui/button";
32
33
  import { Kbd } from "../ui/kbd";
33
34
  import type { RuntimeCell } from "@/core/cells/types";
35
+ import { jotaiJsonStorage } from "@/utils/storage/jotai";
34
36
 
35
37
  export const DEFAULT_SLIDE_TYPE: SlideType = "slide";
36
38
  export const DEFAULT_DECK_TRANSITION: DeckTransition = "slide";
37
39
  const COLLAPSED_CONFIG_WIDTH = 36;
40
+ const slideConfigOpenAtom = atomWithStorage<boolean>(
41
+ "marimo:slides:config-open",
42
+ true,
43
+ jotaiJsonStorage,
44
+ { getOnInit: true },
45
+ );
38
46
 
39
47
  export interface SlideTypeOption {
40
48
  value: SlideType;
@@ -322,7 +330,7 @@ export const SlideSidebar = ({
322
330
  setLayout: (layout: SlidesLayout) => void;
323
331
  activeConfigCell?: RuntimeCell;
324
332
  }) => {
325
- const [isConfigOpen, setIsConfigOpen] = useState(false);
333
+ const [isConfigOpen, setIsConfigOpen] = useAtom(slideConfigOpenAtom);
326
334
 
327
335
  return (
328
336
  <aside
@@ -350,7 +358,7 @@ export const SlideSidebar = ({
350
358
  variant="ghost"
351
359
  size="icon"
352
360
  className="h-7 w-7 text-muted-foreground hover:text-foreground"
353
- onClick={() => setIsConfigOpen(!isConfigOpen)}
361
+ onClick={() => setIsConfigOpen((open) => !open)}
354
362
  aria-expanded={isConfigOpen}
355
363
  aria-controls="slide-config-panel"
356
364
  >
@@ -15,10 +15,17 @@ import { maxFractionalDigits } from "@/utils/numbers";
15
15
  export interface NumberFieldProps extends AriaNumberFieldProps {
16
16
  placeholder?: string;
17
17
  variant?: "default" | "xs";
18
+ /**
19
+ * Fires on every keystroke with the raw text currently in the input.
20
+ * React-aria's `onChange` only fires on commit (blur/Enter/stepper); use this
21
+ * when callers need to peek at the in-progress value, e.g. to enable an Apply
22
+ * button while the user is still typing.
23
+ */
24
+ onInputText?: (text: string) => void;
18
25
  }
19
26
 
20
27
  export const NumberField = React.forwardRef<HTMLInputElement, NumberFieldProps>(
21
- ({ placeholder, variant = "default", ...props }, ref) => {
28
+ ({ placeholder, variant = "default", onInputText, ...props }, ref) => {
22
29
  const { locale } = useLocale();
23
30
  return (
24
31
  <AriaNumberField
@@ -48,6 +55,11 @@ export const NumberField = React.forwardRef<HTMLInputElement, NumberFieldProps>(
48
55
  e.stopPropagation();
49
56
  }
50
57
  }}
58
+ onInput={
59
+ onInputText
60
+ ? (e) => onInputText(e.currentTarget.value)
61
+ : undefined
62
+ }
51
63
  className={cn(
52
64
  "flex-1",
53
65
  "w-full",
@@ -1,46 +1,52 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
  import { beforeEach, describe, expect, it, vi } from "vitest";
3
3
 
4
- // Mock the models.json import
5
4
  vi.mock("@marimo-team/llm-info/models.json", () => {
6
- const models: AiModel[] = [
7
- {
8
- name: "GPT-4",
9
- model: "gpt-4",
10
- description: "OpenAI GPT-4 model",
11
- providers: ["openai"],
12
- roles: ["chat", "edit"],
13
- thinking: false,
14
- },
15
- {
16
- name: "Claude 3",
17
- model: "claude-3-sonnet",
18
- description: "Anthropic Claude 3 Sonnet",
19
- providers: ["anthropic"],
20
- roles: ["chat", "edit"],
21
- thinking: false,
22
- },
23
- {
24
- name: "Gemini Pro",
25
- model: "gemini-pro",
26
- description: "Google Gemini Pro model",
27
- providers: ["google"],
28
- roles: ["chat", "edit"],
29
- thinking: false,
30
- },
31
- {
32
- name: "Multi Provider Model",
33
- model: "multi-model",
34
- description: "Model available on multiple providers",
35
- providers: ["openai", "anthropic"],
36
- roles: ["chat", "edit"],
37
- thinking: false,
38
- },
39
- ];
40
-
41
- return {
42
- models: models,
5
+ const make = (
6
+ overrides: Partial<AiModel> & Pick<AiModel, "name" | "model">,
7
+ ): AiModel => ({
8
+ description: "",
9
+ roles: ["chat", "edit"],
10
+ capabilities: [],
11
+ input_types: [],
12
+ output_types: [],
13
+ release_date: "1970-01-01",
14
+ ...overrides,
15
+ });
16
+
17
+ const multiModel = make({
18
+ name: "Multi Provider Model",
19
+ model: "multi-model",
20
+ description: "Model available on multiple providers",
21
+ });
22
+
23
+ const models: Record<string, AiModel[]> = {
24
+ openai: [
25
+ make({
26
+ name: "GPT-4",
27
+ model: "gpt-4",
28
+ description: "OpenAI GPT-4 model",
29
+ }),
30
+ multiModel,
31
+ ],
32
+ anthropic: [
33
+ make({
34
+ name: "Claude 3",
35
+ model: "claude-3-sonnet",
36
+ description: "Anthropic Claude 3 Sonnet",
37
+ }),
38
+ multiModel,
39
+ ],
40
+ google: [
41
+ make({
42
+ name: "Gemini Pro",
43
+ model: "gemini-pro",
44
+ description: "Google Gemini Pro model",
45
+ }),
46
+ ],
43
47
  };
48
+
49
+ return { models };
44
50
  });
45
51
 
46
52
  import type { AiModel } from "@marimo-team/llm-info";
@@ -107,14 +113,15 @@ describe("AiModelRegistry", () => {
107
113
  });
108
114
 
109
115
  const ids = [...registry.getModelsMap().keys()];
110
- // Include custom and all default ones.
116
+ // Include custom and all default ones; iteration follows provider
117
+ // sections in the source data (openai → anthropic → google).
111
118
  expect(ids).toEqual([
112
119
  "openai/custom-gpt",
113
120
  "openai/gpt-4",
114
- "anthropic/claude-3-sonnet",
115
- "google/gemini-pro",
116
121
  "openai/multi-model",
122
+ "anthropic/claude-3-sonnet",
117
123
  "anthropic/multi-model",
124
+ "google/gemini-pro",
118
125
  ]);
119
126
  });
120
127
  });
@@ -125,9 +132,9 @@ describe("AiModelRegistry", () => {
125
132
  const openaiModels = registry.getModelsByProvider("openai");
126
133
 
127
134
  expect(openaiModels).toHaveLength(2); // gpt-4 and multi-model
128
- expect(
129
- openaiModels.every((model) => model.providers.includes("openai")),
130
- ).toBe(true);
135
+ expect(openaiModels.every((model) => model.provider === "openai")).toBe(
136
+ true,
137
+ );
131
138
  });
132
139
 
133
140
  it("should return empty array for provider with no models", () => {
@@ -147,9 +154,9 @@ describe("AiModelRegistry", () => {
147
154
  expect(customModel?.name).toBe("custom-gpt");
148
155
  expect(customModel?.model).toBe("custom-gpt");
149
156
  expect(customModel?.description).toBe("Custom model");
150
- expect(customModel?.providers).toEqual(["openai"]);
157
+ expect(customModel?.provider).toBe("openai");
151
158
  expect(customModel?.roles).toEqual([]);
152
- expect(customModel?.thinking).toBe(false);
159
+ expect(customModel?.capabilities).toEqual([]);
153
160
  });
154
161
 
155
162
  it("should filter models based on displayed models", () => {
@@ -309,7 +316,10 @@ describe("AiModelRegistry", () => {
309
316
 
310
317
  expect(multiModelInOpenai).toBeDefined();
311
318
  expect(multiModelInAnthropic).toBeDefined();
312
- expect(multiModelInOpenai).toEqual(multiModelInAnthropic);
319
+ // Same model id, but each entry belongs to its own provider.
320
+ expect(multiModelInOpenai?.provider).toBe("openai");
321
+ expect(multiModelInAnthropic?.provider).toBe("anthropic");
322
+ expect(multiModelInOpenai?.name).toBe(multiModelInAnthropic?.name);
313
323
  });
314
324
 
315
325
  it("should handle displayed models filter with non-existent models", () => {
@@ -339,20 +349,20 @@ describe("AiModelRegistry", () => {
339
349
  expect(model).toHaveProperty("name");
340
350
  expect(model).toHaveProperty("model");
341
351
  expect(model).toHaveProperty("description");
342
- expect(model).toHaveProperty("providers");
352
+ expect(model).toHaveProperty("provider");
343
353
  expect(model).toHaveProperty("roles");
344
- expect(model).toHaveProperty("thinking");
354
+ expect(model).toHaveProperty("capabilities");
345
355
  expect(model).toHaveProperty("custom");
346
356
 
347
357
  expect(typeof model.name).toBe("string");
348
358
  expect(typeof model.model).toBe("string");
349
359
  expect(typeof model.description).toBe("string");
350
- expect(Array.isArray(model.providers)).toBe(true);
360
+ expect(typeof model.provider).toBe("string");
351
361
  expect(Array.isArray(model.roles)).toBe(true);
352
- expect(typeof model.thinking).toBe("boolean");
362
+ expect(Array.isArray(model.capabilities)).toBe(true);
353
363
  expect(typeof model.custom).toBe("boolean");
354
364
 
355
- expect(model.providers).toContain(provider);
365
+ expect(model.provider).toBe(provider);
356
366
  }
357
367
  }
358
368
  });
@@ -367,31 +377,33 @@ describe("AiModelRegistry", () => {
367
377
 
368
378
  expect(customModel).toMatchInlineSnapshot(`
369
379
  {
380
+ "capabilities": [],
370
381
  "custom": true,
371
382
  "description": "Custom model",
383
+ "input_types": [],
372
384
  "model": "custom-gpt",
373
385
  "name": "custom-gpt",
374
- "providers": [
375
- "openai",
376
- ],
386
+ "output_types": [],
387
+ "provider": "openai",
388
+ "release_date": "1970-01-01",
377
389
  "roles": [],
378
- "thinking": false,
379
390
  }
380
391
  `);
381
392
  expect(defaultModel).toMatchInlineSnapshot(`
382
393
  {
394
+ "capabilities": [],
383
395
  "custom": false,
384
396
  "description": "OpenAI GPT-4 model",
397
+ "input_types": [],
385
398
  "model": "gpt-4",
386
399
  "name": "GPT-4",
387
- "providers": [
388
- "openai",
389
- ],
400
+ "output_types": [],
401
+ "provider": "openai",
402
+ "release_date": "1970-01-01",
390
403
  "roles": [
391
404
  "chat",
392
405
  "edit",
393
406
  ],
394
- "thinking": false,
395
407
  }
396
408
  `);
397
409
  });
@@ -1,12 +1,8 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import type {
4
- AiModel as AiModelType,
5
- AiProvider,
6
- Role,
7
- } from "@marimo-team/llm-info";
8
- import { models } from "@marimo-team/llm-info/models.json";
9
- import { providers } from "@marimo-team/llm-info/providers.json";
3
+ import type { AiModel as AiModelType, AiProvider } from "@marimo-team/llm-info";
4
+ import { models as modelsJson } from "@marimo-team/llm-info/models.json";
5
+ import { providers as providersJson } from "@marimo-team/llm-info/providers.json";
10
6
  import { Logger } from "@/utils/Logger";
11
7
  import { MultiMap } from "@/utils/multi-map";
12
8
  import { once } from "@/utils/once";
@@ -14,13 +10,19 @@ import type { ProviderId } from "./ids/ids";
14
10
  import { AiModelId, type QualifiedModelId, type ShortModelId } from "./ids/ids";
15
11
 
16
12
  export interface AiModel extends AiModelType {
17
- roles: Role[];
18
13
  model: ShortModelId;
19
- providers: ProviderId[];
14
+ /** The provider this entry belongs to. */
15
+ provider: ProviderId;
20
16
  /** Whether this is a custom model. */
21
17
  custom: boolean;
22
18
  }
23
19
 
20
+ // JSON shape matches the `AiModel` schema (Zod-validated at codegen time).
21
+ const models = modelsJson as unknown as Partial<
22
+ Record<ProviderId, AiModelType[]>
23
+ >;
24
+ const providers = providersJson as unknown as readonly AiProvider[];
25
+
24
26
  interface KnownModelMaps {
25
27
  /** Map of qualified model ID to model info */
26
28
  modelMap: ReadonlyMap<QualifiedModelId, AiModel>;
@@ -32,24 +34,25 @@ export const getKnownModelMaps = once((): KnownModelMaps => {
32
34
  const modelMap = new Map<QualifiedModelId, AiModel>();
33
35
  const defaultModelByProvider = new Map<ProviderId, QualifiedModelId>();
34
36
 
35
- for (const model of models) {
36
- const modelId = model.model as ShortModelId;
37
- const modelInfo: AiModel = {
38
- ...model,
39
- model: model.model as ShortModelId,
40
- roles: model.roles.map((role) => role as Role),
41
- providers: model.providers as ProviderId[],
42
- custom: false,
43
- };
44
-
45
- const supportsChatOrEdit =
46
- modelInfo.roles.includes("chat") || modelInfo.roles.includes("edit");
37
+ for (const [providerKey, providerModels] of Object.entries(models)) {
38
+ if (!providerModels) {
39
+ continue;
40
+ }
41
+ const provider = providerKey as ProviderId;
42
+ for (const raw of providerModels) {
43
+ const modelId = raw.model as ShortModelId;
44
+ const modelInfo: AiModel = {
45
+ ...raw,
46
+ model: modelId,
47
+ provider,
48
+ custom: false,
49
+ };
47
50
 
48
- for (const provider of modelInfo.providers) {
49
51
  const qualifiedModelId: QualifiedModelId = `${provider}/${modelId}`;
50
52
  modelMap.set(qualifiedModelId, modelInfo);
51
53
 
52
- // Track first model per provider that supports chat or edit
54
+ const supportsChatOrEdit =
55
+ modelInfo.roles.includes("chat") || modelInfo.roles.includes("edit");
53
56
  if (supportsChatOrEdit && !defaultModelByProvider.has(provider)) {
54
57
  defaultModelByProvider.set(provider, qualifiedModelId);
55
58
  }
@@ -67,9 +70,8 @@ const getProviderMap = once(
67
70
  const providerMap = new Map<ProviderId, AiProvider>();
68
71
  const providerToOrderIdx = new Map<ProviderId, number>();
69
72
  providers.forEach((provider, idx) => {
70
- const providerId = provider.id as ProviderId;
71
- providerMap.set(providerId, provider);
72
- providerToOrderIdx.set(providerId, idx);
73
+ providerMap.set(provider.id, provider);
74
+ providerToOrderIdx.set(provider.id, idx);
73
75
  });
74
76
  return { providerMap, providerToOrderIdx };
75
77
  },
@@ -158,9 +160,12 @@ export class AiModelRegistry {
158
160
  name: modelId.shortModelId,
159
161
  model: modelId.shortModelId,
160
162
  description: "Custom model",
161
- providers: [modelId.providerId],
163
+ provider: modelId.providerId,
162
164
  roles: [],
163
- thinking: false,
165
+ capabilities: [],
166
+ input_types: [],
167
+ output_types: [],
168
+ release_date: "1970-01-01",
164
169
  custom: true,
165
170
  };
166
171
  customModelsMap.set(model, modelInfo);
@@ -0,0 +1,48 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { MockNotebook } from "@/__mocks__/notebook";
5
+ import { scrollAndHighlightCell } from "@/components/editor/links/cell-link";
6
+ import { notebookAtom } from "@/core/cells/cells";
7
+ import type { CellId } from "@/core/cells/ids";
8
+ import { createCellRuntimeState } from "@/core/cells/types";
9
+ import { store } from "@/core/state/jotai";
10
+ import { notebookScrollToRunning } from "../actions";
11
+
12
+ vi.mock("@/components/editor/links/cell-link", () => ({
13
+ scrollAndHighlightCell: vi.fn(),
14
+ }));
15
+
16
+ describe("notebookScrollToRunning", () => {
17
+ beforeEach(() => {
18
+ vi.clearAllMocks();
19
+ store.set(notebookAtom, MockNotebook.notebookState({ cellData: {} }));
20
+ });
21
+
22
+ it("scrolls to the first running cell in notebook order", () => {
23
+ const runtimeOnlyCellId = "runtime-only" as CellId;
24
+ const idleCellId = "idle-cell" as CellId;
25
+ const runningCellId = "running-cell" as CellId;
26
+
27
+ const notebook = MockNotebook.notebookState({
28
+ cellData: {
29
+ [idleCellId]: {},
30
+ [runningCellId]: {},
31
+ },
32
+ cellRuntime: {
33
+ [idleCellId]: { status: "idle" },
34
+ [runningCellId]: { status: "running" },
35
+ },
36
+ });
37
+ notebook.cellRuntime = {
38
+ [runtimeOnlyCellId]: createCellRuntimeState({ status: "running" }),
39
+ ...notebook.cellRuntime,
40
+ };
41
+ store.set(notebookAtom, notebook);
42
+
43
+ notebookScrollToRunning();
44
+
45
+ expect(scrollAndHighlightCell).toHaveBeenCalledOnce();
46
+ expect(scrollAndHighlightCell).toHaveBeenCalledWith(runningCellId, "focus");
47
+ });
48
+ });
@@ -1,7 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import { scrollAndHighlightCell } from "@/components/editor/links/cell-link";
4
- import { Objects } from "@/utils/objects";
5
4
  import { store } from "../state/jotai";
6
5
  import { notebookAtom } from "./cells";
7
6
 
@@ -10,12 +9,12 @@ import { notebookAtom } from "./cells";
10
9
  */
11
10
  export function notebookScrollToRunning() {
12
11
  // find cell that is currently in "running" state
13
- const { cellRuntime } = store.get(notebookAtom);
14
- const cell = Objects.entries(cellRuntime).find(
15
- ([cellid, runtimestate]) => runtimestate.status === "running",
12
+ const { cellIds, cellRuntime } = store.get(notebookAtom);
13
+ const cellId = cellIds.inOrderIds.find(
14
+ (id) => cellRuntime[id]?.status === "running",
16
15
  );
17
- if (!cell) {
16
+ if (!cellId) {
18
17
  return;
19
18
  }
20
- scrollAndHighlightCell(cell[0], "focus");
19
+ scrollAndHighlightCell(cellId, "focus");
21
20
  }