Skip to content

Commit 2ced1b2

Browse files
authored
feat: 优化移动端界面布局 (#859)
* feat: 移动端增加 skills 配置页面 * feat: 优化移动端设置布局 * feat: 移动端 dialog 组件改为 drawer 展示 * fix(#725): 调整 MCP 选择工具布局,避免参数过长导致勾选无法展示
1 parent 5a8c474 commit 2ced1b2

File tree

19 files changed

+1195
-484
lines changed

19 files changed

+1195
-484
lines changed

src/app/core/record/chat/mcp-button.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ export function McpButton() {
6565
toggleServerSelection(server.id)
6666
}}
6767
>
68-
<div className="flex flex-col flex-1 gap-1">
69-
<div className="flex items-center gap-2">
70-
<span className="font-medium">{server.name}</span>
68+
<div className="flex flex-col flex-1 gap-1 min-w-0">
69+
<div className="flex items-center gap-2 min-w-0">
70+
<span className="font-medium truncate">{server.name}</span>
7171
<Badge variant="outline" className="text-[10px] px-1 py-0 h-4">
7272
{server.type}
7373
</Badge>

src/app/core/record/mark/control-link.tsx

Lines changed: 81 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ import {
1010
DialogTitle,
1111
DialogTrigger,
1212
} from "@/components/ui/dialog"
13+
import {
14+
Drawer,
15+
DrawerContent,
16+
DrawerDescription,
17+
DrawerFooter,
18+
DrawerHeader,
19+
DrawerTitle,
20+
DrawerTrigger,
21+
} from "@/components/ui/drawer"
1322
import { Input } from "@/components/ui/input"
1423
import { insertMark } from "@/db/marks"
1524
import useMarkStore from "@/stores/mark"
@@ -21,13 +30,16 @@ import { v4 as uuidv4 } from 'uuid'
2130
import emitter from '@/lib/emitter'
2231
import { useRouter } from 'next/navigation'
2332
import { handleRecordComplete } from '@/lib/record-navigation'
33+
import { useIsMobile } from '@/hooks/use-mobile'
34+
import { isMobileDevice as checkIsMobileDevice } from '@/lib/check'
2435

2536
export function ControlLink() {
2637
const t = useTranslations();
2738
const router = useRouter();
2839
const [open, setOpen] = useState(false);
2940
const [url, setUrl] = useState('')
3041
const [loading, setLoading] = useState(false)
42+
const isMobile = useIsMobile() || checkIsMobileDevice()
3143

3244
const { currentTagId, fetchTags, getCurrentTag } = useTagStore()
3345
const { fetchMarks, addQueue, setQueue, removeQueue } = useMarkStore()
@@ -186,36 +198,74 @@ export function ControlLink() {
186198
}
187199

188200
return (
189-
<Dialog open={open} onOpenChange={setOpen}>
190-
<DialogTrigger asChild>
191-
<TooltipButton icon={<Link />} tooltipText={t('record.mark.type.link') || '链接'} />
192-
</DialogTrigger>
193-
<DialogContent className="min-w-full md:min-w-[500px]">
194-
<DialogHeader>
195-
<DialogTitle>{t('record.mark.link.title') || '链接记录'}</DialogTitle>
196-
<DialogDescription>
197-
{t('record.mark.link.description') || '输入网页链接,系统将自动爬取页面内容并保存'}
198-
</DialogDescription>
199-
</DialogHeader>
200-
<Input
201-
placeholder="https://example.com"
202-
value={url}
203-
onChange={(e) => setUrl(e.target.value)}
204-
disabled={loading}
205-
/>
206-
<DialogFooter className="flex items-center justify-between">
207-
<p className="text-sm text-zinc-500 mr-4">
208-
{loading ? '正在爬取页面内容...' : ''}
209-
</p>
210-
<Button
211-
type="submit"
212-
onClick={handleSuccess}
213-
disabled={!url || loading}
214-
>
215-
{loading ? '处理中...' : (t('record.mark.link.save') || '保存')}
216-
</Button>
217-
</DialogFooter>
218-
</DialogContent>
219-
</Dialog>
201+
<>
202+
{isMobile ? (
203+
<Drawer open={open} onOpenChange={setOpen}>
204+
<DrawerTrigger asChild>
205+
<TooltipButton icon={<Link />} tooltipText={t('record.mark.type.link') || '链接'} />
206+
</DrawerTrigger>
207+
<DrawerContent>
208+
<DrawerHeader>
209+
<DrawerTitle>{t('record.mark.link.title') || '链接记录'}</DrawerTitle>
210+
<DrawerDescription>
211+
{t('record.mark.link.description') || '输入网页链接,系统将自动爬取页面内容并保存'}
212+
</DrawerDescription>
213+
</DrawerHeader>
214+
<div className="px-4">
215+
<Input
216+
placeholder="https://example.com"
217+
value={url}
218+
onChange={(e) => setUrl(e.target.value)}
219+
disabled={loading}
220+
/>
221+
</div>
222+
<DrawerFooter className="flex items-center justify-between">
223+
<p className="text-sm text-zinc-500 mr-4">
224+
{loading ? '正在爬取页面内容...' : ''}
225+
</p>
226+
<Button
227+
type="submit"
228+
onClick={handleSuccess}
229+
disabled={!url || loading}
230+
>
231+
{loading ? '处理中...' : (t('record.mark.link.save') || '保存')}
232+
</Button>
233+
</DrawerFooter>
234+
</DrawerContent>
235+
</Drawer>
236+
) : (
237+
<Dialog open={open} onOpenChange={setOpen}>
238+
<DialogTrigger asChild>
239+
<TooltipButton icon={<Link />} tooltipText={t('record.mark.type.link') || '链接'} />
240+
</DialogTrigger>
241+
<DialogContent className="min-w-full md:min-w-[500px]">
242+
<DialogHeader>
243+
<DialogTitle>{t('record.mark.link.title') || '链接记录'}</DialogTitle>
244+
<DialogDescription>
245+
{t('record.mark.link.description') || '输入网页链接,系统将自动爬取页面内容并保存'}
246+
</DialogDescription>
247+
</DialogHeader>
248+
<Input
249+
placeholder="https://example.com"
250+
value={url}
251+
onChange={(e) => setUrl(e.target.value)}
252+
disabled={loading}
253+
/>
254+
<DialogFooter className="flex items-center justify-between">
255+
<p className="text-sm text-zinc-500 mr-4">
256+
{loading ? '正在爬取页面内容...' : ''}
257+
</p>
258+
<Button
259+
type="submit"
260+
onClick={handleSuccess}
261+
disabled={!url || loading}
262+
>
263+
{loading ? '处理中...' : (t('record.mark.link.save') || '保存')}
264+
</Button>
265+
</DialogFooter>
266+
</DialogContent>
267+
</Dialog>
268+
)}
269+
</>
220270
)
221271
}

src/app/core/record/mark/control-text.tsx

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ import {
1010
DialogTitle,
1111
DialogTrigger,
1212
} from "@/components/ui/dialog"
13+
import {
14+
Drawer,
15+
DrawerContent,
16+
DrawerDescription,
17+
DrawerFooter,
18+
DrawerHeader,
19+
DrawerTitle,
20+
DrawerTrigger,
21+
} from "@/components/ui/drawer"
1322
import { Textarea } from "@/components/ui/textarea"
1423
import { insertMark } from "@/db/marks"
1524
import useMarkStore from "@/stores/mark"
@@ -19,12 +28,15 @@ import { useEffect, useState, useCallback } from "react"
1928
import emitter from "@/lib/emitter"
2029
import { useRouter } from 'next/navigation'
2130
import { handleRecordComplete } from '@/lib/record-navigation'
31+
import { useIsMobile } from '@/hooks/use-mobile'
32+
import { isMobileDevice as checkIsMobileDevice } from '@/lib/check'
2233

2334
export function ControlText() {
2435
const t = useTranslations();
2536
const router = useRouter();
2637
const [open, setOpen] = useState(false);
2738
const [text, setText] = useState('')
39+
const isMobile = useIsMobile() || checkIsMobileDevice()
2840

2941
const { currentTagId, fetchTags, getCurrentTag } = useTagStore()
3042
const { fetchMarks } = useMarkStore()
@@ -57,24 +69,49 @@ export function ControlText() {
5769
}, [handleOpen])
5870

5971
return (
60-
<Dialog open={open} onOpenChange={setOpen}>
61-
<DialogTrigger asChild>
62-
<TooltipButton icon={<CopySlash />} tooltipText={t('record.mark.type.text')} />
63-
</DialogTrigger>
64-
<DialogContent className="min-w-full md:min-w-[650px]">
65-
<DialogHeader>
66-
<DialogTitle>{t('record.mark.text.title')}</DialogTitle>
67-
<DialogDescription>
68-
{t('record.mark.text.description')}
69-
</DialogDescription>
70-
</DialogHeader>
71-
<Textarea id="username" rows={10} defaultValue={text} onChange={(e) => setText(e.target.value)} />
72-
<DialogFooter className="flex items-center justify-between">
73-
<p className="text-sm text-zinc-500 mr-4">{t('record.mark.text.characterCount', { count: text.length })}</p>
74-
<Button type="submit" onClick={handleSuccess}>{t('record.mark.text.save')}</Button>
75-
</DialogFooter>
76-
</DialogContent>
77-
</Dialog>
72+
<>
73+
{isMobile ? (
74+
<Drawer open={open} onOpenChange={setOpen}>
75+
<DrawerTrigger asChild>
76+
<TooltipButton icon={<CopySlash />} tooltipText={t('record.mark.type.text')} />
77+
</DrawerTrigger>
78+
<DrawerContent>
79+
<DrawerHeader>
80+
<DrawerTitle>{t('record.mark.text.title')}</DrawerTitle>
81+
<DrawerDescription>
82+
{t('record.mark.text.description')}
83+
</DrawerDescription>
84+
</DrawerHeader>
85+
<div className="px-4">
86+
<Textarea id="username" rows={10} defaultValue={text} onChange={(e) => setText(e.target.value)} />
87+
</div>
88+
<DrawerFooter className="flex items-center justify-between">
89+
<p className="text-sm text-zinc-500 mr-4">{t('record.mark.text.characterCount', { count: text.length })}</p>
90+
<Button type="submit" onClick={handleSuccess}>{t('record.mark.text.save')}</Button>
91+
</DrawerFooter>
92+
</DrawerContent>
93+
</Drawer>
94+
) : (
95+
<Dialog open={open} onOpenChange={setOpen}>
96+
<DialogTrigger asChild>
97+
<TooltipButton icon={<CopySlash />} tooltipText={t('record.mark.type.text')} />
98+
</DialogTrigger>
99+
<DialogContent className="min-w-full md:min-w-[650px]">
100+
<DialogHeader>
101+
<DialogTitle>{t('record.mark.text.title')}</DialogTitle>
102+
<DialogDescription>
103+
{t('record.mark.text.description')}
104+
</DialogDescription>
105+
</DialogHeader>
106+
<Textarea id="username" rows={10} defaultValue={text} onChange={(e) => setText(e.target.value)} />
107+
<DialogFooter className="flex items-center justify-between">
108+
<p className="text-sm text-zinc-500 mr-4">{t('record.mark.text.characterCount', { count: text.length })}</p>
109+
<Button type="submit" onClick={handleSuccess}>{t('record.mark.text.save')}</Button>
110+
</DialogFooter>
111+
</DialogContent>
112+
</Dialog>
113+
)}
114+
</>
78115
)
79116
}
80117

src/app/core/setting/components/model-select.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ export function ModelSelect({modelKey}: {modelKey: string}) {
247247
variant="outline"
248248
role="combobox"
249249
aria-expanded={open}
250-
className="w-full md:w-[280px] justify-between"
250+
className="w-[280px] justify-between"
251251
>
252252
{model
253253
? findSelectedModelDisplay()

src/app/core/setting/dev/setting-dev.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export function SettingDev({id, icon}: {id: string, icon?: React.ReactNode}) {
7373
return (
7474
<SettingType id={id} icon={icon} title={t('settings.dev.title')} desc={t('settings.dev.desc')}>
7575
<ItemGroup className="gap-4">
76-
<Item variant="outline">
76+
<Item variant="outline" className="max-md:flex-col max-md:items-start">
7777
<ItemMedia variant="icon"><Network className="size-4" /></ItemMedia>
7878
<ItemContent>
7979
<ItemTitle>{t('settings.dev.proxyTitle')}</ItemTitle>

src/app/core/setting/imageMethod/vlm.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function VlmSetting() {
1414
<ItemTitle>{t('title')}</ItemTitle>
1515
<ItemDescription>{t('desc')}</ItemDescription>
1616
</ItemContent>
17-
<ItemActions>
17+
<ItemActions className='max-md:w-full'>
1818
<ModelSelect modelKey={'imageMethod'} />
1919
</ItemActions>
2020
</Item>

0 commit comments

Comments
 (0)