unbroken-markdown
v0.3.0
Published
Fix broken markdown formatting by moving punctuation outside bold/italic text and removing incomplete image markdown during streaming
Maintainers
Readme
unbroken-markdown
Fix broken markdown during streaming to ensure proper rendering. This package intelligently moves punctuation marks outside of bold/italic markers and handles incomplete markdown patterns during streaming.
What it does
When markdown is streamed or improperly formatted, you might see broken rendering like:
**"text"**→"**text**"(quotes outside bold)*text(info)*→*text*(info)(parentheses outside italic)**50%**→**50**%(percentage outside bold)**<text>**→<**text**>(angle brackets outside bold)**『책 제목』**→『**책 제목**』(Korean quotation marks)- Incomplete image markdown removal during streaming
Installation
npm install unbroken-markdown
# or
yarn add unbroken-markdown
# or
bun add unbroken-markdownUsage
import { unbreak } from 'unbroken-markdown';
const input = '**"Hello (world)"**';
const output = unbreak(input);
console.log(output); // "**Hello** (world)"
// Works with italic too
const italicInput = '*text(info)*';
const italicOutput = unbreak(italicInput);
console.log(italicOutput); // *text*(info)Features
Bold Pattern Fixes
- Quotes:
**"text"**→"**text**" - Parentheses:
**text(info)**→**text**(info) - Percentages:
**50%**→**50**% - Links:
**[text](url)**→[**text**](url) - Angle brackets:
**<text>**→<**text**> - Korean brackets:
**『text』**→『**text**』,**「text」**→「**text**」,**《text》**→《**text**》,**〈text〉**→〈**text**〉 - CJK brackets:
**【text】**→【**text**】,**〔text〕**→〔**text**〕,**(text)**→(**text**)
CJK Flanking Fixes
CommonMark's right-flanking rule rejects a closing delimiter that is preceded by punctuation and directly followed by a letter. In CJK text the next clause or particle attaches with no space, so patterns like these fail to render entirely. When (and only when) a letter or number follows the closing delimiter directly, trailing punctuation is moved outside:
- ASCII:
**제목:**내용→**제목**:내용,**대박!**이라고→**대박**!이라고(also.,;~) - Full-width:
**質問?**に→**質問**?に,**文章。**次→**文章**。次(also!、,:;….~) - Full-width parenthetical:
**텍스트(설명)**뒤에→**텍스트**(설명)뒤에 - GFM strikethrough:
~~취소!~~라고→~~취소~~!라고
Patterns that already render — **Note:** text (space after), **文章。** (end of input) — are left untouched.
Italic Pattern Fixes
- Quotes:
*"text"*→"*text*" - Parentheses:
*text(info)*→*text*(info) - Percentages:
*50%*→*50*% - Links:
*[text](url)*→[*text*](url) - Angle brackets:
*<text>*→<*text*> - Korean brackets:
*『text』*→『*text*』,*「text」*→「*text*」,*《text》*→《*text*》,*〈text〉*→〈*text*〉
Streaming Support
With streaming: true:
- Removes incomplete image markdown patterns (e.g.
 - Handles partial markdown during real-time streaming
- Ensures consistent rendering even with interrupted markdown
// While streaming, run intermediate chunks with streaming: true
const partial = unbreak(accumulatedText, { streaming: true });
// Run the final, complete document without it so legitimate
// trailing "!" or "