---
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"
---

<objective>
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.
</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-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
</context>

<interfaces>
From server/src/services/renderers/wallpaper-renderer.ts (Plan 02):
```typescript
export const PLATFORM_DIMENSIONS: Record<string, { width: number; height: number; label: string }>;
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<string, number>;
// "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<string, unknown>) => Promise<void>;
  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; }
```
</interfaces>

<tasks>

<task type="auto">
  <name>Task 1: Create WallpaperGeneratePanel and WallpaperPreview components</name>
  <files>ui/src/components/WallpaperGeneratePanel.tsx, ui/src/components/WallpaperPreview.tsx</files>
  <read_first>ui/src/components/DiagramGeneratePanel.tsx, ui/src/components/IconGeneratePanel.tsx, ui/src/hooks/useContentJob.ts, ui/src/api/contentJobs.ts</read_first>
  <action>
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.
  </action>
  <verify>
    <automated>cd /opt/nexus/ui && npx tsc --noEmit 2>&1 | head -20</automated>
  </verify>
  <acceptance_criteria>
    - 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
  </acceptance_criteria>
  <done>Wallpaper panel allows prompt + platform selection, shows progress, displays result with download. App icon shows multi-size grid.</done>
</task>

<task type="auto">
  <name>Task 2: Create SocialPostPanel, SocialPostResult, and extend ContentStudio tabs</name>
  <files>ui/src/components/SocialPostPanel.tsx, ui/src/components/SocialPostResult.tsx, ui/src/pages/ContentStudio.tsx</files>
  <read_first>ui/src/pages/ContentStudio.tsx, ui/src/components/DiagramGeneratePanel.tsx</read_first>
  <action>
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.
  </action>
  <verify>
    <automated>cd /opt/nexus/ui && npx tsc --noEmit 2>&1 | head -20</automated>
  </verify>
  <acceptance_criteria>
    - 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
  </acceptance_criteria>
  <done>Social panel with character count, hashtag chips, carousel support. ContentStudio extended with Wallpapers and Social tabs.</done>
</task>

</tasks>

<verification>
- `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
</verification>

<success_criteria>
- 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
</success_criteria>

<output>
After completion, create `.planning/phases/42-wallpapers-social-format-conversion-voice/42-05-SUMMARY.md`
</output>
