@orangecatai/adgen-canvas 0.0.30 → 0.0.31
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-2WAJTVZS.js → chunk-3SKNYE32.js} +2 -2
- package/dist/dev/{chunk-X6KPARLJ.js → chunk-N472V5Q6.js} +3 -3
- package/dist/dev/data/{image-HGVO7LLH.js → image-C5LHCG7R.js} +3 -3
- package/dist/dev/index.css +3 -0
- package/dist/dev/index.css.map +2 -2
- package/dist/dev/index.js +231 -48
- package/dist/dev/index.js.map +3 -3
- package/dist/dev/subset-shared.chunk.js +1 -1
- package/dist/dev/subset-worker.chunk.js +1 -1
- package/dist/prod/{chunk-SZMIDXG6.js → chunk-7Z7DBJZ7.js} +1 -1
- package/dist/prod/{chunk-GTYNSLQE.js → chunk-ZRY46W7L.js} +2 -2
- package/dist/prod/data/image-NBSHNEUL.js +1 -0
- package/dist/prod/index.css +1 -1
- package/dist/prod/index.js +65 -55
- package/dist/prod/subset-shared.chunk.js +1 -1
- package/dist/prod/subset-worker.chunk.js +1 -1
- package/dist/types/excalidraw/components/AIChatPanel.d.ts +11 -1
- package/dist/types/excalidraw/components/ai-chat/agentLoop.d.ts +18 -1
- package/dist/types/excalidraw/components/ai-chat/canvasTools.d.ts +43 -0
- package/dist/types/excalidraw/components/ai-chat/reviewerAgent.d.ts +7 -1
- package/package.json +1 -1
- package/dist/prod/data/image-B2RV64CG.js +0 -1
- /package/dist/dev/{chunk-2WAJTVZS.js.map → chunk-3SKNYE32.js.map} +0 -0
- /package/dist/dev/{chunk-X6KPARLJ.js.map → chunk-N472V5Q6.js.map} +0 -0
- /package/dist/dev/data/{image-HGVO7LLH.js.map → image-C5LHCG7R.js.map} +0 -0
package/dist/dev/index.js
CHANGED
|
@@ -66,10 +66,10 @@ import {
|
|
|
66
66
|
serializeAsJSON,
|
|
67
67
|
serializeLibraryAsJSON,
|
|
68
68
|
strokeRectWithRotation_simple
|
|
69
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-N472V5Q6.js";
|
|
70
70
|
import {
|
|
71
71
|
define_import_meta_env_default
|
|
72
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-3SKNYE32.js";
|
|
73
73
|
import {
|
|
74
74
|
en_default
|
|
75
75
|
} from "./chunk-LIELFD5T.js";
|
|
@@ -9796,7 +9796,7 @@ var exportCanvas = async (type, elements, appState, files, {
|
|
|
9796
9796
|
let blob = canvasToBlob(tempCanvas);
|
|
9797
9797
|
if (appState.exportEmbedScene) {
|
|
9798
9798
|
blob = blob.then(
|
|
9799
|
-
(blob2) => import("./data/image-
|
|
9799
|
+
(blob2) => import("./data/image-C5LHCG7R.js").then(
|
|
9800
9800
|
({ encodePngMetadata: encodePngMetadata2 }) => encodePngMetadata2({
|
|
9801
9801
|
blob: blob2,
|
|
9802
9802
|
metadata: serializeAsJSON(elements, appState, files, "local")
|
|
@@ -26064,7 +26064,7 @@ var SIZE_SCALE_MAP = {
|
|
|
26064
26064
|
};
|
|
26065
26065
|
function buildBrandContextMessage(ctx) {
|
|
26066
26066
|
const lines = [
|
|
26067
|
-
"## Brand Identity \u2014 apply
|
|
26067
|
+
"## Brand Identity \u2014 you MUST apply every guideline below to all ads you create, by default"
|
|
26068
26068
|
];
|
|
26069
26069
|
if (ctx.colors) {
|
|
26070
26070
|
const { primary, secondary, accent, background } = ctx.colors;
|
|
@@ -26139,6 +26139,10 @@ function buildBrandContextMessage(ctx) {
|
|
|
26139
26139
|
lines.push(`**Imagery direction**: ${parts.join(", ")}`);
|
|
26140
26140
|
}
|
|
26141
26141
|
}
|
|
26142
|
+
lines.push(
|
|
26143
|
+
"",
|
|
26144
|
+
"**Precedence**: these are defaults, not absolutes. If the user's request explicitly states or clearly implies something different from a guideline above \u2014 a different color, mood, style, subject treatment, or tone \u2014 follow the user's explicit instruction for that ad instead of the brand guideline it conflicts with. Do not silently override what the user asked for in favor of brand consistency; only apply a guideline where the user's request is silent on that point."
|
|
26145
|
+
);
|
|
26142
26146
|
return lines.join("\n");
|
|
26143
26147
|
}
|
|
26144
26148
|
function buildImageGenSafePrompt(prompt, brandContext, dimensions) {
|
|
@@ -26168,7 +26172,9 @@ function buildImageGenSafePrompt(prompt, brandContext, dimensions) {
|
|
|
26168
26172
|
}
|
|
26169
26173
|
const brandImageHint = brandImageInstructions.length > 0 ? ` ${brandImageInstructions.join(" ")}` : "";
|
|
26170
26174
|
const brandNameHint = brandContext.name ? ` This ad is for the brand "${brandContext.name}" \u2014 all visual elements, copy, and style must reflect this brand exclusively.` : "";
|
|
26171
|
-
return `${prompt}${brandNameHint}${fontHint}${brandImageHint} Render all specified copy as crisp, correctly-spelled, legible text within the image. Maintain generous padding and whitespace \u2014 at minimum 4\u20135% of the frame width as margin on all sides; copy blocks, the logo, and the CTA must each have clear breathing room and must not feel cramped or crowded. Professional advertising quality
|
|
26175
|
+
return `${prompt}${brandNameHint}${fontHint}${brandImageHint} Render all specified copy as crisp, correctly-spelled, legible text within the image. Maintain generous padding and whitespace \u2014 at minimum 4\u20135% of the frame width as margin on all sides; copy blocks, the logo, and the CTA must each have clear breathing room and must not feel cramped or crowded. Professional advertising quality.
|
|
26176
|
+
|
|
26177
|
+
${PRECEDENCE_LINE}`;
|
|
26172
26178
|
}
|
|
26173
26179
|
var clean = (value) => {
|
|
26174
26180
|
const trimmed = value?.trim();
|
|
@@ -56721,6 +56727,38 @@ function execAddText(args, ctx) {
|
|
|
56721
56727
|
};
|
|
56722
56728
|
}
|
|
56723
56729
|
var frameGenerationMemory = /* @__PURE__ */ new Map();
|
|
56730
|
+
var chatCreatedFrames = [];
|
|
56731
|
+
function getChatCreatedFramesContext() {
|
|
56732
|
+
if (chatCreatedFrames.length === 0)
|
|
56733
|
+
return "";
|
|
56734
|
+
const lines = chatCreatedFrames.map(
|
|
56735
|
+
(f, i) => `${i + 1}. frameId="${f.frameId}"${i === chatCreatedFrames.length - 1 ? " (most recent)" : ""} \u2014 ${f.summary}`
|
|
56736
|
+
);
|
|
56737
|
+
return [
|
|
56738
|
+
`## Frames generated in this conversation (not all frames on the canvas \u2014 the canvas may contain other unrelated images/content)`,
|
|
56739
|
+
...lines,
|
|
56740
|
+
``,
|
|
56741
|
+
`When the user refers to an image ambiguously \u2014 "the previous one," "that image," "the above ad," "fix it" \u2014 without an @mention or a distinguishing description, they mean the MOST RECENT frame in this list. Do NOT call list_frames() to search the whole canvas for it; use the frameId given here directly. Only search more broadly (list_frames) if the user's wording clearly points at something NOT in this list, or if this list has more than one plausible match and the user's description (e.g. "the dog one," "the second one") disambiguates between entries here.`
|
|
56742
|
+
].join("\n");
|
|
56743
|
+
}
|
|
56744
|
+
function resetAndSeedFrameGenerationMemory(messages) {
|
|
56745
|
+
frameGenerationMemory.clear();
|
|
56746
|
+
chatCreatedFrames.length = 0;
|
|
56747
|
+
for (const msg of messages) {
|
|
56748
|
+
const g = msg.generatedFrame;
|
|
56749
|
+
if (!g || frameGenerationMemory.has(g.frameId))
|
|
56750
|
+
continue;
|
|
56751
|
+
frameGenerationMemory.set(g.frameId, {
|
|
56752
|
+
originalPrompt: g.prompt,
|
|
56753
|
+
referenceImageIndex: g.referenceImageIndex,
|
|
56754
|
+
userBrief: g.userBrief
|
|
56755
|
+
});
|
|
56756
|
+
chatCreatedFrames.push({
|
|
56757
|
+
frameId: g.frameId,
|
|
56758
|
+
summary: g.prompt.slice(0, 80) + (g.prompt.length > 80 ? "\u2026" : "")
|
|
56759
|
+
});
|
|
56760
|
+
}
|
|
56761
|
+
}
|
|
56724
56762
|
async function execGenerateImage(args, ctx) {
|
|
56725
56763
|
const frameId = args.frameId;
|
|
56726
56764
|
const prompt = args.prompt;
|
|
@@ -56737,6 +56775,11 @@ async function execGenerateImage(args, ctx) {
|
|
|
56737
56775
|
|
|
56738
56776
|
ORIGINAL AD BRIEF (this is a revision, not a new ad \u2014 keep the same subject, composition, and color palette as this original brief unless a fix above explicitly changes it):
|
|
56739
56777
|
"${priorGeneration.originalPrompt}"` : "";
|
|
56778
|
+
const userBrief = priorGeneration?.userBrief ?? ctx.originalUserBrief;
|
|
56779
|
+
const userBriefAnchor = userBrief ? `
|
|
56780
|
+
|
|
56781
|
+
USER'S EXACT REQUEST (verbatim, do not paraphrase, simplify, or substitute the specific subject, action, pose, expression, or scene described here \u2014 depict it faithfully even if the description above worded it differently):
|
|
56782
|
+
"${userBrief}"` : "";
|
|
56740
56783
|
const fontNames = [ctx.brandHeadlineFontName, ctx.brandBodyFontName].filter(
|
|
56741
56784
|
(n, i, arr) => Boolean(n) && arr.indexOf(n) === i
|
|
56742
56785
|
);
|
|
@@ -56771,7 +56814,7 @@ LAYOUT & SPACING RULES \u2014 these are non-negotiable:
|
|
|
56771
56814
|
6. NO CLUTTER: The overall composition must feel open and airy \u2014 fewer elements with more space between them is always better than many elements squeezed together.
|
|
56772
56815
|
7. ASPECT RATIO: Do NOT squish, stretch, or distort any element. Maintain the natural proportions of all visuals, logos, and text at all times.
|
|
56773
56816
|
|
|
56774
|
-
Render all text as crisp, correctly-spelled, legible copy. Professional advertising quality \u2014 strong visual hierarchy, brand-consistent, generous whitespace throughout.${originalPromptAnchor}`;
|
|
56817
|
+
Render all text as crisp, correctly-spelled, legible copy. Professional advertising quality \u2014 strong visual hierarchy, brand-consistent, generous whitespace throughout.${originalPromptAnchor}${userBriefAnchor}`;
|
|
56775
56818
|
if (!frameId || !prompt) {
|
|
56776
56819
|
return {
|
|
56777
56820
|
success: false,
|
|
@@ -56918,7 +56961,12 @@ Render all text as crisp, correctly-spelled, legible copy. Professional advertis
|
|
|
56918
56961
|
if (!priorGeneration) {
|
|
56919
56962
|
frameGenerationMemory.set(frameId, {
|
|
56920
56963
|
originalPrompt: prompt,
|
|
56921
|
-
referenceImageIndex: effectiveReferenceImageIndex
|
|
56964
|
+
referenceImageIndex: effectiveReferenceImageIndex,
|
|
56965
|
+
userBrief
|
|
56966
|
+
});
|
|
56967
|
+
chatCreatedFrames.push({
|
|
56968
|
+
frameId,
|
|
56969
|
+
summary: prompt.slice(0, 80) + (prompt.length > 80 ? "\u2026" : "")
|
|
56922
56970
|
});
|
|
56923
56971
|
} else if (effectiveReferenceImageIndex !== void 0) {
|
|
56924
56972
|
priorGeneration.referenceImageIndex = effectiveReferenceImageIndex;
|
|
@@ -58406,6 +58454,18 @@ function isStreamingResponse(response) {
|
|
|
58406
58454
|
}
|
|
58407
58455
|
|
|
58408
58456
|
// components/ai-chat/agentLoop.ts
|
|
58457
|
+
function isRasterImageDataUrl(dataUrl) {
|
|
58458
|
+
return /^data:image\/(png|jpe?g|gif|webp);base64,/i.test(dataUrl);
|
|
58459
|
+
}
|
|
58460
|
+
function dataUrlByteSize(dataUrl) {
|
|
58461
|
+
const commaIdx = dataUrl.indexOf(",");
|
|
58462
|
+
const b64 = commaIdx >= 0 ? dataUrl.slice(commaIdx + 1) : dataUrl;
|
|
58463
|
+
return Math.floor(b64.length * 3 / 4);
|
|
58464
|
+
}
|
|
58465
|
+
var MAX_REQUEST_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
58466
|
+
var BRAND_CONTEXT_IMAGE_BUDGET = MAX_REQUEST_IMAGE_BYTES / 2;
|
|
58467
|
+
var PER_BRAND_IMAGE_BYTE_CAP = 600 * 1024;
|
|
58468
|
+
var MAX_TOTAL_IMAGES = 18;
|
|
58409
58469
|
var SYSTEM_PROMPT = `You are an expert advertising art director embedded inside a canvas editor. You create finished advertisement creatives by generating a single complete image \u2014 visuals AND all copy baked directly into the image \u2014 using an AI image model. You do NOT assemble ads from separate text/shape layers.
|
|
58410
58470
|
|
|
58411
58471
|
## Core principle \u2014 ONE image IS the ad
|
|
@@ -58421,7 +58481,7 @@ Check: is the user asking to change something in an ad that already exists on th
|
|
|
58421
58481
|
Triggers: "change the CTA", "update the headline", "change the button text", "make the headline say X", "use a different background", "change the colors", "different photo", etc.
|
|
58422
58482
|
|
|
58423
58483
|
**For a from-scratch (image-generated) ad, the copy is baked into the image \u2014 it CANNOT be patched element-by-element.** To change anything (text or visuals):
|
|
58424
|
-
1. **Find the frame** \u2014 use the frameId from an @mention or
|
|
58484
|
+
1. **Find the frame** \u2014 use the frameId from an @mention if present. Otherwise, for a vague reference like "the previous one", "that image", "the above ad", or "fix it", use the most recent frameId from the "Frames generated in this conversation" list provided to you \u2014 do NOT call \`list_frames()\` for this, the canvas may contain other unrelated frames that list excludes on purpose. Only call \`list_frames()\` when that list is empty or genuinely doesn't cover what the user means.
|
|
58425
58485
|
2. **Duplicate the frame to a fresh, non-overlapping position** (you keep the original intact).
|
|
58426
58486
|
3. Call \`generate_image\` on the duplicate with the SAME prompt as the original, modified with the requested change (new copy text, new visual direction, etc.).
|
|
58427
58487
|
4. Call \`finalize_ad\` on the new frame.
|
|
@@ -58463,11 +58523,13 @@ The \`generate_image\` prompt is the ONLY creative call \u2014 it must produce t
|
|
|
58463
58523
|
- Sign-off / brand name: exact words.
|
|
58464
58524
|
Spell every word exactly as given; do not paraphrase on-image text.
|
|
58465
58525
|
|
|
58466
|
-
2. **BRAND COLORS** (use exact hex values from the Brand Identity context when present): which color is the background, the headline, the CTA button, accents.
|
|
58526
|
+
2. **BRAND COLORS** (use exact hex values from the Brand Identity context when present, by default): which color is the background, the headline, the CTA button, accents.
|
|
58527
|
+
|
|
58528
|
+
3. **TYPOGRAPHY STYLE** (described in words; the image model cannot load font files): describe the feel \u2014 e.g. "bold condensed sans-serif headline, clean body text", matching the brand typography from the Brand Identity context, by default.
|
|
58467
58529
|
|
|
58468
|
-
|
|
58530
|
+
**Brand guidelines vs. the user's request \u2014 precedence:** Brand Identity guidelines (colors, typography, visual style, tone) are defaults that apply where the user hasn't said otherwise. If the user's message explicitly asks for something that conflicts with a guideline \u2014 a different color, a different mood or style, a different tone \u2014 follow the user's explicit instruction for that ad. Do not silently revert to brand colors/style/tone over what the user actually asked for. This does not apply to the brand logo or brand font files (item 6 below) \u2014 those are always reproduced faithfully regardless of the rest of the prompt, since omitting or altering the logo isn't a stylistic choice the user is likely to have intended just by not mentioning it.
|
|
58469
58531
|
|
|
58470
|
-
4. **VISUAL DIRECTION:** composition and layout (where the subject sits, where copy sits), lighting, mood, and emotional tone from the brief and \`imagery\` / \`visualStyle\`.
|
|
58532
|
+
4. **VISUAL DIRECTION:** composition and layout (where the subject sits, where copy sits), lighting, mood, and emotional tone from the brief and \`imagery\` / \`visualStyle\`. If the user described a specific subject, action, pose, expression, object, or animal (e.g. "boy playing guitar, dog watching with one ear up, amused"), you MUST depict that exact scene \u2014 do not invent, substitute, or simplify it into something else (e.g. do not turn "playing guitar" into "walking"). Preserve every specific detail the user gave you; only add the brand/style/spacing layers on top of it.
|
|
58471
58533
|
|
|
58472
58534
|
5. **DIMENSIONS / ASPECT:** describe the format so the composition fits (e.g. "square 1080\xD71080 social post", "landscape 1200\xD7628 banner", "portrait 1080\xD71920 story").
|
|
58473
58535
|
|
|
@@ -58643,8 +58705,12 @@ async function runAgentLoop(opts) {
|
|
|
58643
58705
|
allImageAttachments.push(img);
|
|
58644
58706
|
}
|
|
58645
58707
|
}
|
|
58708
|
+
const fullUserBrief = userMessages.filter(
|
|
58709
|
+
(m) => m.role === "user" && typeof m.content === "string" && m.content.trim().length > 0
|
|
58710
|
+
).map((m) => m.content).join("\n\n");
|
|
58646
58711
|
const augmentedToolCtx = {
|
|
58647
58712
|
...toolCtx,
|
|
58713
|
+
...fullUserBrief ? { originalUserBrief: fullUserBrief } : {},
|
|
58648
58714
|
// Lets long-running tools (video gen) push interim progress lines into the
|
|
58649
58715
|
// same status indicator the loop itself writes to.
|
|
58650
58716
|
onStatus: (message) => onUpdate({ type: "status", message }),
|
|
@@ -58677,6 +58743,12 @@ async function runAgentLoop(opts) {
|
|
|
58677
58743
|
${elementContext}`
|
|
58678
58744
|
});
|
|
58679
58745
|
}
|
|
58746
|
+
const chatFramesContext = getChatCreatedFramesContext();
|
|
58747
|
+
if (chatFramesContext) {
|
|
58748
|
+
messages.push({ role: "system", content: chatFramesContext });
|
|
58749
|
+
}
|
|
58750
|
+
let brandContextBytesUsed = 0;
|
|
58751
|
+
let brandContextImagesUsed = 0;
|
|
58680
58752
|
{
|
|
58681
58753
|
const lastUserText = [...userMessages].reverse().find((m) => m.role === "user")?.content ?? "";
|
|
58682
58754
|
if (lastUserText && (toolCtx.onSearchBrandAssets || toolCtx.onSearchAdCreatives || toolCtx.onListBrandTemplates)) {
|
|
@@ -58690,9 +58762,11 @@ ${elementContext}`
|
|
|
58690
58762
|
toolCtx.onSearchAdCreatives ? toolCtx.onSearchAdCreatives(lastUserText) : Promise.resolve([]),
|
|
58691
58763
|
toolCtx.onListBrandTemplates ? toolCtx.onListBrandTemplates(keywords) : Promise.resolve([])
|
|
58692
58764
|
]);
|
|
58693
|
-
const top2Assets = (assetRes.status === "fulfilled" ? assetRes.value : []).filter((a) => a.previewDataUrl).slice(0, 2);
|
|
58694
|
-
const top2Creatives = (creativeRes.status === "fulfilled" ? creativeRes.value : []).filter((c) => c.previewDataUrl).slice(0, 2);
|
|
58695
|
-
const top2Templates = (templateRes.status === "fulfilled" ? templateRes.value : []).filter(
|
|
58765
|
+
const top2Assets = (assetRes.status === "fulfilled" ? assetRes.value : []).filter((a) => a.previewDataUrl && isRasterImageDataUrl(a.previewDataUrl)).slice(0, 2);
|
|
58766
|
+
const top2Creatives = (creativeRes.status === "fulfilled" ? creativeRes.value : []).filter((c) => c.previewDataUrl && isRasterImageDataUrl(c.previewDataUrl)).slice(0, 2);
|
|
58767
|
+
const top2Templates = (templateRes.status === "fulfilled" ? templateRes.value : []).filter(
|
|
58768
|
+
(t2) => t2.thumbnailDataUrl && isRasterImageDataUrl(t2.thumbnailDataUrl)
|
|
58769
|
+
).slice(0, 2);
|
|
58696
58770
|
console.log(
|
|
58697
58771
|
`[agent:pre-fetch] brand assets=${top2Assets.length}/${assetRes.status === "fulfilled" ? assetRes.value.length : "err"} ad-creatives=${top2Creatives.length}/${creativeRes.status === "fulfilled" ? creativeRes.value.length : "err"} templates=${top2Templates.length}/${templateRes.status === "fulfilled" ? templateRes.value.length : "err"}`
|
|
58698
58772
|
);
|
|
@@ -58710,8 +58784,27 @@ ${elementContext}`
|
|
|
58710
58784
|
dataUrl: t2.thumbnailDataUrl
|
|
58711
58785
|
}))
|
|
58712
58786
|
];
|
|
58713
|
-
|
|
58714
|
-
|
|
58787
|
+
const budgetedPreviews = [];
|
|
58788
|
+
for (const p of previews) {
|
|
58789
|
+
const size = dataUrlByteSize(p.dataUrl);
|
|
58790
|
+
if (size > PER_BRAND_IMAGE_BYTE_CAP) {
|
|
58791
|
+
console.warn(
|
|
58792
|
+
`[agent:pre-fetch] dropping "${p.label}" \u2014 ${Math.round(size / 1024)}KB exceeds the per-image cap (${Math.round(PER_BRAND_IMAGE_BYTE_CAP / 1024)}KB)`
|
|
58793
|
+
);
|
|
58794
|
+
continue;
|
|
58795
|
+
}
|
|
58796
|
+
if (brandContextBytesUsed + size > BRAND_CONTEXT_IMAGE_BUDGET || brandContextImagesUsed >= MAX_TOTAL_IMAGES) {
|
|
58797
|
+
console.warn(
|
|
58798
|
+
`[agent:pre-fetch] dropping "${p.label}" \u2014 brand-context image budget exhausted`
|
|
58799
|
+
);
|
|
58800
|
+
continue;
|
|
58801
|
+
}
|
|
58802
|
+
brandContextBytesUsed += size;
|
|
58803
|
+
brandContextImagesUsed += 1;
|
|
58804
|
+
budgetedPreviews.push(p);
|
|
58805
|
+
}
|
|
58806
|
+
if (budgetedPreviews.length > 0) {
|
|
58807
|
+
budgetedPreviews.forEach(
|
|
58715
58808
|
(p) => console.log(
|
|
58716
58809
|
`[agent:pre-fetch] injecting "${p.label}" as base64 image \u2014 ${Math.round(p.dataUrl.length / 1024)}KB (${p.dataUrl.slice(
|
|
58717
58810
|
0,
|
|
@@ -58722,7 +58815,7 @@ ${elementContext}`
|
|
|
58722
58815
|
messages.push({
|
|
58723
58816
|
role: "system",
|
|
58724
58817
|
content: `BRAND REFERENCES (auto-fetched, up to 2 per category):
|
|
58725
|
-
${
|
|
58818
|
+
${budgetedPreviews.map((p) => `\u2022 ${p.label}`).join(
|
|
58726
58819
|
"\n"
|
|
58727
58820
|
)}
|
|
58728
58821
|
|
|
@@ -58733,9 +58826,9 @@ Visual previews follow in the next message. Use them as context: match their sty
|
|
|
58733
58826
|
content: [
|
|
58734
58827
|
{
|
|
58735
58828
|
type: "text",
|
|
58736
|
-
text: `Brand reference previews (${
|
|
58829
|
+
text: `Brand reference previews (${budgetedPreviews.length} total):`
|
|
58737
58830
|
},
|
|
58738
|
-
...
|
|
58831
|
+
...budgetedPreviews.map((p) => ({
|
|
58739
58832
|
type: "image_url",
|
|
58740
58833
|
image_url: { url: p.dataUrl }
|
|
58741
58834
|
}))
|
|
@@ -58743,17 +58836,57 @@ Visual previews follow in the next message. Use them as context: match their sty
|
|
|
58743
58836
|
});
|
|
58744
58837
|
messages.push({
|
|
58745
58838
|
role: "assistant",
|
|
58746
|
-
content: `Got it \u2014 I can see ${
|
|
58839
|
+
content: `Got it \u2014 I can see ${budgetedPreviews.length} brand reference(s): ${budgetedPreviews.map((p) => p.label).join(", ")}. I'll use these as visual context for the ad.`
|
|
58747
58840
|
});
|
|
58748
58841
|
}
|
|
58749
58842
|
}
|
|
58750
58843
|
}
|
|
58844
|
+
const frameScreenshotBytes = frameScreenshot ? dataUrlByteSize(frameScreenshot) : 0;
|
|
58845
|
+
const currentSendImagesForBudget = fileAttachments?.filter((a) => a.type === "image" && a.dataUrl) ?? [];
|
|
58846
|
+
const currentSendImageBytes = currentSendImagesForBudget.reduce(
|
|
58847
|
+
(sum2, a) => sum2 + dataUrlByteSize(a.dataUrl),
|
|
58848
|
+
0
|
|
58849
|
+
);
|
|
58850
|
+
const currentSendImageCount = currentSendImagesForBudget.length + (frameScreenshot ? 1 : 0);
|
|
58851
|
+
const remainingBudgetBytes = MAX_REQUEST_IMAGE_BYTES - brandContextBytesUsed;
|
|
58852
|
+
const remainingBudgetImages = MAX_TOTAL_IMAGES - brandContextImagesUsed;
|
|
58853
|
+
if (currentSendImageBytes + frameScreenshotBytes > remainingBudgetBytes || currentSendImageCount > remainingBudgetImages) {
|
|
58854
|
+
const message = "This message is too large to process \u2014 try attaching fewer or smaller images.";
|
|
58855
|
+
console.warn(
|
|
58856
|
+
`[agent:budget] aborting \u2014 current-send content alone (${Math.round(
|
|
58857
|
+
(currentSendImageBytes + frameScreenshotBytes) / 1024
|
|
58858
|
+
)}KB, ${currentSendImageCount} image(s)) exceeds the remaining request budget`
|
|
58859
|
+
);
|
|
58860
|
+
onUpdate({ type: "final", message });
|
|
58861
|
+
return { reply: message, toolActions: [] };
|
|
58862
|
+
}
|
|
58863
|
+
let historicalBudgetBytes = remainingBudgetBytes - currentSendImageBytes - frameScreenshotBytes;
|
|
58864
|
+
let historicalBudgetImages = remainingBudgetImages - currentSendImageCount;
|
|
58865
|
+
const droppedHistoricalMessageIndices = /* @__PURE__ */ new Set();
|
|
58866
|
+
for (let i = userMessages.length - 1; i >= 0; i--) {
|
|
58867
|
+
const imgs = userMessages[i].imageAttachments;
|
|
58868
|
+
if (!imgs || imgs.length === 0)
|
|
58869
|
+
continue;
|
|
58870
|
+
const bytes = imgs.reduce(
|
|
58871
|
+
(sum2, img) => sum2 + dataUrlByteSize(img.dataUrl),
|
|
58872
|
+
0
|
|
58873
|
+
);
|
|
58874
|
+
if (bytes <= historicalBudgetBytes && imgs.length <= historicalBudgetImages) {
|
|
58875
|
+
historicalBudgetBytes -= bytes;
|
|
58876
|
+
historicalBudgetImages -= imgs.length;
|
|
58877
|
+
} else {
|
|
58878
|
+
droppedHistoricalMessageIndices.add(i);
|
|
58879
|
+
console.warn(
|
|
58880
|
+
`[agent:budget] dropping ${imgs.length} historical image(s) from an earlier turn (${Math.round(bytes / 1024)}KB) \u2014 request budget exceeded`
|
|
58881
|
+
);
|
|
58882
|
+
}
|
|
58883
|
+
}
|
|
58751
58884
|
for (let i = 0; i < userMessages.length; i++) {
|
|
58752
58885
|
const msg = userMessages[i];
|
|
58753
58886
|
const isLast = i === userMessages.length - 1;
|
|
58754
58887
|
const lastMsgImages = isLast && fileAttachments ? fileAttachments.filter((a) => a.type === "image" && a.dataUrl) : [];
|
|
58755
58888
|
const lastMsgPdfs = isLast && fileAttachments ? fileAttachments.filter((a) => a.type === "pdf" && a.dataUrl) : [];
|
|
58756
|
-
const historicalImages2 = msg.imageAttachments ?? [];
|
|
58889
|
+
const historicalImages2 = droppedHistoricalMessageIndices.has(i) ? [] : msg.imageAttachments ?? [];
|
|
58757
58890
|
const hasImages = historicalImages2.length > 0 || lastMsgImages.length > 0;
|
|
58758
58891
|
const hasFrameScreenshot = isLast && !!frameScreenshot;
|
|
58759
58892
|
const hasTextFiles = isLast && fileAttachments && fileAttachments.some((a) => a.type === "text" || a.type === "docx");
|
|
@@ -58839,7 +58972,16 @@ ${f.textContent}`).join("\n\n")}`;
|
|
|
58839
58972
|
if (lastCreatedFrameId) {
|
|
58840
58973
|
adGenShimmerStore.stopGenerating(lastCreatedFrameId);
|
|
58841
58974
|
}
|
|
58842
|
-
return {
|
|
58975
|
+
return {
|
|
58976
|
+
reply,
|
|
58977
|
+
toolActions,
|
|
58978
|
+
frameGeneration: imageGenData ? {
|
|
58979
|
+
frameId: imageGenData.frameId,
|
|
58980
|
+
prompt: imageGenData.prompt,
|
|
58981
|
+
referenceImageIndex: imageGenData.referenceImageIndex,
|
|
58982
|
+
userBrief: fullUserBrief
|
|
58983
|
+
} : void 0
|
|
58984
|
+
};
|
|
58843
58985
|
}
|
|
58844
58986
|
messages.push({
|
|
58845
58987
|
role: "assistant",
|
|
@@ -58969,7 +59111,8 @@ ${f.textContent}`).join("\n\n")}`;
|
|
|
58969
59111
|
y: result.data.y,
|
|
58970
59112
|
width: result.data.width,
|
|
58971
59113
|
height: result.data.height,
|
|
58972
|
-
referenceImageIndex: result.data.referenceImageIndex
|
|
59114
|
+
referenceImageIndex: result.data.referenceImageIndex,
|
|
59115
|
+
prompt: parsedArgs.prompt ?? ""
|
|
58973
59116
|
};
|
|
58974
59117
|
if (onAfterImageGen) {
|
|
58975
59118
|
onAfterImageGen();
|
|
@@ -59031,25 +59174,45 @@ Use the frameId="${imageGenData.frameId}" for all elements. Keep all elements wi
|
|
|
59031
59174
|
]);
|
|
59032
59175
|
const assetSearchResult = result;
|
|
59033
59176
|
if (searchToolNames.has(name) && result.success && assetSearchResult.previewDataUrls?.length) {
|
|
59034
|
-
const
|
|
59035
|
-
|
|
59036
|
-
|
|
59037
|
-
|
|
59038
|
-
|
|
59039
|
-
|
|
59040
|
-
|
|
59041
|
-
|
|
59042
|
-
|
|
59043
|
-
|
|
59044
|
-
|
|
59045
|
-
|
|
59046
|
-
|
|
59047
|
-
|
|
59048
|
-
|
|
59049
|
-
|
|
59050
|
-
|
|
59051
|
-
|
|
59052
|
-
}
|
|
59177
|
+
const budgetedPreviews = [];
|
|
59178
|
+
for (const p of assetSearchResult.previewDataUrls) {
|
|
59179
|
+
const size = dataUrlByteSize(p.dataUrl);
|
|
59180
|
+
if (size > PER_BRAND_IMAGE_BYTE_CAP) {
|
|
59181
|
+
console.warn(
|
|
59182
|
+
`[agent:mid-loop] dropping "${p.name}" \u2014 ${Math.round(size / 1024)}KB exceeds the per-image cap`
|
|
59183
|
+
);
|
|
59184
|
+
continue;
|
|
59185
|
+
}
|
|
59186
|
+
if (brandContextBytesUsed + size > BRAND_CONTEXT_IMAGE_BUDGET || brandContextImagesUsed >= MAX_TOTAL_IMAGES) {
|
|
59187
|
+
console.warn(
|
|
59188
|
+
`[agent:mid-loop] dropping "${p.name}" \u2014 brand-context image budget exhausted`
|
|
59189
|
+
);
|
|
59190
|
+
continue;
|
|
59191
|
+
}
|
|
59192
|
+
brandContextBytesUsed += size;
|
|
59193
|
+
brandContextImagesUsed += 1;
|
|
59194
|
+
budgetedPreviews.push(p);
|
|
59195
|
+
}
|
|
59196
|
+
if (budgetedPreviews.length > 0) {
|
|
59197
|
+
console.log(
|
|
59198
|
+
`[agent:mid-loop] ${name} injecting ${budgetedPreviews.length} preview image(s) as base64 vision content: ${budgetedPreviews.map(
|
|
59199
|
+
(p) => `"${p.name}" (${Math.round(p.dataUrl.length / 1024)}KB)`
|
|
59200
|
+
).join(", ")}`
|
|
59201
|
+
);
|
|
59202
|
+
messages.push({
|
|
59203
|
+
role: "user",
|
|
59204
|
+
content: [
|
|
59205
|
+
{
|
|
59206
|
+
type: "text",
|
|
59207
|
+
text: `Visual previews for the ${budgetedPreviews.length} result(s) from ${name}:`
|
|
59208
|
+
},
|
|
59209
|
+
...budgetedPreviews.map((p) => ({
|
|
59210
|
+
type: "image_url",
|
|
59211
|
+
image_url: { url: p.dataUrl }
|
|
59212
|
+
}))
|
|
59213
|
+
]
|
|
59214
|
+
});
|
|
59215
|
+
}
|
|
59053
59216
|
}
|
|
59054
59217
|
if (name === "generate_image" && result.success && imageGenData) {
|
|
59055
59218
|
const autoFinalizeId = `auto_finalize_${Date.now()}`;
|
|
@@ -59122,7 +59285,11 @@ Use the frameId="${imageGenData.frameId}" for all elements. Keep all elements wi
|
|
|
59122
59285
|
width: imageGenData.width,
|
|
59123
59286
|
height: imageGenData.height
|
|
59124
59287
|
},
|
|
59125
|
-
|
|
59288
|
+
// Full conversation's user turns, not just userMessages[0] —
|
|
59289
|
+
// the actual creative brief is frequently NOT the first
|
|
59290
|
+
// message (e.g. "hi" -> clarifying questions -> the message
|
|
59291
|
+
// that actually describes the ad). See fullUserBrief above.
|
|
59292
|
+
userBrief: fullUserBrief,
|
|
59126
59293
|
originalImageDataUrl,
|
|
59127
59294
|
brandContext,
|
|
59128
59295
|
chatUrl,
|
|
@@ -59223,7 +59390,16 @@ Describe the final ad to the user and stop.`
|
|
|
59223
59390
|
if (lastCreatedFrameId) {
|
|
59224
59391
|
adGenShimmerStore.stopGenerating(lastCreatedFrameId);
|
|
59225
59392
|
}
|
|
59226
|
-
return {
|
|
59393
|
+
return {
|
|
59394
|
+
reply: fallbackReply,
|
|
59395
|
+
toolActions,
|
|
59396
|
+
frameGeneration: imageGenData ? {
|
|
59397
|
+
frameId: imageGenData.frameId,
|
|
59398
|
+
prompt: imageGenData.prompt,
|
|
59399
|
+
referenceImageIndex: imageGenData.referenceImageIndex,
|
|
59400
|
+
userBrief: fullUserBrief
|
|
59401
|
+
} : void 0
|
|
59402
|
+
};
|
|
59227
59403
|
} catch (err) {
|
|
59228
59404
|
if (lastCreatedFrameId) {
|
|
59229
59405
|
adGenShimmerStore.stopGenerating(lastCreatedFrameId);
|
|
@@ -59698,9 +59874,11 @@ var AIChatPanel = React69.forwardRef(
|
|
|
59698
59874
|
const [prompt, setPrompt] = useState60("");
|
|
59699
59875
|
const [isLoading, setIsLoading] = useState60(false);
|
|
59700
59876
|
const [statusText, setStatusText] = useState60("");
|
|
59701
|
-
const [messages, setMessages] = useState60(
|
|
59702
|
-
initialMessages ?? []
|
|
59703
|
-
|
|
59877
|
+
const [messages, setMessages] = useState60(() => {
|
|
59878
|
+
const initial = initialMessages ?? [];
|
|
59879
|
+
resetAndSeedFrameGenerationMemory(initial);
|
|
59880
|
+
return initial;
|
|
59881
|
+
});
|
|
59704
59882
|
const [sessions, setSessions] = useState60(
|
|
59705
59883
|
initialSessions ?? []
|
|
59706
59884
|
);
|
|
@@ -59806,6 +59984,7 @@ var AIChatPanel = React69.forwardRef(
|
|
|
59806
59984
|
saveCurrentSession();
|
|
59807
59985
|
const newSessionId = genId();
|
|
59808
59986
|
setMessages([]);
|
|
59987
|
+
resetAndSeedFrameGenerationMemory([]);
|
|
59809
59988
|
setCurrentSessionId(newSessionId);
|
|
59810
59989
|
setIsLoadingSession(false);
|
|
59811
59990
|
setHistoryOpen(false);
|
|
@@ -59823,9 +60002,11 @@ var AIChatPanel = React69.forwardRef(
|
|
|
59823
60002
|
setError(null);
|
|
59824
60003
|
if (session.messages.length > 0) {
|
|
59825
60004
|
setMessages(session.messages);
|
|
60005
|
+
resetAndSeedFrameGenerationMemory(session.messages);
|
|
59826
60006
|
setIsLoadingSession(false);
|
|
59827
60007
|
} else {
|
|
59828
60008
|
setMessages([]);
|
|
60009
|
+
resetAndSeedFrameGenerationMemory([]);
|
|
59829
60010
|
setIsLoadingSession(true);
|
|
59830
60011
|
if (onSessionSwitch) {
|
|
59831
60012
|
onSessionSwitch(session.id);
|
|
@@ -60424,7 +60605,8 @@ var AIChatPanel = React69.forwardRef(
|
|
|
60424
60605
|
content: result.reply,
|
|
60425
60606
|
timestamp: /* @__PURE__ */ new Date(),
|
|
60426
60607
|
toolActions: result.toolActions.length > 0 ? result.toolActions : void 0,
|
|
60427
|
-
reviewFeedback: lastReviewFeedbackRef.current ?? void 0
|
|
60608
|
+
reviewFeedback: lastReviewFeedbackRef.current ?? void 0,
|
|
60609
|
+
generatedFrame: result.frameGeneration
|
|
60428
60610
|
};
|
|
60429
60611
|
const updatedMessagesAgent = [...nextMessages, assistantMsgAgent];
|
|
60430
60612
|
setMessages(updatedMessagesAgent);
|
|
@@ -60498,6 +60680,7 @@ var AIChatPanel = React69.forwardRef(
|
|
|
60498
60680
|
});
|
|
60499
60681
|
if (targetSessionId === currentSessionIdRef.current) {
|
|
60500
60682
|
setMessages(msgs);
|
|
60683
|
+
resetAndSeedFrameGenerationMemory(msgs);
|
|
60501
60684
|
setIsLoadingSession(false);
|
|
60502
60685
|
}
|
|
60503
60686
|
},
|