mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-01-17 06:05:29 +01:00
Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
86e248749e
30 changed files with 308 additions and 34 deletions
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "إزالة الـ ID من أسماء ملفات الموارد",
|
||||
"removeAssetsIDTip": "عند التفعيل سيتم إزالة جزء الـ ID من أسماء ملفات الموارد عند تصدير Markdown",
|
||||
"clearTempFiles": "Temporäre Dateien bereinigen",
|
||||
"clearTempFilesTip": "Bereinigen Sie temporäre Dateien, die während der Ausführung der Anwendung erstellt wurden, um Speicherplatz freizugeben",
|
||||
"uploadFileTooLarge": "⚠️ الملف الذي تم رفعه [${x}] كبير جدًا [${y}],هل تؤكد المتابعة بالرفع?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "نقر",
|
||||
"allowHTMLBLockScript": "السماح بتنفيذ البرامج النصية في كتل HTML",
|
||||
"allowHTMLBLockScriptTip": "بعد التمكين، لن يتم تصحيح البرنامج النصي في كتلة HTML، يرجى إدراك المخاطر المحتملة لهجمات XSS",
|
||||
"allowSVGScript": "السماح بتشغيل السكربتات داخل SVG",
|
||||
"allowSVGScriptTip": "عند التفعيل، لن يتم تصفية الكود داخل SVG لأغراض الأمان. يرجى الانتباه إلى مخاطر XSS المحتملة",
|
||||
"autoLaunchMode0": "عدم التشغيل تلقائيًا",
|
||||
"autoLaunchMode1": "التشغيل التلقائي بعد الإقلاع",
|
||||
"autoLaunchMode2": "التشغيل التلقائي وتقليل الواجهة الرئيسية بعد الإقلاع",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "ID aus Asset-Dateinamen entfernen",
|
||||
"removeAssetsIDTip": "Wenn aktiviert, wird beim Export nach Markdown der ID-Teil aus den Asset-Dateinamen entfernt",
|
||||
"clearTempFiles": "Temporäre Dateien bereinigen",
|
||||
"clearTempFilesTip": "Bereinigen Sie temporäre Dateien, die während der Ausführung der Anwendung erstellt wurden, um Speicherplatz freizugeben",
|
||||
"uploadFileTooLarge": "⚠️ Die hochgeladene Datei [${x}] ist zu groß [${y}]. Sind Sie sicher, dass Sie den Upload fortsetzen möchten?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "Klick",
|
||||
"allowHTMLBLockScript": "Die Ausführung von Skripten innerhalb von HTML-Blöcken zulassen",
|
||||
"allowHTMLBLockScriptTip": "Nach der Aktivierung wird das Skript im HTML-Block nicht bereinigt. Bitte seien Sie sich des potenziellen Risikos von XSS-Angriffen bewusst.",
|
||||
"allowSVGScript": "Ausführen von Skripten innerhalb von SVG erlauben",
|
||||
"allowSVGScriptTip": "Wenn aktiviert, wird der Code im SVG nicht sicherheitsgefiltert。Achten Sie auf mögliche XSS-Risiken",
|
||||
"autoLaunchMode0": "Nicht automatisch starten",
|
||||
"autoLaunchMode1": "Automatisch nach dem Booten starten",
|
||||
"autoLaunchMode2": "Automatisch starten und die Hauptoberfläche minimieren nach dem Booten",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "Remove ID from asset file names",
|
||||
"removeAssetsIDTip": "When enabled, the ID part will be removed from asset file names when exporting Markdown",
|
||||
"clearTempFiles": "Clear temporary files",
|
||||
"clearTempFilesTip": "Clear temporary files generated while the app is running to free up storage space",
|
||||
"uploadFileTooLarge": "⚠️ The uploaded file [${x}] is too large [${y}]. Are you sure you want to continue uploading?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "Click",
|
||||
"allowHTMLBLockScript": "Allow execution of scripts within HTML blocks",
|
||||
"allowHTMLBLockScriptTip": "When enabled, the script in the HTML block will not be sanitized, Please be aware of the potential risk of XSS attacks",
|
||||
"allowSVGScript": "Allow execution of scripts inside SVG",
|
||||
"allowSVGScriptTip": "When enabled, code inside SVG will not be security-filtered. Be aware of potential XSS risks",
|
||||
"autoLaunchMode0": "Do not launch automatically",
|
||||
"autoLaunchMode1": "Auto launch after booting",
|
||||
"autoLaunchMode2": "Auto launch and minimize the main interface after booting",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "Eliminar el ID de los nombres de archivo de los recursos",
|
||||
"removeAssetsIDTip": "Al habilitar, al exportar a Markdown se eliminará la parte del nombre de archivo de recursos que contiene el ID",
|
||||
"clearTempFiles": "Limpiar archivos temporales",
|
||||
"clearTempFilesTip": "Limpia los archivos temporales generados durante la ejecución de la aplicación para liberar espacio de almacenamiento",
|
||||
"uploadFileTooLarge": "⚠️ El archivo subido [${x}] es demasiado grande [${y}],¿estás seguro de que deseas continuar con la subida?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "Hacer clic",
|
||||
"allowHTMLBLockScript": "Permitir la ejecución de scripts dentro de bloques HTML",
|
||||
"allowHTMLBLockScriptTip": "Después de habilitarlo, el script en el bloque HTML no se desinfectará. Tenga en cuenta el riesgo potencial de ataques XSS",
|
||||
"allowSVGScript": "Permitir ejecutar scripts dentro del SVG",
|
||||
"allowSVGScriptTip": "Al activarlo, el código dentro del SVG no será filtrado por seguridad。Tenga en cuenta el riesgo potencial de XSS",
|
||||
"autoLaunchMode0": "No iniciar automáticamente",
|
||||
"autoLaunchMode1": "Inicio automático después del arranque",
|
||||
"autoLaunchMode2": "Iniciar automáticamente y minimizar la interfaz principal después del arranque",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "Supprimer l'ID des noms de fichiers des ressources",
|
||||
"removeAssetsIDTip": "Une fois activé, lors de l'export en Markdown, la partie ID dans le nom des fichiers de ressources sera supprimée",
|
||||
"clearTempFiles": "Effacer les fichiers temporaires",
|
||||
"clearTempFilesTip": "Effacez les fichiers temporaires générés lors de l'exécution de l'application pour libérer de l'espace de stockage",
|
||||
"uploadFileTooLarge": "⚠️ Le fichier envoyé [${x}] est trop volumineux [${y}]. Voulez-vous continuer l'envoi ?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "Cliquez sur",
|
||||
"allowHTMLBLockScript": "Autoriser l'exécution de scripts dans les blocs HTML",
|
||||
"allowHTMLBLockScriptTip": "Après activation, le script dans le bloc HTML ne sera pas nettoyé. Veuillez être conscient du risque potentiel d'attaques XSS",
|
||||
"allowSVGScript": "Autoriser l'exécution de scripts dans le SVG",
|
||||
"allowSVGScriptTip": "Si activé, le code dans le SVG ne sera pas filtré pour la sécurité。Veuillez noter le risque potentiel de XSS",
|
||||
"autoLaunchMode0": "Ne pas lancer automatiquement",
|
||||
"autoLaunchMode1": "Lancement automatique après le démarrage",
|
||||
"autoLaunchMode2": "Lancement automatique et minimisation de l'interface principale après le démarrage",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "הסר את ה‑ID משמות קבצי המשאבים",
|
||||
"removeAssetsIDTip": "כאשר מופעל, בעת ייצוא ל‑Markdown יוסר חלק ה‑ID משמות קבצי המשאבים",
|
||||
"clearTempFiles": "ניקוי קבצים זמניים",
|
||||
"clearTempFilesTip": "נקה קבצים זמניים שנוצרו במהלך הפעלת היישום כדי לפנות מקום אחסון",
|
||||
"uploadFileTooLarge": "⚠️ הקובץ שהועלה [${x}] גדול מאוד [${y}],אשר האם להמשיך בהעלאה?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "לחץ",
|
||||
"allowHTMLBLockScript": "אפשר הפעלת סקריפטים בתוך בלוקים של HTML",
|
||||
"allowHTMLBLockScriptTip": "לאחר הפעלה, הסקריפט בבלוק HTML לא יחוטא, שים לב לסיכון פוטנציאלי של התקפות XSS",
|
||||
"allowSVGScript": "לאפשר הרצת סקריפטים בתוך SVG",
|
||||
"allowSVGScriptTip": "בהפעלת האפשרות, הקוד בתוך SVG לא יסונן מבחינה בטיחותית — יש לשים לב לסיכון אפשרי של XSS",
|
||||
"autoLaunchMode0": "אל תצא אוטומטית",
|
||||
"autoLaunchMode1": "צא אוטומטית לאחר אתחול",
|
||||
"autoLaunchMode2": "צא אוטומטית ומזער את הממשק העיקרי לאחר אתחול",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "Rimuovi l'ID dai nomi dei file delle risorse",
|
||||
"removeAssetsIDTip": "Se abilitato, durante l'esportazione in Markdown verrà rimossa la parte ID dai nomi dei file delle risorse",
|
||||
"clearTempFiles": "Elimina file temporanei",
|
||||
"clearTempFilesTip": "Elimina i file temporanei creati durante l'esecuzione dell'app per liberare spazio di archiviazione",
|
||||
"uploadFileTooLarge": "⚠️ Il file caricato [${x}] è troppo grande [${y}]. Sei sicuro di voler continuare il caricamento?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "Clicca",
|
||||
"allowHTMLBLockScript": "Consenti l'esecuzione di script nei blocchi HTML",
|
||||
"allowHTMLBLockScriptTip": "Dopo l'abilitazione, lo script nel blocco HTML non verrà sanificato. Si prega di essere consapevoli del potenziale rischio di attacchi XSS.",
|
||||
"allowSVGScript": "Consenti l'esecuzione di script all'interno di SVG",
|
||||
"allowSVGScriptTip": "Se abilitato, il codice all'interno dell'SVG non sarà filtrato per motivi di sicurezza. Prestare attenzione al rischio potenziale di XSS",
|
||||
"autoLaunchMode0": "Non avviare automaticamente",
|
||||
"autoLaunchMode1": "Avvio automatico dopo l'accensione",
|
||||
"autoLaunchMode2": "Avvio automatico e minimizzazione dell'interfaccia principale dopo l'accensione",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "リソースファイル名から ID を削除",
|
||||
"removeAssetsIDTip": "有効にすると Markdown をエクスポートする際にリソースファイル名の ID 部分を削除します",
|
||||
"clearTempFiles": "一時ファイルを削除",
|
||||
"clearTempFilesTip": "アプリの実行中に生成された一時ファイルを削除して、ストレージ領域を解放します",
|
||||
"uploadFileTooLarge": "⚠️ アップロードしたファイル [${x}] は [${y}] で大きすぎます。アップロードを続行しますか?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "クリック",
|
||||
"allowHTMLBLockScript": "HTML ブロック内のスクリプトの実行を許可",
|
||||
"allowHTMLBLockScriptTip": "HTML ブロック内のスクリプトはサニタイズされません。XSS 攻撃の潜在的なリスクに十分注意してください",
|
||||
"allowSVGScript": "SVG 内のスクリプトを実行許可",
|
||||
"allowSVGScriptTip": "有効にすると SVG 内のコードはセキュリティフィルタの対象になりません。潜在的な XSS 攻撃に注意してください",
|
||||
"autoLaunchMode0": "自動的に起動しない",
|
||||
"autoLaunchMode1": "システムの起動後に自動的に起動する",
|
||||
"autoLaunchMode2": "システムの起動後にインターフェースを最小化して自動的に起動する",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "리소스 파일 이름의 ID 제거",
|
||||
"removeAssetsIDTip": "활성화하면 Markdown 내보내기 시 리소스 파일 이름의 ID 부분을 제거합니다",
|
||||
"clearTempFiles": "임시 파일 정리",
|
||||
"clearTempFilesTip": "앱 실행 중 생성된 임시 파일을 정리하여 저장 공간을 확보합니다",
|
||||
"uploadFileTooLarge": "⚠️ 업로드한 파일 [${x}]의 크기가 [${y}]로 너무 큽니다. 계속 업로드하시겠습니까?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "클릭",
|
||||
"allowHTMLBLockScript": "HTML 블록 내 스크립트 실행 허용",
|
||||
"allowHTMLBLockScriptTip": "활성화하면 HTML 블록의 스크립트가 삭제되지 않습니다. XSS 공격의 잠재적 위험에 주의하세요",
|
||||
"allowSVGScript": "SVG 내부 스크립트 실행 허용",
|
||||
"allowSVGScriptTip": "활성화하면 SVG 내의 코드는 보안 필터링 대상이 되지 않습니다。잠재적 XSS 공격에 주의하세요",
|
||||
"autoLaunchMode0": "자동으로 시작하지 않음",
|
||||
"autoLaunchMode1": "부팅 후 자동 시작",
|
||||
"autoLaunchMode2": "부팅 후 자동 시작 및 메인 인터페이스 최소화",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "Usuń ID z nazw plików zasobów",
|
||||
"removeAssetsIDTip": "Po włączeniu podczas eksportu do Markdown zostanie usunięta część nazwy pliku zasobu zawierająca ID",
|
||||
"clearTempFiles": "Wyczyść pliki tymczasowe",
|
||||
"clearTempFilesTip": "Wyczyść pliki tymczasowe tworzone podczas działania aplikacji, aby zwolnić miejsce na dysku",
|
||||
"uploadFileTooLarge": "⚠️ Przesłany plik [${x}] jest za duży [${y}]. Czy na pewno chcesz kontynuować przesyłanie?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "Kliknij",
|
||||
"allowHTMLBLockScript": "Zezwalaj na wykonywanie skryptów w blokach HTML",
|
||||
"allowHTMLBLockScriptTip": "Po włączeniu skrypt w bloku HTML nie będzie czyszczony, proszę być świadomym potencjalnego ryzyka ataków XSS",
|
||||
"allowSVGScript": "Zezwól na wykonywanie skryptów w SVG",
|
||||
"allowSVGScriptTip": "Po włączeniu kod w SVG nie będzie filtrowany pod kątem bezpieczeństwa — uważaj na potencjalne ataki XSS",
|
||||
"autoLaunchMode0": "Nie uruchamiaj automatycznie",
|
||||
"autoLaunchMode1": "Uruchom automatycznie po włączeniu",
|
||||
"autoLaunchMode2": "Uruchom automatycznie i zminimalizuj główny interfejs po włączeniu",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "Remover o ID dos nomes dos arquivos de recursos",
|
||||
"removeAssetsIDTip": "Ao ativar, ao exportar para Markdown a parte do nome do arquivo de recursos contendo o ID será removida",
|
||||
"clearTempFiles": "Limpar arquivos temporários",
|
||||
"clearTempFilesTip": "Limpe os arquivos temporários gerados durante a execução do aplicativo para liberar espaço de armazenamento",
|
||||
"uploadFileTooLarge": "⚠️ O arquivo enviado [${x}] é muito grande [${y}]。 Tem certeza de que deseja continuar o upload?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "Clique",
|
||||
"allowHTMLBLockScript": "Permitir execução de scripts dentro de blocos HTML",
|
||||
"allowHTMLBLockScriptTip": "Quando ativado, o script no bloco HTML não será sanitizado, esteja ciente do risco potencial de ataques XSS",
|
||||
"allowSVGScript": "Permitir execução de scripts dentro de SVG",
|
||||
"allowSVGScriptTip": "Ao ativar, o código dentro do SVG não será filtrado por segurança。Atenção ao risco potencial de XSS",
|
||||
"autoLaunchMode0": "Não iniciar automaticamente",
|
||||
"autoLaunchMode1": "Iniciar automaticamente após inicialização",
|
||||
"autoLaunchMode2": "Iniciar automaticamente e minimizar a interface principal após inicialização",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "Удалить ID из имени файла ресурса",
|
||||
"removeAssetsIDTip": "При включении при экспорте в Markdown из имён файлов ресурсов будет удалена часть с ID",
|
||||
"clearTempFiles": "Очистить временные файлы",
|
||||
"clearTempFilesTip": "Очистите временные файлы, созданные при работе приложения, чтобы освободить место в хранилище",
|
||||
"uploadFileTooLarge": "⚠️ Загруженный файл [${x}] очень большой [${y}]. Вы уверены, что хотите продолжить загрузку?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "Клик",
|
||||
"allowHTMLBLockScript": "Разрешить выполнение скриптов внутри HTML блоков",
|
||||
"allowHTMLBLockScriptTip": "После включения скрипт в HTML блоке не будет очищен, Пожалуйста, имейте в виду потенциальный риск XSS-атак",
|
||||
"allowSVGScript": "Разрешить выполнение скриптов в SVG",
|
||||
"allowSVGScriptTip": "При включении код внутри SVG не будет проходить фильтрацию безопасности — будьте внимательны к потенциальным XSS-уязвимостям",
|
||||
"autoLaunchMode0": "Не запускать автоматически",
|
||||
"autoLaunchMode1": "Автозапуск после загрузки",
|
||||
"autoLaunchMode2": "Автозапуск и минимизация главного интерфейса после загрузки",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "Varlık dosya adlarındaki ID'yi kaldır",
|
||||
"removeAssetsIDTip": "Etkinleştirildiğinde Markdown dışa aktarılırken varlık dosya adlarındaki ID kısmı kaldırılacaktır",
|
||||
"clearTempFiles": "Geçici dosyaları temizle",
|
||||
"clearTempFilesTip": "Uygulama çalışırken oluşan geçici dosyaları temizleyerek depolama alanını boşalt",
|
||||
"uploadFileTooLarge": "⚠️ Yüklenen dosya [${x}], boyutu [${y}] kadar büyük. Yüklemeye devam etmek istediğinize emin misiniz?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "Tıkla",
|
||||
"allowHTMLBLockScript": "HTML bloklarındaki betiklerin çalıştırılmasına izin ver",
|
||||
"allowHTMLBLockScriptTip": "Etkinleştirildiğinde, HTML bloğundaki betikler filtrelenmez. XSS saldırısı riski olduğunu unutma",
|
||||
"allowSVGScript": "SVG içindeki betiklerin çalıştırılmasına izin ver",
|
||||
"allowSVGScriptTip": "Etkinleştirildiğinde SVG içindeki kod güvenlik filtresinden geçirilmez, potansiyel XSS saldırılarına karşı dikkatli olun",
|
||||
"autoLaunchMode0": "Otomatik başlatma",
|
||||
"autoLaunchMode1": "Açılışta otomatik başlat",
|
||||
"autoLaunchMode2": "Açılışta otomatik başlat ve ana arayüzü küçült",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "移除資源檔名中的 ID",
|
||||
"removeAssetsIDTip": "啟用後在匯出 Markdown 時會移除資源檔名中的 ID 部分",
|
||||
"clearTempFiles": "清理臨時檔案",
|
||||
"clearTempFilesTip": "清理應用運行過程中產生的臨時檔案以釋放儲存空間",
|
||||
"uploadFileTooLarge": "⚠️ 上傳的檔案 [${x}] 很大 [${y}],請確認是否繼續上傳?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "點擊",
|
||||
"allowHTMLBLockScript": "允許執行 HTML 塊內腳本",
|
||||
"allowHTMLBLockScriptTip": "啟用後將不對 HTML 塊中的程式碼進行安全過濾,請注意潛在的 XSS 攻擊風險",
|
||||
"allowSVGScript": "允許執行 SVG 內腳本",
|
||||
"allowSVGScriptTip": "啟用後將不對 SVG 中的程式碼進行安全過濾,請注意潛在的 XSS 攻擊風險",
|
||||
"autoLaunchMode0": "不自動啟動",
|
||||
"autoLaunchMode1": "開機自動啟動",
|
||||
"autoLaunchMode2": "開機後自動啟動並最小化主介面",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"removeAssetsID": "移除资源文件名中的 ID",
|
||||
"removeAssetsIDTip": "启用后在导出 Markdown 时将移除资源文件名中的 ID 部分",
|
||||
"clearTempFiles": "清理临时文件",
|
||||
"clearTempFilesTip": "清理应用运行过程中产生的临时文件以释放存储空间",
|
||||
"uploadFileTooLarge": "⚠️ 上传的文件 [${x}] 很大 [${y}],请确认是否继续上传?",
|
||||
|
|
@ -128,6 +130,8 @@
|
|||
"click": "点击",
|
||||
"allowHTMLBLockScript": "允许执行 HTML 块内脚本",
|
||||
"allowHTMLBLockScriptTip": "启用后将不对 HTML 块中的代码进行安全过滤,请注意潜在的 XSS 攻击风险",
|
||||
"allowSVGScript": "允许执行 SVG 内脚本",
|
||||
"allowSVGScriptTip": "启用后将不对 SVG 中的代码进行安全过滤,请注意潜在的 XSS 攻击风险",
|
||||
"autoLaunchMode0": "不自动启动",
|
||||
"autoLaunchMode1": "开机后自动启动",
|
||||
"autoLaunchMode2": "开机后自动启动并最小化主界面",
|
||||
|
|
|
|||
|
|
@ -305,6 +305,14 @@ export const editor = {
|
|||
<textarea class="b3-text-field fn__block" id="katexMacros" spellcheck="false">${window.siyuan.config.editor.katexMacros}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<label class="fn__flex b3-label">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.allowSVGScript}
|
||||
<div class="b3-label__text">${window.siyuan.languages.allowSVGScriptTip}</div>
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="allowSVGScript" type="checkbox"${window.siyuan.config.editor.allowSVGScript ? " checked" : ""}/>
|
||||
</label>
|
||||
<label class="fn__flex b3-label">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.allowHTMLBLockScript}
|
||||
|
|
@ -470,6 +478,7 @@ export const editor = {
|
|||
inlineStrikethrough: (editor.element.querySelector("#editorMarkdownInlineStrikethrough") as HTMLInputElement).checked,
|
||||
inlineMark: (editor.element.querySelector("#editorMarkdownInlineMark") as HTMLInputElement).checked
|
||||
},
|
||||
allowSVGScript: (editor.element.querySelector("#allowSVGScript") as HTMLInputElement).checked,
|
||||
allowHTMLBLockScript: (editor.element.querySelector("#allowHTMLBLockScript") as HTMLInputElement).checked,
|
||||
justify: (editor.element.querySelector("#justify") as HTMLInputElement).checked,
|
||||
rtl: (editor.element.querySelector("#rtl") as HTMLInputElement).checked,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,14 @@ export const exportConfig = {
|
|||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="markdownYFM" type="checkbox"${window.siyuan.config.export.markdownYFM ? " checked" : ""}/>
|
||||
</label>
|
||||
<label class="fn__flex b3-label">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.removeAssetsID}
|
||||
<div class="b3-label__text">${window.siyuan.languages.removeAssetsIDTip}</div>
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="removeAssetsID" type="checkbox"${window.siyuan.config.export.removeAssetsID ? " checked" : ""}/>
|
||||
</label>
|
||||
<label class="fn__flex b3-label">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.export31}
|
||||
|
|
@ -207,6 +215,7 @@ export const exportConfig = {
|
|||
fetchPost("/api/setting/setExport", {
|
||||
paragraphBeginningSpace: (exportConfig.element.querySelector("#paragraphBeginningSpace") as HTMLInputElement).checked,
|
||||
addTitle: (exportConfig.element.querySelector("#addTitle") as HTMLInputElement).checked,
|
||||
removeAssetsID: (exportConfig.element.querySelector("#removeAssetsID") as HTMLInputElement).checked,
|
||||
markdownYFM: (exportConfig.element.querySelector("#markdownYFM") as HTMLInputElement).checked,
|
||||
inlineMemo: (exportConfig.element.querySelector("#inlineMemo") as HTMLInputElement).checked,
|
||||
blockRefMode: parseInt((exportConfig.element.querySelector("#blockRefMode") as HTMLSelectElement).value, 10),
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export const initConfigSearch = (element: HTMLElement, app: App) => {
|
|||
"editorMarkdownInlineTag", "editorMarkdownInlineTagTip", "editorMarkdownInlineMath", "editorMarkdownInlineMathTip",
|
||||
"editorMarkdownInlineStrikethrough", "editorMarkdownInlineStrikethroughTip", "editorMarkdownInlineMark", "editorMarkdownInlineMarkTip",
|
||||
"allowHTMLBLockScript", "allowHTMLBLockScriptTip", "backlinkExpandTip", "backmentionExpandTip",
|
||||
"backlinkContainChildren", "backlinkContainChildrenTip"
|
||||
"backlinkContainChildren", "backlinkContainChildrenTip", "allowSVGScript", "allowSVGScriptTip"
|
||||
]),
|
||||
|
||||
// 文档树
|
||||
|
|
@ -56,7 +56,7 @@ export const initConfigSearch = (element: HTMLElement, app: App) => {
|
|||
// 导出
|
||||
getLang(["paragraphBeginningSpace", "md4", "export", "export1", "export2", "export5", "export11",
|
||||
"export13", "export14", "export15", "export19", "export20", "ref", "blockEmbed", "export17", "export18",
|
||||
"export23", "export24", "export25", "export26", "export27", "export28", "export29"]),
|
||||
"export23", "export24", "export25", "export26", "export27", "export28", "export29", "removeAssetsID", "removeAssetsIDTip"]),
|
||||
|
||||
// 外观
|
||||
getLang(["language", "language1", "appearance", "appearance1", "appearance2", "appearance3", "appearance4",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {FileFilter, ipcRenderer} from "electron";
|
|||
import * as path from "path";
|
||||
/// #endif
|
||||
import {MenuItem} from "./Menu";
|
||||
import {getDisplayName, getNotebookName, getTopPaths, useShell, pathPosix} from "../util/pathName";
|
||||
import {getDisplayName, getNotebookName, getTopPaths, pathPosix, useShell} from "../util/pathName";
|
||||
import {hideMessage, showMessage} from "../dialog/message";
|
||||
import {fetchPost, fetchSyncPost} from "../util/fetch";
|
||||
import {onGetnotebookconf} from "./onGetnotebookconf";
|
||||
|
|
@ -158,6 +158,19 @@ const initMultiMenu = (selectItemElements: NodeListOf<Element>, app: App) => {
|
|||
type: "submenu",
|
||||
icon: "iconUpload",
|
||||
submenu: [{
|
||||
id: "exportSiYuanZip",
|
||||
label: "SiYuan .sy.zip",
|
||||
icon: "iconSiYuan",
|
||||
click: () => {
|
||||
const msgId = showMessage(window.siyuan.languages.exporting, -1);
|
||||
fetchPost("/api/export/exportSYs", {
|
||||
ids: blockIDs,
|
||||
}, response => {
|
||||
hideMessage(msgId);
|
||||
openByMobile(response.data.zip);
|
||||
});
|
||||
}
|
||||
}, {
|
||||
id: "exportMarkdown",
|
||||
label: "Markdown .zip",
|
||||
icon: "iconMarkdown",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ const setEditor = (modelMainElement: Element) => {
|
|||
inlineStrikethrough: (modelMainElement.querySelector("#editorMarkdownInlineStrikethrough") as HTMLInputElement).checked,
|
||||
inlineMark: (modelMainElement.querySelector("#editorMarkdownInlineMark") as HTMLInputElement).checked
|
||||
};
|
||||
window.siyuan.config.editor.allowSVGScript = (modelMainElement.querySelector("#allowSVGScript") as HTMLInputElement).checked;
|
||||
window.siyuan.config.editor.allowHTMLBLockScript = (modelMainElement.querySelector("#allowHTMLBLockScript") as HTMLInputElement).checked;
|
||||
window.siyuan.config.editor.dynamicLoadBlocks = dynamicLoadBlocks;
|
||||
window.siyuan.config.editor.justify = (modelMainElement.querySelector("#justify") as HTMLInputElement).checked;
|
||||
|
|
@ -279,6 +280,14 @@ export const initEditor = () => {
|
|||
<textarea class="b3-text-field fn__block" id="katexMacros">${window.siyuan.config.editor.katexMacros}</textarea>
|
||||
<div class="b3-label__text">${window.siyuan.languages.katexMacrosTip}</div>
|
||||
</div>
|
||||
<label class="fn__flex b3-label">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.allowSVGScript}
|
||||
<div class="b3-label__text">${window.siyuan.languages.allowSVGScriptTip}</div>
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="allowSVGScript" type="checkbox"${window.siyuan.config.editor.allowSVGScript ? " checked" : ""}/>
|
||||
</label>
|
||||
<label class="fn__flex b3-label">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.allowHTMLBLockScript}
|
||||
|
|
|
|||
|
|
@ -509,6 +509,7 @@ ${getIconScript(servePath)}
|
|||
config: {
|
||||
appearance: { mode: 0, codeBlockThemeDark: "${window.siyuan.config.appearance.codeBlockThemeDark}", codeBlockThemeLight: "${window.siyuan.config.appearance.codeBlockThemeLight}" },
|
||||
editor: {
|
||||
allowSVGScriptTip: ${window.siyuan.config.editor.allowSVGScript},
|
||||
allowHTMLBLockScript: ${window.siyuan.config.editor.allowHTMLBLockScript},
|
||||
fontSize: ${window.siyuan.config.editor.fontSize},
|
||||
codeLineWrap: true,
|
||||
|
|
|
|||
9
app/src/types/config.d.ts
vendored
9
app/src/types/config.d.ts
vendored
|
|
@ -350,6 +350,11 @@ declare namespace Config {
|
|||
*/
|
||||
export interface IEditor {
|
||||
|
||||
/**
|
||||
* Whether to allow to execute javascript in the SVG
|
||||
*/
|
||||
allowSVGScript: boolean;
|
||||
|
||||
/**
|
||||
* Whether to allow to execute javascript in the HTML block
|
||||
*/
|
||||
|
|
@ -569,6 +574,10 @@ declare namespace Config {
|
|||
* Whether to add YAML Front Matter when exporting to Markdown
|
||||
*/
|
||||
markdownYFM: boolean;
|
||||
/**
|
||||
* Whether to remove the asset ID when exporting to Markdown
|
||||
*/
|
||||
removeAssetsID: boolean;
|
||||
/**
|
||||
* Whether to export the inline memo
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -406,6 +406,27 @@ func exportNotebookSY(c *gin.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
func exportSYs(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
idsArg := arg["ids"].([]interface{})
|
||||
var ids []string
|
||||
for _, id := range idsArg {
|
||||
ids = append(ids, id.(string))
|
||||
}
|
||||
|
||||
zipPath := model.ExportSYs(ids)
|
||||
ret.Data = map[string]interface{}{
|
||||
"zip": zipPath,
|
||||
}
|
||||
}
|
||||
|
||||
func exportSY(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
|
@ -416,10 +437,9 @@ func exportSY(c *gin.Context) {
|
|||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
name, zipPath := model.ExportSY(id)
|
||||
zipPath := model.ExportSYs([]string{id})
|
||||
ret.Data = map[string]interface{}{
|
||||
"name": name,
|
||||
"zip": zipPath,
|
||||
"zip": zipPath,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -315,6 +315,7 @@ func ServeAPI(ginServer *gin.Engine) {
|
|||
ginServer.Handle("POST", "/api/export/exportNotebookMd", model.CheckAuth, model.CheckAdminRole, exportNotebookMd)
|
||||
ginServer.Handle("POST", "/api/export/exportMds", model.CheckAuth, model.CheckAdminRole, exportMds)
|
||||
ginServer.Handle("POST", "/api/export/exportMd", model.CheckAuth, model.CheckAdminRole, exportMd)
|
||||
ginServer.Handle("POST", "/api/export/exportSYs", model.CheckAuth, model.CheckAdminRole, exportSYs)
|
||||
ginServer.Handle("POST", "/api/export/exportSY", model.CheckAuth, model.CheckAdminRole, exportSY)
|
||||
ginServer.Handle("POST", "/api/export/exportNotebookSY", model.CheckAuth, model.CheckAdminRole, exportNotebookSY)
|
||||
ginServer.Handle("POST", "/api/export/exportMdContent", model.CheckAuth, model.CheckAdminRole, exportMdContent)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package conf
|
|||
import "github.com/siyuan-note/siyuan/kernel/util"
|
||||
|
||||
type Editor struct {
|
||||
AllowSVGScript bool `json:"allowSVGScript"` // 允许执行 SVG 内脚本
|
||||
AllowHTMLBLockScript bool `json:"allowHTMLBLockScript"` // 允许执行 HTML 块内脚本
|
||||
FontSize int `json:"fontSize"` // 字体大小
|
||||
FontSizeScrollZoom bool `json:"fontSizeScrollZoom"` // 字体大小是否支持滚轮缩放
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ type Export struct {
|
|||
TagCloseMarker string `json:"tagCloseMarker"` // 标签结束标记符,默认是 #
|
||||
FileAnnotationRefMode int `json:"fileAnnotationRefMode"` // 文件标注引用导出模式,0:文件名 - 页码 - 锚文本,1:仅锚文本
|
||||
PandocBin string `json:"pandocBin"` // Pandoc 可执行文件路径
|
||||
RemoveAssetsID bool `json:"removeAssetsID"` // Markdown 导出时是否移除资源文件名 ID 部分 https://github.com/siyuan-note/siyuan/issues/16065
|
||||
MarkdownYFM bool `json:"markdownYFM"` // Markdown 导出时是否添加 YAML Front Matter https://github.com/siyuan-note/siyuan/issues/7727
|
||||
InlineMemo bool `json:"inlineMemo"` // 是否导出行级备注 https://github.com/siyuan-note/siyuan/issues/14605
|
||||
PDFFooter string `json:"pdfFooter"` // PDF 导出时页脚内容
|
||||
|
|
@ -55,6 +56,7 @@ func NewExport() *Export {
|
|||
TagCloseMarker: "#",
|
||||
FileAnnotationRefMode: 0,
|
||||
PandocBin: "",
|
||||
RemoveAssetsID: false,
|
||||
MarkdownYFM: false,
|
||||
InlineMemo: false,
|
||||
PDFFooter: "%page / %pages",
|
||||
|
|
|
|||
|
|
@ -1353,6 +1353,23 @@ func getAssetsLinkDests(node *ast.Node, includeServePath bool) (ret []string) {
|
|||
return
|
||||
}
|
||||
|
||||
func getAssetsLinkDestsInTree(tree *parse.Tree, includeServePath bool) (nodes []*ast.Node) {
|
||||
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||
if !entering {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
|
||||
dests := getAssetsLinkDests(n, includeServePath)
|
||||
if 1 > len(dests) {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
|
||||
nodes = append(nodes, n)
|
||||
return ast.WalkContinue
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func setAssetsLinkDest(node *ast.Node, oldDest, dest string) {
|
||||
if ast.NodeLinkDest == node.Type {
|
||||
if bytes.HasPrefix(node.Tokens, []byte("//")) {
|
||||
|
|
|
|||
|
|
@ -451,27 +451,24 @@ func ExportNotebookSY(id string) (zipPath string) {
|
|||
return
|
||||
}
|
||||
|
||||
func ExportSY(id string) (name, zipPath string) {
|
||||
block := treenode.GetBlockTree(id)
|
||||
if nil == block {
|
||||
logging.LogErrorf("not found block [%s]", id)
|
||||
return
|
||||
}
|
||||
|
||||
boxID := block.BoxID
|
||||
box := Conf.Box(boxID)
|
||||
func ExportSYs(ids []string) (zipPath string) {
|
||||
block := treenode.GetBlockTree(ids[0])
|
||||
box := Conf.Box(block.BoxID)
|
||||
baseFolderName := path.Base(block.HPath)
|
||||
if "." == baseFolderName {
|
||||
baseFolderName = path.Base(block.Path)
|
||||
}
|
||||
rootPath := block.Path
|
||||
docPaths := []string{rootPath}
|
||||
docFiles := box.ListFiles(strings.TrimSuffix(block.Path, ".sy"))
|
||||
for _, docFile := range docFiles {
|
||||
docPaths = append(docPaths, docFile.path)
|
||||
|
||||
var docPaths []string
|
||||
bts := treenode.GetBlockTrees(ids)
|
||||
for _, bt := range bts {
|
||||
docPaths = append(docPaths, bt.Path)
|
||||
docFiles := box.ListFiles(strings.TrimSuffix(bt.Path, ".sy"))
|
||||
for _, docFile := range docFiles {
|
||||
docPaths = append(docPaths, docFile.path)
|
||||
}
|
||||
}
|
||||
zipPath = exportSYZip(boxID, path.Dir(rootPath), baseFolderName, docPaths)
|
||||
name = util.GetTreeID(block.Path)
|
||||
zipPath = exportSYZip(block.BoxID, path.Dir(block.Path), baseFolderName, docPaths)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -1452,7 +1449,9 @@ func processPDFLinkEmbedAssets(pdfCtx *model.Context, assetDests []string, remov
|
|||
now := types.StringLiteral(types.DateString(time.Now()))
|
||||
for _, link := range assetLinks {
|
||||
link.URI = strings.ReplaceAll(link.URI, "http://"+util.LocalHost+":"+util.ServerPort+"/export/temp/", "")
|
||||
link.URI = strings.ReplaceAll(link.URI, "http://"+util.LocalHost+":6806/export/temp/", "")
|
||||
link.URI = strings.ReplaceAll(link.URI, "http://"+util.LocalHost+":"+util.ServerPort+"/", "") // Exporting PDF embedded asset files as attachments fails https://github.com/siyuan-note/siyuan/issues/7414#issuecomment-1704573557
|
||||
link.URI = strings.ReplaceAll(link.URI, "http://"+util.LocalHost+":6806/", "")
|
||||
link.URI, _ = url.PathUnescape(link.URI)
|
||||
if idx := strings.Index(link.URI, "?"); 0 < idx {
|
||||
link.URI = link.URI[:idx]
|
||||
|
|
@ -2840,6 +2839,10 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
|
|||
img.AppendChild(&ast.Node{Type: ast.NodeLinkDest, Tokens: []byte(a.Content)})
|
||||
img.AppendChild(&ast.Node{Type: ast.NodeCloseParen})
|
||||
mdTableCell.AppendChild(img)
|
||||
height := "height: 128px;"
|
||||
spanIAL := &ast.Node{Type: ast.NodeKramdownSpanIAL, Tokens: []byte("style=\"" + height + "\"")}
|
||||
mdTableCell.AppendChild(spanIAL)
|
||||
img.SetIALAttr("style", height)
|
||||
} else if av.AssetTypeFile == a.Type {
|
||||
linkText := strings.TrimSpace(a.Name)
|
||||
if "" == linkText {
|
||||
|
|
@ -3323,6 +3326,7 @@ func exportPandocConvertZip(baseFolderName string, docPaths, defBlockIDs []strin
|
|||
return
|
||||
}
|
||||
|
||||
assetsOldNew, assetsNewOld := map[string]string{}, map[string]string{}
|
||||
luteEngine := util.NewLute()
|
||||
for i, p := range docPaths {
|
||||
id := util.GetTreeID(p)
|
||||
|
|
@ -3360,31 +3364,42 @@ func exportPandocConvertZip(baseFolderName string, docPaths, defBlockIDs []strin
|
|||
|
||||
// 解析导出后的标准 Markdown,汇总 assets
|
||||
tree = parse.Parse("", gulu.Str.ToBytes(md), luteEngine.ParseOptions)
|
||||
var assets []string
|
||||
assets = append(assets, getAssetsLinkDests(tree.Root, false)...)
|
||||
for _, asset := range assets {
|
||||
asset = string(html.DecodeDestination([]byte(asset)))
|
||||
if strings.Contains(asset, "?") {
|
||||
asset = asset[:strings.LastIndex(asset, "?")]
|
||||
removeAssetsID(tree, assetsOldNew, assetsNewOld)
|
||||
|
||||
newAssets := getAssetsLinkDests(tree.Root, false)
|
||||
for _, newAsset := range newAssets {
|
||||
newAsset = string(html.DecodeDestination([]byte(newAsset)))
|
||||
if strings.Contains(newAsset, "?") {
|
||||
newAsset = newAsset[:strings.LastIndex(newAsset, "?")]
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(asset, "assets/") {
|
||||
if !strings.HasPrefix(newAsset, "assets/") {
|
||||
continue
|
||||
}
|
||||
|
||||
srcPath := assetsPathMap[asset]
|
||||
oldAsset := assetsNewOld[newAsset]
|
||||
if "" == oldAsset {
|
||||
logging.LogWarnf("get asset old path for new asset [%s] failed", newAsset)
|
||||
continue
|
||||
}
|
||||
|
||||
srcPath := assetsPathMap[oldAsset]
|
||||
if "" == srcPath {
|
||||
logging.LogWarnf("get asset [%s] abs path failed", asset)
|
||||
logging.LogWarnf("get asset [%s] abs path failed", oldAsset)
|
||||
continue
|
||||
}
|
||||
|
||||
destPath := filepath.Join(writeFolder, asset)
|
||||
destPath := filepath.Join(writeFolder, newAsset)
|
||||
if copyErr := filelock.Copy(srcPath, destPath); copyErr != nil {
|
||||
logging.LogErrorf("copy asset from [%s] to [%s] failed: %s", srcPath, destPath, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
for assetsOld, assetsNew := range assetsOldNew {
|
||||
md = strings.ReplaceAll(md, assetsOld, assetsNew)
|
||||
}
|
||||
|
||||
// 调用 Pandoc 进行格式转换
|
||||
pandocErr := util.Pandoc(pandocFrom, pandocTo, writePath, md)
|
||||
if pandocErr != nil {
|
||||
|
|
@ -3436,6 +3451,47 @@ func exportPandocConvertZip(baseFolderName string, docPaths, defBlockIDs []strin
|
|||
return
|
||||
}
|
||||
|
||||
func removeAssetsID(tree *parse.Tree, assetsOldNew, assetsNewOld map[string]string) {
|
||||
assetNodes := getAssetsLinkDestsInTree(tree, false)
|
||||
for _, node := range assetNodes {
|
||||
dests := getAssetsLinkDests(node, false)
|
||||
if 1 > len(dests) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, dest := range dests {
|
||||
if !Conf.Export.RemoveAssetsID {
|
||||
assetsOldNew[dest] = dest
|
||||
assetsNewOld[dest] = dest
|
||||
continue
|
||||
}
|
||||
|
||||
if newDest := assetsOldNew[dest]; "" != newDest {
|
||||
setAssetsLinkDest(node, dest, newDest)
|
||||
continue
|
||||
}
|
||||
|
||||
name := path.Base(dest)
|
||||
name = util.RemoveID(name)
|
||||
newDest := "assets/" + name
|
||||
if existOld := assetsNewOld[newDest]; "" != existOld {
|
||||
if existOld == dest { // 已存在相同资源路径
|
||||
setAssetsLinkDest(node, dest, newDest)
|
||||
} else {
|
||||
// 存在同名但内容不同的资源文件,保留 ID
|
||||
assetsNewOld[dest] = dest
|
||||
assetsOldNew[dest] = dest
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
setAssetsLinkDest(node, dest, newDest)
|
||||
assetsOldNew[dest] = newDest
|
||||
assetsNewOld[newDest] = dest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getExportBlockRefLinkText(blockRef *ast.Node, blockRefTextLeft, blockRefTextRight string) (defID, linkText string) {
|
||||
defID, linkText, _ = treenode.GetBlockRef(blockRef)
|
||||
if "" == linkText {
|
||||
|
|
|
|||
|
|
@ -545,8 +545,7 @@ func serveAssets(ginServer *gin.Engine) {
|
|||
}
|
||||
}
|
||||
|
||||
if serveThumbnail(context, p, requestPath) {
|
||||
// 如果请求缩略图服务成功则返回
|
||||
if serveThumbnail(context, p, requestPath) || serveSVG(context, p) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -562,6 +561,24 @@ func serveAssets(ginServer *gin.Engine) {
|
|||
})
|
||||
}
|
||||
|
||||
func serveSVG(context *gin.Context, assetAbsPath string) bool {
|
||||
if strings.HasSuffix(assetAbsPath, ".svg") {
|
||||
data, err := os.ReadFile(assetAbsPath)
|
||||
if err != nil {
|
||||
logging.LogErrorf("read svg file failed: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if !model.Conf.Editor.AllowSVGScript {
|
||||
data = []byte(util.RemoveScriptsInSVG(string(data)))
|
||||
}
|
||||
|
||||
context.Data(200, "image/svg+xml", data)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func serveThumbnail(context *gin.Context, assetAbsPath, requestPath string) bool {
|
||||
if style := context.Query("style"); style == "thumb" && model.NeedGenerateAssetsThumbnail(assetAbsPath) { // 请求缩略图
|
||||
thumbnailPath := filepath.Join(util.TempDir, "thumbnails", "assets", requestPath)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"unicode"
|
||||
|
||||
"github.com/88250/lute/html"
|
||||
"github.com/siyuan-note/logging"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -220,3 +221,52 @@ func ReplaceStr(strs []string, old, new string) (ret []string, changed bool) {
|
|||
ret = strs
|
||||
return
|
||||
}
|
||||
|
||||
// RemoveScriptsInSVG 移除 SVG 中的 <script> 标签及其内部所有内容
|
||||
func RemoveScriptsInSVG(svgInput string) string {
|
||||
// 1. 将字符串解析为节点树
|
||||
doc, err := html.Parse(strings.NewReader(svgInput))
|
||||
if err != nil {
|
||||
logging.LogWarnf("parse svg failed: %v", err)
|
||||
return svgInput
|
||||
}
|
||||
|
||||
// 2. 定义递归移除逻辑
|
||||
var walk func(*html.Node)
|
||||
walk = func(n *html.Node) {
|
||||
// 倒序遍历子节点,确保删除操作不影响后续迭代
|
||||
for c := n.FirstChild; c != nil; {
|
||||
next := c.NextSibling
|
||||
// 检查标签名是否为 script
|
||||
if c.Type == html.ElementNode && strings.EqualFold(c.Data, "script") {
|
||||
n.RemoveChild(c)
|
||||
} else {
|
||||
// 递归处理子节点
|
||||
walk(c)
|
||||
}
|
||||
c = next
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 执行移除
|
||||
walk(doc)
|
||||
|
||||
// 4. 将处理后的树重新渲染回字符串
|
||||
var buf bytes.Buffer
|
||||
if err = html.Render(&buf, doc); err != nil {
|
||||
logging.LogWarnf("render svg failed: %v", err)
|
||||
return svgInput
|
||||
}
|
||||
|
||||
// 5. 提取 SVG 部分 (html.Render 会自动加上 <html><body> 标签)
|
||||
return extractSVG(buf.String())
|
||||
}
|
||||
|
||||
func extractSVG(fullHTML string) string {
|
||||
start := strings.Index(fullHTML, "<svg")
|
||||
end := strings.LastIndex(fullHTML, "</svg>")
|
||||
if start == -1 || end == -1 {
|
||||
return fullHTML
|
||||
}
|
||||
return fullHTML[start : end+6]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue