mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-25 19:15:18 +02:00
chore: ran linting
This commit is contained in:
parent
e46b24a2b1
commit
a482cc95de
67 changed files with 4971 additions and 5539 deletions
|
|
@ -1,25 +1,24 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { createContext, useContext } from 'react';
|
||||
import { createContext, useContext } from "react";
|
||||
|
||||
interface EditorSaveContextValue {
|
||||
/** Callback to save the current editor content */
|
||||
onSave?: () => void;
|
||||
/** Whether there are unsaved changes */
|
||||
hasUnsavedChanges: boolean;
|
||||
/** Whether a save operation is in progress */
|
||||
isSaving: boolean;
|
||||
/** Whether the user can toggle between editing and viewing modes */
|
||||
canToggleMode: boolean;
|
||||
/** Callback to save the current editor content */
|
||||
onSave?: () => void;
|
||||
/** Whether there are unsaved changes */
|
||||
hasUnsavedChanges: boolean;
|
||||
/** Whether a save operation is in progress */
|
||||
isSaving: boolean;
|
||||
/** Whether the user can toggle between editing and viewing modes */
|
||||
canToggleMode: boolean;
|
||||
}
|
||||
|
||||
export const EditorSaveContext = createContext<EditorSaveContextValue>({
|
||||
hasUnsavedChanges: false,
|
||||
isSaving: false,
|
||||
canToggleMode: false,
|
||||
hasUnsavedChanges: false,
|
||||
isSaving: false,
|
||||
canToggleMode: false,
|
||||
});
|
||||
|
||||
export function useEditorSave() {
|
||||
return useContext(EditorSaveContext);
|
||||
return useContext(EditorSaveContext);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,152 +1,150 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { MarkdownPlugin, remarkMdx } from '@platejs/markdown';
|
||||
import { Plate, usePlateEditor } from 'platejs/react';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkMath from 'remark-math';
|
||||
import { useEffect, useRef } from "react";
|
||||
import { MarkdownPlugin, remarkMdx } from "@platejs/markdown";
|
||||
import { Plate, usePlateEditor } from "platejs/react";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
|
||||
import { AutoformatKit } from '@/components/editor/plugins/autoformat-kit';
|
||||
import { BasicNodesKit } from '@/components/editor/plugins/basic-nodes-kit';
|
||||
import { CalloutKit } from '@/components/editor/plugins/callout-kit';
|
||||
import { CodeBlockKit } from '@/components/editor/plugins/code-block-kit';
|
||||
import { DndKit } from '@/components/editor/plugins/dnd-kit';
|
||||
import { FixedToolbarKit } from '@/components/editor/plugins/fixed-toolbar-kit';
|
||||
import { FloatingToolbarKit } from '@/components/editor/plugins/floating-toolbar-kit';
|
||||
import { LinkKit } from '@/components/editor/plugins/link-kit';
|
||||
import { ListKit } from '@/components/editor/plugins/list-kit';
|
||||
import { MathKit } from '@/components/editor/plugins/math-kit';
|
||||
import { SelectionKit } from '@/components/editor/plugins/selection-kit';
|
||||
import { SlashCommandKit } from '@/components/editor/plugins/slash-command-kit';
|
||||
import { TableKit } from '@/components/editor/plugins/table-kit';
|
||||
import { ToggleKit } from '@/components/editor/plugins/toggle-kit';
|
||||
import { Editor, EditorContainer } from '@/components/ui/editor';
|
||||
import { escapeMdxExpressions } from '@/components/editor/utils/escape-mdx';
|
||||
import { EditorSaveContext } from '@/components/editor/editor-save-context';
|
||||
import { AutoformatKit } from "@/components/editor/plugins/autoformat-kit";
|
||||
import { BasicNodesKit } from "@/components/editor/plugins/basic-nodes-kit";
|
||||
import { CalloutKit } from "@/components/editor/plugins/callout-kit";
|
||||
import { CodeBlockKit } from "@/components/editor/plugins/code-block-kit";
|
||||
import { DndKit } from "@/components/editor/plugins/dnd-kit";
|
||||
import { FixedToolbarKit } from "@/components/editor/plugins/fixed-toolbar-kit";
|
||||
import { FloatingToolbarKit } from "@/components/editor/plugins/floating-toolbar-kit";
|
||||
import { LinkKit } from "@/components/editor/plugins/link-kit";
|
||||
import { ListKit } from "@/components/editor/plugins/list-kit";
|
||||
import { MathKit } from "@/components/editor/plugins/math-kit";
|
||||
import { SelectionKit } from "@/components/editor/plugins/selection-kit";
|
||||
import { SlashCommandKit } from "@/components/editor/plugins/slash-command-kit";
|
||||
import { TableKit } from "@/components/editor/plugins/table-kit";
|
||||
import { ToggleKit } from "@/components/editor/plugins/toggle-kit";
|
||||
import { Editor, EditorContainer } from "@/components/ui/editor";
|
||||
import { escapeMdxExpressions } from "@/components/editor/utils/escape-mdx";
|
||||
import { EditorSaveContext } from "@/components/editor/editor-save-context";
|
||||
|
||||
interface PlateEditorProps {
|
||||
/** Markdown string to load as initial content */
|
||||
markdown?: string;
|
||||
/** Called when the editor content changes, with serialized markdown */
|
||||
onMarkdownChange?: (markdown: string) => void;
|
||||
/**
|
||||
* Force permanent read-only mode (e.g. public/shared view).
|
||||
* When true, the editor cannot be toggled to editing mode.
|
||||
* When false (default), the editor starts in viewing mode but
|
||||
* the user can switch to editing via the mode toolbar button.
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
/** Placeholder text */
|
||||
placeholder?: string;
|
||||
/** Editor container variant */
|
||||
variant?: 'default' | 'demo' | 'comment' | 'select';
|
||||
/** Editor text variant */
|
||||
editorVariant?: 'default' | 'demo' | 'fullWidth' | 'none';
|
||||
/** Additional className for the container */
|
||||
className?: string;
|
||||
/** Save callback. When provided, a save button appears in the toolbar on unsaved changes. */
|
||||
onSave?: () => void;
|
||||
/** Whether there are unsaved changes */
|
||||
hasUnsavedChanges?: boolean;
|
||||
/** Whether a save is in progress */
|
||||
isSaving?: boolean;
|
||||
/** Start the editor in editing mode instead of viewing mode. Ignored when readOnly is true. */
|
||||
defaultEditing?: boolean;
|
||||
/** Markdown string to load as initial content */
|
||||
markdown?: string;
|
||||
/** Called when the editor content changes, with serialized markdown */
|
||||
onMarkdownChange?: (markdown: string) => void;
|
||||
/**
|
||||
* Force permanent read-only mode (e.g. public/shared view).
|
||||
* When true, the editor cannot be toggled to editing mode.
|
||||
* When false (default), the editor starts in viewing mode but
|
||||
* the user can switch to editing via the mode toolbar button.
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
/** Placeholder text */
|
||||
placeholder?: string;
|
||||
/** Editor container variant */
|
||||
variant?: "default" | "demo" | "comment" | "select";
|
||||
/** Editor text variant */
|
||||
editorVariant?: "default" | "demo" | "fullWidth" | "none";
|
||||
/** Additional className for the container */
|
||||
className?: string;
|
||||
/** Save callback. When provided, a save button appears in the toolbar on unsaved changes. */
|
||||
onSave?: () => void;
|
||||
/** Whether there are unsaved changes */
|
||||
hasUnsavedChanges?: boolean;
|
||||
/** Whether a save is in progress */
|
||||
isSaving?: boolean;
|
||||
/** Start the editor in editing mode instead of viewing mode. Ignored when readOnly is true. */
|
||||
defaultEditing?: boolean;
|
||||
}
|
||||
|
||||
export function PlateEditor({
|
||||
markdown,
|
||||
onMarkdownChange,
|
||||
readOnly = false,
|
||||
placeholder = 'Type...',
|
||||
variant = 'default',
|
||||
editorVariant = 'default',
|
||||
className,
|
||||
onSave,
|
||||
hasUnsavedChanges = false,
|
||||
isSaving = false,
|
||||
defaultEditing = false,
|
||||
markdown,
|
||||
onMarkdownChange,
|
||||
readOnly = false,
|
||||
placeholder = "Type...",
|
||||
variant = "default",
|
||||
editorVariant = "default",
|
||||
className,
|
||||
onSave,
|
||||
hasUnsavedChanges = false,
|
||||
isSaving = false,
|
||||
defaultEditing = false,
|
||||
}: PlateEditorProps) {
|
||||
const lastMarkdownRef = useRef(markdown);
|
||||
const lastMarkdownRef = useRef(markdown);
|
||||
|
||||
// When readOnly is forced, always start in readOnly.
|
||||
// Otherwise, respect defaultEditing to decide initial mode.
|
||||
// The user can still toggle between editing/viewing via ModeToolbarButton.
|
||||
const editor = usePlateEditor({
|
||||
readOnly: readOnly || !defaultEditing,
|
||||
plugins: [
|
||||
...BasicNodesKit,
|
||||
...TableKit,
|
||||
...ListKit,
|
||||
...CodeBlockKit,
|
||||
...LinkKit,
|
||||
...CalloutKit,
|
||||
...ToggleKit,
|
||||
...MathKit,
|
||||
...SelectionKit,
|
||||
...SlashCommandKit,
|
||||
...FixedToolbarKit,
|
||||
...FloatingToolbarKit,
|
||||
...AutoformatKit,
|
||||
...DndKit,
|
||||
MarkdownPlugin.configure({
|
||||
options: {
|
||||
remarkPlugins: [remarkGfm, remarkMath, remarkMdx],
|
||||
},
|
||||
}),
|
||||
],
|
||||
// Use markdown deserialization for initial value if provided
|
||||
value: markdown
|
||||
? (editor) =>
|
||||
editor
|
||||
.getApi(MarkdownPlugin)
|
||||
.markdown.deserialize(escapeMdxExpressions(markdown))
|
||||
: undefined,
|
||||
});
|
||||
// When readOnly is forced, always start in readOnly.
|
||||
// Otherwise, respect defaultEditing to decide initial mode.
|
||||
// The user can still toggle between editing/viewing via ModeToolbarButton.
|
||||
const editor = usePlateEditor({
|
||||
readOnly: readOnly || !defaultEditing,
|
||||
plugins: [
|
||||
...BasicNodesKit,
|
||||
...TableKit,
|
||||
...ListKit,
|
||||
...CodeBlockKit,
|
||||
...LinkKit,
|
||||
...CalloutKit,
|
||||
...ToggleKit,
|
||||
...MathKit,
|
||||
...SelectionKit,
|
||||
...SlashCommandKit,
|
||||
...FixedToolbarKit,
|
||||
...FloatingToolbarKit,
|
||||
...AutoformatKit,
|
||||
...DndKit,
|
||||
MarkdownPlugin.configure({
|
||||
options: {
|
||||
remarkPlugins: [remarkGfm, remarkMath, remarkMdx],
|
||||
},
|
||||
}),
|
||||
],
|
||||
// Use markdown deserialization for initial value if provided
|
||||
value: markdown
|
||||
? (editor) =>
|
||||
editor.getApi(MarkdownPlugin).markdown.deserialize(escapeMdxExpressions(markdown))
|
||||
: undefined,
|
||||
});
|
||||
|
||||
// Update editor content when markdown prop changes externally
|
||||
// (e.g., version switching in report panel)
|
||||
useEffect(() => {
|
||||
if (markdown !== undefined && markdown !== lastMarkdownRef.current) {
|
||||
lastMarkdownRef.current = markdown;
|
||||
const newValue = editor
|
||||
.getApi(MarkdownPlugin)
|
||||
.markdown.deserialize(escapeMdxExpressions(markdown));
|
||||
editor.tf.reset();
|
||||
editor.tf.setValue(newValue);
|
||||
}
|
||||
}, [markdown, editor]);
|
||||
// Update editor content when markdown prop changes externally
|
||||
// (e.g., version switching in report panel)
|
||||
useEffect(() => {
|
||||
if (markdown !== undefined && markdown !== lastMarkdownRef.current) {
|
||||
lastMarkdownRef.current = markdown;
|
||||
const newValue = editor
|
||||
.getApi(MarkdownPlugin)
|
||||
.markdown.deserialize(escapeMdxExpressions(markdown));
|
||||
editor.tf.reset();
|
||||
editor.tf.setValue(newValue);
|
||||
}
|
||||
}, [markdown, editor]);
|
||||
|
||||
// When not forced read-only, the user can toggle between editing/viewing.
|
||||
const canToggleMode = !readOnly;
|
||||
// When not forced read-only, the user can toggle between editing/viewing.
|
||||
const canToggleMode = !readOnly;
|
||||
|
||||
return (
|
||||
<EditorSaveContext.Provider
|
||||
value={{
|
||||
onSave,
|
||||
hasUnsavedChanges,
|
||||
isSaving,
|
||||
canToggleMode,
|
||||
}}
|
||||
>
|
||||
<Plate
|
||||
editor={editor}
|
||||
// Only pass readOnly as a controlled prop when forced (permanently read-only).
|
||||
// For non-forced mode, the Plate store manages readOnly internally
|
||||
// (initialized to true via usePlateEditor, toggled via ModeToolbarButton).
|
||||
{...(readOnly ? { readOnly: true } : {})}
|
||||
onChange={({ value }) => {
|
||||
if (onMarkdownChange) {
|
||||
const md = editor.getApi(MarkdownPlugin).markdown.serialize({ value });
|
||||
lastMarkdownRef.current = md;
|
||||
onMarkdownChange(md);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<EditorContainer variant={variant} className={className}>
|
||||
<Editor variant={editorVariant} placeholder={placeholder} />
|
||||
</EditorContainer>
|
||||
</Plate>
|
||||
</EditorSaveContext.Provider>
|
||||
);
|
||||
return (
|
||||
<EditorSaveContext.Provider
|
||||
value={{
|
||||
onSave,
|
||||
hasUnsavedChanges,
|
||||
isSaving,
|
||||
canToggleMode,
|
||||
}}
|
||||
>
|
||||
<Plate
|
||||
editor={editor}
|
||||
// Only pass readOnly as a controlled prop when forced (permanently read-only).
|
||||
// For non-forced mode, the Plate store manages readOnly internally
|
||||
// (initialized to true via usePlateEditor, toggled via ModeToolbarButton).
|
||||
{...(readOnly ? { readOnly: true } : {})}
|
||||
onChange={({ value }) => {
|
||||
if (onMarkdownChange) {
|
||||
const md = editor.getApi(MarkdownPlugin).markdown.serialize({ value });
|
||||
lastMarkdownRef.current = md;
|
||||
onMarkdownChange(md);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<EditorContainer variant={variant} className={className}>
|
||||
<Editor variant={editorVariant} placeholder={placeholder} />
|
||||
</EditorContainer>
|
||||
</Plate>
|
||||
</EditorSaveContext.Provider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,238 +1,237 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import type { AutoformatRule } from '@platejs/autoformat';
|
||||
import type { AutoformatRule } from "@platejs/autoformat";
|
||||
|
||||
import {
|
||||
autoformatArrow,
|
||||
autoformatLegal,
|
||||
autoformatLegalHtml,
|
||||
autoformatMath,
|
||||
AutoformatPlugin,
|
||||
autoformatPunctuation,
|
||||
autoformatSmartQuotes,
|
||||
} from '@platejs/autoformat';
|
||||
import { insertEmptyCodeBlock } from '@platejs/code-block';
|
||||
import { toggleList } from '@platejs/list';
|
||||
import { openNextToggles } from '@platejs/toggle/react';
|
||||
import { KEYS } from 'platejs';
|
||||
autoformatArrow,
|
||||
autoformatLegal,
|
||||
autoformatLegalHtml,
|
||||
autoformatMath,
|
||||
AutoformatPlugin,
|
||||
autoformatPunctuation,
|
||||
autoformatSmartQuotes,
|
||||
} from "@platejs/autoformat";
|
||||
import { insertEmptyCodeBlock } from "@platejs/code-block";
|
||||
import { toggleList } from "@platejs/list";
|
||||
import { openNextToggles } from "@platejs/toggle/react";
|
||||
import { KEYS } from "platejs";
|
||||
|
||||
const autoformatMarks: AutoformatRule[] = [
|
||||
{
|
||||
match: '***',
|
||||
mode: 'mark',
|
||||
type: [KEYS.bold, KEYS.italic],
|
||||
},
|
||||
{
|
||||
match: '__*',
|
||||
mode: 'mark',
|
||||
type: [KEYS.underline, KEYS.italic],
|
||||
},
|
||||
{
|
||||
match: '__**',
|
||||
mode: 'mark',
|
||||
type: [KEYS.underline, KEYS.bold],
|
||||
},
|
||||
{
|
||||
match: '___***',
|
||||
mode: 'mark',
|
||||
type: [KEYS.underline, KEYS.bold, KEYS.italic],
|
||||
},
|
||||
{
|
||||
match: '**',
|
||||
mode: 'mark',
|
||||
type: KEYS.bold,
|
||||
},
|
||||
{
|
||||
match: '__',
|
||||
mode: 'mark',
|
||||
type: KEYS.underline,
|
||||
},
|
||||
{
|
||||
match: '*',
|
||||
mode: 'mark',
|
||||
type: KEYS.italic,
|
||||
},
|
||||
{
|
||||
match: '_',
|
||||
mode: 'mark',
|
||||
type: KEYS.italic,
|
||||
},
|
||||
{
|
||||
match: '~~',
|
||||
mode: 'mark',
|
||||
type: KEYS.strikethrough,
|
||||
},
|
||||
{
|
||||
match: '^',
|
||||
mode: 'mark',
|
||||
type: KEYS.sup,
|
||||
},
|
||||
{
|
||||
match: '~',
|
||||
mode: 'mark',
|
||||
type: KEYS.sub,
|
||||
},
|
||||
{
|
||||
match: '==',
|
||||
mode: 'mark',
|
||||
type: KEYS.highlight,
|
||||
},
|
||||
{
|
||||
match: '≡',
|
||||
mode: 'mark',
|
||||
type: KEYS.highlight,
|
||||
},
|
||||
{
|
||||
match: '`',
|
||||
mode: 'mark',
|
||||
type: KEYS.code,
|
||||
},
|
||||
{
|
||||
match: "***",
|
||||
mode: "mark",
|
||||
type: [KEYS.bold, KEYS.italic],
|
||||
},
|
||||
{
|
||||
match: "__*",
|
||||
mode: "mark",
|
||||
type: [KEYS.underline, KEYS.italic],
|
||||
},
|
||||
{
|
||||
match: "__**",
|
||||
mode: "mark",
|
||||
type: [KEYS.underline, KEYS.bold],
|
||||
},
|
||||
{
|
||||
match: "___***",
|
||||
mode: "mark",
|
||||
type: [KEYS.underline, KEYS.bold, KEYS.italic],
|
||||
},
|
||||
{
|
||||
match: "**",
|
||||
mode: "mark",
|
||||
type: KEYS.bold,
|
||||
},
|
||||
{
|
||||
match: "__",
|
||||
mode: "mark",
|
||||
type: KEYS.underline,
|
||||
},
|
||||
{
|
||||
match: "*",
|
||||
mode: "mark",
|
||||
type: KEYS.italic,
|
||||
},
|
||||
{
|
||||
match: "_",
|
||||
mode: "mark",
|
||||
type: KEYS.italic,
|
||||
},
|
||||
{
|
||||
match: "~~",
|
||||
mode: "mark",
|
||||
type: KEYS.strikethrough,
|
||||
},
|
||||
{
|
||||
match: "^",
|
||||
mode: "mark",
|
||||
type: KEYS.sup,
|
||||
},
|
||||
{
|
||||
match: "~",
|
||||
mode: "mark",
|
||||
type: KEYS.sub,
|
||||
},
|
||||
{
|
||||
match: "==",
|
||||
mode: "mark",
|
||||
type: KEYS.highlight,
|
||||
},
|
||||
{
|
||||
match: "≡",
|
||||
mode: "mark",
|
||||
type: KEYS.highlight,
|
||||
},
|
||||
{
|
||||
match: "`",
|
||||
mode: "mark",
|
||||
type: KEYS.code,
|
||||
},
|
||||
];
|
||||
|
||||
const autoformatBlocks: AutoformatRule[] = [
|
||||
{
|
||||
match: '# ',
|
||||
mode: 'block',
|
||||
type: KEYS.h1,
|
||||
},
|
||||
{
|
||||
match: '## ',
|
||||
mode: 'block',
|
||||
type: KEYS.h2,
|
||||
},
|
||||
{
|
||||
match: '### ',
|
||||
mode: 'block',
|
||||
type: KEYS.h3,
|
||||
},
|
||||
{
|
||||
match: '#### ',
|
||||
mode: 'block',
|
||||
type: KEYS.h4,
|
||||
},
|
||||
{
|
||||
match: '##### ',
|
||||
mode: 'block',
|
||||
type: KEYS.h5,
|
||||
},
|
||||
{
|
||||
match: '###### ',
|
||||
mode: 'block',
|
||||
type: KEYS.h6,
|
||||
},
|
||||
{
|
||||
match: '> ',
|
||||
mode: 'block',
|
||||
type: KEYS.blockquote,
|
||||
},
|
||||
{
|
||||
match: '```',
|
||||
mode: 'block',
|
||||
type: KEYS.codeBlock,
|
||||
format: (editor) => {
|
||||
insertEmptyCodeBlock(editor, {
|
||||
defaultType: KEYS.p,
|
||||
insertNodesOptions: { select: true },
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
match: '+ ',
|
||||
mode: 'block',
|
||||
preFormat: openNextToggles,
|
||||
type: KEYS.toggle,
|
||||
},
|
||||
{
|
||||
match: ['---', '—-', '___ '],
|
||||
mode: 'block',
|
||||
type: KEYS.hr,
|
||||
format: (editor) => {
|
||||
editor.tf.setNodes({ type: KEYS.hr });
|
||||
editor.tf.insertNodes({
|
||||
children: [{ text: '' }],
|
||||
type: KEYS.p,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
match: "# ",
|
||||
mode: "block",
|
||||
type: KEYS.h1,
|
||||
},
|
||||
{
|
||||
match: "## ",
|
||||
mode: "block",
|
||||
type: KEYS.h2,
|
||||
},
|
||||
{
|
||||
match: "### ",
|
||||
mode: "block",
|
||||
type: KEYS.h3,
|
||||
},
|
||||
{
|
||||
match: "#### ",
|
||||
mode: "block",
|
||||
type: KEYS.h4,
|
||||
},
|
||||
{
|
||||
match: "##### ",
|
||||
mode: "block",
|
||||
type: KEYS.h5,
|
||||
},
|
||||
{
|
||||
match: "###### ",
|
||||
mode: "block",
|
||||
type: KEYS.h6,
|
||||
},
|
||||
{
|
||||
match: "> ",
|
||||
mode: "block",
|
||||
type: KEYS.blockquote,
|
||||
},
|
||||
{
|
||||
match: "```",
|
||||
mode: "block",
|
||||
type: KEYS.codeBlock,
|
||||
format: (editor) => {
|
||||
insertEmptyCodeBlock(editor, {
|
||||
defaultType: KEYS.p,
|
||||
insertNodesOptions: { select: true },
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
match: "+ ",
|
||||
mode: "block",
|
||||
preFormat: openNextToggles,
|
||||
type: KEYS.toggle,
|
||||
},
|
||||
{
|
||||
match: ["---", "—-", "___ "],
|
||||
mode: "block",
|
||||
type: KEYS.hr,
|
||||
format: (editor) => {
|
||||
editor.tf.setNodes({ type: KEYS.hr });
|
||||
editor.tf.insertNodes({
|
||||
children: [{ text: "" }],
|
||||
type: KEYS.p,
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const autoformatLists: AutoformatRule[] = [
|
||||
{
|
||||
match: ['* ', '- '],
|
||||
mode: 'block',
|
||||
type: 'list',
|
||||
format: (editor) => {
|
||||
toggleList(editor, {
|
||||
listStyleType: KEYS.ul,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
match: [String.raw`^\d+\.$ `, String.raw`^\d+\)$ `],
|
||||
matchByRegex: true,
|
||||
mode: 'block',
|
||||
type: 'list',
|
||||
format: (editor, { matchString }) => {
|
||||
toggleList(editor, {
|
||||
listRestartPolite: Number(matchString) || 1,
|
||||
listStyleType: KEYS.ol,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
match: ['[] '],
|
||||
mode: 'block',
|
||||
type: 'list',
|
||||
format: (editor) => {
|
||||
toggleList(editor, {
|
||||
listStyleType: KEYS.listTodo,
|
||||
});
|
||||
editor.tf.setNodes({
|
||||
checked: false,
|
||||
listStyleType: KEYS.listTodo,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
match: ['[x] '],
|
||||
mode: 'block',
|
||||
type: 'list',
|
||||
format: (editor) => {
|
||||
toggleList(editor, {
|
||||
listStyleType: KEYS.listTodo,
|
||||
});
|
||||
editor.tf.setNodes({
|
||||
checked: true,
|
||||
listStyleType: KEYS.listTodo,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
match: ["* ", "- "],
|
||||
mode: "block",
|
||||
type: "list",
|
||||
format: (editor) => {
|
||||
toggleList(editor, {
|
||||
listStyleType: KEYS.ul,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
match: [String.raw`^\d+\.$ `, String.raw`^\d+\)$ `],
|
||||
matchByRegex: true,
|
||||
mode: "block",
|
||||
type: "list",
|
||||
format: (editor, { matchString }) => {
|
||||
toggleList(editor, {
|
||||
listRestartPolite: Number(matchString) || 1,
|
||||
listStyleType: KEYS.ol,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
match: ["[] "],
|
||||
mode: "block",
|
||||
type: "list",
|
||||
format: (editor) => {
|
||||
toggleList(editor, {
|
||||
listStyleType: KEYS.listTodo,
|
||||
});
|
||||
editor.tf.setNodes({
|
||||
checked: false,
|
||||
listStyleType: KEYS.listTodo,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
match: ["[x] "],
|
||||
mode: "block",
|
||||
type: "list",
|
||||
format: (editor) => {
|
||||
toggleList(editor, {
|
||||
listStyleType: KEYS.listTodo,
|
||||
});
|
||||
editor.tf.setNodes({
|
||||
checked: true,
|
||||
listStyleType: KEYS.listTodo,
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const AutoformatKit = [
|
||||
AutoformatPlugin.configure({
|
||||
options: {
|
||||
enableUndoOnDelete: true,
|
||||
rules: [
|
||||
...autoformatBlocks,
|
||||
...autoformatMarks,
|
||||
...autoformatSmartQuotes,
|
||||
...autoformatPunctuation,
|
||||
...autoformatLegal,
|
||||
...autoformatLegalHtml,
|
||||
...autoformatArrow,
|
||||
...autoformatMath,
|
||||
...autoformatLists,
|
||||
].map(
|
||||
(rule): AutoformatRule => ({
|
||||
...rule,
|
||||
query: (editor) =>
|
||||
!editor.api.some({
|
||||
match: { type: editor.getType(KEYS.codeBlock) },
|
||||
}),
|
||||
})
|
||||
),
|
||||
},
|
||||
}),
|
||||
AutoformatPlugin.configure({
|
||||
options: {
|
||||
enableUndoOnDelete: true,
|
||||
rules: [
|
||||
...autoformatBlocks,
|
||||
...autoformatMarks,
|
||||
...autoformatSmartQuotes,
|
||||
...autoformatPunctuation,
|
||||
...autoformatLegal,
|
||||
...autoformatLegalHtml,
|
||||
...autoformatArrow,
|
||||
...autoformatMath,
|
||||
...autoformatLists,
|
||||
].map(
|
||||
(rule): AutoformatRule => ({
|
||||
...rule,
|
||||
query: (editor) =>
|
||||
!editor.api.some({
|
||||
match: { type: editor.getType(KEYS.codeBlock) },
|
||||
}),
|
||||
})
|
||||
),
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,86 +1,86 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import {
|
||||
BlockquotePlugin,
|
||||
H1Plugin,
|
||||
H2Plugin,
|
||||
H3Plugin,
|
||||
H4Plugin,
|
||||
H5Plugin,
|
||||
H6Plugin,
|
||||
HorizontalRulePlugin,
|
||||
} from '@platejs/basic-nodes/react';
|
||||
import { ParagraphPlugin } from 'platejs/react';
|
||||
BlockquotePlugin,
|
||||
H1Plugin,
|
||||
H2Plugin,
|
||||
H3Plugin,
|
||||
H4Plugin,
|
||||
H5Plugin,
|
||||
H6Plugin,
|
||||
HorizontalRulePlugin,
|
||||
} from "@platejs/basic-nodes/react";
|
||||
import { ParagraphPlugin } from "platejs/react";
|
||||
|
||||
import { BlockquoteElement } from '@/components/ui/blockquote-node';
|
||||
import { BlockquoteElement } from "@/components/ui/blockquote-node";
|
||||
import {
|
||||
H1Element,
|
||||
H2Element,
|
||||
H3Element,
|
||||
H4Element,
|
||||
H5Element,
|
||||
H6Element,
|
||||
} from '@/components/ui/heading-node';
|
||||
import { HrElement } from '@/components/ui/hr-node';
|
||||
import { ParagraphElement } from '@/components/ui/paragraph-node';
|
||||
H1Element,
|
||||
H2Element,
|
||||
H3Element,
|
||||
H4Element,
|
||||
H5Element,
|
||||
H6Element,
|
||||
} from "@/components/ui/heading-node";
|
||||
import { HrElement } from "@/components/ui/hr-node";
|
||||
import { ParagraphElement } from "@/components/ui/paragraph-node";
|
||||
|
||||
export const BasicBlocksKit = [
|
||||
ParagraphPlugin.withComponent(ParagraphElement),
|
||||
H1Plugin.configure({
|
||||
node: {
|
||||
component: H1Element,
|
||||
},
|
||||
rules: {
|
||||
break: { empty: 'reset' },
|
||||
},
|
||||
shortcuts: { toggle: { keys: 'mod+alt+1' } },
|
||||
}),
|
||||
H2Plugin.configure({
|
||||
node: {
|
||||
component: H2Element,
|
||||
},
|
||||
rules: {
|
||||
break: { empty: 'reset' },
|
||||
},
|
||||
shortcuts: { toggle: { keys: 'mod+alt+2' } },
|
||||
}),
|
||||
H3Plugin.configure({
|
||||
node: {
|
||||
component: H3Element,
|
||||
},
|
||||
rules: {
|
||||
break: { empty: 'reset' },
|
||||
},
|
||||
shortcuts: { toggle: { keys: 'mod+alt+3' } },
|
||||
}),
|
||||
H4Plugin.configure({
|
||||
node: {
|
||||
component: H4Element,
|
||||
},
|
||||
rules: {
|
||||
break: { empty: 'reset' },
|
||||
},
|
||||
shortcuts: { toggle: { keys: 'mod+alt+4' } },
|
||||
}),
|
||||
H5Plugin.configure({
|
||||
node: {
|
||||
component: H5Element,
|
||||
},
|
||||
rules: {
|
||||
break: { empty: 'reset' },
|
||||
},
|
||||
}),
|
||||
H6Plugin.configure({
|
||||
node: {
|
||||
component: H6Element,
|
||||
},
|
||||
rules: {
|
||||
break: { empty: 'reset' },
|
||||
},
|
||||
}),
|
||||
BlockquotePlugin.configure({
|
||||
node: { component: BlockquoteElement },
|
||||
shortcuts: { toggle: { keys: 'mod+shift+period' } },
|
||||
}),
|
||||
HorizontalRulePlugin.withComponent(HrElement),
|
||||
ParagraphPlugin.withComponent(ParagraphElement),
|
||||
H1Plugin.configure({
|
||||
node: {
|
||||
component: H1Element,
|
||||
},
|
||||
rules: {
|
||||
break: { empty: "reset" },
|
||||
},
|
||||
shortcuts: { toggle: { keys: "mod+alt+1" } },
|
||||
}),
|
||||
H2Plugin.configure({
|
||||
node: {
|
||||
component: H2Element,
|
||||
},
|
||||
rules: {
|
||||
break: { empty: "reset" },
|
||||
},
|
||||
shortcuts: { toggle: { keys: "mod+alt+2" } },
|
||||
}),
|
||||
H3Plugin.configure({
|
||||
node: {
|
||||
component: H3Element,
|
||||
},
|
||||
rules: {
|
||||
break: { empty: "reset" },
|
||||
},
|
||||
shortcuts: { toggle: { keys: "mod+alt+3" } },
|
||||
}),
|
||||
H4Plugin.configure({
|
||||
node: {
|
||||
component: H4Element,
|
||||
},
|
||||
rules: {
|
||||
break: { empty: "reset" },
|
||||
},
|
||||
shortcuts: { toggle: { keys: "mod+alt+4" } },
|
||||
}),
|
||||
H5Plugin.configure({
|
||||
node: {
|
||||
component: H5Element,
|
||||
},
|
||||
rules: {
|
||||
break: { empty: "reset" },
|
||||
},
|
||||
}),
|
||||
H6Plugin.configure({
|
||||
node: {
|
||||
component: H6Element,
|
||||
},
|
||||
rules: {
|
||||
break: { empty: "reset" },
|
||||
},
|
||||
}),
|
||||
BlockquotePlugin.configure({
|
||||
node: { component: BlockquoteElement },
|
||||
shortcuts: { toggle: { keys: "mod+shift+period" } },
|
||||
}),
|
||||
HorizontalRulePlugin.withComponent(HrElement),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,38 +1,38 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import {
|
||||
BoldPlugin,
|
||||
CodePlugin,
|
||||
HighlightPlugin,
|
||||
ItalicPlugin,
|
||||
StrikethroughPlugin,
|
||||
SubscriptPlugin,
|
||||
SuperscriptPlugin,
|
||||
UnderlinePlugin,
|
||||
} from '@platejs/basic-nodes/react';
|
||||
BoldPlugin,
|
||||
CodePlugin,
|
||||
HighlightPlugin,
|
||||
ItalicPlugin,
|
||||
StrikethroughPlugin,
|
||||
SubscriptPlugin,
|
||||
SuperscriptPlugin,
|
||||
UnderlinePlugin,
|
||||
} from "@platejs/basic-nodes/react";
|
||||
|
||||
import { CodeLeaf } from '@/components/ui/code-node';
|
||||
import { HighlightLeaf } from '@/components/ui/highlight-node';
|
||||
import { CodeLeaf } from "@/components/ui/code-node";
|
||||
import { HighlightLeaf } from "@/components/ui/highlight-node";
|
||||
|
||||
export const BasicMarksKit = [
|
||||
BoldPlugin,
|
||||
ItalicPlugin,
|
||||
UnderlinePlugin,
|
||||
CodePlugin.configure({
|
||||
node: { component: CodeLeaf },
|
||||
shortcuts: { toggle: { keys: 'mod+e' } },
|
||||
}),
|
||||
StrikethroughPlugin.configure({
|
||||
shortcuts: { toggle: { keys: 'mod+shift+x' } },
|
||||
}),
|
||||
SubscriptPlugin.configure({
|
||||
shortcuts: { toggle: { keys: 'mod+comma' } },
|
||||
}),
|
||||
SuperscriptPlugin.configure({
|
||||
shortcuts: { toggle: { keys: 'mod+period' } },
|
||||
}),
|
||||
HighlightPlugin.configure({
|
||||
node: { component: HighlightLeaf },
|
||||
shortcuts: { toggle: { keys: 'mod+shift+h' } },
|
||||
}),
|
||||
BoldPlugin,
|
||||
ItalicPlugin,
|
||||
UnderlinePlugin,
|
||||
CodePlugin.configure({
|
||||
node: { component: CodeLeaf },
|
||||
shortcuts: { toggle: { keys: "mod+e" } },
|
||||
}),
|
||||
StrikethroughPlugin.configure({
|
||||
shortcuts: { toggle: { keys: "mod+shift+x" } },
|
||||
}),
|
||||
SubscriptPlugin.configure({
|
||||
shortcuts: { toggle: { keys: "mod+comma" } },
|
||||
}),
|
||||
SuperscriptPlugin.configure({
|
||||
shortcuts: { toggle: { keys: "mod+period" } },
|
||||
}),
|
||||
HighlightPlugin.configure({
|
||||
node: { component: HighlightLeaf },
|
||||
shortcuts: { toggle: { keys: "mod+shift+h" } },
|
||||
}),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { BasicBlocksKit } from './basic-blocks-kit';
|
||||
import { BasicMarksKit } from './basic-marks-kit';
|
||||
import { BasicBlocksKit } from "./basic-blocks-kit";
|
||||
import { BasicMarksKit } from "./basic-marks-kit";
|
||||
|
||||
export const BasicNodesKit = [...BasicBlocksKit, ...BasicMarksKit];
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { CalloutPlugin } from '@platejs/callout/react';
|
||||
import { CalloutPlugin } from "@platejs/callout/react";
|
||||
|
||||
import { CalloutElement } from '@/components/ui/callout-node';
|
||||
import { CalloutElement } from "@/components/ui/callout-node";
|
||||
|
||||
export const CalloutKit = [CalloutPlugin.withComponent(CalloutElement)];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +1,18 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import {
|
||||
CodeBlockPlugin,
|
||||
CodeLinePlugin,
|
||||
CodeSyntaxPlugin,
|
||||
} from '@platejs/code-block/react';
|
||||
import { all, createLowlight } from 'lowlight';
|
||||
import { CodeBlockPlugin, CodeLinePlugin, CodeSyntaxPlugin } from "@platejs/code-block/react";
|
||||
import { all, createLowlight } from "lowlight";
|
||||
|
||||
import {
|
||||
CodeBlockElement,
|
||||
CodeLineElement,
|
||||
CodeSyntaxLeaf,
|
||||
} from '@/components/ui/code-block-node';
|
||||
import { CodeBlockElement, CodeLineElement, CodeSyntaxLeaf } from "@/components/ui/code-block-node";
|
||||
|
||||
const lowlight = createLowlight(all);
|
||||
|
||||
export const CodeBlockKit = [
|
||||
CodeBlockPlugin.configure({
|
||||
node: { component: CodeBlockElement },
|
||||
options: { lowlight },
|
||||
shortcuts: { toggle: { keys: 'mod+alt+8' } },
|
||||
}),
|
||||
CodeLinePlugin.withComponent(CodeLineElement),
|
||||
CodeSyntaxPlugin.withComponent(CodeSyntaxLeaf),
|
||||
CodeBlockPlugin.configure({
|
||||
node: { component: CodeBlockElement },
|
||||
options: { lowlight },
|
||||
shortcuts: { toggle: { keys: "mod+alt+8" } },
|
||||
}),
|
||||
CodeLinePlugin.withComponent(CodeLineElement),
|
||||
CodeSyntaxPlugin.withComponent(CodeSyntaxLeaf),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,23 +1,20 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { DndProvider } from "react-dnd";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
|
||||
import { DndPlugin } from '@platejs/dnd';
|
||||
import { DndPlugin } from "@platejs/dnd";
|
||||
|
||||
import { BlockDraggable } from '@/components/ui/block-draggable';
|
||||
import { BlockDraggable } from "@/components/ui/block-draggable";
|
||||
|
||||
export const DndKit = [
|
||||
DndPlugin.configure({
|
||||
options: {
|
||||
enableScroller: true,
|
||||
},
|
||||
render: {
|
||||
aboveNodes: BlockDraggable,
|
||||
aboveSlate: ({ children }) => (
|
||||
<DndProvider backend={HTML5Backend}>{children}</DndProvider>
|
||||
),
|
||||
},
|
||||
}),
|
||||
DndPlugin.configure({
|
||||
options: {
|
||||
enableScroller: true,
|
||||
},
|
||||
render: {
|
||||
aboveNodes: BlockDraggable,
|
||||
aboveSlate: ({ children }) => <DndProvider backend={HTML5Backend}>{children}</DndProvider>,
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,19 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { createPlatePlugin } from 'platejs/react';
|
||||
import { createPlatePlugin } from "platejs/react";
|
||||
|
||||
import { FixedToolbar } from '@/components/ui/fixed-toolbar';
|
||||
import { FixedToolbarButtons } from '@/components/ui/fixed-toolbar-buttons';
|
||||
import { FixedToolbar } from "@/components/ui/fixed-toolbar";
|
||||
import { FixedToolbarButtons } from "@/components/ui/fixed-toolbar-buttons";
|
||||
|
||||
export const FixedToolbarKit = [
|
||||
createPlatePlugin({
|
||||
key: 'fixed-toolbar',
|
||||
render: {
|
||||
beforeEditable: () => (
|
||||
<FixedToolbar>
|
||||
<FixedToolbarButtons />
|
||||
</FixedToolbar>
|
||||
),
|
||||
},
|
||||
}),
|
||||
createPlatePlugin({
|
||||
key: "fixed-toolbar",
|
||||
render: {
|
||||
beforeEditable: () => (
|
||||
<FixedToolbar>
|
||||
<FixedToolbarButtons />
|
||||
</FixedToolbar>
|
||||
),
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { createPlatePlugin } from 'platejs/react';
|
||||
import { createPlatePlugin } from "platejs/react";
|
||||
|
||||
import { FloatingToolbar } from '@/components/ui/floating-toolbar';
|
||||
import { FloatingToolbarButtons } from '@/components/ui/floating-toolbar-buttons';
|
||||
import { FloatingToolbar } from "@/components/ui/floating-toolbar";
|
||||
import { FloatingToolbarButtons } from "@/components/ui/floating-toolbar-buttons";
|
||||
|
||||
export const FloatingToolbarKit = [
|
||||
createPlatePlugin({
|
||||
key: 'floating-toolbar',
|
||||
render: {
|
||||
afterEditable: () => (
|
||||
<FloatingToolbar>
|
||||
<FloatingToolbarButtons />
|
||||
</FloatingToolbar>
|
||||
),
|
||||
},
|
||||
}),
|
||||
createPlatePlugin({
|
||||
key: "floating-toolbar",
|
||||
render: {
|
||||
afterEditable: () => (
|
||||
<FloatingToolbar>
|
||||
<FloatingToolbarButtons />
|
||||
</FloatingToolbar>
|
||||
),
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,19 +1,12 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { IndentPlugin } from '@platejs/indent/react';
|
||||
import { KEYS } from 'platejs';
|
||||
import { IndentPlugin } from "@platejs/indent/react";
|
||||
import { KEYS } from "platejs";
|
||||
|
||||
export const IndentKit = [
|
||||
IndentPlugin.configure({
|
||||
inject: {
|
||||
targetPlugins: [
|
||||
...KEYS.heading,
|
||||
KEYS.p,
|
||||
KEYS.blockquote,
|
||||
KEYS.codeBlock,
|
||||
KEYS.toggle,
|
||||
],
|
||||
},
|
||||
}),
|
||||
IndentPlugin.configure({
|
||||
inject: {
|
||||
targetPlugins: [...KEYS.heading, KEYS.p, KEYS.blockquote, KEYS.codeBlock, KEYS.toggle],
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { LinkPlugin } from '@platejs/link/react';
|
||||
import { LinkPlugin } from "@platejs/link/react";
|
||||
|
||||
import { LinkElement } from '@/components/ui/link-node';
|
||||
import { LinkFloatingToolbar } from '@/components/ui/link-toolbar';
|
||||
import { LinkElement } from "@/components/ui/link-node";
|
||||
import { LinkFloatingToolbar } from "@/components/ui/link-toolbar";
|
||||
|
||||
export const LinkKit = [
|
||||
LinkPlugin.configure({
|
||||
render: {
|
||||
node: LinkElement,
|
||||
afterEditable: () => <LinkFloatingToolbar />,
|
||||
},
|
||||
}),
|
||||
LinkPlugin.configure({
|
||||
render: {
|
||||
node: LinkElement,
|
||||
afterEditable: () => <LinkFloatingToolbar />,
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,26 +1,19 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { ListPlugin } from '@platejs/list/react';
|
||||
import { KEYS } from 'platejs';
|
||||
import { ListPlugin } from "@platejs/list/react";
|
||||
import { KEYS } from "platejs";
|
||||
|
||||
import { IndentKit } from '@/components/editor/plugins/indent-kit';
|
||||
import { BlockList } from '@/components/ui/block-list';
|
||||
import { IndentKit } from "@/components/editor/plugins/indent-kit";
|
||||
import { BlockList } from "@/components/ui/block-list";
|
||||
|
||||
export const ListKit = [
|
||||
...IndentKit,
|
||||
ListPlugin.configure({
|
||||
inject: {
|
||||
targetPlugins: [
|
||||
...KEYS.heading,
|
||||
KEYS.p,
|
||||
KEYS.blockquote,
|
||||
KEYS.codeBlock,
|
||||
KEYS.toggle,
|
||||
],
|
||||
},
|
||||
render: {
|
||||
belowNodes: BlockList,
|
||||
},
|
||||
}),
|
||||
...IndentKit,
|
||||
ListPlugin.configure({
|
||||
inject: {
|
||||
targetPlugins: [...KEYS.heading, KEYS.p, KEYS.blockquote, KEYS.codeBlock, KEYS.toggle],
|
||||
},
|
||||
render: {
|
||||
belowNodes: BlockList,
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { EquationPlugin, InlineEquationPlugin } from '@platejs/math/react';
|
||||
import { EquationPlugin, InlineEquationPlugin } from "@platejs/math/react";
|
||||
|
||||
import { EquationElement, InlineEquationElement } from '@/components/ui/equation-node';
|
||||
import { EquationElement, InlineEquationElement } from "@/components/ui/equation-node";
|
||||
|
||||
export const MathKit = [
|
||||
EquationPlugin.withComponent(EquationElement),
|
||||
InlineEquationPlugin.withComponent(InlineEquationElement),
|
||||
EquationPlugin.withComponent(EquationElement),
|
||||
InlineEquationPlugin.withComponent(InlineEquationElement),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { BlockSelectionPlugin } from '@platejs/selection/react';
|
||||
import { BlockSelectionPlugin } from "@platejs/selection/react";
|
||||
|
||||
import { BlockSelection } from '@/components/ui/block-selection';
|
||||
import { BlockSelection } from "@/components/ui/block-selection";
|
||||
|
||||
export const SelectionKit = [
|
||||
BlockSelectionPlugin.configure({
|
||||
render: {
|
||||
belowRootNodes: BlockSelection as any,
|
||||
},
|
||||
options: {
|
||||
isSelectable: (element) => {
|
||||
// Exclude specific block types from selection
|
||||
if (['code_line', 'td', 'th'].includes(element.type as string)) {
|
||||
return false;
|
||||
}
|
||||
BlockSelectionPlugin.configure({
|
||||
render: {
|
||||
belowRootNodes: BlockSelection as any,
|
||||
},
|
||||
options: {
|
||||
isSelectable: (element) => {
|
||||
// Exclude specific block types from selection
|
||||
if (["code_line", "td", "th"].includes(element.type as string)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
}),
|
||||
return true;
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,21 +1,20 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { SlashInputPlugin, SlashPlugin } from '@platejs/slash-command/react';
|
||||
import { KEYS } from 'platejs';
|
||||
import { SlashInputPlugin, SlashPlugin } from "@platejs/slash-command/react";
|
||||
import { KEYS } from "platejs";
|
||||
|
||||
import { SlashInputElement } from '@/components/ui/slash-node';
|
||||
import { SlashInputElement } from "@/components/ui/slash-node";
|
||||
|
||||
export const SlashCommandKit = [
|
||||
SlashPlugin.configure({
|
||||
options: {
|
||||
trigger: '/',
|
||||
triggerPreviousCharPattern: /^\s?$/,
|
||||
triggerQuery: (editor) =>
|
||||
!editor.api.some({
|
||||
match: { type: editor.getType(KEYS.codeBlock) },
|
||||
}),
|
||||
},
|
||||
}),
|
||||
SlashInputPlugin.withComponent(SlashInputElement),
|
||||
SlashPlugin.configure({
|
||||
options: {
|
||||
trigger: "/",
|
||||
triggerPreviousCharPattern: /^\s?$/,
|
||||
triggerQuery: (editor) =>
|
||||
!editor.api.some({
|
||||
match: { type: editor.getType(KEYS.codeBlock) },
|
||||
}),
|
||||
},
|
||||
}),
|
||||
SlashInputPlugin.withComponent(SlashInputElement),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import {
|
||||
TableCellHeaderPlugin,
|
||||
TableCellPlugin,
|
||||
TablePlugin,
|
||||
TableRowPlugin,
|
||||
} from '@platejs/table/react';
|
||||
TableCellHeaderPlugin,
|
||||
TableCellPlugin,
|
||||
TablePlugin,
|
||||
TableRowPlugin,
|
||||
} from "@platejs/table/react";
|
||||
|
||||
import {
|
||||
TableCellElement,
|
||||
TableCellHeaderElement,
|
||||
TableElement,
|
||||
TableRowElement,
|
||||
} from '@/components/ui/table-node';
|
||||
TableCellElement,
|
||||
TableCellHeaderElement,
|
||||
TableElement,
|
||||
TableRowElement,
|
||||
} from "@/components/ui/table-node";
|
||||
|
||||
export const TableKit = [
|
||||
TablePlugin.withComponent(TableElement),
|
||||
TableRowPlugin.withComponent(TableRowElement),
|
||||
TableCellPlugin.withComponent(TableCellElement),
|
||||
TableCellHeaderPlugin.withComponent(TableCellHeaderElement),
|
||||
TablePlugin.withComponent(TableElement),
|
||||
TableRowPlugin.withComponent(TableRowElement),
|
||||
TableCellPlugin.withComponent(TableCellElement),
|
||||
TableCellHeaderPlugin.withComponent(TableCellHeaderElement),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { TogglePlugin } from '@platejs/toggle/react';
|
||||
import { TogglePlugin } from "@platejs/toggle/react";
|
||||
|
||||
import { ToggleElement } from '@/components/ui/toggle-node';
|
||||
import { ToggleElement } from "@/components/ui/toggle-node";
|
||||
|
||||
export const ToggleKit = [
|
||||
TogglePlugin.configure({
|
||||
node: { component: ToggleElement },
|
||||
shortcuts: { toggle: { keys: 'mod+alt+9' } },
|
||||
}),
|
||||
TogglePlugin.configure({
|
||||
node: { component: ToggleElement },
|
||||
shortcuts: { toggle: { keys: "mod+alt+9" } },
|
||||
}),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,184 +1,160 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import type { PlateEditor } from 'platejs/react';
|
||||
import type { PlateEditor } from "platejs/react";
|
||||
|
||||
import { insertCallout } from '@platejs/callout';
|
||||
import { insertCodeBlock, toggleCodeBlock } from '@platejs/code-block';
|
||||
import { triggerFloatingLink } from '@platejs/link/react';
|
||||
import { insertInlineEquation } from '@platejs/math';
|
||||
import { TablePlugin } from '@platejs/table/react';
|
||||
import {
|
||||
type NodeEntry,
|
||||
type Path,
|
||||
type TElement,
|
||||
KEYS,
|
||||
PathApi,
|
||||
} from 'platejs';
|
||||
import { insertCallout } from "@platejs/callout";
|
||||
import { insertCodeBlock, toggleCodeBlock } from "@platejs/code-block";
|
||||
import { triggerFloatingLink } from "@platejs/link/react";
|
||||
import { insertInlineEquation } from "@platejs/math";
|
||||
import { TablePlugin } from "@platejs/table/react";
|
||||
import { type NodeEntry, type Path, type TElement, KEYS, PathApi } from "platejs";
|
||||
|
||||
const insertList = (editor: PlateEditor, type: string) => {
|
||||
editor.tf.insertNodes(
|
||||
editor.api.create.block({
|
||||
indent: 1,
|
||||
listStyleType: type,
|
||||
}),
|
||||
{ select: true }
|
||||
);
|
||||
editor.tf.insertNodes(
|
||||
editor.api.create.block({
|
||||
indent: 1,
|
||||
listStyleType: type,
|
||||
}),
|
||||
{ select: true }
|
||||
);
|
||||
};
|
||||
|
||||
const insertBlockMap: Record<
|
||||
string,
|
||||
(editor: PlateEditor, type: string) => void
|
||||
> = {
|
||||
[KEYS.listTodo]: insertList,
|
||||
[KEYS.ol]: insertList,
|
||||
[KEYS.ul]: insertList,
|
||||
[KEYS.codeBlock]: (editor) => insertCodeBlock(editor, { select: true }),
|
||||
[KEYS.table]: (editor) =>
|
||||
editor.getTransforms(TablePlugin).insert.table({}, { select: true }),
|
||||
[KEYS.callout]: (editor) => insertCallout(editor, { select: true }),
|
||||
[KEYS.toggle]: (editor) => {
|
||||
editor.tf.insertNodes(
|
||||
editor.api.create.block({ type: KEYS.toggle }),
|
||||
{ select: true }
|
||||
);
|
||||
},
|
||||
const insertBlockMap: Record<string, (editor: PlateEditor, type: string) => void> = {
|
||||
[KEYS.listTodo]: insertList,
|
||||
[KEYS.ol]: insertList,
|
||||
[KEYS.ul]: insertList,
|
||||
[KEYS.codeBlock]: (editor) => insertCodeBlock(editor, { select: true }),
|
||||
[KEYS.table]: (editor) => editor.getTransforms(TablePlugin).insert.table({}, { select: true }),
|
||||
[KEYS.callout]: (editor) => insertCallout(editor, { select: true }),
|
||||
[KEYS.toggle]: (editor) => {
|
||||
editor.tf.insertNodes(editor.api.create.block({ type: KEYS.toggle }), { select: true });
|
||||
},
|
||||
};
|
||||
|
||||
const insertInlineMap: Record<
|
||||
string,
|
||||
(editor: PlateEditor, type: string) => void
|
||||
> = {
|
||||
[KEYS.link]: (editor) => triggerFloatingLink(editor, { focused: true }),
|
||||
[KEYS.equation]: (editor) => insertInlineEquation(editor),
|
||||
const insertInlineMap: Record<string, (editor: PlateEditor, type: string) => void> = {
|
||||
[KEYS.link]: (editor) => triggerFloatingLink(editor, { focused: true }),
|
||||
[KEYS.equation]: (editor) => insertInlineEquation(editor),
|
||||
};
|
||||
|
||||
type InsertBlockOptions = {
|
||||
upsert?: boolean;
|
||||
upsert?: boolean;
|
||||
};
|
||||
|
||||
export const insertBlock = (
|
||||
editor: PlateEditor,
|
||||
type: string,
|
||||
options: InsertBlockOptions = {}
|
||||
editor: PlateEditor,
|
||||
type: string,
|
||||
options: InsertBlockOptions = {}
|
||||
) => {
|
||||
const { upsert = false } = options;
|
||||
const { upsert = false } = options;
|
||||
|
||||
editor.tf.withoutNormalizing(() => {
|
||||
const block = editor.api.block();
|
||||
editor.tf.withoutNormalizing(() => {
|
||||
const block = editor.api.block();
|
||||
|
||||
if (!block) return;
|
||||
if (!block) return;
|
||||
|
||||
const [currentNode, path] = block;
|
||||
const isCurrentBlockEmpty = editor.api.isEmpty(currentNode);
|
||||
const currentBlockType = getBlockType(currentNode);
|
||||
const [currentNode, path] = block;
|
||||
const isCurrentBlockEmpty = editor.api.isEmpty(currentNode);
|
||||
const currentBlockType = getBlockType(currentNode);
|
||||
|
||||
const isSameBlockType = type === currentBlockType;
|
||||
const isSameBlockType = type === currentBlockType;
|
||||
|
||||
if (upsert && isCurrentBlockEmpty && isSameBlockType) {
|
||||
return;
|
||||
}
|
||||
if (upsert && isCurrentBlockEmpty && isSameBlockType) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type in insertBlockMap) {
|
||||
insertBlockMap[type](editor, type);
|
||||
} else {
|
||||
editor.tf.insertNodes(editor.api.create.block({ type }), {
|
||||
at: PathApi.next(path),
|
||||
select: true,
|
||||
});
|
||||
}
|
||||
if (type in insertBlockMap) {
|
||||
insertBlockMap[type](editor, type);
|
||||
} else {
|
||||
editor.tf.insertNodes(editor.api.create.block({ type }), {
|
||||
at: PathApi.next(path),
|
||||
select: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!isSameBlockType) {
|
||||
editor.tf.removeNodes({ previousEmptyBlock: true });
|
||||
}
|
||||
});
|
||||
if (!isSameBlockType) {
|
||||
editor.tf.removeNodes({ previousEmptyBlock: true });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const insertInlineElement = (editor: PlateEditor, type: string) => {
|
||||
if (insertInlineMap[type]) {
|
||||
insertInlineMap[type](editor, type);
|
||||
}
|
||||
if (insertInlineMap[type]) {
|
||||
insertInlineMap[type](editor, type);
|
||||
}
|
||||
};
|
||||
|
||||
const setList = (
|
||||
editor: PlateEditor,
|
||||
type: string,
|
||||
entry: NodeEntry<TElement>
|
||||
) => {
|
||||
editor.tf.setNodes(
|
||||
editor.api.create.block({
|
||||
indent: 1,
|
||||
listStyleType: type,
|
||||
}),
|
||||
{
|
||||
at: entry[1],
|
||||
}
|
||||
);
|
||||
const setList = (editor: PlateEditor, type: string, entry: NodeEntry<TElement>) => {
|
||||
editor.tf.setNodes(
|
||||
editor.api.create.block({
|
||||
indent: 1,
|
||||
listStyleType: type,
|
||||
}),
|
||||
{
|
||||
at: entry[1],
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const setBlockMap: Record<
|
||||
string,
|
||||
(editor: PlateEditor, type: string, entry: NodeEntry<TElement>) => void
|
||||
string,
|
||||
(editor: PlateEditor, type: string, entry: NodeEntry<TElement>) => void
|
||||
> = {
|
||||
[KEYS.listTodo]: setList,
|
||||
[KEYS.ol]: setList,
|
||||
[KEYS.ul]: setList,
|
||||
[KEYS.codeBlock]: (editor) => toggleCodeBlock(editor),
|
||||
[KEYS.callout]: (editor, _type, entry) => {
|
||||
editor.tf.setNodes({ type: KEYS.callout }, { at: entry[1] });
|
||||
},
|
||||
[KEYS.toggle]: (editor, _type, entry) => {
|
||||
editor.tf.setNodes({ type: KEYS.toggle }, { at: entry[1] });
|
||||
},
|
||||
[KEYS.listTodo]: setList,
|
||||
[KEYS.ol]: setList,
|
||||
[KEYS.ul]: setList,
|
||||
[KEYS.codeBlock]: (editor) => toggleCodeBlock(editor),
|
||||
[KEYS.callout]: (editor, _type, entry) => {
|
||||
editor.tf.setNodes({ type: KEYS.callout }, { at: entry[1] });
|
||||
},
|
||||
[KEYS.toggle]: (editor, _type, entry) => {
|
||||
editor.tf.setNodes({ type: KEYS.toggle }, { at: entry[1] });
|
||||
},
|
||||
};
|
||||
|
||||
export const setBlockType = (
|
||||
editor: PlateEditor,
|
||||
type: string,
|
||||
{ at }: { at?: Path } = {}
|
||||
) => {
|
||||
editor.tf.withoutNormalizing(() => {
|
||||
const setEntry = (entry: NodeEntry<TElement>) => {
|
||||
const [node, path] = entry;
|
||||
export const setBlockType = (editor: PlateEditor, type: string, { at }: { at?: Path } = {}) => {
|
||||
editor.tf.withoutNormalizing(() => {
|
||||
const setEntry = (entry: NodeEntry<TElement>) => {
|
||||
const [node, path] = entry;
|
||||
|
||||
if (node[KEYS.listType]) {
|
||||
editor.tf.unsetNodes([KEYS.listType, 'indent'], { at: path });
|
||||
}
|
||||
if (type in setBlockMap) {
|
||||
return setBlockMap[type](editor, type, entry);
|
||||
}
|
||||
if (node.type !== type) {
|
||||
editor.tf.setNodes({ type }, { at: path });
|
||||
}
|
||||
};
|
||||
if (node[KEYS.listType]) {
|
||||
editor.tf.unsetNodes([KEYS.listType, "indent"], { at: path });
|
||||
}
|
||||
if (type in setBlockMap) {
|
||||
return setBlockMap[type](editor, type, entry);
|
||||
}
|
||||
if (node.type !== type) {
|
||||
editor.tf.setNodes({ type }, { at: path });
|
||||
}
|
||||
};
|
||||
|
||||
if (at) {
|
||||
const entry = editor.api.node<TElement>(at);
|
||||
if (at) {
|
||||
const entry = editor.api.node<TElement>(at);
|
||||
|
||||
if (entry) {
|
||||
setEntry(entry);
|
||||
if (entry) {
|
||||
setEntry(entry);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const entries = editor.api.blocks({ mode: 'lowest' });
|
||||
const entries = editor.api.blocks({ mode: "lowest" });
|
||||
|
||||
entries.forEach((entry) => {
|
||||
setEntry(entry);
|
||||
});
|
||||
});
|
||||
entries.forEach((entry) => {
|
||||
setEntry(entry);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const getBlockType = (block: TElement) => {
|
||||
if (block[KEYS.listType]) {
|
||||
if (block[KEYS.listType] === KEYS.ol) {
|
||||
return KEYS.ol;
|
||||
}
|
||||
if (block[KEYS.listType] === KEYS.listTodo) {
|
||||
return KEYS.listTodo;
|
||||
}
|
||||
return KEYS.ul;
|
||||
}
|
||||
if (block[KEYS.listType]) {
|
||||
if (block[KEYS.listType] === KEYS.ol) {
|
||||
return KEYS.ol;
|
||||
}
|
||||
if (block[KEYS.listType] === KEYS.listTodo) {
|
||||
return KEYS.listTodo;
|
||||
}
|
||||
return KEYS.ul;
|
||||
}
|
||||
|
||||
return block.type;
|
||||
return block.type;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,15 +12,14 @@
|
|||
const FENCED_OR_INLINE_CODE = /(```[\s\S]*?```|`[^`\n]+`)/g;
|
||||
|
||||
export function escapeMdxExpressions(md: string): string {
|
||||
const parts = md.split(FENCED_OR_INLINE_CODE);
|
||||
const parts = md.split(FENCED_OR_INLINE_CODE);
|
||||
|
||||
return parts
|
||||
.map((part, i) => {
|
||||
// Odd indices are code blocks / inline code – leave untouched
|
||||
if (i % 2 === 1) return part;
|
||||
// Escape { and } that are NOT already escaped (no preceding \)
|
||||
return part.replace(/(?<!\\)\{/g, '\\{').replace(/(?<!\\)\}/g, '\\}');
|
||||
})
|
||||
.join('');
|
||||
return parts
|
||||
.map((part, i) => {
|
||||
// Odd indices are code blocks / inline code – leave untouched
|
||||
if (i % 2 === 1) return part;
|
||||
// Escape { and } that are NOT already escaped (no preceding \)
|
||||
return part.replace(/(?<!\\)\{/g, "\\{").replace(/(?<!\\)\}/g, "\\}");
|
||||
})
|
||||
.join("");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue