@agent-native/toolkit 0.225.2-nightly-20261106152255 → 0.226.0
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/agent-native.eject.json +1 -0
- package/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +124 -9
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/agentkit/react/context.d.ts +1 -1
- package/dist/app/agentkit/react/core-file-storage.d.ts +21 -0
- package/dist/app/agentkit/react/core-file-storage.d.ts.map +1 -0
- package/dist/app/agentkit/react/core-file-storage.js +3 -0
- package/dist/app/agentkit/react/core-file-storage.js.map +1 -0
- package/dist/app/auth/AuthPage.d.ts.map +1 -1
- package/dist/app/auth/AuthPage.js +89 -10
- package/dist/app/auth/AuthPage.js.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +39 -58
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.js +3 -1
- package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
- package/dist/app/chat/agentkit-chat/root.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/root.js +66 -2
- package/dist/app/chat/agentkit-chat/root.js.map +1 -1
- package/dist/app/chat/chat/surface-types.d.ts +1 -0
- package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
- package/dist/app/chat/chat/surface-types.js.map +1 -1
- package/dist/app/chat/composer/runtime-adapters.d.ts.map +1 -1
- package/dist/app/chat/composer/runtime-adapters.js +2 -0
- package/dist/app/chat/composer/runtime-adapters.js.map +1 -1
- package/dist/app/chat/composer/use-agentkit-capabilities.d.ts +3 -1
- package/dist/app/chat/composer/use-agentkit-capabilities.d.ts.map +1 -1
- package/dist/app/chat/composer/use-agentkit-capabilities.js +11 -11
- package/dist/app/chat/composer/use-agentkit-capabilities.js.map +1 -1
- package/dist/app/chat/composer/use-workspace-composer-menu.d.ts +3 -0
- package/dist/app/chat/composer/use-workspace-composer-menu.d.ts.map +1 -0
- package/dist/app/chat/composer/use-workspace-composer-menu.js +80 -0
- package/dist/app/chat/composer/use-workspace-composer-menu.js.map +1 -0
- package/dist/app/chat/index.d.ts +1 -0
- package/dist/app/chat/index.d.ts.map +1 -1
- package/dist/app/chat/index.js +1 -0
- package/dist/app/chat/index.js.map +1 -1
- package/dist/app/chat/prepare-prompt-composer-agent-handoff.d.ts +5 -0
- package/dist/app/chat/prepare-prompt-composer-agent-handoff.d.ts.map +1 -0
- package/dist/app/chat/prepare-prompt-composer-agent-handoff.js +108 -0
- package/dist/app/chat/prepare-prompt-composer-agent-handoff.js.map +1 -0
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +6 -1
- package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.js +6 -1
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +6 -1
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +6 -1
- package/dist/app/i18n/catalogs/en-US.js.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.js +6 -1
- package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.js +6 -1
- package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.js +6 -1
- package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.js +6 -1
- package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.js +6 -1
- package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.js +6 -1
- package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.js +6 -1
- package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.js +6 -1
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/resources/McpIntegrationDialog.d.ts +3 -1
- package/dist/app/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/app/resources/McpIntegrationDialog.js +2 -2
- package/dist/app/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/app/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/app/settings/useBuilderStatus.js +119 -40
- package/dist/app/settings/useBuilderStatus.js.map +1 -1
- package/dist/composer/ComposerContextMenu.d.ts +36 -2
- package/dist/composer/ComposerContextMenu.d.ts.map +1 -1
- package/dist/composer/ComposerContextMenu.js +108 -40
- package/dist/composer/ComposerContextMenu.js.map +1 -1
- package/dist/composer/ComposerContextPickerDialog.d.ts.map +1 -1
- package/dist/composer/ComposerContextPickerDialog.js +63 -26
- package/dist/composer/ComposerContextPickerDialog.js.map +1 -1
- package/dist/composer/ComposerPlusMenu.d.ts +5 -3
- package/dist/composer/ComposerPlusMenu.d.ts.map +1 -1
- package/dist/composer/ComposerPlusMenu.js +166 -121
- package/dist/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/composer/MentionItemMedia.d.ts.map +1 -1
- package/dist/composer/MentionItemMedia.js +3 -1
- package/dist/composer/MentionItemMedia.js.map +1 -1
- package/dist/composer/PromptComposer.d.ts +1 -0
- package/dist/composer/PromptComposer.d.ts.map +1 -1
- package/dist/composer/PromptComposer.js +1 -0
- package/dist/composer/PromptComposer.js.map +1 -1
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +117 -50
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/dist/composer/index.d.ts +1 -0
- package/dist/composer/index.d.ts.map +1 -1
- package/dist/composer/runtime-adapters.d.ts +15 -0
- package/dist/composer/runtime-adapters.d.ts.map +1 -1
- package/dist/composer/runtime-adapters.js.map +1 -1
- package/dist/composer/use-composer-panel-placement.d.ts.map +1 -1
- package/dist/composer/use-composer-panel-placement.js +2 -4
- package/dist/composer/use-composer-panel-placement.js.map +1 -1
- package/dist/composer/use-mention-search.d.ts +7 -0
- package/dist/composer/use-mention-search.d.ts.map +1 -1
- package/dist/composer/use-mention-search.js +86 -19
- package/dist/composer/use-mention-search.js.map +1 -1
- package/dist/composer/useComposerContextPicker.d.ts +5 -1
- package/dist/composer/useComposerContextPicker.d.ts.map +1 -1
- package/dist/composer/useComposerContextPicker.js +18 -4
- package/dist/composer/useComposerContextPicker.js.map +1 -1
- package/package.json +4 -4
- package/src/app/agentkit/react/components.tsx +158 -11
- package/src/app/agentkit/react/composer-context.spec.tsx +180 -0
- package/src/app/agentkit/react/core-file-storage.tsx +14 -0
- package/src/app/auth/AuthPage.interaction.spec.tsx +143 -0
- package/src/app/auth/AuthPage.tsx +159 -13
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +65 -3
- package/src/app/chat/AgentKitAssistantChat.tsx +57 -65
- package/src/app/chat/MultiTabAssistantChat.spec.tsx +21 -0
- package/src/app/chat/MultiTabAssistantChat.tsx +4 -0
- package/src/app/chat/agentkit-chat/root.tsx +91 -8
- package/src/app/chat/chat/surface-types.ts +1 -0
- package/src/app/chat/composer/runtime-adapters.tsx +2 -0
- package/src/app/chat/composer/use-agentkit-capabilities.hardening.spec.tsx +26 -1
- package/src/app/chat/composer/use-agentkit-capabilities.ts +11 -11
- package/src/app/chat/composer/use-agentkit-integration-menu.spec.tsx +2 -4
- package/src/app/chat/composer/use-workspace-composer-menu.spec.tsx +174 -0
- package/src/app/chat/composer/use-workspace-composer-menu.tsx +122 -0
- package/src/app/chat/index.ts +1 -0
- package/src/app/chat/prepare-prompt-composer-agent-handoff.spec.ts +162 -0
- package/src/app/chat/prepare-prompt-composer-agent-handoff.ts +134 -0
- package/src/app/i18n/catalogs/ar-SA.ts +6 -1
- package/src/app/i18n/catalogs/de-DE.ts +6 -1
- package/src/app/i18n/catalogs/en-US.ts +6 -1
- package/src/app/i18n/catalogs/es-ES.ts +6 -1
- package/src/app/i18n/catalogs/fr-FR.ts +6 -1
- package/src/app/i18n/catalogs/hi-IN.ts +6 -1
- package/src/app/i18n/catalogs/ja-JP.ts +6 -1
- package/src/app/i18n/catalogs/ko-KR.ts +6 -1
- package/src/app/i18n/catalogs/pt-BR.ts +6 -1
- package/src/app/i18n/catalogs/zh-CN.ts +6 -1
- package/src/app/i18n/catalogs/zh-TW.ts +6 -1
- package/src/app/resources/McpIntegrationDialog.tsx +5 -0
- package/src/app/settings/useBuilderStatus.spec.tsx +229 -13
- package/src/app/settings/useBuilderStatus.ts +145 -47
- package/src/composer/ComposerContextMenu.spec.tsx +95 -32
- package/src/composer/ComposerContextMenu.tsx +284 -73
- package/src/composer/ComposerContextPicker.spec.tsx +13 -14
- package/src/composer/ComposerContextPickerDialog.spec.tsx +40 -3
- package/src/composer/ComposerContextPickerDialog.tsx +158 -24
- package/src/composer/ComposerPlusMenu.parity.spec.tsx +442 -62
- package/src/composer/ComposerPlusMenu.tsx +322 -237
- package/src/composer/MentionItemMedia.tsx +3 -0
- package/src/composer/PromptComposer.context.spec.tsx +253 -82
- package/src/composer/PromptComposer.tsx +2 -0
- package/src/composer/TiptapComposer.spec.ts +6 -8
- package/src/composer/TiptapComposer.tsx +148 -55
- package/src/composer/index.ts +5 -0
- package/src/composer/runtime-adapters.tsx +18 -0
- package/src/composer/use-composer-panel-placement.ts +2 -4
- package/src/composer/use-mention-search.spec.tsx +99 -1
- package/src/composer/use-mention-search.ts +101 -24
- package/src/composer/useComposerContextPicker.ts +25 -5
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { IconPlus } from "@tabler/icons-react";
|
|
1
|
+
import { IconPaperclip, IconPlus } from "@tabler/icons-react";
|
|
2
2
|
import {
|
|
3
3
|
useCallback,
|
|
4
|
+
useId,
|
|
4
5
|
useEffect,
|
|
5
6
|
useRef,
|
|
6
7
|
useState,
|
|
7
8
|
type ReactNode,
|
|
9
|
+
type ReactElement,
|
|
8
10
|
} from "react";
|
|
9
11
|
|
|
10
12
|
import { Button } from "../ui/button.js";
|
|
@@ -14,12 +16,13 @@ import {
|
|
|
14
16
|
DropdownMenuCheckboxItem,
|
|
15
17
|
DropdownMenuGroup,
|
|
16
18
|
DropdownMenuItem,
|
|
19
|
+
DropdownMenuLabel,
|
|
20
|
+
DropdownMenuSeparator,
|
|
17
21
|
DropdownMenuSub,
|
|
18
22
|
DropdownMenuSubContent,
|
|
19
23
|
DropdownMenuSubTrigger,
|
|
20
24
|
DropdownMenuTrigger,
|
|
21
25
|
} from "../ui/dropdown-menu.js";
|
|
22
|
-
import { Skeleton } from "../ui/skeleton.js";
|
|
23
26
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip.js";
|
|
24
27
|
import { formatAttachmentError } from "./attachment-accept.js";
|
|
25
28
|
import {
|
|
@@ -49,20 +52,47 @@ interface ComposerContextMenuEntry {
|
|
|
49
52
|
label: string;
|
|
50
53
|
description?: string;
|
|
51
54
|
intent?: "add-context" | "invoke-integration";
|
|
55
|
+
order?: number;
|
|
56
|
+
section?: "context" | "integrations" | "commands";
|
|
57
|
+
referenceType?: string;
|
|
58
|
+
referencePicker?: { referenceType: string; source: string };
|
|
52
59
|
keywords?: readonly string[];
|
|
53
60
|
icon?: ReactNode;
|
|
54
61
|
disabled?: boolean;
|
|
62
|
+
opensDialog?: boolean;
|
|
55
63
|
}
|
|
56
64
|
export type ComposerContextMenuAction = ComposerContextMenuEntry & {
|
|
57
65
|
checked?: boolean;
|
|
58
66
|
onDismiss?: () => void;
|
|
59
67
|
children?: never;
|
|
60
68
|
} & (
|
|
61
|
-
| {
|
|
69
|
+
| {
|
|
70
|
+
picker: ComposerContextPickerConfig;
|
|
71
|
+
referencePicker?: never;
|
|
72
|
+
render?: never;
|
|
73
|
+
onSelect?: never;
|
|
74
|
+
renderLink?: never;
|
|
75
|
+
}
|
|
76
|
+
| {
|
|
77
|
+
referencePicker: { referenceType: string; source: string };
|
|
78
|
+
picker?: never;
|
|
79
|
+
render?: never;
|
|
80
|
+
onSelect?: never;
|
|
81
|
+
renderLink?: never;
|
|
82
|
+
}
|
|
83
|
+
| {
|
|
84
|
+
renderLink: (children: ReactNode) => ReactElement;
|
|
85
|
+
referencePicker?: never;
|
|
86
|
+
picker?: never;
|
|
87
|
+
render?: never;
|
|
88
|
+
onSelect?: never;
|
|
89
|
+
}
|
|
62
90
|
| {
|
|
63
91
|
onSelect: () => void | Promise<void>;
|
|
92
|
+
referencePicker?: never;
|
|
64
93
|
render?: (controls: ComposerContextPageControls) => ReactNode;
|
|
65
94
|
picker?: never;
|
|
95
|
+
renderLink?: never;
|
|
66
96
|
}
|
|
67
97
|
);
|
|
68
98
|
export interface ComposerContextPageControls {
|
|
@@ -71,6 +101,7 @@ export interface ComposerContextPageControls {
|
|
|
71
101
|
onResume(): void;
|
|
72
102
|
}
|
|
73
103
|
export interface ComposerContextMenuCategory extends ComposerContextMenuEntry {
|
|
104
|
+
referencePicker?: never;
|
|
74
105
|
children: readonly ComposerContextMenuItem[];
|
|
75
106
|
searchPlaceholder?: string;
|
|
76
107
|
onSelect?: never;
|
|
@@ -82,6 +113,12 @@ export type ComposerContextMenuItem =
|
|
|
82
113
|
| ComposerContextMenuCategory;
|
|
83
114
|
export interface ComposerContextMenuProps {
|
|
84
115
|
items: readonly ComposerContextMenuItem[];
|
|
116
|
+
integrationStatus?: {
|
|
117
|
+
scopeKey?: string;
|
|
118
|
+
loading?: boolean;
|
|
119
|
+
error?: string;
|
|
120
|
+
onRetry?: () => void | Promise<unknown>;
|
|
121
|
+
};
|
|
85
122
|
menuActionItems?: readonly ComposerContextMenuItem[];
|
|
86
123
|
loading?: boolean;
|
|
87
124
|
error?: string;
|
|
@@ -223,7 +260,11 @@ function ContextSubmenu({
|
|
|
223
260
|
return (
|
|
224
261
|
<DropdownMenuSub open={open} onOpenChange={onOpenChange}>
|
|
225
262
|
<DropdownMenuSubTrigger ref={trigger} disabled={disabled}>
|
|
226
|
-
{icon
|
|
263
|
+
{icon && (
|
|
264
|
+
<span aria-hidden="true" className="shrink-0 [&>svg]:size-4">
|
|
265
|
+
{icon}
|
|
266
|
+
</span>
|
|
267
|
+
)}
|
|
227
268
|
<span className="min-w-0 truncate">{label}</span>
|
|
228
269
|
</DropdownMenuSubTrigger>
|
|
229
270
|
<DropdownMenuSubContent
|
|
@@ -269,30 +310,42 @@ function LegacyContextPage({ children }: { children: ReactNode }) {
|
|
|
269
310
|
return <div ref={element}>{children}</div>;
|
|
270
311
|
}
|
|
271
312
|
|
|
272
|
-
function ContextEntryLabel({
|
|
313
|
+
function ContextEntryLabel({
|
|
314
|
+
label,
|
|
315
|
+
description,
|
|
316
|
+
icon,
|
|
317
|
+
}: ComposerContextMenuEntry) {
|
|
273
318
|
return (
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
className=
|
|
277
|
-
|
|
278
|
-
? "min-w-0 truncate @md:max-w-[60%] @md:shrink-0"
|
|
279
|
-
: "min-w-0 truncate"
|
|
280
|
-
}
|
|
281
|
-
>
|
|
282
|
-
{label}
|
|
283
|
-
</span>
|
|
284
|
-
{description && (
|
|
285
|
-
<span className="min-w-0 flex-1 truncate text-muted-foreground">
|
|
286
|
-
{description}
|
|
319
|
+
<>
|
|
320
|
+
{icon && (
|
|
321
|
+
<span aria-hidden="true" className="shrink-0 [&>svg]:size-4">
|
|
322
|
+
{icon}
|
|
287
323
|
</span>
|
|
288
324
|
)}
|
|
289
|
-
|
|
325
|
+
<span className="flex min-w-0 flex-1 flex-col gap-0.5 @md:flex-row @md:items-baseline @md:gap-2">
|
|
326
|
+
<span
|
|
327
|
+
className={
|
|
328
|
+
description
|
|
329
|
+
? "min-w-0 truncate @md:max-w-[60%] @md:shrink-0"
|
|
330
|
+
: "min-w-0 truncate"
|
|
331
|
+
}
|
|
332
|
+
>
|
|
333
|
+
{label}
|
|
334
|
+
</span>
|
|
335
|
+
{description && (
|
|
336
|
+
<span className="min-w-0 flex-1 truncate text-muted-foreground">
|
|
337
|
+
{description}
|
|
338
|
+
</span>
|
|
339
|
+
)}
|
|
340
|
+
</span>
|
|
341
|
+
</>
|
|
290
342
|
);
|
|
291
343
|
}
|
|
292
344
|
|
|
293
345
|
export function ComposerContextMenu({
|
|
294
346
|
items,
|
|
295
347
|
menuActionItems = [],
|
|
348
|
+
integrationStatus,
|
|
296
349
|
loading,
|
|
297
350
|
error: searchError,
|
|
298
351
|
onRetry,
|
|
@@ -309,6 +362,66 @@ export function ComposerContextMenu({
|
|
|
309
362
|
openEntry,
|
|
310
363
|
}: ComposerContextMenuProps) {
|
|
311
364
|
const t = useComposerRuntimeAdapters().translate!;
|
|
365
|
+
const sectionId = useId();
|
|
366
|
+
const sortEntries = (entries: readonly ComposerContextMenuItem[]) =>
|
|
367
|
+
[...entries].sort(
|
|
368
|
+
(left, right) =>
|
|
369
|
+
(left.order ?? 100) - (right.order ?? 100) ||
|
|
370
|
+
left.label.localeCompare(right.label) ||
|
|
371
|
+
left.id.localeCompare(right.id),
|
|
372
|
+
);
|
|
373
|
+
const seen = new Set<string>();
|
|
374
|
+
const sections: Record<
|
|
375
|
+
"context" | "integrations" | "commands",
|
|
376
|
+
ComposerContextMenuItem[]
|
|
377
|
+
> = { context: [], integrations: [], commands: [] };
|
|
378
|
+
const collect = (
|
|
379
|
+
entries: readonly ComposerContextMenuItem[],
|
|
380
|
+
inherited?: "context" | "integrations" | "commands",
|
|
381
|
+
inheritedDisabled = false,
|
|
382
|
+
) => {
|
|
383
|
+
const retained: ComposerContextMenuItem[] = [];
|
|
384
|
+
for (const item of entries) {
|
|
385
|
+
if (seen.has(item.id)) continue;
|
|
386
|
+
seen.add(item.id);
|
|
387
|
+
const section =
|
|
388
|
+
item.section ??
|
|
389
|
+
(item.intent === "invoke-integration"
|
|
390
|
+
? "integrations"
|
|
391
|
+
: (inherited ?? "context"));
|
|
392
|
+
const entry = inheritedDisabled ? { ...item, disabled: true } : item;
|
|
393
|
+
if (item.children && !item.section && !item.intent) {
|
|
394
|
+
const children = collect(
|
|
395
|
+
item.children,
|
|
396
|
+
section,
|
|
397
|
+
inheritedDisabled || item.disabled === true,
|
|
398
|
+
);
|
|
399
|
+
if (children.length)
|
|
400
|
+
retained.push({
|
|
401
|
+
...item,
|
|
402
|
+
...(inheritedDisabled ? { disabled: true } : {}),
|
|
403
|
+
children,
|
|
404
|
+
});
|
|
405
|
+
} else if (section !== (inherited ?? "context"))
|
|
406
|
+
sections[section].push(entry);
|
|
407
|
+
else retained.push(entry);
|
|
408
|
+
}
|
|
409
|
+
return retained;
|
|
410
|
+
};
|
|
411
|
+
sections.context.push(...collect(items));
|
|
412
|
+
sections.commands.push(...collect(menuActionItems, "commands"));
|
|
413
|
+
const hasIntegrations = Boolean(
|
|
414
|
+
sections.integrations.length ||
|
|
415
|
+
integrationStatus?.loading ||
|
|
416
|
+
integrationStatus?.error,
|
|
417
|
+
);
|
|
418
|
+
const hasAttachments = Boolean(
|
|
419
|
+
sections.context.length ||
|
|
420
|
+
addAttachment ||
|
|
421
|
+
onAttachmentRequest ||
|
|
422
|
+
loading ||
|
|
423
|
+
searchError,
|
|
424
|
+
);
|
|
312
425
|
const allItems = [...menuActionItems, ...items];
|
|
313
426
|
const onDisabledFocusRef = useRef(onDisabledFocus);
|
|
314
427
|
onDisabledFocusRef.current = onDisabledFocus;
|
|
@@ -363,7 +476,7 @@ export function ComposerContextMenu({
|
|
|
363
476
|
defaultValue: "Add context",
|
|
364
477
|
});
|
|
365
478
|
const uploadLabel = t("agentChat.composer.menu.uploadFile", {
|
|
366
|
-
defaultValue: "
|
|
479
|
+
defaultValue: "Files and images",
|
|
367
480
|
});
|
|
368
481
|
const reportError = useCallback(
|
|
369
482
|
(cause: unknown) => {
|
|
@@ -493,7 +606,7 @@ export function ComposerContextMenu({
|
|
|
493
606
|
origin: string[],
|
|
494
607
|
): ReactNode => (
|
|
495
608
|
<>
|
|
496
|
-
{entries.map((entry) => {
|
|
609
|
+
{sortEntries(entries).map((entry) => {
|
|
497
610
|
const branch = [...origin, entry.id];
|
|
498
611
|
const expanded = branch.every((id, index) => path[index] === id);
|
|
499
612
|
if (entry.children) {
|
|
@@ -501,9 +614,17 @@ export function ComposerContextMenu({
|
|
|
501
614
|
<ContextSubmenu
|
|
502
615
|
key={entry.id}
|
|
503
616
|
label={entry.label}
|
|
617
|
+
icon={entry.icon}
|
|
504
618
|
disabled={entry.disabled}
|
|
505
619
|
open={expanded}
|
|
506
|
-
onOpenChange={(next) => {
|
|
620
|
+
onOpenChange={(next, reason) => {
|
|
621
|
+
if (
|
|
622
|
+
!next &&
|
|
623
|
+
reason !== "escape" &&
|
|
624
|
+
resumedPageClose.current &&
|
|
625
|
+
pathRef.current.includes(resumedPageClose.current)
|
|
626
|
+
)
|
|
627
|
+
return;
|
|
507
628
|
const isOpen = branch.every(
|
|
508
629
|
(id, index) => pathRef.current[index] === id,
|
|
509
630
|
);
|
|
@@ -548,6 +669,7 @@ export function ComposerContextMenu({
|
|
|
548
669
|
<ContextSubmenu
|
|
549
670
|
key={entry.id}
|
|
550
671
|
label={entry.label}
|
|
672
|
+
icon={entry.icon}
|
|
551
673
|
disabled={entry.disabled}
|
|
552
674
|
open={expanded}
|
|
553
675
|
onOpenChange={(next, reason) => {
|
|
@@ -605,7 +727,6 @@ export function ComposerContextMenu({
|
|
|
605
727
|
);
|
|
606
728
|
return;
|
|
607
729
|
}
|
|
608
|
-
// Radix can emit one stale close after native-picker focus returns.
|
|
609
730
|
resumedPageClose.current = entry.id;
|
|
610
731
|
if (resumedPageCloseFrame.current !== null)
|
|
611
732
|
window.cancelAnimationFrame(
|
|
@@ -627,6 +748,17 @@ export function ComposerContextMenu({
|
|
|
627
748
|
</ContextSubmenu>
|
|
628
749
|
);
|
|
629
750
|
}
|
|
751
|
+
if (entry.renderLink) {
|
|
752
|
+
return (
|
|
753
|
+
<DropdownMenuItem
|
|
754
|
+
key={entry.id}
|
|
755
|
+
asChild
|
|
756
|
+
disabled={entry.disabled === true}
|
|
757
|
+
>
|
|
758
|
+
{entry.renderLink(<ContextEntryLabel {...entry} />)}
|
|
759
|
+
</DropdownMenuItem>
|
|
760
|
+
);
|
|
761
|
+
}
|
|
630
762
|
if (entry.checked !== undefined) {
|
|
631
763
|
return (
|
|
632
764
|
<DropdownMenuCheckboxItem
|
|
@@ -647,6 +779,7 @@ export function ComposerContextMenu({
|
|
|
647
779
|
key={entry.id}
|
|
648
780
|
disabled={entry.disabled}
|
|
649
781
|
onSelect={() => {
|
|
782
|
+
if (entry.opensDialog) restoreFocusOnClose.current = false;
|
|
650
783
|
changeOpen(false);
|
|
651
784
|
selectAction(entry);
|
|
652
785
|
}}
|
|
@@ -717,7 +850,7 @@ export function ComposerContextMenu({
|
|
|
717
850
|
maxHeight: placement.maxHeight,
|
|
718
851
|
boxShadow: "none",
|
|
719
852
|
}}
|
|
720
|
-
className={`@container flex w-64 max-w-[calc(100vw-24px)] flex-col p-1 data-[state=open]:fade-in-100 data-[state=closed]:fade-out-100 ${COMPOSER_POPOVER_SURFACE_CLASS}`}
|
|
853
|
+
className={`@container [&_[role^=menuitem]]:min-h-8 flex w-64 max-w-[calc(100vw-24px)] flex-col overflow-y-auto p-1 data-[state=open]:fade-in-100 data-[state=closed]:fade-out-100 ${COMPOSER_POPOVER_SURFACE_CLASS}`}
|
|
721
854
|
data-agent-native-composer-popover="true"
|
|
722
855
|
onCloseAutoFocus={(event) => {
|
|
723
856
|
if (pendingDialog.current) {
|
|
@@ -744,76 +877,154 @@ export function ComposerContextMenu({
|
|
|
744
877
|
restoreFocusOnClose.current = true;
|
|
745
878
|
}}
|
|
746
879
|
>
|
|
747
|
-
<div className="
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
880
|
+
<div className="contents">
|
|
881
|
+
{hasAttachments && (
|
|
882
|
+
<DropdownMenuGroup
|
|
883
|
+
aria-labelledby={`${sectionId}-context`}
|
|
884
|
+
aria-busy={loading || undefined}
|
|
885
|
+
>
|
|
886
|
+
<DropdownMenuLabel id={`${sectionId}-context`}>
|
|
887
|
+
{t("agentChat.composer.menu.attachToMessage", {
|
|
888
|
+
defaultValue: "Add context",
|
|
889
|
+
})}
|
|
890
|
+
</DropdownMenuLabel>
|
|
891
|
+
{(addAttachment || onAttachmentRequest) && (
|
|
892
|
+
<DropdownMenuItem
|
|
893
|
+
onSelect={() => {
|
|
894
|
+
if (onAttachmentRequest) {
|
|
895
|
+
pendingAttachmentRequest.current = true;
|
|
896
|
+
changeOpen(false);
|
|
897
|
+
} else {
|
|
898
|
+
changeOpen(false);
|
|
899
|
+
inputRef.current?.click();
|
|
900
|
+
}
|
|
901
|
+
}}
|
|
902
|
+
>
|
|
903
|
+
<IconPaperclip aria-hidden="true" />
|
|
904
|
+
{uploadLabel}
|
|
905
|
+
</DropdownMenuItem>
|
|
906
|
+
)}
|
|
907
|
+
{renderEntries(sections.context, [])}
|
|
908
|
+
<div
|
|
909
|
+
role="presentation"
|
|
910
|
+
aria-hidden="true"
|
|
911
|
+
className={
|
|
912
|
+
loading
|
|
913
|
+
? "px-2 py-1 text-xs text-muted-foreground"
|
|
914
|
+
: "sr-only"
|
|
915
|
+
}
|
|
760
916
|
>
|
|
761
|
-
{
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
917
|
+
{loading
|
|
918
|
+
? t("agentChat.composer.contextPending", {
|
|
919
|
+
defaultValue: "Loading context…",
|
|
920
|
+
})
|
|
921
|
+
: ""}
|
|
922
|
+
</div>
|
|
923
|
+
{searchError && (
|
|
924
|
+
<div
|
|
925
|
+
role="presentation"
|
|
926
|
+
aria-hidden="true"
|
|
927
|
+
className="px-2 py-1 text-sm text-destructive"
|
|
928
|
+
>
|
|
929
|
+
{searchError}
|
|
930
|
+
</div>
|
|
931
|
+
)}
|
|
932
|
+
{searchError && onRetry && (
|
|
933
|
+
<DropdownMenuItem
|
|
934
|
+
onSelect={(event) => {
|
|
935
|
+
event.preventDefault();
|
|
936
|
+
try {
|
|
937
|
+
void Promise.resolve(onRetry()).catch(reportError);
|
|
938
|
+
} catch (cause) {
|
|
939
|
+
reportError(cause);
|
|
940
|
+
}
|
|
941
|
+
}}
|
|
942
|
+
>
|
|
943
|
+
{t("agentChat.common.retry", { defaultValue: "Retry" })}
|
|
944
|
+
</DropdownMenuItem>
|
|
945
|
+
)}
|
|
946
|
+
</DropdownMenuGroup>
|
|
947
|
+
)}
|
|
948
|
+
{hasIntegrations && (
|
|
949
|
+
<>
|
|
950
|
+
{hasAttachments && <DropdownMenuSeparator />}
|
|
951
|
+
<DropdownMenuGroup
|
|
952
|
+
aria-labelledby={`${sectionId}-integrations`}
|
|
953
|
+
aria-busy={integrationStatus?.loading || undefined}
|
|
778
954
|
>
|
|
779
|
-
{
|
|
780
|
-
|
|
955
|
+
<DropdownMenuLabel id={`${sectionId}-integrations`}>
|
|
956
|
+
{t("agentChat.composer.menu.integrations", {
|
|
957
|
+
defaultValue: "Integrations",
|
|
958
|
+
})}
|
|
959
|
+
</DropdownMenuLabel>
|
|
960
|
+
{renderEntries(sections.integrations, [])}
|
|
961
|
+
{integrationStatus?.loading && (
|
|
781
962
|
<div
|
|
782
|
-
role="
|
|
783
|
-
aria-
|
|
784
|
-
|
|
785
|
-
})}
|
|
786
|
-
className="grid gap-2 p-3"
|
|
963
|
+
role="presentation"
|
|
964
|
+
aria-hidden="true"
|
|
965
|
+
className="px-2 py-1 text-xs text-muted-foreground"
|
|
787
966
|
>
|
|
788
|
-
|
|
789
|
-
|
|
967
|
+
{t("agentChat.common.loading", {
|
|
968
|
+
defaultValue: "Loading...",
|
|
969
|
+
})}
|
|
790
970
|
</div>
|
|
791
971
|
)}
|
|
792
|
-
{
|
|
972
|
+
{integrationStatus?.error && (
|
|
793
973
|
<div
|
|
794
|
-
role="
|
|
795
|
-
|
|
974
|
+
role="presentation"
|
|
975
|
+
aria-hidden="true"
|
|
976
|
+
className="px-2 py-1 text-sm text-destructive"
|
|
796
977
|
>
|
|
797
|
-
{
|
|
978
|
+
{integrationStatus.error}
|
|
798
979
|
</div>
|
|
799
980
|
)}
|
|
800
|
-
{
|
|
981
|
+
{integrationStatus?.error && integrationStatus.onRetry && (
|
|
801
982
|
<DropdownMenuItem
|
|
802
983
|
onSelect={(event) => {
|
|
803
984
|
event.preventDefault();
|
|
804
|
-
|
|
985
|
+
try {
|
|
986
|
+
void Promise.resolve(
|
|
987
|
+
integrationStatus.onRetry?.(),
|
|
988
|
+
).catch(reportError);
|
|
989
|
+
} catch (cause) {
|
|
990
|
+
reportError(cause);
|
|
991
|
+
}
|
|
805
992
|
}}
|
|
806
993
|
>
|
|
807
994
|
{t("agentChat.common.retry", { defaultValue: "Retry" })}
|
|
808
995
|
</DropdownMenuItem>
|
|
809
996
|
)}
|
|
810
|
-
</
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
997
|
+
</DropdownMenuGroup>
|
|
998
|
+
</>
|
|
999
|
+
)}
|
|
1000
|
+
{sections.commands.length > 0 && (
|
|
1001
|
+
<>
|
|
1002
|
+
{(hasAttachments || hasIntegrations) && (
|
|
1003
|
+
<DropdownMenuSeparator />
|
|
1004
|
+
)}
|
|
1005
|
+
<DropdownMenuGroup aria-labelledby={`${sectionId}-commands`}>
|
|
1006
|
+
<DropdownMenuLabel id={`${sectionId}-commands`}>
|
|
1007
|
+
{t("agentChat.composer.menu.commands", {
|
|
1008
|
+
defaultValue: "Commands",
|
|
1009
|
+
})}
|
|
1010
|
+
</DropdownMenuLabel>
|
|
1011
|
+
{renderEntries(sections.commands, [])}
|
|
1012
|
+
</DropdownMenuGroup>
|
|
1013
|
+
</>
|
|
1014
|
+
)}
|
|
814
1015
|
</div>
|
|
815
1016
|
</DropdownMenuContent>
|
|
816
1017
|
</DropdownMenu>
|
|
1018
|
+
<div role="status" aria-live="polite" className="sr-only">
|
|
1019
|
+
{open && (loading || integrationStatus?.loading)
|
|
1020
|
+
? t("agentChat.common.loading", { defaultValue: "Loading..." })
|
|
1021
|
+
: ""}
|
|
1022
|
+
</div>
|
|
1023
|
+
{open && (searchError || integrationStatus?.error) && (
|
|
1024
|
+
<div role="alert" className="sr-only">
|
|
1025
|
+
{searchError ?? integrationStatus?.error}
|
|
1026
|
+
</div>
|
|
1027
|
+
)}
|
|
817
1028
|
{dialog && dialogAvailable && dialogAction?.picker && (
|
|
818
1029
|
<ComposerContextPickerDialog
|
|
819
1030
|
key={JSON.stringify([dialog.id, dialogAction.picker.scopeKey])}
|
|
@@ -71,7 +71,6 @@ async function open() {
|
|
|
71
71
|
container.querySelector('button[aria-label="Add context"]')!,
|
|
72
72
|
"ArrowDown",
|
|
73
73
|
);
|
|
74
|
-
await click("Add context");
|
|
75
74
|
await click("Category");
|
|
76
75
|
await click("Source");
|
|
77
76
|
}
|
|
@@ -169,7 +168,7 @@ describe("declarative context picker", () => {
|
|
|
169
168
|
expect(select).not.toHaveBeenCalled();
|
|
170
169
|
await click("Clear selection");
|
|
171
170
|
expect(clear).toHaveBeenCalledOnce();
|
|
172
|
-
expect(menus()).toHaveLength(
|
|
171
|
+
expect(menus()).toHaveLength(3);
|
|
173
172
|
});
|
|
174
173
|
it("retries a failed selection and closes only on success", async () => {
|
|
175
174
|
const onSelect = vi
|
|
@@ -194,7 +193,7 @@ describe("declarative context picker", () => {
|
|
|
194
193
|
await render({ ...config, scopeKey: "account-b" });
|
|
195
194
|
expect(onSelect.mock.calls[0][1].signal.aborted).toBe(true);
|
|
196
195
|
await act(async () => pending.resolve());
|
|
197
|
-
expect(menus()).toHaveLength(
|
|
196
|
+
expect(menus()).toHaveLength(3);
|
|
198
197
|
});
|
|
199
198
|
it("keeps link, search and page on sync refresh, and aborts a dismissed load", async () => {
|
|
200
199
|
const pending = deferred<{ items: { id: string; title: string }[] }>();
|
|
@@ -233,13 +232,13 @@ describe("declarative context picker", () => {
|
|
|
233
232
|
await act(async () =>
|
|
234
233
|
pending.resolve({ items: [{ id: "late", title: "Late" }] }),
|
|
235
234
|
);
|
|
236
|
-
expect(menus()).toHaveLength(
|
|
235
|
+
expect(menus()).toHaveLength(2);
|
|
237
236
|
expect(document.body.textContent).not.toContain("Late");
|
|
238
237
|
});
|
|
239
238
|
it("keeps connected panels, integrated headers and source search autofocus", async () => {
|
|
240
239
|
await render(local());
|
|
241
240
|
await open();
|
|
242
|
-
expect(menus()).toHaveLength(
|
|
241
|
+
expect(menus()).toHaveLength(3);
|
|
243
242
|
expect(menus().at(-1)?.classList.contains("w-64")).toBe(true);
|
|
244
243
|
const searches = document.querySelectorAll('input[role="searchbox"]');
|
|
245
244
|
expect(searches).toHaveLength(1);
|
|
@@ -272,7 +271,7 @@ describe("declarative context picker", () => {
|
|
|
272
271
|
signal: expect.any(AbortSignal),
|
|
273
272
|
}),
|
|
274
273
|
);
|
|
275
|
-
expect(menus()).toHaveLength(
|
|
274
|
+
expect(menus()).toHaveLength(3);
|
|
276
275
|
});
|
|
277
276
|
it("routes typing and arrows through the root item and picker submenu", async () => {
|
|
278
277
|
await render(local());
|
|
@@ -287,10 +286,10 @@ describe("declarative context picker", () => {
|
|
|
287
286
|
await key(row("One"), "ArrowDown");
|
|
288
287
|
expect(document.activeElement).toBe(row("Two"));
|
|
289
288
|
await key(row("Two"), "ArrowLeft");
|
|
290
|
-
expect(menus()).toHaveLength(
|
|
289
|
+
expect(menus()).toHaveLength(2);
|
|
291
290
|
expect(document.activeElement).toBe(row("Source"));
|
|
292
291
|
await key(row("Source"), "Escape");
|
|
293
|
-
expect(menus()).toHaveLength(
|
|
292
|
+
expect(menus()).toHaveLength(1);
|
|
294
293
|
});
|
|
295
294
|
it("loads pages with cursor history and preserves location on refresh without descriptor loops", async () => {
|
|
296
295
|
const load = vi
|
|
@@ -390,7 +389,7 @@ describe("declarative context picker", () => {
|
|
|
390
389
|
expect(signal.aborted).toBe(true);
|
|
391
390
|
await click("Source");
|
|
392
391
|
await act(async () => pending.resolve());
|
|
393
|
-
expect(menus()).toHaveLength(
|
|
392
|
+
expect(menus()).toHaveLength(3);
|
|
394
393
|
});
|
|
395
394
|
it("validates links before reading, searches results, and returns to the populated link stage", async () => {
|
|
396
395
|
const load = vi
|
|
@@ -435,19 +434,19 @@ describe("declarative context picker", () => {
|
|
|
435
434
|
'input[placeholder="Source URL"]',
|
|
436
435
|
)?.value,
|
|
437
436
|
).toBe("https://example.com/source");
|
|
438
|
-
expect(menus()).toHaveLength(
|
|
437
|
+
expect(menus()).toHaveLength(3);
|
|
439
438
|
await key(
|
|
440
439
|
document.querySelector('input[placeholder="Source URL"]')!,
|
|
441
440
|
"ArrowLeft",
|
|
442
441
|
);
|
|
443
|
-
expect(menus()).toHaveLength(
|
|
442
|
+
expect(menus()).toHaveLength(3);
|
|
444
443
|
await key(
|
|
445
444
|
document.querySelector('input[placeholder="Source URL"]')!,
|
|
446
445
|
"ArrowRight",
|
|
447
446
|
);
|
|
448
|
-
expect(menus()).toHaveLength(4);
|
|
449
|
-
await key(row("Read link"), "ArrowLeft");
|
|
450
447
|
expect(menus()).toHaveLength(3);
|
|
448
|
+
await key(row("Read link"), "ArrowLeft");
|
|
449
|
+
expect(menus()).toHaveLength(2);
|
|
451
450
|
});
|
|
452
451
|
it("keeps a genuine link footer present in empty and populated states", async () => {
|
|
453
452
|
const config = {
|
|
@@ -514,6 +513,6 @@ describe("declarative context picker", () => {
|
|
|
514
513
|
await click("Retry");
|
|
515
514
|
expect(footer).toHaveBeenCalledTimes(2);
|
|
516
515
|
expect(load).not.toHaveBeenCalled();
|
|
517
|
-
expect(menus()).toHaveLength(
|
|
516
|
+
expect(menus()).toHaveLength(3);
|
|
518
517
|
});
|
|
519
518
|
});
|
|
@@ -140,7 +140,6 @@ async function open() {
|
|
|
140
140
|
new KeyboardEvent("keydown", { key: "ArrowDown", bubbles: true }),
|
|
141
141
|
),
|
|
142
142
|
);
|
|
143
|
-
await act(async () => row("Add context").click());
|
|
144
143
|
await act(async () => row("Design").click());
|
|
145
144
|
expect(row("Attach source").hasAttribute("aria-haspopup")).toBe(false);
|
|
146
145
|
await act(async () => row("Attach source").click());
|
|
@@ -184,6 +183,46 @@ function deferred<T>() {
|
|
|
184
183
|
}
|
|
185
184
|
|
|
186
185
|
describe("shared context URL dialog", () => {
|
|
186
|
+
it("keeps an opened loading source in its dialog when the URL form becomes available", async () => {
|
|
187
|
+
await render({
|
|
188
|
+
presentation: { type: "dialog", mode: "single" },
|
|
189
|
+
scopeKey: "account-a",
|
|
190
|
+
searchPlaceholder: "Search frames",
|
|
191
|
+
items: [],
|
|
192
|
+
loading: true,
|
|
193
|
+
onSelect: vi.fn(),
|
|
194
|
+
});
|
|
195
|
+
await open();
|
|
196
|
+
expect(dialog()).not.toBeNull();
|
|
197
|
+
const connected = multiple();
|
|
198
|
+
await render(connected);
|
|
199
|
+
await tick();
|
|
200
|
+
const input =
|
|
201
|
+
dialog()?.querySelector<HTMLInputElement>('input[type="url"]');
|
|
202
|
+
expect(input).not.toBeNull();
|
|
203
|
+
expect(document.activeElement).toBe(input);
|
|
204
|
+
expect(connected.load).not.toHaveBeenCalled();
|
|
205
|
+
await render({
|
|
206
|
+
presentation: { type: "dialog", mode: "single" },
|
|
207
|
+
scopeKey: "account-a",
|
|
208
|
+
searchPlaceholder: "Search frames",
|
|
209
|
+
items: [],
|
|
210
|
+
emptyMessage: "Connect this source",
|
|
211
|
+
footerAction: {
|
|
212
|
+
label: "Connect source",
|
|
213
|
+
renderLink: (children) => (
|
|
214
|
+
<a href="/settings/integrations">{children}</a>
|
|
215
|
+
),
|
|
216
|
+
},
|
|
217
|
+
onSelect: vi.fn(),
|
|
218
|
+
});
|
|
219
|
+
await tick();
|
|
220
|
+
expect(dialog()).not.toBeNull();
|
|
221
|
+
expect(
|
|
222
|
+
dialog()?.querySelector('a[href="/settings/integrations"]')?.textContent,
|
|
223
|
+
).toBe("Connect source");
|
|
224
|
+
});
|
|
225
|
+
|
|
187
226
|
it("supports gallery previews with immediate single selection", async () => {
|
|
188
227
|
const onSelect = vi.fn().mockResolvedValue(undefined);
|
|
189
228
|
const onClose = vi.fn();
|
|
@@ -455,8 +494,6 @@ describe("shared context URL dialog", () => {
|
|
|
455
494
|
),
|
|
456
495
|
);
|
|
457
496
|
expect(document.querySelector('[role="searchbox"]')).toBeNull();
|
|
458
|
-
expect(row("Add context")).toBeDefined();
|
|
459
|
-
await act(async () => row("Add context").click());
|
|
460
497
|
expect(row("Design")).toBeDefined();
|
|
461
498
|
await act(async () => row("Design").click());
|
|
462
499
|
expect(row("Attach source").querySelector("svg")).toBeNull();
|