@orangecatai/adgen-canvas 0.0.20 → 0.0.21

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 (29) hide show
  1. package/dist/dev/{chunk-Y6SGUVKW.js → chunk-RD5LX7MN.js} +2 -2
  2. package/dist/dev/{chunk-ILOOQ6LL.js → chunk-WFKR6OGI.js} +3 -15
  3. package/dist/dev/{chunk-ILOOQ6LL.js.map → chunk-WFKR6OGI.js.map} +2 -2
  4. package/dist/dev/data/{image-MFQPU4SU.js → image-DSU2H6P5.js} +3 -3
  5. package/dist/dev/index.js +132 -494
  6. package/dist/dev/index.js.map +4 -4
  7. package/dist/dev/subset-shared.chunk.js +1 -1
  8. package/dist/dev/subset-worker.chunk.js +1 -1
  9. package/dist/prod/{chunk-SDWSFP7L.js → chunk-OWNL6YOR.js} +1 -1
  10. package/dist/prod/{chunk-3BUGCS63.js → chunk-RXJEXEKA.js} +3 -3
  11. package/dist/prod/data/image-I7MZ4QNS.js +1 -0
  12. package/dist/prod/index.js +56 -56
  13. package/dist/prod/subset-shared.chunk.js +1 -1
  14. package/dist/prod/subset-worker.chunk.js +1 -1
  15. package/dist/types/excalidraw/components/AIChatPanel.d.ts +12 -1
  16. package/dist/types/excalidraw/components/ImageEditToolbar.d.ts +0 -1
  17. package/dist/types/excalidraw/components/ai-chat/agentLoop.d.ts +1 -1
  18. package/dist/types/excalidraw/components/ai-chat/audioUtils.d.ts +0 -5
  19. package/dist/types/excalidraw/components/ai-chat/canvasTools.d.ts +2 -1
  20. package/dist/types/excalidraw/components/ai-chat/reviewerAgent.d.ts +2 -1
  21. package/dist/types/excalidraw/components/auto-resize/AutoResizePanel.d.ts +2 -2
  22. package/dist/types/excalidraw/components/auto-resize/autoResizeEngine.d.ts +1 -1
  23. package/dist/types/excalidraw/types.d.ts +15 -19
  24. package/dist/types/excalidraw/utils/imageApi.d.ts +1 -1
  25. package/package.json +1 -1
  26. package/dist/prod/data/image-NBDZMG3P.js +0 -1
  27. package/dist/types/excalidraw/utils/leonardoApiKey.d.ts +0 -1
  28. /package/dist/dev/{chunk-Y6SGUVKW.js.map → chunk-RD5LX7MN.js.map} +0 -0
  29. /package/dist/dev/data/{image-MFQPU4SU.js.map → image-DSU2H6P5.js.map} +0 -0
package/dist/dev/index.js CHANGED
@@ -13,7 +13,6 @@ import {
13
13
  canvasToBlob,
14
14
  centerScrollOn,
15
15
  createFile,
16
- dataURLToFile,
17
16
  dataURLToString,
18
17
  encodePngMetadata,
19
18
  exportToCanvas,
@@ -67,10 +66,10 @@ import {
67
66
  serializeAsJSON,
68
67
  serializeLibraryAsJSON,
69
68
  strokeRectWithRotation_simple
70
- } from "./chunk-ILOOQ6LL.js";
69
+ } from "./chunk-WFKR6OGI.js";
71
70
  import {
72
71
  define_import_meta_env_default
73
- } from "./chunk-Y6SGUVKW.js";
72
+ } from "./chunk-RD5LX7MN.js";
74
73
  import {
75
74
  en_default
76
75
  } from "./chunk-4K5LIQQU.js";
@@ -9771,7 +9770,7 @@ var exportCanvas = async (type, elements, appState, files, {
9771
9770
  let blob = canvasToBlob(tempCanvas);
9772
9771
  if (appState.exportEmbedScene) {
9773
9772
  blob = blob.then(
9774
- (blob2) => import("./data/image-MFQPU4SU.js").then(
9773
+ (blob2) => import("./data/image-DSU2H6P5.js").then(
9775
9774
  ({ encodePngMetadata: encodePngMetadata2 }) => encodePngMetadata2({
9776
9775
  blob: blob2,
9777
9776
  metadata: serializeAsJSON(elements, appState, files, "local")
@@ -23049,12 +23048,6 @@ import { useEffect as useEffect32, useRef as useRef26, useState as useState29 }
23049
23048
  import { sceneCoordsToViewportCoords as sceneCoordsToViewportCoords6 } from "@orangecatai/common";
23050
23049
  import { getElementAbsoluteCoords as getElementAbsoluteCoords9 } from "@orangecatai/element";
23051
23050
 
23052
- // utils/openRouterApiKey.ts
23053
- function resolveOpenRouterApiKey(propKey) {
23054
- const normalizedPropKey = propKey?.trim();
23055
- return (normalizedPropKey ? normalizedPropKey : void 0) ?? (typeof import.meta !== "undefined" && define_import_meta_env_default?.VITE_APP_OPENROUTER_API_KEY ? define_import_meta_env_default.VITE_APP_OPENROUTER_API_KEY : "") ?? "";
23056
- }
23057
-
23058
23051
  // components/auto-resize/AutoResizePanel.tsx
23059
23052
  import { useCallback as useCallback15, useEffect as useEffect31, useRef as useRef25, useState as useState28 } from "react";
23060
23053
 
@@ -23071,7 +23064,7 @@ import {
23071
23064
  import { FRAME_STYLE as FRAME_STYLE2, arrayToMap as arrayToMap22 } from "@orangecatai/common";
23072
23065
 
23073
23066
  // utils/imageApi.ts
23074
- async function callOpenRouterImageAPI(prompt, modelId, aspectRatio, imageSize, textOutput, apiKey, referenceImageDataUrl, signal) {
23067
+ async function callOpenRouterImageAPI(prompt, modelId, aspectRatio, imageSize, textOutput, imageGenUrl, referenceImageDataUrl, signal) {
23075
23068
  const contentParts = [];
23076
23069
  if (referenceImageDataUrl) {
23077
23070
  contentParts.push({
@@ -23091,18 +23084,14 @@ async function callOpenRouterImageAPI(prompt, modelId, aspectRatio, imageSize, t
23091
23084
  stream: false,
23092
23085
  image_config: imageConfig
23093
23086
  };
23094
- const response = await fetch(
23095
- "https://openrouter.ai/api/v1/chat/completions",
23096
- {
23097
- method: "POST",
23098
- signal,
23099
- headers: {
23100
- Authorization: `Bearer ${apiKey}`,
23101
- "Content-Type": "application/json"
23102
- },
23103
- body: JSON.stringify(body)
23104
- }
23105
- );
23087
+ const response = await fetch(imageGenUrl, {
23088
+ method: "POST",
23089
+ signal,
23090
+ headers: {
23091
+ "Content-Type": "application/json"
23092
+ },
23093
+ body: JSON.stringify(body)
23094
+ });
23106
23095
  if (!response.ok) {
23107
23096
  let message = `OpenRouter API error ${response.status}`;
23108
23097
  try {
@@ -23541,7 +23530,7 @@ async function runReviewerAgent(opts) {
23541
23530
  imagePlacement,
23542
23531
  userBrief,
23543
23532
  brandContext,
23544
- apiKey,
23533
+ chatUrl,
23545
23534
  reviewerModel = "qwen/qwen3.7-plus",
23546
23535
  signal,
23547
23536
  iteration
@@ -23607,10 +23596,9 @@ async function runReviewerAgent(opts) {
23607
23596
  };
23608
23597
  let response;
23609
23598
  try {
23610
- response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
23599
+ response = await fetch(chatUrl, {
23611
23600
  method: "POST",
23612
23601
  headers: {
23613
- Authorization: `Bearer ${apiKey}`,
23614
23602
  "Content-Type": "application/json",
23615
23603
  "HTTP-Referer": "https://dughub.app",
23616
23604
  "X-Title": "ADGEN Reviewer"
@@ -24148,7 +24136,7 @@ Prioritise: (1) readability of text, (2) prominence of the primary CTA, (3) bran
24148
24136
  return result;
24149
24137
  }
24150
24138
  var VALID_SLOT_TYPES = /* @__PURE__ */ new Set(["logo", "product", "decoration", "cta"]);
24151
- async function tagImageElementsWithVision(info, screenshotDataUrl, openRouterApiKey, model, signal) {
24139
+ async function tagImageElementsWithVision(info, screenshotDataUrl, autoResizeUrl, model, signal) {
24152
24140
  const imageEls = info.foreground.filter(
24153
24141
  (el) => el.kind === "image" && !el.slotType
24154
24142
  );
@@ -24178,12 +24166,11 @@ Assign each element exactly one role:
24178
24166
  Return ONLY a JSON object mapping the element number (string key) to its role. Example: {"1": "logo", "2": "product"}`;
24179
24167
  try {
24180
24168
  const response = await fetch(
24181
- "https://openrouter.ai/api/v1/chat/completions",
24169
+ autoResizeUrl,
24182
24170
  {
24183
24171
  method: "POST",
24184
24172
  signal,
24185
24173
  headers: {
24186
- Authorization: `Bearer ${openRouterApiKey}`,
24187
24174
  "Content-Type": "application/json"
24188
24175
  },
24189
24176
  body: JSON.stringify({
@@ -24317,14 +24304,13 @@ Only change positions and sizes.
24317
24304
  Return ONLY the raw HTML. No markdown, no code fences, no explanation.`;
24318
24305
  return { prompt, imagePlaceholders };
24319
24306
  }
24320
- async function callOpenRouterForHtml(prompt, apiKey, model, signal) {
24307
+ async function callOpenRouterForHtml(prompt, autoResizeUrl, model, signal) {
24321
24308
  const response = await fetch(
24322
- "https://openrouter.ai/api/v1/chat/completions",
24309
+ autoResizeUrl,
24323
24310
  {
24324
24311
  method: "POST",
24325
24312
  signal,
24326
24313
  headers: {
24327
- Authorization: `Bearer ${apiKey}`,
24328
24314
  "Content-Type": "application/json"
24329
24315
  },
24330
24316
  body: JSON.stringify({
@@ -24373,7 +24359,7 @@ async function runAutoResizeForDimension(opts) {
24373
24359
  placementX,
24374
24360
  placementY,
24375
24361
  app,
24376
- openRouterApiKey,
24362
+ autoResizeUrl,
24377
24363
  chatModel,
24378
24364
  agentImageModel,
24379
24365
  customFontMap = {},
@@ -24439,7 +24425,7 @@ async function runAutoResizeForDimension(opts) {
24439
24425
  const compressedBgUrl = await downscaleForGemini(srcBgUrl);
24440
24426
  debug(
24441
24427
  `[AutoResize] bg-regen starting: ar=${ar}`,
24442
- `keyLen=${openRouterApiKey.length}`,
24428
+ `keyLen=${autoResizeUrl.length}`,
24443
24429
  `compressedLen=${compressedBgUrl.length}`,
24444
24430
  `model=${agentImageModel || DEFAULT_BG_REGEN_MODEL}`
24445
24431
  );
@@ -24459,7 +24445,7 @@ No new text, logos, watermarks, or unrelated objects added.`,
24459
24445
  ar,
24460
24446
  resolution,
24461
24447
  true,
24462
- openRouterApiKey,
24448
+ autoResizeUrl,
24463
24449
  compressedBgUrl,
24464
24450
  signal
24465
24451
  );
@@ -24533,7 +24519,7 @@ No new text, logos, watermarks, or unrelated objects added.`,
24533
24519
  );
24534
24520
  let html = await callOpenRouterForHtml(
24535
24521
  prompt,
24536
- openRouterApiKey,
24522
+ autoResizeUrl,
24537
24523
  chatModel,
24538
24524
  signal
24539
24525
  );
@@ -24744,7 +24730,7 @@ No new text, logos, watermarks, or unrelated objects added.`,
24744
24730
  userBrief: `Auto-resize ad from ${Math.round(
24745
24731
  srcFrame.width
24746
24732
  )}\xD7${Math.round(srcFrame.height)} to ${tgtW}\xD7${tgtH}`,
24747
- apiKey: openRouterApiKey,
24733
+ chatUrl: autoResizeUrl,
24748
24734
  reviewerModel: chatModel,
24749
24735
  signal,
24750
24736
  iteration: reviewRounds + 1
@@ -24816,7 +24802,7 @@ async function runAutoResize(opts) {
24816
24802
  sourceFrameId,
24817
24803
  targetDimensions,
24818
24804
  app,
24819
- openRouterApiKey,
24805
+ autoResizeUrl,
24820
24806
  chatModel = "qwen/qwen3.7-plus",
24821
24807
  agentImageModel,
24822
24808
  customFontMap = {},
@@ -24844,7 +24830,7 @@ async function runAutoResize(opts) {
24844
24830
  await tagImageElementsWithVision(
24845
24831
  info,
24846
24832
  sourceScreenshot,
24847
- openRouterApiKey,
24833
+ autoResizeUrl,
24848
24834
  chatModel,
24849
24835
  signal
24850
24836
  );
@@ -24894,7 +24880,7 @@ async function runAutoResize(opts) {
24894
24880
  placementY: placements[i].y,
24895
24881
  existingFrameId: preCreatedFrameInfos?.[i]?.frameId,
24896
24882
  app,
24897
- openRouterApiKey,
24883
+ autoResizeUrl,
24898
24884
  chatModel,
24899
24885
  agentImageModel,
24900
24886
  customFontMap,
@@ -25082,7 +25068,7 @@ var AutoResizePanel = ({
25082
25068
  onBeforeAutoResize,
25083
25069
  onAfterAutoResize,
25084
25070
  onRunningChange,
25085
- openRouterApiKey,
25071
+ autoResizeUrl,
25086
25072
  chatModel,
25087
25073
  agentImageModel
25088
25074
  }) => {
@@ -25185,7 +25171,7 @@ var AutoResizePanel = ({
25185
25171
  targetDimensions: allDimensions,
25186
25172
  preCreatedFrameInfos: preCreatedFrames.length > 0 ? preCreatedFrames : void 0,
25187
25173
  app,
25188
- openRouterApiKey,
25174
+ autoResizeUrl,
25189
25175
  chatModel,
25190
25176
  agentImageModel,
25191
25177
  customFontMap,
@@ -25232,7 +25218,7 @@ var AutoResizePanel = ({
25232
25218
  cleanupShimmer,
25233
25219
  onBeforeAutoResize,
25234
25220
  onAfterAutoResize,
25235
- openRouterApiKey,
25221
+ autoResizeUrl,
25236
25222
  chatModel,
25237
25223
  agentImageModel
25238
25224
  ]);
@@ -25503,9 +25489,7 @@ var AutoResizePanelHost = ({ app }) => {
25503
25489
  onRunningChange: (running) => autoResizePanelStore.setRunning(running),
25504
25490
  onBeforeAutoResize: app.props.onBeforeAutoResize,
25505
25491
  onAfterAutoResize: app.props.onAfterAutoResize,
25506
- openRouterApiKey: resolveOpenRouterApiKey(
25507
- app.props.openRouterApiKey
25508
- ),
25492
+ autoResizeUrl: app.props.autoResizeUrl ?? "",
25509
25493
  chatModel: app.props.chatModel ?? "google/gemini-3.1-flash-lite-preview",
25510
25494
  agentImageModel: app.props.agentImageModel
25511
25495
  }
@@ -26567,7 +26551,7 @@ var ImageGeneratorPanel = ({
26567
26551
  };
26568
26552
  };
26569
26553
  const appState = app.state;
26570
- const resolvedApiKey = resolveOpenRouterApiKey(app.props.openRouterApiKey);
26554
+ const imageGenUrl = app.props.imageGenUrl;
26571
26555
  const [prompt, setPrompt] = useState32(() => getPendingPrompt(element.id));
26572
26556
  const [selectedModel, setSelectedModel] = useState32(() => {
26573
26557
  const stored = getStoredSettings(element);
@@ -26787,9 +26771,9 @@ var ImageGeneratorPanel = ({
26787
26771
  setGenerateError("Please enter a prompt.");
26788
26772
  return;
26789
26773
  }
26790
- if (!resolvedApiKey) {
26774
+ if (!imageGenUrl) {
26791
26775
  setGenerateError(
26792
- "No OpenRouter API key found. Set VITE_APP_OPENROUTER_API_KEY in .env."
26776
+ "Image generation is not configured."
26793
26777
  );
26794
26778
  return;
26795
26779
  }
@@ -26832,7 +26816,7 @@ var ImageGeneratorPanel = ({
26832
26816
  selectedRatio,
26833
26817
  cfg.supportsImageSize ? selectedResolution : null,
26834
26818
  cfg.textOutput,
26835
- resolvedApiKey,
26819
+ imageGenUrl,
26836
26820
  referenceImage ?? void 0,
26837
26821
  controller.signal
26838
26822
  );
@@ -26861,7 +26845,7 @@ var ImageGeneratorPanel = ({
26861
26845
  }
26862
26846
  }, [
26863
26847
  prompt,
26864
- resolvedApiKey,
26848
+ imageGenUrl,
26865
26849
  selectedModel,
26866
26850
  selectedRatio,
26867
26851
  selectedResolution,
@@ -27085,18 +27069,6 @@ import { useCallback as useCallback18, useState as useState34 } from "react";
27085
27069
  import { sceneCoordsToViewportCoords as sceneCoordsToViewportCoords9 } from "@orangecatai/common";
27086
27070
  import { getElementAbsoluteCoords as getElementAbsoluteCoords12 } from "@orangecatai/element";
27087
27071
 
27088
- // utils/leonardoApiKey.ts
27089
- function resolveLeonardoApiKey(propKey) {
27090
- const normalizedPropKey = propKey?.trim();
27091
- return (normalizedPropKey ? normalizedPropKey : void 0) ?? (typeof import.meta !== "undefined" && define_import_meta_env_default?.VITE_APP_LEONARDO_API_KEY ? define_import_meta_env_default.VITE_APP_LEONARDO_API_KEY : "") ?? "";
27092
- }
27093
-
27094
- // utils/removeBgApiKey.ts
27095
- function resolveRemoveBgApiKey(propKey) {
27096
- const normalizedPropKey = propKey?.trim();
27097
- return (normalizedPropKey ? normalizedPropKey : void 0) ?? (typeof import.meta !== "undefined" && define_import_meta_env_default?.VITE_APP_REMOVE_BG_API_KEY ? define_import_meta_env_default.VITE_APP_REMOVE_BG_API_KEY : "") ?? "";
27098
- }
27099
-
27100
27072
  // components/ImageQuickEditPanel.tsx
27101
27073
  import { useCallback as useCallback17, useEffect as useEffect35, useRef as useRef29, useState as useState33 } from "react";
27102
27074
  import { sceneCoordsToViewportCoords as sceneCoordsToViewportCoords8 } from "@orangecatai/common";
@@ -27264,7 +27236,7 @@ var ImageQuickEditPanel = ({
27264
27236
  onClose
27265
27237
  }) => {
27266
27238
  const appState = app.state;
27267
- const resolvedApiKey = resolveOpenRouterApiKey(app.props.openRouterApiKey);
27239
+ const imageGenUrl = app.props.imageGenUrl;
27268
27240
  const [isOpen, setIsOpen] = useState33(
27269
27241
  () => forceOpen || hasPendingGeneration(element.id)
27270
27242
  );
@@ -27355,10 +27327,8 @@ var ImageQuickEditPanel = ({
27355
27327
  setGenerateError("Please enter a prompt.");
27356
27328
  return;
27357
27329
  }
27358
- if (!resolvedApiKey) {
27359
- setGenerateError(
27360
- "No OpenRouter API key found. Set VITE_APP_OPENROUTER_API_KEY in .env or pass openRouterApiKey as a prop."
27361
- );
27330
+ if (!imageGenUrl) {
27331
+ setGenerateError("Image generation is not configured.");
27362
27332
  return;
27363
27333
  }
27364
27334
  setIsGenerating(true);
@@ -27401,7 +27371,7 @@ var ImageQuickEditPanel = ({
27401
27371
  selectedRatio,
27402
27372
  cfg.supportsImageSize ? selectedResolution : null,
27403
27373
  cfg.textOutput,
27404
- resolvedApiKey,
27374
+ imageGenUrl,
27405
27375
  refDataUrl,
27406
27376
  controller.signal
27407
27377
  );
@@ -27440,7 +27410,7 @@ var ImageQuickEditPanel = ({
27440
27410
  }
27441
27411
  }, [
27442
27412
  prompt,
27443
- resolvedApiKey,
27413
+ imageGenUrl,
27444
27414
  selectedModel,
27445
27415
  selectedRatio,
27446
27416
  selectedResolution,
@@ -27661,48 +27631,7 @@ var ImageQuickEditPanel = ({
27661
27631
 
27662
27632
  // components/ImageEditToolbar.tsx
27663
27633
  import { Fragment as Fragment15, jsx as jsx94, jsxs as jsxs52 } from "react/jsx-runtime";
27664
- var LEONARDO_API_BASE = "https://cloud.leonardo.ai/api/rest/v1";
27665
- var LEONARDO_POLL_INTERVAL_MS = 1500;
27666
- var LEONARDO_MAX_POLLS = 40;
27667
- var getLeonardoHeaders = (apiKey) => ({
27668
- Authorization: `Bearer ${apiKey}`,
27669
- "Content-Type": "application/json"
27670
- });
27671
- var getImageExtension = (mimeType) => {
27672
- switch (mimeType) {
27673
- case "image/jpeg":
27674
- return "jpeg";
27675
- case "image/jpg":
27676
- return "jpg";
27677
- case "image/webp":
27678
- return "webp";
27679
- default:
27680
- return "png";
27681
- }
27682
- };
27683
- var getOperationLabel = (operation) => operation === "upscale" ? "Upscale" : "Remove background";
27684
- var getOperationStatus = (operation) => operation === "upscale" ? "Upscaling image..." : "Removing background...";
27685
- var sleep = (ms) => new Promise((resolve) => {
27686
- window.setTimeout(resolve, ms);
27687
- });
27688
- var readLeonardoError = async (response, fallbackMessage) => {
27689
- try {
27690
- const body = await response.json();
27691
- const message = body?.error?.message ?? body?.message ?? body?.detail ?? body?.errors?.[0]?.message;
27692
- if (typeof message === "string" && message.trim()) {
27693
- return message;
27694
- }
27695
- } catch {
27696
- }
27697
- try {
27698
- const text = await response.text();
27699
- if (text.trim()) {
27700
- return text.trim();
27701
- }
27702
- } catch {
27703
- }
27704
- return `${fallbackMessage} (${response.status})`;
27705
- };
27634
+ var getOperationStatus = (_operation) => "Removing background...";
27706
27635
  var exportSourceImageDataURL = async (element, sourceDataURL) => {
27707
27636
  const crop = element.crop;
27708
27637
  if (!crop) {
@@ -27736,150 +27665,28 @@ var exportSourceImageDataURL = async (element, sourceDataURL) => {
27736
27665
  img.src = sourceDataURL;
27737
27666
  });
27738
27667
  };
27739
- var uploadImageToLeonardo = async (sourceDataURL, apiKey) => {
27740
- const extension = getImageExtension(getMimeTypeFromDataURL(sourceDataURL));
27741
- const initResponse = await fetch(`${LEONARDO_API_BASE}/init-image`, {
27668
+ var callImageProxy = async (proxyUrl, imageDataUrl) => {
27669
+ const response = await fetch(proxyUrl, {
27742
27670
  method: "POST",
27743
- headers: getLeonardoHeaders(apiKey),
27744
- body: JSON.stringify({ extension })
27745
- });
27746
- if (!initResponse.ok) {
27747
- throw new Error(
27748
- await readLeonardoError(
27749
- initResponse,
27750
- "Leonardo init image request failed"
27751
- )
27752
- );
27753
- }
27754
- const initData = await initResponse.json();
27755
- const uploadInitImage = initData.uploadInitImage;
27756
- if (!uploadInitImage?.id || !uploadInitImage.url) {
27757
- throw new Error("Leonardo init image response was incomplete.");
27758
- }
27759
- const rawFields = uploadInitImage.fields ?? {};
27760
- const fields = typeof rawFields === "string" ? JSON.parse(rawFields) : rawFields;
27761
- const formData = new FormData();
27762
- Object.entries(fields).forEach(([key, value]) => {
27763
- formData.append(key, value);
27764
- });
27765
- formData.append(
27766
- "file",
27767
- dataURLToFile(sourceDataURL, `image.${extension}`)
27768
- );
27769
- await fetch(uploadInitImage.url, {
27770
- method: "POST",
27771
- body: formData,
27772
- mode: "no-cors"
27773
- });
27774
- return uploadInitImage.id;
27775
- };
27776
- var removeBackgroundWithRemoveBg = async (sourceDataURL, apiKey) => {
27777
- const mimeType = getMimeTypeFromDataURL(sourceDataURL);
27778
- const extension = getImageExtension(mimeType);
27779
- const file2 = dataURLToFile(sourceDataURL, `image.${extension}`);
27780
- const formData = new FormData();
27781
- formData.append("size", "auto");
27782
- formData.append("image_file", file2);
27783
- const response = await fetch("https://api.remove.bg/v1.0/removebg", {
27784
- method: "POST",
27785
- headers: { "X-Api-Key": apiKey },
27786
- body: formData
27671
+ headers: { "Content-Type": "application/json" },
27672
+ body: JSON.stringify({ imageDataUrl })
27787
27673
  });
27788
27674
  if (!response.ok) {
27789
- let errorMessage = `Remove background failed (${response.status})`;
27675
+ let message = `Request failed (${response.status})`;
27790
27676
  try {
27791
27677
  const body = await response.json();
27792
- const title = body?.errors?.[0]?.title;
27793
- if (title) {
27794
- errorMessage = title;
27678
+ if (body?.error) {
27679
+ message = body.error;
27795
27680
  }
27796
27681
  } catch {
27797
27682
  }
27798
- throw new Error(errorMessage);
27799
- }
27800
- const arrayBuffer = await response.arrayBuffer();
27801
- return await getDataURL(new Blob([arrayBuffer], { type: "image/png" }));
27802
- };
27803
- var createLeonardoVariation = async (imageId, apiKey) => {
27804
- const response = await fetch(
27805
- `${LEONARDO_API_BASE}/variations/universal-upscaler`,
27806
- {
27807
- method: "POST",
27808
- headers: getLeonardoHeaders(apiKey),
27809
- body: JSON.stringify({
27810
- ultraUpscaleStyle: "ARTISTIC",
27811
- creativityStrength: 1,
27812
- // minimum deviation — keeps output faithful to input
27813
- detailContrast: 5,
27814
- similarity: 10,
27815
- // maximum similarity — no structural divergence
27816
- upscaleMultiplier: 1.5,
27817
- initImageId: imageId
27818
- })
27819
- }
27820
- );
27821
- if (!response.ok) {
27822
- throw new Error(
27823
- await readLeonardoError(response, "Leonardo upscale request failed")
27824
- );
27825
- }
27826
- const body = await response.json();
27827
- const variationId = body.universalUpscaler?.id;
27828
- if (!variationId) {
27829
- throw new Error("Leonardo variation job id was missing from the response.");
27830
- }
27831
- return variationId;
27832
- };
27833
- var pollLeonardoVariationURL = async (variationId, apiKey) => {
27834
- for (let attempt = 0; attempt < LEONARDO_MAX_POLLS; attempt++) {
27835
- const response = await fetch(
27836
- `${LEONARDO_API_BASE}/variations/${variationId}`,
27837
- {
27838
- method: "GET",
27839
- headers: { Authorization: `Bearer ${apiKey}` }
27840
- }
27841
- );
27842
- if (!response.ok) {
27843
- throw new Error(
27844
- await readLeonardoError(
27845
- response,
27846
- "Leonardo variation status check failed"
27847
- )
27848
- );
27849
- }
27850
- const body = await response.json();
27851
- const variation = Array.isArray(body.generated_image_variation_generic) ? body.generated_image_variation_generic[0] : body.generated_image_variation_generic;
27852
- if (!variation) {
27853
- throw new Error(
27854
- "Leonardo variation details were missing from the response."
27855
- );
27856
- }
27857
- if (variation.status === "COMPLETE") {
27858
- if (!variation.url) {
27859
- throw new Error("Leonardo variation completed without an image URL.");
27860
- }
27861
- return variation.url;
27862
- }
27863
- if (variation.status === "FAILED") {
27864
- throw new Error("Leonardo variation failed.");
27865
- }
27866
- if (attempt < LEONARDO_MAX_POLLS - 1) {
27867
- await sleep(LEONARDO_POLL_INTERVAL_MS);
27868
- }
27683
+ throw new Error(message);
27869
27684
  }
27870
- throw new Error("Leonardo variation timed out.");
27871
- };
27872
- var fetchLeonardoResultDataURL = async (resultURL) => {
27873
- const response = await fetch(resultURL);
27874
- if (!response.ok) {
27875
- throw new Error(
27876
- await readLeonardoError(
27877
- response,
27878
- "Leonardo result image download failed"
27879
- )
27880
- );
27685
+ const data = await response.json();
27686
+ if (!data?.imageDataUrl) {
27687
+ throw new Error("No image was returned.");
27881
27688
  }
27882
- return getDataURL(await response.blob());
27689
+ return data.imageDataUrl;
27883
27690
  };
27884
27691
  var QuickEditIcon = () => /* @__PURE__ */ jsxs52(
27885
27692
  "svg",
@@ -27898,25 +27705,6 @@ var QuickEditIcon = () => /* @__PURE__ */ jsxs52(
27898
27705
  ]
27899
27706
  }
27900
27707
  );
27901
- var UpscaleIcon = () => /* @__PURE__ */ jsxs52(
27902
- "svg",
27903
- {
27904
- width: "16",
27905
- height: "16",
27906
- viewBox: "0 0 24 24",
27907
- fill: "none",
27908
- stroke: "currentColor",
27909
- strokeWidth: "2",
27910
- strokeLinecap: "round",
27911
- strokeLinejoin: "round",
27912
- children: [
27913
- /* @__PURE__ */ jsx94("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
27914
- /* @__PURE__ */ jsx94("path", { d: "M9 3v18" }),
27915
- /* @__PURE__ */ jsx94("path", { d: "M14 9l3-3 3 3" }),
27916
- /* @__PURE__ */ jsx94("path", { d: "M17 6v6" })
27917
- ]
27918
- }
27919
- );
27920
27708
  var RemoveBGIcon = () => /* @__PURE__ */ jsxs52(
27921
27709
  "svg",
27922
27710
  {
@@ -28000,7 +27788,6 @@ var PRIMARY_TOOLS = [
28000
27788
  label: "Quick Edit",
28001
27789
  icon: /* @__PURE__ */ jsx94(QuickEditIcon, {})
28002
27790
  },
28003
- { id: "upscale", label: "Upscale", icon: /* @__PURE__ */ jsx94(UpscaleIcon, {}) },
28004
27791
  { id: "remove-bg", label: "Remove BG", icon: /* @__PURE__ */ jsx94(RemoveBGIcon, {}) },
28005
27792
  { id: "crop", label: "Crop", icon: /* @__PURE__ */ jsx94(CropIcon, {}) }
28006
27793
  ];
@@ -28010,92 +27797,18 @@ var ImageEditToolbar = ({
28010
27797
  callbacks
28011
27798
  }) => {
28012
27799
  const appState = app.state;
28013
- const resolvedLeonardoApiKey = resolveLeonardoApiKey(
28014
- app.props.leonardoApiKey
28015
- );
28016
- const resolvedRemoveBgApiKey = resolveRemoveBgApiKey(
28017
- app.props.removeBgApiKey
28018
- );
27800
+ const removeBgUrl = app.props.removeBgUrl;
28019
27801
  const [quickEditOpen, setQuickEditOpen] = useState34(
28020
27802
  () => hasPendingGeneration(element.id)
28021
27803
  );
28022
- const [activeOperation, setActiveOperation] = useState34(null);
27804
+ const [activeOperation, setActiveOperation] = useState34(
27805
+ null
27806
+ );
28023
27807
  const [operationError, setOperationError] = useState34(null);
28024
27808
  const isToolbarBusy = activeOperation !== null;
28025
- const runLeonardoAction = useCallback18(
28026
- async (operation) => {
28027
- if (!resolvedLeonardoApiKey) {
28028
- setOperationError(
28029
- "No Leonardo API key found. Set VITE_APP_LEONARDO_API_KEY in .env or pass leonardoApiKey as a prop."
28030
- );
28031
- return;
28032
- }
28033
- const fileId = element.fileId;
28034
- if (!fileId) {
28035
- setOperationError("The selected image is missing its file reference.");
28036
- return;
28037
- }
28038
- const sourceDataURL = app.files[fileId]?.dataURL;
28039
- if (!sourceDataURL) {
28040
- setOperationError("The selected image file could not be found.");
28041
- return;
28042
- }
28043
- const onBefore = app.props.onBeforeUpscale;
28044
- const onAfter = app.props.onAfterUpscale;
28045
- if (onBefore) {
28046
- try {
28047
- const { allowed, error } = await onBefore();
28048
- if (!allowed) {
28049
- setOperationError(error || "Insufficient credits for upscale");
28050
- return;
28051
- }
28052
- } catch {
28053
- setOperationError("Credit check failed");
28054
- return;
28055
- }
28056
- }
28057
- setActiveOperation(operation);
28058
- setOperationError(null);
28059
- try {
28060
- const exportedSourceDataURL = await exportSourceImageDataURL(
28061
- element,
28062
- sourceDataURL
28063
- );
28064
- const imageId = await uploadImageToLeonardo(
28065
- exportedSourceDataURL,
28066
- resolvedLeonardoApiKey
28067
- );
28068
- const variationId = await createLeonardoVariation(
28069
- imageId,
28070
- resolvedLeonardoApiKey
28071
- );
28072
- const resultURL = await pollLeonardoVariationURL(
28073
- variationId,
28074
- resolvedLeonardoApiKey
28075
- );
28076
- const resultDataURL = await fetchLeonardoResultDataURL(resultURL);
28077
- await app.insertGeneratedImageNearElement(
28078
- resultDataURL,
28079
- element,
28080
- element.width,
28081
- element.height
28082
- );
28083
- onAfter?.();
28084
- } catch (error) {
28085
- setOperationError(
28086
- error instanceof Error ? error.message : `${getOperationLabel(operation)} failed.`
28087
- );
28088
- } finally {
28089
- setActiveOperation(null);
28090
- }
28091
- },
28092
- [app, element, resolvedLeonardoApiKey]
28093
- );
28094
27809
  const runRemoveBgAction = useCallback18(async () => {
28095
- if (!resolvedRemoveBgApiKey) {
28096
- setOperationError(
28097
- "No remove.bg API key found. Set VITE_APP_REMOVE_BG_API_KEY in .env or pass removeBgApiKey as a prop."
28098
- );
27810
+ if (!removeBgUrl) {
27811
+ setOperationError("Remove background is not configured.");
28099
27812
  return;
28100
27813
  }
28101
27814
  const fileId = element.fileId;
@@ -28131,9 +27844,9 @@ var ImageEditToolbar = ({
28131
27844
  element,
28132
27845
  sourceDataURL
28133
27846
  );
28134
- const resultDataURL = await removeBackgroundWithRemoveBg(
28135
- exportedSourceDataURL,
28136
- resolvedRemoveBgApiKey
27847
+ const resultDataURL = await callImageProxy(
27848
+ removeBgUrl,
27849
+ exportedSourceDataURL
28137
27850
  );
28138
27851
  await app.insertGeneratedImageNearElement(
28139
27852
  resultDataURL,
@@ -28149,7 +27862,7 @@ var ImageEditToolbar = ({
28149
27862
  } finally {
28150
27863
  setActiveOperation(null);
28151
27864
  }
28152
- }, [app, element, resolvedRemoveBgApiKey]);
27865
+ }, [app, element, removeBgUrl]);
28153
27866
  const handleAction = useCallback18(
28154
27867
  (id) => {
28155
27868
  setOperationError(null);
@@ -28169,21 +27882,15 @@ var ImageEditToolbar = ({
28169
27882
  }
28170
27883
  return;
28171
27884
  }
28172
- const callbackMap = {
28173
- upscale: callbacks?.onUpscale,
28174
- "remove-bg": callbacks?.onRemoveBG
28175
- };
28176
- if (callbackMap[id]) {
28177
- callbackMap[id]?.();
28178
- return;
28179
- }
28180
- if (id === "upscale") {
28181
- void runLeonardoAction(id);
28182
- } else if (id === "remove-bg") {
28183
- void runRemoveBgAction();
27885
+ if (id === "remove-bg") {
27886
+ if (callbacks?.onRemoveBG) {
27887
+ callbacks.onRemoveBG();
27888
+ } else {
27889
+ void runRemoveBgAction();
27890
+ }
28184
27891
  }
28185
27892
  },
28186
- [callbacks, app.actionManager, runLeonardoAction, runRemoveBgAction]
27893
+ [callbacks, app.actionManager, runRemoveBgAction]
28187
27894
  );
28188
27895
  const handleDownload = useCallback18(() => {
28189
27896
  if (callbacks?.onDownload) {
@@ -52951,11 +52658,11 @@ async function execGenerateImage(args, ctx) {
52951
52658
  statusMessage: "Failed: missing required parameters"
52952
52659
  };
52953
52660
  }
52954
- if (!ctx.openRouterApiKey) {
52661
+ if (!ctx.imageGenUrl) {
52955
52662
  return {
52956
52663
  success: false,
52957
- error: "No OpenRouter API key configured. Set openRouterApiKey prop or VITE_APP_OPENROUTER_API_KEY.",
52958
- statusMessage: "Failed: no OpenRouter API key"
52664
+ error: "Image generation is not configured.",
52665
+ statusMessage: "Failed: image generation not configured"
52959
52666
  };
52960
52667
  }
52961
52668
  const elements = ctx.excalidrawAPI.getSceneElements();
@@ -52979,7 +52686,7 @@ async function execGenerateImage(args, ctx) {
52979
52686
  aspectRatio,
52980
52687
  null,
52981
52688
  true,
52982
- ctx.openRouterApiKey,
52689
+ ctx.imageGenUrl,
52983
52690
  referenceImageDataUrl,
52984
52691
  ctx.signal
52985
52692
  );
@@ -54172,7 +53879,7 @@ Never write full sentences or long phrases for any text slot. Tight, punchy copy
54172
53879
 
54173
53880
  FINALIZING THE AD: When ALL elements are placed \u2014 brand assets, generated images, and the HTML text overlay \u2014 call finalize_ad(frameId) to signal completion. Do not call it before all assets are in place. This triggers the final quality review.`;
54174
53881
  var MAX_ITERATIONS = 15;
54175
- async function callOpenRouter(messages, apiKey, signal, webSearchEnabled, chatModel, onContentChunk, tools = CANVAS_TOOLS) {
53882
+ async function callOpenRouter(messages, chatUrl, signal, webSearchEnabled, chatModel, onContentChunk, tools = CANVAS_TOOLS) {
54176
53883
  const baseModel = chatModel || "minimax/minimax-m3";
54177
53884
  const model = webSearchEnabled ? `${baseModel}:online` : baseModel;
54178
53885
  const body = {
@@ -54208,18 +53915,14 @@ async function callOpenRouter(messages, apiKey, signal, webSearchEnabled, chatMo
54208
53915
  plugins.push({ id: "file-parser", pdf: { engine: "cloudflare-ai" } });
54209
53916
  if (plugins.length > 0)
54210
53917
  body.plugins = plugins;
54211
- const response = await fetch(
54212
- "https://openrouter.ai/api/v1/chat/completions",
54213
- {
54214
- method: "POST",
54215
- signal,
54216
- headers: {
54217
- Authorization: `Bearer ${apiKey}`,
54218
- "Content-Type": "application/json"
54219
- },
54220
- body: JSON.stringify(body)
54221
- }
54222
- );
53918
+ const response = await fetch(chatUrl, {
53919
+ method: "POST",
53920
+ signal,
53921
+ headers: {
53922
+ "Content-Type": "application/json"
53923
+ },
53924
+ body: JSON.stringify(body)
53925
+ });
54223
53926
  if (!response.ok) {
54224
53927
  let message = `OpenRouter error ${response.status}`;
54225
53928
  try {
@@ -54353,7 +54056,7 @@ async function runAgentLoop(opts) {
54353
54056
  frameScreenshot,
54354
54057
  fileAttachments,
54355
54058
  webSearchEnabled,
54356
- apiKey,
54059
+ chatUrl,
54357
54060
  chatModel,
54358
54061
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
54359
54062
  agentImageModel: _agentImageModel,
@@ -54491,7 +54194,7 @@ ${f.textContent}`).join("\n\n")}`;
54491
54194
  });
54492
54195
  const response = await callOpenRouter(
54493
54196
  messages,
54494
- apiKey,
54197
+ chatUrl,
54495
54198
  signal,
54496
54199
  webSearchEnabled,
54497
54200
  chatModel,
@@ -54719,7 +54422,7 @@ Use the frameId="${imageGenData.frameId}" for all elements. Keep all elements wi
54719
54422
  } : void 0,
54720
54423
  userBrief: typeof userMessages[0]?.content === "string" ? userMessages[0].content : "",
54721
54424
  brandContext,
54722
- apiKey,
54425
+ chatUrl,
54723
54426
  reviewerModel: reviewerModel ?? chatModel,
54724
54427
  signal,
54725
54428
  iteration: reviewRounds
@@ -54897,50 +54600,6 @@ async function blobToWavBase64(blob) {
54897
54600
  }
54898
54601
  return btoa(binary);
54899
54602
  }
54900
- async function transcribeAudio(base64Audio, format, apiKey, voiceModel) {
54901
- const model = voiceModel || "mistralai/voxtral-mini-transcribe";
54902
- const response = await fetch(
54903
- "https://openrouter.ai/api/v1/chat/completions",
54904
- {
54905
- method: "POST",
54906
- headers: {
54907
- Authorization: `Bearer ${apiKey}`,
54908
- "Content-Type": "application/json"
54909
- },
54910
- body: JSON.stringify({
54911
- model,
54912
- messages: [
54913
- {
54914
- role: "user",
54915
- content: [
54916
- {
54917
- type: "text",
54918
- text: "Transcribe this audio. Return only the transcription text, nothing else."
54919
- },
54920
- {
54921
- type: "input_audio",
54922
- input_audio: { data: base64Audio, format }
54923
- }
54924
- ]
54925
- }
54926
- ]
54927
- })
54928
- }
54929
- );
54930
- if (!response.ok) {
54931
- let message = `Transcription error ${response.status}`;
54932
- try {
54933
- const err = await response.json();
54934
- if (err?.error?.message) {
54935
- message = err.error.message;
54936
- }
54937
- } catch {
54938
- }
54939
- throw new Error(message);
54940
- }
54941
- const data = await response.json();
54942
- return data?.choices?.[0]?.message?.content || "";
54943
- }
54944
54603
 
54945
54604
  // components/ui/chat-container.tsx
54946
54605
  import { StickToBottom } from "use-stick-to-bottom";
@@ -55289,7 +54948,8 @@ var AIChatPanel = React64.forwardRef(
55289
54948
  ({
55290
54949
  isOpen,
55291
54950
  onClose,
55292
- apiKey,
54951
+ chatUrl,
54952
+ imageGenUrl,
55293
54953
  excalidrawAPI,
55294
54954
  initialMessages,
55295
54955
  initialSessionId,
@@ -55303,7 +54963,6 @@ var AIChatPanel = React64.forwardRef(
55303
54963
  onBeforeImageGen,
55304
54964
  onAfterImageGen,
55305
54965
  chatModel,
55306
- voiceModel,
55307
54966
  transcribeUrl,
55308
54967
  agentImageModel,
55309
54968
  brandContext,
@@ -55587,40 +55246,29 @@ var AIChatPanel = React64.forwardRef(
55587
55246
  setStatusText("Transcribing\u2026");
55588
55247
  try {
55589
55248
  const wavBase64 = await blobToWavBase64(audioBlob);
55590
- let transcription;
55591
- if (transcribeUrl) {
55592
- const res = await fetch(transcribeUrl, {
55593
- method: "POST",
55594
- headers: { "Content-Type": "application/json" },
55595
- body: JSON.stringify({ audio: wavBase64, format: "wav" })
55596
- });
55597
- if (!res.ok) {
55598
- let msg = `Transcription error ${res.status}`;
55599
- try {
55600
- const err = await res.json();
55601
- if (err?.error)
55602
- msg = err.error;
55603
- } catch {
55604
- }
55605
- throw new Error(msg);
55606
- }
55607
- const data = await res.json();
55608
- transcription = data?.text ?? "";
55609
- } else {
55610
- const normalizedKey = resolveOpenRouterApiKey(apiKey);
55611
- if (!normalizedKey) {
55612
- setError("No API key for voice transcription.");
55613
- setIsTranscribing(false);
55614
- setStatusText("");
55615
- return;
55249
+ if (!transcribeUrl) {
55250
+ setError("Voice transcription is not configured.");
55251
+ setIsTranscribing(false);
55252
+ setStatusText("");
55253
+ return;
55254
+ }
55255
+ const res = await fetch(transcribeUrl, {
55256
+ method: "POST",
55257
+ headers: { "Content-Type": "application/json" },
55258
+ body: JSON.stringify({ audio: wavBase64, format: "wav" })
55259
+ });
55260
+ if (!res.ok) {
55261
+ let msg = `Transcription error ${res.status}`;
55262
+ try {
55263
+ const err = await res.json();
55264
+ if (err?.error)
55265
+ msg = err.error;
55266
+ } catch {
55616
55267
  }
55617
- transcription = await transcribeAudio(
55618
- wavBase64,
55619
- "wav",
55620
- normalizedKey,
55621
- voiceModel
55622
- );
55268
+ throw new Error(msg);
55623
55269
  }
55270
+ const data = await res.json();
55271
+ const transcription = data?.text ?? "";
55624
55272
  const updated = (prevPromptRef.current ? `${prevPromptRef.current} ` : "") + transcription;
55625
55273
  prevPromptRef.current = updated;
55626
55274
  setPrompt(updated);
@@ -55640,7 +55288,7 @@ var AIChatPanel = React64.forwardRef(
55640
55288
  stream?.getTracks().forEach((t2) => t2.stop());
55641
55289
  setError("Could not access microphone. Check browser permissions.");
55642
55290
  }
55643
- }, [isRecording, apiKey, transcribeUrl]);
55291
+ }, [isRecording, transcribeUrl]);
55644
55292
  const handleCopy = useCallback29((msgId, content) => {
55645
55293
  navigator.clipboard.writeText(content).then(
55646
55294
  () => {
@@ -55655,16 +55303,13 @@ var AIChatPanel = React64.forwardRef(
55655
55303
  const handleSend = useCallback29(
55656
55304
  async (textOverride) => {
55657
55305
  const text = (textOverride ?? prompt).trim();
55658
- const normalizedKey = resolveOpenRouterApiKey(apiKey);
55659
55306
  if (!text || isLoading) {
55660
55307
  voiceUsedRef.current = false;
55661
55308
  return;
55662
55309
  }
55663
- if (!normalizedKey) {
55310
+ if (!chatUrl) {
55664
55311
  voiceUsedRef.current = false;
55665
- setError(
55666
- "No OpenRouter API key. Set VITE_APP_OPENROUTER_API_KEY in .env or pass the key via the `apiKey` prop."
55667
- );
55312
+ setError("Chat is not configured.");
55668
55313
  return;
55669
55314
  }
55670
55315
  const capturedFiles = [...attachedFiles];
@@ -55747,7 +55392,7 @@ var AIChatPanel = React64.forwardRef(
55747
55392
  try {
55748
55393
  const reply = await callPlainChatAPI(
55749
55394
  nextMessages,
55750
- normalizedKey,
55395
+ chatUrl,
55751
55396
  controller.signal,
55752
55397
  {
55753
55398
  webSearchEnabled: capturedWebSearch,
@@ -55841,14 +55486,14 @@ var AIChatPanel = React64.forwardRef(
55841
55486
  textContent: f.textContent
55842
55487
  })) : void 0,
55843
55488
  webSearchEnabled: capturedWebSearch,
55844
- apiKey: normalizedKey,
55489
+ chatUrl,
55845
55490
  chatModel,
55846
55491
  agentImageModel,
55847
55492
  brandContext,
55848
55493
  customFontIds,
55849
55494
  toolCtx: {
55850
55495
  excalidrawAPI,
55851
- openRouterApiKey: normalizedKey,
55496
+ imageGenUrl: imageGenUrl ?? chatUrl,
55852
55497
  signal: controller.signal,
55853
55498
  agentImageModel,
55854
55499
  onSearchBrandAssets,
@@ -55961,7 +55606,8 @@ var AIChatPanel = React64.forwardRef(
55961
55606
  prompt,
55962
55607
  isLoading,
55963
55608
  messages,
55964
- apiKey,
55609
+ chatUrl,
55610
+ imageGenUrl,
55965
55611
  excalidrawAPI,
55966
55612
  frameRef,
55967
55613
  attachedFiles,
@@ -56496,7 +56142,7 @@ function ReviewCritiqueCard({ feedback }) {
56496
56142
  }
56497
56143
  );
56498
56144
  }
56499
- async function callPlainChatAPI(messages, apiKey, signal, opts) {
56145
+ async function callPlainChatAPI(messages, chatUrl, signal, opts) {
56500
56146
  const baseModel = opts?.chatModel || "minimax/minimax-m3";
56501
56147
  const model = opts?.webSearchEnabled ? `${baseModel}:online` : baseModel;
56502
56148
  const systemMessages = [];
@@ -56545,18 +56191,14 @@ ${f.textContent}`).join("\n\n")}`;
56545
56191
  if (opts?.onChunk) {
56546
56192
  body.stream = true;
56547
56193
  }
56548
- const response = await fetch(
56549
- "https://openrouter.ai/api/v1/chat/completions",
56550
- {
56551
- method: "POST",
56552
- signal,
56553
- headers: {
56554
- Authorization: `Bearer ${apiKey}`,
56555
- "Content-Type": "application/json"
56556
- },
56557
- body: JSON.stringify(body)
56558
- }
56559
- );
56194
+ const response = await fetch(chatUrl, {
56195
+ method: "POST",
56196
+ signal,
56197
+ headers: {
56198
+ "Content-Type": "application/json"
56199
+ },
56200
+ body: JSON.stringify(body)
56201
+ });
56560
56202
  if (!response.ok) {
56561
56203
  let message = `OpenRouter error ${response.status}`;
56562
56204
  try {
@@ -56696,14 +56338,12 @@ var ExcalidrawBase = (props) => {
56696
56338
  renderEmbeddable,
56697
56339
  aiEnabled,
56698
56340
  showDeprecatedFonts,
56699
- leonardoApiKey,
56700
- openRouterApiKey,
56701
- removeBgApiKey,
56341
+ imageGenUrl,
56342
+ autoResizeUrl,
56343
+ removeBgUrl,
56702
56344
  renderScrollbars,
56703
56345
  onBeforeImageGen,
56704
56346
  onAfterImageGen,
56705
- onBeforeUpscale,
56706
- onAfterUpscale,
56707
56347
  onBeforeRemoveBg,
56708
56348
  onAfterRemoveBg,
56709
56349
  onBeforeAutoResize,
@@ -56794,14 +56434,12 @@ var ExcalidrawBase = (props) => {
56794
56434
  renderEmbeddable,
56795
56435
  aiEnabled: aiEnabled !== false,
56796
56436
  showDeprecatedFonts,
56797
- leonardoApiKey,
56798
- openRouterApiKey,
56799
- removeBgApiKey,
56437
+ imageGenUrl,
56438
+ autoResizeUrl,
56439
+ removeBgUrl,
56800
56440
  renderScrollbars,
56801
56441
  onBeforeImageGen,
56802
56442
  onAfterImageGen,
56803
- onBeforeUpscale,
56804
- onAfterUpscale,
56805
56443
  onBeforeRemoveBg,
56806
56444
  onAfterRemoveBg,
56807
56445
  onBeforeAutoResize,