diff --git a/assets/css/_page/_single/_code.scss b/assets/css/_page/_single/_code.scss index 4fb7d5a2..29fcb6c0 100644 --- a/assets/css/_page/_single/_code.scss +++ b/assets/css/_page/_single/_code.scss @@ -237,6 +237,12 @@ pre { color: fixit-var(global-link-hover-color); } + .download-btn[data-downloaded] .fa-download { + --fa: "\f1ce"; + font-weight: 900; + color: fixit-var(global-link-hover-color); + } + @each $type, $text in $code-type-map { &.language-#{to-lower-case($type)} { // e.g., --fi-code-type: "JavaScript"; diff --git a/assets/js/theme.js b/assets/js/theme.js index e46511dd..68667aaa 100644 --- a/assets/js/theme.js +++ b/assets/js/theme.js @@ -459,6 +459,33 @@ class FixIt { }, false); } + initDownloadCode(codeBlock, codePreEl) { + const downloadBtn = codeBlock.querySelector('.code-header .download-btn'); + if (!downloadBtn) return; + downloadBtn.addEventListener('click', () => { + const $codeHeader = codeBlock.querySelector('.code-header'); + const fileNameFromTitle = $codeHeader?.querySelector('.code-title')?.dataset.name?.trim(); + const language = Array.from($codeHeader?.classList || []).find((className) => className.startsWith('language-'))?.replace('language-', ''); + const fallbackName = language && language !== 'fallback' ? `code.${language}` : 'code.txt'; + const fileName = (fileNameFromTitle || fallbackName).replace(/[\\/:*?"<>|\r\n]+/g, '-'); + const blob = new Blob([codePreEl.innerText], { type: 'text/plain;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = fileName || fallbackName; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + downloadBtn.toggleAttribute('data-downloaded', true); + downloadBtn.classList.toggle('fa-spin', true); + setTimeout(() => { + downloadBtn.toggleAttribute('data-downloaded', false); + downloadBtn.classList.toggle('fa-spin', false); + }, 300); + }, false); + } + /** * init code wrapper */ @@ -477,6 +504,7 @@ class FixIt { if ($codeBlock.dataset.mode === 'classic') { const $codeHeader = $codeBlock.querySelector('.code-header'); if (!$codeHeader) return; + this.initDownloadCode($codeBlock, $codePreEl); // code title $codeHeader.querySelector('.code-title').addEventListener('click', () => { $codeBlock.classList.toggle('is-collapsed'); diff --git a/hugo.toml b/hugo.toml index f831344e..9748f7a1 100644 --- a/hugo.toml +++ b/hugo.toml @@ -1144,6 +1144,8 @@ wrapperClass = "" maxShownLines = 10 # whether to enable code copy button copyable = true +# [classic] whether to enable code download button in the code block header +downloadable = false # [classic] whether to enable line numbers toggle button in the code block header lineNosToggler = true # [classic] whether to enable toggle line wrapping button in the code block header diff --git a/i18n/de.toml b/i18n/de.toml index 35f8cee9..f4a62e62 100644 --- a/i18n/de.toml +++ b/i18n/de.toml @@ -102,6 +102,7 @@ copiedText = "Kopiert" toggleLineNumbers = "Zeilennummern umschalten" toggleLineWrap = "Zeilenumbruch umschalten" toggleCodeEditable = "Codeblock editierbar umschalten" +downloadCode = "Code herunterladen" expandCode = "Codeblock erweitern oder reduzieren" cookieconsentMessage = "Diese Website verwendet Cookies, um Ihre Erfahrung zu verbessern." cookieconsentDismiss = "Zustimmen" diff --git a/i18n/en.toml b/i18n/en.toml index eef2d38e..3f4aea87 100644 --- a/i18n/en.toml +++ b/i18n/en.toml @@ -102,6 +102,7 @@ copiedText = "Copied" toggleLineNumbers = "Toggle line numbers" toggleLineWrap = "Toggle code wrap" toggleCodeEditable = "Toggle code block editable" +downloadCode = "Download code" expandCode = "Expand or collapse code block" cookieconsentMessage = "This website uses Cookies to improve your experience." cookieconsentDismiss = "Got it!" diff --git a/i18n/es.toml b/i18n/es.toml index f51f2fab..a71807db 100644 --- a/i18n/es.toml +++ b/i18n/es.toml @@ -102,6 +102,7 @@ copiedText = "Copiado" toggleLineNumbers = "Alternar números de línea" toggleLineWrap = "Alternar ajuste de línea" toggleCodeEditable = "Alternar bloque de código editable" +downloadCode = "Descargar código" expandCode = "Expandir o contraer bloque de código" cookieconsentMessage = "Este sitio web utiliza Cookies para mejorar su experiencia." cookieconsentDismiss = "De acuerdo" diff --git a/i18n/fr.toml b/i18n/fr.toml index 753d1fa7..24cd209e 100644 --- a/i18n/fr.toml +++ b/i18n/fr.toml @@ -102,6 +102,7 @@ copiedText = "Copié" toggleLineNumbers = "Basculer les numéros de ligne" toggleLineWrap = "Basculer le retour à la ligne" toggleCodeEditable = "Basculer le bloc de code modifiable" +downloadCode = "Télécharger le code" expandCode = "Développer ou réduire le bloc de code" cookieconsentMessage = "Ce site Web utilise des Cookies pour améliorer votre expérience." cookieconsentDismiss = "J'ai compris !" diff --git a/i18n/hi.toml b/i18n/hi.toml index 4b6be040..25f6046a 100644 --- a/i18n/hi.toml +++ b/i18n/hi.toml @@ -103,6 +103,7 @@ copiedText = "कॉपी किया गया" toggleLineNumbers = "लाइन नंबर टॉगल करें" toggleLineWrap = "लाइन रैप टॉगल करें" toggleCodeEditable = "कोड ब्लॉक संपादन योग्य टॉगल करें" +downloadCode = "कोड डाउनलोड करें" expandCode = "कोड ब्लॉक का विस्तार या संकुचित करें" cookieconsentMessage = "यह वेबसाइट आपके अनुभव को बेहतर बनाने के लिए कुकीज़ का उपयोग करती है।" cookieconsentDismiss = "समझ गया!" diff --git a/i18n/it.toml b/i18n/it.toml index b5e4fe18..5b5e3874 100644 --- a/i18n/it.toml +++ b/i18n/it.toml @@ -102,6 +102,7 @@ copiedText = "Copiato" toggleLineNumbers = "Attiva/disattiva i numeri di linea" toggleLineWrap = "Attiva/disattiva il ritorno a capo del codice" toggleCodeEditable = "Attiva/disattiva la modifica del blocco di codice" +downloadCode = "Scarica codice" expandCode = "Espandi o riduci il blocco di codice" cookieconsentMessage = "Questo sito Web utilizza i Cookies per migliorare la tua esperienza." cookieconsentDismiss = "Essere d'accordo" diff --git a/i18n/ja.toml b/i18n/ja.toml index cde23e92..2c2cd76b 100644 --- a/i18n/ja.toml +++ b/i18n/ja.toml @@ -100,6 +100,7 @@ copiedText = "コピー済み" toggleLineNumbers = "行番号の切り替え" toggleLineWrap = "自動折り返しの切り替え" toggleCodeEditable = "コードブロックの編集切り替え" +downloadCode = "コードをダウンロード" expandCode = "コードブロックの展開/折りたたみ" cookieconsentMessage = "このウェブサイトはクッキーを使用して体験を向上させます。" cookieconsentDismiss = "同意する" diff --git a/i18n/ko.toml b/i18n/ko.toml index ef7a7eb1..47c3228d 100644 --- a/i18n/ko.toml +++ b/i18n/ko.toml @@ -100,6 +100,7 @@ copiedText = "복사됨" toggleLineNumbers = "줄 번호 전환" toggleLineWrap = "자동 줄 바꿈 전환" toggleCodeEditable = "코드 블록 편집 전환" +downloadCode = "코드 다운로드" expandCode = "코드 블록 확장/축소" cookieconsentMessage = "이 웹사이트는 쿠키를 사용하여 경험을 향상시킵니다." cookieconsentDismiss = "동의" diff --git a/i18n/pl.toml b/i18n/pl.toml index acd9876d..39e80653 100644 --- a/i18n/pl.toml +++ b/i18n/pl.toml @@ -102,6 +102,7 @@ copiedText = "Skopiowano" toggleLineNumbers = "Przełącz numery linii" toggleLineWrap = "Przełącz zawijanie linii" toggleCodeEditable = "Przełącz edytowalny blok kodu" +downloadCode = "Pobierz kod" expandCode = "Rozwiń lub zwiń blok kodu" cookieconsentMessage = "Ta strona korzysta z plików Cookies, aby poprawić komfort użytkowania." cookieconsentDismiss = "Zgodzić się" diff --git a/i18n/pt-BR.toml b/i18n/pt-BR.toml index 184e237a..661d5bbb 100644 --- a/i18n/pt-BR.toml +++ b/i18n/pt-BR.toml @@ -103,6 +103,7 @@ copiedText = "Copiado" toggleLineNumbers = "Alternar números de linha" toggleLineWrap = "Alternar quebra de linha" toggleCodeEditable = "Alternar bloco de código editável" +downloadCode = "Baixar código" expandCode = "Expandir ou recolher bloco de código" cookieconsentMessage = "Este site usa Cookies para melhorar sua experiência." cookieconsentDismiss = "Aceitar" diff --git a/i18n/ro.toml b/i18n/ro.toml index 23827fbd..dfbee87e 100644 --- a/i18n/ro.toml +++ b/i18n/ro.toml @@ -102,6 +102,7 @@ copiedText = "Copiat" toggleLineNumbers = "Comută numerele de linie" toggleLineWrap = "Comută înfășurarea liniei" toggleCodeEditable = "Comută blocul de cod editabil" +downloadCode = "Descarcă codul" expandCode = "Extinde sau restrânge blocul de cod" cookieconsentMessage = "Acest site web utilizează Cookies pentru a vă îmbunătăți experiența." cookieconsentDismiss = "De acord" diff --git a/i18n/ru.toml b/i18n/ru.toml index be7890db..bb72a719 100644 --- a/i18n/ru.toml +++ b/i18n/ru.toml @@ -102,6 +102,7 @@ copiedText = "Скопировано" toggleLineNumbers = "Переключить номера строк" toggleLineWrap = "Переключить перенос строк" toggleCodeEditable = "Переключить редактируемый блок кода" +downloadCode = "Скачать код" expandCode = "Развернуть или свернуть блок кода" cookieconsentMessage = "Этот сайт использует Cookies для улучшения вашего опыта." cookieconsentDismiss = "Согласен" diff --git a/i18n/sr.toml b/i18n/sr.toml index 01c91c2c..c2f20938 100644 --- a/i18n/sr.toml +++ b/i18n/sr.toml @@ -102,6 +102,7 @@ copiedText = "Копирано" toggleLineNumbers = "Прекидач за бројеве линија" toggleLineWrap = "Прекидач за оматање линија" toggleCodeEditable = "Пребаци уређивање блока кода" +downloadCode = "Преузми код" expandCode = "Прошири или смањи блок кода" cookieconsentMessage = "Ова веб локација користи Cookies да би побољшала ваше искуство." cookieconsentDismiss = "Договорити се" diff --git a/i18n/vi.toml b/i18n/vi.toml index 44a847ac..06ecbf91 100644 --- a/i18n/vi.toml +++ b/i18n/vi.toml @@ -101,6 +101,7 @@ copiedText = "Đã sao chép" toggleLineNumbers = "Chuyển đổi số dòng" toggleLineWrap = "Chuyển đổi xuống dòng" toggleCodeEditable = "Chuyển đổi khối mã có thể chỉnh sửa" +downloadCode = "Tải mã" expandCode = "Mở rộng hoặc thu gọn khối mã" cookieconsentMessage = "Trang web này sử dụng Cookies để cải thiện trải nghiệm của bạn." cookieconsentDismiss = "Đã hiểu!" diff --git a/i18n/zh-CN.toml b/i18n/zh-CN.toml index 7cf5f24b..6d0c1ed4 100644 --- a/i18n/zh-CN.toml +++ b/i18n/zh-CN.toml @@ -100,6 +100,7 @@ copiedText = "已复制" toggleLineNumbers = "切换行号" toggleLineWrap = "切换自动换行" toggleCodeEditable = "切换可编辑代码块" +downloadCode = "下载代码" expandCode = "展开或折叠代码块" cookieconsentMessage = "本网站使用 Cookies 来改善您的浏览体验。" cookieconsentDismiss = "同意" diff --git a/i18n/zh-TW.toml b/i18n/zh-TW.toml index 5a0f9210..726378e3 100644 --- a/i18n/zh-TW.toml +++ b/i18n/zh-TW.toml @@ -100,6 +100,7 @@ copiedText = "已複製" toggleLineNumbers = "切換行號" toggleLineWrap = "切換自動換行" toggleCodeEditable = "切換可編輯程式碼區塊" +downloadCode = "下載程式碼" expandCode = "展開或摺疊程式碼區塊" cookieconsentMessage = "本網站使用 Cookies 來改善您的流覽體驗。" cookieconsentDismiss = "同意" diff --git a/layouts/_partials/function/code-header.html b/layouts/_partials/function/code-header.html index 3f00f603..14a33687 100644 --- a/layouts/_partials/function/code-header.html +++ b/layouts/_partials/function/code-header.html @@ -7,6 +7,7 @@ {{- $lineWrapBtn := "" -}} {{- $editBtn := "" -}} {{- $copyBtn := "" -}} +{{- $downloadBtn := "" -}} {{- $foldIcon := dict "Class" "arrow fa-solid fa-chevron-down" | partial "plugin/icon.html" -}} {{- $titleEl := printf `%s` $foldIcon -}} {{- $ellipsisBtn := printf `%s` (dict "Class" "fa-solid fa-ellipsis-h" | partial "plugin/icon.html") -}} @@ -30,10 +31,14 @@ {{- $copyIcon := dict "Class" "fa-regular fa-clone" | partial "plugin/icon.html" -}} {{- $copyBtn = printf `%s` (T "assets.copyToClipboard") (T "assets.copyToClipboard") $copyIcon -}} {{- end -}} +{{- if $config.downloadable -}} + {{- $downloadIcon := dict "Class" "fa-solid fa-download" | partial "plugin/icon.html" -}} + {{- $downloadBtn = printf `%s` (T "assets.downloadCode") (T "assets.downloadCode") $downloadIcon -}} +{{- end -}} {{- with $config.name -}} {{- $titleEl = replace $titleEl `` (printf `` .) -}} {{- end -}} -{{- $codeHeader := printf `
%s%s%s%s%s%s
` $lang $titleEl $ellipsisBtn $lineNosBtn $lineWrapBtn $editBtn $copyBtn -}} +{{- $codeHeader := printf `
%s%s%s%s%s%s%s
` $lang $titleEl $ellipsisBtn $lineNosBtn $lineWrapBtn $editBtn $copyBtn $downloadBtn -}} {{- $wrapper = replaceRE `(
)` (printf "%s$1" $codeHeader) $wrapper -}} {{- return $wrapper -}}