From 1122070b9c356b828239543b8f64c894d7a6783a Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Wed, 25 Feb 2026 23:08:53 +0000 Subject: [PATCH] feat(ui): highlight delete targets on hover --- packages/ui/src/components/message-block.tsx | 49 +++++++++++++++++-- packages/ui/src/components/message-item.tsx | 48 +++++++++++++----- packages/ui/src/styles/messaging.css | 2 +- .../src/styles/messaging/delete-overlays.css | 37 ++++++++++++++ 4 files changed, 119 insertions(+), 17 deletions(-) create mode 100644 packages/ui/src/styles/messaging/delete-overlays.css diff --git a/packages/ui/src/components/message-block.tsx b/packages/ui/src/components/message-block.tsx index e065be82..a0410eae 100644 --- a/packages/ui/src/components/message-block.tsx +++ b/packages/ui/src/components/message-block.tsx @@ -198,6 +198,7 @@ interface MessageContentItemProps { onFork?: (messageId?: string) => void onContentRendered?: () => void showDeleteMessage?: boolean + onDeleteMessageHoverChange?: (hovered: boolean) => void } function isSupportedPartType(part: unknown): boolean { @@ -285,6 +286,7 @@ function MessageContentItem(props: MessageContentItemProps) { isQueued={isQueued()} showAgentMeta={showAgentMeta()} showDeleteMessage={props.showDeleteMessage} + onDeleteMessageHoverChange={props.onDeleteMessageHoverChange} onRevert={props.onRevert} onFork={props.onFork} onContentRendered={props.onContentRendered} @@ -302,12 +304,14 @@ interface ToolCallItemProps { partId: string onContentRendered?: () => void showDeleteMessage?: boolean + onDeleteMessageHoverChange?: (hovered: boolean) => void } function ToolCallItem(props: ToolCallItemProps) { const { t } = useI18n() const [deleting, setDeleting] = createSignal(false) const [deletingMessage, setDeletingMessage] = createSignal(false) + const [hoverDeletePart, setHoverDeletePart] = createSignal(false) const record = createMemo(() => props.store().getMessage(props.messageId)) const messageInfo = createMemo(() => props.store().getMessageInfo(props.messageId)) @@ -399,7 +403,7 @@ function ToolCallItem(props: ToolCallItemProps) { return ( {(resolvedToolPart) => ( - <> +
{TOOL_ICON} @@ -426,6 +430,8 @@ function ToolCallItem(props: ToolCallItemProps) { type="button" disabled={deleteDisabled()} onClick={handleDeleteToolPart} + onMouseEnter={() => setHoverDeletePart(true)} + onMouseLeave={() => setHoverDeletePart(false)} title={deleting() ? t("messageBlock.tool.deletePart.deleting") : t("messageBlock.tool.deletePart.label")} aria-label={deleting() ? t("messageBlock.tool.deletePart.deleting") : t("messageBlock.tool.deletePart.label")} > @@ -438,6 +444,8 @@ function ToolCallItem(props: ToolCallItemProps) { type="button" disabled={deletingMessage()} onClick={handleDeleteMessage} + onMouseEnter={() => props.onDeleteMessageHoverChange?.(true)} + onMouseLeave={() => props.onDeleteMessageHoverChange?.(false)} title={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")} aria-label={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")} > @@ -457,7 +465,7 @@ function ToolCallItem(props: ToolCallItemProps) { sessionId={props.sessionId} onContentRendered={props.onContentRendered} /> - +
)} ) @@ -519,6 +527,11 @@ export default function MessageBlock(props: MessageBlockProps) { const record = createMemo(() => props.store().getMessage(props.messageId)) const messageInfo = createMemo(() => props.store().getMessageInfo(props.messageId)) const sessionCache = getSessionRenderCache(props.instanceId, props.sessionId) + const [deleteMessageHovered, setDeleteMessageHovered] = createSignal(false) + + const handleDeleteMessageHoverChange = (hovered: boolean) => { + setDeleteMessageHovered(hovered) + } const block = createMemo(() => { const current = record() @@ -707,7 +720,11 @@ export default function MessageBlock(props: MessageBlockProps) { return ( {(resolvedBlock) => ( -
+
{(item, index) => ( @@ -721,6 +738,7 @@ export default function MessageBlock(props: MessageBlockProps) { messageIndex={props.messageIndex} lastAssistantIndex={props.lastAssistantIndex} showDeleteMessage={index() === 0} + onDeleteMessageHoverChange={handleDeleteMessageHoverChange} onRevert={props.onRevert} onFork={props.onFork} onContentRendered={props.onContentRendered} @@ -738,6 +756,7 @@ export default function MessageBlock(props: MessageBlockProps) { messageId={toolItem.messageId} partId={toolItem.partId} showDeleteMessage={index() === 0} + onDeleteMessageHoverChange={handleDeleteMessageHoverChange} onContentRendered={props.onContentRendered} />
@@ -754,6 +773,7 @@ export default function MessageBlock(props: MessageBlockProps) { instanceId={props.instanceId} sessionId={props.sessionId} messageId={props.messageId} + onDeleteMessageHoverChange={handleDeleteMessageHoverChange} /> @@ -767,6 +787,7 @@ export default function MessageBlock(props: MessageBlockProps) { instanceId={props.instanceId} sessionId={props.sessionId} messageId={props.messageId} + onDeleteMessageHoverChange={handleDeleteMessageHoverChange} /> @@ -779,6 +800,7 @@ export default function MessageBlock(props: MessageBlockProps) { messageId={(item as CompactionDisplayItem).messageId} partId={(item as CompactionDisplayItem).partId} showDeleteMessage={index() === 0} + onDeleteMessageHoverChange={handleDeleteMessageHoverChange} /> @@ -792,6 +814,7 @@ export default function MessageBlock(props: MessageBlockProps) { showAgentMeta={(item as ReasoningDisplayItem).showAgentMeta} defaultExpanded={(item as ReasoningDisplayItem).defaultExpanded} showDeleteMessage={index() === 0} + onDeleteMessageHoverChange={handleDeleteMessageHoverChange} /> @@ -814,6 +837,7 @@ interface StepCardProps { instanceId?: string sessionId?: string messageId?: string + onDeleteMessageHoverChange?: (hovered: boolean) => void } interface CompactionCardProps { @@ -825,12 +849,14 @@ interface CompactionCardProps { messageId: string partId: string showDeleteMessage?: boolean + onDeleteMessageHoverChange?: (hovered: boolean) => void } function CompactionCard(props: CompactionCardProps) { const { t } = useI18n() const [deleting, setDeleting] = createSignal(false) const [deletingMessage, setDeletingMessage] = createSignal(false) + const [hoverDeletePart, setHoverDeletePart] = createSignal(false) const isAuto = () => Boolean((props.part as any)?.auto) const label = () => (isAuto() ? t("messageBlock.compaction.autoLabel") : t("messageBlock.compaction.manualLabel")) const borderColor = () => props.borderColor ?? (isAuto() ? "var(--session-status-compacting-fg)" : USER_BORDER_COLOR) @@ -881,7 +907,8 @@ function CompactionCard(props: CompactionCardProps) { return (
props.onDeleteMessageHoverChange?.(true)} + onMouseLeave={() => props.onDeleteMessageHoverChange?.(false)} title={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")} aria-label={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")} > @@ -905,6 +934,8 @@ function CompactionCard(props: CompactionCardProps) { class="tool-call-header-button" disabled={!canDelete()} onClick={handleDelete} + onMouseEnter={() => setHoverDeletePart(true)} + onMouseLeave={() => setHoverDeletePart(false)} title={t("messagePart.actions.deleteTitle")} aria-label={t("messagePart.actions.deleteTitle")} > @@ -1025,6 +1056,8 @@ function StepCard(props: StepCardProps) { class="message-action-button absolute right-2 top-1/2 -translate-y-1/2" disabled={!canDeleteMessage()} onClick={handleDeleteMessage} + onMouseEnter={() => props.onDeleteMessageHoverChange?.(true)} + onMouseLeave={() => props.onDeleteMessageHoverChange?.(false)} title={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")} aria-label={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")} > @@ -1072,6 +1105,7 @@ interface ReasoningCardProps { showAgentMeta?: boolean defaultExpanded?: boolean showDeleteMessage?: boolean + onDeleteMessageHoverChange?: (hovered: boolean) => void } function ReasoningCard(props: ReasoningCardProps) { @@ -1079,6 +1113,7 @@ function ReasoningCard(props: ReasoningCardProps) { const [expanded, setExpanded] = createSignal(Boolean(props.defaultExpanded)) const [deleting, setDeleting] = createSignal(false) const [deletingMessage, setDeletingMessage] = createSignal(false) + const [hoverDeletePart, setHoverDeletePart] = createSignal(false) createEffect(() => { setExpanded(Boolean(props.defaultExpanded)) @@ -1188,7 +1223,7 @@ function ReasoningCard(props: ReasoningCardProps) { } return ( -
+