Let a paused flow be stepped from the dock

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H7LwYgJfpkbLCTeiAf8U4A
This commit is contained in:
2026-08-16 17:16:53 +02:00
co-authored by Claude Fable 5
parent 63d3183518
commit d5e1466d8c
3 changed files with 35 additions and 8 deletions
@@ -9,13 +9,7 @@ import { cn } from "@/lib/utils"
import "./dashboard.css" import "./dashboard.css"
import { WidgetBody, WidgetFrame, widgetIssue } from "./widgets" import { WidgetBody, WidgetFrame, widgetIssue } from "./widgets"
/** export type Dashboard = DashboardDef_Output
* A dashboard, plus the settings the generated client does not carry yet.
*
* `columns` is stored by the backend but the SDK is regenerated on its own
* schedule, so it is widened here rather than waited for.
*/
export type Dashboard = DashboardDef_Output & { columns?: number }
/** How many columns a wall panel is cut into, if the document does not say. */ /** How many columns a wall panel is cut into, if the document does not say. */
export const DEFAULT_COLUMNS = 12 export const DEFAULT_COLUMNS = 12
+24
View File
@@ -9,6 +9,7 @@ import {
Play, Play,
PlayCircle, PlayCircle,
Plus, Plus,
StepForward,
WifiOff, WifiOff,
ZoomIn, ZoomIn,
ZoomOut, ZoomOut,
@@ -62,6 +63,8 @@ export function FlowDock({
onAddNode, onAddNode,
onRun, onRun,
onTogglePause, onTogglePause,
onStep,
stepping,
onFocusNode, onFocusNode,
onEditFlow, onEditFlow,
onPublish, onPublish,
@@ -79,6 +82,8 @@ export function FlowDock({
onAddNode: () => void onAddNode: () => void
onRun: () => void onRun: () => void
onTogglePause: () => void onTogglePause: () => void
onStep: () => void
stepping: boolean
onFocusNode: (nodeId: string) => void onFocusNode: (nodeId: string) => void
onEditFlow: () => void onEditFlow: () => void
onPublish: () => void onPublish: () => void
@@ -214,6 +219,25 @@ export function FlowDock({
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
{paused && enabled ? (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="size-11 text-muted-foreground md:size-8"
onClick={onStep}
disabled={stepping}
aria-label="Step the flow"
data-testid="step-flow"
>
<StepForward />
</Button>
</TooltipTrigger>
<TooltipContent>Let one held-back item through</TooltipContent>
</Tooltip>
) : null}
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<span> <span>
+10 -1
View File
@@ -211,7 +211,7 @@ function FlowEditorInner({
}) { }) {
const navigate = useNavigate() const navigate = useNavigate()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const { showErrorToast } = useCustomToast() const { showErrorToast, showSuccessToast } = useCustomToast()
const { screenToFlowPosition, fitView } = useReactFlow() const { screenToFlowPosition, fitView } = useReactFlow()
const updateNodeInternals = useUpdateNodeInternals() const updateNodeInternals = useUpdateNodeInternals()
@@ -507,6 +507,13 @@ function FlowEditorInner({
onError: () => showErrorToast("The flow could not be paused."), onError: () => showErrorToast("The flow could not be paused."),
}) })
const stepMutation = useMutation({
mutationFn: () => FlowsService.stepFlow({ name: flowName }),
// Says which node ran, or that nothing was held back — both are answers.
onSuccess: (result) => showSuccessToast(result.message),
onError: () => showErrorToast("The flow could not be stepped."),
})
const deleteMutation = useMutation({ const deleteMutation = useMutation({
mutationFn: () => FlowsService.deleteFlow({ name: flowName }), mutationFn: () => FlowsService.deleteFlow({ name: flowName }),
onSuccess: () => { onSuccess: () => {
@@ -970,6 +977,8 @@ function FlowEditorInner({
runMutation.mutate() runMutation.mutate()
}} }}
onTogglePause={() => pauseMutation.mutate(!paused)} onTogglePause={() => pauseMutation.mutate(!paused)}
onStep={() => stepMutation.mutate()}
stepping={stepMutation.isPending}
onFocusNode={focusNode} onFocusNode={focusNode}
/> />
</div> </div>