fix(ui): highlight all tool segments on message delete hover

This commit is contained in:
Shantur Rathore
2026-02-26 09:34:34 +00:00
parent 0f9c99e3bd
commit 688b127c6d

View File

@@ -42,10 +42,6 @@ interface PendingSegment {
type: TimelineSegmentType type: TimelineSegmentType
texts: string[] texts: string[]
reasoningTexts: string[] reasoningTexts: string[]
toolTitles: string[]
toolTypeLabels: string[]
toolIcons: string[]
toolPartIds: string[]
partIds: string[] partIds: string[]
hasPrimaryText: boolean hasPrimaryText: boolean
} }
@@ -176,17 +172,12 @@ export function buildTimelineSegments(
pending = null pending = null
return return
} }
const isToolSegment = pending.type === "tool" const label = segmentLabel(pending.type)
const label = isToolSegment const shortLabel = undefined
? pending.toolTypeLabels[0] || segmentLabel("tool") const tooltip = formatTextsTooltip(
: segmentLabel(pending.type) [...pending.texts, ...pending.reasoningTexts],
const shortLabel = isToolSegment ? pending.toolIcons[0] || getToolIcon("tool") : undefined pending.type === "user" ? t("messageTimeline.tooltip.userFallback") : t("messageTimeline.tooltip.assistantFallback"),
const tooltip = isToolSegment )
? formatToolTooltip(pending.toolTitles, t)
: formatTextsTooltip(
[...pending.texts, ...pending.reasoningTexts],
pending.type === "user" ? t("messageTimeline.tooltip.userFallback") : t("messageTimeline.tooltip.assistantFallback"),
)
result.push({ result.push({
id: `${record.id}:${segmentIndex}`, id: `${record.id}:${segmentIndex}`,
@@ -195,8 +186,7 @@ export function buildTimelineSegments(
label, label,
tooltip, tooltip,
shortLabel, shortLabel,
toolPartIds: isToolSegment ? pending.toolPartIds : undefined, partIds: pending.partIds,
partIds: !isToolSegment ? pending.partIds : undefined,
}) })
segmentIndex += 1 segmentIndex += 1
pending = null pending = null
@@ -209,10 +199,6 @@ export function buildTimelineSegments(
type, type,
texts: [], texts: [],
reasoningTexts: [], reasoningTexts: [],
toolTitles: [],
toolTypeLabels: [],
toolIcons: [],
toolPartIds: [],
partIds: [], partIds: [],
hasPrimaryText: type !== "assistant", hasPrimaryText: type !== "assistant",
} }
@@ -227,14 +213,20 @@ export function buildTimelineSegments(
if (!part || typeof part !== "object") continue if (!part || typeof part !== "object") continue
if (part.type === "tool") { if (part.type === "tool") {
const target = ensureSegment("tool") flushPending()
const toolPart = part as ToolCallPart const toolPart = part as ToolCallPart
target.toolTitles.push(getToolTitle(toolPart, t)) const partId = typeof toolPart.id === "string" ? toolPart.id : ""
target.toolTypeLabels.push(getToolTypeLabel(toolPart, t)) const title = getToolTitle(toolPart, t)
target.toolIcons.push(getToolIcon(typeof toolPart.tool === "string" ? toolPart.tool : "tool")) result.push({
if (typeof toolPart.id === "string" && toolPart.id.length > 0) { id: `${record.id}:${segmentIndex}`,
target.toolPartIds.push(toolPart.id) messageId: record.id,
} type: "tool",
label: getToolTypeLabel(toolPart, t) || segmentLabel("tool"),
tooltip: formatToolTooltip([title], t),
shortLabel: getToolIcon(typeof toolPart.tool === "string" ? toolPart.tool : "tool"),
toolPartIds: partId ? [partId] : undefined,
})
segmentIndex += 1
continue continue
} }
@@ -421,6 +413,24 @@ const MessageTimeline: Component<MessageTimelineProps> = (props) => {
onCleanup(() => buttonRefs.delete(segment.id)) onCleanup(() => buttonRefs.delete(segment.id))
const isActive = () => props.activeMessageId === segment.messageId const isActive = () => props.activeMessageId === segment.messageId
const isDeleteHovered = () => {
const hover = deleteHover() as DeleteHoverState
if (hover.kind === "message") {
return hover.messageId === segment.messageId
}
if (hover.kind === "part") {
if (hover.messageId !== segment.messageId) return false
if (segment.type === "tool") {
return segment.toolPartIds?.includes(hover.partId) ?? false
}
if (segment.type === "compaction") {
return segment.partId === hover.partId
}
return segment.partIds?.includes(hover.partId) ?? false
}
return false
}
const hasActivePermission = () => { const hasActivePermission = () => {
if (segment.type !== "tool") return false if (segment.type !== "tool") return false
const partIds = segment.toolPartIds ?? [] const partIds = segment.toolPartIds ?? []
@@ -432,7 +442,7 @@ const MessageTimeline: Component<MessageTimelineProps> = (props) => {
return false return false
} }
const isHidden = () => segment.type === "tool" && !(showTools() || isActive() || hasActivePermission()) const isHidden = () => segment.type === "tool" && !(showTools() || isActive() || hasActivePermission() || isDeleteHovered())
const shortLabelContent = () => { const shortLabelContent = () => {
if (segment.type === "tool") { if (segment.type === "tool") {
@@ -451,29 +461,13 @@ const MessageTimeline: Component<MessageTimelineProps> = (props) => {
} }
return ( return (
<button <button
ref={(el) => registerButtonRef(segment.id, el)} ref={(el) => registerButtonRef(segment.id, el)}
type="button" type="button"
data-variant={segment.variant} data-variant={segment.variant}
class={`message-timeline-segment message-timeline-${segment.type} ${hasActivePermission() ? "message-timeline-segment-permission" : ""} ${segment.type === "compaction" ? `message-timeline-compaction-${segment.variant ?? "manual"}` : ""} ${isActive() ? "message-timeline-segment-active" : ""} ${isHidden() ? "message-timeline-segment-hidden" : ""}`} class={`message-timeline-segment message-timeline-${segment.type} ${hasActivePermission() ? "message-timeline-segment-permission" : ""} ${segment.type === "compaction" ? `message-timeline-compaction-${segment.variant ?? "manual"}` : ""} ${isActive() ? "message-timeline-segment-active" : ""} ${isHidden() ? "message-timeline-segment-hidden" : ""}`}
data-delete-hover={(() => { data-delete-hover={isDeleteHovered() ? "true" : undefined}
const hover = deleteHover() as DeleteHoverState
if (hover.kind === "message") {
return hover.messageId === segment.messageId ? "true" : undefined
}
if (hover.kind === "part") {
if (hover.messageId !== segment.messageId) return undefined
if (segment.type === "tool") {
return segment.toolPartIds?.includes(hover.partId) ? "true" : undefined
}
if (segment.type === "compaction") {
return segment.partId === hover.partId ? "true" : undefined
}
return segment.partIds?.includes(hover.partId) ? "true" : undefined
}
return undefined
})()}
aria-current={isActive() ? "true" : undefined} aria-current={isActive() ? "true" : undefined}
aria-hidden={isHidden() ? "true" : undefined} aria-hidden={isHidden() ? "true" : undefined}