@orangecatai/adgen-canvas 0.0.14 → 0.0.15

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,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.14", 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.15", DEV: true };
3
3
 
4
4
  export {
5
5
  define_import_meta_env_default
6
6
  };
7
- //# sourceMappingURL=chunk-3OPVOEOA.js.map
7
+ //# sourceMappingURL=chunk-EL4HTLYE.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  define_import_meta_env_default
3
- } from "./chunk-3OPVOEOA.js";
3
+ } from "./chunk-EL4HTLYE.js";
4
4
  import {
5
5
  __publicField
6
6
  } from "./chunk-XDFCUUT6.js";
@@ -5150,7 +5150,7 @@ var parseFileContents = async (blob) => {
5150
5150
  let contents;
5151
5151
  if (blob.type === MIME_TYPES6.png) {
5152
5152
  try {
5153
- return await (await import("./data/image-NKXZX2BO.js")).decodePngMetadata(blob);
5153
+ return await (await import("./data/image-AVR5GHYZ.js")).decodePngMetadata(blob);
5154
5154
  } catch (error) {
5155
5155
  if (error.message === "INVALID") {
5156
5156
  throw new ImageSceneDataError(
@@ -5606,4 +5606,4 @@ export {
5606
5606
  createFile,
5607
5607
  normalizeFile
5608
5608
  };
5609
- //# sourceMappingURL=chunk-W3WRQE6Q.js.map
5609
+ //# sourceMappingURL=chunk-FGSZO5WI.js.map
@@ -2,12 +2,12 @@ import {
2
2
  decodePngMetadata,
3
3
  encodePngMetadata,
4
4
  getTEXtChunk
5
- } from "../chunk-W3WRQE6Q.js";
6
- import "../chunk-3OPVOEOA.js";
5
+ } from "../chunk-FGSZO5WI.js";
6
+ import "../chunk-EL4HTLYE.js";
7
7
  import "../chunk-XDFCUUT6.js";
8
8
  export {
9
9
  decodePngMetadata,
10
10
  encodePngMetadata,
11
11
  getTEXtChunk
12
12
  };
13
- //# sourceMappingURL=image-NKXZX2BO.js.map
13
+ //# sourceMappingURL=image-AVR5GHYZ.js.map
package/dist/dev/index.js CHANGED
@@ -67,10 +67,10 @@ import {
67
67
  serializeAsJSON,
68
68
  serializeLibraryAsJSON,
69
69
  strokeRectWithRotation_simple
70
- } from "./chunk-W3WRQE6Q.js";
70
+ } from "./chunk-FGSZO5WI.js";
71
71
  import {
72
72
  define_import_meta_env_default
73
- } from "./chunk-3OPVOEOA.js";
73
+ } from "./chunk-EL4HTLYE.js";
74
74
  import {
75
75
  en_default
76
76
  } from "./chunk-IFMURN5W.js";
@@ -9770,7 +9770,7 @@ var exportCanvas = async (type, elements, appState, files, {
9770
9770
  let blob = canvasToBlob(tempCanvas);
9771
9771
  if (appState.exportEmbedScene) {
9772
9772
  blob = blob.then(
9773
- (blob2) => import("./data/image-NKXZX2BO.js").then(
9773
+ (blob2) => import("./data/image-AVR5GHYZ.js").then(
9774
9774
  ({ encodePngMetadata: encodePngMetadata2 }) => encodePngMetadata2({
9775
9775
  blob: blob2,
9776
9776
  metadata: serializeAsJSON(elements, appState, files, "local")
@@ -26144,13 +26144,31 @@ var TemplateBuilderPanelHost = ({
26144
26144
  }
26145
26145
  const frame = framesToSave[0];
26146
26146
  const frameChildren = getFrameChildren5(allElements, frame.id);
26147
+ const files = app.files;
26148
+ const templateFiles = {};
26149
+ for (const el of frameChildren) {
26150
+ const imgEl = el;
26151
+ if (el.type === "image" && imgEl.fileId) {
26152
+ const file2 = files[imgEl.fileId];
26153
+ if (file2?.dataURL) {
26154
+ templateFiles[imgEl.fileId] = {
26155
+ dataURL: file2.dataURL,
26156
+ mimeType: file2.mimeType
26157
+ };
26158
+ }
26159
+ }
26160
+ }
26147
26161
  onSaveTemplate({
26148
26162
  name: panelState.templateName,
26149
26163
  campaignTag: panelState.campaignTag || void 0,
26150
26164
  label: `${Math.round(frame.width)}x${Math.round(frame.height)}`,
26151
26165
  width: Math.round(frame.width),
26152
26166
  height: Math.round(frame.height),
26153
- canvasJson: JSON.stringify({ frame: { ...frame }, elements: frameChildren })
26167
+ canvasJson: JSON.stringify({
26168
+ frame: { ...frame },
26169
+ elements: frameChildren,
26170
+ ...Object.keys(templateFiles).length > 0 ? { files: templateFiles } : {}
26171
+ })
26154
26172
  });
26155
26173
  templateBuilderStore.close();
26156
26174
  };
@@ -51432,12 +51450,13 @@ function execAddText(args, ctx) {
51432
51450
  statusMessage: "Failed: missing frameId"
51433
51451
  };
51434
51452
  }
51453
+ const resolvedFontFamily = ctx.brandBodyFontId ?? ctx.brandHeadlineFontId ?? args.fontFamily ?? 2;
51435
51454
  const textEl = newTextElement6({
51436
51455
  x: args.x,
51437
51456
  y: args.y,
51438
51457
  text: args.text,
51439
51458
  fontSize: args.fontSize || 20,
51440
- fontFamily: args.fontFamily || 2,
51459
+ fontFamily: resolvedFontFamily,
51441
51460
  strokeColor: args.strokeColor || "#000000",
51442
51461
  textAlign: args.textAlign || "left",
51443
51462
  width: args.width,
@@ -51910,8 +51929,7 @@ async function execGenerateHtmlBanner(args, ctx) {
51910
51929
  y: Math.round(frameY),
51911
51930
  width: frameW,
51912
51931
  height: frameH,
51913
- elementCount: newEls.length,
51914
- screenshot: screenshot ?? void 0
51932
+ elementCount: newEls.length
51915
51933
  },
51916
51934
  statusMessage: `Banner updated: ${newEls.length} elements in frame "${args.name || "Banner"}" (${frameW}\xD7${frameH})`,
51917
51935
  screenshot: screenshot ?? void 0
@@ -51948,7 +51966,7 @@ async function execFinalizeAd(args, ctx) {
51948
51966
  const screenshot = await captureFrameScreenshot(ctx.excalidrawAPI, frameId);
51949
51967
  return {
51950
51968
  success: true,
51951
- data: { frameId, screenshot: screenshot ?? void 0 },
51969
+ data: { frameId },
51952
51970
  statusMessage: "Ad finalized, ready for review",
51953
51971
  screenshot: screenshot ?? void 0
51954
51972
  };
@@ -52051,6 +52069,18 @@ async function execLoadTemplateIntoFrame(args, ctx) {
52051
52069
  ];
52052
52070
  const merged = syncMovedIndices7(mergedArr, arrayToMap30(mergedArr));
52053
52071
  ctx.excalidrawAPI.updateScene({ elements: merged });
52072
+ const templateFiles = parsed.files;
52073
+ if (templateFiles && Object.keys(templateFiles).length > 0) {
52074
+ ctx.excalidrawAPI.addFiles(
52075
+ Object.entries(templateFiles).map(([fileId, file2]) => ({
52076
+ id: fileId,
52077
+ dataURL: file2.dataURL,
52078
+ mimeType: file2.mimeType,
52079
+ created: Date.now(),
52080
+ lastRetrieved: Date.now()
52081
+ }))
52082
+ );
52083
+ }
52054
52084
  if (templateId) {
52055
52085
  const frameEl = ctx.excalidrawAPI.getSceneElements().find((el) => el.id === frameId);
52056
52086
  if (frameEl) {
@@ -52254,18 +52284,20 @@ async function execFillTemplateSlots(args, ctx) {
52254
52284
  return imageReplacements.get(index);
52255
52285
  }
52256
52286
  let mutation = {};
52257
- const makeTextMutation = (value) => {
52287
+ const makeTextMutation = (value, useHeadlineFont = false) => {
52258
52288
  const { fontSize, wrappedText, height, autoResize } = fitFontSize(value, el);
52289
+ const brandFontId = useHeadlineFont ? ctx.brandHeadlineFontId ?? ctx.brandBodyFontId : ctx.brandBodyFontId;
52259
52290
  return {
52260
52291
  text: wrappedText,
52261
52292
  originalText: value,
52262
52293
  fontSize,
52263
52294
  height,
52264
- autoResize
52295
+ autoResize,
52296
+ ...brandFontId !== void 0 ? { fontFamily: brandFontId } : {}
52265
52297
  };
52266
52298
  };
52267
52299
  if (slotType === "headline" && args.headline) {
52268
- mutation = makeTextMutation(args.headline);
52300
+ mutation = makeTextMutation(args.headline, true);
52269
52301
  } else if (slotType === "subhead" && args.subhead) {
52270
52302
  mutation = makeTextMutation(args.subhead);
52271
52303
  } else if (slotType === "cta" && args.cta) {
@@ -52491,7 +52523,11 @@ When the design needs a full-frame photorealistic background (a scene, landscape
52491
52523
  Use hex (\`#rrggbb\`) or \`rgba(r,g,b,a)\`. No named colors (no \`red\`, \`blue\`). CSS gradients (\`linear-gradient\`, \`radial-gradient\`) are allowed.
52492
52524
 
52493
52525
  ### 7. Fonts
52494
- If the Brand Identity context specifies fonts, use those exact font-family names in your HTML \`<style>\` block (e.g. \`font-family: "Brand Font Name", Arial, sans-serif\`). Otherwise default to web-safe: \`Arial\`, \`"Helvetica Neue"\`, \`Helvetica\`, \`sans-serif\`. Do not use serif fonts unless the brand explicitly requires them.
52526
+ **MANDATORY \u2014 Brand font enforcement:**
52527
+ - If the Brand Identity context specifies fonts with a registered fontFamilyId, you MUST use those fonts everywhere. Do NOT use any other font \u2014 no Arial, no Helvetica, no web-safe fallbacks as primary fonts.
52528
+ - In HTML \`<style>\` blocks: use the exact brand font-family name (e.g. \`font-family: "Brand Font Name", sans-serif\`). The canvas renderer maps this name to the registered font automatically.
52529
+ - For \`add_text\` calls: the system enforces the brand fontFamilyId automatically \u2014 you do not need to pass a fontFamily param.
52530
+ - Only fall back to web-safe fonts (\`Arial\`, \`"Helvetica Neue"\`, \`sans-serif\`) when the Brand Identity context has no font specified at all.
52495
52531
 
52496
52532
  ### 9. No unsupported features
52497
52533
  No \`transform\`, \`animation\`, \`transition\`, \`filter\`, \`clip-path\`, \`<img>\` tags, or JavaScript. No \`box-shadow\`.
@@ -52590,8 +52626,10 @@ ASSET USE MANDATE: If search_brand_assets() returns any results, you MUST incorp
52590
52626
 
52591
52627
  IMAGE GENERATION SCOPE: generate_image is only for supplementary content images (backgrounds, lifestyle photography) where no brand asset matches AND the brief explicitly requires a photo or illustration. For solid-color backgrounds or brand-asset-only ads, skip generate_image entirely.
52592
52628
 
52593
- TEMPLATE BANK: When the user references a campaign, asks to "use a template", or requests multiple ad sizes, use this workflow:
52594
- 1. Call \`list_brand_templates()\` to see available templates
52629
+ TEMPLATE BANK: Whenever the user asks to create, make, or build an ad \u2014 even without mentioning templates \u2014 call \`list_brand_templates()\` as your very first action before asking any clarifying questions. Use the returned catalog to ask smarter, more specific questions (e.g. "I see you have a Noida Campaign template \u2014 should I use that, or build from scratch?"). If multiple templates could match, ask which one to use. If none match or the user prefers no template, proceed with HTML generation. Only skip this tool call for messages that are clearly not ad-creation requests (greetings, questions, edits to existing canvas elements, etc.).
52630
+
52631
+ Template workflow once you know which template to use:
52632
+ 1. Call \`list_brand_templates()\` to see available templates (already done above)
52595
52633
  2. Call \`load_template_into_frame(frameId, variantId)\` to insert the template
52596
52634
  3. Call \`get_frame_elements(frameId)\` to inspect which slot types exist (headline, subhead, cta, background, product_image, logo, etc.)
52597
52635
  4. For each **image slot** found (product_image, logo): call \`search_brand_assets()\` to look up the asset. Pass the returned \`blobUrl\` as \`product_image_url\` or \`logo_url\` to \`fill_template_slots\`. Only call \`generate_image\` if no matching asset exists in the bank.
@@ -52714,7 +52752,7 @@ function buildBrandContextMessage(ctx) {
52714
52752
  const { headline, body } = ctx.typography;
52715
52753
  if (headline?.family) {
52716
52754
  const sizeHint = headline.sizeScale ? ` (suggested size: ${SIZE_SCALE_MAP[headline.sizeScale] ?? headline.sizeScale})` : "";
52717
- const idNote = headline.fontFamilyId ? ` \u2014 custom font registered in canvas; use \`font-family: "${headline.family}", Arial, sans-serif\` in your HTML CSS` : ` \u2014 no custom font file loaded; use \`font-family: "${headline.family}", Arial, sans-serif\` in your HTML CSS (will render as Arial on canvas)`;
52755
+ const idNote = headline.fontFamilyId ? ` \u2014 MANDATORY: use \`font-family: "${headline.family}", sans-serif\` in ALL HTML CSS for headlines. Do NOT use any other font.` : ` \u2014 no custom font file loaded; use \`font-family: "${headline.family}", Arial, sans-serif\` in your HTML CSS (will render as Arial on canvas)`;
52718
52756
  const fileNote = headline.fontName ? ` [file: ${headline.fontName}]` : "";
52719
52757
  lines.push(
52720
52758
  `**Headline font**: ${headline.family}${headline.weight ? ` weight ${headline.weight}` : ""}${sizeHint}${idNote}${fileNote}`
@@ -52722,7 +52760,7 @@ function buildBrandContextMessage(ctx) {
52722
52760
  }
52723
52761
  if (body?.family) {
52724
52762
  const sizeHint = body.sizeScale ? ` (suggested size: ${SIZE_SCALE_MAP[body.sizeScale] ?? body.sizeScale})` : "";
52725
- const idNote = body.fontFamilyId ? ` \u2014 custom font registered in canvas; use \`font-family: "${body.family}", Arial, sans-serif\` in your HTML CSS` : ` \u2014 no custom font file loaded; use \`font-family: "${body.family}", Arial, sans-serif\` in your HTML CSS (will render as Arial on canvas)`;
52763
+ const idNote = body.fontFamilyId ? ` \u2014 MANDATORY: use \`font-family: "${body.family}", sans-serif\` in ALL HTML CSS for body copy, subheads, CTAs, and any other text. Do NOT use any other font.` : ` \u2014 no custom font file loaded; use \`font-family: "${body.family}", Arial, sans-serif\` in your HTML CSS (will render as Arial on canvas)`;
52726
52764
  const fileNote = body.fontName ? ` [file: ${body.fontName}]` : "";
52727
52765
  lines.push(
52728
52766
  `**Body font**: ${body.family}${body.weight ? ` weight ${body.weight}` : ""}${sizeHint}${idNote}${fileNote}`
@@ -52826,7 +52864,9 @@ async function runAgentLoop(opts) {
52826
52864
  ...Object.keys(customFontMap).length ? { customFontMap } : {},
52827
52865
  ...allImageAttachments.length ? { fileAttachments: allImageAttachments } : {},
52828
52866
  ...brandContext?.typography?.headline?.fontName ? { brandHeadlineFontName: brandContext.typography.headline.fontName } : {},
52829
- ...brandContext?.typography?.body?.fontName ? { brandBodyFontName: brandContext.typography.body.fontName } : {}
52867
+ ...brandContext?.typography?.body?.fontName ? { brandBodyFontName: brandContext.typography.body.fontName } : {},
52868
+ ...brandContext?.typography?.headline?.fontFamilyId ? { brandHeadlineFontId: brandContext.typography.headline.fontFamilyId } : {},
52869
+ ...brandContext?.typography?.body?.fontFamilyId ? { brandBodyFontId: brandContext.typography.body.fontFamilyId } : {}
52830
52870
  };
52831
52871
  const messages = [{ role: "system", content: SYSTEM_PROMPT }];
52832
52872
  if (brandContext) {
@@ -52953,9 +52993,7 @@ ${f.textContent}`).join("\n\n")}`;
52953
52993
  statusMessage: error || "Insufficient credits for image generation"
52954
52994
  };
52955
52995
  const gatedAction = {
52956
- toolName: name,
52957
- args: parsedArgs,
52958
- result: gatedResult
52996
+ result: { success: gatedResult.success, statusMessage: gatedResult.statusMessage }
52959
52997
  };
52960
52998
  toolActions.push(gatedAction);
52961
52999
  onUpdate({
@@ -52981,9 +53019,7 @@ ${f.textContent}`).join("\n\n")}`;
52981
53019
  statusMessage: "Blocked: template already provides text slots"
52982
53020
  };
52983
53021
  const blockedAction = {
52984
- toolName: name,
52985
- args: parsedArgs,
52986
- result: blockedResult
53022
+ result: { success: blockedResult.success, statusMessage: blockedResult.statusMessage }
52987
53023
  };
52988
53024
  toolActions.push(blockedAction);
52989
53025
  onUpdate({
@@ -53018,9 +53054,7 @@ ${f.textContent}`).join("\n\n")}`;
53018
53054
  }
53019
53055
  }
53020
53056
  const action = {
53021
- toolName: name,
53022
- args: parsedArgs,
53023
- result
53057
+ result: { success: result.success, statusMessage: result.statusMessage }
53024
53058
  };
53025
53059
  toolActions.push(action);
53026
53060
  onUpdate({