Refactor code structure for improved readability and maintainability
Browse files- .gitattributes +1 -0
- app/main.py +2 -0
- arabic_splash/README.md +12 -0
- arabic_splash/app.py +12 -0
- arabic_splash/index.html +20 -0
- arabic_splash/requirements.txt +2 -0
- arabic_splash/static/logo.png +3 -0
- arabic_splash/static/splash.css +12 -0
- arabic_splash/static/splash.js +9 -0
- static/index.html +33 -2
- static/logo.png +3 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* 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
|
|
|
|
|
|
| 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
|
| 36 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
app/main.py
CHANGED
|
@@ -19,6 +19,7 @@ from pathlib import Path
|
|
| 19 |
from dotenv import load_dotenv
|
| 20 |
from fastapi import FastAPI, File, Form, HTTPException, Query, UploadFile
|
| 21 |
from fastapi.responses import HTMLResponse, Response
|
|
|
|
| 22 |
|
| 23 |
from .phrases import CATEGORIES, LESSONS, PHRASES, public_phrases
|
| 24 |
from .prompt_tts import ENGINES, PromptTTSUnavailable, hebrew_mp3
|
|
@@ -53,6 +54,7 @@ def create_app(scorer: Scorer | None = None, tts: SawtakTTS | None = None) -> Fa
|
|
| 53 |
yield
|
| 54 |
|
| 55 |
app = FastAPI(title="ihki", lifespan=lifespan)
|
|
|
|
| 56 |
|
| 57 |
@app.get("/")
|
| 58 |
def index():
|
|
|
|
| 19 |
from dotenv import load_dotenv
|
| 20 |
from fastapi import FastAPI, File, Form, HTTPException, Query, UploadFile
|
| 21 |
from fastapi.responses import HTMLResponse, Response
|
| 22 |
+
from fastapi.staticfiles import StaticFiles
|
| 23 |
|
| 24 |
from .phrases import CATEGORIES, LESSONS, PHRASES, public_phrases
|
| 25 |
from .prompt_tts import ENGINES, PromptTTSUnavailable, hebrew_mp3
|
|
|
|
| 54 |
yield
|
| 55 |
|
| 56 |
app = FastAPI(title="ihki", lifespan=lifespan)
|
| 57 |
+
app.mount("/static", StaticFiles(directory=STATIC), name="static")
|
| 58 |
|
| 59 |
@app.get("/")
|
| 60 |
def index():
|
arabic_splash/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Animated splash for FastAPI / HTML
|
| 2 |
+
|
| 3 |
+
Run locally:
|
| 4 |
+
|
| 5 |
+
```bash
|
| 6 |
+
pip install -r requirements.txt
|
| 7 |
+
uvicorn app:app --host 0.0.0.0 --port 7860
|
| 8 |
+
```
|
| 9 |
+
|
| 10 |
+
For Hugging Face Spaces Docker, run uvicorn on port 7860 and expose port 7860 in your Dockerfile.
|
| 11 |
+
|
| 12 |
+
To integrate into an existing project: copy static/logo.png, splash.css, splash.js; add the stylesheet to your HTML head, the `#app-splash` div from index.html at the beginning of body, and the JS script at the end of body. Ensure your FastAPI app serves /static. The demo uses three clipped copies of the original image, preserving the logo exactly. It is a reveal animation, not true vector stroke tracing.
|
arabic_splash/app.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
from fastapi import FastAPI
|
| 3 |
+
from fastapi.responses import FileResponse
|
| 4 |
+
from fastapi.staticfiles import StaticFiles
|
| 5 |
+
|
| 6 |
+
BASE = Path(__file__).resolve().parent
|
| 7 |
+
app = FastAPI()
|
| 8 |
+
app.mount('/static', StaticFiles(directory=BASE / 'static'), name='static')
|
| 9 |
+
|
| 10 |
+
@app.get('/')
|
| 11 |
+
def home():
|
| 12 |
+
return FileResponse(BASE / 'index.html')
|
arabic_splash/index.html
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="he" dir="rtl">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
| 5 |
+
<title>Arabic School — Splash Demo</title>
|
| 6 |
+
<link rel="stylesheet" href="/static/splash.css">
|
| 7 |
+
<style>body{margin:0;font-family:system-ui,sans-serif;background:#faf9f6;color:#252525}main{max-width:720px;margin:12vh auto;padding:24px;text-align:center}button{padding:12px 20px;border:1px solid #333;background:white;border-radius:8px;cursor:pointer}</style>
|
| 8 |
+
</head>
|
| 9 |
+
<body>
|
| 10 |
+
<div id="app-splash" role="status" aria-label="האפליקציה נטענת">
|
| 11 |
+
<div class="splash-art" aria-hidden="true">
|
| 12 |
+
<img class="splash-layer splash-symbol" src="/static/logo.png" alt="">
|
| 13 |
+
<img class="splash-layer splash-arabic" src="/static/logo.png" alt="">
|
| 14 |
+
<img class="splash-layer splash-hebrew" src="/static/logo.png" alt="">
|
| 15 |
+
</div>
|
| 16 |
+
</div>
|
| 17 |
+
<main><h1>ברוכים הבאים</h1><p>זהו מסך הדגמה. החליפו את התוכן הזה בממשק האפליקציה שלכם.</p><button onclick="location.reload()">הפעל את האנימציה שוב</button></main>
|
| 18 |
+
<script src="/static/splash.js" defer></script>
|
| 19 |
+
</body>
|
| 20 |
+
</html>
|
arabic_splash/requirements.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn
|
arabic_splash/static/logo.png
ADDED
|
Git LFS Details
|
arabic_splash/static/splash.css
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
:root { --splash-bg: #fff; --splash-duration: 3100ms; }
|
| 2 |
+
#app-splash { position:fixed; inset:0; z-index:99999; display:grid; place-items:center; background:var(--splash-bg); transition:opacity .45s ease, visibility .45s ease; }
|
| 3 |
+
#app-splash.splash-done { opacity:0; visibility:hidden; pointer-events:none; }
|
| 4 |
+
.splash-art { position:relative; width:min(88vw, 560px); aspect-ratio:489/225; }
|
| 5 |
+
.splash-layer { position:absolute; inset:0; width:100%; height:100%; object-fit:contain; }
|
| 6 |
+
.splash-symbol { clip-path:polygon(64% 0,100% 0,100% 100%,64% 100%); animation:symbol-in .95s ease-out both; transform-origin:82% 48%; }
|
| 7 |
+
.splash-arabic { clip-path:polygon(0 0,65% 0,65% 72%,0 72%); animation:arabic-in 1.05s .65s ease-out both; }
|
| 8 |
+
.splash-hebrew { clip-path:polygon(0 69%,65% 69%,65% 100%,0 100%); animation:hebrew-in .65s 1.6s ease-out both; }
|
| 9 |
+
@keyframes symbol-in { from {opacity:0; transform:scale(.78) rotate(-18deg)} to {opacity:1; transform:scale(1) rotate(0)} }
|
| 10 |
+
@keyframes arabic-in { from {opacity:0; clip-path:polygon(65% 0,65% 0,65% 72%,65% 72%)} to {opacity:1; clip-path:polygon(0 0,65% 0,65% 72%,0 72%)} }
|
| 11 |
+
@keyframes hebrew-in { from {opacity:0; transform:translateY(10px)} to {opacity:1; transform:translateY(0)} }
|
| 12 |
+
@media (prefers-reduced-motion:reduce) { .splash-layer {animation:none!important} #app-splash {transition:none} }
|
arabic_splash/static/splash.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
(() => {
|
| 2 |
+
const splash = document.getElementById('app-splash');
|
| 3 |
+
if (!splash) return;
|
| 4 |
+
const hide = () => { splash.classList.add('splash-done'); window.setTimeout(() => splash.remove(), 500); };
|
| 5 |
+
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
| 6 |
+
const start = () => window.setTimeout(hide, reduced ? 300 : 3100);
|
| 7 |
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start, { once:true });
|
| 8 |
+
else start();
|
| 9 |
+
})();
|
static/index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
<head>
|
| 4 |
<meta charset="utf-8">
|
| 5 |
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
| 6 |
-
<title>
|
| 7 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 8 |
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Assistant:wght@400;600;700&family=Secular+One&family=Noto+Naskh+Arabic:wght@500;700&family=Noto+Sans+Mono&display=swap">
|
| 9 |
<style>
|
|
@@ -99,12 +99,32 @@ body.driving .mic{width:84px;height:84px}
|
|
| 99 |
.voicebox select{font:600 14px var(--f-body);padding:4px 8px;border-radius:8px;border:1px solid var(--line);background:var(--surface);color:var(--ink)}
|
| 100 |
.note{font-size:12px;margin:0;color:var(--muted);max-width:65ch}
|
| 101 |
.warn{background:var(--amber-bg);color:var(--amber);border-radius:10px;padding:10px 12px;font-size:14px}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
</style>
|
| 103 |
</head>
|
| 104 |
<body>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
<div class="wrap">
|
| 106 |
<header>
|
| 107 |
-
<div class="brand">א
|
| 108 |
<div class="sub">תרגמו לערבית מדוברת ואמרו בקול</div>
|
| 109 |
<button class="drive" id="driveBtn" title="מצב נסיעה: האפליקציה מקריאה, מקשיבה ואומרת את ההערכה בקול">🚗 נסיעה</button>
|
| 110 |
</header>
|
|
@@ -498,5 +518,16 @@ fetch("/api/phrases").then(r=>r.json()).then(d=>{
|
|
| 498 |
buildCats(); startCat();
|
| 499 |
}).catch(()=>{ $("prompt").textContent="השרת לא זמין."; });
|
| 500 |
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 501 |
</body>
|
| 502 |
</html>
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="utf-8">
|
| 5 |
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
| 6 |
+
<title>אהלא וסהלא</title>
|
| 7 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 8 |
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Assistant:wght@400;600;700&family=Secular+One&family=Noto+Naskh+Arabic:wght@500;700&family=Noto+Sans+Mono&display=swap">
|
| 9 |
<style>
|
|
|
|
| 99 |
.voicebox select{font:600 14px var(--f-body);padding:4px 8px;border-radius:8px;border:1px solid var(--line);background:var(--surface);color:var(--ink)}
|
| 100 |
.note{font-size:12px;margin:0;color:var(--muted);max-width:65ch}
|
| 101 |
.warn{background:var(--amber-bg);color:var(--amber);border-radius:10px;padding:10px 12px;font-size:14px}
|
| 102 |
+
/* Opening splash: three clipped copies of the logo revealed in turn, then faded out by the script at the end of body. */
|
| 103 |
+
:root { --splash-bg: #fff; --splash-duration: 3100ms; }
|
| 104 |
+
#app-splash { position:fixed; inset:0; z-index:99999; display:grid; place-items:center; background:var(--splash-bg); transition:opacity .45s ease, visibility .45s ease; }
|
| 105 |
+
#app-splash.splash-done { opacity:0; visibility:hidden; pointer-events:none; }
|
| 106 |
+
.splash-art { position:relative; width:min(88vw, 560px); aspect-ratio:489/225; }
|
| 107 |
+
.splash-layer { position:absolute; inset:0; width:100%; height:100%; object-fit:contain; }
|
| 108 |
+
.splash-symbol { clip-path:polygon(64% 0,100% 0,100% 100%,64% 100%); animation:symbol-in .95s ease-out both; transform-origin:82% 48%; }
|
| 109 |
+
.splash-arabic { clip-path:polygon(0 0,65% 0,65% 72%,0 72%); animation:arabic-in 1.05s .65s ease-out both; }
|
| 110 |
+
.splash-hebrew { clip-path:polygon(0 69%,65% 69%,65% 100%,0 100%); animation:hebrew-in .65s 1.6s ease-out both; }
|
| 111 |
+
@keyframes symbol-in { from {opacity:0; transform:scale(.78) rotate(-18deg)} to {opacity:1; transform:scale(1) rotate(0)} }
|
| 112 |
+
@keyframes arabic-in { from {opacity:0; clip-path:polygon(65% 0,65% 0,65% 72%,65% 72%)} to {opacity:1; clip-path:polygon(0 0,65% 0,65% 72%,0 72%)} }
|
| 113 |
+
@keyframes hebrew-in { from {opacity:0; transform:translateY(10px)} to {opacity:1; transform:translateY(0)} }
|
| 114 |
+
@media (prefers-reduced-motion:reduce) { .splash-layer {animation:none!important} #app-splash {transition:none} }
|
| 115 |
</style>
|
| 116 |
</head>
|
| 117 |
<body>
|
| 118 |
+
<div id="app-splash" role="status" aria-label="האפליקציה נטענת">
|
| 119 |
+
<div class="splash-art" aria-hidden="true">
|
| 120 |
+
<img class="splash-layer splash-symbol" src="/static/logo.png" alt="">
|
| 121 |
+
<img class="splash-layer splash-arabic" src="/static/logo.png" alt="">
|
| 122 |
+
<img class="splash-layer splash-hebrew" src="/static/logo.png" alt="">
|
| 123 |
+
</div>
|
| 124 |
+
</div>
|
| 125 |
<div class="wrap">
|
| 126 |
<header>
|
| 127 |
+
<div class="brand">אהלא וסהלא</div>
|
| 128 |
<div class="sub">תרגמו לערבית מדוברת ואמרו בקול</div>
|
| 129 |
<button class="drive" id="driveBtn" title="מצב נסיעה: האפליקציה מקריאה, מקשיבה ואומרת את ההערכה בקול">🚗 נסיעה</button>
|
| 130 |
</header>
|
|
|
|
| 518 |
buildCats(); startCat();
|
| 519 |
}).catch(()=>{ $("prompt").textContent="השרת לא זמין."; });
|
| 520 |
</script>
|
| 521 |
+
<script>
|
| 522 |
+
(() => {
|
| 523 |
+
const splash = document.getElementById('app-splash');
|
| 524 |
+
if (!splash) return;
|
| 525 |
+
const hide = () => { splash.classList.add('splash-done'); window.setTimeout(() => splash.remove(), 500); };
|
| 526 |
+
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
| 527 |
+
const start = () => window.setTimeout(hide, reduced ? 300 : 3100);
|
| 528 |
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start, { once:true });
|
| 529 |
+
else start();
|
| 530 |
+
})();
|
| 531 |
+
</script>
|
| 532 |
</body>
|
| 533 |
</html>
|
static/logo.png
ADDED
|
Git LFS Details
|