没想到这篇笔记哗啦啦一下子写了这么长,脑子嗡嗡的
鉴于人类的本质,这篇笔记也许会一直更新……
因为本人没有任何的代码基础,基本上是现学现卖,所以一定会有很多地方写得非常小白,欢迎大佬指正,就是,轻点喷 qwq
相关颜色变量请换成自己的。
以下所有修改测试于
Stellar 1.29.1
。
温馨提示:道路千万条,安全第一条;魔改不备份,博主两行泪
请您:转载注明出处!转载注明出处!转载注明出处!
给超长代码块增加滚动条#
首先判断代码块是否过长,如果是,则设置最大高度并开启滚动。
新建 source/js/adjust-codeblock-height.js
,添加以下内容:
document.addEventListener("DOMContentLoaded", function() { // 选择所有的.md-text元素 var codeBlocks = document.querySelectorAll('.md-text'); // 遍历每个.md-text元素 codeBlocks.forEach(function(block) { // 检查是否包含.highlight类的子元素,且父元素高度超过500px var highlightBlocks = block.querySelectorAll('.highlight'); highlightBlocks.forEach(function(highlightBlock) { if (highlightBlock.clientHeight > 800) { highlightBlock.style.maxHeight = '300px'; highlightBlock.style.overflow = 'auto'; } }); }); });
以上代码代表如果代码框高度超过 800px,则开启折叠,折叠框最大高度为 300px。其中,可自行设置判断阈值 if (highlightBlock.clientHeight > 800) {
以及折叠后最大高度 highlightBlock.style.maxHeight = '300px';
。
雪花特效#
代码来自这里。我稍微做了一点修改,做成了一个按钮引入到主题中并用 localStorage 记录下雪状态,很简单的代码完美的解决了我的强迫症~
博客已运行x天x小时x分钟#
在网站页脚部分添加一个“博客已运行 x 天 x 小时 x 分钟”字样,显示效果:

代码抄自这里,我为了调整样式加了一行代码 。在
_config.stellar.yml
里添加如下代码,其中 <span class='runtime'>
中的类名 runtime
可自行设置。
footer: ... content: | # 支持 Markdown 格式 <span id="runtime_span"></span> <script type="text/javascript"> function show_runtime() { window.setTimeout("show_runtime()", 1000); X = new Date("2024/01/01 17:00:00"); // 网站开始运行的日期和时间 Y = new Date(); // 当前日期和时间 T = (Y.getTime() - X.getTime()); // 网站运行的总毫秒数 M = 24 * 60 * 60 * 1000; // 一天的毫秒数 a = T / M; // 总天数 A = Math.floor(a); // 总天数的整数部分 b = (a - A) * 24; // 总小时数 B = Math.floor(b); // 总小时数的整数部分 c = (b - B) * 60; // 总分钟数 C = Math.floor((b - B) * 60); // 总分钟数的整数部分 D = Math.floor((c - C) * 60); // 总秒数 runtime_span.innerHTML = "⏱️勉强运行 <span class='runtime'>" + A + "天" + B + "小时" + C + "分" + D + "秒</span>"; } show_runtime(); </script>
再在自定义的 css 文件里添加以下代码,其中 color 可设置为主题色 var(--theme-link)
或自行设置:
.runtime{ font-weight: bold; color: #7F84A7;}
页脚增加猫猫图片#
显示效果:
首先,如果是使用本地图片,将图片上传到主题的资源文件夹,比如 source/asset/posts/keyboard.png
然后在主题配置文件的 _config.stellar.yml
中添加:
footer: ... content: | # 支持 Markdown 格式 <img src="/你的/图片/路径.png" alt="描述文字" style="float: right; width: 60px; margin-left: 20px;">
其中 float: right
限定图片右对齐,width:60px
限制图片大小,可自行调整。
外部链接后面显示图标#
显示效果:

方法一:
老方法依赖 cheerio 模块,可行,但似乎会带来一些网站加载过慢的问题,我现在已经开心地转用新方法了,把老方法摆在这里全当(水)记(字)录(数)。
新建 themes/stellar/scripts/filters/link-icon.js
文件,增加以下代码:
//使用 cheerio 模块在文章中的外部链接后添加一个小图标:npm i cheerio --savehexo.extend.filter.register('after_render:html', function(html, data) { const cheerio = require('cheerio'); const $ = cheerio.load(html, {decodeEntities: false});
// 只选择<article class="md-text content">元素内的<a>标签 $('article.md-text.content a, footer.page-footer.footnote a').each(function() { const link = $(this); const href = link.attr('href'); //排除一些特殊的链接 if (!link.parents('div.tag-plugin.users-wrap').length && !link.parents('div.tag-plugin.sites-wrap').length && !link.parent('div.tag-plugin.ghcard').length && !link.parents('div.tag-plugin.link.dis-select').length && !link.parents('div.tag-plugin.colorful.note').length && !link.parents('div.social-wrap.dis-select').length) {
// 确保链接的 href 属性存在,并检查其是否以 'http' 或 '/' 开头 if (href && (href.startsWith('http') || href.startsWith('/'))) { link.html(link.html() + ` <span style="white-space: nowrap;"><svg width=".7em" height=".7em" viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg"><path d="m13 3l3.293 3.293l-7 7l1.414 1.414l7-7L21 11V3z" fill="currentColor" /><path d="M19 19H5V5h7l-2-2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2h14c1.103 0 2-.897 2-2v-5l-2-2v7z" fill="currentColor"></svg></span>`); //link.attr('target', '_blank'); // 可选:确保链接在新标签页打开 } } });
return $.html(); });
方法二:
用老方法配置完我总觉得使用 Cheerio 模块后会导致网站加载过慢,就又优化了一下。询问 ChatGPT 得知可以考虑不使用 Node.js 的服务器端处理,而是使用纯前端的方法来达到同样的效果,通过在客户端 JavaScript 中添加代码来实现类似的功能,而不是在 Hexo 的后端渲染过程中处理。(好了,可以卸载 cheerio 了)
下面的这段代码可以在页面加载完成后运行,它会查找指定元素中的链接,并在这些链接后添加一个图标。这种方法的好处是,它不需要服务端的处理,所有操作都在用户的浏览器内完成,可以减少服务器负担,并且避免可能因服务器端渲染引起的加载问题。此外,这种方法也提供了更好的用户体验,因为它不会延迟页面内容的显示。
新建source/js/link-icon.js
文件,填入以下内容:
document.addEventListener('DOMContentLoaded', function () { console.log('Document is ready.');
const links = document.querySelectorAll('article.md-text.content a, footer.page-footer.footnote a'); console.log('Links found:', links.length);
links.forEach(function(link) { console.log('Processing link:', link.href);
const parentClasses = ['tag-plugin.users-wrap', 'tag-plugin.sites-wrap', 'tag-plugin.ghcard', 'tag-plugin.link.dis-select', 'tag-plugin.colorful.note', 'social-wrap.dis-select']; let skip = false;
parentClasses.forEach(pc => { if (link.closest(`div.${pc}`)) { skip = true; console.log('Skipping link due to parent class:', pc); } });
if (!skip) { const href = link.getAttribute('href'); console.log('Link href:', href);
if (href && (href.startsWith('http') || href.startsWith('/'))) { link.innerHTML += ` <span style="white-space: nowrap;"><svg width=".7em" height=".7em" viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg"><path d="m13 3l3.293 3.293l-7 7l1.414 1.414l7-7L21 11V3z" fill="currentColor" /><path d="M19 19H5V5h7l-2-2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2h14c1.103 0 2-.897 2-2v-5l-2-2v7z" fill="currentColor"></svg></span>`; console.log('Icon added to link:', link.innerHTML); } } });});
这里做了两个筛选:
const parentClasses = ['tag-plugin.users-wrap', 'tag-plugin.sites-wrap', 'tag-plugin.ghcard', 'tag-plugin.link.dis-select', 'tag-plugin.colorful.note', 'social-wrap.dis-select'];
是被排除的类,可自行增减;if (href && (href.startsWith('http') || href.startsWith('/')))
判断链接是否以http
或/
开头,如果不想给站内链接添加图标的话可以把后面的筛选条件去掉。
然后在主题文件 _config.stellar.yml
中引入:
inject: head: ... - <script src="/js/link-icon.js"></script> # 链接图标
增加参与讨论按钮#
代码抄自星日语,最新主题已自带此功能。
适配 Obsidian Callouts 标注块语法#
显示效果:
暗黑模式下的显示效果:
参考了 Hexo 博客适配 Obsidian 新语法,基础的设置请参考此链接。我暂时用不上其他功能,就把 callout 的样式搬来并做了一些修改。我个人还挺喜欢这个 callout 样式,比 quote 要好看而且添加也很方便,主要是可以和 Obsidian 打通,嘿嘿。
样式修改#
原版的 callouts 标注块样式间距太大,我在此基础上改了 callout_blocks_common.css(不是很懂,写得很烂……但是能用):
:root{--callout-note:68,138,255;--callout-abstract:0,176,255;--callout-info:0,184,212;--callout-tip:0,191,165;--callout-success:8,185,78;--callout-question:224,172,0;--callout-warning:255,145,0;--callout-failure:255,82,82;--callout-danger:255,23,68;--callout-bug:245,0,87;--callout-example:124,77,255;--callout-quote:158,158,158;--callout-radius:6px;--callout-border-opacity:0.5;--callout-title-bg-opacity:0.08}.callout-fold:before{align-self:center;content:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="gray" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="chevron-down"><path d="m6 9 6 6 6-6"/></svg>')}.callout-fold{display:flex;transform:rotate(-90deg);transition:.5s cubic-bezier(.075,.82,.165,1)}.custom-callout[open]>summary>.callout-fold{transform:rotate(0deg)}.custom-callout>summary{border-top-left-radius:var(--callout-radius);border-top-right-radius:var(--callout-radius);cursor:pointer;margin:0;padding:0.5rem 1rem}.custom-callout>summary::marker{content:""}.custom-callout>summary:before{margin-right:.5rem}.custom-callout>summary::-webkit-details-marker{display:none}.callout-title{--fsp: calc(17px - 1px);font-size: var(--fsp);display:flex;justify-content:space-between;font-weight:bold;}.custom-callout>.callout-body{background:transparent!important;border-left:none;margin:0!important;padding:.3rem 1rem;position:relative}
.custom-callout>.callout-body>p{--fsp: calc(17px - 1px);font-size: var(--fsp);margin:8px 0}.custom-callout>.callout-body>pre{margin:1.25rem -1rem}.custom-callout>.callout-body>pre:first-child{margin-top:-.75rem}.custom-callout>.callout-body>pre:last-child{margin-bottom:-.75rem}
.custom-callout.note,.custom-callout.seealso{border-color:rgba(var(--callout-note),var(--callout-border-opacity))}.custom-callout.note>summary,.custom-callout.seealso>summary{ background-color:rgba(var(--callout-note),var(--callout-title-bg-opacity)); color:rgba(var(var(--callout-note)))}
.custom-callout.abstract,.custom-callout.summary,.custom-callout.tldr{border-color:rgba(var(--callout-abstract),var(--callout-border-opacity))}.custom-callout.abstract>summary,.custom-callout.summary>summary,.custom-callout.tldr>summary{ background-color:rgba(var(--callout-abstract),var(--callout-title-bg-opacity)); color:rgba(var(--callout-abstract))}
.custom-callout.info,.custom-callout.todo{border-color:rgba(var(--callout-info),var(--callout-border-opacity))}.custom-callout.info>summary,.custom-callout.todo>summary{ background-color:rgba(var(--callout-info),var(--callout-title-bg-opacity)); color:rgba(var(--callout-info))}
.custom-callout.hint,.custom-callout.important,.custom-callout.tip{border-color:rgba(var(--callout-tip),var(--callout-border-opacity))}.custom-callout.hint>summary,.custom-callout.important>summary,.custom-callout.tip>summary{ background-color:rgba(var(--callout-tip),var(--callout-title-bg-opacity)); color:rgba(var(--callout-tip))}
.custom-callout.check,.custom-callout.done,.custom-callout.success{border-color:rgba(var(--callout-success),var(--callout-border-opacity))}.custom-callout.check>summary,.custom-callout.done>summary,.custom-callout.success>summary{ background-color:rgba(var(--callout-success),var(--callout-title-bg-opacity)); color:rgba(var(--callout-success))}
.custom-callout.faq,.custom-callout.help,.custom-callout.question{border-color:rgba(var(--callout-question),var(--callout-border-opacity))}.custom-callout.faq>summary,.custom-callout.help>summary,.custom-callout.question>summary{ background-color:rgba(var(--callout-question),var(--callout-title-bg-opacity)); color:rgba(var(--callout-question))}
.custom-callout.attention,.custom-callout.caution,.custom-callout.warning{border-color:rgba(var(--callout-warning),var(--callout-border-opacity))}.custom-callout.attention>summary,.custom-callout.caution>summary,.custom-callout.warning>summary{ background-color:rgba(var(--callout-warning),var(--callout-title-bg-opacity)); color:rgba(var(--callout-warning))}
.custom-callout.fail,.custom-callout.failure,.custom-callout.missing{border-color:rgba(var(--callout-failure),var(--callout-border-opacity))}.custom-callout.fail>summary,.custom-callout.failure>summary,.custom-callout.missing>summary{ background-color:rgba(var(--callout-failure),var(--callout-title-bg-opacity)); color:rgba(var(--callout-failure))}
.custom-callout.danger,.custom-callout.error{border-color:rgba(var(--callout-danger),var(--callout-border-opacity))}.custom-callout.danger>summary,.custom-callout.error>summary{ background-color:rgba(var(--callout-danger),var(--callout-title-bg-opacity)); color:rgba(var(--callout-danger))}
.custom-callout.bug{border-color:rgba(var(--callout-bug),var(--callout-border-opacity))}.custom-callout.bug>summary{ background-color:rgba(var(--callout-bug),var(--callout-title-bg-opacity)); color:rgba(var(--callout-bug))}
.custom-callout.example{border-color:rgba(var(--callout-example),var(--callout-border-opacity))}.custom-callout.example>summary{ background-color:rgba(var(--callout-example),var(--callout-title-bg-opacity)); color:rgba(var(--callout-example))}
.custom-callout.cite,.custom-callout.quote{border-color:rgba(var(--callout-quote),var(--callout-border-opacity))}.custom-callout.cite>summary,.custom-callout.quote>summary{ background-color:rgba(var(--callout-quote),var(--callout-title-bg-opacity)); color:rgba(var(--callout-quote))}
.callout-title>.callout-icon+div{-webkit-box-flex:1;-ms-flex:1 1 0%;-webkit-flex:1 1 0%;flex:1 1 0%;margin-left:.25rem}.callout-icon{align-items:center;color:#000;display:flex}.callout-icon:before{height:20px;width:20px}.custom-callout.attention>.callout-title>.callout-icon:before,.custom-callout.caution>.callout-title>.callout-icon:before,.custom-callout.warning>.callout-title>.callout-icon:before{content:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%23FF9100" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-alert-triangle"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3ZM12 9v4M12 17h.01"/></svg>')}.custom-callout.note>.callout-title>.callout-icon:before,.custom-callout.seealso>.callout-title>.callout-icon:before{content:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%23448AFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-pencil"><path d="m18 2 4 4M7.5 20.5 19 9l-4-4L3.5 16.5 2 22z"/></svg>')}.custom-callout.abstract>.callout-title>.callout-icon:before,.custom-callout.summary>.callout-title>.callout-icon:before,.custom-callout.tldr>.callout-title>.callout-icon:before{content:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%2300B0FF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-clipboard-list"><rect x="8" y="2" width="8" height="4" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2M12 11h4M12 16h4M8 11h.01M8 16h.01"/></svg>')}.custom-callout.info>.callout-title>.callout-icon:before,.custom-callout.todo>.callout-title>.callout-icon:before{content:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%2300B8D4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-check-circle-2"><path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"/><path d="m9 12 2 2 4-4"/></svg>')}.custom-callout.hint>.callout-title>.callout-icon:before,.custom-callout.important>.callout-title>.callout-icon:before,.custom-callout.tip>.callout-title>.callout-icon:before{content:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%2300BFA5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-flame"><path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"/></svg>')}.custom-callout.check>.callout-title>.callout-icon:before,.custom-callout.done>.callout-title>.callout-icon:before,.custom-callout.success>.callout-title>.callout-icon:before{content:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%2300C853" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>')}.custom-callout.faq>.callout-title>.callout-icon:before,.custom-callout.help>.callout-title>.callout-icon:before,.custom-callout.question>.callout-title>.callout-icon:before{content:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%23E0AC00" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-help-circle"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3M12 17h.01"/></svg>')}.custom-callout.fail>.callout-title>.callout-icon:before,.custom-callout.failure>.callout-title>.callout-icon:before,.custom-callout.missing>.callout-title>.callout-icon:before{content:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%23FF5252" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-x"><path d="M18 6 6 18M6 6l12 12"/></svg>')}.custom-callout.danger>.callout-title>.callout-icon:before,.custom-callout.error>.callout-title>.callout-icon:before{content:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%23FF1744" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-zap"><path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"/></svg>')}.custom-callout.bug>.callout-title>.callout-icon:before{content:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%23F50057" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-bug"><rect x="8" y="6" width="8" height="14" rx="4"/><path d="m19 7-3 2M5 7l3 2M19 19l-3-2M5 19l3-2M20 13h-4M4 13h4M10 4l1 2M14 4l-1 2"/></svg>')}.custom-callout.example>.callout-title>.callout-icon:before{content:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%237C4DFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-list"><path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01"/></svg>')}.custom-callout.cite>.callout-title>.callout-icon:before,.custom-callout.quote>.callout-title>.callout-icon:before{content:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%239E9E9E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-quote"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1zM15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"/></svg>')}
.custom-callout.note > .callout-body { /* 移除了 background:transparent!important; 改为根据类型变化的背景色 */ background-color: rgba(var(--callout-note), var(--callout-title-bg-opacity)); }
/* 根据不同的类型设置背景色和文字/图标颜色 */.custom-callout.note, .custom-callout.note > summary { background-color: rgba(var(--callout-note), var(--callout-title-bg-opacity)); }
.custom-callout.abstract > .callout-body { background-color: rgba(var(--callout-abstract), var(--callout-title-bg-opacity)); }
.custom-callout.abstract, .custom-callout.abstract > summary { background-color: rgba(var(--callout-abstract), var(--callout-title-bg-opacity)); }
.custom-callout.info > .callout-body { background-color: rgba(var(--callout-info), var(--callout-title-bg-opacity)); }
.custom-callout.info, .custom-callout.info > summary { background-color: rgba(var(--callout-info), var(--callout-title-bg-opacity)); }
.custom-callout.tip > .callout-body { background-color: rgba(var(--callout-tip), var(--callout-title-bg-opacity)); }
.custom-callout.tip, .custom-callout.tip > summary { background-color: rgba(var(--callout-tip), var(--callout-title-bg-opacity)); }
.custom-callout.success > .callout-body { background-color: rgba(var(--callout-success), var(--callout-title-bg-opacity)); }
.custom-callout.success, .custom-callout.success > summary { background-color: rgba(var(--callout-success), var(--callout-title-bg-opacity)); }
.custom-callout.question > .callout-body { background-color: rgba(var(--callout-question), var(--callout-title-bg-opacity)); }
.custom-callout.question, .custom-callout.question > summary { background-color: rgba(var(--callout-question), var(--callout-title-bg-opacity)); }
.custom-callout.warning > .callout-body { background-color: rgba(var(--callout-warning), var(--callout-title-bg-opacity)); }
.custom-callout.warning, .custom-callout.warning > summary { background-color: rgba(var(--callout-warning), var(--callout-title-bg-opacity)); }
.custom-callout.failure > .callout-body { background-color: rgba(var(--callout-failure), var(--callout-title-bg-opacity)); }
.custom-callout.failure, .custom-callout.failure > summary { background-color: rgba(var(--callout-failure), var(--callout-title-bg-opacity)); }
.custom-callout.danger > .callout-body { background-color: rgba(var(--callout-danger), var(--callout-title-bg-opacity)); }
.custom-callout.danger, .custom-callout.danger > summary { background-color: rgba(var(--callout-danger), var(--callout-title-bg-opacity)); }
.custom-callout.bug > .callout-body { background-color: rgba(var(--callout-bug), var(--callout-title-bg-opacity)); }
.custom-callout.bug, .custom-callout.bug > summary { background-color: rgba(var(--callout-bug), var(--callout-title-bg-opacity)); }
.custom-callout.example > .callout-body { background-color: rgba(var(--callout-example), var(--callout-title-bg-opacity)); }
.custom-callout.example, .custom-callout.example > summary { background-color: rgba(var(--callout-example), var(--callout-title-bg-opacity)); }
.custom-callout.quote > .callout-body { background-color: rgba(var(--callout-quote), var(--callout-title-bg-opacity)); }
.custom-callout.quote, .custom-callout.quote > summary { background-color: rgba(var(--callout-quote), var(--callout-title-bg-opacity)); }
.custom-callout.cite > .callout-body { background-color: rgba(var(--callout-quote), var(--callout-title-bg-opacity)); }
.custom-callout.cite, .custom-callout.cite > summary { background-color: rgba(var(--callout-quote), var(--callout-title-bg-opacity)); }
.custom-callout.todo > .callout-body { background-color: rgba(var(--callout-info), var(--callout-title-bg-opacity)); }
.custom-callout.todo, .custom-callout.todo > summary { background-color: rgba(var(--callout-info), var(--callout-title-bg-opacity)); }
.custom-callout.seealso > .callout-body { background-color: rgba(var(--callout-note), var(--callout-title-bg-opacity)); }
.custom-callout.seealso, .custom-callout.seealso > summary { background-color: rgba(var(--callout-note), var(--callout-title-bg-opacity)); }
.custom-callout.hint > .callout-body { background-color: rgba(var(--callout-tip), var(--callout-title-bg-opacity)); }
.custom-callout.hint, .custom-callout.hint > summary { background-color: rgba(var(--callout-tip), var(--callout-title-bg-opacity)); }
.custom-callout.important > .callout-body { background-color: rgba(var(--callout-tip), var(--callout-title-bg-opacity)); }
.custom-callout.important, .custom-callout.important > summary { background-color: rgba(var(--callout-tip), var(--callout-title-bg-opacity)); }
.custom-callout.attention > .callout-body { background-color: rgba(var(--callout-warning), var(--callout-title-bg-opacity)); }
.custom-callout.attention, .custom-callout.attention > summary { background-color: rgba(var(--callout-warning), var(--callout-title-bg-opacity)); }
.custom-callout.caution > .callout-body { background-color: rgba(var(--callout-warning), var(--callout-title-bg-opacity)); }
.custom-callout.caution, .custom-callout.caution > summary { background-color: rgba(var(--callout-warning), var(--callout-title-bg-opacity)); }
.custom-callout.done > .callout-body { background-color: rgba(var(--callout-success), var(--callout-title-bg-opacity)); }
.custom-callout.done, .custom-callout.done > summary { background-color: rgba(var(--callout-success), var(--callout-title-bg-opacity)); }
.custom-callout.check > .callout-body { background-color: rgba(var(--callout-success), var(--callout-title-bg-opacity)); }
.custom-callout.check, .custom-callout.check > summary { background-color: rgba(var(--callout-success), var(--callout-title-bg-opacity)); }
.custom-callout.faq > .callout-body { background-color: rgba(var(--callout-question), var(--callout-title-bg-opacity)); }
.custom-callout.faq, .custom-callout.faq > summary { background-color: rgba(var(--callout-question), var(--callout-title-bg-opacity)); }
.custom-callout.help > .callout-body { background-color: rgba(var(--callout-question), var(--callout-title-bg-opacity)); }
.custom-callout.help, .custom-callout.help > summary { background-color: rgba(var(--callout-question), var(--callout-title-bg-opacity)); }
.custom-callout.fail > .callout-body { background-color: rgba(var(--callout-failure), var(--callout-title-bg-opacity)); }
.custom-callout.fail, .custom-callout.fail > summary { background-color: rgba(var(--callout-failure), var(--callout-title-bg-opacity)); }
.custom-callout.missing > .callout-body { background-color: rgba(var(--callout-failure), var(--callout-title-bg-opacity)); }
.custom-callout.missing, .custom-callout.missing > summary { background-color: rgba(var(--callout-failure), var(--callout-title-bg-opacity)); }
.custom-callout.error > .callout-body { background-color: rgba(var(--callout-danger), var(--callout-title-bg-opacity)); }
.custom-callout.error, .custom-callout.error > summary { background-color: rgba(var(--callout-danger), var(--callout-title-bg-opacity)); }
.custom-callout.tldr > .callout-body { background-color: rgba(var(--callout-abstract), var(--callout-title-bg-opacity)); }
.custom-callout.tldr, .custom-callout.tldr > summary { background-color: rgba(var(--callout-abstract), var(--callout-title-bg-opacity)); }
集成 Telegram Channel 说说#
显示效果:
篇幅限制,只展示2条,请耐心等待加载。(可能要挂代理)
代码抄自把Tg Channel接入到Stellar时间线。因为我懒得做标签筛选所以直接把这个去掉啦,在此还要感谢佬的耐心解答
GitHub Action 自动部署并修复更新时间#
在自动部署这里遇到了几个坑,总结下来大概有下:
- 网上流行的很多 yml workflow 文件都有些过时
- 公钥私钥啥的不太懂,配置了半天
- 因为我的博客有数学公式显示,所以要在 workflow 里加入安装 pandoc 的部分,才能够成功运行
- 自动部署后网站的文章更新时间全部变成 push 时间,但在本地是正常的。一番搜索后找到了解决方法,在 yml 文件里加入了以下代码,分别修复 posts、wiki、notes 的更新时间:
- name: Restore file modification time 🕒 run: find source/_posts -name '*.md' | while read file; do touch -d "$(git log -1 --format="@%ct" "$file")" "$file"; done
- name: Restore file modification time of wiki🕒 run: find source/wiki -name '*.md' | while read file; do touch -d "$(git log -1 --format="@%ct" "$file")" "$file"; done
- name: Restore file modification time of notes🕒 run: find source/notes -name '*.md' | while read file; do touch -d "$(git log -1 --format="@%ct" "$file")" "$file"; done
最后附上完整代码,拿去用的话要自己配置一下 GitHub 部分的设置:
name: auto deploy
on: workflow_dispatch: push:
jobs: build: runs-on: ubuntu-latest # 运行环境为最新版 Ubuntu name: auto deploy steps: # 1. 获取源码 - name: Checkout uses: actions/checkout@v4 # 使用 actions/checkout@v3 with: # 条件 submodules: true # Checkout private submodules(themes or something else). 当有子模块时切换分支? fetch-depth: 0
- name: Restore file modification time 🕒 run: find source/_posts -name '*.md' | while read file; do touch -d "$(git log -1 --format="@%ct" "$file")" "$file"; done
- name: Restore file modification time of wiki🕒 run: find source/wiki -name '*.md' | while read file; do touch -d "$(git log -1 --format="@%ct" "$file")" "$file"; done
- name: Restore file modification time of notes🕒 run: find source/notes -name '*.md' | while read file; do touch -d "$(git log -1 --format="@%ct" "$file")" "$file"; done
# 2. 配置环境 - name: Setup Node.js 18.19.x uses: actions/setup-node@master with: node-version: "18.19.x"
- name: Install pandoc run: | cd /tmp wget -c https://github.com/jgm/pandoc/releases/download/2.14.0.3/pandoc-2.14.0.3-1-amd64.deb sudo dpkg -i pandoc-2.14.0.3-1-amd64.deb
# 3. 生成静态文件 - name: Generate Public Files run: | npm i npm install hexo-cli -g hexo clean && hexo generate # 4a. 部署到 GitHub 仓库(可选) - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v3 with: deploy_key: ${{ secrets.HEXO_DEPLOY_PRI }} # 配置密钥 external_repository: # 填入你的GitHub pages部署仓库 publish_branch: gt-pages # 填入部署分支 publish_dir: ./public commit_message: ${{ github.event.head_commit.message }} user_name: 'github-actions[bot]' user_email: 'github-actions[bot]@users.noreply.github.com'
给博客添加地理定位并制作个性欢迎#
显示效果:

代码来自给博客添加腾讯地图定位并制作个性欢迎。我稍微做了一点调整:
新建 source/js/services/txmap.js
,并添加以下代码:
{% folding 点击展开代码 %}
//get请求$.ajax({ type: 'get', url: 'https://apis.map.qq.com/ws/location/v1/ip', data: { key: '你的key', output: 'jsonp', }, dataType: 'jsonp', success: function (res) { ipLoacation = res; }})function getDistance(e1, n1, e2, n2) { const R = 6371 const { sin, cos, asin, PI, hypot } = Math let getPoint = (e, n) => { e *= PI / 180 n *= PI / 180 return { x: cos(n) * cos(e), y: cos(n) * sin(e), z: sin(n) } }
let a = getPoint(e1, n1) let b = getPoint(e2, n2) let c = hypot(a.x - b.x, a.y - b.y, a.z - b.z) let r = asin(c / 2) * 2 * R return Math.round(r);}
function showWelcome() {
let dist = getDistance(113.34499552, 23.15537143, ipLoacation.result.location.lng, ipLoacation.result.location.lat); //这里换成自己的经纬度 let pos = ipLoacation.result.ad_info.nation; let ip; let posdesc; //根据国家、省份、城市信息自定义欢迎语 switch (ipLoacation.result.ad_info.nation) { case "日本": posdesc = "よろしく,一起去看樱花吗"; break; case "美国": posdesc = "Let us live in peace!"; break; case "英国": posdesc = "想同你一起夜乘伦敦眼"; break; case "俄罗斯": posdesc = "干了这瓶伏特加!"; break; case "法国": posdesc = "C'est La Vie"; break; case "德国": posdesc = "Die Zeit verging im Fluge."; break; case "澳大利亚": posdesc = "一起去大堡礁吧!"; break; case "加拿大": posdesc = "拾起一片枫叶赠予你"; break; case "中国": pos = ipLoacation.result.ad_info.province + " " + ipLoacation.result.ad_info.city + " " + ipLoacation.result.ad_info.district; ip = ipLoacation.result.ip; switch (ipLoacation.result.ad_info.province) { case "北京市": posdesc = "北——京——欢迎你~~~"; break; case "天津市": posdesc = "讲段相声吧。"; break; case "河北省": posdesc = "山势巍巍成壁垒,天下雄关。铁马金戈由此向,无限江山。"; break; case "山西省": posdesc = "展开坐具长三尺,已占山河五百余。"; break; case "内蒙古自治区": posdesc = "天苍苍,野茫茫,风吹草低见牛羊。"; break; case "辽宁省": posdesc = "我想吃烤鸡架!"; break; case "吉林省": posdesc = "状元阁就是东北烧烤之王。"; break; case "黑龙江省": posdesc = "很喜欢哈尔滨大剧院。"; break; case "上海市": posdesc = "众所周知,中国只有两个城市。"; break; case "江苏省": switch (ipLoacation.result.ad_info.city) { case "南京市": posdesc = "这是我挺想去的城市啦。"; break; case "苏州市": posdesc = "上有天堂,下有苏杭。"; break; default: posdesc = "散装是必须要散装的。"; break; } break; case "浙江省": posdesc = "东风渐绿西湖柳,雁已还人未南归。"; break; case "河南省": switch (ipLoacation.result.ad_info.city) { case "郑州市": posdesc = "豫州之域,天地之中。"; break; case "南阳市": posdesc = "臣本布衣,躬耕于南阳。此南阳非彼南阳!"; break; case "驻马店市": posdesc = "峰峰有奇石,石石挟仙气。嵖岈山的花很美哦!"; break; case "开封市": posdesc = "刚正不阿包青天。"; break; case "洛阳市": posdesc = "洛阳牡丹甲天下。"; break; default: posdesc = "可否带我品尝河南烩面啦?"; break; } break; case "安徽省": posdesc = "蚌埠住了,芜湖起飞。"; break; case "福建省": posdesc = "井邑白云间,岩城远带山。"; break; case "江西省": posdesc = "落霞与孤鹜齐飞,秋水共长天一色。"; break; case "山东省": posdesc = "遥望齐州九点烟,一泓海水杯中泻。"; break; case "湖北省": posdesc = "来碗热干面!"; break; case "湖南省": posdesc = "74751,长沙斯塔克。"; break; case "广东省": posdesc = "老板来两斤福建人。"; break; case "广西壮族自治区": posdesc = "桂林山水甲天下。"; break; case "海南省": posdesc = "朝观日出逐白浪,夕看云起收霞光。"; break; case "四川省": posdesc = "康康川妹子。"; break; case "贵州省": posdesc = "茅台,学生,再塞200。"; break; case "云南省": posdesc = "玉龙飞舞云缠绕,万仞冰川直耸天。"; break; case "西藏自治区": posdesc = "躺在茫茫草原上,仰望蓝天。"; break; case "陕西省": posdesc = "来份臊子面加馍。"; break; case "甘肃省": posdesc = "羌笛何须怨杨柳,春风不度玉门关。"; break; case "青海省": posdesc = "牛肉干和老酸奶都好好吃。"; break; case "宁夏回族自治区": posdesc = "大漠孤烟直,长河落日圆。"; break; case "新疆维吾尔自治区": posdesc = "驼铃古道丝绸路,胡马犹闻唐汉风。"; break; case "台湾省": posdesc = "我在这头,大陆在那头。"; break; case "香港特别行政区": posdesc = "永定贼有残留地鬼嚎,迎击光非岁玉。"; break; case "澳门特别行政区": posdesc = "性感荷官,在线发牌。"; break; default: posdesc = "带我去你的城市逛逛吧!"; break; } break; default: posdesc = "带我去你的国家逛逛吧。"; break; }
//根据本地时间切换欢迎语 let timeChange; let date = new Date(); if (date.getHours() >= 5 && date.getHours() < 11) timeChange = "<span>上午好</span>,一日之计在于晨!"; else if (date.getHours() >= 11 && date.getHours() < 13) timeChange = "<span>中午好</span>,该摸鱼吃午饭了。"; else if (date.getHours() >= 13 && date.getHours() < 15) timeChange = "<span>下午好</span>,懒懒地睡个午觉吧!"; else if (date.getHours() >= 15 && date.getHours() < 16) timeChange = "<span>三点几啦</span>,一起饮茶呀!"; else if (date.getHours() >= 16 && date.getHours() < 19) timeChange = "<span>夕阳无限好!</span>"; else if (date.getHours() >= 19 && date.getHours() < 24) timeChange = "<span>晚上好</span>,夜生活嗨起来!"; else timeChange = "夜深了,早点休息,少熬夜。";
try { //自定义文本和需要放的位置 document.getElementById("welcome-info").innerHTML = `<b><center>🎉 欢迎信息 🎉</center>  欢迎来自 <span style="color:var(--theme-color)">${pos}</span> 的小伙伴,${timeChange}您现在距离站长约 <span style="color:var(--theme-color)">${dist}</span> 公里,当前的IP地址为: <span style="color:var(--theme-color)">${ip}</span>, ${posdesc}</b>`; } catch (err) { // console.log("Pjax无法获取#welcome-info元素🙄🙄🙄") }}window.onload = showWelcome;// 如果使用了pjax在加上下面这行代码document.addEventListener('pjax:complete', showWelcome);
{% endfolding %}
在主题文件中配置#
在主题配置文件 _config.stellar.yml
中引入jQuery依赖和刚刚的js文件:
inject: - <script src="https://cdn.staticfile.org/jquery/3.6.3/jquery.min.js"></script> # jQuery - <script async data-pjax src="/js/services/txmap.js"></script> # 腾讯位置API
在 source/_data/widgets.yml
中添加小组件,我在里面嵌套了一个随机文章跳转,不要的话可以删掉,其中,<span id="welcome-info" ></span>
是必须的不可以删:
welcomeloc: layout: markdown title: '🎉 抓到你啦' linklist: columns: 1 items: - icon: '<img src="https://api.iconify.design/ion:dice-outline.svg"/>' title: 随机文章 url: 'javascript:toRandomPost()' content: | <span id="welcome-info" style="font-family: LXGW WenKai Screen;"></span>
然后就跟正常的小组件一样在想要的地方引用即可。
添加更改字体按钮#
显示效果:
第一种:
在任意位置增加一个 button 按钮
鼠标放到上面会显示提示:
第二种: 在文章页面目录下方显示
之前一直纠结要不要把自定义字体效果去掉,在选择和留下之间来回切换 最终才出现了这里的方案:默认不加载任何字体,喜欢 LXGW 字体的话可点击图标转换,同时再点击一下就恢复。代码不长但完美地解决了我的强迫症~
第一步:准备字体文件#
可以是在线文件也可以是本地文件,我是在主题 config 文件下通过 inject 引入了 LXGW 字体。
第二步:修改 css#
首先确保 LXGW WenKai Screen 字体已经通过 CSS 正确引入。你可以在 CSS 文件中添加一个特定的类,用于当用户选择使用这种字体时切换到它:
/* 设置字体 */.LXGWMode { font-family: 'LXGW WenKai Screen', system-ui, 'Helvetica Neue', sans-serif; // 使用 LXGW WenKai 字体,并指定后备字体 }
第三步:添加 javascript#
新建 source/js/changefont.js
文件,添加以下代码:
document.addEventListener('DOMContentLoaded', function () { applyFontSetting(); updateButtonText(); // Ensure the button text is correct on page load});
document.addEventListener('pjax:success', function () { applyFontSetting(); updateButtonText(); // Update the button text after PJAX updates});
function applyFontSetting() { if (localStorage.getItem("LXGWFontEnabled") === "true") { document.body.classList.add("LXGWMode"); } else { document.body.classList.remove("LXGWMode"); }}
function toggleLXGWFont() { var button = document.querySelector('.custom-button'); // Find the button if (localStorage.getItem("LXGWFontEnabled") === "true") { localStorage.setItem("LXGWFontEnabled", "false"); document.body.classList.remove("LXGWMode"); button.innerHTML = '<img src="https://cdn.jsdelivr.net/gh/infinitesum/Twikoo-emoji@master/Blob/ablobcatrainbow.png" alt="Emoji" style="vertical-align: middle; width: 20px; height: 20px;"> 危险,请勿点击'; } else { localStorage.setItem("LXGWFontEnabled", "true"); document.body.classList.add("LXGWMode"); button.innerHTML = '<img src="https://cdn.jsdelivr.net/gh/infinitesum/Twikoo-emoji@master/Blob/ablobcatrainbow.png" alt="Emoji" style="vertical-align: middle; width: 20px; height: 20px;"> 不要说我没有警告过你'; }}
function updateButtonText() { var button = document.querySelector('.custom-button'); // Find the button if (localStorage.getItem("LXGWFontEnabled") === "true") { button.innerHTML = '<img src="https://cdn.jsdelivr.net/gh/infinitesum/Twikoo-emoji@master/Blob/ablobcatrainbow.png" alt="Emoji" style="vertical-align: middle; width: 20px; height: 20px;"> 不要点这里啦!'; } else { button.innerHTML = '<img src="https://cdn.jsdelivr.net/gh/infinitesum/Twikoo-emoji@master/Blob/ablobcatrainbow.png" alt="Emoji" style="vertical-align: middle; width: 20px; height: 20px;"> 危险,请勿点击'; }}
第四步:添加切换按钮#
然后在想要的地方引用即可,可以自行添加各种 emoji,比如:
<button class="custom-button tooltip" onclick="toggleLXGWFont()" data-msg="警告,真的很危险"><img src="https://cdn.jsdelivr.net/gh/infinitesum/Twikoo-emoji@master/Blob/ablobcatrainbow.png" alt="Emoji" style="vertical-align: middle; width: 20px; height: 20px;"> 危险,请勿点击</button>
给按钮加入 css 提示框#
在自定义 css 文件中添加:
.custom-button { display: inline-block; padding: 2px 10px; /*margin: 10px; background-color: #f2f2f2; /* Light grey background, change as needed */ font-family: inherit; /* Inherits the font-family from parent container */ color: #835EEC; background-color: #F2EEFD; @media (prefers-color-scheme: dark) { color: #A28BF2; background-color: #282433; } text-align: center; cursor: pointer; /*border: 2px solid #ccc; /* Grey border */ border-radius: 16px; /* Rounded corners */ transition: all 0.3s ease; }
.custom-button:hover { background-color: #e9e9e9; /* Slightly darker on hover */ @media (prefers-color-scheme: dark) { background-color: #333; /* Darker background on hover */ } border-color: #999; /* Darker border on hover */ }
/* toggle-font 提示框的样式 */
.tooltip { position: relative; cursor: pointer; /* 可选,让用户知道这是一个可以互动的元素 */}
.tooltip:hover::before { white-space: nowrap; line-height: 18px; content: attr(data-msg); position: absolute; padding: 0 8px; display: block; color: #ffffff; background: #656565; border-radius: 6px; font-size: 12px; top: -25px; left: 50%; transform: translateX(-50%); Z-index: 1000; /* 确保提示框在其他元素之上 */}
.tooltip:hover:: after { Content: ""; Position: absolute; Top: -8 px; Left: 50%; Transform: translateX (-50%); Border: 6 px solid transparent; border-top-color: #656565 ; /* 简化写法 */}
/* toggle-font 按钮的样式 */
.widget-wrapper. Toggle-font { Background: none; // Example: making background transparent /* Add other styles specific to the toggle-font widget here */}
第二种样式#
第二种样式需要对主题文件进行一丢丢修改,但貌似不太影响更新……只要无冲突的话可以一直 update fork
在 languages/zh-CN.yml
中添加一行 font: 更改字体
,并在 icons.yml 里添加:
default:font: <svg class="theme-icon" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="m12.677 17.781l-2.626-6.256l-2.694 6.256Zm6.723 6.511h-7.069v-1.365l.458-.023a1.847 1.847 0 0 0 .972-.2a.313.313 0 0 0 .145-.263a4.158 4.158 0 0 0-.419-1.4l-.812-1.931H7.322L6.4 21.259a3.319 3.319 0 0 0-.349 1.157c0 .036 0 .119.154.241a2.481 2.481 0 0 0 1.191.247l.448.033v1.354H2v-1.31l.4-.07a2.188 2.188 0 0 0 1-.318a6.318 6.318 0 0 0 1.18-2.066l5.575-13.036H11.2l5.512 13.174a5.255 5.255 0 0 0 1.049 1.835a1.959 1.959 0 0 0 1.19.4l.454.027Zm6.441-2.732v-3.985a22.542 22.542 0 0 0-2.226.97a3.845 3.845 0 0 0-1.29 1.05a2.03 2.03 0 0 0-.388 1.2a1.951 1.951 0 0 0 .491 1.362a1.49 1.49 0 0 0 1.13.544a4.142 4.142 0 0 0 2.283-1.141m-3.333 2.949a2.833 2.833 0 0 1-2.139-.893a3.206 3.206 0 0 1-.833-2.285a2.959 2.959 0 0 1 .415-1.577a5 5 0 0 1 1.791-1.625a23.876 23.876 0 0 1 3.617-1.588v-.074a2.905 2.905 0 0 0-.383-1.833a1.325 1.325 0 0 0-1.075-.412a1.155 1.155 0 0 0-.816.26a.687.687 0 0 0-.277.536l.023.646a1.62 1.62 0 0 1-.4 1.158a1.481 1.481 0 0 1-2.1-.019a1.634 1.634 0 0 1-.391-1.134a2.8 2.8 0 0 1 1.182-2.177a4.813 4.813 0 0 1 3.125-.932a5.381 5.381 0 0 1 2.508.524a2.628 2.628 0 0 1 1.213 1.346a6.391 6.391 0 0 1 .244 2.2v3.55a14.665 14.665 0 0 0 .051 1.749a.661.661 0 0 0 .054.2c.085-.078.284-.225.864-.806l.819-.828v1.967l-.1.128c-.958 1.283-1.883 1.907-2.83 1.907a1.6 1.6 0 0 1-1.257-.557a1.788 1.788 0 0 1-.358-.74a9.688 9.688 0 0 1-1.433.977a3.579 3.579 0 0 1-1.514.332"/></svg>
在 layout/_partial/widgets/toc.ejs
中,在想要的位置,如 el += editBtn
后,添加以下代码:
el += `<a class="toggle-font" onclick="toggleLXGWFont()">` el += icon('default:font') el += `<span>${__('btn.font')}</span>` el += `</a>`
为了使这个图标随主题明暗自动变化,在自定义 css 文件中加入:
/* 设置图标颜色 *//* 白天模式,默认填充色为黑色 */.theme-icon { fill: black;}
/* 暗黑模式,填充色为白色 */@media (prefers-color-scheme: dark) { .theme-icon { fill: white; }}
随机文章跳转#
要在主题文件夹里新增文件,不影响主题后续更新
终于来到了我最爱的生活哲学!代码参考了这个链接。创建 themes/stellar/scripts/helpers/random.js
,增加以下代码:
hexo.extend.filter.register('after_render:html', function (data) { const posts = [] hexo.locals.get('posts').map(function (post) { if (post.random !== false) posts.push(post.path) }) data += `<script>var posts=${JSON.stringify(posts)};function toRandomPost(){ window.pjax ? pjax.loadUrl('/'+posts[Math.floor(Math.random()*posts.length)]) : window.open('/'+posts[Math.floor(Math.random()*posts.length)], "_self"); };</script>` return data})
在主题配置文件引入 _config.stellar.yml
,inject的 head里添加
- <script src="/js/random.js"></script> # 随机文章
然后在需要调用的位置执行 toRandomPost()
函数即可。比如任意 dom 添加 onclick="toRandomPost()"
反正我当时看完是一脸懵圈
不过没关系,我最后还是琢磨明白啦,下面就有填写示例,接着看就好
添加一个按钮:
代码:<button onclick="toRandomPost()">随机阅读一篇文章</button>
或者添加一个链接: 随机阅读一篇文章
代码:<a href="#" onclick="toRandomPost(); return false;">随机阅读一篇文章</a>
在下一节还有应用示例,请往下看——
超链接样式调整#
文章内链接:加粗并下移下划线。显示效果:

在自定义 css 文件里加入:
/* 文章内链接 */li:not([class]) a:not([class]),p:not([class]) a:not([class]),table a:not([class]) { /*color: var(--theme-link);*/ padding-bottom: 3px; /* 增加底部padding */ padding-right: 1px; margin-right: 2px; background: linear-gradient(0, var(--theme-link), var(--theme-link)) no-repeat center bottom / 100% 2px;}
测试链接:关于
新样式!为链接使用荧光笔下划线效果,这个和上面的样式二选一就好。显示效果:
/* 文章内链接:为链接使用荧光笔下划线效果 */li:not([class]) a:not([class]),p:not([class]) a:not([class]),table a:not([class]) { padding-bottom: 0.1rem; background: linear-gradient(0, var(--theme-link-opa), var(--theme-link-opa)) no-repeat center bottom / 100% 40%;}
选中文本:使用超链接高亮的背景色#
在自定义 css 文件里加入:
/* 选中文本:使用超链接高亮的背景色 */::selection { background: var(--theme-link-opa);}
Twikoo 评论样式优化#
样式优化需要改主题文件,但下面的给评论输入框加入提示是纯 css 实现的不需要改
显示效果:
只截了部分,整体效果可在评论区查看。代码全部抄自星日语大佬的这条 commit。评论区表情显示优化可参考这条 commit。
给评论输入框加入提示#
显示效果:
原始代码忘记在哪里抄的了,我就修改了最后 3 行……在自定义 css 文件中加入以下内容:
/* 设置文字内容 :nth-child(1)的作用是选择第几个 */.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(1):before { content: '输入QQ号会自动获取昵称和头像🐧';}
.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(2):before { content: '收到回复将会发送到您的邮箱📧';}
.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(3):before { content: '填写后可以点击昵称访问您的网站🔗';}
/* 当用户点击输入框时显示 */.el-input.el-input--small.el-input-group.el-input-group--prepend:focus-within::before,.el-input.el-input--small.el-input-group.el-input-group--prepend:focus-within::after { display: block;}
/* 主内容区 */.el-input.el-input--small.el-input-group.el-input-group--prepend::before { /* 先隐藏起来 */ display: none; /* 绝对定位 */ position: absolute; /* 向上移动60像素 */ top: -60px; /* 文字强制不换行,防止left:50%导致的文字换行 */ white-space: nowrap; /* 圆角 */ border-radius: 10px; /* 距离左边50% */ left: 50%; /* 然后再向左边挪动自身的一半,即可实现居中 */ transform: translate(-50%); /* 填充 */ padding: 14px 18px; background: #444; color: #fff;}
/* 小角标 */.el-input.el-input--small.el-input-group.el-input-group--prepend::after { display: none; content: ''; position: absolute; /* 内容大小(宽高)为0且边框大小不为0的情况下,每一条边(4个边)都是一个三角形,组成一个正方形。 我们先将所有边框透明,再给其中的一条边添加颜色就可以实现小三角图标 */ border: 12px solid transparent; border-top-color: #444; left: 50%; transform: translate(-50%, -48px);}
.el-input.el-input--small.el-input-group.el-input-group--prepend::before, .el-input.el-input--small.el-input-group.el-input-group--prepend::after { z-index: 9999; /* 提高层级,确保内容显示在最前 */}
Stellar & Twikoo 表情包补全计划#
blobcat#
这个系列表情真的不要太可爱,一眼爱上
光在博客正文里用怎么够,当然还要在评论区里也安排上
blobcat 表情主要来自星日语佬。本人在学会自定义后收集癖大发,一口气制作了几个系列的表情,往现有的 blobcat里也加了几个比较好看的
Stellar 引入:blobcatplus:https://cdn.jsdelivr.net/gh/infinitesum/Twikoo-emoji@master/Blob/{name}.png
Twikoo 使用链接:
https://cdn.jsdelivr.net/gh/infinitesum/Twikoo-emoji@master/blobcatplus.json
表情 | 索引 | 表情 | 索引 | 表情 | 索引 |
---|---|---|---|---|---|
![]() | ablobcatheart | ![]() | ablobcatheartbroken | ![]() | blobcatheart |
![]() | blobcatheartpride | ![]() | blobcatlove | ![]() | blobcatkissheart |
![]() | blobcatsnuggle | ![]() | comfyuee | ![]() | comfyslep |
![]() | blobcatcomfysweat | ![]() | blobcatcomftears | ![]() | blobcatfacepalm |
![]() | blobcat0_0 | ![]() | blobcatangry | ![]() | blobbanhammerr |
![]() | blobcatt | ![]() | blobcatblush | ![]() | blobcatcoffee |
![]() | blobcatcry | ![]() | blobcatdead | ![]() | blobcatdied |
![]() | blobcatdisturbed | ![]() | blobcatfearful | ![]() | blobcatfingerguns |
![]() | blobcatflip | ![]() | blobcatflower | ![]() | blobcatgay |
![]() | blobcatgooglycry | ![]() | blobcatneutral | ![]() | blobcatopenmouth |
![]() | blobcatsadreach | ![]() | blobcatscared | ![]() | blobcatnomblobcat |
![]() | blobcatpresentred | ![]() | blobcatread | ![]() | blobcatsipsweat |
![]() | blobcatsnapped | ![]() | blobcatthink | ![]() | blobcattriumph |
![]() | blobcatumm | ![]() | blobcatverified | ![]() | blobcatbox |
![]() | blobcatcaged | ![]() | blobcatgooglytrash | ![]() | blobcatheadphones |
![]() | blobcathighfive | ![]() | blobcatmelt | ![]() | blobcatmeltthumb |
![]() | blobcatnotlikethis | ![]() | blobcatsaitama | ![]() | blobcatyandere |
![]() | blobcatpeek2 | ![]() | blobcatpeekaboo | ![]() | blobcatphoto |
![]() | ablobcatattentionreverse | ![]() | ablobcatreachrev | ![]() | ablobcatwave |
![]() | blobcatalt | ![]() | blobcatpolice | ![]() | blobcatshocked |
![]() | ablobcatrainbow | ||||
![]() | A_BlobCat_REEEE | ![]() | A_BlobCat_Code | ![]() | ablobcatknitsweats |
![]() | A_BlobCat_Nervous | ![]() | blobcat-aww | ![]() | ablobcatcry |
![]() | ablobcatdead |
azuki#
Stellar 引入:azuki: https://cdn.jsdelivr.net/gh/Saidosi/azuki-emoji-for-waline@1.0/azukisan/{name}.png
Twikoo 使用链接:
https://cdn.jsdelivr.net/gh/infinitesum/Twikoo-emoji@master/xiaodouni.json
表情 | 索引 | 表情 | 索引 | 表情 | 索引 |
---|---|---|---|---|---|
![]() | 001 | ![]() | 015 | ![]() | 029 |
![]() | 002 | ![]() | 016 | ![]() | 030 |
![]() | 003 | ![]() | 017 | ![]() | 031 |
![]() | 004 | ![]() | 018 | ![]() | 032 |
![]() | 005 | ![]() | 019 | ![]() | 033 |
![]() | 006 | ![]() | 020 | ![]() | 034 |
![]() | 007 | ![]() | 021 | ![]() | 035 |
![]() | 008 | ![]() | 022 | ![]() | 036 |
![]() | 009 | ![]() | 023 | ![]() | 037 |
![]() | 010 | ![]() | 024 | ![]() | 038 |
![]() | 011 | ![]() | 025 | ![]() | 039 |
![]() | 012 | ![]() | 026 | ![]() | 040 |
![]() | 013 | ![]() | 027 | ||
![]() | 014 | ![]() | 028 |
neko#
Stellar 引入:neko: https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/Yurui-Neko/{name}.png
Twikoo 使用链接:
https://cdn.jsdelivr.net/gh/infinitesum/Twikoo-emoji@master/neko.json
表情 | 索引 | 表情 | 索引 | 表情 | 索引 |
---|---|---|---|---|---|
![]() | 001 | ![]() | 015 | ![]() | 028 |
![]() | 002 | ![]() | 016 | ![]() | 029 |
![]() | 003 | ![]() | 017 | ![]() | 030 |
![]() | 004 | ![]() | 018 | ![]() | 031 |
![]() | 005 | ![]() | 019 | ![]() | 032 |
![]() | 006 | ![]() | 020 | ![]() | 033 |
![]() | 007 | ![]() | 021 | ![]() | 034 |
![]() | 008 | ![]() | 022 | ![]() | 035 |
![]() | 009 | ![]() | 023 | ![]() | 036 |
![]() | 010 | ![]() | 024 | ![]() | 037 |
![]() | 011 | ![]() | 025 | ![]() | 038 |
![]() | 012 | ![]() | 026 | ![]() | 039 |
![]() | 013 | ![]() | 027 | ||
![]() | 014 |
dokomo#
Stellar 引入: dokomo: https://cdn.jsdelivr.net/gh/infinitesum/Twikoo-emoji@master/dokomo/{name}.png
Twikoo 使用链接:
https://raw.githubusercontent.com/infinitesum/Twikoo-emoji/main/dokomo/dokomo.json
表情 | 索引 | 表情 | 索引 | 表情 | 索引 |
---|---|---|---|---|---|
![]() | dokomo-1 | ![]() | dokomo-18 | ![]() | dokomo-35 |
![]() | dokomo-2 | ![]() | dokomo-19 | ![]() | dokomo-36 |
![]() | dokomo-3 | ![]() | dokomo-20 | ![]() | dokomo-37 |
![]() | dokomo-4 | ![]() | dokomo-21 | ![]() | dokomo-38 |
![]() | dokomo-5 | ![]() | dokomo-22 | ![]() | dokomo-39 |
![]() | dokomo-6 | ![]() | dokomo-23 | ![]() | dokomo-40 |
![]() | dokomo-7 | ![]() | dokomo-24 | ![]() | dokomo-41 |
![]() | dokomo-8 | ![]() | dokomo-25 | ![]() | dokomo-42 |
![]() | dokomo-9 | ![]() | dokomo-26 | ![]() | dokomo-43 |
![]() | dokomo-10 | ![]() | dokomo-27 | ![]() | dokomo-44 |
![]() | dokomo-11 | ![]() | dokomo-28 | ![]() | dokomo-45 |
![]() | dokomo-12 | ![]() | dokomo-29 | ![]() | dokomo-46 |
![]() | dokomo-13 | ![]() | dokomo-30 | ![]() | dokomo-47 |
![]() | dokomo-14 | ![]() | dokomo-31 | ![]() | dokomo-48 |
![]() | dokomo-15 | ![]() | dokomo-32 | ![]() | dokomo-49 |
![]() | dokomo-16 | ![]() | dokomo-33 | ||
![]() | dokomo-17 | ![]() | dokomo-34 |
总字数统计:“发表了x篇文章,共计x字”#
需要修改主题文件
显示效果:

首先,安装 hexo-wordcount
插件:
npm i hexo-wordcount --save
在 themes/stellar/layout/_partial/main/footer.ejs
中,找到以下部分:
// footer el += '<div class="text">' if (content) { el += markdown(content) }
在以上代码后面添加以下代码:
el += '<span class="totalcount">发表了 ' + site.posts.length + ' 篇文章 · </span><span class="post-count">总计 ' + totalcount(site) + ' 字</span>'
最后的代码看上去应该像这样:
... // footer el += '<div class="text">' if (content) { el += markdown(content) } // 在这里添加帖子总数 el += '<span class="totalcount">发表了 ' + site.posts.length + ' 篇文章 · </span><span class="post-count">总计 ' + totalcount(site) + ' 字</span>' el += '</div></footer>' return el}%><%- layoutDiv() %>
在自定义 css 里,增加以下代码修改风格:
.post-count { scrollbar-width: none; color: var(--text-p4); }
.totalcount { color: var(--text-p4); }
评论区增加可爱猫猫图片#
需要修改主题文件
(这也能水一个)显示效果:
在 themes/stellar/layout/_partial/comments/layout.ejs
,找到下面这一部分:
<div style="display: flex; align-items: center;"> <%- markdown(page.comment_title || theme.comments.comment_title) %>
在其后面添加:
<img src="/你的/图片/路径.png" alt="描述文字" style="margin-left: 10px; height: 50px;">
最后代码应该长这样:
<div style="display: flex; align-items: center;"> <%- markdown(page.comment_title || theme.comments.comment_title) %> <img src="/asset/posts/keyboard.png" alt="描述" style="margin-left: 10px; height: 50px;">
主页文章列表添加标签显示、字数统计#
需要修改主题文件
显示效果:
这里加入了字数统计并将位置移到了上面,要是不想要字数统计可以将相应的代码删掉。
找到 themes/stellar/layout/_partial/main/post_list/post_card.ejs
,删掉原有的代码,替换为以下内容:
<%const poster = post.poster;var obj = { image: post.cover};if (poster) { obj.headline = poster.headline; obj.topic = poster.topic; obj.caption = poster.caption; obj.color = poster.color;}function div_default() { var el = ''; el += '<article class="md-text">';
// 封面 if (obj.image || theme.article.auto_cover) { var cover_url; if (obj.image != undefined) { if (obj.image.includes('/')) { cover_url = obj.image; } else { cover_url = 'https://source.unsplash.com/1280x640/?' + obj.image; } } else { // 自动以 tags 作为关键词搜索封面 if (post.tags) { var params = ''; post.tags.reverse().forEach((tag, i) => { if (i > 0) { params += ','; } params += tag.name; }); cover_url = 'https://source.unsplash.com/1280x640/?' + params; } else { cover_url = 'https://source.unsplash.com/random/1280x640'; } } if (cover_url) { el += '<div class="post-cover">'; el += '<img src="' + cover_url + '"/>'; el += '</div>'; } }
// meta el += '<div class="meta cap">'; el += '<span class="cap" id="post-meta">';
el += icon('default:calendar') // time el += `<time datetime="${date_xml(post.date)}">${date(post.date, config.date_format)} </time>` // 字数统计 el += '<span class="post-count"> '+ wordcount(post.content) +' 字</span>'; el += '</span>'; el += '</div>';
// 标题 el += '<h2 class="post-title">'; el += post.title ? post.title : date(post.date, config.date_format); el += '</h2>';
// 摘要 el += '<div class="excerpt'; if (theme.plugins.heti?.enable) { el += ' heti'; } el += '">'; el += '<p>'; if (post.excerpt) { el += strip_html(post.excerpt); } else if (post.description) { el += post.description; } else if (post.content && theme.article.auto_excerpt > 0) { el += truncate(strip_html(post.content), {length: theme.article.auto_excerpt}); } el += '</p>'; el += '</div>';
el += '<div class="meta cap">'; // cat tags if (post.tags && post.tags.length > 0) { el += '<span class="cap tags">';
el += post.tags.map(tag => `<span style="background-color: var(--tag-bg-color); padding: 0px 6px; border-radius: 5px; color: var(--tag-text-color); line-height: 1.5;">${icon('default:tag')}<span>${tag.name}</span></span>`).join('');
// el += post.tags.map(tag => `<span style="border: 1px solid #529dd6; padding: 0 5px; border-radius: 5px; margin-right: 5px; line-height: 1.3; display: inline-flex; align-items: center;">${icon('default:tag')}<span>${tag.name}</span></span>`).join('');
el += '</span>'; } // cat categories if (post.categories && post.categories.length > 0) { if (post.layout === 'post' && post.categories && post.categories.length > 0) { var cats = []; if (post.categories) { post.categories.forEach((cat, i) => { cats.push(cat.name); }); } if (cats.length > 0) { let cat = cats.shift(); el += '<span class="cap breadcrumb"' + category_color(cat) + '>'; el += icon('default:category') el += `<span>${cat}</span>` el += '</span>'; } } }
if (post.sticky) { el += `<span class="pin">${icon('default:pin')}</span>` } el += '</div>'; el += '</article>'; return el;}function div_photo() { var el = ''; el += '<div class="cover">'; el += '<img src="' + obj.image + '"/>'; if (obj.headline || obj.topic || obj.caption) { el += '<div class="cover-info"'; if (obj.color) { el += 'style="color:' + obj.color + '"'; } if (obj.topic) { el += 'position="top">'; } else { el += 'position="bottom">'; } if (obj.topic) { el += '<div class="cap">' + obj.topic + '</div>'; } if (obj.headline) { el += '<div class="title">' + obj.headline + '</div>'; } if (obj.caption) { el += '<div class="cap">' + obj.caption + '</div>'; } el += '</div>';
}el += '</div>'; return el;}function div() { if (obj.image && obj.image.length > 0 && obj.headline != undefined) { return div_photo(); } return div_default();}%><%- div() %>
{% endfolding %}
标签样式#
新建 source/css/tagcolor-switch.css
文件,添加以下代码(颜色可以自行修改):
/* 默认的浅色模式颜色 */:root { --tag-bg-color: #F2EEFD; --tag-text-color: #835EEC; }
/* 暗黑模式下的颜色 */ @media (prefers-color-scheme: dark) { :root { --tag-bg-color: #282433; --tag-text-color: #A28BF2; } }
字数统计样式#
在自定义 css 文件中,增加以下代码:
#post-meta { font-size: 12px; color: var(--text-p4); /* 或 --text-p2,设置为灰色 */}
更改暗色模式颜色#
在 themes/stellar/source/css/_defines/theme.styl
里按需修改,这里就不放代码了(主要是自己改了半天还不如不改,我的审美告诉我是时候放弃)
添加分类 widget#
自己挺想要的功能,效果如图:

教程写在这里:Stellar 主题中添加分类索引 Widget
添加展示最新评论 widget#
显示效果可见主页,教程来自这里,毫无修改直接照搬,就不复制了。
在文章页面添加标签显示#
需要修改主题文件
显示效果:
在 layout/_partial/main/navbar/article_banner.ejs
文件中,找到
// 3.left.top: 面包屑导航 el += `<div class="flex-row" id="breadcrumb">` // 首页 el += `<a class="cap breadcrumb" href="${url_for(config.root)}">${__("btn.home")}</a>` if (theme.wiki.tree[page.wiki]) { el += partial('breadcrumb/wiki') } else if (page.layout == 'post') { el += partial('breadcrumb/blog') } else { el += partial('breadcrumb/page') } // end 3.left.top el += `</div>`
并在后面添加:
// 在这里添加标签代码 if (page.layout == "post" && page.tags && page.tags.length > 0) { el += '<div id="tag">'; // 将标签容器的创建移动到条件内部 el += ' <span> 标签:</span>'; el += list_categories(page.tags, { class: "cap breadcrumb", show_count: false, separator: ' ', style: "none" }); el += ' </div>'; }
toc 字体大小调整#
需要修改主题文件
就是把文章目录字体调小了一点点。
在themes/stellar/source/css/_layout/widgets/toc.styl
文件中,找到
// 各级缩进样式.widget-wrapper.toc .toc .toc-item font-weight: 500 --fsp: $fsp1 .toc-item .toc-item font-weight: 400 --fsp: $fsp2
把--fsp: $fsp1
一行注释掉:
// 各级缩进样式.widget-wrapper.toc .toc .toc-item font-weight: 500 /*--fsp: $fsp1*/ .toc-item .toc-item font-weight: 400 --fsp: $fsp2