@@ -301,10 +383,10 @@ export function CloudEnvironmentConnectRows({
) : (
)}
diff --git a/apps/web/src/components/onboarding/FirstRunGate.tsx b/apps/web/src/components/onboarding/FirstRunGate.tsx
index a9df5cb00..939dbbcc4 100644
--- a/apps/web/src/components/onboarding/FirstRunGate.tsx
+++ b/apps/web/src/components/onboarding/FirstRunGate.tsx
@@ -1,15 +1,14 @@
+import { RefreshIcon } from "~/components/ui/refresh-icon";
import { useAtomValue } from "@effect/atom-react";
import { useLocation, useNavigate } from "@tanstack/react-router";
import { Atom } from "effect/unstable/reactivity";
-import { RotateCcwIcon } from "lucide-react";
-import { useEffect, useLayoutEffect, useState } from "react";
+import { useEffect, useState } from "react";
import {
ensureClientSettingsHydrated,
useClientSettings,
useClientSettingsHydrationStatus,
} from "../../hooks/useSettings";
-import { mountOnboardingTheme } from "../../hooks/useTheme";
import { useCompleteOnboarding } from "../../onboarding/firstRun";
import {
isFirstRunWorkspaceProvenanceAuthoritative,
@@ -104,13 +103,6 @@ export function FirstRunGate({
}));
const { decision, stalled } = gateState;
const settingsReadFailed = hydrationStatus === "failed" || hydrationStatus === "retrying";
- const ownsOnboardingTheme = settingsReadFailed || stalled || decision === "wizard";
-
- useLayoutEffect(() => {
- if (!ownsOnboardingTheme) return;
- return mountOnboardingTheme();
- }, [ownsOnboardingTheme]);
-
// A workspace still counts as fresh when its only content is the server's
// own cwd auto-bootstrap: web mode creates a project + thread from cwd at
// startup (`autoBootstrapProjectFromCwd` defaults on there), so "no
@@ -220,7 +212,7 @@ function FirstRunRecovery({
{settingsReadFailed
? "Your saved settings could not be loaded."
- : "T3 Code could not confirm this workspace."}
+ : "Pylon could not confirm this workspace."}
diff --git a/apps/web/src/components/onboarding/WelcomeWizard.tsx b/apps/web/src/components/onboarding/WelcomeWizard.tsx
index 70c99853f..40af2a333 100644
--- a/apps/web/src/components/onboarding/WelcomeWizard.tsx
+++ b/apps/web/src/components/onboarding/WelcomeWizard.tsx
@@ -1,7 +1,6 @@
import { useAuth } from "@clerk/react";
import { useAtomValue } from "@effect/atom-react";
import type {
- AgentSessionProjectCandidate,
EnvironmentId,
ProjectId,
ScopedProjectRef,
@@ -18,25 +17,23 @@ import * as Schema from "effect/Schema";
import {
ArrowRightIcon,
CheckIcon,
- ChevronLeftIcon,
ChevronRightIcon,
CloudIcon,
CopyIcon,
LinkIcon,
MonitorIcon,
TerminalIcon,
- type LucideIcon,
} from "lucide-react";
-import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { TYPOGRAPHY_ADVANCED_STORAGE_KEY } from "../../appearanceFonts";
import { useLocalStorage } from "../../hooks/useLocalStorage";
-import { mountOnboardingTheme } from "../../hooks/useTheme";
import { hasCloudPublicConfig } from "../../cloud/publicConfig";
import { useT3ConnectAuthPrompt } from "../clerk/useT3ConnectAuthPrompt";
import { useCompleteOnboarding } from "../../onboarding/firstRun";
import {
partitionOnboardingProjects,
+ onboardingProjectKey,
resolveOnboardingLandingProject,
resolveOnboardingProjectId,
} from "../../onboarding/projectImport.logic";
@@ -46,37 +43,38 @@ import {
resolveOnboardingProviderLoginCommand,
selectOnboardingProvidersByDriver,
} from "../../onboarding/providerReadiness.logic";
-import {
- isOnboardingRelayEnvironment,
- resolveOnboardingTargetEnvironment,
-} from "../../onboarding/targetEnvironment.logic";
import { useCopyToClipboard } from "../../hooks/useCopyToClipboard";
import { newProjectId, randomUUID } from "../../lib/utils";
import { resolveDefaultProviderModelSelection } from "../../providerInstances";
-import { agentSessionImport, agentSessionScan } from "../../state/agentSessions";
+import { agentSessionImport } from "../../state/agentSessions";
import { readProjects, useProjects } from "../../state/entities";
import { useEnvironments, usePrimaryEnvironment } from "../../state/environments";
-import { useEnvironmentQuery } from "../../state/query";
+import { isOnboardingRelayEnvironment } from "../../onboarding/targetEnvironment.logic";
+import { useProjectScans } from "../../onboarding/useProjectScans";
import { projectEnvironment } from "../../state/projects";
import { serverEnvironment } from "../../state/server";
import { terminalEnvironment } from "../../state/terminal";
import { useAtomCommand } from "../../state/use-atom-command";
import { connectPairing } from "../../connection/onboarding";
-import { isElectron } from "../../env";
-import { formatRelativeTimeLabel } from "../../timestampFormat";
import { getProviderSummary } from "../settings/providerStatus";
import { getDriverOption } from "../settings/providerDriverMeta";
-import { CloudEnvironmentConnectRows } from "../cloud/CloudEnvironmentConnectList";
import { TerminalViewport } from "../ThreadTerminalDrawer";
+import { CloudEnvironmentConnectRows } from "../cloud/CloudEnvironmentConnectList";
+import { PylonMark } from "../PylonMark";
import { Button } from "../ui/button";
import { Checkbox } from "../ui/checkbox";
import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "../ui/collapsible";
import { Input } from "../ui/input";
+import { Tooltip, TooltipTrigger, TooltipPopup } from "../ui/tooltip";
+import { ScrollArea } from "../ui/scroll-area";
+import { Spinner } from "../ui/spinner";
+import { WizardPanel, WizardSteps } from "../ui/wizard";
+import { Dialog, DialogHeader, DialogPopup, DialogTitle } from "../ui/dialog";
import { toastManager } from "../ui/toast";
import { cn } from "../../lib/utils";
/**
- * First-run welcome wizard. Rendered as the full-screen `/welcome` route on a
+ * First-run welcome wizard. Rendered over the workspace at `/welcome` on a
* fresh install (no completed-onboarding flag, empty workspace). Flow per the
* onboarding overhaul spec: connection choice → sign-in/pair (remote paths) →
* agent setup with inline install terminal → project import → main screen.
@@ -84,32 +82,8 @@ import { cn } from "../../lib/utils";
* re-runnable by clearing the flag.
*/
-type WizardStep = "connection" | "connect-machines" | "pair-direct" | "agents" | "import";
-
-type ConnectionMode = "local" | "connect" | "direct";
-
-/**
- * The machine the agent and import steps run against. Local mode targets the
- * primary environment; the remote modes prefer the machine the user just
- * connected (the most recently added connected non-primary environment), so
- * probing and import happen where their code lives rather than on the local
- * server that happens to serve the app. Deliberately not a persisted
- * "primary machine" concept — just whichever machine fits the chosen path
- * right now, labeled inline on each step.
- */
-function useOnboardingTargetEnvironment(
- mode: ConnectionMode,
- pairedEnvironmentId: EnvironmentId | null,
-) {
- const { environments } = useEnvironments();
- const primaryEnvironment = usePrimaryEnvironment();
- return resolveOnboardingTargetEnvironment({
- mode,
- environments,
- primaryEnvironment,
- pairedEnvironmentId,
- });
-}
+type WizardStep = "connection" | "agents" | "import";
+const NO_ENVIRONMENTS: readonly EnvironmentId[] = [];
const AGENT_ONBOARDING_THREAD_ID = ThreadId.make("onboarding-agent-setup");
const ONBOARDING_STAGES = ["Connect", "Agents", "Projects"] as const;
@@ -119,23 +93,48 @@ export function WelcomeWizard({
localAvailable,
onDone,
}: {
- /**
- * Whether the "Local Only" card is offered. True whenever the app is served
- * by an authenticated primary server — desktop, `npx t3`, or a dev server —
- * since that server is "this machine" regardless of the hostname the app
- * was opened from. Only hosted-static (app.t3.codes) has no local server.
- */
+ /** Whether this client is authenticated to the server serving the app. */
readonly localAvailable: boolean;
readonly onDone: (projectRef?: ScopedProjectRef) => void;
}) {
- useLayoutEffect(() => mountOnboardingTheme(), []);
const completeOnboarding = useCompleteOnboarding();
const [step, setStep] = useState