feat(opencode): add manual provider key input with duplicate check

- Add Provider Key input field for OpenCode providers (between icon and name)
- User must manually enter a unique key instead of auto-generating from name
- Real-time validation: format check and duplicate detection
- Key is immutable after creation (disabled in edit mode)
- Remove slugify auto-generation logic from mutations
- Add beforeNameSlot prop to BasicFormFields for extensibility
- Add i18n translations for zh/en/ja
This commit is contained in:
Jason
2026-01-17 20:28:11 +08:00
parent ad6f5b388b
commit 5bcf5bf382
8 changed files with 142 additions and 27 deletions
+18 -19
View File
@@ -6,36 +6,35 @@ import type { Provider, Settings } from "@/types";
import { extractErrorMessage } from "@/utils/errorUtils";
import { generateUUID } from "@/utils/uuid";
/**
* Convert a name to a URL-safe slug for use as provider ID
* Used for OpenCode's additive mode where ID becomes the config key
*/
function slugify(name: string): string {
return name
.toLowerCase()
.trim()
.replace(/[\s_]+/g, "-") // spaces and underscores → hyphens
.replace(/[^a-z0-9-]/g, "") // remove special characters
.replace(/-+/g, "-") // collapse multiple hyphens
.replace(/^-|-$/g, ""); // trim leading/trailing hyphens
}
export const useAddProviderMutation = (appId: AppId) => {
const queryClient = useQueryClient();
const { t } = useTranslation();
return useMutation({
mutationFn: async (providerInput: Omit<Provider, "id">) => {
// OpenCode: use slugified name as ID (for readable config keys)
// Other apps: use random UUID
const id =
appId === "opencode" ? slugify(providerInput.name) : generateUUID();
mutationFn: async (
providerInput: Omit<Provider, "id"> & { providerKey?: string }
) => {
let id: string;
if (appId === "opencode") {
// OpenCode: use user-provided providerKey as ID
if (!providerInput.providerKey) {
throw new Error("Provider key is required for OpenCode");
}
id = providerInput.providerKey;
} else {
// Other apps: use random UUID
id = generateUUID();
}
const newProvider: Provider = {
...providerInput,
id,
createdAt: Date.now(),
};
// Remove providerKey from the provider object before saving
delete (newProvider as any).providerKey;
await providersApi.add(newProvider, appId);
return newProvider;
},