mirror of
https://github.com/tiennm99/ccs.git
synced 2026-08-14 14:25:20 +00:00
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:
@@ -12,6 +12,13 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -41,6 +48,7 @@ import {
|
|||||||
getNewestModelsPerProvider,
|
getNewestModelsPerProvider,
|
||||||
} from '@/lib/openrouter-utils';
|
} from '@/lib/openrouter-utils';
|
||||||
import type { CategorizedModel } from '@/lib/openrouter-types';
|
import type { CategorizedModel } from '@/lib/openrouter-types';
|
||||||
|
import type { CliTarget } from '@/lib/api-client';
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
name: z
|
name: z
|
||||||
@@ -53,6 +61,7 @@ const schema = z.object({
|
|||||||
opusModel: z.string().optional(),
|
opusModel: z.string().optional(),
|
||||||
sonnetModel: z.string().optional(),
|
sonnetModel: z.string().optional(),
|
||||||
haikuModel: z.string().optional(),
|
haikuModel: z.string().optional(),
|
||||||
|
target: z.enum(['claude', 'droid']),
|
||||||
});
|
});
|
||||||
|
|
||||||
type FormData = z.infer<typeof schema>;
|
type FormData = z.infer<typeof schema>;
|
||||||
@@ -78,6 +87,7 @@ const EMPTY_FORM_VALUES: FormData = {
|
|||||||
opusModel: '',
|
opusModel: '',
|
||||||
sonnetModel: '',
|
sonnetModel: '',
|
||||||
haikuModel: '',
|
haikuModel: '',
|
||||||
|
target: 'claude',
|
||||||
};
|
};
|
||||||
|
|
||||||
const RECOMMENDED_PRESETS = getPresetsByCategory('recommended');
|
const RECOMMENDED_PRESETS = getPresetsByCategory('recommended');
|
||||||
@@ -117,6 +127,7 @@ export function ProfileCreateDialog({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const baseUrlValue = useWatch({ control, name: 'baseUrl' });
|
const baseUrlValue = useWatch({ control, name: 'baseUrl' });
|
||||||
|
const targetValue = useWatch({ control, name: 'target' });
|
||||||
const applyPresetToForm = useCallback(
|
const applyPresetToForm = useCallback(
|
||||||
(preset: ProviderPreset | null) => {
|
(preset: ProviderPreset | null) => {
|
||||||
if (!preset) {
|
if (!preset) {
|
||||||
@@ -445,6 +456,30 @@ export function ProfileCreateDialog({
|
|||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<TabsContent value="models" className="p-6 mt-0 space-y-4">
|
<TabsContent value="models" className="p-6 mt-0 space-y-4">
|
||||||
|
|||||||
@@ -94,10 +94,13 @@ async function request<T>(url: string, options?: RequestInit): Promise<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
|
export type CliTarget = 'claude' | 'droid';
|
||||||
|
|
||||||
export interface Profile {
|
export interface Profile {
|
||||||
name: string;
|
name: string;
|
||||||
settingsPath: string;
|
settingsPath: string;
|
||||||
configured: boolean;
|
configured: boolean;
|
||||||
|
target?: CliTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateProfile {
|
export interface CreateProfile {
|
||||||
@@ -108,6 +111,7 @@ export interface CreateProfile {
|
|||||||
opusModel?: string;
|
opusModel?: string;
|
||||||
sonnetModel?: string;
|
sonnetModel?: string;
|
||||||
haikuModel?: string;
|
haikuModel?: string;
|
||||||
|
target?: CliTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateProfile {
|
export interface UpdateProfile {
|
||||||
@@ -117,6 +121,7 @@ export interface UpdateProfile {
|
|||||||
opusModel?: string;
|
opusModel?: string;
|
||||||
sonnetModel?: string;
|
sonnetModel?: string;
|
||||||
haikuModel?: string;
|
haikuModel?: string;
|
||||||
|
target?: CliTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Variant {
|
export interface Variant {
|
||||||
@@ -126,6 +131,7 @@ export interface Variant {
|
|||||||
account?: string;
|
account?: string;
|
||||||
port?: number;
|
port?: number;
|
||||||
model?: string;
|
model?: string;
|
||||||
|
target?: CliTarget;
|
||||||
type?: 'composite';
|
type?: 'composite';
|
||||||
default_tier?: 'opus' | 'sonnet' | 'haiku';
|
default_tier?: 'opus' | 'sonnet' | 'haiku';
|
||||||
tiers?: {
|
tiers?: {
|
||||||
@@ -140,6 +146,7 @@ export interface CreateVariant {
|
|||||||
provider: CLIProxyProvider;
|
provider: CLIProxyProvider;
|
||||||
model?: string;
|
model?: string;
|
||||||
account?: string;
|
account?: string;
|
||||||
|
target?: CliTarget;
|
||||||
type?: 'composite';
|
type?: 'composite';
|
||||||
default_tier?: 'opus' | 'sonnet' | 'haiku';
|
default_tier?: 'opus' | 'sonnet' | 'haiku';
|
||||||
tiers?: {
|
tiers?: {
|
||||||
@@ -153,6 +160,7 @@ export interface UpdateVariant {
|
|||||||
provider?: CLIProxyProvider;
|
provider?: CLIProxyProvider;
|
||||||
model?: string;
|
model?: string;
|
||||||
account?: string;
|
account?: string;
|
||||||
|
target?: CliTarget;
|
||||||
type?: 'composite';
|
type?: 'composite';
|
||||||
default_tier?: 'opus' | 'sonnet' | 'haiku';
|
default_tier?: 'opus' | 'sonnet' | 'haiku';
|
||||||
tiers?: {
|
tiers?: {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useState, useMemo } from 'react';
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
|
import { Badge } from '@/components/ui/badge';
|
||||||
import {
|
import {
|
||||||
Plus,
|
Plus,
|
||||||
Search,
|
Search,
|
||||||
@@ -222,6 +223,7 @@ export function ApiPage() {
|
|||||||
<ProfileEditor
|
<ProfileEditor
|
||||||
key={selectedProfileData.name}
|
key={selectedProfileData.name}
|
||||||
profileName={selectedProfileData.name}
|
profileName={selectedProfileData.name}
|
||||||
|
profileTarget={selectedProfileData.target}
|
||||||
onDelete={() => setDeleteConfirm(selectedProfileData.name)}
|
onDelete={() => setDeleteConfirm(selectedProfileData.name)}
|
||||||
onHasChangesUpdate={setEditorHasChanges}
|
onHasChangesUpdate={setEditorHasChanges}
|
||||||
/>
|
/>
|
||||||
@@ -308,7 +310,12 @@ function ProfileListItem({
|
|||||||
|
|
||||||
{/* Profile info */}
|
{/* Profile info */}
|
||||||
<div className="flex-1 min-w-0">
|
<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="flex items-center gap-1.5 min-w-0">
|
||||||
<div className="text-xs text-muted-foreground truncate flex-1">
|
<div className="text-xs text-muted-foreground truncate flex-1">
|
||||||
{profile.settingsPath}
|
{profile.settingsPath}
|
||||||
|
|||||||
Reference in New Issue
Block a user