@marimo-team/islands 0.22.1-dev3 → 0.22.1-dev31

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 (325) hide show
  1. package/dist/{ConnectedDataExplorerComponent-Dl1grr8z.js → ConnectedDataExplorerComponent-DTOsfq2x.js} +54 -54
  2. package/dist/_basePickBy-Sow3pJjS.js +41 -0
  3. package/dist/{_baseUniq-B4eL5sTC.js → _baseUniq-C87CckHL.js} +15 -54
  4. package/dist/{any-language-editor-CT_9yBde.js → any-language-editor-BHH_pQ6M.js} +21 -21
  5. package/dist/architecture-7HQA4BMR-BHdkAMvZ.js +6 -0
  6. package/dist/{architectureDiagram-VXUJARFQ-vxgYGIMP.js → architectureDiagram-VXUJARFQ-B3YQo9At.js} +15 -15
  7. package/dist/{arrays-Du-jRBAy.js → arrays-beUWo8RF.js} +1 -1
  8. package/dist/assets/__vite-browser-external-WSlCcXn_.js +1 -0
  9. package/dist/assets/{worker-D10K3OOz.js → worker-DUYMdbtA.js} +2 -2
  10. package/dist/{blockDiagram-VD42YOAC-C7x6YTH7.js → blockDiagram-VD42YOAC-CpQ3TKEN.js} +7 -7
  11. package/dist/{button-qsiIHncQ.js → button-DNlNlZY_.js} +82 -84
  12. package/dist/{c4Diagram-YG6GDRKO-Cx4oseGg.js → c4Diagram-YG6GDRKO-CZSU4uqU.js} +4 -4
  13. package/dist/{capabilities-26mwv03y.js → capabilities-Coe9eM9T.js} +2 -2
  14. package/dist/{channel-C_50jIAn.js → channel-X3JKk8gE.js} +1 -1
  15. package/dist/{chat-ui-CtqUthFR.js → chat-ui-eH46RYWT.js} +147 -146
  16. package/dist/{check-D_YwHEgY.js → check-CWUkiHmb.js} +1 -1
  17. package/dist/{chunk-4F5CHEZ2-Dvo_CFnR.js → chunk-4F5CHEZ2-D5mClyDv.js} +1 -1
  18. package/dist/{chunk-ABZYJK2D-D5YIs71w.js → chunk-ABZYJK2D-CZYCCtLy.js} +1 -1
  19. package/dist/{chunk-ATLVNIR6-CyOjzOcf.js → chunk-ATLVNIR6-DaOzLLgN.js} +1 -1
  20. package/dist/{chunk-B2363JML-BzZqINRO.js → chunk-B2363JML-Br0eA2T3.js} +1 -1
  21. package/dist/{chunk-B4BG7PRW-ZJeV3KdD.js → chunk-B4BG7PRW-4BjV11Br.js} +4 -4
  22. package/dist/{chunk-DI55MBZ5-Dx_wwX6l.js → chunk-DI55MBZ5-DITY3EyP.js} +4 -4
  23. package/dist/{chunk-EXTU4WIE-CgefpSXQ.js → chunk-EXTU4WIE-jUPSAk3i.js} +1 -1
  24. package/dist/{chunk-FRFDVMJY-Derq8UzY.js → chunk-FRFDVMJY-DnEvEFRR.js} +1 -1
  25. package/dist/{chunk-JA3XYJ7Z-CcIOIFpc.js → chunk-JA3XYJ7Z-BcPEfxk_.js} +2 -2
  26. package/dist/{chunk-JZLCHNYA-CgO0GG1p.js → chunk-JZLCHNYA-2bnLL3xL.js} +4 -4
  27. package/dist/{chunk-N4CR4FBY-2qzGzAxT.js → chunk-N4CR4FBY-CpZSuGSU.js} +5 -5
  28. package/dist/{chunk-PL6DKKU2-KoG71Zin.js → chunk-PL6DKKU2-DnId6G-x.js} +1 -1
  29. package/dist/{chunk-QN33PNHL-Dp1qBo28.js → chunk-QN33PNHL-B9p5ojHB.js} +1 -1
  30. package/dist/{chunk-QXUST7PY-BxmmeIwf.js → chunk-QXUST7PY-Ch6F5Obl.js} +5 -5
  31. package/dist/{chunk-S3R3BYOJ-D3Rys9ZW.js → chunk-S3R3BYOJ-B0UOFJwq.js} +3 -3
  32. package/dist/{chunk-SJTYNZTY-Co-DhKAG.js → chunk-SJTYNZTY-BsBZnJUj.js} +1 -1
  33. package/dist/{chunk-TCCFYFTB-BAhzIqBO.js → chunk-TCCFYFTB-Clbl-fTg.js} +8 -7
  34. package/dist/{chunk-TQ3KTPDO-DxYI735Z.js → chunk-TQ3KTPDO-CFkSQ30e.js} +1 -1
  35. package/dist/{chunk-TZMSLE5B-Dxumt0wv.js → chunk-TZMSLE5B-D45397J2.js} +1 -1
  36. package/dist/{chunk-UMXZTB3W-CuahpKin.js → chunk-UMXZTB3W-D-A834Bq.js} +1 -1
  37. package/dist/{classDiagram-v2-WZHVMYZB-CYoFMQKE.js → classDiagram-2ON5EDUG-C8-zE3Zv.js} +10 -10
  38. package/dist/{classDiagram-2ON5EDUG-DkOvXRlx.js → classDiagram-v2-WZHVMYZB-DrmbGANl.js} +10 -10
  39. package/dist/{clone-DDndUqI0.js → clone-DZFQCtFJ.js} +1 -1
  40. package/dist/{constants-D1Am36hX.js → constants-CvyfaCvs.js} +3 -3
  41. package/dist/{copy-CBo9JcJW.js → copy-B7781WJ3.js} +2 -2
  42. package/dist/{dagre-6UL2VRFP-BXBaU8PB.js → dagre-6UL2VRFP-OMItEBnY.js} +12 -12
  43. package/dist/{dagre-D3dlYz-r.js → dagre-QVd-lCXU.js} +13 -23
  44. package/dist/{data-grid-overlay-editor-nZux6_d2.js → data-grid-overlay-editor-lKF301ME.js} +1 -1
  45. package/dist/{diagram-PSM6KHXK-CJxjk4LG.js → diagram-PSM6KHXK-CkKbohWI.js} +16 -16
  46. package/dist/{diagram-QEK2KX5R-IMILPh_p.js → diagram-QEK2KX5R-DjUMpVcx.js} +14 -14
  47. package/dist/{diagram-S2PKOQOG-6O0g6Boj.js → diagram-S2PKOQOG-b-c0d-wZ.js} +14 -14
  48. package/dist/{dist-BkXs8bw0.js → dist--6TSlp8H.js} +1 -1
  49. package/dist/dist-7K5doRvB.js +6 -0
  50. package/dist/{dist-CQidOwep.js → dist-B43sbpd0.js} +3 -3
  51. package/dist/dist-B6I_A2-E.js +8 -0
  52. package/dist/dist-BEQsmaZY.js +5 -0
  53. package/dist/dist-BasY2RHp.js +8 -0
  54. package/dist/{dist-BemtTYzN.js → dist-Bfp1XXWt.js} +5 -5
  55. package/dist/{dist-v-1kgqZ3.js → dist-BjDuO5JW.js} +1 -1
  56. package/dist/dist-Bosc00dY.js +5 -0
  57. package/dist/{dist-DLNKBPsk.js → dist-BrxqmS9Q.js} +4 -4
  58. package/dist/{dist-DBYL08Lu.js → dist-BvhGByxL.js} +4 -4
  59. package/dist/{dist-CVqlhD3M.js → dist-C4bq5Ioy.js} +2 -2
  60. package/dist/{dist-DwmxBUOe.js → dist-CFKdzOIu.js} +2 -2
  61. package/dist/{dist-5nTQE2yt.js → dist-CIB8w0Fl.js} +2 -2
  62. package/dist/{dist-C-EcLtO9.js → dist-CNF0QBLR.js} +1 -1
  63. package/dist/dist-CQMZOn-_.js +8 -0
  64. package/dist/dist-CViQhWZ8.js +5 -0
  65. package/dist/{dist-Dg65j0em.js → dist-CcMfr7jD.js} +1 -1
  66. package/dist/{dist-C0XYIHKJ.js → dist-Ci0CXEFt.js} +1 -1
  67. package/dist/dist-Cz6rLfwY.js +5 -0
  68. package/dist/dist-D8eq8st3.js +6 -0
  69. package/dist/{dist-qoCY8giM.js → dist-DAfcmt-d.js} +2 -2
  70. package/dist/{dist-B6Op2ogv.js → dist-DD_cYHOl.js} +2 -2
  71. package/dist/{dist-BUSLKXcu.js → dist-DFK94vuS.js} +2 -2
  72. package/dist/{dist-DBXPlQ0D.js → dist-DGNtjMZu.js} +1 -1
  73. package/dist/{dist-C9qF7MRB.js → dist-DJ9F1eHs.js} +2 -2
  74. package/dist/{dist-Ci_jEudG.js → dist-DJKubHDd.js} +1 -1
  75. package/dist/{dist-CnFp2Kcl.js → dist-DLafRu9s.js} +2 -2
  76. package/dist/dist-DM1UDXdl.js +5 -0
  77. package/dist/dist-DNrtWPgS.js +5 -0
  78. package/dist/dist-D_UjpfOY.js +1381 -0
  79. package/dist/{dist-DmFS6KZW.js → dist-DbnBiLNH.js} +3 -3
  80. package/dist/{dist-BiZZAo22.js → dist-DlSUOIm9.js} +1 -1
  81. package/dist/{dist-DStU8He1.js → dist-Doy0mQDg.js} +2 -2
  82. package/dist/{dist-CxAX99oC.js → dist-DpkJHKB8.js} +2 -2
  83. package/dist/{dist-DjaZNkZ7.js → dist-LhQNUe5A.js} +3 -3
  84. package/dist/dist-V7q2qnpA.js +5 -0
  85. package/dist/{dist-BJ3fhRYu.js → dist-VqF3W_ue.js} +2 -2
  86. package/dist/dist-a5_hPgu2.js +8 -0
  87. package/dist/{dist-B9KLrfoh.js → dist-ej6AQKaS.js} +1 -1
  88. package/dist/{dist-Brb6VNc4.js → dist-m9tsXsFf.js} +2 -2
  89. package/dist/{dist-CBwMSFDu.js → dist-tGk0aZ--.js} +2 -2
  90. package/dist/dist-uVyZcV1-.js +5 -0
  91. package/dist/{erDiagram-Q2GNP2WA-sho7Cl9f.js → erDiagram-Q2GNP2WA-CDhLaOZ1.js} +10 -10
  92. package/dist/{error-banner-Bx9kIgrs.js → error-banner-Cjf0RU9I.js} +79 -79
  93. package/dist/{esm-CMg2ABu6.js → esm-4wmsH2lp.js} +6 -6
  94. package/dist/{esm-cqK9POGH.js → esm-CD1iby2n.js} +23 -23
  95. package/dist/{flowDiagram-NV44I4VS-C4nY4Fbz.js → flowDiagram-NV44I4VS-BDi4O4CL.js} +10 -10
  96. package/dist/{ganttDiagram-JELNMOA3-CtxNcCM2.js → ganttDiagram-JELNMOA3-BpZE6kVp.js} +3 -3
  97. package/dist/{gitGraph-G5XIXVHT-SL6TDof6.js → gitGraph-G5XIXVHT-B_c6xFJv.js} +3 -3
  98. package/dist/{gitGraphDiagram-V2S2FVAM-D9885mxd.js → gitGraphDiagram-V2S2FVAM-iQnXzbPM.js} +13 -13
  99. package/dist/{glide-data-editor-CkVEV-Gk.js → glide-data-editor-VgPtWvhu.js} +63 -63
  100. package/dist/{graphlib-CxWdvYQt.js → graphlib-BV1_gi0C.js} +4 -3
  101. package/dist/hasIn-DnfJcYpY.js +108 -0
  102. package/dist/{info-VBDWY6EO-6MXPTSmi.js → info-VBDWY6EO-BTyzxmhr.js} +3 -3
  103. package/dist/{infoDiagram-HS3SLOUP-Bw2FlRwF.js → infoDiagram-HS3SLOUP-OYrX6uO3.js} +13 -13
  104. package/dist/{input-BSde8uV4.js → input-CFY9gApZ.js} +5055 -5055
  105. package/dist/{isEmpty-BQtUinxJ.js → isEmpty-B7FX9wKt.js} +1 -1
  106. package/dist/{isSymbol-DFp8040B.js → isSymbol-DCbjQG_U.js} +1 -1
  107. package/dist/{journeyDiagram-XKPGCS4Q-BXlCEth8.js → journeyDiagram-XKPGCS4Q-ClPC94aN.js} +3 -3
  108. package/dist/{kanban-definition-3W4ZIXB7-CorxzSYm.js → kanban-definition-3W4ZIXB7-DHEAKdZt.js} +7 -7
  109. package/dist/{label-DTNqw9tv.js → label-DbZGAoCH.js} +538 -569
  110. package/dist/{loader-3c9hT4kT.js → loader-Bd1kgLn7.js} +19 -16
  111. package/dist/main.js +2602 -2594
  112. package/dist/{memoize-CuHciEBb.js → memoize-CSTI9eOX.js} +1 -1
  113. package/dist/{merge-CA_buyY3.js → merge-CVhG7q_o.js} +1 -1
  114. package/dist/{mermaid-CEKslOkI.js → mermaid-B2HDLx2g.js} +54 -54
  115. package/dist/{mermaid-parser.core-cq4YDee-.js → mermaid-parser.core-ntCgyx0x.js} +8 -8
  116. package/dist/min-Ds3gG0Ff.js +96 -0
  117. package/dist/{mindmap-definition-VGOIOE7T-DRsT8UaN.js → mindmap-definition-VGOIOE7T-CxEUZZvY.js} +9 -9
  118. package/dist/{now-CXAdKY5k.js → now-nrrrOr01.js} +1 -1
  119. package/dist/{once-CZno0h-b.js → once-C_TIu-kR.js} +1 -1
  120. package/dist/{packet-DYOGHKS2-Dw08gMaZ.js → packet-DYOGHKS2-BhvnpoGi.js} +3 -3
  121. package/dist/{pie-VRWISCQL-C5SPSvT8.js → pie-VRWISCQL-dILuA3iG.js} +3 -3
  122. package/dist/{pieDiagram-ADFJNKIX-DhJ1Cx2O.js → pieDiagram-ADFJNKIX-U3LrUqAS.js} +14 -14
  123. package/dist/{process-output-KyzWazB-.js → process-output-BbUNe4iH.js} +3181 -3204
  124. package/dist/{quadrantDiagram-AYHSOK5B-DXUFIWlz.js → quadrantDiagram-AYHSOK5B-BVWuq-3R.js} +2 -2
  125. package/dist/{radar-ZZBFDIW7-BvY0bgSg.js → radar-ZZBFDIW7-DwFrOJDj.js} +3 -3
  126. package/dist/range-fJeId9Ri.js +30 -0
  127. package/dist/{requirementDiagram-UZGBJVZJ-DO_gtQIb.js → requirementDiagram-UZGBJVZJ-D0zpQnKC.js} +9 -9
  128. package/dist/{sankeyDiagram-TZEHDZUN-OZzXEkuG.js → sankeyDiagram-TZEHDZUN-CExy1joT.js} +2 -2
  129. package/dist/{sequenceDiagram-WL72ISMW-K7nZRifV.js → sequenceDiagram-WL72ISMW-D1BJxLjH.js} +4 -4
  130. package/dist/{slides-component-CIcSvFh7.js → slides-component-CX2JC-Ws.js} +2 -2
  131. package/dist/{spec-DYaR1rJh.js → spec-CiHus5Bb.js} +3 -3
  132. package/dist/{stateDiagram-FKZM4ZOC-DzXJZAq7.js → stateDiagram-FKZM4ZOC-B1S8jGMn.js} +12 -12
  133. package/dist/{stateDiagram-v2-4FDKWEC3-BZBPUmyF.js → stateDiagram-v2-4FDKWEC3-BH5ozUbc.js} +10 -10
  134. package/dist/stex-CQDv3aS8.js +4 -0
  135. package/dist/style.css +1 -1
  136. package/dist/{timeline-definition-IT6M3QCI-DNoLAh-i.js → timeline-definition-IT6M3QCI-BDT9JAmn.js} +2 -2
  137. package/dist/{toDate-D6VXexnV.js → toDate-BzYZtEK7.js} +4 -4
  138. package/dist/{toNumber-xFPoy1OI.js → toNumber-55tjPCWr.js} +2 -2
  139. package/dist/tooltip-BXEpXV3R.js +404 -0
  140. package/dist/{treemap-GDKQZRPO-C5OoxpmV.js → treemap-GDKQZRPO-bx2ngsgN.js} +3 -3
  141. package/dist/{types-CQ-RbYxp.js → types-D_ntCXg0.js} +3 -3
  142. package/dist/{useAsyncData-Cd4Urlww.js → useAsyncData-rN1nzPaS.js} +2 -2
  143. package/dist/{useDeepCompareMemoize-X7clcrcQ.js → useDeepCompareMemoize-iM1YNTEF.js} +4 -4
  144. package/dist/{useIframeCapabilities-BVQrlRBd.js → useIframeCapabilities-CqhrVue6.js} +1 -1
  145. package/dist/{useLifecycle-Dids8BPm.js → useLifecycle-DgDTfOLZ.js} +9 -9
  146. package/dist/{useTheme-Dm1WaAGy.js → useTheme-MWfxn4oz.js} +4 -5
  147. package/dist/{vega-component-A6unyUJS.js → vega-component-CkpTXaRx.js} +23 -23
  148. package/dist/{xychartDiagram-PRI3JC2R-ehVeySMW.js → xychartDiagram-PRI3JC2R-CuAZiqHS.js} +5 -5
  149. package/dist/{Combination-B--d1_LV.js → zod-C6UGQ3fz.js} +8085 -8151
  150. package/package.json +8 -41
  151. package/src/__tests__/branded.ts +6 -0
  152. package/src/__tests__/main.test.tsx +12 -14
  153. package/src/components/ai/ai-provider-icon.tsx +3 -2
  154. package/src/components/app-config/user-config-form.tsx +0 -27
  155. package/src/components/chat/acp/agent-docs.tsx +3 -3
  156. package/src/components/chat/acp/agent-panel.tsx +69 -22
  157. package/src/components/chat/acp/agent-selector.tsx +2 -11
  158. package/src/components/chat/acp/state.ts +14 -2
  159. package/src/components/chat/chat-panel.tsx +2 -1
  160. package/src/components/data-table/TableBottomBar.tsx +12 -1
  161. package/src/components/data-table/TableTopBar.tsx +31 -35
  162. package/src/components/data-table/cell-selection/types.ts +3 -2
  163. package/src/components/data-table/charts/charts.tsx +42 -13
  164. package/src/components/data-table/charts/components/chart-items.tsx +1 -1
  165. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  166. package/src/components/data-table/column-formatting/types.ts +3 -2
  167. package/src/components/data-table/column-header.tsx +4 -2
  168. package/src/components/data-table/column-wrapping/types.ts +3 -2
  169. package/src/components/data-table/columns.tsx +4 -1
  170. package/src/components/data-table/copy-column/types.ts +3 -2
  171. package/src/components/data-table/data-table.tsx +6 -1
  172. package/src/components/data-table/focus-row/types.ts +3 -2
  173. package/src/components/data-table/loading-table.tsx +4 -1
  174. package/src/components/data-table/range-focus/__tests__/atoms.test.ts +11 -11
  175. package/src/components/data-table/range-focus/__tests__/use-cell-range-selection.test.ts +9 -11
  176. package/src/components/data-table/range-focus/cell-selection-stats.tsx +3 -1
  177. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +1 -1
  178. package/src/components/data-table/table-explorer-panel/table-explorer-panel.tsx +2 -2
  179. package/src/components/editor/__tests__/data-attributes.test.tsx +93 -94
  180. package/src/components/editor/actions/name-cell-input.tsx +4 -2
  181. package/src/components/editor/actions/useCellActionButton.tsx +4 -2
  182. package/src/components/editor/ai/add-cell-with-ai.tsx +2 -1
  183. package/src/components/editor/cell/CellStatus.tsx +4 -5
  184. package/src/components/editor/cell/cell-context-menu.tsx +4 -2
  185. package/src/components/editor/cell/code/cell-editor.tsx +2 -1
  186. package/src/components/editor/cell/toolbar.tsx +2 -1
  187. package/src/components/editor/chrome/components/contribute-snippet-button.tsx +4 -1
  188. package/src/components/editor/chrome/components/feedback-button.tsx +4 -1
  189. package/src/components/editor/chrome/panels/context-aware-panel/context-aware-panel.tsx +1 -1
  190. package/src/components/editor/chrome/panels/file-explorer-panel.tsx +0 -10
  191. package/src/components/editor/chrome/wrapper/app-chrome.tsx +4 -1
  192. package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +2 -1
  193. package/src/components/editor/header/filename-input.tsx +4 -1
  194. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +11 -12
  195. package/src/components/storage/__tests__/storage-snippets.test.ts +4 -6
  196. package/src/components/tracing/tracing.test.tsx +30 -30
  197. package/src/components/ui/accordion.tsx +1 -1
  198. package/src/components/ui/alert-dialog.tsx +1 -1
  199. package/src/components/ui/badge.tsx +2 -1
  200. package/src/components/ui/button.tsx +4 -3
  201. package/src/components/ui/calendar.tsx +3 -2
  202. package/src/components/ui/checkbox.tsx +1 -1
  203. package/src/components/ui/combobox.tsx +2 -1
  204. package/src/components/ui/command.tsx +4 -1
  205. package/src/components/ui/context-menu.tsx +1 -1
  206. package/src/components/ui/date-input.tsx +7 -6
  207. package/src/components/ui/date-picker.tsx +6 -4
  208. package/src/components/ui/dialog.tsx +1 -1
  209. package/src/components/ui/draggable-popover.tsx +1 -1
  210. package/src/components/ui/dropdown-menu.tsx +2 -1
  211. package/src/components/ui/field.tsx +1 -2
  212. package/src/components/ui/form.tsx +4 -5
  213. package/src/components/ui/fullscreen.tsx +4 -1
  214. package/src/components/ui/label.tsx +1 -1
  215. package/src/components/ui/navigation.tsx +1 -1
  216. package/src/components/ui/popover.tsx +1 -1
  217. package/src/components/ui/progress.tsx +4 -3
  218. package/src/components/ui/query-param-preserving-link.tsx +4 -2
  219. package/src/components/ui/radio-group.tsx +1 -1
  220. package/src/components/ui/range-slider.tsx +1 -1
  221. package/src/components/ui/scroll-area.tsx +1 -1
  222. package/src/components/ui/select.tsx +1 -1
  223. package/src/components/ui/sheet.tsx +3 -2
  224. package/src/components/ui/slider.tsx +1 -1
  225. package/src/components/ui/switch.tsx +1 -1
  226. package/src/components/ui/tabs.tsx +1 -1
  227. package/src/components/ui/textarea.tsx +1 -2
  228. package/src/components/ui/toast.tsx +1 -1
  229. package/src/components/ui/toggle.tsx +1 -1
  230. package/src/components/ui/tooltip.tsx +1 -1
  231. package/src/core/ai/context/providers/cell-output.ts +1 -2
  232. package/src/core/ai/tools/edit-notebook-tool.ts +4 -3
  233. package/src/core/ai/tools/run-cells-tool.ts +4 -3
  234. package/src/core/cells/__tests__/add-missing-import.test.ts +23 -22
  235. package/src/core/cells/__tests__/apply-transaction.test.ts +12 -11
  236. package/src/core/cells/__tests__/cell.test.ts +14 -13
  237. package/src/core/cells/document-changes.ts +9 -9
  238. package/src/core/cells/logs.ts +1 -1
  239. package/src/core/codemirror/cells/__tests__/extensions.test.ts +15 -17
  240. package/src/core/codemirror/copilot/__tests__/transport.test.ts +128 -2
  241. package/src/core/codemirror/copilot/client.ts +9 -2
  242. package/src/core/codemirror/copilot/language-server.ts +11 -0
  243. package/src/core/codemirror/copilot/transport.ts +33 -8
  244. package/src/core/codemirror/language/languages/markdown.ts +1 -3
  245. package/src/core/codemirror/language/languages/python.ts +4 -0
  246. package/src/core/codemirror/language/languages/sql/completion-sources.tsx +4 -6
  247. package/src/core/codemirror/language/languages/sql/sql.ts +1 -3
  248. package/src/core/codemirror/language/panel/sql.tsx +4 -1
  249. package/src/core/codemirror/reactive-references/__tests__/analyzer.test.ts +28 -42
  250. package/src/core/config/__tests__/config-schema.test.ts +2 -6
  251. package/src/core/config/config-schema.ts +0 -1
  252. package/src/core/config/feature-flag.tsx +0 -2
  253. package/src/core/datasets/data-source-connections.ts +4 -2
  254. package/src/core/dom/__tests__/htmlUtils.test.ts +8 -14
  255. package/src/core/dom/__tests__/outline.test.ts +2 -3
  256. package/src/core/edit-app.tsx +4 -1
  257. package/src/core/islands/__tests__/bridge.test.ts +20 -10
  258. package/src/core/islands/__tests__/parse.test.ts +8 -7
  259. package/src/core/network/__tests__/requests-lazy.test.ts +30 -14
  260. package/src/core/saving/__tests__/filename.test.ts +7 -6
  261. package/src/core/static/__tests__/download-html.test.ts +16 -15
  262. package/src/core/static/__tests__/files.test.ts +30 -28
  263. package/src/core/websocket/useMarimoKernelConnection.tsx +5 -11
  264. package/src/core/websocket/useWebSocket.tsx +3 -1
  265. package/src/css/app/Cell.css +25 -1
  266. package/src/css/globals.css +40 -14
  267. package/src/css/table.css +17 -0
  268. package/src/plugins/core/BadPlugin.tsx +7 -6
  269. package/src/plugins/impl/CheckboxPlugin.tsx +4 -1
  270. package/src/plugins/impl/DataEditorPlugin.tsx +8 -3
  271. package/src/plugins/impl/DataTablePlugin.tsx +5 -1
  272. package/src/plugins/impl/FormPlugin.tsx +1 -2
  273. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +4 -1
  274. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +43 -15
  275. package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +2 -11
  276. package/src/plugins/impl/chat/chat-ui.tsx +4 -1
  277. package/src/plugins/impl/data-frames/forms/__tests__/form.test.tsx +7 -9
  278. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +12 -68
  279. package/src/plugins/impl/plotly/__tests__/selection.test.ts +237 -0
  280. package/src/plugins/impl/plotly/selection.ts +115 -0
  281. package/src/plugins/impl/vega/__tests__/make-selectable.test.ts +13 -14
  282. package/src/plugins/impl/vega/__tests__/utils.test.ts +68 -0
  283. package/src/plugins/impl/vega/utils.ts +14 -5
  284. package/src/plugins/impl/vega/vega.css +2 -1
  285. package/src/plugins/layout/ImageComparisonPlugin.tsx +1 -3
  286. package/src/plugins/stateless-plugin.ts +4 -2
  287. package/src/utils/__tests__/cell-urls.test.ts +24 -21
  288. package/src/utils/__tests__/filenames.test.ts +15 -14
  289. package/src/utils/__tests__/json-parser.test.ts +14 -21
  290. package/src/utils/__tests__/path.test.ts +34 -31
  291. package/src/utils/__tests__/urls.test.ts +19 -18
  292. package/src/utils/json/base64.ts +2 -5
  293. package/src/utils/time.ts +4 -2
  294. package/src/utils/tracer.ts +1 -0
  295. package/src/utils/typed.ts +2 -2
  296. package/dist/_basePickBy-QjOmBDRE.js +0 -110
  297. package/dist/_baseSet-xgn1IbGV.js +0 -27
  298. package/dist/architecture-7HQA4BMR-BRyVh_Za.js +0 -6
  299. package/dist/assets/__vite-browser-external-Us1ds95c.js +0 -1
  300. package/dist/dist-B0R_ZM4-.js +0 -6
  301. package/dist/dist-B4a9_9pj.js +0 -5
  302. package/dist/dist-BCSUKEwO.js +0 -5
  303. package/dist/dist-BONIDQq6.js +0 -5
  304. package/dist/dist-BYeRx2hb.js +0 -5
  305. package/dist/dist-D2Rk1j4R.js +0 -1381
  306. package/dist/dist-DZjX5TYv.js +0 -5
  307. package/dist/dist-Dkw9x6kc.js +0 -5
  308. package/dist/dist-Ds6UaXGR.js +0 -6
  309. package/dist/dist-KuEJ1Q53.js +0 -8
  310. package/dist/dist-S72WNyTZ.js +0 -5
  311. package/dist/dist-bTG-yssT.js +0 -5
  312. package/dist/dist-diF0sguc.js +0 -8
  313. package/dist/dist-mJ84BIgu.js +0 -8
  314. package/dist/dist-wSIhFWQz.js +0 -8
  315. package/dist/get-CqrzlV1v.js +0 -68
  316. package/dist/range-CYz5jI--.js +0 -17
  317. package/dist/stex-CZyTRGVB.js +0 -4
  318. package/dist/tooltip-DGHTbHl5.js +0 -404
  319. /package/dist/{dist-KZI_BHqV.js → dist-CxZvoNao.js} +0 -0
  320. /package/dist/{invariant-D4hPsZFI.js → invariant-e8eBgdux.js} +0 -0
  321. /package/dist/{isArrayLikeObject-C-hFPChh.js → isArrayLikeObject-LXbTYiBa.js} +0 -0
  322. /package/dist/{main-CvkAPtaq.js → main-XimWhSi_.js} +0 -0
  323. /package/dist/{purify.es-ukiMXY-F.js → purify.es-hTCfRGdl.js} +0 -0
  324. /package/dist/{react-dom-BKwCWYPW.js → react-dom-BSUuJjCR.js} +0 -0
  325. /package/dist/{stex-Ze8D4R_5.js → stex-D887Ylhf.js} +0 -0
@@ -0,0 +1,237 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type * as Plotly from "plotly.js";
4
+ import { describe, expect, it } from "vitest";
5
+ import {
6
+ extractClickSelection,
7
+ extractIndices,
8
+ extractPoints,
9
+ } from "../selection";
10
+
11
+ interface PlotlyPointInput {
12
+ data: {
13
+ type: string;
14
+ hovertemplate?: string | string[];
15
+ };
16
+ [key: string]: unknown;
17
+ }
18
+
19
+ function makePoint(point: PlotlyPointInput): Plotly.PlotDatum {
20
+ return point as unknown as Plotly.PlotDatum;
21
+ }
22
+
23
+ function makeClickEvent(
24
+ points: Plotly.PlotDatum[],
25
+ ): Readonly<Plotly.PlotMouseEvent> {
26
+ return { points } as unknown as Readonly<Plotly.PlotMouseEvent>;
27
+ }
28
+
29
+ describe("extractIndices", () => {
30
+ it("prefers pointIndex and falls back to pointNumber", () => {
31
+ const points = [
32
+ makePoint({
33
+ pointIndex: 2,
34
+ pointNumber: 99,
35
+ data: { type: "scatter" },
36
+ }),
37
+ makePoint({
38
+ pointNumber: 4,
39
+ data: { type: "scattergl" },
40
+ }),
41
+ makePoint({
42
+ data: { type: "heatmap" },
43
+ }),
44
+ ];
45
+
46
+ expect(extractIndices(points)).toEqual([2, 4]);
47
+ });
48
+ });
49
+
50
+ describe("extractPoints", () => {
51
+ it("extracts parsed scatter payload fields from the hovertemplate", () => {
52
+ const points = [
53
+ makePoint({
54
+ x: 3,
55
+ y: 7,
56
+ curveNumber: 0,
57
+ pointIndex: 1,
58
+ customdata: ["B"],
59
+ data: {
60
+ type: "scatter",
61
+ hovertemplate:
62
+ "label=%{customdata[0]}<br>x=%{x}<br>y=%{y}<extra></extra>",
63
+ },
64
+ }),
65
+ ];
66
+
67
+ expect(extractPoints(points)).toEqual([
68
+ {
69
+ x: 3,
70
+ y: 7,
71
+ curveNumber: 0,
72
+ pointIndex: 1,
73
+ label: "B",
74
+ },
75
+ ]);
76
+ });
77
+
78
+ it("keeps standard heatmap keys without hovertemplate parsing", () => {
79
+ const points = [
80
+ makePoint({
81
+ x: "B",
82
+ y: "Row 2",
83
+ z: 6,
84
+ curveNumber: 0,
85
+ pointIndex: 5,
86
+ data: { type: "heatmap", hovertemplate: "ignored=%{z}" },
87
+ }),
88
+ ];
89
+
90
+ expect(extractPoints(points)).toEqual([
91
+ {
92
+ x: "B",
93
+ y: "Row 2",
94
+ z: 6,
95
+ curveNumber: 0,
96
+ pointIndex: 5,
97
+ },
98
+ ]);
99
+ });
100
+ });
101
+
102
+ describe("extractClickSelection", () => {
103
+ it("returns undefined for unsupported trace types", () => {
104
+ const event = makeClickEvent([
105
+ makePoint({
106
+ x: "A",
107
+ y: 10,
108
+ pointIndex: 0,
109
+ data: { type: "bar" },
110
+ }),
111
+ ]);
112
+
113
+ expect(extractClickSelection(event)).toBeUndefined();
114
+ });
115
+
116
+ it("returns undefined when all points are non-click-selectable trace types", () => {
117
+ // scatter and scattergl use onSelected (box/lasso) for selection, not onClick.
118
+ // Clicks on these traces fire both plotly_click and plotly_selected; the
119
+ // latter provides a range and must be the authoritative source.
120
+ const event = makeClickEvent([
121
+ makePoint({
122
+ x: "ignore",
123
+ y: 1,
124
+ pointIndex: 0,
125
+ data: { type: "bar" },
126
+ }),
127
+ makePoint({
128
+ x: 2,
129
+ y: 5,
130
+ curveNumber: 1,
131
+ pointIndex: 3,
132
+ data: { type: "scatter" },
133
+ }),
134
+ makePoint({
135
+ x: 4,
136
+ y: 12,
137
+ curveNumber: 2,
138
+ pointNumber: 5,
139
+ data: { type: "scattergl" },
140
+ }),
141
+ ]);
142
+
143
+ expect(extractClickSelection(event)).toBeUndefined();
144
+ });
145
+
146
+ it("filters unsupported points and preserves supported click payloads", () => {
147
+ // bar is unsupported; histogram is supported and its pointNumbers must be
148
+ // forwarded so the backend can recover the exact sample rows.
149
+ const event = makeClickEvent([
150
+ makePoint({
151
+ x: "ignore",
152
+ y: 1,
153
+ pointIndex: 0,
154
+ data: { type: "bar" },
155
+ }),
156
+ makePoint({
157
+ x: 8,
158
+ y: 3,
159
+ curveNumber: 1,
160
+ pointNumber: 2,
161
+ pointNumbers: [4, 5, 6],
162
+ data: { type: "histogram" },
163
+ }),
164
+ ]);
165
+
166
+ expect(extractClickSelection(event)).toEqual({
167
+ selections: [],
168
+ range: undefined,
169
+ indices: [2],
170
+ points: [
171
+ {
172
+ x: 8,
173
+ y: 3,
174
+ curveNumber: 1,
175
+ pointNumber: 2,
176
+ pointNumbers: [4, 5, 6],
177
+ },
178
+ ],
179
+ });
180
+ });
181
+
182
+ it("preserves histogram pointNumbers for backend row extraction", () => {
183
+ const event = makeClickEvent([
184
+ makePoint({
185
+ x: 8,
186
+ y: 2,
187
+ curveNumber: 0,
188
+ pointNumber: 3,
189
+ pointNumbers: [6, 7],
190
+ data: { type: "histogram" },
191
+ }),
192
+ ]);
193
+
194
+ expect(extractClickSelection(event)).toEqual({
195
+ selections: [],
196
+ range: undefined,
197
+ indices: [3],
198
+ points: [
199
+ {
200
+ x: 8,
201
+ y: 2,
202
+ curveNumber: 0,
203
+ pointNumber: 3,
204
+ pointNumbers: [6, 7],
205
+ },
206
+ ],
207
+ });
208
+ });
209
+
210
+ it("preserves standard heatmap click payloads", () => {
211
+ const event = makeClickEvent([
212
+ makePoint({
213
+ x: "C",
214
+ y: "Row 3",
215
+ z: 11,
216
+ curveNumber: 0,
217
+ pointIndex: 10,
218
+ data: { type: "heatmap" },
219
+ }),
220
+ ]);
221
+
222
+ expect(extractClickSelection(event)).toEqual({
223
+ selections: [],
224
+ range: undefined,
225
+ indices: [10],
226
+ points: [
227
+ {
228
+ x: "C",
229
+ y: "Row 3",
230
+ z: 11,
231
+ curveNumber: 0,
232
+ pointIndex: 10,
233
+ },
234
+ ],
235
+ });
236
+ });
237
+ });
@@ -0,0 +1,115 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { pick } from "lodash-es";
4
+ import type * as Plotly from "plotly.js";
5
+ import { Arrays } from "@/utils/arrays";
6
+ import { createParser, type PlotlyTemplateParser } from "./parse-from-template";
7
+
8
+ type AxisName = string;
9
+ type AxisDatum = unknown;
10
+
11
+ export interface PlotlyClickSelection {
12
+ points: Record<AxisName, AxisDatum>[] | Plotly.PlotDatum[];
13
+ indices: number[];
14
+ range: undefined;
15
+ selections: unknown[];
16
+ }
17
+
18
+ const CLICK_SELECTABLE_TRACE_TYPES = new Set(["heatmap", "histogram"]);
19
+
20
+ const STANDARD_POINT_KEYS: string[] = [
21
+ "x",
22
+ "y",
23
+ "z",
24
+ "lat",
25
+ "lon",
26
+ "curveNumber",
27
+ "pointNumber",
28
+ "pointNumbers",
29
+ "pointIndex",
30
+ ] as const;
31
+
32
+ function getPointIndex(point: Plotly.PlotDatum): number | undefined {
33
+ if (typeof point.pointIndex === "number") {
34
+ return point.pointIndex;
35
+ }
36
+
37
+ if (typeof point.pointNumber === "number") {
38
+ return point.pointNumber;
39
+ }
40
+
41
+ return undefined;
42
+ }
43
+
44
+ function isClickSelectablePoint(point: Plotly.PlotDatum): boolean {
45
+ const traceType = point.data?.type;
46
+ return typeof traceType === "string"
47
+ ? CLICK_SELECTABLE_TRACE_TYPES.has(traceType)
48
+ : false;
49
+ }
50
+
51
+ export function extractIndices(points: Plotly.PlotDatum[]): number[] {
52
+ return points.flatMap((point) => {
53
+ const index = getPointIndex(point);
54
+ return typeof index === "number" ? [index] : [];
55
+ });
56
+ }
57
+
58
+ /**
59
+ * This is a hack to extract the points with their original keys,
60
+ * instead of the ones that Plotly uses internally,
61
+ * by using the hovertemplate.
62
+ */
63
+ export function extractPoints(
64
+ points: Plotly.PlotDatum[],
65
+ ): Record<AxisName, AxisDatum>[] {
66
+ if (!points) {
67
+ return [];
68
+ }
69
+
70
+ let parser: PlotlyTemplateParser | undefined;
71
+
72
+ return points.map((point) => {
73
+ const standardPointFields = pick(point, STANDARD_POINT_KEYS);
74
+
75
+ // Get the first hovertemplate
76
+ const hovertemplate = Array.isArray(point.data.hovertemplate)
77
+ ? point.data.hovertemplate[0]
78
+ : point.data.hovertemplate;
79
+
80
+ // For chart types with standard point keys (e.g. heatmaps),
81
+ // or when there's no hovertemplate, pick keys directly from the point.
82
+ if (!hovertemplate || point.data?.type === "heatmap") {
83
+ return standardPointFields;
84
+ }
85
+
86
+ // Update or create a parser
87
+ parser = parser
88
+ ? parser.update(hovertemplate)
89
+ : createParser(hovertemplate);
90
+ return {
91
+ ...standardPointFields,
92
+ ...parser.parse(point),
93
+ };
94
+ });
95
+ }
96
+
97
+ export function extractClickSelection(
98
+ evt: Readonly<Plotly.PlotMouseEvent>,
99
+ ): PlotlyClickSelection | undefined {
100
+ if (!evt.points?.length) {
101
+ return undefined;
102
+ }
103
+
104
+ const points = evt.points.filter(isClickSelectablePoint);
105
+ if (points.length === 0) {
106
+ return undefined;
107
+ }
108
+
109
+ return {
110
+ selections: Arrays.EMPTY,
111
+ points: extractPoints(points),
112
+ indices: extractIndices(points),
113
+ range: undefined,
114
+ };
115
+ }
@@ -578,20 +578,19 @@ describe("makeSelectable", () => {
578
578
  `);
579
579
  });
580
580
 
581
- it.each([
582
- "errorbar",
583
- "errorband",
584
- "boxplot",
585
- ])("should return the same spec if mark is %s", (mark) => {
586
- const spec = {
587
- mark,
588
- } as unknown as VegaLiteSpec;
589
- const newSpec = makeSelectable(spec, {});
590
- expect(newSpec).toEqual(spec);
591
- expect(getSelectionParamNames(newSpec)).toEqual([]);
592
- expect(newSpec).toMatchSnapshot();
593
- expect(parse(newSpec)).toBeDefined();
594
- });
581
+ it.each(["errorbar", "errorband", "boxplot"])(
582
+ "should return the same spec if mark is %s",
583
+ (mark) => {
584
+ const spec = {
585
+ mark,
586
+ } as unknown as VegaLiteSpec;
587
+ const newSpec = makeSelectable(spec, {});
588
+ expect(newSpec).toEqual(spec);
589
+ expect(getSelectionParamNames(newSpec)).toEqual([]);
590
+ expect(newSpec).toMatchSnapshot();
591
+ expect(parse(newSpec)).toBeDefined();
592
+ },
593
+ );
595
594
 
596
595
  it("should add legend selection to composite charts (issue #6676)", () => {
597
596
  // Test case from https://github.com/marimo-team/marimo/issues/6676
@@ -33,4 +33,72 @@ describe("getContainerWidth", () => {
33
33
  it("should return undefined when width is explicitly undefined", () => {
34
34
  expect(getContainerWidth({ width: undefined })).toBeUndefined();
35
35
  });
36
+
37
+ it("should find width in nested facet spec", () => {
38
+ expect(
39
+ getContainerWidth({
40
+ $schema: "https://vega.github.io/schema/vega-lite/v6.json",
41
+ facet: { column: { field: "Origin", type: "nominal" } },
42
+ spec: {
43
+ mark: "point",
44
+ encoding: {},
45
+ width: "container",
46
+ },
47
+ }),
48
+ ).toBe("container");
49
+ });
50
+
51
+ it("should find width in nested repeat spec", () => {
52
+ expect(
53
+ getContainerWidth({
54
+ $schema: "https://vega.github.io/schema/vega-lite/v6.json",
55
+ repeat: { row: ["A", "B"] },
56
+ spec: {
57
+ mark: "point",
58
+ encoding: {},
59
+ width: "container",
60
+ },
61
+ }),
62
+ ).toBe("container");
63
+ });
64
+
65
+ it("should return undefined for nested spec without width", () => {
66
+ expect(
67
+ getContainerWidth({
68
+ facet: { column: { field: "Origin" } },
69
+ spec: { mark: "point", encoding: {} },
70
+ }),
71
+ ).toBeUndefined();
72
+ });
73
+
74
+ it("should return undefined for hconcat (width on sub-specs)", () => {
75
+ expect(
76
+ getContainerWidth({
77
+ hconcat: [{ width: "container" }, { width: "container" }],
78
+ }),
79
+ ).toBeUndefined();
80
+ });
81
+
82
+ it("should return undefined for vconcat (width on sub-specs)", () => {
83
+ expect(
84
+ getContainerWidth({
85
+ vconcat: [{ width: "container" }, { width: "container" }],
86
+ }),
87
+ ).toBeUndefined();
88
+ });
89
+
90
+ it("should return undefined for compiled Vega spec (width as signal)", () => {
91
+ expect(
92
+ getContainerWidth({
93
+ $schema: "https://vega.github.io/schema/vega/v6.json",
94
+ autosize: { contains: "padding", type: "fit-x" },
95
+ signals: [
96
+ {
97
+ name: "width",
98
+ init: "isFinite(containerSize()[0]) ? containerSize()[0] : 300",
99
+ },
100
+ ],
101
+ }),
102
+ ).toBeUndefined();
103
+ });
36
104
  });
@@ -6,13 +6,22 @@ import type { DataType, FieldTypes, VegaDataType } from "./vega-loader";
6
6
  export type ContainerWidth = number | "container";
7
7
 
8
8
  /**
9
- * Get the container width from a VegaLite spec.
10
- * @param spec - The VegaLite spec.
11
- * @returns The container width.
9
+ * Get the container width from a Vega-Lite spec.
10
+ *
11
+ * For unit specs, `width` is at the top level. For facet/repeat specs,
12
+ * `width` is nested inside `spec`. This does not handle hconcat/vconcat and Vega spec
13
+ * where the width may be a signal. These cases are covered by
14
+ * the CSS fallback `.vega-embed:has(> .chart-wrapper.fit-x)`.
12
15
  */
13
16
  export function getContainerWidth(spec: unknown): ContainerWidth | undefined {
14
- if (typeof spec === "object" && spec !== null && "width" in spec) {
15
- return spec.width as ContainerWidth | undefined;
17
+ if (typeof spec === "object" && spec !== null) {
18
+ if ("width" in spec) {
19
+ return spec.width as ContainerWidth | undefined;
20
+ }
21
+ // Faceted/repeated spec
22
+ if ("spec" in spec) {
23
+ return getContainerWidth(spec.spec);
24
+ }
16
25
  }
17
26
  return undefined;
18
27
  }
@@ -4,7 +4,8 @@
4
4
  max-width: 100%;
5
5
  }
6
6
 
7
- .vega-embed[data-container-width="container"] {
7
+ .vega-embed[data-container-width="container"],
8
+ .vega-embed:has(> .chart-wrapper.fit-x) {
8
9
  width: 100%;
9
10
  }
10
11
 
@@ -11,9 +11,7 @@ const LazyImageComparisonComponent = React.lazy(
11
11
  () => import("../impl/image-comparison/ImageComparisonComponent"),
12
12
  );
13
13
 
14
- export class ImageComparisonPlugin
15
- implements IStatelessPlugin<ImageComparisonData>
16
- {
14
+ export class ImageComparisonPlugin implements IStatelessPlugin<ImageComparisonData> {
17
15
  tagName = "marimo-image-comparison";
18
16
 
19
17
  validator = z.object({
@@ -20,8 +20,10 @@ export interface IStatelessPluginProps<D> {
20
20
  children?: React.ReactNode | undefined;
21
21
  }
22
22
 
23
- export interface IStatelessPlugin<D>
24
- extends Omit<IPlugin<never, D>, "render" | "functions"> {
23
+ export interface IStatelessPlugin<D> extends Omit<
24
+ IPlugin<never, D>,
25
+ "render" | "functions"
26
+ > {
25
27
  /**
26
28
  * Render the plugin.
27
29
  */
@@ -92,27 +92,30 @@ describe("cell-urls utilities", () => {
92
92
  });
93
93
 
94
94
  describe("edge case cell names with unicode and special characters", () => {
95
- it.each(
96
- EDGE_CASE_CELL_NAMES,
97
- )("should handle unicode cell names in createCellLink: %s", (cellName) => {
98
- const url = createCellLink(cellName);
99
- expect(url).toContain("scrollTo=");
100
- expect(url).toContain(encodeURIComponent(cellName));
101
- });
102
-
103
- it.each(
104
- EDGE_CASE_CELL_NAMES,
105
- )("should round-trip unicode cell names correctly: %s", (cellName) => {
106
- const url = createCellLink(cellName);
107
- const hash = url.split("#")[1];
108
- const extracted = extractCellNameFromHash(`#${hash}`);
109
- expect(extracted).toBe(cellName);
110
- });
95
+ it.each(EDGE_CASE_CELL_NAMES)(
96
+ "should handle unicode cell names in createCellLink: %s",
97
+ (cellName) => {
98
+ const url = createCellLink(cellName);
99
+ expect(url).toContain("scrollTo=");
100
+ expect(url).toContain(encodeURIComponent(cellName));
101
+ },
102
+ );
103
+
104
+ it.each(EDGE_CASE_CELL_NAMES)(
105
+ "should round-trip unicode cell names correctly: %s",
106
+ (cellName) => {
107
+ const url = createCellLink(cellName);
108
+ const hash = url.split("#")[1];
109
+ const extracted = extractCellNameFromHash(`#${hash}`);
110
+ expect(extracted).toBe(cellName);
111
+ },
112
+ );
111
113
 
112
- it.each(
113
- EDGE_CASE_CELL_NAMES,
114
- )("should allow linking to unicode cell names: %s", (cellName) => {
115
- expect(canLinkToCell(cellName)).toBe(true);
116
- });
114
+ it.each(EDGE_CASE_CELL_NAMES)(
115
+ "should allow linking to unicode cell names: %s",
116
+ (cellName) => {
117
+ expect(canLinkToCell(cellName)).toBe(true);
118
+ },
119
+ );
117
120
  });
118
121
  });
@@ -35,22 +35,23 @@ describe("Filenames", () => {
35
35
  expect(Filenames.withoutExtension("test.foo.txt")).toEqual("test.foo");
36
36
  });
37
37
 
38
- it.each(
39
- EDGE_CASE_FILENAMES,
40
- )("should handle edge case filenames: %s", (filename) => {
41
- // Test all filename operations with edge cases
42
- const withoutExt = Filenames.withoutExtension(filename);
38
+ it.each(EDGE_CASE_FILENAMES)(
39
+ "should handle edge case filenames: %s",
40
+ (filename) => {
41
+ // Test all filename operations with edge cases
42
+ const withoutExt = Filenames.withoutExtension(filename);
43
43
 
44
- expect(Filenames.toMarkdown(filename)).toEqual(`${withoutExt}.md`);
45
- expect(Filenames.toHTML(filename)).toEqual(`${withoutExt}.html`);
46
- expect(Filenames.toPNG(filename)).toEqual(`${withoutExt}.png`);
47
- expect(Filenames.toPY(filename)).toEqual(`${withoutExt}.py`);
48
- expect(Filenames.toIPYNB(filename)).toEqual(`${withoutExt}.ipynb`);
44
+ expect(Filenames.toMarkdown(filename)).toEqual(`${withoutExt}.md`);
45
+ expect(Filenames.toHTML(filename)).toEqual(`${withoutExt}.html`);
46
+ expect(Filenames.toPNG(filename)).toEqual(`${withoutExt}.png`);
47
+ expect(Filenames.toPY(filename)).toEqual(`${withoutExt}.py`);
48
+ expect(Filenames.toIPYNB(filename)).toEqual(`${withoutExt}.ipynb`);
49
49
 
50
- // Ensure operations preserve unicode and special characters in base name
51
- expect(withoutExt).not.toEqual("");
52
- expect(typeof withoutExt).toBe("string");
53
- });
50
+ // Ensure operations preserve unicode and special characters in base name
51
+ expect(withoutExt).not.toEqual("");
52
+ expect(typeof withoutExt).toBe("string");
53
+ },
54
+ );
54
55
  });
55
56
 
56
57
  describe("getImageExtension", () => {