@orangecatai/adgen-canvas 0.0.17 → 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.
- package/dist/dev/{chunk-IFMURN5W.js → chunk-4K5LIQQU.js} +2 -1
- package/dist/dev/chunk-4K5LIQQU.js.map +7 -0
- package/dist/dev/{chunk-7JV3TYRQ.js → chunk-JFIHKE5E.js} +3 -3
- package/dist/dev/{chunk-N5ZWSPF6.js → chunk-SFB47GDQ.js} +2 -2
- package/dist/dev/data/{image-2XZ3M7M6.js → image-OXVYDFFE.js} +3 -3
- package/dist/dev/index.css +5 -0
- package/dist/dev/index.css.map +2 -2
- package/dist/dev/index.js +589 -77
- package/dist/dev/index.js.map +3 -3
- package/dist/dev/locales/{en-ZZQASRYX.js → en-IMRJ2ODG.js} +2 -2
- package/dist/dev/subset-shared.chunk.js +1 -1
- package/dist/dev/subset-worker.chunk.js +1 -1
- package/dist/prod/{chunk-WMOQJMH6.js → chunk-5GYFLSBF.js} +2 -2
- package/dist/prod/{chunk-3WJLWPNH.js → chunk-7XQHY7WT.js} +1 -1
- package/dist/prod/{chunk-MSBJ7C4T.js → chunk-TX6PVFUX.js} +3 -3
- package/dist/prod/data/image-FRJAQJ62.js +1 -0
- package/dist/prod/index.css +1 -1
- package/dist/prod/index.js +84 -65
- package/dist/prod/locales/{en-RQF5X6GM.js → en-5WIO63S4.js} +1 -1
- package/dist/prod/subset-shared.chunk.js +1 -1
- package/dist/prod/subset-worker.chunk.js +1 -1
- package/dist/types/common/src/keys.d.ts +2 -0
- package/dist/types/element/src/frame.d.ts +3 -3
- package/dist/types/excalidraw/actions/actionFrame.d.ts +178 -0
- package/dist/types/excalidraw/actions/shortcuts.d.ts +1 -1
- package/dist/types/excalidraw/actions/types.d.ts +1 -1
- package/dist/types/excalidraw/components/AIChatPanel.d.ts +1 -1
- package/dist/types/excalidraw/components/ImageGeneratorPanel.d.ts +3 -3
- package/dist/types/excalidraw/components/auto-resize/AutoResizeShimmerLayer.d.ts +4 -0
- package/dist/types/excalidraw/components/auto-resize/autoResizeEngine.d.ts +47 -0
- package/package.json +3 -3
- package/dist/dev/chunk-IFMURN5W.js.map +0 -7
- package/dist/prod/data/image-3F2KCNSC.js +0 -1
- /package/dist/dev/{chunk-7JV3TYRQ.js.map → chunk-JFIHKE5E.js.map} +0 -0
- /package/dist/dev/{chunk-N5ZWSPF6.js.map → chunk-SFB47GDQ.js.map} +0 -0
- /package/dist/dev/data/{image-2XZ3M7M6.js.map → image-OXVYDFFE.js.map} +0 -0
- /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-
|
|
70
|
+
} from "./chunk-JFIHKE5E.js";
|
|
71
71
|
import {
|
|
72
72
|
define_import_meta_env_default
|
|
73
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-SFB47GDQ.js";
|
|
74
74
|
import {
|
|
75
75
|
en_default
|
|
76
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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((
|
|
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:
|
|
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 =
|
|
22910
|
-
const height =
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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.
|
|
25119
|
-
3.
|
|
25120
|
-
4.
|
|
25121
|
-
5.
|
|
25122
|
-
6.
|
|
25123
|
-
7.
|
|
25124
|
-
8.
|
|
25125
|
-
9.
|
|
25126
|
-
10.
|
|
25127
|
-
11.
|
|
25128
|
-
12.
|
|
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
|
-
|
|
25224
|
-
|
|
25225
|
-
|
|
25226
|
-
|
|
25227
|
-
|
|
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:
|
|
25269
|
-
y:
|
|
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
|
-
|
|
25330
|
-
|
|
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,
|
|
25798
|
+
} = containImageInBbox(natW, natH, adjusted.width, adjusted.height);
|
|
25406
25799
|
const el = newImageElement({
|
|
25407
25800
|
type: "image",
|
|
25408
|
-
x:
|
|
25409
|
-
y:
|
|
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: {
|
|
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
|
|
26167
|
-
const
|
|
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: `${
|
|
26175
|
-
top: `${
|
|
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,
|
|
@@ -52668,11 +53137,12 @@ async function execFillTemplateSlots(args, ctx) {
|
|
|
52668
53137
|
}
|
|
52669
53138
|
let mutation = {};
|
|
52670
53139
|
const makeTextMutation = (value, useHeadlineFont = false) => {
|
|
53140
|
+
const brandFontId = useHeadlineFont ? ctx.brandHeadlineFontId ?? ctx.brandBodyFontId : ctx.brandBodyFontId;
|
|
53141
|
+
const elForFit = brandFontId !== void 0 ? { ...el, fontFamily: brandFontId } : el;
|
|
52671
53142
|
const { fontSize, wrappedText, height, autoResize } = fitFontSize(
|
|
52672
53143
|
value,
|
|
52673
|
-
|
|
53144
|
+
elForFit
|
|
52674
53145
|
);
|
|
52675
|
-
const brandFontId = useHeadlineFont ? ctx.brandHeadlineFontId ?? ctx.brandBodyFontId : ctx.brandBodyFontId;
|
|
52676
53146
|
return {
|
|
52677
53147
|
text: wrappedText,
|
|
52678
53148
|
originalText: value,
|
|
@@ -53208,7 +53678,9 @@ async function runAgentLoop(opts) {
|
|
|
53208
53678
|
webSearchEnabled,
|
|
53209
53679
|
apiKey,
|
|
53210
53680
|
chatModel,
|
|
53211
|
-
|
|
53681
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
53682
|
+
agentImageModel: _agentImageModel,
|
|
53683
|
+
// Currently unused but kept for future use
|
|
53212
53684
|
toolCtx,
|
|
53213
53685
|
onUpdate,
|
|
53214
53686
|
signal,
|
|
@@ -53401,6 +53873,38 @@ ${f.textContent}`).join("\n\n")}`;
|
|
|
53401
53873
|
continue;
|
|
53402
53874
|
}
|
|
53403
53875
|
}
|
|
53876
|
+
if (name === "create_frame" && lastCreatedFrameId) {
|
|
53877
|
+
const frameStillExists = augmentedToolCtx.excalidrawAPI.getSceneElements().some((el) => el.id === lastCreatedFrameId && !el.isDeleted);
|
|
53878
|
+
if (frameStillExists) {
|
|
53879
|
+
const blockedResult = {
|
|
53880
|
+
success: false,
|
|
53881
|
+
error: `DUPLICATE FRAME BLOCKED: You already created frame "${lastCreatedFrameId}" in this turn. Do NOT call create_frame again. Reuse this frameId for every subsequent tool call (generate_image, load_template_into_frame, fill_template_slots, etc.). If the frame needs different dimensions, call update_element on this frameId instead.`,
|
|
53882
|
+
statusMessage: `Blocked: reuse existing frame ${lastCreatedFrameId}`
|
|
53883
|
+
};
|
|
53884
|
+
const blockedAction = {
|
|
53885
|
+
result: {
|
|
53886
|
+
success: blockedResult.success,
|
|
53887
|
+
statusMessage: blockedResult.statusMessage
|
|
53888
|
+
}
|
|
53889
|
+
};
|
|
53890
|
+
toolActions.push(blockedAction);
|
|
53891
|
+
onUpdate({
|
|
53892
|
+
type: "tool_done",
|
|
53893
|
+
message: blockedResult.statusMessage,
|
|
53894
|
+
toolAction: blockedAction
|
|
53895
|
+
});
|
|
53896
|
+
messages.push({
|
|
53897
|
+
role: "tool",
|
|
53898
|
+
tool_call_id: toolCall.id,
|
|
53899
|
+
content: JSON.stringify({
|
|
53900
|
+
success: false,
|
|
53901
|
+
error: blockedResult.error,
|
|
53902
|
+
existingFrameId: lastCreatedFrameId
|
|
53903
|
+
})
|
|
53904
|
+
});
|
|
53905
|
+
continue;
|
|
53906
|
+
}
|
|
53907
|
+
}
|
|
53404
53908
|
if (name === "generate_html_banner" && templateLoaded) {
|
|
53405
53909
|
const blockedResult = {
|
|
53406
53910
|
success: false,
|
|
@@ -53470,7 +53974,8 @@ ${f.textContent}`).join("\n\n")}`;
|
|
|
53470
53974
|
messages.push({
|
|
53471
53975
|
role: "user",
|
|
53472
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:
|
|
53473
|
-
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=${
|
|
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(
|
|
53474
53979
|
augmentedToolCtx.excalidrawAPI.getSceneElements().find((el) => el.id === imageGenData.frameId)?.height ?? 512
|
|
53475
53980
|
)} inside frameId="${imageGenData.frameId}")
|
|
53476
53981
|
2. Call add_text for each text element (headline, subheadline, CTA label) inside the same frameId
|
|
@@ -53506,14 +54011,20 @@ Use the frameId="${imageGenData.frameId}" for all elements. Keep all elements wi
|
|
|
53506
54011
|
htmlSource: parsedArgs.html ?? "",
|
|
53507
54012
|
frame: {
|
|
53508
54013
|
id: imageGenData?.frameId ?? "",
|
|
53509
|
-
width: imageGenData ? (
|
|
53510
|
-
|
|
53511
|
-
|
|
53512
|
-
|
|
53513
|
-
|
|
53514
|
-
|
|
53515
|
-
|
|
53516
|
-
|
|
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
|
|
53517
54028
|
},
|
|
53518
54029
|
imagePlacement: imageGenData ? {
|
|
53519
54030
|
x: imageGenData.x,
|
|
@@ -54703,6 +55214,7 @@ var AIChatPanel = React63.forwardRef(
|
|
|
54703
55214
|
setStatusText("");
|
|
54704
55215
|
}
|
|
54705
55216
|
},
|
|
55217
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
54706
55218
|
[
|
|
54707
55219
|
prompt,
|
|
54708
55220
|
isLoading,
|