Skip to content

API 레퍼런스

<newtil-editor> 웹 컴포넌트의 전체 API. 정본은 src/index.ts 이다.

속성 (Attributes)

HTML 속성으로 설정한다. setAttribute / removeAttribute 로 동적으로 바꿀 수 있다.

속성기본설명
value마크다운 문자열""초기 내용. 바꾸면 내용이 교체된다. 편집기가 마지막에 낸 markdown/html 과 같은 값이면 무시한다(순환 방지)
placeholder문자열빈 편집기의 안내 문구. 마크다운 모드의 textarea 에도 같이 붙는다
readonly(존재 여부)있으면 읽기 전용. 마크다운 모드의 textarea 도 읽기 전용
toolbar(존재 여부)있으면 상단 툴바. 붙이고 떼면 즉시 반영
floating-toolbar"false"(켜짐)"false" 면 글자를 골랐을 때 뜨는 플로팅 툴바를 숨긴다
mode"wysiwyg" | "source"wysiwyg마크다운 원문 편집 모드로 시작. 프로퍼티 mode 로 바꾸면 속성도 따라간다 (0.6.0)
langko | en (ko-KR 같은 태그도 됨)조상 lang<html lang>en툴팁·메뉴·안내 문자열 언어. 바꾸면 내용과 모드를 보존한 채 다시 마운트한다(되돌리기 이력 초기화) (0.10.2)
empty-line-hint문자열messages.emptyLineHint빈 문단에서 Enter 를 눌렀을 때의 안내. 빈 문자열이면 끈다 (0.10.0)

readonly·toolbar 는 값이 아니라 존재 여부로 판단한다. <newtil-editor toolbar> 가 켜기, 속성 제거가 끄기.

프로퍼티 (Properties)

프로퍼티타입R/W설명
markdownstringR/W현재 내용의 마크다운. 마크다운 모드에서는 textarea 원문 그대로
valuestringR/Wmarkdown 의 별칭
htmlstringR현재 내용의 HTML. 마크다운 모드에서는 원문을 파싱해 만든다
modeEditorModeR/W"wysiwyg" | "source". 바꾸는 순간에만 한 번 변환한다. 되돌리기 이력은 초기화
messagesMessagesR/W읽으면 지금 쓰는 문자열 표(언어 + 덮어쓴 것). 쓰면 일부만 덮어쓴다 — editor.messages = { emptyLineHint: "…" }. 툴바·메뉴 문구는 다시 마운트해 반영 (0.10.2)
onImageUploadImageUploadHandlerR/W이미지 업로드 훅. 파일 선택·붙여넣기·끌어놓기가 모두 이 훅을 탄다 (0.10.8)
onDrawingSaveDrawingSaveHandlerR/W그림판 저장 훅 — 구운 PNG 와 도형 데이터를 받아 문서에 넣을 주소를 준다. 없으면 data URL (0.10.7)
onDrawingLoadDrawingLoadHandlerR/W그림판 불러오기 훅 — 이미지 주소의 도형 데이터(있으면). 없으면 그 그림을 배경으로 새로 그린다
selectionActionsSelectionAction[]R/W선택 범위 동작 — 호스트가 정한 동작을 말풍선 툴바·슬래시 메뉴에 붙인다. 비어 있으면(기본) 버튼도 없다 (0.10.17)

onImageUpload

js
editor.onImageUpload = async (file) => {
  const form = new FormData(); form.append("file", file);
  const res = await fetch("/api/upload", { method: "POST", body: form });
  return (await res.json()).url;
};
  • 훅 있음 → 업로드 후 반환 URL 로 이미지 삽입. 훅이 던지면 data URL 로 대체
  • 훅 없음 → data URL(base64) 로 본문에 들어간다
  • 파일 선택을 취소하면 URL 을 묻는다(messages.imageUrlPrompt, altPrompt)

onDrawingSave / onDrawingLoad

ts
editor.onDrawingSave = async ({ png, data, previousSrc }) => {
  // png: Blob, data: DrawingData(도형 JSON), previousSrc: 고치던 그림의 주소 | null
  return "/upload/drawing-1.png";       // 문서에 넣을 주소
};
editor.onDrawingLoad = async (src) => await fetchDrawingJson(src);   // DrawingData | null

자세한 데이터 흐름은 그림판 연동.

selectionActions

ts
editor.selectionActions = [{
  id: "translate", label: "영어로 번역",
  prompt: { presets: ["격식체", "구어체"] },      // 없으면 누르는 즉시 실행
  run: async ({ markdown, document, before, after, input }) => "…대상을 대신할 마크다운…",   // null 이면 취소, 던지면 이유를 보인다
}];

대상은 선택을 최상위 블록 경계로 넓힌 범위. 결과는 한 번의 편집으로 들어간다. 자세한 것은 선택 범위 동작.

이벤트

이벤트detail언제
change{ markdown, html }문서가 실제로 바뀔 때(tr.docChanged). 커서 이동은 아님. 마크다운 모드에서는 매 입력마다, 디자인 모드로 돌아올 때 한 번
modechange{ mode }디자인 ↔ 마크다운 모드 전환 (0.6.0)
focus · blur편집 영역의 focus/blur 를 호스트에서 다시 발행(원래는 버블링되지 않는다)
input편집 영역의 input 을 재발행(bubbles: true)
js
editor.addEventListener("change", (e) => {
  save(e.detail.markdown);
  preview.innerHTML = e.detail.html;
});

붙여넣기 · 끌어놓기

  • 마크다운 글 (0.10.5): 붙여 넣은 글에 줄 머리 표식(#·>·-·1.·```·|…|)이나 인라인 표식(**굵게**·[링크](url)·`코드`)이 있으면 마크다운으로 해석한다. Shift 를 누른 붙여넣기(plain)는 글자 그대로. 마크다운 같지 않은 글은 줄마다 문단
  • 이미지 파일 (0.10.8): 붙여넣기·끌어놓기의 image/* 파일은 onImageUpload 를 탄다. 없으면 data URL. 끌어놓은 자리에 들어간다

Enter · 빈 문단 정책

마크다운에는 빈 문단이 없으므로 편집기도 만들지 않는다 (0.6.2).

  • 빈 문단에서 Enter → 아무 일도 없고 안내 말풍선(messages.emptyLineHint)이 약 2초 뜬다. 인용구 안이면 인용구 탈출, 빈 목록 항목이면 목록 탈출
  • 글자 줄 맨 앞에서 Enter → 위에 빈 문단을 열고 커서를 올린다(코드 블록 뒤에 글을 끼우는 길). 안 쓰고 떠나면 지워진다 (0.10.6)
  • 커서가 없는 빈 문단은 어떻게 생겼든(한글 IME 조합 직후 브라우저가 만든 것 포함) 지운다. 목록 항목·표 셀 안은 예외
  • 문서 끝에는 항상 빈 문단 하나가 있어 표·코드·영상 아래로 나갈 수 있다. 저장되지는 않는다

저장 형식

markdown 은 표준 마크다운(CommonMark + GFM 표)이다. 마크다운이 표현하지 못하는 것만 CommonMark 가 허용하는 원문 HTML 로 남긴다.

내용저장
제목·문단·강조·링크·목록·인용·코드·표마크다운. 표는 열 정렬(:---:) 포함
이미지![alt](src "title"). 폭·정렬·테두리를 바꾼 것만 <img src alt title width="480" align="center" data-border="thin">
문단 상자(정렬·테두리)<div align="center" data-border="thin"></div> — 안쪽은 마크다운 그대로. 둘 다 없으면 상자를 걷는다 (0.10.9)
MP4 영상<video src controls> (폭을 바꾸면 width)
영상 임베드<iframe src> (폭을 바꾸면 width + 16:9 height)
오디오·코드 임베드<iframe src> (폭을 바꾸면 width)
빈 문단없음
목록촘촘(tight). 항목 안에 문단이 둘 이상일 때만 느슨

편집 영역에는 .n-prose 클래스가 붙어 있다. 보기 화면도 .n-prose(@newtil/components)로 그리면 편집과 읽기가 같은 모습이 된다. 정렬·테두리는 같은 속성 이름으로 CSS 를 두면 된다(.n-prose div[align], [data-border], img[align]).

문자열 표 (Messages)

lang 이 정한 표(ko/en) 위에 messages 로 일부만 덮어쓴다. 내장 표는 messagesKo·messagesEn 으로 가져올 수 있다. 키는 src/i18n.ts 가 정본이며 다음과 같다.

영역
툴바undo redo blockType paragraph heading(n) codeBlock bold italic underline inlineCode link linkPrompt linkTextPrompt textColor colorNone removeColor colorRed colorOrange colorYellow colorGreen colorBlue colorPurple colorPink colorGray bulletList orderedList quote codeBlockButton insertTable image video
표 편집rowAdd colAdd rowDel colDel headerRow tableDel rowAddTitle colAddTitle rowDelTitle colDelTitle headerRowTitle tableDelTitle
모드modeMarkdown modeDesign toMarkdownTitle toDesignTitle
코드 블록toCodeBlock clickToSelect langPlaceholder langAria
손잡이·원문dragHandle resizeHandle resetSize resetSizeTitle sourceAria
안내emptyLineHint
슬래시 메뉴slashHeadingLabel(n) slashHeadingDesc(n) slashTextLabel/Desc slashBulletLabel/Desc slashOrderedLabel/Desc slashQuoteLabel/Desc slashCodeLabel/Desc slashTableLabel/Desc slashDividerLabel/Desc slashLinkLabel/Desc slashImageLabel/Desc slashEmbedLabel/Desc slashAudioLabel/Desc slashVideoLabel/Desc slashDrawingLabel/Desc noCommand(query)
정렬·테두리alignLeft alignCenter alignRight border borderNone borderThin borderThick borderDashed borderRound borderShadow
그림판 접착부drawing drawingEdit drawingAlt drawingNoDataNotice (그림판 자체 문자열은 @newtil/drawing 이 가진다)
선택 범위 동작 입력칸actionRun actionCancel actionWorking actionFailed actionInputPlaceholder (동작의 이름·설명은 호스트가 준다)
프롬프트imageUrlPrompt altPrompt embedPrompt embedInvalid audioPrompt audioInvalid videoPrompt
js
editor.messages = { emptyLineHint: "빈 줄은 저장되지 않아요.", bold: "굵게" };

CSS 변수

스타일은 Shadow DOM 안에 자동 주입되고, 색은 CSS 변수로 넘긴다(변수는 Shadow 경계를 넘어 상속된다). 호스트 요소에 주면 된다.

호스트 전용 변수

변수기본대상
--newtil-code-bg--color-code-bg#1e293b코드 블록 배경 (두 테마 모두 어두운 상자)
--newtil-code-fg--color-code-fg#e2e8f0코드 블록 글자
--newtil-code-border--color-code-bordertransparent코드 블록 테두리

테마가 읽는 design-tokens 변수 (themes/newtil.css)

변수쓰는 곳
--color-text본문 글자, 툴바 셀렉트·버튼 hover 글자, 코드 언어 입력칸
--color-text-muted인용구 글자, 플레이스홀더, 툴바 버튼·드래그 핸들 기본색
--color-surface편집기 배경, 플로팅 툴바·팔레트 배경, 셀렉트 배경
--color-surface-2인라인 코드 배경, 상단 툴바 배경, 표 머리칸, 플로팅 버튼 hover
--color-surface-3상단 툴바 버튼 hover, 드래그 핸들 active
--color-border편집기·툴바·인용구·수평선·표·팔레트 테두리
--color-primary링크, 포커스 아웃라인, 눌린 버튼 글자, 폭 손잡이, 드롭 표시선
--color-primary-subtle눌린 버튼 배경
--color-code-bg / --color-code-fg / --color-code-border코드 블록 (위 --newtil-code-* 의 기본값)
--radius-2 / --radius-3인라인 코드 / 편집기 모서리
--font-size-body, --font-size-heading-lg/md/sm본문, h1/h2/h3
css
newtil-editor {
  min-height: 420px;
  --color-primary: #16a34a;
  --color-primary-subtle: #dcfce7;
  --newtil-code-bg: #0f172a;
}

다크 모드: <html data-theme="dark"> 를 호스트의 data-theme 로 동기화해 따라간다(MutationObserver). 없으면 prefers-color-scheme.

타입 (TypeScript)

ts
import type {
  NewtilEditorChangeDetail, ImageUploadHandler, EditorMode, SlashCommand,
  Lang, Messages,
  DrawingData, DrawingShape, DrawingSaveInput, DrawingSaveHandler, DrawingLoadHandler,
  SelectionAction, SelectionActionContext, SelectionActionPrompt,
} from "@newtil/editor";
import { NewtilEditor, messagesKo, messagesEn } from "@newtil/editor";
타입정의
NewtilEditorChangeDetail{ markdown: string; html: string }
ImageUploadHandler(file: File) => Promise<string>
EditorMode"wysiwyg" | "source"
SlashCommand{ id, label, description?, icon?, keywords?, run(view) }
Lang"ko" | "en"
Messages문자열 표 (위 표의 키)
DrawingData@newtil/drawingDrawing{ version: 1, width, height, background, shapes }
DrawingShape@newtil/drawingShape
DrawingSaveInput{ png: Blob; data: DrawingData; previousSrc: string | null }
DrawingSaveHandler(input: DrawingSaveInput) => Promise<string>
DrawingLoadHandler(src: string) => Promise<DrawingData | null>
SelectionAction{ id, label, description?, icon?, prompt?, run(ctx) }선택 범위 동작
SelectionActionContext{ markdown, document, before, after, input }
SelectionActionPrompt{ placeholder?, presets? }

NewtilEditor 클래스도 export 되며 HTMLElementTagNameMap["newtil-editor"] 가 선언돼 있어 document.querySelector("newtil-editor") 가 타입을 안다.

패키지 엔트리

경로내용
@newtil/editor웹 컴포넌트 등록(customElements.define("newtil-editor"), 이미 있으면 건너뜀) + 타입
@newtil/editor/reactReact 래퍼 — React
@newtil/editor/vueVue 3 래퍼 — Vue 3
@newtil/editor/themes/newtil테마 CSS 원본. Shadow DOM 에 이미 묶여 있어 따로 import 할 필요 없다(호환용)