refactor(onboarding): replace 50-prop drilling with OnboardingContext

OnboardingStepPanel previously forwarded ~50 props to seven step
components, so adding a single wizard field meant touching four layers.
Introduce a renderer-local React Context (OnboardingProvider /
useOnboarding) that carries all wizard state and actions (connectors,
node status, company inputs, model selection, execution mode, mission,
launch summary, locale, t, error/loading). Step panels now receive only
their navigation callbacks (onContinue / onBack / onLaunch / onFinish)
and read everything else via useOnboarding(). CompanyOnboarding wraps
the step panel tree in OnboardingProvider with a memoised value.

Business logic is unchanged: each onContinueFrom* transition, the
bootstrap call, and the welcome-step branch remain exactly as before;
this refactor only moves the data-passing mechanism.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-16 22:11:45 -04:00
1 parent f3816c6115
commit de77f5f38e
10 files changed
+256 -333

No files matched your search

@@ -1,53 +1,36 @@
import { motion } from "framer-motion";
import { Buildings, FolderOpen } from "@phosphor-icons/react";
import type { ConnectorId, ProfileSnapshot } from "@shared/types";
import type { AppLocale } from "@shared/locale";
import type { ConnectorId } from "@shared/types";
import { ActionButton, Input, TextArea, Select } from "../ui";
import { getStepNumber, TOTAL_STEPS } from "./OnboardingHero";
import { useOnboarding } from "./OnboardingContext";
interface CompanyStepProps {
companyName: string;
setCompanyName: (v: string) => void;
companyDescription: string;
setCompanyDescription: (v: string) => void;
selectedConnectorId: ConnectorId | "";
setSelectedConnectorId: (v: ConnectorId | "") => void;
selectedModel: string;
setSelectedModel: (v: string) => void;
executionReadyConnectors: ProfileSnapshot["connectors"];
modelOptions: Array<{ id: string; label: string }>;
loadingModels: boolean;
workspacePath: string;
setWorkspacePath: (v: string) => void;
onPickDirectory: () => void;
onContinue: () => void;
onBack: () => void;
error: string | null;
currentLocale: AppLocale;
t: (key: string) => string;
}
export function CompanyStep({
companyName,
setCompanyName,
companyDescription,
setCompanyDescription,
selectedConnectorId,
setSelectedConnectorId,
selectedModel,
setSelectedModel,
executionReadyConnectors,
modelOptions,
loadingModels,
workspacePath,
setWorkspacePath,
onPickDirectory,
onContinue,
onBack,
error,
currentLocale,
t,
}: CompanyStepProps) {
export function CompanyStep({ onContinue, onBack }: CompanyStepProps) {
const {
companyName,
setCompanyName,
companyDescription,
setCompanyDescription,
selectedConnectorId,
setSelectedConnectorId,
selectedModel,
setSelectedModel,
executionReadyConnectors,
modelOptions,
loadingModels,
workspacePath,
setWorkspacePath,
onPickDirectory,
error,
currentLocale,
t,
} = useOnboarding();
return (
<motion.div
key="company"
@@ -1,23 +1,16 @@
import { motion } from "framer-motion";
import { ArrowRight, Brain, UserCircle, type Icon } from "@phosphor-icons/react";
import type { ExecutionMode } from "@shared/types";
import { getStepNumber, TOTAL_STEPS } from "./OnboardingHero";
import { useOnboarding } from "./OnboardingContext";
interface ExecutionModeStepProps {
executionMode: ExecutionMode;
setExecutionMode: (mode: ExecutionMode) => void;
onContinue: () => void;
onBack: () => void;
t: (key: string) => string;
}
export function ExecutionModeStep({
executionMode,
setExecutionMode,
onContinue,
onBack,
t,
}: ExecutionModeStepProps) {
export function ExecutionModeStep({ onContinue, onBack }: ExecutionModeStepProps) {
const { executionMode, setExecutionMode, t } = useOnboarding();
return (
<motion.div
key="executionmode"
@@ -1,25 +1,17 @@
import { motion } from "framer-motion";
import { CheckCircle, Rocket } from "@phosphor-icons/react";
import type { OnboardingBootstrapResult, ProfileSnapshot } from "@shared/types";
import { ActionButton } from "../ui";
import { useOnboarding } from "./OnboardingContext";
interface LaunchStepProps {
launchSummary: OnboardingBootstrapResult;
selectedConnector: ProfileSnapshot["connectors"][number] | null;
onFinish: () => void;
loading: boolean;
error: string | null;
t: (key: string) => string;
}
export function LaunchStep({
launchSummary,
selectedConnector,
onFinish,
loading,
error,
t,
}: LaunchStepProps) {
export function LaunchStep({ onFinish }: LaunchStepProps) {
const { launchSummary, selectedConnector, loading, error, t } = useOnboarding();
if (!launchSummary) return null;
return (
<motion.div
key="launch"
@@ -2,30 +2,24 @@ import { motion } from "framer-motion";
import { Cpu, Lightning } from "@phosphor-icons/react";
import { ActionButton, Input, TextArea } from "../ui";
import { getStepNumber, TOTAL_STEPS } from "./OnboardingHero";
import { useOnboarding } from "./OnboardingContext";
interface MissionStepProps {
goalTitle: string;
setGoalTitle: (v: string) => void;
goalDescription: string;
setGoalDescription: (v: string) => void;
onLaunch: () => void;
onBack: () => void;
loading: boolean;
error: string | null;
t: (key: string) => string;
}
export function MissionStep({
goalTitle,
setGoalTitle,
goalDescription,
setGoalDescription,
onLaunch,
onBack,
loading,
error,
t,
}: MissionStepProps) {
export function MissionStep({ onLaunch, onBack }: MissionStepProps) {
const {
goalTitle,
setGoalTitle,
goalDescription,
setGoalDescription,
loading,
error,
t,
} = useOnboarding();
return (
<motion.div
key="mission"
@@ -1,39 +1,26 @@
import { motion } from "framer-motion";
import type { ConnectorModelEntry } from "@shared/connector-models";
import type { ConnectorId, ProfileSnapshot } from "@shared/types";
import type { AppLocale } from "@shared/locale";
import { ActionButton } from "../ui";
import { ModelToggleList, StatusBadge } from "../execution/ModelManagementShared";
import { getStepNumber, TOTAL_STEPS } from "./OnboardingHero";
import { useOnboarding } from "./OnboardingContext";
interface ModelPoolStepProps {
currentLocale: AppLocale;
connectorModelPools: Array<{
connector: ProfileSnapshot["connectors"][number];
models: ConnectorModelEntry[];
}>;
error: string | null;
onBack: () => void;
onContinue: () => void;
onToggleConnectorModel: (connectorId: ConnectorId, modelId: string) => void;
selectedConnectorId: ConnectorId | "";
selectedConnectorEnabledModels: number;
totalEnabledModels: number;
t: (key: string) => string;
}
export function ModelPoolStep({
currentLocale,
connectorModelPools,
error,
onBack,
onContinue,
onToggleConnectorModel,
selectedConnectorId,
selectedConnectorEnabledModels,
totalEnabledModels,
t,
}: ModelPoolStepProps) {
export function ModelPoolStep({ onBack, onContinue }: ModelPoolStepProps) {
const {
currentLocale,
connectorModelPools,
error,
onToggleConnectorModel,
selectedConnectorId,
selectedConnectorEnabledModels,
totalEnabledModels,
t,
} = useOnboarding();
const selectedConnectorIsRunnable = selectedConnectorEnabledModels > 0;
const hasAnyEnabledModels = totalEnabledModels > 0;
const canContinue = hasAnyEnabledModels && selectedConnectorIsRunnable;
@@ -9,15 +9,11 @@ import {
import { ActionButton } from "../ui";
import type { TerminalCommandKey } from "@shared/contracts";
import { getStepNumber, TOTAL_STEPS } from "./OnboardingHero";
import { useOnboarding } from "./OnboardingContext";
interface NodeCheckStepProps {
nodeStatus: { installed: boolean; version: string | null } | null;
checking: boolean;
onCheck: () => void;
onInstall: (commandKey: TerminalCommandKey) => void;
onContinue: () => void;
onBack: () => void;
t: (key: string) => string;
}
const INSTALL_COMMANDS: Record<string, { label: string; code: string; commandKey: TerminalCommandKey }> = {
@@ -48,15 +44,8 @@ node -v`,
},
};
export function NodeCheckStep({
nodeStatus,
checking,
onCheck,
onInstall,
onContinue,
onBack,
t,
}: NodeCheckStepProps) {
export function NodeCheckStep({ onContinue, onBack }: NodeCheckStepProps) {
const { nodeStatus, nodeChecking, onCheckNode, onInstallCommand, t } = useOnboarding();
const platform = window.agentCompany.platform;
const platformInfo = INSTALL_COMMANDS[platform] ?? INSTALL_COMMANDS.linux;
@@ -79,7 +68,7 @@ export function NodeCheckStep({
{/* Status */}
<div className="mb-5 flex items-center gap-2.5 text-[13px]">
{checking ? (
{nodeChecking ? (
<span className="h-4 w-4 animate-spin rounded-full border-2 border-muted-foreground border-t-primary" />
) : nodeStatus?.installed ? (
<CheckCircle size={18} weight="fill" className="shrink-0 text-success" />
@@ -87,7 +76,7 @@ export function NodeCheckStep({
<XCircle size={18} weight="fill" className="shrink-0 text-destructive" />
) : null}
{checking ? (
{nodeChecking ? (
<span className="text-muted-foreground">{t("onboarding.providerChecking")}...</span>
) : nodeStatus?.installed ? (
<span className="font-medium text-success">
@@ -111,7 +100,7 @@ export function NodeCheckStep({
</pre>
<ActionButton
label={t("onboarding.installNodeTerminal")}
onClick={() => onInstall(platformInfo.commandKey)}
onClick={() => onInstallCommand(platformInfo.commandKey)}
icon={Terminal}
/>
</div>
@@ -121,7 +110,7 @@ export function NodeCheckStep({
<div className="mb-6">
<ActionButton
label={t("onboarding.checkAgain")}
onClick={onCheck}
onClick={onCheckNode}
icon={ArrowCounterClockwise}
/>
</div>
@@ -0,0 +1,91 @@
import { createContext, useContext, type ReactNode } from "react";
import type { AppLocale } from "@shared/locale";
import type {
ConnectorId,
ExecutionMode,
OnboardingBootstrapResult,
ProfileSnapshot,
} from "@shared/types";
import type { ConnectorModelEntry } from "@shared/connector-models";
import type { TerminalCommandKey } from "@shared/contracts";
import type { TranslationFn } from "../../i18n";
export interface OnboardingContextValue {
currentLocale: AppLocale;
t: TranslationFn;
error: string | null;
loading: boolean;
nodeStatus: { installed: boolean; version: string | null } | null;
nodeChecking: boolean;
onCheckNode: () => void;
onInstallCommand: (commandKey: TerminalCommandKey) => void;
connectors: ProfileSnapshot["connectors"];
hasAtLeastOneReady: boolean;
onTestConnector: (connectorId: string) => void;
onSaveConnector: (
connectorId: string,
envBindingText: string,
model: string | null,
notes?: string,
) => void;
onLoginConnector: (connectorId: string) => void;
companyName: string;
setCompanyName: (value: string) => void;
companyDescription: string;
setCompanyDescription: (value: string) => void;
selectedConnectorId: ConnectorId | "";
setSelectedConnectorId: (value: ConnectorId | "") => void;
selectedModel: string;
setSelectedModel: (value: string) => void;
selectedConnector: ProfileSnapshot["connectors"][number] | null;
executionReadyConnectors: ProfileSnapshot["connectors"];
connectorModelPools: Array<{
connector: ProfileSnapshot["connectors"][number];
models: ConnectorModelEntry[];
}>;
modelOptions: Array<{ id: string; label: string }>;
loadingModels: boolean;
totalEnabledModels: number;
selectedConnectorEnabledModels: number;
onToggleConnectorModel: (connectorId: ConnectorId, modelId: string) => void;
workspacePath: string;
setWorkspacePath: (value: string) => void;
onPickDirectory: () => void;
executionMode: ExecutionMode;
setExecutionMode: (value: ExecutionMode) => void;
goalTitle: string;
setGoalTitle: (value: string) => void;
goalDescription: string;
setGoalDescription: (value: string) => void;
launchSummary: OnboardingBootstrapResult | null;
}
const OnboardingContext = createContext<OnboardingContextValue | null>(null);
export function OnboardingProvider({
value,
children,
}: {
value: OnboardingContextValue;
children: ReactNode;
}) {
return <OnboardingContext.Provider value={value}>{children}</OnboardingContext.Provider>;
}
export function useOnboarding(): OnboardingContextValue {
const ctx = useContext(OnboardingContext);
if (!ctx) {
throw new Error("useOnboarding must be used within an OnboardingProvider");
}
return ctx;
}
@@ -1,9 +1,4 @@
import type { AppLocale } from "@shared/locale";
import type { ConnectorId, OnboardingBootstrapResult, ProfileSnapshot } from "@shared/types";
import type { ConnectorModelEntry } from "@shared/connector-models";
import type { TerminalCommandKey } from "@shared/contracts";
import { AnimatePresence } from "framer-motion";
import type { TranslationFn } from "../../i18n";
import { OnboardingHero, type OnboardingStep } from "./OnboardingHero";
import { NodeCheckStep } from "./NodeCheckStep";
import { ProvidersStep } from "./ProvidersStep";
@@ -12,54 +7,13 @@ import { ModelPoolStep } from "./ModelPoolStep";
import { ExecutionModeStep } from "./ExecutionModeStep";
import { MissionStep } from "./MissionStep";
import { LaunchStep } from "./LaunchStep";
import { useOnboarding } from "./OnboardingContext";
interface OnboardingStepPanelProps {
step: OnboardingStep;
currentLocale: AppLocale;
t: TranslationFn;
canCancel: boolean;
onCancel?: () => void;
isFirstLaunch: boolean;
nodeStatus: { installed: boolean; version: string | null } | null;
nodeChecking: boolean;
onCheckNode: () => void;
onInstallCommand: (commandKey: TerminalCommandKey) => void;
connectors: ProfileSnapshot["connectors"];
hasAtLeastOneReady: boolean;
onTestConnector: (connectorId: string) => void;
onSaveConnector: (connectorId: string, envBindingText: string, model: string | null, notes?: string) => void;
onLoginConnector: (connectorId: string) => void;
error: string | null;
companyName: string;
setCompanyName: (value: string) => void;
companyDescription: string;
setCompanyDescription: (value: string) => void;
selectedConnectorId: ConnectorId | "";
setSelectedConnectorId: (value: ConnectorId | "") => void;
selectedModel: string;
setSelectedModel: (value: string) => void;
executionReadyConnectors: ProfileSnapshot["connectors"];
connectorModelPools: Array<{
connector: ProfileSnapshot["connectors"][number];
models: ConnectorModelEntry[];
}>;
modelOptions: Array<{ id: string; label: string }>;
loadingModels: boolean;
totalEnabledModels: number;
selectedConnectorEnabledModels: number;
onToggleConnectorModel: (connectorId: ConnectorId, modelId: string) => void;
workspacePath: string;
setWorkspacePath: (value: string) => void;
onPickDirectory: () => void;
executionMode: "ai_ceo" | "ai_cofounder";
setExecutionMode: (value: "ai_ceo" | "ai_cofounder") => void;
goalTitle: string;
setGoalTitle: (value: string) => void;
goalDescription: string;
setGoalDescription: (value: string) => void;
loading: boolean;
launchSummary: OnboardingBootstrapResult | null;
selectedConnector: ProfileSnapshot["connectors"][number] | null;
onBack: (step: OnboardingStep) => void;
onContinueFromNodeCheck: () => void;
onContinueFromProviders: () => void;
@@ -72,48 +26,9 @@ interface OnboardingStepPanelProps {
export function OnboardingStepPanel({
step,
currentLocale,
t,
canCancel,
onCancel,
isFirstLaunch,
nodeStatus,
nodeChecking,
onCheckNode,
onInstallCommand,
connectors,
hasAtLeastOneReady,
onTestConnector,
onSaveConnector,
onLoginConnector,
error,
companyName,
setCompanyName,
companyDescription,
setCompanyDescription,
selectedConnectorId,
setSelectedConnectorId,
selectedModel,
setSelectedModel,
executionReadyConnectors,
connectorModelPools,
modelOptions,
loadingModels,
totalEnabledModels,
selectedConnectorEnabledModels,
onToggleConnectorModel,
workspacePath,
setWorkspacePath,
onPickDirectory,
executionMode,
setExecutionMode,
goalTitle,
setGoalTitle,
goalDescription,
setGoalDescription,
loading,
launchSummary,
selectedConnector,
onBack,
onContinueFromNodeCheck,
onContinueFromProviders,
@@ -123,6 +38,8 @@ export function OnboardingStepPanel({
onLaunchCompany,
onFinishLaunch,
}: OnboardingStepPanelProps) {
const { currentLocale, t, launchSummary } = useOnboarding();
return (
<>
<OnboardingHero
@@ -146,103 +63,48 @@ export function OnboardingStepPanel({
<AnimatePresence mode="wait">
{step === "nodecheck" ? (
<NodeCheckStep
nodeStatus={nodeStatus}
checking={nodeChecking}
onCheck={onCheckNode}
onInstall={(commandKey) => onInstallCommand(commandKey)}
onContinue={onContinueFromNodeCheck}
onBack={() => onBack("welcome")}
t={t}
/>
) : null}
{step === "providers" ? (
<ProvidersStep
connectors={connectors}
onTestConnector={onTestConnector}
onSaveConnector={onSaveConnector}
onLoginConnector={onLoginConnector}
onInstallCommand={onInstallCommand}
hasAtLeastOneReady={hasAtLeastOneReady}
onContinue={onContinueFromProviders}
onBack={() => onBack("nodecheck")}
error={error}
t={t}
/>
) : null}
{step === "company" ? (
<CompanyStep
companyName={companyName}
setCompanyName={setCompanyName}
companyDescription={companyDescription}
setCompanyDescription={setCompanyDescription}
selectedConnectorId={selectedConnectorId}
setSelectedConnectorId={setSelectedConnectorId}
selectedModel={selectedModel}
setSelectedModel={setSelectedModel}
executionReadyConnectors={executionReadyConnectors}
modelOptions={modelOptions}
loadingModels={loadingModels}
workspacePath={workspacePath}
setWorkspacePath={setWorkspacePath}
onPickDirectory={onPickDirectory}
onContinue={onContinueFromCompany}
onBack={() => onBack("providers")}
error={error}
currentLocale={currentLocale}
t={t}
/>
) : null}
{step === "modelpool" ? (
<ModelPoolStep
currentLocale={currentLocale}
connectorModelPools={connectorModelPools}
error={error}
onBack={() => onBack("company")}
onContinue={onContinueFromModelPool}
onToggleConnectorModel={onToggleConnectorModel}
selectedConnectorId={selectedConnectorId}
selectedConnectorEnabledModels={selectedConnectorEnabledModels}
totalEnabledModels={totalEnabledModels}
t={t}
/>
) : null}
{step === "executionmode" ? (
<ExecutionModeStep
executionMode={executionMode}
setExecutionMode={setExecutionMode}
onContinue={onContinueFromExecutionMode}
onBack={() => onBack("modelpool")}
t={t}
/>
) : null}
{step === "mission" ? (
<MissionStep
goalTitle={goalTitle}
setGoalTitle={setGoalTitle}
goalDescription={goalDescription}
setGoalDescription={setGoalDescription}
onLaunch={onLaunchCompany}
onBack={() => onBack("executionmode")}
loading={loading}
error={error}
t={t}
/>
) : null}
{step === "launch" && launchSummary ? (
<LaunchStep
launchSummary={launchSummary}
selectedConnector={selectedConnector}
onFinish={onFinishLaunch}
loading={loading}
error={error}
t={t}
/>
<LaunchStep onFinish={onFinishLaunch} />
) : null}
</AnimatePresence>
</div>
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import type { ConnectorId, ExecutionMode, OnboardingBootstrapResult, ProfileSnapshot } from "@shared/types";
import type { AppLocale } from "@shared/locale";
import { useT } from "../../i18n";
@@ -8,6 +8,7 @@ import { WelcomeStep } from "./WelcomeStep";
import { OnboardingStepPanel } from "./OnboardingStepPanel";
import { useConnectorCallbacks } from "./useConnectorCallbacks";
import { useConnectorModelSelection } from "./useConnectorModelSelection";
import { OnboardingProvider, type OnboardingContextValue } from "./OnboardingContext";
export function CompanyOnboarding({
connectors,
@@ -159,6 +160,85 @@ export function CompanyOnboarding({
const isWelcome = step === "welcome";
const heroStep: OnboardingStep = step as OnboardingStep;
const contextValue = useMemo<OnboardingContextValue>(
() => ({
currentLocale,
t,
error,
loading,
nodeStatus,
nodeChecking,
onCheckNode: () => void checkNode(),
onInstallCommand: (commandKey) => void handleInstallCommand(commandKey),
connectors,
hasAtLeastOneReady,
onTestConnector: (id) => void handleTestConnector(id),
onSaveConnector: (id, env, model, notes) => void handleSaveConnector(id, env, model, notes),
onLoginConnector: (id) => void handleLoginConnector(id),
companyName,
setCompanyName,
companyDescription,
setCompanyDescription,
selectedConnectorId,
setSelectedConnectorId,
selectedModel,
setSelectedModel,
selectedConnector,
executionReadyConnectors,
connectorModelPools,
modelOptions,
loadingModels,
totalEnabledModels,
selectedConnectorEnabledModels,
onToggleConnectorModel: (connectorId, modelId) => void toggleConnectorModel(connectorId, modelId),
workspacePath,
setWorkspacePath,
onPickDirectory: () => void pickDirectory(),
executionMode,
setExecutionMode,
goalTitle,
setGoalTitle,
goalDescription,
setGoalDescription,
launchSummary,
}),
[
currentLocale,
t,
error,
loading,
nodeStatus,
nodeChecking,
checkNode,
handleInstallCommand,
connectors,
hasAtLeastOneReady,
handleTestConnector,
handleSaveConnector,
handleLoginConnector,
companyName,
companyDescription,
selectedConnectorId,
setSelectedConnectorId,
selectedModel,
setSelectedModel,
selectedConnector,
executionReadyConnectors,
connectorModelPools,
modelOptions,
loadingModels,
totalEnabledModels,
selectedConnectorEnabledModels,
toggleConnectorModel,
workspacePath,
pickDirectory,
executionMode,
goalTitle,
goalDescription,
launchSummary,
],
);
return (
<div
className={`relative h-[100dvh] overflow-hidden bg-background ${
@@ -178,51 +258,12 @@ export function CompanyOnboarding({
t={t}
/>
) : (
<>
<OnboardingProvider value={contextValue}>
<OnboardingStepPanel
step={heroStep}
currentLocale={currentLocale}
t={t}
canCancel={canCancel}
onCancel={onCancel}
isFirstLaunch={isFirstLaunch}
nodeStatus={nodeStatus}
nodeChecking={nodeChecking}
onCheckNode={() => void checkNode()}
onInstallCommand={(commandKey) => void handleInstallCommand(commandKey)}
connectors={connectors}
hasAtLeastOneReady={hasAtLeastOneReady}
onTestConnector={(id) => void handleTestConnector(id)}
onSaveConnector={(id, env, model, notes) => void handleSaveConnector(id, env, model, notes)}
onLoginConnector={(id) => void handleLoginConnector(id)}
error={error}
companyName={companyName}
setCompanyName={setCompanyName}
companyDescription={companyDescription}
setCompanyDescription={setCompanyDescription}
selectedConnectorId={selectedConnectorId}
setSelectedConnectorId={setSelectedConnectorId}
selectedModel={selectedModel}
setSelectedModel={setSelectedModel}
executionReadyConnectors={executionReadyConnectors}
connectorModelPools={connectorModelPools}
modelOptions={modelOptions}
loadingModels={loadingModels}
totalEnabledModels={totalEnabledModels}
selectedConnectorEnabledModels={selectedConnectorEnabledModels}
onToggleConnectorModel={(connectorId, modelId) => void toggleConnectorModel(connectorId, modelId)}
workspacePath={workspacePath}
setWorkspacePath={setWorkspacePath}
onPickDirectory={() => void pickDirectory()}
executionMode={executionMode}
setExecutionMode={setExecutionMode}
goalTitle={goalTitle}
setGoalTitle={setGoalTitle}
goalDescription={goalDescription}
setGoalDescription={setGoalDescription}
loading={loading}
launchSummary={launchSummary}
selectedConnector={selectedConnector}
onBack={setStep}
onContinueFromNodeCheck={() => { setError(null); setStep("providers"); }}
onContinueFromProviders={() => { setError(null); setStep("company"); }}
@@ -232,7 +273,7 @@ export function CompanyOnboarding({
onLaunchCompany={() => void launchCompany()}
onFinishLaunch={() => void finishLaunch()}
/>
</>
</OnboardingProvider>
)}
</div>
);
@@ -1,8 +1,6 @@
import { useState } from "react";
import { motion } from "framer-motion";
import { ArrowRight, FloppyDisk, SignIn } from "@phosphor-icons/react";
import type { ProfileSnapshot } from "@shared/types";
import type { TerminalCommandKey } from "@shared/contracts";
import { ActionButton } from "../ui";
import {
CompatProviderCard,
@@ -12,22 +10,15 @@ import {
INPUT_CLS,
} from "./ProvidersStepShared";
import { getStepNumber, TOTAL_STEPS } from "./OnboardingHero";
import { useOnboarding } from "./OnboardingContext";
/* ------------------------------------------------------------------ */
/* Props */
/* ------------------------------------------------------------------ */
interface ProvidersStepProps {
connectors: ProfileSnapshot["connectors"];
onTestConnector: (connectorId: string) => void;
onSaveConnector: (connectorId: string, envBindingText: string, model: string | null, notes?: string) => void;
onLoginConnector: (connectorId: string) => void;
onInstallCommand: (commandKey: TerminalCommandKey) => void;
hasAtLeastOneReady: boolean;
onContinue: () => void;
onBack: () => void;
error: string | null;
t: (key: string) => string;
}
const COMPAT_SLOTS = ["claude_compatible", "claude_compat_2", "claude_compat_3", "claude_compat_4", "claude_compat_5", "claude_compat_6", "claude_compat_7", "claude_compat_8", "claude_compat_9", "claude_compat_10"] as const;
@@ -36,18 +27,18 @@ const COMPAT_SLOTS = ["claude_compatible", "claude_compat_2", "claude_compat_3",
/* Main component */
/* ------------------------------------------------------------------ */
export function ProvidersStep({
connectors,
onTestConnector,
onSaveConnector,
onLoginConnector,
onInstallCommand,
hasAtLeastOneReady,
onContinue,
onBack,
error,
t,
}: ProvidersStepProps) {
export function ProvidersStep({ onContinue, onBack }: ProvidersStepProps) {
const {
connectors,
onTestConnector,
onSaveConnector,
onLoginConnector,
onInstallCommand,
hasAtLeastOneReady,
error,
t,
} = useOnboarding();
const [expandedCard, setExpandedCard] = useState<string | null>("claude");
const claudeConnector = connectors.find((c) => c.id === "claude_local");