@aeriz/wysiwyg 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +634 -96
  2. package/angular/README.md +112 -0
  3. package/angular/aeriz-editor.directive.ts +243 -0
  4. package/angular/aeriz-wysiwyg.module.ts +14 -0
  5. package/angular/index.ts +5 -0
  6. package/dist/adapters/jquery.cjs +1 -0
  7. package/dist/adapters/jquery.js +91 -0
  8. package/dist/adapters/react.cjs +1 -0
  9. package/dist/adapters/react.js +66 -0
  10. package/dist/adapters/shared.cjs +1 -0
  11. package/dist/adapters/shared.js +87 -0
  12. package/dist/adapters/svelte.cjs +1 -0
  13. package/dist/adapters/svelte.js +29 -0
  14. package/dist/adapters/vue.cjs +1 -0
  15. package/dist/adapters/vue.js +110 -0
  16. package/dist/adapters/vue2.cjs +1 -0
  17. package/dist/adapters/vue2.js +117 -0
  18. package/dist/adapters/wc.cjs +1 -0
  19. package/dist/adapters/wc.js +207 -0
  20. package/dist/aeriz-wysiwyg-adapter.js +169 -0
  21. package/dist/aeriz-wysiwyg-adapter.min.js +1 -0
  22. package/dist/aeriz-wysiwyg-jquery.js +275 -0
  23. package/dist/aeriz-wysiwyg-jquery.min.js +1 -0
  24. package/dist/aeriz-wysiwyg-wc.js +401 -0
  25. package/dist/aeriz-wysiwyg-wc.min.js +1 -0
  26. package/dist/aeriz-wysiwyg.cjs +8759 -0
  27. package/dist/aeriz-wysiwyg.css +587 -163
  28. package/dist/aeriz-wysiwyg.esm.js +4249 -1381
  29. package/dist/aeriz-wysiwyg.esm.min.js +4456 -1995
  30. package/dist/aeriz-wysiwyg.js +4249 -1381
  31. package/dist/aeriz-wysiwyg.min.cjs +56 -0
  32. package/dist/aeriz-wysiwyg.min.css +1 -1
  33. package/dist/aeriz-wysiwyg.min.js +54 -21
  34. package/dist/server.cjs +34 -0
  35. package/dist/server.js +798 -0
  36. package/dist-types/adapters/jquery.d.ts +34 -0
  37. package/dist-types/adapters/jquery.d.ts.map +1 -0
  38. package/dist-types/adapters/react.d.ts +28 -0
  39. package/dist-types/adapters/react.d.ts.map +1 -0
  40. package/dist-types/adapters/shared.d.ts +83 -0
  41. package/dist-types/adapters/shared.d.ts.map +1 -0
  42. package/dist-types/adapters/svelte.d.ts +38 -0
  43. package/dist-types/adapters/svelte.d.ts.map +1 -0
  44. package/dist-types/adapters/vue.d.ts +189 -0
  45. package/dist-types/adapters/vue.d.ts.map +1 -0
  46. package/dist-types/adapters/vue2.d.ts +175 -0
  47. package/dist-types/adapters/vue2.d.ts.map +1 -0
  48. package/dist-types/adapters/wc.d.ts +96 -0
  49. package/dist-types/adapters/wc.d.ts.map +1 -0
  50. package/dist-types/cjs/adapters/jquery.d.cts +34 -0
  51. package/dist-types/cjs/adapters/react.d.cts +28 -0
  52. package/dist-types/cjs/adapters/shared.d.cts +83 -0
  53. package/dist-types/cjs/adapters/svelte.d.cts +38 -0
  54. package/dist-types/cjs/adapters/vue.d.cts +189 -0
  55. package/dist-types/cjs/adapters/vue2.d.cts +175 -0
  56. package/dist-types/cjs/adapters/wc.d.cts +96 -0
  57. package/dist-types/cjs/core/AerizEditor.d.cts +530 -0
  58. package/dist-types/cjs/core/AerizViewer.d.cts +61 -0
  59. package/dist-types/cjs/core/block-meta.d.cts +98 -0
  60. package/dist-types/cjs/core/block-policy.d.cts +43 -0
  61. package/dist-types/cjs/core/blocks.d.cts +411 -0
  62. package/dist-types/cjs/core/fonts.d.cts +60 -0
  63. package/dist-types/cjs/core/highlight.d.cts +44 -0
  64. package/dist-types/cjs/core/history.d.cts +86 -0
  65. package/dist-types/cjs/core/html-mini.d.cts +65 -0
  66. package/dist-types/cjs/core/i18n.d.cts +221 -0
  67. package/dist-types/cjs/core/import-html.d.cts +40 -0
  68. package/dist-types/cjs/core/import-markdown.d.cts +33 -0
  69. package/dist-types/cjs/core/input-rules.d.cts +46 -0
  70. package/dist-types/cjs/core/markdown-export.d.cts +30 -0
  71. package/dist-types/cjs/core/plain-text.d.cts +17 -0
  72. package/dist-types/cjs/core/render-string.d.cts +68 -0
  73. package/dist-types/cjs/core/selection.d.cts +46 -0
  74. package/dist-types/cjs/core/snapshot.d.cts +51 -0
  75. package/dist-types/cjs/core/table-ops.d.cts +30 -0
  76. package/dist-types/cjs/index.d.cts +22 -0
  77. package/dist-types/cjs/server/index.d.cts +37 -0
  78. package/dist-types/cjs/types/index.d.cts +61 -0
  79. package/dist-types/cjs/ui/Toolbar.d.cts +21 -0
  80. package/dist-types/cjs/ui/dialogs.d.cts +74 -0
  81. package/dist-types/cjs/ui/icons.d.cts +2 -0
  82. package/dist-types/cjs/ui/popovers.d.cts +70 -0
  83. package/dist-types/core/AerizEditor.d.ts +210 -17
  84. package/dist-types/core/AerizEditor.d.ts.map +1 -1
  85. package/dist-types/core/AerizViewer.d.ts +16 -4
  86. package/dist-types/core/AerizViewer.d.ts.map +1 -1
  87. package/dist-types/core/block-meta.d.ts +98 -0
  88. package/dist-types/core/block-meta.d.ts.map +1 -0
  89. package/dist-types/core/block-policy.d.ts +43 -0
  90. package/dist-types/core/block-policy.d.ts.map +1 -0
  91. package/dist-types/core/blocks.d.ts +109 -11
  92. package/dist-types/core/blocks.d.ts.map +1 -1
  93. package/dist-types/core/fonts.d.ts +11 -0
  94. package/dist-types/core/fonts.d.ts.map +1 -1
  95. package/dist-types/core/history.d.ts +86 -0
  96. package/dist-types/core/history.d.ts.map +1 -0
  97. package/dist-types/core/html-mini.d.ts +65 -0
  98. package/dist-types/core/html-mini.d.ts.map +1 -0
  99. package/dist-types/core/i18n.d.ts +12 -0
  100. package/dist-types/core/i18n.d.ts.map +1 -1
  101. package/dist-types/core/import-html.d.ts +1 -0
  102. package/dist-types/core/import-html.d.ts.map +1 -1
  103. package/dist-types/core/import-markdown.d.ts +0 -4
  104. package/dist-types/core/import-markdown.d.ts.map +1 -1
  105. package/dist-types/core/input-rules.d.ts +46 -0
  106. package/dist-types/core/input-rules.d.ts.map +1 -0
  107. package/dist-types/core/markdown-export.d.ts +30 -0
  108. package/dist-types/core/markdown-export.d.ts.map +1 -0
  109. package/dist-types/core/plain-text.d.ts +17 -0
  110. package/dist-types/core/plain-text.d.ts.map +1 -0
  111. package/dist-types/core/render-string.d.ts +68 -0
  112. package/dist-types/core/render-string.d.ts.map +1 -0
  113. package/dist-types/core/selection.d.ts +46 -0
  114. package/dist-types/core/selection.d.ts.map +1 -0
  115. package/dist-types/core/snapshot.d.ts +51 -0
  116. package/dist-types/core/snapshot.d.ts.map +1 -0
  117. package/dist-types/core/table-ops.d.ts +30 -0
  118. package/dist-types/core/table-ops.d.ts.map +1 -0
  119. package/dist-types/index.d.ts +21 -10
  120. package/dist-types/index.d.ts.map +1 -1
  121. package/dist-types/server/index.d.ts +37 -0
  122. package/dist-types/server/index.d.ts.map +1 -0
  123. package/dist-types/types/index.d.ts +21 -0
  124. package/dist-types/types/index.d.ts.map +1 -1
  125. package/dist-types/ui/Toolbar.d.ts +1 -1
  126. package/dist-types/ui/Toolbar.d.ts.map +1 -1
  127. package/dist-types/ui/dialogs.d.ts.map +1 -1
  128. package/dist-types/ui/icons.d.ts.map +1 -1
  129. package/dist-types/ui/popovers.d.ts +1 -1
  130. package/dist-types/ui/popovers.d.ts.map +1 -1
  131. package/package.json +213 -58
@@ -0,0 +1,96 @@
1
+ /**
2
+ * 커스텀 엘리먼트 어댑터 — `<aeriz-editor>` / `<aeriz-viewer>`.
3
+ *
4
+ * 프레임워크 의존이 전혀 없는 최소 공통 분모다. Vanilla / jQuery 는 물론
5
+ * Angular 처럼 별도 컴파일러가 필요한 프레임워크도 이 엘리먼트를 통해 버전
6
+ * 제약 없이 붙을 수 있다 (`angular/` 디렉터리의 디렉티브가 이 위에 올라간다).
7
+ *
8
+ * Shadow DOM 을 쓰지 않는다. 에디터 스타일시트가 전역 CSS 변수 기반이라
9
+ * 그림자 경계를 두면 테마 변수와 폰트 상속이 끊긴다.
10
+ *
11
+ * @example
12
+ * <script type="module">
13
+ * import '@aeriz/wysiwyg/wc';
14
+ * </script>
15
+ * <link rel="stylesheet" href="/aeriz-wysiwyg.min.css" />
16
+ *
17
+ * <aeriz-editor name="body" theme="dark" placeholder="내용을 입력하세요">
18
+ * </aeriz-editor>
19
+ * <script>
20
+ * const el = document.querySelector('aeriz-editor');
21
+ * el.addEventListener('change', (e) => console.log(e.detail.html));
22
+ * el.value = '<p>안녕하세요</p>';
23
+ * </script>
24
+ */
25
+ import type { AerizEditor, AerizViewer, BlockSnapshot } from '@aeriz/wysiwyg';
26
+ type UploadFn = (file: File) => Promise<string>;
27
+ /**
28
+ * `<aeriz-editor>`.
29
+ *
30
+ * 문자열로 표현되는 설정은 속성(attribute), 함수/객체는 프로퍼티로 준다.
31
+ * 폼 연동을 지원하는 브라우저에서는 `name` 속성을 주면 `<form>` 전송에
32
+ * 본문 HTML 이 함께 실린다.
33
+ */
34
+ export declare class AerizEditorElement extends HTMLElement {
35
+ static get observedAttributes(): string[];
36
+ /** 폼 연동 대상임을 선언. 미지원 브라우저는 이 필드를 무시한다. */
37
+ static formAssociated: boolean;
38
+ /** 프로퍼티로 설정된 업로드 핸들러. 속성으로는 표현할 수 없다. */
39
+ onImageUpload: UploadFn | undefined;
40
+ onFileUpload: UploadFn | undefined;
41
+ private controller;
42
+ private internals;
43
+ /**
44
+ * 아직 에디터에 반영되지 않은 **외부에서 지정한** 값.
45
+ *
46
+ * 반영이 끝나면 즉시 비운다. 이 값이 남아 있으면 관계없는 속성(`theme` 등) 이
47
+ * 바뀔 때마다 낡은 값이 `setHTML()` 로 다시 밀려들어가, 모든 블록 id 가
48
+ * 재발급되고 입력 중이던 캐럿이 문서 맨 앞으로 튄다.
49
+ */
50
+ private pendingValue;
51
+ /** 에디터가 마지막으로 보고한 본문. 폼 값과 마운트 해제 후 `value` 의 근거. */
52
+ private currentHTML;
53
+ constructor();
54
+ /** 현재 본문 HTML. 설정하면 본문을 교체한다. */
55
+ get value(): string;
56
+ set value(next: string);
57
+ /** 코어 인스턴스. 마운트 전에는 `null`. */
58
+ get editor(): AerizEditor | null;
59
+ /** 블록 상태 스냅샷. */
60
+ getBlocks(): BlockSnapshot[];
61
+ /** 본문에 캐럿을 둔다. `HTMLElement.focus()` 를 본문 쪽으로 위임한다. */
62
+ focus(): void;
63
+ connectedCallback(): void;
64
+ disconnectedCallback(): void;
65
+ attributeChangedCallback(name: string, _previous: string | null, next: string | null): void;
66
+ /**
67
+ * 현재 속성·프로퍼티 상태를 컨트롤러에 반영한다.
68
+ * 반영 직후 pendingValue 를 비워, 다음 갱신에서 같은 값이 되살아나지 않게 한다.
69
+ */
70
+ private applyProps;
71
+ private buildProps;
72
+ private syncFormValue;
73
+ }
74
+ /** `<aeriz-viewer>` — 읽기 전용 렌더링. */
75
+ export declare class AerizViewerElement extends HTMLElement {
76
+ static get observedAttributes(): string[];
77
+ private controller;
78
+ private pendingBlocks;
79
+ /** 블록 상태를 직접 넣는다. `html` 속성보다 우선한다. */
80
+ get blocks(): BlockSnapshot[];
81
+ set blocks(next: BlockSnapshot[]);
82
+ get viewer(): AerizViewer | null;
83
+ connectedCallback(): void;
84
+ disconnectedCallback(): void;
85
+ attributeChangedCallback(): void;
86
+ private buildProps;
87
+ }
88
+ /**
89
+ * 커스텀 엘리먼트를 등록한다. 이미 등록된 이름은 건너뛰므로 여러 번 불러도
90
+ * 안전하다. 이름 충돌이 있는 페이지에서는 `prefix` 로 바꿀 수 있다.
91
+ *
92
+ * @param prefix 태그 접두사. 기본 `'aeriz'` → `<aeriz-editor>` / `<aeriz-viewer>`
93
+ */
94
+ export declare function defineAerizElements(prefix?: string): void;
95
+ export {};
96
+ // (소스맵은 CJS 선언 사본에서 제외됨 — scripts/finalize-types.mjs)
@@ -0,0 +1,530 @@
1
+ import type { EditorOptions, SerializeHtmlOptions } from '../types/index.cjs';
2
+ import { Toolbar } from '../ui/Toolbar.cjs';
3
+ import type { Block, BlockAttrs, BlockKind, BlockSnapshot } from './blocks.cjs';
4
+ export type { Block, BlockAttrs, BlockKind, BlockMatch, BlockSnapshot, BlockType, ClassifyResult, HeadingLevel, } from './blocks.cjs';
5
+ export interface InlineState {
6
+ bold: boolean;
7
+ italic: boolean;
8
+ underline: boolean;
9
+ strike: boolean;
10
+ }
11
+ export interface EditorState {
12
+ blocks: Block[];
13
+ activeBlockId: string | null;
14
+ inline: InlineState;
15
+ }
16
+ export type EditorStateListener = (state: EditorState) => void;
17
+ export declare class AerizEditor {
18
+ readonly root: HTMLDivElement;
19
+ readonly content: HTMLDivElement;
20
+ readonly toolbar: Toolbar | null;
21
+ private readonly contentWrap;
22
+ private readonly handlesLayer;
23
+ private readonly dropIndicator;
24
+ private readonly handles;
25
+ private readonly blockLabelPill;
26
+ private readonly pillGlyphEl;
27
+ private readonly pillTextEl;
28
+ private readonly statusBar;
29
+ private readonly undoBtn;
30
+ private readonly redoBtn;
31
+ private readonly statusBlockEl;
32
+ private readonly statusBlocksEl;
33
+ private readonly statusCharsEl;
34
+ private readonly statusSourceEl;
35
+ private readonly options;
36
+ private readonly observer;
37
+ private readonly blockIds;
38
+ private readonly idFor;
39
+ private readonly listeners;
40
+ private readonly handleSelectionChange;
41
+ private readonly handleResize;
42
+ /** document 에 붙는다 — destroy() 에서 반드시 떼야 인스턴스가 GC 된다. */
43
+ private readonly handleDocumentMouseUp;
44
+ private state;
45
+ private destroyed;
46
+ private syncQueued;
47
+ private lastSerialized;
48
+ /** 포인터가 올라가 있는 블록. 그 블록의 컨트롤만 드러낸다. */
49
+ private hoveredBlockId;
50
+ /** hover 판정을 프레임당 1 회로 묶기 위한 rAF 핸들. */
51
+ private hoverRaf;
52
+ /** 라벨 pill 의 등장 애니메이션을 블록이 **바뀔 때만** 다시 재생하기 위한 표식. */
53
+ private pillBlockId;
54
+ /**
55
+ * 편집 이력. 브라우저 기본 undo 는 우리가 직접 한 DOM 조작(블록 이동 · 표
56
+ * 편집 · 유형 변경) 을 모르기 때문에 자체 스택을 둔다.
57
+ */
58
+ private history;
59
+ /**
60
+ * 이력 되돌리기가 만든 DOM 변경인지. 이 동안의 변경은 다시 기록하지 않는다
61
+ * (되돌리기가 스스로를 이력에 쌓으면 되돌리기를 되돌릴 수 없다).
62
+ */
63
+ private applyingHistory;
64
+ /**
65
+ * 다음 기록의 성격. 구조를 바꾸는 조작은 타이핑과 합쳐지지 않도록
66
+ * 조작 직전에 이 값을 올려 둔다.
67
+ */
68
+ private pendingChangeKind;
69
+ /** 사용자가 색 다이얼로그에서 직접 고른 색상의 LRU. 에디터 인스턴스 메모리에만 유지. */
70
+ private recentCustomColors;
71
+ constructor(options: EditorOptions);
72
+ /**
73
+ * 본문 HTML 을 반환한다.
74
+ *
75
+ * 두 가지 스타일 전략을 지원한다 (`options.styles`, 기본값은 생성 시
76
+ * `EditorOptions.htmlStyles`, 그것도 없으면 `'inline'`).
77
+ *
78
+ * - `'inline'` — 호출 시점의 테마 변수(--aw-primary 등) 를 읽어 블록 단위
79
+ * 인라인 스타일에 베이크한다. 스타일시트 없이 어디에 붙여도(이메일 본문,
80
+ * 다른 페이지) 에디터에서 보이는 그대로 렌더된다.
81
+ * - `'class'` — 테마 스타일을 베이크하지 않고 `aw-block-*` / `aw-align-*`
82
+ * 클래스만 남긴다. `style` 속성을 지우는 서버 sanitizer 아래에서 서식을
83
+ * 보존해야 할 때 쓴다. 출력은 `aeriz-wysiwyg.min.css` 를 로드한 페이지의
84
+ * `.aeriz-wysiwyg > .aw-content` 안에 넣는 것을 전제로 한다.
85
+ *
86
+ * 어느 쪽이든 에디터 내부 DOM 은 변형되지 않으며(clone 트리에 적용),
87
+ * data-block-kind / data-block-focused 같은 에디터 전용 속성과 리사이즈
88
+ * 핸들 · 정렬 컨트롤 같은 편집 보조 마크업은 출력에서 제거된다.
89
+ * data-block-id 는 보존.
90
+ */
91
+ getHTML(options?: SerializeHtmlOptions): string;
92
+ /** 가공 없는 원본 innerHTML. 직렬화 검증/디버그 용도. */
93
+ getRawHTML(): string;
94
+ setHTML(html: string): void;
95
+ focus(): void;
96
+ setTheme(theme: 'light' | 'dark'): void;
97
+ getState(): EditorState;
98
+ getBlocks(): Block[];
99
+ /**
100
+ * 본문 블록들을 직렬화 가능한 BlockSnapshot 배열로 반환한다.
101
+ * AerizViewer.setBlocks(snapshots) 가 받아 동일한 트리를 렌더할 수 있다.
102
+ */
103
+ serialize(): BlockSnapshot[];
104
+ /**
105
+ * data-block-id 로 본문에서 블록을 찾아 반환. 없으면 null.
106
+ */
107
+ findBlockById(id: string): Block | null;
108
+ getActiveBlock(): Block | null;
109
+ /**
110
+ * 블록 유형을 변경/추가한다.
111
+ * - `targetBlockId` 지정 시 그 id 의 블록을 강제 대상으로 한다 (다이얼로그
112
+ * 확인 등으로 선택이 다른 곳으로 넘어간 뒤에도 원래 블록을 정확히 변환).
113
+ * - 미지정 시 현재 active block 사용. 둘 다 없으면 본문 끝에 새 블록 추가.
114
+ */
115
+ setBlockType(kind: BlockKind, attrs?: BlockAttrs, targetBlockId?: string): void;
116
+ private replaceBlock;
117
+ private appendBlock;
118
+ onStateChange(listener: EditorStateListener): () => void;
119
+ destroy(): void;
120
+ /**
121
+ * DOM 변화 후 호출되는 reconcile 진입점.
122
+ * 누락된 data-block-id 를 채우고, 자기 쓰기로 발생한 옵저버 레코드를
123
+ * takeRecords 로 흡수한 뒤 onChange + queueSync.
124
+ */
125
+ private flush;
126
+ /**
127
+ * 지금 본문을 이력에 기록한다.
128
+ *
129
+ * 되돌리기가 만든 변경은 기록하지 않는다 — 자기 자신을 이력에 쌓으면
130
+ * 되돌리기를 되돌릴 수 없다.
131
+ */
132
+ private recordHistory;
133
+ /**
134
+ * 유형이 막 바뀐 블록을 한 번 반짝인다.
135
+ *
136
+ * 유형 변경은 결과가 화면 아래쪽(들여쓰기 · 글꼴 · 배경) 에서 조용히 일어나
137
+ * "눌리긴 한 건가" 싶을 때가 있다. 짧은 하이라이트 하나로 어느 블록이
138
+ * 바뀌었는지가 분명해진다.
139
+ *
140
+ * 표식은 `data-block-flash` 속성이다 — class 로 걸면 사용자 컨텐츠의 class 에
141
+ * 섞여 `getHTML()` 로 새어 나갈 수 있다. data-block-* 는 출력 단계에서
142
+ * 통째로 제거되는 에디터 전용 네임스페이스다.
143
+ *
144
+ * 속성을 넣고 뺄 때마다 옵저버 레코드를 흡수한다. 이걸 빠뜨리면 이 표식
145
+ * 하나가 "문서가 바뀌었다" 로 잡혀 이력에 항목이 하나 더 쌓이고, 되돌리기가
146
+ * 눈에 보이는 변화 없이 한 번 헛돈다 (실제로 그랬다).
147
+ */
148
+ private flashBlock;
149
+ /**
150
+ * 이어지는 한 번의 변경을 **구조 변경**으로 표시한다.
151
+ *
152
+ * 블록 이동 · 유형 변경 · 표 편집처럼 사용자가 "한 번에 하나씩" 되돌리길
153
+ * 기대하는 조작 직전에 부른다. 이 표시가 없으면 직전 타이핑과 한 항목으로
154
+ * 합쳐져 Ctrl+Z 한 번에 둘 다 사라진다.
155
+ */
156
+ private markStructuralChange;
157
+ /** 되돌리기. 되돌릴 것이 없으면 아무 일도 하지 않는다. */
158
+ undo(): void;
159
+ /** 다시 실행. */
160
+ redo(): void;
161
+ private applyHistoryEntry;
162
+ private handleToolbarAction;
163
+ /**
164
+ * 선택 영역의 인라인 서식을 지운다. 블록 유형은 건드리지 않는다 —
165
+ * "서식 지우기" 로 제목이 본문이 되면 되돌릴 방법을 찾기 어렵다.
166
+ */
167
+ private clearInlineFormat;
168
+ /**
169
+ * 인라인 마크(bold/italic/underline/strike) 토글. 활성 블록의 allowedMarks
170
+ * 에 포함되지 않은 마크면 무시. styleWithCSS=true 모드로 deprecated 태그
171
+ * (<b>/<i>/<u>) 대신 `<span style="font-weight:bold">` 형태가 만들어진다.
172
+ *
173
+ * 'strike' 는 execCommand 표준 명령명이 'strikeThrough'.
174
+ */
175
+ private applyMark;
176
+ /**
177
+ * 색상 선택기에서 받은 색을 LRU 에 누적하고 selection 에 적용한다.
178
+ * `command` 가 'foreColor' 면 글자색, 'hiliteColor' 면 글 배경색.
179
+ */
180
+ private handleColorPick;
181
+ /**
182
+ * selection 에 색상을 적용. styleWithCSS=true 모드로 `<span style="color:…">`
183
+ * 또는 `<span style="background-color:…">` 형태가 만들어진다.
184
+ * `null` 은 '기본색' — 'inherit' 으로 부모 색을 따라가도록.
185
+ */
186
+ private applyColor;
187
+ /**
188
+ * 정렬을 활성 블록(또는 표 셀) 에 적용한다.
189
+ *
190
+ * execCommand('justify*') 는 일부 브라우저에서 blockquote 안의 <p> 에 적용
191
+ * 시 blockquote 를 <div style="text-align:..."> 로 바꿔버리는 잘 알려진
192
+ * 버그가 있어 직접 DOM 조작으로 대체한다.
193
+ *
194
+ * 적용 대상:
195
+ * paragraph / heading → element 자체
196
+ * blockquote → 안의 <p> (없으면 자기 자신)
197
+ * table 단일 셀 → 캐럿이 들어 있는 td/th
198
+ * table 다중 셀 마킹 → 각 cell.style.textAlign 일괄
199
+ */
200
+ private applyAlign;
201
+ /** 캐럿이 들어 있는 표 셀(td/th) 반환. 표 밖이면 null. */
202
+ private getActiveTableCell;
203
+ /**
204
+ * 현재 활성 셀(들) 을 반환한다.
205
+ * 1순위: 커스텀 드래그-셀렉션으로 마킹된 `.aw-cell-selected` 셀들
206
+ * 2순위: 브라우저 selection range 가 가리키는 표 셀(들)
207
+ * 3순위: 캐럿이 들어 있는 단일 셀
208
+ */
209
+ private getSelectedTableCells;
210
+ private cellSelStart;
211
+ private cellSelDrag;
212
+ private handleCellSelStart;
213
+ private handleCellSelMove;
214
+ private handleCellSelEnd;
215
+ /** `.aw-cell-selected` 마킹을 모두 제거. */
216
+ private clearCellSelection;
217
+ /**
218
+ * 표 셀 배열 각각에 `cell.style.fontSize` 를 직접 설정. Range API 의 wrap
219
+ * 경로(셀 중첩 버그 발생) 를 우회하기 위한 안전한 다중 셀 적용 경로.
220
+ */
221
+ private applyFontSizeToCells;
222
+ /**
223
+ * 캡처해 둔 셀(들) 에 배경색을 일괄 적용. null 이면 inline background-color
224
+ * 제거. `<th>` 의 stylesheet 배경(0,1,2 specificity) 보다 inline + !important
225
+ * 가 항상 우선하도록 setProperty('important') 로 박는다.
226
+ */
227
+ private applyCellBgToCells;
228
+ /** 표 편집 드롭다운에서 받은 액션을 분기 실행. */
229
+ private runTableMenu;
230
+ /** 활성 셀의 행 위/아래에 새 행 삽입. 같은 열 수, 빈 td 로 채움. */
231
+ private insertRow;
232
+ /** 활성 셀이 속한 열의 좌/우에 새 열 삽입. 모든 행에 빈 td 추가. */
233
+ private insertColumn;
234
+ /** 활성 셀의 행 전체 삭제. 마지막 행이면 보존(빈 표 회피). */
235
+ private deleteRow;
236
+ /**
237
+ * 코드 블록 input 마다 호출되는 debounced rehighlight. 활성 블록이 codeBlock
238
+ * 이고 data-language 가 설정돼 있을 때만 동작 — 타이핑 멈춘 뒤 300ms 후 1 회.
239
+ */
240
+ private codeHighlightTimer;
241
+ private scheduleCodeHighlight;
242
+ /**
243
+ * code 블록의 언어를 변경하고 즉시 highlight 를 다시 적용. 캐럿 위치는
244
+ * 텍스트 offset 으로 보존.
245
+ */
246
+ private applyCodeLanguage;
247
+ /**
248
+ * `<code>` element 의 텍스트를 highlight 후 innerHTML 로 갱신. 셀렉션이
249
+ * code 안에 있다면 textContent offset 으로 caret 위치 보존.
250
+ */
251
+ private rehighlightCode;
252
+ /**
253
+ * 본문에서 모든 codeBlock 을 순회하며 highlight 적용. setHTML / 초기 로드
254
+ * / 외부 import 후 호출.
255
+ */
256
+ private rehighlightAllCodeBlocks;
257
+ /** 활성 셀의 열 전체 삭제. 마지막 열이면 보존(빈 표 회피). */
258
+ private deleteColumn;
259
+ /**
260
+ * 마우스 이동 시 셀 우/하 가장자리에서 col-resize / row-resize 커서 표시.
261
+ * 드래그 진행 중에는 별도 핸들러가 cursor 를 관리하므로 skip.
262
+ */
263
+ private handleTableCursor;
264
+ private tableResize;
265
+ /**
266
+ * 셀 가장자리 / 이미지·유튜브 리사이즈 핸들 mousedown 진입점.
267
+ * - 셀: 우(col-resize) / 하(row-resize) 가장자리 6px 영역
268
+ * - 이미지·유튜브: 우하단 `.aw-resize-handle` element (corner resize)
269
+ */
270
+ private handleTableResizeStart;
271
+ private handleTableResizeMove;
272
+ /**
273
+ * 체크리스트 항목의 체크 표시 토글.
274
+ *
275
+ * 체크박스는 실제 element 가 아니라 `<li>` 의 `::before` 다 — 폼 요소를
276
+ * contentEditable 안에 두면 캐럿이 그 안으로 들어가고 서버 sanitizer 가
277
+ * 지워 버린다. 그래서 클릭 좌표가 그 표시 영역 안인지 직접 따진다.
278
+ *
279
+ * `click` 이 아니라 `mousedown` 에서 처리한다 — click 까지 기다리면 그
280
+ * 사이에 캐럿이 항목 안으로 들어가 화면이 한 번 흔들린다.
281
+ */
282
+ private handleTaskToggle;
283
+ /**
284
+ * 이미지 캡션(`<figcaption>`) 에 캐럿을 둔다. 없으면 만든다.
285
+ *
286
+ * 버튼은 토글이 아니다 — 다시 눌러 캡션을 지우면 사용자가 쓴 글이 말없이
287
+ * 사라진다. 캡션을 없애는 방법은 글자를 지우는 것이고, 빈 캡션은 캐럿이
288
+ * 떠난 뒤 `normalizeContent` 가 알아서 걷어낸다.
289
+ */
290
+ private focusImageCaption;
291
+ /**
292
+ * 이미지/유튜브 좌상단 정렬 컨트롤 클릭 핸들러. data-align 에 따라 wrapper
293
+ * 의 marginLeft / marginRight 를 조정 — left/center/right.
294
+ */
295
+ private handleBlockAlignClick;
296
+ /**
297
+ * 이미지 / 유튜브 wrapper 의 너비를 드래그로 조정. 높이는 자동 (이미지는
298
+ * 종횡비 유지, 유튜브는 CSS aspect-ratio 16/9). mouseup 에서 cursor 복귀
299
+ * + flush.
300
+ *
301
+ * 너비는 본문 안쪽 폭을 상한으로 한다. 상한이 없으면 오른쪽으로 계속 끌 때
302
+ * wrapper 가 본문보다 넓어지고, 그만큼 에디터 전체가 가로로 늘어난다
303
+ * (드래그를 멈춰도 늘어난 상태가 남는다).
304
+ */
305
+ private startBlockResize;
306
+ /**
307
+ * 본문의 padding 을 제외한 실제 컨텐츠 폭. 블록 리사이즈의 상한이다.
308
+ * 에디터가 아주 좁을 때도 최소 폭은 확보되도록 하한을 둔다.
309
+ */
310
+ private contentInnerWidth;
311
+ private currentTheme;
312
+ /**
313
+ * 선택 영역에 글꼴을 적용한다.
314
+ * `styleWithCSS=true` 모드의 execCommand('fontName') 가 `<font face=…>` 대신
315
+ * `<span style="font-family: …">` 를 생성하도록 강제한다. selection 이
316
+ * 비어 있으면(캐럿만) 다음 입력 글꼴이 결정된다(브라우저 기본 동작).
317
+ */
318
+ private applyFontFamily;
319
+ /**
320
+ * 현재 selection 의 anchor 노드 기준 computed font-family 를 읽어 알려진
321
+ * FONT_FAMILIES id 와 매칭. 글꼴 드롭다운의 active 표시에 사용.
322
+ */
323
+ private detectActiveFontId;
324
+ /**
325
+ * 선택 영역에 글자 크기를 적용한다. execCommand('fontSize') 는 1~7 만 받기
326
+ * 때문에 px 적용을 위해 Range API 로 직접 `<span style="font-size: Xpx">`
327
+ * wrap 한다.
328
+ *
329
+ * **다중 셀 선택**(커스텀 마킹 또는 셀 경계를 넘는 browser range) 의 경우엔
330
+ * Range.extractContents 가 셀들을 묶어 한 셀 안에 끼워넣는 셀 중첩 버그가
331
+ * 있어 — 셀별로 `cell.style.fontSize` 를 직접 설정하는 안전한 경로를 사용.
332
+ */
333
+ private applyFontSize;
334
+ /**
335
+ * 현재 selection 의 anchor 노드 기준 computed font-size 를 px 정수로 읽어
336
+ * FONT_SIZES 의 값과 일치할 때만 반환. 글자 크기 드롭다운의 active 표시에
337
+ * 사용.
338
+ */
339
+ private detectActiveFontSize;
340
+ /**
341
+ * 블록 유형 드롭다운을 열어 사용자 선택 결과를 처리한다.
342
+ * 툴바의 blockType 버튼 / 활성 블록 라벨 pill 양쪽 진입점이 동일한 분기
343
+ * 로직(리치 블록 다이얼로그 분기 + targetId 보존) 을 공유한다.
344
+ */
345
+ private openBlockTypeMenu;
346
+ /**
347
+ * 링크 다이얼로그를 열어 URL/텍스트를 받은 뒤 linkCard 블록으로 변환/추가.
348
+ * 표시 텍스트가 비어 있으면 URL 을 텍스트로 사용. `targetId` 가 있으면
349
+ * 그 id 의 블록을 정확히 변환 대상으로 삼는다.
350
+ */
351
+ private openLinkDialogAndApply;
352
+ /**
353
+ * 이미지 다이얼로그(임베드/URL/업로드)를 열어 src 를 받은 뒤 image 블록으로
354
+ * 변환/추가.
355
+ */
356
+ private openImageDialogAndApply;
357
+ /**
358
+ * 유튜브 다이얼로그를 열어 URL 을 받고, 영상 ID 를 추출해 youtube 블록으로
359
+ * 변환/추가. 잘못된 URL 이면 무시.
360
+ */
361
+ private openYouTubeDialogAndApply;
362
+ /**
363
+ * HTML 가져오기 다이얼로그를 열어 사용자가 고른 HTML 을 iframe 으로 로드/
364
+ * 처리한 뒤 본문에 element 들을 삽입한다.
365
+ *
366
+ * - `targetId` 가 있으면 그 블록 바로 뒤에 삽입, 없으면 본문 끝에 append.
367
+ * - 삽입된 element 는 MutationObserver flush 가 새 data-block-id 를 발급.
368
+ */
369
+ private openImportHtmlDialogAndApply;
370
+ /**
371
+ * Markdown 가져오기 다이얼로그를 열어 사용자가 입력한 markdown 을 파싱해
372
+ * 본문에 element 들을 삽입한다. importHtml 과 동일한 삽입 정책을 따른다.
373
+ */
374
+ private openMarkdownImportDialogAndApply;
375
+ /**
376
+ * 파일 첨부 다이얼로그(URL/업로드)를 열어 src 를 받은 뒤 file 블록으로
377
+ * 변환/추가. src 가 비어 있으면 무시. 파일명이 없으면 URL 의 마지막
378
+ * segment 를 fallback 으로 사용.
379
+ */
380
+ private openFileAttachmentDialogAndApply;
381
+ /**
382
+ * 파일 업로드 핸들러를 결정. onFileUpload 가 우선, 없으면 fileUploadUrl 에
383
+ * multipart/form-data POST 후 응답 JSON 의 `url` 필드를 사용. 둘 다 없으면
384
+ * undefined → 다이얼로그 업로드 탭 비활성.
385
+ */
386
+ private resolveFileUploadFn;
387
+ /**
388
+ * 이미지 업로드 핸들러를 결정. options.onImageUpload 가 우선이며, 없으면
389
+ * options.imageUploadUrl 로 multipart/form-data POST 후 응답 JSON 의
390
+ * `url` 필드를 사용. 둘 다 없으면 undefined → 다이얼로그 업로드 탭 비활성.
391
+ */
392
+ private resolveImageUploadFn;
393
+ private queueSync;
394
+ private sync;
395
+ private handleKeyDown;
396
+ /**
397
+ * 마크다운 입력 규칙.
398
+ *
399
+ * 본문 블록 맨 앞에 `# ` / `- ` / `1. ` / `> ` / ``` 을 치면 그 자리에서
400
+ * 블록 유형이 바뀌고 방아쇠 문자는 사라진다. 메뉴를 열지 않고 글을 쓰던
401
+ * 흐름 그대로 유형을 고르는 길이다.
402
+ *
403
+ * 조건을 좁게 잡는다 — **본문 블록**에서, **캐럿이 그 텍스트 바로 뒤**에
404
+ * 있을 때만. 그렇지 않으면 이미 쓴 문단 중간에서 "- " 를 고치다가 블록이
405
+ * 통째로 바뀌는 사고가 난다.
406
+ */
407
+ private handleInputRule;
408
+ /**
409
+ * 글머리 목록 항목에서 `[ ] ` / `[x] ` 를 알아보고 체크리스트로 바꾼다.
410
+ *
411
+ * `setBlockType` 을 쓰지 않는다 — 그 경로는 항목들을 `<br>` 로 이어 붙여
412
+ * 한 덩어리로 만들기 때문에, 항목이 여럿인 목록에서 줄이 뭉개진다. 목록
413
+ * 구조는 그대로 두고 클래스만 올린다.
414
+ */
415
+ private applyTaskItemRule;
416
+ /**
417
+ * 블록 단축키. 처리했으면 `true`.
418
+ *
419
+ * 유형 변경은 `Ctrl/Cmd + Alt + 숫자` 다 — 구글 문서 · 노션이 같은 자리를
420
+ * 쓰기 때문에 손이 이미 알고 있는 배열이고, 브라우저 기본 단축키
421
+ * (Ctrl+숫자 = 탭 전환) 와 겹치지 않는다. 어느 유형의 블록에서든 다른
422
+ * 유형으로 바로 넘어간다.
423
+ *
424
+ * Ctrl+Alt+0 본문 Ctrl+Alt+1~6 제목 1~6
425
+ * Ctrl+Alt+7 번호 목록 Ctrl+Alt+8 글머리 목록
426
+ * Ctrl+Alt+9 인용구 Ctrl+Alt+C 코드 블록
427
+ * Ctrl+Shift+↑/↓ 블록 이동 Ctrl+Shift+D 블록 삭제
428
+ *
429
+ * 이동과 삭제는 캐럿이 어디 있든 **블록 단위**로 동작한다. 지금까지는
430
+ * 드래그 핸들을 잡는 것 말고는 블록을 옮길 방법이 없었다.
431
+ */
432
+ private handleBlockShortcut;
433
+ /**
434
+ * 활성 블록을 위/아래 형제와 자리바꿈한다. 문서 끝에서는 아무 일도 하지
435
+ * 않는다 — 경고 없이 조용히 멈추는 편이 캐럿을 잃는 것보다 낫다.
436
+ */
437
+ private moveActiveBlock;
438
+ /**
439
+ * 활성 블록을 지우고 캐럿을 이웃 블록으로 옮긴다.
440
+ *
441
+ * 마지막 블록을 지우면 `normalizeContent` 가 빈 본문 블록을 다시 만든다 —
442
+ * 본문이 블록 없는 상태로 남지 않는다.
443
+ */
444
+ private deleteActiveBlock;
445
+ /**
446
+ * Tab 처리. 처리했으면 `true`.
447
+ *
448
+ * - 표 안: 다음(Shift 면 이전) 셀로 캐럿 이동. 마지막 셀에서 Tab 은 새 행을
449
+ * 만든다 — 표를 채워 넣을 때 손이 마우스로 갈 일이 없어진다.
450
+ * - 목록 안: `indent` / `outdent` 로 한 단계 들여쓰기. 중첩 목록을 만들
451
+ * 방법이 이것 말고는 없었다(마크다운으로 가져온 중첩 목록은 편집만 가능).
452
+ */
453
+ private handleTabKey;
454
+ /**
455
+ * Backspace 처리. 처리했으면 `true`.
456
+ *
457
+ * 비어 있는 제목 · 인용구 · 코드 블록 · 목록의 맨 앞에서 Backspace 를 누르면
458
+ * **본문 블록으로 되돌린다.** 유형을 잘못 골랐을 때 되돌리는 가장 자연스러운
459
+ * 동작이고, 브라우저 기본 동작(앞 블록에 병합) 이 만들어 내는 이상한 중첩
460
+ * (제목 안에 목록이 들어가는 등) 도 함께 피한다.
461
+ */
462
+ private handleBackspaceKey;
463
+ /**
464
+ * 활성 블록 뒤에 새 빈 **본문 블록**을 만들고 캐럿을 그 안에 둔다.
465
+ * 인용구 / 코드 블록의 Enter 탈출, 제목 끝 Enter 에 사용.
466
+ */
467
+ private exitBlockOnEnter;
468
+ private syncBlockHandles;
469
+ /**
470
+ * 컨트롤(드래그 핸들 · 단락 추가) 은 **지금 다루고 있는 블록** 에만 보인다.
471
+ * 포인터가 놓인 블록과 캐럿이 있는 블록 두 가지가 그 대상이다.
472
+ *
473
+ * 예전에는 본문에 포인터만 올라가면 모든 블록의 핸들이 한꺼번에 흐리게
474
+ * 떠올랐다. 글이 길어질수록 왼쪽에 점 세 개짜리 기둥이 생겨서, 정작 어느
475
+ * 블록을 잡는 손잡이인지가 안 보였다.
476
+ */
477
+ private syncHandleVisibility;
478
+ private readonly handleHoverMove;
479
+ private readonly handleHoverLeave;
480
+ private setHoveredBlock;
481
+ /**
482
+ * 뷰포트 y 좌표가 걸치는 블록의 id. 좌표가 블록들 사이의 여백이면 가장
483
+ * 가까운 블록으로 붙인다 — 문단 사이 간격에서 컨트롤이 깜빡이지 않게.
484
+ */
485
+ private blockIdAtViewportY;
486
+ private repositionHandles;
487
+ private positionHandle;
488
+ private createBlockHandle;
489
+ /**
490
+ * `blockId` 의 블록 뒤에 빈 단락을 삽입하고 캐럿을 그 단락으로 이동.
491
+ * MutationObserver flush 가 새 단락에 신규 data-block-id 를 발급한다.
492
+ */
493
+ private insertParagraphAfter;
494
+ private handleDragOver;
495
+ private handleDrop;
496
+ private handleDragLeave;
497
+ private findDropTarget;
498
+ private showDropIndicator;
499
+ private hideDropIndicator;
500
+ /**
501
+ * 본문 블록들에 data-block-kind / data-block-label 을 갱신하고,
502
+ * 활성 블록에 data-block-focused 를 표기한다. CSS 가 이 속성을 읽어
503
+ * 포커스 인디케이터와 라벨 태그를 그린다.
504
+ *
505
+ * 자기 쓰기로 발생한 attributes 레코드는 takeRecords 로 흡수.
506
+ */
507
+ /**
508
+ * 활성 블록 위쪽에 떠 있는 인터랙티브 라벨 pill 갱신.
509
+ * - 활성 블록이 없으면 숨김
510
+ * - 활성 블록의 라벨 텍스트로 갱신, 위치를 블록 상단에 맞춤
511
+ * - 클릭 시 openBlockTypeDropdown 가 열려 블록 유형을 즉시 변경 가능
512
+ */
513
+ private syncBlockLabelPill;
514
+ /**
515
+ * 하단 Status Bar 갱신.
516
+ * - 좌측: 활성 블록 라벨 + id (없으면 — )
517
+ * - 우측: 총 블록 수, 본문 글자 수(공백 제외)
518
+ */
519
+ private syncStatusBar;
520
+ /**
521
+ * 되돌리기 / 다시 실행 버튼의 활성 상태.
522
+ *
523
+ * 누를 수 없는 버튼이 누를 수 있는 것처럼 보이면, 눌러 보고 아무 일도 안
524
+ * 일어나는 경험을 준다. 이력 끝에 닿았다는 사실 자체가 정보다.
525
+ */
526
+ private syncHistoryButtons;
527
+ private syncBlockMarkers;
528
+ private applyToolbarState;
529
+ }
530
+ // (소스맵은 CJS 선언 사본에서 제외됨 — scripts/finalize-types.mjs)