import type { Edge, Node } from "@xyflow/react"; /** Port data types, derived from Gradio component kinds. */ export type DataType = | "text" | "number" | "boolean" | "image" | "audio" | "video" | "file" | "gallery" | "json" | "any"; export type Category = "Inputs" | "Utilities" | "Spaces" | "Functions"; export type WidgetValue = string | number | boolean; export type WidgetDef = | { kind: "text"; id: string; label: string; default: string; placeholder?: string } | { kind: "textarea"; id: string; label: string; default: string; placeholder?: string; /** Always show the expanded editor (notes, prompts). */ block?: boolean } | { kind: "number"; id: string; label: string; default: number; min?: number; max?: number; step?: number; precision?: number } | { kind: "slider"; id: string; label: string; default: number; min: number; max: number; step?: number; precision?: number } | { kind: "seed"; id: string; label: string; default: number } | { kind: "select"; id: string; label: string; default: string; options: readonly string[]; /** Raw choice values when the Space uses non-string choices (numbers, booleans), parallel to `options`. */ raw?: readonly (string | number | boolean)[] } | { kind: "toggle"; id: string; label: string; default: boolean } /** A file input: value is a URL, or `local:` for a file picked in the browser. */ | { kind: "file"; id: string; label: string; default: string; accept: DataType }; export interface Port { id: string; label: string; type: DataType; /** Inputs only: how the value is entered when nothing is wired in. */ widget?: WidgetDef; /** Inputs only: the Gradio component this maps to (for payload shaping). */ component?: string; optional?: boolean; } export interface SpaceRef { id: string; apiName: string; /** Gradio dependency id (fn_index) for the queue protocol. */ fnIndex: number; triggerId: number | null; host: string; apiPrefix: string; /** Component ids of outputs, in order. */ outputComponents: string[]; /** * The queue protocol wants one value per dependency input component, but /info lists only the * ones that take a value (State, buttons and the like are skipped). inputSlots[i] is the port * index that fills dependency input i, or null for a component that gets no value. */ inputSlots?: (number | null)[]; /** outputSlots[k]: which entry of the returned data list is port k (State outputs are skipped). */ outputSlots?: number[]; } export interface NodeDef { type: string; title: string; category: Category; description: string; inputs: Port[]; outputs: Port[]; /** Pure widgets (not wirable), used by input nodes. */ widgets: WidgetDef[]; width?: number; /** Colour accent; defaults to the first output type. */ accent?: DataType; /** Set for nodes that call a Space endpoint. */ space?: SpaceRef; /** Set for input nodes: which widget value is exposed on the single output. */ emits?: string; /** Simulated cost is gone; 0 marks nodes that never execute (groups, notes). */ cost?: number; } export type NodeStatus = "idle" | "queued" | "running" | "done" | "error"; export type NodeMode = "always" | "bypass" | "never"; /** A file produced by a Space, as returned in its output payload. */ export interface FileRef { url: string; name?: string; mime?: string; } export type OutputValue = string | number | boolean | FileRef | FileRef[] | null | Record | unknown[]; export interface CanvasNodeData extends Record { defType: string; title?: string; values: Record; collapsed?: boolean; mode: NodeMode; status: NodeStatus; progress: number; /** True while `progress` is extrapolated from past run times rather than reported by the Space. */ progressEstimated?: boolean; /** Short live status, e.g. queue rank or ETA. */ statusText?: string; error?: string; /** Results of the last run, keyed by output port id. */ outputs?: Record; /** Hash of the inputs that produced `outputs`; lets unchanged nodes skip. */ cacheKey?: string; /** Pinned in place: can't be dragged. */ locked?: boolean; /** Colour tag (a key of NODE_COLORS). */ color?: string; /** Free-form note shown under the header. */ note?: string; /** Group nodes only: the reusable function this group was made from. */ fnId?: string; /** Group nodes only, derived for display: ports shown while collapsed. */ groupPorts?: { inputs: GroupPort[]; outputs: GroupPort[] }; } export type CanvasNode = Node; export type CanvasEdge = Edge<{ type: DataType; flowing?: boolean }, "typed">; /** One line of the run log; the copilot reads these to diagnose failures. */ export interface LogEntry { t: number; level: "info" | "warn" | "error"; node?: string; msg: string; } export interface HistoryItem { id: string; workflow: string; finishedAt: number; durationMs: number; nodes: number; status: "done" | "cancelled" | "error"; thumb?: string; error?: string; /** The graph as it was when the run finished, outputs included, so it can be restored. */ graph?: GraphSnapshot; /** Set when this run was one cell of a batch / sweep. */ batch?: BatchRef; /** Which widget values this run set before starting (seeds, the swept text). */ vary?: VaryRef[]; } /** Where a run sits in its batch grid: rows are sweep lines, columns are seed variations. */ export interface BatchRef { id: string; index: number; count: number; rows: number; cols: number; row: number; col: number; /** What the rows / columns vary ("prompt", "seed"). */ rowLabel?: string; colLabel?: string; } export interface VaryRef { nodeId: string; widgetId: string; label: string; value: WidgetValue; } export interface GraphSnapshot { nodes: CanvasNode[]; edges: CanvasEdge[]; order: string[]; } export interface QueueItem { id: string; workflow: string; queuedAt: number; /** Run only these nodes and what they depend on. */ targets?: string[]; /** Widget values to set just before this item runs (batch runs and sweeps). */ apply?: { nodeId: string; widgetId: string; value: WidgetValue }[]; /** Ignore cached outputs on the targets so they call their Space again. */ force?: boolean; batch?: BatchRef; } export type ViewMode = "canvas" | "pipeline"; export interface GroupPort extends Port { nodeId: string; portId: string; } export interface FunctionDef { id: string; name: string; description: string; nodes: CanvasNode[]; edges: CanvasEdge[]; } export type SpaceStage = "RUNNING" | "SLEEPING" | "BUILDING" | "PAUSED" | "STOPPED" | "RUNTIME_ERROR" | "NO_APP_FILE" | "CONFIG_ERROR" | "BUILD_ERROR" | "DELETING" | "APP_STARTING" | "UNKNOWN"; export interface SpaceSummary { id: string; author: string; name: string; title: string; emoji: string; likes: number; sdk?: string; stage: SpaceStage; hardware: string | null; zeroGpu: boolean; subdomain: string; shortDescription?: string; /** Card gradient colours from the Space README, as Hugging Face shows them. */ colorFrom?: string; colorTo?: string; private?: boolean; } /** A Space added to the workflow, with the node defs derived from its API. */ export interface SpaceEntry { summary: SpaceSummary; host: string; apiPrefix: string; version?: string; defs: NodeDef[]; status: "loading" | "ready" | "error"; error?: string; loadedAt?: number; } export interface WorkflowTemplate { id: string; name: string; description: string; /** Space ids that must be loaded before the graph can be built. */ spaces: string[]; build: (defsOf: (spaceId: string) => NodeDef[]) => { nodes: CanvasNode[]; edges: CanvasEdge[] }; } /** The on-disk workflow file. */ export interface WorkflowFile { version: 1; /** Stable id of the saved document (cloud sync key). Absent in older files. */ id?: string; /** ms epoch of the last edit that was saved. */ updatedAt?: number; /** Changed by the user (not just a template as loaded). */ edited?: boolean; name: string; nodes: CanvasNode[]; edges: CanvasEdge[]; order: string[]; functions: FunctionDef[]; /** Schema snapshot so the graph renders before live schemas arrive. */ spaces: SpaceEntry[]; }