-
Notifications
You must be signed in to change notification settings - Fork 0
Canonical Editable HTML Strategy
이 문서는 Bear급 note app을 만들 수 있는 core engine을 목표로 할 때, HTML, Markdown, DOM, JSON model의 책임을 어떻게 나눌지 고정한다.
HTML은 표준이지만 core model의 source of truth로 삼지 않는다.
Core model은 typed JSON document다. HTML은 그 model에서 투영된 canonical editable dialect일 때만 lossless reverse mapping 대상이다.
Typed JSON document model
-> canonical editable HTML
-> contenteditable DOM
-> DOM observation
-> typed operation / JSON Patch
-> Typed JSON document model
외부 HTML은 이 경로에 바로 들어오지 않는다. 외부 HTML은 importer/sanitizer가 best-effort로 typed JSON model로 변환한다.
HTML은 같은 의미를 여러 방식으로 표현할 수 있다.
<strong>text</strong>
<b>text</b>
<span style="font-weight: 700">text</span>
<span class="bold">text</span>이 HTML을 모두 lossless하게 같은 model로 되돌리려면 sanitizer, schema, id registry, selection mapping, command semantics를 HTML 위에 다시 만들어야 한다. 그 순간 HTML이 source of truth가 아니라 HTML 안에 숨긴 별도 document model이 된다.
그래서 core가 보장할 것은 임의의 HTML round-trip이 아니다. Core가 보장할 것은 우리가 렌더한 canonical editable HTML dialect의 round-trip이다.
| Layer | 책임 | Core truth 여부 |
|---|---|---|
| Typed JSON model | block, text surface, range, atom, fragment, operation 의미론 | 예 |
| Canonical editable HTML | contenteditable이 조작할 수 있는 stable projection | 아니오 |
| DOM | native input, IME, selection, clipboard event transport | 아니오 |
| Markdown | authoring shortcut, import/export, interchange format | 아니오 |
Markdown 호환은 목표지만 Markdown editor가 목표는 아니다. Markdown은 typed model로 들어오고 typed model에서 나가는 format이다.
Canonical editable HTML은 제한된 HTML subset과 data-* anchor를 가진다.
<section data-editable-document="note-1">
<h1
data-editable-block="block-title"
data-editable-block-type="heading"
data-editable-heading-level="1"
data-json-text="/blocks/0/text"
>
Hello <strong data-editable-mark="bold">world</strong>
<span
data-json-atom="tag-1"
data-editable-atom-type="tag"
contenteditable="false"
>#tag</span>
</h1>
</section>필수 anchor:
| Anchor | 의미 |
|---|---|
data-editable-document |
document identity |
data-editable-block |
stable block id |
data-editable-block-type |
block variant |
data-json-text |
json-document text path |
data-json-atom |
atom id |
data-editable-atom-type |
atom variant |
data-editable-mark |
inline range/mark projection |
data-json-text와 data-json-atom은 현재 contenteditable-web package의 public contract와 맞춘다.
Reverse mapping은 두 종류로 나뉜다.
Lossless reverse mapping 대상이다.
- block은
data-editable-block으로 model block id에 연결한다. - text surface는
data-json-text로 model string path에 연결한다. - atom은
data-json-atom을 만나면 model text에서\uFFFC한 글자로 계산한다. - mark는 canonical tag와
data-editable-mark를 range sidecar로 환원한다. - DOM selection은 text surface offset으로 환원한다.
Importer/sanitizer 대상이다.
- semantic tag는 가능한 한 typed block/range/atom으로 normalize한다.
- unknown style/class/span은 기본적으로 버린다.
- unsupported structure는 plain text나 fallback block으로 낮춘다.
- lossless 보장을 하지 않는다.
Bear급 note app을 만들 수 있으려면 core model에는 기능이 아니라 공통 표현 단위가 있어야 한다.
-
Stable identity
- document id
- block id
- atom id
- range id
-
Block model
- paragraph
- heading level 1~6
- list item: bullet, ordered, task
- quote
- code block
- extension block slot
-
Text surface
- text path
- atoms sidecar path
- ranges sidecar path
- atom은 한 글자 offset으로 계산
-
Inline range
- bold
- italic
- underline
- strike
- code
- highlight
- link with payload
-
Atom
- mention
- tag
- wiki link
- attachment/image placeholder
- model text에서는
\uFFFC
-
Fragment
- selected text
- selected atoms
- selected ranges
- selected blocks
- plain text / HTML / Markdown fallback 가능
-
Structural operation
- split block
- merge block
- set/toggle block type
- wrap/unwrap list
- indent/outdent
- toggle range
- insert/remove atom
- paste fragment
- 모든 operation은
selectionAfter를 계산한다.
다음은 Bear급 앱에는 필요하지만 core model의 불변부가 아니다.
- toolbar
- theme
- Markdown parser policy
- Markdown shortcut keymap
- search index
- backlink index
- file storage
- sync
- encryption
- export UI
- visual line geometry
이들은 typed model 위의 package, adapter, host app에서 조립한다.
json-document 본체는 JSON editing foundation이다. Rich note model은 본체에 넣지 않고 공식 extension package로 둔다.
후보 이름:
@interactive-os/json-document-rich-text
이 package는 DOM을 요구하지 않는다. DOM과 contenteditable은 contenteditable-web adapter가 맡는다.
json-document
-> rich-text model/operations
-> canonical editable HTML codec
-> contenteditable-web adapter
-> host app
첫 안정화 목표는 다음 round-trip이다.
typed document
-> canonical editable HTML
-> DOM observation
-> typed document patch
-> same typed document semantics
동일해야 하는 것은 HTML string이 아니다. 동일해야 하는 것은 block id, text, atom offset, range start/end, selection 의미론이다.
이 기준을 만족하지 못하면 DOM bridge는 다시 임의 DOM 해석기가 되고, legacy editor와 같은 실패 경로로 돌아간다.