@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
|
@@ -96,8 +96,9 @@ describe("controlled composer context", () => {
|
|
|
96
96
|
),
|
|
97
97
|
);
|
|
98
98
|
const menu = document.querySelector('[role="menu"]')!;
|
|
99
|
-
expect(menu.querySelector('[role="searchbox"]')).
|
|
99
|
+
expect(menu.querySelector('[role="searchbox"]')).toBeNull();
|
|
100
100
|
expect(menu.textContent).toContain("Upload File");
|
|
101
|
+
expect(menu.textContent).toContain("Add context");
|
|
101
102
|
await act(async () =>
|
|
102
103
|
document.dispatchEvent(
|
|
103
104
|
new KeyboardEvent("keydown", { key: "Escape", bubbles: true }),
|
|
@@ -129,18 +130,39 @@ describe("controlled composer context", () => {
|
|
|
129
130
|
|
|
130
131
|
const mentionMenu = document.querySelector<HTMLElement>('[role="menu"]');
|
|
131
132
|
expect(mentionMenu?.textContent).toContain("Upload File");
|
|
132
|
-
expect(mentionMenu?.textContent).toContain("
|
|
133
|
+
expect(mentionMenu?.textContent).toContain("Add context");
|
|
134
|
+
expect(mentionMenu?.textContent).not.toContain("Choose source");
|
|
133
135
|
expect(editor.textContent).toBe("");
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
const openContext = async () => {
|
|
137
|
+
const trigger = Array.from(
|
|
138
|
+
document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
|
|
139
|
+
).find((item) => item.textContent === "Add context")!;
|
|
140
|
+
await act(async () => {
|
|
141
|
+
trigger.focus();
|
|
142
|
+
trigger.dispatchEvent(
|
|
143
|
+
new KeyboardEvent("keydown", {
|
|
144
|
+
key: "ArrowRight",
|
|
145
|
+
bubbles: true,
|
|
146
|
+
cancelable: true,
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
const nestedItems = () =>
|
|
152
|
+
Array.from(
|
|
153
|
+
Array.from(document.querySelectorAll<HTMLElement>('[role="menu"]'))
|
|
154
|
+
.at(-1)!
|
|
155
|
+
.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
|
|
156
|
+
).map((item) => item.textContent);
|
|
157
|
+
await openContext();
|
|
158
|
+
const mentionOptions = nestedItems();
|
|
137
159
|
|
|
138
160
|
const plusButton = container.querySelector<HTMLButtonElement>(
|
|
139
161
|
'button[aria-label="Add context"]',
|
|
140
162
|
)!;
|
|
141
163
|
await act(async () =>
|
|
142
|
-
|
|
143
|
-
.
|
|
164
|
+
Array.from(document.querySelectorAll<HTMLElement>('[role^="menuitem"]'))
|
|
165
|
+
.find((item) => item.textContent === "Choose source")!
|
|
144
166
|
.dispatchEvent(
|
|
145
167
|
new KeyboardEvent("keydown", {
|
|
146
168
|
key: "Escape",
|
|
@@ -149,6 +171,11 @@ describe("controlled composer context", () => {
|
|
|
149
171
|
}),
|
|
150
172
|
),
|
|
151
173
|
);
|
|
174
|
+
await act(async () =>
|
|
175
|
+
document.dispatchEvent(
|
|
176
|
+
new KeyboardEvent("keydown", { key: "Escape", bubbles: true }),
|
|
177
|
+
),
|
|
178
|
+
);
|
|
152
179
|
expect(document.querySelector('[role="menu"]')).toBeNull();
|
|
153
180
|
await act(async () =>
|
|
154
181
|
plusButton.dispatchEvent(
|
|
@@ -161,13 +188,11 @@ describe("controlled composer context", () => {
|
|
|
161
188
|
);
|
|
162
189
|
|
|
163
190
|
const plusMenu = document.querySelector<HTMLElement>('[role="menu"]');
|
|
164
|
-
expect(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
).map((item) => item.textContent),
|
|
168
|
-
).toEqual(mentionOptions);
|
|
191
|
+
expect(plusMenu?.textContent).toContain("Add context");
|
|
192
|
+
await openContext();
|
|
193
|
+
expect(nestedItems()).toEqual(mentionOptions);
|
|
169
194
|
const sourceAction = Array.from(
|
|
170
|
-
|
|
195
|
+
document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
|
|
171
196
|
).find((item) => item.textContent === "Choose source")!;
|
|
172
197
|
await act(async () => sourceAction.click());
|
|
173
198
|
expect(onSelect).toHaveBeenCalledOnce();
|
|
@@ -205,9 +230,22 @@ describe("controlled composer context", () => {
|
|
|
205
230
|
}),
|
|
206
231
|
);
|
|
207
232
|
});
|
|
208
|
-
const
|
|
209
|
-
'[role
|
|
210
|
-
)!;
|
|
233
|
+
const addContext = Array.from(
|
|
234
|
+
document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
|
|
235
|
+
).find((item) => item.textContent === "Add context")!;
|
|
236
|
+
await act(async () => {
|
|
237
|
+
addContext.focus();
|
|
238
|
+
addContext.dispatchEvent(
|
|
239
|
+
new KeyboardEvent("keydown", {
|
|
240
|
+
key: "ArrowRight",
|
|
241
|
+
bubbles: true,
|
|
242
|
+
cancelable: true,
|
|
243
|
+
}),
|
|
244
|
+
);
|
|
245
|
+
});
|
|
246
|
+
const integrations = Array.from(
|
|
247
|
+
document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
|
|
248
|
+
).find((item) => item.textContent === "Integrations")!;
|
|
211
249
|
expect(integrations.textContent).toBe("Integrations");
|
|
212
250
|
await act(async () => {
|
|
213
251
|
integrations.focus();
|
|
@@ -268,6 +306,36 @@ describe("controlled composer context", () => {
|
|
|
268
306
|
}),
|
|
269
307
|
);
|
|
270
308
|
});
|
|
309
|
+
const addContext = Array.from(
|
|
310
|
+
document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
|
|
311
|
+
).find((item) => item.textContent === "Add context");
|
|
312
|
+
if (addContext) {
|
|
313
|
+
await act(async () => {
|
|
314
|
+
addContext.focus();
|
|
315
|
+
addContext.dispatchEvent(
|
|
316
|
+
new KeyboardEvent("keydown", {
|
|
317
|
+
key: "ArrowRight",
|
|
318
|
+
bubbles: true,
|
|
319
|
+
cancelable: true,
|
|
320
|
+
}),
|
|
321
|
+
);
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
const section = Array.from(
|
|
325
|
+
document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
|
|
326
|
+
).find((item) => item.textContent === "Connected Agents");
|
|
327
|
+
if (section) {
|
|
328
|
+
await act(async () => {
|
|
329
|
+
section.focus();
|
|
330
|
+
section.dispatchEvent(
|
|
331
|
+
new KeyboardEvent("keydown", {
|
|
332
|
+
key: "ArrowRight",
|
|
333
|
+
bubbles: true,
|
|
334
|
+
cancelable: true,
|
|
335
|
+
}),
|
|
336
|
+
);
|
|
337
|
+
});
|
|
338
|
+
}
|
|
271
339
|
const row = Array.from(
|
|
272
340
|
document.querySelectorAll<HTMLElement>('[role="menuitem"]'),
|
|
273
341
|
).find(
|
|
@@ -398,15 +466,28 @@ describe("controlled composer context", () => {
|
|
|
398
466
|
'[data-agent-native-composer-popover="true"]',
|
|
399
467
|
)!;
|
|
400
468
|
expect(panel).not.toBeNull();
|
|
401
|
-
|
|
469
|
+
if (plusMenu) {
|
|
470
|
+
expect(panel.classList.contains("w-64")).toBe(true);
|
|
471
|
+
expect(panel.style.width).toBe("");
|
|
472
|
+
} else {
|
|
473
|
+
expect(panel.style.width).toBe("700px");
|
|
474
|
+
}
|
|
402
475
|
const observation = observations.find(({ target }) => target === frame)!;
|
|
403
476
|
expect(observation).toBeDefined();
|
|
404
477
|
for (const width of [324, 280, 700]) {
|
|
405
478
|
bounds = { ...bounds, width };
|
|
406
479
|
await act(async () => observation.resize());
|
|
407
|
-
|
|
480
|
+
if (plusMenu) {
|
|
481
|
+
expect(panel.style.width).toBe("");
|
|
482
|
+
expect(panel.classList.contains("w-64")).toBe(true);
|
|
483
|
+
} else {
|
|
484
|
+
expect(panel.style.width).toBe(`${width}px`);
|
|
485
|
+
}
|
|
408
486
|
if (!plusMenu) expect(panel.style.left).toBe("100px");
|
|
409
487
|
}
|
|
488
|
+
bounds = { ...bounds, y: window.innerHeight + 500 };
|
|
489
|
+
await act(async () => observation.resize());
|
|
490
|
+
if (plusMenu) expect(panel.style.maxHeight).toBe("280px");
|
|
410
491
|
bounds = { ...bounds, x: 120, y: 160 };
|
|
411
492
|
await act(async () => window.dispatchEvent(new Event("scroll")));
|
|
412
493
|
if (!plusMenu) {
|
|
@@ -785,6 +866,19 @@ describe("controlled composer context", () => {
|
|
|
785
866
|
new KeyboardEvent("keydown", { key: "ArrowDown", bubbles: true }),
|
|
786
867
|
),
|
|
787
868
|
);
|
|
869
|
+
const addContext = Array.from(
|
|
870
|
+
document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
|
|
871
|
+
).find((element) => element.textContent === "Add context")!;
|
|
872
|
+
await act(async () => {
|
|
873
|
+
addContext.focus();
|
|
874
|
+
addContext.dispatchEvent(
|
|
875
|
+
new KeyboardEvent("keydown", {
|
|
876
|
+
key: "ArrowRight",
|
|
877
|
+
bubbles: true,
|
|
878
|
+
cancelable: true,
|
|
879
|
+
}),
|
|
880
|
+
);
|
|
881
|
+
});
|
|
788
882
|
const option = Array.from(
|
|
789
883
|
document.querySelectorAll<HTMLElement>('[role="menuitem"]'),
|
|
790
884
|
).find((element) => element.textContent?.includes("Choose brief"))!;
|
|
@@ -356,6 +356,17 @@ describe("createTiptapComposerExtensions", () => {
|
|
|
356
356
|
}),
|
|
357
357
|
);
|
|
358
358
|
});
|
|
359
|
+
act(() => {
|
|
360
|
+
Array.from(document.querySelectorAll<HTMLElement>('[role="menuitem"]'))
|
|
361
|
+
.find((button) => button.textContent?.trim() === "Add context")
|
|
362
|
+
?.dispatchEvent(
|
|
363
|
+
new KeyboardEvent("keydown", {
|
|
364
|
+
key: "ArrowRight",
|
|
365
|
+
bubbles: true,
|
|
366
|
+
cancelable: true,
|
|
367
|
+
}),
|
|
368
|
+
);
|
|
369
|
+
});
|
|
359
370
|
act(() => {
|
|
360
371
|
Array.from(document.querySelectorAll<HTMLElement>('[role="menuitem"]'))
|
|
361
372
|
.find((button) => button.textContent?.trim() === "Schedule Task")
|
|
@@ -502,6 +513,10 @@ describe("createTiptapComposerExtensions", () => {
|
|
|
502
513
|
);
|
|
503
514
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
504
515
|
});
|
|
516
|
+
const addContextItem = Array.from(
|
|
517
|
+
document.querySelectorAll<HTMLElement>('[role="menuitem"]'),
|
|
518
|
+
).find((item) => item.textContent?.trim() === "Add context");
|
|
519
|
+
await act(async () => addContextItem?.click());
|
|
505
520
|
const clickMenuItem = async (label: string) => {
|
|
506
521
|
const item = Array.from(
|
|
507
522
|
document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
|
|
@@ -2634,7 +2634,6 @@ export function TiptapComposer({
|
|
|
2634
2634
|
});
|
|
2635
2635
|
const [popover, setPopover] = useState<PopoverState>(null);
|
|
2636
2636
|
const [contextMenuOpen, setContextMenuOpen] = useState(false);
|
|
2637
|
-
const [contextMenuSearch, setContextMenuSearch] = useState("");
|
|
2638
2637
|
const popoverRef = useRef<MentionPopoverRef>(null);
|
|
2639
2638
|
const composerRuntime = useComposerRuntime();
|
|
2640
2639
|
const lastComposerRuntimeSyncRef = useRef<{
|
|
@@ -2748,18 +2747,10 @@ export function TiptapComposer({
|
|
|
2748
2747
|
error: mentionsError,
|
|
2749
2748
|
retry: retryMentions,
|
|
2750
2749
|
} = useMentionSearch(
|
|
2751
|
-
|
|
2752
|
-
? contextMenuSearch
|
|
2753
|
-
: popover?.type === "@"
|
|
2754
|
-
? popover.query
|
|
2755
|
-
: "",
|
|
2750
|
+
popover?.type === "@" ? popover.query : "",
|
|
2756
2751
|
includeDefaultMentionSearch && (contextMenuOpen || popover?.type === "@"),
|
|
2757
2752
|
);
|
|
2758
|
-
const mentionQuery =
|
|
2759
|
-
? contextMenuSearch
|
|
2760
|
-
: popover?.type === "@"
|
|
2761
|
-
? popover.query
|
|
2762
|
-
: "";
|
|
2753
|
+
const mentionQuery = popover?.type === "@" ? popover.query : "";
|
|
2763
2754
|
const filteredMentionItems = useMemo(
|
|
2764
2755
|
() =>
|
|
2765
2756
|
filterMentionItemsForSlots(
|
|
@@ -5302,7 +5293,6 @@ export function TiptapComposer({
|
|
|
5302
5293
|
attachmentsEnabled={attachmentsEnabled}
|
|
5303
5294
|
open={contextMenuOpen}
|
|
5304
5295
|
onOpenChange={setContextMenuOpen}
|
|
5305
|
-
onSearchChange={setContextMenuSearch}
|
|
5306
5296
|
loading={mentionsLoading}
|
|
5307
5297
|
error={
|
|
5308
5298
|
mentionsError
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { useLayoutEffect, useState, type RefObject } from "react";
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// Keep the compact menu's actions reachable in short viewports.
|
|
4
4
|
const MIN_USABLE_PANEL_HEIGHT = 120;
|
|
5
|
+
const MAX_COMPOSER_PANEL_HEIGHT = 280;
|
|
5
6
|
|
|
6
7
|
export function useComposerPanelPlacement(
|
|
7
8
|
triggerRef: RefObject<HTMLElement | null>,
|
|
8
9
|
open: boolean,
|
|
9
10
|
) {
|
|
10
11
|
const [placement, setPlacement] = useState({
|
|
11
|
-
|
|
12
|
-
maxHeight: 440,
|
|
12
|
+
maxHeight: MAX_COMPOSER_PANEL_HEIGHT,
|
|
13
13
|
side: "top" as "top" | "bottom",
|
|
14
14
|
sideOffset: 8,
|
|
15
15
|
alignOffset: 0,
|
|
@@ -37,10 +37,12 @@ export function useComposerPanelPlacement(
|
|
|
37
37
|
? "bottom"
|
|
38
38
|
: "top";
|
|
39
39
|
setPlacement({
|
|
40
|
-
width: bounds.width,
|
|
41
40
|
direction: rtl ? "rtl" : "ltr",
|
|
42
41
|
side,
|
|
43
|
-
maxHeight: Math.min(
|
|
42
|
+
maxHeight: Math.min(
|
|
43
|
+
MAX_COMPOSER_PANEL_HEIGHT,
|
|
44
|
+
side === "top" ? spaceAbove : spaceBelow,
|
|
45
|
+
),
|
|
44
46
|
sideOffset:
|
|
45
47
|
side === "top"
|
|
46
48
|
? button.top - bounds.top + 8
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export declare const ASSET_PICKER_HANDOFF_PARAM = "__an_asset_picker_handoff";
|
|
2
|
-
export declare const ASSET_PICKER_RETURN_ORIGIN_PARAM = "__an_asset_picker_return_origin";
|
|
3
|
-
export interface StandaloneAssetPickerOptions {
|
|
4
|
-
handoffId?: string;
|
|
5
|
-
returnOrigin?: string;
|
|
6
|
-
}
|
|
7
|
-
export declare function createAssetPickerHandoffId(): string;
|
|
8
|
-
export declare function isExternalAssetPickerUrl(value: string, currentOrigin: string): boolean;
|
|
9
|
-
export declare function standaloneAssetPickerUrl(value: string, baseUrl?: string, options?: StandaloneAssetPickerOptions): string;
|
|
10
|
-
//# sourceMappingURL=asset-picker-url.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"asset-picker-url.d.ts","sourceRoot":"","sources":["../../src/composer/asset-picker-url.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,0BAA0B,8BAA8B,CAAC;AACtE,eAAO,MAAM,gCAAgC,oCACV,CAAC;AAEpC,MAAM,WAAW,4BAA4B;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,0BAA0B,IAAI,MAAM,CAWnD;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,GACpB,OAAO,CAMT;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,OAAO,SAAoB,EAC3B,OAAO,GAAE,4BAAiC,GACzC,MAAM,CAgCR"}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
const FALLBACK_BASE_URL = "http://agent-native.invalid";
|
|
2
|
-
export const ASSET_PICKER_HANDOFF_PARAM = "__an_asset_picker_handoff";
|
|
3
|
-
export const ASSET_PICKER_RETURN_ORIGIN_PARAM = "__an_asset_picker_return_origin";
|
|
4
|
-
export function createAssetPickerHandoffId() {
|
|
5
|
-
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
6
|
-
return crypto.randomUUID();
|
|
7
|
-
}
|
|
8
|
-
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
9
|
-
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
10
|
-
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
11
|
-
}
|
|
12
|
-
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
13
|
-
}
|
|
14
|
-
export function isExternalAssetPickerUrl(value, currentOrigin) {
|
|
15
|
-
try {
|
|
16
|
-
return new URL(value, currentOrigin).origin !== currentOrigin;
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
export function standaloneAssetPickerUrl(value, baseUrl = FALLBACK_BASE_URL, options = {}) {
|
|
23
|
-
try {
|
|
24
|
-
const parsed = new URL(value, baseUrl);
|
|
25
|
-
parsed.searchParams.delete("embedded");
|
|
26
|
-
parsed.searchParams.delete("__an_embed_token");
|
|
27
|
-
parsed.searchParams.set("mediaType", "image");
|
|
28
|
-
let returnOrigin = null;
|
|
29
|
-
if (options.returnOrigin) {
|
|
30
|
-
try {
|
|
31
|
-
const parsedOrigin = new URL(options.returnOrigin);
|
|
32
|
-
if (parsedOrigin.protocol === "http:" ||
|
|
33
|
-
parsedOrigin.protocol === "https:") {
|
|
34
|
-
returnOrigin = parsedOrigin.origin;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
// Omit an invalid return target rather than emitting an unverified
|
|
39
|
-
// cross-origin callback URL.
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
if (options.handoffId && returnOrigin) {
|
|
43
|
-
parsed.searchParams.set(ASSET_PICKER_HANDOFF_PARAM, options.handoffId);
|
|
44
|
-
parsed.searchParams.set(ASSET_PICKER_RETURN_ORIGIN_PARAM, returnOrigin);
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
parsed.searchParams.delete(ASSET_PICKER_HANDOFF_PARAM);
|
|
48
|
-
parsed.searchParams.delete(ASSET_PICKER_RETURN_ORIGIN_PARAM);
|
|
49
|
-
}
|
|
50
|
-
return parsed.toString();
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
return value;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
//# sourceMappingURL=asset-picker-url.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"asset-picker-url.js","sourceRoot":"","sources":["../../src/composer/asset-picker-url.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
isExternalAssetPickerUrl,
|
|
5
|
-
standaloneAssetPickerUrl,
|
|
6
|
-
} from "./asset-picker-url.js";
|
|
7
|
-
|
|
8
|
-
describe("asset picker auth handoff", () => {
|
|
9
|
-
it("treats the hosted Assets picker as external to the host app", () => {
|
|
10
|
-
expect(
|
|
11
|
-
isExternalAssetPickerUrl(
|
|
12
|
-
"https://assets.agent-native.com/picker",
|
|
13
|
-
"https://clips.agent-native.com",
|
|
14
|
-
),
|
|
15
|
-
).toBe(true);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("keeps same-origin pickers eligible for inline rendering", () => {
|
|
19
|
-
expect(
|
|
20
|
-
isExternalAssetPickerUrl("/picker", "https://clips.agent-native.com"),
|
|
21
|
-
).toBe(false);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("removes iframe flags from the top-level fallback URL", () => {
|
|
25
|
-
expect(
|
|
26
|
-
standaloneAssetPickerUrl(
|
|
27
|
-
"https://assets.agent-native.com/picker?embedded=1&__an_embed_token=fake-token",
|
|
28
|
-
),
|
|
29
|
-
).toBe("https://assets.agent-native.com/picker?mediaType=image");
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("adds a nonce-bound exact-origin callback to the top-level URL", () => {
|
|
33
|
-
expect(
|
|
34
|
-
standaloneAssetPickerUrl(
|
|
35
|
-
"https://assets.agent-native.com/picker?embedded=1",
|
|
36
|
-
"https://clips.agent-native.com",
|
|
37
|
-
{
|
|
38
|
-
handoffId: "handoff-123",
|
|
39
|
-
returnOrigin: "https://clips.agent-native.com/chat",
|
|
40
|
-
},
|
|
41
|
-
),
|
|
42
|
-
).toBe(
|
|
43
|
-
"https://assets.agent-native.com/picker?mediaType=image&__an_asset_picker_handoff=handoff-123&__an_asset_picker_return_origin=https%3A%2F%2Fclips.agent-native.com",
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("omits an invalid callback target", () => {
|
|
48
|
-
expect(
|
|
49
|
-
standaloneAssetPickerUrl(
|
|
50
|
-
"https://assets.agent-native.com/picker",
|
|
51
|
-
"https://clips.agent-native.com",
|
|
52
|
-
{ handoffId: "handoff-123", returnOrigin: "javascript:alert(1)" },
|
|
53
|
-
),
|
|
54
|
-
).toBe("https://assets.agent-native.com/picker?mediaType=image");
|
|
55
|
-
});
|
|
56
|
-
});
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
const FALLBACK_BASE_URL = "http://agent-native.invalid";
|
|
2
|
-
|
|
3
|
-
export const ASSET_PICKER_HANDOFF_PARAM = "__an_asset_picker_handoff";
|
|
4
|
-
export const ASSET_PICKER_RETURN_ORIGIN_PARAM =
|
|
5
|
-
"__an_asset_picker_return_origin";
|
|
6
|
-
|
|
7
|
-
export interface StandaloneAssetPickerOptions {
|
|
8
|
-
handoffId?: string;
|
|
9
|
-
returnOrigin?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function createAssetPickerHandoffId(): string {
|
|
13
|
-
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
14
|
-
return crypto.randomUUID();
|
|
15
|
-
}
|
|
16
|
-
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
17
|
-
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
18
|
-
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join(
|
|
19
|
-
"",
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function isExternalAssetPickerUrl(
|
|
26
|
-
value: string,
|
|
27
|
-
currentOrigin: string,
|
|
28
|
-
): boolean {
|
|
29
|
-
try {
|
|
30
|
-
return new URL(value, currentOrigin).origin !== currentOrigin;
|
|
31
|
-
} catch {
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function standaloneAssetPickerUrl(
|
|
37
|
-
value: string,
|
|
38
|
-
baseUrl = FALLBACK_BASE_URL,
|
|
39
|
-
options: StandaloneAssetPickerOptions = {},
|
|
40
|
-
): string {
|
|
41
|
-
try {
|
|
42
|
-
const parsed = new URL(value, baseUrl);
|
|
43
|
-
parsed.searchParams.delete("embedded");
|
|
44
|
-
parsed.searchParams.delete("__an_embed_token");
|
|
45
|
-
parsed.searchParams.set("mediaType", "image");
|
|
46
|
-
let returnOrigin: string | null = null;
|
|
47
|
-
if (options.returnOrigin) {
|
|
48
|
-
try {
|
|
49
|
-
const parsedOrigin = new URL(options.returnOrigin);
|
|
50
|
-
if (
|
|
51
|
-
parsedOrigin.protocol === "http:" ||
|
|
52
|
-
parsedOrigin.protocol === "https:"
|
|
53
|
-
) {
|
|
54
|
-
returnOrigin = parsedOrigin.origin;
|
|
55
|
-
}
|
|
56
|
-
} catch {
|
|
57
|
-
// Omit an invalid return target rather than emitting an unverified
|
|
58
|
-
// cross-origin callback URL.
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (options.handoffId && returnOrigin) {
|
|
62
|
-
parsed.searchParams.set(ASSET_PICKER_HANDOFF_PARAM, options.handoffId);
|
|
63
|
-
parsed.searchParams.set(ASSET_PICKER_RETURN_ORIGIN_PARAM, returnOrigin);
|
|
64
|
-
} else {
|
|
65
|
-
parsed.searchParams.delete(ASSET_PICKER_HANDOFF_PARAM);
|
|
66
|
-
parsed.searchParams.delete(ASSET_PICKER_RETURN_ORIGIN_PARAM);
|
|
67
|
-
}
|
|
68
|
-
return parsed.toString();
|
|
69
|
-
} catch {
|
|
70
|
-
return value;
|
|
71
|
-
}
|
|
72
|
-
}
|