@peaceroad/markdown-it-renderer-inline-text
v0.9.0
Published
A markdown-it plugin and analyzer for inline ruby, figure references, and star/percent comment rendering.
Readme
p7d-markdown-it-renderer-inline-text
Inline text transform plugin for markdown-it.
It adds:
- Ruby conversion (
漢字《かんじ》) - Automatic and explicit figure-reference decoration (
(図1),**Figure A**,*Fig. 1*) - Star comments (
★...★) - Percent comments (
%%...%%)
Quick Start
import mdit from 'markdown-it'
import mditRendererInlineText from '@peaceroad/markdown-it-renderer-inline-text'
// Ruby/figure/star/percent transforms work with both html:true and html:false.
const md = mdit({ html: true }).use(mditRendererInlineText, {
ruby: true,
figureReference: true,
starComment: true,
percentComment: true,
})
console.log(md.render('今夜は★カツ★カレーです。'))
// <p>今夜は<span class="star-comment">★カツ★</span>カレーです。</p>
console.log(md.render('今日は甘味処が%%午後%%休みです。'))
// <p>今日は甘味処が<span class="percent-comment">%%午後%%</span>休みです。</p>
console.log(md.render('昼食は親子丼《おやこどん》です。'))
// <p>昼食は<ruby>親子丼<rp>《</rp><rt>おやこどん</rt><rp>》</rp></ruby>です。</p>
console.log(md.render('詳細は(図A.1)を参照してください。'))
// <p>詳細は(<span class="f-ref">図A.1</span>)を参照してください。</p>
console.log(md.render('詳細は **図A.1** を参照してください。'))
// <p>詳細は <span class="f-ref">図A.1</span> を参照してください。</p>Ruby Syntax
Ruby conversion is based on:
- bare shorthand:
([Han + 0-9A-Za-z._-]+)《reading》 - explicit wrapper shorthand:
<ruby>base《reading》</ruby>(open/close pair required, case-insensitive tag match)
Examples:
Input: 寿司は職人《しょくにん》の技です。
Output: <p>寿司は<ruby>職人<rp>《</rp><rt>しょくにん</rt><rp>》</rp></ruby>の技です。</p>
Input: 商品名はRAMEN2025《らーめんにーぜろにーごー》です。
Output: <p>商品名は<ruby>RAMEN2025<rp>《</rp><rt>らーめんにーぜろにーごー</rt><rp>》</rp></ruby>です。</p>When the base text has long kanji runs or includes hiragana/katakana, use explicit <ruby>...</ruby> for predictable wrapping:
Input: お店の名物<ruby>鯛茶漬《たいちゃづけ》</ruby>を紹介します。
Output: <p>お店の名物<ruby>鯛茶漬<rp>《</rp><rt>たいちゃづけ</rt><rp>》</rp></ruby>を紹介します。</p>Ruby shorthand conversion works in both html:true and html:false. With html:false, HTML-like input is generally escaped, while ruby shorthand and explicit <ruby>...</ruby> wrappers are still rendered as ruby HTML.
If the wrapper is unclosed (for example <RUBY>寿司《すし》), the wrapper text is treated as normal text and only shorthand part is converted.
html:false example:
Input:案内文:<ruby>寿司《すし》</ruby>を掲載します。
Output:<p>案内文:<ruby>寿司<rp>《</rp><rt>すし</rt><rp>》</rp></ruby>を掲載します。</p>Figure Reference Syntax
figureReference: true enables both authoring styles. Use the two mode-specific
options when only one style is wanted or when overriding one side of the
shorthand:
figureReferenceAuto: automatically decorate a recognized label and identifier inside paired ASCII or fullwidth parentheses. The parentheses and surrounding whitespace are preserved.figureReferenceManual: treat an exact recognized reference wrapped in**...**or*...*as an explicit reference marker. Both use the configured figure-reference tag instead of emitting<strong>or<em>because the wrapper is a presentation hook rather than author emphasis.
Input: 詳細は(図1)と(図A.1)を参照してください。
Output: <p>詳細は(<span class="f-ref">図1</span>)と(<span class="f-ref">図A.1</span>)を参照してください。</p>
Input: See (Figure 1-1) and (Figure A).
Output: <p>See (<span class="f-ref">Figure 1-1</span>) and (<span class="f-ref">Figure A</span>).</p>
Input: See (Fig. 1), (Fig.A-1), and (Figure.A.1).
Output: <p>See (<span class="f-ref">Fig. 1</span>), (<span class="f-ref">Fig.A-1</span>), and (<span class="f-ref">Figure.A.1</span>).</p>
Input: 詳細は(**図1**)と **図A.1** を参照してください。
Output: <p>詳細は(<span class="f-ref">図1</span>)と <span class="f-ref">図A.1</span> を参照してください。</p>
Input: See *Figure A* and *Fig. 1*.
Output: <p>See <span class="f-ref">Figure A</span> and <span class="f-ref">Fig. 1</span>.</p>Recognized forms:
- Parentheses must be a matching
(...)or(...)pair. - Japanese references start with
図and have no intervening space. - English references start with case-sensitive
FigureorFig. Figureis followed by either an ASCII dot or one or more spaces, tabs, or fullwidth spaces.Figis followed by one or more such spaces, or by an ASCII dot with optional spaces after it. This coversFig 1,Fig.1, andFig. 1.- Each identifier component is either one ASCII/fullwidth uppercase letter or one or more ASCII/fullwidth digits.
- Components can be joined with ASCII
.or-.
Examples include 図1, 図1, 図A, 図A, 図1.1, 図A.1, 図1-1,
図A-1, Figure 1, Figure A.1, Figure.1, Figure.A-1, and
Figure.A.1, Fig 1, Fig.1, Fig. 1, and Fig.A-1. Lowercase aliases
such as figure / fig and all-uppercase FIG are not recognized. Fullwidth
separators such as ., -, and : are intentionally not recognized.
Both modes default to <span class="f-ref">. Use figureReferenceTag to select
span, b, or i for both. Set figureReferenceManualTagFromMarker: true
when manual **...** should use b and manual *...* should use i instead.
Use figureReferenceClass to change the class in both modes.
The plugin emits the class hook but does not bundle a stylesheet for it.
md.use(mditRendererInlineText, {
figureReferenceManual: true,
figureReferenceManualTagFromMarker: true,
})
// **図1** -> <b class="f-ref">図1</b>
// *図1* -> <i class="f-ref">図1</i>Manual mode accepts only an exact recognized reference inside one asterisk
delimiter pair. It intentionally leaves _図1_, ***図1***, ** 図1 **, and
ordinary emphasis such as **重要** to markdown-it.
It reuses markdown-it's parsed emphasis tokens; if a preset disables the
emphasis inline rule (for example the unmodified zero preset), manual mode
has no delimiter tokens to retag and leaves the source unchanged.
Inline/fenced code and link destinations are not rewritten. With html:true,
raw HTML and its attributes are also left unchanged. With html:false,
HTML-looking source is ordinary text, so a reference inside it can be
decorated while markdown-it escapes the tag-like text. An odd backslash run
before either opening parenthesis prevents conversion. Markdown-it removes the
escaping backslash from \(Figure 1); a backslash before fullwidth ( remains
literal because it is not standard Markdown ASCII punctuation.
Compatibility with @peaceroad/markdown-it-figure-with-p-caption
Caption detection runs before this plugin's manual core transform, so plain
caption labels such as 図1 Caption and Figure 1. Caption remain owned by the
figure plugin. This is stable in either .use(...) order. Caption labels
generated as <span>, <b>, or <strong> by that plugin are not retagged;
explicit references in caption body text can still use the manual syntax.
Do not use **図1** as the leading caption label. The figure plugin does not
recognize an emphasis token as a caption label even without this plugin. Use a
plain source label and its bLabel: true option when the rendered caption label
should use <b>.
The classes intentionally remain role-specific: f-img-label identifies a
label in a caption, while f-ref identifies a reference in body text. When
they need the same appearance, group the selectors in CSS (for example,
:where(.f-img-label, .f-ref)) rather than making the DOM roles share one
class name.
★ / %% Comment Syntax
Both syntaxes are pair-based markers:
- Star:
★...★-><span class="star-comment">...</span> - Percent:
%%...%%-><span class="percent-comment">...</span>(or custom class)
Behavior summary (without examples):
- Pair matching is per marker type, so
★...★and%%...%%are handled independently. - Escaped markers (odd backslash parity) stay as plain text.
- In default span mode, matched ranges are wrapped and preserved in output.
- In delete mode, only the enabled marker type is removed from output.
- In inline mode, marker ranges are fixed by preparse before markdown inline formatting.
Escape Markers
Input:料理名は\★限定★ではありません。
Output:<p>料理名は★限定★ではありません。</p>
Input:今日の注記は\%%内輪%%ではありません。
Output:<p>今日の注記は%%内輪%%ではありません。</p>Span Element (Default)
Basic:
Input: 蕎麦は★売り切れ次第終了です。★美味しいです。
Output: <p>蕎麦は<span class="star-comment">★売り切れ次第終了です。★</span>美味しいです。</p>
Input: 喫茶店に%%季節限定の%%パフェがあります。
Output: <p>喫茶店に<span class="percent-comment">%%季節限定の%%</span>パフェがあります。</p>
Input: 今日は★**本日のおすすめ**の★ハンバーグを注文します。
Output: <p>今日は<span class="star-comment">★**本日のおすすめ**の★</span>ハンバーグを注文します。</p>In html:true, inline HTML inside a marker range is kept as HTML:
Input: メニューから★<span>だし</span>香る★うどんを選びます。
Output: <p>メニューから<span class="star-comment">★<span>だし</span>香る★</span>うどんを選びます。</p>Marker priority is high in inline mode (html:true / html:false): markdown syntax inside marker ranges stays literal.
Input: **春★御膳**定★食を案内します。
Output: <p>**春<span class="star-comment">★御膳**定★</span>食を案内します。</p>Delete option
starCommentDelete and percentCommentDelete are independent:
starCommentDelete: trueremoves only★...★ranges.percentCommentDelete: trueremoves only%%...%%ranges.
const md = MarkdownIt().use(rendererInlineText, {
starComment: true,
starCommentDelete: true,
percentComment: true,
percentCommentDelete: true,
})
console.log(md.render('カレーのメインは%%海老%%鶏肉です。'))
// <p>カレーのメインは鶏肉です。</p>
console.log(md.render('カレーのメインは★あさり★マトンです。'))
// <p>カレーのメインはマトンです。</p>Line and Paragraph Mode
Line mode example:
Options:{ starComment: true, starCommentLine: true }
Input:
通常案内
★売り切れ注意
通常案内
Output:<p>通常案内
<span class="star-comment">★売り切れ注意</span>
通常案内</p>Percent comments follow the same line/paragraph behaviors with percentCommentLine, percentCommentParagraph, and percentCommentParagraphClass.
Paragraph mode example:
Options:{ starComment: true, starCommentParagraph: true }
Input: ★本日は売り切れ次第終了です。
Output: <p><span class="star-comment">★本日は売り切れ次第終了です。</span></p>Paragraph-only class example:
Options:{ starComment: true, starCommentParagraph: true, starCommentParagraphClass: true }
Input: ★本日は売り切れ次第終了です。
Output: <p class="star-comment">★本日は売り切れ次第終了です。</p>Options
ruby(default:false) Enable ruby conversion.figureReference(default:false) Enable both automatic and manual figure-reference conversion. It supplies the default for each mode-specific option.figureReferenceAuto(default: value offigureReference) Automatically decorate recognized図...,Figure..., andFig...references inside paired parentheses. An explicit value overridesfigureReferencefor this mode.figureReferenceManual(default: value offigureReference) Convert exact**reference**/*reference*author markers with the configured reference tag and class. An explicit value overridesfigureReferencefor this mode.figureReferenceTag(default:"span") Output tag for automatic and manual references. Allowed values are"span","b", and"i".figureReferenceManualTagFromMarker(default:false) In manual mode, derive the tag from the author marker instead offigureReferenceTag:**reference**uses<b>and*reference*uses<i>.figureReferenceClass(default:"f-ref") CSS class for automatic and manual references.starComment(default:false) Enable★...★comments.starCommentDelete(default:false) Delete star-comment spans instead of rendering them.starCommentParagraph(default:false) Paragraph mode for stars (paragraph starts with★).starCommentLine(default:false) Line mode for stars (editor line starts with★).starCommentParagraphClass(default:false) In star paragraph mode, add class to<p>and skip inner span wrapping.trueuses"star-comment", and a string uses that class name.percentComment(default:false) Enable%%...%%comments.percentCommentDelete(default:false) Delete percent-comment spans instead of rendering them.percentCommentParagraph(default:false) Paragraph mode for percents (paragraph starts with%%).percentCommentLine(default:false) Line mode for percents (editor line starts with%%).percentCommentParagraphClass(default:false) In percent paragraph mode, add class to<p>and skip inner span wrapping.trueusespercentClass, and a string uses that class name.percentClass(default:"percent-comment") CSS class for percent-comment spans.
Notes:
- If
starCommentLineistrue,starCommentParagraphis disabled. - If
percentCommentLineistrue,percentCommentParagraphis disabled. - Star and percent line modes can be enabled together; mixed ★/%% lines are classified independently, including delete mode.
percentClassis escaped viamd.utils.escapeHtml.figureReferenceTagis validated at setup time; token attribute rendering escapesfigureReferenceClass.figureReferenceManualTagFromMarkeraffects only the manual output tag; range recognition is unchanged.figureReference: trueenables both modes; explicitfigureReferenceAuto/figureReferenceManualvalues override the shorthand independently.
Analyzer API (Experimental)
You can also import a parserless analyzer API from:
import {
normalizeOptions,
createRuntimePlan,
lineStartsWithStar,
lineStartsWithPercent,
isEscapedStar,
isEscapedPercent,
normalizeLineWindow,
expandToParagraphBoundaries,
shouldFullAnalyze,
scanInlineRanges,
analyzeLines,
analyzeLineWindow,
} from '@peaceroad/markdown-it-renderer-inline-text/analyzer'This API is intended for editor-side highlighting (for example VSCode) without running markdown-it.
Recommended editor workflow:
- Use
analyzeLineWindow(...)for viewport/diff updates. - Expand a changed range with
expandToParagraphBoundaries(...)(or useanalyzeLineWindowdefault expansion). - Paragraph types are resolved from the actual paragraph start even when a requested window or its context starts mid-paragraph.
- Use
shouldFullAnalyze(changeCount, totalLines)to decide when to fallback to full-documentanalyzeLines(...). - Keep final HTML output on
markdown-it + plugin(not analyzer output).
Guaranteed parity scope:
- marker escape parity (
★,%%) - inline marker pairing in inline mode (
starInlineEnabled/percentInlineEnabled) - line-start checks (
starCommentLine/percentCommentLine) - ruby shorthand range detection outside inline marker-wrapped ranges
- automatic/manual figure-reference range detection outside inline marker-wrapped ranges
With figureReferenceAuto, matching ranges use type: "figure-reference" and
cover the full parenthesized source range. With figureReferenceManual, they
cover the full asterisk-marked range. Rendering wraps only the recognized label
and identifier in either mode.
Out of scope for strict parity:
- full markdown-it token behavior (lists, links, emphasis nesting, fenced blocks)
- HTML block/inline token boundary decisions made by markdown-it and other plugins
- renderer-time wrapper suppression / list-item hiding behavior
Notes
Rule Details
- Escape parity rule:
- Odd number of backslashes before marker: marker is escaped.
- Even number: marker can participate in pairing.
- In inline mode (
html:true/html:false), one preparse pass selects the earliest valid enabled star, percent, or figure-reference candidate. - Markdown inline syntax inside a marker range is kept literal (for example,
★**bold**★,★`code`★,★[link](...)★). - Ruby conversion runs on text tokens; when marker preparse has already wrapped a range, ruby conversion does not rewrite inside that wrapped marker content.
- Figure references emit normal paired inline tokens; figure-only configuration does not install the core conversion rule.
HTML Boundary Behavior
- With
html:true, conversion targets markdown-it inlinetexttokens. - Raw
html_blocktoken bodies are not rewritten by this plugin. - HTML attributes are not rewritten.
- Raw-text elements are skipped:
script,style,textarea,title. - With
html:false, HTML-like input is treated as text and escaped, but marker/ruby transforms still apply. Explicit<ruby>...</ruby>wrappers used with ruby shorthand are preserved.
Block/inline example:
Input: <div>店内に★売り切れ注意の★張り紙が貼ってあります。</div>
Output: <div>店内に★売り切れ注意の★張り紙が貼ってあります。</div>
Input:
<div>
店内に★売り切れ注意の★張り紙が貼ってあります。
</div>
Output:
<div>
<p>店内に<span class="star-comment">★売り切れ注意の★</span>張り紙が貼ってあります。</p>
</div>markdown-it Compatibility
- Ruby and comment conversion run as core transforms and may rewrite
texttokens tohtml_inline; figure references are emitted by inline preparse. - If another plugin expects raw
textonly, run that plugin earlier or supporthtml_inline. - Designed to coexist with
text_join/cjk_breaksby forcing conversion at the tail of core processing. - Install this plugin once per
markdown-itinstance. Use a fresh instance for a different option set.
Install
npm i @peaceroad/markdown-it-renderer-inline-textThis package is ESM ("type": "module").
