feat(ui): add target support to API profile dashboard

- extend API client types with claude|droid target fields

- add default target selector in API profile create dialog

- show target badges in API profile list
This commit is contained in:
Tam Nhu Tran
2026-02-25 15:53:02 +07:00
parent 9a63f9bd36
commit ca78e63205
3 changed files with 51 additions and 1 deletions
@@ -12,6 +12,13 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { ScrollArea } from '@/components/ui/scroll-area';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import {
Dialog,
DialogContent,
@@ -41,6 +48,7 @@ import {
getNewestModelsPerProvider,
} from '@/lib/openrouter-utils';
import type { CategorizedModel } from '@/lib/openrouter-types';
import type { CliTarget } from '@/lib/api-client';
const schema = z.object({
name: z
@@ -53,6 +61,7 @@ const schema = z.object({
opusModel: z.string().optional(),
sonnetModel: z.string().optional(),
haikuModel: z.string().optional(),
target: z.enum(['claude', 'droid']),
});
type FormData = z.infer<typeof schema>;
@@ -78,6 +87,7 @@ const EMPTY_FORM_VALUES: FormData = {
opusModel: '',
sonnetModel: '',
haikuModel: '',
target: 'claude',
};
const RECOMMENDED_PRESETS = getPresetsByCategory('recommended');
@@ -117,6 +127,7 @@ export function ProfileCreateDialog({
});
const baseUrlValue = useWatch({ control, name: 'baseUrl' });
const targetValue = useWatch({ control, name: 'target' });
const applyPresetToForm = useCallback(
(preset: ProviderPreset | null) => {
if (!preset) {
@@ -445,6 +456,30 @@ export function ProfileCreateDialog({
)
)}
</div>
<div className="space-y-1.5">
<Label htmlFor="target">Default Target CLI</Label>
<Select
value={targetValue}
onValueChange={(value) => setValue('target', value as CliTarget)}
>
<SelectTrigger id="target">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="claude">Claude Code (default)</SelectItem>
<SelectItem value="droid">Factory Droid</SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">
Run with{' '}
<code className="bg-muted px-1 rounded text-[10px]">
{targetValue === 'droid' ? 'ccsd' : 'ccs'}
</code>{' '}
by default. You can still override each run with{' '}
<code className="bg-muted px-1 rounded text-[10px]">--target</code>.
</p>
</div>
</TabsContent>
<TabsContent value="models" className="p-6 mt-0 space-y-4">
+8
View File
@@ -94,10 +94,13 @@ async function request<T>(url: string, options?: RequestInit): Promise<T> {
}
// Types
export type CliTarget = 'claude' | 'droid';
export interface Profile {
name: string;
settingsPath: string;
configured: boolean;
target?: CliTarget;
}
export interface CreateProfile {
@@ -108,6 +111,7 @@ export interface CreateProfile {
opusModel?: string;
sonnetModel?: string;
haikuModel?: string;
target?: CliTarget;
}
export interface UpdateProfile {
@@ -117,6 +121,7 @@ export interface UpdateProfile {
opusModel?: string;
sonnetModel?: string;
haikuModel?: string;
target?: CliTarget;
}
export interface Variant {
@@ -126,6 +131,7 @@ export interface Variant {
account?: string;
port?: number;
model?: string;
target?: CliTarget;
type?: 'composite';
default_tier?: 'opus' | 'sonnet' | 'haiku';
tiers?: {
@@ -140,6 +146,7 @@ export interface CreateVariant {
provider: CLIProxyProvider;
model?: string;
account?: string;
target?: CliTarget;
type?: 'composite';
default_tier?: 'opus' | 'sonnet' | 'haiku';
tiers?: {
@@ -153,6 +160,7 @@ export interface UpdateVariant {
provider?: CLIProxyProvider;
model?: string;
account?: string;
target?: CliTarget;
type?: 'composite';
default_tier?: 'opus' | 'sonnet' | 'haiku';
tiers?: {
+8 -1
View File
@@ -7,6 +7,7 @@ import { useState, useMemo } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Badge } from '@/components/ui/badge';
import {
Plus,
Search,
@@ -222,6 +223,7 @@ export function ApiPage() {
<ProfileEditor
key={selectedProfileData.name}
profileName={selectedProfileData.name}
profileTarget={selectedProfileData.target}
onDelete={() => setDeleteConfirm(selectedProfileData.name)}
onHasChangesUpdate={setEditorHasChanges}
/>
@@ -308,7 +310,12 @@ function ProfileListItem({
{/* Profile info */}
<div className="flex-1 min-w-0">
<div className="font-medium text-sm truncate">{profile.name}</div>
<div className="flex items-center gap-2 min-w-0">
<div className="font-medium text-sm truncate">{profile.name}</div>
<Badge variant="outline" className="text-[10px] h-4 px-1.5 uppercase">
{profile.target || 'claude'}
</Badge>
</div>
<div className="flex items-center gap-1.5 min-w-0">
<div className="text-xs text-muted-foreground truncate flex-1">
{profile.settingsPath}