@marimo-team/islands 0.25.3-dev209 → 0.25.3-dev210
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/{ConnectedDataExplorerComponent-ApTYh0V3.js → ConnectedDataExplorerComponent-D6RHJV8D.js} +114 -112
- package/dist/{ErrorBoundary-B1QlXN-y.js → ErrorBoundary-VruaQLnK.js} +10 -10
- package/dist/{abnfDiagram-VCTEODGH-uo9xhZY0.js → abnfDiagram-VCTEODGH-0q7Lbmii.js} +2 -2
- package/dist/{any-language-editor-BVFHDDQ3.js → any-language-editor-jGaR0EAo.js} +5 -5
- package/dist/{architectureDiagram-5GKGNRK7-Hz6eyeOD.js → architectureDiagram-5GKGNRK7-1PydKPmE.js} +1 -1
- package/dist/{button-BQRdqQi1.js → button-B6RjDKJx.js} +54 -51
- package/dist/{capabilities-C2QUNRXi.js → capabilities-CkVzWJEX.js} +1 -1
- package/dist/{chat-ui-BTA1I5i1.js → chat-ui-B5pH_aXk.js} +414 -413
- package/dist/{check-C9Mp9eCK.js → check-Z_vtI3OL.js} +1 -1
- package/dist/{chunk-IMKFNOWR-DBM2i23k.js → chunk-IMKFNOWR-DEzZZ1Zc.js} +1 -1
- package/dist/{chunk-SHT3W25Y-L8KQLLBy.js → chunk-SHT3W25Y-CuHj-hnn.js} +1 -1
- package/dist/{chunk-SVP7TREG-BOUJKJEP.js → chunk-SVP7TREG-BPiZK2bA.js} +1 -1
- package/dist/{chunk-TICWLB2K-DY3CzFjX.js → chunk-TICWLB2K-DF6XcIjM.js} +1 -1
- package/dist/{classDiagram-ZZMXUADV-s9iwW-q4.js → classDiagram-ZZMXUADV-CMdR-cP4.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-2GI5CjGB.js → classDiagram-v2-VYDZK3BY-DRvc3_7Y.js} +2 -2
- package/dist/{code-visibility-CCGlDSq4.js → code-visibility-BdFhHfIC.js} +28 -28
- package/dist/{common-C1PmVtVW.js → common-Ce1SgsX1.js} +8034 -8027
- package/dist/{toDate-BZZ64K_m.js → config-DbjzjwxX.js} +98 -164
- package/dist/{copy-icon-De584pZ4.js → copy-icon-CG57cxwb.js} +3 -3
- package/dist/createReducer-DePYJ0F3.js +63 -0
- package/dist/{cynefinDiagram-5FMLGOSQ-DOxnhBK2.js → cynefinDiagram-5FMLGOSQ-BkBjEY9l.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-D1AhEfpM.js → dagre-GXQ25YYZ-D-HEaW4I.js} +1 -1
- package/dist/{data-grid-overlay-editor-B3KXkZvo.js → data-grid-overlay-editor-Bps1p6jQ.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-DV_GebF_.js → diagram-S7CK7UJ4-Bn-oF-rB.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-6dNzGFfD.js → diagram-UQ7AKVKN-BpzzXWBj.js} +1 -1
- package/dist/{diagram-VX7I27RA-DUh9jsfi.js → diagram-VX7I27RA-COOtvfBP.js} +1 -1
- package/dist/{diagram-Z3DM3KII-Cug-qcQ8.js → diagram-Z3DM3KII-K5cXvbrl.js} +1 -1
- package/dist/{dialog-DVRfQuhD.js → dialog-DsZ49mmU.js} +26 -26
- package/dist/{dist-BJtmSgcE.js → dist-11T5wq3_.js} +41 -40
- package/dist/{dist-B1M0bGO3.js → dist-17ugwGZc.js} +59 -58
- package/dist/{dist-D7mrh5sO.js → dist-7cRjf--y.js} +135 -134
- package/dist/{dist-Ck_C32ye.js → dist-B8pkfkzc.js} +31 -30
- package/dist/{dist-Ba2VIn1K.js → dist-B9UoM_wq.js} +54 -53
- package/dist/{dist-KN80qWCP.js → dist-BGuMTs1i.js} +118 -117
- package/dist/{dist-T5rh6JvL.js → dist-BHW11fni.js} +18 -17
- package/dist/dist-BLY4m1Wi.js +850 -0
- package/dist/{dist-Bj4bmwUm.js → dist-BNlcVoxs.js} +31 -30
- package/dist/{dist-BA13RJj5.js → dist-BQYe5Bju.js} +47 -46
- package/dist/{dist-OUle2RT7.js → dist-BX1mn8na.js} +297 -297
- package/dist/{dist-CTi5FTiV.js → dist-Bh0PTKUW.js} +108 -107
- package/dist/{dist-D9Q3xjiP.js → dist-CKaXK92p.js} +56 -55
- package/dist/{dist-DYXM73-k.js → dist-CLhwfhpJ.js} +58 -57
- package/dist/{dist-BmS-ZGjl.js → dist-CYeqK4Xw.js} +1854 -2702
- package/dist/{dist-D1fnFp1J.js → dist-Cxlp3WH6.js} +16 -15
- package/dist/{dist-DVVwOfrD.js → dist-DSJPGj42.js} +64 -63
- package/dist/{dist-Bzogh7CW.js → dist-DU9ICsWZ.js} +93 -92
- package/dist/{dist-DjeybPOb.js → dist-DWv0ALBi.js} +47 -46
- package/dist/{dist-D3x9__Fu.js → dist-DiBpWEsB.js} +39 -38
- package/dist/{dist-DtGn9N4h.js → dist-DiLFTlnl.js} +165 -165
- package/dist/{dist-DTv9KkpW.js → dist-cVDV5IuI.js} +26 -25
- package/dist/{dist-DP60Ubh8.js → dist-fRfaOoq8.js} +44 -43
- package/dist/{dist-O0qFpEJh.js → dist-gTTI-9Px.js} +66 -65
- package/dist/{dropdown-menu-dlEm41Bt.js → dropdown-menu-CSNdPSnS.js} +2886 -1529
- package/dist/{ebnfDiagram-PWID7BFC-d5LKx7Vb.js → ebnfDiagram-PWID7BFC-BI1ZmjVm.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-BwvlkcID.js → erDiagram-RLTQ6QDP-BtWjj9xy.js} +1 -1
- package/dist/{error-banner-DxJ3s8YO.js → error-banner-IsonnvA0.js} +47 -46
- package/dist/error-entries-CRPfxMqm.js +2505 -0
- package/dist/{esm-Bbw_Njjx.js → esm-BgKjMvWw.js} +389 -388
- package/dist/{esm-mDQNEFe_.js → esm-CHjVSexr.js} +305 -306
- package/dist/{feedback-button-UsovT9sL.js → feedback-button-2ycmR9GG.js} +112 -112
- package/dist/{flowDiagram-HODETNUW-CJcSoFer.js → flowDiagram-HODETNUW-DQvZh96A.js} +2 -2
- package/dist/{formats-Ry7c-aIV.js → formats-BK4VefeF.js} +6 -6
- package/dist/{glide-data-editor-CRd8kBSp.js → glide-data-editor-Buj2OZjL.js} +99 -99
- package/dist/{glide-utils-D98laidb.js → glide-utils-CQddGpa-.js} +3 -3
- package/dist/{globals-BDWrOstF.js → globals-B-14fUeb.js} +3 -3
- package/dist/{error-entries-CVUHA5r_.js → html-to-image-CgAnxFi8.js} +26314 -25474
- package/dist/{infoDiagram-27XIBGKW-Cezgppw3.js → infoDiagram-27XIBGKW-BdAObnc_.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-BahZQwJz.js → ishikawaDiagram-5VMMS53U-DP-jE8dw.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-Cvfn_Obm.js → kanban-definition-UXKFOSKX-BkMgPjfm.js} +1 -1
- package/dist/{loader-DOVB0Syl.js → loader-BisjOLiW.js} +1 -1
- package/dist/main.js +2996 -2992
- package/dist/{mermaid-BrdwbArF.js → mermaid-B-OFqheA.js} +36 -36
- package/dist/{mindmap-definition-YA3MSWOX-uB4B2d-J.js → mindmap-definition-YA3MSWOX-Du7toAZX.js} +1 -1
- package/dist/{number-overlay-editor-Bp9a4z5Q.js → number-overlay-editor-ZVFcJ7_u.js} +1 -1
- package/dist/{dist-CThPVKaP.js → objectWithoutPropertiesLoose-ai5NUqGa.js} +1139 -1120
- package/dist/{pegDiagram-XKGWAZYB-CYaIwAmH.js → pegDiagram-XKGWAZYB-CX_729j0.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-BJ1oUxVv.js → pieDiagram-E7YTZNPT-dtTFJcVI.js} +1 -1
- package/dist/{process-output-SjJxB1wh.js → process-output-Bmev5azU.js} +5 -5
- package/dist/{railroadDiagram-O6MQD6OU-BqKSaBux.js → railroadDiagram-O6MQD6OU-Dsc2Cf4c.js} +2 -2
- package/dist/{requirementDiagram-BXWQKSXE-DwFhgDhH.js → requirementDiagram-BXWQKSXE-D3g0c60I.js} +1 -1
- package/dist/{reveal-component-ov3pS-oO.js → reveal-component-BWz1KjWe.js} +725 -725
- package/dist/schemas-CdscWNy_.js +4747 -0
- package/dist/select-BSxlITBo.js +1200 -0
- package/dist/{sequenceDiagram-WJ2MYXX4-L7ENjfKF.js → sequenceDiagram-WJ2MYXX4-w1pWbIpk.js} +1 -1
- package/dist/{spec-BcJ9ncAY.js → spec-Dq8RNplp.js} +4 -4
- package/dist/{stateDiagram-D77RDMKH-CVf88u-p.js → stateDiagram-D77RDMKH-BCv7kP4H.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-DvpkQ1V6.js → stateDiagram-v2-MP3YSRHH-D1cTOt0x.js} +2 -2
- package/dist/static-state-BldGd99T.js +72 -0
- package/dist/strings-BGW-fXVJ.js +507 -0
- package/dist/{swimlanes-42K2YHIH-DIuFHbVE.js → swimlanes-42K2YHIH-DNO6jDnd.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-CAqHAziW.js → swimlanesDiagram-VR7AAH4N-CkSWsxYJ.js} +2 -2
- package/dist/{swiper-component-BVfnhbnt.js → swiper-component-Dir1o8ml.js} +2 -2
- package/dist/{timeline-definition-24CTP7MA-xO4EQqBm.js → timeline-definition-24CTP7MA-DQvqGFM9.js} +1 -1
- package/dist/toast-BWIPi3aF.js +491 -0
- package/dist/{tooltip-s4zZxwKe.js → tooltip-C3UnvsRe.js} +58 -58
- package/dist/use-restore-focus-BWO0z1lo.js +1672 -0
- package/dist/{useAsyncData-Dqw22wZQ.js → useAsyncData-DsZMvNXn.js} +1 -1
- package/dist/{useDateFormatter-D3Tkfzej.js → useDateFormatter-ChB4eyim.js} +2 -2
- package/dist/{useDeepCompareMemoize-D3gozcjJ.js → useDeepCompareMemoize-KMBBux8C.js} +1 -1
- package/dist/{useIframeCapabilities-6a_yDNXp.js → useIframeCapabilities-cMNQ80CK.js} +1 -1
- package/dist/useLifecycle-BquH9ET-.js +112 -0
- package/dist/useTheme-B9C1JIy9.js +307 -0
- package/dist/utils-DXkj82sK.js +813 -0
- package/dist/{vega-component-Blvspl7v.js → vega-component-9NOEITbg.js} +55 -54
- package/dist/{vennDiagram-4TSXK5OY-DzCi-lRh.js → vennDiagram-4TSXK5OY-BfzXp4SH.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-CW48xa0n.js → wardleyDiagram-VM6X3IG4-CiqEE8eq.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-UPxGGI2B.js → xychartDiagram-S5SC5T6Z-BTIHCVOI.js} +1 -1
- package/package.json +1 -1
- package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +1 -1
- package/src/components/editor/cell/code/cell-editor.tsx +13 -36
- package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +1 -1
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +11 -5
- package/src/components/editor/notebook-cell.tsx +0 -1
- package/src/components/scratchpad/scratchpad.test.tsx +3 -11
- package/src/components/scratchpad/scratchpad.tsx +24 -22
- package/src/components/scratchpad/state.ts +7 -0
- package/src/core/ai/__tests__/staged-cells.test.ts +6 -8
- package/src/core/ai/staged-cells.ts +4 -2
- package/src/core/cells/__tests__/cells.test.ts +44 -60
- package/src/core/cells/__tests__/document.test.ts +1 -139
- package/src/core/cells/__tests__/notebook-outline.test.ts +8 -6
- package/src/core/cells/actions.ts +39 -0
- package/src/core/cells/cells.ts +15 -35
- package/src/core/cells/document.ts +9 -84
- package/src/core/codemirror/__tests__/format.test.ts +0 -34
- package/src/core/codemirror/cells/extensions.ts +20 -18
- package/src/core/codemirror/cells/state.ts +0 -5
- package/src/core/codemirror/format.ts +1 -19
- package/src/core/codemirror/misc/__tests__/paste.test.ts +3 -1
- package/src/core/codemirror/misc/paste.ts +1 -1
- package/src/core/notebook/document/__tests__/codemirror.test.ts +172 -0
- package/src/stories/layout/vertical/one-column.stories.tsx +26 -18
- package/dist/html-to-image--E3uFbJ9.js +0 -3348
- package/dist/label-Qvw1E97W.js +0 -281
- package/dist/objectWithoutPropertiesLoose-BRDcTsfB.js +0 -23
- package/dist/select-2KOiReNY.js +0 -1423
- package/dist/toast-B5t9o6s_.js +0 -1848
- package/dist/use-restore-focus-BgDTd2tc.js +0 -6418
- package/dist/useLifecycle-CaZi4wTw.js +0 -172
- package/dist/useTheme-TzPgWXwB.js +0 -1114
|
@@ -95,7 +95,7 @@ interface UpdateStagedCellAction {
|
|
|
95
95
|
/** Manage staged cells without assuming which AI workflow owns them. */
|
|
96
96
|
export function useStagedCells(store: JotaiStore) {
|
|
97
97
|
const { addStagedCell, removeStagedCell } = useStagedAICellsActions();
|
|
98
|
-
const {
|
|
98
|
+
const { setCellCode, createCell } = useCellActions();
|
|
99
99
|
const deleteCellCallback = useDeleteCellCallback();
|
|
100
100
|
|
|
101
101
|
const createStagedCell = (code: string): CellId => {
|
|
@@ -114,6 +114,8 @@ export function useStagedCells(store: JotaiStore) {
|
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
// The AI may change the cell's language, and only the editor infers
|
|
118
|
+
// one. Without a mounted editor the code arrives through the document.
|
|
117
119
|
const editorView = getCellEditorView(cellId);
|
|
118
120
|
if (editorView) {
|
|
119
121
|
const update = prepareCellCodeUpdate(editorView.state, code, {
|
|
@@ -123,7 +125,7 @@ export function useStagedCells(store: JotaiStore) {
|
|
|
123
125
|
editorView.dispatch(update);
|
|
124
126
|
}
|
|
125
127
|
} else {
|
|
126
|
-
|
|
128
|
+
setCellCode({ cellId, code });
|
|
127
129
|
}
|
|
128
130
|
};
|
|
129
131
|
|
|
@@ -178,6 +178,38 @@ describe("cell reducer", () => {
|
|
|
178
178
|
return id;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
/** Edit a cell's code as the document delivers it. */
|
|
182
|
+
function editCode(cellId: CellId, code: string) {
|
|
183
|
+
const cells = state.cellIds.map((id) => {
|
|
184
|
+
const cell = state.cellData[id];
|
|
185
|
+
return {
|
|
186
|
+
id,
|
|
187
|
+
code: id === cellId ? code : cell.code,
|
|
188
|
+
name: cell.name,
|
|
189
|
+
config: {
|
|
190
|
+
column: cell.config.column ?? null,
|
|
191
|
+
disabled: cell.config.disabled ?? false,
|
|
192
|
+
hide_code: cell.config.hide_code ?? false,
|
|
193
|
+
expand_output: cell.config.expand_output ?? false,
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
});
|
|
197
|
+
actions.reconcileDocument({
|
|
198
|
+
order: state.cellIds,
|
|
199
|
+
cells: new Map(cells.map((cell) => [cell.id, cell])),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Format a cell's code, as the editor reports it. */
|
|
204
|
+
function formatCode(cellId: CellId, code: string) {
|
|
205
|
+
actions.codeFormatted({
|
|
206
|
+
cellId,
|
|
207
|
+
before: state.cellData[cellId].code,
|
|
208
|
+
after: code,
|
|
209
|
+
});
|
|
210
|
+
editCode(cellId, code);
|
|
211
|
+
}
|
|
212
|
+
|
|
181
213
|
let i = 0;
|
|
182
214
|
const originalCreate = CellId.create.bind(CellId);
|
|
183
215
|
|
|
@@ -200,11 +232,7 @@ describe("cell reducer", () => {
|
|
|
200
232
|
});
|
|
201
233
|
|
|
202
234
|
it("can update a cell", () => {
|
|
203
|
-
|
|
204
|
-
cellId: firstCellId,
|
|
205
|
-
code: "import numpy as np",
|
|
206
|
-
formattingChange: false,
|
|
207
|
-
});
|
|
235
|
+
editCode(firstCellId, "import numpy as np");
|
|
208
236
|
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
209
237
|
"
|
|
210
238
|
[0] 'import numpy as np'
|
|
@@ -243,11 +271,7 @@ describe("cell reducer", () => {
|
|
|
243
271
|
stale_inputs: null,
|
|
244
272
|
timestamp: 1,
|
|
245
273
|
});
|
|
246
|
-
|
|
247
|
-
cellId: firstCellId,
|
|
248
|
-
code: "print('new code')",
|
|
249
|
-
formattingChange: false,
|
|
250
|
-
});
|
|
274
|
+
editCode(firstCellId, "print('new code')");
|
|
251
275
|
actions.prepareForRun({ cellId: firstCellId });
|
|
252
276
|
actions.handleCellMessage({
|
|
253
277
|
cell_id: firstCellId,
|
|
@@ -275,11 +299,7 @@ describe("cell reducer", () => {
|
|
|
275
299
|
expect(cell).toMatchSnapshot("initial state");
|
|
276
300
|
|
|
277
301
|
// Update code
|
|
278
|
-
|
|
279
|
-
cellId: firstCellId,
|
|
280
|
-
code: "import marimo as mo",
|
|
281
|
-
formattingChange: false,
|
|
282
|
-
});
|
|
302
|
+
editCode(firstCellId, "import marimo as mo");
|
|
283
303
|
cell = cells[0];
|
|
284
304
|
expect(cell.status).toBe("idle");
|
|
285
305
|
expect(cell.lastCodeRun).toBe(null);
|
|
@@ -384,32 +404,20 @@ describe("cell reducer", () => {
|
|
|
384
404
|
// EDITING BACK AND FORTH
|
|
385
405
|
/////////////////
|
|
386
406
|
// Update code again
|
|
387
|
-
|
|
388
|
-
cellId: firstCellId,
|
|
389
|
-
code: "import marimo as mo\nimport numpy",
|
|
390
|
-
formattingChange: false,
|
|
391
|
-
});
|
|
407
|
+
editCode(firstCellId, "import marimo as mo\nimport numpy");
|
|
392
408
|
cell = cells[0];
|
|
393
409
|
expect(cell.status).toBe("idle");
|
|
394
410
|
expect(cell.edited).toBe(true);
|
|
395
411
|
expect(cell).toMatchSnapshot("code edited again");
|
|
396
412
|
|
|
397
413
|
// Update code should be unedited
|
|
398
|
-
|
|
399
|
-
cellId: firstCellId,
|
|
400
|
-
code: "import marimo as mo",
|
|
401
|
-
formattingChange: false,
|
|
402
|
-
});
|
|
414
|
+
editCode(firstCellId, "import marimo as mo");
|
|
403
415
|
cell = cells[0];
|
|
404
416
|
expect(cell.edited).toBe(false);
|
|
405
417
|
expect(cell).toMatchSnapshot("code reverted");
|
|
406
418
|
|
|
407
419
|
// Update code should be edited again
|
|
408
|
-
|
|
409
|
-
cellId: firstCellId,
|
|
410
|
-
code: "import marimo as mo\nimport numpy",
|
|
411
|
-
formattingChange: false,
|
|
412
|
-
});
|
|
420
|
+
editCode(firstCellId, "import marimo as mo\nimport numpy");
|
|
413
421
|
cell = cells[0];
|
|
414
422
|
expect(cell.status).toBe("idle");
|
|
415
423
|
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
@@ -533,11 +541,7 @@ describe("cell reducer", () => {
|
|
|
533
541
|
expect(cell).toMatchSnapshot("initial state");
|
|
534
542
|
|
|
535
543
|
// Update code
|
|
536
|
-
|
|
537
|
-
cellId: firstCellId,
|
|
538
|
-
code: "import marimo as mo",
|
|
539
|
-
formattingChange: false,
|
|
540
|
-
});
|
|
544
|
+
editCode(firstCellId, "import marimo as mo");
|
|
541
545
|
cell = cells[0];
|
|
542
546
|
expect(cell.status).toBe("idle");
|
|
543
547
|
expect(cell.lastCodeRun).toBe(null);
|
|
@@ -589,11 +593,7 @@ describe("cell reducer", () => {
|
|
|
589
593
|
|
|
590
594
|
it("can run a stale cell", () => {
|
|
591
595
|
// Update code of first cell
|
|
592
|
-
|
|
593
|
-
cellId: firstCellId,
|
|
594
|
-
code: "import marimo as mo",
|
|
595
|
-
formattingChange: false,
|
|
596
|
-
});
|
|
596
|
+
editCode(firstCellId, "import marimo as mo");
|
|
597
597
|
// Add cell
|
|
598
598
|
seedCell({
|
|
599
599
|
cellId: firstCellId,
|
|
@@ -601,11 +601,7 @@ describe("cell reducer", () => {
|
|
|
601
601
|
});
|
|
602
602
|
const secondCell = cells[1].id;
|
|
603
603
|
// Update code
|
|
604
|
-
|
|
605
|
-
code: "mo.slider()",
|
|
606
|
-
cellId: secondCell,
|
|
607
|
-
formattingChange: false,
|
|
608
|
-
});
|
|
604
|
+
editCode(secondCell, "mo.slider()");
|
|
609
605
|
|
|
610
606
|
// Prepare for run
|
|
611
607
|
actions.prepareForRun({
|
|
@@ -649,11 +645,7 @@ describe("cell reducer", () => {
|
|
|
649
645
|
|
|
650
646
|
it("can format code and update cell", () => {
|
|
651
647
|
const firstCellId = cells[0].id;
|
|
652
|
-
|
|
653
|
-
cellId: firstCellId,
|
|
654
|
-
code: "import marimo as mo",
|
|
655
|
-
formattingChange: false,
|
|
656
|
-
});
|
|
648
|
+
editCode(firstCellId, "import marimo as mo");
|
|
657
649
|
let cell = cells[0];
|
|
658
650
|
expect(cell.status).toBe("idle");
|
|
659
651
|
expect(cell.lastCodeRun).toBe(null);
|
|
@@ -689,11 +681,7 @@ describe("cell reducer", () => {
|
|
|
689
681
|
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
690
682
|
|
|
691
683
|
// Format code
|
|
692
|
-
|
|
693
|
-
cellId: firstCellId,
|
|
694
|
-
code: "import marimo as mo",
|
|
695
|
-
formattingChange: true,
|
|
696
|
-
});
|
|
684
|
+
formatCode(firstCellId, "import marimo as mo");
|
|
697
685
|
cell = cells[0];
|
|
698
686
|
expect(cell.status).toBe("idle");
|
|
699
687
|
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
@@ -702,11 +690,7 @@ describe("cell reducer", () => {
|
|
|
702
690
|
|
|
703
691
|
it("can run a stopped cell", () => {
|
|
704
692
|
// Update code of first cell
|
|
705
|
-
|
|
706
|
-
cellId: firstCellId,
|
|
707
|
-
code: "mo.md('This has an ancestor that was stopped')",
|
|
708
|
-
formattingChange: false,
|
|
709
|
-
});
|
|
693
|
+
editCode(firstCellId, "mo.md('This has an ancestor that was stopped')");
|
|
710
694
|
|
|
711
695
|
// Prepare for run
|
|
712
696
|
actions.prepareForRun({
|
|
@@ -1,32 +1,17 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import { python } from "@codemirror/lang-python";
|
|
4
|
-
import { EditorState } from "@codemirror/state";
|
|
5
|
-
import { EditorView } from "@codemirror/view";
|
|
6
3
|
import { atom } from "jotai";
|
|
7
4
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
8
5
|
import { cellId } from "@/__tests__/branded";
|
|
9
6
|
import type {
|
|
10
|
-
DocumentEdit,
|
|
11
|
-
DocumentEditor,
|
|
12
7
|
DocumentModel,
|
|
13
8
|
DocumentSyncStatus,
|
|
14
9
|
EditableCell,
|
|
15
10
|
EditableDocument,
|
|
16
11
|
} from "@/core/notebook/document";
|
|
17
|
-
import {
|
|
18
|
-
adaptiveLanguageConfiguration,
|
|
19
|
-
languageAdapterState,
|
|
20
|
-
} from "@/core/codemirror/language/extension";
|
|
21
|
-
import { OverridingHotkeyProvider } from "@/core/hotkeys/hotkeys";
|
|
22
12
|
import { store } from "@/core/state/jotai";
|
|
23
13
|
import { activeDocumentModelAtom } from "@/core/session/active-notebook";
|
|
24
|
-
import {
|
|
25
|
-
createNotebookActions,
|
|
26
|
-
exportedForTesting,
|
|
27
|
-
type NotebookState,
|
|
28
|
-
notebookReducer,
|
|
29
|
-
} from "../cells";
|
|
14
|
+
import { exportedForTesting, type NotebookState } from "../cells";
|
|
30
15
|
import { applyDocument, flushDocument, followDocument } from "../document";
|
|
31
16
|
import { type CellId, SCRATCH_CELL_ID } from "../ids";
|
|
32
17
|
|
|
@@ -58,29 +43,6 @@ function documentOf(cells: EditableCell[]): EditableDocument {
|
|
|
58
43
|
};
|
|
59
44
|
}
|
|
60
45
|
|
|
61
|
-
/** A mounted cell editor, as the notebook's cells have. */
|
|
62
|
-
function createEditor(content: string) {
|
|
63
|
-
const state = EditorState.create({
|
|
64
|
-
doc: content,
|
|
65
|
-
extensions: [
|
|
66
|
-
python(),
|
|
67
|
-
adaptiveLanguageConfiguration({
|
|
68
|
-
cellId: a,
|
|
69
|
-
completionConfig: {
|
|
70
|
-
activate_on_typing: true,
|
|
71
|
-
signature_hint_on_typing: false,
|
|
72
|
-
copilot: false,
|
|
73
|
-
codeium_api_key: null,
|
|
74
|
-
},
|
|
75
|
-
hotkeys: new OverridingHotkeyProvider({}),
|
|
76
|
-
placeholderType: "marimo-import",
|
|
77
|
-
lspConfig: {},
|
|
78
|
-
}),
|
|
79
|
-
],
|
|
80
|
-
});
|
|
81
|
-
return new EditorView({ state, parent: globalThis.document.body });
|
|
82
|
-
}
|
|
83
|
-
|
|
84
46
|
/** A notebook that follows `cells`, as the session builds one. */
|
|
85
47
|
function notebookOf(cells: EditableCell[]): NotebookState {
|
|
86
48
|
return applyDocument(initialNotebookState(), documentOf(cells));
|
|
@@ -110,38 +72,6 @@ describe("applyDocument", () => {
|
|
|
110
72
|
expect(applyDocument(state, documentOf(cells))).toBe(state);
|
|
111
73
|
});
|
|
112
74
|
|
|
113
|
-
it("brings a code change into the cell's editor", () => {
|
|
114
|
-
const state = notebookOf([cell(a), cell(b)]);
|
|
115
|
-
const view = createEditor("b = 1");
|
|
116
|
-
state.cellHandles[b].current = { editorView: view, editorViewOrNull: view };
|
|
117
|
-
state.cellData[b] = { ...state.cellData[b], lastCodeRun: "b = 1" };
|
|
118
|
-
|
|
119
|
-
const next = applyDocument(
|
|
120
|
-
state,
|
|
121
|
-
documentOf([cell(a), cell(b, { code: "b = 2" })]),
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
expect(next.cellData[b]).toMatchObject({ code: "b = 2", edited: true });
|
|
125
|
-
expect(next.cellData[a]).toBe(state.cellData[a]);
|
|
126
|
-
expect(view.state.doc.toString()).toBe("b = 2");
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it("leaves an editor that already shows the document's code alone", () => {
|
|
130
|
-
const state = notebookOf([cell(a)]);
|
|
131
|
-
// The editor runs ahead of the notebook while this client types.
|
|
132
|
-
const view = createEditor('mo.md("hi")');
|
|
133
|
-
state.cellHandles[a].current = { editorView: view, editorViewOrNull: view };
|
|
134
|
-
|
|
135
|
-
const next = applyDocument(
|
|
136
|
-
state,
|
|
137
|
-
documentOf([cell(a, { code: 'mo.md("hi")' })]),
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
expect(next.cellData[a].code).toBe('mo.md("hi")');
|
|
141
|
-
expect(view.state.doc.toString()).toBe('mo.md("hi")');
|
|
142
|
-
expect(view.state.field(languageAdapterState).type).toBe("python");
|
|
143
|
-
});
|
|
144
|
-
|
|
145
75
|
it("keeps the code this client has when the host withholds source", () => {
|
|
146
76
|
const state = notebookOf([cell(a)]);
|
|
147
77
|
|
|
@@ -182,74 +112,6 @@ describe("applyDocument", () => {
|
|
|
182
112
|
});
|
|
183
113
|
});
|
|
184
114
|
|
|
185
|
-
describe("documentEditMiddleware", () => {
|
|
186
|
-
type Call = [string, ...unknown[]];
|
|
187
|
-
let calls: Call[];
|
|
188
|
-
let state: NotebookState;
|
|
189
|
-
const editor: DocumentEditor = {
|
|
190
|
-
setCode: (id, code) => calls.push(["setCode", id, code]),
|
|
191
|
-
setName: (id, name) => calls.push(["setName", id, name]),
|
|
192
|
-
updateConfig: (id, patch) => calls.push(["updateConfig", id, patch]),
|
|
193
|
-
createCell: (cell, placement) =>
|
|
194
|
-
calls.push(["createCell", cell, placement]),
|
|
195
|
-
deleteCell: (id) => calls.push(["deleteCell", id]),
|
|
196
|
-
setOrder: (order) => calls.push(["setOrder", order]),
|
|
197
|
-
};
|
|
198
|
-
const model: DocumentModel = {
|
|
199
|
-
documentAtom: atom(null),
|
|
200
|
-
editAtom: atom(null, (_get, _set, edit: DocumentEdit) => edit(editor)),
|
|
201
|
-
syncStatusAtom: atom<DocumentSyncStatus>({ status: "idle" }),
|
|
202
|
-
flush: vi.fn(),
|
|
203
|
-
retry: vi.fn(),
|
|
204
|
-
start: vi.fn(),
|
|
205
|
-
};
|
|
206
|
-
const actions = createNotebookActions((action) => {
|
|
207
|
-
state = notebookReducer(state, action);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
beforeEach(() => {
|
|
211
|
-
calls = [];
|
|
212
|
-
state = notebookOf([cell(a), cell(b), cell(c)]);
|
|
213
|
-
store.set(activeDocumentModelAtom, model);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
afterEach(() => {
|
|
217
|
-
store.set(activeDocumentModelAtom, null);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it("writes code", () => {
|
|
221
|
-
actions.updateCellCode({
|
|
222
|
-
cellId: a,
|
|
223
|
-
code: "a = 2",
|
|
224
|
-
formattingChange: false,
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
expect(calls).toEqual([["setCode", a, "a = 2"]]);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it("ignores the document and the kernel's run state", () => {
|
|
231
|
-
actions.prepareForRun({ cellId: a });
|
|
232
|
-
actions.setLastRun({
|
|
233
|
-
lastCodeRun: { [a]: "a = 0" },
|
|
234
|
-
lastExecutionTime: {},
|
|
235
|
-
});
|
|
236
|
-
actions.resetCellRuntimes();
|
|
237
|
-
actions.reconcileDocument(documentOf([cell(a)]));
|
|
238
|
-
|
|
239
|
-
expect(calls).toEqual([]);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it("leaves the scratchpad alone", () => {
|
|
243
|
-
actions.updateCellCode({
|
|
244
|
-
cellId: SCRATCH_CELL_ID,
|
|
245
|
-
code: "scratch",
|
|
246
|
-
formattingChange: false,
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
expect(calls).toEqual([]);
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
|
|
253
115
|
describe("followDocument", () => {
|
|
254
116
|
it("reconciles the document now and on each change, until aborted", () => {
|
|
255
117
|
const documentAtom = atom<EditableDocument | null>(null);
|
|
@@ -42,16 +42,18 @@ function setup() {
|
|
|
42
42
|
|
|
43
43
|
describe("notebookOutline", () => {
|
|
44
44
|
it("does not notify outline subscribers when cell code changes", () => {
|
|
45
|
-
const { store
|
|
45
|
+
const { store } = setup();
|
|
46
46
|
const initial = store.get(notebookOutline);
|
|
47
47
|
const listener = vi.fn();
|
|
48
48
|
const unsubscribe = store.sub(notebookOutline, listener);
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
store.set(notebookAtom, (state) => ({
|
|
51
|
+
...state,
|
|
52
|
+
cellData: {
|
|
53
|
+
...state.cellData,
|
|
54
|
+
[first]: { ...state.cellData[first], code: "x = 2" },
|
|
55
|
+
},
|
|
56
|
+
}));
|
|
55
57
|
|
|
56
58
|
expect(store.get(notebookOutline)).toBe(initial);
|
|
57
59
|
expect(listener).not.toHaveBeenCalled();
|
|
@@ -159,6 +159,20 @@ export const createCellAtom = atom(
|
|
|
159
159
|
},
|
|
160
160
|
);
|
|
161
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Write a cell's code, or nothing for a cell the document no longer has.
|
|
164
|
+
* A mounted editor follows the document.
|
|
165
|
+
*/
|
|
166
|
+
export const setCellCodeAtom = atom(
|
|
167
|
+
null,
|
|
168
|
+
(get, set, { cellId, code }: { cellId: CellId; code: string }): void => {
|
|
169
|
+
if (!documentOf(get).cells.has(cellId)) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
set(modelOf(get).editAtom, (tx) => tx.setCode(cellId, code));
|
|
173
|
+
},
|
|
174
|
+
);
|
|
175
|
+
|
|
162
176
|
/**
|
|
163
177
|
* Split a cell at its cursor. The text before stays, the text after
|
|
164
178
|
* becomes a new cell below, which is focused. Returns the new cell's id,
|
|
@@ -398,6 +412,27 @@ export const addSetupCellAtom = atom(
|
|
|
398
412
|
},
|
|
399
413
|
);
|
|
400
414
|
|
|
415
|
+
/**
|
|
416
|
+
* Create the setup cell with `code`, or append `code` to the one that exists.
|
|
417
|
+
*/
|
|
418
|
+
export const addOrAppendSetupCellAtom = atom(
|
|
419
|
+
null,
|
|
420
|
+
(get, set, { code }: { code: string }): void => {
|
|
421
|
+
const setup = documentOf(get).cells.get(SETUP_CELL_ID);
|
|
422
|
+
if (setup === undefined) {
|
|
423
|
+
set(addSetupCellAtom, { code });
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
// Withheld source keeps the code this client has.
|
|
427
|
+
const existing =
|
|
428
|
+
setup.code ?? get(notebookAtom).cellData[SETUP_CELL_ID]?.code ?? "";
|
|
429
|
+
set(setCellCodeAtom, {
|
|
430
|
+
cellId: SETUP_CELL_ID,
|
|
431
|
+
code: existing.trim() ? `${existing.trimEnd()}\n\n${code}` : code,
|
|
432
|
+
});
|
|
433
|
+
},
|
|
434
|
+
);
|
|
435
|
+
|
|
401
436
|
/**
|
|
402
437
|
* Delete cells, keeping them in the history for undo. The notebook keeps at
|
|
403
438
|
* least one cell. Returns false when nothing was deleted.
|
|
@@ -574,6 +609,7 @@ export function useCellActions() {
|
|
|
574
609
|
const createCell = useSetAtom(createCellAtom);
|
|
575
610
|
const moveToNextCell = useSetAtom(moveToNextCellAtom);
|
|
576
611
|
const addSetupCell = useSetAtom(addSetupCellAtom);
|
|
612
|
+
const addOrAppendSetupCell = useSetAtom(addOrAppendSetupCellAtom);
|
|
577
613
|
const deleteCells = useSetAtom(deleteCellsAtom);
|
|
578
614
|
const undoDeleteCell = useSetAtom(undoDeleteCellAtom);
|
|
579
615
|
const focusCell = useSetAtom(focusCellAtom);
|
|
@@ -597,11 +633,13 @@ export function useCellActions() {
|
|
|
597
633
|
const mergeAllColumns = useSetAtom(mergeAllColumnsAtom);
|
|
598
634
|
const compactColumns = useSetAtom(compactColumnsAtom);
|
|
599
635
|
const moveColumn = useSetAtom(moveColumnAtom);
|
|
636
|
+
const setCellCode = useSetAtom(setCellCodeAtom);
|
|
600
637
|
const splitCell = useSetAtom(splitCellAtom);
|
|
601
638
|
const undoSplitCell = useSetAtom(undoSplitCellAtom);
|
|
602
639
|
return useMemo(
|
|
603
640
|
() => ({
|
|
604
641
|
...operations,
|
|
642
|
+
setCellCode,
|
|
605
643
|
splitCell,
|
|
606
644
|
undoSplitCell,
|
|
607
645
|
addColumn,
|
|
@@ -620,6 +658,7 @@ export function useCellActions() {
|
|
|
620
658
|
createCell,
|
|
621
659
|
moveToNextCell,
|
|
622
660
|
addSetupCell,
|
|
661
|
+
addOrAppendSetupCell,
|
|
623
662
|
deleteCells,
|
|
624
663
|
undoDeleteCell,
|
|
625
664
|
focusCell,
|
package/src/core/cells/cells.ts
CHANGED
|
@@ -22,7 +22,7 @@ import type { CellConfig } from "../network/types";
|
|
|
22
22
|
import { createDeepEqualAtom, store } from "../state/jotai";
|
|
23
23
|
import { isWasm } from "../wasm/utils";
|
|
24
24
|
import { prepareCellForExecution, transitionCell } from "./cell";
|
|
25
|
-
import { applyDocument
|
|
25
|
+
import { applyDocument } from "./document";
|
|
26
26
|
import { isCodeHidden, shownHiddenCodeAtom } from "./editor";
|
|
27
27
|
import { type CellId, SCRATCH_CELL_ID } from "./ids";
|
|
28
28
|
import { type CellLog, getCellLogsForMessage } from "./logs";
|
|
@@ -154,50 +154,35 @@ export function initialNotebookState(): NotebookState {
|
|
|
154
154
|
*/
|
|
155
155
|
const {
|
|
156
156
|
reducer,
|
|
157
|
-
addMiddleware,
|
|
158
157
|
createActions,
|
|
159
158
|
useActions,
|
|
160
159
|
valueAtom: notebookAtom,
|
|
161
160
|
} = createReducerAndAtoms(initialNotebookState, {
|
|
162
161
|
/**
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
162
|
+
* Keep a cell as run, or as edited, as it was before formatting rewrote
|
|
163
|
+
* its code from `before` to `after`. The code itself arrives through the
|
|
164
|
+
* document.
|
|
166
165
|
*/
|
|
167
|
-
|
|
166
|
+
codeFormatted: (
|
|
168
167
|
state,
|
|
169
|
-
action: {
|
|
170
|
-
cellId: CellId;
|
|
171
|
-
code: string;
|
|
172
|
-
/**
|
|
173
|
-
* Whether or not the update is a formatting change,
|
|
174
|
-
* if so, the 'edited' state will be handled differently.
|
|
175
|
-
*/
|
|
176
|
-
formattingChange: boolean;
|
|
177
|
-
},
|
|
168
|
+
action: { cellId: CellId; before: string; after: string },
|
|
178
169
|
) => {
|
|
179
|
-
const { cellId,
|
|
170
|
+
const { cellId, before, after } = action;
|
|
180
171
|
if (!state.cellData[cellId]) {
|
|
181
172
|
return state;
|
|
182
173
|
}
|
|
183
|
-
|
|
184
174
|
return updateCellData({
|
|
185
175
|
state,
|
|
186
176
|
cellId,
|
|
187
177
|
cellReducer: (cell) => {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
: {
|
|
197
|
-
...cell,
|
|
198
|
-
code: code,
|
|
199
|
-
edited: code.trim() !== cell.lastCodeRun,
|
|
200
|
-
};
|
|
178
|
+
const lastCodeRun = isEdited(cell.lastCodeRun, before)
|
|
179
|
+
? cell.lastCodeRun
|
|
180
|
+
: after;
|
|
181
|
+
return {
|
|
182
|
+
...cell,
|
|
183
|
+
lastCodeRun,
|
|
184
|
+
edited: isEdited(lastCodeRun, cell.code),
|
|
185
|
+
};
|
|
201
186
|
},
|
|
202
187
|
});
|
|
203
188
|
},
|
|
@@ -426,11 +411,6 @@ const {
|
|
|
426
411
|
},
|
|
427
412
|
});
|
|
428
413
|
|
|
429
|
-
// We apply the middleware here (rather than inline in createReducerAndAtoms)
|
|
430
|
-
// so that the document edit middleware can import CellActions and
|
|
431
|
-
// strictly type the dispatched actions without creating a circular dependency.
|
|
432
|
-
addMiddleware(documentEditMiddleware);
|
|
433
|
-
|
|
434
414
|
// Helper function to update a cell in the array
|
|
435
415
|
function updateCellRuntimeState({
|
|
436
416
|
state,
|