205 lines
9.9 KiB
Markdown
205 lines
9.9 KiB
Markdown
---
|
|
phase: 42-wallpapers-social-format-conversion-voice
|
|
plan: 02
|
|
type: execute
|
|
wave: 2
|
|
depends_on: [42-01]
|
|
files_modified:
|
|
- server/src/services/renderers/wallpaper-renderer.ts
|
|
- server/src/services/renderers/social-renderer.ts
|
|
autonomous: true
|
|
requirements: [WALL-01, WALL-02, WALL-03, WALL-04, SOCIAL-01, SOCIAL-02, SOCIAL-03]
|
|
must_haves:
|
|
truths:
|
|
- "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"
|
|
artifacts:
|
|
- path: "server/src/services/renderers/wallpaper-renderer.ts"
|
|
provides: "renderWallpaper function + PLATFORM_DIMENSIONS constant"
|
|
exports: ["renderWallpaper", "PLATFORM_DIMENSIONS"]
|
|
- path: "server/src/services/renderers/social-renderer.ts"
|
|
provides: "renderSocialPost function + PLATFORM_CHAR_LIMITS constant"
|
|
exports: ["renderSocialPost", "PLATFORM_CHAR_LIMITS"]
|
|
key_links:
|
|
- from: "server/src/services/renderers/wallpaper-renderer.ts"
|
|
to: "server/src/services/puter-inference.ts"
|
|
via: "puterChatComplete for SVG generation"
|
|
pattern: "puterChatComplete"
|
|
- from: "server/src/services/renderers/wallpaper-renderer.ts"
|
|
to: "sharp"
|
|
via: "SVG to PNG rasterization at target dimensions"
|
|
pattern: "sharp.*density.*300"
|
|
- from: "server/src/services/renderers/social-renderer.ts"
|
|
to: "server/src/services/puter-inference.ts"
|
|
via: "puterChatComplete for post generation"
|
|
pattern: "puterChatComplete"
|
|
---
|
|
|
|
<objective>
|
|
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.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
|
@$HOME/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<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
|
|
</context>
|
|
|
|
<interfaces>
|
|
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:
|
|
```typescript
|
|
export async function puterChatComplete(prompt: string, systemPrompt?: string): Promise<string>;
|
|
```
|
|
</interfaces>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Implement wallpaper renderer with PLATFORM_DIMENSIONS</name>
|
|
<files>server/src/services/renderers/wallpaper-renderer.ts</files>
|
|
<read_first>server/src/services/renderers/icon-renderer.ts, server/src/services/puter-inference.ts, server/src/services/renderers/types.ts</read_first>
|
|
<action>
|
|
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.
|
|
</action>
|
|
<verify>
|
|
<automated>cd /opt/nexus/server && npx tsc --noEmit 2>&1 | head -20</automated>
|
|
</verify>
|
|
<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>
|
|
<done>Wallpaper renderer generates LLM SVG and rasterizes at exact platform dimensions. PLATFORM_DIMENSIONS exported as constant. App icon generates multi-size bundle.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Implement social post renderer with hashtags and carousel</name>
|
|
<files>server/src/services/renderers/social-renderer.ts</files>
|
|
<read_first>server/src/services/renderers/icon-renderer.ts, server/src/services/puter-inference.ts</read_first>
|
|
<action>
|
|
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.
|
|
</action>
|
|
<verify>
|
|
<automated>cd /opt/nexus/server && npx tsc --noEmit 2>&1 | head -20</automated>
|
|
</verify>
|
|
<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>
|
|
<done>Social renderer generates platform-aware posts with hashtag suggestions and carousel support via LLM. PLATFORM_CHAR_LIMITS exported as constant.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
- `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
|
|
</verification>
|
|
|
|
<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>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/42-wallpapers-social-format-conversion-voice/42-02-SUMMARY.md`
|
|
</output>
|