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