@marimo-team/islands 0.25.3-dev192 → 0.25.3-dev193
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-BVu_xT-p.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-BTVimCUw.js} +38 -42
- package/dist/{common-BUwWHpnK.js → common-fTgq7gO-.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-DAZZkLWg.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-DzFFn-n4.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-DFsqVbWR.js} +5 -5
- package/dist/{globals-BeBDOk22.js → globals-Dr0MRANu.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-DupOSYHx.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-Bg8rP_Uo.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 +15 -0
- 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
|
@@ -3,26 +3,54 @@
|
|
|
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,
|
|
6
|
+
import { atom, type Getter, useSetAtom } from "jotai";
|
|
7
7
|
import { useMemo } from "react";
|
|
8
8
|
import { scrollAndHighlightCell } from "@/components/editor/links/cell-link";
|
|
9
|
-
import type {
|
|
9
|
+
import type { EditableDocument } from "@/core/notebook/document";
|
|
10
10
|
import type { CellColumnId } from "@/utils/id-tree";
|
|
11
11
|
import { Logger } from "@/utils/Logger";
|
|
12
|
+
import { clamp } from "@/utils/math";
|
|
12
13
|
import type { SerializedEditorState } from "../codemirror/types";
|
|
14
|
+
import { canCollapseOutline } from "../dom/outline";
|
|
13
15
|
import type { CellConfig } from "../network/types";
|
|
14
16
|
import { store } from "../state/jotai";
|
|
15
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
dropCellOverCellAtom,
|
|
19
|
+
dropCellOverColumnAtom,
|
|
20
|
+
moveCellAtom,
|
|
21
|
+
moveCellsRelativeToAtom,
|
|
22
|
+
moveCellToIndexAtom,
|
|
23
|
+
placeCells,
|
|
24
|
+
sendToBottomAtom,
|
|
25
|
+
sendToTopAtom,
|
|
26
|
+
} from "./arrange";
|
|
16
27
|
import {
|
|
17
28
|
type DeletedCell,
|
|
18
29
|
notebookAtom,
|
|
19
|
-
notebookReducer,
|
|
20
30
|
releaseCellAtoms,
|
|
21
31
|
useReducerActions,
|
|
22
32
|
} from "./cells";
|
|
23
33
|
import { resolveColumns } from "./columns";
|
|
34
|
+
import { documentOf, focusWhenMounted, modelOf } from "./edit";
|
|
35
|
+
import {
|
|
36
|
+
isCodeHidden,
|
|
37
|
+
restoredEditorStatesAtom,
|
|
38
|
+
shownHiddenCodeAtom,
|
|
39
|
+
} from "./editor";
|
|
24
40
|
import { CellId, SCRATCH_CELL_ID, SETUP_CELL_ID } from "./ids";
|
|
25
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
collapsedAtom,
|
|
43
|
+
columnOf,
|
|
44
|
+
foldOf,
|
|
45
|
+
foldsAfter,
|
|
46
|
+
foldsHiding,
|
|
47
|
+
layoutAtom,
|
|
48
|
+
} from "./layout";
|
|
49
|
+
import {
|
|
50
|
+
focusAndScrollCellIntoView,
|
|
51
|
+
scrollToBottom,
|
|
52
|
+
scrollToTop,
|
|
53
|
+
} from "./scrollCellIntoView";
|
|
26
54
|
|
|
27
55
|
export interface CreateCellOptions {
|
|
28
56
|
/** Place the cell after this one. */
|
|
@@ -45,22 +73,6 @@ export interface CreateCellOptions {
|
|
|
45
73
|
|
|
46
74
|
export type CreateCell = (options?: CreateCellOptions) => CellId | null;
|
|
47
75
|
|
|
48
|
-
function modelOf(get: Getter): DocumentModel {
|
|
49
|
-
const model = get(activeDocumentModelAtom);
|
|
50
|
-
if (!model) {
|
|
51
|
-
throw new Error("The notebook has no document to edit");
|
|
52
|
-
}
|
|
53
|
-
return model;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function documentOf(get: Getter): EditableDocument {
|
|
57
|
-
const document = get(modelOf(get).documentAtom);
|
|
58
|
-
if (!document) {
|
|
59
|
-
throw new Error("The notebook has no document to edit");
|
|
60
|
-
}
|
|
61
|
-
return document;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
76
|
function hasCellWithCode(document: EditableDocument, code: string): boolean {
|
|
65
77
|
for (const cell of document.cells.values()) {
|
|
66
78
|
if (cell.code === code) {
|
|
@@ -70,11 +82,6 @@ function hasCellWithCode(document: EditableDocument, code: string): boolean {
|
|
|
70
82
|
return false;
|
|
71
83
|
}
|
|
72
84
|
|
|
73
|
-
/** Scroll to and focus the cell once it mounts. */
|
|
74
|
-
function focusWhenMounted(set: Setter, cellId: CellId): void {
|
|
75
|
-
set(notebookAtom, (state) => ({ ...state, scrollKey: cellId }));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
85
|
/** Create a cell. Returns its id, or null when it was skipped. */
|
|
79
86
|
export const createCellAtom = atom(
|
|
80
87
|
null,
|
|
@@ -91,12 +98,19 @@ export const createCellAtom = atom(
|
|
|
91
98
|
// column, so a cell placed first in a later column names its column.
|
|
92
99
|
let placement: { after?: CellId; before?: CellId } = {};
|
|
93
100
|
let column: number | null = null;
|
|
101
|
+
// The cell the new one comes right after, whose folds would hide it.
|
|
102
|
+
let follows: CellId | undefined;
|
|
103
|
+
const layout = get(layoutAtom);
|
|
94
104
|
if (options.after) {
|
|
95
|
-
|
|
105
|
+
// A folded header hides the cells after it, so a cell placed after
|
|
106
|
+
// the header goes after its fold.
|
|
107
|
+
follows = foldOf(layout, options.after)?.at(-1) ?? options.after;
|
|
108
|
+
placement = { after: follows };
|
|
96
109
|
} else if (options.before) {
|
|
97
110
|
const tree = cellIds.findWithId(options.before);
|
|
98
111
|
const index = cellIds.indexOf(tree);
|
|
99
112
|
placement = { before: options.before };
|
|
113
|
+
follows = tree.before(options.before);
|
|
100
114
|
if (index > 0 && tree.indexOfOrThrow(options.before) === 0) {
|
|
101
115
|
column = index;
|
|
102
116
|
}
|
|
@@ -104,7 +118,8 @@ export const createCellAtom = atom(
|
|
|
104
118
|
const tree =
|
|
105
119
|
(options.endOf && cellIds.get(options.endOf)) ?? cellIds.atOrThrow(0);
|
|
106
120
|
if (tree.length > 0) {
|
|
107
|
-
|
|
121
|
+
follows = tree.last();
|
|
122
|
+
placement = { after: follows };
|
|
108
123
|
} else if (cellIds.colLength > 1) {
|
|
109
124
|
column = cellIds.indexOf(tree);
|
|
110
125
|
}
|
|
@@ -120,13 +135,22 @@ export const createCellAtom = atom(
|
|
|
120
135
|
}
|
|
121
136
|
});
|
|
122
137
|
if (focus) {
|
|
138
|
+
// The new cell has no heading, so every section that runs past the
|
|
139
|
+
// cell before it holds it too. Those open, so the cell is on the page.
|
|
140
|
+
const hiding = follows === undefined ? [] : foldsAfter(layout, follows);
|
|
141
|
+
if (hiding.length > 0) {
|
|
142
|
+
set(collapsedAtom, (collapsed) => {
|
|
143
|
+
const next = new Set(collapsed);
|
|
144
|
+
for (const header of hiding) {
|
|
145
|
+
next.delete(header);
|
|
146
|
+
}
|
|
147
|
+
return next;
|
|
148
|
+
});
|
|
149
|
+
}
|
|
123
150
|
focusWhenMounted(set, id);
|
|
124
|
-
// Hidden code stays
|
|
151
|
+
// Hidden code stays showing until the cell is blurred for the first time.
|
|
125
152
|
if (config.hide_code) {
|
|
126
|
-
set(
|
|
127
|
-
...state,
|
|
128
|
-
untouchedNewCells: new Set([...state.untouchedNewCells, id]),
|
|
129
|
-
}));
|
|
153
|
+
set(shownHiddenCodeAtom, (cells) => new Set([...cells, id]));
|
|
130
154
|
}
|
|
131
155
|
}
|
|
132
156
|
return id;
|
|
@@ -151,26 +175,140 @@ export const moveToNextCellAtom = atom(
|
|
|
151
175
|
if (cellId === SCRATCH_CELL_ID) {
|
|
152
176
|
return;
|
|
153
177
|
}
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
const index = column.indexOfOrThrow(cellId);
|
|
178
|
+
const { visible } = columnOf(get(layoutAtom), cellId);
|
|
179
|
+
const index = visible.indexOf(cellId);
|
|
157
180
|
const nextIndex = before ? index - 1 : index + 1;
|
|
158
|
-
if (nextIndex < 0 || nextIndex >=
|
|
181
|
+
if (index === -1 || nextIndex < 0 || nextIndex >= visible.length) {
|
|
159
182
|
if (create) {
|
|
160
183
|
set(createCellAtom, before ? { before: cellId } : { after: cellId });
|
|
161
184
|
}
|
|
162
185
|
return;
|
|
163
186
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
187
|
+
focusOnPage(get, visible[nextIndex], before ? "bottom" : "top");
|
|
188
|
+
},
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
/** Focus a cell on the page, scrolling to it. */
|
|
192
|
+
function focusOnPage(
|
|
193
|
+
get: Getter,
|
|
194
|
+
cellId: CellId,
|
|
195
|
+
codeFocus: "top" | "bottom" | undefined,
|
|
196
|
+
): void {
|
|
197
|
+
const notebook = get(notebookAtom);
|
|
198
|
+
focusAndScrollCellIntoView({
|
|
199
|
+
cellId,
|
|
200
|
+
cell: notebook.cellHandles[cellId],
|
|
201
|
+
isCodeHidden: isCodeHidden(
|
|
202
|
+
notebook.cellData[cellId]?.config.hide_code,
|
|
203
|
+
get(shownHiddenCodeAtom),
|
|
204
|
+
cellId,
|
|
205
|
+
),
|
|
206
|
+
codeFocus,
|
|
207
|
+
variableName: undefined,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Focus the visible cell before, after, or at `cellId` within its column. */
|
|
212
|
+
export const focusCellAtom = atom(
|
|
213
|
+
null,
|
|
214
|
+
(
|
|
215
|
+
get,
|
|
216
|
+
_set,
|
|
217
|
+
{ cellId, where }: { cellId: CellId; where: "before" | "after" | "exact" },
|
|
218
|
+
): void => {
|
|
219
|
+
const { visible } = columnOf(get(layoutAtom), cellId);
|
|
220
|
+
const index = visible.indexOf(cellId);
|
|
221
|
+
if (index === -1) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const offset = where === "before" ? -1 : where === "after" ? 1 : 0;
|
|
225
|
+
const target = clamp(index + offset, 0, visible.length - 1);
|
|
226
|
+
focusOnPage(get, visible[target], where === "after" ? "top" : "bottom");
|
|
227
|
+
},
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
/** Focus the first visible cell of the first column. */
|
|
231
|
+
export const focusTopCellAtom = atom(null, (get): void => {
|
|
232
|
+
const first = get(layoutAtom).columns[0]?.visible[0];
|
|
233
|
+
if (first === undefined) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
focusOnPage(get, first, undefined);
|
|
237
|
+
scrollToTop();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
/** Focus the last visible cell of the last column. */
|
|
241
|
+
export const focusBottomCellAtom = atom(null, (get): void => {
|
|
242
|
+
const last = get(layoutAtom).columns.at(-1)?.visible.at(-1);
|
|
243
|
+
if (last === undefined) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
focusOnPage(get, last, undefined);
|
|
247
|
+
scrollToBottom();
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Folding ---------------------------------------------------------------------
|
|
251
|
+
//
|
|
252
|
+
// A folded header hides the cells of its section. Which headers are folded is
|
|
253
|
+
// editor state; the section each one hides derives from the outlines.
|
|
254
|
+
|
|
255
|
+
/** Fold the section under `cellId`, if its output has a heading. */
|
|
256
|
+
export const collapseCellAtom = atom(
|
|
257
|
+
null,
|
|
258
|
+
(get, set, { cellId }: { cellId: CellId }): void => {
|
|
259
|
+
if (!canCollapseOutline(get(notebookAtom).cellRuntime[cellId]?.outline)) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
set(collapsedAtom, (collapsed) => new Set([...collapsed, cellId]));
|
|
263
|
+
focusWhenMounted(set, cellId);
|
|
264
|
+
},
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
/** Unfold the section under `cellId`. */
|
|
268
|
+
export const expandCellAtom = atom(
|
|
269
|
+
null,
|
|
270
|
+
(_get, set, { cellId }: { cellId: CellId }): void => {
|
|
271
|
+
set(collapsedAtom, (collapsed) => {
|
|
272
|
+
const next = new Set(collapsed);
|
|
273
|
+
next.delete(cellId);
|
|
274
|
+
return next;
|
|
275
|
+
});
|
|
276
|
+
focusWhenMounted(set, cellId);
|
|
277
|
+
},
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
/** Fold every section in the notebook. */
|
|
281
|
+
export const collapseAllCellsAtom = atom(null, (get, set): void => {
|
|
282
|
+
const { cellIds, cellRuntime } = get(notebookAtom);
|
|
283
|
+
set(
|
|
284
|
+
collapsedAtom,
|
|
285
|
+
new Set(
|
|
286
|
+
cellIds.inOrderIds.filter((id) =>
|
|
287
|
+
canCollapseOutline(cellRuntime[id]?.outline),
|
|
288
|
+
),
|
|
289
|
+
),
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
/** Unfold every section. */
|
|
294
|
+
export const expandAllCellsAtom = atom(null, (_get, set): void => {
|
|
295
|
+
set(collapsedAtom, new Set());
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
/** Unfold whatever hides `cellId`, so it is on the page. */
|
|
299
|
+
export const showCellIfHiddenAtom = atom(
|
|
300
|
+
null,
|
|
301
|
+
(get, set, { cellId }: { cellId: CellId }): void => {
|
|
302
|
+
const hiding = foldsHiding(get(layoutAtom), cellId);
|
|
303
|
+
if (hiding.length === 0) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
set(collapsedAtom, (collapsed) => {
|
|
307
|
+
const next = new Set(collapsed);
|
|
308
|
+
for (const header of hiding) {
|
|
309
|
+
next.delete(header);
|
|
310
|
+
}
|
|
311
|
+
return next;
|
|
174
312
|
});
|
|
175
313
|
},
|
|
176
314
|
);
|
|
@@ -247,9 +385,10 @@ export const deleteCellsAtom = atom(
|
|
|
247
385
|
};
|
|
248
386
|
});
|
|
249
387
|
|
|
250
|
-
// Focus the nearest survivor in the column, looking up before down.
|
|
388
|
+
// Focus the nearest visible survivor in the column, looking up before down.
|
|
251
389
|
const first = deleting[0].id as CellId;
|
|
252
|
-
const
|
|
390
|
+
const column = columnOf(get(layoutAtom), first);
|
|
391
|
+
const ids = column.visible.includes(first) ? column.visible : column.ids;
|
|
253
392
|
const at = ids.indexOf(first);
|
|
254
393
|
const scrollKey =
|
|
255
394
|
ids.slice(0, at).findLast((id) => !gone.has(id)) ??
|
|
@@ -285,12 +424,19 @@ export const deleteCellsAtom = atom(
|
|
|
285
424
|
for (const id of gone) {
|
|
286
425
|
releaseCellAtoms(id);
|
|
287
426
|
}
|
|
427
|
+
set(shownHiddenCodeAtom, (cells) =>
|
|
428
|
+
cells.size === 0
|
|
429
|
+
? cells
|
|
430
|
+
: new Set([...cells].filter((id) => !gone.has(id))),
|
|
431
|
+
);
|
|
432
|
+
set(restoredEditorStatesAtom, (states) =>
|
|
433
|
+
[...states.keys()].some((id) => gone.has(id))
|
|
434
|
+
? new Map([...states].filter(([id]) => !gone.has(id)))
|
|
435
|
+
: states,
|
|
436
|
+
);
|
|
288
437
|
set(notebookAtom, (state) => ({
|
|
289
438
|
...state,
|
|
290
439
|
history: [...state.history, { type: "delete", cells }],
|
|
291
|
-
untouchedNewCells: new Set(
|
|
292
|
-
[...state.untouchedNewCells].filter((id) => !gone.has(id)),
|
|
293
|
-
),
|
|
294
440
|
scrollKey,
|
|
295
441
|
}));
|
|
296
442
|
return true;
|
|
@@ -304,12 +450,12 @@ export const undoDeleteCellAtom = atom(null, (get, set): void => {
|
|
|
304
450
|
return;
|
|
305
451
|
}
|
|
306
452
|
if (last.type === "move") {
|
|
307
|
-
set(notebookAtom, (state) =>
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
);
|
|
453
|
+
set(notebookAtom, (state) => ({
|
|
454
|
+
...state,
|
|
455
|
+
history: state.history.slice(0, -1),
|
|
456
|
+
}));
|
|
457
|
+
placeCells(get, set, last.cellIds, last.placements);
|
|
458
|
+
focusWhenMounted(set, last.cellIds[0]);
|
|
313
459
|
return;
|
|
314
460
|
}
|
|
315
461
|
const model = modelOf(get);
|
|
@@ -328,32 +474,21 @@ export const undoDeleteCellAtom = atom(null, (get, set): void => {
|
|
|
328
474
|
restored.add(cell.id);
|
|
329
475
|
}
|
|
330
476
|
});
|
|
331
|
-
//
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
if (document !== null) {
|
|
335
|
-
set(notebookAtom, (state) =>
|
|
336
|
-
notebookReducer(state, { type: "reconcileDocument", payload: document }),
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
set(notebookAtom, (state) => {
|
|
340
|
-
let { cellData } = state;
|
|
477
|
+
// The editors take their history back when they mount.
|
|
478
|
+
set(restoredEditorStatesAtom, (states) => {
|
|
479
|
+
const next = new Map(states);
|
|
341
480
|
for (const { cell, serializedEditorState } of last.cells) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
cellData = {
|
|
345
|
-
...cellData,
|
|
346
|
-
[id]: { ...cellData[id], serializedEditorState },
|
|
347
|
-
};
|
|
481
|
+
if (serializedEditorState !== null) {
|
|
482
|
+
next.set(cell.id as CellId, serializedEditorState);
|
|
348
483
|
}
|
|
349
484
|
}
|
|
350
|
-
return
|
|
351
|
-
...state,
|
|
352
|
-
cellData,
|
|
353
|
-
history: state.history.slice(0, -1),
|
|
354
|
-
scrollKey: last.cells[0].cell.id as CellId,
|
|
355
|
-
};
|
|
485
|
+
return next;
|
|
356
486
|
});
|
|
487
|
+
set(notebookAtom, (state) => ({
|
|
488
|
+
...state,
|
|
489
|
+
history: state.history.slice(0, -1),
|
|
490
|
+
scrollKey: last.cells[0].cell.id as CellId,
|
|
491
|
+
}));
|
|
357
492
|
});
|
|
358
493
|
|
|
359
494
|
/**
|
|
@@ -378,18 +513,48 @@ export function useCellActions() {
|
|
|
378
513
|
const addSetupCell = useSetAtom(addSetupCellAtom);
|
|
379
514
|
const deleteCells = useSetAtom(deleteCellsAtom);
|
|
380
515
|
const undoDeleteCell = useSetAtom(undoDeleteCellAtom);
|
|
516
|
+
const focusCell = useSetAtom(focusCellAtom);
|
|
517
|
+
const focusTopCell = useSetAtom(focusTopCellAtom);
|
|
518
|
+
const focusBottomCell = useSetAtom(focusBottomCellAtom);
|
|
519
|
+
const collapseCell = useSetAtom(collapseCellAtom);
|
|
520
|
+
const expandCell = useSetAtom(expandCellAtom);
|
|
521
|
+
const collapseAllCells = useSetAtom(collapseAllCellsAtom);
|
|
522
|
+
const expandAllCells = useSetAtom(expandAllCellsAtom);
|
|
523
|
+
const showCellIfHidden = useSetAtom(showCellIfHiddenAtom);
|
|
524
|
+
const moveCell = useSetAtom(moveCellAtom);
|
|
525
|
+
const moveCellToIndex = useSetAtom(moveCellToIndexAtom);
|
|
526
|
+
const moveCellsRelativeTo = useSetAtom(moveCellsRelativeToAtom);
|
|
527
|
+
const dropCellOverCell = useSetAtom(dropCellOverCellAtom);
|
|
528
|
+
const dropCellOverColumn = useSetAtom(dropCellOverColumnAtom);
|
|
529
|
+
const sendToTop = useSetAtom(sendToTopAtom);
|
|
530
|
+
const sendToBottom = useSetAtom(sendToBottomAtom);
|
|
381
531
|
return useMemo(
|
|
382
532
|
() => ({
|
|
383
533
|
...operations,
|
|
534
|
+
moveCell,
|
|
535
|
+
moveCellToIndex,
|
|
536
|
+
moveCellsRelativeTo,
|
|
537
|
+
dropCellOverCell,
|
|
538
|
+
dropCellOverColumn,
|
|
539
|
+
sendToTop,
|
|
540
|
+
sendToBottom,
|
|
384
541
|
createCell,
|
|
385
542
|
moveToNextCell,
|
|
386
543
|
addSetupCell,
|
|
387
544
|
deleteCells,
|
|
388
545
|
undoDeleteCell,
|
|
546
|
+
focusCell,
|
|
547
|
+
focusTopCell,
|
|
548
|
+
focusBottomCell,
|
|
549
|
+
collapseCell,
|
|
550
|
+
expandCell,
|
|
551
|
+
collapseAllCells,
|
|
552
|
+
expandAllCells,
|
|
553
|
+
showCellIfHidden,
|
|
389
554
|
}),
|
|
390
|
-
// The reducer's actions are stable for the store's lifetime.
|
|
555
|
+
// The reducer's actions and the atom setters are stable for the store's lifetime.
|
|
391
556
|
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
392
|
-
[
|
|
557
|
+
[],
|
|
393
558
|
);
|
|
394
559
|
}
|
|
395
560
|
|