MCPcopy
hub / github.com/garrytan/gstack / variants

Function variants

design/src/variants.ts:140–214  ·  view source on GitHub ↗
(options: VariantsOptions)

Source from the content-addressed store, hash-verified

138 * Generate N variants with staggered parallel execution.
139 */
140export async function variants(options: VariantsOptions): Promise<void> {
141 const apiKey = requireApiKey();
142 const baseBrief = options.briefFile
143 ? parseBrief(options.briefFile, true)
144 : parseBrief(options.brief!, false);
145
146 const quality = options.quality || "high";
147
148 fs.mkdirSync(options.outputDir, { recursive: true });
149
150 // If viewports specified, generate responsive variants instead of style variants
151 if (options.viewports) {
152 await generateResponsiveVariants(apiKey, baseBrief, options.outputDir, options.viewports, quality);
153 return;
154 }
155
156 const count = Math.min(options.count, 7); // Cap at 7 style variations
157 const size = options.size || "1536x1024";
158
159 console.error(`Generating ${count} variants...`);
160 const startTime = Date.now();
161
162 // Staggered parallel: start each call 1.5s apart
163 const promises: Promise<{ path: string; success: boolean; error?: string }>[] = [];
164
165 for (let i = 0; i < count; i++) {
166 const variation = STYLE_VARIATIONS[i] || "";
167 const prompt = variation
168 ? `${baseBrief}\n\nStyle direction: ${variation}`
169 : baseBrief;
170
171 const outputPath = path.join(options.outputDir, `variant-${String.fromCharCode(65 + i)}.png`);
172
173 // Stagger: wait 1.5s between launches
174 const delay = i * 1500;
175 promises.push(
176 new Promise(resolve => setTimeout(resolve, delay))
177 .then(() => {
178 console.error(` Starting variant ${String.fromCharCode(65 + i)}...`);
179 return generateVariant(apiKey, prompt, outputPath, size, quality);
180 })
181 );
182 }
183
184 const results = await Promise.allSettled(promises);
185 const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
186
187 const succeeded: string[] = [];
188 const failed: string[] = [];
189
190 for (const result of results) {
191 if (result.status === "fulfilled" && result.value.success) {
192 const size = fs.statSync(result.value.path).size;
193 console.error(` ✓ ${path.basename(result.value.path)} (${(size / 1024).toFixed(0)}KB)`);
194 succeeded.push(result.value.path);
195 } else {
196 const error = result.status === "fulfilled" ? result.value.error : (result.reason as Error).message;
197 const filePath = result.status === "fulfilled" ? result.value.path : "unknown";

Callers 1

mainFunction · 0.90

Calls 5

requireApiKeyFunction · 0.90
parseBriefFunction · 0.90
generateVariantFunction · 0.85
pushMethod · 0.45

Tested by

no test coverage detected