refactor(site): address plan-mode frontend review feedback (#24426)

> This PR was authored by Mux on behalf of Mike.

Address all 8 frontend review comments from DanielleMaywood on #24236.

## Changes

**Low-risk cleanups:**
- Inlined module-level `toolBadgeClassName` in `AgentChatInput.tsx`
- Inlined `planModeInvisibleCharWarning` variable in
`PlanModeInstructionsSettings.tsx`
- Replaced loose `nilUUID` and `clearPlanMode` sentinels in `chats.ts`
with a `toChatPlanModePayload()` helper and inline nil UUID. Page-level
mutation callers now pass `undefined` to clear plan mode instead of
`""`.

**Tool state management:**
- Replaced manual `isSubmitting`/`setIsSubmitting` in
`ProposePlanTool.tsx` with `useMutation`
- Overhauled `AskUserQuestionTool.tsx`: replaced `cloneAnswer` with
`structuredClone`, replaced `setIsSubmitting` with `useMutation`,
extracted `QuestionStep`, `QuestionOption`, `OtherQuestionOption`, and
`AnsweredQuestionText` components

**Timeline complexity reduction:**
- Extracted `deriveMessageDisplayState()` into `messageHelpers.ts`
(pure, testable)
- Extracted user-message rendering into `UserMessageContent.tsx`
- Reduced `ConversationTimeline.tsx` by ~285 lines

## Validation
- TypeScript: pass
- Lint (Biome + ESLint): pass
- React Compiler: 247 functions, 0 diagnostics
- Storybook tests: 42/42 pass (ProposePlanTool 9, ConversationTimeline
22, AskUserQuestionTool 11)
This commit is contained in:
Michael Suchacz
2026-04-16 15:16:29 +02:00
committed by GitHub
parent 4064b602de
commit 6bb44447d4
8 changed files with 701 additions and 501 deletions
+16 -8
View File
@@ -211,7 +211,6 @@ export const cancelChatListRefetches = (queryClient: QueryClient) => {
};
const DEFAULT_CHAT_PAGE_LIMIT = 50;
const nilUUID = "00000000-0000-0000-0000-000000000000";
type UpdateChatWorkspaceVariables = {
chatId: string;
@@ -220,10 +219,17 @@ type UpdateChatWorkspaceVariables = {
type UpdateChatPlanModeVariables = {
chatId: string;
planMode?: ChatPlanModeOrClear;
planMode?: TypesGen.ChatPlanMode;
};
const clearPlanMode = "" satisfies ChatPlanModeOrClear;
const CLEAR_PLAN_MODE_WIRE_VALUE = "" satisfies ChatPlanModeOrClear;
const toChatPlanModePayload = (
planMode: TypesGen.ChatPlanMode | undefined,
): ChatPlanModeOrClear => {
// The API expects an empty string on the wire to clear plan mode.
return planMode ?? CLEAR_PLAN_MODE_WIRE_VALUE;
};
export const infiniteChats = (opts?: { q?: string; archived?: boolean }) => {
const limit = DEFAULT_CHAT_PAGE_LIMIT;
@@ -407,7 +413,7 @@ export const unarchiveChat = (queryClient: QueryClient) => ({
export const updateChatPlanMode = (queryClient: QueryClient) => ({
mutationFn: ({ chatId, planMode }: UpdateChatPlanModeVariables) =>
API.experimental.updateChat(chatId, {
plan_mode: planMode ?? clearPlanMode,
plan_mode: toChatPlanModePayload(planMode),
}),
onMutate: async ({ chatId, planMode }: UpdateChatPlanModeVariables) => {
await queryClient.cancelQueries({
@@ -421,16 +427,15 @@ export const updateChatPlanMode = (queryClient: QueryClient) => ({
const previousChat = queryClient.getQueryData<TypesGen.Chat>(
chatKey(chatId),
);
const nextPlanMode = planMode === clearPlanMode ? undefined : planMode;
updateInfiniteChatsCache(queryClient, (chats) =>
chats.map((chat) =>
chat.id === chatId ? { ...chat, plan_mode: nextPlanMode } : chat,
chat.id === chatId ? { ...chat, plan_mode: planMode } : chat,
),
);
if (previousChat) {
queryClient.setQueryData<TypesGen.Chat>(chatKey(chatId), {
...previousChat,
plan_mode: nextPlanMode,
plan_mode: planMode,
});
}
return { previousChat };
@@ -466,7 +471,10 @@ export const updateChatPlanMode = (queryClient: QueryClient) => ({
export const updateChatWorkspace = (queryClient: QueryClient) => ({
mutationFn: ({ chatId, workspaceId }: UpdateChatWorkspaceVariables) =>
API.experimental.updateChat(chatId, {
workspace_id: workspaceId ?? nilUUID,
workspace_id:
workspaceId ??
// The API uses the nil UUID to clear the workspace association.
"00000000-0000-0000-0000-000000000000",
}),
onMutate: async ({ chatId, workspaceId }: UpdateChatWorkspaceVariables) => {
await queryClient.cancelQueries({