Commit Graph
104 Commits
Author SHA1 Message Date
bruno 2be0b15bcf fix: partage dirigé — ACL court-circuitée après résolution Partage/ + chemin canonique au token #196
CI / lint (push) Successful in 3m0s
CI / security (push) Failing after 1m38s
CI / test (push) Successful in 4m58s
CI / build (push) Successful in 1m36s
CI / e2e (push) Successful in 19m55s
- le partage EST l'autorisation: plus de 403 'Accès refusé à la vault' pour un
  destinataire sans accès au vault émetteur (api_file, raw, download)
- chemin virtuel canonique Partage/<token>/<nom> — désambiguïse les homonymes,
  fallback par nom conservé
- 'reçu' = partagé_avec contient l'utilisateur: un admin ne voit plus les
  partages des autres comme reçus (fix File not found: Partage/…)
- +3 tests (14 total)
2026-10-10 23:11:12 -04:00
bruno de6bde1613 fix: partage dirigé — dossier Partage virtuel + ouverture en onglet #196
CI / lint (push) Successful in 3m1s
CI / security (push) Failing after 1m58s
CI / test (push) Successful in 4m43s
CI / build (push) Successful in 1m39s
CI / e2e (push) Successful in 20m28s
- _resolve_shared_file: home-<user>/Partage/<f> -> fichier source (/api/file, raw, download)
- clic dossier Partage: dépliage sans navigation (fix Directory not found)
- fichiers reçus ouverts en onglet applicatif (arbre, recherche, dashboard)
- POST /api/share: path vide/null rejeté (400) — un share path:null cassait /api/shares
- +2 tests (13 total)
2026-10-10 21:48:46 -04:00
bruno fdc9f47a0d feat: visibilité & recherche des partages dirigés #196
CI / lint (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / security (push) Canceled after 0s
CI / build (push) Canceled after 0s
CI / e2e (push) Canceled after 0s
- icône share-2 bleue sur les fichiers partagés dans l'arborescence
- dossier virtuel Partage/ à la racine du home du destinataire (lecture seule /s/{token})
- recherche plein texte couvre les documents reçus (merge search_vaults, share_token)
- cache front invalidé à la création/révocation ; +2 tests non-fuite
2026-10-10 21:23:00 -04:00
bruno 49c715199d feat: partage dirigé entre utilisateurs #196
CI / test (push) Canceled after 0s
CI / security (push) Canceled after 0s
CI / build (push) Canceled after 0s
CI / e2e (push) Canceled after 0s
CI / lint (push) Canceled after 1m29s
- create_share/shared_with + validation des destinataires
- gate auth sur /s/{token} (+pdf, raw) : 404 opaque hors créateur/admin/destinataires
- GET /api/shares scopé (non-admin = créés + reçus), révocation créateur/admin
- UI : dialogue dirigé/public + destinataires, dashboard « partagé par X »
- i18n FR/EN, tests test_directed_shares.py (9), fiche feature
2026-10-10 20:14:29 -04:00
bruno 8662d23ec8 fix: un dossier perso n'est plus visible par les autres comptes #194
CI / lint (push) Successful in 2m57s
CI / security (push) Successful in 1m40s
CI / test (push) Successful in 4m39s
CI / build (push) Successful in 1m34s
CI / e2e (push) Successful in 17m44s
L'admin (vaults: ["*"]) voyait le home de chaque utilisateur dans sa
barre latérale : "*" ouvrait tous les vaults, home-* compris.

- backend/auth/middleware.py : check_vault_access exige un octroi
  explicite pour tout vault home-* (nouveau is_home_vault()).
- Filtres « * » en dur remplacés par check_vault_access : dashboard,
  conflits, liens retour, favoris, abonnements push.
- /api/search : search_vaults(is_allowed=…) filtre les bruts avant
  pagination (total et page restent justes).
- backend/user_home.py : _grant n'écarte plus les comptes « * » —
  l'admin reçoit son propre home-admin (auto-réparé au démarrage).
- Tests : test_user_home.py +2, assertion API inversée dans
  test_auth_api.py (admin ne voit plus home-alice).
2026-10-10 16:42:00 -04:00
bruno 10453d8dfe feat: dossier perso (#194) — vaults home-* en tête de l'arborescence, icône maison, compose de test
CI / lint (push) Successful in 3m1s
CI / security (push) Successful in 1m41s
CI / test (push) Successful in 4m37s
CI / build (push) Successful in 1m34s
CI / e2e (push) Successful in 17m40s
- frontend/js/sidebar.js : tri stable _homeFirst appliqué à la source des
  deux listes de vaults (loadVaults + soft refresh) → les vaults home-<user>
  passent en premier, arborescence et sélecteurs cohérents.
- frontend/js/viewer.js : getVaultIcon renvoie l'icône lucide "home" pour
  home-* (même safeCreateIcons / stroke currentColor que les autres).
- docker-compose.test-win.yml : montage test_home:/vaults/Home +
  OBSIGATE_HOME_ROOT=/vaults/Home pour tester la feature en local ;
  test_home/ ajouté au .gitignore.
- Tests : sidebar-filters.test.mjs +2 (icône maison, gemme violette
  inchangée), vérifié live sur l'instance 2020 (ordre + svg.lucide-home).
2026-10-10 16:09:59 -04:00
bruno 8235d632b8 feat: dossier personnel par utilisateur — vault home-<user> + registre de vaults persistant #194
CI / lint (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / security (push) Canceled after 0s
CI / build (push) Canceled after 0s
CI / e2e (push) Canceled after 0s
- backend/user_home.py : ensure_user_home()/release_user_home() idempotents
  (mkdir, vault, watcher, octroi dans user.vaults), rappelés à la création
  d'un compte et au démarrage (migration + réparation + sweep des vaults
  home-* orphelins), dossier conservé à la suppression.
- backend/indexer.py : data/vaults.json, registre persistant des vaults
  ajoutés à la volée, fusionné par load_vault_config() — ils disparaissaient
  au premier reindex complet et au redémarrage. reload_single_vault remet
  vault_config[name] après remove_vault_from_index.
- backend/auth/router.py + routers/vaults.py : appels ensure/release +
  persistance à l'ajout/retrait d'un vault.
- docker-compose.yml : montage unique /NFS/OBSIDIAN_DOC/Home:/vaults/Home +
  OBSIGATE_HOME_ROOT (absente = fonctionnalité inactive).
- Tests : tests/test_user_home.py (12) + tests/test_auth_api.py::TestUserHome (2).
2026-10-10 00:40:35 -04:00
bruno f4c8504c8d feat: chat — les posts affichent le markdown rendu comme un document, code coloré #193
CI / lint (push) Successful in 2m50s
CI / security (push) Successful in 1m37s
CI / test (push) Successful in 4m34s
CI / build (push) Successful in 1m31s
CI / e2e (push) Successful in 17m36s
2026-10-09 15:48:04 -04:00
bruno 54caa48d1f feat: chat — saisie auto-agrandissante, accusé de réception ✓✓, vignette de tuile #192 (BUG-109)
CI / lint (push) Successful in 2m52s
CI / security (push) Successful in 1m35s
CI / test (push) Successful in 4m27s
CI / build (push) Successful in 1m29s
CI / e2e (push) Successful in 17m1s
2026-10-08 23:28:08 -04:00
bruno f00a8bea8f feat: chat — suppression de post, messages privés, boîte compacte, link preview #191
CI / lint (push) Successful in 2m50s
CI / security (push) Successful in 1m35s
CI / test (push) Successful in 4m28s
CI / build (push) Successful in 1m28s
CI / e2e (push) Successful in 17m11s
2026-10-08 22:33:29 -04:00
bruno ae06436f91 feat: chat — suivi du document, chat général en onglet sidebar, onglets au-dessus du filtre #190
CI / lint (push) Successful in 2m48s
CI / security (push) Successful in 1m34s
CI / test (push) Successful in 4m33s
CI / build (push) Successful in 1m30s
CI / e2e (push) Successful in 17m5s
- **A — Suivi du document** : l'en-tête du panneau chat affiche le document
  ciblé (titre + chemin) et `followFileChat()` appelé par `renderFile()`
  re-cible le panneau ouvert à chaque changement de document.
- **B — Chat général** : onglet « Chat » (dernier) dans la sidebar,
  conversation centrale stockée dans le store #169 via les sentinelles
  `__global__/general` (`GET/POST /api/chat`) ; pastille de messages non
  lus sur l'onglet ; pièces jointes image/vidéo (`POST /api/chat/upload` :
  allow-list d'extensions, 25 MB, nom UUID ; `GET /api/chat/attachment/{name}`
  résolu contre l'allow-list) ; URL cliquables au rendu ; date/heure d'envoi.
- **C — Ordre des onglets** : la barre de filtre passe sous les onglets et
  sert de recherche dans le chat (texte + auteur) quand l'onglet Chat est
  actif (`switchSidebarTab` + `routeFilter`).
- Transport : broadcast SSE `chat_message` réutilisé (`vault __global__`
  route vers la sidebar, sinon le panneau fichier) — pas de 2ᵉ WebSocket.
- i18n FR/EN (6 clés), CSS bloc #190, `initSidebarChat()` dans l'orchestrateur.
- Tests : pytest 27 chat (12 nouveaux) ; JSDOM `filechat.test.mjs` 11 (5
  nouveaux) ; suite 1655 passed, ruff/mypy 0, validate-imports 42 modules.
- Docs : CHANGELOG [Unreleased], ROADMAP #190 → index, fiche
  `file-chat-169.md` §#190, guide « Chat général », journal des interventions.
2026-10-08 20:40:54 -04:00
bruno c94f065f80 feat: chat intégré par fichier — panneau latéral, historique, temps réel SSE #169
CI / lint (push) Successful in 2m46s
CI / security (push) Successful in 1m35s
CI / test (push) Successful in 4m28s
CI / build (push) Successful in 1m27s
CI / e2e (push) Successful in 17m11s
- Store backend `backend/file_chat.py` : messages JSON par (vault, path)
  sous `data/chats/` (nom hashé SHA-256 → traversal impossible), plafond
  500 messages, texte tronqué à 4000 caractères, écriture atomique.
- Routes `GET/POST /api/file/{vault}/chat` : auth + accès vault +
  `resolve_safe_path`, schémas Pydantic (`response_model`), broadcast SSE
  `chat_message` sur le transport existant (#62) — pas de second WebSocket.
- Panneau latéral `frontend/js/filechat.js` : bouton 💬 dans la toolbar
  fichier, historique chronologique, envoi optimiste + dédoublonnage par id,
  toast « Nouveau message » si le panneau est fermé/autre fichier.
- Relais SSE dans `sync.js` (import dynamique), CSS bloc #169 (plein écran
  ≤ 768 px, input 16 px anti-zoom), i18n FR/EN (10 clés `chat.*`).
- Tests : `tests/test_file_chat.py` (15) + `tests/frontend/filechat.test.mjs`
  (6, ajouté au pipeline CI), regex toolbar-order mise à jour.
- Docs : CHANGELOG [Unreleased], ROADMAP #169 → livré + index, fiche
  `docs/features/file-chat-169.md`, guide « Discuter d'un fichier ».
2026-10-08 17:03:29 -04:00
bruno 36b4962efb feat: tableur mobile — barres repliées par défaut, bascule unique #189
CI / lint (push) Successful in 2m42s
CI / security (push) Successful in 1m38s
CI / test (push) Successful in 4m19s
CI / build (push) Successful in 1m27s
CI / e2e (push) Successful in 17m12s
Sous 768 px, la barre de menus, le ruban et le groupe Recherche sont
repliés par défaut : la barre de formule (fx), les onglets et Enregistrer
restent visibles et la grille récupère la hauteur d'écran. Un bouton
unique (#xlsx-tools-toggle) dans la barre des feuilles déplie/replie
l'ensemble (aria-expanded, état actif) ; desktop inchangé.

Ergonomie tactile : cibles ≥ 44 px, onglets en défilement horizontal,
champs de la barre de formule en 16 px (anti-zoom iOS), barre de formule
en 2 lignes quand les outils sont ouverts.

Tests : JSDOM xlsx-viewer.test.mjs (+2, 165/165), E2E mobile (repli par
défaut, dépliage, no-overflow ruban, cibles 44 px) ; openXlsx() déplie
automatiquement ≤ 768 px pour les tests desktop.
2026-10-08 15:45:39 -04:00
bruno e01e837a2a feat: secrets masqués — couverture universelle clés API/mots de passe + clic pour copier (#188)
CI / lint (push) Successful in 2m40s
CI / security (push) Successful in 1m33s
CI / test (push) Successful in 4m19s
CI / build (push) Successful in 1m31s
CI / e2e (push) Successful in 16m56s
2026-10-08 13:22:27 -04:00
bruno 634ba8a272 feat: éditeur tableur — grille A → Z par blocs, barre de menus Sheets, panneau de couleurs, création .xlsx (#180 → #186)
CI / lint (push) Successful in 2m43s
CI / security (push) Successful in 1m33s
CI / test (push) Canceled after 0s
CI / build (push) Canceled after 0s
CI / e2e (push) Canceled after 0s
Restaure et termine le lot tableur resté non committé. Il n'existait que dans un
`git stash` (24 fichiers suivis) et en fichiers non suivis (menus.js, formula.js,
color-picker.js, tests, fiches) : la 2.53.3 livrée ne le contenait donc pas. Le
stash, créé avant le commit BUG-108, n'a jamais été restauré.

#180 Redimensionnement au curseur (en-tête de bord miroité + classe de repaint),
grille thémée sur les 15 thèmes et les modes contraste élevé / sépia, menu
Fichier au niveau des onglets, barre épinglée pleine largeur.

#181 Modèle de saisie Google Sheets : sélection ≠ édition (caret masqué sans
quitter contenteditable, double-clic / F2 / première frappe qui remplace,
Entrée contextuelle, Échap qui restaure) + inventaire priorisé des écarts.

#182 Désélection fiable (clic simple sans dépendre du focus), contours de plage
non empilés (sélecteur de classes sans point), couleurs texte/fond sur une
plage, clic droit qui préserve la sélection multiple.

#183 Panneau de couleurs façon Google Sheets : palette 8 × 10, STANDARD,
PERSONNALISÉ, coche selon la luminance, sortie `#rrggbb` (une valeur HSL était
rejetée par normHex).

#184 Peinture de format complète (toutes propriétés, source sans format =
réinitialisation), sélection multi-lignes/colonnes depuis les marges, grille
étendue : colonnes A → Z d'emblée, lignes ajoutées PAR BLOCS DE 100 au
défilement jusqu'à 1000 — matérialiser 1000 lignes d'un coup = ~26 000 cellules
câblées par feuille, ce qui épuisait le tas de la suite JSDOM ; index de
cellules `ref → td`, court-circuits formule/styles, marqueur data-wired.

#185 Barre de menus : les 10 menus Google Sheets (161 entrées, 125 câblées, 36
annoncées indisponibles), ruban façon Sheets, grille unie 1 px dérivée du thème.

#186 « Créer un fichier » propose .xlsx et construit un vrai classeur OPC
(openpyxl) au lieu d'une charge utile texte illisible.

Corrections trouvées en restaurant et en exerçant le lot :
- fuite mémoire : écouteur `click` anonyme posé sur #content-area à chaque
  rendu, jamais retiré — sa fermeture retenait la grille précédente en entier ;
- sorties Markdown / HTML / Imprimer de la barre de menus inertes
  (`data-xlsx-export` jamais réparti, seul `data-xlsx-action` l'était) ;
- collision de classe `.xlsx-structure-menu` entre la barre de menus et le menu
  Structure de la barre d'outils (toute requête tombait sur un nœud masqué) ;
- curseur col/row-resize absent quand le pointeur est sur la table elle-même ;
- l'export emportait les lignes et colonnes vides du quadrillage (CSV, Markdown,
  HTML, impression) ;
- clic extérieur avalé par la grâce de 250 ms du menu contextuel (destinée au
  seul appui long tactile) ;
- une entrée indisponible laissait la barre de menus ouverte.

Tests : pytest 1595 passés / 2 ignorés ; ruff et mypy 0 erreur ; bandit 0 ;
33 suites frontend vertes (xlsx-viewer 163/163, xlsx-menus 19/19,
xlsx-formula 14/14) ; E2E complet 132 passés / 12 ignorés ; E2E xlsx-viewer
19/19.
2026-10-08 12:00:15 -04:00
bruno d79202e698 feat: menus tableur a icones, fermeture au focus et polish mobile #179
CI / lint (push) Successful in 2m52s
CI / security (push) Successful in 2m11s
CI / test (push) Successful in 4m48s
CI / build (push) Successful in 2m0s
CI / e2e (push) Failing after 16m50s
2026-10-04 19:27:50 -04:00
bruno 4ce677902a feat: agent IA phase 4 — doublons #166, notifications externes #168 (Discord/Telegram/SMTP/webhook), taches planifiees #170
CI / lint (push) Successful in 2m53s
CI / security (push) Successful in 2m5s
CI / test (push) Successful in 4m52s
CI / build (push) Successful in 2m5s
CI / e2e (push) Successful in 16m14s
2026-10-04 13:11:25 -04:00
bruno fe9f7b49f7 feat: premier lancement ObsiGate + section Configuration harmonisée (#160)
CI / lint (push) Successful in 2m50s
CI / security (push) Successful in 2m1s
CI / test (push) Successful in 4m44s
CI / build (push) Successful in 1m54s
CI / e2e (push) Successful in 16m25s
- Premier lancement : %USERPROFILE%\ObsiGate créé et monté comme vault de
  démarrage « ObsiGate » (remplace voute_obsidian), vault_path dessus,
  racine home nommée d'après son dernier segment (fin du « bruno » codé en
  dur), champs de fenêtre préservés.
- Document « Prise en main.md » embarqué dans le binaire
  (include_str!) et écrit dans ce répertoire si absent — jamais écrasé.
- Section « 🖥️ Vaults & dossiers (Desktop) » refondue : markup à classes
  (zéro style inline), bloc CSS desktop-roots-* sur variables (motif
  webauthn-key-item), boutons .config-btn-sm primaire/secondaire,
  cibles tactiles 44px en mobile.
- Tests : test_default_first_run_config, test_welcome_doc_embedded
  (cargo test 28 passed), suites frontend vertes, E2E 123/123.
2026-10-03 10:02:16 -04:00
bruno d2734dc8ce feat: gestion desktop des vaults & dossiers — retrait par menu contextuel et ajout en configuration #159
CI / lint (push) Successful in 3m2s
CI / security (push) Successful in 2m2s
CI / test (push) Successful in 4m42s
CI / build (push) Successful in 1m52s
CI / e2e (push) Successful in 15m54s
2026-10-02 21:04:17 -04:00
bruno 2add24a9c1 feat: onglet Accueil, menus contextuels de navigation et racine vault #158
CI / lint (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / security (push) Canceled after 0s
CI / build (push) Canceled after 0s
CI / e2e (push) Canceled after 0s
- clic sur le titre: la page d'accueil s'ouvre dans un onglet Accueil (icone Maison) epingle en tete de barre, reactivable d'un clic (onglet propre au panneau actif en split)
- page de navigation: menus contextuels des repertoires et des fichiers identiques a ceux de la sidebar, clic sur la racine d'un vault qui ouvre la vue, icone dossier a la couleur de l'arbre de navigation
- BUG-101: bloc « Keyboard shortcuts for tabs » duplique dans ui.js (un seul Ctrl+W fermait deux onglets)
- BUG-102: le menu contextuel se refermait 10 ms apres son ouverture (scroll de reflow des icones lucide)
2026-10-02 14:05:01 -04:00
bruno a50227849d feat: onglet de navigation avec facettes, tris et retour Home complet #158
CI / lint (push) Successful in 2m46s
CI / security (push) Successful in 1m57s
CI / build (push) Canceled after 0s
CI / e2e (push) Canceled after 0s
CI / test (push) Canceled after 1h50m30s
- clic sur un repertoire de l'arbre (modes focus vault et All) ouvre un onglet de navigation dedie qui coexiste avec les onglets de fichiers ouverts
- vue: sous-repertoires cliquables, facettes Vaults/Tags/Extensions (meme mecanisme que la recherche), tris Pertinence/Date, bouton Sauver; tags indexes exposes par GET /api/vault/{v}/files
- BUG-100: showWelcome re-injecte la section Raccourcis & Astuces et goHome efface la recherche globale + le filtre de la sidebar
2026-10-02 12:27:27 -04:00
bruno c5c225a68e 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
2026-09-30 07:04:47 -04:00
bruno 383ffa6a65 feat: menu contextuel et selection type Excel dans la grille XLSX #155
CI / lint (push) Successful in 2m31s
CI / security (push) Failing after 1m46s
CI / test (push) Successful in 4m20s
CI / build (push) Successful in 1m41s
CI / e2e (push) Successful in 15m30s
2026-09-29 16:43:39 -04:00
bruno 3f52b56251 refactor: extraction des modules xlsx, lien dashboard-grille et inspecteur redimensionnable #154 2026-09-29 15:25:38 -04:00
bruno 72da123a51 feat: undo/redo, chargement IntersectionObserver et ARIA pour la visionneuse XLSX #154
CI / lint (push) Successful in 2m30s
CI / security (push) Failing after 1m45s
CI / build (push) Canceled after 0s
CI / e2e (push) Canceled after 0s
CI / test (push) Canceled after 19s
2026-09-29 15:01:00 -04:00
bruno e94af0369b feat: inspecteur droit pour le tableau de bord XLSX #154
CI / lint (push) Successful in 2m30s
CI / security (push) Failing after 1m46s
CI / build (push) Canceled after 0s
CI / e2e (push) Canceled after 0s
CI / test (push) Canceled after 1h32m51s
2026-09-29 14:56:08 -04:00
bruno 8da65611cb feat: dialogues themes et conflits non bloquants pour la visionneuse XLSX #154
CI / lint (push) Successful in 2m31s
CI / security (push) Failing after 1m46s
CI / test (push) Successful in 4m17s
CI / build (push) Successful in 1m40s
CI / e2e (push) Canceled after 42s
2026-09-29 14:50:47 -04:00
bruno 605060c51d feat: visionneuse XLSX - ruban groupe, onglets permanents et badges d'etat #154
CI / lint (push) Successful in 2m30s
CI / security (push) Failing after 1m45s
CI / test (push) Successful in 4m17s
CI / build (push) Successful in 1m40s
CI / e2e (push) Successful in 15m10s
2026-09-29 14:30:36 -04:00
bruno 99779ecc08 docs: cloture documentaire #153 - changelog A6-A17, fiche, guide utilisateur et README
CI / lint (push) Successful in 2m33s
CI / security (push) Failing after 1m41s
CI / test (push) Failing after 3h11m57s
CI / build (push) Skipped
CI / e2e (push) Skipped
2026-09-28 17:48:43 -04:00
bruno 472ea9d309 feat: chargement à la demande des lignes cachées des tableurs #153
CI / lint (push) Successful in 2m24s
CI / security (push) Failing after 1m39s
CI / test (push) Successful in 4m0s
CI / build (push) Successful in 1m37s
CI / e2e (push) Successful in 15m34s
A9bis : sous une feuille tronquée, un pied de page « N lignes affichées
sur M · Charger la suite » fetch la fenêtre suivante (limit=500) au clic
ou à l'approche du bas du tableau (sentinelle de défilement, marge 120px).
Les lignes ajoutées passent par le même pipeline d'édition que le rendu
initial (setupCell factorisé) : éditables et sauvegardables immédiatement.
Fetch échoué → bouton restauré (retry) + toast ; feuille complète → pied
de page masqué (class done).

Vérifié : xlsx-viewer.test.mjs 19/19 (5 nouveaux, contre-preuve
wireLazyRows désactivé → 5 échecs), E2E 8/8 (A520 visible et éditable
après clic), validate-imports 40 modules, unit.test.mjs 12/12, i18n parity.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>
2026-09-28 11:52:42 -04:00
bruno 6ba04c4381 feat: troncature annoncee et lecture par fenetres des tableurs #153
CI / lint (push) Successful in 2m29s
CI / security (push) Failing after 1m39s
CI / test (push) Successful in 4m1s
CI / build (push) Successful in 1m37s
CI / e2e (push) Successful in 14m47s
- BUG-090 (#153 A8) : render_sheets() expose total_rows/total_cols,
  max_rows/max_cols et truncated ; la visionneuse affiche un bandeau
  « Feuille tronquée » (i18n FR/EN) au lieu de couper en silence, et la
  ligne d'en-têtes devient sticky (top:auto sur les numéros de ligne).
- #153 A9 : GET /api/file/{vault}/xlsx/sheet?sheet&offset&limit sert une
  fenêtre de 1 à 1000 lignes avec les vraies coordonnées A1, has_more de
  pagination et valeurs calculées A12 ; 404 feuille inconnue, 415 non-xlsx.
- Tests : TestXlsxTruncationNotice (4) + TestXlsxSheetWindow (11) avec
  contre-preuves, xlsx-viewer.test.mjs 14/14, E2E 7/7 (fixture
  sample-xlsx-large.xlsx 520 lignes), suite 1417 passed / 6 skipped,
  ruff/mypy 0, i18n parity.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>
2026-09-28 10:38:24 -04:00
bruno 06f8e63d06 feat: tableurs indexables, saisie typée et valeurs calculées #153
Les tableurs étaient invisibles à la recherche, chaque saisie devenait
du texte, et une formule n'affichait que sa formule.

- A5 : extract_indexable_text() indexe les noms de feuilles et les 20
  premières lignes (plafond 5 k caractères) pour le TF-IDF et la
  recherche sémantique. Un mot tapé dans une cellule rend le fichier
  trouvable ; un classeur chiffré s'indexe par son seul nom.
- A10 : la saisie est typée comme dans Excel — booléens
  (TRUE/FAUX/OUI/NON) et dates FR JJ/MM/AAAA en ordre jour-first, donc
  01/02/2026 est le 1er février. Une saisie ressemblant à une formule
  n'est jamais convertie (BUG-088 préservé).
- A12 : la valeur calculée par Excel s'affiche sous la formule, via une
  2e lecture data_only=True faite seulement si l'archive contient un
  <v>. Info-bulle traduite FR/EN, aucun texte d'interface côté backend.
- BUG-089 : un reindex manuel ne reconstruisait pas l'index inversé, et
  backend/search.py lisait l'index via un import par valeur — après un
  rechargement du module, la recherche écrivait dans un dict périmé.

Contre-preuves vérifiées pour A5, A10 et A12 (neutralisation de chaque
fonction → échec des tests concernés). Tests : 1402 pytest, 10 JSDOM,
4 E2E, suite E2E complète verte, ruff/mypy 0.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>
2026-09-27 22:43:24 -04:00
bruno 31d4616baf feat: garde-fous d'écriture des classeurs Excel #153 (P0)
L'édition d'un .xlsx pouvait détruire une partie du classeur, le
concurrencer en silence, ou diffuser une injection de formule.

- BUG-085 : inspect_workbook() détecte ce qu'un round-trip openpyxl perd
  (valeurs calculées en cache, slicers, contrôles, connexions, custom
  XML, signature, commentaires enrichis, macros) → xlsx_lossy_features
  exposé en lecture, bandeau FR/EN, et 409 xlsx_lossy_content sans
  `force` (confirmation explicite puis reprise). Périmètre réel
  revalidé : graphiques, images et TCD survivent au round-trip.
- BUG-086 : écriture atomique (fichier .tmp + os.replace) : un plantage
  ne peut plus tronquer le classeur, le backup reste intact.
- BUG-087 : verrou par fichier autour du read-modify-write (timeout 15 s,
  409 conflict) ; endpoint xlsx/save devenu synchrone pour que
  l'attente s'exécute dans le threadpool.
- BUG-088 : une saisie en '=' ou '@' est stockée en texte, sauf opt-in
  `allow_formula` ou le bouton f(x) de la visionneuse. Le handler
  ServiceError expose désormais code + details, que api() propage.
- BUG-084 : la suppression d'une vault purge enfin l'index inversé
  (documents fantômes qui continuaient de matcher) et is_stale() devient
  is_ready(), le nom étant trompeur (la staleness n'existe plus).

Tests : 1390 pytest, 10 JSDOM (xlsx-viewer.test.mjs, branché au CI),
3 E2E Playwright, suite E2E complète verte, ruff/mypy 0.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>
2026-09-27 20:39:12 -04:00
bruno d6cca2b1af feat: #85 T10 persistance etat (verrous stores, ratelimit SQLite) et cloture refonte 2026-09-26 18:10:10 -04:00
bruno 943005328c feat: barre d'outils de lecture epinglee, coloration syntaxique des fichiers de code et avatars predefinis (#115, #117, BUG-078)
CI / lint (push) Successful in 2m5s
CI / security (push) Successful in 1m27s
CI / test (push) Successful in 4m10s
CI / build (push) Successful in 1m23s
CI / e2e (push) Successful in 13m55s
2026-09-24 16:21:45 -04:00
bruno e1842043d8 feat: assistant IA — approbation groupee des actions, bloc d'etapes, refresh UI et bouton Stop (BUG-074, BUG-075, BUG-076, BUG-077)
CI / lint (push) Successful in 2m1s
CI / security (push) Successful in 1m25s
CI / test (push) Successful in 4m19s
CI / build (push) Successful in 1m26s
CI / e2e (push) Successful in 13m38s
2026-09-24 10:05:32 -04:00
bruno 9fb094f505 feat: refonte mobile de la section Configurations #114
CI / lint (push) Successful in 2m1s
CI / security (push) Successful in 1m26s
CI / test (push) Successful in 4m50s
CI / build (push) Successful in 1m22s
CI / e2e (push) Successful in 15m34s
2026-09-24 08:43:18 -04:00
bruno 33fe1a3439 feat: ordre naturel des sections Configurations et avatar utilisateur #113
CI / lint (push) Successful in 1m59s
CI / security (push) Successful in 1m28s
CI / test (push) Successful in 4m14s
CI / build (push) Successful in 1m20s
CI / e2e (push) Successful in 13m43s
2026-09-23 22:18:29 -04:00
bruno a726ad8511 feat: en-tete allege, compte utilisateur en sidebar et pellicule d'images defilable (molette + fleches) #112
CI / lint (push) Successful in 1m59s
CI / security (push) Successful in 1m29s
CI / test (push) Successful in 4m59s
CI / build (push) Successful in 1m48s
CI / e2e (push) Successful in 13m49s
2026-09-23 21:06:41 -04:00
bruno b926f01b85 feat: visionneuse d'images - navigation fluide, image ajustee au cadre, fleches laterales et pellicule persistante #111
CI / lint (push) Successful in 2m2s
CI / security (push) Successful in 1m23s
CI / test (push) Successful in 4m23s
CI / build (push) Successful in 1m19s
CI / e2e (push) Successful in 13m27s
2026-09-23 14:37:41 -04:00
bruno 80852374a8 fix: positionnement lecteur media (mobile/desktop) et drag libre de la mini-video #110
CI / lint (push) Successful in 2m0s
CI / security (push) Successful in 1m22s
CI / test (push) Successful in 4m36s
CI / build (push) Successful in 1m18s
CI / e2e (push) Successful in 13m7s
2026-09-23 11:42:23 -04:00
bruno e3c6789776 feat: lecteur média persistant Now Playing (dock audio, mini-vidéo PiP, Media Session, mobile) #110
CI / lint (push) Successful in 2m5s
CI / security (push) Successful in 1m23s
CI / test (push) Successful in 3m57s
CI / build (push) Successful in 1m18s
CI / e2e (push) Successful in 13m18s
2026-09-23 10:41:15 -04:00
bruno e2417cb5ab feat: support audio & vidéo — lecteurs HTML5 intégrés #109
CI / lint (push) Successful in 1m58s
CI / security (push) Successful in 1m22s
CI / test (push) Successful in 4m32s
CI / build (push) Successful in 2m6s
CI / e2e (push) Successful in 12m57s
2026-09-23 09:14:34 -04:00
bruno eccbf7474e feat: support complet des images — arborescence, visionneuse, indexation #108
CI / lint (push) Successful in 1m57s
CI / security (push) Successful in 1m22s
CI / test (push) Successful in 4m32s
CI / build (push) Failing after 1m16s
CI / e2e (push) Skipped
2026-09-23 07:47:04 -04:00
bruno 705f755b6b docs: guides d'utilisation, capture reelle et README ameliores
CI / lint (push) Successful in 2m2s
CI / security (push) Successful in 1m25s
CI / test (push) Successful in 4m13s
CI / build (push) Successful in 1m16s
CI / e2e (push) Successful in 11m56s
2026-09-22 22:40:51 -04:00
bruno f621620593 feat: optimisation globale des performances #86 (scan differentiel, excalidraw differe, garde-fou replace; inverted index/PDF lazy/caps regex deja livres via BUG-033/040/025)
CI / lint (push) Successful in 1m54s
CI / security (push) Successful in 1m22s
CI / test (push) Successful in 4m23s
CI / build (push) Successful in 1m17s
CI / e2e (push) Successful in 12m9s
2026-09-22 19:04:26 -04:00
bruno eff74cabe0 feat: #107 configuration - gestion des clés API & MCP (création/révocation, expiration 1j/1mois/6mois/1an/sans fin, une clé pour API REST + serveur MCP, dernière utilisation, store sans secret persisté; fix révocation longue durée) + script token MCP
CI / lint (push) Successful in 1m58s
CI / security (push) Successful in 1m32s
CI / test (push) Successful in 4m0s
CI / build (push) Successful in 1m15s
CI / e2e (push) Successful in 12m9s
2026-09-22 14:48:51 -04:00
bruno ab7c227b97 feat: assistant IA - actions instantanées contextuelles, catalogue Toutes les actions & frontmatter complet #106
CI / lint (push) Successful in 1m43s
CI / security (push) Successful in 1m8s
CI / test (push) Successful in 4m4s
CI / build (push) Successful in 2m10s
CI / e2e (push) Successful in 11m51s
2026-09-19 11:07:50 -04:00
bruno b8054665bc fix: guide - bouton telechargement icone seule, diagramme Architecture rendu en image dans le PDF, emoji couleur (Noto) (#105)
CI / lint (push) Successful in 1m39s
CI / security (push) Successful in 1m5s
CI / test (push) Successful in 3m25s
CI / build (push) Successful in 1m18s
CI / e2e (push) Successful in 11m43s
2026-09-18 15:01:14 -04:00
bruno fb2d83e9e3 feat: guide d'utilisation - couverture complete, telechargement MD/PDF, section Architecture Mermaid, guide desktop elargi (#105, BUG-067)
CI / lint (push) Successful in 1m40s
CI / security (push) Successful in 1m6s
CI / test (push) Failing after 1m52s
CI / build (push) Skipped
CI / e2e (push) Skipped
2026-09-18 13:06:30 -04:00