mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 11:47:54 +01:00
Merge pull request #7655 from 01zulfi/global/keyboard
global: keyboard shortcuts documentation && web dialog
This commit is contained in:
@@ -230,7 +230,7 @@ kbd {
|
||||
background: var(--background);
|
||||
border-radius: 3px;
|
||||
padding: 2px 5px;
|
||||
color: var(--paragraph-secondary);
|
||||
color: var(--paragraph);
|
||||
}
|
||||
|
||||
.ping {
|
||||
|
||||
@@ -23,6 +23,9 @@ import { useStore as useSearchStore } from "../stores/search-store";
|
||||
import { useEditorManager } from "../components/editor/manager";
|
||||
import { CommandPaletteDialog } from "../dialogs/command-palette";
|
||||
import { hashNavigate } from "../navigation";
|
||||
import { getKeybinding, keybindings } from "@notesnook/common";
|
||||
import { KeyboardShortcutsDialog } from "../dialogs/keyboard-shortcuts-dialog";
|
||||
import { isMac } from "../utils/platform";
|
||||
|
||||
function isInEditor(e: KeyboardEvent) {
|
||||
return (
|
||||
@@ -30,200 +33,68 @@ function isInEditor(e: KeyboardEvent) {
|
||||
);
|
||||
}
|
||||
|
||||
const KEYMAP = [
|
||||
// {
|
||||
// keys: ["command+n", "ctrl+n", "command+alt+n", "ctrl+alt+n"],
|
||||
// description: "Create a new note",
|
||||
// global: true,
|
||||
// action: (e) => {
|
||||
// e.preventDefault();
|
||||
// hashNavigate("/notes/create", {
|
||||
// addNonce: true,
|
||||
// replace: true,
|
||||
// notify: true,
|
||||
// });
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// keys: [
|
||||
// "command+shift+n",
|
||||
// "ctrl+shift+n",
|
||||
// "command+shift+alt+n",
|
||||
// "ctrl+shift+alt+n",
|
||||
// ],
|
||||
// description: "Create a new notebook",
|
||||
// global: true,
|
||||
// action: (e) => {
|
||||
// e.preventDefault();
|
||||
// hashNavigate("/notebooks/create", {
|
||||
// replace: true,
|
||||
// notify: true,
|
||||
// });
|
||||
// },
|
||||
// },
|
||||
{
|
||||
keys: [
|
||||
"command+option+right",
|
||||
"ctrl+alt+right",
|
||||
"command+option+shift+right",
|
||||
"ctrl+alt+shift+right",
|
||||
"ctrl+tab",
|
||||
"command+tab"
|
||||
],
|
||||
description: "Go to next tab",
|
||||
action: () => useEditorStore.getState().focusNextTab()
|
||||
},
|
||||
{
|
||||
keys: [
|
||||
"command+option+left",
|
||||
"ctrl+alt+left",
|
||||
"command+option+shift+left",
|
||||
"ctrl+alt+shift+left",
|
||||
"ctrl+shift+tab",
|
||||
"command+shift+tab"
|
||||
],
|
||||
description: "Go to previous tab",
|
||||
action: () => useEditorStore.getState().focusPreviousTab()
|
||||
},
|
||||
{
|
||||
keys: ["ctrl+t", "command+t"],
|
||||
description: "Create a new tab",
|
||||
action: () => useEditorStore.getState().addTab()
|
||||
},
|
||||
{
|
||||
keys: ["ctrl+n", "command+n"],
|
||||
description: "Create a new note",
|
||||
action: () => useEditorStore.getState().newSession()
|
||||
},
|
||||
{
|
||||
keys: ["ctrl+w", "command+w"],
|
||||
description:
|
||||
"Close active tab or focus previously activated tab if active tab pinned",
|
||||
action: () => {
|
||||
const activeTab = useEditorStore.getState().getActiveTab();
|
||||
if (activeTab?.pinned) {
|
||||
useEditorStore.getState().focusLastActiveTab();
|
||||
return;
|
||||
}
|
||||
useEditorStore.getState().closeActiveTab();
|
||||
const actions: Partial<
|
||||
Record<keyof typeof keybindings, (() => void) | ((e: KeyboardEvent) => void)>
|
||||
> = {
|
||||
nextTab: () => useEditorStore.getState().focusNextTab(),
|
||||
previousTab: () => useEditorStore.getState().focusPreviousTab(),
|
||||
newTab: () => useEditorStore.getState().addTab(),
|
||||
newNote: () => useEditorStore.getState().newSession(),
|
||||
closeActiveTab: () => {
|
||||
const activeTab = useEditorStore.getState().getActiveTab();
|
||||
if (activeTab?.pinned) {
|
||||
useEditorStore.getState().focusLastActiveTab();
|
||||
return;
|
||||
}
|
||||
useEditorStore.getState().closeActiveTab();
|
||||
},
|
||||
{
|
||||
keys: ["ctrl+shift+w", "command+shift+w"],
|
||||
description: "Close all tabs",
|
||||
action: () => useEditorStore.getState().closeAllTabs()
|
||||
},
|
||||
{
|
||||
keys: ["command+f", "ctrl+f"],
|
||||
description: "Search all notes",
|
||||
global: false,
|
||||
action: (e: KeyboardEvent) => {
|
||||
if (isInEditor(e)) {
|
||||
const activeSession = useEditorStore.getState().getActiveSession();
|
||||
if (activeSession?.type === "readonly") {
|
||||
e.preventDefault();
|
||||
const editor = useEditorManager
|
||||
.getState()
|
||||
.getEditor(activeSession.id);
|
||||
editor?.editor?.startSearch();
|
||||
}
|
||||
return;
|
||||
closeAllTabs: () => useEditorStore.getState().closeAllTabs(),
|
||||
searchInNotes: (e: KeyboardEvent) => {
|
||||
if (isInEditor(e)) {
|
||||
const activeSession = useEditorStore.getState().getActiveSession();
|
||||
if (activeSession?.type === "readonly") {
|
||||
e.preventDefault();
|
||||
const editor = useEditorManager.getState().getEditor(activeSession.id);
|
||||
editor?.editor?.startSearch();
|
||||
}
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
|
||||
useSearchStore.setState({ isSearching: true, searchType: "notes" });
|
||||
}
|
||||
useSearchStore.setState({ isSearching: true, searchType: "notes" });
|
||||
},
|
||||
// {
|
||||
// keys: ["alt+n"],
|
||||
// description: "Go to Notes",
|
||||
// global: false,
|
||||
// action: (e) => {
|
||||
// e.preventDefault();
|
||||
// navigate("/notes");
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// keys: ["alt+b"],
|
||||
// description: "Go to Notebooks",
|
||||
// global: false,
|
||||
// action: (e) => {
|
||||
// e.preventDefault();
|
||||
// navigate("/notebooks");
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// keys: ["alt+f"],
|
||||
// description: "Go to Favorites",
|
||||
// global: false,
|
||||
// action: (e) => {
|
||||
// e.preventDefault();
|
||||
// navigate("/favorites");
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// keys: ["alt+t"],
|
||||
// description: "Go to Tags",
|
||||
// global: false,
|
||||
// action: (e) => {
|
||||
// e.preventDefault();
|
||||
// navigate("/tags");
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// keys: ["alt+d"],
|
||||
// description: "Go to Trash",
|
||||
// global: false,
|
||||
// action: (e) => {
|
||||
// e.preventDefault();
|
||||
// navigate("/trash");
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// keys: ["alt+s"],
|
||||
// description: "Go to Settings",
|
||||
// global: false,
|
||||
// action: (e) => {
|
||||
// e.preventDefault();
|
||||
// navigate("/settings");
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// keys: ["command+d", "ctrl+d"],
|
||||
// description: "Toggle dark/light mode",
|
||||
// global: true,
|
||||
// action: (e) => {
|
||||
// e.preventDefault();
|
||||
// themestore.get().toggleNightMode();
|
||||
// },
|
||||
// },
|
||||
{
|
||||
keys: ["ctrl+k", "cmd+k", "ctrl+p", "cmd+p"],
|
||||
description: "Open command palette",
|
||||
action: (e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
CommandPaletteDialog.close();
|
||||
CommandPaletteDialog.show({
|
||||
isCommandMode: e.key === "k"
|
||||
}).catch(() => {});
|
||||
}
|
||||
openCommandPalette: () => {
|
||||
CommandPaletteDialog.close();
|
||||
CommandPaletteDialog.show({
|
||||
isCommandMode: true
|
||||
}).catch(() => {});
|
||||
},
|
||||
{
|
||||
keys: ["ctrl+,", "command+,"],
|
||||
description: "Open settings",
|
||||
action: () => hashNavigate("/settings", { replace: true })
|
||||
}
|
||||
];
|
||||
openQuickOpen: () => {
|
||||
CommandPaletteDialog.close();
|
||||
CommandPaletteDialog.show({
|
||||
isCommandMode: false
|
||||
}).catch(() => {});
|
||||
},
|
||||
openSettings: () => hashNavigate("/settings", { replace: true }),
|
||||
openKeyboardShortcuts: () => KeyboardShortcutsDialog.show({})
|
||||
};
|
||||
|
||||
export function registerKeyMap() {
|
||||
hotkeys.filter = function () {
|
||||
return true;
|
||||
};
|
||||
|
||||
KEYMAP.forEach((key) => {
|
||||
hotkeys(key.keys.join(","), (e) => {
|
||||
Object.entries(actions).forEach(([id, action]) => {
|
||||
const keys = getKeybinding(
|
||||
id as keyof typeof keybindings,
|
||||
IS_DESKTOP_APP,
|
||||
isMac()
|
||||
);
|
||||
if (!keys || keys.length === 0) return;
|
||||
|
||||
hotkeys(keys.join(","), (e) => {
|
||||
e.preventDefault();
|
||||
key.action?.(e);
|
||||
action(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,7 +17,12 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { debounce, usePromise } from "@notesnook/common";
|
||||
import {
|
||||
debounce,
|
||||
formatKey,
|
||||
keybindings,
|
||||
usePromise
|
||||
} from "@notesnook/common";
|
||||
import { EVENTS, fuzzy, Note, Notebook, Reminder, Tag } from "@notesnook/core";
|
||||
import { Box, Button, Flex, Input, Text } from "@theme-ui/components";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
@@ -539,7 +544,10 @@ function getCommandPaletteHelp(isCommandMode: boolean) {
|
||||
...(isCommandMode
|
||||
? [
|
||||
{
|
||||
key: isMac() ? "⌘P" : "Ctrl+P",
|
||||
key: keybindings.openQuickOpen
|
||||
.keys(IS_DESKTOP_APP)
|
||||
.map((k) => formatKey(k, isMac(), "+"))
|
||||
.join(" / "),
|
||||
description: strings.quickOpen()
|
||||
}
|
||||
]
|
||||
@@ -553,7 +561,10 @@ function getCommandPaletteHelp(isCommandMode: boolean) {
|
||||
description: strings.createNewNote()
|
||||
},
|
||||
{
|
||||
key: isMac() ? "⌘K" : "Ctrl+K",
|
||||
key: keybindings.openCommandPalette
|
||||
.keys(IS_DESKTOP_APP)
|
||||
.map((k) => formatKey(k, isMac(), "+"))
|
||||
.join(" / "),
|
||||
description: strings.commandPalette()
|
||||
}
|
||||
])
|
||||
|
||||
@@ -50,6 +50,7 @@ import { notebookMenuItems } from "../../components/notebook";
|
||||
import { tagMenuItems } from "../../components/tag";
|
||||
import { useEditorManager } from "../../components/editor/manager";
|
||||
import Config from "../../utils/config";
|
||||
import { KeyboardShortcutsDialog } from "../keyboard-shortcuts-dialog";
|
||||
|
||||
export interface BaseCommand {
|
||||
id: string;
|
||||
@@ -181,6 +182,14 @@ const staticCommands: Command[] = [
|
||||
group: strings.navigate(),
|
||||
type: "command"
|
||||
},
|
||||
{
|
||||
id: "keyboard-shortcuts",
|
||||
title: "Keyboard shortcuts",
|
||||
icon: ArrowTopRight,
|
||||
action: () => KeyboardShortcutsDialog.show({}),
|
||||
group: strings.navigate(),
|
||||
type: "command"
|
||||
},
|
||||
{
|
||||
id: "attachment-manager",
|
||||
title: strings.attachmentManager(),
|
||||
|
||||
112
apps/web/src/dialogs/keyboard-shortcuts-dialog.tsx
Normal file
112
apps/web/src/dialogs/keyboard-shortcuts-dialog.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { formatKey, getGroupedKeybindings } from "@notesnook/common";
|
||||
import { Flex, Text } from "@theme-ui/components";
|
||||
import { DialogManager } from "../common/dialog-manager";
|
||||
import Dialog from "../components/dialog";
|
||||
import { isMac } from "../utils/platform";
|
||||
|
||||
const groupedKeybindings = getGroupedKeybindings(IS_DESKTOP_APP, isMac());
|
||||
|
||||
export const KeyboardShortcutsDialog = DialogManager.register(
|
||||
function KeyboardShortcutsDialog(props) {
|
||||
return (
|
||||
<Dialog
|
||||
isOpen={true}
|
||||
title={"Keyboard Shortcuts"}
|
||||
width={750}
|
||||
onClose={() => props.onClose(false)}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
flexWrap: "nowrap",
|
||||
mt: 2,
|
||||
gap: 1
|
||||
}}
|
||||
>
|
||||
{groupedKeybindings.map((group) => {
|
||||
return (
|
||||
<Flex key={group.category} sx={{ flexDirection: "column" }}>
|
||||
<Text
|
||||
variant="subtitle"
|
||||
sx={{
|
||||
borderBottom: "1px solid var(--border)",
|
||||
mb: 1,
|
||||
pb: 1
|
||||
}}
|
||||
>
|
||||
{group.category}
|
||||
</Text>
|
||||
{group.shortcuts.map((shortcut) => {
|
||||
return (
|
||||
<Flex
|
||||
key={shortcut.description}
|
||||
sx={{
|
||||
mb: 2,
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
>
|
||||
<Text variant="body">{shortcut.description}</Text>
|
||||
<Keys keys={shortcut.keys} />
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
function Keys({ keys }: { keys: string[] }) {
|
||||
return (
|
||||
<Flex sx={{ gap: 1 }}>
|
||||
{keys.map((key, index) => (
|
||||
<>
|
||||
{key.split(" ").map((k) => (
|
||||
<Text
|
||||
key={k}
|
||||
as="code"
|
||||
sx={{
|
||||
bg: "background",
|
||||
color: "paragraph",
|
||||
px: 1,
|
||||
borderRadius: 5,
|
||||
fontSize: "body",
|
||||
border: "1px solid var(--border)"
|
||||
}}
|
||||
>
|
||||
{formatKey(k, isMac())}
|
||||
</Text>
|
||||
))}
|
||||
{keys.length - 1 !== index && (
|
||||
<Text as="span" sx={{ fontSize: "0.8em" }}>
|
||||
/
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
76
docs/help/contents/keyboard-shortcuts.md
Normal file
76
docs/help/contents/keyboard-shortcuts.md
Normal file
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Keyboard Shortcuts
|
||||
description: Keyboard shortcuts for Notesnook
|
||||
---
|
||||
|
||||
The following keyboard shortcuts will help you navigate Notesnook faster.
|
||||
|
||||
### General
|
||||
|
||||
| Description | Web | Windows/Linux | Mac |
|
||||
| --- | --- | --- | --- |
|
||||
| Search in notes list view if editor is not focused | Ctrl F | Ctrl F | ⌘ F |
|
||||
| Settings | Ctrl , | Ctrl , | ⌘ , |
|
||||
| Keyboard shortcuts | Ctrl / | Ctrl / | ⌘ / |
|
||||
| New note | - | Ctrl N | ⌘ N |
|
||||
|
||||
### Navigation
|
||||
|
||||
| Description | Web | Windows/Linux | Mac |
|
||||
| --- | --- | --- | --- |
|
||||
| Next tab | Ctrl Alt → / Ctrl Alt ⇧ → | Ctrl tab | ⌘ tab |
|
||||
| Previous tab | Ctrl Alt ← / Ctrl Alt ⇧ ← | Ctrl ⇧ tab | ⌘ ⇧ tab |
|
||||
| Command palette | Ctrl K | Ctrl K | ⌘ K |
|
||||
| Quick open | Ctrl P | Ctrl P | ⌘ P |
|
||||
| New tab | - | Ctrl T | ⌘ T |
|
||||
| Close active tab | - | Ctrl W | ⌘ W |
|
||||
| Close all tabs | - | Ctrl ⇧ W | ⌘ ⇧ W |
|
||||
|
||||
### Editor
|
||||
|
||||
| Description | Web | Windows/Linux | Mac |
|
||||
| --- | --- | --- | --- |
|
||||
| Add attachment | Ctrl ⇧ A | Ctrl ⇧ A | ⌘ ⇧ A |
|
||||
| Insert blockquote | Ctrl ⇧ B | Ctrl ⇧ B | ⌘ ⇧ B |
|
||||
| Toggle bold | Ctrl B | Ctrl B | ⌘ B |
|
||||
| Toggle bullet list | Ctrl ⇧ 8 | Ctrl ⇧ 8 | ⌘ ⇧ 8 |
|
||||
| Toggle check list | Ctrl ⇧ 9 | Ctrl ⇧ 9 | ⌘ ⇧ 9 |
|
||||
| Split list item | ↵ | ↵ | ↵ |
|
||||
| Lift list item | ⇧ Tab | ⇧ Tab | ⇧ Tab |
|
||||
| Sink list item | Ctrl ⇧ Down | Ctrl ⇧ Down | ⌘ ⇧ Down |
|
||||
| Toggle code | Ctrl E | Ctrl E | ⌘ E |
|
||||
| Toggle code block | Ctrl ⇧ C | Ctrl ⇧ C | ⌘ ⇧ C |
|
||||
| Insert date | Alt D | Alt D | ⌥ D |
|
||||
| Insert time | Alt T | Alt T | ⌥ T |
|
||||
| Insert date and time | Ctrl Alt D | Ctrl Alt D | ⌘ ⌥ D |
|
||||
| Insert date and time with timezone | Ctrl Alt Z | Ctrl Alt Z | ⌘ ⌥ Z |
|
||||
| Increase font size | Ctrl [ | Ctrl [ | ⌘ [ |
|
||||
| Decrease font size | Ctrl ] | Ctrl ] | ⌘ ] |
|
||||
| Insert paragraph | Ctrl ⇧ 0 | Ctrl ⇧ 0 | ⌘ ⇧ 0 |
|
||||
| Insert heading 1 | Ctrl Alt 1 | Ctrl Alt 1 | ⌘ ⌥ 1 |
|
||||
| Insert heading 2 | Ctrl Alt 2 | Ctrl Alt 2 | ⌘ ⌥ 2 |
|
||||
| Insert heading 3 | Ctrl Alt 3 | Ctrl Alt 3 | ⌘ ⌥ 3 |
|
||||
| Insert heading 4 | Ctrl Alt 4 | Ctrl Alt 4 | ⌘ ⌥ 4 |
|
||||
| Insert heading 5 | Ctrl Alt 5 | Ctrl Alt 5 | ⌘ ⌥ 5 |
|
||||
| Insert heading 6 | Ctrl Alt 6 | Ctrl Alt 6 | ⌘ ⌥ 6 |
|
||||
| Undo | Ctrl Z | Ctrl Z | ⌘ Z |
|
||||
| Redo | Ctrl ⇧ Z / Ctrl Y | Ctrl ⇧ Z / Ctrl Y | ⌘ ⇧ Z / ⌘ Y |
|
||||
| Add image | Ctrl ⇧ I | Ctrl ⇧ I | ⌘ ⇧ I |
|
||||
| Toggle italic | Ctrl I | Ctrl I | ⌘ I |
|
||||
| Remove formatting in selection | Ctrl \ | Ctrl \ | ⌘ \ |
|
||||
| Insert internal link | Ctrl ⇧ L | Ctrl ⇧ L | ⌘ ⇧ L |
|
||||
| Insert link | Ctrl ⇧ K | Ctrl ⇧ K | ⌘ ⇧ K |
|
||||
| Insert math block | Ctrl ⇧ M | Ctrl ⇧ M | ⌘ ⇧ M |
|
||||
| Toggle ordered list | Ctrl ⇧ 7 | Ctrl ⇧ 7 | ⌘ ⇧ 7 |
|
||||
| Toggle outline list | Ctrl ⇧ O | Ctrl ⇧ O | ⌘ ⇧ O |
|
||||
| Toggle outline list expand | Ctrl Space | Ctrl Space | ⌘ Space |
|
||||
| Open search | Ctrl F | Ctrl F | ⌘ F |
|
||||
| Toggle strike | Ctrl ⇧ S | Ctrl ⇧ S | ⌘ ⇧ S |
|
||||
| Toggle subscript | Ctrl , | Ctrl , | ⌘ , |
|
||||
| Toggle superscript | Ctrl . | Ctrl . | ⌘ . |
|
||||
| Toggle task list | Ctrl ⇧ T | Ctrl ⇧ T | ⌘ ⇧ T |
|
||||
| Text align center | Ctrl ⇧ E | Ctrl ⇧ E | ⌘ ⇧ E |
|
||||
| Text align justify | Ctrl ⇧ J | Ctrl ⇧ J | ⌘ ⇧ J |
|
||||
| Text align left | Ctrl ⇧ L | Ctrl ⇧ L | ⌘ ⇧ L |
|
||||
| Text align right | Ctrl ⇧ R | Ctrl ⇧ R | ⌘ ⇧ R |
|
||||
| Underline | Ctrl U | Ctrl U | ⌘ U |
|
||||
@@ -54,6 +54,7 @@ navigation:
|
||||
- path: deleting-your-account.md
|
||||
- path: app-lock.md
|
||||
- path: gift-cards.md
|
||||
- path: keyboard-shortcuts.md
|
||||
|
||||
- path: privacy-mode.md
|
||||
- path: web-clipper
|
||||
|
||||
49
docs/help/package-lock.json
generated
Normal file
49
docs/help/package-lock.json
generated
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "@notesnook/docs-help",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/docs-help",
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"devDependencies": {
|
||||
"@notesnook/common": "file:../../packages/common"
|
||||
}
|
||||
},
|
||||
"../../../packages/common": {
|
||||
"extraneous": true
|
||||
},
|
||||
"../../packages/common": {
|
||||
"name": "@notesnook/common",
|
||||
"version": "2.1.3",
|
||||
"dev": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@notesnook/core": "file:../core",
|
||||
"@readme/data-urls": "^3.0.0",
|
||||
"dayjs": "1.11.13",
|
||||
"pathe": "^1.1.2",
|
||||
"timeago.js": "4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@notesnook/core": "file:../core",
|
||||
"@types/react": "18.3.5",
|
||||
"react": "18.3.1",
|
||||
"vitest": "2.1.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18",
|
||||
"timeago.js": "4.0.2"
|
||||
}
|
||||
},
|
||||
"../common": {
|
||||
"extraneous": true
|
||||
},
|
||||
"node_modules/@notesnook/common": {
|
||||
"resolved": "../../packages/common",
|
||||
"link": true
|
||||
}
|
||||
}
|
||||
}
|
||||
25
docs/help/package.json
Normal file
25
docs/help/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@notesnook/docs-help",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"document-keyboard-shortcuts": "node scripts/document-keyboard-shortcuts.mjs"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/streetwriters/notesnook.git"
|
||||
},
|
||||
"keywords": [
|
||||
"notesnook",
|
||||
"docs",
|
||||
"help"
|
||||
],
|
||||
"author": "",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"bugs": {
|
||||
"url": "https://github.com/streetwriters/notesnook/issues"
|
||||
},
|
||||
"homepage": "https://github.com/streetwriters/notesnook#readme",
|
||||
"devDependencies": {
|
||||
"@notesnook/common": "file:../../packages/common"
|
||||
}
|
||||
}
|
||||
99
docs/help/scripts/document-keyboard-shortcuts.mjs
Normal file
99
docs/help/scripts/document-keyboard-shortcuts.mjs
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { writeFileSync } from "fs";
|
||||
import {
|
||||
getGroupedKeybindings,
|
||||
formatKey,
|
||||
macify,
|
||||
CATEGORIES
|
||||
} from "@notesnook/common";
|
||||
|
||||
console.log("Generating keyboard shortcuts documentation...");
|
||||
|
||||
const keyboardShortcutFilePath = "./contents/keyboard-shortcuts.md";
|
||||
|
||||
const frontmatter = `---
|
||||
title: Keyboard Shortcuts
|
||||
description: Keyboard shortcuts for Notesnook
|
||||
---
|
||||
`;
|
||||
|
||||
const content =
|
||||
"The following keyboard shortcuts will help you navigate Notesnook faster.";
|
||||
|
||||
const markdownTable = getGroupedTableKeybindingsMarkdown();
|
||||
|
||||
writeFileSync(
|
||||
keyboardShortcutFilePath,
|
||||
frontmatter + "\n" + content + "\n\n" + markdownTable,
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
console.log("Keyboard shortcuts documentation updated successfully!");
|
||||
|
||||
/**
|
||||
* @returns markdown formatted table of keyboard shortcuts grouped by category.
|
||||
*/
|
||||
function getGroupedTableKeybindingsMarkdown() {
|
||||
const desktopKeybindings = getGroupedKeybindings(true, false);
|
||||
const webKeybindings = getGroupedKeybindings(false, false);
|
||||
|
||||
const header = `| Description | Web | Windows/Linux | Mac |
|
||||
| --- | --- | --- | --- |`;
|
||||
|
||||
return CATEGORIES.map((category) => {
|
||||
const webShortcuts =
|
||||
webKeybindings.find((g) => g.category === category)?.shortcuts || [];
|
||||
const desktopShortcuts =
|
||||
desktopKeybindings.find((g) => g.category === category)?.shortcuts || [];
|
||||
|
||||
const mergedShortcuts = {};
|
||||
|
||||
webShortcuts.forEach(({ description, keys }) => {
|
||||
if (!mergedShortcuts[description]) {
|
||||
mergedShortcuts[description] = {};
|
||||
}
|
||||
mergedShortcuts[description].web = keys;
|
||||
});
|
||||
desktopShortcuts.forEach(({ description, keys }) => {
|
||||
if (!mergedShortcuts[description]) {
|
||||
mergedShortcuts[description] = {};
|
||||
}
|
||||
mergedShortcuts[description].desktop = keys;
|
||||
});
|
||||
|
||||
const rows = Object.entries(mergedShortcuts)
|
||||
.map(([description, { web, desktop }]) => {
|
||||
const webKeys = web?.map((k) => formatKey(k)).join(" / ") || "-";
|
||||
const windowsLinuxKeys =
|
||||
desktop?.map((k) => formatKey(k)).join(" / ") || "-";
|
||||
const macKeys =
|
||||
desktop
|
||||
?.map(macify)
|
||||
.map((k) => formatKey(k, true))
|
||||
.join(" / ") || "-";
|
||||
|
||||
return `| ${description} | ${webKeys} | ${windowsLinuxKeys} | ${macKeys} |`;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
return `### ${category}\n\n${header}\n${rows}`;
|
||||
}).join("\n\n");
|
||||
}
|
||||
@@ -28,3 +28,4 @@ export * from "./migrate-toolbar.js";
|
||||
export * from "./export-notes.js";
|
||||
export * from "./dataurl.js";
|
||||
export * from "./tab-session-history.js";
|
||||
export * from "./keybindings.js";
|
||||
|
||||
485
packages/common/src/utils/keybindings.ts
Normal file
485
packages/common/src/utils/keybindings.ts
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
interface Hotkeys {
|
||||
keys: (isDesktop: boolean) => string[];
|
||||
description: string;
|
||||
category: Category;
|
||||
type: "hotkeys";
|
||||
}
|
||||
|
||||
interface TipTapKey {
|
||||
keys: string | string[];
|
||||
description: string;
|
||||
category: Category;
|
||||
type: "tiptap";
|
||||
}
|
||||
|
||||
type Category = (typeof CATEGORIES)[number];
|
||||
|
||||
export const CATEGORIES = ["General", "Navigation", "Editor"] as const;
|
||||
|
||||
/**
|
||||
* consumed by hotkeys-js
|
||||
*/
|
||||
export const hotkeys = {
|
||||
nextTab: {
|
||||
keys: normalizeKeys({
|
||||
web: ["ctrl+alt+right", "ctrl+alt+shift+right"],
|
||||
desktop: ["ctrl+tab"]
|
||||
}),
|
||||
description: "Next tab",
|
||||
category: "Navigation",
|
||||
type: "hotkeys"
|
||||
},
|
||||
previousTab: {
|
||||
keys: normalizeKeys({
|
||||
web: ["ctrl+alt+left", "ctrl+alt+shift+left"],
|
||||
desktop: ["ctrl+shift+tab"]
|
||||
}),
|
||||
description: "Previous tab",
|
||||
category: "Navigation",
|
||||
type: "hotkeys"
|
||||
},
|
||||
newTab: {
|
||||
keys: normalizeKeys({
|
||||
desktop: ["ctrl+t"]
|
||||
}),
|
||||
description: "New tab",
|
||||
category: "Navigation",
|
||||
type: "hotkeys"
|
||||
},
|
||||
closeActiveTab: {
|
||||
keys: normalizeKeys({
|
||||
desktop: ["ctrl+w"]
|
||||
}),
|
||||
description: "Close active tab",
|
||||
category: "Navigation",
|
||||
type: "hotkeys"
|
||||
},
|
||||
closeAllTabs: {
|
||||
keys: normalizeKeys({
|
||||
desktop: ["ctrl+shift+w"]
|
||||
}),
|
||||
description: "Close all tabs",
|
||||
category: "Navigation",
|
||||
type: "hotkeys"
|
||||
},
|
||||
newNote: {
|
||||
keys: normalizeKeys({
|
||||
desktop: ["ctrl+n"]
|
||||
}),
|
||||
description: "New note",
|
||||
category: "General",
|
||||
type: "hotkeys"
|
||||
},
|
||||
searchInNotes: {
|
||||
keys: normalizeKeys(["ctrl+f"]),
|
||||
description: "Search in notes list view if editor is not focused",
|
||||
category: "General",
|
||||
type: "hotkeys"
|
||||
},
|
||||
openCommandPalette: {
|
||||
keys: normalizeKeys(["ctrl+k"]),
|
||||
description: "Command palette",
|
||||
category: "Navigation",
|
||||
type: "hotkeys"
|
||||
},
|
||||
openQuickOpen: {
|
||||
keys: normalizeKeys(["ctrl+p"]),
|
||||
description: "Quick open",
|
||||
category: "Navigation",
|
||||
type: "hotkeys"
|
||||
},
|
||||
openSettings: {
|
||||
keys: normalizeKeys(["ctrl+,"]),
|
||||
description: "Settings",
|
||||
category: "General",
|
||||
type: "hotkeys"
|
||||
},
|
||||
openKeyboardShortcuts: {
|
||||
keys: normalizeKeys(["ctrl+/"]),
|
||||
description: "Keyboard shortcuts",
|
||||
category: "General",
|
||||
type: "hotkeys"
|
||||
}
|
||||
} satisfies Record<string, Hotkeys>;
|
||||
|
||||
/**
|
||||
* consumed by tiptap
|
||||
*/
|
||||
export const tiptapKeys = {
|
||||
addAttachment: {
|
||||
keys: "Mod-Shift-A",
|
||||
description: "Add attachment",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertBlockquote: {
|
||||
keys: "Mod-Shift-B",
|
||||
description: "Insert blockquote",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleBold: {
|
||||
keys: "Mod-b",
|
||||
description: "Toggle bold",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleBulletList: {
|
||||
keys: "Mod-Shift-8",
|
||||
description: "Toggle bullet list",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleCheckList: {
|
||||
keys: "Mod-Shift-9",
|
||||
description: "Toggle check list",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
splitListItem: {
|
||||
keys: "Enter",
|
||||
description: "Split list item",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
liftListItem: {
|
||||
keys: "Shift-Tab",
|
||||
description: "Lift list item",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
sinkListItem: {
|
||||
keys: "Mod-Shift-Down",
|
||||
description: "Sink list item",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleCode: {
|
||||
keys: "Mod-e",
|
||||
description: "Toggle code",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleCodeBlock: {
|
||||
keys: "Mod-Shift-C",
|
||||
description: "Toggle code block",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertDate: {
|
||||
keys: "Alt-d",
|
||||
description: "Insert date",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertTime: {
|
||||
keys: "Alt-t",
|
||||
description: "Insert time",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertDateTime: {
|
||||
keys: "Mod-Alt-d",
|
||||
description: "Insert date and time",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertDateTimeWithTimezone: {
|
||||
keys: "Mod-Alt-z",
|
||||
description: "Insert date and time with timezone",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
increaseFontSize: {
|
||||
keys: "Ctrl-[",
|
||||
description: "Increase font size",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
decreaseFontSize: {
|
||||
keys: "Ctrl-]",
|
||||
description: "Decrease font size",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertParagraph: {
|
||||
keys: "Mod-Shift-0",
|
||||
description: "Insert paragraph",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertHeading1: {
|
||||
keys: "Mod-Alt-1",
|
||||
description: "Insert heading 1",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertHeading2: {
|
||||
keys: "Mod-Alt-2",
|
||||
description: "Insert heading 2",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertHeading3: {
|
||||
keys: "Mod-Alt-3",
|
||||
description: "Insert heading 3",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertHeading4: {
|
||||
keys: "Mod-Alt-4",
|
||||
description: "Insert heading 4",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertHeading5: {
|
||||
keys: "Mod-Alt-5",
|
||||
description: "Insert heading 5",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertHeading6: {
|
||||
keys: "Mod-Alt-6",
|
||||
description: "Insert heading 6",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
undo: {
|
||||
keys: "Mod-z",
|
||||
description: "Undo",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
redo: {
|
||||
keys: ["Mod-Shift-z", "Mod-y"],
|
||||
description: "Redo",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
addImage: {
|
||||
keys: "Mod-Shift-I",
|
||||
description: "Add image",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleItalic: {
|
||||
keys: "Mod-i",
|
||||
description: "Toggle italic",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
removeFormattingInSelection: {
|
||||
keys: "Mod-\\",
|
||||
description: "Remove formatting in selection",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertInternalLink: {
|
||||
keys: "Mod-Shift-L",
|
||||
description: "Insert internal link",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertLink: {
|
||||
keys: "Mod-Shift-K",
|
||||
description: "Insert link",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
insertMathBlock: {
|
||||
keys: "Mod-Shift-M",
|
||||
description: "Insert math block",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleOrderedList: {
|
||||
keys: "Mod-Shift-7",
|
||||
description: "Toggle ordered list",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleOutlineList: {
|
||||
keys: "Mod-Shift-O",
|
||||
description: "Toggle outline list",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleOutlineListExpand: {
|
||||
keys: "Mod-Space",
|
||||
description: "Toggle outline list expand",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
openSearch: {
|
||||
keys: "Mod-F",
|
||||
description: "Open search",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleStrike: {
|
||||
keys: "Mod-Shift-S",
|
||||
description: "Toggle strike",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleSubscript: {
|
||||
keys: "Mod-,",
|
||||
description: "Toggle subscript",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleSuperscript: {
|
||||
keys: "Mod-.",
|
||||
description: "Toggle superscript",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleTaskList: {
|
||||
keys: "Mod-Shift-T",
|
||||
description: "Toggle task list",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
textAlignCenter: {
|
||||
keys: "Mod-Shift-E",
|
||||
description: "Text align center",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
textAlignJustify: {
|
||||
keys: "Mod-Shift-J",
|
||||
description: "Text align justify",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
textAlignLeft: {
|
||||
keys: "Mod-Shift-L",
|
||||
description: "Text align left",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
textAlignRight: {
|
||||
keys: "Mod-Shift-R",
|
||||
description: "Text align right",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
underline: {
|
||||
keys: "Mod-u",
|
||||
description: "Underline",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
}
|
||||
} satisfies Record<string, TipTapKey>;
|
||||
|
||||
export const keybindings = {
|
||||
...hotkeys,
|
||||
...tiptapKeys
|
||||
};
|
||||
|
||||
export function getKeybinding(
|
||||
key: keyof typeof keybindings,
|
||||
isDesktop = false,
|
||||
isMac = false
|
||||
) {
|
||||
const keybinding = keybindings[key];
|
||||
if (keybinding.type === "hotkeys") {
|
||||
const hotkeys = keybinding.keys(isDesktop);
|
||||
return isMac ? hotkeys.map(macify) : hotkeys;
|
||||
}
|
||||
const tiptapKeys = Array.isArray(keybinding.keys)
|
||||
? keybinding.keys
|
||||
: [keybinding.keys];
|
||||
return isMac ? tiptapKeys.map(macify) : tiptapKeys;
|
||||
}
|
||||
|
||||
function normalizeKeys(
|
||||
keys: string[] | { web?: string[]; desktop?: string[] }
|
||||
): (isDesktop?: boolean) => string[] {
|
||||
return (isDesktop = false) => {
|
||||
let keyList: string[] = [];
|
||||
if (Array.isArray(keys)) {
|
||||
keyList = keys;
|
||||
} else {
|
||||
keyList = isDesktop ? keys.desktop ?? [] : keys.web ?? [];
|
||||
}
|
||||
return keyList;
|
||||
};
|
||||
}
|
||||
|
||||
export function macify(key: string) {
|
||||
return key
|
||||
.replace(/ctrl/gi, "Command")
|
||||
.replace(/alt/gi, "Option")
|
||||
.replace(/mod/gi, "Command");
|
||||
}
|
||||
|
||||
export function formatKey(key: string, isMac = false, separator = " ") {
|
||||
return key
|
||||
.replace(/\+|-/g, separator)
|
||||
.replace(/\bcommand\b/gi, isMac ? "⌘" : "Ctrl")
|
||||
.replace(/\bctrl\b/gi, isMac ? "⌘" : "Ctrl")
|
||||
.replace(/\bmod\b/gi, isMac ? "⌘" : "Ctrl")
|
||||
.replace(/\balt\b/gi, isMac ? "⌥" : "Alt")
|
||||
.replace(/\boption\b/gi, isMac ? "⌥" : "Alt")
|
||||
.replace(/\bshift\b/gi, "⇧")
|
||||
.replace(/\bright\b/gi, "→")
|
||||
.replace(/\bleft\b/gi, "←")
|
||||
.replace(/\benter\b/gi, "↵")
|
||||
.replace(/\b\w\b/gi, (e) => e.toUpperCase())
|
||||
.trim();
|
||||
}
|
||||
|
||||
export function getGroupedKeybindings(isDesktop: boolean, isMac: boolean) {
|
||||
const grouped: {
|
||||
shortcuts: { keys: string[]; description: string }[];
|
||||
category: Category;
|
||||
}[] = CATEGORIES.map((c) => ({
|
||||
category: c,
|
||||
shortcuts: []
|
||||
}));
|
||||
|
||||
const allKeybindings = { ...hotkeys, ...tiptapKeys };
|
||||
|
||||
for (const key in allKeybindings) {
|
||||
const binding = allKeybindings[key as keyof typeof allKeybindings];
|
||||
let keys =
|
||||
typeof binding.keys === "function"
|
||||
? binding.keys(isDesktop)
|
||||
: binding.keys;
|
||||
if (!keys || !keys.length) continue;
|
||||
|
||||
if (isMac) {
|
||||
keys = Array.isArray(keys) ? keys.map(macify) : macify(keys);
|
||||
}
|
||||
|
||||
const group = grouped.find((g) => g.category === binding.category);
|
||||
if (!group) throw new Error("Invalid group category: " + binding.category);
|
||||
|
||||
group.shortcuts.push({
|
||||
keys: Array.isArray(keys) ? keys : [keys],
|
||||
description: binding.description
|
||||
});
|
||||
}
|
||||
|
||||
return grouped;
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import { Attribute } from "@tiptap/core";
|
||||
import { createNodeView } from "../react/index.js";
|
||||
import { AttachmentComponent } from "./component.js";
|
||||
import { Attachment } from "./types.js";
|
||||
import { tiptapKeys } from "@notesnook/common";
|
||||
|
||||
export type AttachmentType = "image" | "file" | "camera";
|
||||
export interface AttachmentOptions {
|
||||
@@ -161,7 +162,7 @@ export const AttachmentNode = Node.create<AttachmentOptions>({
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Mod-Shift-A": () =>
|
||||
[tiptapKeys.addAttachment.keys]: () =>
|
||||
this.editor.storage.openAttachmentPicker?.("file") || true
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { tiptapKeys } from "@notesnook/common";
|
||||
import { getParentAttributes } from "../../utils/prosemirror.js";
|
||||
import { wrappingInputRule } from "@tiptap/core";
|
||||
import TiptapBlockquote, { inputRegex } from "@tiptap/extension-blockquote";
|
||||
@@ -47,5 +48,13 @@ export const Blockquote = TiptapBlockquote.extend({
|
||||
getAttributes: () => getParentAttributes(this.editor)
|
||||
})
|
||||
];
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
[tiptapKeys.insertBlockquote.keys]: () =>
|
||||
this.editor.commands.toggleBlockquote()
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { keybindings } from "@notesnook/common";
|
||||
import { KeyboardShortcutCommand, mergeAttributes, Node } from "@tiptap/core";
|
||||
import { Node as ProseMirrorNode } from "@tiptap/pm/model";
|
||||
|
||||
@@ -80,7 +81,8 @@ export const CheckListItem = Node.create<CheckListItemOptions>({
|
||||
[key: string]: KeyboardShortcutCommand;
|
||||
} = {
|
||||
Enter: () => this.editor.commands.splitListItem(this.name),
|
||||
"Shift-Tab": () => this.editor.commands.liftListItem(this.name)
|
||||
[keybindings.liftListItem.keys]: () =>
|
||||
this.editor.commands.liftListItem(this.name)
|
||||
};
|
||||
|
||||
if (!this.options.nested) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import { mergeAttributes, Node, wrappingInputRule } from "@tiptap/core";
|
||||
import { inputRegex } from "@tiptap/extension-task-item";
|
||||
import { getParentAttributes } from "../../utils/prosemirror.js";
|
||||
import { ListItem } from "../list-item/index.js";
|
||||
import { tiptapKeys } from "@notesnook/common";
|
||||
|
||||
export interface CheckListOptions {
|
||||
itemTypeName: string;
|
||||
@@ -123,7 +124,8 @@ export const CheckList = Node.create<CheckListOptions>({
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Mod-Shift-9": () => this.editor.commands.toggleCheckList()
|
||||
[tiptapKeys.toggleCheckList.keys]: () =>
|
||||
this.editor.commands.toggleCheckList()
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -36,6 +36,7 @@ import stripIndent from "strip-indent";
|
||||
import { nanoid } from "nanoid";
|
||||
import Languages from "./languages.json";
|
||||
import { CaretPosition, CodeLine } from "./utils.js";
|
||||
import { tiptapKeys } from "@notesnook/common";
|
||||
|
||||
interface Indent {
|
||||
type: "tab" | "space";
|
||||
@@ -292,7 +293,8 @@ export const CodeBlock = Node.create<CodeBlockOptions>({
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Mod-Shift-C": () => this.editor.commands.toggleCodeBlock(),
|
||||
[tiptapKeys.toggleCodeBlock.keys]: () =>
|
||||
this.editor.commands.toggleCodeBlock(),
|
||||
"Mod-a": ({ editor }) => {
|
||||
const { $anchor } = this.editor.state.selection;
|
||||
if ($anchor.parent.type.name !== this.name) {
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
InputRuleFinder,
|
||||
ExtendedRegExpMatchArray
|
||||
} from "@tiptap/core";
|
||||
import { formatDate } from "@notesnook/common";
|
||||
import { formatDate, tiptapKeys } from "@notesnook/common";
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
interface Commands<ReturnType> {
|
||||
@@ -67,10 +67,14 @@ export const DateTime = Extension.create<DateTimeOptions>({
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Alt-t": ({ editor }) => editor.commands.insertTime(),
|
||||
"Alt-d": ({ editor }) => editor.commands.insertDate(),
|
||||
"Mod-Alt-d": ({ editor }) => editor.commands.insertDateTime(),
|
||||
"Mod-Alt-z": ({ editor }) => editor.commands.insertDateTimeWithTimeZone()
|
||||
[tiptapKeys.insertTime.keys]: ({ editor }) =>
|
||||
editor.commands.insertTime(),
|
||||
[tiptapKeys.insertDate.keys]: ({ editor }) =>
|
||||
editor.commands.insertDate(),
|
||||
[tiptapKeys.insertDateTime.keys]: ({ editor }) =>
|
||||
editor.commands.insertDateTime(),
|
||||
[tiptapKeys.insertDateTimeWithTimezone.keys]: ({ editor }) =>
|
||||
editor.commands.insertDateTimeWithTimeZone()
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { tiptapKeys } from "@notesnook/common";
|
||||
import { useToolbarStore } from "../../toolbar/stores/toolbar-store.js";
|
||||
import { Editor, Extension } from "@tiptap/core";
|
||||
|
||||
@@ -86,7 +87,7 @@ export const FontSize = Extension.create<FontSizeOptions>({
|
||||
},
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"ctrl-]": ({ editor }) => {
|
||||
[tiptapKeys.decreaseFontSize.keys]: ({ editor }) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
@@ -94,7 +95,7 @@ export const FontSize = Extension.create<FontSizeOptions>({
|
||||
.run();
|
||||
return true;
|
||||
},
|
||||
"Ctrl-[": ({ editor }) => {
|
||||
[tiptapKeys.increaseFontSize.keys]: ({ editor }) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
|
||||
@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { tiptapKeys } from "@notesnook/common";
|
||||
import { textblockTypeInputRule } from "@tiptap/core";
|
||||
import { Heading as TiptapHeading } from "@tiptap/extension-heading";
|
||||
|
||||
@@ -49,7 +50,8 @@ export const Heading = TiptapHeading.extend({
|
||||
(items, level) => ({
|
||||
...items,
|
||||
...{
|
||||
[`Mod-Alt-${level}`]: () => this.editor.commands.setHeading({ level })
|
||||
[tiptapKeys[`insertHeading${level}`].keys]: () =>
|
||||
this.editor.commands.setHeading({ level })
|
||||
}
|
||||
}),
|
||||
{}
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
import { createNodeView } from "../react/index.js";
|
||||
import { TextDirections } from "../text-direction/index.js";
|
||||
import { ImageComponent } from "./component.js";
|
||||
import { tiptapKeys } from "@notesnook/common";
|
||||
|
||||
export interface ImageOptions {
|
||||
inline: boolean;
|
||||
@@ -199,7 +200,7 @@ export const ImageNode = Node.create<ImageOptions>({
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Mod-Shift-I": () =>
|
||||
[tiptapKeys.addImage.keys]: () =>
|
||||
this.editor.storage.openAttachmentPicker?.("image") || true
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import { isInTable } from "@tiptap/pm/tables";
|
||||
import { CodeBlock } from "../code-block/index.js";
|
||||
import { showLinkPopup } from "../../toolbar/popups/link-popup.js";
|
||||
import { isListActive } from "../../utils/list.js";
|
||||
import { tiptapKeys } from "@notesnook/common";
|
||||
|
||||
export const KeyMap = Extension.create({
|
||||
name: "key-map",
|
||||
@@ -42,7 +43,7 @@ export const KeyMap = Extension.create({
|
||||
if (isListActive(editor)) return false;
|
||||
return true;
|
||||
},
|
||||
"Mod-\\": ({ editor }) => {
|
||||
[tiptapKeys.removeFormattingInSelection.keys]: ({ editor }) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
@@ -52,7 +53,7 @@ export const KeyMap = Extension.create({
|
||||
.run();
|
||||
return true;
|
||||
},
|
||||
"Shift-Mod-L": ({ editor }) => {
|
||||
[tiptapKeys.insertInternalLink.keys]: ({ editor }) => {
|
||||
editor.storage.createInternalLink?.().then((link) => {
|
||||
if (!link) return;
|
||||
|
||||
@@ -67,7 +68,7 @@ export const KeyMap = Extension.create({
|
||||
});
|
||||
return true;
|
||||
},
|
||||
"Shift-Mod-k": ({ editor }) => {
|
||||
[tiptapKeys.insertLink.keys]: ({ editor }) => {
|
||||
showLinkPopup(editor);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { Node, mergeAttributes } from "@tiptap/core";
|
||||
import { insertMathNode } from "./plugin/index.js";
|
||||
import { NodeSelection } from "prosemirror-state";
|
||||
import { tiptapKeys } from "@notesnook/common";
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
interface Commands<ReturnType> {
|
||||
@@ -66,7 +67,8 @@ export const MathBlock = Node.create({
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Mod-Shift-M": () => this.editor.commands.insertMathBlock()
|
||||
[tiptapKeys.insertMathBlock.keys]: () =>
|
||||
this.editor.commands.insertMathBlock()
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
} from "@tiptap/core";
|
||||
import { findParentNodeOfTypeClosestToPos } from "../../utils/prosemirror.js";
|
||||
import { OutlineList } from "../outline-list/outline-list.js";
|
||||
import { keybindings, tiptapKeys } from "@notesnook/common";
|
||||
|
||||
export interface ListItemOptions {
|
||||
HTMLAttributes: Record<string, unknown>;
|
||||
@@ -75,7 +76,7 @@ export const OutlineListItem = Node.create<ListItemOptions>({
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Mod-Space": ({ editor }) => {
|
||||
[tiptapKeys.toggleOutlineListExpand.keys]: ({ editor }) => {
|
||||
const { selection } = editor.state;
|
||||
const { $from, empty } = selection;
|
||||
|
||||
@@ -104,7 +105,8 @@ export const OutlineListItem = Node.create<ListItemOptions>({
|
||||
return this.editor.commands.splitListItem(this.name);
|
||||
},
|
||||
Tab: () => this.editor.commands.sinkListItem(this.name),
|
||||
"Shift-Tab": () => this.editor.commands.liftListItem(this.name)
|
||||
[keybindings.liftListItem.keys]: () =>
|
||||
this.editor.commands.liftListItem(this.name)
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { tiptapKeys } from "@notesnook/common";
|
||||
import { getParentAttributes } from "../../utils/prosemirror.js";
|
||||
import { Node, mergeAttributes, wrappingInputRule } from "@tiptap/core";
|
||||
|
||||
@@ -100,7 +101,8 @@ export const OutlineList = Node.create<OutlineListOptions>({
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Mod-Shift-O": () => this.editor.commands.toggleOutlineList()
|
||||
[tiptapKeys.toggleOutlineList.keys]: () =>
|
||||
this.editor.commands.toggleOutlineList()
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { keybindings } from "@notesnook/common";
|
||||
import { Editor, mergeAttributes, Node } from "@tiptap/core";
|
||||
import { NodeType } from "prosemirror-model";
|
||||
|
||||
@@ -120,7 +121,8 @@ export const Paragraph = Node.create<ParagraphOptions>({
|
||||
createParagraph(editor, this.type, false, true),
|
||||
"Shift-Enter": ({ editor }) =>
|
||||
createParagraph(editor, this.type, false, true),
|
||||
"Mod-Alt-0": () => this.editor.commands.setParagraph()
|
||||
[keybindings.insertParagraph.keys]: () =>
|
||||
this.editor.commands.setParagraph()
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
TextSelection
|
||||
} from "prosemirror-state";
|
||||
import { SearchSettings } from "../../toolbar/stores/search-store.js";
|
||||
import { tiptapKeys } from "@notesnook/common";
|
||||
|
||||
type DispatchFn = (tr: Transaction) => void;
|
||||
declare module "@tiptap/core" {
|
||||
@@ -354,7 +355,8 @@ export const SearchReplace = Extension.create<SearchOptions, SearchStorage>({
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Mod-f": ({ editor }) => editor.commands.startSearch(),
|
||||
[tiptapKeys.openSearch.keys]: ({ editor }) =>
|
||||
editor.commands.startSearch(),
|
||||
Escape: ({ editor }) => editor.commands.endSearch()
|
||||
};
|
||||
},
|
||||
|
||||
@@ -40,6 +40,7 @@ import {
|
||||
import { Node as ProsemirrorNode } from "@tiptap/pm/model";
|
||||
import { TaskItemNode } from "../task-item/index.js";
|
||||
import { ListItem } from "../list-item/list-item.js";
|
||||
import { tiptapKeys } from "@notesnook/common";
|
||||
|
||||
type TaskListStats = { checked: number; total: number };
|
||||
export type TaskListAttributes = {
|
||||
@@ -368,7 +369,8 @@ export const TaskListNode = TaskList.extend({
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Mod-Shift-T": () => this.editor.commands.toggleTaskList()
|
||||
[tiptapKeys.toggleTaskList.keys]: () =>
|
||||
this.editor.commands.toggleTaskList()
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -30,6 +30,7 @@ import { showPopup } from "../../components/popup-presenter/index.js";
|
||||
import { ImageUploadPopup } from "../popups/image-upload.js";
|
||||
import { Button } from "../../components/button.js";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { keybindings } from "@notesnook/common";
|
||||
|
||||
export function InsertBlock(props: ToolProps) {
|
||||
const { editor } = props;
|
||||
@@ -114,7 +115,7 @@ const codeblock = (editor: Editor): MenuItem => ({
|
||||
icon: Icons.codeblock,
|
||||
isChecked: editor.isActive("codeBlock"),
|
||||
onClick: () => editor.chain().focus().toggleCodeBlock().run(),
|
||||
modifier: "Mod-Shift-C"
|
||||
modifier: keybindings.toggleCodeBlock.keys
|
||||
});
|
||||
|
||||
const blockquote = (editor: Editor): MenuItem => ({
|
||||
@@ -124,7 +125,7 @@ const blockquote = (editor: Editor): MenuItem => ({
|
||||
icon: Icons.blockquote,
|
||||
isChecked: editor.isActive("blockQuote"),
|
||||
onClick: () => editor.chain().focus().toggleBlockquote().run(),
|
||||
modifier: "Mod-Shift-B"
|
||||
modifier: keybindings.insertBlockquote.keys
|
||||
});
|
||||
|
||||
const mathblock = (editor: Editor): MenuItem => ({
|
||||
@@ -134,7 +135,7 @@ const mathblock = (editor: Editor): MenuItem => ({
|
||||
icon: Icons.mathBlock,
|
||||
isChecked: editor.isActive("mathBlock"),
|
||||
onClick: () => editor.chain().focus().insertMathBlock().run(),
|
||||
modifier: "Mod-Shift-M"
|
||||
modifier: keybindings.insertMathBlock.keys
|
||||
});
|
||||
|
||||
const callout = (editor: Editor): MenuItem => ({
|
||||
@@ -181,7 +182,7 @@ const image = (editor: Editor, isMobile: boolean): MenuItem => ({
|
||||
title: strings.uploadFromDisk(),
|
||||
icon: Icons.upload,
|
||||
onClick: () => editor.storage.openAttachmentPicker?.("image"),
|
||||
modifier: "Mod-Shift-I"
|
||||
modifier: keybindings.addImage.keys
|
||||
},
|
||||
{
|
||||
key: "camera",
|
||||
@@ -284,7 +285,7 @@ const attachment = (editor: Editor): MenuItem => ({
|
||||
icon: Icons.attachment,
|
||||
isChecked: editor.isActive("attachment"),
|
||||
onClick: () => editor.storage.openAttachmentPicker?.("file"),
|
||||
modifier: "Mod-Shift-A"
|
||||
modifier: keybindings.addAttachment.keys
|
||||
});
|
||||
|
||||
const tasklist = (editor: Editor): MenuItem => ({
|
||||
@@ -294,7 +295,7 @@ const tasklist = (editor: Editor): MenuItem => ({
|
||||
icon: Icons.checkbox,
|
||||
isChecked: editor.isActive("taskList"),
|
||||
onClick: () => editor.chain().focus().toggleTaskList().run(),
|
||||
modifier: "Mod-Shift-T"
|
||||
modifier: keybindings.toggleTaskList.keys
|
||||
});
|
||||
|
||||
const outlinelist = (editor: Editor): MenuItem => ({
|
||||
@@ -304,7 +305,7 @@ const outlinelist = (editor: Editor): MenuItem => ({
|
||||
icon: Icons.outlineList,
|
||||
isChecked: editor.isActive("outlineList"),
|
||||
onClick: () => editor.chain().focus().toggleOutlineList().run(),
|
||||
modifier: "Mod-Shift-O"
|
||||
modifier: keybindings.toggleOutlineList.keys
|
||||
});
|
||||
|
||||
const uploadImageFromURLMobile = (editor: Editor): MenuItem => ({
|
||||
|
||||
Reference in New Issue
Block a user