Télécharge marked.js, Chart.js et la police choisie au moment de l'export et les écrit dans le fichier. Le fichier livré ne dépend plus d'aucun CDN : une panne jsDelivr ne coupe plus le rendu Markdown ni les graphiques chez le client. Nécessite une connexion pendant l'export, et alourdit le fichier.
Google fournit un fichier par jeu de caractères. Tout embarquer en ajoute six à dix ; une interface francophone n'en utilise que deux.
En mode Persistant, la conversation reste indéfiniment sur l'appareil : sur un poste partagé, le visiteur suivant la retrouve telle quelle. Session convient à une interface publique, Aucun aux bornes et aux données sensibles. Le mode ne s'applique qu'au fichier exporté.
Génère un fichier HTML standalone avec votre config baked-in.
Supprime la conversation et l'historique en localStorage.
Cette action supprime tout l'historique du chat.
` littéral présent dans un texte de config ne détourne plus l'insertion. */ const injectBefore = (html, tag, snippet) => { const i = tag === '' ? html.lastIndexOf(tag) : html.indexOf(tag); return i === -1 ? html + snippet : html.slice(0, i) + snippet + html.slice(i); }; const selfExport = async () => { /* `resetExportClone` ferme déjà le modal dans le clone : plus besoin de fermer l'UI avant de sérialiser. On la ferme à la fin, une fois le fichier réellement produit. */ const exportCfg = JSON.parse(JSON.stringify(cfg)); delete exportCfg.webhook.vault; /* Un mot de passe défini scelle le webhook : c'est la seule option qui rend l'URL réellement irrécupérable dans le fichier livré. Le mot de passe n'est alors plus écrit dans la config exportée. */ const password = exportCfg.security.addPassword && exportCfg.security.passwordValue; if (password && cryptoReady()) { exportCfg.webhook.vault = await sealVault(password, { url: exportCfg.webhook.url || '', token: exportCfg.webhook.token || '', voiceAgentUrl: exportCfg.webhook.voiceAgentUrl || '' }); exportCfg.webhook.url = ''; exportCfg.webhook.token = ''; exportCfg.webhook.voiceAgentUrl = ''; exportCfg.security.passwordValue = ''; } else { /* Sans mot de passe, `btoa` reste de l'obfuscation, pas de la sécurité. On la conserve telle quelle, sans la déguiser. */ if (exportCfg.security.maskUrl && exportCfg.webhook.url) { exportCfg.webhook.url = btoa(exportCfg.webhook.url); } if (exportCfg.security.maskToken && exportCfg.webhook.token) { exportCfg.webhook.token = btoa(exportCfg.webhook.token); } } const clone = pruneDisabledFeatures( resetExportClone(document.documentElement.cloneNode(true))); let embedFailures = []; if (cfg.delivery.embedAssets) { setExportNote('⏳ Téléchargement des dépendances à embarquer…'); embedFailures = await embedAssets(clone); } let html = serializeExport(clone); /* replace PLAYER_CONFIG block using sentinel comments */ const configStr = JSON.stringify(exportCfg, null, 6); html = html.replace( /\/\* CONFIG_START \*\/[\s\S]*?\/\* CONFIG_END \*\//, `/* CONFIG_START */\n window.IS_EXPORTED = true;\n window.PLAYER_CONFIG = ${configStr};\n /* CONFIG_END */` ); if (exportCfg.security.removeConsole) { html = injectBefore(html, '', `