@xom11/whiteboard 0.10.0 → 0.11.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.
- package/dist/{chunk-ZVN356JZ.mjs → chunk-D257NCQW.mjs} +3 -3
- package/dist/{chunk-ZVN356JZ.mjs.map → chunk-D257NCQW.mjs.map} +1 -1
- package/dist/{chunk-PDKKDZ4H.mjs → chunk-YVJP7NRG.mjs} +3 -3
- package/dist/{chunk-PDKKDZ4H.mjs.map → chunk-YVJP7NRG.mjs.map} +1 -1
- package/dist/geometry-2d.js +6 -6
- package/dist/geometry-2d.js.map +1 -1
- package/dist/geometry-2d.mjs +1 -1
- package/dist/graph-2d.js +88 -20
- package/dist/graph-2d.js.map +1 -1
- package/dist/graph-2d.mjs +1 -1
- package/dist/{host-LZH2FZ2N.mjs → host-NKGV6RF2.mjs} +91 -23
- package/dist/host-NKGV6RF2.mjs.map +1 -0
- package/dist/{host-DJETSFCG.mjs → host-XVK7UCRE.mjs} +8 -8
- package/dist/host-XVK7UCRE.mjs.map +1 -0
- package/dist/index.d.mts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +122 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +32 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/host-DJETSFCG.mjs.map +0 -1
- package/dist/host-LZH2FZ2N.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -79,8 +79,24 @@ interface WhiteboardProps {
|
|
|
79
79
|
* `STABLE_STAMPS` để chỉ bật stamp ổn định.
|
|
80
80
|
*/
|
|
81
81
|
stamps?: ReadonlyArray<StampType>;
|
|
82
|
+
/**
|
|
83
|
+
* Snapshot từ server. Precedence: `initialScene` > localStorage > blank.
|
|
84
|
+
* - `undefined` (default) → đọc từ localStorage qua `storageKey`
|
|
85
|
+
* - `null` → explicit blank, bỏ qua localStorage
|
|
86
|
+
* - object → dùng làm initialData của Excalidraw, bỏ qua localStorage
|
|
87
|
+
*
|
|
88
|
+
* Dùng để load board từ server. Thường đi cùng `storageKey={null}` để
|
|
89
|
+
* tránh localStorage stale override server data.
|
|
90
|
+
*/
|
|
91
|
+
initialScene?: ExcalidrawSceneSnapshot | null;
|
|
92
|
+
/**
|
|
93
|
+
* Binary files (raster, base64) từ server. Add vào Excalidraw đúng 1 lần
|
|
94
|
+
* khi api ready. Dùng kèm `initialScene` cho flow load-from-server.
|
|
95
|
+
* Nếu cần inject files động về sau, dùng `onApi` rồi gọi `api.addFiles`.
|
|
96
|
+
*/
|
|
97
|
+
initialFiles?: BinaryFiles;
|
|
82
98
|
}
|
|
83
|
-
declare function Whiteboard({ storageKey, readOnly, onSceneChange, onFilesChange, onApi, langCode, stamps, }: WhiteboardProps): react_jsx_runtime.JSX.Element;
|
|
99
|
+
declare function Whiteboard({ storageKey, readOnly, onSceneChange, onFilesChange, onApi, langCode, stamps, initialScene, initialFiles, }: WhiteboardProps): react_jsx_runtime.JSX.Element;
|
|
84
100
|
|
|
85
101
|
declare function pickSyncableAppState(s: AppState): SyncableAppState;
|
|
86
102
|
|
package/dist/index.d.ts
CHANGED
|
@@ -79,8 +79,24 @@ interface WhiteboardProps {
|
|
|
79
79
|
* `STABLE_STAMPS` để chỉ bật stamp ổn định.
|
|
80
80
|
*/
|
|
81
81
|
stamps?: ReadonlyArray<StampType>;
|
|
82
|
+
/**
|
|
83
|
+
* Snapshot từ server. Precedence: `initialScene` > localStorage > blank.
|
|
84
|
+
* - `undefined` (default) → đọc từ localStorage qua `storageKey`
|
|
85
|
+
* - `null` → explicit blank, bỏ qua localStorage
|
|
86
|
+
* - object → dùng làm initialData của Excalidraw, bỏ qua localStorage
|
|
87
|
+
*
|
|
88
|
+
* Dùng để load board từ server. Thường đi cùng `storageKey={null}` để
|
|
89
|
+
* tránh localStorage stale override server data.
|
|
90
|
+
*/
|
|
91
|
+
initialScene?: ExcalidrawSceneSnapshot | null;
|
|
92
|
+
/**
|
|
93
|
+
* Binary files (raster, base64) từ server. Add vào Excalidraw đúng 1 lần
|
|
94
|
+
* khi api ready. Dùng kèm `initialScene` cho flow load-from-server.
|
|
95
|
+
* Nếu cần inject files động về sau, dùng `onApi` rồi gọi `api.addFiles`.
|
|
96
|
+
*/
|
|
97
|
+
initialFiles?: BinaryFiles;
|
|
82
98
|
}
|
|
83
|
-
declare function Whiteboard({ storageKey, readOnly, onSceneChange, onFilesChange, onApi, langCode, stamps, }: WhiteboardProps): react_jsx_runtime.JSX.Element;
|
|
99
|
+
declare function Whiteboard({ storageKey, readOnly, onSceneChange, onFilesChange, onApi, langCode, stamps, initialScene, initialFiles, }: WhiteboardProps): react_jsx_runtime.JSX.Element;
|
|
84
100
|
|
|
85
101
|
declare function pickSyncableAppState(s: AppState): SyncableAppState;
|
|
86
102
|
|
package/dist/index.js
CHANGED
|
@@ -2308,15 +2308,15 @@ function CloseIcon() {
|
|
|
2308
2308
|
] });
|
|
2309
2309
|
}
|
|
2310
2310
|
function UndoIcon() {
|
|
2311
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "
|
|
2312
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
2313
|
-
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3
|
|
2311
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
2312
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 10 L8 5 L8 8 L15 8 A5 5 0 0 1 20 13 L20 16" }),
|
|
2313
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 10 L8 15 L8 12" })
|
|
2314
2314
|
] });
|
|
2315
2315
|
}
|
|
2316
2316
|
function RedoIcon() {
|
|
2317
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "
|
|
2318
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
2319
|
-
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "
|
|
2317
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
2318
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 10 L16 5 L16 8 L9 8 A5 5 0 0 0 4 13 L4 16" }),
|
|
2319
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 10 L16 15 L16 12" })
|
|
2320
2320
|
] });
|
|
2321
2321
|
}
|
|
2322
2322
|
function AxisIcon() {
|
|
@@ -8756,9 +8756,15 @@ function CloseIcon3() {
|
|
|
8756
8756
|
] });
|
|
8757
8757
|
}
|
|
8758
8758
|
function UndoIcon3() {
|
|
8759
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "
|
|
8760
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
8761
|
-
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3
|
|
8759
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
8760
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 10 L8 5 L8 8 L15 8 A5 5 0 0 1 20 13 L20 16" }),
|
|
8761
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 10 L8 15 L8 12" })
|
|
8762
|
+
] });
|
|
8763
|
+
}
|
|
8764
|
+
function RedoIcon3() {
|
|
8765
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
8766
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 10 L16 5 L16 8 L9 8 A5 5 0 0 0 4 13 L4 16" }),
|
|
8767
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 10 L16 15 L16 12" })
|
|
8762
8768
|
] });
|
|
8763
8769
|
}
|
|
8764
8770
|
function ResetViewIcon() {
|
|
@@ -8843,9 +8849,23 @@ function PanelBody(props) {
|
|
|
8843
8849
|
disabled: !props.canUndo,
|
|
8844
8850
|
title: "Ho\xE0n t\xE1c (Ctrl/Cmd+Z)",
|
|
8845
8851
|
"aria-label": "Ho\xE0n t\xE1c",
|
|
8852
|
+
"data-testid": "undo-btn",
|
|
8846
8853
|
className: "inline-flex items-center justify-center rounded p-1 text-slate-600 transition hover:bg-slate-100 hover:text-slate-900 disabled:cursor-not-allowed disabled:text-slate-300 disabled:hover:bg-transparent",
|
|
8847
8854
|
children: /* @__PURE__ */ jsxRuntime.jsx(UndoIcon3, {})
|
|
8848
8855
|
}
|
|
8856
|
+
),
|
|
8857
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8858
|
+
"button",
|
|
8859
|
+
{
|
|
8860
|
+
type: "button",
|
|
8861
|
+
onClick: props.onRedo,
|
|
8862
|
+
disabled: !props.canRedo,
|
|
8863
|
+
title: "L\xE0m l\u1EA1i (Ctrl/Cmd+Shift+Z)",
|
|
8864
|
+
"aria-label": "L\xE0m l\u1EA1i",
|
|
8865
|
+
"data-testid": "redo-btn",
|
|
8866
|
+
className: "inline-flex items-center justify-center rounded p-1 text-slate-600 transition hover:bg-slate-100 hover:text-slate-900 disabled:cursor-not-allowed disabled:text-slate-300 disabled:hover:bg-transparent",
|
|
8867
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(RedoIcon3, {})
|
|
8868
|
+
}
|
|
8849
8869
|
)
|
|
8850
8870
|
] }) }),
|
|
8851
8871
|
/* @__PURE__ */ jsxRuntime.jsx(Section4, { label: "C\xF4ng c\u1EE5", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-4 gap-1", children: GRAPH_TOOLS.map((t) => {
|
|
@@ -9201,6 +9221,7 @@ var init_EditorPanel3 = __esm({
|
|
|
9201
9221
|
const [errors, setErrors] = React8.useState({});
|
|
9202
9222
|
const [tool, setToolState] = React8.useState("move");
|
|
9203
9223
|
const undoStackRef = React8.useRef([]);
|
|
9224
|
+
const redoStackRef = React8.useRef([]);
|
|
9204
9225
|
const idCounterRef = React8.useRef(1);
|
|
9205
9226
|
const toolRef = React8.useRef(tool);
|
|
9206
9227
|
toolRef.current = tool;
|
|
@@ -9211,6 +9232,7 @@ var init_EditorPanel3 = __esm({
|
|
|
9211
9232
|
const pushUndo = React8.useCallback((g) => {
|
|
9212
9233
|
undoStackRef.current.push(g);
|
|
9213
9234
|
if (undoStackRef.current.length > 30) undoStackRef.current.shift();
|
|
9235
|
+
redoStackRef.current = [];
|
|
9214
9236
|
}, []);
|
|
9215
9237
|
const setErrorsWithNotify = React8.useCallback(
|
|
9216
9238
|
(updater) => {
|
|
@@ -9227,7 +9249,8 @@ var init_EditorPanel3 = __esm({
|
|
|
9227
9249
|
tool: t,
|
|
9228
9250
|
showAxis: g.view.showAxis,
|
|
9229
9251
|
showGrid: g.view.showGrid,
|
|
9230
|
-
canUndo: undoStackRef.current.length > 0
|
|
9252
|
+
canUndo: undoStackRef.current.length > 0,
|
|
9253
|
+
canRedo: redoStackRef.current.length > 0
|
|
9231
9254
|
});
|
|
9232
9255
|
}, []);
|
|
9233
9256
|
const updateGraph = React8.useCallback(
|
|
@@ -9242,6 +9265,58 @@ var init_EditorPanel3 = __esm({
|
|
|
9242
9265
|
},
|
|
9243
9266
|
[pushUndo, notifyStateChange]
|
|
9244
9267
|
);
|
|
9268
|
+
const doUndo = React8.useCallback(() => {
|
|
9269
|
+
const prev = undoStackRef.current.pop();
|
|
9270
|
+
if (!prev) return;
|
|
9271
|
+
redoStackRef.current.push(graphRef.current);
|
|
9272
|
+
if (redoStackRef.current.length > 30) redoStackRef.current.shift();
|
|
9273
|
+
graphRef.current = prev;
|
|
9274
|
+
forceUpdate((n) => n + 1);
|
|
9275
|
+
propsRef.current.onStateChange({
|
|
9276
|
+
tool: toolRef.current,
|
|
9277
|
+
showAxis: prev.view.showAxis,
|
|
9278
|
+
showGrid: prev.view.showGrid,
|
|
9279
|
+
canUndo: undoStackRef.current.length > 0,
|
|
9280
|
+
canRedo: redoStackRef.current.length > 0
|
|
9281
|
+
});
|
|
9282
|
+
propsRef.current.onGraphChange?.(prev);
|
|
9283
|
+
}, []);
|
|
9284
|
+
const doRedo = React8.useCallback(() => {
|
|
9285
|
+
const next = redoStackRef.current.pop();
|
|
9286
|
+
if (!next) return;
|
|
9287
|
+
undoStackRef.current.push(graphRef.current);
|
|
9288
|
+
if (undoStackRef.current.length > 30) undoStackRef.current.shift();
|
|
9289
|
+
graphRef.current = next;
|
|
9290
|
+
forceUpdate((n) => n + 1);
|
|
9291
|
+
propsRef.current.onStateChange({
|
|
9292
|
+
tool: toolRef.current,
|
|
9293
|
+
showAxis: next.view.showAxis,
|
|
9294
|
+
showGrid: next.view.showGrid,
|
|
9295
|
+
canUndo: undoStackRef.current.length > 0,
|
|
9296
|
+
canRedo: redoStackRef.current.length > 0
|
|
9297
|
+
});
|
|
9298
|
+
propsRef.current.onGraphChange?.(next);
|
|
9299
|
+
}, []);
|
|
9300
|
+
React8.useEffect(() => {
|
|
9301
|
+
const onKey = (e) => {
|
|
9302
|
+
const ae = document.activeElement;
|
|
9303
|
+
const inField = !!(ae && (ae.tagName === "INPUT" || ae.tagName === "TEXTAREA" || ae.isContentEditable));
|
|
9304
|
+
if (inField) return;
|
|
9305
|
+
if (!(e.metaKey || e.ctrlKey)) return;
|
|
9306
|
+
const key = e.key.toLowerCase();
|
|
9307
|
+
if (key === "z" && !e.shiftKey) {
|
|
9308
|
+
e.preventDefault();
|
|
9309
|
+
e.stopPropagation();
|
|
9310
|
+
doUndo();
|
|
9311
|
+
} else if (key === "z" && e.shiftKey || key === "y" && !e.shiftKey) {
|
|
9312
|
+
e.preventDefault();
|
|
9313
|
+
e.stopPropagation();
|
|
9314
|
+
doRedo();
|
|
9315
|
+
}
|
|
9316
|
+
};
|
|
9317
|
+
window.addEventListener("keydown", onKey, { capture: true });
|
|
9318
|
+
return () => window.removeEventListener("keydown", onKey, { capture: true });
|
|
9319
|
+
}, [doUndo, doRedo]);
|
|
9245
9320
|
const onBoardEvent = React8.useCallback((ev) => {
|
|
9246
9321
|
const currentTool = toolRef.current;
|
|
9247
9322
|
if (currentTool === "point-on-curve" && ev.type === "click-curve" && ev.functionId && ev.x !== void 0) {
|
|
@@ -9294,7 +9369,8 @@ var init_EditorPanel3 = __esm({
|
|
|
9294
9369
|
tool: t,
|
|
9295
9370
|
showAxis: g.view.showAxis,
|
|
9296
9371
|
showGrid: g.view.showGrid,
|
|
9297
|
-
canUndo: undoStackRef.current.length > 0
|
|
9372
|
+
canUndo: undoStackRef.current.length > 0,
|
|
9373
|
+
canRedo: redoStackRef.current.length > 0
|
|
9298
9374
|
});
|
|
9299
9375
|
},
|
|
9300
9376
|
setShowAxis: (b) => updateGraph((g) => ({ ...g, view: { ...g.view, showAxis: b } })),
|
|
@@ -9303,19 +9379,8 @@ var init_EditorPanel3 = __esm({
|
|
|
9303
9379
|
...g,
|
|
9304
9380
|
view: { ...g.view, xMin: -10, xMax: 10, yMin: -10, yMax: 10 }
|
|
9305
9381
|
})),
|
|
9306
|
-
undo:
|
|
9307
|
-
|
|
9308
|
-
if (!prev) return;
|
|
9309
|
-
graphRef.current = prev;
|
|
9310
|
-
forceUpdate((n) => n + 1);
|
|
9311
|
-
propsRef.current.onStateChange({
|
|
9312
|
-
tool: toolRef.current,
|
|
9313
|
-
showAxis: prev.view.showAxis,
|
|
9314
|
-
showGrid: prev.view.showGrid,
|
|
9315
|
-
canUndo: undoStackRef.current.length > 0
|
|
9316
|
-
});
|
|
9317
|
-
propsRef.current.onGraphChange?.(prev);
|
|
9318
|
-
},
|
|
9382
|
+
undo: doUndo,
|
|
9383
|
+
redo: doRedo,
|
|
9319
9384
|
addFunction: (expr) => {
|
|
9320
9385
|
const g = graphRef.current;
|
|
9321
9386
|
if (g.functions.length >= MAX_FUNCTIONS) {
|
|
@@ -9408,7 +9473,7 @@ var init_EditorPanel3 = __esm({
|
|
|
9408
9473
|
}),
|
|
9409
9474
|
// deps: updateGraph stable; errors changes when function errors change; setErrorsWithNotify stable
|
|
9410
9475
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
9411
|
-
[updateGraph, errors, setErrorsWithNotify]
|
|
9476
|
+
[updateGraph, errors, setErrorsWithNotify, doUndo, doRedo]
|
|
9412
9477
|
);
|
|
9413
9478
|
React8.useEffect(() => {
|
|
9414
9479
|
if (!initialGraphNotifiedRef.current) {
|
|
@@ -9553,7 +9618,8 @@ var init_host4 = __esm({
|
|
|
9553
9618
|
tool: "move",
|
|
9554
9619
|
showAxis: true,
|
|
9555
9620
|
showGrid: true,
|
|
9556
|
-
canUndo: false
|
|
9621
|
+
canUndo: false,
|
|
9622
|
+
canRedo: false
|
|
9557
9623
|
};
|
|
9558
9624
|
Graph2DStampHost = React8.forwardRef(
|
|
9559
9625
|
function Graph2DStampHost2({ api, editingElement, onClose, isDark }, ref) {
|
|
@@ -9613,6 +9679,8 @@ var init_host4 = __esm({
|
|
|
9613
9679
|
onResetView: () => panelRef.current?.resetView(),
|
|
9614
9680
|
onUndo: () => panelRef.current?.undo(),
|
|
9615
9681
|
canUndo: graphUIState.canUndo,
|
|
9682
|
+
onRedo: () => panelRef.current?.redo(),
|
|
9683
|
+
canRedo: graphUIState.canRedo,
|
|
9616
9684
|
onClose,
|
|
9617
9685
|
isDark,
|
|
9618
9686
|
isMobile,
|
|
@@ -11361,7 +11429,9 @@ function Whiteboard({
|
|
|
11361
11429
|
onFilesChange,
|
|
11362
11430
|
onApi,
|
|
11363
11431
|
langCode = "vi-VN",
|
|
11364
|
-
stamps = DEFAULT_STAMPS
|
|
11432
|
+
stamps = DEFAULT_STAMPS,
|
|
11433
|
+
initialScene,
|
|
11434
|
+
initialFiles
|
|
11365
11435
|
}) {
|
|
11366
11436
|
const [api, setApi] = React8.useState(null);
|
|
11367
11437
|
const apiRef = React8.useRef(null);
|
|
@@ -11390,7 +11460,7 @@ function Whiteboard({
|
|
|
11390
11460
|
() => persistEnabled ? readScene(storageKey) : null,
|
|
11391
11461
|
[persistEnabled, storageKey]
|
|
11392
11462
|
);
|
|
11393
|
-
const effectiveInitialScene = persistedInitial ? {
|
|
11463
|
+
const effectiveInitialScene = initialScene !== void 0 ? initialScene : persistedInitial ? {
|
|
11394
11464
|
elements: persistedInitial.elements,
|
|
11395
11465
|
appState: persistedInitial.appState
|
|
11396
11466
|
} : null;
|
|
@@ -11565,6 +11635,30 @@ function Whiteboard({
|
|
|
11565
11635
|
console.warn("[whiteboard] flushPrune th\u1EA5t b\u1EA1i:", err);
|
|
11566
11636
|
}
|
|
11567
11637
|
};
|
|
11638
|
+
const initialFilesAddedRef = React8.useRef(false);
|
|
11639
|
+
React8.useEffect(() => {
|
|
11640
|
+
if (!api || initialFilesAddedRef.current) return;
|
|
11641
|
+
initialFilesAddedRef.current = true;
|
|
11642
|
+
if (!initialFiles) return;
|
|
11643
|
+
const entries = Object.entries(initialFiles);
|
|
11644
|
+
if (entries.length === 0) return;
|
|
11645
|
+
try {
|
|
11646
|
+
api.addFiles(
|
|
11647
|
+
entries.map(([id, f]) => ({
|
|
11648
|
+
id,
|
|
11649
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11650
|
+
dataURL: f.dataURL,
|
|
11651
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11652
|
+
mimeType: f.mimeType,
|
|
11653
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11654
|
+
created: f.created ?? Date.now()
|
|
11655
|
+
}))
|
|
11656
|
+
);
|
|
11657
|
+
entries.forEach(([id]) => knownFileIdsRef.current.add(id));
|
|
11658
|
+
} catch (err) {
|
|
11659
|
+
console.warn("[whiteboard] addFiles initialFiles th\u1EA5t b\u1EA1i:", err);
|
|
11660
|
+
}
|
|
11661
|
+
}, [api]);
|
|
11568
11662
|
React8.useEffect(() => {
|
|
11569
11663
|
if (!api || !persistEnabled) return;
|
|
11570
11664
|
let cancelled = false;
|