Copy nodes, bind the keys, and keep publish within reach
The floating bars used to make way for a side panel, which hid Publish exactly when a node had just been edited. They keep their lane now and the panel is inset out of it; the enlarged code editor stays a floating surface between the two bars rather than covering them. Shortcuts are one small registry (`lib/shortcuts.ts`): undo, redo, ⌘K, copy, paste, and ⌘S — which publishes the flow, or applies the code when the editor has focus. Copying carries a node's own source through localStorage, so a paste works in another flow too. A flow is now named where a node is, at the top of its panel and only there, and both take effect on Confirm. Flow-level edits go through the undo stack with everything else, clicking the canvas puts the flow panel away, a failing node opens the logs filtered to its own traceback, and the panel carries its test id on a phone as well. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H7LwYgJfpkbLCTeiAf8U4A
This commit is contained in:
@@ -2,6 +2,7 @@ import { Handle, type NodeProps, Position } from "@xyflow/react"
|
||||
import {
|
||||
Bell,
|
||||
Braces,
|
||||
Bug,
|
||||
Clock,
|
||||
Code2,
|
||||
Database,
|
||||
@@ -20,6 +21,7 @@ import {
|
||||
import { memo } from "react"
|
||||
|
||||
import type { MessageSpec, NodeDef_Input } from "@/client"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
@@ -61,6 +63,8 @@ export type FlowNodeData = {
|
||||
typeLabel: string
|
||||
isPlugin?: boolean
|
||||
issueText: string
|
||||
/** Open the logs at this node's own lines. */
|
||||
onShowLogs?: (nodeId: string) => void
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
@@ -103,7 +107,7 @@ function PortHandles({
|
||||
}
|
||||
|
||||
function FlowNodeComponent({ data, selected }: NodeProps) {
|
||||
const { definition, flow, typeLabel, isPlugin, issueText } =
|
||||
const { definition, flow, typeLabel, isPlugin, issueText, onShowLogs } =
|
||||
data as FlowNodeData
|
||||
const live = useNodeStatus(`${flow}.${definition.id}`)
|
||||
const emits = useNodeEmits(`${flow}.${definition.id}`)
|
||||
@@ -153,6 +157,29 @@ function FlowNodeComponent({ data, selected }: NodeProps) {
|
||||
{typeLabel}
|
||||
</span>
|
||||
</span>
|
||||
{status === "error" && 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="nodrag nopan -my-1 size-6 shrink-0 text-muted-foreground hover:text-destructive"
|
||||
aria-label="Show what this node printed"
|
||||
data-testid="node-traceback"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
onShowLogs(definition.id)
|
||||
}}
|
||||
>
|
||||
<Bug />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Show the traceback</TooltipContent>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
|
||||
{style ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
|
||||
Reference in New Issue
Block a user