mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 04:18:05 +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 { 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">
|
||||
|
||||
@@ -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?: {
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user