ssboost commited on
Commit
81be558
ยท
verified ยท
1 Parent(s): 6dab0aa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1072 -1
app.py CHANGED
@@ -1,2 +1,1073 @@
 
1
  import os
2
- exec(os.environ.get('APP'))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
  import os
3
+ import tempfile
4
+ from datetime import datetime
5
+ from PIL import Image
6
+
7
+ # ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ API ์—”๋“œํฌ์ธํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ (์ฝ”๋“œ์—์„œ๋Š” ์ ˆ๋Œ€ ๋…ธ์ถœ ์•ˆ๋จ)
8
+ API_ENDPOINT = os.getenv('API_ENDPOINT')
9
+
10
+ if not API_ENDPOINT:
11
+ print("โŒ API_ENDPOINT ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
12
+ exit(1)
13
+
14
+ # ๋กœ๊ทธ ์–ต์ œ๋œ ํด๋ผ์ด์–ธํŠธ ์ž„ํฌํŠธ
15
+ try:
16
+ from gradio_client import Client, handle_file
17
+ print("โœ… Gradio Client ์ž„ํฌํŠธ ์„ฑ๊ณต")
18
+ except ImportError as e:
19
+ print(f"โŒ Gradio Client ์ž„ํฌํŠธ ์˜ค๋ฅ˜: {e}")
20
+ exit(1)
21
+
22
+ # ์ปค์Šคํ…€ CSS ์Šคํƒ€์ผ (๊ธฐ์กด๊ณผ ๋™์ผ)
23
+ custom_css = """
24
+ /* ============================================
25
+ ๋‹คํฌ๋ชจ๋“œ ์ž๋™ ๋ณ€๊ฒฝ ํ…œํ”Œ๋ฆฟ CSS
26
+ ============================================ */
27
+
28
+ /* 1. CSS ๋ณ€์ˆ˜ ์ •์˜ (๋ผ์ดํŠธ๋ชจ๋“œ - ๊ธฐ๋ณธ๊ฐ’) */
29
+ :root {
30
+ /* ๋ฉ”์ธ ์ปฌ๋Ÿฌ */
31
+ --primary-color: #FB7F0D;
32
+ --secondary-color: #ff9a8b;
33
+ --accent-color: #FF6B6B;
34
+
35
+ /* ๋ฐฐ๊ฒฝ ์ปฌ๋Ÿฌ */
36
+ --background-color: #FFFFFF;
37
+ --card-bg: #ffffff;
38
+ --input-bg: #ffffff;
39
+
40
+ /* ํ…์ŠคํŠธ ์ปฌ๋Ÿฌ */
41
+ --text-color: #334155;
42
+ --text-secondary: #64748b;
43
+
44
+ /* ๋ณด๋” ๋ฐ ๊ตฌ๋ถ„์„  */
45
+ --border-color: #dddddd;
46
+ --border-light: #e5e5e5;
47
+
48
+ /* ํ…Œ์ด๋ธ” ์ปฌ๋Ÿฌ */
49
+ --table-even-bg: #f3f3f3;
50
+ --table-hover-bg: #f0f0f0;
51
+
52
+ /* ๊ทธ๋ฆผ์ž */
53
+ --shadow: 0 8px 30px rgba(251, 127, 13, 0.08);
54
+ --shadow-light: 0 2px 4px rgba(0, 0, 0, 0.1);
55
+
56
+ /* ๊ธฐํƒ€ */
57
+ --border-radius: 18px;
58
+ }
59
+
60
+ /* 2. ๋‹คํฌ๋ชจ๋“œ ์ƒ‰์ƒ ๋ณ€์ˆ˜ (์ž๋™ ๊ฐ์ง€) */
61
+ @media (prefers-color-scheme: dark) {
62
+ :root {
63
+ /* ๋ฐฐ๊ฒฝ ์ปฌ๋Ÿฌ */
64
+ --background-color: #1a1a1a;
65
+ --card-bg: #2d2d2d;
66
+ --input-bg: #2d2d2d;
67
+
68
+ /* ํ…์ŠคํŠธ ์ปฌ๋Ÿฌ */
69
+ --text-color: #e5e5e5;
70
+ --text-secondary: #a1a1aa;
71
+
72
+ /* ๋ณด๋” ๋ฐ ๊ตฌ๋ถ„์„  */
73
+ --border-color: #404040;
74
+ --border-light: #525252;
75
+
76
+ /* ํ…Œ์ด๋ธ” ์ปฌ๋Ÿฌ */
77
+ --table-even-bg: #333333;
78
+ --table-hover-bg: #404040;
79
+
80
+ /* ๊ทธ๋ฆผ์ž */
81
+ --shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
82
+ --shadow-light: 0 2px 4px rgba(0, 0, 0, 0.2);
83
+ }
84
+ }
85
+
86
+ /* 3. ์ˆ˜๋™ ๋‹คํฌ๋ชจ๋“œ ํด๋ž˜์Šค (Gradio ํ† ๊ธ€์šฉ) */
87
+ [data-theme="dark"],
88
+ .dark,
89
+ .gr-theme-dark {
90
+ /* ๋ฐฐ๊ฒฝ ์ปฌ๋Ÿฌ */
91
+ --background-color: #1a1a1a;
92
+ --card-bg: #2d2d2d;
93
+ --input-bg: #2d2d2d;
94
+
95
+ /* ํ…์ŠคํŠธ ์ปฌ๋Ÿฌ */
96
+ --text-color: #e5e5e5;
97
+ --text-secondary: #a1a1aa;
98
+
99
+ /* ๋ณด๋” ๋ฐ ๊ตฌ๋ถ„์„  */
100
+ --border-color: #404040;
101
+ --border-light: #525252;
102
+
103
+ /* ํ…Œ์ด๋ธ” ์ปฌ๋Ÿฌ */
104
+ --table-even-bg: #333333;
105
+ --table-hover-bg: #404040;
106
+
107
+ /* ๊ทธ๋ฆผ์ž */
108
+ --shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
109
+ --shadow-light: 0 2px 4px rgba(0, 0, 0, 0.2);
110
+ }
111
+
112
+ /* 4. ๊ธฐ๋ณธ ์š”์†Œ ๋‹คํฌ๋ชจ๋“œ ์ ์šฉ */
113
+ body {
114
+ font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
115
+ background-color: var(--background-color) !important;
116
+ color: var(--text-color) !important;
117
+ line-height: 1.6;
118
+ margin: 0;
119
+ padding: 0;
120
+ font-size: 16px;
121
+ transition: background-color 0.3s ease, color 0.3s ease;
122
+ }
123
+
124
+ /* 5. Gradio ์ปจํ…Œ์ด๋„ˆ ๊ฐ•์ œ ์ ์šฉ */
125
+ .gradio-container,
126
+ .gradio-container *,
127
+ .gr-app,
128
+ .gr-app *,
129
+ .gr-interface {
130
+ background-color: var(--background-color) !important;
131
+ color: var(--text-color) !important;
132
+ }
133
+
134
+ /* ํ‘ธํ„ฐ ์ˆจ๊น€ ์„ค์ • ์ถ”๊ฐ€ */
135
+ footer {
136
+ visibility: hidden;
137
+ }
138
+
139
+ .gradio-container {
140
+ width: 100%;
141
+ margin: 0 auto;
142
+ padding: 20px;
143
+ background-color: var(--background-color);
144
+ }
145
+
146
+ /* โ”€โ”€ ๊ทธ๋ฃน ๋ž˜ํผ ๋ฐฐ๊ฒฝ ์™„์ „ ์ œ๊ฑฐ โ”€โ”€ */
147
+ .custom-section-group,
148
+ .gr-block.gr-group {
149
+ background-color: var(--background-color) !important;
150
+ box-shadow: none !important;
151
+ }
152
+ .custom-section-group::before,
153
+ .custom-section-group::after,
154
+ .gr-block.gr-group::before,
155
+ .gr-block.gr-group::after {
156
+ display: none !important;
157
+ content: none !important;
158
+ }
159
+
160
+ /* ๊ทธ๋ฃน ์ปจํ…Œ์ด๋„ˆ ๋ฐฐ๊ฒฝ์„ ์•„์ด๋ณด๋ฆฌ๋กœ, ๊ทธ๋ฆผ์ž ์ œ๊ฑฐ */
161
+ .custom-section-group {
162
+ background-color: var(--background-color) !important;
163
+ box-shadow: none !important;
164
+ }
165
+
166
+ /* 6. ์นด๋“œ ๋ฐ ํŒจ๋„ ์Šคํƒ€์ผ */
167
+ .custom-frame,
168
+ .gr-form,
169
+ .gr-box,
170
+ .gr-panel,
171
+ [class*="frame"],
172
+ [class*="card"],
173
+ [class*="panel"] {
174
+ background-color: var(--card-bg) !important;
175
+ border: 1px solid var(--border-color) !important;
176
+ border-radius: var(--border-radius);
177
+ padding: 20px;
178
+ margin: 10px 0;
179
+ box-shadow: var(--shadow) !important;
180
+ color: var(--text-color) !important;
181
+ }
182
+
183
+ /* ์„น์…˜ ๊ทธ๋ฃน ์Šคํƒ€์ผ - ํšŒ์ƒ‰ ๋ฐฐ๊ฒฝ ์™„์ „ ์ œ๊ฑฐ */
184
+ .custom-section-group {
185
+ margin-top: 20px;
186
+ padding: 0;
187
+ border: none;
188
+ border-radius: 0;
189
+ background-color: var(--background-color);
190
+ box-shadow: none !important;
191
+ }
192
+
193
+ /* ๋ฒ„ํŠผ ์Šคํƒ€์ผ - ๊ธ€์ž ํฌ๊ธฐ 18px */
194
+ .custom-button {
195
+ border-radius: 30px !important;
196
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)) !important;
197
+ color: white !important;
198
+ font-size: 18px !important;
199
+ padding: 10px 20px !important;
200
+ border: none;
201
+ box-shadow: 0 4px 8px rgba(251, 127, 13, 0.25);
202
+ transition: transform 0.3s ease;
203
+ }
204
+ .custom-button:hover {
205
+ transform: translateY(-2px);
206
+ box-shadow: 0 6px 12px rgba(251, 127, 13, 0.3);
207
+ }
208
+
209
+ /* ์ œ๋ชฉ ์Šคํƒ€์ผ (๋ชจ๋“  ํ•ญ๋ชฉ๋ช…์ด ๋™์ผํ•˜๊ฒŒ custom-title ํด๋ž˜์Šค๋กœ) */
210
+ .custom-title {
211
+ font-size: 28px;
212
+ font-weight: bold;
213
+ margin-bottom: 10px;
214
+ color: var(--text-color);
215
+ border-bottom: 2px solid var(--primary-color);
216
+ padding-bottom: 5px;
217
+ }
218
+
219
+ /* ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ - ํฌ๊ธฐ ๊ณ ์ • */
220
+ .image-container {
221
+ border-radius: var(--border-radius);
222
+ overflow: hidden;
223
+ border: 2px dashed var(--border-color);
224
+ transition: all 0.3s ease;
225
+ background-color: var(--card-bg);
226
+ }
227
+
228
+ /* ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์˜์—ญ ๊ฐœ์„  */
229
+ .gradio-container .gr-image {
230
+ border: 2px dashed var(--border-color) !important;
231
+ border-radius: var(--border-radius) !important;
232
+ background-color: var(--card-bg) !important;
233
+ transition: all 0.3s ease !important;
234
+ }
235
+
236
+ .gradio-container .gr-image:hover {
237
+ border-color: var(--primary-color) !important;
238
+ box-shadow: 0 4px 12px rgba(251, 127, 13, 0.15) !important;
239
+ }
240
+
241
+ /* ์—…๋กœ๋“œ ์˜์—ญ ๋‚ด๋ถ€ ํ…์ŠคํŠธ */
242
+ .gradio-container .gr-image .upload-container,
243
+ .gradio-container .gr-image [data-testid="upload-container"] {
244
+ background-color: var(--card-bg) !important;
245
+ color: var(--text-color) !important;
246
+ border: none !important;
247
+ }
248
+
249
+ /* ์—…๋กœ๋“œ ์˜์—ญ ๋“œ๋ž˜๊ทธ ์•ˆ๋‚ด ํ…์ŠคํŠธ */
250
+ .gradio-container .gr-image .upload-container p,
251
+ .gradio-container .gr-image [data-testid="upload-container"] p {
252
+ color: var(--text-color) !important;
253
+ font-size: 14px !important;
254
+ }
255
+
256
+ /* ์—…๋กœ๋“œ ๋ฒ„ํŠผ ์Šคํƒ€์ผ ๊ฐœ์„  */
257
+ .gradio-container .gr-image .upload-container button,
258
+ .gradio-container .gr-image [data-testid="upload-container"] button {
259
+ background-color: var(--primary-color) !important;
260
+ color: white !important;
261
+ border: none !important;
262
+ padding: 8px 16px !important;
263
+ border-radius: 8px !important;
264
+ font-size: 14px !important;
265
+ cursor: pointer !important;
266
+ transition: all 0.3s ease !important;
267
+ }
268
+
269
+ .gradio-container .gr-image .upload-container button:hover,
270
+ .gradio-container .gr-image [data-testid="upload-container"] button:hover {
271
+ background-color: var(--secondary-color) !important;
272
+ transform: translateY(-1px) !important;
273
+ }
274
+
275
+ /* ์—…๋กœ๋“œ ์˜์—ญ ์•„์ด์ฝ˜ */
276
+ .gradio-container .gr-image .upload-container svg,
277
+ .gradio-container .gr-image [data-testid="upload-container"] svg {
278
+ color: var(--primary-color) !important;
279
+ width: 32px !important;
280
+ height: 32px !important;
281
+ }
282
+
283
+ /* ์ด๋ฏธ์ง€๊ฐ€ ์—…๋กœ๋“œ๋œ ํ›„ ํ‘œ์‹œ ์˜์—ญ */
284
+ .gradio-container .gr-image img {
285
+ background-color: var(--card-bg) !important;
286
+ border-radius: var(--border-radius) !important;
287
+ }
288
+
289
+ /* ์ด๋ฏธ์ง€ ์ œ๊ฑฐ ๋ฒ„ํŠผ */
290
+ .gradio-container .gr-image .image-container button,
291
+ .gradio-container .gr-image [data-testid="image"] button {
292
+ background-color: rgba(255, 255, 255, 0.9) !important;
293
+ color: #333 !important;
294
+ border: none !important;
295
+ border-radius: 50% !important;
296
+ width: 28px !important;
297
+ height: 28px !important;
298
+ display: flex !important;
299
+ align-items: center !important;
300
+ justify-content: center !important;
301
+ cursor: pointer !important;
302
+ transition: all 0.3s ease !important;
303
+ }
304
+
305
+ .gradio-container .gr-image .image-container button:hover,
306
+ .gradio-container .gr-image [data-testid="image"] button:hover {
307
+ background-color: rgba(255, 255, 255, 1) !important;
308
+ transform: scale(1.1) !important;
309
+ }
310
+
311
+ /* ์—…๋กœ๋“œ ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ (600x600) */
312
+ .upload-image-container {
313
+ width: 600px !important;
314
+ height: 600px !important;
315
+ min-width: 600px !important;
316
+ min-height: 600px !important;
317
+ max-width: 600px !important;
318
+ max-height: 600px !important;
319
+ }
320
+
321
+ /* ์ถœ๋ ฅ ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ (700x600) */
322
+ .output-image-container {
323
+ width: 700px !important;
324
+ height: 600px !important;
325
+ min-width: 700px !important;
326
+ min-height: 600px !important;
327
+ max-width: 700px !important;
328
+ max-height: 600px !important;
329
+ }
330
+
331
+ .image-container:hover {
332
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
333
+ }
334
+
335
+ .image-container img {
336
+ width: 100% !important;
337
+ height: 100% !important;
338
+ object-fit: contain !important;
339
+ }
340
+
341
+ /* Gradio ์—…๋กœ๋“œ ์ด๋ฏธ์ง€ ์ปดํฌ๋„ŒํŠธ ํฌ๊ธฐ ๊ณ ์ • (600x600) */
342
+ .gradio-container .gr-image.upload-image {
343
+ width: 600px !important;
344
+ height: 600px !important;
345
+ min-width: 600px !important;
346
+ min-height: 600px !important;
347
+ max-width: 600px !important;
348
+ max-height: 600px !important;
349
+ }
350
+
351
+ /* Gradio ์ถœ๋ ฅ ์ด๋ฏธ์ง€ ์ปดํฌ๋„ŒํŠธ ํฌ๊ธฐ ๊ณ ์ • (700x600) */
352
+ .gradio-container .gr-image.output-image {
353
+ width: 700px !important;
354
+ height: 600px !important;
355
+ min-width: 700px !important;
356
+ min-height: 600px !important;
357
+ max-width: 700px !important;
358
+ max-height: 600px !important;
359
+ }
360
+
361
+ /* ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์˜์—ญ ํฌ๊ธฐ ๊ณ ์ • */
362
+ .gradio-container .gr-image.upload-image > div {
363
+ width: 600px !important;
364
+ height: 600px !important;
365
+ min-width: 600px !important;
366
+ min-height: 600px !important;
367
+ max-width: 600px !important;
368
+ max-height: 600px !important;
369
+ }
370
+
371
+ /* ์ด๋ฏธ์ง€ ์ถœ๋ ฅ ์˜์—ญ ํฌ๊ธฐ ๊ณ ์ • */
372
+ .gradio-container .gr-image.output-image > div {
373
+ width: 700px !important;
374
+ height: 600px !important;
375
+ min-width: 700px !important;
376
+ min-height: 600px !important;
377
+ max-width: 700px !important;
378
+ max-height: 600px !important;
379
+ }
380
+
381
+ /* ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ๋“œ๋ž˜๊ทธ ์˜์—ญ ํฌ๊ธฐ ๊ณ ์ • */
382
+ .gradio-container .gr-image.upload-image .image-container,
383
+ .gradio-container .gr-image.upload-image [data-testid="image"],
384
+ .gradio-container .gr-image.upload-image .upload-container {
385
+ width: 600px !important;
386
+ height: 600px !important;
387
+ min-width: 600px !important;
388
+ min-height: 600px !important;
389
+ max-width: 600px !important;
390
+ max-height: 600px !important;
391
+ }
392
+
393
+ /* ์ด๋ฏธ์ง€ ์ถœ๋ ฅ ๋“œ๋ž˜๊ทธ ์˜์—ญ ํฌ๊ธฐ ๊ณ ์ • */
394
+ .gradio-container .gr-image.output-image .image-container,
395
+ .gradio-container .gr-image.output-image [data-testid="image"],
396
+ .gradio-container .gr-image.output-image .upload-container {
397
+ width: 700px !important;
398
+ height: 600px !important;
399
+ min-width: 700px !important;
400
+ min-height: 600px !important;
401
+ max-width: 700px !important;
402
+ max-height: 600px !important;
403
+ }
404
+
405
+ /* 7. ์ž…๋ ฅ ํ•„๋“œ ์Šคํƒ€์ผ */
406
+ .gr-input, .gr-text-input, .gr-sample-inputs,
407
+ input[type="text"],
408
+ input[type="number"],
409
+ input[type="email"],
410
+ input[type="password"],
411
+ textarea,
412
+ select,
413
+ .gr-textarea,
414
+ .gr-dropdown {
415
+ border-radius: var(--border-radius) !important;
416
+ border: 1px solid var(--border-color) !important;
417
+ padding: 14px !important;
418
+ font-size: 15px !important;
419
+ background-color: var(--input-bg) !important;
420
+ color: var(--text-color) !important;
421
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05) !important;
422
+ transition: all 0.3s ease !important;
423
+ }
424
+
425
+ .gr-input:focus, .gr-text-input:focus,
426
+ input[type="text"]:focus,
427
+ input[type="number"]:focus,
428
+ input[type="email"]:focus,
429
+ input[type="password"]:focus,
430
+ textarea:focus,
431
+ select:focus,
432
+ .gr-textarea:focus,
433
+ .gr-dropdown:focus {
434
+ border-color: var(--primary-color) !important;
435
+ outline: none !important;
436
+ box-shadow: 0 0 0 2px rgba(251, 127, 13, 0.2) !important;
437
+ }
438
+
439
+ /* 8. ๋ผ๋ฒจ ๋ฐ ํ…์ŠคํŠธ ์š”์†Œ */
440
+ .gradio-container label,
441
+ label,
442
+ .gr-label,
443
+ .gr-checkbox label,
444
+ .gr-radio label,
445
+ p, span, div {
446
+ color: var(--text-color) !important;
447
+ font-size: 16px !important;
448
+ font-weight: 600 !important;
449
+ margin-bottom: 8px !important;
450
+ }
451
+
452
+ /* ๋“œ๋กญ๋‹ค์šด ๋ฐ ๋ผ๋””์˜ค ๋ฒ„ํŠผ ํฐํŠธ ํฌ๊ธฐ */
453
+ .gr-radio label, .gr-dropdown label, .gr-checkbox label {
454
+ font-size: 15px !important;
455
+ }
456
+
457
+ /* ๋ผ๋””์˜ค ๋ฒ„ํŠผ ์„ ํƒ์ง€ ๋ณผ๋“œ ์ฒ˜๋ฆฌ ์ œ๊ฑฐ */
458
+ .gr-radio .gr-radio-option label,
459
+ .gr-radio input[type="radio"] + label,
460
+ .gr-radio .gr-form label {
461
+ font-weight: normal !important;
462
+ font-size: 15px !important;
463
+ }
464
+
465
+ /* ๋ผ๋””์˜ค ๋ฒ„ํŠผ ๊ทธ๋ฃน ๋‚ด ๋ชจ๋“  ๋ผ๋ฒจ ์ผ๋ฐ˜ ํฐํŠธ๋กœ ์„ค์ • */
466
+ .gr-radio fieldset label {
467
+ font-weight: normal !important;
468
+ }
469
+
470
+ /* ๋งˆํฌ๋‹ค์šด ํ…์ŠคํŠธ ํฌ๊ธฐ ์ฆ๊ฐ€ */
471
+ .gradio-container .gr-markdown {
472
+ font-size: 15px !important;
473
+ line-height: 1.6 !important;
474
+ color: var(--text-color) !important;
475
+ }
476
+
477
+ /* ํ…์ŠคํŠธ๋ฐ•์Šค ๋‚ด์šฉ ํฐํŠธ ํฌ๊ธฐ */
478
+ .gr-textbox textarea, .gr-textbox input {
479
+ font-size: 15px !important;
480
+ background-color: var(--input-bg) !important;
481
+ color: var(--text-color) !important;
482
+ }
483
+
484
+ /* ์•„์ฝ”๋””์–ธ ์ œ๋ชฉ ํฐํŠธ ํฌ๊ธฐ */
485
+ .gr-accordion summary {
486
+ font-size: 17px !important;
487
+ font-weight: 600 !important;
488
+ background-color: var(--card-bg) !important;
489
+ color: var(--text-color) !important;
490
+ }
491
+
492
+ /* ๋ฉ”์ธ ์ปจํ…์ธ  ์Šคํฌ๋กค๋ฐ” */
493
+ ::-webkit-scrollbar {
494
+ width: 8px;
495
+ height: 8px;
496
+ }
497
+
498
+ ::-webkit-scrollbar-track {
499
+ background: var(--card-bg);
500
+ border-radius: 10px;
501
+ }
502
+
503
+ ::-webkit-scrollbar-thumb {
504
+ background: var(--primary-color);
505
+ border-radius: 10px;
506
+ }
507
+
508
+ ::-webkit-scrollbar-thumb:hover {
509
+ background: var(--secondary-color);
510
+ }
511
+
512
+ /* ์• ๋‹ˆ๋ฉ”์ด์…˜ ์Šคํƒ€์ผ */
513
+ @keyframes fadeIn {
514
+ from { opacity: 0; transform: translateY(10px); }
515
+ to { opacity: 1; transform: translateY(0); }
516
+ }
517
+
518
+ .fade-in {
519
+ animation: fadeIn 0.5s ease-out;
520
+ }
521
+
522
+ /* ๋ฐ˜์‘ํ˜• */
523
+ @media (max-width: 768px) {
524
+ .button-grid {
525
+ grid-template-columns: repeat(2, 1fr);
526
+ }
527
+ }
528
+
529
+ /* ์„น์…˜ ์ œ๋ชฉ ์Šคํƒ€์ผ */
530
+ .section-title {
531
+ display: flex;
532
+ align-items: center;
533
+ font-size: 24px;
534
+ font-weight: 700;
535
+ color: var(--text-color);
536
+ margin-bottom: 15px;
537
+ padding-bottom: 8px;
538
+ border-bottom: 2px solid var(--primary-color);
539
+ font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
540
+ }
541
+
542
+ .section-title img {
543
+ margin-right: 12px;
544
+ width: 28px;
545
+ height: 28px;
546
+ /* ๋‹คํฌ๋ชจ๋“œ์—์„œ ์•„์ด์ฝ˜ ํ•„ํ„ฐ ์ ์šฉ */
547
+ filter: brightness(0) saturate(100%) invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%);
548
+ }
549
+
550
+ /* ๋ผ์ดํŠธ๋ชจ๋“œ์—์„œ๋Š” ์›๋ž˜ ์•„์ด์ฝ˜ ์ƒ‰์ƒ ์œ ์ง€ */
551
+ @media (prefers-color-scheme: light) {
552
+ .section-title img {
553
+ filter: none;
554
+ }
555
+ }
556
+
557
+ /* ์ˆ˜๋™ ๋‹คํฌ๋ชจ๋“œ ํด๋ž˜์Šค์—์„œ๋„ ์•„์ด์ฝ˜ ์ƒ‰์ƒ ์ ์šฉ */
558
+ [data-theme="dark"] .section-title img,
559
+ .dark .section-title img,
560
+ .gr-theme-dark .section-title img {
561
+ filter: brightness(0) saturate(100%) invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%);
562
+ }
563
+
564
+ /* 10. ์•„์ฝ”๋””์–ธ ๋ฐ ๋“œ๋กญ๋‹ค์šด - ์ˆ˜๋™์„ค์ • ์˜์—ญ ํšŒ์ƒ‰ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ */
565
+ details,
566
+ .gr-accordion,
567
+ .gr-accordion details {
568
+ background-color: var(--background-color) !important;
569
+ border: 1px solid var(--border-color) !important;
570
+ color: var(--text-color) !important;
571
+ border-radius: var(--border-radius) !important;
572
+ margin: 10px 0 !important;
573
+ }
574
+
575
+ details summary,
576
+ .gr-accordion summary,
577
+ .gr-accordion details summary {
578
+ background-color: var(--card-bg) !important;
579
+ color: var(--text-color) !important;
580
+ padding: 12px 16px !important;
581
+ border-radius: var(--border-radius) !important;
582
+ cursor: pointer !important;
583
+ border: none !important;
584
+ font-weight: 600 !important;
585
+ transition: all 0.3s ease !important;
586
+ }
587
+
588
+ details summary:hover,
589
+ .gr-accordion summary:hover,
590
+ .gr-accordion details summary:hover {
591
+ background-color: var(--table-hover-bg) !important;
592
+ }
593
+
594
+ /* ์•„์ฝ”๋””์–ธ ๋‚ด๋ถ€ ์ฝ˜ํ…์ธ  */
595
+ details[open],
596
+ .gr-accordion[open],
597
+ .gr-accordion details[open] {
598
+ background-color: var(--background-color) !important;
599
+ }
600
+
601
+ details[open] > *:not(summary),
602
+ .gr-accordion[open] > *:not(summary),
603
+ .gr-accordion details[open] > *:not(summary) {
604
+ background-color: var(--background-color) !important;
605
+ color: var(--text-color) !important;
606
+ padding: 16px !important;
607
+ border-top: 1px solid var(--border-color) !important;
608
+ }
609
+
610
+ /* ๊ทธ๋ฃน ๋‚ด๋ถ€ ์Šคํƒ€์ผ - ์ˆ˜๋™์„ค์ • ๋‚ด๋ถ€ ๊ทธ๋ฃน๋“ค */
611
+ .gr-group,
612
+ details .gr-group,
613
+ .gr-accordion .gr-group {
614
+ background-color: var(--background-color) !important;
615
+ border: none !important;
616
+ padding: 12px 0 !important;
617
+ margin: 8px 0 !important;
618
+ border-radius: var(--border-radius) !important;
619
+ }
620
+
621
+ /* ๊ทธ๋ฃน ๋‚ด๋ถ€ ์ œ๋ชฉ */
622
+ .gr-group .gr-markdown h3,
623
+ .gr-group h3 {
624
+ color: var(--text-color) !important;
625
+ font-size: 16px !important;
626
+ font-weight: 600 !important;
627
+ margin-bottom: 12px !important;
628
+ padding-bottom: 6px !important;
629
+ border-bottom: 1px solid var(--border-color) !important;
630
+ }
631
+
632
+ /* 11. ์ถ”๊ฐ€ Gradio ์ปดํฌ๋„ŒํŠธ๋“ค */
633
+ .gr-block,
634
+ .gr-group,
635
+ .gr-row,
636
+ .gr-column {
637
+ background-color: var(--background-color) !important;
638
+ color: var(--text-color) !important;
639
+ }
640
+
641
+ /* 12. ๋ฒ„ํŠผ์€ ๊ธฐ์กด ์Šคํƒ€์ผ ์œ ์ง€ (primary-color ์‚ฌ์šฉ) */
642
+ button:not([class*="custom"]):not([class*="primary"]):not([class*="secondary"]) {
643
+ background-color: var(--card-bg) !important;
644
+ color: var(--text-color) !important;
645
+ border-color: var(--border-color) !important;
646
+ }
647
+
648
+ /* 13. ์ฝ”๋“œ ๋ธ”๋ก ๋ฐ pre ํƒœ๊ทธ */
649
+ code,
650
+ pre,
651
+ .code-block {
652
+ background-color: var(--table-even-bg) !important;
653
+ color: var(--text-color) !important;
654
+ border-color: var(--border-color) !important;
655
+ }
656
+
657
+ /* 14. ์ „ํ™˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ */
658
+ * {
659
+ transition: background-color 0.3s ease,
660
+ color 0.3s ease,
661
+ border-color 0.3s ease !important;
662
+ }
663
+ """
664
+
665
+ # FontAwesome ์•„์ด์ฝ˜ ํฌํ•จ
666
+ fontawesome_link = """
667
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
668
+ """
669
+
670
+ def create_download_filename(keyword):
671
+ """ํ•œ๊ตญ์‹œ๊ฐ„ ๊ธฐ์ค€์œผ๋กœ ๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ๋ช… ์ƒ์„ฑ"""
672
+ from datetime import datetime, timezone, timedelta
673
+
674
+ # ํ•œ๊ตญ ์‹œ๊ฐ„๋Œ€ ์„ค์ • (UTC+9)
675
+ kst = timezone(timedelta(hours=9))
676
+ now = datetime.now(kst)
677
+
678
+ # ํŒŒ์ผ๋ช…์— ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๋ฌธ์ž ์ œ๊ฑฐ
679
+ import re
680
+ safe_keyword = re.sub(r'[<>:"/\\|?*]', '_', keyword) if keyword else "์ƒํ’ˆ"
681
+ safe_keyword = safe_keyword[:30] # ๊ธธ์ด ์ œํ•œ 30์ž๋กœ ๋ณ€๊ฒฝ
682
+
683
+ # YYMMDD_์‹œ๊ฐ„๋ถ„ ํ˜•์‹
684
+ time_str = now.strftime("%y%m%d_%H%M")
685
+ filename = f"{safe_keyword}_{time_str}.jpg"
686
+
687
+ return filename
688
+
689
+ def prepare_download_file(image, keyword):
690
+ """๋‹ค์šด๋กœ๋“œ์šฉ ์ž„์‹œ ํŒŒ์ผ ์ƒ์„ฑ (์˜ฌ๋ฐ”๋ฅธ ํŒŒ์ผ๋ช… ํฌํ•จ)"""
691
+ if image is None:
692
+ return None
693
+
694
+ try:
695
+ # ํ•œ๊ตญ์‹œ๊ฐ„ ๊ธฐ์ค€ ํŒŒ์ผ๋ช… ์ƒ์„ฑ
696
+ filename = create_download_filename(keyword)
697
+
698
+ # ์ž„์‹œ ๋””๋ ‰ํ† ๋ฆฌ์— ์›ํ•˜๋Š” ๏ฟฝ๏ฟฝ์ผ๋ช…์œผ๋กœ ์ €์žฅ
699
+ import tempfile
700
+ import os
701
+
702
+ # ์ž„์‹œ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ
703
+ temp_dir = tempfile.mkdtemp()
704
+ file_path = os.path.join(temp_dir, filename)
705
+
706
+ # PIL Image๋ฅผ RGB๋กœ ๋ณ€ํ™˜ ํ›„ ์ €์žฅ
707
+ if isinstance(image, Image.Image):
708
+ # RGBA๋ฅผ RGB๋กœ ๋ณ€ํ™˜
709
+ if image.mode == 'RGBA':
710
+ background = Image.new('RGB', image.size, (255, 255, 255))
711
+ background.paste(image, mask=image.split()[-1])
712
+ image_to_save = background
713
+ else:
714
+ image_to_save = image.convert('RGB')
715
+ else:
716
+ image_to_save = image
717
+
718
+ # JPG๋กœ ์ €์žฅ (์›ํ•˜๋Š” ํŒŒ์ผ๋ช…์œผ๋กœ)
719
+ image_to_save.save(file_path, 'JPEG', quality=95)
720
+
721
+ print(f"โœ… ๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ ์ค€๋น„: {filename}")
722
+ return file_path
723
+
724
+ except Exception as e:
725
+ print(f"โŒ ๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ ์ค€๋น„ ์‹คํŒจ: {e}")
726
+ return None
727
+
728
+ # ๐ŸŽฏ ํ•ต์‹ฌ: ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ํ•จ์ˆ˜ (๋กœ๊ทธ ์ตœ์†Œํ™”)
729
+ def get_client():
730
+ """ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ธฐ๋ฐ˜ ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ"""
731
+ try:
732
+ client = Client(API_ENDPOINT)
733
+ return client
734
+ except Exception as e:
735
+ print(f"โŒ ์—ฐ๊ฒฐ ์‹คํŒจ: {str(e)[:50]}...")
736
+ return None
737
+
738
+ def main():
739
+ with gr.Blocks(
740
+ css=custom_css,
741
+ theme=gr.themes.Default(
742
+ primary_hue="orange",
743
+ secondary_hue="orange",
744
+ font=[gr.themes.GoogleFont("Noto Sans KR"), "ui-sans-serif", "system-ui"]
745
+ ),
746
+ title="UHP ์ด๋ฏธ์ง€์ƒ์„ฑ๊ธฐ"
747
+ ) as app:
748
+
749
+ # FontAwesome ๋งํฌ ์ถ”๊ฐ€
750
+ gr.HTML(fontawesome_link)
751
+
752
+ # ์ƒํƒœ ๊ด€๋ฆฌ
753
+ copy_suggestions_state = gr.State({})
754
+ current_keyword_state = gr.State("")
755
+
756
+ with gr.Row():
757
+ # ์™ผ์ชฝ: 1๋‹จ๊ณ„ AI ์นดํ”ผ ์ƒ์„ฑ
758
+ with gr.Column(scale=1):
759
+ # AI ์นดํ”ผ ์ƒ์„ฑ ์„น์…˜
760
+ with gr.Column(elem_classes="custom-frame"):
761
+ gr.HTML('<div class="section-title"><img src="https://cdn-icons-png.flaticon.com/512/3097/3097412.png"> 1๋‹จ๊ณ„: AI ์นดํ”ผ ์ƒ์„ฑ</div>')
762
+
763
+ product_keyword = gr.Textbox(
764
+ label="์ƒํ’ˆ ํ‚ค์›Œ๋“œ",
765
+ placeholder="์˜ˆ: ๋ฐ”๋‚˜๋ฐ”์žŽ ์ถ”์ถœ๋ฌผ, ํ”„๋ฆฌ๋ฏธ์—„ ํ…€๋ธ”๋Ÿฌ, ์ฒœ์—ฐ ์Šคํ‚จ์ผ€์–ด"
766
+ )
767
+
768
+ copy_type_selection = gr.Radio(
769
+ choices=[
770
+ "์žฅ์ ์š”์•ฝํ˜•", "๋ฌธ์ œ์ œ์‹œํ˜•", "์‚ฌํšŒ์ ์ฆ๊ฑฐํ˜•", "๊ธด๊ธ‰์„ฑ์œ ๋„ํ˜•",
771
+ "๊ฐ€๊ฒฉ๊ฒฝ์Ÿ๋ ฅํ˜•", "๋งค์ธ๋ณ€ํ™”ํ˜•", "์ถฉ๋™๊ตฌ๋งค์œ ๋„ํ˜•", "๊ณตํฌ์†Œ๊ตฌํ˜•"
772
+ ],
773
+ label="๐Ÿ“‹ 1. ์นดํ”ผ ํƒ€์ž… ์„ ํƒ",
774
+ value="์žฅ์ ์š”์•ฝํ˜•",
775
+ visible=True
776
+ )
777
+
778
+ copy_type_description = gr.Markdown(
779
+ "**์žฅ์ ์š”์•ฝํ˜•**: ์ œํ’ˆ์˜ ์žฅ์ ์„ ํ•œ๋ˆˆ์— ๊ฐ•์กฐ - ํ•ต์‹ฌ ๊ธฐ๋Šฅ๊ณผ ํ˜œํƒ์„ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์š”์•ฝํ•˜์—ฌ ์ œ์‹œ",
780
+ visible=True
781
+ )
782
+
783
+ generate_copy_btn = gr.Button("๋ฉ”์ธ์นดํ”ผ ์ƒ์„ฑ", elem_classes="custom-button")
784
+
785
+ # ์นดํ”ผ ์ถœ๋ ฅ ์˜์—ญ
786
+ copy1_display = gr.Textbox(label="์ถ”์ฒœ1", interactive=False, show_label=True)
787
+ copy2_display = gr.Textbox(label="์ถ”์ฒœ2", interactive=False, show_label=True)
788
+ copy3_display = gr.Textbox(label="์ถ”์ฒœ3", interactive=False, show_label=True)
789
+ copy4_display = gr.Textbox(label="์ถ”์ฒœ4", interactive=False, show_label=True)
790
+ copy5_display = gr.Textbox(label="์ถ”์ฒœ5", interactive=False, show_label=True)
791
+
792
+ copy_selection = gr.Radio(
793
+ choices=["์ถ”์ฒœ 1", "์ถ”์ฒœ 2", "์ถ”์ฒœ 3", "์ถ”์ฒœ 4", "์ถ”์ฒœ 5"],
794
+ label="๐Ÿ“ 2. ์นดํ”ผ ์„ ํƒ",
795
+ value="์ถ”์ฒœ 1",
796
+ visible=True
797
+ )
798
+
799
+ with gr.Row():
800
+ main_text = gr.Textbox(
801
+ label="๋ฉ”์ธ์นดํ”ผ",
802
+ placeholder="์œ„์—์„œ ์นดํ”ผ๋ฅผ ์„ ํƒํ•˜๋ฉด ์ž๋™์œผ๋กœ ์ž…๋ ฅ๋ฉ๋‹ˆ๋‹ค",
803
+ interactive=True
804
+ )
805
+ sub_text = gr.Textbox(
806
+ label="์„œ๋ธŒ์นดํ”ผ",
807
+ placeholder="์œ„์—์„œ ์นดํ”ผ๋ฅผ ์„ ํƒํ•˜๋ฉด ์ž๋™์œผ๋กœ ์ž…๋ ฅ๋ฉ๋‹ˆ๋‹ค",
808
+ interactive=True
809
+ )
810
+
811
+ # ์˜ˆ์‹œ
812
+ gr.Examples(
813
+ examples=[
814
+ ["ํ†ต๊ตฝ์Šฌ๋ฆฌํผ"], ["๋ฐ”๋‚˜๋ฐ”์žŽ ์ถ”์ถœ๋ฌผ"], ["ํ”„๋ฆฌ๋ฏธ์—„ ํ…€๋ธ”๋Ÿฌ"],
815
+ ["์ฒœ์—ฐ ์Šคํ‚จ์ผ€์–ด"], ["์œ ๊ธฐ๋† ์›๋‘"], ["๋ฌด์„  ์ด์–ดํฐ"]
816
+ ],
817
+ inputs=[product_keyword]
818
+ )
819
+
820
+ # ์˜ค๋ฅธ์ชฝ: 2๋‹จ๊ณ„ ์ด๋ฏธ์ง€์ƒ์„ฑ
821
+ with gr.Column(scale=1):
822
+ # ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ๋ฐ ์„ค์ • ์„น์…˜
823
+ with gr.Column(elem_classes="custom-frame"):
824
+ gr.HTML('<div class="section-title"><img src="https://cdn-icons-png.flaticon.com/512/4297/4297825.png"> 2๋‹จ๊ณ„: ์ด๋ฏธ์ง€์ƒ์„ฑ</div>')
825
+
826
+ input_image = gr.Image(
827
+ type="filepath",
828
+ label="์ƒํ’ˆ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ",
829
+ elem_classes="image-container upload-image-container"
830
+ )
831
+
832
+ color_mode = gr.Radio(
833
+ choices=["์ถ”์ฒœ๋ฐฐ๊ฒฝ", "ํฐ์ƒ‰๋ฐฐ๊ฒฝ", "์ˆ˜๋™ ์„ค์ •๋ฐฐ๊ฒฝ"],
834
+ value="์ถ”์ฒœ๋ฐฐ๊ฒฝ",
835
+ label="๋ฐฐ๊ฒฝ์ƒ‰ ์„ค์ •"
836
+ )
837
+
838
+ generate_image_btn = gr.Button("์ด๋ฏธ์ง€์ƒ์„ฑ", elem_classes="custom-button")
839
+
840
+ # ์ˆ˜๋™์„ค์ • ์•„์ฝ”๋””์–ธ
841
+ with gr.Accordion("์ˆ˜๋™์„ค์ •", open=False):
842
+ with gr.Group():
843
+ gr.Markdown("### ํฐํŠธ ์„ ํƒ")
844
+ with gr.Row():
845
+ main_font_choice = gr.Dropdown(
846
+ choices=[
847
+ "ํ”„๋ฆฌํ…๋‹ค๋“œ-Bold", "์—์Šค์ฝ”์–ด๋“œ๋ฆผ-Bold", "๋…ธํ† ์‚ฐ์Šค-Bold",
848
+ "๋ฐฐ๋‹ฌ์˜๋ฏผ์กฑ ๋„ํ˜„์ฒด", "๋ฐฐ๋‹ฌ์˜๋ฏผ์กฑ ์ฃผ์•„์ฒด", "์—ฌ๊ธฐ์–ด๋•Œ ์ž˜๋‚œ์ฒด",
849
+ "์˜จ๊ธ€์žŽ ์ฝ˜์ฝ˜์ฒด", "์˜จ๊ธ€์žŽ ๋ฐ•๋‹คํ˜„์ฒด", "์ด์‚ฌ๋งŒ๋ฃจ-Bold",
850
+ "์นดํŽ˜24 ์•„๋„ค๋ชจ๋„ค-Bold", "์–ด๊ทธ๋กœ์ฒด-Bold", "SFํ•จ๋ฐ•๋ˆˆ", "ํŽ˜์ดํผ๋กœ์ง€-Bold"
851
+ ],
852
+ value="ํ”„๋ฆฌํ…๋‹ค๋“œ-Bold",
853
+ label="๋ฉ”์ธ ์นดํ”ผ ํฐํŠธ"
854
+ )
855
+ sub_font_choice = gr.Dropdown(
856
+ choices=[
857
+ "ํ”„๋ฆฌํ…๋‹ค๋“œ-Regular", "์—์Šค์ฝ”์–ด๋“œ๋ฆผ-Regular", "๋…ธํ† ์‚ฐ์Šค-Regular",
858
+ "๋ฐฐ๋‹ฌ์˜๋ฏผ์กฑ ๋„ํ˜„์ฒด", "๋ฐฐ๋‹ฌ์˜๋ฏผ์กฑ ์ฃผ์•„์ฒด", "์—ฌ๊ธฐ์–ด๋•Œ ์ž˜๋‚œ์ฒด",
859
+ "์˜จ๊ธ€์žŽ ์ฝ˜์ฝ˜์ฒด", "์˜จ๊ธ€์žŽ ๋ฐ•๋‹คํ˜„์ฒด", "์ด์‚ฌ๋งŒ๋ฃจ-Light",
860
+ "์นดํŽ˜24 ์•„๋„ค๋ชจ๋„ค-Regular", "์–ด๊ทธ๋กœ์ฒด-Light", "SFํ•จ๋ฐ•๋ˆˆ", "ํŽ˜์ดํผ๋กœ์ง€-Regular"
861
+ ],
862
+ value="ํ”„๋ฆฌํ…๋‹ค๋“œ-Regular",
863
+ label="์„œ๋ธŒ ์นดํ”ผ ํฐํŠธ"
864
+ )
865
+
866
+ with gr.Group():
867
+ gr.Markdown("### ์ˆ˜๋™ ์ƒ‰์ƒ ์„ค์ •")
868
+ with gr.Row():
869
+ manual_bg_color = gr.ColorPicker(label="๋ฐฐ๊ฒฝ์ƒ‰", value="#FFFFFF", interactive=True)
870
+ with gr.Row():
871
+ manual_main_text_color = gr.ColorPicker(label="๋ฉ”์ธ ํ…์ŠคํŠธ์ƒ‰", value="#000000", interactive=True)
872
+ manual_sub_text_color = gr.ColorPicker(label="์„œ๋ธŒ ํ…์ŠคํŠธ์ƒ‰", value="#000000", interactive=True)
873
+
874
+ with gr.Group():
875
+ gr.Markdown("### ์ˆ˜๋™ ํฐํŠธ ํฌ๊ธฐ ์„ค์ •")
876
+ with gr.Row():
877
+ manual_main_font_size = gr.Slider(
878
+ minimum=20, maximum=200, value=100, step=5,
879
+ label="๋ฉ”์ธ ํฐํŠธ ํฌ๊ธฐ (px)", interactive=True
880
+ )
881
+ manual_sub_font_size = gr.Slider(
882
+ minimum=15, maximum=120, value=55, step=5,
883
+ label="์„œ๋ธŒ ํฐํŠธ ํฌ๊ธฐ (px)", interactive=True
884
+ )
885
+
886
+ with gr.Group():
887
+ gr.Markdown("### ๐Ÿ“ ์—ฌ๋ฐฑ ๋ฐ ๊ฐ„๊ฒฉ ์กฐ์ •")
888
+ with gr.Row():
889
+ top_bottom_margin = gr.Slider(
890
+ minimum=50, maximum=800, value=450, step=10,
891
+ label="์ƒํ•˜ ์—ฌ๋ฐฑ (px)",
892
+ info="์—ฌ๋ฐฑ-์นดํ”ผ-์—ฌ๋ฐฑ์˜ ์—ฌ๋ฐฑ ํฌ๊ธฐ",
893
+ interactive=True
894
+ )
895
+ with gr.Row():
896
+ text_gap = gr.Slider(
897
+ minimum=5, maximum=100, value=30, step=5,
898
+ label="๋ฉ”์ธโ†”์„œ๋ธŒ ๊ฐ„๊ฒฉ (px)",
899
+ info="๋ฉ”์ธ์นดํ”ผ์™€ ์„œ๋ธŒ์นดํ”ผ ์‚ฌ์ด ๊ฐ„๊ฒฉ",
900
+ interactive=True
901
+ )
902
+
903
+ # ํ˜„์žฌ ์ ์šฉ๋œ ์—ฌ๋ฐฑ ์ •๋ณด ํ‘œ์‹œ
904
+ margin_info = gr.Markdown(
905
+ "๐Ÿ’ก **ํ˜„์žฌ ์—ฌ๋ฐฑ ์ •๋ณด:** ์ด๋ฏธ์ง€ ์ƒ์„ฑ ํ›„ ์‹ค์ œ ์ ์šฉ๋œ ์ˆ˜์น˜๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.",
906
+ visible=True
907
+ )
908
+
909
+ # ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€ ์„น์…˜
910
+ with gr.Column(elem_classes="custom-frame"):
911
+ gr.HTML('<div class="section-title"><img src="https://cdn-icons-png.flaticon.com/512/1375/1375106.png"> ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€</div>')
912
+
913
+ # ๐ŸŽฏ ํ•ต์‹ฌ: Gradio ๊ธฐ๋ณธ ์ด๋ฏธ์ง€ ์ถœ๋ ฅ (๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ ์ž๋™ ํฌํ•จ)
914
+ output_image = gr.Image(
915
+ label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€",
916
+ show_download_button=True, # ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ ํ‘œ์‹œ
917
+ show_share_button=False, # ๊ณต์œ  ๋ฒ„ํŠผ ์ˆจ๊น€
918
+ interactive=False,
919
+ type="pil", # PIL Image ํƒ€์ž…์œผ๋กœ ์„ค์ •
920
+ elem_classes="image-container output-image-container"
921
+ )
922
+
923
+ # ๐ŸŽฏ ํ•ต์‹ฌ: Gradio ๊ธฐ๋ณธ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ ์ปดํฌ๋„ŒํŠธ
924
+ download_file = gr.File(
925
+ label="๐Ÿ“ฅ ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ",
926
+ visible=True,
927
+ interactive=False
928
+ )
929
+
930
+ # ๐ŸŽฏ ํ•ต์‹ฌ: ์—”๋“œํฌ์ธํŠธ ๊ธฐ๋ฐ˜ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋“ค
931
+
932
+ def update_copy_type_description(selected_type):
933
+ """์›๋ณธ๊ณผ ๋™์ผํ•œ ์นดํ”ผ ํƒ€์ž… ์„ ํƒ์‹œ ์„ค๋ช… ์—…๋ฐ์ดํŠธ"""
934
+ try:
935
+ client = get_client()
936
+ if not client:
937
+ return "โŒ ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์‹คํŒจ"
938
+
939
+ result = client.predict(
940
+ selected_type=selected_type,
941
+ api_name="/update_copy_type_description"
942
+ )
943
+ return result
944
+ except Exception as e:
945
+ print(f"โŒ ์นดํ”ผ ํƒ€์ž… ์„ค๋ช… ์—…๋ฐ์ดํŠธ ์‹คํŒจ: {str(e)[:50]}...")
946
+ return f"โŒ ์˜ค๋ฅ˜: ์„ค๋ช… ์—…๋ฐ์ดํŠธ ์‹คํŒจ"
947
+
948
+ def handle_copy_generation(keyword, selected_type):
949
+ """์›๋ณธ๊ณผ ๋™์ผํ•œ ์นดํ”ผ ์ƒ์„ฑ ์ฒ˜๋ฆฌ"""
950
+ try:
951
+ client = get_client()
952
+ if not client:
953
+ return ("โŒ ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์‹คํŒจ", {}, "", "", "", "", "", "", "", keyword.strip() if keyword else "")
954
+
955
+ result = client.predict(
956
+ keyword=keyword,
957
+ selected_type=selected_type,
958
+ api_name="/handle_copy_generation"
959
+ )
960
+
961
+ # ์›๋ณธ๊ณผ ๋™์ผํ•œ ํŠœํ”Œ ๋ฐ˜ํ™˜ (10๊ฐœ ์š”์†Œ)
962
+ # [0] ์ƒํƒœ๋ฉ”์‹œ์ง€, [1] copy_suggestions_state, [2-6] ์นดํ”ผ1-5, [7-8] ๋ฉ”์ธ/์„œ๋ธŒ, [9] current_keyword_state
963
+ if len(result) >= 8:
964
+ # API์—์„œ 8๊ฐœ๋ฅผ ๋ฐ›์œผ๋ฉด ์ถ”๊ฐ€ ์ƒํƒœ๊ฐ’๋“ค์„ ๋กœ์ปฌ์—์„œ ์ฒ˜๋ฆฌ
965
+ return (result[0], {}, result[1], result[2], result[3], result[4], result[5], result[6], result[7], keyword.strip() if keyword else "")
966
+ else:
967
+ return result
968
+
969
+ except Exception as e:
970
+ print(f"โŒ ์นดํ”ผ ์ƒ์„ฑ ์‹คํŒจ: {str(e)[:50]}...")
971
+ error_msg = "โŒ ์นดํ”ผ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค"
972
+ return (error_msg, {}, "", "", "", "", "", "", "", keyword.strip() if keyword else "")
973
+
974
+ def handle_copy_selection(copy_suggestions_state, selected_type, selected_copy):
975
+ """์›๋ณธ๊ณผ ๋™์ผํ•œ ์นดํ”ผ ์„ ํƒ์‹œ ๋ฉ”์ธ/์„œ๋ธŒ ํ…์ŠคํŠธ๋ฐ•์Šค ์—…๋ฐ์ดํŠธ"""
976
+ try:
977
+ client = get_client()
978
+ if not client:
979
+ return ("", "")
980
+
981
+ result = client.predict(
982
+ selected_type=selected_type,
983
+ selected_copy=selected_copy,
984
+ api_name="/handle_copy_selection"
985
+ )
986
+
987
+ # ํŠœํ”Œ ๋ฐ˜ํ™˜ (2๊ฐœ ์š”์†Œ)
988
+ return result
989
+
990
+ except Exception as e:
991
+ print(f"โŒ ์นดํ”ผ ์„ ํƒ ์‹คํŒจ: {str(e)[:50]}...")
992
+ return ("", "")
993
+
994
+ def handle_image_generation(input_image, main_text, sub_text, color_mode,
995
+ main_font_choice, sub_font_choice, manual_bg_color, manual_main_text_color,
996
+ manual_sub_text_color, manual_main_font_size, manual_sub_font_size,
997
+ top_bottom_margin, text_gap, current_keyword):
998
+ """์›๋ณธ๊ณผ ๋™์ผํ•œ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ฒ˜๋ฆฌ (์—ฌ๋ฐฑ ์กฐ์ • ๊ธฐ๋Šฅ ํฌํ•จ)"""
999
+ try:
1000
+ client = get_client()
1001
+ if not client:
1002
+ return (None, color_mode, manual_bg_color, manual_main_text_color,
1003
+ manual_sub_text_color, manual_main_font_size, manual_sub_font_size,
1004
+ None, "โŒ ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์‹คํŒจ")
1005
+
1006
+ # ์ด๋ฏธ์ง€ ํŒŒ์ผ ํ•ธ๋“ค๋ง
1007
+ image_file = None
1008
+ if input_image:
1009
+ image_file = handle_file(input_image)
1010
+
1011
+ result = client.predict(
1012
+ input_image=image_file,
1013
+ main_text=main_text,
1014
+ sub_text=sub_text,
1015
+ color_mode=color_mode,
1016
+ main_font_choice=main_font_choice,
1017
+ sub_font_choice=sub_font_choice,
1018
+ manual_bg_color=manual_bg_color,
1019
+ manual_main_text_color=manual_main_text_color,
1020
+ manual_sub_text_color=manual_sub_text_color,
1021
+ manual_main_font_size=manual_main_font_size,
1022
+ manual_sub_font_size=manual_sub_font_size,
1023
+ top_bottom_margin=top_bottom_margin,
1024
+ text_gap=text_gap,
1025
+ api_name="/handle_image_generation"
1026
+ )
1027
+
1028
+ # ์›๋ณธ๊ณผ ๋™์ผํ•œ ํŠœํ”Œ ๋ฐ˜ํ™˜ (9๊ฐœ ์š”์†Œ)
1029
+ # [0] ์ด๋ฏธ์ง€, [1] ์ƒ‰์ƒ๋ชจ๋“œ, [2] ๋ฐฐ๊ฒฝ์ƒ‰, [3] ๋ฉ”์ธํ…์ŠคํŠธ์ƒ‰, [4] ์„œ๋ธŒํ…์ŠคํŠธ์ƒ‰,
1030
+ # [5] ๋ฉ”์ธํฐํŠธํฌ๊ธฐ, [6] ์„œ๋ธŒํฐํŠธํฌ๊ธฐ, [7] ๋‹ค์šด๋กœ๋“œํŒŒ์ผ, [8] ์—ฌ๋ฐฑ์ •๋ณด
1031
+ return result
1032
+
1033
+ except Exception as e:
1034
+ print(f"โŒ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์‹คํŒจ: {str(e)[:50]}...")
1035
+ return (None, color_mode, manual_bg_color, manual_main_text_color,
1036
+ manual_sub_text_color, manual_main_font_size, manual_sub_font_size,
1037
+ None, "โŒ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค")
1038
+
1039
+ # ์›๋ณธ๊ณผ ๋™์ผํ•œ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
1040
+ copy_type_selection.change(
1041
+ fn=update_copy_type_description,
1042
+ inputs=[copy_type_selection],
1043
+ outputs=[copy_type_description]
1044
+ )
1045
+
1046
+ generate_copy_btn.click(
1047
+ fn=handle_copy_generation,
1048
+ inputs=[product_keyword, copy_type_selection],
1049
+ outputs=[copy_type_description, copy_suggestions_state,
1050
+ copy1_display, copy2_display, copy3_display, copy4_display, copy5_display,
1051
+ main_text, sub_text, current_keyword_state]
1052
+ )
1053
+
1054
+ copy_selection.change(
1055
+ fn=handle_copy_selection,
1056
+ inputs=[copy_suggestions_state, copy_type_selection, copy_selection],
1057
+ outputs=[main_text, sub_text]
1058
+ )
1059
+
1060
+ generate_image_btn.click(
1061
+ fn=handle_image_generation,
1062
+ inputs=[input_image, main_text, sub_text, color_mode,
1063
+ main_font_choice, sub_font_choice, manual_bg_color, manual_main_text_color, manual_sub_text_color,
1064
+ manual_main_font_size, manual_sub_font_size, top_bottom_margin, text_gap, current_keyword_state],
1065
+ outputs=[output_image, color_mode, manual_bg_color, manual_main_text_color, manual_sub_text_color,
1066
+ manual_main_font_size, manual_sub_font_size, download_file, margin_info]
1067
+ )
1068
+
1069
+ return app
1070
+
1071
+ if __name__ == "__main__":
1072
+ app = main()
1073
+ app.launch(share=False, inbrowser=True)