Skip to content

Commit 8ff9eaf

Browse files
committed
refactor(web): unify home + chat onto one workspace shell (DRY)
The home page duplicated the chat/projects 'computer open' split with a parallel mechanism (.cheatcode-home-* padding/calc reserves + fixed-positioned composer & Computer panel), so the two views drifted (different column widths + panel sizes). - New WorkspaceRunLayout (components/workspace/) is the single source of truth for the split: .cc-agent-run-layout flex + .cc-agent-chat-pane content | RunPanelDivider | .cc-agent-computer-pane (flex 1 1 70%). Exports the layout/pane class helpers + RunPanelDivider. - projects-shell.tsx now renders WorkspaceRunLayout (dropped its local copies of the layout class + divider) — chat/projects DOM + classes unchanged (verified: 557px pane, transcript renders). - Home now renders the SAME shell via HomeWorkspace: greeting/composer in the chat-pane, an in-flow HomeComputerPane as the computer pane (no more fixed panel/ composer). Deleted the entire .cheatcode-home-computer-open CSS block + those classes. Result: the home computer-open layout is identical to the chat by construction (measured: content 557px, Computer pane 1299px @ left=614 — same as chat). Gate green (15/15).
1 parent 2456b6d commit 8ff9eaf

9 files changed

Lines changed: 400 additions & 342 deletions

File tree

apps/web/src/app/globals.css

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -566,38 +566,6 @@
566566
}
567567
}
568568

569-
/* Home "computer open" split — content collapses to a narrow left column and the
570-
Computer panel takes the wide right side (bud parity). Left UNLAYERED on purpose:
571-
inside @layer utilities these lose to the elements' own Tailwind utilities
572-
(px-6 / max-w / right-0); unlayered rules win. Reserve = viewport minus the
573-
sidebar rail minus the ~32rem content column, matching the panel's own width. */
574-
@media (min-width: 768px) {
575-
html.cheatcode-home-computer-open .cheatcode-home-main {
576-
padding-right: calc(100vw - var(--cheatcode-sidebar-offset, 4rem) - 32rem);
577-
transition: padding-right 200ms ease;
578-
}
579-
580-
html.cheatcode-home-computer-open .cheatcode-home-center {
581-
max-width: 30rem;
582-
transition: max-width 200ms ease;
583-
}
584-
585-
html.cheatcode-home-computer-open .cheatcode-home-composer-form {
586-
right: calc(100vw - var(--cheatcode-sidebar-offset, 4rem) - 32rem);
587-
padding-left: var(--cheatcode-sidebar-offset, 4rem);
588-
width: auto;
589-
transition:
590-
right 200ms ease,
591-
width 200ms ease,
592-
padding-left 200ms ease;
593-
}
594-
595-
html.cheatcode-home-computer-open .cheatcode-home-composer-shell {
596-
max-width: 30rem;
597-
transition: max-width 200ms ease;
598-
}
599-
}
600-
601569
/* Custom styles for Markdown chat content */
602570
.prose.chat-markdown {
603571
/* Ensure reasonable spacing */

apps/web/src/app/page.tsx

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,5 @@
1-
import { Suspense } from "react";
2-
import { HomeComposerFromSearchParams } from "@/components/home/home-composer-from-search-params";
3-
import { HomeGreeting } from "@/components/home/home-greeting";
4-
import { HomeHeadline } from "@/components/home/home-headline";
5-
import { HomeSessionChrome } from "@/components/home/home-session-chrome";
6-
import { HomeSidebarOffset } from "@/components/home/home-sidebar-offset";
7-
import { AppSidebar } from "@/components/shell/app-sidebar";
8-
import { CheatcodeMark } from "@/components/ui/cheatcode-mark";
1+
import { HomeWorkspace } from "@/components/home/home-workspace";
92

103
export default function HomePage() {
11-
return (
12-
<div className="relative min-h-screen bg-white text-[#1b1b1b] transition-[padding] duration-200 md:pl-[var(--cheatcode-sidebar-offset,16rem)]">
13-
<HomeSidebarOffset />
14-
<Suspense fallback={null}>
15-
<AppSidebar variant="full" />
16-
</Suspense>
17-
<HomeSessionChrome />
18-
<main className="cheatcode-home-main relative min-h-screen px-6 pt-[80px] pb-44">
19-
<section className="cheatcode-home-center mx-auto flex w-full max-w-[740px] flex-col items-center px-4 pb-10">
20-
<CheatcodeMark aria-hidden="true" className="h-[42px] w-[42px] text-[#f8af2c]" />
21-
<Suspense fallback={<div className="mb-2 h-4 w-40 rounded-full bg-[#f7f7f7]" />}>
22-
<HomeGreeting />
23-
</Suspense>
24-
<HomeHeadline />
25-
<HomeComposerFromSearchParams />
26-
</section>
27-
</main>
28-
</div>
29-
);
4+
return <HomeWorkspace />;
305
}

apps/web/src/components/home/home-composer.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -386,15 +386,12 @@ export function HomeComposer({
386386
<div className="mt-6 w-full">
387387
<HomeQuickActions activeIntentId={intentId} onIntentClick={selectQuickIntent} />
388388
{skillCreatorMode ? <SkillCreatorSuggestions onPick={(text) => setValue(text)} /> : null}
389-
<form
390-
className="cheatcode-home-composer-form mt-8 w-full md:pointer-events-none md:fixed md:right-0 md:bottom-8 md:left-0 md:z-20 md:flex md:justify-center md:pl-[var(--cheatcode-sidebar-offset,16rem)]"
391-
onSubmit={submit}
392-
>
393-
<div className="relative z-10 mx-auto flex w-full max-w-[708px] flex-col gap-0 md:pointer-events-auto">
389+
<form className="mt-8 w-full" onSubmit={submit}>
390+
<div className="relative z-10 mx-auto flex w-full max-w-[708px] flex-col gap-0">
394391
<FreePlanComposerBanner getToken={getToken} />
395392
<div
396393
className={cn(
397-
"bud-composer-shell cheatcode-home-composer-shell relative w-full rounded-[24px] p-px",
394+
"bud-composer-shell relative w-full rounded-[24px] p-px",
398395
"transition-colors focus-within:border-[#e4e4e4]",
399396
)}
400397
>
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import { BudTooltip } from "@/components/ui/bud-tooltip";
5+
import { CheatcodeMark } from "@/components/ui/cheatcode-mark";
6+
import { Code, FileSpreadsheet, Monitor, MoreVertical, Plus } from "@/components/ui/icons";
7+
import { cn } from "@/lib/ui/cn";
8+
9+
type HomeComputerTab = "browser" | "files";
10+
11+
/**
12+
* The home page's demo "Computer" pane. Renders as the `.cc-agent-computer-pane`
13+
* flex child of {@link WorkspaceRunLayout} (not `position: fixed`), mirroring the
14+
* chat's {@link PreviewSidePanel} structure. The home has no threadId/real sandbox,
15+
* so the body stays a demo "Booting computer" state. Also renders the floating
16+
* "Open computer" pill shown while the pane is collapsed.
17+
*/
18+
export function HomeComputerPane({
19+
computerOpen,
20+
onClose,
21+
onOpen,
22+
}: {
23+
computerOpen: boolean;
24+
onClose: () => void;
25+
onOpen: () => void;
26+
}) {
27+
const [activeTab, setActiveTab] = useState<HomeComputerTab>("files");
28+
29+
function openComputer() {
30+
setActiveTab("files");
31+
onOpen();
32+
}
33+
34+
return (
35+
<>
36+
<BudTooltip
37+
className={cn(
38+
"fixed top-3.5 right-3.5 z-40 hidden transition-[opacity,transform] duration-200 ease-[cubic-bezier(0.4,0,0.2,1)] motion-reduce:transition-none md:flex",
39+
computerOpen
40+
? "pointer-events-none translate-y-0 scale-[0.98] opacity-0"
41+
: "translate-y-0 scale-100 opacity-100",
42+
)}
43+
label="Open computer"
44+
side="bottom"
45+
>
46+
<button
47+
aria-hidden={computerOpen}
48+
aria-label="Open computer"
49+
className="flex h-7 items-center gap-1.5 rounded-full bg-[#1b1b1b] py-1 pr-3 pl-2.5 font-medium text-[14px] text-white transition-[background-color,transform] duration-200 ease-[cubic-bezier(0.4,0,0.2,1)] hover:bg-black active:scale-[0.97] motion-reduce:transition-none"
50+
onClick={openComputer}
51+
tabIndex={computerOpen ? -1 : undefined}
52+
type="button"
53+
>
54+
<Monitor aria-hidden="true" className="h-4 w-4" />
55+
<span>Computer</span>
56+
</button>
57+
</BudTooltip>
58+
<aside
59+
aria-hidden={!computerOpen}
60+
aria-label="Computer"
61+
className={cn(
62+
"cc-agent-computer-pane hidden min-h-0 min-w-0 overflow-hidden bg-white transition-[opacity,transform,filter] duration-200 ease-[cubic-bezier(0.4,0,0.2,1)] motion-reduce:transform-none motion-reduce:transition-none md:flex",
63+
computerOpen
64+
? "translate-x-0 opacity-100 blur-0"
65+
: "pointer-events-none translate-x-3 opacity-0 blur-[1px]",
66+
)}
67+
inert={computerOpen ? undefined : true}
68+
>
69+
<div className="flex h-full max-h-full w-full min-w-0 flex-col gap-2 overflow-hidden bg-white">
70+
<HomeComputerTabs activeTab={activeTab} onClose={onClose} onTabChange={setActiveTab} />
71+
<div className="flex min-h-0 w-full flex-1 flex-col overflow-hidden rounded-[24px] border border-[#f1f1f1] bg-white shadow-[0_0_1px_rgba(0,0,0,0.08)]">
72+
<div className="flex min-h-0 flex-1 items-center justify-center px-8">
73+
{activeTab === "files" ? <BootingComputerState /> : <BrowserComputerState />}
74+
</div>
75+
<ComputerConsoleStrip />
76+
</div>
77+
</div>
78+
</aside>
79+
</>
80+
);
81+
}
82+
83+
function HomeComputerTabs({
84+
activeTab,
85+
onClose,
86+
onTabChange,
87+
}: {
88+
activeTab: HomeComputerTab;
89+
onClose: () => void;
90+
onTabChange: (tab: HomeComputerTab) => void;
91+
}) {
92+
return (
93+
<div className="hidden h-12 w-full shrink-0 items-center justify-between overflow-visible px-[3px] md:flex">
94+
<div
95+
aria-label="Computer views"
96+
className="inline-flex items-center gap-1 rounded-full bg-[#f7f7f7] p-0.5 shadow-[0_0_1px_rgba(0,0,0,0.08)]"
97+
role="tablist"
98+
>
99+
{(["files", "browser"] as const).map((tab) => (
100+
<button
101+
aria-selected={activeTab === tab}
102+
className={cn(
103+
"flex h-7 items-center justify-center whitespace-nowrap rounded-full px-3 font-medium text-[14px] transition-colors",
104+
activeTab === tab
105+
? "bg-white text-[#1b1b1b] shadow-[0_1px_5px_rgba(0,0,0,0.08)]"
106+
: "text-[#707070] hover:text-[#1b1b1b]",
107+
)}
108+
key={tab}
109+
onClick={() => onTabChange(tab)}
110+
role="tab"
111+
type="button"
112+
>
113+
{tab === "files" ? "Files" : "Browser"}
114+
</button>
115+
))}
116+
</div>
117+
<div className="flex shrink-0 items-center gap-1 pr-1">
118+
<button
119+
aria-label="Computer actions"
120+
className="flex h-7 w-7 items-center justify-center rounded-full text-[#5f5f5f] transition-colors hover:bg-[#f7f7f7] hover:text-[#1b1b1b]"
121+
type="button"
122+
>
123+
<MoreVertical aria-hidden="true" className="h-4 w-4" />
124+
</button>
125+
<BudTooltip label="Close computer" side="bottom">
126+
<button
127+
aria-label="Close computer"
128+
className="inline-flex h-7 items-center gap-1.5 rounded-full bg-[#1b1b1b] py-1 pr-3 pl-2.5 font-medium text-[14px] text-white transition-[background-color,transform] duration-200 ease-[cubic-bezier(0.4,0,0.2,1)] hover:bg-black active:scale-[0.97] motion-reduce:transition-none"
129+
onClick={onClose}
130+
type="button"
131+
>
132+
<Monitor aria-hidden="true" className="h-4 w-4" />
133+
<span>Computer</span>
134+
</button>
135+
</BudTooltip>
136+
</div>
137+
</div>
138+
);
139+
}
140+
141+
function BootingComputerState() {
142+
return (
143+
<div className="text-center">
144+
<CheatcodeMark aria-hidden="true" className="mx-auto h-12 w-12 text-[#f8af2c]" />
145+
<p className="mt-5 font-medium text-[#707070] text-[15px]">Booting computer</p>
146+
</div>
147+
);
148+
}
149+
150+
function BrowserComputerState() {
151+
return (
152+
<div className="w-full max-w-md rounded-[24px] border border-[#f1f1f1] bg-[#fafafa] p-5 text-center">
153+
<Monitor aria-hidden="true" className="mx-auto h-8 w-8 text-[#707070]" />
154+
<p className="mt-4 font-semibold text-[18px]">Browser ready</p>
155+
<p className="mt-2 text-[#707070] text-[14px] leading-6">
156+
Start a task and Cheatcode will open the live browser here.
157+
</p>
158+
</div>
159+
);
160+
}
161+
162+
function ComputerConsoleStrip() {
163+
return (
164+
<div className="flex h-9 shrink-0 items-center gap-3 border-[#f1f1f1] border-t px-3 text-[#707070] text-[14px]">
165+
<button
166+
aria-label="Expand console"
167+
className="flex h-7 w-7 items-center justify-center rounded-full transition-colors hover:bg-[#f7f7f7] hover:text-[#1b1b1b]"
168+
type="button"
169+
>
170+
<span aria-hidden="true" className="text-[16px] leading-none">
171+
^
172+
</span>
173+
</button>
174+
<div className="flex h-7 items-center gap-2 rounded-full px-2 font-medium text-[#1b1b1b]">
175+
<Code aria-hidden="true" className="h-3.5 w-3.5" />
176+
Console
177+
</div>
178+
<button
179+
aria-label="New terminal"
180+
className="flex h-7 w-7 items-center justify-center rounded-full transition-colors hover:bg-[#f7f7f7] hover:text-[#1b1b1b]"
181+
type="button"
182+
>
183+
<Plus aria-hidden="true" className="h-4 w-4" />
184+
</button>
185+
<div className="ml-auto hidden items-center gap-1.5 text-[#a0a0a0] text-[12px] lg:flex">
186+
<FileSpreadsheet aria-hidden="true" className="h-3.5 w-3.5" />
187+
No files yet
188+
</div>
189+
</div>
190+
);
191+
}

0 commit comments

Comments
 (0)