docs/solutions/best-practices/input-rule-context-should-provide-lazy-snapshot-getters.md
Package-owned input rules were repeating the same resolve() setup over and
over: check collapsed selection, read text from block start, inspect adjacent
characters, then do the real semantic match.
That duplication was noisy, and it also made the runtime API worse than it had to be.
createInputRule DSL just to hide repeated lookup code.Extend the shared input-rule context with lazy cached getters computed from the input-event snapshot:
type SelectionInputRuleContext<TEditor extends SlateEditor = SlateEditor> = {
editor: TEditor;
isCollapsed: boolean;
getBlockEntry: () => NodeEntry | undefined;
getBlockStartRange: () => TRange | undefined;
getBlockStartText: () => string | undefined;
getTextBeforeSelection: () => string;
getCharBefore: () => string | undefined;
getCharAfter: () => string | undefined;
};
The runtime creates those lazily and caches the result for the current input event. That keeps the fast path cheap while still cutting repeated rule code.
Then rules use the context instead of rebuilding the same lookup chain:
resolve: ({ getBlockStartText, isCollapsed, text }) => {
if (text !== ' ' || !isCollapsed) return;
return getBlockStartText() === '>' ? true : undefined;
}
For the runtime registry, keep authoring typed through defineInputRule(...),
but widen the stored container shape to target-specific runtime rules. The
container only needs to know:
resolveapplyIt does not need to preserve the exact payload generic at storage time.
Lazy getters solve the right problem.
editor.api.* ceremony.The widened runtime registry also solves the right typing problem.
resolve and apply through defineInputRule.apply() starts editing the document.