본문으로 건너뛰기

문서 편집기

긴 문서 · 회의록 · 정책을 읽기 화면과 같은 글꼴 · 같은 폭으로 씁니다. 문서 언어(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 표고치지 않은 표는 원문 그대로 냅니다. 고친 표는 정렬(:--)을 지키고 칸 너비를 맞추지 않습니다
이미지![대체 텍스트](src)올리는 중 · 실패한 이미지는 저장 값에 넣지 않습니다. 대체 텍스트가 없으면 주의 칩
& · 밑줄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타입기본값설명
valuestring—제어 값(마크다운)
defaultValuestring—비제어 첫 값(마크다운)
onChange(markdown: string) => void—직렬화는 changeDebounceMs 뒤에 한다. 칸을 떠나거나 사라질 때는 바로 낸다
changeDebounceMsnumber400큰 문서는 직렬화가 무거워 기다렸다 낸다(ms).
lang"ko" | "en" | "ja""ko"문서 언어. 명조 · 줄 높이 · 읽기 폭이 따른다. UI 문구 언어는 labelsLang.
labelsLang"ko" | "en" | "ja"—UI 문구 언어. 없으면 lang
placeholderstring—빈 줄 안내. 없으면 labels.placeholderEmpty
toolbar"sticky" | "none""sticky"sticky(z 10) 는 위에 붙는 고정 툴바. none 이면 툴바 없이 단축키 · 슬래시 메뉴만.
toolbarEndReactNode—툴바 끝에 둘 앱 동작(참여자 · 공유)
readOnlyboolean—보기 권한만 있을 때. 툴바 대신 안내 한 줄
onRequestEdit() => void—읽기 전용 안내의 [편집 권한 요청]
saveStatus"saved" | "saving" | "offline" | "failed"—툴바 끝의 저장 상태. 없으면 그리지 않는다
onUploadImageImageUploader—이미지 붙여 넣기 · 끌어 놓기 · 툴바 · 슬래시. 없으면 이미지 키가 없다
maxImageBytesnumber10485760이미지 크기 상한(바이트).
mentions(query: string) => MentionItem[] | Promise<MentionItem[]>—
onReady(commands: EditorCommands) => void—편집기가 준비되면 명령을 넘긴다(인용 넣기 · AI 삽입)
aiAssistedboolean—AI 로 넣은 내용이 있다(문서 정보에 남은 기록). 툴바 끝에 'AI 도움' 표시
onAiAssist(event: { mode: "insert" | "replace"; markdown: string }) => void—source "ai" 로 넣거나 바꿨을 때. 앱이 문서 정보에 기록한다(W2~W5 결정 #41)
autoFocusboolean—
labelsPartial<DocumentEditorLabels>—
classNamestring—

CollaborativeEditor props

소스의 타입과 JSDoc 에서 생성한 표입니다. 컴포넌트를 고치면 pnpm gen 으로 다시 만듭니다.

prop타입기본값설명
lang"ko" | "en" | "ja""ko"문서 언어. 명조 · 줄 높이 · 읽기 폭이 따른다. UI 문구 언어는 labelsLang.
labelsLang"ko" | "en" | "ja"—UI 문구 언어. 없으면 lang
placeholderstring—빈 줄 안내. 없으면 labels.placeholderEmpty
toolbar"sticky" | "none""sticky"sticky(z 10) 는 위에 붙는 고정 툴바. none 이면 툴바 없이 단축키 · 슬래시 메뉴만.
toolbarEndReactNode—툴바 끝에 둘 앱 동작(참여자 · 공유)
readOnlyboolean—보기 권한만 있을 때. 툴바 대신 안내 한 줄
onRequestEdit() => void—읽기 전용 안내의 [편집 권한 요청]
saveStatus"saved" | "saving" | "offline" | "failed"—툴바 끝의 저장 상태. 없으면 그리지 않는다
onUploadImageImageUploader—이미지 붙여 넣기 · 끌어 놓기 · 툴바 · 슬래시. 없으면 이미지 키가 없다
maxImageBytesnumber10485760이미지 크기 상한(바이트).
mentions(query: string) => MentionItem[] | Promise<MentionItem[]>—
onReady(commands: EditorCommands) => void—편집기가 준비되면 명령을 넘긴다(인용 넣기 · AI 삽입)
aiAssistedboolean—AI 로 넣은 내용이 있다(문서 정보에 남은 기록). 툴바 끝에 'AI 도움' 표시
onAiAssist(event: { mode: "insert" | "replace"; markdown: string }) => void—source "ai" 로 넣거나 바꿨을 때. 앱이 문서 정보에 기록한다(W2~W5 결정 #41)
autoFocusboolean—
labelsPartial<DocumentEditorLabels>—
classNamestring—
collaboration필수CollaborationOptions—
onSave(markdown: string) => void—저장 시점의 마크다운. 직렬화 비용 때문에 트랜잭션마다 부르지 않는다(saveDebounceMs · 칸을 떠날 때 · 닫을 때)
saveDebounceMsnumber1000기본 1000
connection"connected" | "connecting" | "offline"—연결 상태. 기본은 공급자의 status 로 계산한다
followingstring | null—따라가는 사람 id. 주면 제어
onFollowingChange(id: string | null) => void—
showParticipantsbooleantrue참여자 묶음을 툴바 끝에 그린다. false 면 앱이 <Participants> 를 따로 둔다
idleAfterMsnumber3000커서가 이만큼(ms) 움직이지 않으면 깃발을 접고 목록에 '자리 비움'. 기본 3000
collabLabelsPartial<CollabLabels>—

Participants props

참여자 묶음을 툴바 밖에 둘 때(showParticipants={false}).

prop타입기본값설명
users필수Participant[]—
selfId필수string—나. 묶음에서 맨 앞에 세지 않고 목록에 (나) 로 적는다
maxnumber3보이는 아바타 수. 넘치면 +N
followingstring | null—
onFollow(id: string) => void—
onJumpTo(id: string) => void—
lang"ko" | "en" | "ja""ko"
labelsPartial<CollabLabels>—
classNamestring—