feat: completude de l'editeur Excel - mise en forme, sortie, concurrence, cache, outils IA #156
CI / lint (push) Successful in 2m36s
CI / security (push) Successful in 1m49s
CI / test (push) Successful in 4m33s
CI / build (push) Successful in 2m39s
CI / e2e (push) Successful in 15m45s

L'editeur lisait les styles mais ne les ecrivait pas, exportait la feuille
entiere et laissait le dernier-ecrivain gagner entre processus.

- A8 : bouton Mise en forme (gras/italique/souligne, alignements, couleurs via
  selecteur natif, formats de nombre, fusion, volets figes, largeur/hauteur) et
  nouvelle route PUT .../xlsx/style (verrou, backup, swap atomique, garde de
  perte, If-Match) ; A9 : decision "pas de moteur de formule" annoncee dans
  l'UI ; A10 : undo/redo unifie, piles par fichier conservees au re-rendu
- A11 : export de la selection + Markdown/HTML/impression et recherche sur
  toutes les feuilles ; A12 : concurrence optimiste (If-Match -> 409 reparable,
  retry qui relit) ; A13 : cache des metadonnees par (chemin, mtime, taille)
- A14 : outils IA .xlsm/.csv + search_workbook, analyze_range,
  edit_xlsx_structure
- tests : test_xlsx_styles.py (17), test_spreadsheet_tools.py (34),
  TestOptimisticConcurrency/TestMetaCache, JSDOM xlsx-viewer 107/107
This commit is contained in:
2026-09-30 07:04:47 -04:00
parent 1bacfd69d9
commit c5c225a68e
32 changed files with 4615 additions and 302 deletions
+118 -28
View File
@@ -164,9 +164,11 @@ l'application. Si vous refusez, rien n'est écrit.
> Les **graphiques, images et tableaux croisés** sont, eux, bien conservés.
Si le classeur est modifié ailleurs entre-temps (verrou concurrent), ObsiGate
n'interrompt pas votre travail : un bandeau vous propose de **réessayer**
l'enregistrement, vos modifications restant en place.
Si le classeur est modifié ailleurs entre-temps (autre poste, Excel,
synchronisation…), ObsiGate n'interrompt pas votre travail : un bandeau vous
propose de **réessayer**. Le bouton **Réessayer** relit d'abord le fichier pour
récupérer la version courante, puis rejoue l'enregistrement — vos
modifications restent en place pendant tout ce temps.
### Formules
@@ -182,8 +184,14 @@ curl -X PUT "http://localhost:2020/api/file/Recettes/xlsx/save?path=budget.xlsx"
- `allow_formula` : `true` pour écrire une vraie formule (`=B1*2`).
- `force` : `true` pour enregistrer malgré les éléments non préservés
(sinon l'API répond **409** `xlsx_lossy_content`).
- Deux sauvegardes simultanées sur le même fichier : la seconde reçoit
**409** `conflict` au lieu d'écraser la première.
- `if_match` (facultatif) : la **version** du fichier attendue, telle que la
lecture la renvoie (`xlsx_revision` ou `revision`). Si le fichier a changé
depuis, l'écriture est refusée (**409** `conflict`, `details.reason =
"stale_revision"`) au lieu d'écraser le travail de l'autre écrivain ;
relisez le fichier puis renvoyez la nouvelle version. Les trois routes
d'écriture (`xlsx/save`, `xlsx/structure`, `csv/save`) acceptent l'en-tête
`If-Match` ou le champ `if_match` et renvoient la version à jour dans
`revision`.
### Feuilles volumineuses et lecture par fenêtres
@@ -206,14 +214,35 @@ curl "http://localhost:2020/api/file/Recettes/xlsx/sheet?path=budget.xlsx&sheet=
lignes (1 à 1 000 par requête).
- La réponse porte `total_rows`, `truncated` et `has_more` pour paginer.
- Erreurs : **404** si la feuille n'existe pas, **415** si le fichier
n'est pas un `.xlsx`.
n'est ni un `.xlsx` ni un `.xlsm`.
### Fonctions avancées
**Barre de formule et navigation clavier** — la cellule active est nommée en
A1 au-dessus du tableau ; `Tab`/`Maj+Tab` et les flèches circulent entre les
cellules, `Entrée` valide, `Maj+Entrée` insère un retour à la ligne, une
plage se copie telle quelle vers un tableur.
**Barre de formule, zone Nom et navigation clavier** — au-dessus du tableau, la
**zone Nom** affiche l'adresse de la cellule active (`B12`) ou de la plage
sélectionnée (`A1:B3`) et elle est **éditable** (« Atteindre ») : saisissez une
référence puis `Entrée` pour y aller (`B12`, `A1:B3`, `$A$1`, ou `Feuille2!A1`
pour changer d'onglet) ; une référence inconnue est refusée avec un message et
l'adresse précédente est restaurée. La barre de formule reflète la cellule
active et propose les **noms de fonctions** courants pendant la saisie.
`Tab`/`Maj+Tab` et les flèches circulent entre les cellules, `Maj+flèches` étend
la sélection, `Entrée` valide, `Maj+Entrée` insère un saut de ligne **dans** la
cellule, `F2` ouvre la cellule en édition, `Suppr` vide la sélection,
`Échap` restaure la valeur d'origine ; `Origine`/`Fin` vont au bord de la ligne,
`Ctrl+Origine`/`Ctrl+Fin` aux coins de la feuille affichée,
`PgPréc`/`PgSuiv` font défiler d'un écran, `Ctrl+flèches` saute au bout de la
plage de données, `Ctrl+A` sélectionne toute la feuille affichée et `Ctrl+S`
enregistre. Tant que la cellule n'est pas en cours d'édition, `Suppr`
efface la sélection plutôt qu'un caractère.
**Presse-papiers de plage** — copier, couper et coller un **bloc** de cellules
(`Ctrl+C`, `Ctrl+X`, `Ctrl+V`, ou les entrées correspondantes du menu
contextuel) : coller un bloc copié ici **ou depuis Excel** remplit la plage à
partir de la cellule active et la laisse sélectionnée. Le collage est du
**texte** (formules et valeurs recopiées telles quelles) et reste **annulable** ;
« couper » efface la source après le collage (un collage sur place ne l'efface
pas). Un bloc plus large que la grille affichée est tronqué, avec un message.
**Annuler / rétablir** — `Ctrl+Z` (ou le bouton **Annuler** du ruban) revient
sur les dernières éditions de cellules, `Ctrl+Maj+Z` / `Ctrl+Y` les rétablit.
@@ -221,28 +250,65 @@ sur les dernières éditions de cellules, `Ctrl+Maj+Z` / `Ctrl+Y` les rétablit.
**Sélection et menu contextuel** — cliquer une cellule l'active, **glisser**
ou `Maj+clic` sélectionne une plage (affichée dans la zone Nom, ex. `A1:B3`),
et cliquer un **en-tête** sélectionne toute la ligne ou colonne. Un **clic
droit** (ou un **appui long** sur mobile) ouvre un menu : insérer/supprimer
une ligne ou une colonne, trier A→Z / Z→A, effacer le contenu.
droit** (ou un **appui long** sur mobile) ouvre un menu : copier, couper,
coller, insérer/supprimer une ligne ou une colonne, trier A→Z / Z→A, effacer le
contenu.
**Tri, filtre, recherche, export** — le tri (ascendant / descendant) s'applique
depuis le menu contextuel et n'affecte que l'affichage ; les lignes se filtrent,
la recherche (`Ctrl+F` du panneau) va de correspondance en correspondance, et
la feuille s'exporte en CSV. Rien de tout cela ne modifie le classeur.
depuis le menu contextuel et n'affecte que l'affichage ; les lignes se filtrent et
la recherche (`Ctrl+F` du panneau) parcourt **toutes les feuilles** : le compteur
indique le nombre de feuilles concernées et passer sur une correspondance
**active l'onglet** qui la contient.
La sortie propose quatre formats, toujours sur le **contenu affiché** (et jamais
sur les valeurs calculées en cache) :
- **CSV** (bouton `CSV`) — exporte la **sélection** quand une plage de plusieurs
cellules est active (le nom du fichier reprend la plage, ex.
`Fruits-A1B2.csv`), sinon la feuille entière ;
- **Markdown** et **HTML** (menu **Exporter**) — tableau markdown ou document
HTML autonome, mêmes règles de sélection ;
- **Imprimer** (menu **Exporter**) — imprime la feuille ou la sélection seule,
sans le ruban ni les panneaux de l'application.
Rien de tout cela ne modifie le classeur.
**Structure** — le menu **Structure** de la barre d'outils ajoute,
renomme, duplique ou supprime une feuille, et insère/supprime des lignes ou
colonnes autour de la cellule active (`PUT …/xlsx/structure`, backup
automatique et confirmation, comme pour l'édition des cellules).
**Styles et mise en page** — la lecture restitue couleurs de police et de
fond, gras/italique/souligné, alignements, cellules fusionnées et volets
figés ; l'ancrage de la zone figée est conservé au défilement.
**Mise en forme** — le bouton **Mise en forme** ouvre un menu qui agit sur la
**sélection courante** (une cellule ou une plage) :
- **caractère** — gras, italique, souligné, effacer la mise en forme ;
- **alignement** — gauche, centré, droite ;
- **couleurs** — couleur de police et couleur de fond (sélecteur natif, aucune
palette imposée) ;
- **format de nombre** — général, nombre, pourcentage, devise, date, texte ;
- **structure** — fusionner / défusionner les cellules, figer / libérer les
volets, largeur de colonne, hauteur de ligne (fusionner exige une vraie
plage).
L'écriture passe par `PUT …/xlsx/style`, avec les mêmes garanties que l'édition
des cellules : backup automatique, écriture atomique, confirmation si
l'opération détruirait des éléments non préservables (graphiques, valeurs
calculées en cache…) et **détection d'un écrivain externe** (`If-Match` →
message « Réessayer »). Un `.csv` ne portant pas de mise en forme, le bouton
n'y est pas proposé (il est également absent d'une grille en lecture seule).
La lecture restitue par ailleurs les couleurs, polices, cellules fusionnées et
volets figés du fichier ; l'ancrage de la zone figée est conservé au défilement.
Les **commentaires**, liens hypertexte, validation de données, mise en forme
conditionnelle et bordures restent hors périmètre.
**Formats de fichiers** — `.xlsm` s'édite comme un `.xlsx` et ses
**macros sont préservées** à l'enregistrement ; `.xls` et `.ods` s'affichent
en **lecture seule** ; un `.csv` s'ouvre dans la même grille et se réécrit
conformément à la RFC 4180 (les guillemets et séparateurs sont
échappés).
**macros sont préservées** à l'enregistrement (y compris le chargement des
lignes au-delà du plafond) ; `.xls` et `.ods` s'affichent en **lecture
seule** ; un `.csv` s'ouvre dans la même grille et se réécrit conformément à
la RFC 4180 (les guillemets et séparateurs sont échappés). Le **séparateur
du CSV est détecté** (`;`, `,` ou tabulation) à la lecture et **réutilisé à
l'enregistrement** : un fichier exporté par Excel en français (point-virgule)
s'affiche donc en colonnes distinctes et le reste après édition.
**Tableau de bord** — le bouton **Tableau de bord** ouvre un **inspecteur
latéral droit** (la grille reste visible à côté) qui liste les plages
@@ -253,8 +319,13 @@ numériques) avec quelques chiffres clés. Cliquer une **plage nommée**
sélectionne sa première cellule dans la grille, et le panneau est
**redimensionnable**. L'en-tête de l'inspecteur offre
aussi un accès direct à l'**assistant IA**, qui peut ensuite exploiter ces
plages (outils `list_xlsx_sheets`, `xlsx_to_markdown`, `update_xlsx_cells`,
`append_xlsx_rows`).
plages. Ses outils couvrent désormais les **trois formats édités**
(`.xlsx`, `.xlsm`, `.csv`) : `list_xlsx_sheets` et `xlsx_to_markdown` pour lire,
`search_workbook` (recherche dans toutes les feuilles, comptée par feuille),
`analyze_range` (agrégats — nombre, somme, moyenne, min, max — d'une plage A1),
`update_xlsx_cells` et `append_xlsx_rows` pour modifier, et
`edit_xlsx_structure` pour la structure (ajouter/renommer/dupliquer/supprimer
une feuille, insérer/supprimer des lignes ou des colonnes).
### Limites
@@ -262,14 +333,33 @@ plages (outils `list_xlsx_sheets`, `xlsx_to_markdown`, `update_xlsx_cells`,
sous une feuille plus grande, le bouton **« Charger la suite »** (ou le
défilement vers le bas du tableau) ajoute les lignes suivantes par
fenêtres de 500 — elles deviennent aussitôt éditables et
sauvegardables.
sauvegardables. Le chargement paresseux est **vertical uniquement** :
l'axe des colonnes reste tronqué à 40 (les colonnes au-delà ne sont ni
affichées ni exportées).
- Un **format de nombre personnalisé** (devise, pourcentage…) est signalé
par une police à chasse fixe, mais la valeur reste affichée brute.
- L'application de **styles** depuis la visionneuse (mettre en gras,
colorer) n'est pas proposée — seuls les styles existants sont rendus.
par une police à chasse fixe à la lecture ; depuis le bouton **Mise en
forme**, appliquer un format ne change que le format de la cellule, **pas**
la valeur affichée (aucun recalcul n'est fait, cf. les limites d'export
ci-dessous).
- `.xls` et `.ods` restent en lecture seule (convertir vers `.xlsx` pour
éditer) ; les macros d'un `.xlsm` sont conservées mais ne s'exécutent
pas dans ObsiGate.
- La **poignée de recopie** (fill), la multi-sélection `Ctrl+clic` et le
glisser-déposer de lignes/colonnes ne sont pas proposés ; un collage de
plusieurs cellules s'annule **cellule par cellule** (`Ctrl+Z` répété).
- Les exports (CSV, Markdown, HTML, impression) reflètent ce qui est **affiché** :
une feuille tronquée s'exporte tronquée, et les formules sortent telles
qu'enregistrées (aucune valeur calculée n'est recalculée). Un classeur reste
la source de vérité : utilisez **Charger la suite** pour exporter au-delà du
plafond.
- **Aucun moteur de formule** : ObsiGate lit et écrit les formules telles
qu'Excel les a enregistrées, sans jamais les recalculer. Une saisie
commençant par `=` ou `@` est stockée comme **texte** (garde anti-DDE)
tant que le bouton `f(x)` n'est pas activé ; l'enregistrement vous le
signale par un message. Excel reste la référence pour les valeurs calculées.
- L'**annulation** couvre l'édition, l'effacement, le tri/filtre et les
actions de structure ; en revanche une **suppression** (feuille, ligne,
colonne) n'est pas annulable, faute d'inverse.
---