@marimo-team/islands 0.24.1-dev67 → 0.24.1-dev68

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.
@@ -1,4 +1,4 @@
1
- import { $ as e, O as t, et as n } from "./html-to-image-BKk_y4-k.js";
1
+ import { O as e, et as t, tt as n } from "./html-to-image-B7RQgQQy.js";
2
2
  import { u as r } from "./dist-wdOJ5T7-.js";
3
3
  import { b as i } from "./select-DQ9jU_B_.js";
4
4
  import { n as a } from "./semaphore-89jsoHtS.js";
@@ -23,8 +23,8 @@ function c(e) {
23
23
  for (let e = 0; e < a; e++) o[e] = i.charCodeAt(e);
24
24
  return new Blob([o], { type: r });
25
25
  }
26
- function l({ contents: e, mimeType: n, isBase64: r }) {
27
- return r ? c(t(e, n)) : new Blob([e], { type: n });
26
+ function l({ contents: t, mimeType: n, isBase64: r }) {
27
+ return r ? c(e(t, n)) : new Blob([t], { type: n });
28
28
  }
29
29
  //#endregion
30
30
  //#region src/core/dom/events.ts
@@ -72,8 +72,8 @@ function v(e) {
72
72
  }
73
73
  //#endregion
74
74
  //#region src/components/editor/output/console/process-output.ts
75
- function y(t) {
76
- return t.mimetype.startsWith("application/vnd.marimo") || t.mimetype === "text/html" ? n(i.asString(t.data)) : e(i.asString(t.data));
75
+ function y(e) {
76
+ return e.mimetype.startsWith("application/vnd.marimo") || e.mimetype === "text/html" ? n(i.asString(e.data)) : t(i.asString(e.data));
77
77
  }
78
78
  //#endregion
79
79
  export { d as a, h as c, s as d, o as f, m as i, c as l, _ as n, p as o, v as r, f as s, y as t, l as u };
@@ -2,9 +2,9 @@ import { a as e } from "./rolldown-runtime-Cb76FRpK.js";
2
2
  import { c as t, g as n, h as r, m as i, t as a } from "./button-DlqUC8W-.js";
3
3
  import { t as o } from "./react-DMWcHin-.js";
4
4
  import { t as s } from "./compiler-runtime-CAH9RLw2.js";
5
- import { Ct as c, rt as l } from "./html-to-image-BKk_y4-k.js";
5
+ import { it as c, wt as l } from "./html-to-image-B7RQgQQy.js";
6
6
  import { u } from "./dist-wdOJ5T7-.js";
7
- import { J as d, X as f, Y as p, in as m, l as h, n as g, rn as _, sn as v, t as y, u as b } from "./common-8QNQrcue.js";
7
+ import { J as d, X as f, Y as p, in as m, l as h, n as g, rn as _, sn as v, t as y, u as b } from "./common-C0qiyUYJ.js";
8
8
  import { R as x } from "./select-DQ9jU_B_.js";
9
9
  import { X as S } from "./dropdown-menu-C8N-kZzD.js";
10
10
  import { t as C } from "./jsx-runtime-CpoKNRgu.js";
@@ -6391,20 +6391,20 @@ var sa = (e) => {
6391
6391
  let t = (0, A.c)(13), { cell: n } = e, r;
6392
6392
  t[0] !== n.id || t[1] !== n.name ? (r = y(n.id, n.name), t[0] = n.id, t[1] = n.name, t[2] = r) : r = t[2];
6393
6393
  let i = n.id, a = n.status, o = n.output, s;
6394
- t[3] === n ? s = t[4] : (s = l(n, !1), t[3] = n, t[4] = s);
6395
- let c;
6396
- t[5] !== n.id || t[6] !== n.output || t[7] !== n.status || t[8] !== s ? (c = /* @__PURE__ */ (0, q.jsx)(b, {
6394
+ t[3] === n ? s = t[4] : (s = c(n, !1), t[3] = n, t[4] = s);
6395
+ let l;
6396
+ t[5] !== n.id || t[6] !== n.output || t[7] !== n.status || t[8] !== s ? (l = /* @__PURE__ */ (0, q.jsx)(b, {
6397
6397
  cellId: i,
6398
6398
  status: a,
6399
6399
  output: o,
6400
6400
  stale: s
6401
- }), t[5] = n.id, t[6] = n.output, t[7] = n.status, t[8] = s, t[9] = c) : c = t[9];
6401
+ }), t[5] = n.id, t[6] = n.output, t[7] = n.status, t[8] = s, t[9] = l) : l = t[9];
6402
6402
  let u;
6403
- return t[10] !== r || t[11] !== c ? (u = /* @__PURE__ */ (0, q.jsx)("div", {
6403
+ return t[10] !== r || t[11] !== l ? (u = /* @__PURE__ */ (0, q.jsx)("div", {
6404
6404
  className: "flex flex-col",
6405
6405
  ...r,
6406
- children: c
6407
- }), t[10] = r, t[11] = c, t[12] = u) : u = t[12], u;
6406
+ children: l
6407
+ }), t[10] = r, t[11] = l, t[12] = u) : u = t[12], u;
6408
6408
  }, ca = (e) => {
6409
6409
  let t = (0, A.c)(24), { subslide: n, resolveShowCode: r, isEditable: i, slideConfigs: a, contentStyle: o } = e, s, c, l, u, d, f;
6410
6410
  if (t[0] !== o || t[1] !== i || t[2] !== r || t[3] !== a || t[4] !== n) {
@@ -6453,7 +6453,7 @@ var ua = (e) => {
6453
6453
  let o;
6454
6454
  return t[3] === n ? o = t[4] : (o = /* @__PURE__ */ (0, q.jsx)(sa, { cell: n }), t[3] = n, t[4] = o), o;
6455
6455
  }, da = (e) => {
6456
- let n = (0, A.c)(147), { slideCells: o, layout: s, setLayout: l, noOutputIds: u, activeIndex: y, onSlideChange: b, mode: x, configWidth: S, isEditable: C } = e, O = C !== void 0 && C, j = (0, k.useRef)(null), M = (0, k.useRef)(null), { width: te, height: N } = ea(j), P = w() != null, ne = E(c), re;
6456
+ let n = (0, A.c)(147), { slideCells: o, layout: s, setLayout: c, noOutputIds: u, activeIndex: y, onSlideChange: b, mode: x, configWidth: S, isEditable: C } = e, O = C !== void 0 && C, j = (0, k.useRef)(null), M = (0, k.useRef)(null), { width: te, height: N } = ea(j), P = w() != null, ne = E(l), re;
6457
6457
  n[0] === ne ? re = n[1] : (re = ne ? [] : [Ji], n[0] = ne, n[1] = re);
6458
6458
  let ie = re, [ae, F] = (0, k.useState)(fa), oe = g(o), se;
6459
6459
  n[2] === oe ? se = n[3] : (se = !ee() && oe, n[2] = oe, n[3] = se);
@@ -6720,17 +6720,17 @@ var ua = (e) => {
6720
6720
  disabled: P
6721
6721
  }), n[129] = P, n[130] = ot);
6722
6722
  let st = L?.id, ct;
6723
- n[131] !== s || n[132] !== l || n[133] !== st ? (ct = /* @__PURE__ */ (0, q.jsx)(d, {
6723
+ n[131] !== s || n[132] !== c || n[133] !== st ? (ct = /* @__PURE__ */ (0, q.jsx)(d, {
6724
6724
  defaultSize: 10,
6725
6725
  minSize: 4,
6726
6726
  collapsible: !0,
6727
6727
  collapsedSize: 4,
6728
6728
  children: /* @__PURE__ */ (0, q.jsx)(fr, {
6729
6729
  layout: s,
6730
- setLayout: l,
6730
+ setLayout: c,
6731
6731
  cellId: st
6732
6732
  })
6733
- }), n[131] = s, n[132] = l, n[133] = st, n[134] = ct) : ct = n[134];
6733
+ }), n[131] = s, n[132] = c, n[133] = st, n[134] = ct) : ct = n[134];
6734
6734
  let lt;
6735
6735
  n[135] !== K || n[136] !== ot || n[137] !== ct ? (lt = /* @__PURE__ */ (0, q.jsxs)(p, {
6736
6736
  direction: "vertical",
@@ -6743,12 +6743,12 @@ var ua = (e) => {
6743
6743
  ]
6744
6744
  }), n[135] = K, n[136] = ot, n[137] = ct, n[138] = lt) : lt = n[138];
6745
6745
  let ut;
6746
- n[139] !== L || n[140] !== S || n[141] !== s || n[142] !== l ? (ut = /* @__PURE__ */ (0, q.jsx)(h, {
6746
+ n[139] !== L || n[140] !== S || n[141] !== s || n[142] !== c ? (ut = /* @__PURE__ */ (0, q.jsx)(h, {
6747
6747
  configWidth: S,
6748
6748
  layout: s,
6749
- setLayout: l,
6749
+ setLayout: c,
6750
6750
  activeConfigCell: L
6751
- }), n[139] = L, n[140] = S, n[141] = s, n[142] = l, n[143] = ut) : ut = n[143];
6751
+ }), n[139] = L, n[140] = S, n[141] = s, n[142] = c, n[143] = ut) : ut = n[143];
6752
6752
  let dt;
6753
6753
  return n[144] !== lt || n[145] !== ut ? (dt = /* @__PURE__ */ (0, q.jsxs)("div", {
6754
6754
  className: "flex-1 min-w-0 flex flex-row gap-3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.24.1-dev67",
3
+ "version": "0.24.1-dev68",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -0,0 +1,110 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { python } from "@codemirror/lang-python";
4
+ import { EditorState } from "@codemirror/state";
5
+ import { EditorView } from "@codemirror/view";
6
+ import { fireEvent, render, screen } from "@testing-library/react";
7
+ import { Provider } from "jotai";
8
+ import { afterEach, describe, expect, test } from "vitest";
9
+ import { MockNotebook } from "@/__mocks__/notebook";
10
+ import { cellId, variableName } from "@/__tests__/branded";
11
+ import type { CellHandle } from "@/components/editor/notebook-cell";
12
+ import { TooltipProvider } from "@/components/ui/tooltip";
13
+ import { initialNotebookState, notebookAtom } from "@/core/cells/cells";
14
+ import { HTMLCellId } from "@/core/cells/ids";
15
+ import { editorMountScheduler } from "@/core/codemirror/editor-mount-scheduler";
16
+ import { store } from "@/core/state/jotai";
17
+ import type { Variables } from "@/core/variables/types";
18
+ import { VariableTable } from "../variables-table";
19
+
20
+ async function tick(): Promise<void> {
21
+ await new Promise((resolve) => requestAnimationFrame(resolve));
22
+ }
23
+
24
+ const views: EditorView[] = [];
25
+ const elements: HTMLElement[] = [];
26
+
27
+ afterEach(() => {
28
+ for (const view of views.splice(0)) {
29
+ view.destroy();
30
+ }
31
+ for (const element of elements.splice(0)) {
32
+ element.remove();
33
+ }
34
+ store.set(notebookAtom, initialNotebookState());
35
+ });
36
+
37
+ describe("VariableTable cell links", () => {
38
+ test("declared-by click builds the queued editor and highlights the variable", async () => {
39
+ const usageCell = cellId("usage");
40
+ const definingCell = cellId("defining");
41
+ const definingCode = "y = 1\nx = 42";
42
+
43
+ const definingView = new EditorView({
44
+ state: EditorState.create({
45
+ doc: definingCode,
46
+ extensions: [python()],
47
+ }),
48
+ });
49
+ views.push(definingView);
50
+
51
+ const notebook = MockNotebook.notebookState({
52
+ cellData: {
53
+ [usageCell]: { name: "usage_cell" },
54
+ [definingCell]: { name: "defining_cell" },
55
+ },
56
+ });
57
+ // The defining cell's editor build still waits in the mount queue, the
58
+ // state right after a large notebook opens.
59
+ const handle: { current: CellHandle | null } = { current: null };
60
+ notebook.cellHandles[definingCell] = handle;
61
+ editorMountScheduler.request(definingCell, () => {
62
+ handle.current = {
63
+ editorView: definingView,
64
+ editorViewOrNull: definingView,
65
+ };
66
+ });
67
+ store.set(notebookAtom, notebook);
68
+
69
+ // The link's click handler requires the target cell's element on the page.
70
+ const cellElement = document.createElement("div");
71
+ cellElement.id = HTMLCellId.create(definingCell);
72
+ document.body.append(cellElement);
73
+ elements.push(cellElement);
74
+
75
+ const variables: Variables = {
76
+ [variableName("x")]: {
77
+ dataType: "int",
78
+ declaredBy: [definingCell],
79
+ name: variableName("x"),
80
+ usedBy: [usageCell],
81
+ value: "42",
82
+ },
83
+ };
84
+
85
+ render(
86
+ <Provider store={store}>
87
+ <TooltipProvider>
88
+ <VariableTable
89
+ cellIds={[usageCell, definingCell]}
90
+ variables={variables}
91
+ />
92
+ </TooltipProvider>
93
+ </Provider>,
94
+ );
95
+
96
+ expect(handle.current).toBeNull();
97
+
98
+ fireEvent.click(screen.getByText("defining_cell"));
99
+ // First frame: the link's click callback runs and builds the queued
100
+ // editor synchronously.
101
+ await tick();
102
+ expect(handle.current?.editorView).toBe(definingView);
103
+ expect(cellElement.classList.contains("focus-outline")).toBe(true);
104
+ // Second frame: the jump dispatches the selection.
105
+ await tick();
106
+ expect(definingView.state.selection.main.head).toBe(
107
+ definingCode.indexOf("x = 42"),
108
+ );
109
+ });
110
+ });
@@ -15,7 +15,7 @@ import { SquareEqualIcon, WorkflowIcon } from "lucide-react";
15
15
  import React, { memo, useMemo } from "react";
16
16
  import { useLocale } from "react-aria";
17
17
  import { CellLink } from "@/components/editor/links/cell-link";
18
- import { getCellEditorView, useCellNames } from "@/core/cells/cells";
18
+ import { ensureCellEditorView, useCellNames } from "@/core/cells/cells";
19
19
  import type { CellId } from "@/core/cells/ids";
20
20
  import { isInternalCellName } from "@/core/cells/names";
21
21
  import { goToVariableDefinition } from "@/core/codemirror/go-to-definition/commands";
@@ -135,9 +135,8 @@ const COLUMNS = [
135
135
  cell: ({ getValue }) => {
136
136
  const [declaredBy, usedBy, name] = getValue();
137
137
 
138
- // Highlight the variable in the cell editor
139
138
  const highlightInCell = (cellId: CellId) => {
140
- const editorView = getCellEditorView(cellId);
139
+ const editorView = ensureCellEditorView(cellId);
141
140
  if (editorView) {
142
141
  goToVariableDefinition(editorView, name);
143
142
  }
@@ -19,6 +19,7 @@ import { Objects } from "@/utils/objects";
19
19
  import { extractAllTracebackInfo, type TracebackInfo } from "@/utils/traceback";
20
20
  import { createReducerAndAtoms } from "../../utils/createReducer";
21
21
  import { foldAllBulk, unfoldAllBulk } from "../codemirror/editing/commands";
22
+ import { editorMountScheduler } from "../codemirror/editor-mount-scheduler";
22
23
  import {
23
24
  splitEditor,
24
25
  updateEditorCodeFromPython,
@@ -1919,6 +1920,16 @@ export const getCellEditorView = (cellId: CellId) => {
1919
1920
  return cellHandles[cellId].current?.editorView;
1920
1921
  };
1921
1922
 
1923
+ /**
1924
+ * Get the cell's editor view. When the view's build is still queued in the
1925
+ * progressive-mount scheduler, build it synchronously first. This lets user
1926
+ * actions target a cell whose editor is not mounted yet.
1927
+ */
1928
+ export const ensureCellEditorView = (cellId: CellId) => {
1929
+ editorMountScheduler.promote(cellId);
1930
+ return getCellEditorView(cellId);
1931
+ };
1932
+
1922
1933
  export function flattenTopLevelNotebookCells(
1923
1934
  state: NotebookState,
1924
1935
  ): (CellData & CellRuntimeState)[] {
@@ -5,7 +5,9 @@ import { EditorState } from "@codemirror/state";
5
5
  import { EditorView, keymap } from "@codemirror/view";
6
6
  import { afterEach, describe, expect, test, vi } from "vitest";
7
7
  import { cellId, variableName } from "@/__tests__/branded";
8
+ import type { CellHandle } from "@/components/editor/notebook-cell";
8
9
  import { initialNotebookState, notebookAtom } from "@/core/cells/cells";
10
+ import { editorMountScheduler } from "@/core/codemirror/editor-mount-scheduler";
9
11
  import { store } from "@/core/state/jotai";
10
12
  import { variablesAtom } from "@/core/variables/state";
11
13
  import {
@@ -184,6 +186,52 @@ print(mymodule)`;
184
186
  moduleCode.indexOf("mymodule"),
185
187
  );
186
188
  });
189
+
190
+ test("builds the defining cell's queued editor before jumping", async () => {
191
+ const definingCell = cellId("queued-defining-cell");
192
+ const usageCell = cellId("usage-cell");
193
+ const definingCode = "a = 10";
194
+ const usageCode = "print(a)";
195
+
196
+ const definingView = createEditor(definingCode, definingCode.length);
197
+ const usageView = createEditor(usageCode, usageCode.indexOf("a"));
198
+ views.push(definingView, usageView);
199
+
200
+ // The defining cell's editor build still waits in the mount queue, the
201
+ // state right after a large notebook opens.
202
+ const handle: { current: CellHandle | null } = { current: null };
203
+ editorMountScheduler.request(definingCell, () => {
204
+ handle.current = {
205
+ editorView: definingView,
206
+ editorViewOrNull: definingView,
207
+ };
208
+ });
209
+
210
+ const notebook = initialNotebookState();
211
+ notebook.cellHandles[definingCell] = handle;
212
+ notebook.cellHandles[usageCell] = {
213
+ current: { editorView: usageView, editorViewOrNull: usageView },
214
+ };
215
+
216
+ store.set(notebookAtom, notebook);
217
+ store.set(variablesAtom, {
218
+ [variableName("a")]: {
219
+ dataType: "int",
220
+ declaredBy: [definingCell],
221
+ name: variableName("a"),
222
+ usedBy: [usageCell],
223
+ value: "10",
224
+ },
225
+ });
226
+
227
+ const result = goToDefinitionAtCursorPosition(usageView);
228
+
229
+ expect(result).toBe(true);
230
+ // The jump built the queued editor synchronously.
231
+ expect(handle.current?.editorView).toBe(definingView);
232
+ await tick();
233
+ expect(definingView.state.selection.main.head).toBe(0);
234
+ });
187
235
  });
188
236
 
189
237
  describe("goToDefinitionWithLspFallback", () => {
@@ -10,7 +10,7 @@ import {
10
10
  } from "@codemirror/view";
11
11
  import type { CellId } from "@/core/cells/ids";
12
12
  import { hotkeysAtom, platformAtom } from "../../config/config";
13
- import { notebookAtom } from "../../cells/cells";
13
+ import { ensureCellEditorView } from "../../cells/cells";
14
14
  import { store } from "../../state/jotai";
15
15
  import { variablesAtom } from "../../variables/state";
16
16
  import type { VariableName, Variables } from "../../variables/types";
@@ -181,12 +181,6 @@ function getEditorForVariable(
181
181
  return null;
182
182
  }
183
183
 
184
- /**
185
- * Go to the given line number in the editor view.
186
- * @param view The editor view to go to.
187
- * @param line The line number to go to.
188
- */
189
184
  function getEditorForCell(cellId: CellId): EditorView | null {
190
- const notebookState = store.get(notebookAtom);
191
- return notebookState.cellHandles[cellId].current?.editorView ?? null;
185
+ return ensureCellEditorView(cellId) ?? null;
192
186
  }