diff --git a/CHANGELOG.md b/CHANGELOG.md
index 00da863..2c371c2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,36 @@ with a shared version number.
## [Unreleased]
+## [1.1.0] - 2026-08-08
+
+### Added
+
+- **`@devslab/editor-ruler-summernote`** — new package: a [Summernote](https://summernote.org)
+ plugin (`summernote >= 0.8`). Register with `defineRulerPlugin($)` and add
+ `'ruler'` to `toolbar` for the ruler-icon dropdown (Show/Hide, Vertical
+ Ruler, Lock/Clear Guides, cm/inch/px). Options live under the `ruler` key:
+ `enabled`, `visible`, `unit`, `vertical`, `verticalGutter`, `guides`,
+ `guideSnap`, `language`.
+
+ Summernote is direct-DOM like Froala, so this adapter reuses the same
+ behavior rather than approximating it: **whole-table indent and column-width
+ markers work here too** — the first adapter besides Froala to have them.
+ Commits go through Summernote's own `editor.afterCommand`, which is the
+ method that calls `history.recordUndo()`, so a whole drag gesture is exactly
+ one undo step.
+
+- Landing page gains a Summernote demo tab (jQuery and Summernote lazy-load
+ from a CDN only when the tab is opened) and a Summernote integration
+ section; `examples/summernote` added for one-click StackBlitz.
+
+### Changed
+
+- Docs that counted adapters ("all three", "the four packages") are now
+ count-free or corrected, and the feature split is stated where it matters:
+ the vertical ruler and guides are on every adapter, while whole-table indent
+ and column markers are on the direct-DOM adapters (Froala, Summernote).
+- README CDN pins bumped `@1.0` → `@1.1`.
+
## [1.0.1] - 2026-08-08
### Fixed
@@ -359,7 +389,8 @@ What it deliberately does **not** cover, so these can still improve:
records one Froala undo step per gesture. iife build exposes the
`EditorRulerFroala` global (core bundled) for CDN use.
-[Unreleased]: https://github.com/devslab-kr/editor-ruler/compare/v1.0.1...HEAD
+[Unreleased]: https://github.com/devslab-kr/editor-ruler/compare/v1.1.0...HEAD
+[1.1.0]: https://github.com/devslab-kr/editor-ruler/compare/v1.0.1...v1.1.0
[1.0.1]: https://github.com/devslab-kr/editor-ruler/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/devslab-kr/editor-ruler/compare/v0.16.0...v1.0.0
[0.16.0]: https://github.com/devslab-kr/editor-ruler/compare/v0.15.0...v0.16.0
diff --git a/README.ko.md b/README.ko.md
index 1b9e30d..1549744 100644
--- a/README.ko.md
+++ b/README.ko.md
@@ -23,6 +23,7 @@ Froala·TinyMCE·CKEditor 5·Quill 등 범용 WYSIWYG 에디터에는 줄자가
| [`@devslab/editor-ruler-froala`](packages/editor-ruler-froala) | [Froala WYSIWYG 에디터](https://froala.com) 플러그인 어댑터. |
| [`@devslab/editor-ruler-tiptap`](packages/editor-ruler-tiptap) | [Tiptap](https://tiptap.dev) 확장 (v2/v3). |
| [`@devslab/editor-ruler-ckeditor5`](packages/editor-ruler-ckeditor5) | [CKEditor 5](https://ckeditor.com/ckeditor-5/) 플러그인 (`ckeditor5 >= 42`). |
+| [`@devslab/editor-ruler-summernote`](packages/editor-ruler-summernote) | [Summernote](https://summernote.org) 플러그인 (`summernote >= 0.8`). |
## 빠른 시작 (코어 — 아무 contenteditable)
@@ -48,7 +49,7 @@ ruler.refresh(); // 선택/내용이 바뀔 때마다 호출
iife 빌드가 전역 `EditorRuler`를 노출합니다:
```html
-
+
@@ -60,8 +61,8 @@ Froala 어댑터도 동일합니다 — `@devslab/editor-ruler-froala/dist/index
| URL | 의미 |
|---|---|
-| `@1.0.1` | 정확한 버전 고정 — 절대 안 바뀜, 캐시 최장 |
-| `@1.0` | `1.0.x` 최신 패치 — 버그픽스 자동 반영, 브레이킹 없음 (권장) |
+| `@1.1.0` | 정확한 버전 고정 — 절대 안 바뀜, 캐시 최장 |
+| `@1.1` | `1.1.x` 최신 패치 — 버그픽스 자동 반영, 브레이킹 없음 (권장) |
| `@latest` (또는 버전 생략) | 항상 최신 릴리스 — 메이저 포함이라 브레이킹 체인지가 예고 없이 들어올 수 있음; jsDelivr가 별칭을 최대 12시간 캐시 |
## 빠른 시작 (Froala)
@@ -118,6 +119,26 @@ ClassicEditor.create(element, {
모델 속성이 순수 인라인 CSS로 다운캐스트되고, 드래그가 undo 1스텝입니다. [패키지 README](packages/editor-ruler-ckeditor5) 참조.
+## 빠른 시작 (Summernote)
+
+```bash
+npm install @devslab/editor-ruler-summernote summernote
+```
+
+```ts
+import $ from 'jquery';
+import 'summernote';
+import { defineRulerPlugin } from '@devslab/editor-ruler-summernote';
+
+defineRulerPlugin($); // 에디터 초기화 전에 1회
+$('#editor').summernote({
+ toolbar: [['misc', ['ruler']]],
+ ruler: { unit: 'cm' },
+});
+```
+
+Froala 어댑터와 같은 직접-DOM 방식이라 **테이블 통째 밀기와 컬럼 마커까지 동일하게** 동작합니다. 커밋이 Summernote의 `afterCommand`를 거쳐 드래그가 undo 1스텝입니다. [패키지 README](packages/editor-ruler-summernote) 참조.
+
`defineRulerPlugin`은 `ruler` 플러그인과 **함께** 툴바 커맨드들을 등록합니다 — 필요한 것을 `toolbarButtons`에 추가하세요:
- `rulerOptions` — **권장 단일 버튼**: 줄자 아이콘 드롭다운 하나에 보이기/숨기기 · 세로 줄자 · 가이드 잠금 · 가이드 지우기 + cm / inch / px (활성 상태 체크 표시)
@@ -125,7 +146,7 @@ ClassicEditor.create(element, {
Froala 옵션: `rulerVisible: false`면 가로 줄자를 숨긴 채 시작(플러그인·툴바는 살아 있어 나중에 토글로 켬), `rulerVertical: true`면 초기화 시 세로 줄자 표시, `rulerVerticalGutter: true`면 세로 줄자의 23px 자리를 처음부터 예약해(`scrollbar-gutter: stable`과 같은 발상) 토글해도 본문이 리플로우되지 않음, `rulerGuides: false`면 가이드선 비활성.
-Tiptap·CKEditor 5도 같은 제어를 지원합니다: `visible: false`면 숨긴 채 시작(Tiptap은 `showRuler`/`hideRuler`/`toggleRuler` 커맨드, CKEditor 5는 플러그인의 `show()`/`toggle()`), 그리고 **세로 줄자는 세 어댑터 모두에서 동작합니다** — `vertical: true`면 초기 표시, `verticalGutter: true`면 23px 자리를 예약해 토글해도 본문이 리플로우되지 않음 (Tiptap: `showVerticalRuler`/`toggleVerticalRuler` 커맨드, CKEditor 5: `showVRuler()`/`toggleVRuler()` + 툴바 드롭다운의 세로 줄자 항목).
+Tiptap·CKEditor 5·Summernote는 각자의 설정 키 아래에서 같은 제어를 받습니다: `visible: false`면 숨긴 채 시작, `vertical: true`면 초기 세로 줄자 표시, `verticalGutter: true`면 23px 자리를 예약해 토글해도 본문이 리플로우되지 않습니다. **세로 줄자와 가이드선은 전 어댑터 공통**이고, 테이블 통째 밀기와 컬럼 마커는 직접-DOM 어댑터(Froala·Summernote)에 있습니다. 토글은 Tiptap이 커맨드(`toggleRuler`·`toggleVerticalRuler`), 나머지는 플러그인 메서드(`toggle()`·`toggleVRuler()`)입니다.
## 기능
@@ -153,6 +174,7 @@ Tiptap·CKEditor 5도 같은 제어를 지원합니다: `visible: false`면 숨
- [Froala — StackBlitz](https://stackblitz.com/github/devslab-kr/editor-ruler/tree/main/examples/froala)
- [Tiptap — StackBlitz](https://stackblitz.com/github/devslab-kr/editor-ruler/tree/main/examples/tiptap)
- [CKEditor 5 — StackBlitz](https://stackblitz.com/github/devslab-kr/editor-ruler/tree/main/examples/ckeditor5)
+- [Summernote — StackBlitz](https://stackblitz.com/github/devslab-kr/editor-ruler/tree/main/examples/summernote)
로컬:
@@ -167,7 +189,7 @@ pnpm install && pnpm build
약속에 포함되는 것:
-- 네 패키지 진입점에서 export하는 모든 심볼, 각 어댑터가 받는 옵션·설정
+- 각 패키지 진입점에서 export하는 모든 심볼, 각 어댑터가 받는 옵션·설정
- 테마링에 쓰는 `--edr-*` CSS 커스텀 프로퍼티
- 출력 계약 — 들여쓰기는 순수 인라인 CSS(`margin-left` / `margin-right` / `text-indent`, px)로 기록
diff --git a/README.md b/README.md
index 5c143ea..ad789be 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ Every classic WYSIWYG editor (Froala, TinyMCE, CKEditor 5, Quill, …) ships wit
| [`@devslab/editor-ruler-froala`](packages/editor-ruler-froala) | [Froala WYSIWYG editor](https://froala.com) plugin adapter. |
| [`@devslab/editor-ruler-tiptap`](packages/editor-ruler-tiptap) | [Tiptap](https://tiptap.dev) extension (v2/v3). |
| [`@devslab/editor-ruler-ckeditor5`](packages/editor-ruler-ckeditor5) | [CKEditor 5](https://ckeditor.com/ckeditor-5/) plugin (`ckeditor5 >= 42`). |
+| [`@devslab/editor-ruler-summernote`](packages/editor-ruler-summernote) | [Summernote](https://summernote.org) plugin (`summernote >= 0.8`). |
## Quick start (core, any contenteditable)
@@ -48,7 +49,7 @@ ruler.refresh(); // call whenever selection or content changes
The iife build exposes an `EditorRuler` global:
```html
-
+
@@ -60,8 +61,8 @@ Version pinning options:
| URL | Meaning |
|---|---|
-| `@1.0.1` | Exact version — never changes, cached longest |
-| `@1.0` | Latest `1.0.x` patch — bugfixes auto-applied, no breaking changes (recommended) |
+| `@1.1.0` | Exact version — never changes, cached longest |
+| `@1.1` | Latest `1.1.x` patch — bugfixes auto-applied, no breaking changes (recommended) |
| `@latest` (or no version) | Always the newest release — majors included, so breaking changes can land without warning; jsDelivr caches the alias for up to 12h |
## Quick start (Froala)
@@ -118,6 +119,26 @@ ClassicEditor.create(element, {
Model attributes down-cast to plain inline CSS; one undo step per drag. See the [package README](packages/editor-ruler-ckeditor5).
+## Quick start (Summernote)
+
+```bash
+npm install @devslab/editor-ruler-summernote summernote
+```
+
+```ts
+import $ from 'jquery';
+import 'summernote';
+import { defineRulerPlugin } from '@devslab/editor-ruler-summernote';
+
+defineRulerPlugin($); // once, before initializing any editor
+$('#editor').summernote({
+ toolbar: [['misc', ['ruler']]],
+ ruler: { unit: 'cm' },
+});
+```
+
+Same direct-DOM behavior as the Froala adapter — including whole-table indent and column markers. Commits go through Summernote's `afterCommand`, so a drag is one undo step. See the [package README](packages/editor-ruler-summernote).
+
`defineRulerPlugin` registers the `ruler` plugin **and** toolbar commands — add what you need to `toolbarButtons`:
- `rulerOptions` — **recommended single button**: one ruler-icon dropdown holding Show/Hide, Vertical Ruler, Lock Guides, Clear Guides, plus cm / inch / px (active states checkmarked)
@@ -125,7 +146,7 @@ Model attributes down-cast to plain inline CSS; one undo step per drag. See the
Froala options: `rulerVisible: false` starts the horizontal ruler hidden (plugin and toolbar stay alive — toggle it on later); `rulerVertical: true` shows the vertical ruler on init; `rulerVerticalGutter: true` reserves the strip's 23px column from the start (like `scrollbar-gutter: stable`) so toggling the vertical ruler never reflows the content; `rulerGuides: false` disables guide lines.
-Tiptap and CKEditor 5 support the same controls: `visible: false` starts the ruler hidden (`showRuler`/`hideRuler`/`toggleRuler` commands on Tiptap, the plugin's `show()`/`toggle()` on CKEditor 5), and the **vertical ruler works on all three adapters** — `vertical: true` shows it on init, `verticalGutter: true` reserves its 23px column so toggling never reflows the content (Tiptap: `showVerticalRuler`/`toggleVerticalRuler` commands; CKEditor 5: `showVRuler()`/`toggleVRuler()` plus a Vertical Ruler entry in the toolbar dropdown).
+Tiptap, CKEditor 5, and Summernote take the same controls under their own config key: `visible: false` starts the ruler hidden, `vertical: true` shows the vertical ruler on init, and `verticalGutter: true` reserves its 23px column so toggling never reflows the content. **Every adapter has the vertical ruler and guides**; whole-table indent and column markers are on the direct-DOM adapters (Froala, Summernote). Toggling is via commands on Tiptap (`toggleRuler`, `toggleVerticalRuler`) and plugin methods elsewhere (`toggle()`, `toggleVRuler()`).
## Features
@@ -153,6 +174,7 @@ One-click sandboxes:
- [Froala on StackBlitz](https://stackblitz.com/github/devslab-kr/editor-ruler/tree/main/examples/froala)
- [Tiptap on StackBlitz](https://stackblitz.com/github/devslab-kr/editor-ruler/tree/main/examples/tiptap)
- [CKEditor 5 on StackBlitz](https://stackblitz.com/github/devslab-kr/editor-ruler/tree/main/examples/ckeditor5)
+- [Summernote on StackBlitz](https://stackblitz.com/github/devslab-kr/editor-ruler/tree/main/examples/summernote)
Locally:
@@ -167,7 +189,7 @@ Stable since **1.0.0**. Breaking changes to the documented API require a major r
Covered by that promise:
-- Every symbol exported from the four packages' entry points, and the options / config each adapter accepts
+- Every symbol exported from the packages' entry points, and the options / config each adapter accepts
- The `--edr-*` CSS custom properties used for theming
- The output contract — indentation is written as plain inline CSS (`margin-left` / `margin-right` / `text-indent`, in px)
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index c794ec4..ed9759e 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -324,6 +324,13 @@ Then wire the normalization from §3 (cells → table, img → parent block) and
call `ruler.refresh()` on selection change, content change, and window resize.
Copy an existing adapter's test file as your template.
+The Summernote adapter is the worked example of exactly this: it is direct-DOM
+like Froala, so it reuses the same table/image normalization and column-marker
+math, and only the four host-specific calls differ — `$.summernote.plugins` for
+registration, `context.layoutInfo.editable` for the DOM, `editor.getLastRange`
+for the selection, and `editor.afterCommand` for the undo boundary (that method
+is what calls `history.recordUndo()`).
+
## 7. Landmines we already stepped on (so you don't)
1. **jsdom has no layout** — `clientWidth` is 0, which collapses clamp ranges.
@@ -372,6 +379,7 @@ packages/editor-ruler/src/
packages/editor-ruler-froala/src/index.ts direct-DOM adapter + toolbar dropdown
packages/editor-ruler-tiptap/src/index.ts model adapter (node attribute + transactions)
packages/editor-ruler-ckeditor5/src/index.ts model adapter (schema + up/downcast)
+packages/editor-ruler-summernote/src/index.ts direct-DOM adapter (jQuery plugin + toolbar dropdown)
examples/ one-click StackBlitz projects
site/ landing page with per-editor demo tabs
```
diff --git a/examples/ckeditor5/package.json b/examples/ckeditor5/package.json
index 7d90cf0..c11ce01 100644
--- a/examples/ckeditor5/package.json
+++ b/examples/ckeditor5/package.json
@@ -9,7 +9,7 @@
"preview": "vite preview"
},
"dependencies": {
- "@devslab/editor-ruler-ckeditor5": "^1.0.1",
+ "@devslab/editor-ruler-ckeditor5": "^1.1.0",
"ckeditor5": "^48.4.0"
},
"devDependencies": {
diff --git a/examples/froala/package.json b/examples/froala/package.json
index ca5b395..8677cc0 100644
--- a/examples/froala/package.json
+++ b/examples/froala/package.json
@@ -9,7 +9,7 @@
"preview": "vite preview"
},
"dependencies": {
- "@devslab/editor-ruler-froala": "^1.0.1",
+ "@devslab/editor-ruler-froala": "^1.1.0",
"froala-editor": "^4.5.2"
},
"devDependencies": {
diff --git a/examples/summernote/index.html b/examples/summernote/index.html
new file mode 100644
index 0000000..5287e12
--- /dev/null
+++ b/examples/summernote/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ editor-ruler — Summernote example
+
+
+
+
+
+
+
+
+
diff --git a/examples/summernote/main.js b/examples/summernote/main.js
new file mode 100644
index 0000000..8476ff6
--- /dev/null
+++ b/examples/summernote/main.js
@@ -0,0 +1,36 @@
+import $ from 'jquery';
+import 'summernote/dist/summernote-lite.css';
+import 'summernote/dist/summernote-lite.js';
+import { defineRulerPlugin } from '@devslab/editor-ruler-summernote';
+
+// Summernote attaches itself to the jQuery instance it finds, so hand it ours.
+window.jQuery = window.$ = $;
+
+defineRulerPlugin($);
+
+$('#editor').summernote({
+ height: 300,
+ toolbar: [
+ ['style', ['bold', 'italic', 'underline']],
+ ['para', ['ul', 'ol']],
+ ['table', ['table']],
+ ['misc', ['ruler']],
+ ],
+ ruler: {
+ unit: 'cm',
+ verticalGutter: true, // reserve the vertical ruler's column up front
+ },
+ callbacks: {
+ onInit() {
+ $('#editor').summernote(
+ 'code',
+ '
Summernote는 jQuery 기반 직접-DOM 에디터라 Froala와 같은 경로로 동작합니다 — 테이블 셀에 커서를 두면 컬럼 마커가 뜨고 여백 핸들은 테이블 전체를 밉니다. undo는 Summernote의 afterCommand를 거쳐 드래그당 1스텝입니다. jQuery와 Summernote를 CDN에서 불러오므로 네트워크에 따라 잠시 걸릴 수 있습니다.
+
+
@@ -431,6 +447,28 @@
CKEditor 5 연동
들여쓰기는 모델 속성으로 저장되어 getData()에 순수 인라인 CSS로 다운캐스트되고, 드래그 전체가 undo 한 단계입니다. editorRuler 툴바 드롭다운에 보이기/숨기기·세로 줄자·가이드 잠금/지우기·단위 전환이 들어 있습니다. ckeditor5 >= 42 필요.
테이블·이미지는요? Word처럼 다 밀립니다 — 테이블 안에 커서를 두고 드래그하면 테이블 전체가 이동하고, 셀 안 문단은 개별 들여쓰기, 이미지는 소속 블록과 함께 움직입니다. 테이블 안에서는 줄자에 컬럼 경계 마커가 떠서 드래그로 컬럼 폭도 조절됩니다 (병합 셀 테이블 제외). 이 테이블·이미지 동작은 현재 Froala 어댑터 전용입니다 — Tiptap·CKEditor 5에서는 문단 들여쓰기·세로 줄자·가이드선까지 동일하게 동작합니다.
가이드선은 문서에 남나요? 아니요 — 순수 시각 오버레이라 내보내는 HTML에 전혀 남지 않습니다. 정렬 참고용이며, 드롭다운에서 잠그거나 한 번에 지울 수 있고, 드래그 중 핸들과 컬럼 마커가 가이드 근처에서 스냅됩니다.
탭 스톱은요? HTML에는 탭 스톱 모델이 없어 의도적으로 범위 밖입니다. 여백·들여쓰기는 CSS와 1:1이라 어디서나 안전하게 동작합니다.
-
다른 에디터는요? 코어가 에디터 불문이라 어댑터는 "선택 블록 읽기/스타일 쓰기/undo 연결"만 하면 됩니다 — Froala·Tiptap·CKEditor 5 어댑터가 이미 있고, 위 탭에서 셋 다 바로 써볼 수 있습니다. 다른 에디터가 필요하면 이슈로 알려주세요.
+
다른 에디터는요? 코어가 에디터 불문이라 어댑터는 "선택 블록 읽기/스타일 쓰기/undo 연결"만 하면 됩니다 — Froala·Tiptap·CKEditor 5·Summernote 어댑터가 이미 있고, 위 탭에서 넷 다 바로 써볼 수 있습니다. 다른 에디터가 필요하면 이슈로 알려주세요.
@@ -498,6 +536,29 @@
자주 묻는 것
'note.tiptap':
'Tiptap is headless, so it ships no toolbar — the mini toolbar above is hand-wired for this demo. What actually differs from the core tab is the document model (node attributes, undo integration, getHTML serialization). Modules load from a CDN (esm.sh), so this can take a moment.',
'loading.ckeditor': 'Loading CKEditor 5…',
+ 'loading.summernote': 'Loading Summernote…',
+ 'note.summernote':
+ "Summernote is a jQuery-based direct-DOM editor, so it takes the same path as Froala — put the caret in a table cell and column markers appear, and the margin handles push the whole table. Undo goes through Summernote's own afterCommand, one step per drag. jQuery and Summernote load from a CDN, so this can take a moment.",
+ 'h2.summernote': 'Summernote integration',
+ 'code.summernote':
+ "import $ from 'jquery';
+import 'summernote';
+import { defineRulerPlugin } from '@devslab/editor-ruler-summernote';
+
+defineRulerPlugin($); // once, before initializing any editor
+
+$('#editor').summernote({
+ toolbar: [['misc', ['ruler']]], // ruler-icon dropdown
+ ruler: {
+ unit: 'cm', // 'cm' | 'in' | 'px'
+ visible: true, // false starts it hidden
+ vertical: false, // show the vertical ruler on init
+ verticalGutter: false, // reserve its column — toggling never reflows
+ guides: true,
+ },
+});",
+ 'note.summernoteDocs':
+ 'Adding 'ruler' to toolbar gives you the ruler-icon dropdown. Same direct-DOM behavior as the Froala adapter, including whole-table indent and column markers. Commits go through Summernote's afterCommand, which is what records the undo snapshot. Requires summernote >= 0.8 and its jQuery peer.',
'note.ckeditor':
'CKEditor 5 modules load from a CDN, so this can take a moment. Images behave the same way, subject to how you configure CKEditor’s image plugin.',
'label.unit': 'Unit',
@@ -553,7 +614,7 @@
자주 묻는 것
'faq.tabstops':
'What about tab stops? HTML has no tab-stop model, so they are deliberately out of scope. Margins and indents map 1:1 to CSS, which is why they work safely everywhere.',
'faq.editors':
- 'What about other editors? The core is editor-agnostic, so an adapter only has to read the selected blocks, write styles, and hook into undo — the Froala, Tiptap, and CKEditor 5 adapters already exist, and you can try all three in the tabs above. Need a different editor? Open an issue.',
+ 'What about other editors? The core is editor-agnostic, so an adapter only has to read the selected blocks, write styles, and hook into undo — the Froala, Tiptap, CKEditor 5, and Summernote adapters already exist, and you can try all four in the tabs above. Need a different editor? Open an issue.',
};
(function () {
@@ -613,7 +674,8 @@