--- phase: 42-wallpapers-social-format-conversion-voice plan: 05 type: execute wave: 3 depends_on: [42-02] files_modified: - ui/src/components/WallpaperGeneratePanel.tsx - ui/src/components/WallpaperPreview.tsx - ui/src/components/SocialPostPanel.tsx - ui/src/components/SocialPostResult.tsx - ui/src/pages/ContentStudio.tsx autonomous: true requirements: [WALL-01, WALL-02, WALL-03, WALL-04, SOCIAL-01, SOCIAL-02, SOCIAL-03] must_haves: truths: - "User can type a prompt, select a platform, and generate a wallpaper/social image" - "Platform selector shows all 12 platform options grouped by Desktop/Mobile/Social/App" - "Wallpaper preview shows the image with download button after job completes" - "App icon/favicon shows multi-size grid with individual download links" - "Social post panel shows character count that turns red over limit" - "Generated post shows copy button and clickable hashtag chips" - "Instagram carousel shows numbered collapsible slide sections" - "ContentStudio has Wallpapers and Social tabs" artifacts: - path: "ui/src/components/WallpaperGeneratePanel.tsx" provides: "Prompt input + platform selector + generate button" contains: "PLATFORM_DIMENSIONS" - path: "ui/src/components/WallpaperPreview.tsx" provides: "Image display + download button + multi-size grid for icons" contains: "Download PNG" - path: "ui/src/components/SocialPostPanel.tsx" provides: "Prompt input + platform selector + character count" contains: "PLATFORM_CHAR_LIMITS" - path: "ui/src/components/SocialPostResult.tsx" provides: "Post display + copy button + hashtag chips + carousel slides" contains: "Copy Post" - path: "ui/src/pages/ContentStudio.tsx" provides: "Wallpapers and Social tabs added to TabsList" contains: "wallpapers" key_links: - from: "ui/src/components/WallpaperGeneratePanel.tsx" to: "ui/src/hooks/useContentJob.ts" via: "useContentJob hook for SSE job tracking" pattern: "useContentJob" - from: "ui/src/components/SocialPostPanel.tsx" to: "ui/src/hooks/useContentJob.ts" via: "useContentJob hook for SSE job tracking" pattern: "useContentJob" - from: "ui/src/pages/ContentStudio.tsx" to: "ui/src/components/WallpaperGeneratePanel.tsx" via: "TabsContent for wallpapers" pattern: "WallpaperGeneratePanel" --- Build the Wallpaper and Social UI panels and extend ContentStudio with new tabs. Purpose: This surfaces the wallpaper and social renderers (Plan 02) to users through the ContentStudio interface. Output: Four new UI components + updated ContentStudio tabs. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.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-UI-SPEC.md @.planning/phases/42-wallpapers-social-format-conversion-voice/42-02-SUMMARY.md @ui/src/pages/ContentStudio.tsx @ui/src/components/DiagramGeneratePanel.tsx @ui/src/components/IconGeneratePanel.tsx @ui/src/hooks/useContentJob.ts @ui/src/api/contentJobs.ts From server/src/services/renderers/wallpaper-renderer.ts (Plan 02): ```typescript export const PLATFORM_DIMENSIONS: Record; export const APP_ICON_SIZES: readonly [1024, 512, 256, 64, 32]; ``` From server/src/services/renderers/social-renderer.ts (Plan 02): ```typescript export const PLATFORM_CHAR_LIMITS: Record; // "twitter-x": 280, "linkedin": 3000, "instagram-caption": 2200, "instagram-carousel": 300 ``` From ui/src/hooks/useContentJob.ts: ```typescript export function useContentJob(companyId: string | null): { job: ContentJob | null; submit: (jobType: string, input: Record) => Promise; status: string; }; ``` Bundle types (job.bundle after status === 'done'): ```typescript interface WallpaperBundle { type: "wallpaper-bundle"; platform: string; width: number; height: number; pngBase64: string; prompt: string; } interface AppIconBundle { type: "app-icon-bundle"; sizes: Array<{ size: number; pngBase64: string }>; prompt: string; } interface SocialPostBundle { type: "social-post-bundle"; platform: string; post: string; hashtags: string[]; slides?: string[]; charLimit: number; } ``` Task 1: Create WallpaperGeneratePanel and WallpaperPreview components ui/src/components/WallpaperGeneratePanel.tsx, ui/src/components/WallpaperPreview.tsx ui/src/components/DiagramGeneratePanel.tsx, ui/src/components/IconGeneratePanel.tsx, ui/src/hooks/useContentJob.ts, ui/src/api/contentJobs.ts 1. Create WallpaperGeneratePanel.tsx following the DiagramGeneratePanel pattern: - Props: { companyId: string } - Define PLATFORM_DIMENSIONS as a local constant (same values as server — 12 entries). Group into categories: Desktop, Mobile, Social, App. - State: prompt (string), platform (string, default "desktop-hd") - Use useContentJob(companyId) hook for job submission and tracking - Render a Card with: - Textarea (4 rows, placeholder: "Describe the scene, mood, or concept...") - Select component for platform. Use optgroup-style grouping (shadcn Select supports groups via SelectGroup + SelectLabel). Each option shows the label with dimensions in text-xs font-mono text-muted-foreground. - "Generate Wallpaper" Button (primary, full-width). Disabled while job is in progress. Shows spinner + "Generating..." when active. - Progress bar (shadcn progress) below button — driven by job.progress from SSE - On submit: call job.submit("wallpaper", { prompt, platform }) - When job.status === "done": render WallpaperPreview with job.bundle 2. Create WallpaperPreview.tsx: - Props: { bundle: WallpaperBundle | AppIconBundle } - For WallpaperBundle (type === "wallpaper-bundle"): - Render image in `max-h-80 object-contain` container - Image alt text: prompt truncated to 100 chars - Below image: "Download PNG" Button (primary) + resolution badge (text-xs font-mono text-muted-foreground showing "{width} x {height}") - Download: create blob URL from base64, trigger download via anchor click - For AppIconBundle (type === "app-icon-bundle"): - Render a grid of size entries. Each cell: size label (e.g. "32 x 32") + Download link - Grid uses responsive columns: 2 cols on mobile, 3 on md, 5 on lg - Empty state: heading "No image yet", body "Describe a scene or mood and pick a platform size to generate a ready-to-use image." Per UI spec: all copy matches the Copywriting Contract exactly. cd /opt/nexus/ui && npx tsc --noEmit 2>&1 | head -20 - grep "PLATFORM_DIMENSIONS" ui/src/components/WallpaperGeneratePanel.tsx - grep "Generate Wallpaper" ui/src/components/WallpaperGeneratePanel.tsx - grep "useContentJob" ui/src/components/WallpaperGeneratePanel.tsx - grep "Download PNG" ui/src/components/WallpaperPreview.tsx - grep "app-icon-bundle" ui/src/components/WallpaperPreview.tsx - grep "No image yet" ui/src/components/WallpaperPreview.tsx - grep "2560" ui/src/components/WallpaperGeneratePanel.tsx Wallpaper panel allows prompt + platform selection, shows progress, displays result with download. App icon shows multi-size grid. Task 2: Create SocialPostPanel, SocialPostResult, and extend ContentStudio tabs ui/src/components/SocialPostPanel.tsx, ui/src/components/SocialPostResult.tsx, ui/src/pages/ContentStudio.tsx ui/src/pages/ContentStudio.tsx, ui/src/components/DiagramGeneratePanel.tsx 1. Create SocialPostPanel.tsx following DiagramGeneratePanel pattern: - Props: { companyId: string } - Define PLATFORM_CHAR_LIMITS as local constant: { "twitter-x": 280, "linkedin": 3000, "instagram-caption": 2200, "instagram-carousel": 300 } - State: prompt (string), platform (string, default "twitter-x"), charCount (number tracking textarea length) - Use useContentJob(companyId) hook - Render Card with: - Textarea (4 rows, placeholder: "Describe the topic or paste existing content to adapt...") - onChange: update prompt and charCount - Platform selector (Select): "Twitter/X", "LinkedIn", "Instagram Caption", "Instagram Carousel" - Character count below textarea, right-aligned: `text-xs text-muted-foreground`. Format: "{N} / {limit}". When over limit: add `text-destructive` class and append " - over limit" - Character count has `aria-live="polite"` for screen readers - "Generate Post" Button (primary). Enabled even when over limit (AI may trim). Shows spinner + "Generating..." when active. - Progress bar below button - On submit: call job.submit("social-post", { prompt, platform }) - When job.status === "done": render SocialPostResult with job.bundle 2. Create SocialPostResult.tsx: - Props: { bundle: SocialPostBundle } - Post text in read-only Card (bg-card, rounded-lg, p-4) - Character count badge inline (text-xs font-mono text-muted-foreground) - "Copy Post" Button (secondary, full-width). On click: copy bundle.post to clipboard. Change text to "Copied!" for 2 seconds then revert. - Hashtag section below copy button: render bundle.hashtags as chips in rounded-full badges (bg-muted text-muted-foreground). Each chip has role="button" and aria-label="Copy hashtag {tag}". On click: copy tag to clipboard. Show CheckCheck icon (12px) for 1.5s then revert to text. - For carousel (bundle.slides exists and length > 0): render numbered collapsible sections using shadcn Collapsible. "Slide 1", "Slide 2" etc. Each section shows the slide text. - Empty state: heading "No post yet", body "Describe your topic and choose a platform to generate a ready-to-publish post." 3. Update ContentStudio.tsx: - Add two new TabsTrigger entries to the existing TabsList: "Wallpapers" (value: wallpapers), "Social" (value: social) - Add two new TabsContent sections: - wallpapers: renders WallpaperGeneratePanel with companyId - social: renders SocialPostPanel with companyId - Import WallpaperGeneratePanel and SocialPostPanel - Follow the exact same pattern as the existing diagrams/icons/themes tabs Per UI spec copywriting contract: match ALL copy strings exactly. cd /opt/nexus/ui && npx tsc --noEmit 2>&1 | head -20 - grep "PLATFORM_CHAR_LIMITS" ui/src/components/SocialPostPanel.tsx - grep "Generate Post" ui/src/components/SocialPostPanel.tsx - grep "aria-live" ui/src/components/SocialPostPanel.tsx - grep "Copy Post" ui/src/components/SocialPostResult.tsx - grep "hashtags" ui/src/components/SocialPostResult.tsx - grep "Collapsible" ui/src/components/SocialPostResult.tsx - grep "wallpapers" ui/src/pages/ContentStudio.tsx - grep "social" ui/src/pages/ContentStudio.tsx - grep "WallpaperGeneratePanel" ui/src/pages/ContentStudio.tsx - grep "SocialPostPanel" ui/src/pages/ContentStudio.tsx Social panel with character count, hashtag chips, carousel support. ContentStudio extended with Wallpapers and Social tabs. - `cd /opt/nexus/ui && npx tsc --noEmit` passes - ContentStudio shows 5 tabs: Diagrams, Icons, Themes, Wallpapers, Social - All UI copy matches the UI spec copywriting contract - Wallpaper panel: prompt + 12-platform selector + generate + download - Social panel: prompt + 4-platform selector + live character count + generate - Social result: copy post + hashtag chips + carousel collapsibles - ContentStudio has Wallpapers and Social tabs - tsc compiles cleanly After completion, create `.planning/phases/42-wallpapers-social-format-conversion-voice/42-05-SUMMARY.md`