diff --git a/ui/src/lib/paste-normalization.test.ts b/ui/src/lib/normalize-markdown.test.ts similarity index 92% rename from ui/src/lib/paste-normalization.test.ts rename to ui/src/lib/normalize-markdown.test.ts index ae1306fa67a..6afeed59102 100644 --- a/ui/src/lib/paste-normalization.test.ts +++ b/ui/src/lib/normalize-markdown.test.ts @@ -51,4 +51,9 @@ describe("normalizeMarkdown", () => { const expected = "base\n nested\nback\n deep"; expect(normalizeMarkdown(input)).toBe(expected); }); + + it("leaves mixed tab and space indentation unchanged", () => { + const input = "\t# Title\n body\n\t- item"; + expect(normalizeMarkdown(input)).toBe(input); + }); }); diff --git a/ui/src/lib/normalize-markdown.ts b/ui/src/lib/normalize-markdown.ts index a36aadb13fc..6cfc2584309 100644 --- a/ui/src/lib/normalize-markdown.ts +++ b/ui/src/lib/normalize-markdown.ts @@ -12,11 +12,18 @@ export function normalizeMarkdown(text: string): string { // Find minimum indentation across non-empty lines let minIndent = Infinity; + let indentStyle: "spaces" | "tabs" | null = null; for (const line of lines) { if (line.trim() === "") continue; const match = line.match(/^(\s+)/); if (match) { - minIndent = Math.min(minIndent, match[1].length); + const leadingWhitespace = match[1]; + const currentStyle = leadingWhitespace.includes("\t") ? "tabs" : "spaces"; + if (indentStyle && indentStyle !== currentStyle) { + return result; + } + indentStyle = currentStyle; + minIndent = Math.min(minIndent, leadingWhitespace.length); } else { minIndent = 0; break; diff --git a/ui/src/lib/paste-normalization.ts b/ui/src/lib/paste-normalization.ts index c180b5f719b..c766e201e32 100644 --- a/ui/src/lib/paste-normalization.ts +++ b/ui/src/lib/paste-normalization.ts @@ -1,5 +1,6 @@ import { createRootEditorSubscription$, realmPlugin } from "@mdxeditor/editor"; import { COMMAND_PRIORITY_CRITICAL, PASTE_COMMAND } from "lexical"; +import { looksLikeMarkdownPaste } from "./markdownPaste"; import { normalizeMarkdown } from "./normalize-markdown"; /** @@ -32,6 +33,10 @@ export const pasteNormalizationPlugin = realmPlugin({ // 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;