Spaces:
Sleeping
Sleeping
Update: Added default gradient background, fixed UI layout, and improved demo data
Browse files- .gitattributes +35 -0
- app.py +12 -1
- setup_assets.py +39 -0
- static/default_bg.png +0 -0
- templates/index.html +10 -9
.gitattributes
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
app.py
CHANGED
|
@@ -5,7 +5,10 @@ app = Flask(__name__, static_folder='static', template_folder='templates')
|
|
| 5 |
|
| 6 |
@app.route('/')
|
| 7 |
def index():
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
@app.route('/health')
|
| 11 |
def health():
|
|
@@ -15,6 +18,14 @@ def health():
|
|
| 15 |
def serve_static(path):
|
| 16 |
return send_from_directory('static', path)
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
if __name__ == '__main__':
|
| 19 |
# Use 7860 for Hugging Face Spaces
|
| 20 |
port = int(os.environ.get('PORT', 7860))
|
|
|
|
| 5 |
|
| 6 |
@app.route('/')
|
| 7 |
def index():
|
| 8 |
+
try:
|
| 9 |
+
return render_template('index.html')
|
| 10 |
+
except Exception as e:
|
| 11 |
+
return jsonify({"error": str(e), "message": "Template rendering failed. Please check server logs."}), 500
|
| 12 |
|
| 13 |
@app.route('/health')
|
| 14 |
def health():
|
|
|
|
| 18 |
def serve_static(path):
|
| 19 |
return send_from_directory('static', path)
|
| 20 |
|
| 21 |
+
@app.errorhandler(500)
|
| 22 |
+
def internal_error(error):
|
| 23 |
+
return jsonify({"error": "Internal Server Error", "details": str(error)}), 500
|
| 24 |
+
|
| 25 |
+
@app.errorhandler(404)
|
| 26 |
+
def not_found(error):
|
| 27 |
+
return jsonify({"error": "Not Found", "path": str(error)}), 404
|
| 28 |
+
|
| 29 |
if __name__ == '__main__':
|
| 30 |
# Use 7860 for Hugging Face Spaces
|
| 31 |
port = int(os.environ.get('PORT', 7860))
|
setup_assets.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from PIL import Image, ImageDraw
|
| 2 |
+
|
| 3 |
+
def create_gradient_image(width, height, start_color, end_color):
|
| 4 |
+
base = Image.new('RGB', (width, height), start_color)
|
| 5 |
+
top = Image.new('RGB', (width, height), end_color)
|
| 6 |
+
mask = Image.new('L', (width, height))
|
| 7 |
+
mask_data = []
|
| 8 |
+
for y in range(height):
|
| 9 |
+
for x in range(width):
|
| 10 |
+
mask_data.append(int(255 * (y / height)))
|
| 11 |
+
mask.putdata(mask_data)
|
| 12 |
+
base.paste(top, (0, 0), mask)
|
| 13 |
+
return base
|
| 14 |
+
|
| 15 |
+
def main():
|
| 16 |
+
# Create a nice gradient background (Purple to Blue/Pink)
|
| 17 |
+
# Dimensions: Enough for 4 slides of 3:4 (1080x1350)
|
| 18 |
+
# 4 * 1080 = 4320 width
|
| 19 |
+
# But user might want it stretched, so let's make a generic 16:9 or similar high res
|
| 20 |
+
# Let's make a 4000x3000 image to be safe
|
| 21 |
+
|
| 22 |
+
# Soft mesh gradient style (simulated with linear gradient for simplicity)
|
| 23 |
+
# Start: #e0c3fc (Light Purple)
|
| 24 |
+
# End: #8ec5fc (Light Blue)
|
| 25 |
+
|
| 26 |
+
img = create_gradient_image(2000, 1500, (224, 195, 252), (142, 197, 252))
|
| 27 |
+
|
| 28 |
+
# Add some "noise" or pattern to make it look professional?
|
| 29 |
+
# Let's keep it simple gradient for now, clean and modern.
|
| 30 |
+
|
| 31 |
+
import os
|
| 32 |
+
if not os.path.exists('static'):
|
| 33 |
+
os.makedirs('static')
|
| 34 |
+
|
| 35 |
+
img.save('static/default_bg.png')
|
| 36 |
+
print("Created static/default_bg.png")
|
| 37 |
+
|
| 38 |
+
if __name__ == "__main__":
|
| 39 |
+
main()
|
static/default_bg.png
ADDED
|
templates/index.html
CHANGED
|
@@ -172,7 +172,7 @@
|
|
| 172 |
</button>
|
| 173 |
<button @click="addNumbering" class="col-span-2 flex items-center justify-center gap-2 p-2 bg-indigo-50 border border-indigo-100 rounded-lg hover:bg-indigo-100 text-indigo-700 transition">
|
| 174 |
<span class="text-xs font-bold">#</span>
|
| 175 |
-
<span class="text-xs font-medium">自动页码 (1/
|
| 176 |
</button>
|
| 177 |
</div>
|
| 178 |
</section>
|
|
@@ -210,7 +210,7 @@
|
|
| 210 |
<label class="text-[10px] text-gray-400 mb-1 block">文字颜色</label>
|
| 211 |
<div class="flex items-center gap-2 border rounded p-1 bg-white">
|
| 212 |
<input type="color" v-model="selectedElement.color" class="w-6 h-6 border-none rounded cursor-pointer">
|
| 213 |
-
<span class="text-xs text-gray-500">
|
| 214 |
</div>
|
| 215 |
</div>
|
| 216 |
<div>
|
|
@@ -283,7 +283,7 @@
|
|
| 283 |
<!-- Page Labels -->
|
| 284 |
<div class="absolute -top-6 left-0 w-full flex text-xs text-gray-500 font-mono">
|
| 285 |
<div v-for="n in slideCount" :key="n" class="flex-1 text-center relative">
|
| 286 |
-
<span class="bg-gray-200 px-2 py-0.5 rounded-full">Page
|
| 287 |
<!-- Vertical Dashed Line Marker -->
|
| 288 |
<div class="absolute top-6 bottom-[-600px] right-0 border-r border-dashed border-gray-400 opacity-30 h-[1000px] pointer-events-none" v-if="n < slideCount"></div>
|
| 289 |
</div>
|
|
@@ -325,7 +325,7 @@
|
|
| 325 |
<div class="flex gap-4 mx-auto w-fit">
|
| 326 |
<div v-for="(img, idx) in previewImages" :key="idx" class="relative group shadow-lg">
|
| 327 |
<img :src="img" class="h-[500px] object-contain bg-white">
|
| 328 |
-
<span class="absolute bottom-2 right-2 bg-black/50 text-white text-xs px-2 py-1 rounded">
|
| 329 |
</div>
|
| 330 |
</div>
|
| 331 |
</div>
|
|
@@ -340,7 +340,7 @@
|
|
| 340 |
<div v-if="isProcessing" class="fixed inset-0 z-[60] flex flex-col items-center justify-center bg-white/80 backdrop-blur-md">
|
| 341 |
<div class="loader mb-4 border-t-red-600 w-12 h-12 border-4"></div>
|
| 342 |
<p class="text-gray-800 font-bold text-lg animate-pulse">正在高清渲染切片...</p>
|
| 343 |
-
<p class="text-gray-500 text-sm mt-2">Generating
|
| 344 |
</div>
|
| 345 |
</div>
|
| 346 |
|
|
@@ -348,6 +348,7 @@
|
|
| 348 |
const { createApp, ref, computed, onMounted } = Vue;
|
| 349 |
|
| 350 |
createApp({
|
|
|
|
| 351 |
setup() {
|
| 352 |
// State
|
| 353 |
const slideCount = ref(4);
|
|
@@ -390,7 +391,7 @@
|
|
| 390 |
y: BASE_HEIGHT * 0.4,
|
| 391 |
fontSize: 64,
|
| 392 |
fontWeight: 900,
|
| 393 |
-
color: '#
|
| 394 |
fontFamily: "'Noto Sans SC', sans-serif",
|
| 395 |
textAlign: 'center',
|
| 396 |
hasBg: false,
|
|
@@ -407,12 +408,12 @@
|
|
| 407 |
y: BASE_HEIGHT * 0.55,
|
| 408 |
fontSize: 24,
|
| 409 |
fontWeight: 400,
|
| 410 |
-
color: '#
|
| 411 |
fontFamily: "'ZCOOL KuaiLe', cursive",
|
| 412 |
textAlign: 'center',
|
| 413 |
hasBg: true,
|
| 414 |
-
bgColor: '
|
| 415 |
-
hasShadow:
|
| 416 |
isBold: false
|
| 417 |
});
|
| 418 |
|
|
|
|
| 172 |
</button>
|
| 173 |
<button @click="addNumbering" class="col-span-2 flex items-center justify-center gap-2 p-2 bg-indigo-50 border border-indigo-100 rounded-lg hover:bg-indigo-100 text-indigo-700 transition">
|
| 174 |
<span class="text-xs font-bold">#</span>
|
| 175 |
+
<span class="text-xs font-medium">自动页码 (1/[[slideCount]])</span>
|
| 176 |
</button>
|
| 177 |
</div>
|
| 178 |
</section>
|
|
|
|
| 210 |
<label class="text-[10px] text-gray-400 mb-1 block">文字颜色</label>
|
| 211 |
<div class="flex items-center gap-2 border rounded p-1 bg-white">
|
| 212 |
<input type="color" v-model="selectedElement.color" class="w-6 h-6 border-none rounded cursor-pointer">
|
| 213 |
+
<span class="text-xs text-gray-500">[[selectedElement.color]]</span>
|
| 214 |
</div>
|
| 215 |
</div>
|
| 216 |
<div>
|
|
|
|
| 283 |
<!-- Page Labels -->
|
| 284 |
<div class="absolute -top-6 left-0 w-full flex text-xs text-gray-500 font-mono">
|
| 285 |
<div v-for="n in slideCount" :key="n" class="flex-1 text-center relative">
|
| 286 |
+
<span class="bg-gray-200 px-2 py-0.5 rounded-full">Page [[ n ]]</span>
|
| 287 |
<!-- Vertical Dashed Line Marker -->
|
| 288 |
<div class="absolute top-6 bottom-[-600px] right-0 border-r border-dashed border-gray-400 opacity-30 h-[1000px] pointer-events-none" v-if="n < slideCount"></div>
|
| 289 |
</div>
|
|
|
|
| 325 |
<div class="flex gap-4 mx-auto w-fit">
|
| 326 |
<div v-for="(img, idx) in previewImages" :key="idx" class="relative group shadow-lg">
|
| 327 |
<img :src="img" class="h-[500px] object-contain bg-white">
|
| 328 |
+
<span class="absolute bottom-2 right-2 bg-black/50 text-white text-xs px-2 py-1 rounded">[[idx+1]]</span>
|
| 329 |
</div>
|
| 330 |
</div>
|
| 331 |
</div>
|
|
|
|
| 340 |
<div v-if="isProcessing" class="fixed inset-0 z-[60] flex flex-col items-center justify-center bg-white/80 backdrop-blur-md">
|
| 341 |
<div class="loader mb-4 border-t-red-600 w-12 h-12 border-4"></div>
|
| 342 |
<p class="text-gray-800 font-bold text-lg animate-pulse">正在高清渲染切片...</p>
|
| 343 |
+
<p class="text-gray-500 text-sm mt-2">Generating [[ slideCount ]] slides at [[ aspectRatio ]]</p>
|
| 344 |
</div>
|
| 345 |
</div>
|
| 346 |
|
|
|
|
| 348 |
const { createApp, ref, computed, onMounted } = Vue;
|
| 349 |
|
| 350 |
createApp({
|
| 351 |
+
delimiters: ['[[', ']]'],
|
| 352 |
setup() {
|
| 353 |
// State
|
| 354 |
const slideCount = ref(4);
|
|
|
|
| 391 |
y: BASE_HEIGHT * 0.4,
|
| 392 |
fontSize: 64,
|
| 393 |
fontWeight: 900,
|
| 394 |
+
color: '#ffffff',
|
| 395 |
fontFamily: "'Noto Sans SC', sans-serif",
|
| 396 |
textAlign: 'center',
|
| 397 |
hasBg: false,
|
|
|
|
| 408 |
y: BASE_HEIGHT * 0.55,
|
| 409 |
fontSize: 24,
|
| 410 |
fontWeight: 400,
|
| 411 |
+
color: '#ffffff',
|
| 412 |
fontFamily: "'ZCOOL KuaiLe', cursive",
|
| 413 |
textAlign: 'center',
|
| 414 |
hasBg: true,
|
| 415 |
+
bgColor: 'rgba(255,255,255,0.2)',
|
| 416 |
+
hasShadow: true,
|
| 417 |
isBold: false
|
| 418 |
});
|
| 419 |
|