Mistral AI - Delete All Chats

Adds a native-looking "Delete All Chats" button to Mistral AI interface with multi-language support

目前为 2025-03-23 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Mistral AI - Delete All Chats
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.02
  5. // @description Adds a native-looking "Delete All Chats" button to Mistral AI interface with multi-language support
  6. // @author Ognisty321
  7. // @match https://chat.mistral.ai/*
  8. // @license MIT
  9. // @grant none
  10. // @run-at document-end
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. const translations = {
  17. en: {
  18. deleteAllChats: "Delete All Chats",
  19. confirmDeleteAll: "Are you sure you want to delete ALL chats? This action cannot be undone!",
  20. modalTitle: "Deleting Chats",
  21. modalClose: "Close",
  22. startingDeletion: "Starting deletion process...",
  23. fetchingChats: "Fetching chats...",
  24. foundChats: "Found {0} chats to delete.",
  25. noMoreChats: "No more chats to delete!",
  26. deletionComplete: "✅ Deletion complete! Successfully deleted {0} chats in total.",
  27. startingBatch: "Starting batch #{0}...",
  28. completedBatch: "Completed batch #{0}: Deleted {1} chats",
  29. deletedChat: "Deleted chat: {0} ({1}...)",
  30. failedChat: "Failed to delete chat {0}: {1}",
  31. errorFetchingChats: "Error fetching chats: {0}",
  32. buttonAdded: "\"Delete All Chats\" button added successfully",
  33. confirmButtonLog: "Attempting to add native delete button..."
  34. },
  35. fr: {
  36. deleteAllChats: "Supprimer tous les chats",
  37. confirmDeleteAll: "Êtes-vous sûr de vouloir supprimer TOUS les chats ? Cette action est irréversible !",
  38. modalTitle: "Suppression des chats",
  39. modalClose: "Fermer",
  40. startingDeletion: "Début du processus de suppression...",
  41. fetchingChats: "Récupération des chats...",
  42. foundChats: "{0} chats trouvés à supprimer.",
  43. noMoreChats: "Aucun autre chat à supprimer !",
  44. deletionComplete: "✅ Suppression terminée ! {0} chats supprimés au total.",
  45. startingBatch: "Lancement du lot #{0}...",
  46. completedBatch: "Lot #{0} terminé : {1} chats supprimés",
  47. deletedChat: "Chat supprimé : {0} ({1}...)",
  48. failedChat: "Échec de la suppression du chat {0} : {1}",
  49. errorFetchingChats: "Erreur lors de la récupération des chats : {0}",
  50. buttonAdded: "Bouton « Supprimer tous les chats » ajouté avec succès",
  51. confirmButtonLog: "Tentative d’ajout du bouton natif de suppression..."
  52. },
  53. de: {
  54. deleteAllChats: "Alle Chats löschen",
  55. confirmDeleteAll: "Sind Sie sicher, dass Sie ALLE Chats löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden!",
  56. modalTitle: "Chats löschen",
  57. modalClose: "Schließen",
  58. startingDeletion: "Löschvorgang wird gestartet...",
  59. fetchingChats: "Chats werden geladen...",
  60. foundChats: "{0} Chats zum Löschen gefunden.",
  61. noMoreChats: "Keine weiteren Chats zum Löschen!",
  62. deletionComplete: "✅ Löschvorgang abgeschlossen! Insgesamt wurden {0} Chats erfolgreich gelöscht.",
  63. startingBatch: "Batch #{0} wird gestartet...",
  64. completedBatch: "Batch #{0} abgeschlossen: {1} Chats gelöscht",
  65. deletedChat: "Chat gelöscht: {0} ({1}...)",
  66. failedChat: "Chat {0} konnte nicht gelöscht werden: {1}",
  67. errorFetchingChats: "Fehler beim Laden der Chats: {0}",
  68. buttonAdded: "Button „Alle Chats löschen“ erfolgreich hinzugefügt",
  69. confirmButtonLog: "Versuche, nativen Lösch-Button hinzuzufügen..."
  70. },
  71. es: {
  72. deleteAllChats: "Eliminar todos los chats",
  73. confirmDeleteAll: "¿Seguro que quieres eliminar TODOS los chats? ¡Esta acción no se puede deshacer!",
  74. modalTitle: "Eliminando chats",
  75. modalClose: "Cerrar",
  76. startingDeletion: "Iniciando el proceso de eliminación...",
  77. fetchingChats: "Obteniendo los chats...",
  78. foundChats: "{0} chats encontrados para eliminar.",
  79. noMoreChats: "¡No quedan más chats para eliminar!",
  80. deletionComplete: "✅ ¡Eliminación completada! Se eliminaron correctamente {0} chats en total.",
  81. startingBatch: "Iniciando lote #{0}...",
  82. completedBatch: "Lote #{0} completado: {1} chats eliminados",
  83. deletedChat: "Chat eliminado: {0} ({1}...)",
  84. failedChat: "No se pudo eliminar el chat {0}: {1}",
  85. errorFetchingChats: "Error al obtener los chats: {0}",
  86. buttonAdded: "Botón «Eliminar todos los chats» agregado correctamente",
  87. confirmButtonLog: "Intentando añadir botón nativo de eliminación..."
  88. },
  89. pl: {
  90. deleteAllChats: "Usuń wszystkie czaty",
  91. confirmDeleteAll: "Czy na pewno chcesz usunąć WSZYSTKIE czaty? Tej operacji nie można cofnąć!",
  92. modalTitle: "Usuwanie czatów",
  93. modalClose: "Zamknij",
  94. startingDeletion: "Rozpoczynanie procesu usuwania...",
  95. fetchingChats: "Pobieranie czatów...",
  96. foundChats: "Znaleziono {0} czatów do usunięcia.",
  97. noMoreChats: "Brak kolejnych czatów do usunięcia!",
  98. deletionComplete: "✅ Usuwanie zakończone! Łącznie usunięto {0} czatów.",
  99. startingBatch: "Rozpoczynanie partii nr {0}...",
  100. completedBatch: "Zakończono partię nr {0}: usunięto {1} czatów",
  101. deletedChat: "Usunięto czat: {0} ({1}...)",
  102. failedChat: "Nie udało się usunąć czatu {0}: {1}",
  103. errorFetchingChats: "Błąd podczas pobierania czatów: {0}",
  104. buttonAdded: "Przycisk „Usuń wszystkie czaty” został dodany pomyślnie",
  105. confirmButtonLog: "Próba dodania natywnego przycisku usuwania..."
  106. },
  107. it: {
  108. deleteAllChats: "Elimina tutte le chat",
  109. confirmDeleteAll: "Sei sicuro di voler eliminare TUTTE le chat? Questa azione non può essere annullata!",
  110. modalTitle: "Eliminazione chat",
  111. modalClose: "Chiudi",
  112. startingDeletion: "Avvio del processo di eliminazione...",
  113. fetchingChats: "Recupero delle chat...",
  114. foundChats: "Trovate {0} chat da eliminare.",
  115. noMoreChats: "Non ci sono più chat da eliminare!",
  116. deletionComplete: "✅ Eliminazione completata! {0} chat eliminate con successo.",
  117. startingBatch: "Avvio batch #{0}...",
  118. completedBatch: "Batch #{0} completato: {1} chat eliminate",
  119. deletedChat: "Chat eliminata: {0} ({1}...)",
  120. failedChat: "Impossibile eliminare la chat {0}: {1}",
  121. errorFetchingChats: "Errore nel recupero delle chat: {0}",
  122. buttonAdded: "Pulsante «Elimina tutte le chat» aggiunto con successo",
  123. confirmButtonLog: "Tentativo di aggiungere il pulsante nativo di eliminazione..."
  124. },
  125. pt: {
  126. deleteAllChats: "Excluir todas as conversas",
  127. confirmDeleteAll: "Tem certeza de que deseja excluir TODAS as conversas? Esta ação não pode ser desfeita!",
  128. modalTitle: "Excluindo conversas",
  129. modalClose: "Fechar",
  130. startingDeletion: "Iniciando o processo de exclusão...",
  131. fetchingChats: "Obtendo conversas...",
  132. foundChats: "{0} conversas encontradas para exclusão.",
  133. noMoreChats: "Não há mais conversas para excluir!",
  134. deletionComplete: "✅ Exclusão concluída! {0} conversas excluídas com sucesso.",
  135. startingBatch: "Iniciando lote #{0}...",
  136. completedBatch: "Lote #{0} concluído: {1} conversas excluídas",
  137. deletedChat: "Conversa excluída: {0} ({1}...)",
  138. failedChat: "Falha ao excluir a conversa {0}: {1}",
  139. errorFetchingChats: "Erro ao obter conversas: {0}",
  140. buttonAdded: "Botão «Excluir todas as conversas» adicionado com sucesso",
  141. confirmButtonLog: "Tentando adicionar botão nativo de exclusão..."
  142. },
  143. ar: {
  144. deleteAllChats: "حذف كل المحادثات",
  145. confirmDeleteAll: "هل أنت متأكد أنك تريد حذف كل المحادثات؟ لا يمكن التراجع عن هذا الإجراء!",
  146. modalTitle: "جارٍ حذف المحادثات",
  147. modalClose: "إغلاق",
  148. startingDeletion: "بدء عملية الحذف...",
  149. fetchingChats: "جارٍ جلب المحادثات...",
  150. foundChats: "تم العثور على {0} محادثة للحذف.",
  151. noMoreChats: "لا توجد محادثات أخرى للحذف!",
  152. deletionComplete: "✅ اكتملت عملية الحذف! تم حذف {0} محادثة بنجاح.",
  153. startingBatch: "بدء الدفعة رقم {0}...",
  154. completedBatch: "اكتملت الدفعة رقم {0}: تم حذف {1} محادثة",
  155. deletedChat: "تم حذف المحادثة: {0} ({1}...)",
  156. failedChat: "فشل حذف المحادثة {0}: {1}",
  157. errorFetchingChats: "حدث خطأ أثناء جلب المحادثات: {0}",
  158. buttonAdded: "تمت إضافة زر «حذف كل المحادثات» بنجاح",
  159. confirmButtonLog: "جارٍ محاولة إضافة زر الحذف الأصلي..."
  160. }
  161. };
  162.  
  163. function formatString(template, ...args) {
  164. return template.replace(/\{(\d+)\}/g, (match, index) => {
  165. return typeof args[index] !== 'undefined' ? args[index] : match;
  166. });
  167. }
  168.  
  169. const userLang = (navigator.language || navigator.userLanguage || 'en').slice(0, 2);
  170. const i18n = translations[userLang] || translations.en;
  171.  
  172. function addNativeDeleteButton() {
  173. console.log(i18n.confirmButtonLog);
  174. const sidebarMenu = document.querySelector('ul[data-sidebar="menu"]');
  175. if (!sidebarMenu) {
  176. console.log('Sidebar menu not found, retrying in 1 second...');
  177. setTimeout(addNativeDeleteButton, 1000);
  178. return;
  179. }
  180. if (document.getElementById('delete-all-chats-button')) {
  181. console.log('Delete button already exists');
  182. return;
  183. }
  184. const menuItem = document.createElement('li');
  185. menuItem.setAttribute('data-sidebar', 'menu-item');
  186. menuItem.className = 'group/menu-item relative';
  187. const button = document.createElement('button');
  188. button.id = 'delete-all-chats-button';
  189. button.setAttribute('data-sidebar', 'menu-button');
  190. button.setAttribute('data-size', 'default');
  191. button.setAttribute('data-active', 'false');
  192. button.className = 'peer/menu-button ring-default active:bg-muted active:text-default data-[active=true]:bg-muted data-[active=true]:text-default data-[state=open]:hover:bg-muted data-[state=open]:hover:text-default outline-hidden group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left transition-colors focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:font-medium [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 hover:bg-red-100 hover:text-red-700 h-8 text-sm text-red-600';
  193. button.type = 'button';
  194. button.innerHTML = `
  195. <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  196. <path d="M3 6h18"></path>
  197. <path d="M8 6V4c0-1.1.9-2 2-2h4c1.1 0 2 .9 2 2v2"></path>
  198. <path d="M19 6l-1 14c-.1 1-1 2-2 2H8c-1 0-1.9-1-2-2L5 6"></path>
  199. <line x1="10" y1="11" x2="10" y2="17"></line>
  200. <line x1="14" y1="11" x2="14" y2="17"></line>
  201. </svg>
  202. <span>${i18n.deleteAllChats}</span>
  203. `;
  204. button.addEventListener('click', () => {
  205. confirmAndDeleteAllChats();
  206. });
  207. menuItem.appendChild(button);
  208. sidebarMenu.appendChild(menuItem);
  209. console.log(i18n.buttonAdded);
  210. createStatusModal();
  211. }
  212.  
  213. function createStatusModal() {
  214. if (document.getElementById('delete-status-modal')) {
  215. return;
  216. }
  217. const modal = document.createElement('div');
  218. modal.id = 'delete-status-modal';
  219. modal.style.position = 'fixed';
  220. modal.style.top = '0';
  221. modal.style.left = '0';
  222. modal.style.right = '0';
  223. modal.style.bottom = '0';
  224. modal.style.backgroundColor = 'rgba(0, 0, 0, 0.75)';
  225. modal.style.zIndex = '9999';
  226. modal.style.display = 'none';
  227. modal.style.overflow = 'auto';
  228. modal.style.alignItems = 'flex-start';
  229. modal.style.justifyContent = 'center';
  230. modal.style.paddingTop = '50px';
  231. modal.style.paddingBottom = '50px';
  232. const modalContent = document.createElement('div');
  233. modalContent.className = 'relative w-full max-w-md rounded-lg bg-gray-900 shadow-lg text-gray-100';
  234. modalContent.style.margin = '0 auto';
  235. modalContent.style.boxShadow = '0 10px 25px -5px rgba(0, 0, 0, 0.3)';
  236. modalContent.style.display = 'flex';
  237. modalContent.style.flexDirection = 'column';
  238. modalContent.style.maxHeight = '80vh';
  239. const modalHeader = document.createElement('div');
  240. modalHeader.className = 'flex items-center justify-between border-b border-gray-700 pb-3 px-4 pt-4';
  241. modalHeader.style.position = 'sticky';
  242. modalHeader.style.top = '0';
  243. modalHeader.style.backgroundColor = 'rgb(17,24,39)';
  244. modalHeader.style.zIndex = '1';
  245. modalHeader.style.borderTopLeftRadius = '0.5rem';
  246. modalHeader.style.borderTopRightRadius = '0.5rem';
  247. modalHeader.innerHTML = `
  248. <div class="flex items-center">
  249. <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 8px;">
  250. <circle cx="12" cy="12" r="10"></circle>
  251. <path d="M15 9l-6 6"></path>
  252. <path d="M9 9l6 6"></path>
  253. </svg>
  254. <h3 class="text-lg font-semibold">${i18n.modalTitle}</h3>
  255. </div>
  256. <button id="close-status-modal" class="rounded-full p-1.5 hover:bg-gray-100 transition-colors">
  257. <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  258. <path d="M18 6 6 18"></path>
  259. <path d="m6 6 12 12"></path>
  260. </svg>
  261. </button>
  262. `;
  263. const statusContainer = document.createElement('div');
  264. statusContainer.id = 'delete-status';
  265. statusContainer.className = 'overflow-y-auto px-4 py-3 space-y-2.5';
  266. statusContainer.style.flex = '1';
  267. statusContainer.style.overflowY = 'auto';
  268. statusContainer.style.minHeight = '100px';
  269. statusContainer.style.maxHeight = 'calc(80vh - 120px)';
  270. const modalFooter = document.createElement('div');
  271. modalFooter.className = 'border-t border-gray-700 px-4 py-3';
  272. modalFooter.style.position = 'sticky';
  273. modalFooter.style.bottom = '0';
  274. modalFooter.style.backgroundColor = 'rgb(17,24,39)';
  275. modalFooter.style.borderBottomLeftRadius = '0.5rem';
  276. modalFooter.style.borderBottomRightRadius = '0.5rem';
  277. const closeButton = document.createElement('button');
  278. closeButton.id = 'close-status-button';
  279. closeButton.className = 'px-4 py-2 bg-gray-800 hover:bg-gray-700 rounded-md text-sm font-medium transition-colors text-gray-100';
  280. closeButton.textContent = i18n.modalClose;
  281. modalFooter.appendChild(closeButton);
  282. modalContent.appendChild(modalHeader);
  283. modalContent.appendChild(statusContainer);
  284. modalContent.appendChild(modalFooter);
  285. modal.appendChild(modalContent);
  286. document.body.appendChild(modal);
  287. document.getElementById('close-status-modal').addEventListener('click', hideModal);
  288. document.getElementById('close-status-button').addEventListener('click', hideModal);
  289. modal.addEventListener('click', (e) => {
  290. if (e.target === modal) {
  291. hideModal();
  292. }
  293. });
  294. function hideModal() {
  295. document.getElementById('delete-status-modal').style.display = 'none';
  296. document.body.style.overflow = '';
  297. }
  298. }
  299.  
  300. function addStatus(message, type = 'info') {
  301. const statusContainer = document.getElementById('delete-status');
  302. if (!statusContainer) return;
  303. const statusItem = document.createElement('div');
  304. statusItem.className = `mb-2 p-3 rounded-md text-sm border`;
  305. if (type === 'success') {
  306. statusItem.innerHTML = `<div class="flex items-center">
  307. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2">
  308. <path d="M20 6L9 17l-5-5"></path>
  309. </svg>
  310. ${message}
  311. </div>`;
  312. } else if (type === 'error') {
  313. statusItem.innerHTML = `<div class="flex items-center">
  314. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2">
  315. <circle cx="12" cy="12" r="10"></circle>
  316. <line x1="12" y1="8" x2="12" y2="12"></line>
  317. <line x1="12" y1="16" x2="12.01" y2="16"></line>
  318. </svg>
  319. ${message}
  320. </div>`;
  321. } else {
  322. statusItem.innerHTML = `<div class="flex items-center">
  323. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#3b82f6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2">
  324. <circle cx="12" cy="12" r="10"></circle>
  325. <line x1="12" y1="16" x2="12" y2="12"></line>
  326. <line x1="12" y1="8" x2="12.01" y2="8"></line>
  327. </svg>
  328. ${message}
  329. </div>`;
  330. }
  331. statusContainer.appendChild(statusItem);
  332. statusContainer.scrollTop = statusContainer.scrollHeight;
  333. }
  334.  
  335. function showStatusModal() {
  336. const modal = document.getElementById('delete-status-modal');
  337. if (modal) {
  338. modal.style.display = 'flex';
  339. document.body.style.overflow = 'hidden';
  340. }
  341. }
  342.  
  343. function confirmAndDeleteAllChats() {
  344. if (confirm(i18n.confirmDeleteAll)) {
  345. const statusContainer = document.getElementById('delete-status');
  346. if (statusContainer) {
  347. statusContainer.innerHTML = '';
  348. }
  349. showStatusModal();
  350. addStatus(i18n.startingDeletion, 'info');
  351. deleteAllChats();
  352. }
  353. }
  354.  
  355. async function fetchChats() {
  356. try {
  357. addStatus(i18n.fetchingChats, 'info');
  358. const url = "https://chat.mistral.ai/api/trpc/chat.last";
  359. const params = {
  360. "batch": "1",
  361. "input": JSON.stringify({
  362. "0": {
  363. "json": {
  364. "chatVisibility": "private",
  365. "chatPermission": "write",
  366. "direction": "forward",
  367. "limit": 20
  368. }
  369. }
  370. })
  371. };
  372. const response = await fetch(`${url}?${new URLSearchParams(params)}`, {
  373. method: 'GET',
  374. headers: {
  375. 'accept': '*/*',
  376. 'content-type': 'application/json',
  377. 'trpc-accept': 'application/jsonl',
  378. 'x-trpc-source': 'nextjs-react'
  379. },
  380. credentials: 'include'
  381. });
  382. if (!response.ok) {
  383. throw new Error(`Failed to fetch chats: ${response.status}`);
  384. }
  385. const text = await response.text();
  386. const chatIds = [];
  387. const lines = text.trim().split('\n');
  388. for (const line of lines) {
  389. try {
  390. const data = JSON.parse(line);
  391. if (data.json && Array.isArray(data.json)) {
  392. if (data.json[2]?.[0]?.[0]?.items) {
  393. for (const chat of data.json[2][0][0].items) {
  394. if (chat.id) {
  395. chatIds.push({
  396. id: chat.id,
  397. title: chat.title || 'No title'
  398. });
  399. }
  400. }
  401. }
  402. }
  403. } catch (e) {
  404. console.error('Error parsing JSON line:', e);
  405. }
  406. }
  407. addStatus(formatString(i18n.foundChats, chatIds.length), 'info');
  408. return chatIds;
  409. } catch (error) {
  410. addStatus(formatString(i18n.errorFetchingChats, error.message), 'error');
  411. console.error('Error fetching chats:', error);
  412. return [];
  413. }
  414. }
  415.  
  416. async function deleteChat(chatId, title) {
  417. try {
  418. const url = "https://chat.mistral.ai/api/trpc/chat.delete";
  419. const params = {"batch": "1"};
  420. const payload = {"0": {"json": {"id": chatId}}};
  421. const response = await fetch(`${url}?${new URLSearchParams(params)}`, {
  422. method: 'POST',
  423. headers: {
  424. 'accept': '*/*',
  425. 'content-type': 'application/json',
  426. 'trpc-accept': 'application/jsonl',
  427. 'x-trpc-source': 'nextjs-react'
  428. },
  429. credentials: 'include',
  430. body: JSON.stringify(payload)
  431. });
  432. if (!response.ok) {
  433. throw new Error(`Failed to delete chat: ${response.status}`);
  434. }
  435. addStatus(formatString(i18n.deletedChat, title, chatId.substring(0, 8)), 'success');
  436. return true;
  437. } catch (error) {
  438. addStatus(formatString(i18n.failedChat, chatId, error.message), 'error');
  439. console.error('Error deleting chat:', error);
  440. return false;
  441. }
  442. }
  443.  
  444. async function deleteAllChats() {
  445. let batchNumber = 1;
  446. let totalDeleted = 0;
  447. while (true) {
  448. addStatus(formatString(i18n.startingBatch, batchNumber), 'info');
  449. const chats = await fetchChats();
  450. if (chats.length === 0) {
  451. addStatus(i18n.noMoreChats, 'success');
  452. break;
  453. }
  454. let batchDeleted = 0;
  455. for (const chat of chats) {
  456. await deleteChat(chat.id, chat.title);
  457. batchDeleted++;
  458. totalDeleted++;
  459. await new Promise(resolve => setTimeout(resolve, 500));
  460. }
  461. addStatus(formatString(i18n.completedBatch, batchNumber, batchDeleted), 'info');
  462. batchNumber++;
  463. await new Promise(resolve => setTimeout(resolve, 2000));
  464. }
  465. addStatus(formatString(i18n.deletionComplete, totalDeleted), 'success');
  466. }
  467.  
  468. setTimeout(addNativeDeleteButton, 2000);
  469. let lastUrl = location.href;
  470. const observer = new MutationObserver(() => {
  471. if (lastUrl !== location.href) {
  472. lastUrl = location.href;
  473. setTimeout(addNativeDeleteButton, 2000);
  474. }
  475. if (!document.getElementById('delete-all-chats-button')) {
  476. addNativeDeleteButton();
  477. }
  478. });
  479. observer.observe(document, {subtree: true, childList: true});
  480. setTimeout(addNativeDeleteButton, 5000);
  481. console.log('Mistral AI - Delete All Chats script (multi-language) loaded!');
  482. })();