Preserve plain text spacing and backslashes on paste
Lexxy now preserves the exact whitespace and backslashes of plain‑text paths when they are pasted, eliminating the silent corruption caused by the previous markdown conversion path.
背景
Problem: Windows/UNC file paths that contain consecutive spaces or leading backslashes were corrupted on paste because the plain‑text input was forced through the markdown conversion pipeline (Clipboard#pasteMarkdown). The markdown renderer collapses runs of whitespace according to HTML rules and unescapes backslashes, which rewrites the user's original string. This issue manifested as paths like \\Arina-alvand\alvand\03 - ENGINEERING\... being pasted as \Arina-alvand\alvand\03 - ENGINEERING\... with spaces collapsed.
The bug was reported in Basecamp card 10006219528 and reproduced by pasting a UNC path with multiple spaces and backslashes.
技術的な変更
新しい判定ロジック: Clipboard#pasteMarkdown now invokes a private helper #isPlainTextWithoutMarkdown after the markdown conversion. If the resulting document consists of a single <p> element whose children are all text nodes, the method returns true and the original text is inserted verbatim.
@@ -165,14 +165,31 @@ export default class Clipboard {
#pasteMarkdown(text) {
const html = marked(text, { breaks: true })
const doc = parseHtml(html)
- const detail = Object.freeze({
- markdown: text,
- document: doc,
- addBlockSpacing: () => addBlockSpacing(doc)
- })
-
- dispatch(this.editorElement, "lexxy:insert-markdown", detail)
- this.contents.insertDOM(doc, { tag: PASTE_TAG })
+ if (this.#isPlainTextWithoutMarkdown(doc)) {
+ this.contents.insertText(text, { tag: PASTE_TAG })
+ } else {
+ const detail = Object.freeze({
+ markdown: text,
+ document: doc,
+ addBlockSpacing: () => addBlockSpacing(doc)
+ })
+
+ dispatch(this.editorElement, "lexxy:insert-markdown", detail)
+ this.contents.insertDOM(doc, { tag: PASTE_TAG })
+ }
+ }
+
+ // Markdown conversion collapses runs of whitespace and unescapes backslashes,
+ // silently corrupting plain text such as Windows/UNC file paths. When the text
+ // carries no Markdown structure, paste it verbatim instead.
+ #isPlainTextWithoutMarkdown(doc) {
+ const elements = Array.from(doc.body.children)
+ if (elements.length !== 1) return false
+
+ const paragraph = elements[0]
+ return paragraph.nodeName === "P"
+ && Array.from(paragraph.childNodes).every((node) => node.nodeType === Node.TEXT_NODE)
}
新しい挿入メソッド: Contents#insertText creates a paragraph node, appends a text node with the raw string, and delegates to the existing insertAtCursor pipeline. This reuses NodeInserter, ensuring that edge cases such as pasting inside a blockquote (addressed in PR #1109) remain safe.
@@ -49,6 +49,13 @@ export default class Contents {
}, { tag })
}
+ insertText(text, { tag } = {}) {
+ this.editor.update(() => {
+ const paragraph = $createParagraphNode().append($createTextNode(text))
+ this.insertAtCursor(paragraph)
+ }, { tag })
+ }
+
insertAtCursor(...nodes) {
const selection = $getSelection() ?? $getRoot().selectEnd()
const inserter = NodeInserter.for(selection)
テスト追加: Playwright tests under test/browser/tests/paste/plain_text_paths.test.js verify that UNC paths, consecutive spaces, and ordinary plain text survive paste unchanged, while genuine markdown (Hello **there**) still renders as expected.
設計判断
既存インフラの再利用: The fix opts to route plain‑text insertion through the same NodeInserter machinery rather than a low‑level insertRawText. This preserves the quote‑element‑point handling introduced in PR [#1109] and avoids code duplication. The detection method is deliberately lightweight—checking element count and node types—so the common markdown path remains unchanged.
マークダウン判定の境界: By limiting the verbatim path to documents that consist of a single text‑only paragraph, the implementation guarantees that any genuine markdown structure (headings, lists, links) continues to be processed by the full markdown pipeline. This design maintains backward compatibility for existing markdown‑centric workflows while fixing a specific plain‑text regression.
まとめ
The changes ensure that pasting Windows/UNC paths or any plain text with multiple spaces and backslashes retains its exact characters, addressing the silent corruption described in the original bug report. At the same time, the markdown conversion flow remains untouched for real markdown input, and the existing insertion infrastructure continues to handle complex cursor positions safely.