@orangecatai/adgen-canvas 0.0.18 → 0.0.19

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 (37) hide show
  1. package/dist/dev/{chunk-IFMURN5W.js → chunk-4K5LIQQU.js} +2 -1
  2. package/dist/dev/chunk-4K5LIQQU.js.map +7 -0
  3. package/dist/dev/{chunk-V5POPSPR.js → chunk-JFIHKE5E.js} +3 -3
  4. package/dist/dev/{chunk-MCENPNB7.js → chunk-SFB47GDQ.js} +2 -2
  5. package/dist/dev/data/{image-5TNF6BOW.js → image-OXVYDFFE.js} +3 -3
  6. package/dist/dev/index.css +5 -0
  7. package/dist/dev/index.css.map +2 -2
  8. package/dist/dev/index.js +554 -75
  9. package/dist/dev/index.js.map +3 -3
  10. package/dist/dev/locales/{en-ZZQASRYX.js → en-IMRJ2ODG.js} +2 -2
  11. package/dist/dev/subset-shared.chunk.js +1 -1
  12. package/dist/dev/subset-worker.chunk.js +1 -1
  13. package/dist/prod/{chunk-TDDASSRC.js → chunk-5GYFLSBF.js} +2 -2
  14. package/dist/prod/{chunk-OH4MQBSI.js → chunk-7XQHY7WT.js} +1 -1
  15. package/dist/prod/{chunk-MSBJ7C4T.js → chunk-TX6PVFUX.js} +3 -3
  16. package/dist/prod/data/image-FRJAQJ62.js +1 -0
  17. package/dist/prod/index.css +1 -1
  18. package/dist/prod/index.js +84 -65
  19. package/dist/prod/locales/{en-RQF5X6GM.js → en-5WIO63S4.js} +1 -1
  20. package/dist/prod/subset-shared.chunk.js +1 -1
  21. package/dist/prod/subset-worker.chunk.js +1 -1
  22. package/dist/types/common/src/keys.d.ts +2 -0
  23. package/dist/types/element/src/frame.d.ts +3 -3
  24. package/dist/types/excalidraw/actions/actionFrame.d.ts +178 -0
  25. package/dist/types/excalidraw/actions/shortcuts.d.ts +1 -1
  26. package/dist/types/excalidraw/actions/types.d.ts +1 -1
  27. package/dist/types/excalidraw/components/AIChatPanel.d.ts +1 -1
  28. package/dist/types/excalidraw/components/ImageGeneratorPanel.d.ts +3 -3
  29. package/dist/types/excalidraw/components/auto-resize/AutoResizeShimmerLayer.d.ts +4 -0
  30. package/dist/types/excalidraw/components/auto-resize/autoResizeEngine.d.ts +47 -0
  31. package/package.json +3 -3
  32. package/dist/dev/chunk-IFMURN5W.js.map +0 -7
  33. package/dist/prod/data/image-3TR7TIWR.js +0 -1
  34. /package/dist/dev/{chunk-V5POPSPR.js.map → chunk-JFIHKE5E.js.map} +0 -0
  35. /package/dist/dev/{chunk-MCENPNB7.js.map → chunk-SFB47GDQ.js.map} +0 -0
  36. /package/dist/dev/data/{image-5TNF6BOW.js.map → image-OXVYDFFE.js.map} +0 -0
  37. /package/dist/dev/locales/{en-ZZQASRYX.js.map → en-IMRJ2ODG.js.map} +0 -0
package/dist/dev/index.js CHANGED
@@ -67,13 +67,13 @@ import {
67
67
  serializeAsJSON,
68
68
  serializeLibraryAsJSON,
69
69
  strokeRectWithRotation_simple
70
- } from "./chunk-V5POPSPR.js";
70
+ } from "./chunk-JFIHKE5E.js";
71
71
  import {
72
72
  define_import_meta_env_default
73
- } from "./chunk-MCENPNB7.js";
73
+ } from "./chunk-SFB47GDQ.js";
74
74
  import {
75
75
  en_default
76
- } from "./chunk-IFMURN5W.js";
76
+ } from "./chunk-4K5LIQQU.js";
77
77
  import {
78
78
  percentages_default
79
79
  } from "./chunk-URPEKBQ3.js";
@@ -390,7 +390,7 @@ var globImport_locales_json = __glob({
390
390
  "./locales/de-CH.json": () => import("./locales/de-CH-GCXOD4LK.js"),
391
391
  "./locales/de-DE.json": () => import("./locales/de-DE-CGDBECYD.js"),
392
392
  "./locales/el-GR.json": () => import("./locales/el-GR-G5QZC24A.js"),
393
- "./locales/en.json": () => import("./locales/en-ZZQASRYX.js"),
393
+ "./locales/en.json": () => import("./locales/en-IMRJ2ODG.js"),
394
394
  "./locales/es-ES.json": () => import("./locales/es-ES-UMEOH76W.js"),
395
395
  "./locales/eu-ES.json": () => import("./locales/eu-ES-IWRZXJC5.js"),
396
396
  "./locales/fa-IR.json": () => import("./locales/fa-IR-QOYVIIJA.js"),
@@ -5553,6 +5553,7 @@ var FontPickerList = React10.memo(
5553
5553
  }).sort(
5554
5554
  (a, b) => a.text.toLowerCase() > b.text.toLowerCase() ? 1 : -1
5555
5555
  ),
5556
+ // eslint-disable-next-line react-hooks/exhaustive-deps
5556
5557
  [customFontVersion]
5557
5558
  );
5558
5559
  const sceneFamilies = useMemo(
@@ -9770,7 +9771,7 @@ var exportCanvas = async (type, elements, appState, files, {
9770
9771
  let blob = canvasToBlob(tempCanvas);
9771
9772
  if (appState.exportEmbedScene) {
9772
9773
  blob = blob.then(
9773
- (blob2) => import("./data/image-5TNF6BOW.js").then(
9774
+ (blob2) => import("./data/image-OXVYDFFE.js").then(
9774
9775
  ({ encodePngMetadata: encodePngMetadata2 }) => encodePngMetadata2({
9775
9776
  blob: blob2,
9776
9777
  metadata: serializeAsJSON(elements, appState, files, "local")
@@ -13339,6 +13340,7 @@ var shortcutMap = {
13339
13340
  toggleEraserTool: [getShortcutKey("E")],
13340
13341
  toggleHandTool: [getShortcutKey("H")],
13341
13342
  setFrameAsActiveTool: [getShortcutKey("F")],
13343
+ cycleThroughFrameChildren: [getShortcutKey("CtrlOrCmd+J")],
13342
13344
  saveFileToDisk: [getShortcutKey("CtrlOrCmd+S")],
13343
13345
  saveToActiveFile: [getShortcutKey("CtrlOrCmd+S")],
13344
13346
  toggleShortcuts: [getShortcutKey("?")],
@@ -20553,6 +20555,103 @@ var actionSetFrameAsActiveTool = register({
20553
20555
  },
20554
20556
  keyTest: (event) => !event[KEYS41.CTRL_OR_CMD] && !event.shiftKey && !event.altKey && event.key.toLocaleLowerCase() === KEYS41.F
20555
20557
  });
20558
+ var actionCycleThroughFrameChildren = register({
20559
+ name: "cycleThroughFrameChildren",
20560
+ label: "labels.cycleThroughFrameChildren",
20561
+ trackEvent: { category: "element" },
20562
+ predicate: (elements, appState, _, app) => {
20563
+ const selectedElements = app.scene.getSelectedElements(appState);
20564
+ if (selectedElements.length === 1) {
20565
+ const selected = selectedElements[0];
20566
+ return isFrameLikeElement8(selected) || Boolean(selected.frameId);
20567
+ }
20568
+ return false;
20569
+ },
20570
+ perform: (elements, appState, _, app) => {
20571
+ const selectedElement = app.scene.getSelectedElements(appState).at(0);
20572
+ if (!selectedElement) {
20573
+ return {
20574
+ elements,
20575
+ appState,
20576
+ captureUpdate: CaptureUpdateAction32.EVENTUALLY
20577
+ };
20578
+ }
20579
+ let frameId = null;
20580
+ let currentlySelectedId = null;
20581
+ if (isFrameLikeElement8(selectedElement)) {
20582
+ frameId = selectedElement.id;
20583
+ } else if (selectedElement.frameId) {
20584
+ frameId = selectedElement.frameId;
20585
+ currentlySelectedId = selectedElement.id;
20586
+ }
20587
+ if (!frameId) {
20588
+ return {
20589
+ elements,
20590
+ appState,
20591
+ captureUpdate: CaptureUpdateAction32.EVENTUALLY
20592
+ };
20593
+ }
20594
+ const frameChildren = getFrameChildren3(
20595
+ getNonDeletedElements12(elements),
20596
+ frameId
20597
+ ).filter((element) => !(element.type === "text" && element.containerId));
20598
+ if (frameChildren.length === 0) {
20599
+ if (!isFrameLikeElement8(selectedElement)) {
20600
+ const frame = elements.find((el) => el.id === frameId);
20601
+ if (frame) {
20602
+ return {
20603
+ elements,
20604
+ appState: {
20605
+ ...appState,
20606
+ selectedElementIds: { [frame.id]: true }
20607
+ },
20608
+ captureUpdate: CaptureUpdateAction32.IMMEDIATELY
20609
+ };
20610
+ }
20611
+ }
20612
+ return {
20613
+ elements,
20614
+ appState,
20615
+ captureUpdate: CaptureUpdateAction32.EVENTUALLY
20616
+ };
20617
+ }
20618
+ let nextElementToSelect;
20619
+ if (isFrameLikeElement8(selectedElement)) {
20620
+ nextElementToSelect = frameChildren[0];
20621
+ } else if (currentlySelectedId) {
20622
+ const currentIndex = frameChildren.findIndex(
20623
+ (el) => el.id === currentlySelectedId
20624
+ );
20625
+ if (currentIndex >= 0 && currentIndex < frameChildren.length - 1) {
20626
+ nextElementToSelect = frameChildren[currentIndex + 1];
20627
+ } else {
20628
+ const frame = elements.find((el) => el.id === frameId);
20629
+ if (frame) {
20630
+ return {
20631
+ elements,
20632
+ appState: {
20633
+ ...appState,
20634
+ selectedElementIds: { [frame.id]: true }
20635
+ },
20636
+ captureUpdate: CaptureUpdateAction32.IMMEDIATELY
20637
+ };
20638
+ }
20639
+ nextElementToSelect = frameChildren[0];
20640
+ }
20641
+ } else {
20642
+ nextElementToSelect = frameChildren[0];
20643
+ }
20644
+ return {
20645
+ elements,
20646
+ appState: {
20647
+ ...appState,
20648
+ selectedElementIds: { [nextElementToSelect.id]: true }
20649
+ },
20650
+ captureUpdate: CaptureUpdateAction32.IMMEDIATELY
20651
+ };
20652
+ },
20653
+ keyTest: (event) => event[KEYS41.CTRL_OR_CMD] && !event.shiftKey && !event.altKey && event.key === KEYS41.J
20654
+ });
20556
20655
  var actionWrapSelectionInFrame = register({
20557
20656
  name: "wrapSelectionInFrame",
20558
20657
  label: "labels.wrapSelectionInFrame",
@@ -20584,8 +20683,17 @@ var actionWrapSelectionInFrame = register({
20584
20683
  });
20585
20684
  }
20586
20685
  }
20686
+ const allElements = app.scene.getElementsIncludingDeleted();
20687
+ const firstSelectedIndex = allElements.findIndex(
20688
+ (el) => selectedElements.some((sel) => sel.id === el.id)
20689
+ );
20690
+ const elementsWithFrame = firstSelectedIndex >= 0 ? [
20691
+ ...allElements.slice(0, firstSelectedIndex),
20692
+ frame,
20693
+ ...allElements.slice(firstSelectedIndex)
20694
+ ] : [...allElements, frame];
20587
20695
  const nextElements = addElementsToFrame(
20588
- [...app.scene.getElementsIncludingDeleted(), frame],
20696
+ elementsWithFrame,
20589
20697
  selectedElements,
20590
20698
  frame,
20591
20699
  appState
@@ -22892,6 +23000,7 @@ var autoResizeStore = {
22892
23000
  import { Fragment as Fragment12, jsx as jsx84 } from "react/jsx-runtime";
22893
23001
  var AutoResizeShimmerLayer = () => {
22894
23002
  const appState = useExcalidrawAppState();
23003
+ const sceneElements = useExcalidrawElements();
22895
23004
  const [, forceUpdate] = useReducer((n) => n + 1, 0);
22896
23005
  useEffect30(() => autoResizeStore.subscribe(forceUpdate), []);
22897
23006
  const frames = autoResizeStore.getLoadingFrames();
@@ -22899,15 +23008,21 @@ var AutoResizeShimmerLayer = () => {
22899
23008
  return null;
22900
23009
  }
22901
23010
  const zoomVal = appState.zoom.value;
22902
- return /* @__PURE__ */ jsx84(Fragment12, { children: frames.map((frame) => {
23011
+ return /* @__PURE__ */ jsx84(Fragment12, { children: frames.map((frameInfo) => {
23012
+ const currentFrame = sceneElements.find(
23013
+ (el) => el.id === frameInfo.frameId && el.type === "frame"
23014
+ );
23015
+ if (!currentFrame) {
23016
+ return null;
23017
+ }
22903
23018
  const { x: viewportX, y: viewportY } = sceneCoordsToViewportCoords5(
22904
- { sceneX: frame.x, sceneY: frame.y },
23019
+ { sceneX: currentFrame.x, sceneY: currentFrame.y },
22905
23020
  appState
22906
23021
  );
22907
23022
  const left = viewportX - appState.offsetLeft;
22908
23023
  const top = viewportY - appState.offsetTop;
22909
- const width = frame.width * zoomVal;
22910
- const height = frame.height * zoomVal;
23024
+ const width = currentFrame.width * zoomVal;
23025
+ const height = currentFrame.height * zoomVal;
22911
23026
  return /* @__PURE__ */ jsx84(
22912
23027
  "div",
22913
23028
  {
@@ -22924,7 +23039,7 @@ var AutoResizeShimmerLayer = () => {
22924
23039
  boxSizing: "border-box"
22925
23040
  }
22926
23041
  },
22927
- frame.frameId
23042
+ frameInfo.frameId
22928
23043
  );
22929
23044
  }) });
22930
23045
  };
@@ -23232,7 +23347,7 @@ function PromptInputAction({
23232
23347
  import { jsx as jsx88, jsxs as jsxs47 } from "react/jsx-runtime";
23233
23348
  var MODEL_CONFIG = {
23234
23349
  "Gemini 2.5 Flash": {
23235
- geminiModelId: "gemini-3.1-flash-image-preview",
23350
+ geminiModelId: "gemini-3.1-flash-image",
23236
23351
  supportsImageSize: false,
23237
23352
  supportsThinking: false,
23238
23353
  supportedResolutions: ["1K"],
@@ -23250,7 +23365,7 @@ var MODEL_CONFIG = {
23250
23365
  ]
23251
23366
  },
23252
23367
  "Gemini 3.1 Flash": {
23253
- geminiModelId: "gemini-3.1-flash-image-preview",
23368
+ geminiModelId: "gemini-3.1-flash-image",
23254
23369
  supportsImageSize: true,
23255
23370
  supportsThinking: true,
23256
23371
  supportedResolutions: ["0.5K", "1K", "2K", "4K"],
@@ -23272,7 +23387,7 @@ var MODEL_CONFIG = {
23272
23387
  ]
23273
23388
  },
23274
23389
  "Gemini 3 Pro": {
23275
- geminiModelId: "gemini-3-pro-image-preview",
23390
+ geminiModelId: "gemini-3-pro-image",
23276
23391
  supportsImageSize: true,
23277
23392
  supportsThinking: false,
23278
23393
  supportedResolutions: ["1K", "2K", "4K"],
@@ -24687,13 +24802,13 @@ async function runReviewerAgent(opts) {
24687
24802
  }
24688
24803
 
24689
24804
  // components/auto-resize/autoResizeEngine.ts
24690
- var DEBUG_AUTO_RESIZE = false;
24805
+ var DEBUG_AUTO_RESIZE = true;
24691
24806
  var debug = (...args) => {
24692
24807
  if (DEBUG_AUTO_RESIZE) {
24693
24808
  console.log(...args);
24694
24809
  }
24695
24810
  };
24696
- var DEFAULT_BG_REGEN_MODEL = "gemini-3.1-flash-image-preview";
24811
+ var DEFAULT_BG_REGEN_MODEL = "gemini-3.1-flash-image";
24697
24812
  var MAX_REVIEW_ROUNDS = 2;
24698
24813
  var GEMINI_RATIOS = {
24699
24814
  "21:9": 21 / 9,
@@ -24739,6 +24854,9 @@ function extractFrameInfo(elements, frame, files) {
24739
24854
  };
24740
24855
  continue;
24741
24856
  }
24857
+ console.warn(
24858
+ `[AutoResize] extractFrameInfo: full-frame image element has no file data (fileId=${el.fileId}). Will fall back to frame.backgroundColor.`
24859
+ );
24742
24860
  } else if (el.type === "rectangle" && el.backgroundColor && el.backgroundColor !== "transparent") {
24743
24861
  background = {
24744
24862
  type: "solid",
@@ -24798,6 +24916,16 @@ function extractFrameInfo(elements, frame, files) {
24798
24916
  }
24799
24917
  }
24800
24918
  }
24919
+ if (background.type === "none") {
24920
+ const fc = frame.backgroundColor;
24921
+ const isBlankOrWhite = !fc || fc === "transparent" || fc === "#ffffff" || fc === "#ffffffff" || fc === "#fff" || fc === "#FFFFFF";
24922
+ if (!isBlankOrWhite) {
24923
+ background = { type: "solid", color: fc };
24924
+ debug(
24925
+ `[AutoResize] extractFrameInfo: using frame.backgroundColor as fallback background: ${fc}`
24926
+ );
24927
+ }
24928
+ }
24801
24929
  return { frame, background, foreground };
24802
24930
  }
24803
24931
  function computeCropLoss(sourceAR, targetAR) {
@@ -24830,6 +24958,115 @@ function nearestGeminiAspectRatio(w, h) {
24830
24958
  }
24831
24959
  return best;
24832
24960
  }
24961
+ var GEMINI_RESOLUTION_TABLE = {
24962
+ "1:1": {
24963
+ "0.5K": [512, 512],
24964
+ "1K": [1024, 1024],
24965
+ "2K": [2048, 2048],
24966
+ "4K": [4096, 4096]
24967
+ },
24968
+ "1:4": {
24969
+ "0.5K": [256, 1024],
24970
+ "1K": [512, 2048],
24971
+ "2K": [1024, 4096],
24972
+ "4K": [2048, 8192]
24973
+ },
24974
+ "1:8": {
24975
+ "0.5K": [192, 1536],
24976
+ "1K": [384, 3072],
24977
+ "2K": [768, 6144],
24978
+ "4K": [1536, 12288]
24979
+ },
24980
+ "2:3": {
24981
+ "0.5K": [424, 632],
24982
+ "1K": [848, 1264],
24983
+ "2K": [1696, 2528],
24984
+ "4K": [3392, 5056]
24985
+ },
24986
+ "3:2": {
24987
+ "0.5K": [632, 424],
24988
+ "1K": [1264, 848],
24989
+ "2K": [2528, 1696],
24990
+ "4K": [5056, 3392]
24991
+ },
24992
+ "3:4": {
24993
+ "0.5K": [448, 600],
24994
+ "1K": [896, 1200],
24995
+ "2K": [1792, 2400],
24996
+ "4K": [3584, 4800]
24997
+ },
24998
+ "4:1": {
24999
+ "0.5K": [1024, 256],
25000
+ "1K": [2048, 512],
25001
+ "2K": [4096, 1024],
25002
+ "4K": [8192, 2048]
25003
+ },
25004
+ "4:3": {
25005
+ "0.5K": [600, 448],
25006
+ "1K": [1200, 896],
25007
+ "2K": [2400, 1792],
25008
+ "4K": [4800, 3584]
25009
+ },
25010
+ "4:5": {
25011
+ "0.5K": [464, 576],
25012
+ "1K": [928, 1152],
25013
+ "2K": [1856, 2304],
25014
+ "4K": [3712, 4608]
25015
+ },
25016
+ "5:4": {
25017
+ "0.5K": [576, 464],
25018
+ "1K": [1152, 928],
25019
+ "2K": [2304, 1856],
25020
+ "4K": [4608, 3712]
25021
+ },
25022
+ "8:1": {
25023
+ "0.5K": [1536, 192],
25024
+ "1K": [3072, 384],
25025
+ "2K": [6144, 768],
25026
+ "4K": [12288, 1536]
25027
+ },
25028
+ "9:16": {
25029
+ "0.5K": [384, 688],
25030
+ "1K": [768, 1376],
25031
+ "2K": [1536, 2752],
25032
+ "4K": [3072, 5504]
25033
+ },
25034
+ "16:9": {
25035
+ "0.5K": [688, 384],
25036
+ "1K": [1376, 768],
25037
+ "2K": [2752, 1536],
25038
+ "4K": [5504, 3072]
25039
+ },
25040
+ "21:9": {
25041
+ "0.5K": [792, 168],
25042
+ "1K": [1584, 672],
25043
+ "2K": [3168, 1344],
25044
+ "4K": [6336, 2688]
25045
+ }
25046
+ };
25047
+ function selectOptimalResolution(targetW, targetH, aspectRatio) {
25048
+ const resolutions = GEMINI_RESOLUTION_TABLE[aspectRatio];
25049
+ if (!resolutions) {
25050
+ return "1K";
25051
+ }
25052
+ const targetArea = targetW * targetH;
25053
+ let best = "1K";
25054
+ let bestDiff = Infinity;
25055
+ for (const [resLevel, [w, h]] of Object.entries(resolutions)) {
25056
+ const resArea = w * h;
25057
+ const diff = Math.abs(resArea - targetArea);
25058
+ if (resArea >= targetArea) {
25059
+ if (diff < bestDiff) {
25060
+ bestDiff = diff;
25061
+ best = resLevel;
25062
+ }
25063
+ } else if (bestDiff === Infinity || diff < bestDiff) {
25064
+ bestDiff = diff;
25065
+ best = resLevel;
25066
+ }
25067
+ }
25068
+ return best;
25069
+ }
24833
25070
  function getImageNaturalDimensions(dataUrl) {
24834
25071
  return new Promise((resolve) => {
24835
25072
  const img = new Image();
@@ -24838,6 +25075,32 @@ function getImageNaturalDimensions(dataUrl) {
24838
25075
  img.src = dataUrl;
24839
25076
  });
24840
25077
  }
25078
+ function downscaleForGemini(dataUrl, maxDim = 800, quality = 0.85) {
25079
+ return new Promise((resolve) => {
25080
+ const img = new Image();
25081
+ img.onload = () => {
25082
+ const { naturalWidth: w, naturalHeight: h } = img;
25083
+ if (w <= maxDim && h <= maxDim) {
25084
+ const canvas2 = document.createElement("canvas");
25085
+ canvas2.width = w;
25086
+ canvas2.height = h;
25087
+ canvas2.getContext("2d").drawImage(img, 0, 0);
25088
+ resolve(canvas2.toDataURL("image/jpeg", quality));
25089
+ return;
25090
+ }
25091
+ const scale = maxDim / Math.max(w, h);
25092
+ const dw = Math.round(w * scale);
25093
+ const dh = Math.round(h * scale);
25094
+ const canvas = document.createElement("canvas");
25095
+ canvas.width = dw;
25096
+ canvas.height = dh;
25097
+ canvas.getContext("2d").drawImage(img, 0, 0, dw, dh);
25098
+ resolve(canvas.toDataURL("image/jpeg", quality));
25099
+ };
25100
+ img.onerror = () => resolve(dataUrl);
25101
+ img.src = dataUrl;
25102
+ });
25103
+ }
24841
25104
  function fitImageToFrame(imgW, imgH, frameW, frameH) {
24842
25105
  if (imgW <= 0 || imgH <= 0) {
24843
25106
  return { relX: 0, relY: 0, width: frameW, height: frameH };
@@ -24860,6 +25123,57 @@ function containImageInBbox(imgW, imgH, bboxW, bboxH) {
24860
25123
  const relY = (bboxH - fittedH) / 2;
24861
25124
  return { relX, relY, width: fittedW, height: fittedH };
24862
25125
  }
25126
+ function getProductMinBox(targetW, targetH) {
25127
+ const ar = targetW / targetH;
25128
+ if (ar >= 3.5) {
25129
+ return {
25130
+ minWidth: Math.min(targetW, Math.max(Math.round(targetW * 0.28), 300)),
25131
+ minHeight: Math.round(targetH * 0.72)
25132
+ };
25133
+ }
25134
+ if (ar <= 1 / 3.5) {
25135
+ return {
25136
+ minWidth: Math.round(targetW * 0.82),
25137
+ minHeight: Math.round(targetH * 0.34)
25138
+ };
25139
+ }
25140
+ if (ar <= 0.65) {
25141
+ return {
25142
+ minWidth: Math.round(targetW * 0.76),
25143
+ minHeight: Math.round(targetH * 0.32)
25144
+ };
25145
+ }
25146
+ if (ar >= 1.6) {
25147
+ return {
25148
+ minWidth: Math.round(targetW * 0.36),
25149
+ minHeight: Math.round(targetH * 0.58)
25150
+ };
25151
+ }
25152
+ return {
25153
+ minWidth: Math.round(targetW * 0.54),
25154
+ minHeight: Math.round(targetH * 0.42)
25155
+ };
25156
+ }
25157
+ function enforceMinProductBox({
25158
+ x,
25159
+ y,
25160
+ width,
25161
+ height,
25162
+ targetW,
25163
+ targetH
25164
+ }) {
25165
+ const { minWidth, minHeight } = getProductMinBox(targetW, targetH);
25166
+ const nextWidth = Math.min(targetW, Math.max(width, minWidth));
25167
+ const nextHeight = Math.min(targetH, Math.max(height, minHeight));
25168
+ const centerX = x + width / 2;
25169
+ const centerY = y + height / 2;
25170
+ return {
25171
+ x: Math.min(Math.max(centerX - nextWidth / 2, 0), targetW - nextWidth),
25172
+ y: Math.min(Math.max(centerY - nextHeight / 2, 0), targetH - nextHeight),
25173
+ width: nextWidth,
25174
+ height: nextHeight
25175
+ };
25176
+ }
24863
25177
  async function captureFrameScreenshotFromApp(app, frameId) {
24864
25178
  const elements = app.getSceneElements();
24865
25179
  const frame = elements.find((el) => el.id === frameId);
@@ -24917,7 +25231,11 @@ HORIZONTAL STRIP BANNER (${tgtW}\xD7${tgtH}) \u2014 VERY WIDE AND SHORT:
24917
25231
  Math.round(tgtH * 0.5)
24918
25232
  )}px). Single line only \u2014 no wrapping.
24919
25233
  \u2022 CTA button / call-to-action text: anchor hard right with right padding 8\u201316px, vertically centred.
24920
- \u2022 Product image (if present): place centre or centre-right; height = ~80% of canvas height.
25234
+ \u2022 Product image (if present): place centre or centre-right; height = 70\u201390% of canvas height (${Math.round(
25235
+ tgtH * 0.7
25236
+ )}\u2013${Math.round(
25237
+ tgtH * 0.9
25238
+ )}px); width = AT LEAST 300px to ensure product remains visible and impactful.
24921
25239
  \u2022 NOTHING should be taller than the canvas. All elements must fit within ${tgtH}px height.
24922
25240
  \u2022 Remove or collapse any decorative shapes that cannot fit at this scale.`,
24923
25241
  "vertical-strip": `
@@ -25108,6 +25426,17 @@ ${elementsDesc || "(no foreground elements)"}
25108
25426
  ## Target canvas: ${targetW}\xD7${targetH}
25109
25427
  ${layoutGuidance}
25110
25428
 
25429
+ ## Non-negotiable element count rule
25430
+ The transformed frame must contain EXACTLY ${foreground.length} component(s), matching the ${foreground.length} source element(s) listed above.
25431
+ Create a strict one-to-one mapping: source element [1] becomes output child [1], source element [2] becomes output child [2], and so on.
25432
+ Do NOT add any new elements, components, headlines, subheadlines, CTA buttons, badges, logos, captions, decorative shapes, overlays, or background layers.
25433
+ Do NOT remove any source elements.
25434
+ Do NOT split one source element into multiple output elements.
25435
+ Do NOT merge multiple source elements into one output element.
25436
+ If the source has zero text elements, the output must have zero text elements.
25437
+ If the source does not include a headline or CTA, the output must not include a headline or CTA.
25438
+ The layout guidance is only for repositioning and resizing existing elements; it never permits adding new content.
25439
+
25111
25440
  ## Task
25112
25441
  Produce complete, self-contained HTML that represents this ad at exactly ${targetW}\xD7${targetH} pixels.
25113
25442
  Preserve the original text content, image assets, colors, and brand identity.
@@ -25115,17 +25444,18 @@ Only change positions and sizes.
25115
25444
 
25116
25445
  ## STRICT HTML CONTRACT (parser requirements \u2014 do NOT deviate):
25117
25446
  1. Root element MUST be: <div class="canvas" style="position: relative; width: ${targetW}px; height: ${targetH}px; overflow: hidden; ${bgCss}">
25118
- 2. Every child element MUST have: position: absolute; left: Xpx; top: Ypx; width: Wpx; height: Hpx
25119
- 3. Use ONLY <div> elements. NO <span>, <p>, <img>, <h1>\u2013<h6>, <button>, etc.
25120
- 4. Text: inline style with color, font-size (px), font-weight, text-align, line-height, white-space: pre-wrap
25121
- 5. Images: background-image: url(IMG_PLACEHOLDER_N); background-size: contain; background-repeat: no-repeat; background-position: center
25122
- 6. Shapes: background-color in hex (#rrggbb). Gradients allowed (linear-gradient only).
25123
- 7. NO flexbox. NO grid. NO transform. NO animation. NO filter. NO clip-path.
25124
- 8. Use the exact IMG_PLACEHOLDER_N tokens provided above \u2014 do NOT substitute real URLs.
25125
- 9. All numeric values in px. No %, em, rem, vw, vh.
25126
- 10. Opacity via CSS opacity property (0\u20131 decimal).
25127
- 11. border-radius in px if needed.
25128
- 12. One purpose per div: background fill OR image OR text (never mixed).
25447
+ 2. The .canvas root MUST contain exactly ${foreground.length} direct child <div> element(s), one for each source element listed above, in the same order.
25448
+ 3. Every child element MUST have: position: absolute; left: Xpx; top: Ypx; width: Wpx; height: Hpx
25449
+ 4. Use ONLY <div> elements. NO <span>, <p>, <img>, <h1>\u2013<h6>, <button>, etc.
25450
+ 5. Text: inline style with color, font-size (px), font-weight, text-align, line-height, white-space: pre-wrap
25451
+ 6. Images: background-image: url(IMG_PLACEHOLDER_N); background-size: contain; background-repeat: no-repeat; background-position: center
25452
+ 7. Shapes: background-color in hex (#rrggbb). Gradients allowed (linear-gradient only).
25453
+ 8. NO flexbox. NO grid. NO transform. NO animation. NO filter. NO clip-path.
25454
+ 9. Use the exact IMG_PLACEHOLDER_N tokens provided above \u2014 do NOT substitute real URLs.
25455
+ 10. All numeric values in px. No %, em, rem, vw, vh.
25456
+ 11. Opacity via CSS opacity property (0\u20131 decimal).
25457
+ 12. border-radius in px if needed.
25458
+ 13. One purpose per div: background fill OR image OR text (never mixed).
25129
25459
 
25130
25460
  Return ONLY the raw HTML. No markdown, no code fences, no explanation.`;
25131
25461
  return { prompt, imagePlaceholders };
@@ -25197,6 +25527,14 @@ async function runAutoResizeForDimension(opts) {
25197
25527
  existingFrameId
25198
25528
  } = opts;
25199
25529
  const srcFrame = info.frame;
25530
+ const getCurrentFramePosition = () => {
25531
+ const currentEls = app.getSceneElements();
25532
+ const currentFrame = currentEls.find((el) => el.id === frameId);
25533
+ if (currentFrame) {
25534
+ return { x: currentFrame.x, y: currentFrame.y };
25535
+ }
25536
+ return { x: placementX, y: placementY };
25537
+ };
25200
25538
  let frameId;
25201
25539
  if (existingFrameId) {
25202
25540
  frameId = existingFrameId;
@@ -25218,35 +25556,69 @@ async function runAutoResizeForDimension(opts) {
25218
25556
  frameId = newFrame.id;
25219
25557
  }
25220
25558
  let bgImageDataUrl = null;
25559
+ const needsRegen = needsBackgroundRegeneration(
25560
+ srcFrame.width,
25561
+ srcFrame.height,
25562
+ tgtW,
25563
+ tgtH
25564
+ );
25565
+ debug(
25566
+ `[AutoResize] ${debugLabel} \u2014 background: type=${info.background.type}, needsRegen=${needsRegen}`
25567
+ );
25221
25568
  if (info.background.type === "image") {
25222
25569
  const srcBgUrl = info.background.dataUrl;
25223
- const needsRegen = needsBackgroundRegeneration(
25224
- srcFrame.width,
25225
- srcFrame.height,
25226
- tgtW,
25227
- tgtH
25228
- );
25229
- debug(`[AutoResize] ${debugLabel} \u2014 background: needsRegen=${needsRegen}`);
25230
- if (needsRegen) {
25570
+ if (!srcBgUrl) {
25571
+ console.warn(
25572
+ `[AutoResize] ${debugLabel} \u2014 image background missing dataUrl`
25573
+ );
25574
+ }
25575
+ if (needsRegen && srcBgUrl) {
25231
25576
  onProgress?.("Regenerating background image\u2026");
25232
25577
  const ar = nearestGeminiAspectRatio(tgtW, tgtH);
25578
+ const resolution = selectOptimalResolution(tgtW, tgtH, ar);
25233
25579
  debug(
25234
- `[AutoResize] ${debugLabel} \u2014 calling Gemini for background regen (ar=${ar})`
25235
- );
25236
- bgImageDataUrl = await callGeminiAPI(
25237
- `You are given an input background image. Preserve the original scene identity, composition, color palette, and style. Outpaint only beyond the existing edges so the final image fits aspect ratio ${ar}. Keep the center subject and key content unchanged. Ensure all surfaces extend with natural, seamless continuity \u2014 no visible seams, repeated patterns, or tiling artifacts. For areas far from the center, gradually soften into ambient bokeh rather than extending sharp textures. Do not add text, logos, watermarks, UI, symbols, or new unrelated objects. Avoid altering brand-relevant details.`,
25238
- agentImageModel || DEFAULT_BG_REGEN_MODEL,
25239
- ar,
25240
- "1K",
25241
- // resolution parameter — supportsImageSize=false so this is ignored by API
25242
- false,
25243
- // supportsImageSize: false → no imageSize sent to API, uses default
25244
- false,
25245
- // supportsThinking
25246
- geminiApiKey,
25247
- srcBgUrl,
25248
- signal
25580
+ `[AutoResize] ${debugLabel} \u2014 calling Gemini for background regen (ar=${ar}, res=${resolution})`
25249
25581
  );
25582
+ try {
25583
+ const compressedBgUrl = await downscaleForGemini(srcBgUrl);
25584
+ debug(
25585
+ `[AutoResize] bg-regen starting: ar=${ar}`,
25586
+ `keyLen=${geminiApiKey.length}`,
25587
+ `compressedLen=${compressedBgUrl.length}`,
25588
+ `model=${agentImageModel || DEFAULT_BG_REGEN_MODEL}`
25589
+ );
25590
+ debug(
25591
+ `[AutoResize] ${debugLabel} \u2014 reference image compressed for Gemini`
25592
+ );
25593
+ bgImageDataUrl = await callGeminiAPI(
25594
+ `Expand this image to fill the full canvas dimensions.
25595
+ Preserve the original scene exactly \u2014 same lighting, color palette, style, and mood.
25596
+ Fill any new areas with content that naturally continues the existing scene,
25597
+ as if the camera simply zoomed out.\xA0The original image content must appear embedded naturally within the larger scene,
25598
+ with no visible boundary or transition between old and new areas.
25599
+ No blurring, softening, or bokeh.
25600
+ No visible seams, tiling, repeated patterns, or artifacts.
25601
+ No new text, logos, watermarks, or unrelated objects added.`,
25602
+ agentImageModel || DEFAULT_BG_REGEN_MODEL,
25603
+ ar,
25604
+ resolution,
25605
+ // Dynamic resolution based on target dimensions
25606
+ true,
25607
+ // supportsImageSize: true → sends imageSize to API, required for extreme ratios (8:1, 4:1, etc.)
25608
+ false,
25609
+ // supportsThinking
25610
+ geminiApiKey,
25611
+ compressedBgUrl,
25612
+ signal
25613
+ );
25614
+ debug(`[AutoResize] ${debugLabel} \u2014 bg-regen succeeded`);
25615
+ } catch (bgErr) {
25616
+ console.error(
25617
+ `[AutoResize] bg-regen failed for ${debugLabel}:`,
25618
+ bgErr instanceof Error ? bgErr.message : String(bgErr)
25619
+ );
25620
+ bgImageDataUrl = srcBgUrl;
25621
+ }
25250
25622
  } else {
25251
25623
  bgImageDataUrl = srcBgUrl;
25252
25624
  }
@@ -25262,11 +25634,12 @@ async function runAutoResizeForDimension(opts) {
25262
25634
  width: imgW,
25263
25635
  height: imgH
25264
25636
  } = fitImageToFrame(imgNatW, imgNatH, tgtW, tgtH);
25637
+ const currentPos = getCurrentFramePosition();
25265
25638
  const bgFileId = nanoid();
25266
25639
  const bgEl = newImageElement({
25267
25640
  type: "image",
25268
- x: placementX + relX,
25269
- y: placementY + relY,
25641
+ x: currentPos.x + relX,
25642
+ y: currentPos.y + relY,
25270
25643
  width: imgW,
25271
25644
  height: imgH,
25272
25645
  fileId: bgFileId,
@@ -25322,12 +25695,13 @@ async function runAutoResizeForDimension(opts) {
25322
25695
  html = html.split(`url(${placeholder})`).join(`url(${dataUrl})`);
25323
25696
  }
25324
25697
  onProgress?.("Parsing layout\u2026");
25698
+ const currentPos = getCurrentFramePosition();
25325
25699
  let parsedEls;
25326
25700
  try {
25327
25701
  parsedEls = await htmlToExcalidrawElements(
25328
25702
  html,
25329
- placementX,
25330
- placementY,
25703
+ currentPos.x,
25704
+ currentPos.y,
25331
25705
  tgtW,
25332
25706
  tgtH,
25333
25707
  customFontMap
@@ -25340,6 +25714,11 @@ async function runAutoResizeForDimension(opts) {
25340
25714
  if (!parsedEls || parsedEls.length === 0) {
25341
25715
  throw new Error("HTML parser produced no elements");
25342
25716
  }
25717
+ const productDataUrls = new Set(
25718
+ info.foreground.filter(
25719
+ (el) => el.kind === "image" && el.slotType === "product"
25720
+ ).map((el) => el.dataUrl)
25721
+ );
25343
25722
  onProgress?.("Inserting elements\u2026");
25344
25723
  const newEls = [];
25345
25724
  const imageFiles = [];
@@ -25393,6 +25772,20 @@ async function runAutoResizeForDimension(opts) {
25393
25772
  });
25394
25773
  newEls.push({ ...el, frameId });
25395
25774
  } else if (pel.kind === "image") {
25775
+ const isProduct = productDataUrls.has(pel.dataUrl);
25776
+ const adjusted = isProduct ? enforceMinProductBox({
25777
+ x: pel.x - currentPos.x,
25778
+ y: pel.y - currentPos.y,
25779
+ width: pel.width,
25780
+ height: pel.height,
25781
+ targetW: tgtW,
25782
+ targetH: tgtH
25783
+ }) : {
25784
+ x: pel.x - currentPos.x,
25785
+ y: pel.y - currentPos.y,
25786
+ width: pel.width,
25787
+ height: pel.height
25788
+ };
25396
25789
  const fileId2 = nanoid();
25397
25790
  const { width: natW, height: natH } = await getImageNaturalDimensions(
25398
25791
  pel.dataUrl
@@ -25402,17 +25795,20 @@ async function runAutoResizeForDimension(opts) {
25402
25795
  relY,
25403
25796
  width: imgW,
25404
25797
  height: imgH
25405
- } = containImageInBbox(natW, natH, pel.width, pel.height);
25798
+ } = containImageInBbox(natW, natH, adjusted.width, adjusted.height);
25406
25799
  const el = newImageElement({
25407
25800
  type: "image",
25408
- x: pel.x + relX,
25409
- y: pel.y + relY,
25801
+ x: currentPos.x + adjusted.x + relX,
25802
+ y: currentPos.y + adjusted.y + relY,
25410
25803
  width: imgW,
25411
25804
  height: imgH,
25412
25805
  fileId: fileId2,
25413
25806
  status: "pending",
25414
25807
  scale: [1, 1],
25415
- customData: { generatedByHtml: true }
25808
+ customData: {
25809
+ generatedByHtml: true,
25810
+ ...isProduct ? { slotType: "product" } : {}
25811
+ }
25416
25812
  });
25417
25813
  newEls.push({ ...el, frameId });
25418
25814
  imageFiles.push({
@@ -25468,6 +25864,16 @@ async function runAutoResizeForDimension(opts) {
25468
25864
  );
25469
25865
  if (reviewRounds >= MAX_REVIEW_ROUNDS) {
25470
25866
  debug(`[AutoResize] ${debugLabel} \u2014 reviewer cap reached, finalising`);
25867
+ if (lastFeedback && lastFeedback.issues.length > 0) {
25868
+ debug(
25869
+ `[AutoResize] ${debugLabel} \u2014 WARNING: Giving up with ${lastFeedback.issues.length} unresolved issue(s)`
25870
+ );
25871
+ lastFeedback.issues.forEach((issue, i) => {
25872
+ debug(
25873
+ `[AutoResize] ${debugLabel} \u2014 Unresolved ${i + 1}: [${issue.severity}] ${issue.element}: ${issue.problem}`
25874
+ );
25875
+ });
25876
+ }
25471
25877
  break;
25472
25878
  }
25473
25879
  const screenshot = await captureFrameScreenshotFromApp(app, frameId);
@@ -25501,6 +25907,14 @@ async function runAutoResizeForDimension(opts) {
25501
25907
  debug(
25502
25908
  `[AutoResize] ${debugLabel} \u2014 reviewer found ${feedback.issues.length} issue(s), regenerating\u2026`
25503
25909
  );
25910
+ feedback.issues.forEach((issue, i) => {
25911
+ debug(
25912
+ `[AutoResize] ${debugLabel} \u2014 Issue ${i + 1}: [${issue.severity.toUpperCase()}] ${issue.element}: ${issue.problem}`
25913
+ );
25914
+ debug(
25915
+ `[AutoResize] ${debugLabel} \u2014 Fix: ${issue.preciseInstruction}`
25916
+ );
25917
+ });
25504
25918
  lastFeedback = feedback;
25505
25919
  onProgress?.("Refining layout\u2026");
25506
25920
  } catch {
@@ -26132,9 +26546,19 @@ var AutoResizePanelHost = ({ app }) => {
26132
26546
  autoResizePanelStore.getState()
26133
26547
  );
26134
26548
  const panelRef = useRef28(null);
26549
+ const [dragOffset, setDragOffset] = useState31(
26550
+ null
26551
+ );
26552
+ const [isDragging, setIsDragging] = useState31(false);
26553
+ const dragStartRef = useRef28(null);
26135
26554
  useEffect33(() => {
26136
26555
  return autoResizePanelStore.subscribe(setPanelState);
26137
26556
  }, []);
26557
+ useEffect33(() => {
26558
+ if (panelState.isOpen) {
26559
+ setDragOffset(null);
26560
+ }
26561
+ }, [panelState.element?.id]);
26138
26562
  useEffect33(() => {
26139
26563
  if (!panelState.isOpen) {
26140
26564
  return;
@@ -26150,6 +26574,32 @@ var AutoResizePanelHost = ({ app }) => {
26150
26574
  document.addEventListener("mousedown", handleMouseDown);
26151
26575
  return () => document.removeEventListener("mousedown", handleMouseDown);
26152
26576
  }, [panelState.isOpen, panelState.isRunning]);
26577
+ useEffect33(() => {
26578
+ if (!isDragging) {
26579
+ return;
26580
+ }
26581
+ const handleMouseMove = (e) => {
26582
+ if (!dragStartRef.current) {
26583
+ return;
26584
+ }
26585
+ const deltaX = e.clientX - dragStartRef.current.mouseX;
26586
+ const deltaY = e.clientY - dragStartRef.current.mouseY;
26587
+ setDragOffset({
26588
+ x: dragStartRef.current.panelX + deltaX,
26589
+ y: dragStartRef.current.panelY + deltaY
26590
+ });
26591
+ };
26592
+ const handleMouseUp = () => {
26593
+ setIsDragging(false);
26594
+ dragStartRef.current = null;
26595
+ };
26596
+ document.addEventListener("mousemove", handleMouseMove);
26597
+ document.addEventListener("mouseup", handleMouseUp);
26598
+ return () => {
26599
+ document.removeEventListener("mousemove", handleMouseMove);
26600
+ document.removeEventListener("mouseup", handleMouseUp);
26601
+ };
26602
+ }, [isDragging]);
26153
26603
  if (!panelState.isOpen || !panelState.element) {
26154
26604
  return null;
26155
26605
  }
@@ -26163,19 +26613,38 @@ var AutoResizePanelHost = ({ app }) => {
26163
26613
  { sceneX: x1 + element.width / 2, sceneY: y1 },
26164
26614
  appState
26165
26615
  );
26166
- const panelX = viewportX - appState.offsetLeft;
26167
- const panelY = Math.max(viewportY - appState.offsetTop - 82, 8);
26616
+ const defaultPanelX = viewportX - appState.offsetLeft;
26617
+ const defaultPanelY = Math.max(viewportY - appState.offsetTop - 82, 8);
26618
+ const finalPanelX = dragOffset !== null ? dragOffset.x : defaultPanelX;
26619
+ const finalPanelY = dragOffset !== null ? dragOffset.y : defaultPanelY;
26620
+ const handleDragStart = (e) => {
26621
+ const target = e.target;
26622
+ if (!target.closest(".arp-header")) {
26623
+ return;
26624
+ }
26625
+ e.preventDefault();
26626
+ e.stopPropagation();
26627
+ setIsDragging(true);
26628
+ dragStartRef.current = {
26629
+ mouseX: e.clientX,
26630
+ mouseY: e.clientY,
26631
+ panelX: finalPanelX,
26632
+ panelY: finalPanelY
26633
+ };
26634
+ };
26168
26635
  return /* @__PURE__ */ jsx90(
26169
26636
  "div",
26170
26637
  {
26171
26638
  ref: panelRef,
26639
+ onMouseDown: handleDragStart,
26172
26640
  style: {
26173
26641
  position: "absolute",
26174
- left: `${panelX}px`,
26175
- top: `${panelY}px`,
26176
- transform: "translateX(-50%)",
26642
+ left: `${finalPanelX}px`,
26643
+ top: `${finalPanelY}px`,
26644
+ transform: dragOffset !== null ? "none" : "translateX(-50%)",
26177
26645
  zIndex: "var(--zIndex-canvasButtons)",
26178
- pointerEvents: "all"
26646
+ pointerEvents: "all",
26647
+ cursor: isDragging ? "grabbing" : "default"
26179
26648
  },
26180
26649
  children: /* @__PURE__ */ jsx90(
26181
26650
  AutoResizePanel,
@@ -53209,7 +53678,9 @@ async function runAgentLoop(opts) {
53209
53678
  webSearchEnabled,
53210
53679
  apiKey,
53211
53680
  chatModel,
53212
- agentImageModel,
53681
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
53682
+ agentImageModel: _agentImageModel,
53683
+ // Currently unused but kept for future use
53213
53684
  toolCtx,
53214
53685
  onUpdate,
53215
53686
  signal,
@@ -53503,7 +53974,8 @@ ${f.textContent}`).join("\n\n")}`;
53503
53974
  messages.push({
53504
53975
  role: "user",
53505
53976
  content: `generate_html_banner has failed ${htmlBannerFailures} times. STOP trying generate_html_banner. Instead, build the text layer using primitive tools in this order:
53506
- 1. Call add_rectangle to create the background zone (left side of the frame: x=0, y=0, width=${imageGenData.x > 0 ? imageGenData.x : Math.round(frameW * 0.45)}, height=${Math.round(
53977
+ 1. Call add_rectangle to create the background zone (left side of the frame: x=0, y=0, width=${imageGenData.x > 0 ? imageGenData.x : Math.round(frameW * 0.45)}, height=${// eslint-disable-next-line no-loop-func
53978
+ Math.round(
53507
53979
  augmentedToolCtx.excalidrawAPI.getSceneElements().find((el) => el.id === imageGenData.frameId)?.height ?? 512
53508
53980
  )} inside frameId="${imageGenData.frameId}")
53509
53981
  2. Call add_text for each text element (headline, subheadline, CTA label) inside the same frameId
@@ -53539,14 +54011,20 @@ Use the frameId="${imageGenData.frameId}" for all elements. Keep all elements wi
53539
54011
  htmlSource: parsedArgs.html ?? "",
53540
54012
  frame: {
53541
54013
  id: imageGenData?.frameId ?? "",
53542
- width: imageGenData ? (() => {
53543
- const el = augmentedToolCtx.excalidrawAPI.getSceneElements().find((e) => e.id === imageGenData.frameId);
53544
- return Math.round(el?.width ?? 512);
53545
- })() : 512,
53546
- height: imageGenData ? (() => {
53547
- const el = augmentedToolCtx.excalidrawAPI.getSceneElements().find((e) => e.id === imageGenData.frameId);
53548
- return Math.round(el?.height ?? 512);
53549
- })() : 512
54014
+ width: imageGenData ? (
54015
+ // eslint-disable-next-line no-loop-func
54016
+ (() => {
54017
+ const el = augmentedToolCtx.excalidrawAPI.getSceneElements().find((e) => e.id === imageGenData.frameId);
54018
+ return Math.round(el?.width ?? 512);
54019
+ })()
54020
+ ) : 512,
54021
+ height: imageGenData ? (
54022
+ // eslint-disable-next-line no-loop-func
54023
+ (() => {
54024
+ const el = augmentedToolCtx.excalidrawAPI.getSceneElements().find((e) => e.id === imageGenData.frameId);
54025
+ return Math.round(el?.height ?? 512);
54026
+ })()
54027
+ ) : 512
53550
54028
  },
53551
54029
  imagePlacement: imageGenData ? {
53552
54030
  x: imageGenData.x,
@@ -54736,6 +55214,7 @@ var AIChatPanel = React63.forwardRef(
54736
55214
  setStatusText("");
54737
55215
  }
54738
55216
  },
55217
+ // eslint-disable-next-line react-hooks/exhaustive-deps
54739
55218
  [
54740
55219
  prompt,
54741
55220
  isLoading,