chore: apply prettier formatting and fix lint errors
This commit is contained in:
@@ -132,7 +132,9 @@ async function main() {
|
||||
|
||||
const size = process.env.OPENAI_IMAGE_CATALOG_SIZE || '1024x1536';
|
||||
const quality = process.env.OPENAI_IMAGE_CATALOG_QUALITY || 'low';
|
||||
console.log(`대상: ${targets.length}종 · ${size} · quality=${quality}${dryRun ? ' (dry-run)' : ''}`);
|
||||
console.log(
|
||||
`대상: ${targets.length}종 · ${size} · quality=${quality}${dryRun ? ' (dry-run)' : ''}`
|
||||
);
|
||||
|
||||
for (const flower of targets) {
|
||||
const outPath = join(OUT_DIR, `${flower.id}.png`);
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
</script>
|
||||
|
||||
<section
|
||||
class="relative flex w-full shrink-0 flex-col border-b border-line lg:min-h-0 lg:h-full lg:w-[44%] lg:shrink-0 lg:overflow-y-auto lg:border-r lg:border-b-0"
|
||||
class="relative flex w-full shrink-0 flex-col border-b border-line lg:h-full lg:min-h-0 lg:w-[44%] lg:shrink-0 lg:overflow-y-auto lg:border-r lg:border-b-0"
|
||||
>
|
||||
<!--
|
||||
mobile: row · desktop: 꽃 슬롯 높이 고정 → 설명 카드 길이와 무관하게 Y·크기 유지
|
||||
-->
|
||||
<div
|
||||
class="mx-auto flex min-h-0 w-full max-w-100 flex-1 flex-row items-start gap-8 px-6 pt-6 pb-8 lg:flex-col lg:items-center lg:justify-start lg:gap-4 lg:px-6 lg:pb-12 lg:pt-[calc(50%-5rem)]"
|
||||
class="mx-auto flex min-h-0 w-full max-w-100 flex-1 flex-row items-start gap-8 px-6 pt-6 pb-8 lg:flex-col lg:items-center lg:justify-start lg:gap-4 lg:px-6 lg:pt-[calc(50%-5rem)] lg:pb-12"
|
||||
>
|
||||
<div
|
||||
class="flex h-[11rem] shrink-0 items-end justify-center sm:h-[13rem] lg:h-[min(24rem,36vh)] lg:w-full"
|
||||
@@ -43,7 +43,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="min-w-0 shrink-0 lg:w-full lg:flex lg:justify-center">
|
||||
<div class="min-w-0 shrink-0 lg:flex lg:w-full lg:justify-center">
|
||||
<DescriptionCard {title} {description} mode={cardMode} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,9 +7,14 @@
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<div class="w-64 max-w-full flex-none border border-line-strong bg-white px-4 py-3 shadow-sm lg:px-6 lg:py-5">
|
||||
<div
|
||||
class="w-64 max-w-full flex-none border border-line-strong bg-white px-4 py-3 shadow-sm lg:px-6 lg:py-5"
|
||||
>
|
||||
<h3
|
||||
class={['text-sm leading-snug font-semibold', mode === 'instruction' ? 'text-muted' : 'text-ink']}
|
||||
class={[
|
||||
'text-sm leading-snug font-semibold',
|
||||
mode === 'instruction' ? 'text-muted' : 'text-ink'
|
||||
]}
|
||||
>
|
||||
{title}
|
||||
</h3>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<script>
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve } from '$app/paths';
|
||||
import FlowContinueBar, {
|
||||
FLOW_CONTINUE_BUTTON
|
||||
} from '$lib/components/ui/FlowContinueBar.svelte';
|
||||
import FlowContinueBar, { FLOW_CONTINUE_BUTTON } from '$lib/components/ui/FlowContinueBar.svelte';
|
||||
import GrowthMetaphorIllustration from '$lib/components/ui/landing/GrowthMetaphorIllustration.svelte';
|
||||
|
||||
function handleStart() {
|
||||
@@ -15,7 +13,7 @@
|
||||
class="relative flex min-h-dvh flex-col bg-surface px-6 py-8 pb-[3.75rem] font-sans text-ink sm:px-10 sm:py-10 lg:px-14 lg:pb-8"
|
||||
aria-label="Every bouquet starts with a muse — seed to bouquet growth metaphor"
|
||||
>
|
||||
<div class="mx-auto flex w-full max-w-6xl min-h-0 flex-1 flex-col justify-center">
|
||||
<div class="mx-auto flex min-h-0 w-full max-w-6xl flex-1 flex-col justify-center">
|
||||
<GrowthMetaphorIllustration />
|
||||
|
||||
<p class="mt-3 text-left text-sm tracking-[0.18em] text-muted sm:mt-4 sm:text-base">
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<script>
|
||||
let {
|
||||
enPlainText = '',
|
||||
koPlainText = ''
|
||||
} = $props();
|
||||
let { enPlainText = '', koPlainText = '' } = $props();
|
||||
|
||||
/** @type {'ko' | 'en'} */
|
||||
let activeLang = $state('ko');
|
||||
@@ -61,10 +58,14 @@
|
||||
rows={4}
|
||||
value={activeText}
|
||||
oninput={handleInput}
|
||||
aria-label={activeLang === 'ko' ? '꽃집 주문 멘트 (한국어)' : 'Florist order message (English)'}
|
||||
aria-label={activeLang === 'ko'
|
||||
? '꽃집 주문 멘트 (한국어)'
|
||||
: 'Florist order message (English)'}
|
||||
></textarea>
|
||||
{:else}
|
||||
<p class="min-w-0 flex-1 text-sm text-muted">Complete the flow to generate your order message.</p>
|
||||
<p class="min-w-0 flex-1 text-sm text-muted">
|
||||
Complete the flow to generate your order message.
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<div class="flex shrink-0 flex-col items-stretch gap-2">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
import { env } from '$env/dynamic/public';
|
||||
|
||||
let {
|
||||
@@ -23,8 +24,8 @@
|
||||
let mapInstance = $state(null);
|
||||
/** @type {ReturnType<typeof window.kakao.maps.InfoWindow> | null} */
|
||||
let infoWindow = null;
|
||||
/** @type {Map<string, { marker: ReturnType<typeof window.kakao.maps.Marker>; shop: (typeof shops)[number] }>} */
|
||||
let shopMarkerMap = new Map();
|
||||
/** @type {SvelteMap<string, { marker: ReturnType<typeof window.kakao.maps.Marker>; shop: (typeof shops)[number] }>} */
|
||||
let shopMarkerMap = new SvelteMap();
|
||||
|
||||
function relayoutMap() {
|
||||
mapInstance?.relayout?.();
|
||||
@@ -226,9 +227,7 @@
|
||||
{mapError}
|
||||
</div>
|
||||
{:else if !mapReady}
|
||||
<div
|
||||
class="absolute inset-0 flex items-center justify-center bg-track text-sm text-muted"
|
||||
>
|
||||
<div class="absolute inset-0 flex items-center justify-center bg-track text-sm text-muted">
|
||||
Loading map...
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -71,10 +71,12 @@
|
||||
{/if}
|
||||
|
||||
<div class="flex min-h-0 flex-1 flex-col gap-6 px-6 pb-8 md:px-10 lg:flex-row lg:px-12 lg:pb-10">
|
||||
<div class="relative flex min-h-64 flex-1 flex-col overflow-hidden border border-line lg:min-h-0">
|
||||
<div
|
||||
class="relative flex min-h-64 flex-1 flex-col overflow-hidden border border-line lg:min-h-0"
|
||||
>
|
||||
<KakaoMap
|
||||
initialLat={initialLat}
|
||||
initialLng={initialLng}
|
||||
{initialLat}
|
||||
{initialLng}
|
||||
{shops}
|
||||
selectedId={selectedShopId}
|
||||
{fitBounds}
|
||||
@@ -96,7 +98,7 @@
|
||||
{#if loading && shops.length === 0}
|
||||
<p class="text-sm text-muted">Searching for flower shops...</p>
|
||||
{:else}
|
||||
<ShopList shops={shops} bind:selectedId={selectedShopId} onselect={handleShopSelect} />
|
||||
<ShopList {shops} bind:selectedId={selectedShopId} onselect={handleShopSelect} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
'Thank you for always being there',
|
||||
'I love you',
|
||||
"I'm proud of you",
|
||||
'Congratulations!',
|
||||
'Congratulations!'
|
||||
];
|
||||
|
||||
const selectedPreset = $derived(presets.find((preset) => preset === message) ?? null);
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
<script>
|
||||
import flowerIconUrl from '$lib/assets/flower.svg';
|
||||
|
||||
let {
|
||||
name,
|
||||
nameKo,
|
||||
wordOfFlower,
|
||||
wordOfFlowerKo,
|
||||
imageSrc,
|
||||
role = 'main'
|
||||
} = $props();
|
||||
let { name, nameKo, wordOfFlower, wordOfFlowerKo, imageSrc, role = 'main' } = $props();
|
||||
|
||||
let flipped = $state(false);
|
||||
|
||||
@@ -26,7 +19,7 @@
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="flip-card h-[16.25rem] w-40 shrink-0 snap-start cursor-pointer border-none bg-transparent p-0 text-left"
|
||||
class="flip-card h-[16.25rem] w-40 shrink-0 cursor-pointer snap-start border-none bg-transparent p-0 text-left"
|
||||
aria-label={flipped ? `${nameKo} card, show English` : `${name} card, show Korean`}
|
||||
onclick={toggleFlip}
|
||||
>
|
||||
@@ -48,7 +41,7 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="shrink-0 px-3 pb-4 pt-2">
|
||||
<div class="shrink-0 px-3 pt-2 pb-4">
|
||||
<h3
|
||||
class="flex min-h-8 items-center justify-center text-center text-sm leading-tight tracking-wide text-ink"
|
||||
>
|
||||
@@ -79,7 +72,7 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="shrink-0 px-3 pb-4 pt-2">
|
||||
<div class="shrink-0 px-3 pt-2 pb-4">
|
||||
<h3
|
||||
class="flex min-h-8 items-center justify-center text-center text-sm leading-tight tracking-wide text-ink"
|
||||
>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<p class="mb-4 text-xs tracking-[0.2em] text-muted uppercase">Flowers in your bouquet</p>
|
||||
|
||||
<div
|
||||
class="flex snap-x snap-mandatory gap-4 overflow-x-auto px-0.5 py-1 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
||||
class="flex snap-x snap-mandatory [scrollbar-width:none] gap-4 overflow-x-auto px-0.5 py-1 [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden"
|
||||
>
|
||||
{#each flowers as flower (flower.id)}
|
||||
<BouquetFlowerCard
|
||||
|
||||
@@ -16,8 +16,7 @@ export const ARTWORK_CARD_DEFAULTS = {
|
||||
},
|
||||
generating: {
|
||||
title: 'Crafting your bouquet',
|
||||
description:
|
||||
'We are turning their mood, photos, and message into a one-of-a-kind arrangement.'
|
||||
description: 'We are turning their mood, photos, and message into a one-of-a-kind arrangement.'
|
||||
},
|
||||
map: {
|
||||
title: 'Choose a florist',
|
||||
|
||||
@@ -20,7 +20,9 @@ export function formatStrictBouquetImagePrompt(recipe) {
|
||||
'Generate a realistic Korean florist bouquet product photo.',
|
||||
'',
|
||||
'STRICT RECIPE — the bouquet must contain ONLY these flowers and NO other flower species:',
|
||||
allFlowers.length > 0 ? allFlowers.map((flower) => `- ${flower}`).join('\n') : '- (none listed)',
|
||||
allFlowers.length > 0
|
||||
? allFlowers.map((flower) => `- ${flower}`).join('\n')
|
||||
: '- (none listed)',
|
||||
'',
|
||||
`Main focal blooms (each must be clearly visible): ${mains.join(', ') || 'none'}`,
|
||||
`Supporting filler/line flowers (each must appear): ${subs.join(', ') || 'none'}`,
|
||||
|
||||
@@ -60,7 +60,10 @@ export function buildFloristOrderMessage(input) {
|
||||
`Would a reservation be possible?`;
|
||||
|
||||
const segments = [
|
||||
{ text: "Hello, I'd like to inquire about a flower order. It's a bouquet for ", highlight: false },
|
||||
{
|
||||
text: "Hello, I'd like to inquire about a flower order. It's a bouquet for ",
|
||||
highlight: false
|
||||
},
|
||||
{ text: relationship, highlight: true },
|
||||
{ text: ' for ', highlight: false },
|
||||
{ text: occasion, highlight: true },
|
||||
|
||||
@@ -3,468 +3,468 @@
|
||||
/** @type {{ id: number, name: string, wordOfFlower: string }[]} */
|
||||
export const flowerCatalogLite = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Daffodil",
|
||||
"wordOfFlower": "self-love"
|
||||
id: 1,
|
||||
name: 'Daffodil',
|
||||
wordOfFlower: 'self-love'
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Stock",
|
||||
"wordOfFlower": "lasting beauty"
|
||||
id: 2,
|
||||
name: 'Stock',
|
||||
wordOfFlower: 'lasting beauty'
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Amaryllis",
|
||||
"wordOfFlower": "dazzling beauty"
|
||||
id: 3,
|
||||
name: 'Amaryllis',
|
||||
wordOfFlower: 'dazzling beauty'
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "Sweet Pea",
|
||||
"wordOfFlower": "delicate pleasures"
|
||||
id: 4,
|
||||
name: 'Sweet Pea',
|
||||
wordOfFlower: 'delicate pleasures'
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Anthurium",
|
||||
"wordOfFlower": "hospitality"
|
||||
id: 5,
|
||||
name: 'Anthurium',
|
||||
wordOfFlower: 'hospitality'
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "Freesia",
|
||||
"wordOfFlower": "purity"
|
||||
id: 6,
|
||||
name: 'Freesia',
|
||||
wordOfFlower: 'purity'
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "Tulip",
|
||||
"wordOfFlower": "benevolence"
|
||||
id: 7,
|
||||
name: 'Tulip',
|
||||
wordOfFlower: 'benevolence'
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "Hyacinth",
|
||||
"wordOfFlower": "joy of the heart"
|
||||
id: 8,
|
||||
name: 'Hyacinth',
|
||||
wordOfFlower: 'joy of the heart'
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": "Ranunculus",
|
||||
"wordOfFlower": "radiant charm"
|
||||
id: 9,
|
||||
name: 'Ranunculus',
|
||||
wordOfFlower: 'radiant charm'
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"name": "Lilac",
|
||||
"wordOfFlower": "memories of youth"
|
||||
id: 10,
|
||||
name: 'Lilac',
|
||||
wordOfFlower: 'memories of youth'
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "Iris",
|
||||
"wordOfFlower": "good news"
|
||||
id: 11,
|
||||
name: 'Iris',
|
||||
wordOfFlower: 'good news'
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"name": "Tree Peony",
|
||||
"wordOfFlower": "wealth and honor"
|
||||
id: 12,
|
||||
name: 'Tree Peony',
|
||||
wordOfFlower: 'wealth and honor'
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"name": "Peony",
|
||||
"wordOfFlower": "shyness"
|
||||
id: 13,
|
||||
name: 'Peony',
|
||||
wordOfFlower: 'shyness'
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"name": "Rose",
|
||||
"wordOfFlower": "passionate love"
|
||||
id: 14,
|
||||
name: 'Rose',
|
||||
wordOfFlower: 'passionate love'
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"name": "Snowball Viburnum",
|
||||
"wordOfFlower": "grace"
|
||||
id: 15,
|
||||
name: 'Snowball Viburnum',
|
||||
wordOfFlower: 'grace'
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"name": "Carnation",
|
||||
"wordOfFlower": "a woman's affection"
|
||||
id: 16,
|
||||
name: 'Carnation',
|
||||
wordOfFlower: "a woman's affection"
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"name": "Clematis",
|
||||
"wordOfFlower": "nobility"
|
||||
id: 17,
|
||||
name: 'Clematis',
|
||||
wordOfFlower: 'nobility'
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"name": "Lily",
|
||||
"wordOfFlower": "purity"
|
||||
id: 18,
|
||||
name: 'Lily',
|
||||
wordOfFlower: 'purity'
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"name": "Hydrangea",
|
||||
"wordOfFlower": "heartlessness"
|
||||
id: 19,
|
||||
name: 'Hydrangea',
|
||||
wordOfFlower: 'heartlessness'
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"name": "Agapanthus",
|
||||
"wordOfFlower": "love letter"
|
||||
id: 20,
|
||||
name: 'Agapanthus',
|
||||
wordOfFlower: 'love letter'
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"name": "Allium",
|
||||
"wordOfFlower": "endless sorrow"
|
||||
id: 21,
|
||||
name: 'Allium',
|
||||
wordOfFlower: 'endless sorrow'
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"name": "Bellflower (Campanula)",
|
||||
"wordOfFlower": "a coquettish look"
|
||||
id: 22,
|
||||
name: 'Bellflower (Campanula)',
|
||||
wordOfFlower: 'a coquettish look'
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"name": "China Aster (Callistephus)",
|
||||
"wordOfFlower": "trusting love"
|
||||
id: 23,
|
||||
name: 'China Aster (Callistephus)',
|
||||
wordOfFlower: 'trusting love'
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"name": "Poppy (Papaver)",
|
||||
"wordOfFlower": "consolation"
|
||||
id: 24,
|
||||
name: 'Poppy (Papaver)',
|
||||
wordOfFlower: 'consolation'
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"name": "Dahlia",
|
||||
"wordOfFlower": "gratitude"
|
||||
id: 25,
|
||||
name: 'Dahlia',
|
||||
wordOfFlower: 'gratitude'
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"name": "Lotus",
|
||||
"wordOfFlower": "purity"
|
||||
id: 26,
|
||||
name: 'Lotus',
|
||||
wordOfFlower: 'purity'
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"name": "Gentian",
|
||||
"wordOfFlower": "I love you when you're sad"
|
||||
id: 27,
|
||||
name: 'Gentian',
|
||||
wordOfFlower: "I love you when you're sad"
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"name": "Sunflower",
|
||||
"wordOfFlower": "adoration"
|
||||
id: 28,
|
||||
name: 'Sunflower',
|
||||
wordOfFlower: 'adoration'
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"name": "Chrysanthemum",
|
||||
"wordOfFlower": "purity"
|
||||
id: 29,
|
||||
name: 'Chrysanthemum',
|
||||
wordOfFlower: 'purity'
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
"name": "Cockscomb (Celosia)",
|
||||
"wordOfFlower": "ardent love"
|
||||
id: 30,
|
||||
name: 'Cockscomb (Celosia)',
|
||||
wordOfFlower: 'ardent love'
|
||||
},
|
||||
{
|
||||
"id": 31,
|
||||
"name": "Anemone",
|
||||
"wordOfFlower": "sincerity"
|
||||
id: 31,
|
||||
name: 'Anemone',
|
||||
wordOfFlower: 'sincerity'
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
"name": "Cosmos",
|
||||
"wordOfFlower": "a girl's pure heart"
|
||||
id: 32,
|
||||
name: 'Cosmos',
|
||||
wordOfFlower: "a girl's pure heart"
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
"name": "Red Spider Lily (Lycoris radiata)",
|
||||
"wordOfFlower": "true love"
|
||||
id: 33,
|
||||
name: 'Red Spider Lily (Lycoris radiata)',
|
||||
wordOfFlower: 'true love'
|
||||
},
|
||||
{
|
||||
"id": 34,
|
||||
"name": "Gerbera",
|
||||
"wordOfFlower": "mystery"
|
||||
id: 34,
|
||||
name: 'Gerbera',
|
||||
wordOfFlower: 'mystery'
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"name": "Calla Lily",
|
||||
"wordOfFlower": "joy"
|
||||
id: 35,
|
||||
name: 'Calla Lily',
|
||||
wordOfFlower: 'joy'
|
||||
},
|
||||
{
|
||||
"id": 36,
|
||||
"name": "Bird of Paradise (Strelitzia)",
|
||||
"wordOfFlower": "mystery"
|
||||
id: 36,
|
||||
name: 'Bird of Paradise (Strelitzia)',
|
||||
wordOfFlower: 'mystery'
|
||||
},
|
||||
{
|
||||
"id": 37,
|
||||
"name": "Hellebore",
|
||||
"wordOfFlower": "reason for being"
|
||||
id: 37,
|
||||
name: 'Hellebore',
|
||||
wordOfFlower: 'reason for being'
|
||||
},
|
||||
{
|
||||
"id": 38,
|
||||
"name": "Lisianthus (Eustoma)",
|
||||
"wordOfFlower": "appreciation"
|
||||
id: 38,
|
||||
name: 'Lisianthus (Eustoma)',
|
||||
wordOfFlower: 'appreciation'
|
||||
},
|
||||
{
|
||||
"id": 39,
|
||||
"name": "Scabiosa",
|
||||
"wordOfFlower": "I have lost all"
|
||||
id: 39,
|
||||
name: 'Scabiosa',
|
||||
wordOfFlower: 'I have lost all'
|
||||
},
|
||||
{
|
||||
"id": 40,
|
||||
"name": "Wax Flower (Chamelaucium)",
|
||||
"wordOfFlower": "lasting love"
|
||||
id: 40,
|
||||
name: 'Wax Flower (Chamelaucium)',
|
||||
wordOfFlower: 'lasting love'
|
||||
},
|
||||
{
|
||||
"id": 41,
|
||||
"name": "Caspia (Limonium)",
|
||||
"wordOfFlower": "remembrance"
|
||||
id: 41,
|
||||
name: 'Caspia (Limonium)',
|
||||
wordOfFlower: 'remembrance'
|
||||
},
|
||||
{
|
||||
"id": 42,
|
||||
"name": "Ruscus",
|
||||
"wordOfFlower": "endurance"
|
||||
id: 42,
|
||||
name: 'Ruscus',
|
||||
wordOfFlower: 'endurance'
|
||||
},
|
||||
{
|
||||
"id": 43,
|
||||
"name": "Veronica (Speedwell)",
|
||||
"wordOfFlower": "fidelity"
|
||||
id: 43,
|
||||
name: 'Veronica (Speedwell)',
|
||||
wordOfFlower: 'fidelity'
|
||||
},
|
||||
{
|
||||
"id": 44,
|
||||
"name": "Solidago (Goldenrod)",
|
||||
"wordOfFlower": "encouragement"
|
||||
id: 44,
|
||||
name: 'Solidago (Goldenrod)',
|
||||
wordOfFlower: 'encouragement'
|
||||
},
|
||||
{
|
||||
"id": 45,
|
||||
"name": "Bouvardia",
|
||||
"wordOfFlower": "enthusiasm"
|
||||
id: 45,
|
||||
name: 'Bouvardia',
|
||||
wordOfFlower: 'enthusiasm'
|
||||
},
|
||||
{
|
||||
"id": 46,
|
||||
"name": "Tweedia (Oxypetalum)",
|
||||
"wordOfFlower": "believe in me"
|
||||
id: 46,
|
||||
name: 'Tweedia (Oxypetalum)',
|
||||
wordOfFlower: 'believe in me'
|
||||
},
|
||||
{
|
||||
"id": 47,
|
||||
"name": "Craspedia (Billy Balls)",
|
||||
"wordOfFlower": "good health"
|
||||
id: 47,
|
||||
name: 'Craspedia (Billy Balls)',
|
||||
wordOfFlower: 'good health'
|
||||
},
|
||||
{
|
||||
"id": 48,
|
||||
"name": "Amaranthus",
|
||||
"wordOfFlower": "immortality"
|
||||
id: 48,
|
||||
name: 'Amaranthus',
|
||||
wordOfFlower: 'immortality'
|
||||
},
|
||||
{
|
||||
"id": 49,
|
||||
"name": "Queen Anne's Lace (Ammi)",
|
||||
"wordOfFlower": "sanctuary"
|
||||
id: 49,
|
||||
name: "Queen Anne's Lace (Ammi)",
|
||||
wordOfFlower: 'sanctuary'
|
||||
},
|
||||
{
|
||||
"id": 50,
|
||||
"name": "Nigella (Love-in-a-mist)",
|
||||
"wordOfFlower": "perplexity"
|
||||
id: 50,
|
||||
name: 'Nigella (Love-in-a-mist)',
|
||||
wordOfFlower: 'perplexity'
|
||||
},
|
||||
{
|
||||
"id": 51,
|
||||
"name": "Brunia",
|
||||
"wordOfFlower": "unity"
|
||||
id: 51,
|
||||
name: 'Brunia',
|
||||
wordOfFlower: 'unity'
|
||||
},
|
||||
{
|
||||
"id": 52,
|
||||
"name": "Snapdragon",
|
||||
"wordOfFlower": "desire"
|
||||
id: 52,
|
||||
name: 'Snapdragon',
|
||||
wordOfFlower: 'desire'
|
||||
},
|
||||
{
|
||||
"id": 53,
|
||||
"name": "Lupine",
|
||||
"wordOfFlower": "lust for life"
|
||||
id: 53,
|
||||
name: 'Lupine',
|
||||
wordOfFlower: 'lust for life'
|
||||
},
|
||||
{
|
||||
"id": 54,
|
||||
"name": "Gladiolus",
|
||||
"wordOfFlower": "secret meeting"
|
||||
id: 54,
|
||||
name: 'Gladiolus',
|
||||
wordOfFlower: 'secret meeting'
|
||||
},
|
||||
{
|
||||
"id": 55,
|
||||
"name": "Foxglove (Digitalis)",
|
||||
"wordOfFlower": "ardent love"
|
||||
id: 55,
|
||||
name: 'Foxglove (Digitalis)',
|
||||
wordOfFlower: 'ardent love'
|
||||
},
|
||||
{
|
||||
"id": 56,
|
||||
"name": "Delphinium",
|
||||
"wordOfFlower": "understand my heart"
|
||||
id: 56,
|
||||
name: 'Delphinium',
|
||||
wordOfFlower: 'understand my heart'
|
||||
},
|
||||
{
|
||||
"id": 57,
|
||||
"name": "Salvia (Scarlet Sage)",
|
||||
"wordOfFlower": "burning heart"
|
||||
id: 57,
|
||||
name: 'Salvia (Scarlet Sage)',
|
||||
wordOfFlower: 'burning heart'
|
||||
},
|
||||
{
|
||||
"id": 58,
|
||||
"name": "Grape Hyacinth (Muscari)",
|
||||
"wordOfFlower": "disappointment"
|
||||
id: 58,
|
||||
name: 'Grape Hyacinth (Muscari)',
|
||||
wordOfFlower: 'disappointment'
|
||||
},
|
||||
{
|
||||
"id": 59,
|
||||
"name": "Forget-me-not",
|
||||
"wordOfFlower": "true love"
|
||||
id: 59,
|
||||
name: 'Forget-me-not',
|
||||
wordOfFlower: 'true love'
|
||||
},
|
||||
{
|
||||
"id": 60,
|
||||
"name": "Statice (Limonium)",
|
||||
"wordOfFlower": "eternal love"
|
||||
id: 60,
|
||||
name: 'Statice (Limonium)',
|
||||
wordOfFlower: 'eternal love'
|
||||
},
|
||||
{
|
||||
"id": 61,
|
||||
"name": "Astilbe",
|
||||
"wordOfFlower": "bashfulness"
|
||||
id: 61,
|
||||
name: 'Astilbe',
|
||||
wordOfFlower: 'bashfulness'
|
||||
},
|
||||
{
|
||||
"id": 62,
|
||||
"name": "Strawflower (Helichrysum)",
|
||||
"wordOfFlower": "always remember"
|
||||
id: 62,
|
||||
name: 'Strawflower (Helichrysum)',
|
||||
wordOfFlower: 'always remember'
|
||||
},
|
||||
{
|
||||
"id": 63,
|
||||
"name": "Cornflower (Centaurea)",
|
||||
"wordOfFlower": "happiness"
|
||||
id: 63,
|
||||
name: 'Cornflower (Centaurea)',
|
||||
wordOfFlower: 'happiness'
|
||||
},
|
||||
{
|
||||
"id": 64,
|
||||
"name": "Chinese Lantern (Physalis)",
|
||||
"wordOfFlower": "falsehood"
|
||||
id: 64,
|
||||
name: 'Chinese Lantern (Physalis)',
|
||||
wordOfFlower: 'falsehood'
|
||||
},
|
||||
{
|
||||
"id": 65,
|
||||
"name": "Globe Amaranth (Gomphrena)",
|
||||
"wordOfFlower": "immortality"
|
||||
id: 65,
|
||||
name: 'Globe Amaranth (Gomphrena)',
|
||||
wordOfFlower: 'immortality'
|
||||
},
|
||||
{
|
||||
"id": 66,
|
||||
"name": "Showy Stonecrop (Sedum)",
|
||||
"wordOfFlower": "hope"
|
||||
id: 66,
|
||||
name: 'Showy Stonecrop (Sedum)',
|
||||
wordOfFlower: 'hope'
|
||||
},
|
||||
{
|
||||
"id": 67,
|
||||
"name": "Baby's Breath (Gypsophila)",
|
||||
"wordOfFlower": "earnest joy"
|
||||
id: 67,
|
||||
name: "Baby's Breath (Gypsophila)",
|
||||
wordOfFlower: 'earnest joy'
|
||||
},
|
||||
{
|
||||
"id": 68,
|
||||
"name": "Cattleya",
|
||||
"wordOfFlower": "you are a beauty"
|
||||
id: 68,
|
||||
name: 'Cattleya',
|
||||
wordOfFlower: 'you are a beauty'
|
||||
},
|
||||
{
|
||||
"id": 69,
|
||||
"name": "Oncidium",
|
||||
"wordOfFlower": "innocent heart"
|
||||
id: 69,
|
||||
name: 'Oncidium',
|
||||
wordOfFlower: 'innocent heart'
|
||||
},
|
||||
{
|
||||
"id": 70,
|
||||
"name": "Dendrobium",
|
||||
"wordOfFlower": "a beauty"
|
||||
id: 70,
|
||||
name: 'Dendrobium',
|
||||
wordOfFlower: 'a beauty'
|
||||
},
|
||||
{
|
||||
"id": 71,
|
||||
"name": "Vanda Orchid",
|
||||
"wordOfFlower": "a token of affection"
|
||||
id: 71,
|
||||
name: 'Vanda Orchid',
|
||||
wordOfFlower: 'a token of affection'
|
||||
},
|
||||
{
|
||||
"id": 72,
|
||||
"name": "Holly",
|
||||
"wordOfFlower": "protection"
|
||||
id: 72,
|
||||
name: 'Holly',
|
||||
wordOfFlower: 'protection'
|
||||
},
|
||||
{
|
||||
"id": 73,
|
||||
"name": "Nandina (Heavenly Bamboo)",
|
||||
"wordOfFlower": "enduring love"
|
||||
id: 73,
|
||||
name: 'Nandina (Heavenly Bamboo)',
|
||||
wordOfFlower: 'enduring love'
|
||||
},
|
||||
{
|
||||
"id": 74,
|
||||
"name": "Pussy Willow (Salix gracilistyla)",
|
||||
"wordOfFlower": "freedom"
|
||||
id: 74,
|
||||
name: 'Pussy Willow (Salix gracilistyla)',
|
||||
wordOfFlower: 'freedom'
|
||||
},
|
||||
{
|
||||
"id": 75,
|
||||
"name": "Maidenhair Fern (Adiantum)",
|
||||
"wordOfFlower": "charm"
|
||||
id: 75,
|
||||
name: 'Maidenhair Fern (Adiantum)',
|
||||
wordOfFlower: 'charm'
|
||||
},
|
||||
{
|
||||
"id": 76,
|
||||
"name": "Cotton",
|
||||
"wordOfFlower": "mother's love"
|
||||
id: 76,
|
||||
name: 'Cotton',
|
||||
wordOfFlower: "mother's love"
|
||||
},
|
||||
{
|
||||
"id": 77,
|
||||
"name": "Hosta (Plantain Lily)",
|
||||
"wordOfFlower": "composure"
|
||||
id: 77,
|
||||
name: 'Hosta (Plantain Lily)',
|
||||
wordOfFlower: 'composure'
|
||||
},
|
||||
{
|
||||
"id": 78,
|
||||
"name": "Beautyberry (Callicarpa)",
|
||||
"wordOfFlower": "intelligence"
|
||||
id: 78,
|
||||
name: 'Beautyberry (Callicarpa)',
|
||||
wordOfFlower: 'intelligence'
|
||||
},
|
||||
{
|
||||
"id": 79,
|
||||
"name": "Reed",
|
||||
"wordOfFlower": "faith"
|
||||
id: 79,
|
||||
name: 'Reed',
|
||||
wordOfFlower: 'faith'
|
||||
},
|
||||
{
|
||||
"id": 80,
|
||||
"name": "Foxtail Millet",
|
||||
"wordOfFlower": "equality"
|
||||
id: 80,
|
||||
name: 'Foxtail Millet',
|
||||
wordOfFlower: 'equality'
|
||||
},
|
||||
{
|
||||
"id": 81,
|
||||
"name": "Mint",
|
||||
"wordOfFlower": "virtue"
|
||||
id: 81,
|
||||
name: 'Mint',
|
||||
wordOfFlower: 'virtue'
|
||||
},
|
||||
{
|
||||
"id": 82,
|
||||
"name": "Asparagus Fern",
|
||||
"wordOfFlower": "constancy"
|
||||
id: 82,
|
||||
name: 'Asparagus Fern',
|
||||
wordOfFlower: 'constancy'
|
||||
},
|
||||
{
|
||||
"id": 83,
|
||||
"name": "Silver Grass (Miscanthus)",
|
||||
"wordOfFlower": "retirement"
|
||||
id: 83,
|
||||
name: 'Silver Grass (Miscanthus)',
|
||||
wordOfFlower: 'retirement'
|
||||
},
|
||||
{
|
||||
"id": 84,
|
||||
"name": "Eucalyptus",
|
||||
"wordOfFlower": "memories"
|
||||
id: 84,
|
||||
name: 'Eucalyptus',
|
||||
wordOfFlower: 'memories'
|
||||
},
|
||||
{
|
||||
"id": 85,
|
||||
"name": "Ivy (Hedera)",
|
||||
"wordOfFlower": "a steadfast heart"
|
||||
id: 85,
|
||||
name: 'Ivy (Hedera)',
|
||||
wordOfFlower: 'a steadfast heart'
|
||||
},
|
||||
{
|
||||
"id": 86,
|
||||
"name": "Olive",
|
||||
"wordOfFlower": "peace"
|
||||
id: 86,
|
||||
name: 'Olive',
|
||||
wordOfFlower: 'peace'
|
||||
},
|
||||
{
|
||||
"id": 87,
|
||||
"name": "Mock Orange (Pittosporum tobira)",
|
||||
"wordOfFlower": "embrace"
|
||||
id: 87,
|
||||
name: 'Mock Orange (Pittosporum tobira)',
|
||||
wordOfFlower: 'embrace'
|
||||
},
|
||||
{
|
||||
"id": 88,
|
||||
"name": "Cherry Blossom",
|
||||
"wordOfFlower": "spiritual beauty"
|
||||
id: 88,
|
||||
name: 'Cherry Blossom',
|
||||
wordOfFlower: 'spiritual beauty'
|
||||
},
|
||||
{
|
||||
"id": 89,
|
||||
"name": "Forsythia",
|
||||
"wordOfFlower": "hope"
|
||||
id: 89,
|
||||
name: 'Forsythia',
|
||||
wordOfFlower: 'hope'
|
||||
},
|
||||
{
|
||||
"id": 90,
|
||||
"name": "Plum Blossom (Prunus mume)",
|
||||
"wordOfFlower": "pure heart"
|
||||
id: 90,
|
||||
name: 'Plum Blossom (Prunus mume)',
|
||||
wordOfFlower: 'pure heart'
|
||||
},
|
||||
{
|
||||
"id": 91,
|
||||
"name": "Magnolia",
|
||||
"wordOfFlower": "love of nature"
|
||||
id: 91,
|
||||
name: 'Magnolia',
|
||||
wordOfFlower: 'love of nature'
|
||||
},
|
||||
{
|
||||
"id": 92,
|
||||
"name": "Redbud (Cercis chinensis)",
|
||||
"wordOfFlower": "friendship"
|
||||
id: 92,
|
||||
name: 'Redbud (Cercis chinensis)',
|
||||
wordOfFlower: 'friendship'
|
||||
},
|
||||
{
|
||||
"id": 93,
|
||||
"name": "Flowering Quince (Chaenomeles)",
|
||||
"wordOfFlower": "trust"
|
||||
id: 93,
|
||||
name: 'Flowering Quince (Chaenomeles)',
|
||||
wordOfFlower: 'trust'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -236,8 +236,7 @@ export function buildBouquetRationale(moodAnalysis, userInput, recipe) {
|
||||
}
|
||||
|
||||
if (cardMessage && mainFlower) {
|
||||
const messageRef =
|
||||
cardMessage.length <= 40 ? `your message, "${cardMessage}"` : 'your message';
|
||||
const messageRef = cardMessage.length <= 40 ? `your message, "${cardMessage}"` : 'your message';
|
||||
parts.push(
|
||||
`For ${messageRef}, ${mainFlower.name} (${mainFlower.wordOfFlower}) felt like the right fit.`
|
||||
);
|
||||
|
||||
@@ -56,7 +56,8 @@ export function getFlowUserInput() {
|
||||
const input = getFlowObject('userInput');
|
||||
if (!input) return {};
|
||||
|
||||
const { notes: _notes, ...createOnly } = input;
|
||||
const createOnly = { ...input };
|
||||
delete createOnly.notes;
|
||||
return createOnly;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,5 +35,4 @@ export const LANDING_CYCLE_HOLD_MS = 3000;
|
||||
const lastStage = LANDING_GROWTH_STAGES[LANDING_GROWTH_STAGES.length - 1];
|
||||
|
||||
/** 4단계 reveal 완료 + hold 후 다음 사이클까지 */
|
||||
export const LANDING_CYCLE_MS =
|
||||
lastStage.delayMs + LANDING_STAGE_REVEAL_MS + LANDING_CYCLE_HOLD_MS;
|
||||
export const LANDING_CYCLE_MS = lastStage.delayMs + LANDING_STAGE_REVEAL_MS + LANDING_CYCLE_HOLD_MS;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,8 @@ function pointInPolygon(x, y, polygon) {
|
||||
const yi = polygon[i].y;
|
||||
const xj = polygon[j].x;
|
||||
const yj = polygon[j].y;
|
||||
const intersects = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi + Number.EPSILON) + xi;
|
||||
const intersects =
|
||||
yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi + Number.EPSILON) + xi;
|
||||
if (intersects) inside = !inside;
|
||||
}
|
||||
return inside;
|
||||
@@ -68,11 +69,18 @@ function readJpegDimensions(buffer) {
|
||||
* @param {string} mimeType
|
||||
*/
|
||||
export function readImageDimensions(buffer, mimeType) {
|
||||
if (mimeType.includes('png') || (buffer[0] === 0x89 && buffer.toString('ascii', 1, 4) === 'PNG')) {
|
||||
if (
|
||||
mimeType.includes('png') ||
|
||||
(buffer[0] === 0x89 && buffer.toString('ascii', 1, 4) === 'PNG')
|
||||
) {
|
||||
return readPngDimensions(buffer);
|
||||
}
|
||||
|
||||
if (mimeType.includes('jpeg') || mimeType.includes('jpg') || (buffer[0] === 0xff && buffer[1] === 0xd8)) {
|
||||
if (
|
||||
mimeType.includes('jpeg') ||
|
||||
mimeType.includes('jpg') ||
|
||||
(buffer[0] === 0xff && buffer[1] === 0xd8)
|
||||
) {
|
||||
return readJpegDimensions(buffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -99,12 +99,15 @@ export async function editBouquetImage(sourceImage, editPrompt, options = {}) {
|
||||
|
||||
const model = getImageModel();
|
||||
/** @type {import('@google/generative-ai').Part[]} */
|
||||
const parts = [{ text: editPrompt }, {
|
||||
inlineData: {
|
||||
data: sourceImage.base64,
|
||||
mimeType: sourceImage.mimeType
|
||||
const parts = [
|
||||
{ text: editPrompt },
|
||||
{
|
||||
inlineData: {
|
||||
data: sourceImage.base64,
|
||||
mimeType: sourceImage.mimeType
|
||||
}
|
||||
}
|
||||
}];
|
||||
];
|
||||
|
||||
if (mask) {
|
||||
parts.push(
|
||||
|
||||
@@ -107,8 +107,7 @@ export function mockApplyRecipeEdit(recipe, editPrompt) {
|
||||
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();
|
||||
const capitalizedTo = toToken.charAt(0).toUpperCase() + toToken.slice(1).toLowerCase();
|
||||
|
||||
if (colorPrefix && !fromToken.includes(' ')) {
|
||||
return `${colorPrefix} ${capitalizedTo}`;
|
||||
|
||||
@@ -68,7 +68,9 @@ Rules:
|
||||
- Budget should be ${budget}.`;
|
||||
|
||||
const result = await model.generateContent(prompt);
|
||||
return normalizeRecipeLists(/** @type {BouquetRecipe} */ (parseJsonFromText(result.response.text())));
|
||||
return normalizeRecipeLists(
|
||||
/** @type {BouquetRecipe} */ (parseJsonFromText(result.response.text()))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,5 +146,7 @@ Rules:
|
||||
- The updated recipe is the sole source of truth for the next bouquet image — every listed flower must be included in the image prompt.`;
|
||||
|
||||
const result = await model.generateContent(prompt);
|
||||
return normalizeRecipeLists(/** @type {BouquetRecipe} */ (parseJsonFromText(result.response.text())));
|
||||
return normalizeRecipeLists(
|
||||
/** @type {BouquetRecipe} */ (parseJsonFromText(result.response.text()))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,10 @@ export async function POST({ request }) {
|
||||
}
|
||||
|
||||
if (!job.images?.primary) {
|
||||
return json({ error: 'bouquet image is missing. Generate images first.', code: 'bad_request' }, 400);
|
||||
return json(
|
||||
{ error: 'bouquet image is missing. Generate images first.', code: 'bad_request' },
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
const priorRecipe = normalizeRecipeLists(job.recipe);
|
||||
@@ -75,11 +78,7 @@ export async function POST({ request }) {
|
||||
const provider = getImageProvider();
|
||||
const mask =
|
||||
mode === 'area' && selection.length >= 3
|
||||
? buildAreaEditMask(
|
||||
sourceImage,
|
||||
selection,
|
||||
provider === 'gemini' ? 'gemini' : 'openai'
|
||||
)
|
||||
? buildAreaEditMask(sourceImage, selection, provider === 'gemini' ? 'gemini' : 'openai')
|
||||
: null;
|
||||
|
||||
console.log(
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
import Header from '$lib/components/ui/Header.svelte';
|
||||
import Artwork from '$lib/components/ui/Artwork/Artwork.svelte';
|
||||
import ContextForm from '$lib/components/ui/create/ContextForm.svelte';
|
||||
import FlowContinueBar, {
|
||||
FLOW_CONTINUE_BUTTON
|
||||
} from '$lib/components/ui/FlowContinueBar.svelte';
|
||||
import FlowContinueBar, { FLOW_CONTINUE_BUTTON } from '$lib/components/ui/FlowContinueBar.svelte';
|
||||
import {
|
||||
consumeDevCreateSnapshot,
|
||||
deleteFlowKey,
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve } from '$app/paths';
|
||||
import DescriptionCard from '$lib/components/ui/Artwork/DescriptionCard.svelte';
|
||||
import FlowContinueBar, {
|
||||
FLOW_CONTINUE_BUTTON
|
||||
} from '$lib/components/ui/FlowContinueBar.svelte';
|
||||
import FlowContinueBar, { FLOW_CONTINUE_BUTTON } from '$lib/components/ui/FlowContinueBar.svelte';
|
||||
import Header from '$lib/components/ui/Header.svelte';
|
||||
import { editImages, fetchJob, finalizeJob, toDataUrl } from '$lib/flowerFlow/api.js';
|
||||
import { buildBriefBouquetTitle } from '$lib/flowerFlow/resolveRecipeFlowers.js';
|
||||
@@ -185,9 +183,7 @@
|
||||
const afterImage = result.images?.primary ?? null;
|
||||
generatedImage = afterImage;
|
||||
chatMessages = chatMessages.map((message) =>
|
||||
message.id === assistantMessageId
|
||||
? { ...message, status: 'done', afterImage }
|
||||
: message
|
||||
message.id === assistantMessageId ? { ...message, status: 'done', afterImage } : message
|
||||
);
|
||||
saveFlow({
|
||||
editInstruction: instruction,
|
||||
@@ -248,7 +244,7 @@
|
||||
</script>
|
||||
|
||||
{#snippet editableImageFrame(image, editable = false)}
|
||||
<div class="relative w-full max-w-44 sm:max-w-52 overflow-hidden bg-track ring-1 ring-black/5">
|
||||
<div class="relative w-full max-w-44 overflow-hidden bg-track ring-1 ring-black/5 sm:max-w-52">
|
||||
{#if image}
|
||||
<img
|
||||
src={toDataUrl(image)}
|
||||
@@ -339,8 +335,12 @@
|
||||
<section
|
||||
class="flex min-h-0 w-full shrink-0 flex-col border-b border-line px-6 py-6 lg:w-[44%] lg:border-r lg:border-b-0 lg:px-10 lg:py-8 lg:pb-12"
|
||||
>
|
||||
<div class="mx-auto flex min-h-0 w-full max-w-100 flex-1 flex-col items-center justify-center gap-6">
|
||||
<div class="w-full max-w-24 overflow-hidden bg-track shadow-sm ring-1 ring-black/5 sm:max-w-28 lg:max-w-75">
|
||||
<div
|
||||
class="mx-auto flex min-h-0 w-full max-w-100 flex-1 flex-col items-center justify-center gap-6"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-24 overflow-hidden bg-track shadow-sm ring-1 ring-black/5 sm:max-w-28 lg:max-w-75"
|
||||
>
|
||||
{#if loading}
|
||||
<div class="aspect-[3/4] w-full animate-pulse bg-placeholder"></div>
|
||||
{:else if imageSrc}
|
||||
@@ -359,9 +359,7 @@
|
||||
</section>
|
||||
|
||||
<section class="relative flex min-h-0 flex-1 flex-col overflow-hidden pb-44 lg:pb-8">
|
||||
<div
|
||||
class="mx-auto flex min-h-0 w-full max-w-2xl flex-1 flex-col gap-4 px-6 py-5 lg:py-6"
|
||||
>
|
||||
<div class="mx-auto flex min-h-0 w-full max-w-2xl flex-1 flex-col gap-4 px-6 py-5 lg:py-6">
|
||||
<div class="shrink-0">
|
||||
<p class="text-xs tracking-[0.2em] text-muted uppercase">Edit bouquet</p>
|
||||
<h2 class="mt-1 text-lg">Tell us how you want to refine it.</h2>
|
||||
@@ -430,7 +428,9 @@
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<div class="flex w-full items-center gap-2 rounded-full border border-pill bg-surface py-1.5 pr-1.5 pl-5">
|
||||
<div
|
||||
class="flex w-full items-center gap-2 rounded-full border border-pill bg-surface py-1.5 pr-1.5 pl-5"
|
||||
>
|
||||
<textarea
|
||||
bind:value={prompt}
|
||||
rows="1"
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
import Artwork from '$lib/components/ui/Artwork/Artwork.svelte';
|
||||
import GenerationActivityFeed from '$lib/components/ui/generating/GenerationActivityFeed.svelte';
|
||||
import { buildRecipe, generateImages } from '$lib/flowerFlow/api.js';
|
||||
import { createGenerationProgress, DEFAULT_ESTIMATED_MS, MOCK_ESTIMATED_MS } from '$lib/flowerFlow/generationProgress.js';
|
||||
import {
|
||||
createGenerationProgress,
|
||||
DEFAULT_ESTIMATED_MS,
|
||||
MOCK_ESTIMATED_MS
|
||||
} from '$lib/flowerFlow/generationProgress.js';
|
||||
import { createGeneratingArtworkCycle } from '$lib/flowerFlow/generatingArtworkCycle.js';
|
||||
import {
|
||||
clearFlow,
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
import Header from '$lib/components/ui/Header.svelte';
|
||||
import Artwork from '$lib/components/ui/Artwork/Artwork.svelte';
|
||||
import MessageForm from '$lib/components/ui/message/MessageForm.svelte';
|
||||
import FlowContinueBar, {
|
||||
FLOW_CONTINUE_BUTTON
|
||||
} from '$lib/components/ui/FlowContinueBar.svelte';
|
||||
import FlowContinueBar, { FLOW_CONTINUE_BUTTON } from '$lib/components/ui/FlowContinueBar.svelte';
|
||||
import { skipDevImages } from '$lib/flowerFlow/devSeed.js';
|
||||
import {
|
||||
consumeDevMessageSnapshot,
|
||||
@@ -32,9 +30,7 @@
|
||||
|
||||
const artworkVariant = $derived(hasMessage ? 'message1' : 'upload2');
|
||||
|
||||
const artworkTitle = $derived(
|
||||
hasMessage ? 'Your message' : ARTWORK_CARD_DEFAULTS.message.title
|
||||
);
|
||||
const artworkTitle = $derived(hasMessage ? 'Your message' : ARTWORK_CARD_DEFAULTS.message.title);
|
||||
|
||||
const artworkDescription = $derived(
|
||||
hasMessage ? message.trim() : ARTWORK_CARD_DEFAULTS.message.description
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
import Header from '$lib/components/ui/Header.svelte';
|
||||
import Artwork from '$lib/components/ui/Artwork/Artwork.svelte';
|
||||
import BouquetFlowerCarousel from '$lib/components/ui/result/BouquetFlowerCarousel.svelte';
|
||||
import FlowContinueBar, {
|
||||
FLOW_CONTINUE_BUTTON
|
||||
} from '$lib/components/ui/FlowContinueBar.svelte';
|
||||
import FlowContinueBar, { FLOW_CONTINUE_BUTTON } from '$lib/components/ui/FlowContinueBar.svelte';
|
||||
import { fetchJob, toDataUrl } from '$lib/flowerFlow/api.js';
|
||||
import { getFlowerImageSrc } from '$lib/flowerFlow/flowerImagePaths.js';
|
||||
import { resolveRecipeFlowers, buildBouquetRationale, buildBriefBouquetTitle } from '$lib/flowerFlow/resolveRecipeFlowers.js';
|
||||
import {
|
||||
resolveRecipeFlowers,
|
||||
buildBouquetRationale,
|
||||
buildBriefBouquetTitle
|
||||
} from '$lib/flowerFlow/resolveRecipeFlowers.js';
|
||||
import { getFlowString } from '$lib/flowerFlow/session.js';
|
||||
|
||||
let loading = $state(true);
|
||||
@@ -63,7 +65,9 @@
|
||||
/>
|
||||
|
||||
<section class="relative flex min-h-0 flex-1 flex-col pb-[3.75rem] lg:overflow-hidden lg:pb-8">
|
||||
<div class="flex min-h-0 flex-1 flex-col justify-center overflow-hidden px-6 py-6 lg:px-8 lg:py-8">
|
||||
<div
|
||||
class="flex min-h-0 flex-1 flex-col justify-center overflow-hidden px-6 py-6 lg:px-8 lg:py-8"
|
||||
>
|
||||
{#if loading}
|
||||
<p class="text-sm text-muted">Loading result...</p>
|
||||
{:else if error}
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
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 FlowContinueBar, {
|
||||
FLOW_CONTINUE_BUTTON
|
||||
} from '$lib/components/ui/FlowContinueBar.svelte';
|
||||
import FlowContinueBar, { FLOW_CONTINUE_BUTTON } from '$lib/components/ui/FlowContinueBar.svelte';
|
||||
import { analyzeMood } from '$lib/flowerFlow/api.js';
|
||||
import {
|
||||
deleteFlowKey,
|
||||
@@ -61,12 +59,12 @@
|
||||
},
|
||||
character: {
|
||||
title: 'Their character',
|
||||
description: 'A face, a gesture, a presence. Something in them is starting to take floral form.'
|
||||
description:
|
||||
'A face, a gesture, a presence. Something in them is starting to take floral form.'
|
||||
},
|
||||
location: {
|
||||
title: 'A sense of place',
|
||||
description:
|
||||
'City grit or quiet coast. Where they belong roots the arrangement in memory.'
|
||||
description: 'City grit or quiet coast. Where they belong roots the arrangement in memory.'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user