@@ -4,6 +4,8 @@ import type { EditorView } from "@codemirror/view";
44import { useAtomValue } from "jotai" ;
55import {
66 ChevronDownIcon ,
7+ ChevronsDownIcon ,
8+ ChevronsUpIcon ,
79 ChevronUpIcon ,
810 Code2Icon ,
911 EyeIcon ,
@@ -17,6 +19,7 @@ import {
1719} from "lucide-react" ;
1820import React from "react" ;
1921import useEvent from "react-use-event-hook" ;
22+ import { MinimalShortcut } from "@/components/shortcuts/renderShortcut" ;
2023import { Button } from "@/components/ui/button" ;
2124import {
2225 DropdownMenu ,
@@ -33,6 +36,7 @@ import {
3336} from "@/core/cells/cells" ;
3437import type { CellId } from "@/core/cells/ids" ;
3538import { formatEditorViews } from "@/core/codemirror/format" ;
39+ import type { HotkeyAction } from "@/core/hotkeys/hotkeys" ;
3640import { saveCellConfig } from "@/core/network/requests" ;
3741import type { CellConfig } from "@/core/network/types" ;
3842import { store } from "@/core/state/jotai" ;
@@ -44,6 +48,7 @@ import { useCellSelectionActions, useCellSelectionState } from "./selection";
4448
4549interface MultiCellActionButton extends Omit < ActionButton , "handle" > {
4650 handle : ( selectedCells : CellId [ ] ) => void ;
51+ hotkey ?: HotkeyAction ;
4752}
4853
4954const CellStateDropdown : React . FC < {
@@ -71,9 +76,19 @@ const CellStateDropdown: React.FC<{
7176 onSelect = { ( ) => action . handle ( cellIds ) }
7277 className = "flex items-center gap-2"
7378 >
74- < div className = "flex items-center gap-2" >
75- { action . icon }
76- < span className = "text-xs" > { action . label } </ span >
79+ < div className = "flex items-center flex-1" >
80+ { action . icon && (
81+ < div className = "mr-2 w-5 text-muted-foreground" >
82+ { action . icon }
83+ </ div >
84+ ) }
85+ < div className = "flex-1" > { action . label } </ div >
86+ { action . hotkey && (
87+ < MinimalShortcut
88+ shortcut = { action . hotkey }
89+ className = "ml-4"
90+ />
91+ ) }
7792 </ div >
7893 </ DropdownMenuItem >
7994 ) ;
@@ -91,7 +106,13 @@ const CellStateDropdown: React.FC<{
91106} ;
92107
93108export function useMultiCellActionButtons ( cellIds : CellId [ ] ) {
94- const { updateCellConfig, moveCell, clearCellOutput } = useCellActions ( ) ;
109+ const {
110+ updateCellConfig,
111+ moveCell,
112+ clearCellOutput,
113+ sendToTop,
114+ sendToBottom,
115+ } = useCellActions ( ) ;
95116 const deleteCell = useDeleteManyCellsCallback ( ) ;
96117 const hasOnlyOneCell = useAtomValue ( hasOnlyOneCellAtom ) ;
97118 const selectionActions = useCellSelectionActions ( ) ;
@@ -138,6 +159,19 @@ export function useMultiCellActionButtons(cellIds: CellId[]) {
138159 } ,
139160 ) ;
140161
162+ const sendSelectedCellsToTop = useEvent ( ( cellIds : CellId [ ] ) => {
163+ // Send in reverse order to maintain relative positions
164+ [ ...cellIds ] . reverse ( ) . forEach ( ( cellId ) => {
165+ sendToTop ( { cellId } ) ;
166+ } ) ;
167+ } ) ;
168+
169+ const sendSelectedCellsToBottom = useEvent ( ( cellIds : CellId [ ] ) => {
170+ cellIds . forEach ( ( cellId ) => {
171+ sendToBottom ( { cellId } ) ;
172+ } ) ;
173+ } ) ;
174+
141175 const formatSelectedCells = useEvent ( ( cellIds : CellId [ ] ) => {
142176 const editorViews : Record < CellId , EditorView > = { } ;
143177 cellIds . forEach ( ( cellId ) => {
@@ -176,18 +210,21 @@ export function useMultiCellActionButtons(cellIds: CellId[]) {
176210 icon : < PlayIcon size = { 13 } strokeWidth = { 1.5 } /> ,
177211 label : "Run cells" ,
178212 handle : ( cellIds ) => runCells ( cellIds ) ,
213+ hotkey : "cell.run" ,
179214 } ,
180215 ] ,
181216 [
182217 {
183218 icon : < ChevronUpIcon size = { 13 } strokeWidth = { 1.5 } /> ,
184219 label : "Move up" ,
185220 handle : ( cellIds ) => moveSelectedCells ( cellIds , "up" ) ,
221+ hotkey : "cell.moveUp" ,
186222 } ,
187223 {
188224 icon : < ChevronDownIcon size = { 13 } strokeWidth = { 1.5 } /> ,
189225 label : "Move down" ,
190226 handle : ( cellIds ) => moveSelectedCells ( cellIds , "down" ) ,
227+ hotkey : "cell.moveDown" ,
191228 } ,
192229 ] ,
193230 [
@@ -207,6 +244,7 @@ export function useMultiCellActionButtons(cellIds: CellId[]) {
207244 icon : < Code2Icon size = { 13 } strokeWidth = { 1.5 } /> ,
208245 label : "Format cells" ,
209246 handle : formatSelectedCells ,
247+ hotkey : "cell.format" ,
210248 } ,
211249 {
212250 icon : < XCircleIcon size = { 13 } strokeWidth = { 1.5 } /> ,
@@ -220,12 +258,40 @@ export function useMultiCellActionButtons(cellIds: CellId[]) {
220258 label : "Hide code" ,
221259 handle : ( cellIds ) =>
222260 toggleSelectedCellsProperty ( cellIds , "hide_code" , true ) ,
261+ hotkey : "cell.hideCode" ,
223262 } ,
224263 {
225264 icon : < EyeIcon size = { 13 } strokeWidth = { 1.5 } /> ,
226265 label : "Show code" ,
227266 handle : ( cellIds ) =>
228267 toggleSelectedCellsProperty ( cellIds , "hide_code" , false ) ,
268+ hotkey : "cell.hideCode" ,
269+ } ,
270+ ] ,
271+ [
272+ {
273+ icon : < ChevronUpIcon size = { 13 } strokeWidth = { 1.5 } /> ,
274+ label : "Move up" ,
275+ handle : ( cellIds ) => moveSelectedCells ( cellIds , "up" ) ,
276+ hotkey : "cell.moveUp" ,
277+ } ,
278+ {
279+ icon : < ChevronDownIcon size = { 13 } strokeWidth = { 1.5 } /> ,
280+ label : "Move down" ,
281+ handle : ( cellIds ) => moveSelectedCells ( cellIds , "down" ) ,
282+ hotkey : "cell.moveDown" ,
283+ } ,
284+ {
285+ icon : < ChevronsUpIcon size = { 13 } strokeWidth = { 1.5 } /> ,
286+ label : "Send to top" ,
287+ handle : sendSelectedCellsToTop ,
288+ hotkey : "cell.sendToTop" ,
289+ } ,
290+ {
291+ icon : < ChevronsDownIcon size = { 13 } strokeWidth = { 1.5 } /> ,
292+ label : "Send to bottom" ,
293+ handle : sendSelectedCellsToBottom ,
294+ hotkey : "cell.sendToBottom" ,
229295 } ,
230296 ] ,
231297 [
@@ -317,11 +383,16 @@ const MultiCellActionToolbarInternal = ({ cellIds }: { cellIds: CellId[] }) => {
317383 }
318384 size = "sm"
319385 onClick = { ( ) => action . handle ( cellIds ) }
320- className = "h-8 px-2 gap-1 flex-shrink-0"
386+ className = "h-8 px-2 gap-1 flex-shrink-0 flex items-center "
321387 title = { action . label }
322388 >
323389 { action . icon }
324390 < span className = "text-xs" > { action . label } </ span >
391+ { action . hotkey && (
392+ < div className = "ml-1 border bg-muted rounded-md px-1" >
393+ < MinimalShortcut shortcut = { action . hotkey } />
394+ </ div >
395+ ) }
325396 </ Button >
326397 ) ) }
327398 { groupIndex < actions . length - 1 && < Separator /> }
0 commit comments