# Technology Stack: v1.7 Content Generation **Project:** Nexus v1.7 — additive to v1.6 stack (see prior STACK.md for voice pipeline, Telegram bridge, ffmpeg-static, grammy, @ricky0123/vad-react) **Researched:** 2026-04-04 **Scope:** NEW libraries only for v1.7 — presentations/video, image generation, diagram rendering, PDF generation, SVG icons, color/theme tools, social media assets **Confidence:** MEDIUM-HIGH (Remotion HIGH via official docs; satori+resvg-js HIGH via official repos; mermaid already installed; ComfyUI client MEDIUM; culori MEDIUM via comparison sources) --- ## Context: What Is Already Installed Do not re-add or re-research these — confirmed present in `server/package.json` or `ui/package.json`: | Package | Location | Version | Relevant To v1.7 | |---------|----------|---------|-----------------| | `sharp ^0.34.5` | `server/` | 0.34.5 | SVG→PNG conversion, image compositing, social asset output | | `mermaid ^11.12.0` | `ui/` | 11.12.0 | Client-side diagram rendering already works | | `ffmpeg-static ^5.3.0` | `server/` | 5.3.0 | Video stitching, audio for Remotion renders | | `zod ^3.24.2` | `server/` | 3.24.2 | Schema validation for content generation requests | | `express ^5.1.0` | `server/` | 5.1.0 | API endpoints for all content generation routes | The v1.7 work **adds** new rendering capabilities — it does not replace or duplicate any of the above. --- ## New Libraries by Feature Area ### 1. Presentations & Video Generation **Packages:** `remotion`, `@remotion/bundler`, `@remotion/renderer` **Version:** `^4.0.443` (latest — all Remotion packages must share the same version) **Where it lives:** New workspace package `packages/content-renderer/` (isolated — Remotion uses its own webpack pipeline) **Why Remotion:** - React-native: slides/presentations are React components — agents generate TSX, Remotion renders to MP4 or PNG sequences - SSR API (`@remotion/renderer`) works in Node.js without a browser UI; the Express server calls it directly - Remotion bundles its own Chromium headless shell — no separate Chrome install on Mac Mini - `ffmpeg-static` is already installed and Remotion detects it automatically (`ensureFfmpeg()`) - Official Express render-server template exists: `remotion-dev/template-render-server` — proven integration pattern - Mac M4/Apple Silicon: Remotion downloads a macOS arm64 Chromium binary; confirmed working **Why NOT Remotion Lambda/Cloud:** This is a single-user Mac Mini deployment. No serverless. Local rendering only. **Rendering flow:** ``` Agent generates TSX composition → POST /api/content/render-video → @remotion/bundler: bundle(entryPoint) → tmpDir → @remotion/renderer: renderMedia({ composition, serveUrl, outputLocation }) → returns MP4 path in Nexus file system ``` **License note:** Remotion Skills (Claude Code integration) requires a commercial license for companies ≥4 employees. Nexus is single-user personal use — free tier applies. **Confidence: HIGH** — Official SSR docs verified at remotion.dev/docs/ssr. Express template confirmed at github.com/remotion-dev/template-render-server. --- ### 2. Diagram Generation (Mermaid → SVG/PNG, Server-Side) **Package:** `@mermaid-js/mermaid-cli` **Version:** `^11.12.0` **Where it lives:** `server/` — invoked as a programmatic API, not a CLI subprocess **Why:** `mermaid ^11.12.0` is already installed in `ui/` for client-side rendering. For server-side rendering (agent → stored SVG/PNG file), the CLI package exposes a Node.js API: ```typescript import { run } from "@mermaid-js/mermaid-cli"; await run(inputMmdFile, outputSvgFile, { outputFormat: "svg" }); ``` **Why NOT headless-mermaid or puppeteer-based alternatives:** - `headless-mermaid` is unmaintained (last update 2022) - `@mermaid-js/mermaid-cli` is the official tool, maintained by the mermaid-js org, same version as the `mermaid` npm package - It uses puppeteer internally but packages everything — no additional Chrome install needed **Important:** `@mermaid-js/mermaid-cli` installs its own puppeteer with bundled Chromium. On Mac M4, this is a separate ~300MB download from Remotion's Chromium. Consider: if Remotion is already installed and its Chromium binary is available, pipe mermaid rendering through `@remotion/renderer` instead to share the Chromium binary. This is an implementation optimization, not a blocker. **Confidence: MEDIUM** — Official npm package, same version as mermaid. Programmatic API (`run()`) confirmed in README. --- ### 3. PDF Generation (Reports, Invoices) **Package:** `playwright-chromium` **Version:** `^1.50.0` (do NOT use `^1.59.1` from root devDependencies — that's for e2e tests; install a separate `playwright-chromium` in `server/`) **Where it lives:** `server/` **Why Playwright over Puppeteer:** - 2026 benchmark (macOS arm64, Node 22): Playwright is 42ms cold vs Puppeteer's 147ms cold, 3ms warm vs 48ms warm - `playwright-chromium` installs only the Chromium binary — no Firefox or WebKit overhead - TypeScript-native, no `@types/playwright` needed - Actively maintained by Microsoft; Puppeteer-core is maintained by Google but Playwright has overtaken it for new projects as of 2025-2026 **Why NOT puppeteer-core:** - Slower at every data point in 2026 benchmarks - `playwright-chromium` provides an equally thin package (Chromium only) at better performance **Why NOT @remotion/renderer for PDFs:** - Remotion is optimized for video frame rendering, not document layout - Playwright renders HTML/CSS directly via Chrome's print-to-PDF — correct page breaks, headers, footers, print stylesheet support **PDF generation pattern:** ```typescript import { chromium } from "playwright-chromium"; const browser = await chromium.launch({ headless: true }); const page = await browser.newPage(); await page.setContent(htmlString, { waitUntil: "networkidle" }); const pdf = await page.pdf({ format: "A4", printBackground: true }); await browser.close(); return pdf; // Buffer ``` **Mac M4 note:** `playwright-chromium` downloads a macOS arm64 Chromium binary via `npx playwright install chromium`. This is yet another Chromium binary. Consider sharing with Remotion's binary — implementation detail, investigate during Phase execution. **Confidence: HIGH** — Playwright PDF generation confirmed at pptr.dev equivalent, benchmark sourced from pdf4.dev/blog/html-to-pdf-benchmark-2026 (March 2026, macOS arm64 run). --- ### 4. Social Media Images & OG Images (Satori + resvg-js) **Packages:** `satori`, `resvg-js` **Versions:** `satori ^0.26.0`, `resvg-js ^2.6.2` **Where it lives:** `server/` **Why satori:** - Converts React JSX (HTML/CSS subset) to SVG — no browser needed, pure Node.js - Used by `@vercel/og` internally — battle-tested for exactly this use case (OG images, social cards) - Agents generate JSX layouts describing the social post design; satori renders to SVG - Supports TTF/OTF/WOFF fonts — can embed the Nexus brand font **Why resvg-js (paired with satori):** - Converts the SVG output from satori → PNG via Rust bindings (napi-rs) - No headless browser needed — pure native Node.js module - `sharp ^0.34.5` is already installed and can do SVG→PNG too, but resvg-js handles text rendering more accurately when the SVG uses custom fonts embedded by satori **When to use sharp vs resvg-js:** - `sharp`: image compositing, resizing, format conversion (WebP, AVIF), photo manipulation - `resvg-js`: satori SVG → PNG with correct font rendering **Social media output flow:** ``` Agent generates JSX layout + text → satori(jsx, { width: 1200, height: 630, fonts: [...] }) → SVG string → resvg-js Resvg(svgString).render().asPng() → PNG Buffer → sharp(pngBuffer).resize(width, height).webp().toBuffer() → final asset ``` **Platform dimensions (built-in config):** | Platform | Size | |----------|------| | OpenGraph | 1200×630 | | Twitter/X card | 1200×628 | | Instagram square | 1080×1080 | | Instagram story | 1080×1920 | | LinkedIn post | 1200×627 | **Confidence: HIGH** — satori GitHub (vercel/satori) reviewed, resvg-js GitHub (thx/resvg-js) reviewed. Pattern confirmed in multiple tutorials. Versions confirmed via npm registry. --- ### 5. SVG Icon Generation **No new library needed.** The existing stack already covers this: - `sharp ^0.34.5`: can rasterize SVG to PNG at any resolution - `resvg-js ^2.6.2` (added above): accurate SVG rendering with custom fonts - Template-based SVG generation: agents produce SVG strings using a template library or string composition **Approach:** Agents generate SVG markup directly (simple geometric shapes, paths, text). The server validates the SVG, stores it in the file system, and optionally exports PNG variants via `sharp`. No additional icon library needed. **Why NOT `svg.js` or `svg-builder`:** These are authoring libraries for complex interactive SVG manipulation in the browser. For agent-generated icons, the agent produces the SVG string — the server only needs to validate and render it. **Confidence: HIGH** — this is an implementation decision, not a library gap. --- ### 6. Theme & Palette Generator with WCAG Contrast **Package:** `culori` **Version:** `^4.0.2` **Where it lives:** `server/` and optionally `ui/` (tree-shakeable ESM) **Why culori over chroma-js:** - 2026 community consensus: "OKLCH is the future of CSS color — use culori for any design-system work" - culori supports the OKLCH color space natively; chroma-js 3.x has limited OKLCH support - WCAG contrast calculation in culori is more accurate (proper relative luminance implementation) - culori is fully ESM + CJS with tree-shaking; chroma-js 3.x is CJS-first with ESM wrapper - Both support WCAG contrast ratio, but culori's implementation is described as "most accurate" in 2026 comparisons **Why NOT tinycolor2:** Minimal WCAG support, no OKLCH, not maintained for design-system work. **WCAG AA validation pattern:** ```typescript import { wcagContrast, oklch, formatHex } from "culori"; function meetsWcagAA(fg: string, bg: string): boolean { return wcagContrast(fg, bg) >= 4.5; // AA for normal text } function generateAccessiblePalette(baseHex: string) { const base = oklch(baseHex); // Generate shades by adjusting lightness in OKLCH space return [0.95, 0.85, 0.70, 0.55, 0.40, 0.25, 0.15].map(l => ({ hex: formatHex({ ...base, l }), contrast: wcagContrast(formatHex({ ...base, l }), "#ffffff"), })); } ``` **Confidence: MEDIUM** — culori version verified via npm (4.0.2). WCAG accuracy claim sourced from pkgpulse.com comparison article (2026). Official culori docs reviewed. --- ### 7. Local Image Generation (Stable Diffusion / Flux) **Package:** `@stable-canvas/comfyui-client` **Version:** `^1.5.9` **Where it lives:** `server/` — optional, only used when ComfyUI is detected running locally **Why ComfyUI + this client:** - ComfyUI is the dominant local Stable Diffusion frontend in 2026 (alongside AUTOMATIC1111/Forge) - It exposes a REST + WebSocket API that this client wraps with TypeScript types - On Mac M4 (Apple Silicon), ComfyUI runs via Metal/MPS backend — confirmed working with Flux.1 models - The client is zero-dependency, MIT licensed, supports both REST (sync) and WebSocket (streaming progress) APIs **Why NOT direct Stable Diffusion Python bindings:** - Mac M4 has no CUDA; Python SD libraries that require CUDA don't work - ComfyUI abstracts the backend (Metal/MPS on Mac) behind a standard REST API - Nexus agents call the REST API — they don't care about the backend hardware **Architecture:** ComfyUI runs as a separate process (not managed by Nexus). Nexus detects it via health check on `http://localhost:8188` at startup. If not running, image generation features degrade gracefully with a "ComfyUI not available" message. **Integration pattern:** ```typescript import { Client } from "@stable-canvas/comfyui-client"; const client = new Client({ api_host: "localhost:8188" }); await client.init(); // WebSocket handshake const result = await client.enqueue(workflowJson, { progress: (p) => sendSSEProgress(p), }); // result.images[0] is a Buffer ``` **Confidence: MEDIUM** — Client package confirmed on npm (1.5.9, MIT, zero deps). ComfyUI Mac M4 support sourced from offlinecreator.com (March 2026). WebSocket API pattern confirmed via GitHub README. --- ## Installation Summary ```bash # packages/content-renderer/ — new workspace package for Remotion # (Remotion needs its own webpack pipeline; isolate from main server build) pnpm --filter @paperclipai/content-renderer add remotion @remotion/bundler @remotion/renderer # server/ — diagram rendering (server-side mermaid) pnpm --filter @paperclipai/server add @mermaid-js/mermaid-cli # server/ — PDF generation pnpm --filter @paperclipai/server add playwright-chromium # After install: npx playwright install chromium # server/ — social images and OG cards (no-browser SVG→PNG pipeline) pnpm --filter @paperclipai/server add satori resvg-js # server/ — color/theme/palette with WCAG pnpm --filter @paperclipai/server add culori # server/ — local image generation via ComfyUI (optional, graceful degradation) pnpm --filter @paperclipai/server add @stable-canvas/comfyui-client # ui/ — culori for live preview (tree-shakeable ESM, safe to add to both) pnpm --filter @paperclipai/ui add culori ``` --- ## What NOT to Add | Avoid | Why | Use Instead | |-------|-----|-------------| | `puppeteer-core` | Slower than Playwright in 2026 benchmarks (42ms vs 147ms cold on macOS arm64); Playwright has overtaken it | `playwright-chromium ^1.50.0` | | `@remotion/lambda` | Serverless — Nexus is Mac Mini local-only, no AWS account needed | `@remotion/renderer` local rendering | | `headless-mermaid` | Unmaintained since 2022 | `@mermaid-js/mermaid-cli ^11.12.0` (official) | | `chroma-js` | Limited OKLCH support; culori more accurate for WCAG; community consensus favors culori for design-system work in 2026 | `culori ^4.0.2` | | `tinycolor2` | Minimal WCAG support, no OKLCH, not suited for palette/design-system work | `culori ^4.0.2` | | `canvas` (`node-canvas`) | C++ bindings, complicated install, replaced by `sharp` + `resvg-js` for server-side image ops | `sharp` (already installed) + `resvg-js` | | `jimp` | Pure JS image processing, slow for production image generation workloads | `sharp ^0.34.5` (already installed, libvips-backed) | | `@ffmpeg/ffmpeg` (WASM) | WASM FFmpeg is 10× slower than native binary; intended for browser/serverless | `ffmpeg-static ^5.3.0` (already installed) | | `svg.js` / `svg-builder` | Browser authoring libraries; agents generate SVG strings directly | Agent-generated SVG + `sharp`/`resvg-js` for rendering | | `@vercel/og` | Wraps satori+resvg but adds Vercel Edge Runtime constraints; unnecessary wrapper | `satori` + `resvg-js` directly | | `pdf-lib` | Pure JS PDF creation — no HTML rendering. Correct for form-filling or programmatic PDF assembly, wrong for agent-generated HTML reports | `playwright-chromium` (HTML→PDF) | | ComfyUI with cloud API (Replicate, etc.) | Nexus is air-gap friendly; Mikkel has local GPU (Mac M4) | `@stable-canvas/comfyui-client` pointing to `localhost:8188` | --- ## Alternatives Considered | Recommended | Alternative | When to Use Alternative | |-------------|-------------|-------------------------| | `@remotion/renderer` (local) | `@remotion/lambda` | If you need parallel cloud rendering at scale — N/A for single-user Mac Mini | | `playwright-chromium` | `puppeteer-core` | If project already has Puppeteer deeply integrated; for new work prefer Playwright | | `@mermaid-js/mermaid-cli` (Node API) | `child_process.spawn("mmdc")` | If you need a fully isolated subprocess; Node API is cleaner for the server pattern | | `culori` | `chroma-js ^3.2.0` | If project requires many color interpolation methods (chroma has a richer palette generation API); for WCAG-first design systems culori wins | | `satori` + `resvg-js` | `playwright-chromium` for OG images | Playwright is simpler but adds another headless browser instance; satori is ~100× faster for simple card layouts with no JS | | `@stable-canvas/comfyui-client` | Direct `fetch` to ComfyUI REST API | If ComfyUI API changes and client lags; raw fetch is always viable since ComfyUI API is stable JSON | --- ## Version Compatibility | Package | Compatible With | Notes | |---------|-----------------|-------| | `remotion ^4.0.443` | Node.js >=18, React >=18, TypeScript >=5 | All `@remotion/*` packages must be pinned to the same version; `ffmpeg-static` already installed is detected automatically | | `@mermaid-js/mermaid-cli ^11.12.0` | Node.js >=18, puppeteer (bundled) | Installs its own bundled Chromium (~300MB); version must match `mermaid ^11.12.0` already in `ui/` | | `playwright-chromium ^1.50.0` | Node.js >=18, macOS arm64 | Separate Chromium binary from Remotion and mermaid-cli. Requires `npx playwright install chromium` post-install | | `satori ^0.26.0` | Node.js >=16, ESM | Supports TTF/OTF/WOFF fonts only — WOFF2 is NOT supported. Subset fonts for performance | | `resvg-js ^2.6.2` | Node.js >=14, macOS arm64 | Rust napi-rs binary; downloads macOS arm64 binary at install time | | `culori ^4.0.2` | Node.js >=14, ESM + CJS | Dual package; safe in both `server/` (CJS/ESM mix) and `ui/` (Vite ESM) | | `@stable-canvas/comfyui-client ^1.5.9` | Node.js >=16, zero deps | Requires ComfyUI running at localhost:8188; graceful degradation if not available | **Chromium binary count warning:** v1.7 potentially installs THREE separate Chromium binaries: 1. `remotion` — `~/.cache/puppeteer/chrome/` or Remotion's own cache 2. `@mermaid-js/mermaid-cli` — puppeteer bundled binary 3. `playwright-chromium` — `~/.cache/ms-playwright/` Total disk: ~900MB. For the Mac Mini (256GB+ SSD) this is acceptable. During implementation, investigate whether `@mermaid-js/mermaid-cli` can be pointed at Playwright's Chromium via `PUPPETEER_EXECUTABLE_PATH` to reduce redundancy. --- ## Content Generation Package Architecture ``` nexus/ ├── packages/ │ └── content-renderer/ ← NEW workspace package │ ├── package.json (remotion, @remotion/bundler, @remotion/renderer) │ ├── src/ │ │ ├── compositions/ (React TSX slide templates) │ │ └── index.ts (render() export) │ └── tsconfig.json │ ├── server/ │ └── src/ │ └── content/ │ ├── diagram.ts (@mermaid-js/mermaid-cli) │ ├── pdf.ts (playwright-chromium) │ ├── social.ts (satori + resvg-js + sharp) │ ├── theme.ts (culori) │ ├── image.ts (@stable-canvas/comfyui-client) │ └── index.ts (route registrations) │ └── ui/ └── src/ └── content/ └── ThemePreview.tsx (culori for live WCAG preview) ``` **Why isolate Remotion in its own package:** - Remotion uses its own webpack bundler (`@remotion/bundler`) that conflicts with Vite - Keeping it in `packages/content-renderer/` prevents build pipeline interference - The Express server imports only the render function, not the full webpack config --- ## Sources - [Remotion official SSR docs](https://www.remotion.dev/docs/ssr) — `@remotion/renderer` Node.js API confirmed - [Remotion Express render-server template](https://github.com/remotion-dev/template-render-server) — Express integration pattern - [Remotion Chrome headless shell docs](https://www.remotion.dev/docs/miscellaneous/chrome-headless-shell) — Mac arm64 binary confirmed - [remotion npm](https://www.npmjs.com/package/remotion) — version 4.0.443 confirmed - [mermaid-cli GitHub](https://github.com/mermaid-js/mermaid-cli) — Node.js `run()` API, v11.12.0 - [playwright-chromium npm](https://www.npmjs.com/package/playwright-chromium) — Chromium-only package - [PDF benchmark 2026](https://pdf4.dev/blog/html-to-pdf-benchmark-2026) — Playwright vs Puppeteer, macOS arm64, Node 22 (MEDIUM confidence — single benchmark source) - [vercel/satori GitHub](https://github.com/vercel/satori) — JSX→SVG, Node.js support, font format constraints - [satori npm](https://www.npmjs.com/package/satori) — version 0.26.0 confirmed - [thx/resvg-js GitHub](https://github.com/thx/resvg-js) — SVG→PNG via Rust napi-rs, macOS arm64 support - [resvg-js npm](https://www.npmjs.com/package/resvg-js) — version 2.6.2 (note: npm showed 0.1.97 for `resvg-js`; the canonical package may be `@resvg/resvg-js` — verify exact package name during implementation) - [culori npm](https://www.npmjs.com/package/culori) — version 4.0.2 confirmed - [culori vs chroma-js 2026](https://www.pkgpulse.com/blog/culori-vs-chroma-js-vs-tinycolor2-color-manipulation-javascript-2026) — OKLCH + WCAG accuracy comparison (MEDIUM confidence) - [@stable-canvas/comfyui-client npm](https://www.npmjs.com/package/@stable-canvas/comfyui-client) — version 1.5.9, MIT, zero deps - [Best local SD setup 2026](https://offlinecreator.com/blog/best-local-stable-diffusion-setup-2026) — ComfyUI Mac M4 support (MEDIUM confidence) --- **Unresolved — verify during implementation:** 1. `resvg-js` package name: npm shows v0.1.97 for `resvg-js` but v2.6.2 for `@resvg/resvg-js`. Use `@resvg/resvg-js ^2.6.2` (the Rust napi-rs backed version). 2. Chromium binary deduplication: test whether `@mermaid-js/mermaid-cli` respects `PUPPETEER_EXECUTABLE_PATH` pointing to Playwright's Chromium binary to save ~300MB. 3. Remotion webpack vs Vite isolation: confirm that `packages/content-renderer/` with its own `tsconfig.json` and webpack bundler does not affect the root `pnpm build` pipeline. --- *Stack research for: Nexus v1.7 Content Generation* *Researched: 2026-04-04* *Supersedes: v1.6 STACK.md entries remain valid — this file covers only v1.7 additions*