mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-15 10:30:37 +09:00
67 lines
2.3 KiB
TypeScript
67 lines
2.3 KiB
TypeScript
|
|
import { createRootEditorSubscription$, realmPlugin } from "@mdxeditor/editor";
|
||
|
|
import { COMMAND_PRIORITY_CRITICAL, PASTE_COMMAND } from "lexical";
|
||
|
|
import { looksLikeMarkdownPaste } from "./markdownPaste";
|
||
|
|
import { normalizeMarkdown } from "./normalize-markdown";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* MDXEditor/Lexical plugin that intercepts paste events and normalizes
|
||
|
|
* markdown content before the editor processes it. Fixes issues with
|
||
|
|
* extra leading spaces when pasting from terminals or consoles.
|
||
|
|
*/
|
||
|
|
export const pasteNormalizationPlugin = realmPlugin({
|
||
|
|
init(realm) {
|
||
|
|
realm.pub(createRootEditorSubscription$, [
|
||
|
|
(editor) => {
|
||
|
|
let skipNext = false;
|
||
|
|
|
||
|
|
return editor.registerCommand(
|
||
|
|
PASTE_COMMAND,
|
||
|
|
(event) => {
|
||
|
|
if (skipNext) {
|
||
|
|
skipNext = false;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
const clipboardData =
|
||
|
|
event instanceof ClipboardEvent ? event.clipboardData : null;
|
||
|
|
if (!clipboardData) return false;
|
||
|
|
|
||
|
|
const text = clipboardData.getData("text/plain");
|
||
|
|
if (!text) return false;
|
||
|
|
|
||
|
|
// If there's HTML content, the source app already formatted it —
|
||
|
|
// let the default paste handler deal with rich content as-is.
|
||
|
|
if (clipboardData.getData("text/html")) return false;
|
||
|
|
|
||
|
|
// Markdown-looking pastes are handled by MarkdownEditor.tsx via
|
||
|
|
// insertMarkdown(), so the plugin only owns the plain-text fallback.
|
||
|
|
if (looksLikeMarkdownPaste(text)) return false;
|
||
|
|
|
||
|
|
const cleaned = normalizeMarkdown(text);
|
||
|
|
if (cleaned === text) return false;
|
||
|
|
|
||
|
|
// Prevent the original paste from being processed
|
||
|
|
if (event instanceof ClipboardEvent) {
|
||
|
|
event.preventDefault();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Re-dispatch with cleaned data so MDXEditor's handler processes it
|
||
|
|
const dt = new DataTransfer();
|
||
|
|
dt.setData("text/plain", cleaned);
|
||
|
|
const newEvent = new ClipboardEvent("paste", {
|
||
|
|
clipboardData: dt,
|
||
|
|
bubbles: true,
|
||
|
|
cancelable: true,
|
||
|
|
});
|
||
|
|
|
||
|
|
skipNext = true;
|
||
|
|
editor.dispatchCommand(PASTE_COMMAND, newEvent);
|
||
|
|
return true;
|
||
|
|
},
|
||
|
|
COMMAND_PRIORITY_CRITICAL,
|
||
|
|
);
|
||
|
|
},
|
||
|
|
]);
|
||
|
|
},
|
||
|
|
});
|