fix: polish agent voice controls
This commit is contained in:
@@ -610,7 +610,13 @@ export function AgentCommandPanel({
|
||||
status={streaming ? "streaming" : "ready"}
|
||||
onStop={onStopPrompt}
|
||||
>
|
||||
{streaming ? undefined : <SendHorizontal size={15} aria-hidden="true" />}
|
||||
{streaming ? undefined : (
|
||||
<SendHorizontal
|
||||
size={15}
|
||||
style={{ transform: "rotate(-45deg)" }}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</PromptInputSubmit>
|
||||
</div>
|
||||
</PromptInputFooter>
|
||||
@@ -650,13 +656,13 @@ function VoiceInputButton({
|
||||
title={label}
|
||||
className={cn(
|
||||
"group relative grid h-8 w-8 shrink-0 place-items-center overflow-visible rounded-lg border after:absolute after:-inset-1 after:content-['']",
|
||||
"border-transparent bg-transparent text-blue-600 shadow-none",
|
||||
"border-transparent bg-transparent shadow-none",
|
||||
"transition-[color,background-color,border-color,box-shadow,transform] duration-200",
|
||||
"hover:bg-slate-50 hover:text-blue-700",
|
||||
"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-blue-600/25 focus-visible:ring-offset-1",
|
||||
"disabled:cursor-not-allowed disabled:opacity-45",
|
||||
isListening &&
|
||||
"status-tone-danger bg-[var(--status-soft)] text-[var(--status-foreground)] [@media(hover:hover)]:hover:brightness-95"
|
||||
isListening
|
||||
? "status-tone-danger bg-[var(--status-soft)] text-[var(--status-foreground)] [@media(hover:hover)]:hover:border-[var(--status-border)] [@media(hover:hover)]:hover:brightness-95"
|
||||
: "text-slate-600 [@media(hover:hover)]:hover:border-[var(--action-border-hover)] [@media(hover:hover)]:hover:bg-[var(--action-soft)] [@media(hover:hover)]:hover:text-[var(--action-blue-hover)]"
|
||||
)}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
@@ -668,10 +674,19 @@ function VoiceInputButton({
|
||||
aria-hidden="true"
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={
|
||||
reduceMotion ? { opacity: 0.38, scale: 1 } : { opacity: [0.4, 0], scale: [1, 1.28] }
|
||||
reduceMotion
|
||||
? { opacity: 0.38, scale: 1 }
|
||||
: { opacity: [0, 0.4, 0], scale: [0.9, 1, 1.28] }
|
||||
}
|
||||
transition={
|
||||
reduceMotion ? { duration: 0.16 } : { duration: 1.7, ease: "easeOut", repeat: Infinity }
|
||||
reduceMotion
|
||||
? { duration: 0.16 }
|
||||
: {
|
||||
duration: 1.7,
|
||||
ease: "easeOut",
|
||||
repeat: Infinity,
|
||||
times: [0, 0.12, 1]
|
||||
}
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
+1
-1
@@ -928,7 +928,7 @@ button.agent-history-session-trigger:not(:disabled):active {
|
||||
}
|
||||
|
||||
.agent-panel-primary-icon {
|
||||
border: 1px solid var(--action-blue);
|
||||
border: 0;
|
||||
background: var(--action-blue);
|
||||
color: #ffffff;
|
||||
box-shadow: var(--action-shadow-primary);
|
||||
|
||||
@@ -83,3 +83,48 @@ test("speech recognition writes final transcript into the prompt", async ({ page
|
||||
|
||||
await expect(page.getByPlaceholder("输入调度问题,Agent 将通过后端会话流式响应")).toHaveValue("检查城南泵站水位");
|
||||
});
|
||||
|
||||
test("speech recognition pulse stays transparent across its loop boundary", async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
Object.defineProperty(window, "SpeechRecognition", {
|
||||
configurable: true,
|
||||
value: class SpeechRecognition {
|
||||
start() {}
|
||||
|
||||
stop() {}
|
||||
|
||||
abort() {}
|
||||
}
|
||||
});
|
||||
});
|
||||
await page.goto("/", { waitUntil: "domcontentloaded" });
|
||||
|
||||
await page.getByRole("button", { name: "语音输入" }).click();
|
||||
|
||||
const pulse = page.locator('[data-slot="voice-input-pulse"]');
|
||||
await expect(pulse).toBeVisible();
|
||||
const seamOpacity = await pulse.evaluate((element) => {
|
||||
const animation = element
|
||||
.getAnimations()
|
||||
.find((candidate) =>
|
||||
(candidate.effect as KeyframeEffect | null)
|
||||
?.getKeyframes()
|
||||
.some((keyframe) => keyframe.opacity !== undefined)
|
||||
);
|
||||
if (!animation?.effect) {
|
||||
throw new Error("Voice input pulse opacity animation was not found");
|
||||
}
|
||||
|
||||
animation.pause();
|
||||
const duration = Number(animation.effect.getTiming().duration);
|
||||
animation.currentTime = duration - 1;
|
||||
const before = Number(window.getComputedStyle(element).opacity);
|
||||
animation.currentTime = duration + 1;
|
||||
const after = Number(window.getComputedStyle(element).opacity);
|
||||
|
||||
return { before, after };
|
||||
});
|
||||
|
||||
expect(seamOpacity.before).toBeLessThan(0.02);
|
||||
expect(seamOpacity.after).toBeLessThan(0.02);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user