Feat: 新增自定义选择模型功能

This commit is contained in:
奇趣保罗 2025-11-12 14:49:43 +08:00
parent b71de1f5c9
commit 83e826e034
4 changed files with 29 additions and 7 deletions

View File

@ -25,6 +25,7 @@ type Props = {
languages: string[];
paths: string[];
prompt: string | undefined;
model?: string | undefined;
initialSelectedLanguages?: string[];
getExistingByPath?: (path: string) => Record<string, string>;
onConfirm: (
@ -33,7 +34,7 @@ type Props = {
) => Promise<void> | void;
};
function AiTranslateModalImpl({ open, onOpenChange, languages, paths, prompt, initialSelectedLanguages, getExistingByPath, onConfirm }: Props) {
function AiTranslateModalImpl({ open, onOpenChange, languages, paths, prompt, model, initialSelectedLanguages, getExistingByPath, onConfirm }: Props) {
const [inputsByKey, setInputsByKey] = useState<Record<string, string>>({});
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
@ -92,7 +93,7 @@ function AiTranslateModalImpl({ open, onOpenChange, languages, paths, prompt, in
setLoading(true);
setError(null);
try {
const result = await requestTranslations({ items, languages: selectedLangs, prompt });
const result = await requestTranslations({ items, languages: selectedLangs, prompt, model });
await onConfirm(result, { overwrite, selectedLanguages: selectedLangs });
setInputsByKey({});
onOpenChange(false);

View File

@ -8,13 +8,14 @@ type Props = {
open: boolean;
onOpenChange: (v: boolean) => void;
project: Project | null;
onSave: (update: { name: string; preferences: { aiPrompt?: string } }) => Promise<void> | void;
onSave: (update: { name: string; preferences: { aiPrompt?: string; aiModel?: string } }) => Promise<void> | void;
onDelete: () => Promise<void> | void;
};
function ProjectSettingsModalImpl({ open, onOpenChange, project, onSave, onDelete }: Props) {
const [name, setName] = useState("");
const [aiPrompt, setAiPrompt] = useState<string>("");
const [aiModel, setAiModel] = useState<string>("");
const [saving, setSaving] = useState(false);
const [err, setErr] = useState<string | null>(null);
@ -22,6 +23,7 @@ function ProjectSettingsModalImpl({ open, onOpenChange, project, onSave, onDelet
if (open) {
setName(project?.name ?? "");
setAiPrompt(project?.preferences?.aiPrompt ?? "");
setAiModel(project?.preferences?.aiModel ?? "");
setSaving(false);
setErr(null);
}
@ -35,7 +37,7 @@ function ProjectSettingsModalImpl({ open, onOpenChange, project, onSave, onDelet
try {
await onSave({
name: trimmed,
preferences: { aiPrompt },
preferences: { aiPrompt, aiModel },
});
onOpenChange(false);
} catch (e) {
@ -69,6 +71,22 @@ function ProjectSettingsModalImpl({ open, onOpenChange, project, onSave, onDelet
<label className="block text-sm text-muted-foreground"></label>
<Input value={name} onChange={(e) => setName(e.target.value)} placeholder="输入项目名称" />
</div>
<div>
<label className="block text-sm text-muted-foreground">AI </label>
<Input
list="ai-model-options"
value={aiModel}
onChange={(e) => setAiModel(e.target.value)}
placeholder="例如openai/gpt-5"
/>
<datalist id="ai-model-options">
<option value="openai/gpt-5" />
<option value="gpt-4o-mini" />
<option value="gpt-4o" />
<option value="gpt-4.1-mini" />
<option value="o4-mini" />
</datalist>
</div>
<div>
<label className="block text-sm text-muted-foreground">AI Prompt</label>
<textarea

View File

@ -5,6 +5,7 @@ export type Project = {
updatedAt: number; // ms timestamp
preferences?: {
aiPrompt?: string;
aiModel?: string;
};
};

View File

@ -232,7 +232,7 @@ export default function Editor() {
/>
</td>
<td className="px-3 py-2 font-mono wrap-break-word">
<button type="button" className="w-full text-left min-h-8 leading-8 px-2 rounded hover:bg-accent" onClick={handleCopy} title="点击复制">
<button type="button" className="w-full text-left min-h-8 leading-normal px-2 rounded hover:bg-accent" onClick={handleCopy} title="点击复制">
{entry.path}
</button>
</td>
@ -251,12 +251,12 @@ export default function Editor() {
onBlur={inline.saveEdit}
onKeyDown={inline.handleKeyDown}
disabled={isSaving}
className="leading-8 px-2 py-0"
className="leading-normal px-2 py-0"
/>
) : (
<button
type="button"
className="w-full text-left min-h-8 leading-8 px-2 rounded hover:bg-accent"
className="w-full text-left min-h-8 leading-normal px-2 rounded hover:bg-accent"
onClick={() => inline.startEdit(entry.path, lang)}
title="点击编辑"
>
@ -517,6 +517,7 @@ export default function Editor() {
initialSelectedLanguages={aiModal?.path ? computeSuggestedLanguages([aiModal.path]) : []}
getExistingByPath={getExistingByPath}
prompt={project?.preferences?.aiPrompt}
model={project?.preferences?.aiModel}
onConfirm={async (translations, options) => {
if (!projectId || !aiModal) return;
const updates: Record<string, Record<string, string>> = {};
@ -554,6 +555,7 @@ export default function Editor() {
initialSelectedLanguages={computeSuggestedLanguages(Array.from(selected))}
getExistingByPath={getExistingByPath}
prompt={project?.preferences?.aiPrompt}
model={project?.preferences?.aiModel}
onConfirm={async (translations, options) => {
if (!projectId || selected.size === 0) return;
try {