fix(core): restore markdown shortcuts and text substitutions after autoformat deprecation#4983
Merged
Conversation
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
🦋 Changeset detectedLatest commit: ccc4c21 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Collaborator
|
@codex review |
|
Codex Review: Didn't find any major issues. Delightful! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4968
Checklist
pnpm typecheckpnpm lint:fixbun testpnpm brlpnpm changesetSummary
This PR fixes two regressions introduced with the
@platejs/autoformat→ per-plugininputRulesmigration in v53. Either alone is enough to make the examples in docs feel broken; together they explain every variant of "shortcuts stopped working" being reported.createTextSubstitutionInputRulemis-derives the trigger character. Inline patterns like->→→,(c)→©, arrows, fractions, sub/superscripts all silently no-op on every keystroke.resolvePluginmutates the user's plugin config object. Any second editor instance built from the same kit array (StrictMode remount, HMR, multi-editor page) loses every.configure({ inputRules: [...] })rule — heading/list/blockquote/mark shortcuts stop firing entirely.Bug 1 - text substitutions never fire on the right trigger
Where:
packages/core/src/lib/plugins/input-rules/createInputRules.ts(getTextSubstitutionMatchRange)Root cause: the v53 rewrite collapsed v52's
{ end: match, start: '' }non-paired branch and the paired single-char branch into one path that always reverses the match string. For non-paired patterns the slice math then inverts:For
match: '->'the trigger becomes'-'and the search string becomes'>', so the rule registers under the wrong key and, even when it fires, searches for>in the doc before the cursor — which can never be there because the user just typed it. Single-char paired patterns (smart quotes) accidentally worked becausereversed === matchcollapses both branches to the same result.Fix: drop the reversal entirely.
Then mirror the existing
end: isPaired ? '' : endoverride withstart: isPaired ? start : ''inresolveTextSubstitution, so non-paired patterns don't try to find an opening delimiter that doesn't exist.Drive-by perf refactor (in the same function):
resolveTextSubstitutionwas rebuilding the{ end, start, triggers }tuple for every pattern on every keystroke (~50 patterns inAutoformatKit), then doing a lineartriggers.includes(text)scan to filter. The result is deterministic per pattern, so I lift it intocompilePatternsByTrigger(patterns)once at rule construction:O(P)string allocs + scans, whereP= total patterns across the ruleO(1)Map.get(text)+ iterate only patterns whose trigger equalstextArray.isArray(pattern.format) ? ... : ...twice per pattern per keystrokeMap<string, CompiledPattern[]>per rule + oneCompiledPatternper match stringThe
Maplookup also means typing any character that isn't a trigger short-circuits to zero work, instead of iterating every pattern.Bug 2 -
.configure({ inputRules })loses its rules on second resolveWhere:
packages/core/src/internal/plugin/resolvePlugin.tsRoot cause:
plugin.configure(config)stores a closure that returns the user'sconfigobject by reference:Then
resolvePluginconsumesinputRulesby mutating that exact object:On the first editor creation,
__configuredInputRulesis populated correctly. On the second editor creation using the same kit array, the closure returns the sameconfigobject — but now itsinputRulesisundefined, theifis skipped, and the plugin gets no configured rules. Verified with aProxyand direct property inspection:This is what the user observed on
/editors: the AI block iframe mounts twice (React StrictMode + iframe load behavior), so the second editor it builds fromEditorKithas zero configured rules forh1..h6,blockquote,bold,italic,list, etc. The basic editor next to it only mounts once, so its rules survive.Fix: rest-destructure instead of mutating.
Same allocation cost as the previous code (which already created
configResult), zero added work in the steady state. Local copy is the onlyinputRulesreference we mutate; the user's object stays intact.