From ffb4d27eb57fa4445c8df3d9b4f814f7b0718f01 Mon Sep 17 00:00:00 2001 From: celine Date: Thu, 8 Jan 2026 19:26:32 -0500 Subject: [PATCH] added heyboard shortcuts --- app/[leaflet_id]/actions/HelpButton.tsx | 24 ++++ components/SelectionManager/index.tsx | 149 +++++++++++++++++++++++- 2 files changed, 172 insertions(+), 1 deletion(-) diff --git a/app/[leaflet_id]/actions/HelpButton.tsx b/app/[leaflet_id]/actions/HelpButton.tsx index d20d2f04..28690a7d 100644 --- a/app/[leaflet_id]/actions/HelpButton.tsx +++ b/app/[leaflet_id]/actions/HelpButton.tsx @@ -58,6 +58,30 @@ export const HelpButton = (props: { noShortcuts?: boolean }) => { keys={[metaKey(), isMac() ? "Ctrl" : "Meta", "X"]} /> + + + + + + {/* shift + up/down arrows (or click + drag): select multiple blocks */} diff --git a/components/SelectionManager/index.tsx b/components/SelectionManager/index.tsx index 5fd8253e..773403c1 100644 --- a/components/SelectionManager/index.tsx +++ b/components/SelectionManager/index.tsx @@ -87,6 +87,154 @@ export function SelectionManager() { } }, }, + { + metaKey: true, + altKey: true, + key: ["1", "¡"], + handler: async () => { + let [sortedBlocks] = await getSortedSelectionBound(); + for (let block of sortedBlocks) { + await rep?.mutate.assertFact({ + entity: block.value, + attribute: "block/heading-level", + data: { type: "number", value: 1 }, + }); + await rep?.mutate.assertFact({ + entity: block.value, + attribute: "block/type", + data: { type: "block-type-union", value: "heading" }, + }); + } + }, + }, + { + metaKey: true, + altKey: true, + key: ["2", "™"], + handler: async () => { + let [sortedBlocks] = await getSortedSelectionBound(); + for (let block of sortedBlocks) { + await rep?.mutate.assertFact({ + entity: block.value, + attribute: "block/heading-level", + data: { type: "number", value: 2 }, + }); + await rep?.mutate.assertFact({ + entity: block.value, + attribute: "block/type", + data: { type: "block-type-union", value: "heading" }, + }); + } + }, + }, + { + metaKey: true, + altKey: true, + key: ["3", "£"], + handler: async () => { + let [sortedBlocks] = await getSortedSelectionBound(); + for (let block of sortedBlocks) { + await rep?.mutate.assertFact({ + entity: block.value, + attribute: "block/heading-level", + data: { type: "number", value: 3 }, + }); + await rep?.mutate.assertFact({ + entity: block.value, + attribute: "block/type", + data: { type: "block-type-union", value: "heading" }, + }); + } + }, + }, + { + metaKey: true, + altKey: true, + key: ["p", "π"], + handler: async () => { + let [sortedBlocks] = await getSortedSelectionBound(); + for (let block of sortedBlocks) { + // Convert to text block + await rep?.mutate.assertFact({ + entity: block.value, + attribute: "block/type", + data: { type: "block-type-union", value: "text" }, + }); + // Remove heading level if exists + let headingLevel = await rep?.query((tx) => + scanIndex(tx).eav(block.value, "block/heading-level"), + ); + if (headingLevel?.[0]) { + await rep?.mutate.retractFact({ factID: headingLevel[0].id }); + } + // Remove text-size to make it default + let textSizeFact = await rep?.query((tx) => + scanIndex(tx).eav(block.value, "block/text-size"), + ); + if (textSizeFact?.[0]) { + await rep?.mutate.retractFact({ factID: textSizeFact[0].id }); + } + } + }, + }, + { + metaKey: true, + altKey: true, + key: ["+", "≠"], + handler: async () => { + let [sortedBlocks] = await getSortedSelectionBound(); + for (let block of sortedBlocks) { + // Convert to text block + await rep?.mutate.assertFact({ + entity: block.value, + attribute: "block/type", + data: { type: "block-type-union", value: "text" }, + }); + // Remove heading level if exists + let headingLevel = await rep?.query((tx) => + scanIndex(tx).eav(block.value, "block/heading-level"), + ); + if (headingLevel?.[0]) { + await rep?.mutate.retractFact({ factID: headingLevel[0].id }); + } + // Set text size to large + await rep?.mutate.assertFact({ + entity: block.value, + attribute: "block/text-size", + data: { type: "text-size-union", value: "large" }, + }); + } + }, + }, + { + metaKey: true, + altKey: true, + key: ["-", "–"], + handler: async () => { + let [sortedBlocks] = await getSortedSelectionBound(); + for (let block of sortedBlocks) { + // Convert to text block + await rep?.mutate.assertFact({ + entity: block.value, + attribute: "block/type", + data: { type: "block-type-union", value: "text" }, + }); + // Remove heading level if exists + let headingLevel = await rep?.query((tx) => + scanIndex(tx).eav(block.value, "block/heading-level"), + ); + if (headingLevel?.[0]) { + await rep?.mutate.retractFact({ factID: headingLevel[0].id }); + } + // Set text size to small + await rep?.mutate.assertFact({ + entity: block.value, + attribute: "block/text-size", + data: { type: "text-size-union", value: "small" }, + }); + } + }, + }, { metaKey: true, shift: true, @@ -685,7 +833,6 @@ function getContentEditableParent(e: Node | null): Node | null { return null; } - function toggleMarkInBlocks(blocks: string[], mark: MarkType, attrs?: any) { let everyBlockHasMark = blocks.reduce((acc, block) => { let editor = useEditorStates.getState().editorStates[block]; -- 2.51.2