From d8f93f4c17e473708c41915dbf04ad6851b9e45a Mon Sep 17 00:00:00 2001 From: Chaewon Lee Date: Tue, 9 Jun 2026 17:07:38 +0900 Subject: [PATCH] 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) --- .env.example | 15 ++ package-lock.json | 31 +++ package.json | 4 + src/lib/components/ui/Button.svelte | 4 +- src/lib/components/ui/Header.svelte | 2 +- .../components/ui/upload/MoodboardGrid.svelte | 73 +++++-- .../components/ui/upload/SnsFeedUpload.svelte | 23 ++- .../components/ui/upload/UploadTile.svelte | 9 +- src/lib/flowerFlow/api.js | 115 +++++++++++ src/lib/flowerFlow/session.js | 35 ++++ src/lib/server/aiError.js | 169 +++++++++++++++++ src/lib/server/flowerFlow/flowerDB.js | 145 ++++++++++++++ src/lib/server/flowerFlow/jobStore.js | 110 +++++++++++ src/lib/server/gemini/client.js | 57 ++++++ src/lib/server/gemini/image.js | 85 +++++++++ src/lib/server/gemini/mock.js | 66 +++++++ src/lib/server/gemini/text.js | 104 ++++++++++ src/lib/server/gemini/vision.js | 48 +++++ src/lib/server/http.js | 96 ++++++++++ src/lib/server/openai/image.js | 54 ++++++ src/routes/+page.svelte | 6 +- .../flower-flow/generate-images/+server.js | 88 +++++++++ src/routes/api/flower-flow/job/+server.js | 30 +++ .../api/flower-flow/mood-analysis/+server.js | 31 +++ src/routes/api/flower-flow/recipe/+server.js | 40 ++++ .../api/flower-flow/select-option/+server.js | 49 +++++ src/routes/create/+page.svelte | 39 +++- src/routes/generating/+page.svelte | 178 ++++++++++++++++++ src/routes/map/+page.svelte | 16 ++ src/routes/message/+page.svelte | 48 ++++- src/routes/options/+page.svelte | 108 ++++++++++- src/routes/result/+page.svelte | 103 +++++++++- src/routes/upload/+page.svelte | 81 ++++++-- 33 files changed, 2008 insertions(+), 54 deletions(-) create mode 100644 .env.example create mode 100644 src/lib/flowerFlow/api.js create mode 100644 src/lib/flowerFlow/session.js create mode 100644 src/lib/server/aiError.js create mode 100644 src/lib/server/flowerFlow/flowerDB.js create mode 100644 src/lib/server/flowerFlow/jobStore.js create mode 100644 src/lib/server/gemini/client.js create mode 100644 src/lib/server/gemini/image.js create mode 100644 src/lib/server/gemini/mock.js create mode 100644 src/lib/server/gemini/text.js create mode 100644 src/lib/server/gemini/vision.js create mode 100644 src/lib/server/http.js create mode 100644 src/lib/server/openai/image.js create mode 100644 src/routes/api/flower-flow/generate-images/+server.js create mode 100644 src/routes/api/flower-flow/job/+server.js create mode 100644 src/routes/api/flower-flow/mood-analysis/+server.js create mode 100644 src/routes/api/flower-flow/recipe/+server.js create mode 100644 src/routes/api/flower-flow/select-option/+server.js create mode 100644 src/routes/generating/+page.svelte create mode 100644 src/routes/map/+page.svelte diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..3a8214c --- /dev/null +++ b/.env.example @@ -0,0 +1,15 @@ +# Gemini +GEMINI_API_KEY= +GEMINI_TEXT_MODEL=gemini-2.5-flash-lite + +# Image generation +# IMAGE_PROVIDER: openai | gemini | mock +# mock = instant placeholder images, zero API calls (develop without burning quota) +IMAGE_PROVIDER=openai +OPENAI_API_KEY= +OPENAI_IMAGE_MODEL=gpt-image-1 +OPENAI_IMAGE_SIZE=1024x1024 +GEMINI_IMAGE_MODEL=gemini-3.1-flash-image + +# Kakao REST API (used later for /map) +KAKAO_REST_API_KEY= diff --git a/package-lock.json b/package-lock.json index e0ca8dd..544ff97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,10 @@ "": { "name": "ai-florist", "version": "0.0.1", + "dependencies": { + "@google/generative-ai": "^0.24.1", + "openai": "^6.42.0" + }, "devDependencies": { "@eslint/compat": "^2.0.4", "@eslint/js": "^10.0.1", @@ -210,6 +214,15 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, + "node_modules/@google/generative-ai": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz", + "integrity": "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -2137,6 +2150,24 @@ ], "license": "MIT" }, + "node_modules/openai": { + "version": "6.42.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.42.0.tgz", + "integrity": "sha512-1WFEt/uXMXOLhYRNkgJWo08Y2YNvNwpVU72K7ibrWgWpNOXd4VojXLbe6SQ4bLiUQ3Y8jz4IiyVkylJCL1DtZg==", + "license": "Apache-2.0", + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", diff --git a/package.json b/package.json index 86988e4..517f28b 100644 --- a/package.json +++ b/package.json @@ -29,5 +29,9 @@ "svelte": "^5.55.2", "tailwindcss": "^4.2.2", "vite": "^8.0.7" + }, + "dependencies": { + "@google/generative-ai": "^0.24.1", + "openai": "^6.42.0" } } diff --git a/src/lib/components/ui/Button.svelte b/src/lib/components/ui/Button.svelte index e7655ec..dfe9fbb 100644 --- a/src/lib/components/ui/Button.svelte +++ b/src/lib/components/ui/Button.svelte @@ -1,7 +1,7 @@ - diff --git a/src/lib/components/ui/Header.svelte b/src/lib/components/ui/Header.svelte index de87fe0..f614129 100644 --- a/src/lib/components/ui/Header.svelte +++ b/src/lib/components/ui/Header.svelte @@ -1,6 +1,6 @@ diff --git a/src/lib/components/ui/upload/MoodboardGrid.svelte b/src/lib/components/ui/upload/MoodboardGrid.svelte index abb3f3b..8f5150e 100644 --- a/src/lib/components/ui/upload/MoodboardGrid.svelte +++ b/src/lib/components/ui/upload/MoodboardGrid.svelte @@ -1,23 +1,70 @@ -
- {#each tiles as tile (tile.key)} - - {/each} +
+ + + +