- **A — Suivi du document** : l'en-tête du panneau chat affiche le document ciblé (titre + chemin) et `followFileChat()` appelé par `renderFile()` re-cible le panneau ouvert à chaque changement de document. - **B — Chat général** : onglet « Chat » (dernier) dans la sidebar, conversation centrale stockée dans le store #169 via les sentinelles `__global__/general` (`GET/POST /api/chat`) ; pastille de messages non lus sur l'onglet ; pièces jointes image/vidéo (`POST /api/chat/upload` : allow-list d'extensions, 25 MB, nom UUID ; `GET /api/chat/attachment/{name}` résolu contre l'allow-list) ; URL cliquables au rendu ; date/heure d'envoi. - **C — Ordre des onglets** : la barre de filtre passe sous les onglets et sert de recherche dans le chat (texte + auteur) quand l'onglet Chat est actif (`switchSidebarTab` + `routeFilter`). - Transport : broadcast SSE `chat_message` réutilisé (`vault __global__` route vers la sidebar, sinon le panneau fichier) — pas de 2ᵉ WebSocket. - i18n FR/EN (6 clés), CSS bloc #190, `initSidebarChat()` dans l'orchestrateur. - Tests : pytest 27 chat (12 nouveaux) ; JSDOM `filechat.test.mjs` 11 (5 nouveaux) ; suite 1655 passed, ruff/mypy 0, validate-imports 42 modules. - Docs : CHANGELOG [Unreleased], ROADMAP #190 → index, fiche `file-chat-169.md` §#190, guide « Chat général », journal des interventions.
142 lines
4.9 KiB
JavaScript
142 lines
4.9 KiB
JavaScript
/* ObsiGate — Module orchestrator (ES modules).
|
|
Replaces the monolithic frontend/app.js IIFE.
|
|
All code lives in frontend/js/*.js modules. */
|
|
|
|
import * as UI from './ui.js';
|
|
import * as Utils from './utils.js';
|
|
import { initI18n, t } from './i18n.js';
|
|
import { initAIFab } from './ai-fab.js';
|
|
import { initNowPlaying } from './now-playing.js';
|
|
|
|
// Wire up AI toolbar toast (avoids circular import in utils.js)
|
|
window._obsigateShowToast = UI.showToast;
|
|
import * as Sidebar from './sidebar.js';
|
|
import * as Auth from './auth.js';
|
|
import * as Viewer from './viewer.js';
|
|
import * as Sync from './sync.js';
|
|
import { initGraphView } from './graph.js';
|
|
import * as Legacy from './legacy.js';
|
|
import { initCommandPalette } from './palette.js';
|
|
import { initMobileToolbar } from './mobile-toolbar.js';
|
|
import { initMobileEditor } from './mobile-editor.js';
|
|
import { initDragDrop } from './dragdrop.js';
|
|
import { initMermaid, setupFocusMode } from './mermaid-viewer.js';
|
|
import PaneManager from './pane-manager.js';
|
|
import { initDesktopIntegration, isTauriEnv } from './desktop.js';
|
|
import { initPush } from './push.js';
|
|
import { initPlugins } from './plugins.js';
|
|
import { populateVersions } from './config.js';
|
|
import { initSidebarChat } from './filechat.js';
|
|
|
|
// =========================================================================
|
|
// Initialization — mirrors the original app.js init() ordering
|
|
// =========================================================================
|
|
async function init() {
|
|
// 1. Init i18n FIRST — loads fallback (FR) + selected locale
|
|
// Server language check may fail (no auth yet), falls to localStorage
|
|
// LocalStorage has correct language if user set it previously
|
|
await initI18n();
|
|
|
|
// 1b. Desktop bridge — crash banner + OS theme sync (before theme engine).
|
|
// No-op on web (Docker); on desktop it also readies the vault picker.
|
|
await initDesktopIntegration();
|
|
|
|
// 2. Init auth — now t() works inside showApp()/renderUserMenu()
|
|
const authOk = await Auth.AuthManager.initAuth();
|
|
|
|
UI.initTheme();
|
|
UI.initHeaderMenu();
|
|
UI.initCustomDropdowns();
|
|
|
|
document.getElementById("theme-toggle").addEventListener("click", UI.toggleTheme);
|
|
document.getElementById("header-logo").addEventListener("click", Legacy.goHome);
|
|
const refreshBtn = document.getElementById("header-refresh-btn");
|
|
if (refreshBtn) refreshBtn.addEventListener("click", Legacy.goHome);
|
|
|
|
Legacy.initSearch();
|
|
UI.initSidebarToggle();
|
|
UI.initMobile();
|
|
Sidebar.initVaultContext();
|
|
Legacy.initSidebarTabs();
|
|
Legacy.initHelpModal();
|
|
Legacy.initConfigModal();
|
|
Legacy.initAboutModal();
|
|
Sidebar.initSidebarFilter();
|
|
initSidebarChat(); // #190 B — formulaire du chat général (sidebar)
|
|
UI.initSidebarResize();
|
|
Utils.initEditor();
|
|
Auth.initLoginForm();
|
|
Legacy.initRecentTab();
|
|
|
|
UI.RightSidebarManager.init();
|
|
UI.FindInPageManager.init();
|
|
UI.ContextMenuManager.init();
|
|
UI.TabManager.init();
|
|
PaneManager.init();
|
|
initCommandPalette();
|
|
initMobileToolbar();
|
|
initMobileEditor();
|
|
initPush();
|
|
initPlugins();
|
|
|
|
if (authOk) {
|
|
Legacy.initSyncStatus();
|
|
|
|
try {
|
|
await Promise.all([Legacy.loadVaultSettings(), Sidebar.loadVaults(), Sidebar.loadTags()]);
|
|
|
|
initDragDrop();
|
|
|
|
Legacy.DashboardRecentWidget.init();
|
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
if (urlParams.get("popup") === "true") {
|
|
document.body.classList.add("popup-mode");
|
|
}
|
|
|
|
if (window.location.hash && window.location.hash.startsWith("#file=")) {
|
|
const hashVal = window.location.hash.substring(6);
|
|
const sepIndex = hashVal.indexOf(":");
|
|
if (sepIndex > -1) {
|
|
const vault = decodeURIComponent(hashVal.substring(0, sepIndex));
|
|
const path = decodeURIComponent(hashVal.substring(sepIndex + 1));
|
|
Viewer.openFile(vault, path);
|
|
}
|
|
} else if (urlParams.get("popup") !== "true") {
|
|
Legacy.showWelcome();
|
|
}
|
|
} catch (err) {
|
|
console.error("Failed to initialize ObsiGate:", err);
|
|
UI.showToast(t("error.initialization"), "error");
|
|
}
|
|
}
|
|
|
|
initGraphView();
|
|
initMermaid();
|
|
setupFocusMode();
|
|
Utils.safeCreateIcons();
|
|
initAIFab();
|
|
initNowPlaying();
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", async () => {
|
|
try {
|
|
await init();
|
|
Sync.init();
|
|
} finally {
|
|
// Signale au splash de démarrage (index.html #boot-splash) que l'app est
|
|
// initialisée : le splash est retiré et le squelette stylé révélé.
|
|
// Set AFTER init() resolves so every handler (search, tabs, …) is bound.
|
|
window.__OBSIGATE_BOOTED = true;
|
|
if (typeof window.__obsigateBootReady === "function") window.__obsigateBootReady();
|
|
}
|
|
// Show version in header — always visible, fallback before the async fetch
|
|
populateVersions();
|
|
fetch('/api/health')
|
|
.then(r => r.json())
|
|
.then(d => {
|
|
if (d && d.version) populateVersions(d.version);
|
|
})
|
|
.catch(() => {});
|
|
});
|