@orangecatai/adgen-canvas 0.0.35 → 0.0.37

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  define_import_meta_env_default
3
- } from "./chunk-AVI2TDX6.js";
3
+ } from "./chunk-X5D27DN4.js";
4
4
  import {
5
5
  __publicField
6
6
  } from "./chunk-XDFCUUT6.js";
@@ -5155,7 +5155,7 @@ var parseFileContents = async (blob) => {
5155
5155
  let contents;
5156
5156
  if (blob.type === MIME_TYPES6.png) {
5157
5157
  try {
5158
- return await (await import("./data/image-JWQCODTZ.js")).decodePngMetadata(blob);
5158
+ return await (await import("./data/image-6PRWOWBV.js")).decodePngMetadata(blob);
5159
5159
  } catch (error) {
5160
5160
  if (error.message === "INVALID") {
5161
5161
  throw new ImageSceneDataError(
@@ -5607,4 +5607,4 @@ export {
5607
5607
  createFile,
5608
5608
  normalizeFile
5609
5609
  };
5610
- //# sourceMappingURL=chunk-KJZTX4HK.js.map
5610
+ //# sourceMappingURL=chunk-2TI3DKZO.js.map
@@ -1,7 +1,7 @@
1
1
  // <define:import.meta.env>
2
- var define_import_meta_env_default = { MODE: "development", VITE_APP_GEMINI_API_KEY: "", VITE_APP_OPENROUTER_API_KEY: "", VITE_APP_BACKEND_V2_GET_URL: "https://json-dev.excalidraw.com/api/v2/", VITE_APP_BACKEND_V2_POST_URL: "https://json-dev.excalidraw.com/api/v2/post/", VITE_APP_LIBRARY_URL: "https://libraries.excalidraw.com", VITE_APP_LIBRARY_BACKEND: "https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries", VITE_APP_WS_SERVER_URL: "http://localhost:3002", VITE_APP_PLUS_LP: "https://plus.excalidraw.com", VITE_APP_PLUS_APP: "http://localhost:3000", VITE_APP_AI_BACKEND: "http://localhost:3016", VITE_APP_FIREBASE_CONFIG: '{"apiKey":"AIzaSyCMkxA60XIW8KbqMYL7edC4qT5l4qHX2h8","authDomain":"excalidraw-oss-dev.firebaseapp.com","projectId":"excalidraw-oss-dev","storageBucket":"excalidraw-oss-dev.appspot.com","messagingSenderId":"664559512677","appId":"1:664559512677:web:a385181f2928d328a7aa8c"}', VITE_APP_DEV_DISABLE_LIVE_RELOAD: "", VITE_APP_ENABLE_TRACKING: "true", FAST_REFRESH: "false", VITE_APP_PORT: "3001", VITE_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX: "", VITE_APP_COLLAPSE_OVERLAY: "true", VITE_APP_ENABLE_ESLINT: "true", VITE_APP_ENABLE_PWA: "false", VITE_APP_PLUS_EXPORT_PUBLIC_KEY: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2g5T+Rub6Kbf1Mf57t0\n7r2zeHuVg4dla3r5ryXMswtzz6x767octl6oLThn33mQsPSy3GKglFZoCTXJR4ij\nba8SxB04sL/N8eRrKja7TFWjCVtRwTTfyy771NYYNFVJclkxHyE5qw4m27crHF1y\nUNWEjuqNMi/lwAErS9fFa2oJlWyT8U7zzv/5kQREkxZI6y9v0AF3qcbsy2731FnD\ns9ChJvOUW9toIab2gsIdrKW8ZNpu084ZFVKb6LNjvIXI1Se4oMTHeszXzNptzlot\nkdxxjOoaQMAyfljFSot1F1FlU6MQlag7UnFGvFjRHN1JI5q4K+n3a67DX+TMyRqS\nHQIDAQAB", VITE_APP_DISABLE_PREVENT_UNLOAD: "", PKG_NAME: "@orangecatai/adgen-canvas", PKG_VERSION: "0.0.35", DEV: true };
2
+ var define_import_meta_env_default = { MODE: "development", VITE_APP_GEMINI_API_KEY: "", VITE_APP_OPENROUTER_API_KEY: "", VITE_APP_BACKEND_V2_GET_URL: "https://json-dev.excalidraw.com/api/v2/", VITE_APP_BACKEND_V2_POST_URL: "https://json-dev.excalidraw.com/api/v2/post/", VITE_APP_LIBRARY_URL: "https://libraries.excalidraw.com", VITE_APP_LIBRARY_BACKEND: "https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries", VITE_APP_WS_SERVER_URL: "http://localhost:3002", VITE_APP_PLUS_LP: "https://plus.excalidraw.com", VITE_APP_PLUS_APP: "http://localhost:3000", VITE_APP_AI_BACKEND: "http://localhost:3016", VITE_APP_FIREBASE_CONFIG: '{"apiKey":"AIzaSyCMkxA60XIW8KbqMYL7edC4qT5l4qHX2h8","authDomain":"excalidraw-oss-dev.firebaseapp.com","projectId":"excalidraw-oss-dev","storageBucket":"excalidraw-oss-dev.appspot.com","messagingSenderId":"664559512677","appId":"1:664559512677:web:a385181f2928d328a7aa8c"}', VITE_APP_DEV_DISABLE_LIVE_RELOAD: "", VITE_APP_ENABLE_TRACKING: "true", FAST_REFRESH: "false", VITE_APP_PORT: "3001", VITE_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX: "", VITE_APP_COLLAPSE_OVERLAY: "true", VITE_APP_ENABLE_ESLINT: "true", VITE_APP_ENABLE_PWA: "false", VITE_APP_PLUS_EXPORT_PUBLIC_KEY: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2g5T+Rub6Kbf1Mf57t0\n7r2zeHuVg4dla3r5ryXMswtzz6x767octl6oLThn33mQsPSy3GKglFZoCTXJR4ij\nba8SxB04sL/N8eRrKja7TFWjCVtRwTTfyy771NYYNFVJclkxHyE5qw4m27crHF1y\nUNWEjuqNMi/lwAErS9fFa2oJlWyT8U7zzv/5kQREkxZI6y9v0AF3qcbsy2731FnD\ns9ChJvOUW9toIab2gsIdrKW8ZNpu084ZFVKb6LNjvIXI1Se4oMTHeszXzNptzlot\nkdxxjOoaQMAyfljFSot1F1FlU6MQlag7UnFGvFjRHN1JI5q4K+n3a67DX+TMyRqS\nHQIDAQAB", VITE_APP_DISABLE_PREVENT_UNLOAD: "", PKG_NAME: "@orangecatai/adgen-canvas", PKG_VERSION: "0.0.37", DEV: true };
3
3
 
4
4
  export {
5
5
  define_import_meta_env_default
6
6
  };
7
- //# sourceMappingURL=chunk-AVI2TDX6.js.map
7
+ //# sourceMappingURL=chunk-X5D27DN4.js.map
@@ -2,12 +2,12 @@ import {
2
2
  decodePngMetadata,
3
3
  encodePngMetadata,
4
4
  getTEXtChunk
5
- } from "../chunk-KJZTX4HK.js";
6
- import "../chunk-AVI2TDX6.js";
5
+ } from "../chunk-2TI3DKZO.js";
6
+ import "../chunk-X5D27DN4.js";
7
7
  import "../chunk-XDFCUUT6.js";
8
8
  export {
9
9
  decodePngMetadata,
10
10
  encodePngMetadata,
11
11
  getTEXtChunk
12
12
  };
13
- //# sourceMappingURL=image-JWQCODTZ.js.map
13
+ //# sourceMappingURL=image-6PRWOWBV.js.map
package/dist/dev/index.js CHANGED
@@ -66,10 +66,10 @@ import {
66
66
  serializeAsJSON,
67
67
  serializeLibraryAsJSON,
68
68
  strokeRectWithRotation_simple
69
- } from "./chunk-KJZTX4HK.js";
69
+ } from "./chunk-2TI3DKZO.js";
70
70
  import {
71
71
  define_import_meta_env_default
72
- } from "./chunk-AVI2TDX6.js";
72
+ } from "./chunk-X5D27DN4.js";
73
73
  import {
74
74
  en_default
75
75
  } from "./chunk-LIELFD5T.js";
@@ -9797,7 +9797,7 @@ var exportCanvas = async (type, elements, appState, files, {
9797
9797
  let blob = canvasToBlob(tempCanvas);
9798
9798
  if (appState.exportEmbedScene) {
9799
9799
  blob = blob.then(
9800
- (blob2) => import("./data/image-JWQCODTZ.js").then(
9800
+ (blob2) => import("./data/image-6PRWOWBV.js").then(
9801
9801
  ({ encodePngMetadata: encodePngMetadata2 }) => encodePngMetadata2({
9802
9802
  blob: blob2,
9803
9803
  metadata: serializeAsJSON(elements, appState, files, "local")
@@ -30463,7 +30463,7 @@ var TemplateBuilderPanelHost = ({
30463
30463
  if (!panelState.isOpen) {
30464
30464
  return null;
30465
30465
  }
30466
- const handleSaveTemplate = () => {
30466
+ const handleSaveTemplate = async () => {
30467
30467
  const onSaveTemplate = app.props.onSaveTemplate;
30468
30468
  if (!onSaveTemplate) {
30469
30469
  return;
@@ -30511,7 +30511,27 @@ var TemplateBuilderPanelHost = ({
30511
30511
  }
30512
30512
  }
30513
30513
  }
30514
- onSaveTemplate({
30514
+ let thumbnailDataUrl;
30515
+ try {
30516
+ const blob = await exportToBlob({
30517
+ elements: [...frameChildren, frame],
30518
+ appState: app.state,
30519
+ files,
30520
+ exportingFrame: frame,
30521
+ exportPadding: 0,
30522
+ mimeType: "image/png"
30523
+ });
30524
+ thumbnailDataUrl = await new Promise((resolve, reject) => {
30525
+ const reader = new FileReader();
30526
+ reader.onload = () => resolve(reader.result);
30527
+ reader.onerror = () => reject(reader.error);
30528
+ reader.readAsDataURL(blob);
30529
+ });
30530
+ } catch {
30531
+ alert("Couldn't capture this template frame. Please try again.");
30532
+ return;
30533
+ }
30534
+ await onSaveTemplate({
30515
30535
  name: panelState.templateName,
30516
30536
  campaignTag: panelState.campaignTag || void 0,
30517
30537
  label: `${Math.round(frame.width)}x${Math.round(frame.height)}`,
@@ -30521,7 +30541,8 @@ var TemplateBuilderPanelHost = ({
30521
30541
  frame: { ...frame },
30522
30542
  elements: frameChildren,
30523
30543
  ...Object.keys(templateFiles).length > 0 ? { files: templateFiles } : {}
30524
- })
30544
+ }),
30545
+ thumbnailDataUrl
30525
30546
  });
30526
30547
  templateBuilderStore.close();
30527
30548
  };
@@ -58739,31 +58760,16 @@ var BANNER_TOOLS = [
58739
58760
  type: "function",
58740
58761
  function: {
58741
58762
  name: "list_brand_templates",
58742
- description: `List available brand templates by keyword search across template names, campaign tags, and labels.
58743
-
58744
- MANDATORY SEARCH PROTOCOL \u2014 follow exactly, no shortcuts:
58745
-
58746
- ATTEMPT 1: Generate TWO sets of 10 keywords each from the user's request:
58747
- - Set A (campaign tag variations): 10 synonyms/abbreviations/phrasings of the campaign topic (e.g. for "noida datacenter": ["datacenter", "data center", "dc", "noida dc", "noida datacenter", "data centre", "server farm", "cloud infra", "hyperscale", "colocation"])
58748
- - Set B (template name variations): 10 different name keywords the template might have (e.g. ["noida", "datacenter campaign", "new datacenter", "dc launch", "infrastructure", "cloud", "enterprise", "b2b", "tech", "facility"])
58749
- Merge all 20 into the keywords array. Call list_brand_templates with all 20.
58750
-
58751
- IF templates returned is empty \u2192 ATTEMPT 2 (MANDATORY \u2014 do NOT skip):
58752
- Generate a completely DIFFERENT set of 20 keywords \u2014 do not reuse any from attempt 1. Think laterally: different synonyms, related industry terms, the city name, brand-adjacent words, seasonal terms. Call list_brand_templates again with the new 20.
58753
-
58754
- ONLY after both attempts return empty \u2192 tell the user no matching template was found.
58755
-
58756
- If no search context exists, call with an empty keywords array to list all templates.`,
58763
+ description: "Search saved brand templates semantically against the full ad brief. Results are ranked from captions generated from the actual template frame. Use the returned visual description, campaign fit, dimensions, and ID to decide whether to use a template.",
58757
58764
  parameters: {
58758
58765
  type: "object",
58759
58766
  properties: {
58760
- keywords: {
58761
- type: "array",
58762
- items: { type: "string" },
58763
- description: "Up to 20 keyword variations to search across template names, campaign tags, and labels. All are OR-matched case-insensitively. Generate 10 tag synonyms + 10 name variations from the user's request."
58767
+ brief: {
58768
+ type: "string",
58769
+ description: "The user's complete ad brief, including campaign, subject, style, copy, and requested dimensions when known."
58764
58770
  }
58765
58771
  },
58766
- required: [],
58772
+ required: ["brief"],
58767
58773
  additionalProperties: false
58768
58774
  }
58769
58775
  }
@@ -60450,17 +60456,12 @@ async function execFinalizeAd(args, ctx) {
60450
60456
  };
60451
60457
  }
60452
60458
  async function execListBrandTemplates(args, ctx) {
60453
- let keywords;
60454
- if (Array.isArray(args.keywords) && args.keywords.length > 0) {
60455
- keywords = args.keywords;
60456
- } else if (args.query || args.campaignTag) {
60457
- keywords = [args.query ?? args.campaignTag];
60458
- }
60459
+ const brief = typeof args.brief === "string" ? args.brief : "";
60459
60460
  if (!ctx.onListBrandTemplates) {
60460
60461
  return { success: true, statusMessage: "No template list configured" };
60461
60462
  }
60462
60463
  try {
60463
- const templates = await ctx.onListBrandTemplates(keywords);
60464
+ const templates = await ctx.onListBrandTemplates(brief);
60464
60465
  if (templates.length === 0) {
60465
60466
  return {
60466
60467
  success: true,
@@ -60468,28 +60469,18 @@ async function execListBrandTemplates(args, ctx) {
60468
60469
  statusMessage: "No templates found"
60469
60470
  };
60470
60471
  }
60471
- if (keywords && keywords.length > 0) {
60472
- const kws = keywords.map((k) => k.toLowerCase());
60473
- templates.sort((a, b) => {
60474
- const scoreA = kws.filter(
60475
- (k) => a.name.toLowerCase().includes(k) || (a.campaignTag ?? "").toLowerCase().includes(k) || (a.label ?? "").toLowerCase().includes(k)
60476
- ).length;
60477
- const scoreB = kws.filter(
60478
- (k) => b.name.toLowerCase().includes(k) || (b.campaignTag ?? "").toLowerCase().includes(k) || (b.label ?? "").toLowerCase().includes(k)
60479
- ).length;
60480
- return scoreB - scoreA;
60481
- });
60482
- }
60483
60472
  const list = templates.map((t2) => {
60484
60473
  const dims = t2.width && t2.height ? ` (${t2.width}x${t2.height})` : "";
60485
- return `Template: ${t2.name}${t2.campaignTag ? ` [${t2.campaignTag}]` : ""}${dims} [id:${t2.id}]`;
60474
+ const metadata = t2.visualMetadata;
60475
+ return `Template: ${t2.name}${dims} [id:${t2.id}]
60476
+ Caption: ${metadata?.caption ?? "No caption"}
60477
+ Layout: ${metadata?.layout ?? "No layout description"}
60478
+ Best for: ${(metadata?.useCases ?? []).join(", ") || "unspecified"}`;
60486
60479
  }).join("\n");
60487
- const previews = templates.filter((t2) => t2.thumbnailDataUrl).slice(0, 3).map((t2) => ({ name: t2.name, dataUrl: t2.thumbnailDataUrl }));
60488
60480
  return {
60489
60481
  success: true,
60490
60482
  data: { templates, summary: list },
60491
- statusMessage: `Found ${templates.length} template(s)`,
60492
- previewDataUrls: previews.length > 0 ? previews : void 0
60483
+ statusMessage: `Found ${templates.length} template(s)`
60493
60484
  };
60494
60485
  } catch {
60495
60486
  return {
@@ -60571,6 +60562,13 @@ async function execLoadTemplateIntoFrame(args, ctx) {
60571
60562
  );
60572
60563
  const scaleX = targetFrame.width / (templateFrame.width || 1);
60573
60564
  const scaleY = targetFrame.height / (templateFrame.height || 1);
60565
+ if (Math.abs(scaleX - scaleY) > 0.01) {
60566
+ return {
60567
+ success: false,
60568
+ error: `Template is ${Math.round(templateFrame.width)}x${Math.round(templateFrame.height)} and cannot be stretched into ${Math.round(targetFrame.width)}x${Math.round(targetFrame.height)}. Tell the user you will make the native template size first, then resize it afterward.`,
60569
+ statusMessage: "Template aspect ratio mismatch"
60570
+ };
60571
+ }
60574
60572
  const offsetX = targetFrame.x - templateFrame.x * scaleX;
60575
60573
  const offsetY = targetFrame.y - templateFrame.y * scaleY;
60576
60574
  const importedElements = templateElements.map(
@@ -61145,7 +61143,7 @@ Aim for typography that is crisp and legible with copy correctly spelled.
61145
61143
 
61146
61144
  Brand context is mandatory input, not optional decoration. The generated ad must reflect the brand kit / brand code, the approved copy, and the visual direction \u2014 never generate something arbitrary.
61147
61145
 
61148
- BRAND REFERENCES are auto-fetched and provided to you as visual context at the start of every session (up to 2 brand assets, 2 ad creatives, 2 templates). Check them before deciding how to generate:
61146
+ BRAND REFERENCES are auto-fetched at the start of every session: visual previews for assets/creatives, and text-only semantic captions for up to 2 ranked templates. Check them before deciding how to generate:
61149
61147
 
61150
61148
  - If a **template** clearly fits the user's request \u2192 use it: \`load_template_into_frame\` + \`fill_template_slots\`. Do NOT call \`generate_image\` on a template ad.
61151
61149
  - If a **brand asset or ad creative** is relevant \u2192 pass its image as a reference to \`generate_image\` so the output matches existing brand visuals.
@@ -61153,11 +61151,11 @@ BRAND REFERENCES are auto-fetched and provided to you as visual context at the s
61153
61151
 
61154
61152
  You may still call \`search_brand_assets()\`, \`search_ad_creatives()\`, or \`list_brand_templates()\` explicitly if the user asks for something specific that may not have been in the auto-fetched top 2.
61155
61153
 
61156
- TEMPLATE BANK (when using a template): call \`list_brand_templates()\`, present matches, then:
61154
+ TEMPLATE BANK (when using a template): call \`list_brand_templates()\` with the full brief, present the semantic match, then:
61157
61155
  1. \`load_template_into_frame(frameId, variantId)\`
61158
61156
  2. \`get_frame_elements(frameId)\` to see the slots
61159
61157
  3. \`fill_template_slots(frameId, headline, subhead, cta, background_color, product_image_url, logo_url)\` \u2014 all slots in one call.
61160
- Template text slots are fixed-size boxes \u2014 keep copy tight (headline \u22645 words, subhead \u22648, cta \u22643, signoff \u22644). After \`fill_template_slots\`, the template ad is done \u2014 do NOT call \`generate_image\` or \`generate_html_banner\` on it. The quality review runs automatically.`;
61158
+ Template text slots are fixed-size boxes \u2014 keep copy tight (headline \u22645 words, subhead \u22648, cta \u22643, signoff \u22644). A template's listed dimensions are authoritative: if the user requested an incompatible aspect ratio (for example, a 1:1 template for a 16:9 deliverable), tell them that you will make the ad at the template's native 1:1 size first and later use resize for 16:9. Create the template frame at its native dimensions; never stretch it to the requested ratio. After \`fill_template_slots\`, the template ad is done \u2014 do NOT call \`generate_image\` or \`generate_html_banner\` on it. The quality review runs automatically.`;
61161
61159
  var MAX_ITERATIONS = 15;
61162
61160
  async function callOpenRouter(messages, chatUrl, signal, webSearchEnabled, chatModel, onContentChunk, tools = CANVAS_TOOLS) {
61163
61161
  const baseModel = chatModel || "minimax/minimax-m3";
@@ -61335,15 +61333,10 @@ ${elementContext}`
61335
61333
  {
61336
61334
  const lastUserText = [...userMessages].reverse().find((m) => m.role === "user")?.content ?? "";
61337
61335
  if (lastUserText && (toolCtx.onSearchBrandAssets || toolCtx.onSearchAdCreatives || toolCtx.onListBrandTemplates)) {
61338
- const keywords = [
61339
- ...new Set(
61340
- lastUserText.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 3)
61341
- )
61342
- ].slice(0, 10);
61343
61336
  const [assetRes, creativeRes, templateRes] = await Promise.allSettled([
61344
61337
  toolCtx.onSearchBrandAssets ? toolCtx.onSearchBrandAssets(lastUserText) : Promise.resolve([]),
61345
61338
  toolCtx.onSearchAdCreatives ? toolCtx.onSearchAdCreatives(lastUserText) : Promise.resolve([]),
61346
- toolCtx.onListBrandTemplates ? toolCtx.onListBrandTemplates(keywords) : Promise.resolve([])
61339
+ toolCtx.onListBrandTemplates ? toolCtx.onListBrandTemplates(lastUserText) : Promise.resolve([])
61347
61340
  ]);
61348
61341
  const top2Assets = (assetRes.status === "fulfilled" ? assetRes.value : []).filter(
61349
61342
  (a) => a.previewDataUrl && isRasterImageDataUrl2(a.previewDataUrl)
@@ -61351,9 +61344,7 @@ ${elementContext}`
61351
61344
  const top2Creatives = (creativeRes.status === "fulfilled" ? creativeRes.value : []).filter(
61352
61345
  (c) => c.previewDataUrl && isRasterImageDataUrl2(c.previewDataUrl)
61353
61346
  ).slice(0, 2);
61354
- const top2Templates = (templateRes.status === "fulfilled" ? templateRes.value : []).filter(
61355
- (t2) => t2.thumbnailDataUrl && isRasterImageDataUrl2(t2.thumbnailDataUrl)
61356
- ).slice(0, 2);
61347
+ const top2Templates = (templateRes.status === "fulfilled" ? templateRes.value : []).slice(0, 2);
61357
61348
  console.log(
61358
61349
  `[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"}`
61359
61350
  );
@@ -61365,12 +61356,19 @@ ${elementContext}`
61365
61356
  ...top2Creatives.map((c) => ({
61366
61357
  label: `Ad Creative \u2014 ${c.name}`,
61367
61358
  dataUrl: c.previewDataUrl
61368
- })),
61369
- ...top2Templates.map((t2) => ({
61370
- label: `Template \u2014 ${t2.name}`,
61371
- dataUrl: t2.thumbnailDataUrl
61372
61359
  }))
61373
61360
  ];
61361
+ if (top2Templates.length > 0) {
61362
+ messages.push({
61363
+ role: "system",
61364
+ content: `SEMANTIC TEMPLATE MATCHES (ranked; no template images attached):
61365
+ ${top2Templates.map((t2) => {
61366
+ const dims = t2.width && t2.height ? `${t2.width}x${t2.height}` : "unknown dimensions";
61367
+ return `\u2022 ${t2.name} [id:${t2.id}, ${dims}] \u2014 ${t2.visualMetadata?.caption ?? "No caption"}. Layout: ${t2.visualMetadata?.layout ?? "unspecified"}. Best for: ${(t2.visualMetadata?.useCases ?? []).join(", ") || "unspecified"}.`;
61368
+ }).join("\n")}
61369
+ Use a matching template at its native dimensions. Do not attach its thumbnail or send it to an image model.`
61370
+ });
61371
+ }
61374
61372
  const budgetedPreviews = [];
61375
61373
  for (const p of previews) {
61376
61374
  if (brandContextImagesUsed >= MAX_TOTAL_IMAGES) {
@@ -61726,8 +61724,7 @@ Use the frameId="${imageGenData.frameId}" for all elements. Keep all elements wi
61726
61724
  const searchToolNames = /* @__PURE__ */ new Set([
61727
61725
  "find_brand_asset",
61728
61726
  "search_brand_assets",
61729
- "search_ad_creatives",
61730
- "list_brand_templates"
61727
+ "search_ad_creatives"
61731
61728
  ]);
61732
61729
  const assetSearchResult = result;
61733
61730
  if (searchToolNames.has(name) && result.success && assetSearchResult.previewDataUrls?.length) {