15 KiB
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 41-diagrams-icons-theme-engine | 04 | execute | 3 |
|
|
true |
|
|
Purpose: User-facing UI for diagram and icon generation. Output: ContentStudio page with Diagrams and Icons tabs fully functional, with DiagramSourcePanel test coverage.
<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/41-diagrams-icons-theme-engine/41-UI-SPEC.md @.planning/phases/41-diagrams-icons-theme-engine/41-RESEARCH.md @.planning/phases/41-diagrams-icons-theme-engine/41-01-SUMMARY.md @.planning/phases/41-diagrams-icons-theme-engine/41-02-SUMMARY.md ```typescript export function useContentJob(companyId: string): { state: { jobId: string | null; status: "idle" | "queued" | "running" | "done" | "failed"; progress: number; resultAssetId: string | null; errorMessage: string | null }; submit: (jobType: string, input: Record) => Promise; reset: () => void; } ```export function submitContentJob(companyId: string, jobType: string, input: Record<string, unknown>): Promise<{ jobId: string; status: string }>;
export function getContentJob(companyId: string, jobId: string): Promise<ContentJob>;
export function getContentJobAsset(companyId: string, assetId: string): Promise<Blob>;
interface DiagramBundle { type: "diagram-bundle"; svgBase64: string; pngBase64: string; mermaidSource: string; stripped: boolean; }
interface IconSetBundle { type: "icon-set-bundle"; style: string; icons: Array<{ name: string; svgSource: string; pngs: Record<string, string> }>; }
Task 1: ContentStudio page + Diagram UI components + DiagramSourcePanel test
ui/src/pages/ContentStudio.tsx, ui/src/components/DiagramGeneratePanel.tsx, ui/src/components/DiagramPreview.tsx, ui/src/components/DiagramSourcePanel.tsx, ui/src/components/DiagramSourcePanel.test.tsx, ui/src/App.tsx
ui/src/App.tsx, ui/src/pages/Dashboard.tsx, ui/src/components/ui/tabs.tsx, ui/src/components/ui/collapsible.tsx, ui/src/components/ui/select.tsx, ui/src/components/ui/textarea.tsx, ui/src/components/ui/button.tsx, ui/src/components/ui/progress.tsx, ui/src/hooks/useContentJob.ts, ui/src/index.css
- DiagramSourcePanel renders a collapsible trigger with text "View Mermaid source"
- Clicking the trigger expands to show a monospace textarea with the Mermaid source
- When expanded, trigger text changes to "Hide source"
- A "Copy source" button with aria-label="Copy source" is present in expanded state
- Editing the textarea content marks it dirty and shows a "Re-render diagram" button
- "Re-render diagram" button calls the onRerender callback with the edited source
1. Create `ui/src/components/DiagramSourcePanel.test.tsx` FIRST (Wave 0 for DIAG-03):
- Test that collapsed state shows "View Mermaid source" trigger text
- Test that clicking trigger expands to show textarea with provided mermaidSource prop
- Test that expanded state shows "Hide source" trigger text
- Test that "Copy source" button has `aria-label="Copy source"`
- Test that editing textarea and blurring shows "Re-render diagram" button
- Test that clicking "Re-render diagram" calls onRerender with the edited source text
- Use @testing-library/react for rendering and user interactions
-
Create
ui/src/pages/ContentStudio.tsx:- Tabbed layout using shadcn Tabs component
- Three tabs: "Diagrams", "Icons", "Themes"
- Each tab renders its respective panel component
- Page heading: "Content Studio" (h1, 20px semibold per UI spec)
- Get companyId from route params or company context (check how Dashboard.tsx gets it)
-
Register route in
ui/src/App.tsx:- Add route
/:companyId/content-studiopointing to ContentStudio - Add navigation entry if a sidebar/nav component exists (check existing nav structure)
- Add route
-
Create
ui/src/components/DiagramGeneratePanel.tsx:- Textarea (4 rows) for diagram description prompt
- Select component for diagram type: Architecture, Flowchart, ERD, Sequence, Mind Map
- "Generate Diagram" Button (primary) -- disabled during generation, shows "Generating..." with spinner
- Progress bar (shadcn progress) below button, visible during queued/running states
- On submit: call
useContentJob.submit("diagram", { prompt: promptText, diagramType, darkMode: false }) - On done: fetch asset, parse DiagramBundle JSON, pass to DiagramPreview
- On error: show error message "Render failed -- {detail}. Try again." below progress bar
- If directives were stripped (bundle.stripped === true), show muted helper text: "Unsafe directives were removed before rendering."
- Empty state: heading "No diagram yet", body "Describe an architecture, flow, or sequence and we'll render it."
-
Create
ui/src/components/DiagramPreview.tsx:- Renders SVG inside container with className "paperclip-mermaid" (reuse existing CSS)
- SVG content is pre-sanitized server-side via DOMPurify (DIAG-05). Render the trusted sanitized SVG string.
- Two ghost buttons below: "Download SVG", "Download PNG"
- Download SVG: create blob from decoded svgBase64, trigger download as
diagram.svg - Download PNG: create blob from decoded pngBase64, trigger download as
diagram.png
-
Create
ui/src/components/DiagramSourcePanel.tsx(must pass the tests from step 1):- Collapsible component (shadcn collapsible)
- Trigger label: "View Mermaid source" (collapsed) / "Hide source" (expanded) with chevron icon
- Content: monospace Textarea (14px, read/write) showing mermaidSource
- "Copy source" IconButton at top-right with
aria-label="Copy source"andtitle="Copy source" - When source is modified (dirty), show "Re-render diagram" Button (secondary, xs) at bottom-right
- Re-render submits a new job with the edited source (via
onRerenderprop, which parent wires touseContentJob.submit("diagram", { source: editedSource })— note:sourcefield skips LLM synthesis on server) - Height transition: 250ms ease (respect prefers-reduced-motion)
Copywriting: Use exact strings from UI-SPEC copywriting contract table. Accessibility: All aria-labels per UI-SPEC accessibility section. cd /opt/nexus && pnpm --filter ui exec vitest run src/components/DiagramSourcePanel.test.tsx && pnpm tsc --noEmit --project ui/tsconfig.json <acceptance_criteria>
grep "content-studio" ui/src/App.tsxmatches (route registered)grep "Generate Diagram" ui/src/components/DiagramGeneratePanel.tsxmatchesgrep "paperclip-mermaid" ui/src/components/DiagramPreview.tsxmatchesgrep 'aria-label="Copy source"' ui/src/components/DiagramSourcePanel.tsxmatchesgrep "View Mermaid source" ui/src/components/DiagramSourcePanel.tsxmatchesgrep "Re-render diagram" ui/src/components/DiagramSourcePanel.tsxmatchesgrep "Unsafe directives were removed" ui/src/components/DiagramGeneratePanel.tsxmatchesgrep "No diagram yet" ui/src/components/DiagramGeneratePanel.tsxmatches- DiagramSourcePanel.test.tsx exists and all tests pass
- TypeScript compiles without errors </acceptance_criteria> ContentStudio page registered at /:companyId/content-studio with Diagrams tab fully wired: prompt input, type selector, generate button, progress bar, SVG preview, download buttons, collapsible source editor with test coverage (DIAG-03)
-
Create
ui/src/components/IconResultGrid.tsx:- Props:
icons: Array<{ name: string; svgSource: string; pngs: Record<string, string> }>,selectedIds: Set<string>,onToggle: (name: string) => void - CSS grid: 4 columns on desktop (
grid-cols-4), 2 on mobile (grid-cols-2) - Each cell: card-colored Card, SVG preview centered (render the server-sanitized SVG source string), icon name label below (text-xs)
- Checkbox on card hover (top-left, 16px) with
aria-label="Select {icon name}" - On coarse pointer (
@media (pointer: coarse)): checkbox always visible - Hover reveals download row at bottom of card: SVG, PNG 16, PNG 32, PNG 64 as small ghost buttons
- Each download button creates a blob from the appropriate base64 data and triggers download
- Props:
-
Create
ui/src/components/IconDownloadBar.tsx:- Props:
selectedCount: number,onDownload: (format: string) => void,onClear: () => void - Sticky bar at bottom of panel (position: sticky, bottom: 0, z-10)
- Only visible when selectedCount > 0
- "Download selected ({N})" Button (primary)
- Format selector (Select: SVG, PNG 16, PNG 32, PNG 64)
- "Clear selection" link button
- Download creates individual files (or a zip for multiple) with the chosen format
- Props:
-
Wire IconGeneratePanel into ContentStudio "Icons" tab. Manage icon selection state (selectedIds Set) in IconGeneratePanel, pass down to IconResultGrid and IconDownloadBar.
Copywriting: Use exact strings from UI-SPEC copywriting contract. Accessibility: All aria-labels per UI-SPEC accessibility section. cd /opt/nexus && pnpm tsc --noEmit --project ui/tsconfig.json <acceptance_criteria>
grep "Generate Icons" ui/src/components/IconGeneratePanel.tsxmatchesgrep "No icons yet" ui/src/components/IconGeneratePanel.tsxmatchesgrep "grid-cols-4" ui/src/components/IconResultGrid.tsxmatchesgrep 'aria-label.*Select' ui/src/components/IconResultGrid.tsxmatchesgrep "Download selected" ui/src/components/IconDownloadBar.tsxmatchesgrep "Clear selection" ui/src/components/IconDownloadBar.tsxmatches- TypeScript compiles without errors </acceptance_criteria> Icons tab fully wired: description input, style/count selectors, generate button, 4-column grid with selection, bulk download bar with format choice
<success_criteria>
- User can navigate to content-studio, see tabbed interface with Diagrams and Icons
- Diagram generation flow: type + describe -> generate -> see SVG -> download SVG/PNG -> edit source -> re-render (DIAG-01, DIAG-03, DIAG-04)
- DiagramSourcePanel has test coverage for collapsible behavior, copy, and re-render (DIAG-03)
- Icon generation flow: describe + style + count -> generate -> see grid -> select -> download (ICON-01, ICON-02, ICON-03)
- Empty states, error states, and loading states all handled per UI spec </success_criteria>