diff --git a/themes/butterfly/layout/includes/head/config.pug b/themes/butterfly/layout/includes/head/config.pug index efa3654..137c041 100644 --- a/themes/butterfly/layout/includes/head/config.pug +++ b/themes/butterfly/layout/includes/head/config.pug @@ -10,7 +10,6 @@ hitsPerPage: theme.search.algolia_search.hitsPerPage, // search languages languages: { - input_placeholder: theme.search.placeholder || _p("search.input_placeholder"), hits_empty: _p("search.algolia_search.hits_empty"), hits_stats: _p("search.algolia_search.hits_stats"), } @@ -19,12 +18,16 @@ let localSearch = 'undefined' if (theme.search.use === 'local_search') { - const { CDN, preload, top_n_per_article, unescape } = theme.search.local_search + const { CDN, preload, top_n_per_article, pagination, unescape } = theme.search.local_search localSearch = JSON.stringify({ path: CDN || config.root + config.search.path, preload, top_n_per_article, unescape, + pagination: { + enable: pagination.enable, + hitsPerPage: pagination.hitsPerPage + }, languages: { // search languages hits_empty: _p("search.local_search.hits_empty"), diff --git a/themes/butterfly/layout/includes/head/structured_data.pug b/themes/butterfly/layout/includes/head/structured_data.pug index 3fbb06f..a45206f 100644 --- a/themes/butterfly/layout/includes/head/structured_data.pug +++ b/themes/butterfly/layout/includes/head/structured_data.pug @@ -40,10 +40,22 @@ if theme.structured_data const isRootOrSubdomain = currentPath.split('/').filter(Boolean).length === 0; if (isRootOrSubdomain) { + const domain = new URL(config.url).hostname; + const alternateNames = theme.structured_data.alternate_name || []; + + if (config.subtitle) { + alternateNames.push(config.subtitle); + } + + if (domain) { + alternateNames.push(domain); + } + const jsonLd = { "@context": "https://schema.org", "@type": "WebSite", "name": config.title, + "alternateName": alternateNames, "url": full_url_for('/'), } diff --git a/themes/butterfly/layout/includes/loading/fullpage-loading.pug b/themes/butterfly/layout/includes/loading/fullpage-loading.pug index 9aa9296..2cea198 100644 --- a/themes/butterfly/layout/includes/loading/fullpage-loading.pug +++ b/themes/butterfly/layout/includes/loading/fullpage-loading.pug @@ -1,23 +1,42 @@ -#loading-box(onclick='document.getElementById("loading-box").classList.add("loaded")') - .loading-bg - div.loading-img - .loading-image-dot +#loading-box + .loading-left-bg + .loading-right-bg + .spinner-box + .configure-border-1 + .configure-core + .configure-border-2 + .configure-core + .loading-word= _p('loading') script. - const preloader = { - endLoading: () => { - document.body.style.overflow = 'auto'; - document.getElementById('loading-box').classList.add("loaded") - }, - initLoading: () => { - document.body.style.overflow = ''; - document.getElementById('loading-box').classList.remove("loaded") - + (()=>{ + const $loadingBox = document.getElementById('loading-box') + const $body = document.body + const preloader = { + endLoading: () => { + if ($loadingBox.classList.contains('loaded')) return + $body.style.overflow = '' + $loadingBox.classList.add('loaded') + }, + initLoading: () => { + $body.style.overflow = 'hidden' + $loadingBox.classList.remove('loaded') + } } - } - window.addEventListener('load',()=> { preloader.endLoading() }) - if (!{theme.pjax && theme.pjax.enable}) { - document.addEventListener('pjax:send', () => { preloader.initLoading() }) - document.addEventListener('pjax:complete', () => { preloader.endLoading() }) - } \ No newline at end of file + preloader.initLoading() + + if (document.readyState === 'complete') { + preloader.endLoading() + } else { + window.addEventListener('load', preloader.endLoading) + document.addEventListener('DOMContentLoaded', preloader.endLoading) + // Add timeout protection: force end after 7 seconds + setTimeout(preloader.endLoading, 7000) + } + + if (!{theme.pjax && theme.pjax.enable}) { + btf.addGlobalFn('pjaxSend', preloader.initLoading, 'preloader_init') + btf.addGlobalFn('pjaxComplete', preloader.endLoading, 'preloader_end') + } + })() \ No newline at end of file diff --git a/themes/butterfly/layout/includes/loading/index.pug b/themes/butterfly/layout/includes/loading/index.pug index f585d78..6a6facc 100644 --- a/themes/butterfly/layout/includes/loading/index.pug +++ b/themes/butterfly/layout/includes/loading/index.pug @@ -1,7 +1,5 @@ -if theme.preloader.source === 1 - include ./fullpage-loading.pug -else if theme.preloader.source === 2 - include ./pace.pug -else - include ./fullpage-loading.pug - include ./pace.pug \ No newline at end of file +if theme.preloader.enable + if theme.preloader.source === 1 + include ./fullpage-loading.pug + else + include ./pace.pug \ No newline at end of file diff --git a/themes/butterfly/layout/includes/mixins/indexPostUI.pug b/themes/butterfly/layout/includes/mixins/indexPostUI.pug index 6ae3ebf..4f2c3ee 100644 --- a/themes/butterfly/layout/includes/mixins/indexPostUI.pug +++ b/themes/butterfly/layout/includes/mixins/indexPostUI.pug @@ -2,8 +2,6 @@ mixin indexPostUI() - const indexLayout = theme.index_layout - const masonryLayoutClass = (indexLayout === 6 || indexLayout === 7) ? 'masonry' : '' #recent-posts.recent-posts.nc(class=masonryLayoutClass) - #category-bar.category-bar - include ../categoryBar.pug .recent-post-items each article, index in page.posts.data .recent-post-item diff --git a/themes/butterfly/layout/includes/page/categories.pug b/themes/butterfly/layout/includes/page/categories.pug index fe02784..79153c8 100644 --- a/themes/butterfly/layout/includes/page/categories.pug +++ b/themes/butterfly/layout/includes/page/categories.pug @@ -1,3 +1 @@ - -.category-lists!= list_categories() -#categories-chart(data-parent="true" style="height: 300px; padding: 10px;") \ No newline at end of file +.category-lists!= list_categories() \ No newline at end of file diff --git a/themes/butterfly/layout/includes/page/shuoshuo.pug b/themes/butterfly/layout/includes/page/shuoshuo.pug index 3bcb01d..1e2dbee 100644 --- a/themes/butterfly/layout/includes/page/shuoshuo.pug +++ b/themes/butterfly/layout/includes/page/shuoshuo.pug @@ -9,40 +9,46 @@ - page.toc = false #article-container - if page.comments !== false && theme.comments.use - - commentsJsLoad = true + if page.shuoshuo_url || (site.data.shuoshuo && site.data.shuoshuo.length) + if page.comments !== false && theme.comments.use + - commentsJsLoad = true - script. - (() => { - const commentDiv = `!{partial('includes/third-party/comments/index', {}, {cache: true})}` + script. + (() => { + const commentDiv = `!{partial('includes/third-party/comments/index', {}, {cache: true})}` - const runDestroy = (shuoshuoComment) => { - if (!shuoshuoComment) return + const runDestroy = (shuoshuoComment) => { + if (!shuoshuoComment) return - for (const [key, fn] of Object.entries(shuoshuoComment)) { - if (key.startsWith('destroy')) fn() + for (const [key, fn] of Object.entries(shuoshuoComment)) { + if (key.startsWith('destroy')) fn() + } } - } - window.addCommentToShuoshuo = e => { - const btn = e.target.closest('.shuoshuo-comment-btn') - if (!btn) return + window.addCommentToShuoshuo = e => { + const btn = e.target.closest('.shuoshuo-comment-btn') + if (!btn) return - const ele = btn.closest('.container').nextElementSibling - const { shuoshuoComment } = window - const isInclude = ele.classList.contains('no-comment') - runDestroy(shuoshuoComment) - if (isInclude) { - ele.classList.remove('no-comment') - ele.innerHTML = commentDiv - const key = `${location.pathname.replace(/\/$/, '')}?key=${ele.getAttribute('data-key')}` - btf.switchComments(ele, key) - shuoshuoComment.loadComment && shuoshuoComment.loadComment(ele, key) + const ele = btn.closest('.container').nextElementSibling + const { shuoshuoComment } = window + const isInclude = ele.classList.contains('no-comment') + runDestroy(shuoshuoComment) + if (isInclude) { + ele.classList.remove('no-comment') + ele.innerHTML = commentDiv + const key = `${location.pathname.replace(/\/$/, '')}?key=${ele.getAttribute('data-key')}` + btf.switchComments(ele, key) + shuoshuoComment.loadComment && shuoshuoComment.loadComment(ele, key) + } } - } - })() + })() + + + - const localDate = page.shuoshuo_url ? [] : shuoshuoFN(site.data.shuoshuo, page) + + if !page.shuoshuo_url + script(type='application/json' id='shuoshuo-data')!= safeJSON(localDate) - if page.shuoshuo_url script. (() => { const limitConfig = !{ JSON.stringify(page.limit || {}) } @@ -78,111 +84,218 @@ return `${year}-${month}-${day} ${hour}:${minute}:${second}` } + let currentPage = 1 + const itemsPerPage = 8 + let totalPages = 0 + let data = [] + let inputEventsAttached = false // Flag to mark if input event listeners have been added + + const renderData = (dataSlice) => { + const content = dataSlice.map(item => { + const formattedDate = formatToTimeZone(item.date) + const tags = item.tags && item.tags.map(tag => `${tag}`).join('') || '' + const commentButton = item.key && !{commentsJsLoad} + ? `
+ +
` + : '' + const commentContainer = item.key + ? `
` + : '' + + return ` +
+
+
+
+ +
+
+
${item.author || '!{config.author}'}
+ +
+
+
${item.content}
+ +
+ ${commentContainer} +
` + }).join('') + + const container = document.getElementById('article-container') + container.innerHTML = content + + window.lazyLoadInstance && window.lazyLoadInstance.update() + btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)')) + } + + const renderNavigation = () => { + const container = document.getElementById('article-container') + const existingNav = container.nextElementSibling + if (existingNav && existingNav.classList.contains('shuoshuo-navigation')) { + existingNav.remove() + } + + const pageInfoTemplate = '#{__('pagination.page_info')}' + const pageInfoText = pageInfoTemplate + .replace(/\$\{current}/g, currentPage) + .replace(/\$\{total}/g, totalPages) + + const navHtml = ` +
+ + ${pageInfoText} + + +
+ ` + container.insertAdjacentHTML('afterend', navHtml) + + // Add input validation event listeners (only once) + if (!inputEventsAttached) { + setTimeout(() => { + const input = document.querySelector('.shuoshuo-page-input') + if (input) { + // Clear placeholder when clicking the input box + input.addEventListener('focus', (event) => { + event.target.placeholder = '' + }) + + // Restore placeholder if no content when losing focus + input.addEventListener('blur', (event) => { + if (!event.target.value.trim()) { + event.target.placeholder = currentPage + } + }) + + input.addEventListener('input', (event) => { + const value = parseInt(event.target.value) || 0 + let wasInvalid = false + + if (value > totalPages) { + event.target.value = totalPages + wasInvalid = true + } else if (value < 1 && event.target.value !== '') { + event.target.value = 1 + wasInvalid = true + } + + // If value is corrected, show red and shake effect + if (wasInvalid) { + event.target.classList.add('invalid') + setTimeout(() => { + event.target.classList.remove('invalid') + }, 500) + } + }) + + inputEventsAttached = true // Mark that event listeners have been added + } + }, 0) + } + } + + const renderPage = (page) => { + const start = (page - 1) * itemsPerPage + const end = start + itemsPerPage + const pageData = data.slice(start, end) + renderData(pageData) + renderNavigation() + } + + window.shuoshuoPrevPage = () => { + if (currentPage > 1) { + currentPage-- + renderPage(currentPage) + } + } + + window.shuoshuoNextPage = () => { + if (currentPage < totalPages) { + currentPage++ + renderPage(currentPage) + } + } + + window.shuoshuoGoToPage = (page) => { + if (typeof page === 'number') { + // Directly jump to the specified page + if (page >= 1 && page <= totalPages && page !== currentPage) { + currentPage = page + renderPage(currentPage) + } + } else { + // Get page from input box + const input = document.querySelector('.shuoshuo-page-input') + const inputValue = input.value.trim() + const inputPage = inputValue === '' ? currentPage : parseInt(inputValue) + if (inputPage >= 1 && inputPage <= totalPages && inputPage !== currentPage) { + currentPage = inputPage + renderPage(currentPage) + } else if (inputValue === '') { + // If input box is empty, re-render current page (update placeholder) + renderPage(currentPage) + } + } + } + + window.shuoshuoHandleKeyDown = (event) => { + const input = event.target + const value = input.value + event.key + + // Allow delete, arrow keys, backspace, etc. + if (event.key === 'Enter' || event.key === 'Backspace' || event.key === 'Delete' || + event.key === 'ArrowLeft' || event.key === 'ArrowRight' || + event.key === 'Tab' || event.ctrlKey || event.metaKey) { + if (event.key === 'Enter') { + window.shuoshuoGoToPage() + } + return + } + + // Only allow numbers + if (!/^\d$/.test(event.key)) { + event.preventDefault() + return + } + + // Check if the value after input exceeds the range + const newValue = parseInt(value) || 0 + if (newValue > totalPages || (value.length > 1 && newValue === 0)) { + event.preventDefault() + // Add red and shake effect + input.classList.add('invalid') + setTimeout(() => { + input.classList.remove('invalid') + }, 500) + } + } + const loadShuoshuo = async () => { try { - const response = await fetch('!{url_for(page.shuoshuo_url)}') - let data = await response.json() - - data = filterDataByLimit(sortDataByDate(data), limitConfig) - - const container = document.getElementById('article-container') - let start = 0 - - const renderData = (dataSlice) => { - const content = dataSlice.map(item => { - const formattedDate = formatToTimeZone(item.date) - const tags = item.tags && item.tags.map(tag => `${tag}`).join('') || '' - const commentButton = item.key && !{commentsJsLoad} - ? `
- -
` - : '' - const commentContainer = item.key - ? `
` - : '' - - return ` -
-
-
-
- -
-
-
${item.author || '!{config.author}'}
- -
-
-
${item.content}
- -
- ${commentContainer} -
` - }).join('') - - container.insertAdjacentHTML('beforeend', content) - - window.lazyLoadInstance.update() - btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)')) + let originData = [] + if (!{Boolean(page.shuoshuo_url)}) { + const response = await fetch('!{url_for(page.shuoshuo_url)}') + originData = await response.json() + } else { + const dataElement = document.getElementById('shuoshuo-data') + originData = dataElement ? JSON.parse(dataElement.textContent) : [] } - const handleIntersection = (entries) => { - if (!entries[0].isIntersecting) return - observer.unobserve(entries[0].target) + data = filterDataByLimit(sortDataByDate(originData), limitConfig) - const slice = data.slice(start, start + 10) - renderData(slice) - start += 10 + totalPages = Math.ceil(data.length / itemsPerPage) - if (start < data.length) { - setTimeout(() => observer.observe(container.lastElementChild), 100) - } else { - observer.disconnect() - } - }; - - const observer = new IntersectionObserver(handleIntersection, { - root: null, - rootMargin: '0px', - threshold: 1.0 - }) - - renderData(data.slice(start, 10)) - start += 10 - - if (container.lastElementChild) observer.observe(container.lastElementChild) + renderPage(currentPage) } catch (error) { console.error(error) } }; window.pjax ? loadShuoshuo() : window.addEventListener('load', loadShuoshuo) - })() - else - if site.data.shuoshuo - each i in shuoshuoFN(site.data.shuoshuo, page) - .shuoshuo-item - .container - .shuoshuo-item-header - .shuoshuo-avatar - img.no-lightbox(src=i.avatar || url_for(theme.avatar.img)) - .shuoshuo-info - .shuoshuo-author=i.author || config.author - time.shuoshuo-date(title=i.date)=i.date - .shuoshuo-content - !=markdown(i.content) - .shuoshuo-footer(class=i.tags && i.tags.length ? 'flex-between' : 'flex-end') - if i.tags - .shuoshuo-tags - each tag in i.tags - span.shuoshuo-tag=tag - if i.key && commentsJsLoad - .shuoshuo-comment-btn(onclick='addCommentToShuoshuo(event)') - i.fa-solid.fa-comments - if i.key && commentsJsLoad - .shuoshuo-comment.no-comment(data-key=i.key) \ No newline at end of file + })() \ No newline at end of file diff --git a/themes/butterfly/layout/includes/page/tags.pug b/themes/butterfly/layout/includes/page/tags.pug index ef56d4f..b5b62cd 100644 --- a/themes/butterfly/layout/includes/page/tags.pug +++ b/themes/butterfly/layout/includes/page/tags.pug @@ -1,4 +1,2 @@ - .tag-cloud-list.text-center - !=cloudTags({source: site.tags, orderby: page.orderby || 'random', order: page.order || 1, minfontsize: 1.2, maxfontsize: 1.5, limit: 0, unit: 'em'}) -#tags-chart(data-length="10" style="height: 300px; padding: 10px;") \ No newline at end of file + !=cloudTags({source: site.tags, orderby: page.orderby || 'random', order: page.order || 1, minfontsize: 1.2, maxfontsize: 1.5, limit: 0, unit: 'em'}) \ No newline at end of file diff --git a/themes/butterfly/layout/includes/third-party/math/mathjax.pug b/themes/butterfly/layout/includes/third-party/math/mathjax.pug index f0483ef..019d779 100644 --- a/themes/butterfly/layout/includes/third-party/math/mathjax.pug +++ b/themes/butterfly/layout/includes/third-party/math/mathjax.pug @@ -1,19 +1,50 @@ -//- Mathjax 3 +//- Mathjax 4/5 - const { tags, enableMenu } = theme.math.mathjax script. (() => { const loadMathjax = () => { if (!window.MathJax) { window.MathJax = { + loader: { + load: [ + // Four font extension packages (optional) + //- '[tex]/bbm', + //- '[tex]/bboldx', + //- '[tex]/dsfont', + '[tex]/mhchem' + ], + paths: { + 'mathjax-newcm': '[mathjax]/../@mathjax/mathjax-newcm-font', + + //- // Four font extension packages (optional) + //- 'mathjax-bbm-extension': '[mathjax]/../@mathjax/mathjax-bbm-font-extension', + //- 'mathjax-bboldx-extension': '[mathjax]/../@mathjax/mathjax-bboldx-font-extension', + //- 'mathjax-dsfont-extension': '[mathjax]/../@mathjax/mathjax-dsfont-font-extension', + 'mathjax-mhchem-extension': '[mathjax]/../@mathjax/mathjax-mhchem-font-extension' + } + }, + output: { + font: 'mathjax-newcm', + }, tex: { inlineMath: [['$', '$'], ['\\(', '\\)']], tags: '!{tags}', + packages: { + '[+]': [ + 'mhchem' + ] + } }, chtml: { scale: 1.1 }, options: { enableMenu: !{enableMenu}, + menuOptions: { + settings: { + enrich: false // Turn off Braille and voice narration text automatic generation + } + }, renderActions: { findScript: [10, doc => { for (const node of document.querySelectorAll('script[type^="math/tex"]')) { diff --git a/themes/butterfly/layout/includes/third-party/pjax.pug b/themes/butterfly/layout/includes/third-party/pjax.pug index d6b69bb..7f2db6b 100644 --- a/themes/butterfly/layout/includes/third-party/pjax.pug +++ b/themes/butterfly/layout/includes/third-party/pjax.pug @@ -29,7 +29,13 @@ script. const triggerPjaxFn = (val) => { if (!val) return - Object.values(val).forEach(fn => fn()) + Object.values(val).forEach(fn => { + try { + fn() + } catch (err) { + console.debug('Pjax callback failed:', err) + } + }) } document.addEventListener('pjax:send', () => { diff --git a/themes/butterfly/layout/includes/third-party/search/algolia.pug b/themes/butterfly/layout/includes/third-party/search/algolia.pug index 4ef6001..dd24962 100644 --- a/themes/butterfly/layout/includes/third-party/search/algolia.pug +++ b/themes/butterfly/layout/includes/third-party/search/algolia.pug @@ -2,21 +2,33 @@ .search-dialog nav.search-nav span.search-dialog-title= _p('search.title') + i.fas.fa-spinner.fa-pulse#loading-status(hidden) button.search-close-button i.fas.fa-times - .search-wrap - #algolia-search-input + #algolia-search-input + .ais-SearchBox + form.ais-SearchBox-form(action="" role="search" novalidate="") + input.ais-SearchBox-input(type="search" placeholder=theme.search.placeholder || _p("search.input_placeholder") autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" maxlength="512" aria-label="Search") + button.ais-SearchBox-submit(type="submit" title="Submit the search query" style="display:none;") + svg.ais-SearchBox-submitIcon(width="10" height="10" viewBox="0 0 40 40" aria-hidden="true") + path(d="M26.804 29.01c-2.832 2.34-6.465 3.746-10.426 3.746C7.333 32.756 0 25.424 0 16.378 0 7.333 7.333 0 16.378 0c9.046 0 16.378 7.333 16.378 16.378 0 3.96-1.406 7.594-3.746 10.426l10.534 10.534c.607.607.61 1.59-.004 2.202-.61.61-1.597.61-2.202.004L26.804 29.01zm-10.426.627c7.323 0 13.26-5.936 13.26-13.26 0-7.32-5.937-13.257-13.26-13.257C9.056 3.12 3.12 9.056 3.12 16.378c0 7.323 5.936 13.26 13.258 13.26z") hr #algolia-search-results #algolia-hits - #algolia-pagination + #algolia-hits-empty(style="display:none;") + .ais-Hits(style="display:none;") + ol.ais-Hits-list + #algolia-pagination.ais-Pagination(style="display:none;") + ul.ais-Pagination-list #algolia-info - .algolia-stats - .algolia-poweredBy + span.ais-Stats-text + a.algolia-poweredBy(href="https://www.algolia.com/?utm_source=algoliasearch.js&utm_medium=website&utm_content=localhost&utm_campaign=poweredby" target="_blank" aria-label="Search by Algolia" rel="noopener noreferrer") + svg.ais-PoweredBy-logo(height="1.2em" viewBox="0 0 572 64" style="width: auto;") + path(fill="#36395A" d="M16 48.3c-3.4 0-6.3-.6-8.7-1.7A12.4 12.4 0 0 1 1.9 42C.6 40 0 38 0 35.4h6.5a6.7 6.7 0 0 0 3.9 6c1.4.7 3.3 1.1 5.6 1.1 2.2 0 4-.3 5.4-1a7 7 0 0 0 3-2.4 6 6 0 0 0 1-3.4c0-1.5-.6-2.8-1.9-3.7-1.3-1-3.3-1.6-5.9-1.8l-4-.4c-3.7-.3-6.6-1.4-8.8-3.4a10 10 0 0 1-3.3-7.9c0-2.4.6-4.6 1.8-6.4a12 12 0 0 1 5-4.3c2.2-1 4.7-1.6 7.5-1.6s5.5.5 7.6 1.6a12 12 0 0 1 5 4.4c1.2 1.8 1.8 4 1.8 6.7h-6.5a6.4 6.4 0 0 0-3.5-5.9c-1-.6-2.6-1-4.4-1s-3.2.3-4.4 1c-1.1.6-2 1.4-2.6 2.4-.5 1-.8 2-.8 3.1a5 5 0 0 0 1.5 3.6c1 1 2.6 1.7 4.7 1.9l4 .3c2.8.2 5.2.8 7.2 1.8 2.1 1 3.7 2.2 4.9 3.8a9.7 9.7 0 0 1 1.7 5.8c0 2.5-.7 4.7-2 6.6a13 13 0 0 1-5.6 4.4c-2.4 1-5.2 1.6-8.4 1.6Zm35.6 0c-2.6 0-4.8-.4-6.7-1.3a13 13 0 0 1-4.7-3.5 17.1 17.1 0 0 1-3.6-10.4v-1c0-2 .3-3.8 1-5.6a13 13 0 0 1 7.3-8.3 15 15 0 0 1 6.3-1.4A13.2 13.2 0 0 1 64 24.3c1 2.2 1.6 4.6 1.6 7.2V34H39.4v-4.3h21.8l-1.8 2.2c0-2-.3-3.7-.9-5.1a7.3 7.3 0 0 0-2.7-3.4c-1.2-.7-2.7-1.1-4.6-1.1s-3.4.4-4.7 1.3a8 8 0 0 0-2.9 3.6c-.6 1.5-.9 3.3-.9 5.4 0 2 .3 3.7 1 5.3a7.9 7.9 0 0 0 2.8 3.7c1.3.8 3 1.3 5 1.3s3.8-.5 5.1-1.3c1.3-1 2.1-2 2.4-3.2h6a11.8 11.8 0 0 1-7 8.7 16 16 0 0 1-6.4 1.2ZM80 48c-2.2 0-4-.3-5.7-1a8.4 8.4 0 0 1-3.7-3.3 9.7 9.7 0 0 1-1.3-5.2c0-2 .5-3.8 1.5-5.2a9 9 0 0 1 4.3-3.1c1.8-.7 4-1 6.7-1H89v4.1h-7.5c-2 0-3.4.5-4.4 1.4-1 1-1.6 2.1-1.6 3.6s.5 2.7 1.6 3.6c1 1 2.5 1.4 4.4 1.4 1.1 0 2.2-.2 3.2-.7 1-.4 1.9-1 2.6-2 .6-1 1-2.4 1-4.2l1.7 2.1c-.2 2-.7 3.8-1.5 5.2a9 9 0 0 1-3.4 3.3 12 12 0 0 1-5.3 1Zm9.5-.7v-8.8h-1v-10c0-1.8-.5-3.2-1.4-4.1-1-1-2.4-1.4-4.2-1.4a142.9 142.9 0 0 0-10.2.4v-5.6a74.8 74.8 0 0 1 8.6-.4c3 0 5.5.4 7.5 1.2s3.4 2 4.4 3.6c1 1.7 1.4 4 1.4 6.7v18.4h-5Zm12.9 0V17.8h5v12.3h-.2c0-4.2 1-7.4 2.8-9.5a11 11 0 0 1 8.3-3.1h1v5.6h-2a9 9 0 0 0-6.3 2.2c-1.5 1.5-2.2 3.6-2.2 6.4v15.6h-6.4Zm34.4 1a15 15 0 0 1-6.6-1.3c-1.9-.9-3.4-2-4.7-3.5a15.5 15.5 0 0 1-2.7-5c-.6-1.7-1-3.6-1-5.4v-1c0-2 .4-3.8 1-5.6a15 15 0 0 1 2.8-4.9c1.3-1.5 2.8-2.6 4.6-3.5a16.4 16.4 0 0 1 13.3.2c2 1 3.5 2.3 4.8 4a12 12 0 0 1 2 6H144c-.2-1.6-1-3-2.2-4.1a7.5 7.5 0 0 0-5.2-1.7 8 8 0 0 0-4.7 1.3 8 8 0 0 0-2.8 3.6 13.8 13.8 0 0 0 0 10.3c.6 1.5 1.5 2.7 2.8 3.6s2.8 1.3 4.8 1.3c1.5 0 2.7-.2 3.8-.8a7 7 0 0 0 2.6-2c.7-1 1-2 1.2-3.2h6.2a11 11 0 0 1-2 6.2 15.1 15.1 0 0 1-11.8 5.5Zm19.7-1v-40h6.4V31h-1.3c0-3 .4-5.5 1.1-7.6a9.7 9.7 0 0 1 3.5-4.8A9.9 9.9 0 0 1 172 17h.3c3.5 0 6 1.1 7.9 3.5 1.7 2.3 2.6 5.7 2.6 10v16.8h-6.4V29.6c0-2.1-.6-3.8-1.8-5a6.4 6.4 0 0 0-4.8-1.8c-2 0-3.7.7-5 2a7.8 7.8 0 0 0-1.9 5.5v17h-6.4Zm63.8 1a12.2 12.2 0 0 1-10.9-6.2 19 19 0 0 1-1.8-7.3h1.4v12.5h-5.1v-40h6.4v19.8l-2 3.5c.2-3.1.8-5.7 1.9-7.7a11 11 0 0 1 4.4-4.5c1.8-1 3.9-1.5 6.1-1.5a13.4 13.4 0 0 1 12.8 9.1c.7 1.9 1 3.8 1 6v1c0 2.2-.3 4.1-1 6a13.6 13.6 0 0 1-13.2 9.4Zm-1.2-5.5a8.4 8.4 0 0 0 7.9-5c.7-1.5 1.1-3.3 1.1-5.3s-.4-3.8-1.1-5.3a8.7 8.7 0 0 0-3.2-3.6 9.6 9.6 0 0 0-9.2-.2 8.5 8.5 0 0 0-3.3 3.2c-.8 1.4-1.3 3-1.3 5v2.3a9 9 0 0 0 1.3 4.8 9 9 0 0 0 3.4 3c1.4.7 2.8 1 4.4 1Zm27.3 3.9-10-28.9h6.5l9.5 28.9h-6Zm-7.5 12.2v-5.7h4.9c1 0 2-.1 2.9-.4a4 4 0 0 0 2-1.4c.4-.7.9-1.6 1.2-2.7l8.6-30.9h6.2l-9.3 32.4a14 14 0 0 1-2.5 5 8.9 8.9 0 0 1-4 2.8c-1.5.6-3.4.9-5.6.9h-4.4Zm9-12.2v-5.2h6.4v5.2H248Z") + path(fill="#003DFF" d="M534.4 9.1H528a.8.8 0 0 1-.7-.7V1.8c0-.4.2-.7.6-.8l6.5-1c.4 0 .8.2.9.6v7.8c0 .4-.4.7-.8.7zM428 35.2V.8c0-.5-.3-.8-.7-.8h-.2l-6.4 1c-.4 0-.7.4-.7.8v35c0 1.6 0 11.8 12.3 12.2.5 0 .8-.4.8-.8V43c0-.4-.3-.7-.6-.8-4.5-.5-4.5-6-4.5-7zm106.5-21.8H528c-.4 0-.7.4-.7.8v34c0 .4.3.8.7.8h6.5c.4 0 .8-.4.8-.8v-34c0-.5-.4-.8-.8-.8zm-17.7 21.8V.8c0-.5-.3-.8-.8-.8l-6.5 1c-.4 0-.7.4-.7.8v35c0 1.6 0 11.8 12.3 12.2.4 0 .8-.4.8-.8V43c0-.4-.3-.7-.7-.8-4.4-.5-4.4-6-4.4-7zm-22.2-20.6a16.5 16.5 0 0 1 8.6 9.3c.8 2.2 1.3 4.8 1.3 7.5a19.4 19.4 0 0 1-4.6 12.6 14.8 14.8 0 0 1-5.2 3.6c-2 .9-5.2 1.4-6.8 1.4a21 21 0 0 1-6.7-1.4 15.4 15.4 0 0 1-8.6-9.3 21.3 21.3 0 0 1 0-14.4 15.2 15.2 0 0 1 8.6-9.3c2-.8 4.3-1.2 6.7-1.2s4.6.4 6.7 1.2zm-6.7 27.6c2.7 0 4.7-1 6.2-3s2.2-4.3 2.2-7.8-.7-6.3-2.2-8.3-3.5-3-6.2-3-4.7 1-6.1 3c-1.5 2-2.2 4.8-2.2 8.3s.7 5.8 2.2 7.8 3.5 3 6.2 3zm-88.8-28.8c-6.2 0-11.7 3.3-14.8 8.2a18.6 18.6 0 0 0 4.8 25.2c1.8 1.2 4 1.8 6.2 1.7s.1 0 .1 0h.9c4.2-.7 8-4 9.1-8.1v7.4c0 .4.3.7.8.7h6.4a.7.7 0 0 0 .7-.7V14.2c0-.5-.3-.8-.7-.8h-13.5zm6.3 26.5a9.8 9.8 0 0 1-5.7 2h-.5a10 10 0 0 1-9.2-14c1.4-3.7 5-6.3 9-6.3h6.4v18.3zm152.3-26.5h13.5c.5 0 .8.3.8.7v33.7c0 .4-.3.7-.8.7h-6.4a.7.7 0 0 1-.8-.7v-7.4c-1.2 4-4.8 7.4-9 8h-.1a4.2 4.2 0 0 1-.5.1h-.9a10.3 10.3 0 0 1-7-2.6c-4-3.3-6.5-8.4-6.5-14.2 0-3.7 1-7.2 3-10 3-5 8.5-8.3 14.7-8.3zm.6 28.4c2.2-.1 4.2-.6 5.7-2V21.7h-6.3a9.8 9.8 0 0 0-9 6.4 10.2 10.2 0 0 0 9.1 13.9h.5zM452.8 13.4c-6.2 0-11.7 3.3-14.8 8.2a18.5 18.5 0 0 0 3.6 24.3 10.4 10.4 0 0 0 13 .6c2.2-1.5 3.8-3.7 4.5-6.1v7.8c0 2.8-.8 5-2.2 6.3-1.5 1.5-4 2.2-7.5 2.2l-6-.3c-.3 0-.7.2-.8.5l-1.6 5.5c-.1.4.1.8.5 1h.1c2.8.4 5.5.6 7 .6 6.3 0 11-1.4 14-4.1 2.7-2.5 4.2-6.3 4.5-11.4V14.2c0-.5-.4-.8-.8-.8h-13.5zm6.3 8.2v18.3a9.6 9.6 0 0 1-5.6 2h-1a10.3 10.3 0 0 1-8.8-14c1.4-3.7 5-6.3 9-6.3h6.4zM291 31.5A32 32 0 0 1 322.8 0h30.8c.6 0 1.2.5 1.2 1.2v61.5c0 1.1-1.3 1.7-2.2 1l-19.2-17a18 18 0 0 1-11 3.4 18.1 18.1 0 1 1 18.2-14.8c-.1.4-.5.7-.9.6-.1 0-.3 0-.4-.2l-3.8-3.4c-.4-.3-.6-.8-.7-1.4a12 12 0 1 0-2.4 8.3c.4-.4 1-.5 1.6-.2l14.7 13.1v-46H323a26 26 0 1 0 10 49.7c.8-.4 1.6-.2 2.3.3l3 2.7c.3.2.3.7 0 1l-.2.2a32 32 0 0 1-47.2-28.6z") #search-mask script(src=url_for(theme.asset.algolia_search)) - script(src=url_for(theme.asset.instantsearch)) script(src=url_for(theme.asset.algolia_js)) \ No newline at end of file diff --git a/themes/butterfly/layout/includes/third-party/search/local-search.pug b/themes/butterfly/layout/includes/third-party/search/local-search.pug index e459a94..f2d0f70 100644 --- a/themes/butterfly/layout/includes/third-party/search/local-search.pug +++ b/themes/butterfly/layout/includes/third-party/search/local-search.pug @@ -2,7 +2,7 @@ .search-dialog nav.search-nav span.search-dialog-title= _p('search.title') - span#loading-status + i.fas.fa-spinner.fa-pulse#loading-status(hidden) button.search-close-button i.fas.fa-times @@ -10,13 +10,15 @@ i.fas.fa-spinner.fa-pulse span= ' ' + _p("search.load_data") - .search-wrap - #local-search-input - .local-search-box - input(placeholder=theme.search.placeholder || _p("search.input_placeholder") type="text").local-search-box--input - hr - #local-search-results - #local-search-stats-wrap - #search-mask + .local-search-input + input(placeholder=theme.search.placeholder || _p("search.input_placeholder") type="text") + hr + + #local-search-results + #local-search-pagination.ais-Pagination(style="display:none;") + ul.ais-Pagination-list + #local-search-stats + + #search-mask script(src=url_for(theme.asset.local_search)) \ No newline at end of file diff --git a/themes/butterfly/layout/includes/third-party/umami_analytics.pug b/themes/butterfly/layout/includes/third-party/umami_analytics.pug index 3534c20..3da80ed 100644 --- a/themes/butterfly/layout/includes/third-party/umami_analytics.pug +++ b/themes/butterfly/layout/includes/third-party/umami_analytics.pug @@ -1,4 +1,4 @@ -- let { serverURL, website_id, option, UV_PV } = theme.umami_analytics +- let { serverURL, script_name, website_id, option, UV_PV } = theme.umami_analytics - const isServerURL = !!serverURL - const baseURL = serverURL ? serverURL.replace(/\/$/, '') : 'https://cloud.umami.is' - const apiUrl = serverURL ? serverURL.replace(/\/$/, '') + '/api' : 'https://api.umami.is/v1' @@ -9,28 +9,51 @@ script. const config = !{JSON.stringify(UV_PV)} const runTrack = () => { - umami.track(props => ({ ...props, url: window.location.pathname, title: GLOBAL_CONFIG_SITE.title })) + if (typeof umami !== 'undefined' && typeof umami.track === 'function') { + umami.track(props => ({ ...props, url: window.location.pathname, title: GLOBAL_CONFIG_SITE.title })) + } else { + console.warn('Umami Analytics: umami.track is not available') + } } const loadUmamiJS = () => { - btf.getScript('!{baseURL}/script.js', { + btf.getScript('!{baseURL}/!{script_name}', { 'data-website-id': '!{website_id}', 'data-auto-track': 'false', ...option - }).then(runTrack) + }).then(() => { + runTrack() + }).catch(error => { + console.error('Umami Analytics: Error loading script', error) + }) } const getData = async (isPost) => { - const now = Date.now() - const keyUrl = isPost ? `&url=${window.location.pathname}` : '' - const headerList = { 'Accept': 'application/json' } - if (!{isServerURL}) headerList['Authorization'] = `Bearer ${config.token}` - else headerList['x-umami-api-key'] = config.token - const res = await fetch(`!{apiUrl}/websites/!{website_id}/stats?startAt=0000000000&endAt=${now}${keyUrl}`, { - method: "GET", - headers: headerList - }) - return await res.json() + try { + const now = Date.now() + const keyUrl = isPost ? `&url=${window.location.pathname}` : '' + const headerList = { 'Accept': 'application/json' } + + if (!{isServerURL}) { + headerList['Authorization'] = `Bearer ${config.token}` + } else { + headerList['x-umami-api-key'] = config.token + } + + const res = await fetch(`!{apiUrl}/websites/!{website_id}/stats?startAt=0000000000&endAt=${now}${keyUrl}`, { + method: "GET", + headers: headerList + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + return await res.json() + } catch (error) { + console.error('Umami Analytics: Failed to fetch data', error) + throw error + } } const insertData = async () => { @@ -39,27 +62,49 @@ script. const pagePV = document.getElementById('umamiPV') if (pagePV) { const data = await getData(true) - pagePV.textContent = data.pageviews.value - } - } else { - const data = (config.site_uv || config.site_pv) && await getData() - if (config.site_uv) { - const siteUV = document.getElementById('umami-site-uv') - if (siteUV) siteUV.textContent = data.visitors.value - } - if (config.site_pv) { - const sitePV = document.getElementById('umami-site-pv') - if (sitePV) sitePV.textContent = data.pageviews.value + if (data && data.pageviews && typeof data.pageviews.value !== 'undefined') { + pagePV.textContent = data.pageviews.value + } else { + console.warn('Umami Analytics: Invalid page view data received') + } } } - } catch (e) { - console.error('Failed to load Umami Analytics:', e) + + if (config.site_uv || config.site_pv) { + const data = await getData(false) + + if (config.site_uv) { + const siteUV = document.getElementById('umami-site-uv') + if (siteUV && data && data.visitors && typeof data.visitors.value !== 'undefined') { + siteUV.textContent = data.visitors.value + } else if (siteUV) { + console.warn('Umami Analytics: Invalid site UV data received') + } + } + + if (config.site_pv) { + const sitePV = document.getElementById('umami-site-pv') + if (sitePV && data && data.pageviews && typeof data.pageviews.value !== 'undefined') { + sitePV.textContent = data.pageviews.value + } else if (sitePV) { + console.warn('Umami Analytics: Invalid site PV data received') + } + } + } + } catch (error) { + console.error('Umami Analytics: Failed to insert data', error) } } btf.addGlobalFn('pjaxComplete', runTrack, 'umami_analytics_run_track') btf.addGlobalFn('pjaxComplete', insertData, 'umami_analytics_insert') + loadUmamiJS() - insertData() + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', insertData) + } else { + setTimeout(insertData, 100) + } })() \ No newline at end of file diff --git a/themes/butterfly/layout/includes/widget/card_announcement.pug b/themes/butterfly/layout/includes/widget/card_announcement.pug index 171ec9c..9e63627 100644 --- a/themes/butterfly/layout/includes/widget/card_announcement.pug +++ b/themes/butterfly/layout/includes/widget/card_announcement.pug @@ -3,5 +3,4 @@ if theme.aside.card_announcement.enable .item-headline i.fas.fa-bullhorn.fa-shake span= _p('aside.card_announcement') - .announcement_content!= theme.aside.card_announcement.content - #welcome-info \ No newline at end of file + .announcement_content!= theme.aside.card_announcement.content \ No newline at end of file diff --git a/themes/butterfly/layout/includes/widget/card_tags.pug b/themes/butterfly/layout/includes/widget/card_tags.pug index 49296b7..3f062d1 100644 --- a/themes/butterfly/layout/includes/widget/card_tags.pug +++ b/themes/butterfly/layout/includes/widget/card_tags.pug @@ -9,6 +9,6 @@ if theme.aside.card_tags.enable - limit = limit === 0 ? 0 : limit || 40 if theme.aside.card_tags.color - .card-tag-cloud!= cloudTags({source: site.tags, orderby: orderby, order: order, minfontsize: 1.15, maxfontsize: 1.45, limit: limit, unit: 'em'}) + .card-tag-cloud!= cloudTags({source: site.tags, orderby: orderby, order: order, minfontsize: 1.15, maxfontsize: 1.45, limit: limit, unit: 'em', page: 'index'}) else .card-tag-cloud!= tagcloud({orderby: orderby, order: order, min_font: 1.1, max_font: 1.5, amount: limit , color: true, start_color: '#999', end_color: '#99a9bf', unit: 'em'})