# -*- coding: utf-8 -*- """Nouveau contenu du Guide d'utilisation (#105) — source unique de vérité. Rôles : 1. ``CONTENT`` : dictionnaire i18n clé -> (fr, en). Les locales FR/EN sont générées depuis ce dictionnaire par ``scripts/merge_guide_locales.py`` (ne jamais éditer les blocs ``guide105.*`` des JSON à la main). 2. Les constantes ``SECTION_*`` / ``EXTRA_BLOCKS`` / ``TOC_INSERT_BEFORE`` décrivent le HTML à insérer dans ``frontend/index.html`` par ``scripts/insert_guide_sections.py``. Le texte FR inline de chaque élément portant ``data-i18n="guide105.X"`` DOIT être identique à ``CONTENT[X][0]`` (sinon ``_applyDOM`` afficherait un texte incohérent quand la locale FR est appliquée). Règle i18n : tout élément textuel des nouvelles sections porte une clé ``guide105.*``. Les clés préexistantes ne sont jamais réutilisées avec un texte différent. """ # --------------------------------------------------------------------------- # Dictionnaire i18n (clé -> FR, EN) # --------------------------------------------------------------------------- CONTENT: dict[str, tuple[str, str]] = { # -- TOC / titres de sections --------------------------------------------- "nav_architecture": ("🏗️ Architecture", "🏗️ Architecture"), "nav_library": ("⭐ Bibliothèque", "⭐ Library"), "nav_diagrams": ("📊 Diagrammes", "📊 Diagrams"), "nav_offline": ("📴 Hors-ligne", "📴 Offline"), "nav_collab": ("👥 Collaboration", "👥 Collaboration"), "nav_desktop": ("🖥️ Desktop", "🖥️ Desktop"), "nav_api": ("🔌 API", "🔌 API"), "nav_languages": ("🌍 Multilingue", "🌍 Languages"), # -- Section Architecture --------------------------------------------------- "arch_intro": ( "ObsiGate est une application web complète construite en couches indépendantes, sans base" " de données externe : les notes vivent dans vos dossiers Obsidian, l'état applicatif dans" " des fichiers JSON de data/, l'index de recherche en mémoire.", "ObsiGate is a full web application built as independent layers, with no external" " database: notes live in your Obsidian folders, app state in JSON files under" " data/, the search index in memory.", ), "arch_diagram_note": ( "Le diagramme est interactif dans l'application : zoom, plein écran, copie SVG ou code.", "The diagram is interactive in the app: zoom, fullscreen, copy SVG or code.", ), "arch_h3_layers": ("Les grandes composantes", "The main components"), "arch_lbl_fe": ("Frontend", "Frontend"), "arch_fe": ( " — SPA en JavaScript vanilla (modules ES), sans framework ni build npm :" " frontend/js/ (~30 modules). Le CSS utilise des variables pour les thèmes.", " — vanilla-JavaScript SPA (ES modules), no framework, no npm build:" " frontend/js/ (~30 modules). CSS variables drive the themes.", ), "arch_lbl_be": ("Backend", "Backend"), "arch_be": ( " — serveur FastAPI (Python 3.11) : rendu markdown (mistune + wikilinks)," " recherche TF-IDF stemmisée (index inversé en mémoire), watchers watchdog, JWT +" " Argon2id, webhooks HMAC, export PDF (WeasyPrint).", " — FastAPI server (Python 3.11): markdown rendering (mistune + wikilinks), stemmed" " TF-IDF search (in-memory inverted index), watchdog file watchers, JWT + Argon2id," " HMAC webhooks, PDF export (WeasyPrint).", ), "arch_lbl_realtime": ("Temps réel & MCP", "Realtime & MCP"), "arch_rt": ( " — passerelle WebSocket (collaboration Yjs, notifications SSE/push) et serveur MCP" " (Streamable HTTP, /mcp) pour les clients externes.", " — WebSocket gateway (Yjs collaboration, SSE/push notifications) and an MCP server" " (Streamable HTTP, /mcp) for external clients.", ), "arch_lbl_ai": ("Couche IA", "AI layer"), "arch_ai": ( " — assistants d'édition et BooksLM multi-providers (DeepSeek, OpenRouter, Gemini," " Mistral…), bibliothèque d'outils (function calling, recherche web, crawl, lecture de" " documents) et embeddings optionnels pour la recherche sémantique.", " — editor assistant and BooksLM with multiple providers (DeepSeek, OpenRouter, Gemini," " Mistral…), a tool library (function calling, web search, crawl, document reading) and" " optional embeddings for semantic search.", ), "arch_lbl_data": ("Données", "Data"), "arch_data": ( " — les vaults Obsidian sur disque (source de vérité), la configuration en JSON" " (data/), les backups horodatés (.obsigate-backup/)," " l'audit en JSON lines, les clés API chiffrées dans data/api_keys.json.", " — Obsidian vaults on disk (the source of truth), JSON configuration" " (data/), timestamped backups (.obsigate-backup/), a JSON-lines" " audit log, encrypted API keys in data/api_keys.json.", ), "arch_lbl_deploy": ("Déploiement", "Deployment"), "arch_deploy": ( " — application desktop Tauri (Rust) embarquant le backend Python, conteneur Docker, ou" " PWA installable dans le navigateur (mode hors-ligne).", " — Tauri desktop app (Rust) embedding the Python backend, a Docker container, or an" " installable PWA in the browser (offline mode).", ), "arch_h3_flux": ("Flux typique", "Typical request flow"), "arch_flux": ( " Un clic sur un fichier émet GET /api/file/... ; le backend résout le chemin" " en sécurité, parse le frontmatter, rend le markdown et renvoie le HTML ; le frontend" " enrichit l'affichage (Mermaid, coloration, wikilinks cliquables). Chaque écriture crée" " un backup avant application.", " Clicking a file issues GET /api/file/...; the backend resolves the path" " safely, parses frontmatter, renders the markdown and returns HTML; the frontend" " enriches the view (Mermaid, syntax highlighting, clickable wikilinks). Every write" " creates a backup before applying.", ), # -- Section Diagrammes --------------------------------------------------------- "dia_intro": ( "Les blocs ```mermaid de vos notes sont rendus en diagrammes interactifs" " (Mermaid v11, chargé depuis un CDN).", "```mermaid blocks in your notes render as interactive diagrams (Mermaid v11," " loaded from a CDN).", ), "dia_zoom": ("Zoom : boutons + / − dans la barre d'outils du diagramme.", "Zoom: + / − buttons in the diagram toolbar."), "dia_fs": ("Plein écran : idéal pour les grandes matrices.", "Fullscreen: ideal for large charts."), "dia_copy": ("Copie : exportez le SVG ou le code source (boutons dédiés).", "Copy: export the SVG or the source code (dedicated buttons)."), "dia_toggle": ("Bascule Aperçu / Code pour éditer la source sans quitter la vue.", "Preview / Code toggle to edit the source without leaving the view."), "dia_theme": ("Thème : le diagramme suit le thème clair/sombre de l'application.", "Theme: the diagram follows the app's light/dark theme."), "dia_types": ( "Types supportés : flowchart, sequence, class, state, ER, gantt, pie, journey, quadrant," " radar, mindmap, timeline, C4, xychart, sankey — plus un préprocesseur qui comprend la" " syntaxe Obsidian.", "Supported types: flowchart, sequence, class, state, ER, gantt, pie, journey, quadrant," " radar, mindmap, timeline, C4, xychart, sankey — plus a preprocessor that understands" " Obsidian syntax.", ), "dia_excalidraw_ref": ( "Les dessins à main levée (.excalidraw, .excalidraw.md) sont" " couverts dans la section 🎨 Excalidraw.", "Hand-drawn sketches (.excalidraw, .excalidraw.md) are covered" " in the 🎨 Excalidraw section.", ), # -- Section Bibliothèque & signets --------------------------------------------- "lib_h3_bookmarks": ("Signets & récents", "Bookmarks & recents"), "lib_bookmarks": ( "Marquez un fichier d'un ★ (bouton Signet de la barre d'actions) : il rejoint la liste" " des signets du dashboard. Les fichiers récemment ouverts sont listés automatiquement" " dans l'onglet « Récents » de la sidebar, avec un filtre de recherche dédié.", "Star a file with the Bookmark button in the action bar: it joins the dashboard's" " bookmark list. Recently opened files are listed automatically in the sidebar's" " \"Recent\" tab, with a dedicated search filter.", ), "lib_h3_saved": ("Recherches sauvegardées", "Saved searches"), "lib_saved": ( "Enregistrez une recherche depuis la page de résultats pour la relancer en un clic depuis" " la sidebar : chaque recherche sauvegardée conserve ses opérateurs et filtres.", "Save a search from the results page to rerun it in one click from the sidebar: each saved" " search keeps its operators and filters.", ), "lib_h3_backlinks": ("Backlinks & graphe", "Backlinks & graph"), "lib_backlinks": ( "Le panneau Backlinks liste toutes les notes qui pointent vers le fichier ouvert. La vue" " Graphe (bouton 🕸️) affiche les liens entre fichiers : glissez les nœuds, zoomez à la" " molette, double-cliquez pour ouvrir une note.", "The Backlinks panel lists every note pointing to the open file. The Graph view (🕸️" " button) shows links between files: drag nodes, scroll to zoom, double-click a node to" " open the note.", ), "lib_h3_conflicts": ("Conflits de synchronisation", "Sync conflicts"), "lib_conflicts": ( "Si vous synchronisez le vault avec Syncthing, ObsiGate détecte les fichiers de conflit" " (copies « sync-conflict ») et propose de les comparer puis résoudre depuis la page" " dédiée du menu Options.", "If you sync the vault with Syncthing, ObsiGate detects conflict files" " (\"sync-conflict\" copies) and offers to compare then resolve them from a dedicated" " page in the Options menu.", ), "lib_h3_attach": ("Fichiers joints & médias", "Attachments & media"), "lib_attach": ( "Les images ![[image.png]], pièces jointes et médias (audio, vidéo, PDF" " intégrés) dans les notes sont rendus dans le viewer et indexés pour la recherche ; le" " bouton « Rescan attachments » de la configuration recrée l'index des pièces jointes.", "Inline ![[image.png]] images, attachments and media (audio, video, embedded" " PDFs) are rendered in the viewer and indexed for search; the \"Rescan attachments\"" " button in Configuration rebuilds the attachment index.", ), # -- Section Hors-ligne ----------------------------------------------------------- "off_pwa": ( "ObsiGate est une PWA : installez-la (icône d'installation de la barre d'adresse) pour" " l'ouvrir comme une application. Le service worker met en cache l'interface et vos" " derniers documents consultés.", "ObsiGate is a PWA: install it (install icon in the address bar) to open it like an app." " The service worker caches the UI and your recently viewed documents.", ), "off_edit": ( "Hors-ligne, vous pouvez lire les documents en cache et même les éditer : les" " modifications sont mises en file d'attente dans IndexedDB.", "Offline you can read cached documents and even edit them: changes are queued in" " IndexedDB.", ), "off_sync": ( "Au retour en ligne, la file se rejoue automatiquement (badge de synchronisation dans" " l'en-tête). Si la version serveur a divergé entre-temps, le fichier est marqué en" " conflit et la version serveur est préservée en backup.", "When back online the queue replays automatically (sync badge in the header). If the" " server version diverged meanwhile, the file is flagged as conflict and the server copy" " is kept as a backup.", ), "off_watch": ( "Les modifications externes (Obsidian sur disque) sont détectées par le watcher : la vue" " se recharge sans perte de position, ou signale « modifié en externe » pendant une" " édition.", "External changes (Obsidian on disk) are detected by the watcher: the view reloads" " without losing your position, or flags \"modified externally\" during an edit.", ), # -- Section Collaboration ---------------------------------------------------------- "col_intro": ( "Ouvrez un document en mode Édition : plusieurs personnes peuvent travailler" " simultanément sur le même fichier via un WebSocket Yjs (CRDT). Les modifications" " fusionnent sans verrou.", "Open a document in Edit mode: several people can work on the same file simultaneously" " over a Yjs (CRDT) WebSocket. Changes merge without locks.", ), "col_cursors": ( "Les curseurs et sélections des collaborateurs apparaissent avec une couleur et un nom" " par personne (awareness).", "Collaborators' cursors and selections appear with a per-person colour and name" " (awareness).", ), "col_save": ( "La fusion est persistée côté serveur après 2 s d'inactivité ; chaque écriture crée un" " backup horodaté avant application.", "The merge is persisted server-side after 2 s of idle; every write creates a timestamped" " backup before applying.", ), "col_perm": ( "Accès limité aux utilisateurs authentifiés disposant de la permission sur la vault.", "Limited to authenticated users with permission on the vault.", ), # -- Section Desktop ------------------------------------------------------------------ "des_get": ( "L'application desktop ObsiGate (Tauri) embarque le serveur Python : aucune installation" " de Docker nécessaire. Elle se télécharge sur la page des Releases du dépôt et se met à" " jour automatiquement (updater signé).", "The ObsiGate desktop app (Tauri) embeds the Python server: no Docker install needed." " Download it from the repository's Releases page; it auto-updates (signed updater).", ), "des_wizard": ( "Au premier lancement, un assistant demande le dossier de vos vaults (ou crée un vault de" " démonstration). Chaque document peut être détaché en fenêtre native séparée.", "On first launch a wizard asks for your vaults folder (or creates a demo vault). Any" " document can be detached into its own native window.", ), "des_data": ( "Les données desktop restent dans le répertoire applicatif ; les vaults pointent sur vos" " dossiers existants. Toutes les fonctionnalités web (recherche, IA, partage) sont" " disponibles.", "Desktop data stays in the app directory; vaults point at your existing folders. Every web" " feature (search, AI, sharing) is available.", ), "des_native": ( "Menu système natif, raccourci global optionnel pour afficher/masquer la fenêtre et" " jumplist des vaults récents.", "Native system menu, optional global show/hide shortcut and a recents vault jumplist.", ), # -- Section API ------------------------------------------------------------------------ "api_intro": ( "ObsiGate expose une API REST couvrant toute l'application (vaults, fichiers, recherche," " backups, export, IA, partage, admin), documentée en OpenAPI 3.1 :", "ObsiGate exposes a REST API covering the whole application (vaults, files, search," " backups, export, AI, sharing, admin), documented in OpenAPI 3.1:", ), "api_docs_url": ( "/docs — interface Swagger UI pour essayer les requêtes en direct.", "/docs — Swagger UI to try requests live.", ), "api_redoc": ( "/redoc — référence alternative plus compacte.", "/redoc — compact alternative reference.", ), "api_landing": ( "/api — page de garde regroupant les endpoints par catégorie.", "/api — landing page grouping endpoints by category.", ), "api_schema": ( "/openapi.json — le schéma machine, à importer dans Postman ou Insomnia.", "/openapi.json — the machine schema, import into Postman or Insomnia.", ), "api_h3_auth": ("Authentification", "Authentication"), "api_auth": ( "Connectez-vous via POST /api/auth/login pour obtenir un token Bearer (le" " même jeton est accepté en cookie HttpOnly, ce qui permet aux clients navigateur" ' d\'utiliser credentials: "include"). Toutes les routes' " /api/* exigent ce jeton sauf mention contraire.", "Log in via POST /api/auth/login to get a Bearer token (the same token is" " accepted as an HttpOnly cookie, so browser clients can use" ' credentials: "include"). All /api/* routes require it unless' " documented otherwise.", ), "api_h3_mcp": ("Serveur MCP", "MCP server"), "api_mcp": ( "Les outils de l'assistant IA (lire, lister, chercher, ouvrir, écrire…) sont exposés à" " tout client MCP (Claude Desktop, Cursor, Cline…) sur" " https://votre-instance/mcp avec un token d'API. Configuration et exemples :" " docs/MCP_GUIDE.md.", "The assistant's tools (read, list, search, open, write…) are exposed to any MCP client" " (Claude Desktop, Cursor, Cline…) at https://your-instance/mcp with an API" " token. Setup and examples: docs/MCP_GUIDE.md.", ), "api_h3_autom": ("Automatisation", "Automation"), "api_autom": ( "Pour automatiser depuis l'extérieur : GET /api/search?q=… et" " GET /api/file/{vault}?path=… permettent d'indexer ou relire vos notes dans" " un autre outil ; les webhooks sortants (section 🪝) évitent le polling.", "To automate from outside: GET /api/search?q=… and" " GET /api/file/{vault}?path=… let another tool index or re-read your notes;" " outgoing webhooks (🪝 section) avoid polling.", ), # -- Section Multilingue ------------------------------------------------------------------- "lng_how": ( "L'interface est intégralement bilingue français / anglais. Réglages → Profil → Langue :" " le choix est enregistré sur votre compte et vous suit sur tous les appareils.", "The interface is fully bilingual FR/EN. Settings → Profile → Language: the choice is" " stored on your account and follows you across devices.", ), "lng_scope": ( "Tout est traduit : menus, messages, notifications, et le présent guide. Les réponses de" " l'assistant IA suivent la langue de vos documents.", "Everything is translated: menus, messages, notifications, and this guide. AI assistant" " answers follow the language of your documents.", ), "lng_export": ( "Les boutons Markdown / PDF de ce guide téléchargent la version dans votre langue.", "This guide's Markdown / PDF buttons download the version in your language.", ), # -- Compléments dans sections existantes --------------------------------------------------- "h3_semantic": ("Recherche sémantique (hybride)", "Semantic search (hybrid)"), "sem_p1": ( "Activez le bouton « S » de la barre de recherche (ou Alt-S) pour combiner TF-IDF et" " similarité vectorielle (fusion RRF) : les concepts approchants (« velours » trouve" " « tissu doux ») remontent mieux.", "Toggle the \"S\" button in the search bar (or Alt-S) to combine TF-IDF with vector" " similarity (RRF fusion): near concepts (\"velvet\" finds \"soft fabric\") surface" " higher.", ), "sem_p2": ( "Le moteur d'embeddings (modèle multilingue) est optionnel : sans lui, un repli par hash" " conserve une recherche hybride fonctionnelle. Les vecteurs sont recalculés à chaque" " indexation du vault.", "The embedding engine (multilingual model) is optional: without it a hash fallback keeps" " hybrid search working. Vectors are recomputed on each vault reindex.", ), "h3_push": ("Notifications web (push)", "Web notifications (push)"), "push_p1": ( "Autorisez les notifications (bouton 🔔 de l'en-tête) pour être averti des fins de" " synchronisation hors-ligne et des événements importants. La gestion des abonnements est" " dans les Configurations.", "Grant notification permission (🔔 button in the header) to be alerted of offline-sync" " completions and important events. Subscription management lives in Configuration.", ), "push_p2": ( "Basée sur la Web Push API (clés VAPID) ; fonctionne sur desktop et PWA mobile, sans" " service tiers : le serveur émet directement vers les endpoints push des navigateurs.", "Built on the Web Push API (VAPID keys); works on desktop and mobile PWA with no" " third-party service: the server sends directly to browser push endpoints.", ), "h3_panes": ("Vue multi-panneaux (split view)", "Multi-pane split view"), "panes_p": ( "Le bouton « Diviser » de la barre d'actions ouvre le document dans un panneau jumeau ;" " empilez plusieurs panneaux pour comparer deux notes ou lire et éditer en parallèle. Les" " largeurs se règlent au bord des panneaux et sont mémorisées.", "The \"Split\" button in the action bar opens the document in a twin pane; stack several" " panes to compare two notes or read and edit side by side. Pane widths drag on the" " border and are remembered.", ), "h3_dupe": ("Anti-doublons à l'upload", "Duplicate-proof uploads"), "dupe_p": ( "L'upload en masse (glisser-déposer un dossier sur la sidebar) compare chaque fichier au" " contenu existant : un fichier déjà présent est ignoré plutôt que dupliqué avec un" " suffixe « (1) ». Utile pour restaurer un vault sans créer de doublons.", "Bulk upload (drag a folder onto the sidebar) compares each file with existing content: an" " already-present file is skipped rather than duplicated with a \"(1)\" suffix. Handy" " when restoring a vault.", ), "h3_pdf": ("Export PDF", "PDF export"), "pdf_p": ( "Le bouton « PDF » d'un document le rend avec le même moteur que la vue (WeasyPrint) :" " titres, tableaux, listes et code sont conservés. Depuis un lien public, la route" " /s/{token}/pdf produit le même PDF.", "A document's \"PDF\" button renders it with the same engine as the viewer (WeasyPrint):" " headings, tables, lists and code are preserved. From a public link, the" " /s/{token}/pdf route produces the same PDF.", ), "h3_exports": ("Export HTML / ePub / ZIP", "HTML / ePub / ZIP export"), "exp_p": ( "Le menu « Exporter » propose trois formats : HTML autonome (fichier unique, images" " incluses), ePub pour les liseuses et, pour un dossier, un bundle Markdown en ZIP —" " liens et ressources résolus pendant l'export.", "The \"Export\" menu offers three formats: standalone HTML (single file, images inlined)," " ePub for e-readers and, for a folder, a Markdown ZIP bundle — links and resources" " resolved during export.", ), "h3_mfa": ("MFA : TOTP, WebAuthn, codes de secours", "MFA: TOTP, WebAuthn, recovery codes"), "mfa_p": ( "Activez la double authentification dans Réglages → Profil : applications TOTP (Authy," " Aegis…), clés de sécurité et passkeys (WebAuthn, y compris Windows Hello) et 10 codes" " de secours à conserver hors ligne. Chaque méthode s'active et se désactive" " indépendamment.", "Enable two-factor auth in Settings → Profile: TOTP apps (Authy, Aegis…), security keys" " and passkeys (WebAuthn, including Windows Hello) and 10 recovery codes to keep offline." " Each method can be enabled and disabled independently.", ), "h3_admin": ("Tableau de bord administrateur", "Admin dashboard"), "admin_p": ( "Le rôle admin ouvre une page dédiée /admin.html (bouton du menu Options) :" " statut du serveur en direct, utilisateurs, vaults, sessions actives et journal d'audit." " Le CRUD utilisateurs est aussi disponible dans les Configurations.", "The admin role unlocks a dedicated /admin.html page (Options menu button):" " live server status, users, vaults, active sessions and the audit log. User CRUD also" " lives in Configuration.", ), # -- Boutons de téléchargement --------------------------------------------------------------- "dl_md_title": ("Télécharger ce guide en Markdown", "Download this guide as Markdown"), "dl_pdf_title": ("Télécharger ce guide en PDF", "Download this guide as PDF"), # -- Export MD/PDF (côté serveur) ------------------------------------------------------------- "export_title": ("Guide d'utilisation ObsiGate", "ObsiGate User Guide"), "export_footer": ( "Généré depuis ObsiGate {version} — {date}. Ce document est la copie du guide intégré ;" " la version la plus récente est toujours dans l'application.", "Generated from ObsiGate {version} — {date}. This document mirrors the in-app guide; the" " latest version always lives in the application.", ), } # --------------------------------------------------------------------------- # Diagramme Mermaid de la section Architecture (également utilisé par l'export) # --------------------------------------------------------------------------- ARCH_MERMAID = """flowchart TB subgraph client["Clients"] UI["SPA vanilla JS\\n(frontend/js)"] PWA["PWA hors-ligne\\n(service worker + IndexedDB)"] DESK["App desktop Tauri\\n(fenêtre native)"] end subgraph server["Serveur FastAPI (Python 3.11)"] API["REST /api\\nJWT + Argon2id"] IDX["Index recherche\\nTF-IDF + embeddings"] FS["Accès fichiers\\nwatchdog + safe paths"] PDF["Rendu markdown\\nmistune + WeasyPrint"] AI["Assistant IA\\nproviders + outils"] MCP["Serveur MCP\\n/mcp (HTTP)"] WS["WebSocket\\ncollab Yjs + SSE"] WH["Webhooks\\nHMAC-SHA256"] end subgraph data["Données"] V1["Vault 1 (dossier)"] V2["Vault 2 (dossier)"] CFG["data/*.json\\nconfig, users, audit"] BK[".obsigate-backup/\\nbackups horodatés"] end UI -- HTTP --> API PWA -- "cache + queue" --> API DESK -- embarqué --> API API --> IDX API --> FS API --> PDF API --> AI MCP --> AI WS --> FS FS --> V1 FS --> V2 IDX --> V1 IDX --> V2 BK --> V1 API --> CFG API -- événements --> WH""" # --------------------------------------------------------------------------- # Constructeurs de HTML (FR inline == CONTENT[key][0] garanti) # --------------------------------------------------------------------------- def section_html(title_key: str, sec_id: str, body: str) -> str: """Nouvelle
complète avec son h2 data-i18n.""" return ( '
\n' '

%s

\n' "%s" "
\n" "\n" % (sec_id, title_key, CONTENT[title_key][0], body) ) def _li(key: str) -> str: return '
  • %s
  • \n' % (key, CONTENT[key][0]) def _bullets(keys: list) -> str: return " \n" def _p(key: str) -> str: return '

    %s

    \n' % (key, CONTENT[key][0]) def _h3(key: str) -> str: return '

    %s

    \n' % (key, CONTENT[key][0]) def _pair(label_key: str, text_key: str) -> str: """
  • Label — texte
  • (deux clés i18n).""" return ( "
  • \n" ' %s' '%s\n' "
  • \n" % (label_key, CONTENT[label_key][0], text_key, CONTENT[text_key][0]) ) def _h3p(h3_key: str, *p_keys: str) -> str: return _h3(h3_key) + "".join(_p(k) for k in p_keys) # --------------------------------------------------------------------------- # Les huit nouvelles sections # --------------------------------------------------------------------------- SECTION_ARCHITECTURE = section_html( "nav_architecture", "help-architecture", _p("arch_intro") + '
    %s
    \n' % ARCH_MERMAID + _p("arch_diagram_note") + _h3("arch_h3_layers") + " \n" + _h3p("arch_h3_flux", "arch_flux"), ) SECTION_DIAGRAMS = section_html( "nav_diagrams", "help-diagrams", _p("dia_intro") + _bullets(["dia_zoom", "dia_fs", "dia_copy", "dia_toggle", "dia_theme"]) + _p("dia_types") + _p("dia_excalidraw_ref"), ) SECTION_LIBRARY = section_html( "nav_library", "help-library", _h3p("lib_h3_bookmarks", "lib_bookmarks") + _h3p("lib_h3_saved", "lib_saved") + _h3p("lib_h3_backlinks", "lib_backlinks") + _h3p("lib_h3_conflicts", "lib_conflicts") + _h3p("lib_h3_attach", "lib_attach"), ) SECTION_OFFLINE = section_html("nav_offline", "help-offline", _bullets(["off_pwa", "off_edit", "off_sync", "off_watch"])) SECTION_COLLAB = section_html("nav_collab", "help-collab", _bullets(["col_intro", "col_cursors", "col_save", "col_perm"])) SECTION_DESKTOP = section_html("nav_desktop", "help-desktop", _bullets(["des_get", "des_wizard", "des_data", "des_native"])) SECTION_API = section_html( "nav_api", "help-api", _p("api_intro") + _bullets(["api_docs_url", "api_redoc", "api_landing", "api_schema"]) + _h3p("api_h3_auth", "api_auth") + _h3p("api_h3_mcp", "api_mcp") + _h3p("api_h3_autom", "api_autom"), ) SECTION_LANG = section_html("nav_languages", "help-languages", _bullets(["lng_how", "lng_scope", "lng_export"])) # (id de section, HTML complet, id de la section AVANT laquelle insérer) NEW_SECTIONS: list[tuple[str, str, str]] = [ ("help-architecture", SECTION_ARCHITECTURE, "help-interface"), ("help-diagrams", SECTION_DIAGRAMS, "help-edition"), ("help-library", SECTION_LIBRARY, "help-graphe"), ("help-offline", SECTION_OFFLINE, "help-partage"), ("help-collab", SECTION_COLLAB, "help-partage"), ("help-desktop", SECTION_DESKTOP, "help-partage"), ("help-api", SECTION_API, "help-plugins"), ("help-languages", SECTION_LANG, "help-plugins"), ] # Compléments insérés À LA FIN de sections existantes (avant leur
    ) : # section id -> bloc HTML EXTRA_BLOCKS: list[tuple[str, str]] = [ ("help-interface", _h3p("h3_push", "push_p1", "push_p2")), ("help-recherche", _h3p("h3_semantic", "sem_p1", "sem_p2")), ("help-fichiers", _h3p("h3_pdf", "pdf_p") + _h3p("h3_exports", "exp_p") + _h3p("h3_dupe", "dupe_p")), ("help-personnalisation", _h3p("h3_panes", "panes_p")), ("help-securite", _h3p("h3_mfa", "mfa_p") + _h3p("h3_admin", "admin_p")), ] # Entrées TOC à insérer AVANT l'entrée dont l'href est la 3e valeur. TOC_INSERT_BEFORE: list[tuple[str, str, str]] = [ ("nav_architecture", "#help-architecture", "#help-interface"), ("nav_diagrams", "#help-diagrams", "#help-edition"), ("nav_library", "#help-library", "#help-graphe"), ("nav_offline", "#help-offline", "#help-partage"), ("nav_collab", "#help-collab", "#help-partage"), ("nav_desktop", "#help-desktop", "#help-partage"), ("nav_api", "#help-api", "#help-plugins"), ("nav_languages", "#help-languages", "#help-plugins"), ] # Section « Édition mobile » dédiée (fix BUG-067) : créée par le script # d'insertion après la section help-edition. MOBILE_SECTION_TITLE_KEY = "help.nav_mobile_editor" MOBILE_SECTION_TITLE_FR = "📱 Édition mobile"