add hotkeys

This commit is contained in:
Fedor Katurov 2024-10-02 20:50:30 +07:00
parent 6d00bffbec
commit db911e51e4
14 changed files with 91 additions and 73 deletions

4
.gitignore vendored
View file

@ -22,4 +22,6 @@ dist-ssr
*.njsproj
*.sln
*.sw?
web-ext-artifacts
web-ext-artifacts
output
*.tgz

View file

@ -22,4 +22,4 @@ yarn dev
## TO-DO
- Use Remirror as editor
- Use HyperMD editor

View file

@ -1,12 +1,12 @@
{
"name": "markdown-home-tab",
"private": true,
"version": "0.0.4",
"version": "0.0.5",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"package": "yarn build && web-ext build -s ./dist",
"package": "yarn build && web-ext build -s ./dist -a ./output",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},

View file

@ -1,7 +1,7 @@
{
"name": "Markdown Home Tab",
"short_name": "Markdown New Tab",
"version": "0.0.4",
"version": "0.0.5",
"description": "Markdown right in your home tab! Paste links, pictures, lists and more. You can also customize colors to match your needs.",
"manifest_version": 2,
"permissions": ["storage"],

View file

@ -13,7 +13,7 @@ const EmptyViewer: FC<EmptyViewerProps> = ({ startEditing }) => {
const { t } = useTranslation();
return (
<div className={styles.empty} style={style}>
<div className={styles.empty} style={style} onDoubleClick={startEditing}>
<div className={styles.title}>{t(`Nothing's here yet`)}</div>
<div>
<Button

View file

@ -8,6 +8,8 @@
box-sizing: border-box;
opacity: 0;
transition: opacity 0.25s;
user-select: none;
cursor: pointer;
&:hover {
opacity: 0.5;

View file

@ -1,4 +1,4 @@
import { FC } from "react";
import { FC, useCallback, MouseEvent } from "react";
import ReactMarkdown from "react-markdown";
import { useContainerPaddings } from "~/modules/theme/hooks/useContainerPaddings";
import styles from "./styles.module.scss";
@ -7,20 +7,26 @@ import { useTranslation } from "react-i18next";
import remarkGfm from "remark-gfm";
import rehypeRaw from "rehype-raw";
interface ReactMarkdownViewerProps {
interface Props {
value: string;
startEditing: () => void;
}
const ReactMarkdownViewer: FC<ReactMarkdownViewerProps> = ({
value,
startEditing,
}) => {
const MarkdownViewer: FC<Props> = ({ value, startEditing }) => {
const { t } = useTranslation();
const style = useContainerPaddings();
const onDoubleClick = useCallback(
(event: MouseEvent) => {
event.preventDefault();
event.stopPropagation();
startEditing();
},
[startEditing]
);
return (
<div style={style} className={styles.editor}>
<div style={style} className={styles.editor} onDoubleClick={onDoubleClick}>
<div className={styles.edit}>
<Button
size="small"
@ -41,4 +47,4 @@ const ReactMarkdownViewer: FC<ReactMarkdownViewerProps> = ({
);
};
export { ReactMarkdownViewer };
export { MarkdownViewer };

View file

@ -1,37 +1,16 @@
import { FC, useCallback } from "react";
import {
EditorComponent,
FloatingToolbar,
FormattingButtonGroup,
HeadingLevelButtonGroup,
Remirror,
useRemirror,
EditorComponent,
HeadingLevelButtonGroup,
FormattingButtonGroup,
} from "@remirror/react";
import jsx from "refractor/lang/jsx.js";
import typescript from "refractor/lang/typescript.js";
import { Extension, ExtensionPriority, RemirrorEventListener } from "remirror";
import {
BlockquoteExtension,
UnderlineExtension,
BoldExtension,
BulletListExtension,
CodeBlockExtension,
CodeExtension,
HardBreakExtension,
HeadingExtension,
ItalicExtension,
LinkExtension,
ListItemExtension,
MarkdownExtension,
OrderedListExtension,
StrikeExtension,
TableExtension,
TrailingNodeExtension,
GapCursorExtension,
} from "remirror/extensions";
import styles from "./styles.module.scss";
import { Extension, RemirrorEventListener } from "remirror";
import { useContainerPaddings } from "~/modules/theme/hooks/useContainerPaddings";
import styles from "./styles.module.scss";
interface RemirrorEditorProps {
locked: boolean;
@ -50,7 +29,7 @@ const RemirrorEditor: FC<RemirrorEditorProps> = ({
exitMarksOnArrowPress: false,
},
content: value,
stringHandler: "markdown",
// stringHandler: "markdown",
});
const onStateChange = useCallback<RemirrorEventListener<Extension>>(
@ -90,26 +69,26 @@ const RemirrorEditor: FC<RemirrorEditorProps> = ({
};
const extensions = (): Extension[] => [
new LinkExtension({ autoLink: true }),
new BoldExtension(),
new UnderlineExtension(),
new StrikeExtension(),
new ItalicExtension(),
new HeadingExtension(),
new BlockquoteExtension(),
new BulletListExtension({ enableSpine: false }),
new OrderedListExtension(),
new ListItemExtension({
priority: ExtensionPriority.High,
// enableCollapsible: true,
}),
new CodeExtension(),
new CodeBlockExtension({ supportedLanguages: [jsx, typescript] }),
new TrailingNodeExtension(),
new TableExtension(),
new MarkdownExtension({ copyAsMarkdown: true }),
new GapCursorExtension(),
new HardBreakExtension(),
// new LinkExtension({ autoLink: true }),
// new BoldExtension(),
// new UnderlineExtension(),
// new StrikeExtension(),
// new ItalicExtension(),
// new HeadingExtension(),
// new BlockquoteExtension(),
// new BulletListExtension({ enableSpine: false }),
// new OrderedListExtension(),
// new ListItemExtension({
// priority: ExtensionPriority.High,
// // enableCollapsible: true,
// }),
// new CodeExtension(),
// new CodeBlockExtension({ supportedLanguages: [jsx, typescript] }),
// new TrailingNodeExtension(),
// new TableExtension(),
// new MarkdownExtension({ copyAsMarkdown: true }),
// new GapCursorExtension(),
// new HardBreakExtension(),
];
export { RemirrorEditor };

View file

@ -1,13 +1,15 @@
import { ChangeEvent, FC, useCallback, useMemo } from "react";
import { ChangeEvent, FC, useCallback, useMemo, KeyboardEvent } from "react";
import styles from "./styles.module.scss";
import { useTheme } from "~/modules/theme/context/ThemeContext";
interface ReactMarkdownEditorProps {
value: string;
onChange: (val: string) => void;
save: VoidFunction;
}
const ReactMarkdownEditor: FC<ReactMarkdownEditorProps> = ({
const SimpleTextareaEditor: FC<ReactMarkdownEditorProps> = ({
save,
value,
onChange,
}) => {
@ -27,8 +29,22 @@ const ReactMarkdownEditor: FC<ReactMarkdownEditorProps> = ({
[paddingHorizontal, paddingVertical]
);
const onKeyDown = useCallback(
(event: KeyboardEvent) => {
if (event.key === "Enter" && event.ctrlKey) {
save();
}
if (event.key === "Escape") {
save();
}
},
[save]
);
return (
<textarea
onKeyDown={onKeyDown}
onChange={changeHandler}
className={styles.textarea}
style={style}
@ -39,4 +55,4 @@ const ReactMarkdownEditor: FC<ReactMarkdownEditorProps> = ({
);
};
export { ReactMarkdownEditor };
export { SimpleTextareaEditor };

View file

@ -1,6 +1,6 @@
import { FC, Suspense, lazy } from "react";
import { ReactMarkdownEditor } from "../../components/ReactMarkdownEditor";
import { ReactMarkdownViewer } from "../../components/ReactMarkdownViewer";
import { SimpleTextareaEditor } from "../../components/SimpleTextareaEditor";
import { MarkdownViewer } from "../../components/MarkdownViewer";
import { usePersistedValue } from "./hooks/usePersistedValue";
import styles from "./styles.module.scss";
import { useSettings } from "~/modules/settings/context/SettingsContext";
@ -10,8 +10,8 @@ import { EditorWrapper } from "../../components/EditorWrapper";
interface MarkdownEditorContainerProps {
id: string;
locked: boolean;
startEditing: () => void;
remove: () => void;
startEditing: VoidCallback;
remove: VoidCallback;
}
const RichEditor = lazy(() =>
@ -40,7 +40,7 @@ export const MarkdownEditorContainer: FC<MarkdownEditorContainerProps> = ({
const viewer = empty ? (
<EmptyViewer startEditing={startEditing} />
) : (
<ReactMarkdownViewer value={value} startEditing={startEditing} />
<MarkdownViewer value={value} startEditing={startEditing} />
);
const editor = (
@ -48,13 +48,17 @@ export const MarkdownEditorContainer: FC<MarkdownEditorContainerProps> = ({
{richEditorEnabled ? (
<RichEditor value={value} onChange={setValue} locked={locked} />
) : (
<ReactMarkdownEditor value={value} onChange={setValue} />
<SimpleTextareaEditor
value={value}
onChange={setValue}
save={startEditing}
/>
)}
</EditorWrapper>
);
return (
<div className={styles.editor}>
<div className={styles.editor} id={id}>
{hydrated && <Suspense>{locked ? viewer : editor}</Suspense>}
</div>
);

View file

@ -44,6 +44,15 @@ const DefaultLayout = ({
...panelProps.params,
locked: !locked,
});
if (panelProps.params.locked) {
setTimeout(() => {
document
.getElementById(panelProps.api.id)
?.querySelector("textarea")
?.focus();
}, 0);
}
}, [locked, panelProps.api, panelProps.params]);
useEffect(() => {

View file

@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next";
import { RowGroup } from "~/components/containers/RowGroup";
import { SettingsRow } from "~/components/containers/SettingsRow";
import {
ColorSettings,
ColorSettings as ColorSettingsValue,
SettingsValue,
useSettings,
} from "~/modules/settings/context/SettingsContext";
@ -37,7 +37,7 @@ const ColorSettings: FC = () => {
);
const setThemeColors = useCallback(
(val: ColorSettings) => {
(val: ColorSettingsValue) => {
update(fillThemeHeadings(val));
},
[update]