Pass 3 - add commands, remove run, formatting, mobile view

This commit is contained in:
Owen
2026-08-18 14:59:10 -04:00
parent 8feca67431
commit 03085eb188
4 changed files with 27 additions and 51 deletions
@@ -38,7 +38,6 @@ type AiClientConfigCardProps = {
keyAuth: AiClientAuthInput; keyAuth: AiClientAuthInput;
description: string; description: string;
icon: LucideIcon; icon: LucideIcon;
stackBlocks?: boolean;
}; };
export function AiClientConfigCard({ export function AiClientConfigCard({
@@ -47,8 +46,7 @@ export function AiClientConfigCard({
endpoint, endpoint,
keyAuth, keyAuth,
description, description,
icon: Icon, icon: Icon
stackBlocks = true
}: AiClientConfigCardProps) { }: AiClientConfigCardProps) {
const t = useTranslations(); const t = useTranslations();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -94,7 +92,7 @@ export function AiClientConfigCard({
guide?.presets.find((p) => p.id === presetId) ?? guide?.presets[0]; guide?.presets.find((p) => p.id === presetId) ?? guide?.presets[0];
const manualContent = guide ? ( const manualContent = guide ? (
<div className="space-y-4"> <div className="min-w-0 space-y-4">
{guide.presets.length > 1 ? ( {guide.presets.length > 1 ? (
<Select <Select
value={presetId} value={presetId}
@@ -114,14 +112,7 @@ export function AiClientConfigCard({
</SelectContent> </SelectContent>
</Select> </Select>
) : null} ) : null}
<div <div className="grid min-w-0 gap-4">
className={cn(
"grid gap-4",
!stackBlocks &&
(preset?.blocks.length ?? 0) > 1 &&
"@lg:grid-cols-2"
)}
>
{preset?.blocks.map((block) => ( {preset?.blocks.map((block) => (
<AiConfigCodeBlock key={block.id} block={block} /> <AiConfigCodeBlock key={block.id} block={block} />
))} ))}
@@ -133,7 +124,7 @@ export function AiClientConfigCard({
<Collapsible <Collapsible
open={open} open={open}
onOpenChange={handleOpenChange} onOpenChange={handleOpenChange}
className="rounded-md border bg-card" className="min-w-0 rounded-md border bg-card"
> >
<CollapsibleTrigger className="flex w-full items-center gap-3 px-4 py-3 text-left cursor-pointer"> <CollapsibleTrigger className="flex w-full items-center gap-3 px-4 py-3 text-left cursor-pointer">
<Icon className="size-4 shrink-0 text-muted-foreground" /> <Icon className="size-4 shrink-0 text-muted-foreground" />
@@ -150,7 +141,7 @@ export function AiClientConfigCard({
)} )}
/> />
</CollapsibleTrigger> </CollapsibleTrigger>
<CollapsibleContent className="border-t px-4 py-4"> <CollapsibleContent className="min-w-0 border-t px-4 py-4">
{!guide && revealing ? ( {!guide && revealing ? (
<div className="flex items-center justify-center py-6 text-muted-foreground"> <div className="flex items-center justify-center py-6 text-muted-foreground">
<Loader2 className="size-5 animate-spin" /> <Loader2 className="size-5 animate-spin" />
@@ -179,27 +170,21 @@ export function AiClientConfigCard({
</TabsList> </TabsList>
<TabsContent <TabsContent
value="cli" value="cli"
className={cn( className="grid min-w-0 gap-4 mt-4"
"grid gap-4 mt-4",
!stackBlocks && "@lg:grid-cols-2"
)}
> >
<AiConfigCodeBlock <AiConfigCodeBlock
block={guide.cli.configure} block={guide.cli.configure}
/> />
<AiConfigCodeBlock block={guide.cli.run} />
{guide.cli.configureWithKey ? ( {guide.cli.configureWithKey ? (
<AiConfigCodeBlock <AiConfigCodeBlock
block={guide.cli.configureWithKey} block={guide.cli.configureWithKey}
/> />
) : null} ) : null}
{guide.cli.runWithKey ? (
<AiConfigCodeBlock
block={guide.cli.runWithKey}
/>
) : null}
</TabsContent> </TabsContent>
<TabsContent value="manual" className="mt-4"> <TabsContent
value="manual"
className="min-w-0 mt-4"
>
{manualContent} {manualContent}
</TabsContent> </TabsContent>
</Tabs> </Tabs>
@@ -21,10 +21,10 @@ type AiClientConfigSectionProps = {
endpoint: string; endpoint: string;
auth: AiClientAuthInput; auth: AiClientAuthInput;
/** /**
* "wide" lays the client cards out side by side and allows a card's * "wide" lays the client cards out side by side once there's room
* code blocks to sit side by side once there's room (e.g. the Keys * (e.g. the Keys page). "compact" always stacks them, which is what
* page). "compact" always stacks both, which is what fits the * fits the Resource Launcher's side panel. A card's own code blocks
* Resource Launcher's side panel. * always stack regardless of this setting.
*/ */
layout?: "wide" | "compact"; layout?: "wide" | "compact";
className?: string; className?: string;
@@ -64,10 +64,10 @@ export function AiClientConfigSection({
</SettingsSectionDescription> </SettingsSectionDescription>
</SettingsSectionHeader> </SettingsSectionHeader>
<SettingsSectionBody> <SettingsSectionBody>
<div className={cn("@container", className)}> <div className={cn("@container min-w-0", className)}>
<div <div
className={cn( className={cn(
"grid gap-3", "grid min-w-0 gap-3",
isWide && "@3xl:grid-cols-2" isWide && "@3xl:grid-cols-2"
)} )}
> >
@@ -80,7 +80,6 @@ export function AiClientConfigSection({
keyAuth={auth} keyAuth={auth}
description={descriptions[clientId]} description={descriptions[clientId]}
icon={CLIENT_ICONS[clientId]} icon={CLIENT_ICONS[clientId]}
stackBlocks={!isWide}
/> />
))} ))}
</div> </div>
@@ -2,19 +2,21 @@
import CopyTextBox from "@app/components/CopyTextBox"; import CopyTextBox from "@app/components/CopyTextBox";
import type { AiConfigBlock } from "@app/lib/aiClientConfig"; import type { AiConfigBlock } from "@app/lib/aiClientConfig";
import { cn } from "@app/lib/cn";
export function AiConfigCodeBlock({ block }: { block: AiConfigBlock }) { export function AiConfigCodeBlock({ block }: { block: AiConfigBlock }) {
return ( return (
<div className="space-y-1.5"> <div className="min-w-0 space-y-1.5">
<p className="font-mono text-xs text-muted-foreground"> <p className="font-mono text-xs text-muted-foreground">
{block.label} {block.label}
</p> </p>
<div <div
className={ className={cn(
"min-w-0",
block.kind === "steps" block.kind === "steps"
? "[&_pre]:text-sm" ? "[&_pre]:text-sm"
: "[&_pre]:text-xs [&_code]:font-mono" : "[&_pre]:text-xs [&_code]:font-mono"
} )}
> >
<CopyTextBox <CopyTextBox
text={block.displayText} text={block.displayText}
+6 -16
View File
@@ -34,8 +34,6 @@ export type AiConfigPreset = {
export type AiCliCommands = { export type AiCliCommands = {
configure: AiConfigBlock; configure: AiConfigBlock;
configureWithKey?: AiConfigBlock; configureWithKey?: AiConfigBlock;
run: AiConfigBlock;
runWithKey?: AiConfigBlock;
}; };
export type AiClientGuide = { export type AiClientGuide = {
@@ -59,34 +57,26 @@ function block(
return { id, label, kind, displayText: build(keyValue(auth)) }; return { id, label, kind, displayText: build(keyValue(auth)) };
} }
function buildCli(clientArg: "claude" | "codex", auth: AiClientAuth): AiCliCommands { function buildCli(
clientArg: "claude" | "codex" | "opencode",
auth: AiClientAuth
): AiCliCommands {
const configure: AiConfigBlock = { const configure: AiConfigBlock = {
id: `cli-configure-${clientArg}`, id: `cli-configure-${clientArg}`,
label: "Configure", label: "Configure",
displayText: `pangolin configure ${clientArg}` displayText: `pangolin configure ${clientArg}`
}; };
const run: AiConfigBlock = {
id: `cli-run-${clientArg}`,
label: "Run",
displayText: `pangolin run ${clientArg}`
};
if (auth.mode !== "keyed") { if (auth.mode !== "keyed") {
return { configure, run }; return { configure };
} }
return { return {
configure, configure,
run,
configureWithKey: { configureWithKey: {
id: `cli-configure-key-${clientArg}`, id: `cli-configure-key-${clientArg}`,
label: "Configure with an API key", label: "Configure with an API key",
displayText: `pangolin configure ${clientArg} ${auth.key}` displayText: `pangolin configure ${clientArg} ${auth.key}`
},
runWithKey: {
id: `cli-run-key-${clientArg}`,
label: "Run with an API key",
displayText: `pangolin run ${clientArg} ${auth.key}`
} }
}; };
} }
@@ -278,7 +268,7 @@ function buildOpencodeGuide(endpoint: string, auth: AiClientAuth): AiClientGuide
return { return {
id: "opencode", id: "opencode",
name: AI_CLIENT_NAMES.opencode, name: AI_CLIENT_NAMES.opencode,
cli: null, cli: buildCli("opencode", auth),
presets: [ presets: [
{ {
id: "default", id: "default",