nexus/.planning/phases/42-wallpapers-social-format-conversion-voice/42-02-PLAN.md

9.9 KiB

phase plan type wave depends_on files_modified autonomous requirements must_haves
42-wallpapers-social-format-conversion-voice 02 execute 2
42-01
server/src/services/renderers/wallpaper-renderer.ts
server/src/services/renderers/social-renderer.ts
true
WALL-01
WALL-02
WALL-03
WALL-04
SOCIAL-01
SOCIAL-02
SOCIAL-03
truths artifacts key_links
Wallpaper renderer generates SVG via LLM then rasterizes to exact platform dimensions via sharp
PLATFORM_DIMENSIONS is an exported constant, not magic numbers anywhere
App icon/favicon request produces multi-size bundle (1024, 512, 256, 64, 32)
Social renderer generates post text + hashtags as JSON via LLM
Instagram carousel returns slides array with per-slide character limit
LLM JSON output is parsed with markdown fence stripping
path provides exports
server/src/services/renderers/wallpaper-renderer.ts renderWallpaper function + PLATFORM_DIMENSIONS constant
renderWallpaper
PLATFORM_DIMENSIONS
path provides exports
server/src/services/renderers/social-renderer.ts renderSocialPost function + PLATFORM_CHAR_LIMITS constant
renderSocialPost
PLATFORM_CHAR_LIMITS
from to via pattern
server/src/services/renderers/wallpaper-renderer.ts server/src/services/puter-inference.ts puterChatComplete for SVG generation puterChatComplete
from to via pattern
server/src/services/renderers/wallpaper-renderer.ts sharp SVG to PNG rasterization at target dimensions sharp.*density.*300
from to via pattern
server/src/services/renderers/social-renderer.ts server/src/services/puter-inference.ts puterChatComplete for post generation puterChatComplete
Implement the wallpaper renderer (LLM SVG + sharp rasterization at platform dimensions) and social post renderer (LLM JSON with hashtags and carousel support).

Purpose: These renderers fulfill all WALL and SOCIAL requirements. The UI panels in Plan 05 depend on these renderers being functional. Output: Two working renderer files replacing the Plan 01 stubs.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/42-wallpapers-social-format-conversion-voice/42-RESEARCH.md @.planning/phases/42-wallpapers-social-format-conversion-voice/42-01-SUMMARY.md

@server/src/services/renderers/types.ts @server/src/services/renderers/icon-renderer.ts @server/src/services/puter-inference.ts

From server/src/services/renderers/types.ts (after Plan 01): ```typescript export interface WallpaperBundle { type: "wallpaper-bundle"; platform: string; width: number; height: number; pngBase64: string; prompt: string; } export interface AppIconBundle { type: "app-icon-bundle"; sizes: Array<{ size: number; pngBase64: string }>; prompt: string; } export interface SocialPostBundle { type: "social-post-bundle"; platform: string; post: string; hashtags: string[]; slides?: string[]; charLimit: number; } ```

From server/src/services/puter-inference.ts:

export async function puterChatComplete(prompt: string, systemPrompt?: string): Promise<string>;
Task 1: Implement wallpaper renderer with PLATFORM_DIMENSIONS server/src/services/renderers/wallpaper-renderer.ts server/src/services/renderers/icon-renderer.ts, server/src/services/puter-inference.ts, server/src/services/renderers/types.ts Replace the stub wallpaper-renderer.ts with a full implementation:
  1. Export PLATFORM_DIMENSIONS constant as Record<string, { width: number; height: number; label: string }> with all 12 platform entries from research:

    • desktop-hd (2560x1440), desktop-fhd (1920x1080), desktop-4k (3840x2160)
    • mobile-portrait (1080x1920), mobile-landscape (1920x1080)
    • og-image (1200x630), twitter-card (1200x628), instagram-post (1080x1080), instagram-banner (1080x566), linkedin-banner (1584x396)
    • app-icon (1024x1024), favicon (32x32)
  2. Export APP_ICON_SIZES = [1024, 512, 256, 64, 32] as const.

  3. Export async function renderWallpaper(input: Record<string, unknown>): Promise<RenderResult>:

    • Extract prompt (string) and platform (string) from input
    • Look up dimensions from PLATFORM_DIMENSIONS[platform]; throw if not found
    • Call puterChatComplete with a system prompt instructing the LLM to generate an SVG artwork matching the requested aspect ratio. The system prompt must specify:
      • Output ONLY valid SVG (no markdown fences, no explanation)
      • Use viewBox="0 0 {width} {height}" matching target dimensions
      • Create a visually rich scene based on the user prompt
      • Use gradients, shapes, patterns — no text elements
    • Strip markdown fences from response (same pattern as icon-renderer.ts)
    • For app-icon or favicon platform: render multi-size bundle using sharp at each APP_ICON_SIZES, return AppIconBundle with sizes array
    • For all other platforms: rasterize SVG to PNG using sharp(Buffer.from(svgString), { density: 300 }).resize(width, height, { fit: 'fill' }).png({ compressionLevel: 9 }).toBuffer()
    • Return RenderResult with filename wallpaper-{platform}.png, contentType image/png
    • Store bundle in job (WallpaperBundle or AppIconBundle) by setting it on the result

CRITICAL: Always pass { density: 300 } to sharp when loading SVG to avoid blurry rasterization at large dimensions (Pitfall 1 from research).

Follow the exact pattern from icon-renderer.ts for LLM prompt structure and SVG extraction. cd /opt/nexus/server && npx tsc --noEmit 2>&1 | head -20 <acceptance_criteria> - grep "PLATFORM_DIMENSIONS" server/src/services/renderers/wallpaper-renderer.ts - grep "APP_ICON_SIZES" server/src/services/renderers/wallpaper-renderer.ts - grep "density: 300" server/src/services/renderers/wallpaper-renderer.ts - grep "puterChatComplete" server/src/services/renderers/wallpaper-renderer.ts - grep "renderWallpaper" server/src/services/renderers/wallpaper-renderer.ts - grep "2560" server/src/services/renderers/wallpaper-renderer.ts - grep "og-image" server/src/services/renderers/wallpaper-renderer.ts </acceptance_criteria> Wallpaper renderer generates LLM SVG and rasterizes at exact platform dimensions. PLATFORM_DIMENSIONS exported as constant. App icon generates multi-size bundle.

Task 2: Implement social post renderer with hashtags and carousel server/src/services/renderers/social-renderer.ts server/src/services/renderers/icon-renderer.ts, server/src/services/puter-inference.ts Replace the stub social-renderer.ts with a full implementation:
  1. Export PLATFORM_CHAR_LIMITS constant as Record<string, number>:

    • "twitter-x": 280
    • "linkedin": 3000
    • "instagram-caption": 2200
    • "instagram-carousel": 300 (per slide)
  2. Export async function renderSocialPost(input: Record<string, unknown>): Promise<RenderResult>:

    • Extract prompt (string) and platform (string) from input
    • Look up charLimit from PLATFORM_CHAR_LIMITS[platform]; throw if not found
    • Call puterChatComplete with a system prompt:
      • For non-carousel platforms: "Generate a {platform} post under {charLimit} characters. Also suggest 3-5 relevant hashtags. Return JSON only: { "post": "...", "hashtags": ["#tag1", "#tag2"] }"
      • For instagram-carousel: "Generate an Instagram carousel with 5-10 slides, each under {charLimit} characters. Also suggest 3-5 relevant hashtags. Return JSON only: { "post": "intro caption", "slides": ["slide 1 text", "slide 2 text"], "hashtags": ["#tag1"] }"
    • Parse LLM response: strip markdown fences (json ... ) before JSON.parse. Use robust extraction: const match = raw.match(/```json\s*([\s\S]*?)\s*```/) || raw.match(/(\{[\s\S]*\})/); JSON.parse(match ? match[1] : raw) (Pitfall 5 from research)
    • Build SocialPostBundle from parsed JSON
    • Return RenderResult with filename social-{platform}.json, contentType application/json, buffer from JSON.stringify of bundle

Follow the SVG extraction pattern from icon-renderer.ts for robustness. cd /opt/nexus/server && npx tsc --noEmit 2>&1 | head -20 <acceptance_criteria> - grep "PLATFORM_CHAR_LIMITS" server/src/services/renderers/social-renderer.ts - grep "renderSocialPost" server/src/services/renderers/social-renderer.ts - grep "puterChatComplete" server/src/services/renderers/social-renderer.ts - grep "twitter-x" server/src/services/renderers/social-renderer.ts - grep "instagram-carousel" server/src/services/renderers/social-renderer.ts - grep "hashtags" server/src/services/renderers/social-renderer.ts - grep "slides" server/src/services/renderers/social-renderer.ts </acceptance_criteria> Social renderer generates platform-aware posts with hashtag suggestions and carousel support via LLM. PLATFORM_CHAR_LIMITS exported as constant.

- `cd /opt/nexus/server && npx tsc --noEmit` passes with zero errors - PLATFORM_DIMENSIONS has all 12 platform entries - PLATFORM_CHAR_LIMITS has all 4 platform entries - Both renderers use puterChatComplete and strip markdown fences from LLM output

<success_criteria>

  • Wallpaper renderer rasterizes LLM-generated SVG at exact platform dimensions
  • Social renderer generates posts with hashtags as JSON via LLM
  • All dimensions are constants, never magic numbers
  • tsc compiles cleanly </success_criteria>
After completion, create `.planning/phases/42-wallpapers-social-format-conversion-voice/42-02-SUMMARY.md`