fix(ui): sync delete-hover overlays across preview and stream
This commit is contained in:
@@ -199,6 +199,7 @@ interface MessageContentItemProps {
|
||||
onFork?: (messageId?: string) => void
|
||||
onContentRendered?: () => void
|
||||
showDeleteMessage?: boolean
|
||||
deleteHover?: () => DeleteHoverState
|
||||
onDeleteHoverChange?: (state: DeleteHoverState) => void
|
||||
}
|
||||
|
||||
@@ -287,6 +288,7 @@ function MessageContentItem(props: MessageContentItemProps) {
|
||||
isQueued={isQueued()}
|
||||
showAgentMeta={showAgentMeta()}
|
||||
showDeleteMessage={props.showDeleteMessage}
|
||||
deleteHover={props.deleteHover}
|
||||
onDeleteHoverChange={props.onDeleteHoverChange}
|
||||
onRevert={props.onRevert}
|
||||
onFork={props.onFork}
|
||||
@@ -305,6 +307,7 @@ interface ToolCallItemProps {
|
||||
partId: string
|
||||
onContentRendered?: () => void
|
||||
showDeleteMessage?: boolean
|
||||
deleteHover?: () => DeleteHoverState
|
||||
onDeleteHoverChange?: (state: DeleteHoverState) => void
|
||||
}
|
||||
|
||||
@@ -401,10 +404,18 @@ function ToolCallItem(props: ToolCallItemProps) {
|
||||
}
|
||||
}
|
||||
|
||||
const isDeleteHoveredFromStore = () => {
|
||||
const hover = props.deleteHover?.() ?? ({ kind: "none" } as DeleteHoverState)
|
||||
return hover.kind === "part" && hover.messageId === props.messageId && hover.partId === props.partId
|
||||
}
|
||||
|
||||
return (
|
||||
<Show when={toolPart()}>
|
||||
{(resolvedToolPart) => (
|
||||
<div class="delete-hover-scope" data-delete-part-hover={hoverDeletePart() ? "true" : undefined}>
|
||||
<div
|
||||
class="delete-hover-scope"
|
||||
data-delete-part-hover={(hoverDeletePart() || isDeleteHoveredFromStore()) ? "true" : undefined}
|
||||
>
|
||||
<div class="tool-call-header-label">
|
||||
<div class="tool-call-header-meta">
|
||||
<span class="tool-call-icon">{TOOL_ICON}</span>
|
||||
@@ -747,6 +758,7 @@ export default function MessageBlock(props: MessageBlockProps) {
|
||||
messageIndex={props.messageIndex}
|
||||
lastAssistantIndex={props.lastAssistantIndex}
|
||||
showDeleteMessage={index() === 0}
|
||||
deleteHover={props.deleteHover}
|
||||
onDeleteHoverChange={props.onDeleteHoverChange}
|
||||
onRevert={props.onRevert}
|
||||
onFork={props.onFork}
|
||||
@@ -765,6 +777,7 @@ export default function MessageBlock(props: MessageBlockProps) {
|
||||
messageId={toolItem.messageId}
|
||||
partId={toolItem.partId}
|
||||
showDeleteMessage={index() === 0}
|
||||
deleteHover={props.deleteHover}
|
||||
onDeleteHoverChange={props.onDeleteHoverChange}
|
||||
onContentRendered={props.onContentRendered}
|
||||
/>
|
||||
@@ -809,6 +822,7 @@ export default function MessageBlock(props: MessageBlockProps) {
|
||||
messageId={(item as CompactionDisplayItem).messageId}
|
||||
partId={(item as CompactionDisplayItem).partId}
|
||||
showDeleteMessage={index() === 0}
|
||||
deleteHover={props.deleteHover}
|
||||
onDeleteHoverChange={props.onDeleteHoverChange}
|
||||
/>
|
||||
</Match>
|
||||
@@ -823,6 +837,7 @@ export default function MessageBlock(props: MessageBlockProps) {
|
||||
showAgentMeta={(item as ReasoningDisplayItem).showAgentMeta}
|
||||
defaultExpanded={(item as ReasoningDisplayItem).defaultExpanded}
|
||||
showDeleteMessage={index() === 0}
|
||||
deleteHover={props.deleteHover}
|
||||
onDeleteHoverChange={props.onDeleteHoverChange}
|
||||
/>
|
||||
</Match>
|
||||
@@ -858,6 +873,7 @@ interface CompactionCardProps {
|
||||
messageId: string
|
||||
partId: string
|
||||
showDeleteMessage?: boolean
|
||||
deleteHover?: () => DeleteHoverState
|
||||
onDeleteHoverChange?: (state: DeleteHoverState) => void
|
||||
}
|
||||
|
||||
@@ -914,10 +930,15 @@ function CompactionCard(props: CompactionCardProps) {
|
||||
}
|
||||
}
|
||||
|
||||
const isDeleteHoveredFromStore = () => {
|
||||
const hover = props.deleteHover?.() ?? ({ kind: "none" } as DeleteHoverState)
|
||||
return hover.kind === "part" && hover.messageId === props.messageId && hover.partId === props.partId
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
class={`delete-hover-scope ${containerClass()} relative`}
|
||||
data-delete-part-hover={hoverDeletePart() ? "true" : undefined}
|
||||
data-delete-part-hover={(hoverDeletePart() || isDeleteHoveredFromStore()) ? "true" : undefined}
|
||||
style={{ "border-left": `4px solid ${borderColor()}` }}
|
||||
role="status"
|
||||
aria-label={t("messageBlock.compaction.ariaLabel")}
|
||||
@@ -1120,6 +1141,7 @@ interface ReasoningCardProps {
|
||||
showAgentMeta?: boolean
|
||||
defaultExpanded?: boolean
|
||||
showDeleteMessage?: boolean
|
||||
deleteHover?: () => DeleteHoverState
|
||||
onDeleteHoverChange?: (state: DeleteHoverState) => void
|
||||
}
|
||||
|
||||
@@ -1130,6 +1152,11 @@ function ReasoningCard(props: ReasoningCardProps) {
|
||||
const [deletingMessage, setDeletingMessage] = createSignal(false)
|
||||
const [hoverDeletePart, setHoverDeletePart] = createSignal(false)
|
||||
|
||||
const isDeleteHoveredFromStore = () => {
|
||||
const hover = props.deleteHover?.() ?? ({ kind: "none" } as DeleteHoverState)
|
||||
return hover.kind === "part" && hover.messageId === props.messageId && hover.partId === props.partId
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
setExpanded(Boolean(props.defaultExpanded))
|
||||
})
|
||||
@@ -1238,7 +1265,10 @@ function ReasoningCard(props: ReasoningCardProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="delete-hover-scope message-reasoning-card" data-delete-part-hover={hoverDeletePart() ? "true" : undefined}>
|
||||
<div
|
||||
class="delete-hover-scope message-reasoning-card"
|
||||
data-delete-part-hover={(hoverDeletePart() || isDeleteHoveredFromStore()) ? "true" : undefined}
|
||||
>
|
||||
<div class="message-reasoning-header">
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -23,6 +23,7 @@ interface MessageItemProps {
|
||||
showAgentMeta?: boolean
|
||||
onContentRendered?: () => void
|
||||
showDeleteMessage?: boolean
|
||||
deleteHover?: () => DeleteHoverState
|
||||
onDeleteHoverChange?: (state: DeleteHoverState) => void
|
||||
}
|
||||
|
||||
@@ -33,6 +34,11 @@ export default function MessageItem(props: MessageItemProps) {
|
||||
const [deletingMessage, setDeletingMessage] = createSignal(false)
|
||||
const [hoveredDeletePartId, setHoveredDeletePartId] = createSignal<string | null>(null)
|
||||
|
||||
const isDeleteHoveredFromStore = (partId: string) => {
|
||||
const hover = props.deleteHover?.() ?? ({ kind: "none" } as DeleteHoverState)
|
||||
return hover.kind === "part" && hover.messageId === props.record.id && hover.partId === partId
|
||||
}
|
||||
|
||||
const isUser = () => props.record.role === "user"
|
||||
const createdTimestamp = () => props.messageInfo?.time?.created ?? props.record.createdAt
|
||||
|
||||
@@ -447,7 +453,8 @@ export default function MessageItem(props: MessageItemProps) {
|
||||
<For each={messageParts()}>
|
||||
{(part) => {
|
||||
const partId = typeof (part as any)?.id === "string" ? ((part as any).id as string) : ""
|
||||
const isHoveredDeleteTarget = () => Boolean(partId) && hoveredDeletePartId() === partId
|
||||
const isHoveredDeleteTarget = () =>
|
||||
Boolean(partId) && (hoveredDeletePartId() === partId || isDeleteHoveredFromStore(partId))
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -474,7 +481,8 @@ export default function MessageItem(props: MessageItemProps) {
|
||||
{(attachment) => {
|
||||
const name = getAttachmentName(attachment)
|
||||
const isImage = isImageAttachment(attachment)
|
||||
const isHoveredDeleteTarget = () => hoveredDeletePartId() === attachment.id
|
||||
const isHoveredDeleteTarget = () =>
|
||||
Boolean(attachment.id) && (hoveredDeletePartId() === attachment.id || isDeleteHoveredFromStore(attachment.id))
|
||||
return (
|
||||
<div
|
||||
class={`delete-hover-scope attachment-chip ${isImage ? "attachment-chip-image" : ""}`}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import type { Component } from "solid-js"
|
||||
import MessageBlock from "./message-block"
|
||||
import type { InstanceMessageStore } from "../stores/message-v2/instance-store"
|
||||
import type { DeleteHoverState } from "../types/delete-hover"
|
||||
|
||||
interface MessagePreviewProps {
|
||||
instanceId: string
|
||||
sessionId: string
|
||||
messageId: string
|
||||
store: () => InstanceMessageStore
|
||||
deleteHover?: () => DeleteHoverState
|
||||
onDeleteHoverChange?: (state: DeleteHoverState) => void
|
||||
}
|
||||
|
||||
const MessagePreview: Component<MessagePreviewProps> = (props) => {
|
||||
@@ -24,6 +27,8 @@ const MessagePreview: Component<MessagePreviewProps> = (props) => {
|
||||
showThinking={() => false}
|
||||
thinkingDefaultExpanded={() => false}
|
||||
showUsageMetrics={() => false}
|
||||
deleteHover={props.deleteHover}
|
||||
onDeleteHoverChange={props.onDeleteHoverChange}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -963,6 +963,7 @@ export default function MessageSection(props: MessageSectionProps) {
|
||||
sessionId={props.sessionId}
|
||||
showToolSegments={showTimelineToolsPreference()}
|
||||
deleteHover={deleteHover}
|
||||
onDeleteHoverChange={setDeleteHover}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
@@ -32,6 +32,7 @@ interface MessageTimelineProps {
|
||||
sessionId: string
|
||||
showToolSegments?: boolean
|
||||
deleteHover?: () => DeleteHoverState
|
||||
onDeleteHoverChange?: (state: DeleteHoverState) => void
|
||||
}
|
||||
|
||||
const MAX_TOOLTIP_LENGTH = 220
|
||||
@@ -497,6 +498,8 @@ const MessageTimeline: Component<MessageTimelineProps> = (props) => {
|
||||
instanceId={props.instanceId}
|
||||
sessionId={props.sessionId}
|
||||
store={store}
|
||||
deleteHover={props.deleteHover}
|
||||
onDeleteHoverChange={props.onDeleteHoverChange}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user