프로토타입 번호
This commit is contained in:
parent
380a4a5319
commit
c441df22e3
BIN
.playwright-mcp/debug/prototype-00-login.png
Normal file
BIN
.playwright-mcp/debug/prototype-00-login.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 138 KiB |
@ -4,140 +4,13 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AI 이미지 생성 - KT 이벤트 마케팅</title>
|
||||
<link rel="stylesheet" href="css/variables.css">
|
||||
<link rel="stylesheet" href="css/common.css">
|
||||
<style>
|
||||
/* CSS Variables */
|
||||
:root {
|
||||
/* Colors */
|
||||
--color-primary: #E31E24;
|
||||
--color-primary-dark: #B71C1C;
|
||||
--color-primary-light: #FFEBEE;
|
||||
--color-secondary: #FF6B6B;
|
||||
--color-secondary-dark: #FF5252;
|
||||
--color-secondary-light: #FFE9E9;
|
||||
--color-success: #4CAF50;
|
||||
--color-success-light: #E8F5E9;
|
||||
--color-warning: #FF9800;
|
||||
--color-warning-light: #FFF3E0;
|
||||
--color-error: #F44336;
|
||||
--color-error-light: #FFEBEE;
|
||||
--color-info: #2196F3;
|
||||
--color-info-light: #E3F2FD;
|
||||
|
||||
/* Grayscale */
|
||||
--color-white: #FFFFFF;
|
||||
--color-gray-50: #FAFAFA;
|
||||
--color-gray-100: #F5F5F5;
|
||||
--color-gray-200: #EEEEEE;
|
||||
--color-gray-300: #E0E0E0;
|
||||
--color-gray-400: #BDBDBD;
|
||||
--color-gray-500: #9E9E9E;
|
||||
--color-gray-600: #757575;
|
||||
--color-gray-700: #616161;
|
||||
--color-gray-800: #424242;
|
||||
--color-gray-900: #212121;
|
||||
--color-black: #000000;
|
||||
|
||||
/* Text Colors */
|
||||
--color-text-primary: #212121;
|
||||
--color-text-secondary: #757575;
|
||||
--color-text-tertiary: #9E9E9E;
|
||||
--color-text-disabled: #BDBDBD;
|
||||
--color-text-inverse: #FFFFFF;
|
||||
|
||||
/* Border Colors */
|
||||
--color-border-light: #E0E0E0;
|
||||
--color-border-medium: #BDBDBD;
|
||||
--color-border-dark: #757575;
|
||||
|
||||
/* Background Colors */
|
||||
--color-bg-primary: #FFFFFF;
|
||||
--color-bg-secondary: #F5F5F5;
|
||||
--color-bg-tertiary: #FAFAFA;
|
||||
|
||||
/* Spacing (4px base unit) */
|
||||
--spacing-xs: 4px;
|
||||
--spacing-s: 8px;
|
||||
--spacing-m: 16px;
|
||||
--spacing-l: 24px;
|
||||
--spacing-xl: 32px;
|
||||
--spacing-2xl: 48px;
|
||||
--spacing-3xl: 64px;
|
||||
|
||||
/* Typography */
|
||||
--font-family-primary: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
--font-size-xs: 12px;
|
||||
--font-size-s: 14px;
|
||||
--font-size-m: 16px;
|
||||
--font-size-l: 18px;
|
||||
--font-size-xl: 20px;
|
||||
--font-size-2xl: 24px;
|
||||
--font-size-3xl: 32px;
|
||||
--font-size-4xl: 40px;
|
||||
|
||||
--font-weight-regular: 400;
|
||||
--font-weight-medium: 500;
|
||||
--font-weight-semibold: 600;
|
||||
--font-weight-bold: 700;
|
||||
|
||||
--line-height-tight: 1.2;
|
||||
--line-height-normal: 1.5;
|
||||
--line-height-relaxed: 1.75;
|
||||
|
||||
/* Border Radius */
|
||||
--radius-s: 4px;
|
||||
--radius-m: 8px;
|
||||
--radius-l: 12px;
|
||||
--radius-xl: 16px;
|
||||
--radius-2xl: 24px;
|
||||
--radius-full: 9999px;
|
||||
|
||||
/* Shadows */
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
|
||||
|
||||
/* Transitions */
|
||||
--transition-fast: 150ms ease-in-out;
|
||||
--transition-normal: 250ms ease-in-out;
|
||||
--transition-slow: 350ms ease-in-out;
|
||||
|
||||
/* Z-index */
|
||||
--z-dropdown: 1000;
|
||||
--z-sticky: 1020;
|
||||
--z-fixed: 1030;
|
||||
--z-modal-backdrop: 1040;
|
||||
--z-modal: 1050;
|
||||
--z-popover: 1060;
|
||||
--z-tooltip: 1070;
|
||||
}
|
||||
|
||||
/* Reset & Base Styles */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-family-primary);
|
||||
font-size: var(--font-size-m);
|
||||
line-height: var(--line-height-normal);
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-bg-secondary);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
.image-container {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #FFF5F5 0%, #FFFFFF 100%);
|
||||
background: linear-gradient(135deg, #F8F9FF 0%, #FFFFFF 100%);
|
||||
padding-bottom: var(--spacing-2xl);
|
||||
}
|
||||
|
||||
@ -175,126 +48,307 @@
|
||||
padding: var(--spacing-l) var(--spacing-m);
|
||||
}
|
||||
|
||||
/* Loading State */
|
||||
.loading-state {
|
||||
/* Prompt Section */
|
||||
.prompt-section {
|
||||
background: var(--color-white);
|
||||
border-radius: var(--radius-l);
|
||||
padding: var(--spacing-2xl);
|
||||
padding: var(--spacing-m);
|
||||
margin-bottom: var(--spacing-l);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.prompt-section__label {
|
||||
font-size: var(--font-size-m);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text-primary);
|
||||
margin-bottom: var(--spacing-s);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.prompt-section__textarea {
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
padding: var(--spacing-m);
|
||||
border: 1px solid var(--color-border-light);
|
||||
border-radius: var(--radius-m);
|
||||
font-size: var(--font-size-m);
|
||||
font-family: var(--font-family);
|
||||
resize: vertical;
|
||||
transition: all var(--transition-normal);
|
||||
margin-bottom: var(--spacing-s);
|
||||
}
|
||||
|
||||
.prompt-section__textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-secondary);
|
||||
box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1);
|
||||
}
|
||||
|
||||
.prompt-section__hint {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-text-tertiary);
|
||||
margin-bottom: var(--spacing-m);
|
||||
}
|
||||
|
||||
.prompt-section__styles {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: var(--spacing-s);
|
||||
margin-bottom: var(--spacing-m);
|
||||
}
|
||||
|
||||
.style-chip {
|
||||
padding: var(--spacing-s);
|
||||
border: 2px solid var(--color-border-light);
|
||||
border-radius: var(--radius-m);
|
||||
text-align: center;
|
||||
font-size: var(--font-size-s);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-normal);
|
||||
}
|
||||
|
||||
.style-chip:hover {
|
||||
border-color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.style-chip--selected {
|
||||
border-color: var(--color-secondary);
|
||||
background: linear-gradient(135deg, #F0F7FF 0%, #FFFFFF 100%);
|
||||
color: var(--color-secondary);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.generate-button {
|
||||
width: 100%;
|
||||
padding: var(--spacing-m);
|
||||
background: linear-gradient(135deg, var(--color-secondary) 0%, var(--color-secondary-dark) 100%);
|
||||
color: var(--color-white);
|
||||
border: none;
|
||||
border-radius: var(--radius-m);
|
||||
font-size: var(--font-size-m);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-normal);
|
||||
}
|
||||
|
||||
.generate-button:hover:not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.loading-state__icon {
|
||||
font-size: 64px;
|
||||
margin-bottom: var(--spacing-m);
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
.generate-button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
.generate-button--loading {
|
||||
position: relative;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.loading-state__title {
|
||||
font-size: var(--font-size-l);
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--color-text-primary);
|
||||
margin-bottom: var(--spacing-s);
|
||||
.generate-button--loading:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -10px;
|
||||
margin-top: -10px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: var(--color-white);
|
||||
animation: spinner 0.6s linear infinite;
|
||||
}
|
||||
|
||||
.loading-state__text {
|
||||
font-size: var(--font-size-s);
|
||||
color: var(--color-text-secondary);
|
||||
margin-bottom: var(--spacing-l);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: var(--color-gray-200);
|
||||
border-radius: var(--radius-full);
|
||||
overflow: hidden;
|
||||
margin-bottom: var(--spacing-s);
|
||||
}
|
||||
|
||||
.progress-bar__fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--color-secondary), var(--color-primary));
|
||||
border-radius: var(--radius-full);
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
font-size: var(--font-size-s);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* Results */
|
||||
.image-results {
|
||||
/* Loading State */
|
||||
.loading-state {
|
||||
display: none;
|
||||
text-align: center;
|
||||
padding: var(--spacing-2xl) var(--spacing-m);
|
||||
}
|
||||
|
||||
.image-results--visible {
|
||||
.loading-state--visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.loading-animation {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin: 0 auto var(--spacing-l);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.loading-animation__circle {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 4px solid rgba(0, 102, 255, 0.1);
|
||||
border-radius: 50%;
|
||||
border-top-color: var(--color-secondary);
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-animation__icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.loading-state__text {
|
||||
font-size: var(--font-size-l);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* Results Section */
|
||||
.results-section {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.results-section--visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.results-section__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--spacing-m);
|
||||
}
|
||||
|
||||
.results-section__title {
|
||||
font-size: var(--font-size-l);
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.results-section__regenerate {
|
||||
background: none;
|
||||
border: 1px solid var(--color-border-light);
|
||||
color: var(--color-text-secondary);
|
||||
padding: var(--spacing-xs) var(--spacing-m);
|
||||
border-radius: var(--radius-m);
|
||||
font-size: var(--font-size-s);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-normal);
|
||||
}
|
||||
|
||||
.results-section__regenerate:hover {
|
||||
border-color: var(--color-secondary);
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
|
||||
/* Image Grid */
|
||||
.image-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: var(--spacing-m);
|
||||
margin-bottom: var(--spacing-l);
|
||||
}
|
||||
|
||||
.image-item {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
background: linear-gradient(135deg, #FFE9E9 0%, #FFF5F5 100%);
|
||||
/* Image Card */
|
||||
.image-card {
|
||||
background: var(--color-white);
|
||||
border: 2px solid var(--color-border-light);
|
||||
border-radius: var(--radius-l);
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
border: 3px solid transparent;
|
||||
transition: all var(--transition-normal);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.image-item:hover {
|
||||
transform: translateY(-4px);
|
||||
.image-card:hover {
|
||||
border-color: var(--color-secondary);
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.image-card--selected {
|
||||
border-color: var(--color-secondary);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.image-item--selected {
|
||||
border-color: var(--color-secondary);
|
||||
box-shadow: var(--shadow-xl);
|
||||
}
|
||||
|
||||
.image-item__preview {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 64px;
|
||||
}
|
||||
|
||||
.image-item__checkbox {
|
||||
.image-card__badge {
|
||||
position: absolute;
|
||||
top: var(--spacing-s);
|
||||
right: var(--spacing-s);
|
||||
background: linear-gradient(135deg, var(--color-secondary) 0%, var(--color-secondary-dark) 100%);
|
||||
color: var(--color-white);
|
||||
padding: var(--spacing-xs) var(--spacing-s);
|
||||
border-radius: var(--radius-s);
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
box-shadow: var(--shadow-md);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.image-card__checkbox {
|
||||
position: absolute;
|
||||
top: var(--spacing-s);
|
||||
left: var(--spacing-s);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: var(--color-white);
|
||||
border: 2px solid var(--color-border-light);
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.image-card__preview {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
background: linear-gradient(135deg, #E0E7FF 0%, #F0F7FF 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 48px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.image-card__info {
|
||||
padding: var(--spacing-m);
|
||||
}
|
||||
|
||||
.image-card__title {
|
||||
font-size: var(--font-size-m);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text-primary);
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.image-card__style {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-white);
|
||||
color: var(--color-text-secondary);
|
||||
margin-bottom: var(--spacing-s);
|
||||
}
|
||||
|
||||
.image-card__actions {
|
||||
display: flex;
|
||||
gap: var(--spacing-xs);
|
||||
padding-top: var(--spacing-s);
|
||||
border-top: 1px solid var(--color-border-light);
|
||||
}
|
||||
|
||||
.image-card__action-btn {
|
||||
flex: 1;
|
||||
padding: var(--spacing-xs);
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-border-light);
|
||||
border-radius: var(--radius-s);
|
||||
font-size: var(--font-size-xs);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-normal);
|
||||
}
|
||||
|
||||
.image-item--selected .image-item__checkbox {
|
||||
background: var(--color-secondary);
|
||||
.image-card__action-btn:hover {
|
||||
border-color: var(--color-secondary);
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
|
||||
/* Next Button */
|
||||
@ -359,6 +413,10 @@
|
||||
}
|
||||
|
||||
.image-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.prompt-section__styles {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
@ -379,163 +437,287 @@
|
||||
←
|
||||
</button>
|
||||
<h1 class="image-header__title">AI 이미지 생성</h1>
|
||||
<p class="image-header__subtitle">이벤트에 사용할 매력적인 이미지를 생성해 드려요</p>
|
||||
<p class="image-header__subtitle">이벤트에 활용할 이미지를 AI로 생성해 보세요</p>
|
||||
</header>
|
||||
|
||||
<!-- Content -->
|
||||
<main class="image-content">
|
||||
<!-- Prompt Section -->
|
||||
<div class="prompt-section" id="promptSection">
|
||||
<label class="prompt-section__label">
|
||||
✨ 원하는 이미지 설명
|
||||
</label>
|
||||
<textarea
|
||||
class="prompt-section__textarea"
|
||||
id="promptInput"
|
||||
placeholder="예: 친구들이 함께 행복하게 웃고 있는 모습, 밝고 따뜻한 분위기"
|
||||
maxlength="500"
|
||||
></textarea>
|
||||
<p class="prompt-section__hint">
|
||||
생성하고 싶은 이미지를 자세히 설명해 주세요 (최대 500자)
|
||||
</p>
|
||||
|
||||
<label class="prompt-section__label">
|
||||
🎨 이미지 스타일
|
||||
</label>
|
||||
<div class="prompt-section__styles">
|
||||
<div class="style-chip style-chip--selected" data-style="modern">
|
||||
🌟 모던
|
||||
</div>
|
||||
<div class="style-chip" data-style="warm">
|
||||
☀️ 따뜻한
|
||||
</div>
|
||||
<div class="style-chip" data-style="vibrant">
|
||||
🎨 생동감
|
||||
</div>
|
||||
<div class="style-chip" data-style="minimal">
|
||||
✨ 미니멀
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="generate-button" id="generateButton">
|
||||
🎨 이미지 생성하기
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Loading State -->
|
||||
<div class="loading-state" id="loadingState">
|
||||
<div class="loading-state__icon">🎨</div>
|
||||
<h2 class="loading-state__title">AI가 이미지를 생성하고 있어요</h2>
|
||||
<p class="loading-state__text">
|
||||
이벤트 정보를 바탕으로 최적의 이미지를 생성 중입니다.<br>
|
||||
잠시만 기다려 주세요...
|
||||
<div class="loading-animation">
|
||||
<div class="loading-animation__circle"></div>
|
||||
<div class="loading-animation__icon">🎨</div>
|
||||
</div>
|
||||
<p class="loading-state__text" id="loadingText">
|
||||
AI가 이미지를 생성하고 있어요...
|
||||
</p>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-bar__fill" id="progressBar" style="width: 0%"></div>
|
||||
</div>
|
||||
<p class="progress-text" id="progressText">0%</p>
|
||||
</div>
|
||||
|
||||
<!-- Results -->
|
||||
<div class="image-results" id="imageResults">
|
||||
<!-- Results Section -->
|
||||
<div class="results-section" id="resultsSection">
|
||||
<div class="results-section__header">
|
||||
<h2 class="results-section__title">생성된 이미지</h2>
|
||||
<button class="results-section__regenerate" id="regenerateButton">
|
||||
🔄 다시 생성
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Image Grid -->
|
||||
<div class="image-grid" id="imageGrid">
|
||||
<!-- Images will be inserted here -->
|
||||
<!-- Image cards will be inserted here -->
|
||||
</div>
|
||||
|
||||
<!-- Next Button -->
|
||||
<button class="next-button" id="nextButton" disabled>
|
||||
선택 완료 (최소 1개)
|
||||
다음 단계로
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/common.js"></script>
|
||||
<script>
|
||||
// Utility functions
|
||||
const $ = (selector) => document.querySelector(selector);
|
||||
|
||||
const addClass = (el, className) => el?.classList.add(className);
|
||||
const removeClass = (el, className) => el?.classList.remove(className);
|
||||
const hasClass = (el, className) => el?.classList.contains(className);
|
||||
|
||||
const storage = {
|
||||
get: (key) => {
|
||||
try {
|
||||
const item = localStorage.getItem(key);
|
||||
return item ? JSON.parse(item) : null;
|
||||
} catch (e) {
|
||||
console.error('Storage get error:', e);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
set: (key, value) => {
|
||||
try {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
} catch (e) {
|
||||
console.error('Storage set error:', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
const { $, addClass, removeClass, storage } = window.CommonUtils;
|
||||
|
||||
// Elements
|
||||
const promptSection = $('#promptSection');
|
||||
const promptInput = $('#promptInput');
|
||||
const styleChips = document.querySelectorAll('.style-chip');
|
||||
const generateButton = $('#generateButton');
|
||||
const loadingState = $('#loadingState');
|
||||
const progressBar = $('#progressBar');
|
||||
const progressText = $('#progressText');
|
||||
const imageResults = $('#imageResults');
|
||||
const loadingText = $('#loadingText');
|
||||
const resultsSection = $('#resultsSection');
|
||||
const imageGrid = $('#imageGrid');
|
||||
const regenerateButton = $('#regenerateButton');
|
||||
const nextButton = $('#nextButton');
|
||||
|
||||
// State
|
||||
const selectedImages = new Set();
|
||||
let currentProgress = 0;
|
||||
let selectedStyle = 'modern';
|
||||
let selectedImages = [];
|
||||
const generatedImages = [];
|
||||
|
||||
// Mock generated images
|
||||
const generatedImages = [
|
||||
{ id: 1, icon: '🎉', alt: '친구 초대 이벤트 이미지 1' },
|
||||
{ id: 2, icon: '🎊', alt: '친구 초대 이벤트 이미지 2' },
|
||||
{ id: 3, icon: '🎈', alt: '친구 초대 이벤트 이미지 3' },
|
||||
{ id: 4, icon: '🎁', alt: '친구 초대 이벤트 이미지 4' },
|
||||
{ id: 5, icon: '🌟', alt: '친구 초대 이벤트 이미지 5' },
|
||||
{ id: 6, icon: '✨', alt: '친구 초대 이벤트 이미지 6' },
|
||||
{ id: 7, icon: '💝', alt: '친구 초대 이벤트 이미지 7' },
|
||||
{ id: 8, icon: '🎯', alt: '친구 초대 이벤트 이미지 8' }
|
||||
// Handle style selection
|
||||
styleChips.forEach(chip => {
|
||||
chip.addEventListener('click', () => {
|
||||
styleChips.forEach(c => removeClass(c, 'style-chip--selected'));
|
||||
addClass(chip, 'style-chip--selected');
|
||||
selectedStyle = chip.getAttribute('data-style');
|
||||
});
|
||||
});
|
||||
|
||||
// Generate images
|
||||
generateButton.addEventListener('click', () => {
|
||||
const prompt = promptInput.value.trim();
|
||||
|
||||
if (!prompt) {
|
||||
alert('이미지 설명을 입력해 주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
generateImages(prompt, selectedStyle);
|
||||
});
|
||||
|
||||
// Regenerate images
|
||||
regenerateButton.addEventListener('click', () => {
|
||||
const prompt = promptInput.value.trim();
|
||||
generateImages(prompt, selectedStyle);
|
||||
});
|
||||
|
||||
// Generate images function
|
||||
function generateImages(prompt, style) {
|
||||
// Hide prompt section and results
|
||||
promptSection.style.display = 'none';
|
||||
removeClass(resultsSection, 'results-section--visible');
|
||||
|
||||
// Show loading
|
||||
addClass(loadingState, 'loading-state--visible');
|
||||
|
||||
// Simulate AI generation
|
||||
const loadingTexts = [
|
||||
'AI가 이미지를 생성하고 있어요...',
|
||||
'이미지 구도를 설계하고 있어요...',
|
||||
'색상과 스타일을 조정하고 있어요...',
|
||||
'마지막 마무리 중이에요...'
|
||||
];
|
||||
|
||||
// Simulate AI image generation
|
||||
function simulateGeneration() {
|
||||
const interval = setInterval(() => {
|
||||
currentProgress += 12.5;
|
||||
if (currentProgress >= 100) {
|
||||
currentProgress = 100;
|
||||
clearInterval(interval);
|
||||
setTimeout(showResults, 500);
|
||||
let step = 0;
|
||||
const loadingInterval = setInterval(() => {
|
||||
if (step < loadingTexts.length) {
|
||||
loadingText.textContent = loadingTexts[step];
|
||||
step++;
|
||||
}
|
||||
updateProgress(currentProgress);
|
||||
}, 400);
|
||||
}, 1200);
|
||||
|
||||
// Show results after delay
|
||||
setTimeout(() => {
|
||||
clearInterval(loadingInterval);
|
||||
removeClass(loadingState, 'loading-state--visible');
|
||||
renderImages(prompt, style);
|
||||
addClass(resultsSection, 'results-section--visible');
|
||||
promptSection.style.display = 'block';
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function updateProgress(progress) {
|
||||
progressBar.style.width = progress + '%';
|
||||
progressText.textContent = Math.round(progress) + '%';
|
||||
// Render generated images
|
||||
function renderImages(prompt, style) {
|
||||
const imageVariations = [
|
||||
{
|
||||
id: 1,
|
||||
title: '옵션 1',
|
||||
style: style === 'modern' ? '모던 스타일' :
|
||||
style === 'warm' ? '따뜻한 스타일' :
|
||||
style === 'vibrant' ? '생동감 있는 스타일' :
|
||||
'미니멀 스타일',
|
||||
icon: '🎉',
|
||||
recommended: true
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '옵션 2',
|
||||
style: style === 'modern' ? '모던 스타일' :
|
||||
style === 'warm' ? '따뜻한 스타일' :
|
||||
style === 'vibrant' ? '생동감 있는 스타일' :
|
||||
'미니멀 스타일',
|
||||
icon: '🎊',
|
||||
recommended: false
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '옵션 3',
|
||||
style: style === 'modern' ? '모던 스타일' :
|
||||
style === 'warm' ? '따뜻한 스타일' :
|
||||
style === 'vibrant' ? '생동감 있는 스타일' :
|
||||
'미니멀 스타일',
|
||||
icon: '🎈',
|
||||
recommended: false
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: '옵션 4',
|
||||
style: style === 'modern' ? '모던 스타일' :
|
||||
style === 'warm' ? '따뜻한 스타일' :
|
||||
style === 'vibrant' ? '생동감 있는 스타일' :
|
||||
'미니멀 스타일',
|
||||
icon: '🎁',
|
||||
recommended: false
|
||||
}
|
||||
];
|
||||
|
||||
function showResults() {
|
||||
loadingState.style.display = 'none';
|
||||
addClass(imageResults, 'image-results--visible');
|
||||
renderImages();
|
||||
}
|
||||
|
||||
function renderImages() {
|
||||
imageGrid.innerHTML = generatedImages.map(image => `
|
||||
<div class="image-item" data-id="${image.id}">
|
||||
<div class="image-item__preview">${image.icon}</div>
|
||||
<div class="image-item__checkbox">✓</div>
|
||||
imageGrid.innerHTML = imageVariations.map(image => `
|
||||
<div class="image-card" data-id="${image.id}">
|
||||
${image.recommended ? '<div class="image-card__badge">🤖 AI 추천</div>' : ''}
|
||||
<input type="checkbox" class="image-card__checkbox" value="${image.id}">
|
||||
<div class="image-card__preview">${image.icon}</div>
|
||||
<div class="image-card__info">
|
||||
<h3 class="image-card__title">${image.title}</h3>
|
||||
<p class="image-card__style">${image.style}</p>
|
||||
<div class="image-card__actions">
|
||||
<button class="image-card__action-btn" onclick="downloadImage(${image.id})">
|
||||
💾 저장
|
||||
</button>
|
||||
<button class="image-card__action-btn" onclick="editImage(${image.id})">
|
||||
✏️ 수정
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
// Add click listeners
|
||||
const items = imageGrid.querySelectorAll('.image-item');
|
||||
items.forEach(item => {
|
||||
item.addEventListener('click', () => {
|
||||
const imageId = parseInt(item.getAttribute('data-id'));
|
||||
toggleImage(imageId, item);
|
||||
// Add event listeners
|
||||
const cards = document.querySelectorAll('.image-card');
|
||||
cards.forEach(card => {
|
||||
const checkbox = card.querySelector('.image-card__checkbox');
|
||||
|
||||
card.addEventListener('click', (e) => {
|
||||
if (e.target.closest('.image-card__action-btn')) return;
|
||||
|
||||
checkbox.checked = !checkbox.checked;
|
||||
toggleImageSelection(card, checkbox.checked);
|
||||
});
|
||||
|
||||
checkbox.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
toggleImageSelection(card, checkbox.checked);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function toggleImage(imageId, element) {
|
||||
if (selectedImages.has(imageId)) {
|
||||
selectedImages.delete(imageId);
|
||||
removeClass(element, 'image-item--selected');
|
||||
} else {
|
||||
selectedImages.add(imageId);
|
||||
addClass(element, 'image-item--selected');
|
||||
// Toggle image selection
|
||||
function toggleImageSelection(card, isSelected) {
|
||||
const id = parseInt(card.getAttribute('data-id'));
|
||||
|
||||
if (isSelected) {
|
||||
addClass(card, 'image-card--selected');
|
||||
if (!selectedImages.includes(id)) {
|
||||
selectedImages.push(id);
|
||||
}
|
||||
updateNextButton();
|
||||
} else {
|
||||
removeClass(card, 'image-card--selected');
|
||||
selectedImages = selectedImages.filter(imageId => imageId !== id);
|
||||
}
|
||||
|
||||
function updateNextButton() {
|
||||
const count = selectedImages.size;
|
||||
if (count > 0) {
|
||||
nextButton.disabled = false;
|
||||
nextButton.textContent = `선택 완료 (${count}개)`;
|
||||
} else {
|
||||
nextButton.disabled = true;
|
||||
nextButton.textContent = '선택 완료 (최소 1개)';
|
||||
}
|
||||
nextButton.disabled = selectedImages.length === 0;
|
||||
}
|
||||
|
||||
// Download image
|
||||
window.downloadImage = function(id) {
|
||||
alert(`이미지 ${id}를 다운로드했습니다.`);
|
||||
};
|
||||
|
||||
// Edit image
|
||||
window.editImage = function(id) {
|
||||
alert(`이미지 ${id} 편집 기능은 다음 버전에서 제공됩니다.`);
|
||||
};
|
||||
|
||||
// Handle next button
|
||||
nextButton.addEventListener('click', () => {
|
||||
if (selectedImages.size === 0) return;
|
||||
if (selectedImages.length === 0) return;
|
||||
|
||||
// Save selected images
|
||||
const eventContent = storage.get('eventContent') || {};
|
||||
eventContent.generatedImages = {
|
||||
selectedImages: Array.from(selectedImages),
|
||||
images: generatedImages.filter(img => selectedImages.has(img.id)),
|
||||
createdAt: new Date().toISOString()
|
||||
prompt: promptInput.value.trim(),
|
||||
style: selectedStyle,
|
||||
selectedImages: selectedImages,
|
||||
generatedAt: new Date().toISOString()
|
||||
};
|
||||
storage.set('eventContent', eventContent);
|
||||
|
||||
@ -545,15 +727,16 @@
|
||||
|
||||
// Navigate to next screen
|
||||
setTimeout(() => {
|
||||
window.location.href = '10-AI영상제작.html';
|
||||
window.location.href = '11-SNS콘텐츠생성.html';
|
||||
}, 800);
|
||||
});
|
||||
|
||||
// Initialize
|
||||
// Auto-fill prompt from event draft
|
||||
window.addEventListener('load', () => {
|
||||
setTimeout(() => {
|
||||
simulateGeneration();
|
||||
}, 500);
|
||||
const eventDraft = storage.get('eventDraft') || {};
|
||||
if (eventDraft.promotionalCopy && !promptInput.value) {
|
||||
promptInput.value = `${eventDraft.promotionalCopy.headline} 이미지`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@ -1,743 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AI 이미지 생성 - KT 이벤트 마케팅</title>
|
||||
<link rel="stylesheet" href="css/variables.css">
|
||||
<link rel="stylesheet" href="css/common.css">
|
||||
<style>
|
||||
/* Layout */
|
||||
.image-container {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #F8F9FF 0%, #FFFFFF 100%);
|
||||
padding-bottom: var(--spacing-2xl);
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.image-header {
|
||||
background: linear-gradient(135deg, var(--color-secondary) 0%, var(--color-secondary-dark) 100%);
|
||||
color: var(--color-white);
|
||||
padding: var(--spacing-xl) var(--spacing-m);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.image-header__back {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--color-white);
|
||||
font-size: var(--font-size-xl);
|
||||
padding: 0;
|
||||
margin-bottom: var(--spacing-m);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.image-header__title {
|
||||
font-size: var(--font-size-2xl);
|
||||
font-weight: var(--font-weight-bold);
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.image-header__subtitle {
|
||||
font-size: var(--font-size-m);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* Content */
|
||||
.image-content {
|
||||
padding: var(--spacing-l) var(--spacing-m);
|
||||
}
|
||||
|
||||
/* Prompt Section */
|
||||
.prompt-section {
|
||||
background: var(--color-white);
|
||||
border-radius: var(--radius-l);
|
||||
padding: var(--spacing-m);
|
||||
margin-bottom: var(--spacing-l);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.prompt-section__label {
|
||||
font-size: var(--font-size-m);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text-primary);
|
||||
margin-bottom: var(--spacing-s);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.prompt-section__textarea {
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
padding: var(--spacing-m);
|
||||
border: 1px solid var(--color-border-light);
|
||||
border-radius: var(--radius-m);
|
||||
font-size: var(--font-size-m);
|
||||
font-family: var(--font-family);
|
||||
resize: vertical;
|
||||
transition: all var(--transition-normal);
|
||||
margin-bottom: var(--spacing-s);
|
||||
}
|
||||
|
||||
.prompt-section__textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-secondary);
|
||||
box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1);
|
||||
}
|
||||
|
||||
.prompt-section__hint {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-text-tertiary);
|
||||
margin-bottom: var(--spacing-m);
|
||||
}
|
||||
|
||||
.prompt-section__styles {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: var(--spacing-s);
|
||||
margin-bottom: var(--spacing-m);
|
||||
}
|
||||
|
||||
.style-chip {
|
||||
padding: var(--spacing-s);
|
||||
border: 2px solid var(--color-border-light);
|
||||
border-radius: var(--radius-m);
|
||||
text-align: center;
|
||||
font-size: var(--font-size-s);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-normal);
|
||||
}
|
||||
|
||||
.style-chip:hover {
|
||||
border-color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.style-chip--selected {
|
||||
border-color: var(--color-secondary);
|
||||
background: linear-gradient(135deg, #F0F7FF 0%, #FFFFFF 100%);
|
||||
color: var(--color-secondary);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.generate-button {
|
||||
width: 100%;
|
||||
padding: var(--spacing-m);
|
||||
background: linear-gradient(135deg, var(--color-secondary) 0%, var(--color-secondary-dark) 100%);
|
||||
color: var(--color-white);
|
||||
border: none;
|
||||
border-radius: var(--radius-m);
|
||||
font-size: var(--font-size-m);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-normal);
|
||||
}
|
||||
|
||||
.generate-button:hover:not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.generate-button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.generate-button--loading {
|
||||
position: relative;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.generate-button--loading:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -10px;
|
||||
margin-top: -10px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: var(--color-white);
|
||||
animation: spinner 0.6s linear infinite;
|
||||
}
|
||||
|
||||
/* Loading State */
|
||||
.loading-state {
|
||||
display: none;
|
||||
text-align: center;
|
||||
padding: var(--spacing-2xl) var(--spacing-m);
|
||||
}
|
||||
|
||||
.loading-state--visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.loading-animation {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin: 0 auto var(--spacing-l);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.loading-animation__circle {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 4px solid rgba(0, 102, 255, 0.1);
|
||||
border-radius: 50%;
|
||||
border-top-color: var(--color-secondary);
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-animation__icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.loading-state__text {
|
||||
font-size: var(--font-size-l);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* Results Section */
|
||||
.results-section {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.results-section--visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.results-section__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--spacing-m);
|
||||
}
|
||||
|
||||
.results-section__title {
|
||||
font-size: var(--font-size-l);
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.results-section__regenerate {
|
||||
background: none;
|
||||
border: 1px solid var(--color-border-light);
|
||||
color: var(--color-text-secondary);
|
||||
padding: var(--spacing-xs) var(--spacing-m);
|
||||
border-radius: var(--radius-m);
|
||||
font-size: var(--font-size-s);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-normal);
|
||||
}
|
||||
|
||||
.results-section__regenerate:hover {
|
||||
border-color: var(--color-secondary);
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
|
||||
/* Image Grid */
|
||||
.image-grid {
|
||||
display: grid;
|
||||
gap: var(--spacing-m);
|
||||
margin-bottom: var(--spacing-l);
|
||||
}
|
||||
|
||||
/* Image Card */
|
||||
.image-card {
|
||||
background: var(--color-white);
|
||||
border: 2px solid var(--color-border-light);
|
||||
border-radius: var(--radius-l);
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-normal);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.image-card:hover {
|
||||
border-color: var(--color-secondary);
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.image-card--selected {
|
||||
border-color: var(--color-secondary);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.image-card__badge {
|
||||
position: absolute;
|
||||
top: var(--spacing-s);
|
||||
right: var(--spacing-s);
|
||||
background: linear-gradient(135deg, var(--color-secondary) 0%, var(--color-secondary-dark) 100%);
|
||||
color: var(--color-white);
|
||||
padding: var(--spacing-xs) var(--spacing-s);
|
||||
border-radius: var(--radius-s);
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
box-shadow: var(--shadow-md);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.image-card__checkbox {
|
||||
position: absolute;
|
||||
top: var(--spacing-s);
|
||||
left: var(--spacing-s);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.image-card__preview {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
background: linear-gradient(135deg, #E0E7FF 0%, #F0F7FF 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 48px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.image-card__info {
|
||||
padding: var(--spacing-m);
|
||||
}
|
||||
|
||||
.image-card__title {
|
||||
font-size: var(--font-size-m);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text-primary);
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.image-card__style {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-text-secondary);
|
||||
margin-bottom: var(--spacing-s);
|
||||
}
|
||||
|
||||
.image-card__actions {
|
||||
display: flex;
|
||||
gap: var(--spacing-xs);
|
||||
padding-top: var(--spacing-s);
|
||||
border-top: 1px solid var(--color-border-light);
|
||||
}
|
||||
|
||||
.image-card__action-btn {
|
||||
flex: 1;
|
||||
padding: var(--spacing-xs);
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-border-light);
|
||||
border-radius: var(--radius-s);
|
||||
font-size: var(--font-size-xs);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-normal);
|
||||
}
|
||||
|
||||
.image-card__action-btn:hover {
|
||||
border-color: var(--color-secondary);
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
|
||||
/* Next Button */
|
||||
.next-button {
|
||||
width: 100%;
|
||||
padding: var(--spacing-m);
|
||||
background: linear-gradient(135deg, var(--color-secondary) 0%, var(--color-secondary-dark) 100%);
|
||||
color: var(--color-white);
|
||||
border: none;
|
||||
border-radius: var(--radius-m);
|
||||
font-size: var(--font-size-m);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-normal);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.next-button:hover:not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.next-button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.next-button--loading {
|
||||
position: relative;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.next-button--loading:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -10px;
|
||||
margin-top: -10px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: var(--color-white);
|
||||
animation: spinner 0.6s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spinner {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Tablet */
|
||||
@media (min-width: 768px) {
|
||||
.image-header {
|
||||
padding: var(--spacing-2xl) var(--spacing-xl);
|
||||
}
|
||||
|
||||
.image-content {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
padding: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.image-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.prompt-section__styles {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Desktop */
|
||||
@media (min-width: 1024px) {
|
||||
.image-content {
|
||||
max-width: 960px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="image-container">
|
||||
<!-- Header -->
|
||||
<header class="image-header">
|
||||
<button class="image-header__back" onclick="history.back()">
|
||||
←
|
||||
</button>
|
||||
<h1 class="image-header__title">AI 이미지 생성</h1>
|
||||
<p class="image-header__subtitle">이벤트에 활용할 이미지를 AI로 생성해 보세요</p>
|
||||
</header>
|
||||
|
||||
<!-- Content -->
|
||||
<main class="image-content">
|
||||
<!-- Prompt Section -->
|
||||
<div class="prompt-section" id="promptSection">
|
||||
<label class="prompt-section__label">
|
||||
✨ 원하는 이미지 설명
|
||||
</label>
|
||||
<textarea
|
||||
class="prompt-section__textarea"
|
||||
id="promptInput"
|
||||
placeholder="예: 친구들이 함께 행복하게 웃고 있는 모습, 밝고 따뜻한 분위기"
|
||||
maxlength="500"
|
||||
></textarea>
|
||||
<p class="prompt-section__hint">
|
||||
생성하고 싶은 이미지를 자세히 설명해 주세요 (최대 500자)
|
||||
</p>
|
||||
|
||||
<label class="prompt-section__label">
|
||||
🎨 이미지 스타일
|
||||
</label>
|
||||
<div class="prompt-section__styles">
|
||||
<div class="style-chip style-chip--selected" data-style="modern">
|
||||
🌟 모던
|
||||
</div>
|
||||
<div class="style-chip" data-style="warm">
|
||||
☀️ 따뜻한
|
||||
</div>
|
||||
<div class="style-chip" data-style="vibrant">
|
||||
🎨 생동감
|
||||
</div>
|
||||
<div class="style-chip" data-style="minimal">
|
||||
✨ 미니멀
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="generate-button" id="generateButton">
|
||||
🎨 이미지 생성하기
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Loading State -->
|
||||
<div class="loading-state" id="loadingState">
|
||||
<div class="loading-animation">
|
||||
<div class="loading-animation__circle"></div>
|
||||
<div class="loading-animation__icon">🎨</div>
|
||||
</div>
|
||||
<p class="loading-state__text" id="loadingText">
|
||||
AI가 이미지를 생성하고 있어요...
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Results Section -->
|
||||
<div class="results-section" id="resultsSection">
|
||||
<div class="results-section__header">
|
||||
<h2 class="results-section__title">생성된 이미지</h2>
|
||||
<button class="results-section__regenerate" id="regenerateButton">
|
||||
🔄 다시 생성
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Image Grid -->
|
||||
<div class="image-grid" id="imageGrid">
|
||||
<!-- Image cards will be inserted here -->
|
||||
</div>
|
||||
|
||||
<!-- Next Button -->
|
||||
<button class="next-button" id="nextButton" disabled>
|
||||
다음 단계로
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/common.js"></script>
|
||||
<script>
|
||||
const { $, addClass, removeClass, storage } = window.CommonUtils;
|
||||
|
||||
// Elements
|
||||
const promptSection = $('#promptSection');
|
||||
const promptInput = $('#promptInput');
|
||||
const styleChips = document.querySelectorAll('.style-chip');
|
||||
const generateButton = $('#generateButton');
|
||||
const loadingState = $('#loadingState');
|
||||
const loadingText = $('#loadingText');
|
||||
const resultsSection = $('#resultsSection');
|
||||
const imageGrid = $('#imageGrid');
|
||||
const regenerateButton = $('#regenerateButton');
|
||||
const nextButton = $('#nextButton');
|
||||
|
||||
// State
|
||||
let selectedStyle = 'modern';
|
||||
let selectedImages = [];
|
||||
const generatedImages = [];
|
||||
|
||||
// Handle style selection
|
||||
styleChips.forEach(chip => {
|
||||
chip.addEventListener('click', () => {
|
||||
styleChips.forEach(c => removeClass(c, 'style-chip--selected'));
|
||||
addClass(chip, 'style-chip--selected');
|
||||
selectedStyle = chip.getAttribute('data-style');
|
||||
});
|
||||
});
|
||||
|
||||
// Generate images
|
||||
generateButton.addEventListener('click', () => {
|
||||
const prompt = promptInput.value.trim();
|
||||
|
||||
if (!prompt) {
|
||||
alert('이미지 설명을 입력해 주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
generateImages(prompt, selectedStyle);
|
||||
});
|
||||
|
||||
// Regenerate images
|
||||
regenerateButton.addEventListener('click', () => {
|
||||
const prompt = promptInput.value.trim();
|
||||
generateImages(prompt, selectedStyle);
|
||||
});
|
||||
|
||||
// Generate images function
|
||||
function generateImages(prompt, style) {
|
||||
// Hide prompt section and results
|
||||
promptSection.style.display = 'none';
|
||||
removeClass(resultsSection, 'results-section--visible');
|
||||
|
||||
// Show loading
|
||||
addClass(loadingState, 'loading-state--visible');
|
||||
|
||||
// Simulate AI generation
|
||||
const loadingTexts = [
|
||||
'AI가 이미지를 생성하고 있어요...',
|
||||
'이미지 구도를 설계하고 있어요...',
|
||||
'색상과 스타일을 조정하고 있어요...',
|
||||
'마지막 마무리 중이에요...'
|
||||
];
|
||||
|
||||
let step = 0;
|
||||
const loadingInterval = setInterval(() => {
|
||||
if (step < loadingTexts.length) {
|
||||
loadingText.textContent = loadingTexts[step];
|
||||
step++;
|
||||
}
|
||||
}, 1200);
|
||||
|
||||
// Show results after delay
|
||||
setTimeout(() => {
|
||||
clearInterval(loadingInterval);
|
||||
removeClass(loadingState, 'loading-state--visible');
|
||||
renderImages(prompt, style);
|
||||
addClass(resultsSection, 'results-section--visible');
|
||||
promptSection.style.display = 'block';
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Render generated images
|
||||
function renderImages(prompt, style) {
|
||||
const imageVariations = [
|
||||
{
|
||||
id: 1,
|
||||
title: '옵션 1',
|
||||
style: style === 'modern' ? '모던 스타일' :
|
||||
style === 'warm' ? '따뜻한 스타일' :
|
||||
style === 'vibrant' ? '생동감 있는 스타일' :
|
||||
'미니멀 스타일',
|
||||
icon: '🎉',
|
||||
recommended: true
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '옵션 2',
|
||||
style: style === 'modern' ? '모던 스타일' :
|
||||
style === 'warm' ? '따뜻한 스타일' :
|
||||
style === 'vibrant' ? '생동감 있는 스타일' :
|
||||
'미니멀 스타일',
|
||||
icon: '🎊',
|
||||
recommended: false
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '옵션 3',
|
||||
style: style === 'modern' ? '모던 스타일' :
|
||||
style === 'warm' ? '따뜻한 스타일' :
|
||||
style === 'vibrant' ? '생동감 있는 스타일' :
|
||||
'미니멀 스타일',
|
||||
icon: '🎈',
|
||||
recommended: false
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: '옵션 4',
|
||||
style: style === 'modern' ? '모던 스타일' :
|
||||
style === 'warm' ? '따뜻한 스타일' :
|
||||
style === 'vibrant' ? '생동감 있는 스타일' :
|
||||
'미니멀 스타일',
|
||||
icon: '🎁',
|
||||
recommended: false
|
||||
}
|
||||
];
|
||||
|
||||
imageGrid.innerHTML = imageVariations.map(image => `
|
||||
<div class="image-card" data-id="${image.id}">
|
||||
${image.recommended ? '<div class="image-card__badge">🤖 AI 추천</div>' : ''}
|
||||
<input type="checkbox" class="image-card__checkbox" value="${image.id}">
|
||||
<div class="image-card__preview">${image.icon}</div>
|
||||
<div class="image-card__info">
|
||||
<h3 class="image-card__title">${image.title}</h3>
|
||||
<p class="image-card__style">${image.style}</p>
|
||||
<div class="image-card__actions">
|
||||
<button class="image-card__action-btn" onclick="downloadImage(${image.id})">
|
||||
💾 저장
|
||||
</button>
|
||||
<button class="image-card__action-btn" onclick="editImage(${image.id})">
|
||||
✏️ 수정
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
// Add event listeners
|
||||
const cards = document.querySelectorAll('.image-card');
|
||||
cards.forEach(card => {
|
||||
const checkbox = card.querySelector('.image-card__checkbox');
|
||||
|
||||
card.addEventListener('click', (e) => {
|
||||
if (e.target.closest('.image-card__action-btn')) return;
|
||||
|
||||
checkbox.checked = !checkbox.checked;
|
||||
toggleImageSelection(card, checkbox.checked);
|
||||
});
|
||||
|
||||
checkbox.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
toggleImageSelection(card, checkbox.checked);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Toggle image selection
|
||||
function toggleImageSelection(card, isSelected) {
|
||||
const id = parseInt(card.getAttribute('data-id'));
|
||||
|
||||
if (isSelected) {
|
||||
addClass(card, 'image-card--selected');
|
||||
if (!selectedImages.includes(id)) {
|
||||
selectedImages.push(id);
|
||||
}
|
||||
} else {
|
||||
removeClass(card, 'image-card--selected');
|
||||
selectedImages = selectedImages.filter(imageId => imageId !== id);
|
||||
}
|
||||
|
||||
nextButton.disabled = selectedImages.length === 0;
|
||||
}
|
||||
|
||||
// Download image
|
||||
window.downloadImage = function(id) {
|
||||
alert(`이미지 ${id}를 다운로드했습니다.`);
|
||||
};
|
||||
|
||||
// Edit image
|
||||
window.editImage = function(id) {
|
||||
alert(`이미지 ${id} 편집 기능은 다음 버전에서 제공됩니다.`);
|
||||
};
|
||||
|
||||
// Handle next button
|
||||
nextButton.addEventListener('click', () => {
|
||||
if (selectedImages.length === 0) return;
|
||||
|
||||
// Save selected images
|
||||
const eventContent = storage.get('eventContent') || {};
|
||||
eventContent.generatedImages = {
|
||||
prompt: promptInput.value.trim(),
|
||||
style: selectedStyle,
|
||||
selectedImages: selectedImages,
|
||||
generatedAt: new Date().toISOString()
|
||||
};
|
||||
storage.set('eventContent', eventContent);
|
||||
|
||||
// Show loading
|
||||
addClass(nextButton, 'next-button--loading');
|
||||
nextButton.disabled = true;
|
||||
|
||||
// Navigate to next screen
|
||||
setTimeout(() => {
|
||||
window.location.href = '11-SNS콘텐츠생성.html';
|
||||
}, 800);
|
||||
});
|
||||
|
||||
// Auto-fill prompt from event draft
|
||||
window.addEventListener('load', () => {
|
||||
const eventDraft = storage.get('eventDraft') || {};
|
||||
if (eventDraft.promotionalCopy && !promptInput.value) {
|
||||
promptInput.value = `${eventDraft.promotionalCopy.headline} 이미지`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user