3v324v23 commited on
Commit
228cbdf
·
1 Parent(s): 731718b

Refactor code structure for improved readability and maintainability

Browse files
.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

  • SHA256: 35ce45096bf91d4a8ba094ea8a9857ed558736a54a8eb299eefd6e8fcbc82600
  • Pointer size: 130 Bytes
  • Size of remote file: 49.3 kB
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>احكي · אחכי</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">אחכי<span class="ar">احكي</span></div>
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

  • SHA256: 35ce45096bf91d4a8ba094ea8a9857ed558736a54a8eb299eefd6e8fcbc82600
  • Pointer size: 130 Bytes
  • Size of remote file: 49.3 kB