diff --git a/apps/desktop/src/ui/Rows.tsx b/apps/desktop/src/ui/Rows.tsx
index ecf6f511b..6330e8011 100644
--- a/apps/desktop/src/ui/Rows.tsx
+++ b/apps/desktop/src/ui/Rows.tsx
@@ -100,15 +100,28 @@ function messageMarkdown(text: string): string {
return slash ? `\`${COMMAND_MARK}${slash.command}\` ${slash.rest}` : text;
}
+/**
+ * A message's Edit and Retry: shown on hover with Copy, and dimmed rather than removed while a
+ * turn runs (`aria-disabled` keeps the tooltip that says why).
+ */
+const MESSAGE_ACTION =
+ "inline-flex size-[22px] items-center justify-center rounded-md text-fg-muted opacity-0 transition-opacity hover:bg-fill hover:text-fg focus-visible:opacity-100 group-hover:opacity-100 aria-disabled:cursor-default aria-disabled:text-fg-faint aria-disabled:hover:bg-transparent aria-disabled:hover:text-fg-faint";
+
export const UserRow = memo(function UserRow({
item,
threadId,
+ busy,
rewind,
before,
after,
}: {
item: Item<"user">;
threadId?: string;
+ /**
+ * A turn runs: Edit and Retry keep their place but are off until it ends, so the message's
+ * actions are the same three whether the thread runs or not.
+ */
+ busy?: boolean;
/** Edit and Retry take the thread back to this message (`_rustcode/rewind`). */
rewind?: boolean;
/** The message after this one is the sender's too. */
@@ -258,24 +271,34 @@ export const UserRow = memo(function UserRow({
<>
-
+
diff --git a/apps/desktop/src/ui/Transcript.tsx b/apps/desktop/src/ui/Transcript.tsx
index 26d4bc8bd..09c9b8dc5 100644
--- a/apps/desktop/src/ui/Transcript.tsx
+++ b/apps/desktop/src/ui/Transcript.tsx
@@ -1414,7 +1414,8 @@ export function Transcript({