Reduce workspace render churn
This commit is contained in:
@@ -22,7 +22,12 @@ import type { UserComposerAttachment } from "@/attachments/types";
|
||||
import { RewindComposerRestoreProvider } from "@/components/rewind/composer-restore";
|
||||
import type { ImageAttachment } from "@/composer/types";
|
||||
import { getProviderIcon } from "@/components/provider-icons";
|
||||
import { ToastViewport, useToastHost } from "@/components/toast-host";
|
||||
import {
|
||||
ToastViewport,
|
||||
useToastHost,
|
||||
type ToastApi,
|
||||
type ToastState,
|
||||
} from "@/components/toast-host";
|
||||
import type { WorkspaceComposerAttachment } from "@/attachments/types";
|
||||
import {
|
||||
useWorkspaceAttachments,
|
||||
@@ -682,7 +687,7 @@ function ChatAgentContent({
|
||||
onOpenWorkspaceFile?: (request: WorkspaceFileOpenRequest) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const panelToast = useToastHost();
|
||||
const { api: toastApi, toast: toastState, dismiss: dismissToast } = useToastHost();
|
||||
const { isArchivingAgent } = useArchiveAgent();
|
||||
const streamViewRef = useRef<AgentStreamViewHandle>(null);
|
||||
const addImagesRef = useRef<((images: ImageAttachment[]) => void) | null>(null);
|
||||
@@ -737,10 +742,10 @@ function ChatAgentContent({
|
||||
addFilesRef.current?.(uploaded);
|
||||
} catch (error) {
|
||||
console.error("[AgentPanel] Failed to upload dropped files:", error);
|
||||
panelToast.api.error(error instanceof Error ? error.message : "Failed to upload file");
|
||||
toastApi.error(error instanceof Error ? error.message : "Failed to upload file");
|
||||
}
|
||||
},
|
||||
[client, isConnected, panelToast.api],
|
||||
[client, isConnected, toastApi],
|
||||
);
|
||||
|
||||
const agentState = useSessionStore(
|
||||
@@ -841,7 +846,7 @@ function ChatAgentContent({
|
||||
if (connectionStatus === "online") {
|
||||
if (reconnectToastArmedRef.current) {
|
||||
reconnectToastArmedRef.current = false;
|
||||
panelToast.dismiss();
|
||||
dismissToast();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -850,12 +855,12 @@ function ChatAgentContent({
|
||||
}
|
||||
if (!reconnectToastArmedRef.current) {
|
||||
reconnectToastArmedRef.current = true;
|
||||
panelToast.api.show(t("agentPanel.states.reconnecting"), {
|
||||
toastApi.show(t("agentPanel.states.reconnecting"), {
|
||||
durationMs: null,
|
||||
testID: "agent-reconnecting-toast",
|
||||
});
|
||||
}
|
||||
}, [connectionStatus, panelToast, t]);
|
||||
}, [connectionStatus, dismissToast, toastApi, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPaneFocused || !agentId || !isConnected || !hasSession) {
|
||||
@@ -1089,7 +1094,9 @@ function ChatAgentContent({
|
||||
effectiveAgent={effectiveAgent}
|
||||
routeBottomAnchorRequest={routeBottomAnchorRequest}
|
||||
hasAppliedAuthoritativeHistory={hasAppliedAuthoritativeHistory}
|
||||
panelToast={panelToast}
|
||||
toastApi={toastApi}
|
||||
toast={toastState}
|
||||
dismiss={dismissToast}
|
||||
streamViewRef={streamViewRef}
|
||||
animatedContentStyle={animatedContentStyle}
|
||||
handleFilesDropped={handleFilesDropped}
|
||||
@@ -1100,7 +1107,8 @@ function ChatAgentContent({
|
||||
handleMessageSent={handleMessageSent}
|
||||
showHistorySyncOverlay={showHistorySyncOverlay}
|
||||
cwd={agentCwd}
|
||||
attentionController={attentionController}
|
||||
onAttentionInputFocus={attentionController.clearOnInputFocus}
|
||||
onAttentionPromptSend={attentionController.clearOnPromptSend}
|
||||
onOpenWorkspaceFile={onOpenWorkspaceFile}
|
||||
/>
|
||||
);
|
||||
@@ -1124,7 +1132,8 @@ function isImagePath(path: string): boolean {
|
||||
]);
|
||||
return imageExts.has(ext);
|
||||
}
|
||||
function ChatAgentReadyContent({
|
||||
|
||||
const ChatAgentReadyContent = memo(function ChatAgentReadyContent({
|
||||
serverId,
|
||||
agentId,
|
||||
isPaneFocused,
|
||||
@@ -1133,7 +1142,9 @@ function ChatAgentReadyContent({
|
||||
effectiveAgent,
|
||||
routeBottomAnchorRequest,
|
||||
hasAppliedAuthoritativeHistory,
|
||||
panelToast,
|
||||
toastApi,
|
||||
toast,
|
||||
dismiss,
|
||||
streamViewRef,
|
||||
animatedContentStyle,
|
||||
handleFilesDropped,
|
||||
@@ -1144,7 +1155,8 @@ function ChatAgentReadyContent({
|
||||
handleMessageSent,
|
||||
showHistorySyncOverlay,
|
||||
cwd,
|
||||
attentionController,
|
||||
onAttentionInputFocus,
|
||||
onAttentionPromptSend,
|
||||
onOpenWorkspaceFile,
|
||||
}: {
|
||||
serverId: string;
|
||||
@@ -1155,7 +1167,9 @@ function ChatAgentReadyContent({
|
||||
effectiveAgent: AgentScreenAgent;
|
||||
routeBottomAnchorRequest: RouteBottomAnchorRequest;
|
||||
hasAppliedAuthoritativeHistory: boolean;
|
||||
panelToast: ReturnType<typeof useToastHost>;
|
||||
toastApi: ToastApi;
|
||||
toast: ToastState | null;
|
||||
dismiss: () => void;
|
||||
streamViewRef: React.RefObject<AgentStreamViewHandle | null>;
|
||||
animatedContentStyle: object[];
|
||||
handleFilesDropped: (files: ImageAttachment[]) => void;
|
||||
@@ -1166,16 +1180,33 @@ function ChatAgentReadyContent({
|
||||
handleMessageSent: () => void;
|
||||
showHistorySyncOverlay: boolean;
|
||||
cwd: string;
|
||||
attentionController: ReturnType<typeof useAgentAttentionClear>;
|
||||
onAttentionInputFocus: () => void;
|
||||
onAttentionPromptSend: () => void;
|
||||
onOpenWorkspaceFile?: (request: WorkspaceFileOpenRequest) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const agentInputDraft = useAgentInputDraft({
|
||||
const rawAgentInputDraft = useAgentInputDraft({
|
||||
draftKey: buildDraftStoreKey({
|
||||
serverId,
|
||||
agentId,
|
||||
}),
|
||||
});
|
||||
// Stabilize the agentInputDraft object identity so that memo(AgentComposerSection) can bail out
|
||||
// when only toast state changes (which does not affect any draft field).
|
||||
const { text, setText, attachments, setAttachments, clear, isHydrated, composerState } =
|
||||
rawAgentInputDraft;
|
||||
const agentInputDraft = useMemo(
|
||||
(): AgentInputDraft => ({
|
||||
text,
|
||||
setText,
|
||||
attachments,
|
||||
setAttachments,
|
||||
clear,
|
||||
isHydrated,
|
||||
composerState,
|
||||
}),
|
||||
[text, setText, attachments, setAttachments, clear, isHydrated, composerState],
|
||||
);
|
||||
const streamSection = (
|
||||
<RenderProfile id={`AgentStreamSection:${agentId}`}>
|
||||
<AgentStreamSection
|
||||
@@ -1185,7 +1216,7 @@ function ChatAgentReadyContent({
|
||||
agent={effectiveAgent}
|
||||
routeBottomAnchorRequest={routeBottomAnchorRequest}
|
||||
hasAppliedAuthoritativeHistory={hasAppliedAuthoritativeHistory}
|
||||
toast={panelToast.api}
|
||||
toast={toastApi}
|
||||
onOpenWorkspaceFile={onOpenWorkspaceFile}
|
||||
/>
|
||||
</RenderProfile>
|
||||
@@ -1201,8 +1232,8 @@ function ChatAgentReadyContent({
|
||||
cwd={cwd}
|
||||
isSubmitLoading={false}
|
||||
agentInputDraft={agentInputDraft}
|
||||
onAttentionInputFocus={attentionController.clearOnInputFocus}
|
||||
onAttentionPromptSend={attentionController.clearOnPromptSend}
|
||||
onAttentionInputFocus={onAttentionInputFocus}
|
||||
onAttentionPromptSend={onAttentionPromptSend}
|
||||
onAddImages={handleAddImagesCallback}
|
||||
onAddFiles={handleAddFilesCallback}
|
||||
onComposerHeightChange={handleComposerHeightChange}
|
||||
@@ -1234,11 +1265,7 @@ function ChatAgentReadyContent({
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
<ToastViewport
|
||||
toast={panelToast.toast}
|
||||
onDismiss={panelToast.dismiss}
|
||||
placement="panel"
|
||||
/>
|
||||
<ToastViewport toast={toast} onDismiss={dismiss} placement="panel" />
|
||||
</View>
|
||||
</FileDropZone>
|
||||
|
||||
@@ -1252,7 +1279,7 @@ function ChatAgentReadyContent({
|
||||
</View>
|
||||
</RewindComposerRestoreProvider>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const AgentStreamSection = memo(function AgentStreamSection({
|
||||
streamViewRef,
|
||||
@@ -1320,7 +1347,7 @@ const AgentStreamSection = memo(function AgentStreamSection({
|
||||
);
|
||||
});
|
||||
|
||||
function AgentComposerSection({
|
||||
const AgentComposerSection = memo(function AgentComposerSection({
|
||||
agentId,
|
||||
serverId,
|
||||
isPaneFocused,
|
||||
@@ -1377,7 +1404,7 @@ function AgentComposerSection({
|
||||
onMessageSent={onMessageSent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
function ActiveAgentComposer({
|
||||
agentId,
|
||||
|
||||
Reference in New Issue
Block a user