Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ contact:
zhihu:
facebook:
email:
bluesky:

# 首页头像图片
avatar: /images/avatar.png
Expand Down Expand Up @@ -90,3 +91,12 @@ cdn:
enable: false
# CDN 提供者
provider: jsdelivr # 可选 jsdelivr、unpkg、bootcdn

search:
enable: false # 是否启用搜索
type: algolia # 可选:json 或 algolia
placeholder: 输入关键词搜索...
algolia:
appID: ''
apiKey: ''
indexName: ''
2 changes: 1 addition & 1 deletion layout/includes/footer.ejs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<footer>
<div class="site-footer">
<div class="social-container">
<% ['github', 'twitter', 'weibo', 'zhihu', 'facebook', 'email', 'rss'].forEach((item) => { %>
<% ['github', 'twitter', 'weibo', 'zhihu', 'facebook', 'email', 'rss', 'bluesky'].forEach((item) => { %>
<% if (theme.contact[item]) { %>
<a aria-label="跳转至<%= item %>" href="<%= theme.contact[item] %>" target="_blank">
<i class="icon icon-<%= item %>"></i>
Expand Down
7 changes: 4 additions & 3 deletions layout/includes/head.ejs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<%
var lang = config.language || 'zh-CN';
var title = page.title;
// tags, categories, about pages title
if (title === 'tags') {
title = '标签 | '+ config.title;
title = lang === 'en' ? 'Tags | ' + config.title : '标签 | ' + config.title;
} else if (title === 'categories') {
title = '分类归档 | '+ config.title;
title = lang === 'en' ? 'Categories | ' + config.title : '分类归档 | ' + config.title;
} else if (title === 'about') {
title = '关于 | '+ config.title;
title = lang === 'en' ? 'About | ' + config.title : '关于 | ' + config.title;
} else if (!title) {
title = config.title;
}
Expand Down
5 changes: 3 additions & 2 deletions layout/includes/post-list.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@

<% if (page.total > 1) { %>
<div class="pagination-container">
<% var lang = config.language || 'zh-CN'; %>
<% if (page.prev) { %>
<a href="<%- url_for(page.prev_link) %>" class="prev"><i class="icon icon-arrow-ios-back-outline"></i> 上一页</a>
<a href="<%- url_for(page.prev_link) %>" class="prev"><i class="icon icon-arrow-ios-back-outline"></i> <%= lang === 'en' ? 'Prev' : '上一页' %></a>
<% } %>
<% if (page.next) { %>
<a href="<%- url_for(page.next_link) %>" class="next">下一页 <i class="icon icon-arrow-ios-forward-outline"></i></a>
<a href="<%- url_for(page.next_link) %>" class="next"><%= lang === 'en' ? 'Next' : '下一页' %> <i class="icon icon-arrow-ios-forward-outline"></i></a>
<% } %>
</div>
<% } %>
3 changes: 2 additions & 1 deletion layout/post.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
<div class="next-post">
<a class="purple-link" href="<%- url_for(page.next.path) %>">
<h3 class="post-title">
下一篇:<%= page.next.title %>
<% var lang = config.language || 'zh-CN'; %>
<%= lang === 'en' ? 'Next post: ' : '下一篇:' %><%= page.next.title %>
</h3>
</a>
</div>
Expand Down
3 changes: 2 additions & 1 deletion layout/tags.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
<div class="content-container">

<div class="tags-container">
<% var lang = config.language || 'zh-CN'; %>
<% if (site.tags.length <= 0) { %>
<a class="tag" href="">暂无标签</a>
<a class="tag" href=""><%= lang === 'en' ? 'No tags yet' : '暂无标签' %></a>
<% } %>
<% site.tags.forEach((tag) => { %>
<a class="tag" href="<%= url_for(tag.path) %>"><%= tag.name %></a>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hexo-theme-minimalism",
"version": "1.4.3",
"version": "1.4.4",
"private": false,
"description": "a minimalist Hexo theme that is simple yet elegant, enabling your blog content to shine. Its powerful features also help you create a personalized blog that truly stands out.",
"scripts": {
Expand Down
10 changes: 7 additions & 3 deletions scripts/helper/export-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ hexo.extend.helper.register('export_config', function() {

const {theme} = this;

const { image } = Object.assign({image: {
const { image, search } = Object.assign({image: {
lazyload_enable: true,
photo_zoom: 'simple-lightbox'
}}, theme.config);
}, search: {
enable: false
}}, theme);

const theme_config = {
image: image
image: image,
search: search,
language: this.config.language
};

const script = `<script id="hexo-configurations">
Expand Down
Binary file modified source/fonts/iconfont.eot
Binary file not shown.
2 changes: 2 additions & 0 deletions source/fonts/iconfont.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified source/fonts/iconfont.ttf
Binary file not shown.
Binary file modified source/fonts/iconfont.woff
Binary file not shown.
Binary file modified source/fonts/iconfont.woff2
Binary file not shown.
142 changes: 142 additions & 0 deletions source/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,145 @@ window.addEventListener('DOMContentLoaded', () => {
window.addEventListener('hexo-blog-decrypt', () => {
themeBoot();
});


// 搜索弹窗交互与搜索逻辑
(function() {
const theme_config = window.theme_config || {};
if (!theme_config || !theme_config.search || !theme_config.search.enable) {
return;
}
const language = theme_config.language || 'zh-CN';

// 动态插入搜索弹窗结构到 body
const modalHtml = `
<div id="search-modal" class="search-modal" style="display:none;">
<div class="search-modal-mask"></div>
<div class="search-modal-content">
<span class="search-modal-close" id="search-modal-close">&times;</span>
<div id="search-container">
<input type="text" id="search-input" placeholder="${theme_config.search.placeholder || (language === 'en' ? 'Enter keyword search...' : '输入关键词搜索...')}">
<ul id="search-results"></ul>
</div>
</div>
</div>
`;
const temp = document.createElement('div');
temp.innerHTML = modalHtml;
document.body.appendChild(temp.firstElementChild);

// 创建右下角悬浮按钮
const floatBtn = document.createElement('button');
floatBtn.id = 'search-float-btn';
floatBtn.title = language === 'en' ? 'Search' : '搜索';
const searchIcon = '<svg width="22" height="22" viewBox="0 0 24 24"><circle cx="11" cy="11" r="8" stroke="#409eff" stroke-width="2" fill="none"/><line x1="17" y1="17" x2="21" y2="21" stroke="#409eff" stroke-width="2" stroke-linecap="round"/></svg>';
const closeIcon = '<svg width="22" height="22" viewBox="0 0 24 24"><line x1="6" y1="6" x2="18" y2="18" stroke="#f56c6c" stroke-width="2" stroke-linecap="round"/><line x1="18" y1="6" x2="6" y2="18" stroke="#f56c6c" stroke-width="2" stroke-linecap="round"/></svg>';
floatBtn.innerHTML = searchIcon;
document.body.appendChild(floatBtn);

const modal = document.getElementById('search-modal');
const closeBtn = document.getElementById('search-modal-close');
const mask = document.querySelector('.search-modal-mask');

// 搜索数据缓存
let posts = [];
let algoliaLoaded = false;
let algoliaIndex = null;

// 搜索类型
const searchType = theme_config.search.type || 'json';

// 打开弹窗时绑定 input 事件
function openModal() {
modal.style.display = 'flex';
floatBtn.innerHTML = closeIcon;
const input = document.getElementById('search-input');
const results = document.getElementById('search-results');
setTimeout(() => { input && input.focus(); }, 100);
// 解绑旧事件,防止多次绑定
input.oninput = null;
if (searchType === 'algolia') {
if (!algoliaLoaded) {
const appId = theme_config.search.algolia.appID;
const apiKey = theme_config.search.algolia.apiKey;
const indexName = theme_config.search.algolia.indexName;
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/algoliasearch@4/dist/algoliasearch-lite.umd.js';
script.onload = function() {
// eslint-disable-next-line no-undef
const client = algoliasearch(appId, apiKey);
algoliaIndex = client.initIndex(indexName);
algoliaLoaded = true;
};
document.body.appendChild(script);
}
input.oninput = function() {
if (!algoliaIndex) return;
const keyword = this.value.trim();
renderResults(results, []);
if (!keyword) return;
algoliaIndex.search(keyword).then(({ hits }) => {
renderResults(results, hits);
});
};
} else {
// 本地 JSON
if (posts.length === 0) {
fetch('/search.json')
.then(response => response.json())
.then(data => { posts = data; });
}
input.oninput = function() {
const keyword = this.value.trim().toLowerCase();
if (!keyword) return renderResults(results, []);
const filtered = posts.filter(post =>
(post.title && post.title.toLowerCase().includes(keyword))
|| (post.content && post.content.toLowerCase().includes(keyword))
);
renderResults(results, filtered);
};
}
}

function closeModal() {
modal.style.display = 'none';
floatBtn.innerHTML = searchIcon;
const input = document.getElementById('search-input');
const results = document.getElementById('search-results');
if (input) input.value = '';
if (results) results.innerHTML = '';
}

function renderResults(results, list) {
results.innerHTML = '';
if (!list.length) {
const text = theme_config.language === 'en' ? 'No results found' : '未找到结果';
results.innerHTML = '<li style="color:#888;padding:1em;">' + text + '</li>';
return;
}
list.forEach(item => {
let summary = '';
if (item.content) {
const clean = item.content.replace(/<[^>]+>/g, '').replace(/\n/g, '');
summary = clean.length > 80 ? clean.slice(0, 80) + '...' : clean;
}
const li = document.createElement('li');
li.innerHTML = `<a href="${item.url || item.permalink}" target="_blank"><div class="search-title">${item.title}</div><div class="search-summary">${summary}</div></a>`;
results.appendChild(li);
});
}

// 悬浮按钮切换弹窗显示/隐藏
floatBtn.onclick = function() {
if (modal.style.display === 'flex') {
closeModal();
} else {
openModal();
}
};
if (closeBtn) closeBtn.onclick = closeModal;
if (mask) mask.onclick = closeModal;
window.addEventListener('keydown', e => {
if (e.key === 'Escape') closeModal();
});
}());
4 changes: 4 additions & 0 deletions source/style/_blocks/fonts.styl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
-moz-osx-font-smoothing: grayscale;
}

.icon-bluesky:before {
content: "\ea98";
}

.icon-rss:before {
content: "\e600";
}
Expand Down
Loading
Loading