From 9f00455064c41751791dd2737754d7903e4d7192 Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Tue, 2 Jun 2026 15:25:08 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20guide=20d'utilisation=20enrichi=20+=20n?= =?UTF-8?q?avigation=20clavier=20=E2=80=94=20sections=20Tabs,=20=C3=89diti?= =?UTF-8?q?on,=20Graphe,=20Palette,=20Partage,=20Sauvegardes,=20S=C3=A9cur?= =?UTF-8?q?it=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ROADMAP.md | 6 +- frontend/index.html | 238 +++++++++++++++++++++++++++++++++++++++--- frontend/js/config.js | 55 +++++++++- 3 files changed, 282 insertions(+), 17 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index f5ef822..ec8e67c 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -79,7 +79,7 @@ - Export dans la vue publique `/s/{token}/pdf` ✅ - GTK/Pango installé dans le Dockerfile ✅ -### ✅ 3. Palette de commandes (Ctrl+P) — FAIT +### ✅ 3. Palette de commandes (Ctrl+ALT+SPACE) — FAIT - Navigation rapide fichiers + commandes (> prefix) ✅ - Module `frontend/js/palette.js` + intégré dans app.js ✅ @@ -107,6 +107,10 @@ - **Effort** : 0.5 jour | **Impact** : 🟢 - `indexer.py` et `watcher.py` ont des définitions séparées → mutualiser +### 10. Indicateur AI Actif +- **Effort** : 0.5 jour | **Impact** : 🟡 +- Dans le bouton/indicateur à droite du titre, en cliquant il y a un panneau avec les statut de syncro. Ajouter un indicateur visuel dans ce panneau pour indiquer si l'IA est actif, configuré, et fonctionnelle. Durant la phase de traitement de l'IA, le bouton/indicateur devrait changer de couleur pour un blanc qui flashindiquant le statut de la tâche AI en cours. + --- ## 📋 Backlog (P3/P4 — Long terme) diff --git a/frontend/index.html b/frontend/index.html index 1b24b42..d71d476 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -773,16 +773,23 @@
@@ -891,6 +898,35 @@ +
+

📑 Gestion des onglets

+

ObsiGate supporte l'ouverture de plusieurs fichiers simultanément dans des onglets.

+ +

Ouverture d'onglets

+
    +
  • Simple clic : Ouvre le fichier dans un onglet temporaire (aperçu, italique)
  • +
  • Double clic : Ouvre le fichier dans un onglet persistant
  • +
  • Navigation récente : L'onglet "Récent" dans la sidebar liste vos derniers fichiers
  • +
+ +

Navigation entre onglets

+
    +
  • Clic sur un onglet : Active l'onglet
  • +
  • Ctrl+Tab : Onglet suivant
  • +
  • Ctrl+Shift+Tab : Onglet précédent
  • +
  • Ctrl+W : Fermer l'onglet actif
  • +
  • Bouton ✕ : Ferme l'onglet (sauf s'il est seul)
  • +
+ +

Fonctionnalités

+
    +
  • Barre d'onglets : Affichage horizontal avec le nom du fichier et son icône
  • +
  • Onglet actif : Surligné en couleur d'accentuation
  • +
  • Prévisualisation : Un onglet temporaire (italique) est remplacé par le prochain clic
  • +
  • Onglets persistants : Restent ouverts jusqu'à fermeture manuelle
  • +
+
+

🔍 Recherche

@@ -1017,6 +1053,59 @@
+ +
+

✏️ Édition avancée

+ +

Éditeur CodeMirror 6

+

L'éditeur intégré offre une expérience complète d'édition :

+
    +
  • Coloration syntaxique : Python, JS, HTML, CSS, Java, C++, Rust, Go et bien d'autres
  • +
  • Numéros de ligne : Navigation facilitée dans les fichiers longs
  • +
  • Find in page : Ctrl+F pour chercher dans le fichier (casse, regex, mot entier)
  • +
  • Sauvegarde automatique : Auto-save silencieux après 2s d'inactivité, flash vert de confirmation
  • +
  • Bouton ✓ : Sauvegarde manuelle
  • +
  • Bouton 🗑️ : Suppression du fichier
  • +
  • Barre d'outils AI : Complétion, réécriture, traduction (voir section IA)
  • +
+
+ + +
+

🗺️ Vue Graphe

+

La vue graphe offre une visualisation interactive des connexions entre vos notes via les wikilinks.

+ +

Ouverture

+
    +
  • Menu contextuel : Clic droit sur un vault ou dossier → Vue Graphique
  • +
  • Bouton dédié : Dans la barre d'outils (le cas échéant)
  • +
+ +

Interaction

+
    +
  • Zoom/Pan : Molette pour zoomer, cliquer-glisser pour se déplacer
  • +
  • Slider de profondeur : Contrôle la profondeur d'exploration (0-3)
  • +
  • Filtre par tag : N'affiche que les nœuds avec un tag spécifique
  • +
  • Filtre par type : Inclut/exclut fichiers, dossiers, markdown, autres
  • +
  • Recherche visuelle : Barre de recherche pour surligner des nœuds
  • +
  • Mode focus : Centre la vue sur un nœud spécifique
  • +
  • Navigation ← → ↑ : Historique de navigation entre les nœuds
  • +
+ +

Affichage

+
    +
  • Nœuds : Fichiers (carrés) et dossiers (cercles) avec icônes
  • +
  • Arêtes : Wikilinks sortants (bleu) et backlinks entrants (vert)
  • +
  • Panneau d'info : Métadonnées du nœud (tags, liens, chemin)
  • +
  • Ctrl+survol : Aperçu du contenu de la note sans naviguer
  • +
  • Plein écran : Bouton pour agrandir la vue
  • +
  • Export PNG : Sauvegardez le graphe en image
  • +
+ +

Performance

+

La vue utilise l'algorithme Barnes-Hut pour calculer la disposition en O(n log n), garantissant des performances fluides même avec des centaines de nœuds.

+
+

🎨 Personnalisation

@@ -1049,8 +1138,48 @@
-
-

⌨️ Raccourcis clavier

+ +
+

⌨️ Palette de commandes

+

La palette de commandes permet d'accéder rapidement aux fichiers et aux actions sans utiliser la souris.

+ +

Ouverture

+
    +
  • Ctrl+Shift+Espace : Palette de fichiers (recherche par titre)
  • +
  • Ctrl+Alt+Espace : Palette de commandes (liste d'actions)
  • +
  • Tab : Bascule entre mode fichiers et mode commandes
  • +
+ +

Mode fichiers

+

Tapez le nom d'un fichier pour le trouver rapidement. La recherche utilise l'API /api/suggest pour des suggestions instantanées. Appuyez sur Enter pour ouvrir le fichier sélectionné.

+ +

Mode commandes

+

Tapez > (ou Ctrl+Alt+Espace) pour voir la liste des actions disponibles :

+
    +
  • 🏠 Accueil : Retour à l'accueil
  • +
  • ✏️ Éditer fichier courant : Ouvre l'éditeur sur l'onglet actif
  • +
  • 🪟 Pop-out : Ouvre le document dans une fenêtre détachée
  • +
  • 📄 Créer fichier markdown : Crée un nouveau fichier .md et l'ouvre dans l'éditeur
  • +
  • 🗑️ Supprimer fichier courant : Supprime le fichier de l'onglet actif
  • +
  • 📁 Créer répertoire : Crée un nouveau dossier
  • +
  • 🗑️ Supprimer répertoire : Supprime un dossier et son contenu
  • +
  • 🌓 Changer le thème : Bascule clair/sombre
  • +
  • 🔄 Réindexer : Force la réindexation complète
  • +
  • Aide : Ouvre ce guide
  • +
  • ⚙️ Configuration : Ouvre les paramètres
  • +
+ +

Navigation

+
    +
  • : Se déplacer dans la liste
  • +
  • Enter : Ouvrir/sélectionner
  • +
  • Esc : Fermer la palette
  • +
  • Tab : Basculer entre fichiers et commandes
  • +
+
+ +
+

⌨️ Raccourcis clavier

Navigation

    @@ -1069,6 +1198,16 @@

    Interface

    • Ctrl+T : Basculer le thème clair/sombre
    • +
    • Ctrl+Shift+Espace : Palette de fichiers
    • +
    • Ctrl+Alt+Espace : Palette de commandes
    • +
    • Escape : Fermer la palette / les suggestions
    • +
    + +

    Onglets

    +
      +
    • Ctrl+W : Fermer l'onglet actif
    • +
    • Ctrl+Tab : Onglet suivant
    • +
    • Ctrl+Shift+Tab : Onglet précédent

    Éditeur CodeMirror

    @@ -1154,8 +1293,77 @@

    Pour changer de provider par défaut : AI_DEFAULT_PROVIDER=openrouter

-
-

💡 Astuces et bonnes pratiques

+ +
+

🔗 Partage et Webhooks

+ +

Publication publique

+

ObsiGate permet de partager des documents via un lien public temporaire.

+
    +
  • Bouton Partager : Dans les actions du fichier, génère un lien unique
  • +
  • Token unique : 64 caractères hexadécimaux, impossible à deviner
  • +
  • Expiration configurable : Durée de vie en heures
  • +
  • Lecture seule : La vue publique n'affiche que le contenu sans édition
  • +
  • Gestion des liens : Liste et révocation dans les Configurations
  • +
  • Export PDF : Téléchargement du document partagé en PDF via /s/{token}/pdf
  • +
+ +

Webhooks

+

Les webhooks notifient des services externes lors des changements de fichiers.

+
    +
  • Événements : Création, modification, suppression, renommage
  • +
  • Format : POST JSON avec détails complets de l'événement
  • +
  • Signature HMAC-SHA256 : Optionnelle pour vérifier l'origine
  • +
  • Configuration : Gestion via le modal Configurations
  • +
+
+ + +
+

💾 Sauvegardes et Audits

+ +

Backup automatique

+

ObsiGate crée automatiquement des sauvegardes avant chaque modification destructive.

+
    +
  • Avant écriture : Sauvegarde du contenu original dans .obsigate-backup/
  • +
  • Timestamp : Chaque backup est horodaté
  • +
  • Répertoire dédié : .obsigate-backup/ dans le répertoire de la vault
  • +
+ +

Audit log

+

Toutes les opérations sensibles sont tracées dans un journal.

+
    +
  • Journal : Fichier data/audit.log au format JSON lines
  • +
  • Événements tracés : Écritures, suppressions, changements de configuration
  • +
  • Rotation : Rotation automatique à 10 Mo
  • +
+
+ + +
+

🛡️ Sécurité

+ +

Authentification

+
    +
  • JWT + Argon2id : Tokens JWT avec mots de passe hachés Argon2id
  • +
  • Contrôle d'accès par vault : Chaque utilisateur voit uniquement ses vaults
  • +
  • Refresh tokens : Cookies HttpOnly avec rotation automatique
  • +
  • Rate limiting : 10 tentatives / 15 min par IP + verrouillage compte
  • +
  • Compte admin : Créé automatiquement au premier démarrage
  • +
+ +

Protection

+
    +
  • Secret redactor : Masque JWT, clés API, tokens GitHub dans les aperçus
  • +
  • Path traversal : Validation des chemins contre les attaques ../
  • +
  • Headers CSP : Content-Security-Policy, X-Frame-Options, XSS-Protection
  • +
  • Atomic writes : Écriture via fichier temporaire + renommage atomique
  • +
  • Non-root Docker : Conteneur tourne avec UID 1000
  • +
+
+ +
+

💡 Astuces et bonnes pratiques

Workflow recommandé

    @@ -1195,7 +1403,7 @@
diff --git a/frontend/js/config.js b/frontend/js/config.js index 93f8817..2e1ec02 100644 --- a/frontend/js/config.js +++ b/frontend/js/config.js @@ -214,6 +214,7 @@ function initHelpNavigation() { const helpContent = document.querySelector(".help-content"); const helpBody = document.getElementById("help-body"); const navLinks = document.querySelectorAll(".help-nav-link"); + const navList = document.querySelector(".help-nav-list"); if (!helpContent || !helpBody || !navLinks.length) return; @@ -225,11 +226,63 @@ function initHelpNavigation() { const targetSection = document.getElementById(targetId); if (targetSection) { targetSection.scrollIntoView({ behavior: "smooth", block: "start" }); + // Focus the section for accessibility + targetSection.setAttribute("tabindex", "-1"); + targetSection.focus({ preventScroll: true }); } }); }); - // Scroll spy - update active nav link based on scroll position + // ── Keyboard navigation inside nav list ── + if (navList) { + navList.addEventListener("keydown", (e) => { + const items = [...navList.querySelectorAll(".help-nav-link")]; + const currentIdx = items.indexOf(document.activeElement); + + if (e.key === "ArrowDown" || e.key === "ArrowRight") { + e.preventDefault(); + const next = (currentIdx + 1) % items.length; + items[next].focus(); + } else if (e.key === "ArrowUp" || e.key === "ArrowLeft") { + e.preventDefault(); + const prev = (currentIdx - 1 + items.length) % items.length; + items[prev].focus(); + } else if (e.key === "Home") { + e.preventDefault(); + items[0].focus(); + } else if (e.key === "End") { + e.preventDefault(); + items[items.length - 1].focus(); + } + }); + + // Make nav items focusable + navList.querySelectorAll("li").forEach((li) => { + li.setAttribute("role", "none"); // remove listitem role for cleaner a11y + }); + } + + // ── Keyboard: Ctrl+↑/↓ inside help body to jump between sections ── + helpBody.addEventListener("keydown", (e) => { + if ((e.ctrlKey || e.metaKey) && (e.key === "ArrowDown" || e.key === "ArrowUp")) { + e.preventDefault(); + const sections = [...document.querySelectorAll(".help-section")]; + const currentSection = sections.find((s) => { + const rect = s.getBoundingClientRect(); + return rect.top >= 0 && rect.top < 100; + }); + if (!currentSection) return; + + const currentIdx = sections.indexOf(currentSection); + if (e.key === "ArrowDown" && currentIdx < sections.length - 1) { + sections[currentIdx + 1].scrollIntoView({ behavior: "smooth", block: "start" }); + } else if (e.key === "ArrowUp" && currentIdx > 0) { + sections[currentIdx - 1].scrollIntoView({ behavior: "smooth", block: "start" }); + } + } + }); + + // ── Scroll spy ── const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => {