Files
ObsiGate/docs/features/mobile-editor.md
T
bruno d47fcbf2be
CI / lint (push) Successful in 1m10s
CI / security (push) Successful in 46s
CI / test (push) Successful in 2m23s
CI / build (push) Successful in 41s
CI / e2e (push) Successful in 10m28s
fix(mobile): bouton mode lecture visible uniquement fichier ouvert (BUG-016)
2026-09-12 22:44:15 -04:00

131 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# #69 - Éditeur mobile natif — Interface tactile optimisée
> **Statut :** ✅ Terminé — 100 % implémenté + 24 tests frontend (2026-09-12)
> **Effort :** 2-3 jours (réalisé) | **Impact :** 🟢
> **Références :** [Roadmap](../ROADMAP.md) · [Changelog](../../CHANGELOG.md)
- **Fichiers clés :**
- `frontend/js/mobile-editor.js` — module complet (helpers purs + câblage UI)
- `frontend/style.css` — barre flottante, poignée de redimensionnement, mode lecture, bottom sheet
- `frontend/index.html` — section « Édition mobile » du guide intégré + lien de navigation
- `frontend/locales/{fr,en}.json` — clés `mobile_editor.*` et `help.mobile_editor_*`
- `frontend/js/app.js` — appel `initMobileEditor()` au démarrage
- `tests/frontend/mobile-editor.test.mjs` — 24 tests JSDOM (helpers + câblage DOM)
- `.gitea/workflows/ci.yml` — test ajouté au job `lint`
- **Description :** sur téléphone et tablette, l'édition et la lecture s'adaptent au tactile :
barre d'outils Markdown flottante, bouton « Coller » persistant, zoom par pincement, hauteur
ajustable, gestes de balayage (liens entrants / table des matières) et mode lecture plein écran
avec navigation d'un fichier à l'autre.
---
## Architecture
```
mobile-editor.js
├── Helpers purs (testables) clampZoom · detectSwipe · formatChange · insertText
│ applyFormatToView · applyFormatToTextarea
└── Câblage UI (initMobileEditor)
├── Barre d'outils flottante #me-toolbar (gras/italique/code/liste/lien + A−/A+)
├── Presse-papiers #me-paste (Clipboard API + repli iOS)
├── Zoom pincement touchstart/move (2 doigts → taille de police)
├── Hauteur ajustable #me-resize-handle (poignée, persistée)
├── Bottom sheet #me-sheet (liens entrants / table des matières)
├── Raccourcis swipe #content-area (gauche → backlinks, droite → TOC)
└── Mode lecture #me-reading-btn (plein écran + navigation fichiers)
```
## 1. Barre d'outils mobile flottante
Injectée dans `#editor-modal` et affichée uniquement sous `768px` lorsque l'éditeur est actif
(`.editor-modal.active .me-toolbar`). Chaque bouton applique une transformation Markdown à la
sélection courante de CodeMirror (ou du `textarea` de secours) :
| Bouton | Action | Transformation |
|---|---|---|
| **B** | `bold` | `**texte**` |
| *I* | `italic` | `*texte*` |
| `<>` | `code` | `` `texte` `` |
| • | `list` | préfixe `- ` sur chaque ligne sélectionnée |
| 🔗 | `link` | `[texte](url)` avec le curseur placé sur `url` |
- **Toggle** : ré-appuyer sur un bouton alors que la sélection porte déjà les marqueurs
retire ces marqueurs (ex. `**hi**` sélectionné → `hi`).
- **Sans sélection** : les marqueurs sont insérés et le curseur est placé à l'intérieur.
- Les helpers `formatChange()` (calcul de la transaction) et `applyFormatToView()` /
`applyFormatToTextarea()` (application) sont purs côté calcul → testables sans CodeMirror.
## 2. Presse-papiers — bouton « Coller » persistant
iOS Safari n'autorise `navigator.clipboard.readText()` que sur geste utilisateur explicite et peut
le refuser. Le bouton `📋` :
1. tente `navigator.clipboard.readText()` ;
2. en cas de refus ou d'absence, affiche un message d'aide (`mobile_editor.paste_hint`) invitant à
appuyer longuement dans le champ puis à choisir « Coller » ;
3. en cas de succès, insère le texte au niveau du curseur (CodeMirror ou textarea).
## 3. Adaptation CodeMirror
- **Zoom par pincement** : deux doigts sur `#editor-body` calculent le ratio de distance et
ajustent la taille de police du `.cm-scroller` (bornée `10–28px`). Boutons `A−`/`A+` pour les
utilisateurs sans geste tactile.
- **Hauteur ajustable** : une poignée (`#me-resize-handle`) entre le corps de l'éditeur et le
pied permet d'étirer/réduire la zone d'édition (`flex: 0 0 <px>`), bornée à `140px`.
- Les deux valeurs sont **persistées** dans `localStorage` (`obsigate-editor-font-size`,
`obsigate-editor-height`) et réappliquées à chaque ouverture / changement de fichier via un
`MutationObserver`.
## 4. Raccourcis swipe (lecture)
Sur `#content-area`, un `touchstart`/`touchend` unique détecte un geste horizontal
(`detectSwipe`, seuil `60px`, dominance horizontale requise) :
- **Balayage gauche** → bottom sheet des **liens entrants** (fetch `/api/file/{vault}/backlinks`,
clic → ouverture dans un onglet) ;
- **Balayage droite** → bottom sheet de la **table des matières** (`state.headingsCache`,
clic → `scrollIntoView`).
## 5. Mode lecture plein écran
Le bouton flottant `📖` (`#me-reading-btn`) bascule `body.reading-mode` :
- masque l'en-tête, la sidebar, la barre latérale droite, la barre d'outils mobile et les actions
de fichier ; centre le contenu en colonne de lecture ;
- demande l'API **Fullscreen** (repli silencieux si non supportée, ex. iOS) ; la sortie du
plein écran (Échap) resynchronise la classe ;
- en mode lecture, le **balayage horizontal** navigue vers le fichier **suivant/précédent** du
même dossier (`/api/vault/{vault}/files?dir=…&recursive=false`, tri alphabétique, bouclage
borné avec message si extrémité atteinte) ;
- l'état est mémorisé (`obsigate-reading-mode`) et restauré au rechargement sur mobile.
**Visibilité conditionnelle (BUG-016).** Le bouton `📖` n'est affiché que si un **fichier est
ouvert** (`state.currentPath`) **et** que le panneau assistant est fermé : sinon il passait
au-dessus du panneau mobile plein écran et masquait le bouton d'envoi. `updateReadingButtonVisibility()`
est appelé à l'init, à chaque mutation de `#content-area` (MutationObserver) et sur les événements
`bookslm:opened` / `bookslm:closed`.
---
## Tests
`tests/frontend/mobile-editor.test.mjs` (JSDOM, 24 tests) couvre :
- `FORMAT_ACTIONS`, `clampZoom`, `detectSwipe` (seuil, dominance verticale, gauche/droite) ;
- `formatChange` pour bold/italic/code/liste/lien, avec et sans sélection, toggle et ligne unique ;
- `insertText`, `applyFormatToTextarea`, `applyEditorFontSize` (clamp + persistance) ;
- le câblage DOM : construction de `#me-toolbar`, idempotence, contrôles du mode lecture,
bascule `toggleReadingMode`/`isReadingMode`, et visibilité conditionnelle de `#me-reading-btn`
(fichier ouvert + assistant fermé).
Les chemins réseau (backlinks, fichiers adjacents) et les gestes réels restent à valider en E2E
navigateur / test manuel mobile.
## Notes
- Le module n'importe `ui.js`, `auth.js` et `viewer.js` que **dynamiquement** dans les fonctions
concernées, afin de rester testable en JSDOM sans charger la pile applicative complète.
- Aucune modification backend : les endpoints réutilisés (`/backlinks`, `/vault/{vault}/files`)
existaient déjà.