diff --git a/TODO.md b/TODO.md index 063c6de..ed166cc 100644 --- a/TODO.md +++ b/TODO.md @@ -63,40 +63,35 @@ --- ### 4. π± ΠΠΎΠ±ΠΈΠ»ΡΠ½Π°Ρ ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΡ ΠΈ Π°Π΄Π°ΠΏΡΠΈΠ²Π½Π°Ρ Π²Π΅ΡΡΡΠΊΠ° (iPhone 12 / iOS Safari) -- [ ] **Π‘ΠΈΡΡΠ΅ΠΌΠ½ΡΠΉ ΡΡΠ½Π΄Π°ΠΌΠ΅Π½Ρ iOS ΠΈ ΡΠ±ΡΠΎΡ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠΉ (Core Foundation)**: - - ΠΠ΅ΡΠ°ΡΠ΅Π³ `viewport`: Π΄ΠΎΠ±Π°Π²ΠΈΡΡ `viewport-fit=cover` Π΄Π»Ρ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠΈ ΡΠ΅Π»ΠΊΠΈ ΠΈ Π½ΠΈΠΆΠ½Π΅ΠΉ ΠΏΠ°Π½Π΅Π»ΠΈ iPhone. - - Π£ΡΡΡΠ°Π½Π΅Π½ΠΈΠ΅ Π±Π°Π³Π° ΠΏΡΠΈΠ½ΡΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΠ³ΠΎ Π°Π²ΡΠΎ-Π·ΡΠΌΠ°: ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ `font-size: 16px` Π΄Π»Ρ ``, ``, `` Π½Π° ΡΠΊΡΠ°Π½Π°Ρ `β€ 768px` (ΡΠΎΡ ΡΠ°Π½ΡΡ ΠΊΠΎΠΌΠΏΠ°ΠΊΡΠ½ΡΠ΅ Π²Π½ΡΡΡΠ΅Π½Π½ΠΈΠ΅ ΠΎΡΡΡΡΠΏΡ). - - ΠΠ°ΠΌΠ΅Π½Π° `100vh` Π½Π° Π΄ΠΈΠ½Π°ΠΌΠΈΡΠ΅ΡΠΊΠΈΠ΅ Π΅Π΄ΠΈΠ½ΠΈΡΡ `100dvh` (Dynamic Viewport Height) Π΄Π»Ρ ΠΌΠΎΠ΄Π°Π»ΡΠ½ΡΡ ΠΎΠΊΠΎΠ½, ΠΎΠ²Π΅ΡΠ»Π΅Π΅Π² ΠΈ ΡΠΊΡΠ°Π½Π° Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΠΈ. - - ΠΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΡΡ ΠΎΡΡΡΡΠΏΠΎΠ² `env(safe-area-inset-top)` ΠΈ `env(safe-area-inset-bottom)`. - - ΠΡΠ΅Π΄ΠΎΡΠ²ΡΠ°ΡΠ΅Π½ΠΈΠ΅ Π³ΠΎΡΠΈΠ·ΠΎΠ½ΡΠ°Π»ΡΠ½ΠΎΠ³ΠΎ ΡΠΊΡΠΎΠ»Π»Π° (`overflow-x: hidden` Π½Π° ΡΡΠΎΠ²Π½Π΅ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ° ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ). -- [ ] **ΠΠΎΠ±ΠΈΠ»ΡΠ½Π°Ρ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΡ: ΠΏΠ΅ΡΠ΅Ρ ΠΎΠ΄ ΠΊ Π½Π°ΡΠΈΠ²Π½ΠΎΠΌΡ ΠΏΠ°ΡΡΠ΅ΡΠ½Ρ (App Header + Bottom Tab Bar)**: - - ΠΠΎΠΌΠΏΠ°ΠΊΡΠ½Π°Ρ Π²Π΅ΡΡ Π½ΡΡ ΡΠ°ΠΏΠΊΠ°: Π»ΠΎΠ³ΠΎΡΠΈΠΏ `π Notes`, ΠΏΠ΅ΡΠ΅ΠΊΠ»ΡΡΠ°ΡΠ΅Π»Ρ ΡΠ΅ΠΌΡ `π`, ΠΊΠ½ΠΎΠΏΠΊΠ° Π²ΡΠ·ΠΎΠ²Π° ΠΏΡΠΎΡΠΈΠ»Ρ/Π΄Π΅ΠΉΡΡΠ²ΠΈΠΉ. +- [x] **Π‘ΠΈΡΡΠ΅ΠΌΠ½ΡΠΉ ΡΡΠ½Π΄Π°ΠΌΠ΅Π½Ρ iOS ΠΈ ΡΠ±ΡΠΎΡ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠΉ (Core Foundation)**: + - ΠΠ΅ΡΠ°ΡΠ΅Π³ `viewport`: Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ `viewport-fit=cover` Π΄Π»Ρ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠΈ Π²ΡΡΠ΅Π·Π° (notch) ΠΈ Π½ΠΈΠΆΠ½Π΅ΠΉ ΠΏΠ°Π½Π΅Π»ΠΈ iPhone. + - Π£ΡΡΡΠ°Π½Π΅Π½ΠΈΠ΅ Π±Π°Π³Π° ΠΏΡΠΈΠ½ΡΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΠ³ΠΎ Π°Π²ΡΠΎ-Π·ΡΠΌΠ°: ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½ `font-size: 16px` Π΄Π»Ρ ``, ``, `` Π½Π° ΡΠΊΡΠ°Π½Π°Ρ `β€ 768px`. + - ΠΠ½Π΅Π΄ΡΠ΅Π½Ρ Π΄ΠΈΠ½Π°ΠΌΠΈΡΠ΅ΡΠΊΠΈΠ΅ Π΅Π΄ΠΈΠ½ΠΈΡΡ `100dvh` (Dynamic Viewport Height) ΠΈ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΡΠ΅ ΠΎΡΡΡΡΠΏΡ `env(safe-area-inset-top)` / `env(safe-area-inset-bottom)`. + - ΠΠ°ΡΠΈΡΠ° ΠΎΡ Π³ΠΎΡΠΈΠ·ΠΎΠ½ΡΠ°Π»ΡΠ½ΠΎΠ³ΠΎ ΡΠ΄Π²ΠΈΠ³Π° (`overflow-x: hidden` Π½Π° `html`, `body` ΠΈ `#app-shell`). +- [x] **ΠΠΎΠ±ΠΈΠ»ΡΠ½Π°Ρ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΡ: ΠΏΠ΅ΡΠ΅Ρ ΠΎΠ΄ ΠΊ Π½Π°ΡΠΈΠ²Π½ΠΎΠΌΡ ΠΏΠ°ΡΡΠ΅ΡΠ½Ρ (App Header + Bottom Tab Bar)**: + - ΠΠΎΠΌΠΏΠ°ΠΊΡΠ½Π°Ρ Π²Π΅ΡΡ Π½ΡΡ ΡΠ°ΠΏΠΊΠ° Π΄Π»Ρ ΡΠΌΠ°ΡΡΡΠΎΠ½ΠΎΠ²: Π»ΠΎΠ³ΠΎΡΠΈΠΏ `π Notes`, ΠΏΠ΅ΡΠ΅ΠΊΠ»ΡΡΠ°ΡΠ΅Π»Ρ ΡΠ΅ΠΌΡ `π`, ΠΊΠ½ΠΎΠΏΠΊΠ° Π²ΡΠ·ΠΎΠ²Π° ΠΏΡΠΎΡΠΈΠ»Ρ `βοΈ`. - Π€ΠΈΠΊΡΠΈΡΠΎΠ²Π°Π½Π½Π°Ρ Π½ΠΈΠΆΠ½ΡΡ ΠΏΠ°Π½Π΅Π»Ρ Π²ΠΊΠ»Π°Π΄ΠΎΠΊ (Bottom Tab Bar) Ρ Π½Π°ΡΠΈΠ²Π½ΡΠΌ iOS UX: 1. π **Π‘Π»ΠΎΠ²Π°ΡΡ** (ΠΠΎΠΈΡΠΊ ΠΈ ΡΠ»ΠΎΠ²Π°ΡΡ) 2. π **Π’ΡΠ΅Π½Π°ΠΆΡΡ** (SRS Ρ Π±Π΅ΠΉΠ΄ΠΆΠ΅ΠΌ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²Π° ΠΏΠΎΠ²ΡΠΎΡΠ΅Π½ΠΈΠΉ) 3. π **Π§ΠΈΡΠ°Π»ΠΊΠ°** (ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΠΊΠ½ΠΈΠ³) 4. π **ΠΡΠΎΠ³ΡΠ΅ΡΡ** (Π‘ΡΠ°ΡΠΈΡΡΠΈΠΊΠ° ΠΈ ΡΡΡΠΈΠΊ) 5. βοΈ **ΠΠ΅Π½Ρ** (Π‘ΠΌΠ΅Π½Π° ΠΏΠ°ΡΠΎΠ»Ρ, ΡΠΊΡΠΏΠΎΡΡ, Π²ΡΡ ΠΎΠ΄) - - Π‘ΠΎΡ ΡΠ°Π½Π΅Π½ΠΈΠ΅ ΡΠ΅ΠΊΡΡΠ΅ΠΉ Π΄Π΅ΡΠΊΡΠΎΠΏΠ½ΠΎΠΉ ΡΡΡΠΎΠΊΠΈ Π±Π΅Π· ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ Π½Π° Π±ΠΎΠ»ΡΡΠΈΡ ΡΠΊΡΠ°Π½Π°Ρ (`> 768px`). -- [ ] **ΠΠ΄Π°ΠΏΡΠ°ΡΠΈΡ ΡΠΊΡΠ°Π½Π° ΡΠ»ΠΎΠ²Π°ΡΡ ΠΈ ΠΊΠ°ΡΡΠΎΡΠ΅ΠΊ (Dictionary & Cards View)**: - - ΠΠΎΠ»Π΅ ΠΏΠΎΠΈΡΠΊΠ°/ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄Π° Π½Π° ΠΏΠΎΠ»Π½ΡΡ ΡΠΈΡΠΈΠ½Ρ ΡΠΊΡΠ°Π½Π° Ρ ΡΠ΄ΠΎΠ±Π½ΠΎΠΉ ΡΠ΅Π½ΡΠΎΡΠ½ΠΎΠΉ ΠΊΠ½ΠΎΠΏΠΊΠΎΠΉ Β«ΠΠ΅ΡΠ΅Π²Π΅ΡΡΠΈΒ». + - ΠΠΎΠ»Π½ΠΎΠ΅ ΡΠΎΡ ΡΠ°Π½Π΅Π½ΠΈΠ΅ Π΄Π΅ΡΠΊΡΠΎΠΏΠ½ΠΎΠ³ΠΎ `navbar` Π±Π΅Π· ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ Π½Π° Π±ΠΎΠ»ΡΡΠΈΡ ΡΠΊΡΠ°Π½Π°Ρ (`> 768px`). +- [x] **ΠΠ΄Π°ΠΏΡΠ°ΡΠΈΡ ΡΠΊΡΠ°Π½Π° ΡΠ»ΠΎΠ²Π°ΡΡ ΠΈ ΠΊΠ°ΡΡΠΎΡΠ΅ΠΊ (Dictionary & Cards View)**: + - ΠΠΎΠ»Π΅ ΠΏΠΎΠΈΡΠΊΠ°/ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄Π° Π½Π° ΠΏΠΎΠ»Π½ΡΡ ΡΠΈΡΠΈΠ½Ρ ΡΠΊΡΠ°Π½Π° Ρ ΠΊΠ½ΠΎΠΏΠΊΠΎΠΉ Β«ΠΠ΅ΡΠ΅Π²Π΅ΡΡΠΈΒ». - Π‘ΠΊΡΡΡΠΈΠ΅ Π½Π΅Π°ΠΊΡΡΠ°Π»ΡΠ½ΡΡ Π΄Π΅ΡΠΊΡΠΎΠΏΠ½ΡΡ ΠΏΠΎΠ΄ΡΠΊΠ°Π·ΠΎΠΊ Π³ΠΎΡΡΡΠΈΡ ΠΊΠ»Π°Π²ΠΈΡ (`Ctrl+Enter`) Π½Π° ΠΌΠΎΠ±ΠΈΠ»ΡΠ½ΡΡ ΡΡΡΡΠΎΠΉΡΡΠ²Π°Ρ . - - ΠΠ΅ΡΡΠΈΠΊΠ°Π»ΡΠ½Π°Ρ ΡΠ°ΡΠΊΠ»Π°Π΄ΠΊΠ° ΠΏΠΎΠ»Π΅ΠΉ ΠΌΠ΅ΡΠ°Π΄Π°Π½Π½ΡΡ ΠΏΡΠΈ ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ ΠΊΠ°ΡΡΠΎΡΠΊΠΈ (ΠΏΠ°ΠΏΠΊΠ°, ΡΠ΅Π³ΠΈ, Π·Π°ΠΌΠ΅ΡΠΊΠ°). - - ΠΠΎΡΠΈΠ·ΠΎΠ½ΡΠ°Π»ΡΠ½ΡΠΉ ΡΠΊΡΠΎΠ»Π» ΡΠΈΠΏΡΠΎΠ² ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΠΉ ΠΈ ΡΠ΅Π³ΠΎΠ² (`overflow-x: auto; white-space: nowrap; -webkit-overflow-scrolling: touch`) Π²ΠΌΠ΅ΡΡΠΎ ΠΌΠ½ΠΎΠ³ΠΎΡΡΡΠΎΡΠ½ΠΎΠ³ΠΎ ΠΏΠ΅ΡΠ΅Π½ΠΎΡΠ°. - - Π£Π²Π΅Π»ΠΈΡΠ΅Π½Π½ΡΠ΅ Π·ΠΎΠ½Ρ Π½Π°ΠΆΠ°ΡΠΈΡ (Touch Targets β₯ 44px) Π΄Π»Ρ ΠΊΠ½ΠΎΠΏΠΎΠΊ Π΄Π΅ΠΉΡΡΠ²ΠΈΠΉ Π½Π°Π΄ ΠΊΠ°ΡΡΠΎΡΠΊΠΎΠΉ (ΡΠ΄Π°Π»Π΅Π½ΠΈΠ΅, ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅, ΠΎΠ·Π²ΡΡΠΊΠ°). -- [ ] **ΠΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΠ²Π½Π°Ρ ΡΠΈΡΠ°Π»ΠΊΠ° ΠΈ ΠΏΠ΅ΡΠ΅Ρ ΠΎΠ΄ ΠΊ Mobile Bottom Sheet**: - - Π’ΡΠ°Π½ΡΡΠΎΡΠΌΠ°ΡΠΈΡ Π²ΡΠΏΠ»ΡΠ²Π°ΡΡΠ΅Π³ΠΎ ΠΏΠΎΠΏΠ°ΠΏΠ° ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄Π° (`reader-word-popup`) Π² Π½Π°ΡΠΈΠ²Π½ΡΡ Π²ΡΠ΄Π²ΠΈΠΆΠ½ΡΡ Π½ΠΈΠΆΠ½ΡΡ ΡΡΠΎΡΠΊΡ (Bottom Sheet) Π½Π° ΡΠΌΠ°ΡΡΡΠΎΠ½Π°Ρ : - - ΠΠ°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ Ρ ΡΠ΅ΡΠΌΠΈΠ½ΠΎΠΌ, ΡΡΠ°Π½ΡΠΊΡΠΈΠΏΡΠΈΠ΅ΠΉ ΠΈ Π±Π΅ΠΉΠ΄ΠΆΠ΅ΠΌ `β Π Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ΅`. - - ΠΠ»ΠΎΠΊ ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄Π° ΠΈ ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡΠ½ΠΎΠΉ ΡΠΈΡΠ°ΡΡ ΠΊΠ½ΠΈΠ³ΠΈ. - - ΠΠΎΠ»ΡΡΠ°Ρ ΠΊΠ½ΠΎΠΏΠΊΠ° Β«πΎ ΠΠΎΠ±Π°Π²ΠΈΡΡ Π² ΡΠ»ΠΎΠ²Π°ΡΡΒ» ΠΏΠΎΠ΄ Π±ΠΎΠ»ΡΡΠΎΠΉ ΠΏΠ°Π»Π΅Ρ. - - ΠΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π²ΡΠ»Π΅ΡΠΎΠ² Π·Π° Π³ΡΠ°Π½ΠΈΡΡ ΡΠΊΡΠ°Π½Π° ΠΈ ΠΏΠ΅ΡΠ΅ΠΊΡΡΡΠΈΡ ΡΠ΅ΠΊΡΡΠ° ΠΏΠ°Π»ΡΡΠ΅ΠΌ. + - ΠΠ΅ΡΡΠΈΠΊΠ°Π»ΡΠ½Π°Ρ ΡΠ°ΡΠΊΠ»Π°Π΄ΠΊΠ° ΠΏΠΎΠ»Π΅ΠΉ ΠΌΠ΅ΡΠ°Π΄Π°Π½Π½ΡΡ ΠΏΡΠΈ ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ ΠΊΠ°ΡΡΠΎΡΠΊΠΈ. + - ΠΠΎΡΠΈΠ·ΠΎΠ½ΡΠ°Π»ΡΠ½ΡΠΉ ΡΠΊΡΠΎΠ»Π» ΡΠΈΠΏΡΠΎΠ² ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΠΉ ΠΈ ΡΠ΅Π³ΠΎΠ² (`overflow-x: auto; white-space: nowrap; -webkit-overflow-scrolling: touch`). + - Π£Π²Π΅Π»ΠΈΡΠ΅Π½Π½ΡΠ΅ Π·ΠΎΠ½Ρ Π½Π°ΠΆΠ°ΡΠΈΡ (Touch Targets β₯ 44px) Π΄Π»Ρ ΠΊΠ½ΠΎΠΏΠΎΠΊ Π΄Π΅ΠΉΡΡΠ²ΠΈΠΉ Π½Π°Π΄ ΠΊΠ°ΡΡΠΎΡΠΊΠΎΠΉ. +- [x] **ΠΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΠ²Π½Π°Ρ ΡΠΈΡΠ°Π»ΠΊΠ° ΠΈ ΠΏΠ΅ΡΠ΅Ρ ΠΎΠ΄ ΠΊ Mobile Bottom Sheet**: + - Π’ΡΠ°Π½ΡΡΠΎΡΠΌΠ°ΡΠΈΡ Π²ΡΠΏΠ»ΡΠ²Π°ΡΡΠ΅Π³ΠΎ ΠΏΠΎΠΏΠ°ΠΏΠ° ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄Π° (`reader-word-popup`) Π² Π½Π°ΡΠΈΠ²Π½ΡΡ Π²ΡΠ΄Π²ΠΈΠΆΠ½ΡΡ Π½ΠΈΠΆΠ½ΡΡ ΡΡΠΎΡΠΊΡ (Bottom Sheet) Π½Π° ΡΠΌΠ°ΡΡΡΠΎΠ½Π°Ρ Ρ ΡΡΡΠΊΠΎΠΉ ΠΏΠ΅ΡΠ΅ΡΠ°ΡΠΊΠΈΠ²Π°Π½ΠΈΡ `sheet-drag-handle`. - ΠΠ²ΡΡ ΡΡΡΠΎΡΠ½ΡΠΉ Π°Π΄Π°ΠΏΡΠΈΠ²Π½ΡΠΉ ΡΡΠ»Π±Π°Ρ ΡΠΈΡΠ°Π»ΠΊΠΈ (Π½Π°Π²ΠΈΠ³Π°ΡΠΈΡ ΠΏΠΎ Π³Π»Π°Π²Π°ΠΌ + ΡΠ°Π·ΠΌΠ΅Ρ ΡΡΠΈΡΡΠ° `A-/A+` ΠΈ ΠΏΠΎΠ΄ΡΠ²Π΅ΡΠΊΠ°). - Π‘Π΅ΡΠΊΠ° Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ `bookshelf-grid` Π² ΠΎΠ΄Π½Ρ ΠΊΠΎΠ»ΠΎΠ½ΠΊΡ Π½Π° ΡΠΊΡΠ°Π½Π°Ρ `β€ 640px`. -- [ ] **Π’ΡΠ΅Π½Π°ΠΆΡΡ ΠΈΠ½ΡΠ΅ΡΠ²Π°Π»ΡΠ½ΡΡ ΠΏΠΎΠ²ΡΠΎΡΠ΅Π½ΠΈΠΉ (SRS Trainer)**: +- [x] **Π’ΡΠ΅Π½Π°ΠΆΡΡ ΠΈΠ½ΡΠ΅ΡΠ²Π°Π»ΡΠ½ΡΡ ΠΏΠΎΠ²ΡΠΎΡΠ΅Π½ΠΈΠΉ (SRS Trainer)**: - ΠΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠ΅ Π²ΡΡΠΎΡΡ ΠΊΠ°ΡΡΠΎΡΠΊΠΈ SRS (`max-height: 48dvh`), ΡΡΠΎΠ±Ρ ΠΊΠ°ΡΡΠΎΡΠΊΠ° ΠΈ ΠΊΠ½ΠΎΠΏΠΊΠΈ ΠΎΡΠ΅Π½ΠΊΠΈ Π³Π°ΡΠ°Π½ΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎ ΠΏΠΎΠΌΠ΅ΡΠ°Π»ΠΈΡΡ Π±Π΅Π· Π²Π΅ΡΡΠΈΠΊΠ°Π»ΡΠ½ΠΎΠ³ΠΎ ΡΠΊΡΠΎΠ»Π»Π° Π½Π° 390px. - - ΠΠ½ΠΎΠΏΠΊΠΈ ΠΎΡΠ΅Π½ΠΊΠΈ (Π‘Π½ΠΎΠ²Π° / Π’ΡΡΠ΄Π½ΠΎ / Π₯ΠΎΡΠΎΡΠΎ / ΠΠ΅Π³ΠΊΠΎ) Π² ΡΠ΅ΡΠΊΡ 2Γ2 Ρ Π²ΡΡΠΎΡΠΎΠΉ ΠΎΡ 50px ΠΈ ΡΠ΅ΡΠΊΠΈΠΌΠΈ ΡΠ²Π΅ΡΠΎΠ²ΡΠΌΠΈ Π°ΠΊΡΠ΅Π½ΡΠ°ΠΌΠΈ. + - ΠΠ½ΠΎΠΏΠΊΠΈ ΠΎΡΠ΅Π½ΠΊΠΈ (Π‘Π½ΠΎΠ²Π° / Π’ΡΡΠ΄Π½ΠΎ / Π₯ΠΎΡΠΎΡΠΎ / ΠΠ΅Π³ΠΊΠΎ) Π² ΡΠ΅ΡΠΊΡ 2Γ2 Ρ Π²ΡΡΠΎΡΠΎΠΉ ΠΎΡ 48px ΠΈ ΡΠ΅ΡΠΊΠΈΠΌΠΈ ΡΠ²Π΅ΡΠΎΠ²ΡΠΌΠΈ Π°ΠΊΡΠ΅Π½ΡΠ°ΠΌΠΈ. - ΠΠ΅ΡΡΠΈΠΊΠ°Π»ΡΠ½ΡΠΉ ΡΠΏΠΈΡΠΎΠΊ Π²Π°ΡΠΈΠ°Π½ΡΠΎΠ² Π² ΡΠ΅ΠΆΠΈΠΌΠ΅ ΠΊΠ²ΠΈΠ·Π° (4 Π²Π°ΡΠΈΠ°Π½ΡΠ°) Π½Π° Π²ΡΡ ΡΠΈΡΠΈΠ½Ρ ΡΠΊΡΠ°Π½Π°. -- [ ] **ΠΠΎΠ΄Π°Π»ΡΠ½ΡΠ΅ ΠΎΠΊΠ½Π° (Π‘ΡΠ°ΡΠΈΡΡΠΈΠΊΠ°, ΠΠ°ΡΠΎΠ»Ρ, ΠΠΊΡΠΏΠΎΡΡ)**: +- [x] **ΠΠΎΠ΄Π°Π»ΡΠ½ΡΠ΅ ΠΎΠΊΠ½Π° (Π‘ΡΠ°ΡΠΈΡΡΠΈΠΊΠ°, ΠΠ°ΡΠΎΠ»Ρ, ΠΠΊΡΠΏΠΎΡΡ)**: - ΠΠ΅ΡΡΠΈΠΊΠΈ ΡΡΠ°ΡΠΈΡΡΠΈΠΊΠΈ Π² ΡΠ΅ΡΠΊΡ 2Γ2 Π²ΠΌΠ΅ΡΡΠΎ ΡΠΆΠ°ΡΡΡ 4 ΠΊΠΎΠ»ΠΎΠ½ΠΎΠΊ. - ΠΠ»Π°Π²Π½ΡΠΉ Π³ΠΎΡΠΈΠ·ΠΎΠ½ΡΠ°Π»ΡΠ½ΡΠΉ ΡΠΊΡΠΎΠ»Π» ΠΊΠ°Π»Π΅Π½Π΄Π°ΡΡ Π°ΠΊΡΠΈΠ²Π½ΠΎΡΡΠΈ (Heatmap). - Π Π°Π·Π²ΠΎΡΠ°ΡΠΈΠ²Π°Π½ΠΈΠ΅ ΠΌΠΎΠ΄Π°Π»ΡΠ½ΡΡ ΠΎΠΊΠΎΠ½ Π² Π°Π΄Π°ΠΏΡΠΈΠ²Π½ΡΠΉ ΠΌΠΎΠ±ΠΈΠ»ΡΠ½ΡΠΉ ΡΠ»ΠΎΠΉ Ρ ΡΠ΄ΠΎΠ±Π½ΠΎΠΉ ΠΊΠ½ΠΎΠΏΠΊΠΎΠΉ Π·Π°ΠΊΡΡΡΠΈΡ. diff --git a/app/static/css/style.css b/app/static/css/style.css index b360ab6..14d3e67 100644 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -38,6 +38,11 @@ --font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; --transition: all 0.18s ease-in-out; + + --sat: env(safe-area-inset-top, 0px); + --sar: env(safe-area-inset-right, 0px); + --sab: env(safe-area-inset-bottom, 0px); + --sal: env(safe-area-inset-left, 0px); } [data-theme="light"] { @@ -75,6 +80,11 @@ padding: 0; } +html, body { + overflow-x: hidden; + -webkit-tap-highlight-color: transparent; +} + body { background-color: var(--bg-main); color: var(--text-main); @@ -82,6 +92,7 @@ body { line-height: 1.5; -webkit-font-smoothing: antialiased; min-height: 100vh; + min-height: 100dvh; } .hidden { @@ -2321,75 +2332,547 @@ body { 75% { transform: translateX(4px); } } -/* Responsive */ -@media (max-width: 640px) { +/* Mobile Navigation & Tab Bar (iOS Native Style Base) */ +.mobile-only-btn, +.mobile-tab-bar, +.mobile-only { + display: none; +} + +.mobile-tab-bar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: calc(56px + env(safe-area-inset-bottom, 0px)); + padding-bottom: env(safe-area-inset-bottom, 0px); + background-color: rgba(30, 41, 59, 0.94); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border-top: 1px solid var(--border-color); + z-index: 1000; + align-items: center; + justify-content: space-around; +} + +[data-theme="light"] .mobile-tab-bar { + background-color: rgba(255, 255, 255, 0.94); +} + +.tab-item { + flex: 1; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 3px; + background: none; + border: none; + color: var(--text-muted); + cursor: pointer; + position: relative; + padding: 6px 0; + -webkit-tap-highlight-color: transparent; + transition: var(--transition); +} + +.tab-item:active { + transform: scale(0.92); +} + +.tab-item.active { + color: var(--primary); +} + +.tab-icon { + font-size: 19px; + line-height: 1; +} + +.tab-label { + font-size: 10px; + font-weight: 600; + letter-spacing: -0.2px; +} + +.tab-badge { + position: absolute; + top: 5px; + right: calc(50% - 18px); + background-color: #ef4444; + color: #ffffff; + font-size: 10px; + font-weight: 700; + padding: 1px 5px; + border-radius: 9999px; + line-height: 1.2; +} + +/* Mobile Sheet / Menu */ +.mobile-sheet-window { + max-width: 440px; +} + +.mobile-user-card { + display: flex; + align-items: center; + gap: 12px; + padding: 14px 16px; + background: var(--bg-input); + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + margin-bottom: 16px; +} + +.mobile-user-avatar { + font-size: 28px; + background: var(--bg-surface-hover); + width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--radius-full); +} + +.mobile-user-info strong { + font-size: 16px; + color: var(--text-main); +} + +.mobile-user-info .subtitle { + font-size: 12px; + color: var(--text-muted); +} + +.mobile-menu-actions { + display: flex; + flex-direction: column; + gap: 10px; +} + +/* Sheet drag handle for mobile bottom sheets */ +.sheet-drag-handle { + width: 38px; + height: 4px; + border-radius: 2px; + background-color: var(--border-color); + margin: 0 auto 10px auto; +} + +/* ========================================================================== + Comprehensive Mobile & Responsive System (iPhone 12 / iOS WebKit & Mobile) + ========================================================================== */ +@media (max-width: 768px) { + /* 1. iOS Safari Auto-Zoom Fix (< 16px triggers zoom) */ + input, select, textarea, + .input-styled, .search-input-box input, .form-group input, + .search-mini-box input, .reader-select { + font-size: 16px !important; + } + + /* 2. Global Mobile App Container & Safe Areas */ + html, body { + overflow-x: hidden; + -webkit-overflow-scrolling: touch; + min-height: 100dvh; + } + + .app-shell { + min-height: 100dvh; + overflow-x: hidden; + } + + .main-content, + .srs-view, + .reader-view { + padding-bottom: calc(74px + env(safe-area-inset-bottom, 0px)) !important; + } + + /* 3. Header & Navigation: Clean iOS App Header */ .navbar { - padding: 10px 14px; + padding: calc(8px + env(safe-area-inset-top, 0px)) 12px 8px 12px; + position: sticky; + top: 0; } - .brand-sub { - display: none; + + .brand-sub, + .nav-center, + .nav-actions #nav-reader-btn, + .nav-actions #srs-trainer-btn, + .nav-actions #stats-open-btn, + .nav-actions #export-open-btn, + .nav-actions .user-profile { + display: none !important; } - .trainer-text { - display: none; + + .mobile-only-btn { + display: inline-flex !important; + align-items: center; + justify-content: center; } + + .mobile-tab-bar { + display: flex !important; + } + + .mobile-only { + display: block !important; + } + + /* 4. Dictionary & Search */ .main-content { - padding: 16px 12px; - gap: 24px; + padding: 14px 12px calc(76px + env(safe-area-inset-bottom, 0px)); + gap: 20px; } - .field-row { + + .search-input-box { + padding: 6px 8px 6px 12px; + } + + .search-input-box input { + padding: 8px 0; + min-width: 0; + } + + .translate-action-btn { + padding: 8px 14px; + font-size: 14px; + white-space: nowrap; + } + + .hotkey-hints { + display: none !important; + } + + .lookup-card { + padding: 16px 14px; + } + + .lookup-header { flex-direction: column; + align-items: flex-start; + gap: 8px; } - .res-term { - font-size: 22px; + + .meta-inputs-grid .field-row { + flex-direction: column; + gap: 12px; } + + .result-actions .btn { + width: 100%; + justify-content: center; + min-height: 48px; + } + + .result-actions .btn-subtext { + display: none; + } + + /* Horizontal scrollable chips for collections and tags */ + .filter-chips-row { + flex-wrap: nowrap; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + padding-bottom: 6px; + scrollbar-width: none; + } + + .filter-chips-row::-webkit-scrollbar { + display: none; + } + + .col-chip, .filter-chip { + flex-shrink: 0; + } + .list-controls { width: 100%; display: flex; align-items: center; gap: 8px; + margin-top: 10px; } + + .section-header { + flex-direction: column; + align-items: stretch; + } + .search-mini-box { flex: 1; - } - .search-mini-box input { min-width: 0; + } + + .search-mini-box input { width: 100%; + min-width: 0; } - .fc-flip-btn-wrap { - display: block; + + /* Cards */ + .vocab-card { + padding: 14px; + gap: 8px; } + + .card-top { + gap: 8px; + } + + .card-term { + font-size: 17px; + } + + .card-actions .icon-btn { + min-width: 36px; + min-height: 36px; + display: flex; + align-items: center; + justify-content: center; + } + + .card-bottom { + font-size: 11px; + } + + /* 5. SRS Trainer */ .srs-topbar { padding: 10px 12px; flex-direction: column; align-items: stretch; gap: 8px; } + .srs-topbar-left, .srs-topbar-right { justify-content: space-between; + width: 100%; } + .srs-mode-switch { overflow-x: auto; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; } + + .srs-mode-switch::-webkit-scrollbar { + display: none; + } + + .srs-tab { + flex-shrink: 0; + padding: 6px 10px; + font-size: 12px; + } + + .flashcard-wrapper { + min-height: 270px; + max-height: 48dvh; + } + + .flashcard-inner { + min-height: 270px; + } + + .fc-main-term { + font-size: 28px; + } + + .fc-footer-hint { + font-size: 11px; + } + .srs-rating-bar { grid-template-columns: 1fr 1fr; + gap: 8px; + width: 100%; } + + .btn-rate { + min-height: 48px; + padding: 8px 6px; + } + + .rate-key { + display: none; + } + .quiz-options-grid { grid-template-columns: 1fr; + gap: 8px; } + + .quiz-option-btn { + min-height: 48px; + font-size: 14px; + padding: 10px 14px; + } + + /* 6. Bookshelf & Reader View */ + .reader-view { + padding: 14px 12px calc(76px + env(safe-area-inset-bottom, 0px)); + } + + .bookshelf-header { + flex-direction: column; + align-items: stretch; + gap: 12px; + margin-bottom: 18px; + } + + .bookshelf-title-wrap h2 { + font-size: 19px; + } + + .bookshelf-actions { + width: 100%; + display: flex; + gap: 8px; + } + + .bookshelf-actions .btn, + .bookshelf-actions .file-upload-label { + flex: 1; + justify-content: center; + text-align: center; + padding: 10px; + } + + .bookshelf-grid { + grid-template-columns: 1fr; + gap: 12px; + } + + .book-card { + padding: 14px; + } + + /* Reader Interface Toolbar */ + .reader-toolbar { + padding: 8px 10px; + gap: 8px; + flex-direction: column; + align-items: stretch; + } + + .reader-toolbar-left { + width: 100%; + justify-content: space-between; + gap: 8px; + } + + .reader-meta { + flex: 1; + min-width: 0; + } + + .reader-title { + font-size: 14px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .reader-author { + font-size: 11px; + } + + .reader-toolbar-center { + width: 100%; + justify-content: space-between; + gap: 6px; + } + + .reader-select { + flex: 1; + max-width: none; + min-width: 0; + } + + .reader-toolbar-right { + width: 100%; + justify-content: space-between; + gap: 6px; + border-top: 1px solid var(--border-color); + padding-top: 6px; + } + + .reader-content-body { + padding: 14px 12px; + font-size: 17px; + line-height: 1.65; + } + + /* 7. Bottom Sheet for Word Translation Popup on Mobile */ + .reader-word-popup { + position: fixed !important; + top: auto !important; + bottom: calc(58px + env(safe-area-inset-bottom, 0px)) !important; + left: 8px !important; + right: 8px !important; + width: auto !important; + max-width: none !important; + border-radius: 18px !important; + box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.5) !important; + border: 1px solid var(--border-focus) !important; + background-color: var(--bg-surface) !important; + z-index: 10005 !important; + padding: 14px 16px !important; + animation: slideUpSheet 0.22s cubic-bezier(0.16, 1, 0.3, 1) !important; + } + + @keyframes slideUpSheet { + from { + transform: translateY(100%); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } + } + + /* 8. Modals: Mobile Bottom-Sheet style */ + .modal-backdrop { + padding: 0; + align-items: flex-end; + } + + .modal-window { + max-width: 100%; + max-height: calc(90dvh - env(safe-area-inset-top, 0px)); + border-radius: 20px 20px 0 0; + border-bottom: none; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + padding-bottom: env(safe-area-inset-bottom, 0px); + animation: slideUpSheet 0.22s cubic-bezier(0.16, 1, 0.3, 1); + } + + .stats-modal-window { + width: 100%; + max-width: 100%; + } + .stats-hero-grid { grid-template-columns: 1fr 1fr; + gap: 8px; } - .export-grid { - grid-template-columns: 1fr; - } - .summary-metrics { - grid-template-columns: 1fr; + + .heatmap-container-wrap { + -webkit-overflow-scrolling: touch; } + + /* 9. Batch action bar */ .batch-action-bar { flex-direction: column; gap: 10px; - bottom: 12px; + bottom: calc(64px + env(safe-area-inset-bottom, 0px)) !important; + left: 10px; + right: 10px; } + .batch-actions { width: 100%; justify-content: space-between; diff --git a/app/static/index.html b/app/static/index.html index 590ac74..1d95c39 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -2,10 +2,10 @@ - + Notes β ΠΠ΅ΡΡΠΎΠ½Π°Π»ΡΠ½ΡΠΉ ΡΡΠ΅Π½Π°ΠΆΠ΅Ρ ΠΈ ΡΠ»ΠΎΠ²Π°ΡΡ Π°Π½Π³Π»ΠΈΠΉΡΠΊΠΎΠ³ΠΎ ΡΠ·ΡΠΊΠ° - + @@ -82,6 +82,7 @@ π€ ΠΠΊΡΠΏΠΎΡΡ π + βοΈ user π ΠΠ°ΡΠΎΠ»Ρ @@ -599,6 +600,57 @@ β ΠΡΠΌΠ΅Π½Π° + + + + + π + Π‘Π»ΠΎΠ²Π°ΡΡ + + + π + Π’ΡΠ΅Π½Π°ΠΆΡΡ + 0 + + + π + Π§ΠΈΡΠ°Π»ΠΊΠ° + + + π + ΠΡΠΎΠ³ΡΠ΅ΡΡ + + + βοΈ + ΠΠ΅Π½Ρ + + + + + + + + + βοΈ ΠΡΠΎΡΠΈΠ»Ρ & ΠΠ΅Π½Ρ + β + + + + π€ + + user + ΠΠΈΡΠ½ΡΠΉ ΡΠ»ΠΎΠ²Π°ΡΡ English Lexicon + + + + π Π‘ΡΠ°ΡΠΈΡΡΠΈΠΊΠ° & ΠΠ°Π»Π΅Π½Π΄Π°ΡΡ + π€ ΠΠΊΡΠΏΠΎΡΡ ΡΠ»ΠΎΠ²Π°ΡΡ + π Π‘ΠΌΠ΅Π½ΠΈΡΡ ΠΏΠ°ΡΠΎΠ»Ρ + π ΠΠ΅ΡΠ΅ΠΊΠ»ΡΡΠΈΡΡ ΡΠ΅ΠΌΡ + ΠΡΠΉΡΠΈ ΠΈΠ· Π°ΠΊΠΊΠ°ΡΠ½ΡΠ° + + + @@ -902,6 +954,7 @@ + word @@ -928,7 +981,7 @@ - + diff --git a/app/static/js/app.js b/app/static/js/app.js index e9d9c10..9d57358 100644 --- a/app/static/js/app.js +++ b/app/static/js/app.js @@ -392,7 +392,10 @@ document.addEventListener('DOMContentLoaded', () => { function showApp() { loginOverlay.classList.add('hidden'); if (appShell) appShell.classList.remove('hidden'); - userDisplayName.textContent = currentUser ? currentUser.username : 'user'; + const name = currentUser ? currentUser.username : 'user'; + userDisplayName.textContent = name; + const mobileUserName = document.getElementById('mobile-user-name'); + if (mobileUserName) mobileUserName.textContent = name; } loginForm.addEventListener('submit', async (e) => { @@ -506,7 +509,17 @@ document.addEventListener('DOMContentLoaded', () => { const resp = await fetch('/api/srs/stats'); if (resp.ok) { const stats = await resp.json(); - srsBadge.textContent = stats.due_cards || 0; + const due = stats.due_cards || 0; + srsBadge.textContent = due; + const tabSrsBadge = document.getElementById('tab-srs-badge'); + if (tabSrsBadge) { + tabSrsBadge.textContent = due; + if (due > 0) { + tabSrsBadge.classList.remove('hidden'); + } else { + tabSrsBadge.classList.add('hidden'); + } + } } } catch (e) {} } @@ -970,10 +983,19 @@ document.addEventListener('DOMContentLoaded', () => { renderSrsCurrentCard(); }); + function updateMobileActiveTab(tabName) { + document.querySelectorAll('.mobile-tab-bar .tab-item').forEach(btn => { + btn.classList.remove('active'); + }); + const target = document.getElementById(`tab-nav-${tabName}`); + if (target) target.classList.add('active'); + } + function openDictionaryView() { srsView.classList.add('hidden'); if (readerView) readerView.classList.add('hidden'); dictionaryView.classList.remove('hidden'); + updateMobileActiveTab('dict'); loadInitialData(); } @@ -981,6 +1003,7 @@ document.addEventListener('DOMContentLoaded', () => { dictionaryView.classList.add('hidden'); if (readerView) readerView.classList.add('hidden'); srsView.classList.remove('hidden'); + updateMobileActiveTab('srs'); startSrsTrainingSession(false, tagFilter); } @@ -988,6 +1011,7 @@ document.addEventListener('DOMContentLoaded', () => { srsView.classList.add('hidden'); if (readerView) readerView.classList.add('hidden'); dictionaryView.classList.remove('hidden'); + updateMobileActiveTab('dict'); loadInitialData(); } @@ -995,11 +1019,107 @@ document.addEventListener('DOMContentLoaded', () => { dictionaryView.classList.add('hidden'); srsView.classList.add('hidden'); if (readerView) readerView.classList.remove('hidden'); + updateMobileActiveTab('reader'); loadBookshelf(); } + function initMobileNavigation() { + const tabDict = document.getElementById('tab-nav-dict'); + const tabSrs = document.getElementById('tab-nav-srs'); + const tabReader = document.getElementById('tab-nav-reader'); + const tabStats = document.getElementById('tab-nav-stats'); + const tabMenu = document.getElementById('tab-nav-menu'); + + const mobileMenuModal = document.getElementById('mobile-menu-modal'); + const mobileMenuToggleBtn = document.getElementById('mobile-menu-toggle-btn'); + const mobileMenuCloseBtn = document.getElementById('mobile-menu-close-btn'); + + const mobileStatsBtn = document.getElementById('mobile-stats-btn'); + const mobileExportBtn = document.getElementById('mobile-export-btn'); + const mobilePwdBtn = document.getElementById('mobile-pwd-btn'); + const mobileThemeBtn = document.getElementById('mobile-theme-btn'); + const mobileLogoutBtn = document.getElementById('mobile-logout-btn'); + + if (tabDict) tabDict.addEventListener('click', () => openDictionaryView()); + if (tabSrs) tabSrs.addEventListener('click', () => openSrsView()); + if (tabReader) tabReader.addEventListener('click', () => openReaderView()); + + if (tabStats) { + tabStats.addEventListener('click', () => { + if (statsModal) { + statsModal.classList.remove('hidden'); + loadStatsAndHeatmap(); + } + }); + } + + function openMobileMenu() { + if (mobileMenuModal) { + const mobileUserName = document.getElementById('mobile-user-name'); + if (mobileUserName && currentUser) { + mobileUserName.textContent = currentUser.username; + } + mobileMenuModal.classList.remove('hidden'); + } + } + + function closeMobileMenu() { + if (mobileMenuModal) { + mobileMenuModal.classList.add('hidden'); + } + } + + if (tabMenu) tabMenu.addEventListener('click', openMobileMenu); + if (mobileMenuToggleBtn) mobileMenuToggleBtn.addEventListener('click', openMobileMenu); + if (mobileMenuCloseBtn) mobileMenuCloseBtn.addEventListener('click', closeMobileMenu); + + if (mobileMenuModal) { + mobileMenuModal.addEventListener('click', (e) => { + if (e.target === mobileMenuModal) closeMobileMenu(); + }); + } + + if (mobileStatsBtn) { + mobileStatsBtn.addEventListener('click', () => { + closeMobileMenu(); + if (statsModal) { + statsModal.classList.remove('hidden'); + loadStatsAndHeatmap(); + } + }); + } + + if (mobileExportBtn) { + mobileExportBtn.addEventListener('click', () => { + closeMobileMenu(); + if (exportModal) exportModal.classList.remove('hidden'); + }); + } + + if (mobilePwdBtn) { + mobilePwdBtn.addEventListener('click', () => { + closeMobileMenu(); + if (changePwdModal) changePwdModal.classList.remove('hidden'); + }); + } + + if (mobileThemeBtn) { + mobileThemeBtn.addEventListener('click', () => { + themeToggle.click(); + }); + } + + if (mobileLogoutBtn) { + mobileLogoutBtn.addEventListener('click', () => { + closeMobileMenu(); + logoutBtn.click(); + }); + } + } + navBrandHome.addEventListener('click', () => openDictionaryView()); if (navReaderBtn) navReaderBtn.addEventListener('click', () => openReaderView()); + initMobileNavigation(); async function startSrsTrainingSession(forceAll = false, tagFilter = null) { srsLoading.classList.remove('hidden'); @@ -2396,6 +2516,11 @@ document.addEventListener('DOMContentLoaded', () => { // Precise popup positioning near target element without scroll double-offset function positionWordPopup(targetElement) { if (!targetElement || !readerWordPopup) return; + if (window.innerWidth <= 768) { + readerWordPopup.style.top = ''; + readerWordPopup.style.left = ''; + return; + } const rect = targetElement.getBoundingClientRect(); const popupWidth = 320; const popupHeight = readerWordPopup.offsetHeight || 220; @@ -2533,6 +2658,7 @@ document.addEventListener('DOMContentLoaded', () => { function updatePopupPositionOnScroll() { if (!readerWordPopup || readerWordPopup.classList.contains('hidden')) return; + if (window.innerWidth <= 768) return; // Mobile uses fixed bottom sheet if (!popupCurrentData || !popupCurrentData.targetElement || !popupCurrentData.targetElement.isConnected) { readerWordPopup.classList.add('hidden'); return;
ΠΠΈΡΠ½ΡΠΉ ΡΠ»ΠΎΠ²Π°ΡΡ English Lexicon