feat(deeplink): add Claude model fields support and enhance import dialog
- Add Claude-specific model field support in deeplink import: * Support model (ANTHROPIC_MODEL) - general default model * Support haikuModel (ANTHROPIC_DEFAULT_HAIKU_MODEL) * Support sonnetModel (ANTHROPIC_DEFAULT_SONNET_MODEL) * Support opusModel (ANTHROPIC_DEFAULT_OPUS_MODEL) * Backend: Update DeepLinkImportRequest struct to include optional model fields * Frontend: Add TypeScript type definitions for new model parameters - Enhance deeplink demo page (deplink.html): * Add 5 new Claude configuration examples showcasing different model setups * Add parameter documentation with required/optional tags * Include basic config (no models), single model, complete 4-model, partial models, and third-party provider examples * Improve visual design with param-list component and color-coded badges * Add detailed descriptions for each configuration scenario - Redesign DeepLinkImportDialog layout: * Switch from 3-column to compact 2-column grid layout * Reduce dialog width from 500px to 650px for better content display * Add dedicated section for Claude model configurations with blue highlight box * Use uppercase labels and smaller text for more information density * Add truncation and tooltips for long URLs * Improve visual hierarchy with spacing and grouping * Increase z-index to 9999 to ensure dialog appears on top - Minor UI refinements: * Update App.tsx layout adjustments * Optimize McpFormModal styling * Refine ProviderCard and BasicFormFields components This enables users to import Claude providers with precise model configurations via deeplink.
This commit is contained in:
@@ -509,8 +509,9 @@ function App() {
|
||||
</header>
|
||||
|
||||
<main
|
||||
className={`flex-1 overflow-y-auto pb-12 animate-fade-in scroll-overlay ${currentView === "providers" ? "pt-24" : "pt-20"
|
||||
}`}
|
||||
className={`flex-1 overflow-y-auto pb-12 animate-fade-in scroll-overlay ${
|
||||
currentView === "providers" ? "pt-24" : "pt-20"
|
||||
}`}
|
||||
style={{ overflowX: "hidden" }}
|
||||
>
|
||||
{renderContent()}
|
||||
@@ -553,8 +554,8 @@ function App() {
|
||||
message={
|
||||
confirmDelete
|
||||
? t("confirm.deleteProviderMessage", {
|
||||
name: confirmDelete.name,
|
||||
})
|
||||
name: confirmDelete.name,
|
||||
})
|
||||
: ""
|
||||
}
|
||||
onConfirm={() => void handleConfirmDelete()}
|
||||
|
||||
@@ -96,8 +96,8 @@ export function DeepLinkImportDialog() {
|
||||
: "****";
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogContent className="sm:max-w-[500px]">
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen} modal={true}>
|
||||
<DialogContent className="sm:max-w-[650px] z-[9999]">
|
||||
{/* 标题显式左对齐,避免默认居中样式影响 */}
|
||||
<DialogHeader className="text-left sm:text-left">
|
||||
<DialogTitle>{t("deeplink.confirmImport")}</DialogTitle>
|
||||
@@ -106,82 +106,120 @@ export function DeepLinkImportDialog() {
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{/* 主体内容整体右移,略大于标题内边距,让内容看起来不贴边 */}
|
||||
<div className="space-y-4 px-8 py-4">
|
||||
{/* App Type */}
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<div className="font-medium text-sm text-muted-foreground">
|
||||
{t("deeplink.app")}
|
||||
{/* 使用两列布局压缩内容 */}
|
||||
<div className="space-y-4 px-4 py-3">
|
||||
{/* 第一行:应用类型 + 供应商名称 */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-1">
|
||||
<div className="font-medium text-xs text-muted-foreground uppercase">
|
||||
{t("deeplink.app")}
|
||||
</div>
|
||||
<div className="text-sm font-medium capitalize">
|
||||
{request.app}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-2 text-sm font-medium capitalize">
|
||||
{request.app}
|
||||
<div className="space-y-1">
|
||||
<div className="font-medium text-xs text-muted-foreground uppercase">
|
||||
{t("deeplink.providerName")}
|
||||
</div>
|
||||
<div className="text-sm font-medium truncate" title={request.name}>
|
||||
{request.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Provider Name */}
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<div className="font-medium text-sm text-muted-foreground">
|
||||
{t("deeplink.providerName")}
|
||||
{/* 第二行:官网 + 端点 */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-1">
|
||||
<div className="font-medium text-xs text-muted-foreground uppercase">
|
||||
{t("deeplink.homepage")}
|
||||
</div>
|
||||
<div className="text-xs break-all text-blue-600 dark:text-blue-400 line-clamp-2" title={request.homepage}>
|
||||
{request.homepage}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-2 text-sm font-medium">{request.name}</div>
|
||||
</div>
|
||||
|
||||
{/* Homepage */}
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<div className="font-medium text-sm text-muted-foreground">
|
||||
{t("deeplink.homepage")}
|
||||
</div>
|
||||
<div className="col-span-2 text-sm break-all text-blue-600 dark:text-blue-400">
|
||||
{request.homepage}
|
||||
<div className="space-y-1">
|
||||
<div className="font-medium text-xs text-muted-foreground uppercase">
|
||||
{t("deeplink.endpoint")}
|
||||
</div>
|
||||
<div className="text-xs break-all line-clamp-2" title={request.endpoint}>
|
||||
{request.endpoint}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* API Endpoint */}
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<div className="font-medium text-sm text-muted-foreground">
|
||||
{t("deeplink.endpoint")}
|
||||
</div>
|
||||
<div className="col-span-2 text-sm break-all">
|
||||
{request.endpoint}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* API Key (masked) */}
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<div className="font-medium text-sm text-muted-foreground">
|
||||
{/* 第三行:API Key */}
|
||||
<div className="space-y-1">
|
||||
<div className="font-medium text-xs text-muted-foreground uppercase">
|
||||
{t("deeplink.apiKey")}
|
||||
</div>
|
||||
<div className="col-span-2 text-sm font-mono text-muted-foreground">
|
||||
<div className="text-sm font-mono text-muted-foreground">
|
||||
{maskedApiKey}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Model (if present) */}
|
||||
{/* 第四行:默认模型(如果有) */}
|
||||
{request.model && (
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<div className="font-medium text-sm text-muted-foreground">
|
||||
<div className="space-y-1">
|
||||
<div className="font-medium text-xs text-muted-foreground uppercase">
|
||||
{t("deeplink.model")}
|
||||
</div>
|
||||
<div className="col-span-2 text-sm font-mono">
|
||||
{request.model}
|
||||
<div className="text-sm font-mono">{request.model}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Claude 专用模型字段(紧凑布局) */}
|
||||
{request.app === "claude" && (request.haikuModel || request.sonnetModel || request.opusModel) && (
|
||||
<div className="rounded-lg bg-blue-50 dark:bg-blue-900/20 p-3 space-y-2">
|
||||
<div className="font-medium text-xs text-blue-900 dark:text-blue-100 uppercase">
|
||||
{t("deeplink.claudeModels", "Claude 模型配置")}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-2 text-xs">
|
||||
{request.haikuModel && (
|
||||
<div>
|
||||
<span className="text-muted-foreground">Haiku:</span>
|
||||
<div className="font-mono truncate" title={request.haikuModel}>
|
||||
{request.haikuModel}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{request.sonnetModel && (
|
||||
<div>
|
||||
<span className="text-muted-foreground">Sonnet:</span>
|
||||
<div className="font-mono truncate" title={request.sonnetModel}>
|
||||
{request.sonnetModel}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{request.opusModel && (
|
||||
<div>
|
||||
<span className="text-muted-foreground">Opus:</span>
|
||||
<div className="font-mono truncate" title={request.opusModel}>
|
||||
{request.opusModel}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Notes (if present) */}
|
||||
{/* 备注(如果有) */}
|
||||
{request.notes && (
|
||||
<div className="grid grid-cols-3 items-start gap-4">
|
||||
<div className="font-medium text-sm text-muted-foreground">
|
||||
<div className="space-y-1">
|
||||
<div className="font-medium text-xs text-muted-foreground uppercase">
|
||||
{t("deeplink.notes")}
|
||||
</div>
|
||||
<div className="col-span-2 text-sm text-muted-foreground">
|
||||
<div className="text-sm text-muted-foreground line-clamp-2" title={request.notes}>
|
||||
{request.notes}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Warning */}
|
||||
<div className="rounded-lg bg-yellow-50 dark:bg-yellow-900/20 p-3 text-sm text-yellow-800 dark:text-yellow-200">
|
||||
{/* 警告提示(紧凑版) */}
|
||||
<div className="rounded-lg bg-yellow-50 dark:bg-yellow-900/20 p-2 text-xs text-yellow-800 dark:text-yellow-200">
|
||||
{t("deeplink.warning")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -440,10 +440,11 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
<button
|
||||
type="button"
|
||||
onClick={applyCustom}
|
||||
className={`inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors ${selectedPreset === -1
|
||||
className={`inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
selectedPreset === -1
|
||||
? "bg-emerald-500 text-white dark:bg-emerald-600"
|
||||
: "bg-accent text-muted-foreground hover:bg-accent/80"
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
{t("presetSelector.custom")}
|
||||
</button>
|
||||
@@ -454,10 +455,11 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
key={preset.id}
|
||||
type="button"
|
||||
onClick={() => applyPreset(idx)}
|
||||
className={`inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors ${selectedPreset === idx
|
||||
className={`inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
selectedPreset === idx
|
||||
? "bg-emerald-500 text-white dark:bg-emerald-600"
|
||||
: "bg-accent text-muted-foreground hover:bg-accent/80"
|
||||
}`}
|
||||
}`}
|
||||
title={t(descriptionKey)}
|
||||
>
|
||||
{preset.id}
|
||||
@@ -631,9 +633,7 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<label className="text-sm font-medium text-foreground">
|
||||
{useToml
|
||||
? t("mcp.form.tomlConfig")
|
||||
: t("mcp.form.jsonConfig")}
|
||||
{useToml ? t("mcp.form.tomlConfig") : t("mcp.form.jsonConfig")}
|
||||
</label>
|
||||
{(isEditing || selectedPreset === -1) && (
|
||||
<button
|
||||
|
||||
@@ -120,7 +120,7 @@ export function ProviderCard({
|
||||
? "border-primary/50 bg-primary/5 shadow-[0_0_20px_rgba(59,130,246,0.15)]"
|
||||
: "hover:scale-[1.01]",
|
||||
dragHandleProps?.isDragging &&
|
||||
"cursor-grabbing border-primary shadow-lg scale-105 z-10",
|
||||
"cursor-grabbing border-primary shadow-lg scale-105 z-10",
|
||||
)}
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-primary/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500 pointer-events-none" />
|
||||
|
||||
@@ -130,7 +130,10 @@ export function BasicFormFields({ form }: BasicFormFieldsProps) {
|
||||
<FormItem>
|
||||
<FormLabel>{t("provider.notes")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder={t("provider.notesPlaceholder")} />
|
||||
<Input
|
||||
{...field}
|
||||
placeholder={t("provider.notesPlaceholder")}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@@ -10,6 +10,10 @@ export interface DeepLinkImportRequest {
|
||||
apiKey: string;
|
||||
model?: string;
|
||||
notes?: string;
|
||||
// Claude 专用模型字段 (v3.7.1+)
|
||||
haikuModel?: string;
|
||||
sonnetModel?: string;
|
||||
opusModel?: string;
|
||||
}
|
||||
|
||||
export const deeplinkApi = {
|
||||
|
||||
Reference in New Issue
Block a user