fix(ui): highlight all tool segments on message delete hover
This commit is contained in:
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user