From d232858afe3c66507f368c3d6abeaed32a6f38de Mon Sep 17 00:00:00 2001 From: Dotta Date: Tue, 3 Mar 2026 13:05:35 -0600 Subject: [PATCH] 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 --- ui/src/components/OnboardingWizard.tsx | 228 +++++++++++++++++-------- 1 file changed, 158 insertions(+), 70 deletions(-) diff --git a/ui/src/components/OnboardingWizard.tsx b/ui/src/components/OnboardingWizard.tsx index 12deaa4b..51ac157a 100644 --- a/ui/src/components/OnboardingWizard.tsx +++ b/ui/src/components/OnboardingWizard.tsx @@ -13,7 +13,7 @@ import { Dialog, DialogOverlay, DialogPortal } from "@/components/ui/dialog"; import { Popover, PopoverContent, - PopoverTrigger, + PopoverTrigger } from "@/components/ui/popover"; import { Button } from "@/components/ui/button"; import { cn } from "../lib/utils"; @@ -21,7 +21,7 @@ import { getUIAdapter } from "../adapters"; import { defaultCreateValues } from "./agent-config-defaults"; import { DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX, - DEFAULT_CODEX_LOCAL_MODEL, + DEFAULT_CODEX_LOCAL_MODEL } from "@paperclipai/adapter-codex-local"; import { AsciiArtAnimation } from "./AsciiArtAnimation"; import { ChoosePathButton } from "./PathInstructionsModal"; @@ -42,15 +42,22 @@ import { Loader2, FolderOpen, ChevronDown, - X, + X } from "lucide-react"; 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) -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() { const { onboardingOpen, onboardingOptions, closeOnboarding } = useDialog(); @@ -78,26 +85,33 @@ export function OnboardingWizard() { const [command, setCommand] = useState(""); const [args, setArgs] = useState(""); const [url, setUrl] = useState(""); - const [adapterEnvResult, setAdapterEnvResult] = useState(null); + const [adapterEnvResult, setAdapterEnvResult] = + useState(null); const [adapterEnvError, setAdapterEnvError] = useState(null); const [adapterEnvLoading, setAdapterEnvLoading] = useState(false); // Step 3 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 const textareaRef = useRef(null); const autoResizeTextarea = useCallback(() => { const el = textareaRef.current; if (!el) return; - el.style.height = 'auto'; - el.style.height = el.scrollHeight + 'px'; + el.style.height = "auto"; + el.style.height = el.scrollHeight + "px"; }, []); // Created entity IDs — pre-populate from existing company when skipping step 1 - const [createdCompanyId, setCreatedCompanyId] = useState(existingCompanyId ?? null); - const [createdCompanyPrefix, setCreatedCompanyPrefix] = useState(null); + const [createdCompanyId, setCreatedCompanyId] = useState( + existingCompanyId ?? null + ); + const [createdCompanyPrefix, setCreatedCompanyPrefix] = useState< + string | null + >(null); const [createdAgentId, setCreatedAgentId] = useState(null); const [createdIssueRef, setCreatedIssueRef] = useState(null); @@ -110,7 +124,11 @@ export function OnboardingWizard() { setStep(onboardingOptions.initialStep ?? 1); setCreatedCompanyId(cId); setCreatedCompanyPrefix(null); - }, [onboardingOpen, onboardingOptions.companyId, onboardingOptions.initialStep]); + }, [ + onboardingOpen, + onboardingOptions.companyId, + onboardingOptions.initialStep + ]); // Backfill issue prefix for an existing company once companies are loaded. useEffect(() => { @@ -127,10 +145,12 @@ export function OnboardingWizard() { const { data: adapterModels } = useQuery({ queryKey: ["adapter-models", adapterType], queryFn: () => agentsApi.adapterModels(adapterType), - enabled: onboardingOpen && step === 2, + enabled: onboardingOpen && step === 2 }); - const isLocalAdapter = adapterType === "claude_local" || adapterType === "codex_local"; - const effectiveAdapterCommand = command.trim() || (adapterType === "codex_local" ? "codex" : "claude"); + const isLocalAdapter = + adapterType === "claude_local" || adapterType === "codex_local"; + const effectiveAdapterCommand = + command.trim() || (adapterType === "codex_local" ? "codex" : "claude"); useEffect(() => { if (step !== 2) return; @@ -175,7 +195,10 @@ export function OnboardingWizard() { ...defaultCreateValues, adapterType, cwd, - model: adapterType === "codex_local" ? model || DEFAULT_CODEX_LOCAL_MODEL : model, + model: + adapterType === "codex_local" + ? model || DEFAULT_CODEX_LOCAL_MODEL + : model, command, args, url, @@ -183,25 +206,33 @@ export function OnboardingWizard() { dangerouslyBypassSandbox: adapterType === "codex_local" ? DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX - : defaultCreateValues.dangerouslyBypassSandbox, + : defaultCreateValues.dangerouslyBypassSandbox }); } async function runAdapterEnvironmentTest(): Promise { if (!createdCompanyId) { - setAdapterEnvError("Create or select a company before testing adapter environment."); + setAdapterEnvError( + "Create or select a company before testing adapter environment." + ); return null; } setAdapterEnvLoading(true); setAdapterEnvError(null); try { - const result = await agentsApi.testEnvironment(createdCompanyId, adapterType, { - adapterConfig: buildAdapterConfig(), - }); + const result = await agentsApi.testEnvironment( + createdCompanyId, + adapterType, + { + adapterConfig: buildAdapterConfig() + } + ); setAdapterEnvResult(result); return result; } catch (err) { - setAdapterEnvError(err instanceof Error ? err.message : "Adapter environment test failed"); + setAdapterEnvError( + err instanceof Error ? err.message : "Adapter environment test failed" + ); return null; } finally { setAdapterEnvLoading(false); @@ -222,9 +253,11 @@ export function OnboardingWizard() { await goalsApi.create(company.id, { title: companyGoal.trim(), level: "company", - status: "active", + status: "active" + }); + queryClient.invalidateQueries({ + queryKey: queryKeys.goals.list(company.id) }); - queryClient.invalidateQueries({ queryKey: queryKeys.goals.list(company.id) }); } setStep(2); @@ -244,7 +277,9 @@ export function OnboardingWizard() { const result = adapterEnvResult ?? (await runAdapterEnvironmentTest()); if (!result) return; 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; } } @@ -260,13 +295,13 @@ export function OnboardingWizard() { intervalSec: 300, wakeOnDemand: true, cooldownSec: 10, - maxConcurrentRuns: 1, - }, - }, + maxConcurrentRuns: 1 + } + } }); setCreatedAgentId(agent.id); queryClient.invalidateQueries({ - queryKey: queryKeys.agents.list(createdCompanyId), + queryKey: queryKeys.agents.list(createdCompanyId) }); setStep(3); } catch (err) { @@ -283,13 +318,15 @@ export function OnboardingWizard() { try { const issue = await issuesApi.create(createdCompanyId, { title: taskTitle.trim(), - ...(taskDescription.trim() ? { description: taskDescription.trim() } : {}), + ...(taskDescription.trim() + ? { description: taskDescription.trim() } + : {}), assigneeAgentId: createdAgentId, - status: "todo", + status: "todo" }); setCreatedIssueRef(issue.identifier ?? issue.id); queryClient.invalidateQueries({ - queryKey: queryKeys.issues.list(createdCompanyId), + queryKey: queryKeys.issues.list(createdCompanyId) }); setStep(4); } catch (err) { @@ -343,10 +380,7 @@ export function OnboardingWizard() { > -
+
{/* Close 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", m.id === model && "bg-accent" )} - onClick={() => { setModel(m.id); setModelOpen(false); }} + onClick={() => { + setModel(m.id); + setModelOpen(false); + }} > {m.label} - {m.id} + + {m.id} + ))} @@ -594,9 +646,12 @@ export function OnboardingWizard() {
-

Adapter environment check

+

+ Adapter environment check +

- 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.

@@ -696,8 +756,8 @@ export function OnboardingWizard() {

Give it something to do

- Give your agent a small task to start with — a bug fix, a - research question, writing a script. + Give your agent a small task to start with — a bug fix, + a research question, writing a script.

@@ -737,7 +797,8 @@ export function OnboardingWizard() {

Ready to launch

- Everything is set up. Launch your agent and watch it work. + Everything is set up. Launch your agent and watch it + work.

@@ -745,7 +806,9 @@ export function OnboardingWizard() {
-

{companyName}

+

+ {companyName} +

Company

@@ -753,7 +816,9 @@ export function OnboardingWizard() {
-

{agentName}

+

+ {agentName} +

{getUIAdapter(adapterType).label}

@@ -763,7 +828,9 @@ export function OnboardingWizard() {
-

{taskTitle}

+

+ {taskTitle} +

Task

@@ -812,7 +879,9 @@ export function OnboardingWizard() { {step === 2 && (
{result.checks.map((check, idx) => ( -
+
{check.level} · {check.message} - {check.detail && ({check.detail})} - {check.hint && Hint: {check.hint}} + {check.detail && ( + + ({check.detail}) + + )} + {check.hint && ( + + Hint: {check.hint} + + )}
))}