diff --git a/src/components/chat/chat-input.tsx b/src/components/chat/chat-input.tsx index 9fac5bb..f5be27d 100644 --- a/src/components/chat/chat-input.tsx +++ b/src/components/chat/chat-input.tsx @@ -1,5 +1,6 @@ "use client" +import { memo } from "react" import { useTranslations } from "next-intl" import type { AgentType, @@ -48,7 +49,7 @@ interface ChatInputProps { onForkSend?: (draft: PromptDraft, modeId?: string | null) => void } -export function ChatInput({ +export const ChatInput = memo(function ChatInput({ status, promptCapabilities, defaultPath, @@ -138,4 +139,6 @@ export function ChatInput({ /> ) -} +}) + +ChatInput.displayName = "ChatInput" diff --git a/src/components/chat/conversation-shell.tsx b/src/components/chat/conversation-shell.tsx index 50e0a62..78ba43a 100644 --- a/src/components/chat/conversation-shell.tsx +++ b/src/components/chat/conversation-shell.tsx @@ -1,4 +1,4 @@ -import type { ReactNode } from "react" +import { useMemo, type ReactNode } from "react" import { useTranslations } from "next-intl" import type { AgentType, @@ -104,50 +104,48 @@ export function ConversationShell({ onForkSend, }: ConversationShellProps) { const tAcp = useTranslations("Folder.chat.acpConnections") - const retry = claudeApiRetry - const retryAttemptRaw = retry?.attempt - const retryMaxRaw = retry?.maxRetries - const retryDelayMsRaw = retry?.retryDelayMs - const retryErrorStatusRaw = retry?.errorStatus + const retryLineText = useMemo(() => { + const retry = claudeApiRetry + if (!retry) return null - const retryAttempt = - retryAttemptRaw !== null && retryAttemptRaw !== undefined - ? Math.trunc(retryAttemptRaw) - : null - const retryMax = - retryMaxRaw !== null && retryMaxRaw !== undefined - ? Math.trunc(retryMaxRaw) - : null - const retryDelaySeconds = - retryDelayMsRaw !== null && retryDelayMsRaw !== undefined - ? (retryDelayMsRaw / 1000).toFixed(1) - : null - const errorLabel = retry?.error ?? tAcp("claudeApiRetry.fallbackError") - const statusLabel = - retryErrorStatusRaw !== null && retryErrorStatusRaw !== undefined - ? tAcp("claudeApiRetry.httpStatus", { - status: Math.trunc(retryErrorStatusRaw), - }) - : "" - const retryLabel = - retryAttempt !== null && retryMax !== null - ? tAcp("claudeApiRetry.retryingWithMax", { - attempt: retryAttempt, - max: retryMax, - }) - : retryAttempt !== null - ? tAcp("claudeApiRetry.retryingAttempt", { - attempt: retryAttempt, + const retryAttempt = + retry.attempt !== null && retry.attempt !== undefined + ? Math.trunc(retry.attempt) + : null + const retryMax = + retry.maxRetries !== null && retry.maxRetries !== undefined + ? Math.trunc(retry.maxRetries) + : null + const retryDelaySeconds = + retry.retryDelayMs !== null && retry.retryDelayMs !== undefined + ? (retry.retryDelayMs / 1000).toFixed(1) + : null + const errorLabel = retry.error ?? tAcp("claudeApiRetry.fallbackError") + const statusLabel = + retry.errorStatus !== null && retry.errorStatus !== undefined + ? tAcp("claudeApiRetry.httpStatus", { + status: Math.trunc(retry.errorStatus), }) - : tAcp("claudeApiRetry.retrying") - const delayLabel = - retryDelaySeconds !== null - ? tAcp("claudeApiRetry.nextRetryIn", { - seconds: retryDelaySeconds, - }) - : null - const retryLineText = - delayLabel !== null + : "" + const retryLabel = + retryAttempt !== null && retryMax !== null + ? tAcp("claudeApiRetry.retryingWithMax", { + attempt: retryAttempt, + max: retryMax, + }) + : retryAttempt !== null + ? tAcp("claudeApiRetry.retryingAttempt", { + attempt: retryAttempt, + }) + : tAcp("claudeApiRetry.retrying") + const delayLabel = + retryDelaySeconds !== null + ? tAcp("claudeApiRetry.nextRetryIn", { + seconds: retryDelaySeconds, + }) + : null + + return delayLabel !== null ? tAcp("claudeApiRetry.lineWithDelay", { error: errorLabel, status: statusLabel, @@ -159,6 +157,7 @@ export function ConversationShell({ status: statusLabel, retry: retryLabel, }) + }, [claudeApiRetry, tAcp]) return (