# 개념

> 채널, 입력 표기, 옵션, 정규화 보고, 스트리밍 계약 — mdwire 의 모든 API 가 같이 쓰는 것.

HTML: https://mdwire.minjun.dev/ko/docs/

Rust 코어, npm 패키지, Go 이식, CLI 는 같은 입력을 받아 같은 답을 낸다 — 같은
[코퍼스](https://github.com/minjun0219/mdwire/tree/main/corpus/cases)를 통과해야 한다. 이 페이지는 넷이
같이 쓰는 것을 다룬다. 정확한 이름은 언어별 페이지에 있다.

이 문서는 `main` 을 따른다. (다음 릴리스) 표시가 붙은 것은 지금 npm · crates.io · Go 에 올라간 판에는 아직
없고 다음 릴리스에 나간다.

## 채널

출력이 갈 곳. 이름은 어디서나 같은 문자열이다.

| 이름 | 가는 곳 | 한도 |
|---|---|---|
| `telegram-html` | 텔레그램 `parse_mode: "HTML"`. 태그 아홉 개(`b i u s code pre a blockquote tg-spoiler`), 헤딩·표 없음 — 표는 고정폭 블록으로 나간다. | 4,096 |
| `slack-markdown` | 슬랙 `markdown_text`. 표준 마크다운을 슬랙이 직접 변환한다. 남는 일은 정규화와 분할이다. | 12,000 |
| `github-markdown` | GitHub 코멘트·PR 본문(GFM). 글자로 쓴 `~` `<` 는 이스케이프하고, GFM 이 닫지 못하는 굵게(한국어 조사 앞)는 `<strong>` 으로 낸다. | 65,536 |
| `notion-markdown` | 노션 페이지 본문(Notion-flavored Markdown). 헤딩은 네 단계, 노션이 못 그리는 인라인 HTML 은 벗기고 오토링크는 `[url](url)` 로 쓴다. | 65,536 (재지 않음) |
| `plain` | 평문 — 마커를 전부 뺀다. 폴백 경로. | 12,000 |
| `html` | 브라우저에 넣을 HTML 조각. `innerHTML` 로 바로 넣어도 된다 — 글자는 이스케이프하고, 원문의 인라인 태그는 속성을 버리고, 허용한 스킴만 링크가 된다. | 없음 |

한도는 렌더한 출력의 글자 수다. 보내는 쪽이 따로 정할 수 있다([옵션](#옵션)) — `plain` 을 텔레그램에
보내면 4,096 에 맞춰야 한다.

## 입력 표기

에이전트가 무슨 표기로 썼는가. 채널과는 따로 정한다.

| 이름 | 읽는 법 |
|---|---|
| `markdown` | 표준 마크다운(CommonMark · GFM). 기본값. |
| `slack-mrkdwn` | 슬랙 레거시 `mrkdwn`: 별표는 몇 개든 굵게, 물결은 하나든 둘이든 취소선, `<url\|텍스트>` 는 링크. 표준 표기가 섞여도 같은 뜻으로 읽는다. |

슬랙 문서로 배운 에이전트는 `mrkdwn` 으로 쓴다. 표준으로 읽으면 `*굵게*` 가 기울임이 되고 `~취소~` 는
글자로 남는다 — 그러니 입력 표기를 밝혀 준다.

## 옵션

| 옵션 | 기본값 | 뜻 |
|---|---|---|
| from | `markdown` | 입력 표기. |
| limit | 채널의 한도 | 조각당 글자 수. 256 보다 작으면 256 으로 올린다 — 조각마다 마크업을 닫고 다시 열 자리가 있어야 한다. 스트리밍은 나누지 않고, `html` 도 나누지 않는다. |
| html · 줄바꿈 | `br` | 블록 안의 줄바꿈을 `<br>` 로 낸다. `space` 는 공백으로 바꿔 브라우저가 이어 붙이게 둔다(80열에서 줄을 접어 쓴 글). |
| html · 이미지 | `link` | `![alt](url)` 을 링크로 낸다 — 누르기 전엔 아무것도 불러오지 않는다. `load` 는 `<img>` 로 불러온다(허용한 스킴만). |
| html · 스킴 | `http`, `https`, `mailto` | 링크·이미지 주소로 받는 스킴. 목록을 주면 기본값을 바꾼다 — 더하는 것이 아니다. |

`html` 옵션 셋은 `html` 채널에만 쓰인다.

## 정규화 보고

렌더하면서 한 일도 센다. 앞 넷은 **고친 것** — 모델이 제 서식을 얼마나 자주 깨는가:

| 필드 | 세는 것 |
|---|---|
| 닫아 준 강조 | 블록이 끝나도록 안 닫혀서 닫아 준 강조(`**영향 범위`). |
| 닫아 준 펜스 | 문서 끝까지 안 닫혀서 닫아 준 코드펜스. |
| 되돌린 코드 스팬 | 짝이 없어 글자로 되돌린 백틱 런. |
| 버린 마커 | 짝이 없어 버린 `**`(`꼬리**` 처럼 앞이 글자인 것). |

뒤 여섯은 **채널에 맞춰 바꾼 것** — 보내기 전에 채널이 요구해서 바꾼 것:

| 필드 | 세는 것 |
|---|---|
| 이스케이프한 글자 | 채널이 구문으로 읽을 글자를 이스케이프한 수(GitHub 의 `\~` `\<` `\*`). |
| 태그로 낸 강조 | 마커 대신 태그로 낸 강조 — GitHub 의 `<strong>`. |
| 걷어 낸 HTML | 걷어 낸 원문 HTML: 채널이 못 그리는 태그, 주석, 줄바꿈으로 바꾼 `<br>`. |
| 바꿔 쓴 불릿 | 바꿔 쓴 목록 기호(`* ` `• ` → `- `, 텔레그램은 `- ` → `• `, `1)` → `1.`). |
| 다시 쓴 표 | 다시 쓴 표(구분선·칸 공백 정리, 또는 고정폭으로 내림). |
| 바꿔 쓴 마커 | 다른 표기로 바꿔 쓴 강조와 링크 — `mrkdwn` `*굵게*` → `**굵게**`, `<url\|텍스트>` → `[텍스트](url)`. 마크다운을 내는 채널에서만. |

## 스트리밍 계약

스트리머는 출력을 토큰 단위로 받아, 지금 보내도 안전한 만큼을 돌려준다.

- **`push` 가 돌려준 것은 확정이다.** 뒤의 조각이 그것을 고치지 않고, `finish` 는 꼬리만 붙인다.
  그래서 조각을 이어 붙이면 조각 크기와 무관하게 한 번에 렌더한 것과 같다 — 문서가 길어 여러
  조각으로 나뉘는 경우만 빼고.
- **붙드는 것은 꼭 필요한 만큼이다:** 아직 무엇인지 모르는 앞머리, 조각 끝의 마커 런, 아직 안 닫힌
  강조의 안쪽. 문단은 붙들지 않는다.
- **스트리밍은 한도로 나누지 않는다.** 예외 하나: 한도를 넘는 노션 표는 머리글을 되풀이한
  표 여럿으로 내고, 스트리밍도 같게 낸다.

스트리밍 중에 보내는 법은 채널에 따라 둘이다:

| 채널이… | 보낼 것 | 예 |
|---|---|---|
| 메시지를 통째로 다시 쓴다 | 누적본 **+ `preview()`** — 붙든 것을 입력이 여기서 끝난 것처럼 그린 꼬리. `finish` 뒤 `revised()` 가 거짓이면 마지막 편집은 건너뛴다. | 텔레그램 `editMessageText`, 슬랙 `chat.update`, React |
| 덧붙이기만 한다 | `push` 조각을 그대로. `preview()` 는 부르지 않는다. | 슬랙 `appendStream` |

`closeOpen()` 은 `preview()` 보다 엄격하다 — 이미 나간 블록 마크업(`<blockquote>`, `<pre>`)만 닫고,
붙든 것은 그리지 않는다. 둘 다 스트리머의 상태를 바꾸지 않는다.
