@haklex/rich-ext-excalidraw 0.49.1 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/ExcalidrawConfigContext.d.ts +3 -1
  2. package/dist/ExcalidrawConfigContext.d.ts.map +1 -1
  3. package/dist/ExcalidrawDisplayRenderer.d.ts +1 -0
  4. package/dist/ExcalidrawDisplayRenderer.d.ts.map +1 -1
  5. package/dist/ExcalidrawEditRenderer.d.ts.map +1 -1
  6. package/dist/ExcalidrawFrameStates.d.ts +11 -0
  7. package/dist/ExcalidrawFrameStates.d.ts.map +1 -0
  8. package/dist/ExcalidrawSSRRenderer.d.ts.map +1 -1
  9. package/dist/index.mjs +51 -55
  10. package/dist/rich-ext-excalidraw.css +1 -1
  11. package/dist/scene/InteractiveScene.d.ts +11 -0
  12. package/dist/scene/InteractiveScene.d.ts.map +1 -0
  13. package/dist/scene/Scene.d.ts +24 -0
  14. package/dist/scene/Scene.d.ts.map +1 -0
  15. package/dist/scene/fonts.d.ts +8 -0
  16. package/dist/scene/fonts.d.ts.map +1 -0
  17. package/dist/scene/normalize.d.ts +4 -0
  18. package/dist/scene/normalize.d.ts.map +1 -0
  19. package/dist/scene/rough.d.ts +19 -0
  20. package/dist/scene/rough.d.ts.map +1 -0
  21. package/dist/scene/scene.test.d.ts +2 -0
  22. package/dist/scene/scene.test.d.ts.map +1 -0
  23. package/dist/scene/styles.css.d.ts +7 -0
  24. package/dist/scene/styles.css.d.ts.map +1 -0
  25. package/dist/scene/types.d.ts +118 -0
  26. package/dist/scene/types.d.ts.map +1 -0
  27. package/dist/static.mjs +2 -2
  28. package/dist/styles.css.d.ts +5 -2
  29. package/dist/styles.css.d.ts.map +1 -1
  30. package/dist/transformer-C2e_qiIg.js +487 -0
  31. package/dist/useExcalidrawData.d.ts +2 -0
  32. package/dist/useExcalidrawData.d.ts.map +1 -1
  33. package/dist/useExcalidrawFont-DAmoYxvU.js +795 -0
  34. package/dist/useExcalidrawFont.d.ts +2 -0
  35. package/dist/useExcalidrawFont.d.ts.map +1 -0
  36. package/package.json +12 -6
  37. package/dist/constants.d.ts +0 -5
  38. package/dist/constants.d.ts.map +0 -1
  39. package/dist/transformer-BaQILRBF.js +0 -315
  40. package/dist/useExcalidrawData-ZN9PHFyy.js +0 -227
@@ -0,0 +1,795 @@
1
+ import { createContext, memo, use, useCallback, useEffect, useMemo, useState } from "react";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { PenLine, TriangleAlert } from "lucide-react";
4
+ import { RoughGenerator } from "roughjs/bin/generator";
5
+ //#region src/ExcalidrawConfigContext.tsx
6
+ var ExcalidrawConfigContext = createContext({});
7
+ function ExcalidrawConfigProvider({ apiUrl, fontCssHref, saveSnapshot, children }) {
8
+ const value = useMemo(() => ({
9
+ apiUrl,
10
+ fontCssHref,
11
+ saveSnapshot
12
+ }), [
13
+ apiUrl,
14
+ fontCssHref,
15
+ saveSnapshot
16
+ ]);
17
+ return /* @__PURE__ */ jsx(ExcalidrawConfigContext.Provider, {
18
+ value,
19
+ children
20
+ });
21
+ }
22
+ function useExcalidrawConfig() {
23
+ return use(ExcalidrawConfigContext);
24
+ }
25
+ //#endregion
26
+ //#region src/styles.css.ts
27
+ var excalidrawStaticContainer = "_1c3wdzl0";
28
+ var excalidrawEditorContainer = "_1c3wdzl1";
29
+ var excalidrawPlaceholder = "_1c3wdzl2";
30
+ var excalidrawStateLayer = "_1c3wdzl3";
31
+ var excalidrawStateIcon = "_1c3wdzl4";
32
+ var excalidrawLoading = "_1c3wdzl6";
33
+ var excalidrawEmpty = "_1c3wdzl7";
34
+ var excalidrawError = "_1c3wdzl8";
35
+ var excalidrawErrorIcon = "_1c3wdzl9";
36
+ var excalidrawFullscreenPopup = "_1c3wdzlb";
37
+ var excalidrawDialogHeader = "_1c3wdzlc";
38
+ var excalidrawDialogHeaderTitle = "_1c3wdzld";
39
+ var excalidrawStatusDot = "_1c3wdzle";
40
+ var excalidrawDialogTitle = "_1c3wdzlf";
41
+ var excalidrawDialogMeta = "_1c3wdzlg";
42
+ var excalidrawHeaderActions = "_1c3wdzlh";
43
+ var excalidrawHeaderClose = "_1c3wdzli";
44
+ var excalidrawActionBarBtn = "_1c3wdzlj";
45
+ var excalidrawActionBarSep = "_1c3wdzlk";
46
+ var excalidrawActionBarUrl = "_1c3wdzll";
47
+ var excalidrawDialogCanvas = "_1c3wdzlm";
48
+ var excalidrawConfirmActions = "_1c3wdzln";
49
+ var excalidrawConfirmBtn = "_1c3wdzlo";
50
+ var excalidrawConfirmBtnPrimary = "_1c3wdzlp";
51
+ var excalidrawConfirmBtnDanger = "_1c3wdzlq";
52
+ var excalidrawEditOverlay = "_1c3wdzlr";
53
+ var excalidrawEditLabel = "_1c3wdzls";
54
+ //#endregion
55
+ //#region src/ExcalidrawFrameStates.tsx
56
+ var ExcalidrawPlaceholderState = ({ elementCount }) => {
57
+ const label = elementCount ? `Whiteboard · ${elementCount} elements` : "Whiteboard";
58
+ return /* @__PURE__ */ jsxs("div", {
59
+ "aria-label": label,
60
+ className: excalidrawStateLayer,
61
+ children: [/* @__PURE__ */ jsx(PenLine, {
62
+ "aria-hidden": true,
63
+ className: excalidrawStateIcon
64
+ }), /* @__PURE__ */ jsx("span", { children: label })]
65
+ });
66
+ };
67
+ var ExcalidrawLoadingState = () => /* @__PURE__ */ jsx("div", {
68
+ "aria-busy": true,
69
+ className: excalidrawLoading,
70
+ children: /* @__PURE__ */ jsx("span", { children: "Loading whiteboard…" })
71
+ });
72
+ var ExcalidrawEmptyState = () => /* @__PURE__ */ jsx("div", {
73
+ className: excalidrawEmpty,
74
+ children: /* @__PURE__ */ jsx("span", { children: "Empty whiteboard" })
75
+ });
76
+ var ExcalidrawErrorState = ({ message, onRetry }) => /* @__PURE__ */ jsxs("div", {
77
+ className: excalidrawError,
78
+ role: "alert",
79
+ children: [
80
+ /* @__PURE__ */ jsx(TriangleAlert, {
81
+ "aria-hidden": true,
82
+ className: excalidrawErrorIcon
83
+ }),
84
+ /* @__PURE__ */ jsx("span", { children: message }),
85
+ onRetry && /* @__PURE__ */ jsx("button", {
86
+ className: "_1c3wdzla",
87
+ type: "button",
88
+ onClick: onRetry,
89
+ children: "Retry"
90
+ })
91
+ ]
92
+ });
93
+ //#endregion
94
+ //#region src/scene/fonts.ts
95
+ var EXCALIDRAW_FONT_STACK = {
96
+ hand: "\"Excalifont\", \"LXGW WenKai Screen\", \"Virgil\", \"Comic Sans MS\", \"Segoe Print\", \"Bradley Hand\", cursive",
97
+ sans: "Helvetica, \"PingFang SC\", \"Microsoft YaHei\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", system-ui, sans-serif",
98
+ mono: "\"Cascadia Code\", \"Cascadia\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace"
99
+ };
100
+ function fontFamilyToCss(id) {
101
+ switch (id) {
102
+ case 3: return EXCALIDRAW_FONT_STACK.mono;
103
+ default: return EXCALIDRAW_FONT_STACK.hand;
104
+ }
105
+ }
106
+ //#endregion
107
+ //#region src/scene/rough.ts
108
+ var _gen = null;
109
+ function gen() {
110
+ if (!_gen) _gen = new RoughGenerator();
111
+ return _gen;
112
+ }
113
+ function roughOptionsForElement(el, override) {
114
+ const dashes = strokeDash(el.strokeStyle, el.strokeWidth);
115
+ const opts = {
116
+ seed: el.seed || 1,
117
+ stroke: el.strokeColor,
118
+ strokeWidth: el.strokeWidth,
119
+ roughness: el.roughness,
120
+ disableMultiStroke: el.strokeWidth < 2,
121
+ fill: el.backgroundColor === "transparent" ? void 0 : el.backgroundColor,
122
+ fillStyle: el.fillStyle,
123
+ fillWeight: el.strokeWidth / 2,
124
+ hachureGap: el.strokeWidth * 4,
125
+ preserveVertices: false
126
+ };
127
+ if (dashes) opts.strokeLineDash = dashes;
128
+ return {
129
+ ...opts,
130
+ ...override
131
+ };
132
+ }
133
+ function strokeDash(style, w) {
134
+ if (style === "solid") return void 0;
135
+ if (style === "dashed") return [8, 8 + w];
136
+ if (style === "dotted") return [1.5, 6 + w];
137
+ }
138
+ function rectangle(el, override) {
139
+ return gen().rectangle(0, 0, el.width, el.height, roughOptionsForElement(el, override));
140
+ }
141
+ function ellipse(el, override) {
142
+ return gen().ellipse(el.width / 2, el.height / 2, el.width, el.height, roughOptionsForElement(el, override));
143
+ }
144
+ function diamond(el, override) {
145
+ const hw = el.width / 2;
146
+ const hh = el.height / 2;
147
+ return gen().polygon([
148
+ [hw, 0],
149
+ [el.width, hh],
150
+ [hw, el.height],
151
+ [0, hh]
152
+ ], roughOptionsForElement(el, override));
153
+ }
154
+ function path(d, options) {
155
+ return gen().path(d, options);
156
+ }
157
+ function linearPath(points, options) {
158
+ return gen().linearPath(points, options);
159
+ }
160
+ function curve(points, options) {
161
+ return gen().curve(points, options);
162
+ }
163
+ function opsToPath(ops) {
164
+ let d = "";
165
+ for (const { op, data } of ops) switch (op) {
166
+ case "move":
167
+ d += `M${data[0]} ${data[1]} `;
168
+ break;
169
+ case "lineTo":
170
+ d += `L${data[0]} ${data[1]} `;
171
+ break;
172
+ case "bcurveTo": d += `C${data[0]} ${data[1]}, ${data[2]} ${data[3]}, ${data[4]} ${data[5]} `;
173
+ }
174
+ return d.trim();
175
+ }
176
+ function drawableToOpSets(drawable) {
177
+ return drawable.sets.map((set) => ({
178
+ type: set.type,
179
+ d: opsToPath(set.ops)
180
+ }));
181
+ }
182
+ function roundedRectPath(width, height, radius) {
183
+ const r = Math.max(0, Math.min(radius, width / 2, height / 2));
184
+ return [
185
+ `M ${r} 0`,
186
+ `L ${width - r} 0`,
187
+ `Q ${width} 0 ${width} ${r}`,
188
+ `L ${width} ${height - r}`,
189
+ `Q ${width} ${height} ${width - r} ${height}`,
190
+ `L ${r} ${height}`,
191
+ `Q 0 ${height} 0 ${height - r}`,
192
+ `L 0 ${r}`,
193
+ `Q 0 0 ${r} 0`,
194
+ "Z"
195
+ ].join(" ");
196
+ }
197
+ var PROPORTIONAL_RADIUS = .25;
198
+ var FIXED_RADIUS = 32;
199
+ function getCornerRadius(el) {
200
+ if (!el.roundness) return 0;
201
+ if (el.roundness.type === 3) {
202
+ const min = Math.min(el.width, el.height);
203
+ return Math.min(min * PROPORTIONAL_RADIUS, FIXED_RADIUS);
204
+ }
205
+ return el.roundness.value ?? 0;
206
+ }
207
+ //#endregion
208
+ //#region src/scene/Scene.tsx
209
+ var SVG_PADDING = 32;
210
+ function computeSceneBounds(elements) {
211
+ if (elements.length === 0) return {
212
+ minX: 0,
213
+ minY: 0,
214
+ width: 100,
215
+ height: 100
216
+ };
217
+ let minX = Infinity;
218
+ let minY = Infinity;
219
+ let maxX = -Infinity;
220
+ let maxY = -Infinity;
221
+ for (const el of elements) {
222
+ if (el.isDeleted) continue;
223
+ const x0 = el.x;
224
+ const y0 = el.y;
225
+ const x1 = el.x + el.width;
226
+ const y1 = el.y + el.height;
227
+ if (x0 < minX) minX = x0;
228
+ if (y0 < minY) minY = y0;
229
+ if (x1 > maxX) maxX = x1;
230
+ if (y1 > maxY) maxY = y1;
231
+ }
232
+ if (!Number.isFinite(minX)) return {
233
+ minX: 0,
234
+ minY: 0,
235
+ width: 100,
236
+ height: 100
237
+ };
238
+ return {
239
+ minX: minX - SVG_PADDING,
240
+ minY: minY - SVG_PADDING,
241
+ width: maxX - minX + 64,
242
+ height: maxY - minY + 64
243
+ };
244
+ }
245
+ var DARK_SCENE_FILTER = "invert(93%) hue-rotate(180deg)";
246
+ var COUNTER_INVERT_FILTER_ID = "rich-excalidraw-counter-invert";
247
+ var SceneDarkContext = createContext(false);
248
+ var CounterInvertFilter = () => /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("filter", {
249
+ colorInterpolationFilters: "sRGB",
250
+ id: COUNTER_INVERT_FILTER_ID,
251
+ children: [/* @__PURE__ */ jsx("feColorMatrix", {
252
+ type: "hueRotate",
253
+ values: "180"
254
+ }), /* @__PURE__ */ jsx("feColorMatrix", {
255
+ type: "matrix",
256
+ values: "-1.16279 0 0 0 1.081395 0 -1.16279 0 0 1.081395 0 0 -1.16279 0 1.081395 0 0 0 1 0"
257
+ })]
258
+ }) });
259
+ var SceneContent = memo(({ dark = false, files, scene }) => {
260
+ const elements = scene.elements.filter((el) => !el.isDeleted);
261
+ return /* @__PURE__ */ jsxs(SceneDarkContext, {
262
+ value: dark,
263
+ children: [dark && /* @__PURE__ */ jsx(CounterInvertFilter, {}), elements.map((el) => /* @__PURE__ */ jsx(ElementGroup, {
264
+ el,
265
+ files
266
+ }, el.id))]
267
+ });
268
+ });
269
+ SceneContent.displayName = "SceneContent";
270
+ var Scene = ({ className, files, scene, style, theme = "light" }) => {
271
+ const bounds = computeSceneBounds(scene.elements.filter((el) => !el.isDeleted));
272
+ const isDark = theme === "dark";
273
+ return /* @__PURE__ */ jsx("svg", {
274
+ className,
275
+ preserveAspectRatio: "xMidYMid meet",
276
+ role: "img",
277
+ viewBox: `${bounds.minX} ${bounds.minY} ${bounds.width} ${bounds.height}`,
278
+ xmlns: "http://www.w3.org/2000/svg",
279
+ style: {
280
+ width: "100%",
281
+ height: "100%",
282
+ display: "block",
283
+ ...isDark ? { filter: DARK_SCENE_FILTER } : null,
284
+ ...style
285
+ },
286
+ children: /* @__PURE__ */ jsx(SceneContent, {
287
+ dark: isDark,
288
+ files: files ?? scene.files,
289
+ scene
290
+ })
291
+ });
292
+ };
293
+ var ElementGroup = ({ el, files }) => {
294
+ const cx = el.x + el.width / 2;
295
+ const cy = el.y + el.height / 2;
296
+ const angleDeg = el.angle * 180 / Math.PI;
297
+ const transform = `translate(${el.x} ${el.y}) rotate(${angleDeg} ${el.width / 2} ${el.height / 2})`;
298
+ return /* @__PURE__ */ jsx("g", {
299
+ "data-cx": cx,
300
+ "data-cy": cy,
301
+ "data-id": el.id,
302
+ "data-type": el.type,
303
+ opacity: el.opacity != null ? el.opacity / 100 : 1,
304
+ transform,
305
+ children: /* @__PURE__ */ jsx(ElementContent, {
306
+ el,
307
+ files
308
+ })
309
+ });
310
+ };
311
+ var ElementContent = ({ el, files }) => {
312
+ switch (el.type) {
313
+ case "rectangle": return /* @__PURE__ */ jsx(RectangleRender, { el });
314
+ case "ellipse": return /* @__PURE__ */ jsx(EllipseRender, { el });
315
+ case "diamond": return /* @__PURE__ */ jsx(DiamondRender, { el });
316
+ case "line":
317
+ case "arrow": return /* @__PURE__ */ jsx(LinearRender, { el });
318
+ case "freedraw": return /* @__PURE__ */ jsx(FreedrawRender, { el });
319
+ case "text": return /* @__PURE__ */ jsx(TextRender, { el });
320
+ case "image": return /* @__PURE__ */ jsx(ImageRender, {
321
+ el,
322
+ files
323
+ });
324
+ case "frame":
325
+ case "magicframe": return /* @__PURE__ */ jsx(FrameRender, { el });
326
+ case "embeddable":
327
+ case "iframe": return /* @__PURE__ */ jsx(EmbedPlaceholder, { el });
328
+ default: return null;
329
+ }
330
+ };
331
+ var PathSet = ({ drawable, fillColor, strokeColor, strokeWidth }) => {
332
+ const opSets = drawableToOpSets(drawable);
333
+ return /* @__PURE__ */ jsx(Fragment, { children: opSets.map((set, i) => {
334
+ if (set.type === "path") return /* @__PURE__ */ jsx("path", {
335
+ d: set.d,
336
+ fill: "none",
337
+ stroke: strokeColor,
338
+ strokeLinecap: "round",
339
+ strokeLinejoin: "round",
340
+ strokeWidth
341
+ }, i);
342
+ if (set.type === "fillPath") return /* @__PURE__ */ jsx("path", {
343
+ d: set.d,
344
+ fill: fillColor || "none",
345
+ fillRule: "evenodd",
346
+ stroke: "none"
347
+ }, i);
348
+ return /* @__PURE__ */ jsx("path", {
349
+ d: set.d,
350
+ fill: "none",
351
+ stroke: fillColor || strokeColor,
352
+ strokeLinecap: "round",
353
+ strokeLinejoin: "round",
354
+ strokeWidth: drawable.options.fillWeight ?? strokeWidth / 2
355
+ }, i);
356
+ }) });
357
+ };
358
+ var RectangleRender = ({ el }) => {
359
+ const radius = getCornerRadius(el);
360
+ const opts = roughOptionsForElement(el);
361
+ const drawable = radius > 0 ? path(roundedRectPath(el.width, el.height, radius), opts) : rectangle(el);
362
+ return /* @__PURE__ */ jsx(PathSet, {
363
+ drawable,
364
+ fillColor: el.backgroundColor !== "transparent" ? el.backgroundColor : void 0,
365
+ strokeColor: el.strokeColor,
366
+ strokeWidth: el.strokeWidth
367
+ });
368
+ };
369
+ var EllipseRender = ({ el }) => {
370
+ const drawable = ellipse(el);
371
+ return /* @__PURE__ */ jsx(PathSet, {
372
+ drawable,
373
+ fillColor: el.backgroundColor !== "transparent" ? el.backgroundColor : void 0,
374
+ strokeColor: el.strokeColor,
375
+ strokeWidth: el.strokeWidth
376
+ });
377
+ };
378
+ var DiamondRender = ({ el }) => {
379
+ const drawable = diamond(el);
380
+ return /* @__PURE__ */ jsx(PathSet, {
381
+ drawable,
382
+ fillColor: el.backgroundColor !== "transparent" ? el.backgroundColor : void 0,
383
+ strokeColor: el.strokeColor,
384
+ strokeWidth: el.strokeWidth
385
+ });
386
+ };
387
+ var ARROWHEAD_LENGTH = 18;
388
+ function arrowheadAngle(points, end) {
389
+ if (points.length < 2) return {
390
+ x: 0,
391
+ y: 0,
392
+ angle: 0
393
+ };
394
+ const idx = end === "end" ? points.length - 1 : 0;
395
+ const next = end === "end" ? points.length - 2 : 1;
396
+ const [x, y] = points[idx];
397
+ const [px, py] = points[next];
398
+ return {
399
+ x,
400
+ y,
401
+ angle: Math.atan2(y - py, x - px)
402
+ };
403
+ }
404
+ var Arrowhead = ({ angle, color, shape, strokeWidth, x, y }) => {
405
+ if (!shape) return null;
406
+ if (shape === "arrow" || shape === "triangle" || shape === "triangle_outline") {
407
+ const len = ARROWHEAD_LENGTH;
408
+ const a1 = angle + Math.PI - Math.PI / 7;
409
+ const a2 = angle + Math.PI + Math.PI / 7;
410
+ const x1 = x + Math.cos(a1) * len;
411
+ const y1 = y + Math.sin(a1) * len;
412
+ const x2 = x + Math.cos(a2) * len;
413
+ const y2 = y + Math.sin(a2) * len;
414
+ if (shape === "arrow") return /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("line", {
415
+ stroke: color,
416
+ strokeLinecap: "round",
417
+ strokeWidth,
418
+ x1: x,
419
+ x2: x1,
420
+ y1: y,
421
+ y2: y1
422
+ }), /* @__PURE__ */ jsx("line", {
423
+ stroke: color,
424
+ strokeLinecap: "round",
425
+ strokeWidth,
426
+ x1: x,
427
+ x2,
428
+ y1: y,
429
+ y2
430
+ })] });
431
+ return /* @__PURE__ */ jsx("polygon", {
432
+ fill: shape === "triangle" ? color : "none",
433
+ points: `${x},${y} ${x1},${y1} ${x2},${y2}`,
434
+ stroke: color,
435
+ strokeLinejoin: "round",
436
+ strokeWidth
437
+ });
438
+ }
439
+ if (shape === "dot" || shape === "circle" || shape === "circle_outline") return /* @__PURE__ */ jsx("circle", {
440
+ cx: x,
441
+ cy: y,
442
+ fill: shape === "circle_outline" ? "none" : color,
443
+ r: ARROWHEAD_LENGTH / 3,
444
+ stroke: color,
445
+ strokeWidth
446
+ });
447
+ if (shape === "bar") {
448
+ const len = ARROWHEAD_LENGTH / 1.5;
449
+ const perp = angle + Math.PI / 2;
450
+ return /* @__PURE__ */ jsx("line", {
451
+ stroke: color,
452
+ strokeLinecap: "round",
453
+ strokeWidth,
454
+ x1: x + Math.cos(perp) * len,
455
+ x2: x - Math.cos(perp) * len,
456
+ y1: y + Math.sin(perp) * len,
457
+ y2: y - Math.sin(perp) * len
458
+ });
459
+ }
460
+ return null;
461
+ };
462
+ var LinearRender = ({ el }) => {
463
+ const points = el.points && el.points.length > 0 ? el.points : [[0, 0], [el.width, el.height]];
464
+ const isCurved = el.roundness?.type === 2;
465
+ const opts = roughOptionsForElement(el, {
466
+ fill: void 0,
467
+ fillStyle: void 0
468
+ });
469
+ const drawable = isCurved && points.length > 2 ? curve(points, opts) : linearPath(points, opts);
470
+ const startHead = el.startArrowhead;
471
+ const endHead = el.type === "arrow" ? el.endArrowhead ?? "arrow" : el.endArrowhead;
472
+ const startInfo = startHead ? arrowheadAngle(points, "start") : null;
473
+ const endInfo = endHead ? arrowheadAngle(points, "end") : null;
474
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
475
+ /* @__PURE__ */ jsx(PathSet, {
476
+ drawable,
477
+ strokeColor: el.strokeColor,
478
+ strokeWidth: el.strokeWidth
479
+ }),
480
+ startHead && startInfo && /* @__PURE__ */ jsx(Arrowhead, {
481
+ angle: startInfo.angle + Math.PI,
482
+ color: el.strokeColor,
483
+ shape: startHead,
484
+ strokeWidth: el.strokeWidth,
485
+ x: startInfo.x,
486
+ y: startInfo.y
487
+ }),
488
+ endHead && endInfo && /* @__PURE__ */ jsx(Arrowhead, {
489
+ angle: endInfo.angle,
490
+ color: el.strokeColor,
491
+ shape: endHead,
492
+ strokeWidth: el.strokeWidth,
493
+ x: endInfo.x,
494
+ y: endInfo.y
495
+ })
496
+ ] });
497
+ };
498
+ var FreedrawRender = ({ el }) => {
499
+ if (!el.points || el.points.length === 0) return null;
500
+ if (el.points.length === 1) {
501
+ const [x, y] = el.points[0];
502
+ return /* @__PURE__ */ jsx("circle", {
503
+ cx: x,
504
+ cy: y,
505
+ fill: el.strokeColor,
506
+ r: el.strokeWidth
507
+ });
508
+ }
509
+ const d = el.points.map(([x, y], i) => `${i === 0 ? "M" : "L"}${x} ${y}`).join(" ");
510
+ return /* @__PURE__ */ jsx("path", {
511
+ d,
512
+ fill: "none",
513
+ stroke: el.strokeColor,
514
+ strokeLinecap: "round",
515
+ strokeLinejoin: "round",
516
+ strokeWidth: el.strokeWidth
517
+ });
518
+ };
519
+ var DEFAULT_LINE_HEIGHT = 1.25;
520
+ var TextRender = ({ el }) => {
521
+ const lineHeight = el.lineHeight ?? DEFAULT_LINE_HEIGHT;
522
+ const lines = el.text.split("\n");
523
+ const fontSize = el.fontSize;
524
+ const family = fontFamilyToCss(el.fontFamily);
525
+ const anchor = el.textAlign === "center" ? "middle" : el.textAlign === "right" ? "end" : "start";
526
+ const ax = el.textAlign === "center" ? el.width / 2 : el.textAlign === "right" ? el.width : 0;
527
+ const dy = fontSize * lineHeight;
528
+ const baseline = fontSize * .82;
529
+ return /* @__PURE__ */ jsx("text", {
530
+ dominantBaseline: "alphabetic",
531
+ fill: el.strokeColor,
532
+ fontFamily: family,
533
+ fontSize,
534
+ style: { whiteSpace: "pre" },
535
+ textAnchor: anchor,
536
+ x: ax,
537
+ y: baseline,
538
+ children: lines.map((line, i) => /* @__PURE__ */ jsx("tspan", {
539
+ dy: i === 0 ? 0 : dy,
540
+ x: ax,
541
+ children: line || " "
542
+ }, i))
543
+ });
544
+ };
545
+ var ImageRender = ({ el, files }) => {
546
+ const dark = use(SceneDarkContext);
547
+ const file = el.fileId && files ? files[el.fileId] : void 0;
548
+ if (!file?.dataURL) return /* @__PURE__ */ jsx(ImagePlaceholder, {
549
+ el,
550
+ label: "Image"
551
+ });
552
+ const scaleX = el.scale?.[0] ?? 1;
553
+ const scaleY = el.scale?.[1] ?? 1;
554
+ return /* @__PURE__ */ jsx("image", {
555
+ filter: dark ? `url(#${COUNTER_INVERT_FILTER_ID})` : void 0,
556
+ height: el.height,
557
+ href: file.dataURL,
558
+ preserveAspectRatio: "none",
559
+ width: el.width,
560
+ transform: scaleX !== 1 || scaleY !== 1 ? `translate(${scaleX < 0 ? el.width : 0} ${scaleY < 0 ? el.height : 0}) scale(${scaleX} ${scaleY})` : void 0
561
+ });
562
+ };
563
+ var ImagePlaceholder = ({ el, label }) => {
564
+ return /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("rect", {
565
+ fill: "none",
566
+ height: el.height,
567
+ stroke: el.strokeColor || "#888",
568
+ strokeDasharray: "4 4",
569
+ strokeWidth: 1,
570
+ width: el.width
571
+ }), /* @__PURE__ */ jsx("text", {
572
+ dominantBaseline: "middle",
573
+ fill: el.strokeColor || "#888",
574
+ fontFamily: fontFamilyToCss(1),
575
+ fontSize: Math.min(16, el.height / 4),
576
+ textAnchor: "middle",
577
+ x: el.width / 2,
578
+ y: el.height / 2,
579
+ children: label
580
+ })] });
581
+ };
582
+ var FrameRender = ({ el }) => {
583
+ const name = el.name || (el.type === "magicframe" ? "Magic frame" : "Frame");
584
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("rect", {
585
+ fill: "none",
586
+ height: el.height,
587
+ rx: 4,
588
+ stroke: "#bbb",
589
+ strokeWidth: 1.5,
590
+ width: el.width
591
+ }), /* @__PURE__ */ jsx("text", {
592
+ fill: "#999",
593
+ fontFamily: fontFamilyToCss(2),
594
+ fontSize: 12,
595
+ x: 4,
596
+ y: -4,
597
+ children: name
598
+ })] });
599
+ };
600
+ var EmbedPlaceholder = ({ el }) => /* @__PURE__ */ jsx(ImagePlaceholder, {
601
+ el,
602
+ label: "Embed"
603
+ });
604
+ //#endregion
605
+ //#region src/scene/normalize.ts
606
+ function toScene(data) {
607
+ if (!data) return null;
608
+ if (typeof data !== "object") return null;
609
+ if (!Array.isArray(data.elements) && data.data !== void 0) return toScene(data.data);
610
+ return {
611
+ type: "excalidraw",
612
+ elements: Array.isArray(data.elements) ? data.elements : [],
613
+ appState: data.appState ?? {},
614
+ files: data.files ?? {}
615
+ };
616
+ }
617
+ function countElements(scene) {
618
+ return scene.elements.filter((el) => !el.isDeleted).length;
619
+ }
620
+ //#endregion
621
+ //#region src/types.ts
622
+ var LOCAL_SCENE_PATH = /^(?:\.{1,2}\/)?[\w-][\w./-]*\.excalidraw$/;
623
+ function isLocalScenePath(ref) {
624
+ return LOCAL_SCENE_PATH.test(ref);
625
+ }
626
+ function parseSnapshot(raw) {
627
+ if (!raw || !raw.trim()) return null;
628
+ try {
629
+ const json = JSON.parse(raw);
630
+ if (json && typeof json === "object") return {
631
+ type: "inline",
632
+ data: json
633
+ };
634
+ } catch {}
635
+ const lines = raw.split("\n");
636
+ const firstLine = lines[0].trim();
637
+ if (!firstLine.startsWith("http") && !firstLine.startsWith("blob:") && !firstLine.startsWith("ref:") && !isLocalScenePath(firstLine)) return null;
638
+ const remaining = lines.slice(1).join("\n").trim();
639
+ if (remaining) try {
640
+ const delta = JSON.parse(remaining);
641
+ if (delta && typeof delta === "object") return {
642
+ type: "delta",
643
+ baseUrl: firstLine,
644
+ delta
645
+ };
646
+ } catch {}
647
+ return {
648
+ type: "remote",
649
+ url: firstLine
650
+ };
651
+ }
652
+ function serializeSnapshot(snapshot) {
653
+ switch (snapshot.type) {
654
+ case "inline": return JSON.stringify(snapshot.data);
655
+ case "remote": return snapshot.url;
656
+ case "delta": return [snapshot.baseUrl, JSON.stringify(snapshot.delta)].join("\n");
657
+ }
658
+ }
659
+ //#endregion
660
+ //#region src/useExcalidrawData.ts
661
+ function resolveUrl(url, apiUrl) {
662
+ const refLine = url;
663
+ if (url.startsWith("http") || url.startsWith("blob:") || isLocalScenePath(url)) return {
664
+ fetchUrl: url,
665
+ refLine
666
+ };
667
+ if (url.startsWith("ref:") && apiUrl) return {
668
+ fetchUrl: `${apiUrl}/objects/${url.slice(4)}`,
669
+ refLine
670
+ };
671
+ return { error: url.startsWith("ref:") ? "Missing apiUrl for ref resolution" : "Unrecognized snapshot format" };
672
+ }
673
+ function typedToParsed(typed, apiUrl) {
674
+ switch (typed.type) {
675
+ case "inline": return {
676
+ type: "inline",
677
+ snapshot: typed.data
678
+ };
679
+ case "remote": {
680
+ const result = resolveUrl(typed.url, apiUrl);
681
+ if ("error" in result) return {
682
+ type: "error",
683
+ error: result.error
684
+ };
685
+ return {
686
+ type: "remote",
687
+ fetchUrl: result.fetchUrl,
688
+ refLine: result.refLine
689
+ };
690
+ }
691
+ case "delta": {
692
+ const result = resolveUrl(typed.baseUrl, apiUrl);
693
+ if ("error" in result) return {
694
+ type: "error",
695
+ error: result.error
696
+ };
697
+ return {
698
+ type: "incremental",
699
+ fetchUrl: result.fetchUrl,
700
+ refLine: result.refLine,
701
+ delta: typed.delta
702
+ };
703
+ }
704
+ }
705
+ }
706
+ function stringToParsed(data, apiUrl) {
707
+ const typed = parseSnapshot(data);
708
+ if (!typed) return { type: "empty" };
709
+ return typedToParsed(typed, apiUrl);
710
+ }
711
+ function useExcalidrawData(data) {
712
+ const { apiUrl } = useExcalidrawConfig();
713
+ const parsed = useMemo(() => {
714
+ if (data === null || data === void 0) return { type: "empty" };
715
+ if (typeof data === "string") return stringToParsed(data, apiUrl);
716
+ return typedToParsed(data, apiUrl);
717
+ }, [data, apiUrl]);
718
+ const [remoteSnapshot, setRemoteSnapshot] = useState();
719
+ const [baseData, setBaseData] = useState();
720
+ const [loading, setLoading] = useState(parsed.type === "remote" || parsed.type === "incremental");
721
+ const [error, setError] = useState("");
722
+ const [attempt, setAttempt] = useState(0);
723
+ const retry = useCallback(() => setAttempt((n) => n + 1), []);
724
+ useEffect(() => {
725
+ if (parsed.type !== "remote" && parsed.type !== "incremental") return;
726
+ const { fetchUrl } = parsed;
727
+ const delta = parsed.type === "incremental" ? parsed.delta : void 0;
728
+ let cancelled = false;
729
+ setLoading(true);
730
+ setError("");
731
+ fetch(fetchUrl).then((res) => {
732
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
733
+ return res.json();
734
+ }).then(async (json) => {
735
+ if (cancelled) return;
736
+ setBaseData(json);
737
+ if ((delta ? Object.keys(delta) : []).length > 0) {
738
+ const { patch } = await import("jsondiffpatch");
739
+ if (cancelled) return;
740
+ const patched = patch(structuredClone(json), delta);
741
+ setRemoteSnapshot(patched);
742
+ } else setRemoteSnapshot(json);
743
+ setLoading(false);
744
+ }).catch((err) => {
745
+ if (!cancelled) {
746
+ setError(err instanceof Error ? err.message : "Failed to load");
747
+ setLoading(false);
748
+ }
749
+ });
750
+ return () => {
751
+ cancelled = true;
752
+ };
753
+ }, [parsed, attempt]);
754
+ if (parsed.type === "inline") return {
755
+ snapshot: parsed.snapshot,
756
+ loading: false,
757
+ error: "",
758
+ retry
759
+ };
760
+ if (parsed.type === "remote" || parsed.type === "incremental") return {
761
+ snapshot: remoteSnapshot,
762
+ loading,
763
+ error,
764
+ retry,
765
+ baseRef: parsed.refLine,
766
+ baseData
767
+ };
768
+ if (parsed.type === "error") return {
769
+ snapshot: void 0,
770
+ loading: false,
771
+ error: parsed.error,
772
+ retry
773
+ };
774
+ return {
775
+ snapshot: void 0,
776
+ loading: false,
777
+ error: "",
778
+ retry
779
+ };
780
+ }
781
+ //#endregion
782
+ //#region src/useExcalidrawFont.ts
783
+ function useExcalidrawFont() {
784
+ const { fontCssHref } = useExcalidrawConfig();
785
+ useEffect(() => {
786
+ if (!fontCssHref || typeof document === "undefined") return;
787
+ if (document.head.querySelector(`link[href="${CSS.escape(fontCssHref)}"]`)) return;
788
+ const link = document.createElement("link");
789
+ link.rel = "stylesheet";
790
+ link.href = fontCssHref;
791
+ document.head.append(link);
792
+ }, [fontCssHref]);
793
+ }
794
+ //#endregion
795
+ export { excalidrawEditorContainer as A, excalidrawDialogCanvas as C, excalidrawDialogTitle as D, excalidrawDialogMeta as E, excalidrawStaticContainer as F, excalidrawStatusDot as I, ExcalidrawConfigProvider as L, excalidrawHeaderActions as M, excalidrawHeaderClose as N, excalidrawEditLabel as O, excalidrawPlaceholder as P, useExcalidrawConfig as R, excalidrawConfirmBtnPrimary as S, excalidrawDialogHeaderTitle as T, excalidrawActionBarSep as _, serializeSnapshot as a, excalidrawConfirmBtn as b, DARK_SCENE_FILTER as c, computeSceneBounds as d, ExcalidrawEmptyState as f, excalidrawActionBarBtn as g, ExcalidrawPlaceholderState as h, parseSnapshot as i, excalidrawFullscreenPopup as j, excalidrawEditOverlay as k, Scene as l, ExcalidrawLoadingState as m, useExcalidrawData as n, countElements as o, ExcalidrawErrorState as p, isLocalScenePath as r, toScene as s, useExcalidrawFont as t, SceneContent as u, excalidrawActionBarUrl as v, excalidrawDialogHeader as w, excalidrawConfirmBtnDanger as x, excalidrawConfirmActions as y };