Give the traceback button its width back

Reserving a slot for all three status controls left 72px for a node's title,
against about 140px before. Only the stop button and the dot come and go on
their own; the traceback button appears when a node newly fails and goes when
someone acknowledges it, which is a thing to notice rather than a flicker. It
goes back in the row, and the title gets 106px.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZeGnqVsf5VHQqvz4HdUhN
This commit is contained in:
2026-08-23 09:28:25 +02:00
co-authored by Claude Opus 5
parent 31f603cc5f
commit c9f672c65f
+48 -47
View File
@@ -226,11 +226,14 @@ function FlowNodeComponent({ data, selected }: NodeProps) {
{typeLabel}
</span>
</span>
{/* Each control sits in a slot that is there whether the control
is or not. They come and go with what the node is doing — and a
node running many times a second comes and goes that often — so
in the flex row itself they would resize the card on every
execution, which reads as a flickering shape. */}
{/* This and the dot below sit in slots that are there whether the
control is or not: both come and go on their own — a node running
many times a second mounts and unmounts this on every execution —
and in the flex row itself that resizes the card each time, which
reads as a flickering shape. The traceback button below is left
in the row on purpose: it appears when a node newly fails and
goes when someone acknowledges it, which is a thing to notice
rather than a flicker, and a slot for it costs the title 34px. */}
<span className="flex size-6 shrink-0 items-center justify-center">
{running ? (
<Tooltip>
@@ -259,48 +262,46 @@ function FlowNodeComponent({ data, selected }: NodeProps) {
) : null}
</span>
<span className="flex size-6 shrink-0 items-center justify-center">
{(status === "error" || failedEarlier) && onShowLogs ? (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon-sm"
// `nodrag` keeps xyflow from reading the press as a drag; the
// click itself is stopped so the node panel stays closed.
className={cn(
"nodrag nopan size-6 shrink-0 hover:text-destructive",
// Red while it is the only thing left saying so, and named
// in words beside it: colour never carries a status alone.
failedEarlier
? "text-destructive"
: "text-muted-foreground",
)}
aria-label={
failedEarlier
? `Failed at ${failedAt} — show the traceback`
: "Show what this node printed"
}
data-testid="node-traceback"
onClick={(event) => {
event.stopPropagation()
onShowLogs(definition.id)
// Reading it is what dismisses it: nothing else does, and a
// marker that never goes away stops meaning anything.
liveStore.acknowledgeFailure(nodeId)
}}
>
<Bug />
</Button>
</TooltipTrigger>
<TooltipContent>
{failedEarlier
? `Failed at ${failedAt} — show the traceback`
: "Show the traceback"}
</TooltipContent>
</Tooltip>
) : null}
</span>
{(status === "error" || failedEarlier) && onShowLogs ? (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon-sm"
// `nodrag` keeps xyflow from reading the press as a drag; the
// click itself is stopped so the node panel stays closed.
className={cn(
"nodrag nopan size-6 shrink-0 hover:text-destructive",
// Red while it is the only thing left saying so, and named
// in words beside it: colour never carries a status alone.
failedEarlier
? "text-destructive"
: "text-muted-foreground",
)}
aria-label={
failedEarlier
? `Failed at ${failedAt} — show the traceback`
: "Show what this node printed"
}
data-testid="node-traceback"
onClick={(event) => {
event.stopPropagation()
onShowLogs(definition.id)
// Reading it is what dismisses it: nothing else does, and a
// marker that never goes away stops meaning anything.
liveStore.acknowledgeFailure(nodeId)
}}
>
<Bug />
</Button>
</TooltipTrigger>
<TooltipContent>
{failedEarlier
? `Failed at ${failedAt} — show the traceback`
: "Show the traceback"}
</TooltipContent>
</Tooltip>
) : null}
<span className="flex size-2 shrink-0 items-center justify-center">
{style ? (