Recover from a dead session, and report a node problem once

An expired or orphaned session left the app on a half-rendered page instead of
the login screen: a properly signed token naming a user who no longer exists
answered 404, which the client does not treat as an authentication failure. All
failures in get_current_user are 401 now, and the client stops retrying them,
so a dead session goes straight back to the login screen.

- Clicking an edge names the two nodes it runs between, not just the message.
- A node with a problem shows one badge carrying the whole explanation, rather
  than a corner badge and a status dot saying the same thing twice. The dot is
  back to what it is good at: whether the node ran.
- The sidebar's collapse control sits in the sidebar, where a phone still finds
  one in the chrome because there is no sidebar on screen to hold it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WzrvW7rjQbynnhF6pxh6i
This commit is contained in:
Melvin Strobl
2026-08-15 19:19:57 +02:00
co-authored by Claude Fable 5
parent 6fb42bb3ef
commit c254d487ba
9 changed files with 128 additions and 24 deletions
+20 -11
View File
@@ -29,17 +29,17 @@ const NODE_ICONS = {
mlp: Braces,
} as const
// Only the states worth a quiet marker. Anything wrong goes to the badge
// instead, so a problem is never reported twice on the same node.
const STATUS_STYLES = {
running: { dot: "bg-primary animate-pulse", label: "Running" },
success: { dot: "bg-status-success", label: "Last run succeeded" },
error: { dot: "bg-destructive", label: "Failed" },
} as const
export type FlowNodeData = {
definition: NodeDef_Input
flow: string
typeLabel: string
issues: number
issueText: string
[key: string]: unknown
}
@@ -83,15 +83,18 @@ function PortHandles({
}
function FlowNodeComponent({ data, selected }: NodeProps) {
const { definition, flow, typeLabel, issues, issueText } =
data as FlowNodeData
const { definition, flow, typeLabel, issueText } = data as FlowNodeData
const live = useNodeStatus(`${flow}.${definition.id}`)
const Icon = NODE_ICONS[definition.type as keyof typeof NODE_ICONS] ?? Code2
const status = live?.status === "active" ? undefined : live?.status
const style = status
? STATUS_STYLES[status as keyof typeof STATUS_STYLES]
: undefined
// Whatever is wrong — it failed to load, it failed to run, or the graph
// around it does not add up — is one badge with one explanation.
const problem = [live?.status === "error" ? live.error : null, issueText]
.filter(Boolean)
.join("\n")
const style = problem
? undefined
: STATUS_STYLES[live?.status as keyof typeof STATUS_STYLES]
return (
<div
@@ -132,14 +135,20 @@ function FlowNodeComponent({ data, selected }: NodeProps) {
) : null}
</div>
{issues > 0 ? (
{problem ? (
<Tooltip>
<TooltipTrigger asChild>
<span className="absolute -right-1.5 -top-1.5 flex size-4 items-center justify-center rounded-full bg-destructive text-primary-foreground">
<span
role="img"
aria-label="This node has a problem"
className="absolute -right-1.5 -top-1.5 flex size-4 items-center justify-center rounded-full bg-destructive text-primary-foreground"
>
<AlertCircle className="size-3" />
</span>
</TooltipTrigger>
<TooltipContent className="max-w-xs">{issueText}</TooltipContent>
<TooltipContent className="max-w-xs whitespace-pre-line">
{problem}
</TooltipContent>
</Tooltip>
) : null}