优化会话输入框在当前会话宽度不足时的显示样式
This commit is contained in:
@@ -7,8 +7,9 @@ import { open } from "@tauri-apps/plugin-dialog"
|
|||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import { useTranslations } from "next-intl"
|
import { useTranslations } from "next-intl"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
|
||||||
import { Textarea } from "@/components/ui/textarea"
|
import { Textarea } from "@/components/ui/textarea"
|
||||||
import { FileSearch, Plus, Send, Square, X } from "lucide-react"
|
import { Ellipsis, FileSearch, Plus, Send, Square, X } from "lucide-react"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { readFileBase64 } from "@/lib/tauri"
|
import { readFileBase64 } from "@/lib/tauri"
|
||||||
import { disposeTauriListener } from "@/lib/tauri-listener"
|
import { disposeTauriListener } from "@/lib/tauri-listener"
|
||||||
@@ -327,6 +328,8 @@ export function MessageInput({
|
|||||||
hasModes && Boolean(effectiveModeId) && !hasConfigOptions
|
hasModes && Boolean(effectiveModeId) && !hasConfigOptions
|
||||||
const showModeLoading = modeLoading && !hasConfigOptions && !showModeSelector
|
const showModeLoading = modeLoading && !hasConfigOptions && !showModeSelector
|
||||||
const showConfigLoading = configOptionsLoading && !hasConfigOptions
|
const showConfigLoading = configOptionsLoading && !hasConfigOptions
|
||||||
|
const hasAnySelector =
|
||||||
|
showConfigLoading || hasConfigOptions || showModeLoading || showModeSelector
|
||||||
const imageAttachments = useMemo(
|
const imageAttachments = useMemo(
|
||||||
() =>
|
() =>
|
||||||
attachments.filter(
|
attachments.filter(
|
||||||
@@ -996,6 +999,32 @@ export function MessageInput({
|
|||||||
const bottomPaddingClass = "pb-10"
|
const bottomPaddingClass = "pb-10"
|
||||||
const showDragActive = isDragActive && !disabled && !isPrompting
|
const showDragActive = isDragActive && !disabled && !isPrompting
|
||||||
|
|
||||||
|
const selectorItems = (
|
||||||
|
<>
|
||||||
|
{showConfigLoading && (
|
||||||
|
<SelectorLoadingChip label={t("loadingSettings")} />
|
||||||
|
)}
|
||||||
|
{hasConfigOptions &&
|
||||||
|
availableConfigOptions.map((option) => (
|
||||||
|
<SessionConfigSelector
|
||||||
|
key={option.id}
|
||||||
|
option={option}
|
||||||
|
onSelect={(configId, valueId) =>
|
||||||
|
onConfigOptionChange?.(configId, valueId)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{showModeLoading && <SelectorLoadingChip label={t("loadingMode")} />}
|
||||||
|
{showModeSelector && (
|
||||||
|
<ModeSelector
|
||||||
|
modes={availableModes}
|
||||||
|
selectedModeId={effectiveModeId!}
|
||||||
|
onSelect={handleModeSelect}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
@@ -1091,8 +1120,8 @@ export function MessageInput({
|
|||||||
{t("dropFilesToAttach")}
|
{t("dropFilesToAttach")}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="absolute left-2 right-24 bottom-2 flex flex-col gap-1">
|
<div className="@container absolute left-2 right-24 bottom-2">
|
||||||
<div className="flex items-center gap-1 overflow-x-auto">
|
<div className="flex items-center gap-1">
|
||||||
<Button
|
<Button
|
||||||
onClick={handlePickFiles}
|
onClick={handlePickFiles}
|
||||||
disabled={disabled || isPrompting}
|
disabled={disabled || isPrompting}
|
||||||
@@ -1103,26 +1132,29 @@ export function MessageInput({
|
|||||||
>
|
>
|
||||||
<Plus className="size-4" />
|
<Plus className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
{showConfigLoading && (
|
{/* 宽屏内联显示,窄屏(<300px)通过"更多"气泡显示 */}
|
||||||
<SelectorLoadingChip label={t("loadingSettings")} />
|
<div className="hidden @[300px]:contents">
|
||||||
)}
|
{selectorItems}
|
||||||
{hasConfigOptions &&
|
</div>
|
||||||
availableConfigOptions.map((option) => (
|
{hasAnySelector && (
|
||||||
<SessionConfigSelector
|
<Popover>
|
||||||
key={option.id}
|
<PopoverTrigger asChild>
|
||||||
option={option}
|
<Button
|
||||||
onSelect={(configId, valueId) =>
|
variant="ghost"
|
||||||
onConfigOptionChange?.(configId, valueId)
|
size="icon"
|
||||||
}
|
className="h-6 w-6 shrink-0 @[300px]:hidden"
|
||||||
/>
|
>
|
||||||
))}
|
<Ellipsis className="size-4" />
|
||||||
{showModeLoading && <SelectorLoadingChip label={t("loadingMode")} />}
|
</Button>
|
||||||
{showModeSelector && effectiveModeId && (
|
</PopoverTrigger>
|
||||||
<ModeSelector
|
<PopoverContent
|
||||||
modes={availableModes}
|
side="top"
|
||||||
selectedModeId={effectiveModeId}
|
align="start"
|
||||||
onSelect={handleModeSelect}
|
className="flex w-auto flex-col gap-1 rounded-xl p-1"
|
||||||
/>
|
>
|
||||||
|
{selectorItems}
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -34,11 +34,11 @@ export function ModeSelector({
|
|||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="xs"
|
size="xs"
|
||||||
className={cn("gap-1", isActive && "text-primary")}
|
className={cn("gap-1 min-w-0", isActive && "text-primary")}
|
||||||
title={selectedMode?.description ?? selectedMode?.name}
|
title={selectedMode?.description ?? selectedMode?.name}
|
||||||
>
|
>
|
||||||
{label}
|
<span className="truncate">{label}</span>
|
||||||
<ChevronUp className="size-3" />
|
<ChevronUp className="size-3 shrink-0" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent side="top" align="start" className="min-w-72">
|
<DropdownMenuContent side="top" align="start" className="min-w-72">
|
||||||
|
|||||||
@@ -39,11 +39,11 @@ export function SessionConfigSelector({
|
|||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="xs"
|
size="xs"
|
||||||
className={cn("gap-1 shrink-0", isActive && "text-primary")}
|
className={cn("gap-1 min-w-0", isActive && "text-primary")}
|
||||||
title={option.description ?? option.name}
|
title={option.description ?? option.name}
|
||||||
>
|
>
|
||||||
{label}
|
<span className="truncate">{label}</span>
|
||||||
<ChevronUp className="size-3" />
|
<ChevronUp className="size-3 shrink-0" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent side="top" align="start" className="min-w-72">
|
<DropdownMenuContent side="top" align="start" className="min-w-72">
|
||||||
|
|||||||
Reference in New Issue
Block a user