kindy-editor 3.2.6 → 3.2.7
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-B7ALyZOw.js → worker-DjZBUOhB.js} +1009 -997
- package/dist-lib/assets/{worker-z-dyEi8F.js → worker-pUzyeoiR.js} +2 -4
- package/dist-lib/builder.js +4 -4
- package/dist-lib/{changeAggregator-ZI959nPP.js → changeAggregator-DEuynIcV.js} +1 -1
- package/dist-lib/{editorApp-Bu3dbUIU.js → editorApp-BtDzRfJq.js} +6070 -5955
- package/dist-lib/events.js +2 -2
- package/dist-lib/export.js +7 -9
- package/dist-lib/generate-toc.js +3 -3
- package/dist-lib/import.js +2 -2
- package/dist-lib/{kindy-editor-CToKn_gP.js → kindy-editor---oVGhZU.js} +32 -13
- package/dist-lib/kindy-editor.js +2 -2
- package/dist-lib/{parse-CKw-ZDr9.js → parse-CpGoMQ_v.js} +3 -3
- package/dist-lib/{pipeline-BkC7G8gU.js → pipeline-CCwWMjhw.js} +850 -837
- package/dist-lib/{qrcode-efBlPzzh.js → qrcode-B-JKix2T.js} +5 -5
- package/dist-lib/{qrcode-oNwKusWD.js → qrcode-BCaJLV1G.js} +1 -1
- package/dist-lib/recalc-docx.js +1 -1
- package/dist-lib/{recalcToc-Bc8V9Ecn.js → recalcToc-CaiTn2IJ.js} +1 -1
- package/dist-lib/{stylesheet-BT1DGhnx.js → stylesheet-BPIJJMlK.js} +1 -1
- package/dist-lib/{webmcp-ClHEa6Gq.js → webmcp-B0swfp0T.js} +2 -2
- package/docs/find-and-replace.md +224 -0
- package/docs/security-review.md +26 -0
- package/docs/table-editing.md +3 -1
- package/docs/table-form-integration.md +4 -0
- package/package.json +6 -6
- package/types/import.d.ts +7 -0
- package/types/kindy-editor.d.ts +26 -0
|
@@ -2288,13 +2288,13 @@ export {
|
|
|
2288
2288
|
Ue as i,
|
|
2289
2289
|
$e as j,
|
|
2290
2290
|
qe as k,
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2291
|
+
lt as l,
|
|
2292
|
+
Ct as m,
|
|
2293
|
+
pt as n,
|
|
2294
|
+
U as o,
|
|
2295
2295
|
jt as p,
|
|
2296
2296
|
z as q,
|
|
2297
|
-
|
|
2297
|
+
Ke as r,
|
|
2298
2298
|
Ut as s,
|
|
2299
2299
|
X as t,
|
|
2300
2300
|
He as u,
|
package/dist-lib/recalc-docx.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { u, s as f, a as m, z as g } from "./browser-ODPzT3Lm.js";
|
|
2
2
|
import { f as h } from "./contentTypes-ChqPfcVI.js";
|
|
3
|
-
import { p as b, c as k } from "./recalcToc-
|
|
3
|
+
import { p as b, c as k } from "./recalcToc-CaiTn2IJ.js";
|
|
4
4
|
function w(s, r) {
|
|
5
5
|
const n = b(r), a = {};
|
|
6
6
|
for (const [o, i] of Object.entries(s.bookmarks ?? {})) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { l as Ln, m as Ls, t as Os, s as Ps } from "./qrcode-B-JKix2T.js";
|
|
2
2
|
import { c as Is, f as Ze } from "./lists-BVCy5dMa.js";
|
|
3
3
|
import { m as he, f as Ue, c as le } from "./metrics-CdfHD-8y.js";
|
|
4
4
|
import { c as vs, f as On, M as nt, g as Rs, P as Mt, w as Ws, s as Ts } from "./paintStyle-Cb7IGKPW.js";
|
|
@@ -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-B-JKix2T.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("./editorApp-
|
|
335
|
+
const { insertText: d } = await import("./editorApp-BtDzRfJq.js").then((m) => m.c);
|
|
336
336
|
return e.dispatch(d(t)), i && e.searchClear(), g("inserted");
|
|
337
337
|
}
|
|
338
338
|
}), a({
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Tìm kiếm, Thay thế & Cuộn Focus (Find & Replace API)
|
|
2
|
+
|
|
3
|
+
Kindy Editor cung cấp hệ thống Tìm kiếm & Thay thế (Find & Replace) mạnh mẽ trên nền Canvas, hỗ trợ tính toán tọa độ ký tự chính xác, tự động bôi đen (selection) và **tự động cuộn màn hình (scroll focus)** đến vị trí văn bản cần thay thế.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Cơ chế hoạt động (How it works)
|
|
8
|
+
|
|
9
|
+
Khi tìm kiếm hoặc thay thế, editor thực hiện các bước sau:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
[search(query)]
|
|
13
|
+
│
|
|
14
|
+
├── 1. Quét AST text của các block trong Document
|
|
15
|
+
├── 2. Highlight tất cả các matches trên Canvas overlay
|
|
16
|
+
└── 3. gotoMatch(0):
|
|
17
|
+
├── setSelection({ anchor, focus }) --> Bôi đen match
|
|
18
|
+
├── caretRect(tree, position) --> Tính tọa độ pixel trên trang
|
|
19
|
+
└── paint.ensureVisible(rect) --> TỰ ĐỘNG CUỘN VIEWPORT ĐẾN MATCH
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Khi thực hiện thay thế (`searchReplaceCurrent`):
|
|
23
|
+
1. Match đang chọn được thay thế bằng chuỗi mới thông qua một **Transaction nguyên tử** (Atomic Op Group: `deleteRange` + `insertText`).
|
|
24
|
+
2. Editor tự động quét lại các match còn lại.
|
|
25
|
+
3. Tự động chuyển con trỏ và **cuộn màn hình đến match kế tiếp** ngay lập tức.
|
|
26
|
+
4. Toàn bộ thao tác có thể **Undo (`Ctrl+Z`)** hoàn hảo.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 2. Danh sách API (API Reference)
|
|
31
|
+
|
|
32
|
+
Các API này có thể được gọi trực tiếp từ đối tượng editor (`KindyEditorElement` hoặc `EditorHandle`):
|
|
33
|
+
|
|
34
|
+
### `search(query, opts?): Promise<SearchState>`
|
|
35
|
+
Tìm kiếm văn bản trong tài liệu, đánh dấu tất cả các vị trí trùng khớp và tự động cuộn focus đến match đầu tiên.
|
|
36
|
+
- **Tham số**:
|
|
37
|
+
- `query: string`: Từ khóa cần tìm.
|
|
38
|
+
- `opts?: { matchCase?: boolean; wholeWord?: boolean }`:
|
|
39
|
+
- `matchCase`: Khớp chính xác chữ hoa/chữ thường (mặc định: `false`).
|
|
40
|
+
- `wholeWord`: Chỉ khớp từ nguyên vẹn (mặc định: `false`).
|
|
41
|
+
- **Trả về**: `Promise<SearchState>` gồm `{ index: number, total: number }` (1-based, ví dụ `1/12`).
|
|
42
|
+
|
|
43
|
+
### `searchNav(dir): Promise<SearchState>`
|
|
44
|
+
Điều hướng giữa các kết quả tìm kiếm và tự động cuộn màn hình đến kết quả đó.
|
|
45
|
+
- **Tham số**:
|
|
46
|
+
- `dir: 1 | -1`: `1` để chuyển đến match kế tiếp, `-1` để quay lại match trước.
|
|
47
|
+
- **Trả về**: `Promise<SearchState>`.
|
|
48
|
+
|
|
49
|
+
### `searchReplaceCurrent(replacement): Promise<SearchState>`
|
|
50
|
+
Thay thế kết quả đang được chọn bằng nội dung mới, sau đó **tự động nhảy và cuộn focus đến match kế tiếp**.
|
|
51
|
+
- **Tham số**:
|
|
52
|
+
- `replacement: string`: Chuỗi văn bản thay thế.
|
|
53
|
+
- **Trả về**: `Promise<SearchState>` biểu thị vị trí match tiếp theo.
|
|
54
|
+
|
|
55
|
+
### `searchReplaceAll(replacement): Promise<number>`
|
|
56
|
+
Thay thế toàn bộ tất cả các kết quả tìm kiếm trong tài liệu trong một transaction duy nhất (1 lần Undo).
|
|
57
|
+
- **Tham số**:
|
|
58
|
+
- `replacement: string`: Chuỗi văn bản thay thế.
|
|
59
|
+
- **Trả về**: `Promise<number>`: Số lượng đoạn văn bản đã được thay thế.
|
|
60
|
+
|
|
61
|
+
### `searchClear(): Promise<void>`
|
|
62
|
+
Xóa tất cả highlight tìm kiếm và đóng trạng thái tìm kiếm hiện tại.
|
|
63
|
+
|
|
64
|
+
### `revealRange(range, options?): Promise<void>`
|
|
65
|
+
Cuộn viewport đến bất kỳ vị trí text range nào trong văn bản (hữu ích cho AI suggestion hoặc spellcheck).
|
|
66
|
+
- **Tham số**:
|
|
67
|
+
- `range: ExternalRange`: Gồm `{ start: { blockId, offset }, end: { blockId, offset } }`.
|
|
68
|
+
- `options?: RevealRangeOptions`:
|
|
69
|
+
- `behavior?: "auto" | "smooth"`: Hiệu ứng cuộn mượt (mặc định `"smooth"`).
|
|
70
|
+
- `align?: "nearest" | "center"`: Vị trí canh lề (mặc định `"center"`).
|
|
71
|
+
- `durationMs?: number`: Thời gian animation cuộn (mặc định `280ms`).
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 3. Hướng dẫn tích hợp hệ thống (System Integration Guide)
|
|
76
|
+
|
|
77
|
+
### Cách 1: Sử dụng UI Tìm kiếm & Thay thế có sẵn (Built-in UI)
|
|
78
|
+
|
|
79
|
+
Kindy Editor đã tích hợp sẵn thanh công cụ Find & Replace:
|
|
80
|
+
- **Phím tắt**: Nhấn `Ctrl + F` (hoặc `Cmd + F` trên macOS).
|
|
81
|
+
- **Ribbon**: Chọn tab **Trang chủ** / **Home** -> nút **Tìm kiếm / Thay thế**.
|
|
82
|
+
- Giao diện có sẵn:
|
|
83
|
+
- Ô nhập từ khóa tìm kiếm và ô nhập nội dung thay thế.
|
|
84
|
+
- Tùy chọn khớp hoa/thường (`Aa`) và khớp nguyên từ (`[W]`).
|
|
85
|
+
- Nút điều hướng trước/sau (`‹`, `›`) với phím tắt `Enter` / `Shift+Enter`.
|
|
86
|
+
- Nút `Replace` và `Replace All`.
|
|
87
|
+
- Bộ đếm `1/5` hiển thị số lượng match trực quan.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### Cách 2: Tích hợp Custom Search & Replace Toolbar từ Host Application
|
|
92
|
+
|
|
93
|
+
Nếu hệ thống của bạn muốn xây dựng thanh tìm kiếm riêng bên ngoài khung editor (ví dụ: thanh công cụ header của ứng dụng web React / Vue / Angular):
|
|
94
|
+
|
|
95
|
+
#### Mã nguồn tích hợp JavaScript / TypeScript:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import type { KindyEditorElement, SearchState } from "kindy-editor";
|
|
99
|
+
|
|
100
|
+
// 1. Lấy tham chiếu đến web component kindy-editor
|
|
101
|
+
const editor = document.querySelector("kindy-editor") as KindyEditorElement;
|
|
102
|
+
|
|
103
|
+
// 2. Thực hiện tìm kiếm
|
|
104
|
+
async function handleSearch(keyword: string) {
|
|
105
|
+
if (!keyword.trim()) {
|
|
106
|
+
await editor.searchClear();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// search() tự động highlight VÀ cuộn focus đến kết quả đầu tiên
|
|
111
|
+
const state: SearchState = await editor.search(keyword, {
|
|
112
|
+
matchCase: false, // Không phân biệt hoa/thường
|
|
113
|
+
wholeWord: false, // Không bắt buộc nguyên từ
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
console.log(`Tìm thấy ${state.total} kết quả. Đang ở vị trí ${state.index}`);
|
|
117
|
+
updateMatchUI(state);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 3. Chuyển sang kết quả tiếp theo (Next match)
|
|
121
|
+
async function handleNext() {
|
|
122
|
+
const state = await editor.searchNav(1);
|
|
123
|
+
updateMatchUI(state);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 4. Quay lại kết quả trước (Previous match)
|
|
127
|
+
async function handlePrev() {
|
|
128
|
+
const state = await editor.searchNav(-1);
|
|
129
|
+
updateMatchUI(state);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 5. Thay thế kết quả hiện tại và tự động cuộn đến kết quả tiếp theo
|
|
133
|
+
async function handleReplace(newText: string) {
|
|
134
|
+
const state = await editor.searchReplaceCurrent(newText);
|
|
135
|
+
console.log(`Đã thay thế. Chuyển đến kết quả tiếp theo: ${state.index}/${state.total}`);
|
|
136
|
+
updateMatchUI(state);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 6. Thay thế tất cả (Replace All)
|
|
140
|
+
async function handleReplaceAll(newText: string) {
|
|
141
|
+
const count = await editor.searchReplaceAll(newText);
|
|
142
|
+
alert(`Đã thay thế thành công ${count} vị trí.`);
|
|
143
|
+
await editor.searchClear();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 7. Đóng tìm kiếm
|
|
147
|
+
async function handleCloseSearch() {
|
|
148
|
+
await editor.searchClear();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function updateMatchUI(state: SearchState) {
|
|
152
|
+
const badge = document.getElementById("search-counter");
|
|
153
|
+
if (badge) {
|
|
154
|
+
badge.innerText = state.total > 0 ? `${state.index}/${state.total}` : "0/0";
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
### Cách 3: Thay thế tại vị trí cụ thể và Scroll Focus (AI Suggestion / Linter)
|
|
162
|
+
|
|
163
|
+
Khi hệ thống backend (AI rà soát hợp đồng, kiểm tra chính tả) trả về một vị trí lỗi cụ thể và bạn muốn người dùng duyệt qua từng lỗi, cuộn màn hình đến đoạn đó và thay thế:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
interface ReplacementSuggestion {
|
|
167
|
+
range: {
|
|
168
|
+
start: { blockId: string; offset: number };
|
|
169
|
+
end: { blockId: string; offset: number };
|
|
170
|
+
};
|
|
171
|
+
originalText: string;
|
|
172
|
+
suggestedText: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function reviewAndApplySuggestion(suggestion: ReplacementSuggestion) {
|
|
176
|
+
// 1. Cuộn màn hình canvas đến vị trí cần sửa với animation mượt
|
|
177
|
+
await editor.revealRange(suggestion.range, {
|
|
178
|
+
behavior: "smooth",
|
|
179
|
+
align: "center",
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// 2. Tùy chọn: Chọn đoạn văn bản đó để người dùng thấy rõ
|
|
183
|
+
// (Nếu host có hỗ trợ setSelection)
|
|
184
|
+
|
|
185
|
+
// 3. Khi người dùng bấm "Chấp nhận thay thế":
|
|
186
|
+
// Nếu dùng search theo cụm từ:
|
|
187
|
+
const state = await editor.search(suggestion.originalText);
|
|
188
|
+
if (state.total > 0) {
|
|
189
|
+
await editor.searchReplaceCurrent(suggestion.suggestedText);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
### Cách 4: Phân biệt với Thay thế Biến mẫu hợp đồng (`setTemplateVariables`)
|
|
197
|
+
|
|
198
|
+
Nếu mục đích của bạn là **điền dữ liệu hợp đồng tự động** (ví dụ thay thế `{{HO_TEN}}`, `{{SO_CCCD}}`, `{{NGAY_KY}}`):
|
|
199
|
+
|
|
200
|
+
👉 **Nên dùng `setTemplateVariables`** thay vì `searchReplaceCurrent`:
|
|
201
|
+
- `setTemplateVariables` cập nhật nguyên tử, bảo toàn format chữ, không phá vỡ cấu trúc và có lịch sử Audit Trail.
|
|
202
|
+
- `searchReplaceCurrent` phù hợp cho tìm kiếm / thay thế văn bản thông thường của người dùng.
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
// Điền tự động các trường dữ liệu hợp đồng
|
|
206
|
+
await editor.setTemplateVariables({
|
|
207
|
+
HO_TEN: "Nguyễn Văn A",
|
|
208
|
+
SO_CCCD: "0123456789",
|
|
209
|
+
NGAY_KY: "10/10/2026"
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 4. Bảng tóm tắt các tính năng
|
|
216
|
+
|
|
217
|
+
| Nhu cầu | Hàm đề xuất | Tự động Scroll & Focus? |
|
|
218
|
+
| :--- | :--- | :---: |
|
|
219
|
+
| Tìm từ khóa và nhảy đến vị trí đó | `editor.search(query, opts)` | Có (`gotoMatch` -> `paint.ensureVisible`) |
|
|
220
|
+
| Xem kết quả kế tiếp / trước đó | `editor.searchNav(1)` / `(-1)` | Có |
|
|
221
|
+
| Thay thế từ đang chọn và nhảy đến từ kế tiếp | `editor.searchReplaceCurrent(newText)` | Có |
|
|
222
|
+
| Thay thế toàn bộ tài liệu | `editor.searchReplaceAll(newText)` | Giữ nguyên selection |
|
|
223
|
+
| Cuộn đến một đoạn bất kỳ theo Range | `editor.revealRange(range, options)` | Có (Smooth animation) |
|
|
224
|
+
| Xóa highlight tìm kiếm | `editor.searchClear()` | - |
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Security review — editor hardening
|
|
2
|
+
|
|
3
|
+
**Reviewed:** 2026-10-09
|
|
4
|
+
**Scope:** editor repository changes based on `c1aef59f`, covering signature demo DOM rendering, host table mutation permissions and input limits, DOCX archive import, QR configuration logging, and JavaScript development/build dependencies. This is a scoped code review, not a penetration test or product-wide compliance assessment.
|
|
5
|
+
|
|
6
|
+
## Changes made
|
|
7
|
+
|
|
8
|
+
- Signature metadata from imported documents is written with DOM `textContent`; the demo's dynamic log messages are text nodes. Fixed placeholder HTML was replaced with DOM construction as well.
|
|
9
|
+
- Host table mutation remains available in normal edit mode. It is off in view/comment modes and turns off when `editContent: false` unless the host explicitly enables `hostTableMutation`. Hosts still need to authorize users and documents at their own trusted boundary.
|
|
10
|
+
- Host table APIs reject over-budget tables and replacements before allocating new cells: 10,000 rows, 256 columns, 50,000 cells, 1,000,000 text characters, 100,000 characters or 1,024 paragraphs per cell, 20,000 paragraphs, and 8 nested table levels.
|
|
11
|
+
- DOCX import rejects archives over 64 MiB compressed, more than 4,096 entries, an entry over 32 MiB expanded, or more than 128 MiB expanded in total. The file/Blob size is checked before reading it into memory.
|
|
12
|
+
- QR configuration values are no longer printed to the developer console. `.env*` files are ignored, with `.env.example` permitted.
|
|
13
|
+
- Updated Vite to 6.4.4, Vitest to 4.1.11, and Vue to 3.5.43. A workspace override keeps VitePress and its Vite plugins on Vite 6.4.4, which meets their published peer ranges.
|
|
14
|
+
|
|
15
|
+
## Verification
|
|
16
|
+
|
|
17
|
+
- Focused regression/security tests: **PASS**, 36 tests across table mutation, permissions, DOCX archive limits, and signature rendering, using Vitest 4.1.11.
|
|
18
|
+
- Production library build: **PASS**, Vite 6.4.4.
|
|
19
|
+
- Production dependency audit (`npm audit --omit=dev`): **PASS**, 0 reported findings.
|
|
20
|
+
- Typecheck: **FAIL**. The repository has existing errors in demo/test fixtures, including model shape mismatches and stale editor APIs. This scoped change also revealed no retained `defaultReviewAccessCan` missing-import error; full typecheck remains blocked by the listed broader issues.
|
|
21
|
+
- `npm audit`: **12 findings remain**: 4 high, 2 moderate, and 6 low. High findings are in the `patch-package` → `find-yarn-workspace-root` → `micromatch`/`braces` install tooling chain; npm offers only a major downgrade to patch-package 6.0.7. Remaining moderate findings are `pbkdf2` and `qs`; remaining low findings are in the `vite-plugin-node-polyfills`/`node-stdlib-browser` compatibility chain, whose npm fix also requires a major downgrade. These were not changed automatically because doing so would alter the patch workflow or browser polyfill support. Review and replace these toolchain components before production release if their install/build risk is unacceptable.
|
|
22
|
+
- Secret-pattern scan: **PASS for the checked patterns only** across tracked files and this reviewed change (0 matches); it does not prove the entire Git history is free of secrets.
|
|
23
|
+
|
|
24
|
+
## Remaining limits and risk
|
|
25
|
+
|
|
26
|
+
This review does not verify server authentication/authorization, hosting headers/CSP, CORS, CSRF, production secrets, external API rate limits, or runtime behavior in every host integration. The DOCX byte budgets bound archive expansion; malformed files and parser complexity still need normal failure handling. No browser exploit exercise or complete Git-history secrets scan was run.
|
package/docs/table-editing.md
CHANGED
|
@@ -30,7 +30,7 @@ Mọi structural command là một transaction duy nhất, nên Undo/Redo không
|
|
|
30
30
|
|
|
31
31
|
Khi ứng dụng chạy ở chế độ xem (`mode: "view"`), người dùng **không thể click hoặc chỉnh sửa canvas trực tiếp**. Toolbar, thanh hover rail và context menu chỉnh sửa hoàn toàn bị khóa.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
Trong `edit` mode, các thao tác bảng sẵn có được bật như trước. Nếu đặt `editContent: false`, thao tác bảng từ host sẽ tự tắt trừ khi ứng dụng cấp rõ ràng `hostTableMutation: true` sau khi xác thực actor và tài liệu tại lớp host/backend. Trong `view`/`comment` mode, quyền này mặc định tắt:
|
|
34
34
|
|
|
35
35
|
```ts
|
|
36
36
|
const editor = new KindyEditor({
|
|
@@ -42,6 +42,8 @@ const editor = new KindyEditor({
|
|
|
42
42
|
});
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
Các giới hạn để tránh payload làm nghẽn giao diện: tối đa 10.000 hàng đầu vào, 256 cột mỗi hàng, 50.000 ô trong bảng sau khi thay thế, 1.000.000 ký tự tổng, 100.000 ký tự và 1.024 đoạn trong một ô, 20.000 paragraph và 8 tầng bảng lồng. API trả `false` khi vượt giới hạn; bảng giữ nguyên.
|
|
46
|
+
|
|
45
47
|
### Các API thao tác bảng từ Host
|
|
46
48
|
|
|
47
49
|
#### 1. Lấy danh sách bảng (`getTables`)
|
|
@@ -539,6 +539,8 @@ Nếu sản phẩm yêu cầu Kindy chạy ở chế độ **View Mode** (ngư
|
|
|
539
539
|
|
|
540
540
|
### 10.1 Cấp quyền `hostTableMutation`
|
|
541
541
|
|
|
542
|
+
Quyền này mặc định tắt trong `view`/`comment` mode. Ở `edit` mode, thao tác bảng tích hợp vẫn được bật. Nếu đặt `editContent: false`, thao tác bảng từ host tự tắt trừ khi ứng dụng cấp rõ ràng `hostTableMutation: true` sau khi xác thực quyền người dùng và tài liệu. Đây là quyền ngoại lệ ở API client, không thay thế kiểm tra quyền backend.
|
|
543
|
+
|
|
542
544
|
```ts
|
|
543
545
|
const editor = new KindyEditor({
|
|
544
546
|
container: document.getElementById("editor-container"),
|
|
@@ -549,6 +551,8 @@ const editor = new KindyEditor({
|
|
|
549
551
|
});
|
|
550
552
|
```
|
|
551
553
|
|
|
554
|
+
Giới hạn đầu vào `replaceTableRows`: 10.000 hàng, 256 cột mỗi hàng, tối đa 50.000 ô sau khi thay thế, 1.000.000 ký tự tổng, 100.000 ký tự/1.024 paragraph mỗi ô, tối đa 20.000 paragraph và 8 tầng bảng lồng. API trả `false` nếu vượt ngưỡng và không ghi thay đổi.
|
|
555
|
+
|
|
552
556
|
### 10.2 Lấy danh sách bảng (`editor.getTables()`)
|
|
553
557
|
|
|
554
558
|
```ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kindy-editor",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Trình soạn thảo tài liệu Word chính xác, vẽ bằng canvas",
|
|
6
6
|
"license": "MIT",
|
|
@@ -59,10 +59,10 @@
|
|
|
59
59
|
"fontkit": "^2.0.4",
|
|
60
60
|
"pdfkit": "^0.19.0",
|
|
61
61
|
"txml": "^6.0.0",
|
|
62
|
-
"vite": "^6.0.0",
|
|
63
|
-
"vite-plugin-node-polyfills": "^0.28.0",
|
|
64
|
-
"vitest": "^3.0.0",
|
|
65
62
|
"typescript": "^5.9.2",
|
|
66
|
-
"
|
|
63
|
+
"vite": "^6.4.4",
|
|
64
|
+
"vite-plugin-node-polyfills": "^0.28.0",
|
|
65
|
+
"vitest": "^4.1.11",
|
|
66
|
+
"vue": "^3.5.43"
|
|
67
67
|
}
|
|
68
|
-
}
|
|
68
|
+
}
|
package/types/import.d.ts
CHANGED
|
@@ -10,6 +10,13 @@ export type { Document } from "./model";
|
|
|
10
10
|
|
|
11
11
|
export type ImportPhase = "unzip" | "styles" | "parse" | "map";
|
|
12
12
|
|
|
13
|
+
export type ImportErrorCode = "NOT_ZIP" | "ENCRYPTED" | "NO_DOCUMENT_PART" | "MALFORMED_XML" | "ARCHIVE_LIMIT";
|
|
14
|
+
|
|
15
|
+
/** Error raised when a DOCX is malformed, encrypted, missing its main part, or exceeds safe import limits. */
|
|
16
|
+
export declare class ImportError extends Error {
|
|
17
|
+
readonly code: ImportErrorCode;
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
/** A lossy-mapping decision — surfaced, not swallowed (deduplicated by code). */
|
|
14
21
|
export interface ImportWarning {
|
|
15
22
|
code: string;
|
package/types/kindy-editor.d.ts
CHANGED
|
@@ -26,6 +26,12 @@ export interface Participant {
|
|
|
26
26
|
user?: UserInfo;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/** Search and replace state. */
|
|
30
|
+
export interface SearchState {
|
|
31
|
+
index: number;
|
|
32
|
+
total: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
29
35
|
/** A WeSign signing box with laid-out geometry. `page` is 1-based for WeSign
|
|
30
36
|
* `PositionInfoRequest.page`; `pageIndex` is the 0-based layout index. */
|
|
31
37
|
export interface SignatureBox {
|
|
@@ -1026,6 +1032,16 @@ export interface EditorHandle {
|
|
|
1026
1032
|
pageNumber: number,
|
|
1027
1033
|
options?: { behavior?: "auto" | "smooth" },
|
|
1028
1034
|
): Promise<boolean>;
|
|
1035
|
+
/** Find & replace. search() highlights all matches, focuses first match, and returns state. */
|
|
1036
|
+
search(query: string, opts?: { matchCase?: boolean; wholeWord?: boolean }): SearchState;
|
|
1037
|
+
/** Navigate between search matches (-1 for previous, 1 for next) and scroll focus to it. */
|
|
1038
|
+
searchNav(dir: 1 | -1): SearchState;
|
|
1039
|
+
/** Replace the currently focused match with text and scroll focus to the next match. */
|
|
1040
|
+
searchReplaceCurrent(replacement: string): SearchState;
|
|
1041
|
+
/** Replace all matches in one atomic transaction. */
|
|
1042
|
+
searchReplaceAll(replacement: string): number;
|
|
1043
|
+
/** Clear search highlights and reset search state. */
|
|
1044
|
+
searchClear(): void;
|
|
1029
1045
|
/** Live-update {{KEY}} template placeholders in the document without full reload,
|
|
1030
1046
|
* preserving zoom, scroll position, and selection. */
|
|
1031
1047
|
setTemplateVariables(values: TemplateVariableValues, options?: SetTemplateVariablesOptions): void;
|
|
@@ -1218,5 +1234,15 @@ export declare class KindyEditor {
|
|
|
1218
1234
|
setQRCode(config: QRCodeConfig | null): void;
|
|
1219
1235
|
/** Get current header/footer QR code configuration. */
|
|
1220
1236
|
getQRCode(): QRCodeConfig | undefined;
|
|
1237
|
+
/** Find & replace. search() highlights all matches, focuses first match, and returns state. */
|
|
1238
|
+
search(query: string, opts?: { matchCase?: boolean; wholeWord?: boolean }): Promise<SearchState>;
|
|
1239
|
+
/** Navigate between search matches (-1 for previous, 1 for next) and scroll focus to it. */
|
|
1240
|
+
searchNav(dir: 1 | -1): Promise<SearchState>;
|
|
1241
|
+
/** Replace the currently focused match with text and scroll focus to the next match. */
|
|
1242
|
+
searchReplaceCurrent(replacement: string): Promise<SearchState>;
|
|
1243
|
+
/** Replace all search matches in document in a single atomic undoable transaction. */
|
|
1244
|
+
searchReplaceAll(replacement: string): Promise<number>;
|
|
1245
|
+
/** Clear search highlights and reset search state. */
|
|
1246
|
+
searchClear(): Promise<void>;
|
|
1221
1247
|
destroy(): void;
|
|
1222
1248
|
}
|