Autolink bare URLs pasted with an HTML companion
Pasting a URL copied directly from the address bar in Chromium‑based browsers on macOS now automatically creates a link. The editor detects the clipboard shape where text/plain holds a bare URL and an optional matching text/html, and treats it as a URL paste.
背景
Clipboard type variance across browsers caused the regression. Safari supplies [ text/plain, text/uri-list ], the App ShareSheet provides [ text/uri-list ], while Chromium on macOS emits [ text/plain, text/html, (?:text/link-preview) ]. The original #isOnlyURLPasted method only returned true when text/uri-list was present, so Chromium‑derived pastes were excluded.
A second issue stemmed from #pastePlainText reading the first entry in clipboardData.items. Chromium lists the text/html entry before text/plain, causing the wrong fragment to be consumed even when a URL was detected.
技術的な変更
The paste flow now invokes #pastePlainTextOrURL instead of the former #pastePlainText. This method explicitly extracts the URL from text/plain or text/uri-list, bypassing the order‑dependent items[0] logic while preserving the existing call site.
@@ -40,7 +40,7 @@ export default class Clipboard {
}
if (this.#isPlainTextOrURLPasted(clipboardData)) {
- this.#pastePlainText(clipboardData)
+ this.#pastePlainTextOrURL(clipboardData)
event.preventDefault()
return true
}
The #isOnlyURLPasted implementation was overhauled. It now rejects Lexical‑specific clipboard data, handles pure text/uri-list sets, and detects a solitary URL in text/plain. When text/html is present, the new #htmlIsBareLinkToURL helper verifies that the HTML contains only that bare link before treating the paste as a URL.
@@ -78,14 +78,34 @@ export default class Clipboard {
return types.length === 1 && types[0] === "text/plain"
}
+ // Browsers expose a copied URL in several shapes:
+ // Safari [ text/plain, text/uri-list ]
+ // App ShareSheet [ text/uri-list ]
+ // Chromium macOS [ text/plain, text/html, (?:text/link-preview) ]
#isOnlyURLPasted(clipboardData) {
- // Safari URLs are copied as a text/plain + text/uri-list object
- // App ShareSheet URLs are copied as solo text/uri-list object
+ if (this.#isLexicalClipboardData(clipboardData)) return false
const types = Array.from(clipboardData.types)
- return types.length
- && types.length <= 2
- && types.includes("text/uri-list")
- && (types.length < 2 || types.includes("text/plain"))
+ if (types.includes("text/uri-list")) {
+ return types.every(type => type === "text/plain" || type === "text/uri-list")
+ }
+
+ if (clipboardData.files.length) return false
+
+ const text = clipboardData.getData("text/plain").trim()
+ if (!isAutolinkableURL(text)) return false
+
+ const html = clipboardData.getData("text/html")
+ return !html || this.#htmlIsBareLinkToURL(html, text)
}
+
+ #htmlIsBareLinkToURL(html, url) {
+ const doc = parseHtml(html)
+ if (doc.body.textContent.trim() !== url) return false
+
+ const links = doc.body.querySelectorAll("a")
+ if (links.length === 0) return true
+ return links.length === 1 && links[0].getAttribute("href") === url
+ }
Two new browser tests were added to verify the corrected behaviour. One test confirms that a clipboard containing text/plain + matching text/html autolinks the URL on Chromium macOS, and another ensures that richer HTML content falls back to the normal HTML insertion path.
設計判断
The primary design decision was to treat a clipboard where text/plain holds a lone autolinkable URL and an optional matching text/html as a URL paste. This guard keeps genuine rich pastes—where the HTML contains additional markup or a different link target—on the HTML path, preserving existing user expectations.
Rather than relying on the ordering of clipboardData.items, the implementation now explicitly queries text/plain and text/uri-list, removing browser‑specific ordering dependencies. The HTML‑equality check introduces a minimal code path while maintaining backward compatibility; Safari and ShareSheet behaviours remain unchanged.
まとめ
By recognizing the Chromium‑macOS clipboard shape and reading the URL from the appropriate data type, the editor now autolinks bare URLs consistently across browsers. The change adds a small HTML‑validation helper, retains legacy behaviours, and is verified by 2 new browser tests while the full paste suite remains green (345 tests).