diff --git a/design/src/cli.ts b/design/src/cli.ts index 1c72b816..481eb29d 100644 --- a/design/src/cli.ts +++ b/design/src/cli.ts @@ -24,6 +24,7 @@ import { diffMockups, verifyAgainstMockup } from "./diff"; import { evolve } from "./evolve"; import { generateDesignToCodePrompt } from "./design-to-code"; import { serve } from "./serve"; +import { gallery } from "./gallery"; function parseArgs(argv: string[]): { command: string; flags: Record } { const args = argv.slice(2); // skip bun/node and script path @@ -237,6 +238,13 @@ async function main(): Promise { }); break; + case "gallery": + gallery({ + designsDir: flags["designs-dir"] as string, + output: (flags.output as string) || "/tmp/gstack-design-gallery.html", + }); + break; + case "serve": await serve({ html: flags.html as string, diff --git a/design/src/commands.ts b/design/src/commands.ts index 70c174e3..c8331e97 100644 --- a/design/src/commands.ts +++ b/design/src/commands.ts @@ -64,6 +64,11 @@ export const COMMANDS = new Map b.date.localeCompare(a.date)); + + const sessionCards = sessions.map(session => { + const variantImgs = session.variants.map((vPath, i) => { + try { + const imgData = fs.readFileSync(vPath).toString("base64"); + const ext = path.extname(vPath).slice(1) || "png"; + const label = path.basename(vPath, `.${ext}`).replace("variant-", ""); + const isApproved = session.approved?.approved_variant === label; + return ` + `; + } catch { + return ""; // Skip unreadable images + } + }).filter(Boolean).join("\n"); + + const feedbackNote = session.approved?.feedback + ? `` + : ""; + + return ` + `; + }).join("\n"); + + return ` + + + + +Design History + + + +
+

Design History

+
${sessions.length} exploration${sessions.length === 1 ? "" : "s"}
+
+ + +`; +} + +function generateEmptyGallery(): string { + return ` + + + + +Design History + + + +
+

No design history yet

+

Run /design-shotgun to start exploring design directions.

+
+ +`; +} + +function escapeHtml(str: string): string { + return str.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); +} + +/** + * Gallery command: generate HTML timeline from design explorations. + */ +export function gallery(options: GalleryOptions): void { + const html = generateGalleryHtml(options.designsDir); + const outputDir = path.dirname(options.output); + fs.mkdirSync(outputDir, { recursive: true }); + fs.writeFileSync(options.output, html); + console.log(JSON.stringify({ outputPath: options.output })); +}