fix: allow onboarding wizard pane to scroll when content overflows
The inner content wrapper in the onboarding form used flex-shrink default (1), causing it to compress instead of overflowing the scroll container. Adding shrink-0 prevents flex compression and lets overflow-y-auto work correctly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,7 @@ import { Dialog, DialogOverlay, DialogPortal } from "@/components/ui/dialog";
|
|||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { cn } from "../lib/utils";
|
import { cn } from "../lib/utils";
|
||||||
@@ -21,7 +21,7 @@ import { getUIAdapter } from "../adapters";
|
|||||||
import { defaultCreateValues } from "./agent-config-defaults";
|
import { defaultCreateValues } from "./agent-config-defaults";
|
||||||
import {
|
import {
|
||||||
DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX,
|
DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX,
|
||||||
DEFAULT_CODEX_LOCAL_MODEL,
|
DEFAULT_CODEX_LOCAL_MODEL
|
||||||
} from "@paperclipai/adapter-codex-local";
|
} from "@paperclipai/adapter-codex-local";
|
||||||
import { AsciiArtAnimation } from "./AsciiArtAnimation";
|
import { AsciiArtAnimation } from "./AsciiArtAnimation";
|
||||||
import { ChoosePathButton } from "./PathInstructionsModal";
|
import { ChoosePathButton } from "./PathInstructionsModal";
|
||||||
@@ -42,15 +42,22 @@ import {
|
|||||||
Loader2,
|
Loader2,
|
||||||
FolderOpen,
|
FolderOpen,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
X,
|
X
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
type Step = 1 | 2 | 3 | 4;
|
type Step = 1 | 2 | 3 | 4;
|
||||||
type AdapterType = "claude_local" | "codex_local" | "process" | "http" | "openclaw";
|
type AdapterType =
|
||||||
|
| "claude_local"
|
||||||
|
| "codex_local"
|
||||||
|
| "process"
|
||||||
|
| "http"
|
||||||
|
| "openclaw";
|
||||||
|
|
||||||
const DEFAULT_TASK_DESCRIPTION = `Setup yourself as the CEO. Use the ceo persona found here: [https://github.com/paperclipai/companies/blob/main/default/ceo/AGENTS.md](https://github.com/paperclipai/companies/blob/main/default/ceo/AGENTS.md)
|
const DEFAULT_TASK_DESCRIPTION = `Setup yourself as the CEO. Use the ceo persona found here: [https://github.com/paperclipai/companies/blob/main/default/ceo/AGENTS.md](https://github.com/paperclipai/companies/blob/main/default/ceo/AGENTS.md)
|
||||||
|
|
||||||
Ensure you have a folder agents/ceo and then download this AGENTS.md as well as the sibling HEARTBEAT.md, SOUL.md, and TOOLS.md. and set that AGENTS.md as the path to your agents instruction file`;
|
Ensure you have a folder agents/ceo and then download this AGENTS.md as well as the sibling HEARTBEAT.md, SOUL.md, and TOOLS.md. and set that AGENTS.md as the path to your agents instruction file
|
||||||
|
|
||||||
|
And after you've finished that, hire yourself a Founding Engineer agent`;
|
||||||
|
|
||||||
export function OnboardingWizard() {
|
export function OnboardingWizard() {
|
||||||
const { onboardingOpen, onboardingOptions, closeOnboarding } = useDialog();
|
const { onboardingOpen, onboardingOptions, closeOnboarding } = useDialog();
|
||||||
@@ -78,26 +85,33 @@ export function OnboardingWizard() {
|
|||||||
const [command, setCommand] = useState("");
|
const [command, setCommand] = useState("");
|
||||||
const [args, setArgs] = useState("");
|
const [args, setArgs] = useState("");
|
||||||
const [url, setUrl] = useState("");
|
const [url, setUrl] = useState("");
|
||||||
const [adapterEnvResult, setAdapterEnvResult] = useState<AdapterEnvironmentTestResult | null>(null);
|
const [adapterEnvResult, setAdapterEnvResult] =
|
||||||
|
useState<AdapterEnvironmentTestResult | null>(null);
|
||||||
const [adapterEnvError, setAdapterEnvError] = useState<string | null>(null);
|
const [adapterEnvError, setAdapterEnvError] = useState<string | null>(null);
|
||||||
const [adapterEnvLoading, setAdapterEnvLoading] = useState(false);
|
const [adapterEnvLoading, setAdapterEnvLoading] = useState(false);
|
||||||
|
|
||||||
// Step 3
|
// Step 3
|
||||||
const [taskTitle, setTaskTitle] = useState("Create your CEO HEARTBEAT.md");
|
const [taskTitle, setTaskTitle] = useState("Create your CEO HEARTBEAT.md");
|
||||||
const [taskDescription, setTaskDescription] = useState(DEFAULT_TASK_DESCRIPTION);
|
const [taskDescription, setTaskDescription] = useState(
|
||||||
|
DEFAULT_TASK_DESCRIPTION
|
||||||
|
);
|
||||||
|
|
||||||
// Auto-grow textarea for task description
|
// Auto-grow textarea for task description
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const autoResizeTextarea = useCallback(() => {
|
const autoResizeTextarea = useCallback(() => {
|
||||||
const el = textareaRef.current;
|
const el = textareaRef.current;
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
el.style.height = 'auto';
|
el.style.height = "auto";
|
||||||
el.style.height = el.scrollHeight + 'px';
|
el.style.height = el.scrollHeight + "px";
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Created entity IDs — pre-populate from existing company when skipping step 1
|
// Created entity IDs — pre-populate from existing company when skipping step 1
|
||||||
const [createdCompanyId, setCreatedCompanyId] = useState<string | null>(existingCompanyId ?? null);
|
const [createdCompanyId, setCreatedCompanyId] = useState<string | null>(
|
||||||
const [createdCompanyPrefix, setCreatedCompanyPrefix] = useState<string | null>(null);
|
existingCompanyId ?? null
|
||||||
|
);
|
||||||
|
const [createdCompanyPrefix, setCreatedCompanyPrefix] = useState<
|
||||||
|
string | null
|
||||||
|
>(null);
|
||||||
const [createdAgentId, setCreatedAgentId] = useState<string | null>(null);
|
const [createdAgentId, setCreatedAgentId] = useState<string | null>(null);
|
||||||
const [createdIssueRef, setCreatedIssueRef] = useState<string | null>(null);
|
const [createdIssueRef, setCreatedIssueRef] = useState<string | null>(null);
|
||||||
|
|
||||||
@@ -110,7 +124,11 @@ export function OnboardingWizard() {
|
|||||||
setStep(onboardingOptions.initialStep ?? 1);
|
setStep(onboardingOptions.initialStep ?? 1);
|
||||||
setCreatedCompanyId(cId);
|
setCreatedCompanyId(cId);
|
||||||
setCreatedCompanyPrefix(null);
|
setCreatedCompanyPrefix(null);
|
||||||
}, [onboardingOpen, onboardingOptions.companyId, onboardingOptions.initialStep]);
|
}, [
|
||||||
|
onboardingOpen,
|
||||||
|
onboardingOptions.companyId,
|
||||||
|
onboardingOptions.initialStep
|
||||||
|
]);
|
||||||
|
|
||||||
// Backfill issue prefix for an existing company once companies are loaded.
|
// Backfill issue prefix for an existing company once companies are loaded.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -127,10 +145,12 @@ export function OnboardingWizard() {
|
|||||||
const { data: adapterModels } = useQuery({
|
const { data: adapterModels } = useQuery({
|
||||||
queryKey: ["adapter-models", adapterType],
|
queryKey: ["adapter-models", adapterType],
|
||||||
queryFn: () => agentsApi.adapterModels(adapterType),
|
queryFn: () => agentsApi.adapterModels(adapterType),
|
||||||
enabled: onboardingOpen && step === 2,
|
enabled: onboardingOpen && step === 2
|
||||||
});
|
});
|
||||||
const isLocalAdapter = adapterType === "claude_local" || adapterType === "codex_local";
|
const isLocalAdapter =
|
||||||
const effectiveAdapterCommand = command.trim() || (adapterType === "codex_local" ? "codex" : "claude");
|
adapterType === "claude_local" || adapterType === "codex_local";
|
||||||
|
const effectiveAdapterCommand =
|
||||||
|
command.trim() || (adapterType === "codex_local" ? "codex" : "claude");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (step !== 2) return;
|
if (step !== 2) return;
|
||||||
@@ -175,7 +195,10 @@ export function OnboardingWizard() {
|
|||||||
...defaultCreateValues,
|
...defaultCreateValues,
|
||||||
adapterType,
|
adapterType,
|
||||||
cwd,
|
cwd,
|
||||||
model: adapterType === "codex_local" ? model || DEFAULT_CODEX_LOCAL_MODEL : model,
|
model:
|
||||||
|
adapterType === "codex_local"
|
||||||
|
? model || DEFAULT_CODEX_LOCAL_MODEL
|
||||||
|
: model,
|
||||||
command,
|
command,
|
||||||
args,
|
args,
|
||||||
url,
|
url,
|
||||||
@@ -183,25 +206,33 @@ export function OnboardingWizard() {
|
|||||||
dangerouslyBypassSandbox:
|
dangerouslyBypassSandbox:
|
||||||
adapterType === "codex_local"
|
adapterType === "codex_local"
|
||||||
? DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX
|
? DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX
|
||||||
: defaultCreateValues.dangerouslyBypassSandbox,
|
: defaultCreateValues.dangerouslyBypassSandbox
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runAdapterEnvironmentTest(): Promise<AdapterEnvironmentTestResult | null> {
|
async function runAdapterEnvironmentTest(): Promise<AdapterEnvironmentTestResult | null> {
|
||||||
if (!createdCompanyId) {
|
if (!createdCompanyId) {
|
||||||
setAdapterEnvError("Create or select a company before testing adapter environment.");
|
setAdapterEnvError(
|
||||||
|
"Create or select a company before testing adapter environment."
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
setAdapterEnvLoading(true);
|
setAdapterEnvLoading(true);
|
||||||
setAdapterEnvError(null);
|
setAdapterEnvError(null);
|
||||||
try {
|
try {
|
||||||
const result = await agentsApi.testEnvironment(createdCompanyId, adapterType, {
|
const result = await agentsApi.testEnvironment(
|
||||||
adapterConfig: buildAdapterConfig(),
|
createdCompanyId,
|
||||||
});
|
adapterType,
|
||||||
|
{
|
||||||
|
adapterConfig: buildAdapterConfig()
|
||||||
|
}
|
||||||
|
);
|
||||||
setAdapterEnvResult(result);
|
setAdapterEnvResult(result);
|
||||||
return result;
|
return result;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setAdapterEnvError(err instanceof Error ? err.message : "Adapter environment test failed");
|
setAdapterEnvError(
|
||||||
|
err instanceof Error ? err.message : "Adapter environment test failed"
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
setAdapterEnvLoading(false);
|
setAdapterEnvLoading(false);
|
||||||
@@ -222,9 +253,11 @@ export function OnboardingWizard() {
|
|||||||
await goalsApi.create(company.id, {
|
await goalsApi.create(company.id, {
|
||||||
title: companyGoal.trim(),
|
title: companyGoal.trim(),
|
||||||
level: "company",
|
level: "company",
|
||||||
status: "active",
|
status: "active"
|
||||||
|
});
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: queryKeys.goals.list(company.id)
|
||||||
});
|
});
|
||||||
queryClient.invalidateQueries({ queryKey: queryKeys.goals.list(company.id) });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setStep(2);
|
setStep(2);
|
||||||
@@ -244,7 +277,9 @@ export function OnboardingWizard() {
|
|||||||
const result = adapterEnvResult ?? (await runAdapterEnvironmentTest());
|
const result = adapterEnvResult ?? (await runAdapterEnvironmentTest());
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
if (result.status === "fail") {
|
if (result.status === "fail") {
|
||||||
setError("Adapter environment test failed. Fix the errors and test again before continuing.");
|
setError(
|
||||||
|
"Adapter environment test failed. Fix the errors and test again before continuing."
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,13 +295,13 @@ export function OnboardingWizard() {
|
|||||||
intervalSec: 300,
|
intervalSec: 300,
|
||||||
wakeOnDemand: true,
|
wakeOnDemand: true,
|
||||||
cooldownSec: 10,
|
cooldownSec: 10,
|
||||||
maxConcurrentRuns: 1,
|
maxConcurrentRuns: 1
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
setCreatedAgentId(agent.id);
|
setCreatedAgentId(agent.id);
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: queryKeys.agents.list(createdCompanyId),
|
queryKey: queryKeys.agents.list(createdCompanyId)
|
||||||
});
|
});
|
||||||
setStep(3);
|
setStep(3);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -283,13 +318,15 @@ export function OnboardingWizard() {
|
|||||||
try {
|
try {
|
||||||
const issue = await issuesApi.create(createdCompanyId, {
|
const issue = await issuesApi.create(createdCompanyId, {
|
||||||
title: taskTitle.trim(),
|
title: taskTitle.trim(),
|
||||||
...(taskDescription.trim() ? { description: taskDescription.trim() } : {}),
|
...(taskDescription.trim()
|
||||||
|
? { description: taskDescription.trim() }
|
||||||
|
: {}),
|
||||||
assigneeAgentId: createdAgentId,
|
assigneeAgentId: createdAgentId,
|
||||||
status: "todo",
|
status: "todo"
|
||||||
});
|
});
|
||||||
setCreatedIssueRef(issue.identifier ?? issue.id);
|
setCreatedIssueRef(issue.identifier ?? issue.id);
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: queryKeys.issues.list(createdCompanyId),
|
queryKey: queryKeys.issues.list(createdCompanyId)
|
||||||
});
|
});
|
||||||
setStep(4);
|
setStep(4);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -343,10 +380,7 @@ export function OnboardingWizard() {
|
|||||||
>
|
>
|
||||||
<DialogPortal>
|
<DialogPortal>
|
||||||
<DialogOverlay className="bg-background" />
|
<DialogOverlay className="bg-background" />
|
||||||
<div
|
<div className="fixed inset-0 z-50 flex" onKeyDown={handleKeyDown}>
|
||||||
className="fixed inset-0 z-50 flex"
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
>
|
|
||||||
{/* Close button */}
|
{/* Close button */}
|
||||||
<button
|
<button
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
@@ -358,7 +392,7 @@ export function OnboardingWizard() {
|
|||||||
|
|
||||||
{/* Left half — form */}
|
{/* Left half — form */}
|
||||||
<div className="w-full md:w-1/2 flex flex-col overflow-y-auto">
|
<div className="w-full md:w-1/2 flex flex-col overflow-y-auto">
|
||||||
<div className="w-full max-w-md mx-auto my-auto px-8 py-12">
|
<div className="w-full max-w-md mx-auto my-auto px-8 py-12 shrink-0">
|
||||||
{/* Progress indicators */}
|
{/* Progress indicators */}
|
||||||
<div className="flex items-center gap-2 mb-8">
|
<div className="flex items-center gap-2 mb-8">
|
||||||
<Sparkles className="h-4 w-4 text-muted-foreground" />
|
<Sparkles className="h-4 w-4 text-muted-foreground" />
|
||||||
@@ -455,48 +489,48 @@ export function OnboardingWizard() {
|
|||||||
Adapter type
|
Adapter type
|
||||||
</label>
|
</label>
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
{([
|
{[
|
||||||
{
|
{
|
||||||
value: "claude_local" as const,
|
value: "claude_local" as const,
|
||||||
label: "Claude Code",
|
label: "Claude Code",
|
||||||
icon: Sparkles,
|
icon: Sparkles,
|
||||||
desc: "Local Claude agent",
|
desc: "Local Claude agent"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "codex_local" as const,
|
value: "codex_local" as const,
|
||||||
label: "Codex",
|
label: "Codex",
|
||||||
icon: Code,
|
icon: Code,
|
||||||
desc: "Local Codex agent",
|
desc: "Local Codex agent"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "openclaw" as const,
|
value: "openclaw" as const,
|
||||||
label: "OpenClaw",
|
label: "OpenClaw",
|
||||||
icon: Bot,
|
icon: Bot,
|
||||||
desc: "Notify OpenClaw webhook",
|
desc: "Notify OpenClaw webhook",
|
||||||
comingSoon: true,
|
comingSoon: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "cursor" as const,
|
value: "cursor" as const,
|
||||||
label: "Cursor",
|
label: "Cursor",
|
||||||
icon: MousePointer2,
|
icon: MousePointer2,
|
||||||
desc: "Cursor AI agent",
|
desc: "Cursor AI agent",
|
||||||
comingSoon: true,
|
comingSoon: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "process" as const,
|
value: "process" as const,
|
||||||
label: "Shell Command",
|
label: "Shell Command",
|
||||||
icon: Terminal,
|
icon: Terminal,
|
||||||
desc: "Run a process",
|
desc: "Run a process",
|
||||||
comingSoon: true,
|
comingSoon: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "http" as const,
|
value: "http" as const,
|
||||||
label: "HTTP Webhook",
|
label: "HTTP Webhook",
|
||||||
icon: Globe,
|
icon: Globe,
|
||||||
desc: "Call an endpoint",
|
desc: "Call an endpoint",
|
||||||
comingSoon: true,
|
comingSoon: true
|
||||||
},
|
}
|
||||||
]).map((opt) => (
|
].map((opt) => (
|
||||||
<button
|
<button
|
||||||
key={opt.value}
|
key={opt.value}
|
||||||
disabled={!!opt.comingSoon}
|
disabled={!!opt.comingSoon}
|
||||||
@@ -528,7 +562,8 @@ export function OnboardingWizard() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Conditional adapter fields */}
|
{/* Conditional adapter fields */}
|
||||||
{(adapterType === "claude_local" || adapterType === "codex_local") && (
|
{(adapterType === "claude_local" ||
|
||||||
|
adapterType === "codex_local") && (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-1.5 mb-1">
|
<div className="flex items-center gap-1.5 mb-1">
|
||||||
@@ -555,19 +590,31 @@ export function OnboardingWizard() {
|
|||||||
<Popover open={modelOpen} onOpenChange={setModelOpen}>
|
<Popover open={modelOpen} onOpenChange={setModelOpen}>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<button className="inline-flex items-center gap-1.5 rounded-md border border-border px-2.5 py-1.5 text-sm hover:bg-accent/50 transition-colors w-full justify-between">
|
<button className="inline-flex items-center gap-1.5 rounded-md border border-border px-2.5 py-1.5 text-sm hover:bg-accent/50 transition-colors w-full justify-between">
|
||||||
<span className={cn(!model && "text-muted-foreground")}>
|
<span
|
||||||
{selectedModel ? selectedModel.label : model || "Default"}
|
className={cn(
|
||||||
|
!model && "text-muted-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{selectedModel
|
||||||
|
? selectedModel.label
|
||||||
|
: model || "Default"}
|
||||||
</span>
|
</span>
|
||||||
<ChevronDown className="h-3 w-3 text-muted-foreground" />
|
<ChevronDown className="h-3 w-3 text-muted-foreground" />
|
||||||
</button>
|
</button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-[var(--radix-popover-trigger-width)] p-1" align="start">
|
<PopoverContent
|
||||||
|
className="w-[var(--radix-popover-trigger-width)] p-1"
|
||||||
|
align="start"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center gap-2 w-full px-2 py-1.5 text-sm rounded hover:bg-accent/50",
|
"flex items-center gap-2 w-full px-2 py-1.5 text-sm rounded hover:bg-accent/50",
|
||||||
!model && "bg-accent"
|
!model && "bg-accent"
|
||||||
)}
|
)}
|
||||||
onClick={() => { setModel(""); setModelOpen(false); }}
|
onClick={() => {
|
||||||
|
setModel("");
|
||||||
|
setModelOpen(false);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Default
|
Default
|
||||||
</button>
|
</button>
|
||||||
@@ -578,10 +625,15 @@ export function OnboardingWizard() {
|
|||||||
"flex items-center justify-between w-full px-2 py-1.5 text-sm rounded hover:bg-accent/50",
|
"flex items-center justify-between w-full px-2 py-1.5 text-sm rounded hover:bg-accent/50",
|
||||||
m.id === model && "bg-accent"
|
m.id === model && "bg-accent"
|
||||||
)}
|
)}
|
||||||
onClick={() => { setModel(m.id); setModelOpen(false); }}
|
onClick={() => {
|
||||||
|
setModel(m.id);
|
||||||
|
setModelOpen(false);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<span>{m.label}</span>
|
<span>{m.label}</span>
|
||||||
<span className="text-xs text-muted-foreground font-mono">{m.id}</span>
|
<span className="text-xs text-muted-foreground font-mono">
|
||||||
|
{m.id}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
@@ -594,9 +646,12 @@ export function OnboardingWizard() {
|
|||||||
<div className="space-y-2 rounded-md border border-border p-3">
|
<div className="space-y-2 rounded-md border border-border p-3">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs font-medium">Adapter environment check</p>
|
<p className="text-xs font-medium">
|
||||||
|
Adapter environment check
|
||||||
|
</p>
|
||||||
<p className="text-[11px] text-muted-foreground">
|
<p className="text-[11px] text-muted-foreground">
|
||||||
Runs a live probe that asks the adapter CLI to respond with hello.
|
Runs a live probe that asks the adapter CLI to
|
||||||
|
respond with hello.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
@@ -628,16 +683,21 @@ export function OnboardingWizard() {
|
|||||||
: `${effectiveAdapterCommand} --print - --output-format stream-json --verbose`}
|
: `${effectiveAdapterCommand} --print - --output-format stream-json --verbose`}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
Prompt: <span className="font-mono">Respond with hello.</span>
|
Prompt:{" "}
|
||||||
|
<span className="font-mono">Respond with hello.</span>
|
||||||
</p>
|
</p>
|
||||||
{adapterType === "codex_local" ? (
|
{adapterType === "codex_local" ? (
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
If auth fails, set <span className="font-mono">OPENAI_API_KEY</span> in env or run{" "}
|
If auth fails, set{" "}
|
||||||
|
<span className="font-mono">OPENAI_API_KEY</span> in
|
||||||
|
env or run{" "}
|
||||||
<span className="font-mono">codex login</span>.
|
<span className="font-mono">codex login</span>.
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
If login is required, run <span className="font-mono">claude login</span> and retry.
|
If login is required, run{" "}
|
||||||
|
<span className="font-mono">claude login</span> and
|
||||||
|
retry.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -696,8 +756,8 @@ export function OnboardingWizard() {
|
|||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium">Give it something to do</h3>
|
<h3 className="font-medium">Give it something to do</h3>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Give your agent a small task to start with — a bug fix, a
|
Give your agent a small task to start with — a bug fix,
|
||||||
research question, writing a script.
|
a research question, writing a script.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -737,7 +797,8 @@ export function OnboardingWizard() {
|
|||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium">Ready to launch</h3>
|
<h3 className="font-medium">Ready to launch</h3>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Everything is set up. Launch your agent and watch it work.
|
Everything is set up. Launch your agent and watch it
|
||||||
|
work.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -745,7 +806,9 @@ export function OnboardingWizard() {
|
|||||||
<div className="flex items-center gap-3 px-3 py-2.5">
|
<div className="flex items-center gap-3 px-3 py-2.5">
|
||||||
<Building2 className="h-4 w-4 text-muted-foreground shrink-0" />
|
<Building2 className="h-4 w-4 text-muted-foreground shrink-0" />
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-sm font-medium truncate">{companyName}</p>
|
<p className="text-sm font-medium truncate">
|
||||||
|
{companyName}
|
||||||
|
</p>
|
||||||
<p className="text-xs text-muted-foreground">Company</p>
|
<p className="text-xs text-muted-foreground">Company</p>
|
||||||
</div>
|
</div>
|
||||||
<Check className="h-4 w-4 text-green-500 shrink-0" />
|
<Check className="h-4 w-4 text-green-500 shrink-0" />
|
||||||
@@ -753,7 +816,9 @@ export function OnboardingWizard() {
|
|||||||
<div className="flex items-center gap-3 px-3 py-2.5">
|
<div className="flex items-center gap-3 px-3 py-2.5">
|
||||||
<Bot className="h-4 w-4 text-muted-foreground shrink-0" />
|
<Bot className="h-4 w-4 text-muted-foreground shrink-0" />
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-sm font-medium truncate">{agentName}</p>
|
<p className="text-sm font-medium truncate">
|
||||||
|
{agentName}
|
||||||
|
</p>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
{getUIAdapter(adapterType).label}
|
{getUIAdapter(adapterType).label}
|
||||||
</p>
|
</p>
|
||||||
@@ -763,7 +828,9 @@ export function OnboardingWizard() {
|
|||||||
<div className="flex items-center gap-3 px-3 py-2.5">
|
<div className="flex items-center gap-3 px-3 py-2.5">
|
||||||
<ListTodo className="h-4 w-4 text-muted-foreground shrink-0" />
|
<ListTodo className="h-4 w-4 text-muted-foreground shrink-0" />
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-sm font-medium truncate">{taskTitle}</p>
|
<p className="text-sm font-medium truncate">
|
||||||
|
{taskTitle}
|
||||||
|
</p>
|
||||||
<p className="text-xs text-muted-foreground">Task</p>
|
<p className="text-xs text-muted-foreground">Task</p>
|
||||||
</div>
|
</div>
|
||||||
<Check className="h-4 w-4 text-green-500 shrink-0" />
|
<Check className="h-4 w-4 text-green-500 shrink-0" />
|
||||||
@@ -812,7 +879,9 @@ export function OnboardingWizard() {
|
|||||||
{step === 2 && (
|
{step === 2 && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
disabled={!agentName.trim() || loading || adapterEnvLoading}
|
disabled={
|
||||||
|
!agentName.trim() || loading || adapterEnvLoading
|
||||||
|
}
|
||||||
onClick={handleStep2Next}
|
onClick={handleStep2Next}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
@@ -862,9 +931,17 @@ export function OnboardingWizard() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AdapterEnvironmentResult({ result }: { result: AdapterEnvironmentTestResult }) {
|
function AdapterEnvironmentResult({
|
||||||
|
result
|
||||||
|
}: {
|
||||||
|
result: AdapterEnvironmentTestResult;
|
||||||
|
}) {
|
||||||
const statusLabel =
|
const statusLabel =
|
||||||
result.status === "pass" ? "Passed" : result.status === "warn" ? "Warnings" : "Failed";
|
result.status === "pass"
|
||||||
|
? "Passed"
|
||||||
|
: result.status === "warn"
|
||||||
|
? "Warnings"
|
||||||
|
: "Failed";
|
||||||
const statusClass =
|
const statusClass =
|
||||||
result.status === "pass"
|
result.status === "pass"
|
||||||
? "text-green-700 dark:text-green-300 border-green-300 dark:border-green-500/40 bg-green-50 dark:bg-green-500/10"
|
? "text-green-700 dark:text-green-300 border-green-300 dark:border-green-500/40 bg-green-50 dark:bg-green-500/10"
|
||||||
@@ -882,14 +959,25 @@ function AdapterEnvironmentResult({ result }: { result: AdapterEnvironmentTestRe
|
|||||||
</div>
|
</div>
|
||||||
<div className="mt-1.5 space-y-1">
|
<div className="mt-1.5 space-y-1">
|
||||||
{result.checks.map((check, idx) => (
|
{result.checks.map((check, idx) => (
|
||||||
<div key={`${check.code}-${idx}`} className="leading-relaxed break-words">
|
<div
|
||||||
|
key={`${check.code}-${idx}`}
|
||||||
|
className="leading-relaxed break-words"
|
||||||
|
>
|
||||||
<span className="font-medium uppercase tracking-wide opacity-80">
|
<span className="font-medium uppercase tracking-wide opacity-80">
|
||||||
{check.level}
|
{check.level}
|
||||||
</span>
|
</span>
|
||||||
<span className="mx-1 opacity-60">·</span>
|
<span className="mx-1 opacity-60">·</span>
|
||||||
<span>{check.message}</span>
|
<span>{check.message}</span>
|
||||||
{check.detail && <span className="block opacity-75 break-all">({check.detail})</span>}
|
{check.detail && (
|
||||||
{check.hint && <span className="block opacity-90 break-words">Hint: {check.hint}</span>}
|
<span className="block opacity-75 break-all">
|
||||||
|
({check.detail})
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{check.hint && (
|
||||||
|
<span className="block opacity-90 break-words">
|
||||||
|
Hint: {check.hint}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user