@q9labsai/chalk-whiteboard 5.0.1 → 5.2.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/CHANGELOG.md +28 -0
- package/dist/chunk-IVFWOYSW.js +10 -0
- package/dist/{chunk-W4FLPXAX.js → chunk-MPNIDZHM.js} +1 -1
- package/dist/{chunk-6ZV7GBAC.js → chunk-X3WDTGPQ.js} +57 -7
- package/dist/collab/engine.d.ts +41 -0
- package/dist/collab/index.js +3 -2
- package/dist/collab/ink.d.ts +5 -0
- package/dist/collab/presence.d.ts +1 -0
- package/dist/embedded/chalk-whiteboard/asset-manifest.json +2 -2
- package/dist/embedded/chalk-whiteboard/renderer.js +579 -579
- package/dist/embedded/controller.d.ts +17 -1
- package/dist/embedded/index.js +2 -0
- package/dist/{engine-BIJ57YN3.js → engine-MERF7RL3.js} +2 -1
- package/dist/index.js +3 -2
- package/dist/react/WhiteboardCanvas.d.ts +17 -0
- package/dist/react/engine-history.d.ts +7 -0
- package/dist/react/index.d.ts +3 -1
- package/dist/react/index.js +164 -55
- package/package.json +4 -3
|
@@ -33,7 +33,23 @@ export type ChalkEmbeddedWhiteboardTransportEvent = {
|
|
|
33
33
|
readonly type: "reset_required";
|
|
34
34
|
readonly sceneId: string;
|
|
35
35
|
readonly reason: "scene_changed" | "cursor_expired" | "gap";
|
|
36
|
-
}
|
|
36
|
+
} | ({
|
|
37
|
+
readonly type: "view";
|
|
38
|
+
readonly participantId: string;
|
|
39
|
+
readonly displayName: string;
|
|
40
|
+
readonly x: number;
|
|
41
|
+
readonly y: number;
|
|
42
|
+
readonly width: number;
|
|
43
|
+
readonly height: number;
|
|
44
|
+
readonly bring: boolean;
|
|
45
|
+
readonly occurredAt: string;
|
|
46
|
+
} & ({
|
|
47
|
+
readonly originParticipantId: string;
|
|
48
|
+
readonly originSequence: number;
|
|
49
|
+
} | {
|
|
50
|
+
readonly originParticipantId?: never;
|
|
51
|
+
readonly originSequence?: never;
|
|
52
|
+
}));
|
|
37
53
|
export interface ChalkEmbeddedWhiteboardFileTransport {
|
|
38
54
|
readonly getDownloadUrl: (fileId: string) => Promise<{
|
|
39
55
|
readonly downloadUrl: string;
|
package/dist/embedded/index.js
CHANGED
|
@@ -415,6 +415,8 @@ var ChalkWhiteboardController = class {
|
|
|
415
415
|
case "cursor":
|
|
416
416
|
this.#send({ type: "apply_cursor", payload: event });
|
|
417
417
|
return;
|
|
418
|
+
case "view":
|
|
419
|
+
return;
|
|
418
420
|
case "reset_required":
|
|
419
421
|
this.#sceneGeneration = null;
|
|
420
422
|
this.#send({ type: "reset_required", payload: event });
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
mergeWhiteboardElements
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MPNIDZHM.js";
|
|
4
4
|
import {
|
|
5
5
|
fromWireElement,
|
|
6
6
|
isWireElement,
|
|
7
7
|
toWireElement
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-X3WDTGPQ.js";
|
|
9
|
+
import "./chunk-IVFWOYSW.js";
|
|
9
10
|
export {
|
|
10
11
|
fromWireElement,
|
|
11
12
|
isWireElement,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import type { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types";
|
|
3
3
|
import type { ExcalidrawCollabEngineOptions } from "../collab/engine.js";
|
|
4
|
+
type ExcalidrawModule = typeof import("@excalidraw/excalidraw");
|
|
4
5
|
export interface WhiteboardCollaborationOptions extends Omit<ExcalidrawCollabEngineOptions, "excalidrawAPI" | "canDraw"> {
|
|
5
6
|
canDraw?: boolean;
|
|
6
7
|
}
|
|
@@ -29,8 +30,24 @@ export interface WhiteboardCanvasIcons {
|
|
|
29
30
|
submit?: ReactNode;
|
|
30
31
|
submitting?: ReactNode;
|
|
31
32
|
}
|
|
33
|
+
/** What a host that draws its own controls needs from the loaded engine. */
|
|
34
|
+
export interface WhiteboardEngineHandle {
|
|
35
|
+
api: ExcalidrawImperativeAPI;
|
|
36
|
+
excalidraw: ExcalidrawModule;
|
|
37
|
+
/** Clears the shared board; resolves once the new empty scene is committed. */
|
|
38
|
+
clear: () => Promise<void>;
|
|
39
|
+
/** Clears and returns a restore bound to that committed scene and this engine lifetime. */
|
|
40
|
+
clearWithUndo: () => Promise<() => boolean>;
|
|
41
|
+
/** Renders LaTeX and places it on the board, or replaces the selected formula. */
|
|
42
|
+
placeMath: (latex: string) => Promise<void>;
|
|
43
|
+
}
|
|
32
44
|
export interface WhiteboardCanvasProps {
|
|
33
45
|
className?: string;
|
|
46
|
+
/** Hides every engine control; the host draws its own and drives the engine through `onEngineReady`. */
|
|
47
|
+
headless?: boolean;
|
|
48
|
+
/** The colour this viewer sees for Ink. Ink travels as one canonical colour. */
|
|
49
|
+
inkColor?: string;
|
|
50
|
+
onEngineReady?: (engine: WhiteboardEngineHandle) => void;
|
|
34
51
|
isVisible?: boolean;
|
|
35
52
|
canDraw?: boolean;
|
|
36
53
|
theme?: "light" | "dark";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types";
|
|
2
|
+
export type EngineHistoryState = {
|
|
3
|
+
readonly canUndo: boolean;
|
|
4
|
+
readonly canRedo: boolean;
|
|
5
|
+
};
|
|
6
|
+
/** 0.18.1 exposes clear, but not the history reader/emitter, on its imperative API. */
|
|
7
|
+
export declare function observeEngineHistory(api: ExcalidrawImperativeAPI, canvas: HTMLElement, onChange: (state: EngineHistoryState) => void): () => void;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export { WhiteboardCanvas, preloadWhiteboardStylesheet } from "./WhiteboardCanvas.js";
|
|
2
|
-
export type { WhiteboardCanvasClassNames, WhiteboardCanvasIcons, WhiteboardCanvasProps, WhiteboardCollaborationOptions } from "./WhiteboardCanvas.js";
|
|
2
|
+
export type { WhiteboardCanvasClassNames, WhiteboardCanvasIcons, WhiteboardCanvasProps, WhiteboardCollaborationOptions, WhiteboardEngineHandle } from "./WhiteboardCanvas.js";
|
|
3
3
|
export { appendOrReplaceMathElement, CHALK_MATH_CUSTOM_DATA_KEY, CHALK_MATH_RENDERER, createMathFileId, createMathImageAsset, getChalkMathData, getInsertionPoint, getSelectedMathElement, isChalkMathElement, svgToDataUrl } from "./math-elements.js";
|
|
4
4
|
export type { ChalkMathCustomData, MathImageAsset } from "./math-elements.js";
|
|
5
5
|
export { getRenderedSvgSize, renderLatexToSvg } from "./mathjax-renderer.js";
|
|
6
6
|
export type { RenderedMathSvg } from "./mathjax-renderer.js";
|
|
7
7
|
export type { AppState, BinaryFileData, BinaryFiles, Collaborator, CollaboratorPointer, ExcalidrawElement, ExcalidrawImperativeAPI, OrderedExcalidrawElement } from "../collab/index.js";
|
|
8
|
+
export { CANONICAL_INK } from "../collab/ink.js";
|
|
9
|
+
export { observeEngineHistory, type EngineHistoryState } from "./engine-history.js";
|
package/dist/react/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CANONICAL_INK
|
|
3
|
+
} from "../chunk-IVFWOYSW.js";
|
|
4
|
+
|
|
1
5
|
// src/react/WhiteboardCanvas.tsx
|
|
2
6
|
import { memo, useCallback, useEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
3
7
|
|
|
@@ -282,6 +286,7 @@ function useWhiteboardCanvasConfiguration(props) {
|
|
|
282
286
|
const rootClassName = [classNames.root, valueOr(props.className, "")].filter(Boolean).join(" ");
|
|
283
287
|
return {
|
|
284
288
|
canDraw,
|
|
289
|
+
headless: props.headless === true,
|
|
285
290
|
classNames,
|
|
286
291
|
excalidrawCssPath: valueOr(props.excalidrawCssPath, DEFAULT_EXCALIDRAW_CSS),
|
|
287
292
|
icons: valueOr(props.icons, {}),
|
|
@@ -355,67 +360,71 @@ function useExcalidrawModule(onLoadError, setLoadError) {
|
|
|
355
360
|
}, [onLoadError, setLoadError]);
|
|
356
361
|
return excalidraw;
|
|
357
362
|
}
|
|
358
|
-
|
|
363
|
+
var TRANSPARENT = "transparent";
|
|
364
|
+
function useWhiteboardInitialData(theme, headless, localParticipantColor, inkColor) {
|
|
365
|
+
const initialInk = useRef(inkColor);
|
|
359
366
|
return useMemo(
|
|
360
367
|
() => ({
|
|
361
368
|
appState: {
|
|
362
|
-
viewBackgroundColor: theme === "dark" ? "#000000" : "#ffffff",
|
|
363
|
-
currentItemStrokeColor: localParticipantColor ?? "#4CB9FF",
|
|
369
|
+
viewBackgroundColor: headless ? TRANSPARENT : theme === "dark" ? "#000000" : "#ffffff",
|
|
370
|
+
currentItemStrokeColor: (headless ? initialInk.current : localParticipantColor) ?? "#4CB9FF",
|
|
364
371
|
theme
|
|
365
372
|
}
|
|
366
373
|
}),
|
|
367
|
-
[localParticipantColor, theme]
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
function useWhiteboardUiOptions() {
|
|
371
|
-
return useMemo(
|
|
372
|
-
() => ({
|
|
373
|
-
canvasActions: {
|
|
374
|
-
changeViewBackgroundColor: false,
|
|
375
|
-
clearCanvas: false,
|
|
376
|
-
export: false,
|
|
377
|
-
loadScene: false,
|
|
378
|
-
saveAsImage: false,
|
|
379
|
-
saveToActiveFile: false,
|
|
380
|
-
toggleTheme: false
|
|
381
|
-
},
|
|
382
|
-
tools: { image: false },
|
|
383
|
-
welcomeScreen: false
|
|
384
|
-
}),
|
|
385
|
-
[]
|
|
374
|
+
[headless, localParticipantColor, theme]
|
|
386
375
|
);
|
|
387
376
|
}
|
|
377
|
+
var UI_OPTIONS = {
|
|
378
|
+
canvasActions: {
|
|
379
|
+
changeViewBackgroundColor: false,
|
|
380
|
+
clearCanvas: false,
|
|
381
|
+
export: false,
|
|
382
|
+
loadScene: false,
|
|
383
|
+
saveAsImage: false,
|
|
384
|
+
saveToActiveFile: false,
|
|
385
|
+
toggleTheme: false
|
|
386
|
+
},
|
|
387
|
+
tools: { image: false },
|
|
388
|
+
welcomeScreen: false
|
|
389
|
+
};
|
|
388
390
|
function useWhiteboardCollaboration(options) {
|
|
389
391
|
const apiRef = useRef(null);
|
|
390
|
-
const refs = useCollaborationRefs(options.collab, options.canDraw);
|
|
391
|
-
const
|
|
392
|
+
const refs = useCollaborationRefs(options.collab, options.canDraw, options.inkColor);
|
|
393
|
+
const [engineReady, setEngineReady] = useState2(false);
|
|
394
|
+
const initializeEngine = useCollaborationEngineLoader(refs, apiRef, options.headless, options.onLoadError, options.setLoadError, setEngineReady);
|
|
392
395
|
const handleApiReady = useExcalidrawApiReady(apiRef, initializeEngine, options.onExcalidrawApiReady);
|
|
393
396
|
const handlers = useExcalidrawSceneHandlers(apiRef, refs.engineRef, options.onSelectedLatexChange);
|
|
394
|
-
|
|
397
|
+
useCollaborationPermissions(refs.engineRef, options.canDraw, options.collab?.canManage);
|
|
398
|
+
useCollaborationInk(refs.engineRef, options.inkColor);
|
|
395
399
|
useCollaborationDisposal(refs.engineRef, refs.isMountedRef);
|
|
396
400
|
useCollaborationInitialization(options.collab, apiRef, refs.engineRef, initializeEngine);
|
|
397
|
-
return { apiRef, handleApiReady, ...handlers };
|
|
401
|
+
return { ready: !options.collab || engineReady, apiRef, engineRef: refs.engineRef, handleApiReady, ...handlers };
|
|
398
402
|
}
|
|
399
|
-
function useCollaborationRefs(collab, canDraw) {
|
|
403
|
+
function useCollaborationRefs(collab, canDraw, inkColor) {
|
|
400
404
|
const canDrawRef = useRef(canDraw);
|
|
405
|
+
const inkColorRef = useRef(inkColor);
|
|
401
406
|
const collabOptionsRef = useRef(collab);
|
|
402
407
|
const enginePromiseRef = useRef(null);
|
|
403
408
|
const engineRef = useRef(null);
|
|
404
409
|
const isMountedRef = useRef(true);
|
|
405
410
|
canDrawRef.current = canDraw;
|
|
411
|
+
inkColorRef.current = inkColor;
|
|
406
412
|
collabOptionsRef.current = collab;
|
|
407
|
-
return useMemo(() => ({ canDrawRef, collabOptionsRef, enginePromiseRef, engineRef, isMountedRef }), [canDrawRef, collabOptionsRef, enginePromiseRef, engineRef, isMountedRef]);
|
|
413
|
+
return useMemo(() => ({ canDrawRef, inkColorRef, collabOptionsRef, enginePromiseRef, engineRef, isMountedRef }), [canDrawRef, inkColorRef, collabOptionsRef, enginePromiseRef, engineRef, isMountedRef]);
|
|
408
414
|
}
|
|
409
|
-
function useCollaborationEngineLoader(refs, apiRef, onLoadError, setLoadError) {
|
|
415
|
+
function useCollaborationEngineLoader(refs, apiRef, headless, onLoadError, setLoadError, setEngineReady) {
|
|
410
416
|
return useCallback(() => {
|
|
411
417
|
if (!refs.collabOptionsRef.current || refs.engineRef.current || refs.enginePromiseRef.current) return;
|
|
412
|
-
refs.enginePromiseRef.current = import("../engine-
|
|
418
|
+
refs.enginePromiseRef.current = import("../engine-MERF7RL3.js").then(({ ExcalidrawCollabEngine }) => {
|
|
413
419
|
const api = apiRef.current;
|
|
414
420
|
const collab = refs.collabOptionsRef.current;
|
|
415
421
|
if (!refs.isMountedRef.current || !api || !collab) return;
|
|
416
422
|
refs.engineRef.current = new ExcalidrawCollabEngine({
|
|
417
423
|
excalidrawAPI: api,
|
|
418
424
|
canDraw: refs.canDrawRef.current,
|
|
425
|
+
canManage: collab.canManage,
|
|
426
|
+
renderCursors: !headless,
|
|
427
|
+
...refs.inkColorRef.current ? { inkColor: refs.inkColorRef.current } : {},
|
|
419
428
|
submitUpdate: collab.submitUpdate,
|
|
420
429
|
sendCursor: collab.sendCursor,
|
|
421
430
|
requestSnapshot: collab.requestSnapshot,
|
|
@@ -426,6 +435,7 @@ function useCollaborationEngineLoader(refs, apiRef, onLoadError, setLoadError) {
|
|
|
426
435
|
onFileSyncStateChange: collab.onFileSyncStateChange,
|
|
427
436
|
onSubmissionError: collab.onSubmissionError
|
|
428
437
|
});
|
|
438
|
+
setEngineReady(true);
|
|
429
439
|
}).catch((reason) => {
|
|
430
440
|
if (!refs.isMountedRef.current) return;
|
|
431
441
|
const error = reason instanceof Error ? reason : new Error("Failed to load whiteboard collaboration");
|
|
@@ -434,7 +444,7 @@ function useCollaborationEngineLoader(refs, apiRef, onLoadError, setLoadError) {
|
|
|
434
444
|
}).finally(() => {
|
|
435
445
|
refs.enginePromiseRef.current = null;
|
|
436
446
|
});
|
|
437
|
-
}, [apiRef, onLoadError, refs, setLoadError]);
|
|
447
|
+
}, [apiRef, headless, onLoadError, refs, setLoadError, setEngineReady]);
|
|
438
448
|
}
|
|
439
449
|
function useExcalidrawApiReady(apiRef, initializeEngine, onExcalidrawApiReady) {
|
|
440
450
|
const didReportApiRef = useRef(false);
|
|
@@ -478,10 +488,16 @@ function selectedMathLatex(api) {
|
|
|
478
488
|
const math = getChalkMathData(selected);
|
|
479
489
|
return math ? math.latex : null;
|
|
480
490
|
}
|
|
481
|
-
function
|
|
491
|
+
function useCollaborationPermissions(engineRef, canDraw, canManage) {
|
|
482
492
|
useEffect(() => {
|
|
483
493
|
engineRef.current?.setCanDraw(canDraw);
|
|
484
|
-
|
|
494
|
+
engineRef.current?.setCanManage(canManage);
|
|
495
|
+
}, [canDraw, canManage, engineRef]);
|
|
496
|
+
}
|
|
497
|
+
function useCollaborationInk(engineRef, inkColor) {
|
|
498
|
+
useEffect(() => {
|
|
499
|
+
if (inkColor) engineRef.current?.setInkColor(inkColor);
|
|
500
|
+
}, [engineRef, inkColor]);
|
|
485
501
|
}
|
|
486
502
|
function useCollaborationDisposal(engineRef, isMountedRef) {
|
|
487
503
|
useEffect(() => {
|
|
@@ -537,6 +553,8 @@ function useWhiteboardCanvasState(props) {
|
|
|
537
553
|
const stylesheet = useExternalStylesheet(configuration.excalidrawCssPath);
|
|
538
554
|
const collaboration = useWhiteboardCollaboration({
|
|
539
555
|
canDraw: configuration.canDraw,
|
|
556
|
+
headless: configuration.headless,
|
|
557
|
+
inkColor: props.inkColor,
|
|
540
558
|
collab: props.collab,
|
|
541
559
|
onExcalidrawApiReady: props.onExcalidrawApiReady,
|
|
542
560
|
onLoadError: props.onLoadError,
|
|
@@ -552,39 +570,87 @@ function useWhiteboardCanvasState(props) {
|
|
|
552
570
|
setSelectedLatex,
|
|
553
571
|
syncScene: collaboration.syncScene
|
|
554
572
|
});
|
|
555
|
-
const initialData = useWhiteboardInitialData(configuration.theme, props.localParticipantColor);
|
|
556
|
-
const uiOptions = useWhiteboardUiOptions();
|
|
573
|
+
const initialData = useWhiteboardInitialData(configuration.theme, configuration.headless, props.localParticipantColor, props.inkColor);
|
|
557
574
|
const resolvedLoadError = loadError ?? stylesheet.error;
|
|
575
|
+
const isReady = Boolean(excalidraw && stylesheet.loaded && !resolvedLoadError);
|
|
576
|
+
const engine = useEngineHandle(props.onEngineReady, excalidraw, collaboration, math.handleSubmitMath, configuration.headless, isReady);
|
|
558
577
|
return {
|
|
559
578
|
collaboration,
|
|
560
579
|
configuration,
|
|
561
580
|
excalidraw,
|
|
562
581
|
initialData,
|
|
563
|
-
isReady
|
|
582
|
+
isReady,
|
|
564
583
|
math,
|
|
565
584
|
resolvedLoadError,
|
|
566
|
-
uiOptions
|
|
585
|
+
uiOptions: UI_OPTIONS,
|
|
586
|
+
...engine
|
|
567
587
|
};
|
|
568
588
|
}
|
|
589
|
+
function useEngineHandle(onEngineReady, excalidraw, collaboration, placeMath, headless, isReady) {
|
|
590
|
+
const [api, setApi] = useState2(null);
|
|
591
|
+
const [loaded, setLoaded] = useState2(false);
|
|
592
|
+
const { handleApiReady, handleChange } = collaboration;
|
|
593
|
+
const onApiReady = useCallback(
|
|
594
|
+
(next) => {
|
|
595
|
+
handleApiReady(next);
|
|
596
|
+
setApi(next);
|
|
597
|
+
},
|
|
598
|
+
[handleApiReady]
|
|
599
|
+
);
|
|
600
|
+
const onChange = useCallback(
|
|
601
|
+
(elements, appState, files) => {
|
|
602
|
+
handleChange(elements, appState, files);
|
|
603
|
+
if (!appState.isLoading) setLoaded(true);
|
|
604
|
+
if (headless && appState.viewBackgroundColor !== TRANSPARENT) api?.updateScene({ appState: { viewBackgroundColor: TRANSPARENT } });
|
|
605
|
+
},
|
|
606
|
+
[api, handleChange, headless]
|
|
607
|
+
);
|
|
608
|
+
const placeMathRef = useRef(placeMath);
|
|
609
|
+
placeMathRef.current = placeMath;
|
|
610
|
+
const liveApi = useRef(api);
|
|
611
|
+
useEffect(() => {
|
|
612
|
+
liveApi.current = api;
|
|
613
|
+
return () => {
|
|
614
|
+
liveApi.current = null;
|
|
615
|
+
};
|
|
616
|
+
}, [api]);
|
|
617
|
+
const { engineRef } = collaboration;
|
|
618
|
+
useEffect(() => {
|
|
619
|
+
if (!api || !excalidraw || !loaded || !isReady || !collaboration.ready || !onEngineReady) return;
|
|
620
|
+
onEngineReady({
|
|
621
|
+
api,
|
|
622
|
+
excalidraw,
|
|
623
|
+
clear: async () => {
|
|
624
|
+
const engine = engineRef.current;
|
|
625
|
+
if (engine) await engine.clear();
|
|
626
|
+
else {
|
|
627
|
+
api.updateScene({ elements: [], captureUpdate: excalidraw.CaptureUpdateAction.NEVER });
|
|
628
|
+
api.history.clear();
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
clearWithUndo: async () => {
|
|
632
|
+
const engine = engineRef.current;
|
|
633
|
+
if (engine) return engine.clearWithUndo();
|
|
634
|
+
const before = api.getSceneElements();
|
|
635
|
+
api.updateScene({ elements: [], captureUpdate: excalidraw.CaptureUpdateAction.NEVER });
|
|
636
|
+
api.history.clear();
|
|
637
|
+
return () => {
|
|
638
|
+
if (liveApi.current !== api || api.getSceneElementsIncludingDeleted().length > 0) return false;
|
|
639
|
+
api.updateScene({ elements: before.map((element) => excalidraw.newElementWith(element, {})), captureUpdate: excalidraw.CaptureUpdateAction.IMMEDIATELY });
|
|
640
|
+
return true;
|
|
641
|
+
};
|
|
642
|
+
},
|
|
643
|
+
placeMath: (latex) => placeMathRef.current(latex)
|
|
644
|
+
});
|
|
645
|
+
}, [api, engineRef, excalidraw, isReady, loaded, collaboration.ready, onEngineReady]);
|
|
646
|
+
return { onApiReady, onChange };
|
|
647
|
+
}
|
|
569
648
|
function WhiteboardCanvasView({ state }) {
|
|
570
|
-
const {
|
|
649
|
+
const { configuration, excalidraw, initialData, isReady, math, onApiReady, onChange, collaboration, resolvedLoadError, uiOptions } = state;
|
|
571
650
|
return /* @__PURE__ */ jsxs2("div", { className: configuration.rootClassName, hidden: !configuration.isVisible, children: [
|
|
572
|
-
/* @__PURE__ */ jsx2(WhiteboardMathToolbar, { canDraw: configuration.canDraw && !configuration.isCollaborating, classNames: configuration.classNames, isReady, onOpenMath: math.openMath, selectedLatex: math.selectedLatex }),
|
|
573
|
-
/* @__PURE__ */ jsx2(WhiteboardCanvasStatus, { classNames: configuration.classNames, error: resolvedLoadError, icons: configuration.icons, isReady }),
|
|
574
|
-
excalidraw && /* @__PURE__ */ jsx2(
|
|
575
|
-
WhiteboardScene,
|
|
576
|
-
{
|
|
577
|
-
canDraw: configuration.canDraw,
|
|
578
|
-
collab: configuration.isCollaborating,
|
|
579
|
-
excalidraw,
|
|
580
|
-
initialData,
|
|
581
|
-
onApiReady: collaboration.handleApiReady,
|
|
582
|
-
onChange: collaboration.handleChange,
|
|
583
|
-
onPointerUpdate: collaboration.handlePointerUpdate,
|
|
584
|
-
theme: configuration.theme,
|
|
585
|
-
uiOptions
|
|
586
|
-
}
|
|
587
|
-
),
|
|
651
|
+
/* @__PURE__ */ jsx2(WhiteboardMathToolbar, { canDraw: configuration.canDraw && !configuration.isCollaborating && !configuration.headless, classNames: configuration.classNames, isReady, onOpenMath: math.openMath, selectedLatex: math.selectedLatex }),
|
|
652
|
+
/* @__PURE__ */ jsx2(WhiteboardCanvasStatus, { classNames: configuration.classNames, error: resolvedLoadError, icons: configuration.icons, isReady: isReady || configuration.headless }),
|
|
653
|
+
excalidraw && /* @__PURE__ */ jsx2(WhiteboardScene, { canDraw: configuration.canDraw, collab: configuration.isCollaborating, excalidraw, initialData, onApiReady, onChange, onPointerUpdate: collaboration.handlePointerUpdate, theme: configuration.theme, uiOptions }),
|
|
588
654
|
math.isMathOpen && /* @__PURE__ */ jsx2(MathEditor, { initialLatex: math.initialLatex, isEditing: Boolean(math.selectedLatex), classNames: configuration.classNames, icons: configuration.icons, onClose: math.closeMath, onSubmit: math.handleSubmitMath })
|
|
589
655
|
] });
|
|
590
656
|
}
|
|
@@ -605,7 +671,7 @@ function WhiteboardLoading({ classNames, loadingIcon }) {
|
|
|
605
671
|
] }) });
|
|
606
672
|
}
|
|
607
673
|
function WhiteboardLoadError({ className, error }) {
|
|
608
|
-
return /* @__PURE__ */ jsx2("div", { className, style: { position: "absolute", inset: 0, zIndex: 30, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx2("div", { children: error }) });
|
|
674
|
+
return /* @__PURE__ */ jsx2("div", { role: "alert", className, style: { position: "absolute", inset: 0, zIndex: 30, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx2("div", { children: error }) });
|
|
609
675
|
}
|
|
610
676
|
function WhiteboardScene({
|
|
611
677
|
canDraw,
|
|
@@ -625,7 +691,49 @@ function WhiteboardCanvasBase(props) {
|
|
|
625
691
|
}
|
|
626
692
|
var WhiteboardCanvas = memo(WhiteboardCanvasBase);
|
|
627
693
|
WhiteboardCanvas.displayName = "WhiteboardCanvas";
|
|
694
|
+
|
|
695
|
+
// src/react/engine-history.ts
|
|
696
|
+
function observeEngineHistory(api, canvas, onChange) {
|
|
697
|
+
let connected = false;
|
|
698
|
+
let dispose = () => {
|
|
699
|
+
};
|
|
700
|
+
api.registerAction({
|
|
701
|
+
name: "stats",
|
|
702
|
+
label: "Stats",
|
|
703
|
+
trackEvent: false,
|
|
704
|
+
perform: () => false,
|
|
705
|
+
keyTest: (_event, _state, _elements, app) => {
|
|
706
|
+
if (connected) return false;
|
|
707
|
+
connected = true;
|
|
708
|
+
const history = app.history;
|
|
709
|
+
const read = () => ({ isUndoStackEmpty: history.isUndoStackEmpty, isRedoStackEmpty: history.isRedoStackEmpty });
|
|
710
|
+
const originalClear = history.clear;
|
|
711
|
+
const clear = () => {
|
|
712
|
+
originalClear.call(history);
|
|
713
|
+
history.onHistoryChangedEmitter.trigger(read());
|
|
714
|
+
};
|
|
715
|
+
history.clear = clear;
|
|
716
|
+
const update = () => {
|
|
717
|
+
const state = read();
|
|
718
|
+
onChange({ canUndo: !state.isUndoStackEmpty, canRedo: !state.isRedoStackEmpty });
|
|
719
|
+
};
|
|
720
|
+
const off = history.onHistoryChangedEmitter.on(update);
|
|
721
|
+
update();
|
|
722
|
+
dispose = () => {
|
|
723
|
+
off();
|
|
724
|
+
history.clear = originalClear;
|
|
725
|
+
};
|
|
726
|
+
return false;
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
canvas.dispatchEvent(new KeyboardEvent("keydown", { key: "Shift", bubbles: true }));
|
|
730
|
+
return () => {
|
|
731
|
+
connected = true;
|
|
732
|
+
dispose();
|
|
733
|
+
};
|
|
734
|
+
}
|
|
628
735
|
export {
|
|
736
|
+
CANONICAL_INK,
|
|
629
737
|
CHALK_MATH_CUSTOM_DATA_KEY,
|
|
630
738
|
CHALK_MATH_RENDERER,
|
|
631
739
|
WhiteboardCanvas,
|
|
@@ -637,6 +745,7 @@ export {
|
|
|
637
745
|
getRenderedSvgSize,
|
|
638
746
|
getSelectedMathElement,
|
|
639
747
|
isChalkMathElement,
|
|
748
|
+
observeEngineHistory,
|
|
640
749
|
preloadWhiteboardStylesheet,
|
|
641
750
|
renderLatexToSvg,
|
|
642
751
|
svgToDataUrl
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@q9labsai/chalk-whiteboard",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"directory": "packages/whiteboard"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"dist"
|
|
11
|
+
"dist",
|
|
12
|
+
"CHANGELOG.md"
|
|
12
13
|
],
|
|
13
14
|
"type": "module",
|
|
14
15
|
"sideEffects": false,
|
|
@@ -70,7 +71,7 @@
|
|
|
70
71
|
"@types/react": "^19.0.0",
|
|
71
72
|
"@types/react-dom": "^19.2.3",
|
|
72
73
|
"esbuild": "0.28.2",
|
|
73
|
-
"happy-dom": "^
|
|
74
|
+
"happy-dom": "^21.0.4",
|
|
74
75
|
"react": "19.2.7",
|
|
75
76
|
"react-dom": "19.2.7",
|
|
76
77
|
"typescript": "^5.7.2"
|