/** @typedef {import('../flowerFlow/jobStore.js').MoodAnalysis} MoodAnalysis */ /** @typedef {import('../flowerFlow/jobStore.js').BouquetRecipe} BouquetRecipe */ import { formatStrictBouquetImagePrompt } from '../../flowerFlow/bouquetImageFormat.js'; /** @returns {MoodAnalysis} */ export function mockMoodAnalysis() { return { colorPalette: ['pale pink', 'ivory', 'light green'], moodKeywords: ['soft', 'warm', 'natural'], styleImpression: ['minimal', 'romantic'], textureKeywords: ['airy', 'delicate'], energyLevel: 'medium' }; } /** * @param {Partial} userInput * @returns {BouquetRecipe} */ export function mockRecipe(userInput = {}) { const budget = userInput.budget ? `around ₩${userInput.budget.toLocaleString('en-US')}` : 'around ₩50,000'; const notes = userInput.notes?.toLowerCase() ?? ''; /** @type {string[]} */ let mainFlowers = ['Pink tulip']; /** @type {string} */ let concept = 'Soft Romantic Tulip Bouquet'; if (/love|사랑/.test(notes)) { mainFlowers = ['Red rose']; concept = 'Romantic Rose Bouquet'; } else if (/thank|grateful|감사/.test(notes)) { mainFlowers = ['Dahlia']; concept = 'Grateful Dahlia Bouquet'; } else if (/proud|congratul/.test(notes)) { mainFlowers = ['Sunflower']; concept = 'Celebratory Sunflower Bouquet'; } else if (/birthday|happy/.test(notes)) { mainFlowers = ['Gerbera']; concept = 'Cheerful Gerbera Bouquet'; } return { concept, mainFlowers, subFlowers: ["Baby's breath", 'Seasonal white flowers'], greenery: ['Eucalyptus'], colors: ['pale pink', 'ivory', 'soft green'], wrapping: 'ivory paper with pale pink ribbon', shape: 'loose round bouquet', budget }; } /** @param {BouquetRecipe} recipe */ export function mockImagePrompt(recipe) { return formatStrictBouquetImagePrompt(recipe); } /** @param {string} [label] */ export function mockGeneratedImage(label = 'Bouquet') { const svg = ` Mock ${label} Set OPENAI_API_KEY for real images `; return { mimeType: 'image/svg+xml', base64: Buffer.from(svg).toString('base64') }; } /** * Apply a simple swap edit to the recipe in mock mode (e.g. "change tulip to rose"). * @param {BouquetRecipe} recipe * @param {string} editPrompt * @returns {BouquetRecipe} */ export function mockApplyRecipeEdit(recipe, editPrompt) { /** @type {BouquetRecipe} */ const updated = structuredClone(recipe); const lower = editPrompt.toLowerCase(); const swapMatch = lower.match(/(?:change|replace|swap)\s+(.+?)\s+(?:to|with|into)\s+(.+)/) ?? lower.match(/(.+?)\s+(?:to|into)\s+(.+)/); if (!swapMatch) return updated; const fromToken = swapMatch[1].trim().replace(/[.!?]$/, ''); const toToken = swapMatch[2].trim().replace(/[.!?]$/, ''); if (!fromToken || !toToken) return updated; /** @param {string[]} labels */ const replaceInList = (labels) => labels.map((label) => { if (!label.toLowerCase().includes(fromToken)) return label; const colorPrefix = label.match(/^(\w+)\s+/i)?.[1]; const capitalizedTo = toToken.charAt(0).toUpperCase() + toToken.slice(1).toLowerCase(); if (colorPrefix && !fromToken.includes(' ')) { return `${colorPrefix} ${capitalizedTo}`; } return label.replace(new RegExp(fromToken, 'i'), capitalizedTo); }); updated.mainFlowers = replaceInList(updated.mainFlowers); updated.subFlowers = replaceInList(updated.subFlowers); updated.greenery = replaceInList(updated.greenery); return updated; }