@orangecatai/adgen-canvas 0.0.28 → 0.0.30

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.28", 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.30", DEV: true };
3
3
 
4
4
  export {
5
5
  define_import_meta_env_default
6
6
  };
7
- //# sourceMappingURL=chunk-WELJ6MTO.js.map
7
+ //# sourceMappingURL=chunk-2WAJTVZS.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  define_import_meta_env_default
3
- } from "./chunk-WELJ6MTO.js";
3
+ } from "./chunk-2WAJTVZS.js";
4
4
  import {
5
5
  __publicField
6
6
  } from "./chunk-XDFCUUT6.js";
@@ -5152,7 +5152,7 @@ var parseFileContents = async (blob) => {
5152
5152
  let contents;
5153
5153
  if (blob.type === MIME_TYPES6.png) {
5154
5154
  try {
5155
- return await (await import("./data/image-F5HBZTBB.js")).decodePngMetadata(blob);
5155
+ return await (await import("./data/image-HGVO7LLH.js")).decodePngMetadata(blob);
5156
5156
  } catch (error) {
5157
5157
  if (error.message === "INVALID") {
5158
5158
  throw new ImageSceneDataError(
@@ -5604,4 +5604,4 @@ export {
5604
5604
  createFile,
5605
5605
  normalizeFile
5606
5606
  };
5607
- //# sourceMappingURL=chunk-OTDOAN2W.js.map
5607
+ //# sourceMappingURL=chunk-X6KPARLJ.js.map
@@ -2,12 +2,12 @@ import {
2
2
  decodePngMetadata,
3
3
  encodePngMetadata,
4
4
  getTEXtChunk
5
- } from "../chunk-OTDOAN2W.js";
6
- import "../chunk-WELJ6MTO.js";
5
+ } from "../chunk-X6KPARLJ.js";
6
+ import "../chunk-2WAJTVZS.js";
7
7
  import "../chunk-XDFCUUT6.js";
8
8
  export {
9
9
  decodePngMetadata,
10
10
  encodePngMetadata,
11
11
  getTEXtChunk
12
12
  };
13
- //# sourceMappingURL=image-F5HBZTBB.js.map
13
+ //# sourceMappingURL=image-HGVO7LLH.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-OTDOAN2W.js";
69
+ } from "./chunk-X6KPARLJ.js";
70
70
  import {
71
71
  define_import_meta_env_default
72
- } from "./chunk-WELJ6MTO.js";
72
+ } from "./chunk-2WAJTVZS.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-F5HBZTBB.js").then(
9799
+ (blob2) => import("./data/image-HGVO7LLH.js").then(
9800
9800
  ({ encodePngMetadata: encodePngMetadata2 }) => encodePngMetadata2({
9801
9801
  blob: blob2,
9802
9802
  metadata: serializeAsJSON(elements, appState, files, "local")
@@ -24225,6 +24225,7 @@ async function runReviewerAgent(opts) {
24225
24225
  // imagePlacement retained in opts type for backward compatibility but unused
24226
24226
  // in the image-first flow (the ad is a single full-frame generated image).
24227
24227
  userBrief,
24228
+ originalImageDataUrl,
24228
24229
  brandContext,
24229
24230
  chatUrl,
24230
24231
  reviewerModel = "google/gemini-2.5-flash",
@@ -24244,6 +24245,7 @@ async function runReviewerAgent(opts) {
24244
24245
  const fontPreviewIsBase64 = typeof fontPreviewDataUrl === "string" && fontPreviewDataUrl.startsWith("data:");
24245
24246
  const bodyFontPreviewDataUrl = brandContext?.bodyFontPreviewDataUrl;
24246
24247
  const bodyFontPreviewIsBase64 = typeof bodyFontPreviewDataUrl === "string" && bodyFontPreviewDataUrl.startsWith("data:");
24248
+ const originalImageIsBase64 = typeof originalImageDataUrl === "string" && originalImageDataUrl.startsWith("data:");
24247
24249
  const contextLines = [
24248
24250
  `## Ad Brief (what the user requested)`,
24249
24251
  userBrief,
@@ -24251,6 +24253,13 @@ async function runReviewerAgent(opts) {
24251
24253
  `## Frame dimensions`,
24252
24254
  `Width: ${frame.width}px, Height: ${frame.height}px`
24253
24255
  ];
24256
+ if (originalImageIsBase64) {
24257
+ contextLines.push(
24258
+ ``,
24259
+ `## Original reference image (what the user uploaded)`,
24260
+ `The image the user originally attached is included below (immediately after the ad screenshot). Judge fidelity against it: the generated ad should preserve the subject, composition, and style of this reference unless the brief explicitly asked for a different treatment. Flag it as an issue if the ad has drifted from this reference in ways the brief did not request (wrong subject, different color scheme, altered composition).`
24261
+ );
24262
+ }
24254
24263
  if (brandContext) {
24255
24264
  const brandSummary = buildReviewerBrandSummary(brandContext);
24256
24265
  if (brandSummary) {
@@ -24261,7 +24270,7 @@ async function runReviewerAgent(opts) {
24261
24270
  contextLines.push(
24262
24271
  ``,
24263
24272
  `## Brand logo (reference)`,
24264
- `The brand logo is attached as the second image below (after the ad screenshot). Use it to evaluate whether the logo in the ad matches the brand's actual logo in colour, shape, and typographic treatment.`
24273
+ `The brand logo is attached below (after the ad screenshot${originalImageIsBase64 ? " and the original reference image" : ""}). Use it to evaluate whether the logo in the ad matches the brand's actual logo in colour, shape, and typographic treatment.`
24265
24274
  );
24266
24275
  } else if (logoIsExternalUrl) {
24267
24276
  contextLines.push(
@@ -24292,6 +24301,12 @@ async function runReviewerAgent(opts) {
24292
24301
  image_url: { url: screenshotDataUrl }
24293
24302
  }
24294
24303
  ];
24304
+ if (originalImageIsBase64 && originalImageDataUrl) {
24305
+ userContent.push({
24306
+ type: "image_url",
24307
+ image_url: { url: originalImageDataUrl }
24308
+ });
24309
+ }
24295
24310
  if (logoIsBase64 && logoDataUrl) {
24296
24311
  userContent.push({
24297
24312
  type: "image_url",
@@ -27888,20 +27903,49 @@ async function fetchSegmentBlob(videoPresignedUrl, videoDownloadProxyUrl, signal
27888
27903
  }
27889
27904
  return res.blob();
27890
27905
  }
27906
+ async function readErrorMessage(res, fallback) {
27907
+ try {
27908
+ const body = await res.json();
27909
+ return body?.error ? body.error : `${fallback} (${res.status})`;
27910
+ } catch {
27911
+ return `${fallback} (${res.status})`;
27912
+ }
27913
+ }
27891
27914
  async function uploadMergedVideo(blob, uploadUrl) {
27892
27915
  if (!uploadUrl) {
27893
27916
  return URL.createObjectURL(blob);
27894
27917
  }
27895
- const formData = new FormData();
27896
- formData.append(
27897
- "file",
27898
- new File([blob], "chain-output.mp4", { type: "video/mp4" })
27899
- );
27900
- const res = await fetch(uploadUrl, { method: "POST", body: formData });
27901
- if (!res.ok) {
27902
- throw new Error(`Upload failed (${res.status})`);
27918
+ const contentType = blob.type || "video/mp4";
27919
+ const presignRes = await fetch(uploadUrl, {
27920
+ method: "POST",
27921
+ headers: { "Content-Type": "application/json" },
27922
+ body: JSON.stringify({
27923
+ action: "presign",
27924
+ contentType,
27925
+ sizeBytes: blob.size
27926
+ })
27927
+ });
27928
+ if (!presignRes.ok) {
27929
+ throw new Error(await readErrorMessage(presignRes, "Presign failed"));
27930
+ }
27931
+ const { fileId, uploadUrl: putUrl } = await presignRes.json();
27932
+ const putRes = await fetch(putUrl, {
27933
+ method: "PUT",
27934
+ headers: { "Content-Type": contentType },
27935
+ body: blob
27936
+ });
27937
+ if (!putRes.ok) {
27938
+ throw new Error(`Upload failed (${putRes.status})`);
27903
27939
  }
27904
- const data = await res.json();
27940
+ const finalizeRes = await fetch(uploadUrl, {
27941
+ method: "POST",
27942
+ headers: { "Content-Type": "application/json" },
27943
+ body: JSON.stringify({ action: "finalize", fileId })
27944
+ });
27945
+ if (!finalizeRes.ok) {
27946
+ throw new Error(await readErrorMessage(finalizeRes, "Finalize failed"));
27947
+ }
27948
+ const data = await finalizeRes.json();
27905
27949
  return data.url;
27906
27950
  }
27907
27951
  async function planVideoChain(videoPlanUrl, params, signal) {
@@ -49895,17 +49939,7 @@ var App = class _App extends React63.Component {
49895
49939
  try {
49896
49940
  let videoUrl;
49897
49941
  if (this.props.videoUploadUrl) {
49898
- const formData = new FormData();
49899
- formData.append("file", file2);
49900
- const res = await fetch(this.props.videoUploadUrl, {
49901
- method: "POST",
49902
- body: formData
49903
- });
49904
- if (!res.ok) {
49905
- throw new Error(`Upload failed: ${res.status}`);
49906
- }
49907
- const data = await res.json();
49908
- videoUrl = data.url;
49942
+ videoUrl = await uploadMergedVideo(file2, this.props.videoUploadUrl);
49909
49943
  URL.revokeObjectURL(localBlobUrl);
49910
49944
  } else {
49911
49945
  videoUrl = localBlobUrl;
@@ -56686,6 +56720,7 @@ function execAddText(args, ctx) {
56686
56720
  statusMessage: `Added text "${args.text.slice(0, 40)}${args.text.length > 40 ? "\u2026" : ""}"`
56687
56721
  };
56688
56722
  }
56723
+ var frameGenerationMemory = /* @__PURE__ */ new Map();
56689
56724
  async function execGenerateImage(args, ctx) {
56690
56725
  const frameId = args.frameId;
56691
56726
  const prompt = args.prompt;
@@ -56694,7 +56729,14 @@ async function execGenerateImage(args, ctx) {
56694
56729
  const width = args.width;
56695
56730
  const height = args.height;
56696
56731
  const referenceImageIndex = args.referenceImageIndex;
56697
- const referenceImageDataUrl = referenceImageIndex !== void 0 ? ctx.fileAttachments?.[referenceImageIndex]?.dataUrl : void 0;
56732
+ const priorGeneration = frameGenerationMemory.get(frameId);
56733
+ const effectiveReferenceImageIndex = referenceImageIndex !== void 0 ? referenceImageIndex : priorGeneration ? priorGeneration.referenceImageIndex : ctx.fileAttachments && ctx.fileAttachments.length > 0 ? 0 : void 0;
56734
+ const referenceImageDataUrl = effectiveReferenceImageIndex !== void 0 ? ctx.fileAttachments?.[effectiveReferenceImageIndex]?.dataUrl : void 0;
56735
+ const isRegeneration = !!priorGeneration && priorGeneration.originalPrompt !== prompt;
56736
+ const originalPromptAnchor = isRegeneration ? `
56737
+
56738
+ 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
+ "${priorGeneration.originalPrompt}"` : "";
56698
56740
  const fontNames = [ctx.brandHeadlineFontName, ctx.brandBodyFontName].filter(
56699
56741
  (n, i, arr) => Boolean(n) && arr.indexOf(n) === i
56700
56742
  );
@@ -56729,7 +56771,7 @@ LAYOUT & SPACING RULES \u2014 these are non-negotiable:
56729
56771
  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.
56730
56772
  7. ASPECT RATIO: Do NOT squish, stretch, or distort any element. Maintain the natural proportions of all visuals, logos, and text at all times.
56731
56773
 
56732
- Render all text as crisp, correctly-spelled, legible copy. Professional advertising quality \u2014 strong visual hierarchy, brand-consistent, generous whitespace throughout.`;
56774
+ Render all text as crisp, correctly-spelled, legible copy. Professional advertising quality \u2014 strong visual hierarchy, brand-consistent, generous whitespace throughout.${originalPromptAnchor}`;
56733
56775
  if (!frameId || !prompt) {
56734
56776
  return {
56735
56777
  success: false,
@@ -56873,6 +56915,14 @@ Render all text as crisp, correctly-spelled, legible copy. Professional advertis
56873
56915
  lastRetrieved: Date.now()
56874
56916
  }
56875
56917
  ]);
56918
+ if (!priorGeneration) {
56919
+ frameGenerationMemory.set(frameId, {
56920
+ originalPrompt: prompt,
56921
+ referenceImageIndex: effectiveReferenceImageIndex
56922
+ });
56923
+ } else if (effectiveReferenceImageIndex !== void 0) {
56924
+ priorGeneration.referenceImageIndex = effectiveReferenceImageIndex;
56925
+ }
56876
56926
  return {
56877
56927
  success: true,
56878
56928
  data: {
@@ -56882,7 +56932,8 @@ Render all text as crisp, correctly-spelled, legible copy. Professional advertis
56882
56932
  x: Math.round(absX),
56883
56933
  y: Math.round(absY),
56884
56934
  width: Math.round(width),
56885
- height: Math.round(height)
56935
+ height: Math.round(height),
56936
+ ...effectiveReferenceImageIndex !== void 0 ? { referenceImageIndex: effectiveReferenceImageIndex } : {}
56886
56937
  },
56887
56938
  statusMessage: `Generated image from prompt "${prompt.slice(0, 50)}${prompt.length > 50 ? "\u2026" : ""}"`
56888
56939
  };
@@ -58917,7 +58968,8 @@ ${f.textContent}`).join("\n\n")}`;
58917
58968
  x: result.data.x,
58918
58969
  y: result.data.y,
58919
58970
  width: result.data.width,
58920
- height: result.data.height
58971
+ height: result.data.height,
58972
+ referenceImageIndex: result.data.referenceImageIndex
58921
58973
  };
58922
58974
  if (onAfterImageGen) {
58923
58975
  onAfterImageGen();
@@ -59055,6 +59107,7 @@ Use the frameId="${imageGenData.frameId}" for all elements. Keep all elements wi
59055
59107
  let feedback;
59056
59108
  try {
59057
59109
  const frameEl = augmentedToolCtx.excalidrawAPI.getSceneElements().find((e) => e.id === imageGenData.frameId);
59110
+ const originalImageDataUrl = imageGenData.referenceImageIndex !== void 0 ? allImageAttachments[imageGenData.referenceImageIndex]?.dataUrl : void 0;
59058
59111
  feedback = await runReviewerAgent({
59059
59112
  screenshotDataUrl: bannerScreenshot,
59060
59113
  htmlSource: "",
@@ -59070,6 +59123,7 @@ Use the frameId="${imageGenData.frameId}" for all elements. Keep all elements wi
59070
59123
  height: imageGenData.height
59071
59124
  },
59072
59125
  userBrief: typeof userMessages[0]?.content === "string" ? userMessages[0].content : "",
59126
+ originalImageDataUrl,
59073
59127
  brandContext,
59074
59128
  chatUrl,
59075
59129
  reviewerModel: reviewerModel ?? chatModel,
@@ -59104,7 +59158,7 @@ Use the frameId="${imageGenData.frameId}" for all elements. Keep all elements wi
59104
59158
  "",
59105
59159
  `Overall verdict: ${feedback.summary}`,
59106
59160
  "",
59107
- `Apply ALL fixes by calling generate_image AGAIN on frameId="${imageGenData.frameId}" with a revised prompt that incorporates every correction above (re-state the full ad \u2014 visuals AND all copy \u2014 with every fix folded in). Prioritise critical issues first. Do NOT call generate_html_banner or add separate text/shape layers \u2014 the ad is a single generated image.`
59161
+ `Apply ALL fixes by calling generate_image AGAIN on frameId="${imageGenData.frameId}" with a revised prompt. This is a REVISION of the existing ad, not a new one: start from the exact prompt you used last time and change ONLY what the fixes above require \u2014 keep the same subject, composition, color palette, and any reference image untouched otherwise. Do NOT invent a new color scheme, layout, or brand style. If a fix mentions a specific position (e.g. "top-right", "bottom-left"), use that exact position. Prioritise critical issues first. Do NOT call generate_html_banner or add separate text/shape layers \u2014 the ad is a single generated image.`
59108
59162
  ].join("\n");
59109
59163
  messages.push({
59110
59164
  role: "user",