refactor(forms): extract OpenCode/OMO/OpenClaw state from ProviderForm into dedicated hooks

Split ProviderForm.tsx (2227 → 1526 lines) by extracting:
- opencodeFormUtils.ts: pure functions and default config constants
- useOmoModelSource: OMO model source collection from OpenCode providers
- useOpencodeFormState: OpenCode provider form state and handlers
- useOmoDraftState: OMO profile draft editing state
- useOpenclawFormState: OpenClaw provider form state and handlers
This commit is contained in:
Jason
2026-02-18 23:36:36 +08:00
parent 9514d08ef6
commit 0f4ce74916
7 changed files with 1161 additions and 854 deletions
@@ -0,0 +1,161 @@
import type { OpenCodeModel, OpenCodeProviderConfig } from "@/types";
import type { OmoGlobalConfig } from "@/types/omo";
import { parseOmoOtherFieldsObject } from "@/types/omo";
import type { PricingModelSourceOption } from "../ProviderAdvancedConfig";
// ── Default configs ──────────────────────────────────────────────────
export const CLAUDE_DEFAULT_CONFIG = JSON.stringify({ env: {} }, null, 2);
export const CODEX_DEFAULT_CONFIG = JSON.stringify(
{ auth: {}, config: "" },
null,
2,
);
export const GEMINI_DEFAULT_CONFIG = JSON.stringify(
{
env: {
GOOGLE_GEMINI_BASE_URL: "",
GEMINI_API_KEY: "",
GEMINI_MODEL: "gemini-3-pro-preview",
},
},
null,
2,
);
export const OPENCODE_DEFAULT_NPM = "@ai-sdk/openai-compatible";
export const OPENCODE_DEFAULT_CONFIG = JSON.stringify(
{
npm: OPENCODE_DEFAULT_NPM,
options: {
baseURL: "",
apiKey: "",
},
models: {},
},
null,
2,
);
export const OPENCODE_KNOWN_OPTION_KEYS = [
"baseURL",
"apiKey",
"headers",
] as const;
export const OPENCLAW_DEFAULT_CONFIG = JSON.stringify(
{
baseUrl: "",
apiKey: "",
api: "openai-completions",
models: [],
},
null,
2,
);
export const EMPTY_OMO_GLOBAL_CONFIG: OmoGlobalConfig = {
id: "global",
disabledAgents: [],
disabledMcps: [],
disabledHooks: [],
disabledSkills: [],
updatedAt: "",
};
// ── Pure functions ───────────────────────────────────────────────────
export function isKnownOpencodeOptionKey(key: string): boolean {
return OPENCODE_KNOWN_OPTION_KEYS.includes(
key as (typeof OPENCODE_KNOWN_OPTION_KEYS)[number],
);
}
export function parseOpencodeConfig(
settingsConfig?: Record<string, unknown>,
): OpenCodeProviderConfig {
const normalize = (
parsed: Partial<OpenCodeProviderConfig>,
): OpenCodeProviderConfig => ({
npm: parsed.npm || OPENCODE_DEFAULT_NPM,
options:
parsed.options && typeof parsed.options === "object"
? (parsed.options as OpenCodeProviderConfig["options"])
: {},
models:
parsed.models && typeof parsed.models === "object"
? (parsed.models as Record<string, OpenCodeModel>)
: {},
});
try {
const parsed = JSON.parse(
settingsConfig ? JSON.stringify(settingsConfig) : OPENCODE_DEFAULT_CONFIG,
) as Partial<OpenCodeProviderConfig>;
return normalize(parsed);
} catch {
return {
npm: OPENCODE_DEFAULT_NPM,
options: {},
models: {},
};
}
}
export function parseOpencodeConfigStrict(
settingsConfig?: Record<string, unknown>,
): OpenCodeProviderConfig {
const parsed = JSON.parse(
settingsConfig ? JSON.stringify(settingsConfig) : OPENCODE_DEFAULT_CONFIG,
) as Partial<OpenCodeProviderConfig>;
return {
npm: parsed.npm || OPENCODE_DEFAULT_NPM,
options:
parsed.options && typeof parsed.options === "object"
? (parsed.options as OpenCodeProviderConfig["options"])
: {},
models:
parsed.models && typeof parsed.models === "object"
? (parsed.models as Record<string, OpenCodeModel>)
: {},
};
}
export function toOpencodeExtraOptions(
options: OpenCodeProviderConfig["options"],
): Record<string, string> {
const extra: Record<string, string> = {};
for (const [k, v] of Object.entries(options || {})) {
if (!isKnownOpencodeOptionKey(k)) {
extra[k] = typeof v === "string" ? v : JSON.stringify(v);
}
}
return extra;
}
export function buildOmoProfilePreview(
agents: Record<string, Record<string, unknown>>,
categories: Record<string, Record<string, unknown>>,
otherFieldsStr: string,
): Record<string, unknown> {
const profileOnly: Record<string, unknown> = {};
if (Object.keys(agents).length > 0) {
profileOnly.agents = agents;
}
if (Object.keys(categories).length > 0) {
profileOnly.categories = categories;
}
if (otherFieldsStr.trim()) {
try {
const other = parseOmoOtherFieldsObject(otherFieldsStr);
if (other) {
Object.assign(profileOnly, other);
}
} catch {}
}
return profileOnly;
}
export const normalizePricingSource = (
value?: string,
): PricingModelSourceOption =>
value === "request" || value === "response" ? value : "inherit";