@xom11/whiteboard 0.7.0 → 0.10.0

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 (50) hide show
  1. package/README.md +51 -1
  2. package/dist/chunk-74VEEZBV.mjs +619 -0
  3. package/dist/chunk-74VEEZBV.mjs.map +1 -0
  4. package/dist/{chunk-BJX4YNA5.mjs → chunk-G7FR3AIV.mjs} +68 -12
  5. package/dist/chunk-G7FR3AIV.mjs.map +1 -0
  6. package/dist/{chunk-SHFOGORM.mjs → chunk-PDKKDZ4H.mjs} +4 -4
  7. package/dist/{chunk-SHFOGORM.mjs.map → chunk-PDKKDZ4H.mjs.map} +1 -1
  8. package/dist/chunk-PWIMZIB6.mjs +62 -0
  9. package/dist/chunk-PWIMZIB6.mjs.map +1 -0
  10. package/dist/{chunk-LPM4MM45.mjs → chunk-SBDMF4NQ.mjs} +3 -2
  11. package/dist/chunk-SBDMF4NQ.mjs.map +1 -0
  12. package/dist/chunk-WQOABS6N.mjs +197 -0
  13. package/dist/chunk-WQOABS6N.mjs.map +1 -0
  14. package/dist/{chunk-3SSQKRRO.mjs → chunk-ZVN356JZ.mjs} +4 -4
  15. package/dist/{chunk-3SSQKRRO.mjs.map → chunk-ZVN356JZ.mjs.map} +1 -1
  16. package/dist/geometry-2d.js +344 -228
  17. package/dist/geometry-2d.js.map +1 -1
  18. package/dist/geometry-2d.mjs +2 -2
  19. package/dist/geometry-3d.d.mts +1 -1
  20. package/dist/geometry-3d.d.ts +1 -1
  21. package/dist/geometry-3d.js +3411 -1277
  22. package/dist/geometry-3d.js.map +1 -1
  23. package/dist/geometry-3d.mjs +3 -2
  24. package/dist/graph-2d.js +360 -66
  25. package/dist/graph-2d.js.map +1 -1
  26. package/dist/graph-2d.mjs +2 -2
  27. package/dist/{host-T2W6R6SO.mjs → host-DJETSFCG.mjs} +272 -223
  28. package/dist/host-DJETSFCG.mjs.map +1 -0
  29. package/dist/{host-2QGKMGCT.mjs → host-LZH2FZ2N.mjs} +3 -3
  30. package/dist/{host-2QGKMGCT.mjs.map → host-LZH2FZ2N.mjs.map} +1 -1
  31. package/dist/host-N6ACNJKI.mjs +3226 -0
  32. package/dist/host-N6ACNJKI.mjs.map +1 -0
  33. package/dist/index.d.mts +133 -6
  34. package/dist/index.d.ts +133 -6
  35. package/dist/index.js +5634 -1999
  36. package/dist/index.js.map +1 -1
  37. package/dist/index.mjs +1231 -146
  38. package/dist/index.mjs.map +1 -1
  39. package/package.json +9 -6
  40. package/dist/chunk-BJX4YNA5.mjs.map +0 -1
  41. package/dist/chunk-DJTBZEAR.mjs +0 -25
  42. package/dist/chunk-DJTBZEAR.mjs.map +0 -1
  43. package/dist/chunk-HM7RIXJE.mjs +0 -331
  44. package/dist/chunk-HM7RIXJE.mjs.map +0 -1
  45. package/dist/chunk-HYXFHEDJ.mjs +0 -129
  46. package/dist/chunk-HYXFHEDJ.mjs.map +0 -1
  47. package/dist/chunk-LPM4MM45.mjs.map +0 -1
  48. package/dist/host-T2W6R6SO.mjs.map +0 -1
  49. package/dist/host-XUFON6CQ.mjs +0 -1422
  50. package/dist/host-XUFON6CQ.mjs.map +0 -1
package/dist/index.d.mts CHANGED
@@ -13,6 +13,7 @@ import { Geometry3DCustomData } from './geometry-3d.mjs';
13
13
  export { geometry3dStamp, isGeometry3DCustomData } from './geometry-3d.mjs';
14
14
  import { Graph2DCustomData } from './graph-2d.mjs';
15
15
  export { graph2dStamp, isGraph2DCustomData } from './graph-2d.mjs';
16
+ import { PDFDocumentProxy } from 'pdfjs-dist';
16
17
  import 'react';
17
18
 
18
19
  interface SyncableAppState {
@@ -35,9 +36,8 @@ declare const EXPERIMENTAL_STAMPS: ReadonlyArray<StampType>;
35
36
  /** Tất cả stamp (stable + experimental). Dùng khi consumer muốn full feature. */
36
37
  declare const ALL_STAMPS: ReadonlyArray<StampType>;
37
38
  /**
38
- * Set stamp mặc định cho Whiteboard. v0.7.0 trở đi = STABLE_STAMPS.
39
- * Consumer muốn experimental: `<Whiteboard stamps={ALL_STAMPS} />` hoặc
40
- * `[...DEFAULT_STAMPS, geometry3dStamp]`.
39
+ * Set stamp mặc định cho Whiteboard = ALL_STAMPS (bật tất cả tool).
40
+ * Consumer muốn ẩn experimental: `<Whiteboard stamps={STABLE_STAMPS} />`.
41
41
  *
42
42
  * Để thêm 1 stamp mới (vd chart):
43
43
  * 1. Tạo `src/stamps/chart/index.tsx` export `chartStamp: StampType`.
@@ -73,8 +73,10 @@ interface WhiteboardProps {
73
73
  langCode?: string;
74
74
  /**
75
75
  * Danh sách stamp đăng ký. Mỗi stamp khai báo phím tắt + toolbar button +
76
- * Host component (UI editing). Mặc định DEFAULT_STAMPS (geometry + latex).
77
- * Truyền `[...DEFAULT_STAMPS, customStamp]` để thêm stamp mới.
76
+ * Host component (UI editing). Mặc định DEFAULT_STAMPS (= ALL_STAMPS,
77
+ * gồm geometry + latex + geometry3d + graph2d).
78
+ * Truyền `[...DEFAULT_STAMPS, customStamp]` để thêm stamp mới hoặc
79
+ * `STABLE_STAMPS` để chỉ bật stamp ổn định.
78
80
  */
79
81
  stamps?: ReadonlyArray<StampType>;
80
82
  }
@@ -106,4 +108,129 @@ declare function restoreMissingStampFiles(api: any, elements: readonly ElementLi
106
108
 
107
109
  type StampCustomData = GeometryCustomData | LatexCustomData | Geometry3DCustomData | Graph2DCustomData;
108
110
 
109
- export { ALL_STAMPS, DEFAULT_STAMPS, EXPERIMENTAL_STAMPS, type ExcalidrawSceneSnapshot, Geometry3DCustomData, GeometryCustomData, Graph2DCustomData, LatexCustomData, STABLE_STAMPS, type StampCustomData, StampType, type SyncableAppState, Whiteboard, type WhiteboardProps, findStampForCustomData, isStampElement, pickSyncableAppState, restoreMissingStampFiles };
111
+ /**
112
+ * Rasterize PDF → PNG dataURLs cho từng trang.
113
+ *
114
+ * Worker config:
115
+ * - pdfjs-dist 5.x cần `GlobalWorkerOptions.workerSrc` (URL tới worker .mjs).
116
+ * - Mặc định trỏ CDN (jsdelivr). Consumer có thể override qua
117
+ * `configurePdfWorker(src)` trước khi gọi rasterize lần đầu.
118
+ * - Lý do CDN thay vì bundled worker: tsup không có cách clean để emit
119
+ * worker chunk ra cùng dist/ với URL ổn định, và consumer (Next.js)
120
+ * có thể self-host nếu cần offline.
121
+ *
122
+ * Lazy import pdfjs-dist để không phình bundle khi user không dùng PDF.
123
+ */
124
+
125
+ /**
126
+ * Override workerSrc trước khi rasterize. Gọi từ consumer khi cần self-host
127
+ * worker file (vd offline mode, CSP cấm CDN).
128
+ *
129
+ * Mặc định nếu không gọi: dùng CDN jsdelivr theo version pdfjs-dist đã cài.
130
+ */
131
+ declare function configurePdfWorker(workerSrc: string): void;
132
+ interface RasterizedPage {
133
+ pageNumber: number;
134
+ dataURL: string;
135
+ width: number;
136
+ height: number;
137
+ mimeType: 'image/png';
138
+ }
139
+ interface RasterizeOptions {
140
+ /** Scale render. Mặc định 2 (HiDPI sharp). */
141
+ scale?: number;
142
+ /** Danh sách trang 1-based. Mặc định: tất cả. */
143
+ pages?: number[];
144
+ /** Callback progress sau mỗi page. */
145
+ onProgress?: (done: number, total: number) => void;
146
+ /** AbortSignal để cancel giữa chừng. */
147
+ signal?: AbortSignal;
148
+ }
149
+ /**
150
+ * Load PDF chỉ để lấy `numPages` (vd hiển thị tổng số trang trên dialog).
151
+ * Caller phải tự đóng document bằng `closePdfDocument(doc)` khi xong.
152
+ */
153
+ declare function loadPdfDocument(source: File | Blob | ArrayBuffer): Promise<PDFDocumentProxy>;
154
+ declare function closePdfDocument(doc: PDFDocumentProxy): Promise<void>;
155
+ /**
156
+ * Render danh sách trang ra PNG dataURL.
157
+ *
158
+ * Lưu ý:
159
+ * - Sử dụng `OffscreenCanvas` nếu có (browser hiện đại) để không touch DOM,
160
+ * fallback `<canvas>` document.createElement.
161
+ * - Mỗi page render xong sẽ release canvas (cho GC sớm với PDF nhiều trang).
162
+ */
163
+ declare function rasterizePdf(doc: PDFDocumentProxy, options?: RasterizeOptions): Promise<RasterizedPage[]>;
164
+
165
+ type ExApi = any;
166
+ interface InsertRasterizedPagesOptions {
167
+ /** Scale dùng khi rasterize (để chia pixel → scene units). */
168
+ scale: number;
169
+ /** Toạ độ scene gốc cho page đầu tiên. Bỏ qua → giữa viewport. */
170
+ origin?: {
171
+ x: number;
172
+ y: number;
173
+ };
174
+ }
175
+ interface InsertRasterizedPagesResult {
176
+ insertedElementIds: string[];
177
+ fileIds: string[];
178
+ }
179
+ /**
180
+ * Chèn array `RasterizedPage` đã có sẵn vào scene. Tách ra để Whiteboard
181
+ * có thể: load doc → hỏi user range → rasterize → insert mà không phải gọi
182
+ * insertPdfPages (load lại từ ArrayBuffer 2 lần).
183
+ */
184
+ declare function insertRasterizedPagesIntoScene(api: ExApi, rendered: RasterizedPage[], options: InsertRasterizedPagesOptions): InsertRasterizedPagesResult;
185
+ interface InsertPdfPagesOptions {
186
+ /** Trang cần chèn (1-based). Bỏ qua → chèn tất cả. */
187
+ pages?: number[];
188
+ /** Scale rasterize. Mặc định 2. */
189
+ scale?: number;
190
+ /** Toạ độ scene gốc cho page đầu tiên. Bỏ qua → giữa viewport. */
191
+ origin?: {
192
+ x: number;
193
+ y: number;
194
+ };
195
+ /** Progress callback. */
196
+ onProgress?: (done: number, total: number) => void;
197
+ /** AbortSignal. */
198
+ signal?: AbortSignal;
199
+ }
200
+ interface InsertPdfPagesResult {
201
+ insertedElementIds: string[];
202
+ pages: RasterizedPage[];
203
+ }
204
+ /**
205
+ * High-level: rasterize PDF + insert thành nhiều image element xếp dọc.
206
+ *
207
+ * Flow:
208
+ * 1. loadPdfDocument(source) → PDFDocumentProxy
209
+ * 2. rasterizePdf(doc, {pages, scale}) → RasterizedPage[]
210
+ * 3. Tạo fileId cho từng page, gọi api.addFiles batch.
211
+ * 4. Tính position: page 1 trung tâm viewport (hoặc origin), các page sau
212
+ * xếp dưới, cách PAGE_GAP.
213
+ * 5. api.updateScene({ elements: [...cũ, ...mới] })
214
+ *
215
+ * Không serialize PDF bytes — sau insert, các page là image element thuần,
216
+ * không thể re-edit qua double-click (theo design quyết định).
217
+ */
218
+ declare function insertPdfPages(api: ExApi, source: File | Blob | ArrayBuffer, options?: InsertPdfPagesOptions): Promise<InsertPdfPagesResult>;
219
+
220
+ /**
221
+ * Parse chuỗi range trang dạng "1,3,5-10" → array số 1-based đã sort + dedupe.
222
+ *
223
+ * - Tokens cách nhau bằng dấu phẩy hoặc khoảng trắng.
224
+ * - Token có gạch "-" → range inclusive (5-10 = [5,6,7,8,9,10]).
225
+ * - Khoảng trắng quanh số bị bỏ qua.
226
+ * - Empty / chỉ space → [].
227
+ *
228
+ * Throws `Error` với message tiếng Việt khi:
229
+ * - Token không phải số / không phải range hợp lệ.
230
+ * - Số <= 0 hoặc > totalPages.
231
+ * - Range đảo ngược (vd "10-5") — coi là lỗi user thay vì auto-reverse để
232
+ * tránh nuốt typo.
233
+ */
234
+ declare function parsePageRange(input: string, totalPages: number): number[];
235
+
236
+ export { ALL_STAMPS, DEFAULT_STAMPS, EXPERIMENTAL_STAMPS, type ExcalidrawSceneSnapshot, Geometry3DCustomData, GeometryCustomData, Graph2DCustomData, type InsertPdfPagesOptions, type InsertPdfPagesResult, type InsertRasterizedPagesOptions, type InsertRasterizedPagesResult, LatexCustomData, type RasterizeOptions, type RasterizedPage, STABLE_STAMPS, type StampCustomData, StampType, type SyncableAppState, Whiteboard, type WhiteboardProps, closePdfDocument, configurePdfWorker, findStampForCustomData, insertPdfPages, insertRasterizedPagesIntoScene, isStampElement, loadPdfDocument, parsePageRange, pickSyncableAppState, rasterizePdf, restoreMissingStampFiles };
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ import { Geometry3DCustomData } from './geometry-3d.js';
13
13
  export { geometry3dStamp, isGeometry3DCustomData } from './geometry-3d.js';
14
14
  import { Graph2DCustomData } from './graph-2d.js';
15
15
  export { graph2dStamp, isGraph2DCustomData } from './graph-2d.js';
16
+ import { PDFDocumentProxy } from 'pdfjs-dist';
16
17
  import 'react';
17
18
 
18
19
  interface SyncableAppState {
@@ -35,9 +36,8 @@ declare const EXPERIMENTAL_STAMPS: ReadonlyArray<StampType>;
35
36
  /** Tất cả stamp (stable + experimental). Dùng khi consumer muốn full feature. */
36
37
  declare const ALL_STAMPS: ReadonlyArray<StampType>;
37
38
  /**
38
- * Set stamp mặc định cho Whiteboard. v0.7.0 trở đi = STABLE_STAMPS.
39
- * Consumer muốn experimental: `<Whiteboard stamps={ALL_STAMPS} />` hoặc
40
- * `[...DEFAULT_STAMPS, geometry3dStamp]`.
39
+ * Set stamp mặc định cho Whiteboard = ALL_STAMPS (bật tất cả tool).
40
+ * Consumer muốn ẩn experimental: `<Whiteboard stamps={STABLE_STAMPS} />`.
41
41
  *
42
42
  * Để thêm 1 stamp mới (vd chart):
43
43
  * 1. Tạo `src/stamps/chart/index.tsx` export `chartStamp: StampType`.
@@ -73,8 +73,10 @@ interface WhiteboardProps {
73
73
  langCode?: string;
74
74
  /**
75
75
  * Danh sách stamp đăng ký. Mỗi stamp khai báo phím tắt + toolbar button +
76
- * Host component (UI editing). Mặc định DEFAULT_STAMPS (geometry + latex).
77
- * Truyền `[...DEFAULT_STAMPS, customStamp]` để thêm stamp mới.
76
+ * Host component (UI editing). Mặc định DEFAULT_STAMPS (= ALL_STAMPS,
77
+ * gồm geometry + latex + geometry3d + graph2d).
78
+ * Truyền `[...DEFAULT_STAMPS, customStamp]` để thêm stamp mới hoặc
79
+ * `STABLE_STAMPS` để chỉ bật stamp ổn định.
78
80
  */
79
81
  stamps?: ReadonlyArray<StampType>;
80
82
  }
@@ -106,4 +108,129 @@ declare function restoreMissingStampFiles(api: any, elements: readonly ElementLi
106
108
 
107
109
  type StampCustomData = GeometryCustomData | LatexCustomData | Geometry3DCustomData | Graph2DCustomData;
108
110
 
109
- export { ALL_STAMPS, DEFAULT_STAMPS, EXPERIMENTAL_STAMPS, type ExcalidrawSceneSnapshot, Geometry3DCustomData, GeometryCustomData, Graph2DCustomData, LatexCustomData, STABLE_STAMPS, type StampCustomData, StampType, type SyncableAppState, Whiteboard, type WhiteboardProps, findStampForCustomData, isStampElement, pickSyncableAppState, restoreMissingStampFiles };
111
+ /**
112
+ * Rasterize PDF → PNG dataURLs cho từng trang.
113
+ *
114
+ * Worker config:
115
+ * - pdfjs-dist 5.x cần `GlobalWorkerOptions.workerSrc` (URL tới worker .mjs).
116
+ * - Mặc định trỏ CDN (jsdelivr). Consumer có thể override qua
117
+ * `configurePdfWorker(src)` trước khi gọi rasterize lần đầu.
118
+ * - Lý do CDN thay vì bundled worker: tsup không có cách clean để emit
119
+ * worker chunk ra cùng dist/ với URL ổn định, và consumer (Next.js)
120
+ * có thể self-host nếu cần offline.
121
+ *
122
+ * Lazy import pdfjs-dist để không phình bundle khi user không dùng PDF.
123
+ */
124
+
125
+ /**
126
+ * Override workerSrc trước khi rasterize. Gọi từ consumer khi cần self-host
127
+ * worker file (vd offline mode, CSP cấm CDN).
128
+ *
129
+ * Mặc định nếu không gọi: dùng CDN jsdelivr theo version pdfjs-dist đã cài.
130
+ */
131
+ declare function configurePdfWorker(workerSrc: string): void;
132
+ interface RasterizedPage {
133
+ pageNumber: number;
134
+ dataURL: string;
135
+ width: number;
136
+ height: number;
137
+ mimeType: 'image/png';
138
+ }
139
+ interface RasterizeOptions {
140
+ /** Scale render. Mặc định 2 (HiDPI sharp). */
141
+ scale?: number;
142
+ /** Danh sách trang 1-based. Mặc định: tất cả. */
143
+ pages?: number[];
144
+ /** Callback progress sau mỗi page. */
145
+ onProgress?: (done: number, total: number) => void;
146
+ /** AbortSignal để cancel giữa chừng. */
147
+ signal?: AbortSignal;
148
+ }
149
+ /**
150
+ * Load PDF chỉ để lấy `numPages` (vd hiển thị tổng số trang trên dialog).
151
+ * Caller phải tự đóng document bằng `closePdfDocument(doc)` khi xong.
152
+ */
153
+ declare function loadPdfDocument(source: File | Blob | ArrayBuffer): Promise<PDFDocumentProxy>;
154
+ declare function closePdfDocument(doc: PDFDocumentProxy): Promise<void>;
155
+ /**
156
+ * Render danh sách trang ra PNG dataURL.
157
+ *
158
+ * Lưu ý:
159
+ * - Sử dụng `OffscreenCanvas` nếu có (browser hiện đại) để không touch DOM,
160
+ * fallback `<canvas>` document.createElement.
161
+ * - Mỗi page render xong sẽ release canvas (cho GC sớm với PDF nhiều trang).
162
+ */
163
+ declare function rasterizePdf(doc: PDFDocumentProxy, options?: RasterizeOptions): Promise<RasterizedPage[]>;
164
+
165
+ type ExApi = any;
166
+ interface InsertRasterizedPagesOptions {
167
+ /** Scale dùng khi rasterize (để chia pixel → scene units). */
168
+ scale: number;
169
+ /** Toạ độ scene gốc cho page đầu tiên. Bỏ qua → giữa viewport. */
170
+ origin?: {
171
+ x: number;
172
+ y: number;
173
+ };
174
+ }
175
+ interface InsertRasterizedPagesResult {
176
+ insertedElementIds: string[];
177
+ fileIds: string[];
178
+ }
179
+ /**
180
+ * Chèn array `RasterizedPage` đã có sẵn vào scene. Tách ra để Whiteboard
181
+ * có thể: load doc → hỏi user range → rasterize → insert mà không phải gọi
182
+ * insertPdfPages (load lại từ ArrayBuffer 2 lần).
183
+ */
184
+ declare function insertRasterizedPagesIntoScene(api: ExApi, rendered: RasterizedPage[], options: InsertRasterizedPagesOptions): InsertRasterizedPagesResult;
185
+ interface InsertPdfPagesOptions {
186
+ /** Trang cần chèn (1-based). Bỏ qua → chèn tất cả. */
187
+ pages?: number[];
188
+ /** Scale rasterize. Mặc định 2. */
189
+ scale?: number;
190
+ /** Toạ độ scene gốc cho page đầu tiên. Bỏ qua → giữa viewport. */
191
+ origin?: {
192
+ x: number;
193
+ y: number;
194
+ };
195
+ /** Progress callback. */
196
+ onProgress?: (done: number, total: number) => void;
197
+ /** AbortSignal. */
198
+ signal?: AbortSignal;
199
+ }
200
+ interface InsertPdfPagesResult {
201
+ insertedElementIds: string[];
202
+ pages: RasterizedPage[];
203
+ }
204
+ /**
205
+ * High-level: rasterize PDF + insert thành nhiều image element xếp dọc.
206
+ *
207
+ * Flow:
208
+ * 1. loadPdfDocument(source) → PDFDocumentProxy
209
+ * 2. rasterizePdf(doc, {pages, scale}) → RasterizedPage[]
210
+ * 3. Tạo fileId cho từng page, gọi api.addFiles batch.
211
+ * 4. Tính position: page 1 trung tâm viewport (hoặc origin), các page sau
212
+ * xếp dưới, cách PAGE_GAP.
213
+ * 5. api.updateScene({ elements: [...cũ, ...mới] })
214
+ *
215
+ * Không serialize PDF bytes — sau insert, các page là image element thuần,
216
+ * không thể re-edit qua double-click (theo design quyết định).
217
+ */
218
+ declare function insertPdfPages(api: ExApi, source: File | Blob | ArrayBuffer, options?: InsertPdfPagesOptions): Promise<InsertPdfPagesResult>;
219
+
220
+ /**
221
+ * Parse chuỗi range trang dạng "1,3,5-10" → array số 1-based đã sort + dedupe.
222
+ *
223
+ * - Tokens cách nhau bằng dấu phẩy hoặc khoảng trắng.
224
+ * - Token có gạch "-" → range inclusive (5-10 = [5,6,7,8,9,10]).
225
+ * - Khoảng trắng quanh số bị bỏ qua.
226
+ * - Empty / chỉ space → [].
227
+ *
228
+ * Throws `Error` với message tiếng Việt khi:
229
+ * - Token không phải số / không phải range hợp lệ.
230
+ * - Số <= 0 hoặc > totalPages.
231
+ * - Range đảo ngược (vd "10-5") — coi là lỗi user thay vì auto-reverse để
232
+ * tránh nuốt typo.
233
+ */
234
+ declare function parsePageRange(input: string, totalPages: number): number[];
235
+
236
+ export { ALL_STAMPS, DEFAULT_STAMPS, EXPERIMENTAL_STAMPS, type ExcalidrawSceneSnapshot, Geometry3DCustomData, GeometryCustomData, Graph2DCustomData, type InsertPdfPagesOptions, type InsertPdfPagesResult, type InsertRasterizedPagesOptions, type InsertRasterizedPagesResult, LatexCustomData, type RasterizeOptions, type RasterizedPage, STABLE_STAMPS, type StampCustomData, StampType, type SyncableAppState, Whiteboard, type WhiteboardProps, closePdfDocument, configurePdfWorker, findStampForCustomData, insertPdfPages, insertRasterizedPagesIntoScene, isStampElement, loadPdfDocument, parsePageRange, pickSyncableAppState, rasterizePdf, restoreMissingStampFiles };