@marimo-team/frontend 0.23.1-dev1 → 0.23.1-dev10

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 (119) hide show
  1. package/dist/assets/{CellStatus-CNNGwOIK.js → CellStatus-zTcdYfqx.js} +1 -1
  2. package/dist/assets/{ConnectedDataExplorerComponent-CfU-ThkK.js → ConnectedDataExplorerComponent-B0Mh-Jhz.js} +1 -1
  3. package/dist/assets/{ErrorBoundary-dxChUL02.js → ErrorBoundary-CxTq44MI.js} +1 -1
  4. package/dist/assets/{ImperativeModal-DoGv2BXV.js → ImperativeModal-hsPVDTG-.js} +1 -1
  5. package/dist/assets/{JsonOutput-D_ORGqdz.js → JsonOutput-BY31ccA7.js} +1 -1
  6. package/dist/assets/{LazyAnyLanguageCodeMirror-CoYqQxHb.js → LazyAnyLanguageCodeMirror-CsrwfW0n.js} +2 -2
  7. package/dist/assets/{MarimoErrorOutput-Bc9JufDr.js → MarimoErrorOutput--Yd2Aw0J.js} +1 -1
  8. package/dist/assets/{RSPContexts-LWFBF00h.js → RSPContexts-DzigvqmT.js} +1 -1
  9. package/dist/assets/{RenderHTML-0dk6-mYI.js → RenderHTML-CbuarQqA.js} +1 -1
  10. package/dist/assets/{add-cell-with-ai-3_AIzd22.js → add-cell-with-ai-_Y6SqxBB.js} +1 -1
  11. package/dist/assets/{add-connection-dialog-DGgtN73u.js → add-connection-dialog-CjvNOKgb.js} +1 -1
  12. package/dist/assets/{agent-panel-D7gqlew5.js → agent-panel-C24uwabG.js} +1 -1
  13. package/dist/assets/{ai-model-dropdown-DWOGmhDj.js → ai-model-dropdown-Dyxi3_nW.js} +1 -1
  14. package/dist/assets/{alert-dialog-CXspBRlP.js → alert-dialog-C2mTH3GM.js} +1 -1
  15. package/dist/assets/{any-language-editor-DYgTL8eG.js → any-language-editor-DkEDDsUJ.js} +1 -1
  16. package/dist/assets/{app-config-button-BxCSZCVS.js → app-config-button-BT2Do4RJ.js} +1 -1
  17. package/dist/assets/button-COIw2x9i.js +1 -0
  18. package/dist/assets/{cache-panel-8E_Y5OSb.js → cache-panel-C3V9UubH.js} +1 -1
  19. package/dist/assets/{cell-editor-B2IIBFCB.js → cell-editor-zW0u82sK.js} +1 -1
  20. package/dist/assets/{cell-link-CcAqXeeg.js → cell-link-CRkrHl-y.js} +1 -1
  21. package/dist/assets/{cells-EJo3u4za.js → cells-BqYYXi6G.js} +69 -69
  22. package/dist/assets/{chat-display-DFUo2Riv.js → chat-display-DsHMZa9F.js} +1 -1
  23. package/dist/assets/{chat-panel-Dl4jq1Dp.js → chat-panel-o9D3upnX.js} +1 -1
  24. package/dist/assets/{chat-ui-CysJeVE6.js → chat-ui-BYS03y86.js} +1 -1
  25. package/dist/assets/{column-preview-DQBtRWJG.js → column-preview-Dwv5a_zE.js} +1 -1
  26. package/dist/assets/{command-DvF_4mQa.js → command-DnzBp4l4.js} +1 -1
  27. package/dist/assets/{command-palette-BCrWwbIt.js → command-palette-BYbKGSF3.js} +1 -1
  28. package/dist/assets/{common-Bty2yo-n.js → common-DeoGL9rK.js} +1 -1
  29. package/dist/assets/{components-Dh-L-jYg.js → components-CDgxb-5o.js} +1 -1
  30. package/dist/assets/{components-B8TZ_vT_.js → components-DKHyHZBv.js} +1 -1
  31. package/dist/assets/{copy-icon-BGs1Pbai.js → copy-icon-Ci08KCdY.js} +1 -1
  32. package/dist/assets/{datasource-DY0N42ZB.js → datasource-COFRe84u.js} +1 -1
  33. package/dist/assets/{dependency-graph-panel-C23HsAdh.js → dependency-graph-panel-BXSe6z1R.js} +1 -1
  34. package/dist/assets/{dialog-BYjetQgE.js → dialog-H-hXtEOq.js} +1 -1
  35. package/dist/assets/{documentation-panel-okcEKCQM.js → documentation-panel-CA2pWMgB.js} +1 -1
  36. package/dist/assets/{download-TSo32ofd.js → download-5XbM3TL_.js} +1 -1
  37. package/dist/assets/edit-page-CMUN3ESy.js +9 -0
  38. package/dist/assets/{error-banner-bXc_9BBZ.js → error-banner-frvr6JXK.js} +1 -1
  39. package/dist/assets/{error-panel-aq2j0jIa.js → error-panel-CbqfK1HJ.js} +1 -1
  40. package/dist/assets/{field-DTzXkFLZ.js → field-DaQqUJ5I.js} +1 -1
  41. package/dist/assets/{file-explorer-panel-bcSqGkbZ.js → file-explorer-panel-CbS8z-JR.js} +1 -1
  42. package/dist/assets/{file-icons-DBaXCICA.js → file-icons-Bj5YoM7H.js} +1 -1
  43. package/dist/assets/{floating-outline-BTmyhMGv.js → floating-outline-XObNWtN8.js} +1 -1
  44. package/dist/assets/{focus-DXeddo75.js → focus-DzMo6UAI.js} +1 -1
  45. package/dist/assets/{form-30oC5z9y.js → form-DLyXacSF.js} +1 -1
  46. package/dist/assets/{gallery-page-XSrY7bw_.js → gallery-page-BDI9wEUZ.js} +1 -1
  47. package/dist/assets/{glide-data-editor-Bd4FOxvW.js → glide-data-editor-QI6B6uKw.js} +1 -1
  48. package/dist/assets/{globals-DQM2RvzM.js → globals-Bu6OEURn.js} +1 -1
  49. package/dist/assets/{home-page-CruHjoHv.js → home-page-BUdd5uTz.js} +1 -1
  50. package/dist/assets/{hooks-DLUrd-jH.js → hooks-kZJc1iBf.js} +1 -1
  51. package/dist/assets/{html-to-image-BJiJlwQY.js → html-to-image-DGqJ93hW.js} +1 -1
  52. package/dist/assets/index-CKRn_SiB.css +2 -0
  53. package/dist/assets/index-CKYJeIAb.js +42 -0
  54. package/dist/assets/{input-Bg12i6qY.js → input-DNCT6U6R.js} +1 -1
  55. package/dist/assets/{kiosk-mode-JCcLyeoQ.js → kiosk-mode-DYHoqMaZ.js} +1 -1
  56. package/dist/assets/layout-tmN-U1zs.js +9 -0
  57. package/dist/assets/{logs-panel-BzhPrie8.js → logs-panel-CRW4c2IL.js} +1 -1
  58. package/dist/assets/{markdown-renderer-B9RsGqHb.js → markdown-renderer-DNANigO8.js} +1 -1
  59. package/dist/assets/{mermaid-BJFSZcG6.js → mermaid-BPufPrIN.js} +1 -1
  60. package/dist/assets/{name-cell-input-CYsY4A1G.js → name-cell-input-3iKP6YTw.js} +1 -1
  61. package/dist/assets/{outline-panel-BCAWCKi6.js → outline-panel-VIqWcHj6.js} +1 -1
  62. package/dist/assets/{packages-panel-DxS7zji3.js → packages-panel-D_z4ylBE.js} +1 -1
  63. package/dist/assets/panels-CLfdzLPR.js +1 -0
  64. package/dist/assets/{process-output-DqiZsqG9.js → process-output-Q6wVr7a-.js} +1 -1
  65. package/dist/assets/{readonly-python-code-D8ITm60r.js → readonly-python-code-CI_b818F.js} +1 -1
  66. package/dist/assets/{run-page-CI2eOA-G.js → run-page-DPuH6QY4.js} +1 -1
  67. package/dist/assets/{scratchpad-panel-Dwp8-2S1.js → scratchpad-panel-BsMm0GQP.js} +1 -1
  68. package/dist/assets/{secrets-panel-C6X5jB8Q.js → secrets-panel-DWMqzwkS.js} +1 -1
  69. package/dist/assets/{select--zcABebs.js → select-BwwUWhww.js} +1 -1
  70. package/dist/assets/{session-panel-ryqVmqVd.js → session-panel-CTDzGShO.js} +1 -1
  71. package/dist/assets/{slides-component-DXMG6OXG.js → slides-component-ncUJNz7U.js} +1 -1
  72. package/dist/assets/{snippets-panel--mh2FUXA.js → snippets-panel-CWof0wHk.js} +1 -1
  73. package/dist/assets/{state-CMxx6hcP.js → state-BvnlMKdT.js} +1 -1
  74. package/dist/assets/{state-BDrig0S2.js → state-DPomuurt.js} +1 -1
  75. package/dist/assets/{switch-C6xjg01T.js → switch-YkPg_CVc.js} +1 -1
  76. package/dist/assets/{textarea-Cfp3upzK.js → textarea-CS2o3y4W.js} +1 -1
  77. package/dist/assets/{tracing-BExYhl1z.js → tracing-CPDDwzIA.js} +1 -1
  78. package/dist/assets/{tracing-panel-Co5DeX-F.js → tracing-panel-Ku1LapXJ.js} +2 -2
  79. package/dist/assets/{useAddCell-BaTlDxTu.js → useAddCell-B6yUY_RG.js} +1 -1
  80. package/dist/assets/{useBoolean-BvsK1Xcs.js → useBoolean-Dk1Mb_so.js} +1 -1
  81. package/dist/assets/{useCellActionButton-DftkIqUl.js → useCellActionButton-SxeK4dmW.js} +1 -1
  82. package/dist/assets/{useDeleteCell-d6yWnL3H.js → useDeleteCell-DHUjJQJx.js} +1 -1
  83. package/dist/assets/{useDependencyPanelTab-BaVcOBM4.js → useDependencyPanelTab-CflgayoH.js} +1 -1
  84. package/dist/assets/{useNotebookActions-D8Dm93y8.js → useNotebookActions-DHBEqrc_.js} +1 -1
  85. package/dist/assets/{useRunCells-d2edY6Tu.js → useRunCells-DFYAOTWd.js} +1 -1
  86. package/dist/assets/{useSplitCell-DOiFyMgH.js → useSplitCell-Bh-NZsBl.js} +1 -1
  87. package/dist/assets/{vega-component-CiVPyAwP.js → vega-component-DtSTT5wO.js} +1 -1
  88. package/dist/assets/{write-secret-modal-CHfFN0H8.js → write-secret-modal-CInxHVWJ.js} +1 -1
  89. package/dist/index.html +39 -39
  90. package/package.json +1 -1
  91. package/src/components/editor/renderers/slides-layout/slides-layout.tsx +50 -44
  92. package/src/components/slides/__tests__/minimap.test.ts +402 -0
  93. package/src/components/slides/minimap.tsx +534 -0
  94. package/src/components/slides/slide.tsx +29 -0
  95. package/src/components/slides/slides-component.tsx +16 -1
  96. package/src/components/ui/button.tsx +1 -2
  97. package/src/core/cells/__tests__/cells.test.ts +105 -1
  98. package/src/core/cells/cells.ts +43 -0
  99. package/src/core/cells/document-changes.ts +2 -1
  100. package/src/plugins/core/registerReactComponent.tsx +11 -8
  101. package/src/plugins/impl/ButtonPlugin.tsx +4 -6
  102. package/src/plugins/impl/CodeEditorPlugin.tsx +15 -18
  103. package/src/plugins/impl/DataEditorPlugin.tsx +8 -14
  104. package/src/plugins/impl/DataTablePlugin.tsx +1 -6
  105. package/src/plugins/impl/FileUploadPlugin.tsx +39 -43
  106. package/src/plugins/impl/FormPlugin.tsx +2 -6
  107. package/src/plugins/impl/chat/ChatPlugin.tsx +17 -20
  108. package/src/plugins/impl/data-explorer/DataExplorerPlugin.tsx +5 -8
  109. package/src/plugins/impl/matplotlib/matplotlib-renderer.ts +38 -14
  110. package/src/plugins/impl/vega/VegaPlugin.tsx +5 -8
  111. package/src/plugins/layout/NavigationMenuPlugin.tsx +2 -6
  112. package/src/utils/__tests__/events.test.ts +223 -0
  113. package/src/utils/events.ts +28 -14
  114. package/dist/assets/button-BKVLeSTX.js +0 -1
  115. package/dist/assets/edit-page-fl9KJOcI.js +0 -13
  116. package/dist/assets/index-DTBKKey_.js +0 -42
  117. package/dist/assets/index-qO0a4zuT.css +0 -2
  118. package/dist/assets/layout-cq-nxDfO.js +0 -5
  119. package/dist/assets/panels-B5BFPT3k.js +0 -1
@@ -22,7 +22,11 @@ import { foldAllBulk, unfoldAllBulk } from "@/core/codemirror/editing/commands";
22
22
  import { adaptiveLanguageConfiguration } from "@/core/codemirror/language/extension";
23
23
  import { OverridingHotkeyProvider } from "@/core/hotkeys/hotkeys";
24
24
  import type { OutputMessage } from "@/core/kernel/messages";
25
- import { type CollapsibleTree, MultiColumn } from "@/utils/id-tree";
25
+ import {
26
+ type CollapsibleTree,
27
+ MultiColumn,
28
+ type CellColumnId,
29
+ } from "@/utils/id-tree";
26
30
  import type { Seconds } from "@/utils/time";
27
31
  import {
28
32
  exportedForTesting,
@@ -495,6 +499,106 @@ describe("cell reducer", () => {
495
499
  `);
496
500
  });
497
501
 
502
+ it("can move a cell to an exact index within a column", () => {
503
+ actions.createNewCell({
504
+ cellId: firstCellId,
505
+ before: false,
506
+ });
507
+ actions.createNewCell({
508
+ cellId: cellId("1"),
509
+ before: false,
510
+ });
511
+
512
+ const columnId = state.cellIds.atOrThrow(0).id;
513
+ actions.moveCellToIndex({
514
+ cellId: firstCellId,
515
+ columnId,
516
+ index: 3,
517
+ });
518
+
519
+ expect(formatCells(state)).toMatchInlineSnapshot(`
520
+ "
521
+ [1] ''
522
+
523
+ [2] ''
524
+
525
+ [0] ''
526
+ "
527
+ `);
528
+ });
529
+
530
+ it("can move a cell to an exact index across columns", () => {
531
+ actions.createNewCell({
532
+ cellId: firstCellId,
533
+ before: false,
534
+ });
535
+ actions.createNewCell({
536
+ cellId: cellId("1"),
537
+ before: false,
538
+ });
539
+ actions.addColumnBreakpoint({ cellId: cellId("1") });
540
+
541
+ const secondColumnId = state.cellIds.atOrThrow(1).id;
542
+ actions.moveCellToIndex({
543
+ cellId: firstCellId,
544
+ columnId: secondColumnId,
545
+ index: 1,
546
+ });
547
+
548
+ expect(formatCells(state)).toMatchInlineSnapshot(`
549
+ "
550
+ > col 0
551
+
552
+
553
+ > col 1
554
+ [1] ''
555
+
556
+ [0] ''
557
+
558
+ [2] ''
559
+ "
560
+ `);
561
+ });
562
+
563
+ it("moveCellToIndex is a no-op when moving to the same position", () => {
564
+ actions.createNewCell({
565
+ cellId: firstCellId,
566
+ before: false,
567
+ });
568
+ actions.createNewCell({
569
+ cellId: cellId("1"),
570
+ before: false,
571
+ });
572
+
573
+ const columnId = state.cellIds.atOrThrow(0).id;
574
+ const before = formatCells(state);
575
+
576
+ actions.moveCellToIndex({
577
+ cellId: firstCellId,
578
+ columnId,
579
+ index: 0,
580
+ });
581
+
582
+ expect(formatCells(state)).toBe(before);
583
+ });
584
+
585
+ it("moveCellToIndex is a no-op for an invalid columnId", () => {
586
+ actions.createNewCell({
587
+ cellId: firstCellId,
588
+ before: false,
589
+ });
590
+
591
+ const before = formatCells(state);
592
+
593
+ actions.moveCellToIndex({
594
+ cellId: firstCellId,
595
+ columnId: "nonexistent-column" as CellColumnId,
596
+ index: 0,
597
+ });
598
+
599
+ expect(formatCells(state)).toBe(before);
600
+ });
601
+
498
602
  it("can run cell and receive cell messages", () => {
499
603
  // HAPPY PATH
500
604
  /////////////////
@@ -338,6 +338,49 @@ const {
338
338
  scrollKey: cellId,
339
339
  };
340
340
  },
341
+ moveCellToIndex: (
342
+ state,
343
+ action: {
344
+ cellId: CellId;
345
+ columnId: CellColumnId;
346
+ index: number;
347
+ },
348
+ ) => {
349
+ const { cellId, columnId, index } = action;
350
+ const fromColumn = state.cellIds.findWithId(cellId);
351
+ const fromIndex = fromColumn.indexOfOrThrow(cellId);
352
+
353
+ const destinationColumn = state.cellIds.get(columnId);
354
+ if (!destinationColumn) {
355
+ return state;
356
+ }
357
+
358
+ const clampedIndex = Math.max(0, Math.min(index, destinationColumn.length));
359
+ const adjustedIndex =
360
+ fromColumn.id === columnId && fromIndex < clampedIndex
361
+ ? clampedIndex - 1
362
+ : clampedIndex;
363
+
364
+ if (fromColumn.id === columnId && fromIndex === adjustedIndex) {
365
+ return state;
366
+ }
367
+
368
+ const withoutCell = state.cellIds.deleteById(cellId);
369
+ const updatedColumn = withoutCell.get(columnId);
370
+ if (!updatedColumn) {
371
+ return state;
372
+ }
373
+
374
+ return {
375
+ ...state,
376
+ cellIds: withoutCell.insertId(
377
+ cellId,
378
+ columnId,
379
+ Math.max(0, Math.min(adjustedIndex, updatedColumn.length)),
380
+ ),
381
+ scrollKey: null,
382
+ };
383
+ },
341
384
  dropCellOverCell: (state, action: { cellId: CellId; overCellId: CellId }) => {
342
385
  const { cellId, overCellId } = action;
343
386
 
@@ -215,12 +215,13 @@ export function toDocumentChanges(
215
215
  ];
216
216
  }
217
217
 
218
- // dropCellOverCell/dropCellOverColumn → set-config + reorder-cells
218
+ // dropCellOverCell/dropCellOverColumn/moveCellToIndex → set-config + reorder-cells
219
219
  // Drag-and-drop reorders can move cells within or across columns.
220
220
  // We emit config changes for cells whose column changed, then
221
221
  // the full ordering.
222
222
  case "dropCellOverCell":
223
223
  case "dropCellOverColumn":
224
+ case "moveCellToIndex":
224
225
  return columnChanges(prevState, newState);
225
226
 
226
227
  // updateCellCode → set-code
@@ -23,6 +23,7 @@ import React, {
23
23
  import ReactDOM, { type Root } from "react-dom/client";
24
24
  import useEvent from "react-use-event-hook";
25
25
  import { type ZodSchema, z } from "zod";
26
+ import { TooltipProvider } from "@/components/ui/tooltip";
26
27
  import { notebookAtom } from "@/core/cells/cells.ts";
27
28
  import { HTMLCellId } from "@/core/cells/ids.ts";
28
29
  import { isUninstantiated } from "@/core/cells/utils";
@@ -250,14 +251,16 @@ function PluginSlotInternal<T>(
250
251
  <StyleNamespace>
251
252
  <div className={`contents ${theme}`}>
252
253
  <Suspense fallback={<div />}>
253
- {plugin.render({
254
- setValue: setValueAndSendInput,
255
- value,
256
- data: parsedResult.data,
257
- children: childNodes,
258
- host: hostElement,
259
- functions: functionMethods,
260
- })}
254
+ <TooltipProvider>
255
+ {plugin.render({
256
+ setValue: setValueAndSendInput,
257
+ value,
258
+ data: parsedResult.data,
259
+ children: childNodes,
260
+ host: hostElement,
261
+ functions: functionMethods,
262
+ })}
263
+ </TooltipProvider>
261
264
  </Suspense>
262
265
  </div>
263
266
  </StyleNamespace>
@@ -3,7 +3,7 @@
3
3
  import type { JSX } from "react";
4
4
  import { z } from "zod";
5
5
  import { KeyboardHotkeys } from "@/components/shortcuts/renderShortcut";
6
- import { Tooltip, TooltipProvider } from "@/components/ui/tooltip";
6
+ import { Tooltip } from "@/components/ui/tooltip";
7
7
  import { cn } from "@/utils/cn";
8
8
  import { Button } from "../../components/ui/button";
9
9
  import { renderHTML } from "../core/RenderHTML";
@@ -69,11 +69,9 @@ export class ButtonPlugin implements IPlugin<number, Data> {
69
69
 
70
70
  if (tooltipContent) {
71
71
  return (
72
- <TooltipProvider>
73
- <Tooltip content={tooltipContent} delayDuration={200}>
74
- {button}
75
- </Tooltip>
76
- </TooltipProvider>
72
+ <Tooltip content={tooltipContent} delayDuration={200}>
73
+ {button}
74
+ </Tooltip>
77
75
  );
78
76
  }
79
77
 
@@ -4,7 +4,6 @@ import { EditorView } from "@codemirror/view";
4
4
  import { type JSX, useEffect, useMemo, useState } from "react";
5
5
  import useEvent from "react-use-event-hook";
6
6
  import { z } from "zod";
7
- import { TooltipProvider } from "@/components/ui/tooltip";
8
7
  import { useDebounceControlledState } from "@/hooks/useDebounce";
9
8
  import { type Theme, useTheme } from "@/theme/useTheme";
10
9
  import type { IPlugin, IPluginProps, Setter } from "../types";
@@ -98,22 +97,20 @@ const CodeEditorComponent = (props: CodeEditorComponentProps) => {
98
97
  }, [props.debounce, onBlur]);
99
98
 
100
99
  return (
101
- <TooltipProvider>
102
- <Labeled label={props.label} align="top" fullWidth={true}>
103
- <LazyAnyLanguageCodeMirror
104
- className={`cm *:outline-hidden border rounded overflow-hidden ${finalTheme}`}
105
- theme={finalTheme === "dark" ? "dark" : "light"}
106
- minHeight={minHeight}
107
- maxHeight={maxHeight}
108
- placeholder={props.placeholder}
109
- editable={!props.disabled}
110
- value={localValue}
111
- language={props.language}
112
- onChange={handleChange}
113
- showCopyButton={props.showCopyButton}
114
- extensions={extensions}
115
- />
116
- </Labeled>
117
- </TooltipProvider>
100
+ <Labeled label={props.label} align="top" fullWidth={true}>
101
+ <LazyAnyLanguageCodeMirror
102
+ className={`cm *:outline-hidden border rounded overflow-hidden ${finalTheme}`}
103
+ theme={finalTheme === "dark" ? "dark" : "light"}
104
+ minHeight={minHeight}
105
+ maxHeight={maxHeight}
106
+ placeholder={props.placeholder}
107
+ editable={!props.disabled}
108
+ value={localValue}
109
+ language={props.language}
110
+ onChange={handleChange}
111
+ showCopyButton={props.showCopyButton}
112
+ extensions={extensions}
113
+ />
114
+ </Labeled>
118
115
  );
119
116
  };
@@ -1,10 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import glideCss from "@glideapps/glide-data-grid/dist/index.css?inline";
4
- import { Tooltip } from "radix-ui";
5
-
6
- const TooltipProvider = Tooltip.Provider;
7
-
8
4
  import React, { useState } from "react";
9
5
  import { z } from "zod";
10
6
  import { inferFieldTypes } from "@/components/data-table/columns";
@@ -63,16 +59,14 @@ export const DataEditorPlugin = createPlugin<Edits>("marimo-data-editor", {
63
59
  .withFunctions({})
64
60
  .renderer((props) => {
65
61
  return (
66
- <TooltipProvider>
67
- <LoadingDataEditor
68
- data={props.data.data}
69
- fieldTypes={props.data.fieldTypes}
70
- edits={props.value}
71
- onEdits={props.setValue}
72
- host={props.host}
73
- editableColumns={props.data.editableColumns}
74
- />
75
- </TooltipProvider>
62
+ <LoadingDataEditor
63
+ data={props.data.data}
64
+ fieldTypes={props.data.fieldTypes}
65
+ edits={props.value}
66
+ onEdits={props.setValue}
67
+ host={props.host}
68
+ editableColumns={props.data.editableColumns}
69
+ />
76
70
  );
77
71
  });
78
72
 
@@ -1,9 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import { Provider as SlotzProvider } from "@marimo-team/react-slotz";
4
- import { Tooltip } from "radix-ui";
5
-
6
- const TooltipProvider = Tooltip.Provider;
7
4
 
8
5
  import type {
9
6
  ColumnFiltersState,
@@ -1123,9 +1120,7 @@ export const TableProviders: React.FC<{ children: React.ReactNode }> = ({
1123
1120
  return (
1124
1121
  <ErrorBoundary>
1125
1122
  <Provider store={store}>
1126
- <SlotzProvider controller={slotsController}>
1127
- <TooltipProvider>{children}</TooltipProvider>
1128
- </SlotzProvider>
1123
+ <SlotzProvider controller={slotsController}>{children}</SlotzProvider>
1129
1124
  </Provider>
1130
1125
  </ErrorBoundary>
1131
1126
  );
@@ -4,7 +4,7 @@ import { MousePointerSquareDashedIcon, Upload } from "lucide-react";
4
4
  import type { JSX } from "react";
5
5
  import { useDropzone } from "react-dropzone";
6
6
  import { z } from "zod";
7
- import { Tooltip, TooltipProvider } from "@/components/ui/tooltip";
7
+ import { Tooltip } from "@/components/ui/tooltip";
8
8
  import { toast } from "@/components/ui/use-toast";
9
9
  import { cn } from "@/utils/cn";
10
10
  import { Logger } from "@/utils/Logger";
@@ -184,42 +184,40 @@ export const FileUpload = (props: FileUploadProps): JSX.Element => {
184
184
  // link button to the hidden input element
185
185
  const label = props.label ?? "Upload";
186
186
  return (
187
- <TooltipProvider>
188
- <div className="flex flex-row items-center justify-start gap-2">
189
- <button
190
- data-testid="marimo-plugin-file-upload-button"
191
- {...getRootProps({})}
192
- className={buttonVariants({
193
- variant: "secondary",
194
- size: "xs",
195
- })}
196
- >
197
- {renderHTML({ html: label })}
198
- <Upload size={14} className="ml-2" />
199
- </button>
200
- <input {...getInputProps({})} type="file" />
201
- {uploaded ? (
202
- <>
203
- <Tooltip content={uploadedFiles}>
204
- <span className="text-xs text-muted-foreground">
205
- Uploaded{" "}
206
- <span className="underline cursor-pointer">
207
- {value.length} {value.length === 1 ? "file" : "files"}.
208
- </span>
187
+ <div className="flex flex-row items-center justify-start gap-2">
188
+ <button
189
+ data-testid="marimo-plugin-file-upload-button"
190
+ {...getRootProps({})}
191
+ className={buttonVariants({
192
+ variant: "secondary",
193
+ size: "xs",
194
+ })}
195
+ >
196
+ {renderHTML({ html: label })}
197
+ <Upload size={14} className="ml-2" />
198
+ </button>
199
+ <input {...getInputProps({})} type="file" />
200
+ {uploaded ? (
201
+ <>
202
+ <Tooltip content={uploadedFiles}>
203
+ <span className="text-xs text-muted-foreground">
204
+ Uploaded{" "}
205
+ <span className="underline cursor-pointer">
206
+ {value.length} {value.length === 1 ? "file" : "files"}.
209
207
  </span>
210
- </Tooltip>
208
+ </span>
209
+ </Tooltip>
211
210
 
212
- <button
213
- className={cn("text-xs text-destructive hover:underline")}
214
- onClick={() => setValue([])}
215
- type="button"
216
- >
217
- Click to clear files.
218
- </button>
219
- </>
220
- ) : null}
221
- </div>
222
- </TooltipProvider>
211
+ <button
212
+ className={cn("text-xs text-destructive hover:underline")}
213
+ onClick={() => setValue([])}
214
+ type="button"
215
+ >
216
+ Click to clear files.
217
+ </button>
218
+ </>
219
+ ) : null}
220
+ </div>
223
221
  );
224
222
  }
225
223
 
@@ -274,14 +272,12 @@ export const FileUpload = (props: FileUploadProps): JSX.Element => {
274
272
  {uploaded ? (
275
273
  <div className="flex flex-row gap-1">
276
274
  <div className="text-xs text-muted-foreground">
277
- <TooltipProvider>
278
- Uploaded{" "}
279
- <Tooltip content={uploadedFiles}>
280
- <span className="underline cursor-pointer">
281
- {value.length} {value.length === 1 ? "file" : "files"}.
282
- </span>
283
- </Tooltip>
284
- </TooltipProvider>
275
+ Uploaded{" "}
276
+ <Tooltip content={uploadedFiles}>
277
+ <span className="underline cursor-pointer">
278
+ {value.length} {value.length === 1 ? "file" : "files"}.
279
+ </span>
280
+ </Tooltip>
285
281
  </div>
286
282
  <span className="text-xs text-destructive hover:underline hover:cursor-pointer">
287
283
  <button
@@ -3,7 +3,7 @@
3
3
  import { Loader2Icon } from "lucide-react";
4
4
  import { type JSX, useEffect, useRef, useState } from "react";
5
5
  import { z } from "zod";
6
- import { Tooltip, TooltipProvider } from "@/components/ui/tooltip";
6
+ import { Tooltip } from "@/components/ui/tooltip";
7
7
  import type { UIElementId } from "@/core/cells/ids";
8
8
  import {
9
9
  MarimoValueInputEvent,
@@ -72,11 +72,7 @@ export const FormPlugin = createPlugin("marimo-form")
72
72
  .output(z.string().nullish()),
73
73
  })
74
74
  .renderer(({ data, functions, ...rest }) => {
75
- return (
76
- <TooltipProvider>
77
- <Form {...data} {...rest} {...functions} />
78
- </TooltipProvider>
79
- );
75
+ return <Form {...data} {...rest} {...functions} />;
80
76
  });
81
77
 
82
78
  export interface FormWrapperProps<T>
@@ -3,7 +3,6 @@
3
3
  import type { UIMessage } from "ai";
4
4
  import React, { Suspense } from "react";
5
5
  import { z } from "zod";
6
- import { TooltipProvider } from "@/components/ui/tooltip";
7
6
  import { createPlugin } from "@/plugins/core/builder";
8
7
  import { rpc } from "@/plugins/core/rpc";
9
8
  import { Arrays } from "@/utils/arrays";
@@ -73,23 +72,21 @@ export const ChatPlugin = createPlugin<{ messages: UIMessage[] }>(
73
72
  .output(z.unknown()),
74
73
  })
75
74
  .renderer((props) => (
76
- <TooltipProvider>
77
- <Suspense>
78
- <LazyChatbot
79
- prompts={props.data.prompts}
80
- showConfigurationControls={props.data.showConfigurationControls}
81
- maxHeight={props.data.maxHeight}
82
- allowAttachments={props.data.allowAttachments}
83
- disabled={props.data.disabled}
84
- config={props.data.config}
85
- get_chat_history={props.functions.get_chat_history}
86
- delete_chat_history={props.functions.delete_chat_history}
87
- delete_chat_message={props.functions.delete_chat_message}
88
- send_prompt={props.functions.send_prompt}
89
- value={props.value?.messages || Arrays.EMPTY}
90
- setValue={(messages) => props.setValue({ messages })}
91
- host={props.host}
92
- />
93
- </Suspense>
94
- </TooltipProvider>
75
+ <Suspense>
76
+ <LazyChatbot
77
+ prompts={props.data.prompts}
78
+ showConfigurationControls={props.data.showConfigurationControls}
79
+ maxHeight={props.data.maxHeight}
80
+ allowAttachments={props.data.allowAttachments}
81
+ disabled={props.data.disabled}
82
+ config={props.data.config}
83
+ get_chat_history={props.functions.get_chat_history}
84
+ delete_chat_history={props.functions.delete_chat_history}
85
+ delete_chat_message={props.functions.delete_chat_message}
86
+ send_prompt={props.functions.send_prompt}
87
+ value={props.value?.messages || Arrays.EMPTY}
88
+ setValue={(messages) => props.setValue({ messages })}
89
+ host={props.host}
90
+ />
91
+ </Suspense>
95
92
  ));
@@ -3,7 +3,6 @@ import "../vega/vega.css";
3
3
 
4
4
  import React from "react";
5
5
  import { z } from "zod";
6
- import { TooltipProvider } from "@/components/ui/tooltip";
7
6
  import { createPlugin } from "@/plugins/core/builder";
8
7
  import type { DataExplorerState } from "./ConnectedDataExplorerComponent";
9
8
 
@@ -21,11 +20,9 @@ export const DataExplorerPlugin = createPlugin<DataExplorerState>(
21
20
  }),
22
21
  )
23
22
  .renderer((props) => (
24
- <TooltipProvider>
25
- <LazyDataExplorerComponent
26
- {...props.data}
27
- value={props.value}
28
- setValue={props.setValue}
29
- />
30
- </TooltipProvider>
23
+ <LazyDataExplorerComponent
24
+ {...props.data}
25
+ value={props.value}
26
+ setValue={props.setValue}
27
+ />
31
28
  ));
@@ -297,13 +297,7 @@ export class MatplotlibRenderer {
297
297
  // Create canvas
298
298
  const canvas = document.createElement("canvas");
299
299
  canvas.className = "block cursor-crosshair";
300
- const dpr = globalThis.devicePixelRatio ?? 1;
301
- canvas.width = this.#state.width * dpr;
302
- canvas.height = this.#state.height * dpr;
303
- canvas.style.width = `${this.#state.width}px`;
304
- canvas.style.maxWidth = "100%";
305
- canvas.style.height = "auto";
306
- canvas.style.aspectRatio = `${this.#state.width} / ${this.#state.height}`;
300
+ this.#syncCanvasSize(canvas);
307
301
  canvas.style.touchAction = "none";
308
302
  container.append(canvas);
309
303
  this.#canvas = canvas;
@@ -322,6 +316,10 @@ export class MatplotlibRenderer {
322
316
  signal: options.signal,
323
317
  });
324
318
 
319
+ // Watch for devicePixelRatio changes (e.g. browser zoom, moving between
320
+ // displays). matchMedia fires exactly once per DPR transition.
321
+ this.#watchDevicePixelRatio(options.signal);
322
+
325
323
  // Clean up on abort
326
324
  options.signal.addEventListener("abort", () => {
327
325
  cancelAnimationFrame(this.#rafId);
@@ -333,6 +331,38 @@ export class MatplotlibRenderer {
333
331
  this.#restoreSelection(this.#state.value);
334
332
  }
335
333
 
334
+ /** Set the canvas buffer + CSS size to match current logical size and DPR. */
335
+ #syncCanvasSize(canvas: HTMLCanvasElement = this.#canvas): void {
336
+ const dpr = globalThis.devicePixelRatio ?? 1;
337
+ const { width, height } = this.#state;
338
+ canvas.width = width * dpr;
339
+ canvas.height = height * dpr;
340
+ canvas.style.width = `${width}px`;
341
+ canvas.style.maxWidth = "100%";
342
+ canvas.style.height = "auto";
343
+ canvas.style.aspectRatio = `${width} / ${height}`;
344
+ }
345
+
346
+ /**
347
+ * Observe devicePixelRatio changes via matchMedia. Each listener fires once
348
+ * per transition, so we re-register after every change.
349
+ */
350
+ #watchDevicePixelRatio(signal: AbortSignal): void {
351
+ if (signal.aborted) {
352
+ return;
353
+ }
354
+ const mq = matchMedia(
355
+ `(resolution: ${globalThis.devicePixelRatio ?? 1}dppx)`,
356
+ );
357
+ const onChange = () => {
358
+ this.#syncCanvasSize();
359
+ this.#drawCanvas();
360
+ // Re-register for the next DPR transition
361
+ this.#watchDevicePixelRatio(signal);
362
+ };
363
+ mq.addEventListener("change", onChange, { once: true, signal });
364
+ }
365
+
336
366
  update(state: MatplotlibState): void {
337
367
  const prev = this.#state;
338
368
  this.#state = state;
@@ -341,13 +371,7 @@ export class MatplotlibRenderer {
341
371
 
342
372
  // Update canvas dimensions if changed
343
373
  if (state.width !== prev.width || state.height !== prev.height) {
344
- const dpr = globalThis.devicePixelRatio ?? 1;
345
- this.#canvas.width = state.width * dpr;
346
- this.#canvas.height = state.height * dpr;
347
- this.#canvas.style.width = `${state.width}px`;
348
- this.#canvas.style.maxWidth = "100%";
349
- this.#canvas.style.height = "auto";
350
- this.#canvas.style.aspectRatio = `${state.width} / ${state.height}`;
374
+ this.#syncCanvasSize();
351
375
  needsRedraw = true;
352
376
  }
353
377
 
@@ -8,7 +8,6 @@ import type { Data, VegaComponentState } from "./vega-component";
8
8
 
9
9
  import "./vega.css";
10
10
  import React, { type JSX } from "react";
11
- import { TooltipProvider } from "@/components/ui/tooltip";
12
11
 
13
12
  const LazyVegaComponent = React.lazy(() => import("./vega-component"));
14
13
 
@@ -29,13 +28,11 @@ export class VegaPlugin implements IPlugin<VegaComponentState, Data> {
29
28
 
30
29
  render(props: IPluginProps<VegaComponentState, Data>): JSX.Element {
31
30
  return (
32
- <TooltipProvider>
33
- <LazyVegaComponent
34
- value={props.value}
35
- setValue={props.setValue}
36
- {...props.data}
37
- />
38
- </TooltipProvider>
31
+ <LazyVegaComponent
32
+ value={props.value}
33
+ setValue={props.setValue}
34
+ {...props.data}
35
+ />
39
36
  );
40
37
  }
41
38
  }