--- phase: 41-diagrams-icons-theme-engine plan: "02" subsystem: api tags: [diagram-renderer, icon-renderer, playwright, dompurify, resvg, svgo, sharp, llm-synthesis, mermaid, tdd] # Dependency graph requires: - phase: 41-01 provides: RenderResult/DiagramBundle/IconSetBundle types, renderer stubs, server deps installed provides: - server/src/services/renderers/diagram-renderer.ts — LLM prompt synthesis + Playwright Mermaid render + DOMPurify + Resvg - server/src/services/renderers/icon-renderer.ts — LLM SVG icon generation + SVGO cleanup + sharp PNG rasterization - server/src/services/puter-inference.ts — shared non-streaming LLM completion helper (Puter API) - server/src/__tests__/diagram-renderer.test.ts — 18 tests for stripUnsafeDirectives, buildDiagramPrompt, renderDiagram - server/src/__tests__/icon-renderer.test.ts — 12 tests for validateAndCleanSvg, renderIconSet affects: [41-03-theme, 41-05-ui-generator, 41-06-ui-theme] # Tech tracking tech-stack: added: [] patterns: - "puterChatComplete: non-streaming LLM helper via PUTER_AUTH_TOKEN env var + Puter AI proxy (OpenAI-compatible)" - "stripUnsafeDirectives: regex-based Mermaid security — removes %%{init}%% blocks and click directives before render" - "renderDiagram: prompt -> LLM Mermaid synthesis -> stripUnsafeDirectives -> Playwright headless -> DOMPurify -> Resvg PNG" - "validateAndCleanSvg: SVGO preset-default + viewBox/xmlns normalization + drawable element presence check" - "renderIconSet: LLM JSON array -> validateAndCleanSvg each -> sharp rasterize 16/32/64 -> IconSetBundle" - "JSON retry pattern: parse fail -> retry LLM with explicit JSON-only follow-up message" key-files: created: - server/src/services/renderers/diagram-renderer.ts - server/src/services/renderers/icon-renderer.ts - server/src/services/puter-inference.ts - server/src/__tests__/diagram-renderer.test.ts - server/src/__tests__/icon-renderer.test.ts modified: [] key-decisions: - "puter-inference.ts created as shared non-streaming helper — avoids duplicating Puter fetch boilerplate in each renderer" - "resolveBrowserPath uses built-in fs/path (not glob npm dep) — simpler, no new dependency" - "DOMPurify window cast uses 'any' — @types/dompurify WindowLike interface incompatible with JSDOM window; type-safe cast not possible" - "SVGO preset-default is applied before drawable element check — SVGO removes degenerate paths so test SVGs must use complete path data" # Metrics duration: 10min completed: 2026-04-04 --- # Phase 41 Plan 02: Diagram Renderer + Icon Renderer Summary **Diagram renderer synthesizes Mermaid from natural language via LLM (DIAG-01), strips unsafe directives (DIAG-05), renders SVG+PNG via Playwright+DOMPurify+Resvg; icon renderer generates SVG icon sets via LLM, cleans with SVGO, rasterizes to 3 PNG sizes via sharp** ## Performance - **Duration:** ~10 min - **Started:** 2026-04-04T20:36:38Z - **Completed:** 2026-04-04T20:42:00Z - **Tasks:** 2 - **Files created/modified:** 5 ## Accomplishments - Diagram renderer: full LLM->Mermaid->Playwright->SVG->PNG pipeline with security stripping - Icon renderer: full LLM->JSON->SVGO->sharp->PNG bundle pipeline with retry and partial failure handling - puter-inference.ts: reusable non-streaming LLM helper for all server-side renderers - 30 total tests passing (18 diagram + 12 icon), all TDD RED->GREEN - TypeScript passes cleanly with `pnpm tsc --noEmit` - Both renderers replace their Plan 01 stubs and conform to RenderResult contract ## Task Commits 1. **Task 1: Diagram renderer with LLM synthesis + Playwright + security + tests** - `9c3146fd` (feat) 2. **Task 2: Icon renderer with LLM SVG generation + SVGO + PNG variants + tests** - `175dce3b` (feat) ## Files Created - `server/src/services/renderers/diagram-renderer.ts` - Full implementation replacing stub from Plan 01 - `server/src/services/renderers/icon-renderer.ts` - Full implementation replacing stub from Plan 01 - `server/src/services/puter-inference.ts` - Shared non-streaming LLM completion via Puter AI proxy - `server/src/__tests__/diagram-renderer.test.ts` - 18 tests: stripUnsafeDirectives (5), buildDiagramPrompt (7), renderDiagram (6) - `server/src/__tests__/icon-renderer.test.ts` - 12 tests: validateAndCleanSvg (6), renderIconSet (6) ## Decisions Made - Created `puter-inference.ts` as a shared helper instead of inlining Puter fetch in each renderer. All renderer LLM calls flow through this one module, which reads `PUTER_AUTH_TOKEN` from environment. - Used Node.js `fs`/`path` for browser path resolution instead of adding `glob` as a dependency. - DOMPurify type cast uses `any` because `@types/dompurify`'s `WindowLike` interface does not match JSDOM's window type exactly — this is a known type incompatibility. ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 3 - Blocking] Replaced glob import with built-in fs/path** - **Found during:** Task 1 (GREEN phase) - **Issue:** `import { glob } from "glob"` failed — `glob` is not installed in server package - **Fix:** Implemented a simple two-level directory scan using Node.js `fs.readdirSync` and `fs.existsSync` - **Files modified:** server/src/services/renderers/diagram-renderer.ts - **Commit:** 9c3146fd **2. [Rule 1 - Bug] Fixed test SVG for "adds xmlns when missing" test** - **Found during:** Task 2 (GREEN phase test run) - **Issue:** SVGO `preset-default` correctly removes degenerate SVG paths (e.g. `d="M12 2"` — just a move, no shape). The test used this degenerate path, so after SVGO the SVG had no drawable elements and `validateAndCleanSvg` returned `valid: false`. - **Fix:** Updated test SVG to use a complete triangle path `d="M12 2L2 7l10 5z"` that SVGO preserves - **Files modified:** server/src/__tests__/icon-renderer.test.ts - **Commit:** 175dce3b **3. [Rule 1 - Bug] Fixed DOMPurify window type cast** - **Found during:** Task 1 tsc check - **Issue:** `window as unknown as Window` caused TS2345 error — JSDOM's window type is not assignable to `WindowLike` - **Fix:** Changed cast to `window as any` with eslint-disable comments - **Files modified:** server/src/services/renderers/diagram-renderer.ts - **Commit:** 175dce3b --- **Total deviations:** 3 auto-fixed (Rules 1 and 3 — blocking and bug) **Impact on plan:** No scope change. All fixes are minor corrections that maintain the intent of the plan. ## Known Stubs None — both renderers are fully implemented. All Plan 01 stubs have been replaced. ## User Setup Required - `PUTER_AUTH_TOKEN` environment variable must be set for LLM calls to work at runtime - `npx playwright install chromium` must be run (or `PLAYWRIGHT_BROWSERS_PATH` set) for diagram rendering at runtime ## Next Phase Readiness - Plan 41-03 (theme renderer) can now follow the same pattern: `puterChatComplete` for LLM + `RenderResult` return - puter-inference.ts is ready for reuse in theme-renderer.ts - All Phase 41 content types (diagram, icon, theme) share the same job runner → renderer dispatch pattern --- *Phase: 41-diagrams-icons-theme-engine* *Completed: 2026-04-04*