Files
outline/shared/editor/lib/listInputRule.ts
huiseo 9ec5c473f1 fix: prevent list conversion inside heading nodes (#10462)
* fix: prevent list conversion inside heading nodes

Fixes a bug where typing list syntax (e.g., "1. ", "* ", "[ ]")
inside heading nodes would incorrectly trigger list conversion.

Previously, when a user selected H1 from the "/" menu and typed
"1. " followed by a space, the OrderedList inputRule would attempt
to convert the heading into an ordered list, causing a conflict
since headings can only contain inline content.

Changes:
- Add isInHeading utility to detect if selection is inside a heading
- Create safeWrappingInputRule wrapper that prevents list conversion
  when inside heading nodes
- Apply the fix to OrderedList, BulletList, and CheckboxList nodes

This ensures that list markdown syntax is preserved as plain text
when typed within headings, matching expected editor behavior.

* refactor: extract listWrappingInputRule to shared helper

Refactored duplicated safeWrappingInputRule implementations across
BulletList, OrderedList, and CheckboxList into a single shared helper
function named listWrappingInputRule in shared/editor/lib/listInputRule.ts.

This reduces code duplication and follows the same pattern as other
input rule helpers like markInputRule.

Changes:
- Create shared/editor/lib/listInputRule.ts with listWrappingInputRule
- Update BulletList.ts to use shared helper
- Update OrderedList.ts to use shared helper
- Update CheckboxList.ts to use shared helper
- Restore .env.development file

Co-Authored-By: huiseo <hui.seo@gmail.com>
2025-10-23 20:23:47 -04:00

30 lines
1.1 KiB
TypeScript

import { wrappingInputRule, InputRule } from "prosemirror-inputrules";
import { NodeType, Node as ProsemirrorNode, Attrs } from "prosemirror-model";
import { isInHeading } from "../queries/isInHeading";
/**
* A wrapper for wrappingInputRule that prevents execution inside heading nodes.
* This fixes the bug where typing list triggers ("* ", "- ", "1. ", etc.) inside
* a heading would trigger list conversion.
*/
export function listWrappingInputRule(
regexp: RegExp,
nodeType: NodeType,
getAttrs?: (match: RegExpMatchArray) => Attrs | null,
joinPredicate?: (match: RegExpMatchArray, node: ProsemirrorNode) => boolean
): InputRule {
const rule = wrappingInputRule(regexp, nodeType, getAttrs, joinPredicate);
// Wrap the original rule to check if we're inside a heading
return new InputRule(regexp, (state, match, start, end) => {
// Don't apply the rule if we're inside a heading
if (isInHeading(state)) {
return null;
}
// Otherwise, execute the original wrappingInputRule handler
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (rule as any).handler(state, match, start, end);
});
}