Editor
블록 카탈로그, JSON Patch 스트리밍, 직접 편집과 diff 검토, Markdown 변환, 커스텀 블록을 지원하는 AI 중심 구조화 콘텐츠 에디터입니다.
reopt design업데이트
블록 시스템, 스트리밍 프로토콜, Markdown 변환, AI 백엔드 연결과 커스텀 블록 확장을 담당합니다.
사용 사례와 선택 기준
AI가 초안을 쓰고 사람이 고치는 노트, 보고서, 콘텐츠 제작 도구에 사용합니다. 같은 문서를 edit·stream·diff 모드로 다루고, 블록 카탈로그로 허용할 콘텐츠 구조를 정합니다.
대화 메시지를 보여주는 제품이면 opt-chat을, 저장 가능한 구조화 문서를 편집한다면 opt-editor를 선택하세요. 커스텀 블록을 추가할 때는 렌더링과 스키마를 함께 정의하고 AI 변경은 검토 흐름에 연결하세요.
직접 실행해 볼 예제
스토어·카탈로그·스타일 설정 →1. 개요
@reopt-ai/opt-editor는 AI 에이전트가 스트림으로 콘텐츠를 생성하는 시나리오에 최적화된 에디터입니다. PlateJS/Slate 대비 핵심 차별점:
- Flat element map — 스트리밍 중 인덱스 시프트 없음. 트리 대신
Record<string, EditorElement>구조 - RFC 6902 NDJSON — 산업 표준 JSON Patch를 한 줄씩 스트리밍. 결정론적 적용
- 스키마 기반 AI 프롬프트 —
catalog.prompt()로 블록 스키마 + 스트리밍 규칙 자동 생성 - Runtime boundary — 런타임 peer는 React/React DOM이고, AI SDK·Zod는
./ai-sdk헬퍼를 쓸 때만 필요한 optional peer입니다.
2. 설치
bun add @reopt-ai/opt-editor앱의 전역 스타일 엔트리에서 패키지 CSS를 한 번 import합니다.
import "@reopt-ai/opt-editor/styles.css";3. 빠른 시작
AI 스트림을 에디터에 연결하는 최소 코드:
import { useMemo } from "react";
import {
Editor,
defaultCatalog,
createEditorStore,
useEditorStream,
} from "@reopt-ai/opt-editor";
import "@reopt-ai/opt-editor/styles.css";
function AIEditor() {
const store = useMemo(() => createEditorStore(), []);
const { start, status, appliedCount } = useEditorStream(store);
const handleGenerate = async () => {
const response = await fetch("/api/ai/generate", {
method: "POST",
body: JSON.stringify({ prompt: "Write an article about..." }),
});
// AI 응답을 NDJSON 스트림으로 에디터에 주입
await start(response.body!.pipeThrough(new TextDecoderStream()));
};
return (
<div>
<button onClick={handleGenerate} disabled={status === "streaming"}>
{status === "streaming"
? `생성 중... (${appliedCount} patches)`
: "AI 생성"}
</button>
<Editor store={store} catalog={defaultCatalog} mode="stream" />
</div>
);
}4. Stream / Edit / Diff 모드
에디터는 세 가지 모드를 지원합니다:
| 모드 | 용도 | 특징 |
|---|---|---|
| stream | AI 생성 콘텐츠 표시 | 읽기 전용, MarkedText 렌더링, syntax highlighting |
| edit | 사용자 직접 편집 | contentEditable, split/merge, slash commands |
| diff | AI 제안 검수 | 읽기 전용 리뷰, 변경 블록 강조/배지/hover diff, 블록 단위 승인·거절 |
5. 블록 타입 (15개)
기본 카탈로그에 포함된 블록 타입입니다. 각 블록의 Props와 사용법은 블록 타입 레퍼런스를 참고하세요.
| 카테고리 | 블록 |
|---|---|
| 텍스트 | paragraph, heading (h1-h6), quote, callout |
| 구조 | list / list-item, table / table-row / table-cell |
| 미디어 | image, video, file, embed |
| 기타 | code, divider |
6. 인라인 서식
텍스트 props는 마크다운 인라인 서식을 지원합니다. AI가 생성한 마크다운이 그대로 렌더링됩니다.
| 서식 | 문법 | HTML |
|---|---|---|
| Bold | **text** | <strong> |
| Italic | *text* | <em> |
| Code | `text` | <code> |
| Strikethrough | ~~text~~ | <s> |
| Link | [text](url) | <a> |
7. 커스텀 블록 확장
defineCatalog()로 커스텀 블록을 추가하면, AI 프롬프트와 JSON Schema에 자동 반영됩니다.
import {
MarkedText,
defineCatalog,
defaultCatalog,
type BlockDefinition,
type BlockRenderProps,
} from "@reopt-ai/opt-editor";
type AlertAttrs = {
severity?: string;
};
function AlertBlock({
attrs,
content,
editableContent,
}: BlockRenderProps<AlertAttrs>) {
return (
<aside data-alert-severity={attrs.severity ?? "info"}>
{editableContent?.({
as: "div",
placeholder: "Alert message",
}) ?? <MarkedText content={content} />}
</aside>
);
}
const alertDefinition: BlockDefinition<AlertAttrs> = {
type: "alert",
attrsSchema: {
severity: { type: "string", default: "info" },
},
contentKind: "rich-text",
component: AlertBlock,
prompt: "An alert box with severity (info/warning/error) and message.",
};
const customCatalog = defineCatalog({
...defaultCatalog.blocks,
alert: alertDefinition,
});
// catalog.prompt() 에 alert 블록이 자동 포함됨
// catalog.jsonSchema() 에도 alert 스키마가 포함됨8. Syntax Highlighting
코드 블록에 하이라이팅을 적용하려면 highlightCode prop을 전달합니다. shiki, hljs, prism 등 아무 라이브러리나 사용 가능합니다.
import { codeToHtml } from "shiki";
const highlighter = (code: string, lang: string) =>
codeToHtml(code, { lang, theme: "github-dark" });
<Editor
store={store}
catalog={defaultCatalog}
mode="stream"
highlightCode={highlighter}
/>9. 하위 문서
- 블록 타입 레퍼런스 — 15개 블록의 Props, 중첩 구조, 렌더링 방식
- 스트리밍 프로토콜 — NDJSON 패치, StreamCompiler, 재시도/이어쓰기
- Markdown 변환 — specToMarkdown, markdownToSpec
- Skills — 설치, 진단, Plate 마이그레이션, 업그레이드, release workflow
- Playground — 라이브 데모