kindy-editor 3.2.7 → 3.2.9
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/dist-lib/assets/{worker-pUzyeoiR.js → worker-Du8hmveE.js} +2232 -2218
- package/dist-lib/builder.js +4 -4
- package/dist-lib/events.js +9 -120
- package/dist-lib/export.js +7 -7
- package/dist-lib/generate-toc.js +3 -3
- package/dist-lib/import.js +2 -2
- package/dist-lib/index-D5dkvB0x.js +490 -0
- package/dist-lib/{editorApp-BtDzRfJq.js → kindy-editor-Bo4FnkLV.js} +13102 -10850
- package/dist-lib/kindy-editor.js +4 -4
- package/dist-lib/measure.js +3 -4
- package/dist-lib/{measureHost-CpqJsipA.js → measureHost-BG9FPivu.js} +17 -18
- package/dist-lib/{paintStyle-Cb7IGKPW.js → metrics-D7Ma2xlj.js} +175 -148
- package/dist-lib/{parse-CpGoMQ_v.js → parse-BsUkqk-n.js} +3 -3
- package/dist-lib/{pipeline-CCwWMjhw.js → pipeline-CX_Hc5BD.js} +1 -1
- package/dist-lib/{qrcode-BCaJLV1G.js → qrcode--5MhVNEZ.js} +1 -1
- package/dist-lib/{qrcode-B-JKix2T.js → qrcode-CR8i1-w0.js} +6 -6
- package/dist-lib/recalc-docx.js +1 -1
- package/dist-lib/{recalcToc-CaiTn2IJ.js → recalcToc-zAFS4S6S.js} +755 -742
- package/dist-lib/{stylesheet-BPIJJMlK.js → stylesheet-BfxJTGA5.js} +1 -1
- package/dist-lib/{webmcp-B0swfp0T.js → webmcp-CpWvhcrz.js} +2 -2
- package/docs/table-editing.md +14 -1
- package/docs/table-form-integration.md +50 -1
- package/package.json +1 -1
- package/types/kindy-editor.d.ts +4 -0
- package/dist-lib/changeAggregator-DEuynIcV.js +0 -376
- package/dist-lib/kindy-editor---oVGhZU.js +0 -2190
- package/dist-lib/metrics-CdfHD-8y.js +0 -31
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { p as j, c as _, e as F, t as k, D as L } from "./qrcode-
|
|
1
|
+
import { p as j, c as _, e as F, t as k, D as L } from "./qrcode-CR8i1-w0.js";
|
|
2
2
|
const s = (e) => Math.round(e * 100) / 100;
|
|
3
3
|
function A(e) {
|
|
4
4
|
const o = { font: e.fontFamily, sizePx: e.fontSizePx };
|
|
@@ -332,7 +332,7 @@ function H(e, o = {}, r = {}) {
|
|
|
332
332
|
if (i && e.search(i).total === 0)
|
|
333
333
|
return e.searchClear(), f(`text not found: ${i}`);
|
|
334
334
|
if (!e.getSelection()) return f("no selection (pass 'find' or select_range first)");
|
|
335
|
-
const { insertText: d } = await import("./
|
|
335
|
+
const { insertText: d } = await import("./kindy-editor-Bo4FnkLV.js").then((m) => m.c);
|
|
336
336
|
return e.dispatch(d(t)), i && e.searchClear(), g("inserted");
|
|
337
337
|
}
|
|
338
338
|
}), a({
|
package/docs/table-editing.md
CHANGED
|
@@ -95,11 +95,13 @@ if (editor.canExecuteHostTableAction("deleteRow", { tableId: "table_1", row: 1 }
|
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
#### 3. Nhập giá trị vào 1 ô (`setTableCellValue`)
|
|
98
|
-
Cập nhật nội dung một ô theo tọa độ `(row, column)`. Tự động giữ nguyên định dạng cell/paragraph (font, kích thước, căn lề, màu sắc) và
|
|
98
|
+
Cập nhật nội dung một ô theo tọa độ `(row, column)`. Tự động giữ nguyên định dạng cell/paragraph (font, kích thước, căn lề, màu sắc), tăng `revision`, giải phóng layout cache của bảng, tự động phân trang lại khi kích thước hàng thay đổi và lên lịch repaint mượt mà qua `requestAnimationFrame`:
|
|
99
99
|
|
|
100
100
|
```ts
|
|
101
101
|
await editor.setTableCellValue("table_1", 1, 2, "250.000 đ");
|
|
102
102
|
```
|
|
103
|
+
- **Không cần gọi `setDocument()`**: Thay đổi được áp dụng ngay lập tức mà không phải rebuild lại DOM editor.
|
|
104
|
+
- **Giữ nguyên trạng thái**: Bảo toàn `mode="view"`, vị trí scroll, zoom và vùng chọn ô (`cellSelection`).
|
|
103
105
|
|
|
104
106
|
#### 4. Thay thế nhiều dòng dữ liệu cùng lúc (`replaceTableRows`)
|
|
105
107
|
Cập nhật hàng loạt dòng dữ liệu trong một transaction nguyên tử (atomic update).
|
|
@@ -124,6 +126,17 @@ await editor.replaceTableRows(
|
|
|
124
126
|
);
|
|
125
127
|
```
|
|
126
128
|
|
|
129
|
+
#### 5. Giải phóng Cache Layout thủ công (`invalidateLayout`)
|
|
130
|
+
Ép buộc giải phóng cache đo đạc layout của bảng hoặc toàn bộ tài liệu:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
// Giải phóng cache bảng và repaint
|
|
134
|
+
await editor.invalidateLayout("table_1");
|
|
135
|
+
|
|
136
|
+
// Hoặc giải phóng toàn bộ cache
|
|
137
|
+
await editor.invalidateLayout();
|
|
138
|
+
```
|
|
139
|
+
|
|
127
140
|
## API tương tác canvas thông thường
|
|
128
141
|
|
|
129
142
|
```ts
|
|
@@ -568,11 +568,20 @@ const tables = editor.getTables();
|
|
|
568
568
|
|
|
569
569
|
### 10.3 Cập nhật giá trị ô (`editor.setTableCellValue()`)
|
|
570
570
|
|
|
571
|
+
Cập nhật nội dung một ô theo tọa độ `(row, column)`. Tự động tăng `revision`, giải phóng layout cache (`linesCache`, `tableCache`), tự động phân trang lại nếu chiều cao hàng thay đổi, và lên lịch repaint mượt mà qua `requestAnimationFrame`:
|
|
572
|
+
|
|
571
573
|
```ts
|
|
572
574
|
// Cập nhật ô dòng 1, cột 2 (0-indexed)
|
|
573
575
|
await editor.setTableCellValue("tbl_invoice", 1, 2, "Khám tổng quát");
|
|
574
576
|
```
|
|
575
577
|
|
|
578
|
+
::: tip Tự động Repaint & Giữ Nguyên Viewport
|
|
579
|
+
- **Không cần gọi `setDocument()`**: Canvas sẽ tự động tính lại layout và repaint ngay sau khi gọi `setTableCellValue()`.
|
|
580
|
+
- **Bảo toàn trạng thái**: Giữ nguyên `mode="view"`, vị trí scroll, tỷ lệ zoom và vùng chọn ô (`cellSelection`).
|
|
581
|
+
- **Bảng tách nhiều trang (Multi-page Fragments)**: Nếu nội dung dài làm tăng chiều cao hàng và đẩy các hàng sang trang sau, hệ thống tự động phân trang lại và repaint tất cả các trang/fragment bị ảnh hưởng.
|
|
582
|
+
- **Kế thừa định dạng thông minh**: Khi thêm dòng hoặc nhập liệu vào dòng dữ liệu thông thường, hệ thống tự động ưu tiên kế thừa định dạng từ data row liền kề, không kế thừa kiểu chữ in đậm (`bold: false`) hay màu nền (`shading`) từ dòng tiêu đề (header) hoặc dòng tổng cộng (total/footer).
|
|
583
|
+
:::
|
|
584
|
+
|
|
576
585
|
### 10.4 Thay thế nhiều dòng dữ liệu một lần (`editor.replaceTableRows()`)
|
|
577
586
|
|
|
578
587
|
Cập nhật hàng loạt dòng dữ liệu trong một transaction nguyên tử:
|
|
@@ -622,9 +631,49 @@ await editor.executeHostTableAction("mergeCells", {
|
|
|
622
631
|
await editor.executeHostTableAction("deleteTable", "tbl_invoice");
|
|
623
632
|
```
|
|
624
633
|
|
|
634
|
+
### 10.6 Giải phóng Cache Layout thủ công (`editor.invalidateLayout()`)
|
|
635
|
+
|
|
636
|
+
Nếu cần ép buộc làm mới cache đo đạc và vẽ lại một bảng hoặc toàn bộ tài liệu:
|
|
637
|
+
|
|
638
|
+
```ts
|
|
639
|
+
// Xóa cache đo đạc của bảng cụ thể và repaint
|
|
640
|
+
await editor.invalidateLayout("tbl_invoice");
|
|
641
|
+
|
|
642
|
+
// Hoặc làm mới toàn bộ layout cache
|
|
643
|
+
await editor.invalidateLayout();
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
---
|
|
647
|
+
|
|
648
|
+
## 11. Tối Ưu Hiệu Năng: Nhập Liệu Liên Tục Không Gây Giật
|
|
649
|
+
|
|
650
|
+
::: warning Tuyệt đối không gọi `setDocument()` khi người dùng đang nhập Form
|
|
651
|
+
Phương thức `setDocument()` thực hiện hủy và tái tạo toàn bộ DOM của Editor (`editor.destroy()` + mount lại). Nếu gọi `setDocument()` trên mỗi sự kiện `input` / `change` của form, canvas sẽ bị nhấp nháy trắng, tụt FPS và nhảy vị trí cuộn.
|
|
652
|
+
|
|
653
|
+
**Hãy dùng `setTableCellValue()` kết hợp gom frame bằng `requestAnimationFrame`:**
|
|
654
|
+
:::
|
|
655
|
+
|
|
656
|
+
```ts
|
|
657
|
+
let pendingRaf: number | null = null;
|
|
658
|
+
let pendingValue = "";
|
|
659
|
+
|
|
660
|
+
function onFormCellInput(newValue: string) {
|
|
661
|
+
pendingValue = newValue;
|
|
662
|
+
if (pendingRaf !== null) return;
|
|
663
|
+
|
|
664
|
+
pendingRaf = requestAnimationFrame(async () => {
|
|
665
|
+
pendingRaf = null;
|
|
666
|
+
await editor.setTableCellValue("tbl_invoice", 1, 2, pendingValue);
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
```
|
|
670
|
+
Nhờ cơ chế này:
|
|
671
|
+
- Trình duyệt chỉ cập nhật cell và repaint 1 lần mỗi khung hình (60/120 FPS).
|
|
672
|
+
- Trải nghiệm nhập liệu ở Form ngoài sang Canvas đạt độ mượt mà tối đa.
|
|
673
|
+
|
|
625
674
|
---
|
|
626
675
|
|
|
627
|
-
##
|
|
676
|
+
## 12. Trải Nghiệm Trang Demo Trực Quan
|
|
628
677
|
|
|
629
678
|
Kindy Editor cung cấp sẵn trang demo tương tác đầy đủ các tính năng trên:
|
|
630
679
|
|
package/package.json
CHANGED
package/types/kindy-editor.d.ts
CHANGED
|
@@ -991,6 +991,8 @@ export interface EditorHandle {
|
|
|
991
991
|
options?: ReplaceTableRowsOptions,
|
|
992
992
|
): boolean;
|
|
993
993
|
getTables(): TableSummary[];
|
|
994
|
+
/** Invalidate layout cache (for a specific table or full relayout) and repaint. */
|
|
995
|
+
invalidateLayout(blockId?: string): void;
|
|
994
996
|
// ---- review layer (track changes + comments) ----------------------------
|
|
995
997
|
/** The current editor mode. */
|
|
996
998
|
getMode(): EditMode;
|
|
@@ -1173,6 +1175,8 @@ export declare class KindyEditor {
|
|
|
1173
1175
|
options?: ReplaceTableRowsOptions,
|
|
1174
1176
|
): Promise<boolean>;
|
|
1175
1177
|
getTables(): TableSummary[];
|
|
1178
|
+
/** Invalidate layout cache (for a specific table or full relayout) and repaint. */
|
|
1179
|
+
invalidateLayout(blockId?: string): Promise<void>;
|
|
1176
1180
|
getDocId(): string | null;
|
|
1177
1181
|
getShareLink(): string | null;
|
|
1178
1182
|
/** Render ephemeral highlight decorations for external comments/annotations.
|
|
@@ -1,376 +0,0 @@
|
|
|
1
|
-
import { t as T } from "./qrcode-B-JKix2T.js";
|
|
2
|
-
function y(c) {
|
|
3
|
-
if (c.length === 0) return [];
|
|
4
|
-
const e = /* @__PURE__ */ new Map();
|
|
5
|
-
for (const s of c) {
|
|
6
|
-
let t = e.get(s.blockId);
|
|
7
|
-
t || (t = [], e.set(s.blockId, t)), t.push({ ...s });
|
|
8
|
-
}
|
|
9
|
-
const i = [];
|
|
10
|
-
for (const [s, t] of e) {
|
|
11
|
-
if (t.length === 0) continue;
|
|
12
|
-
const r = [];
|
|
13
|
-
for (const n of t) {
|
|
14
|
-
if (r.length === 0) {
|
|
15
|
-
r.push(n);
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
const o = r[r.length - 1];
|
|
19
|
-
if (o.action === "delete" && n.action === "insert" && o.offset === n.offset) {
|
|
20
|
-
o.action = "replace", o.toText = n.toText;
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
if (o.action === "replace" && n.action === "insert" && n.offset === o.offset + o.toText.length) {
|
|
24
|
-
o.toText += n.toText;
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
if (o.action === "insert" && n.action === "insert" && n.offset === o.offset + o.toText.length) {
|
|
28
|
-
o.toText += n.toText;
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
if (o.action === "delete" && n.action === "delete" && n.offset === o.offset) {
|
|
32
|
-
o.fromText += n.fromText;
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
if (o.action === "delete" && n.action === "delete" && n.offset + n.fromText.length === o.offset) {
|
|
36
|
-
o.offset = n.offset, o.fromText = n.fromText + o.fromText;
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
r.push(n);
|
|
40
|
-
}
|
|
41
|
-
for (const n of r)
|
|
42
|
-
n.action === "replace" && n.fromText === n.toText || n.action === "insert" && n.toText.length === 0 || n.action === "delete" && n.fromText.length === 0 || i.push({
|
|
43
|
-
action: n.action,
|
|
44
|
-
blockId: s,
|
|
45
|
-
offset: n.offset,
|
|
46
|
-
fromText: n.fromText,
|
|
47
|
-
toText: n.toText
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
return i;
|
|
51
|
-
}
|
|
52
|
-
function v(c, e) {
|
|
53
|
-
const i = new Map(Object.entries(e)), s = [];
|
|
54
|
-
for (const t of c)
|
|
55
|
-
if (t.type === "deleteRange") {
|
|
56
|
-
const r = i.get(t.blockId) ?? "", n = Math.max(0, Math.min(t.start, r.length)), o = Math.max(n, Math.min(t.end, r.length)), l = r.slice(n, o);
|
|
57
|
-
l.length > 0 && s.push({
|
|
58
|
-
action: "delete",
|
|
59
|
-
blockId: t.blockId,
|
|
60
|
-
offset: n,
|
|
61
|
-
fromText: l,
|
|
62
|
-
toText: ""
|
|
63
|
-
}), i.set(t.blockId, r.slice(0, n) + r.slice(o));
|
|
64
|
-
} else if (t.type === "insertText") {
|
|
65
|
-
const r = i.get(t.at.blockId) ?? "", n = Math.max(0, Math.min(t.at.offset, r.length));
|
|
66
|
-
t.text.length > 0 && s.push({
|
|
67
|
-
action: "insert",
|
|
68
|
-
blockId: t.at.blockId,
|
|
69
|
-
offset: n,
|
|
70
|
-
fromText: "",
|
|
71
|
-
toText: t.text
|
|
72
|
-
}), i.set(
|
|
73
|
-
t.at.blockId,
|
|
74
|
-
r.slice(0, n) + t.text + r.slice(n)
|
|
75
|
-
);
|
|
76
|
-
} else if (t.type === "insertRuns") {
|
|
77
|
-
const r = T(t.runs), n = i.get(t.at.blockId) ?? "", o = Math.max(0, Math.min(t.at.offset, n.length));
|
|
78
|
-
r.length > 0 && s.push({
|
|
79
|
-
action: "insert",
|
|
80
|
-
blockId: t.at.blockId,
|
|
81
|
-
offset: o,
|
|
82
|
-
fromText: "",
|
|
83
|
-
toText: r
|
|
84
|
-
}), i.set(
|
|
85
|
-
t.at.blockId,
|
|
86
|
-
n.slice(0, o) + r + n.slice(o)
|
|
87
|
-
);
|
|
88
|
-
} else if (t.type === "setRuns") {
|
|
89
|
-
const r = i.get(t.blockId) ?? "", n = T(t.runs);
|
|
90
|
-
r !== n && s.push({
|
|
91
|
-
action: "replace",
|
|
92
|
-
blockId: t.blockId,
|
|
93
|
-
offset: 0,
|
|
94
|
-
fromText: r,
|
|
95
|
-
toText: n
|
|
96
|
-
}), i.set(t.blockId, n);
|
|
97
|
-
} else if (t.type === "splitParagraph") {
|
|
98
|
-
const r = i.get(t.at.blockId) ?? "", n = Math.max(0, Math.min(t.at.offset, r.length)), o = r.slice(0, n), l = r.slice(n);
|
|
99
|
-
i.set(t.at.blockId, o), i.set(t.newBlockId, l);
|
|
100
|
-
} else if (t.type === "removeBlock") {
|
|
101
|
-
const r = i.get(t.blockId) ?? "";
|
|
102
|
-
r.length > 0 && s.push({
|
|
103
|
-
action: "delete",
|
|
104
|
-
blockId: t.blockId,
|
|
105
|
-
offset: 0,
|
|
106
|
-
fromText: r,
|
|
107
|
-
toText: ""
|
|
108
|
-
}), i.set(t.blockId, "");
|
|
109
|
-
}
|
|
110
|
-
return y(s);
|
|
111
|
-
}
|
|
112
|
-
function b(c, e, i, s = 0) {
|
|
113
|
-
if (c === e) return [];
|
|
114
|
-
let t = 0;
|
|
115
|
-
const r = Math.min(c.length, e.length);
|
|
116
|
-
for (; t < r && c[t] === e[t]; )
|
|
117
|
-
t++;
|
|
118
|
-
let n = 0;
|
|
119
|
-
const o = Math.min(c.length - t, e.length - t);
|
|
120
|
-
for (; n < o && c[c.length - 1 - n] === e[e.length - 1 - n]; )
|
|
121
|
-
n++;
|
|
122
|
-
const l = c.slice(t, c.length - n), f = e.slice(t, e.length - n), d = s + t;
|
|
123
|
-
return l.length > 0 && f.length === 0 ? [{ action: "delete", blockId: i, offset: d, fromText: l, toText: "" }] : l.length === 0 && f.length > 0 ? [{ action: "insert", blockId: i, offset: d, fromText: "", toText: f }] : l.length > 0 && f.length > 0 ? [{ action: "replace", blockId: i, offset: d, fromText: l, toText: f }] : [];
|
|
124
|
-
}
|
|
125
|
-
class S {
|
|
126
|
-
detail;
|
|
127
|
-
includeSelection;
|
|
128
|
-
handlers = /* @__PURE__ */ new Map();
|
|
129
|
-
anyHandlers = /* @__PURE__ */ new Set();
|
|
130
|
-
sink;
|
|
131
|
-
redact;
|
|
132
|
-
onError;
|
|
133
|
-
disposed = !1;
|
|
134
|
-
constructor(e = {}) {
|
|
135
|
-
this.detail = e.detail ?? "metadata", this.includeSelection = e.includeSelection ?? !1, this.sink = e.sink, this.redact = e.redact, this.onError = e.onError ?? ((i) => console.error("[kindy-editor:event]", i));
|
|
136
|
-
}
|
|
137
|
-
on(e, i, s = {}) {
|
|
138
|
-
if (this.disposed || s.signal?.aborted) return () => {
|
|
139
|
-
};
|
|
140
|
-
let t = this.handlers.get(e);
|
|
141
|
-
t || this.handlers.set(e, t = /* @__PURE__ */ new Set()), t.add(i);
|
|
142
|
-
const r = () => {
|
|
143
|
-
t?.delete(i), t?.size === 0 && this.handlers.delete(e);
|
|
144
|
-
};
|
|
145
|
-
return s.signal?.addEventListener("abort", r, { once: !0 }), r;
|
|
146
|
-
}
|
|
147
|
-
onAny(e, i = {}) {
|
|
148
|
-
if (this.disposed || i.signal?.aborted) return () => {
|
|
149
|
-
};
|
|
150
|
-
this.anyHandlers.add(e);
|
|
151
|
-
const s = () => {
|
|
152
|
-
this.anyHandlers.delete(e);
|
|
153
|
-
};
|
|
154
|
-
return i.signal?.addEventListener("abort", s, { once: !0 }), s;
|
|
155
|
-
}
|
|
156
|
-
off(e, i) {
|
|
157
|
-
this.handlers.get(e)?.delete(i);
|
|
158
|
-
}
|
|
159
|
-
/** Called by the editor runtime after state is already consistent. Public only
|
|
160
|
-
* for custom bridges; normal integrations subscribe with on/onAny. */
|
|
161
|
-
dispatch(e) {
|
|
162
|
-
if (this.disposed) return;
|
|
163
|
-
const i = this.redact ? this.redact(e) : e;
|
|
164
|
-
if (i) {
|
|
165
|
-
for (const s of this.handlers.get(i.type) ?? []) this.safeCall(s, i);
|
|
166
|
-
for (const s of this.anyHandlers) this.safeCall(s, i);
|
|
167
|
-
if (this.sink)
|
|
168
|
-
try {
|
|
169
|
-
Promise.resolve(this.sink(i)).catch((s) => this.reportError(s, i));
|
|
170
|
-
} catch (s) {
|
|
171
|
-
this.reportError(s, i);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
destroy() {
|
|
176
|
-
this.disposed = !0, this.handlers.clear(), this.anyHandlers.clear();
|
|
177
|
-
}
|
|
178
|
-
safeCall(e, i) {
|
|
179
|
-
try {
|
|
180
|
-
e(i);
|
|
181
|
-
} catch (s) {
|
|
182
|
-
this.reportError(s, i);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
reportError(e, i) {
|
|
186
|
-
try {
|
|
187
|
-
this.onError(e, i);
|
|
188
|
-
} catch (s) {
|
|
189
|
-
console.error("[kindy-editor:event:onError]", s);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
function w(c) {
|
|
194
|
-
const e = c.fetch ?? globalThis.fetch;
|
|
195
|
-
if (!e) throw new Error("createHttpEventSink requires fetch");
|
|
196
|
-
const i = Math.max(1, c.batchSize ?? 25), s = Math.max(10, c.flushIntervalMs ?? 1e3), t = Math.max(1, c.maxAttempts ?? 3);
|
|
197
|
-
let r = [], n = null, o = Promise.resolve(), l = !1;
|
|
198
|
-
const f = () => {
|
|
199
|
-
n && clearTimeout(n), n = null;
|
|
200
|
-
}, d = async () => {
|
|
201
|
-
const a = typeof c.headers == "function" ? await c.headers() : c.headers, h = new Headers(a);
|
|
202
|
-
return h.has("content-type") || h.set("content-type", "application/json"), h;
|
|
203
|
-
}, p = async (a) => {
|
|
204
|
-
let h;
|
|
205
|
-
for (let g = 1; g <= t; g++)
|
|
206
|
-
try {
|
|
207
|
-
const m = await e(c.endpoint, {
|
|
208
|
-
method: "POST",
|
|
209
|
-
headers: await d(),
|
|
210
|
-
body: JSON.stringify({ schemaVersion: a[0]?.schemaVersion ?? "1.0", events: a })
|
|
211
|
-
});
|
|
212
|
-
if (!m.ok) throw new Error(`event sink returned HTTP ${m.status}`);
|
|
213
|
-
return;
|
|
214
|
-
} catch (m) {
|
|
215
|
-
h = m, g < t && await new Promise((I) => setTimeout(I, 100 * 2 ** (g - 1)));
|
|
216
|
-
}
|
|
217
|
-
c.onDeliveryError?.(h, a);
|
|
218
|
-
}, u = async () => {
|
|
219
|
-
if (f(), r.length === 0) return o;
|
|
220
|
-
const a = r;
|
|
221
|
-
r = [], o = o.then(() => p(a)), await o;
|
|
222
|
-
}, x = ((a) => {
|
|
223
|
-
l || c.eventTypes && !c.eventTypes.includes(a.type) || (r.push(a), r.length >= i ? u() : n || (n = setTimeout(() => void u(), s)));
|
|
224
|
-
});
|
|
225
|
-
return x.flush = u, x.destroy = async () => {
|
|
226
|
-
l = !0, await u();
|
|
227
|
-
}, x;
|
|
228
|
-
}
|
|
229
|
-
class k {
|
|
230
|
-
idleMs;
|
|
231
|
-
selectionJumpThreshold;
|
|
232
|
-
constructor(e = {}) {
|
|
233
|
-
this.idleMs = Math.max(100, e.idleMs ?? 1e3), this.selectionJumpThreshold = Math.max(1, e.selectionJumpThreshold ?? 2);
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Evaluates if a selection movement constitutes a boundary (e.g. jumped to another block
|
|
237
|
-
* or moved far away from the active typing position).
|
|
238
|
-
*/
|
|
239
|
-
shouldCommitOnSelectionChange(e, i, s, t) {
|
|
240
|
-
return !e || s === null || t === null ? !1 : e !== s || i !== null && Math.abs(t - i) > this.selectionJumpThreshold;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
class B {
|
|
244
|
-
policy;
|
|
245
|
-
emit;
|
|
246
|
-
getVersion;
|
|
247
|
-
idGenerator;
|
|
248
|
-
currentSession = null;
|
|
249
|
-
idleTimer = null;
|
|
250
|
-
disposed = !1;
|
|
251
|
-
nextGroupId = 1;
|
|
252
|
-
constructor(e) {
|
|
253
|
-
this.policy = e.policy instanceof k ? e.policy : new k({
|
|
254
|
-
...typeof e.idleMs == "number" ? { idleMs: e.idleMs } : {},
|
|
255
|
-
...e.policy
|
|
256
|
-
}), this.emit = e.emit, this.getVersion = e.getVersion ?? (() => 0), this.idGenerator = e.idGenerator ?? (() => `chg-${Date.now()}-${this.nextGroupId++}`);
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Tracks a raw applied change. Starts a new session or extends the active session.
|
|
260
|
-
*/
|
|
261
|
-
trackChange(e) {
|
|
262
|
-
if (this.disposed) return;
|
|
263
|
-
this.currentSession && this.currentSession.origin !== e.origin && this.flush("origin-change");
|
|
264
|
-
const i = e.version ?? this.getVersion();
|
|
265
|
-
if (!this.currentSession) {
|
|
266
|
-
const t = this.getVersion(), r = e.version ?? t;
|
|
267
|
-
this.currentSession = {
|
|
268
|
-
changeGroupId: this.idGenerator(),
|
|
269
|
-
origin: e.origin,
|
|
270
|
-
...e.intent !== void 0 ? { intent: e.intent } : {},
|
|
271
|
-
beforeVersion: t,
|
|
272
|
-
afterVersion: r,
|
|
273
|
-
lastTransactionId: e.transactionId,
|
|
274
|
-
initialBlockTexts: /* @__PURE__ */ new Map(),
|
|
275
|
-
currentBlockTexts: /* @__PURE__ */ new Map(),
|
|
276
|
-
diffs: [],
|
|
277
|
-
activeBlockId: e.selectionAfter?.focus.blockId ?? null,
|
|
278
|
-
lastOffset: e.selectionAfter?.focus.offset ?? null
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
const s = this.currentSession;
|
|
282
|
-
if (s) {
|
|
283
|
-
s.afterVersion = i, s.lastTransactionId = e.transactionId, e.intent !== void 0 && s.intent === void 0 && (s.intent = e.intent);
|
|
284
|
-
for (const t of e.affectedBlockIds)
|
|
285
|
-
if (!s.initialBlockTexts.has(t)) {
|
|
286
|
-
const r = e.blockTextBefore?.[t] ?? "";
|
|
287
|
-
s.initialBlockTexts.set(t, r), s.currentBlockTexts.set(t, r);
|
|
288
|
-
}
|
|
289
|
-
if (this.applyOpsToCurrentTexts(s.currentBlockTexts, e.ops), e.textDiffs && e.textDiffs.length > 0 && s.diffs.push(...e.textDiffs), e.selectionAfter && (s.activeBlockId = e.selectionAfter.focus.blockId, s.lastOffset = e.selectionAfter.focus.offset), e.origin === "paste" || e.origin === "command") {
|
|
290
|
-
this.flush("explicit");
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
this.restartIdleTimer();
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* Signals a semantic boundary from the editor environment (blur, save, selection change, etc.).
|
|
298
|
-
*/
|
|
299
|
-
notifyBoundary(e, i) {
|
|
300
|
-
if (!(this.disposed || !this.currentSession)) {
|
|
301
|
-
if (e === "selection-jump") {
|
|
302
|
-
this.policy.shouldCommitOnSelectionChange(
|
|
303
|
-
this.currentSession.activeBlockId,
|
|
304
|
-
this.currentSession.lastOffset,
|
|
305
|
-
i?.newBlockId ?? null,
|
|
306
|
-
i?.newOffset ?? null
|
|
307
|
-
) && this.flush("selection-jump");
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
this.flush(e);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
/**
|
|
314
|
-
* Flushes and commits the active session if one exists and contains non-trivial changes.
|
|
315
|
-
*/
|
|
316
|
-
flush(e) {
|
|
317
|
-
this.clearIdleTimer();
|
|
318
|
-
const i = this.currentSession;
|
|
319
|
-
if (!i) return;
|
|
320
|
-
this.currentSession = null;
|
|
321
|
-
const s = [];
|
|
322
|
-
for (const [t, r] of i.initialBlockTexts) {
|
|
323
|
-
const n = i.currentBlockTexts.get(t) ?? r;
|
|
324
|
-
if (r !== n) {
|
|
325
|
-
const o = b(r, n, t);
|
|
326
|
-
s.push(...o);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
s.length !== 0 && this.emit({
|
|
330
|
-
transactionId: i.lastTransactionId,
|
|
331
|
-
changeGroupId: i.changeGroupId,
|
|
332
|
-
beforeVersion: i.beforeVersion,
|
|
333
|
-
afterVersion: i.afterVersion,
|
|
334
|
-
origin: i.origin,
|
|
335
|
-
textDiffs: s,
|
|
336
|
-
...i.intent ? { intent: i.intent } : {},
|
|
337
|
-
affectedBlockIds: Array.from(i.initialBlockTexts.keys())
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
destroy() {
|
|
341
|
-
this.disposed || (this.flush("destroy"), this.disposed = !0);
|
|
342
|
-
}
|
|
343
|
-
applyOpsToCurrentTexts(e, i) {
|
|
344
|
-
for (const s of i)
|
|
345
|
-
if (s.type === "deleteRange") {
|
|
346
|
-
const t = e.get(s.blockId) ?? "", r = Math.max(0, Math.min(s.start, t.length)), n = Math.max(r, Math.min(s.end, t.length));
|
|
347
|
-
e.set(s.blockId, t.slice(0, r) + t.slice(n));
|
|
348
|
-
} else if (s.type === "insertText") {
|
|
349
|
-
const t = e.get(s.at.blockId) ?? "", r = Math.max(0, Math.min(s.at.offset, t.length));
|
|
350
|
-
e.set(s.at.blockId, t.slice(0, r) + s.text + t.slice(r));
|
|
351
|
-
} else if (s.type === "insertRuns") {
|
|
352
|
-
const t = T(s.runs), r = e.get(s.at.blockId) ?? "", n = Math.max(0, Math.min(s.at.offset, r.length));
|
|
353
|
-
e.set(s.at.blockId, r.slice(0, n) + t + r.slice(n));
|
|
354
|
-
} else if (s.type === "setRuns")
|
|
355
|
-
e.set(s.blockId, T(s.runs));
|
|
356
|
-
else if (s.type === "splitParagraph") {
|
|
357
|
-
const t = e.get(s.at.blockId) ?? "", r = Math.max(0, Math.min(s.at.offset, t.length));
|
|
358
|
-
e.set(s.at.blockId, t.slice(0, r)), e.set(s.newBlockId, t.slice(r));
|
|
359
|
-
} else s.type === "removeBlock" && e.set(s.blockId, "");
|
|
360
|
-
}
|
|
361
|
-
restartIdleTimer() {
|
|
362
|
-
this.clearIdleTimer(), this.idleTimer = setTimeout(() => {
|
|
363
|
-
this.flush("idle");
|
|
364
|
-
}, this.policy.idleMs);
|
|
365
|
-
}
|
|
366
|
-
clearIdleTimer() {
|
|
367
|
-
this.idleTimer && (clearTimeout(this.idleTimer), this.idleTimer = null);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
export {
|
|
371
|
-
B as C,
|
|
372
|
-
S as E,
|
|
373
|
-
k as a,
|
|
374
|
-
v as b,
|
|
375
|
-
w as c
|
|
376
|
-
};
|