diff --git a/packages/producer/build.mjs b/packages/producer/build.mjs index 7b4cacbf6..4721ef685 100644 --- a/packages/producer/build.mjs +++ b/packages/producer/build.mjs @@ -15,6 +15,14 @@ mkdirSync("dist", { recursive: true }); const scriptDir = dirname(fileURLToPath(import.meta.url)); +// The banner provides a real `require` function via createRequire so that +// esbuild's CJS interop (__require) works correctly in ESM output. +// Without this, bundled CJS deps (recast, yauzl, etc.) that call +// require("fs") throw "Dynamic require of 'fs' is not supported". +const cjsBanner = { + js: "import { createRequire as __cjsRequire } from 'module'; const require = __cjsRequire(import.meta.url);", +}; + const workspaceAliasPlugin = { name: "workspace-alias", setup(build) { @@ -36,80 +44,32 @@ const workspaceAliasPlugin = { }, }; +const sharedOpts = { + bundle: true, + platform: "node", + target: "node22", + format: "esm", + external: ["puppeteer", "esbuild", "postcss"], + plugins: [workspaceAliasPlugin], + minify: false, + sourcemap: true, + banner: cjsBanner, +}; + await Promise.all([ + build({ ...sharedOpts, entryPoints: ["src/index.ts"], outfile: "dist/index.js" }), + build({ ...sharedOpts, entryPoints: ["src/server.ts"], outfile: "dist/public-server.js" }), build({ - bundle: true, - platform: "node", - target: "node22", - format: "esm", - external: ["puppeteer", "esbuild", "postcss", "wawoff2"], - plugins: [workspaceAliasPlugin], - minify: false, - sourcemap: true, - entryPoints: ["src/index.ts"], - outfile: "dist/index.js", - }), - build({ - bundle: true, - platform: "node", - target: "node22", - format: "esm", - external: ["puppeteer", "esbuild", "postcss", "wawoff2"], - plugins: [workspaceAliasPlugin], - minify: false, - sourcemap: true, - entryPoints: ["src/server.ts"], - outfile: "dist/public-server.js", - }), - // PNG decode + alpha-blit worker (hf#732 lever-4). Loaded by - // `pngDecodeBlitWorkerPool.createPngDecodeBlitWorkerPool` via - // `new Worker()`. Must be a separate entry point so the worker - // module is standalone and shares no parent module-graph state. - build({ - bundle: true, - platform: "node", - target: "node22", - format: "esm", - external: ["puppeteer", "esbuild", "postcss", "wawoff2"], - plugins: [workspaceAliasPlugin], - minify: false, - sourcemap: true, + ...sharedOpts, entryPoints: ["src/services/pngDecodeBlitWorker.ts"], outfile: "dist/services/pngDecodeBlitWorker.js", }), - // Shader-blend worker (hf#677 follow-up). Loaded by - // `shaderTransitionWorkerPool.createShaderTransitionWorkerPool` via - // `new Worker()`. Same bundling rationale as the - // `pngDecodeBlitWorker` entry above. build({ - bundle: true, - platform: "node", - target: "node22", - format: "esm", - external: ["puppeteer", "esbuild", "postcss", "wawoff2"], - plugins: [workspaceAliasPlugin], - minify: false, - sourcemap: true, + ...sharedOpts, entryPoints: ["src/services/shaderTransitionWorker.ts"], outfile: "dist/services/shaderTransitionWorker.js", }), - // `@hyperframes/producer/distributed` subpath — the public distributed - // render primitives (plan / renderChunk / assemble). Bundled as a - // separate entry so adopters that don't need the in-process renderer - // (Lambda chunk workers, CDK constructs, thin orchestrators) can import - // only this surface and skip the rest of the producer's dependency tree. - build({ - bundle: true, - platform: "node", - target: "node22", - format: "esm", - external: ["puppeteer", "esbuild", "postcss", "wawoff2"], - plugins: [workspaceAliasPlugin], - minify: false, - sourcemap: true, - entryPoints: ["src/distributed.ts"], - outfile: "dist/distributed.js", - }), + build({ ...sharedOpts, entryPoints: ["src/distributed.ts"], outfile: "dist/distributed.js" }), ]); // Copy core runtime artifacts so the producer can find them at dist/ diff --git a/packages/studio/src/components/renders/RenderQueue.tsx b/packages/studio/src/components/renders/RenderQueue.tsx index 1e00b9326..f3362843e 100644 --- a/packages/studio/src/components/renders/RenderQueue.tsx +++ b/packages/studio/src/components/renders/RenderQueue.tsx @@ -154,22 +154,22 @@ function FormatInfoTooltip({ format }: { format: "mp4" | "webm" | "mov" }) { strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" - className="text-neutral-600 hover:text-neutral-400 transition-colors cursor-help" + className="text-panel-text-5 hover:text-panel-text-3 transition-colors cursor-help" > {open && ( -
-

{info.label}

-

{info.desc}

+
+

{info.label}

+

{info.desc}

{(["mp4", "mov", "webm"] as const) .filter((f) => f !== format) .map((f) => ( -

- {FORMAT_INFO[f].label} +

+ {FORMAT_INFO[f].label} {" — "} {FORMAT_INFO[f].desc}

@@ -209,80 +209,97 @@ function FormatExportButton({ // MOV (ProRes) is a fixed-quality codec — quality selector has no effect. const showQuality = format !== "mov"; + const selectCls = + "h-7 w-full px-2 text-[11px] bg-panel-input rounded-md text-panel-text-1 outline-none cursor-pointer disabled:opacity-50 hover:bg-panel-hover transition-colors"; + return ( -
- - {/* Resolution must remain the leftmost setResolution(e.target.value as ResolutionPreset | "auto")} - disabled={isRendering} - className="h-5 px-1 text-[10px] rounded-l bg-neutral-800 border border-neutral-700 text-neutral-300 outline-none disabled:opacity-50" - > - {SCALE_OPTION_ORDER.map((value) => ( - - ))} - - {showQuality && ( - - )} - - +
+
+
+
+ Format + +
+ +
+
+ Resolution + +
+
+ Frame rate + +
+ {showQuality && ( +
+ Quality + +
+ )} +
@@ -313,23 +330,12 @@ export const RenderQueue = memo(function RenderQueue({ return (
- {/* Header — no title, already shown in header button */} -
-
- {completedCount > 0 && ( - - )} - -
+
+
{/* Job list */} @@ -343,7 +349,7 @@ export const RenderQueue = memo(function RenderQueue({ fill="none" stroke="currentColor" strokeWidth="1.5" - className="text-neutral-700" + className="text-panel-text-5" > -

No renders yet

+

No renders yet

) : ( - jobs.map((job) => ( - onDelete(job.id)} - /> - )) +
+ {completedCount > 0 && ( +
+ + {jobs.length} render{jobs.length === 1 ? "" : "s"} + + +
+ )} + {jobs.map((job) => ( + onDelete(job.id)} + /> + ))} +
)}
diff --git a/packages/studio/src/components/renders/RenderQueueItem.tsx b/packages/studio/src/components/renders/RenderQueueItem.tsx index 5c4377947..1005328f2 100644 --- a/packages/studio/src/components/renders/RenderQueueItem.tsx +++ b/packages/studio/src/components/renders/RenderQueueItem.tsx @@ -142,54 +142,53 @@ export const RenderQueueItem = memo(function RenderQueueItem({ )}
- {/* Actions — always visible to prevent layout shifts */} -
- - -
+ + + + +
+ )}
); diff --git a/packages/studio/tailwind.config.js b/packages/studio/tailwind.config.js index f3898cf36..3c9488b5e 100644 --- a/packages/studio/tailwind.config.js +++ b/packages/studio/tailwind.config.js @@ -18,15 +18,19 @@ export default { accent: "#3CE6AC", }, panel: { - bg: "#0a0a0a", - border: "#262626", - hover: "#1a1a1a", - accent: "#3CE6AC", - "text-1": "#FAFAFA", + bg: "#0C0C0E", + input: "#161618", + surface: "#18181B", + hover: "#27272A", + border: "#1E1E1E", + "border-input": "#27272A", + "text-1": "#E4E4E7", "text-2": "#A1A1AA", "text-3": "#71717A", "text-4": "#52525B", "text-5": "#3F3F46", + accent: "#3CE6AC", + danger: "#EF4444", }, }, }, diff --git a/packages/studio/vite.config.ts b/packages/studio/vite.config.ts index fd213fd6f..a5e661549 100644 --- a/packages/studio/vite.config.ts +++ b/packages/studio/vite.config.ts @@ -192,4 +192,7 @@ export default defineConfig({ // "Dynamic require of fs is not supported". Browser bundles never reach them. external: ["recast", "@babel/parser", "ast-types"], }, + test: { + exclude: ["data/**", "node_modules/**"], + }, });