mirror of
https://github.com/outline/outline.git
synced 2026-01-23 11:50:35 -06:00
* 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>
30 lines
1.1 KiB
TypeScript
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);
|
|
});
|
|
}
|