@@ -512,6 +512,78 @@ function linkedImageRule(nodeType: NodeType): InputRule {
512512 } ) ;
513513}
514514
515+ /**
516+ * 创建下标输入规则
517+ * <sub>text</sub>
518+ */
519+ function subRule ( markType : MarkType ) : InputRule {
520+ return createInlineRuleWithSyntax ( / < s u b > ( .+ ?) < \/ s u b > $ / , markType , "<sub>" , "</sub>" , 1 , "sub" ) ;
521+ }
522+
523+ /**
524+ * 创建上标输入规则
525+ * <sup>text</sup>
526+ */
527+ function supRule ( markType : MarkType ) : InputRule {
528+ return createInlineRuleWithSyntax ( / < s u p > ( .+ ?) < \/ s u p > $ / , markType , "<sup>" , "</sup>" , 1 , "sup" ) ;
529+ }
530+
531+ /** 不应通过通用 html_inline 规则处理的标签(已有专用 mark) */
532+ const HTML_INLINE_SKIP_TAGS = new Set ( [ "sub" , "sup" ] ) ;
533+
534+ /**
535+ * 创建通用行内 HTML 输入规则
536+ * <tag attrs>content</tag>
537+ */
538+ function htmlInlineRule ( markType : MarkType ) : InputRule {
539+ return new InputRule (
540+ / < ( [ a - z A - Z ] [ a - z A - Z 0 - 9 ] * ) ( \s (?: [ ^ > " ' ] | " [ ^ " ] * " | ' [ ^ ' ] * ' ) * ) ? > ( .+ ?) < \/ \1> $ / ,
541+ ( state , match , start , end ) => {
542+ const tag = match [ 1 ] . toLowerCase ( ) ;
543+ const htmlAttrs = ( match [ 2 ] || "" ) . trim ( ) ;
544+
545+ // 跳过有专用 mark 的标签
546+ if ( HTML_INLINE_SKIP_TAGS . has ( tag ) ) return null ;
547+
548+ const schema = state . schema ;
549+ const syntaxMarkerType = schema . marks . syntax_marker ;
550+ const contentMark = markType . create ( { tag, htmlAttrs } ) ;
551+
552+ const prefix = `<${ match [ 1 ] } ${ match [ 2 ] || "" } >` ;
553+ const suffix = `</${ match [ 1 ] } >` ;
554+ const content = match [ 3 ] ;
555+
556+ if ( ! content ) return null ;
557+
558+ let tr = state . tr . delete ( start , end ) ;
559+
560+ // 插入前缀(带 syntax_marker + html_inline mark)
561+ tr = tr . insertText ( prefix , start ) ;
562+ if ( syntaxMarkerType ) {
563+ const syntaxMark = syntaxMarkerType . create ( { syntaxType : "html_inline" } ) ;
564+ tr = tr . addMark ( start , start + prefix . length , syntaxMark ) ;
565+ }
566+ tr = tr . addMark ( start , start + prefix . length , contentMark ) ;
567+
568+ // 插入内容(带 html_inline mark)
569+ const contentStart = start + prefix . length ;
570+ tr = tr . insertText ( content , contentStart ) ;
571+ tr = tr . addMark ( contentStart , contentStart + content . length , contentMark ) ;
572+
573+ // 插入后缀(带 syntax_marker + html_inline mark)
574+ const suffixStart = contentStart + content . length ;
575+ tr = tr . insertText ( suffix , suffixStart ) ;
576+ if ( syntaxMarkerType ) {
577+ const syntaxMark = syntaxMarkerType . create ( { syntaxType : "html_inline" } ) ;
578+ tr = tr . addMark ( suffixStart , suffixStart + suffix . length , syntaxMark ) ;
579+ }
580+ tr = tr . addMark ( suffixStart , suffixStart + suffix . length , contentMark ) ;
581+
582+ return tr ;
583+ }
584+ ) ;
585+ }
586+
515587/**
516588 * 创建数学块输入规则
517589 * $$ 在行首输入时创建数学块
@@ -649,6 +721,15 @@ export function createInputRulesPlugin(schema: Schema = milkupSchema): Plugin {
649721 if ( schema . marks . math_inline ) {
650722 rules . push ( mathInlineRule ( schema . marks . math_inline ) ) ;
651723 }
724+ if ( schema . marks . sub ) {
725+ rules . push ( subRule ( schema . marks . sub ) ) ;
726+ }
727+ if ( schema . marks . sup ) {
728+ rules . push ( supRule ( schema . marks . sup ) ) ;
729+ }
730+ if ( schema . marks . html_inline ) {
731+ rules . push ( htmlInlineRule ( schema . marks . html_inline ) ) ;
732+ }
652733
653734 return inputRules ( { rules } ) ;
654735}
0 commit comments