@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
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
createCellRuntimeState,
|
|
12
12
|
} from "@/core/cells/types";
|
|
13
13
|
import type { MarimoError } from "@/core/kernel/messages";
|
|
14
|
-
import { MultiColumn } from "@/utils/id-tree";
|
|
15
14
|
import { Objects } from "@/utils/objects";
|
|
16
15
|
|
|
17
16
|
export const MockNotebook = {
|
|
@@ -46,7 +45,7 @@ export const MockNotebook = {
|
|
|
46
45
|
serializedEditorState: null,
|
|
47
46
|
...data,
|
|
48
47
|
})),
|
|
49
|
-
cellIds:
|
|
48
|
+
cellIds: Object.keys(cellData) as CellId[],
|
|
50
49
|
cellRuntime: Objects.mapValues(cellData, (_data, cid) =>
|
|
51
50
|
createCellRuntimeState({ ...cellRuntime[cid] }),
|
|
52
51
|
),
|
|
@@ -30,7 +30,7 @@ const EQUALITY_CHECK = (
|
|
|
30
30
|
export const CustomNode = memo((props: CustomNodeProps) => {
|
|
31
31
|
const { data, selected } = props; // must match the equality check
|
|
32
32
|
const cell = useAtomValue(data.atom);
|
|
33
|
-
const cellIndex = useCellIds().
|
|
33
|
+
const cellIndex = useCellIds().indexOf(cell.id);
|
|
34
34
|
const handleColor = selected ? "var(--gray-9)" : "var(--gray-3)";
|
|
35
35
|
const edgeMarkers = use(EdgeMarkerContext);
|
|
36
36
|
|
|
@@ -7,10 +7,11 @@ import {
|
|
|
7
7
|
cellGraphsAtom,
|
|
8
8
|
isVariableAffectedBySelectedCell,
|
|
9
9
|
} from "@/components/editor/chrome/wrapper/minimap-state";
|
|
10
|
-
import { useCellData,
|
|
10
|
+
import { useCellData, useCellRuntime } from "@/core/cells/cells";
|
|
11
11
|
import { useCellActions } from "@/core/cells/actions";
|
|
12
12
|
import { cellFocusAtom, useCellFocusActions } from "@/core/cells/focus";
|
|
13
13
|
import type { CellId } from "@/core/cells/ids";
|
|
14
|
+
import { useLayout } from "@/core/cells/layout";
|
|
14
15
|
import { useVariables } from "@/core/variables/state";
|
|
15
16
|
import { cn } from "@/utils/cn";
|
|
16
17
|
import { extractCellPreview } from "./utils/cell-preview";
|
|
@@ -380,28 +381,25 @@ function isNonReferenceableCell(graph: CellGraph): boolean {
|
|
|
380
381
|
* Shows a scrollable list of cells with dependency visualization.
|
|
381
382
|
*/
|
|
382
383
|
export const MinimapContent: React.FC = () => {
|
|
383
|
-
const
|
|
384
|
+
const layout = useLayout();
|
|
385
|
+
const rows = layout.columns.flatMap((column, index) =>
|
|
386
|
+
column.ids.map((cellId, position) => ({
|
|
387
|
+
cellId,
|
|
388
|
+
startsColumn: index > 0 && position === 0,
|
|
389
|
+
})),
|
|
390
|
+
);
|
|
384
391
|
const cellPositions: Record<CellId, number> = Object.fromEntries(
|
|
385
|
-
|
|
392
|
+
rows.map(({ cellId }, idx) => [cellId, idx]),
|
|
386
393
|
);
|
|
387
|
-
const columnBoundaries: number[] = [];
|
|
388
|
-
let cellCount = 0;
|
|
389
|
-
for (const [idx, column] of cellIds.getColumns().entries()) {
|
|
390
|
-
if (idx > 0) {
|
|
391
|
-
columnBoundaries.push(cellCount);
|
|
392
|
-
}
|
|
393
|
-
cellCount += column.inOrderIds.length;
|
|
394
|
-
}
|
|
395
394
|
|
|
396
395
|
return (
|
|
397
396
|
<div className="overflow-y-auto overflow-x-hidden flex-1 scrollbar-none h-full max-w-80">
|
|
398
397
|
<div className="py-3 pl-3 pr-4 relative min-h-full">
|
|
399
|
-
{
|
|
400
|
-
const isColumnBoundary = columnBoundaries.includes(idx);
|
|
398
|
+
{rows.map(({ cellId, startsColumn }, idx) => {
|
|
401
399
|
return (
|
|
402
400
|
<React.Fragment key={cellId}>
|
|
403
401
|
{/* Subtle visual divider between nodes */}
|
|
404
|
-
{
|
|
402
|
+
{startsColumn && (
|
|
405
403
|
<div
|
|
406
404
|
className="absolute left-5 w-[36px] h-px bg-(--gray-4) pointer-events-none"
|
|
407
405
|
aria-hidden="true"
|
|
@@ -29,7 +29,7 @@ export const useSetCodeVisibility = () => {
|
|
|
29
29
|
|
|
30
30
|
const newConfigs: Record<CellId, Partial<CellConfig>> = {};
|
|
31
31
|
|
|
32
|
-
for (const cellId of notebook.cellIds
|
|
32
|
+
for (const cellId of notebook.cellIds) {
|
|
33
33
|
const cell = notebook.cellData[cellId];
|
|
34
34
|
if (cell === undefined || cell.config.hide_code === hidden) {
|
|
35
35
|
continue;
|
|
@@ -19,7 +19,7 @@ export function outputExpansionConfigs(
|
|
|
19
19
|
expanded: boolean,
|
|
20
20
|
): Record<CellId, Partial<CellConfig>> {
|
|
21
21
|
const configs: Record<CellId, Partial<CellConfig>> = {};
|
|
22
|
-
for (const cellId of notebook.cellIds
|
|
22
|
+
for (const cellId of notebook.cellIds) {
|
|
23
23
|
const cell = notebook.cellData[cellId];
|
|
24
24
|
if (
|
|
25
25
|
cell === undefined ||
|
|
@@ -12,7 +12,7 @@ const hasBlockingStdinAtom = atom((get) => {
|
|
|
12
12
|
const notebook = get(notebookAtom);
|
|
13
13
|
|
|
14
14
|
// Check each cell in order
|
|
15
|
-
for (const cellId of notebook.cellIds
|
|
15
|
+
for (const cellId of notebook.cellIds) {
|
|
16
16
|
// Check if the cell is idle and has unresolved stdin
|
|
17
17
|
const runtime = notebook.cellRuntime[cellId];
|
|
18
18
|
if (runtime.status === "idle") {
|
|
@@ -236,7 +236,7 @@ const CellEditorInternal = ({
|
|
|
236
236
|
addOrAppendSetupCell: (code) => {
|
|
237
237
|
const notebook = getNotebook();
|
|
238
238
|
// No setup cell yet: create one with this code at the top.
|
|
239
|
-
if (!notebook.cellIds.
|
|
239
|
+
if (!notebook.cellIds.includes(SETUP_CELL_ID)) {
|
|
240
240
|
cellActions.addSetupCell({ code });
|
|
241
241
|
return;
|
|
242
242
|
}
|
|
@@ -11,16 +11,18 @@ import { getEditorCodeAsPython } from "@/core/codemirror/language/utils";
|
|
|
11
11
|
export function useSplitCellCallback() {
|
|
12
12
|
const { splitCell, undoSplitCell } = useCellActions();
|
|
13
13
|
|
|
14
|
-
return useEvent((opts: { cellId: CellId }) => {
|
|
14
|
+
return useEvent((opts: { cellId: CellId }): CellId | null => {
|
|
15
15
|
// Save snapshot of code for undo
|
|
16
16
|
const cellEditorView = getCellEditorView(opts.cellId);
|
|
17
17
|
if (!cellEditorView) {
|
|
18
|
-
return;
|
|
18
|
+
return null;
|
|
19
19
|
}
|
|
20
20
|
const code = getEditorCodeAsPython(cellEditorView);
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const newCellId = splitCell(opts);
|
|
23
|
+
if (newCellId === null) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
24
26
|
|
|
25
27
|
const { dismiss } = toast({
|
|
26
28
|
title: "Cell split",
|
|
@@ -30,7 +32,7 @@ export function useSplitCellCallback() {
|
|
|
30
32
|
size="sm"
|
|
31
33
|
variant="outline"
|
|
32
34
|
onClick={() => {
|
|
33
|
-
undoSplitCell({ ...opts, snapshot: code });
|
|
35
|
+
undoSplitCell({ ...opts, newCellId, snapshot: code });
|
|
34
36
|
dismiss();
|
|
35
37
|
}}
|
|
36
38
|
>
|
|
@@ -38,5 +40,6 @@ export function useSplitCellCallback() {
|
|
|
38
40
|
</UndoButton>
|
|
39
41
|
),
|
|
40
42
|
});
|
|
43
|
+
return newCellId;
|
|
41
44
|
});
|
|
42
45
|
}
|
|
@@ -88,7 +88,7 @@ const SessionPanel: React.FC = () => {
|
|
|
88
88
|
No variables defined
|
|
89
89
|
</div>
|
|
90
90
|
) : (
|
|
91
|
-
<VariableTable cellIds={cellIds
|
|
91
|
+
<VariableTable cellIds={cellIds} variables={variables} />
|
|
92
92
|
)}
|
|
93
93
|
</PanelAccordionContent>
|
|
94
94
|
</PanelAccordionItem>
|
|
@@ -22,11 +22,7 @@ const VariablePanel: React.FC = () => {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
|
-
<VariableTable
|
|
26
|
-
className="flex-1"
|
|
27
|
-
cellIds={cellIds.inOrderIds}
|
|
28
|
-
variables={variables}
|
|
29
|
-
/>
|
|
25
|
+
<VariableTable className="flex-1" cellIds={cellIds} variables={variables} />
|
|
30
26
|
);
|
|
31
27
|
};
|
|
32
28
|
|
|
@@ -104,7 +104,7 @@ export function buildCellGraph(
|
|
|
104
104
|
export const cellGraphsAtom = atom((get) => {
|
|
105
105
|
const cellIds = get(cellIdsAtom);
|
|
106
106
|
const variables = get(variablesAtom);
|
|
107
|
-
return buildCellGraph(cellIds
|
|
107
|
+
return buildCellGraph(cellIds, variables);
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
/**
|
|
@@ -3,7 +3,7 @@ import { render } from "@testing-library/react";
|
|
|
3
3
|
import { createStore, Provider } from "jotai";
|
|
4
4
|
import { describe, expect, it } from "vitest";
|
|
5
5
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
6
|
-
import type { CellColumnId } from "@/
|
|
6
|
+
import type { CellColumnId } from "@/core/cells/layout";
|
|
7
7
|
import { Column } from "../cell-column";
|
|
8
8
|
|
|
9
9
|
const columnId = "col-1" as CellColumnId;
|
|
@@ -4,7 +4,7 @@ import React, { memo, useRef } from "react";
|
|
|
4
4
|
import type { AppConfig } from "@/core/config/config-schema";
|
|
5
5
|
import { useResizeHandle } from "@/hooks/useResizeHandle";
|
|
6
6
|
import { cn } from "@/utils/cn";
|
|
7
|
-
import type { CellColumnId } from "@/
|
|
7
|
+
import type { CellColumnId } from "@/core/cells/layout";
|
|
8
8
|
import { SortableColumn } from "./sortable-column";
|
|
9
9
|
import { storageFn } from "./storage";
|
|
10
10
|
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
import { Tooltip } from "@/components/ui/tooltip";
|
|
22
22
|
import { useCellActions } from "@/core/cells/actions";
|
|
23
23
|
import { cn } from "@/utils/cn";
|
|
24
|
-
import type { CellColumnId } from "@/
|
|
24
|
+
import type { CellColumnId } from "@/core/cells/layout";
|
|
25
25
|
import { mergeRefs } from "../../../utils/mergeRefs";
|
|
26
26
|
|
|
27
27
|
interface Props extends React.HTMLAttributes<HTMLDivElement> {
|
|
@@ -15,7 +15,7 @@ vi.mock("@/core/cells/actions", () => ({
|
|
|
15
15
|
useCellActions: () => ({ showCellIfHidden }),
|
|
16
16
|
}));
|
|
17
17
|
vi.mock("@/core/cells/cells", () => ({
|
|
18
|
-
useCellIds: () =>
|
|
18
|
+
useCellIds: () => [targetId],
|
|
19
19
|
useCellNames: () => ({ [targetId]: "Target" }),
|
|
20
20
|
}));
|
|
21
21
|
vi.mock("@/core/saving/filename", () => ({ useFilename: () => "notebook.py" }));
|
|
@@ -25,7 +25,7 @@ export const CellLinkList: React.FC<Props> = ({
|
|
|
25
25
|
}) => {
|
|
26
26
|
const cellIndex = useCellIds();
|
|
27
27
|
const sortedCellIds = cellIds.toSorted((a, b) => {
|
|
28
|
-
return cellIndex.
|
|
28
|
+
return cellIndex.indexOf(a) - cellIndex.indexOf(b);
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
if (cellIds.length === 0) {
|
|
@@ -27,7 +27,7 @@ export const CellLink = (props: Props): JSX.Element => {
|
|
|
27
27
|
const { className, cellId, variant, onClick, formatCellName, skipScroll } =
|
|
28
28
|
props;
|
|
29
29
|
const cellName = useCellNames()[cellId] ?? "";
|
|
30
|
-
const cellIndex = useCellIds().
|
|
30
|
+
const cellIndex = useCellIds().indexOf(cellId);
|
|
31
31
|
const { showCellIfHidden } = useCellActions();
|
|
32
32
|
const formatName = formatCellName ?? ((name: string) => name);
|
|
33
33
|
|
|
@@ -125,7 +125,6 @@ afterAll(() => {
|
|
|
125
125
|
});
|
|
126
126
|
|
|
127
127
|
import { defaultUserConfig } from "@/core/config/config-schema";
|
|
128
|
-
import { MultiColumn } from "@/utils/id-tree";
|
|
129
128
|
import { focusCell, focusCellEditor } from "../focus-utils";
|
|
130
129
|
import {
|
|
131
130
|
type CellSelectionState,
|
|
@@ -1427,12 +1426,27 @@ describe("useCellNavigationProps", () => {
|
|
|
1427
1426
|
},
|
|
1428
1427
|
},
|
|
1429
1428
|
});
|
|
1430
|
-
// Override cellIds to be multi-column
|
|
1431
|
-
notebookState.cellIds = MultiColumn.from([
|
|
1432
|
-
[cellId1, cellId3],
|
|
1433
|
-
[cellId2, cellId4, cellId5],
|
|
1434
|
-
]);
|
|
1435
1429
|
store.set(notebookAtom, notebookState);
|
|
1430
|
+
// The layout reads columns from the document's cells.
|
|
1431
|
+
stopModel();
|
|
1432
|
+
const inColumn = (id: CellId, column: number) =>
|
|
1433
|
+
cell(id, "", { config: { ...CONFIG, column } });
|
|
1434
|
+
({ model, stop: stopModel } = mockDocumentModel(
|
|
1435
|
+
store,
|
|
1436
|
+
withCells(
|
|
1437
|
+
documentOf(),
|
|
1438
|
+
{
|
|
1439
|
+
c1: null,
|
|
1440
|
+
c2: null,
|
|
1441
|
+
[cellId1]: inColumn(cellId1, 0),
|
|
1442
|
+
[cellId3]: inColumn(cellId3, 0),
|
|
1443
|
+
[cellId2]: inColumn(cellId2, 1),
|
|
1444
|
+
[cellId4]: inColumn(cellId4, 1),
|
|
1445
|
+
[cellId5]: inColumn(cellId5, 1),
|
|
1446
|
+
},
|
|
1447
|
+
[cellId1, cellId3, cellId2, cellId4, cellId5],
|
|
1448
|
+
),
|
|
1449
|
+
));
|
|
1436
1450
|
|
|
1437
1451
|
// Mock DOM elements and their positions
|
|
1438
1452
|
const mockGetElementById = vi.spyOn(document, "getElementById");
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
4
4
|
import { cellId } from "@/__tests__/branded";
|
|
5
5
|
import type { CellId } from "@/core/cells/ids";
|
|
6
|
-
import type { Layout } from "@/core/cells/layout";
|
|
7
|
-
import type { CellColumnId } from "@/utils/id-tree";
|
|
6
|
+
import type { CellColumnId, Layout } from "@/core/cells/layout";
|
|
8
7
|
import type { CellSelectionState } from "../selection";
|
|
9
8
|
import { exportedForTesting } from "../selection";
|
|
10
9
|
|
|
@@ -6,6 +6,8 @@ import { toast } from "@/components/ui/use-toast";
|
|
|
6
6
|
import { getNotebook } from "@/core/cells/cells";
|
|
7
7
|
import { useCellActions } from "@/core/cells/actions";
|
|
8
8
|
import type { CellId } from "@/core/cells/ids";
|
|
9
|
+
import { columnOf, layoutAtom } from "@/core/cells/layout";
|
|
10
|
+
import { store } from "@/core/state/jotai";
|
|
9
11
|
import {
|
|
10
12
|
usePendingCutActions,
|
|
11
13
|
usePendingCutState,
|
|
@@ -140,10 +142,10 @@ export function useCellClipboard() {
|
|
|
140
142
|
// Check if we have pending cut cells (internal move)
|
|
141
143
|
if (pendingCutState.cellIds.size > 0) {
|
|
142
144
|
const pendingCellIds = [...pendingCutState.cellIds];
|
|
143
|
-
const
|
|
145
|
+
const layout = store.get(layoutAtom);
|
|
144
146
|
const previousPlacements = pendingCellIds.map((id) => {
|
|
145
|
-
const column =
|
|
146
|
-
return { columnId: column.id, index: column.
|
|
147
|
+
const column = columnOf(layout, id);
|
|
148
|
+
return { columnId: column.id, index: column.ids.indexOf(id) };
|
|
147
149
|
});
|
|
148
150
|
|
|
149
151
|
actions.moveCellsRelativeTo({
|
|
@@ -29,14 +29,9 @@ import { canInteractWithAppAtom } from "@/core/network/connection";
|
|
|
29
29
|
import { useBoolean } from "@/hooks/useBoolean";
|
|
30
30
|
import { cn } from "@/utils/cn";
|
|
31
31
|
import { Functions } from "@/utils/functions";
|
|
32
|
-
import { findColumn, useLayout } from "@/core/cells/layout";
|
|
33
|
-
import type { CellColumnId } from "@/utils/id-tree";
|
|
32
|
+
import { type CellColumnId, findColumn, useLayout } from "@/core/cells/layout";
|
|
34
33
|
import { invariant } from "@/utils/invariant";
|
|
35
|
-
import {
|
|
36
|
-
columnIdsAtom,
|
|
37
|
-
useCellIds,
|
|
38
|
-
useScrollKey,
|
|
39
|
-
} from "../../../core/cells/cells";
|
|
34
|
+
import { useCellIds, useScrollKey } from "../../../core/cells/cells";
|
|
40
35
|
import { useCellActions } from "../../../core/cells/actions";
|
|
41
36
|
import { formatAll } from "../../../core/codemirror/format";
|
|
42
37
|
import type { AppConfig, UserConfig } from "../../../core/config/config-schema";
|
|
@@ -61,7 +56,7 @@ interface CellArrayProps {
|
|
|
61
56
|
}
|
|
62
57
|
|
|
63
58
|
export const CellArray: React.FC<CellArrayProps> = (props) => {
|
|
64
|
-
const columnIds =
|
|
59
|
+
const columnIds = useLayout().columns.map((column) => column.id);
|
|
65
60
|
|
|
66
61
|
// Setup context for sorting
|
|
67
62
|
return (
|
|
@@ -116,7 +111,7 @@ const CellArrayInternal: React.FC<CellArrayProps> = ({
|
|
|
116
111
|
|
|
117
112
|
const cellIds = useCellIds();
|
|
118
113
|
const scrollKey = useScrollKey();
|
|
119
|
-
const columnIds =
|
|
114
|
+
const columnIds = useLayout().columns.map((column) => column.id);
|
|
120
115
|
|
|
121
116
|
// Scroll to a cell targeted by a previous action
|
|
122
117
|
const scrollToTarget = actions.scrollToTarget;
|
|
@@ -140,7 +135,7 @@ const CellArrayInternal: React.FC<CellArrayProps> = ({
|
|
|
140
135
|
<StdinBlockingAlert />
|
|
141
136
|
<NotebookBanner width={appConfig.width} />
|
|
142
137
|
{/* Only show if not cells, otherwise running a single cell will start the connection */}
|
|
143
|
-
{cellIds.
|
|
138
|
+
{cellIds.length === 0 && <NotStartedConnectionAlert />}
|
|
144
139
|
<div
|
|
145
140
|
className={cn(
|
|
146
141
|
appConfig.width === "columns" &&
|
|
@@ -192,8 +187,8 @@ const CellColumn: React.FC<{
|
|
|
192
187
|
const column = findColumn(useLayout(), columnId);
|
|
193
188
|
invariant(column, `Expected column for: ${columnId}`);
|
|
194
189
|
|
|
195
|
-
const hasOnlyOneCell = cellIds.
|
|
196
|
-
const hasSetupCell = cellIds.
|
|
190
|
+
const hasOnlyOneCell = cellIds.length === 1;
|
|
191
|
+
const hasSetupCell = cellIds.includes(SETUP_CELL_ID);
|
|
197
192
|
const isPresenting = mode === "present";
|
|
198
193
|
|
|
199
194
|
return (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import React, { useMemo, useState } from "react";
|
|
3
3
|
import { useAtomValue } from "jotai";
|
|
4
|
-
import { numColumnsAtom } from "@/core/cells/
|
|
4
|
+
import { numColumnsAtom } from "@/core/cells/layout";
|
|
5
5
|
import type { CellId } from "@/core/cells/ids";
|
|
6
6
|
import { kioskModeAtom } from "@/core/mode";
|
|
7
7
|
import type { ICellRendererProps } from "../types";
|
package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts
CHANGED
|
@@ -25,7 +25,7 @@ describe("useFocusFirstEditor", () => {
|
|
|
25
25
|
const getNotebookSpy = vi
|
|
26
26
|
.spyOn(cellsModule, "getNotebook")
|
|
27
27
|
.mockReturnValue({
|
|
28
|
-
cellIds:
|
|
28
|
+
cellIds: ["cell-1", "cell-2"],
|
|
29
29
|
cellData: {
|
|
30
30
|
"cell-1": { config: { hide_code: false } },
|
|
31
31
|
"cell-2": { config: { hide_code: false } },
|
|
@@ -52,7 +52,7 @@ describe("useFocusFirstEditor", () => {
|
|
|
52
52
|
const getNotebookSpy = vi
|
|
53
53
|
.spyOn(cellsModule, "getNotebook")
|
|
54
54
|
.mockReturnValue({
|
|
55
|
-
cellIds:
|
|
55
|
+
cellIds: ["cell-1", "cell-2"],
|
|
56
56
|
cellData: {
|
|
57
57
|
"cell-1": { config: { hide_code: true } }, // hidden
|
|
58
58
|
"cell-2": { config: { hide_code: false } }, // visible
|
|
@@ -94,7 +94,7 @@ describe("useFocusFirstEditor", () => {
|
|
|
94
94
|
// Mock getNotebook
|
|
95
95
|
const mockEditor = { focus: vi.fn() };
|
|
96
96
|
vi.spyOn(cellsModule, "getNotebook").mockReturnValue({
|
|
97
|
-
cellIds:
|
|
97
|
+
cellIds: [],
|
|
98
98
|
cellData: {},
|
|
99
99
|
cellHandles: {
|
|
100
100
|
"cell-123": { current: { editorView: mockEditor } },
|
|
@@ -129,7 +129,7 @@ describe("useFocusFirstEditor", () => {
|
|
|
129
129
|
|
|
130
130
|
const mockEditor = { focus: vi.fn() };
|
|
131
131
|
vi.spyOn(cellsModule, "getNotebook").mockReturnValue({
|
|
132
|
-
cellIds:
|
|
132
|
+
cellIds: ["cell-1"],
|
|
133
133
|
cellData: {
|
|
134
134
|
"cell-1": { config: { hide_code: false } },
|
|
135
135
|
},
|
|
@@ -52,7 +52,7 @@ function focusFirstEditor() {
|
|
|
52
52
|
const { cellIds, cellData, cellHandles } = getNotebook();
|
|
53
53
|
|
|
54
54
|
// Focus on the first cell if it's been mounted and is not hidden
|
|
55
|
-
for (const cellId of cellIds
|
|
55
|
+
for (const cellId of cellIds) {
|
|
56
56
|
const handle = cellHandles[cellId];
|
|
57
57
|
const hidden = cellData[cellId]?.config.hide_code;
|
|
58
58
|
if (!hidden && handle?.current?.editorView) {
|
|
@@ -13,7 +13,7 @@ const hasBlockingStdinAtom = atom((get) => {
|
|
|
13
13
|
const notebook = get(notebookAtom);
|
|
14
14
|
|
|
15
15
|
// Check each cell in order
|
|
16
|
-
for (const cellId of notebook.cellIds
|
|
16
|
+
for (const cellId of notebook.cellIds) {
|
|
17
17
|
// Check if the cell is idle and has unresolved stdin
|
|
18
18
|
const runtime = notebook.cellRuntime[cellId];
|
|
19
19
|
if (runtime.status === "idle") {
|
|
@@ -6,7 +6,7 @@ import { cellId } from "@/__tests__/branded";
|
|
|
6
6
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
7
7
|
import type { CellId } from "@/core/cells/ids";
|
|
8
8
|
import type { CellData, CellRuntimeState } from "@/core/cells/types";
|
|
9
|
-
import {
|
|
9
|
+
import type { CellColumnId } from "@/core/cells/layout";
|
|
10
10
|
import { SlidesMinimap } from "../minimap";
|
|
11
11
|
|
|
12
12
|
const A = cellId("a");
|
|
@@ -22,11 +22,20 @@ const { createCell, deleteCell, moveCellToIndex } = vi.hoisted(() => ({
|
|
|
22
22
|
vi.mock("@/core/cells/actions", () => ({
|
|
23
23
|
useCellActions: () => ({ moveCellToIndex, createCell }),
|
|
24
24
|
}));
|
|
25
|
-
vi.mock("@/core/cells/
|
|
26
|
-
const actual = await importOriginal<typeof import("@/core/cells/
|
|
25
|
+
vi.mock("@/core/cells/layout", async (importOriginal) => {
|
|
26
|
+
const actual = await importOriginal<typeof import("@/core/cells/layout")>();
|
|
27
27
|
return {
|
|
28
28
|
...actual,
|
|
29
|
-
|
|
29
|
+
useLayout: () => ({
|
|
30
|
+
columns: [
|
|
31
|
+
{
|
|
32
|
+
id: "column-0" as CellColumnId,
|
|
33
|
+
ids: [A, B],
|
|
34
|
+
visible: [A, B],
|
|
35
|
+
folds: new Map(),
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
}),
|
|
30
39
|
};
|
|
31
40
|
});
|
|
32
41
|
|
|
@@ -4,7 +4,7 @@ import { renderHook, act } from "@testing-library/react";
|
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
5
|
import type { CellId } from "@/core/cells/ids";
|
|
6
6
|
import type { DragMoveEvent } from "@dnd-kit/core";
|
|
7
|
-
import {
|
|
7
|
+
import type { CellColumnId, Layout } from "@/core/cells/layout";
|
|
8
8
|
import { exportedForTesting } from "../minimap";
|
|
9
9
|
|
|
10
10
|
const { useVisibleCellIds, projectDropTarget, resolveDropTarget } =
|
|
@@ -318,15 +318,23 @@ describe("resolveDropTarget", () => {
|
|
|
318
318
|
const C = "c" as CellId;
|
|
319
319
|
const D = "d" as CellId;
|
|
320
320
|
|
|
321
|
-
function
|
|
322
|
-
return
|
|
321
|
+
function makeLayout(...columns: CellId[][]): Layout {
|
|
322
|
+
return {
|
|
323
|
+
columns: columns.map((ids, index) => ({
|
|
324
|
+
id: `column-${index}` as CellColumnId,
|
|
325
|
+
ids,
|
|
326
|
+
visible: ids,
|
|
327
|
+
folds: new Map(),
|
|
328
|
+
})),
|
|
329
|
+
};
|
|
323
330
|
}
|
|
331
|
+
const makeSingleColumnIds = (...ids: CellId[]) => makeLayout(ids);
|
|
324
332
|
|
|
325
333
|
it("returns null when activeId matches target overId", () => {
|
|
326
|
-
const
|
|
334
|
+
const layout = makeSingleColumnIds(A, B);
|
|
327
335
|
expect(
|
|
328
336
|
resolveDropTarget({
|
|
329
|
-
|
|
337
|
+
layout,
|
|
330
338
|
activeId: A,
|
|
331
339
|
target: { overId: A, position: "after" },
|
|
332
340
|
}),
|
|
@@ -334,68 +342,79 @@ describe("resolveDropTarget", () => {
|
|
|
334
342
|
});
|
|
335
343
|
|
|
336
344
|
it("returns null for multi-column layouts", () => {
|
|
337
|
-
const
|
|
345
|
+
const layout = makeLayout([A, B], [C]);
|
|
338
346
|
expect(
|
|
339
347
|
resolveDropTarget({
|
|
340
|
-
|
|
348
|
+
layout,
|
|
341
349
|
activeId: A,
|
|
342
350
|
target: { overId: B, position: "after" },
|
|
343
351
|
}),
|
|
344
352
|
).toBeNull();
|
|
345
353
|
});
|
|
346
354
|
|
|
355
|
+
it("returns null when the over cell has left the layout", () => {
|
|
356
|
+
const layout = makeSingleColumnIds(A, B);
|
|
357
|
+
expect(
|
|
358
|
+
resolveDropTarget({
|
|
359
|
+
layout,
|
|
360
|
+
activeId: A,
|
|
361
|
+
target: { overId: C, position: "after" },
|
|
362
|
+
}),
|
|
363
|
+
).toBeNull();
|
|
364
|
+
});
|
|
365
|
+
|
|
347
366
|
it('resolves "before" to the over index', () => {
|
|
348
|
-
const
|
|
367
|
+
const layout = makeSingleColumnIds(A, B, C);
|
|
349
368
|
const result = resolveDropTarget({
|
|
350
|
-
|
|
369
|
+
layout,
|
|
351
370
|
activeId: A,
|
|
352
371
|
target: { overId: C, position: "before" },
|
|
353
372
|
});
|
|
354
373
|
expect(result).toEqual({
|
|
355
374
|
cellId: A,
|
|
356
|
-
columnId:
|
|
375
|
+
columnId: layout.columns[0].id,
|
|
357
376
|
index: 2,
|
|
358
377
|
});
|
|
359
378
|
});
|
|
360
379
|
|
|
361
380
|
it('resolves "after" to one past the over index', () => {
|
|
362
|
-
const
|
|
381
|
+
const layout = makeSingleColumnIds(A, B, C);
|
|
363
382
|
const result = resolveDropTarget({
|
|
364
|
-
|
|
383
|
+
layout,
|
|
365
384
|
activeId: C,
|
|
366
385
|
target: { overId: A, position: "after" },
|
|
367
386
|
});
|
|
368
387
|
expect(result).toEqual({
|
|
369
388
|
cellId: C,
|
|
370
|
-
columnId:
|
|
389
|
+
columnId: layout.columns[0].id,
|
|
371
390
|
index: 1,
|
|
372
391
|
});
|
|
373
392
|
});
|
|
374
393
|
|
|
375
394
|
it('resolves "before" on first item to index 0', () => {
|
|
376
|
-
const
|
|
395
|
+
const layout = makeSingleColumnIds(A, B, C);
|
|
377
396
|
const result = resolveDropTarget({
|
|
378
|
-
|
|
397
|
+
layout,
|
|
379
398
|
activeId: C,
|
|
380
399
|
target: { overId: A, position: "before" },
|
|
381
400
|
});
|
|
382
401
|
expect(result).toEqual({
|
|
383
402
|
cellId: C,
|
|
384
|
-
columnId:
|
|
403
|
+
columnId: layout.columns[0].id,
|
|
385
404
|
index: 0,
|
|
386
405
|
});
|
|
387
406
|
});
|
|
388
407
|
|
|
389
408
|
it('resolves "after" on last item to one past the end', () => {
|
|
390
|
-
const
|
|
409
|
+
const layout = makeSingleColumnIds(A, B, C, D);
|
|
391
410
|
const result = resolveDropTarget({
|
|
392
|
-
|
|
411
|
+
layout,
|
|
393
412
|
activeId: A,
|
|
394
413
|
target: { overId: D, position: "after" },
|
|
395
414
|
});
|
|
396
415
|
expect(result).toEqual({
|
|
397
416
|
cellId: A,
|
|
398
|
-
columnId:
|
|
417
|
+
columnId: layout.columns[0].id,
|
|
399
418
|
index: 4,
|
|
400
419
|
});
|
|
401
420
|
});
|