Hexo Shoka Theme Waline Comment System Upgrade Report
Project Overview
This report documents in detail how to integrate the Waline v2 comment system into the Hexo Shoka theme. The original theme only supported the MiniValine comment system; it has now been successfully extended to support both Waline and Valine, with dynamic switching implemented.
Upgrade Date: February 2026
Waline Version: v2 (UMD format)
Theme: Hexo Shoka
Server Address: https://waline.vull.top
Technical Decisions
Why Waline v2 Instead of v3?
During integration, we initially tried using Waline v3 but encountered module compatibility issues:
- Issue: Waline v3 uses the ES Module format, which is incompatible with the Shoka theme’s UMD loader
- Solution: Downgrade to Waline v2, which provides full UMD support
- CDN Path:
npm/@waline/client@v2/dist/waline.js
Architecture Design
A conditional loading strategy is used to dynamically select the comment system based on the config file:
if (CONFIG.waline && CONFIG.waline.enable) {
// Load Waline
} else {
// Load Valine (default)
}
Modified Files List
A total of 6 files were modified, adding approximately 97 lines of code:
1. themes/shoka/_config.yml (Config File)
Changes:
- Added a Waline configuration block
- Configured Waline CDN resource paths
Key Configuration:
# Waline comment system
waline:
enable: true
serverURL: https://waline.vull.top
locale:
placeholder: 请文明评论呀
commentCount: false
pageview: false
lang: zh-CN
search: false
reaction: false
imageUploader: false
requiredMeta:
- nick
vendors:
css:
waline: npm/@waline/client@v2/dist/waline.css
js:
waline: npm/@waline/client@v2/dist/waline.jsxxxxxxxxxx cateCards: - slug: Study cover: Study/cover.jpg - slug: Life cover: Life/cover.jpgyml
enable: Enable/disable Waline (set tofalseto fall back to Valine)serverURL: Waline server addresslocale.placeholder: Comment box placeholder textlang: Interface language (zh-CN/en-US)search: Sticker search featurereaction: Article reaction featureimageUploader: Image upload featurerequiredMeta: Required fields (nick/mail/link)
2. themes/shoka/scripts/generaters/script.js (Backend Config Generation)
Changes:
- Extended the
CONFIGobject to add Waline configuration - Added Waline resource paths to the
jsandcssobjects
Code Changes:
var siteConfig = {
// ... other config
js: {
valine: theme.vendors.js.valine,
waline: theme.vendors.js.waline, // new
// ...
},
css: {
valine: theme.css + "/comment.css",
waline: theme.vendors.css.waline, // new
// ...
},
valine: theme.valine,
waline: theme.waline, // new
};Purpose: Passes the Waline configuration from _config.yml to the frontend JavaScript
3. themes/shoka/layout/_partials/layout.njk (Template File)
Changes:
- Added a
walineflag to theLOCALobject - Implemented conditional rendering logic
Code Changes:
{%- if theme.widgets.recent_comments or page.comment !== false %}
{%- if theme.waline and theme.waline.enable %}
LOCAL.waline = {{ page.waline|safedump if page.waline else "true" }},
{%- else %}
LOCAL.valine = {{ page.valine|safedump if page.valine else "true" }},
{%- endif %}
{%- endif %}
Purpose: Determines whether to pass the waline or valine flag to the frontend based on the theme configuration
4. themes/shoka/source/js/_app/pjax.js (Core Initialization Logic)
Changes:
- Implemented Waline initialization logic
- Added error handling and debug logging
- Maintained compatibility with Valine
Complete Code:
const siteRefresh = function (reload) {
// ... other code
// Support Waline and Valine
if (CONFIG.waline && CONFIG.waline.enable) {
vendorCss('waline');
vendorJs('waline', function() {
var options = Object.assign({}, CONFIG.waline);
options = Object.assign(options, LOCAL.waline||{});
// Waline v2 uses Waline.init()
if (typeof Waline !== 'undefined') {
try {
Waline.init({
el: '#comments',
serverURL: options.serverURL,
path: LOCAL.path,
lang: options.lang || 'zh-CN',
locale: options.locale,
emoji: options.emoji,
meta: options.meta || ['nick', 'mail', 'link'],
requiredMeta: options.requiredMeta || [],
pageSize: options.pageSize || 10,
wordLimit: options.wordLimit,
login: options.login || 'enable',
pageview: options.pageview || false,
comment: options.commentCount || false,
search: options.search !== undefined ? options.search : false,
reaction: options.reaction !== undefined ? options.reaction : false,
imageUploader: options.imageUploader !== undefined ? options.imageUploader : false
});
console.log('Waline 初始化成功');
} catch (e) {
console.error('Waline 初始化失败:', e);
}
} else {
console.error('Waline 未加载');
}
setTimeout(function(){
positionInit(1);
postFancybox('.wl-content');
}, 1000);
}, window.Waline);
} else {
// Valine initialization logic (unchanged)
vendorJs('valine', function() {
var options = Object.assign({}, CONFIG.valine);
options = Object.assign(options, LOCAL.valine||{});
options.el = '#comments';
options.pathname = LOCAL.path;
options.pjax = pjax;
options.lazyload = lazyload;
new MiniValine(options);
setTimeout(function(){
positionInit(1);
postFancybox('.v');
}, 1000);
}, window.MiniValine);
}
// ... other code
}Key Technical Points:
- Config merging: Uses
Object.assign()to merge global and page-level configuration - API call: Waline v2 uses
Waline.init()instead ofnew Waline() - Error handling: Added try-catch and type checks
- Delayed initialization: Uses
setTimeoutto ensure the DOM is fully loaded - Fancybox integration: Enables the image lightbox for Waline comment content (
.wl-content)
5. themes/shoka/source/js/_app/page.js (Comment Loading Logic)
Changes:
- Modified the
loadComments()function to support dynamic comment system detection - Used IntersectionObserver for lazy loading
Code Changes:
const loadComments = function () {
var element = $('#comments');
if (!element) {
goToComment.display("none")
return;
} else {
goToComment.display("")
}
// Support Waline and Valine
var commentType = CONFIG.waline && CONFIG.waline.enable ? 'waline' : 'valine';
if (!window.IntersectionObserver) {
vendorCss(commentType);
} else {
var io = new IntersectionObserver(function(entries, observer) {
var entry = entries[0];
vendorCss(commentType);
if (entry.isIntersecting || entry.intersectionRatio > 0) {
transition($('#comments'), 'bounceUpIn');
observer.disconnect();
}
});
io.observe(element);
}
}Optimizations:
- Dynamically detects the comment system type
- Loads CSS only when the comment area enters the viewport
- Falls back for browsers that don’t support IntersectionObserver
6. themes/shoka/source/css/comment.styl (Style File)
Changes:
- Added Waline-specific styles
New Code:
// Waline style support
.wl-container
margin-top: 2rem
.wl-content
word-break: break-word
.wl-editor
border-radius: 4px
.wl-panel
border-radius: 4pxStyle Description:
.wl-container: Top spacing of the comment container.wl-content: Auto line wrapping for comment content.wl-editor: Rounded corners for the editor.wl-panel: Rounded corners for the panel
Features
✅ Implemented Features
-
Dual comment system support
- Waline v2
- MiniValine (a Valine variant)
- One-click switching via the config file
-
Complete config passing
- Backend config → frontend CONFIG object
- Page-level config overrides global config
-
Lazy loading optimization
- CSS resources loaded on demand
- Uses IntersectionObserver to watch the viewport
-
Error handling
- Catches initialization failures
- Console debug logging
-
Backward compatibility
- Preserves the original Valine configuration
- Seamless switching without affecting existing features
-
Chinese localization
- Custom placeholder text
- Chinese interface language
🔧 Optional Features
Can be enabled/disabled via the config file:
- Comment count (
commentCount) - Page view count (
pageview) - Sticker search (
search) - Article reaction (
reaction) - Image upload (
imageUploader) - Custom stickers (
emoji)
Usage Guide
Enabling Waline
Edit themes/shoka/_config.yml:
waline:
enable: true # Set to true to enable Waline
serverURL: https://your-waline-server.com
Switching Back to Valine
waline:
enable: false # Set to false to disable Waline; Valine is used automatically
Page-Level Configuration
Override global config in the article’s Front Matter:
---
title: 我的文章
waline:
locale:
placeholder: 这篇文章欢迎评论!
---
Disabling Comments
---
title: 关于页面
comment: false # Completely disable comments
---
Testing and Verification
Test Environment
- Browsers: Chrome, Firefox, Safari
- Devices: Desktop, Mobile
- Test pages: Article pages, home page, archive pages
Test Results
✅ Functional testing
- Waline comment box displays correctly
- Comment submission succeeds
- Comment list loads correctly
- Sticker picker works correctly
✅ Compatibility testing
- Switching to Valine works correctly
- PJAX page navigation has no issues
- Image lightbox (Fancybox) works correctly
✅ Performance testing
- CSS/JS lazy loading takes effect
- No blocking on first-screen load
- No errors in the console
Known Issues
⚠️ Server-side issues (not frontend integration issues)
- Occasional 500 errors when registering with the Waline server
- Recommend checking the server configuration and database connection
Technical Challenges and Solutions
Challenge 1: Waline v3 Module Compatibility
Problem: Waline v3 uses ES Modules, while the Shoka theme uses a UMD loader
Solution:
# Use v2 version
vendors:
js:
waline: npm/@waline/client@v2/dist/waline.js
Challenge 2: Missing waline Flag in the LOCAL Object
Problem: The frontend cannot determine whether Waline is enabled
Solution: Add conditional logic in layout.njk
{%- if theme.waline and theme.waline.enable %}
waline: true,
{%- endif %}
Challenge 3: Waline.init() API Confusion
Problem: The documentation mixes v2 and v3 APIs
Solution:
// Correct usage for v2
Waline.init({ el: '#comments', ... })
// Incorrect usage (v3)
// new Waline({ el: '#comments', ... })
Code Statistics
| File | Lines Added | Lines Modified | Lines Deleted |
|---|---|---|---|
_config.yml | 35 | 0 | 0 |
script.js | 3 | 2 | 0 |
layout.njk | 4 | 1 | 1 |
pjax.js | 40 | 5 | 0 |
page.js | 3 | 2 | 0 |
comment.styl | 12 | 0 | 0 |
| Total | 97 | 10 | 1 |
Maintenance Recommendations
Daily Maintenance
-
Regularly update the Waline version
vendors: js: waline: npm/@waline/[email protected]/dist/waline.js -
Monitor console logs
- Check for the “Waline 初始化成功” message
- Watch for error logs
-
Back up config files
_config.ymlmcp.json(if used)
Upgrading to Waline v3 (Future)
When the Shoka theme supports ES Modules:
-
Modify the CDN path:
waline: npm/@waline/client@v3/dist/waline.js -
Update the initialization code:
import { init } from '@waline/client'; init({ el: '#comments', ... });
Reference Resources
- Waline official docs: https://waline.js.org/
- Waline v2 API: https://v2.waline.js.org/
- Hexo Shoka theme: https://github.com/amehime/hexo-theme-shoka
- Project server: https://waline.vull.top
Summary
This upgrade successfully integrated the Waline v2 comment system into the Hexo Shoka theme, achieving the following goals:
✅ Complete functionality: Supports all core Waline features
✅ Backward compatible: Retains Valine support, switchable at any time
✅ Performance optimized: Lazy loads CSS/JS resources
✅ Easy to maintain: Centralized config management with clear code structure
✅ User friendly: Chinese localization and custom placeholders
Upgrade impact: Minimally invasive, only 6 files modified, no breaking changes
User feedback: “The comment system is working properly now” ✅
Report date: February 7, 2026
Report version: 1.0
Maintainer: Vullfin
If you enjoyed this, leave a comment~