import { useEffect, useState } from "react"; import { getConnectors, getSessionConnections } from "../api"; import type { Attachment } from "../types"; import { ConnectorIcon } from "../connectors/ConnectorIcon"; import { indexConnectors, visualFor, type ConnectorMap } from "../connectors/visuals"; import { useRoots } from "../useRoots"; import { AddFolderForm } from "./AddFolderForm"; // Empty-state for a fresh Cowork session (§27): a greeting, exactly three concrete template // tasks, and the composer — nothing else. Each task carries its own setup: no icon tiles (the // title is the row), connector dots on the sub-line (brand color = connected and enabled for // this session, grayscale = not — §23's vocabulary), and sub-line copy that is always the task's // OUTCOME, never connection state. Sources ready → "Start →" on hover, click prefills the // composer. Not ready → "Configure ›" always visible (for a gated row the setup action IS the // row's meaning), opening the §23 Session settings drawer — no second setup surface here. const FOLDER_PROMPT = "Analyze the files in this folder and summarize what matters."; const HUBSPOT_PROMPT = "Create a report on my recent HubSpot leads: sources, stages, and who needs follow-up."; const GH_SLACK_PROMPT = "Set up a weekly progress report: summarize activity in my GitHub repos and post it to Slack every Friday morning."; export function SessionIntro({ sessionId, onOpenSessionSettings, onPrefill, }: { sessionId: string; // Opens the §23 Session settings drawer (sources section) — the gated rows' Configure target. onOpenSessionSettings: () => void; onPrefill: (text: string, attachments?: Attachment[]) => void; }) { const { roots, busy, error, addRoot } = useRoots(sessionId); const [live, setLive] = useState>(new Set()); const [byName, setByName] = useState({}); const [addingFolder, setAddingFolder] = useState(false); useEffect(() => { // Live = what this session can touch right now (connected AND not muted here) — the same // truth the §23 glance renders, so the dots here can never disagree with the row above. getSessionConnections(sessionId) .then((c) => setLive(new Set(c.connected.filter((x) => x.enabled).map((x) => x.connector)))) .catch(() => {}); getConnectors() .then((list) => setByName(indexConnectors(list))) .catch(() => {}); }, [sessionId]); const shared = roots.filter((r) => !r.primary); const hubspotReady = live.has("hubspot"); const ghSlackReady = live.has("github") && live.has("slack"); const dot = (name: string, on: boolean) => ( ); const pickFolder = () => { // A shared folder already exists → straight to the prompt; otherwise share one first. if (shared.length > 0) onPrefill(FOLDER_PROMPT); else setAddingFolder((v) => !v); }; return (

What should we produce?

Pick a task to start — I'll do the work and save the result. Or just type what you need below.

{addingFolder && (
{ const ok = await addRoot(path, writable); if (ok !== false) onPrefill(FOLDER_PROMPT); return ok; }} onDismiss={() => setAddingFolder(false)} /> {error &&
{error}
}
)}
); }