diff --git a/themes/Ying/assets/js/modules/artalk.js b/themes/Ying/assets/js/modules/artalk.js
index 9d4959a9..736bafab 100644
--- a/themes/Ying/assets/js/modules/artalk.js
+++ b/themes/Ying/assets/js/modules/artalk.js
@@ -15,12 +15,93 @@ window.initArtalk = function() {
}
try {
- var artalk = Artalk.init(window.artalkConfig);
- window.artalk = artalk; // Expose to window for other scripts
+ // Logic for caching Artalk configuration to speed up rendering
+ // 1. Manual Refresh (F5): Request from server and save to localStorage
+ // 2. PJAX: Load from localStorage if available
+
+ // Initialize initialization count to track PJAX usage
+ if (typeof window.artalkInitCount === 'undefined') {
+ window.artalkInitCount = 0;
+ }
+ window.artalkInitCount++;
+
+ var config = Object.assign({}, window.artalkConfig); // Clone config
+ var server = config.server.replace(/\/$/, '');
+ var confKey = 'artalk_conf_' + btoa(server);
+ var cachedConf = null;
+
+ // Try to read cache
+ try {
+ var raw = localStorage.getItem(confKey);
+ if (raw) cachedConf = JSON.parse(raw);
+ } catch(e) {
+ console.warn('Failed to parse Artalk cache:', e);
+ }
+
+ // Decision: Use Cache or Fetch?
+ // Use cache only if:
+ // 1. Not the first load (PJAX)
+ // 2. Cache exists
+ if (window.artalkInitCount > 1 && cachedConf) {
+ // Merge cache into config
+ // CAUTION: cachedConf contains page-specific data (like pageKey) from the previous page.
+ // We must ensure current window.artalkConfig overwrites it.
+ try {
+ // Defensive copy to avoid mutating cached object
+ var mergedConfig = Object.assign({}, config);
+ Object.assign(mergedConfig, cachedConf);
+ Object.assign(mergedConfig, window.artalkConfig); // Re-apply current page config
+ mergedConfig.useBackendConf = false;
+
+ // Attempt to init with cache
+ window.artalk = Artalk.init(mergedConfig);
+ } catch (initErr) {
+ console.warn('Artalk init with cache failed, falling back to default:', initErr);
+ localStorage.removeItem(confKey); // Clear bad cache
+ window.artalk = Artalk.init(config); // Fallback to normal init
+ }
+ } else {
+ // First load or no cache: Allow Artalk to fetch (useBackendConf defaults to true)
+ window.artalk = Artalk.init(config);
+ }
+
+ var artalk = window.artalk; // Local reference
+
+ // Aggressive Config Saving Strategy
+ var saveConf = function() {
+ try {
+ // Attempt to find the full configuration object
+ // artalk.conf is the standard property in v2+
+ // artalk.ctx.conf is for internal context access
+ var remoteConf = artalk.conf || (artalk.ctx && artalk.ctx.conf);
+
+ if (remoteConf) {
+ // We only really want to cache the fields that come from the server,
+ // but saving the whole object is easier. We just have to be careful when merging.
+ localStorage.setItem(confKey, JSON.stringify(remoteConf));
+ // console.log('Artalk: Config cached to', confKey);
+ }
+ } catch(e) {
+ console.error('Artalk saveConf error:', e);
+ }
+ };
+
+ // Hook into multiple events to ensure we capture the config after it's loaded
+ // 'conf-loaded' is the ideal event if supported
+ artalk.on('conf-loaded', saveConf);
+
+ // 'mounted' is when the widget is ready
+ artalk.on('mounted', saveConf);
+
+ // 'list-loaded' means comments (and likely config) are fetched
+ artalk.on('list-loaded', saveConf);
+
+ // Fallback: Poll check after a few seconds (for slow networks)
+ setTimeout(saveConf, 2000);
+ setTimeout(saveConf, 5000);
// Handle list-loaded event for lazy loading avatars and animations
artalk.on('list-loaded', function() {
- // Restore AI Comment if configured
if (window.artalkConfig.aiComment) {
var ai = window.artalkConfig.aiComment;
var listBody = document.querySelector('.atk-list .atk-list-body');
diff --git a/themes/Ying/assets/js/modules/floating-tools.js b/themes/Ying/assets/js/modules/floating-tools.js
index 5aef7fd3..030b176f 100644
--- a/themes/Ying/assets/js/modules/floating-tools.js
+++ b/themes/Ying/assets/js/modules/floating-tools.js
@@ -119,6 +119,10 @@ window.initFloatingTools = function() {
if (window.artalk && typeof window.artalk.setDarkMode === 'function') {
window.artalk.setDarkMode(newTheme === 'dark');
}
+
+ if (window.showToast) {
+ window.showToast('已切换至 ' + (newTheme === 'dark' ? '深色' : '浅色') + '模式', 'success');
+ }
});
}
diff --git a/themes/Ying/assets/js/modules/utils.js b/themes/Ying/assets/js/modules/utils.js
index 153a4d1b..dd37218f 100644
--- a/themes/Ying/assets/js/modules/utils.js
+++ b/themes/Ying/assets/js/modules/utils.js
@@ -8,9 +8,15 @@ window.isMobile = function() {
};
// Toast Message Helper
-window.showToast = function(text) {
+window.showToast = function(text, type) {
+ type = type || 'info'; // default to info
if (typeof Qmsg !== 'undefined') {
- Qmsg.info(text);
+ // Map common types to Qmsg methods
+ if (typeof Qmsg[type] === 'function') {
+ Qmsg[type](text);
+ } else {
+ Qmsg.info(text);
+ }
} else {
// Fallback custom toast
let toast = document.querySelector('.custom-toast');
@@ -31,16 +37,26 @@ window.showToast = function(text) {
opacity: 0;
transition: opacity 0.3s;
pointer-events: none;
+ display: flex;
+ align-items: center;
+ gap: 8px;
`;
document.body.appendChild(toast);
}
- toast.innerText = text;
+
+ // Simple icon mapping for fallback
+ let icon = '';
+ if (type === 'success') icon = '✅ ';
+ else if (type === 'error') icon = '❌ ';
+ else if (type === 'warning') icon = '⚠️ ';
+
+ toast.innerText = icon + text;
toast.style.opacity = '1';
if (window.toastTimeout) clearTimeout(window.toastTimeout);
window.toastTimeout = setTimeout(() => {
toast.style.opacity = '0';
- }, 2000);
+ }, 2500);
}
};
@@ -64,7 +80,7 @@ window.initThemeToggle = function() {
html.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
- window.showToast('已切换至 ' + (newTheme === 'dark' ? '深色' : '浅色') + '模式');
+ window.showToast('已切换至 ' + (newTheme === 'dark' ? '深色' : '浅色') + '模式', 'success');
// Trigger generic resize event to force charts/etc to redraw if needed
window.dispatchEvent(new Event('resize'));
@@ -149,12 +165,12 @@ window.initCodeCopy = function() {
if (navigator.clipboard) {
navigator.clipboard.writeText(code).then(() => {
- window.showToast('代码已复制 / Copied!');
+ window.showToast('代码已复制 / Copied!', 'success');
btn.innerHTML = '';
setTimeout(() => btn.innerHTML = '', 2000);
}).catch(err => {
console.error('Copy failed', err);
- window.showToast('复制失败 / Failed');
+ window.showToast('复制失败 / Failed', 'error');
});
} else {
// Fallback
@@ -164,7 +180,7 @@ window.initCodeCopy = function() {
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
- window.showToast('代码已复制 / Copied!');
+ window.showToast('代码已复制 / Copied!', 'success');
}
});