ginipick commited on
Commit
2e20ef4
ยท
verified ยท
1 Parent(s): b16d98a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +159 -215
app.py CHANGED
@@ -74,239 +74,183 @@ def page(lst, pg):
74
  def html(cards, pg, total):
75
  if not cards:
76
  return "<div style='text-align:center;padding:70px;color:#555;'>ํ‘œ์‹œํ•  ๋ฐฐํฌ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.</div>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
- # ์ˆœ์ˆ˜ HTML๊ณผ CSS๋กœ ๊ตฌํ˜„
79
- h = f"""
80
- <!DOCTYPE html>
81
- <html>
82
- <head>
83
- <meta charset="UTF-8">
84
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
85
- <style>
86
- * {{
87
- margin: 0;
88
- padding: 0;
89
- box-sizing: border-box;
90
- }}
91
-
92
- body, html {{
93
- width: 100%;
94
- height: 100%;
95
- overflow: hidden;
96
- font-family: Arial, sans-serif;
97
- background: #f0f0f0;
98
- }}
99
-
100
- #app {{
101
- position: fixed;
102
- top: 0;
103
- left: 0;
104
- width: 100%;
105
- height: 100%;
106
- display: flex;
107
- flex-direction: column;
108
- }}
109
-
110
- #grid-container {{
111
- flex: 1;
112
- display: grid;
113
- grid-template-columns: repeat(3, 1fr);
114
- grid-template-rows: repeat(3, 1fr);
115
- gap: 10px;
116
- padding: 10px;
117
- width: 100%;
118
- height: 100%;
119
- }}
120
-
121
- .card {{
122
- background: white;
123
- border-radius: 10px;
124
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
125
- display: flex;
126
- flex-direction: column;
127
- overflow: hidden;
128
- position: relative;
129
- height: 100%;
130
- }}
131
-
132
- .card-header {{
133
- padding: 8px 12px;
134
- font-size: 0.9rem;
135
- font-weight: bold;
136
- border-bottom: 1px solid #f0f0f0;
137
- z-index: 2;
138
- background: rgba(255,255,255,0.9);
139
- overflow: hidden;
140
- text-overflow: ellipsis;
141
- white-space: nowrap;
142
- }}
143
-
144
- .card-date {{
145
- font-size: 0.75rem;
146
- color: #888;
147
- margin-top: 2px;
148
- }}
149
-
150
- .card-frame {{
151
- flex: 1;
152
- position: relative;
153
- overflow: hidden;
154
- }}
155
-
156
- .card-frame iframe {{
157
- position: absolute;
158
- top: 0;
159
- left: 0;
160
- width: 142.857%;
161
- height: 142.857%;
162
- transform: scale(0.7);
163
- transform-origin: top left;
164
- border: 0;
165
- }}
166
-
167
- .card-footer {{
168
- padding: 6px 12px;
169
- text-align: right;
170
- font-size: 0.8rem;
171
- border-top: 1px solid #f0f0f0;
172
- background: rgba(255,255,255,0.9);
173
- z-index: 2;
174
- }}
175
-
176
- .card-footer a {{
177
- color: #4a6dd8;
178
- text-decoration: none;
179
- font-weight: bold;
180
- }}
181
-
182
- #nav-container {{
183
- position: absolute;
184
- bottom: 0;
185
- left: 0;
186
- width: 100%;
187
- padding: 15px 0;
188
- display: flex;
189
- justify-content: center;
190
- align-items: center;
191
- gap: 20px;
192
- z-index: 10;
193
- }}
194
-
195
- .nav-button {{
196
- padding: 10px 25px;
197
- background: rgba(255,255,255,0.9);
198
- border: 1px solid #ddd;
199
- border-radius: 50px;
200
- font-size: 1.1rem;
201
- font-weight: bold;
202
- cursor: pointer;
203
- box-shadow: 0 4px 10px rgba(0,0,0,0.15);
204
- transition: all 0.2s ease;
205
- color: #333;
206
- }}
207
-
208
- .nav-button:hover {{
209
- transform: translateY(-2px);
210
- box-shadow: 0 6px 15px rgba(0,0,0,0.18);
211
- }}
212
-
213
- .page-info {{
214
- position: absolute;
215
- bottom: 5px;
216
- left: 0;
217
- width: 100%;
218
- text-align: center;
219
- font-size: 0.8rem;
220
- color: #888;
221
- z-index: 9;
222
- }}
223
-
224
- /* ๋ฐฐ๊ฒฝ ๊ทธ๋ž˜๋””์–ธํŠธ ํšจ๊ณผ */
225
- #bottom-gradient {{
226
- position: absolute;
227
- bottom: 0;
228
- left: 0;
229
- width: 100%;
230
- height: 80px;
231
- background: linear-gradient(to bottom, rgba(240,240,240,0) 0%, rgba(240,240,240,0.9) 70%);
232
- z-index: 5;
233
- pointer-events: none;
234
- }}
235
- </style>
236
- </head>
237
- <body>
238
- <div id="app">
239
- <div id="grid-container">
240
  """
241
 
242
- # ์นด๋“œ ์ถ”๊ฐ€
243
  for c in cards:
244
  date = datetime.datetime.fromtimestamp(int(c["ts"])).strftime("%Y-%m-%d")
245
  h += f"""
246
- <div class="card">
247
- <div class="card-header">
248
- {c['title']}
249
- <div class="card-date">{date}</div>
250
- </div>
251
- <div class="card-frame">
252
- <iframe src="{c['url']}" loading="lazy" allow="accelerometer; camera; encrypted-media; gyroscope;"></iframe>
253
- </div>
254
- <div class="card-footer">
255
- <a href="{c['url']}" target="_blank">์›๋ณธโ†—</a>
256
- </div>
257
- </div>
258
- """
259
 
260
- # ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ ํŽ˜์ด์ง€ ์ •๋ณด
261
- h += f"""
262
- </div>
263
- <div id="bottom-gradient"></div>
264
- <div id="nav-container">
265
- <button class="nav-button" onclick="prevPage()">โ—€ ์ด์ „</button>
266
- <button class="nav-button" onclick="nextPage()">๋‹ค์Œ โ–ถ</button>
267
- </div>
268
- <div class="page-info">Page {pg} / {total}</div>
269
  </div>
270
-
271
- <script>
272
- function prevPage() {{
273
- if ({pg} > 1) {{
274
- document.getElementById('prev-button').click();
275
- }}
276
- }}
277
-
278
- function nextPage() {{
279
- if ({pg} < {total}) {{
280
- document.getElementById('next-button').click();
281
- }}
282
- }}
283
-
284
- // ์Šคํฌ๋กค ๋ฐฉ์ง€
285
- document.addEventListener('wheel', function(e) {{
286
- e.preventDefault();
287
- }}, {{ passive: false }});
288
-
289
- document.addEventListener('touchmove', function(e) {{
290
- e.preventDefault();
291
- }}, {{ passive: false }});
292
- </script>
293
- </body>
294
- </html>
295
  """
296
 
 
 
 
297
  return h
298
 
299
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 6. Gradio Blocks UI โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
300
  def build():
301
  _init_best()
302
- with gr.Blocks(title="Vibe Game Craft", css="footer{display:none !important;}") as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  # ์ƒํƒœ ๋ฐ ์ถœ๋ ฅ
304
  bp = gr.State(1)
305
- out = gr.HTML(elem_id="display-area", visible=True)
306
 
307
- # ์ˆจ๊ฒจ์ง„ ๋ฒ„ํŠผ (JavaScript์—์„œ ํ˜ธ์ถœ์šฉ)
308
- b_prev = gr.Button("์ด์ „", elem_id="prev-button", visible=False)
309
- b_next = gr.Button("๋‹ค์Œ", elem_id="next-button", visible=False)
 
310
 
311
  def show_best(p=1):
312
  d, t = page(_load_best(), p)
 
74
  def html(cards, pg, total):
75
  if not cards:
76
  return "<div style='text-align:center;padding:70px;color:#555;'>ํ‘œ์‹œํ•  ๋ฐฐํฌ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.</div>"
77
+ css = r"""
78
+ <style>
79
+ body {
80
+ margin: 0;
81
+ padding: 0;
82
+ font-family: Poppins, sans-serif;
83
+ background: #f0f0f0;
84
+ overflow: hidden;
85
+ }
86
+ .container {
87
+ display: flex;
88
+ flex-direction: column;
89
+ width: 100%;
90
+ height: 100vh;
91
+ box-sizing: border-box;
92
+ padding: 10px;
93
+ overflow: hidden;
94
+ }
95
+ .grid {
96
+ display: grid;
97
+ grid-template-columns: repeat(3, 1fr);
98
+ grid-template-rows: repeat(3, 1fr);
99
+ gap: 12px;
100
+ width: 100%;
101
+ flex: 1;
102
+ margin-bottom: 60px; /* ๋ฒ„ํŠผ์„ ์œ„ํ•œ ๊ณต๊ฐ„ */
103
+ }
104
+ .card {
105
+ background: #fff;
106
+ border-radius: 10px;
107
+ overflow: hidden;
108
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
109
+ display: flex;
110
+ flex-direction: column;
111
+ height: 100%;
112
+ }
113
+ .hdr {
114
+ padding: 8px 12px;
115
+ background: rgba(255,255,255,.95);
116
+ border-bottom: 1px solid #eee;
117
+ flex-shrink: 0;
118
+ z-index: 10;
119
+ }
120
+ .ttl {
121
+ margin: 0;
122
+ font-size: 0.95rem;
123
+ font-weight: 600;
124
+ color: #333;
125
+ white-space: nowrap;
126
+ overflow: hidden;
127
+ text-overflow: ellipsis;
128
+ }
129
+ .date {
130
+ margin-top: 2px;
131
+ font-size: 0.75rem;
132
+ color: #777;
133
+ }
134
+ .frame {
135
+ flex: 1;
136
+ position: relative;
137
+ }
138
+ .frame iframe {
139
+ position: absolute;
140
+ top: 0;
141
+ left: 0;
142
+ width: 142.857%;
143
+ height: 142.857%;
144
+ transform: scale(0.7);
145
+ transform-origin: top left;
146
+ border: 0;
147
+ }
148
+ .foot {
149
+ padding: 6px 12px;
150
+ background: rgba(255,255,255,.95);
151
+ text-align: right;
152
+ flex-shrink: 0;
153
+ border-top: 1px solid #f0f0f0;
154
+ z-index: 10;
155
+ }
156
+ .link {
157
+ font-size: 0.8rem;
158
+ font-weight: 600;
159
+ color: #4a6dd8;
160
+ text-decoration: none;
161
+ }
162
+
163
+ /* ๋ฒ„ํŠผ ์ปจํ…Œ์ด๋„ˆ */
164
+ .button-area {
165
+ position: fixed;
166
+ bottom: 0;
167
+ left: 0;
168
+ right: 0;
169
+ height: 60px;
170
+ display: flex;
171
+ justify-content: center;
172
+ align-items: center;
173
+ background: #f0f0f0;
174
+ box-shadow: 0 -2px 10px rgba(0,0,0,0.05);
175
+ z-index: 100;
176
+ }
177
+
178
+ /* ํŽ˜์ด์ง€ ์ •๋ณด */
179
+ .page-info {
180
+ position: fixed;
181
+ bottom: 5px;
182
+ left: 0;
183
+ right: 0;
184
+ text-align: center;
185
+ font-size: 0.8rem;
186
+ color: #777;
187
+ z-index: 101;
188
+ }
189
+ </style>"""
190
 
191
+ h = css + """
192
+ <div class="container">
193
+ <div class="grid">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  """
195
 
 
196
  for c in cards:
197
  date = datetime.datetime.fromtimestamp(int(c["ts"])).strftime("%Y-%m-%d")
198
  h += f"""
199
+ <div class="card">
200
+ <div class="hdr"><p class="ttl">{c['title']}</p><p class="date">{date}</p></div>
201
+ <div class="frame"><iframe src="{c['url']}" loading="lazy"
202
+ allow="accelerometer; camera; encrypted-media; gyroscope;"></iframe></div>
203
+ <div class="foot"><a class="link" href="{c['url']}" target="_blank">์›๋ณธโ†—</a></div>
204
+ </div>"""
 
 
 
 
 
 
 
205
 
206
+ h += """
 
 
 
 
 
 
 
 
207
  </div>
208
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  """
210
 
211
+ # ํŽ˜์ด์ง€ ์ •๋ณด
212
+ h += f'<div class="page-info">Page {pg} / {total}</div>'
213
+
214
  return h
215
 
216
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 6. Gradio Blocks UI โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
217
  def build():
218
  _init_best()
219
+ with gr.Blocks(title="Vibe Game Craft", css="""
220
+ footer {display: none !important;}
221
+ .button-row {
222
+ position: fixed !important;
223
+ bottom: 0 !important;
224
+ left: 0 !important;
225
+ right: 0 !important;
226
+ background: #f0f0f0 !important;
227
+ padding: 10px !important;
228
+ text-align: center !important;
229
+ box-shadow: 0 -2px 10px rgba(0,0,0,0.05) !important;
230
+ margin: 0 !important;
231
+ z-index: 1000 !important;
232
+ }
233
+ .button-row button {
234
+ margin: 0 10px !important;
235
+ padding: 10px 20px !important;
236
+ font-size: 16px !important;
237
+ font-weight: bold !important;
238
+ border-radius: 50px !important;
239
+ }
240
+ body {
241
+ margin: 0 !important;
242
+ padding: 0 !important;
243
+ overflow: hidden !important;
244
+ }
245
+ """) as demo:
246
  # ์ƒํƒœ ๋ฐ ์ถœ๋ ฅ
247
  bp = gr.State(1)
248
+ out = gr.HTML()
249
 
250
+ # ํŽ˜์ด์ง€ ๋„ค๋น„๊ฒŒ์ด์…˜ (๋งจ ์•„๋ž˜ ๊ณ ์ •)
251
+ with gr.Row(elem_classes="button-row"):
252
+ b_prev = gr.Button("โ—€ ์ด์ „", size="lg")
253
+ b_next = gr.Button("๋‹ค์Œ โ–ถ", size="lg")
254
 
255
  def show_best(p=1):
256
  d, t = page(_load_best(), p)