diff --git a/src/components/editor/Editor.tsx b/src/components/editor/Editor.tsx index dcf07161..611c9a58 100644 --- a/src/components/editor/Editor.tsx +++ b/src/components/editor/Editor.tsx @@ -546,7 +546,7 @@ export function Editor({ const pinNote = notesCtx?.pinNote; const unpinNote = notesCtx?.unpinNote; const notes = notesCtx?.notes; - const { textDirection } = useTheme(); + const { textDirection, pasteMode } = useTheme(); const [isSaving, setIsSaving] = useState(false); // Force re-render when selection changes to update toolbar active states const [, setSelectionKey] = useState(0); @@ -1213,22 +1213,46 @@ export function Editor({ } } - // Handle markdown text paste const text = clipboardData.getData("text/plain"); if (!text) return false; - // Check if text looks like markdown (has common markdown patterns) + const currentEditor = editorRef.current; + if (!currentEditor) return false; + + // Paste as plain text — strip all markdown formatting + if (pasteMode === "plain") { + currentEditor.commands.insertContent( + text.replace(/\r\n/g, "\n"), + { parseOptions: { preserveWhitespace: "full" } }, + ); + return true; + } + + // Paste as code block wrapped in ``` ``` + if (pasteMode === "code-block") { + const manager = currentEditor.storage.markdown?.manager; + if (manager && typeof manager.parse === "function") { + try { + const fenced = "```\n" + text + "\n```"; + const parsed = manager.parse(fenced); + if (parsed) { + currentEditor.commands.insertContent(parsed); + return true; + } + } catch { + // fall through to default + } + } + return false; + } + + // Default: "markdown" — detect and parse markdown patterns const markdownPatterns = /^#{1,6}\s|^\s*[-*+]\s|^\s*\d+\.\s|^\s*>\s|```|^\s*\[.*\]\(.*\)|^\s*!\[|\*\*.*\*\*|__.*__|~~.*~~|^\s*[-*_]{3,}\s*$|^\|.+\||\$\$[\s\S]+?\$\$/m; if (!markdownPatterns.test(text)) { - // Not markdown, let TipTap handle it normally return false; } - // Parse markdown and insert using editor ref - const currentEditor = editorRef.current; - if (!currentEditor) return false; - const manager = currentEditor.storage.markdown?.manager; if (manager && typeof manager.parse === "function") { try { diff --git a/src/components/settings/EditorSettingsSection.tsx b/src/components/settings/EditorSettingsSection.tsx index 997e9180..0e47c681 100644 --- a/src/components/settings/EditorSettingsSection.tsx +++ b/src/components/settings/EditorSettingsSection.tsx @@ -6,6 +6,7 @@ import type { TextDirection, EditorWidth, ThemeColorKey, + PasteMode, } from "../../types/note"; import { ChevronRightIcon, EyeIcon, MinusIcon, PlusIcon } from "../icons"; import { cn } from "../../lib/utils"; @@ -48,6 +49,13 @@ const fontFamilyOptions: { value: FontFamily; label: string }[] = [ { value: "monospace", label: "Mono" }, ]; +// Paste mode options +const pasteModeOptions: { value: PasteMode; label: string; description: string }[] = [ + { value: "markdown", label: "Markdown", description: "Parse and render markdown syntax" }, + { value: "plain", label: "Plain text", description: "Insert as plain text, no formatting" }, + { value: "code-block", label: "Code block", description: "Wrap in a ``` code block" }, +]; + // Bold weight options (medium excluded for monospace) const boldWeightOptions = [ { value: 500, label: "Medium", excludeForMonospace: true }, @@ -77,6 +85,8 @@ export function AppearanceSettingsSection() { setCustomColor, resetCustomColor, resetAllCustomColors, + pasteMode, + setPasteMode, } = useTheme(); // Validated numeric change handler @@ -458,6 +468,35 @@ export function AppearanceSettingsSection() {
+ + {/* Divider */} + + + {/* Editing Section */} +