"decoration 엔진이 세 역할을 한다는 건 덤이 아니야. CodeMirror 를 고른 이유 그 자체지."
세 기능, 하나의 원시 요소
한 발 물러나서 세 역할을 나란히 놓고 봐. Live Preview 는 범위를 계산해서 mark 를 붙여(토큰을 가리는 replace decoration 도 같이). voice underline 은 범위를 계산해서 mark 와 호버 노트를 붙이고. CMD+K 는 제안을 담은 widget 을 붙였다가 수락할 때 문서를 바꿔. 겉보기엔 아무 상관 없는 세 기능, 그러니까 렌더러와 linter 와 AI 에디터가 같은 모양으로 정리되는 거야. 소스 위의 범위를 계산하고, decoration 을 붙이고, 사용자가 확정할 때만 문서를 바꾼다. 원시 요소 하나를 한 번 익히면 soul 레이어 전체가 그 위에서 굴러가.
Live Preview 범위 -> mark + replace (모습 렌더)
voice underline 범위 -> mark + 호버 노트 (voice 이슈 표시)
CMD+K widget 제안 -> 수락 편집 하나 (AI 재작성 무대)
# 입력만 다르고 decoration 원시 요소는 같아. 그게 이 선택의 승리야.
rich-text 모델은 어디서 갈라지나
이제 같은 세 기능을 rich-text 트리 위에 짓는다고 상상해봐. ProseMirror 위의 Tiptap 이든 Lexical 이든. 먼저 공정하게 보자. ProseMirror 에도 decoration 이 있어. 범위에 스타일을 입히는 inline decoration, UI 를 끼워 넣는 widget decoration, 둘 다 문서를 안 바꿔. 그러니 voice 밑줄과 수락 전 CMD+K 제안은 거기서도 원시 요소 하나를 나눠 쓸 수 있어. 갈라지는 건 밑에 깔린 모델 때문이야. 트리 위에선 Live Preview 라는 개념 자체가 성립을 안 해. 트리가 이미 렌더된 형태라서 커서 줄에 드러낼 마크다운 소스가 남아 있지 않거든. 그래서 '소스를 렌더된 모습으로 보여준다' 는 발상이 모델과 부딪혀. 그리고 나머지 두 기능이 계산하는 위치는 전부 트리 위치라서, 실제로 저장하는 마크다운 파일 쪽으로 다시 번역해야 해. 세 역할 중 둘은 맞아. 그런데 Rekindle 을 Rekindle 답게 만든 그 역할이 안 맞고, 나머지도 전부 번역 세금을 내.
재사용이 곧 아키텍처
그래서 '메커니즘 하나에 역할 셋' 은 운 좋은 우연이 아니라 하중을 떠받치는 결정이야. CodeMirror 를 고른 건 소스 위에 decoration 을 얹는 그 원시 요소가 세 soul 기능을 전부 같은 모양으로 만들어주기 때문이거든. 라이브 voice 도 inline AI 도 decoration 이 실은 UI 도, soul 레이어 전체가 한 번 짓고 한 번 디버깅한 메커니즘 하나 위에 올라타. (margin-Pippa 자신은 bind 를 타지. 그건 트랙 6 이야.) 이 가족의 첫 번째 pillar 인 Reuse 를 에디터 코어 선택으로 표현하면 이렇게 되는 거야. 기능이 제일 많은 substrate 를 고르지 마. 네 기능들이 이미 공유하고 있는 원시 요소를 가진 쪽을 골라. 재사용이 이 정도로 깊어지면 그건 더 이상 최적화가 아니라 아키텍처야.