@q9labsai/chalk-whiteboard 5.2.0 → 5.2.2

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 (39) hide show
  1. package/dist/assets.d.ts +13 -0
  2. package/dist/assets.js +10 -0
  3. package/dist/chunk-BN4VA2BS.js +20 -0
  4. package/dist/collab/view-limits.d.ts +1 -0
  5. package/dist/collab/view.d.ts +26 -0
  6. package/dist/embedded/chalk-whiteboard/Figtree-Regular.ttf +0 -0
  7. package/dist/embedded/chalk-whiteboard/Figtree-SemiBold.ttf +0 -0
  8. package/dist/embedded/chalk-whiteboard/asset-manifest.json +19 -4
  9. package/dist/embedded/chalk-whiteboard/chalk.css +44 -0
  10. package/dist/embedded/chalk-whiteboard/licenses/Figtree.txt +93 -0
  11. package/dist/embedded/chalk-whiteboard/renderer.js +1669 -1418
  12. package/dist/embedded/controller.d.ts +16 -14
  13. package/dist/embedded/index.d.ts +1 -1
  14. package/dist/embedded/index.js +127 -8
  15. package/dist/embedded/protocol.d.ts +86 -0
  16. package/dist/embedded/renderer-assets.d.ts +1 -0
  17. package/dist/embedded/renderer-bridge.d.ts +10 -0
  18. package/dist/embedded/renderer-entry.d.ts +2 -0
  19. package/dist/embedded/renderer.d.ts +0 -5
  20. package/dist/react/board/Board.d.ts +41 -0
  21. package/dist/react/board/BoardCursors.d.ts +23 -0
  22. package/dist/react/board/BoardIcon.d.ts +46 -0
  23. package/dist/react/board/BoardIsland.d.ts +22 -0
  24. package/dist/react/board/BoardMenu.d.ts +20 -0
  25. package/dist/react/board/BoardStyles.d.ts +2 -0
  26. package/dist/react/board/board-draw-policy.d.ts +10 -0
  27. package/dist/react/board/board-engine.d.ts +30 -0
  28. package/dist/react/board/board-history.d.ts +7 -0
  29. package/dist/react/board/board-model.d.ts +91 -0
  30. package/dist/react/board/board-toasts.d.ts +15 -0
  31. package/dist/react/board/board-view-versions.d.ts +1 -0
  32. package/dist/react/board/controls/ChalkMenu.d.ts +22 -0
  33. package/dist/react/board/controls/ChalkPopover.d.ts +13 -0
  34. package/dist/react/board/controls/ChalkTooltip.d.ts +5 -0
  35. package/dist/react/board/controls/index.d.ts +3 -0
  36. package/dist/react/board/useMediaQuery.d.ts +6 -0
  37. package/dist/react/index.d.ts +3 -0
  38. package/dist/react/index.js +1811 -18
  39. package/package.json +19 -5
@@ -1,3 +1,5 @@
1
+ import type { WhiteboardView } from "../collab/view.js";
2
+ import type { ChalkEmbeddedBoardContext, ChalkEmbeddedBoardNotice } from "./protocol.js";
1
3
  import type { WhiteboardCommit, WhiteboardJsonValue } from "../collab/wire.js";
2
4
  import { type ChalkEmbeddedWhiteboardTheme, type ChalkEmbeddedWhiteboardViewport } from "./protocol.js";
3
5
  export type ChalkEmbeddedWhiteboardTransportElement = {
@@ -33,23 +35,12 @@ export type ChalkEmbeddedWhiteboardTransportEvent = {
33
35
  readonly type: "reset_required";
34
36
  readonly sceneId: string;
35
37
  readonly reason: "scene_changed" | "cursor_expired" | "gap";
36
- } | ({
38
+ } | (WhiteboardView & {
37
39
  readonly type: "view";
38
40
  readonly participantId: string;
39
41
  readonly displayName: string;
40
- readonly x: number;
41
- readonly y: number;
42
- readonly width: number;
43
- readonly height: number;
44
- readonly bring: boolean;
45
42
  readonly occurredAt: string;
46
- } & ({
47
- readonly originParticipantId: string;
48
- readonly originSequence: number;
49
- } | {
50
- readonly originParticipantId?: never;
51
- readonly originSequence?: never;
52
- }));
43
+ });
53
44
  export interface ChalkEmbeddedWhiteboardFileTransport {
54
45
  readonly getDownloadUrl: (fileId: string) => Promise<{
55
46
  readonly downloadUrl: string;
@@ -70,6 +61,9 @@ export interface ChalkEmbeddedWhiteboardTransport {
70
61
  }) => void;
71
62
  readonly requestSnapshot: () => Promise<void>;
72
63
  readonly clear: () => Promise<WhiteboardCommit>;
64
+ readonly sendView?: (view: WhiteboardView) => boolean;
65
+ readonly setDrawPolicy?: (policy: "everyone" | "hosts" | "picked") => Promise<void>;
66
+ readonly setDrawPermission?: (participantId: string, canDraw: boolean) => Promise<void>;
73
67
  readonly files: ChalkEmbeddedWhiteboardFileTransport;
74
68
  }
75
69
  export interface ChalkEmbeddedWhiteboardRendererPort {
@@ -107,7 +101,13 @@ export interface ChalkEmbeddedWhiteboardControllerOptions {
107
101
  readonly format: "png" | "svg";
108
102
  readonly mimeType: string;
109
103
  readonly dataURL: string;
110
- }) => void;
104
+ readonly action?: "download" | "copy";
105
+ }) => void | Promise<void>;
106
+ readonly manageSceneSubscription?: boolean;
107
+ readonly board?: ChalkEmbeddedBoardContext;
108
+ readonly onNotice?: (notice: ChalkEmbeddedBoardNotice | null) => void;
109
+ readonly onFollow?: (participantId: string | null) => void;
110
+ readonly onCloseBoard?: () => void;
111
111
  readonly onCloseReady?: (reason: string) => void;
112
112
  }
113
113
  export declare class ChalkWhiteboardController {
@@ -120,6 +120,8 @@ export declare class ChalkWhiteboardController {
120
120
  readonly canDraw: boolean;
121
121
  readonly canClear: boolean;
122
122
  }): void;
123
+ performNoticeAction(id: string): void;
124
+ setBoard(board: ChalkEmbeddedBoardContext): void;
123
125
  setViewport(viewport: ChalkEmbeddedWhiteboardViewport): void;
124
126
  requestUserExport(requestId: string, format: "png" | "svg"): void;
125
127
  prepareClose(reason: string): void;
@@ -2,4 +2,4 @@ export { ChalkWhiteboardController } from "./controller.js";
2
2
  export type { ChalkEmbeddedWhiteboardControllerOptions, ChalkEmbeddedWhiteboardFileTransport, ChalkEmbeddedWhiteboardRendererPort, ChalkEmbeddedWhiteboardTransport, ChalkEmbeddedWhiteboardTransportEvent } from "./controller.js";
3
3
  export { chalkEmbeddedWhiteboardManifest } from "./manifest.js";
4
4
  export { CHALK_EMBEDDED_WHITEBOARD_BRIDGE_VERSION, CHALK_EMBEDDED_WHITEBOARD_EXCALIDRAW_VERSION, CHALK_EMBEDDED_WHITEBOARD_MAX_MESSAGE_BYTES, CHALK_EMBEDDED_WHITEBOARD_RENDERER_BUILD_ID, decodeEmbeddedWhiteboardHostMessage, decodeEmbeddedWhiteboardRendererMessage, encodeEmbeddedWhiteboardMessage, } from "./protocol.js";
5
- export type { ChalkEmbeddedWhiteboardBridgeEnvelope, ChalkEmbeddedWhiteboardHostEnvelope, ChalkEmbeddedWhiteboardHostMessage, ChalkEmbeddedWhiteboardMessageContext, ChalkEmbeddedWhiteboardOperationResult, ChalkEmbeddedWhiteboardRendererEnvelope, ChalkEmbeddedWhiteboardRendererMessage, ChalkEmbeddedWhiteboardTheme, ChalkEmbeddedWhiteboardViewport, } from "./protocol.js";
5
+ export type { ChalkEmbeddedBoardContext, ChalkEmbeddedBoardNotice, ChalkEmbeddedWhiteboardBridgeEnvelope, ChalkEmbeddedWhiteboardHostEnvelope, ChalkEmbeddedWhiteboardHostMessage, ChalkEmbeddedWhiteboardMessageContext, ChalkEmbeddedWhiteboardOperationResult, ChalkEmbeddedWhiteboardRendererEnvelope, ChalkEmbeddedWhiteboardRendererMessage, ChalkEmbeddedWhiteboardTheme, ChalkEmbeddedWhiteboardViewport, } from "./protocol.js";
@@ -31,8 +31,42 @@ function decodeEmbeddedWhiteboardHostMessage(value, expectedGeneration) {
31
31
  function decodeEmbeddedWhiteboardRendererMessage(value, expectedGeneration) {
32
32
  return decodeEnvelope(value, expectedGeneration, rendererMessageTypes);
33
33
  }
34
- var hostMessageTypes = /* @__PURE__ */ new Set(["initialize", "apply_snapshot", "apply_update", "apply_cursor", "reset_required", "set_capabilities", "set_viewport", "operation_result", "provide_file_bytes", "request_user_export", "prepare_close"]);
35
- var rendererMessageTypes = /* @__PURE__ */ new Set(["ready", "local_update", "cursor", "camera", "request_snapshot", "clear", "file_read", "file_write", "user_export", "metric", "error", "close_ready"]);
34
+ var hostMessageTypes = /* @__PURE__ */ new Set([
35
+ "notice_action",
36
+ "set_board",
37
+ "apply_view",
38
+ "initialize",
39
+ "apply_snapshot",
40
+ "apply_update",
41
+ "apply_cursor",
42
+ "reset_required",
43
+ "set_capabilities",
44
+ "set_viewport",
45
+ "operation_result",
46
+ "provide_file_bytes",
47
+ "request_user_export",
48
+ "prepare_close"
49
+ ]);
50
+ var rendererMessageTypes = /* @__PURE__ */ new Set([
51
+ "set_draw_policy",
52
+ "set_draw_permission",
53
+ "notice",
54
+ "view",
55
+ "follow",
56
+ "close_board",
57
+ "ready",
58
+ "local_update",
59
+ "cursor",
60
+ "camera",
61
+ "request_snapshot",
62
+ "clear",
63
+ "file_read",
64
+ "file_write",
65
+ "user_export",
66
+ "metric",
67
+ "error",
68
+ "close_ready"
69
+ ]);
36
70
  function decodeEnvelope(value, expectedGeneration, allowedTypes) {
37
71
  if (encodedBytes(value) > CHALK_EMBEDDED_WHITEBOARD_MAX_MESSAGE_BYTES) throw new Error("embedded whiteboard message exceeds the bridge limit");
38
72
  let parsed;
@@ -166,6 +200,7 @@ var ChalkWhiteboardController = class {
166
200
  #nextMessageId;
167
201
  #canDraw;
168
202
  #canClear;
203
+ #board;
169
204
  #viewport = null;
170
205
  #rendererGeneration = null;
171
206
  #sceneId = null;
@@ -200,7 +235,7 @@ var ChalkWhiteboardController = class {
200
235
  this.#documentCompatible = true;
201
236
  this.#rendererMessageIds.clear();
202
237
  this.#abortFileTransfers();
203
- if (this.#transportStarted) this.options.transport.stopSceneSubscription();
238
+ if (this.#transportStarted && this.options.manageSceneSubscription !== false) this.options.transport.stopSceneSubscription();
204
239
  this.#transportStarted = false;
205
240
  }
206
241
  setCapabilities(value) {
@@ -208,6 +243,13 @@ var ChalkWhiteboardController = class {
208
243
  this.#canClear = value.canClear;
209
244
  this.#sendEffectiveCapabilities();
210
245
  }
246
+ performNoticeAction(id) {
247
+ this.#send({ type: "notice_action", payload: { id } });
248
+ }
249
+ setBoard(board) {
250
+ this.#board = board;
251
+ this.#send({ type: "set_board", payload: board });
252
+ }
211
253
  setViewport(viewport) {
212
254
  this.#viewport = viewport;
213
255
  this.#send({ type: "set_viewport", payload: viewport });
@@ -233,6 +275,60 @@ var ChalkWhiteboardController = class {
233
275
  case "ready":
234
276
  await this.#ready(message);
235
277
  return;
278
+ case "view": {
279
+ const payload = message.payload;
280
+ const requestId = requireString(payload.requestId, "request ID");
281
+ await this.#operation(requestId, async () => {
282
+ const board = this.#board ?? this.options.board;
283
+ if (!board?.viewsAvailable || !isWhiteboardView(payload, board.viewBounds)) throw new Error("whiteboard view is invalid");
284
+ if (payload.bring && !this.#effectiveCanClear()) throw new Error("whiteboard presentation is not permitted");
285
+ const { requestId: _requestId, ...view } = payload;
286
+ if (!this.options.transport.sendView?.(view)) throw new Error("whiteboard view could not be sent");
287
+ });
288
+ return;
289
+ }
290
+ case "notice": {
291
+ if (message.payload === null) {
292
+ this.options.onNotice?.(null);
293
+ return;
294
+ }
295
+ const notice = requireRecord(message.payload, ["id", "message", "kind"], ["actionLabel"]);
296
+ if (notice.kind !== "info" && notice.kind !== "error") throw new Error("Board notice kind is invalid");
297
+ this.options.onNotice?.({ id: requireString(notice.id, "notice ID"), message: requireString(notice.message, "notice message", 1024), kind: notice.kind, ...typeof notice.actionLabel === "string" ? { actionLabel: requireString(notice.actionLabel, "notice action", 64) } : {} });
298
+ return;
299
+ }
300
+ case "follow": {
301
+ const payload = message.payload;
302
+ if (typeof payload !== "object" || payload === null || !("participantId" in payload) || payload.participantId !== null && typeof payload.participantId !== "string") throw new Error("whiteboard follow target is invalid");
303
+ this.options.onFollow?.(payload.participantId);
304
+ return;
305
+ }
306
+ case "set_draw_policy": {
307
+ const payload = requireRecord(message.payload, ["requestId", "policy"]);
308
+ const requestId = requireString(payload.requestId, "request ID");
309
+ const policy = payload.policy;
310
+ if (policy !== "everyone" && policy !== "hosts" && policy !== "picked") throw new Error("whiteboard draw policy is invalid");
311
+ await this.#operation(requestId, async () => {
312
+ if (!this.#canClear || !this.options.transport.setDrawPolicy) throw new Error("whiteboard policy changes are unavailable");
313
+ await this.options.transport.setDrawPolicy(policy);
314
+ });
315
+ return;
316
+ }
317
+ case "set_draw_permission": {
318
+ const payload = requireRecord(message.payload, ["requestId", "participantId", "canDraw"]);
319
+ const requestId = requireString(payload.requestId, "request ID");
320
+ const participantId = requireString(payload.participantId, "participant ID");
321
+ const canDraw = payload.canDraw;
322
+ if (typeof canDraw !== "boolean") throw new Error("whiteboard draw permission is invalid");
323
+ await this.#operation(requestId, async () => {
324
+ if (!this.#canClear || !this.options.transport.setDrawPermission) throw new Error("whiteboard permission changes are unavailable");
325
+ await this.options.transport.setDrawPermission(participantId, canDraw);
326
+ });
327
+ return;
328
+ }
329
+ case "close_board":
330
+ this.options.onCloseBoard?.();
331
+ return;
236
332
  case "local_update":
237
333
  await this.#localUpdate(message);
238
334
  return;
@@ -273,9 +369,14 @@ var ChalkWhiteboardController = class {
273
369
  case "error":
274
370
  this.options.onError?.(requireRendererError(message.payload));
275
371
  return;
276
- case "user_export":
277
- this.options.onUserExport?.(requireUserExport(message.payload));
372
+ case "user_export": {
373
+ const payload = requireUserExport(message.payload);
374
+ await this.#operation(payload.requestId, async () => {
375
+ if (!this.options.onUserExport) throw new Error("Board export is unavailable");
376
+ await this.options.onUserExport(payload);
377
+ });
278
378
  return;
379
+ }
279
380
  case "close_ready":
280
381
  this.options.onCloseReady?.(requireCloseReady(message.payload).reason);
281
382
  }
@@ -295,11 +396,12 @@ var ChalkWhiteboardController = class {
295
396
  canDraw: this.#effectiveCanDraw(),
296
397
  canClear: this.#effectiveCanClear(),
297
398
  theme: this.options.theme ?? "light",
399
+ ...this.#board ?? this.options.board ? { board: this.#board ?? this.options.board } : {},
298
400
  ...this.options.localParticipantColor ? { localParticipantColor: this.options.localParticipantColor } : {}
299
401
  }
300
402
  });
301
403
  if (this.#viewport) this.#send({ type: "set_viewport", payload: this.#viewport });
302
- if (!this.#transportStarted) {
404
+ if (!this.#transportStarted && this.options.manageSceneSubscription !== false) {
303
405
  await this.options.transport.startSceneSubscription();
304
406
  this.#transportStarted = true;
305
407
  } else {
@@ -415,8 +517,15 @@ var ChalkWhiteboardController = class {
415
517
  case "cursor":
416
518
  this.#send({ type: "apply_cursor", payload: event });
417
519
  return;
418
- case "view":
520
+ case "view": {
521
+ if (event.bring) {
522
+ const board = this.#board ?? this.options.board;
523
+ if (board) this.#board = { ...board, following: event.participantId };
524
+ this.options.onFollow?.(event.participantId);
525
+ }
526
+ this.#send({ type: "apply_view", payload: event });
419
527
  return;
528
+ }
420
529
  case "reset_required":
421
530
  this.#sceneGeneration = null;
422
531
  this.#send({ type: "reset_required", payload: event });
@@ -604,11 +713,12 @@ function requireRendererError(value) {
604
713
  };
605
714
  }
606
715
  function requireUserExport(value) {
607
- const record = requireRecord(value, ["requestId", "format", "mimeType", "dataURL"]);
716
+ const record = requireRecord(value, ["requestId", "format", "mimeType", "dataURL"], ["action"]);
608
717
  if (record.format !== "png" && record.format !== "svg") throw new Error("embedded whiteboard export is invalid");
609
718
  return {
610
719
  requestId: requireString(record.requestId, "request ID"),
611
720
  format: record.format,
721
+ ...record.action === "copy" || record.action === "download" ? { action: record.action } : {},
612
722
  mimeType: normalizeMimeType(requireString(record.mimeType, "MIME type")),
613
723
  dataURL: requireString(record.dataURL, "export data", CHALK_EMBEDDED_WHITEBOARD_MAX_MESSAGE_BYTES - 1024)
614
724
  };
@@ -838,6 +948,15 @@ function recordMessageId(messageIds, messageId) {
838
948
  }
839
949
  return true;
840
950
  }
951
+ function isWhiteboardView(value, bounds) {
952
+ if (typeof value !== "object" || value === null || !("x" in value) || !("y" in value) || !("width" in value) || !("height" in value) || !("bring" in value)) return false;
953
+ if (typeof value.x !== "number" || !Number.isFinite(value.x) || Math.abs(value.x) > bounds.maxCoordinate || typeof value.y !== "number" || !Number.isFinite(value.y) || Math.abs(value.y) > bounds.maxCoordinate) return false;
954
+ if (typeof value.width !== "number" || !Number.isFinite(value.width) || value.width <= 0 || value.width > bounds.maxExtent || typeof value.height !== "number" || !Number.isFinite(value.height) || value.height <= 0 || value.height > bounds.maxExtent || typeof value.bring !== "boolean")
955
+ return false;
956
+ if ("lead" in value && typeof value.lead !== "boolean") return false;
957
+ if (!("originParticipantId" in value) || value.originParticipantId === void 0) return !("originSequence" in value) && !("originEpoch" in value);
958
+ return typeof value.originParticipantId === "string" && value.originParticipantId.length > 0 && value.originParticipantId.length <= 256 && "originSequence" in value && typeof value.originSequence === "number" && Number.isSafeInteger(value.originSequence) && value.originSequence > 0 && (!("originEpoch" in value) || typeof value.originEpoch === "string" && value.originEpoch.length > 0 && value.originEpoch.length <= 128);
959
+ }
841
960
  export {
842
961
  CHALK_EMBEDDED_WHITEBOARD_BRIDGE_VERSION,
843
962
  CHALK_EMBEDDED_WHITEBOARD_EXCALIDRAW_VERSION,
@@ -1,3 +1,5 @@
1
+ import type { WhoCanDraw } from "../react/board/board-draw-policy.js";
2
+ import type { WhiteboardView, WhiteboardViewBounds } from "../collab/view.js";
1
3
  import type { WhiteboardCommit, WhiteboardWireElement } from "../collab/wire.js";
2
4
  export declare const CHALK_EMBEDDED_WHITEBOARD_BRIDGE_VERSION: 1;
3
5
  export declare const CHALK_EMBEDDED_WHITEBOARD_EXCALIDRAW_VERSION: "0.18.1";
@@ -22,6 +24,44 @@ export type ChalkEmbeddedWhiteboardOperationResult = {
22
24
  readonly recoverable: boolean;
23
25
  };
24
26
  };
27
+ export type ChalkEmbeddedBoardNotice = {
28
+ readonly id: string;
29
+ readonly message: string;
30
+ readonly kind: "info" | "error";
31
+ readonly actionLabel?: string;
32
+ };
33
+ export type ChalkEmbeddedBoardContext = {
34
+ readonly primary: boolean;
35
+ readonly phone: boolean;
36
+ readonly landscapeControlsVisible?: boolean;
37
+ readonly following: string | null;
38
+ readonly ownParticipantId: string | null;
39
+ readonly people: readonly {
40
+ readonly id: string;
41
+ readonly displayName: string;
42
+ }[];
43
+ readonly viewBounds: WhiteboardViewBounds;
44
+ readonly sceneId: string | null;
45
+ readonly sceneStatus: string;
46
+ readonly connectionStatus: string;
47
+ readonly viewsAvailable: boolean;
48
+ readonly whoCanDraw?: Pick<WhoCanDraw, "policy" | "people">;
49
+ readonly colors: {
50
+ readonly canvas: string;
51
+ readonly surface: string;
52
+ readonly raised: string;
53
+ readonly popover: string;
54
+ readonly hover: string;
55
+ readonly line: string;
56
+ readonly lineStrong: string;
57
+ readonly text: string;
58
+ readonly textStrong: string;
59
+ readonly textMuted: string;
60
+ readonly textSoft: string;
61
+ readonly dangerText: string;
62
+ readonly focus: string;
63
+ };
64
+ };
25
65
  export type ChalkEmbeddedWhiteboardHostMessage = {
26
66
  readonly type: "initialize";
27
67
  readonly payload: {
@@ -29,6 +69,7 @@ export type ChalkEmbeddedWhiteboardHostMessage = {
29
69
  readonly canClear: boolean;
30
70
  readonly theme: ChalkEmbeddedWhiteboardTheme;
31
71
  readonly localParticipantColor?: string;
72
+ readonly board?: ChalkEmbeddedBoardContext;
32
73
  };
33
74
  } | {
34
75
  readonly type: "apply_snapshot";
@@ -70,6 +111,21 @@ export type ChalkEmbeddedWhiteboardHostMessage = {
70
111
  readonly canDraw: boolean;
71
112
  readonly canClear: boolean;
72
113
  };
114
+ } | {
115
+ readonly type: "notice_action";
116
+ readonly payload: {
117
+ readonly id: string;
118
+ };
119
+ } | {
120
+ readonly type: "set_board";
121
+ readonly payload: ChalkEmbeddedBoardContext;
122
+ } | {
123
+ readonly type: "apply_view";
124
+ readonly payload: WhiteboardView & {
125
+ readonly participantId: string;
126
+ readonly displayName: string;
127
+ readonly occurredAt: string;
128
+ };
73
129
  } | {
74
130
  readonly type: "set_viewport";
75
131
  readonly payload: ChalkEmbeddedWhiteboardViewport;
@@ -117,6 +173,35 @@ export type ChalkEmbeddedWhiteboardRendererMessage = {
117
173
  readonly x: number;
118
174
  readonly y: number;
119
175
  };
176
+ } | {
177
+ readonly type: "view";
178
+ readonly payload: WhiteboardView & {
179
+ readonly requestId: string;
180
+ };
181
+ } | {
182
+ readonly type: "notice";
183
+ readonly payload: ChalkEmbeddedBoardNotice | null;
184
+ } | {
185
+ readonly type: "follow";
186
+ readonly payload: {
187
+ readonly participantId: string | null;
188
+ };
189
+ } | {
190
+ readonly type: "set_draw_policy";
191
+ readonly payload: {
192
+ readonly requestId: string;
193
+ readonly policy: WhoCanDraw["policy"];
194
+ };
195
+ } | {
196
+ readonly type: "set_draw_permission";
197
+ readonly payload: {
198
+ readonly requestId: string;
199
+ readonly participantId: string;
200
+ readonly canDraw: boolean;
201
+ };
202
+ } | {
203
+ readonly type: "close_board";
204
+ readonly payload: {};
120
205
  } | {
121
206
  readonly type: "camera";
122
207
  readonly payload: {
@@ -157,6 +242,7 @@ export type ChalkEmbeddedWhiteboardRendererMessage = {
157
242
  readonly format: "png" | "svg";
158
243
  readonly mimeType: string;
159
244
  readonly dataURL: string;
245
+ readonly action?: "download" | "copy";
160
246
  };
161
247
  } | {
162
248
  readonly type: "metric";
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,4 @@
1
+ import type { WhiteboardView } from "../collab/view.js";
1
2
  import type { WhiteboardCommit } from "../collab/wire.js";
2
3
  import { type ChalkEmbeddedWhiteboardHostEnvelope, type ChalkEmbeddedWhiteboardRendererMessage } from "./protocol.js";
3
4
  declare global {
@@ -20,7 +21,16 @@ export declare class ChalkEmbeddedWhiteboardRendererBridge {
20
21
  readonly syncAll: boolean;
21
22
  readonly elements: readonly import("../collab/wire").WhiteboardWireElement[];
22
23
  }): Promise<WhiteboardCommit>;
24
+ sendView(view: WhiteboardView): Promise<boolean>;
25
+ exportImage(payload: {
26
+ readonly format: "png";
27
+ readonly mimeType: "image/png";
28
+ readonly dataURL: string;
29
+ readonly action: "download" | "copy";
30
+ }): Promise<void>;
23
31
  requestSnapshot(): Promise<void>;
32
+ setDrawPolicy(policy: import("../react/board/board-draw-policy").WhoCanDraw["policy"]): Promise<void>;
33
+ setDrawPermission(participantId: string, canDraw: boolean): Promise<void>;
24
34
  clear(): Promise<WhiteboardCommit>;
25
35
  downloadFile(fileId: string): Promise<{
26
36
  readonly mimeType: string;
@@ -0,0 +1,2 @@
1
+ import "./renderer-assets.js";
2
+ import "./renderer.js";
@@ -1,10 +1,5 @@
1
1
  import type { WhiteboardCollaborationEvent } from "../collab/engine.js";
2
2
  import { ChalkEmbeddedWhiteboardRendererBridge } from "./renderer-bridge.js";
3
- declare global {
4
- interface Window {
5
- EXCALIDRAW_ASSET_PATH?: string;
6
- }
7
- }
8
3
  export declare function createCollaborationBridge(rendererBridge: ChalkEmbeddedWhiteboardRendererBridge): {
9
4
  submitUpdate: (payload: Parameters<ChalkEmbeddedWhiteboardRendererBridge["submitUpdate"]>[0]) => Promise<import("..").WhiteboardCommit>;
10
5
  sendCursor: ({ x, y }: {
@@ -0,0 +1,41 @@
1
+ import type { WhiteboardView as ChalkWhiteboardView } from "../../collab/view.js";
2
+ import type { WhiteboardCanvasProps } from "../WhiteboardCanvas.js";
3
+ import type { WhiteboardViewBounds } from "../../collab/view.js";
4
+ import type { ReactNode } from "react";
5
+ import { type CallTheme } from "./board-model.js";
6
+ import type { WhoCanDraw } from "./board-draw-policy.js";
7
+ export type BoardProps = {
8
+ readonly board: Omit<WhiteboardCanvasProps, "classNames" | "icons">;
9
+ readonly isHost: boolean;
10
+ readonly connectionStatus: string;
11
+ readonly sceneId: string | null;
12
+ readonly sceneStatus: string;
13
+ readonly ownParticipantId: string | null;
14
+ readonly viewBounds: WhiteboardViewBounds;
15
+ readonly participantIds?: readonly string[];
16
+ readonly whoCanDraw?: WhoCanDraw;
17
+ readonly renderCursorAvatar?: (name: string) => ReactNode;
18
+ readonly onExport?: (image: Blob, action: "download" | "copy") => Promise<void>;
19
+ /** False for the Grid tile: the live drawing with no controls. */
20
+ readonly primary: boolean;
21
+ readonly visible: boolean;
22
+ readonly phone?: boolean;
23
+ readonly landscapeControlsVisible?: boolean;
24
+ readonly presentation?: "web" | "mobile";
25
+ readonly colorScheme: CallTheme;
26
+ readonly people: readonly {
27
+ readonly id: string;
28
+ readonly displayName: string;
29
+ }[];
30
+ readonly following: string | null;
31
+ readonly onFollow: (participantId: string | null) => void;
32
+ /** Absent, or answering false, when the board service does not share views. */
33
+ readonly sendView?: (view: ChalkWhiteboardView) => boolean | Promise<boolean>;
34
+ readonly downloadLabel?: string;
35
+ readonly onClose?: () => void;
36
+ };
37
+ /**
38
+ * Chalk's board: Excalidraw draws, and every control around it is the call's own. Each person picks their own
39
+ * surface and island place; the drawing, the cursors and the views are shared.
40
+ */
41
+ export declare function Board({ board, primary, visible, colorScheme, people, following, onFollow, sendView, onClose, isHost, connectionStatus, sceneId, sceneStatus, ownParticipantId, participantIds, whoCanDraw, viewBounds, renderCursorAvatar, onExport, phone: phoneOverride, downloadLabel, presentation, landscapeControlsVisible, }: BoardProps): import("react").JSX.Element;
@@ -0,0 +1,23 @@
1
+ import { type MutableRefObject } from "react";
2
+ import type { ReactNode } from "react";
3
+ import { type Camera } from "./board-model.js";
4
+ export type RemoteCursor = {
5
+ readonly participantId: string;
6
+ readonly displayName: string;
7
+ readonly x: number;
8
+ readonly y: number;
9
+ readonly occurredAt: string;
10
+ };
11
+ /** Pruning belongs to receipt too: grid boards do not mount the cursor layer. */
12
+ export declare function rememberCursor(cursors: Map<string, RemoteCursor>, cursor: RemoteCursor): void;
13
+ /**
14
+ * Remote people's cursors over the board: a rounded arrow, the Facehash and the name, in the person's colour.
15
+ * Updates arrive about 15 times a second and unevenly, so each shown cursor is a spring chasing the newest one.
16
+ */
17
+ export declare function BoardCursors({ cursorsRef, cameraRef, colorFor, hidden, renderAvatar, }: {
18
+ readonly cursorsRef: MutableRefObject<Map<string, RemoteCursor>>;
19
+ readonly cameraRef: MutableRefObject<Camera>;
20
+ readonly colorFor: (participantId: string) => string;
21
+ readonly hidden: boolean;
22
+ readonly renderAvatar?: (name: string) => ReactNode;
23
+ }): import("react").JSX.Element;
@@ -0,0 +1,46 @@
1
+ declare const GLYPHS: {
2
+ readonly select: import("react").JSX.Element;
3
+ readonly hand: import("react").JSX.Element;
4
+ readonly pen: import("react").JSX.Element;
5
+ readonly highlighter: import("react").JSX.Element;
6
+ readonly rectangle: import("react").JSX.Element;
7
+ readonly ellipse: import("react").JSX.Element;
8
+ readonly diamond: import("react").JSX.Element;
9
+ readonly arrow: import("react").JSX.Element;
10
+ readonly line: import("react").JSX.Element;
11
+ readonly text: import("react").JSX.Element;
12
+ readonly note: import("react").JSX.Element;
13
+ readonly eraser: import("react").JSX.Element;
14
+ readonly laser: import("react").JSX.Element;
15
+ readonly undo: import("react").JSX.Element;
16
+ readonly redo: import("react").JSX.Element;
17
+ readonly minus: import("react").JSX.Element;
18
+ readonly plus: import("react").JSX.Element;
19
+ readonly fit: import("react").JSX.Element;
20
+ readonly download: import("react").JSX.Element;
21
+ readonly copy: import("react").JSX.Element;
22
+ readonly trash: import("react").JSX.Element;
23
+ readonly close: import("react").JSX.Element;
24
+ readonly check: import("react").JSX.Element;
25
+ readonly target: import("react").JSX.Element;
26
+ readonly eye: import("react").JSX.Element;
27
+ readonly wifiOff: import("react").JSX.Element;
28
+ readonly lock: import("react").JSX.Element;
29
+ readonly more: import("react").JSX.Element;
30
+ readonly chevron: import("react").JSX.Element;
31
+ };
32
+ export type BoardGlyph = keyof typeof GLYPHS;
33
+ export declare function BoardIcon({ name, size, className }: {
34
+ readonly name: BoardGlyph;
35
+ readonly size?: number;
36
+ readonly className?: string;
37
+ }): import("react").JSX.Element;
38
+ /** Six dots in two columns: the island's drag handle. */
39
+ export declare function GripIcon({ className }: {
40
+ readonly className?: string;
41
+ }): import("react").JSX.Element;
42
+ /** The remote cursor: a rounded arrow with a white outline, filled in the person's colour. */
43
+ export declare function CursorArrow({ color }: {
44
+ readonly color: string;
45
+ }): import("react").JSX.Element;
46
+ export {};
@@ -0,0 +1,22 @@
1
+ import { type BoardSurface, type BoardTool, type CallTheme, type IslandPlace, type PenColor, type PenWidth, type ShapeTool } from "./board-model.js";
2
+ export type IslandProps = {
3
+ readonly tool: BoardTool;
4
+ readonly shape: ShapeTool;
5
+ readonly onTool: (tool: BoardTool) => void;
6
+ readonly color: PenColor;
7
+ readonly width: PenWidth;
8
+ readonly onColor: (color: PenColor) => void;
9
+ readonly onWidth: (width: PenWidth) => void;
10
+ readonly surface: BoardSurface;
11
+ readonly theme: CallTheme;
12
+ readonly canUndo: boolean;
13
+ readonly canRedo: boolean;
14
+ readonly onUndo: () => void;
15
+ readonly onRedo: () => void;
16
+ readonly place: IslandPlace;
17
+ readonly onPlace: (place: IslandPlace, announce: boolean) => void;
18
+ readonly compact: boolean;
19
+ readonly primaryToolsFirst?: boolean;
20
+ };
21
+ /** The tool island: a pill of tools that docks at the bottom, the top or the left, or one pen button when compact. */
22
+ export declare function BoardIsland(props: IslandProps): import("react").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import type { WhoCanDraw } from "./board-draw-policy.js";
2
+ import { type BoardSurface, type IslandPlace } from "./board-model.js";
3
+ export type BoardMenuProps = {
4
+ readonly presentation?: "web" | "mobile";
5
+ readonly surface: BoardSurface;
6
+ readonly onSurface: (surface: BoardSurface) => void;
7
+ /** Offered outside a phone, to people who can draw. */
8
+ readonly place?: IslandPlace;
9
+ readonly onPlace: (place: IslandPlace) => void;
10
+ readonly downloadLabel?: string;
11
+ readonly onDownload: () => void;
12
+ readonly onCopy: () => void;
13
+ /** Offered to hosts. */
14
+ readonly whoCanDraw?: WhoCanDraw;
15
+ readonly onBring?: () => void;
16
+ readonly onClear?: () => void;
17
+ readonly onClose?: () => void;
18
+ };
19
+ /** The board's own menu: export, the person's own appearance, and the host's board actions. */
20
+ export declare function BoardMenu({ surface, onSurface, place, onPlace, onDownload, onCopy, whoCanDraw, onBring, onClear, onClose, downloadLabel, presentation }: BoardMenuProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ /** The board's look: the mockup's surfaces, island, zoom, chips and cursors, on the call's own tokens. */
2
+ export declare function BoardStyles(): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ export type WhoCanDraw = {
2
+ readonly policy: "everyone" | "hosts" | "picked";
3
+ readonly people: readonly {
4
+ readonly id: string;
5
+ readonly name: string;
6
+ readonly picked: boolean;
7
+ }[];
8
+ readonly onPolicy: (policy: WhoCanDraw["policy"]) => void;
9
+ readonly onPick: (id: string, picked: boolean) => void;
10
+ };
@@ -0,0 +1,30 @@
1
+ import type { WhiteboardEngineHandle } from "../WhiteboardCanvas.js";
2
+ import { type BoardSurface, type BoardTool, type CallTheme, type Camera, type PenColor, type PenWidth } from "./board-model.js";
3
+ /** The pen the person has chosen, and the surface it is drawn on. */
4
+ export type PenChoice = {
5
+ readonly color: PenColor;
6
+ readonly width: PenWidth;
7
+ readonly surface: BoardSurface;
8
+ readonly theme: CallTheme;
9
+ };
10
+ export declare function applyTool(engine: WhiteboardEngineHandle, tool: BoardTool, pen: PenChoice): void;
11
+ type PenChanges = {
12
+ readonly color?: PenColor;
13
+ readonly width?: PenWidth;
14
+ };
15
+ /** A colour or width change applies to what is selected, as well as to the next stroke. */
16
+ export declare function restyleSelection(engine: WhiteboardEngineHandle, pen: PenChoice, changes: PenChanges): boolean;
17
+ /** Places a plain sticky note centred on the board point and opens its text at once. */
18
+ export declare function placeNote(engine: WhiteboardEngineHandle, container: HTMLElement, point: {
19
+ readonly x: number;
20
+ readonly y: number;
21
+ }, color: PenColor): void;
22
+ /** Text written in a note sits at its top left, like the mockup's notes. */
23
+ export declare function settleNoteText(engine: WhiteboardEngineHandle): void;
24
+ /** Undo and redo have no engine call, so the board presses the engine's own keys. */
25
+ export declare function pressHistory(container: HTMLElement, redo: boolean): void;
26
+ export declare function readCamera(engine: WhiteboardEngineHandle): Camera;
27
+ export declare function setCamera(engine: WhiteboardEngineHandle, camera: Camera): void;
28
+ export declare function boardImage(engine: WhiteboardEngineHandle, background: string): Promise<Blob>;
29
+ export declare function sceneSignature(engine: WhiteboardEngineHandle): number;
30
+ export {};