Files
ObsiGate/docs/features/mobile-editor.md
T
bruno 45be3125de
CI / lint (push) Successful in 1m3s
CI / security (push) Successful in 46s
CI / test (push) Successful in 2m43s
CI / build (push) Successful in 38s
CI / e2e (push) Successful in 11m12s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
feat(mobile): editeur mobile natif - interface tactile optimisee (#69)
Barre d'outils Markdown flottante (gras/italique/code/liste/lien) sur la selection CodeMirror, bouton Coller persistant (contournement iOS), zoom par pincement + hauteur ajustable persistes, raccourcis swipe (gauche -> liens entrants, droite -> table des matieres), mode lecture plein ecran avec navigation entre fichiers du dossier. 22 tests JSDOM + spec E2E mobile. Docs: fiche feature, CHANGELOG, ROADMAP, guide i18n FR/EN, README.
2026-09-11 23:51:16 -04:00

124 lines
6.3 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é + 22 tests frontend (2026-09-11)
> **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` — 22 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.
---
## Tests
`tests/frontend/mobile-editor.test.mjs` (JSDOM, 22 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`.
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à.