@moda-editor/core 0.1.1
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.
- package/LICENSE +21 -0
- package/README.md +16 -0
- package/dist/commands.d.ts +78 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +299 -0
- package/dist/commands.js.map +1 -0
- package/dist/conformance.d.ts +59 -0
- package/dist/conformance.d.ts.map +1 -0
- package/dist/conformance.js +1609 -0
- package/dist/conformance.js.map +1 -0
- package/dist/core.d.ts +37 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +117 -0
- package/dist/core.js.map +1 -0
- package/dist/editor/editor.d.ts +104 -0
- package/dist/editor/editor.d.ts.map +1 -0
- package/dist/editor/editor.js +204 -0
- package/dist/editor/editor.js.map +1 -0
- package/dist/editor/extension.d.ts +170 -0
- package/dist/editor/extension.d.ts.map +1 -0
- package/dist/editor/extension.js +2 -0
- package/dist/editor/extension.js.map +1 -0
- package/dist/features/ace.d.ts +9 -0
- package/dist/features/ace.d.ts.map +1 -0
- package/dist/features/ace.js +144 -0
- package/dist/features/ace.js.map +1 -0
- package/dist/features/alignment.d.ts +9 -0
- package/dist/features/alignment.d.ts.map +1 -0
- package/dist/features/alignment.js +28 -0
- package/dist/features/alignment.js.map +1 -0
- package/dist/features/blockquote.d.ts +9 -0
- package/dist/features/blockquote.d.ts.map +1 -0
- package/dist/features/blockquote.js +65 -0
- package/dist/features/blockquote.js.map +1 -0
- package/dist/features/code-block.d.ts +77 -0
- package/dist/features/code-block.d.ts.map +1 -0
- package/dist/features/code-block.js +986 -0
- package/dist/features/code-block.js.map +1 -0
- package/dist/features/doc.d.ts +15 -0
- package/dist/features/doc.d.ts.map +1 -0
- package/dist/features/doc.js +79 -0
- package/dist/features/doc.js.map +1 -0
- package/dist/features/feature.d.ts +26 -0
- package/dist/features/feature.d.ts.map +1 -0
- package/dist/features/feature.js +8 -0
- package/dist/features/feature.js.map +1 -0
- package/dist/features/hard-break.d.ts +9 -0
- package/dist/features/hard-break.d.ts.map +1 -0
- package/dist/features/hard-break.js +48 -0
- package/dist/features/hard-break.js.map +1 -0
- package/dist/features/heading.d.ts +3 -0
- package/dist/features/heading.d.ts.map +1 -0
- package/dist/features/heading.js +31 -0
- package/dist/features/heading.js.map +1 -0
- package/dist/features/horizontal-rule.d.ts +9 -0
- package/dist/features/horizontal-rule.d.ts.map +1 -0
- package/dist/features/horizontal-rule.js +64 -0
- package/dist/features/horizontal-rule.js.map +1 -0
- package/dist/features/image.d.ts +12 -0
- package/dist/features/image.d.ts.map +1 -0
- package/dist/features/image.js +116 -0
- package/dist/features/image.js.map +1 -0
- package/dist/features/index.d.ts +31 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/index.js +65 -0
- package/dist/features/index.js.map +1 -0
- package/dist/features/inline-style.d.ts +12 -0
- package/dist/features/inline-style.d.ts.map +1 -0
- package/dist/features/inline-style.js +126 -0
- package/dist/features/inline-style.js.map +1 -0
- package/dist/features/link.d.ts +29 -0
- package/dist/features/link.d.ts.map +1 -0
- package/dist/features/link.js +198 -0
- package/dist/features/link.js.map +1 -0
- package/dist/features/lists.d.ts +14 -0
- package/dist/features/lists.d.ts.map +1 -0
- package/dist/features/lists.js +178 -0
- package/dist/features/lists.js.map +1 -0
- package/dist/features/marks.d.ts +6 -0
- package/dist/features/marks.d.ts.map +1 -0
- package/dist/features/marks.js +68 -0
- package/dist/features/marks.js.map +1 -0
- package/dist/features/script.d.ts +2 -0
- package/dist/features/script.d.ts.map +1 -0
- package/dist/features/script.js +55 -0
- package/dist/features/script.js.map +1 -0
- package/dist/features/table.d.ts +20 -0
- package/dist/features/table.d.ts.map +1 -0
- package/dist/features/table.js +195 -0
- package/dist/features/table.js.map +1 -0
- package/dist/features/task-list.d.ts +10 -0
- package/dist/features/task-list.d.ts.map +1 -0
- package/dist/features/task-list.js +68 -0
- package/dist/features/task-list.js.map +1 -0
- package/dist/features/testkit.d.ts +32 -0
- package/dist/features/testkit.d.ts.map +1 -0
- package/dist/features/testkit.js +92 -0
- package/dist/features/testkit.js.map +1 -0
- package/dist/history.d.ts +40 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +192 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/model/node.d.ts +47 -0
- package/dist/model/node.d.ts.map +1 -0
- package/dist/model/node.js +30 -0
- package/dist/model/node.js.map +1 -0
- package/dist/model/schema.d.ts +105 -0
- package/dist/model/schema.d.ts.map +1 -0
- package/dist/model/schema.js +46 -0
- package/dist/model/schema.js.map +1 -0
- package/dist/model/traverse.d.ts +36 -0
- package/dist/model/traverse.d.ts.map +1 -0
- package/dist/model/traverse.js +123 -0
- package/dist/model/traverse.js.map +1 -0
- package/dist/mount.d.ts +24 -0
- package/dist/mount.d.ts.map +1 -0
- package/dist/mount.js +45 -0
- package/dist/mount.js.map +1 -0
- package/dist/parse/html.d.ts +8 -0
- package/dist/parse/html.d.ts.map +1 -0
- package/dist/parse/html.js +247 -0
- package/dist/parse/html.js.map +1 -0
- package/dist/parse/sanitize.d.ts +20 -0
- package/dist/parse/sanitize.d.ts.map +1 -0
- package/dist/parse/sanitize.js +116 -0
- package/dist/parse/sanitize.js.map +1 -0
- package/dist/presets/highlight.d.ts +23 -0
- package/dist/presets/highlight.d.ts.map +1 -0
- package/dist/presets/highlight.js +170 -0
- package/dist/presets/highlight.js.map +1 -0
- package/dist/presets/starter.d.ts +14 -0
- package/dist/presets/starter.d.ts.map +1 -0
- package/dist/presets/starter.js +18 -0
- package/dist/presets/starter.js.map +1 -0
- package/dist/state/apply.d.ts +64 -0
- package/dist/state/apply.d.ts.map +1 -0
- package/dist/state/apply.js +528 -0
- package/dist/state/apply.js.map +1 -0
- package/dist/state/navigate.d.ts +5 -0
- package/dist/state/navigate.d.ts.map +1 -0
- package/dist/state/navigate.js +81 -0
- package/dist/state/navigate.js.map +1 -0
- package/dist/state/selection.d.ts +44 -0
- package/dist/state/selection.d.ts.map +1 -0
- package/dist/state/selection.js +63 -0
- package/dist/state/selection.js.map +1 -0
- package/dist/state/state.d.ts +39 -0
- package/dist/state/state.d.ts.map +1 -0
- package/dist/state/state.js +123 -0
- package/dist/state/state.js.map +1 -0
- package/dist/state/transaction.d.ts +234 -0
- package/dist/state/transaction.d.ts.map +1 -0
- package/dist/state/transaction.js +988 -0
- package/dist/state/transaction.js.map +1 -0
- package/dist/styles.css +948 -0
- package/dist/types.d.ts +44 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/view/dom.d.ts +26 -0
- package/dist/view/dom.d.ts.map +1 -0
- package/dist/view/dom.js +249 -0
- package/dist/view/dom.js.map +1 -0
- package/dist/view/input.d.ts +45 -0
- package/dist/view/input.d.ts.map +1 -0
- package/dist/view/input.js +901 -0
- package/dist/view/input.js.map +1 -0
- package/dist/view/mount.d.ts +26 -0
- package/dist/view/mount.d.ts.map +1 -0
- package/dist/view/mount.js +119 -0
- package/dist/view/mount.js.map +1 -0
- package/dist/view/render.d.ts +64 -0
- package/dist/view/render.d.ts.map +1 -0
- package/dist/view/render.js +203 -0
- package/dist/view/render.js.map +1 -0
- package/dist/view/serialize.d.ts +4 -0
- package/dist/view/serialize.d.ts.map +1 -0
- package/dist/view/serialize.js +37 -0
- package/dist/view/serialize.js.map +1 -0
- package/package.json +44 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @moda-editor/core 타입 정의.
|
|
3
|
+
*
|
|
4
|
+
* 이 파일은 템플릿의 골든 패스 예시 — 실제 라이브러리에서는
|
|
5
|
+
* 여기에 라이브러리의 공개 타입을 작성한다.
|
|
6
|
+
*/
|
|
7
|
+
/** 행 데이터 기본 타입 — 제네릭 TData는 object로 제한한다 */
|
|
8
|
+
export type RowData = Record<string, unknown>;
|
|
9
|
+
/** 코어 생성 옵션 */
|
|
10
|
+
export interface WidgetCoreOptions<TData extends object = RowData> {
|
|
11
|
+
/** 초기 항목 목록 */
|
|
12
|
+
items: TData[];
|
|
13
|
+
/** 항목의 표시·검색 텍스트 (기본값: String(item)) */
|
|
14
|
+
getItemText?: (item: TData) => string;
|
|
15
|
+
/** 항목 고유 ID — 목록 렌더 키로 사용 (기본값: 인덱스 문자열) */
|
|
16
|
+
getItemId?: (item: TData, index: number) => string;
|
|
17
|
+
/** 이벤트 핸들러 — on()과 같은 이름 */
|
|
18
|
+
events?: WidgetEventHandlers<TData>;
|
|
19
|
+
}
|
|
20
|
+
/** 구독자에게 전달되는 읽기 전용 스냅샷 — 어댑터는 이것만 렌더한다 */
|
|
21
|
+
export interface WidgetSnapshot<TData extends object = RowData> {
|
|
22
|
+
/** 전체 항목 */
|
|
23
|
+
items: readonly TData[];
|
|
24
|
+
/** searchText 필터가 적용된 항목 */
|
|
25
|
+
filteredItems: readonly TData[];
|
|
26
|
+
searchText: string;
|
|
27
|
+
totalCount: number;
|
|
28
|
+
filteredCount: number;
|
|
29
|
+
}
|
|
30
|
+
/** 발행되는 이벤트 이름 */
|
|
31
|
+
export type WidgetEventType = "searchChange" | "itemAdd";
|
|
32
|
+
export interface WidgetEventMap<TData extends object = RowData> {
|
|
33
|
+
searchChange: {
|
|
34
|
+
searchText: string;
|
|
35
|
+
};
|
|
36
|
+
itemAdd: {
|
|
37
|
+
item: TData;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** 이벤트 핸들러 맵 — 옵션/구독 양쪽에서 사용 */
|
|
41
|
+
export type WidgetEventHandlers<TData extends object = RowData> = {
|
|
42
|
+
[K in WidgetEventType]?: (e: WidgetEventMap<TData>[K]) => void;
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,4CAA4C;AAC5C,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE9C,eAAe;AACf,MAAM,WAAW,iBAAiB,CAAC,KAAK,SAAS,MAAM,GAAG,OAAO;IAC/D,eAAe;IACf,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,wCAAwC;IACxC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,MAAM,CAAC;IACtC,4CAA4C;IAC5C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACnD,4BAA4B;IAC5B,MAAM,CAAC,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC;CACrC;AAED,2CAA2C;AAC3C,MAAM,WAAW,cAAc,CAAC,KAAK,SAAS,MAAM,GAAG,OAAO;IAC5D,YAAY;IACZ,KAAK,EAAE,SAAS,KAAK,EAAE,CAAC;IACxB,4BAA4B;IAC5B,aAAa,EAAE,SAAS,KAAK,EAAE,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,kBAAkB;AAClB,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,SAAS,CAAC;AAEzD,MAAM,WAAW,cAAc,CAAC,KAAK,SAAS,MAAM,GAAG,OAAO;IAC5D,YAAY,EAAE;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,OAAO,EAAE;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE,CAAC;CAC1B;AAED,gCAAgC;AAChC,MAAM,MAAM,mBAAmB,CAAC,KAAK,SAAS,MAAM,GAAG,OAAO,IAAI;KAC/D,CAAC,IAAI,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;CAC/D,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Point } from "../state/selection.js";
|
|
2
|
+
/**
|
|
3
|
+
* DOM 좌표 ↔ doc Point 변환.
|
|
4
|
+
*
|
|
5
|
+
* render.ts의 DOM 계약 덕분에:
|
|
6
|
+
* - 각 AST 노드의 최외곽 엘리먼트가 data-me-path를 가진다
|
|
7
|
+
* - 텍스트 홀더 안의 위치 = 문자 오프셋
|
|
8
|
+
* - 컨테이너의 위치 = 자식 엘리먼트 인덱스 (AST 자식과 1:1)
|
|
9
|
+
* 브라우저 전용 모듈 — 어댑터와 mountEditor가 쓴다.
|
|
10
|
+
*/
|
|
11
|
+
export declare const PATH_ATTR = "data-me-path";
|
|
12
|
+
/** DOM 선택 (node, offset) → doc Point */
|
|
13
|
+
export declare function domToPoint(root: HTMLElement, domNode: Node, domOffset: number): Point | null;
|
|
14
|
+
/** doc Point → DOM (node, offset) — 셀렉션 복원용 */
|
|
15
|
+
export declare function pointToDOM(root: HTMLElement, point: Point): {
|
|
16
|
+
node: Node;
|
|
17
|
+
offset: number;
|
|
18
|
+
} | null;
|
|
19
|
+
/** 현재 브라우저 셀렉션이 root 안에 있으면 Point 쌍으로 읽는다 */
|
|
20
|
+
export declare function domSelection(root: HTMLElement): {
|
|
21
|
+
anchor: Point;
|
|
22
|
+
focus: Point;
|
|
23
|
+
} | null;
|
|
24
|
+
/** doc 셀렉션을 DOM 셀렉션으로 복원한다 */
|
|
25
|
+
export declare function applyDomSelection(root: HTMLElement, anchor: Point, focus: Point): void;
|
|
26
|
+
//# sourceMappingURL=dom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/view/dom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD;;;;;;;;GAQG;AAEH,eAAO,MAAM,SAAS,iBAAiB,CAAC;AAqDxC,wCAAwC;AACxC,wBAAgB,UAAU,CACxB,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,IAAI,EACb,SAAS,EAAE,MAAM,GAChB,KAAK,GAAG,IAAI,CA0Cd;AAED,+CAA+C;AAC/C,wBAAgB,UAAU,CACxB,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,KAAK,GACX;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAmFvC;AAED,6CAA6C;AAC7C,wBAAgB,YAAY,CAC1B,IAAI,EAAE,WAAW,GAChB;IAAE,MAAM,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,IAAI,CAmCxC;AAED,8BAA8B;AAC9B,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,KAAK,EACb,KAAK,EAAE,KAAK,GACX,IAAI,CAMN"}
|
package/dist/view/dom.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM 좌표 ↔ doc Point 변환.
|
|
3
|
+
*
|
|
4
|
+
* render.ts의 DOM 계약 덕분에:
|
|
5
|
+
* - 각 AST 노드의 최외곽 엘리먼트가 data-me-path를 가진다
|
|
6
|
+
* - 텍스트 홀더 안의 위치 = 문자 오프셋
|
|
7
|
+
* - 컨테이너의 위치 = 자식 엘리먼트 인덱스 (AST 자식과 1:1)
|
|
8
|
+
* 브라우저 전용 모듈 — 어댑터와 mountEditor가 쓴다.
|
|
9
|
+
*/
|
|
10
|
+
export const PATH_ATTR = "data-me-path";
|
|
11
|
+
function parsePath(el) {
|
|
12
|
+
const raw = el.getAttribute(PATH_ATTR) ?? "";
|
|
13
|
+
return raw === "" ? [] : raw.split(",").map(Number);
|
|
14
|
+
}
|
|
15
|
+
/** el 안에서 (node, offset) 앞의 문자 수 — 텍스트 홀더의 문자 오프셋 */
|
|
16
|
+
function textOffsetWithin(el, node, offset) {
|
|
17
|
+
if (node === el) {
|
|
18
|
+
// 자식 인덱스 → 앞쪽 자식들의 텍스트 길이 합
|
|
19
|
+
let count = 0;
|
|
20
|
+
for (let i = 0; i < offset; i++) {
|
|
21
|
+
count += el.childNodes[i]?.textContent?.length ?? 0;
|
|
22
|
+
}
|
|
23
|
+
return count;
|
|
24
|
+
}
|
|
25
|
+
let count = 0;
|
|
26
|
+
let done = false;
|
|
27
|
+
const walk = (n) => {
|
|
28
|
+
if (done)
|
|
29
|
+
return;
|
|
30
|
+
if (n === node) {
|
|
31
|
+
if (n.nodeType === 3) {
|
|
32
|
+
count += offset;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// (요소, 자식인덱스) 경계 — 인덱스 앞쪽 자식들의 텍스트만 센다.
|
|
36
|
+
// 전체 textContent를 더하면 (.me-cl, 0) 같은 줄 시작 클릭이
|
|
37
|
+
// 줄 끝으로 밀린다
|
|
38
|
+
for (let i = 0; i < offset; i++) {
|
|
39
|
+
count += n.childNodes[i]?.textContent?.length ?? 0;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
done = true;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (n.nodeType === 3) {
|
|
46
|
+
count += n.textContent?.length ?? 0;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
for (const c of Array.from(n.childNodes)) {
|
|
50
|
+
walk(c);
|
|
51
|
+
if (done)
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
walk(el);
|
|
56
|
+
return count;
|
|
57
|
+
}
|
|
58
|
+
/** 텍스트 홀더인가 — 안에 me-path 자식 엘리먼트가 없으면 텍스트 */
|
|
59
|
+
function isTextHolder(el) {
|
|
60
|
+
return el.querySelector(`[${PATH_ATTR}]`) === null;
|
|
61
|
+
}
|
|
62
|
+
/** DOM 선택 (node, offset) → doc Point */
|
|
63
|
+
export function domToPoint(root, domNode, domOffset) {
|
|
64
|
+
const el = domNode.nodeType === 1
|
|
65
|
+
? domNode
|
|
66
|
+
: domNode.parentElement;
|
|
67
|
+
const holder = el?.closest(`[${PATH_ATTR}]`);
|
|
68
|
+
if (!holder || !root.contains(holder))
|
|
69
|
+
return null;
|
|
70
|
+
const path = parsePath(holder);
|
|
71
|
+
// contenteditable=false 홀더(.me-node-view 등 atom) — 안쪽 오프셋은
|
|
72
|
+
// 의미가 없으므로 부모 경계로 환원한다. 앞쪽이면 노드 "앞", 아니면 "뒤"
|
|
73
|
+
if (holder !== root && holder.getAttribute("contenteditable") === "false") {
|
|
74
|
+
const idx = path[path.length - 1];
|
|
75
|
+
if (idx == null)
|
|
76
|
+
return { path: [], offset: 0 };
|
|
77
|
+
const before = domNode === holder
|
|
78
|
+
? domOffset === 0
|
|
79
|
+
: textOffsetWithin(holder, domNode, domOffset) === 0;
|
|
80
|
+
return { path: path.slice(0, -1), offset: idx + (before ? 0 : 1) };
|
|
81
|
+
}
|
|
82
|
+
if (domNode !== holder && isTextHolder(holder)) {
|
|
83
|
+
return { path, offset: textOffsetWithin(holder, domNode, domOffset) };
|
|
84
|
+
}
|
|
85
|
+
// 텍스트 홀더의 컨테이너 경계 — 자식 인덱스를 문자 오프셋으로 환산
|
|
86
|
+
// (코드블록의 .me-cl 줄 인덱스가 AST 리프 인덱스가 아니다)
|
|
87
|
+
if (domNode === holder && isTextHolder(holder)) {
|
|
88
|
+
return { path, offset: textOffsetWithin(holder, holder, domOffset) };
|
|
89
|
+
}
|
|
90
|
+
// 컨테이너 경계 — 자식 인덱스는 AST 자식(요소)만 센다. childNodes
|
|
91
|
+
// 인덱스 그대로 쓰면 공백 텍스트 노드만큼 어긋난다
|
|
92
|
+
if (domNode === holder) {
|
|
93
|
+
let count = 0;
|
|
94
|
+
for (let i = 0; i < domOffset; i++) {
|
|
95
|
+
if (holder.childNodes[i]?.nodeType === 1)
|
|
96
|
+
count++;
|
|
97
|
+
}
|
|
98
|
+
return { path, offset: count };
|
|
99
|
+
}
|
|
100
|
+
// holder 직계 자식도 아니고 텍스트도 아닌 위치 — 가장 가까운 자식 경계로
|
|
101
|
+
let child = el;
|
|
102
|
+
while (child && child.parentElement !== holder)
|
|
103
|
+
child = child.parentElement;
|
|
104
|
+
if (!child)
|
|
105
|
+
return { path, offset: 0 };
|
|
106
|
+
const index = Array.prototype.indexOf.call(holder.children, child);
|
|
107
|
+
return { path, offset: Math.max(0, index) };
|
|
108
|
+
}
|
|
109
|
+
/** doc Point → DOM (node, offset) — 셀렉션 복원용 */
|
|
110
|
+
export function pointToDOM(root, point) {
|
|
111
|
+
// 루트 포인트(path=[])는 root 자신 — querySelector는 자손만 검색해
|
|
112
|
+
// 자기 자신의 data-me-path=""는 매칭되지 않는다
|
|
113
|
+
const holder = point.path.length === 0
|
|
114
|
+
? root
|
|
115
|
+
: root.querySelector(`[${PATH_ATTR}="${point.path.join(",")}"]`);
|
|
116
|
+
if (!holder)
|
|
117
|
+
return null;
|
|
118
|
+
if (isTextHolder(holder)) {
|
|
119
|
+
// 문자 오프셋 → 텍스트 노드 + 남은 오프셋
|
|
120
|
+
let rest = point.offset;
|
|
121
|
+
const walk = (n) => {
|
|
122
|
+
if (n.nodeType === 3) {
|
|
123
|
+
const len = n.textContent?.length ?? 0;
|
|
124
|
+
if (rest <= len)
|
|
125
|
+
return { node: n, offset: rest };
|
|
126
|
+
rest -= len;
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
for (const c of Array.from(n.childNodes)) {
|
|
130
|
+
const r = walk(c);
|
|
131
|
+
if (r)
|
|
132
|
+
return r;
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
};
|
|
136
|
+
const found = walk(holder);
|
|
137
|
+
if (found)
|
|
138
|
+
return found;
|
|
139
|
+
// 오프셋이 텍스트보다 길면 끝으로 클램프 — 요소 노드엔 자식
|
|
140
|
+
// 인덱스로 놓는다 (textContent 길이는 문자 수라 범위를 넘는다)
|
|
141
|
+
const last = holder.lastChild;
|
|
142
|
+
// 빈 텍스트 홀더 — 요소 안쪽이 유효한 유일한 위치다 (빈 문단 캐럿)
|
|
143
|
+
return last
|
|
144
|
+
? {
|
|
145
|
+
node: last,
|
|
146
|
+
offset: last.nodeType === 3
|
|
147
|
+
? (last.textContent?.length ?? 0)
|
|
148
|
+
: last.childNodes.length,
|
|
149
|
+
}
|
|
150
|
+
: { node: holder, offset: 0 };
|
|
151
|
+
}
|
|
152
|
+
// 컨테이너 경계를 리프 텍스트 위치로 내린다 — contenteditable 요소의
|
|
153
|
+
// (요소, 자식인덱스) 경계 셀렉션은 ce=false/중첩 editable을 건널 때
|
|
154
|
+
// Chromium이 조용히 collapse시킨다(네이티브 select-all 버그와 동일
|
|
155
|
+
// 원인). 오프셋 앞 자식의 첫 리프를 쓰되, 그 자식이 편집 불가
|
|
156
|
+
// (atom/.me-node-view)이면 이전 형제의 마지막 리프로 돌아간다
|
|
157
|
+
let childIdx = 0;
|
|
158
|
+
let domIdx = 0;
|
|
159
|
+
while (domIdx < holder.childNodes.length && childIdx < point.offset) {
|
|
160
|
+
if (holder.childNodes[domIdx]?.nodeType === 1)
|
|
161
|
+
childIdx++;
|
|
162
|
+
domIdx++;
|
|
163
|
+
}
|
|
164
|
+
const leafPos = (from, last) => {
|
|
165
|
+
const w = root.ownerDocument.createTreeWalker(from, 4);
|
|
166
|
+
const nodes = [];
|
|
167
|
+
for (let n = w.nextNode(); n; n = w.nextNode())
|
|
168
|
+
nodes.push(n);
|
|
169
|
+
if (last)
|
|
170
|
+
nodes.reverse();
|
|
171
|
+
for (const n of nodes) {
|
|
172
|
+
// 가장 가까운 contenteditable 조상이 false면 편집 위치가 아니다
|
|
173
|
+
// (아톰/노드뷰 크롬). 콜아웃 아울렛 안의 텍스트는 조상이 true라
|
|
174
|
+
// 통과한다
|
|
175
|
+
const ce = n.parentElement?.closest("[contenteditable]");
|
|
176
|
+
if (ce?.getAttribute("contenteditable") === "false")
|
|
177
|
+
continue;
|
|
178
|
+
return {
|
|
179
|
+
node: n,
|
|
180
|
+
offset: last ? (n.textContent?.length ?? 0) : 0,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
};
|
|
185
|
+
// 앞쪽 자식(경계 바로 뒤 노드)의 첫 리프
|
|
186
|
+
const next = holder.childNodes[domIdx];
|
|
187
|
+
if (next) {
|
|
188
|
+
const p = leafPos(next, false);
|
|
189
|
+
if (p)
|
|
190
|
+
return p;
|
|
191
|
+
}
|
|
192
|
+
// 없으면 이전 형제들의 마지막 리프를 거슬러 찾는다
|
|
193
|
+
for (let i = domIdx - 1; i >= 0; i--) {
|
|
194
|
+
const p = leafPos(holder.childNodes[i], true);
|
|
195
|
+
if (p)
|
|
196
|
+
return p;
|
|
197
|
+
}
|
|
198
|
+
return { node: holder, offset: domIdx };
|
|
199
|
+
}
|
|
200
|
+
/** 현재 브라우저 셀렉션이 root 안에 있으면 Point 쌍으로 읽는다 */
|
|
201
|
+
export function domSelection(root) {
|
|
202
|
+
const sel = root.ownerDocument.getSelection?.() ?? null;
|
|
203
|
+
if (!sel || sel.rangeCount === 0 || !sel.anchorNode)
|
|
204
|
+
return null;
|
|
205
|
+
const anchor = domToPoint(root, sel.anchorNode, sel.anchorOffset);
|
|
206
|
+
const focus = sel.focusNode
|
|
207
|
+
? domToPoint(root, sel.focusNode, sel.focusOffset)
|
|
208
|
+
: null;
|
|
209
|
+
if (anchor && focus)
|
|
210
|
+
return { anchor, focus };
|
|
211
|
+
// 한쪽 끝이 루트 밖이면(전체 선택의 anchor가 .me-root/body 경계에
|
|
212
|
+
// 오는 경우) 루트를 포괄하는 선택이므로 doc 가장자리로 클램프한다.
|
|
213
|
+
// 양쪽 다 밖이면 에디터와 무관한 선택 — null
|
|
214
|
+
if (anchor || focus) {
|
|
215
|
+
const clamp = (node, off) => {
|
|
216
|
+
if (!node)
|
|
217
|
+
return null;
|
|
218
|
+
try {
|
|
219
|
+
const range = root.ownerDocument.createRange();
|
|
220
|
+
range.selectNodeContents(root);
|
|
221
|
+
// -1: 루트보다 앞 → doc 시작, 1: 뒤 → doc 끝
|
|
222
|
+
const cmp = range.comparePoint(node, off);
|
|
223
|
+
return {
|
|
224
|
+
path: [],
|
|
225
|
+
offset: cmp <= 0
|
|
226
|
+
? 0
|
|
227
|
+
: root.querySelectorAll(":scope > [data-me-path]").length,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
const a = anchor ?? clamp(sel.anchorNode, sel.anchorOffset);
|
|
235
|
+
const f = focus ?? clamp(sel.focusNode, sel.focusOffset);
|
|
236
|
+
return a && f ? { anchor: a, focus: f } : null;
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
/** doc 셀렉션을 DOM 셀렉션으로 복원한다 */
|
|
241
|
+
export function applyDomSelection(root, anchor, focus) {
|
|
242
|
+
const a = pointToDOM(root, anchor);
|
|
243
|
+
const f = pointToDOM(root, focus);
|
|
244
|
+
const sel = root.ownerDocument.getSelection?.();
|
|
245
|
+
if (!a || !f || !sel)
|
|
246
|
+
return;
|
|
247
|
+
sel.setBaseAndExtent(a.node, a.offset, f.node, f.offset);
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=dom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom.js","sourceRoot":"","sources":["../../src/view/dom.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Editor } from "../editor/editor.js";
|
|
2
|
+
/**
|
|
3
|
+
* contenteditable ↔ 에디터 공용 입력 바인딩.
|
|
4
|
+
*
|
|
5
|
+
* 5개 렌더러가 공유하는 편집 루프:
|
|
6
|
+
* DOM beforeinput → preventDefault → 트랜잭션 디스패치
|
|
7
|
+
* (브라우저가 DOM을 직접 바꾸는 것을 막고 doc이 유일한 진실이 된다)
|
|
8
|
+
* 렌더 커밋 후 → restoreSelection()으로 doc 셀렉션을 DOM에 반영
|
|
9
|
+
* selectionchange → doc 셀렉션 동기화
|
|
10
|
+
*
|
|
11
|
+
* IME: composition 중 beforeinput/keydown은 전부 무시하고 브라우저가
|
|
12
|
+
* DOM을 직접 바꾸게 둔다. compositionend에서 더러워진 텍스트 홀더를
|
|
13
|
+
* doc과 diff해 차이만큼 delete+insert로 흡수한다. composing 해제는
|
|
14
|
+
* setTimeout으로 지연 — Safari는 compositionend 뒤 같은 태스크에
|
|
15
|
+
* 최종 beforeinput을 보내고, Chromium은 조합 확정 Enter의 keydown을
|
|
16
|
+
* 같은 태스크에 보내므로 둘 다 무시되어야 한다.
|
|
17
|
+
*/
|
|
18
|
+
export interface EditorInputBinding {
|
|
19
|
+
unbind(): void;
|
|
20
|
+
/** 렌더 커밋 후 호출 — doc 셀렉션을 DOM 셀렉션으로 복원 */
|
|
21
|
+
restoreSelection(): void;
|
|
22
|
+
/** DOM 셀렉션을 즉시 doc으로 동기화 */
|
|
23
|
+
syncDocSelection(): void;
|
|
24
|
+
/**
|
|
25
|
+
* 외부(IME 등)가 프레임워크 몰래 직접 바꾼 최상위 블록 경로.
|
|
26
|
+
* 브라우저가 변경한 DOM은 vnode와 어긋나므로 프레임워크 리컨실이
|
|
27
|
+
* 깨진다 (React removeChild 크래시 등) — 어댑터는 렌더할 때
|
|
28
|
+
* 해당 경로의 키 버전을 올려 블록을 통째 리마운트해야 한다.
|
|
29
|
+
* drain 방식: 어댑터가 소비한 항목은 지운다.
|
|
30
|
+
*/
|
|
31
|
+
readonly dirtyPaths: Set<string>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 어댑터 공용 dirty-key 헬퍼 — binding.dirtyPaths를 소비해 블록
|
|
35
|
+
* 렌더 키를 안정적으로 버전업한다. 일반 블록은 path 그대로,
|
|
36
|
+
* 외부 DOM 변경이 있었던 블록은 path@N으로 키가 바뀌어
|
|
37
|
+
* 프레임워크가 내부 diff 대신 리마운트를 택한다.
|
|
38
|
+
*/
|
|
39
|
+
export declare class DirtyKeyMap {
|
|
40
|
+
private readonly versions;
|
|
41
|
+
/** rn.path.join(",")와 binding.dirtyPaths를 넣으면 렌더 키를 반환 */
|
|
42
|
+
key(pathKey: string, dirty: Set<string> | undefined): string;
|
|
43
|
+
}
|
|
44
|
+
export declare function bindEditorInput(editor: Editor, el: HTMLElement): EditorInputBinding;
|
|
45
|
+
//# sourceMappingURL=input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/view/input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAoFlD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,IAAI,IAAI,CAAC;IACf,yCAAyC;IACzC,gBAAgB,IAAI,IAAI,CAAC;IACzB,4BAA4B;IAC5B,gBAAgB,IAAI,IAAI,CAAC;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAClC;AAED;;;;;GAKG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,0DAA0D;IAC1D,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,GAAG,MAAM;CAO7D;AA2BD,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,WAAW,GACd,kBAAkB,CAy1BpB"}
|