a tool for shared writing and social publishing

only add multiblock text decoration shortcuts if multiple blocks selected

+148 -145
+147 -144
components/SelectionManager.tsx
··· 10 import { getBlocksWithType } from "src/hooks/queries/useBlocks"; 11 import { v7 } from "uuid"; 12 import { indent, outdent, outdentFull } from "src/utils/list-operations"; 13 - import { addShortcut } from "src/shortcuts"; 14 import { htmlToMarkdown } from "src/htmlMarkdownParsers"; 15 import { elementId } from "src/utils/elementId"; 16 import { scrollIntoViewIfNeeded } from "src/utils/scrollIntoViewIfNeeded"; ··· 38 if (!entity_set.permissions.write || !rep) return; 39 if (isMobile) return; 40 const getSortedSelectionBound = getSortedSelection.bind(null, rep); 41 - let removeListener = addShortcut( 42 - [ 43 - { 44 - metaKey: true, 45 - key: "ArrowUp", 46 - handler: async () => { 47 - let [firstBlock] = 48 - (await rep?.query((tx) => 49 - getBlocksWithType( 50 - tx, 51 - useUIState.getState().selectedBlocks[0].parent, 52 - ), 53 - )) || []; 54 - if (firstBlock) focusBlock(firstBlock, { type: "start" }); 55 - }, 56 }, 57 - { 58 - metaKey: true, 59 - key: "ArrowDown", 60 - handler: async () => { 61 - let blocks = 62 - (await rep?.query((tx) => 63 - getBlocksWithType( 64 - tx, 65 - useUIState.getState().selectedBlocks[0].parent, 66 - ), 67 - )) || []; 68 - let folded = useUIState.getState().foldedBlocks; 69 - blocks = blocks.filter( 70 - (f) => 71 - !f.listData || 72 - !f.listData.path.find( 73 - (path) => 74 - folded.includes(path.entity) && f.value !== path.entity, 75 - ), 76 - ); 77 - let lastBlock = blocks[blocks.length - 1]; 78 - if (lastBlock) focusBlock(lastBlock, { type: "end" }); 79 - }, 80 - }, 81 - { 82 - metaKey: true, 83 - altKey: true, 84 - key: ["l", "¬"], 85 - handler: async () => { 86 - let [sortedBlocks, siblings] = await getSortedSelectionBound(); 87 - for (let block of sortedBlocks) { 88 - if (!block.listData) { 89 - await rep?.mutate.assertFact({ 90 - entity: block.value, 91 - attribute: "block/is-list", 92 - data: { type: "boolean", value: true }, 93 - }); 94 - } else { 95 - outdentFull(block, rep); 96 - } 97 - } 98 - }, 99 }, 100 - { 101 - metaKey: true, 102 - shift: true, 103 - key: ["ArrowDown", "J"], 104 - handler: async () => { 105 - let [sortedBlocks, siblings] = await getSortedSelectionBound(); 106 - let block = sortedBlocks[0]; 107 - let nextBlock = siblings 108 - .slice(siblings.findIndex((s) => s.value === block.value) + 1) 109 - .find( 110 - (f) => 111 - f.listData && 112 - block.listData && 113 - !f.listData.path.find((f) => f.entity === block.value), 114 - ); 115 - if ( 116 - nextBlock?.listData && 117 - block.listData && 118 - nextBlock.listData.depth === block.listData.depth - 1 119 - ) { 120 - if (useUIState.getState().foldedBlocks.includes(nextBlock.value)) 121 - useUIState.getState().toggleFold(nextBlock.value); 122 - rep?.mutate.moveBlock({ 123 - block: block.value, 124 - oldParent: block.listData?.parent, 125 - newParent: nextBlock.value, 126 - position: { type: "first" }, 127 }); 128 } else { 129 - rep?.mutate.moveBlockDown({ 130 - entityID: block.value, 131 - parent: block.listData?.parent || block.parent, 132 - }); 133 } 134 - }, 135 }, 136 - { 137 - metaKey: true, 138 - shift: true, 139 - key: ["ArrowUp", "K"], 140 - handler: async () => { 141 - let [sortedBlocks, siblings] = await getSortedSelectionBound(); 142 - let block = sortedBlocks[0]; 143 - let previousBlock = 144 siblings?.[ 145 - siblings.findIndex((s) => s.value === block.value) - 1 146 ]; 147 - if (previousBlock.value === block.listData?.parent) { 148 - previousBlock = 149 - siblings?.[ 150 - siblings.findIndex((s) => s.value === block.value) - 2 151 - ]; 152 - } 153 154 - if ( 155 - previousBlock?.listData && 156 - block.listData && 157 - block.listData.depth > 1 && 158 - !previousBlock.listData.path.find( 159 - (f) => f.entity === block.listData?.parent, 160 - ) 161 - ) { 162 - let depth = block.listData.depth; 163 - let newParent = previousBlock.listData.path.find( 164 - (f) => f.depth === depth - 1, 165 - ); 166 - if (!newParent) return; 167 - if (useUIState.getState().foldedBlocks.includes(newParent.entity)) 168 - useUIState.getState().toggleFold(newParent.entity); 169 - rep?.mutate.moveBlock({ 170 - block: block.value, 171 - oldParent: block.listData?.parent, 172 - newParent: newParent.entity, 173 - position: { type: "end" }, 174 - }); 175 - } else { 176 - rep?.mutate.moveBlockUp({ 177 - entityID: block.value, 178 - parent: block.listData?.parent || block.parent, 179 - }); 180 - } 181 - }, 182 }, 183 { 184 metaKey: true, 185 key: "u", ··· 238 ); 239 }, 240 }, 241 - { 242 - metaKey: true, 243 - shift: true, 244 - key: "Enter", 245 - handler: async () => { 246 - let [sortedBlocks, siblings] = await getSortedSelectionBound(); 247 - if (!sortedBlocks[0].listData) return; 248 - useUIState.getState().toggleFold(sortedBlocks[0].value); 249 - }, 250 - }, 251 - ].map((shortcut) => ({ 252 ...shortcut, 253 handler: () => undoManager.withUndoGroup(() => shortcut.handler()), 254 })),
··· 10 import { getBlocksWithType } from "src/hooks/queries/useBlocks"; 11 import { v7 } from "uuid"; 12 import { indent, outdent, outdentFull } from "src/utils/list-operations"; 13 + import { addShortcut, Shortcut } from "src/shortcuts"; 14 import { htmlToMarkdown } from "src/htmlMarkdownParsers"; 15 import { elementId } from "src/utils/elementId"; 16 import { scrollIntoViewIfNeeded } from "src/utils/scrollIntoViewIfNeeded"; ··· 38 if (!entity_set.permissions.write || !rep) return; 39 if (isMobile) return; 40 const getSortedSelectionBound = getSortedSelection.bind(null, rep); 41 + let shortcuts: Shortcut[] = [ 42 + { 43 + metaKey: true, 44 + key: "ArrowUp", 45 + handler: async () => { 46 + let [firstBlock] = 47 + (await rep?.query((tx) => 48 + getBlocksWithType( 49 + tx, 50 + useUIState.getState().selectedBlocks[0].parent, 51 + ), 52 + )) || []; 53 + if (firstBlock) focusBlock(firstBlock, { type: "start" }); 54 }, 55 + }, 56 + { 57 + metaKey: true, 58 + key: "ArrowDown", 59 + handler: async () => { 60 + let blocks = 61 + (await rep?.query((tx) => 62 + getBlocksWithType( 63 + tx, 64 + useUIState.getState().selectedBlocks[0].parent, 65 + ), 66 + )) || []; 67 + let folded = useUIState.getState().foldedBlocks; 68 + blocks = blocks.filter( 69 + (f) => 70 + !f.listData || 71 + !f.listData.path.find( 72 + (path) => 73 + folded.includes(path.entity) && f.value !== path.entity, 74 + ), 75 + ); 76 + let lastBlock = blocks[blocks.length - 1]; 77 + if (lastBlock) focusBlock(lastBlock, { type: "end" }); 78 }, 79 + }, 80 + { 81 + metaKey: true, 82 + altKey: true, 83 + key: ["l", "¬"], 84 + handler: async () => { 85 + let [sortedBlocks, siblings] = await getSortedSelectionBound(); 86 + for (let block of sortedBlocks) { 87 + if (!block.listData) { 88 + await rep?.mutate.assertFact({ 89 + entity: block.value, 90 + attribute: "block/is-list", 91 + data: { type: "boolean", value: true }, 92 }); 93 } else { 94 + outdentFull(block, rep); 95 } 96 + } 97 + }, 98 + }, 99 + { 100 + metaKey: true, 101 + shift: true, 102 + key: ["ArrowDown", "J"], 103 + handler: async () => { 104 + let [sortedBlocks, siblings] = await getSortedSelectionBound(); 105 + let block = sortedBlocks[0]; 106 + let nextBlock = siblings 107 + .slice(siblings.findIndex((s) => s.value === block.value) + 1) 108 + .find( 109 + (f) => 110 + f.listData && 111 + block.listData && 112 + !f.listData.path.find((f) => f.entity === block.value), 113 + ); 114 + if ( 115 + nextBlock?.listData && 116 + block.listData && 117 + nextBlock.listData.depth === block.listData.depth - 1 118 + ) { 119 + if (useUIState.getState().foldedBlocks.includes(nextBlock.value)) 120 + useUIState.getState().toggleFold(nextBlock.value); 121 + rep?.mutate.moveBlock({ 122 + block: block.value, 123 + oldParent: block.listData?.parent, 124 + newParent: nextBlock.value, 125 + position: { type: "first" }, 126 + }); 127 + } else { 128 + rep?.mutate.moveBlockDown({ 129 + entityID: block.value, 130 + parent: block.listData?.parent || block.parent, 131 + }); 132 + } 133 }, 134 + }, 135 + { 136 + metaKey: true, 137 + shift: true, 138 + key: ["ArrowUp", "K"], 139 + handler: async () => { 140 + let [sortedBlocks, siblings] = await getSortedSelectionBound(); 141 + let block = sortedBlocks[0]; 142 + let previousBlock = 143 + siblings?.[siblings.findIndex((s) => s.value === block.value) - 1]; 144 + if (previousBlock.value === block.listData?.parent) { 145 + previousBlock = 146 siblings?.[ 147 + siblings.findIndex((s) => s.value === block.value) - 2 148 ]; 149 + } 150 + 151 + if ( 152 + previousBlock?.listData && 153 + block.listData && 154 + block.listData.depth > 1 && 155 + !previousBlock.listData.path.find( 156 + (f) => f.entity === block.listData?.parent, 157 + ) 158 + ) { 159 + let depth = block.listData.depth; 160 + let newParent = previousBlock.listData.path.find( 161 + (f) => f.depth === depth - 1, 162 + ); 163 + if (!newParent) return; 164 + if (useUIState.getState().foldedBlocks.includes(newParent.entity)) 165 + useUIState.getState().toggleFold(newParent.entity); 166 + rep?.mutate.moveBlock({ 167 + block: block.value, 168 + oldParent: block.listData?.parent, 169 + newParent: newParent.entity, 170 + position: { type: "end" }, 171 + }); 172 + } else { 173 + rep?.mutate.moveBlockUp({ 174 + entityID: block.value, 175 + parent: block.listData?.parent || block.parent, 176 + }); 177 + } 178 + }, 179 + }, 180 181 + { 182 + metaKey: true, 183 + shift: true, 184 + key: "Enter", 185 + handler: async () => { 186 + let [sortedBlocks, siblings] = await getSortedSelectionBound(); 187 + if (!sortedBlocks[0].listData) return; 188 + useUIState.getState().toggleFold(sortedBlocks[0].value); 189 }, 190 + }, 191 + ]; 192 + if (moreThanOneSelected) 193 + shortcuts = shortcuts.concat([ 194 { 195 metaKey: true, 196 key: "u", ··· 249 ); 250 }, 251 }, 252 + ]); 253 + let removeListener = addShortcut( 254 + shortcuts.map((shortcut) => ({ 255 ...shortcut, 256 handler: () => undoManager.withUndoGroup(() => shortcut.handler()), 257 })),
+1 -1
src/shortcuts.ts
··· 3 import { ReplicacheMutators } from "./replicache"; 4 import { isMac } from "./utils/isDevice"; 5 6 - type Shortcut = { 7 metaKey?: boolean; 8 metaAndCtrl?: boolean; 9 altKey?: boolean;
··· 3 import { ReplicacheMutators } from "./replicache"; 4 import { isMac } from "./utils/isDevice"; 5 6 + export type Shortcut = { 7 metaKey?: boolean; 8 metaAndCtrl?: boolean; 9 altKey?: boolean;