初步支持AskUserQuestion交互

This commit is contained in:
xintaofei
2026-03-11 19:43:24 +08:00
parent 56100c6759
commit 79a22c8a03
16 changed files with 273 additions and 2 deletions

View File

@@ -7,9 +7,13 @@ import type {
SessionModeInfo,
AvailableCommandInfo,
} from "@/lib/types"
import type { PendingPermission } from "@/contexts/acp-connections-context"
import type {
PendingPermission,
PendingQuestion,
} from "@/contexts/acp-connections-context"
import { ChatInput } from "@/components/chat/chat-input"
import { PermissionDialog } from "@/components/chat/permission-dialog"
import { QuestionDialog } from "@/components/chat/question-dialog"
interface ConversationShellProps {
status: ConnectionStatus | null
@@ -17,10 +21,12 @@ interface ConversationShellProps {
defaultPath?: string
error: string | null
pendingPermission: PendingPermission | null
pendingQuestion: PendingQuestion | null
onFocus: () => void
onSend: (draft: PromptDraft, modeId?: string | null) => void
onCancel: () => void
onRespondPermission: (requestId: string, optionId: string) => void
onAnswerQuestion: (answer: string) => void
children: ReactNode
modes?: SessionModeInfo[]
configOptions?: SessionConfigOptionInfo[]
@@ -42,10 +48,12 @@ export function ConversationShell({
defaultPath,
error,
pendingPermission,
pendingQuestion,
onFocus,
onSend,
onCancel,
onRespondPermission,
onAnswerQuestion,
children,
modes,
configOptions,
@@ -69,6 +77,8 @@ export function ConversationShell({
onRespond={onRespondPermission}
/>
<QuestionDialog question={pendingQuestion} onAnswer={onAnswerQuestion} />
{!hideInput && (
<ChatInput
status={status}

View File

@@ -0,0 +1,86 @@
"use client"
import { useState, useRef, useEffect, useCallback } from "react"
import { useTranslations } from "next-intl"
import { MessageCircleQuestion, SendHorizonal } from "lucide-react"
import { Button } from "@/components/ui/button"
import type { PendingQuestion } from "@/contexts/acp-connections-context"
interface QuestionDialogProps {
question: PendingQuestion | null
onAnswer: (answer: string) => void
}
export function QuestionDialog({ question, onAnswer }: QuestionDialogProps) {
const t = useTranslations("Folder.chat.questionDialog")
const [answer, setAnswer] = useState("")
const textareaRef = useRef<HTMLTextAreaElement>(null)
const prevQuestionIdRef = useRef<string | null>(null)
const questionId = question?.tool_call_id ?? null
if (questionId !== prevQuestionIdRef.current) {
prevQuestionIdRef.current = questionId
if (questionId && answer !== "") {
setAnswer("")
}
}
useEffect(() => {
if (question) {
textareaRef.current?.focus()
}
}, [question])
const handleSubmit = useCallback(() => {
const trimmed = answer.trim()
if (!trimmed) return
onAnswer(trimmed)
setAnswer("")
}, [answer, onAnswer])
const handleKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault()
handleSubmit()
}
},
[handleSubmit]
)
if (!question) return null
return (
<div className="mx-4 mb-3 rounded-xl border border-blue-500/30 bg-card/95 p-3 shadow-sm">
<div className="flex items-center gap-1.5 text-sm font-medium">
<MessageCircleQuestion className="h-4 w-4 shrink-0 text-blue-500" />
<span>{t("title")}</span>
</div>
<p className="mt-2 text-sm text-foreground/90 whitespace-pre-wrap">
{question.question}
</p>
<div className="mt-3 flex gap-2">
<textarea
ref={textareaRef}
value={answer}
onChange={(e) => setAnswer(e.target.value)}
onKeyDown={handleKeyDown}
placeholder={t("placeholder")}
rows={2}
className="flex-1 resize-none rounded-md border border-border bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
/>
<Button
size="sm"
disabled={!answer.trim()}
onClick={handleSubmit}
className="self-end"
>
<SendHorizonal className="mr-1.5 h-3.5 w-3.5" />
{t("send")}
</Button>
</div>
</div>
)
}