feat: implement AI bouquet generation flow with Gemini/OpenAI

* feat: scaffold message, generating, and map pages and align header steps

* feat: implement AI bouquet generation flow with Gemini/OpenAI

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chaewon Lee
2026-06-09 17:07:38 +09:00
committed by GitHub
parent d0ba482451
commit d8f93f4c17
33 changed files with 2008 additions and 54 deletions

View File

@@ -1,48 +1,93 @@
<script>
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import Header from '$lib/components/ui/Header.svelte';
import Artwork from '$lib/components/ui/Artwork/Artwork.svelte';
import MoodboardGrid from '$lib/components/ui/upload/MoodboardGrid.svelte';
import SnsFeedUpload from '$lib/components/ui/upload/SnsFeedUpload.svelte';
import { analyzeMood } from '$lib/flowerFlow/api.js';
import { getFlowObject, saveFlow } from '$lib/flowerFlow/session.js';
// "Build Moodboard" is selected by default in the design
let mode = $state('moodboard');
let primaryFile = $state(null);
let loading = $state(false);
let error = $state('');
const userInput = getFlowObject('userInput') ?? {};
async function continueToMessage() {
error = '';
if (!primaryFile) {
error = 'Upload at least one image to continue.';
return;
}
loading = true;
try {
const result = await analyzeMood(primaryFile, userInput);
saveFlow({
jobId: result.jobId,
moodAnalysis: result.moodAnalysis,
recipe: null,
imagePrompt: null,
images: null,
imagesJobId: null,
selectedSize: null,
floristNote: null,
mock: result.mock
});
await goto(resolve('/message'));
} catch (err) {
error = err instanceof Error ? err.message : 'Upload failed';
} finally {
loading = false;
}
}
</script>
<!--
On desktop the split layout is locked to the viewport height so the left
artwork stays put while switching modes. The right panel is a full-bleed
upload canvas with the mode toggle floating over it.
-->
<div
class="flex h-dvh flex-col overflow-x-hidden bg-surface text-ink lg:h-screen lg:overflow-hidden"
>
<Header step={3} total={6} />
<Header step={2} total={7} />
<main class="flex min-h-0 flex-1 flex-col lg:flex-row">
<Artwork />
<!-- Right panel: full-bleed workspace + floating tab switch -->
<section
class="relative flex min-h-0 flex-1 flex-col pb-[4.75rem] lg:overflow-hidden lg:pb-0"
>
<section class="relative flex min-h-0 flex-1 flex-col pb-[4.75rem] lg:overflow-hidden lg:pb-0">
{#if mode === 'moodboard'}
<MoodboardGrid />
<MoodboardGrid bind:primaryFile />
{:else}
<SnsFeedUpload />
<SnsFeedUpload bind:primaryFile />
{/if}
<!-- full-width on mobile; centered pill on desktop -->
<div
class="fixed right-0 bottom-0 left-0 z-20 px-4 pb-5 lg:absolute lg:right-auto lg:bottom-8 lg:left-1/2 lg:w-auto lg:-translate-x-1/2 lg:px-0 lg:pb-0"
class="fixed right-0 bottom-0 left-0 z-20 space-y-2 px-4 pb-5 lg:absolute lg:right-8 lg:bottom-8 lg:left-auto lg:w-72 lg:px-0 lg:pb-0"
>
{#if error}
<p class="rounded bg-surface/95 px-3 py-2 text-sm text-red-600 ring-1 ring-black/5">
{error}
</p>
{/if}
<button
type="button"
disabled={loading}
onclick={continueToMessage}
class="w-full bg-pill px-4 py-3 text-sm text-surface disabled:opacity-50"
>
{loading ? 'Analyzing mood...' : 'Continue to message'}
</button>
<div
class="flex w-full items-center rounded-full bg-surface/95 p-1.5 shadow-xl ring-1 ring-black/5 backdrop-blur lg:w-auto"
class="flex w-full items-center rounded-full bg-surface/95 p-1.5 shadow-xl ring-1 ring-black/5 backdrop-blur"
>
<button
type="button"
onclick={() => (mode = 'sns')}
class={[
'flex-1 rounded-full px-4 py-2.5 text-center text-sm whitespace-nowrap transition-colors lg:flex-none lg:px-5',
'flex-1 rounded-full px-4 py-2.5 text-center text-sm whitespace-nowrap transition-colors',
mode === 'sns' ? 'bg-pill text-surface' : 'text-muted hover:text-ink'
]}
>
@@ -52,7 +97,7 @@
type="button"
onclick={() => (mode = 'moodboard')}
class={[
'flex-1 rounded-full px-4 py-2.5 text-center text-sm whitespace-nowrap transition-colors lg:flex-none lg:px-5',
'flex-1 rounded-full px-4 py-2.5 text-center text-sm whitespace-nowrap transition-colors',
mode === 'moodboard' ? 'bg-pill text-surface' : 'text-muted hover:text-ink'
]}
>