import React, { useEffect, useMemo, useState } from "react"; import { ApiClient } from "./api-client.ts"; import { addNodeToDraft, createDefaultWorkflowDraft, removeNodeFromDraft, resolveDefinitionIdForNode, serializeWorkflowDraft, workflowDraftFromVersion, type WorkflowDraft, } from "./workflow-editor-state.ts"; type NavItem = "Assets" | "Workflows" | "Runs" | "Explore" | "Labels" | "Admin"; type BootstrapContext = { userId: string; workspace: { _id: string; name: string }; project: { _id: string; name: string }; }; type AppProps = { apiBaseUrl: string; }; function usePathname() { const [pathname, setPathname] = useState( typeof window === "undefined" ? "/assets" : window.location.pathname || "/assets", ); useEffect(() => { const handle = () => setPathname(window.location.pathname || "/assets"); window.addEventListener("popstate", handle); return () => window.removeEventListener("popstate", handle); }, []); return pathname === "/" ? "/assets" : pathname; } function AppShell(props: { workspaceName: string; projectName: string; active: NavItem; children: React.ReactNode; }) { const navItems: Array<{ label: NavItem; href: string }> = [ { label: "Assets", href: "/assets" }, { label: "Workflows", href: "/workflows" }, { label: "Runs", href: "/runs" }, { label: "Explore", href: "/explore" }, { label: "Labels", href: "/labels" }, { label: "Admin", href: "/admin" }, ]; return (
Workspace {props.workspaceName}
Project {props.projectName}
Runs Local Dev
{props.children}
); } function AssetsPage(props: { api: ApiClient; bootstrap: BootstrapContext; }) { const [sourcePath, setSourcePath] = useState(""); const [assets, setAssets] = useState([]); const [error, setError] = useState(null); const loadAssets = async () => { try { setAssets(await props.api.listAssets(props.bootstrap.project._id)); } catch (loadError) { setError(loadError instanceof Error ? loadError.message : "Failed to load assets"); } }; useEffect(() => { void loadAssets(); }, [props.bootstrap.project._id]); return (

Assets

Register local folders, archives, or dataset files, then probe them into managed asset metadata.

{error ?

{error}

: null}
{assets.length === 0 ? (

No assets have been registered yet.

) : ( assets.map((asset) => (
{asset.displayName} {asset.status}

Type: {asset.type}

Source: {asset.sourceType}

Detected: {(asset.detectedFormats ?? []).join(", ") || "pending"}

Top-level entries: {(asset.topLevelPaths ?? []).slice(0, 6).join(", ") || "n/a"}

)) )}
); } function AssetDetailPage(props: { api: ApiClient; assetId: string; }) { const [asset, setAsset] = useState(null); const [probeReport, setProbeReport] = useState(null); const [artifactId, setArtifactId] = useState(null); const [error, setError] = useState(null); useEffect(() => { void (async () => { try { const [assetData, reportData] = await Promise.all([ props.api.getAsset(props.assetId), props.api.getProbeReport(props.assetId).catch(() => null), ]); setAsset(assetData); setProbeReport(reportData); } catch (loadError) { setError(loadError instanceof Error ? loadError.message : "Failed to load asset detail"); } })(); }, [props.assetId]); if (error) { return
{error}
; } if (!asset) { return
Loading asset detail...
; } return (

{asset.displayName}

Asset ID: {asset._id}

Type: {asset.type}

Status: {asset.status}

Source path: {asset.sourcePath ?? "n/a"}

{artifactId ? Open Explore View : null}
); } function WorkflowsPage(props: { api: ApiClient; bootstrap: BootstrapContext; }) { const [workflows, setWorkflows] = useState([]); const [error, setError] = useState(null); const load = async () => { try { setWorkflows(await props.api.listWorkflows(props.bootstrap.project._id)); } catch (loadError) { setError(loadError instanceof Error ? loadError.message : "Failed to load workflows"); } }; useEffect(() => { void load(); }, [props.bootstrap.project._id]); return (

Workflows

{error ?

{error}

: null}
{workflows.length === 0 ? (

No workflows yet.

) : ( workflows.map((workflow) => (
{workflow.name}

Status: {workflow.status}

Latest version: {workflow.latestVersionNumber}

)) )}
); } function WorkflowEditorPage(props: { api: ApiClient; workflowId: string; }) { const [workflow, setWorkflow] = useState(null); const [nodes, setNodes] = useState([]); const [assets, setAssets] = useState([]); const [selectedAssetId, setSelectedAssetId] = useState(null); const [versions, setVersions] = useState([]); const [draft, setDraft] = useState(() => createDefaultWorkflowDraft()); const [selectedNodeId, setSelectedNodeId] = useState("rename-folder"); const [lastRunId, setLastRunId] = useState(null); const [dirty, setDirty] = useState(false); const [error, setError] = useState(null); useEffect(() => { void (async () => { try { const workflowDefinition = await props.api.getWorkflowDefinition(props.workflowId); const [nodeDefs, workflowVersions, workflowAssets] = await Promise.all([ props.api.listNodeDefinitions(), props.api.listWorkflowVersions(props.workflowId), props.api.listAssets(workflowDefinition.projectId), ]); setWorkflow(workflowDefinition); setNodes(nodeDefs); setAssets(workflowAssets); setSelectedAssetId((previous) => { if (previous && workflowAssets.some((asset) => asset._id === previous)) { return previous; } return workflowAssets[0]?._id ?? null; }); setVersions(workflowVersions); const nextDraft = workflowDraftFromVersion(workflowVersions[0] ?? null); setDraft(nextDraft); setSelectedNodeId(nextDraft.logicGraph.nodes[0]?.id ?? "rename-folder"); setDirty(false); } catch (loadError) { setError(loadError instanceof Error ? loadError.message : "Failed to load workflow"); } })(); }, [props.workflowId]); const selectedNode = useMemo( () => nodes.find((node) => node.id === resolveDefinitionIdForNode(draft, selectedNodeId)) ?? null, [draft, nodes, selectedNodeId], ); async function saveCurrentDraft() { const version = await props.api.saveWorkflowVersion( props.workflowId, serializeWorkflowDraft(draft), ); setVersions((previous) => [version, ...previous.filter((item) => item._id !== version._id)]); setDirty(false); return version; } return (

{workflow?.name ?? "Workflow Editor"}

{lastRunId ? Open Latest Run : null}
{error ?

{error}

: null}

Canvas

{draft.logicGraph.nodes.map((node) => (
setSelectedNodeId(node.id)} style={{ cursor: "pointer" }} > {node.id}

Type: {node.type}

Definition: {resolveDefinitionIdForNode(draft, node.id)}

))}

Latest saved versions: {versions.length > 0 ? versions.map((item) => item.versionNumber).join(", ") : "none"}

Draft status: {dirty ? "unsaved changes" : "synced"}

); } function RunsIndexPage() { return (

Runs

Open a specific run from the workflow editor.

); } function RunDetailPage(props: { api: ApiClient; runId: string; }) { const [run, setRun] = useState(null); const [tasks, setTasks] = useState([]); const [error, setError] = useState(null); useEffect(() => { void (async () => { try { const [runData, taskData] = await Promise.all([ props.api.getRun(props.runId), props.api.listRunTasks(props.runId), ]); setRun(runData); setTasks(taskData); } catch (loadError) { setError(loadError instanceof Error ? loadError.message : "Failed to load run detail"); } })(); }, [props.runId]); if (error) { return
{error}
; } if (!run) { return
Loading run...
; } return (

Run Detail

Run ID: {run._id}

Status: {run.status}

Input assets:{" "} {(run.assetIds ?? []).length > 0 ? run.assetIds.map((assetId: string) => assetId).join(", ") : "none"}

Run Graph

{tasks.map((task) => (
{task.nodeId} {task.status}

Node type: {task.nodeType}

Bound assets: {(task.assetIds ?? []).join(", ") || "none"}

))}
); } function ExplorePage(props: { api: ApiClient; artifactId: string | null; }) { const [artifact, setArtifact] = useState(null); const [error, setError] = useState(null); useEffect(() => { if (!props.artifactId) { return; } void (async () => { try { setArtifact(await props.api.getArtifact(props.artifactId!)); } catch (loadError) { setError(loadError instanceof Error ? loadError.message : "Failed to load artifact"); } })(); }, [props.artifactId]); if (!props.artifactId) { return (

Explore

Create an artifact from asset detail to inspect it here.

); } if (error) { return
{error}
; } if (!artifact) { return
Loading artifact...
; } return (

{artifact.title}

Artifact ID: {artifact._id}

Type: {artifact.type}

{JSON.stringify(artifact.payload, null, 2)}
); } export function App(props: AppProps) { const api = useMemo(() => new ApiClient(props.apiBaseUrl), [props.apiBaseUrl]); const pathname = usePathname(); const [bootstrap, setBootstrap] = useState(null); const [error, setError] = useState(null); useEffect(() => { void (async () => { try { setBootstrap(await api.bootstrapDev()); } catch (bootstrapError) { setError( bootstrapError instanceof Error ? bootstrapError.message : "Failed to bootstrap local context", ); } })(); }, [api]); if (error) { return
{error}
; } if (!bootstrap) { return
Bootstrapping local workspace...
; } const assetMatch = pathname.match(/^\/assets\/([^/]+)$/); const workflowMatch = pathname.match(/^\/workflows\/([^/]+)$/); const runMatch = pathname.match(/^\/runs\/([^/]+)$/); const exploreMatch = pathname.match(/^\/explore\/([^/]+)$/); let active: NavItem = "Assets"; let content: React.ReactNode = ; if (pathname === "/workflows") { active = "Workflows"; content = ; } else if (workflowMatch) { active = "Workflows"; content = ; } else if (pathname === "/runs") { active = "Runs"; content = ; } else if (runMatch) { active = "Runs"; content = ; } else if (exploreMatch) { active = "Explore"; content = ; } else if (pathname === "/explore") { active = "Explore"; content = ; } else if (assetMatch) { active = "Assets"; content = ; } return ( {content} ); }