fix(desktop): keep message actions hover-only (#844)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
thomaspblock
2026-06-04 08:07:36 -07:00
committed by GitHub
co-authored by Cursor
parent db46c42546
commit b3be9ecba7
9 changed files with 4 additions and 33 deletions
@@ -91,7 +91,6 @@ type ChannelPaneProps = {
threadMessages: MainTimelineEntry[];
threadPanelWidthPx: number;
threadTypingPubkeys: string[];
threadReplyTargetId: string | null;
threadReplyTargetMessage: TimelineMessage | null;
threadScrollTargetId: string | null;
targetMessageId: string | null;
@@ -160,7 +159,6 @@ export const ChannelPane = React.memo(function ChannelPane({
threadPanelWidthPx,
threadScrollTargetId,
threadTypingPubkeys,
threadReplyTargetId,
threadReplyTargetMessage,
typingPubkeys,
}: ChannelPaneProps) {
@@ -307,7 +305,6 @@ export const ChannelPane = React.memo(function ChannelPane({
) : null}
<MessageTimeline
channelId={activeChannel?.id}
activeReplyTargetId={openThreadHeadId}
scrollContainerRef={timelineScrollRef}
currentPubkey={currentPubkey}
fetchOlder={fetchOlder}
@@ -456,7 +453,6 @@ export const ChannelPane = React.memo(function ChannelPane({
onToggleReaction={onToggleReaction}
onUnfollowThread={onUnfollowThread}
profiles={profiles}
replyTargetId={threadReplyTargetId}
replyTargetMessage={threadReplyTargetMessage}
scrollTargetId={threadScrollTargetId}
canResetWidth={canResetThreadPanelWidth}
@@ -551,7 +551,6 @@ export function ChannelScreen({
threadMessages={threadMessages}
threadPanelWidthPx={threadPanelWidthPx}
threadTypingPubkeys={threadTypingPubkeys}
threadReplyTargetId={threadReplyTargetId}
threadReplyTargetMessage={threadReplyTargetMessage}
threadScrollTargetId={threadScrollTargetId}
isJoining={joinChannelMutation.isPending}
@@ -324,7 +324,6 @@ export function InboxDetailPane({
<div className="mx-6 my-3 border-t border-border/60" />
) : null}
<InboxMessageRow
activeReplyTargetId={replyTargetId}
canReply={canReply}
channelId={item.item.channelId}
isFocusHighlightVisible={isFocusHighlightVisible}
@@ -27,7 +27,6 @@ function toTimelineMessage(message: InboxDisplayMessage): TimelineMessage {
}
type InboxMessageRowProps = {
activeReplyTargetId: string | null;
canReply: boolean;
/** Channel UUID for "Copy link" — passed straight through to MessageActionBar. */
channelId?: string | null;
@@ -42,7 +41,6 @@ type InboxMessageRowProps = {
};
export function InboxMessageRow({
activeReplyTargetId,
canReply,
channelId = null,
isFocusHighlightVisible,
@@ -89,7 +87,6 @@ export function InboxMessageRow({
{canReply || canToggleReactions ? (
<div className="absolute right-2 top-1 z-10">
<MessageActionBar
activeReplyTargetId={activeReplyTargetId}
channelId={channelId}
message={timelineMessage}
onReactionSelect={
@@ -257,7 +257,6 @@ function MoreActionsMenu({
// ---------------------------------------------------------------------------
export function MessageActionBar({
activeReplyTargetId = null,
channelId,
message,
onDelete,
@@ -272,7 +271,6 @@ export function MessageActionBar({
reactionPending = false,
isFollowingThread,
}: {
activeReplyTargetId?: string | null;
/** Channel UUID — required for the "Copy link" action; when omitted the
* action is hidden (callers like the home inbox that lack the context). */
channelId?: string | null;
@@ -306,7 +304,6 @@ export function MessageActionBar({
return null;
}
const isReplyingToMessage = activeReplyTargetId === message.id;
const selectedReactionCount = reactions.filter(
(reaction) => reaction.reactedByCurrentUser,
).length;
@@ -318,7 +315,7 @@ export function MessageActionBar({
"opacity-100 sm:pointer-events-none sm:opacity-0",
"sm:group-hover/message:pointer-events-auto sm:group-hover/message:opacity-100",
"sm:group-focus-within/message:pointer-events-auto sm:group-focus-within/message:opacity-100",
isReplyingToMessage || isReactionPickerOpen || isDropdownOpen
isReactionPickerOpen || isDropdownOpen
? "sm:pointer-events-auto sm:opacity-100"
: "",
)}
@@ -390,7 +387,7 @@ export function MessageActionBar({
<Tooltip>
<TooltipTrigger asChild>
<Button
aria-label={isReplyingToMessage ? "Cancel reply" : "Reply"}
aria-label="Reply"
className="h-6 w-6 rounded-full p-0"
data-testid={`reply-message-${message.id}`}
onClick={() => {
@@ -398,14 +395,12 @@ export function MessageActionBar({
}}
size="sm"
type="button"
variant={isReplyingToMessage ? "secondary" : "ghost"}
variant="ghost"
>
<CornerUpLeft className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>
{isReplyingToMessage ? "Cancel reply" : "Reply"}
</TooltipContent>
<TooltipContent>Reply</TooltipContent>
</Tooltip>
) : null}
@@ -28,7 +28,6 @@ const NESTED_REPLY_OFFSET_PX = 28;
export const MessageRow = React.memo(
function MessageRow({
activeReplyTargetId = null,
channelId = null,
highlighted = false,
isFollowingThread,
@@ -44,7 +43,6 @@ export const MessageRow = React.memo(
profiles,
searchQuery,
}: {
activeReplyTargetId?: string | null;
channelId?: string | null;
highlighted?: boolean;
isFollowingThread?: boolean;
@@ -212,7 +210,6 @@ export const MessageRow = React.memo(
const actionBarNode = (
<div className="absolute right-2 top-1 z-10">
<MessageActionBar
activeReplyTargetId={activeReplyTargetId}
channelId={channelId}
isFollowingThread={isFollowingThread}
message={message}
@@ -451,7 +448,6 @@ export const MessageRow = React.memo(
prev.message.role === next.message.role &&
prev.message.personaDisplayName === next.message.personaDisplayName &&
prev.highlighted === next.highlighted &&
prev.activeReplyTargetId === next.activeReplyTargetId &&
prev.isFollowingThread === next.isFollowingThread &&
prev.layoutVariant === next.layoutVariant &&
prev.profiles === next.profiles &&
@@ -63,7 +63,6 @@ type MessageThreadPanelProps = {
remove: boolean,
) => Promise<void>;
profiles?: UserProfileLookup;
replyTargetId: string | null;
replyTargetMessage: TimelineMessage | null;
scrollTargetId: string | null;
threadHead: TimelineMessage | null;
@@ -116,7 +115,6 @@ export function MessageThreadPanel({
onToggleReaction,
onUnfollowThread,
profiles,
replyTargetId,
replyTargetMessage,
scrollTargetId,
threadHead,
@@ -275,7 +273,6 @@ export function MessageThreadPanel({
<div className="px-3 pb-1 pt-0" data-testid="message-thread-head">
<div className="rounded-2xl">
<MessageRow
activeReplyTargetId={replyTargetId}
channelId={channelId}
isFollowingThread={isFollowingThread}
layoutVariant="thread-reply"
@@ -316,7 +313,6 @@ export function MessageThreadPanel({
key={entry.message.id}
>
<MessageRow
activeReplyTargetId={replyTargetId}
channelId={channelId}
layoutVariant="thread-reply"
message={entry.message}
@@ -18,7 +18,6 @@ type MessageTimelineProps = {
isLoading?: boolean;
emptyTitle?: string;
emptyDescription?: string;
activeReplyTargetId?: string | null;
currentPubkey?: string;
fetchOlder?: () => Promise<void>;
hasOlderMessages?: boolean;
@@ -58,7 +57,6 @@ export const MessageTimeline = React.memo(function MessageTimeline({
isLoading = false,
emptyTitle = "No messages yet",
emptyDescription = "Send the first message to start the thread.",
activeReplyTargetId = null,
currentPubkey,
fetchOlder,
hasOlderMessages = true,
@@ -192,7 +190,6 @@ export const MessageTimeline = React.memo(function MessageTimeline({
{!isLoading && messages.length > 0 ? (
<TimelineMessageList
activeReplyTargetId={activeReplyTargetId}
channelId={channelId}
currentPubkey={currentPubkey}
followThreadById={followThreadById}
@@ -15,7 +15,6 @@ import { MessageThreadSummaryRow } from "./MessageThreadSummaryRow";
import { SystemMessageRow } from "./SystemMessageRow";
type TimelineMessageListProps = {
activeReplyTargetId?: string | null;
channelId?: string | null;
currentPubkey?: string;
followThreadById?: (rootId: string) => void;
@@ -45,7 +44,6 @@ type TimelineMessageListProps = {
};
export const TimelineMessageList = React.memo(function TimelineMessageList({
activeReplyTargetId = null,
channelId,
currentPubkey,
followThreadById,
@@ -116,7 +114,6 @@ export const TimelineMessageList = React.memo(function TimelineMessageList({
)}
>
<MessageRow
activeReplyTargetId={activeReplyTargetId}
channelId={channelId}
highlighted={false}
isFollowingThread={
@@ -165,7 +162,6 @@ export const TimelineMessageList = React.memo(function TimelineMessageList({
currentDayGroup?.elements.push(
<div key={message.id} className="flex flex-col gap-1">
<MessageRow
activeReplyTargetId={activeReplyTargetId}
channelId={channelId}
highlighted={message.id === highlightedMessageId || isSearchActive}
message={message}