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 `
+
+
+ ${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 `
-
-
- ${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'})