支持在历史会话中分叉出新会话

This commit is contained in:
xintaofei
2026-03-15 11:44:01 +08:00
parent a85ac9dcfe
commit f50484f08c
23 changed files with 503 additions and 31 deletions

View File

@@ -41,6 +41,7 @@ interface ChatInputProps {
isEditingQueueItem?: boolean
onSaveQueueEdit?: (draft: PromptDraft) => void
onCancelQueueEdit?: () => void
onForkSend?: (draft: PromptDraft, modeId?: string | null) => void
}
export function ChatInput({
@@ -71,6 +72,7 @@ export function ChatInput({
isEditingQueueItem,
onSaveQueueEdit,
onCancelQueueEdit,
onForkSend,
}: ChatInputProps) {
const t = useTranslations("Folder.chat.chatInput")
const isConnected = status === "connected"
@@ -116,6 +118,7 @@ export function ChatInput({
isEditingQueueItem={isEditingQueueItem}
onSaveQueueEdit={onSaveQueueEdit}
onCancelQueueEdit={onCancelQueueEdit}
onForkSend={onForkSend}
placeholder={
isConnecting
? t("connecting")

View File

@@ -51,6 +51,7 @@ interface ConversationShellProps {
isEditingQueueItem?: boolean
onSaveQueueEdit?: (draft: PromptDraft) => void
onCancelQueueEdit?: () => void
onForkSend?: (draft: PromptDraft, modeId?: string | null) => void
}
export function ConversationShell({
@@ -88,6 +89,7 @@ export function ConversationShell({
isEditingQueueItem,
onSaveQueueEdit,
onCancelQueueEdit,
onForkSend,
}: ConversationShellProps) {
return (
<div className="flex h-full min-h-0 flex-col">
@@ -129,6 +131,7 @@ export function ConversationShell({
isEditingQueueItem={isEditingQueueItem}
onSaveQueueEdit={onSaveQueueEdit}
onCancelQueueEdit={onCancelQueueEdit}
onForkSend={onForkSend}
/>
)}

View File

@@ -15,14 +15,22 @@ import {
import { Textarea } from "@/components/ui/textarea"
import {
Check,
ChevronUp,
Ellipsis,
FileSearch,
GitFork,
ListPlus,
Plus,
Send,
Square,
X,
} from "lucide-react"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { cn } from "@/lib/utils"
import { matchShortcutEvent } from "@/lib/keyboard-shortcuts"
import { useShortcutSettings } from "@/hooks/use-shortcut-settings"
@@ -76,6 +84,7 @@ interface MessageInputProps {
isEditingQueueItem?: boolean
onSaveQueueEdit?: (draft: PromptDraft) => void
onCancelQueueEdit?: () => void
onForkSend?: (draft: PromptDraft, modeId?: string | null) => void
}
interface ResourceInputAttachment {
@@ -280,6 +289,7 @@ export function MessageInput({
isEditingQueueItem = false,
onSaveQueueEdit,
onCancelQueueEdit,
onForkSend,
}: MessageInputProps) {
const t = useTranslations("Folder.chat.messageInput")
const tQueue = useTranslations("Folder.chat.messageQueue")
@@ -960,6 +970,24 @@ export function MessageInput({
effectiveDraftStorageKey,
])
const handleForkSendClick = useCallback(() => {
if (!onForkSend) return
const draft = buildDraft()
if (!draft) return
onForkSend(draft, showModeSelector ? effectiveModeId : null)
if (effectiveDraftStorageKey) {
clearMessageInputDraft(effectiveDraftStorageKey)
}
setText("")
setAttachments([])
}, [
onForkSend,
buildDraft,
effectiveModeId,
showModeSelector,
effectiveDraftStorageKey,
])
const handleKeyDown = useCallback(
(e: React.KeyboardEvent) => {
if (
@@ -1288,6 +1316,35 @@ export function MessageInput({
<Square className="h-4 w-4" />
</Button>
</div>
) : onForkSend ? (
<div className="absolute right-2 bottom-2 flex items-center">
<Button
onClick={handleSend}
disabled={disabled || !hasSendableContent}
size="icon"
className="rounded-r-none"
title={t("send")}
>
<Send className="h-4 w-4" />
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
disabled={disabled || !hasSendableContent}
size="icon"
className="rounded-l-none border-l border-primary-foreground/20 w-6"
>
<ChevronUp className="h-3 w-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" side="top">
<DropdownMenuItem onSelect={handleForkSendClick}>
<GitFork className="h-4 w-4" />
{t("forkAndSend")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
) : (
<Button
onClick={handleSend}