- Bouton « mode agent » du panneau supprimé : l'assistant est toujours agent (toute requête texte part sur /api/ai/bookslm/agent, les images restent sur /chat multimodal). Les mutations gardent la confirmation two-step. Deep Research et les quick actions `agent: true` basculaient déjà le mode en silence : le toggle ne protégeait plus rien. - /agent résout le provider comme /chat (req.provider brut transmis à l'adapter pouvait désigner un fournisseur indisponible et retomber silencieusement sur un autre que l'étiquette SSE affichée). - Schémas des outils mis en cache par (scope, taille du registre) avec copies fraîches par appelant (~27 ms de pydantic économisées par requête agent/MCP). - Aide in-app réécrite, i18n FR/EN (retrait de ai.agent_mode_*), tests frontend adaptés (ai.test.mjs 100/100) + non-régression pytest (provider résolu, cache sûr).
5826 lines
327 KiB
HTML
5826 lines
327 KiB
HTML
<!doctype html>
|
||
<html lang="fr" data-theme="dark">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||
<title data-i18n="header.logo">ObsiGate</title>
|
||
|
||
<!-- PWA Meta Tags -->
|
||
<meta
|
||
name="description"
|
||
content="Porte d'entrée web pour vos vaults Obsidian - Accédez, naviguez et recherchez dans toutes vos notes"
|
||
/>
|
||
<meta name="theme-color" content="#2563eb" />
|
||
<meta name="mobile-web-app-capable" content="yes" />
|
||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||
<meta
|
||
name="apple-mobile-web-app-status-bar-style"
|
||
content="black-translucent"
|
||
/>
|
||
<meta name="apple-mobile-web-app-title" content="ObsiGate" />
|
||
|
||
<!-- PWA Manifest -->
|
||
<link rel="manifest" href="/static/manifest.json" />
|
||
|
||
<!-- Apple Touch Icons -->
|
||
<link
|
||
rel="apple-touch-icon"
|
||
sizes="152x152"
|
||
href="/static/icons/icon-152x152.svg"
|
||
/>
|
||
<link
|
||
rel="apple-touch-icon"
|
||
sizes="192x192"
|
||
href="/static/icons/icon-192x192.svg"
|
||
/>
|
||
|
||
<!-- Favicon -->
|
||
<link
|
||
rel="icon"
|
||
type="image/svg+xml"
|
||
href="/static/icons/icon-72x72.svg"
|
||
/>
|
||
<link
|
||
rel="icon"
|
||
type="image/svg+xml"
|
||
sizes="192x192"
|
||
href="/static/icons/icon-192x192.svg"
|
||
/>
|
||
|
||
<!-- Boot splash CSS — inline so it applies BEFORE /static/style.css is
|
||
available (desktop: embedded page served from tauri:// has no /static) -->
|
||
<style id="boot-splash-css">
|
||
body:not(.app-booted) > *:not(#boot-splash) { display: none !important; }
|
||
#boot-splash {
|
||
position: fixed; inset: 0; z-index: 2147483647;
|
||
display: flex; align-items: center; justify-content: center;
|
||
background: radial-gradient(1200px 600px at 50% -10%, #1b2030 0%, #0e1117 60%);
|
||
color: #e6e8ee; font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
|
||
}
|
||
body.app-booted #boot-splash { display: none; }
|
||
.bs-card { display: flex; flex-direction: column; align-items: center; gap: 14px; }
|
||
.bs-logo {
|
||
width: 72px; height: 72px; display: flex; align-items: center; justify-content: center;
|
||
font-size: 38px; background: #171c26; border: 1px solid #2a3242; border-radius: 18px;
|
||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.45);
|
||
}
|
||
.bs-title { font-size: 22px; font-weight: 600; letter-spacing: 0.3px; }
|
||
.bs-spinner {
|
||
width: 34px; height: 34px; border-radius: 50%;
|
||
border: 3px solid rgba(124, 58, 237, 0.25); border-top-color: #7c3aed;
|
||
animation: bs-spin 0.9s linear infinite;
|
||
}
|
||
@keyframes bs-spin { to { transform: rotate(360deg); } }
|
||
.bs-status { font-size: 13px; color: #9aa3b2; min-height: 18px; }
|
||
@media (prefers-reduced-motion: reduce) { .bs-spinner { animation-duration: 1.8s; } }
|
||
</style>
|
||
<link rel="stylesheet" href="/static/style.css" />
|
||
<link
|
||
rel="stylesheet"
|
||
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css"
|
||
id="hljs-theme-dark"
|
||
/>
|
||
<link
|
||
rel="stylesheet"
|
||
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css"
|
||
id="hljs-theme-light"
|
||
disabled
|
||
/>
|
||
<!-- defer: ne bloque pas le premier paint (le splash inline s'affiche immédiatement) -->
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||
<!-- Langues highlight.js hors bundle "common" (enregistrement auto apres le core) -->
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/dockerfile.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/powershell.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/dos.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cmake.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/dart.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/elixir.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/haskell.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/nginx.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/http.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/plaintext.min.js"></script>
|
||
<script defer src="https://unpkg.com/[email protected]/dist/umd/lucide.min.js"></script>
|
||
<script defer src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
|
||
<script type="importmap">
|
||
{
|
||
"imports": {
|
||
"@codemirror/state": "https://esm.sh/@codemirror/[email protected]",
|
||
"yjs": "https://esm.sh/[email protected]"
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<!-- CodeMirror 6 -->
|
||
<script type="module">
|
||
import {
|
||
EditorView,
|
||
keymap,
|
||
lineNumbers,
|
||
highlightActiveLineGutter,
|
||
highlightSpecialChars,
|
||
drawSelection,
|
||
dropCursor,
|
||
rectangularSelection,
|
||
crosshairCursor,
|
||
highlightActiveLine,
|
||
Decoration,
|
||
ViewPlugin,
|
||
WidgetType,
|
||
} from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import { EditorState, StateEffect } from "@codemirror/state";
|
||
import {
|
||
defaultHighlightStyle,
|
||
syntaxHighlighting,
|
||
indentOnInput,
|
||
bracketMatching,
|
||
foldGutter,
|
||
foldKeymap,
|
||
} from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import {
|
||
defaultKeymap,
|
||
history,
|
||
historyKeymap,
|
||
undo,
|
||
redo,
|
||
} from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import {
|
||
searchKeymap,
|
||
highlightSelectionMatches,
|
||
} from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import {
|
||
autocompletion,
|
||
completionKeymap,
|
||
closeBrackets,
|
||
closeBracketsKeymap,
|
||
} from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import { markdown } from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import { python } from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import { javascript } from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import { html } from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import { css } from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import { json } from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import { xml } from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import { sql } from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import { php } from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import { cpp } from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import { java } from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import { rust } from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
import { oneDark } from "https://esm.sh/@codemirror/[email protected]?external=@codemirror/state";
|
||
|
||
const basicSetup = [
|
||
lineNumbers(),
|
||
highlightActiveLineGutter(),
|
||
highlightSpecialChars(),
|
||
history(),
|
||
foldGutter(),
|
||
drawSelection(),
|
||
dropCursor(),
|
||
EditorState.allowMultipleSelections.of(true),
|
||
indentOnInput(),
|
||
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||
bracketMatching(),
|
||
closeBrackets(),
|
||
autocompletion(),
|
||
rectangularSelection(),
|
||
crosshairCursor(),
|
||
highlightActiveLine(),
|
||
highlightSelectionMatches(),
|
||
keymap.of([
|
||
...closeBracketsKeymap,
|
||
...defaultKeymap,
|
||
...searchKeymap,
|
||
...historyKeymap,
|
||
...foldKeymap,
|
||
...completionKeymap,
|
||
]),
|
||
];
|
||
|
||
window.CodeMirror = {
|
||
EditorView,
|
||
EditorState,
|
||
basicSetup,
|
||
markdown,
|
||
python,
|
||
javascript,
|
||
html,
|
||
css,
|
||
json,
|
||
xml,
|
||
sql,
|
||
php,
|
||
cpp,
|
||
java,
|
||
rust,
|
||
oneDark,
|
||
keymap,
|
||
undo,
|
||
redo,
|
||
Decoration,
|
||
ViewPlugin,
|
||
WidgetType,
|
||
StateEffect,
|
||
};
|
||
</script>
|
||
</head>
|
||
<body>
|
||
<!-- Boot splash : visible tant que l'app n'a pas booté avec son CSS.
|
||
Sur desktop, la page embarquée (tauri://) n'a pas /static/style.css →
|
||
sans ce splash, le squelette HTML brut serait visible pendant le
|
||
démarrage du backend (FOUC 3-10 s). Styles 100% inline. -->
|
||
<div id="boot-splash" role="status" aria-live="polite">
|
||
<div class="bs-card">
|
||
<div class="bs-logo">📖</div>
|
||
<div class="bs-title">ObsiGate</div>
|
||
<div class="bs-spinner" aria-hidden="true"></div>
|
||
<div class="bs-status" id="boot-splash-status">Démarrage…</div>
|
||
</div>
|
||
</div>
|
||
<script>
|
||
(function () {
|
||
var done = false;
|
||
function bootReady() {
|
||
if (done) return;
|
||
done = true;
|
||
document.body.classList.add("app-booted");
|
||
var s = document.getElementById("boot-splash");
|
||
if (s && s.parentNode) s.parentNode.removeChild(s);
|
||
}
|
||
window.__obsigateBootReady = bootReady;
|
||
window.__setBootStatus = function (txt) {
|
||
var el = document.getElementById("boot-splash-status");
|
||
if (el) el.textContent = txt;
|
||
};
|
||
var poll = setInterval(function () {
|
||
if (window.__OBSIGATE_BOOTED) { clearInterval(poll); bootReady(); }
|
||
}, 80);
|
||
// Filet de sécurité : si le CSS principal a chargé (origine backend réelle)
|
||
// mais que le JS a échoué, révéler l'app après 10 s au lieu d'un splash infini.
|
||
setTimeout(function () {
|
||
var link = document.querySelector('link[href*="style.css"]');
|
||
if (link && link.sheet) { clearInterval(poll); bootReady(); }
|
||
}, 10000);
|
||
})();
|
||
</script>
|
||
<div class="search-progress-bar" id="search-progress-bar">
|
||
<div class="search-progress-bar__fill"></div>
|
||
</div>
|
||
|
||
<!-- Login Screen (hidden by default, shown when auth is required) -->
|
||
<div id="login-screen" class="login-screen hidden">
|
||
<div class="login-card">
|
||
<div class="login-header">
|
||
<div class="login-logo">
|
||
<span class="login-logo-icon">📖</span>
|
||
<span class="login-logo-text" data-i18n="header.logo">ObsiGate</span>
|
||
</div>
|
||
<p class="login-subtitle" data-i18n="auth.login_title">Accès sécurisé à vos vaults</p>
|
||
</div>
|
||
<form id="login-form" class="login-form" autocomplete="on">
|
||
<div class="form-group">
|
||
<label for="login-username" data-i18n="auth.username">Nom d'utilisateur</label>
|
||
<input
|
||
type="text"
|
||
id="login-username"
|
||
name="username"
|
||
autocomplete="username"
|
||
placeholder="utilisateur"
|
||
required
|
||
autofocus
|
||
/>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="login-password" data-i18n="auth.password">Mot de passe</label>
|
||
<div class="password-input-wrapper">
|
||
<input
|
||
type="password"
|
||
id="login-password"
|
||
name="password"
|
||
autocomplete="current-password"
|
||
placeholder="••••••••"
|
||
required
|
||
/>
|
||
<button
|
||
type="button"
|
||
id="toggle-password"
|
||
class="password-toggle"
|
||
aria-label="Afficher le mot de passe"
|
||
>
|
||
👁
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="form-group form-group--inline">
|
||
<label class="checkbox-label">
|
||
<input
|
||
type="checkbox"
|
||
id="remember-me"
|
||
name="remember_me"
|
||
/>
|
||
<span data-i18n="auth.remember_me">Se souvenir de moi (30 jours)</span>
|
||
</label>
|
||
</div>
|
||
<div id="login-error" class="login-error hidden"></div>
|
||
<button type="submit" id="login-btn" class="btn-login">
|
||
<span class="btn-text" data-i18n="auth.login_button">Se connecter</span>
|
||
<span class="btn-spinner hidden">⏳</span>
|
||
</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Main Application (wrapped for show/hide) -->
|
||
<div id="app" class="app-container">
|
||
<!-- Header -->
|
||
<header class="header">
|
||
<div class="header-left">
|
||
<button
|
||
class="hamburger-btn"
|
||
id="hamburger-btn"
|
||
title="Menu"
|
||
>
|
||
<i
|
||
data-lucide="menu"
|
||
style="width: 20px; height: 20px"
|
||
></i>
|
||
</button>
|
||
<button
|
||
class="sidebar-toggle-btn"
|
||
id="sidebar-toggle-btn"
|
||
title="Afficher/Masquer la sidebar"
|
||
>
|
||
<i
|
||
data-lucide="sidebar"
|
||
style="width: 20px; height: 20px"
|
||
></i>
|
||
</button>
|
||
|
||
<div class="header-logo" id="header-logo">
|
||
<i
|
||
data-lucide="book-open"
|
||
style="width: 20px; height: 20px"
|
||
></i>
|
||
ObsiGate
|
||
</div>
|
||
<span
|
||
class="sync-badge sync-badge--disconnected"
|
||
id="sync-badge"
|
||
title="Synchronisation"
|
||
></span>
|
||
</div>
|
||
|
||
<div class="header-center">
|
||
<div class="search-wrapper">
|
||
<i
|
||
data-lucide="search"
|
||
class="search-icon"
|
||
style="width: 16px; height: 16px"
|
||
></i>
|
||
<div class="search-input-wrapper">
|
||
<input
|
||
type="text"
|
||
id="search-input"
|
||
data-i18n-placeholder="header.search_placeholder"
|
||
placeholder="Rechercher ..."
|
||
autocomplete="off"
|
||
/>
|
||
<div class="search-actions">
|
||
<button
|
||
class="search-btn icon-only"
|
||
id="search-filter-btn"
|
||
type="button"
|
||
title="Filtres de chemin (Alt-F)"
|
||
>
|
||
<i
|
||
data-lucide="filter"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
</button>
|
||
<button
|
||
class="search-btn icon-only"
|
||
id="search-clear-btn"
|
||
type="button"
|
||
title="Effacer"
|
||
aria-label="Effacer la recherche"
|
||
>
|
||
<i
|
||
data-lucide="x"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<!-- Path filter row -->
|
||
<div
|
||
class="search-filter-row"
|
||
id="search-filter-row"
|
||
style="display: none"
|
||
>
|
||
<input
|
||
type="text"
|
||
class="search-filter-input"
|
||
id="search-include-input"
|
||
data-i18n-placeholder="search.include_placeholder" placeholder="Inclure: **/*.md, notes/**"
|
||
/>
|
||
<input
|
||
type="text"
|
||
class="search-filter-input"
|
||
id="search-exclude-input"
|
||
data-i18n-placeholder="search.exclude_placeholder" placeholder="Exclure: vendor/*, *.lock"
|
||
/>
|
||
</div>
|
||
<!-- Advanced search autocomplete dropdown -->
|
||
<div
|
||
class="search-dropdown"
|
||
id="search-dropdown"
|
||
role="listbox"
|
||
aria-label="Suggestions de recherche"
|
||
hidden
|
||
>
|
||
<div
|
||
class="search-dropdown__section search-dropdown__section--history"
|
||
id="search-dropdown-history"
|
||
>
|
||
<div class="search-dropdown__section-header">
|
||
<span data-i18n="search.dropdown_history">Historique</span>
|
||
<button
|
||
class="search-dropdown__clear-btn"
|
||
id="search-dropdown-clear-history"
|
||
type="button"
|
||
title="Effacer l'historique"
|
||
>
|
||
<i
|
||
data-lucide="trash-2"
|
||
style="width: 12px; height: 12px"
|
||
></i>
|
||
</button>
|
||
</div>
|
||
<ul
|
||
class="search-dropdown__list"
|
||
id="search-dropdown-history-list"
|
||
></ul>
|
||
</div>
|
||
<div
|
||
class="search-dropdown__section search-dropdown__section--titles"
|
||
id="search-dropdown-titles"
|
||
>
|
||
<div class="search-dropdown__section-header">
|
||
<span data-i18n="palette.mode_files">Fichiers</span>
|
||
</div>
|
||
<ul
|
||
class="search-dropdown__list"
|
||
id="search-dropdown-titles-list"
|
||
></ul>
|
||
</div>
|
||
<div
|
||
class="search-dropdown__section search-dropdown__section--tags"
|
||
id="search-dropdown-tags"
|
||
>
|
||
<div class="search-dropdown__section-header">
|
||
<span data-i18n="sidebar.tab_tags">Tags</span>
|
||
</div>
|
||
<ul
|
||
class="search-dropdown__list"
|
||
id="search-dropdown-tags-list"
|
||
></ul>
|
||
</div>
|
||
<div
|
||
class="search-dropdown__empty"
|
||
id="search-dropdown-empty"
|
||
hidden
|
||
>
|
||
Aucune suggestion
|
||
</div>
|
||
</div>
|
||
<!-- Active search filter chips -->
|
||
<div
|
||
class="search-chips"
|
||
id="search-chips"
|
||
hidden
|
||
></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="header-right">
|
||
<!-- Offline indicator (hidden by default) -->
|
||
<div
|
||
class="header-icon offline-indicator"
|
||
id="offline-badge"
|
||
style="display:none"
|
||
title="Mode hors-ligne"
|
||
></div>
|
||
<div
|
||
class="vault-context-indicator"
|
||
id="vault-context-indicator"
|
||
title="Contexte vault actif"
|
||
>
|
||
<i
|
||
data-lucide="database"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
<span id="vault-context-text">All</span>
|
||
</div>
|
||
<div class="header-menu">
|
||
<button
|
||
class="header-menu-btn"
|
||
id="header-menu-btn"
|
||
title="Options"
|
||
>
|
||
<i
|
||
data-lucide="settings"
|
||
style="width: 18px; height: 18px"
|
||
></i>
|
||
</button>
|
||
<div
|
||
class="header-menu-dropdown"
|
||
id="header-menu-dropdown"
|
||
>
|
||
<div
|
||
class="menu-list"
|
||
role="menu"
|
||
aria-label="Options"
|
||
>
|
||
<label
|
||
class="menu-list-row menu-list-select-row hidden"
|
||
for="vault-filter"
|
||
>
|
||
<span
|
||
class="menu-list-icon"
|
||
aria-hidden="true"
|
||
>
|
||
<i
|
||
data-lucide="folders"
|
||
style="width: 17px; height: 17px"
|
||
></i>
|
||
</span>
|
||
<span class="menu-list-content">
|
||
<span class="menu-list-title"
|
||
>Vault</span
|
||
>
|
||
<div
|
||
class="custom-dropdown"
|
||
id="vault-filter-dropdown"
|
||
>
|
||
<button
|
||
class="custom-dropdown-trigger"
|
||
type="button"
|
||
aria-haspopup="listbox"
|
||
aria-expanded="false"
|
||
>
|
||
<span
|
||
class="custom-dropdown-selected"
|
||
>Tous les vaults</span
|
||
>
|
||
<i
|
||
data-lucide="chevron-down"
|
||
style="
|
||
width: 14px;
|
||
height: 14px;
|
||
"
|
||
></i>
|
||
</button>
|
||
<ul
|
||
class="custom-dropdown-menu"
|
||
role="listbox"
|
||
id="vault-filter-options"
|
||
>
|
||
<li role="option"
|
||
data-value="all"
|
||
class="custom-dropdown-option selected" data-i18n="help.all_vaults">
|
||
Tous les vaults
|
||
</li>
|
||
</ul>
|
||
<input
|
||
type="hidden"
|
||
id="vault-filter"
|
||
value="all"
|
||
/>
|
||
</div>
|
||
</span>
|
||
</label>
|
||
<button
|
||
class="menu-list-row menu-list-button"
|
||
id="theme-toggle"
|
||
type="button"
|
||
role="menuitem"
|
||
>
|
||
<span
|
||
class="menu-list-icon"
|
||
aria-hidden="true"
|
||
>
|
||
<i
|
||
data-lucide="sun"
|
||
style="width: 17px; height: 17px"
|
||
></i>
|
||
</span>
|
||
<span class="menu-list-content">
|
||
<span class="menu-list-title" data-i18n="header.menu_theme"
|
||
>Thème</span
|
||
>
|
||
<span
|
||
class="menu-list-subtitle"
|
||
id="theme-label"
|
||
>Clair</span
|
||
>
|
||
</span>
|
||
</button>
|
||
<button
|
||
class="menu-list-row menu-list-button hidden"
|
||
id="admin-menu-row"
|
||
type="button"
|
||
role="menuitem"
|
||
>
|
||
<span
|
||
class="menu-list-icon"
|
||
aria-hidden="true"
|
||
>
|
||
<i
|
||
data-lucide="shield"
|
||
style="width: 17px; height: 17px"
|
||
></i>
|
||
</span>
|
||
<span class="menu-list-content">
|
||
<span class="menu-list-title" data-i18n="header.menu_admin"
|
||
>Administration</span
|
||
>
|
||
<span class="menu-list-subtitle" data-i18n="header.menu_admin_desc"
|
||
>Gérer les utilisateurs</span
|
||
>
|
||
</span>
|
||
</button>
|
||
<button
|
||
class="menu-list-row menu-list-button"
|
||
id="api-docs-menu-row"
|
||
type="button"
|
||
role="menuitem"
|
||
>
|
||
<span
|
||
class="menu-list-icon"
|
||
aria-hidden="true"
|
||
>
|
||
<i
|
||
data-lucide="code-2"
|
||
style="width: 17px; height: 17px"
|
||
></i>
|
||
</span>
|
||
<span class="menu-list-content">
|
||
<span class="menu-list-title" data-i18n="header.menu_api"
|
||
>API</span
|
||
>
|
||
<span class="menu-list-subtitle" data-i18n="header.menu_api_desc"
|
||
>Documentation OpenAPI</span
|
||
>
|
||
</span>
|
||
</button>
|
||
<button
|
||
class="menu-list-row menu-list-button"
|
||
id="config-open-btn"
|
||
type="button"
|
||
role="menuitem"
|
||
>
|
||
<span
|
||
class="menu-list-icon"
|
||
aria-hidden="true"
|
||
>
|
||
<i
|
||
data-lucide="settings"
|
||
style="width: 17px; height: 17px"
|
||
></i>
|
||
</span>
|
||
<span class="menu-list-content">
|
||
<span class="menu-list-title" data-i18n="header.menu_config"
|
||
>Configurations</span
|
||
>
|
||
<span class="menu-list-subtitle" data-i18n="header.menu_config_desc"
|
||
>Paramètres de l'application</span
|
||
>
|
||
</span>
|
||
</button>
|
||
<button
|
||
class="menu-list-row menu-list-button"
|
||
id="help-open-btn"
|
||
type="button"
|
||
role="menuitem"
|
||
>
|
||
<span
|
||
class="menu-list-icon"
|
||
aria-hidden="true"
|
||
>
|
||
<i
|
||
data-lucide="book-open"
|
||
style="width: 17px; height: 17px"
|
||
></i>
|
||
</span>
|
||
<span class="menu-list-content">
|
||
<span class="menu-list-title" data-i18n="header.menu_help"
|
||
>Guide d'utilisation</span
|
||
>
|
||
<span class="menu-list-subtitle" data-i18n="header.menu_help_desc"
|
||
>Ouvrir l'aide</span
|
||
>
|
||
</span>
|
||
</button>
|
||
<button
|
||
class="menu-list-row menu-list-button"
|
||
id="about-open-btn"
|
||
type="button"
|
||
role="menuitem"
|
||
>
|
||
<span
|
||
class="menu-list-icon"
|
||
aria-hidden="true"
|
||
>
|
||
<i
|
||
data-lucide="sparkles"
|
||
style="width: 17px; height: 17px"
|
||
></i>
|
||
</span>
|
||
<span class="menu-list-content">
|
||
<span class="menu-list-title" data-i18n="header.menu_about"
|
||
>A propos</span
|
||
>
|
||
<span class="menu-list-subtitle" data-i18n="header.menu_about_desc"
|
||
>Version, credits & info</span
|
||
>
|
||
</span>
|
||
</button>
|
||
<button
|
||
class="menu-list-row menu-list-button"
|
||
id="profile-open-btn"
|
||
type="button"
|
||
role="menuitem"
|
||
>
|
||
<span
|
||
class="menu-list-icon"
|
||
aria-hidden="true"
|
||
>
|
||
<i
|
||
data-lucide="user"
|
||
style="width: 17px; height: 17px"
|
||
></i>
|
||
</span>
|
||
<span class="menu-list-content">
|
||
<span class="menu-list-title" data-i18n="header.menu_profile"
|
||
>Profil</span
|
||
>
|
||
<span class="menu-list-subtitle" data-i18n="header.menu_profile_desc"
|
||
>Langue & preferences</span
|
||
>
|
||
</span>
|
||
</button>
|
||
<div
|
||
class="menu-list-row menu-list-version"
|
||
role="none"
|
||
>
|
||
<span
|
||
class="menu-list-icon"
|
||
aria-hidden="true"
|
||
>
|
||
<i
|
||
data-lucide="tag"
|
||
style="width: 17px; height: 17px"
|
||
></i>
|
||
</span>
|
||
<span class="menu-list-content">
|
||
<span class="menu-list-title" data-i18n="header.menu_version"
|
||
>Version</span
|
||
>
|
||
<span class="menu-list-subtitle"
|
||
>ObsiGate
|
||
<span
|
||
class="version-badge"
|
||
id="version-badge"
|
||
title="Version ObsiGate"
|
||
></span
|
||
></span>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- Main -->
|
||
<div class="main-body">
|
||
<!-- Mobile overlay -->
|
||
<div class="sidebar-overlay" id="sidebar-overlay"></div>
|
||
|
||
<!-- Sidebar -->
|
||
<aside
|
||
class="sidebar"
|
||
id="sidebar"
|
||
role="navigation"
|
||
aria-label="Navigation des vaults"
|
||
>
|
||
<!-- Sidebar filter -->
|
||
<div class="sidebar-filter">
|
||
<div class="sidebar-filter-input-wrapper">
|
||
<i
|
||
data-lucide="filter"
|
||
class="sidebar-filter-icon"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
<input
|
||
type="text"
|
||
id="sidebar-filter-input"
|
||
data-i18n-placeholder="sidebar.filter_placeholder" placeholder="Filtrer fichiers..."
|
||
autocomplete="off"
|
||
/>
|
||
<div class="sidebar-filter-actions">
|
||
<button
|
||
class="sidebar-filter-btn"
|
||
id="sidebar-filter-case-btn"
|
||
type="button"
|
||
title="Respecter la casse"
|
||
aria-label="Respecter la casse"
|
||
>
|
||
<span>Aa</span>
|
||
</button>
|
||
<button
|
||
class="sidebar-filter-btn"
|
||
id="sidebar-filter-clear-btn"
|
||
type="button"
|
||
title="Effacer le filtrage"
|
||
aria-label="Effacer le filtrage"
|
||
>
|
||
<i
|
||
data-lucide="x"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tab bar -->
|
||
<div
|
||
class="sidebar-tabs"
|
||
role="tablist"
|
||
aria-label="Sections sidebar"
|
||
>
|
||
<button
|
||
class="sidebar-tab active"
|
||
id="sidebar-tab-vaults"
|
||
role="tab"
|
||
aria-selected="true"
|
||
aria-controls="sidebar-panel-vaults"
|
||
data-tab="vaults"
|
||
title="Vaults"
|
||
>
|
||
<i
|
||
data-lucide="folder-tree"
|
||
style="width: 18px; height: 18px"
|
||
></i>
|
||
</button>
|
||
<button
|
||
class="sidebar-tab"
|
||
id="sidebar-tab-tags"
|
||
role="tab"
|
||
aria-selected="false"
|
||
aria-controls="sidebar-panel-tags"
|
||
data-tab="tags"
|
||
title="Tags"
|
||
>
|
||
<i
|
||
data-lucide="tag"
|
||
style="width: 18px; height: 18px"
|
||
></i>
|
||
</button>
|
||
<button
|
||
class="sidebar-tab"
|
||
id="sidebar-tab-recent"
|
||
role="tab"
|
||
aria-selected="false"
|
||
aria-controls="sidebar-panel-recent"
|
||
data-tab="recent"
|
||
title="Récent"
|
||
>
|
||
<i
|
||
data-lucide="clock"
|
||
style="width: 18px; height: 18px"
|
||
></i>
|
||
</button>
|
||
<button
|
||
class="sidebar-tab"
|
||
id="sidebar-tab-saved"
|
||
role="tab"
|
||
aria-selected="false"
|
||
aria-controls="sidebar-panel-saved"
|
||
data-tab="saved"
|
||
title="Sauvegardes"
|
||
>
|
||
<i
|
||
data-lucide="bookmark"
|
||
style="width: 18px; height: 18px"
|
||
></i>
|
||
</button>
|
||
<button
|
||
class="sidebar-tab"
|
||
id="sidebar-tab-ai"
|
||
role="tab"
|
||
aria-selected="false"
|
||
aria-controls="sidebar-panel-ai"
|
||
data-tab="ai"
|
||
title="Historique IA"
|
||
>
|
||
<i
|
||
data-lucide="messages-square"
|
||
style="width: 18px; height: 18px"
|
||
></i>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Vaults panel -->
|
||
<div
|
||
class="sidebar-tab-panel active"
|
||
id="sidebar-panel-vaults"
|
||
role="tabpanel"
|
||
aria-labelledby="sidebar-tab-vaults"
|
||
>
|
||
<div
|
||
class="custom-dropdown sidebar-dropdown"
|
||
id="vault-quick-select-dropdown"
|
||
>
|
||
<button
|
||
class="custom-dropdown-trigger"
|
||
type="button"
|
||
aria-haspopup="listbox"
|
||
aria-expanded="false"
|
||
>
|
||
<span class="custom-dropdown-selected"
|
||
>Tous les vaults</span
|
||
>
|
||
<i
|
||
data-lucide="chevron-down"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
</button>
|
||
<ul
|
||
class="custom-dropdown-menu"
|
||
role="listbox"
|
||
id="vault-quick-select-options"
|
||
>
|
||
<li role="option"
|
||
data-value="all"
|
||
class="custom-dropdown-option selected" data-i18n="help.all_vaults">
|
||
Tous les vaults
|
||
</li>
|
||
</ul>
|
||
<input
|
||
type="hidden"
|
||
id="vault-quick-select"
|
||
value="all"
|
||
/>
|
||
</div>
|
||
<div
|
||
id="vault-tree"
|
||
role="tree"
|
||
aria-label="Arborescence des fichiers"
|
||
></div>
|
||
</div>
|
||
|
||
<!-- Tags panel -->
|
||
<div
|
||
class="sidebar-tab-panel"
|
||
id="sidebar-panel-tags"
|
||
role="tabpanel"
|
||
aria-labelledby="sidebar-tab-tags"
|
||
>
|
||
<div class="tag-cloud" id="tag-cloud"></div>
|
||
</div>
|
||
|
||
<!-- Recent panel -->
|
||
<div
|
||
class="sidebar-tab-panel"
|
||
id="sidebar-panel-recent"
|
||
role="tabpanel"
|
||
aria-labelledby="sidebar-tab-recent"
|
||
>
|
||
<div class="recent-filter-bar">
|
||
<select id="recent-vault-filter">
|
||
<option value="" data-i18n="help.all_vaults">Tous les vaults</option>
|
||
</select>
|
||
</div>
|
||
<div id="recent-list" class="recent-list"></div>
|
||
<div id="recent-empty" class="recent-empty hidden">
|
||
<i
|
||
data-lucide="inbox"
|
||
style="width: 32px; height: 32px"
|
||
></i>
|
||
<span data-i18n="dashboard.no_recent">Aucun fichier récent</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Saved Searches panel -->
|
||
<div
|
||
class="sidebar-tab-panel"
|
||
id="sidebar-panel-saved"
|
||
role="tabpanel"
|
||
aria-labelledby="sidebar-tab-saved"
|
||
>
|
||
<div class="saved-filter-bar" id="saved-filter-bar">
|
||
<button class="saved-filter-pill active" data-filter="all" data-i18n="common.all">Tous</button>
|
||
<button class="saved-filter-pill" data-filter="search" data-i18n="sidebar.tab_saved">Recherches</button>
|
||
<button class="saved-filter-pill" data-filter="directory">Repertoires</button>
|
||
</div>
|
||
<div id="saved-searches-list" class="recent-list"></div>
|
||
<div id="saved-searches-empty" class="recent-empty">
|
||
<i
|
||
data-lucide="bookmark"
|
||
style="width: 32px; height: 32px"
|
||
></i>
|
||
<span>Aucune recherche sauvegardee</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- AI history panel -->
|
||
<div
|
||
class="sidebar-tab-panel"
|
||
id="sidebar-panel-ai"
|
||
role="tabpanel"
|
||
aria-labelledby="sidebar-tab-ai"
|
||
>
|
||
<div id="ai-history-list" class="recent-list"></div>
|
||
<div id="ai-history-empty" class="recent-empty hidden">
|
||
<i
|
||
data-lucide="messages-square"
|
||
style="width: 32px; height: 32px"
|
||
></i>
|
||
<span>Aucune conversation</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- User section (bottom of the sidebar, #112) -->
|
||
<div class="sidebar-user" id="sidebar-user" hidden>
|
||
<button
|
||
class="sidebar-user-profile"
|
||
id="sidebar-user-profile"
|
||
type="button"
|
||
title="Profil"
|
||
>
|
||
<span
|
||
class="sidebar-user-avatar"
|
||
id="sidebar-user-avatar"
|
||
aria-hidden="true"
|
||
></span>
|
||
<span class="sidebar-user-meta">
|
||
<span
|
||
class="sidebar-user-name user-display-name"
|
||
id="sidebar-user-name"
|
||
></span>
|
||
<span
|
||
class="sidebar-user-role"
|
||
id="sidebar-user-role"
|
||
></span>
|
||
</span>
|
||
</button>
|
||
<button
|
||
class="sidebar-user-logout"
|
||
id="sidebar-user-logout"
|
||
type="button"
|
||
title="Déconnexion"
|
||
>
|
||
<i
|
||
data-lucide="log-out"
|
||
style="width: 16px; height: 16px"
|
||
></i>
|
||
</button>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- Sidebar resize handle -->
|
||
<div
|
||
class="sidebar-resize-handle"
|
||
id="sidebar-resize-handle"
|
||
></div>
|
||
|
||
<!-- Content Wrapper (tab bar + content area) -->
|
||
<div class="vault-context-bar" id="vault-context-bar">
|
||
<span class="vault-context-pill" id="vault-context-pill"
|
||
>📂 Toutes les vaults</span
|
||
>
|
||
</div>
|
||
<div class="content-wrapper">
|
||
<!-- Tab bar -->
|
||
<div class="tab-bar" id="tab-bar" hidden>
|
||
<div class="tab-list" id="tab-list"></div>
|
||
</div>
|
||
|
||
<!-- Content -->
|
||
<main
|
||
class="content-area"
|
||
id="content-area"
|
||
aria-label="Contenu principal"
|
||
>
|
||
<div
|
||
id="dashboard-home"
|
||
class="dashboard-home"
|
||
role="region"
|
||
aria-label="Tableau de bord"
|
||
>
|
||
<!-- Dashboard Tabs -->
|
||
<div class="dashboard-tabs">
|
||
<button
|
||
class="dashboard-tab active"
|
||
data-tab="stats"
|
||
>
|
||
<i
|
||
data-lucide="bar-chart-3"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
Statistiques
|
||
</button>
|
||
<button
|
||
class="dashboard-tab"
|
||
data-tab="bookmarks"
|
||
>
|
||
<i
|
||
data-lucide="bookmark"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
Bookmarks
|
||
</button>
|
||
<button class="dashboard-tab" data-tab="recent">
|
||
<i
|
||
data-lucide="clock"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
Récents
|
||
</button>
|
||
<button class="dashboard-tab" data-tab="shared">
|
||
<i
|
||
data-lucide="share-2"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
Partagés
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Stats Panel -->
|
||
<div
|
||
id="dashboard-panel-stats"
|
||
class="dashboard-panel active"
|
||
>
|
||
<div
|
||
id="dashboard-stats-grid"
|
||
class="dashboard-stats-grid"
|
||
>
|
||
<div class="dashboard-stats-loading" data-i18n="common.loading">
|
||
Chargement...
|
||
</div>
|
||
</div>
|
||
<div
|
||
id="dashboard-conflicts-container"
|
||
style="margin-top: 16px"
|
||
></div>
|
||
<!-- Quick Help -->
|
||
<div class="quick-help" id="quick-help">
|
||
<div class="qh-header">
|
||
<i
|
||
data-lucide="keyboard"
|
||
style="width: 16px; height: 16px"
|
||
></i>
|
||
<span data-i18n="help.shortcuts_title">Raccourcis & Astuces</span>
|
||
<button
|
||
class="qh-toggle"
|
||
id="qh-toggle"
|
||
title="Masquer"
|
||
aria-label="Masquer l'aide"
|
||
>
|
||
−
|
||
</button>
|
||
</div>
|
||
<div class="qh-body" id="qh-body">
|
||
<div class="qh-row">
|
||
<span class="qh-kbd"
|
||
>Ctrl+Shift+Space</span
|
||
>
|
||
<span class="qh-desc"
|
||
>Palette de fichiers (nav.
|
||
rapide)</span
|
||
>
|
||
</div>
|
||
<div class="qh-row">
|
||
<span class="qh-kbd"
|
||
>Ctrl+Alt+Space</span
|
||
>
|
||
<span class="qh-desc">Palette de commandes</span
|
||
>
|
||
</div>
|
||
<div class="qh-row">
|
||
<span class="qh-kbd">Ctrl+K</span>
|
||
<span class="qh-desc"
|
||
>Focus barre de recherche</span
|
||
>
|
||
</div>
|
||
<div class="qh-row">
|
||
<span class="qh-kbd">/</span>
|
||
<span class="qh-desc"
|
||
>Recherche rapide (hors champ
|
||
texte)</span
|
||
>
|
||
</div>
|
||
<div class="qh-row">
|
||
<span class="qh-kbd">Ctrl+S</span>
|
||
<span class="qh-desc"
|
||
>Sauvegarder le fichier
|
||
ouvert</span
|
||
>
|
||
</div>
|
||
<div class="qh-row">
|
||
<span class="qh-kbd">Ctrl+F</span>
|
||
<span class="qh-desc"
|
||
>Rechercher dans le
|
||
document</span
|
||
>
|
||
</div>
|
||
<div class="qh-row">
|
||
<span class="qh-kbd">Esc</span>
|
||
<span class="qh-desc"
|
||
>Fermer l'editeur / modale</span
|
||
>
|
||
</div>
|
||
<div class="qh-row">
|
||
<span class="qh-kbd">Ctrl+J</span>
|
||
<span class="qh-desc"
|
||
>Completion IA inline (dans
|
||
l'editeur)</span
|
||
>
|
||
</div>
|
||
<div class="qh-row">
|
||
<span class="qh-kbd">Alt+I</span>
|
||
<span class="qh-desc"
|
||
>Insertion rapide (editeur
|
||
Forge)</span
|
||
>
|
||
</div>
|
||
<div class="qh-row">
|
||
<span class="qh-kbd">F1</span>
|
||
<span class="qh-desc"
|
||
>Aide de l'editeur Forge</span
|
||
>
|
||
</div>
|
||
<div class="qh-divider"></div>
|
||
<div class="qh-tip">
|
||
<i
|
||
data-lucide="lightbulb"
|
||
style="
|
||
width: 14px;
|
||
height: 14px;
|
||
"
|
||
></i>
|
||
<span
|
||
>Survolez un document avec
|
||
<strong>Ctrl</strong> pour un
|
||
apercu rapide. Cliquez sur
|
||
<strong>Forge</strong> dans la
|
||
barre d'outils pour l'editeur
|
||
avance. <strong>Editer</strong> et
|
||
<strong>Forge</strong> remplacent
|
||
le document en mode lecture
|
||
(retour par ✓, ✕ ou
|
||
<strong>Echap</strong>) : le
|
||
panneau de l'assistant IA reste
|
||
accessible a cote.</span
|
||
>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Bookmarks Panel -->
|
||
<div
|
||
id="dashboard-panel-bookmarks"
|
||
class="dashboard-panel"
|
||
>
|
||
<div
|
||
id="dashboard-bookmarks-grid"
|
||
class="dashboard-recent-grid"
|
||
></div>
|
||
<div
|
||
id="dashboard-bookmarks-empty"
|
||
class="dashboard-recent-empty"
|
||
>
|
||
<i data-lucide="pin"></i>
|
||
<span>Aucun bookmark</span>
|
||
<p data-i18n="auto.c089d968">
|
||
Épinglez des fichiers pour les retrouver
|
||
ici.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Recent Panel -->
|
||
<div
|
||
id="dashboard-panel-recent"
|
||
class="dashboard-panel"
|
||
>
|
||
<div class="dashboard-header">
|
||
<div class="dashboard-title-row">
|
||
<span
|
||
id="dashboard-count"
|
||
class="dashboard-badge"
|
||
></span>
|
||
</div>
|
||
</div>
|
||
<div
|
||
id="dashboard-recent-grid"
|
||
class="dashboard-recent-grid"
|
||
></div>
|
||
<div
|
||
id="dashboard-loading"
|
||
class="dashboard-loading"
|
||
>
|
||
<div class="skeleton-card"></div>
|
||
<div class="skeleton-card"></div>
|
||
<div class="skeleton-card"></div>
|
||
<div class="skeleton-card"></div>
|
||
<div class="skeleton-card"></div>
|
||
<div class="skeleton-card"></div>
|
||
</div>
|
||
<div
|
||
id="dashboard-recent-empty"
|
||
class="dashboard-recent-empty hidden"
|
||
>
|
||
<i data-lucide="inbox"></i>
|
||
<span data-i18n="dashboard.no_recent">Aucun fichier récent</span>
|
||
<p data-i18n="auto.753d30a8">
|
||
Ouvrez un fichier pour le voir
|
||
apparaître ici
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Shared Panel -->
|
||
<div
|
||
id="dashboard-panel-shared"
|
||
class="dashboard-panel"
|
||
>
|
||
<div
|
||
id="dashboard-shared-grid"
|
||
class="dashboard-recent-grid"
|
||
></div>
|
||
<div
|
||
id="dashboard-shared-empty"
|
||
class="dashboard-recent-empty"
|
||
>
|
||
<i data-lucide="share-2"></i>
|
||
<span data-i18n="dashboard.no_shares">Aucun document partagé</span>
|
||
<p data-i18n="auto.e61598c1">
|
||
Partagez un document pour le voir
|
||
apparaître ici
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
<!-- /content-wrapper -->
|
||
|
||
<!-- Right sidebar resize handle -->
|
||
<div
|
||
class="right-sidebar-resize-handle"
|
||
id="right-sidebar-resize-handle"
|
||
></div>
|
||
|
||
<!-- Right Sidebar (Outline/TOC) -->
|
||
<aside
|
||
class="right-sidebar hidden"
|
||
id="right-sidebar"
|
||
role="complementary"
|
||
aria-label="Table des matières"
|
||
>
|
||
<div class="right-sidebar-header">
|
||
<h2 class="right-sidebar-title" data-i18n="viewer.on_this_page">SUR CETTE PAGE</h2>
|
||
<button
|
||
class="right-sidebar-toggle-btn"
|
||
id="right-sidebar-toggle-btn"
|
||
type="button"
|
||
title="Masquer le panneau"
|
||
aria-label="Masquer le panneau"
|
||
>
|
||
<i
|
||
data-lucide="chevron-right"
|
||
style="width: 16px; height: 16px"
|
||
></i>
|
||
</button>
|
||
</div>
|
||
<div class="outline-panel" id="outline-panel">
|
||
<nav
|
||
class="outline-list"
|
||
id="outline-list"
|
||
role="navigation"
|
||
aria-label="Navigation dans le document"
|
||
>
|
||
<!-- Outline items will be injected here -->
|
||
</nav>
|
||
<div class="outline-empty" id="outline-empty" hidden>
|
||
<i
|
||
data-lucide="list"
|
||
style="width: 24px; height: 24px; opacity: 0.3"
|
||
></i>
|
||
<span data-i18n="viewer.no_headings">Aucun titre dans ce document</span>
|
||
</div>
|
||
</div>
|
||
<div class="reading-progress" id="reading-progress">
|
||
<div class="reading-progress-bar">
|
||
<div
|
||
class="reading-progress-fill"
|
||
id="reading-progress-fill"
|
||
></div>
|
||
</div>
|
||
<div
|
||
class="reading-progress-text"
|
||
id="reading-progress-text"
|
||
>
|
||
0%
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Editor Modal — the .editor-container is moved into the document
|
||
content area (inline mode, #93) when a file is being edited, so the
|
||
editor replaces the read view instead of overlaying it. -->
|
||
<div class="editor-modal" id="editor-modal">
|
||
<div class="editor-container" id="editor-container">
|
||
<div class="editor-header">
|
||
<span class="editor-brand" data-i18n="header.logo">ObsiGate</span>
|
||
<input
|
||
class="editor-title-input"
|
||
id="editor-title-input"
|
||
data-i18n-placeholder="forge.title_placeholder" placeholder="Titre..."
|
||
spellcheck="false"
|
||
title="Titre (sync avec # Heading)"
|
||
/>
|
||
<span class="editor-sep">/</span>
|
||
<input
|
||
class="editor-filename-input"
|
||
id="editor-filename-input"
|
||
placeholder="nom.md"
|
||
spellcheck="false"
|
||
title="Renommer le fichier"
|
||
/>
|
||
<div class="editor-spacer"></div>
|
||
<div class="collab-presence" id="collab-presence" style="display: none"
|
||
title="Collaboration"></div>
|
||
<span class="editor-save-dot ok" id="editor-save-dot"
|
||
><span class="dot"></span
|
||
><span id="editor-save-label">Saved</span></span
|
||
>
|
||
<button
|
||
class="editor-btn"
|
||
id="editor-fullscreen"
|
||
title="Plein écran"
|
||
aria-label="Plein écran"
|
||
data-i18n-attr="title:editor.fullscreen"
|
||
>
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/></svg>
|
||
</button>
|
||
<button
|
||
class="editor-btn danger"
|
||
id="editor-delete"
|
||
title="Supprimer"
|
||
aria-label="Supprimer"
|
||
>
|
||
🗑
|
||
</button>
|
||
<button
|
||
class="editor-btn"
|
||
id="editor-cancel"
|
||
title="Annuler"
|
||
aria-label="Annuler"
|
||
>
|
||
✕
|
||
</button>
|
||
<button
|
||
class="editor-btn primary"
|
||
id="editor-save"
|
||
title="Sauvegarder"
|
||
aria-label="Sauvegarder"
|
||
>
|
||
✓
|
||
</button>
|
||
</div>
|
||
<div class="editor-body editor-body-cm" id="editor-body">
|
||
<div id="ai-toolbar-container"></div>
|
||
</div>
|
||
<div class="editor-footer">
|
||
<span class="editor-stat-file" id="editor-stat-file"></span>
|
||
<span class="editor-stat-words" id="editor-stat-words"
|
||
>0 words</span
|
||
>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Configurations Modal -->
|
||
<div class="editor-modal" id="config-modal">
|
||
<div class="editor-container">
|
||
<div class="editor-header">
|
||
<button
|
||
class="help-hamburger"
|
||
id="config-hamburger"
|
||
data-i18n-attr="title:config.toc_toggle;aria-label:config.toc_toggle"
|
||
title="Afficher le sommaire"
|
||
aria-label="Afficher le sommaire"
|
||
>
|
||
<i
|
||
data-lucide="menu"
|
||
style="width: 18px; height: 18px"
|
||
></i>
|
||
</button>
|
||
<div class="editor-title" data-i18n="header.menu_config">Configurations</div>
|
||
<div class="editor-actions">
|
||
<button
|
||
class="editor-btn"
|
||
id="config-close"
|
||
title="Fermer"
|
||
aria-label="Fermer"
|
||
>
|
||
<i
|
||
data-lucide="x"
|
||
style="width: 16px; height: 16px"
|
||
></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="editor-body help-body" id="config-body">
|
||
<nav class="help-nav" id="config-nav">
|
||
<div class="help-nav-header">
|
||
<div class="help-nav-title" data-i18n="config.title">Configuration</div>
|
||
<button
|
||
class="help-toc-close"
|
||
id="config-toc-close"
|
||
data-i18n-attr="aria-label:config.toc_close"
|
||
aria-label="Fermer le sommaire"
|
||
>
|
||
<i
|
||
data-lucide="x"
|
||
style="width: 16px; height: 16px"
|
||
></i>
|
||
</button>
|
||
</div>
|
||
<div class="help-nav-search-wrap">
|
||
<input
|
||
type="text"
|
||
class="help-nav-search"
|
||
id="config-nav-search"
|
||
data-i18n-placeholder="settings.search_placeholder" placeholder="Rechercher..."
|
||
autocomplete="off"
|
||
spellcheck="false"
|
||
/>
|
||
<button
|
||
class="help-search-clear"
|
||
id="config-search-clear"
|
||
title="Effacer"
|
||
>
|
||
X
|
||
</button>
|
||
</div>
|
||
<ul class="help-nav-list">
|
||
<li><a href="#cfg-profile" class="help-nav-link" data-i18n="settings.profile"></a></li>
|
||
<li><a href="#cfg-security" class="help-nav-link" data-i18n="settings.security"></a></li>
|
||
<li><a href="#cfg-themes" class="help-nav-link" data-i18n="settings.themes"></a></li>
|
||
<li><a href="#cfg-search" class="help-nav-link" data-i18n="help.nav_search"></a></li>
|
||
<li><a href="#cfg-recent" class="help-nav-link" data-i18n="config.section_recent"></a></li>
|
||
<li><a href="#cfg-tags" class="help-nav-link" data-i18n="config.section_tags"></a></li>
|
||
<li><a href="#cfg-hidden-files" class="help-nav-link" data-i18n="config.section_hidden"></a></li>
|
||
<li><a href="#cfg-desktop-roots" class="help-nav-link" data-i18n="config.section_desktop"></a></li>
|
||
<li><a href="#cfg-sync" class="help-nav-link" data-i18n="settings.sync"></a></li>
|
||
<li><a href="#cfg-backend-settings" class="help-nav-link" data-i18n="config.section_backend"></a></li>
|
||
<li><a href="#cfg-diags" class="help-nav-link" data-i18n="settings.diagnostics"></a></li>
|
||
<li><a href="#cfg-ai" class="help-nav-link" data-i18n="settings.ai"></a></li>
|
||
<li><a href="#cfg-sources" class="help-nav-link" data-i18n="config.section_sources">Sources connectées</a></li>
|
||
<li><a href="#cfg-tokens" class="help-nav-link" data-i18n="config.nav_tokens">🔑 Clés API & MCP</a></li>
|
||
<li><a href="#cfg-push" class="help-nav-link" data-i18n="config.section_push">Notifications push</a></li>
|
||
<li><a href="#cfg-webhooks" class="help-nav-link" data-i18n="config.section_webhooks"></a></li>
|
||
<li><a href="#cfg-partages-publics" class="help-nav-link" data-i18n="config.section_shares"></a></li>
|
||
<li><a href="#cfg-plugins" class="help-nav-link" data-i18n="config.section_plugins">🧩 Plugins</a></li>
|
||
<li><a href="#cfg-about" class="help-nav-link" data-i18n="settings.about"></a></li>
|
||
</ul>
|
||
</nav>
|
||
<div class="help-content" id="config-scroll">
|
||
<div class="config-content">
|
||
<!-- Profil -->
|
||
<section
|
||
class="config-section help-section"
|
||
id="cfg-profile"
|
||
>
|
||
<h2 data-i18n="config.profile">👤 Profil utilisateur</h2>
|
||
<p class="config-description" data-i18n="config.profile_desc">
|
||
Préférences personnelles pour personnaliser
|
||
l'expérience ObsiGate.
|
||
</p>
|
||
<div class="profile-form" id="profile-form">
|
||
<div class="profile-field profile-avatar-field" id="profile-avatar-field">
|
||
<span class="profile-avatar-label" id="profile-avatar-label" data-i18n="config.avatar_label">Photo de profil</span>
|
||
<div class="profile-avatar-row">
|
||
<div
|
||
class="profile-avatar-preview"
|
||
id="profile-avatar-preview"
|
||
data-i18n-attr="title:config.avatar_choose;aria-label:config.avatar_choose"
|
||
title="Choisir une image"
|
||
aria-label="Choisir une image"
|
||
>
|
||
<img id="profile-avatar-img" alt="" hidden />
|
||
<span class="profile-avatar-initials" id="profile-avatar-initials" aria-hidden="true"></span>
|
||
<button
|
||
type="button"
|
||
class="profile-avatar-overlay"
|
||
id="profile-avatar-overlay"
|
||
data-i18n-attr="title:config.avatar_choose;aria-label:config.avatar_choose"
|
||
title="Choisir une image"
|
||
aria-label="Choisir une image"
|
||
>
|
||
<i data-lucide="camera" style="width: 18px; height: 18px"></i>
|
||
</button>
|
||
</div>
|
||
<div class="profile-avatar-actions">
|
||
<button type="button" class="config-btn-secondary" id="profile-avatar-choose" data-i18n="config.avatar_choose">Choisir une image</button>
|
||
<button type="button" class="config-btn-secondary profile-avatar-remove-btn" id="profile-avatar-remove" data-i18n="config.avatar_remove" hidden>Supprimer la photo</button>
|
||
<input
|
||
type="file"
|
||
id="profile-avatar-input"
|
||
accept="image/png,image/jpeg,image/webp"
|
||
hidden
|
||
/>
|
||
<span class="profile-hint" data-i18n="config.avatar_hint">PNG, JPG ou WEBP — image carrée recadrée et réduite à 256 px.</span>
|
||
</div>
|
||
</div>
|
||
<span class="profile-avatar-label" data-i18n="config.avatar_presets_label">Avatars prédéfinis</span>
|
||
<div
|
||
class="profile-avatar-presets"
|
||
id="profile-avatar-presets"
|
||
role="radiogroup"
|
||
data-i18n-attr="aria-label:config.avatar_presets_label"
|
||
aria-label="Avatars prédéfinis"
|
||
>
|
||
<button type="button" class="profile-avatar-preset" data-avatar="Chat.jpg" role="radio" aria-checked="false"><img src="/static/icons/avatar/Chat.jpg" alt="" loading="lazy" /></button>
|
||
<button type="button" class="profile-avatar-preset" data-avatar="chien.jpg" role="radio" aria-checked="false"><img src="/static/icons/avatar/chien.jpg" alt="" loading="lazy" /></button>
|
||
<button type="button" class="profile-avatar-preset" data-avatar="elephan.jpg" role="radio" aria-checked="false"><img src="/static/icons/avatar/elephan.jpg" alt="" loading="lazy" /></button>
|
||
<button type="button" class="profile-avatar-preset" data-avatar="hibou.jpg" role="radio" aria-checked="false"><img src="/static/icons/avatar/hibou.jpg" alt="" loading="lazy" /></button>
|
||
<button type="button" class="profile-avatar-preset" data-avatar="koala.jpg" role="radio" aria-checked="false"><img src="/static/icons/avatar/koala.jpg" alt="" loading="lazy" /></button>
|
||
<button type="button" class="profile-avatar-preset" data-avatar="lapin.jpg" role="radio" aria-checked="false"><img src="/static/icons/avatar/lapin.jpg" alt="" loading="lazy" /></button>
|
||
<button type="button" class="profile-avatar-preset" data-avatar="lion.jpg" role="radio" aria-checked="false"><img src="/static/icons/avatar/lion.jpg" alt="" loading="lazy" /></button>
|
||
<button type="button" class="profile-avatar-preset" data-avatar="ours.jpg" role="radio" aria-checked="false"><img src="/static/icons/avatar/ours.jpg" alt="" loading="lazy" /></button>
|
||
<button type="button" class="profile-avatar-preset" data-avatar="penda.jpg" role="radio" aria-checked="false"><img src="/static/icons/avatar/penda.jpg" alt="" loading="lazy" /></button>
|
||
<button type="button" class="profile-avatar-preset" data-avatar="pingouin.jpg" role="radio" aria-checked="false"><img src="/static/icons/avatar/pingouin.jpg" alt="" loading="lazy" /></button>
|
||
<button type="button" class="profile-avatar-preset" data-avatar="raton.jpg" role="radio" aria-checked="false"><img src="/static/icons/avatar/raton.jpg" alt="" loading="lazy" /></button>
|
||
<button type="button" class="profile-avatar-preset" data-avatar="tigre.jpg" role="radio" aria-checked="false"><img src="/static/icons/avatar/tigre.jpg" alt="" loading="lazy" /></button>
|
||
</div>
|
||
<p class="profile-avatar-error hidden" id="profile-avatar-error" role="alert"></p>
|
||
</div>
|
||
<div class="profile-field">
|
||
<label for="profile-lang" data-i18n="config.lang_label">Langue préférée</label>
|
||
<select id="profile-lang" class="config-select">
|
||
<option value="fr">Francais</option>
|
||
<option value="en">English</option>
|
||
<option value="es">Espanol</option>
|
||
<option value="de">Deutsch</option>
|
||
</select>
|
||
<span class="profile-hint" data-i18n="config.lang_hint">Utilisée par l'autocomplétion et le ghost text.</span>
|
||
</div>
|
||
<div class="profile-field">
|
||
<label for="profile-name" data-i18n="config.display_name">Nom d'affichage</label>
|
||
<input type="text" id="profile-name" class="config-input" placeholder="Votre nom" maxlength="60">
|
||
</div>
|
||
<button class="config-save-btn" id="profile-save" data-i18n="config.save">Enregistrer</button>
|
||
<button class="config-save-btn" id="profile-logout" style="background:var(--danger-bg);color:var(--danger);border-color:var(--danger);margin-left:8px" data-i18n="config.logout">Déconnexion</button>
|
||
<span class="profile-saved" id="profile-saved" style="display:none" data-i18n="config.saved">✓ Sauvegardé</span>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Sécurité du compte (MFA) -->
|
||
<section
|
||
class="config-section help-section"
|
||
id="cfg-security"
|
||
>
|
||
<h2 data-i18n="settings.security">🔒 Sécurité du compte</h2>
|
||
<p class="config-description" data-i18n="settings.security_desc">
|
||
Activez l'authentification à deux facteurs (2FA) pour renforcer la sécurité de votre compte.
|
||
</p>
|
||
<div id="mfa-settings">
|
||
<div id="mfa-status" class="mfa-status-section">
|
||
<div class="mfa-status-row">
|
||
<span class="mfa-status-label" data-i18n="mfa.status_label">Authentification 2FA</span>
|
||
<span id="mfa-status-badge" class="mfa-badge mfa-badge-off" data-i18n="mfa.disabled">Désactivée</span>
|
||
</div>
|
||
<div id="mfa-setup-area"></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Themes -->
|
||
<section
|
||
class="config-section help-section"
|
||
id="cfg-themes"
|
||
>
|
||
<h2 data-i18n="auto.c14b5603">🎨 Thèmes</h2>
|
||
<p class="config-description" data-i18n="auto.caac40b9">
|
||
Choisissez un thème visuel. Chaque thème
|
||
offre un mode sombre, clair, contraste élevé et sépia.
|
||
</p>
|
||
<div class="theme-grid" id="theme-grid">
|
||
<div class="config-diag-loading" data-i18n="common.loading">Chargement...</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Performance Settings - Frontend -->
|
||
<section
|
||
class="config-section help-section"
|
||
id="cfg-search"
|
||
>
|
||
<h2 data-i18n="config.section_search">Paramètres de recherche</h2>
|
||
<p class="config-description" data-i18n="auto.7e13c97a">
|
||
Ces paramètres s'appliquent immédiatement
|
||
côté client.
|
||
</p>
|
||
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-debounce"
|
||
>Délai debounce (ms)</label
|
||
>
|
||
<input
|
||
type="number"
|
||
id="cfg-debounce"
|
||
class="config-input config-input--num"
|
||
min="100"
|
||
max="2000"
|
||
step="50"
|
||
value="300"
|
||
/>
|
||
<span class="config-hint"
|
||
>Délai avant exécution de la recherche
|
||
(100-2000)</span
|
||
>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-results-per-page"
|
||
>Résultats par page</label
|
||
>
|
||
<input
|
||
type="number"
|
||
id="cfg-results-per-page"
|
||
class="config-input config-input--num"
|
||
min="10"
|
||
max="200"
|
||
step="10"
|
||
value="50"
|
||
/>
|
||
<span class="config-hint"
|
||
>Nombre de résultats affichés par page
|
||
(10-200)</span
|
||
>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-min-query"
|
||
>Longueur min. requête</label
|
||
>
|
||
<input
|
||
type="number"
|
||
id="cfg-min-query"
|
||
class="config-input config-input--num"
|
||
min="1"
|
||
max="5"
|
||
step="1"
|
||
value="2"
|
||
/>
|
||
<span class="config-hint"
|
||
>Nombre minimum de caractères avant
|
||
recherche (1-5)</span
|
||
>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-timeout"
|
||
>Timeout recherche (ms)</label
|
||
>
|
||
<input
|
||
type="number"
|
||
id="cfg-timeout"
|
||
class="config-input config-input--num"
|
||
min="5000"
|
||
max="120000"
|
||
step="5000"
|
||
value="30000"
|
||
/>
|
||
<span class="config-hint"
|
||
>Annuler la recherche après ce délai
|
||
(5000-120000)</span
|
||
>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Recent History -->
|
||
<section id="cfg-recent" class="config-section help-section">
|
||
<h2>
|
||
📋 <span data-i18n="settings.history_section">Historique récent</span>
|
||
<span class="config-badge-restart"
|
||
data-i18n="settings.no_restart_badge">Redémarrage non requis</span
|
||
>
|
||
</h2>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-recent-limit"
|
||
data-i18n="settings.history_count_label"
|
||
>Nombre de fichiers dans l'historique</label
|
||
>
|
||
<input
|
||
type="number"
|
||
id="cfg-recent-limit"
|
||
class="config-input config-input--num"
|
||
min="5"
|
||
max="100"
|
||
step="5"
|
||
value="20"
|
||
/>
|
||
<span class="config-hint"
|
||
data-i18n="settings.history_count_desc"
|
||
>Entre 5 et 100 fichiers (sauvegarde sur le serveur)</span
|
||
>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-max-backups"
|
||
data-i18n="config.max_backups_label"
|
||
>Max backups par fichier</label
|
||
>
|
||
<input
|
||
type="number"
|
||
id="cfg-max-backups"
|
||
class="config-input config-input--num"
|
||
min="1"
|
||
max="50"
|
||
step="1"
|
||
value="10"
|
||
/>
|
||
<span class="config-hint"
|
||
data-i18n="config.max_backups_desc"
|
||
>Nombre maximum de versions backup conservées par fichier (1-50)</span
|
||
>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Tag Filtering (existing) -->
|
||
<section class="config-section help-section" id="cfg-tags">
|
||
<h2 data-i18n="config.tag_filtering_label">Filtrage de tags</h2>
|
||
<p class="config-description" data-i18n="auto.e0b8a606">
|
||
Définissez les patterns de tags à masquer
|
||
dans la sidebar. Vous pouvez utiliser des
|
||
wildcards pour cibler les tags de template.
|
||
</p>
|
||
|
||
<div
|
||
class="config-filters-list"
|
||
id="config-filters-list"
|
||
></div>
|
||
|
||
<div class="config-add-pattern">
|
||
<input
|
||
type="text"
|
||
id="config-pattern-input"
|
||
placeholder="Ex: #<% ... %> ou #{{ ... }}"
|
||
class="config-input"
|
||
/>
|
||
<button id="config-add-btn"
|
||
class="config-btn-add" data-i18n="config.add_webhook">
|
||
Ajouter
|
||
</button>
|
||
</div>
|
||
|
||
<div
|
||
class="config-regex-preview"
|
||
id="config-regex-preview"
|
||
style="display: none"
|
||
>
|
||
<small
|
||
>Regex :
|
||
<code id="config-regex-code"></code
|
||
></small>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Hidden Files/Folders Configuration -->
|
||
<section id="cfg-hidden-files" class="config-section help-section">
|
||
<h2 data-i18n="auto.f8ba6127">🗂️ Fichiers cachés</h2>
|
||
<p class="config-description">
|
||
Contrôlez l'affichage des fichiers/dossiers
|
||
cachés (commençant par <code>.</code>) par
|
||
vault.
|
||
</p>
|
||
<p
|
||
class="config-hint"
|
||
style="
|
||
margin-bottom: 12px;
|
||
padding: 8px;
|
||
background: var(--background-secondary);
|
||
border-radius: 4px;
|
||
"
|
||
>
|
||
i️ <strong data-i18n="config.note">Note :</strong> Tous les fichiers
|
||
sont toujours indexés et cherchables. Ce
|
||
paramètre contrôle uniquement leur
|
||
visibilité dans l'interface.
|
||
</p>
|
||
|
||
<div id="hidden-files-vault-list">
|
||
<!-- Vault-specific settings will be injected here -->
|
||
</div>
|
||
|
||
<div
|
||
class="config-actions-row"
|
||
style="margin-top: 16px"
|
||
>
|
||
<button
|
||
class="config-btn-save"
|
||
id="cfg-save-hidden-files"
|
||
>
|
||
💾 Sauvegarder
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Desktop vaults & root directories (#159, #160) -->
|
||
<section
|
||
id="cfg-desktop-roots"
|
||
class="config-section help-section"
|
||
>
|
||
<h2 data-i18n="config.section_desktop">🖥️ Vaults & dossiers (Desktop)</h2>
|
||
<p
|
||
class="config-description"
|
||
data-i18n="config.desktop_roots_desc"
|
||
>
|
||
Ajoutez ou retirez les vaults et dossiers
|
||
racine chargés par l'application desktop.
|
||
</p>
|
||
|
||
<div
|
||
id="desktop-roots-list"
|
||
class="desktop-roots-list"
|
||
>
|
||
<!-- Racines injectées par renderDesktopRoots() -->
|
||
</div>
|
||
|
||
<div class="config-actions-row desktop-roots-actions">
|
||
<button
|
||
class="config-btn-primary config-btn-sm"
|
||
id="cfg-desktop-add-vault"
|
||
data-i18n="desktop.add_vault"
|
||
>
|
||
📁 Ajouter un vault
|
||
</button>
|
||
<button
|
||
class="config-btn-secondary config-btn-sm"
|
||
id="cfg-desktop-add-dir"
|
||
data-i18n="desktop.add_dir"
|
||
>
|
||
📂 Ajouter un dossier
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Watcher / Synchronisation -->
|
||
<section
|
||
class="config-section help-section"
|
||
id="cfg-sync"
|
||
>
|
||
<h2 data-i18n="config.section_watcher">Synchronisation automatique</h2>
|
||
<p class="config-description" data-i18n="auto.d33601dc">
|
||
Surveillance des fichiers en temps réel via
|
||
watchdog. Les modifications sont détectées
|
||
automatiquement et l'index est mis à jour
|
||
sans redémarrage.
|
||
</p>
|
||
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-watcher-enabled"
|
||
>Activer la surveillance</label
|
||
>
|
||
<label class="config-toggle">
|
||
<input
|
||
type="checkbox"
|
||
id="cfg-watcher-enabled"
|
||
checked
|
||
/>
|
||
<span
|
||
class="config-toggle-slider"
|
||
></span>
|
||
</label>
|
||
<span class="config-hint"
|
||
>Activer/désactiver la surveillance
|
||
automatique des fichiers</span
|
||
>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-watcher-polling"
|
||
>Mode polling (fallback)</label
|
||
>
|
||
<label class="config-toggle">
|
||
<input
|
||
type="checkbox"
|
||
id="cfg-watcher-polling"
|
||
/>
|
||
<span
|
||
class="config-toggle-slider"
|
||
></span>
|
||
</label>
|
||
<span class="config-hint"
|
||
>Forcer le mode polling au lieu de
|
||
inotify natif (utile si le mode natif ne
|
||
fonctionne pas)</span
|
||
>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-watcher-interval"
|
||
>Intervalle polling (s)</label
|
||
>
|
||
<input
|
||
type="number"
|
||
id="cfg-watcher-interval"
|
||
class="config-input config-input--num"
|
||
min="1"
|
||
max="30"
|
||
step="1"
|
||
value="5"
|
||
/>
|
||
<span class="config-hint"
|
||
>Intervalle de scrutation en mode
|
||
polling (1-30 secondes)</span
|
||
>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-watcher-debounce"
|
||
>Debounce (s)</label
|
||
>
|
||
<input
|
||
type="number"
|
||
id="cfg-watcher-debounce"
|
||
class="config-input config-input--num"
|
||
min="0.5"
|
||
max="10"
|
||
step="0.5"
|
||
value="2"
|
||
/>
|
||
<span class="config-hint"
|
||
>Délai avant traitement des changements
|
||
groupés (0.5-10 secondes)</span
|
||
>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Performance Settings - Backend -->
|
||
<section id="cfg-backend-settings" class="config-section help-section">
|
||
<h2 data-i18n="config.section_backend">
|
||
Paramètres backend
|
||
<span class="config-badge-restart"
|
||
data-i18n="config.restart_required">Redémarrage requis</span
|
||
>
|
||
</h2>
|
||
<p class="config-description" data-i18n="auto.b114ed1c">
|
||
Ces paramètres sont sauvegardés sur le
|
||
serveur. Certains nécessitent un redémarrage
|
||
ou une réindexation.
|
||
</p>
|
||
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-workers"
|
||
data-i18n="config.search_workers_label"
|
||
>Workers de recherche</label
|
||
>
|
||
<input
|
||
type="number"
|
||
id="cfg-workers"
|
||
class="config-input config-input--num"
|
||
min="1"
|
||
max="8"
|
||
step="1"
|
||
value="2"
|
||
/>
|
||
<span class="config-hint"
|
||
data-i18n="config.search_workers_hint"
|
||
>Threads dédiés à la recherche (1-8)</span
|
||
>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-max-content"
|
||
data-i18n="config.max_content_label"
|
||
>Taille max contenu (octets)</label
|
||
>
|
||
<input
|
||
type="number"
|
||
id="cfg-max-content"
|
||
class="config-input config-input--num"
|
||
min="10000"
|
||
max="1000000"
|
||
step="10000"
|
||
value="100000"
|
||
/>
|
||
<span class="config-hint"
|
||
data-i18n="config.max_content_hint"
|
||
>Contenu indexé par fichier (10K-1M). Réindexation requise.</span
|
||
>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-title-boost"
|
||
data-i18n="config.title_boost_label"
|
||
>Boost titre</label
|
||
>
|
||
<input
|
||
type="number"
|
||
id="cfg-title-boost"
|
||
class="config-input config-input--num"
|
||
min="0.1"
|
||
max="10"
|
||
step="0.5"
|
||
value="3"
|
||
/>
|
||
<span class="config-hint"
|
||
data-i18n="config.title_boost_hint"
|
||
>Multiplicateur de pertinence pour les correspondances dans le titre</span
|
||
>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-tag-boost"
|
||
data-i18n="config.tag_boost_label"
|
||
>Boost tags</label
|
||
>
|
||
<input
|
||
type="number"
|
||
id="cfg-tag-boost"
|
||
class="config-input config-input--num"
|
||
min="0.1"
|
||
max="10"
|
||
step="0.5"
|
||
value="2"
|
||
/>
|
||
<span class="config-hint"
|
||
data-i18n="config.tag_boost_hint"
|
||
>Multiplicateur de pertinence pour les correspondances dans les tags</span
|
||
>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-prefix-exp"
|
||
data-i18n="config.prefix_expansions_label"
|
||
>Expansions préfixe max</label
|
||
>
|
||
<input
|
||
type="number"
|
||
id="cfg-prefix-exp"
|
||
class="config-input config-input--num"
|
||
min="10"
|
||
max="200"
|
||
step="10"
|
||
value="50"
|
||
/>
|
||
<span class="config-hint"
|
||
data-i18n="config.prefix_expansions_hint"
|
||
>Nombre max de tokens élargis par préfixe (10-200)</span
|
||
>
|
||
</div>
|
||
<div class="config-actions-row">
|
||
<button class="config-btn-save"
|
||
id="cfg-save-backend" data-i18n="help.shortcut_save">
|
||
Sauvegarder
|
||
</button>
|
||
<button class="config-btn-secondary"
|
||
id="cfg-reindex" data-i18n="settings.reindex">
|
||
Forcer réindexation
|
||
</button>
|
||
<button class="config-btn-secondary"
|
||
id="cfg-reset-defaults" data-i18n="config.reset">
|
||
Réinitialiser
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Diagnostics -->
|
||
<section
|
||
class="config-section help-section"
|
||
id="cfg-diags"
|
||
>
|
||
<h2 data-i18n="config.diagnostics">Diagnostics</h2>
|
||
<p class="config-description">
|
||
Statistiques de l'index et du moteur de
|
||
recherche.
|
||
</p>
|
||
<div
|
||
id="config-diagnostics"
|
||
class="config-diagnostics"
|
||
>
|
||
<div class="config-diag-loading" data-i18n="common.loading">
|
||
Chargement...
|
||
</div>
|
||
</div>
|
||
<button class="config-btn-secondary"
|
||
id="cfg-refresh-diag"
|
||
style="margin-top: 8px" data-i18n="header.refresh">
|
||
Rafraîchir
|
||
</button>
|
||
</section>
|
||
|
||
<!-- Cles API AI (#104 — accordéon fournisseurs) -->
|
||
<section
|
||
class="config-section help-section"
|
||
id="cfg-ai"
|
||
>
|
||
<div class="ai-keys-header">
|
||
<div class="ai-keys-header-text">
|
||
<h2 data-i18n="config.section_cles-api-intelligence-artificielle">🤖 Cles API Intelligence Artificielle</h2>
|
||
<p class="config-description" data-i18n="config.ai_header_desc">
|
||
Configurez vos cles API fournisseur par fournisseur. Dépliez une carte pour saisir une
|
||
clé, puis cliquez sur <strong data-i18n="config.test">Tester</strong> pour charger les
|
||
modeles.
|
||
</p>
|
||
</div>
|
||
<div class="ai-keys-search">
|
||
<i data-lucide="search" class="icon"></i>
|
||
<input
|
||
type="search"
|
||
id="cfg-ai-search"
|
||
data-i18n-placeholder="config.ai_search_placeholder"
|
||
placeholder="Rechercher un fournisseur…"
|
||
autocomplete="off"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ai-default-card">
|
||
<div class="ai-card-title" data-i18n="config.ai_default_section">Configuration par defaut</div>
|
||
<div class="ai-default-grid">
|
||
<div class="ai-field">
|
||
<label
|
||
class="ai-field-label"
|
||
for="cfg-ai-default-provider"
|
||
data-i18n="config.ai_default_provider"
|
||
>Fournisseur par defaut</label
|
||
>
|
||
<select
|
||
id="cfg-ai-default-provider"
|
||
class="config-select"
|
||
>
|
||
<option value="">--</option>
|
||
</select>
|
||
</div>
|
||
<div class="ai-field">
|
||
<label
|
||
class="ai-field-label"
|
||
for="cfg-ai-default-model"
|
||
data-i18n="config.ai_default_model"
|
||
>Modele par defaut</label
|
||
>
|
||
<select
|
||
id="cfg-ai-default-model"
|
||
class="config-select"
|
||
>
|
||
<option value="">--</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="ai-field">
|
||
<span
|
||
class="ai-field-label"
|
||
data-i18n="config.ai_capabilities"
|
||
>Capacites du modele</span
|
||
>
|
||
<div
|
||
id="cfg-ai-default-model-caps"
|
||
class="ai-caps-badges"
|
||
></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ai-providers-header">
|
||
<h3
|
||
class="ai-card-title"
|
||
data-i18n="config.ai_providers_title"
|
||
>
|
||
Fournisseurs d'API
|
||
</h3>
|
||
</div>
|
||
<div id="cfg-ai-providers" class="ai-providers-list"></div>
|
||
<div
|
||
id="cfg-ai-providers-empty"
|
||
class="ai-providers-empty hidden"
|
||
data-i18n="config.ai_providers_empty"
|
||
>
|
||
Aucun fournisseur ne correspond
|
||
</div>
|
||
|
||
<div class="ai-keys-footer">
|
||
<button class="config-btn-save"
|
||
id="cfg-save-ai-keys" data-i18n="help.shortcut_save">
|
||
Sauvegarder
|
||
</button>
|
||
<button class="config-btn-secondary"
|
||
id="cfg-test-ai-keys" data-i18n="config.test">
|
||
Tester
|
||
</button>
|
||
<span
|
||
id="cfg-ai-status"
|
||
class="ai-keys-status"
|
||
></span>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Connected sources & keyed web search (#103) -->
|
||
<section
|
||
class="config-section help-section"
|
||
id="cfg-sources"
|
||
>
|
||
<h2 data-i18n="config.section_sources"
|
||
>Sources connectées & recherche web</h2
|
||
>
|
||
<p
|
||
class="config-description"
|
||
data-i18n="config.sources_desc"
|
||
>
|
||
Clés utilisées par les outils de l'Assistant IA
|
||
(recherche web à clé, Gitea, GitHub). Elles sont
|
||
stockées localement et priment sur les variables
|
||
d'environnement.
|
||
</p>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-tavily-key"
|
||
>Tavily API Key</label
|
||
>
|
||
<input
|
||
type="password"
|
||
id="cfg-tavily-key"
|
||
class="config-input"
|
||
placeholder="tvly-..."
|
||
autocomplete="off"
|
||
/>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-brave-key"
|
||
>Brave Search API Key</label
|
||
>
|
||
<input
|
||
type="password"
|
||
id="cfg-brave-key"
|
||
class="config-input"
|
||
placeholder="BSA..."
|
||
autocomplete="off"
|
||
/>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-serpapi-key"
|
||
>SerpAPI Key</label
|
||
>
|
||
<input
|
||
type="password"
|
||
id="cfg-serpapi-key"
|
||
class="config-input"
|
||
placeholder="..."
|
||
autocomplete="off"
|
||
/>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-exa-key"
|
||
>Exa API Key</label
|
||
>
|
||
<input
|
||
type="password"
|
||
id="cfg-exa-key"
|
||
class="config-input"
|
||
placeholder="..."
|
||
autocomplete="off"
|
||
/>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-gitea-url"
|
||
data-i18n="config.gitea_url"
|
||
>URL Gitea</label
|
||
>
|
||
<input
|
||
type="text"
|
||
id="cfg-gitea-url"
|
||
class="config-input"
|
||
placeholder="https://git.example.net"
|
||
autocomplete="off"
|
||
/>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-gitea-token"
|
||
>Gitea Token</label
|
||
>
|
||
<input
|
||
type="password"
|
||
id="cfg-gitea-token"
|
||
class="config-input"
|
||
placeholder="token personnel..."
|
||
autocomplete="off"
|
||
/>
|
||
</div>
|
||
<div class="config-row">
|
||
<label
|
||
class="config-label"
|
||
for="cfg-github-token"
|
||
>GitHub Token</label
|
||
>
|
||
<input
|
||
type="password"
|
||
id="cfg-github-token"
|
||
class="config-input"
|
||
placeholder="ghp_..."
|
||
autocomplete="off"
|
||
/>
|
||
</div>
|
||
<div
|
||
class="config-actions-row"
|
||
style="margin-top: 16px"
|
||
>
|
||
<button class="config-btn-save"
|
||
id="cfg-save-tool-keys" data-i18n="help.shortcut_save">
|
||
Sauvegarder
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Clés API / MCP (#107) -->
|
||
<section id="cfg-tokens" class="config-section help-section">
|
||
<h2 data-i18n="config.section_tokens">🔑 Clés API & MCP</h2>
|
||
<p class="config-description" data-i18n="config.tokens_desc">
|
||
Jetons longue durée pour l'API REST et le serveur MCP.
|
||
La même clé fonctionne pour les deux (en-tête Authorization: Bearer).
|
||
</p>
|
||
<div id="tokens-list"></div>
|
||
<div class="config-add-row" id="tokens-create-row">
|
||
<input
|
||
type="text"
|
||
id="token-name-input"
|
||
data-i18n-placeholder="config.token_name_placeholder"
|
||
placeholder="Nom (ex: Claude Desktop)"
|
||
class="config-input"
|
||
style="width: 180px"
|
||
/>
|
||
<select id="token-expiry-select" class="config-input" style="width: 140px">
|
||
<option value="1d" data-i18n="config.token_expiry_1d">1 jour</option>
|
||
<option value="30d" selected data-i18n="config.token_expiry_30d">1 mois</option>
|
||
<option value="180d" data-i18n="config.token_expiry_180d">6 mois</option>
|
||
<option value="365d" data-i18n="config.token_expiry_365d">1 an</option>
|
||
<option value="never" data-i18n="config.token_expiry_never">Sans fin</option>
|
||
</select>
|
||
<button class="config-btn-add" id="token-create-btn" data-i18n="config.token_create">Créer une clé</button>
|
||
</div>
|
||
<div id="token-secret-area" class="token-secret-area hidden">
|
||
<p class="config-description" data-i18n="config.token_secret_warning">Copiez cette clé maintenant — elle ne sera plus jamais affichée.</p>
|
||
<textarea id="token-secret-value" class="config-input token-secret-text" rows="3" readonly></textarea>
|
||
<div class="config-add-row">
|
||
<button class="config-btn-add" id="token-copy-btn" data-i18n="config.token_copy">Copier</button>
|
||
<button class="config-btn-add" id="token-dismiss-btn" data-i18n="config.token_done">Terminé</button>
|
||
</div>
|
||
</div>
|
||
<div class="qh-tip" style="margin-top:10px">
|
||
<strong data-i18n="config.token_usage">Utilisation</strong>
|
||
<span data-i18n="config.token_usage_detail">: en-tête `Authorization: Bearer ` sur l'API, et config MCP (`claude_desktop_config.json`) : `{"mcpServers":{"obsigate":{"url":"<base>/mcp","headers":{"Authorization":"Bearer "}}}}`.</span>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Notifications push -->
|
||
<section
|
||
class="config-section help-section"
|
||
id="cfg-push"
|
||
>
|
||
<h2 data-i18n="config.section_push">🔔 Notifications push</h2>
|
||
<p class="config-description">
|
||
Recevez des notifications web push
|
||
lorsque des fichiers changent dans vos
|
||
vaults, même si ObsiGate n'est pas ouvert.
|
||
</p>
|
||
<div id="push-settings">
|
||
<div class="config-diag-loading" data-i18n="common.loading">
|
||
Chargement...
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Webhooks -->
|
||
<section id="cfg-webhooks" class="config-section help-section">
|
||
<h2>🔔 Webhooks</h2>
|
||
<p class="config-description">
|
||
Notifications HTTP vers des services
|
||
externes lors des changements de fichiers.
|
||
</p>
|
||
<div id="webhooks-list"></div>
|
||
<div class="config-add-row">
|
||
<input
|
||
type="text"
|
||
id="webhook-name-input"
|
||
data-i18n-placeholder="config.webhook_name_placeholder" placeholder="Nom"
|
||
class="config-input"
|
||
style="width: 100px"
|
||
/>
|
||
<input
|
||
type="text"
|
||
id="webhook-url-input"
|
||
placeholder="https://..."
|
||
class="config-input"
|
||
style="flex: 1"
|
||
/>
|
||
<button id="webhook-add-btn"
|
||
class="config-btn config-btn-add" data-i18n="config.add_webhook">
|
||
Ajouter
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Partages publics -->
|
||
<section class="config-section help-section" id="cfg-partages-publics">
|
||
<h2>📤 Partages publics</h2>
|
||
<p class="config-description">
|
||
Liens de partage publics pour des documents
|
||
(lecture seule, sans authentification).
|
||
</p>
|
||
<div id="shares-list"></div>
|
||
</section>
|
||
|
||
<!-- Plugins -->
|
||
<section
|
||
class="config-section help-section"
|
||
id="cfg-plugins"
|
||
>
|
||
<h2 data-i18n="config.section_plugins">🧩 Plugins</h2>
|
||
<p class="config-description" data-i18n="plugins.description">
|
||
Extend ObsiGate with custom renderers, search filters, and editor actions.
|
||
</p>
|
||
</section>
|
||
|
||
<!-- À propos -->
|
||
<section
|
||
class="config-section help-section"
|
||
id="cfg-about"
|
||
>
|
||
<h2 data-i18n="auto.a3319169">📦 À propos</h2>
|
||
<div
|
||
id="config-about"
|
||
class="config-diagnostics"
|
||
>
|
||
<div class="config-diag-loading" data-i18n="common.loading">
|
||
Chargement...
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Graph View Modal -->
|
||
<div class="editor-modal" id="graph-modal">
|
||
<div
|
||
class="editor-container"
|
||
style="
|
||
max-width: 95vw;
|
||
width: 1000px;
|
||
height: 85vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
"
|
||
>
|
||
<!-- Header: 2 lignes — nav+titre+contrôles | recherche -->
|
||
<div
|
||
class="editor-header"
|
||
style="
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid var(--border-color);
|
||
"
|
||
>
|
||
<!-- Line 1: nav + title + controls -->
|
||
<div
|
||
style="
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
min-width: 0;
|
||
"
|
||
>
|
||
<button
|
||
class="editor-btn"
|
||
id="graph-nav-back"
|
||
title="Retour"
|
||
style="
|
||
font-size: 0.75rem;
|
||
padding: 2px 4px;
|
||
opacity: 0.3;
|
||
flex-shrink: 0;
|
||
min-width: 22px;
|
||
"
|
||
disabled
|
||
>
|
||
←
|
||
</button>
|
||
<button
|
||
class="editor-btn"
|
||
id="graph-nav-fwd"
|
||
title="Avancer"
|
||
style="
|
||
font-size: 0.75rem;
|
||
padding: 2px 4px;
|
||
opacity: 0.3;
|
||
flex-shrink: 0;
|
||
min-width: 22px;
|
||
"
|
||
disabled
|
||
>
|
||
→
|
||
</button>
|
||
<button
|
||
class="editor-btn"
|
||
id="graph-nav-up"
|
||
title="Dossier parent"
|
||
style="
|
||
font-size: 0.65rem;
|
||
padding: 2px 5px;
|
||
flex-shrink: 0;
|
||
display: none;
|
||
"
|
||
>
|
||
↑
|
||
</button>
|
||
<div
|
||
class="editor-title"
|
||
id="graph-title"
|
||
style="
|
||
font-weight: 600;
|
||
font-size: 0.85rem;
|
||
flex: 1;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
"
|
||
>
|
||
Vue Graphique
|
||
</div>
|
||
<button
|
||
class="editor-btn"
|
||
id="graph-full-vault"
|
||
title="Vue complète du vault"
|
||
style="
|
||
font-size: 0.65rem;
|
||
padding: 3px 6px;
|
||
flex-shrink: 0;
|
||
"
|
||
>
|
||
🌐 Tout
|
||
</button>
|
||
<span
|
||
style="
|
||
font-size: 0.6rem;
|
||
color: var(--text-muted);
|
||
flex-shrink: 0;
|
||
"
|
||
>Prof.</span
|
||
>
|
||
<input
|
||
type="range"
|
||
id="graph-depth"
|
||
min="0"
|
||
max="3"
|
||
value="1"
|
||
style="width: 40px; flex-shrink: 0"
|
||
title="Profondeur"
|
||
/>
|
||
<button
|
||
class="editor-btn"
|
||
id="graph-zoom-out"
|
||
title="Zoom arrière"
|
||
style="flex-shrink: 0"
|
||
>
|
||
<i
|
||
data-lucide="zoom-out"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
</button>
|
||
<button
|
||
class="editor-btn"
|
||
id="graph-zoom-in"
|
||
title="Zoom avant"
|
||
style="flex-shrink: 0"
|
||
>
|
||
<i
|
||
data-lucide="zoom-in"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
</button>
|
||
<button
|
||
class="editor-btn"
|
||
id="graph-reset"
|
||
title="Réinitialiser"
|
||
style="flex-shrink: 0"
|
||
>
|
||
<i
|
||
data-lucide="maximize"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
</button>
|
||
<button
|
||
class="editor-btn"
|
||
id="graph-export"
|
||
title="Exporter PNG"
|
||
style="flex-shrink: 0"
|
||
>
|
||
<i
|
||
data-lucide="download"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
</button>
|
||
<button
|
||
class="editor-btn"
|
||
id="graph-fullscreen"
|
||
title="Plein écran"
|
||
style="flex-shrink: 0"
|
||
>
|
||
<i
|
||
data-lucide="expand"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
</button>
|
||
<button
|
||
class="editor-btn"
|
||
id="graph-close"
|
||
title="Fermer"
|
||
style="flex-shrink: 0"
|
||
>
|
||
<i
|
||
data-lucide="x"
|
||
style="width: 15px; height: 15px"
|
||
></i>
|
||
</button>
|
||
</div>
|
||
<!-- Line 2: centered search -->
|
||
<div style="display: flex; justify-content: center">
|
||
<div
|
||
style="
|
||
position: relative;
|
||
width: 100%;
|
||
max-width: 450px;
|
||
"
|
||
>
|
||
<i
|
||
data-lucide="search"
|
||
style="
|
||
position: absolute;
|
||
left: 10px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 14px;
|
||
height: 14px;
|
||
color: var(--text-muted);
|
||
pointer-events: none;
|
||
"
|
||
></i>
|
||
<input
|
||
type="text"
|
||
id="graph-search"
|
||
placeholder="Rechercher ou #tag..."
|
||
style="
|
||
width: 100%;
|
||
font-size: 0.78rem;
|
||
padding: 5px 12px 5px 30px;
|
||
border-radius: 8px;
|
||
border: none;
|
||
background: var(--bg-secondary);
|
||
color: var(--text-primary);
|
||
outline: none;
|
||
box-shadow: inset 0 1px 3px
|
||
rgba(0, 0, 0, 0.2);
|
||
"
|
||
/>
|
||
<span
|
||
style="
|
||
position: absolute;
|
||
right: 10px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
font-size: 0.6rem;
|
||
color: var(--text-muted);
|
||
opacity: 0.4;
|
||
pointer-events: none;
|
||
"
|
||
>Ctrl+survol</span
|
||
>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="editor-body"
|
||
style="
|
||
flex: 1;
|
||
overflow: hidden;
|
||
position: relative;
|
||
padding: 0;
|
||
"
|
||
>
|
||
<canvas
|
||
id="graph-canvas"
|
||
style="width: 100%; height: 100%; cursor: grab"
|
||
></canvas>
|
||
<!-- Info panel: top-right, shows on hover, stays until closed -->
|
||
<div
|
||
id="graph-info-panel"
|
||
style="
|
||
display: none;
|
||
position: absolute;
|
||
top: 10px;
|
||
left: 10px;
|
||
max-width: 320px;
|
||
background: var(--bg-primary);
|
||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||
border-radius: 8px;
|
||
padding: 12px 14px;
|
||
font-size: 0.78rem;
|
||
color: var(--text-primary);
|
||
z-index: 10;
|
||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
||
"
|
||
>
|
||
<button
|
||
id="graph-info-close"
|
||
style="
|
||
position: absolute;
|
||
top: 6px;
|
||
right: 8px;
|
||
background: none;
|
||
border: none;
|
||
color: var(--text-muted);
|
||
cursor: pointer;
|
||
font-size: 1rem;
|
||
line-height: 1;
|
||
padding: 2px;
|
||
"
|
||
title="Fermer"
|
||
>
|
||
✕
|
||
</button>
|
||
<div id="graph-info-content"></div>
|
||
</div>
|
||
<!-- Preview panel: right sidebar, full height, shows on Ctrl+hover, stays until closed -->
|
||
<div
|
||
id="graph-preview-panel"
|
||
style="
|
||
display: none;
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
width: 380px;
|
||
height: 100%;
|
||
background: var(--bg-primary);
|
||
border-left: 1px solid var(--border-color);
|
||
z-index: 9;
|
||
overflow-y: auto;
|
||
font-size: 0.8rem;
|
||
color: var(--text-primary);
|
||
box-shadow: -4px 0 16px rgba(0, 0, 0, 0.3);
|
||
"
|
||
>
|
||
<button
|
||
id="graph-preview-close"
|
||
style="
|
||
position: sticky;
|
||
top: 0;
|
||
float: right;
|
||
background: var(--bg-primary);
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 4px;
|
||
color: var(--text-muted);
|
||
cursor: pointer;
|
||
font-size: 0.9rem;
|
||
padding: 2px 8px;
|
||
z-index: 1;
|
||
margin-bottom: -24px;
|
||
"
|
||
title="Fermer"
|
||
>
|
||
✕ Fermer
|
||
</button>
|
||
<div
|
||
id="graph-preview-content"
|
||
style="padding: 16px; padding-top: 8px"
|
||
></div>
|
||
</div>
|
||
<!-- Status bar + legend -->
|
||
<div
|
||
style="
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 4px 12px;
|
||
background: var(--bg-primary);
|
||
border-top: 1px solid var(--border-color);
|
||
font-size: 0.7rem;
|
||
color: var(--text-muted);
|
||
"
|
||
>
|
||
<span id="graph-status">—</span>
|
||
<div
|
||
style="
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
"
|
||
>
|
||
<label
|
||
style="
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 3px;
|
||
"
|
||
><input
|
||
type="checkbox"
|
||
id="graph-filter-dir"
|
||
checked
|
||
/>
|
||
🟦 Dossier</label
|
||
>
|
||
<label
|
||
style="
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 3px;
|
||
"
|
||
><input
|
||
type="checkbox"
|
||
id="graph-filter-md"
|
||
checked
|
||
/>
|
||
🟩 .md</label
|
||
>
|
||
<label
|
||
style="
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 3px;
|
||
"
|
||
><input
|
||
type="checkbox"
|
||
id="graph-filter-other"
|
||
checked
|
||
/>
|
||
⬜ Autre</label
|
||
>
|
||
<span style="opacity: 0.5">│</span>
|
||
<span>── Parent</span>
|
||
<span style="color: var(--accent-color, #2563eb)"
|
||
>┅┅ Wikilink</span
|
||
>
|
||
<span style="color: #e74c3c">← Backlink</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Help Modal -->
|
||
<div class="editor-modal" id="help-modal">
|
||
<div class="editor-container help-container">
|
||
<div class="editor-header">
|
||
<button
|
||
class="help-hamburger"
|
||
id="help-hamburger"
|
||
title="Sommaire"
|
||
aria-label="Afficher le sommaire"
|
||
>
|
||
<i
|
||
data-lucide="menu"
|
||
style="width: 18px; height: 18px"
|
||
></i>
|
||
</button>
|
||
<div class="editor-title" id="help-title" data-i18n="help.modal_title">
|
||
Guide d'utilisation ObsiGate
|
||
</div>
|
||
<div class="editor-actions">
|
||
<button
|
||
class="editor-btn"
|
||
id="help-download-md"
|
||
title="Télécharger ce guide en Markdown"
|
||
data-i18n-attr="title:guide105.dl_md_title"
|
||
>
|
||
<i
|
||
data-lucide="file-code-2"
|
||
style="width: 16px; height: 16px"
|
||
></i>
|
||
</button>
|
||
<button
|
||
class="editor-btn"
|
||
id="help-download-pdf"
|
||
title="Télécharger ce guide en PDF"
|
||
data-i18n-attr="title:guide105.dl_pdf_title"
|
||
>
|
||
<i
|
||
data-lucide="file-down"
|
||
style="width: 16px; height: 16px"
|
||
></i>
|
||
</button>
|
||
<button
|
||
class="editor-btn"
|
||
id="help-close"
|
||
title="Fermer l'aide"
|
||
aria-label="Fermer l'aide"
|
||
>
|
||
<i
|
||
data-lucide="x"
|
||
style="width: 16px; height: 16px"
|
||
></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="editor-body help-body" id="help-body">
|
||
<nav class="help-nav" id="help-nav">
|
||
<div class="help-nav-header">
|
||
<div class="help-nav-title" data-i18n="help.toc_title">Sommaire</div>
|
||
</div>
|
||
<div class="help-nav-search-wrap">
|
||
<input
|
||
type="text"
|
||
class="help-nav-search"
|
||
id="help-nav-search"
|
||
data-i18n-placeholder="help.search_placeholder" placeholder="Rechercher dans l'aide..."
|
||
autocomplete="off"
|
||
spellcheck="false"
|
||
/>
|
||
<button
|
||
class="help-search-clear"
|
||
id="help-search-clear"
|
||
title="Effacer la recherche"
|
||
aria-label="Effacer"
|
||
>
|
||
✕
|
||
</button>
|
||
</div>
|
||
<ul class="help-nav-list" id="help-nav-list">
|
||
<li>
|
||
<a href="#help-intro" class="help-nav-link"
|
||
data-i18n="help.nav_intro">📘 Introduction</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-architecture" class="help-nav-link" data-i18n="guide105.nav_architecture">🏗️ Architecture</a>
|
||
</li>
|
||
<li>
|
||
<a href="#help-interface" class="help-nav-link"
|
||
data-i18n="help.nav_interface">🧭 Interface</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-navigation" class="help-nav-link"
|
||
data-i18n="help.nav_navigation">🗺️ Navigation</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-tabs" class="help-nav-link"
|
||
data-i18n="help.nav_tabs">📑 Onglets</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-recherche" class="help-nav-link"
|
||
data-i18n="help.nav_search">🔍 Recherche</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-tags" class="help-nav-link"
|
||
data-i18n="help.nav_tags">🏷️ Tags</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-fichiers" class="help-nav-link"
|
||
data-i18n="help.nav_files">📁 Fichiers</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-excalidraw" class="help-nav-link"
|
||
data-i18n="help.nav_excalidraw">🎨 Excalidraw</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-diagrams" class="help-nav-link" data-i18n="guide105.nav_diagrams">📊 Diagrammes</a>
|
||
</li>
|
||
<li>
|
||
<a href="#help-edition" class="help-nav-link"
|
||
data-i18n="help.nav_editing">✏️ Édition</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-mobile-editor" class="help-nav-link"
|
||
data-i18n="help.nav_mobile_editor">📱 Mobile</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-library" class="help-nav-link" data-i18n="guide105.nav_library">⭐ Bibliothèque</a>
|
||
</li>
|
||
<li>
|
||
<a href="#help-graphe" class="help-nav-link"
|
||
data-i18n="help.nav_graph">🗺️ Graphe</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-ai" class="help-nav-link"
|
||
data-i18n="help.nav_ai">🤖 IA Éditeur</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-palette" class="help-nav-link"
|
||
data-i18n="help.nav_palette">⌨️ Palette</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-raccourcis" class="help-nav-link"
|
||
data-i18n="help.nav_shortcuts">⌨️ Raccourcis</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-offline" class="help-nav-link" data-i18n="guide105.nav_offline">📴 Hors-ligne</a>
|
||
</li>
|
||
<li>
|
||
<a href="#help-collab" class="help-nav-link" data-i18n="guide105.nav_collab">👥 Collaboration</a>
|
||
</li>
|
||
<li>
|
||
<a href="#help-desktop" class="help-nav-link" data-i18n="guide105.nav_desktop">🖥️ Desktop</a>
|
||
</li>
|
||
<li>
|
||
<a href="#help-partage" class="help-nav-link"
|
||
data-i18n="help.nav_sharing">🔗 Partage</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-webhooks" class="help-nav-link"
|
||
data-i18n="help.nav_webhooks">🪝 Webhooks</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a
|
||
href="#help-sauvegardes"
|
||
class="help-nav-link"
|
||
data-i18n="help.nav_backups">💾 Sauvegardes</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-securite" class="help-nav-link"
|
||
data-i18n="help.nav_security">🛡️ Sécurité</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a
|
||
href="#help-personnalisation"
|
||
class="help-nav-link"
|
||
data-i18n="help.nav_customization">🎨 Personnalisation</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-astuces" class="help-nav-link"
|
||
data-i18n="help.nav_tips">💡 Astuces</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="#help-api" class="help-nav-link" data-i18n="guide105.nav_api">🔌 API</a>
|
||
</li>
|
||
<li>
|
||
<a href="#help-languages" class="help-nav-link" data-i18n="guide105.nav_languages">🌍 Multilingue</a>
|
||
</li>
|
||
<li>
|
||
<a href="#help-plugins" class="help-nav-link"
|
||
data-i18n="help.nav_plugins">🧩 Plugins</a
|
||
>
|
||
</li>
|
||
</ul>
|
||
</nav>
|
||
<div class="help-content">
|
||
<section class="help-section help-hero" id="help-intro">
|
||
<div class="help-hero-copy">
|
||
<div class="help-kicker" data-i18n="help.guide_official">Guide officiel</div>
|
||
<h1 data-i18n="help.welcome_title">📘 Bienvenue dans ObsiGate</h1>
|
||
<p class="help-lead" data-i18n="auto.6c166d35">
|
||
ObsiGate est votre porte d'entrée web vers
|
||
vos vaults Obsidian. Accédez, naviguez et
|
||
recherchez dans toutes vos notes depuis
|
||
n'importe quel appareil via une interface
|
||
moderne et responsive.
|
||
</p>
|
||
<div class="help-badges">
|
||
<span class="help-badge" data-i18n="help.welcome_tag1"
|
||
>⚡ Navigation rapide</span
|
||
>
|
||
<span class="help-badge" data-i18n="help.welcome_tag2"
|
||
>🗂️ Multi-vault</span
|
||
>
|
||
<span class="help-badge" data-i18n="help.welcome_tag3"
|
||
>🏷️ Tags intelligents</span
|
||
>
|
||
<span class="help-badge" data-i18n="help.welcome_tag4"
|
||
>📝 Lecture et édition</span
|
||
>
|
||
<span class="help-badge" data-i18n="help.welcome_tag5"
|
||
>🔍 Recherche TF-IDF</span
|
||
>
|
||
</div>
|
||
</div>
|
||
<div class="help-hero-visual" aria-hidden="true">
|
||
<div class="help-stat-card">
|
||
<span class="help-stat-label" data-i18n="help.experience"
|
||
>Expérience</span
|
||
>
|
||
<strong data-i18n="help.simple">Simple</strong>
|
||
</div>
|
||
<div class="help-stat-card">
|
||
<span class="help-stat-label" data-i18n="help.search_bar"
|
||
>Recherche</span
|
||
>
|
||
<strong data-i18n="help.instant">Instantanée</strong>
|
||
</div>
|
||
<div class="help-stat-card">
|
||
<span class="help-stat-label" data-i18n="help.access"
|
||
>Accès</span
|
||
>
|
||
<strong data-i18n="help.multi_vault">Multi-vault</strong>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-architecture">
|
||
<h2 data-i18n="guide105.nav_architecture">🏗️ Architecture</h2>
|
||
<p data-i18n-html="guide105.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 <code>data/</code>, l'index de recherche en mémoire.</p>
|
||
<pre class="mermaid-code"><code class="language-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</code></pre>
|
||
<p data-i18n="guide105.arch_diagram_note">Le diagramme est interactif dans l'application : zoom, plein écran, copie SVG ou code.</p>
|
||
<h3 data-i18n="guide105.arch_h3_layers">Les grandes composantes</h3>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="guide105.arch_lbl_fe">Frontend</strong><span data-i18n-html="guide105.arch_fe"> — SPA en JavaScript vanilla (modules ES), sans framework ni build npm : <code>frontend/js/</code> (~30 modules). Le CSS utilise des variables pour les thèmes.</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="guide105.arch_lbl_be">Backend</strong><span data-i18n="guide105.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).</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="guide105.arch_lbl_realtime">Temps réel & MCP</strong><span data-i18n-html="guide105.arch_rt"> — passerelle WebSocket (collaboration Yjs, notifications SSE/push) et serveur MCP (Streamable HTTP, <code>/mcp</code>) pour les clients externes.</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="guide105.arch_lbl_ai">Couche IA</strong><span data-i18n="guide105.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.</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="guide105.arch_lbl_data">Données</strong><span data-i18n-html="guide105.arch_data"> — les vaults Obsidian sur disque (source de vérité), la configuration en JSON (<code>data/</code>), les backups horodatés (<code>.obsigate-backup/</code>), l'audit en JSON lines, les clés API chiffrées dans <code>data/api_keys.json</code>.</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="guide105.arch_lbl_deploy">Déploiement</strong><span data-i18n="guide105.arch_deploy"> — application desktop Tauri (Rust) embarquant le backend Python, conteneur Docker, ou PWA installable dans le navigateur (mode hors-ligne).</span>
|
||
</li>
|
||
</ul>
|
||
<h3 data-i18n="guide105.arch_h3_flux">Flux typique</h3>
|
||
<p data-i18n-html="guide105.arch_flux"> Un clic sur un fichier émet <code>GET /api/file/...</code> ; 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.</p>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-interface">
|
||
<h2 data-i18n="help.nav_interface">🧭 Interface utilisateur</h2>
|
||
|
||
<h3 data-i18n="help.header_section">En-tête</h3>
|
||
<p data-i18n="auto.209c5490">
|
||
La barre supérieure contient les éléments
|
||
essentiels :
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.hamburger_btn">Bouton menu hamburger</strong>
|
||
: <span data-i18n="help.desc_hamburger">obile) : Affiche/masque la sidebar</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.sidebar_toggle_btn">Bouton toggle sidebar</strong> <span data-i18n="help.desc_sidebar_toggle">(desktop) : Masque la sidebar pour plus
|
||
d'espace</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.logo">Logo ObsiGate</strong> <span data-i18n="help.desc_logo">: Retour à
|
||
l'accueil</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.search_bar">Barre de recherche</strong> <span data-i18n="help.desc_search_bar">:
|
||
Recherche globale avec autocomplétion</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.vault_indicator">Indicateur de vault</strong> <span data-i18n="help.desc_vault_indicator">:
|
||
Affiche le contexte actif (All, vault1,
|
||
etc.)</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.menu_options">Menu Options</strong> <span data-i18n="help.desc_menu_options">: Accès aux
|
||
paramètres, thème, aide</span>
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.sidebar_section">Sidebar (barre latérale)</h3>
|
||
<p data-i18n="help.sidebar_tabs">La sidebar est divisée en deux onglets :</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="sidebar.tab_vaults">Vaults</strong> <span data-i18n="help.desc_vaults_tab">: Arborescence des
|
||
fichiers et dossiers</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="sidebar.tab_tags">Tags</strong> <span data-i18n="help.desc_tags_tab">: Nuage de tags
|
||
cliquables</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.sidebar_user">Compte</strong> <span data-i18n="help.desc_sidebar_user">: Avatar, nom,
|
||
rôle et déconnexion en bas de la sidebar</span>
|
||
</li>
|
||
</ul>
|
||
<p data-i18n="help.sidebar_features">Fonctionnalités de la sidebar :</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.filtering">Filtrage</strong> <span data-i18n="help.desc_filtering">: Barre de
|
||
recherche pour filtrer fichiers/tags</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.quick_select">Sélection rapide</strong> <span data-i18n="help.desc_quick_select">: Dropdown
|
||
pour changer de vault</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.resize">Redimensionnement</strong> <span data-i18n="help.desc_resize">: Glissez
|
||
le bord droit pour ajuster la largeur</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.hide">Masquage</strong> <span data-i18n="help.desc_hide">: Cliquez sur
|
||
l'icône sidebar dans le header</span>
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.content_area">Zone de contenu</h3>
|
||
<p data-i18n="help.content_shows">La zone centrale affiche :</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.breadcrumb">Breadcrumb</strong> <span data-i18n="help.desc_breadcrumb">: Chemin du
|
||
fichier (cliquable pour repositionner la
|
||
sidebar)</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.title_tags">Titre et tags</strong> <span data-i18n="help.desc_title_tags">: Métadonnées
|
||
du fichier</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.action_buttons">Boutons d'action</strong> <span data-i18n="help.desc_action_buttons">: Copier,
|
||
Source, Télécharger, Éditer, Ouvrir dans
|
||
nouvelle fenêtre</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.rendered_content">Contenu rendu</strong> <span data-i18n="help.desc_rendered_content">: Markdown
|
||
avec wikilinks et images</span>
|
||
</li>
|
||
</ul>
|
||
<h3 data-i18n="guide105.h3_push">Notifications web (push)</h3>
|
||
<p data-i18n="guide105.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.</p>
|
||
<p data-i18n="guide105.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.</p>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-navigation">
|
||
<h2 data-i18n="help.733f0559">🗺️ Navigation</h2>
|
||
|
||
<h3 data-i18n="help.tree_section">Arborescence des vaults</h3>
|
||
<p data-i18n="auto.4c908ade">
|
||
Cliquez sur un vault pour l'ouvrir et voir son
|
||
contenu. Les dossiers affichent un chevron et un
|
||
badge avec le nombre d'éléments.
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="sidebar.expand_collapse">Développer/Réduire</strong><span data-i18n="help.desc_expand">:
|
||
Cliquez sur un dossier</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="sidebar.open_file">Ouvrir un fichier</strong><span data-i18n="help.desc_open_file">: Cliquez
|
||
sur le nom du fichier</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="sidebar.file_icons">Icônes</strong><span data-i18n="help.desc_fdeaf803"> : Chaque type de
|
||
fichier a son icône (markdown, code, etc.)</span></li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.sidebar_filtering">Filtrage de la sidebar</h3>
|
||
<p data-i18n="help.desc_filter_usage">
|
||
Utilisez la barre de filtrage en haut de la
|
||
sidebar pour rechercher rapidement :
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="sidebar.filter_instant">Recherche instantanée</strong><span data-i18n="help.desc_instant_filter">:
|
||
Tapez pour filtrer fichiers et dossiers</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.case_sensitive">Case-sensitive</strong><span data-i18n="help.desc_case_sensitive">: Bouton
|
||
"Aa" pour respecter la casse</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.clear">Effacer</strong><span data-i18n="help.desc_a4a4e21b"> : Bouton "×" pour
|
||
réinitialiser le filtre</span></li>
|
||
<li>
|
||
<strong data-i18n="help.results_grouped">Résultats groupés</strong><span data-i18n="help.desc_grouped_results">:
|
||
Affichage par vault avec chemins complets</span>
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.breadcrumb_reposition">Breadcrumb et repositionnement</h3>
|
||
<p data-i18n="auto.1abf6661">
|
||
Le chemin affiché au-dessus du contenu est
|
||
interactif :
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.click_vault">Cliquez sur le vault</strong><span data-i18n="help.desc_click_vault">:
|
||
Focus sur le vault dans la sidebar</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.click_segment">Cliquez sur un segment</strong><span data-i18n="help.desc_click_segment">:
|
||
Navigation vers ce dossier</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.auto_scroll">Auto-scroll</strong><span data-i18n="help.desc_autoscroll">: La sidebar se
|
||
positionne automatiquement</span>
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.vault_selector">Sélection de vault</h3>
|
||
<p data-i18n="help.desc_vault_context">
|
||
Changez de contexte pour filtrer l'affichage :
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.menu_header">Menu header</strong><span data-i18n="help.desc_menu_header">: Dropdown
|
||
"Vault" dans le menu Options</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.sidebar_dropdown">Sidebar dropdown</strong><span data-i18n="help.desc_085ec31f"> :
|
||
Sélection rapide en haut de l'onglet Vaults</span></li>
|
||
<li>
|
||
<strong data-i18n="help.all_vaults">Tous les vaults</strong><span data-i18n="help.desc_all_vaults">: Vue
|
||
globale de tous vos contenus</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.vault_specific">Vault spécifique</strong><span data-i18n="help.desc_specific_vault">: Focus
|
||
sur un seul vault</span>
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-tabs">
|
||
<h2 data-i18n="help.8c5341e0">📑 Gestion des onglets</h2>
|
||
<p data-i18n="auto.27a2f7c1">
|
||
ObsiGate supporte l'ouverture de plusieurs
|
||
fichiers simultanément dans des onglets.
|
||
</p>
|
||
|
||
<h3 data-i18n="help.tabs_section">Ouverture d'onglets</h3>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.single_click">Simple clic</strong><span data-i18n="help.desc_e3fce7a3"> : Ouvre le
|
||
fichier dans un onglet temporaire (aperçu,
|
||
italique)</span></li>
|
||
<li>
|
||
<strong data-i18n="help.double_click">Double clic</strong><span data-i18n="help.desc_double_click">: Ouvre le
|
||
fichier dans un onglet persistant</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.recent_nav">Navigation récente</strong><span data-i18n="help.desc_d186a19e"> :
|
||
L'onglet "Récent" dans la sidebar liste vos
|
||
derniers fichiers</span></li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.tab_nav">Navigation entre onglets</h3>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.click_tab">Clic sur un onglet</strong><span data-i18n="help.desc_click_tab">: Active
|
||
l'onglet</span>
|
||
</li>
|
||
<li>
|
||
<strong>Ctrl+Tab</strong><span data-i18n="help.desc_ctrl_tab">: Onglet suivant</span>
|
||
</li>
|
||
<li>
|
||
<strong>Ctrl+Shift+Tab</strong><span data-i18n="help.desc_e5246734"> : Onglet
|
||
précédent</span></li>
|
||
<li>
|
||
<strong>Ctrl+W</strong><span data-i18n="help.desc_ctrl_w">: Fermer l'onglet
|
||
actif</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.close_tab_x">Bouton ✕</strong><span data-i18n="help.desc_close_btn">: Ferme l'onglet
|
||
(sauf s'il est seul)</span>
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.tab_features">Fonctionnalités</h3>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.tab_bar">Barre d'onglets</strong><span data-i18n="help.desc_170a6982"> : Affichage
|
||
horizontal avec le nom du fichier et son
|
||
icône</span></li>
|
||
<li>
|
||
<strong data-i18n="help.active_tab">Onglet actif</strong><span data-i18n="help.desc_6b649b1e"> : Surligné en
|
||
couleur d'accentuation</span></li>
|
||
<li>
|
||
<strong data-i18n="help.preview_tab">Prévisualisation</strong><span data-i18n="help.desc_ef1531ce"> : Un
|
||
onglet temporaire (italique) est remplacé
|
||
par le prochain clic</span></li>
|
||
<li>
|
||
<strong data-i18n="help.persistent_tabs">Onglets persistants</strong><span data-i18n="help.desc_5ab8c78d"> :
|
||
Restent ouverts jusqu'à fermeture manuelle</span></li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-recherche">
|
||
<h2 data-i18n="help.adf30d78">🔍 Recherche</h2>
|
||
|
||
<h3 data-i18n="help.simple_search">Recherche simple</h3>
|
||
<p data-i18n-html="help.desc_search_intro">
|
||
Tapez dans la barre de recherche en haut pour
|
||
lancer une recherche fulltext :
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="search.full_text">Recherche dans tout</strong><span data-i18n="help.desc_search_all">:
|
||
Titre, contenu, tags, chemins</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.smart_scoring">Scoring intelligent</strong><span data-i18n="help.desc_00b9572b"> :
|
||
Résultats classés par pertinence TF-IDF</span></li>
|
||
<li>
|
||
<strong data-i18n="help.highlighted_snippets">Snippets surlignés</strong><span data-i18n="help.desc_snippets">:
|
||
Extraits avec termes en surbrillance</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.pagination">Pagination</strong><span data-i18n="help.desc_53bfaeca"> : Navigation par
|
||
pages de 50 résultats</span></li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.advanced_operators">Opérateurs avancés</h3>
|
||
<p data-i18n="auto.4bc1f00a">
|
||
Utilisez des opérateurs pour affiner votre
|
||
recherche :
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<span data-i18n="help.desc_op_tag"><code>tag:docker</code> ou
|
||
<code>#docker</code> : Filtrer par tag</span>
|
||
</li>
|
||
<li>
|
||
<span data-i18n="help.desc_op_vault"><code>vault:IT</code> : Rechercher dans un
|
||
vault spécifique</span>
|
||
</li>
|
||
<li>
|
||
<span data-i18n="help.desc_op_title"><code>title:kubernetes</code> : Chercher
|
||
dans les titres uniquement</span>
|
||
</li>
|
||
<li>
|
||
<span data-i18n="help.desc_op_path"><code>path:recettes/soupes</code> : Filtrer
|
||
par chemin</span>
|
||
</li>
|
||
<li>
|
||
<span data-i18n="help.desc_op_ext"><code>ext:md</code> : Filtrer par type de
|
||
fichier</span>
|
||
</li>
|
||
<li>
|
||
<span data-i18n="help.desc_op_phrase"><code>"phrase exacte"</code> : Recherche de
|
||
phrase entre guillemets</span>
|
||
</li>
|
||
</ul>
|
||
<p>
|
||
<strong>Exemples</strong><span data-i18n-html="help.desc_examples">:
|
||
<code>ext:sh</code> recherche dans les scripts
|
||
bash, <code>ext:py</code> dans les scripts
|
||
Python, <code>ext:md</code> dans les fichiers
|
||
Markdown.
|
||
</p></span>
|
||
<p>
|
||
<strong data-i18n="auto.1a54bd69">Exemple combiné</strong> :
|
||
<code
|
||
>tag:linux vault:IT ext:md serveur web</code
|
||
>
|
||
<span data-i18n="help.desc_combined">recherche "serveur web" dans les fichiers
|
||
Markdown du vault IT avec le tag linux.</span>
|
||
</p>
|
||
|
||
<h3 data-i18n="help.autocomplete">Autocomplétion</h3>
|
||
<p data-i18n="auto.404c8f1c">
|
||
L'autocomplétion vous aide à trouver rapidement
|
||
:
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="search.dropdown_history">Historique</strong><span data-i18n="help.desc_4de7e318"> : Les 50
|
||
dernières recherches (localStorage)</span></li>
|
||
<li>
|
||
<strong data-i18n="palette.mode_files">Fichiers</strong><span data-i18n="help.desc_file_suggest">: Suggestions de
|
||
titres de fichiers</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="sidebar.tab_tags">Tags</strong><span data-i18n="help.desc_tag_suggest">: Suggestions de tags
|
||
avec compteurs</span>
|
||
</li>
|
||
<li>
|
||
<strong>Navigation clavier</strong><span data-i18n="help.desc_6b8fc728"> : ↑/↓
|
||
pour naviguer, Enter pour sélectionner</span></li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.active_filter_chips">Chips de filtres actifs</h3>
|
||
<p data-i18n="auto.03a6e5b1">
|
||
Les opérateurs utilisés s'affichent sous forme
|
||
de chips colorés :
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong>Visualisation</strong><span data-i18n="help.desc_chip_view">: Voir d'un
|
||
coup d'œil les filtres actifs</span>
|
||
</li>
|
||
<li>
|
||
<strong>Suppression</strong><span data-i18n="help.desc_chip_remove">: Cliquez sur
|
||
"×" pour retirer un filtre</span>
|
||
</li>
|
||
<li>
|
||
<strong>Couleurs</strong><span data-i18n="help.desc_6ebebdfd"> : Chaque type
|
||
d'opérateur a sa couleur</span></li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.a648c49e">Facettes et tri</h3>
|
||
<p data-i18n="auto.cd7332c8">
|
||
Les résultats incluent des facettes pour affiner
|
||
:
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong>Facettes tags</strong><span data-i18n="help.desc_cf5ccd40"> : Compteurs
|
||
par tag dans les résultats</span></li>
|
||
<li>
|
||
<strong>Facettes vaults</strong><span data-i18n="help.desc_facet_vaults">: Compteurs
|
||
par vault</span>
|
||
</li>
|
||
<li>
|
||
<strong>Facettes extensions</strong><span data-i18n="help.desc_facet_extensions">:
|
||
Compteurs par extension de fichier dans les résultats</span>
|
||
</li>
|
||
<li>
|
||
<strong>Tri par pertinence</strong><span data-i18n="help.desc_9fec424d"> : Score
|
||
TF-IDF (défaut)</span></li>
|
||
<li>
|
||
<strong>Tri par date</strong><span data-i18n="help.desc_42a81347"> : Dernière
|
||
modification</span></li>
|
||
</ul>
|
||
<h3 data-i18n="guide105.h3_semantic">Recherche sémantique (hybride)</h3>
|
||
<p data-i18n="guide105.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.</p>
|
||
<p data-i18n="guide105.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.</p>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-tags">
|
||
<h2 data-i18n="help.2507242f">🏷️ Tags</h2>
|
||
|
||
<h3 data-i18n="help.5f87ea79">Tag cloud</h3>
|
||
<p data-i18n-html="help.desc_tag_cloud_intro">
|
||
L'onglet Tags de la sidebar affiche un nuage de
|
||
tags :
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong>Tous les tags</strong><span data-i18n="help.desc_all_tags">: Extraits
|
||
des frontmatters YAML et inline (#tag)</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.clickable">Cliquable</strong><span data-i18n="help.desc_6caffeed"> : Cliquez pour
|
||
filtrer les résultats</span></li>
|
||
<li>
|
||
<strong data-i18n="help.filtering">Filtrage</strong><span data-i18n="help.desc_tag_filter_use">: Utilisez la
|
||
barre de filtrage pour chercher un tag</span>
|
||
</li>
|
||
<li>
|
||
<strong>Contexte vault</strong><span data-i18n="help.desc_6a5ddef2"> : Les tags
|
||
s'adaptent au vault sélectionné</span></li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.2c03a7ba">Tags inline vs frontmatter</h3>
|
||
<p data-i18n-html="help.desc_two_tag_types">ObsiGate supporte deux types de tags :</p>
|
||
<ul>
|
||
<li>
|
||
<strong>Frontmatter YAML</strong><span data-i18n-html="help.desc_frontmatter">:
|
||
<code>tags: [docker, linux]</code> ou
|
||
<code>tags: docker, linux</code></span>
|
||
</li>
|
||
<li>
|
||
<strong>Inline</strong><span data-i18n-html="help.desc_inline_tags">:
|
||
<code>#docker</code> dans le contenu
|
||
markdown</span>
|
||
</li>
|
||
<li>
|
||
<strong>Fusion automatique</strong><span data-i18n="help.desc_828db5da"> : Les
|
||
deux types sont combinés</span></li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.63f024d4">Filtrage de tags template</h3>
|
||
<p data-i18n-html="help.desc_template_intro">
|
||
Dans les Configurations, vous pouvez masquer les
|
||
tags de template :
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong>Patterns wildcards</strong><span data-i18n-html="help.desc_patterns">: Ex:
|
||
<code>#<% ... %></code> ou
|
||
<code>#{{ ... }}</code></span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="auto.5d936af6">Regex générée</strong><span data-i18n="help.desc_b9c94994"> : Aperçu de
|
||
la regex appliquée</span></li>
|
||
<li>
|
||
<strong data-i18n="help.add_remove">Ajout/Suppression</strong><span data-i18n="help.desc_ef3f8158"> : Gérez
|
||
vos patterns de filtrage</span></li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-fichiers">
|
||
<h2 data-i18n="help.c017944b">📄 Fichiers</h2>
|
||
|
||
<h3 data-i18n="help.cf8615b1">Visualisation</h3>
|
||
<p data-i18n="help.desc_md_render">Les fichiers markdown sont rendus avec :</p>
|
||
<ul>
|
||
<li>
|
||
<strong>Wikilinks cliquables</strong><span data-i18n-html="help.desc_wikilinks">:
|
||
<code>[[lien]]</code> et
|
||
<code>[[lien|texte]]</code></span>
|
||
</li>
|
||
<li>
|
||
<strong>Images Obsidian</strong><span data-i18n-html="help.desc_images">: Support
|
||
de <code>![[image.png]]</code></span>
|
||
</li>
|
||
<li>
|
||
<strong>Syntax highlighting</strong><span data-i18n="help.desc_syntax_hl">:
|
||
Coloration des blocs de code</span>
|
||
</li>
|
||
<li>
|
||
<strong>Tables, listes, quotes</strong><span data-i18n="help.desc_markdown_all">:
|
||
Rendu complet du markdown</span>
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.context_actions">Actions sur fichiers</h3>
|
||
<p data-i18n="help.actions_detail">Boutons disponibles au-dessus du contenu :</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="viewer.copy">Copier</strong><span data-i18n="help.desc_copy_btn">: Copie le contenu
|
||
source dans le presse-papiers</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="viewer.source">Source</strong><span data-i18n="help.desc_source_btn">: Affiche/masque le
|
||
markdown brut</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="viewer.download">Télécharger</strong><span data-i18n="help.desc_a52fc3b1"> : Télécharge le
|
||
fichier</span></li>
|
||
<li>
|
||
<strong data-i18n="viewer.edit">Éditer</strong><span data-i18n="help.desc_995a711f"> : Ouvre l'éditeur
|
||
CodeMirror</span></li>
|
||
<li>
|
||
<strong
|
||
>Ouvrir dans nouvelle fenêtre</strong
|
||
><span data-i18n="help.desc_open_new">: Ouvre le fichier dans un nouvel onglet</span>
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="auto.1efbbc9a">Édition avec CodeMirror 6</h3>
|
||
<p data-i18n="auto.edf89626">L'éditeur intégré offre :</p>
|
||
<ul>
|
||
<li>
|
||
<strong>Coloration syntaxique</strong><span data-i18n="help.desc_cm_syntax">:
|
||
Support de nombreux langages</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="auto.1d46301c">Numéros de ligne</strong><span data-i18n="help.desc_cm_lines">:
|
||
Navigation facile</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.autocomplete">Autocomplétion</strong><span data-i18n="help.desc_cm_autocomplete">:
|
||
Suggestions contextuelles</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="settings.search">Recherche</strong><span data-i18n="help.desc_cm_search">: Ctrl+F pour
|
||
chercher dans le fichier</span>
|
||
</li>
|
||
<li>
|
||
<strong>Sauvegarde</strong><span data-i18n="help.desc_save_btn_new">: Bouton ✓ pour
|
||
enregistrer</span>
|
||
</li>
|
||
<li>
|
||
<strong>Suppression</strong><span data-i18n="help.desc_delete_btn_new">: Bouton
|
||
corbeille pour supprimer</span>
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="auto.ec7260fa">Types de fichiers supportés</h3>
|
||
<p data-i18n="help.indexes">ObsiGate indexe et affiche :</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="viewer.markdown">Markdown</strong> : .md (rendu
|
||
complet)
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="mermaid.code">Code</strong> : .py, .js, .ts, .jsx,
|
||
.tsx, .java, .c, .cpp, .rs, .go, etc.
|
||
</li>
|
||
<li>
|
||
<strong>Config</strong> : .json, .yaml,
|
||
.yml, .toml, .xml, .ini, .env
|
||
</li>
|
||
<li>
|
||
<strong>Scripts</strong> : .sh, .bash, .ps1,
|
||
.bat
|
||
</li>
|
||
<li>
|
||
<strong>Web</strong> : .html, .css, .scss
|
||
</li>
|
||
<li>
|
||
<strong>PDF</strong> : .pdf (visualiseur natif, texte indexé)
|
||
</li>
|
||
<li>
|
||
<strong>Excalidraw</strong> : .excalidraw,
|
||
.excalidraw.md
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.other">Autres</strong> : .txt, .log, .sql,
|
||
.csv
|
||
</li>
|
||
</ul>
|
||
<h3 data-i18n="guide105.h3_pdf">Export PDF</h3>
|
||
<p data-i18n-html="guide105.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 <code>/s/{token}/pdf</code> produit le même PDF.</p>
|
||
<h3 data-i18n="guide105.h3_exports">Export HTML / ePub / ZIP</h3>
|
||
<p data-i18n="guide105.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.</p>
|
||
<h3 data-i18n="guide105.h3_dupe">Anti-doublons à l'upload</h3>
|
||
<p data-i18n="guide105.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.</p>
|
||
</section>
|
||
|
||
<!-- 🎨 Excalidraw -->
|
||
<section class="help-section" id="help-excalidraw">
|
||
<h2 data-i18n="help.excalidraw_title">🎨 Diagrammes Excalidraw</h2>
|
||
<p data-i18n-html="help.excalidraw_intro">
|
||
Les fichiers <code>.excalidraw</code> et
|
||
<code>.excalidraw.md</code> s'ouvrent dans un éditeur visuel
|
||
Excalidraw complet, intégré dans une iframe sandboxée.
|
||
</p>
|
||
|
||
<h3 data-i18n="help.excalidraw_open_title">Ouverture</h3>
|
||
<ul>
|
||
<li data-i18n-html="help.excalidraw_open">
|
||
Cliquez sur un fichier <code>.excalidraw</code> dans
|
||
l'arborescence : l'éditeur s'ouvre à la place du viewer markdown.
|
||
</li>
|
||
<li data-i18n="help.excalidraw_edit">
|
||
Dessinez, ajoutez des formes, du texte, des flèches et des
|
||
images — l'expérience Excalidraw complète.
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.excalidraw_save_title">Sauvegarde</h3>
|
||
<ul>
|
||
<li data-i18n-html="help.excalidraw_save">
|
||
Les modifications sont sauvegardées automatiquement (2 s) ou
|
||
avec <strong>Ctrl+S</strong>. L'éditeur suit le thème
|
||
clair/sombre.
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.excalidraw_search_title">Recherche</h3>
|
||
<ul>
|
||
<li data-i18n="help.excalidraw_search">
|
||
Le texte des éléments du diagramme est extrait à l'indexation :
|
||
il est donc recherchable via la recherche full-text.
|
||
</li>
|
||
<li data-i18n-html="help.excalidraw_compat">
|
||
Les fichiers créés avec le plugin Obsidian Excalidraw (y
|
||
compris le format <code>.excalidraw.md</code> compressé) sont
|
||
compatibles.
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<!-- ✏️ Édition -->
|
||
<section class="help-section" id="help-diagrams">
|
||
<h2 data-i18n="guide105.nav_diagrams">📊 Diagrammes</h2>
|
||
<p data-i18n-html="guide105.dia_intro">Les blocs <code>```mermaid</code> de vos notes sont rendus en diagrammes interactifs (Mermaid v11, chargé depuis un CDN).</p>
|
||
<ul>
|
||
<li data-i18n="guide105.dia_zoom">Zoom : boutons + / − dans la barre d'outils du diagramme.</li>
|
||
<li data-i18n="guide105.dia_fs">Plein écran : idéal pour les grandes matrices.</li>
|
||
<li data-i18n="guide105.dia_copy">Copie : exportez le SVG ou le code source (boutons dédiés).</li>
|
||
<li data-i18n="guide105.dia_toggle">Bascule Aperçu / Code pour éditer la source sans quitter la vue.</li>
|
||
<li data-i18n="guide105.dia_theme">Thème : le diagramme suit le thème clair/sombre de l'application.</li>
|
||
</ul>
|
||
<p data-i18n="guide105.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.</p>
|
||
<p data-i18n-html="guide105.dia_excalidraw_ref">Les dessins à main levée (<code>.excalidraw</code>, <code>.excalidraw.md</code>) sont couverts dans la section 🎨 Excalidraw.</p>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-edition">
|
||
<h2 data-i18n="auto.f346076a">✏️ Édition avancée</h2>
|
||
|
||
<h3 data-i18n="auto.695d9e47">Éditeur CodeMirror 6</h3>
|
||
<p data-i18n="auto.ef8c5f4d">
|
||
L'éditeur intégré offre une expérience complète
|
||
d'édition :
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong>Coloration syntaxique</strong><span data-i18n="help.desc_adv_syntax">:
|
||
Python, JS, HTML, CSS, Java, C++, Rust, Go
|
||
et bien d'autres</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="auto.1d46301c">Numéros de ligne</strong><span data-i18n="help.desc_9f78b3ec"> :
|
||
Navigation facilitée dans les fichiers longs</span></li>
|
||
<li>
|
||
<strong>Find in page</strong><span data-i18n="help.desc_find_in_page">: Ctrl+F pour
|
||
chercher dans le fichier (casse, regex, mot
|
||
entier)</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.auto_save">Sauvegarde automatique</strong><span data-i18n="help.desc_065f53ee"> :
|
||
Auto-save silencieux après 2s d'inactivité,
|
||
flash vert de confirmation</span></li>
|
||
<li>
|
||
<strong>Bouton ✓</strong><span data-i18n="help.desc_check_btn">: Sauvegarde
|
||
manuelle</span>
|
||
</li>
|
||
<li>
|
||
<strong>Bouton 🗑️</strong><span data-i18n="help.desc_trash_btn">: Suppression du
|
||
fichier</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.toolbar_section">Barre d'outils AI</strong><span data-i18n="help.desc_c8532869"> :
|
||
Complétion, réécriture, traduction (voir
|
||
section</span><a href="#help-ai" data-i18n="settings.ai">🤖 IA</a>)
|
||
</li>
|
||
</ul>
|
||
|
||
</section>
|
||
|
||
<!-- 🗺️ Graphe -->
|
||
<section class="help-section" id="help-mobile-editor">
|
||
<h2 data-i18n="help.mobile_editor_title">📱 Édition mobile</h2>
|
||
<p data-i18n="help.mobile_editor_intro">
|
||
Sur téléphone et tablette, l'édition s'adapte au
|
||
tactile : barre d'outils flottante, gestes et mode
|
||
lecture plein écran.
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.mobile_editor_toolbar_label">Barre d'outils flottante</strong><span data-i18n="help.mobile_editor_toolbar"> :
|
||
gras, italique, code, liste à puces et lien.
|
||
Les boutons A−/A+ ajustent la taille de la
|
||
police.</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.mobile_editor_paste_label">Presse-papiers</strong><span data-i18n="help.mobile_editor_paste"> :
|
||
bouton « Coller » persistant (contourne la
|
||
restriction iOS).</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.mobile_editor_gestures_label">Gestes tactiles</strong><span data-i18n="help.mobile_editor_gestures"> :
|
||
balayage vers la gauche → liens entrants,
|
||
vers la droite → table des matières.</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.mobile_editor_reading_label">Mode lecture</strong><span data-i18n="help.mobile_editor_reading"> :
|
||
plein écran sans en-tête ni barres latérales ;
|
||
balayage horizontal pour passer au fichier
|
||
suivant ou précédent.</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.mobile_editor_zoom_label">Zoom & hauteur</strong><span data-i18n="help.mobile_editor_zoom"> :
|
||
pincement à deux doigts pour zoomer, poignée
|
||
pour redimensionner la zone d'édition.</span>
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-library">
|
||
<h2 data-i18n="guide105.nav_library">⭐ Bibliothèque</h2>
|
||
<h3 data-i18n="guide105.lib_h3_bookmarks">Signets & récents</h3>
|
||
<p data-i18n="guide105.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é.</p>
|
||
<h3 data-i18n="guide105.lib_h3_saved">Recherches sauvegardées</h3>
|
||
<p data-i18n="guide105.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.</p>
|
||
<h3 data-i18n="guide105.lib_h3_backlinks">Backlinks & graphe</h3>
|
||
<p data-i18n="guide105.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.</p>
|
||
<h3 data-i18n="guide105.lib_h3_conflicts">Conflits de synchronisation</h3>
|
||
<p data-i18n="guide105.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.</p>
|
||
<h3 data-i18n="guide105.lib_h3_attach">Fichiers joints & médias</h3>
|
||
<p data-i18n-html="guide105.lib_attach">Les images <code>![[image.png]]</code>, 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.</p>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-graphe">
|
||
<h2 data-i18n="help.9c7a0a8d">🗺️ Vue Graphe</h2>
|
||
<p data-i18n="help.desc_graph_intro">
|
||
La vue graphe offre une visualisation
|
||
interactive des connexions entre vos notes via
|
||
les wikilinks.
|
||
</p>
|
||
|
||
<h3 data-i18n="help.b6619ac9">Ouverture</h3>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.graph_context">Menu contextuel</strong><span data-i18n="help.desc_context_menu">: Clic
|
||
droit sur un vault ou dossier → Vue
|
||
Graphique</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.popout_button">Bouton dédié</strong><span data-i18n="help.desc_e23c94dd"> : Dans la
|
||
barre d'outils (le cas échéant)</span></li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.61ee5a22">Interaction</h3>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.graph_zoom">Zoom/Pan</strong><span data-i18n="help.desc_f0adf67a"> : Molette pour
|
||
zoomer, cliquer-glisser pour se déplacer</span></li>
|
||
<li>
|
||
<strong data-i18n="help.graph_depth">Slider de profondeur</strong><span data-i18n="help.desc_9edeaa11"> :
|
||
Contrôle la profondeur d'exploration (0-3)</span></li>
|
||
<li>
|
||
<strong data-i18n="help.graph_tag_filter">Filtre par tag</strong><span data-i18n="help.desc_89bd3c4c"> : N'affiche
|
||
que les nœuds avec un tag spécifique</span></li>
|
||
<li>
|
||
<strong data-i18n="help.graph_type_filter">Filtre par type</strong><span data-i18n="help.desc_filter_type">:
|
||
Inclut/exclut fichiers, dossiers, markdown,
|
||
autres</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.graph_visual">Recherche visuelle</strong><span data-i18n="help.desc_visual_search">: Barre
|
||
de recherche pour surligner des nœuds</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.graph_focus">Mode focus</strong><span data-i18n="help.desc_a9db82b5"> : Centre la vue
|
||
sur un nœud spécifique</span></li>
|
||
<li>
|
||
<strong data-i18n="help.graph_nav_arrows">Navigation ← → ↑</strong><span data-i18n="help.desc_graph_nav">:
|
||
Historique de navigation entre les nœuds</span>
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.8caed987">Affichage</h3>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.graph_nodes">Nœuds</strong><span data-i18n="help.desc_ab2c4150"> : Fichiers (carrés)
|
||
et dossiers (cercles) avec icônes</span></li>
|
||
<li>
|
||
<strong data-i18n="help.nodes_edges">Arêtes</strong><span data-i18n="help.desc_edges">: Wikilinks sortants
|
||
(bleu) et backlinks entrants (vert)</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.graph_info">Panneau d'info</strong><span data-i18n="help.desc_8717768b"> :
|
||
Métadonnées du nœud (tags, liens, chemin)</span></li>
|
||
<li>
|
||
<strong data-i18n="help.graph_preview">Ctrl+survol</strong><span data-i18n="help.desc_a579cf4d"> : Aperçu du
|
||
contenu de la note sans naviguer</span></li>
|
||
<li>
|
||
<strong data-i18n="graph.fullscreen">Plein écran</strong><span data-i18n="help.desc_fullscreen">: Bouton pour
|
||
agrandir la vue</span>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.graph_export">Export PNG</strong><span data-i18n="help.desc_export_png">: Sauvegardez le
|
||
graphe en image</span>
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 id="cfg-performance" data-i18n="config.section_perf">Performance</h3>
|
||
<p data-i18n="auto.aa8ca265">
|
||
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.
|
||
</p>
|
||
</section>
|
||
|
||
<section
|
||
class="help-section"
|
||
id="help-personnalisation"
|
||
>
|
||
<h2 data-i18n="help.86e3825d">🎨 Personnalisation</h2>
|
||
|
||
<h3 data-i18n="auto.337d8626">Thème clair/sombre</h3>
|
||
<p data-i18n="help.theme_toggle">Basculez entre les thèmes :</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.menu_options">Menu Options</strong><span data-i18n="help.desc_beed5686"> : Cliquez sur
|
||
"Thème" dans le menu</span></li>
|
||
<li>
|
||
<strong>Raccourci</strong> :
|
||
<code>Ctrl+T</code>
|
||
</li>
|
||
<li>
|
||
<strong>Persistance</strong><span data-i18n="help.desc_e5b67e3d"> : Votre choix
|
||
est sauvegardé en localStorage</span></li>
|
||
<li>
|
||
<strong>Highlight.js</strong><span data-i18n="help.desc_d5c977ac"> : Les blocs de
|
||
code s'adaptent au thème</span></li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.7bcbb2e3">Redimensionnement de la sidebar</h3>
|
||
<p>
|
||
Sur desktop, ajustez la largeur de la sidebar :
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong>Handle de redimensionnement</strong>
|
||
: Bordure droite de la sidebar
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="auto.1a8a8118">Glisser-déposer</strong> : Cliquez
|
||
et glissez pour ajuster
|
||
</li>
|
||
<li>
|
||
<strong>Limites</strong> : Min 200px, Max
|
||
500px
|
||
</li>
|
||
<li>
|
||
<strong>Persistance</strong><span data-i18n="help.desc_6a80241e"> : La largeur
|
||
est sauvegardée</span></li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="auto.9122fd5c">Configuration avancée</h3>
|
||
<p>
|
||
Le modal Configurations (menu Options) permet de
|
||
:
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="config.section_search">Paramètres de recherche</strong><span data-i18n="help.desc_6b88a375"> :
|
||
Délai debounce, résultats par page, timeout</span></li>
|
||
<li>
|
||
<strong data-i18n="settings.backend_section">Paramètres backend</strong><span data-i18n="help.desc_5d0fbb81"> :
|
||
Workers, boosts TF-IDF, expansions préfixe</span></li>
|
||
<li>
|
||
<strong data-i18n="config.tag_filtering_label">Filtrage de tags</strong> : Patterns
|
||
pour masquer les tags template
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="config.diagnostics">Diagnostics</strong><span data-i18n="help.desc_6ae5342c"> : Stats index,
|
||
mémoire, moteur de recherche</span></li>
|
||
<li>
|
||
<strong data-i18n="auto.83a0efdf">Réindexation</strong> : Forcer un
|
||
rescan des vaults
|
||
</li>
|
||
</ul>
|
||
<h3 data-i18n="guide105.h3_panes">Vue multi-panneaux (split view)</h3>
|
||
<p data-i18n="guide105.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.</p>
|
||
</section>
|
||
|
||
<!-- ⌨️ Palette de commandes -->
|
||
<section class="help-section" id="help-palette">
|
||
<h2 data-i18n="help.62308f96">⌨️ Palette de commandes</h2>
|
||
<p data-i18n="auto.774dbd63">
|
||
La palette de commandes permet d'accéder
|
||
rapidement aux fichiers et aux actions sans
|
||
utiliser la souris.
|
||
</p>
|
||
|
||
<h3 data-i18n="help.b6619ac9">Ouverture</h3>
|
||
<ul>
|
||
<li>
|
||
<strong>Ctrl+Shift+Espace</strong> : Palette
|
||
de fichiers (recherche par titre)
|
||
</li>
|
||
<li>
|
||
<strong>Ctrl+Alt+Espace</strong> : Palette
|
||
de commandes (liste d'actions)
|
||
</li>
|
||
<li>
|
||
<strong>Tab</strong> : Bascule entre mode
|
||
fichiers et mode commandes
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.6d547397">Mode fichiers</h3>
|
||
<p>
|
||
Tapez le nom d'un fichier pour le trouver
|
||
rapidement. La recherche utilise l'API
|
||
<code>/api/suggest</code> pour des suggestions
|
||
instantanées. Appuyez sur <kbd>Enter</kbd> pour
|
||
ouvrir le fichier sélectionné.
|
||
</p>
|
||
|
||
<h3 data-i18n="help.3cf370b1">Mode commandes</h3>
|
||
<p>
|
||
Tapez <code>></code> (ou Ctrl+Alt+Espace)
|
||
pour voir la liste des actions disponibles :
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
🏠 <strong data-i18n="viewer.breadcrumb_home">Accueil</strong> : Retour à
|
||
l'accueil
|
||
</li>
|
||
<li>
|
||
✏️ <strong data-i18n="auto.b893650a">Éditer fichier courant</strong> :
|
||
Ouvre l'éditeur sur l'onglet actif
|
||
</li>
|
||
<li>
|
||
🪟 <strong>Pop-out</strong> : Ouvre le
|
||
document dans une fenêtre détachée
|
||
</li>
|
||
<li>
|
||
📄 <strong data-i18n="auto.aedff644">Créer fichier markdown</strong> :
|
||
Crée un nouveau fichier .md et l'ouvre dans
|
||
l'éditeur
|
||
</li>
|
||
<li>
|
||
🗑️
|
||
<strong>Supprimer fichier courant</strong> :
|
||
Supprime le fichier de l'onglet actif
|
||
</li>
|
||
<li>
|
||
📁 <strong data-i18n="auto.195a8e44">Créer répertoire</strong> : Crée
|
||
un nouveau dossier
|
||
</li>
|
||
<li>
|
||
🗑️ <strong data-i18n="auto.5a486420">Supprimer répertoire</strong> :
|
||
Supprime un dossier et son contenu
|
||
</li>
|
||
<li>
|
||
🌓 <strong data-i18n="theme.change">Changer le thème</strong> :
|
||
Bascule clair/sombre
|
||
</li>
|
||
<li>
|
||
🔄 <strong data-i18n="config.reindex">Réindexer</strong> : Force la
|
||
réindexation complète
|
||
</li>
|
||
<li>
|
||
❓ <strong data-i18n="header.help">Aide</strong> : Ouvre ce guide
|
||
</li>
|
||
<li>
|
||
⚙️ <strong data-i18n="config.title">Configuration</strong> : Ouvre
|
||
les paramètres
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.nav_navigation">Navigation</h3>
|
||
<ul>
|
||
<li>
|
||
<kbd>↑</kbd> <kbd>↓</kbd> : Se déplacer dans
|
||
la liste
|
||
</li>
|
||
<li><kbd>Enter</kbd> : Ouvrir/sélectionner</li>
|
||
<li><kbd>Esc</kbd> : Fermer la palette</li>
|
||
<li>
|
||
<kbd>Tab</kbd> : Basculer entre fichiers et
|
||
commandes
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-raccourcis">
|
||
<h2 data-i18n="help.2a2e5b0f">⌨️ Raccourcis clavier</h2>
|
||
|
||
<h3 data-i18n="help.nav_navigation">Navigation</h3>
|
||
<ul>
|
||
<li>
|
||
<code>Ctrl+K</code> ou <code>Cmd+K</code> :
|
||
Focaliser la barre de recherche
|
||
</li>
|
||
<li>
|
||
<code>/</code> : Focaliser la recherche
|
||
(hors champ texte)
|
||
</li>
|
||
<li>
|
||
<code>Escape</code> : Fermer les suggestions
|
||
/ quitter la recherche
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="auto.13c3ee2c">Recherche et autocomplétion</h3>
|
||
<ul>
|
||
<li>
|
||
<code>↑</code> / <code>↓</code> : Naviguer
|
||
dans les suggestions
|
||
</li>
|
||
<li>
|
||
<code>Enter</code> : Sélectionner la
|
||
suggestion active ou lancer la recherche
|
||
</li>
|
||
<li>
|
||
<code>Escape</code> : Fermer le dropdown de
|
||
suggestions
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.nav_interface">Interface</h3>
|
||
<ul>
|
||
<li>
|
||
<code>Ctrl+T</code> : Basculer le thème
|
||
clair/sombre
|
||
</li>
|
||
<li>
|
||
<code>Ctrl+Shift+Espace</code> : Palette de
|
||
fichiers
|
||
</li>
|
||
<li>
|
||
<code>Ctrl+Alt+Espace</code> : Palette de
|
||
commandes
|
||
</li>
|
||
<li>
|
||
<code>Escape</code> : Fermer la palette /
|
||
les suggestions
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.nav_tabs">Onglets</h3>
|
||
<ul>
|
||
<li>
|
||
<code>Ctrl+W</code> : Fermer l'onglet actif
|
||
</li>
|
||
<li><code>Ctrl+Tab</code> : Onglet suivant</li>
|
||
<li>
|
||
<code>Ctrl+Shift+Tab</code> : Onglet
|
||
précédent
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="auto.a131dfab">Éditeur CodeMirror</h3>
|
||
<ul>
|
||
<li>
|
||
<code>Ctrl+F</code> : Rechercher dans le
|
||
fichier
|
||
</li>
|
||
<li><code>Ctrl+Z</code> : Annuler</li>
|
||
<li><code>Ctrl+Y</code> : Refaire</li>
|
||
<li>
|
||
<code>Ctrl+S</code> : Sauvegarder (si
|
||
configuré)
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-ai">
|
||
<h2 data-i18n="auto.c4922a99">🤖 Intelligence Artificielle dans l'Éditeur</h2>
|
||
<p>
|
||
ObsiGate intègre un assistant IA directement
|
||
dans l'éditeur de texte. Pour l'activer,
|
||
configurez au moins un provider AI dans le
|
||
fichier <code>.env</code> (DeepSeek, OpenRouter
|
||
ou Gemini).
|
||
</p>
|
||
|
||
<h3 data-i18n="help.activation">Activation</h3>
|
||
<ol>
|
||
<li>
|
||
Ajoutez votre clé API dans
|
||
<code>.env</code> :
|
||
<code>DEEPSEEK_API_KEY=sk-...</code>
|
||
</li>
|
||
<li>
|
||
Redémarrez le conteneur :
|
||
<code
|
||
>docker compose down && docker compose
|
||
up -d --build</code
|
||
>
|
||
</li>
|
||
<li data-i18n="auto.5d4a2516">
|
||
Ouvrez un fichier en mode Édition (bouton ✏️
|
||
Éditer)
|
||
</li>
|
||
<li data-i18n="auto.396d0dd8">
|
||
La barre d'outils AI apparaît sous le titre
|
||
de l'éditeur
|
||
</li>
|
||
</ol>
|
||
|
||
<h3 data-i18n="help.toolbar_section">Barre d'outils AI</h3>
|
||
<p>
|
||
La barre d'outils contient les boutons suivants
|
||
:
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong>✦ AI</strong><span data-i18n="help.desc_3ace259e"> — Complétion inline
|
||
(</span><kbd>Ctrl+J</kbd>). Complète
|
||
automatiquement le texte là où se trouve le
|
||
curseur.
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="auto.a105a2d9">Éditer ▾</strong><span data-i18n="help.desc_a0ec890a"> — Menu de
|
||
modification du texte sélectionné :</span><ul>
|
||
<li>
|
||
🪄
|
||
<strong>Improve writing</strong> :
|
||
Améliore la qualité, la clarté et le
|
||
flux du texte
|
||
</li>
|
||
<li>
|
||
🔤
|
||
<strong
|
||
>Fix spelling & grammar</strong
|
||
>
|
||
: Corrige les fautes d'orthographe
|
||
et de grammaire
|
||
</li>
|
||
<li>
|
||
📏 <strong>Make shorter</strong> :
|
||
Rend le texte plus concis tout en
|
||
préservant l'information
|
||
</li>
|
||
<li>
|
||
📐 <strong>Make longer</strong> :
|
||
Ajoute des détails, exemples ou
|
||
explications
|
||
</li>
|
||
<li>
|
||
📋
|
||
<strong>Simplify language</strong> :
|
||
Simplifie le langage, évite le
|
||
jargon
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<strong>Ton ▾</strong> — Change le ton du
|
||
texte :
|
||
<ul>
|
||
<li>
|
||
💼 <strong>Professional</strong> :
|
||
Ton formel et professionnel
|
||
</li>
|
||
<li>
|
||
💬 <strong>Casual</strong> : Ton
|
||
décontracté et conversationnel
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<strong>Traduire ▾</strong><span data-i18n="help.desc_1c130d89"> — Traduit le
|
||
texte sélectionné vers une autre langue :</span><ul>
|
||
<li>
|
||
🇬🇧 English · 🇨🇳 Chinese · 🇯🇵
|
||
Japanese · 🇩🇪 German · 🇫🇷 French ·
|
||
🇪🇸 Spanish
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="auto.ce4eb802">Générer ▾</strong><span data-i18n="help.desc_991307d6"> — Génère du
|
||
contenu à partir de la sélection :</span><ul>
|
||
<li>
|
||
ℹ️ <strong>Explain this</strong> :
|
||
Explique le texte sélectionné de
|
||
façon claire
|
||
</li>
|
||
<li>
|
||
📝 <strong>Summarize</strong> :
|
||
Résume le texte de façon concise
|
||
</li>
|
||
<li>
|
||
✏️
|
||
<strong>Continue writing</strong> :
|
||
Continue l'écriture à partir du
|
||
texte
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="auto.62c3d50d">💬 Réécrire</strong><span data-i18n="help.desc_c63f02cc"> — Réécrit le
|
||
texte selon vos instructions personnalisées.
|
||
Une boîte de dialogue vous demande votre
|
||
instruction.</span></li>
|
||
<li>
|
||
<strong data-i18n="auto.0257e77f">🧰 Boîte ▾</strong> — Outils de
|
||
conversion :
|
||
<ul>
|
||
<li>
|
||
📋
|
||
<strong>Convert to list</strong> :
|
||
Transforme le texte en liste
|
||
Markdown
|
||
</li>
|
||
<li>
|
||
📊
|
||
<strong>Convert to table</strong> :
|
||
Transforme en tableau Markdown
|
||
</li>
|
||
<li>
|
||
⚙️
|
||
<strong
|
||
>Generate frontmatter</strong
|
||
>
|
||
: Génère un bloc YAML frontmatter
|
||
</li>
|
||
<li>
|
||
🔷
|
||
<strong>Convert to canvas</strong> :
|
||
Convertit en diagramme Mermaid.js
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.096367af">Indicateurs visuels</h3>
|
||
<ul>
|
||
<li>
|
||
Pendant une requête AI, un toast
|
||
<strong
|
||
>⏳ AI: traitement en cours...</strong
|
||
>
|
||
s'affiche
|
||
</li>
|
||
<li>
|
||
Une fois terminé,
|
||
<strong data-i18n="auto.4d281c6c">AI: texte traité ✓</strong> confirme
|
||
le succès
|
||
</li>
|
||
<li>
|
||
En cas d'erreur de clé API :
|
||
<strong data-i18n="auto.13eb7a51">AI: clé API invalide</strong>
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.2dad2bfd">Sauvegarde automatique</h3>
|
||
<p>
|
||
L'éditeur sauvegarde automatiquement après 2
|
||
secondes d'inactivité. Le bouton de sauvegarde
|
||
<strong>flashe en vert</strong> brièvement pour
|
||
confirmer.
|
||
</p>
|
||
|
||
<h3 data-i18n="auto.272dd29a">Providers supportés</h3>
|
||
<table>
|
||
<tr>
|
||
<th>Provider</th>
|
||
<th>Variable .env</th>
|
||
<thdata-i18n="auto.033a9ddf">Modèle par défaut</th>
|
||
</tr>
|
||
<tr>
|
||
<td>DeepSeek</td>
|
||
<td><code>DEEPSEEK_API_KEY</code></td>
|
||
<td><code>deepseek-chat</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>OpenRouter</td>
|
||
<td><code>OPENROUTER_API_KEY</code></td>
|
||
<td><code>openai/gpt-4o-mini</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Gemini</td>
|
||
<td><code>GEMINI_API_KEY</code></td>
|
||
<td><code>gemini-2.0-flash</code></td>
|
||
</tr>
|
||
</table>
|
||
<p>
|
||
Pour changer de provider par défaut :
|
||
<code>AI_DEFAULT_PROVIDER=openrouter</code>
|
||
</p>
|
||
|
||
<h3 data-i18n="help.assistant_panel">🧠 Panneau Assistant (BooksLM)</h3>
|
||
<p data-i18n="help.assistant_panel_desc">
|
||
L'assistant latéral (bouton flottant ou menu contextuel d'un
|
||
dossier) répond en Markdown formaté et contextualise vos
|
||
répertoires ou documents.
|
||
</p>
|
||
<ul>
|
||
<li data-i18n="help.assistant_markdown">
|
||
Réponses formatées : titres, listes, tableaux, citations et
|
||
blocs de code.
|
||
</li>
|
||
<li data-i18n="help.assistant_insert">
|
||
Le bouton « Ajouter » (au survol d'une réponse) insère la
|
||
réponse dans le document ouvert dans l'éditeur (Editer ou
|
||
Forge) ; chaque bloc de code propose « Ajouter la section »
|
||
pour n'insérer que ce bloc.
|
||
</li>
|
||
<li data-i18n="help.assistant_links">
|
||
Les fichiers et chemins cités sont des liens : cliquez sur un
|
||
fichier pour l'ouvrir, sur un dossier pour le révéler dans
|
||
l'arborescence.
|
||
</li>
|
||
<li data-i18n="help.assistant_sessions">
|
||
L'icône historique de l'en-tête liste les sessions passées
|
||
(recharger ou supprimer) ; « + » démarre une nouvelle
|
||
conversation.
|
||
</li>
|
||
<li data-i18n="help.assistant_agent">
|
||
L'assistant utilise toujours ses outils (lire, lister,
|
||
chercher, modifier) ; les actions de modification demandent
|
||
une confirmation avec aperçu des changements.
|
||
</li>
|
||
<li data-i18n="help.assistant_agent_run">
|
||
Les actions s'affichent dans le fil : l'assistant regroupe les
|
||
modifications en une seule approbation (« Tout approuver ») et
|
||
met à jour l'arborescence et le document ouvert dès qu'elles
|
||
sont appliquées. Le bouton d'envoi devient « Stop » pour
|
||
interrompre l'exécution à tout moment.
|
||
</li>
|
||
<li data-i18n="help.assistant_resize">
|
||
Le bord gauche du panneau est redimensionnable ; la largeur est
|
||
mémorisée.
|
||
</li>
|
||
<li data-i18n="help.assistant_at">
|
||
Tapez <code>@</code> pour joindre un fichier ou un répertoire au
|
||
contexte, ou pour attacher une image d'un répertoire.
|
||
</li>
|
||
<li data-i18n="help.assistant_slash">
|
||
Tapez <code>/</code> pour lancer un skill (recherche, résumé,
|
||
correction, plan…) ou une commande admin
|
||
(<code>/help</code>, <code>/providers</code>,
|
||
<code>/model</code>, <code>/keys</code>).
|
||
</li>
|
||
<li data-i18n="help.assistant_images">
|
||
Collez une image dans la zone de saisie (ou joignez-la) pour
|
||
l'analyser avec un modèle compatible vision. Le bouton ⓘ affiche
|
||
les capacités du modèle sélectionné, et la liste des modèles est
|
||
filtrable par recherche.
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<!-- 🔗 Partage -->
|
||
<section class="help-section" id="help-offline">
|
||
<h2 data-i18n="guide105.nav_offline">📴 Hors-ligne</h2>
|
||
<ul>
|
||
<li data-i18n="guide105.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.</li>
|
||
<li data-i18n="guide105.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.</li>
|
||
<li data-i18n="guide105.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.</li>
|
||
<li data-i18n="guide105.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.</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-collab">
|
||
<h2 data-i18n="guide105.nav_collab">👥 Collaboration</h2>
|
||
<ul>
|
||
<li data-i18n="guide105.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.</li>
|
||
<li data-i18n="guide105.col_cursors">Les curseurs et sélections des collaborateurs apparaissent avec une couleur et un nom par personne (awareness).</li>
|
||
<li data-i18n="guide105.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.</li>
|
||
<li data-i18n="guide105.col_perm">Accès limité aux utilisateurs authentifiés disposant de la permission sur la vault.</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-desktop">
|
||
<h2 data-i18n="guide105.nav_desktop">🖥️ Desktop</h2>
|
||
<ul>
|
||
<li data-i18n="guide105.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é).</li>
|
||
<li data-i18n="guide105.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.</li>
|
||
<li data-i18n="guide105.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.</li>
|
||
<li data-i18n="guide105.des_native">Menu système natif, raccourci global optionnel pour afficher/masquer la fenêtre et jumplist des vaults récents.</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-partage">
|
||
<h2 data-i18n="help.8c65b2f2">🔗 Partage et Webhooks</h2>
|
||
|
||
<h3 data-i18n="help.public_shares">Publication publique</h3>
|
||
<p>
|
||
ObsiGate permet de partager des documents via un
|
||
lien public temporaire.
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.share_button">Bouton Partager</strong><span data-i18n="help.desc_d66e911c"> : Dans les
|
||
actions du fichier, génère un lien unique</span></li>
|
||
<li>
|
||
<strong>Token unique</strong><span data-i18n="help.desc_04493800"> : 64
|
||
caractères hexadécimaux, impossible à
|
||
deviner</span></li>
|
||
<li>
|
||
<strong>Expiration configurable</strong><span data-i18n="help.desc_858681a1"> :
|
||
Durée de vie en heures</span></li>
|
||
<li>
|
||
<strong>Lecture seule</strong><span data-i18n="help.desc_e47d2328"> : La vue
|
||
publique n'affiche que le contenu sans
|
||
édition</span></li>
|
||
<li>
|
||
<strong>Gestion des liens</strong><span data-i18n="help.desc_5e9a7fd9"> : Liste
|
||
et révocation dans les Configurations</span></li>
|
||
<li>
|
||
<strong data-i18n="help.pdf_export">Export PDF</strong><span data-i18n="help.desc_e93517a5"> : Téléchargement
|
||
du document partagé en PDF via</span><code>/s/{token}/pdf</code>
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<!-- 🪝 Webhooks -->
|
||
<section class="help-section" id="help-webhooks">
|
||
<h2 data-i18n="help.29afa01d">🪝 Webhooks</h2>
|
||
<p>
|
||
Les webhooks permettent à ObsiGate de
|
||
<strong
|
||
>notifier automatiquement des services
|
||
externes</strong
|
||
>
|
||
lorsque des fichiers ou dossiers sont modifiés.
|
||
Chaque événement déclenche un
|
||
<code>POST</code> HTTP vers l'URL de votre
|
||
choix, avec un payload JSON détaillé.
|
||
</p>
|
||
|
||
<h3 data-i18n="help.use_cases">Cas d'usage</h3>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.ci_cd">Automatisation CI/CD</strong><span data-i18n="help.desc_1ba39cc4"> :
|
||
Déclencher un build quand une note de
|
||
procédure est mise à jour</span></li>
|
||
<li>
|
||
<strong data-i18n="header.sync_status">Synchronisation</strong><span data-i18n="help.desc_d9cdaa89"> : Copier
|
||
les nouveaux fichiers vers un autre système
|
||
(SharePoint, Google Drive)</span></li>
|
||
<li>
|
||
<strong>Notifications</strong><span data-i18n="help.desc_6f7c5889"> : Alerter un
|
||
canal Slack/Discord quand un document est
|
||
créé</span></li>
|
||
<li>
|
||
<strong data-i18n="help.external_audit">Audit externe</strong><span data-i18n="help.desc_599f3242"> : Logger
|
||
toutes les modifications dans une base de
|
||
données externe</span></li>
|
||
<li>
|
||
<strong data-i18n="auto.a232672b">Déploiement</strong><span data-i18n="help.desc_2f2c16b5"> : Publier un
|
||
article de blog quand un fichier est marqué</span><code>publish: true</code>
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="auto.ba768f01">Événements supportés</h3>
|
||
<div style="overflow-x: auto">
|
||
<table
|
||
style="
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 0.85rem;
|
||
margin: 8px 0 16px;
|
||
"
|
||
>
|
||
<tr style="background: var(--bg-secondary)">
|
||
<th
|
||
style="
|
||
padding: 8px 12px;
|
||
text-align: left;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
data-i18n="auto.f5a8c116">
|
||
Événement
|
||
</th>
|
||
<th
|
||
style="
|
||
padding: 8px 12px;
|
||
text-align: left;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
data-i18n="auto.d4d28717">
|
||
Déclencheur
|
||
</th>
|
||
<th
|
||
style="
|
||
padding: 8px 12px;
|
||
text-align: left;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
data-i18n="auto.460b8298">
|
||
Données
|
||
</th>
|
||
</tr>
|
||
<tr>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
<code>file_created</code>
|
||
</td>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
data-i18n="auto.a05514cb">
|
||
Création via l'API ou la palette
|
||
</td>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
<code>vault</code>,
|
||
<code>path</code>
|
||
</td>
|
||
</tr>
|
||
<tr style="background: var(--bg-secondary)">
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
<code>file_deleted</code>
|
||
</td>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
data-i18n="auto.fc694490">
|
||
Suppression via l'API ou l'éditeur
|
||
</td>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
<code>vault</code>,
|
||
<code>path</code>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
<code>file_renamed</code>
|
||
</td>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
Renommage via l'API
|
||
</td>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
<code>vault</code>,
|
||
<code>old_path</code>,
|
||
<code>new_path</code>
|
||
</td>
|
||
</tr>
|
||
<tr style="background: var(--bg-secondary)">
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
<code>directory_created</code>
|
||
</td>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
data-i18n="auto.2bc66bab">
|
||
Création de dossier via l'API
|
||
</td>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
<code>vault</code>,
|
||
<code>path</code>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
<code>directory_deleted</code>
|
||
</td>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
Suppression de dossier via l'API
|
||
</td>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
<code>vault</code>,
|
||
<code>path</code>
|
||
</td>
|
||
</tr>
|
||
<tr style="background: var(--bg-secondary)">
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
<code>directory_renamed</code>
|
||
</td>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
Renommage de dossier via l'API
|
||
</td>
|
||
<td
|
||
style="
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid
|
||
var(--border);
|
||
"
|
||
>
|
||
<code>vault</code>,
|
||
<code>old_path</code>,
|
||
<code>new_path</code>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<p>
|
||
<em
|
||
>Note : l'événement
|
||
<code>file_modified</code> est réservé pour
|
||
usage futur. Les modifications de contenu
|
||
via l'éditeur ne déclenchent pas encore de
|
||
webhook.</em
|
||
>
|
||
</p>
|
||
|
||
<h3 data-i18n="help.77f4f06e">Format du payload</h3>
|
||
<p>
|
||
Chaque webhook reçoit un <code>POST</code> avec
|
||
<code>Content-Type: application/json</code> :
|
||
</p>
|
||
<pre
|
||
style="
|
||
background: var(--bg-secondary);
|
||
padding: 12px;
|
||
border-radius: 8px;
|
||
overflow-x: auto;
|
||
font-size: 0.78rem;
|
||
"
|
||
><code>{
|
||
"event": "file_created",
|
||
"timestamp": "2026-06-03T14:30:00.123456+00:00",
|
||
"data": {
|
||
"vault": "IT",
|
||
"path": "procedures/deploiement.md"
|
||
}
|
||
}</code></pre>
|
||
|
||
<h3 data-i18n="auto.28bf6aab">Sécurité : signature HMAC-SHA256</h3>
|
||
<p>
|
||
Si vous configurez un secret, chaque requête
|
||
inclut un header
|
||
<code>X-ObsiGate-Signature</code> :
|
||
</p>
|
||
<pre
|
||
style="
|
||
background: var(--bg-secondary);
|
||
padding: 12px;
|
||
border-radius: 8px;
|
||
overflow-x: auto;
|
||
font-size: 0.78rem;
|
||
"
|
||
><code>X-ObsiGate-Signature: sha256=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08</code></pre>
|
||
<p>
|
||
Pour vérifier côté récepteur : calculez
|
||
<code>HMAC-SHA256(corps_brut, secret)</code> et
|
||
comparez. Le header
|
||
<code>X-ObsiGate-Event</code> contient aussi le
|
||
type d'événement pour un routage rapide.
|
||
</p>
|
||
|
||
<h3 data-i18n="config.title">Configuration</h3>
|
||
<p>
|
||
Les webhooks se gèrent dans le modal
|
||
<strong data-i18n="header.menu_config">Configurations</strong> (⚙️ menu
|
||
Options), section Webhooks. Réservé aux
|
||
administrateurs.
|
||
</p>
|
||
<ol>
|
||
<li>
|
||
Allez dans ⚙️ Configurations → onglet
|
||
Webhooks
|
||
</li>
|
||
<li>
|
||
Remplissez :
|
||
<strong data-i18n="config.webhook_name_placeholder">Nom</strong> (descriptif),
|
||
<strong data-i18n="config.webhook_url_placeholder">URL</strong> (endpoint cible),
|
||
<strong>Secret</strong> (optionnel)
|
||
</li>
|
||
<li data-i18n="auto.7c9cdc3c">Cochez les événements à écouter</li>
|
||
<li>Cliquez <strong data-i18n="config.add_webhook">Ajouter</strong></li>
|
||
</ol>
|
||
<p>
|
||
Les webhooks peuvent être activés/désactivés,
|
||
modifiés ou supprimés à tout moment. Le
|
||
<code>last_fired_at</code> indique la dernière
|
||
exécution.
|
||
</p>
|
||
|
||
<h3 data-i18n="help.550b0240">Exemples concrets</h3>
|
||
|
||
<h4 data-i18n="auto.b419c2c2">Récepteur Python (Flask)</h4>
|
||
<pre
|
||
style="
|
||
background: var(--bg-secondary);
|
||
padding: 12px;
|
||
border-radius: 8px;
|
||
overflow-x: auto;
|
||
font-size: 0.78rem;
|
||
"
|
||
><code>from flask import Flask, request\nimport hmac, hashlib\n\napp = Flask(__name__)\nWEBHOOK_SECRET = "mon-secret" # même que dans ObsiGate\n\[email protected]("/webhook", methods=["POST"])\ndef webhook():\n # Vérifier la signature\n signature = request.headers.get("X-ObsiGate-Signature", "")\n body = request.get_data()\n expected = "sha256=" + hmac.new(\n WEBHOOK_SECRET.encode(), body, hashlib.sha256\n ).hexdigest()\n if not hmac.compare_digest(signature, expected):\n return "Invalid signature", 403\n\n payload = request.get_json()\n print(f"Événement: {payload['event']}")\n print(f"Fichier: {payload['data']['vault']}/{payload['data']['path']}")\n return "OK", 200\n\napp.run(port=5000)</code></pre>
|
||
|
||
<h4 data-i18n="help.d1ea1dc6">n8n / Make (no-code)</h4>
|
||
<ol>
|
||
<li>
|
||
Créez un
|
||
<strong>Webhook Trigger</strong> dans n8n ou
|
||
Make
|
||
</li>
|
||
<li>
|
||
Copiez l'URL générée (ex:
|
||
<code
|
||
>https://n8n.monserveur.com/webhook/abc123</code
|
||
>)
|
||
</li>
|
||
<li>
|
||
Dans ObsiGate, ajoutez un webhook pointant
|
||
vers cette URL
|
||
</li>
|
||
<li data-i18n="auto.14c75ac6">Sélectionnez les événements souhaités</li>
|
||
<li data-i18n="auto.2a5af19b">
|
||
Le workflow se déclenche automatiquement à
|
||
chaque événement
|
||
</li>
|
||
</ol>
|
||
|
||
<h4 data-i18n="help.229fd3e2">Notification Discord</h4>
|
||
<p>
|
||
Utilisez un webhook relais (n8n, Make, ou un
|
||
script) pour transformer le payload ObsiGate en
|
||
message Discord :
|
||
</p>
|
||
<pre
|
||
style="
|
||
background: var(--bg-secondary);
|
||
padding: 12px;
|
||
border-radius: 8px;
|
||
overflow-x: auto;
|
||
font-size: 0.78rem;
|
||
"
|
||
><code># Exemple avec curl + jq dans un script bash
|
||
WEBHOOK_URL="https://discord.com/api/webhooks/..."
|
||
OBSIGATE_SECRET="mon-secret"
|
||
|
||
# Lire le payload depuis stdin
|
||
read -r payload
|
||
event=$(echo "$payload" | jq -r '.event')
|
||
path=$(echo "$payload" | jq -r '.data.path')
|
||
|
||
curl -H "Content-Type: application/json" -d "{
|
||
\"content\": \"📝 **$event** — $path\"
|
||
}" "$WEBHOOK_URL"</code></pre>
|
||
|
||
<h4 data-i18n="help.40dd7e38">Tester avec curl</h4>
|
||
<pre
|
||
style="
|
||
background: var(--bg-secondary);
|
||
padding: 12px;
|
||
border-radius: 8px;
|
||
overflow-x: auto;
|
||
font-size: 0.78rem;
|
||
"
|
||
><code># Simuler ce qu'ObsiGate envoie (pour tester votre endpoint)
|
||
curl -X POST https://votre-serveur.com/webhook \
|
||
-H "Content-Type: application/json" \
|
||
-H "X-ObsiGate-Event: file_created" \
|
||
-H "X-ObsiGate-Signature: sha256=$(echo -n '{"event":"file_created",...}' | openssl dgst -sha256 -hmac 'mon-secret' | cut -d' ' -f2)" \
|
||
-d '{"event":"file_created","timestamp":"2026-06-03T14:30:00Z","data":{"vault":"IT","path":"notes/test.md"}}'</code></pre>
|
||
|
||
<h3 data-i18n="help.381abec7">Limitations actuelles</h3>
|
||
<ul>
|
||
<li>
|
||
Timeout de <strong>5 secondes</strong> par
|
||
requête — les webhooks sont "fire and
|
||
forget" (pas de retry)
|
||
</li>
|
||
<li>
|
||
Les modifications détectées par le watcher
|
||
filesystem (watchdog)
|
||
<strong data-i18n="auto.98c547ea">ne déclenchent pas</strong> de
|
||
webhooks — seules les opérations via l'API
|
||
REST sont prises en compte
|
||
</li>
|
||
<li>
|
||
<code>file_modified</code> n'est pas encore
|
||
dispatché (réservé pour version future)
|
||
</li>
|
||
<li data-i18n="auto.1d3330cc">Pas de retry automatique en cas d'échec</li>
|
||
<li data-i18n="auto.4dfa6a25">
|
||
Pas de filtrage par vault — un webhook
|
||
écoute toutes les vaults
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
<section class="help-section" id="help-sauvegardes">
|
||
<h2 data-i18n="help.62549eca">💾 Sauvegardes et Audits</h2>
|
||
|
||
<h3 data-i18n="help.backup_auto">Backup automatique</h3>
|
||
<p data-i18n="auto.ab5e6155">
|
||
ObsiGate crée automatiquement des sauvegardes
|
||
avant chaque modification destructive.
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="help.before_write">Avant écriture</strong> : Sauvegarde
|
||
du contenu original dans
|
||
<code>.obsigate-backup/</code>
|
||
</li>
|
||
<li>
|
||
<strong>Timestamp</strong><span data-i18n="help.desc_31e7253b"> : Chaque backup
|
||
est horodaté</span></li>
|
||
<li>
|
||
<strong data-i18n="auto.41d7c6af">Répertoire dédié</strong> :
|
||
<code>.obsigate-backup/</code> dans le
|
||
répertoire de la vault
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.audit_log">Audit log</h3>
|
||
<p data-i18n="auto.2bc24d2e">
|
||
Toutes les opérations sensibles sont tracées
|
||
dans un journal.
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong>Journal</strong> : Fichier
|
||
<code>data/audit.log</code> au format JSON
|
||
lines
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="auto.598a0ca7">Événements tracés</strong><span data-i18n="help.desc_9f321d1e"> :
|
||
Écritures, suppressions, changements de
|
||
configuration</span></li>
|
||
<li>
|
||
<strong>Rotation</strong><span data-i18n="help.desc_19cf5730"> : Rotation
|
||
automatique à 10 Mo</span></li>
|
||
</ul>
|
||
</section>
|
||
|
||
<!-- 🛡️ Sécurité -->
|
||
<section class="help-section" id="help-securite">
|
||
<h2 data-i18n="auto.37561472">🛡️ Sécurité</h2>
|
||
|
||
<h3 data-i18n="help.c48fb18a">Authentification</h3>
|
||
<ul>
|
||
<li>
|
||
<strong>JWT + Argon2id</strong><span data-i18n="help.desc_8f0bffe3"> : Tokens JWT
|
||
avec mots de passe hachés Argon2id</span></li>
|
||
<li>
|
||
<strong data-i18n="auto.72e515fa">Contrôle d'accès par vault</strong>
|
||
: Chaque utilisateur voit uniquement ses
|
||
vaults
|
||
</li>
|
||
<li>
|
||
<strong>Refresh tokens</strong> : Cookies
|
||
HttpOnly avec rotation automatique
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.rate_limiting">Rate limiting</strong> : 10
|
||
tentatives / 15 min par IP + verrouillage
|
||
compte
|
||
</li>
|
||
<li>
|
||
<strong>Compte admin</strong><span data-i18n="help.desc_5fc28b7d"> : Créé
|
||
automatiquement au premier démarrage</span></li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.83f1d9ca">Protection</h3>
|
||
<ul>
|
||
<li>
|
||
<strong>Secret redactor</strong><span data-i18n="help.desc_9846fc07"> : Masque
|
||
JWT, clés API, tokens GitHub dans les
|
||
aperçus</span></li>
|
||
<li>
|
||
<strong>Path traversal</strong> : Validation
|
||
des chemins contre les attaques
|
||
<code>../</code>
|
||
</li>
|
||
<li>
|
||
<strong>Headers CSP</strong> :
|
||
Content-Security-Policy, X-Frame-Options,
|
||
XSS-Protection
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.atomic_writes">Atomic writes</strong><span data-i18n="help.desc_752789ab"> : Écriture
|
||
via fichier temporaire + renommage atomique</span></li>
|
||
<li>
|
||
<strong>Non-root Docker</strong> : Conteneur
|
||
tourne avec UID 1000
|
||
</li>
|
||
</ul>
|
||
<h3 data-i18n="guide105.h3_mfa">MFA : TOTP, WebAuthn, codes de secours</h3>
|
||
<p data-i18n="guide105.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.</p>
|
||
<h3 data-i18n="guide105.h3_admin">Tableau de bord administrateur</h3>
|
||
<p data-i18n-html="guide105.admin_p">Le rôle admin ouvre une page dédiée <code>/admin.html</code> (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.</p>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-astuces">
|
||
<h2 data-i18n="help.686f8313">💡 Astuces et bonnes pratiques</h2>
|
||
|
||
<h3 data-i18n="auto.559ef1da">Workflow recommandé</h3>
|
||
<ol>
|
||
<li>
|
||
<strong data-i18n="auto.678711b5">Sélectionnez un contexte</strong><span data-i18n="help.desc_39979e71"> :
|
||
Choisissez un vault pour réduire le bruit</span></li>
|
||
<li>
|
||
<strong>Explorez l'arborescence</strong> :
|
||
Naviguez dans les dossiers
|
||
</li>
|
||
<li>
|
||
<strong>Utilisez les tags</strong> : Filtrez
|
||
par tags pour affiner
|
||
</li>
|
||
<li>
|
||
<strong>Recherchez intelligemment</strong><span data-i18n="help.desc_ae213f54"> :
|
||
Combinez opérateurs et texte libre</span></li>
|
||
<li>
|
||
<strong>Profitez de l'historique</strong><span data-i18n="help.desc_71eeb16a"> :
|
||
Réutilisez vos recherches fréquentes</span></li>
|
||
</ol>
|
||
|
||
<h3 data-i18n="help.fe388b67">Optimisations</h3>
|
||
<ul>
|
||
<li>
|
||
<strong>Masquez la sidebar</strong> : Plus
|
||
d'espace pour le contenu
|
||
</li>
|
||
<li>
|
||
<strong
|
||
>Utilisez le filtrage sidebar</strong
|
||
>
|
||
: Plus rapide que la recherche globale pour
|
||
trouver un fichier
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="auto.3be698f6">Favorisez les opérateurs</strong> :
|
||
<code>tag:</code> et
|
||
<code>vault:</code> accélèrent la recherche
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.breadcrumb_clickable">Breadcrumb cliquable</strong> :
|
||
Repositionnez rapidement la sidebar
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.2a92203f">Trucs et astuces</h3>
|
||
<ul>
|
||
<li>
|
||
<strong>Navigation visuelle</strong> : Le
|
||
breadcrumb repositionne automatiquement la
|
||
sidebar
|
||
</li>
|
||
<li>
|
||
<strong>Gestion de l'espace</strong><span data-i18n="help.desc_287be69c"> :
|
||
Réduisez les sections Vaults/Tags selon vos
|
||
besoins</span></li>
|
||
<li>
|
||
<strong>Confort de lecture</strong> :
|
||
Ajustez la largeur de la sidebar
|
||
</li>
|
||
<li>
|
||
<strong>Mobile-friendly</strong> : Utilisez
|
||
le bouton hamburger sur mobile
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="help.wikilinks">Wikilinks</strong> : Naviguez entre
|
||
notes comme dans Obsidian
|
||
</li>
|
||
<li>
|
||
<strong>Images</strong> : Support complet
|
||
des syntaxes Obsidian
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.advanced_use_cases">Cas d'usage avancés</h3>
|
||
<ul>
|
||
<li>
|
||
<strong data-i18n="auto.b86ba5ea">Recherche multi-critères</strong> :
|
||
<code
|
||
>tag:docker tag:linux vault:IT
|
||
nginx</code
|
||
>
|
||
</li>
|
||
<li>
|
||
<strong>Exploration par tags</strong> :
|
||
Cliquez sur un tag dans une note pour voir
|
||
toutes les notes similaires
|
||
</li>
|
||
<li>
|
||
<strong data-i18n="auto.4fa5d7c2">Édition rapide</strong><span data-i18n="help.desc_cb6f0bfd"> : Ouvrez
|
||
l'éditeur, modifiez, sauvegardez</span></li>
|
||
<li>
|
||
<strong data-i18n="help.export">Export</strong><span data-i18n="help.desc_f9083ce5"> : Téléchargez des
|
||
fichiers pour backup ou partage</span></li>
|
||
<li>
|
||
<strong data-i18n="auto.79f6e1dd">Multi-fenêtres</strong><span data-i18n="help.desc_9cb5974c"> : Ouvrez
|
||
plusieurs notes dans des onglets séparés</span></li>
|
||
</ul>
|
||
</section>
|
||
|
||
<!-- 🧩 Plugins -->
|
||
<section class="help-section" id="help-api">
|
||
<h2 data-i18n="guide105.nav_api">🔌 API</h2>
|
||
<p data-i18n="guide105.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 :</p>
|
||
<ul>
|
||
<li data-i18n-html="guide105.api_docs_url"><code>/docs</code> — interface Swagger UI pour essayer les requêtes en direct.</li>
|
||
<li data-i18n-html="guide105.api_redoc"><code>/redoc</code> — référence alternative plus compacte.</li>
|
||
<li data-i18n-html="guide105.api_landing"><code>/api</code> — page de garde regroupant les endpoints par catégorie.</li>
|
||
<li data-i18n-html="guide105.api_schema"><code>/openapi.json</code> — le schéma machine, à importer dans Postman ou Insomnia.</li>
|
||
</ul>
|
||
<h3 data-i18n="guide105.api_h3_auth">Authentification</h3>
|
||
<p data-i18n-html="guide105.api_auth">Connectez-vous via <code>POST /api/auth/login</code> pour obtenir un token Bearer (le même jeton est accepté en cookie HttpOnly, ce qui permet aux clients navigateur d'utiliser <code>credentials: "include"</code>). Toutes les routes <code>/api/*</code> exigent ce jeton sauf mention contraire.</p>
|
||
<h3 data-i18n="guide105.api_h3_mcp">Serveur MCP</h3>
|
||
<p data-i18n-html="guide105.api_mcp">Les outils de l'assistant IA (lire, lister, chercher, ouvrir, écrire…) sont exposés à tout client MCP (Claude Desktop, Cursor, Cline…) sur <code>https://votre-instance/mcp</code> avec un token d'API. Configuration et exemples : <code>docs/MCP_GUIDE.md</code>.</p>
|
||
<h3 data-i18n="guide105.api_h3_autom">Automatisation</h3>
|
||
<p data-i18n-html="guide105.api_autom">Pour automatiser depuis l'extérieur : <code>GET /api/search?q=…</code> et <code>GET /api/file/{vault}?path=…</code> permettent d'indexer ou relire vos notes dans un autre outil ; les webhooks sortants (section 🪝) évitent le polling.</p>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-languages">
|
||
<h2 data-i18n="guide105.nav_languages">🌍 Multilingue</h2>
|
||
<ul>
|
||
<li data-i18n="guide105.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.</li>
|
||
<li data-i18n="guide105.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.</li>
|
||
<li data-i18n="guide105.lng_export">Les boutons Markdown / PDF de ce guide téléchargent la version dans votre langue.</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section class="help-section" id="help-plugins">
|
||
<h2 data-i18n="help.plugins_title">🧩 Plugins</h2>
|
||
<p data-i18n="help.plugins_intro">
|
||
Les plugins étendent ObsiGate : affichage personnalisé des
|
||
fichiers, filtres de recherche, actions d'édition, widgets
|
||
et intégrations. Ils s'exécutent dans des Web Workers
|
||
sandboxés — isolés de la page pour la sécurité.
|
||
</p>
|
||
|
||
<h3 data-i18n="help.plugins_install">Installation</h3>
|
||
<ol>
|
||
<li data-i18n="help.plugins_install_1">Ouvrez Réglages → 🧩 Plugins.</li>
|
||
<li data-i18n="help.plugins_install_2">Cliquez « Installer un plugin ».</li>
|
||
<li data-i18n="help.plugins_install_3">Sélectionnez un fichier <code>.zip</code> contenant le manifest
|
||
<code>plugin.json</code> + le code du plugin.</li>
|
||
<li data-i18n="help.plugins_install_4">Le manifest est validé (nom, version semver, hooks, permissions)
|
||
avant installation.</li>
|
||
</ol>
|
||
|
||
<h3 data-i18n="help.plugins_manage">Gestion</h3>
|
||
<ul>
|
||
<li data-i18n="help.plugins_manage_1"><strong>Activer / Désactiver</strong> : boutons sur chaque plugin — les
|
||
hooks ne tournent que si le plugin est activé.</li>
|
||
<li data-i18n="help.plugins_manage_2"><strong>Désinstaller</strong> : supprime définitivement le plugin du vault.</li>
|
||
<li data-i18n="help.plugins_manage_3"><strong>Code</strong> : visualisez le code source du plugin installé.</li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.plugins_dev">Développer un plugin</h3>
|
||
<p data-i18n="help.plugins_dev_intro">
|
||
Téléchargez le modèle depuis Réglages → Plugins → « Obtenir le
|
||
modèle ». Un plugin se compose d'un manifest
|
||
<code>plugin.json</code> et d'un fichier principal (ex.
|
||
<code>index.js</code>).
|
||
</p>
|
||
|
||
<h3 data-i18n="help.plugins_hooks">Hooks disponibles</h3>
|
||
<ul>
|
||
<li><strong data-i18n="help.plugins_hook_render">onFileRender</strong> :
|
||
<span data-i18n="help.plugins_hook_render_desc">transforme le contenu affiché d'un fichier.</span></li>
|
||
<li><strong data-i18n="help.plugins_hook_search">onSearchFilter</strong> :
|
||
<span data-i18n="help.plugins_hook_search_desc">filtre les résultats de recherche.</span></li>
|
||
<li><strong data-i18n="help.plugins_hook_editor">onEditorAction</strong> :
|
||
<span data-i18n="help.plugins_hook_editor_desc">intercepte les actions de l'éditeur.</span></li>
|
||
<li><strong data-i18n="help.plugins_hook_sidebar">onSidebarItem</strong> :
|
||
<span data-i18n="help.plugins_hook_sidebar_desc">ajoute des éléments à la barre latérale.</span></li>
|
||
<li><strong data-i18n="help.plugins_hook_file">onFileCreate / onFileDelete</strong> :
|
||
<span data-i18n="help.plugins_hook_file_desc">réagit à la création / suppression de fichiers.</span></li>
|
||
<li><strong data-i18n="help.plugins_hook_vault">onVaultMount</strong> :
|
||
<span data-i18n="help.plugins_hook_vault_desc">exécuté au chargement d'un vault.</span></li>
|
||
</ul>
|
||
|
||
<h3 data-i18n="help.plugins_security">Sécurité</h3>
|
||
<ul>
|
||
<li data-i18n="help.plugins_security_1"><strong>Sandbox Web Worker</strong> : le code plugin tourne isolé, sans
|
||
accès direct au DOM.</li>
|
||
<li data-i18n="help.plugins_security_2"><strong>Permissions</strong> : chaque hook déclaré doit être satisfait
|
||
par les permissions du manifest.</li>
|
||
<li data-i18n="help.plugins_security_3"><strong>Empaquetage sécurisé</strong> : le .zip est vérifié contre le
|
||
path traversal à l'installation.</li>
|
||
<li data-i18n="help.plugins_security_4"><strong>Admin uniquement</strong> : l'installation et la suppression
|
||
requièrent le rôle admin.</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<div class="help-footer">
|
||
<p>
|
||
<strong>ObsiGate <span id="help-version"></span></strong>
|
||
<span data-i18n="help.footer_tagline">- Porte d'entrée web pour vos vaults Obsidian</span>
|
||
</p>
|
||
<p>
|
||
Pour plus d'informations, consultez le
|
||
<a
|
||
href="https://git.dracodev.net/Projets/ObsiGate"
|
||
target="_blank"
|
||
>dépôt du projet</a
|
||
>.
|
||
</p>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Find in Page Bar -->
|
||
<div class="find-in-page-bar" id="find-in-page-bar" hidden>
|
||
<div class="find-in-page-content">
|
||
<i data-lucide="search" class="find-icon"></i>
|
||
<input
|
||
type="text"
|
||
id="find-input"
|
||
data-i18n-placeholder="settings.search_placeholder" placeholder="Rechercher..."
|
||
autocomplete="off"
|
||
aria-label="Rechercher dans la page"
|
||
/>
|
||
<span class="find-counter" id="find-counter" aria-live="polite"
|
||
>0 occurrence</span
|
||
>
|
||
<div class="find-nav-buttons">
|
||
<button
|
||
class="find-btn"
|
||
id="find-prev"
|
||
title="Précédent (Shift+Enter)"
|
||
aria-label="Résultat précédent"
|
||
disabled
|
||
>
|
||
<i
|
||
data-lucide="chevron-up"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
</button>
|
||
<button
|
||
class="find-btn"
|
||
id="find-next"
|
||
title="Suivant (Enter)"
|
||
aria-label="Résultat suivant"
|
||
disabled
|
||
>
|
||
<i
|
||
data-lucide="chevron-down"
|
||
style="width: 14px; height: 14px"
|
||
></i>
|
||
</button>
|
||
</div>
|
||
<div class="find-options">
|
||
<button
|
||
class="find-btn find-option-btn"
|
||
id="find-case-sensitive"
|
||
title="Respecter la casse"
|
||
aria-label="Respecter la casse"
|
||
aria-pressed="false"
|
||
>
|
||
<span class="find-option-text">Aa</span>
|
||
</button>
|
||
<button
|
||
class="find-btn find-option-btn"
|
||
id="find-whole-word"
|
||
title="Mot entier"
|
||
aria-label="Mot entier"
|
||
aria-pressed="false"
|
||
>
|
||
<span class="find-option-text">abc</span>
|
||
</button>
|
||
<button
|
||
class="find-btn find-option-btn"
|
||
id="find-regex"
|
||
title="Expression régulière"
|
||
aria-label="Expression régulière"
|
||
aria-pressed="false"
|
||
>
|
||
<span class="find-option-text">.*</span>
|
||
</button>
|
||
</div>
|
||
<button
|
||
class="find-btn find-close"
|
||
id="find-close"
|
||
title="Fermer (Escape)"
|
||
aria-label="Fermer la recherche"
|
||
>
|
||
<i data-lucide="x" style="width: 16px; height: 16px"></i>
|
||
</button>
|
||
</div>
|
||
<div class="find-error" id="find-error" hidden></div>
|
||
</div>
|
||
|
||
<script>
|
||
// One-time migration for devices stuck on the legacy service worker
|
||
// (cache-first with a fixed cache name). Deletes the old caches and
|
||
// triggers an update check; the new network-first SW then takes over.
|
||
// Keyed in localStorage so it never loops or re-runs on every session.
|
||
(function () {
|
||
if (!('serviceWorker' in navigator) || !('caches' in window)) return;
|
||
var MIGRATION_KEY = 'obsigate-sw-migration';
|
||
try {
|
||
if (localStorage.getItem(MIGRATION_KEY) === 'v3') return;
|
||
localStorage.setItem(MIGRATION_KEY, 'v3');
|
||
} catch (e) { return; }
|
||
caches.keys().then(function (names) {
|
||
return Promise.all(names.map(function (name) {
|
||
return /^obsigate-/.test(name) ? caches.delete(name) : null;
|
||
}));
|
||
}).then(function () {
|
||
return navigator.serviceWorker.getRegistration();
|
||
}).then(function (reg) {
|
||
if (reg) reg.update();
|
||
}).catch(function () { /* best effort */ });
|
||
})();
|
||
</script>
|
||
<script type="module" src="/static/js/app.js"></script>
|
||
|
||
<!-- ── Mobile Bottom Toolbar ── -->
|
||
<nav
|
||
class="mobile-toolbar"
|
||
id="mobile-toolbar"
|
||
aria-label="Barre d'outils mobile"
|
||
>
|
||
<button
|
||
class="mt-btn"
|
||
id="mt-explorer"
|
||
title="Explorateur"
|
||
aria-label="Explorateur de vault"
|
||
>
|
||
<i
|
||
data-lucide="folder-open"
|
||
style="width: 20px; height: 20px"
|
||
></i>
|
||
<span class="mt-label" data-i18n="settings.explorer">Explorateur</span>
|
||
</button>
|
||
<button
|
||
class="mt-btn"
|
||
id="mt-search"
|
||
title="Rechercher un fichier"
|
||
aria-label="Rechercher un fichier"
|
||
>
|
||
<i data-lucide="search" style="width: 20px; height: 20px"></i>
|
||
<span class="mt-label" data-i18n="settings.search">Recherche</span>
|
||
</button>
|
||
<button
|
||
class="mt-btn mt-btn-ai"
|
||
id="mt-ai"
|
||
title="Assistant AI"
|
||
aria-label="Ouvrir l'assistant AI"
|
||
>
|
||
<i data-lucide="bot" style="width: 24px; height: 24px"></i>
|
||
</button>
|
||
<button
|
||
class="mt-btn mt-btn-primary"
|
||
id="mt-palette"
|
||
title="Commandes"
|
||
aria-label="Palette de commandes"
|
||
>
|
||
<i data-lucide="terminal" style="width: 20px; height: 20px"></i>
|
||
<span class="mt-label" data-i18n="palette.mode_commands">Commandes</span>
|
||
</button>
|
||
<button
|
||
class="mt-btn"
|
||
id="mt-tabs"
|
||
title="Onglets"
|
||
aria-label="Onglets ouverts"
|
||
>
|
||
<i data-lucide="files" style="width: 20px; height: 20px"></i>
|
||
<span class="mt-badge" id="mt-tab-count" style="display: none"
|
||
>0</span
|
||
>
|
||
<span class="mt-label" data-i18n="settings.tabs">Onglets</span>
|
||
</button>
|
||
</nav>
|
||
|
||
<!-- ── Mobile Tab Switcher (bottom sheet) ── -->
|
||
<div class="mt-tab-switcher" id="mt-tab-switcher">
|
||
<div class="mt-sheet-handle"></div>
|
||
<div class="mt-sheet-header">
|
||
<span class="mt-sheet-title">Fichiers ouverts</span>
|
||
<div class="mt-sheet-actions">
|
||
<button
|
||
class="mt-sheet-close-all"
|
||
id="mt-close-all"
|
||
title="Fermer tout"
|
||
aria-label="Fermer tous les fichiers"
|
||
>
|
||
Fermer tout
|
||
</button>
|
||
<button
|
||
class="mt-sheet-close"
|
||
id="mt-sheet-close"
|
||
aria-label="Fermer"
|
||
>
|
||
✕
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="mt-sheet-list" id="mt-tab-list"></div>
|
||
</div>
|
||
<div class="mt-overlay" id="mt-overlay"></div>
|
||
<!-- Floating results bar (outside content-area, survives innerHTML) -->
|
||
<div class="results-bar" id="results-bar" style="display:none">
|
||
<span class="results-bar-count" id="rb-count">0/0</span>
|
||
<button class="results-bar-btn" id="rb-prev" title="Resultat precedent" aria-label="Precedent">↑</button>
|
||
<button class="results-bar-btn" id="rb-next" title="Resultat suivant" aria-label="Suivant">↓</button>
|
||
<span class="results-bar-sep"></span>
|
||
<button class="results-bar-btn tog" id="rb-case" title="Respecter la casse (Alt-C)">Aa</button>
|
||
<button class="results-bar-btn tog" id="rb-word" title="Mot entier (Alt-W)">wd</button>
|
||
<button class="results-bar-btn tog" id="rb-regex" title="Expression reguliere (Alt-R)">.*</button>
|
||
<button class="results-bar-btn tog" id="rb-semantic" title="Recherche sémantique (Alt-S)">~</button>
|
||
</div>
|
||
<!-- Offline conflict resolution modal -->
|
||
<div class="editor-modal" id="offline-conflict-modal">
|
||
<div class="editor-container" style="max-width:520px">
|
||
<div class="editor-header">
|
||
<span class="editor-brand">Conflit hors-ligne</span>
|
||
<div class="editor-spacer"></div>
|
||
<button class="editor-btn" id="offline-conflict-close" title="Fermer" aria-label="Fermer">✕</button>
|
||
</div>
|
||
<div class="editor-body" style="padding:20px;overflow:auto">
|
||
<p style="color:var(--text-secondary);margin-bottom:12px" id="offline-conflict-msg"></p>
|
||
<div style="display:flex;gap:8px">
|
||
<button class="editor-btn primary" id="offline-conflict-keep-local">Garder ma version</button>
|
||
<button class="editor-btn" id="offline-conflict-keep-server" data-i18n="offline.keep_server">Garder version serveur</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- About Modal -->
|
||
<div class="about-overlay" id="about-overlay">
|
||
<div class="about-modal">
|
||
<button class="about-close" id="about-close" aria-label="Fermer">×</button>
|
||
<div class="about-hero">
|
||
<div class="about-logo">
|
||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none">
|
||
<rect width="56" height="56" rx="14" fill="var(--accent)" opacity="0.15"/>
|
||
<rect x="12" y="8" width="14" height="18" rx="3" fill="var(--accent)" opacity="0.9"/>
|
||
<rect x="29" y="8" width="14" height="18" rx="3" fill="var(--accent)" opacity="0.5"/>
|
||
<rect x="12" y="30" width="14" height="18" rx="3" fill="var(--accent)" opacity="0.7"/>
|
||
<rect x="29" y="30" width="14" height="18" rx="3" fill="var(--accent)" opacity="0.35"/>
|
||
</svg>
|
||
</div>
|
||
<h1 class="about-title" data-i18n="header.logo">ObsiGate</h1>
|
||
<p class="about-tagline">Vos notes Obsidian,<br>simplement accessibles.</p>
|
||
<span class="about-version" id="about-version">—</span>
|
||
</div>
|
||
<div class="about-body">
|
||
<div class="about-stats">
|
||
<div class="about-stat">
|
||
<span class="about-stat-value" id="about-stat-files">—</span>
|
||
<span class="about-stat-label" data-i18n="palette.mode_files">Fichiers</span>
|
||
</div>
|
||
<div class="about-stat">
|
||
<span class="about-stat-value" id="about-stat-vaults">—</span>
|
||
<span class="about-stat-label" data-i18n="sidebar.tab_vaults">Vaults</span>
|
||
</div>
|
||
<div class="about-stat">
|
||
<span class="about-stat-value">285</span>
|
||
<span class="about-stat-label">Tests</span>
|
||
</div>
|
||
</div>
|
||
<div class="about-stack">
|
||
<h3>Construit avec</h3>
|
||
<div class="about-stack-grid">
|
||
<span class="about-stack-badge">🐍 FastAPI</span>
|
||
<span class="about-stack-badge">⚡ vanilla JS</span>
|
||
<span class="about-stack-badge">🎨 CSS custom</span>
|
||
<span class="about-stack-badge">🐳 Docker</span>
|
||
<span class="about-stack-badge">🔍 TF-IDF</span>
|
||
<span class="about-stack-badge">📝 CodeMirror 6</span>
|
||
<span class="about-stack-badge">🧜 Mermaid.js</span>
|
||
<span class="about-stack-badge">🧠 Argon2id</span>
|
||
<span class="about-stack-badge">🔄 Watchdog</span>
|
||
<span class="about-stack-badge">📊 WeasyPrint</span>
|
||
<span class="about-stack-badge">🧪 Playwright</span>
|
||
<span class="about-stack-badge">🤖 AI multi-provider</span>
|
||
</div>
|
||
</div>
|
||
<div class="about-footer">
|
||
<div class="about-meta">
|
||
<span>Derniere livraison : <strong id="about-build-date">Juin 2026</strong></span>
|
||
<span>Git : <strong id="about-commit">—</strong></span>
|
||
</div>
|
||
<p class="about-joke" id="about-joke"></p>
|
||
<p class="about-copy">© 2026 — Fait avec ☕, 🎧 et quelques <code>git push --force</code> accidentels.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html>
|