@xom11/whiteboard 0.2.1 → 0.6.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/README.md CHANGED
@@ -4,11 +4,14 @@ Excalidraw-based whiteboard component dùng cho HocTotBachKhoa classroom: bút/s
4
4
 
5
5
  ## Install
6
6
 
7
+ Cài qua git URL (repo public, không cần npm publish):
8
+
7
9
  ```bash
8
- npm install @xom11/whiteboard
9
- # hoặc
10
- pnpm add @xom11/whiteboard
11
- yarn add @xom11/whiteboard
10
+ # pin tag (recommended)
11
+ npm install github:xom11/whiteboard#v0.2.0
12
+
13
+ # hoặc trong package.json
14
+ "@xom11/whiteboard": "github:xom11/whiteboard#v0.2.0"
12
15
  ```
13
16
 
14
17
  Peer deps: `react >=18`, `react-dom >=18`, `next >=14`.
@@ -43,13 +46,17 @@ npm run dev # tsup watch mode
43
46
 
44
47
  ## Workflow phát hành phiên bản mới
45
48
 
49
+ `npm ci --ignore-scripts` ở consumer skip prepare hook → phải commit `dist/`:
50
+
46
51
  ```bash
47
- npm version patch # bump package.json + tạo git tag (vX.Y.Z)
52
+ npm run build
53
+ git add dist/
54
+ git commit -am "release vX.Y.Z"
55
+ npm version patch # bump package.json + tạo tag
48
56
  git push --follow-tags
49
- npm publish # prepublishOnly tự chạy typecheck + build
50
57
  ```
51
58
 
52
- `prepublishOnly` đảm bảo `dist/` luôn fresh. Consumer chỉ cần `npm install @xom11/whiteboard@latest`.
59
+ Consumer pin tag mới trong `package.json` rồi `npm install`.
53
60
 
54
61
  ## Architecture
55
62
 
@@ -18,5 +18,5 @@ function ExcalidrawWithMenus(props) {
18
18
  }
19
19
 
20
20
  export { ExcalidrawWithMenus };
21
- //# sourceMappingURL=ExcalidrawWithMenus-YGFFNZYY.mjs.map
22
- //# sourceMappingURL=ExcalidrawWithMenus-YGFFNZYY.mjs.map
21
+ //# sourceMappingURL=ExcalidrawWithMenus-KBLDWPM2.mjs.map
22
+ //# sourceMappingURL=ExcalidrawWithMenus-KBLDWPM2.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ExcalidrawWithMenus.tsx"],"names":[],"mappings":";;;AAiBO,SAAS,oBAAoB,KAAA,EAAwB;AAC1D,EAAA,MAAM,EAAE,QAAA,EAAU,GAAG,IAAA,EAAK,GAAI,KAAA;AAC9B,EAAA,uBACE,IAAA,CAAC,UAAA,EAAA,EAAY,GAAG,IAAA,EAEd,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAC,QAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,QAAA,CAAS,YAAA,CAAa,SAAA,EAAtB,EAAgC,CAAA;AAAA,sBACjC,GAAA,CAAC,QAAA,CAAS,YAAA,CAAa,WAAA,EAAtB,EAAkC,CAAA;AAAA,sBACnC,GAAA,CAAC,QAAA,CAAS,YAAA,CAAa,WAAA,EAAtB,EAAkC,CAAA;AAAA,sBACnC,GAAA,CAAC,QAAA,CAAS,YAAA,CAAa,WAAA,EAAtB,EAAkC;AAAA,KAAA,EACrC,CAAA;AAAA,oBAEA,GAAA,CAAC,MAAA,EAAA,EACC,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EACR,CAAA;AAAA,oBAGA,GAAA,CAAC,aAAA,EAAA,EACC,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EACR,CAAA;AAAA,IACC;AAAA,GAAA,EACH,CAAA;AAEJ","file":"ExcalidrawWithMenus-KBLDWPM2.mjs","sourcesContent":["'use client';\n\n// Client-only wrapper around Excalidraw that lets us reach for static helpers\n// like `MainMenu.DefaultItems.*`. Whiteboard dynamic-imports this\n// file so SSR never evaluates @excalidraw/excalidraw, while inside this file we\n// can use plain static imports (the entire module loads on the client).\n\nimport React from 'react';\nimport {\n Excalidraw,\n MainMenu,\n Footer,\n WelcomeScreen,\n} from '@excalidraw/excalidraw';\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype ExcalidrawProps = any;\n\nexport function ExcalidrawWithMenus(props: ExcalidrawProps) {\n const { children, ...rest } = props;\n return (\n <Excalidraw {...rest}>\n {/* Replace default menu with curated items — no socials/help/branding */}\n <MainMenu>\n <MainMenu.DefaultItems.LoadScene />\n <MainMenu.DefaultItems.SaveAsImage />\n <MainMenu.DefaultItems.ClearCanvas />\n <MainMenu.DefaultItems.ToggleTheme />\n </MainMenu>\n {/* Footer slot with no content suppresses default \"Made with Excalidraw\" link */}\n <Footer>\n <span />\n </Footer>\n {/* WelcomeScreen slot (empty) prevents the default Excalidraw welcome panel\n * (which includes the Excalidraw logo and social links) from appearing. */}\n <WelcomeScreen>\n <span />\n </WelcomeScreen>\n {children}\n </Excalidraw>\n );\n}\n"]}
package/dist/index.css CHANGED
@@ -1,4 +1,4 @@
1
- /* src/stamp/stamp.css */
1
+ /* src/stamps/shared/stamp.css */
2
2
  .theme--dark [data-stamp-area=true] {
3
3
  color: rgb(226 232 240);
4
4
  }
@@ -71,4 +71,32 @@
71
71
  border-color: rgb(71 85 105);
72
72
  color: rgb(226 232 240);
73
73
  }
74
+ .theme--dark [data-stamp-area=true] .bg-emerald-100 {
75
+ background-color: rgb(6 78 59);
76
+ }
77
+ .theme--dark [data-stamp-area=true] .border-emerald-500 {
78
+ border-color: rgb(52 211 153);
79
+ }
80
+ .theme--dark [data-stamp-area=true] .ring-emerald-300 {
81
+ --tw-ring-color: rgb(110 231 183);
82
+ }
83
+ .theme--dark [data-stamp-area=true] .border-rose-300 {
84
+ border-color: rgb(190 18 60);
85
+ }
86
+ .theme--dark [data-stamp-area=true] .hover\:bg-rose-100:hover {
87
+ background-color: rgb(127 29 29);
88
+ }
89
+ .theme--dark [data-stamp-area=true] .bg-gradient-to-r.from-emerald-600 {
90
+ background-image:
91
+ linear-gradient(
92
+ to right,
93
+ rgb(6 95 70),
94
+ rgb(15 76 76));
95
+ }
96
+ .theme--dark [data-stamp-area=true] .bg-emerald-600 {
97
+ background-color: rgb(6 95 70);
98
+ }
99
+ .theme--dark [data-stamp-area=true] .hover\:bg-emerald-700:hover {
100
+ background-color: rgb(4 120 87);
101
+ }
74
102
  /*# sourceMappingURL=index.css.map */
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/stamp/stamp.css"],"sourcesContent":["/* Dark-mode overrides cho math-stamp panels.\n * Active khi outer wrapper có class `theme--dark` (mirrored từ Excalidraw appState).\n * Selector `[data-stamp-area=\"true\"]` đã có sẵn trên mọi panel để dùng cho click-outside detection. */\n\n.theme--dark [data-stamp-area=\"true\"] {\n color: rgb(226 232 240);\n}\n\n/* ---- Backgrounds ---- */\n.theme--dark [data-stamp-area=\"true\"].bg-white,\n.theme--dark [data-stamp-area=\"true\"] .bg-white {\n background-color: rgb(30 41 59);\n}\n.theme--dark [data-stamp-area=\"true\"] .bg-slate-50 {\n background-color: rgb(15 23 42);\n}\n.theme--dark [data-stamp-area=\"true\"] .bg-emerald-50 {\n background-color: rgb(6 78 59);\n}\n.theme--dark [data-stamp-area=\"true\"] .bg-rose-50 {\n background-color: rgb(127 29 29);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:bg-slate-100:hover {\n background-color: rgb(51 65 85);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:bg-slate-50:hover {\n background-color: rgb(15 23 42);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:bg-emerald-50:hover {\n background-color: rgb(6 78 59);\n}\n\n/* Left-panel header có gradient sáng — đổi sang slate */\n.theme--dark [data-testid=\"stamp-left-panel\"] > header.bg-gradient-to-r {\n background-image: linear-gradient(to right, rgb(15 23 42), rgb(30 41 59));\n}\n\n/* ---- Borders ---- */\n.theme--dark [data-stamp-area=\"true\"] .border-slate-200,\n.theme--dark [data-stamp-area=\"true\"] .border-slate-300 {\n border-color: rgb(51 65 85);\n}\n\n/* ---- Text ---- */\n.theme--dark [data-stamp-area=\"true\"] .text-slate-400 {\n color: rgb(100 116 139);\n}\n.theme--dark [data-stamp-area=\"true\"] .text-slate-500 {\n color: rgb(148 163 184);\n}\n.theme--dark [data-stamp-area=\"true\"] .text-slate-600,\n.theme--dark [data-stamp-area=\"true\"] .text-slate-700,\n.theme--dark [data-stamp-area=\"true\"] .text-slate-800,\n.theme--dark [data-stamp-area=\"true\"] .text-slate-900 {\n color: rgb(226 232 240);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:text-slate-800:hover,\n.theme--dark [data-stamp-area=\"true\"] .hover\\:text-slate-900:hover {\n color: rgb(241 245 249);\n}\n.theme--dark [data-stamp-area=\"true\"] .text-emerald-700 {\n color: rgb(110 231 183);\n}\n.theme--dark [data-stamp-area=\"true\"] .text-rose-700 {\n color: rgb(253 164 175);\n}\n\n/* ---- Inputs ---- */\n.theme--dark [data-stamp-area=\"true\"] input[type=\"text\"],\n.theme--dark [data-stamp-area=\"true\"] textarea {\n background-color: rgb(15 23 42);\n color: rgb(241 245 249);\n}\n.theme--dark [data-stamp-area=\"true\"] input::placeholder,\n.theme--dark [data-stamp-area=\"true\"] textarea::placeholder {\n color: rgb(100 116 139);\n}\n\n/* ---- Kbd badge ---- */\n.theme--dark [data-stamp-area=\"true\"] kbd {\n background-color: rgb(15 23 42);\n border-color: rgb(71 85 105);\n color: rgb(226 232 240);\n}\n"],"mappings":";AAIA,CAAC,YAAY,CAAC;AACZ,SAAO,IAAI,IAAI,IAAI;AACrB;AAGA,CALC,YAKY,CAAC,qBAAuB,CAAC;AACtC,CANC,YAMY,CAAC,sBAAwB,CADA;AAEpC,oBAAkB,IAAI,GAAG,GAAG;AAC9B;AACA,CATC,YASY,CAAC,sBAAwB,CAAC;AACrC,oBAAkB,IAAI,GAAG,GAAG;AAC9B;AACA,CAZC,YAYY,CAAC,sBAAwB,CAAC;AACrC,oBAAkB,IAAI,EAAE,GAAG;AAC7B;AACA,CAfC,YAeY,CAAC,sBAAwB,CAAC;AACrC,oBAAkB,IAAI,IAAI,GAAG;AAC/B;AACA,CAlBC,YAkBY,CAAC,sBAAwB,CAAC,mBAAmB;AACxD,oBAAkB,IAAI,GAAG,GAAG;AAC9B;AACA,CArBC,YAqBY,CAAC,sBAAwB,CAAC,kBAAkB;AACvD,oBAAkB,IAAI,GAAG,GAAG;AAC9B;AACA,CAxBC,YAwBY,CAAC,sBAAwB,CAAC,oBAAoB;AACzD,oBAAkB,IAAI,EAAE,GAAG;AAC7B;AAGA,CA7BC,YA6BY,CAAC,8BAAgC,EAAE,MAAM,CAAC;AACrD;AAAA,IAAkB;AAAA,MAAgB,GAAG,KAAnB;AAAA,MAA0B,IAAI,GAAG,GAAG,GAApC;AAAA,MAAyC,IAAI,GAAG,GAAG;AACvE;AAGA,CAlCC,YAkCY,CAAC,sBAAwB,CAAC;AACvC,CAnCC,YAmCY,CAAC,sBAAwB,CAAC;AACrC,gBAAc,IAAI,GAAG,GAAG;AAC1B;AAGA,CAxCC,YAwCY,CAAC,sBAAwB,CAAC;AACrC,SAAO,IAAI,IAAI,IAAI;AACrB;AACA,CA3CC,YA2CY,CAAC,sBAAwB,CAAC;AACrC,SAAO,IAAI,IAAI,IAAI;AACrB;AACA,CA9CC,YA8CY,CAAC,sBAAwB,CAAC;AACvC,CA/CC,YA+CY,CAAC,sBAAwB,CAAC;AACvC,CAhDC,YAgDY,CAAC,sBAAwB,CAAC;AACvC,CAjDC,YAiDY,CAAC,sBAAwB,CAAC;AACrC,SAAO,IAAI,IAAI,IAAI;AACrB;AACA,CApDC,YAoDY,CAAC,sBAAwB,CAAC,qBAAqB;AAC5D,CArDC,YAqDY,CAAC,sBAAwB,CAAC,qBAAqB;AAC1D,SAAO,IAAI,IAAI,IAAI;AACrB;AACA,CAxDC,YAwDY,CAAC,sBAAwB,CAAC;AACrC,SAAO,IAAI,IAAI,IAAI;AACrB;AACA,CA3DC,YA2DY,CAAC,sBAAwB,CAAC;AACrC,SAAO,IAAI,IAAI,IAAI;AACrB;AAGA,CAhEC,YAgEY,CAAC,sBAAwB,KAAK,CAAC;AAC5C,CAjEC,YAiEY,CAAC,sBAAwB;AACpC,oBAAkB,IAAI,GAAG,GAAG;AAC5B,SAAO,IAAI,IAAI,IAAI;AACrB;AACA,CArEC,YAqEY,CAAC,sBAAwB,KAAK;AAC3C,CAtEC,YAsEY,CAAC,sBAAwB,QAAQ;AAC5C,SAAO,IAAI,IAAI,IAAI;AACrB;AAGA,CA3EC,YA2EY,CAAC,sBAAwB;AACpC,oBAAkB,IAAI,GAAG,GAAG;AAC5B,gBAAc,IAAI,GAAG,GAAG;AACxB,SAAO,IAAI,IAAI,IAAI;AACrB;","names":[]}
1
+ {"version":3,"sources":["../src/stamps/shared/stamp.css"],"sourcesContent":["/* Dark-mode overrides cho math-stamp panels.\n * Active khi outer wrapper có class `theme--dark` (mirrored từ Excalidraw appState).\n * Selector `[data-stamp-area=\"true\"]` đã có sẵn trên mọi panel để dùng cho click-outside detection. */\n\n.theme--dark [data-stamp-area=\"true\"] {\n color: rgb(226 232 240);\n}\n\n/* ---- Backgrounds ---- */\n.theme--dark [data-stamp-area=\"true\"].bg-white,\n.theme--dark [data-stamp-area=\"true\"] .bg-white {\n background-color: rgb(30 41 59);\n}\n.theme--dark [data-stamp-area=\"true\"] .bg-slate-50 {\n background-color: rgb(15 23 42);\n}\n.theme--dark [data-stamp-area=\"true\"] .bg-emerald-50 {\n background-color: rgb(6 78 59);\n}\n.theme--dark [data-stamp-area=\"true\"] .bg-rose-50 {\n background-color: rgb(127 29 29);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:bg-slate-100:hover {\n background-color: rgb(51 65 85);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:bg-slate-50:hover {\n background-color: rgb(15 23 42);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:bg-emerald-50:hover {\n background-color: rgb(6 78 59);\n}\n\n/* Left-panel header có gradient sáng — đổi sang slate */\n.theme--dark [data-testid=\"stamp-left-panel\"] > header.bg-gradient-to-r {\n background-image: linear-gradient(to right, rgb(15 23 42), rgb(30 41 59));\n}\n\n/* ---- Borders ---- */\n.theme--dark [data-stamp-area=\"true\"] .border-slate-200,\n.theme--dark [data-stamp-area=\"true\"] .border-slate-300 {\n border-color: rgb(51 65 85);\n}\n\n/* ---- Text ---- */\n.theme--dark [data-stamp-area=\"true\"] .text-slate-400 {\n color: rgb(100 116 139);\n}\n.theme--dark [data-stamp-area=\"true\"] .text-slate-500 {\n color: rgb(148 163 184);\n}\n.theme--dark [data-stamp-area=\"true\"] .text-slate-600,\n.theme--dark [data-stamp-area=\"true\"] .text-slate-700,\n.theme--dark [data-stamp-area=\"true\"] .text-slate-800,\n.theme--dark [data-stamp-area=\"true\"] .text-slate-900 {\n color: rgb(226 232 240);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:text-slate-800:hover,\n.theme--dark [data-stamp-area=\"true\"] .hover\\:text-slate-900:hover {\n color: rgb(241 245 249);\n}\n.theme--dark [data-stamp-area=\"true\"] .text-emerald-700 {\n color: rgb(110 231 183);\n}\n.theme--dark [data-stamp-area=\"true\"] .text-rose-700 {\n color: rgb(253 164 175);\n}\n\n/* ---- Inputs ---- */\n.theme--dark [data-stamp-area=\"true\"] input[type=\"text\"],\n.theme--dark [data-stamp-area=\"true\"] textarea {\n background-color: rgb(15 23 42);\n color: rgb(241 245 249);\n}\n.theme--dark [data-stamp-area=\"true\"] input::placeholder,\n.theme--dark [data-stamp-area=\"true\"] textarea::placeholder {\n color: rgb(100 116 139);\n}\n\n/* ---- Kbd badge ---- */\n.theme--dark [data-stamp-area=\"true\"] kbd {\n background-color: rgb(15 23 42);\n border-color: rgb(71 85 105);\n color: rgb(226 232 240);\n}\n\n/* --- Popover & swatch active states (Task 13) --- */\n.theme--dark [data-stamp-area=\"true\"] .bg-emerald-100 {\n background-color: rgb(6 78 59);\n}\n.theme--dark [data-stamp-area=\"true\"] .border-emerald-500 {\n border-color: rgb(52 211 153);\n}\n.theme--dark [data-stamp-area=\"true\"] .ring-emerald-300 {\n --tw-ring-color: rgb(110 231 183);\n}\n.theme--dark [data-stamp-area=\"true\"] .border-rose-300 {\n border-color: rgb(190 18 60);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:bg-rose-100:hover {\n background-color: rgb(127 29 29);\n}\n\n/* --- Geometry header dimmer trong dark --- */\n.theme--dark [data-stamp-area=\"true\"] .bg-gradient-to-r.from-emerald-600 {\n background-image: linear-gradient(to right, rgb(6 95 70), rgb(15 76 76));\n}\n.theme--dark [data-stamp-area=\"true\"] .bg-emerald-600 {\n background-color: rgb(6 95 70);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:bg-emerald-700:hover {\n background-color: rgb(4 120 87);\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.mts CHANGED
@@ -1,8 +1,9 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { NonDeletedExcalidrawElement } from '@excalidraw/excalidraw/element/types';
2
+ import { NonDeletedExcalidrawElement, ExcalidrawElement } from '@excalidraw/excalidraw/element/types';
3
3
  export { ExcalidrawElement, NonDeletedExcalidrawElement } from '@excalidraw/excalidraw/element/types';
4
4
  import { AppState, BinaryFiles } from '@excalidraw/excalidraw/types';
5
5
  export { AppState, BinaryFiles } from '@excalidraw/excalidraw/types';
6
+ import { ReactNode, ForwardRefExoticComponent, RefAttributes } from 'react';
6
7
 
7
8
  interface SyncableAppState {
8
9
  viewBackgroundColor: string;
@@ -17,19 +18,201 @@ interface ExcalidrawSceneSnapshot {
17
18
  appState: SyncableAppState;
18
19
  }
19
20
 
20
- interface ExcalidrawWhiteboardViewProps {
21
- role: 'teacher' | 'student';
22
- roomId: string;
23
- initialScene: ExcalidrawSceneSnapshot | null;
24
- remoteScene: ExcalidrawSceneSnapshot | null;
25
- remoteFiles?: BinaryFiles | null;
26
- onSceneChange: (snapshot: ExcalidrawSceneSnapshot) => void;
27
- onFilesChange: (files: BinaryFiles, newFileIds: string[]) => void;
21
+ /**
22
+ * Kết quả trả về từ `restoreFileFromCustomData`. Chứa đủ thông tin để
23
+ * consumer gọi `api.addFiles(...)`.
24
+ */
25
+ interface RestoredStampFile {
26
+ fileId: string;
27
+ dataURL: string;
28
+ mimeType: 'image/svg+xml' | 'image/png';
29
+ }
30
+ /**
31
+ * Tối thiểu mọi custom data của stamp cần có. Các stamp cụ thể (geometry,
32
+ * latex, ...) extend interface này với fields riêng.
33
+ */
34
+ interface BaseStampCustomData {
35
+ kind: string;
36
+ version: number;
37
+ }
38
+ /**
39
+ * Props mà mỗi StampHost nhận từ Whiteboard. Host component tự
40
+ * quản lý state nội bộ (panel ref, undo stack, displayMode...) — main view
41
+ * chỉ điều phối show/hide.
42
+ */
43
+ interface StampHostProps {
44
+ api: any;
45
+ /**
46
+ * Element đang re-edit (double-click) hoặc null nếu đang tạo mới.
47
+ * Host tự parse customData để load state ban đầu.
48
+ */
49
+ editingElement: {
50
+ id: string;
51
+ customData: unknown;
52
+ } | null;
53
+ /** Đóng stamp panel (gọi sau khi insert hoặc khi user huỷ). */
54
+ onClose: () => void;
55
+ /** Dark theme flag. */
56
+ isDark: boolean;
57
+ }
58
+ /**
59
+ * Imperative API mà main view truy cập qua ref:
60
+ * - tryInsert(): khi user click ra ngoài → auto-commit nếu valid.
61
+ * Trả về true nếu chèn thành công, false nếu chưa có nội dung.
62
+ * - hasContent(): có nội dung để chèn không.
63
+ */
64
+ interface StampHostHandle {
65
+ tryInsert(): boolean;
66
+ hasContent(): boolean;
67
+ }
68
+ type StampHostComponent = ForwardRefExoticComponent<StampHostProps & RefAttributes<StampHostHandle>>;
69
+ /**
70
+ * Định nghĩa 1 loại stamp. Mỗi stamp khai báo:
71
+ * - kind: unique string (khớp với customData.kind)
72
+ * - phím tắt + UI toolbar
73
+ * - cách nhận biết customData thuộc về stamp này (matchesCustomData)
74
+ * - cách re-render SVG từ customData (cho restore sau reload)
75
+ * - Host component: bọc trọn editor + left panel + insert logic
76
+ *
77
+ * Main view dispatch generic: `<stamp.Host ... />` — không cần biết kind.
78
+ */
79
+ interface StampType {
80
+ /** Unique kind. VD: 'geometry', 'latex'. Phải khớp với customData.kind. */
81
+ kind: string;
82
+ /** Phím tắt mở/đóng stamp (lowercase, 1 ký tự). VD: 'g', 'l'. */
83
+ shortcutKey: string;
84
+ /** Chữ hiển thị overlay góc dưới nút toolbar (e.g. "G"). */
85
+ toolbarLabel: string;
86
+ /** Tooltip + aria-label của nút toolbar. */
87
+ toolbarTitle: string;
88
+ /** Icon SVG (ReactNode) trong nút toolbar. */
89
+ toolbarIcon: ReactNode;
90
+ /** Test data-testid cho nút toolbar (optional). */
91
+ toolbarTestId?: string;
92
+ /** Type guard: customData có thuộc về stamp này không. */
93
+ matchesCustomData(data: unknown): boolean;
94
+ /**
95
+ * Re-render SVG từ customData. Dùng khi restore math-stamp file sau reload
96
+ * page (Excalidraw không persist binary file payload, chỉ giữ fileId trong
97
+ * element). SVG render với light palette (nét đậm) — Excalidraw tự đảo
98
+ * màu trong dark mode qua CSS filter.
99
+ */
100
+ renderSvgFromCustomData(data: unknown): Promise<string>;
101
+ /**
102
+ * Regenerate file SVG/PNG cho element thuộc stamp này khi reload từ persisted
103
+ * snapshot. Trả về `RestoredStampFile` để consumer gọi `api.addFiles`, hoặc
104
+ * `null` nếu element không cần file (vd stamp chỉ là text overlay).
105
+ *
106
+ * Khi method này có mặt, `restoreMissingStampFiles` sẽ ưu tiên gọi method
107
+ * này thay vì dùng `renderSvgFromCustomData`. Stamp tự chịu trách nhiệm lấy
108
+ * `fileId` từ element và render file.
109
+ */
110
+ restoreFileFromCustomData?: (element: ExcalidrawElement) => Promise<RestoredStampFile | null>;
111
+ /**
112
+ * Host component bọc toàn bộ UI editing (panel + left panel + insert
113
+ * handler). Whiteboard mount Host khi activeStamp khớp kind.
114
+ */
115
+ Host: StampHostComponent;
116
+ }
117
+
118
+ interface GeometryCustomData extends BaseStampCustomData {
119
+ kind: 'geometry';
120
+ version: 1;
121
+ jsonState: string;
122
+ svgWidth: number;
123
+ svgHeight: number;
124
+ }
125
+ declare function isGeometryCustomData(data: unknown): data is GeometryCustomData;
126
+ declare const geometryStamp: StampType;
127
+
128
+ interface LatexCustomData extends BaseStampCustomData {
129
+ kind: 'latex';
130
+ version: 1;
131
+ src: string;
132
+ displayMode: boolean;
133
+ }
134
+ declare function isLatexCustomData(data: unknown): data is LatexCustomData;
135
+ declare const latexStamp: StampType;
136
+
137
+ interface Geometry3DCustomData extends BaseStampCustomData {
138
+ kind: 'geometry3d';
139
+ version: 1;
140
+ jsonState: string;
141
+ svgWidth: number;
142
+ svgHeight: number;
143
+ }
144
+ declare function isGeometry3DCustomData(data: unknown): data is Geometry3DCustomData;
145
+
146
+ declare const geometry3dStamp: StampType;
147
+
148
+ /**
149
+ * Set stamp mặc định dùng trong Whiteboard. Consumer có thể
150
+ * truyền custom array để bật/tắt từng stamp hoặc đăng ký stamp mới.
151
+ *
152
+ * Để thêm 1 stamp mới (vd chart):
153
+ * 1. Tạo `src/stamp/registry/chart.tsx` với StampType object.
154
+ * 2. Add vào DEFAULT_STAMPS ở dưới, HOẶC consumer truyền
155
+ * `<Whiteboard stamps={[...DEFAULT_STAMPS, chartStamp]} />`.
156
+ */
157
+ declare const DEFAULT_STAMPS: ReadonlyArray<StampType>;
158
+ /** Tìm stamp tương ứng với customData của element. null nếu không match. */
159
+ declare function findStampForCustomData(data: unknown, stamps?: ReadonlyArray<StampType>): StampType | null;
160
+ /** isMathStamp version dựa trên registry — replace logic hardcode trong types.ts. */
161
+ declare function isStampElement<T extends {
162
+ customData?: unknown;
163
+ }>(element: T, stamps?: ReadonlyArray<StampType>): boolean;
164
+
165
+ interface WhiteboardProps {
166
+ /**
167
+ * Storage key cho persist client-side.
168
+ * - Scene -> localStorage['whiteboard:scene:'+storageKey]
169
+ * - Files raster -> IndexedDB 'whiteboard-files' index theo storageKey
170
+ * - Default: 'default'
171
+ * - Truyen `null` de tat persist (consumer drive state qua onApi).
172
+ */
173
+ storageKey?: string | null;
174
+ /** View-only (Excalidraw viewModeEnabled). Default false. */
175
+ readOnly?: boolean;
176
+ /** Local edits -> consumer broadcast. Optional. */
177
+ onSceneChange?: (snapshot: ExcalidrawSceneSnapshot) => void;
178
+ onFilesChange?: (files: BinaryFiles, newFileIds: string[]) => void;
179
+ /** Excalidraw imperative API. Consumer dung inject remote scene khi can. */
180
+ onApi?: (api: any) => void;
28
181
  /** Excalidraw UI language. Defaults to 'vi-VN'. See @excalidraw/excalidraw locales. */
29
182
  langCode?: string;
183
+ /**
184
+ * Danh sách stamp đăng ký. Mỗi stamp khai báo phím tắt + toolbar button +
185
+ * Host component (UI editing). Mặc định DEFAULT_STAMPS (geometry + latex).
186
+ * Truyền `[...DEFAULT_STAMPS, customStamp]` để thêm stamp mới.
187
+ */
188
+ stamps?: ReadonlyArray<StampType>;
30
189
  }
31
- declare function ExcalidrawWhiteboardView({ role, initialScene, remoteScene, remoteFiles, onSceneChange, onFilesChange, langCode, }: ExcalidrawWhiteboardViewProps): react_jsx_runtime.JSX.Element;
190
+ declare function Whiteboard({ storageKey, readOnly, onSceneChange, onFilesChange, onApi, langCode, stamps, }: WhiteboardProps): react_jsx_runtime.JSX.Element;
32
191
 
33
192
  declare function pickSyncableAppState(s: AppState): SyncableAppState;
34
193
 
35
- export { type ExcalidrawSceneSnapshot, ExcalidrawWhiteboardView, type ExcalidrawWhiteboardViewProps, type SyncableAppState, pickSyncableAppState };
194
+ interface ElementLike {
195
+ id: string;
196
+ type?: string;
197
+ fileId?: string | null;
198
+ customData?: unknown;
199
+ }
200
+ /**
201
+ * Find stamp elements whose binary file is missing from Excalidraw, then
202
+ * regenerate via registry dispatch. Idempotent: safe to call on every scene
203
+ * update.
204
+ *
205
+ * Stamps that implement `restoreFileFromCustomData` are handled via the new
206
+ * registry-driven path (stamp receives the full element and returns the file
207
+ * record). Stamps that only implement `renderSvgFromCustomData` use the legacy
208
+ * path (filter type=image + fileId, skip already-present files).
209
+ *
210
+ * @param api Excalidraw imperative API.
211
+ * @param elements Tất cả elements trong scene.
212
+ * @param stamps Registry. Default = DEFAULT_STAMPS.
213
+ */
214
+ declare function restoreMissingStampFiles(api: any, elements: readonly ElementLike[], stamps?: ReadonlyArray<StampType>): Promise<void>;
215
+
216
+ type StampCustomData = GeometryCustomData | LatexCustomData | Geometry3DCustomData;
217
+
218
+ export { type BaseStampCustomData, DEFAULT_STAMPS, type ExcalidrawSceneSnapshot, type Geometry3DCustomData, type GeometryCustomData, type LatexCustomData, type StampCustomData, type StampType, type SyncableAppState, Whiteboard, type WhiteboardProps, findStampForCustomData, geometry3dStamp, geometryStamp, isGeometry3DCustomData, isGeometryCustomData, isLatexCustomData, isStampElement, latexStamp, pickSyncableAppState, restoreMissingStampFiles };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { NonDeletedExcalidrawElement } from '@excalidraw/excalidraw/element/types';
2
+ import { NonDeletedExcalidrawElement, ExcalidrawElement } from '@excalidraw/excalidraw/element/types';
3
3
  export { ExcalidrawElement, NonDeletedExcalidrawElement } from '@excalidraw/excalidraw/element/types';
4
4
  import { AppState, BinaryFiles } from '@excalidraw/excalidraw/types';
5
5
  export { AppState, BinaryFiles } from '@excalidraw/excalidraw/types';
6
+ import { ReactNode, ForwardRefExoticComponent, RefAttributes } from 'react';
6
7
 
7
8
  interface SyncableAppState {
8
9
  viewBackgroundColor: string;
@@ -17,19 +18,201 @@ interface ExcalidrawSceneSnapshot {
17
18
  appState: SyncableAppState;
18
19
  }
19
20
 
20
- interface ExcalidrawWhiteboardViewProps {
21
- role: 'teacher' | 'student';
22
- roomId: string;
23
- initialScene: ExcalidrawSceneSnapshot | null;
24
- remoteScene: ExcalidrawSceneSnapshot | null;
25
- remoteFiles?: BinaryFiles | null;
26
- onSceneChange: (snapshot: ExcalidrawSceneSnapshot) => void;
27
- onFilesChange: (files: BinaryFiles, newFileIds: string[]) => void;
21
+ /**
22
+ * Kết quả trả về từ `restoreFileFromCustomData`. Chứa đủ thông tin để
23
+ * consumer gọi `api.addFiles(...)`.
24
+ */
25
+ interface RestoredStampFile {
26
+ fileId: string;
27
+ dataURL: string;
28
+ mimeType: 'image/svg+xml' | 'image/png';
29
+ }
30
+ /**
31
+ * Tối thiểu mọi custom data của stamp cần có. Các stamp cụ thể (geometry,
32
+ * latex, ...) extend interface này với fields riêng.
33
+ */
34
+ interface BaseStampCustomData {
35
+ kind: string;
36
+ version: number;
37
+ }
38
+ /**
39
+ * Props mà mỗi StampHost nhận từ Whiteboard. Host component tự
40
+ * quản lý state nội bộ (panel ref, undo stack, displayMode...) — main view
41
+ * chỉ điều phối show/hide.
42
+ */
43
+ interface StampHostProps {
44
+ api: any;
45
+ /**
46
+ * Element đang re-edit (double-click) hoặc null nếu đang tạo mới.
47
+ * Host tự parse customData để load state ban đầu.
48
+ */
49
+ editingElement: {
50
+ id: string;
51
+ customData: unknown;
52
+ } | null;
53
+ /** Đóng stamp panel (gọi sau khi insert hoặc khi user huỷ). */
54
+ onClose: () => void;
55
+ /** Dark theme flag. */
56
+ isDark: boolean;
57
+ }
58
+ /**
59
+ * Imperative API mà main view truy cập qua ref:
60
+ * - tryInsert(): khi user click ra ngoài → auto-commit nếu valid.
61
+ * Trả về true nếu chèn thành công, false nếu chưa có nội dung.
62
+ * - hasContent(): có nội dung để chèn không.
63
+ */
64
+ interface StampHostHandle {
65
+ tryInsert(): boolean;
66
+ hasContent(): boolean;
67
+ }
68
+ type StampHostComponent = ForwardRefExoticComponent<StampHostProps & RefAttributes<StampHostHandle>>;
69
+ /**
70
+ * Định nghĩa 1 loại stamp. Mỗi stamp khai báo:
71
+ * - kind: unique string (khớp với customData.kind)
72
+ * - phím tắt + UI toolbar
73
+ * - cách nhận biết customData thuộc về stamp này (matchesCustomData)
74
+ * - cách re-render SVG từ customData (cho restore sau reload)
75
+ * - Host component: bọc trọn editor + left panel + insert logic
76
+ *
77
+ * Main view dispatch generic: `<stamp.Host ... />` — không cần biết kind.
78
+ */
79
+ interface StampType {
80
+ /** Unique kind. VD: 'geometry', 'latex'. Phải khớp với customData.kind. */
81
+ kind: string;
82
+ /** Phím tắt mở/đóng stamp (lowercase, 1 ký tự). VD: 'g', 'l'. */
83
+ shortcutKey: string;
84
+ /** Chữ hiển thị overlay góc dưới nút toolbar (e.g. "G"). */
85
+ toolbarLabel: string;
86
+ /** Tooltip + aria-label của nút toolbar. */
87
+ toolbarTitle: string;
88
+ /** Icon SVG (ReactNode) trong nút toolbar. */
89
+ toolbarIcon: ReactNode;
90
+ /** Test data-testid cho nút toolbar (optional). */
91
+ toolbarTestId?: string;
92
+ /** Type guard: customData có thuộc về stamp này không. */
93
+ matchesCustomData(data: unknown): boolean;
94
+ /**
95
+ * Re-render SVG từ customData. Dùng khi restore math-stamp file sau reload
96
+ * page (Excalidraw không persist binary file payload, chỉ giữ fileId trong
97
+ * element). SVG render với light palette (nét đậm) — Excalidraw tự đảo
98
+ * màu trong dark mode qua CSS filter.
99
+ */
100
+ renderSvgFromCustomData(data: unknown): Promise<string>;
101
+ /**
102
+ * Regenerate file SVG/PNG cho element thuộc stamp này khi reload từ persisted
103
+ * snapshot. Trả về `RestoredStampFile` để consumer gọi `api.addFiles`, hoặc
104
+ * `null` nếu element không cần file (vd stamp chỉ là text overlay).
105
+ *
106
+ * Khi method này có mặt, `restoreMissingStampFiles` sẽ ưu tiên gọi method
107
+ * này thay vì dùng `renderSvgFromCustomData`. Stamp tự chịu trách nhiệm lấy
108
+ * `fileId` từ element và render file.
109
+ */
110
+ restoreFileFromCustomData?: (element: ExcalidrawElement) => Promise<RestoredStampFile | null>;
111
+ /**
112
+ * Host component bọc toàn bộ UI editing (panel + left panel + insert
113
+ * handler). Whiteboard mount Host khi activeStamp khớp kind.
114
+ */
115
+ Host: StampHostComponent;
116
+ }
117
+
118
+ interface GeometryCustomData extends BaseStampCustomData {
119
+ kind: 'geometry';
120
+ version: 1;
121
+ jsonState: string;
122
+ svgWidth: number;
123
+ svgHeight: number;
124
+ }
125
+ declare function isGeometryCustomData(data: unknown): data is GeometryCustomData;
126
+ declare const geometryStamp: StampType;
127
+
128
+ interface LatexCustomData extends BaseStampCustomData {
129
+ kind: 'latex';
130
+ version: 1;
131
+ src: string;
132
+ displayMode: boolean;
133
+ }
134
+ declare function isLatexCustomData(data: unknown): data is LatexCustomData;
135
+ declare const latexStamp: StampType;
136
+
137
+ interface Geometry3DCustomData extends BaseStampCustomData {
138
+ kind: 'geometry3d';
139
+ version: 1;
140
+ jsonState: string;
141
+ svgWidth: number;
142
+ svgHeight: number;
143
+ }
144
+ declare function isGeometry3DCustomData(data: unknown): data is Geometry3DCustomData;
145
+
146
+ declare const geometry3dStamp: StampType;
147
+
148
+ /**
149
+ * Set stamp mặc định dùng trong Whiteboard. Consumer có thể
150
+ * truyền custom array để bật/tắt từng stamp hoặc đăng ký stamp mới.
151
+ *
152
+ * Để thêm 1 stamp mới (vd chart):
153
+ * 1. Tạo `src/stamp/registry/chart.tsx` với StampType object.
154
+ * 2. Add vào DEFAULT_STAMPS ở dưới, HOẶC consumer truyền
155
+ * `<Whiteboard stamps={[...DEFAULT_STAMPS, chartStamp]} />`.
156
+ */
157
+ declare const DEFAULT_STAMPS: ReadonlyArray<StampType>;
158
+ /** Tìm stamp tương ứng với customData của element. null nếu không match. */
159
+ declare function findStampForCustomData(data: unknown, stamps?: ReadonlyArray<StampType>): StampType | null;
160
+ /** isMathStamp version dựa trên registry — replace logic hardcode trong types.ts. */
161
+ declare function isStampElement<T extends {
162
+ customData?: unknown;
163
+ }>(element: T, stamps?: ReadonlyArray<StampType>): boolean;
164
+
165
+ interface WhiteboardProps {
166
+ /**
167
+ * Storage key cho persist client-side.
168
+ * - Scene -> localStorage['whiteboard:scene:'+storageKey]
169
+ * - Files raster -> IndexedDB 'whiteboard-files' index theo storageKey
170
+ * - Default: 'default'
171
+ * - Truyen `null` de tat persist (consumer drive state qua onApi).
172
+ */
173
+ storageKey?: string | null;
174
+ /** View-only (Excalidraw viewModeEnabled). Default false. */
175
+ readOnly?: boolean;
176
+ /** Local edits -> consumer broadcast. Optional. */
177
+ onSceneChange?: (snapshot: ExcalidrawSceneSnapshot) => void;
178
+ onFilesChange?: (files: BinaryFiles, newFileIds: string[]) => void;
179
+ /** Excalidraw imperative API. Consumer dung inject remote scene khi can. */
180
+ onApi?: (api: any) => void;
28
181
  /** Excalidraw UI language. Defaults to 'vi-VN'. See @excalidraw/excalidraw locales. */
29
182
  langCode?: string;
183
+ /**
184
+ * Danh sách stamp đăng ký. Mỗi stamp khai báo phím tắt + toolbar button +
185
+ * Host component (UI editing). Mặc định DEFAULT_STAMPS (geometry + latex).
186
+ * Truyền `[...DEFAULT_STAMPS, customStamp]` để thêm stamp mới.
187
+ */
188
+ stamps?: ReadonlyArray<StampType>;
30
189
  }
31
- declare function ExcalidrawWhiteboardView({ role, initialScene, remoteScene, remoteFiles, onSceneChange, onFilesChange, langCode, }: ExcalidrawWhiteboardViewProps): react_jsx_runtime.JSX.Element;
190
+ declare function Whiteboard({ storageKey, readOnly, onSceneChange, onFilesChange, onApi, langCode, stamps, }: WhiteboardProps): react_jsx_runtime.JSX.Element;
32
191
 
33
192
  declare function pickSyncableAppState(s: AppState): SyncableAppState;
34
193
 
35
- export { type ExcalidrawSceneSnapshot, ExcalidrawWhiteboardView, type ExcalidrawWhiteboardViewProps, type SyncableAppState, pickSyncableAppState };
194
+ interface ElementLike {
195
+ id: string;
196
+ type?: string;
197
+ fileId?: string | null;
198
+ customData?: unknown;
199
+ }
200
+ /**
201
+ * Find stamp elements whose binary file is missing from Excalidraw, then
202
+ * regenerate via registry dispatch. Idempotent: safe to call on every scene
203
+ * update.
204
+ *
205
+ * Stamps that implement `restoreFileFromCustomData` are handled via the new
206
+ * registry-driven path (stamp receives the full element and returns the file
207
+ * record). Stamps that only implement `renderSvgFromCustomData` use the legacy
208
+ * path (filter type=image + fileId, skip already-present files).
209
+ *
210
+ * @param api Excalidraw imperative API.
211
+ * @param elements Tất cả elements trong scene.
212
+ * @param stamps Registry. Default = DEFAULT_STAMPS.
213
+ */
214
+ declare function restoreMissingStampFiles(api: any, elements: readonly ElementLike[], stamps?: ReadonlyArray<StampType>): Promise<void>;
215
+
216
+ type StampCustomData = GeometryCustomData | LatexCustomData | Geometry3DCustomData;
217
+
218
+ export { type BaseStampCustomData, DEFAULT_STAMPS, type ExcalidrawSceneSnapshot, type Geometry3DCustomData, type GeometryCustomData, type LatexCustomData, type StampCustomData, type StampType, type SyncableAppState, Whiteboard, type WhiteboardProps, findStampForCustomData, geometry3dStamp, geometryStamp, isGeometry3DCustomData, isGeometryCustomData, isLatexCustomData, isStampElement, latexStamp, pickSyncableAppState, restoreMissingStampFiles };