Skip to content

Commit cea1fef

Browse files
authored
fix: setup cell can be focused / enter command mode (#6645)
1 parent 620525c commit cea1fef

File tree

11 files changed

+45
-32
lines changed

11 files changed

+45
-32
lines changed

frontend/src/components/editor/__tests__/data-attributes.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type { UserConfig } from "@/core/config/config-schema";
1212
import type { OutputMessage } from "@/core/kernel/messages";
1313
import type { AppMode } from "@/core/mode";
1414
import { requestClientAtom } from "@/core/network/requests";
15-
import { Cell } from "../Cell";
15+
import { Cell } from "../notebook-cell";
1616
import { OutputArea } from "../Output";
1717

1818
function createTestWrapper() {

frontend/src/components/editor/actions/useNotebookActions.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export function useNotebookActions() {
102102
updateCellConfig,
103103
undoDeleteCell,
104104
clearAllCellOutputs,
105-
upsertSetupCell,
105+
addSetupCellIfDoesntExist,
106106
collapseAllCells,
107107
expandAllCells,
108108
} = useCellActions();
@@ -401,9 +401,7 @@ export function useNotebookActions() {
401401
icon: <DiamondPlusIcon size={14} strokeWidth={1.5} />,
402402
label: "Add setup cell",
403403
handle: () => {
404-
upsertSetupCell({
405-
code: "# Initialization code that runs before all other cells",
406-
});
404+
addSetupCellIfDoesntExist({});
407405
},
408406
},
409407
{

frontend/src/components/editor/cell/CreateCellButton.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* Copyright 2024 Marimo. All rights reserved. */
2-
import { DatabaseIcon, PlusIcon } from "lucide-react";
2+
import { DatabaseIcon, DiamondPlusIcon, PlusIcon } from "lucide-react";
33
import { Button } from "@/components/editor/inputs/Inputs";
44
import {
55
ContextMenu,
@@ -69,7 +69,7 @@ const CreateCellButtonContextMenu = (props: {
6969
children: React.ReactNode;
7070
}) => {
7171
const { children, onClick } = props;
72-
const { createNewCell } = useCellActions();
72+
const { createNewCell, addSetupCellIfDoesntExist } = useCellActions();
7373

7474
if (!onClick) {
7575
return children;
@@ -125,6 +125,18 @@ const CreateCellButtonContextMenu = (props: {
125125
</div>
126126
SQL cell
127127
</ContextMenuItem>
128+
<ContextMenuItem
129+
key="setup"
130+
onSelect={(evt) => {
131+
evt.stopPropagation();
132+
addSetupCellIfDoesntExist({});
133+
}}
134+
>
135+
<div className="mr-3 text-muted-foreground">
136+
<DiamondPlusIcon size={13} strokeWidth={1.5} />
137+
</div>
138+
Setup cell
139+
</ContextMenuItem>
128140
</ContextMenuContent>
129141
</ContextMenu>
130142
);

frontend/src/components/editor/Cell.tsx renamed to frontend/src/components/editor/notebook-cell.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1051,12 +1051,16 @@ const SetupCellComponent = ({
10511051
data-status={cellRuntime.status}
10521052
ref={cellRef}
10531053
{...mergeProps(navigationProps, {
1054-
className,
1054+
className: cn(
1055+
className,
1056+
"focus:ring-1 focus:ring-(--blue-7) focus:ring-offset-0",
1057+
),
10551058
onBlur: closeCompletionHandler,
10561059
onKeyDown: resumeCompletionHandler,
10571060
})}
10581061
{...cellDomProps(cellId, cellData.name)}
10591062
title={renderCellTitle()}
1063+
tabIndex={-1}
10601064
data-setup-cell={true}
10611065
>
10621066
<div className={cn("tray")} data-hidden={!isCellCodeShown}>

frontend/src/components/editor/renderers/CellArray.tsx renamed to frontend/src/components/editor/renderers/cell-array.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
} from "lucide-react";
1515
import { useEffect } from "react";
1616
import { StartupLogsAlert } from "@/components/editor/alerts/startup-logs-alert";
17-
import { Cell } from "@/components/editor/Cell";
17+
import { Cell } from "@/components/editor/notebook-cell";
1818
import { PackageAlert } from "@/components/editor/package-alert";
1919
import { SortableCellsProvider } from "@/components/sort/SortableCellsProvider";
2020
import { Button } from "@/components/ui/button";

frontend/src/core/cells/__tests__/cells.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
it,
1313
vi,
1414
} from "vitest";
15-
import type { CellHandle } from "@/components/editor/Cell";
15+
import type { CellHandle } from "@/components/editor/notebook-cell";
1616
import { CellId } from "@/core/cells/ids";
1717
import { foldAllBulk, unfoldAllBulk } from "@/core/codemirror/editing/commands";
1818
import { adaptiveLanguageConfiguration } from "@/core/codemirror/language/extension";
@@ -2094,9 +2094,9 @@ describe("cell reducer", () => {
20942094
`);
20952095
});
20962096

2097-
it("can create and update a setup cell", () => {
2097+
it("can create and noop-update a setup cell", () => {
20982098
// Create the setup cell
2099-
actions.upsertSetupCell({ code: "# Setup code" });
2099+
actions.addSetupCellIfDoesntExist({ code: "# Setup code" });
21002100

21012101
// Check that setup cell was created
21022102
expect(state.cellData[SETUP_CELL_ID].id).toBe(SETUP_CELL_ID);
@@ -2106,17 +2106,17 @@ describe("cell reducer", () => {
21062106
expect(state.cellIds.inOrderIds).toContain(SETUP_CELL_ID);
21072107

21082108
// Update the setup cell
2109-
actions.upsertSetupCell({ code: "# Updated setup code" });
2109+
actions.addSetupCellIfDoesntExist({ code: "# Updated setup code" });
21102110

21112111
// Check that the same setup cell was updated, not duplicated
2112-
expect(state.cellData[SETUP_CELL_ID].code).toBe("# Updated setup code");
2112+
expect(state.cellData[SETUP_CELL_ID].code).toBe("# Setup code");
21132113
expect(state.cellData[SETUP_CELL_ID].edited).toBe(true);
21142114
expect(state.cellIds.inOrderIds).toContain(SETUP_CELL_ID);
21152115
});
21162116

21172117
it("can delete and undelete the setup cell", () => {
21182118
// Create the setup cell
2119-
actions.upsertSetupCell({ code: "# Setup code" });
2119+
actions.addSetupCellIfDoesntExist({ code: "# Setup code" });
21202120

21212121
// Check that setup cell was created
21222122
expect(state.cellData[SETUP_CELL_ID].id).toBe(SETUP_CELL_ID);

frontend/src/core/cells/cells.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { type Atom, atom, useAtom, useAtomValue } from "jotai";
55
import { atomFamily, selectAtom, splitAtom } from "jotai/utils";
66
import { isEqual, zip } from "lodash-es";
77
import { createRef, type ReducerWithoutAction } from "react";
8-
import type { CellHandle } from "@/components/editor/Cell";
8+
import type { CellHandle } from "@/components/editor/notebook-cell";
99
import {
1010
type CellColumnId,
1111
type CellIndex,
@@ -1324,21 +1324,19 @@ const {
13241324
cellRuntime: newCellRuntime,
13251325
};
13261326
},
1327-
upsertSetupCell: (state, action: { code: string }) => {
1328-
const { code } = action;
1327+
addSetupCellIfDoesntExist: (state, action: { code?: string }) => {
1328+
let { code } = action;
1329+
if (code == null) {
1330+
code = "# Initialization code that runs before all other cells";
1331+
}
13291332

13301333
// First check if setup cell already exists
13311334
if (SETUP_CELL_ID in state.cellData) {
1332-
// Update existing setup cell
1333-
return updateCellData({
1334-
state,
1335-
cellId: SETUP_CELL_ID,
1336-
cellReducer: (cell) => ({
1337-
...cell,
1338-
code,
1339-
edited: code.trim() !== cell.lastCodeRun?.trim(),
1340-
}),
1341-
});
1335+
// Just focus on the existing setup cell
1336+
return {
1337+
...state,
1338+
scrollKey: SETUP_CELL_ID,
1339+
};
13421340
}
13431341

13441342
return {
@@ -1365,6 +1363,7 @@ const {
13651363
...state.cellHandles,
13661364
[SETUP_CELL_ID]: createRef(),
13671365
},
1366+
scrollKey: SETUP_CELL_ID,
13681367
};
13691368
},
13701369
});

frontend/src/core/cells/scrollCellIntoView.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
/* Copyright 2024 Marimo. All rights reserved. */
22
import type { RefObject } from "react";
3-
import type { CellHandle } from "@/components/editor/Cell";
43
import {
54
isAnyCellFocused,
65
tryFocus,
76
} from "@/components/editor/navigation/focus-utils";
7+
import type { CellHandle } from "@/components/editor/notebook-cell";
88
import { retryWithTimeout } from "@/utils/timeout";
99
import { Logger } from "../../utils/Logger";
1010
import { goToVariableDefinition } from "../codemirror/go-to-definition/commands";

frontend/src/core/edit-app.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
useRunAllCells,
1616
useRunStaleCells,
1717
} from "../components/editor/cell/useRunCells";
18-
import { CellArray } from "../components/editor/renderers/CellArray";
18+
import { CellArray } from "../components/editor/renderers/cell-array";
1919
import { CellsRenderer } from "../components/editor/renderers/cells-renderer";
2020
import { useHotkey } from "../hooks/useHotkey";
2121
import {

frontend/src/stories/cell.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import type { CellConfig } from "@/core/network/types";
1616
import { WebSocketState } from "@/core/websocket/types";
1717
import { MultiColumn } from "@/utils/id-tree";
1818
import type { Milliseconds, Seconds } from "@/utils/time";
19-
import { Cell as EditorCell } from "../components/editor/Cell";
19+
import { Cell as EditorCell } from "../components/editor/notebook-cell";
2020
import { TooltipProvider } from "../components/ui/tooltip";
2121
import type { CellId } from "../core/cells/ids";
2222

0 commit comments

Comments
 (0)