문서 편집기
긴 문서 · 회의록 · 정책을 읽기 화면과 같은 글꼴 · 같은 폭으로 씁니다. 문서 언어(lang)의 명조, 읽기 폭 680(영어 612). 카드 면 위에 붙는 고정 툴바, 제목 H2–H4 · 작업 목록 · 표 · 이미지 · 링크 · 슬래시 메뉴.
- import
- import { DocumentEditor } from "@uyoolabs/ui/editor/document"; // 지연 로드
- 쓰는 경우
- 제목과 표가 필요한 긴 글. 저장 상태(저장됨 · 저장하는 중 · 오프라인 · 실패)를 늘 보입니다. 가져온 문서는 normalizeMarkdown 으로 한 번 고쳐 두면 첫 편집의 diff 가 사람이 고친 줄만 남습니다.
- 쓰지 않는 경우
- 댓글 · 메시지는 ./editor 의 RichTextEditor 입니다. 툴바를 뜬 판 · 반투명으로 바꾸지 않습니다. 원격 커서를 상태 색(초록 · 빨강)으로 칠하지 않습니다(사람은 아이덴티티 색). 오프라인이 됐다고 편집을 막지 않습니다.
- 진입점
- ./editor/document(표 · 이미지 확장은 여기만) · ./editor/collab(yjs · @tiptap/extension-collaboration(+caret) · y-protocols)
document
⌘K 링크 · 빈 줄 처음의 / 로 블록 넣기 · 이미지를 붙여 넣거나 끌어 놓기.
## 환불 정책 결제가 실패하면 앱 알림과 이메일로 바로 알립니다. | 수단 | 기한 | | :--- | ---: | | 카드 | 3일 | | 계좌 | 5일 | - [x] 테스트 카드로 재현 - [ ] 알림 문구 확정
동시 편집
collaboration={{ doc, provider, user }} 를 넘기면 켜집니다. 툴바 끝에 참여자 묶음(누르면 따라가기 · 그 자리로 가기), 본문에 아이덴티티 색 원격 커서 · 선택. 오프라인에서도 고치고, 다시 연결되면 합칩니다.
마크다운 계약
document 프리셋이 더하는 것(ADR 0007 · 스파이크 0001 W5 후속)
| 블록 | 마크다운 | 메모 |
|---|---|---|
| 제목 | ## · ### · #### | # 은 H2 로 읽고 냅니다. 문서의 h1 은 제목 칸 하나입니다(W1 결정 #40) |
| 작업 목록 | - [ ] · - [x] | 이어진 줄은 같은 항목의 줄바꿈으로 읽고, 들여쓰기를 그대로 냅니다 |
| 표 | GFM 표 | 고치지 않은 표는 원문 그대로 냅니다. 고친 표는 정렬(:--)을 지키고 칸 너비를 맞추지 않습니다 |
| 이미지 |  | 올리는 중 · 실패한 이미지는 저장 값에 넣지 않습니다. 대체 텍스트가 없으면 주의 칩 |
| & · 밑줄 | R&D · snake_case | & · 낱말 안 \_ 로 바꾸지 않습니다(화면이 같은 과잉 이스케이프) |
| 동시 편집 | — | Y 문서가 정본입니다. 마크다운은 저장 시점(1초 · 칸을 떠날 때 · 닫을 때)에 onSave 로 냅니다 |
키보드
| 키 | 동작 |
|---|---|
| ⌘B⌘I⌘⇧X⌘E | 굵게 · 기울임 · 취소선 · 인라인 코드 |
| ⌘K | 링크 넣기 · 고치기 |
| ⌘⌥0⌘⌥2⌘⌥3⌘⌥4 | 본문 · 제목 2 · 3 · 4 |
| ⌘⇧7⌘⇧8⌘⇧9 | 번호 · 글머리 · 작업 목록 |
| / | 빈 줄 처음에서 슬래시 메뉴. ↑ ↓ · Enter, Esc 는 메뉴만 닫습니다 |
| Tab⇧Tab | 목록 들여쓰기 · 표 다음 · 이전 칸 |
| AltF10 | 툴바로 갑니다. ← → 로 옮기고 Esc 로 본문에 돌아옵니다 |
| Esc | 동시 편집에서 따라가기를 끝냅니다. 타자를 쳐도 끝납니다 |
| Enter | 모든 Enter 처리는 isComposing 가드를 거칩니다(조합 중이면 확정만) |
명령(onReady)
ref 가 아니라 콜백으로 받습니다(React 19, forwardRef 없음).
- insertMarkdown(md, { source })
- 커서 자리에 넣습니다. 되돌리기 한 번에 사라집니다
- replaceSelection(md, { source })
- 고른 글을 바꿉니다. 문단 하나짜리는 글자로 끼웁니다
- source: "ai"
- onAiAssist 를 부르고 툴바 끝에 ‘AI 도움’ 기록을 남깁니다(결정 #41)
- getMarkdown()
- 기다림(changeDebounceMs)을 무시한 지금 값
DocumentEditor props
소스의 타입과 JSDoc 에서 생성한 표입니다. 컴포넌트를 고치면 pnpm gen 으로 다시 만듭니다.
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| value | string | — | 제어 값(마크다운) |
| defaultValue | string | — | 비제어 첫 값(마크다운) |
| onChange | (markdown: string) => void | — | 직렬화는 changeDebounceMs 뒤에 한다. 칸을 떠나거나 사라질 때는 바로 낸다 |
| changeDebounceMs | number | 400 | 큰 문서는 직렬화가 무거워 기다렸다 낸다(ms). |
| lang | "ko" | "en" | "ja" | "ko" | 문서 언어. 명조 · 줄 높이 · 읽기 폭이 따른다. UI 문구 언어는 labelsLang. |
| labelsLang | "ko" | "en" | "ja" | — | UI 문구 언어. 없으면 lang |
| placeholder | string | — | 빈 줄 안내. 없으면 labels.placeholderEmpty |
| toolbar | "sticky" | "none" | "sticky" | sticky(z 10) 는 위에 붙는 고정 툴바. none 이면 툴바 없이 단축키 · 슬래시 메뉴만. |
| toolbarEnd | ReactNode | — | 툴바 끝에 둘 앱 동작(참여자 · 공유) |
| readOnly | boolean | — | 보기 권한만 있을 때. 툴바 대신 안내 한 줄 |
| onRequestEdit | () => void | — | 읽기 전용 안내의 [편집 권한 요청] |
| saveStatus | "saved" | "saving" | "offline" | "failed" | — | 툴바 끝의 저장 상태. 없으면 그리지 않는다 |
| onUploadImage | ImageUploader | — | 이미지 붙여 넣기 · 끌어 놓기 · 툴바 · 슬래시. 없으면 이미지 키가 없다 |
| maxImageBytes | number | 10485760 | 이미지 크기 상한(바이트). |
| mentions | (query: string) => MentionItem[] | Promise<MentionItem[]> | — | |
| onReady | (commands: EditorCommands) => void | — | 편집기가 준비되면 명령을 넘긴다(인용 넣기 · AI 삽입) |
| aiAssisted | boolean | — | AI 로 넣은 내용이 있다(문서 정보에 남은 기록). 툴바 끝에 'AI 도움' 표시 |
| onAiAssist | (event: { mode: "insert" | "replace"; markdown: string }) => void | — | source "ai" 로 넣거나 바꿨을 때. 앱이 문서 정보에 기록한다(W2~W5 결정 #41) |
| autoFocus | boolean | — | |
| labels | Partial<DocumentEditorLabels> | — | |
| className | string | — |
CollaborativeEditor props
소스의 타입과 JSDoc 에서 생성한 표입니다. 컴포넌트를 고치면 pnpm gen 으로 다시 만듭니다.
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| lang | "ko" | "en" | "ja" | "ko" | 문서 언어. 명조 · 줄 높이 · 읽기 폭이 따른다. UI 문구 언어는 labelsLang. |
| labelsLang | "ko" | "en" | "ja" | — | UI 문구 언어. 없으면 lang |
| placeholder | string | — | 빈 줄 안내. 없으면 labels.placeholderEmpty |
| toolbar | "sticky" | "none" | "sticky" | sticky(z 10) 는 위에 붙는 고정 툴바. none 이면 툴바 없이 단축키 · 슬래시 메뉴만. |
| toolbarEnd | ReactNode | — | 툴바 끝에 둘 앱 동작(참여자 · 공유) |
| readOnly | boolean | — | 보기 권한만 있을 때. 툴바 대신 안내 한 줄 |
| onRequestEdit | () => void | — | 읽기 전용 안내의 [편집 권한 요청] |
| saveStatus | "saved" | "saving" | "offline" | "failed" | — | 툴바 끝의 저장 상태. 없으면 그리지 않는다 |
| onUploadImage | ImageUploader | — | 이미지 붙여 넣기 · 끌어 놓기 · 툴바 · 슬래시. 없으면 이미지 키가 없다 |
| maxImageBytes | number | 10485760 | 이미지 크기 상한(바이트). |
| mentions | (query: string) => MentionItem[] | Promise<MentionItem[]> | — | |
| onReady | (commands: EditorCommands) => void | — | 편집기가 준비되면 명령을 넘긴다(인용 넣기 · AI 삽입) |
| aiAssisted | boolean | — | AI 로 넣은 내용이 있다(문서 정보에 남은 기록). 툴바 끝에 'AI 도움' 표시 |
| onAiAssist | (event: { mode: "insert" | "replace"; markdown: string }) => void | — | source "ai" 로 넣거나 바꿨을 때. 앱이 문서 정보에 기록한다(W2~W5 결정 #41) |
| autoFocus | boolean | — | |
| labels | Partial<DocumentEditorLabels> | — | |
| className | string | — | |
| collaboration필수 | CollaborationOptions | — | |
| onSave | (markdown: string) => void | — | 저장 시점의 마크다운. 직렬화 비용 때문에 트랜잭션마다 부르지 않는다(saveDebounceMs · 칸을 떠날 때 · 닫을 때) |
| saveDebounceMs | number | 1000 | 기본 1000 |
| connection | "connected" | "connecting" | "offline" | — | 연결 상태. 기본은 공급자의 status 로 계산한다 |
| following | string | null | — | 따라가는 사람 id. 주면 제어 |
| onFollowingChange | (id: string | null) => void | — | |
| showParticipants | boolean | true | 참여자 묶음을 툴바 끝에 그린다. false 면 앱이 <Participants> 를 따로 둔다 |
| idleAfterMs | number | 3000 | 커서가 이만큼(ms) 움직이지 않으면 깃발을 접고 목록에 '자리 비움'. 기본 3000 |
| collabLabels | Partial<CollabLabels> | — |
Participants props
참여자 묶음을 툴바 밖에 둘 때(showParticipants={false}).
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| users필수 | Participant[] | — | |
| selfId필수 | string | — | 나. 묶음에서 맨 앞에 세지 않고 목록에 (나) 로 적는다 |
| max | number | 3 | 보이는 아바타 수. 넘치면 +N |
| following | string | null | — | |
| onFollow | (id: string) => void | — | |
| onJumpTo | (id: string) => void | — | |
| lang | "ko" | "en" | "ja" | "ko" | |
| labels | Partial<CollabLabels> | — | |
| className | string | — |