docs/plans/2026-04-11-link-input-autolink-policy-rewrite-plan.md
Proposed.
Replace the current mixed link input behavior with a coherent, explicit, and profile-friendly design for:
[text](...) source entry fixes a real UX
bug without adding expensive machineryBaseLinkPlugin currently mixes:
rangeBeforeOptionskeepSelectedTextOnPastegetUrlHrefshouldAutoLinkPastelinkAutomdInputRule living as a neighboring but separate source-entry
laneIf Plate wants the best long-term design, this should be a breaking cleanup, not another local callback.
Current implementation:
Current law and evidence:
Split link input assist out of BaseLinkPlugin.
Create a dedicated link input surface in @platejs/link:
BaseLinkPlugin
BaseLinkInputPlugin or LinkInputPlugin
Keep markdown source-entry conversion ([text](url) on )) as a neighboring
link-owned input lane, not folded into plain autolink. It can stay hosted by
the shared typed-input runtime.
rich-first, reversible-rich, or future source-first profiles can make
different input-policy choices cleanlyBaseLinkPluginReject.
That path keeps growing callback knobs on the wrong seam.
autolink objectBetter than today, but still not the best permanent architecture.
It still makes link semantics and input assist feel like one inseparable feature.
Reject.
Link automd is already correctly spec'd as richer source-entry interaction, not plain autolink literal behavior.
type LinkInputContext = {
cause: "insert-space" | "insert-break" | "paste";
editor: SlateEditor;
selectionMode: "collapsed" | "expanded";
sourceText: string;
textBefore: string;
textAfter: string;
url: string;
inCodeLikeContext: boolean;
inLink: boolean;
inMarkdownSourceEntry: boolean;
};
type LinkInputConfig = {
autolink?: {
typing?: {
enabled?: boolean;
commitTriggers?: ("space" | "break")[];
};
paste?: {
enabled?: boolean;
selectedText?: "preserve" | "replace";
};
resolveUrl?: (context: {
editor: SlateEditor;
text: string;
cause: LinkInputContext["cause"];
}) => string | undefined;
shouldLink?: (context: LinkInputContext) => boolean;
};
};
Retire or replace these current options:
keepSelectedTextOnPaste
autolink.paste.selectedTextgetUrlHref
autolink.resolveUrlshouldAutoLinkPaste
autolink.shouldLinkrangeBeforeOptions
Keep transformInput where it belongs:
It should not become the generic autolink policy hook.
For the default rich profile:
Enter)Do not redesign this around slower abstractions.
The winning design must keep:
The fix is about policy and ownership, not raw speed.
BaseLinkPlugin alone no longer owns typing/paste autolink behavior[text](...) source stays literal by defaultEnter after a plain URL still finalizes autolink in rich
profiles) still works and does not fight the
plain autolink laneSpec the split before code
Extract the input surface
packages/link/src/libBaseLinkPlugin semantics-onlyIntroduce the unified policy object
paste, insert-space, and
insert-breakRecompose shipped surfaces
LinkKit includes the input plugin by defaultMigrate docs and release surface
Mitigation:
Mitigation:
Mitigation:
[text](...) source-entry paste stays literalEnter after URLlinkAutomdInputRulepnpm installpnpm turbo build --filter=./packages/linkpnpm turbo typecheck --filter=./packages/linkpnpm lint:fixIf this gets approved, do not keep iterating on shouldAutoLinkPaste.
That callback is already proof that the current seam is too small and too special-cased. The right move is to split link input assist into its own plugin and give it one coherent policy model.