Skip to content

Commit 21f78fe

Browse files
authored
Merge pull request #5 from GwIhViEte/main
设置界面新增模型选项
2 parents aadc66d + b4a0d77 commit 21f78fe

File tree

4 files changed

+128
-7
lines changed

4 files changed

+128
-7
lines changed

src/main/ai.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export function getSolutionStream(base64Image: string) {
1010
})
1111

1212
const { textStream } = streamText({
13-
model: openai(settings.model),
13+
model: openai(settings.model || 'gpt-4o-mini'),
1414
system: settings.customPrompt || PROMPT_SYSTEM,
1515
messages: [
1616
{

src/renderer/src/lib/store/settings.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ interface Settings {
99

1010
opacity: number
1111
codeLanguage: string
12+
model: string
1213
}
1314

1415
interface SettingsStore extends Settings {
@@ -21,6 +22,7 @@ const defaultSettings: Settings = {
2122
apiKey: '',
2223
customPrompt: '',
2324
codeLanguage: '',
25+
model: '',
2426

2527
opacity: 0.8
2628
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { useState } from 'react'
2+
import { Button } from '@/components/ui/button'
3+
import { cn } from '@/lib/utils'
4+
import { ChevronsUpDown, Check, Plus } from 'lucide-react'
5+
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
6+
import {
7+
Command,
8+
CommandEmpty,
9+
CommandGroup,
10+
CommandInput,
11+
CommandItem,
12+
CommandList
13+
} from '@/components/ui/command'
14+
15+
const defaultModels = [
16+
{ value: 'gpt-4o-mini', label: 'gpt-4o-mini' },
17+
{ value: 'gpt-4o', label: 'gpt-4o' },
18+
{ value: 'gpt-4.1-mini', label: 'gpt-4.1-mini' },
19+
{ value: 'gpt-4.1', label: 'gpt-4.1' }
20+
]
21+
22+
export function SelectModel({
23+
value,
24+
onChange,
25+
disabled,
26+
className
27+
}: {
28+
value?: string
29+
onChange?: (value: string) => void
30+
disabled?: boolean
31+
className?: string
32+
}) {
33+
const [open, setOpen] = useState(false)
34+
const [models, setModels] = useState(defaultModels)
35+
const [searchValue, setSearchValue] = useState('')
36+
37+
const addCustomModel = (newModel: string) => {
38+
const trimmed = newModel.trim()
39+
if (!trimmed) return
40+
const newValue = trimmed
41+
const exists = models.some((m) => m.value === newValue)
42+
if (exists) {
43+
onChange?.(newValue)
44+
setOpen(false)
45+
setSearchValue('')
46+
return
47+
}
48+
const item = { value: newValue, label: trimmed }
49+
setModels((prev) => [...prev, item])
50+
onChange?.(newValue)
51+
setSearchValue('')
52+
setOpen(false)
53+
}
54+
55+
const filtered = models.filter((m) => m.label.toLowerCase().includes(searchValue.toLowerCase()))
56+
const showCreate =
57+
searchValue && !filtered.some((m) => m.label.toLowerCase() === searchValue.toLowerCase())
58+
59+
return (
60+
<Popover open={open} onOpenChange={setOpen}>
61+
<PopoverTrigger asChild>
62+
<Button
63+
variant="outline"
64+
role="combobox"
65+
aria-expanded={open}
66+
disabled={disabled}
67+
className={cn('w-60 justify-between', className)}
68+
>
69+
{value ? models.find((m) => m.value === value)?.label : '选择模型...'}
70+
<ChevronsUpDown className="opacity-50" />
71+
</Button>
72+
</PopoverTrigger>
73+
<PopoverContent className="w-60 p-0">
74+
<Command>
75+
<CommandInput
76+
placeholder="输入以搜索或创建..."
77+
className="h-9"
78+
value={searchValue}
79+
onValueChange={setSearchValue}
80+
/>
81+
<CommandList>
82+
<CommandEmpty>未找到结果</CommandEmpty>
83+
<CommandGroup>
84+
{filtered.map((m) => (
85+
<CommandItem
86+
key={m.value}
87+
value={m.value}
88+
onSelect={(current) => {
89+
onChange?.(current === value ? '' : current)
90+
setSearchValue('')
91+
setOpen(false)
92+
}}
93+
>
94+
{m.label}
95+
<Check className={cn('ml-auto', value === m.value ? 'opacity-100' : 'opacity-0')} />
96+
</CommandItem>
97+
))}
98+
{showCreate && (
99+
<CommandItem
100+
value={`create-${searchValue}`}
101+
onSelect={() => addCustomModel(searchValue)}
102+
className="!text-blue-600"
103+
>
104+
<Plus className="mr-2 h-4 w-4" />
105+
创建 “{searchValue}
106+
</CommandItem>
107+
)}
108+
</CommandGroup>
109+
</CommandList>
110+
</Command>
111+
</PopoverContent>
112+
</Popover>
113+
)
114+
}
115+

src/renderer/src/settings/index.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ import { Switch } from '@/components/ui/switch'
1717
import { useSettingsStore } from '@/lib/store/settings'
1818
import { SelectLanguage } from './SelectLanguage'
1919
import { CustomShortcuts, ResetDefaultShortcuts } from './CustomShortcuts'
20+
import { SelectModel } from './SelectModel'
2021

2122
export default function SettingsPage() {
22-
const { opacity, codeLanguage, apiBaseURL, apiKey, customPrompt, updateSetting } =
23+
const { opacity, codeLanguage, apiBaseURL, apiKey, customPrompt, model, updateSetting } =
2324
useSettingsStore()
2425
const [showApiKey, setShowApiKey] = useState(false)
2526
const [enableCustomPrompt, setEnableCustomPrompt] = useState(customPrompt.trim().length > 0)
@@ -69,7 +70,7 @@ export default function SettingsPage() {
6970
value={apiBaseURL}
7071
onChange={(e) => updateSetting('apiBaseURL', e.target.value)}
7172
className="w-60 px-3 py-2 border border-gray-300 rounded-md bg-white text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
72-
placeholder="可不填,默认使用 OpenAI 的 API"
73+
placeholder="可为空,默认使用 OpenAI 的 API"
7374
/>
7475
</div>
7576

@@ -81,7 +82,7 @@ export default function SettingsPage() {
8182
value={apiKey}
8283
onChange={(e) => updateSetting('apiKey', e.target.value)}
8384
className="flex-1 px-3 py-2 border border-gray-300 rounded-l-md bg-white text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
84-
placeholder="请输入 API Key"
85+
placeholder="输入 API Key"
8586
/>
8687
<Button
8788
variant="ghost"
@@ -93,10 +94,13 @@ export default function SettingsPage() {
9394
</Button>
9495
</div>
9596
</div>
97+
98+
<div className="flex items-center justify-between">
99+
<label className="text-sm font-medium">Model</label>
100+
<SelectModel value={model} onChange={(val) => updateSetting('model', val)} />
101+
</div>
96102
</div>
97103
</div>
98-
99-
{/* General Settings */}
100104
<div className="bg-gray-300/80 rounded-lg p-6">
101105
<h2 className="text-lg font-semibold mb-4 flex items-center">
102106
<SquareTerminal className="h-5 w-5 mr-2" />
@@ -206,4 +210,4 @@ export default function SettingsPage() {
206210
</div>
207211
</>
208212
)
209-
}
213+
}

0 commit comments

Comments
 (0)