Skip to content

Commit 6e6eded

Browse files
authored
Merge pull request #7599 from sagemathinc/jupyter-runnall-toolbar
frontend/jupyter: run all cells above/below dropdown button in cell toolbar
2 parents d4ce947 + 6f3e503 commit 6e6eded

File tree

10 files changed

+109
-47
lines changed

10 files changed

+109
-47
lines changed

src/packages/frontend/components/icon.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,15 +208,16 @@ import {
208208
UserDeleteOutlined,
209209
UserOutlined,
210210
UsergroupAddOutlined,
211+
VerticalAlignBottomOutlined,
211212
VerticalAlignMiddleOutlined,
212213
VerticalRightOutlined,
213214
VideoCameraOutlined,
214215
WalletOutlined,
215216
WarningOutlined,
216217
WifiOutlined,
218+
XOutlined,
217219
YoutubeFilled,
218220
YoutubeOutlined,
219-
XOutlined,
220221
} from "@ant-design/icons";
221222

222223
// Unfortunately -- "error TS7056: The inferred type of this node exceeds the maximum length the
@@ -593,6 +594,7 @@ const IconSpec: { [name: string]: any } = {
593594
"vertical-right-outlined": VerticalRightOutlined,
594595
"video-camera": VideoCameraOutlined,
595596
"vertical-align-middle": VerticalAlignMiddleOutlined,
597+
"vertical-align-bottom": VerticalAlignBottomOutlined,
596598
vim: { IconFont: "vim" },
597599
vscode: { IconFont: "vscode" },
598600
wallet: WalletOutlined,
@@ -675,12 +677,14 @@ export function isIconName(name?: string): name is IconName {
675677

676678
export const iconNames: IconName[] = Object.keys(IconSpec) as any;
677679

680+
export type IconRotation = "45" | "90" | "135" | "180" | "225" | "270" | "315";
681+
678682
interface Props {
679683
name?: IconName;
680684
unicode?: number; // (optional) set a hex 16 bit charcode to render a unicode char, e.g. 0x2620
681685
className?: string;
682686
size?: "lg" | "2x" | "3x" | "4x" | "5x";
683-
rotate?: "45" | "90" | "135" | "180" | "225" | "270" | "315";
687+
rotate?: IconRotation;
684688
flip?: "horizontal" | "vertical";
685689
spin?: boolean;
686690
pulse?: boolean;

src/packages/frontend/frame-editors/frame-tree/commands/editor-menus.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ This is basically a more user friendly and compact interface to the addMenus
66
and addCommands functions.
77
*/
88

9+
import { IconRotation } from "@cocalc/frontend/components/icon";
910
import { capitalize } from "@cocalc/util/misc";
1011
import { addCommands } from "./commands";
1112
import { addMenus } from "./menus";
1213
import type { Command, Menus } from "./types";
1314

1415
type Command0 = {
1516
icon?: string;
17+
iconRotate?: IconRotation;
1618
label?: string | (({ props }: any) => any);
1719
name?: string;
1820
children?;
@@ -46,6 +48,7 @@ export function addEditorMenus({
4648
children?;
4749
label?;
4850
icon?;
51+
iconRotate?;
4952
onClick?;
5053
disabled?;
5154
};

src/packages/frontend/frame-editors/frame-tree/commands/manage.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ export class ManageCommands {
256256
};
257257

258258
private getCommandIcon = (cmd: Partial<Command>) => {
259+
const rotate = cmd.iconRotate;
259260
let icon = cmd.icon;
260261
if (!icon) {
261262
return undefined;
@@ -271,7 +272,7 @@ export class ManageCommands {
271272
display: "inline-block",
272273
}}
273274
>
274-
{typeof icon == "string" ? <Icon name={icon} /> : icon}
275+
{typeof icon === "string" ? <Icon name={icon} rotate={rotate} /> : icon}
275276
</span>
276277
);
277278
};

src/packages/frontend/frame-editors/frame-tree/commands/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ReactNode } from "react";
22

3+
import { IconRotation } from "@cocalc/frontend/components/icon";
34
import type { ManageCommands } from "./manage";
45
import { MENUS } from "./menus";
56

@@ -25,6 +26,7 @@ export interface Command {
2526
pos?: number;
2627
title?: ReactNode;
2728
icon?: ReactNode | ((opts: ManageCommands) => ReactNode);
29+
iconRotate?: IconRotation;
2830
button?: ReactNode | ((opts: ManageCommands) => ReactNode);
2931
//color?: string | ((opts: ManageCommands) => string);
3032
label?: ReactNode | ((opts: ManageCommands) => ReactNode);

src/packages/frontend/frame-editors/jupyter-editor/editor.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,7 @@ function initMenus() {
516516
title: cmd.t,
517517
label: cmd.m,
518518
icon: cmd.i,
519+
iconRotate: cmd.ir,
519520
keyboard: cmd.k ? cmd.k.map(shortcut_to_string).join(", ") : undefined,
520521
onClick: ({ props }) => {
521522
allActions.frame_actions = props.actions.frame_actions?.[props.id];

src/packages/frontend/jupyter/cell-buttonbar.tsx

Lines changed: 77 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
React component that describes the input of a cell
88
*/
99

10-
import { Button, Tooltip } from "antd";
10+
import { Button, Dropdown, Tooltip } from "antd";
1111
import { delay } from "awaiting";
1212
import { Map } from "immutable";
1313
import React, { useState } from "react";
@@ -28,6 +28,8 @@ import {
2828
CODE_BAR_BTN_STYLE,
2929
MINI_BUTTONS_STYLE,
3030
MINI_BUTTONS_STYLE_INNER,
31+
RUN_ALL_CELLS_ABOVE_ICON,
32+
RUN_ALL_CELLS_BELOW_ICON,
3133
} from "./consts";
3234
import { LLMCellTool } from "./llm";
3335

@@ -77,48 +79,88 @@ export const CellButtonBar: React.FC<Props> = React.memo(
7779
track("jupyter_cell_buttonbar", { button, project_id, path });
7880
}
7981

80-
function renderCodeBarRunStop() {
81-
if (id == null || actions == null || actions.is_closed()) {
82-
return;
83-
}
82+
function getRunStopButton(): {
83+
tooltip: string;
84+
icon: string;
85+
label: string;
86+
onClick: () => void;
87+
} {
8488
switch (cell.get("state")) {
8589
case "busy":
8690
case "run":
8791
case "start":
88-
return (
89-
<Tooltip placement="top" title="Stop this cell">
90-
<Button
91-
size="small"
92-
type="text"
93-
onClick={() => {
94-
actions?.signal("SIGINT");
95-
// trackButton("stop"); // too much data
96-
}}
97-
style={CODE_BAR_BTN_STYLE}
98-
>
99-
<Icon name="stop" /> Stop
100-
</Button>
101-
</Tooltip>
102-
);
92+
return {
93+
tooltip: "Stop this cell",
94+
icon: "stop",
95+
label: "Stop",
96+
onClick: () => actions?.signal("SIGINT"),
97+
};
98+
10399
default:
104-
return (
105-
<Tooltip placement="top" title="Run this cell">
106-
<Button
107-
size="small"
108-
type="text"
109-
onClick={() => {
110-
actions?.run_cell(id);
111-
// trackButton("run"); // too much data
112-
}}
113-
style={CODE_BAR_BTN_STYLE}
114-
>
115-
<Icon name="step-forward" /> Run
116-
</Button>
117-
</Tooltip>
118-
);
100+
return {
101+
tooltip: "Run this cell",
102+
label: "Run",
103+
icon: "step-forward",
104+
onClick: () => actions?.run_cell(id),
105+
};
119106
}
120107
}
121108

109+
function renderCodeBarRunStop() {
110+
if (id == null || actions == null || actions.is_closed()) {
111+
return;
112+
}
113+
114+
const { label, icon, tooltip, onClick } = getRunStopButton();
115+
116+
return (
117+
<Dropdown.Button
118+
size="small"
119+
type="text"
120+
trigger={["click"]}
121+
mouseLeaveDelay={1.5}
122+
icon={<Icon name="angle-down" />}
123+
onClick={onClick}
124+
menu={{
125+
items: [
126+
{
127+
key: "all-above",
128+
icon: <Icon name={RUN_ALL_CELLS_ABOVE_ICON} />,
129+
label: (
130+
<Tooltip
131+
title={"Run all cells above this one, excluding this cell"}
132+
placement={"left"}
133+
>
134+
Run All Above
135+
</Tooltip>
136+
),
137+
onClick: () => actions?.run_all_above_cell(id),
138+
},
139+
{
140+
key: "all-below",
141+
icon: <Icon name={RUN_ALL_CELLS_BELOW_ICON} rotate={"90"} />,
142+
label: (
143+
<Tooltip
144+
title={"Run all cells below this one, including this cell"}
145+
placement={"left"}
146+
>
147+
Run All Below
148+
</Tooltip>
149+
),
150+
onClick: () => actions?.run_all_below_cell(id),
151+
},
152+
],
153+
}}
154+
>
155+
<Tooltip placement="top" title={tooltip}>
156+
<span style={CODE_BAR_BTN_STYLE}>
157+
<Icon name={icon} /> {label}
158+
</span>
159+
</Tooltip>
160+
</Dropdown.Button>
161+
);
162+
}
163+
122164
function renderCodeBarComputeServer() {
123165
if (!is_current || !computeServerId) return;
124166
return <ComputeServerPrompt id={computeServerId} />;

src/packages/frontend/jupyter/commands.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { NotebookFrameActions } from "@cocalc/frontend/frame-editors/jupyter-edi
1515
import { open_new_tab } from "@cocalc/frontend/misc";
1616
import { JupyterActions } from "./browser-actions";
1717
import { NotebookMode } from "@cocalc/jupyter/types";
18+
import { RUN_ALL_CELLS_ABOVE_ICON, RUN_ALL_CELLS_BELOW_ICON } from "./consts";
1819

1920
export interface KeyboardCommand {
2021
mode?: NotebookMode;
@@ -35,6 +36,7 @@ export interface CommandDescription {
3536
f: Function; // function that implements command.
3637
b?: string; // very short label; use for a button
3738
i?: IconName;
39+
ir?: "90"; // rotate icon
3840
k?: KeyboardCommand[]; // keyboard commands
3941
t?: string; // t=title = much longer description for tooltip
4042
menu?: string; // alternative to m just for dropdown menu
@@ -721,13 +723,14 @@ export function commands(actions: AllActions): {
721723
},
722724

723725
"run all cells above": {
724-
i: "caret-up",
726+
i: RUN_ALL_CELLS_ABOVE_ICON,
725727
m: "Run All Above Selected Cell",
726728
f: () => actions.frame_actions?.run_all_above(),
727729
},
728730

729731
"run all cells below": {
730-
i: "caret-down",
732+
i: RUN_ALL_CELLS_BELOW_ICON,
733+
ir: "90",
731734
m: "Run Selected Cell and All Below",
732735
f: () => actions.frame_actions?.run_all_below(),
733736
},

src/packages/frontend/jupyter/consts.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ export const MINI_BUTTONS_STYLE_INNER: CSS = {
2222
borderRight: `1px solid ${COLORS.GRAY_L}`,
2323
borderRadius: "3px 3px 3px 0",
2424
} as const;
25+
26+
export const RUN_ALL_CELLS_ABOVE_ICON = "vertical-align-bottom";
27+
export const RUN_ALL_CELLS_BELOW_ICON = "angle-double-right"; // and 90°

src/packages/frontend/jupyter/keyboard-shortcuts.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,19 @@ import {
1818
A,
1919
Icon,
2020
IconName,
21-
r_join,
2221
SearchInput,
22+
r_join,
2323
} from "@cocalc/frontend/components";
24+
import { IconRotation } from "@cocalc/frontend/components/icon";
2425
import { JupyterEditorActions } from "@cocalc/frontend/frame-editors/jupyter-editor/actions";
2526
import useNotebookFrameActions from "@cocalc/frontend/frame-editors/jupyter-editor/cell-notebook/hook";
2627
import { ShowSupportLink } from "@cocalc/frontend/support";
2728
import { capitalize, copy_without, field_cmp, split } from "@cocalc/util/misc";
2829
import { JupyterActions } from "./browser-actions";
2930
import {
3031
CommandDescription,
31-
commands as create_commands,
3232
KeyboardCommand,
33+
commands as create_commands,
3334
} from "./commands";
3435
import { evt_to_obj, keyCode_to_chr } from "./keyboard";
3536

@@ -298,17 +299,20 @@ interface CommandProps {
298299
name: string;
299300
desc: string;
300301
icon?: IconName;
302+
iconRotate?: IconRotation;
301303
shortcuts: KeyboardCommand[];
302304
taken: string;
303305
}
304306

305307
const Command: React.FC<CommandProps> = React.memo((props: CommandProps) => {
306-
const { actions, name, desc, icon, shortcuts, taken } = props;
308+
const { actions, name, desc, icon, iconRotate, shortcuts, taken } = props;
307309
const frameActions = useNotebookFrameActions();
308310
const [highlight, set_highlight] = useState<boolean>(false);
309311

310312
function render_icon(): Rendered {
311-
return <span>{icon ? <Icon name={icon} /> : undefined}</span>;
313+
return (
314+
<span>{icon ? <Icon name={icon} rotate={iconRotate} /> : undefined}</span>
315+
);
312316
}
313317

314318
function run_command() {
@@ -423,6 +427,7 @@ const CommandList: React.FC<CommandListProps> = React.memo(
423427
actions={actions}
424428
desc={desc}
425429
icon={icon}
430+
iconRotate={x.val.ir}
426431
shortcuts={shortcuts}
427432
taken={taken[x.name]}
428433
/>,

src/packages/frontend/jupyter/llm/cell-tool.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -409,9 +409,7 @@ export function LLMCellTool({
409409
<Icon name={action.icon} /> {capitalize(mode)}
410410
</Tooltip>
411411
),
412-
onClick: () => {
413-
setMode(mode as Mode);
414-
},
412+
onClick: () => setMode(mode as Mode),
415413
};
416414
},
417415
),

0 commit comments

Comments
 (0)