@tangle-network/agent-app 0.22.0 → 0.23.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.
@@ -56,6 +56,7 @@ import {
56
56
  } from "../chunk-2W4YCAFH.js";
57
57
  import {
58
58
  SCENE_SCHEMA_VERSION,
59
+ assertSceneMediaSrc,
59
60
  bleedAwareExportBounds,
60
61
  scaleForPreset
61
62
  } from "../chunk-JZAJE3JL.js";
@@ -175,8 +176,326 @@ function downloadDataUrl(dataUrl, filename) {
175
176
  globalThis.document.body.removeChild(a);
176
177
  }
177
178
 
179
+ // src/design-canvas-react/insert-builders.ts
180
+ var MAX_INSERT_DIMENSION = 600;
181
+ function mintElementId() {
182
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
183
+ return crypto.randomUUID();
184
+ }
185
+ return `el-${Date.now()}-${Math.random().toString(36).slice(2)}`;
186
+ }
187
+ function fittedSize(naturalW, naturalH, pageWidth, pageHeight) {
188
+ const cap = Math.min(MAX_INSERT_DIMENSION, pageWidth * 0.8, pageHeight * 0.8);
189
+ const longest = Math.max(naturalW, naturalH);
190
+ const scale = naturalW > 0 && naturalH > 0 ? Math.min(1, cap / longest) : 1;
191
+ const width = Math.max(1, Math.round((naturalW || cap) * scale));
192
+ const height = Math.max(1, Math.round((naturalH || cap) * scale));
193
+ return { width, height };
194
+ }
195
+ function centeredPosition(width, height, pageWidth, pageHeight) {
196
+ return {
197
+ x: Math.round((pageWidth - width) / 2),
198
+ y: Math.round((pageHeight - height) / 2)
199
+ };
200
+ }
201
+ function baseAttrs(name, x, y) {
202
+ return { id: mintElementId(), name, x, y, rotation: 0, opacity: 1, locked: false, visible: true };
203
+ }
204
+ function addElementOp(pageId, element) {
205
+ return { type: "add_element", pageId, element };
206
+ }
207
+ function buildInsertImageOp(src, naturalSize, page) {
208
+ assertSceneMediaSrc(src, "image src");
209
+ const { width, height } = fittedSize(naturalSize.width, naturalSize.height, page.width, page.height);
210
+ const { x, y } = centeredPosition(width, height, page.width, page.height);
211
+ const element = {
212
+ ...baseAttrs("Image", x, y),
213
+ kind: "image",
214
+ width,
215
+ height,
216
+ src,
217
+ fit: "contain"
218
+ };
219
+ return addElementOp(page.pageId, element);
220
+ }
221
+ var DEFAULT_INSERT_TEMPLATES = [
222
+ {
223
+ id: "heading",
224
+ label: "Heading",
225
+ build(page) {
226
+ const width = Math.min(480, Math.round(page.width * 0.7));
227
+ const { x, y } = centeredPosition(width, 60, page.width, page.height);
228
+ const element = {
229
+ ...baseAttrs("Heading", x, y),
230
+ kind: "text",
231
+ text: "Add a headline",
232
+ width,
233
+ fontFamily: "Inter",
234
+ fontSize: 48,
235
+ fontStyle: "bold",
236
+ fill: "#111827",
237
+ align: "left",
238
+ lineHeight: 1.1,
239
+ letterSpacing: 0
240
+ };
241
+ return [addElementOp(page.pageId, element)];
242
+ }
243
+ },
244
+ {
245
+ id: "body",
246
+ label: "Body text",
247
+ build(page) {
248
+ const width = Math.min(420, Math.round(page.width * 0.6));
249
+ const { x, y } = centeredPosition(width, 80, page.width, page.height);
250
+ const element = {
251
+ ...baseAttrs("Body", x, y),
252
+ kind: "text",
253
+ text: "Add a paragraph of supporting copy.",
254
+ width,
255
+ fontFamily: "Inter",
256
+ fontSize: 20,
257
+ fontStyle: "normal",
258
+ fill: "#374151",
259
+ align: "left",
260
+ lineHeight: 1.4,
261
+ letterSpacing: 0
262
+ };
263
+ return [addElementOp(page.pageId, element)];
264
+ }
265
+ },
266
+ {
267
+ id: "rect",
268
+ label: "Rectangle",
269
+ build(page) {
270
+ const width = Math.min(320, Math.round(page.width * 0.4));
271
+ const height = Math.min(200, Math.round(page.height * 0.3));
272
+ const { x, y } = centeredPosition(width, height, page.width, page.height);
273
+ const element = {
274
+ ...baseAttrs("Rectangle", x, y),
275
+ kind: "rect",
276
+ width,
277
+ height,
278
+ fill: "#6366f1",
279
+ cornerRadius: 12
280
+ };
281
+ return [addElementOp(page.pageId, element)];
282
+ }
283
+ },
284
+ {
285
+ id: "ellipse",
286
+ label: "Ellipse",
287
+ build(page) {
288
+ const size = Math.min(220, Math.round(Math.min(page.width, page.height) * 0.3));
289
+ const { x, y } = centeredPosition(size, size, page.width, page.height);
290
+ const element = {
291
+ ...baseAttrs("Ellipse", x, y),
292
+ kind: "ellipse",
293
+ width: size,
294
+ height: size,
295
+ fill: "#10b981"
296
+ };
297
+ return [addElementOp(page.pageId, element)];
298
+ }
299
+ }
300
+ ];
301
+
302
+ // src/design-canvas-react/components/CanvasInsertPanel.tsx
303
+ import { useEffect, useRef, useState } from "react";
304
+ import { jsx, jsxs } from "react/jsx-runtime";
305
+ var DEFAULT_ACCEPT = "image/png,image/jpeg,image/gif,image/webp";
306
+ var PROBE_TIMEOUT_MS = 4e3;
307
+ function probeImageSize(url) {
308
+ return new Promise((resolve) => {
309
+ if (typeof Image === "undefined") return resolve({ width: 0, height: 0 });
310
+ let settled = false;
311
+ const finish = (size) => {
312
+ if (settled) return;
313
+ settled = true;
314
+ resolve(size);
315
+ };
316
+ const img = new Image();
317
+ img.onload = () => finish({ width: img.naturalWidth, height: img.naturalHeight });
318
+ img.onerror = () => finish({ width: 0, height: 0 });
319
+ setTimeout(() => finish({ width: 0, height: 0 }), PROBE_TIMEOUT_MS);
320
+ img.src = url;
321
+ });
322
+ }
323
+ function UploadGlyph({ className }) {
324
+ return /* @__PURE__ */ jsx("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 9l5-5 5 5M12 4v12" }) });
325
+ }
326
+ function SparkleGlyph({ className }) {
327
+ return /* @__PURE__ */ jsx("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M12 3v3m0 12v3M3 12h3m12 0h3M5.6 5.6l2.1 2.1m8.6 8.6 2.1 2.1m0-12.8-2.1 2.1M7.7 16.3l-2.1 2.1" }) });
328
+ }
329
+ function SpinnerGlyph({ className }) {
330
+ return /* @__PURE__ */ jsx("svg", { className: `${className ?? ""} animate-spin`, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.2-8.6", strokeLinecap: "round" }) });
331
+ }
332
+ function CanvasInsertPanel({
333
+ canWrite,
334
+ page,
335
+ onInsert,
336
+ onUploadImage,
337
+ loadGenerations,
338
+ templates = DEFAULT_INSERT_TEMPLATES,
339
+ accept = DEFAULT_ACCEPT,
340
+ className
341
+ }) {
342
+ const [tab, setTab] = useState("uploads");
343
+ const [busy, setBusy] = useState(false);
344
+ const [error, setError] = useState("");
345
+ const [dragOver, setDragOver] = useState(false);
346
+ const [generations, setGenerations] = useState([]);
347
+ const [generationsLoaded, setGenerationsLoaded] = useState(false);
348
+ const fileInputRef = useRef(null);
349
+ useEffect(() => {
350
+ if (tab !== "generations" || generationsLoaded || !loadGenerations) return;
351
+ let cancelled = false;
352
+ void (async () => {
353
+ try {
354
+ const rows = await loadGenerations();
355
+ if (!cancelled) setGenerations(rows.filter((g) => g.url));
356
+ } catch (err) {
357
+ if (!cancelled) setError(err instanceof Error ? err.message : "Could not load generations");
358
+ } finally {
359
+ if (!cancelled) setGenerationsLoaded(true);
360
+ }
361
+ })();
362
+ return () => {
363
+ cancelled = true;
364
+ };
365
+ }, [tab, generationsLoaded, loadGenerations]);
366
+ async function insertImageFromUrl(url) {
367
+ assertSceneMediaSrc(url, "image src");
368
+ const natural = await probeImageSize(url);
369
+ await onInsert([buildInsertImageOp(url, natural, page)]);
370
+ }
371
+ async function handleFiles(files) {
372
+ if (!canWrite || busy) return;
373
+ const list = Array.from(files).filter((f) => f.type.startsWith("image/"));
374
+ if (list.length === 0) {
375
+ setError("Only image files can be added to the canvas");
376
+ return;
377
+ }
378
+ setBusy(true);
379
+ setError("");
380
+ try {
381
+ for (const file of list) {
382
+ const url = await onUploadImage(file);
383
+ await insertImageFromUrl(url);
384
+ }
385
+ } catch (err) {
386
+ setError(err instanceof Error ? err.message : "Upload failed");
387
+ } finally {
388
+ setBusy(false);
389
+ }
390
+ }
391
+ async function runInsert(build) {
392
+ if (!canWrite || busy) return;
393
+ setBusy(true);
394
+ setError("");
395
+ try {
396
+ await onInsert(await build());
397
+ } catch (err) {
398
+ setError(err instanceof Error ? err.message : "Insert failed");
399
+ } finally {
400
+ setBusy(false);
401
+ }
402
+ }
403
+ const tabs = [
404
+ { id: "uploads", label: "Uploads", icon: ImageGlyph, show: true },
405
+ { id: "templates", label: "Templates", icon: TextGlyph, show: templates.length > 0 },
406
+ { id: "generations", label: "Generations", icon: SparkleGlyph, show: !!loadGenerations }
407
+ ];
408
+ const visibleTabs = tabs.filter((t) => t.show);
409
+ return /* @__PURE__ */ jsxs("div", { className: `flex h-full min-h-0 flex-col bg-[var(--bg-input)] text-[var(--text-primary)] ${className ?? ""}`, children: [
410
+ /* @__PURE__ */ jsx("div", { className: "flex shrink-0 border-b border-[var(--border-default)]", children: visibleTabs.map(({ id, label, icon: Icon }) => /* @__PURE__ */ jsxs(
411
+ "button",
412
+ {
413
+ type: "button",
414
+ onClick: () => setTab(id),
415
+ className: `flex flex-1 items-center justify-center gap-1.5 px-2 py-2.5 text-xs font-medium transition-colors ${tab === id ? "border-b-2 border-[var(--brand-primary)] text-[var(--text-primary)]" : "text-[var(--text-muted)] hover:text-[var(--text-primary)]"}`,
416
+ children: [
417
+ /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
418
+ label
419
+ ]
420
+ },
421
+ id
422
+ )) }),
423
+ !canWrite ? /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center p-4 text-center text-sm text-[var(--text-muted)]", children: "You have view-only access to this design." }) : /* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto p-3", children: [
424
+ tab === "uploads" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
425
+ /* @__PURE__ */ jsxs(
426
+ "button",
427
+ {
428
+ type: "button",
429
+ disabled: busy,
430
+ onClick: () => fileInputRef.current?.click(),
431
+ onDragOver: (e) => {
432
+ e.preventDefault();
433
+ setDragOver(true);
434
+ },
435
+ onDragLeave: () => setDragOver(false),
436
+ onDrop: (e) => {
437
+ e.preventDefault();
438
+ setDragOver(false);
439
+ if (e.dataTransfer?.files?.length) void handleFiles(e.dataTransfer.files);
440
+ },
441
+ className: `flex flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed px-4 py-8 text-center transition-colors disabled:opacity-50 ${dragOver ? "border-[var(--brand-primary)] bg-[var(--brand-primary)]/5" : "border-[var(--border-default)] hover:border-[var(--brand-primary)]/40"}`,
442
+ children: [
443
+ busy ? /* @__PURE__ */ jsx(SpinnerGlyph, { className: "h-6 w-6 text-[var(--brand-primary)]" }) : /* @__PURE__ */ jsx(UploadGlyph, { className: "h-6 w-6 text-[var(--text-muted)]" }),
444
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--text-primary)]", children: busy ? "Uploading\u2026" : "Drop an image or click to upload" }),
445
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--text-muted)]", children: "PNG, JPEG, GIF, or WebP" })
446
+ ]
447
+ }
448
+ ),
449
+ /* @__PURE__ */ jsx(
450
+ "input",
451
+ {
452
+ ref: fileInputRef,
453
+ type: "file",
454
+ accept,
455
+ multiple: true,
456
+ className: "hidden",
457
+ onChange: (e) => {
458
+ if (e.target.files?.length) void handleFiles(e.target.files);
459
+ e.target.value = "";
460
+ }
461
+ }
462
+ )
463
+ ] }),
464
+ tab === "templates" && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2", children: templates.map((tpl) => /* @__PURE__ */ jsx(
465
+ "button",
466
+ {
467
+ type: "button",
468
+ disabled: busy,
469
+ onClick: () => void runInsert(() => tpl.build(page)),
470
+ className: "flex h-20 flex-col items-center justify-center gap-1.5 rounded-md border border-[var(--border-default)] bg-[var(--bg-input)] text-xs font-medium text-[var(--text-primary)] transition-colors hover:border-[var(--brand-primary)]/40 disabled:opacity-50",
471
+ children: /* @__PURE__ */ jsx("span", { children: tpl.label })
472
+ },
473
+ tpl.id
474
+ )) }),
475
+ tab === "generations" && /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: !generationsLoaded ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-8 text-[var(--text-muted)]", children: /* @__PURE__ */ jsx(SpinnerGlyph, { className: "h-5 w-5" }) }) : generations.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-1 py-6 text-center text-sm text-[var(--text-muted)]", children: "No generated images yet. Ask the agent to generate one." }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2", children: generations.map((gen) => /* @__PURE__ */ jsx(
476
+ "button",
477
+ {
478
+ type: "button",
479
+ disabled: busy,
480
+ onClick: () => {
481
+ if (busy || !canWrite) return;
482
+ setBusy(true);
483
+ setError("");
484
+ insertImageFromUrl(gen.url).catch((err) => setError(err instanceof Error ? err.message : "Insert failed")).finally(() => setBusy(false));
485
+ },
486
+ title: gen.label,
487
+ className: "group relative aspect-square overflow-hidden rounded-md border border-[var(--border-default)] bg-[var(--bg-input)] transition-colors hover:border-[var(--brand-primary)] disabled:opacity-50",
488
+ children: /* @__PURE__ */ jsx("img", { src: gen.url, alt: gen.label || "Generated image", className: "h-full w-full object-cover" })
489
+ },
490
+ gen.id
491
+ )) }) }),
492
+ error ? /* @__PURE__ */ jsx("p", { className: "mt-3 text-xs leading-5 text-[var(--text-danger,#dc2626)]", children: error }) : null
493
+ ] })
494
+ ] });
495
+ }
496
+
178
497
  // src/design-canvas-react/components/LayersPanel.tsx
179
- import { useMemo, useRef, useState } from "react";
498
+ import { useMemo, useRef as useRef2, useState as useState2 } from "react";
180
499
 
181
500
  // src/design-canvas-react/components/layer-tree.ts
182
501
  function flattenLayerTree(page) {
@@ -203,32 +522,32 @@ function flattenLayerTree(page) {
203
522
  var LAYERS_PANEL_ROW_LIMIT = 500;
204
523
 
205
524
  // src/design-canvas-react/components/LayersPanel.tsx
206
- import { jsx, jsxs } from "react/jsx-runtime";
525
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
207
526
  function KindIcon({ kind, className }) {
208
527
  switch (kind) {
209
528
  case "rect":
210
- return /* @__PURE__ */ jsx(RectGlyph, { className });
529
+ return /* @__PURE__ */ jsx2(RectGlyph, { className });
211
530
  case "ellipse":
212
- return /* @__PURE__ */ jsx(EllipseGlyph, { className });
531
+ return /* @__PURE__ */ jsx2(EllipseGlyph, { className });
213
532
  case "line":
214
- return /* @__PURE__ */ jsx(LineGlyph, { className });
533
+ return /* @__PURE__ */ jsx2(LineGlyph, { className });
215
534
  case "text":
216
- return /* @__PURE__ */ jsx(TextGlyph, { className });
535
+ return /* @__PURE__ */ jsx2(TextGlyph, { className });
217
536
  case "image":
218
- return /* @__PURE__ */ jsx(ImageGlyph, { className });
537
+ return /* @__PURE__ */ jsx2(ImageGlyph, { className });
219
538
  case "video":
220
- return /* @__PURE__ */ jsx(VideoGlyph, { className });
539
+ return /* @__PURE__ */ jsx2(VideoGlyph, { className });
221
540
  case "group":
222
- return /* @__PURE__ */ jsx(GroupGlyph, { className });
541
+ return /* @__PURE__ */ jsx2(GroupGlyph, { className });
223
542
  }
224
543
  }
225
544
  var INDENT_PX = 16;
226
545
  function LayersPanel({ page, selectedElementIds, canWrite, onSetAttrs, onReorder, onSelect }) {
227
546
  const rows = useMemo(() => flattenLayerTree(page), [page]);
228
- const [renamingId, setRenamingId] = useState(null);
229
- const [renameValue, setRenameValue] = useState("");
230
- const dragRowRef = useRef(null);
231
- const [dragOverIndex, setDragOverIndex] = useState(null);
547
+ const [renamingId, setRenamingId] = useState2(null);
548
+ const [renameValue, setRenameValue] = useState2("");
549
+ const dragRowRef = useRef2(null);
550
+ const [dragOverIndex, setDragOverIndex] = useState2(null);
232
551
  function startRename(element) {
233
552
  setRenamingId(element.id);
234
553
  setRenameValue(element.name);
@@ -239,14 +558,14 @@ function LayersPanel({ page, selectedElementIds, canWrite, onSetAttrs, onReorder
239
558
  setRenamingId(null);
240
559
  }
241
560
  const visible = rows.length > LAYERS_PANEL_ROW_LIMIT ? rows.slice(0, LAYERS_PANEL_ROW_LIMIT) : rows;
242
- return /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col overflow-hidden text-[var(--text-primary)]", children: [
243
- /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b border-[var(--border-default)] px-3 py-1.5 text-[11px] font-medium uppercase tracking-wide text-[var(--text-muted)]", children: "Layers" }),
244
- /* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [
561
+ return /* @__PURE__ */ jsxs2("div", { className: "flex h-full flex-col overflow-hidden text-[var(--text-primary)]", children: [
562
+ /* @__PURE__ */ jsx2("div", { className: "shrink-0 border-b border-[var(--border-default)] px-3 py-1.5 text-[11px] font-medium uppercase tracking-wide text-[var(--text-muted)]", children: "Layers" }),
563
+ /* @__PURE__ */ jsxs2("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [
245
564
  visible.map((row) => {
246
565
  const { element } = row;
247
566
  const isSelected = selectedElementIds.includes(element.id);
248
567
  const isRenaming = renamingId === element.id;
249
- return /* @__PURE__ */ jsxs(
568
+ return /* @__PURE__ */ jsxs2(
250
569
  "div",
251
570
  {
252
571
  "data-layer-row": element.id,
@@ -285,9 +604,9 @@ function LayersPanel({ page, selectedElementIds, canWrite, onSetAttrs, onReorder
285
604
  ].join(" "),
286
605
  style: { paddingLeft: 8 + row.depth * INDENT_PX },
287
606
  children: [
288
- /* @__PURE__ */ jsx(KindIcon, { kind: element.kind, className: "h-3.5 w-3.5 shrink-0 opacity-60" }),
289
- element.slot ? /* @__PURE__ */ jsx(SlotGlyph, { className: "h-3 w-3 shrink-0 text-[var(--brand-primary)]" }) : null,
290
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: isRenaming ? /* @__PURE__ */ jsx(
607
+ /* @__PURE__ */ jsx2(KindIcon, { kind: element.kind, className: "h-3.5 w-3.5 shrink-0 opacity-60" }),
608
+ element.slot ? /* @__PURE__ */ jsx2(SlotGlyph, { className: "h-3 w-3 shrink-0 text-[var(--brand-primary)]" }) : null,
609
+ /* @__PURE__ */ jsx2("span", { className: "min-w-0 flex-1 truncate", children: isRenaming ? /* @__PURE__ */ jsx2(
291
610
  "input",
292
611
  {
293
612
  autoFocus: true,
@@ -302,7 +621,7 @@ function LayersPanel({ page, selectedElementIds, canWrite, onSetAttrs, onReorder
302
621
  onClick: (event) => event.stopPropagation(),
303
622
  className: "w-full rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-1 py-0 text-[13px] text-[var(--text-primary)] outline-none focus:border-[var(--brand-primary)]"
304
623
  }
305
- ) : /* @__PURE__ */ jsx(
624
+ ) : /* @__PURE__ */ jsx2(
306
625
  "span",
307
626
  {
308
627
  onDoubleClick: (event) => {
@@ -313,7 +632,7 @@ function LayersPanel({ page, selectedElementIds, canWrite, onSetAttrs, onReorder
313
632
  children: element.name
314
633
  }
315
634
  ) }),
316
- /* @__PURE__ */ jsx(
635
+ /* @__PURE__ */ jsx2(
317
636
  "button",
318
637
  {
319
638
  type: "button",
@@ -324,10 +643,10 @@ function LayersPanel({ page, selectedElementIds, canWrite, onSetAttrs, onReorder
324
643
  },
325
644
  disabled: !canWrite,
326
645
  className: "shrink-0 rounded p-0.5 opacity-0 transition-opacity hover:opacity-100 focus:opacity-100 group-hover:opacity-100 [.flex:hover_&]:opacity-60",
327
- children: element.visible ? /* @__PURE__ */ jsx(EyeGlyph, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(EyeOffGlyph, { className: "h-3.5 w-3.5 text-[var(--text-muted)]" })
646
+ children: element.visible ? /* @__PURE__ */ jsx2(EyeGlyph, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx2(EyeOffGlyph, { className: "h-3.5 w-3.5 text-[var(--text-muted)]" })
328
647
  }
329
648
  ),
330
- /* @__PURE__ */ jsx(
649
+ /* @__PURE__ */ jsx2(
331
650
  "button",
332
651
  {
333
652
  type: "button",
@@ -338,7 +657,7 @@ function LayersPanel({ page, selectedElementIds, canWrite, onSetAttrs, onReorder
338
657
  },
339
658
  disabled: !canWrite,
340
659
  className: "shrink-0 rounded p-0.5 opacity-0 transition-opacity hover:opacity-100 focus:opacity-100 [.flex:hover_&]:opacity-60",
341
- children: element.locked ? /* @__PURE__ */ jsx(LockGlyph, { className: "h-3.5 w-3.5 text-amber-400" }) : /* @__PURE__ */ jsx(UnlockGlyph, { className: "h-3.5 w-3.5" })
660
+ children: element.locked ? /* @__PURE__ */ jsx2(LockGlyph, { className: "h-3.5 w-3.5 text-amber-400" }) : /* @__PURE__ */ jsx2(UnlockGlyph, { className: "h-3.5 w-3.5" })
342
661
  }
343
662
  )
344
663
  ]
@@ -346,7 +665,7 @@ function LayersPanel({ page, selectedElementIds, canWrite, onSetAttrs, onReorder
346
665
  element.id
347
666
  );
348
667
  }),
349
- rows.length > LAYERS_PANEL_ROW_LIMIT ? /* @__PURE__ */ jsxs("div", { className: "px-3 py-2 text-[11px] text-[var(--text-muted)]", children: [
668
+ rows.length > LAYERS_PANEL_ROW_LIMIT ? /* @__PURE__ */ jsxs2("div", { className: "px-3 py-2 text-[11px] text-[var(--text-muted)]", children: [
350
669
  "+",
351
670
  rows.length - LAYERS_PANEL_ROW_LIMIT,
352
671
  " more elements \u2014 select a group to scope the list"
@@ -364,6 +683,8 @@ var DesignCanvasChromeLazy = lazy(
364
683
  () => import("../DesignCanvas-JTSAL6KX.js").then((m) => ({ default: m.DesignCanvas }))
365
684
  );
366
685
  export {
686
+ CanvasInsertPanel,
687
+ DEFAULT_INSERT_TEMPLATES,
367
688
  DUPLICATE_OFFSET,
368
689
  DesignCanvas,
369
690
  DesignCanvasChromeLazy,
@@ -373,6 +694,7 @@ export {
373
694
  GridLayer,
374
695
  LAYERS_PANEL_ROW_LIMIT,
375
696
  LayersPanel,
697
+ MAX_INSERT_DIMENSION,
376
698
  PagesStrip,
377
699
  Rulers,
378
700
  SCENE_COMMAND_HISTORY_LIMIT,
@@ -388,7 +710,9 @@ export {
388
710
  bakeTextTransform,
389
711
  bindSlotCommand,
390
712
  bleedAwareExportBounds,
713
+ buildInsertImageOp,
391
714
  buildRulerTicks,
715
+ centeredPosition,
392
716
  collectGridTargets,
393
717
  createSceneCommandStack,
394
718
  createSnapEngine,
@@ -400,6 +724,7 @@ export {
400
724
  duplicatePageCommand,
401
725
  exportDocumentJson,
402
726
  exportPageDataUrl,
727
+ fittedSize,
403
728
  flattenLayerTree,
404
729
  formatRulerLabel,
405
730
  groupElementsCommand,
@@ -407,6 +732,7 @@ export {
407
732
  isCrossOriginSrc,
408
733
  isExportHiddenNodeName,
409
734
  marqueeSelect,
735
+ mintElementId,
410
736
  multiSetAttrsCommand,
411
737
  nudgeDelta,
412
738
  reorderElementCommand,