@orangecatai/adgen-canvas 0.0.21 → 0.0.23

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.
Files changed (33) hide show
  1. package/dist/dev/{chunk-WFKR6OGI.js → chunk-E6AX7UYW.js} +3 -3
  2. package/dist/dev/{chunk-RD5LX7MN.js → chunk-KNIBTSFD.js} +2 -2
  3. package/dist/dev/data/{image-DSU2H6P5.js → image-WJAASDV4.js} +3 -3
  4. package/dist/dev/index.css +103 -0
  5. package/dist/dev/index.css.map +2 -2
  6. package/dist/dev/index.js +3180 -2922
  7. package/dist/dev/index.js.map +4 -4
  8. package/dist/dev/subset-shared.chunk.js +1 -1
  9. package/dist/dev/subset-worker.chunk.js +1 -1
  10. package/dist/prod/{chunk-OWNL6YOR.js → chunk-GOHSI4TL.js} +1 -1
  11. package/dist/prod/{chunk-RXJEXEKA.js → chunk-XIMYEPMA.js} +2 -2
  12. package/dist/prod/data/image-QJTVNM22.js +1 -0
  13. package/dist/prod/index.css +1 -1
  14. package/dist/prod/index.js +224 -423
  15. package/dist/prod/subset-shared.chunk.js +1 -1
  16. package/dist/prod/subset-worker.chunk.js +1 -1
  17. package/dist/types/excalidraw/components/AIChatPanel.d.ts +34 -0
  18. package/dist/types/excalidraw/components/ImageGeneratorPanel.d.ts +16 -2
  19. package/dist/types/excalidraw/components/ai-chat/AdGenShimmerLayer.d.ts +10 -0
  20. package/dist/types/excalidraw/components/ai-chat/adGenShimmerStore.d.ts +12 -0
  21. package/dist/types/excalidraw/components/ai-chat/agentLoop.d.ts +0 -4
  22. package/dist/types/excalidraw/components/ai-chat/canvasTools.d.ts +81 -27
  23. package/dist/types/excalidraw/components/ai-chat/reviewerAgent.d.ts +18 -9
  24. package/dist/types/excalidraw/components/auto-resize/autoResizeEngine.d.ts +42 -0
  25. package/dist/types/excalidraw/index.d.ts +1 -0
  26. package/dist/types/excalidraw/types.d.ts +39 -0
  27. package/dist/types/excalidraw/utils/brandContextUtils.d.ts +23 -0
  28. package/dist/types/excalidraw/utils/imageApi.d.ts +19 -1
  29. package/package.json +1 -1
  30. package/dist/prod/data/image-I7MZ4QNS.js +0 -1
  31. /package/dist/dev/{chunk-WFKR6OGI.js.map → chunk-E6AX7UYW.js.map} +0 -0
  32. /package/dist/dev/{chunk-RD5LX7MN.js.map → chunk-KNIBTSFD.js.map} +0 -0
  33. /package/dist/dev/data/{image-DSU2H6P5.js.map → image-WJAASDV4.js.map} +0 -0
@@ -1 +1 @@
1
- import{a,b,c,d}from"./chunk-Z5NKEFVG.js";import"./chunk-OWNL6YOR.js";import"./chunk-SRAX5OIU.js";export{a as Commands,b as subsetToBase64,c as subsetToBinary,d as toBase64};
1
+ import{a,b,c,d}from"./chunk-Z5NKEFVG.js";import"./chunk-GOHSI4TL.js";import"./chunk-SRAX5OIU.js";export{a as Commands,b as subsetToBase64,c as subsetToBinary,d as toBase64};
@@ -1 +1 @@
1
- import{a as r,c as t}from"./chunk-Z5NKEFVG.js";import"./chunk-OWNL6YOR.js";import"./chunk-SRAX5OIU.js";var s=import.meta.url?new URL(import.meta.url):void 0;typeof window>"u"&&typeof self<"u"&&(self.onmessage=async e=>{switch(e.data.command){case r.Subset:let a=await t(e.data.arrayBuffer,e.data.codePoints);self.postMessage(a,{transfer:[a]});break}});export{s as WorkerUrl};
1
+ import{a as r,c as t}from"./chunk-Z5NKEFVG.js";import"./chunk-GOHSI4TL.js";import"./chunk-SRAX5OIU.js";var s=import.meta.url?new URL(import.meta.url):void 0;typeof window>"u"&&typeof self<"u"&&(self.onmessage=async e=>{switch(e.data.command){case r.Subset:let a=await t(e.data.arrayBuffer,e.data.codePoints);self.postMessage(a,{transfer:[a]});break}});export{s as WorkerUrl};
@@ -115,6 +115,15 @@ export type AIChatPanelProps = {
115
115
  tags: string[];
116
116
  assetType: string;
117
117
  blobUrl: string;
118
+ previewDataUrl?: string;
119
+ }>>;
120
+ onSearchAdCreatives?: (query: string) => Promise<Array<{
121
+ id: string;
122
+ name: string;
123
+ tags: string[];
124
+ mimeType: string;
125
+ blobUrl: string;
126
+ previewDataUrl?: string;
118
127
  }>>;
119
128
  onListBrandTemplates?: (keywords?: string[]) => Promise<Array<{
120
129
  id: string;
@@ -124,10 +133,35 @@ export type AIChatPanelProps = {
124
133
  width?: number;
125
134
  height?: number;
126
135
  thumbnailBlobUrl?: string;
136
+ thumbnailDataUrl?: string;
127
137
  }>>;
128
138
  onGetTemplateVariant?: (variantId: string) => Promise<{
129
139
  canvasJson: string;
130
140
  }>;
141
+ /**
142
+ * Searches brand assets, ad creatives, and templates by keywords, returning
143
+ * top 3 matches with base64 preview images. Used by the visual agent's
144
+ * `find_brand_asset` tool and the @mention autocomplete.
145
+ */
146
+ onFindBrandAssets?: (type: "image-asset" | "ad-creative" | "template", keywords: string[]) => Promise<{
147
+ results: Array<{
148
+ id: string;
149
+ name: string;
150
+ tags?: string[];
151
+ width?: number | null;
152
+ height?: number | null;
153
+ previewDataUrl: string;
154
+ }>;
155
+ }>;
156
+ /**
157
+ * Called when user types after `@` in the chat input. Should return a
158
+ * lightweight list of matching items (no base64 — previews fetched on pick).
159
+ */
160
+ onMentionSearch?: (query: string) => Promise<Array<{
161
+ id: string;
162
+ type: "image-asset" | "ad-creative" | "template";
163
+ name: string;
164
+ }>>;
131
165
  /**
132
166
  * OpenRouter model tag for the reviewer agent. Must be vision-capable.
133
167
  * Defaults to the same model as chatModel.
@@ -12,14 +12,14 @@ export declare const MODEL_CONFIG: {
12
12
  readonly "nano banana 2": {
13
13
  readonly modelId: "google/gemini-3.1-flash-image-preview";
14
14
  readonly textOutput: true;
15
- readonly supportsImageSize: true;
15
+ readonly supportsImageSize: false;
16
16
  readonly supportedResolutions: readonly ["0.5K", "1K", "2K", "4K"];
17
17
  readonly supportedRatios: readonly ["21:9", "16:9", "4:3", "3:2", "1:1", "9:16", "3:4", "2:3", "5:4", "4:5", "4:1", "1:4", "8:1", "1:8"];
18
18
  };
19
19
  readonly "nano banana pro": {
20
20
  readonly modelId: "google/gemini-3-pro-image-preview";
21
21
  readonly textOutput: true;
22
- readonly supportsImageSize: true;
22
+ readonly supportsImageSize: false;
23
23
  readonly supportedResolutions: readonly ["1K", "2K", "4K"];
24
24
  readonly supportedRatios: readonly ["21:9", "16:9", "4:3", "3:2", "1:1", "9:16", "3:4", "2:3", "5:4", "4:5"];
25
25
  };
@@ -37,6 +37,20 @@ export declare const MODEL_CONFIG: {
37
37
  readonly supportedResolutions: readonly ["1K", "2K"];
38
38
  readonly supportedRatios: readonly ["16:9", "4:3", "3:2", "1:1", "9:16", "3:4", "2:3", "5:4", "4:5"];
39
39
  };
40
+ readonly "riverflow 2.5 fast": {
41
+ readonly modelId: "sourceful/riverflow-v2.5-fast";
42
+ readonly textOutput: false;
43
+ readonly supportsImageSize: true;
44
+ readonly supportedResolutions: readonly ["1K", "2K"];
45
+ readonly supportedRatios: readonly ["1:1", "21:9", "16:9", "3:2", "4:3", "5:4", "4:5", "3:4", "2:3", "9:16"];
46
+ };
47
+ readonly "riverflow 2.5 pro": {
48
+ readonly modelId: "sourceful/riverflow-v2.5-pro";
49
+ readonly textOutput: false;
50
+ readonly supportsImageSize: true;
51
+ readonly supportedResolutions: readonly ["1K", "2K", "4K"];
52
+ readonly supportedRatios: readonly ["1:1", "21:9", "16:9", "3:2", "4:3", "5:4", "4:5", "3:4", "2:3", "9:16"];
53
+ };
40
54
  };
41
55
  export type ModelName = keyof typeof MODEL_CONFIG;
42
56
  export declare const MODEL_NAMES: ModelName[];
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Renders a shimmer overlay on top of frames that are currently being
3
+ * generated by the AI chat agent (create_frame → generate_image → review).
4
+ *
5
+ * Rendered unconditionally from App.tsx alongside AutoResizeShimmerLayer.
6
+ * Uses useExcalidrawAppState() so it repositions correctly on scroll/zoom.
7
+ * Reads current frame position from scene elements so it follows the frame
8
+ * if the user drags it during generation.
9
+ */
10
+ export declare const AdGenShimmerLayer: () => import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,12 @@
1
+ export type AdGenFrameInfo = {
2
+ frameId: string;
3
+ };
4
+ type Listener = () => void;
5
+ export declare const adGenShimmerStore: {
6
+ startGenerating(frameId: string): void;
7
+ stopGenerating(frameId: string): void;
8
+ clear(): void;
9
+ getLoadingFrames(): AdGenFrameInfo[];
10
+ subscribe(fn: Listener): () => void;
11
+ };
12
+ export {};
@@ -60,10 +60,6 @@ export type AgentResult = {
60
60
  reply: string;
61
61
  toolActions: ToolAction[];
62
62
  };
63
- /**
64
- * Converts a BrandContext object to a concise system message string.
65
- */
66
- export declare function buildBrandContextMessage(ctx: BrandContext): string;
67
63
  export type FileAttachment = {
68
64
  name: string;
69
65
  type: "image" | "text" | "pdf" | "docx";
@@ -297,7 +297,7 @@ export declare const BANNER_TOOLS: readonly [{
297
297
  readonly type: "function";
298
298
  readonly function: {
299
299
  readonly name: "generate_image";
300
- readonly description: "Generate an image using AI (Gemini) and place it onto the canvas. Always call this for any photo or image content even when the user has attached a reference image. The attached image is passed to Gemini as visual reference; Gemini generates the final output. Use referenceImageIndex to tell Gemini which user attachment to reference. IMPORTANT: your prompt must describe a purely visual scene NO text, NO typography, NO words, NO captions, NO watermarks. All copy is handled by the HTML layer. A 'no text' safety suffix will be auto-appended, but you must also describe composition clearly (e.g. which side should be left open/empty for the HTML text).";
300
+ readonly description: "Generate the COMPLETE advertisement as a single AI image and place it into the frame. The image must contain BOTH the visuals AND all copy (headline, subheadline, CTA button, sign-off/brand name) rendered as legible text inside the image this is the entire ad, not a text-free background. Fill the whole frame (x:0, y:0, width:frameW, height:frameH). Use referenceImageIndex when the user attached an image to use as a subject/style reference. Your prompt must spell on-image text exactly and specify brand colors, typographic style, composition, and dimensions. Do NOT call generate_html_banner afterwards there is no separate text layer.";
301
301
  readonly parameters: {
302
302
  readonly type: "object";
303
303
  readonly properties: {
@@ -307,7 +307,7 @@ export declare const BANNER_TOOLS: readonly [{
307
307
  };
308
308
  readonly prompt: {
309
309
  readonly type: "string";
310
- readonly description: "Detailed generation prompt. When using a reference image, describe what you want Gemini to produce e.g. 'use this building as the subject, wide cinematic crop with open sky on the left for text overlay'.";
310
+ readonly description: "Detailed prompt for the COMPLETE ad: the visual scene PLUS the exact on-image copy (quote headline/subheadline/CTA/sign-off verbatim), brand colors per element, typographic style described in words, composition/layout, and the target dimensions/aspect.";
311
311
  };
312
312
  readonly x: {
313
313
  readonly type: "number";
@@ -334,27 +334,6 @@ export declare const BANNER_TOOLS: readonly [{
334
334
  readonly additionalProperties: false;
335
335
  };
336
336
  };
337
- }, {
338
- readonly type: "function";
339
- readonly function: {
340
- readonly name: "generate_html_banner";
341
- readonly description: "Place HTML/CSS text and shape elements into an EXISTING frame on the canvas. Always call create_frame first to get a frameId, then generate_image, then call this with that frameId. frameId is required — this tool does NOT create frames.";
342
- readonly parameters: {
343
- readonly type: "object";
344
- readonly properties: {
345
- readonly html: {
346
- readonly type: "string";
347
- readonly description: "Complete self-contained HTML with inline <style>. All layer elements must use position:absolute with explicit left/top/width/height in px. Root div must have class='canvas'.";
348
- };
349
- readonly frameId: {
350
- readonly type: "string";
351
- readonly description: "Required. The ID of the frame created by create_frame. Adds HTML elements into this existing frame, preserving existing children like images.";
352
- };
353
- };
354
- readonly required: readonly ["html", "frameId"];
355
- readonly additionalProperties: false;
356
- };
357
- };
358
337
  }, {
359
338
  readonly type: "function";
360
339
  readonly function: {
@@ -470,6 +449,23 @@ export declare const BANNER_TOOLS: readonly [{
470
449
  readonly additionalProperties: false;
471
450
  };
472
451
  };
452
+ }, {
453
+ readonly type: "function";
454
+ readonly function: {
455
+ readonly name: "search_ad_creatives";
456
+ readonly description: "Search uploaded ad creatives (past static ads) by name or tag keywords. Returns top 3 matches with visual previews. Use this to find reference ads for inspiration or remixing.";
457
+ readonly parameters: {
458
+ readonly type: "object";
459
+ readonly properties: {
460
+ readonly query: {
461
+ readonly type: "string";
462
+ readonly description: "Search query — name or tag keywords";
463
+ };
464
+ };
465
+ readonly required: readonly ["query"];
466
+ readonly additionalProperties: false;
467
+ };
468
+ };
473
469
  }, {
474
470
  readonly type: "function";
475
471
  readonly function: {
@@ -592,17 +588,42 @@ export declare const BANNER_TOOLS: readonly [{
592
588
  }, {
593
589
  readonly type: "function";
594
590
  readonly function: {
595
- readonly name: "finalize_ad";
596
- readonly description: "Call this when ALL elements are placed on the canvas brand assets, generated images, and the HTML text overlay. Captures a screenshot of the complete frame for review. Do NOT call this before all assets are in place.";
591
+ readonly name: "place_brand_asset";
592
+ readonly description: "Place a brand image asset or ad creative directly into a frame as an image element. Use after search_brand_assets or search_ad_creatives to insert the found asset. Maintains the asset's aspect ratio inside the frame.";
597
593
  readonly parameters: {
598
594
  readonly type: "object";
599
595
  readonly properties: {
600
596
  readonly frameId: {
601
597
  readonly type: "string";
602
- readonly description: "ID of the frame to capture for review";
598
+ readonly description: "The frame to place the asset in";
599
+ };
600
+ readonly assetId: {
601
+ readonly type: "string";
602
+ readonly description: "Asset ID from search_brand_assets or search_ad_creatives [id:...] field";
603
+ };
604
+ readonly assetType: {
605
+ readonly type: "string";
606
+ readonly enum: readonly ["image-asset", "ad-creative"];
607
+ readonly description: "Type of asset — defaults to image-asset";
608
+ };
609
+ readonly x: {
610
+ readonly type: "number";
611
+ readonly description: "X offset from frame left edge (optional, centers by default)";
612
+ };
613
+ readonly y: {
614
+ readonly type: "number";
615
+ readonly description: "Y offset from frame top edge (optional, centers by default)";
616
+ };
617
+ readonly width: {
618
+ readonly type: "number";
619
+ readonly description: "Width of placed image (optional, auto-fits to frame by default)";
620
+ };
621
+ readonly height: {
622
+ readonly type: "number";
623
+ readonly description: "Height of placed image (optional, auto-fits to frame by default)";
603
624
  };
604
625
  };
605
- readonly required: readonly ["frameId"];
626
+ readonly required: readonly ["frameId", "assetId"];
606
627
  readonly additionalProperties: false;
607
628
  };
608
629
  };
@@ -641,6 +662,19 @@ export type ToolExecutionContext = {
641
662
  brandHeadlineFontName?: string;
642
663
  /** Brand body font file name. Sourced from brand DNA. */
643
664
  brandBodyFontName?: string;
665
+ /** Data URL of the master brand logo — passed as a visual reference image to the image generation model. */
666
+ brandLogoDataUrl?: string;
667
+ /** Data URL of text rendered in the brand headline font — passed as a visual reference to the image generation model. */
668
+ brandFontPreviewDataUrl?: string;
669
+ /** Data URL of text rendered in the brand body font — passed as a second visual reference to the image generation model. */
670
+ brandBodyFontPreviewDataUrl?: string;
671
+ /**
672
+ * Fetchable URL of the actual headline font file (.ttf/.otf), e.g. a presigned
673
+ * S3 URL. Only consumed by image models that accept a font file as input
674
+ * (Sourceful Riverflow v2+ -> OpenRouter `image_config.font_inputs`). Other
675
+ * models ignore it.
676
+ */
677
+ brandFontUrl?: string;
644
678
  /**
645
679
  * Excalidraw fontFamilyId for the brand headline font.
646
680
  * When set, ALL text elements (add_text, fill_template_slots headline slots)
@@ -668,6 +702,15 @@ export type ToolExecutionContext = {
668
702
  tags: string[];
669
703
  assetType: string;
670
704
  blobUrl: string;
705
+ previewDataUrl?: string;
706
+ }>>;
707
+ onSearchAdCreatives?: (query: string) => Promise<Array<{
708
+ id: string;
709
+ name: string;
710
+ tags: string[];
711
+ mimeType: string;
712
+ blobUrl: string;
713
+ previewDataUrl?: string;
671
714
  }>>;
672
715
  onListBrandTemplates?: (keywords?: string[]) => Promise<Array<{
673
716
  id: string;
@@ -677,10 +720,21 @@ export type ToolExecutionContext = {
677
720
  width?: number;
678
721
  height?: number;
679
722
  thumbnailBlobUrl?: string;
723
+ thumbnailDataUrl?: string;
680
724
  }>>;
681
725
  onGetTemplateVariant?: (variantId: string) => Promise<{
682
726
  canvasJson: string;
683
727
  }>;
728
+ onFindBrandAssets?: (type: "image-asset" | "ad-creative" | "template", keywords: string[]) => Promise<{
729
+ results: Array<{
730
+ id: string;
731
+ name: string;
732
+ tags?: string[];
733
+ width?: number | null;
734
+ height?: number | null;
735
+ previewDataUrl: string;
736
+ }>;
737
+ }>;
684
738
  /**
685
739
  * Internal cache: populated by execGetTemplateVariant so that
686
740
  * execLoadTemplateIntoFrame can reuse the already-fetched JSON without a
@@ -1,19 +1,28 @@
1
1
  /**
2
2
  * reviewerAgent.ts
3
3
  *
4
- * Stateless reviewer module. After generate_html_banner succeeds, this
5
- * agent receives a partial, focused context (rendered screenshot, HTML
6
- * source, frame dimensions, brand guidelines, original user brief) and
7
- * returns precise layout/readability critique NOT the full agent
8
- * conversation history.
4
+ * Brand Governance Agent — stateless reviewer that runs after the main agent
5
+ * generates a complete ad image (visuals + all copy baked in) and calls
6
+ * finalize_ad. It receives a focused context (the rendered screenshot, frame
7
+ * dimensions, brand guidelines, original user brief) and returns a precise
8
+ * critique covering creative quality AND brand-governance compliance:
9
+ * copy correctness, brand tone, logo usage, claim accuracy, legal mandatories,
10
+ * offer disclaimers, regulatory sensitivity, prohibited language, competitive
11
+ * claims, and data/privacy concerns.
9
12
  *
10
- * The reviewer is a separate LLM call from the main agent loop so it
11
- * brings an independent eye to the generated banner.
13
+ * The reviewer is a separate LLM call from the main agent loop so it brings an
14
+ * independent eye to the generated ad. Because the ad is a single image, every
15
+ * fix is applied by regenerating the image with a revised prompt — there is no
16
+ * HTML/CSS layer to patch.
17
+ *
18
+ * @deprecated note (2026-06-17): this reviewer previously audited an HTML text
19
+ * layer composited over a text-free image and emitted CSS-only fixes. That
20
+ * flow is gone — see the image-first agent loop in agentLoop.ts.
12
21
  */
13
22
  import type { BrandContext } from "../../types";
14
23
  export type ReviewIssue = {
15
- /** Which design element is affected */
16
- element: "headline" | "subheadline" | "cta" | "image" | "layout" | "brand";
24
+ /** Which design element or governance dimension is affected */
25
+ element: "headline" | "subheadline" | "cta" | "image" | "layout" | "brand" | "brand_tone" | "logo" | "claim" | "legal" | "disclaimer" | "regulatory" | "prohibited_language" | "competitive_claim" | "privacy";
17
26
  /** How bad it is */
18
27
  severity: "critical" | "major" | "minor";
19
28
  /** What is wrong — one sentence */
@@ -69,6 +69,37 @@ export declare function extractFrameInfo(elements: readonly ExcalidrawElement[],
69
69
  export declare function computeCropLoss(sourceAR: number, targetAR: number): number;
70
70
  export declare function needsBackgroundRegeneration(srcW: number, srcH: number, tgtW: number, tgtH: number): boolean;
71
71
  export declare function nearestGeminiAspectRatio(w: number, h: number): string;
72
+ /**
73
+ * Resolution dimensions available for each aspect ratio in Gemini 3.1 Flash
74
+ * Image (Nano Banana 2). Maps aspect ratio → resolution level → [width, height].
75
+ * Exported so the Auto Resize panel can build its dimension presets from the
76
+ * exact model-supported sizes instead of duplicating the numbers.
77
+ */
78
+ export declare const GEMINI_RESOLUTION_TABLE: Record<string, Record<string, [number, number]>>;
79
+ /**
80
+ * Resolution levels offered by the Auto Resize panel.
81
+ * Nano Banana (gemini-2.5-flash-image) and Nano Banana 2 (gemini-3.1-flash-image-preview)
82
+ * both support 0.5K–4K. 0.5K is the fastest/cheapest tier.
83
+ */
84
+ export declare const AUTO_RESIZE_RESOLUTIONS: readonly ["0.5K", "1K", "2K", "4K"];
85
+ export type AutoResizeResolution = typeof AUTO_RESIZE_RESOLUTIONS[number];
86
+ /**
87
+ * Aspect ratios the Nano Banana 2 / Gemini 3.1 Flash Image model supports,
88
+ * ordered for display (common first, then the ultrawide / extreme-narrow ones).
89
+ * Each carries a human label for the panel.
90
+ */
91
+ export declare const AUTO_RESIZE_ASPECT_RATIOS: {
92
+ ratio: string;
93
+ desc: string;
94
+ }[];
95
+ /**
96
+ * Concrete pixel dimensions for a given aspect ratio + resolution, straight from
97
+ * the Nano Banana 2 resolution table. Falls back to 1:1 / 1K if unknown.
98
+ */
99
+ export declare function geminiDimsFor(ratio: string, resolution: string): {
100
+ width: number;
101
+ height: number;
102
+ };
72
103
  /**
73
104
  * Selects the optimal resolution for a given target dimension and aspect ratio.
74
105
  * Chooses the resolution level that is closest to (but preferably >= ) the target size.
@@ -139,5 +170,16 @@ export declare function runAutoResize(opts: {
139
170
  signal?: AbortSignal;
140
171
  /** Pass the result of preCreateAllFrames() to skip redundant frame creation */
141
172
  preCreatedFrameInfos?: PreCreatedFrameInfo[];
173
+ /**
174
+ * Brand assets handed to the image model so the recomposed ad keeps the brand
175
+ * logo, typeface, and (for font-aware models) the real font file. Sourced from
176
+ * `app.state.brandContext` by the panel.
177
+ */
178
+ brandImages?: {
179
+ logo?: string;
180
+ fontPreview?: string;
181
+ fontUrl?: string;
182
+ name?: string;
183
+ };
142
184
  }): Promise<DimensionResult[]>;
143
185
  export {};
@@ -14,6 +14,7 @@ export declare const Excalidraw: React.MemoExoticComponent<(props: ExcalidrawPro
14
14
  export { getSceneVersion, hashElementsVersion, hashString, getNonDeletedElements, } from "@orangecatai/element";
15
15
  export { getTextFromElements } from "@orangecatai/element";
16
16
  export { isInvisiblySmallElement } from "@orangecatai/element";
17
+ export { newImageElement, newTextElement, newFrameElement, } from "@orangecatai/element";
17
18
  export { defaultLang, useI18n, languages } from "./i18n";
18
19
  export { restoreAppState, restoreElement, restoreElements, restoreLibraryItems, } from "./data/restore";
19
20
  export { reconcileElements } from "./data/reconcile";
@@ -454,6 +454,8 @@ export type CustomFontSpec = {
454
454
  * When provided, the AI agent uses these guidelines when creating ads.
455
455
  */
456
456
  export type BrandContext = {
457
+ /** Brand name — injected as a system-level constraint so the model stays on-brand. */
458
+ name?: string;
457
459
  colors?: {
458
460
  primary?: string;
459
461
  secondary?: string;
@@ -503,6 +505,21 @@ export type BrandContext = {
503
505
  environment?: string;
504
506
  negativeSpace?: string;
505
507
  };
508
+ /** Data URL of the master brand logo — passed as a visual reference to the image generation model. */
509
+ logoDataUrl?: string;
510
+ /** Data URL of text rendered in the brand headline font — passed as a visual reference to the image generation model. */
511
+ headlineFontPreviewDataUrl?: string;
512
+ /** Data URL of text rendered in the brand body font — passed as a second visual reference alongside the headline preview. */
513
+ bodyFontPreviewDataUrl?: string;
514
+ /**
515
+ * Fetchable URL (e.g. a short-lived presigned S3 URL) of the actual headline
516
+ * font file (.ttf/.otf). Passed to image models that accept a font file as
517
+ * input (Sourceful Riverflow v2+ via OpenRouter `image_config.font_inputs`).
518
+ * Unlike `headlineFontPreviewDataUrl` (a rendered preview image), this is the
519
+ * real font file the provider's servers fetch directly, so it must be a URL
520
+ * reachable without auth cookies — not the private `/api/brand/font` proxy.
521
+ */
522
+ headlineFontUrl?: string;
506
523
  };
507
524
  export interface ExcalidrawProps {
508
525
  onChange?: (elements: readonly OrderedExcalidrawElement[], appState: AppState, files: BinaryFiles) => void;
@@ -572,6 +589,13 @@ export interface ExcalidrawProps {
572
589
  * browser.
573
590
  */
574
591
  imageGenUrl?: string;
592
+ /**
593
+ * Brand context (colors, typography, logo, font preview) passed to every
594
+ * image generation surface (ImageGeneratorPanel, ImageQuickEditPanel, and the
595
+ * AI chat agent). When present, the logo and font preview are sent as visual
596
+ * references to the image model.
597
+ */
598
+ brandContext?: BrandContext;
575
599
  /**
576
600
  * Host proxy URL for the auto-resize engine's OpenRouter calls (layout
577
601
  * planning, vision tagging, HTML generation, reviewer). Key stays server-side.
@@ -700,6 +724,21 @@ export interface ExcalidrawProps {
700
724
  onGetTemplateVariant?: (templateId: string) => Promise<{
701
725
  canvasJson: string;
702
726
  }>;
727
+ /**
728
+ * Search brand image assets, ad creatives, or templates by keywords and
729
+ * return top 3 matches with base64 preview images. Used by the agent's
730
+ * `find_brand_asset` tool.
731
+ */
732
+ onFindBrandAssets?: (type: "image-asset" | "ad-creative" | "template", keywords: string[]) => Promise<{
733
+ results: Array<{
734
+ id: string;
735
+ name: string;
736
+ tags?: string[];
737
+ width?: number | null;
738
+ height?: number | null;
739
+ previewDataUrl: string;
740
+ }>;
741
+ }>;
703
742
  }
704
743
  export type SceneData = {
705
744
  elements?: ImportedDataState["elements"];
@@ -0,0 +1,23 @@
1
+ import type { BrandContext } from "../types";
2
+ /**
3
+ * Converts a BrandContext object to a concise text block describing brand
4
+ * identity guidelines. Used as a system/prompt prefix for both the agent loop
5
+ * and direct image generation panels.
6
+ */
7
+ export declare function buildBrandContextMessage(ctx: BrandContext): string;
8
+ /**
9
+ * Augments a raw user prompt with strong brand asset instructions — identical to
10
+ * the approach used by execGenerateImage in the AI chat agent. Call this in
11
+ * ImageGeneratorPanel and ImageQuickEditPanel before invoking callOpenRouterImageAPI
12
+ * so that direct-generation panels produce the same brand-faithful output as the
13
+ * agent loop.
14
+ *
15
+ * Brand DNA text (colors, visual style, etc.) should be passed separately as
16
+ * `brandImages.brandContextText` in callOpenRouterImageAPI so it's prepended as a
17
+ * block before the prompt. This function only appends the per-asset mandatory
18
+ * instructions that reference the visual attachments by position.
19
+ */
20
+ export declare function buildImageGenSafePrompt(prompt: string, brandContext: BrandContext, dimensions?: {
21
+ width: number;
22
+ height: number;
23
+ }): string;
@@ -1 +1,19 @@
1
- export declare function callOpenRouterImageAPI(prompt: string, modelId: string, aspectRatio: string, imageSize: string | null, textOutput: boolean, imageGenUrl: string, referenceImageDataUrl?: string, signal?: AbortSignal): Promise<string>;
1
+ export declare function callOpenRouterImageAPI(prompt: string, modelId: string, aspectRatio: string, imageSize: string | null, textOutput: boolean, imageGenUrl: string, referenceImageDataUrl?: string, signal?: AbortSignal, brandImages?: {
2
+ logo?: string;
3
+ fontPreview?: string;
4
+ /** Optional second font preview (brand body typeface) shown as a reference. */
5
+ fontPreviewBody?: string;
6
+ /**
7
+ * Fetchable URL of the real headline font file (.ttf/.otf). Only Sourceful
8
+ * Riverflow v2+ consumes this — sent via `image_config.font_inputs` so the
9
+ * model renders text in the actual brand typeface (not just a preview img).
10
+ */
11
+ fontUrl?: string;
12
+ /** Text to render with `fontUrl` for the font_inputs entry. */
13
+ fontText?: string;
14
+ /**
15
+ * Full brand identity context text (colors, typography, visual style, etc.)
16
+ * prepended to the prompt so the model applies brand guidelines to the image.
17
+ */
18
+ brandContextText?: string;
19
+ }): Promise<string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orangecatai/adgen-canvas",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "type": "module",
5
5
  "types": "./dist/types/excalidraw/index.d.ts",
6
6
  "main": "./dist/prod/index.js",
@@ -1 +0,0 @@
1
- import{T as a,U as b,V as c}from"../chunk-RXJEXEKA.js";import"../chunk-OWNL6YOR.js";import"../chunk-SRAX5OIU.js";export{c as decodePngMetadata,b as encodePngMetadata,a as getTEXtChunk};