@orangecatai/adgen-canvas 0.0.3 → 0.0.5
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.
- package/dist/dev/{chunk-YOQUQ6O5.js → chunk-3FGOYDLK.js} +3 -3
- package/dist/dev/{chunk-EP27W34X.js → chunk-IRIUFXMO.js} +2 -2
- package/dist/dev/data/{image-IZS5VEYX.js → image-HH4XNQRO.js} +3 -3
- package/dist/dev/index.css +149 -1
- package/dist/dev/index.css.map +2 -2
- package/dist/dev/index.js +1486 -599
- package/dist/dev/index.js.map +4 -4
- package/dist/dev/subset-shared.chunk.js +1 -1
- package/dist/dev/subset-worker.chunk.js +1 -1
- package/dist/prod/{chunk-2ALGWY4R.js → chunk-LUZI7MFZ.js} +2 -2
- package/dist/prod/{chunk-I4SUBR2Z.js → chunk-SG4RCQVC.js} +1 -1
- package/dist/prod/data/image-J2ZJZU4A.js +1 -0
- package/dist/prod/index.css +1 -1
- package/dist/prod/index.js +42 -32
- package/dist/prod/subset-shared.chunk.js +1 -1
- package/dist/prod/subset-worker.chunk.js +1 -1
- package/dist/types/excalidraw/actions/actionAddToLibrary.d.ts +1 -1
- package/dist/types/excalidraw/actions/actionCanvas.d.ts +177 -0
- package/dist/types/excalidraw/actions/actionClipboard.d.ts +7 -7
- package/dist/types/excalidraw/actions/actionCropEditor.d.ts +2 -2
- package/dist/types/excalidraw/actions/actionDuplicateSelection.d.ts +2 -2
- package/dist/types/excalidraw/actions/actionElementLink.d.ts +6 -6
- package/dist/types/excalidraw/actions/actionEmbeddable.d.ts +1 -1
- package/dist/types/excalidraw/actions/actionLinearEditor.d.ts +7 -7
- package/dist/types/excalidraw/actions/actionLink.d.ts +3 -3
- package/dist/types/excalidraw/actions/actionMenu.d.ts +1 -1
- package/dist/types/excalidraw/actions/actionSelectAll.d.ts +4 -4
- package/dist/types/excalidraw/actions/actionStyles.d.ts +2 -2
- package/dist/types/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +4 -4
- package/dist/types/excalidraw/actions/actionToggleShapeSwitch.d.ts +3 -3
- package/dist/types/excalidraw/actions/actionToggleStats.d.ts +2 -2
- package/dist/types/excalidraw/actions/actionToggleViewMode.d.ts +4 -4
- package/dist/types/excalidraw/actions/actionToggleZenMode.d.ts +4 -4
- package/dist/types/excalidraw/actions/actionZindex.d.ts +8 -8
- package/dist/types/excalidraw/actions/index.d.ts +1 -1
- package/dist/types/excalidraw/actions/types.d.ts +1 -1
- package/dist/types/excalidraw/components/AIChatPanel.d.ts +49 -3
- package/dist/types/excalidraw/components/Actions.d.ts +3 -0
- package/dist/types/excalidraw/components/DefaultSidebar.d.ts +1 -1
- package/dist/types/excalidraw/components/ImageGeneratorPanel.d.ts +6 -1
- package/dist/types/excalidraw/components/ImageQuickEditPanel.d.ts +6 -1
- package/dist/types/excalidraw/components/Sidebar/Sidebar.d.ts +2 -2
- package/dist/types/excalidraw/components/ai-chat/agentLoop.d.ts +13 -0
- package/dist/types/excalidraw/components/ai-chat/audioUtils.d.ts +10 -0
- package/dist/types/excalidraw/index.d.ts +4 -1
- package/dist/types/excalidraw/types.d.ts +14 -0
- package/dist/types/excalidraw/utils/openRouterApiKey.d.ts +1 -0
- package/package.json +1 -1
- package/dist/prod/data/image-PKF4YK4A.js +0 -1
- /package/dist/dev/{chunk-YOQUQ6O5.js.map → chunk-3FGOYDLK.js.map} +0 -0
- /package/dist/dev/{chunk-EP27W34X.js.map → chunk-IRIUFXMO.js.map} +0 -0
- /package/dist/dev/data/{image-IZS5VEYX.js.map → image-HH4XNQRO.js.map} +0 -0
package/dist/dev/index.js
CHANGED
|
@@ -66,10 +66,10 @@ import {
|
|
|
66
66
|
serializeAsJSON,
|
|
67
67
|
serializeLibraryAsJSON,
|
|
68
68
|
strokeRectWithRotation_simple
|
|
69
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-3FGOYDLK.js";
|
|
70
70
|
import {
|
|
71
71
|
define_import_meta_env_default
|
|
72
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-IRIUFXMO.js";
|
|
73
73
|
import {
|
|
74
74
|
en_default
|
|
75
75
|
} from "./chunk-IFMURN5W.js";
|
|
@@ -158,7 +158,7 @@ import {
|
|
|
158
158
|
isWritableElement as isWritableElement5,
|
|
159
159
|
sceneCoordsToViewportCoords as sceneCoordsToViewportCoords10,
|
|
160
160
|
tupleToCoors,
|
|
161
|
-
viewportCoordsToSceneCoords as
|
|
161
|
+
viewportCoordsToSceneCoords as viewportCoordsToSceneCoords4,
|
|
162
162
|
wrapEvent as wrapEvent2,
|
|
163
163
|
updateObject as updateObject2,
|
|
164
164
|
updateActiveTool as updateActiveTool7,
|
|
@@ -197,7 +197,7 @@ import {
|
|
|
197
197
|
import {
|
|
198
198
|
getObservedAppState,
|
|
199
199
|
getCommonBounds as getCommonBounds11,
|
|
200
|
-
getElementAbsoluteCoords as
|
|
200
|
+
getElementAbsoluteCoords as getElementAbsoluteCoords12,
|
|
201
201
|
bindOrUnbindBindingElements as bindOrUnbindBindingElements2,
|
|
202
202
|
fixBindingsAfterDeletion as fixBindingsAfterDeletion2,
|
|
203
203
|
getHoveredElementForBinding as getHoveredElementForBinding2,
|
|
@@ -7687,9 +7687,14 @@ import {
|
|
|
7687
7687
|
ZOOM_STEP,
|
|
7688
7688
|
updateActiveTool as updateActiveTool2,
|
|
7689
7689
|
CODES as CODES2,
|
|
7690
|
-
KEYS as KEYS12
|
|
7690
|
+
KEYS as KEYS12,
|
|
7691
|
+
viewportCoordsToSceneCoords
|
|
7691
7692
|
} from "@orangecatai/common";
|
|
7692
|
-
import {
|
|
7693
|
+
import {
|
|
7694
|
+
getElementAbsoluteCoords,
|
|
7695
|
+
getNonDeletedElements as getNonDeletedElements5,
|
|
7696
|
+
getVisibleElements
|
|
7697
|
+
} from "@orangecatai/element";
|
|
7693
7698
|
import { newElementWith as newElementWith3 } from "@orangecatai/element";
|
|
7694
7699
|
import { getCommonBounds } from "@orangecatai/element";
|
|
7695
7700
|
import { CaptureUpdateAction as CaptureUpdateAction6 } from "@orangecatai/element";
|
|
@@ -8070,6 +8075,77 @@ var zoomValueToFitBoundsOnViewport = (bounds, viewportDimensions, viewportZoomFa
|
|
|
8070
8075
|
const adjustedZoomValue = smallestZoomValue * clamp(viewportZoomFactor, 0.1, 1);
|
|
8071
8076
|
return Math.min(adjustedZoomValue, 1);
|
|
8072
8077
|
};
|
|
8078
|
+
var COMFORTABLE_VIEWPORT_ZOOM_FACTOR = 0.8;
|
|
8079
|
+
var intersectsBounds = (a, b) => a[0] <= b[2] && a[2] >= b[0] && a[1] <= b[3] && a[3] >= b[1];
|
|
8080
|
+
var expandBounds = (bounds, margin) => [
|
|
8081
|
+
bounds[0] - margin,
|
|
8082
|
+
bounds[1] - margin,
|
|
8083
|
+
bounds[2] + margin,
|
|
8084
|
+
bounds[3] + margin
|
|
8085
|
+
];
|
|
8086
|
+
var getBoundsCenterDistance = (bounds, point) => {
|
|
8087
|
+
const centerX = (bounds[0] + bounds[2]) / 2;
|
|
8088
|
+
const centerY = (bounds[1] + bounds[3]) / 2;
|
|
8089
|
+
return Math.hypot(centerX - point.x, centerY - point.y);
|
|
8090
|
+
};
|
|
8091
|
+
var toSceneBounds = (bounds) => [bounds[0], bounds[1], bounds[2], bounds[3]];
|
|
8092
|
+
var getNearestContentCluster = (elements, appState) => {
|
|
8093
|
+
const nonDeletedElements = getNonDeletedElements5(elements);
|
|
8094
|
+
if (!nonDeletedElements.length) {
|
|
8095
|
+
return [];
|
|
8096
|
+
}
|
|
8097
|
+
const candidateElements = getVisibleElements(nonDeletedElements);
|
|
8098
|
+
const clusterCandidates = candidateElements.length ? candidateElements : nonDeletedElements;
|
|
8099
|
+
const elementsMap = new Map(
|
|
8100
|
+
nonDeletedElements.map((element) => [element.id, element])
|
|
8101
|
+
);
|
|
8102
|
+
const viewportCenter = viewportCoordsToSceneCoords(
|
|
8103
|
+
{
|
|
8104
|
+
clientX: appState.offsetLeft + appState.width / 2,
|
|
8105
|
+
clientY: appState.offsetTop + appState.height / 2
|
|
8106
|
+
},
|
|
8107
|
+
appState
|
|
8108
|
+
);
|
|
8109
|
+
const anchor = clusterCandidates.reduce((closest, element) => {
|
|
8110
|
+
const bounds = toSceneBounds(
|
|
8111
|
+
getElementAbsoluteCoords(element, elementsMap)
|
|
8112
|
+
);
|
|
8113
|
+
if (!closest) {
|
|
8114
|
+
return { element, bounds };
|
|
8115
|
+
}
|
|
8116
|
+
return getBoundsCenterDistance(bounds, viewportCenter) < getBoundsCenterDistance(closest.bounds, viewportCenter) ? { element, bounds } : closest;
|
|
8117
|
+
}, null);
|
|
8118
|
+
if (!anchor) {
|
|
8119
|
+
return [];
|
|
8120
|
+
}
|
|
8121
|
+
const proximityMargin = Math.max(
|
|
8122
|
+
160 / appState.zoom.value,
|
|
8123
|
+
0.12 * Math.min(appState.width, appState.height) / appState.zoom.value
|
|
8124
|
+
);
|
|
8125
|
+
const cluster = /* @__PURE__ */ new Map([
|
|
8126
|
+
[anchor.element.id, anchor.element]
|
|
8127
|
+
]);
|
|
8128
|
+
let clusterBounds = anchor.bounds;
|
|
8129
|
+
let changed = true;
|
|
8130
|
+
while (changed) {
|
|
8131
|
+
changed = false;
|
|
8132
|
+
const expandedBounds = expandBounds(clusterBounds, proximityMargin);
|
|
8133
|
+
for (const element of clusterCandidates) {
|
|
8134
|
+
if (cluster.has(element.id)) {
|
|
8135
|
+
continue;
|
|
8136
|
+
}
|
|
8137
|
+
const elementBounds = toSceneBounds(
|
|
8138
|
+
getElementAbsoluteCoords(element, elementsMap)
|
|
8139
|
+
);
|
|
8140
|
+
if (intersectsBounds(elementBounds, expandedBounds)) {
|
|
8141
|
+
cluster.set(element.id, element);
|
|
8142
|
+
clusterBounds = getCommonBounds([...cluster.values()]);
|
|
8143
|
+
changed = true;
|
|
8144
|
+
}
|
|
8145
|
+
}
|
|
8146
|
+
}
|
|
8147
|
+
return [...cluster.values()];
|
|
8148
|
+
};
|
|
8073
8149
|
var zoomToFitBounds = ({
|
|
8074
8150
|
bounds,
|
|
8075
8151
|
appState,
|
|
@@ -8207,6 +8283,50 @@ var actionZoomToFit = register({
|
|
|
8207
8283
|
}),
|
|
8208
8284
|
keyTest: (event) => event.code === CODES2.ONE && event.shiftKey && !event.altKey && !event[KEYS12.CTRL_OR_CMD]
|
|
8209
8285
|
});
|
|
8286
|
+
var actionScrollBackToContent = register({
|
|
8287
|
+
name: "scrollBackToContent",
|
|
8288
|
+
label: "buttons.scrollBackToContent",
|
|
8289
|
+
icon: zoomAreaIcon,
|
|
8290
|
+
viewMode: true,
|
|
8291
|
+
trackEvent: { category: "canvas" },
|
|
8292
|
+
perform: (elements, appState, _, app) => {
|
|
8293
|
+
const selectedElements = app.scene.getSelectedElements(appState);
|
|
8294
|
+
const targetElements = selectedElements.length ? selectedElements : getNearestContentCluster(elements, appState);
|
|
8295
|
+
if (!targetElements.length) {
|
|
8296
|
+
return false;
|
|
8297
|
+
}
|
|
8298
|
+
return zoomToFit({
|
|
8299
|
+
targetElements,
|
|
8300
|
+
appState: {
|
|
8301
|
+
...appState,
|
|
8302
|
+
userToFollow: null
|
|
8303
|
+
},
|
|
8304
|
+
fitToViewport: true,
|
|
8305
|
+
viewportZoomFactor: COMFORTABLE_VIEWPORT_ZOOM_FACTOR,
|
|
8306
|
+
canvasOffsets: app.getEditorUIOffsets()
|
|
8307
|
+
});
|
|
8308
|
+
},
|
|
8309
|
+
PanelComponent: ({ updateData }) => /* @__PURE__ */ jsx36(
|
|
8310
|
+
Tooltip,
|
|
8311
|
+
{
|
|
8312
|
+
label: t("buttons.scrollBackToContent"),
|
|
8313
|
+
style: { height: "100%" },
|
|
8314
|
+
children: /* @__PURE__ */ jsx36(
|
|
8315
|
+
ToolButton,
|
|
8316
|
+
{
|
|
8317
|
+
type: "button",
|
|
8318
|
+
className: "scroll-back-to-content-button zoom-button",
|
|
8319
|
+
icon: zoomAreaIcon,
|
|
8320
|
+
title: t("buttons.scrollBackToContent"),
|
|
8321
|
+
"aria-label": t("buttons.scrollBackToContent"),
|
|
8322
|
+
onClick: () => {
|
|
8323
|
+
updateData(null);
|
|
8324
|
+
}
|
|
8325
|
+
}
|
|
8326
|
+
)
|
|
8327
|
+
}
|
|
8328
|
+
)
|
|
8329
|
+
});
|
|
8210
8330
|
var actionToggleTheme = register({
|
|
8211
8331
|
name: "toggleTheme",
|
|
8212
8332
|
label: (_, appState) => {
|
|
@@ -9626,7 +9746,7 @@ var exportCanvas = async (type, elements, appState, files, {
|
|
|
9626
9746
|
let blob = canvasToBlob(tempCanvas);
|
|
9627
9747
|
if (appState.exportEmbedScene) {
|
|
9628
9748
|
blob = blob.then(
|
|
9629
|
-
(blob2) => import("./data/image-
|
|
9749
|
+
(blob2) => import("./data/image-HH4XNQRO.js").then(
|
|
9630
9750
|
({ encodePngMetadata: encodePngMetadata2 }) => encodePngMetadata2({
|
|
9631
9751
|
blob: blob2,
|
|
9632
9752
|
metadata: serializeAsJSON(elements, appState, files, "local")
|
|
@@ -11863,13 +11983,13 @@ import {
|
|
|
11863
11983
|
useState as useState10
|
|
11864
11984
|
} from "react";
|
|
11865
11985
|
import { EVENT as EVENT5, HYPERLINK_TOOLTIP_DELAY, KEYS as KEYS27 } from "@orangecatai/common";
|
|
11866
|
-
import { getElementAbsoluteCoords } from "@orangecatai/element";
|
|
11986
|
+
import { getElementAbsoluteCoords as getElementAbsoluteCoords2 } from "@orangecatai/element";
|
|
11867
11987
|
import { hitElementBoundingBox } from "@orangecatai/element";
|
|
11868
11988
|
import { isElementLink } from "@orangecatai/element";
|
|
11869
11989
|
import { getEmbedLink, embeddableURLValidator } from "@orangecatai/element";
|
|
11870
11990
|
import {
|
|
11871
11991
|
sceneCoordsToViewportCoords,
|
|
11872
|
-
viewportCoordsToSceneCoords,
|
|
11992
|
+
viewportCoordsToSceneCoords as viewportCoordsToSceneCoords2,
|
|
11873
11993
|
wrapEvent,
|
|
11874
11994
|
isLocalLink,
|
|
11875
11995
|
normalizeLink
|
|
@@ -12122,7 +12242,7 @@ var Hyperlink = ({
|
|
|
12122
12242
|
);
|
|
12123
12243
|
};
|
|
12124
12244
|
var getCoordsForPopover = (element, appState, elementsMap) => {
|
|
12125
|
-
const [x1, y1] =
|
|
12245
|
+
const [x1, y1] = getElementAbsoluteCoords2(element, elementsMap);
|
|
12126
12246
|
const { x: viewportX, y: viewportY } = sceneCoordsToViewportCoords(
|
|
12127
12247
|
{ sceneX: x1 + element.width / 2, sceneY: y1 },
|
|
12128
12248
|
appState
|
|
@@ -12154,7 +12274,7 @@ var renderTooltip = (element, appState, elementsMap) => {
|
|
|
12154
12274
|
tooltipDiv.classList.add("excalidraw-tooltip--visible");
|
|
12155
12275
|
tooltipDiv.style.maxWidth = "20rem";
|
|
12156
12276
|
tooltipDiv.textContent = isElementLink(element.link) ? t("labels.link.goToElement") : element.link;
|
|
12157
|
-
const [x1, y1, x2, y2] =
|
|
12277
|
+
const [x1, y1, x2, y2] = getElementAbsoluteCoords2(element, elementsMap);
|
|
12158
12278
|
const [linkX, linkY, linkWidth, linkHeight] = getLinkHandleFromCoords(
|
|
12159
12279
|
[x1, y1, x2, y2],
|
|
12160
12280
|
element.angle,
|
|
@@ -12187,7 +12307,7 @@ var hideHyperlinkToolip = () => {
|
|
|
12187
12307
|
}
|
|
12188
12308
|
};
|
|
12189
12309
|
var shouldHideLinkPopup = (element, elementsMap, appState, [clientX, clientY]) => {
|
|
12190
|
-
const { x: sceneX, y: sceneY } =
|
|
12310
|
+
const { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords2(
|
|
12191
12311
|
{ clientX, clientY },
|
|
12192
12312
|
appState
|
|
12193
12313
|
);
|
|
@@ -12195,7 +12315,7 @@ var shouldHideLinkPopup = (element, elementsMap, appState, [clientX, clientY]) =
|
|
|
12195
12315
|
if (hitElementBoundingBox(pointFrom4(sceneX, sceneY), element, elementsMap)) {
|
|
12196
12316
|
return false;
|
|
12197
12317
|
}
|
|
12198
|
-
const [x1, y1, x2] =
|
|
12318
|
+
const [x1, y1, x2] = getElementAbsoluteCoords2(element, elementsMap);
|
|
12199
12319
|
if (sceneX >= x1 && sceneX <= x2 && sceneY >= y1 - SPACE_BOTTOM && sceneY <= y1) {
|
|
12200
12320
|
return false;
|
|
12201
12321
|
}
|
|
@@ -12444,7 +12564,7 @@ import {
|
|
|
12444
12564
|
} from "@orangecatai/element";
|
|
12445
12565
|
import {
|
|
12446
12566
|
getCommonBoundingBox as getCommonBoundingBox2,
|
|
12447
|
-
getElementAbsoluteCoords as
|
|
12567
|
+
getElementAbsoluteCoords as getElementAbsoluteCoords3
|
|
12448
12568
|
} from "@orangecatai/element";
|
|
12449
12569
|
import {
|
|
12450
12570
|
getBoundTextElement as getBoundTextElement4,
|
|
@@ -12547,7 +12667,7 @@ var Panel = ({
|
|
|
12547
12667
|
positionRef.current = newPositionRef;
|
|
12548
12668
|
let bottomLeft;
|
|
12549
12669
|
if (elements2.length === 1) {
|
|
12550
|
-
const [x1, , , y2, cx, cy] =
|
|
12670
|
+
const [x1, , , y2, cx, cy] = getElementAbsoluteCoords3(
|
|
12551
12671
|
elements2[0],
|
|
12552
12672
|
app.scene.getNonDeletedElementsMap()
|
|
12553
12673
|
);
|
|
@@ -17769,14 +17889,17 @@ var ShapesSwitcher = ({
|
|
|
17769
17889
|
] })
|
|
17770
17890
|
] });
|
|
17771
17891
|
};
|
|
17892
|
+
var ScrollBackToContentAction = ({
|
|
17893
|
+
renderAction
|
|
17894
|
+
}) => renderAction("scrollBackToContent");
|
|
17772
17895
|
var ZoomActions = ({
|
|
17773
17896
|
renderAction,
|
|
17774
17897
|
zoom
|
|
17775
|
-
}) => /* @__PURE__ */
|
|
17898
|
+
}) => /* @__PURE__ */ jsxs42(Stack_default.Row, { align: "center", className: CLASSES5.ZOOM_ACTIONS, children: [
|
|
17776
17899
|
renderAction("zoomOut"),
|
|
17777
17900
|
renderAction("resetZoom"),
|
|
17778
17901
|
renderAction("zoomIn")
|
|
17779
|
-
] })
|
|
17902
|
+
] });
|
|
17780
17903
|
var UndoRedoActions = ({
|
|
17781
17904
|
renderAction,
|
|
17782
17905
|
className
|
|
@@ -18679,7 +18802,7 @@ import {
|
|
|
18679
18802
|
vectorFromPoint,
|
|
18680
18803
|
vectorScale
|
|
18681
18804
|
} from "@orangecatai/math";
|
|
18682
|
-
import { getElementAbsoluteCoords as
|
|
18805
|
+
import { getElementAbsoluteCoords as getElementAbsoluteCoords4 } from "@orangecatai/element";
|
|
18683
18806
|
var getCurvePathOps = (shape) => {
|
|
18684
18807
|
if (!shape) {
|
|
18685
18808
|
return [];
|
|
@@ -19011,7 +19134,7 @@ var FOCUS_POINT_SIZE = 10 / 1.5;
|
|
|
19011
19134
|
// ../element/src/sizeHelpers.ts
|
|
19012
19135
|
import {
|
|
19013
19136
|
SHIFT_LOCKING_ANGLE,
|
|
19014
|
-
viewportCoordsToSceneCoords as
|
|
19137
|
+
viewportCoordsToSceneCoords as viewportCoordsToSceneCoords3
|
|
19015
19138
|
} from "@orangecatai/common";
|
|
19016
19139
|
import {
|
|
19017
19140
|
normalizeRadians,
|
|
@@ -20988,7 +21111,7 @@ import { TOOL_TYPE, KEYS as KEYS43 } from "@orangecatai/common";
|
|
|
20988
21111
|
import {
|
|
20989
21112
|
getCommonBounds as getCommonBounds4,
|
|
20990
21113
|
getDraggedElementsBounds,
|
|
20991
|
-
getElementAbsoluteCoords as
|
|
21114
|
+
getElementAbsoluteCoords as getElementAbsoluteCoords6
|
|
20992
21115
|
} from "@orangecatai/element";
|
|
20993
21116
|
import { isBoundToContainer as isBoundToContainer6 } from "@orangecatai/element";
|
|
20994
21117
|
import { getMaximumGroups } from "@orangecatai/element";
|
|
@@ -21051,7 +21174,7 @@ var getElementsCorners = (elements, elementsMap, {
|
|
|
21051
21174
|
let result = [];
|
|
21052
21175
|
if (elements.length === 1) {
|
|
21053
21176
|
const element = elements[0];
|
|
21054
|
-
let [x1, y1, x2, y2, cx, cy] =
|
|
21177
|
+
let [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords6(
|
|
21055
21178
|
element,
|
|
21056
21179
|
elementsMap
|
|
21057
21180
|
);
|
|
@@ -22051,11 +22174,11 @@ var Renderer = class {
|
|
|
22051
22174
|
|
|
22052
22175
|
// components/ElementCanvasButtons.tsx
|
|
22053
22176
|
import { sceneCoordsToViewportCoords as sceneCoordsToViewportCoords3 } from "@orangecatai/common";
|
|
22054
|
-
import { getElementAbsoluteCoords as
|
|
22177
|
+
import { getElementAbsoluteCoords as getElementAbsoluteCoords7 } from "@orangecatai/element";
|
|
22055
22178
|
import { jsx as jsx82 } from "react/jsx-runtime";
|
|
22056
22179
|
var CONTAINER_PADDING = 5;
|
|
22057
22180
|
var getContainerCoords2 = (element, appState, elementsMap) => {
|
|
22058
|
-
const [x1, y1] =
|
|
22181
|
+
const [x1, y1] = getElementAbsoluteCoords7(element, elementsMap);
|
|
22059
22182
|
const { x: viewportX, y: viewportY } = sceneCoordsToViewportCoords3(
|
|
22060
22183
|
{ sceneX: x1 + element.width, sceneY: y1 },
|
|
22061
22184
|
appState
|
|
@@ -22092,7 +22215,7 @@ var ElementCanvasButtons = ({
|
|
|
22092
22215
|
// components/FrameToolbar.tsx
|
|
22093
22216
|
import { useCallback as useCallback14, useEffect as useEffect29, useRef as useRef24, useState as useState27 } from "react";
|
|
22094
22217
|
import { sceneCoordsToViewportCoords as sceneCoordsToViewportCoords4 } from "@orangecatai/common";
|
|
22095
|
-
import { getElementAbsoluteCoords as
|
|
22218
|
+
import { getElementAbsoluteCoords as getElementAbsoluteCoords8 } from "@orangecatai/element";
|
|
22096
22219
|
import { isFrameLikeElement as isFrameLikeElement9 } from "@orangecatai/element";
|
|
22097
22220
|
import { Fragment as Fragment11, jsx as jsx83, jsxs as jsxs45 } from "react/jsx-runtime";
|
|
22098
22221
|
var ASPECT_RATIOS = [
|
|
@@ -22289,7 +22412,7 @@ var FrameToolbar = ({
|
|
|
22289
22412
|
Math.round(element.width),
|
|
22290
22413
|
Math.round(element.height)
|
|
22291
22414
|
);
|
|
22292
|
-
const [x1, y1] =
|
|
22415
|
+
const [x1, y1] = getElementAbsoluteCoords8(
|
|
22293
22416
|
element,
|
|
22294
22417
|
app.scene.getNonDeletedElementsMap()
|
|
22295
22418
|
);
|
|
@@ -22482,13 +22605,14 @@ import { useCallback as useCallback15, useEffect as useEffect30, useRef as useRe
|
|
|
22482
22605
|
import { sceneCoordsToViewportCoords as sceneCoordsToViewportCoords5 } from "@orangecatai/common";
|
|
22483
22606
|
import {
|
|
22484
22607
|
CaptureUpdateAction as CaptureUpdateAction36,
|
|
22485
|
-
getElementAbsoluteCoords as
|
|
22608
|
+
getElementAbsoluteCoords as getElementAbsoluteCoords9
|
|
22486
22609
|
} from "@orangecatai/element";
|
|
22487
22610
|
import { ArrowUp, ImagePlus, X } from "lucide-react";
|
|
22488
22611
|
|
|
22489
22612
|
// utils/geminiApiKey.ts
|
|
22490
22613
|
function resolveGeminiApiKey(propKey) {
|
|
22491
|
-
|
|
22614
|
+
const normalizedPropKey = propKey?.trim();
|
|
22615
|
+
return (normalizedPropKey ? normalizedPropKey : void 0) ?? (typeof import.meta !== "undefined" && define_import_meta_env_default?.VITE_APP_GEMINI_API_KEY ? define_import_meta_env_default.VITE_APP_GEMINI_API_KEY : "") ?? "";
|
|
22492
22616
|
}
|
|
22493
22617
|
|
|
22494
22618
|
// components/image-generation/pendingGenerations.ts
|
|
@@ -23144,14 +23268,28 @@ var RatioShapeIcon = ({ label }) => {
|
|
|
23144
23268
|
};
|
|
23145
23269
|
var ImageGeneratorPanel = ({
|
|
23146
23270
|
element,
|
|
23147
|
-
app
|
|
23271
|
+
app,
|
|
23272
|
+
onBeforeImageGen,
|
|
23273
|
+
onAfterImageGen
|
|
23148
23274
|
}) => {
|
|
23275
|
+
const getStoredSettings = (frame) => {
|
|
23276
|
+
const stored = frame.customData?.imageGenerator;
|
|
23277
|
+
if (!stored) {
|
|
23278
|
+
return null;
|
|
23279
|
+
}
|
|
23280
|
+
return {
|
|
23281
|
+
model: stored.model,
|
|
23282
|
+
ratio: stored.ratio,
|
|
23283
|
+
resolution: stored.resolution
|
|
23284
|
+
};
|
|
23285
|
+
};
|
|
23149
23286
|
const appState = app.state;
|
|
23150
23287
|
const resolvedApiKey = resolveGeminiApiKey(app.props.geminiApiKey);
|
|
23151
23288
|
const [prompt, setPrompt] = useState29(() => getPendingPrompt(element.id));
|
|
23152
23289
|
const [selectedModel, setSelectedModel] = useState29(() => {
|
|
23153
|
-
const
|
|
23154
|
-
|
|
23290
|
+
const stored = getStoredSettings(element);
|
|
23291
|
+
const pending = getPendingSettings(element.id);
|
|
23292
|
+
return stored?.model ?? pending?.model ?? "Gemini 3.1 Flash";
|
|
23155
23293
|
});
|
|
23156
23294
|
const modelCfg = MODEL_CONFIG[selectedModel];
|
|
23157
23295
|
const availableRatios = modelCfg.supportedRatios;
|
|
@@ -23159,15 +23297,39 @@ var ImageGeneratorPanel = ({
|
|
|
23159
23297
|
const defaultRatio = availableRatios.includes("2:3") ? "2:3" : availableRatios[0];
|
|
23160
23298
|
const defaultResolution = availableResolutions.includes("2K") ? "2K" : availableResolutions[availableResolutions.length - 1];
|
|
23161
23299
|
const [selectedRatio, setSelectedRatio] = useState29(() => {
|
|
23162
|
-
const
|
|
23163
|
-
|
|
23300
|
+
const stored = getStoredSettings(element);
|
|
23301
|
+
const pending = getPendingSettings(element.id);
|
|
23302
|
+
return stored?.ratio ?? pending?.ratio ?? defaultRatio;
|
|
23164
23303
|
});
|
|
23165
23304
|
const [selectedResolution, setSelectedResolution] = useState29(
|
|
23166
23305
|
() => {
|
|
23167
|
-
const
|
|
23168
|
-
|
|
23306
|
+
const stored = getStoredSettings(element);
|
|
23307
|
+
const pending = getPendingSettings(element.id);
|
|
23308
|
+
return stored?.resolution ?? pending?.resolution ?? defaultResolution;
|
|
23169
23309
|
}
|
|
23170
23310
|
);
|
|
23311
|
+
const persistFrameSettings = useCallback15(
|
|
23312
|
+
(settings) => {
|
|
23313
|
+
app.scene.mutateElement(element, {
|
|
23314
|
+
customData: {
|
|
23315
|
+
...element.customData ?? {},
|
|
23316
|
+
imageGenerator: settings
|
|
23317
|
+
}
|
|
23318
|
+
});
|
|
23319
|
+
},
|
|
23320
|
+
[app.scene, element]
|
|
23321
|
+
);
|
|
23322
|
+
const fitFrameIntoView = useCallback15(
|
|
23323
|
+
(frame, animate = false) => {
|
|
23324
|
+
app.scrollToContent(frame, {
|
|
23325
|
+
fitToViewport: true,
|
|
23326
|
+
viewportZoomFactor: 0.8,
|
|
23327
|
+
animate,
|
|
23328
|
+
canvasOffsets: app.getEditorUIOffsets()
|
|
23329
|
+
});
|
|
23330
|
+
},
|
|
23331
|
+
[app]
|
|
23332
|
+
);
|
|
23171
23333
|
const isInitialModelMount = useRef26(true);
|
|
23172
23334
|
useEffect30(() => {
|
|
23173
23335
|
if (isInitialModelMount.current) {
|
|
@@ -23196,8 +23358,13 @@ var ImageGeneratorPanel = ({
|
|
|
23196
23358
|
width: dims.width,
|
|
23197
23359
|
height: dims.height
|
|
23198
23360
|
});
|
|
23361
|
+
persistFrameSettings({
|
|
23362
|
+
model: selectedModel,
|
|
23363
|
+
ratio: newRatio,
|
|
23364
|
+
resolution: newRes
|
|
23365
|
+
});
|
|
23199
23366
|
app.syncActionResult({ captureUpdate: CaptureUpdateAction36.IMMEDIATELY });
|
|
23200
|
-
}, [selectedModel]);
|
|
23367
|
+
}, [selectedModel, app, element, persistFrameSettings]);
|
|
23201
23368
|
const [isGenerating, setIsGenerating] = useState29(
|
|
23202
23369
|
() => hasPendingGeneration(element.id)
|
|
23203
23370
|
);
|
|
@@ -23217,6 +23384,32 @@ var ImageGeneratorPanel = ({
|
|
|
23217
23384
|
elementRef.current = element;
|
|
23218
23385
|
appRef.current = app;
|
|
23219
23386
|
});
|
|
23387
|
+
useEffect30(() => {
|
|
23388
|
+
if (!element.customData?.imageGeneratorAutoFitPending) {
|
|
23389
|
+
return;
|
|
23390
|
+
}
|
|
23391
|
+
let frameId = 0;
|
|
23392
|
+
let cancelled = false;
|
|
23393
|
+
const run = () => {
|
|
23394
|
+
if (cancelled) {
|
|
23395
|
+
return;
|
|
23396
|
+
}
|
|
23397
|
+
fitFrameIntoView(element);
|
|
23398
|
+
app.scene.mutateElement(element, {
|
|
23399
|
+
customData: {
|
|
23400
|
+
...element.customData ?? {},
|
|
23401
|
+
imageGeneratorAutoFitPending: false
|
|
23402
|
+
}
|
|
23403
|
+
});
|
|
23404
|
+
};
|
|
23405
|
+
frameId = window.requestAnimationFrame(() => {
|
|
23406
|
+
frameId = window.requestAnimationFrame(run);
|
|
23407
|
+
});
|
|
23408
|
+
return () => {
|
|
23409
|
+
cancelled = true;
|
|
23410
|
+
window.cancelAnimationFrame(frameId);
|
|
23411
|
+
};
|
|
23412
|
+
}, [app.scene, element, fitFrameIntoView]);
|
|
23220
23413
|
const applyRatio = useCallback15(
|
|
23221
23414
|
(ratioLabel) => {
|
|
23222
23415
|
setSelectedRatio(ratioLabel);
|
|
@@ -23224,7 +23417,15 @@ var ImageGeneratorPanel = ({
|
|
|
23224
23417
|
const dims = getDimensions(selectedModel, ratioLabel, selectedResolution);
|
|
23225
23418
|
appRef.current.scene.mutateElement(elementRef.current, {
|
|
23226
23419
|
width: dims.width,
|
|
23227
|
-
height: dims.height
|
|
23420
|
+
height: dims.height,
|
|
23421
|
+
customData: {
|
|
23422
|
+
...elementRef.current.customData ?? {},
|
|
23423
|
+
imageGenerator: {
|
|
23424
|
+
model: selectedModel,
|
|
23425
|
+
ratio: ratioLabel,
|
|
23426
|
+
resolution: selectedResolution
|
|
23427
|
+
}
|
|
23428
|
+
}
|
|
23228
23429
|
});
|
|
23229
23430
|
appRef.current.syncActionResult({
|
|
23230
23431
|
captureUpdate: CaptureUpdateAction36.IMMEDIATELY
|
|
@@ -23239,7 +23440,15 @@ var ImageGeneratorPanel = ({
|
|
|
23239
23440
|
const dims = getDimensions(selectedModel, selectedRatio, res);
|
|
23240
23441
|
appRef.current.scene.mutateElement(elementRef.current, {
|
|
23241
23442
|
width: dims.width,
|
|
23242
|
-
height: dims.height
|
|
23443
|
+
height: dims.height,
|
|
23444
|
+
customData: {
|
|
23445
|
+
...elementRef.current.customData ?? {},
|
|
23446
|
+
imageGenerator: {
|
|
23447
|
+
model: selectedModel,
|
|
23448
|
+
ratio: selectedRatio,
|
|
23449
|
+
resolution: res
|
|
23450
|
+
}
|
|
23451
|
+
}
|
|
23243
23452
|
});
|
|
23244
23453
|
appRef.current.syncActionResult({
|
|
23245
23454
|
captureUpdate: CaptureUpdateAction36.IMMEDIATELY
|
|
@@ -23301,6 +23510,20 @@ var ImageGeneratorPanel = ({
|
|
|
23301
23510
|
);
|
|
23302
23511
|
return;
|
|
23303
23512
|
}
|
|
23513
|
+
if (onBeforeImageGen) {
|
|
23514
|
+
try {
|
|
23515
|
+
const { allowed, error } = await onBeforeImageGen();
|
|
23516
|
+
if (!allowed) {
|
|
23517
|
+
setGenerateError(
|
|
23518
|
+
error || "Insufficient credits for image generation"
|
|
23519
|
+
);
|
|
23520
|
+
return;
|
|
23521
|
+
}
|
|
23522
|
+
} catch {
|
|
23523
|
+
setGenerateError("Credit check failed");
|
|
23524
|
+
return;
|
|
23525
|
+
}
|
|
23526
|
+
}
|
|
23304
23527
|
const controller = new AbortController();
|
|
23305
23528
|
abortControllerRef.current = controller;
|
|
23306
23529
|
startPendingGeneration(
|
|
@@ -23330,6 +23553,7 @@ var ImageGeneratorPanel = ({
|
|
|
23330
23553
|
controller.signal
|
|
23331
23554
|
);
|
|
23332
23555
|
await app.insertGeneratedImageIntoFrame(imageDataUrl, element);
|
|
23556
|
+
onAfterImageGen?.();
|
|
23333
23557
|
} catch (err) {
|
|
23334
23558
|
if (err instanceof Error) {
|
|
23335
23559
|
if (err.name === "AbortError") {
|
|
@@ -23359,12 +23583,14 @@ var ImageGeneratorPanel = ({
|
|
|
23359
23583
|
selectedResolution,
|
|
23360
23584
|
referenceImage,
|
|
23361
23585
|
app,
|
|
23362
|
-
element
|
|
23586
|
+
element,
|
|
23587
|
+
onBeforeImageGen,
|
|
23588
|
+
onAfterImageGen
|
|
23363
23589
|
]);
|
|
23364
23590
|
if (appState.contextMenu || appState.newElement || appState.resizingElement || appState.isRotating || appState.openMenu || appState.viewModeEnabled || appState.selectedElementsAreBeingDragged) {
|
|
23365
23591
|
return null;
|
|
23366
23592
|
}
|
|
23367
|
-
const [x1, , , y2] =
|
|
23593
|
+
const [x1, , , y2] = getElementAbsoluteCoords9(
|
|
23368
23594
|
element,
|
|
23369
23595
|
app.scene.getNonDeletedElementsMap()
|
|
23370
23596
|
);
|
|
@@ -23440,6 +23666,11 @@ var ImageGeneratorPanel = ({
|
|
|
23440
23666
|
{
|
|
23441
23667
|
className: "igp-dropdown-item",
|
|
23442
23668
|
onClick: () => {
|
|
23669
|
+
persistFrameSettings({
|
|
23670
|
+
model: m,
|
|
23671
|
+
ratio: selectedRatio,
|
|
23672
|
+
resolution: selectedResolution
|
|
23673
|
+
});
|
|
23443
23674
|
setSelectedModel(m);
|
|
23444
23675
|
setModelOpen(false);
|
|
23445
23676
|
},
|
|
@@ -23568,7 +23799,7 @@ var ImageGeneratorPanel = ({
|
|
|
23568
23799
|
// components/ImageQuickEditPanel.tsx
|
|
23569
23800
|
import { useCallback as useCallback16, useEffect as useEffect31, useRef as useRef27, useState as useState30 } from "react";
|
|
23570
23801
|
import { sceneCoordsToViewportCoords as sceneCoordsToViewportCoords6 } from "@orangecatai/common";
|
|
23571
|
-
import { getElementAbsoluteCoords as
|
|
23802
|
+
import { getElementAbsoluteCoords as getElementAbsoluteCoords10 } from "@orangecatai/element";
|
|
23572
23803
|
import { ArrowUp as ArrowUp2 } from "lucide-react";
|
|
23573
23804
|
import { Fragment as Fragment12, jsx as jsx88, jsxs as jsxs48 } from "react/jsx-runtime";
|
|
23574
23805
|
var ChevronDownIcon3 = () => /* @__PURE__ */ jsx88("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ jsx88(
|
|
@@ -23671,7 +23902,9 @@ var RatioShapeIcon2 = ({ label }) => {
|
|
|
23671
23902
|
};
|
|
23672
23903
|
var ImageQuickEditPanel = ({
|
|
23673
23904
|
element,
|
|
23674
|
-
app
|
|
23905
|
+
app,
|
|
23906
|
+
onBeforeImageGen,
|
|
23907
|
+
onAfterImageGen
|
|
23675
23908
|
}) => {
|
|
23676
23909
|
const appState = app.state;
|
|
23677
23910
|
const resolvedApiKey = resolveGeminiApiKey(app.props.geminiApiKey);
|
|
@@ -23770,6 +24003,20 @@ var ImageQuickEditPanel = ({
|
|
|
23770
24003
|
);
|
|
23771
24004
|
return;
|
|
23772
24005
|
}
|
|
24006
|
+
if (onBeforeImageGen) {
|
|
24007
|
+
try {
|
|
24008
|
+
const { allowed, error } = await onBeforeImageGen();
|
|
24009
|
+
if (!allowed) {
|
|
24010
|
+
setGenerateError(
|
|
24011
|
+
error || "Insufficient credits for image generation"
|
|
24012
|
+
);
|
|
24013
|
+
return;
|
|
24014
|
+
}
|
|
24015
|
+
} catch {
|
|
24016
|
+
setGenerateError("Credit check failed");
|
|
24017
|
+
return;
|
|
24018
|
+
}
|
|
24019
|
+
}
|
|
23773
24020
|
const controller = new AbortController();
|
|
23774
24021
|
abortControllerRef.current = controller;
|
|
23775
24022
|
startPendingGeneration(
|
|
@@ -23810,6 +24057,7 @@ var ImageQuickEditPanel = ({
|
|
|
23810
24057
|
dims.width,
|
|
23811
24058
|
dims.height
|
|
23812
24059
|
);
|
|
24060
|
+
onAfterImageGen?.();
|
|
23813
24061
|
} catch (err) {
|
|
23814
24062
|
if (err instanceof Error) {
|
|
23815
24063
|
if (err.name === "AbortError") {
|
|
@@ -23838,13 +24086,15 @@ var ImageQuickEditPanel = ({
|
|
|
23838
24086
|
selectedRatio,
|
|
23839
24087
|
selectedResolution,
|
|
23840
24088
|
app,
|
|
23841
|
-
element
|
|
24089
|
+
element,
|
|
24090
|
+
onBeforeImageGen,
|
|
24091
|
+
onAfterImageGen
|
|
23842
24092
|
]);
|
|
23843
24093
|
if (appState.contextMenu || appState.newElement || appState.resizingElement || appState.isRotating || appState.openMenu || appState.viewModeEnabled || appState.selectedElementsAreBeingDragged) {
|
|
23844
24094
|
return null;
|
|
23845
24095
|
}
|
|
23846
24096
|
const elementsMap = app.scene.getNonDeletedElementsMap();
|
|
23847
|
-
const [x1, y1, , y2] =
|
|
24097
|
+
const [x1, y1, , y2] = getElementAbsoluteCoords10(element, elementsMap);
|
|
23848
24098
|
const { x: triggerVpX, y: triggerVpY } = sceneCoordsToViewportCoords6(
|
|
23849
24099
|
{ sceneX: x1 + element.width / 2, sceneY: y1 },
|
|
23850
24100
|
appState
|
|
@@ -26767,24 +27017,40 @@ var Footer = ({
|
|
|
26767
27017
|
className: clsx47("layer-ui__wrapper__footer-left zen-mode-transition", {
|
|
26768
27018
|
"layer-ui__wrapper__footer-left--transition-left": appState.zenModeEnabled
|
|
26769
27019
|
}),
|
|
26770
|
-
children: /* @__PURE__ */ jsx104(Stack_default.Col, { gap: 2, children: /* @__PURE__ */
|
|
27020
|
+
children: /* @__PURE__ */ jsx104(Stack_default.Col, { gap: 2, children: /* @__PURE__ */ jsx104(Section, { heading: "canvasActions", children: /* @__PURE__ */ jsxs60(Stack_default.Col, { gap: 1, className: "footer-canvas-actions", children: [
|
|
26771
27021
|
/* @__PURE__ */ jsx104(
|
|
26772
|
-
|
|
27022
|
+
ScrollBackToContentAction,
|
|
26773
27023
|
{
|
|
26774
|
-
renderAction: actionManager.renderAction
|
|
26775
|
-
zoom: appState.zoom
|
|
27024
|
+
renderAction: actionManager.renderAction
|
|
26776
27025
|
}
|
|
26777
27026
|
),
|
|
26778
|
-
|
|
26779
|
-
|
|
27027
|
+
/* @__PURE__ */ jsxs60(
|
|
27028
|
+
Stack_default.Row,
|
|
26780
27029
|
{
|
|
26781
|
-
|
|
26782
|
-
|
|
26783
|
-
|
|
26784
|
-
|
|
27030
|
+
align: "center",
|
|
27031
|
+
gap: 1,
|
|
27032
|
+
className: "footer-canvas-actions-row",
|
|
27033
|
+
children: [
|
|
27034
|
+
/* @__PURE__ */ jsx104(
|
|
27035
|
+
ZoomActions,
|
|
27036
|
+
{
|
|
27037
|
+
renderAction: actionManager.renderAction,
|
|
27038
|
+
zoom: appState.zoom
|
|
27039
|
+
}
|
|
27040
|
+
),
|
|
27041
|
+
!appState.viewModeEnabled && /* @__PURE__ */ jsx104(
|
|
27042
|
+
UndoRedoActions,
|
|
27043
|
+
{
|
|
27044
|
+
renderAction: actionManager.renderAction,
|
|
27045
|
+
className: clsx47("zen-mode-transition", {
|
|
27046
|
+
"layer-ui__wrapper__footer-left--transition-bottom": appState.zenModeEnabled
|
|
27047
|
+
})
|
|
27048
|
+
}
|
|
27049
|
+
)
|
|
27050
|
+
]
|
|
26785
27051
|
}
|
|
26786
27052
|
)
|
|
26787
|
-
] }) })
|
|
27053
|
+
] }) }) })
|
|
26788
27054
|
}
|
|
26789
27055
|
),
|
|
26790
27056
|
/* @__PURE__ */ jsx104(FooterCenterTunnel.Out, {}),
|
|
@@ -35050,7 +35316,7 @@ import {
|
|
|
35050
35316
|
} from "@orangecatai/element";
|
|
35051
35317
|
import {
|
|
35052
35318
|
getCommonBounds as getCommonBounds9,
|
|
35053
|
-
getElementAbsoluteCoords as
|
|
35319
|
+
getElementAbsoluteCoords as getElementAbsoluteCoords11
|
|
35054
35320
|
} from "@orangecatai/element";
|
|
35055
35321
|
import {
|
|
35056
35322
|
getGlobalFixedPointForBindableElement as getGlobalFixedPointForBindableElement2,
|
|
@@ -35837,7 +36103,7 @@ var renderSelectionBorder = (context, appState, elementProperties) => {
|
|
|
35837
36103
|
context.restore();
|
|
35838
36104
|
};
|
|
35839
36105
|
var renderFrameHighlight = (context, appState, frame, elementsMap) => {
|
|
35840
|
-
const [x1, y1, x2, y2] =
|
|
36106
|
+
const [x1, y1, x2, y2] = getElementAbsoluteCoords11(frame, elementsMap);
|
|
35841
36107
|
const width = x2 - x1;
|
|
35842
36108
|
const height = y2 - y1;
|
|
35843
36109
|
context.strokeStyle = "rgb(0,118,255)";
|
|
@@ -36098,7 +36364,7 @@ var renderTransformHandles = (context, renderConfig, appState, transformHandles,
|
|
|
36098
36364
|
});
|
|
36099
36365
|
};
|
|
36100
36366
|
var renderCropHandles = (context, renderConfig, appState, croppingElement, elementsMap) => {
|
|
36101
|
-
const [x1, y1, , , cx, cy] =
|
|
36367
|
+
const [x1, y1, , , cx, cy] = getElementAbsoluteCoords11(
|
|
36102
36368
|
croppingElement,
|
|
36103
36369
|
elementsMap
|
|
36104
36370
|
);
|
|
@@ -36379,7 +36645,7 @@ var _renderInteractiveScene = ({
|
|
|
36379
36645
|
}
|
|
36380
36646
|
}
|
|
36381
36647
|
if (selectionColors.length) {
|
|
36382
|
-
const [x1, y1, x2, y2, cx, cy] =
|
|
36648
|
+
const [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords11(
|
|
36383
36649
|
element,
|
|
36384
36650
|
elementsMap,
|
|
36385
36651
|
true
|
|
@@ -36506,7 +36772,7 @@ var _renderInteractiveScene = ({
|
|
|
36506
36772
|
appState.searchMatches?.matches.forEach(({ id, focus, matchedLines }) => {
|
|
36507
36773
|
const element = elementsMap.get(id);
|
|
36508
36774
|
if (element) {
|
|
36509
|
-
const [elementX1, elementY1, , , cx, cy] =
|
|
36775
|
+
const [elementX1, elementY1, , , cx, cy] = getElementAbsoluteCoords11(
|
|
36510
36776
|
element,
|
|
36511
36777
|
elementsMap,
|
|
36512
36778
|
true
|
|
@@ -37275,11 +37541,11 @@ var App = class _App extends React52.Component {
|
|
|
37275
37541
|
);
|
|
37276
37542
|
if (frameNameDiv) {
|
|
37277
37543
|
const box = frameNameDiv.getBoundingClientRect();
|
|
37278
|
-
const boxSceneTopLeft =
|
|
37544
|
+
const boxSceneTopLeft = viewportCoordsToSceneCoords4(
|
|
37279
37545
|
{ clientX: box.x, clientY: box.y },
|
|
37280
37546
|
this.state
|
|
37281
37547
|
);
|
|
37282
|
-
const boxSceneBottomRight =
|
|
37548
|
+
const boxSceneBottomRight = viewportCoordsToSceneCoords4(
|
|
37283
37549
|
{ clientX: box.right, clientY: box.bottom },
|
|
37284
37550
|
this.state
|
|
37285
37551
|
);
|
|
@@ -37569,7 +37835,10 @@ var App = class _App extends React52.Component {
|
|
|
37569
37835
|
locked: false,
|
|
37570
37836
|
backgroundColor: "#cce8f5",
|
|
37571
37837
|
fillStyle: "solid",
|
|
37572
|
-
customData: {
|
|
37838
|
+
customData: {
|
|
37839
|
+
type: "image-generator",
|
|
37840
|
+
imageGeneratorAutoFitPending: true
|
|
37841
|
+
}
|
|
37573
37842
|
});
|
|
37574
37843
|
this.scene.insertElement(frame);
|
|
37575
37844
|
this.setActiveTool({ type: "selection" });
|
|
@@ -37614,7 +37883,7 @@ var App = class _App extends React52.Component {
|
|
|
37614
37883
|
const CLEARANCE = 25;
|
|
37615
37884
|
const elementsMap = this.scene.getNonDeletedElementsMap();
|
|
37616
37885
|
const allElements = this.scene.getNonDeletedElements().filter((el) => el.id !== sourceElement.id);
|
|
37617
|
-
const [sx1, sy1, sx2, sy2] =
|
|
37886
|
+
const [sx1, sy1, sx2, sy2] = getElementAbsoluteCoords12(
|
|
37618
37887
|
sourceElement,
|
|
37619
37888
|
elementsMap
|
|
37620
37889
|
);
|
|
@@ -37632,7 +37901,7 @@ var App = class _App extends React52.Component {
|
|
|
37632
37901
|
const right = cRight + CLEARANCE;
|
|
37633
37902
|
const bottom = cBottom + CLEARANCE;
|
|
37634
37903
|
return allElements.some((el) => {
|
|
37635
|
-
const [ex1, ey1, ex2, ey2] =
|
|
37904
|
+
const [ex1, ey1, ex2, ey2] = getElementAbsoluteCoords12(el, elementsMap);
|
|
37636
37905
|
return ex1 < right && ex2 > left && ey1 < bottom && ey2 > top;
|
|
37637
37906
|
});
|
|
37638
37907
|
};
|
|
@@ -38118,7 +38387,7 @@ var App = class _App extends React52.Component {
|
|
|
38118
38387
|
const elementsCenterY = distance2(minY, maxY) / 2;
|
|
38119
38388
|
const clientX = typeof opts.position === "object" ? opts.position.clientX : opts.position === "cursor" ? this.lastViewportPosition.x : this.state.width / 2 + this.state.offsetLeft;
|
|
38120
38389
|
const clientY = typeof opts.position === "object" ? opts.position.clientY : opts.position === "cursor" ? this.lastViewportPosition.y : this.state.height / 2 + this.state.offsetTop;
|
|
38121
|
-
const { x, y } =
|
|
38390
|
+
const { x, y } = viewportCoordsToSceneCoords4(
|
|
38122
38391
|
{ clientX, clientY },
|
|
38123
38392
|
this.state
|
|
38124
38393
|
);
|
|
@@ -38970,7 +39239,7 @@ var App = class _App extends React52.Component {
|
|
|
38970
39239
|
bindMode: "orbit"
|
|
38971
39240
|
});
|
|
38972
39241
|
if (this.lastPointerMoveEvent && getFeatureFlag4("COMPLEX_BINDINGS")) {
|
|
38973
|
-
const scenePointer =
|
|
39242
|
+
const scenePointer = viewportCoordsToSceneCoords4(
|
|
38974
39243
|
{
|
|
38975
39244
|
clientX: this.lastPointerMoveEvent.clientX,
|
|
38976
39245
|
clientY: this.lastPointerMoveEvent.clientY
|
|
@@ -39371,7 +39640,7 @@ var App = class _App extends React52.Component {
|
|
|
39371
39640
|
return;
|
|
39372
39641
|
}
|
|
39373
39642
|
const selectedElements = this.scene.getSelectedElements(this.state);
|
|
39374
|
-
let { x: sceneX, y: sceneY } =
|
|
39643
|
+
let { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords4(
|
|
39375
39644
|
event,
|
|
39376
39645
|
this.state
|
|
39377
39646
|
);
|
|
@@ -39536,7 +39805,7 @@ var App = class _App extends React52.Component {
|
|
|
39536
39805
|
if (!this.hitLinkElement || draggedDistance > DRAGGING_THRESHOLD3) {
|
|
39537
39806
|
return;
|
|
39538
39807
|
}
|
|
39539
|
-
const lastPointerDownCoords =
|
|
39808
|
+
const lastPointerDownCoords = viewportCoordsToSceneCoords4(
|
|
39540
39809
|
this.lastPointerDownEvent,
|
|
39541
39810
|
this.state
|
|
39542
39811
|
);
|
|
@@ -39548,7 +39817,7 @@ var App = class _App extends React52.Component {
|
|
|
39548
39817
|
pointFrom29(lastPointerDownCoords.x, lastPointerDownCoords.y),
|
|
39549
39818
|
this.editorInterface.formFactor === "phone"
|
|
39550
39819
|
);
|
|
39551
|
-
const lastPointerUpCoords =
|
|
39820
|
+
const lastPointerUpCoords = viewportCoordsToSceneCoords4(
|
|
39552
39821
|
this.lastPointerUpEvent,
|
|
39553
39822
|
this.state
|
|
39554
39823
|
);
|
|
@@ -39596,7 +39865,7 @@ var App = class _App extends React52.Component {
|
|
|
39596
39865
|
__publicField(this, "handleCanvasPointerMove", (event) => {
|
|
39597
39866
|
this.savePointer(event.clientX, event.clientY, this.state.cursorButton);
|
|
39598
39867
|
this.lastPointerMoveEvent = event.nativeEvent;
|
|
39599
|
-
const scenePointer =
|
|
39868
|
+
const scenePointer = viewportCoordsToSceneCoords4(event, this.state);
|
|
39600
39869
|
const { x: scenePointerX, y: scenePointerY } = scenePointer;
|
|
39601
39870
|
this.lastPointerMoveCoords = {
|
|
39602
39871
|
x: scenePointerX,
|
|
@@ -40072,7 +40341,7 @@ var App = class _App extends React52.Component {
|
|
|
40072
40341
|
invalidateContextMenu = true;
|
|
40073
40342
|
});
|
|
40074
40343
|
__publicField(this, "handleCanvasPointerDown", (event) => {
|
|
40075
|
-
const scenePointer =
|
|
40344
|
+
const scenePointer = viewportCoordsToSceneCoords4(event, this.state);
|
|
40076
40345
|
const { x: scenePointerX, y: scenePointerY } = scenePointer;
|
|
40077
40346
|
this.lastPointerMoveCoords = {
|
|
40078
40347
|
x: scenePointerX,
|
|
@@ -40364,7 +40633,7 @@ var App = class _App extends React52.Component {
|
|
|
40364
40633
|
}
|
|
40365
40634
|
this.removePointer(event);
|
|
40366
40635
|
this.lastPointerUpEvent = event;
|
|
40367
|
-
const scenePointer =
|
|
40636
|
+
const scenePointer = viewportCoordsToSceneCoords4(
|
|
40368
40637
|
{ clientX: event.clientX, clientY: event.clientY },
|
|
40369
40638
|
this.state
|
|
40370
40639
|
);
|
|
@@ -41444,7 +41713,7 @@ var App = class _App extends React52.Component {
|
|
|
41444
41713
|
try {
|
|
41445
41714
|
const clientX = this.state.width / 2 + this.state.offsetLeft;
|
|
41446
41715
|
const clientY = this.state.height / 2 + this.state.offsetTop;
|
|
41447
|
-
const { x, y } =
|
|
41716
|
+
const { x, y } = viewportCoordsToSceneCoords4(
|
|
41448
41717
|
{ clientX, clientY },
|
|
41449
41718
|
this.state
|
|
41450
41719
|
);
|
|
@@ -41619,7 +41888,7 @@ var App = class _App extends React52.Component {
|
|
|
41619
41888
|
});
|
|
41620
41889
|
});
|
|
41621
41890
|
__publicField(this, "handleAppOnDrop", async (event) => {
|
|
41622
|
-
const { x: sceneX, y: sceneY } =
|
|
41891
|
+
const { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords4(
|
|
41623
41892
|
event,
|
|
41624
41893
|
this.state
|
|
41625
41894
|
);
|
|
@@ -41786,7 +42055,7 @@ var App = class _App extends React52.Component {
|
|
|
41786
42055
|
event.button !== POINTER_BUTTON2.SECONDARY) && this.state.activeTool.type !== this.state.preferredSelectionTool.type) {
|
|
41787
42056
|
return;
|
|
41788
42057
|
}
|
|
41789
|
-
const { x, y } =
|
|
42058
|
+
const { x, y } = viewportCoordsToSceneCoords4(event, this.state);
|
|
41790
42059
|
const element = this.getElementAtPosition(x, y, {
|
|
41791
42060
|
preferSelected: true,
|
|
41792
42061
|
includeLockedElements: true
|
|
@@ -42206,7 +42475,7 @@ var App = class _App extends React52.Component {
|
|
|
42206
42475
|
if (!x || !y) {
|
|
42207
42476
|
return;
|
|
42208
42477
|
}
|
|
42209
|
-
const { x: sceneX, y: sceneY } =
|
|
42478
|
+
const { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords4(
|
|
42210
42479
|
{ clientX: x, clientY: y },
|
|
42211
42480
|
this.state
|
|
42212
42481
|
);
|
|
@@ -42631,7 +42900,7 @@ var App = class _App extends React52.Component {
|
|
|
42631
42900
|
return false;
|
|
42632
42901
|
}
|
|
42633
42902
|
const viewportClickStart_scenePoint = pointFrom29(
|
|
42634
|
-
|
|
42903
|
+
viewportCoordsToSceneCoords4(
|
|
42635
42904
|
{
|
|
42636
42905
|
clientX: this.lastPointerDownEvent.clientX,
|
|
42637
42906
|
clientY: this.lastPointerDownEvent.clientY
|
|
@@ -42640,7 +42909,7 @@ var App = class _App extends React52.Component {
|
|
|
42640
42909
|
)
|
|
42641
42910
|
);
|
|
42642
42911
|
const viewportClickEnd_scenePoint = pointFrom29(
|
|
42643
|
-
|
|
42912
|
+
viewportCoordsToSceneCoords4(
|
|
42644
42913
|
{
|
|
42645
42914
|
clientX: this.lastPointerUpEvent.clientX,
|
|
42646
42915
|
clientY: this.lastPointerUpEvent.clientY
|
|
@@ -43161,14 +43430,18 @@ var App = class _App extends React52.Component {
|
|
|
43161
43430
|
ImageGeneratorPanel,
|
|
43162
43431
|
{
|
|
43163
43432
|
element: firstSelectedElement,
|
|
43164
|
-
app: this
|
|
43433
|
+
app: this,
|
|
43434
|
+
onBeforeImageGen: this.props.onBeforeImageGen,
|
|
43435
|
+
onAfterImageGen: this.props.onAfterImageGen
|
|
43165
43436
|
}
|
|
43166
43437
|
),
|
|
43167
43438
|
selectedElements.length === 1 && isImageElement9(firstSelectedElement) && !this.state.viewModeEnabled && /* @__PURE__ */ jsx168(
|
|
43168
43439
|
ImageQuickEditPanel,
|
|
43169
43440
|
{
|
|
43170
43441
|
element: firstSelectedElement,
|
|
43171
|
-
app: this
|
|
43442
|
+
app: this,
|
|
43443
|
+
onBeforeImageGen: this.props.onBeforeImageGen,
|
|
43444
|
+
onAfterImageGen: this.props.onAfterImageGen
|
|
43172
43445
|
}
|
|
43173
43446
|
),
|
|
43174
43447
|
this.state.toast !== null && /* @__PURE__ */ jsx168(
|
|
@@ -43702,7 +43975,7 @@ var App = class _App extends React52.Component {
|
|
|
43702
43975
|
}
|
|
43703
43976
|
// TODO: Cover with tests
|
|
43704
43977
|
async insertClipboardContent(data, dataTransferFiles, isPlainPaste) {
|
|
43705
|
-
const { x: sceneX, y: sceneY } =
|
|
43978
|
+
const { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords4(
|
|
43706
43979
|
{
|
|
43707
43980
|
clientX: this.lastViewportPosition.x,
|
|
43708
43981
|
clientY: this.lastViewportPosition.y
|
|
@@ -43849,7 +44122,7 @@ var App = class _App extends React52.Component {
|
|
|
43849
44122
|
}
|
|
43850
44123
|
}
|
|
43851
44124
|
addTextFromPaste(text, isPlainPaste = false) {
|
|
43852
|
-
const { x, y } =
|
|
44125
|
+
const { x, y } = viewportCoordsToSceneCoords4(
|
|
43853
44126
|
{
|
|
43854
44127
|
clientX: this.lastViewportPosition.x,
|
|
43855
44128
|
clientY: this.lastViewportPosition.y
|
|
@@ -44153,7 +44426,7 @@ var App = class _App extends React52.Component {
|
|
|
44153
44426
|
if (elements[index].isDeleted) {
|
|
44154
44427
|
continue;
|
|
44155
44428
|
}
|
|
44156
|
-
const [x1, y1, x2, y2] =
|
|
44429
|
+
const [x1, y1, x2, y2] = getElementAbsoluteCoords12(
|
|
44157
44430
|
elements[index],
|
|
44158
44431
|
this.scene.getNonDeletedElementsMap()
|
|
44159
44432
|
);
|
|
@@ -44286,7 +44559,7 @@ var App = class _App extends React52.Component {
|
|
|
44286
44559
|
}
|
|
44287
44560
|
}
|
|
44288
44561
|
initialPointerDownState(event) {
|
|
44289
|
-
const origin =
|
|
44562
|
+
const origin = viewportCoordsToSceneCoords4(event, this.state);
|
|
44290
44563
|
const selectedElements = this.scene.getSelectedElements(this.state);
|
|
44291
44564
|
const [minX, minY, maxX, maxY] = getCommonBounds11(selectedElements);
|
|
44292
44565
|
const isElbowArrowOnly = selectedElements.findIndex(isElbowArrow10) === 0;
|
|
@@ -44450,7 +44723,7 @@ var App = class _App extends React52.Component {
|
|
|
44450
44723
|
if (this.state.openDialog?.name === "elementLinkSelector") {
|
|
44451
44724
|
return;
|
|
44452
44725
|
}
|
|
44453
|
-
const pointerCoords =
|
|
44726
|
+
const pointerCoords = viewportCoordsToSceneCoords4(event, this.state);
|
|
44454
44727
|
if (this.state.activeLockedId) {
|
|
44455
44728
|
this.setState({
|
|
44456
44729
|
activeLockedId: null
|
|
@@ -44699,7 +44972,7 @@ var App = class _App extends React52.Component {
|
|
|
44699
44972
|
);
|
|
44700
44973
|
instantDragOffset[0] *= image.naturalWidth / uncroppedSize.width;
|
|
44701
44974
|
instantDragOffset[1] *= image.naturalHeight / uncroppedSize.height;
|
|
44702
|
-
const [x1, y1, x2, y2, cx, cy] =
|
|
44975
|
+
const [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords12(
|
|
44703
44976
|
croppingElement,
|
|
44704
44977
|
elementsMap
|
|
44705
44978
|
);
|
|
@@ -44867,7 +45140,7 @@ var App = class _App extends React52.Component {
|
|
|
44867
45140
|
},
|
|
44868
45141
|
[]
|
|
44869
45142
|
);
|
|
44870
|
-
pointerDownState.drag.origin =
|
|
45143
|
+
pointerDownState.drag.origin = viewportCoordsToSceneCoords4(
|
|
44871
45144
|
event,
|
|
44872
45145
|
this.state
|
|
44873
45146
|
);
|
|
@@ -45127,7 +45400,7 @@ var App = class _App extends React52.Component {
|
|
|
45127
45400
|
SnapCache.setVisibleGaps(null);
|
|
45128
45401
|
this.savePointer(childEvent.clientX, childEvent.clientY, "up");
|
|
45129
45402
|
const hitElements = pointerDownState.hit.allHitElements;
|
|
45130
|
-
const sceneCoords =
|
|
45403
|
+
const sceneCoords = viewportCoordsToSceneCoords4(
|
|
45131
45404
|
{ clientX: childEvent.clientX, clientY: childEvent.clientY },
|
|
45132
45405
|
this.state
|
|
45133
45406
|
);
|
|
@@ -45265,7 +45538,7 @@ var App = class _App extends React52.Component {
|
|
|
45265
45538
|
childEvent
|
|
45266
45539
|
);
|
|
45267
45540
|
if (newElement6?.type === "freedraw") {
|
|
45268
|
-
const pointerCoords =
|
|
45541
|
+
const pointerCoords = viewportCoordsToSceneCoords4(
|
|
45269
45542
|
childEvent,
|
|
45270
45543
|
this.state
|
|
45271
45544
|
);
|
|
@@ -45288,7 +45561,7 @@ var App = class _App extends React52.Component {
|
|
|
45288
45561
|
if (newElement6.points.length > 1 && newElement6.points[1][0] !== 0 && newElement6.points[1][1] !== 0) {
|
|
45289
45562
|
this.store.scheduleCapture();
|
|
45290
45563
|
}
|
|
45291
|
-
const pointerCoords =
|
|
45564
|
+
const pointerCoords = viewportCoordsToSceneCoords4(
|
|
45292
45565
|
childEvent,
|
|
45293
45566
|
this.state
|
|
45294
45567
|
);
|
|
@@ -45420,7 +45693,7 @@ var App = class _App extends React52.Component {
|
|
|
45420
45693
|
this.scene.triggerUpdate();
|
|
45421
45694
|
}
|
|
45422
45695
|
if (pointerDownState.drag.hasOccurred) {
|
|
45423
|
-
const sceneCoords2 =
|
|
45696
|
+
const sceneCoords2 = viewportCoordsToSceneCoords4(childEvent, this.state);
|
|
45424
45697
|
if (this.state.selectedLinearElement && this.state.selectedLinearElement.isDragging) {
|
|
45425
45698
|
const linearElement = this.scene.getElement(
|
|
45426
45699
|
this.state.selectedLinearElement.elementId
|
|
@@ -45573,7 +45846,7 @@ var App = class _App extends React52.Component {
|
|
|
45573
45846
|
pointFrom29(pointerEnd.clientX, pointerEnd.clientY)
|
|
45574
45847
|
);
|
|
45575
45848
|
if (draggedDistance === 0) {
|
|
45576
|
-
const scenePointer =
|
|
45849
|
+
const scenePointer = viewportCoordsToSceneCoords4(
|
|
45577
45850
|
{
|
|
45578
45851
|
clientX: pointerEnd.clientX,
|
|
45579
45852
|
clientY: pointerEnd.clientY
|
|
@@ -46300,7 +46573,7 @@ import {
|
|
|
46300
46573
|
UserIdleState as UserIdleState2,
|
|
46301
46574
|
normalizeLink as normalizeLink4,
|
|
46302
46575
|
sceneCoordsToViewportCoords as sceneCoordsToViewportCoords2,
|
|
46303
|
-
viewportCoordsToSceneCoords as
|
|
46576
|
+
viewportCoordsToSceneCoords as viewportCoordsToSceneCoords5,
|
|
46304
46577
|
getFormFactor as getFormFactor2
|
|
46305
46578
|
} from "@orangecatai/common";
|
|
46306
46579
|
import {
|
|
@@ -46520,32 +46793,37 @@ import { isElementLink as isElementLink3 } from "@orangecatai/element";
|
|
|
46520
46793
|
import { setCustomTextMetricsProvider } from "@orangecatai/element";
|
|
46521
46794
|
|
|
46522
46795
|
// components/AIChatPanel.tsx
|
|
46523
|
-
import {
|
|
46796
|
+
import React57, {
|
|
46524
46797
|
useCallback as useCallback24,
|
|
46525
46798
|
useEffect as useEffect55,
|
|
46799
|
+
useImperativeHandle as useImperativeHandle4,
|
|
46526
46800
|
useRef as useRef49,
|
|
46527
46801
|
useState as useState49
|
|
46528
46802
|
} from "react";
|
|
46529
46803
|
import {
|
|
46530
46804
|
ArrowUp as ArrowUp3,
|
|
46531
46805
|
AtSign,
|
|
46806
|
+
Check,
|
|
46532
46807
|
ChevronDown as ChevronDown2,
|
|
46533
46808
|
ChevronRight,
|
|
46534
46809
|
Copy,
|
|
46535
46810
|
Globe,
|
|
46536
46811
|
MessageSquare,
|
|
46537
46812
|
Mic,
|
|
46538
|
-
|
|
46539
|
-
|
|
46813
|
+
MicOff,
|
|
46814
|
+
Paperclip,
|
|
46540
46815
|
Plus,
|
|
46541
46816
|
Search,
|
|
46542
|
-
ThumbsDown,
|
|
46543
|
-
ThumbsUp,
|
|
46544
|
-
Trash,
|
|
46545
46817
|
Wrench,
|
|
46546
46818
|
X as X2
|
|
46547
46819
|
} from "lucide-react";
|
|
46548
46820
|
|
|
46821
|
+
// utils/openRouterApiKey.ts
|
|
46822
|
+
function resolveOpenRouterApiKey(propKey) {
|
|
46823
|
+
const normalizedPropKey = propKey?.trim();
|
|
46824
|
+
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 : "") ?? "";
|
|
46825
|
+
}
|
|
46826
|
+
|
|
46549
46827
|
// components/ai-chat/canvasTools.ts
|
|
46550
46828
|
import { nanoid as nanoid2 } from "nanoid";
|
|
46551
46829
|
import {
|
|
@@ -46880,17 +47158,12 @@ async function captureFrameScreenshot(api, frameId) {
|
|
|
46880
47158
|
const files = api.getFiles();
|
|
46881
47159
|
const children = getFrameChildren7(elements, frameId);
|
|
46882
47160
|
try {
|
|
46883
|
-
const canvas = await exportToCanvas(
|
|
46884
|
-
|
|
46885
|
-
appState,
|
|
46886
|
-
|
|
46887
|
-
|
|
46888
|
-
|
|
46889
|
-
viewBackgroundColor: appState.viewBackgroundColor,
|
|
46890
|
-
exportingFrame: frame,
|
|
46891
|
-
exportPadding: 0
|
|
46892
|
-
}
|
|
46893
|
-
);
|
|
47161
|
+
const canvas = await exportToCanvas([...children, frame], appState, files, {
|
|
47162
|
+
exportBackground: true,
|
|
47163
|
+
viewBackgroundColor: appState.viewBackgroundColor,
|
|
47164
|
+
exportingFrame: frame,
|
|
47165
|
+
exportPadding: 0
|
|
47166
|
+
});
|
|
46894
47167
|
return canvas.toDataURL("image/jpeg", 0.7);
|
|
46895
47168
|
} catch {
|
|
46896
47169
|
return null;
|
|
@@ -46965,12 +47238,15 @@ function coversFrame(elX, elY, elW, elH, frameEl) {
|
|
|
46965
47238
|
return Math.abs(elX - frameEl.x) < COVER_TOLERANCE && Math.abs(elY - frameEl.y) < COVER_TOLERANCE && Math.abs(elW - frameEl.width) < COVER_TOLERANCE && Math.abs(elH - frameEl.height) < COVER_TOLERANCE;
|
|
46966
47239
|
}
|
|
46967
47240
|
function getElementTier(type, x, y, w, h, frame) {
|
|
46968
|
-
if (type === "text")
|
|
47241
|
+
if (type === "text") {
|
|
46969
47242
|
return 3 /* TEXT */;
|
|
46970
|
-
|
|
47243
|
+
}
|
|
47244
|
+
if (type === "image") {
|
|
46971
47245
|
return 1 /* IMAGE */;
|
|
46972
|
-
|
|
47246
|
+
}
|
|
47247
|
+
if (coversFrame(x, y, w, h, frame)) {
|
|
46973
47248
|
return 0 /* BG_FILL */;
|
|
47249
|
+
}
|
|
46974
47250
|
return 2 /* SHAPE */;
|
|
46975
47251
|
}
|
|
46976
47252
|
function findInsertIndexByTier(elements, frameIndex, tier) {
|
|
@@ -46979,9 +47255,17 @@ function findInsertIndexByTier(elements, frameIndex, tier) {
|
|
|
46979
47255
|
let insertAt = frameIndex + 1;
|
|
46980
47256
|
for (let i = frameIndex + 1; i < elements.length; i++) {
|
|
46981
47257
|
const el = elements[i];
|
|
46982
|
-
if (el.frameId !== frameId)
|
|
47258
|
+
if (el.frameId !== frameId) {
|
|
46983
47259
|
continue;
|
|
46984
|
-
|
|
47260
|
+
}
|
|
47261
|
+
const elTier = getElementTier(
|
|
47262
|
+
el.type,
|
|
47263
|
+
el.x,
|
|
47264
|
+
el.y,
|
|
47265
|
+
el.width,
|
|
47266
|
+
el.height,
|
|
47267
|
+
frame
|
|
47268
|
+
);
|
|
46985
47269
|
if (elTier > tier) {
|
|
46986
47270
|
return i;
|
|
46987
47271
|
}
|
|
@@ -47113,7 +47397,11 @@ function execAddText(args, ctx) {
|
|
|
47113
47397
|
}
|
|
47114
47398
|
const updatedText = { ...textEl, frameId };
|
|
47115
47399
|
const newElements = [...elements];
|
|
47116
|
-
const insertAt = findInsertIndexByTier(
|
|
47400
|
+
const insertAt = findInsertIndexByTier(
|
|
47401
|
+
newElements,
|
|
47402
|
+
frameIndex,
|
|
47403
|
+
3 /* TEXT */
|
|
47404
|
+
);
|
|
47117
47405
|
newElements.splice(insertAt, 0, updatedText);
|
|
47118
47406
|
ctx.excalidrawAPI.updateScene({ elements: newElements });
|
|
47119
47407
|
return {
|
|
@@ -47203,7 +47491,11 @@ async function execGenerateImage(args, ctx) {
|
|
|
47203
47491
|
}
|
|
47204
47492
|
const updatedImage = { ...imageEl, frameId };
|
|
47205
47493
|
const newElements = [...currentElements];
|
|
47206
|
-
const insertAt = findInsertIndexByTier(
|
|
47494
|
+
const insertAt = findInsertIndexByTier(
|
|
47495
|
+
newElements,
|
|
47496
|
+
currentFrameIndex,
|
|
47497
|
+
1 /* IMAGE */
|
|
47498
|
+
);
|
|
47207
47499
|
newElements.splice(insertAt, 0, updatedImage);
|
|
47208
47500
|
ctx.excalidrawAPI.updateScene({ elements: newElements });
|
|
47209
47501
|
ctx.excalidrawAPI.addFiles([
|
|
@@ -47394,7 +47686,33 @@ When asked to create an ad or visual from scratch:
|
|
|
47394
47686
|
- If the user's request is unclear, use the available tools to create a reasonable default and explain what you did.
|
|
47395
47687
|
- Coordinates: frame x,y is the top-left corner. Width extends right, height extends down.`;
|
|
47396
47688
|
var MAX_ITERATIONS = 15;
|
|
47397
|
-
async function callOpenRouter(messages, apiKey, signal) {
|
|
47689
|
+
async function callOpenRouter(messages, apiKey, signal, webSearchEnabled) {
|
|
47690
|
+
const model = webSearchEnabled ? "openai/gpt-4.1-mini:online" : "openai/gpt-4.1-mini";
|
|
47691
|
+
const body = {
|
|
47692
|
+
model,
|
|
47693
|
+
messages: messages.map((m) => {
|
|
47694
|
+
if (m.role === "assistant" && m.tool_calls) {
|
|
47695
|
+
return {
|
|
47696
|
+
role: m.role,
|
|
47697
|
+
content: m.content,
|
|
47698
|
+
tool_calls: m.tool_calls
|
|
47699
|
+
};
|
|
47700
|
+
}
|
|
47701
|
+
if (m.role === "tool") {
|
|
47702
|
+
return {
|
|
47703
|
+
role: m.role,
|
|
47704
|
+
tool_call_id: m.tool_call_id,
|
|
47705
|
+
content: m.content
|
|
47706
|
+
};
|
|
47707
|
+
}
|
|
47708
|
+
return { role: m.role, content: m.content };
|
|
47709
|
+
}),
|
|
47710
|
+
tools: CANVAS_TOOLS,
|
|
47711
|
+
tool_choice: "auto"
|
|
47712
|
+
};
|
|
47713
|
+
if (webSearchEnabled) {
|
|
47714
|
+
body.plugins = [{ id: "web", max_results: 5 }];
|
|
47715
|
+
}
|
|
47398
47716
|
const response = await fetch(
|
|
47399
47717
|
"https://openrouter.ai/api/v1/chat/completions",
|
|
47400
47718
|
{
|
|
@@ -47404,28 +47722,7 @@ async function callOpenRouter(messages, apiKey, signal) {
|
|
|
47404
47722
|
Authorization: `Bearer ${apiKey}`,
|
|
47405
47723
|
"Content-Type": "application/json"
|
|
47406
47724
|
},
|
|
47407
|
-
body: JSON.stringify(
|
|
47408
|
-
model: "openai/gpt-4.1-mini",
|
|
47409
|
-
messages: messages.map((m) => {
|
|
47410
|
-
if (m.role === "assistant" && m.tool_calls) {
|
|
47411
|
-
return {
|
|
47412
|
-
role: m.role,
|
|
47413
|
-
content: m.content,
|
|
47414
|
-
tool_calls: m.tool_calls
|
|
47415
|
-
};
|
|
47416
|
-
}
|
|
47417
|
-
if (m.role === "tool") {
|
|
47418
|
-
return {
|
|
47419
|
-
role: m.role,
|
|
47420
|
-
tool_call_id: m.tool_call_id,
|
|
47421
|
-
content: m.content
|
|
47422
|
-
};
|
|
47423
|
-
}
|
|
47424
|
-
return { role: m.role, content: m.content };
|
|
47425
|
-
}),
|
|
47426
|
-
tools: CANVAS_TOOLS,
|
|
47427
|
-
tool_choice: "auto"
|
|
47428
|
-
})
|
|
47725
|
+
body: JSON.stringify(body)
|
|
47429
47726
|
}
|
|
47430
47727
|
);
|
|
47431
47728
|
if (!response.ok) {
|
|
@@ -47454,10 +47751,14 @@ async function runAgentLoop(opts) {
|
|
|
47454
47751
|
userMessages,
|
|
47455
47752
|
elementContext,
|
|
47456
47753
|
frameScreenshot,
|
|
47754
|
+
fileAttachments,
|
|
47755
|
+
webSearchEnabled,
|
|
47457
47756
|
apiKey,
|
|
47458
47757
|
toolCtx,
|
|
47459
47758
|
onUpdate,
|
|
47460
|
-
signal
|
|
47759
|
+
signal,
|
|
47760
|
+
onBeforeImageGen,
|
|
47761
|
+
onAfterImageGen
|
|
47461
47762
|
} = opts;
|
|
47462
47763
|
const messages = [{ role: "system", content: SYSTEM_PROMPT }];
|
|
47463
47764
|
if (elementContext) {
|
|
@@ -47471,22 +47772,43 @@ ${elementContext}`
|
|
|
47471
47772
|
for (let i = 0; i < userMessages.length; i++) {
|
|
47472
47773
|
const msg = userMessages[i];
|
|
47473
47774
|
const isLast = i === userMessages.length - 1;
|
|
47474
|
-
|
|
47475
|
-
|
|
47476
|
-
|
|
47477
|
-
|
|
47478
|
-
|
|
47479
|
-
|
|
47480
|
-
|
|
47481
|
-
}
|
|
47482
|
-
|
|
47483
|
-
|
|
47484
|
-
|
|
47485
|
-
|
|
47486
|
-
|
|
47775
|
+
const hasMultimodal = isLast && msg.role === "user" && (frameScreenshot || fileAttachments && fileAttachments.some((a) => a.type === "image"));
|
|
47776
|
+
const hasTextFiles = isLast && msg.role === "user" && fileAttachments && fileAttachments.some((a) => a.type === "text");
|
|
47777
|
+
if (hasMultimodal || hasTextFiles) {
|
|
47778
|
+
const contentParts = [];
|
|
47779
|
+
if (frameScreenshot) {
|
|
47780
|
+
contentParts.push({
|
|
47781
|
+
type: "image_url",
|
|
47782
|
+
image_url: { url: frameScreenshot }
|
|
47783
|
+
});
|
|
47784
|
+
}
|
|
47785
|
+
if (fileAttachments) {
|
|
47786
|
+
for (const att of fileAttachments) {
|
|
47787
|
+
if (att.type === "image" && att.dataUrl) {
|
|
47788
|
+
contentParts.push({
|
|
47789
|
+
type: "image_url",
|
|
47790
|
+
image_url: { url: att.dataUrl }
|
|
47791
|
+
});
|
|
47487
47792
|
}
|
|
47488
|
-
|
|
47489
|
-
}
|
|
47793
|
+
}
|
|
47794
|
+
}
|
|
47795
|
+
let text = msg.content;
|
|
47796
|
+
if (frameScreenshot) {
|
|
47797
|
+
text = `[Screenshot of the referenced frame is attached above]
|
|
47798
|
+
|
|
47799
|
+
${text}`;
|
|
47800
|
+
}
|
|
47801
|
+
if (fileAttachments) {
|
|
47802
|
+
const textFiles = fileAttachments.filter((a) => a.type === "text");
|
|
47803
|
+
if (textFiles.length > 0) {
|
|
47804
|
+
text += `
|
|
47805
|
+
|
|
47806
|
+
${textFiles.map((f) => `[File: ${f.name}]
|
|
47807
|
+
${f.textContent}`).join("\n\n")}`;
|
|
47808
|
+
}
|
|
47809
|
+
}
|
|
47810
|
+
contentParts.push({ type: "text", text });
|
|
47811
|
+
messages.push({ role: "user", content: contentParts });
|
|
47490
47812
|
} else {
|
|
47491
47813
|
messages.push({ role: msg.role, content: msg.content });
|
|
47492
47814
|
}
|
|
@@ -47499,7 +47821,12 @@ ${msg.content}`
|
|
|
47499
47821
|
type: "status",
|
|
47500
47822
|
message: iterations === 1 ? "Thinking\u2026" : "Processing tool results\u2026"
|
|
47501
47823
|
});
|
|
47502
|
-
const response = await callOpenRouter(
|
|
47824
|
+
const response = await callOpenRouter(
|
|
47825
|
+
messages,
|
|
47826
|
+
apiKey,
|
|
47827
|
+
signal,
|
|
47828
|
+
webSearchEnabled
|
|
47829
|
+
);
|
|
47503
47830
|
if (!response.tool_calls || response.tool_calls.length === 0) {
|
|
47504
47831
|
const reply = response.content || "Done!";
|
|
47505
47832
|
onUpdate({ type: "final", message: reply });
|
|
@@ -47521,7 +47848,46 @@ ${msg.content}`
|
|
|
47521
47848
|
type: "tool_start",
|
|
47522
47849
|
message: formatToolStartMessage(name, parsedArgs)
|
|
47523
47850
|
});
|
|
47851
|
+
if (name === "generate_image" && onBeforeImageGen) {
|
|
47852
|
+
let gateResult;
|
|
47853
|
+
try {
|
|
47854
|
+
gateResult = await onBeforeImageGen();
|
|
47855
|
+
} catch {
|
|
47856
|
+
gateResult = { allowed: false, error: "Credit check failed" };
|
|
47857
|
+
}
|
|
47858
|
+
const { allowed, error } = gateResult;
|
|
47859
|
+
if (!allowed) {
|
|
47860
|
+
const gatedResult = {
|
|
47861
|
+
success: false,
|
|
47862
|
+
error: error || "Insufficient credits for image generation",
|
|
47863
|
+
statusMessage: error || "Insufficient credits for image generation"
|
|
47864
|
+
};
|
|
47865
|
+
const gatedAction = {
|
|
47866
|
+
toolName: name,
|
|
47867
|
+
args: parsedArgs,
|
|
47868
|
+
result: gatedResult
|
|
47869
|
+
};
|
|
47870
|
+
toolActions.push(gatedAction);
|
|
47871
|
+
onUpdate({
|
|
47872
|
+
type: "tool_done",
|
|
47873
|
+
message: gatedResult.statusMessage,
|
|
47874
|
+
toolAction: gatedAction
|
|
47875
|
+
});
|
|
47876
|
+
messages.push({
|
|
47877
|
+
role: "tool",
|
|
47878
|
+
tool_call_id: toolCall.id,
|
|
47879
|
+
content: JSON.stringify({
|
|
47880
|
+
success: false,
|
|
47881
|
+
error: gatedResult.error
|
|
47882
|
+
})
|
|
47883
|
+
});
|
|
47884
|
+
continue;
|
|
47885
|
+
}
|
|
47886
|
+
}
|
|
47524
47887
|
const result = await executeCanvasTool(name, rawArgs, toolCtx);
|
|
47888
|
+
if (name === "generate_image" && result.success && onAfterImageGen) {
|
|
47889
|
+
onAfterImageGen();
|
|
47890
|
+
}
|
|
47525
47891
|
const action = {
|
|
47526
47892
|
toolName: name,
|
|
47527
47893
|
args: parsedArgs,
|
|
@@ -47567,6 +47933,100 @@ function formatToolStartMessage(name, args) {
|
|
|
47567
47933
|
}
|
|
47568
47934
|
}
|
|
47569
47935
|
|
|
47936
|
+
// components/ai-chat/audioUtils.ts
|
|
47937
|
+
async function blobToWavBase64(blob) {
|
|
47938
|
+
const arrayBuffer = await blob.arrayBuffer();
|
|
47939
|
+
const audioCtx = new AudioContext();
|
|
47940
|
+
const audioBuffer = await audioCtx.decodeAudioData(arrayBuffer);
|
|
47941
|
+
await audioCtx.close();
|
|
47942
|
+
const numChannels = audioBuffer.numberOfChannels;
|
|
47943
|
+
const sampleRate = audioBuffer.sampleRate;
|
|
47944
|
+
const numSamples = audioBuffer.length;
|
|
47945
|
+
const bytesPerSample = 2;
|
|
47946
|
+
const dataSize = numSamples * numChannels * bytesPerSample;
|
|
47947
|
+
const bufferSize = 44 + dataSize;
|
|
47948
|
+
const wavBuffer = new ArrayBuffer(bufferSize);
|
|
47949
|
+
const view = new DataView(wavBuffer);
|
|
47950
|
+
const writeStr = (offset2, str) => {
|
|
47951
|
+
for (let i = 0; i < str.length; i++) {
|
|
47952
|
+
view.setUint8(offset2 + i, str.charCodeAt(i));
|
|
47953
|
+
}
|
|
47954
|
+
};
|
|
47955
|
+
writeStr(0, "RIFF");
|
|
47956
|
+
view.setUint32(4, 36 + dataSize, true);
|
|
47957
|
+
writeStr(8, "WAVE");
|
|
47958
|
+
writeStr(12, "fmt ");
|
|
47959
|
+
view.setUint32(16, 16, true);
|
|
47960
|
+
view.setUint16(20, 1, true);
|
|
47961
|
+
view.setUint16(22, numChannels, true);
|
|
47962
|
+
view.setUint32(24, sampleRate, true);
|
|
47963
|
+
view.setUint32(28, sampleRate * numChannels * bytesPerSample, true);
|
|
47964
|
+
view.setUint16(32, numChannels * bytesPerSample, true);
|
|
47965
|
+
view.setUint16(34, 16, true);
|
|
47966
|
+
writeStr(36, "data");
|
|
47967
|
+
view.setUint32(40, dataSize, true);
|
|
47968
|
+
let offset = 44;
|
|
47969
|
+
for (let i = 0; i < numSamples; i++) {
|
|
47970
|
+
for (let ch = 0; ch < numChannels; ch++) {
|
|
47971
|
+
const sample = audioBuffer.getChannelData(ch)[i];
|
|
47972
|
+
const clamped = Math.max(-1, Math.min(1, sample));
|
|
47973
|
+
const int16 = clamped < 0 ? clamped * 32768 : clamped * 32767;
|
|
47974
|
+
view.setInt16(offset, int16, true);
|
|
47975
|
+
offset += 2;
|
|
47976
|
+
}
|
|
47977
|
+
}
|
|
47978
|
+
const uint8 = new Uint8Array(wavBuffer);
|
|
47979
|
+
const CHUNK = 8192;
|
|
47980
|
+
let binary = "";
|
|
47981
|
+
for (let i = 0; i < uint8.length; i += CHUNK) {
|
|
47982
|
+
binary += String.fromCharCode(...uint8.subarray(i, i + CHUNK));
|
|
47983
|
+
}
|
|
47984
|
+
return btoa(binary);
|
|
47985
|
+
}
|
|
47986
|
+
async function transcribeAudio(base64Audio, format, apiKey) {
|
|
47987
|
+
const response = await fetch(
|
|
47988
|
+
"https://openrouter.ai/api/v1/chat/completions",
|
|
47989
|
+
{
|
|
47990
|
+
method: "POST",
|
|
47991
|
+
headers: {
|
|
47992
|
+
Authorization: `Bearer ${apiKey}`,
|
|
47993
|
+
"Content-Type": "application/json"
|
|
47994
|
+
},
|
|
47995
|
+
body: JSON.stringify({
|
|
47996
|
+
model: "mistralai/voxtral-small-24b-2507",
|
|
47997
|
+
messages: [
|
|
47998
|
+
{
|
|
47999
|
+
role: "user",
|
|
48000
|
+
content: [
|
|
48001
|
+
{
|
|
48002
|
+
type: "text",
|
|
48003
|
+
text: "Transcribe this audio. Return only the transcription text, nothing else."
|
|
48004
|
+
},
|
|
48005
|
+
{
|
|
48006
|
+
type: "input_audio",
|
|
48007
|
+
input_audio: { data: base64Audio, format }
|
|
48008
|
+
}
|
|
48009
|
+
]
|
|
48010
|
+
}
|
|
48011
|
+
]
|
|
48012
|
+
})
|
|
48013
|
+
}
|
|
48014
|
+
);
|
|
48015
|
+
if (!response.ok) {
|
|
48016
|
+
let message = `Transcription error ${response.status}`;
|
|
48017
|
+
try {
|
|
48018
|
+
const err = await response.json();
|
|
48019
|
+
if (err?.error?.message) {
|
|
48020
|
+
message = err.error.message;
|
|
48021
|
+
}
|
|
48022
|
+
} catch {
|
|
48023
|
+
}
|
|
48024
|
+
throw new Error(message);
|
|
48025
|
+
}
|
|
48026
|
+
const data = await response.json();
|
|
48027
|
+
return data?.choices?.[0]?.message?.content || "";
|
|
48028
|
+
}
|
|
48029
|
+
|
|
47570
48030
|
// components/ui/chat-container.tsx
|
|
47571
48031
|
import { StickToBottom } from "use-stick-to-bottom";
|
|
47572
48032
|
import { jsx as jsx177 } from "react/jsx-runtime";
|
|
@@ -47897,479 +48357,873 @@ import { jsx as jsx184, jsxs as jsxs100 } from "react/jsx-runtime";
|
|
|
47897
48357
|
function genId() {
|
|
47898
48358
|
return Math.random().toString(36).slice(2, 10);
|
|
47899
48359
|
}
|
|
48360
|
+
var MAX_ATTACHED_FILES = 3;
|
|
48361
|
+
var MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
|
|
47900
48362
|
var SUGGESTIONS = [
|
|
47901
48363
|
"Create a pasta ad",
|
|
47902
48364
|
"Design a sale banner",
|
|
47903
48365
|
"Make a product showcase",
|
|
47904
48366
|
"Help me design"
|
|
47905
48367
|
];
|
|
47906
|
-
var AIChatPanel = (
|
|
47907
|
-
|
|
47908
|
-
|
|
47909
|
-
|
|
47910
|
-
|
|
47911
|
-
|
|
47912
|
-
|
|
47913
|
-
|
|
47914
|
-
|
|
47915
|
-
|
|
47916
|
-
|
|
47917
|
-
|
|
47918
|
-
|
|
47919
|
-
|
|
47920
|
-
|
|
47921
|
-
|
|
47922
|
-
|
|
47923
|
-
|
|
47924
|
-
|
|
47925
|
-
|
|
47926
|
-
|
|
47927
|
-
|
|
47928
|
-
|
|
47929
|
-
|
|
47930
|
-
|
|
47931
|
-
|
|
47932
|
-
|
|
47933
|
-
|
|
47934
|
-
|
|
47935
|
-
|
|
47936
|
-
|
|
47937
|
-
|
|
47938
|
-
|
|
47939
|
-
|
|
47940
|
-
|
|
47941
|
-
|
|
47942
|
-
|
|
47943
|
-
|
|
47944
|
-
|
|
47945
|
-
|
|
47946
|
-
|
|
47947
|
-
|
|
47948
|
-
|
|
47949
|
-
|
|
47950
|
-
const
|
|
47951
|
-
|
|
47952
|
-
|
|
47953
|
-
|
|
47954
|
-
|
|
47955
|
-
|
|
47956
|
-
|
|
47957
|
-
|
|
48368
|
+
var AIChatPanel = React57.forwardRef(
|
|
48369
|
+
({
|
|
48370
|
+
isOpen,
|
|
48371
|
+
onClose,
|
|
48372
|
+
apiKey,
|
|
48373
|
+
excalidrawAPI,
|
|
48374
|
+
geminiApiKey,
|
|
48375
|
+
initialMessages,
|
|
48376
|
+
initialSessionId,
|
|
48377
|
+
initialSessions,
|
|
48378
|
+
onMessagesChange,
|
|
48379
|
+
onSessionCreate,
|
|
48380
|
+
onSessionSwitch,
|
|
48381
|
+
onBeforeSend,
|
|
48382
|
+
onAfterSend,
|
|
48383
|
+
onBeforeImageGen,
|
|
48384
|
+
onAfterImageGen
|
|
48385
|
+
}, ref) => {
|
|
48386
|
+
const [prompt, setPrompt] = useState49("");
|
|
48387
|
+
const [isLoading, setIsLoading] = useState49(false);
|
|
48388
|
+
const [statusText, setStatusText] = useState49("");
|
|
48389
|
+
const [messages, setMessages] = useState49(
|
|
48390
|
+
initialMessages ?? []
|
|
48391
|
+
);
|
|
48392
|
+
const [sessions, setSessions] = useState49(
|
|
48393
|
+
initialSessions ?? []
|
|
48394
|
+
);
|
|
48395
|
+
const [currentSessionId, setCurrentSessionId] = useState49(
|
|
48396
|
+
initialSessionId ?? genId
|
|
48397
|
+
);
|
|
48398
|
+
const [historyOpen, setHistoryOpen] = useState49(false);
|
|
48399
|
+
const [historySearch, setHistorySearch] = useState49("");
|
|
48400
|
+
const [error, setError] = useState49(null);
|
|
48401
|
+
const [frameRef, setFrameRef] = useState49(null);
|
|
48402
|
+
const [mentionMenuOpen, setMentionMenuOpen] = useState49(false);
|
|
48403
|
+
const [availableFrames, setAvailableFrames] = useState49([]);
|
|
48404
|
+
const [attachedFiles, setAttachedFiles] = useState49([]);
|
|
48405
|
+
const [webSearchEnabled, setWebSearchEnabled] = useState49(false);
|
|
48406
|
+
const [isSendPending, setIsSendPending] = useState49(false);
|
|
48407
|
+
const [isLoadingSession, setIsLoadingSession] = useState49(false);
|
|
48408
|
+
const [isRecording, setIsRecording] = useState49(false);
|
|
48409
|
+
const [isTranscribing, setIsTranscribing] = useState49(false);
|
|
48410
|
+
const [copiedMsgId, setCopiedMsgId] = useState49(null);
|
|
48411
|
+
const historyRef = useRef49(null);
|
|
48412
|
+
const abortControllerRef = useRef49(null);
|
|
48413
|
+
const mentionMenuRef = useRef49(null);
|
|
48414
|
+
const fileInputRef = useRef49(null);
|
|
48415
|
+
const mediaRecorderRef = useRef49(null);
|
|
48416
|
+
const audioChunksRef = useRef49([]);
|
|
48417
|
+
const prevPromptRef = useRef49("");
|
|
48418
|
+
const voiceUsedRef = useRef49(false);
|
|
48419
|
+
const currentSessionIdRef = useRef49(currentSessionId);
|
|
48420
|
+
useEffect55(() => {
|
|
48421
|
+
currentSessionIdRef.current = currentSessionId;
|
|
48422
|
+
}, [currentSessionId]);
|
|
48423
|
+
useEffect55(() => {
|
|
48424
|
+
const handler = (e) => {
|
|
48425
|
+
if (historyRef.current && !historyRef.current.contains(e.target)) {
|
|
48426
|
+
setHistoryOpen(false);
|
|
48427
|
+
}
|
|
48428
|
+
if (mentionMenuRef.current && !mentionMenuRef.current.contains(e.target)) {
|
|
48429
|
+
setMentionMenuOpen(false);
|
|
48430
|
+
}
|
|
48431
|
+
};
|
|
48432
|
+
document.addEventListener("mousedown", handler);
|
|
48433
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
48434
|
+
}, []);
|
|
48435
|
+
useEffect55(() => {
|
|
48436
|
+
if (!isOpen) {
|
|
48437
|
+
abortControllerRef.current?.abort();
|
|
48438
|
+
}
|
|
48439
|
+
}, [isOpen]);
|
|
48440
|
+
const currentTitle = (() => {
|
|
48441
|
+
const sessionTitle = sessions.find(
|
|
48442
|
+
(s) => s.id === currentSessionId
|
|
48443
|
+
)?.title;
|
|
48444
|
+
if (sessionTitle) {
|
|
48445
|
+
return sessionTitle;
|
|
48446
|
+
}
|
|
48447
|
+
if (messages.length > 0) {
|
|
48448
|
+
return messages[0].content.slice(0, 30) + (messages[0].content.length > 30 ? "\u2026" : "");
|
|
48449
|
+
}
|
|
48450
|
+
return "New chat";
|
|
48451
|
+
})();
|
|
48452
|
+
const saveCurrentSession = useCallback24(() => {
|
|
48453
|
+
if (messages.length === 0) {
|
|
48454
|
+
return;
|
|
47958
48455
|
}
|
|
47959
|
-
|
|
47960
|
-
|
|
47961
|
-
|
|
47962
|
-
|
|
47963
|
-
|
|
47964
|
-
|
|
47965
|
-
|
|
47966
|
-
|
|
47967
|
-
|
|
47968
|
-
|
|
47969
|
-
|
|
47970
|
-
|
|
47971
|
-
|
|
47972
|
-
const handleSwitchSession = useCallback24(
|
|
47973
|
-
(session) => {
|
|
48456
|
+
const title = messages[0].content.slice(0, 40) + (messages[0].content.length > 40 ? "\u2026" : "");
|
|
48457
|
+
setSessions((prev) => {
|
|
48458
|
+
const existing = prev.findIndex((s) => s.id === currentSessionId);
|
|
48459
|
+
const updated = { id: currentSessionId, title, messages };
|
|
48460
|
+
if (existing >= 0) {
|
|
48461
|
+
const next = [...prev];
|
|
48462
|
+
next[existing] = updated;
|
|
48463
|
+
return next;
|
|
48464
|
+
}
|
|
48465
|
+
return [updated, ...prev];
|
|
48466
|
+
});
|
|
48467
|
+
}, [messages, currentSessionId]);
|
|
48468
|
+
const handleNewChat = useCallback24(() => {
|
|
47974
48469
|
saveCurrentSession();
|
|
47975
|
-
|
|
47976
|
-
|
|
48470
|
+
const newSessionId = genId();
|
|
48471
|
+
setMessages([]);
|
|
48472
|
+
setCurrentSessionId(newSessionId);
|
|
48473
|
+
setIsLoadingSession(false);
|
|
47977
48474
|
setHistoryOpen(false);
|
|
47978
48475
|
setError(null);
|
|
47979
|
-
|
|
47980
|
-
|
|
47981
|
-
|
|
47982
|
-
|
|
47983
|
-
|
|
47984
|
-
|
|
47985
|
-
|
|
47986
|
-
|
|
47987
|
-
|
|
47988
|
-
|
|
47989
|
-
|
|
47990
|
-
|
|
47991
|
-
|
|
48476
|
+
setPrompt("");
|
|
48477
|
+
setFrameRef(null);
|
|
48478
|
+
setAttachedFiles([]);
|
|
48479
|
+
setStatusText("");
|
|
48480
|
+
}, [saveCurrentSession]);
|
|
48481
|
+
const handleSwitchSession = useCallback24(
|
|
48482
|
+
(session) => {
|
|
48483
|
+
saveCurrentSession();
|
|
48484
|
+
setCurrentSessionId(session.id);
|
|
48485
|
+
setHistoryOpen(false);
|
|
48486
|
+
setError(null);
|
|
48487
|
+
if (session.messages.length > 0) {
|
|
48488
|
+
setMessages(session.messages);
|
|
48489
|
+
setIsLoadingSession(false);
|
|
48490
|
+
} else {
|
|
48491
|
+
setMessages([]);
|
|
48492
|
+
setIsLoadingSession(true);
|
|
48493
|
+
if (onSessionSwitch) {
|
|
48494
|
+
onSessionSwitch(session.id);
|
|
48495
|
+
}
|
|
48496
|
+
}
|
|
48497
|
+
},
|
|
48498
|
+
[saveCurrentSession, onSessionSwitch]
|
|
48499
|
+
);
|
|
48500
|
+
const handlePromptChange = useCallback24(
|
|
48501
|
+
(value) => {
|
|
48502
|
+
const prevValue = prevPromptRef.current;
|
|
48503
|
+
setPrompt(value);
|
|
48504
|
+
prevPromptRef.current = value;
|
|
48505
|
+
if (excalidrawAPI && value.length > prevValue.length && value.endsWith("@")) {
|
|
48506
|
+
setAvailableFrames(listFrames(excalidrawAPI));
|
|
48507
|
+
setMentionMenuOpen(true);
|
|
48508
|
+
}
|
|
48509
|
+
},
|
|
48510
|
+
[excalidrawAPI]
|
|
48511
|
+
);
|
|
48512
|
+
const handlePickFrame = useCallback24(
|
|
48513
|
+
async (frame) => {
|
|
48514
|
+
if (!excalidrawAPI) {
|
|
48515
|
+
return;
|
|
48516
|
+
}
|
|
48517
|
+
setMentionMenuOpen(false);
|
|
48518
|
+
setPrompt((prev) => {
|
|
48519
|
+
const atIndex2 = prev.lastIndexOf("@");
|
|
48520
|
+
return atIndex2 >= 0 ? prev.slice(0, atIndex2) : prev;
|
|
48521
|
+
});
|
|
48522
|
+
const atIndex = prevPromptRef.current.lastIndexOf("@");
|
|
48523
|
+
prevPromptRef.current = atIndex >= 0 ? prevPromptRef.current.slice(0, atIndex) : prevPromptRef.current;
|
|
48524
|
+
const ctx = getFrameContext(excalidrawAPI, frame.id);
|
|
48525
|
+
if (!ctx) {
|
|
48526
|
+
return;
|
|
48527
|
+
}
|
|
48528
|
+
const screenshot = await captureFrameScreenshot(
|
|
48529
|
+
excalidrawAPI,
|
|
48530
|
+
frame.id
|
|
48531
|
+
);
|
|
48532
|
+
setFrameRef({
|
|
48533
|
+
frameId: frame.id,
|
|
48534
|
+
label: ctx.frameInfo.name,
|
|
48535
|
+
serialized: ctx.serialized,
|
|
48536
|
+
screenshot: screenshot ?? void 0
|
|
48537
|
+
});
|
|
48538
|
+
},
|
|
48539
|
+
[excalidrawAPI]
|
|
48540
|
+
);
|
|
48541
|
+
const handleRemoveRef = useCallback24(() => {
|
|
48542
|
+
setFrameRef(null);
|
|
48543
|
+
}, []);
|
|
48544
|
+
const handleFileUpload = useCallback24(
|
|
48545
|
+
(e) => {
|
|
48546
|
+
const files = e.target.files;
|
|
48547
|
+
if (!files) {
|
|
48548
|
+
return;
|
|
48549
|
+
}
|
|
48550
|
+
const remaining = MAX_ATTACHED_FILES - attachedFiles.length;
|
|
48551
|
+
if (remaining <= 0) {
|
|
48552
|
+
return;
|
|
48553
|
+
}
|
|
48554
|
+
const toAdd = Array.from(files).slice(0, remaining);
|
|
48555
|
+
for (const file2 of toAdd) {
|
|
48556
|
+
if (file2.size > MAX_FILE_SIZE_BYTES) {
|
|
48557
|
+
setError(`"${file2.name}" is too large (max 10 MB).`);
|
|
48558
|
+
continue;
|
|
48559
|
+
}
|
|
48560
|
+
const id = genId();
|
|
48561
|
+
const isImage = file2.type.startsWith("image/");
|
|
48562
|
+
const reader = new FileReader();
|
|
48563
|
+
if (isImage) {
|
|
48564
|
+
reader.onload = () => {
|
|
48565
|
+
setAttachedFiles(
|
|
48566
|
+
(prev) => prev.length < MAX_ATTACHED_FILES ? [
|
|
48567
|
+
...prev,
|
|
48568
|
+
{
|
|
48569
|
+
id,
|
|
48570
|
+
file: file2,
|
|
48571
|
+
name: file2.name,
|
|
48572
|
+
type: "image",
|
|
48573
|
+
dataUrl: reader.result
|
|
48574
|
+
}
|
|
48575
|
+
] : prev
|
|
48576
|
+
);
|
|
48577
|
+
};
|
|
48578
|
+
reader.readAsDataURL(file2);
|
|
48579
|
+
} else {
|
|
48580
|
+
reader.onload = () => {
|
|
48581
|
+
setAttachedFiles(
|
|
48582
|
+
(prev) => prev.length < MAX_ATTACHED_FILES ? [
|
|
48583
|
+
...prev,
|
|
48584
|
+
{
|
|
48585
|
+
id,
|
|
48586
|
+
file: file2,
|
|
48587
|
+
name: file2.name,
|
|
48588
|
+
type: "text",
|
|
48589
|
+
textContent: reader.result
|
|
48590
|
+
}
|
|
48591
|
+
] : prev
|
|
48592
|
+
);
|
|
48593
|
+
};
|
|
48594
|
+
reader.readAsText(file2);
|
|
48595
|
+
}
|
|
48596
|
+
}
|
|
48597
|
+
if (fileInputRef.current) {
|
|
48598
|
+
fileInputRef.current.value = "";
|
|
48599
|
+
}
|
|
48600
|
+
},
|
|
48601
|
+
[attachedFiles.length]
|
|
48602
|
+
);
|
|
48603
|
+
const handleRemoveFile = useCallback24((fileId) => {
|
|
48604
|
+
setAttachedFiles((prev) => prev.filter((f) => f.id !== fileId));
|
|
48605
|
+
}, []);
|
|
48606
|
+
const toggleWebSearch = useCallback24(() => {
|
|
48607
|
+
setWebSearchEnabled((prev) => !prev);
|
|
48608
|
+
}, []);
|
|
48609
|
+
const handleVoiceInput = useCallback24(async () => {
|
|
48610
|
+
if (isRecording) {
|
|
48611
|
+
mediaRecorderRef.current?.stop();
|
|
48612
|
+
setIsRecording(false);
|
|
48613
|
+
voiceUsedRef.current = true;
|
|
47992
48614
|
return;
|
|
47993
48615
|
}
|
|
47994
|
-
|
|
47995
|
-
|
|
47996
|
-
|
|
47997
|
-
|
|
48616
|
+
let stream = null;
|
|
48617
|
+
try {
|
|
48618
|
+
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
48619
|
+
const mimeType = MediaRecorder.isTypeSupported("audio/webm") ? "audio/webm" : "audio/mp4";
|
|
48620
|
+
const mediaRecorder = new MediaRecorder(stream, { mimeType });
|
|
48621
|
+
mediaRecorderRef.current = mediaRecorder;
|
|
48622
|
+
audioChunksRef.current = [];
|
|
48623
|
+
mediaRecorder.ondataavailable = (e) => {
|
|
48624
|
+
if (e.data.size > 0) {
|
|
48625
|
+
audioChunksRef.current.push(e.data);
|
|
48626
|
+
}
|
|
48627
|
+
};
|
|
48628
|
+
mediaRecorder.onstop = async () => {
|
|
48629
|
+
stream?.getTracks().forEach((t2) => t2.stop());
|
|
48630
|
+
const audioBlob = new Blob(audioChunksRef.current, {
|
|
48631
|
+
type: mediaRecorder.mimeType
|
|
48632
|
+
});
|
|
48633
|
+
const normalizedKey = resolveOpenRouterApiKey(apiKey);
|
|
48634
|
+
if (!normalizedKey) {
|
|
48635
|
+
setError("No API key for voice transcription.");
|
|
48636
|
+
return;
|
|
48637
|
+
}
|
|
48638
|
+
setIsTranscribing(true);
|
|
48639
|
+
setStatusText("Transcribing\u2026");
|
|
48640
|
+
try {
|
|
48641
|
+
const wavBase64 = await blobToWavBase64(audioBlob);
|
|
48642
|
+
const transcription = await transcribeAudio(
|
|
48643
|
+
wavBase64,
|
|
48644
|
+
"wav",
|
|
48645
|
+
normalizedKey
|
|
48646
|
+
);
|
|
48647
|
+
const updated = (prevPromptRef.current ? `${prevPromptRef.current} ` : "") + transcription;
|
|
48648
|
+
prevPromptRef.current = updated;
|
|
48649
|
+
setPrompt(updated);
|
|
48650
|
+
} catch (err) {
|
|
48651
|
+
voiceUsedRef.current = false;
|
|
48652
|
+
setError(
|
|
48653
|
+
err instanceof Error ? err.message : "Transcription failed"
|
|
48654
|
+
);
|
|
48655
|
+
} finally {
|
|
48656
|
+
setIsTranscribing(false);
|
|
48657
|
+
setStatusText("");
|
|
48658
|
+
}
|
|
48659
|
+
};
|
|
48660
|
+
mediaRecorder.start();
|
|
48661
|
+
setIsRecording(true);
|
|
48662
|
+
} catch {
|
|
48663
|
+
stream?.getTracks().forEach((t2) => t2.stop());
|
|
48664
|
+
setError("Could not access microphone. Check browser permissions.");
|
|
47998
48665
|
}
|
|
47999
|
-
|
|
48000
|
-
|
|
48001
|
-
|
|
48002
|
-
|
|
48003
|
-
|
|
48004
|
-
|
|
48005
|
-
|
|
48006
|
-
|
|
48007
|
-
|
|
48008
|
-
|
|
48009
|
-
},
|
|
48010
|
-
[excalidrawAPI]
|
|
48011
|
-
);
|
|
48012
|
-
const handleRemoveRef = useCallback24(() => {
|
|
48013
|
-
setFrameRef(null);
|
|
48014
|
-
}, []);
|
|
48015
|
-
const handleSend = useCallback24(async () => {
|
|
48016
|
-
const text = prompt.trim();
|
|
48017
|
-
const normalizedKey = apiKey.trim();
|
|
48018
|
-
if (!text || isLoading) {
|
|
48019
|
-
return;
|
|
48020
|
-
}
|
|
48021
|
-
if (!normalizedKey) {
|
|
48022
|
-
setError(
|
|
48023
|
-
"No OpenRouter API key. Set VITE_APP_OPENROUTER_API_KEY in .env or pass the key via the `apiKey` prop."
|
|
48666
|
+
}, [isRecording, apiKey]);
|
|
48667
|
+
const handleCopy = useCallback24((msgId, content) => {
|
|
48668
|
+
navigator.clipboard.writeText(content).then(
|
|
48669
|
+
() => {
|
|
48670
|
+
setCopiedMsgId(msgId);
|
|
48671
|
+
setTimeout(() => setCopiedMsgId(null), 2e3);
|
|
48672
|
+
},
|
|
48673
|
+
() => {
|
|
48674
|
+
setError("Copy failed \u2014 please grant clipboard permission.");
|
|
48675
|
+
}
|
|
48024
48676
|
);
|
|
48025
|
-
|
|
48026
|
-
|
|
48027
|
-
|
|
48028
|
-
|
|
48029
|
-
|
|
48030
|
-
|
|
48031
|
-
|
|
48032
|
-
|
|
48033
|
-
|
|
48034
|
-
|
|
48035
|
-
|
|
48036
|
-
|
|
48037
|
-
|
|
48038
|
-
|
|
48039
|
-
|
|
48040
|
-
|
|
48041
|
-
|
|
48042
|
-
|
|
48043
|
-
|
|
48044
|
-
|
|
48045
|
-
|
|
48046
|
-
|
|
48047
|
-
|
|
48048
|
-
|
|
48049
|
-
|
|
48050
|
-
|
|
48051
|
-
|
|
48052
|
-
|
|
48677
|
+
}, []);
|
|
48678
|
+
const handleSend = useCallback24(
|
|
48679
|
+
async (textOverride) => {
|
|
48680
|
+
const text = (textOverride ?? prompt).trim();
|
|
48681
|
+
const normalizedKey = resolveOpenRouterApiKey(apiKey);
|
|
48682
|
+
if (!text || isLoading || isSendPending) {
|
|
48683
|
+
voiceUsedRef.current = false;
|
|
48684
|
+
return;
|
|
48685
|
+
}
|
|
48686
|
+
if (!normalizedKey) {
|
|
48687
|
+
voiceUsedRef.current = false;
|
|
48688
|
+
setError(
|
|
48689
|
+
"No OpenRouter API key. Set VITE_APP_OPENROUTER_API_KEY in .env or pass the key via the `apiKey` prop."
|
|
48690
|
+
);
|
|
48691
|
+
return;
|
|
48692
|
+
}
|
|
48693
|
+
const capturedFiles = [...attachedFiles];
|
|
48694
|
+
const capturedWebSearch = webSearchEnabled;
|
|
48695
|
+
const capturedFrameRef = frameRef;
|
|
48696
|
+
const userMsg = {
|
|
48697
|
+
id: genId(),
|
|
48698
|
+
role: "user",
|
|
48699
|
+
content: text,
|
|
48700
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
48701
|
+
attachments: capturedFiles.length > 0 ? capturedFiles.map((f) => ({
|
|
48702
|
+
name: f.name,
|
|
48703
|
+
type: f.type,
|
|
48704
|
+
dataUrl: f.dataUrl
|
|
48705
|
+
})) : void 0
|
|
48706
|
+
};
|
|
48707
|
+
const isFirstMessage = messages.length === 0;
|
|
48708
|
+
const prevMessages = messages;
|
|
48709
|
+
const nextMessages = [...messages, userMsg];
|
|
48710
|
+
setMessages(nextMessages);
|
|
48711
|
+
setPrompt("");
|
|
48712
|
+
prevPromptRef.current = "";
|
|
48713
|
+
setAttachedFiles([]);
|
|
48714
|
+
setFrameRef(null);
|
|
48715
|
+
setIsSendPending(true);
|
|
48716
|
+
setError(null);
|
|
48717
|
+
const hadVoice = voiceUsedRef.current;
|
|
48718
|
+
voiceUsedRef.current = false;
|
|
48719
|
+
if (onBeforeSend) {
|
|
48720
|
+
const { allowed, error: creditError } = await onBeforeSend({
|
|
48721
|
+
hasVoice: hadVoice
|
|
48722
|
+
});
|
|
48723
|
+
if (!allowed) {
|
|
48724
|
+
setMessages(prevMessages);
|
|
48725
|
+
setError(creditError || "Insufficient credits");
|
|
48726
|
+
setIsSendPending(false);
|
|
48727
|
+
return;
|
|
48728
|
+
}
|
|
48729
|
+
}
|
|
48730
|
+
setIsSendPending(false);
|
|
48731
|
+
const controller = new AbortController();
|
|
48732
|
+
abortControllerRef.current = controller;
|
|
48733
|
+
setIsLoading(true);
|
|
48734
|
+
setStatusText("Thinking\u2026");
|
|
48735
|
+
if (!excalidrawAPI) {
|
|
48736
|
+
try {
|
|
48737
|
+
const reply = await callPlainChatAPI(
|
|
48738
|
+
nextMessages,
|
|
48739
|
+
normalizedKey,
|
|
48740
|
+
controller.signal,
|
|
48741
|
+
{
|
|
48742
|
+
webSearchEnabled: capturedWebSearch,
|
|
48743
|
+
attachments: capturedFiles
|
|
48744
|
+
}
|
|
48745
|
+
);
|
|
48746
|
+
const assistantMsg = {
|
|
48747
|
+
id: genId(),
|
|
48748
|
+
role: "assistant",
|
|
48749
|
+
content: reply,
|
|
48750
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
48751
|
+
};
|
|
48752
|
+
const updatedMessages = [...nextMessages, assistantMsg];
|
|
48753
|
+
setMessages(updatedMessages);
|
|
48754
|
+
if (onAfterSend) {
|
|
48755
|
+
onAfterSend({ hasVoice: hadVoice });
|
|
48756
|
+
}
|
|
48757
|
+
if (isFirstMessage && onSessionCreate) {
|
|
48758
|
+
onSessionCreate({
|
|
48759
|
+
id: currentSessionId,
|
|
48760
|
+
title: text.slice(0, 40)
|
|
48761
|
+
});
|
|
48762
|
+
}
|
|
48763
|
+
if (onMessagesChange) {
|
|
48764
|
+
onMessagesChange(updatedMessages, currentSessionId);
|
|
48765
|
+
}
|
|
48766
|
+
} catch (err) {
|
|
48767
|
+
if (err instanceof Error && err.name !== "AbortError") {
|
|
48768
|
+
setError(err.message);
|
|
48769
|
+
}
|
|
48770
|
+
} finally {
|
|
48771
|
+
abortControllerRef.current = null;
|
|
48772
|
+
setIsLoading(false);
|
|
48773
|
+
setStatusText("");
|
|
48774
|
+
}
|
|
48775
|
+
return;
|
|
48776
|
+
}
|
|
48777
|
+
try {
|
|
48778
|
+
const result = await runAgentLoop({
|
|
48779
|
+
userMessages: nextMessages.map((m) => ({
|
|
48780
|
+
role: m.role,
|
|
48781
|
+
content: m.content
|
|
48782
|
+
})),
|
|
48783
|
+
elementContext: capturedFrameRef?.serialized,
|
|
48784
|
+
frameScreenshot: capturedFrameRef?.screenshot,
|
|
48785
|
+
fileAttachments: capturedFiles.length > 0 ? capturedFiles.map((f) => ({
|
|
48786
|
+
name: f.name,
|
|
48787
|
+
type: f.type,
|
|
48788
|
+
dataUrl: f.dataUrl,
|
|
48789
|
+
textContent: f.textContent
|
|
48790
|
+
})) : void 0,
|
|
48791
|
+
webSearchEnabled: capturedWebSearch,
|
|
48792
|
+
apiKey: normalizedKey,
|
|
48793
|
+
toolCtx: {
|
|
48794
|
+
excalidrawAPI,
|
|
48795
|
+
geminiApiKey: geminiApiKey || "",
|
|
48796
|
+
signal: controller.signal
|
|
48797
|
+
},
|
|
48798
|
+
onUpdate: (update) => {
|
|
48799
|
+
if (update.type !== "final") {
|
|
48800
|
+
setStatusText(update.message);
|
|
48801
|
+
}
|
|
48802
|
+
},
|
|
48803
|
+
signal: controller.signal,
|
|
48804
|
+
onBeforeImageGen,
|
|
48805
|
+
onAfterImageGen
|
|
48806
|
+
});
|
|
48807
|
+
const assistantMsgAgent = {
|
|
48053
48808
|
id: genId(),
|
|
48054
48809
|
role: "assistant",
|
|
48055
|
-
content: reply,
|
|
48056
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
48810
|
+
content: result.reply,
|
|
48811
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
48812
|
+
toolActions: result.toolActions.length > 0 ? result.toolActions : void 0
|
|
48813
|
+
};
|
|
48814
|
+
const updatedMessagesAgent = [...nextMessages, assistantMsgAgent];
|
|
48815
|
+
setMessages(updatedMessagesAgent);
|
|
48816
|
+
if (onAfterSend) {
|
|
48817
|
+
onAfterSend({ hasVoice: hadVoice });
|
|
48057
48818
|
}
|
|
48058
|
-
|
|
48059
|
-
|
|
48060
|
-
|
|
48061
|
-
|
|
48819
|
+
if (isFirstMessage && onSessionCreate) {
|
|
48820
|
+
onSessionCreate({ id: currentSessionId, title: text.slice(0, 40) });
|
|
48821
|
+
}
|
|
48822
|
+
if (onMessagesChange) {
|
|
48823
|
+
onMessagesChange(updatedMessagesAgent, currentSessionId);
|
|
48824
|
+
}
|
|
48825
|
+
} catch (err) {
|
|
48826
|
+
if (err instanceof Error && err.name !== "AbortError") {
|
|
48827
|
+
setError(err.message);
|
|
48828
|
+
}
|
|
48829
|
+
} finally {
|
|
48830
|
+
abortControllerRef.current = null;
|
|
48831
|
+
setIsLoading(false);
|
|
48832
|
+
setStatusText("");
|
|
48062
48833
|
}
|
|
48063
|
-
}
|
|
48064
|
-
|
|
48065
|
-
|
|
48066
|
-
|
|
48067
|
-
|
|
48068
|
-
|
|
48069
|
-
|
|
48070
|
-
|
|
48071
|
-
|
|
48072
|
-
|
|
48073
|
-
|
|
48074
|
-
|
|
48075
|
-
|
|
48076
|
-
|
|
48077
|
-
|
|
48078
|
-
|
|
48079
|
-
|
|
48080
|
-
|
|
48081
|
-
|
|
48082
|
-
|
|
48083
|
-
|
|
48084
|
-
|
|
48085
|
-
|
|
48086
|
-
|
|
48834
|
+
},
|
|
48835
|
+
[
|
|
48836
|
+
prompt,
|
|
48837
|
+
isLoading,
|
|
48838
|
+
isSendPending,
|
|
48839
|
+
messages,
|
|
48840
|
+
apiKey,
|
|
48841
|
+
excalidrawAPI,
|
|
48842
|
+
geminiApiKey,
|
|
48843
|
+
frameRef,
|
|
48844
|
+
attachedFiles,
|
|
48845
|
+
webSearchEnabled,
|
|
48846
|
+
currentSessionId,
|
|
48847
|
+
onBeforeSend,
|
|
48848
|
+
onAfterSend,
|
|
48849
|
+
onMessagesChange,
|
|
48850
|
+
onSessionCreate,
|
|
48851
|
+
onBeforeImageGen,
|
|
48852
|
+
onAfterImageGen
|
|
48853
|
+
]
|
|
48854
|
+
);
|
|
48855
|
+
useImperativeHandle4(
|
|
48856
|
+
ref,
|
|
48857
|
+
() => ({
|
|
48858
|
+
setMessages: (msgs, sessionId) => {
|
|
48859
|
+
const targetSessionId = sessionId ?? currentSessionIdRef.current;
|
|
48860
|
+
setSessions((prev) => {
|
|
48861
|
+
const idx = prev.findIndex((s) => s.id === targetSessionId);
|
|
48862
|
+
if (idx >= 0) {
|
|
48863
|
+
const next = [...prev];
|
|
48864
|
+
next[idx] = { ...next[idx], messages: msgs };
|
|
48865
|
+
return next;
|
|
48866
|
+
}
|
|
48867
|
+
return prev;
|
|
48868
|
+
});
|
|
48869
|
+
if (targetSessionId === currentSessionIdRef.current) {
|
|
48870
|
+
setMessages(msgs);
|
|
48871
|
+
setIsLoadingSession(false);
|
|
48087
48872
|
}
|
|
48088
48873
|
},
|
|
48089
|
-
|
|
48090
|
-
})
|
|
48091
|
-
|
|
48092
|
-
|
|
48093
|
-
|
|
48094
|
-
|
|
48095
|
-
|
|
48096
|
-
|
|
48097
|
-
|
|
48098
|
-
|
|
48099
|
-
|
|
48100
|
-
|
|
48101
|
-
|
|
48102
|
-
|
|
48103
|
-
|
|
48104
|
-
}
|
|
48105
|
-
} finally {
|
|
48106
|
-
abortControllerRef.current = null;
|
|
48107
|
-
setIsLoading(false);
|
|
48108
|
-
setStatusText("");
|
|
48874
|
+
send: (text) => handleSend(text)
|
|
48875
|
+
}),
|
|
48876
|
+
[handleSend]
|
|
48877
|
+
);
|
|
48878
|
+
const handleStop = useCallback24(() => {
|
|
48879
|
+
abortControllerRef.current?.abort();
|
|
48880
|
+
}, []);
|
|
48881
|
+
const handleChip = useCallback24((chip) => {
|
|
48882
|
+
setPrompt(chip);
|
|
48883
|
+
}, []);
|
|
48884
|
+
const filteredSessions = sessions.filter(
|
|
48885
|
+
(s) => historySearch ? s.title.toLowerCase().includes(historySearch.toLowerCase()) : true
|
|
48886
|
+
);
|
|
48887
|
+
if (!isOpen) {
|
|
48888
|
+
return null;
|
|
48109
48889
|
}
|
|
48110
|
-
|
|
48111
|
-
|
|
48112
|
-
|
|
48113
|
-
|
|
48114
|
-
|
|
48115
|
-
|
|
48116
|
-
|
|
48117
|
-
|
|
48118
|
-
|
|
48119
|
-
const handleStop = useCallback24(() => {
|
|
48120
|
-
abortControllerRef.current?.abort();
|
|
48121
|
-
}, []);
|
|
48122
|
-
const handleChip = useCallback24((chip) => {
|
|
48123
|
-
setPrompt(chip);
|
|
48124
|
-
}, []);
|
|
48125
|
-
const filteredSessions = sessions.filter(
|
|
48126
|
-
(s) => historySearch ? s.title.toLowerCase().includes(historySearch.toLowerCase()) : true
|
|
48127
|
-
);
|
|
48128
|
-
if (!isOpen) {
|
|
48129
|
-
return null;
|
|
48130
|
-
}
|
|
48131
|
-
return /* @__PURE__ */ jsx184(
|
|
48132
|
-
"div",
|
|
48133
|
-
{
|
|
48134
|
-
className: "acp",
|
|
48135
|
-
onPointerDown: (e) => e.stopPropagation(),
|
|
48136
|
-
onClick: (e) => e.stopPropagation(),
|
|
48137
|
-
children: /* @__PURE__ */ jsxs100("div", { className: "acp-panel", children: [
|
|
48138
|
-
/* @__PURE__ */ jsxs100("div", { className: "acp-header", ref: historyRef, children: [
|
|
48139
|
-
/* @__PURE__ */ jsxs100(
|
|
48140
|
-
"button",
|
|
48141
|
-
{
|
|
48142
|
-
className: "acp-title-btn",
|
|
48143
|
-
onClick: () => setHistoryOpen((v) => !v),
|
|
48144
|
-
title: "Chat history",
|
|
48145
|
-
children: [
|
|
48146
|
-
/* @__PURE__ */ jsx184("span", { children: currentTitle }),
|
|
48147
|
-
/* @__PURE__ */ jsx184(ChevronDown2, { size: 13 })
|
|
48148
|
-
]
|
|
48149
|
-
}
|
|
48150
|
-
),
|
|
48151
|
-
/* @__PURE__ */ jsxs100("div", { className: "acp-header-right", children: [
|
|
48152
|
-
/* @__PURE__ */ jsx184(
|
|
48890
|
+
return /* @__PURE__ */ jsx184(
|
|
48891
|
+
"div",
|
|
48892
|
+
{
|
|
48893
|
+
className: "acp",
|
|
48894
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
48895
|
+
onClick: (e) => e.stopPropagation(),
|
|
48896
|
+
children: /* @__PURE__ */ jsxs100("div", { className: "acp-panel", children: [
|
|
48897
|
+
/* @__PURE__ */ jsxs100("div", { className: "acp-header", ref: historyRef, children: [
|
|
48898
|
+
/* @__PURE__ */ jsxs100(
|
|
48153
48899
|
"button",
|
|
48154
48900
|
{
|
|
48155
|
-
className: "acp-
|
|
48156
|
-
onClick:
|
|
48157
|
-
title: "
|
|
48158
|
-
children:
|
|
48901
|
+
className: "acp-title-btn",
|
|
48902
|
+
onClick: () => setHistoryOpen((v) => !v),
|
|
48903
|
+
title: "Chat history",
|
|
48904
|
+
children: [
|
|
48905
|
+
/* @__PURE__ */ jsx184("span", { children: currentTitle }),
|
|
48906
|
+
/* @__PURE__ */ jsx184(ChevronDown2, { size: 13 })
|
|
48907
|
+
]
|
|
48159
48908
|
}
|
|
48160
48909
|
),
|
|
48161
|
-
/* @__PURE__ */
|
|
48162
|
-
] }),
|
|
48163
|
-
historyOpen && /* @__PURE__ */ jsxs100("div", { className: "acp-history-dropdown", children: [
|
|
48164
|
-
/* @__PURE__ */ jsxs100("div", { className: "acp-history-search", children: [
|
|
48165
|
-
/* @__PURE__ */ jsx184(Search, { size: 13 }),
|
|
48910
|
+
/* @__PURE__ */ jsxs100("div", { className: "acp-header-right", children: [
|
|
48166
48911
|
/* @__PURE__ */ jsx184(
|
|
48167
|
-
"input",
|
|
48168
|
-
{
|
|
48169
|
-
type: "search",
|
|
48170
|
-
placeholder: "Search chats\u2026",
|
|
48171
|
-
value: historySearch,
|
|
48172
|
-
onChange: (e) => setHistorySearch(e.target.value),
|
|
48173
|
-
autoFocus: true
|
|
48174
|
-
}
|
|
48175
|
-
)
|
|
48176
|
-
] }),
|
|
48177
|
-
/* @__PURE__ */ jsxs100("div", { className: "acp-history-list", children: [
|
|
48178
|
-
/* @__PURE__ */ jsxs100(
|
|
48179
48912
|
"button",
|
|
48180
48913
|
{
|
|
48181
|
-
className: "acp-
|
|
48914
|
+
className: "acp-icon-btn",
|
|
48182
48915
|
onClick: handleNewChat,
|
|
48183
|
-
|
|
48184
|
-
|
|
48185
|
-
/* @__PURE__ */ jsx184("span", { children: "New chat" })
|
|
48186
|
-
]
|
|
48916
|
+
title: "New chat",
|
|
48917
|
+
children: /* @__PURE__ */ jsx184(Plus, { size: 15 })
|
|
48187
48918
|
}
|
|
48188
48919
|
),
|
|
48189
|
-
|
|
48190
|
-
|
|
48191
|
-
|
|
48192
|
-
|
|
48193
|
-
|
|
48194
|
-
|
|
48195
|
-
|
|
48196
|
-
|
|
48197
|
-
|
|
48198
|
-
|
|
48199
|
-
|
|
48200
|
-
|
|
48201
|
-
|
|
48920
|
+
/* @__PURE__ */ jsx184("button", { className: "acp-icon-btn", onClick: onClose, title: "Close", children: /* @__PURE__ */ jsx184(X2, { size: 15 }) })
|
|
48921
|
+
] }),
|
|
48922
|
+
historyOpen && /* @__PURE__ */ jsxs100("div", { className: "acp-history-dropdown", children: [
|
|
48923
|
+
/* @__PURE__ */ jsxs100("div", { className: "acp-history-search", children: [
|
|
48924
|
+
/* @__PURE__ */ jsx184(Search, { size: 13 }),
|
|
48925
|
+
/* @__PURE__ */ jsx184(
|
|
48926
|
+
"input",
|
|
48927
|
+
{
|
|
48928
|
+
type: "search",
|
|
48929
|
+
placeholder: "Search chats\u2026",
|
|
48930
|
+
value: historySearch,
|
|
48931
|
+
onChange: (e) => setHistorySearch(e.target.value),
|
|
48932
|
+
autoFocus: true
|
|
48933
|
+
}
|
|
48934
|
+
)
|
|
48935
|
+
] }),
|
|
48936
|
+
/* @__PURE__ */ jsxs100("div", { className: "acp-history-list", children: [
|
|
48937
|
+
/* @__PURE__ */ jsxs100(
|
|
48938
|
+
"button",
|
|
48939
|
+
{
|
|
48940
|
+
className: "acp-history-item acp-history-item--new",
|
|
48941
|
+
onClick: handleNewChat,
|
|
48942
|
+
children: [
|
|
48943
|
+
/* @__PURE__ */ jsx184(Plus, { size: 13 }),
|
|
48944
|
+
/* @__PURE__ */ jsx184("span", { children: "New chat" })
|
|
48945
|
+
]
|
|
48946
|
+
}
|
|
48947
|
+
),
|
|
48948
|
+
filteredSessions.map((session) => /* @__PURE__ */ jsxs100(
|
|
48949
|
+
"button",
|
|
48950
|
+
{
|
|
48951
|
+
className: "acp-history-item",
|
|
48952
|
+
onClick: () => handleSwitchSession(session),
|
|
48953
|
+
children: [
|
|
48954
|
+
/* @__PURE__ */ jsx184(MessageSquare, { size: 13 }),
|
|
48955
|
+
/* @__PURE__ */ jsx184("span", { children: session.title })
|
|
48956
|
+
]
|
|
48957
|
+
},
|
|
48958
|
+
session.id
|
|
48959
|
+
)),
|
|
48960
|
+
filteredSessions.length === 0 && historySearch && /* @__PURE__ */ jsx184("div", { className: "acp-history-empty", children: "No matching chats" })
|
|
48961
|
+
] })
|
|
48202
48962
|
] })
|
|
48203
|
-
] })
|
|
48204
|
-
] }),
|
|
48205
|
-
/* @__PURE__ */ jsx184("div", { className: "acp-messages-wrap", children: messages.length === 0 && !isLoading ? /* @__PURE__ */ jsxs100("div", { className: "acp-empty", children: [
|
|
48206
|
-
/* @__PURE__ */ jsx184("div", { className: "acp-empty-icon", children: /* @__PURE__ */ jsx184(MessageSquare, { size: 22 }) }),
|
|
48207
|
-
/* @__PURE__ */ jsxs100("div", { children: [
|
|
48208
|
-
/* @__PURE__ */ jsx184("div", { className: "acp-empty-title", children: "AI Ad Designer" }),
|
|
48209
|
-
/* @__PURE__ */ jsx184("div", { className: "acp-empty-sub", children: "Describe an ad and I'll create it on the canvas. Use @ to reference selected elements." })
|
|
48210
48963
|
] }),
|
|
48211
|
-
/* @__PURE__ */ jsx184("div", { className: "acp-
|
|
48212
|
-
"
|
|
48213
|
-
|
|
48214
|
-
|
|
48215
|
-
|
|
48216
|
-
|
|
48217
|
-
}
|
|
48218
|
-
|
|
48219
|
-
|
|
48220
|
-
|
|
48221
|
-
|
|
48222
|
-
|
|
48223
|
-
|
|
48224
|
-
|
|
48225
|
-
|
|
48226
|
-
|
|
48227
|
-
|
|
48228
|
-
|
|
48229
|
-
|
|
48230
|
-
|
|
48231
|
-
|
|
48232
|
-
|
|
48233
|
-
|
|
48234
|
-
|
|
48235
|
-
|
|
48236
|
-
|
|
48237
|
-
|
|
48238
|
-
|
|
48239
|
-
|
|
48240
|
-
|
|
48241
|
-
|
|
48242
|
-
|
|
48243
|
-
|
|
48244
|
-
|
|
48245
|
-
|
|
48246
|
-
|
|
48247
|
-
|
|
48964
|
+
/* @__PURE__ */ jsx184("div", { className: "acp-messages-wrap", children: isLoadingSession ? /* @__PURE__ */ jsx184("div", { className: "acp-empty", children: /* @__PURE__ */ jsxs100("div", { className: "acp-loading-session", children: [
|
|
48965
|
+
/* @__PURE__ */ jsxs100("div", { className: "acp-loading-dots", children: [
|
|
48966
|
+
/* @__PURE__ */ jsx184("span", {}),
|
|
48967
|
+
/* @__PURE__ */ jsx184("span", {}),
|
|
48968
|
+
/* @__PURE__ */ jsx184("span", {})
|
|
48969
|
+
] }),
|
|
48970
|
+
/* @__PURE__ */ jsx184("span", { className: "acp-status-text", children: "Loading chat\u2026" })
|
|
48971
|
+
] }) }) : messages.length === 0 && !isLoading && !isSendPending ? /* @__PURE__ */ jsxs100("div", { className: "acp-empty", children: [
|
|
48972
|
+
/* @__PURE__ */ jsx184("div", { className: "acp-empty-icon", children: /* @__PURE__ */ jsx184(MessageSquare, { size: 22 }) }),
|
|
48973
|
+
/* @__PURE__ */ jsxs100("div", { children: [
|
|
48974
|
+
/* @__PURE__ */ jsx184("div", { className: "acp-empty-title", children: "AI Ad Designer" }),
|
|
48975
|
+
/* @__PURE__ */ jsx184("div", { className: "acp-empty-sub", children: "Describe an ad and I'll create it on the canvas. Type @ to reference frames." })
|
|
48976
|
+
] }),
|
|
48977
|
+
/* @__PURE__ */ jsx184("div", { className: "acp-chips", children: SUGGESTIONS.map((chip) => /* @__PURE__ */ jsx184(
|
|
48978
|
+
"button",
|
|
48979
|
+
{
|
|
48980
|
+
className: "acp-chip",
|
|
48981
|
+
onClick: () => handleChip(chip),
|
|
48982
|
+
children: chip
|
|
48983
|
+
},
|
|
48984
|
+
chip
|
|
48985
|
+
)) })
|
|
48986
|
+
] }) : /* @__PURE__ */ jsxs100(ChatContainerRoot, { className: "acp-chat-root", children: [
|
|
48987
|
+
/* @__PURE__ */ jsxs100(ChatContainerContent, { className: "acp-chat-content", children: [
|
|
48988
|
+
messages.map((msg, index) => {
|
|
48989
|
+
const isAssistant = msg.role === "assistant";
|
|
48990
|
+
const isLast = index === messages.length - 1;
|
|
48991
|
+
return /* @__PURE__ */ jsx184(
|
|
48992
|
+
Message,
|
|
48993
|
+
{
|
|
48994
|
+
className: `acp-msg ${isAssistant ? "acp-msg--assistant" : "acp-msg--user"}`,
|
|
48995
|
+
children: isAssistant ? /* @__PURE__ */ jsxs100("div", { className: "acp-msg-inner", children: [
|
|
48996
|
+
msg.toolActions && msg.toolActions.length > 0 && /* @__PURE__ */ jsx184(ToolActionsDisplay, { actions: msg.toolActions }),
|
|
48997
|
+
/* @__PURE__ */ jsx184(
|
|
48998
|
+
MessageContent,
|
|
48999
|
+
{
|
|
49000
|
+
markdown: true,
|
|
49001
|
+
className: "acp-content-assistant",
|
|
49002
|
+
children: msg.content
|
|
49003
|
+
}
|
|
49004
|
+
),
|
|
49005
|
+
/* @__PURE__ */ jsx184(
|
|
49006
|
+
MessageActions,
|
|
49007
|
+
{
|
|
49008
|
+
className: `acp-msg-actions${isLast ? " acp-msg-actions--visible" : ""}`,
|
|
49009
|
+
children: /* @__PURE__ */ jsx184(MessageAction, { tooltip: "Copy", delayDuration: 100, children: /* @__PURE__ */ jsx184(
|
|
49010
|
+
"button",
|
|
48248
49011
|
{
|
|
48249
|
-
|
|
48250
|
-
|
|
48251
|
-
children: /* @__PURE__ */ jsx184(
|
|
49012
|
+
className: "acp-action-btn",
|
|
49013
|
+
onClick: () => handleCopy(msg.id, msg.content),
|
|
49014
|
+
children: copiedMsgId === msg.id ? /* @__PURE__ */ jsx184(Check, { size: 14 }) : /* @__PURE__ */ jsx184(Copy, { size: 14 })
|
|
48252
49015
|
}
|
|
48253
|
-
)
|
|
48254
|
-
|
|
48255
|
-
|
|
48256
|
-
)
|
|
48257
|
-
|
|
48258
|
-
|
|
48259
|
-
|
|
48260
|
-
|
|
48261
|
-
|
|
48262
|
-
|
|
49016
|
+
) })
|
|
49017
|
+
}
|
|
49018
|
+
)
|
|
49019
|
+
] }) : /* @__PURE__ */ jsxs100("div", { className: "acp-msg-inner", children: [
|
|
49020
|
+
msg.attachments && msg.attachments.length > 0 && /* @__PURE__ */ jsx184("div", { className: "acp-msg-attachments", children: msg.attachments.map(
|
|
49021
|
+
(att, i) => att.type === "image" && att.dataUrl ? /* @__PURE__ */ jsx184(
|
|
49022
|
+
"img",
|
|
49023
|
+
{
|
|
49024
|
+
src: att.dataUrl,
|
|
49025
|
+
alt: att.name,
|
|
49026
|
+
className: "acp-msg-att-thumb",
|
|
49027
|
+
title: att.name
|
|
49028
|
+
},
|
|
49029
|
+
i
|
|
49030
|
+
) : /* @__PURE__ */ jsxs100("span", { className: "acp-msg-att-pill", children: [
|
|
49031
|
+
/* @__PURE__ */ jsx184(Paperclip, { size: 11 }),
|
|
49032
|
+
att.name
|
|
49033
|
+
] }, i)
|
|
49034
|
+
) }),
|
|
49035
|
+
/* @__PURE__ */ jsx184(MessageContent, { className: "acp-content-user", children: msg.content }),
|
|
49036
|
+
/* @__PURE__ */ jsx184(MessageActions, { className: "acp-msg-actions", children: /* @__PURE__ */ jsx184(MessageAction, { tooltip: "Copy", delayDuration: 100, children: /* @__PURE__ */ jsx184(
|
|
49037
|
+
"button",
|
|
49038
|
+
{
|
|
49039
|
+
className: "acp-action-btn",
|
|
49040
|
+
onClick: () => handleCopy(msg.id, msg.content),
|
|
49041
|
+
children: copiedMsgId === msg.id ? /* @__PURE__ */ jsx184(Check, { size: 14 }) : /* @__PURE__ */ jsx184(Copy, { size: 14 })
|
|
49042
|
+
}
|
|
49043
|
+
) }) })
|
|
49044
|
+
] })
|
|
49045
|
+
},
|
|
49046
|
+
msg.id
|
|
49047
|
+
);
|
|
49048
|
+
}),
|
|
49049
|
+
isLoading && /* @__PURE__ */ jsx184(Message, { className: "acp-msg acp-msg--assistant", children: /* @__PURE__ */ jsx184("div", { className: "acp-msg-inner", children: /* @__PURE__ */ jsxs100("div", { className: "acp-status-indicator", children: [
|
|
49050
|
+
/* @__PURE__ */ jsxs100("div", { className: "acp-loading-dots", children: [
|
|
49051
|
+
/* @__PURE__ */ jsx184("span", {}),
|
|
49052
|
+
/* @__PURE__ */ jsx184("span", {}),
|
|
49053
|
+
/* @__PURE__ */ jsx184("span", {})
|
|
49054
|
+
] }),
|
|
49055
|
+
statusText && /* @__PURE__ */ jsx184("span", { className: "acp-status-text", children: statusText })
|
|
49056
|
+
] }) }) })
|
|
49057
|
+
] }),
|
|
49058
|
+
/* @__PURE__ */ jsx184("div", { className: "acp-scroll-anchor", children: /* @__PURE__ */ jsx184(ScrollButton, { className: "acp-scroll-btn" }) })
|
|
49059
|
+
] }) }),
|
|
49060
|
+
error && /* @__PURE__ */ jsx184("div", { className: "acp-error", children: error }),
|
|
49061
|
+
frameRef && /* @__PURE__ */ jsx184("div", { className: "acp-ref-area", children: /* @__PURE__ */ jsxs100("div", { className: "acp-ref-pill", children: [
|
|
49062
|
+
/* @__PURE__ */ jsx184(AtSign, { size: 12 }),
|
|
49063
|
+
/* @__PURE__ */ jsx184("span", { children: frameRef.label }),
|
|
49064
|
+
/* @__PURE__ */ jsx184(
|
|
49065
|
+
"button",
|
|
49066
|
+
{
|
|
49067
|
+
className: "acp-ref-remove",
|
|
49068
|
+
onClick: handleRemoveRef,
|
|
49069
|
+
title: "Remove reference",
|
|
49070
|
+
children: /* @__PURE__ */ jsx184(X2, { size: 10 })
|
|
49071
|
+
}
|
|
49072
|
+
)
|
|
49073
|
+
] }) }),
|
|
49074
|
+
attachedFiles.length > 0 && /* @__PURE__ */ jsx184("div", { className: "acp-attachments-area", children: attachedFiles.map((f) => /* @__PURE__ */ jsxs100("div", { className: "acp-attachment-pill", children: [
|
|
49075
|
+
f.type === "image" && f.dataUrl ? /* @__PURE__ */ jsx184(
|
|
49076
|
+
"img",
|
|
49077
|
+
{
|
|
49078
|
+
src: f.dataUrl,
|
|
49079
|
+
alt: f.name,
|
|
49080
|
+
className: "acp-attachment-thumb"
|
|
49081
|
+
}
|
|
49082
|
+
) : /* @__PURE__ */ jsx184(Paperclip, { size: 12 }),
|
|
49083
|
+
/* @__PURE__ */ jsx184("span", { children: f.name }),
|
|
49084
|
+
/* @__PURE__ */ jsx184(
|
|
49085
|
+
"button",
|
|
49086
|
+
{
|
|
49087
|
+
className: "acp-attachment-remove",
|
|
49088
|
+
onClick: () => handleRemoveFile(f.id),
|
|
49089
|
+
title: "Remove file",
|
|
49090
|
+
children: /* @__PURE__ */ jsx184(X2, { size: 10 })
|
|
49091
|
+
}
|
|
49092
|
+
)
|
|
49093
|
+
] }, f.id)) }),
|
|
49094
|
+
/* @__PURE__ */ jsx184("div", { className: "acp-input-area", children: /* @__PURE__ */ jsxs100("div", { className: "acp-input-wrapper", ref: mentionMenuRef, children: [
|
|
49095
|
+
mentionMenuOpen && /* @__PURE__ */ jsx184("div", { className: "acp-mention-menu", children: availableFrames.length > 0 ? availableFrames.map((f) => /* @__PURE__ */ jsxs100(
|
|
49096
|
+
"button",
|
|
49097
|
+
{
|
|
49098
|
+
className: "acp-mention-item",
|
|
49099
|
+
onClick: () => handlePickFrame(f),
|
|
49100
|
+
children: [
|
|
49101
|
+
/* @__PURE__ */ jsx184(AtSign, { size: 14 }),
|
|
49102
|
+
/* @__PURE__ */ jsxs100("span", { children: [
|
|
49103
|
+
f.name,
|
|
49104
|
+
" ",
|
|
49105
|
+
/* @__PURE__ */ jsxs100("span", { className: "acp-mention-dim", children: [
|
|
49106
|
+
"(",
|
|
49107
|
+
f.width,
|
|
49108
|
+
"x",
|
|
49109
|
+
f.height,
|
|
49110
|
+
", ",
|
|
49111
|
+
f.childCount,
|
|
49112
|
+
" items)"
|
|
48263
49113
|
] })
|
|
48264
49114
|
] })
|
|
48265
|
-
|
|
48266
|
-
|
|
48267
|
-
|
|
48268
|
-
}),
|
|
48269
|
-
|
|
48270
|
-
|
|
48271
|
-
|
|
48272
|
-
|
|
48273
|
-
|
|
48274
|
-
|
|
48275
|
-
|
|
48276
|
-
|
|
48277
|
-
|
|
48278
|
-
|
|
48279
|
-
|
|
48280
|
-
|
|
48281
|
-
|
|
48282
|
-
|
|
48283
|
-
|
|
48284
|
-
|
|
48285
|
-
|
|
48286
|
-
|
|
48287
|
-
|
|
48288
|
-
|
|
48289
|
-
|
|
48290
|
-
|
|
48291
|
-
|
|
48292
|
-
|
|
48293
|
-
|
|
48294
|
-
|
|
48295
|
-
|
|
48296
|
-
|
|
48297
|
-
|
|
48298
|
-
|
|
48299
|
-
|
|
48300
|
-
|
|
48301
|
-
|
|
48302
|
-
|
|
48303
|
-
|
|
48304
|
-
|
|
48305
|
-
|
|
48306
|
-
|
|
48307
|
-
|
|
48308
|
-
|
|
48309
|
-
|
|
48310
|
-
|
|
48311
|
-
|
|
48312
|
-
|
|
48313
|
-
|
|
48314
|
-
|
|
48315
|
-
|
|
48316
|
-
|
|
48317
|
-
|
|
48318
|
-
|
|
48319
|
-
|
|
48320
|
-
|
|
48321
|
-
|
|
48322
|
-
|
|
48323
|
-
|
|
48324
|
-
|
|
48325
|
-
|
|
48326
|
-
|
|
48327
|
-
|
|
48328
|
-
|
|
48329
|
-
|
|
48330
|
-
|
|
48331
|
-
|
|
48332
|
-
|
|
48333
|
-
|
|
48334
|
-
|
|
48335
|
-
|
|
48336
|
-
|
|
48337
|
-
|
|
48338
|
-
|
|
48339
|
-
|
|
48340
|
-
|
|
48341
|
-
|
|
48342
|
-
|
|
48343
|
-
|
|
48344
|
-
|
|
48345
|
-
|
|
48346
|
-
|
|
48347
|
-
|
|
48348
|
-
|
|
48349
|
-
|
|
48350
|
-
|
|
48351
|
-
|
|
48352
|
-
|
|
48353
|
-
|
|
48354
|
-
|
|
48355
|
-
|
|
48356
|
-
|
|
48357
|
-
|
|
48358
|
-
|
|
48359
|
-
|
|
48360
|
-
|
|
48361
|
-
|
|
48362
|
-
|
|
49115
|
+
]
|
|
49116
|
+
},
|
|
49117
|
+
f.id
|
|
49118
|
+
)) : /* @__PURE__ */ jsx184("div", { className: "acp-mention-empty", children: "No frames on the canvas. Create a frame first, then type @ to reference it." }) }),
|
|
49119
|
+
/* @__PURE__ */ jsx184(
|
|
49120
|
+
"input",
|
|
49121
|
+
{
|
|
49122
|
+
ref: fileInputRef,
|
|
49123
|
+
type: "file",
|
|
49124
|
+
multiple: true,
|
|
49125
|
+
accept: "image/*,.txt,.json,.csv,.md,.html,.css,.js,.ts,.tsx,.jsx,.py,.xml",
|
|
49126
|
+
onChange: handleFileUpload,
|
|
49127
|
+
style: { display: "none" }
|
|
49128
|
+
}
|
|
49129
|
+
),
|
|
49130
|
+
/* @__PURE__ */ jsxs100(
|
|
49131
|
+
PromptInput,
|
|
49132
|
+
{
|
|
49133
|
+
isLoading,
|
|
49134
|
+
value: prompt,
|
|
49135
|
+
onValueChange: handlePromptChange,
|
|
49136
|
+
onSubmit: () => handleSend(),
|
|
49137
|
+
className: "acp-prompt-input",
|
|
49138
|
+
children: [
|
|
49139
|
+
/* @__PURE__ */ jsx184(
|
|
49140
|
+
PromptInputTextarea,
|
|
49141
|
+
{
|
|
49142
|
+
placeholder: excalidrawAPI ? "Describe an ad to create\u2026 (@ for frames)" : "Ask anything",
|
|
49143
|
+
className: "acp-textarea"
|
|
49144
|
+
}
|
|
49145
|
+
),
|
|
49146
|
+
/* @__PURE__ */ jsxs100(PromptInputActions, { className: "acp-actions-bar", children: [
|
|
49147
|
+
/* @__PURE__ */ jsxs100("div", { className: "acp-actions-left", children: [
|
|
49148
|
+
/* @__PURE__ */ jsx184(PromptInputAction, { tooltip: "Attach files (max 3)", children: /* @__PURE__ */ jsx184(
|
|
49149
|
+
"button",
|
|
49150
|
+
{
|
|
49151
|
+
className: "acp-pill-btn acp-pill-btn--icon",
|
|
49152
|
+
onClick: () => fileInputRef.current?.click(),
|
|
49153
|
+
disabled: attachedFiles.length >= MAX_ATTACHED_FILES,
|
|
49154
|
+
children: /* @__PURE__ */ jsx184(Paperclip, { size: 16 })
|
|
49155
|
+
}
|
|
49156
|
+
) }),
|
|
49157
|
+
/* @__PURE__ */ jsx184(
|
|
49158
|
+
PromptInputAction,
|
|
49159
|
+
{
|
|
49160
|
+
tooltip: webSearchEnabled ? "Web search enabled" : "Enable web search",
|
|
49161
|
+
children: /* @__PURE__ */ jsxs100(
|
|
49162
|
+
"button",
|
|
49163
|
+
{
|
|
49164
|
+
className: `acp-pill-btn${webSearchEnabled ? " acp-pill-btn--active" : ""}`,
|
|
49165
|
+
onClick: toggleWebSearch,
|
|
49166
|
+
children: [
|
|
49167
|
+
/* @__PURE__ */ jsx184(Globe, { size: 15 }),
|
|
49168
|
+
/* @__PURE__ */ jsx184("span", { children: "Search" })
|
|
49169
|
+
]
|
|
49170
|
+
}
|
|
49171
|
+
)
|
|
49172
|
+
}
|
|
49173
|
+
)
|
|
49174
|
+
] }),
|
|
49175
|
+
/* @__PURE__ */ jsxs100("div", { className: "acp-actions-right", children: [
|
|
49176
|
+
/* @__PURE__ */ jsx184(
|
|
49177
|
+
PromptInputAction,
|
|
49178
|
+
{
|
|
49179
|
+
tooltip: isTranscribing ? "Transcribing\u2026" : isRecording ? "Stop recording" : "Voice input",
|
|
49180
|
+
children: /* @__PURE__ */ jsx184(
|
|
49181
|
+
"button",
|
|
49182
|
+
{
|
|
49183
|
+
className: `acp-pill-btn acp-pill-btn--icon${isRecording ? " acp-pill-btn--recording" : ""}${isTranscribing ? " acp-pill-btn--transcribing" : ""}`,
|
|
49184
|
+
onClick: handleVoiceInput,
|
|
49185
|
+
disabled: isTranscribing,
|
|
49186
|
+
children: isTranscribing ? /* @__PURE__ */ jsx184(
|
|
49187
|
+
"svg",
|
|
49188
|
+
{
|
|
49189
|
+
width: "16",
|
|
49190
|
+
height: "16",
|
|
49191
|
+
viewBox: "0 0 24 24",
|
|
49192
|
+
fill: "none",
|
|
49193
|
+
stroke: "currentColor",
|
|
49194
|
+
strokeWidth: "2",
|
|
49195
|
+
strokeLinecap: "round",
|
|
49196
|
+
strokeLinejoin: "round",
|
|
49197
|
+
className: "acp-mic-spinner",
|
|
49198
|
+
children: /* @__PURE__ */ jsx184("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
|
|
49199
|
+
}
|
|
49200
|
+
) : isRecording ? /* @__PURE__ */ jsx184(MicOff, { size: 16 }) : /* @__PURE__ */ jsx184(Mic, { size: 16 })
|
|
49201
|
+
}
|
|
49202
|
+
)
|
|
49203
|
+
}
|
|
49204
|
+
),
|
|
49205
|
+
/* @__PURE__ */ jsx184(
|
|
49206
|
+
"button",
|
|
49207
|
+
{
|
|
49208
|
+
className: "acp-send-btn",
|
|
49209
|
+
onClick: isLoading ? handleStop : () => handleSend(),
|
|
49210
|
+
disabled: !isLoading && (!prompt.trim() || isSendPending),
|
|
49211
|
+
title: isLoading ? "Stop" : "Send",
|
|
49212
|
+
children: isLoading ? /* @__PURE__ */ jsx184("span", { className: "acp-stop-square" }) : /* @__PURE__ */ jsx184(ArrowUp3, { size: 16 })
|
|
49213
|
+
}
|
|
49214
|
+
)
|
|
49215
|
+
] })
|
|
48363
49216
|
] })
|
|
48364
|
-
]
|
|
48365
|
-
|
|
48366
|
-
|
|
48367
|
-
)
|
|
48368
|
-
] })
|
|
48369
|
-
|
|
48370
|
-
|
|
48371
|
-
|
|
48372
|
-
|
|
49217
|
+
]
|
|
49218
|
+
}
|
|
49219
|
+
)
|
|
49220
|
+
] }) })
|
|
49221
|
+
] })
|
|
49222
|
+
}
|
|
49223
|
+
);
|
|
49224
|
+
}
|
|
49225
|
+
);
|
|
49226
|
+
AIChatPanel.displayName = "AIChatPanel";
|
|
48373
49227
|
function ToolActionsDisplay({ actions: actions2 }) {
|
|
48374
49228
|
const [expanded, setExpanded] = useState49(false);
|
|
48375
49229
|
return /* @__PURE__ */ jsxs100("div", { className: "acp-tool-actions", children: [
|
|
@@ -48409,7 +49263,37 @@ function ToolActionsDisplay({ actions: actions2 }) {
|
|
|
48409
49263
|
)) })
|
|
48410
49264
|
] });
|
|
48411
49265
|
}
|
|
48412
|
-
async function callPlainChatAPI(messages, apiKey, signal) {
|
|
49266
|
+
async function callPlainChatAPI(messages, apiKey, signal, opts) {
|
|
49267
|
+
const model = opts?.webSearchEnabled ? "openai/gpt-4.1-mini:online" : "openai/gpt-4.1-mini";
|
|
49268
|
+
const apiMessages = messages.map((m, i) => {
|
|
49269
|
+
const isLast = i === messages.length - 1;
|
|
49270
|
+
if (isLast && m.role === "user" && opts?.attachments?.length) {
|
|
49271
|
+
const content2 = [];
|
|
49272
|
+
for (const att of opts.attachments) {
|
|
49273
|
+
if (att.type === "image" && att.dataUrl) {
|
|
49274
|
+
content2.push({
|
|
49275
|
+
type: "image_url",
|
|
49276
|
+
image_url: { url: att.dataUrl }
|
|
49277
|
+
});
|
|
49278
|
+
}
|
|
49279
|
+
}
|
|
49280
|
+
let text = m.content;
|
|
49281
|
+
const textFiles = opts.attachments.filter((a) => a.type === "text");
|
|
49282
|
+
if (textFiles.length > 0) {
|
|
49283
|
+
text += `
|
|
49284
|
+
|
|
49285
|
+
${textFiles.map((f) => `[File: ${f.name}]
|
|
49286
|
+
${f.textContent}`).join("\n\n")}`;
|
|
49287
|
+
}
|
|
49288
|
+
content2.push({ type: "text", text });
|
|
49289
|
+
return { role: m.role, content: content2 };
|
|
49290
|
+
}
|
|
49291
|
+
return { role: m.role, content: m.content };
|
|
49292
|
+
});
|
|
49293
|
+
const body = { model, messages: apiMessages };
|
|
49294
|
+
if (opts?.webSearchEnabled) {
|
|
49295
|
+
body.plugins = [{ id: "web", max_results: 5 }];
|
|
49296
|
+
}
|
|
48413
49297
|
const response = await fetch(
|
|
48414
49298
|
"https://openrouter.ai/api/v1/chat/completions",
|
|
48415
49299
|
{
|
|
@@ -48419,10 +49303,7 @@ async function callPlainChatAPI(messages, apiKey, signal) {
|
|
|
48419
49303
|
Authorization: `Bearer ${apiKey}`,
|
|
48420
49304
|
"Content-Type": "application/json"
|
|
48421
49305
|
},
|
|
48422
|
-
body: JSON.stringify(
|
|
48423
|
-
model: "openai/gpt-4.1-mini",
|
|
48424
|
-
messages: messages.map((m) => ({ role: m.role, content: m.content }))
|
|
48425
|
-
})
|
|
49306
|
+
body: JSON.stringify(body)
|
|
48426
49307
|
}
|
|
48427
49308
|
);
|
|
48428
49309
|
if (!response.ok) {
|
|
@@ -48482,7 +49363,10 @@ var ExcalidrawBase = (props) => {
|
|
|
48482
49363
|
renderEmbeddable,
|
|
48483
49364
|
aiEnabled,
|
|
48484
49365
|
showDeprecatedFonts,
|
|
48485
|
-
|
|
49366
|
+
geminiApiKey,
|
|
49367
|
+
renderScrollbars,
|
|
49368
|
+
onBeforeImageGen,
|
|
49369
|
+
onAfterImageGen
|
|
48486
49370
|
} = props;
|
|
48487
49371
|
const canvasActions = props.UIOptions?.canvasActions;
|
|
48488
49372
|
const UIOptions = {
|
|
@@ -48554,7 +49438,10 @@ var ExcalidrawBase = (props) => {
|
|
|
48554
49438
|
renderEmbeddable,
|
|
48555
49439
|
aiEnabled: aiEnabled !== false,
|
|
48556
49440
|
showDeprecatedFonts,
|
|
49441
|
+
geminiApiKey,
|
|
48557
49442
|
renderScrollbars,
|
|
49443
|
+
onBeforeImageGen,
|
|
49444
|
+
onAfterImageGen,
|
|
48558
49445
|
children
|
|
48559
49446
|
}
|
|
48560
49447
|
) }) });
|
|
@@ -48668,7 +49555,7 @@ export {
|
|
|
48668
49555
|
useHandleLibrary,
|
|
48669
49556
|
useI18n,
|
|
48670
49557
|
useStylesPanelMode,
|
|
48671
|
-
|
|
49558
|
+
viewportCoordsToSceneCoords5 as viewportCoordsToSceneCoords,
|
|
48672
49559
|
zoomToFitBounds
|
|
48673
49560
|
};
|
|
48674
49561
|
//# sourceMappingURL=index.js.map
|