Files
gstack/design/src/brief.ts
T
Garry Tan a4dd5b0c2e feat: design binary core — generate, check, compare commands
Stateless CLI (design/dist/design) wrapping OpenAI Responses API for
UI mockup generation. Three working commands:

- generate: brief -> PNG mockup via gpt-4o + image_generation tool
- check: vision-based quality gate via GPT-4o (text readability, layout
  completeness, visual coherence)
- compare: generates self-contained HTML comparison board with star
  ratings, radio Pick, per-variant feedback, regenerate controls,
  and Submit button that writes structured JSON for agent polling

Auth reads from ~/.gstack/openai.json (0600), falls back to
OPENAI_API_KEY env var. Compiled separately from browse binary
(openai added to devDependencies, not runtime deps).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:52:16 -06:00

60 lines
1.9 KiB
TypeScript

/**
* Structured design brief — the interface between skill prose and image generation.
*/
export interface DesignBrief {
goal: string; // "Dashboard for coding assessment tool"
audience: string; // "Technical users, YC partners"
style: string; // "Dark theme, cream accents, minimal"
elements: string[]; // ["builder name", "score badge", "narrative letter"]
constraints?: string; // "Max width 1024px, mobile-first"
reference?: string; // DESIGN.md excerpt or style reference text
screenType: string; // "desktop-dashboard" | "mobile-app" | "landing-page" | etc.
}
/**
* Convert a structured brief to a prompt string for image generation.
*/
export function briefToPrompt(brief: DesignBrief): string {
const lines: string[] = [
`Generate a pixel-perfect UI mockup of a ${brief.screenType} for: ${brief.goal}.`,
`Target audience: ${brief.audience}.`,
`Visual style: ${brief.style}.`,
`Required elements: ${brief.elements.join(", ")}.`,
];
if (brief.constraints) {
lines.push(`Constraints: ${brief.constraints}.`);
}
if (brief.reference) {
lines.push(`Design reference: ${brief.reference}`);
}
lines.push(
"The mockup should look like a real production UI, not a wireframe or concept art.",
"All text must be readable. Layout must be clean and intentional.",
"1536x1024 pixels."
);
return lines.join(" ");
}
/**
* Parse a brief from either a plain text string or a JSON file path.
*/
export function parseBrief(input: string, isFile: boolean): string {
if (!isFile) {
// Plain text prompt — use directly
return input;
}
// JSON file — parse and convert to prompt
const raw = Bun.file(input);
// We'll read it synchronously via fs since Bun.file is async
const fs = require("fs");
const content = fs.readFileSync(input, "utf-8");
const brief: DesignBrief = JSON.parse(content);
return briefToPrompt(brief);
}