<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>MSQY's Blog</title>
    <link>https://msqy-h.github.io/</link>
    <description>所见所闻，所思所想</description>
    <language>zh-CN</language>
    <copyright>All rights reserved 2026, 陌上轻烟</copyright>
    <lastBuildDate>Mon, 10 Aug 2026 07:37:40 GMT</lastBuildDate>
    <generator>Hexo</generator>
    <atom:link href="https://msqy-h.github.io/rss2.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>零预算网站域名解决方案</title>
      <link>https://msqy-h.github.io/2026/08/08/domain-name-solutions/</link>
      <description>没有预算买域名？GitHub Pages 的域名 + Cloudflare Pages 作镜像站，这个方案也不错。</description>
      <author>陌上轻烟</author>
      <category domain="https://msqy-h.github.io/categories/%E7%BD%91%E7%AB%99/">网站</category>
      <category domain="https://msqy-h.github.io/tags/Hexo/">Hexo</category>
      <category domain="https://msqy-h.github.io/tags/Pages/">Pages</category>
      <category domain="https://msqy-h.github.io/tags/Bing/">Bing</category>
      <enclosure url="https://msqy-h.github.io/2026/08/08/domain-name-solutions/cover.webp" type="image/webp"/>
      <pubDate>Sat, 08 Aug 2026 13:03:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p><img src="https://msqy-h.github.io/2026/08/08/domain-name-solutions/cover.webp" alt="零预算网站域名解决方案" /></p>
<h2 id="碎碎念"><a href="#碎碎念" class="headerlink" title="碎碎念"></a>碎碎念</h2><p>我之前一直使用的域名都是 ljxh-h.cc.cd。后来在让 Bing 收录的时候，不知道为什么，Bing 已经建立索引了，但是我没有办法搜索到这个域名。我以为是我操作时不小心动到了什么东西，让这个域名被污染了，于是，我又换到了 msqy.cc.cd。</p><p>问题依旧。Bing 死活不显示在搜索结果中。我也咨询了 Bing Webmaster Tools 团队好几次，他们也不说具体原因，只说“你的网站不符合规范，请根据文档进行操作”。没办法了，只好问 DeepSeek。DeepSeek <del>用最直接、最真相、最不绕弯、最扎心、最硬核、最干脆、最不墨迹、最戳痛点、最不留情面、最一针见血、最开门见山、最单刀直入、最不铺垫、最不客套、最不煽情、最不废话、最不拐弯、最不磨叽、最不装、最不端着、最不啰嗦、最不拖沓、最不委婉、最不掩饰、最不藏着掖着、最直白、最露骨、最实在、最通透、最毒辣、最爽快、最解气、最上头、最够劲、最过瘾、最粗暴、最有效、最狠、最准、最稳、最绝、最顶、最炸、最刚、最烈、最飒、最莽、最冲、最猛、最脆、最亮、最透、最干、最净、最利落、最霸道、最硬核、最生猛、最狂野、最直白、最粗暴、最不讲虚的、最不玩套路、最不搞形式、最不整虚头巴脑、最只讲干货、最只说重点、最只给结果、最只聊真相、最只谈核心、最只戳关键的方式来</del>告诉我：</p><blockquote><p>因为 cc.cd 的母域名 .cd 注册局早已瘫痪，过期域名无人清理、长期被滥用于垃圾站，Bing 直接判定其为高风险“僵尸域名池”，所以哪怕索引了也死都不会给你展示。</p></blockquote><p>虽然 Bing 没有证实这一点，但是这句话还是有道理的。</p><p>当然也有可能是我的网站建立时间太短了，权威性不高，但我已经等了两个月了！我之前有一个 cc.cd 域名，Bing 一索引就能搜索到。那时候能显示，大概是因为当时 Bing 的要求比较宽松吧。</p><p>要解决这个问题最简单的方法是：买一个 .com、.net 这样的域名，可惜要钱。</p><p>DeepSeek 推荐了 GitHub Pages 的默认域名 &lt;yourusername&gt;.github.io。这个域名背后是 GitHub，比较稳定，而且“是微软亲儿子”，Bing 的信任度可能比较高。</p><p>那就用 &lt;yourusername&gt;.github.io 吧，毕竟很多人都选这个。</p><p>但是，GitHub Pages 毕竟是国外平台，访问速度是大家有目共睹的慢。那怎么搞比较好？</p><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><p>我想到了个绝佳的好方法：以 Github Pages 为主站，搜索引擎、友链交换都用这个；用更快的 Cloudflare Pages 部署博客，使用 cc.cd 域名作国内镜像站。</p><p>那该怎么提示中国大陆用户去镜像站呢？</p><h3 id="提示用户前往镜像站"><a href="#提示用户前往镜像站" class="headerlink" title="提示用户前往镜像站"></a>提示用户前往镜像站</h3><p>首先要解决提示信息何时出现的问题。</p><p>如果我们把提示信息组件放在 <code>&lt;body&gt;</code> 里面，通过 JavaScript 控制显示隐藏，那么这个脚本可能会被浏览器最后加载，到了这时候，资源文件基本已经加载完了，再提示用户去镜像站就没有意义了。</p><p>那直接把组件和 JavaScript 嵌入到 <code>&lt;head&gt;</code> 里吧。一般的浏览器都会最先加载 <code>&lt;head&gt;</code> 部分，只要浏览器获取到了 html，那么浏览器必定能获取到内嵌的 JavaScript，不会被其他资源文件加载所阻塞。</p><p>至于在 JavaScript 显示组件的时机，应该是要获取到用户位置后立即显示，如果等 DOM 加载完成，那太晚了，资源文件可能已经加载好了。</p><p>获取用户位置，最简单的就是使用 IP 地址获取用户位置的 API 了。向 API 发送请求后，API 会返回带地区代码的数据。我们只要判断数据中的地区代码是否属于中国大陆即可。</p><p>知道了基本实现逻辑，那就可以直接写代码了。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ip-banner.js</span></span><br><span class="line"><span class="comment">// DeepSeek 写的，质量不咋地，但能用</span></span><br><span class="line">(<span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> <span class="variable constant_">MIRROR_HOST</span> = <span class="string">&#x27;替换为你的镜像站&#x27;</span>;</span><br><span class="line">    <span class="keyword">const</span> <span class="variable constant_">API_URL</span> = <span class="string">&#x27;替换为你的 api&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 如果已经在镜像站，不再检测</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">hostname</span> === <span class="variable constant_">MIRROR_HOST</span>) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> isChina = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">let</span> requestFinished = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">let</span> bannerCreated = <span class="literal">false</span>;</span><br><span class="line">    <span class="comment">//这里可能需要根据你的 api 返回数据修改</span></span><br><span class="line">    <span class="keyword">function</span> <span class="title function_">handleResponse</span>(<span class="params">data</span>) &#123;</span><br><span class="line">        <span class="keyword">const</span> country = data.<span class="property">country</span> || data.<span class="property">country_code</span> || data.<span class="property">region</span> || <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">        isChina = <span class="regexp">/中国|CN|China/i</span>.<span class="title function_">test</span>(country);</span><br><span class="line">        requestFinished = <span class="literal">true</span>;</span><br><span class="line">        <span class="comment">// 请求返回后立即尝试创建横幅</span></span><br><span class="line">        <span class="title function_">tryCreateBanner</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 立即发起 IP 请求（不等待 DOM）</span></span><br><span class="line">    <span class="keyword">const</span> xhr = <span class="keyword">new</span> <span class="title class_">XMLHttpRequest</span>();</span><br><span class="line">    xhr.<span class="title function_">open</span>(<span class="string">&#x27;GET&#x27;</span>, <span class="variable constant_">API_URL</span>, <span class="literal">true</span>);</span><br><span class="line">    xhr.<span class="property">timeout</span> = <span class="number">5000</span>;</span><br><span class="line">    xhr.<span class="property">onload</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (xhr.<span class="property">status</span> === <span class="number">200</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">const</span> data = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(xhr.<span class="property">responseText</span>);</span><br><span class="line">                <span class="title function_">handleResponse</span>(data);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (_) &#123; <span class="comment">/* 忽略 */</span> &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    xhr.<span class="property">onerror</span> = xhr.<span class="property">ontimeout</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123; <span class="comment">/* 静默失败 */</span> &#125;;</span><br><span class="line">    xhr.<span class="title function_">send</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 轮询检测 body 是否可用，一旦可用立即创建</span></span><br><span class="line">    <span class="keyword">function</span> <span class="title function_">tryCreateBanner</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (bannerCreated) <span class="keyword">return</span>;</span><br><span class="line">        <span class="keyword">if</span> (!isChina || !requestFinished) <span class="keyword">return</span>;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="variable language_">document</span>.<span class="property">body</span>) &#123;</span><br><span class="line">            <span class="comment">// body 尚未存在，20ms 后重试</span></span><br><span class="line">            <span class="built_in">setTimeout</span>(tryCreateBanner, <span class="number">20</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// body 已存在，立即创建横幅</span></span><br><span class="line">        <span class="title function_">createBanner</span>();</span><br><span class="line">        bannerCreated = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 额外保障：如果请求返回时 body 未准备好，上面轮询会重试；</span></span><br><span class="line">    <span class="comment">// 但如果脚本执行时 body 已经存在（极少情况），也要尝试</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="property">body</span> &amp;&amp; <span class="variable language_">document</span>.<span class="property">readyState</span> !== <span class="string">&#x27;loading&#x27;</span>) &#123;</span><br><span class="line">        <span class="comment">// 如果 body 已经存在且不是加载中，尝试创建（但请求可能未完成）</span></span><br><span class="line">        <span class="comment">// 我们可以先尝试，但会受 isChina 和 requestFinished 限制</span></span><br><span class="line">        <span class="comment">// 所以无事可做，请求回调会触发 tryCreateBanner</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">function</span> <span class="title function_">createBanner</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">const</span> banner = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;div&#x27;</span>);</span><br><span class="line">        banner.<span class="property">id</span> = <span class="string">&#x27;msqy-banner&#x27;</span>;</span><br><span class="line">        banner.<span class="property">style</span>.<span class="property">cssText</span> = <span class="string">`</span></span><br><span class="line"><span class="string">            position: fixed;</span></span><br><span class="line"><span class="string">            top: .4rem;</span></span><br><span class="line"><span class="string">            left: .9rem;</span></span><br><span class="line"><span class="string">            right: .9rem;</span></span><br><span class="line"><span class="string">            z-index: 99999;</span></span><br><span class="line"><span class="string">            max-width: 600px;</span></span><br><span class="line"><span class="string">            margin: 0 auto;</span></span><br><span class="line"><span class="string">            background: var(--efu-card-bg, #fff);</span></span><br><span class="line"><span class="string">            color: var(--efu-fontcolor, #363636);</span></span><br><span class="line"><span class="string">            border-radius: 12px;</span></span><br><span class="line"><span class="string">            padding: 16px 20px;</span></span><br><span class="line"><span class="string">            box-shadow: 0 8px 30px rgba(0,0,0,0.15);</span></span><br><span class="line"><span class="string">            font-size: 14px;</span></span><br><span class="line"><span class="string">            font-family: system-ui, -apple-system, &#x27;Segoe UI&#x27;, Roboto, sans-serif;</span></span><br><span class="line"><span class="string">            display: flex;</span></span><br><span class="line"><span class="string">            align-items: center;</span></span><br><span class="line"><span class="string">            justify-content: space-between;</span></span><br><span class="line"><span class="string">            gap: 12px;</span></span><br><span class="line"><span class="string">            border: var(--style-border, 1px solid var(--efu-card-border, #e3e8f7));</span></span><br><span class="line"><span class="string">            box-sizing: border-box;</span></span><br><span class="line"><span class="string">            transition: opacity 0.3s ease, transform 0.3s ease;</span></span><br><span class="line"><span class="string">            opacity: 0;</span></span><br><span class="line"><span class="string">            transform: translateY(-10px);</span></span><br><span class="line"><span class="string">        `</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">const</span> textWrapper = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;span&#x27;</span>);</span><br><span class="line">        textWrapper.<span class="property">style</span>.<span class="property">cssText</span> = <span class="string">`</span></span><br><span class="line"><span class="string">            flex: 1;</span></span><br><span class="line"><span class="string">            line-height: 1.5;</span></span><br><span class="line"><span class="string">            color: var(--efu-fontcolor, #363636);</span></span><br><span class="line"><span class="string">        `</span>;</span><br><span class="line">        textWrapper.<span class="property">innerHTML</span> = <span class="string">`</span></span><br><span class="line"><span class="string">            🌏 检测到您在中国大陆，建议访问国内镜像站</span></span><br><span class="line"><span class="string">            &lt;a href=&quot;https://<span class="subst">$&#123;MIRROR_HOST&#125;</span><span class="subst">$&#123;<span class="variable language_">window</span>.location.pathname&#125;</span><span class="subst">$&#123;<span class="variable language_">window</span>.location.search&#125;</span>&quot;</span></span><br><span class="line"><span class="string">               style=&quot;color: var(--efu-blue, #425aef); font-weight: 600; text-decoration: none;&quot;&gt;</span></span><br><span class="line"><span class="string">                <span class="subst">$&#123;MIRROR_HOST&#125;</span></span></span><br><span class="line"><span class="string">            &lt;/a&gt;</span></span><br><span class="line"><span class="string">            ，速度更快。</span></span><br><span class="line"><span class="string">        `</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">const</span> closeBtn = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;button&#x27;</span>);</span><br><span class="line">        closeBtn.<span class="property">innerHTML</span> = <span class="string">&#x27;✕&#x27;</span>;</span><br><span class="line">        closeBtn.<span class="property">style</span>.<span class="property">cssText</span> = <span class="string">`</span></span><br><span class="line"><span class="string">            background: none;</span></span><br><span class="line"><span class="string">            border: none;</span></span><br><span class="line"><span class="string">            font-size: 18px;</span></span><br><span class="line"><span class="string">            cursor: pointer;</span></span><br><span class="line"><span class="string">            color: var(--efu-secondtext, #a1a2b8);</span></span><br><span class="line"><span class="string">            padding: 4px 6px;</span></span><br><span class="line"><span class="string">            line-height: 1;</span></span><br><span class="line"><span class="string">            transition: color 0.2s;</span></span><br><span class="line"><span class="string">            flex-shrink: 0;</span></span><br><span class="line"><span class="string">        `</span>;</span><br><span class="line">        closeBtn.<span class="title function_">addEventListener</span>(<span class="string">&#x27;mouseenter&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">            closeBtn.<span class="property">style</span>.<span class="property">color</span> = <span class="string">&#x27;var(--efu-fontcolor, #363636)&#x27;</span>;</span><br><span class="line">        &#125;);</span><br><span class="line">        closeBtn.<span class="title function_">addEventListener</span>(<span class="string">&#x27;mouseleave&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">            closeBtn.<span class="property">style</span>.<span class="property">color</span> = <span class="string">&#x27;var(--efu-secondtext, #a1a2b8)&#x27;</span>;</span><br><span class="line">        &#125;);</span><br><span class="line">        closeBtn.<span class="title function_">addEventListener</span>(<span class="string">&#x27;click&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">            banner.<span class="property">style</span>.<span class="property">opacity</span> = <span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">            banner.<span class="property">style</span>.<span class="property">transform</span> = <span class="string">&#x27;translateY(-20px)&#x27;</span>;</span><br><span class="line">            <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> banner.<span class="title function_">remove</span>(), <span class="number">300</span>);</span><br><span class="line">            <span class="comment">// 不保存状态，刷新后重新出现</span></span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        banner.<span class="title function_">appendChild</span>(textWrapper);</span><br><span class="line">        banner.<span class="title function_">appendChild</span>(closeBtn);</span><br><span class="line">        <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(banner);</span><br><span class="line"></span><br><span class="line">        <span class="title function_">requestAnimationFrame</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">            banner.<span class="property">style</span>.<span class="property">opacity</span> = <span class="string">&#x27;1&#x27;</span>;</span><br><span class="line">            banner.<span class="property">style</span>.<span class="property">transform</span> = <span class="string">&#x27;translateY(0)&#x27;</span>;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><p>将这段代码嵌入到 <code>&lt;head&gt;</code> 里即可。</p><p>Solitude 主题自带了嵌入功能：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Extend</span></span><br><span class="line"><span class="comment"># 扩展</span></span><br><span class="line"><span class="attr">extends:</span></span><br><span class="line">  <span class="comment"># Insert in head</span></span><br><span class="line">  <span class="comment"># 插入到 head</span></span><br><span class="line">  <span class="attr">head:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&lt;scripts&gt;#在这里嵌入代码&lt;/scripts&gt;</span></span><br></pre></td></tr></table></figure><p>这样，Hexo 在生成静态文件时，就会将这段嵌入到每个 html 文件的 <code>&lt;head&gt;</code> 部分。</p><h3 id="部署"><a href="#部署" class="headerlink" title="部署"></a>部署</h3><p>代码准备好了，接下来就是部署到 GitHub Pages 上了。我建议新建一个名为 &lt;yourusername&gt;.github.io 的仓库，将静态文件上传至该仓库，在仓库设置中开启 GitHub Pages。这样博客就会部署到根目录下，不用担心资源文件路径引用问题。</p><p>Hexo 有一个 <code>hexo deploy</code> 命令，能帮你自动把资源文件上传到仓库，特别方便，只要先安装 <code>hexo-deployer-git</code> 插件：</p><div class="tabs" id="安装插件"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#安装插件-0">npm</button></li><li class="tab"><button type="button" data-href="#安装插件-1">pnpm</button></li><li class="tab"><button type="button" data-href="#安装插件-2">yarn</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="安装插件-0"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-deployer-git --save</span><br></pre></td></tr></table></figure></div><div class="tab-item-content" id="安装插件-1"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pnpm add hexo-deployer-git</span><br></pre></td></tr></table></figure></div><div class="tab-item-content" id="安装插件-2"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add hexo-deployer-git</span><br></pre></td></tr></table></figure></div></div></div><p>在 Hexo 配置文件中设置：</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">deploy:</span></span><br><span class="line">  <span class="attr">type:</span> <span class="string">git</span></span><br><span class="line">  <span class="attr">repo:</span> <span class="string">&lt;repository</span> <span class="string">url&gt;</span> <span class="comment"># https://bitbucket.org/JohnSmith/johnsmith.bitbucket.io</span></span><br><span class="line">  <span class="attr">branch:</span> [<span class="string">branch</span>]</span><br><span class="line">  <span class="attr">message:</span> [<span class="string">message</span>]</span><br></pre></td></tr></table></figure><p>之后直接 <code>hexo deploy</code> 即可。</p><p>Cloudflare Pages 可以直接关联 &lt;yourusername&gt;.github.io 仓库，也可以新建一个源码仓库，通过源码构建部署。</p><h3 id="Bing-收录与-SEO-优化"><a href="#Bing-收录与-SEO-优化" class="headerlink" title="Bing 收录与 SEO 优化"></a>Bing 收录与 SEO 优化</h3><p>如果有两个内容相似的网站的话，Bing 会降低网站的排名。</p><p>不过要解决这个问题很简单，只要在 html 文件的 <code>&lt;head&gt;</code> 插入：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;canonical&quot;</span> <span class="attr">href</span>=<span class="string">&quot;你的主网站域名&quot;</span> /&gt;</span></span><br></pre></td></tr></table></figure><p>即可。Bing 会优先索引你的主网站，镜像站不会被收录。</p><p>如果是 Hexo 博客，只要将配置文件中的</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">url:</span> <span class="string">你的主网站</span></span><br></pre></td></tr></table></figure><p>改为你的主网站即可。Hexo 在构建时会自动加入上述标签。</p><p>至于 &lt;yourusername&gt;.github.io 的收录情况，我会在这里更新。</p><h3 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h3><p>折腾了大半天，终于解决了博客的域名问题。但愿能被 Bing 收录吧。不过收不收录也无所谓了，毕竟我一开始选择 &lt;yourusername&gt;.github.io 作主域名，也只是看中它更稳定罢了。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>逃离流媒体，回到本地音乐 - 本地音乐软件推荐</title>
      <link>https://msqy-h.github.io/2026/08/05/native-music/</link>
      <description>受够了国内的流媒体音乐？试试本地音乐，获得更纯粹的音乐体验。</description>
      <author>陌上轻烟</author>
      <category domain="https://msqy-h.github.io/categories/%E8%BD%AF%E4%BB%B6/">软件</category>
      <category domain="https://msqy-h.github.io/tags/%E9%9F%B3%E4%B9%90/">音乐</category>
      <enclosure url="https://msqy-h.github.io/2026/08/05/native-music/cover.webp" type="image/webp"/>
      <pubDate>Wed, 05 Aug 2026 06:14:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="碎碎念"><a href="#碎碎念" class="headerlink" title="碎碎念"></a>碎碎念</h2><p>随着网络的发展，许多流媒体音乐软件出现了。国内有 QQ 音乐、网易云音乐，国外有 Spotify、Apple Music。不可否认，它们让我们听歌方便了许多。</p><p>但是，不知道从什么时候起，我听歌变成了把手机装上稳定器，接上鼠标，打开软件，卡顿几秒钟，小心翼翼地点掉广告，打开搜索，在各种各样的 DJ 版、深情版中找到我真正想要的版本，点击歌曲，发现要开会员，忍痛付费，发现不是最高音质，再开个更高级的会员，却发现是假无损，还是有沙沙声。</p><p>过了一会，想听另一首歌，去搜索，只看到“该歌曲暂无版权”，只好退出软件，打开另一个软件，重复上面的操作，还是假无损。</p><p>花了双倍的价钱，搞到的却是一半的质量。这谁受得了？</p><p>后来也发现了国外的音乐软件，那好很多，但是 Spotify 不能在中国直接用，Apple Music 虽然音质好，软件纯净，但是曲库偏少。不过如果只听主流歌手或者欧美歌曲，Apple Music 也够用了。</p><p>受不了的我只好转到本地音乐。</p><p>本地音乐真是好太多了！</p><h2 id="本地音乐"><a href="#本地音乐" class="headerlink" title="本地音乐"></a>本地音乐</h2><p>本地音乐到底有什么好？</p><p>简单地说，就是有流媒体音乐没有的优点。</p><p>纯净的体验、极高的音质、不用担心的流量，让人真正地投入到音乐中。</p><p>虽说下载新歌比较麻烦，但一劳永逸，下载后想听多少遍都可以，直到听腻。</p><p>下面是我推荐的安卓听歌软件。</p><h2 id="软件"><a href="#软件" class="headerlink" title="软件"></a>软件</h2><h3 id="Salt-Player"><a href="#Salt-Player" class="headerlink" title="Salt Player"></a>Salt Player</h3><p><img src="/2026/08/05/native-music/saltplayer.webp" alt="Salt Player 的界面"></p><p>Salt Player 是很好用的播放器，颜值高，功能比较多，而且在最近的版本支持了 USB 独占，非常推荐。</p><a class="tag-link" href="https://moriafly.com/program/salt-player.html" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">Salt Player</div>            <div class="tag-link-sitename">Official 渠道（境内用户推荐）：OPPO…</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><h3 id="Poweramp"><a href="#Poweramp" class="headerlink" title="Poweramp"></a>Poweramp</h3><p>Poweramp 很有名，挺专业的，界面比较硬核，音质也挺好，自定义功能多，适合喜欢折腾的。</p><p><img src="/2026/08/05/native-music/poweramp.webp" alt="Poweramp 的界面"></p><a class="tag-link" href="http://powerampapp.com/" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">Poweramp: Android…</div>            <div class="tag-link-sitename">Poweramp is a powerful …</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><h3 id="PixelPlayer"><a href="#PixelPlayer" class="headerlink" title="PixelPlayer"></a>PixelPlayer</h3><p>Material 3 风格，好看，功能比较多。</p><p><img src="/2026/08/05/native-music/pixelplayer.webp" alt="PixelPlayer 的界面"></p><div class="repo-card">  <a class='name-ub0ush12k repo-title fancybox' href="https://github.com/PixelPlayerHQ/PixelPlayer"></a>  <p class='desc-ub0ush12k repo-desc'></p>  <div class="repo-gist">    <i class="fas fa-star"></i>    <span class='star-ub0ush12k'></span>    <i class="fas fa-code-branch"></i>    <span class='fork-ub0ush12k'></span>  </div>  <img class='lang-ub0ush12k repo-language no-lightbox' />  <script>    fetch('https://api.github.com/repos/PixelPlayerHQ/PixelPlayer')      .then(res => res.json())      .then(data => {        document.querySelector('.name-ub0ush12k').innerText = data.name;        document.querySelector('.desc-ub0ush12k').innerText = data.description;        document.querySelector('.star-ub0ush12k').innerText = data.stargazers_count;        document.querySelector('.fork-ub0ush12k').innerText = data.forks_count;        document.querySelector('.lang-ub0ush12k').src = 'https://skillicons.dev/icons?i=' + data.language.toLowerCase();    });  </script>  </div><h3 id="AIMP"><a href="#AIMP" class="headerlink" title="AIMP"></a>AIMP</h3><p>很不错的播放器，界面也比较硬核，功能多。</p><p><img src="/2026/08/05/native-music/aimp.webp" alt="AIMP 的界面"></p><a class="tag-link" href="http://aimp.ru/" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">AIMP</div>            <div class="tag-link-sitename">Cumulative updates for …</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><h3 id="Flamingo"><a href="#Flamingo" class="headerlink" title="Flamingo"></a>Flamingo</h3><p>仿 Apple Music 的播放器，歌词效果非常好看。未正式发布，安装包难找一点，不过可以在作者的 Bilibili 或者 Telegram 频道上面找到。</p><p><img src="/2026/08/05/native-music/flamingo.webp" alt="Flamingo 的界面"></p><a class="tag-link" href="https://www.bilibili.com/video/BV1aSgWzYEdo" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">Bilibili: Flamingo 更新内容…</div>            <div class="tag-link-sitename">Flamingo 是一款使…</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><h2 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h2><p>想纯粹地听歌？本地音乐可能是最好的选择之一。</p><p><del>感觉这篇文章挺水的</del>如果我发现了什么好歌，也会分享给大家，欢迎一起听歌。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>Termux Neovim + LazyVim 配置实记</title>
      <link>https://msqy-h.github.io/2026/07/26/termux-neovim/</link>
      <description>在 Termux 环境下，安装 Neovim 并使用 Lazyvim 进行配置</description>
      <author>陌上轻烟</author>
      <category domain="https://msqy-h.github.io/categories/%E8%BD%AF%E4%BB%B6/">软件</category>
      <category domain="https://msqy-h.github.io/tags/Termux/">Termux</category>
      <category domain="https://msqy-h.github.io/tags/%E7%BB%88%E7%AB%AF/">终端</category>
      <enclosure url="https://msqy-h.github.io/2026/07/26/termux-neovim/cover.webp" type="image/webp"/>
      <pubDate>Sun, 26 Jul 2026 07:10:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h2><p>之前在配置 zsh 时，我就说过要写一篇配置 Neovim 的文章。今天，我就在这里说一下怎么在 Termux 中配置 Neovim。</p><h2 id="碎碎念"><a href="#碎碎念" class="headerlink" title="碎碎念"></a>碎碎念</h2><div class="note info modern"><i class="solitude fas fa-info-circle"></i><p>这里可以跳过</p></div><p>我与 Neovim 结缘，还得感谢 VSCode。当时我刚开始写代码用的是简陋的 IDLE。简陋其实有简陋的好，能让初学者快速熟悉 Python 的基础语法，学会自己排查错误。但是 IDLE 实在是太简陋了，写起代码非常费劲。其实当时也只会写 100 行以内的简单代码，谈不上是什么大项目。</p><p>于是我便去寻找更好用的编辑器。PyCharm 固然很好，但是作为 IDE，对于我这种写简单 demo 的人来说，有点太重了。VSCode 显然更符合我的需求（其实 VSCode 用来写前端比较多）。丰富的插件市场让 VSCode 基本上是全能的。</p><p>在安装插件时，我在热门插件列表看到了一个东西：VSCodeVim。</p><p>我点开 VSCodeVim 的插件详情：</p><blockquote><p>VSCodeVim is a Vim emulator for Visual Studio Code.</p></blockquote><p>这个插件是用来在 VSCode 上模拟 Vim 的操作环境的。</p><p>Vim 是什么？我去搜了一下。</p><blockquote><p>Vim is a greatly improved version of the good old UNIX editor Vi. Many new features have been added: multi-level undo, syntax highlighting, command line history, on-line help, spell checking, filename completion, block operations, script language, etc. </p></blockquote><p>看起来很厉害。好奇的我于是把插件下载了下来，看看 Vim 到底是何方神圣。</p><p>为什么我输入不了东西？为什么光标飞来飞去？为什么用不了 Ctrl + V？我又去搜了下使用方法。</p><p>怎么这么多模式啊，怎么这么多快捷键啊，怎么还要输入命令啊！</p><p>太反人类了！我立马将插件卸载掉。我很久都没有再碰过 Vim。</p><p>后来开始折腾 Linux 虚拟机。用 Nano 编辑配置文件真是太麻烦了！（就像用记事本编辑 Windows 的配置文件）于是我便去找 Linux 上轻量又好用的文本编辑器。</p><p>自然，我又看见了 Vim。为什么这么多大佬都用 Vim？难道 Vim 真的很好用吗？反正 Linux 终端文本编辑器只有这些，试一下吧。</p><p>认真看了一篇 Vim 的使用教程后，我知道了 Vim 的基础使用方法。</p><p>不用鼠标真是太爽了！发明 Vim 的人真是天才！</p><p>后来，我开始在安卓手机上折腾 Termux。Termux 是一个终端模拟器，自然也支持 Vim。不过，Vim 有点太古老了，Neovim 是更好的选择。</p><p>说的有点多了，下面开始在 Termux 下配置 Neovim。</p><h2 id="效果图"><a href="#效果图" class="headerlink" title="效果图"></a>效果图</h2><p><img src="/2026/07/26/termux-neovim/result.webp" alt="效果图"></p><h2 id="配置方法"><a href="#配置方法" class="headerlink" title="配置方法"></a>配置方法</h2><h3 id="1-安装-Neovim"><a href="#1-安装-Neovim" class="headerlink" title="1. 安装 Neovim"></a>1. 安装 Neovim</h3><p>这里没什么好说的，直接 pkg 安装即可。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pkg i neovim</span><br></pre></td></tr></table></figure><h3 id="2-对于-Termux-的前置准备"><a href="#2-对于-Termux-的前置准备" class="headerlink" title="2. 对于 Termux 的前置准备"></a>2. 对于 Termux 的前置准备</h3><p>如果你想自己折腾，可以编辑 Neovim 的配置文件。这里不再赘述。需要的可以自行查询其他教程。</p><p>LazyVim 官方教程已经很详细了，如果需要更多支持，建议查询官方教程：</p><a class="tag-link" href="https://www.lazyvim.org/" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">LazyVim - Getting…</div>            <div class="tag-link-sitename">LazyVim is a Neovim setup …</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><p>如果用的是普通手机（遥控器比例），官方 LazyVim 可能适配的不是很好，我找到了为 Termux 定制的 LazyVim，我在这里会用该 LazyVim 的脚本来配置。</p><div class="repo-card">  <a class='name-g0fojnveo repo-title fancybox' href="https://github.com/Veha0001/dotLazyVim"></a>  <p class='desc-g0fojnveo repo-desc'></p>  <div class="repo-gist">    <i class="fas fa-star"></i>    <span class='star-g0fojnveo'></span>    <i class="fas fa-code-branch"></i>    <span class='fork-g0fojnveo'></span>  </div>  <img class='lang-g0fojnveo repo-language no-lightbox' />  <script>    fetch('https://api.github.com/repos/Veha0001/dotLazyVim')      .then(res => res.json())      .then(data => {        document.querySelector('.name-g0fojnveo').innerText = data.name;        document.querySelector('.desc-g0fojnveo').innerText = data.description;        document.querySelector('.star-g0fojnveo').innerText = data.stargazers_count;        document.querySelector('.fork-g0fojnveo').innerText = data.forks_count;        document.querySelector('.lang-g0fojnveo').src = 'https://skillicons.dev/icons?i=' + data.language.toLowerCase();    });  </script>  </div><div class="note warning modern"><i class="solitude fas fa-warning"></i><p>该仓库为第三方仓库，如果已经停更，建议换用官方仓库</p></div><p>先安装需要的包：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">apt update &amp;&amp; apt upgrade</span><br><span class="line">apt install termux-api python neovim git nodejs-lts</span><br><span class="line">apt install termux-tools lazygit ripgrep</span><br><span class="line">apt install ruff luarocks lua-language-server clang</span><br></pre></td></tr></table></figure><p>要安装 Termux:API 以便让功能更完备。</p><a class="tag-link" href="https://github.com/termux/termux-api" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">GitHub - termux…</div>            <div class="tag-link-sitename">This is an app exposing Android…</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><a class="tag-link" href="https://f-droid.org/packages/com.termux.api/" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">F-Droid - Termux…</div>            <div class="tag-link-sitename">Termux:API is an add-on for…</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><h3 id="3-安装-LazyVim"><a href="#3-安装-LazyVim" class="headerlink" title="3. 安装 LazyVim"></a>3. 安装 LazyVim</h3><p>在安装 LazyVim 之前，先备份配置文件。（当然，如果你什么都没做，Termux 提示文件不存在，那么可以跳过备份）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mv ~/.config/nvim ~/.config/nvim.bak</span><br></pre></td></tr></table></figure><p>接着就是安装 LazyVim 了！</p><div class="note warning modern"><i class="solitude fas fa-warning"></i><p>接下来会使用 GitHub，由于 GitHub 服务器在国外，接下来的几步可能需要代理，最好在结束之前都不要关。</p></div><p>克隆 LazyVim 仓库</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone https://github.com/Veha0001/DotLazyVim ~/.config/nvim</span><br></pre></td></tr></table></figure><p>输入 <code>nvim</code>，启动 Neovim。这里会有一段时间的黑屏，不用担心，稍等一下就会弹出界面进行初始化。</p><p>初始化时，LazyVim 会从 GitHub 拉取一些文件，时间可能会比较长，请坐和放宽。</p><p>如果初始化时出现错误，可以 Ctrl + C 强制退出，如果不行，尝试输入 <code>:q</code> 退出。退出后再次输入 <code>nvim</code> 重新初始化。如果还是不行，可以删除 LazyVim 和 Neovim，重头开始配置。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">rm -rf ~/.config/nvim \</span><br><span class="line">       ~/.local/share/nvim \</span><br><span class="line">       ~/.local/state/nvim \</span><br><span class="line">       ~/.cache/nvim</span><br><span class="line">pkg uninstall neovim</span><br></pre></td></tr></table></figure><p>初始化完成后，退出再进入 Neovim，Lazy Vim 就安装完成了！</p><h3 id="4-配置和美化-LazyVim（可选）"><a href="#4-配置和美化-LazyVim（可选）" class="headerlink" title="4. 配置和美化 LazyVim（可选）"></a>4. 配置和美化 LazyVim（可选）</h3><p>如果对 LazyVim 默认效果不满意，可以进行配置和美化。</p><p>由于配置内容过多，这里不详细讲了，具体看官方文档</p><a class="tag-link" href="https://www.lazyvim.org/" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">Configuration | LazyVim</div>            <div class="tag-link-sitename">The files under config will…</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><p>这里只说一下更改欢迎页的顶部图案。</p><p>选择欢迎页的 <code>Config</code>，打开文件树的 <code>nvim/lua/plugins/snacks.lua</code>，更改顶部的 <code>dashboard_custom_header17</code> 变量，例如：</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> dashboard_custom_header17 = &#123;</span><br><span class="line">  <span class="string">&quot; &quot;</span>,</span><br><span class="line">  <span class="string">&quot;██╗          ██╗██╗  ██╗██╗  ██╗                  &quot;</span>,</span><br><span class="line">  <span class="string">&quot;██║          ██║╚██╗██╔╝██║  ██║                  &quot;</span>,</span><br><span class="line">  <span class="string">&quot;██║          ██║ ╚███╔╝ ███████║                  &quot;</span>,</span><br><span class="line">  <span class="string">&quot;██║     ██   ██║ ██╔██╗ ██╔══██║                  &quot;</span>,</span><br><span class="line">  <span class="string">&quot;███████╗╚█████╔╝██╔╝ ██╗██║  ██║                  &quot;</span>,</span><br><span class="line">  <span class="string">&quot;╚══════╝ ╚════╝ ╚═╝  ╚═╝╚═╝  ╚═╝                  &quot;</span>,</span><br><span class="line">  <span class="string">&quot;                                                  &quot;</span>,</span><br><span class="line">  <span class="string">&quot;███╗   ██╗███████╗ ██████╗ ██╗   ██╗██╗███╗   ███╗&quot;</span>,</span><br><span class="line">  <span class="string">&quot;████╗  ██║██╔════╝██╔═══██╗██║   ██║██║████╗ ████║&quot;</span>,</span><br><span class="line">  <span class="string">&quot;██╔██╗ ██║█████╗  ██║   ██║██║   ██║██║██╔████╔██║&quot;</span>,</span><br><span class="line">  <span class="string">&quot;██║╚██╗██║██╔══╝  ██║   ██║╚██╗ ██╔╝██║██║╚██╔╝██║&quot;</span>,</span><br><span class="line">  <span class="string">&quot;██║ ╚████║███████╗╚██████╔╝ ╚████╔╝ ██║██║ ╚═╝ ██║&quot;</span>,</span><br><span class="line">  <span class="string">&quot;╚═╝  ╚═══╝╚══════╝ ╚═════╝   ╚═══╝  ╚═╝╚═╝     ╚═╝&quot;</span>,</span><br><span class="line">  <span class="string">&quot; &quot;</span>,</span><br><span class="line">  <span class="string">&quot; &quot;</span>,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>字符画的生成可以使用这个网站，网站提供很多样式。你可以将生成的字符画发给 AI，让 AI 帮你把字符画转成这个变量的格式。</p><a class="tag-link" href="http://patorjk.com/software/taag/" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">Text to ASCII…</div>            <div class="tag-link-sitename">An online text conversion …</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><p>返回 NORMAL 模式，输入 <code>wq</code> 保存并退出。</p><p>再次进入 Neovim，就会发现，配置文件生效了。</p><h2 id="Neovim-基础使用"><a href="#Neovim-基础使用" class="headerlink" title="Neovim 基础使用"></a>Neovim 基础使用</h2><p>这里记录一下 Neovim 的基础使用方法。此处使用 DeepSeek 辅助生成。这些东西还是 AI 方便，所有键位均已验证过。</p><h3 id="1-核心模式切换"><a href="#1-核心模式切换" class="headerlink" title="1. 核心模式切换"></a>1. 核心模式切换</h3><table><thead><tr><th align="left">按键</th><th align="left">模式名称</th><th align="left">功能说明</th></tr></thead><tbody><tr><td align="left"><code>Esc</code> &#x2F; <code>Ctrl</code> <code>[</code></td><td align="left">普通模式 (Normal)</td><td align="left"><strong>万能回退键</strong>，回到普通模式执行命令</td></tr><tr><td align="left"><code>i</code></td><td align="left">插入模式 (Insert)</td><td align="left">在光标<strong>前</strong>开始输入文字</td></tr><tr><td align="left"><code>a</code></td><td align="left">插入模式 (Insert)</td><td align="left">在光标<strong>后</strong>开始输入文字</td></tr><tr><td align="left"><code>o</code></td><td align="left">插入模式 (Insert)</td><td align="left">在光标所在行<strong>下方</strong>新增一行并进入插入模式</td></tr><tr><td align="left"><code>O</code></td><td align="left">插入模式 (Insert)</td><td align="left">在光标所在行<strong>上方</strong>新增一行并进入插入模式</td></tr><tr><td align="left"><code>v</code></td><td align="left">可视模式 (Visual)</td><td align="left"><strong>按字符</strong>选择文本（用于选中后复制&#x2F;删除）</td></tr><tr><td align="left"><code>V</code></td><td align="left">可视模式 (Visual)</td><td align="left"><strong>按整行</strong>选择文本</td></tr><tr><td align="left"><code>Ctrl</code> <code>v</code></td><td align="left">可视块模式 (Visual Block)</td><td align="left"><strong>按矩形块</strong>选择文本（多列编辑神器）</td></tr></tbody></table><h3 id="2-光标移动（普通模式下）"><a href="#2-光标移动（普通模式下）" class="headerlink" title="2. 光标移动（普通模式下）"></a>2. 光标移动（普通模式下）</h3><table><thead><tr><th align="left">按键</th><th align="left">功能说明</th></tr></thead><tbody><tr><td align="left"><code>h</code> &#x2F; <code>j</code> &#x2F; <code>k</code> &#x2F; <code>l</code></td><td align="left"><strong>左</strong> &#x2F; <strong>下</strong> &#x2F; <strong>上</strong> &#x2F; <strong>右</strong> 移动（远离方向键，保护手腕）</td></tr><tr><td align="left"><code>w</code> &#x2F; <code>b</code></td><td align="left">按<strong>单词</strong>向前跳 &#x2F; 向后跳</td></tr><tr><td align="left"><code>0</code> (数字零)</td><td align="left">跳到当前行的<strong>行首</strong></td></tr><tr><td align="left"><code>$</code></td><td align="left">跳到当前行的<strong>行尾</strong></td></tr><tr><td align="left"><code>gg</code></td><td align="left">跳到文件的<strong>第一行</strong></td></tr><tr><td align="left"><code>G</code></td><td align="left">跳到文件的<strong>最后一行</strong></td></tr><tr><td align="left"><code>Ctrl</code> <code>d</code> &#x2F; <code>Ctrl</code> <code>u</code></td><td align="left">向下 &#x2F; 向上滚动<strong>半页</strong></td></tr><tr><td align="left"><code>%</code></td><td align="left">跳转到匹配的括号 <code>()</code>、<code>&#123;&#125;</code>、<code>[]</code>（用于检查代码结构）</td></tr></tbody></table><h3 id="3-增删改查（编辑操作）"><a href="#3-增删改查（编辑操作）" class="headerlink" title="3. 增删改查（编辑操作）"></a>3. 增删改查（编辑操作）</h3><table><thead><tr><th align="left">按键</th><th align="left">功能说明</th></tr></thead><tbody><tr><td align="left"><code>x</code></td><td align="left">删除光标所在的<strong>单个字符</strong></td></tr><tr><td align="left"><code>dd</code></td><td align="left"><strong>剪切</strong>（删除）当前光标所在<strong>整行</strong></td></tr><tr><td align="left"><code>yy</code></td><td align="left"><strong>复制</strong>当前光标所在<strong>整行</strong></td></tr><tr><td align="left"><code>p</code> &#x2F; <code>P</code></td><td align="left">在光标<strong>之后</strong> &#x2F; <strong>之前</strong>粘贴剪切板内容</td></tr><tr><td align="left"><code>u</code></td><td align="left"><strong>撤销</strong> (Undo) 上一次操作</td></tr><tr><td align="left"><code>Ctrl</code> <code>r</code></td><td align="left"><strong>重做</strong> (Redo) 刚才撤销的操作</td></tr><tr><td align="left"><code>.</code></td><td align="left"><strong>重复</strong>上一次的修改操作（效率极高，多用）</td></tr></tbody></table><h3 id="4-文件与缓冲区操作（需按-进入命令模式）"><a href="#4-文件与缓冲区操作（需按-进入命令模式）" class="headerlink" title="4. 文件与缓冲区操作（需按 : 进入命令模式）"></a>4. 文件与缓冲区操作（需按 <code>:</code> 进入命令模式）</h3><table><thead><tr><th align="left">命令</th><th align="left">功能说明</th></tr></thead><tbody><tr><td align="left"><code>:w</code></td><td align="left"><strong>保存</strong>当前文件 (Write)</td></tr><tr><td align="left"><code>:q</code></td><td align="left"><strong>退出</strong> Neovim（仅当文件未修改时）</td></tr><tr><td align="left"><code>:wq</code> 或 <code>:x</code></td><td align="left"><strong>保存并退出</strong></td></tr><tr><td align="left"><code>:q!</code></td><td align="left"><strong>强制退出</strong>（放弃所有未保存的修改）</td></tr><tr><td align="left"><code>:e 文件名</code></td><td align="left">在当前窗口打开&#x2F;切换指定文件 (Edit)</td></tr><tr><td align="left"><code>:bd</code></td><td align="left"><strong>关闭</strong>当前缓冲区（关闭当前编辑的文件，不退出程序）</td></tr></tbody></table><h3 id="5-搜索与替换"><a href="#5-搜索与替换" class="headerlink" title="5. 搜索与替换"></a>5. 搜索与替换</h3><table><thead><tr><th align="left">按键 &#x2F; 命令</th><th align="left">功能说明</th></tr></thead><tbody><tr><td align="left"><code>/关键词</code></td><td align="left">在当前文件中<strong>向下</strong>搜索关键词</td></tr><tr><td align="left"><code>?关键词</code></td><td align="left">在当前文件中<strong>向上</strong>搜索关键词</td></tr><tr><td align="left"><code>n</code> &#x2F; <code>N</code></td><td align="left">跳转到<strong>下一个</strong> &#x2F; <strong>上一个</strong>搜索匹配项</td></tr><tr><td align="left"><code>:%s/旧词/新词/g</code></td><td align="left">将全文件中所有 <code>旧词</code> <strong>替换</strong>为 <code>新词</code></td></tr><tr><td align="left"><code>:%s/旧词/新词/gc</code></td><td align="left">全文件替换，但每次替换前都会问你是否<strong>确认</strong> (Confirm)</td></tr></tbody></table><h3 id="6-LazyVim-常用快捷键（默认-Leader-键-空格键）"><a href="#6-LazyVim-常用快捷键（默认-Leader-键-空格键）" class="headerlink" title="6. LazyVim 常用快捷键（默认 Leader 键 &#x3D; 空格键）"></a>6. LazyVim 常用快捷键（默认 Leader 键 &#x3D; 空格键）</h3><table><thead><tr><th align="left">快捷键</th><th align="left">功能说明</th></tr></thead><tbody><tr><td align="left"><code>Space</code></td><td align="left">即 Leader 键，几乎所有自定义快捷键的前缀</td></tr><tr><td align="left"><code>Space</code> <code>e</code></td><td align="left">打开&#x2F;关闭文件树 (Neo-tree)</td></tr><tr><td align="left"><code>Space</code> <code>f</code> <code>f</code></td><td align="left">查找项目文件 (Find Files)</td></tr><tr><td align="left"><code>Space</code> <code>s</code> <code>g</code></td><td align="left">全局文本搜索 (Live Grep，类似 VSCode 的全局搜索)</td></tr><tr><td align="left"><code>Space</code> <code>w</code> <code>w</code></td><td align="left">保存当前文件 (Write)</td></tr><tr><td align="left"><code>Space</code> <code>q</code> <code>q</code></td><td align="left">退出 Neovim (Quit)</td></tr><tr><td align="left"><code>Space</code> <code>x</code> <code>x</code></td><td align="left">打开当前文件的错误诊断列表 (Trouble)</td></tr><tr><td align="left"><code>Space</code> <code>/</code></td><td align="left">高亮搜索当前文件（并实时预览）</td></tr></tbody></table><h2 id="小技巧"><a href="#小技巧" class="headerlink" title="小技巧"></a>小技巧</h2><p>如果用的是 Gboard，Termux 会将 Gboard 锁定为英文键盘，无法输入中文。</p><p>但是，如果向左滑动 Termux 的小键盘，小键盘就会变成输入框，现在就可以输入中文了，按回车即可将中文从输入框输入到命令行。</p><p><img src="/2026/07/26/termux-neovim/slide.webp" alt="向左滑动"></p><p>另外，在左侧屏幕边缘向右滑动，可以滑出 Termux 侧边栏，可以添加新 session。</p><p>Termux 也真是的，把这些东西藏这么深，也不做个引导。</p><h2 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h2><p>Neovim 真的是很好的工具，虽然比较难，但是用熟了之后，绝对会让效率提升很多。</p><p>不过，如果有电脑，还是在电脑用 Neovim 比较好一点。</p><p>理论上，这篇文章对于 Linux 也通用，只要将 <code>pkg install</code> 换成对应的包管理器，将 LazyVim 换成官方版本就可以了。</p><p>就说这么多，进阶配置可以看 Neovim 和 LazyVim 官方文档，很详细了，这篇文章用来记录和备忘。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>Termux 配置 zsh 以及简单美化记录</title>
      <link>https://msqy-h.github.io/2026/07/25/termux-zsh/</link>
      <description>Termux 配置 zsh 和 美化的记录，让 Termux 更好看</description>
      <author>陌上轻烟</author>
      <category domain="https://msqy-h.github.io/categories/%E8%BD%AF%E4%BB%B6/">软件</category>
      <category domain="https://msqy-h.github.io/tags/Termux/">Termux</category>
      <category domain="https://msqy-h.github.io/tags/%E7%BB%88%E7%AB%AF/">终端</category>
      <enclosure url="https://msqy-h.github.io/2026/07/25/termux-zsh/cover.webp" type="image/webp"/>
      <pubDate>Sat, 25 Jul 2026 07:58:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h2><p>众所周知，Termux 是安卓手机上最好用的终端模拟器之一。我这个博客在写代码时就是用 Termux 预览的。<del>也就只有我这种人会用手机写代码了（笑）</del></p><p>但是，Termux 默认样式太丑了，还没有自动补全，有点麻烦。还是把 shell 换成 zsh 方便点，顺便安装 Oh My Zsh，好好美化一下。</p><p>这篇文章用作备忘，参考</p><a class="tag-link" href="https://zhuanlan.zhihu.com/p/1956286037249757963" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">［ Termux | Zsh |…</div>            <div class="tag-link-sitename">如果你发现自己的终端中有很多乱码…</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><h2 id="效果图"><a href="#效果图" class="headerlink" title="效果图"></a>效果图</h2><p><img src="/2026/07/25/termux-zsh/result.webp" alt="效果图"></p><h2 id="配置方法"><a href="#配置方法" class="headerlink" title="配置方法"></a>配置方法</h2><div class="note warning modern"><i class="solitude fas fa-warning"></i><p>配置方法可能因软件更新而不同，请注意检查版本号。</p></div><p>开发环境：</p><ul><li>Termux 0.118.3</li><li>zsh 5.9.1 (aarch64-unknown-linux-android)</li><li>Oh My Zsh version master (e1d1f0d)</li></ul><p>你需要准备：</p><ul><li>可以上网和运行终端的安卓设备</li><li>脑子</li><li>手</li></ul><p>让我们开始吧。</p><h3 id="1-安装-zsh"><a href="#1-安装-zsh" class="headerlink" title="1. 安装 zsh"></a>1. 安装 zsh</h3><p>安装 zsh 十分简单，直接</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pkg install zsh</span><br></pre></td></tr></table></figure><p>即可。如果下载时间过久，使用 <code>termux-change-repo</code> 切换仓库镜像。在弹出的页面选择 <code>Mirror group</code>，再选择 <code>Mirrors in Chinese</code>。</p><h3 id="2-安装-Oh-My-Zsh"><a href="#2-安装-Oh-My-Zsh" class="headerlink" title="2. 安装 Oh My Zsh"></a>2. 安装 Oh My Zsh</h3><p>输入</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sh -c &quot;$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)&quot;</span><br></pre></td></tr></table></figure><p>由于 GitHub 服务器在国外，访问速度慢，可能需要使用代理。后面也会使用到 GitHub，注意网络环境。</p><p>安装时会询问 <code>Do you want to change your default shell to zsh?</code>，直接按 <span class="keyboard-tag">Enter↵</span> 即可。</p><h3 id="3-安装-Powerlevel10k-主题"><a href="#3-安装-Powerlevel10k-主题" class="headerlink" title="3. 安装 Powerlevel10k 主题"></a>3. 安装 Powerlevel10k 主题</h3><p>输入</p><div class="tabs" id="安装主题"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#安装主题-0">GitHub</button></li><li class="tab"><button type="button" data-href="#安装主题-1">Gitee</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="安装主题-0"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone --depth=1 [https://github.com/romkatv/powerlevel10k.git](https://link.zhihu.com/?target=https%3A//github.com/romkatv/powerlevel10k.git) &quot;$&#123;ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom&#125;/themes/powerlevel10k&quot;</span><br></pre></td></tr></table></figure></div><div class="tab-item-content" id="安装主题-1"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone --depth=1 https://gitee.com/romkatv/powerlevel10k.git &quot;$&#123;ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom&#125;/themes/powerlevel10k&quot;</span><br></pre></td></tr></table></figure></div></div></div><p>中国大陆建议使用 Gitee 源。</p><h3 id="4-安装插件"><a href="#4-安装插件" class="headerlink" title="4. 安装插件"></a>4. 安装插件</h3><p>最好在配置之前先安装插件。</p><p>推荐几个我用的插件：</p><h4 id="zsh-autosuggestions"><a href="#zsh-autosuggestions" class="headerlink" title="- zsh-autosuggestions"></a>- zsh-autosuggestions</h4><p>能够提供输入建议，在输入时有灰色建议，按向右箭头即可补全。</p><p>安装方式：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone https://github.com/zsh-users/zsh-autosuggestions $&#123;ZSH_CUSTOM:-$&#123;ZSH:-~/.oh-my-zsh&#125;/custom&#125;/plugins/zsh-autosuggestions</span><br></pre></td></tr></table></figure><h4 id="zsh-syntax-highlighting"><a href="#zsh-syntax-highlighting" class="headerlink" title="- zsh-syntax-highlighting"></a>- zsh-syntax-highlighting</h4><p>用来语法高亮，如果命令拼写错，会显示为红色。</p><p>安装方式：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone https://github.com/zsh-users/zsh-syntax-highlighting.git $&#123;ZSH_CUSTOM:-$&#123;ZSH:-~/.oh-my-zsh&#125;/custom&#125;/plugins/zsh-syntax-highlighting</span><br></pre></td></tr></table></figure><h4 id="zsh-completions"><a href="#zsh-completions" class="headerlink" title="- zsh-completions"></a>- zsh-completions</h4><p>按 <span class="keyboard-tag">Tab</span>，可以显示子命令列表，快速补全你要的子命令。</p><p>安装方式：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone https://github.com/zsh-users/zsh-completions.git $&#123;ZSH_CUSTOM:-$&#123;ZSH:-~/.oh-my-zsh&#125;/custom&#125;/plugins/zsh-completions</span><br></pre></td></tr></table></figure><h2 id="5-配置"><a href="#5-配置" class="headerlink" title="5. 配置"></a>5. 配置</h2><p>Zsh 的配置文件是 <code>home</code> 目录下的 <code>.zshrc</code> 文件。打开这个文件。</p><p>这里说一下打开 Termux 的 <code>home</code> 目录文件的方法：</p><ul><li>使用命令行文本编辑器：我常用 Nano 和 Neovim。</li><li>使用 MT管理器：打开 MT管理器侧边栏，点击侧边栏右上角三个点，可以添加本地存储。点击后会弹出安卓文件管理器，选择侧边栏中的 Termux 即可。</li></ul><p>找到 <code>ZSH_THEME</code> 变量，改为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ZSH_THEME=&quot;powerlevel10k/powerlevel10k&quot;</span><br></pre></td></tr></table></figure><p>找到 <code>plugins</code> 变量，这里是 zsh 的插件配置，安装了插件后要在这里启用。将已安装的插件填进去，每个插件一行，或者使用空格隔开，像这样：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">plugins=(</span><br><span class="line">  git</span><br><span class="line">  sudo</span><br><span class="line">  zsh-autosuggestions</span><br><span class="line">  zsh-syntax-highlighting</span><br><span class="line">  zsh-completions</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p><code>git</code> 和 <code>sudo</code> 是 zsh 自带的，可以不用安装</p><p>配置完后，使用 <code>zsh ~/.zshrc</code> 或者直接重启 Termux 使配置生效。</p><h3 id="6-设置-Termux-字体与颜色"><a href="#6-设置-Termux-字体与颜色" class="headerlink" title="6. 设置 Termux 字体与颜色"></a>6. 设置 Termux 字体与颜色</h3><p>由于 powerlevel10k 主题使用的图标依赖字体，这里在配置主题前先设置 Termux 的字体。</p><p>替换 Termux 的字体过于麻烦，建议安装 Termux:Styling 插件。</p><p>你还记得你的 Termux 是在哪里下载的吗？如果是在 GitHub 下载的，那么插件也要在 GitHub 下载；F-Droid 同理；如果是在 Google Play 下的，那你还是换回前面两个渠道下载好一点，不是不能用，而是版本可能比较旧，安装一些包会有问题。</p><p>给出下载链接：</p><a class="tag-link" href="https://github.com/termux/termux-styling" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">GitHub - termux…</div>            <div class="tag-link-sitename">A Termux add-on app to customize…</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><a class="tag-link" href="https://f-droid.org/packages/com.termux.styling/" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">F-Droid - Termux…</div>            <div class="tag-link-sitename">Termux:Styling Customize…</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><p>安装好后，打开 Termux，长按任意一条输出，点击 <code>MORE</code>，选择 <code>Style</code>，点击 <code>CHOOSE FONT</code> 或 <code>CHOOSE COLOR</code> 就可以更改字体和颜色了。</p><p>我建议使用列表中的 Hack 字体，这个字体基本上有主题需要的所有图标，看着也很舒服。</p><p>配色我比较喜欢 <code>Base 16 Google Dark</code>。</p><p>这些看个人喜好，自己喜欢的就可以。</p><h3 id="7-配置-powerlevel10k-主题"><a href="#7-配置-powerlevel10k-主题" class="headerlink" title="7. 配置 powerlevel10k 主题"></a>7. 配置 powerlevel10k 主题</h3><p>输入</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">p10k configure</span><br></pre></td></tr></table></figure><p>就会进入配置页面。</p><p>第一个是否安装字体选择 n 即可，因为已经设置了支持 powerlevel10k 主题需要用的图标的字体。选择图标数量那里，建议选择 <code>Many icons</code>，不然就跟普通主题没啥区别了，当然你想简洁一点也可以。其他配置看个人喜好。</p><p>配置完成后，就可以了。你也可以输入 <code>p10k configure</code> 重新配置。</p><h2 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h2><p>这篇文章写了 Termux 配置 zsh 和 powerlevel10k 主题的方式。Termux 现在好看多了。</p><p>说到 Termux，以后我打算写写 Neovim 的安装和懒人配置方式。<span class="spoiler block-text">在手机用 Neovim？感觉有点豪了</span></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>Minecraft 基岩版光线追踪配置实录</title>
      <link>https://msqy-h.github.io/2026/07/14/minecraft-rtx/</link>
      <description>Minecraft 基岩版光线追踪的配置记录与教程，提取官方资源包并应用到游戏中</description>
      <author>陌上轻烟</author>
      <category domain="https://msqy-h.github.io/categories/%E6%B8%B8%E6%88%8F/">游戏</category>
      <category domain="https://msqy-h.github.io/tags/Minecraft/">Minecraft</category>
      <category domain="https://msqy-h.github.io/tags/%E8%A7%86%E8%A7%89/">视觉</category>
      <enclosure url="https://msqy-h.github.io/2026/07/14/minecraft-rtx/cover.webp" type="image/webp"/>
      <pubDate>Tue, 14 Jul 2026 07:09:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h2><p>之前微软为 Minecraft 基岩版添加了光线追踪，但不知什么原因，成了半成品，现在被灵动视效取代了😭。<span class="spoiler blur-text">微软发力了</span></p><p>我还是更喜欢光线追踪。光线追踪的配置其实挺麻烦的，所以在这里写个教程，做个备忘。</p><h2 id="效果图"><a href="#效果图" class="headerlink" title="效果图"></a>效果图</h2><p><img src="/2026/07/14/minecraft-rtx/result.webp" alt="效果图"></p><h2 id="配置方法"><a href="#配置方法" class="headerlink" title="配置方法"></a>配置方法</h2><div class="note warning modern"><i class="solitude fas fa-warning"></i><p>游戏版本为 26.33，理论上支持光线追踪的版本（1.16.200 以后）均可用。基岩版更新十分频繁，如果该方法不可用，可以通过<a href="mailto:&#x48;&#x79;&#100;&#x72;&#111;&#120;&#x69;&#100;&#x5f;&#72;&#x75;&#97;&#x6c;&#105;&#x6e;&#x40;&#111;&#x75;&#x74;&#x6c;&#111;&#x6f;&#107;&#46;&#99;&#x6f;&#109;">电子邮件</a>联系我。</p></div><h3 id="1-下载官方模板"><a href="#1-下载官方模板" class="headerlink" title="1. 下载官方模板"></a>1. 下载官方模板</h3><p>在商店中搜索 <strong>RTX</strong>，下载官方模板。</p><p><img src="/2026/07/14/minecraft-rtx/download.webp" alt="搜索 RTX"></p><p>个人比较喜欢 Color, light and shadow RTX，偏现代一点。</p><p><img src="/2026/07/14/minecraft-rtx/rtx.webp" alt="Color, light and shadow RTX"></p><h3 id="2-找到资源包"><a href="#2-找到资源包" class="headerlink" title="2. 找到资源包"></a>2. 找到资源包</h3><p>下载并创建世界后，打开 <code>C:\Users\你的用户名\AppData\Roaming\Minecraft Bedrock\premium_cache\world_templates\</code></p><p>接着会有好几个乱码的文件夹。不必理会，打开后点一下里面的图片，就知道是什么模板了。</p><p><img src="/2026/07/14/minecraft-rtx/template.webp" alt="模板文件夹"></p><p>找到想要的模板文件夹，打开 <code>resource_packs\rp0</code></p><p>你可能会看到类似这些文件：</p><p><img src="/2026/07/14/minecraft-rtx/resource_pack.webp" alt="资源包"></p><p>全选，压缩为 <code>.zip</code> 格式。更改后缀名为 <code>.mcpack</code>。双击导入到 Minecraft。</p><h3 id="3-启用资源包"><a href="#3-启用资源包" class="headerlink" title="3. 启用资源包"></a>3. 启用资源包</h3><p>进入 Minecraft，找到想应用光追的世界，点击编辑世界，启用刚刚导入的资源包，像这样就可以了。</p><p><img src="/2026/07/14/minecraft-rtx/enable.webp" alt="启用"></p><h3 id="4-启用光线追踪"><a href="#4-启用光线追踪" class="headerlink" title="4. 启用光线追踪"></a>4. 启用光线追踪</h3><p>进入世界，打开显示设置，选择画面为光线追踪。返回，完成！</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>实际上，我们也可以改用第三方的资源包，这样更方便，双击导入就可以了。不过本人更喜欢官方的光追，所以就写了这么一篇操作方法。</p><p>享受 Minecraft 吧！</p><p>✨</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>Hexo Solitude 博客懒加载的问题与解决</title>
      <link>https://msqy-h.github.io/2026/07/12/hexo-lazyload/</link>
      <description>Hexo Solitude 主题懒加载功能的问题与解决方案</description>
      <author>陌上轻烟</author>
      <category domain="https://msqy-h.github.io/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      <category domain="https://msqy-h.github.io/tags/Hexo/">Hexo</category>
      <category domain="https://msqy-h.github.io/tags/Solitude/">Solitude</category>
      <enclosure url="https://msqy-h.github.io/2026/07/12/hexo-lazyload/cover.webp" type="image/webp"/>
      <pubDate>Sun, 12 Jul 2026 09:47:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p><img src="https://msqy-h.github.io/2026/07/12/hexo-lazyload/cover.webp" alt="Hexo Solitude 博客懒加载的问题与解决" /></p>
<h2 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h2><p>配置过 Solitude 主题的都知道， Solitude 主题有图片懒加载功能。配置如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">lazyload:</span></span><br><span class="line">  <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">field:</span> <span class="string">site</span></span><br><span class="line">  <span class="attr">placeholder:</span> <span class="string">&quot;&quot;</span></span><br><span class="line">  <span class="attr">errorimg:</span> <span class="string">/img/error_load.webp</span></span><br></pre></td></tr></table></figure><p>在我的环境下：</p><ul><li>Node.js 版本：24.17.0</li><li>Yarn 版本：4.17.0</li><li>Hexo 版本：7.3.0</li><li>Solitude 版本：3.0.21</li><li>预览方式：<code>hexo g &amp;&amp; npx serve public</code></li></ul><p><code>field</code> 配置为 <code>post</code> 时，图片懒加载完全没问题，只是文章以外的其它页面就没有懒加载。</p><p><code>field</code> 配置为 <code>site</code> 时，网站就会崩溃，就跟我的单页网站缺少了 js 一样，什么都点不了。</p><p>我们想要完整的图片懒加载，怎么办？</p><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><h3 id="1-使用-post-模式"><a href="#1-使用-post-模式" class="headerlink" title="1. 使用 post 模式"></a>1. 使用 <code>post</code> 模式</h3><p>懒得改主题源码，就用 <code>post</code> 模式吧。</p><p>但是，愿意折腾的我们，怎么能安于现状？</p><h3 id="2-修改主题源码"><a href="#2-修改主题源码" class="headerlink" title="2. 修改主题源码"></a>2. 修改主题源码</h3><p>打开 <code>博客根目录/themes/solitude/scripts/filter/lazyload.js</code></p><p>源代码直接使用正则表达式操作 html 实现懒加载：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">lazyload</span> = (<span class="params">content, img</span>) =&gt; &#123;</span><br><span class="line">    <span class="keyword">return</span> content.<span class="title function_">replace</span>(<span class="regexp">/(&lt;img(?!.*?class[\t]*=[\t]*[&#x27;&quot;].*?nolazyload.*?[&#x27;&quot;]).*? src=)/gi</span>, <span class="string">`$1 &quot;<span class="subst">$&#123;img&#125;</span>&quot; data-lazy-src=`</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>html 千变万化，正则可能破坏掉 html 标签导致页面出现错误，可能波及 <code>&lt;scripts&gt;</code> 标签。<span class="spoiler blur-text">祖传代码发力了（</span></p><p>至于 <code>post</code> 模式能用，应该是因为 <code>post</code> 模式处理的文章界面相对简单，正则刚好可以处理</p><p>所以要换种处理方式，用 <code>cheerio</code> 这种成熟的处理 html 的库。</p><div class="note warning modern"><i class="solitude fas fa-warning"></i><p>以下代码针对我的开发环境编写，不一定通用，并非官方方案，此脚本仅作参考。</p></div><p>先安装 <code>cheerio</code> 库。</p><div class="tabs" id="安装插件"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#安装插件-0">npm</button></li><li class="tab"><button type="button" data-href="#安装插件-1">pnpm</button></li><li class="tab"><button type="button" data-href="#安装插件-2">yarn</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="安装插件-0"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install cheerio --save</span><br></pre></td></tr></table></figure></div><div class="tab-item-content" id="安装插件-1"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pnpm add cheerio</span><br></pre></td></tr></table></figure></div><div class="tab-item-content" id="安装插件-2"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add cheerio</span><br></pre></td></tr></table></figure></div></div></div><p>然后将 <code>博客根目录/themes/solitude/scripts/filter/lazyload.js</code> 全部替换为以下代码：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&#x27;use strict&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> cheerio = <span class="built_in">require</span>(<span class="string">&#x27;cheerio&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">lazyload</span> = (<span class="params">content, imgUrl</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> $ = cheerio.<span class="title function_">load</span>(content, &#123; <span class="attr">decodeEntities</span>: <span class="literal">false</span> &#125;);</span><br><span class="line">  <span class="comment">// 选取所有 img，排除带有 nolazyload 类的图片</span></span><br><span class="line">  $(<span class="string">&#x27;img:not(.nolazyload)&#x27;</span>).<span class="title function_">each</span>(<span class="function">(<span class="params">i, el</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> $el = $(el);</span><br><span class="line">    <span class="keyword">const</span> src = $el.<span class="title function_">attr</span>(<span class="string">&#x27;src&#x27;</span>);</span><br><span class="line">    <span class="comment">// 跳过 base64、svg 或已经带 data-lazy-src 的图片</span></span><br><span class="line">    <span class="keyword">if</span> (src &amp;&amp; !src.<span class="title function_">startsWith</span>(<span class="string">&#x27;data:&#x27;</span>) &amp;&amp; !$el.<span class="title function_">attr</span>(<span class="string">&#x27;data-lazy-src&#x27;</span>)) &#123;</span><br><span class="line">      $el.<span class="title function_">attr</span>(<span class="string">&#x27;data-lazy-src&#x27;</span>, src); <span class="comment">// 存真实地址</span></span><br><span class="line">      $el.<span class="title function_">attr</span>(<span class="string">&#x27;src&#x27;</span>, imgUrl);        <span class="comment">// 替换为占位图</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="keyword">return</span> $.<span class="title function_">html</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 注册全站过滤器 (site)</span></span><br><span class="line">hexo.<span class="property">extend</span>.<span class="property">filter</span>.<span class="title function_">register</span>(<span class="string">&#x27;after_render:html&#x27;</span>, <span class="keyword">function</span> (<span class="params">data</span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; enable, placeholder, field &#125; = hexo.<span class="property">theme</span>.<span class="property">config</span>.<span class="property">lazyload</span>;</span><br><span class="line">  <span class="keyword">if</span> (!enable || field !== <span class="string">&#x27;site&#x27;</span>) <span class="keyword">return</span>;</span><br><span class="line">  <span class="keyword">return</span> <span class="title function_">lazyload</span>(data, placeholder);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 保留 post 过滤器（以防你切换）</span></span><br><span class="line">hexo.<span class="property">extend</span>.<span class="property">filter</span>.<span class="title function_">register</span>(<span class="string">&#x27;after_post_render&#x27;</span>, <span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; enable, placeholder, field &#125; = hexo.<span class="property">theme</span>.<span class="property">config</span>.<span class="property">lazyload</span>;</span><br><span class="line">  <span class="keyword">if</span> (!enable || field !== <span class="string">&#x27;post&#x27;</span>) <span class="keyword">return</span>;</span><br><span class="line">  data.<span class="property">content</span> = <span class="title function_">lazyload</span>(data.<span class="property">content</span>, placeholder);</span><br><span class="line">  <span class="keyword">return</span> data;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>然后预览：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">hexo cl</span><br><span class="line">hexo g</span><br><span class="line">npx serve public</span><br></pre></td></tr></table></figure><p>现在懒加载正常了！🎉🎉🎉</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>经过我们的不懈努力，Hexo Solitude 主题的懒加载终于正常了！</p><p>这个问题根源在于正则不适合处理 html，而主题刚好采用了这种方式。也许在别人那刚好能用，到我这里刚好用不了吧。</p><p>✨</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>Hexo Solitude 的本地预览问题与解决方案</title>
      <link>https://msqy-h.github.io/2026/07/10/hexo-preview/</link>
      <description>Hexo Solitude 主题外挂标签在本地预览时样式丢失的解决思路</description>
      <author>陌上轻烟</author>
      <category domain="https://msqy-h.github.io/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      <category domain="https://msqy-h.github.io/tags/Hexo/">Hexo</category>
      <category domain="https://msqy-h.github.io/tags/Solitude/">Solitude</category>
      <enclosure url="https://msqy-h.github.io/2026/07/10/hexo-preview/cover.webp" type="image/webp"/>
      <pubDate>Fri, 10 Jul 2026 09:25:50 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p><img src="https://msqy-h.github.io/2026/07/10/hexo-preview/cover.webp" alt="Hexo Solitude 的本地预览问题与解决方案" /></p>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"><h2 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h2><p>配置 Solitude 主题时，我遇到了个天大的问题。</p><p>Solitude 主题是支持 Hexo 内置的标签语法和 Solitude 的扩展标签。不过，扩展标签要装插件。</p><div class="tabs" id="安装插件"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#安装插件-0">npm</button></li><li class="tab"><button type="button" data-href="#安装插件-1">pnpm</button></li><li class="tab"><button type="button" data-href="#安装插件-2">yarn</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="安装插件-0"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-solitude-tag --save</span><br></pre></td></tr></table></figure></div><div class="tab-item-content" id="安装插件-1"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pnpm add hexo-solitude-tag</span><br></pre></td></tr></table></figure></div><div class="tab-item-content" id="安装插件-2"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add hexo-solitude-tag</span><br></pre></td></tr></table></figure></div></div></div><p>很简单。具体的语法，请看官方文档。<span class="spoiler blur-text">话说为什么是放在 demo 而不是文档啊</span></p><a class="tag-link" href="https://everfu.github.io/solitude-demo/posts/8f9926b7.html" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">外挂标签使用 | Solitude</div>            <div class="tag-link-sitename">这篇文章介绍了外挂标签的使用，包括内置标…</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><p>但是，遇到了问题。</p><h2 id="大问题"><a href="#大问题" class="headerlink" title="大问题"></a>大问题</h2><p>正当我兴致十足，执行了 Hexo 三连，打开了 <code>localhost:3000</code> 后：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo cl &amp;&amp; hexo g &amp;&amp; hexo s</span><br></pre></td></tr></table></figure><p>没想到，标签的样式竟然消失了！</p><p>本来这是个按钮：</p><button class="st-btn" onclick="window.open(&#39;https:&#x2F;&#x2F;github.com&#x2F;MSQY-H&#39;)"><i class="solitude fa-brands fa-github"></i><span>我的GitHub</span></button><p>结果变成了：</p><p><i class="fa-brands fa-github"></i>我的GitHub</p><p>那可难搞了。</p><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><p>经过与B大哥与D大哥<span class="spoiler blur-text">（必应和 Deepseek）</span>两位好朋友的探讨研究后，我终于发现了问题。</p><p>问题在于 <code>hexo s</code>。它的生成机制与 <code>hexo g</code> 不同，可能内部会与插件产生冲突。</p><p>其实不止我一个人有这个问题，网上很多人在使用插件时也有类似问题。</p><h3 id="手敲命令"><a href="#手敲命令" class="headerlink" title="手敲命令"></a>手敲命令</h3><p>其实要解决这个问题很简单，只要分别输入：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hexo g</span><br><span class="line">npx serve public</span><br></pre></td></tr></table></figure><p>就可以了。（不一定要用 <code>npx serve public</code>，其他可以启动服务器的命令也可以）</p><p>其实原理非常简单：虽然 <code>hexo s</code> 实现机制太过复杂，可能会出现问题，但是 <code>hexo g</code> 不会有这个问题（要是问题还在，就检查一下代码或者给开发者提 issue 吧）</p><p>所以我们曲线救国：通过 <code>hexo g</code> 生成静态文件，再通过 <code>npx serve public</code> 启动本地服务器。</p><p>问题解决了，完美！</p><p>但是，这种方式有缺点：</p><ul><li>没有文件监听，每次都要手动敲，太麻烦了！</li></ul><h3 id="万物皆可脚本"><a href="#万物皆可脚本" class="headerlink" title="万物皆可脚本"></a>万物皆可脚本</h3><p><del>因为懒</del>为了让生活更方便，我 Vibe Coding了一下，和D大哥聊了 15 分钟，写了个脚本。</p><p>其实这个脚本核心逻辑是这样的：</p><ul><li>使用 chokidar 监听文件变动</li><li>执行 <code>hexo g --incremental</code></li><li>启动静态服务器</li></ul><div class="note warning modern"><i class="solitude fas fa-warning"></i><p>以下脚本针对我的开发环境编写，不一定通用，并非官方方案，此脚本仅作参考，建议你自己 Vibe Coding。</p></div><ul><li>Node.js 版本：24.17.0</li><li>Yarn 版本：4.17.0</li><li>Hexo 版本：7.3.0</li><li>Solitude 版本：3.0.21</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br><span class="line">472</span><br><span class="line">473</span><br><span class="line">474</span><br><span class="line">475</span><br><span class="line">476</span><br><span class="line">477</span><br><span class="line">478</span><br><span class="line">479</span><br><span class="line">480</span><br><span class="line">481</span><br><span class="line">482</span><br><span class="line">483</span><br><span class="line">484</span><br><span class="line">485</span><br><span class="line">486</span><br><span class="line">487</span><br><span class="line">488</span><br><span class="line">489</span><br><span class="line">490</span><br><span class="line">491</span><br><span class="line">492</span><br><span class="line">493</span><br><span class="line">494</span><br><span class="line">495</span><br><span class="line">496</span><br><span class="line">497</span><br><span class="line">498</span><br><span class="line">499</span><br><span class="line">500</span><br><span class="line">501</span><br><span class="line">502</span><br><span class="line">503</span><br><span class="line">504</span><br><span class="line">505</span><br><span class="line">506</span><br><span class="line">507</span><br><span class="line">508</span><br><span class="line">509</span><br><span class="line">510</span><br><span class="line">511</span><br><span class="line">512</span><br><span class="line">513</span><br><span class="line">514</span><br><span class="line">515</span><br><span class="line">516</span><br><span class="line">517</span><br><span class="line">518</span><br><span class="line">519</span><br><span class="line">520</span><br><span class="line">521</span><br><span class="line">522</span><br><span class="line">523</span><br><span class="line">524</span><br><span class="line">525</span><br><span class="line">526</span><br><span class="line">527</span><br><span class="line">528</span><br><span class="line">529</span><br><span class="line">530</span><br><span class="line">531</span><br><span class="line">532</span><br><span class="line">533</span><br><span class="line">534</span><br><span class="line">535</span><br><span class="line">536</span><br><span class="line">537</span><br><span class="line">538</span><br><span class="line">539</span><br><span class="line">540</span><br><span class="line">541</span><br><span class="line">542</span><br><span class="line">543</span><br><span class="line">544</span><br><span class="line">545</span><br><span class="line">546</span><br><span class="line">547</span><br><span class="line">548</span><br><span class="line">549</span><br><span class="line">550</span><br><span class="line">551</span><br><span class="line">552</span><br><span class="line">553</span><br><span class="line">554</span><br><span class="line">555</span><br><span class="line">556</span><br><span class="line">557</span><br><span class="line">558</span><br><span class="line">559</span><br><span class="line">560</span><br><span class="line">561</span><br><span class="line">562</span><br><span class="line">563</span><br><span class="line">564</span><br><span class="line">565</span><br><span class="line">566</span><br><span class="line">567</span><br><span class="line">568</span><br><span class="line">569</span><br><span class="line">570</span><br><span class="line">571</span><br><span class="line">572</span><br><span class="line">573</span><br><span class="line">574</span><br><span class="line">575</span><br><span class="line">576</span><br><span class="line">577</span><br><span class="line">578</span><br><span class="line">579</span><br><span class="line">580</span><br><span class="line">581</span><br><span class="line">582</span><br><span class="line">583</span><br><span class="line">584</span><br><span class="line">585</span><br><span class="line">586</span><br><span class="line">587</span><br><span class="line">588</span><br><span class="line">589</span><br><span class="line">590</span><br><span class="line">591</span><br><span class="line">592</span><br><span class="line">593</span><br><span class="line">594</span><br><span class="line">595</span><br><span class="line">596</span><br><span class="line">597</span><br><span class="line">598</span><br><span class="line">599</span><br><span class="line">600</span><br><span class="line">601</span><br><span class="line">602</span><br><span class="line">603</span><br><span class="line">604</span><br><span class="line">605</span><br><span class="line">606</span><br><span class="line">607</span><br><span class="line">608</span><br><span class="line">609</span><br><span class="line">610</span><br><span class="line">611</span><br><span class="line">612</span><br><span class="line">613</span><br><span class="line">614</span><br><span class="line">615</span><br><span class="line">616</span><br><span class="line">617</span><br><span class="line">618</span><br><span class="line">619</span><br><span class="line">620</span><br><span class="line">621</span><br><span class="line">622</span><br><span class="line">623</span><br><span class="line">624</span><br><span class="line">625</span><br><span class="line">626</span><br><span class="line">627</span><br><span class="line">628</span><br><span class="line">629</span><br><span class="line">630</span><br><span class="line">631</span><br><span class="line">632</span><br><span class="line">633</span><br><span class="line">634</span><br><span class="line">635</span><br><span class="line">636</span><br><span class="line">637</span><br><span class="line">638</span><br><span class="line">639</span><br><span class="line">640</span><br><span class="line">641</span><br><span class="line">642</span><br><span class="line">643</span><br><span class="line">644</span><br><span class="line">645</span><br><span class="line">646</span><br><span class="line">647</span><br><span class="line">648</span><br><span class="line">649</span><br><span class="line">650</span><br><span class="line">651</span><br><span class="line">652</span><br><span class="line">653</span><br><span class="line">654</span><br><span class="line">655</span><br><span class="line">656</span><br><span class="line">657</span><br><span class="line">658</span><br><span class="line">659</span><br><span class="line">660</span><br><span class="line">661</span><br><span class="line">662</span><br><span class="line">663</span><br><span class="line">664</span><br><span class="line">665</span><br><span class="line">666</span><br><span class="line">667</span><br><span class="line">668</span><br><span class="line">669</span><br><span class="line">670</span><br><span class="line">671</span><br><span class="line">672</span><br><span class="line">673</span><br><span class="line">674</span><br><span class="line">675</span><br><span class="line">676</span><br><span class="line">677</span><br><span class="line">678</span><br><span class="line">679</span><br><span class="line">680</span><br><span class="line">681</span><br><span class="line">682</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; spawn, execSync &#125; = <span class="built_in">require</span>(<span class="string">&#x27;child_process&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">&#x27;fs&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> os = <span class="built_in">require</span>(<span class="string">&#x27;os&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> chokidar = <span class="built_in">require</span>(<span class="string">&#x27;chokidar&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> serveStatic = <span class="built_in">require</span>(<span class="string">&#x27;serve-static&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- 参数解析 ----------</span></span><br><span class="line"><span class="keyword">let</span> <span class="variable constant_">PORT</span> = <span class="number">3000</span>;</span><br><span class="line"><span class="keyword">let</span> silent = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">let</span> quickMode = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">let</span> directServe = <span class="literal">false</span>;   <span class="comment">// 新增：直接启动服务器，跳过首次构建</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> args = process.<span class="property">argv</span>.<span class="title function_">slice</span>(<span class="number">2</span>);</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; args.<span class="property">length</span>; i++) &#123;</span><br><span class="line">  <span class="keyword">const</span> arg = args[i];</span><br><span class="line">  <span class="keyword">if</span> (arg === <span class="string">&#x27;-h&#x27;</span> || arg === <span class="string">&#x27;--help&#x27;</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`</span></span><br><span class="line"><span class="string">使用方法: node dev.js [端口] [选项]</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">选项:</span></span><br><span class="line"><span class="string">  -s, --silent    静默模式，不输出 hexo 日志（错误除外）</span></span><br><span class="line"><span class="string">  -q, --quick     快速模式，省略手动添加的延迟（防抖延迟除外）</span></span><br><span class="line"><span class="string">  -d, --direct    直接启动服务器，跳过首次构建（适用于已生成 public 的情况）</span></span><br><span class="line"><span class="string">  -h, --help      显示此帮助信息</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">示例:</span></span><br><span class="line"><span class="string">  node dev.js 4000</span></span><br><span class="line"><span class="string">  node dev.js -s 4000</span></span><br><span class="line"><span class="string">  node dev.js -q -d</span></span><br><span class="line"><span class="string">`</span>);</span><br><span class="line">    process.<span class="title function_">exit</span>(<span class="number">0</span>);</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (arg === <span class="string">&#x27;-s&#x27;</span> || arg === <span class="string">&#x27;--silent&#x27;</span>) &#123;</span><br><span class="line">    silent = <span class="literal">true</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (arg === <span class="string">&#x27;-q&#x27;</span> || arg === <span class="string">&#x27;--quick&#x27;</span>) &#123;</span><br><span class="line">    quickMode = <span class="literal">true</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (arg === <span class="string">&#x27;-d&#x27;</span> || arg === <span class="string">&#x27;--direct&#x27;</span>) &#123;</span><br><span class="line">    directServe = <span class="literal">true</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="regexp">/^\d+$/</span>.<span class="title function_">test</span>(arg) &amp;&amp; <span class="variable constant_">PORT</span> === <span class="number">3000</span>) &#123;</span><br><span class="line">    <span class="variable constant_">PORT</span> = <span class="built_in">parseInt</span>(arg, <span class="number">10</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">CWD</span> = process.<span class="title function_">cwd</span>();</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">PUBLIC_DIR</span> = path.<span class="title function_">join</span>(<span class="variable constant_">CWD</span>, <span class="string">&#x27;public&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">WATCH_PATHS</span> = [</span><br><span class="line">  <span class="string">&#x27;source&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;_config.yml&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;scaffolds&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;themes&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;package.json&#x27;</span>,</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">DEBOUNCE_DELAY</span> = <span class="number">300</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">BAR_LENGTH</span> = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">BOUNCE_WIDTH</span> = <span class="number">3</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">FRAME_ACTIVE</span> = <span class="number">100</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">FRAME_IDLE</span> = <span class="number">500</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">FILL_STAY</span> = quickMode ? <span class="number">0</span> : <span class="number">500</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">SPINNER_FRAMES</span> = [<span class="string">&#x27;⠋&#x27;</span>, <span class="string">&#x27;⠙&#x27;</span>, <span class="string">&#x27;⠹&#x27;</span>, <span class="string">&#x27;⠸&#x27;</span>, <span class="string">&#x27;⠼&#x27;</span>, <span class="string">&#x27;⠴&#x27;</span>, <span class="string">&#x27;⠦&#x27;</span>, <span class="string">&#x27;⠧&#x27;</span>, <span class="string">&#x27;⠇&#x27;</span>, <span class="string">&#x27;⠏&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- 状态变量 ----------</span></span><br><span class="line"><span class="keyword">let</span> isBuilding = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">let</span> buildTimer = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">let</span> server = <span class="literal">null</span>;           <span class="comment">// 存储 http.Server 实例</span></span><br><span class="line"><span class="keyword">let</span> watcher = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">let</span> lastBuildFailed = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">let</span> watcherReady = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> sliderWidth = <span class="variable constant_">BOUNCE_WIDTH</span>;</span><br><span class="line"><span class="keyword">let</span> sliderPos = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> dir = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">let</span> progressMode = <span class="string">&#x27;bounce&#x27;</span>;</span><br><span class="line"><span class="keyword">let</span> renderTimeout = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">let</span> spinnerIdx = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> pendingLogs = [];</span><br><span class="line"><span class="keyword">let</span> currentBuildProcess = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> initializing = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">let</span> serverStarting = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">let</span> buildStartTime = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> firstBuild = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">let</span> hexoErrorContext = <span class="literal">false</span>;  <span class="comment">// 错误上下文标志</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- 颜色常量 ----------</span></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">RESET</span> = <span class="string">&#x27;\x1b[0m&#x27;</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">BG_BUILD</span> = <span class="string">&#x27;\x1b[48;5;208m\x1b[30m&#x27;</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">BG_RUN</span>   = <span class="string">&#x27;\x1b[42m\x1b[37m&#x27;</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">BG_FAIL</span>  = <span class="string">&#x27;\x1b[41m\x1b[37m&#x27;</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">BG_INIT</span>  = <span class="string">&#x27;\x1b[44m\x1b[37m&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">LEVEL_COLORS</span> = &#123;</span><br><span class="line">  <span class="attr">INFO</span>:  <span class="string">&#x27;\x1b[36m&#x27;</span>,</span><br><span class="line">  <span class="attr">WARN</span>:  <span class="string">&#x27;\x1b[33m&#x27;</span>,</span><br><span class="line">  <span class="attr">ERROR</span>: <span class="string">&#x27;\x1b[31m&#x27;</span>,</span><br><span class="line">  <span class="attr">HEXO_ERROR</span>: <span class="string">&#x27;\x1b[31m&#x27;</span>,</span><br><span class="line">  <span class="attr">SUCCESS</span>:<span class="string">&#x27;\x1b[32m&#x27;</span>,</span><br><span class="line">  <span class="attr">DEBUG</span>: <span class="string">&#x27;\x1b[35m&#x27;</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- 工具函数 ----------</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">formatTime</span>(<span class="params">seconds</span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (seconds &gt;= <span class="number">60</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> mins = <span class="title class_">Math</span>.<span class="title function_">floor</span>(seconds / <span class="number">60</span>);</span><br><span class="line">    <span class="keyword">const</span> secs = <span class="title class_">Math</span>.<span class="title function_">floor</span>(seconds % <span class="number">60</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">`<span class="subst">$&#123;mins&#125;</span>m<span class="subst">$&#123;secs&#125;</span>s`</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="string">`<span class="subst">$&#123;seconds.toFixed(<span class="number">1</span>)&#125;</span>s`</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getProgressBar</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> bg = <span class="string">&#x27;-&#x27;</span>, fg = <span class="string">&#x27;#&#x27;</span>;</span><br><span class="line">  <span class="keyword">if</span> (progressMode === <span class="string">&#x27;fill&#x27;</span>) <span class="keyword">return</span> fg.<span class="title function_">repeat</span>(<span class="variable constant_">BAR_LENGTH</span>);</span><br><span class="line">  <span class="keyword">const</span> w = <span class="title class_">Math</span>.<span class="title function_">round</span>(sliderWidth);</span><br><span class="line">  <span class="keyword">const</span> pos = <span class="title class_">Math</span>.<span class="title function_">round</span>(sliderPos);</span><br><span class="line">  <span class="keyword">let</span> bar = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="variable constant_">BAR_LENGTH</span>; i++) bar += (i &gt;= pos &amp;&amp; i &lt; pos + w) ? fg : bg;</span><br><span class="line">  <span class="keyword">return</span> bar;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- 统一日志系统 ----------</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">flushLogs</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (!pendingLogs.<span class="property">length</span>) <span class="keyword">return</span>;</span><br><span class="line">  <span class="keyword">const</span> lines = [...pendingLogs];</span><br><span class="line">  pendingLogs = [];</span><br><span class="line">  process.<span class="property">stdout</span>.<span class="title function_">write</span>(<span class="string">&#x27;\r\x1b[K&#x27;</span>);</span><br><span class="line">  lines.<span class="title function_">forEach</span>(<span class="function"><span class="params">l</span> =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(l));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">YARN_PATTERN</span> = <span class="regexp">/This project is configured to use yarn/i</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">outputLog</span>(<span class="params">category, levelOrType, message, extra = &#123;&#125;</span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (category === <span class="string">&#x27;hexo&#x27;</span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> line = message.<span class="title function_">trimEnd</span>();</span><br><span class="line">    <span class="keyword">if</span> (!line || <span class="variable constant_">YARN_PATTERN</span>.<span class="title function_">test</span>(line)) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> match = line.<span class="title function_">match</span>(<span class="regexp">/^(INFO|WARN|ERROR|DEBUG)\s+(.*)/i</span>);</span><br><span class="line">    <span class="keyword">let</span> level = <span class="string">&#x27;INFO&#x27;</span>, msg = line;</span><br><span class="line">    <span class="keyword">if</span> (match) &#123; level = match[<span class="number">1</span>].<span class="title function_">toUpperCase</span>(); msg = match[<span class="number">2</span>]; &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="regexp">/\b(FATAL|YAMLException)\b/i</span>.<span class="title function_">test</span>(line)) &#123;</span><br><span class="line">      level = <span class="string">&#x27;ERROR&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> isFatal = <span class="regexp">/\b(FATAL|YAMLException)\b/i</span>.<span class="title function_">test</span>(line);</span><br><span class="line">    <span class="keyword">const</span> isErrorDesc = <span class="regexp">/Error:\s/i</span>.<span class="title function_">test</span>(line);</span><br><span class="line">    <span class="keyword">const</span> isCodeLine = <span class="regexp">/^\s+\d+\s*\|/</span>.<span class="title function_">test</span>(line) || line.<span class="title function_">includes</span>(<span class="string">&#x27;---^&#x27;</span>);</span><br><span class="line">    <span class="keyword">const</span> isStackLine = <span class="regexp">/^\s+at\s/</span>.<span class="title function_">test</span>(line);</span><br><span class="line">    <span class="keyword">const</span> isError = (level === <span class="string">&#x27;ERROR&#x27;</span>) || isFatal || isErrorDesc || isCodeLine || isStackLine;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 错误上下文管理</span></span><br><span class="line">    <span class="keyword">if</span> (isError) &#123;</span><br><span class="line">      hexoErrorContext = <span class="literal">true</span>;   <span class="comment">// 进入错误上下文</span></span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (hexoErrorContext) &#123;</span><br><span class="line">      <span class="keyword">if</span> (level === <span class="string">&#x27;INFO&#x27;</span> &amp;&amp; !isError) &#123;</span><br><span class="line">        hexoErrorContext = <span class="literal">false</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (level === <span class="string">&#x27;DEBUG&#x27;</span>) &#123;</span><br><span class="line">        hexoErrorContext = <span class="literal">false</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (level === <span class="string">&#x27;WARN&#x27;</span>) &#123;</span><br><span class="line">        <span class="comment">// 保留上下文</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (silent &amp;&amp; !isError &amp;&amp; !hexoErrorContext) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> prefixColor;</span><br><span class="line">    <span class="keyword">if</span> (level === <span class="string">&#x27;INFO&#x27;</span>) prefixColor = <span class="variable constant_">LEVEL_COLORS</span>.<span class="property">INFO</span>;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (level === <span class="string">&#x27;DEBUG&#x27;</span>) prefixColor = <span class="variable constant_">LEVEL_COLORS</span>.<span class="property">DEBUG</span>;</span><br><span class="line">    <span class="keyword">else</span> prefixColor = <span class="variable constant_">LEVEL_COLORS</span>.<span class="property">HEXO_ERROR</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> bodyColor = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    <span class="keyword">if</span> (level === <span class="string">&#x27;ERROR&#x27;</span> || isFatal || isErrorDesc) bodyColor = <span class="string">&#x27;\x1b[31m&#x27;</span>;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (isCodeLine) bodyColor = <span class="string">&#x27;\x1b[34m&#x27;</span>;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (isStackLine) bodyColor = <span class="string">&#x27;\x1b[33m&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> prefix = <span class="string">` <span class="subst">$&#123;prefixColor&#125;</span>│➤ [hexo] [<span class="subst">$&#123;level&#125;</span>]<span class="subst">$&#123;RESET&#125;</span>`</span>;</span><br><span class="line">    <span class="keyword">const</span> body = bodyColor ? <span class="string">`<span class="subst">$&#123;bodyColor&#125;</span><span class="subst">$&#123;msg&#125;</span><span class="subst">$&#123;RESET&#125;</span>`</span> : msg;</span><br><span class="line">    pendingLogs.<span class="title function_">push</span>(<span class="string">`<span class="subst">$&#123;prefix&#125;</span> <span class="subst">$&#123;body&#125;</span>`</span>);</span><br><span class="line"></span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (category === <span class="string">&#x27;dev&#x27;</span>) &#123;</span><br><span class="line">    process.<span class="property">stdout</span>.<span class="title function_">write</span>(<span class="string">&#x27;\r\x1b[K&#x27;</span>);</span><br><span class="line">    <span class="keyword">let</span> prefix, body, color = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    <span class="keyword">const</span> type = levelOrType;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (type === <span class="string">&#x27;stepStart&#x27;</span>) &#123;</span><br><span class="line">      color = <span class="variable constant_">LEVEL_COLORS</span>.<span class="property">INFO</span>;</span><br><span class="line">      prefix = <span class="string">` <span class="subst">$&#123;color&#125;</span>┌➤ [dev] [INFO]<span class="subst">$&#123;RESET&#125;</span>`</span>;</span><br><span class="line">      body = <span class="string">`<span class="subst">$&#123;extra.emoji || <span class="string">&#x27;🚀&#x27;</span>&#125;</span> <span class="subst">$&#123;message&#125;</span>`</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (type === <span class="string">&#x27;stepMid&#x27;</span>) &#123;</span><br><span class="line">      color = <span class="variable constant_">LEVEL_COLORS</span>.<span class="property">INFO</span>;</span><br><span class="line">      prefix = <span class="string">` <span class="subst">$&#123;color&#125;</span>│➤ [dev] [INFO]<span class="subst">$&#123;RESET&#125;</span>`</span>;</span><br><span class="line">      body = <span class="string">`📘 <span class="subst">$&#123;message&#125;</span>`</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (type === <span class="string">&#x27;stepEndSuccess&#x27;</span>) &#123;</span><br><span class="line">      color = <span class="variable constant_">LEVEL_COLORS</span>.<span class="property">SUCCESS</span>;</span><br><span class="line">      prefix = <span class="string">` <span class="subst">$&#123;color&#125;</span>└➤ [dev] [SUCCESS]<span class="subst">$&#123;RESET&#125;</span>`</span>;</span><br><span class="line">      body = <span class="string">`✨ <span class="subst">$&#123;message&#125;</span>`</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (type === <span class="string">&#x27;stepEndFail&#x27;</span>) &#123;</span><br><span class="line">      color = <span class="variable constant_">LEVEL_COLORS</span>.<span class="property">ERROR</span>;</span><br><span class="line">      prefix = <span class="string">` <span class="subst">$&#123;color&#125;</span>└➤ [dev] [ERROR]<span class="subst">$&#123;RESET&#125;</span>`</span>;</span><br><span class="line">      body = <span class="string">`❌ <span class="subst">$&#123;message&#125;</span>`</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (type === <span class="string">&#x27;info&#x27;</span>) &#123;</span><br><span class="line">      color = <span class="variable constant_">LEVEL_COLORS</span>.<span class="property">INFO</span>;</span><br><span class="line">      prefix = <span class="string">`  ➤ <span class="subst">$&#123;color&#125;</span>[dev] [INFO]<span class="subst">$&#123;RESET&#125;</span>`</span>;</span><br><span class="line">      body = <span class="string">`📘 <span class="subst">$&#123;message&#125;</span>`</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      prefix = <span class="string">`[dev]`</span>;</span><br><span class="line">      body = message;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`<span class="subst">$&#123;prefix&#125;</span> <span class="subst">$&#123;body&#125;</span>`</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">logHexoLine</span>(<span class="params">line</span>) &#123; <span class="title function_">outputLog</span>(<span class="string">&#x27;hexo&#x27;</span>, <span class="literal">null</span>, line); &#125;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">logStepStart</span>(<span class="params">msg, emoji = <span class="string">&#x27;🚀&#x27;</span></span>) &#123; <span class="title function_">outputLog</span>(<span class="string">&#x27;dev&#x27;</span>, <span class="string">&#x27;stepStart&#x27;</span>, msg, &#123; emoji &#125;); &#125;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">logStepMid</span>(<span class="params">msg</span>) &#123; <span class="title function_">outputLog</span>(<span class="string">&#x27;dev&#x27;</span>, <span class="string">&#x27;stepMid&#x27;</span>, msg); &#125;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">logStepEndSuccess</span>(<span class="params">msg</span>) &#123; <span class="title function_">outputLog</span>(<span class="string">&#x27;dev&#x27;</span>, <span class="string">&#x27;stepEndSuccess&#x27;</span>, msg); &#125;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">logStepEndFail</span>(<span class="params">msg</span>) &#123; <span class="title function_">outputLog</span>(<span class="string">&#x27;dev&#x27;</span>, <span class="string">&#x27;stepEndFail&#x27;</span>, msg); &#125;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">devInfo</span>(<span class="params">msg</span>) &#123; <span class="title function_">outputLog</span>(<span class="string">&#x27;dev&#x27;</span>, <span class="string">&#x27;info&#x27;</span>, msg); &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">printShortcuts</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="title function_">devInfo</span>(<span class="string">&#x27;快捷键: Ctrl+C 退出 | Ctrl+R 完整重建 | Ctrl+S 启动/重启服务器&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- 网络与服务器 ----------</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getLocalIP</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> nets = os.<span class="title function_">networkInterfaces</span>();</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">const</span> name <span class="keyword">of</span> <span class="title class_">Object</span>.<span class="title function_">keys</span>(nets)) &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">const</span> net <span class="keyword">of</span> nets[name]) &#123;</span><br><span class="line">      <span class="keyword">if</span> (net.<span class="property">family</span> === <span class="string">&#x27;IPv4&#x27;</span> &amp;&amp; !net.<span class="property">internal</span>) <span class="keyword">return</span> net.<span class="property">address</span>;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="string">&#x27;127.0.0.1&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">startServer</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (server || serverStarting) <span class="keyword">return</span>;</span><br><span class="line">  serverStarting = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">logStepStart</span>(<span class="string">&#x27;启动服务器&#x27;</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> delay = quickMode ? <span class="number">0</span> : <span class="number">1000</span>;</span><br><span class="line">  <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> app = <span class="title function_">express</span>();</span><br><span class="line">    app.<span class="title function_">use</span>(<span class="title function_">serveStatic</span>(<span class="variable constant_">PUBLIC_DIR</span>, &#123; <span class="attr">index</span>: <span class="string">&#x27;index.html&#x27;</span>, <span class="attr">setHeaders</span>: <span class="function"><span class="params">res</span> =&gt;</span> res.<span class="title function_">setHeader</span>(<span class="string">&#x27;Cache-Control&#x27;</span>, <span class="string">&#x27;no-cache&#x27;</span>) &#125;));</span><br><span class="line">    app.<span class="title function_">use</span>(<span class="function">(<span class="params">req, res</span>) =&gt;</span> res.<span class="title function_">sendFile</span>(path.<span class="title function_">join</span>(<span class="variable constant_">PUBLIC_DIR</span>, <span class="string">&#x27;index.html&#x27;</span>)));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> httpServer = app.<span class="title function_">listen</span>(<span class="variable constant_">PORT</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">      serverStarting = <span class="literal">false</span>;</span><br><span class="line">      server = httpServer;</span><br><span class="line">      <span class="keyword">const</span> localIP = <span class="title function_">getLocalIP</span>();</span><br><span class="line">      <span class="title function_">logStepMid</span>(<span class="string">`本地访问: http://localhost:<span class="subst">$&#123;PORT&#125;</span>`</span>);</span><br><span class="line">      <span class="title function_">logStepMid</span>(<span class="string">`局域网访问: http://<span class="subst">$&#123;localIP&#125;</span>:<span class="subst">$&#123;PORT&#125;</span>`</span>);</span><br><span class="line">      <span class="keyword">if</span> (watcher) <span class="title function_">logStepMid</span>(<span class="string">&#x27;文件监控已就绪&#x27;</span>);</span><br><span class="line">      <span class="title function_">logStepEndSuccess</span>(<span class="string">&#x27;服务器已启动&#x27;</span>);</span><br><span class="line">      <span class="title function_">printShortcuts</span>();</span><br><span class="line">      <span class="title function_">scheduleScroll</span>();</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    httpServer.<span class="title function_">on</span>(<span class="string">&#x27;error&#x27;</span>, <span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">      serverStarting = <span class="literal">false</span>;</span><br><span class="line">      <span class="title function_">logStepEndFail</span>(<span class="string">`服务器错误: <span class="subst">$&#123;err.message&#125;</span>`</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;, delay);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- 文件监控 ----------</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">startWatcher</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (watcher) <span class="keyword">return</span>;</span><br><span class="line">  watcher = chokidar.<span class="title function_">watch</span>(<span class="variable constant_">WATCH_PATHS</span>, &#123;</span><br><span class="line">    <span class="attr">ignoreInitial</span>: <span class="literal">true</span>, <span class="attr">persistent</span>: <span class="literal">true</span>, <span class="attr">cwd</span>: <span class="variable constant_">CWD</span>,</span><br><span class="line">    <span class="attr">ignored</span>: [<span class="regexp">/(^|[\/\\])\../</span>, <span class="regexp">/\.tmp$/i</span>, <span class="regexp">/~$/</span>, <span class="regexp">/\.sw[op]$/i</span>, <span class="regexp">/\.bak$/i</span>],</span><br><span class="line">  &#125;);</span><br><span class="line">  watcher.<span class="title function_">on</span>(<span class="string">&#x27;add&#x27;</span>, <span class="function"><span class="params">p</span> =&gt;</span> &#123; <span class="title function_">scheduleBuild</span>(<span class="string">`新增: <span class="subst">$&#123;p&#125;</span>`</span>); <span class="title function_">devInfo</span>(<span class="string">`新增文件: <span class="subst">$&#123;p&#125;</span>`</span>); &#125;);</span><br><span class="line">  watcher.<span class="title function_">on</span>(<span class="string">&#x27;change&#x27;</span>, <span class="function"><span class="params">p</span> =&gt;</span> &#123; <span class="title function_">scheduleBuild</span>(<span class="string">`变更: <span class="subst">$&#123;p&#125;</span>`</span>); <span class="title function_">devInfo</span>(<span class="string">`文件变化: <span class="subst">$&#123;p&#125;</span>`</span>); &#125;);</span><br><span class="line">  watcher.<span class="title function_">on</span>(<span class="string">&#x27;unlink&#x27;</span>, <span class="function"><span class="params">p</span> =&gt;</span> &#123; <span class="title function_">scheduleBuild</span>(<span class="string">`删除: <span class="subst">$&#123;p&#125;</span>`</span>); <span class="title function_">devInfo</span>(<span class="string">`文件删除: <span class="subst">$&#123;p&#125;</span>`</span>); &#125;);</span><br><span class="line">  watcher.<span class="title function_">on</span>(<span class="string">&#x27;ready&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    watcherReady = <span class="literal">true</span>;</span><br><span class="line">    <span class="comment">// 如果服务器还未启动，则启动（直接模式下由外部调用）</span></span><br><span class="line">    <span class="title function_">startServer</span>();</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- 构建逻辑 ----------</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">abortCurrentBuild</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (currentBuildProcess) &#123;</span><br><span class="line">    <span class="title function_">devInfo</span>(<span class="string">&#x27;检测到新的变更，中止当前构建...&#x27;</span>);</span><br><span class="line">    <span class="keyword">try</span> &#123; currentBuildProcess.<span class="title function_">kill</span>(<span class="string">&#x27;SIGTERM&#x27;</span>); &#125; <span class="keyword">catch</span> (e) &#123;&#125;</span><br><span class="line">    currentBuildProcess = <span class="literal">null</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">build</span>(<span class="params">trigger</span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (buildTimer) &#123; <span class="built_in">clearTimeout</span>(buildTimer); buildTimer = <span class="literal">null</span>; &#125;</span><br><span class="line">  <span class="keyword">if</span> (isBuilding) <span class="title function_">abortCurrentBuild</span>();</span><br><span class="line"></span><br><span class="line">  hexoErrorContext = <span class="literal">false</span>;</span><br><span class="line">  isBuilding = <span class="literal">true</span>;</span><br><span class="line">  lastBuildFailed = <span class="literal">false</span>;</span><br><span class="line">  initializing = <span class="literal">false</span>;</span><br><span class="line">  progressMode = <span class="string">&#x27;bounce&#x27;</span>;</span><br><span class="line">  sliderWidth = <span class="variable constant_">BOUNCE_WIDTH</span>;</span><br><span class="line">  sliderPos = <span class="number">0</span>;</span><br><span class="line">  dir = <span class="number">1</span>;</span><br><span class="line">  buildStartTime = <span class="title class_">Date</span>.<span class="title function_">now</span>();</span><br><span class="line"></span><br><span class="line">  <span class="title function_">logStepStart</span>(<span class="string">`构建开始 (<span class="subst">$&#123;trigger&#125;</span>)`</span>);</span><br><span class="line">  <span class="title function_">forceRender</span>();</span><br><span class="line">  currentBuildProcess = <span class="title function_">runHexo</span>([<span class="string">&#x27;g&#x27;</span>, <span class="string">&#x27;--incremental&#x27;</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fullRebuild</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (isBuilding) <span class="title function_">abortCurrentBuild</span>();</span><br><span class="line"></span><br><span class="line">  hexoErrorContext = <span class="literal">false</span>;</span><br><span class="line">  isBuilding = <span class="literal">true</span>;</span><br><span class="line">  lastBuildFailed = <span class="literal">false</span>;</span><br><span class="line">  initializing = <span class="literal">false</span>;</span><br><span class="line">  progressMode = <span class="string">&#x27;bounce&#x27;</span>;</span><br><span class="line">  sliderWidth = <span class="variable constant_">BOUNCE_WIDTH</span>;</span><br><span class="line">  sliderPos = <span class="number">0</span>;</span><br><span class="line">  dir = <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">const</span> totalStart = <span class="title class_">Date</span>.<span class="title function_">now</span>();</span><br><span class="line">  buildStartTime = totalStart;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">logStepStart</span>(<span class="string">&#x27;完整重建 - 清理 (hexo clean)&#x27;</span>);</span><br><span class="line">  <span class="title function_">forceRender</span>();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> clean = <span class="title function_">spawn</span>(<span class="string">&#x27;hexo&#x27;</span>, [<span class="string">&#x27;clean&#x27;</span>], &#123; <span class="attr">cwd</span>: <span class="variable constant_">CWD</span>, <span class="attr">stdio</span>: [<span class="string">&#x27;ignore&#x27;</span>,<span class="string">&#x27;pipe&#x27;</span>,<span class="string">&#x27;pipe&#x27;</span>] &#125;);</span><br><span class="line">  currentBuildProcess = clean;</span><br><span class="line">  <span class="keyword">let</span> cleanOut = <span class="string">&#x27;&#x27;</span>, cleanErr = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">  clean.<span class="property">stdout</span>.<span class="title function_">on</span>(<span class="string">&#x27;data&#x27;</span>, <span class="function"><span class="params">d</span> =&gt;</span> &#123;</span><br><span class="line">    cleanOut += d.<span class="title function_">toString</span>();</span><br><span class="line">    <span class="keyword">const</span> lines = cleanOut.<span class="title function_">split</span>(<span class="string">&#x27;\n&#x27;</span>);</span><br><span class="line">    cleanOut = lines.<span class="title function_">pop</span>();</span><br><span class="line">    lines.<span class="title function_">forEach</span>(logHexoLine);</span><br><span class="line">  &#125;);</span><br><span class="line">  clean.<span class="property">stderr</span>.<span class="title function_">on</span>(<span class="string">&#x27;data&#x27;</span>, <span class="function"><span class="params">d</span> =&gt;</span> &#123;</span><br><span class="line">    cleanErr += d.<span class="title function_">toString</span>();</span><br><span class="line">    <span class="keyword">const</span> lines = cleanErr.<span class="title function_">split</span>(<span class="string">&#x27;\n&#x27;</span>);</span><br><span class="line">    cleanErr = lines.<span class="title function_">pop</span>();</span><br><span class="line">    lines.<span class="title function_">forEach</span>(logHexoLine);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  clean.<span class="title function_">on</span>(<span class="string">&#x27;close&#x27;</span>, <span class="function"><span class="params">code</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (cleanOut) <span class="title function_">logHexoLine</span>(cleanOut);</span><br><span class="line">    <span class="keyword">if</span> (cleanErr) <span class="title function_">logHexoLine</span>(cleanErr);</span><br><span class="line">    <span class="keyword">if</span> (currentBuildProcess !== clean) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> cleanTime = (<span class="title class_">Date</span>.<span class="title function_">now</span>() - totalStart) / <span class="number">1000</span>;</span><br><span class="line">    <span class="keyword">if</span> (code !== <span class="number">0</span>) &#123;</span><br><span class="line">      isBuilding = <span class="literal">false</span>;</span><br><span class="line">      lastBuildFailed = <span class="literal">true</span>;</span><br><span class="line">      <span class="title function_">logStepEndFail</span>(<span class="string">`清理失败 (<span class="subst">$&#123;formatTime(cleanTime)&#125;</span>)，重建中止`</span>);</span><br><span class="line">      currentBuildProcess = <span class="literal">null</span>;</span><br><span class="line">      <span class="title function_">startFillThenScroll</span>();</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="title function_">logStepEndSuccess</span>(<span class="string">`清理完成 (<span class="subst">$&#123;formatTime(cleanTime)&#125;</span>)`</span>);</span><br><span class="line"></span><br><span class="line">    <span class="title function_">logStepStart</span>(<span class="string">&#x27;完整重建 - 生成 (hexo g)&#x27;</span>);</span><br><span class="line">    <span class="keyword">const</span> genStart = <span class="title class_">Date</span>.<span class="title function_">now</span>();</span><br><span class="line">    currentBuildProcess = <span class="title function_">runHexo</span>([<span class="string">&#x27;g&#x27;</span>], &#123;</span><br><span class="line">      <span class="attr">suppressEndLog</span>: <span class="literal">true</span>,</span><br><span class="line">      <span class="attr">onClose</span>: <span class="function">(<span class="params">genCode</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> genTime = (<span class="title class_">Date</span>.<span class="title function_">now</span>() - genStart) / <span class="number">1000</span>;</span><br><span class="line">        <span class="keyword">const</span> totalTime = (<span class="title class_">Date</span>.<span class="title function_">now</span>() - totalStart) / <span class="number">1000</span>;</span><br><span class="line">        <span class="keyword">if</span> (genCode === <span class="number">0</span>) &#123;</span><br><span class="line">          <span class="title function_">logStepEndSuccess</span>(<span class="string">`生成完成 (<span class="subst">$&#123;formatTime(genTime)&#125;</span>)`</span>);</span><br><span class="line">          <span class="title function_">logStepEndSuccess</span>(<span class="string">`完整重建完成，总耗时 <span class="subst">$&#123;formatTime(totalTime)&#125;</span>`</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          <span class="title function_">logStepEndFail</span>(<span class="string">`生成失败 (<span class="subst">$&#123;formatTime(genTime)&#125;</span>)，退出码 <span class="subst">$&#123;genCode&#125;</span>`</span>);</span><br><span class="line">          <span class="title function_">logStepEndFail</span>(<span class="string">`完整重建失败，总耗时 <span class="subst">$&#123;formatTime(totalTime)&#125;</span>`</span>);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  clean.<span class="title function_">on</span>(<span class="string">&#x27;error&#x27;</span>, <span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (currentBuildProcess !== clean) <span class="keyword">return</span>;</span><br><span class="line">    <span class="keyword">const</span> cleanTime = (<span class="title class_">Date</span>.<span class="title function_">now</span>() - totalStart) / <span class="number">1000</span>;</span><br><span class="line">    isBuilding = <span class="literal">false</span>;</span><br><span class="line">    lastBuildFailed = <span class="literal">true</span>;</span><br><span class="line">    <span class="title function_">logStepEndFail</span>(<span class="string">`清理错误: <span class="subst">$&#123;err.message&#125;</span> (<span class="subst">$&#123;formatTime(cleanTime)&#125;</span>)`</span>);</span><br><span class="line">    currentBuildProcess = <span class="literal">null</span>;</span><br><span class="line">    <span class="title function_">startFillThenScroll</span>();</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">runHexo</span>(<span class="params">args, opts = &#123;&#125;</span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; suppressEndLog = <span class="literal">false</span>, onClose &#125; = opts;</span><br><span class="line">  <span class="keyword">const</span> child = <span class="title function_">spawn</span>(<span class="string">&#x27;hexo&#x27;</span>, args, &#123; <span class="attr">cwd</span>: <span class="variable constant_">CWD</span>, <span class="attr">stdio</span>: [<span class="string">&#x27;ignore&#x27;</span>,<span class="string">&#x27;pipe&#x27;</span>,<span class="string">&#x27;pipe&#x27;</span>] &#125;);</span><br><span class="line">  <span class="keyword">let</span> outBuf = <span class="string">&#x27;&#x27;</span>, errBuf = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line">  child.<span class="property">stdout</span>.<span class="title function_">on</span>(<span class="string">&#x27;data&#x27;</span>, <span class="function"><span class="params">d</span> =&gt;</span> &#123;</span><br><span class="line">    outBuf += d.<span class="title function_">toString</span>();</span><br><span class="line">    <span class="keyword">const</span> lines = outBuf.<span class="title function_">split</span>(<span class="string">&#x27;\n&#x27;</span>);</span><br><span class="line">    outBuf = lines.<span class="title function_">pop</span>();</span><br><span class="line">    lines.<span class="title function_">forEach</span>(logHexoLine);</span><br><span class="line">  &#125;);</span><br><span class="line">  child.<span class="property">stderr</span>.<span class="title function_">on</span>(<span class="string">&#x27;data&#x27;</span>, <span class="function"><span class="params">d</span> =&gt;</span> &#123;</span><br><span class="line">    errBuf += d.<span class="title function_">toString</span>();</span><br><span class="line">    <span class="keyword">const</span> lines = errBuf.<span class="title function_">split</span>(<span class="string">&#x27;\n&#x27;</span>);</span><br><span class="line">    errBuf = lines.<span class="title function_">pop</span>();</span><br><span class="line">    lines.<span class="title function_">forEach</span>(logHexoLine);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  child.<span class="title function_">on</span>(<span class="string">&#x27;close&#x27;</span>, <span class="function"><span class="params">code</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (currentBuildProcess !== child) <span class="keyword">return</span>;</span><br><span class="line">    <span class="keyword">if</span> (outBuf) <span class="title function_">logHexoLine</span>(outBuf);</span><br><span class="line">    <span class="keyword">if</span> (errBuf) <span class="title function_">logHexoLine</span>(errBuf);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> elapsed = (<span class="title class_">Date</span>.<span class="title function_">now</span>() - buildStartTime) / <span class="number">1000</span>;</span><br><span class="line">    isBuilding = <span class="literal">false</span>;</span><br><span class="line">    lastBuildFailed = (code !== <span class="number">0</span>);</span><br><span class="line">    currentBuildProcess = <span class="literal">null</span>;</span><br><span class="line">    hexoErrorContext = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!suppressEndLog) &#123;</span><br><span class="line">      <span class="keyword">if</span> (code === <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="title function_">logStepEndSuccess</span>(<span class="string">`构建完成 (<span class="subst">$&#123;formatTime(elapsed)&#125;</span>)`</span>);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="title function_">logStepEndFail</span>(<span class="string">`构建失败 (<span class="subst">$&#123;formatTime(elapsed)&#125;</span>)，退出码 <span class="subst">$&#123;code&#125;</span>`</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (firstBuild &amp;&amp; code === <span class="number">0</span>) &#123;</span><br><span class="line">      firstBuild = <span class="literal">false</span>;</span><br><span class="line">      <span class="title function_">startWatcher</span>();</span><br><span class="line">      <span class="title function_">startServer</span>();</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (firstBuild &amp;&amp; code !== <span class="number">0</span>) &#123;</span><br><span class="line">      firstBuild = <span class="literal">false</span>;</span><br><span class="line">      <span class="title function_">logStepEndFail</span>(<span class="string">&#x27;首次构建失败，退出&#x27;</span>);</span><br><span class="line">      process.<span class="title function_">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (onClose) <span class="title function_">onClose</span>(code);</span><br><span class="line">    <span class="title function_">startFillThenScroll</span>();</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  child.<span class="title function_">on</span>(<span class="string">&#x27;error&#x27;</span>, <span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (currentBuildProcess !== child) <span class="keyword">return</span>;</span><br><span class="line">    <span class="keyword">const</span> elapsed = (<span class="title class_">Date</span>.<span class="title function_">now</span>() - buildStartTime) / <span class="number">1000</span>;</span><br><span class="line">    isBuilding = <span class="literal">false</span>;</span><br><span class="line">    lastBuildFailed = <span class="literal">true</span>;</span><br><span class="line">    currentBuildProcess = <span class="literal">null</span>;</span><br><span class="line">    hexoErrorContext = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!suppressEndLog) &#123;</span><br><span class="line">      <span class="title function_">logStepEndFail</span>(<span class="string">`构建进程错误: <span class="subst">$&#123;err.message&#125;</span> (<span class="subst">$&#123;formatTime(elapsed)&#125;</span>)`</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (firstBuild) &#123;</span><br><span class="line">      firstBuild = <span class="literal">false</span>;</span><br><span class="line">      process.<span class="title function_">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (onClose) <span class="title function_">onClose</span>(-<span class="number">1</span>);</span><br><span class="line">    <span class="title function_">startFillThenScroll</span>();</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> child;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- 进度动画控制 ----------</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">startFillThenScroll</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (progressMode === <span class="string">&#x27;fill&#x27;</span>) <span class="keyword">return</span>;</span><br><span class="line">  sliderWidth = <span class="variable constant_">BAR_LENGTH</span>;</span><br><span class="line">  sliderPos = <span class="number">0</span>;</span><br><span class="line">  progressMode = <span class="string">&#x27;fill&#x27;</span>;</span><br><span class="line">  <span class="keyword">if</span> (<span class="variable constant_">FILL_STAY</span> &gt; <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (server &amp;&amp; !isBuilding) &#123;</span><br><span class="line">        sliderWidth = <span class="variable constant_">BOUNCE_WIDTH</span>;</span><br><span class="line">        sliderPos = -sliderWidth;</span><br><span class="line">        progressMode = <span class="string">&#x27;scroll&#x27;</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;, <span class="variable constant_">FILL_STAY</span>);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (server &amp;&amp; !isBuilding) &#123;</span><br><span class="line">      sliderWidth = <span class="variable constant_">BOUNCE_WIDTH</span>;</span><br><span class="line">      sliderPos = -sliderWidth;</span><br><span class="line">      progressMode = <span class="string">&#x27;scroll&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">scheduleScroll</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (!isBuilding &amp;&amp; server &amp;&amp; !serverStarting &amp;&amp; progressMode !== <span class="string">&#x27;scroll&#x27;</span>) &#123;</span><br><span class="line">    sliderWidth = <span class="variable constant_">BOUNCE_WIDTH</span>;</span><br><span class="line">    sliderPos = -sliderWidth;</span><br><span class="line">    progressMode = <span class="string">&#x27;scroll&#x27;</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">scheduleBuild</span>(<span class="params">trigger</span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (buildTimer) <span class="built_in">clearTimeout</span>(buildTimer);</span><br><span class="line">  buildTimer = <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123; buildTimer = <span class="literal">null</span>; <span class="title function_">build</span>(trigger); &#125;, <span class="variable constant_">DEBOUNCE_DELAY</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">forceRender</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (renderTimeout) &#123; <span class="built_in">clearTimeout</span>(renderTimeout); renderTimeout = <span class="literal">null</span>; &#125;</span><br><span class="line">  <span class="title function_">renderLine</span>();</span><br><span class="line">  <span class="title function_">scheduleRender</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">scheduleRender</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (renderTimeout) <span class="built_in">clearTimeout</span>(renderTimeout);</span><br><span class="line">  <span class="keyword">const</span> active = initializing || isBuilding || serverStarting || progressMode === <span class="string">&#x27;fill&#x27;</span>;</span><br><span class="line">  <span class="keyword">const</span> interval = active ? <span class="variable constant_">FRAME_ACTIVE</span> : <span class="variable constant_">FRAME_IDLE</span>;</span><br><span class="line">  renderTimeout = <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">    spinnerIdx++;</span><br><span class="line">    <span class="title function_">renderLine</span>();</span><br><span class="line">    <span class="title function_">scheduleRender</span>();</span><br><span class="line">  &#125;, interval);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- 状态栏渲染 ----------</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">renderLine</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="title function_">flushLogs</span>();</span><br><span class="line">  <span class="title function_">updateProgress</span>();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">let</span> bgStyle;</span><br><span class="line">  <span class="keyword">if</span> (initializing) &#123;</span><br><span class="line">    bgStyle = <span class="variable constant_">BG_INIT</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isBuilding) &#123;</span><br><span class="line">    bgStyle = <span class="variable constant_">BG_BUILD</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (lastBuildFailed) &#123;</span><br><span class="line">    bgStyle = <span class="variable constant_">BG_FAIL</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (serverStarting || server) &#123;</span><br><span class="line">    bgStyle = <span class="variable constant_">BG_RUN</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    bgStyle = <span class="variable constant_">RESET</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> spinner = isBuilding ? <span class="variable constant_">SPINNER_FRAMES</span>[spinnerIdx % <span class="variable constant_">SPINNER_FRAMES</span>.<span class="property">length</span>] : <span class="string">&#x27; &#x27;</span>;</span><br><span class="line">  <span class="keyword">const</span> mark = (server &amp;&amp; !serverStarting) ? <span class="string">&#x27;&#x27;</span> : <span class="string">&#x27;[x]&#x27;</span>;</span><br><span class="line">  <span class="keyword">let</span> statusText;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (initializing) &#123;</span><br><span class="line">    statusText = <span class="string">`初始化中 <span class="subst">$&#123;mark&#125;</span>端口:<span class="subst">$&#123;PORT&#125;</span>`</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (serverStarting) &#123;</span><br><span class="line">    statusText = <span class="string">`启动服务器中 <span class="subst">$&#123;mark&#125;</span>端口:<span class="subst">$&#123;PORT&#125;</span>`</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isBuilding) &#123;</span><br><span class="line">    <span class="keyword">const</span> elapsed = buildStartTime ? (<span class="title class_">Date</span>.<span class="title function_">now</span>() - buildStartTime) / <span class="number">1000</span> : <span class="number">0</span>;</span><br><span class="line">    statusText = <span class="string">`构建中 <span class="subst">$&#123;formatTime(elapsed)&#125;</span> <span class="subst">$&#123;mark&#125;</span>端口:<span class="subst">$&#123;PORT&#125;</span>`</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (lastBuildFailed) &#123;</span><br><span class="line">    statusText = <span class="string">`构建失败 <span class="subst">$&#123;mark&#125;</span>端口:<span class="subst">$&#123;PORT&#125;</span>`</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (server) &#123;</span><br><span class="line">    statusText = <span class="string">`运行中 端口:<span class="subst">$&#123;PORT&#125;</span>`</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    statusText = <span class="string">`就绪 <span class="subst">$&#123;mark&#125;</span>端口:<span class="subst">$&#123;PORT&#125;</span>`</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  process.<span class="property">stdout</span>.<span class="title function_">write</span>(<span class="string">`\r\x1b[K<span class="subst">$&#123;bgStyle&#125;</span> <span class="subst">$&#123;spinner&#125;</span> [<span class="subst">$&#123;getProgressBar()&#125;</span>] <span class="subst">$&#123;statusText&#125;</span> <span class="subst">$&#123;RESET&#125;</span>`</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">updateProgress</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (progressMode === <span class="string">&#x27;bounce&#x27;</span>) &#123;</span><br><span class="line">    sliderPos += dir;</span><br><span class="line">    <span class="keyword">if</span> (sliderPos + sliderWidth &gt;= <span class="variable constant_">BAR_LENGTH</span>) &#123; sliderPos = <span class="variable constant_">BAR_LENGTH</span> - sliderWidth; dir = -<span class="number">1</span>; &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (sliderPos &lt;= <span class="number">0</span>) &#123; sliderPos = <span class="number">0</span>; dir = <span class="number">1</span>; &#125;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (progressMode === <span class="string">&#x27;scroll&#x27;</span>) &#123;</span><br><span class="line">    sliderPos += <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span> (sliderPos &gt; <span class="variable constant_">BAR_LENGTH</span>) sliderPos = -sliderWidth;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- 欢迎语 ----------</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">printWelcome</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="title function_">flushLogs</span>();</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`\x1b[36m✨ 欢迎使用LJXH的Hexo构建脚本 (∠・ω&lt; )⌒☆！\x1b[0m`</span>);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`\x1b[90m⚡ Node.js:\x1b[0m <span class="subst">$&#123;process.version&#125;</span>`</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">let</span> pm = <span class="string">&#x27;npm&#x27;</span>, pmIcon = <span class="string">&#x27;📦&#x27;</span>;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (fs.<span class="title function_">existsSync</span>(path.<span class="title function_">join</span>(<span class="variable constant_">CWD</span>, <span class="string">&#x27;yarn.lock&#x27;</span>))) &#123; pm = <span class="string">&#x27;yarn&#x27;</span>; pmIcon = <span class="string">&#x27;🧶&#x27;</span>; &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (fs.<span class="title function_">existsSync</span>(path.<span class="title function_">join</span>(<span class="variable constant_">CWD</span>, <span class="string">&#x27;pnpm-lock.yaml&#x27;</span>))) &#123; pm = <span class="string">&#x27;pnpm&#x27;</span>; pmIcon = <span class="string">&#x27;📦&#x27;</span>; &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> pkg = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(fs.<span class="title function_">readFileSync</span>(path.<span class="title function_">join</span>(<span class="variable constant_">CWD</span>, <span class="string">&#x27;package.json&#x27;</span>), <span class="string">&#x27;utf8&#x27;</span>));</span><br><span class="line">      <span class="keyword">if</span> (pkg.<span class="property">packageManager</span> &amp;&amp; pkg.<span class="property">packageManager</span>.<span class="title function_">includes</span>(<span class="string">&#x27;yarn&#x27;</span>)) &#123; pm = <span class="string">&#x27;yarn&#x27;</span>; pmIcon = <span class="string">&#x27;🧶&#x27;</span>; &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> pmVersion = <span class="title function_">execSync</span>(<span class="string">`<span class="subst">$&#123;pm&#125;</span> --version`</span>, &#123; <span class="attr">encoding</span>: <span class="string">&#x27;utf8&#x27;</span> &#125;).<span class="title function_">trim</span>();</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`<span class="subst">$&#123;pmIcon&#125;</span> <span class="subst">$&#123;pm&#125;</span>: <span class="subst">$&#123;pmVersion&#125;</span>`</span>);</span><br><span class="line">  &#125; <span class="keyword">catch</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> hexoVersion = <span class="built_in">require</span>(path.<span class="title function_">join</span>(<span class="variable constant_">CWD</span>, <span class="string">&#x27;node_modules/hexo/package.json&#x27;</span>)).<span class="property">version</span>;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`🌐 Hexo: <span class="subst">$&#123;hexoVersion&#125;</span>`</span>);</span><br><span class="line">  &#125; <span class="keyword">catch</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> config = fs.<span class="title function_">readFileSync</span>(path.<span class="title function_">join</span>(<span class="variable constant_">CWD</span>, <span class="string">&#x27;_config.yml&#x27;</span>), <span class="string">&#x27;utf8&#x27;</span>);</span><br><span class="line">    <span class="keyword">const</span> theme = config.<span class="title function_">match</span>(<span class="regexp">/^theme:\s*(\S+)/m</span>)?.[<span class="number">1</span>];</span><br><span class="line">    <span class="keyword">if</span> (theme) <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`🎨 主题: <span class="subst">$&#123;theme&#125;</span>`</span>);</span><br><span class="line">  &#125; <span class="keyword">catch</span> &#123;&#125;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;&#x27;</span>);</span><br><span class="line">  <span class="title function_">printShortcuts</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- Ctrl+S 重启服务器 ----------</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">restartServer</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (server) &#123;</span><br><span class="line">    <span class="title function_">devInfo</span>(<span class="string">&#x27;正在重启服务器...&#x27;</span>);</span><br><span class="line">    server.<span class="title function_">close</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">      server = <span class="literal">null</span>;</span><br><span class="line">      <span class="title function_">startServer</span>();</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="title function_">devInfo</span>(<span class="string">&#x27;正在启动服务器...&#x27;</span>);</span><br><span class="line">    <span class="title function_">startServer</span>();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- 退出 ----------</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">gracefulExit</span>(<span class="params"></span>) &#123;</span><br><span class="line">  process.<span class="property">stdout</span>.<span class="title function_">write</span>(<span class="string">`\r\x1b[K<span class="subst">$&#123;RESET&#125;</span>\n`</span>);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;👋 感谢使用，下次见 (´• ω •`)ﾉ ♪&#x27;</span>);</span><br><span class="line">  <span class="keyword">if</span> (renderTimeout) <span class="built_in">clearTimeout</span>(renderTimeout);</span><br><span class="line">  <span class="keyword">if</span> (server) server.<span class="title function_">close</span>();</span><br><span class="line">  <span class="keyword">if</span> (watcher) watcher.<span class="title function_">close</span>();</span><br><span class="line">  <span class="keyword">if</span> (process.<span class="property">stdin</span>.<span class="property">isTTY</span>) &#123; process.<span class="property">stdin</span>.<span class="title function_">setRawMode</span>(<span class="literal">false</span>); process.<span class="property">stdin</span>.<span class="title function_">pause</span>(); &#125;</span><br><span class="line">  process.<span class="title function_">exit</span>(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- 初始化 ----------</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">init</span>(<span class="params"></span>) &#123;</span><br><span class="line">  initializing = <span class="literal">true</span>;</span><br><span class="line">  hexoErrorContext = <span class="literal">false</span>;</span><br><span class="line">  <span class="title function_">scheduleRender</span>();</span><br><span class="line"></span><br><span class="line">  <span class="title function_">printWelcome</span>();</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 确保 public 目录存在（如果是直接启动而目录不存在，给出警告但不阻止启动）</span></span><br><span class="line">  <span class="keyword">if</span> (!fs.<span class="title function_">existsSync</span>(<span class="variable constant_">PUBLIC_DIR</span>)) &#123;</span><br><span class="line">    <span class="keyword">if</span> (directServe) &#123;</span><br><span class="line">      <span class="title function_">devInfo</span>(<span class="string">&#x27;public 目录不存在，服务器可能返回 404，建议先执行 hexo generate&#x27;</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      fs.<span class="title function_">mkdirSync</span>(<span class="variable constant_">PUBLIC_DIR</span>, &#123; <span class="attr">recursive</span>: <span class="literal">true</span> &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> delay = quickMode ? <span class="number">0</span> : <span class="number">1000</span>;</span><br><span class="line">  <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (directServe) &#123;</span><br><span class="line">      <span class="comment">// 直接启动模式：跳过首次构建，直接启动服务器和文件监控</span></span><br><span class="line">      initializing = <span class="literal">false</span>;</span><br><span class="line">      <span class="title function_">logStepStart</span>(<span class="string">&#x27;直接启动模式 (跳过构建)&#x27;</span>, <span class="string">&#x27;⚡&#x27;</span>);</span><br><span class="line">      <span class="title function_">startWatcher</span>();   <span class="comment">// 启动监控（会内部调用 startServer）</span></span><br><span class="line">      <span class="title function_">startServer</span>();    <span class="comment">// 确保服务器启动</span></span><br><span class="line">      <span class="comment">// 强制渲染一帧，让状态栏更新</span></span><br><span class="line">      <span class="title function_">forceRender</span>();</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">// 正常首次构建</span></span><br><span class="line">      <span class="title function_">logStepStart</span>(<span class="string">&#x27;首次构建&#x27;</span>);</span><br><span class="line">      firstBuild = <span class="literal">true</span>;</span><br><span class="line">      isBuilding = <span class="literal">true</span>;</span><br><span class="line">      lastBuildFailed = <span class="literal">false</span>;</span><br><span class="line">      initializing = <span class="literal">false</span>;</span><br><span class="line">      progressMode = <span class="string">&#x27;bounce&#x27;</span>;</span><br><span class="line">      sliderWidth = <span class="variable constant_">BOUNCE_WIDTH</span>;</span><br><span class="line">      sliderPos = <span class="number">0</span>;</span><br><span class="line">      dir = <span class="number">1</span>;</span><br><span class="line">      buildStartTime = <span class="title class_">Date</span>.<span class="title function_">now</span>();</span><br><span class="line">      <span class="title function_">forceRender</span>();</span><br><span class="line">      currentBuildProcess = <span class="title function_">runHexo</span>([<span class="string">&#x27;g&#x27;</span>, <span class="string">&#x27;--incremental&#x27;</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;, delay);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (process.<span class="property">stdin</span>.<span class="property">isTTY</span>) &#123;</span><br><span class="line">    process.<span class="property">stdin</span>.<span class="title function_">setRawMode</span>(<span class="literal">true</span>);</span><br><span class="line">    process.<span class="property">stdin</span>.<span class="title function_">resume</span>();</span><br><span class="line">    process.<span class="property">stdin</span>.<span class="title function_">on</span>(<span class="string">&#x27;data&#x27;</span>, <span class="function"><span class="params">chunk</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (chunk.<span class="property">length</span> === <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (chunk[<span class="number">0</span>] === <span class="number">3</span>) <span class="title function_">gracefulExit</span>();          <span class="comment">// Ctrl+C</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (chunk[<span class="number">0</span>] === <span class="number">18</span>) <span class="title function_">fullRebuild</span>();     <span class="comment">// Ctrl+R</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (chunk[<span class="number">0</span>] === <span class="number">19</span>) <span class="title function_">restartServer</span>();   <span class="comment">// Ctrl+S</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line">  process.<span class="title function_">on</span>(<span class="string">&#x27;SIGINT&#x27;</span>, gracefulExit);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_">init</span>();</span><br></pre></td></tr></table></figure><p>在博客根目录保存为 <code>dev.js</code>（或者其他的名字也可以）</p><p>先安装依赖：</p><div class="tabs" id="安装插件"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#安装插件-0">npm</button></li><li class="tab"><button type="button" data-href="#安装插件-1">pnpm</button></li><li class="tab"><button type="button" data-href="#安装插件-2">yarn</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="安装插件-0"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install chokidar express serve-static --save</span><br></pre></td></tr></table></figure></div><div class="tab-item-content" id="安装插件-1"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pnpm add chokidar express serve-static</span><br></pre></td></tr></table></figure></div><div class="tab-item-content" id="安装插件-2"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add chokidar express serve-static</span><br></pre></td></tr></table></figure></div></div></div><p>注意 Hexo 要全局安装。</p><p>然后直接运行命令：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node dev.js [端口] [选项]</span><br></pre></td></tr></table></figure><p>选项有这几个：</p><table><thead><tr><th>选项</th><th>简写</th><th>含义</th></tr></thead><tbody><tr><td><code>--silent</code></td><td><code>-s</code></td><td>静默模式，只输出脚本日志和 Hexo 的错误日志</td></tr><tr><td><code>--quick</code></td><td><code>-q</code></td><td>快速模式，跳过手动添加的延迟</td></tr><tr><td><code>--direct</code></td><td><code>-d</code></td><td>直接启动服务器，跳过首次构建（请确保静态文件已生成）</td></tr><tr><td><code>--help</code></td><td><code>-h</code></td><td>输出帮助</td></tr></tbody></table><p>可以使用以下快捷键：</p><ul><li><span class="keyboard-tag">Ctrl</span> + <span class="keyboard-tag">C</span> ：退出脚本</li><li><span class="keyboard-tag">Ctrl</span> + <span class="keyboard-tag">R</span>：完整重构（hexo cl + hexo g）</li><li><span class="keyboard-tag">Ctrl</span> + <span class="keyboard-tag">S</span>：重启本地服务器</li></ul><p>脚本使用 chokidar 库，支持监测文件变动自动重新构建。以后改文章不用重新输入命令了！</p><p>另外，脚本自动构建使用的是 <code>hexo g --incremental</code>，对于文章变动比较高效。如果你改动了配置文件或主题文件，建议执行一次完整重建。</p><p>太方便了！当然，你也可以自己 Vibe Coding 一个，实现自己想要的功能！</p><h2 id="说在最后"><a href="#说在最后" class="headerlink" title="说在最后"></a>说在最后</h2><p>本地预览的问题可算是解决了，这下可以安心写文章了。</p><p>以后打算再写一下关于 Hexo Solitude 的懒加载问题。随缘更新。</p><p>✨</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>Hexo 图片引用相关问题</title>
      <link>https://msqy-h.github.io/2026/07/06/hexo-image/</link>
      <description>Hexo 文章图片引用问题的描述与多个解决方案</description>
      <author>陌上轻烟</author>
      <category domain="https://msqy-h.github.io/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      <category domain="https://msqy-h.github.io/tags/Hexo/">Hexo</category>
      <category domain="https://msqy-h.github.io/tags/Solitude/">Solitude</category>
      <enclosure url="https://msqy-h.github.io/2026/07/06/hexo-image/cover.webp" type="image/webp"/>
      <pubDate>Mon, 06 Jul 2026 12:38:46 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p><img src="https://msqy-h.github.io/2026/07/06/hexo-image/cover.webp" alt="Hexo 图片引用相关问题" /></p>
<h2 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h2><p>这几天在搞 Hexo 的 Solitude 主题，最后写文章时遇见了个问题：就是<strong>图片的引用</strong>。</p><p>Hexo很神奇，如果有主题，通过 <code>/img/1.webp</code> 来引用图片，引用的不是根目录下的 source 文件夹中的 img 文件夹，而是主题目录下的 source 文件夹中的 img 文件夹。更直观的说：</p><ul><li>❌ <code>博客根目录/source/img/1.webp</code></li><li>✔️ <code>博客根目录/themes/solitude/source/img/1.webp</code></li></ul><p>直接把图片放在主题的 source 文件夹也不是不行，就是感觉有点怪怪的，更新主题时也有被覆盖的风险。还是放在根目录的 source 文件夹好一点。</p><p>在必应查了下，发现 Hexo 配置文件中有这么一项：</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">post_asset_folder:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure><p>调成 <code>true</code> ，Hexo CLI 会在 <code>hexo new post 文章标题</code> 时在根目录的 <code>resource/_posts</code> 下生成一个与文章标题名称一样的文件夹，用作每个文章的资源文件夹。</p><p>这下好多了，至少管理文章资源时舒服很多。</p><p>但是，</p><blockquote><p>当你打开文章资源文件夹功能后，你把一个 example.jpg 图片放在了你的资源文件夹中，如果通过使用相对路径的常规 markdown 语法 <code>![](example.jpg)</code> ，它将<strong>不会</strong>出现在首页上。<br>——<a href="https://hexo.io/zh-cn/docs/asset-folders">Hexo官方文档</a></p></blockquote><p>这就麻烦了。不过，我找到了几个解决方法。</p><h2 id="文章详情页引用"><a href="#文章详情页引用" class="headerlink" title="文章详情页引用"></a>文章详情页引用</h2><h3 id="1-使用图床"><a href="#1-使用图床" class="headerlink" title="1.使用图床"></a>1.使用图床</h3><p>这可能是大多数人选择的方式。把图片上传到图床，直接用 <code>[](图片链接)</code> 引用。很方便，但是图床可能会跑路，比较不稳定，还是放在自己服务器上好。<span class="spoiler blur-text">反正cloudflare带宽无限</span></p><h3 id="2-相对路径引用的标签插件"><a href="#2-相对路径引用的标签插件" class="headerlink" title="2. 相对路径引用的标签插件"></a>2. 相对路径引用的标签插件</h3><p>Hexo 3 的更新带来了许多标签插件，可以用</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&#123;% asset<span class="emphasis">_img example.jpg This is an example image %&#125;</span></span><br><span class="line"><span class="emphasis">&#123;% asset_</span>img &quot;spaced asset.jpg&quot; &quot;spaced title&quot; %&#125;</span><br></pre></td></tr></table></figure><p>来引用资源文件夹的图片。</p><p>确实是种方法，但只能在 Hexo 中使用。</p><h3 id="3-使用-Markdown-嵌入图片"><a href="#3-使用-Markdown-嵌入图片" class="headerlink" title="3.使用 Markdown 嵌入图片"></a>3.使用 Markdown 嵌入图片</h3><p>我选择这种方式。</p><p>先安装 <code>hexo-renderer-marked</code> 插件：</p><div class="tabs" id="安装插件"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#安装插件-0">npm</button></li><li class="tab"><button type="button" data-href="#安装插件-1">pnpm</button></li><li class="tab"><button type="button" data-href="#安装插件-2">yarn</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="安装插件-0"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-renderer-marked --save</span><br></pre></td></tr></table></figure></div><div class="tab-item-content" id="安装插件-1"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pnpm add hexo-renderer-marked</span><br></pre></td></tr></table></figure></div><div class="tab-item-content" id="安装插件-2"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add hexo-renderer-marked</span><br></pre></td></tr></table></figure></div></div></div><p>在 <code>_config.yml</code> 中写入：</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">post_asset_folder:</span> <span class="literal">true</span></span><br><span class="line"><span class="attr">marked:</span></span><br><span class="line">  <span class="attr">prependRoot:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">postAsset:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><p>这样你就可以直接通过 <code>![](example.jpg)</code> 直接引用文章资源文件夹的图片了（<code>博客根目录/source/_post/文章文件名/example.jpg</code>）。</p><p>假设 Hexo 三连后 <code>image.jpg</code> 的位置为 <code>/2026/07/05/foo/image.jpg</code> ，这表示它是 <code>/2026/07/05/foo/</code> 文章的一张资源图片，<code>![](image.jpg)</code> 将会被解析为 <code>&lt;img src=&quot;/2020/01/02/foo/image.jpg&quot;&gt;</code>。</p><p>舒服！但是，这有一个问题。</p><h2 id="文章封面图与其他页面的引用"><a href="#文章封面图与其他页面的引用" class="headerlink" title="文章封面图与其他页面的引用"></a>文章封面图与其他页面的引用</h2><p><code>hexo-renderer-marked</code> 插件是不会将 <code>frontmatter</code> 的 <code>cover</code> 或类似字段解析的，这意味着，如果你在 <code>frontmatter</code> 写 <code>cover: image.jpg</code>，那它就指的是 <code>博客根目录/themes/solitude/source/image.jpg</code>。但是我们想要它指向文章的资源文件夹内的图片，这就难搞了。</p><h3 id="1-使用图床-1"><a href="#1-使用图床-1" class="headerlink" title="1. 使用图床"></a>1. 使用图床</h3><p>和上面一样，不多说了。直接写图片链接。</p><h3 id="2-根据permalink计算实际路径"><a href="#2-根据permalink计算实际路径" class="headerlink" title="2. 根据permalink计算实际路径"></a>2. 根据permalink计算实际路径</h3><p>Permalink 是啥？</p><a class="tag-link" href="https://hexo.io/zh-cn/docs/permalinks" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">永久链接（Permalinks…</div>            <div class="tag-link-sitename">介绍了如何在 Hexo 中配置和自定义永久链接…</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><p>默认配置是 <code>permalink: :year/:month/:day/:title/</code></p><p>假设你的 <code>foo</code> 文章的图片 <code>image.jpg</code> 在三连后的位置是 &#x2F;2026&#x2F;07&#x2F;05&#x2F;foo&#x2F;image.jpg ，那么在 <code>cover</code> 字段你应该写的路径是 <code>/2026/07/05/foo/image.jpg</code>。</p><p>每次都这么算，太麻烦了，怎么办？</p><h3 id="3-使用脚本"><a href="#3-使用脚本" class="headerlink" title="3. 使用脚本"></a>3. 使用脚本</h3><p>不知道是我的问题还是必应的问题，我一直都没有搜到很合适的方法。</p><p>Vibe Coding 启动！<span class="spoiler blur-text">梁圣真是大善人✋😭✋</span></p><p>通过我与 Deepseek 10 分钟的交流，也是成功写了个脚本，放在 <code>博客根目录/scripts/cover-path.js</code>。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">hexo.<span class="property">extend</span>.<span class="property">filter</span>.<span class="title function_">register</span>(<span class="string">&#x27;before_post_render&#x27;</span>, <span class="keyword">function</span>(<span class="params">data</span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (data.<span class="property">cover</span> &amp;&amp; !data.<span class="property">cover</span>.<span class="title function_">startsWith</span>(<span class="string">&#x27;http&#x27;</span>) &amp;&amp; !data.<span class="property">cover</span>.<span class="title function_">startsWith</span>(<span class="string">&#x27;/&#x27;</span>)) &#123;</span><br><span class="line">    <span class="comment">// data.path 例如 &quot;2026/07/05/foo/index.html&quot;</span></span><br><span class="line">    <span class="keyword">let</span> dir = data.<span class="property">path</span>.<span class="title function_">replace</span>(<span class="regexp">/\/index\.html$/</span>, <span class="string">&#x27;&#x27;</span>).<span class="title function_">replace</span>(<span class="regexp">/\.html$/</span>, <span class="string">&#x27;&#x27;</span>);</span><br><span class="line">    <span class="keyword">if</span> (!dir.<span class="title function_">endsWith</span>(<span class="string">&#x27;/&#x27;</span>)) dir += <span class="string">&#x27;/&#x27;</span>;</span><br><span class="line">    data.<span class="property">cover</span> = <span class="string">&#x27;/&#x27;</span> + dir + data.<span class="property">cover</span>;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`[转换] <span class="subst">$&#123;data.title&#125;</span> -&gt; <span class="subst">$&#123;data.cover&#125;</span>`</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> data;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>第一行的</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo.<span class="property">extend</span>.<span class="property">filter</span>.<span class="title function_">register</span>(<span class="string">&#x27;before_post_render&#x27;</span>, <span class="keyword">function</span>(<span class="params">data</span>) &#123;</span><br></pre></td></tr></table></figure><p>表示向 Hexo 注册一个过滤器，在文章渲染前执行。在文章渲染前，这个脚本会读取 <code>cover</code> 字段和文章路径，如果 <code>cover</code> 字段不是 <code>http</code> 或 <code>/</code> 开头，就将你本来写的 <code>image.jpg</code> 转换为 <code>/2026/07/05/foo/image.jpg</code>。</p><p>不用自己算了，太方便了。以后直接写封面文件名就行了。</p><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p>Hexo 的图片引用问题总算搞定了，现在引用文章图片更方便了。</p><p>不过 vibe coding 可能会有 bug，对于一些情况处理不太好，建议还是自己检查一下比较好。</p><p>如果遇到脚本处理不了的问题，欢迎发邮件给我<a href="mailto:&#x4c;&#74;&#88;&#x48;&#95;&#72;&#64;&#111;&#117;&#116;&#108;&#x6f;&#111;&#x6b;&#x2e;&#x63;&#x6f;&#109;">点击发送</a></p><p>后续我打算写几个 Hexo 相关问题的记录，再说说我的博客的方案。随缘更新。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>2FA 与 TOTP：更好地保护你的账号</title>
      <link>https://msqy-h.github.io/2026/07/05/2fa/</link>
      <description>2FA 简明开启教程</description>
      <author>陌上轻烟</author>
      <category domain="https://msqy-h.github.io/categories/%E6%8A%80%E6%9C%AF/">技术</category>
      <category domain="https://msqy-h.github.io/tags/2FA/">2FA</category>
      <category domain="https://msqy-h.github.io/tags/%E5%AE%89%E5%85%A8/">安全</category>
      <enclosure url="https://msqy-h.github.io/2026/07/05/2fa/d4.webp" type="image/webp"/>
      <pubDate>Sun, 05 Jul 2026 10:22:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="✍️写在前面"><a href="#✍️写在前面" class="headerlink" title="✍️写在前面"></a>✍️写在前面</h2><p>最近闲来无事，在翻 GitHub 的账号设置。翻到 <code>Password and authentication（密码和身份验证）</code> 时，看到了个 <code>Two-factor authentication（双重认证）</code> ，也就是 2FA。一查才知道，这个东西对账号安全非常重要。也就是说，开启 2FA，基本没人盗得了你的号<span class="spoiler blur-text">包括你自己，但是不用担心，只要保存好恢复码，你还是能找回账号</span></p><p>下面进入正题</p><div class="note warning"><i class="solitude fa-solid fa-triangle-exclamation"></i><p>注意：本人非专业人士，本文内容全是个人理解，如有错误，欢迎指正</p></div><h2 id="🤔介绍-2FA"><a href="#🤔介绍-2FA" class="headerlink" title="🤔介绍 2FA"></a>🤔介绍 2FA</h2><h3 id="什么是-2FA"><a href="#什么是-2FA" class="headerlink" title="什么是 2FA"></a>什么是 2FA</h3><blockquote><p>双因子认证（2FA）是一种结合两种不同验证因素的身份认证方法，通常包括用户已知的密码（如账号密码）和用户持有的实物（如手机、令牌或生物特征）两类要素。其目的是通过双重验证提升账户安全性，主要应用于企业远程访问、金融及电子商务等领域。——<a href="https://baike.baidu.com/item/%E5%8F%8C%E5%9B%A0%E5%AD%90%E8%AE%A4%E8%AF%81%E6%9C%BA%E5%88%B6/9748125">百度百科</a></p></blockquote><p>通俗地讲，其实就是你登录时要用两个东西：一个是<strong>你的密码</strong>，一个是<strong>你手里有的东西</strong>，例如<code>手机</code>、<code>令牌</code>等东西</p><h3 id="常用的认证方式"><a href="#常用的认证方式" class="headerlink" title="常用的认证方式"></a>常用的认证方式</h3><p>常用的认证方式有这几种：</p><ul><li><strong>SMS OTP（短信验证码）</strong>：没错，就是你经常输入的短信验证码</li><li><strong>TOTP（基于时间的一次性密码）</strong>：听起来很高大上，其实就是网站和你约定好一个暗号，各自用相同算法，结合当前时间，算出一个验证码，比对算出的结果，结果一样就放行</li><li><strong>FIDO2（硬件安全密钥）</strong>：使用实体设备通过一定方式连接网站进行认证</li><li><strong>推送确认</strong>：网站推送一个认证请求到手机APP上，在手机APP上确认</li><li>等等</li></ul><p>这里主要介绍 <strong>TOTP</strong>，也<strong>推荐使用这个</strong></p><p>为什么？</p><p>好问题。</p><p>下面是简单的对比。</p><ul><li><strong>TOTP</strong>：<strong>安全性较高</strong>，不依赖网站方的短信发送速度，<strong>速度快且比较方便</strong>。</li><li><strong>短信</strong>：虽然<strong>方便</strong>，但是手机信号差时，根本收不到<span class="spoiler blur-text">虽然一般情况下能收到，但是到地下室或电梯就麻烦了</span>。这还是其次，主要是网站方<strong>发送的短信不一定及时到</strong><span class="spoiler blur-text">特别点名某度，经常是五分钟过去了，点了重发，上一个失效的验证码才到 ~~德铁行为（~~</span>，且安全性低。</li><li><strong>FIDO2</strong>：<strong>最安全</strong>，但是<strong>又麻烦又贵</strong></li><li><strong>推送确认</strong>：这样还需要在手机<strong>多安装APP</strong>，浪费空间<span class="spoiler blur-text">但是GitHub的挺好用的</span></li></ul><p>当然，如果你没有什么特别重要的东西，用短信就够了。但是，如果你想学习也可以，以备不时之需（假如某网站突然强制要求使用了呢）</p><p>下面是 TOTP 的使用教程</p><h2 id="🔐2FA-的开启与-TOTP-的使用"><a href="#🔐2FA-的开启与-TOTP-的使用" class="headerlink" title="🔐2FA 的开启与 TOTP 的使用"></a>🔐2FA 的开启与 TOTP 的使用</h2><div class="note success"><i class="solitude fa-solid fa-lightbulb"></i><p>你需要准备：</p><ul><li>脑子</li><li>手</li><li>手机或电脑</li><li>网络</li></ul></div><p>下面以 <strong>GitHub</strong> 的开启为例</p><h3 id="1-选择你的-TOTP-应用"><a href="#1-选择你的-TOTP-应用" class="headerlink" title="1. 选择你的 TOTP 应用"></a>1. 选择你的 TOTP 应用</h3><p>这里建议使用<strong>开源</strong>的，这是因为开源的代码完全透明，一看就知道有没有后门。就算你看不懂代码，也会有大佬帮你看。</p><p>当然，不想那么麻烦，用 <strong>Google Authenticator</strong> 或 <strong>Microsoft Authenticator</strong> 也行。大厂产品，有保障。</p><p>我推荐 <strong>2FAS</strong>，这个APP完全开源，界面也不错，支持导入导出，苹果安卓都有。<a href="https://2fas.com/">官网传送门</a></p><div class="repo-card">  <a class='name-qromj6t91 repo-title fancybox' href="https://github.com/twofas/2fas-pass-android"></a>  <p class='desc-qromj6t91 repo-desc'></p>  <div class="repo-gist">    <i class="fas fa-star"></i>    <span class='star-qromj6t91'></span>    <i class="fas fa-code-branch"></i>    <span class='fork-qromj6t91'></span>  </div>  <img class='lang-qromj6t91 repo-language no-lightbox' />  <script>    fetch('https://api.github.com/repos/twofas/2fas-pass-android')      .then(res => res.json())      .then(data => {        document.querySelector('.name-qromj6t91').innerText = data.name;        document.querySelector('.desc-qromj6t91').innerText = data.description;        document.querySelector('.star-qromj6t91').innerText = data.stargazers_count;        document.querySelector('.fork-qromj6t91').innerText = data.forks_count;        document.querySelector('.lang-qromj6t91').src = 'https://skillicons.dev/icons?i=' + data.language.toLowerCase();    });  </script>  </div><div class="repo-card">  <a class='name-e5ovd1lwz repo-title fancybox' href="https://github.com/twofas/2fas-pass-ios"></a>  <p class='desc-e5ovd1lwz repo-desc'></p>  <div class="repo-gist">    <i class="fas fa-star"></i>    <span class='star-e5ovd1lwz'></span>    <i class="fas fa-code-branch"></i>    <span class='fork-e5ovd1lwz'></span>  </div>  <img class='lang-e5ovd1lwz repo-language no-lightbox' />  <script>    fetch('https://api.github.com/repos/twofas/2fas-pass-ios')      .then(res => res.json())      .then(data => {        document.querySelector('.name-e5ovd1lwz').innerText = data.name;        document.querySelector('.desc-e5ovd1lwz').innerText = data.description;        document.querySelector('.star-e5ovd1lwz').innerText = data.stargazers_count;        document.querySelector('.fork-e5ovd1lwz').innerText = data.forks_count;        document.querySelector('.lang-e5ovd1lwz').src = 'https://skillicons.dev/icons?i=' + data.language.toLowerCase();    });  </script>  </div><p>进到对应仓库下载即可。iOS 在 AppStore 下载。</p><h3 id="2-开启-GitHub-的-2FA-验证"><a href="#2-开启-GitHub-的-2FA-验证" class="headerlink" title="2. 开启 GitHub 的 2FA 验证"></a>2. 开启 GitHub 的 2FA 验证</h3><p>看到这，我默认你有 GitHub 帐号了。</p><ol><li>打开 <a href="https://github.com/settings/security">GitHub 安全设置</a></li><li>选择 <code>Password and authentication</code></li><li>滑到最底部，你会看见这么个东西：<br><img src="/2026/07/05/2fa/2fa-1.webp" alt="2fa-1.webp" title="双重验证选项"><br>点击<code>Enable two-factor authentication</code></li><li>GitHub 会让你认证账号，按指示操作即可</li><li>设置 2FA 密钥。点击 2FAS 右下角的加号，扫描二维码，复制生成的验证码，输入到 GitHub 中。<br><img src="/2026/07/05/2fa/2fa-2.webp" alt="2fa-2.webp" title="双重验证密钥获取"></li><li>保存恢复代码。**务必妥善保管！丢了将无法找回！**建议抄纸上放抽屉，或者多复制几份到不联网设备上。</li><li>完成！</li></ol><p>以后登录 GitHub 时，只需输入密码，再简单地输入 TOTP 码即可。<strong>现在你的账号很安全</strong>了！</p><p>同理，其他的网站也可以这么操作（比如 <code>Microsoft</code> ）。</p><div class="note warning"><i class="solitude fa-solid fa-triangle-exclamation"></i><p><strong>务必保存好恢复代码，但是不要泄露给其他人</strong>，否则一旦丢失，你的账号永远回不来了！<span class="spoiler blur-text">也不至于彻底找不回来，但是非常麻烦</span></p></div><h2 id="💬再说几句"><a href="#💬再说几句" class="headerlink" title="💬再说几句"></a>💬再说几句</h2><p><strong>并不是开启了 2FA，就可以高枕无忧了。<strong>毕竟 2FA 的 TOTP 不是没有风险的，比如有人监控了你的手机，那他就可以直接获取你的密钥。所以，还是要</strong>注意上网安全，不乱下软件，不泄露真实信息，这才是根本。</strong></p><h2 id="🏁结尾"><a href="#🏁结尾" class="headerlink" title="🏁结尾"></a>🏁结尾</h2><p>本文主要介绍了 2FA，相信看到这里你一定有收获。总之，<strong>没有身份验证方法是绝对安全的</strong>。不过也不用怕，如果你只是普通用户，而且账号没什么重要的东西，那么用短信就够了，比纯密码好。如果你想保护你的账号，那就给你的账号加上 2FA 吧。</p><h2 id="📅更新计划"><a href="#📅更新计划" class="headerlink" title="📅更新计划"></a>📅更新计划</h2><p>打算下一篇写一下 Hexo 博客的部署教程。随缘更新，可能会写写其他的。生活愉快。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>第一篇文章</title>
      <link>https://msqy-h.github.io/2026/06/12/test/</link>
      <description>第一篇文章。</description>
      <author>陌上轻烟</author>
      <category domain="https://msqy-h.github.io/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      <category domain="https://msqy-h.github.io/tags/Hexo/">Hexo</category>
      <category domain="https://msqy-h.github.io/tags/Solitude/">Solitude</category>
      <enclosure url="https://msqy-h.github.io/img/d1.webp" type="image/webp"/>
      <pubDate>Fri, 12 Jun 2026 02:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="欢迎来到第一篇文章！"><a href="#欢迎来到第一篇文章！" class="headerlink" title="欢迎来到第一篇文章！"></a>欢迎来到第一篇文章！</h2><blockquote><p>本文旨在逐一测试 <code>solitude</code> 主题文档中列出的所有外挂标签，同时兼顾 Hexo 原生 Markdown 语法。文章内容基于真实生活片段，每个标签都配有实际示例，助你快速上手。</p></blockquote><hr><h2 id="一、早起，先来点基础-Markdown-热身-🌅"><a href="#一、早起，先来点基础-Markdown-热身-🌅" class="headerlink" title="一、早起，先来点基础 Markdown 热身 🌅"></a>一、早起，先来点基础 Markdown 热身 🌅</h2><p>早上 7:00，被闹钟叫醒。我习惯用 <strong>Markdown</strong> 记录待办事项：</p><ul><li><input disabled="" type="checkbox"> 晨跑 5 公里（<strong>已完成</strong>）</li><li><input checked="" disabled="" type="checkbox"> 写一篇全标签测试文章（<em>正在做</em>）</li><li><input disabled="" type="checkbox"> 整理上周的读书笔记</li></ul><blockquote><p>今日格言：<code>代码</code> 是理性的诗，生活是感性的画。</p></blockquote><p>接着，我用一个表格规划今天的三餐：</p><table><thead><tr><th>时段</th><th>菜单</th><th>热量(千卡)</th></tr></thead><tbody><tr><td>早餐</td><td>燕麦粥+鸡蛋</td><td>350</td></tr><tr><td>午餐</td><td>鸡胸肉沙拉</td><td>420</td></tr><tr><td>晚餐</td><td>清蒸鱼+蔬菜</td><td>480</td></tr></tbody></table><p>以下是今天要用的代码片段（展示 Hexo 代码块）：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># _config.yml 片段</span></span><br><span class="line"><span class="attr">theme:</span> <span class="string">solitude</span></span><br><span class="line"><span class="attr">language:</span> <span class="string">zh-CN</span></span><br></pre></td></tr></table></figure><hr><h2 id="二、内置标签初体验（无需插件）"><a href="#二、内置标签初体验（无需插件）" class="headerlink" title="二、内置标签初体验（无需插件）"></a>二、内置标签初体验（无需插件）</h2><ol><li><p>Charts – 展示我的运动数据</p></li><li><p>Gallery – 随手拍的早餐照片</p></li></ol><p>用图片列表展示早餐美图（占位图示例）。</p><div class="gallery-container waterfall">              </div><ol start="3"><li>GalleryGroup – 周末出游相册</li></ol><p>将一次近郊游的照片归组。</p><figure class="gallery-group">  <img class="gallery-group-img no-lightbox" src='https://picsum.photos/400/300?random=4' alt="Group Image Gallery">  <figcaption>  <div class="gallery-group-name">春日山行</div>  <p>2026.05.16 摄于西山</p>  <a href='https://solitude.js.org'></a>  </figcaption>  </figure>  <ol start="4"><li>Mermaid – 绘制今日流程图</li></ol><p>需要开启 mermaid。我用它画一个简单的晨间决策图。</p><ol start="5"><li>Typeit – 一句早安问候</li></ol><p>需要开启 typeit。它会像打字机一样逐字出现。</p><ol start="6"><li>Tabs – 今日书影音推荐</li></ol><p>用标签页切换不同类别的推荐内容。</p><div class="tabs" id="今日推荐"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#今日推荐-0">书籍</button></li><li class="tab"><button type="button" data-href="#今日推荐-1">电影</button></li><li class="tab"><button type="button" data-href="#今日推荐-2">音乐</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="今日推荐-0"><p>《百年孤独》—— 马尔克斯的魔幻世界。</p></div><div class="tab-item-content" id="今日推荐-1"><p>《星际穿越》—— 诺兰的时空史诗。</p></div><div class="tab-item-content" id="今日推荐-2"><p>《Lofi Girl》直播 —— 专注学习伴侣。</p></div></div></div><hr><h2 id="三、插件标签大集合（需安装对应插件）"><a href="#三、插件标签大集合（需安装对应插件）" class="headerlink" title="三、插件标签大集合（需安装对应插件）"></a>三、插件标签大集合（需安装对应插件）</h2><ol start="7"><li>Bubble – 给文字加注小提示</li></ol><p>给关键词添加浮动解释。</p><p>这是一段关于</p><div class="bubble"><span class="bubble-content">心流</span><span class="bubble-notation bg-blue">🌟</span></div><p>的文字描述，当你悬浮时能看到提示。</p><ol start="8"><li>Button – 一个带链接的按钮</li></ol><p>点击跳转到我的 GitHub。</p><button class="st-btn" onclick="window.open(&#39;https:&#x2F;&#x2F;github.com&#x2F;MSQY-H&#39;)"><i class="solitude fab fa-github"></i><span>访问我的GitHub</span></button><ol start="9"><li>Card – 展示一本我爱读的书</li></ol><p>用卡片形式展示书籍信息。</p><div title="钢铁是怎样炼成的" referrerPolicy="no-referrer" class="card_box" style="background-image: url(https:&#x2F;&#x2F;bookcover.yuewen.com&#x2F;qdbimg&#x2F;349573&#x2F;1023889158&#x2F;300); width:200px; height:275px;"><div class="card_mask"><span>《钢铁是怎样炼成的》是苏联作家尼古拉·奥斯特洛夫斯基创作的自传体长篇小说。</span><a href="https://book.qidian.com/info/1023889158/">查看详情</a></div><div class="card_top"><i class="fa-solid fa-book-open"></i><span>小说</span></div><div class="card_content"><span>钢铁是怎样炼成的</span><div><i class="fa-solid fa-star"></i><i class="fa-solid fa-star"></i><i class="fa-solid fa-star"></i><i class="fa-solid fa-star"></i><i class="fa-solid fa-star-half-alt"></i></div></div></div><ol start="10"><li>Checkbox – 今日任务清单</li></ol><p>用复选框标记已完成和待办。</p><div class="checkbox circle"><input type="checkbox" checked=checked/><p>完成所有标签测试</p></div><div class="checkbox circle"><input type="checkbox" checked=checked/><p>备份博客源码</p></div><div class="checkbox square"><input type="checkbox" checked=checked/><p>写一篇周记</p></div><ol start="11"><li>Fold – 折叠一段深夜碎碎念</li></ol><p>默认收起的情绪记录。</p><details><summary>点击展开我的小情绪</summary><p>今天测试标签的时候，发现 Mermaid 图表真的很方便，但配置时要注意缩进。生活也是这样，细节决定质感。</p></details><ol start="12"><li>Img – 带样式的图片</li></ol><p>控制图片显示大小和样式。</p><img src="https://picsum.photos/600/400?random=5" alt="随机风景" style="width:80%;border-radius:10px;"><ol start="13"><li>Inline Img – 行内小图标</li></ol><p>在段落中插入一个小图标，比如表示“重要”。</p><p>这是一段文字，里面有个重要标记 <img src="https://picsum.photos/20/20?random=6" title="important" height="display:inline;width:20px;vertical-align:middle;" class="inline-img"> 请注意这里。</p><ol start="14"><li>Keyboard – 快捷键提示</li></ol><p>写作时常用的保存快捷键。</p><p>按下 <span class="keyboard-tag">Ctrl+s</span> 即可快速保存文章。</p><ol start="15"><li>Link – 显示链接卡片</li></ol><p>展示一个带标题和描述的链接。</p><a class="tag-link" href="https://blog.starsharbor.com/solitude/" target="_blank">    <div class="tag-link-tips">引用站外链接</div>    <div class="tag-link-bottom">        <div class="tag-link-left">          <i class="solitude fas fa-link"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">Solitude主题文档</div>            <div class="tag-link-sitename">官方使用指南</div>        </div>        <i class="solitude fas fa-chevron-right"></i>    </div></a><ol start="16"><li>Media 系列 – 影音娱乐</li></ol><p>Audio – 一首背景音乐</p><div class="audio"><audio controls preload><source src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3" type="audio&#x2F;mp3">Your browser does not support the audio tag.</source></div><p>Bilibili – 我喜欢的UP主视频</p><div class="video-container"><iframe src="//player.bilibili.com/player.html?autoplay=0&bvid=BV1Ek4y1r7Rg" frameborder="0" loading="lazy" allowfullscreen></iframe></div><p>Video – 本地视频示例</p><div class="video"><video controls preload><source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video&#x2F;mp4">Your browser does not support the video tag.</source></div>Videos – 视频列表（两列）<div class="videos" col="2"><div class="video"><video controls preload><source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video&#x2F;mp4">Your browser does not support the video tag.</source></div><div class="video"><video controls preload><source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video&#x2F;mp4">Your browser does not support the video tag.</source></div></div><ol start="17"><li>Note – 各种提示框</li></ol><p>展示不同风格的提示。</p><div class="note primary"><i class="solitude fa-solid fa-circle-info"></i><p>主要提示：本文章所有标签均基于 solitude 主题文档编写。</p></div><div class="note success"><i class="solitude fa-solid fa-circle-check"></i><p>成功：已测试完成大部分标签。</p></div><div class="note warning"><i class="solitude fa-solid fa-triangle-exclamation"></i><p>警告：部分标签需要先在配置中开启。</p></div><div class="note danger"><i class="solitude fa-solid fa-circle-xmark"></i><p>危险：别忘记备份你的文章！</p></div><ol start="18"><li>Repo 仓库展示 – 我的代码库</li></ol><p>Github</p><div class="repo-card">  <a class='name-xhzyqlylv repo-title fancybox' href="https://github.com/hexojs/hexo"></a>  <p class='desc-xhzyqlylv repo-desc'></p>  <div class="repo-gist">    <i class="fas fa-star"></i>    <span class='star-xhzyqlylv'></span>    <i class="fas fa-code-branch"></i>    <span class='fork-xhzyqlylv'></span>  </div>  <img class='lang-xhzyqlylv repo-language no-lightbox' />  <script>    fetch('https://api.github.com/repos/hexojs/hexo')      .then(res => res.json())      .then(data => {        document.querySelector('.name-xhzyqlylv').innerText = data.name;        document.querySelector('.desc-xhzyqlylv').innerText = data.description;        document.querySelector('.star-xhzyqlylv').innerText = data.stargazers_count;        document.querySelector('.fork-xhzyqlylv').innerText = data.forks_count;        document.querySelector('.lang-xhzyqlylv').src = 'https://skillicons.dev/icons?i=' + data.language.toLowerCase();    });  </script>  </div><p>Gitea（假设自建服务）</p><div class="repo-card">  <a class='name-on4rdel72 repo-title fancybox' href="https://git.example.com/user/repo"></a>  <p class='desc-on4rdel72 repo-desc'></p>  <div class="repo-gist">    <i class="fas fa-star"></i>    <span class='star-on4rdel72'></span>    <i class="fas fa-code-branch"></i>    <span class='fork-on4rdel72'></span>  </div>  <img class='lang-on4rdel72 repo-language no-lightbox' />  <script>    fetch('https://git.example.com/api/v1/repos/user/repo')      .then(res => res.json())      .then(data => {        document.querySelector('.name-on4rdel72').innerText = data.name;        document.querySelector('.desc-on4rdel72').innerText = data.description;        document.querySelector('.star-on4rdel72').innerText = data.stars_count;        document.querySelector('.fork-on4rdel72').innerText = data.forks_count;        document.querySelector('.lang-on4rdel72').src = 'https://skillicons.dev/icons?i=' + data.language.toLowerCase();    });  </script>  </div><p>Gitee</p><div class="repo-card">  <a class='name-9dq4nzjit repo-title fancybox' href="https://gitee.com/oschina/git-osc"></a>  <p class='desc-9dq4nzjit repo-desc'></p>  <div class="repo-gist">    <i class="fas fa-star"></i>    <span class='star-9dq4nzjit'></span>    <i class="fas fa-code-branch"></i>    <span class='fork-9dq4nzjit'></span>  </div>  <img class='lang-9dq4nzjit repo-language no-lightbox' />  <script>    fetch('https://gitee.com/api/v5/repos/oschina/git-osc')      .then(res => res.json())      .then(data => {        document.querySelector('.name-9dq4nzjit').innerText = data.name;        document.querySelector('.desc-9dq4nzjit').innerText = data.description;        document.querySelector('.star-9dq4nzjit').innerText = data.stargazers_count;        document.querySelector('.fork-9dq4nzjit').innerText = data.forks_count;        document.querySelector('.lang-9dq4nzjit').src = 'https://skillicons.dev/icons?i=' + data.language.toLowerCase();    });  </script>  </div><p>Gitlab</p><div class="repo-card">  <a class='name-ytdapezdv repo-title fancybox' href="https://gitlab.com/12345678"></a>  <p class='desc-ytdapezdv repo-desc'></p>  <div class="repo-gist">    <i class="fas fa-star"></i>    <span class='star-ytdapezdv'></span>    <i class="fas fa-code-branch"></i>    <span class='fork-ytdapezdv'></span>  </div>  <img class='lang-ytdapezdv repo-language no-lightbox' />  <script>    fetch('https://gitlab.com/api/v4/projects/12345678')      .then(res => res.json())      .then(data => {        document.querySelector('.name-ytdapezdv').innerText = data.name;        document.querySelector('.desc-ytdapezdv').innerText = data.description;        document.querySelector('.star-ytdapezdv').innerText = data.star_count;        document.querySelector('.fork-ytdapezdv').innerText = data.forks_count;        document.querySelector('.lang-ytdapezdv').src = 'https://skillicons.dev/icons?i=' + data.topics[data.topics.length - 1].toLowerCase().replace('.','');    });  </script>  </div><hr><h2 id="四、文字样式与特殊排版"><a href="#四、文字样式与特殊排版" class="headerlink" title="四、文字样式与特殊排版"></a>四、文字样式与特殊排版</h2><ol start="19"><li>P – 独立成行的特殊文本</li></ol><p>使用不同类名展示样式。</p><p class="center">居中对齐的段落</p><p class="red">红色强调文字</p><p class="huge">巨大字号标题</p><ol start="20"><li>Span – 行内样式</li></ol><p>在一句话中局部改变样式。</p><p>这是一句普通文字，<span class="yellow">其中这段被标黄</span>，还有<span class="blue">蓝色片段</span>。</p><ol start="21"><li>Spoiler – 剧透内容</li></ol><p>两种风格：模糊和方块。</p><p>以下内容涉及剧透，请谨慎观看：</p><p>· 模糊风格：<span class="spoiler blur-text">凶手是管家</span><br>· 方块遮盖：<span class="spoiler block-text">结局是开放式</span></p><ol start="22"><li>Timeline – 记录今天的时间轴</li></ol><p>完整回顾我的一天。</p><div class="timeline"><span class="timeline-title">今日时间轴</span><!-- timeline 07:00 -->起床，查看天气，决定去跑步。<!-- endtimeline --><!-- timeline 08:30 -->跑完步，冲澡，吃早餐（燕麦粥+鸡蛋）。<!-- endtimeline --><!-- timeline 10:00 -->开始写这篇测试文章，逐一试用标签。<!-- endtimeline --><!-- timeline 15:00 -->午休后，继续测试Media和Repo标签。<!-- endtimeline --><!-- timeline 20:00 -->完成所有测试，准备发布文章！<!-- endtimeline --></div><hr><h2 id="五、收尾与检查-✅"><a href="#五、收尾与检查-✅" class="headerlink" title="五、收尾与检查 ✅"></a>五、收尾与检查 ✅</h2><p>至此，我完成了文档中列出的所有外挂标签的测试，同时也覆盖了标题、列表、引用、代码块、表格、粗体斜体、图片、链接、分割线等 Hexo 原生 Markdown 语法。</p><p>小贴士：如果某些标签未正常显示，请确认 _config.yml 中对应的开关（如 charts、mermaid、typeit）已开启，并已安装所需的插件。</p><p>最后，用一条分割线结束这篇全功能测试文。</p><hr><p>感谢阅读！祝你博客之旅愉快。 🚀</p>]]>
      </content:encoded>
    </item>
  </channel>
</rss>
