diff --git a/apps/desktop/src/components/assistant-ui/clarify-tool.tsx b/apps/desktop/src/components/assistant-ui/clarify-tool.tsx
index ce72a8179fb..655ff389ec2 100644
--- a/apps/desktop/src/components/assistant-ui/clarify-tool.tsx
+++ b/apps/desktop/src/components/assistant-ui/clarify-tool.tsx
@@ -2,7 +2,7 @@
import { type ToolCallMessagePartProps } from '@assistant-ui/react'
import { useStore } from '@nanostores/react'
-import { type FormEvent, type KeyboardEvent, useCallback, useMemo, useRef, useState } from 'react'
+import { type FormEvent, type KeyboardEvent, useCallback, useMemo, useRef, useState, type ComponentProps } from 'react'
import { ToolFallback } from '@/components/assistant-ui/tool-fallback'
import { Button } from '@/components/ui/button'
@@ -36,14 +36,30 @@ function readClarifyArgs(args: unknown): ClarifyArgs {
}
// Choice and "Other" rows share a layout; only color/hover differs.
-const OPTION_ROW_CLASS = 'flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm transition-colors'
+const OPTION_ROW_CLASS = 'flex w-full items-start gap-2 rounded-md px-2.5 py-1.5 text-left text-sm transition-colors'
+
+const CLARIFY_SHELL_CLASS =
+ 'relative mb-3 mt-2 rounded-[0.5rem] border border-border/70 bg-card/40 text-sm shadow-[inset_0_1px_0_color-mix(in_srgb,var(--foreground)_3%,transparent)]'
+
+function ClarifyShell({
+ children,
+ className,
+ ...props
+}: ComponentProps<'div'>) {
+ return (
+
+
+ {children}
+
+ )
+}
function RadioDot({ selected }: { selected: boolean }) {
return (
@@ -99,9 +115,11 @@ function ClarifyToolPending({ args }: ToolCallMessagePartProps) {
const textareaRef = useRef(null)
// Race: tool.start fires a tick before clarify.request, so request_id
- // arrives slightly after the tool block mounts. Show the question (from
- // args) but disable submit until we have the request id from the gateway.
+ // arrives slightly after the tool block mounts. Hold the whole panel on a
+ // spinner until the gateway request is wired — showing disabled choices or
+ // a "loading question" stub is worse than a brief wait.
const ready = Boolean(matchingRequest?.requestId)
+ const loading = !ready && !submitting
const respond = useCallback(
async (answer: string) => {
@@ -138,7 +156,11 @@ function ClarifyToolPending({ args }: ToolCallMessagePartProps) {
const handleTextareaKey = useCallback(
(event: KeyboardEvent) => {
- if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {
+ if (event.nativeEvent.isComposing) {
+ return
+ }
+
+ if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault()
const trimmed = draft.trim()
@@ -162,12 +184,20 @@ function ClarifyToolPending({ args }: ToolCallMessagePartProps) {
[draft, respond]
)
+ if (loading) {
+ return (
+
+
+
+ )
+ }
+
return (
-
-
+
-
- {question || {copy.loadingQuestion}}
-
+ {question}
{!typing && hasChoices && (
@@ -190,7 +218,7 @@ function ClarifyToolPending({ args }: ToolCallMessagePartProps) {
selectedChoice === choice && 'bg-accent/60'
)}
data-choice
- disabled={!ready || submitting}
+ disabled={submitting}
key={`${index}-${choice}`}
onClick={() => {
setSelectedChoice(choice)
@@ -200,7 +228,7 @@ function ClarifyToolPending({ args }: ToolCallMessagePartProps) {
>
{choice}
- {selectedChoice === choice && }
+ {selectedChoice === choice && }
))}
-
- {copy.shortcutSuffix}
+
+
+ {t.composer.hotkeyDescs['composer.sendNewline']}
{hasChoices && (
@@ -249,16 +278,10 @@ function ClarifyToolPending({ args }: ToolCallMessagePartProps) {
{copy.back}
)}
-
@@ -270,7 +293,7 @@ function ClarifyToolPending({ args }: ToolCallMessagePartProps) {
void respond('')}
size="xs"
type="button"
@@ -280,6 +303,6 @@ function ClarifyToolPending({ args }: ToolCallMessagePartProps) {
)}
-
+
)
}