@agent-native/toolkit 0.201.0 → 0.201.1
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 +21 -13
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/agentkit/react/context.d.ts +15 -1
- package/dist/app/agentkit/react/context.d.ts.map +1 -1
- package/dist/app/agentkit/react/context.js +30 -4
- package/dist/app/agentkit/react/context.js.map +1 -1
- package/dist/app/agentkit/react/styles.css +11 -7
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +9 -4
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/FileStorageSetupPopover.d.ts.map +1 -1
- package/dist/app/chat/FileStorageSetupPopover.js +3 -2
- package/dist/app/chat/FileStorageSetupPopover.js.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +11 -5
- 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 +11 -5
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +11 -5
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +11 -5
- 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 +11 -5
- 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 +11 -5
- 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 +11 -5
- 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 +11 -5
- 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 +11 -5
- 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 +11 -5
- 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 +11 -5
- 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 +11 -5
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/onboarding/BuilderIncludedServices.d.ts +23 -0
- package/dist/app/onboarding/BuilderIncludedServices.d.ts.map +1 -0
- package/dist/app/onboarding/BuilderIncludedServices.js +67 -0
- package/dist/app/onboarding/BuilderIncludedServices.js.map +1 -0
- package/dist/app/onboarding/FirstRunOnboarding.d.ts.map +1 -1
- package/dist/app/onboarding/FirstRunOnboarding.js +10 -29
- package/dist/app/onboarding/FirstRunOnboarding.js.map +1 -1
- package/dist/app/resources/McpConnectionSuggestion.d.ts.map +1 -1
- package/dist/app/resources/McpConnectionSuggestion.js +55 -21
- package/dist/app/resources/McpConnectionSuggestion.js.map +1 -1
- package/dist/app/settings/BuilderConnectPopover.d.ts +1 -2
- package/dist/app/settings/BuilderConnectPopover.d.ts.map +1 -1
- package/dist/app/settings/BuilderConnectPopover.js +35 -21
- package/dist/app/settings/BuilderConnectPopover.js.map +1 -1
- package/dist/app/settings/deferred-builder-connect-popover.d.ts.map +1 -1
- package/dist/app/settings/deferred-builder-connect-popover.js +28 -1
- package/dist/app/settings/deferred-builder-connect-popover.js.map +1 -1
- package/dist/app/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/app/settings/useBuilderStatus.js +8 -1
- package/dist/app/settings/useBuilderStatus.js.map +1 -1
- package/dist/app/styles/tokens/agent-kit.css +23 -11
- package/dist/composer/MessageQueueDrawer.d.ts +5 -0
- package/dist/composer/MessageQueueDrawer.d.ts.map +1 -1
- package/dist/composer/MessageQueueDrawer.js +11 -5
- package/dist/composer/MessageQueueDrawer.js.map +1 -1
- package/package.json +4 -3
- package/src/app/agentkit/react/components.interaction.spec.tsx +13 -5
- package/src/app/agentkit/react/components.tsx +24 -14
- package/src/app/agentkit/react/context.tsx +53 -6
- package/src/app/agentkit/react/styles.css +11 -7
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +35 -16
- package/src/app/chat/AgentKitAssistantChat.tsx +13 -4
- package/src/app/chat/FileStorageSetupPopover.spec.tsx +129 -0
- package/src/app/chat/FileStorageSetupPopover.tsx +10 -3
- package/src/app/i18n/catalogs/ar-SA.ts +14 -5
- package/src/app/i18n/catalogs/de-DE.ts +15 -5
- package/src/app/i18n/catalogs/en-US.ts +14 -5
- package/src/app/i18n/catalogs/es-ES.ts +14 -5
- package/src/app/i18n/catalogs/fr-FR.ts +15 -5
- package/src/app/i18n/catalogs/hi-IN.ts +14 -5
- package/src/app/i18n/catalogs/ja-JP.ts +14 -5
- package/src/app/i18n/catalogs/ko-KR.ts +14 -5
- package/src/app/i18n/catalogs/pt-BR.ts +15 -5
- package/src/app/i18n/catalogs/zh-CN.ts +13 -5
- package/src/app/i18n/catalogs/zh-TW.ts +13 -5
- package/src/app/onboarding/BuilderIncludedServices.tsx +263 -0
- package/src/app/onboarding/FirstRunOnboarding.spec.tsx +23 -8
- package/src/app/onboarding/FirstRunOnboarding.tsx +22 -112
- package/src/app/resources/McpConnectionSuggestion.render.spec.tsx +54 -2
- package/src/app/resources/McpConnectionSuggestion.tsx +73 -25
- package/src/app/settings/BuilderConnectPopover.spec.tsx +175 -6
- package/src/app/settings/BuilderConnectPopover.tsx +87 -67
- package/src/app/settings/deferred-builder-connect-popover.tsx +46 -0
- package/src/app/settings/model/ModelSettingsPage.spec.tsx +8 -24
- package/src/app/settings/useBuilderStatus.spec.tsx +5 -0
- package/src/app/settings/useBuilderStatus.ts +8 -0
- package/src/app/styles/agent-native.spec.ts +5 -8
- package/src/app/styles/tokens/agent-kit.css +23 -11
- package/src/composer/MessageQueueDrawer.spec.tsx +20 -7
- package/src/composer/MessageQueueDrawer.tsx +47 -21
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
2
|
+
import type { OnboardingCapability } from "@agent-native/core/onboarding/types";
|
|
3
|
+
import {
|
|
4
|
+
Collapsible,
|
|
5
|
+
CollapsibleContent,
|
|
6
|
+
CollapsibleTrigger,
|
|
7
|
+
} from "@agent-native/toolkit/ui/collapsible";
|
|
8
|
+
import { Skeleton } from "@agent-native/toolkit/ui/skeleton";
|
|
9
|
+
import {
|
|
10
|
+
Tooltip,
|
|
11
|
+
TooltipContent,
|
|
12
|
+
TooltipProvider,
|
|
13
|
+
TooltipTrigger,
|
|
14
|
+
} from "@agent-native/toolkit/ui/tooltip";
|
|
15
|
+
import {
|
|
16
|
+
IconCheck,
|
|
17
|
+
IconChevronDown,
|
|
18
|
+
IconInfoCircle,
|
|
19
|
+
} from "@tabler/icons-react";
|
|
20
|
+
|
|
21
|
+
type CapabilityTranslator = (
|
|
22
|
+
key: string,
|
|
23
|
+
options?: Record<string, unknown>,
|
|
24
|
+
) => string;
|
|
25
|
+
|
|
26
|
+
export function getBuilderIncludedCapabilities(
|
|
27
|
+
capabilities: OnboardingCapability[],
|
|
28
|
+
) {
|
|
29
|
+
return capabilities.filter(
|
|
30
|
+
(capability) =>
|
|
31
|
+
capability.builderIncluded &&
|
|
32
|
+
(!!capability.service ||
|
|
33
|
+
capability.required ||
|
|
34
|
+
!!capability.suggested ||
|
|
35
|
+
!!capability.builderOnly),
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function capabilityCopy(
|
|
40
|
+
t: CapabilityTranslator,
|
|
41
|
+
capability: OnboardingCapability,
|
|
42
|
+
) {
|
|
43
|
+
return {
|
|
44
|
+
label: capability.labelKey
|
|
45
|
+
? t(capability.labelKey, { defaultValue: capability.label })
|
|
46
|
+
: capability.label,
|
|
47
|
+
why: capability.whyKey
|
|
48
|
+
? t(capability.whyKey, { defaultValue: capability.why })
|
|
49
|
+
: null,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function CapabilityInfoButton({
|
|
54
|
+
label,
|
|
55
|
+
why,
|
|
56
|
+
}: {
|
|
57
|
+
label: string;
|
|
58
|
+
why: string;
|
|
59
|
+
}) {
|
|
60
|
+
const t = useT();
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Tooltip>
|
|
64
|
+
<TooltipTrigger asChild>
|
|
65
|
+
<button
|
|
66
|
+
type="button"
|
|
67
|
+
aria-label={t("agentChat.onboarding.capability.about", {
|
|
68
|
+
defaultValue: "About {{label}}",
|
|
69
|
+
label,
|
|
70
|
+
})}
|
|
71
|
+
className="inline-flex size-4 items-center justify-center rounded-full text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
72
|
+
onClick={(event) => event.stopPropagation()}
|
|
73
|
+
onKeyDown={(event) => event.stopPropagation()}
|
|
74
|
+
>
|
|
75
|
+
<IconInfoCircle size={13} />
|
|
76
|
+
</button>
|
|
77
|
+
</TooltipTrigger>
|
|
78
|
+
<TooltipContent side="top" className="max-w-xs text-xs">
|
|
79
|
+
{why}
|
|
80
|
+
</TooltipContent>
|
|
81
|
+
</Tooltip>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function CapabilityRows({
|
|
86
|
+
capabilities,
|
|
87
|
+
}: {
|
|
88
|
+
capabilities: OnboardingCapability[];
|
|
89
|
+
}) {
|
|
90
|
+
const t = useT();
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<TooltipProvider>
|
|
94
|
+
{capabilities.map((capability) => {
|
|
95
|
+
const copy = capabilityCopy(t, capability);
|
|
96
|
+
return (
|
|
97
|
+
<div
|
|
98
|
+
key={capability.id}
|
|
99
|
+
className="flex items-center gap-2 rounded-md px-2 py-1"
|
|
100
|
+
>
|
|
101
|
+
<IconCheck
|
|
102
|
+
aria-hidden="true"
|
|
103
|
+
className="shrink-0 text-muted-foreground"
|
|
104
|
+
size={15}
|
|
105
|
+
/>
|
|
106
|
+
<span className="text-xs text-foreground">{copy.label}</span>
|
|
107
|
+
{copy.why ? (
|
|
108
|
+
<CapabilityInfoButton label={copy.label} why={copy.why} />
|
|
109
|
+
) : null}
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
})}
|
|
113
|
+
</TooltipProvider>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function BuilderIncludedServices({
|
|
118
|
+
capabilities,
|
|
119
|
+
collapsible = false,
|
|
120
|
+
loading = false,
|
|
121
|
+
error,
|
|
122
|
+
testId,
|
|
123
|
+
}: {
|
|
124
|
+
capabilities: OnboardingCapability[];
|
|
125
|
+
collapsible?: boolean;
|
|
126
|
+
loading?: boolean;
|
|
127
|
+
error?: string | null;
|
|
128
|
+
testId?: string;
|
|
129
|
+
}) {
|
|
130
|
+
const t = useT();
|
|
131
|
+
|
|
132
|
+
if (!collapsible && capabilities.length === 0) return null;
|
|
133
|
+
if (collapsible && !loading && !error && capabilities.length === 0)
|
|
134
|
+
return null;
|
|
135
|
+
|
|
136
|
+
const content = loading ? (
|
|
137
|
+
<div className="grid gap-2 py-1" aria-label={t("agentChat.common.loading")}>
|
|
138
|
+
<Skeleton className="h-5 w-4/5" />
|
|
139
|
+
<Skeleton className="h-5 w-2/3" />
|
|
140
|
+
<Skeleton className="h-5 w-3/4" />
|
|
141
|
+
</div>
|
|
142
|
+
) : error ? (
|
|
143
|
+
<p role="status" className="py-1 text-xs text-muted-foreground">
|
|
144
|
+
{error}
|
|
145
|
+
</p>
|
|
146
|
+
) : (
|
|
147
|
+
<CapabilityRows capabilities={capabilities} />
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
if (collapsible) {
|
|
151
|
+
return (
|
|
152
|
+
<Collapsible
|
|
153
|
+
defaultOpen={false}
|
|
154
|
+
className="border-t border-border/60"
|
|
155
|
+
data-testid={testId}
|
|
156
|
+
>
|
|
157
|
+
<CollapsibleTrigger className="group flex w-full items-center justify-between gap-2 py-3 text-left text-sm font-medium text-muted-foreground outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring">
|
|
158
|
+
{t("agentChat.onboarding.builderIncludedServices", {
|
|
159
|
+
defaultValue: "Included services",
|
|
160
|
+
})}
|
|
161
|
+
<IconChevronDown
|
|
162
|
+
aria-hidden="true"
|
|
163
|
+
className="size-4 shrink-0 transition-transform group-data-[state=open]:rotate-180"
|
|
164
|
+
/>
|
|
165
|
+
</CollapsibleTrigger>
|
|
166
|
+
<CollapsibleContent className="grid gap-1 pb-1">
|
|
167
|
+
{content}
|
|
168
|
+
</CollapsibleContent>
|
|
169
|
+
</Collapsible>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<div className="flex flex-col gap-1" data-testid={testId}>
|
|
175
|
+
<p className="text-sm text-muted-foreground/70">What’s included</p>
|
|
176
|
+
<CapabilityRows capabilities={capabilities} />
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function BuilderIncludedBenefitsDisclosure({
|
|
182
|
+
capabilities,
|
|
183
|
+
includedLabel,
|
|
184
|
+
creditsLabel,
|
|
185
|
+
loading = false,
|
|
186
|
+
loadingLabel,
|
|
187
|
+
error,
|
|
188
|
+
testId,
|
|
189
|
+
}: {
|
|
190
|
+
capabilities: OnboardingCapability[];
|
|
191
|
+
includedLabel: string;
|
|
192
|
+
creditsLabel: string;
|
|
193
|
+
loading?: boolean;
|
|
194
|
+
loadingLabel: string;
|
|
195
|
+
error?: string | null;
|
|
196
|
+
testId?: string;
|
|
197
|
+
}) {
|
|
198
|
+
const t = useT();
|
|
199
|
+
const additionalServices = capabilities.filter(
|
|
200
|
+
(capability) => capability.id !== "llm" && capability.service !== "model",
|
|
201
|
+
);
|
|
202
|
+
const canExpand = loading || !!error || additionalServices.length > 0;
|
|
203
|
+
const serviceList = loading ? (
|
|
204
|
+
<div className="grid gap-2 py-1" aria-label={loadingLabel}>
|
|
205
|
+
<Skeleton className="h-5 w-4/5" />
|
|
206
|
+
<Skeleton className="h-5 w-2/3" />
|
|
207
|
+
<Skeleton className="h-5 w-3/4" />
|
|
208
|
+
</div>
|
|
209
|
+
) : error ? (
|
|
210
|
+
<p role="status" className="py-1 text-xs text-muted-foreground">
|
|
211
|
+
{error}
|
|
212
|
+
</p>
|
|
213
|
+
) : (
|
|
214
|
+
<CapabilityRows capabilities={additionalServices} />
|
|
215
|
+
);
|
|
216
|
+
const moreServicesLabel = t("agentChat.onboarding.builderMoreServices", {
|
|
217
|
+
defaultValue: "+ {{count}} more services",
|
|
218
|
+
count: additionalServices.length,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const summary = (
|
|
222
|
+
<span className="flex min-w-0 flex-1 flex-col gap-1">
|
|
223
|
+
<span className="text-[13px] font-semibold text-foreground">
|
|
224
|
+
{includedLabel}
|
|
225
|
+
</span>
|
|
226
|
+
<span className="text-xs font-medium text-emerald-700 dark:text-emerald-400">
|
|
227
|
+
{creditsLabel}
|
|
228
|
+
</span>
|
|
229
|
+
{additionalServices.length > 0 ? (
|
|
230
|
+
<span className="text-xs font-medium text-foreground dark:text-white">
|
|
231
|
+
{moreServicesLabel}
|
|
232
|
+
</span>
|
|
233
|
+
) : null}
|
|
234
|
+
</span>
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
if (!canExpand) {
|
|
238
|
+
return (
|
|
239
|
+
<div className="rounded-[10px] bg-emerald-50 px-4 py-3 dark:bg-emerald-950/30">
|
|
240
|
+
{summary}
|
|
241
|
+
</div>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<Collapsible
|
|
247
|
+
defaultOpen={false}
|
|
248
|
+
className="overflow-hidden rounded-[10px] bg-emerald-50 dark:bg-emerald-950/30"
|
|
249
|
+
data-testid={testId}
|
|
250
|
+
>
|
|
251
|
+
<CollapsibleTrigger className="group flex w-full items-start justify-between gap-3 px-4 py-3 text-left outline-none focus-visible:ring-2 focus-visible:ring-ring">
|
|
252
|
+
{summary}
|
|
253
|
+
<IconChevronDown
|
|
254
|
+
aria-hidden="true"
|
|
255
|
+
className="mt-0.5 size-4 shrink-0 text-muted-foreground transition-transform group-data-[state=open]:rotate-180"
|
|
256
|
+
/>
|
|
257
|
+
</CollapsibleTrigger>
|
|
258
|
+
<CollapsibleContent className="border-0 bg-muted px-2 py-2">
|
|
259
|
+
{serviceList}
|
|
260
|
+
</CollapsibleContent>
|
|
261
|
+
</Collapsible>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
@@ -109,6 +109,7 @@ describe("FirstRunOnboarding", () => {
|
|
|
109
109
|
required: true,
|
|
110
110
|
builderIncluded: true,
|
|
111
111
|
keySummary: "LLM provider key",
|
|
112
|
+
whyKey: "agentChat.onboarding.capability.llm.why",
|
|
112
113
|
why: "Needed for chat",
|
|
113
114
|
},
|
|
114
115
|
{
|
|
@@ -844,7 +845,7 @@ describe("FirstRunOnboarding", () => {
|
|
|
844
845
|
);
|
|
845
846
|
});
|
|
846
847
|
|
|
847
|
-
it("
|
|
848
|
+
it("shows included Builder.io services with descriptions in tooltips", () => {
|
|
848
849
|
act(() => {
|
|
849
850
|
root.render(
|
|
850
851
|
<TooltipProvider>
|
|
@@ -862,9 +863,11 @@ describe("FirstRunOnboarding", () => {
|
|
|
862
863
|
const builderCard = document.body
|
|
863
864
|
.querySelector("[data-testid='first-run-builder-create-account']")
|
|
864
865
|
?.closest("section");
|
|
865
|
-
const
|
|
866
|
-
|
|
866
|
+
const services = builderCard?.querySelector<HTMLElement>(
|
|
867
|
+
"[data-testid='first-run-builder-services']",
|
|
867
868
|
);
|
|
869
|
+
expect(services?.querySelector("details, summary")).toBeNull();
|
|
870
|
+
const included = services?.textContent ?? "";
|
|
868
871
|
// Every shared service, whether or not the app recommends it, plus the
|
|
869
872
|
// app's own headline capability Builder.io covers.
|
|
870
873
|
for (const service of [
|
|
@@ -878,6 +881,23 @@ describe("FirstRunOnboarding", () => {
|
|
|
878
881
|
]) {
|
|
879
882
|
expect(included).toContain(service);
|
|
880
883
|
}
|
|
884
|
+
expect(included).not.toContain("Needed for chat");
|
|
885
|
+
expect(included).not.toContain("Turns speech into text");
|
|
886
|
+
expect(
|
|
887
|
+
services?.querySelectorAll("button[aria-label^='About']").length,
|
|
888
|
+
).toBe(1);
|
|
889
|
+
const manualCard = document.body
|
|
890
|
+
.querySelector("[data-testid='first-run-open-key-settings']")
|
|
891
|
+
?.closest("section");
|
|
892
|
+
expect(builderCard?.textContent).not.toContain(
|
|
893
|
+
"Configure using Builder.io and use your account credits to power the app’s services.",
|
|
894
|
+
);
|
|
895
|
+
expect(manualCard?.textContent).not.toContain(
|
|
896
|
+
"Configure your own API keys and credentials to power the app’s services.",
|
|
897
|
+
);
|
|
898
|
+
expect(manualCard?.textContent).not.toContain("Design system intelligence");
|
|
899
|
+
expect(manualCard?.textContent).not.toContain("Background agents");
|
|
900
|
+
expect(manualCard?.querySelector("svg.tabler-icon-x")).toBeNull();
|
|
881
901
|
// Not Builder.io capabilities, and no per-app extras.
|
|
882
902
|
for (const missing of [
|
|
883
903
|
"Connected agents",
|
|
@@ -886,11 +906,6 @@ describe("FirstRunOnboarding", () => {
|
|
|
886
906
|
]) {
|
|
887
907
|
expect(document.body.textContent).not.toContain(missing);
|
|
888
908
|
}
|
|
889
|
-
expect(
|
|
890
|
-
[...document.body.querySelectorAll("button")].find((button) =>
|
|
891
|
-
button.textContent?.trim().endsWith("more"),
|
|
892
|
-
),
|
|
893
|
-
).toBeUndefined();
|
|
894
909
|
});
|
|
895
910
|
|
|
896
911
|
it("does not ask Mail users to connect Gmail again in manual setup", () => {
|
|
@@ -28,20 +28,8 @@ import type {
|
|
|
28
28
|
} from "@agent-native/core/onboarding/types";
|
|
29
29
|
import { Badge } from "@agent-native/toolkit/ui/badge";
|
|
30
30
|
import { Skeleton } from "@agent-native/toolkit/ui/skeleton";
|
|
31
|
-
import {
|
|
32
|
-
Tooltip,
|
|
33
|
-
TooltipContent,
|
|
34
|
-
TooltipTrigger,
|
|
35
|
-
} from "@agent-native/toolkit/ui/tooltip";
|
|
36
31
|
import { cn } from "@agent-native/toolkit/utils";
|
|
37
|
-
import {
|
|
38
|
-
IconArrowRight,
|
|
39
|
-
IconCheck,
|
|
40
|
-
IconInfoCircle,
|
|
41
|
-
IconKey,
|
|
42
|
-
IconLoader2,
|
|
43
|
-
IconX,
|
|
44
|
-
} from "@tabler/icons-react";
|
|
32
|
+
import { IconArrowRight, IconKey, IconLoader2 } from "@tabler/icons-react";
|
|
45
33
|
import React, {
|
|
46
34
|
useCallback,
|
|
47
35
|
useEffect,
|
|
@@ -52,6 +40,11 @@ import React, {
|
|
|
52
40
|
import { useLocation } from "react-router";
|
|
53
41
|
|
|
54
42
|
import { useBuilderConnectFlow } from "../settings/useBuilderStatus.js";
|
|
43
|
+
import {
|
|
44
|
+
BuilderIncludedServices,
|
|
45
|
+
CapabilityInfoButton,
|
|
46
|
+
getBuilderIncludedCapabilities,
|
|
47
|
+
} from "./BuilderIncludedServices.js";
|
|
55
48
|
import {
|
|
56
49
|
ONBOARDING_PRIMARY_BUTTON_CLASS,
|
|
57
50
|
OnboardingStepLayout,
|
|
@@ -523,10 +516,8 @@ export function FirstRunOnboarding({
|
|
|
523
516
|
|
|
524
517
|
// Every shared service Builder.io powers, the same list Infrastructure
|
|
525
518
|
// shows, plus the app's own headline capabilities it covers.
|
|
526
|
-
const builderCapabilities =
|
|
527
|
-
|
|
528
|
-
capability.builderIncluded &&
|
|
529
|
-
(!!capability.service || isHeadlineCapability(capability)),
|
|
519
|
+
const builderCapabilities = getBuilderIncludedCapabilities(
|
|
520
|
+
profile.capabilities,
|
|
530
521
|
);
|
|
531
522
|
const handleBuilder = (provisionAccount = canActivateBuilderFreeCredits) => {
|
|
532
523
|
if (previewMode) {
|
|
@@ -720,10 +711,6 @@ export function FirstRunOnboarding({
|
|
|
720
711
|
Recommended
|
|
721
712
|
</Badge>
|
|
722
713
|
</div>
|
|
723
|
-
<p className="text-sm text-muted-foreground">
|
|
724
|
-
Configure using Builder.io and use your account credits to
|
|
725
|
-
power the app’s services.
|
|
726
|
-
</p>
|
|
727
714
|
</div>
|
|
728
715
|
<div className="flex flex-col gap-1 rounded-[10px] bg-emerald-50 px-4 py-3 dark:bg-emerald-950/30">
|
|
729
716
|
<p className="text-[13px] font-semibold text-foreground">
|
|
@@ -733,40 +720,10 @@ export function FirstRunOnboarding({
|
|
|
733
720
|
{t("agentChat.onboarding.builderMonthlyCredits")}
|
|
734
721
|
</p>
|
|
735
722
|
</div>
|
|
736
|
-
<
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
{builderCapabilities.map((capability) => {
|
|
741
|
-
const copy = getCapabilityCopy(t, capability);
|
|
742
|
-
return (
|
|
743
|
-
<div
|
|
744
|
-
key={capability.id}
|
|
745
|
-
className="flex items-center gap-2 rounded-md px-2 py-1"
|
|
746
|
-
>
|
|
747
|
-
<IconCheck
|
|
748
|
-
className="shrink-0 text-muted-foreground"
|
|
749
|
-
size={15}
|
|
750
|
-
/>
|
|
751
|
-
<span className="flex-1 text-xs text-foreground">
|
|
752
|
-
{copy.label}
|
|
753
|
-
</span>
|
|
754
|
-
{capability.builderOnly && (
|
|
755
|
-
<CapabilityInfoButton
|
|
756
|
-
why={copy.why}
|
|
757
|
-
ariaLabel={t(
|
|
758
|
-
"agentChat.onboarding.capability.about",
|
|
759
|
-
{
|
|
760
|
-
defaultValue: "About {{label}}",
|
|
761
|
-
label: copy.label,
|
|
762
|
-
},
|
|
763
|
-
)}
|
|
764
|
-
/>
|
|
765
|
-
)}
|
|
766
|
-
</div>
|
|
767
|
-
);
|
|
768
|
-
})}
|
|
769
|
-
</div>
|
|
723
|
+
<BuilderIncludedServices
|
|
724
|
+
capabilities={builderCapabilities}
|
|
725
|
+
testId="first-run-builder-services"
|
|
726
|
+
/>
|
|
770
727
|
<div className="flex flex-col gap-2">
|
|
771
728
|
{canActivateBuilderFreeCredits && (
|
|
772
729
|
<button
|
|
@@ -805,10 +762,6 @@ export function FirstRunOnboarding({
|
|
|
805
762
|
<h2 className="text-lg font-bold text-foreground">
|
|
806
763
|
Configure manually
|
|
807
764
|
</h2>
|
|
808
|
-
<p className="text-sm text-muted-foreground">
|
|
809
|
-
Configure your own API keys and credentials to power the
|
|
810
|
-
app’s services.
|
|
811
|
-
</p>
|
|
812
765
|
</div>
|
|
813
766
|
<div className="flex flex-col gap-1 rounded-xl bg-muted px-4 py-3">
|
|
814
767
|
<p className="text-[13px] font-semibold text-foreground">
|
|
@@ -1148,11 +1101,11 @@ type CapabilityTranslator = (
|
|
|
1148
1101
|
|
|
1149
1102
|
type CapabilityCopy = Pick<
|
|
1150
1103
|
OnboardingCapability,
|
|
1151
|
-
"id" | "required" | "suggested"
|
|
1104
|
+
"id" | "required" | "suggested"
|
|
1152
1105
|
> & {
|
|
1153
1106
|
label: string;
|
|
1154
1107
|
keySummary: string;
|
|
1155
|
-
why: string;
|
|
1108
|
+
why: string | null;
|
|
1156
1109
|
};
|
|
1157
1110
|
|
|
1158
1111
|
function getCapabilityCopy(
|
|
@@ -1163,7 +1116,6 @@ function getCapabilityCopy(
|
|
|
1163
1116
|
id: capability.id,
|
|
1164
1117
|
required: capability.required,
|
|
1165
1118
|
suggested: capability.suggested,
|
|
1166
|
-
builderOnly: capability.builderOnly,
|
|
1167
1119
|
label: capability.labelKey
|
|
1168
1120
|
? t(capability.labelKey, { defaultValue: capability.label })
|
|
1169
1121
|
: capability.label,
|
|
@@ -1172,7 +1124,7 @@ function getCapabilityCopy(
|
|
|
1172
1124
|
: capability.keySummary,
|
|
1173
1125
|
why: capability.whyKey
|
|
1174
1126
|
? t(capability.whyKey, { defaultValue: capability.why })
|
|
1175
|
-
:
|
|
1127
|
+
: null,
|
|
1176
1128
|
};
|
|
1177
1129
|
}
|
|
1178
1130
|
|
|
@@ -1187,16 +1139,15 @@ function CapabilityList({
|
|
|
1187
1139
|
const visibleCapabilities = useMemo(() => {
|
|
1188
1140
|
const headline = capabilities.filter(
|
|
1189
1141
|
(capability) =>
|
|
1190
|
-
!capability.satisfiedBySignIn &&
|
|
1142
|
+
!capability.satisfiedBySignIn &&
|
|
1143
|
+
!capability.builderOnly &&
|
|
1144
|
+
(capability.required || capability.suggested),
|
|
1191
1145
|
);
|
|
1192
1146
|
const required = headline.filter((capability) => capability.required);
|
|
1193
1147
|
const suggested = headline.filter(
|
|
1194
1148
|
(capability) => !capability.required && capability.suggested,
|
|
1195
1149
|
);
|
|
1196
|
-
|
|
1197
|
-
(capability) => !capability.required && !capability.suggested,
|
|
1198
|
-
);
|
|
1199
|
-
return [...required, ...suggested, ...noManualPath];
|
|
1150
|
+
return [...required, ...suggested];
|
|
1200
1151
|
}, [capabilities]);
|
|
1201
1152
|
|
|
1202
1153
|
return (
|
|
@@ -1216,24 +1167,7 @@ function CapabilityList({
|
|
|
1216
1167
|
);
|
|
1217
1168
|
}
|
|
1218
1169
|
|
|
1219
|
-
function isHeadlineCapability(capability: OnboardingCapability): boolean {
|
|
1220
|
-
return (
|
|
1221
|
-
capability.required || !!capability.suggested || !!capability.builderOnly
|
|
1222
|
-
);
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
1170
|
function CapabilityRow({ copy }: { copy: CapabilityCopy }) {
|
|
1226
|
-
// Builder-only services have no bring-your-own path, so the manual list
|
|
1227
|
-
// shows them crossed out with no Required/Recommended tag instead of
|
|
1228
|
-
// mislabeling them "Optional".
|
|
1229
|
-
if (copy.builderOnly) {
|
|
1230
|
-
return (
|
|
1231
|
-
<div className="flex items-center gap-2 rounded-md px-2 py-1">
|
|
1232
|
-
<IconX className="shrink-0 text-muted-foreground" size={14} />
|
|
1233
|
-
<span className="flex-1 text-xs text-foreground">{copy.label}</span>
|
|
1234
|
-
</div>
|
|
1235
|
-
);
|
|
1236
|
-
}
|
|
1237
1171
|
return (
|
|
1238
1172
|
<div className="flex items-center gap-2 rounded-md px-2 py-1">
|
|
1239
1173
|
<IconKey className="shrink-0 text-muted-foreground" size={14} />
|
|
@@ -1243,6 +1177,9 @@ function CapabilityRow({ copy }: { copy: CapabilityCopy }) {
|
|
|
1243
1177
|
>
|
|
1244
1178
|
{copy.keySummary}
|
|
1245
1179
|
</span>
|
|
1180
|
+
{copy.why ? (
|
|
1181
|
+
<CapabilityInfoButton label={copy.label} why={copy.why} />
|
|
1182
|
+
) : null}
|
|
1246
1183
|
<span className="shrink-0 text-xs text-muted-foreground">
|
|
1247
1184
|
{copy.required ? "Required" : "Recommended"}
|
|
1248
1185
|
</span>
|
|
@@ -1250,33 +1187,6 @@ function CapabilityRow({ copy }: { copy: CapabilityCopy }) {
|
|
|
1250
1187
|
);
|
|
1251
1188
|
}
|
|
1252
1189
|
|
|
1253
|
-
function CapabilityInfoButton({
|
|
1254
|
-
why,
|
|
1255
|
-
ariaLabel,
|
|
1256
|
-
}: {
|
|
1257
|
-
why: string;
|
|
1258
|
-
ariaLabel: string;
|
|
1259
|
-
}) {
|
|
1260
|
-
return (
|
|
1261
|
-
<Tooltip>
|
|
1262
|
-
<TooltipTrigger asChild>
|
|
1263
|
-
<button
|
|
1264
|
-
type="button"
|
|
1265
|
-
aria-label={ariaLabel}
|
|
1266
|
-
className="inline-flex size-4 items-center justify-center rounded-full text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
1267
|
-
onClick={(event) => event.stopPropagation()}
|
|
1268
|
-
onKeyDown={(event) => event.stopPropagation()}
|
|
1269
|
-
>
|
|
1270
|
-
<IconInfoCircle size={13} />
|
|
1271
|
-
</button>
|
|
1272
|
-
</TooltipTrigger>
|
|
1273
|
-
<TooltipContent side="top" className="max-w-xs text-xs">
|
|
1274
|
-
{why}
|
|
1275
|
-
</TooltipContent>
|
|
1276
|
-
</Tooltip>
|
|
1277
|
-
);
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
1190
|
function FirstRunCompletionError({
|
|
1281
1191
|
message,
|
|
1282
1192
|
onRetry,
|
|
@@ -117,6 +117,7 @@ describe("McpConnectionSuggestion render", () => {
|
|
|
117
117
|
} else {
|
|
118
118
|
Reflect.deleteProperty(window, "localStorage");
|
|
119
119
|
}
|
|
120
|
+
vi.useRealTimers();
|
|
120
121
|
vi.restoreAllMocks();
|
|
121
122
|
vi.unstubAllGlobals();
|
|
122
123
|
});
|
|
@@ -142,6 +143,57 @@ describe("McpConnectionSuggestion render", () => {
|
|
|
142
143
|
|
|
143
144
|
act(() => (dismiss as HTMLButtonElement).click());
|
|
144
145
|
|
|
146
|
+
expect(container.querySelector("[data-mcp-connection-suggestion]")).toBe(
|
|
147
|
+
null,
|
|
148
|
+
);
|
|
149
|
+
const dismissals = JSON.parse(
|
|
150
|
+
window.localStorage.getItem(
|
|
151
|
+
"agent-native:mcp-connection-suggestions-dismissed",
|
|
152
|
+
) ?? "{}",
|
|
153
|
+
) as Record<string, number>;
|
|
154
|
+
expect(dismissals["test-integration"]).toEqual(expect.any(Number));
|
|
155
|
+
|
|
156
|
+
act(() => root.unmount());
|
|
157
|
+
root = createRoot(container);
|
|
158
|
+
renderSuggestion();
|
|
159
|
+
expect(container.querySelector("[data-mcp-connection-suggestion]")).toBe(
|
|
160
|
+
null,
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("shows a dismissed suggestion again after one week", () => {
|
|
165
|
+
vi.useFakeTimers();
|
|
166
|
+
const dismissedAt = new Date("2026-10-01T00:00:00.000Z").getTime();
|
|
167
|
+
vi.setSystemTime(dismissedAt);
|
|
168
|
+
window.localStorage.setItem(
|
|
169
|
+
"agent-native:mcp-connection-suggestions-dismissed",
|
|
170
|
+
JSON.stringify({ "test-integration": dismissedAt }),
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
renderSuggestion();
|
|
174
|
+
|
|
175
|
+
expect(container.querySelector("[data-mcp-connection-suggestion]")).toBe(
|
|
176
|
+
null,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
act(() => vi.advanceTimersByTime(7 * 24 * 60 * 60 * 1000));
|
|
180
|
+
|
|
181
|
+
expect(
|
|
182
|
+
container.querySelector("[data-mcp-connection-suggestion]"),
|
|
183
|
+
).not.toBeNull();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("migrates existing dismissal IDs and preserves them across mounts", () => {
|
|
187
|
+
vi.useFakeTimers();
|
|
188
|
+
const dismissedAt = new Date("2026-10-01T00:00:00.000Z").getTime();
|
|
189
|
+
vi.setSystemTime(dismissedAt);
|
|
190
|
+
window.localStorage.setItem(
|
|
191
|
+
"agent-native:mcp-connection-suggestions-dismissed",
|
|
192
|
+
JSON.stringify(["test-integration"]),
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
renderSuggestion();
|
|
196
|
+
|
|
145
197
|
expect(container.querySelector("[data-mcp-connection-suggestion]")).toBe(
|
|
146
198
|
null,
|
|
147
199
|
);
|
|
@@ -149,9 +201,9 @@ describe("McpConnectionSuggestion render", () => {
|
|
|
149
201
|
JSON.parse(
|
|
150
202
|
window.localStorage.getItem(
|
|
151
203
|
"agent-native:mcp-connection-suggestions-dismissed",
|
|
152
|
-
) ?? "
|
|
204
|
+
) ?? "{}",
|
|
153
205
|
),
|
|
154
|
-
).toEqual(
|
|
206
|
+
).toEqual({ "test-integration": dismissedAt });
|
|
155
207
|
|
|
156
208
|
act(() => root.unmount());
|
|
157
209
|
root = createRoot(container);
|