Skip to content

Commit 432ec3f

Browse files
committed
Split auth screens into a guided layout
1 parent 1377b4e commit 432ec3f

4 files changed

Lines changed: 109 additions & 12 deletions

File tree

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import type { ReactNode } from "react";
2+
3+
import { Wordmark } from "@executor-js/react/components/wordmark";
4+
5+
// Split auth layout for the chromeless pages (setup, login, join): a promo
6+
// panel on the left, the form on the right. The panel follows design.md's
7+
// registry-minimal rules — graph-paper texture, mono eyebrow + index numerals,
8+
// grayscale only — so the first screen a person ever sees speaks the same
9+
// language as the app behind it. Collapses to form-only below lg.
10+
const PANEL_POINTS: ReadonlyArray<{ index: string; title: string; body: string }> = [
11+
{
12+
index: "01",
13+
title: "Connect",
14+
body: "OpenAPI, GraphQL, and MCP sources become tools your agent can call.",
15+
},
16+
{
17+
index: "02",
18+
title: "Control",
19+
body: "Policies decide which tools run, which ask first, and which are blocked.",
20+
},
21+
{
22+
index: "03",
23+
title: "Audit",
24+
body: "Every invocation is recorded, with approvals where they matter.",
25+
},
26+
];
27+
28+
export function AuthLayout(props: { readonly children: ReactNode }) {
29+
return (
30+
<div className="grid min-h-screen bg-background lg:grid-cols-[1.1fr_1fr]">
31+
<aside className="relative hidden flex-col justify-between overflow-hidden border-r border-border bg-sidebar p-12 lg:flex">
32+
{/* Graph-paper texture, faded toward the bottom (design.md signature). */}
33+
<div
34+
aria-hidden
35+
className="pointer-events-none absolute inset-0 text-foreground opacity-[0.05]"
36+
style={{
37+
backgroundImage:
38+
"linear-gradient(currentColor 1px, transparent 1px), linear-gradient(90deg, currentColor 1px, transparent 1px)",
39+
backgroundSize: "32px 32px",
40+
maskImage: "linear-gradient(to bottom, black 30%, transparent 85%)",
41+
}}
42+
/>
43+
44+
<div className="relative">
45+
<Wordmark />
46+
</div>
47+
48+
<div className="relative max-w-md space-y-10">
49+
<div className="space-y-4">
50+
<p className="font-mono text-[11px] font-medium uppercase tracking-[0.18em] text-muted-foreground">
51+
The integration layer for AI agents
52+
</p>
53+
<h2 className="text-4xl font-semibold tracking-[-0.04em] text-foreground">
54+
Every tool your agent needs, behind one endpoint.
55+
</h2>
56+
<p className="text-sm leading-6 text-muted-foreground">
57+
Connect your APIs once. Any MCP-compatible agent gets the whole catalog, governed by
58+
your policies.
59+
</p>
60+
</div>
61+
62+
<ul className="space-y-5">
63+
{PANEL_POINTS.map((point) => (
64+
<li key={point.index} className="flex gap-4">
65+
<span className="font-mono text-[11px] font-medium tracking-[0.08em] text-muted-foreground/70 pt-0.5">
66+
{point.index}
67+
</span>
68+
<div>
69+
<p className="text-sm font-medium text-foreground">{point.title}</p>
70+
<p className="text-sm leading-6 text-muted-foreground">{point.body}</p>
71+
</div>
72+
</li>
73+
))}
74+
</ul>
75+
</div>
76+
77+
<p className="relative font-mono text-[11px] tracking-[0.08em] text-muted-foreground">
78+
self-hosted
79+
</p>
80+
</aside>
81+
82+
<main className="flex flex-col items-center justify-center gap-6 p-6">
83+
<div className="lg:hidden">
84+
<Wordmark />
85+
</div>
86+
{props.children}
87+
</main>
88+
</div>
89+
);
90+
}

apps/host-selfhost/web/login.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Input } from "@executor-js/react/components/input";
55
import { Label } from "@executor-js/react/components/label";
66

77
import { authClient } from "./auth-client";
8+
import { AuthLayout } from "./auth-layout";
89
import { mcpAuthorizeResumeTarget, safeReturnTo } from "../src/auth/return-to";
910

1011
// Self-host login: email + password sign-in via Better Auth. On success we
@@ -54,12 +55,16 @@ export const LoginPage = () => {
5455
};
5556

5657
return (
57-
<div className="flex min-h-screen items-center justify-center bg-background p-6">
58+
<AuthLayout>
5859
<div className="w-full max-w-sm space-y-4 rounded-xl border border-border bg-card p-6 shadow-sm">
59-
<div className="space-y-1 text-center">
60-
<h1 className="font-mono text-2xl tracking-tight text-foreground">Executor</h1>
60+
<div className="space-y-1">
61+
<h1 className="text-xl font-semibold tracking-tight text-foreground">
62+
{mode === "signin" ? "Sign in" : "Join this instance"}
63+
</h1>
6164
<p className="text-sm text-muted-foreground">
62-
{mode === "signin" ? "Sign in to your instance" : "Join with your invite code"}
65+
{mode === "signin"
66+
? "Welcome back. Use your instance account."
67+
: "Enter the invite code you were given."}
6368
</p>
6469
</div>
6570

@@ -127,6 +132,6 @@ export const LoginPage = () => {
127132
</Button>
128133
</div>
129134
</div>
130-
</div>
135+
</AuthLayout>
131136
);
132137
};

apps/host-selfhost/web/setup.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Input } from "@executor-js/react/components/input";
55
import { Label } from "@executor-js/react/components/label";
66

77
import { authClient } from "./auth-client";
8+
import { AuthLayout } from "./auth-layout";
89

910
// First-run setup. A fresh instance has no users, so the first visitor creates
1011
// the admin account here. The server admits the first signup into the empty org
@@ -32,15 +33,15 @@ export const SetupPage = () => {
3233
};
3334

3435
return (
35-
<div className="flex min-h-screen items-center justify-center bg-background p-6">
36+
<AuthLayout>
3637
<form
3738
onSubmit={submit}
3839
className="w-full max-w-sm space-y-4 rounded-xl border border-border bg-card p-6 shadow-sm"
3940
>
40-
<div className="space-y-1 text-center">
41-
<h1 className="font-mono text-2xl tracking-tight text-foreground">Set up Executor</h1>
41+
<div className="space-y-1">
42+
<h1 className="text-xl font-semibold tracking-tight text-foreground">Set up Executor</h1>
4243
<p className="text-sm text-muted-foreground">
43-
Create the admin account for this instance.
44+
Create the admin account for this instance. You can invite your team once you're in.
4445
</p>
4546
</div>
4647

@@ -87,6 +88,6 @@ export const SetupPage = () => {
8788
{busy ? "Creating…" : "Create admin account"}
8889
</Button>
8990
</form>
90-
</div>
91+
</AuthLayout>
9192
);
9293
};

packages/react/src/components/mcp-install-card.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ export function McpInstallCard(props: { className?: string }) {
170170
? isDev
171171
? "Uses the repo-local dev CLI from any agent working directory."
172172
: "Requires the executor CLI on your PATH."
173-
: "Connect to executor as a remote MCP server over streamable HTTP.";
173+
: "Paste this into Claude Code, Cursor, or any MCP client, and your agent gets every tool you connect here.";
174174

175175
const advancedControls = (
176176
<Collapsible open={advancedOpen} onOpenChange={setAdvancedOpen}>
@@ -224,9 +224,10 @@ export function McpInstallCard(props: { className?: string }) {
224224
key={key}
225225
title={label}
226226
aria-label={label}
227+
role="img"
227228
style={{ zIndex: SUPPORTED_AGENTS.length - index }}
228229
className={cn(
229-
"flex h-6 items-center justify-center rounded-md border border-border/60 bg-background px-1.5 transition-[margin] duration-200 ease-[cubic-bezier(0.23,1,0.32,1)]",
230+
"flex h-6 items-center justify-center rounded-md border border-border/60 bg-background px-1.5 text-muted-foreground transition-[margin] duration-200 ease-[cubic-bezier(0.23,1,0.32,1)]",
230231
index > 0 && "-ml-2 group-hover/agents:ml-1",
231232
)}
232233
>

0 commit comments

Comments
 (0)