diff --git a/package.json b/package.json
index 70ed89485c..a824984845 100644
--- a/package.json
+++ b/package.json
@@ -179,7 +179,6 @@
"dexie-react-hooks": "^1.1.7",
"diff": "^7.0.0",
"docx": "^9.0.2",
- "dompurify": "^3.2.6",
"dotenv-cli": "^7.4.2",
"electron": "37.2.3",
"electron-builder": "26.0.15",
diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json
index 86717e01b5..eb83ad5e8f 100644
--- a/src/renderer/src/i18n/locales/en-us.json
+++ b/src/renderer/src/i18n/locales/en-us.json
@@ -840,6 +840,9 @@
"created": "Created",
"last_updated": "Last Updated",
"messages": "Messages",
+ "notion": {
+ "reasoning_truncated": "Chain of thought cannot be chunked and has been truncated."
+ },
"user": "User"
},
"files": {
@@ -1257,7 +1260,8 @@
},
"notion": {
"export": "Failed to export to Notion. Please check connection status and configuration according to documentation",
- "no_api_key": "Notion ApiKey or Notion DatabaseID is not configured"
+ "no_api_key": "Notion ApiKey or Notion DatabaseID is not configured",
+ "no_content": "There is nothing to export to Notion."
},
"siyuan": {
"export": "Failed to export to Siyuan Note, please check connection status and configuration according to documentation",
@@ -1383,14 +1387,8 @@
}
},
"warn": {
- "notion": {
- "exporting": "Exporting to Notion, please do not request export repeatedly!"
- },
- "siyuan": {
- "exporting": "Exporting to Siyuan Note, please do not request export repeatedly!"
- },
- "yuque": {
- "exporting": "Exporting to Yuque, please do not request export repeatedly!"
+ "export": {
+ "exporting": "Another export is in progress. Please wait for the previous export to complete and then try again."
}
},
"warning": {
diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json
index b41b10c4c7..fbee0004dd 100644
--- a/src/renderer/src/i18n/locales/ja-jp.json
+++ b/src/renderer/src/i18n/locales/ja-jp.json
@@ -840,6 +840,9 @@
"created": "作成日",
"last_updated": "最終更新日",
"messages": "メッセージ",
+ "notion": {
+ "reasoning_truncated": "思考過程がブロック分割できません。切り捨てられています。"
+ },
"user": "ユーザー"
},
"files": {
@@ -1257,7 +1260,8 @@
},
"notion": {
"export": "Notionへのエクスポートに失敗しました。接続状態と設定を確認してください",
- "no_api_key": "Notion ApiKey または Notion DatabaseID が設定されていません"
+ "no_api_key": "Notion ApiKey または Notion DatabaseID が設定されていません",
+ "no_content": "Notionにエクスポートできる内容がありません。"
},
"siyuan": {
"export": "思源ノートのエクスポートに失敗しました。接続状態を確認し、ドキュメントに従って設定を確認してください",
@@ -1383,14 +1387,8 @@
}
},
"warn": {
- "notion": {
- "exporting": "Notionにエクスポート中です。重複してエクスポートしないでください! "
- },
- "siyuan": {
- "exporting": "思源ノートにエクスポート中です。重複してエクスポートしないでください!"
- },
- "yuque": {
- "exporting": "語雀にエクスポート中です。重複してエクスポートしないでください!"
+ "export": {
+ "exporting": "他のエクスポートが実行中です。前のエクスポートが完了するまでお待ちください。"
}
},
"warning": {
diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json
index e689aecebf..e9705991ff 100644
--- a/src/renderer/src/i18n/locales/ru-ru.json
+++ b/src/renderer/src/i18n/locales/ru-ru.json
@@ -840,6 +840,9 @@
"created": "Создано",
"last_updated": "Последнее обновление",
"messages": "Сообщения",
+ "notion": {
+ "reasoning_truncated": "Цепочка мыслей не может быть разбита на блоки, обрезана"
+ },
"user": "Пользователь"
},
"files": {
@@ -1257,7 +1260,8 @@
},
"notion": {
"export": "Ошибка экспорта в Notion, пожалуйста, проверьте состояние подключения и настройки в документации",
- "no_api_key": "Notion ApiKey или Notion DatabaseID не настроен"
+ "no_api_key": "Notion ApiKey или Notion DatabaseID не настроен",
+ "no_content": "Нет содержимого для экспорта в Notion"
},
"siyuan": {
"export": "Ошибка экспорта в Siyuan, пожалуйста, проверьте состояние подключения и настройки в документации",
@@ -1383,14 +1387,8 @@
}
},
"warn": {
- "notion": {
- "exporting": "Экспортируется в Notion, пожалуйста, не отправляйте повторные запросы!"
- },
- "siyuan": {
- "exporting": "Экспортируется в Siyuan, пожалуйста, не отправляйте повторные запросы!"
- },
- "yuque": {
- "exporting": "Экспортируется в Yuque, пожалуйста, не отправляйте повторные запросы!"
+ "export": {
+ "exporting": "Выполняется другая экспортация, подождите завершения предыдущей операции экспорта и повторите попытку"
}
},
"warning": {
diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json
index 9c0ee36467..ae65e9360a 100644
--- a/src/renderer/src/i18n/locales/zh-cn.json
+++ b/src/renderer/src/i18n/locales/zh-cn.json
@@ -840,6 +840,9 @@
"created": "创建时间",
"last_updated": "最后更新",
"messages": "消息数",
+ "notion": {
+ "reasoning_truncated": "思维链无法分块,已截断"
+ },
"user": "用户"
},
"files": {
@@ -1257,7 +1260,8 @@
},
"notion": {
"export": "导出 Notion 错误,请检查连接状态并对照文档检查配置",
- "no_api_key": "未配置 Notion API Key 或 Notion Database ID"
+ "no_api_key": "未配置 Notion API Key 或 Notion Database ID",
+ "no_content": "无可导出到 Notion 的内容"
},
"siyuan": {
"export": "导出思源笔记失败,请检查连接状态并对照文档检查配置",
@@ -1383,14 +1387,8 @@
}
},
"warn": {
- "notion": {
- "exporting": "正在导出到 Notion, 请勿重复请求导出!"
- },
- "siyuan": {
- "exporting": "正在导出到思源笔记,请勿重复请求导出!"
- },
- "yuque": {
- "exporting": "正在导出语雀,请勿重复请求导出!"
+ "export": {
+ "exporting": "正在进行其他导出,请等待上一导出完成后重试"
}
},
"warning": {
diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json
index 8a32912fd9..442f957bf1 100644
--- a/src/renderer/src/i18n/locales/zh-tw.json
+++ b/src/renderer/src/i18n/locales/zh-tw.json
@@ -840,6 +840,9 @@
"created": "建立時間",
"last_updated": "最後更新",
"messages": "訊息數",
+ "notion": {
+ "reasoning_truncated": "思維鏈無法分塊,已截斷"
+ },
"user": "使用者"
},
"files": {
@@ -1257,7 +1260,8 @@
},
"notion": {
"export": "匯出 Notion 錯誤,請檢查連接狀態並對照文件檢查設定",
- "no_api_key": "未設定 Notion API Key 或 Notion Database ID"
+ "no_api_key": "未設定 Notion API Key 或 Notion Database ID",
+ "no_content": "沒有可匯出至 Notion 的內容"
},
"siyuan": {
"export": "導出思源筆記失敗,請檢查連接狀態並對照文檔檢查配置",
@@ -1383,14 +1387,8 @@
}
},
"warn": {
- "notion": {
- "exporting": "正在匯出到 Notion,請勿重複請求匯出!"
- },
- "siyuan": {
- "exporting": "正在導出到思源筆記,請勿重複請求導出!"
- },
- "yuque": {
- "exporting": "正在導出語雀,請勿重複請求導出!"
+ "export": {
+ "exporting": "正在進行其他匯出,請等待上一次匯出完成後再試"
}
},
"warning": {
diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json
index d3e74849f9..a35bed49d3 100644
--- a/src/renderer/src/i18n/translate/el-gr.json
+++ b/src/renderer/src/i18n/translate/el-gr.json
@@ -648,6 +648,31 @@
},
"translate": "Μετάφραση"
},
+ "code": {
+ "auto_update_to_latest": "Έλεγχος για ενημερώσεις και εγκατάσταση της τελευταίας έκδοσης",
+ "bun_required_message": "Για τη λειτουργία του εργαλείου CLI πρέπει να εγκαταστήσετε το περιβάλλον Bun",
+ "cli_tool": "Εργαλείο CLI",
+ "cli_tool_placeholder": "Επιλέξτε το CLI εργαλείο που θέλετε να χρησιμοποιήσετε",
+ "description": "Εκκίνηση γρήγορα πολλών εργαλείων CLI κώδικα, για αύξηση της αποδοτικότητας ανάπτυξης",
+ "folder_placeholder": "Επιλέξτε κατάλογο εργασίας",
+ "install_bun": "Εγκατάσταση Bun",
+ "installing_bun": "Εγκατάσταση...",
+ "launch": {
+ "bun_required": "Παρακαλώ εγκαταστήστε πρώτα το περιβάλλον Bun πριν εκκινήσετε το εργαλείο CLI",
+ "error": "Η εκκίνηση απέτυχε, παρακαλώ δοκιμάστε ξανά",
+ "label": "Εκκίνηση",
+ "success": "Επιτυχής εκκίνηση",
+ "validation_error": "Συμπληρώστε όλα τα υποχρεωτικά πεδία: εργαλείο CLI, μοντέλο και κατάλογος εργασίας"
+ },
+ "launching": "Εκκίνηση...",
+ "model": "μοντέλο",
+ "model_placeholder": "Επιλέξτε το μοντέλο που θα χρησιμοποιήσετε",
+ "model_required": "Επιλέξτε μοντέλο",
+ "select_folder": "Επιλογή φακέλου",
+ "title": "Εργαλεία κώδικα",
+ "update_options": "Ενημέρωση επιλογών",
+ "working_directory": "κατάλογος εργασίας"
+ },
"code_block": {
"collapse": "συμπεριληφθείς",
"copy": {
@@ -815,6 +840,9 @@
"created": "Ημερομηνία Δημιουργίας",
"last_updated": "Τελευταία ενημέρωση",
"messages": "Αριθμός Μηνυμάτων",
+ "notion": {
+ "reasoning_truncated": "Η αλυσίδα σκέψης δεν μπορεί να διαιρεθεί, έχει κοπεί"
+ },
"user": "Χρήστης"
},
"files": {
@@ -1232,7 +1260,8 @@
},
"notion": {
"export": "Σφάλμα στην εξαγωγή του Notion, παρακαλείστε να ελέγξετε τη σύνδεση και τη διαμόρφωση κατά τη διατύπωση του χειρισμού",
- "no_api_key": "Δεν έχετε διαθέσιμο το API Key του Notion ή το ID της βάσης του Notion"
+ "no_api_key": "Δεν έχετε διαθέσιμο το API Key του Notion ή το ID της βάσης του Notion",
+ "no_content": "Δεν υπάρχει περιεχόμενο για εξαγωγή στο Notion"
},
"siyuan": {
"export": "Η έκθεση σημειώσεων Siyuan απέτυχε, ελέγξτε την κατάσταση σύνδεσης και τις ρυθμίσεις σύμφωνα με τα έγγραφα",
@@ -1358,14 +1387,8 @@
}
},
"warn": {
- "notion": {
- "exporting": "Εξαγωγή στο Notion, μην επαναλάβετε την διαδικασία εξαγωγής!"
- },
- "siyuan": {
- "exporting": "Γίνεται εξαγωγή στις σημειώσεις Siyuan· μην ξαναζητήσετε την έκθεση!"
- },
- "yuque": {
- "exporting": "Γίνεται έκθεση Yuque· μην ξαναζητήσετε την έκθεση!"
+ "export": {
+ "exporting": "Παρακαλώ περιμένετε την ολοκλήρωση της προηγούμενης εξαγωγής. Εκτελείται άλλη εξαγωγή."
}
},
"warning": {
@@ -2690,6 +2713,17 @@
"title": "Εκκίνηση",
"totray": "Εισαγωγή στην συνδρομή κατά την εκκίνηση"
},
+ "math": {
+ "engine": {
+ "label": "Μηχανισμός μαθηματικών τύπων",
+ "none": "κανένα"
+ },
+ "single_dollar": {
+ "label": "ενεργοποίηση $...$",
+ "tip": "Επεξεργασία μαθηματικών τύπων που περικλείονται σε ένα μόνο σύμβολο δολαρίου $...$, προεπιλογή ενεργοποιημένη."
+ },
+ "title": "Ρύθμιση μαθηματικών τύπων"
+ },
"mcp": {
"actions": "Ενέργειες",
"active": "Ενεργοποίηση",
@@ -2920,10 +2954,6 @@
"title": "Ρυθμίσεις εισαγωγής"
},
"markdown_rendering_input_message": "Markdown Rendering Input Message",
- "math_engine": {
- "label": "Μηχανική μαθηματικών εξισώσεων",
- "none": "Κανένα"
- },
"metrics": "Χρόνος πρώτου χαρακτήρα {{time_first_token_millsec}}ms | {{token_speed}} tokens ανά δευτερόλεπτο",
"model": {
"title": "Ρυθμίσεις μοντέλου"
@@ -2935,6 +2965,7 @@
"none": "Χωρίς εμφάνιση"
},
"prompt": "Λήμμα προτροπής",
+ "show_message_outline": "Εμφάνιση πλαισίου μηνύματος",
"title": "Ρυθμίσεις μηνυμάτων",
"use_serif_font": "Χρήση μορφής Serif"
},
@@ -3561,6 +3592,7 @@
"title": {
"agents": "Πράκτορες",
"apps": "Εφαρμογές",
+ "code": "Κώδικας",
"files": "Αρχεία",
"home": "Αρχική Σελίδα",
"knowledge": "Βάση Γνώσης",
diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json
index 9352f4635d..559eedc98d 100644
--- a/src/renderer/src/i18n/translate/es-es.json
+++ b/src/renderer/src/i18n/translate/es-es.json
@@ -648,6 +648,31 @@
},
"translate": "Traducir"
},
+ "code": {
+ "auto_update_to_latest": "Comprobar actualizaciones e instalar la versión más reciente",
+ "bun_required_message": "Se requiere instalar el entorno Bun para ejecutar la herramienta de línea de comandos",
+ "cli_tool": "Herramienta de línea de comandos",
+ "cli_tool_placeholder": "Seleccione la herramienta de línea de comandos que desea utilizar",
+ "description": "Inicia rápidamente múltiples herramientas de línea de comandos para código, aumentando la eficiencia del desarrollo",
+ "folder_placeholder": "Seleccionar directorio de trabajo",
+ "install_bun": "Instalar Bun",
+ "installing_bun": "Instalando...",
+ "launch": {
+ "bun_required": "Instale el entorno Bun antes de iniciar la herramienta de línea de comandos",
+ "error": "Error al iniciar, intente nuevamente",
+ "label": "Iniciar",
+ "success": "Inicio exitoso",
+ "validation_error": "Complete all required fields: CLI tool, model, and working directory"
+ },
+ "launching": "Iniciando...",
+ "model": "modelo",
+ "model_placeholder": "Seleccionar el modelo que se va a utilizar",
+ "model_required": "Seleccione el modelo",
+ "select_folder": "Seleccionar carpeta",
+ "title": "Herramientas de código",
+ "update_options": "Opciones de actualización",
+ "working_directory": "directorio de trabajo"
+ },
"code_block": {
"collapse": "Replegar",
"copy": {
@@ -815,6 +840,9 @@
"created": "Fecha de creación",
"last_updated": "Última actualización",
"messages": "Mensajes",
+ "notion": {
+ "reasoning_truncated": "La cadena de pensamiento no se puede dividir en bloques, ha sido truncada"
+ },
"user": "Usuario"
},
"files": {
@@ -1232,7 +1260,8 @@
},
"notion": {
"export": "Error de exportación de Notion, verifique el estado de conexión y la configuración según la documentación",
- "no_api_key": "No se ha configurado la clave API de Notion o la ID de la base de datos de Notion"
+ "no_api_key": "No se ha configurado la clave API de Notion o la ID de la base de datos de Notion",
+ "no_content": "No hay contenido que exportar a Notion"
},
"siyuan": {
"export": "Error al exportar la nota de Siyuan, verifique el estado de la conexión y revise la configuración según la documentación",
@@ -1358,14 +1387,8 @@
}
},
"warn": {
- "notion": {
- "exporting": "Se está exportando a Notion, ¡no solicite nuevamente la exportación!"
- },
- "siyuan": {
- "exporting": "Exportando a Siyuan, ¡no solicite la exportación nuevamente!"
- },
- "yuque": {
- "exporting": "Exportando Yuque, ¡no solicite la exportación nuevamente!"
+ "export": {
+ "exporting": "Realizando otra exportación, espere a que finalice la anterior para intentarlo de nuevo"
}
},
"warning": {
@@ -2690,6 +2713,17 @@
"title": "Inicio",
"totray": "Minimizar a la bandeja al iniciar"
},
+ "math": {
+ "engine": {
+ "label": "Motor de fórmulas matemáticas",
+ "none": "sin contenido"
+ },
+ "single_dollar": {
+ "label": "habilitar $...$",
+ "tip": "Renderiza fórmulas matemáticas encerradas entre un único símbolo de dólar $...$, habilitado por defecto."
+ },
+ "title": "Configuración de fórmulas matemáticas"
+ },
"mcp": {
"actions": "Acciones",
"active": "Activar",
@@ -2920,10 +2954,6 @@
"title": "Configuración de entrada"
},
"markdown_rendering_input_message": "Renderizar mensajes de entrada en Markdown",
- "math_engine": {
- "label": "Motor de fórmulas matemáticas",
- "none": "Ninguno"
- },
"metrics": "Retraso inicial {{time_first_token_millsec}}ms | {{token_speed}} tokens por segundo",
"model": {
"title": "Configuración del modelo"
@@ -2935,6 +2965,7 @@
"none": "No mostrar"
},
"prompt": "Palabra de indicación",
+ "show_message_outline": "Mostrar esquema del mensaje",
"title": "Configuración de mensajes",
"use_serif_font": "Usar fuente serif"
},
@@ -3561,6 +3592,7 @@
"title": {
"agents": "Agentes",
"apps": "Aplicaciones",
+ "code": "Código",
"files": "Archivos",
"home": "Inicio",
"knowledge": "Base de conocimiento",
diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json
index b0c4beee9b..b2e996a7ad 100644
--- a/src/renderer/src/i18n/translate/fr-fr.json
+++ b/src/renderer/src/i18n/translate/fr-fr.json
@@ -648,6 +648,31 @@
},
"translate": "Traduire"
},
+ "code": {
+ "auto_update_to_latest": "Vérifier les mises à jour et installer la dernière version",
+ "bun_required_message": "L'exécution de l'outil en ligne de commande nécessite l'installation de l'environnement Bun",
+ "cli_tool": "Outil CLI",
+ "cli_tool_placeholder": "Sélectionnez l'outil CLI à utiliser",
+ "description": "Lancer rapidement plusieurs outils CLI de code pour améliorer l'efficacité du développement",
+ "folder_placeholder": "Sélectionner le répertoire de travail",
+ "install_bun": "Installer Bun",
+ "installing_bun": "Installation en cours...",
+ "launch": {
+ "bun_required": "Veuillez d'abord installer l'environnement Bun avant de lancer l'outil en ligne de commande",
+ "error": "Échec du démarrage, veuillez réessayer",
+ "label": "Démarrer",
+ "success": "Démarrage réussi",
+ "validation_error": "Veuillez remplir tous les champs obligatoires : outil CLI, modèle et répertoire de travail"
+ },
+ "launching": "En cours de démarrage...",
+ "model": "modèle",
+ "model_placeholder": "Sélectionnez le modèle à utiliser",
+ "model_required": "Veuillez sélectionner le modèle",
+ "select_folder": "Sélectionner le dossier",
+ "title": "Outils de code",
+ "update_options": "Options de mise à jour",
+ "working_directory": "répertoire de travail"
+ },
"code_block": {
"collapse": "Réduire",
"copy": {
@@ -815,6 +840,9 @@
"created": "Date de création",
"last_updated": "Dernière mise à jour",
"messages": "Messages",
+ "notion": {
+ "reasoning_truncated": "La chaîne de pensée ne peut pas être fractionnée, elle a été tronquée."
+ },
"user": "Utilisateur"
},
"files": {
@@ -1232,7 +1260,8 @@
},
"notion": {
"export": "Erreur lors de l'exportation vers Notion, veuillez vérifier l'état de la connexion et la configuration dans la documentation",
- "no_api_key": "Aucune clé API Notion ou ID de base de données Notion configurée"
+ "no_api_key": "Aucune clé API Notion ou ID de base de données Notion configurée",
+ "no_content": "Aucun contenu à exporter vers Notion"
},
"siyuan": {
"export": "Échec de l'exportation de la note Siyuan, veuillez vérifier l'état de la connexion et la configuration indiquée dans le document",
@@ -1358,14 +1387,8 @@
}
},
"warn": {
- "notion": {
- "exporting": "Exportation en cours vers Notion, veuillez ne pas faire plusieurs demandes d'exportation!"
- },
- "siyuan": {
- "exporting": "Exportation vers Siyuan en cours, veuillez ne pas demander à exporter à nouveau !"
- },
- "yuque": {
- "exporting": "Exportation Yuque en cours, veuillez ne pas demander à exporter à nouveau !"
+ "export": {
+ "exporting": "Une autre exportation est en cours, veuillez patienter jusqu'à la fin de l'exportation précédente pour réessayer."
}
},
"warning": {
@@ -2690,6 +2713,17 @@
"title": "Démarrage",
"totray": "Minimiser dans la barre d'état système au démarrage"
},
+ "math": {
+ "engine": {
+ "label": "Moteur de formules mathématiques",
+ "none": "Aucun"
+ },
+ "single_dollar": {
+ "label": "activer $...$",
+ "tip": "Rendu des formules mathématiques encapsulées par un seul symbole dollar $...$, activé par défaut."
+ },
+ "title": "Configuration des formules mathématiques"
+ },
"mcp": {
"actions": "Actions",
"active": "Activer",
@@ -2920,10 +2954,6 @@
"title": "Paramètres d'entrée"
},
"markdown_rendering_input_message": "Rendu Markdown des messages d'entrée",
- "math_engine": {
- "label": "Moteur de formules mathématiques",
- "none": "Aucun"
- },
"metrics": "Latence initiale {{time_first_token_millsec}}ms | Vitesse de tokenisation {{token_speed}} tokens/s",
"model": {
"title": "Paramètres du modèle"
@@ -2935,6 +2965,7 @@
"none": "Ne pas afficher"
},
"prompt": "Mot-clé d'affichage",
+ "show_message_outline": "Afficher le plan du message",
"title": "Paramètres des messages",
"use_serif_font": "Utiliser une police serif"
},
@@ -3561,6 +3592,7 @@
"title": {
"agents": "Agent intelligent",
"apps": "Mini-programmes",
+ "code": "Code",
"files": "Fichiers",
"home": "Page d'accueil",
"knowledge": "Base de connaissances",
diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json
index 72f2dd5998..be07031b5f 100644
--- a/src/renderer/src/i18n/translate/pt-pt.json
+++ b/src/renderer/src/i18n/translate/pt-pt.json
@@ -648,6 +648,31 @@
},
"translate": "Traduzir"
},
+ "code": {
+ "auto_update_to_latest": "Verificar atualizações e instalar a versão mais recente",
+ "bun_required_message": "Executar a ferramenta CLI requer a instalação do ambiente Bun",
+ "cli_tool": "Ferramenta de linha de comando",
+ "cli_tool_placeholder": "Selecione a ferramenta de linha de comando a ser utilizada",
+ "description": "Inicie rapidamente várias ferramentas de linha de comando de código, aumentando a eficiência do desenvolvimento",
+ "folder_placeholder": "Selecionar diretório de trabalho",
+ "install_bun": "Instalar o Bun",
+ "installing_bun": "Instalando...",
+ "launch": {
+ "bun_required": "Instale o ambiente Bun antes de iniciar a ferramenta de linha de comando",
+ "error": "Falha ao iniciar, tente novamente",
+ "label": "iniciar",
+ "success": "Início bem-sucedido",
+ "validation_error": "Preencha todos os campos obrigatórios: ferramenta CLI, modelo e diretório de trabalho"
+ },
+ "launching": "Iniciando...",
+ "model": "modelo",
+ "model_placeholder": "Selecione o modelo a ser utilizado",
+ "model_required": "Selecione o modelo",
+ "select_folder": "Selecionar pasta",
+ "title": "Ferramenta de código",
+ "update_options": "Opções de atualização",
+ "working_directory": "diretório de trabalho"
+ },
"code_block": {
"collapse": "Recolher",
"copy": {
@@ -815,6 +840,9 @@
"created": "Criado em",
"last_updated": "Última Atualização",
"messages": "Mensagens",
+ "notion": {
+ "reasoning_truncated": "A cadeia de pensamento não pode ser dividida em partes, foi interrompida"
+ },
"user": "Usuário"
},
"files": {
@@ -1232,7 +1260,8 @@
},
"notion": {
"export": "Erro ao exportar Notion, verifique o status da conexão e a configuração de acordo com a documentação",
- "no_api_key": "API Key ou Notion Database ID não configurados"
+ "no_api_key": "API Key ou Notion Database ID não configurados",
+ "no_content": "Nenhum conteúdo para exportar para o Notion"
},
"siyuan": {
"export": "Falha ao exportar nota do Siyuan, verifique o estado da conexão e confira a configuração no documento",
@@ -1358,14 +1387,8 @@
}
},
"warn": {
- "notion": {
- "exporting": "Exportando para Notion, não solicite novamente a exportação!"
- },
- "siyuan": {
- "exporting": "Exportando para o Siyuan, por favor não solicite a exportação novamente!"
- },
- "yuque": {
- "exporting": "Exportando para Yuque, por favor não solicite a exportação novamente!"
+ "export": {
+ "exporting": "A exportação de outros arquivos está em andamento, aguarde a conclusão da exportação anterior e tente novamente."
}
},
"warning": {
@@ -2690,6 +2713,17 @@
"title": "Inicialização",
"totray": "Minimizar para bandeja ao iniciar"
},
+ "math": {
+ "engine": {
+ "label": "Motor de fórmulas matemáticas",
+ "none": "sem conteúdo"
+ },
+ "single_dollar": {
+ "label": "ativar $...$",
+ "tip": "Renderiza fórmulas matemáticas delimitadas por um único sinal de dólar $...$, habilitado por padrão."
+ },
+ "title": "Configuração de fórmulas matemáticas"
+ },
"mcp": {
"actions": "Ações",
"active": "Ativar",
@@ -2920,10 +2954,6 @@
"title": "Configurações de entrada"
},
"markdown_rendering_input_message": "Renderização de markdown na entrada de mensagens",
- "math_engine": {
- "label": "Motor de fórmulas matemáticas",
- "none": "Nenhum"
- },
"metrics": "Atraso inicial {{time_first_token_millsec}}ms | Taxa de token por segundo {{token_speed}} tokens",
"model": {
"title": "Configurações de modelo"
@@ -2935,6 +2965,7 @@
"none": "Não mostrar"
},
"prompt": "Exibir palavra-chave",
+ "show_message_outline": "Exibir esboço da mensagem",
"title": "Configurações de mensagem",
"use_serif_font": "Usar fonte serif"
},
@@ -3561,6 +3592,7 @@
"title": {
"agents": "Agentes",
"apps": "Miniaplicativos",
+ "code": "Código",
"files": "Arquivos",
"home": "Página Inicial",
"knowledge": "Base de Conhecimento",
diff --git a/src/renderer/src/utils/export.ts b/src/renderer/src/utils/export.ts
index bc7d9b20ad..a7f1a03a6e 100644
--- a/src/renderer/src/utils/export.ts
+++ b/src/renderer/src/utils/export.ts
@@ -12,10 +12,112 @@ import { convertMathFormula, markdownToPlainText } from '@renderer/utils/markdow
import { getCitationContent, getMainTextContent, getThinkingContent } from '@renderer/utils/messageUtils/find'
import { markdownToBlocks } from '@tryfabric/martian'
import dayjs from 'dayjs'
+import DOMPurify from 'dompurify'
import { appendBlocks } from 'notion-helper' // 引入 notion-helper 的 appendBlocks 函数
const logger = loggerService.withContext('Utils:export')
+// 全局的导出状态获取函数
+const getExportState = () => store.getState().runtime.export.isExporting
+
+// 全局的导出状态设置函数,使用 dispatch 保障 Redux 状态更新正确
+const setExportingState = (isExporting: boolean) => {
+ store.dispatch(setExportState({ isExporting }))
+}
+
+/**
+ * 安全地处理思维链内容,保留安全的 HTML 标签如
,移除危险内容
+ *
+ * 支持的标签:
+ * - 结构:br, p, div, span, h1-h6, blockquote
+ * - 格式:strong, b, em, i, u, s, del, mark, small, sup, sub
+ * - 列表:ul, ol, li
+ * - 代码:code, pre, kbd, var, samp
+ * - 表格:table, thead, tbody, tfoot, tr, td, th
+ *
+ * @param content 原始思维链内容
+ * @returns 安全处理后的内容
+ */
+const sanitizeReasoningContent = (content: string): string => {
+ // 先处理换行符转换为
+ const contentWithBr = content.replace(/\n/g, '
')
+
+ // 使用 DOMPurify 清理内容,保留常用的安全标签和属性
+ const cleanContent = DOMPurify.sanitize(contentWithBr, {
+ ALLOWED_TAGS: [
+ // 换行和基础结构
+ 'br',
+ 'p',
+ 'div',
+ 'span',
+ // 文本格式化
+ 'strong',
+ 'b',
+ 'em',
+ 'i',
+ 'u',
+ 's',
+ 'del',
+ 'mark',
+ 'small',
+ // 上标下标(数学公式、引用等)
+ 'sup',
+ 'sub',
+ // 标题
+ 'h1',
+ 'h2',
+ 'h3',
+ 'h4',
+ 'h5',
+ 'h6',
+ // 引用
+ 'blockquote',
+ // 列表
+ 'ul',
+ 'ol',
+ 'li',
+ // 代码相关
+ 'code',
+ 'pre',
+ 'kbd',
+ 'var',
+ 'samp',
+ // 表格(AI输出中可能包含表格)
+ 'table',
+ 'thead',
+ 'tbody',
+ 'tfoot',
+ 'tr',
+ 'td',
+ 'th',
+ // 分隔线
+ 'hr'
+ ],
+ ALLOWED_ATTR: [
+ // 安全的通用属性
+ 'class',
+ 'title',
+ 'lang',
+ 'dir',
+ // code 标签的语言属性
+ 'data-language',
+ // 表格属性
+ 'colspan',
+ 'rowspan',
+ // 列表属性
+ 'start',
+ 'type'
+ ],
+ KEEP_CONTENT: true, // 保留被移除标签的文本内容
+ RETURN_DOM: false,
+ SANITIZE_DOM: true,
+ // 允许的协议(预留,虽然目前没有允许链接标签)
+ ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i
+ })
+
+ return cleanContent
+}
+
/**
* 获取话题的消息列表,使用TopicManager确保消息被正确加载
* 这样可以避免从未打开过的话题导出为空的问题
@@ -33,7 +135,7 @@ async function fetchTopicMessages(topicId: string): Promise {
* @param {number} [length=80] 标题最大长度,默认为 80
* @returns {string} 提取的标题
*/
-export function getTitleFromString(str: string, length: number = 80) {
+export function getTitleFromString(str: string, length: number = 80): string {
let title = str.trimStart().split('\n')[0]
if (title.includes('。')) {
@@ -57,7 +159,7 @@ export function getTitleFromString(str: string, length: number = 80) {
return title
}
-const getRoleText = (role: string, modelName?: string, providerId?: string) => {
+const getRoleText = (role: string, modelName?: string, providerId?: string): string => {
const { showModelNameInMarkdown, showModelProviderInMarkdown } = store.getState().settings
if (role === 'user') {
@@ -166,7 +268,7 @@ const createBaseMarkdown = (
includeReasoning: boolean = false,
excludeCitations: boolean = false,
normalizeCitations: boolean = true
-) => {
+): { titleSection: string; reasoningSection: string; contentSection: string; citation: string } => {
const { forceDollarMathInMarkdown } = store.getState().settings
const roleText = getRoleText(message.role, message.model?.name, message.model?.provider)
const titleSection = `## ${roleText}`
@@ -180,13 +282,8 @@ const createBaseMarkdown = (
} else if (reasoningContent.startsWith('')) {
reasoningContent = reasoningContent.substring(7)
}
- reasoningContent = reasoningContent
- .replace(/&/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"')
- .replace(/'/g, ''')
- .replace(/\n/g, '
')
+ // 使用 DOMPurify 安全地处理思维链内容
+ reasoningContent = sanitizeReasoningContent(reasoningContent)
if (forceDollarMathInMarkdown) {
reasoningContent = convertMathFormula(reasoningContent)
}
@@ -216,7 +313,7 @@ const createBaseMarkdown = (
return { titleSection, reasoningSection, contentSection: processedContent, citation }
}
-export const messageToMarkdown = (message: Message, excludeCitations?: boolean) => {
+export const messageToMarkdown = (message: Message, excludeCitations?: boolean): string => {
const { excludeCitationsInExport, standardizeCitationsInExport } = store.getState().settings
const shouldExcludeCitations = excludeCitations ?? excludeCitationsInExport
const { titleSection, contentSection, citation } = createBaseMarkdown(
@@ -228,7 +325,7 @@ export const messageToMarkdown = (message: Message, excludeCitations?: boolean)
return [titleSection, '', contentSection, citation].join('\n')
}
-export const messageToMarkdownWithReasoning = (message: Message, excludeCitations?: boolean) => {
+export const messageToMarkdownWithReasoning = (message: Message, excludeCitations?: boolean): string => {
const { excludeCitationsInExport, standardizeCitationsInExport } = store.getState().settings
const shouldExcludeCitations = excludeCitations ?? excludeCitationsInExport
const { titleSection, reasoningSection, contentSection, citation } = createBaseMarkdown(
@@ -237,10 +334,14 @@ export const messageToMarkdownWithReasoning = (message: Message, excludeCitation
shouldExcludeCitations,
standardizeCitationsInExport
)
- return [titleSection, '', reasoningSection + contentSection, citation].join('\n')
+ return [titleSection, '', reasoningSection, contentSection, citation].join('\n')
}
-export const messagesToMarkdown = (messages: Message[], exportReasoning?: boolean, excludeCitations?: boolean) => {
+export const messagesToMarkdown = (
+ messages: Message[],
+ exportReasoning?: boolean,
+ excludeCitations?: boolean
+): string => {
return messages
.map((message) =>
exportReasoning
@@ -266,7 +367,11 @@ const messagesToPlainText = (messages: Message[]): string => {
return messages.map(formatMessageAsPlainText).join('\n\n')
}
-export const topicToMarkdown = async (topic: Topic, exportReasoning?: boolean, excludeCitations?: boolean) => {
+export const topicToMarkdown = async (
+ topic: Topic,
+ exportReasoning?: boolean,
+ excludeCitations?: boolean
+): Promise => {
const topicName = `# ${topic.name}`
const messages = await fetchTopicMessages(topic.id)
@@ -290,7 +395,18 @@ export const topicToPlainText = async (topic: Topic): Promise => {
return topicName
}
-export const exportTopicAsMarkdown = async (topic: Topic, exportReasoning?: boolean, excludeCitations?: boolean) => {
+export const exportTopicAsMarkdown = async (
+ topic: Topic,
+ exportReasoning?: boolean,
+ excludeCitations?: boolean
+): Promise => {
+ if (getExportState()) {
+ window.message.warning({ content: i18n.t('message.warn.export.exporting'), key: 'markdown-exporting' })
+ return
+ }
+
+ setExportingState(true)
+
const { markdownExportPath } = store.getState().settings
if (!markdownExportPath) {
try {
@@ -305,7 +421,9 @@ export const exportTopicAsMarkdown = async (topic: Topic, exportReasoning?: bool
}
} catch (error: any) {
window.message.error({ content: i18n.t('message.error.markdown.export.specified'), key: 'markdown-error' })
- logger.debug(error)
+ logger.error('Failed to export topic as markdown:', error)
+ } finally {
+ setExportingState(false)
}
} else {
try {
@@ -316,7 +434,9 @@ export const exportTopicAsMarkdown = async (topic: Topic, exportReasoning?: bool
window.message.success({ content: i18n.t('message.success.markdown.export.preconf'), key: 'markdown-success' })
} catch (error: any) {
window.message.error({ content: i18n.t('message.error.markdown.export.preconf'), key: 'markdown-error' })
- logger.debug(error)
+ logger.error('Failed to export topic as markdown:', error)
+ } finally {
+ setExportingState(false)
}
}
}
@@ -325,7 +445,14 @@ export const exportMessageAsMarkdown = async (
message: Message,
exportReasoning?: boolean,
excludeCitations?: boolean
-) => {
+): Promise => {
+ if (getExportState()) {
+ window.message.warning({ content: i18n.t('message.warn.export.exporting'), key: 'markdown-exporting' })
+ return
+ }
+
+ setExportingState(true)
+
const { markdownExportPath } = store.getState().settings
if (!markdownExportPath) {
try {
@@ -343,7 +470,9 @@ export const exportMessageAsMarkdown = async (
}
} catch (error: any) {
window.message.error({ content: i18n.t('message.error.markdown.export.specified'), key: 'markdown-error' })
- logger.debug(error)
+ logger.error('Failed to export message as markdown:', error)
+ } finally {
+ setExportingState(false)
}
} else {
try {
@@ -357,12 +486,14 @@ export const exportMessageAsMarkdown = async (
window.message.success({ content: i18n.t('message.success.markdown.export.preconf'), key: 'markdown-success' })
} catch (error: any) {
window.message.error({ content: i18n.t('message.error.markdown.export.preconf'), key: 'markdown-error' })
- logger.debug(error)
+ logger.error('Failed to export message as markdown:', error)
+ } finally {
+ setExportingState(false)
}
}
}
-const convertMarkdownToNotionBlocks = async (markdown: string) => {
+const convertMarkdownToNotionBlocks = async (markdown: string): Promise => {
return markdownToBlocks(markdown)
}
@@ -371,77 +502,109 @@ const convertThinkingToNotionBlocks = async (thinkingContent: string): Promise标签转换为真正的换行符
+ const processedContent = thinkingContent.replace(/
/g, '\n')
- return thinkingBlocks
+ // 使用 markdownToBlocks 处理思维链内容
+ const childrenBlocks = markdownToBlocks(processedContent)
+
+ return [
+ {
+ object: 'block',
+ type: 'toggle',
+ toggle: {
+ rich_text: [
+ {
+ type: 'text',
+ text: {
+ content: '🤔 ' + i18n.t('common.reasoning_content')
+ },
+ annotations: {
+ bold: true
+ }
+ }
+ ],
+ children: childrenBlocks
+ }
+ }
+ ]
+ } catch (error) {
+ logger.error('failed to process reasoning content:', error as Error)
+ // 发生错误时,回退到简单的段落处理
+ return [
+ {
+ object: 'block',
+ type: 'toggle',
+ toggle: {
+ rich_text: [
+ {
+ type: 'text',
+ text: {
+ content: '🤔 ' + i18n.t('common.reasoning_content')
+ },
+ annotations: {
+ bold: true
+ }
+ }
+ ],
+ children: [
+ {
+ object: 'block',
+ type: 'paragraph',
+ paragraph: {
+ rich_text: [
+ {
+ type: 'text',
+ text: {
+ content:
+ thinkingContent.length > 1800
+ ? thinkingContent.substring(0, 1800) + '...\n' + i18n.t('export.notion.reasoning_truncated')
+ : thinkingContent
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
}
-const executeNotionExport = async (title: string, allBlocks: any[]): Promise => {
- const { isExporting } = store.getState().runtime.export
- if (isExporting) {
- window.message.warning({ content: i18n.t('message.warn.notion.exporting'), key: 'notion-exporting' })
- return null
+const executeNotionExport = async (title: string, allBlocks: any[]): Promise => {
+ if (getExportState()) {
+ window.message.warning({ content: i18n.t('message.warn.export.exporting'), key: 'notion-exporting' })
+ return false
}
- setExportState({ isExporting: true })
-
- title = title.slice(0, 29) + '...'
-
const { notionDatabaseID, notionApiKey } = store.getState().settings
if (!notionApiKey || !notionDatabaseID) {
window.message.error({ content: i18n.t('message.error.notion.no_api_key'), key: 'notion-no-apikey-error' })
- setExportState({ isExporting: false })
- return null
+ return false
+ }
+
+ if (allBlocks.length === 0) {
+ window.message.error({ content: i18n.t('message.error.notion.export'), key: 'notion-no-content-error' })
+ return false
+ }
+
+ setExportingState(true)
+
+ // 限制标题长度
+ if (title.length > 32) {
+ title = title.slice(0, 29) + '...'
}
try {
const notion = new Client({ auth: notionApiKey })
- if (allBlocks.length === 0) {
- throw new Error('No content to export')
- }
-
window.message.loading({
content: i18n.t('message.loading.notion.preparing'),
key: 'notion-preparing',
duration: 0
})
- let mainPageResponse: any = null
- let parentBlockId: string | null = null
const response = await notion.pages.create({
parent: { database_id: notionDatabaseID },
@@ -451,34 +614,37 @@ const executeNotionExport = async (title: string, allBlocks: any[]): Promise 0) {
- await appendBlocks({
- block_id: parentBlockId,
- children: allBlocks,
- client: notion
- })
- }
+
+ await appendBlocks({
+ block_id: response.id,
+ children: allBlocks,
+ client: notion
+ })
+
window.message.destroy('notion-exporting')
window.message.success({ content: i18n.t('message.success.notion.export'), key: 'notion-success' })
- return mainPageResponse
+ return true
} catch (error: any) {
- window.message.error({ content: i18n.t('message.error.notion.export'), key: 'notion-export-progress' })
- logger.debug(error)
- return null
+ // 清理可能存在的loading消息
+ window.message.destroy('notion-preparing')
+ window.message.destroy('notion-exporting')
+
+ logger.error('Notion export failed:', error)
+ window.message.error({ content: i18n.t('message.error.notion.export'), key: 'notion-export-error' })
+ return false
} finally {
- setExportState({ isExporting: false })
+ setExportingState(false)
}
}
-export const exportMessageToNotion = async (title: string, content: string, message?: Message) => {
+export const exportMessageToNotion = async (title: string, content: string, message?: Message): Promise => {
const { notionExportReasoning } = store.getState().settings
const notionBlocks = await convertMarkdownToNotionBlocks(content)
@@ -498,7 +664,7 @@ export const exportMessageToNotion = async (title: string, content: string, mess
return executeNotionExport(title, notionBlocks)
}
-export const exportTopicToNotion = async (topic: Topic) => {
+export const exportTopicToNotion = async (topic: Topic): Promise => {
const { notionExportReasoning, excludeCitationsInExport } = store.getState().settings
const topicMessages = await fetchTopicMessages(topic.id)
@@ -532,12 +698,11 @@ export const exportTopicToNotion = async (topic: Topic) => {
return executeNotionExport(topic.name, allBlocks)
}
-export const exportMarkdownToYuque = async (title: string, content: string) => {
- const { isExporting } = store.getState().runtime.export
+export const exportMarkdownToYuque = async (title: string, content: string): Promise => {
const { yuqueToken, yuqueRepoId } = store.getState().settings
- if (isExporting) {
- window.message.warning({ content: i18n.t('message.warn.yuque.exporting'), key: 'yuque-exporting' })
+ if (getExportState()) {
+ window.message.warning({ content: i18n.t('message.warn.export.exporting'), key: 'yuque-exporting' })
return
}
@@ -546,7 +711,7 @@ export const exportMarkdownToYuque = async (title: string, content: string) => {
return
}
- setExportState({ isExporting: true })
+ setExportingState(true)
try {
const response = await fetch(`https://www.yuque.com/api/v2/repos/${yuqueRepoId}/docs`, {
@@ -602,7 +767,7 @@ export const exportMarkdownToYuque = async (title: string, content: string) => {
})
return null
} finally {
- setExportState({ isExporting: false })
+ setExportingState(false)
}
}
@@ -617,7 +782,14 @@ export const exportMarkdownToYuque = async (title: string, content: string) => {
* @param attributes.folder 选择的文件夹路径或文件路径
* @param attributes.vault 选择的Vault名称
*/
-export const exportMarkdownToObsidian = async (attributes: any) => {
+export const exportMarkdownToObsidian = async (attributes: any): Promise => {
+ if (getExportState()) {
+ window.message.warning({ content: i18n.t('message.warn.export.exporting'), key: 'obsidian-exporting' })
+ return
+ }
+
+ setExportingState(true)
+
try {
// 从参数获取Vault名称
const obsidianVault = attributes.vault
@@ -669,8 +841,10 @@ export const exportMarkdownToObsidian = async (attributes: any) => {
window.open(obsidianUrl)
window.message.success(i18n.t('chat.topics.export.obsidian_export_success'))
} catch (error) {
- logger.error('导出到Obsidian失败:', error as Error)
+ logger.error('Failed to export to Obsidian:', error as Error)
window.message.error(i18n.t('chat.topics.export.obsidian_export_failed'))
+ } finally {
+ setExportingState(false)
}
}
@@ -719,14 +893,24 @@ function transformObsidianFileName(fileName: string): string {
return sanitized
}
-export const exportMarkdownToJoplin = async (title: string, contentOrMessages: string | Message | Message[]) => {
+export const exportMarkdownToJoplin = async (
+ title: string,
+ contentOrMessages: string | Message | Message[]
+): Promise => {
const { joplinUrl, joplinToken, joplinExportReasoning, excludeCitationsInExport } = store.getState().settings
+ if (getExportState()) {
+ window.message.warning({ content: i18n.t('message.warn.export.exporting'), key: 'joplin-exporting' })
+ return
+ }
+
if (!joplinUrl || !joplinToken) {
window.message.error(i18n.t('message.error.joplin.no_config'))
return
}
+ setExportingState(true)
+
let content: string
if (typeof contentOrMessages === 'string') {
content = contentOrMessages
@@ -763,11 +947,13 @@ export const exportMarkdownToJoplin = async (title: string, contentOrMessages: s
}
window.message.success(i18n.t('message.success.joplin.export'))
- return
+ return data
} catch (error: any) {
+ logger.error('Failed to export to Joplin:', error)
window.message.error(i18n.t('message.error.joplin.export'))
- logger.debug(error)
- return
+ return null
+ } finally {
+ setExportingState(false)
}
}
@@ -776,12 +962,11 @@ export const exportMarkdownToJoplin = async (title: string, contentOrMessages: s
* @param title 笔记标题
* @param content 笔记内容
*/
-export const exportMarkdownToSiyuan = async (title: string, content: string) => {
- const { isExporting } = store.getState().runtime.export
+export const exportMarkdownToSiyuan = async (title: string, content: string): Promise => {
const { siyuanApiUrl, siyuanToken, siyuanBoxId, siyuanRootPath } = store.getState().settings
- if (isExporting) {
- window.message.warning({ content: i18n.t('message.warn.siyuan.exporting'), key: 'siyuan-exporting' })
+ if (getExportState()) {
+ window.message.warning({ content: i18n.t('message.warn.export.exporting'), key: 'siyuan-exporting' })
return
}
@@ -790,7 +975,7 @@ export const exportMarkdownToSiyuan = async (title: string, content: string) =>
return
}
- setExportState({ isExporting: true })
+ setExportingState(true)
try {
// test connection
@@ -826,13 +1011,13 @@ export const exportMarkdownToSiyuan = async (title: string, content: string) =>
key: 'siyuan-success'
})
} catch (error) {
- logger.error('导出到思源笔记失败:', error as Error)
+ logger.error('Failed to export to Siyuan:', error as Error)
window.message.error({
content: i18n.t('message.error.siyuan.export') + (error instanceof Error ? `: ${error.message}` : ''),
key: 'siyuan-error'
})
} finally {
- setExportState({ isExporting: false })
+ setExportingState(false)
}
}
/**
diff --git a/yarn.lock b/yarn.lock
index 3a27d98704..276a523034 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8552,7 +8552,6 @@ __metadata:
dexie-react-hooks: "npm:^1.1.7"
diff: "npm:^7.0.0"
docx: "npm:^9.0.2"
- dompurify: "npm:^3.2.6"
dotenv-cli: "npm:^7.4.2"
electron: "npm:37.2.3"
electron-builder: "npm:26.0.15"
@@ -11446,7 +11445,7 @@ __metadata:
languageName: node
linkType: hard
-"dompurify@npm:^3.2.5, dompurify@npm:^3.2.6":
+"dompurify@npm:^3.2.5":
version: 3.2.6
resolution: "dompurify@npm:3.2.6"
dependencies: