ui: add Cursor adapter option and mark non-functional adapters as Coming Soon

Only Claude Code and Codex are selectable. OpenClaw, Cursor, Shell Command,
and HTTP Webhook are shown as disabled with "Coming soon" across all adapter
selection UIs: onboarding wizard, agent config form, and invite landing page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dotta
2026-03-03 11:29:34 -06:00
parent 5f37b70be5
commit 2ac47ff4cb
5 changed files with 59 additions and 17 deletions

View File

@@ -801,6 +801,18 @@ function AdapterEnvironmentResult({ result }: { result: AdapterEnvironmentTestRe
/* ---- Internal sub-components ---- */ /* ---- Internal sub-components ---- */
const ENABLED_ADAPTER_TYPES = new Set(["claude_local", "codex_local"]);
/** Display list includes all real adapter types plus UI-only coming-soon entries. */
const ADAPTER_DISPLAY_LIST: { value: string; label: string; comingSoon: boolean }[] = [
...AGENT_ADAPTER_TYPES.map((t) => ({
value: t,
label: adapterLabels[t] ?? t,
comingSoon: !ENABLED_ADAPTER_TYPES.has(t),
})),
{ value: "cursor", label: "Cursor", comingSoon: true },
];
function AdapterTypeDropdown({ function AdapterTypeDropdown({
value, value,
onChange, onChange,
@@ -817,16 +829,25 @@ function AdapterTypeDropdown({
</button> </button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-[var(--radix-popover-trigger-width)] p-1" align="start"> <PopoverContent className="w-[var(--radix-popover-trigger-width)] p-1" align="start">
{AGENT_ADAPTER_TYPES.map((t) => ( {ADAPTER_DISPLAY_LIST.map((item) => (
<button <button
key={t} key={item.value}
disabled={item.comingSoon}
className={cn( className={cn(
"flex items-center gap-2 w-full px-2 py-1.5 text-sm rounded hover:bg-accent/50", "flex items-center justify-between w-full px-2 py-1.5 text-sm rounded",
t === value && "bg-accent", item.comingSoon
? "opacity-40 cursor-not-allowed"
: "hover:bg-accent/50",
item.value === value && !item.comingSoon && "bg-accent",
)} )}
onClick={() => onChange(t)} onClick={() => {
if (!item.comingSoon) onChange(item.value);
}}
> >
{adapterLabels[t] ?? t} <span>{item.label}</span>
{item.comingSoon && (
<span className="text-[10px] text-muted-foreground">Coming soon</span>
)}
</button> </button>
))} ))}
</PopoverContent> </PopoverContent>

View File

@@ -18,6 +18,7 @@ const adapterLabels: Record<string, string> = {
claude_local: "Claude (local)", claude_local: "Claude (local)",
codex_local: "Codex (local)", codex_local: "Codex (local)",
openclaw: "OpenClaw", openclaw: "OpenClaw",
cursor: "Cursor",
process: "Process", process: "Process",
http: "HTTP", http: "HTTP",
}; };

View File

@@ -31,6 +31,7 @@ import {
Terminal, Terminal,
Globe, Globe,
Sparkles, Sparkles,
MousePointer2,
Check, Check,
Loader2, Loader2,
FolderOpen, FolderOpen,
@@ -383,34 +384,49 @@ export function OnboardingWizard() {
label: "OpenClaw", label: "OpenClaw",
icon: Bot, icon: Bot,
desc: "Notify OpenClaw webhook", desc: "Notify OpenClaw webhook",
comingSoon: true,
},
{
value: "cursor" as const,
label: "Cursor",
icon: MousePointer2,
desc: "Cursor AI agent",
comingSoon: true,
}, },
{ {
value: "process" as const, value: "process" as const,
label: "Shell Command", label: "Shell Command",
icon: Terminal, icon: Terminal,
desc: "Run a process", desc: "Run a process",
comingSoon: true,
}, },
{ {
value: "http" as const, value: "http" as const,
label: "HTTP Webhook", label: "HTTP Webhook",
icon: Globe, icon: Globe,
desc: "Call an endpoint", desc: "Call an endpoint",
comingSoon: true,
}, },
] as const).map((opt) => ( ]).map((opt) => (
<button <button
key={opt.value} key={opt.value}
disabled={!!opt.comingSoon}
className={cn( className={cn(
"flex flex-col items-center gap-1.5 rounded-md border p-3 text-xs transition-colors", "flex flex-col items-center gap-1.5 rounded-md border p-3 text-xs transition-colors relative",
adapterType === opt.value opt.comingSoon
? "border-foreground bg-accent" ? "border-border opacity-40 cursor-not-allowed"
: "border-border hover:bg-accent/50" : adapterType === opt.value
? "border-foreground bg-accent"
: "border-border hover:bg-accent/50"
)} )}
onClick={() => setAdapterType(opt.value)} onClick={() => {
if (!opt.comingSoon) setAdapterType(opt.value as AdapterType);
}}
> >
<opt.icon className="h-4 w-4" /> <opt.icon className="h-4 w-4" />
<span className="font-medium">{opt.label}</span> <span className="font-medium">{opt.label}</span>
<span className="text-muted-foreground text-[10px]"> <span className="text-muted-foreground text-[10px]">
{opt.desc} {opt.comingSoon ? "Coming soon" : opt.desc}
</span> </span>
</button> </button>
))} ))}

View File

@@ -53,6 +53,7 @@ export const adapterLabels: Record<string, string> = {
claude_local: "Claude (local)", claude_local: "Claude (local)",
codex_local: "Codex (local)", codex_local: "Codex (local)",
openclaw: "OpenClaw", openclaw: "OpenClaw",
cursor: "Cursor",
process: "Process", process: "Process",
http: "HTTP", http: "HTTP",
}; };

View File

@@ -15,14 +15,17 @@ const joinAdapterOptions: AgentAdapterType[] = [
...AGENT_ADAPTER_TYPES.filter((type): type is Exclude<AgentAdapterType, "openclaw"> => type !== "openclaw"), ...AGENT_ADAPTER_TYPES.filter((type): type is Exclude<AgentAdapterType, "openclaw"> => type !== "openclaw"),
]; ];
const adapterLabels: Record<AgentAdapterType, string> = { const adapterLabels: Record<string, string> = {
claude_local: "Claude (local)", claude_local: "Claude (local)",
codex_local: "Codex (local)", codex_local: "Codex (local)",
openclaw: "OpenClaw", openclaw: "OpenClaw",
cursor: "Cursor",
process: "Process", process: "Process",
http: "HTTP", http: "HTTP",
}; };
const ENABLED_INVITE_ADAPTERS = new Set(["claude_local", "codex_local"]);
function dateTime(value: string) { function dateTime(value: string) {
return new Date(value).toLocaleString(); return new Date(value).toLocaleString();
} }
@@ -42,7 +45,7 @@ export function InviteLandingPage() {
const token = (params.token ?? "").trim(); const token = (params.token ?? "").trim();
const [joinType, setJoinType] = useState<JoinType>("human"); const [joinType, setJoinType] = useState<JoinType>("human");
const [agentName, setAgentName] = useState(""); const [agentName, setAgentName] = useState("");
const [adapterType, setAdapterType] = useState<AgentAdapterType>("openclaw"); const [adapterType, setAdapterType] = useState<AgentAdapterType>("claude_local");
const [capabilities, setCapabilities] = useState(""); const [capabilities, setCapabilities] = useState("");
const [result, setResult] = useState<{ kind: "bootstrap" | "join"; payload: unknown } | null>(null); const [result, setResult] = useState<{ kind: "bootstrap" | "join"; payload: unknown } | null>(null);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
@@ -255,8 +258,8 @@ export function InviteLandingPage() {
onChange={(event) => setAdapterType(event.target.value as AgentAdapterType)} onChange={(event) => setAdapterType(event.target.value as AgentAdapterType)}
> >
{joinAdapterOptions.map((type) => ( {joinAdapterOptions.map((type) => (
<option key={type} value={type}> <option key={type} value={type} disabled={!ENABLED_INVITE_ADAPTERS.has(type)}>
{adapterLabels[type]} {adapterLabels[type]}{!ENABLED_INVITE_ADAPTERS.has(type) ? " (Coming soon)" : ""}
</option> </option>
))} ))}
</select> </select>