@marimo-team/islands 0.25.3-dev197 → 0.25.3-dev198
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-BRuwLOIx.js → ConnectedDataExplorerComponent-BsWiIPFk.js} +4 -4
- package/dist/{ErrorBoundary-jQVF8yMb.js → ErrorBoundary-D1Z3KAf_.js} +2 -2
- package/dist/{abnfDiagram-VCTEODGH-LlJTOOU_.js → abnfDiagram-VCTEODGH-I2x4Yu25.js} +2 -2
- package/dist/{architectureDiagram-5GKGNRK7-9PQtGLLZ.js → architectureDiagram-5GKGNRK7-1bSQMKzK.js} +1 -1
- package/dist/{arrays-DA6KkLfq.js → arrays-DoQ-AILx.js} +7 -21
- package/dist/{capabilities-CvWU-ey_.js → capabilities-C2QUNRXi.js} +1 -1
- package/dist/{chat-ui-Bl2euMBD.js → chat-ui-BAyHM_GD.js} +524 -524
- package/dist/{chunk-IMKFNOWR-DcXv-BD5.js → chunk-IMKFNOWR-fsCGAJ9G.js} +1 -1
- package/dist/{chunk-SHT3W25Y-VwnELN23.js → chunk-SHT3W25Y-D2STW4hj.js} +1 -1
- package/dist/{chunk-SVP7TREG-DgQpI5Zi.js → chunk-SVP7TREG-CiY7cQG-.js} +1 -1
- package/dist/{chunk-TICWLB2K-CTg6VTJT.js → chunk-TICWLB2K-C_t5RBp9.js} +1 -1
- package/dist/{classDiagram-ZZMXUADV-UzmcFrnW.js → classDiagram-ZZMXUADV-B2JvZAP4.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-DhjiI6IQ.js → classDiagram-v2-VYDZK3BY-L8VsZG5A.js} +2 -2
- package/dist/{code-visibility-CjZo_SV8.js → code-visibility-CInEQd1e.js} +19 -19
- package/dist/{common-CFLvScF3.js → common-W5HfVJPN.js} +4362 -4362
- package/dist/{cynefinDiagram-5FMLGOSQ-O4ShIbJX.js → cynefinDiagram-5FMLGOSQ-DdllC264.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-dtHhhcPu.js → dagre-GXQ25YYZ-BaWHw4_f.js} +1 -1
- package/dist/{data-grid-overlay-editor-CzW3mY6m.js → data-grid-overlay-editor-BQCaV0fI.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-ppPYVZ6W.js → diagram-S7CK7UJ4-pFt-tUDe.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-CMZDf--k.js → diagram-UQ7AKVKN-DqGJiBp4.js} +1 -1
- package/dist/{diagram-VX7I27RA-CFvJdx-C.js → diagram-VX7I27RA-Dr-yzMjf.js} +1 -1
- package/dist/{diagram-Z3DM3KII-Dt6_uFbv.js → diagram-Z3DM3KII-8xzJzv-w.js} +1 -1
- package/dist/{dropdown-menu-CeaOsjWZ.js → dropdown-menu-NlnsmNA6.js} +2 -2
- package/dist/{ebnfDiagram-PWID7BFC-DpRFrKK-.js → ebnfDiagram-PWID7BFC-Br04Wy-E.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-CqAU4b0u.js → erDiagram-RLTQ6QDP-D5-XVgvv.js} +1 -1
- package/dist/{error-entries-CYGwr5-O.js → error-entries-BacCWMWj.js} +4678 -5028
- package/dist/{feedback-button-GR_8U4Vo.js → feedback-button-DLXUcQ7l.js} +113 -113
- package/dist/{flowDiagram-HODETNUW-i8-Gw8HO.js → flowDiagram-HODETNUW-CVl2kREF.js} +2 -2
- package/dist/{formats-BtoHHy42.js → formats-BCYthwyc.js} +1 -1
- package/dist/{glide-data-editor-Dfdxc22H.js → glide-data-editor-D5YaHPKx.js} +5 -5
- package/dist/{globals-CxjzBEbP.js → globals-DjX1uxzy.js} +2 -2
- package/dist/{html-to-image-Cy5UDpQJ.js → html-to-image-DAmgSo5P.js} +624 -573
- package/dist/{infoDiagram-27XIBGKW-DpfOKdLL.js → infoDiagram-27XIBGKW-BJ4vNcZE.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-Dg7mrMG7.js → ishikawaDiagram-5VMMS53U-B_Jy1Dsm.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-QB6e3Jrj.js → kanban-definition-UXKFOSKX-C6MkS9vw.js} +1 -1
- package/dist/main.js +1117 -1117
- package/dist/{mermaid-CeWG4Egr.js → mermaid-Bh918qrq.js} +34 -34
- package/dist/{mindmap-definition-YA3MSWOX-BTz3uTGe.js → mindmap-definition-YA3MSWOX-BP3ZTuir.js} +1 -1
- package/dist/{number-overlay-editor-CwHYeI4e.js → number-overlay-editor-3C_K6w_0.js} +1 -1
- package/dist/{once-DWGO2mQ2.js → once-NnpX_HnX.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-3ZIEa3mB.js → pegDiagram-XKGWAZYB-nNGOQtnt.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-BJAlk046.js → pieDiagram-E7YTZNPT-CpRz5BZz.js} +1 -1
- package/dist/{process-output-q9MTAIMX.js → process-output-DweUmAmV.js} +3 -3
- package/dist/{railroadDiagram-O6MQD6OU-Dn3bsSRa.js → railroadDiagram-O6MQD6OU-BqOEN4I0.js} +2 -2
- package/dist/{requirementDiagram-BXWQKSXE-wcOmQQCL.js → requirementDiagram-BXWQKSXE-CEfg6vu9.js} +1 -1
- package/dist/{reveal-component-C5bOqXlB.js → reveal-component-JjJRTT9d.js} +17 -17
- package/dist/{sequenceDiagram-WJ2MYXX4-DSh_qxoF.js → sequenceDiagram-WJ2MYXX4-IOmpQcOi.js} +1 -1
- package/dist/{spec-C7Fos-If.js → spec-Dkp64vVQ.js} +1 -1
- package/dist/{stateDiagram-D77RDMKH-DH_H8NMH.js → stateDiagram-D77RDMKH-tgU-EY9_.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-D_JM7MEK.js → stateDiagram-v2-MP3YSRHH-BofhG6q1.js} +2 -2
- package/dist/{swimlanes-42K2YHIH-CfoTJO-9.js → swimlanes-42K2YHIH-DW4vWDBd.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-Z_QhvK0G.js → swimlanesDiagram-VR7AAH4N-BJcrqUJr.js} +2 -2
- package/dist/{swiper-component-D2geL1Ku.js → swiper-component-BVfnhbnt.js} +1 -1
- package/dist/{timeline-definition-24CTP7MA-BFtdScaL.js → timeline-definition-24CTP7MA-D_QU3NGs.js} +1 -1
- package/dist/{toDate-CQAFSwos.js → toDate-BNDEXbfV.js} +30 -30
- package/dist/{useAsyncData-BeyZyjso.js → useAsyncData-ASDni9vH.js} +1 -1
- package/dist/{useDeepCompareMemoize-DOQgae0W.js → useDeepCompareMemoize-B1Ogyf53.js} +1 -1
- package/dist/{useIframeCapabilities-DNvaj9wM.js → useIframeCapabilities-6a_yDNXp.js} +1 -1
- package/dist/{useLifecycle-n2lnCFS8.js → useLifecycle-Bfpa2m7k.js} +1 -1
- package/dist/{useTheme-CY777LXN.js → useTheme-5TwyOh3B.js} +66 -66
- package/dist/{vega-component-0nQzA-I8.js → vega-component-BHS9UOtC.js} +19 -19
- package/dist/{vennDiagram-4TSXK5OY-BXY8IGCA.js → vennDiagram-4TSXK5OY-YFYsNIAh.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-D0tFe6eC.js → wardleyDiagram-VM6X3IG4-i_n8mAWC.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-Dx8wFM-y.js → xychartDiagram-S5SC5T6Z-BYGN95wW.js} +1 -1
- package/package.json +1 -1
- package/src/__mocks__/document.tsx +13 -4
- package/src/__mocks__/notebook.ts +1 -2
- package/src/components/datasources/datasources.tsx +1 -1
- package/src/components/dependency-graph/custom-node.tsx +1 -1
- package/src/components/dependency-graph/minimap-content.tsx +12 -14
- package/src/components/editor/actions/useSetCodeVisibility.ts +1 -1
- package/src/components/editor/actions/useSetOutputsExpanded.ts +1 -1
- package/src/components/editor/alerts/stdin-blocking-alert.tsx +1 -1
- package/src/components/editor/cell/code/cell-editor.tsx +1 -1
- package/src/components/editor/cell/useSplitCell.tsx +8 -5
- package/src/components/editor/chrome/panels/dependency-graph-panel.tsx +1 -1
- package/src/components/editor/chrome/panels/session-panel.tsx +1 -1
- package/src/components/editor/chrome/panels/variable-panel.tsx +1 -5
- package/src/components/editor/chrome/wrapper/minimap-state.ts +1 -1
- package/src/components/editor/columns/__tests__/cell-column.test.tsx +1 -1
- package/src/components/editor/columns/cell-column.tsx +1 -1
- package/src/components/editor/columns/sortable-column.tsx +1 -1
- package/src/components/editor/links/__tests__/cell-link.test.tsx +1 -1
- package/src/components/editor/links/cell-link-list.tsx +1 -1
- package/src/components/editor/links/cell-link.tsx +1 -1
- package/src/components/editor/navigation/__tests__/navigation.test.ts +20 -6
- package/src/components/editor/navigation/__tests__/selection.test.ts +1 -2
- package/src/components/editor/navigation/clipboard.ts +5 -3
- package/src/components/editor/renderers/cell-array.tsx +7 -12
- package/src/components/editor/renderers/slides-layout/slides-layout.tsx +1 -1
- package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +4 -4
- package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +1 -1
- package/src/components/editor/stdin-blocking-alert.tsx +1 -1
- package/src/components/slides/__tests__/minimap-actions.test.tsx +13 -4
- package/src/components/slides/__tests__/minimap.test.ts +38 -19
- package/src/components/slides/minimap.tsx +11 -10
- package/src/components/sort/SortableCellsProvider.tsx +2 -21
- package/src/components/tracing/tracing.tsx +1 -1
- package/src/core/ai/__tests__/staged-cells.test.ts +1 -1
- package/src/core/ai/context/providers/__tests__/cell-output.test.ts +8 -12
- package/src/core/ai/context/providers/cell-output.ts +2 -2
- package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +34 -66
- package/src/core/ai/tools/edit-notebook-tool.ts +12 -10
- package/src/core/cells/__tests__/actions.test.ts +64 -25
- package/src/core/cells/__tests__/arrange.test.ts +156 -4
- package/src/core/cells/__tests__/cells.test.ts +12 -290
- package/src/core/cells/__tests__/document.test.ts +2 -46
- package/src/core/cells/__tests__/harness.ts +28 -4
- package/src/core/cells/__tests__/layout.test.ts +42 -54
- package/src/core/cells/__tests__/session.test.ts +0 -2
- package/src/core/cells/__tests__/utils.test.ts +9 -107
- package/src/core/cells/actions.ts +112 -26
- package/src/core/cells/add-missing-import.ts +1 -1
- package/src/core/cells/arrange.ts +206 -13
- package/src/core/cells/cells.ts +28 -256
- package/src/core/cells/document.ts +18 -122
- package/src/core/cells/layout.ts +79 -10
- package/src/core/cells/pending-delete-service.ts +1 -1
- package/src/core/cells/utils.ts +4 -4
- package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +6 -7
- package/src/core/codemirror/copilot/getCodes.ts +2 -2
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +1 -2
- package/src/core/codemirror/lsp/utils.ts +1 -1
- package/src/core/documentation/doc-lookup.ts +1 -1
- package/src/core/edit-app.tsx +2 -5
- package/src/core/errors/error-entries.ts +1 -1
- package/src/core/islands/__tests__/bootstrap.test.ts +2 -6
- package/src/core/islands/components/web-components.tsx +1 -1
- package/src/core/notebook/document/__tests__/codemirror.test.ts +8 -0
- package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +1 -1
- package/src/stories/cell.stories.tsx +1 -2
- package/src/stories/layout/vertical/one-column.stories.tsx +1 -2
- package/src/stories/runtime-story.tsx +2 -3
- package/src/utils/__tests__/id-tree.test.ts +0 -617
- package/src/utils/id-tree.tsx +0 -609
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
4
|
import { cellId } from "@/__tests__/branded";
|
|
6
|
-
import type {
|
|
7
|
-
import type { CellColumnId } from "@/utils/id-tree";
|
|
8
|
-
import { exportedForTesting, notebookAtom, notebookReducer } from "../cells";
|
|
9
|
-
import { applyDocument } from "../document";
|
|
5
|
+
import type { CellColumnId } from "@/core/cells/layout";
|
|
10
6
|
import type { CellId } from "../ids";
|
|
11
7
|
import {
|
|
12
8
|
collapsedAtom,
|
|
@@ -15,10 +11,13 @@ import {
|
|
|
15
11
|
foldSections,
|
|
16
12
|
foldsAfter,
|
|
17
13
|
foldsHiding,
|
|
14
|
+
isColumnId,
|
|
18
15
|
layoutAtom,
|
|
16
|
+
newColumnId,
|
|
19
17
|
visibleNeighbor,
|
|
20
18
|
} from "../layout";
|
|
21
19
|
import type { Outline } from "../outline";
|
|
20
|
+
import { setupNotebook } from "./harness";
|
|
22
21
|
|
|
23
22
|
const [h1, a, h2, b, c, h1b, d] = ["h1", "a", "h2", "b", "c", "h1b", "d"].map(
|
|
24
23
|
cellId,
|
|
@@ -71,6 +70,13 @@ describe("foldSections", () => {
|
|
|
71
70
|
});
|
|
72
71
|
});
|
|
73
72
|
|
|
73
|
+
describe("column ids", () => {
|
|
74
|
+
it("are told apart from cell ids, as drag and drop needs", () => {
|
|
75
|
+
expect(isColumnId(newColumnId())).toBe(true);
|
|
76
|
+
expect(isColumnId(cellId("Hbol"))).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
74
80
|
describe("deriveLayout", () => {
|
|
75
81
|
const layout = deriveLayout(
|
|
76
82
|
[column("left", ids), column("right", [])],
|
|
@@ -116,74 +122,56 @@ describe("deriveLayout", () => {
|
|
|
116
122
|
});
|
|
117
123
|
|
|
118
124
|
describe("layoutAtom", () => {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
config: {
|
|
127
|
-
column: null,
|
|
128
|
-
disabled: false,
|
|
129
|
-
hide_code: false,
|
|
130
|
-
expand_output: false,
|
|
131
|
-
},
|
|
132
|
-
}));
|
|
133
|
-
store.set(
|
|
134
|
-
notebookAtom,
|
|
135
|
-
applyDocument(exportedForTesting.initialNotebookState(), {
|
|
136
|
-
order: ids,
|
|
137
|
-
cells: new Map(cells.map((cell) => [cell.id, cell])),
|
|
138
|
-
}),
|
|
139
|
-
);
|
|
140
|
-
return store;
|
|
141
|
-
}
|
|
125
|
+
const stops: Array<() => void> = [];
|
|
126
|
+
afterEach(() => {
|
|
127
|
+
for (const stop of stops.splice(0)) {
|
|
128
|
+
stop();
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
const [c1, c2] = ["c1", "c2"].map(cellId);
|
|
142
132
|
|
|
143
133
|
/** Deliver `html` as a cell's output, as the kernel's message does. */
|
|
144
134
|
function output(
|
|
145
|
-
|
|
135
|
+
dispatch: ReturnType<typeof setupNotebook>["dispatch"],
|
|
146
136
|
id: CellId,
|
|
147
137
|
html: string,
|
|
148
138
|
) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
data: html,
|
|
158
|
-
timestamp: 1,
|
|
159
|
-
},
|
|
160
|
-
console: null,
|
|
161
|
-
status: "idle",
|
|
162
|
-
stale_inputs: null,
|
|
139
|
+
dispatch({
|
|
140
|
+
type: "handleCellMessage",
|
|
141
|
+
payload: {
|
|
142
|
+
cell_id: id,
|
|
143
|
+
output: {
|
|
144
|
+
channel: "output",
|
|
145
|
+
mimetype: "text/html",
|
|
146
|
+
data: html,
|
|
163
147
|
timestamp: 1,
|
|
164
148
|
},
|
|
165
|
-
|
|
166
|
-
|
|
149
|
+
console: null,
|
|
150
|
+
status: "idle",
|
|
151
|
+
stale_inputs: null,
|
|
152
|
+
timestamp: 1,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
167
155
|
}
|
|
168
156
|
|
|
169
157
|
it("is the same layout after output that changes no heading", () => {
|
|
170
|
-
const store =
|
|
171
|
-
output(
|
|
158
|
+
const { store, dispatch } = setupNotebook(stops);
|
|
159
|
+
output(dispatch, c1, "<h1>Title</h1>");
|
|
172
160
|
const layout = store.get(layoutAtom);
|
|
173
161
|
|
|
174
|
-
output(
|
|
175
|
-
output(
|
|
162
|
+
output(dispatch, c2, "<p>1</p>");
|
|
163
|
+
output(dispatch, c1, "<h1>Title</h1>");
|
|
176
164
|
|
|
177
165
|
expect(store.get(layoutAtom)).toBe(layout);
|
|
178
166
|
});
|
|
179
167
|
|
|
180
168
|
it("follows a heading arriving in a cell's output", () => {
|
|
181
|
-
const store =
|
|
182
|
-
store.set(collapsedAtom, new Set([
|
|
169
|
+
const { store, dispatch } = setupNotebook(stops);
|
|
170
|
+
store.set(collapsedAtom, new Set([c1]));
|
|
183
171
|
expect(store.get(layoutAtom).columns[0].folds.size).toBe(0);
|
|
184
172
|
|
|
185
|
-
output(
|
|
173
|
+
output(dispatch, c1, "<h1>Title</h1>");
|
|
186
174
|
|
|
187
|
-
expect(store.get(layoutAtom).columns[0].folds.get(
|
|
175
|
+
expect(store.get(layoutAtom).columns[0].folds.get(c1)).toEqual([c2]);
|
|
188
176
|
});
|
|
189
177
|
});
|
|
@@ -6,7 +6,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
6
6
|
import { Mocks } from "@/__mocks__/common";
|
|
7
7
|
import { cellId } from "@/__tests__/branded";
|
|
8
8
|
import { parseOutline } from "@/core/dom/outline";
|
|
9
|
-
import { visibleForTesting } from "@/utils/id-tree";
|
|
10
9
|
import { invariant } from "@/utils/invariant";
|
|
11
10
|
import { Logger } from "@/utils/Logger";
|
|
12
11
|
import { SETUP_CELL_ID } from "../ids";
|
|
@@ -35,7 +34,6 @@ function cellIn(result: ReturnType<typeof readExport>, id: string) {
|
|
|
35
34
|
describe("readExport", () => {
|
|
36
35
|
beforeEach(() => {
|
|
37
36
|
vi.clearAllMocks();
|
|
38
|
-
visibleForTesting.reset();
|
|
39
37
|
});
|
|
40
38
|
|
|
41
39
|
// Test data factories
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
/* oxlint-disable typescript/no-explicit-any */
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
|
-
import {
|
|
5
|
-
getCellConfigs,
|
|
6
|
-
initialNotebookState,
|
|
7
|
-
type NotebookState,
|
|
8
|
-
} from "@/core/cells/cells";
|
|
4
|
+
import { initialNotebookState, type NotebookState } from "@/core/cells/cells";
|
|
9
5
|
import { CellId } from "@/core/cells/ids";
|
|
10
|
-
import type { CellData
|
|
6
|
+
import type { CellData } from "@/core/cells/types";
|
|
11
7
|
import type { OutputMessage } from "@/core/kernel/messages";
|
|
12
|
-
import { MultiColumn } from "@/utils/id-tree";
|
|
13
8
|
import {
|
|
14
9
|
disabledCellIds,
|
|
15
10
|
enabledCellIds,
|
|
@@ -18,103 +13,10 @@ import {
|
|
|
18
13
|
staleCellIds,
|
|
19
14
|
} from "../utils";
|
|
20
15
|
|
|
21
|
-
describe("getCellConfigs", () => {
|
|
22
|
-
it("should return correct cell configs with column indices", () => {
|
|
23
|
-
// Create mock cell IDs
|
|
24
|
-
const cellId1 = CellId.create();
|
|
25
|
-
const cellId2 = CellId.create();
|
|
26
|
-
const cellId3 = CellId.create();
|
|
27
|
-
const cellId4 = CellId.create();
|
|
28
|
-
|
|
29
|
-
// Create a mock NotebookState
|
|
30
|
-
const mockState: NotebookState = {
|
|
31
|
-
...initialNotebookState(),
|
|
32
|
-
cellIds: MultiColumn.from([
|
|
33
|
-
[cellId1, cellId2],
|
|
34
|
-
[cellId3, cellId4],
|
|
35
|
-
]),
|
|
36
|
-
cellData: {
|
|
37
|
-
[cellId1]: {
|
|
38
|
-
id: cellId1,
|
|
39
|
-
config: { hide_code: false, disabled: false },
|
|
40
|
-
} as CellData,
|
|
41
|
-
[cellId2]: {
|
|
42
|
-
id: cellId2,
|
|
43
|
-
config: { hide_code: true, disabled: false },
|
|
44
|
-
} as CellData,
|
|
45
|
-
[cellId3]: {
|
|
46
|
-
id: cellId3,
|
|
47
|
-
config: { hide_code: false, disabled: true },
|
|
48
|
-
} as CellData,
|
|
49
|
-
[cellId4]: {
|
|
50
|
-
id: cellId4,
|
|
51
|
-
config: { hide_code: true, disabled: true },
|
|
52
|
-
} as CellData,
|
|
53
|
-
},
|
|
54
|
-
cellRuntime: {} as Record<CellId, CellRuntimeState>,
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// Call the function
|
|
58
|
-
const result = getCellConfigs(mockState);
|
|
59
|
-
|
|
60
|
-
// Assert the results
|
|
61
|
-
expect(result).toEqual([
|
|
62
|
-
{ hide_code: false, disabled: false, column: 0 },
|
|
63
|
-
{ hide_code: true, disabled: false, column: null },
|
|
64
|
-
{ hide_code: false, disabled: true, column: 1 },
|
|
65
|
-
{ hide_code: true, disabled: true, column: null },
|
|
66
|
-
]);
|
|
67
|
-
|
|
68
|
-
// Check that the original state was not modified
|
|
69
|
-
expect(mockState.cellData[cellId1].config).toEqual({
|
|
70
|
-
hide_code: false,
|
|
71
|
-
disabled: false,
|
|
72
|
-
});
|
|
73
|
-
expect(mockState.cellData[cellId2].config).toEqual({
|
|
74
|
-
hide_code: true,
|
|
75
|
-
disabled: false,
|
|
76
|
-
});
|
|
77
|
-
expect(mockState.cellData[cellId3].config).toEqual({
|
|
78
|
-
hide_code: false,
|
|
79
|
-
disabled: true,
|
|
80
|
-
});
|
|
81
|
-
expect(mockState.cellData[cellId4].config).toEqual({
|
|
82
|
-
hide_code: true,
|
|
83
|
-
disabled: true,
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("should handle single column", () => {
|
|
88
|
-
const cellId1 = CellId.create();
|
|
89
|
-
const mockState: NotebookState = {
|
|
90
|
-
...initialNotebookState(),
|
|
91
|
-
cellIds: MultiColumn.from([[cellId1]]),
|
|
92
|
-
cellData: {
|
|
93
|
-
[cellId1]: {
|
|
94
|
-
id: cellId1,
|
|
95
|
-
config: { hide_code: false, disabled: false },
|
|
96
|
-
} as CellData,
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const result = getCellConfigs(mockState);
|
|
101
|
-
expect(result).toEqual([
|
|
102
|
-
{ hide_code: false, disabled: false, column: null },
|
|
103
|
-
]);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("should handle empty notebook state", () => {
|
|
107
|
-
const mockState: NotebookState = initialNotebookState();
|
|
108
|
-
const result = getCellConfigs(mockState);
|
|
109
|
-
|
|
110
|
-
expect(result).toEqual([]);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
16
|
describe("staleCellIds", () => {
|
|
115
17
|
it("should return cell IDs that have not been instantiated when auto-instantiate is false", () => {
|
|
116
18
|
const state: NotebookState = {
|
|
117
|
-
cellIds:
|
|
19
|
+
cellIds: ["cell1", "cell2"],
|
|
118
20
|
cellData: {
|
|
119
21
|
cell1: {
|
|
120
22
|
lastExecutionTime: null,
|
|
@@ -154,7 +56,7 @@ describe("staleCellIds", () => {
|
|
|
154
56
|
|
|
155
57
|
it("should use lastExecutionTime when runElapsedTimeMs is null", () => {
|
|
156
58
|
const state: NotebookState = {
|
|
157
|
-
cellIds:
|
|
59
|
+
cellIds: ["cell1", "cell2"],
|
|
158
60
|
cellData: {
|
|
159
61
|
cell1: {
|
|
160
62
|
lastExecutionTime: null,
|
|
@@ -194,7 +96,7 @@ describe("staleCellIds", () => {
|
|
|
194
96
|
|
|
195
97
|
it("should return cell IDs that are edited", () => {
|
|
196
98
|
const state: NotebookState = {
|
|
197
|
-
cellIds:
|
|
99
|
+
cellIds: ["cell1", "cell2"],
|
|
198
100
|
cellData: {
|
|
199
101
|
cell1: {
|
|
200
102
|
lastExecutionTime: 123,
|
|
@@ -234,7 +136,7 @@ describe("staleCellIds", () => {
|
|
|
234
136
|
|
|
235
137
|
it("should return cell IDs that are interrupted", () => {
|
|
236
138
|
const state: NotebookState = {
|
|
237
|
-
cellIds:
|
|
139
|
+
cellIds: ["cell1", "cell2"],
|
|
238
140
|
cellData: {
|
|
239
141
|
cell1: {
|
|
240
142
|
lastExecutionTime: 123,
|
|
@@ -274,7 +176,7 @@ describe("staleCellIds", () => {
|
|
|
274
176
|
|
|
275
177
|
it("should not return cell IDs that are disabled", () => {
|
|
276
178
|
const state: NotebookState = {
|
|
277
|
-
cellIds:
|
|
179
|
+
cellIds: ["cell1", "cell2"],
|
|
278
180
|
cellData: {
|
|
279
181
|
cell1: {
|
|
280
182
|
lastExecutionTime: 123,
|
|
@@ -394,7 +296,7 @@ describe("disabledCellIds", () => {
|
|
|
394
296
|
|
|
395
297
|
const state: NotebookState = {
|
|
396
298
|
...initialNotebookState(),
|
|
397
|
-
cellIds:
|
|
299
|
+
cellIds: [cellId1, cellId2, cellId3],
|
|
398
300
|
cellData: {
|
|
399
301
|
[cellId1]: { id: cellId1, config: { disabled: true } } as CellData,
|
|
400
302
|
[cellId2]: { id: cellId2, config: { disabled: false } } as CellData,
|
|
@@ -415,7 +317,7 @@ describe("enabledCellIds", () => {
|
|
|
415
317
|
|
|
416
318
|
const state: NotebookState = {
|
|
417
319
|
...initialNotebookState(),
|
|
418
|
-
cellIds:
|
|
320
|
+
cellIds: [cellId1, cellId2, cellId3],
|
|
419
321
|
cellData: {
|
|
420
322
|
[cellId1]: { id: cellId1, config: { disabled: true } } as CellData,
|
|
421
323
|
[cellId2]: { id: cellId2, config: { disabled: false } } as CellData,
|
|
@@ -3,21 +3,28 @@
|
|
|
3
3
|
/** Notebook operations that edit the document and then adjust the editor. */
|
|
4
4
|
|
|
5
5
|
import { historyField } from "@codemirror/commands";
|
|
6
|
-
import { atom, type Getter, useSetAtom } from "jotai";
|
|
6
|
+
import { atom, type Getter, type Setter, useSetAtom } from "jotai";
|
|
7
7
|
import { useMemo } from "react";
|
|
8
8
|
import { scrollAndHighlightCell } from "@/components/editor/links/cell-link";
|
|
9
9
|
import type { EditableDocument } from "@/core/notebook/document";
|
|
10
|
-
import type { CellColumnId } from "@/utils/id-tree";
|
|
11
10
|
import { Logger } from "@/utils/Logger";
|
|
12
11
|
import { clamp } from "@/utils/math";
|
|
12
|
+
import { prepareCellCodeUpdate } from "../codemirror/language/cell-code";
|
|
13
|
+
import { splitEditor } from "../codemirror/language/utils";
|
|
13
14
|
import type { SerializedEditorState } from "../codemirror/types";
|
|
14
15
|
import { canCollapseOutline } from "../dom/outline";
|
|
15
16
|
import type { CellConfig } from "../network/types";
|
|
16
17
|
import { store } from "../state/jotai";
|
|
17
18
|
import {
|
|
19
|
+
addColumnAtom,
|
|
20
|
+
addColumnBreakpointAtom,
|
|
21
|
+
compactColumnsAtom,
|
|
22
|
+
deleteColumnAtom,
|
|
18
23
|
dropCellOverCellAtom,
|
|
19
24
|
dropCellOverColumnAtom,
|
|
25
|
+
mergeAllColumnsAtom,
|
|
20
26
|
moveCellAtom,
|
|
27
|
+
moveColumnAtom,
|
|
21
28
|
moveCellsRelativeToAtom,
|
|
22
29
|
moveCellToIndexAtom,
|
|
23
30
|
placeCells,
|
|
@@ -27,6 +34,7 @@ import {
|
|
|
27
34
|
import {
|
|
28
35
|
type DeletedCell,
|
|
29
36
|
notebookAtom,
|
|
37
|
+
notebookReducer,
|
|
30
38
|
releaseCellAtoms,
|
|
31
39
|
useReducerActions,
|
|
32
40
|
} from "./cells";
|
|
@@ -39,8 +47,10 @@ import {
|
|
|
39
47
|
} from "./editor";
|
|
40
48
|
import { CellId, SCRATCH_CELL_ID, SETUP_CELL_ID } from "./ids";
|
|
41
49
|
import {
|
|
50
|
+
type CellColumnId,
|
|
42
51
|
collapsedAtom,
|
|
43
52
|
columnOf,
|
|
53
|
+
findColumn,
|
|
44
54
|
foldOf,
|
|
45
55
|
foldsAfter,
|
|
46
56
|
foldsHiding,
|
|
@@ -91,7 +101,7 @@ export const createCellAtom = atom(
|
|
|
91
101
|
if (options.skipIfCodeExists && hasCellWithCode(document, code)) {
|
|
92
102
|
return null;
|
|
93
103
|
}
|
|
94
|
-
const
|
|
104
|
+
const layout = get(layoutAtom);
|
|
95
105
|
|
|
96
106
|
// The document knows cells and order. Columns come from each cell's
|
|
97
107
|
// `column` config, which the cell before it provides unless it starts a
|
|
@@ -100,31 +110,31 @@ export const createCellAtom = atom(
|
|
|
100
110
|
let column: number | null = null;
|
|
101
111
|
// The cell the new one comes right after, whose folds would hide it.
|
|
102
112
|
let follows: CellId | undefined;
|
|
103
|
-
const layout = get(layoutAtom);
|
|
104
113
|
if (options.after) {
|
|
105
114
|
// A folded header hides the cells after it, so a cell placed after
|
|
106
115
|
// the header goes after its fold.
|
|
107
116
|
follows = foldOf(layout, options.after)?.at(-1) ?? options.after;
|
|
108
117
|
placement = { after: follows };
|
|
109
118
|
} else if (options.before) {
|
|
110
|
-
const
|
|
111
|
-
const index =
|
|
119
|
+
const target = columnOf(layout, options.before);
|
|
120
|
+
const index = layout.columns.indexOf(target);
|
|
112
121
|
placement = { before: options.before };
|
|
113
|
-
follows =
|
|
114
|
-
if (index > 0 &&
|
|
122
|
+
follows = target.ids[target.ids.indexOf(options.before) - 1];
|
|
123
|
+
if (index > 0 && target.ids[0] === options.before) {
|
|
115
124
|
column = index;
|
|
116
125
|
}
|
|
117
126
|
} else {
|
|
118
|
-
const
|
|
119
|
-
(options.endOf &&
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
127
|
+
const target =
|
|
128
|
+
(options.endOf && findColumn(layout, options.endOf)) ??
|
|
129
|
+
layout.columns[0];
|
|
130
|
+
const last = target.ids.at(-1);
|
|
131
|
+
if (last !== undefined) {
|
|
132
|
+
follows = last;
|
|
133
|
+
placement = { after: last };
|
|
134
|
+
} else if (layout.columns.length > 1) {
|
|
135
|
+
column = layout.columns.indexOf(target);
|
|
125
136
|
}
|
|
126
137
|
}
|
|
127
|
-
|
|
128
138
|
const id = options.id ?? CellId.create();
|
|
129
139
|
const config = { ...options.config, column };
|
|
130
140
|
set(modelOf(get).editAtom, (tx) => {
|
|
@@ -157,6 +167,70 @@ export const createCellAtom = atom(
|
|
|
157
167
|
},
|
|
158
168
|
);
|
|
159
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Split a cell at its cursor. The text before stays, the text after
|
|
172
|
+
* becomes a new cell below, which is focused. Returns the new cell's id,
|
|
173
|
+
* or null for a cell without an editor.
|
|
174
|
+
*/
|
|
175
|
+
export const splitCellAtom = atom(
|
|
176
|
+
null,
|
|
177
|
+
(get, set, { cellId }: { cellId: CellId }): CellId | null => {
|
|
178
|
+
const view = get(notebookAtom).cellHandles[cellId]?.current?.editorView;
|
|
179
|
+
if (!view) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
const { beforeCursorCode, afterCursorCode } = splitEditor(view);
|
|
183
|
+
const update = prepareCellCodeUpdate(view.state, beforeCursorCode);
|
|
184
|
+
if (update) {
|
|
185
|
+
view.dispatch(update);
|
|
186
|
+
}
|
|
187
|
+
const id = CellId.create();
|
|
188
|
+
set(modelOf(get).editAtom, (tx) => {
|
|
189
|
+
tx.setCode(cellId, beforeCursorCode);
|
|
190
|
+
tx.createCell({ id, code: afterCursorCode }, { after: cellId });
|
|
191
|
+
});
|
|
192
|
+
clearOutput(set, cellId);
|
|
193
|
+
focusWhenMounted(set, id);
|
|
194
|
+
return id;
|
|
195
|
+
},
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
/** Undo a split: the cell takes its code back and the cell split off goes. */
|
|
199
|
+
export const undoSplitCellAtom = atom(
|
|
200
|
+
null,
|
|
201
|
+
(
|
|
202
|
+
get,
|
|
203
|
+
set,
|
|
204
|
+
{
|
|
205
|
+
cellId,
|
|
206
|
+
newCellId,
|
|
207
|
+
snapshot,
|
|
208
|
+
}: { cellId: CellId; newCellId: CellId; snapshot: string },
|
|
209
|
+
): void => {
|
|
210
|
+
const view = get(notebookAtom).cellHandles[cellId]?.current?.editorView;
|
|
211
|
+
const update = view && prepareCellCodeUpdate(view.state, snapshot);
|
|
212
|
+
if (view && update) {
|
|
213
|
+
view.dispatch(update);
|
|
214
|
+
}
|
|
215
|
+
const document = documentOf(get);
|
|
216
|
+
set(modelOf(get).editAtom, (tx) => {
|
|
217
|
+
tx.setCode(cellId, snapshot);
|
|
218
|
+
if (document.cells.has(newCellId)) {
|
|
219
|
+
tx.deleteCell(newCellId);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
releaseCellAtoms(newCellId);
|
|
223
|
+
clearOutput(set, cellId);
|
|
224
|
+
},
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
/** The output belonged to the code before the change; neither version has run. */
|
|
228
|
+
function clearOutput(set: Setter, cellId: CellId): void {
|
|
229
|
+
set(notebookAtom, (state) =>
|
|
230
|
+
notebookReducer(state, { type: "clearCellOutput", payload: { cellId } }),
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
160
234
|
/**
|
|
161
235
|
* Focus the cell before or after `cellId`. Past the edge of the column,
|
|
162
236
|
* create one there, unless `create` is false.
|
|
@@ -283,9 +357,7 @@ export const collapseAllCellsAtom = atom(null, (get, set): void => {
|
|
|
283
357
|
set(
|
|
284
358
|
collapsedAtom,
|
|
285
359
|
new Set(
|
|
286
|
-
cellIds.
|
|
287
|
-
canCollapseOutline(cellRuntime[id]?.outline),
|
|
288
|
-
),
|
|
360
|
+
cellIds.filter((id) => canCollapseOutline(cellRuntime[id]?.outline)),
|
|
289
361
|
),
|
|
290
362
|
);
|
|
291
363
|
});
|
|
@@ -318,18 +390,18 @@ export const addSetupCellAtom = atom(
|
|
|
318
390
|
null,
|
|
319
391
|
(get, set, { code }: { code?: string } = {}): void => {
|
|
320
392
|
const { cellIds } = get(notebookAtom);
|
|
321
|
-
if (cellIds.
|
|
393
|
+
if (cellIds.includes(SETUP_CELL_ID)) {
|
|
322
394
|
focusWhenMounted(set, SETUP_CELL_ID);
|
|
323
395
|
return;
|
|
324
396
|
}
|
|
325
397
|
// The setup cell leads the first column, which may be empty.
|
|
326
|
-
const first =
|
|
398
|
+
const first = get(layoutAtom).columns[0];
|
|
327
399
|
set(createCellAtom, {
|
|
328
400
|
id: SETUP_CELL_ID,
|
|
329
401
|
name: SETUP_CELL_ID,
|
|
330
402
|
code: code ?? "",
|
|
331
|
-
...(first.length > 0
|
|
332
|
-
? { before: first.
|
|
403
|
+
...(first.ids.length > 0
|
|
404
|
+
? { before: first.ids[0] }
|
|
333
405
|
: { endOf: first.id }),
|
|
334
406
|
});
|
|
335
407
|
},
|
|
@@ -528,9 +600,25 @@ export function useCellActions() {
|
|
|
528
600
|
const dropCellOverColumn = useSetAtom(dropCellOverColumnAtom);
|
|
529
601
|
const sendToTop = useSetAtom(sendToTopAtom);
|
|
530
602
|
const sendToBottom = useSetAtom(sendToBottomAtom);
|
|
603
|
+
const addColumn = useSetAtom(addColumnAtom);
|
|
604
|
+
const addColumnBreakpoint = useSetAtom(addColumnBreakpointAtom);
|
|
605
|
+
const deleteColumn = useSetAtom(deleteColumnAtom);
|
|
606
|
+
const mergeAllColumns = useSetAtom(mergeAllColumnsAtom);
|
|
607
|
+
const compactColumns = useSetAtom(compactColumnsAtom);
|
|
608
|
+
const moveColumn = useSetAtom(moveColumnAtom);
|
|
609
|
+
const splitCell = useSetAtom(splitCellAtom);
|
|
610
|
+
const undoSplitCell = useSetAtom(undoSplitCellAtom);
|
|
531
611
|
return useMemo(
|
|
532
612
|
() => ({
|
|
533
613
|
...operations,
|
|
614
|
+
splitCell,
|
|
615
|
+
undoSplitCell,
|
|
616
|
+
addColumn,
|
|
617
|
+
addColumnBreakpoint,
|
|
618
|
+
deleteColumn,
|
|
619
|
+
mergeAllColumns,
|
|
620
|
+
compactColumns,
|
|
621
|
+
moveColumn,
|
|
534
622
|
moveCell,
|
|
535
623
|
moveCellToIndex,
|
|
536
624
|
moveCellsRelativeTo,
|
|
@@ -563,9 +651,7 @@ export type CellActions = ReturnType<typeof useCellActions>;
|
|
|
563
651
|
/** Scroll to the first cell that is currently running. */
|
|
564
652
|
export function notebookScrollToRunning() {
|
|
565
653
|
const { cellIds, cellRuntime } = store.get(notebookAtom);
|
|
566
|
-
const cellId = cellIds.
|
|
567
|
-
(id) => cellRuntime[id]?.status === "running",
|
|
568
|
-
);
|
|
654
|
+
const cellId = cellIds.find((id) => cellRuntime[id]?.status === "running");
|
|
569
655
|
if (!cellId) {
|
|
570
656
|
return;
|
|
571
657
|
}
|