{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "ai-provider-editor", "title": "AI Provider Editor", "description": "Controlled canonical Provider editor for identity, connection, plaintext API key, routing, capabilities, tags, and extensions", "dependencies": ["@wener/ai@^0.1.21", "daisyui"], "registryDependencies": ["https://ui-components.wener.me/r/ai-config-editor.json"], "files": [ { "path": "registry/default/blocks/ai-provider-editor/ai-provider-editor.tsx", "content": "'use client';\n\nimport { DefaultApiRegistry, type Provider, ProviderSchema } from '@wener/ai/schema';\nimport type { ComponentPropsWithRef, ReactNode } from 'react';\nimport {\n\tAiConfigEditor,\n\ttype AiConfigEditorOption,\n\ttype AiConfigEditorSlots,\n\tAiConfigFieldGrid,\n\tAiConfigFieldSlot,\n\tAiConfigFormSection,\n\tAiConfigKeyValueField,\n\ttype AiConfigSchema,\n\tAiConfigSecretField,\n\tAiConfigSelectField,\n\tAiConfigStringListField,\n\tAiConfigTextareaField,\n\tAiConfigTextField,\n\tAiConfigToggleField,\n\ttype AiConfigValidationMode,\n\ttype AiResourceEditorMessageOverrides,\n\ttype AiResourceEditorMessages,\n\taiConfigFieldError,\n\tJsonValueEditor,\n\tmergeResourceEditorMessages,\n\tuseAiConfigDraft,\n} from '../../ui/ai-config-editor';\n\nexport type AiProviderEditorField =\n\t| 'id'\n\t| 'name'\n\t| 'title'\n\t| 'description'\n\t| 'type'\n\t| 'apiType'\n\t| 'baseUrl'\n\t| 'apiKey'\n\t| 'headers'\n\t| 'env'\n\t| 'envVars'\n\t| 'credentials'\n\t| 'proxyMode'\n\t| 'proxy'\n\t| 'endpoints'\n\t| 'defaultEndpointId'\n\t| 'defaultModelId'\n\t| 'capabilities'\n\t| 'tags'\n\t| 'labels'\n\t| 'notes'\n\t| 'options'\n\t| 'extensions'\n\t| 'enabled';\nexport type AiProviderEditorSection = 'identity' | 'connection' | 'routing' | 'metadata';\nexport type AiProviderEditorMessages = AiResourceEditorMessages;\nexport type AiProviderEditorMessageOverrides = AiResourceEditorMessageOverrides<\n\tAiProviderEditorField,\n\tAiProviderEditorSection\n>;\n\nexport type AiProviderEditorProps = Omit, 'onChange' | 'title'> & {\n\tvalue: Provider;\n\tonChange: (value: Provider) => void;\n\tonSubmit?: (value: Provider) => void;\n\tschema?: AiConfigSchema;\n\tvalidationMode?: AiConfigValidationMode;\n\tdisabled?: boolean;\n\treadOnly?: boolean;\n\tmessages?: AiProviderEditorMessageOverrides;\n\tslots?: AiConfigEditorSlots;\n\tapiTypeOptions?: readonly AiConfigEditorOption[];\n};\n\nexport const defaultAiProvider: Provider = ProviderSchema.parse({\n\tid: 'provider-new',\n\tname: 'provider-new',\n\tenabled: true,\n});\n\nconst defaultApiTypeOptions = DefaultApiRegistry.apiTypes.map((apiType) => ({\n\tvalue: apiType.name,\n\tlabel: apiType.name,\n\tdescription: apiType.family.name,\n}));\n\nconst defaultMessages: AiProviderEditorMessages = {\n\ttitle: 'AI Provider 配置',\n\tdescription: '编辑独立 Provider 身份、连接、路由能力和扩展配置。',\n\tsections: { identity: '身份', connection: '连接与凭据', routing: '路由与能力', metadata: '标签与扩展' },\n\tfields: {\n\t\tid: 'ID',\n\t\tname: '名称',\n\t\ttitle: '显示名称',\n\t\tdescription: '描述',\n\t\ttype: 'Provider 类型',\n\t\tapiType: 'API 类型',\n\t\tbaseUrl: 'Base URL',\n\t\tapiKey: 'API Key',\n\t\theaders: '请求头',\n\t\tenv: '环境变量值',\n\t\tenvVars: '环境变量引用',\n\t\tcredentials: '凭据扩展',\n\t\tproxyMode: '代理模式',\n\t\tproxy: '启用代理',\n\t\tendpoints: 'Endpoint 引用',\n\t\tdefaultEndpointId: '默认 Endpoint',\n\t\tdefaultModelId: '默认 Model',\n\t\tcapabilities: '能力',\n\t\ttags: '标签',\n\t\tlabels: 'Labels',\n\t\tnotes: '备注',\n\t\toptions: 'Options',\n\t\textensions: 'Extensions',\n\t\tenabled: '启用 Provider',\n\t},\n};\n\nexport function AiProviderEditor({\n\tvalue,\n\tonChange,\n\tonSubmit,\n\tschema = ProviderSchema,\n\tvalidationMode,\n\tdisabled = false,\n\treadOnly = false,\n\tmessages: messageOverrides,\n\tslots,\n\tapiTypeOptions = defaultApiTypeOptions,\n\t...props\n}: AiProviderEditorProps) {\n\tconst messages = mergeResourceEditorMessages(defaultMessages, messageOverrides);\n\tconst controller = useAiConfigDraft({\n\t\tvalue,\n\t\tfallbackValue: defaultAiProvider,\n\t\tschema,\n\t\tonChange,\n\t\tonSubmit,\n\t\tvalidationMode,\n\t});\n\tconst draft = controller.draft;\n\tconst patch = (next: Partial) => controller.setDraft({ ...draft, ...next });\n\tconst field = (name: AiProviderEditorField, defaultField: ReactNode) => (\n\t\t\n\t);\n\tconst locked = disabled || readOnly;\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'id',\n\t\t\t\t\t\t patch({ id })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'name',\n\t\t\t\t\t\t patch({ name })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'title',\n\t\t\t\t\t\t patch({ title: optional(title) })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'type',\n\t\t\t\t\t\t patch({ type: optional(type) })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t\n\t\t\t\t{field(\n\t\t\t\t\t'description',\n\t\t\t\t\t patch({ description: optional(description) })}\n\t\t\t\t\t/>,\n\t\t\t\t)}\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'apiType',\n\t\t\t\t\t\t patch({ apiType: optional(apiType) })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'baseUrl',\n\t\t\t\t\t\t patch({ baseUrl: optional(baseUrl) })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t\n\t\t\t\t{field(\n\t\t\t\t\t'apiKey',\n\t\t\t\t\t patch({ apiKey: optional(apiKey) })}\n\t\t\t\t\t/>,\n\t\t\t\t)}\n\t\t\t\t
\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'headers',\n\t\t\t\t\t\t patch({ headers })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'env',\n\t\t\t\t\t\t patch({ env })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t
\n\t\t\t\t{field(\n\t\t\t\t\t'envVars',\n\t\t\t\t\t patch({ envVars })}\n\t\t\t\t\t/>,\n\t\t\t\t)}\n\t\t\t\t{field(\n\t\t\t\t\t'credentials',\n\t\t\t\t\t patch({ credentials: credentials as Provider['credentials'] })}\n\t\t\t\t\t/>,\n\t\t\t\t)}\n\t\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'proxyMode',\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tpatch({ proxyMode: proxyMode ? (proxyMode as Provider['proxyMode']) : undefined })\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'proxy',\n\t\t\t\t\t\t patch({ proxy })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'defaultEndpointId',\n\t\t\t\t\t\t patch({ defaultEndpointId: optional(defaultEndpointId) })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'defaultModelId',\n\t\t\t\t\t\t patch({ defaultModelId: optional(defaultModelId) })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'endpoints',\n\t\t\t\t\t\t patch({ endpoints })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'capabilities',\n\t\t\t\t\t\t patch({ capabilities })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t
\n\t\t\t
\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'tags',\n\t\t\t\t\t\t patch({ tags })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'labels',\n\t\t\t\t\t\t patch({ labels })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t
\n\t\t\t\t{field(\n\t\t\t\t\t'notes',\n\t\t\t\t\t patch({ notes: optional(notes) })}\n\t\t\t\t\t/>,\n\t\t\t\t)}\n\t\t\t\t
\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'options',\n\t\t\t\t\t\t patch({ options: options as Provider['options'] })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t\t{field(\n\t\t\t\t\t\t'extensions',\n\t\t\t\t\t\t patch({ extensions: extensions as Provider['extensions'] })}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t
\n\t\t\t\t{field(\n\t\t\t\t\t'enabled',\n\t\t\t\t\t patch({ enabled })}\n\t\t\t\t\t/>,\n\t\t\t\t)}\n\t\t\t
\n\t\t\n\t);\n}\n\nfunction optional(value: string): string | undefined {\n\treturn value.trim() ? value : undefined;\n}\n", "type": "registry:component", "target": "@components/blocks/ai-provider-editor/ai-provider-editor.tsx" }, { "path": "registry/default/blocks/ai-provider-editor/index.ts", "content": "export * from './ai-provider-editor';\n", "type": "registry:component", "target": "@components/blocks/ai-provider-editor/index.ts" } ], "type": "registry:block" }