docs/plans/2026-04-14-input-rules-recipe-registration-plan.md
inputRules public API across package plugins and shipped kitsThe current inputRules API is in an awkward middle state:
That shape is serviceable, but not the best DX.
The stronger model is:
Replace boolean-key activation with explicit rule registration:
H1Plugin.configure({
inputRules: [HeadingRules.markdown()],
});
Not:
H1Plugin.configure({
inputRules: { hash: true },
});
variant,
level, or checked instead of raw mechanics like trigger, match, or
literal delimiters.Examples:
BoldRules.markdown({ variant: '*' })
HeadingRules.markdown()
HorizontalRuleRules.markdown({ variant: '-' })
TaskListRules.markdown({ checked: false })
LinkRules.markdown()
variant, level, checked.trigger, match, or delimiters only for
intentionally open-ended families, not for closed package-owned ones.resolve or apply on canonical recipes.priority can still be overridden on the returned
rule object with object spread when a kit really needs it.Good:
BoldRules.markdown({ variant: '_' })
{ ...LinkRules.autolink({ variant: 'paste' }), priority: 200 }
Bad:
HorizontalRuleRules.markdown({ variant: '-' })
.trigger('-')
.priority(200)
The generic API lives in core builders, not in public leaf names.
mark, blockStart,
delimitedInline, textSubstitution.markdown, autolink, task.insert, toggle, or similarly generic
leaf names.This migration covers package-owned canonical input rules.
It does not flatten app-local shorthand into the same lane. Current
AutoformatKit shorthand remains separate and should be evaluated later as its
own product lane.
BoldRules.markdown(...), HeadingRules.markdown(),
HorizontalRuleRules.markdown(...), LinkRules.markdown(), and
MathRules.markdown(...).on: 'match' | 'break'enabled(context) instead of
helper-local isBlocked(editor) escape hatches.CodeBlockRules.markdown({ on }) and MathRules.markdown({ variant: '$$', on })
now sit on the same core block-fence primitive instead of carrying separate
block-start / insert-break matcher code.BasicMarksPlugin and the separate BasicMarkMarkdownCombosPlugin were
removed from the shipped basic-marks kit. The combo rules are currently
hosted under BoldPlugin.configure({ inputRules: [...] }) as app-local kit
wiring, not as a reusable package-level surface.AutoformatKit dropped the -- -> — and
... -> … transforms.pnpm turbo build --filter=./apps/wwwpnpm turbo typecheck --filter=./apps/wwwpnpm lint:fixResult:
apps/www/next.config.ts remainedCore should accept explicit rule instances:
type InputRulesConfig = AnyInputRule[];
Core should also support recipe materialization ergonomics:
type InputRuleRecipe<TOptions = void> = TOptions extends void
? AnyInputRule
: (options: TOptions) => AnyInputRule;
The runtime registry should only store concrete rule instances. It should not keep an "available but inactive" named rule map on plugins.
If the runtime needs stable identity for debug or test metadata, derive it
internally from the owning family and normalized params. Only add an optional
explicit id override later if real collision cases show up.
Examples of the intended package surface:
export const BoldRules = {
markdown: createBoldMarkdownRule(...),
};
export const HeadingRules = {
markdown: createHeadingMarkdownRule(...),
};
export const HorizontalRuleRules = {
markdown: createHorizontalRuleMarkdownRule(...),
};
Examples of the intended kit surface:
H1Plugin.configure({
inputRules: [HeadingRules.markdown()],
});
HorizontalRulePlugin.configure({
inputRules: [
HorizontalRuleRules.markdown({ variant: '-' }),
HorizontalRuleRules.markdown({ variant: '_' }),
],
});
@platejs/basic-nodesFiles:
packages/basic-nodes/src/lib/BaseBoldPlugin.tspackages/basic-nodes/src/lib/BaseItalicPlugin.tspackages/basic-nodes/src/lib/BaseUnderlinePlugin.tspackages/basic-nodes/src/lib/BaseCodePlugin.tspackages/basic-nodes/src/lib/BaseStrikethroughPlugin.tspackages/basic-nodes/src/lib/BaseSubscriptPlugin.tspackages/basic-nodes/src/lib/BaseSuperscriptPlugin.tspackages/basic-nodes/src/lib/BaseHighlightPlugin.tspackages/basic-nodes/src/lib/BaseHeadingPlugin.tspackages/basic-nodes/src/lib/BaseBlockquotePlugin.tspackages/basic-nodes/src/lib/BaseHorizontalRulePlugin.tsCurrent rule families:
asterisk, underscore, backtick, tilde, caret, equals,
althashmarkerdash, underscoreTarget:
hash declarationsmarker should likely become something more concretealt should likely become something meaning-bearing@platejs/code-blockFiles:
packages/code-block/src/lib/BaseCodeBlockPlugin.tsCurrent rule family:
fenceTarget:
CodeBlockRules.markdown({ on }) if the family is canonical markdown
entryon explicit because typed-fence commit and Enter-based commit are
meaningfully different@platejs/listFiles:
packages/list/src/lib/BaseListPlugin.tsxpackages/list/src/lib/inputRules.tsCurrent rule families:
bulletorderedtodoTarget:
BulletedListRules, OrderedListRules, TaskListRules{ checked: boolean } instead of raw token
mechanics for task variants@platejs/list-classicFiles:
packages/list-classic/src/lib/BaseListPlugin.tspackages/list-classic/src/lib/inputRules.tsCurrent rule families:
bulletorderedcheckeduncheckedTarget:
checked / unchecked collapse into one task family with
{ checked: boolean }@platejs/linkFiles:
packages/link/src/lib/BaseLinkPlugin.tspackages/link/src/lib/internal/inputRules.tsCurrent rule families:
markdownpasteAutolinkspaceAutolinkbreakAutolinkTarget:
markdown() or autolink({ variant: 'paste' })
over raw trigger-based kit config@platejs/mathFiles:
packages/math/src/lib/BaseInlineEquationPlugin.tspackages/math/src/lib/BaseEquationPlugin.tspackages/math/src/lib/inputRules.tsCurrent rule families:
dollardoubleDollarTarget:
MathRules.markdown({ variant: '$' }) for inline and
MathRules.markdown({ variant: '$$', on }) for block entry when the only
extra semantic knob is the block-fence commit modeFiles:
apps/www/src/registry/components/editor/plugins/basic-blocks-kit.tsxapps/www/src/registry/components/editor/plugins/basic-marks-kit.tsxapps/www/src/registry/components/editor/plugins/code-block-kit.tsxapps/www/src/registry/components/editor/plugins/link-kit.tsxapps/www/src/registry/components/editor/plugins/list-kit.tsxapps/www/src/registry/components/editor/plugins/list-classic-kit.tsxapps/www/src/registry/components/editor/plugins/math-kit.tsxTarget:
Files:
apps/www/src/registry/components/editor/plugins/autoformat-kit.tsxThis lane is intentionally separate.
It currently defines app-local text substitution rules and other shortcut behavior. Do not force it into the package-owned canonical recipe migration in phase 1.
Files:
packages/core/src/lib/plugins/input-rules/types.tspackages/core/src/internal/plugin/resolvePlugin.tspackages/core/src/internal/plugin/resolvePlugins.tspackages/core/src/lib/plugin/SlatePlugin.tspackages/core/src/react/plugin/PlatePlugin.tsTasks:
inputRules config with explicit rule-instance arraysTasks:
Tasks:
Open product call:
BasicBlocksKit and BasicMarksKit should remain neutral or become
explicitly markdown-oriented should be decided before implementationFiles to touch:
packages/*/src/lib/*.spec.tsxpackages/core/src/internal/plugin/resolvePlugins.spec.tsxpackages/core/src/react/utils/inputRules.spec.tsxapps/wwwTasks:
Files:
content/(plugins)/(functionality)/autoformat.mdxcontent/(plugins)/(functionality)/autoformat.cn.mdxnorth-star if the final API differs from this plan materiallyplate-plugin-creator if implementation mechanics need updatingTasks:
Recommended order:
Why this order:
Supporting both boolean-key activation and explicit rule-instance registration for long will rot the surface.
Mitigation:
If every feature invents its own export style, discoverability gets worse.
Mitigation:
If callers can replace resolve or apply, canonical recipes stop being
canonical.
Mitigation:
If app-local shorthand gets merged into the package-owned lane, ownership gets muddy again.
Mitigation:
AutoformatKit and other local shortcut bundles explicitly separate in
docs and codeHeadingRules.markdown()BoldRules.markdown({ variant: '*' })HeadingPlugin.rules.markdown(...)markeraltWrite the north-star rule for this activation model, then implement a proof
slice:
basic-blocks-kitIf that slice feels clean, hard-cut the boolean-key model and continue family by family.
north-star reaffirmed: laws
north-star reaffirmed: pattern-catalog
north-star reaffirmed: performance-selection-rules