import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { api, assetFileURL, connectWS, deleteAsset, listAssets, listJobs, listOperators, runJob, uploadFile, } from "./api"; import Viewer from "./Viewer"; const MODEL_EXTS = ["glb", "gltf", "obj", "fbx", "ply", "stl"]; const fmtSize = (n) => n > 1e9 ? (n / 1e9).toFixed(1) + " GB" : n > 1e6 ? (n / 1e6).toFixed(1) + " MB" : n > 1e3 ? (n / 1e3).toFixed(0) + " KB" : n + " B"; const KIND_ICON = { video: "๐ŸŽฌ", image: "๐Ÿ–ผ", model: "๐ŸงŠ", frames: "๐ŸŽž", other: "๐Ÿ“„" }; function ParamForm({ schema, values, onChange }) { const props = schema?.properties || {}; if (!Object.keys(props).length) return

No parameters.

; return (
{Object.entries(props).map(([key, def]) => { const val = values[key] ?? def.default ?? ""; const set = (v) => onChange({ ...values, [key]: v }); return ( ); })}
); } function AssetPreview({ asset }) { const [folder, setFolder] = useState(null); const ext = asset ? asset.name.split(".").pop().toLowerCase() : ""; useEffect(() => { setFolder(null); if (asset && (asset.kind === "frames" || asset.kind === "folder")) { api(assetFileURL(asset.id)).then(setFolder).catch(() => {}); } }, [asset?.id]); if (!asset) return
Select an asset โ€” or drop / paste files anywhere
; if (MODEL_EXTS.includes(ext)) return ; if (asset.kind === "image") return
{asset.name}
; if (asset.kind === "video") return
; if (folder?.files) return (
{folder.files.slice(0, 60).map((f) => ( {f} ))} {folder.files.length > 60 && +{folder.files.length - 60} more}
); if (ext === "json") return ; return
No preview for .{ext}
; } function JsonPreview({ url }) { const [text, setText] = useState("โ€ฆ"); useEffect(() => { fetch(url).then((r) => r.text()).then((t) => setText(t.slice(0, 20000))); }, [url]); return
{text}
; } export default function App() { const [assets, setAssets] = useState([]); const [operators, setOperators] = useState([]); const [jobs, setJobs] = useState([]); const [selected, setSelected] = useState(null); const [opId, setOpId] = useState(null); const [params, setParams] = useState({}); const [openJob, setOpenJob] = useState(null); const fileInput = useRef(null); const refreshAssets = useCallback(() => listAssets().then(setAssets), []); const refreshJobs = useCallback(() => listJobs().then(setJobs), []); useEffect(() => { refreshAssets(); refreshJobs(); listOperators().then(setOperators); connectWS((msg) => { if (msg.type === "job") { setJobs((js) => { const i = js.findIndex((j) => j.id === msg.job.id); if (i === -1) return [msg.job, ...js]; const copy = [...js]; copy[i] = msg.job; return copy; }); } if (msg.type === "assets_changed") refreshAssets(); }); }, []); // drag-drop + paste ingest useEffect(() => { const drop = async (e) => { e.preventDefault(); for (const f of e.dataTransfer?.files || []) await uploadFile(f); refreshAssets(); }; const paste = async (e) => { const items = [...(e.clipboardData?.items || [])]; for (const it of items) { const f = it.getAsFile?.(); if (f) { await uploadFile(f); } } refreshAssets(); }; const prevent = (e) => e.preventDefault(); window.addEventListener("drop", drop); window.addEventListener("dragover", prevent); window.addEventListener("paste", paste); return () => { window.removeEventListener("drop", drop); window.removeEventListener("dragover", prevent); window.removeEventListener("paste", paste); }; }, [refreshAssets]); const selectedAsset = assets.find((a) => a.id === selected) || null; const usableOps = useMemo(() => operators.filter((op) => !selectedAsset || op.accepts.includes(selectedAsset.kind)), [operators, selectedAsset]); const activeOp = operators.find((o) => o.id === opId); useEffect(() => { if (activeOp) { const defaults = {}; for (const [k, d] of Object.entries(activeOp.params_schema?.properties || {})) if (d.default !== undefined) defaults[k] = d.default; setParams(defaults); } }, [opId]); useEffect(() => { if (usableOps.length && !usableOps.find((o) => o.id === opId)) setOpId(usableOps[0].id); }, [usableOps]); const launch = async () => { if (!activeOp || !selectedAsset) return; await runJob(activeOp.id, selectedAsset.id, params); refreshJobs(); }; return (

MODELBEAST

M3 Ultra ยท 256GB ยท local-first 3D factory { for (const f of e.target.files) await uploadFile(f); e.target.value = ""; refreshAssets(); }} />
); }