@marimo-team/islands 0.25.3-dev192 → 0.25.3-dev196
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-BBbOElk3.js → ConnectedDataExplorerComponent-BRuwLOIx.js} +9 -9
- package/dist/{ErrorBoundary-Dw6uRKVk.js → ErrorBoundary-C3Xv3yzV.js} +2 -2
- package/dist/{abnfDiagram-VCTEODGH-DhMT6Uhd.js → abnfDiagram-VCTEODGH-LlJTOOU_.js} +2 -2
- package/dist/{architectureDiagram-5GKGNRK7-BVbEiJLq.js → architectureDiagram-5GKGNRK7-9PQtGLLZ.js} +1 -1
- package/dist/{arrays-R6JsImiT.js → arrays-DA6KkLfq.js} +6 -12
- package/dist/assets/__vite-browser-external-BCJsx8QS.js +1 -0
- package/dist/assets/{worker-C6e9ERyd.js → worker-DodjWT_Z.js} +2 -2
- package/dist/{capabilities-CI4xUPsp.js → capabilities-CvWU-ey_.js} +1 -1
- package/dist/{chat-ui-BguxTQEL.js → chat-ui-Bl2euMBD.js} +93 -93
- package/dist/{chunk-IMKFNOWR-By5hSbJJ.js → chunk-IMKFNOWR-DcXv-BD5.js} +1 -1
- package/dist/{chunk-SHT3W25Y-DFrEYG54.js → chunk-SHT3W25Y-VwnELN23.js} +1 -1
- package/dist/{chunk-SVP7TREG-DFXGc9ew.js → chunk-SVP7TREG-DgQpI5Zi.js} +1 -1
- package/dist/{chunk-TICWLB2K-DZdNIpoZ.js → chunk-TICWLB2K-CTg6VTJT.js} +1 -1
- package/dist/{classDiagram-ZZMXUADV-Cgj_eEQA.js → classDiagram-ZZMXUADV-UzmcFrnW.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-DLAUrSqj.js → classDiagram-v2-VYDZK3BY-DhjiI6IQ.js} +2 -2
- package/dist/{code-visibility-v_7ijLtx.js → code-visibility-BCcqmP58.js} +38 -42
- package/dist/{common-BUwWHpnK.js → common-DrNB2bY2.js} +849 -849
- package/dist/{cynefinDiagram-5FMLGOSQ-Cikf9AKa.js → cynefinDiagram-5FMLGOSQ-O4ShIbJX.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-C5KEi5_l.js → dagre-GXQ25YYZ-dtHhhcPu.js} +1 -1
- package/dist/{data-grid-overlay-editor-4IZymnXZ.js → data-grid-overlay-editor-TGXV_Hf4.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-BaoBTQjR.js → diagram-S7CK7UJ4-ppPYVZ6W.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-DDufBsUA.js → diagram-UQ7AKVKN-CMZDf--k.js} +1 -1
- package/dist/{diagram-VX7I27RA-CY6RALJ8.js → diagram-VX7I27RA-CFvJdx-C.js} +1 -1
- package/dist/{diagram-Z3DM3KII-Bc-LHsD6.js → diagram-Z3DM3KII-Dt6_uFbv.js} +1 -1
- package/dist/{dropdown-menu-drDjxJyV.js → dropdown-menu-CeaOsjWZ.js} +2 -2
- package/dist/{ebnfDiagram-PWID7BFC-Cz0J-tcC.js → ebnfDiagram-PWID7BFC-DpRFrKK-.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-DTXTDgFu.js → erDiagram-RLTQ6QDP-CqAU4b0u.js} +1 -1
- package/dist/{error-entries-CZ1NQ6Kv.js → error-entries-CYGwr5-O.js} +5596 -6012
- package/dist/feedback-button-PTiPn2GP.js +374 -0
- package/dist/{flowDiagram-HODETNUW-RbwWf2zc.js → flowDiagram-HODETNUW-i8-Gw8HO.js} +2 -2
- package/dist/{formats-DbXnijtH.js → formats-BtoHHy42.js} +1 -1
- package/dist/{glide-data-editor-DU8_EDG5.js → glide-data-editor-BSyexjkB.js} +5 -5
- package/dist/{globals-BeBDOk22.js → globals-B7gkJAxm.js} +2 -2
- package/dist/{html-to-image-D_4_hQ8U.js → html-to-image-Cy5UDpQJ.js} +56 -56
- package/dist/{infoDiagram-27XIBGKW-DlmzZjmI.js → infoDiagram-27XIBGKW-DpfOKdLL.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-EN3TYP_q.js → ishikawaDiagram-5VMMS53U-Dg7mrMG7.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-CK_-Ru7Q.js → kanban-definition-UXKFOSKX-QB6e3Jrj.js} +1 -1
- package/dist/main.js +5875 -5876
- package/dist/{mermaid-DU9ZRmkG.js → mermaid-CeWG4Egr.js} +48 -48
- package/dist/{mindmap-definition-YA3MSWOX-C-hqgUvF.js → mindmap-definition-YA3MSWOX-BTz3uTGe.js} +1 -1
- package/dist/{number-overlay-editor-BoIkyh6H.js → number-overlay-editor-Bb91yQ46.js} +1 -1
- package/dist/{once-eG8Vmvjd.js → once-DWGO2mQ2.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-BiMejYPB.js → pegDiagram-XKGWAZYB-3ZIEa3mB.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-B1zCZ1eq.js → pieDiagram-E7YTZNPT-BJAlk046.js} +1 -1
- package/dist/{process-output-DxSCHBWp.js → process-output-q9MTAIMX.js} +2 -2
- package/dist/{railroadDiagram-O6MQD6OU-BLcfaASl.js → railroadDiagram-O6MQD6OU-Dn3bsSRa.js} +2 -2
- package/dist/{requirementDiagram-BXWQKSXE-DtBOf52-.js → requirementDiagram-BXWQKSXE-wcOmQQCL.js} +1 -1
- package/dist/{reveal-component-5M3a2rpj.js → reveal-component-vTdjiyDv.js} +17 -17
- package/dist/{sequenceDiagram-WJ2MYXX4-DIwF19_n.js → sequenceDiagram-WJ2MYXX4-DSh_qxoF.js} +1 -1
- package/dist/{spec-DZDnnlgT.js → spec-C7Fos-If.js} +1 -1
- package/dist/{stateDiagram-D77RDMKH-sWk1AdYL.js → stateDiagram-D77RDMKH-DH_H8NMH.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-CiNo-OI7.js → stateDiagram-v2-MP3YSRHH-D_JM7MEK.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-Bj3hiOY5.js → swimlanes-42K2YHIH-CfoTJO-9.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-BzEo2r_N.js → swimlanesDiagram-VR7AAH4N-Z_QhvK0G.js} +2 -2
- package/dist/{swiper-component-BkCm-kLg.js → swiper-component-D2geL1Ku.js} +1 -1
- package/dist/{timeline-definition-24CTP7MA-CrPQk42H.js → timeline-definition-24CTP7MA-BFtdScaL.js} +1 -1
- package/dist/{toDate-Cu2pIZcC.js → toDate-CQAFSwos.js} +24 -24
- package/dist/{useAsyncData-DEhLdRIu.js → useAsyncData-BeyZyjso.js} +1 -1
- package/dist/{useDeepCompareMemoize-RjZGVhBV.js → useDeepCompareMemoize-DOQgae0W.js} +1 -1
- package/dist/{useIframeCapabilities-CSdWn6NP.js → useIframeCapabilities-DNvaj9wM.js} +1 -1
- package/dist/{useLifecycle-DtcB2s1z.js → useLifecycle-n2lnCFS8.js} +1 -1
- package/dist/{useTheme-CoEtr4mK.js → useTheme-CY777LXN.js} +303 -99
- package/dist/{vega-component-BhjTrP9-.js → vega-component-0nQzA-I8.js} +6 -6
- package/dist/{vennDiagram-4TSXK5OY-BaYMoIpj.js → vennDiagram-4TSXK5OY-BXY8IGCA.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-DjHeuj8p.js → wardleyDiagram-VM6X3IG4-D0tFe6eC.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-Bc_hcuXS.js → xychartDiagram-S5SC5T6Z-Dx8wFM-y.js} +1 -1
- package/package.json +1 -1
- package/src/__mocks__/notebook.ts +0 -1
- package/src/__mocks__/partial.ts +26 -0
- package/src/__tests__/main.test.tsx +24 -1
- package/src/components/app-config/app-config-form.tsx +5 -9
- package/src/components/editor/__tests__/data-attributes.test.tsx +0 -1
- package/src/components/editor/__tests__/output-persistence.test.tsx +0 -1
- package/src/components/editor/actions/useCellActionButton.tsx +6 -5
- package/src/components/editor/actions/useConfigActions.tsx +3 -4
- package/src/components/editor/actions/useNotebookActions.tsx +5 -4
- package/src/components/editor/cell/code/cell-editor.tsx +13 -6
- package/src/components/editor/cell/collapse.tsx +6 -1
- package/src/components/editor/chrome/components/feedback-button.tsx +1 -7
- package/src/components/editor/controls/Controls.tsx +1 -1
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +8 -6
- package/src/components/editor/navigation/__tests__/navigation.test.ts +15 -15
- package/src/components/editor/navigation/__tests__/selection.test.ts +44 -21
- package/src/components/editor/navigation/focus-utils.ts +19 -0
- package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +6 -13
- package/src/components/editor/navigation/navigation.ts +52 -43
- package/src/components/editor/navigation/selection.ts +30 -29
- package/src/components/editor/notebook-cell.tsx +10 -18
- package/src/components/editor/renderers/cell-array.tsx +6 -5
- package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -1
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +1 -1
- package/src/components/pages/edit-page.tsx +0 -13
- package/src/components/scratchpad/scratchpad.tsx +0 -1
- package/src/components/slides/reveal-component.tsx +1 -1
- package/src/components/slides/slide-cell-view.tsx +0 -1
- package/src/components/sort/SortableCellsProvider.tsx +6 -5
- package/src/core/MarimoApp.tsx +1 -1
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +0 -37
- package/src/core/cells/__tests__/actions.test.ts +190 -80
- package/src/core/cells/__tests__/arrange.test.ts +263 -0
- package/src/core/cells/__tests__/cells.test.ts +11 -1101
- package/src/core/cells/__tests__/document.test.ts +3 -32
- package/src/core/cells/__tests__/editor.test.ts +57 -0
- package/src/core/cells/__tests__/harness.ts +82 -0
- package/src/core/cells/__tests__/layout.test.ts +189 -0
- package/src/core/cells/__tests__/notebook-outline.test.ts +7 -1
- package/src/core/cells/__tests__/session.test.ts +329 -174
- package/src/core/cells/__tests__/utils.test.ts +0 -1
- package/src/core/cells/actions.ts +247 -82
- package/src/core/cells/arrange.ts +341 -0
- package/src/core/cells/cells.ts +27 -516
- package/src/core/cells/document.ts +6 -5
- package/src/core/cells/edit.ts +30 -0
- package/src/core/cells/editor.ts +82 -0
- package/src/core/cells/layout.ts +152 -0
- package/src/core/cells/session.ts +75 -52
- package/src/core/cells/types.ts +0 -5
- package/src/core/cells/utils.ts +6 -4
- package/src/core/config/__tests__/app-config.test.ts +64 -0
- package/src/core/config/__tests__/config-schema.test.ts +1 -1
- package/src/core/config/config-schema.ts +6 -2
- package/src/core/config/config.ts +26 -12
- package/src/core/islands/bootstrap.ts +0 -1
- package/src/core/kernel/handlers.ts +1 -13
- package/src/core/meta/__tests__/code-visibility.test.tsx +78 -70
- package/src/core/meta/code-visibility.ts +18 -9
- package/src/core/notebook/__tests__/operations.test.ts +51 -0
- package/src/core/notebook/document/__tests__/codemirror.test.ts +33 -7
- package/src/core/notebook/index.ts +2 -1
- package/src/core/notebook/operations.ts +30 -0
- package/src/core/saving/__tests__/state.test.ts +111 -0
- package/src/core/saving/save-component.tsx +19 -33
- package/src/core/saving/state.ts +38 -39
- package/src/core/saving/useAutoSave.ts +8 -35
- package/src/core/session/__tests__/backend.test.ts +1 -0
- package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +1 -0
- package/src/core/session/active-notebook.ts +11 -0
- package/src/core/session/backend.ts +17 -19
- package/src/core/session/embedded.ts +7 -0
- package/src/core/session/useNotebookReplica.ts +3 -1
- package/src/core/session/useNotebookSession.tsx +7 -8
- package/src/core/transports/__tests__/static.test.ts +139 -8
- package/src/core/transports/static.ts +98 -16
- package/src/core/wasm/__tests__/bridge.test.ts +1 -0
- package/src/mount.tsx +19 -10
- package/src/stories/cell.stories.tsx +0 -2
- package/src/stories/layout/vertical/one-column.stories.tsx +0 -2
- package/src/utils/__tests__/id-tree.test.ts +20 -1548
- package/src/utils/id-tree.tsx +12 -537
- package/dist/assets/__vite-browser-external-C8gQho07.js +0 -1
- package/dist/feedback-button-MpeoIn1D.js +0 -379
- package/src/components/editor/navigation/__tests__/state.test.ts +0 -81
- package/src/components/editor/navigation/state.ts +0 -65
package/src/core/cells/cells.ts
CHANGED
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
} from "@/utils/id-tree";
|
|
14
14
|
import { invariant } from "@/utils/invariant";
|
|
15
15
|
import { Logger } from "@/utils/Logger";
|
|
16
|
-
import { clamp } from "@/utils/math";
|
|
17
16
|
import { Objects } from "@/utils/objects";
|
|
18
17
|
import { extractAllTracebackInfo, type TracebackInfo } from "@/utils/traceback";
|
|
19
18
|
import { createReducerAndAtoms } from "../../utils/createReducer";
|
|
@@ -22,7 +21,7 @@ import { editorMountScheduler } from "../codemirror/editor-mount-scheduler";
|
|
|
22
21
|
import { prepareCellCodeUpdate } from "../codemirror/language/cell-code";
|
|
23
22
|
import { splitEditor } from "../codemirror/language/utils";
|
|
24
23
|
import type { SerializedEditorState } from "../codemirror/types";
|
|
25
|
-
import {
|
|
24
|
+
import { mergeOutlines } from "../dom/outline";
|
|
26
25
|
import type { CellMessage } from "../kernel/messages";
|
|
27
26
|
import { isErrorMime } from "../mime";
|
|
28
27
|
import type { CellConfig } from "../network/types";
|
|
@@ -30,13 +29,11 @@ import { createDeepEqualAtom, store } from "../state/jotai";
|
|
|
30
29
|
import { isWasm } from "../wasm/utils";
|
|
31
30
|
import { prepareCellForExecution, transitionCell } from "./cell";
|
|
32
31
|
import { applyDocument, documentEditMiddleware } from "./document";
|
|
32
|
+
import { isCodeHidden, shownHiddenCodeAtom } from "./editor";
|
|
33
33
|
import { CellId, SCRATCH_CELL_ID } from "./ids";
|
|
34
34
|
import { type CellLog, getCellLogsForMessage } from "./logs";
|
|
35
|
-
import {
|
|
36
|
-
|
|
37
|
-
scrollToBottom,
|
|
38
|
-
scrollToTop,
|
|
39
|
-
} from "./scrollCellIntoView";
|
|
35
|
+
import type { Outline } from "./outline";
|
|
36
|
+
import { focusAndScrollCellIntoView } from "./scrollCellIntoView";
|
|
40
37
|
import {
|
|
41
38
|
type CellData,
|
|
42
39
|
type CellRuntimeState,
|
|
@@ -116,12 +113,6 @@ export interface NotebookState {
|
|
|
116
113
|
* Logs of all cell messages
|
|
117
114
|
*/
|
|
118
115
|
cellLogs: CellLog[];
|
|
119
|
-
/**
|
|
120
|
-
* Set of cells that have been created that are initialized with `hide_code`.
|
|
121
|
-
*
|
|
122
|
-
* These start temporarily open until their first blur event.
|
|
123
|
-
*/
|
|
124
|
-
untouchedNewCells: Set<CellId>;
|
|
125
116
|
}
|
|
126
117
|
|
|
127
118
|
function withScratchCell(notebookState: NotebookState): NotebookState {
|
|
@@ -160,7 +151,6 @@ export function initialNotebookState(): NotebookState {
|
|
|
160
151
|
history: [],
|
|
161
152
|
scrollKey: null,
|
|
162
153
|
cellLogs: [],
|
|
163
|
-
untouchedNewCells: new Set<CellId>(),
|
|
164
154
|
});
|
|
165
155
|
}
|
|
166
156
|
|
|
@@ -174,226 +164,6 @@ const {
|
|
|
174
164
|
useActions,
|
|
175
165
|
valueAtom: notebookAtom,
|
|
176
166
|
} = createReducerAndAtoms(initialNotebookState, {
|
|
177
|
-
moveCell: (
|
|
178
|
-
state,
|
|
179
|
-
action: {
|
|
180
|
-
cellId: CellId;
|
|
181
|
-
before?: boolean;
|
|
182
|
-
direction?: "left" | "right";
|
|
183
|
-
},
|
|
184
|
-
) => {
|
|
185
|
-
const { cellId, before, direction } = action;
|
|
186
|
-
|
|
187
|
-
if (before !== undefined && direction !== undefined) {
|
|
188
|
-
Logger.warn(
|
|
189
|
-
"Both before and direction specified for moveCell. Ignoring one.",
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Handle left/right movement
|
|
194
|
-
if (direction) {
|
|
195
|
-
const fromColumn = state.cellIds.findWithId(cellId);
|
|
196
|
-
const fromColumnIndex = state.cellIds.indexOf(fromColumn);
|
|
197
|
-
const toColumnIndex =
|
|
198
|
-
direction === "left" ? fromColumnIndex - 1 : fromColumnIndex + 1;
|
|
199
|
-
const toColumn = state.cellIds.at(toColumnIndex);
|
|
200
|
-
|
|
201
|
-
// If no column to move to, return unchanged state
|
|
202
|
-
if (!toColumn) {
|
|
203
|
-
return state;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return {
|
|
207
|
-
...state,
|
|
208
|
-
cellIds: state.cellIds.moveAcrossColumns(
|
|
209
|
-
fromColumn.id,
|
|
210
|
-
cellId,
|
|
211
|
-
toColumn.id,
|
|
212
|
-
undefined,
|
|
213
|
-
),
|
|
214
|
-
scrollKey: cellId,
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Handle up/down movement
|
|
219
|
-
const column = state.cellIds.findWithId(cellId);
|
|
220
|
-
const cellIndex = column.indexOfOrThrow(cellId);
|
|
221
|
-
|
|
222
|
-
if (before && cellIndex === 0) {
|
|
223
|
-
return {
|
|
224
|
-
...state,
|
|
225
|
-
cellIds: state.cellIds.moveWithinColumn(column.id, cellIndex, 0),
|
|
226
|
-
scrollKey: cellId,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
if (!before && cellIndex === column.length - 1) {
|
|
230
|
-
return {
|
|
231
|
-
...state,
|
|
232
|
-
cellIds: state.cellIds.moveWithinColumn(
|
|
233
|
-
column.id,
|
|
234
|
-
cellIndex,
|
|
235
|
-
column.length - 1,
|
|
236
|
-
),
|
|
237
|
-
scrollKey: cellId,
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return before
|
|
242
|
-
? {
|
|
243
|
-
...state,
|
|
244
|
-
cellIds: state.cellIds.moveWithinColumn(
|
|
245
|
-
column.id,
|
|
246
|
-
cellIndex,
|
|
247
|
-
cellIndex - 1,
|
|
248
|
-
),
|
|
249
|
-
scrollKey: cellId,
|
|
250
|
-
}
|
|
251
|
-
: {
|
|
252
|
-
...state,
|
|
253
|
-
cellIds: state.cellIds.moveWithinColumn(
|
|
254
|
-
column.id,
|
|
255
|
-
cellIndex,
|
|
256
|
-
cellIndex + 1,
|
|
257
|
-
),
|
|
258
|
-
scrollKey: cellId,
|
|
259
|
-
};
|
|
260
|
-
},
|
|
261
|
-
moveCellToIndex: (
|
|
262
|
-
state,
|
|
263
|
-
action: {
|
|
264
|
-
cellId: CellId;
|
|
265
|
-
columnId: CellColumnId;
|
|
266
|
-
index: number;
|
|
267
|
-
},
|
|
268
|
-
) => {
|
|
269
|
-
const { cellId, columnId, index } = action;
|
|
270
|
-
const fromColumn = state.cellIds.findWithId(cellId);
|
|
271
|
-
const fromIndex = fromColumn.indexOfOrThrow(cellId);
|
|
272
|
-
|
|
273
|
-
const destinationColumn = state.cellIds.get(columnId);
|
|
274
|
-
if (!destinationColumn) {
|
|
275
|
-
return state;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const clampedIndex = Math.max(0, Math.min(index, destinationColumn.length));
|
|
279
|
-
const adjustedIndex =
|
|
280
|
-
fromColumn.id === columnId && fromIndex < clampedIndex
|
|
281
|
-
? clampedIndex - 1
|
|
282
|
-
: clampedIndex;
|
|
283
|
-
|
|
284
|
-
if (fromColumn.id === columnId && fromIndex === adjustedIndex) {
|
|
285
|
-
return state;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const withoutCell = state.cellIds.deleteById(cellId);
|
|
289
|
-
const updatedColumn = withoutCell.get(columnId);
|
|
290
|
-
if (!updatedColumn) {
|
|
291
|
-
return state;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return {
|
|
295
|
-
...state,
|
|
296
|
-
cellIds: withoutCell.insertId(
|
|
297
|
-
cellId,
|
|
298
|
-
columnId,
|
|
299
|
-
Math.max(0, Math.min(adjustedIndex, updatedColumn.length)),
|
|
300
|
-
),
|
|
301
|
-
scrollKey: null,
|
|
302
|
-
};
|
|
303
|
-
},
|
|
304
|
-
dropCellOverCell: (state, action: { cellId: CellId; overCellId: CellId }) => {
|
|
305
|
-
const { cellId, overCellId } = action;
|
|
306
|
-
|
|
307
|
-
const fromColumn = state.cellIds.findWithId(cellId);
|
|
308
|
-
const toColumn = state.cellIds.findWithId(overCellId);
|
|
309
|
-
|
|
310
|
-
const fromIndex = fromColumn.indexOfOrThrow(cellId);
|
|
311
|
-
const toIndex = toColumn.indexOfOrThrow(overCellId);
|
|
312
|
-
|
|
313
|
-
if (fromColumn.id === toColumn.id) {
|
|
314
|
-
if (fromIndex === toIndex) {
|
|
315
|
-
return state;
|
|
316
|
-
}
|
|
317
|
-
return {
|
|
318
|
-
...state,
|
|
319
|
-
cellIds: state.cellIds.moveWithinColumn(
|
|
320
|
-
fromColumn.id,
|
|
321
|
-
fromIndex,
|
|
322
|
-
toIndex,
|
|
323
|
-
),
|
|
324
|
-
scrollKey: null,
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
return {
|
|
329
|
-
...state,
|
|
330
|
-
cellIds: state.cellIds.moveAcrossColumns(
|
|
331
|
-
fromColumn.id,
|
|
332
|
-
cellId,
|
|
333
|
-
toColumn.id,
|
|
334
|
-
overCellId,
|
|
335
|
-
),
|
|
336
|
-
scrollKey: null,
|
|
337
|
-
};
|
|
338
|
-
},
|
|
339
|
-
moveCellsRelativeTo: (
|
|
340
|
-
state,
|
|
341
|
-
action: {
|
|
342
|
-
cellIds: CellId[];
|
|
343
|
-
targetCellId: CellId;
|
|
344
|
-
position: "before" | "after";
|
|
345
|
-
previousPlacements?: Array<{ columnId: CellColumnId; index: CellIndex }>;
|
|
346
|
-
},
|
|
347
|
-
) => {
|
|
348
|
-
const { cellIds, targetCellId, position, previousPlacements } = action;
|
|
349
|
-
if (cellIds.length === 0) {
|
|
350
|
-
return state;
|
|
351
|
-
}
|
|
352
|
-
const newCellIds = state.cellIds.moveCellsRelativeTo(
|
|
353
|
-
cellIds,
|
|
354
|
-
targetCellId,
|
|
355
|
-
position,
|
|
356
|
-
);
|
|
357
|
-
// Only record undo when caller provided full before-state
|
|
358
|
-
const canUndoMove =
|
|
359
|
-
previousPlacements && previousPlacements.length === cellIds.length;
|
|
360
|
-
const history = canUndoMove
|
|
361
|
-
? [
|
|
362
|
-
...state.history,
|
|
363
|
-
{ type: "move" as const, cellIds, placements: previousPlacements },
|
|
364
|
-
]
|
|
365
|
-
: state.history;
|
|
366
|
-
return {
|
|
367
|
-
...state,
|
|
368
|
-
cellIds: newCellIds,
|
|
369
|
-
history,
|
|
370
|
-
scrollKey: null,
|
|
371
|
-
};
|
|
372
|
-
},
|
|
373
|
-
dropCellOverColumn: (
|
|
374
|
-
state,
|
|
375
|
-
action: { cellId: CellId; columnId: CellColumnId },
|
|
376
|
-
) => {
|
|
377
|
-
const { cellId, columnId } = action;
|
|
378
|
-
const fromColumn = state.cellIds.findWithId(cellId);
|
|
379
|
-
|
|
380
|
-
return {
|
|
381
|
-
...state,
|
|
382
|
-
cellIds: state.cellIds.moveAcrossColumns(
|
|
383
|
-
fromColumn.id,
|
|
384
|
-
cellId,
|
|
385
|
-
columnId,
|
|
386
|
-
undefined,
|
|
387
|
-
),
|
|
388
|
-
};
|
|
389
|
-
},
|
|
390
|
-
dropOverNewColumn: (state, action: { cellId: CellId }) => {
|
|
391
|
-
const { cellId } = action;
|
|
392
|
-
return {
|
|
393
|
-
...state,
|
|
394
|
-
cellIds: state.cellIds.moveToNewColumn(cellId),
|
|
395
|
-
};
|
|
396
|
-
},
|
|
397
167
|
moveColumn: (
|
|
398
168
|
state,
|
|
399
169
|
action: {
|
|
@@ -409,112 +179,6 @@ const {
|
|
|
409
179
|
cellIds: state.cellIds.moveColumn(action.column, action.overColumn),
|
|
410
180
|
};
|
|
411
181
|
},
|
|
412
|
-
focusCell: (
|
|
413
|
-
state,
|
|
414
|
-
action: { cellId: CellId; where: "before" | "after" | "exact" },
|
|
415
|
-
) => {
|
|
416
|
-
const column = state.cellIds.findWithId(action.cellId);
|
|
417
|
-
if (column.length === 0) {
|
|
418
|
-
return state;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
const { cellId, where } = action;
|
|
422
|
-
const index = column.indexOfOrThrow(cellId);
|
|
423
|
-
|
|
424
|
-
let focusIndex: number;
|
|
425
|
-
if (where === "before") {
|
|
426
|
-
focusIndex = clamp(index - 1, 0, column.length - 1);
|
|
427
|
-
} else if (where === "after") {
|
|
428
|
-
focusIndex = clamp(index + 1, 0, column.length - 1);
|
|
429
|
-
} else {
|
|
430
|
-
focusIndex = index;
|
|
431
|
-
}
|
|
432
|
-
const focusCellId = column.atOrThrow(focusIndex);
|
|
433
|
-
// can scroll immediately, without setting scrollKey in state, because
|
|
434
|
-
// CellArray won't need to re-render
|
|
435
|
-
focusAndScrollCellIntoView({
|
|
436
|
-
cellId: focusCellId,
|
|
437
|
-
cell: state.cellHandles[focusCellId],
|
|
438
|
-
isCodeHidden: isCellCodeHidden(state, focusCellId),
|
|
439
|
-
codeFocus: where === "after" ? "top" : "bottom",
|
|
440
|
-
variableName: undefined,
|
|
441
|
-
});
|
|
442
|
-
return state;
|
|
443
|
-
},
|
|
444
|
-
focusTopCell: (state) => {
|
|
445
|
-
// TODO: focus the existing column, not the first column
|
|
446
|
-
const column = state.cellIds.getColumns().at(0);
|
|
447
|
-
if (column === undefined || column.length === 0) {
|
|
448
|
-
return state;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
const cellId = column.first();
|
|
452
|
-
focusAndScrollCellIntoView({
|
|
453
|
-
cellId: cellId,
|
|
454
|
-
cell: state.cellHandles[cellId],
|
|
455
|
-
isCodeHidden: isCellCodeHidden(state, cellId),
|
|
456
|
-
codeFocus: undefined,
|
|
457
|
-
variableName: undefined,
|
|
458
|
-
});
|
|
459
|
-
scrollToTop();
|
|
460
|
-
return state;
|
|
461
|
-
},
|
|
462
|
-
focusBottomCell: (state) => {
|
|
463
|
-
// TODO: focus the existing column, not the last column
|
|
464
|
-
const column = state.cellIds.getColumns().at(-1);
|
|
465
|
-
if (column === undefined || column.length === 0) {
|
|
466
|
-
return state;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
const cellId = column.last();
|
|
470
|
-
focusAndScrollCellIntoView({
|
|
471
|
-
cellId: cellId,
|
|
472
|
-
cell: state.cellHandles[cellId],
|
|
473
|
-
isCodeHidden: isCellCodeHidden(state, cellId),
|
|
474
|
-
codeFocus: undefined,
|
|
475
|
-
variableName: undefined,
|
|
476
|
-
});
|
|
477
|
-
scrollToBottom();
|
|
478
|
-
return state;
|
|
479
|
-
},
|
|
480
|
-
sendToTop: (state, action: { cellId: CellId; scroll?: boolean }) => {
|
|
481
|
-
const column = state.cellIds.findWithId(action.cellId);
|
|
482
|
-
if (column.length === 0) {
|
|
483
|
-
return state;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
const { cellId, scroll = true } = action;
|
|
487
|
-
const cellIndex = column.indexOfOrThrow(cellId);
|
|
488
|
-
if (cellIndex === 0) {
|
|
489
|
-
return state;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
return {
|
|
493
|
-
...state,
|
|
494
|
-
cellIds: state.cellIds.moveWithinColumn(column.id, cellIndex, 0),
|
|
495
|
-
scrollKey: scroll ? cellId : null,
|
|
496
|
-
};
|
|
497
|
-
},
|
|
498
|
-
sendToBottom: (state, action: { cellId: CellId; scroll?: boolean }) => {
|
|
499
|
-
const column = state.cellIds.findWithId(action.cellId);
|
|
500
|
-
if (column.length === 0) {
|
|
501
|
-
return state;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
const { cellId, scroll = true } = action;
|
|
505
|
-
const cellIndex = column.indexOfOrThrow(cellId);
|
|
506
|
-
const newIndex = column.length - 1;
|
|
507
|
-
|
|
508
|
-
if (cellIndex === newIndex) {
|
|
509
|
-
return state;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
return {
|
|
513
|
-
...state,
|
|
514
|
-
cellIds: state.cellIds.moveWithinColumn(column.id, cellIndex, newIndex),
|
|
515
|
-
scrollKey: scroll ? cellId : null,
|
|
516
|
-
};
|
|
517
|
-
},
|
|
518
182
|
addColumn: (state, action: { columnId: CellColumnId }) => {
|
|
519
183
|
// Add column and new cell
|
|
520
184
|
const newCellId = CellId.create();
|
|
@@ -573,39 +237,6 @@ const {
|
|
|
573
237
|
*
|
|
574
238
|
* TODO(MO-8028): Move to `undoDeleteCellAtom` with the order family.
|
|
575
239
|
*/
|
|
576
|
-
undoMoveCells: (state, action: UndoMoveEntry) => {
|
|
577
|
-
const { cellIds, placements } = action;
|
|
578
|
-
if (
|
|
579
|
-
cellIds.length === 0 ||
|
|
580
|
-
placements.length !== cellIds.length ||
|
|
581
|
-
cellIds.some((id) => !state.cellData[id])
|
|
582
|
-
) {
|
|
583
|
-
return state;
|
|
584
|
-
}
|
|
585
|
-
const toRestore = cellIds.map((id, i) => ({
|
|
586
|
-
id,
|
|
587
|
-
columnId: placements[i].columnId,
|
|
588
|
-
index: placements[i].index,
|
|
589
|
-
}));
|
|
590
|
-
return {
|
|
591
|
-
...state,
|
|
592
|
-
cellIds: state.cellIds.placeCells(toRestore),
|
|
593
|
-
scrollKey: cellIds[0] ?? null,
|
|
594
|
-
};
|
|
595
|
-
},
|
|
596
|
-
clearSerializedEditorState: (state, action: { cellId: CellId }) => {
|
|
597
|
-
const { cellId } = action;
|
|
598
|
-
return updateCellData({
|
|
599
|
-
state,
|
|
600
|
-
cellId,
|
|
601
|
-
cellReducer: (cell) => {
|
|
602
|
-
return {
|
|
603
|
-
...cell,
|
|
604
|
-
serializedEditorState: null,
|
|
605
|
-
};
|
|
606
|
-
},
|
|
607
|
-
});
|
|
608
|
-
},
|
|
609
240
|
updateCellCode: (
|
|
610
241
|
state,
|
|
611
242
|
action: {
|
|
@@ -781,34 +412,6 @@ const {
|
|
|
781
412
|
}
|
|
782
413
|
return { ...state, cellRuntime };
|
|
783
414
|
},
|
|
784
|
-
markTouched: (state, action: { cellId: CellId }) => {
|
|
785
|
-
const { cellId } = action;
|
|
786
|
-
|
|
787
|
-
if (state.untouchedNewCells.has(cellId)) {
|
|
788
|
-
const nextUntouchedNewCells = new Set(state.untouchedNewCells);
|
|
789
|
-
nextUntouchedNewCells.delete(cellId);
|
|
790
|
-
return {
|
|
791
|
-
...state,
|
|
792
|
-
untouchedNewCells: nextUntouchedNewCells,
|
|
793
|
-
};
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
return state;
|
|
797
|
-
},
|
|
798
|
-
markUntouched: (state, action: { cellId: CellId }) => {
|
|
799
|
-
const { cellId } = action;
|
|
800
|
-
|
|
801
|
-
if (!state.untouchedNewCells.has(cellId)) {
|
|
802
|
-
const nextUntouchedNewCells = new Set(state.untouchedNewCells);
|
|
803
|
-
nextUntouchedNewCells.add(cellId);
|
|
804
|
-
return {
|
|
805
|
-
...state,
|
|
806
|
-
untouchedNewCells: nextUntouchedNewCells,
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
return state;
|
|
811
|
-
},
|
|
812
415
|
scrollToTarget: (state) => {
|
|
813
416
|
// Scroll to the specified cell and clear the scroll key.
|
|
814
417
|
const scrollKey = state.scrollKey;
|
|
@@ -825,7 +428,11 @@ const {
|
|
|
825
428
|
focusAndScrollCellIntoView({
|
|
826
429
|
cellId: cellId,
|
|
827
430
|
cell: state.cellHandles[cellId],
|
|
828
|
-
isCodeHidden:
|
|
431
|
+
isCodeHidden: isCodeHidden(
|
|
432
|
+
state.cellData[cellId]?.config.hide_code,
|
|
433
|
+
store.get(shownHiddenCodeAtom),
|
|
434
|
+
cellId,
|
|
435
|
+
),
|
|
829
436
|
codeFocus: undefined,
|
|
830
437
|
variableName: undefined,
|
|
831
438
|
});
|
|
@@ -855,108 +462,6 @@ const {
|
|
|
855
462
|
cellLogs: [],
|
|
856
463
|
};
|
|
857
464
|
},
|
|
858
|
-
collapseCell: (state, action: { cellId: CellId }) => {
|
|
859
|
-
const { cellId } = action;
|
|
860
|
-
const column = state.cellIds.findWithId(cellId);
|
|
861
|
-
|
|
862
|
-
// Get all the top-level outlines
|
|
863
|
-
const outlines = column.topLevelIds.map((id) => {
|
|
864
|
-
const cell = state.cellRuntime[id];
|
|
865
|
-
return cell.outline;
|
|
866
|
-
});
|
|
867
|
-
|
|
868
|
-
// Find the start/end of the collapsed range
|
|
869
|
-
const startIndex = column.indexOfOrThrow(cellId);
|
|
870
|
-
const range = findCollapseRange(startIndex, outlines);
|
|
871
|
-
if (!range) {
|
|
872
|
-
return state;
|
|
873
|
-
}
|
|
874
|
-
const endCellId = column.atOrThrow(range[1]);
|
|
875
|
-
|
|
876
|
-
return {
|
|
877
|
-
...state,
|
|
878
|
-
// Collapse the range
|
|
879
|
-
cellIds: state.cellIds.transformWithCellId(cellId, (column) =>
|
|
880
|
-
column.collapse(cellId, endCellId),
|
|
881
|
-
),
|
|
882
|
-
scrollKey: cellId,
|
|
883
|
-
};
|
|
884
|
-
},
|
|
885
|
-
expandCell: (state, action: { cellId: CellId }) => {
|
|
886
|
-
const { cellId } = action;
|
|
887
|
-
return {
|
|
888
|
-
...state,
|
|
889
|
-
cellIds: state.cellIds.transformWithCellId(cellId, (column) =>
|
|
890
|
-
column.expand(cellId),
|
|
891
|
-
),
|
|
892
|
-
scrollKey: cellId,
|
|
893
|
-
};
|
|
894
|
-
},
|
|
895
|
-
collapseAllCells: (state) => {
|
|
896
|
-
return {
|
|
897
|
-
...state,
|
|
898
|
-
cellIds: state.cellIds.transformAll((column) => {
|
|
899
|
-
// Get all the top-level outlines
|
|
900
|
-
const outlines = column.topLevelIds.map((id) => {
|
|
901
|
-
const cell = state.cellRuntime[id];
|
|
902
|
-
return cell.outline;
|
|
903
|
-
});
|
|
904
|
-
|
|
905
|
-
// Find the start/end of the collapsed ranges
|
|
906
|
-
const nodes = [...column.nodes];
|
|
907
|
-
const visibleEndByOriginalEnd = new Map<CellIndex, CellIndex>();
|
|
908
|
-
const reversedCollapseRanges = [];
|
|
909
|
-
|
|
910
|
-
// Iterate in reverse order (bottom-up) to process children first
|
|
911
|
-
let i = nodes.length - 1;
|
|
912
|
-
while (i >= 0) {
|
|
913
|
-
const range = findCollapseRange(i, outlines);
|
|
914
|
-
if (range) {
|
|
915
|
-
const startIndex = i;
|
|
916
|
-
const originalEnd = range[1];
|
|
917
|
-
const endIndex =
|
|
918
|
-
visibleEndByOriginalEnd.get(originalEnd) ?? originalEnd;
|
|
919
|
-
|
|
920
|
-
// After this section collapses, its heading represents this
|
|
921
|
-
// endpoint for any enclosing section that shares it.
|
|
922
|
-
visibleEndByOriginalEnd.set(originalEnd, startIndex);
|
|
923
|
-
|
|
924
|
-
// Add the range to the list of ranges
|
|
925
|
-
const cellId = column.atOrThrow(startIndex);
|
|
926
|
-
const until = column.atOrThrow(endIndex);
|
|
927
|
-
reversedCollapseRanges.push({ id: cellId, until });
|
|
928
|
-
} else {
|
|
929
|
-
reversedCollapseRanges.push(null);
|
|
930
|
-
}
|
|
931
|
-
i--;
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
const collapseRanges = reversedCollapseRanges.toReversed();
|
|
935
|
-
return column.collapseAll(collapseRanges);
|
|
936
|
-
}),
|
|
937
|
-
};
|
|
938
|
-
},
|
|
939
|
-
expandAllCells: (state) => {
|
|
940
|
-
return {
|
|
941
|
-
...state,
|
|
942
|
-
cellIds: state.cellIds.transformAll((column) => column.expandAll()),
|
|
943
|
-
};
|
|
944
|
-
},
|
|
945
|
-
showCellIfHidden: (state, action: { cellId: CellId }) => {
|
|
946
|
-
const { cellId } = action;
|
|
947
|
-
const column = state.cellIds.findWithId(cellId);
|
|
948
|
-
const prev = column;
|
|
949
|
-
const result = column.findAndExpandDeep(cellId);
|
|
950
|
-
|
|
951
|
-
if (result.equals(prev)) {
|
|
952
|
-
return state;
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
return {
|
|
956
|
-
...state,
|
|
957
|
-
cellIds: state.cellIds.transformWithCellId(cellId, () => result),
|
|
958
|
-
};
|
|
959
|
-
},
|
|
960
465
|
splitCell: (state, action: { cellId: CellId }) => {
|
|
961
466
|
const { cellId } = action;
|
|
962
467
|
const column = state.cellIds.findWithId(cellId);
|
|
@@ -1108,13 +613,6 @@ const {
|
|
|
1108
613
|
// strictly type the dispatched actions without creating a circular dependency.
|
|
1109
614
|
addMiddleware(documentEditMiddleware);
|
|
1110
615
|
|
|
1111
|
-
function isCellCodeHidden(state: NotebookState, cellId: CellId): boolean {
|
|
1112
|
-
return (
|
|
1113
|
-
Boolean(state.cellData[cellId].config.hide_code) &&
|
|
1114
|
-
!state.untouchedNewCells.has(cellId)
|
|
1115
|
-
);
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
616
|
// Helper function to update a cell in the array
|
|
1119
617
|
function updateCellRuntimeState({
|
|
1120
618
|
state,
|
|
@@ -1266,6 +764,24 @@ export const cellErrorsAtom = atom((get) => {
|
|
|
1266
764
|
return errors;
|
|
1267
765
|
});
|
|
1268
766
|
|
|
767
|
+
/**
|
|
768
|
+
* The outline of each cell whose output has a heading. Compared by value, so
|
|
769
|
+
* readers are left alone by the runtime messages that change no heading.
|
|
770
|
+
*/
|
|
771
|
+
export const cellOutlinesAtom = createDeepEqualAtom(
|
|
772
|
+
atom((get) => {
|
|
773
|
+
const outlines: Partial<Record<CellId, Outline>> = {};
|
|
774
|
+
for (const [id, runtime] of Objects.entries(
|
|
775
|
+
get(notebookAtom).cellRuntime,
|
|
776
|
+
)) {
|
|
777
|
+
if (runtime.outline !== null && runtime.outline.items.length > 0) {
|
|
778
|
+
outlines[id] = runtime.outline;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
return outlines;
|
|
782
|
+
}),
|
|
783
|
+
);
|
|
784
|
+
|
|
1269
785
|
export const notebookOutline = atom((get) => {
|
|
1270
786
|
const cellIds = get(cellIdsAtom);
|
|
1271
787
|
const cellRuntime = get(cellsRuntimeAtom);
|
|
@@ -1445,10 +961,6 @@ export function flattenTopLevelNotebookCells(
|
|
|
1445
961
|
);
|
|
1446
962
|
}
|
|
1447
963
|
|
|
1448
|
-
export function createUntouchedCellAtom(cellId: CellId): Atom<boolean> {
|
|
1449
|
-
return atom((get) => get(notebookAtom).untouchedNewCells.has(cellId));
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
964
|
export function createTracebackInfoAtom(
|
|
1453
965
|
cellId: CellId,
|
|
1454
966
|
): Atom<TracebackInfo[] | undefined> {
|
|
@@ -1523,7 +1035,6 @@ export const exportedForTesting = {
|
|
|
1523
1035
|
reducer,
|
|
1524
1036
|
createActions,
|
|
1525
1037
|
initialNotebookState,
|
|
1526
|
-
isCellCodeHidden,
|
|
1527
1038
|
// Export atom families for testing cleanup
|
|
1528
1039
|
cellDataAtom,
|
|
1529
1040
|
cellRuntimeAtom,
|
|
@@ -42,7 +42,7 @@ type CellAction = DispatchedActionOf<ReducerCellActions>;
|
|
|
42
42
|
|
|
43
43
|
// Following the document ----------------------------------------------------
|
|
44
44
|
|
|
45
|
-
/** Rebuild the tree for `columns`, keeping column ids
|
|
45
|
+
/** Rebuild the tree for `columns`, keeping column ids. */
|
|
46
46
|
function reconcileColumns(
|
|
47
47
|
current: MultiColumn<CellId>,
|
|
48
48
|
columns: CellId[][],
|
|
@@ -59,9 +59,7 @@ function reconcileColumns(
|
|
|
59
59
|
return current;
|
|
60
60
|
}
|
|
61
61
|
return new MultiColumn(
|
|
62
|
-
columns.map((ids, index) =>
|
|
63
|
-
CollapsibleTree.fromWithPreviousShape(ids, previous[index]),
|
|
64
|
-
),
|
|
62
|
+
columns.map((ids, index) => CollapsibleTree.from(ids, previous[index]?.id)),
|
|
65
63
|
);
|
|
66
64
|
}
|
|
67
65
|
|
|
@@ -139,7 +137,10 @@ export function applyDocument(
|
|
|
139
137
|
edited: code.trim().length > 0,
|
|
140
138
|
}),
|
|
141
139
|
};
|
|
142
|
-
cellRuntime = {
|
|
140
|
+
cellRuntime = {
|
|
141
|
+
...cellRuntime,
|
|
142
|
+
[id]: cellRuntime[id] ?? createCellRuntimeState(),
|
|
143
|
+
};
|
|
143
144
|
cellHandles = { ...cellHandles, [id]: createRef() };
|
|
144
145
|
continue;
|
|
145
146
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/** The active document model, as the notebook operations reach it. */
|
|
4
|
+
|
|
5
|
+
import type { Getter, Setter } from "jotai";
|
|
6
|
+
import type { DocumentModel, EditableDocument } from "@/core/notebook/document";
|
|
7
|
+
import { activeDocumentModelAtom } from "../session/active-notebook";
|
|
8
|
+
import { notebookAtom } from "./cells";
|
|
9
|
+
import type { CellId } from "./ids";
|
|
10
|
+
|
|
11
|
+
export function modelOf(get: Getter): DocumentModel {
|
|
12
|
+
const model = get(activeDocumentModelAtom);
|
|
13
|
+
if (!model) {
|
|
14
|
+
throw new Error("The notebook has no document to edit");
|
|
15
|
+
}
|
|
16
|
+
return model;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function documentOf(get: Getter): EditableDocument {
|
|
20
|
+
const document = get(modelOf(get).documentAtom);
|
|
21
|
+
if (!document) {
|
|
22
|
+
throw new Error("The notebook has no document to edit");
|
|
23
|
+
}
|
|
24
|
+
return document;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Scroll to and focus the cell once it mounts. */
|
|
28
|
+
export function focusWhenMounted(set: Setter, cellId: CellId): void {
|
|
29
|
+
set(notebookAtom, (state) => ({ ...state, scrollKey: cellId }));
|
|
30
|
+
}
|