From dc21fbe06bf31e6ca8357b698c72e7ef3860f9be Mon Sep 17 00:00:00 2001 From: Jared Pereira Date: Thu, 1 May 2025 14:09:04 -0400 Subject: [PATCH] add ctrl-j/k for cursor movement --- components/Blocks/TextBlock/keymap.ts | 101 +++++++++++------- components/Blocks/useBlockKeyboardHandlers.ts | 19 +++- 2 files changed, 79 insertions(+), 41 deletions(-) diff --git a/components/Blocks/TextBlock/keymap.ts b/components/Blocks/TextBlock/keymap.ts index 1269d18a..12a41985 100644 --- a/components/Blocks/TextBlock/keymap.ts +++ b/components/Blocks/TextBlock/keymap.ts @@ -117,43 +117,10 @@ export const TextBlockKeymap = ( } return false; }, - ArrowUp: (state, _tr, view) => { - if (!view) return false; - if (state.doc.textContent.startsWith("/")) return true; - if (useUIState.getState().selectedBlocks.length > 1) return true; - if (view.state.selection.from !== view.state.selection.to) return false; - const viewClientRect = view.dom.getBoundingClientRect(); - const coords = view.coordsAtPos(view.state.selection.anchor); - if (coords.top - viewClientRect.top < 12) { - let block = propsRef.current.previousBlock; - if (block) { - view.dom.blur(); - focusBlock(block, { left: coords.left, type: "bottom" }); - return true; - } - return false; - } - return false; - }, - ArrowDown: (state, tr, view) => { - if (!view) return true; - if (state.doc.textContent.startsWith("/")) return true; - if (useUIState.getState().selectedBlocks.length > 1) return true; - if (view.state.selection.from !== view.state.selection.to) return false; - const viewClientRect = view.dom.getBoundingClientRect(); - const coords = view.coordsAtPos(view.state.selection.anchor); - let isBottom = viewClientRect.bottom - coords.bottom < 12; - if (isBottom) { - let block = propsRef.current.nextBlock; - if (block) { - view.dom.blur(); - focusBlock(block, { left: coords.left, type: "top" }); - return true; - } - return false; - } - return false; - }, + "Ctrl-k": moveCursorUp(propsRef, repRef, true), + ArrowUp: moveCursorUp(propsRef, repRef), + "Ctrl-j": moveCursorDown(propsRef, repRef, true), + ArrowDown: moveCursorDown(propsRef, repRef), ArrowLeft: (state, tr, view) => { if (state.selection.content().size > 0) return false; if (state.selection.anchor > 1) return false; @@ -193,6 +160,64 @@ export const TextBlockKeymap = ( "Meta-Enter": CtrlEnter(propsRef, repRef), }) as { [key: string]: Command }; +const moveCursorDown = + ( + propsRef: PropsRef, + repRef: MutableRefObject | null>, + jumpToNextBlock: boolean = false, + ) => + ( + state: EditorState, + dispatch?: (tr: Transaction) => void, + view?: EditorView, + ) => { + if (!view) return true; + if (state.doc.textContent.startsWith("/")) return true; + if (useUIState.getState().selectedBlocks.length > 1) return true; + if (view.state.selection.from !== view.state.selection.to) return false; + const viewClientRect = view.dom.getBoundingClientRect(); + const coords = view.coordsAtPos(view.state.selection.anchor); + let isBottom = viewClientRect.bottom - coords.bottom < 12; + if (isBottom || jumpToNextBlock) { + let block = propsRef.current.nextBlock; + if (block) { + view.dom.blur(); + focusBlock(block, { left: coords.left, type: "top" }); + return true; + } + return false || jumpToNextBlock; + } + return false; + }; +const moveCursorUp = + ( + propsRef: PropsRef, + repRef: MutableRefObject | null>, + jumpToNextBlock: boolean = false, + ) => + ( + state: EditorState, + dispatch?: (tr: Transaction) => void, + view?: EditorView, + ) => { + if (!view) return false; + if (state.doc.textContent.startsWith("/")) return true; + if (useUIState.getState().selectedBlocks.length > 1) return true; + if (view.state.selection.from !== view.state.selection.to) return false; + const viewClientRect = view.dom.getBoundingClientRect(); + const coords = view.coordsAtPos(view.state.selection.anchor); + if (coords.top - viewClientRect.top < 12 || jumpToNextBlock) { + let block = propsRef.current.previousBlock; + if (block) { + view.dom.blur(); + focusBlock(block, { left: coords.left, type: "bottom" }); + return true; + } + return false || jumpToNextBlock; + } + return false; + }; + const backspace = ( propsRef: PropsRef, @@ -399,7 +424,7 @@ const enter = }, }); if (propsRef.current.listData) - repRef.current?.mutate.assertFact({ + await repRef.current?.mutate.assertFact({ entity: newEntityID, attribute: "block/is-list", data: { type: "boolean", value: true }, diff --git a/components/Blocks/useBlockKeyboardHandlers.ts b/components/Blocks/useBlockKeyboardHandlers.ts index bf82c8d8..326d0104 100644 --- a/components/Blocks/useBlockKeyboardHandlers.ts +++ b/components/Blocks/useBlockKeyboardHandlers.ts @@ -40,9 +40,16 @@ export function useBlockKeyboardHandlers( if (e.defaultPrevented) return; //if no permissions, do nothing if (!entity_set.permissions.write) return; - let command = { Tab, ArrowUp, ArrowDown, Backspace, Enter, Escape }[ - e.key - ]; + let command = { + Tab, + ArrowUp, + ArrowDown, + Backspace, + Enter, + Escape, + j, + k, + }[e.key]; let el = e.target as HTMLElement; if ( @@ -93,6 +100,9 @@ function Tab({ e, props, rep }: Args) { } } +function j(args: Args) { + if (args.e.ctrlKey || args.e.metaKey) ArrowDown(args); +} function ArrowDown({ e, props }: Args) { e.preventDefault(); let nextBlock = props.nextBlock; @@ -104,6 +114,9 @@ function ArrowDown({ e, props }: Args) { if (!nextBlock) return; } +function k(args: Args) { + if (args.e.ctrlKey || args.e.metaKey) ArrowUp(args); +} function ArrowUp({ e, props }: Args) { e.preventDefault(); let prevBlock = props.previousBlock; -- 2.54.0