@agent-native/toolkit 0.198.6 → 0.198.7
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/app/chat/AgentPanel.d.ts +4 -3
- package/dist/app/chat/AgentPanel.d.ts.map +1 -1
- package/dist/app/chat/AgentPanel.js +14 -24
- package/dist/app/chat/AgentPanel.js.map +1 -1
- package/dist/app/chat/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/app/chat/ConnectBuilderCard.js +2 -1
- package/dist/app/chat/ConnectBuilderCard.js.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.js +21 -2
- package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
- package/dist/composer/ComposerContextMenu.d.ts +2 -2
- package/dist/composer/ComposerContextMenu.d.ts.map +1 -1
- package/dist/composer/ComposerContextMenu.js +54 -66
- package/dist/composer/ComposerContextMenu.js.map +1 -1
- package/dist/composer/ComposerPlusMenu.d.ts +2 -3
- package/dist/composer/ComposerPlusMenu.d.ts.map +1 -1
- package/dist/composer/ComposerPlusMenu.js +13 -278
- package/dist/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +3 -12
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/dist/composer/use-composer-panel-placement.d.ts +0 -1
- package/dist/composer/use-composer-panel-placement.d.ts.map +1 -1
- package/dist/composer/use-composer-panel-placement.js +4 -5
- package/dist/composer/use-composer-panel-placement.js.map +1 -1
- package/package.json +3 -3
- package/src/app/chat/AgentPanel.header.spec.ts +25 -27
- package/src/app/chat/AgentPanel.overlay.spec.tsx +14 -51
- package/src/app/chat/AgentPanel.tsx +15 -26
- package/src/app/chat/ConnectBuilderCard.spec.tsx +45 -0
- package/src/app/chat/ConnectBuilderCard.tsx +3 -2
- package/src/app/chat/MultiTabAssistantChat.spec.tsx +12 -0
- package/src/app/chat/MultiTabAssistantChat.tsx +26 -2
- package/src/app/chat/composer/use-agentkit-integration-menu.spec.tsx +4 -2
- package/src/composer/ComposerContextMenu.spec.tsx +68 -60
- package/src/composer/ComposerContextMenu.tsx +93 -110
- package/src/composer/ComposerContextPicker.spec.tsx +16 -14
- package/src/composer/ComposerContextPickerDialog.spec.tsx +8 -6
- package/src/composer/ComposerPlusMenu.parity.spec.tsx +37 -75
- package/src/composer/ComposerPlusMenu.tsx +26 -450
- package/src/composer/PromptComposer.context.spec.tsx +112 -18
- package/src/composer/TiptapComposer.spec.ts +15 -0
- package/src/composer/TiptapComposer.tsx +2 -12
- package/src/composer/use-composer-panel-placement.ts +7 -5
- package/dist/composer/asset-picker-url.d.ts +0 -10
- package/dist/composer/asset-picker-url.d.ts.map +0 -1
- package/dist/composer/asset-picker-url.js +0 -56
- package/dist/composer/asset-picker-url.js.map +0 -1
- package/src/composer/asset-picker-url.spec.ts +0 -56
- package/src/composer/asset-picker-url.ts +0 -72
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import { useComposerRuntime } from "@assistant-ui/react";
|
|
2
|
-
import { IconArrowLeft, IconLoader2
|
|
3
|
-
import
|
|
4
|
-
useCallback,
|
|
5
|
-
useEffect,
|
|
6
|
-
useId,
|
|
7
|
-
useMemo,
|
|
8
|
-
useRef,
|
|
9
|
-
useState,
|
|
10
|
-
} from "react";
|
|
11
|
-
import { createPortal } from "react-dom";
|
|
2
|
+
import { IconArrowLeft, IconLoader2 } from "@tabler/icons-react";
|
|
3
|
+
import { useCallback, useEffect, useId, useRef, useState } from "react";
|
|
12
4
|
|
|
13
5
|
import { Button } from "../ui/button.js";
|
|
14
6
|
import { DropdownMenuGroup, DropdownMenuItem } from "../ui/dropdown-menu.js";
|
|
@@ -16,11 +8,6 @@ import { Input } from "../ui/input.js";
|
|
|
16
8
|
import { Label } from "../ui/label.js";
|
|
17
9
|
import { Textarea } from "../ui/textarea.js";
|
|
18
10
|
import { cn } from "../utils.js";
|
|
19
|
-
import {
|
|
20
|
-
createAssetPickerHandoffId,
|
|
21
|
-
isExternalAssetPickerUrl,
|
|
22
|
-
standaloneAssetPickerUrl,
|
|
23
|
-
} from "./asset-picker-url.js";
|
|
24
11
|
import { formatAttachmentError } from "./attachment-accept.js";
|
|
25
12
|
import {
|
|
26
13
|
ComposerContextMenu,
|
|
@@ -51,137 +38,12 @@ interface ComposerPlusMenuProps extends Omit<
|
|
|
51
38
|
|
|
52
39
|
type View = "menu" | "skill-upload";
|
|
53
40
|
|
|
54
|
-
const DEFAULT_ASSETS_PICKER_URL = "https://assets.agent-native.com/picker";
|
|
55
|
-
const EMBED_PROTOCOL = "agent-native.embed";
|
|
56
|
-
const EMBED_VERSION = 1;
|
|
57
|
-
|
|
58
41
|
export function isExtensionComposerMenuEnabled(
|
|
59
42
|
extensionTools?: boolean,
|
|
60
43
|
): boolean {
|
|
61
44
|
return extensionTools === true;
|
|
62
45
|
}
|
|
63
46
|
|
|
64
|
-
interface EmbedEnvelope<TPayload = unknown> {
|
|
65
|
-
protocol?: string;
|
|
66
|
-
version?: number;
|
|
67
|
-
type?: string;
|
|
68
|
-
name?: string;
|
|
69
|
-
payload?: TPayload;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
interface AssetPickerPayload {
|
|
73
|
-
assetId?: unknown;
|
|
74
|
-
handoffId?: unknown;
|
|
75
|
-
url?: unknown;
|
|
76
|
-
previewUrl?: unknown;
|
|
77
|
-
downloadUrl?: unknown;
|
|
78
|
-
embedUrl?: unknown;
|
|
79
|
-
altText?: unknown;
|
|
80
|
-
title?: unknown;
|
|
81
|
-
prompt?: unknown;
|
|
82
|
-
mediaType?: unknown;
|
|
83
|
-
libraryId?: unknown;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function assetPickerUrl() {
|
|
87
|
-
const env =
|
|
88
|
-
(import.meta as ImportMeta & { env?: Record<string, string | undefined> })
|
|
89
|
-
.env ?? {};
|
|
90
|
-
return env.VITE_AGENT_NATIVE_ASSETS_PICKER_URL || DEFAULT_ASSETS_PICKER_URL;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function withEmbeddedParams(url: string): string {
|
|
94
|
-
try {
|
|
95
|
-
const parsed = new URL(url, window.location.href);
|
|
96
|
-
parsed.searchParams.set("embedded", "1");
|
|
97
|
-
parsed.searchParams.set("mediaType", "image");
|
|
98
|
-
return parsed.toString();
|
|
99
|
-
} catch {
|
|
100
|
-
const separator = url.includes("?") ? "&" : "?";
|
|
101
|
-
return `${url}${separator}embedded=1&mediaType=image`;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function assetPickerOrigin(url: string): string | null {
|
|
106
|
-
try {
|
|
107
|
-
return new URL(url, window.location.href).origin;
|
|
108
|
-
} catch {
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function embedEnvelope(
|
|
114
|
-
type: "message" | "ready",
|
|
115
|
-
options: { name?: string; payload?: unknown } = {},
|
|
116
|
-
): EmbedEnvelope {
|
|
117
|
-
return {
|
|
118
|
-
protocol: EMBED_PROTOCOL,
|
|
119
|
-
version: EMBED_VERSION,
|
|
120
|
-
type,
|
|
121
|
-
...options,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function isEmbedEnvelope(value: unknown): value is EmbedEnvelope {
|
|
126
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
127
|
-
const candidate = value as EmbedEnvelope;
|
|
128
|
-
return (
|
|
129
|
-
candidate.protocol === EMBED_PROTOCOL &&
|
|
130
|
-
candidate.version === EMBED_VERSION &&
|
|
131
|
-
typeof candidate.type === "string"
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function assetString(value: unknown): string | null {
|
|
136
|
-
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function assetImageSource(payload: unknown): string | null {
|
|
140
|
-
if (!payload || typeof payload !== "object") return null;
|
|
141
|
-
const asset = payload as AssetPickerPayload;
|
|
142
|
-
return (
|
|
143
|
-
assetString(asset.url) ??
|
|
144
|
-
assetString(asset.previewUrl) ??
|
|
145
|
-
assetString(asset.downloadUrl) ??
|
|
146
|
-
assetString(asset.embedUrl)
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function assetTitle(
|
|
151
|
-
payload: unknown,
|
|
152
|
-
url: string,
|
|
153
|
-
generatedImageLabel: string,
|
|
154
|
-
): string {
|
|
155
|
-
if (payload && typeof payload === "object") {
|
|
156
|
-
const title = assetString((payload as AssetPickerPayload).title);
|
|
157
|
-
if (title) return title;
|
|
158
|
-
const prompt = assetString((payload as AssetPickerPayload).prompt);
|
|
159
|
-
if (prompt) return prompt.slice(0, 80);
|
|
160
|
-
}
|
|
161
|
-
try {
|
|
162
|
-
const name = new URL(url).pathname.split("/").filter(Boolean).pop();
|
|
163
|
-
return name ? decodeURIComponent(name) : generatedImageLabel;
|
|
164
|
-
} catch {
|
|
165
|
-
return generatedImageLabel;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function assetContext(payload: unknown, url: string): string {
|
|
170
|
-
const lines = [`Image URL: ${url}`];
|
|
171
|
-
if (payload && typeof payload === "object") {
|
|
172
|
-
const asset = payload as AssetPickerPayload;
|
|
173
|
-
const assetId = assetString(asset.assetId);
|
|
174
|
-
const libraryId = assetString(asset.libraryId);
|
|
175
|
-
const prompt = assetString(asset.prompt);
|
|
176
|
-
const altText = assetString(asset.altText);
|
|
177
|
-
if (assetId) lines.push(`Asset ID: ${assetId}`);
|
|
178
|
-
if (libraryId) lines.push(`Library ID: ${libraryId}`);
|
|
179
|
-
if (prompt) lines.push(`Prompt: ${prompt}`);
|
|
180
|
-
if (altText) lines.push(`Alt text: ${altText}`);
|
|
181
|
-
}
|
|
182
|
-
return lines.join("\n");
|
|
183
|
-
}
|
|
184
|
-
|
|
185
47
|
function slugifyName(value: string): string {
|
|
186
48
|
return (
|
|
187
49
|
value
|
|
@@ -218,7 +80,6 @@ export function useComposerDefaultActions({
|
|
|
218
80
|
const adapters = useComposerRuntimeAdapters();
|
|
219
81
|
const t = adapters.translate!;
|
|
220
82
|
const resources = adapters.resources!;
|
|
221
|
-
const [assetsPickerOpen, setAssetsPickerOpen] = useState(false);
|
|
222
83
|
const [mcpDialogOpen, setMcpDialogOpen] = useState(false);
|
|
223
84
|
const overlayOpen = useRef(false);
|
|
224
85
|
const showMcpIntegrations = resources.isMcpIntegrationAvailable!();
|
|
@@ -253,7 +114,6 @@ export function useComposerDefaultActions({
|
|
|
253
114
|
useEffect(() => {
|
|
254
115
|
if (!disabled) return;
|
|
255
116
|
cancelSkillWork();
|
|
256
|
-
setAssetsPickerOpen(false);
|
|
257
117
|
setMcpDialogOpen(false);
|
|
258
118
|
overlayOpen.current = false;
|
|
259
119
|
}, [disabled, cancelSkillWork]);
|
|
@@ -365,21 +225,6 @@ export function useComposerDefaultActions({
|
|
|
365
225
|
};
|
|
366
226
|
|
|
367
227
|
const items: ComposerContextMenuItem[] = [
|
|
368
|
-
{
|
|
369
|
-
id: "generate-image",
|
|
370
|
-
label: t("agentChat.composer.menu.generateImage", {
|
|
371
|
-
defaultValue: "Generate Image",
|
|
372
|
-
}),
|
|
373
|
-
keywords: [
|
|
374
|
-
t("agentChat.composer.menu.generateImageDescription", {
|
|
375
|
-
defaultValue: "Open the Assets image picker",
|
|
376
|
-
}),
|
|
377
|
-
],
|
|
378
|
-
onSelect: () => {
|
|
379
|
-
overlayOpen.current = true;
|
|
380
|
-
setAssetsPickerOpen(true);
|
|
381
|
-
},
|
|
382
|
-
},
|
|
383
228
|
{
|
|
384
229
|
id: "schedule-task",
|
|
385
230
|
label: t("agentChat.composer.menu.scheduleTask", {
|
|
@@ -626,14 +471,6 @@ export function useComposerDefaultActions({
|
|
|
626
471
|
onCreateMcpServer={(args: unknown) => createMcp.mutateAsync(args)}
|
|
627
472
|
/>
|
|
628
473
|
) : null}
|
|
629
|
-
<AssetsPickerModal
|
|
630
|
-
open={assetsPickerOpen}
|
|
631
|
-
onOpenChange={(open) => {
|
|
632
|
-
overlayOpen.current = open;
|
|
633
|
-
setAssetsPickerOpen(open);
|
|
634
|
-
if (!open) onRestoreFocus?.();
|
|
635
|
-
}}
|
|
636
|
-
/>
|
|
637
474
|
</>
|
|
638
475
|
),
|
|
639
476
|
};
|
|
@@ -642,14 +479,23 @@ export function useComposerDefaultActions({
|
|
|
642
479
|
function ComposerFullContextMenu(props: ComposerPlusMenuProps) {
|
|
643
480
|
const defaults = useComposerDefaultActions(props);
|
|
644
481
|
const runtime = useComposerRuntime();
|
|
482
|
+
const mergedItems = mergeComposerMenuItems(
|
|
483
|
+
defaults.items,
|
|
484
|
+
props.contextMenuItems ?? [],
|
|
485
|
+
);
|
|
486
|
+
const defaultActionIds = new Set(defaults.items.map((item) => item.id));
|
|
487
|
+
const menuActionItems = mergedItems.filter((item) =>
|
|
488
|
+
defaultActionIds.has(item.id),
|
|
489
|
+
);
|
|
490
|
+
const contextItems = mergedItems.filter(
|
|
491
|
+
(item) => !defaultActionIds.has(item.id),
|
|
492
|
+
);
|
|
645
493
|
return (
|
|
646
494
|
<>
|
|
647
495
|
<ComposerContextMenu
|
|
648
496
|
{...props}
|
|
649
|
-
items={
|
|
650
|
-
|
|
651
|
-
props.contextMenuItems ?? [],
|
|
652
|
-
)}
|
|
497
|
+
items={contextItems}
|
|
498
|
+
menuActionItems={menuActionItems}
|
|
653
499
|
addAttachment={
|
|
654
500
|
props.attachmentsEnabled === false
|
|
655
501
|
? undefined
|
|
@@ -706,14 +552,20 @@ export function ComposerPlusMenu({
|
|
|
706
552
|
},
|
|
707
553
|
]
|
|
708
554
|
: [];
|
|
555
|
+
const mergedTerminalItems =
|
|
556
|
+
mode === "terminal"
|
|
557
|
+
? mergeComposerMenuItems(terminalItems, contextMenuItems)
|
|
558
|
+
: contextMenuItems;
|
|
559
|
+
const terminalActionIds = new Set(terminalItems.map((item) => item.id));
|
|
709
560
|
return (
|
|
710
561
|
<ComposerContextMenu
|
|
711
562
|
{...props}
|
|
712
|
-
items={
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
563
|
+
items={mergedTerminalItems.filter(
|
|
564
|
+
(item) => !terminalActionIds.has(item.id),
|
|
565
|
+
)}
|
|
566
|
+
menuActionItems={mergedTerminalItems.filter((item) =>
|
|
567
|
+
terminalActionIds.has(item.id),
|
|
568
|
+
)}
|
|
717
569
|
addAttachment={
|
|
718
570
|
mode === "upload-only" && attachmentsEnabled
|
|
719
571
|
? (props.addAttachment ?? runtime.addAttachment)
|
|
@@ -725,279 +577,3 @@ export function ComposerPlusMenu({
|
|
|
725
577
|
/>
|
|
726
578
|
);
|
|
727
579
|
}
|
|
728
|
-
|
|
729
|
-
function AssetsPickerModal({
|
|
730
|
-
open,
|
|
731
|
-
onOpenChange,
|
|
732
|
-
}: {
|
|
733
|
-
open: boolean;
|
|
734
|
-
onOpenChange: (open: boolean) => void;
|
|
735
|
-
}) {
|
|
736
|
-
const adapters = useComposerRuntimeAdapters();
|
|
737
|
-
const t = adapters.translate!;
|
|
738
|
-
const iframeRef = useRef<HTMLIFrameElement | null>(null);
|
|
739
|
-
const standaloneWindowRef = useRef<Window | null>(null);
|
|
740
|
-
const [pickerReady, setPickerReady] = useState(false);
|
|
741
|
-
const [standaloneHandoffId, setStandaloneHandoffId] = useState<string | null>(
|
|
742
|
-
null,
|
|
743
|
-
);
|
|
744
|
-
const sourceUrl = useMemo(() => assetPickerUrl(), []);
|
|
745
|
-
const externalPicker = useMemo(
|
|
746
|
-
() =>
|
|
747
|
-
typeof window !== "undefined" &&
|
|
748
|
-
isExternalAssetPickerUrl(sourceUrl, window.location.origin),
|
|
749
|
-
[sourceUrl],
|
|
750
|
-
);
|
|
751
|
-
const standaloneUrl = useMemo(
|
|
752
|
-
() =>
|
|
753
|
-
standaloneAssetPickerUrl(
|
|
754
|
-
sourceUrl,
|
|
755
|
-
typeof window !== "undefined" ? window.location.href : undefined,
|
|
756
|
-
{
|
|
757
|
-
handoffId: standaloneHandoffId ?? undefined,
|
|
758
|
-
returnOrigin:
|
|
759
|
-
typeof window !== "undefined" ? window.location.origin : undefined,
|
|
760
|
-
},
|
|
761
|
-
),
|
|
762
|
-
[sourceUrl, standaloneHandoffId],
|
|
763
|
-
);
|
|
764
|
-
const iframeUrl = useMemo(() => withEmbeddedParams(sourceUrl), [sourceUrl]);
|
|
765
|
-
const targetOrigin = useMemo(() => assetPickerOrigin(iframeUrl), [iframeUrl]);
|
|
766
|
-
const configurePicker = useCallback(() => {
|
|
767
|
-
if (!targetOrigin) return;
|
|
768
|
-
iframeRef.current?.contentWindow?.postMessage(
|
|
769
|
-
embedEnvelope("message", {
|
|
770
|
-
name: "configure",
|
|
771
|
-
payload: { mediaType: "image", count: 3 },
|
|
772
|
-
}),
|
|
773
|
-
targetOrigin,
|
|
774
|
-
);
|
|
775
|
-
}, [targetOrigin]);
|
|
776
|
-
|
|
777
|
-
useEffect(() => {
|
|
778
|
-
if (open) {
|
|
779
|
-
setPickerReady(false);
|
|
780
|
-
if (externalPicker) {
|
|
781
|
-
standaloneWindowRef.current = null;
|
|
782
|
-
setStandaloneHandoffId(createAssetPickerHandoffId());
|
|
783
|
-
} else {
|
|
784
|
-
setStandaloneHandoffId(null);
|
|
785
|
-
}
|
|
786
|
-
return;
|
|
787
|
-
}
|
|
788
|
-
if (!standaloneWindowRef.current) setStandaloneHandoffId(null);
|
|
789
|
-
}, [externalPicker, iframeUrl, open]);
|
|
790
|
-
|
|
791
|
-
useEffect(() => {
|
|
792
|
-
if (
|
|
793
|
-
!targetOrigin ||
|
|
794
|
-
(!open && !standaloneWindowRef.current) ||
|
|
795
|
-
(externalPicker && !standaloneHandoffId)
|
|
796
|
-
)
|
|
797
|
-
return;
|
|
798
|
-
|
|
799
|
-
const handleMessage = (event: MessageEvent) => {
|
|
800
|
-
const expectedSource = externalPicker
|
|
801
|
-
? standaloneWindowRef.current
|
|
802
|
-
: iframeRef.current?.contentWindow;
|
|
803
|
-
if (!expectedSource || event.source !== expectedSource) return;
|
|
804
|
-
if (event.origin !== targetOrigin) return;
|
|
805
|
-
if (!isEmbedEnvelope(event.data)) return;
|
|
806
|
-
|
|
807
|
-
if (event.data.type === "ready") {
|
|
808
|
-
setPickerReady(true);
|
|
809
|
-
configurePicker();
|
|
810
|
-
return;
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
if (event.data.type !== "message") return;
|
|
814
|
-
if (externalPicker) {
|
|
815
|
-
const payload = event.data.payload;
|
|
816
|
-
const handoffId =
|
|
817
|
-
payload && typeof payload === "object"
|
|
818
|
-
? assetString((payload as AssetPickerPayload).handoffId)
|
|
819
|
-
: null;
|
|
820
|
-
if (handoffId !== standaloneHandoffId) return;
|
|
821
|
-
}
|
|
822
|
-
if (event.data.name === "close") {
|
|
823
|
-
onOpenChange(false);
|
|
824
|
-
return;
|
|
825
|
-
}
|
|
826
|
-
if (
|
|
827
|
-
event.data.name !== "chooseImage" &&
|
|
828
|
-
event.data.name !== "chooseAsset"
|
|
829
|
-
)
|
|
830
|
-
return;
|
|
831
|
-
|
|
832
|
-
const url = assetImageSource(event.data.payload);
|
|
833
|
-
if (!url) return;
|
|
834
|
-
const title = assetTitle(
|
|
835
|
-
event.data.payload,
|
|
836
|
-
url,
|
|
837
|
-
t("agentChat.composer.assets.generatedImage", {
|
|
838
|
-
defaultValue: "Generated image",
|
|
839
|
-
}),
|
|
840
|
-
);
|
|
841
|
-
const assetId =
|
|
842
|
-
event.data.payload && typeof event.data.payload === "object"
|
|
843
|
-
? assetString((event.data.payload as AssetPickerPayload).assetId)
|
|
844
|
-
: null;
|
|
845
|
-
adapters.agentChat!.setContextItem!({
|
|
846
|
-
key: `asset-image:${assetId ?? url}`,
|
|
847
|
-
title: t("agentChat.composer.assets.contextTitle", {
|
|
848
|
-
title,
|
|
849
|
-
defaultValue: `Image: ${title}`,
|
|
850
|
-
}),
|
|
851
|
-
context: assetContext(event.data.payload, url),
|
|
852
|
-
});
|
|
853
|
-
standaloneWindowRef.current = null;
|
|
854
|
-
setStandaloneHandoffId(null);
|
|
855
|
-
onOpenChange(false);
|
|
856
|
-
};
|
|
857
|
-
|
|
858
|
-
const handleKey = (event: KeyboardEvent) => {
|
|
859
|
-
if (event.key === "Escape") onOpenChange(false);
|
|
860
|
-
};
|
|
861
|
-
|
|
862
|
-
window.addEventListener("message", handleMessage);
|
|
863
|
-
window.addEventListener("keydown", handleKey);
|
|
864
|
-
return () => {
|
|
865
|
-
window.removeEventListener("message", handleMessage);
|
|
866
|
-
window.removeEventListener("keydown", handleKey);
|
|
867
|
-
};
|
|
868
|
-
}, [
|
|
869
|
-
configurePicker,
|
|
870
|
-
adapters,
|
|
871
|
-
externalPicker,
|
|
872
|
-
onOpenChange,
|
|
873
|
-
open,
|
|
874
|
-
standaloneHandoffId,
|
|
875
|
-
t,
|
|
876
|
-
targetOrigin,
|
|
877
|
-
]);
|
|
878
|
-
|
|
879
|
-
const openStandalonePicker = useCallback(
|
|
880
|
-
(event: React.MouseEvent<HTMLAnchorElement>) => {
|
|
881
|
-
if (!standaloneHandoffId) return;
|
|
882
|
-
event.preventDefault();
|
|
883
|
-
const pickerWindow = window.open(standaloneUrl, "_blank");
|
|
884
|
-
if (!pickerWindow) return;
|
|
885
|
-
standaloneWindowRef.current = pickerWindow;
|
|
886
|
-
onOpenChange(false);
|
|
887
|
-
},
|
|
888
|
-
[onOpenChange, standaloneHandoffId, standaloneUrl],
|
|
889
|
-
);
|
|
890
|
-
|
|
891
|
-
if (!open || typeof document === "undefined") return null;
|
|
892
|
-
|
|
893
|
-
return createPortal(
|
|
894
|
-
<div
|
|
895
|
-
className="fixed inset-0 z-[280] flex items-center justify-center bg-black/50 p-3"
|
|
896
|
-
role="dialog"
|
|
897
|
-
aria-modal="true"
|
|
898
|
-
aria-labelledby="composer-assets-picker-title"
|
|
899
|
-
onMouseDown={(event) => {
|
|
900
|
-
if (event.target === event.currentTarget) onOpenChange(false);
|
|
901
|
-
}}
|
|
902
|
-
>
|
|
903
|
-
<div className="flex h-[min(86vh,760px)] w-[min(96vw,1040px)] flex-col overflow-hidden rounded-xl border border-border bg-background shadow-2xl">
|
|
904
|
-
<div className="flex h-12 shrink-0 items-center justify-between border-b border-border px-4">
|
|
905
|
-
<div
|
|
906
|
-
id="composer-assets-picker-title"
|
|
907
|
-
className="text-sm font-medium text-foreground"
|
|
908
|
-
>
|
|
909
|
-
{t("agentChat.composer.assets.generateImage", {
|
|
910
|
-
defaultValue: "Generate image",
|
|
911
|
-
})}
|
|
912
|
-
</div>
|
|
913
|
-
<button
|
|
914
|
-
type="button"
|
|
915
|
-
onClick={() => onOpenChange(false)}
|
|
916
|
-
className="flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
917
|
-
aria-label={t("agentChat.composer.assets.closePicker", {
|
|
918
|
-
defaultValue: "Close image picker",
|
|
919
|
-
})}
|
|
920
|
-
>
|
|
921
|
-
<IconX className="h-4 w-4" />
|
|
922
|
-
</button>
|
|
923
|
-
</div>
|
|
924
|
-
{externalPicker ? (
|
|
925
|
-
<div className="flex min-h-0 flex-1 flex-col items-center justify-center gap-3 p-8 text-center">
|
|
926
|
-
<div className="max-w-md text-sm text-muted-foreground">
|
|
927
|
-
{t("agentChat.composer.assets.openSecurely", {
|
|
928
|
-
defaultValue:
|
|
929
|
-
"Open Assets in a new tab to sign in and choose an image securely.",
|
|
930
|
-
})}
|
|
931
|
-
</div>
|
|
932
|
-
<a
|
|
933
|
-
href={standaloneUrl}
|
|
934
|
-
target="_blank"
|
|
935
|
-
onClick={openStandalonePicker}
|
|
936
|
-
className="inline-flex h-9 items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground hover:bg-primary/90"
|
|
937
|
-
>
|
|
938
|
-
{t("agentChat.composer.assets.openPicker", {
|
|
939
|
-
defaultValue: "Open Assets image picker",
|
|
940
|
-
})}
|
|
941
|
-
</a>
|
|
942
|
-
</div>
|
|
943
|
-
) : targetOrigin ? (
|
|
944
|
-
<div className="relative min-h-0 flex-1 overflow-hidden bg-background">
|
|
945
|
-
{!pickerReady && <AssetsPickerLoadingSkeleton />}
|
|
946
|
-
<iframe
|
|
947
|
-
ref={iframeRef}
|
|
948
|
-
src={iframeUrl}
|
|
949
|
-
title={t("agentChat.composer.assets.pickerTitle", {
|
|
950
|
-
defaultValue: "Assets image picker",
|
|
951
|
-
})}
|
|
952
|
-
className={cn(
|
|
953
|
-
"absolute inset-0 h-full w-full border-0 bg-background transition-opacity duration-150",
|
|
954
|
-
pickerReady ? "opacity-100" : "pointer-events-none opacity-0",
|
|
955
|
-
)}
|
|
956
|
-
sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-downloads"
|
|
957
|
-
allow="clipboard-read; clipboard-write; microphone; fullscreen"
|
|
958
|
-
referrerPolicy="strict-origin-when-cross-origin"
|
|
959
|
-
onLoad={() => {
|
|
960
|
-
configurePicker();
|
|
961
|
-
setPickerReady(true);
|
|
962
|
-
}}
|
|
963
|
-
/>
|
|
964
|
-
</div>
|
|
965
|
-
) : (
|
|
966
|
-
<div className="flex min-h-0 flex-1 items-center justify-center p-8 text-center text-sm text-muted-foreground">
|
|
967
|
-
{t("agentChat.composer.assets.invalidUrl", {
|
|
968
|
-
defaultValue: "The configured image picker URL is not valid.",
|
|
969
|
-
})}
|
|
970
|
-
</div>
|
|
971
|
-
)}
|
|
972
|
-
</div>
|
|
973
|
-
</div>,
|
|
974
|
-
document.body,
|
|
975
|
-
);
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
function AssetsPickerLoadingSkeleton() {
|
|
979
|
-
const t = useComposerRuntimeAdapters().translate!;
|
|
980
|
-
return (
|
|
981
|
-
<div
|
|
982
|
-
className="absolute inset-0 flex flex-col gap-5 p-5"
|
|
983
|
-
role="status"
|
|
984
|
-
aria-label={t("agentChat.composer.assets.loadingPicker", {
|
|
985
|
-
defaultValue: "Loading Assets picker",
|
|
986
|
-
})}
|
|
987
|
-
>
|
|
988
|
-
<div className="flex items-center gap-3">
|
|
989
|
-
<div className="h-9 flex-1 animate-pulse rounded-md bg-muted" />
|
|
990
|
-
<div className="h-9 w-24 animate-pulse rounded-md bg-muted" />
|
|
991
|
-
</div>
|
|
992
|
-
<div className="grid min-h-0 flex-1 grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4">
|
|
993
|
-
{Array.from({ length: 8 }).map((_, index) => (
|
|
994
|
-
<div key={index} className="flex min-w-0 flex-col gap-2">
|
|
995
|
-
<div className="aspect-square w-full animate-pulse rounded-lg bg-muted" />
|
|
996
|
-
<div className="h-3 w-3/4 animate-pulse rounded bg-muted" />
|
|
997
|
-
<div className="h-3 w-1/2 animate-pulse rounded bg-muted" />
|
|
998
|
-
</div>
|
|
999
|
-
))}
|
|
1000
|
-
</div>
|
|
1001
|
-
</div>
|
|
1002
|
-
);
|
|
1003
|
-
}
|