diff --git a/frontend/src/components/workspace/messages/message-list-item.tsx b/frontend/src/components/workspace/messages/message-list-item.tsx
index 1a0c2f008..05bb4c0f5 100644
--- a/frontend/src/components/workspace/messages/message-list-item.tsx
+++ b/frontend/src/components/workspace/messages/message-list-item.tsx
@@ -165,10 +165,19 @@ export function MessageListItem({
}) {
const { t } = useI18n();
const isHuman = message.type === "human";
- const editableText = useMemo(
- () => (isHuman ? (getMessageCopyData(message) ?? "") : ""),
- [isHuman, message],
+ // One derivation serves both editing and the toolbar, and only runs when
+ // either consumer can use it: assistant rows never render this toolbar
+ // (the sole call site passes showCopyButton only for non-assistant rows)
+ // and the toolbar stays unrendered while loading — matching the guard the
+ // pre-memo call sat behind instead of deriving for every settled row.
+ const copyData = useMemo(
+ () =>
+ isHuman || (!isLoading && showCopyButton)
+ ? (getMessageCopyData(message) ?? "")
+ : "",
+ [isHuman, isLoading, showCopyButton, message],
);
+ const editableText = isHuman ? copyData : "";
const [isEditing, setIsEditing] = useState(false);
const [draft, setDraft] = useState("");
const [isSubmittingEdit, setIsSubmittingEdit] = useState(false);
@@ -239,7 +248,7 @@ export function MessageListItem({
)}
>
-
+
{canEdit && isHuman && onEditAndRegenerate && !isEditing && (