@agent-native/toolkit 0.221.0-same-host-mcp-20261027-20261027081850 → 0.221.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 +16 -1
- package/dist/app/agentkit/react/components.d.ts +5 -3
- package/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +103 -42
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/agentkit/react/styles.css +93 -65
- package/dist/app/agentkit/react/voice-transcripts.d.ts +22 -1
- package/dist/app/agentkit/react/voice-transcripts.d.ts.map +1 -1
- package/dist/app/agentkit/react/voice-transcripts.js +209 -8
- package/dist/app/agentkit/react/voice-transcripts.js.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +55 -40
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/agentkit-chat/guided-questions.d.ts +1 -0
- package/dist/app/chat/agentkit-chat/guided-questions.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/guided-questions.js +18 -6
- package/dist/app/chat/agentkit-chat/guided-questions.js.map +1 -1
- package/dist/app/chat/agentkit-chat/index.d.ts +1 -1
- package/dist/app/chat/agentkit-chat/index.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/index.js +1 -1
- package/dist/app/chat/agentkit-chat/index.js.map +1 -1
- package/dist/app/chat/chat/run-recovery.d.ts.map +1 -1
- package/dist/app/chat/chat/run-recovery.js +5 -1
- package/dist/app/chat/chat/run-recovery.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/reply-connection-suggestion.d.ts +29 -0
- package/dist/app/chat/reply-connection-suggestion.d.ts.map +1 -0
- package/dist/app/chat/reply-connection-suggestion.js +42 -0
- package/dist/app/chat/reply-connection-suggestion.js.map +1 -0
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +7 -0
- 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 +7 -0
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +7 -0
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +7 -0
- 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 +7 -0
- 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 +7 -0
- 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 +7 -0
- 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 +7 -0
- 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 +7 -0
- 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 +7 -0
- 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 +7 -0
- 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 +7 -0
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/mcp-approval/McpApprovalReview.d.ts +25 -0
- package/dist/app/mcp-approval/McpApprovalReview.d.ts.map +1 -0
- package/dist/app/mcp-approval/McpApprovalReview.js +342 -0
- package/dist/app/mcp-approval/McpApprovalReview.js.map +1 -0
- package/dist/app/onboarding/FirstRunOnboarding.d.ts.map +1 -1
- package/dist/app/onboarding/FirstRunOnboarding.js +299 -71
- package/dist/app/onboarding/FirstRunOnboarding.js.map +1 -1
- package/dist/app/onboarding/GettingStartedCard.js +1 -1
- package/dist/app/onboarding/GettingStartedCard.js.map +1 -1
- package/dist/app/onboarding/first-run-builder-credits-flag.d.ts +2 -0
- package/dist/app/onboarding/first-run-builder-credits-flag.d.ts.map +1 -0
- package/dist/app/onboarding/first-run-builder-credits-flag.js +8 -0
- package/dist/app/onboarding/first-run-builder-credits-flag.js.map +1 -0
- package/dist/app/providers/AppProviders.d.ts.map +1 -1
- package/dist/app/providers/AppProviders.js +14 -1
- package/dist/app/providers/AppProviders.js.map +1 -1
- package/dist/app/resources/McpConnectionSuggestion.d.ts +2 -4
- package/dist/app/resources/McpConnectionSuggestion.d.ts.map +1 -1
- package/dist/app/resources/McpConnectionSuggestion.js +59 -36
- package/dist/app/resources/McpConnectionSuggestion.js.map +1 -1
- package/dist/app/settings/account/PreferencesSettings.d.ts.map +1 -1
- package/dist/app/settings/account/PreferencesSettings.js +14 -1
- package/dist/app/settings/account/PreferencesSettings.js.map +1 -1
- package/dist/app/settings/account/ThemeFamilySettings.js +2 -2
- package/dist/app/settings/account/ThemeFamilySettings.js.map +1 -1
- package/dist/app/settings/account/search-entries.d.ts +1 -0
- package/dist/app/settings/account/search-entries.d.ts.map +1 -1
- package/dist/app/settings/account/search-entries.js +5 -0
- package/dist/app/settings/account/search-entries.js.map +1 -1
- package/dist/app/settings/shell/SettingsShell.d.ts.map +1 -1
- package/dist/app/settings/shell/SettingsShell.js +26 -2
- package/dist/app/settings/shell/SettingsShell.js.map +1 -1
- package/dist/app/shared/AppShellSkeleton.d.ts.map +1 -1
- package/dist/app/shared/AppShellSkeleton.js +4 -10
- package/dist/app/shared/AppShellSkeleton.js.map +1 -1
- package/dist/app/shared/EnvironmentBadge.d.ts +2 -1
- package/dist/app/shared/EnvironmentBadge.d.ts.map +1 -1
- package/dist/app/shared/EnvironmentBadge.js +2 -3
- package/dist/app/shared/EnvironmentBadge.js.map +1 -1
- package/dist/app/shared/is-builder-io-employee.d.ts +8 -0
- package/dist/app/shared/is-builder-io-employee.d.ts.map +1 -0
- package/dist/app/shared/is-builder-io-employee.js +10 -0
- package/dist/app/shared/is-builder-io-employee.js.map +1 -0
- package/dist/app/styles/agent-conversation.css +30 -17
- package/dist/app/styles/theme-families.css +22 -5
- package/dist/app/styles/tokens/agent-kit.css +3 -3
- package/dist/composer/PromptComposer.d.ts +15 -3
- package/dist/composer/PromptComposer.d.ts.map +1 -1
- package/dist/composer/PromptComposer.js +74 -16
- package/dist/composer/PromptComposer.js.map +1 -1
- package/dist/composer/RealtimeVoiceMode.d.ts +7 -0
- package/dist/composer/RealtimeVoiceMode.d.ts.map +1 -1
- package/dist/composer/RealtimeVoiceMode.js +9 -2
- package/dist/composer/RealtimeVoiceMode.js.map +1 -1
- package/dist/composer/TiptapComposer.d.ts +7 -0
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +12 -4
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/dist/composer/VoiceButton.d.ts +2 -0
- package/dist/composer/VoiceButton.d.ts.map +1 -1
- package/dist/composer/VoiceButton.js +5 -4
- package/dist/composer/VoiceButton.js.map +1 -1
- package/dist/composer/index.d.ts +2 -2
- package/dist/composer/index.d.ts.map +1 -1
- package/dist/composer/index.js +1 -1
- package/dist/composer/index.js.map +1 -1
- package/dist/composer/prompt-attachments.d.ts +20 -0
- package/dist/composer/prompt-attachments.d.ts.map +1 -1
- package/dist/composer/prompt-attachments.js +188 -11
- package/dist/composer/prompt-attachments.js.map +1 -1
- package/dist/composer/realtime-voice-transcript.d.ts +27 -0
- package/dist/composer/realtime-voice-transcript.d.ts.map +1 -1
- package/dist/composer/realtime-voice-transcript.js +34 -1
- package/dist/composer/realtime-voice-transcript.js.map +1 -1
- package/dist/composer/realtime-voice-turn-metrics.d.ts +43 -0
- package/dist/composer/realtime-voice-turn-metrics.d.ts.map +1 -0
- package/dist/composer/realtime-voice-turn-metrics.js +132 -0
- package/dist/composer/realtime-voice-turn-metrics.js.map +1 -0
- package/dist/composer/runtime-adapters.d.ts +4 -1
- package/dist/composer/runtime-adapters.d.ts.map +1 -1
- package/dist/composer/runtime-adapters.js +2 -1
- package/dist/composer/runtime-adapters.js.map +1 -1
- package/dist/composer/useRealtimeVoiceMode.d.ts +61 -9
- package/dist/composer/useRealtimeVoiceMode.d.ts.map +1 -1
- package/dist/composer/useRealtimeVoiceMode.js +536 -99
- package/dist/composer/useRealtimeVoiceMode.js.map +1 -1
- package/dist/composer/voice-chat-icon.d.ts +7 -0
- package/dist/composer/voice-chat-icon.d.ts.map +1 -0
- package/dist/composer/voice-chat-icon.js +14 -0
- package/dist/composer/voice-chat-icon.js.map +1 -0
- package/dist/editor/DragHandle.d.ts +12 -1
- package/dist/editor/DragHandle.d.ts.map +1 -1
- package/dist/editor/DragHandle.js +165 -41
- package/dist/editor/DragHandle.js.map +1 -1
- package/dist/editor/RegistryBlockNode.d.ts +20 -0
- package/dist/editor/RegistryBlockNode.d.ts.map +1 -1
- package/dist/editor/RegistryBlockNode.js +58 -13
- package/dist/editor/RegistryBlockNode.js.map +1 -1
- package/dist/editor/index.d.ts +2 -2
- package/dist/editor/index.d.ts.map +1 -1
- package/dist/editor/index.js +1 -1
- package/dist/editor/index.js.map +1 -1
- package/dist/styles.css +2 -0
- package/dist/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/ui/dropdown-menu.js +5 -1
- package/dist/ui/dropdown-menu.js.map +1 -1
- package/package.json +14 -4
- package/src/app/agentkit/react/components.regenerate-readiness.spec.tsx +3 -1
- package/src/app/agentkit/react/components.spec.tsx +24 -4
- package/src/app/agentkit/react/components.tsx +174 -44
- package/src/app/agentkit/react/composer-context.spec.tsx +3 -0
- package/src/app/agentkit/react/disclosures.spec.tsx +124 -0
- package/src/app/agentkit/react/lifecycle.spec.tsx +8 -2
- package/src/app/agentkit/react/message-actions.spec.tsx +161 -5
- package/src/app/agentkit/react/saved-reply-reload.spec.tsx +206 -0
- package/src/app/agentkit/react/styles.css +93 -65
- package/src/app/agentkit/react/voice-transcripts.spec.tsx +239 -4
- package/src/app/agentkit/react/voice-transcripts.ts +245 -8
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +59 -11
- package/src/app/chat/AgentKitAssistantChat.tsx +74 -62
- package/src/app/chat/agentkit-chat/guided-answers-context.spec.ts +9 -0
- package/src/app/chat/agentkit-chat/guided-questions.tsx +17 -7
- package/src/app/chat/agentkit-chat/index.ts +1 -0
- package/src/app/chat/chat/run-recovery.spec.tsx +45 -0
- package/src/app/chat/chat/run-recovery.tsx +6 -1
- package/src/app/chat/composer/runtime-adapters.tsx +2 -0
- package/src/app/chat/reply-connection-suggestion.spec.ts +121 -0
- package/src/app/chat/reply-connection-suggestion.ts +76 -0
- package/src/app/i18n/catalogs/ar-SA.ts +10 -0
- package/src/app/i18n/catalogs/de-DE.ts +10 -0
- package/src/app/i18n/catalogs/en-US.ts +10 -0
- package/src/app/i18n/catalogs/es-ES.ts +10 -0
- package/src/app/i18n/catalogs/fr-FR.ts +10 -0
- package/src/app/i18n/catalogs/hi-IN.ts +10 -0
- package/src/app/i18n/catalogs/ja-JP.ts +10 -0
- package/src/app/i18n/catalogs/ko-KR.ts +10 -0
- package/src/app/i18n/catalogs/pt-BR.ts +10 -0
- package/src/app/i18n/catalogs/zh-CN.ts +10 -0
- package/src/app/i18n/catalogs/zh-TW.ts +10 -0
- package/src/app/mcp-approval/McpApprovalReview.spec.tsx +687 -0
- package/src/app/mcp-approval/McpApprovalReview.tsx +913 -0
- package/src/app/onboarding/FirstRunOnboarding.spec.tsx +736 -26
- package/src/app/onboarding/FirstRunOnboarding.tsx +473 -145
- package/src/app/onboarding/GettingStartedCard.tsx +1 -1
- package/src/app/onboarding/first-run-builder-credits-flag.spec.ts +13 -0
- package/src/app/onboarding/first-run-builder-credits-flag.ts +9 -0
- package/src/app/providers/AppProviders.spec.tsx +109 -0
- package/src/app/providers/AppProviders.tsx +53 -1
- package/src/app/resources/McpConnectionSuggestion.spec.ts +77 -12
- package/src/app/resources/McpConnectionSuggestion.tsx +69 -51
- package/src/app/settings/VoiceChatSettings.spec.tsx +6 -1
- package/src/app/settings/account/PreferencesSettings.spec.tsx +73 -0
- package/src/app/settings/account/PreferencesSettings.tsx +24 -7
- package/src/app/settings/account/ThemeFamilySettings.tsx +2 -2
- package/src/app/settings/account/search-entries.spec.ts +14 -0
- package/src/app/settings/account/search-entries.ts +9 -0
- package/src/app/settings/shell/SettingsShell.tsx +29 -2
- package/src/app/shared/AppShellSkeleton.spec.tsx +2 -2
- package/src/app/shared/AppShellSkeleton.tsx +3 -16
- package/src/app/shared/EnvironmentBadge.tsx +3 -4
- package/src/app/shared/is-builder-io-employee.ts +17 -0
- package/src/app/styles/agent-conversation.css +30 -17
- package/src/app/styles/theme-families.css +22 -5
- package/src/app/styles/theme-families.spec.ts +73 -0
- package/src/app/styles/tokens/agent-kit.css +3 -3
- package/src/composer/AgentSuggestionBar.overflow.spec.tsx +1 -1
- package/src/composer/AttachmentChip.spec.tsx +87 -0
- package/src/composer/PromptComposer.context.spec.tsx +8 -4
- package/src/composer/PromptComposer.setup-card.spec.tsx +170 -0
- package/src/composer/PromptComposer.tsx +125 -14
- package/src/composer/RealtimeVoiceMode.spec.tsx +48 -0
- package/src/composer/RealtimeVoiceMode.tsx +45 -2
- package/src/composer/TiptapComposer.tsx +25 -0
- package/src/composer/TiptapComposer.voice-toolbar.spec.tsx +4 -1
- package/src/composer/VoiceButton.tsx +8 -3
- package/src/composer/index.ts +4 -0
- package/src/composer/prompt-attachments.spec.ts +160 -8
- package/src/composer/prompt-attachments.ts +221 -12
- package/src/composer/realtime-voice-transcript.spec.ts +36 -0
- package/src/composer/realtime-voice-transcript.ts +56 -1
- package/src/composer/realtime-voice-turn-metrics.spec.ts +175 -0
- package/src/composer/realtime-voice-turn-metrics.ts +192 -0
- package/src/composer/runtime-adapters.tsx +5 -1
- package/src/composer/useRealtimeVoiceMode.session.spec.tsx +487 -2
- package/src/composer/useRealtimeVoiceMode.spec.ts +267 -65
- package/src/composer/useRealtimeVoiceMode.tsx +716 -139
- package/src/composer/voice-chat-icon.tsx +19 -0
- package/src/editor/DragHandle.spec.ts +217 -0
- package/src/editor/DragHandle.ts +214 -55
- package/src/editor/RegistryBlockNode.spec.ts +172 -2
- package/src/editor/RegistryBlockNode.tsx +91 -13
- package/src/editor/index.ts +4 -0
- package/src/styles.css +2 -0
- package/src/ui/dropdown-menu.tsx +4 -0
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
useOnboardingPreviewMode,
|
|
19
19
|
useOnboardingPreviewStep,
|
|
20
20
|
} from "@agent-native/core/client/onboarding/use-preview-mode";
|
|
21
|
+
import { useActionMutation } from "@agent-native/core/client/use-action";
|
|
21
22
|
import { SETTINGS_REDESIGN_FLAG } from "@agent-native/core/feature-flags/registry";
|
|
22
23
|
import {
|
|
23
24
|
buildSettingsRoute,
|
|
@@ -27,6 +28,10 @@ import type {
|
|
|
27
28
|
OnboardingAppProfile,
|
|
28
29
|
OnboardingCapability,
|
|
29
30
|
} from "@agent-native/core/onboarding/types";
|
|
31
|
+
import {
|
|
32
|
+
FIRST_RUN_BUILDER_CREDITS_V2_EXPERIMENT_ID,
|
|
33
|
+
FIRST_RUN_BUILDER_CREDITS_V2_FLAG_KEY,
|
|
34
|
+
} from "@agent-native/core/shared";
|
|
30
35
|
import { Badge } from "@agent-native/toolkit/ui/badge";
|
|
31
36
|
import { Button } from "@agent-native/toolkit/ui/button";
|
|
32
37
|
import {
|
|
@@ -43,6 +48,7 @@ import {
|
|
|
43
48
|
} from "@agent-native/toolkit/ui/tooltip";
|
|
44
49
|
import { cn } from "@agent-native/toolkit/utils";
|
|
45
50
|
import {
|
|
51
|
+
IconAlertCircle,
|
|
46
52
|
IconArrowRight,
|
|
47
53
|
IconChevronDown,
|
|
48
54
|
IconChevronUp,
|
|
@@ -77,6 +83,7 @@ import {
|
|
|
77
83
|
|
|
78
84
|
type FirstRunScreen = "choice" | "role" | "connecting" | "extension";
|
|
79
85
|
type FirstRunExtensionPlacement = "before-setup" | "after-setup";
|
|
86
|
+
const FIRST_RUN_CREDITS_V2_APP_IDS = new Set(["clips", "design", "slides"]);
|
|
80
87
|
type FirstRunSetupMethodId =
|
|
81
88
|
| "builder_create_account"
|
|
82
89
|
| "builder_sign_in"
|
|
@@ -118,6 +125,7 @@ function firstRunStepProperties(
|
|
|
118
125
|
extensionIndex: number,
|
|
119
126
|
extensionStepIndex: number,
|
|
120
127
|
setupSkippable: boolean,
|
|
128
|
+
setupRecoverySkippable = false,
|
|
121
129
|
): Record<string, unknown> {
|
|
122
130
|
// `optional` must match whether the screen offers a skip: Analytics funnels
|
|
123
131
|
// read it to keep a skip from counting as drop-off.
|
|
@@ -156,7 +164,10 @@ function firstRunStepProperties(
|
|
|
156
164
|
flow: "first_run",
|
|
157
165
|
step_id: screen,
|
|
158
166
|
step_index: stepIndex,
|
|
159
|
-
optional:
|
|
167
|
+
optional:
|
|
168
|
+
screen === "role" ||
|
|
169
|
+
(screen === "choice" && setupSkippable) ||
|
|
170
|
+
(screen === "connecting" && setupRecoverySkippable),
|
|
160
171
|
};
|
|
161
172
|
}
|
|
162
173
|
|
|
@@ -222,17 +233,68 @@ export function FirstRunOnboarding({
|
|
|
222
233
|
const [setupExperimentDecision, setSetupExperimentDecision] = useState<
|
|
223
234
|
boolean | null
|
|
224
235
|
>(null);
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
236
|
+
const [creditsV2Assignment, setCreditsV2Assignment] = useState<{
|
|
237
|
+
experiment_id: typeof FIRST_RUN_BUILDER_CREDITS_V2_EXPERIMENT_ID;
|
|
238
|
+
experiment_variant: "control" | "credits";
|
|
239
|
+
} | null>(null);
|
|
240
|
+
const [creditsV2ExposureTracked, setCreditsV2ExposureTracked] =
|
|
241
|
+
useState(false);
|
|
242
|
+
const creditsV2Flag = useFeatureFlagState(
|
|
243
|
+
FIRST_RUN_BUILDER_CREDITS_V2_FLAG_KEY,
|
|
230
244
|
);
|
|
245
|
+
const creditsV2FlagAppliesToApp = FIRST_RUN_CREDITS_V2_APP_IDS.has(
|
|
246
|
+
profile?.appId ?? "",
|
|
247
|
+
);
|
|
248
|
+
const creditsV2Exposure = useActionMutation<
|
|
249
|
+
{
|
|
250
|
+
assignment: {
|
|
251
|
+
experiment_id: string;
|
|
252
|
+
experiment_variant: string;
|
|
253
|
+
} | null;
|
|
254
|
+
},
|
|
255
|
+
{ confirmedVariant?: "control" | "credits" }
|
|
256
|
+
>("record-first-run-builder-credits-v2-exposure" as never, {
|
|
257
|
+
uiOnly: true,
|
|
258
|
+
skipActionQueryInvalidation: true,
|
|
259
|
+
});
|
|
260
|
+
const previewCreditsV2 =
|
|
261
|
+
previewMode &&
|
|
262
|
+
typeof window !== "undefined" &&
|
|
263
|
+
new URLSearchParams(window.location.search).get("experiment") === "v2";
|
|
264
|
+
const creditsV2Enabled =
|
|
265
|
+
creditsV2FlagAppliesToApp &&
|
|
266
|
+
creditsV2Flag.status === "ready" &&
|
|
267
|
+
creditsV2Flag.enabled;
|
|
268
|
+
const creditsV2FlowEnabled = creditsV2Enabled || previewCreditsV2;
|
|
269
|
+
const creditsV2FlagPending =
|
|
270
|
+
!previewMode &&
|
|
271
|
+
creditsV2FlagAppliesToApp &&
|
|
272
|
+
creditsV2Flag.status === "loading";
|
|
273
|
+
const setupVariant = useMemo(() => {
|
|
274
|
+
const previewVariant =
|
|
275
|
+
previewMode && getPreviewFirstRunSetupVariant(window.location.search);
|
|
276
|
+
if (previewCreditsV2) return previewVariant;
|
|
277
|
+
if (previewVariant) return previewVariant;
|
|
278
|
+
if (creditsV2FlowEnabled) {
|
|
279
|
+
return creditsV2Assignment?.experiment_variant ?? null;
|
|
280
|
+
}
|
|
281
|
+
return assignFirstRunSetupVariant();
|
|
282
|
+
}, [
|
|
283
|
+
creditsV2FlowEnabled,
|
|
284
|
+
creditsV2Assignment,
|
|
285
|
+
previewCreditsV2,
|
|
286
|
+
previewMode,
|
|
287
|
+
profile?.appId,
|
|
288
|
+
]);
|
|
289
|
+
const setupExperimentId = creditsV2FlowEnabled
|
|
290
|
+
? FIRST_RUN_BUILDER_CREDITS_V2_EXPERIMENT_ID
|
|
291
|
+
: FIRST_RUN_SETUP_EXPERIMENT_ID;
|
|
231
292
|
// The credits arm offers Skip in every app, so analytics must mark the choice step optional there too.
|
|
232
293
|
const choiceSkippable =
|
|
233
294
|
setupSkippable ||
|
|
234
295
|
(setupVariant === "credits" &&
|
|
235
|
-
(previewMode || setupExperimentDecision === true)
|
|
296
|
+
(previewMode || setupExperimentDecision === true) &&
|
|
297
|
+
(!creditsV2Enabled || creditsV2ExposureTracked));
|
|
236
298
|
const [extensionPlacement, setExtensionPlacement] =
|
|
237
299
|
useState<FirstRunExtensionPlacement>("after-setup");
|
|
238
300
|
const [extensionIndex, setExtensionIndex] = useState(0);
|
|
@@ -332,6 +394,7 @@ export function FirstRunOnboarding({
|
|
|
332
394
|
reason = "user_action",
|
|
333
395
|
stepExtensionIndex = extensionIndex,
|
|
334
396
|
stepIndex = extensionStepIndex,
|
|
397
|
+
optionalOverride?: boolean,
|
|
335
398
|
) => {
|
|
336
399
|
if (previewMode) return;
|
|
337
400
|
trackOnboardingEvent("onboarding_step_skipped", {
|
|
@@ -344,6 +407,9 @@ export function FirstRunOnboarding({
|
|
|
344
407
|
stepIndex,
|
|
345
408
|
choiceSkippable,
|
|
346
409
|
),
|
|
410
|
+
...(optionalOverride !== undefined
|
|
411
|
+
? { optional: optionalOverride }
|
|
412
|
+
: {}),
|
|
347
413
|
reason,
|
|
348
414
|
});
|
|
349
415
|
},
|
|
@@ -365,6 +431,12 @@ export function FirstRunOnboarding({
|
|
|
365
431
|
const completionInFlightRef = useRef(false);
|
|
366
432
|
const completionRedirectRef = useRef<string | null>(null);
|
|
367
433
|
const setupSkipStartedRef = useRef(false);
|
|
434
|
+
const creditsV2ExposureStartedRef = useRef(false);
|
|
435
|
+
const creditsV2ExposureFailedRef = useRef(false);
|
|
436
|
+
const creditsV2ExposureSettledRef = useRef(false);
|
|
437
|
+
const creditsV2ExposureTrackedRef = useRef(false);
|
|
438
|
+
const creditsV2ExposureConfirmationStartedRef = useRef(false);
|
|
439
|
+
const setupExperimentTimedOutRef = useRef(false);
|
|
368
440
|
const onboardingTerminalRef = useRef(false);
|
|
369
441
|
const abandonmentTrackedRef = useRef(false);
|
|
370
442
|
const onboardingStartedRef = useRef(false);
|
|
@@ -437,14 +509,150 @@ export function FirstRunOnboarding({
|
|
|
437
509
|
trackOnboardingEvent("onboarding_started", { flow: "first_run" });
|
|
438
510
|
}
|
|
439
511
|
}, [canTrackOnboardingScreen, firstRun, previewMode]);
|
|
440
|
-
//
|
|
441
|
-
// credits arm's skip depends on it, so the choice view waits for the decision
|
|
442
|
-
// and is sent once with its resolved optional flag. Without an assigned arm
|
|
443
|
-
// no decision is ever made, and the choice UI renders as is.
|
|
512
|
+
// The v2 choice event waits until the rendered arm has been attributed.
|
|
444
513
|
const choiceStepKnown =
|
|
445
514
|
screen !== "choice" ||
|
|
446
|
-
|
|
447
|
-
|
|
515
|
+
(!creditsV2FlagPending &&
|
|
516
|
+
(!creditsV2FlowEnabled || setupExperimentDecision !== null) &&
|
|
517
|
+
(setupVariant === null || setupExperimentDecision !== null) &&
|
|
518
|
+
(!creditsV2Enabled ||
|
|
519
|
+
setupExperimentDecision !== true ||
|
|
520
|
+
creditsV2ExposureTracked));
|
|
521
|
+
const beginBeforeSetup = useCallback(() => {
|
|
522
|
+
if (beforeSetupExtensions.length === 0) {
|
|
523
|
+
setScreen("choice");
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
setExtensionPlacement("before-setup");
|
|
527
|
+
setExtensionIndex(0);
|
|
528
|
+
setExtensionStepIndex(0);
|
|
529
|
+
setScreen("extension");
|
|
530
|
+
}, [beforeSetupExtensions.length]);
|
|
531
|
+
const handleFinish = useCallback(
|
|
532
|
+
(completedScreen: FirstRunScreen | null, track = true) => {
|
|
533
|
+
if (afterSetupExtensions.length === 0) {
|
|
534
|
+
void finishOnboarding(completedScreen);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
if (completedScreen && track) trackFirstRunStepCompleted(completedScreen);
|
|
538
|
+
setExtensionPlacement("after-setup");
|
|
539
|
+
setExtensionIndex(0);
|
|
540
|
+
setExtensionStepIndex(0);
|
|
541
|
+
setScreen("extension");
|
|
542
|
+
},
|
|
543
|
+
[afterSetupExtensions.length, finishOnboarding, trackFirstRunStepCompleted],
|
|
544
|
+
);
|
|
545
|
+
const skipFailedBuilderSetup = useCallback(() => {
|
|
546
|
+
if (setupSkipStartedRef.current || completionInFlightRef.current) return;
|
|
547
|
+
setupSkipStartedRef.current = true;
|
|
548
|
+
trackFirstRunStepSkipped(
|
|
549
|
+
"connecting",
|
|
550
|
+
"builder_provision_failed",
|
|
551
|
+
extensionIndex,
|
|
552
|
+
extensionStepIndex,
|
|
553
|
+
true,
|
|
554
|
+
);
|
|
555
|
+
if (setupSkippable) completionRedirectRef.current = "/record";
|
|
556
|
+
handleFinish(null);
|
|
557
|
+
}, [
|
|
558
|
+
extensionIndex,
|
|
559
|
+
extensionStepIndex,
|
|
560
|
+
handleFinish,
|
|
561
|
+
setupSkippable,
|
|
562
|
+
trackFirstRunStepSkipped,
|
|
563
|
+
]);
|
|
564
|
+
const handleBuilderConnected = useCallback(() => {
|
|
565
|
+
trackFirstRunSetupOutcome(builderSetupAttemptRef.current, "connected");
|
|
566
|
+
builderSetupAttemptRef.current = null;
|
|
567
|
+
setupAttemptRef.current = null;
|
|
568
|
+
trackFirstRunStepCompleted("choice");
|
|
569
|
+
trackFirstRunStepCompleted("connecting");
|
|
570
|
+
handleFinish(null);
|
|
571
|
+
}, [handleFinish, trackFirstRunStepCompleted]);
|
|
572
|
+
const connectFlow = useBuilderConnectFlow({
|
|
573
|
+
enabled: firstRun && !previewMode,
|
|
574
|
+
provisionAccount: true,
|
|
575
|
+
trackingSource: "first_run_onboarding",
|
|
576
|
+
trackingFlow: "connect_llm",
|
|
577
|
+
onConnected: handleBuilderConnected,
|
|
578
|
+
});
|
|
579
|
+
const accountExists = connectFlow.accountExists && !connectFlow.connecting;
|
|
580
|
+
const provisioning = builderConnectionMode === "provision" && !accountExists;
|
|
581
|
+
const provisioningFailed =
|
|
582
|
+
provisioning &&
|
|
583
|
+
!connectFlow.connecting &&
|
|
584
|
+
Boolean(connectFlow.terminalError);
|
|
585
|
+
const connectionFailed = provisioningFailed;
|
|
586
|
+
useEffect(() => {
|
|
587
|
+
if (
|
|
588
|
+
previewMode ||
|
|
589
|
+
!firstRun ||
|
|
590
|
+
!canTrackOnboardingScreen ||
|
|
591
|
+
screen !== "choice" ||
|
|
592
|
+
!creditsV2Enabled ||
|
|
593
|
+
setupExperimentDecision !== true ||
|
|
594
|
+
creditsV2ExposureTrackedRef.current ||
|
|
595
|
+
!creditsV2Assignment
|
|
596
|
+
) {
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
creditsV2ExposureTrackedRef.current = true;
|
|
600
|
+
setOnboardingExperiment({
|
|
601
|
+
id: creditsV2Assignment.experiment_id,
|
|
602
|
+
variant: creditsV2Assignment.experiment_variant,
|
|
603
|
+
});
|
|
604
|
+
setCreditsV2ExposureTracked(true);
|
|
605
|
+
trackOnboardingEvent("onboarding_experiment_exposed", {
|
|
606
|
+
flow: "first_run",
|
|
607
|
+
step_id: "choice",
|
|
608
|
+
experiment_id: creditsV2Assignment.experiment_id,
|
|
609
|
+
experiment_variant: creditsV2Assignment.experiment_variant,
|
|
610
|
+
});
|
|
611
|
+
if (creditsV2ExposureConfirmationStartedRef.current) return;
|
|
612
|
+
creditsV2ExposureConfirmationStartedRef.current = true;
|
|
613
|
+
const persistExposure = async () => {
|
|
614
|
+
let lastError: unknown;
|
|
615
|
+
// A write can commit even when its response is lost, so retry the idempotent assignment to reconcile it.
|
|
616
|
+
for (const delayMs of [0, 0, 1_000, 5_000, 30_000]) {
|
|
617
|
+
if (delayMs > 0) {
|
|
618
|
+
await new Promise<void>((resolve) =>
|
|
619
|
+
window.setTimeout(resolve, delayMs),
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
try {
|
|
623
|
+
const { assignment } = await creditsV2Exposure.mutateAsync({
|
|
624
|
+
confirmedVariant: creditsV2Assignment.experiment_variant,
|
|
625
|
+
});
|
|
626
|
+
if (
|
|
627
|
+
assignment?.experiment_id !== creditsV2Assignment.experiment_id ||
|
|
628
|
+
assignment.experiment_variant !==
|
|
629
|
+
creditsV2Assignment.experiment_variant
|
|
630
|
+
) {
|
|
631
|
+
throw new Error(
|
|
632
|
+
"The server did not persist the displayed first-run Builder credits v2 assignment.",
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
return;
|
|
636
|
+
} catch (error: unknown) {
|
|
637
|
+
lastError = error;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
console.error(
|
|
641
|
+
"Failed to persist first-run Builder credits v2 exposure:",
|
|
642
|
+
lastError,
|
|
643
|
+
);
|
|
644
|
+
};
|
|
645
|
+
void persistExposure();
|
|
646
|
+
}, [
|
|
647
|
+
canTrackOnboardingScreen,
|
|
648
|
+
creditsV2Assignment,
|
|
649
|
+
creditsV2Enabled,
|
|
650
|
+
creditsV2Exposure.mutateAsync,
|
|
651
|
+
firstRun,
|
|
652
|
+
previewMode,
|
|
653
|
+
setupExperimentDecision,
|
|
654
|
+
screen,
|
|
655
|
+
]);
|
|
448
656
|
useEffect(() => {
|
|
449
657
|
if (
|
|
450
658
|
previewMode ||
|
|
@@ -463,8 +671,14 @@ export function FirstRunOnboarding({
|
|
|
463
671
|
extensionIndex,
|
|
464
672
|
extensionStepIndex,
|
|
465
673
|
choiceSkippable,
|
|
674
|
+
provisioningFailed,
|
|
466
675
|
);
|
|
467
|
-
const key = [
|
|
676
|
+
const key = [
|
|
677
|
+
step.step_id,
|
|
678
|
+
step.extension_id,
|
|
679
|
+
step.step_index,
|
|
680
|
+
step.optional,
|
|
681
|
+
].join(":");
|
|
468
682
|
if (stepViewRef.current?.key !== key) {
|
|
469
683
|
stepViewRef.current = { key, id: createOnboardingCorrelationId() };
|
|
470
684
|
}
|
|
@@ -485,6 +699,7 @@ export function FirstRunOnboarding({
|
|
|
485
699
|
previewMode,
|
|
486
700
|
canTrackOnboardingScreen,
|
|
487
701
|
choiceStepKnown,
|
|
702
|
+
provisioningFailed,
|
|
488
703
|
screen,
|
|
489
704
|
]);
|
|
490
705
|
useEffect(() => {
|
|
@@ -507,6 +722,7 @@ export function FirstRunOnboarding({
|
|
|
507
722
|
extensionIndex,
|
|
508
723
|
extensionStepIndex,
|
|
509
724
|
choiceSkippable,
|
|
725
|
+
provisioningFailed,
|
|
510
726
|
),
|
|
511
727
|
reason: "page_exit",
|
|
512
728
|
});
|
|
@@ -524,46 +740,8 @@ export function FirstRunOnboarding({
|
|
|
524
740
|
canTrackOnboardingScreen,
|
|
525
741
|
screen,
|
|
526
742
|
choiceSkippable,
|
|
743
|
+
provisioningFailed,
|
|
527
744
|
]);
|
|
528
|
-
const beginBeforeSetup = useCallback(() => {
|
|
529
|
-
if (beforeSetupExtensions.length === 0) {
|
|
530
|
-
setScreen("choice");
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
setExtensionPlacement("before-setup");
|
|
534
|
-
setExtensionIndex(0);
|
|
535
|
-
setExtensionStepIndex(0);
|
|
536
|
-
setScreen("extension");
|
|
537
|
-
}, [beforeSetupExtensions.length]);
|
|
538
|
-
const handleFinish = useCallback(
|
|
539
|
-
(completedScreen: FirstRunScreen | null, track = true) => {
|
|
540
|
-
if (afterSetupExtensions.length === 0) {
|
|
541
|
-
void finishOnboarding(completedScreen);
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
if (completedScreen && track) trackFirstRunStepCompleted(completedScreen);
|
|
545
|
-
setExtensionPlacement("after-setup");
|
|
546
|
-
setExtensionIndex(0);
|
|
547
|
-
setExtensionStepIndex(0);
|
|
548
|
-
setScreen("extension");
|
|
549
|
-
},
|
|
550
|
-
[afterSetupExtensions.length, finishOnboarding, trackFirstRunStepCompleted],
|
|
551
|
-
);
|
|
552
|
-
const handleBuilderConnected = useCallback(() => {
|
|
553
|
-
trackFirstRunSetupOutcome(builderSetupAttemptRef.current, "connected");
|
|
554
|
-
builderSetupAttemptRef.current = null;
|
|
555
|
-
setupAttemptRef.current = null;
|
|
556
|
-
trackFirstRunStepCompleted("choice");
|
|
557
|
-
trackFirstRunStepCompleted("connecting");
|
|
558
|
-
handleFinish(null);
|
|
559
|
-
}, [handleFinish, trackFirstRunStepCompleted]);
|
|
560
|
-
const connectFlow = useBuilderConnectFlow({
|
|
561
|
-
enabled: firstRun && !previewMode,
|
|
562
|
-
provisionAccount: true,
|
|
563
|
-
trackingSource: "first_run_onboarding",
|
|
564
|
-
trackingFlow: "connect_llm",
|
|
565
|
-
onConnected: handleBuilderConnected,
|
|
566
|
-
});
|
|
567
745
|
const builderStatusReadCount = connectFlow.statusReadSettledCount ?? 0;
|
|
568
746
|
useEffect(() => {
|
|
569
747
|
const startedAt = retryingBuilderStatusAtCountRef.current;
|
|
@@ -587,12 +765,13 @@ export function FirstRunOnboarding({
|
|
|
587
765
|
trackFirstRunSetupOutcome(attempt, "failed", "account_exists");
|
|
588
766
|
return;
|
|
589
767
|
}
|
|
590
|
-
if (connectFlow.terminalError) {
|
|
768
|
+
if (connectFlow.terminalError || connectFlow.statusUnavailable) {
|
|
591
769
|
trackFirstRunSetupOutcome(attempt, "failed", "connection_error");
|
|
592
770
|
}
|
|
593
771
|
}, [
|
|
594
772
|
connectFlow.accountExists,
|
|
595
773
|
connectFlow.connecting,
|
|
774
|
+
connectFlow.statusUnavailable,
|
|
596
775
|
connectFlow.terminalError,
|
|
597
776
|
]);
|
|
598
777
|
// Preview never fetches status, so show the activation path it would offer.
|
|
@@ -608,12 +787,15 @@ export function FirstRunOnboarding({
|
|
|
608
787
|
const setupExperimentEligible =
|
|
609
788
|
setupStatusSettled &&
|
|
610
789
|
canActivateBuilderFreeCredits &&
|
|
611
|
-
!connectFlow.configured
|
|
790
|
+
!connectFlow.configured &&
|
|
791
|
+
!connectFlow.statusUnavailable;
|
|
612
792
|
useEffect(() => {
|
|
613
793
|
if (
|
|
614
794
|
previewMode ||
|
|
615
795
|
!firstRun ||
|
|
616
796
|
screen !== "choice" ||
|
|
797
|
+
creditsV2FlagPending ||
|
|
798
|
+
creditsV2FlowEnabled ||
|
|
617
799
|
!setupVariant ||
|
|
618
800
|
!setupStatusSettled ||
|
|
619
801
|
setupExperimentDecision !== null
|
|
@@ -622,12 +804,14 @@ export function FirstRunOnboarding({
|
|
|
622
804
|
}
|
|
623
805
|
if (setupExperimentEligible) {
|
|
624
806
|
setOnboardingExperiment({
|
|
625
|
-
id:
|
|
807
|
+
id: setupExperimentId,
|
|
626
808
|
variant: setupVariant,
|
|
627
809
|
});
|
|
628
810
|
trackOnboardingEvent("onboarding_experiment_exposed", {
|
|
629
811
|
flow: "first_run",
|
|
630
812
|
step_id: "choice",
|
|
813
|
+
experiment_id: setupExperimentId,
|
|
814
|
+
experiment_variant: setupVariant,
|
|
631
815
|
});
|
|
632
816
|
}
|
|
633
817
|
setSetupExperimentDecision(setupExperimentEligible);
|
|
@@ -635,11 +819,73 @@ export function FirstRunOnboarding({
|
|
|
635
819
|
firstRun,
|
|
636
820
|
previewMode,
|
|
637
821
|
screen,
|
|
822
|
+
creditsV2FlagPending,
|
|
823
|
+
creditsV2FlowEnabled,
|
|
638
824
|
setupExperimentEligible,
|
|
639
825
|
setupExperimentDecision,
|
|
640
826
|
setupStatusSettled,
|
|
827
|
+
setupExperimentId,
|
|
641
828
|
setupVariant,
|
|
642
829
|
]);
|
|
830
|
+
useEffect(() => {
|
|
831
|
+
if (
|
|
832
|
+
previewMode ||
|
|
833
|
+
!firstRun ||
|
|
834
|
+
screen !== "choice" ||
|
|
835
|
+
!creditsV2Enabled ||
|
|
836
|
+
!setupStatusSettled ||
|
|
837
|
+
setupExperimentTimedOutRef.current ||
|
|
838
|
+
setupExperimentDecision === true
|
|
839
|
+
) {
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
if (!setupExperimentEligible) {
|
|
843
|
+
creditsV2ExposureFailedRef.current = false;
|
|
844
|
+
setSetupExperimentDecision(false);
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
if (creditsV2ExposureFailedRef.current) return;
|
|
848
|
+
if (creditsV2ExposureStartedRef.current) return;
|
|
849
|
+
creditsV2ExposureStartedRef.current = true;
|
|
850
|
+
creditsV2ExposureSettledRef.current = false;
|
|
851
|
+
setSetupExperimentDecision(null);
|
|
852
|
+
void creditsV2Exposure
|
|
853
|
+
.mutateAsync({})
|
|
854
|
+
.then(({ assignment }) => {
|
|
855
|
+
creditsV2ExposureSettledRef.current = true;
|
|
856
|
+
if (setupExperimentTimedOutRef.current) return;
|
|
857
|
+
if (
|
|
858
|
+
assignment?.experiment_id !==
|
|
859
|
+
FIRST_RUN_BUILDER_CREDITS_V2_EXPERIMENT_ID ||
|
|
860
|
+
(assignment.experiment_variant !== "control" &&
|
|
861
|
+
assignment.experiment_variant !== "credits")
|
|
862
|
+
) {
|
|
863
|
+
setSetupExperimentDecision(false);
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
setCreditsV2Assignment({
|
|
867
|
+
experiment_id: FIRST_RUN_BUILDER_CREDITS_V2_EXPERIMENT_ID,
|
|
868
|
+
experiment_variant: assignment.experiment_variant,
|
|
869
|
+
});
|
|
870
|
+
setSetupExperimentDecision(true);
|
|
871
|
+
})
|
|
872
|
+
.catch(() => {
|
|
873
|
+
creditsV2ExposureSettledRef.current = true;
|
|
874
|
+
if (setupExperimentTimedOutRef.current) return;
|
|
875
|
+
creditsV2ExposureStartedRef.current = false;
|
|
876
|
+
creditsV2ExposureFailedRef.current = true;
|
|
877
|
+
setSetupExperimentDecision(false);
|
|
878
|
+
});
|
|
879
|
+
}, [
|
|
880
|
+
creditsV2Enabled,
|
|
881
|
+
creditsV2Exposure.mutateAsync,
|
|
882
|
+
firstRun,
|
|
883
|
+
previewMode,
|
|
884
|
+
screen,
|
|
885
|
+
setupExperimentDecision,
|
|
886
|
+
setupExperimentEligible,
|
|
887
|
+
setupStatusSettled,
|
|
888
|
+
]);
|
|
643
889
|
// A stalled Builder status read must not leave the setup screen blank. Past
|
|
644
890
|
// the limit the person gets the existing page and stays out of the
|
|
645
891
|
// experiment, identically in both arms, so exposure counts stay honest.
|
|
@@ -648,17 +894,28 @@ export function FirstRunOnboarding({
|
|
|
648
894
|
previewMode ||
|
|
649
895
|
!firstRun ||
|
|
650
896
|
screen !== "choice" ||
|
|
651
|
-
|
|
897
|
+
creditsV2FlagPending ||
|
|
898
|
+
(!setupVariant && !creditsV2FlowEnabled) ||
|
|
652
899
|
setupExperimentDecision !== null
|
|
653
900
|
) {
|
|
654
901
|
return;
|
|
655
902
|
}
|
|
656
|
-
const timer = window.setTimeout(
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
903
|
+
const timer = window.setTimeout(() => {
|
|
904
|
+
if (creditsV2ExposureSettledRef.current) return;
|
|
905
|
+
setupExperimentTimedOutRef.current = true;
|
|
906
|
+
setSetupExperimentDecision((decision) => decision ?? false);
|
|
907
|
+
}, SETUP_EXPERIMENT_DECISION_TIMEOUT_MS);
|
|
660
908
|
return () => window.clearTimeout(timer);
|
|
661
|
-
}, [
|
|
909
|
+
}, [
|
|
910
|
+
creditsV2FlowEnabled,
|
|
911
|
+
creditsV2FlagPending,
|
|
912
|
+
firstRun,
|
|
913
|
+
previewMode,
|
|
914
|
+
screen,
|
|
915
|
+
setupExperimentDecision,
|
|
916
|
+
setupStatusSettled,
|
|
917
|
+
setupVariant,
|
|
918
|
+
]);
|
|
662
919
|
const retryOnboardingCompletion = useCallback(() => {
|
|
663
920
|
const attempt = completionAttemptRef.current;
|
|
664
921
|
void finishOnboarding(
|
|
@@ -887,9 +1144,12 @@ export function FirstRunOnboarding({
|
|
|
887
1144
|
);
|
|
888
1145
|
|
|
889
1146
|
const inCreditsArm = setupVariant === "credits";
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
1147
|
+
if (
|
|
1148
|
+
!previewMode &&
|
|
1149
|
+
(creditsV2FlagPending ||
|
|
1150
|
+
(creditsV2FlowEnabled && setupExperimentDecision === null) ||
|
|
1151
|
+
(setupVariant && setupExperimentDecision === null))
|
|
1152
|
+
) {
|
|
893
1153
|
return <OnboardingSkeleton />;
|
|
894
1154
|
}
|
|
895
1155
|
const creditsEligible =
|
|
@@ -1093,6 +1353,17 @@ export function FirstRunOnboarding({
|
|
|
1093
1353
|
>
|
|
1094
1354
|
{t("agentChat.onboarding.builderExistingAccount")}
|
|
1095
1355
|
</button>
|
|
1356
|
+
{creditsV2FlowEnabled && (
|
|
1357
|
+
<button
|
|
1358
|
+
type="button"
|
|
1359
|
+
data-testid="first-run-credits-own-api-keys"
|
|
1360
|
+
className="min-h-7 text-xs font-medium text-muted-foreground underline underline-offset-4 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
1361
|
+
onClick={() => void handleOpenSettings()}
|
|
1362
|
+
disabled={connectFlow.connecting}
|
|
1363
|
+
>
|
|
1364
|
+
{t("agentChat.onboarding.creditsUseOwnApiKeys")}
|
|
1365
|
+
</button>
|
|
1366
|
+
)}
|
|
1096
1367
|
<p className="text-xs leading-5 text-muted-foreground">
|
|
1097
1368
|
{t("agentChat.onboarding.creditsFinePrint")}{" "}
|
|
1098
1369
|
<a
|
|
@@ -1141,74 +1412,88 @@ export function FirstRunOnboarding({
|
|
|
1141
1412
|
</div>
|
|
1142
1413
|
)}
|
|
1143
1414
|
<div className="fixed bottom-6 right-6 z-10">
|
|
1144
|
-
|
|
1145
|
-
<
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
>
|
|
1151
|
-
{t("agentChat.onboarding.creditsSkip")}
|
|
1152
|
-
</button>
|
|
1153
|
-
</PopoverTrigger>
|
|
1154
|
-
<PopoverContent
|
|
1155
|
-
side="top"
|
|
1156
|
-
align="end"
|
|
1157
|
-
portalled={false}
|
|
1158
|
-
aria-labelledby="first-run-credits-skip-title"
|
|
1159
|
-
aria-describedby="first-run-credits-skip-body"
|
|
1160
|
-
className="w-96 max-w-[calc(100vw-2rem)] space-y-3 p-4 text-left"
|
|
1161
|
-
onOpenAutoFocus={(event) => {
|
|
1162
|
-
event.preventDefault();
|
|
1163
|
-
skipGoBackRef.current?.focus();
|
|
1164
|
-
}}
|
|
1415
|
+
{creditsV2FlowEnabled ? (
|
|
1416
|
+
<button
|
|
1417
|
+
type="button"
|
|
1418
|
+
data-testid="first-run-credits-skip"
|
|
1419
|
+
className="inline-flex min-h-6 items-center px-2 rounded-sm text-xs font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
1420
|
+
onClick={skipSetup}
|
|
1165
1421
|
>
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
className="text-xs leading-5 text-muted-foreground"
|
|
1176
|
-
>
|
|
1177
|
-
{t("agentChat.onboarding.creditsSkipConfirmBody")}
|
|
1178
|
-
</p>
|
|
1179
|
-
</div>
|
|
1180
|
-
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
1181
|
-
<Button
|
|
1422
|
+
{t("agentChat.onboarding.creditsSkip")}
|
|
1423
|
+
</button>
|
|
1424
|
+
) : (
|
|
1425
|
+
<Popover
|
|
1426
|
+
open={skipConfirmOpen}
|
|
1427
|
+
onOpenChange={setSkipConfirmOpen}
|
|
1428
|
+
>
|
|
1429
|
+
<PopoverTrigger asChild>
|
|
1430
|
+
<button
|
|
1182
1431
|
type="button"
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
className="px-0 text-muted-foreground hover:bg-transparent hover:text-foreground"
|
|
1186
|
-
onClick={() => void handleOpenSettings()}
|
|
1432
|
+
data-testid="first-run-credits-skip"
|
|
1433
|
+
className="inline-flex min-h-6 items-center px-2 rounded-sm text-xs font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
1187
1434
|
>
|
|
1188
|
-
{t("agentChat.onboarding.
|
|
1189
|
-
</
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1435
|
+
{t("agentChat.onboarding.creditsSkip")}
|
|
1436
|
+
</button>
|
|
1437
|
+
</PopoverTrigger>
|
|
1438
|
+
<PopoverContent
|
|
1439
|
+
side="top"
|
|
1440
|
+
align="end"
|
|
1441
|
+
portalled={false}
|
|
1442
|
+
aria-labelledby="first-run-credits-skip-title"
|
|
1443
|
+
aria-describedby="first-run-credits-skip-body"
|
|
1444
|
+
className="w-96 max-w-[calc(100vw-2rem)] space-y-3 p-4 text-left"
|
|
1445
|
+
onOpenAutoFocus={(event) => {
|
|
1446
|
+
event.preventDefault();
|
|
1447
|
+
skipGoBackRef.current?.focus();
|
|
1448
|
+
}}
|
|
1449
|
+
>
|
|
1450
|
+
<div className="space-y-1">
|
|
1451
|
+
<p
|
|
1452
|
+
id="first-run-credits-skip-title"
|
|
1453
|
+
className="text-sm font-semibold text-foreground"
|
|
1197
1454
|
>
|
|
1198
|
-
{t("agentChat.onboarding.
|
|
1199
|
-
</
|
|
1455
|
+
{t("agentChat.onboarding.creditsSkipConfirmTitle")}
|
|
1456
|
+
</p>
|
|
1457
|
+
<p
|
|
1458
|
+
id="first-run-credits-skip-body"
|
|
1459
|
+
className="text-xs leading-5 text-muted-foreground"
|
|
1460
|
+
>
|
|
1461
|
+
{t("agentChat.onboarding.creditsSkipConfirmBody")}
|
|
1462
|
+
</p>
|
|
1463
|
+
</div>
|
|
1464
|
+
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
1200
1465
|
<Button
|
|
1201
1466
|
type="button"
|
|
1467
|
+
variant="ghost"
|
|
1202
1468
|
size="sm"
|
|
1203
|
-
|
|
1204
|
-
onClick={() =>
|
|
1469
|
+
className="px-0 text-muted-foreground hover:bg-transparent hover:text-foreground"
|
|
1470
|
+
onClick={() => void handleOpenSettings()}
|
|
1205
1471
|
>
|
|
1206
|
-
{t("agentChat.onboarding.
|
|
1472
|
+
{t("agentChat.onboarding.creditsUseCustomKeys")}
|
|
1207
1473
|
</Button>
|
|
1474
|
+
<div className="flex flex-wrap justify-end gap-2">
|
|
1475
|
+
<Button
|
|
1476
|
+
type="button"
|
|
1477
|
+
variant="secondary"
|
|
1478
|
+
size="sm"
|
|
1479
|
+
data-testid="first-run-credits-skip-confirm"
|
|
1480
|
+
onClick={skipSetup}
|
|
1481
|
+
>
|
|
1482
|
+
{t("agentChat.onboarding.creditsSkipAnyway")}
|
|
1483
|
+
</Button>
|
|
1484
|
+
<Button
|
|
1485
|
+
type="button"
|
|
1486
|
+
size="sm"
|
|
1487
|
+
ref={skipGoBackRef}
|
|
1488
|
+
onClick={() => setSkipConfirmOpen(false)}
|
|
1489
|
+
>
|
|
1490
|
+
{t("agentChat.onboarding.creditsSkipGoBack")}
|
|
1491
|
+
</Button>
|
|
1492
|
+
</div>
|
|
1208
1493
|
</div>
|
|
1209
|
-
</
|
|
1210
|
-
</
|
|
1211
|
-
|
|
1494
|
+
</PopoverContent>
|
|
1495
|
+
</Popover>
|
|
1496
|
+
)}
|
|
1212
1497
|
</div>
|
|
1213
1498
|
</div>
|
|
1214
1499
|
</OnboardingShell>
|
|
@@ -1375,8 +1660,6 @@ export function FirstRunOnboarding({
|
|
|
1375
1660
|
);
|
|
1376
1661
|
}
|
|
1377
1662
|
|
|
1378
|
-
const accountExists = connectFlow.accountExists && !connectFlow.connecting;
|
|
1379
|
-
const provisioning = builderConnectionMode === "provision" && !accountExists;
|
|
1380
1663
|
const returnToSetupChoice = () => setScreen("choice");
|
|
1381
1664
|
return (
|
|
1382
1665
|
<OnboardingShell
|
|
@@ -1391,23 +1674,37 @@ export function FirstRunOnboarding({
|
|
|
1391
1674
|
aria-live="polite"
|
|
1392
1675
|
aria-busy={connectFlow.connecting}
|
|
1393
1676
|
>
|
|
1394
|
-
<div
|
|
1395
|
-
{
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1677
|
+
<div
|
|
1678
|
+
className={cn(
|
|
1679
|
+
"flex size-10 items-center justify-center rounded-full",
|
|
1680
|
+
connectionFailed
|
|
1681
|
+
? "bg-destructive/10 text-destructive"
|
|
1682
|
+
: "bg-primary/10 text-primary",
|
|
1399
1683
|
)}
|
|
1684
|
+
>
|
|
1685
|
+
{accountExists ? <IconKey size={19} /> : null}
|
|
1686
|
+
{connectionFailed ? <IconAlertCircle size={19} /> : null}
|
|
1687
|
+
{!accountExists && !connectionFailed ? (
|
|
1688
|
+
<IconLoader2 className="animate-spin" size={19} />
|
|
1689
|
+
) : null}
|
|
1400
1690
|
</div>
|
|
1401
1691
|
<h1 className="mt-5 text-xl font-semibold tracking-[-0.04em]">
|
|
1402
1692
|
{accountExists
|
|
1403
1693
|
? t("agentChat.onboarding.builderAccountExistsTitle")
|
|
1404
|
-
:
|
|
1405
|
-
? t("agentChat.
|
|
1406
|
-
:
|
|
1694
|
+
: provisioningFailed
|
|
1695
|
+
? t("agentChat.settingsShell.builder.setupFailed")
|
|
1696
|
+
: provisioning
|
|
1697
|
+
? t("agentChat.onboarding.creditsActivating")
|
|
1698
|
+
: t("agentChat.onboarding.builderConnecting")}
|
|
1407
1699
|
</h1>
|
|
1408
|
-
<p
|
|
1700
|
+
<p
|
|
1701
|
+
className="mt-2 text-sm text-muted-foreground"
|
|
1702
|
+
role={connectionFailed ? "alert" : undefined}
|
|
1703
|
+
>
|
|
1409
1704
|
{accountExists ? (
|
|
1410
1705
|
t("agentChat.onboarding.builderAccountExistsDescription")
|
|
1706
|
+
) : provisioningFailed ? (
|
|
1707
|
+
t("agentChat.onboarding.builderProvisionFailedDetail")
|
|
1411
1708
|
) : provisioning ? (
|
|
1412
1709
|
<>
|
|
1413
1710
|
{t("agentChat.onboarding.builderProvisioningDescription")}
|
|
@@ -1444,18 +1741,20 @@ export function FirstRunOnboarding({
|
|
|
1444
1741
|
</button>
|
|
1445
1742
|
) : (
|
|
1446
1743
|
<>
|
|
1447
|
-
|
|
1448
|
-
<div className="
|
|
1449
|
-
<
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
<
|
|
1455
|
-
|
|
1456
|
-
|
|
1744
|
+
{connectFlow.connecting ? (
|
|
1745
|
+
<div className="mt-7 w-full rounded-xl bg-muted/35 p-4 text-left">
|
|
1746
|
+
<div className="flex items-center justify-between gap-3">
|
|
1747
|
+
<Skeleton className="h-3 w-28" />
|
|
1748
|
+
<Skeleton className="h-5 w-16 rounded-full" />
|
|
1749
|
+
</div>
|
|
1750
|
+
<Skeleton className="mt-4 h-8 w-full" />
|
|
1751
|
+
<div className="mt-3 grid grid-cols-3 gap-2">
|
|
1752
|
+
<Skeleton className="h-7 w-full" />
|
|
1753
|
+
<Skeleton className="h-7 w-full" />
|
|
1754
|
+
<Skeleton className="h-7 w-full" />
|
|
1755
|
+
</div>
|
|
1457
1756
|
</div>
|
|
1458
|
-
|
|
1757
|
+
) : null}
|
|
1459
1758
|
{connectFlow.connecting &&
|
|
1460
1759
|
(builderCancelRequested ? (
|
|
1461
1760
|
<div className="mt-4 flex flex-col items-center gap-2">
|
|
@@ -1502,6 +1801,7 @@ export function FirstRunOnboarding({
|
|
|
1502
1801
|
</p>
|
|
1503
1802
|
)}
|
|
1504
1803
|
{!connectFlow.connecting &&
|
|
1804
|
+
!provisioningFailed &&
|
|
1505
1805
|
(connectFlow.statusUnavailable || connectFlow.terminalError) && (
|
|
1506
1806
|
<div className="mt-4 flex flex-col items-center gap-2">
|
|
1507
1807
|
<p className="text-xs text-destructive" role="alert">
|
|
@@ -1529,6 +1829,34 @@ export function FirstRunOnboarding({
|
|
|
1529
1829
|
</button>
|
|
1530
1830
|
</div>
|
|
1531
1831
|
)}
|
|
1832
|
+
{provisioningFailed ? (
|
|
1833
|
+
<div className="mt-4 flex w-full flex-col items-center gap-2">
|
|
1834
|
+
<button
|
|
1835
|
+
type="button"
|
|
1836
|
+
data-testid="first-run-retry-builder"
|
|
1837
|
+
className={cn(ONBOARDING_PRIMARY_BUTTON_CLASS, "w-full")}
|
|
1838
|
+
onClick={() => handleBuilder(true)}
|
|
1839
|
+
>
|
|
1840
|
+
{t("agentChat.settingsShell.builder.retry")}
|
|
1841
|
+
</button>
|
|
1842
|
+
<button
|
|
1843
|
+
type="button"
|
|
1844
|
+
data-testid="first-run-open-key-settings-after-failure"
|
|
1845
|
+
className={cn(secondaryButtonClass, "w-full")}
|
|
1846
|
+
onClick={() => void handleOpenSettings()}
|
|
1847
|
+
>
|
|
1848
|
+
{t("agentChat.onboarding.openAiKeySettings")}
|
|
1849
|
+
</button>
|
|
1850
|
+
<button
|
|
1851
|
+
type="button"
|
|
1852
|
+
data-testid="first-run-skip-builder-after-failure"
|
|
1853
|
+
className="inline-flex min-h-9 items-center justify-center rounded-lg px-2.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
1854
|
+
onClick={skipFailedBuilderSetup}
|
|
1855
|
+
>
|
|
1856
|
+
{t("agentChat.onboarding.skipForNow")}
|
|
1857
|
+
</button>
|
|
1858
|
+
</div>
|
|
1859
|
+
) : null}
|
|
1532
1860
|
{!connectFlow.connecting &&
|
|
1533
1861
|
!connectFlow.statusUnavailable &&
|
|
1534
1862
|
!connectFlow.terminalError && (
|