Skip to content

Commit 10db92b

Browse files
committed
feat: 移动端对话 header 增加可以选择 prompt 和模型下拉框
1 parent ec59772 commit 10db92b

File tree

2 files changed

+142
-2
lines changed

2 files changed

+142
-2
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"use client"
2+
3+
import { BotMessageSquare, BotOff, Check, Drama } from "lucide-react"
4+
import usePromptStore from "@/stores/prompt"
5+
import useSettingStore from "@/stores/setting"
6+
import { Store } from "@tauri-apps/plugin-store"
7+
import { AiConfig } from "@/app/core/setting/config"
8+
import { useEffect, useState } from "react"
9+
import * as React from "react"
10+
import { cn } from "@/lib/utils"
11+
import {
12+
Popover,
13+
PopoverContent,
14+
PopoverTrigger,
15+
} from "@/components/ui/popover"
16+
import {
17+
Command,
18+
CommandEmpty,
19+
CommandGroup,
20+
CommandInput,
21+
CommandItem,
22+
CommandList,
23+
} from "@/components/ui/command"
24+
25+
export function ChatHeader() {
26+
const { promptList, currentPrompt, setCurrentPrompt } = usePromptStore()
27+
const { primaryModel, setPrimaryModel } = useSettingStore()
28+
29+
const [models, setModels] = useState<AiConfig[]>([])
30+
const [promptOpen, setPromptOpen] = useState(false)
31+
const [modelOpen, setModelOpen] = useState(false)
32+
33+
async function getModels() {
34+
const store = await Store.load('store.json');
35+
const aiModelList = await store.get<AiConfig[]>('aiModelList');
36+
if (!aiModelList) return [];
37+
const filteredModels = aiModelList.filter(item => {
38+
return item.apiKey && item.model && item.baseURL
39+
})
40+
setModels(filteredModels)
41+
return filteredModels;
42+
}
43+
44+
async function modelSelectChangeHandler(key: string) {
45+
setPrimaryModel(key)
46+
const store = await Store.load('store.json');
47+
store.set('primaryModel', key)
48+
setModelOpen(false)
49+
}
50+
51+
async function promptSelectChangeHandler(id: string) {
52+
const selectedPrompt = promptList.find(item => item.id === id)
53+
if (!selectedPrompt) return
54+
await setCurrentPrompt(selectedPrompt)
55+
setPromptOpen(false)
56+
}
57+
58+
useEffect(() => {
59+
getModels()
60+
}, [])
61+
62+
return (
63+
<header className="h-12 w-full flex justify-between items-center border-b px-4 text-sm gap-4">
64+
<Popover open={promptOpen} onOpenChange={setPromptOpen}>
65+
<PopoverTrigger asChild>
66+
<div className="flex items-center gap-1 cursor-pointer hover:opacity-80">
67+
<Drama className="size-4" />
68+
<span className="line-clamp-1">
69+
{currentPrompt?.title}
70+
</span>
71+
</div>
72+
</PopoverTrigger>
73+
<PopoverContent className="w-[180px] p-0">
74+
<Command>
75+
<CommandInput placeholder="Search prompt..." className="h-9" />
76+
<CommandList>
77+
<CommandGroup>
78+
{promptList?.map((item) => (
79+
<CommandItem
80+
key={item.id}
81+
value={item.id}
82+
onSelect={(currentValue) => {
83+
promptSelectChangeHandler(currentValue)
84+
}}
85+
>
86+
{item.title}
87+
<Check
88+
className={cn(
89+
"ml-auto",
90+
currentPrompt?.id === item.id ? "opacity-100" : "opacity-0"
91+
)}
92+
/>
93+
</CommandItem>
94+
))}
95+
</CommandGroup>
96+
</CommandList>
97+
</Command>
98+
</PopoverContent>
99+
</Popover>
100+
101+
<Popover open={modelOpen} onOpenChange={setModelOpen}>
102+
<PopoverTrigger asChild>
103+
<div className="flex items-center justify-center gap-1 cursor-pointer hover:opacity-80">
104+
{models.length > 0 ? <BotMessageSquare className="!size-4" /> : <BotOff className="size-4" />}
105+
<span className="line-clamp-1 flex-1 lg:flex-none">
106+
{models.find(model => model.key === primaryModel)?.model}
107+
</span>
108+
</div>
109+
</PopoverTrigger>
110+
<PopoverContent className="w-[300px] p-0">
111+
<Command>
112+
<CommandInput placeholder="Search model..." className="h-9" />
113+
<CommandList>
114+
<CommandEmpty>No model found.</CommandEmpty>
115+
<CommandGroup>
116+
{models.filter(item => item.modelType === 'chat' || !item.modelType).map((item) => (
117+
<CommandItem
118+
key={item.key}
119+
value={item.key}
120+
onSelect={(currentValue) => {
121+
modelSelectChangeHandler(currentValue)
122+
}}
123+
>
124+
{`${item.model}(${item.title})`}
125+
<Check
126+
className={cn(
127+
"ml-auto",
128+
primaryModel === item.key ? "opacity-100" : "opacity-0"
129+
)}
130+
/>
131+
</CommandItem>
132+
))}
133+
</CommandGroup>
134+
</CommandList>
135+
</Command>
136+
</PopoverContent>
137+
</Popover>
138+
</header>
139+
)
140+
}

src/app/mobile/chat/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
import ChatContent from '@/app/core/record/chat/chat-content'
33
import { ClipboardListener } from '@/app/core/record/chat/clipboard-listener'
44
import { ChatInput } from '@/app/core/record/chat/chat-input'
5-
import { ChatHeader } from '@/app/core/record/chat/chat-header'
5+
import { ChatHeader } from './components/chat-header'
66

77
export default function Chat() {
88
return (
99
<div className="flex flex-col flex-1 w-full">
10-
<ChatHeader />
10+
<ChatHeader />
1111
<ChatContent />
1212
<ClipboardListener />
1313
<ChatInput />

0 commit comments

Comments
 (0)