From b485d8fc21181226995173644597f21bdeb0ca40 Mon Sep 17 00:00:00 2001 From: zahlekhan Date: Fri, 31 Jul 2026 16:14:34 +0530 Subject: [PATCH 1/2] fix: auto-submit prompt template completions --- .../AgentInterface/ConversationStarter.tsx | 2 +- .../components/AgentInterface/WelcomeScreen.tsx | 15 +++++---------- .../_shared/utils/welcomePrefill.ts | 13 +++++++++++++ .../components/WelcomePrefillChips.tsx | 2 +- .../stories/AgentInterface.stories.tsx | 2 +- packages/react-ui/src/types/PromptTemplate.ts | 6 +++--- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/packages/react-ui/src/components/AgentInterface/ConversationStarter.tsx b/packages/react-ui/src/components/AgentInterface/ConversationStarter.tsx index 0c3782b5d..d5576d813 100644 --- a/packages/react-ui/src/components/AgentInterface/ConversationStarter.tsx +++ b/packages/react-ui/src/components/AgentInterface/ConversationStarter.tsx @@ -89,7 +89,7 @@ export interface ConversationStarterContainerProps { /** * Optional click override. When provided, replaces the default * send-to-thread behavior (still guarded by `isRunning`). The prefill-chips - * welcome uses it to append contextual-starter prompts into the draft. + * welcome uses it to submit contextual starters with the prefilled draft. */ onSelect?: (starter: ConversationStarterProps) => void; } diff --git a/packages/react-ui/src/components/AgentInterface/WelcomeScreen.tsx b/packages/react-ui/src/components/AgentInterface/WelcomeScreen.tsx index 40f79ccd9..af413678d 100644 --- a/packages/react-ui/src/components/AgentInterface/WelcomeScreen.tsx +++ b/packages/react-ui/src/components/AgentInterface/WelcomeScreen.tsx @@ -5,7 +5,7 @@ import { ConversationStarterProps } from "../../types/ConversationStarter"; import { PromptTemplate } from "../../types/PromptTemplate"; import { useStartersFromContext } from "./_shared/startersContext"; import { isChatEmpty } from "./_shared/utils"; -import { appendStarterPrompt } from "./_shared/utils/welcomePrefill"; +import { submitStarterPrompt } from "./_shared/utils/welcomePrefill"; import { DesktopWelcomeComposer, WelcomePrefillChips } from "./components"; import { ConversationStarter, ConversationStarterVariant } from "./ConversationStarter"; import { WelcomeGlow, WelcomeGlowProvider } from "./WelcomeGlow"; @@ -48,8 +48,8 @@ interface WelcomeScreenWithContentProps extends WelcomeScreenBaseProps { /** * Fill-in-the-blank prompt templates, rendered as chips between the composer * and the starters. Clicking one drops its prompt stem into the composer - * (instead of sending) and shows the template's completions, which append to - * the draft. + * (instead of sending) and shows the template's completions. Selecting a + * completion sends the completed prompt immediately. */ promptTemplates?: PromptTemplate[]; /** @@ -95,6 +95,7 @@ export const WelcomeScreen = (props: WelcomeScreenProps) => { const messages = useThread((s) => s.messages); const isLoadingMessages = useThread((s) => s.isLoadingMessages); const isRunning = useThread((s) => s.isRunning); + const processMessage = useThread((s) => s.processMessage); // Prefill-chips draft state — owned here (not in the composer) so chips can // write into the draft. Hooks stay unconditional; unused without chips. @@ -167,13 +168,7 @@ export const WelcomeScreen = (props: WelcomeScreenProps) => { }; const handleContextualSelect = (starter: ConversationStarterProps) => { - const next = appendStarterPrompt(draft, starter.prompt); - setDraft(next); - setSelectedChip(null); - requestAnimationFrame(() => { - inputRef.current?.focus(); - inputRef.current?.setSelectionRange(next.length, next.length); - }); + submitStarterPrompt(processMessage, draft, starter.prompt); }; return ( diff --git a/packages/react-ui/src/components/AgentInterface/_shared/utils/welcomePrefill.ts b/packages/react-ui/src/components/AgentInterface/_shared/utils/welcomePrefill.ts index 1468a1fd4..568e39376 100644 --- a/packages/react-ui/src/components/AgentInterface/_shared/utils/welcomePrefill.ts +++ b/packages/react-ui/src/components/AgentInterface/_shared/utils/welcomePrefill.ts @@ -7,3 +7,16 @@ export const appendStarterPrompt = (draft: string, prompt: string): string => { const separator = draft.length > 0 && !draft.endsWith(" ") ? " " : ""; return `${draft}${separator}${prompt}`; }; + +type ProcessStarterMessage = (message: { role: "user"; content: string }) => void; + +/** Composes a contextual starter with the current draft and submits it. */ +export const submitStarterPrompt = ( + processMessage: ProcessStarterMessage, + draft: string, + prompt: string, +): void => + processMessage({ + role: "user", + content: appendStarterPrompt(draft, prompt), + }); diff --git a/packages/react-ui/src/components/AgentInterface/components/WelcomePrefillChips.tsx b/packages/react-ui/src/components/AgentInterface/components/WelcomePrefillChips.tsx index 5ac2f4e91..c00d82a18 100644 --- a/packages/react-ui/src/components/AgentInterface/components/WelcomePrefillChips.tsx +++ b/packages/react-ui/src/components/AgentInterface/components/WelcomePrefillChips.tsx @@ -34,7 +34,7 @@ export interface WelcomePrefillChipsProps { * Chip row + grid-stacked starters layers for the prefill-chips welcome. * Layer 1 (chips + default starters) hides via `visibility` while drafting so * the layout doesn't jump; layer 2 shows the selected chip's contextual - * starters, which append to the draft (see WelcomeScreen). + * starters, which submit the completed prompt (see WelcomeScreen). */ export const WelcomePrefillChips = ({ chips, diff --git a/packages/react-ui/src/components/AgentInterface/stories/AgentInterface.stories.tsx b/packages/react-ui/src/components/AgentInterface/stories/AgentInterface.stories.tsx index d9a27edff..3da4556b5 100644 --- a/packages/react-ui/src/components/AgentInterface/stories/AgentInterface.stories.tsx +++ b/packages/react-ui/src/components/AgentInterface/stories/AgentInterface.stories.tsx @@ -253,7 +253,7 @@ export const WithWelcome = { ), }; -/** Welcome with prefill chips — chip click prefills the composer; contextual starters append. */ +/** Welcome with prompt templates — a chip prefills the composer; a completion submits it. */ export const WelcomeWithPrefillChips = { render: () => ( Date: Tue, 4 Aug 2026 17:30:21 +0530 Subject: [PATCH 2/2] fix: clear prefill draft state after contextual starter auto-submit The auto-submit left draft/selectedChip populated; the thread-switch reset never fires when selectedThreadId stays null (e.g. createThread fails on a fresh chat), so the welcome could reappear with the stale stem and the completions layer open. Clear both at submit time like the composer's own submit path, and cover submitStarterPrompt with unit tests. Co-Authored-By: Claude Fable 5 --- .../AgentInterface/WelcomeScreen.tsx | 6 +++++ .../__tests__/welcomePrefill.test.ts | 24 +++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/react-ui/src/components/AgentInterface/WelcomeScreen.tsx b/packages/react-ui/src/components/AgentInterface/WelcomeScreen.tsx index af413678d..f4a25bcb9 100644 --- a/packages/react-ui/src/components/AgentInterface/WelcomeScreen.tsx +++ b/packages/react-ui/src/components/AgentInterface/WelcomeScreen.tsx @@ -169,6 +169,12 @@ export const WelcomeScreen = (props: WelcomeScreenProps) => { const handleContextualSelect = (starter: ConversationStarterProps) => { submitStarterPrompt(processMessage, draft, starter.prompt); + // Clear at submit time like the composer's own submit path — the + // thread-switch reset above won't fire when selectedThreadId doesn't + // change (e.g. thread creation fails on a fresh chat and it stays null), + // and the stale stem would resurface if the welcome shows again. + setDraft(""); + setSelectedChip(null); }; return ( diff --git a/packages/react-ui/src/components/AgentInterface/__tests__/welcomePrefill.test.ts b/packages/react-ui/src/components/AgentInterface/__tests__/welcomePrefill.test.ts index f7ec6b4f9..a53adeaa5 100644 --- a/packages/react-ui/src/components/AgentInterface/__tests__/welcomePrefill.test.ts +++ b/packages/react-ui/src/components/AgentInterface/__tests__/welcomePrefill.test.ts @@ -1,5 +1,5 @@ -import { describe, expect, it } from "vitest"; -import { appendStarterPrompt } from "../_shared/utils/welcomePrefill"; +import { describe, expect, it, vi } from "vitest"; +import { appendStarterPrompt, submitStarterPrompt } from "../_shared/utils/welcomePrefill"; describe("appendStarterPrompt", () => { it("returns the prompt alone for an empty draft", () => { @@ -18,3 +18,23 @@ describe("appendStarterPrompt", () => { ); }); }); + +describe("submitStarterPrompt", () => { + it("submits the composed draft + prompt as a user message", () => { + const processMessage = vi.fn(); + submitStarterPrompt(processMessage, "Create a presentation about ", "our Q2 business review"); + expect(processMessage).toHaveBeenCalledExactlyOnceWith({ + role: "user", + content: "Create a presentation about our Q2 business review", + }); + }); + + it("submits the prompt alone when the draft is empty", () => { + const processMessage = vi.fn(); + submitStarterPrompt(processMessage, "", "our Q2 business review"); + expect(processMessage).toHaveBeenCalledExactlyOnceWith({ + role: "user", + content: "our Q2 business review", + }); + }); +});