{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "workflow-run-button",
  "title": "Workflow Run Button",
  "description": "A play, stop and run-again button with a progress ring, built on the Base UI Button primitive.",
  "dependencies": [
    "@base-ui/react",
    "@xyflow/react",
    "class-variance-authority",
    "cn",
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/notra/workflow/components/workflow-run-button.tsx",
      "content": "\"use client\";\n\nimport { Button as ButtonPrimitive } from \"@base-ui/react/button\";\nimport { cva } from \"class-variance-authority\";\nimport { cn } from \"cn\";\nimport { PlayIcon, RotateCcwIcon, SquareIcon } from \"lucide-react\";\n\nimport {\n  WORKFLOW_RUN_LABELS,\n  WORKFLOW_RUN_RING_LENGTH,\n  WORKFLOW_RUN_RING_RADIUS,\n} from \"../constants/workflow-ui\";\nimport type { WorkflowRunButtonProps } from \"../types/workflow\";\n\nconst runButtonVariants = cva(\n  \"group/run focus-visible:ring-ring/50 relative inline-flex h-8 shrink-0 items-center gap-2 rounded-full pr-3.5 pl-1 text-[0.8125rem] font-medium whitespace-nowrap transition-[background-color,box-shadow,color] duration-200 outline-none select-none focus-visible:ring-3 disabled:pointer-events-none disabled:opacity-50\",\n  {\n    variants: {\n      phase: {\n        done: \"bg-foreground text-background hover:bg-foreground/90 shadow-[inset_0_1px_0_rgba(255,255,255,0.18),0_1px_2px_rgba(0,0,0,0.2)]\",\n        idle: \"bg-foreground text-background hover:bg-foreground/90 shadow-[inset_0_1px_0_rgba(255,255,255,0.18),0_1px_2px_rgba(0,0,0,0.2)]\",\n        running:\n          \"bg-background text-foreground ring-foreground/15 hover:bg-muted ring-1\",\n      },\n    },\n  }\n);\n\nexport const WorkflowRunButton = ({\n  className,\n  onClick,\n  onPlay,\n  onStop,\n  phase,\n  progress = 0,\n  ...props\n}: WorkflowRunButtonProps) => {\n  const running = phase === \"running\";\n  const ratio = Number.isFinite(progress)\n    ? Math.min(1, Math.max(0, progress))\n    : 0;\n  let Icon = PlayIcon;\n  if (running) {\n    Icon = SquareIcon;\n  } else if (phase === \"done\") {\n    Icon = RotateCcwIcon;\n  }\n\n  return (\n    <ButtonPrimitive\n      className={cn(runButtonVariants({ phase }), className)}\n      data-phase={phase}\n      data-slot=\"workflow-run-button\"\n      {...props}\n      onClick={(event) => {\n        onClick?.(event);\n        if (!event.defaultPrevented) {\n          (running ? onStop : onPlay)?.();\n        }\n      }}\n    >\n      <span\n        className={cn(\n          \"relative flex size-6 items-center justify-center rounded-full\",\n          running ? \"text-primary\" : \"bg-background/15\"\n        )}\n      >\n        {running && (\n          <svg\n            aria-hidden=\"true\"\n            className=\"absolute inset-0 size-6 -rotate-90\"\n            viewBox=\"0 0 24 24\"\n          >\n            <circle\n              className=\"stroke-primary/20\"\n              cx={12}\n              cy={12}\n              fill=\"none\"\n              r={WORKFLOW_RUN_RING_RADIUS}\n              strokeWidth={2}\n            />\n            <circle\n              className=\"stroke-primary transition-[stroke-dashoffset] duration-100\"\n              cx={12}\n              cy={12}\n              fill=\"none\"\n              r={WORKFLOW_RUN_RING_RADIUS}\n              strokeDasharray={WORKFLOW_RUN_RING_LENGTH}\n              strokeDashoffset={WORKFLOW_RUN_RING_LENGTH * (1 - ratio)}\n              strokeLinecap=\"round\"\n              strokeWidth={2}\n            />\n          </svg>\n        )}\n        <Icon\n          className={cn(\n            \"size-3\",\n            running ? \"fill-current\" : \"fill-current group-hover/run:scale-110\",\n            phase === \"done\" && \"fill-none\",\n            \"transition-transform\"\n          )}\n        />\n      </span>\n      {WORKFLOW_RUN_LABELS[phase]}\n    </ButtonPrimitive>\n  );\n};\n",
      "type": "registry:component",
      "target": "components/workflow/components/workflow-run-button.tsx"
    },
    {
      "path": "registry/notra/workflow/constants/workflow-ui.ts",
      "content": "import type {\n  WorkflowBannerConfig,\n  WorkflowEdgeStatus,\n  WorkflowRunPhase,\n} from \"../types/workflow\";\n\nexport const WORKFLOW_NODE_WIDTH = 256;\n\nexport const WORKFLOW_NODE_DROP_OFFSET_Y = 24;\n\nexport const WORKFLOW_COLUMN = 320;\n\nexport const WORKFLOW_FIT_PADDING = 0.12;\n\nexport const WORKFLOW_FIT_MIN_ZOOM = 0.6;\n\nexport const WORKFLOW_FIT_MAX_ZOOM = 1;\n\nexport const WORKFLOW_FIT_EDGE_PX = 32;\n\nexport const WORKFLOW_FIT_VIEW_OPTIONS = {\n  maxZoom: WORKFLOW_FIT_MAX_ZOOM,\n  padding: WORKFLOW_FIT_PADDING,\n};\n\nexport const WORKFLOW_ZOOM_DURATION_MS = 200;\n\nexport const WORKFLOW_FIT_DURATION_MS = 300;\n\nexport const WORKFLOW_MINIMAP_STYLE = { height: 96, width: 144 };\n\nexport const WORKFLOW_PATTERN_STRENGTH = {\n  cross: 22,\n  dots: 24,\n  gridMajor: 12,\n  gridMinor: 5,\n  lines: 7,\n};\n\nexport const WORKFLOW_PORT_ROW_CLASS =\n  \"text-muted-foreground border-table-card-border relative flex h-7.5 min-w-0 items-center border-t px-3 text-[0.6875rem] font-medium\";\n\nexport const WORKFLOW_CHIP_CLASS =\n  \"bg-foreground/[0.05] text-muted-foreground inline-flex h-5 min-w-0 items-center gap-1 rounded-full px-2 text-[0.6875rem] leading-none tabular-nums [&_svg]:size-3 [&_svg]:shrink-0\";\n\nexport const WORKFLOW_BANNERS: Record<\n  \"error\" | \"waiting\",\n  WorkflowBannerConfig\n> = {\n  error: {\n    className: \"text-red-600 dark:text-red-400\",\n    label: \"Failed\",\n    message: \"Check the run log\",\n  },\n  waiting: {\n    className: \"text-amber-600 dark:text-amber-400\",\n    label: \"Waiting\",\n    message: \"Needs approval\",\n  },\n};\n\nexport const WORKFLOW_PILL_CLASS =\n  \"bg-table-shell border-table-shell-border text-muted-foreground inline-flex h-9 w-7 shrink-0 flex-col overflow-hidden rounded-lg border [&_svg]:size-3\";\n\nexport const WORKFLOW_PILL_TOP_CLASS =\n  \"flex flex-1 items-center justify-center\";\n\nexport const WORKFLOW_PILL_BOTTOM_CLASS =\n  \"border-table-shell-border flex flex-1 items-center justify-center border-t\";\n\nexport const WORKFLOW_LED_CLASS =\n  \"ring-background/60 size-1.5 rounded-full ring-2 transition-colors duration-300\";\n\nexport const WORKFLOW_EDGE_COLOR: Record<WorkflowEdgeStatus, string> = {\n  active: \"text-primary\",\n  done: \"text-primary/70\",\n  idle: \"text-muted-foreground/70\",\n  skipped: \"text-muted-foreground/25\",\n};\n\nexport const WORKFLOW_EDGE_DASH = \"0 10\";\n\nexport const WORKFLOW_EDGE_STEP_OPTIONS = { borderRadius: 24, offset: 24 };\n\nexport const WORKFLOW_EDGE_RADIUS = 24;\n\nexport const WORKFLOW_EDGE_LANE_GAP = 16;\n\nexport const WORKFLOW_RUN_LABELS: Record<WorkflowRunPhase, string> = {\n  done: \"Run again\",\n  idle: \"Run workflow\",\n  running: \"Stop\",\n};\n\nexport const WORKFLOW_RUN_RING_RADIUS = 10;\n\nexport const WORKFLOW_RUN_RING_LENGTH = 2 * Math.PI * WORKFLOW_RUN_RING_RADIUS;\n\nexport const WORKFLOW_SECTION_TITLE_CLASS =\n  \"text-muted-foreground text-[0.6875rem] font-medium tracking-wide uppercase\";\n\nexport const WORKFLOW_EDGE_REVERSE_STYLE = { animationDirection: \"reverse\" };\n\nexport const WORKFLOW_SKIPPED_DIM_CLASS =\n  \"transition-opacity duration-300 group-data-[status=skipped]/node:opacity-45\";\n\nexport const WORKFLOW_BRANCH_RAIL_CLASS =\n  \"border-table-card-border bg-table-card/60 divide-table-card-border mt-0.5 divide-y rounded-[14px] border\";\n\nexport const WORKFLOW_BRANCH_ROW_CLASS =\n  \"relative flex h-10 min-w-0 items-center gap-2.5 px-3\";\n\nexport const WORKFLOW_BRANCH_LED_CLASS =\n  \"size-2 shrink-0 rounded-full ring-2 transition-[background-color,box-shadow,opacity] duration-300\";\n\nexport const WORKFLOW_RULE_TOKEN_CLASS =\n  \"bg-foreground/[0.05] text-foreground/80 inline-flex h-5 max-w-full min-w-0 items-center truncate rounded-md px-1.5 text-[0.6875rem] whitespace-nowrap\";\n",
      "type": "registry:lib",
      "target": "components/workflow/constants/workflow-ui.ts"
    },
    {
      "path": "registry/notra/workflow/types/workflow.ts",
      "content": "import type { Edge, Node, Position } from \"@xyflow/react\";\nimport type { ComponentProps, ComponentType, ReactNode, SVGProps } from \"react\";\n\nexport type WorkflowNodeKind =\n  | \"trigger\"\n  | \"task\"\n  | \"ai\"\n  | \"integration\"\n  | \"condition\"\n  | \"switch\"\n  | \"merge\"\n  | \"loop\"\n  | \"delay\"\n  | \"approval\"\n  | \"email\"\n  | \"output\";\n\nexport type WorkflowNodeStatus =\n  | \"idle\"\n  | \"queued\"\n  | \"running\"\n  | \"waiting\"\n  | \"success\"\n  | \"error\"\n  | \"skipped\";\n\nexport type WorkflowEdgeStatus = \"idle\" | \"active\" | \"done\" | \"skipped\";\n\nexport type WorkflowRunPhase = \"idle\" | \"running\" | \"done\";\n\nexport type WorkflowLogoName =\n  | \"openai\"\n  | \"anthropic\"\n  | \"claude\"\n  | \"gemini\"\n  | \"perplexity\"\n  | \"mistral\"\n  | \"deepseek\"\n  | \"grok\"\n  | \"browserbase\"\n  | \"context-dev\"\n  | \"firecrawl\"\n  | \"resend\"\n  | \"slack\"\n  | \"gmail\"\n  | \"notion\"\n  | \"google\"\n  | \"google-sheets\"\n  | \"webflow\"\n  | \"wordpress\"\n  | \"linear\"\n  | \"github\"\n  | \"ahrefs\"\n  | \"posthog\";\n\nexport type WorkflowLogLevel = \"info\" | \"success\" | \"error\" | \"skip\";\n\nexport type WorkflowIcon = ComponentType<SVGProps<SVGSVGElement>>;\n\nexport interface WorkflowPoint {\n  x: number;\n  y: number;\n}\n\nexport interface WorkflowAnchor extends WorkflowPoint {\n  position: Position;\n}\n\nexport interface WorkflowPort {\n  id: string;\n  label?: string;\n  hint?: string;\n  rate?: number;\n}\n\nexport interface WorkflowRule {\n  subject: string;\n  operator: string;\n  value: string;\n}\n\nexport interface WorkflowField {\n  label: string;\n  value: string;\n}\n\nexport interface WorkflowNodeData {\n  id: string;\n  kind: WorkflowNodeKind;\n  title: string;\n  description?: string;\n  position: WorkflowPoint;\n  inputs?: WorkflowPort[];\n  outputs?: WorkflowPort[];\n  logo?: WorkflowLogoName;\n  model?: string;\n  durationMs?: number;\n  branch?: string;\n  rule?: WorkflowRule;\n  fail?: boolean;\n  fields?: WorkflowField[];\n  output?: string;\n}\n\nexport interface WorkflowEdgeData {\n  id: string;\n  source: string;\n  sourcePort?: string;\n  target: string;\n  targetPort?: string;\n  label?: string;\n  animated?: boolean;\n}\n\nexport interface WorkflowEdgeLane {\n  id: string;\n  down: boolean;\n  from: WorkflowPoint;\n  to: WorkflowPoint;\n}\n\nexport interface WorkflowGraph {\n  edges: WorkflowEdgeData[];\n  nodes: WorkflowNodeData[];\n}\n\nexport interface WorkflowTemplate extends WorkflowGraph {\n  id: string;\n  title: string;\n  description: string;\n  category: string;\n}\n\nexport interface WorkflowKindConfig {\n  icon: WorkflowIcon;\n  label: string;\n  tileClassName: string;\n}\n\nexport interface WorkflowStatusConfig {\n  className: string;\n  label: string;\n  ledClassName: string;\n}\n\nexport interface WorkflowLogoConfig {\n  label: string;\n  logo: WorkflowIcon;\n  tile?: boolean;\n}\n\nexport interface WorkflowModel {\n  id: string;\n  label: string;\n  logo: WorkflowLogoName;\n  provider: string;\n}\n\nexport interface WorkflowBlock {\n  id: string;\n  title: string;\n  description: string;\n  group: string;\n  kind: WorkflowNodeKind;\n  logo?: WorkflowLogoName;\n  model?: string;\n  inputs?: WorkflowPort[];\n  outputs?: WorkflowPort[];\n  durationMs?: number;\n}\n\nexport interface WorkflowNodeRun {\n  start: number;\n  end: number;\n  status: \"success\" | \"error\" | \"skipped\";\n}\n\nexport interface WorkflowEdgeRun {\n  start: number;\n  end: number;\n  taken: boolean;\n}\n\nexport interface WorkflowSchedule {\n  edges: Record<string, WorkflowEdgeRun>;\n  nodes: Record<string, WorkflowNodeRun>;\n  order: string[];\n  totalMs: number;\n}\n\nexport interface WorkflowNodeState {\n  elapsedMs: number;\n  status: WorkflowNodeStatus;\n}\n\nexport interface WorkflowLogEntry {\n  at: number;\n  id: string;\n  level: WorkflowLogLevel;\n  message: string;\n  nodeId: string;\n  title: string;\n}\n\nexport interface WorkflowRunSummary {\n  id: string;\n  durationMs: number;\n  startedAt: string;\n  status: \"success\" | \"error\" | \"running\";\n  trigger: string;\n  steps: number;\n}\n\nexport interface WorkflowRun {\n  edgeState: (id: string) => WorkflowEdgeStatus;\n  elapsedMs: number;\n  nodeState: (id: string) => WorkflowNodeState;\n  phase: WorkflowRunPhase;\n  play: () => void;\n  schedule: WorkflowSchedule;\n  stop: () => void;\n  totalMs: number;\n}\n\nexport type WorkflowLogoProps = Omit<SVGProps<SVGSVGElement>, \"name\"> & {\n  name: WorkflowLogoName;\n};\n\nexport interface WorkflowFlowNodeData extends Record<string, unknown> {\n  node: WorkflowNodeData;\n  state?: WorkflowNodeState;\n}\n\nexport interface WorkflowFlowEdgeData extends Record<string, unknown> {\n  animated?: boolean;\n  reverse?: boolean;\n  label?: string;\n  status: WorkflowEdgeStatus;\n}\n\nexport type WorkflowFlowNode = Node<WorkflowFlowNodeData, \"workflow\">;\n\nexport type WorkflowFlowEdge = Edge<WorkflowFlowEdgeData, \"workflow\">;\n\nexport type WorkflowCanvasBackgroundVariant =\n  | \"dots\"\n  | \"lines\"\n  | \"cross\"\n  | \"grid\"\n  | \"none\";\n\nexport interface WorkflowCanvasBackgroundProps {\n  gap?: number;\n  variant?: WorkflowCanvasBackgroundVariant;\n}\n\nexport type WorkflowNodeSizes = Record<\n  string,\n  { height: number; width: number }\n>;\n\nexport interface WorkflowPortListProps {\n  ports: WorkflowPort[];\n  title: string;\n}\n\nexport interface WorkflowControlsProps {\n  toolbar?: ReactNode;\n}\n\nexport interface WorkflowBannerConfig {\n  className: string;\n  label: string;\n  message: string;\n}\n\nexport interface WorkflowStatusGlyphProps {\n  status: WorkflowNodeStatus;\n}\n\nexport interface WorkflowTemplatePreviewProps {\n  template: WorkflowTemplate;\n}\n\nexport interface WorkflowCanvasProps {\n  className?: string;\n  /** Follows new steps as they are added or move, like a workflow being written. */\n  autoFit?: boolean;\n  focusId?: string;\n  background?: WorkflowCanvasBackgroundVariant;\n  gap?: number;\n  nodes: WorkflowNodeData[];\n  edges: WorkflowEdgeData[];\n  nodeState?: (id: string) => WorkflowNodeState;\n  edgeState?: (id: string) => WorkflowEdgeStatus;\n  selectedId?: string;\n  controls?: boolean;\n  minimap?: boolean;\n  toolbar?: ReactNode;\n  minZoom?: number;\n  maxZoom?: number;\n  /** The smallest zoom that fits the whole graph; below it, the view shows the first or last steps instead. */\n  fitMinZoom?: number;\n  onSelectNode?: (id: string) => void;\n  onNodeMove?: (id: string, position: WorkflowPoint) => void;\n  onNodeRemove?: (id: string) => void;\n  onConnect?: (edge: Omit<WorkflowEdgeData, \"id\">) => void;\n  onDropBlock?: (blockId: string, position: WorkflowPoint) => void;\n}\n\nexport type WorkflowNodeCardProps = ComponentProps<\"div\"> & {\n  node: WorkflowNodeData;\n  state?: WorkflowNodeState;\n  selected?: boolean;\n  connectable?: boolean;\n  children?: ReactNode;\n};\n\nexport interface WorkflowHandleProps {\n  active?: boolean;\n  className?: string;\n  connectable?: boolean;\n  portId: string;\n  type: \"source\" | \"target\";\n}\n\nexport type WorkflowEdgeProps = ComponentProps<\"g\"> & {\n  path: string;\n  reverse?: boolean;\n  status?: WorkflowEdgeStatus;\n  animated?: boolean;\n};\n\nexport type WorkflowTimerProps = ComponentProps<\"span\"> & {\n  ms: number;\n  running?: boolean;\n};\n\nexport type WorkflowStatusIndicatorProps = ComponentProps<\"span\"> & {\n  status: WorkflowNodeStatus;\n};\n\nexport type WorkflowStatusBadgeProps = ComponentProps<\"span\"> & {\n  status: WorkflowNodeStatus;\n};\n\nexport type WorkflowStatusPillProps = ComponentProps<\"span\"> & {\n  status: WorkflowNodeStatus;\n};\n\nexport type WorkflowDeterminismBadgeProps = ComponentProps<\"span\"> & {\n  deterministic: boolean;\n};\n\nexport type WorkflowRunButtonProps = Omit<\n  ComponentProps<\"button\">,\n  \"children\"\n> & {\n  phase: WorkflowRunPhase;\n  progress?: number;\n  onPlay?: () => void;\n  onStop?: () => void;\n};\n\nexport type WorkflowNodePaletteProps = ComponentProps<\"div\"> & {\n  blocks: WorkflowBlock[];\n  onAdd?: (block: WorkflowBlock) => void;\n};\n\nexport interface WorkflowNodeBranchProps {\n  connectable?: boolean;\n  node: WorkflowNodeData;\n  port: WorkflowPort;\n  status: WorkflowNodeStatus;\n}\n\nexport interface WorkflowNodeBannerProps {\n  status: WorkflowNodeStatus;\n}\n\nexport interface WorkflowNodeSummaryProps {\n  children?: ReactNode;\n  node: WorkflowNodeData;\n}\n\nexport interface WorkflowNodeEngineProps {\n  node: WorkflowNodeData;\n}\n\nexport interface WorkflowNodeFooterProps {\n  node: WorkflowNodeData;\n  state?: WorkflowNodeState;\n}\n\nexport interface WorkflowBranchSelectProps {\n  node: WorkflowNodeData;\n  outputs: WorkflowPort[];\n  onBranchChange: (id: string, branch: string) => void;\n}\n\nexport type WorkflowNodeSettingsProps = Pick<\n  WorkflowNodeSheetProps,\n  \"onBranchChange\" | \"onModelChange\"\n> & {\n  node: WorkflowNodeData;\n};\n\nexport type WorkflowNodeSheetProps = WorkflowOverlayProps & {\n  node?: WorkflowNodeData;\n  state?: WorkflowNodeState;\n  log?: WorkflowLogEntry[];\n  onDelete?: (id: string) => void;\n  onModelChange?: (id: string, model: string) => void;\n  onBranchChange?: (id: string, branch: string) => void;\n};\n\nexport type WorkflowRunLogProps = ComponentProps<\"div\"> & {\n  entries: WorkflowLogEntry[];\n  emptyLabel?: string;\n};\n\nexport type WorkflowRunTimelineProps = ComponentProps<\"div\"> & {\n  nodes: WorkflowNodeData[];\n  schedule: WorkflowSchedule;\n  elapsedMs: number;\n  phase: WorkflowRunPhase;\n};\n\nexport type WorkflowRunHistoryProps = Omit<\n  ComponentProps<\"div\">,\n  \"onSelect\"\n> & {\n  runs: WorkflowRunSummary[];\n  selectedId?: string;\n  onSelect?: (id: string) => void;\n};\n\nexport interface WorkflowModelSelectProps {\n  className?: string;\n  defaultValue?: string;\n  disabled?: boolean;\n  id?: string;\n  models?: WorkflowModel[];\n  onValueChange?: (value: string) => void;\n  value?: string;\n}\n\nexport type WorkflowTemplateCardProps = ComponentProps<\"div\"> & {\n  template: WorkflowTemplate;\n  onUse?: (template: WorkflowTemplate) => void;\n};\n\nexport type WorkflowTemplateDialogProps = WorkflowOverlayProps & {\n  template?: WorkflowTemplate;\n  onUse?: (template: WorkflowTemplate) => void;\n};\n\nexport interface WorkflowBlockIconProps {\n  block: Pick<WorkflowBlock, \"kind\" | \"logo\" | \"model\">;\n  className?: string;\n}\n\nexport interface WorkflowOverlayProps {\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  open?: boolean;\n}\n\nexport interface WorkflowCanvasBackgroundOption {\n  label: string;\n  value: WorkflowCanvasBackgroundVariant;\n}\n\nexport interface WorkflowNodeDemo {\n  node: WorkflowNodeData;\n  state: WorkflowNodeState;\n}\n\nexport interface WorkflowEdgeDemo {\n  animated?: boolean;\n  label: string;\n  status: WorkflowEdgeStatus;\n}\n",
      "type": "registry:lib",
      "target": "components/workflow/types/workflow.ts"
    }
  ],
  "type": "registry:component"
}