diff --git a/src/lib/components/ui/Artwork/Artwork.svelte b/src/lib/components/ui/Artwork/Artwork.svelte
index 57a0953..4429c21 100644
--- a/src/lib/components/ui/Artwork/Artwork.svelte
+++ b/src/lib/components/ui/Artwork/Artwork.svelte
@@ -7,6 +7,8 @@
let {
title = 'Title',
description = 'Description Description Description',
+ /** @type {'instruction' | 'summary'} */
+ cardMode = 'summary',
/** @type {import('./artworkVariants.js').ArtworkVariant} */
variant = 'create1',
/** edit Continue 이후 확정된 꽃다발만 전달 (그 전에는 null → Vase) */
@@ -42,7 +44,7 @@
@@ -21,16 +23,20 @@
-
-
-
AI Florist
-
- DearYou
-
-
-
-
-
-
+
+
AI Florist
+
+ Fleumuse
+
+
+
+
+
+
diff --git a/src/lib/components/ui/map/MapPanel.svelte b/src/lib/components/ui/map/MapPanel.svelte
index 3e26f93..e1fab7b 100644
--- a/src/lib/components/ui/map/MapPanel.svelte
+++ b/src/lib/components/ui/map/MapPanel.svelte
@@ -2,6 +2,7 @@
import FloristOrderMessage from './FloristOrderMessage.svelte';
import KakaoMap from './KakaoMap.svelte';
import ShopList from './ShopList.svelte';
+ import { DEFAULT_MAP_CENTER } from '$lib/map/userLocation.js';
let {
shops = [],
@@ -12,14 +13,14 @@
fitBounds = false,
orderPlainText = '',
orderKoPlainText = '',
+ initialLat = DEFAULT_MAP_CENTER.lat,
+ initialLng = DEFAULT_MAP_CENTER.lng,
+ locationNotice = '',
onrefresh
} = $props();
- const DEFAULT_LAT = 37.5665;
- const DEFAULT_LNG = 126.978;
-
- let mapCenterLat = $state(DEFAULT_LAT);
- let mapCenterLng = $state(DEFAULT_LNG);
+ let mapCenterLat = $state(initialLat);
+ let mapCenterLng = $state(initialLng);
let panTarget = $state(null);
function handleCenterChange(lat, lng) {
@@ -46,6 +47,9 @@
Find a nearby florist
Move the map, then refresh to search this area.
+ {#if locationNotice}
+
{locationNotice}
+ {/if}
{#if mock}
Showing sample shops (no Kakao API key).
{/if}
@@ -63,8 +67,8 @@
} */
+export const ARTWORK_CARD_DEFAULTS = {
+ create: {
+ title: 'Who is this bouquet for?',
+ description:
+ 'Choose who will receive it, the occasion, and a style on the right. Adjust the budget if you like.'
+ },
+ message: {
+ title: 'Write a card message',
+ description:
+ 'Type a short note or pick a preset on the right. It will appear on your bouquet card.'
+ },
+ generating: {
+ title: 'Crafting your bouquet',
+ description:
+ 'We are turning their mood, photos, and message into a one-of-a-kind arrangement.'
+ },
+ map: {
+ title: 'Choose a florist',
+ description:
+ 'Browse nearby shops on the map and select where you would like to place your order.'
+ }
+};
diff --git a/src/lib/landing/landingGrowthStages.js b/src/lib/landing/landingGrowthStages.js
index 24f56cf..c1507f0 100644
--- a/src/lib/landing/landingGrowthStages.js
+++ b/src/lib/landing/landingGrowthStages.js
@@ -1,14 +1,31 @@
-import seedSrc from '$lib/assets/landing/seed.svg';
-import sproutSrc from '$lib/assets/landing/sprout.svg';
-import flowerSrc from '$lib/assets/landing/flower.svg';
-import bouquetSrc from '$lib/assets/landing/bouquet.svg';
+import { ARTWORK_SRC } from '$lib/components/ui/Artwork/artworkVariants.js';
-/** 랜딩 growth metaphor — ref/route illustration SVG 4단계 */
+/** 랜딩 growth metaphor — artwork 2 → 3 → 5 → 6 순서 */
export const LANDING_GROWTH_STAGES = [
- { id: 'seed', src: seedSrc, heightClass: 'h-[2.125rem] sm:h-9', delayMs: 0 },
- { id: 'sprout', src: sproutSrc, heightClass: 'h-16 sm:h-20', delayMs: 520 },
- { id: 'flower', src: flowerSrc, heightClass: 'h-24 sm:h-28', delayMs: 1040 },
- { id: 'bouquet', src: bouquetSrc, heightClass: 'h-36 sm:h-44 lg:h-52', delayMs: 1560 }
+ {
+ id: 'create2',
+ src: ARTWORK_SRC.create2,
+ heightClass: 'h-16 sm:h-20',
+ delayMs: 0
+ },
+ {
+ id: 'upload1',
+ src: ARTWORK_SRC.upload1,
+ heightClass: 'h-24 sm:h-28',
+ delayMs: 520
+ },
+ {
+ id: 'message1',
+ src: ARTWORK_SRC.message1,
+ heightClass: 'h-32 sm:h-36 lg:h-40',
+ delayMs: 1040
+ },
+ {
+ id: 'generated',
+ src: ARTWORK_SRC.generated,
+ heightClass: 'h-36 sm:h-44 lg:h-52',
+ delayMs: 1560
+ }
];
export const LANDING_STAGE_GAP_MS = 520;
diff --git a/src/lib/map/userLocation.js b/src/lib/map/userLocation.js
new file mode 100644
index 0000000..ea7d712
--- /dev/null
+++ b/src/lib/map/userLocation.js
@@ -0,0 +1,39 @@
+/** 서울시청 — 위치 권한 거부·미지원 시 fallback */
+export const DEFAULT_MAP_CENTER = { lat: 37.5665, lng: 126.978 };
+
+/**
+ * @typedef {{ lat: number, lng: number, fromDevice: boolean }} UserMapCenter
+ */
+
+/**
+ * 브라우저 Geolocation API로 현재 위치를 가져옵니다.
+ * 실패 시 DEFAULT_MAP_CENTER를 반환합니다.
+ *
+ * @returns {Promise}
+ */
+export function getUserMapCenter() {
+ return new Promise((resolve) => {
+ if (typeof navigator === 'undefined' || !navigator.geolocation) {
+ resolve({ ...DEFAULT_MAP_CENTER, fromDevice: false });
+ return;
+ }
+
+ navigator.geolocation.getCurrentPosition(
+ (position) => {
+ resolve({
+ lat: position.coords.latitude,
+ lng: position.coords.longitude,
+ fromDevice: true
+ });
+ },
+ () => {
+ resolve({ ...DEFAULT_MAP_CENTER, fromDevice: false });
+ },
+ {
+ enableHighAccuracy: true,
+ timeout: 10_000,
+ maximumAge: 60_000
+ }
+ );
+ });
+}
diff --git a/src/routes/create/+page.svelte b/src/routes/create/+page.svelte
index 76fb485..64f31d4 100644
--- a/src/routes/create/+page.svelte
+++ b/src/routes/create/+page.svelte
@@ -15,6 +15,7 @@
isDevSeeded,
saveFlow
} from '$lib/flowerFlow/session.js';
+ import { ARTWORK_CARD_DEFAULTS } from '$lib/flowerFlow/artworkCardCopy.js';
// 항상 빈 폼으로 시작 — Dev Fill은 onMount에서 1회만 스냅샷 적용
let who = $state(null);
@@ -25,7 +26,7 @@
const hasAnySelection = $derived(who !== null || whatFor !== null || style !== null);
const artworkTitle = $derived.by(() => {
- if (!hasAnySelection) return 'Title';
+ if (!hasAnySelection) return ARTWORK_CARD_DEFAULTS.create.title;
const occasion = whatFor ? `A ${whatFor} bouquet for` : 'A bouquet for';
return `${occasion} ${who ?? '...'}`;
});
@@ -34,10 +35,12 @@
const artworkDescription = $derived(
hasAnySelection
- ? `${style ?? '...'} style · ₩${budget.toLocaleString('ko-KR')} budget`
- : 'Description Description Description'
+ ? `${style ?? '—'} style · ₩${budget.toLocaleString('ko-KR')} budget`
+ : ARTWORK_CARD_DEFAULTS.create.description
);
+ const artworkCardMode = $derived(hasAnySelection ? 'summary' : 'instruction');
+
onMount(() => {
const hadSnapshot = !!getFlowObject('devCreateSnapshot');
const snap = consumeDevCreateSnapshot();
@@ -87,7 +90,12 @@
-
+
diff --git a/src/routes/generating/+page.svelte b/src/routes/generating/+page.svelte
index 1608d39..7ee6c97 100644
--- a/src/routes/generating/+page.svelte
+++ b/src/routes/generating/+page.svelte
@@ -16,6 +16,7 @@
loadFlow,
saveFlow
} from '$lib/flowerFlow/session.js';
+ import { ARTWORK_CARD_DEFAULTS } from '$lib/flowerFlow/artworkCardCopy.js';
const MAX_RETRIES = 5;
const userInput = getFlowUserInput();
@@ -24,12 +25,20 @@
const artworkTitle = $derived.by(() => {
const who = typeof userInput.relationship === 'string' ? userInput.relationship : null;
const whatFor = typeof userInput.occasion === 'string' ? userInput.occasion : null;
- if (!who && !whatFor) return 'Your bouquet';
+ if (!who && !whatFor) return ARTWORK_CARD_DEFAULTS.generating.title;
const occasion = whatFor ? `A ${whatFor} bouquet for` : 'A bouquet for';
return `${occasion} ${who ?? '...'}`;
});
- const artworkDescription = $derived(cardMessage || '잠시 관리중 ~');
+ const artworkDescription = $derived(
+ cardMessage?.trim() || ARTWORK_CARD_DEFAULTS.generating.description
+ );
+
+ const artworkCardMode = $derived.by(() => {
+ const who = typeof userInput.relationship === 'string' ? userInput.relationship : null;
+ const whatFor = typeof userInput.occasion === 'string' ? userInput.occasion : null;
+ return who || whatFor || cardMessage?.trim() ? 'summary' : 'instruction';
+ });
/** @type {import('$lib/components/ui/Artwork/artworkVariants.js').ArtworkVariant} */
let artworkVariant = $state('create2');
@@ -212,7 +221,13 @@
-
+
@@ -98,20 +118,34 @@
-
+
- loadShops(lat, lng, { fitBounds: false })}
- />
+ {#if locationReady}
+ loadShops(lat, lng, { fitBounds: false })}
+ />
+ {:else}
+
+ Getting your location...
+
+ {/if}
diff --git a/src/routes/message/+page.svelte b/src/routes/message/+page.svelte
index 0604a46..ce9db3a 100644
--- a/src/routes/message/+page.svelte
+++ b/src/routes/message/+page.svelte
@@ -19,6 +19,7 @@
loadFlow,
saveFlow
} from '$lib/flowerFlow/session.js';
+ import { ARTWORK_CARD_DEFAULTS } from '$lib/flowerFlow/artworkCardCopy.js';
const userInput = getFlowUserInput();
@@ -27,11 +28,19 @@
let error = $state('');
let skipping = $state(false);
- const artworkVariant = $derived(message.trim() ? 'message1' : 'upload2');
+ const hasMessage = $derived(message.trim().length > 0);
- const artworkTitle = $derived(message ? 'Your message' : 'Title');
+ const artworkVariant = $derived(hasMessage ? 'message1' : 'upload2');
- const artworkDescription = $derived(message || 'Description Description Description');
+ const artworkTitle = $derived(
+ hasMessage ? 'Your message' : ARTWORK_CARD_DEFAULTS.message.title
+ );
+
+ const artworkDescription = $derived(
+ hasMessage ? message.trim() : ARTWORK_CARD_DEFAULTS.message.description
+ );
+
+ const artworkCardMode = $derived(hasMessage ? 'summary' : 'instruction');
onMount(() => {
const hadSnapshot = !!getFlowObject('devMessageSnapshot');
@@ -107,7 +116,12 @@
-
+
diff --git a/src/routes/upload/+page.svelte b/src/routes/upload/+page.svelte
index e99e506..9a14eb5 100644
--- a/src/routes/upload/+page.svelte
+++ b/src/routes/upload/+page.svelte
@@ -127,6 +127,15 @@
const artworkTitle = $derived(artworkCopy.title);
const artworkDescription = $derived(artworkCopy.description);
+ const artworkCardMode = $derived.by(() => {
+ if (mode === 'sns') return snsHasImage ? 'summary' : 'instruction';
+
+ const count = ['color', 'season', 'character', 'location'].filter(
+ (key) => moodboardTiles[key]
+ ).length;
+ return count === 0 ? 'instruction' : 'summary';
+ });
+
/** create2(시작) → upload1(1장+) → upload2(전체 채움) */
const artworkVariant = $derived.by(() => {
if (mode === 'sns') {
@@ -193,7 +202,12 @@
-
+