@marimo-team/frontend 0.21.2-dev39 → 0.21.2-dev40
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.
- package/dist/assets/{edit-page-OLDApdB5.js → edit-page-Cqvk7zWH.js} +7 -7
- package/dist/assets/{index-DZVnuqxZ.js → index-BSzIstPK.js} +2 -2
- package/dist/assets/{panels-LFK8M8Jo.js → panels-B2DAaT1E.js} +1 -1
- package/dist/assets/{run-page-B-zpKjX2.js → run-page-DMpRvHgf.js} +1 -1
- package/dist/assets/tracing-BxcUfhUv.js +1 -0
- package/dist/assets/{tracing-panel-CKmy1CaA.js → tracing-panel-DK4YfdoZ.js} +2 -2
- package/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/__mocks__/notebook.ts +9 -9
- package/src/__tests__/branded.ts +20 -0
- package/src/components/data-table/charts/__tests__/storage.test.ts +7 -7
- package/src/components/editor/__tests__/data-attributes.test.tsx +8 -8
- package/src/components/editor/ai/__tests__/completion-utils.test.ts +15 -15
- package/src/components/editor/navigation/__tests__/clipboard.test.ts +2 -2
- package/src/components/editor/navigation/__tests__/selection.test.ts +7 -6
- package/src/components/editor/navigation/__tests__/state.test.ts +8 -7
- package/src/components/editor/output/MarimoErrorOutput.tsx +7 -7
- package/src/components/editor/output/__tests__/traceback.test.tsx +4 -4
- package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +4 -4
- package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +8 -1
- package/src/components/storage/storage-inspector.tsx +1 -2
- package/src/components/tracing/tracing.tsx +3 -1
- package/src/core/ai/__tests__/staged-cells.test.ts +9 -8
- package/src/core/ai/context/providers/__tests__/cell-output.test.ts +31 -31
- package/src/core/ai/context/providers/__tests__/datasource.test.ts +3 -3
- package/src/core/ai/context/providers/__tests__/tables.test.ts +3 -2
- package/src/core/ai/context/providers/__tests__/variable.test.ts +84 -63
- package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +10 -9
- package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +6 -6
- package/src/core/ai/tools/edit-notebook-tool.ts +3 -3
- package/src/core/cells/__tests__/add-missing-import.test.ts +3 -3
- package/src/core/cells/__tests__/cells.test.ts +192 -135
- package/src/core/cells/__tests__/focus.test.ts +5 -4
- package/src/core/cells/__tests__/logs.test.ts +13 -12
- package/src/core/cells/__tests__/pending-delete-service.test.tsx +3 -3
- package/src/core/cells/__tests__/runs.test.ts +22 -21
- package/src/core/cells/__tests__/scrollCellIntoView.test.ts +8 -7
- package/src/core/cells/__tests__/session.test.ts +23 -22
- package/src/core/cells/cells.ts +1 -1
- package/src/core/cells/ids.ts +5 -5
- package/src/core/cells/logs.ts +2 -2
- package/src/core/cells/runs.ts +6 -8
- package/src/core/codemirror/__tests__/format.test.ts +34 -36
- package/src/core/codemirror/__tests__/setup.test.ts +2 -2
- package/src/core/codemirror/cells/__tests__/traceback-decorations.test.ts +33 -32
- package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +12 -13
- package/src/core/codemirror/language/__tests__/utils.test.ts +3 -3
- package/src/core/codemirror/language/embedded/__tests__/embedded-python.test.ts +7 -8
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +4 -3
- package/src/core/codemirror/reactive-references/__tests__/analyzer.test.ts +7 -6
- package/src/core/codemirror/reactive-references/analyzer.ts +2 -2
- package/src/core/datasets/__tests__/data-source.test.ts +5 -6
- package/src/core/datasets/state.ts +1 -1
- package/src/core/errors/__tests__/errors.test.ts +2 -1
- package/src/core/export/__tests__/hooks.test.ts +37 -36
- package/src/core/islands/main.ts +2 -7
- package/src/core/kernel/__tests__/handlers.test.ts +5 -4
- package/src/core/kernel/handlers.ts +7 -4
- package/src/core/network/DeferredRequestRegistry.ts +2 -2
- package/src/core/network/__tests__/CachingRequestRegistry.test.ts +9 -10
- package/src/core/network/__tests__/DeferredRequestRegistry.test.ts +4 -6
- package/src/core/static/__tests__/virtual-file-tracker.test.ts +8 -8
- package/src/core/static/virtual-file-tracker.ts +1 -1
- package/src/core/storage/__tests__/state.test.ts +31 -21
- package/src/core/storage/state.ts +1 -1
- package/src/core/variables/__tests__/state.test.ts +6 -6
- package/src/core/variables/types.ts +2 -2
- package/src/core/wasm/__tests__/state.test.ts +8 -8
- package/src/core/websocket/useMarimoKernelConnection.tsx +12 -15
- package/src/plugins/impl/anywidget/model.ts +1 -2
- package/src/stories/cell.stories.tsx +8 -8
- package/src/stories/layout/vertical/one-column.stories.tsx +9 -8
- package/src/stories/log-viewer.stories.tsx +8 -8
- package/src/stories/variables.stories.tsx +2 -2
- package/src/utils/__tests__/download.test.tsx +19 -18
- package/src/utils/json/base64.ts +3 -3
- package/src/utils/traceback.ts +5 -3
- package/dist/assets/tracing-QGIu3SN5.js +0 -1
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
2
3
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
3
|
-
import
|
|
4
|
+
import { cellId } from "@/__tests__/branded";
|
|
4
5
|
import type { CellFocusState } from "../focus";
|
|
5
6
|
import { exportedForTesting } from "../focus";
|
|
6
7
|
|
|
7
8
|
const { initialState, reducer, createActions } = exportedForTesting;
|
|
8
9
|
|
|
9
10
|
const CellIds = {
|
|
10
|
-
a: "a"
|
|
11
|
-
b: "b"
|
|
12
|
-
c: "c"
|
|
11
|
+
a: cellId("a"),
|
|
12
|
+
b: cellId("b"),
|
|
13
|
+
c: cellId("c"),
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
describe("cell focus reducer", () => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
4
|
+
import { cellId } from "@/__tests__/branded";
|
|
4
5
|
import type { CellMessage } from "../../kernel/messages";
|
|
5
6
|
import { formatLogTimestamp, getCellLogsForMessage } from "../logs";
|
|
6
7
|
|
|
@@ -18,7 +19,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
18
19
|
|
|
19
20
|
test("handles text/plain MIME type on stdout", () => {
|
|
20
21
|
const cellMessage: CellMessage = {
|
|
21
|
-
cell_id: "cell-1",
|
|
22
|
+
cell_id: cellId("cell-1"),
|
|
22
23
|
console: [
|
|
23
24
|
{
|
|
24
25
|
mimetype: "text/plain",
|
|
@@ -46,7 +47,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
46
47
|
|
|
47
48
|
test("handles text/plain MIME type on stderr", () => {
|
|
48
49
|
const cellMessage: CellMessage = {
|
|
49
|
-
cell_id: "cell-2",
|
|
50
|
+
cell_id: cellId("cell-2"),
|
|
50
51
|
console: [
|
|
51
52
|
{
|
|
52
53
|
mimetype: "text/plain",
|
|
@@ -74,7 +75,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
74
75
|
|
|
75
76
|
test("handles text/html MIME type and strips HTML tags", () => {
|
|
76
77
|
const cellMessage: CellMessage = {
|
|
77
|
-
cell_id: "cell-3",
|
|
78
|
+
cell_id: cellId("cell-3"),
|
|
78
79
|
console: [
|
|
79
80
|
{
|
|
80
81
|
mimetype: "text/html",
|
|
@@ -102,7 +103,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
102
103
|
|
|
103
104
|
test("handles text/html MIME type on stderr", () => {
|
|
104
105
|
const cellMessage: CellMessage = {
|
|
105
|
-
cell_id: "cell-4",
|
|
106
|
+
cell_id: cellId("cell-4"),
|
|
106
107
|
console: [
|
|
107
108
|
{
|
|
108
109
|
mimetype: "text/html",
|
|
@@ -130,7 +131,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
130
131
|
|
|
131
132
|
test("handles application/vnd.marimo+traceback MIME type and strips HTML", () => {
|
|
132
133
|
const cellMessage: CellMessage = {
|
|
133
|
-
cell_id: "cell-5",
|
|
134
|
+
cell_id: cellId("cell-5"),
|
|
134
135
|
console: [
|
|
135
136
|
{
|
|
136
137
|
mimetype: "application/vnd.marimo+traceback",
|
|
@@ -156,7 +157,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
156
157
|
|
|
157
158
|
test("handles multiple console outputs with different MIME types", () => {
|
|
158
159
|
const cellMessage: CellMessage = {
|
|
159
|
-
cell_id: "cell-7",
|
|
160
|
+
cell_id: cellId("cell-7"),
|
|
160
161
|
console: [
|
|
161
162
|
{
|
|
162
163
|
mimetype: "text/plain",
|
|
@@ -196,7 +197,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
196
197
|
vi.spyOn(Date, "now").mockReturnValue(now);
|
|
197
198
|
|
|
198
199
|
const cellMessage: CellMessage = {
|
|
199
|
-
cell_id: "cell-8",
|
|
200
|
+
cell_id: cellId("cell-8"),
|
|
200
201
|
console: [
|
|
201
202
|
{
|
|
202
203
|
mimetype: "text/plain",
|
|
@@ -219,7 +220,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
219
220
|
|
|
220
221
|
test("ignores unsupported MIME types", () => {
|
|
221
222
|
const cellMessage: CellMessage = {
|
|
222
|
-
cell_id: "cell-9",
|
|
223
|
+
cell_id: cellId("cell-9"),
|
|
223
224
|
console: [
|
|
224
225
|
{
|
|
225
226
|
mimetype: "application/json",
|
|
@@ -241,7 +242,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
241
242
|
|
|
242
243
|
test("ignores non-logging channels", () => {
|
|
243
244
|
const cellMessage: CellMessage = {
|
|
244
|
-
cell_id: "cell-10",
|
|
245
|
+
cell_id: cellId("cell-10"),
|
|
245
246
|
console: [
|
|
246
247
|
{
|
|
247
248
|
mimetype: "text/plain",
|
|
@@ -263,7 +264,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
263
264
|
|
|
264
265
|
test("returns empty array when console is null", () => {
|
|
265
266
|
const cellMessage: CellMessage = {
|
|
266
|
-
cell_id: "cell-11",
|
|
267
|
+
cell_id: cellId("cell-11"),
|
|
267
268
|
console: null as unknown as CellMessage["console"],
|
|
268
269
|
output: null,
|
|
269
270
|
status: "idle",
|
|
@@ -278,7 +279,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
278
279
|
|
|
279
280
|
test("handles complex HTML with nested elements in text/html", () => {
|
|
280
281
|
const cellMessage: CellMessage = {
|
|
281
|
-
cell_id: "cell-12",
|
|
282
|
+
cell_id: cellId("cell-12"),
|
|
282
283
|
console: [
|
|
283
284
|
{
|
|
284
285
|
mimetype: "text/html",
|
|
@@ -301,7 +302,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
301
302
|
|
|
302
303
|
test("handles marimo-error channel as stderr level", () => {
|
|
303
304
|
const cellMessage: CellMessage = {
|
|
304
|
-
cell_id: "cell-13",
|
|
305
|
+
cell_id: cellId("cell-13"),
|
|
305
306
|
console: [
|
|
306
307
|
{
|
|
307
308
|
mimetype: "text/plain",
|
|
@@ -4,11 +4,11 @@ import { act, renderHook, waitFor } from "@testing-library/react";
|
|
|
4
4
|
import { createStore, Provider } from "jotai";
|
|
5
5
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
6
|
import { MockNotebook } from "@/__mocks__/notebook";
|
|
7
|
+
import { variableName } from "@/__tests__/branded";
|
|
7
8
|
import { notebookAtom } from "@/core/cells/cells";
|
|
8
9
|
import { CellId } from "@/core/cells/ids";
|
|
9
10
|
import { createCellRuntimeState } from "@/core/cells/types";
|
|
10
11
|
import { variablesAtom } from "@/core/variables/state";
|
|
11
|
-
import type { VariableName } from "@/core/variables/types";
|
|
12
12
|
import type { Milliseconds } from "@/utils/time";
|
|
13
13
|
import {
|
|
14
14
|
usePendingDelete,
|
|
@@ -138,8 +138,8 @@ describe("pending-delete-service", () => {
|
|
|
138
138
|
|
|
139
139
|
store.set(notebookAtom, notebook);
|
|
140
140
|
store.set(variablesAtom, {
|
|
141
|
-
["x"
|
|
142
|
-
name: "x"
|
|
141
|
+
[variableName("x")]: {
|
|
142
|
+
name: variableName("x"),
|
|
143
143
|
declaredBy: [cell1Id],
|
|
144
144
|
usedBy: [cell2Id],
|
|
145
145
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
4
|
+
import { cellId } from "@/__tests__/branded";
|
|
4
5
|
import type { CellMessage } from "@/core/kernel/messages";
|
|
5
6
|
import { invariant } from "@/utils/invariant";
|
|
6
7
|
import {
|
|
@@ -22,13 +23,13 @@ describe("RunsState Reducer", () => {
|
|
|
22
23
|
let state: RunsState;
|
|
23
24
|
|
|
24
25
|
const runId = "run1" as RunId;
|
|
25
|
-
const
|
|
26
|
+
const testCellId = cellId("cell1");
|
|
26
27
|
const timestamp = Date.now();
|
|
27
28
|
const code = "print('Hello World')";
|
|
28
29
|
|
|
29
30
|
const cellNotification: CellMessage = {
|
|
30
31
|
run_id: runId,
|
|
31
|
-
cell_id:
|
|
32
|
+
cell_id: testCellId,
|
|
32
33
|
timestamp,
|
|
33
34
|
status: "queued",
|
|
34
35
|
};
|
|
@@ -61,9 +62,9 @@ describe("RunsState Reducer", () => {
|
|
|
61
62
|
runStartTime: timestamp,
|
|
62
63
|
cellRuns: new Map([
|
|
63
64
|
[
|
|
64
|
-
|
|
65
|
+
testCellId,
|
|
65
66
|
{
|
|
66
|
-
cellId,
|
|
67
|
+
cellId: testCellId,
|
|
67
68
|
code: code.slice(0, MAX_CODE_LENGTH),
|
|
68
69
|
elapsedTime: 0,
|
|
69
70
|
startTime: timestamp,
|
|
@@ -98,7 +99,7 @@ describe("RunsState Reducer", () => {
|
|
|
98
99
|
payload: {
|
|
99
100
|
cellNotification: {
|
|
100
101
|
run_id: runId2,
|
|
101
|
-
cell_id: "cell2",
|
|
102
|
+
cell_id: cellId("cell2"),
|
|
102
103
|
timestamp,
|
|
103
104
|
status: "queued",
|
|
104
105
|
},
|
|
@@ -124,7 +125,7 @@ describe("RunsState Reducer", () => {
|
|
|
124
125
|
payload: {
|
|
125
126
|
cellNotification: {
|
|
126
127
|
run_id: runId,
|
|
127
|
-
cell_id:
|
|
128
|
+
cell_id: testCellId,
|
|
128
129
|
timestamp: timestamp + 1000,
|
|
129
130
|
status: "running",
|
|
130
131
|
},
|
|
@@ -145,7 +146,7 @@ describe("RunsState Reducer", () => {
|
|
|
145
146
|
payload: {
|
|
146
147
|
cellNotification: {
|
|
147
148
|
run_id: runId,
|
|
148
|
-
cell_id:
|
|
149
|
+
cell_id: testCellId,
|
|
149
150
|
timestamp: runStartTimestamp + 5000,
|
|
150
151
|
status: "success",
|
|
151
152
|
},
|
|
@@ -171,8 +172,8 @@ describe("RunsState Reducer", () => {
|
|
|
171
172
|
type: "addCellNotification",
|
|
172
173
|
payload: {
|
|
173
174
|
cellNotification: {
|
|
174
|
-
run_id: `run${i}
|
|
175
|
-
cell_id: `cell${i}
|
|
175
|
+
run_id: `run${i}` as RunId,
|
|
176
|
+
cell_id: cellId(`cell${i}`),
|
|
176
177
|
timestamp: timestamp,
|
|
177
178
|
status: "queued",
|
|
178
179
|
},
|
|
@@ -198,7 +199,7 @@ describe("RunsState Reducer", () => {
|
|
|
198
199
|
payload: {
|
|
199
200
|
cellNotification: {
|
|
200
201
|
run_id: runId,
|
|
201
|
-
cell_id:
|
|
202
|
+
cell_id: testCellId,
|
|
202
203
|
timestamp,
|
|
203
204
|
status: "queued",
|
|
204
205
|
},
|
|
@@ -220,7 +221,7 @@ describe("RunsState Reducer", () => {
|
|
|
220
221
|
payload: {
|
|
221
222
|
cellNotification: {
|
|
222
223
|
run_id: runId,
|
|
223
|
-
cell_id:
|
|
224
|
+
cell_id: testCellId,
|
|
224
225
|
timestamp: errorTimestamp,
|
|
225
226
|
status: "running",
|
|
226
227
|
output: {
|
|
@@ -248,7 +249,7 @@ describe("RunsState Reducer", () => {
|
|
|
248
249
|
payload: {
|
|
249
250
|
cellNotification: {
|
|
250
251
|
run_id: runId,
|
|
251
|
-
cell_id:
|
|
252
|
+
cell_id: testCellId,
|
|
252
253
|
timestamp: errorTimestamp,
|
|
253
254
|
status: "running",
|
|
254
255
|
output: {
|
|
@@ -273,7 +274,7 @@ describe("RunsState Reducer", () => {
|
|
|
273
274
|
payload: {
|
|
274
275
|
cellNotification: {
|
|
275
276
|
run_id: runId,
|
|
276
|
-
cell_id:
|
|
277
|
+
cell_id: testCellId,
|
|
277
278
|
timestamp,
|
|
278
279
|
output: {
|
|
279
280
|
channel: "marimo-error",
|
|
@@ -288,7 +289,7 @@ describe("RunsState Reducer", () => {
|
|
|
288
289
|
payload: {
|
|
289
290
|
cellNotification: {
|
|
290
291
|
run_id: runId,
|
|
291
|
-
cell_id:
|
|
292
|
+
cell_id: testCellId,
|
|
292
293
|
timestamp: timestamp + 2000,
|
|
293
294
|
status: "running", // shouldn't happen
|
|
294
295
|
},
|
|
@@ -312,7 +313,7 @@ describe("RunsState Reducer", () => {
|
|
|
312
313
|
payload: {
|
|
313
314
|
cellNotification: {
|
|
314
315
|
run_id: runId2,
|
|
315
|
-
cell_id: "cell2",
|
|
316
|
+
cell_id: cellId("cell2"),
|
|
316
317
|
timestamp: timestamp + 1000,
|
|
317
318
|
status: "queued",
|
|
318
319
|
},
|
|
@@ -325,7 +326,7 @@ describe("RunsState Reducer", () => {
|
|
|
325
326
|
payload: {
|
|
326
327
|
cellNotification: {
|
|
327
328
|
run_id: runId3,
|
|
328
|
-
cell_id: "cell3",
|
|
329
|
+
cell_id: cellId("cell3"),
|
|
329
330
|
timestamp: timestamp + 2000,
|
|
330
331
|
status: "queued",
|
|
331
332
|
},
|
|
@@ -345,7 +346,7 @@ describe("RunsState Reducer", () => {
|
|
|
345
346
|
payload: {
|
|
346
347
|
cellNotification: {
|
|
347
348
|
run_id: runId,
|
|
348
|
-
cell_id: "cell2",
|
|
349
|
+
cell_id: cellId("cell2"),
|
|
349
350
|
timestamp: timestamp + 1000,
|
|
350
351
|
status: "queued",
|
|
351
352
|
},
|
|
@@ -367,7 +368,7 @@ describe("RunsState Reducer", () => {
|
|
|
367
368
|
payload: {
|
|
368
369
|
cellNotification: {
|
|
369
370
|
run_id: runId,
|
|
370
|
-
cell_id:
|
|
371
|
+
cell_id: testCellId,
|
|
371
372
|
timestamp: timestamp + 1000,
|
|
372
373
|
status: "running",
|
|
373
374
|
},
|
|
@@ -386,7 +387,7 @@ describe("RunsState Reducer", () => {
|
|
|
386
387
|
payload: {
|
|
387
388
|
cellNotification: {
|
|
388
389
|
run_id: runId,
|
|
389
|
-
cell_id:
|
|
390
|
+
cell_id: testCellId,
|
|
390
391
|
timestamp,
|
|
391
392
|
status: "queued",
|
|
392
393
|
},
|
|
@@ -405,7 +406,7 @@ describe("RunsState Reducer", () => {
|
|
|
405
406
|
payload: {
|
|
406
407
|
cellNotification: {
|
|
407
408
|
run_id: runId,
|
|
408
|
-
cell_id:
|
|
409
|
+
cell_id: testCellId,
|
|
409
410
|
timestamp,
|
|
410
411
|
status: "queued",
|
|
411
412
|
},
|
|
@@ -419,7 +420,7 @@ describe("RunsState Reducer", () => {
|
|
|
419
420
|
payload: {
|
|
420
421
|
cellNotification: {
|
|
421
422
|
run_id: runId,
|
|
422
|
-
cell_id: "cell2",
|
|
423
|
+
cell_id: cellId("cell2"),
|
|
423
424
|
timestamp: timestamp + 1000,
|
|
424
425
|
status: "queued",
|
|
425
426
|
},
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
-
import {
|
|
4
|
+
import { cellId } from "@/__tests__/branded";
|
|
5
|
+
import { HTMLCellId } from "@/core/cells/ids";
|
|
5
6
|
import { Logger } from "@/utils/Logger";
|
|
6
7
|
|
|
7
8
|
// Mock the getCellEditorView function
|
|
@@ -20,12 +21,12 @@ vi.mock("@/core/codemirror/extensions", () => ({
|
|
|
20
21
|
const { scrollCellIntoView } = await import("@/core/cells/scrollCellIntoView");
|
|
21
22
|
|
|
22
23
|
describe("scrollCellIntoView", () => {
|
|
23
|
-
const
|
|
24
|
+
const cid = cellId("test-cell-id");
|
|
24
25
|
let cellElement: HTMLElement;
|
|
25
26
|
|
|
26
27
|
beforeEach(() => {
|
|
27
28
|
cellElement = document.createElement("div");
|
|
28
|
-
cellElement.id = HTMLCellId.create(
|
|
29
|
+
cellElement.id = HTMLCellId.create(cid);
|
|
29
30
|
cellElement.scrollIntoView = vi.fn();
|
|
30
31
|
document.body.append(cellElement);
|
|
31
32
|
|
|
@@ -42,7 +43,7 @@ describe("scrollCellIntoView", () => {
|
|
|
42
43
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
44
|
mockGetCellEditorView.mockReturnValue(mockEditor as any);
|
|
44
45
|
|
|
45
|
-
scrollCellIntoView(
|
|
46
|
+
scrollCellIntoView(cid);
|
|
46
47
|
|
|
47
48
|
expect(mockScrollActiveLineIntoView).toHaveBeenCalledWith(mockEditor, {
|
|
48
49
|
behavior: "instant",
|
|
@@ -51,7 +52,7 @@ describe("scrollCellIntoView", () => {
|
|
|
51
52
|
});
|
|
52
53
|
|
|
53
54
|
it("should scroll cell element when editor is not focused", () => {
|
|
54
|
-
scrollCellIntoView(
|
|
55
|
+
scrollCellIntoView(cid);
|
|
55
56
|
|
|
56
57
|
expect(mockScrollActiveLineIntoView).not.toHaveBeenCalled();
|
|
57
58
|
expect(cellElement.scrollIntoView).toHaveBeenCalledWith({
|
|
@@ -64,10 +65,10 @@ describe("scrollCellIntoView", () => {
|
|
|
64
65
|
const warnSpy = vi.spyOn(Logger, "warn").mockImplementation(vi.fn());
|
|
65
66
|
cellElement.remove();
|
|
66
67
|
|
|
67
|
-
scrollCellIntoView(
|
|
68
|
+
scrollCellIntoView(cid);
|
|
68
69
|
|
|
69
70
|
expect(warnSpy).toHaveBeenCalledWith(
|
|
70
|
-
`[CellFocusManager] scrollCellIntoView: element not found: ${
|
|
71
|
+
`[CellFocusManager] scrollCellIntoView: element not found: ${cid}`,
|
|
71
72
|
);
|
|
72
73
|
warnSpy.mockRestore();
|
|
73
74
|
});
|
|
@@ -4,11 +4,12 @@ import type * as api from "@marimo-team/marimo-api";
|
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
5
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
6
|
import { Mocks } from "@/__mocks__/common";
|
|
7
|
+
import { cellId } from "@/__tests__/branded";
|
|
7
8
|
import { parseOutline } from "@/core/dom/outline";
|
|
8
9
|
import { MultiColumn, visibleForTesting } from "@/utils/id-tree";
|
|
9
10
|
import { invariant } from "@/utils/invariant";
|
|
10
11
|
import { Logger } from "@/utils/Logger";
|
|
11
|
-
import {
|
|
12
|
+
import { SETUP_CELL_ID } from "../ids";
|
|
12
13
|
import { notebookStateFromSession } from "../session";
|
|
13
14
|
|
|
14
15
|
// Mock dependencies
|
|
@@ -22,7 +23,7 @@ type SessionCell = api.Session["NotebookSessionV1"]["cells"][0];
|
|
|
22
23
|
type NotebookCell = api.Notebook["NotebookV1"]["cells"][0];
|
|
23
24
|
|
|
24
25
|
// Test constants
|
|
25
|
-
const CELL_1 = "cell-1"
|
|
26
|
+
const CELL_1 = cellId("cell-1");
|
|
26
27
|
|
|
27
28
|
describe("notebookStateFromSession", () => {
|
|
28
29
|
beforeEach(() => {
|
|
@@ -671,9 +672,9 @@ describe("notebookStateFromSession", () => {
|
|
|
671
672
|
);
|
|
672
673
|
// Should have correct code and output for each cell
|
|
673
674
|
for (const code of ["a", "b", "c", "d", "e", "f"]) {
|
|
674
|
-
const
|
|
675
|
-
expect(result.cellData[
|
|
676
|
-
expect(result.cellRuntime[
|
|
675
|
+
const cid = cellId(`cell-${code}`);
|
|
676
|
+
expect(result.cellData[cid].code).toBe(code);
|
|
677
|
+
expect(result.cellRuntime[cid].output).toEqual({
|
|
677
678
|
channel: "output",
|
|
678
679
|
data: `${code.toUpperCase()}!`,
|
|
679
680
|
mimetype: "text/plain",
|
|
@@ -751,26 +752,26 @@ describe("notebookStateFromSession", () => {
|
|
|
751
752
|
);
|
|
752
753
|
|
|
753
754
|
// Should have correct code for each cell
|
|
754
|
-
expect(result.cellData["cell-a"
|
|
755
|
-
expect(result.cellData["cell-c"
|
|
756
|
-
expect(result.cellData["cell-z"
|
|
757
|
-
expect(result.cellData["cell-e"
|
|
758
|
-
expect(result.cellData["cell-g"
|
|
755
|
+
expect(result.cellData[cellId("cell-a")].code).toBe("a");
|
|
756
|
+
expect(result.cellData[cellId("cell-c")].code).toBe("c");
|
|
757
|
+
expect(result.cellData[cellId("cell-z")].code).toBe("z");
|
|
758
|
+
expect(result.cellData[cellId("cell-e")].code).toBe("e");
|
|
759
|
+
expect(result.cellData[cellId("cell-g")].code).toBe("g");
|
|
759
760
|
|
|
760
761
|
// Should have session outputs for matching cells (a, c, e)
|
|
761
|
-
expect(result.cellRuntime["cell-a"
|
|
762
|
+
expect(result.cellRuntime[cellId("cell-a")].output).toEqual({
|
|
762
763
|
channel: "output",
|
|
763
764
|
data: "A!",
|
|
764
765
|
mimetype: "text/plain",
|
|
765
766
|
timestamp: 0,
|
|
766
767
|
});
|
|
767
|
-
expect(result.cellRuntime["cell-c"
|
|
768
|
+
expect(result.cellRuntime[cellId("cell-c")].output).toEqual({
|
|
768
769
|
channel: "output",
|
|
769
770
|
data: "C!",
|
|
770
771
|
mimetype: "text/plain",
|
|
771
772
|
timestamp: 0,
|
|
772
773
|
});
|
|
773
|
-
expect(result.cellRuntime["cell-e"
|
|
774
|
+
expect(result.cellRuntime[cellId("cell-e")].output).toEqual({
|
|
774
775
|
channel: "output",
|
|
775
776
|
data: "E!",
|
|
776
777
|
mimetype: "text/plain",
|
|
@@ -778,8 +779,8 @@ describe("notebookStateFromSession", () => {
|
|
|
778
779
|
});
|
|
779
780
|
|
|
780
781
|
// Should have no output for new cells (z, g) - they get stub session cells
|
|
781
|
-
expect(result.cellRuntime["cell-z"
|
|
782
|
-
expect(result.cellRuntime["cell-g"
|
|
782
|
+
expect(result.cellRuntime[cellId("cell-z")].output).toBeNull();
|
|
783
|
+
expect(result.cellRuntime[cellId("cell-g")].output).toBeNull();
|
|
783
784
|
|
|
784
785
|
// Should log warning about different cells
|
|
785
786
|
expect(Logger.warn).toHaveBeenCalledWith(
|
|
@@ -875,18 +876,18 @@ describe("notebookStateFromSession", () => {
|
|
|
875
876
|
);
|
|
876
877
|
|
|
877
878
|
// Should have correct code from notebook
|
|
878
|
-
expect(result.cellData["cell-1"
|
|
879
|
-
expect(result.cellData["cell-2"
|
|
880
|
-
expect(result.cellData["cell-3"
|
|
879
|
+
expect(result.cellData[cellId("cell-1")].code).toBe("1 / 0");
|
|
880
|
+
expect(result.cellData[cellId("cell-2")].code).toBe('mo.md("Hello")');
|
|
881
|
+
expect(result.cellData[cellId("cell-3")].code).toBe(
|
|
881
882
|
"x = mo.ui.slider(0, 10)",
|
|
882
883
|
);
|
|
883
884
|
|
|
884
885
|
// cell-1: No matching session cell (hash is null), gets stub session cell
|
|
885
|
-
expect(result.cellRuntime["cell-1"
|
|
886
|
-
expect(result.cellRuntime["cell-1"
|
|
886
|
+
expect(result.cellRuntime[cellId("cell-1")].output).toBeNull();
|
|
887
|
+
expect(result.cellRuntime[cellId("cell-1")].consoleOutputs).toEqual([]);
|
|
887
888
|
|
|
888
889
|
// cell-2: Matches session cell-1 by hash (moMd), gets its output
|
|
889
|
-
expect(result.cellRuntime["cell-2"
|
|
890
|
+
expect(result.cellRuntime[cellId("cell-2")].output).toEqual({
|
|
890
891
|
channel: "output",
|
|
891
892
|
data: "Welcome to marimo!",
|
|
892
893
|
mimetype: "text/markdown",
|
|
@@ -894,7 +895,7 @@ describe("notebookStateFromSession", () => {
|
|
|
894
895
|
});
|
|
895
896
|
|
|
896
897
|
// cell-3: Matches session cell-2 by hash (slider), gets its output
|
|
897
|
-
expect(result.cellRuntime["cell-3"
|
|
898
|
+
expect(result.cellRuntime[cellId("cell-3")].output).toEqual({
|
|
898
899
|
channel: "output",
|
|
899
900
|
data: "",
|
|
900
901
|
mimetype: "text/plain",
|
package/src/core/cells/cells.ts
CHANGED
package/src/core/cells/ids.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-redeclare */
|
|
3
3
|
|
|
4
|
+
import type { components } from "@marimo-team/marimo-api";
|
|
4
5
|
import { OBJECT_ID_ATTR } from "@/core/dom/ui-element-constants";
|
|
5
6
|
import { invariant } from "@/utils/invariant";
|
|
6
|
-
import type { TypedString } from "../../utils/typed";
|
|
7
7
|
|
|
8
8
|
const lowercase = "abcdefghijklmnopqrstuvwxyz";
|
|
9
9
|
const uppercase = lowercase.toUpperCase();
|
|
@@ -14,9 +14,9 @@ export const SCRATCH_CELL_ID = "__scratch__" as CellId;
|
|
|
14
14
|
export const SETUP_CELL_ID = "setup" as CellId;
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* A typed CellId
|
|
17
|
+
* A typed CellId — derived from the generated OpenAPI schema.
|
|
18
18
|
*/
|
|
19
|
-
export type CellId =
|
|
19
|
+
export type CellId = components["schemas"]["CellId"];
|
|
20
20
|
export const CellId = {
|
|
21
21
|
/**
|
|
22
22
|
* Create a new CellId, a random 4 letter string.
|
|
@@ -106,9 +106,9 @@ export function findCellId(element: HTMLElement): CellId | null {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
|
-
* A typed UIElementId
|
|
109
|
+
* A typed UIElementId — derived from the generated OpenAPI schema.
|
|
110
110
|
*/
|
|
111
|
-
export type UIElementId =
|
|
111
|
+
export type UIElementId = components["schemas"]["UIElementId"];
|
|
112
112
|
export const UIElementId = {
|
|
113
113
|
parse(element: Element): UIElementId | null {
|
|
114
114
|
return element.getAttribute(OBJECT_ID_ATTR) as UIElementId | null;
|
package/src/core/cells/logs.ts
CHANGED
|
@@ -43,7 +43,7 @@ export function getCellLogsForMessage(cell: CellMessage): CellLog[] {
|
|
|
43
43
|
timestamp: output.timestamp || Date.now(),
|
|
44
44
|
level: isError ? "stderr" : "stdout",
|
|
45
45
|
message: message,
|
|
46
|
-
cellId: cell.cell_id
|
|
46
|
+
cellId: cell.cell_id,
|
|
47
47
|
});
|
|
48
48
|
break;
|
|
49
49
|
}
|
|
@@ -66,7 +66,7 @@ export function getCellLogsForMessage(cell: CellMessage): CellLog[] {
|
|
|
66
66
|
cell.output.data.forEach((error) => {
|
|
67
67
|
CellLogLogger.log({
|
|
68
68
|
level: "stderr",
|
|
69
|
-
cellId: cell.cell_id
|
|
69
|
+
cellId: cell.cell_id,
|
|
70
70
|
timestamp: cell.timestamp ?? 0,
|
|
71
71
|
message: JSON.stringify(error),
|
|
72
72
|
});
|
package/src/core/cells/runs.ts
CHANGED
|
@@ -79,9 +79,9 @@ const {
|
|
|
79
79
|
runId,
|
|
80
80
|
cellRuns: new Map([
|
|
81
81
|
[
|
|
82
|
-
cellNotification.cell_id
|
|
82
|
+
cellNotification.cell_id,
|
|
83
83
|
{
|
|
84
|
-
cellId: cellNotification.cell_id
|
|
84
|
+
cellId: cellNotification.cell_id,
|
|
85
85
|
code: code.slice(0, MAX_CODE_LENGTH),
|
|
86
86
|
elapsedTime: 0,
|
|
87
87
|
status: status,
|
|
@@ -111,9 +111,7 @@ const {
|
|
|
111
111
|
|
|
112
112
|
// Update existing run
|
|
113
113
|
const nextCellRuns = new Map(existingRun.cellRuns);
|
|
114
|
-
const existingCellRun = nextCellRuns.get(
|
|
115
|
-
cellNotification.cell_id as CellId,
|
|
116
|
-
);
|
|
114
|
+
const existingCellRun = nextCellRuns.get(cellNotification.cell_id);
|
|
117
115
|
|
|
118
116
|
// Early return if nothing changed
|
|
119
117
|
if (
|
|
@@ -140,15 +138,15 @@ const {
|
|
|
140
138
|
? timestamp - existingCellRun.startTime
|
|
141
139
|
: undefined;
|
|
142
140
|
|
|
143
|
-
nextCellRuns.set(cellNotification.cell_id
|
|
141
|
+
nextCellRuns.set(cellNotification.cell_id, {
|
|
144
142
|
...existingCellRun,
|
|
145
143
|
startTime,
|
|
146
144
|
elapsedTime,
|
|
147
145
|
status,
|
|
148
146
|
});
|
|
149
147
|
} else {
|
|
150
|
-
nextCellRuns.set(cellNotification.cell_id
|
|
151
|
-
cellId: cellNotification.cell_id
|
|
148
|
+
nextCellRuns.set(cellNotification.cell_id, {
|
|
149
|
+
cellId: cellNotification.cell_id,
|
|
152
150
|
code: code.slice(0, MAX_CODE_LENGTH),
|
|
153
151
|
elapsedTime: 0,
|
|
154
152
|
status: status,
|