import { useEffect, useRef } from 'react'; import type { ReactNode } from 'react'; import { AmMark, PlusGlyph, PulseGlyph, KeyGlyph, GridGlyph, BellGlyph, LockGlyph } from './icons'; import WelcomeDiagram from './WelcomeDiagram'; // First-run welcome: one scrollable card covering the things a newcomer can't // guess. Shown once per Space (server-flagged) and reopenable from Settings. const ITEMS: { icon: ReactNode; title: string; body: ReactNode }[] = [ { icon: , title: 'Your agents are always running', body: <>They live in the cloud, not on your laptop. Close the tab, shut the lid, switch devices, and they keep working. Reconnect anytime to pick up where they are., }, { icon: , title: 'Use any agent or harness in one click', body: <>Claude Code, Codex, Gemini, opencode, Hermes, OpenClaw and more, side by side. Hit +, pick one, and it walks you through login on first launch., }, { icon: , title: 'Manage keys and secrets in one place', body: <>Add API keys once as Space secrets and label them in Settings. Every agent then learns what's available through a generated environment skill., }, { icon: , title: 'Run everything from a single window', body: <>Overview shows what each agent did since your last message, whose turn it is, and lets you reply inline, so you steer a whole fleet without juggling tabs., }, { icon: , title: 'Get pinged the moment they need you', body: <>Say "notify me" in a prompt and the agent messages your phone or desktop when it's done or stuck, so you don't have to watch it., }, { icon: , title: 'Persistent and private', body: <>Your work, logins and history persist on your own storage bucket, and the whole Space stays private to you. No shared servers, no one else's eyes., }, ]; export default function Welcome({ onClose }: { onClose: () => void }) { const cardRef = useRef(null); const closeRef = useRef(null); // Accessible dialog: focus into it on open, close on Escape, and trap Tab so // focus can't wander to the (invisible) page behind the modal. useEffect(() => { closeRef.current?.focus(); const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') { e.preventDefault(); onClose(); return; } if (e.key !== 'Tab' || !cardRef.current) return; const f = cardRef.current.querySelectorAll('button, [href], input, [tabindex]:not([tabindex="-1"])'); if (!f.length) return; const first = f[0]; const last = f[f.length - 1]; if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } }; document.addEventListener('keydown', onKey, true); return () => document.removeEventListener('keydown', onKey, true); }, [onClose]); return (
e.stopPropagation()}>

Welcome to Agent Manager

Run a fleet of AI coding agents from one place. Always on, always yours.

{ITEMS.map((it) => (
{it.icon}
{it.title}
{it.body}
))}
reopen anytime from Settings
); }