<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="/atom.xsl"?><feed xmlns="http://www.w3.org/2005/Atom"><title>更新日志</title><subtitle>克喵的小站，记录着我的一些文字~</subtitle><link href="https://blog.518339.xyz/changelog/" rel="alternate"/><link href="https://blog.518339.xyz/changelog/atom.xml" rel="self" type="application/atom+xml"/><id>https://blog.518339.xyz/changelog/</id><updated>2026-10-10T14:45:00+08:00</updated><generator uri="https://gohugo.io/">Hugo 0.166.0</generator><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><entry><title>站点样式按页型加载，非文章页首屏 CSS 减少 14 KB</title><link href="" rel="alternate"/><id/><published>2026-10-10T14:45:00+08:00</published><updated>2026-10-10T14:45:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">上一阶段留下的选择 阶段 A 把 2016 行的单体 custom.css 拆成 29 个分片，但构建产物仍是一个文件、每个页面全量加载。这一阶段让「只在某些页型出现的样式」只在该页型加载。
拆分依据（先量后拆） 层叠零冲突：候选页级分片与全站分片的选择器零重合，候选之间也零重合。审计工具本轮修正了一个过严的冲突判据：原先「两组声明集合不同」即算冲突，会把「B 只是 A 的子集、同值」误报；正确判据是「同一属性被赋了不同值」。 跨页共用 …</summary><content type="html"><![CDATA[<h2 id="上一阶段留下的选择">上一阶段留下的选择</h2>
<p>阶段 A 把 2016 行的单体 <code>custom.css</code> 拆成 29 个分片，但构建产物仍是<strong>一个</strong>文件、每个页面全量加载。这一阶段让「只在某些页型出现的样式」<strong>只在该页型加载</strong>。</p>
<h2 id="拆分依据先量后拆">拆分依据（先量后拆）</h2>
<ul>
<li><strong>层叠零冲突</strong>：候选页级分片与全站分片的选择器零重合，候选之间也零重合。审计工具本轮修正了一个<strong>过严的冲突判据</strong>：原先「两组声明集合不同」即算冲突，会把「B 只是 A 的子集、同值」误报；正确判据是「同一属性被赋了不同值」。</li>
<li><strong>跨页共用的规则组不撕开</strong>：<code>special-components.css</code> 一条选择器列表同时服务 changelog / gallery / 404 / privacy / ai-news / stats 六个页型，按页拆会把一条逻辑规则撕成六份换来每页几百字节，因此<strong>刻意留在全站包</strong>（改名 <code>components/special-pages.css</code>）。</li>
</ul>
<h2 id="结构调整">结构调整</h2>
<p>规则简化为「<strong>目录决定加载范围</strong>」：</p>
<table>
	<thead>
			<tr>
					<th>位置</th>
					<th>加载范围</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>assets/css/custom.css</code></td>
					<td>全站（聚合根：<code>@charset</code> + 23 条 <code>@import</code>）</td>
			</tr>
			<tr>
					<td><code>assets/css/custom/{foundation,layout,components}/</code></td>
					<td>全站（被聚合根内联）</td>
			</tr>
			<tr>
					<td><code>assets/css/custom/pages/&lt;页型&gt;.css</code></td>
					<td>只在该页型（文件名 = 主题的页型键）</td>
			</tr>
			<tr>
					<td><code>assets/css/custom/pages/post/*.css</code></td>
					<td>跟随 <code>pages/post.css</code></td>
			</tr>
	</tbody>
</table>
<p>主题 <code>styles.html</code> 新增<strong>站点页级注入点</strong>（约 13 行，与主题自己的 <code>pages/*.css</code> 逻辑同构）：按同一套页型键取 <code>css/custom/pages/&lt;键&gt;.css</code>，缺失即跳过，输出带 <code>data-solitude-custom-page</code> 标记；排在 <code>css/custom.css</code> 之后，保证站点覆盖层最后生效。页级包内部用 <code>@import</code> 组合、构建期内联，因此<strong>每个页型最多只多出一个 <code>&lt;link&gt;</code></strong>。</p>
<h2 id="验收中发现并修掉的一个真回归">验收中发现并修掉的一个真回归</h2>
<p>静态门（下节）抓出：<code>pages/post/tags.css</code> 里三条规则的选择器列表把「文末标签」和「<strong>移动端侧栏标签云</strong>」写在了一起 ——</p>
<figure class="code-block" data-code-block data-language="css">
  <figcaption class="code-block__toolbar">
    <span class="code-block__language">css</span>
      <button class="code-block__copy" type="button" data-code-copy>复制</button>
  </figcaption>
  <div class="code-block__viewport">
    <pre class="code-block__fallback" tabindex="0"><code class="language-css">#post .post-meta__tags,
#post .post-meta__tags .tags-punctuation,
#sidebar #sidebar-menus .card-tag-cloud a { … }</code></pre>
  </div>
  <button class="code-block__expand" type="button" data-code-expand hidden>展开全部</button>
</figure>
<p>侧栏标签云在<strong>每个页面</strong>都存在，把整个分片划给文章页会让非文章页的移动端侧栏标签云掉样式。修法：按选择器把跨界规则拆开，全局部分独立成 <code>components/tag-cloud.css</code>（含 <code>/tags/</code> 页的标签计数徽标），文章页分片只留 <code>#post …</code> 的部分。这是<strong>阶段 A 的分片边界</strong>留下的隐藏跨界 —— 当时按原文件的注释分区切分，而注释的粒度不等于作用域。</p>
<h2 id="两道互补的机械门">两道互补的机械门</h2>
<p>按页加载只在两个方向会静默出事，各配一道门：</p>
<ol>
<li><strong>有效声明等价</strong>（改样式）：对每个页型，把「该页实际加载的全部产物」与「拆分前的全量产物」展开到<strong>单选择器</strong>粒度，逐属性比对最终生效声明。
<ul>
<li>必须展开到单选择器：压缩器会把相邻同声明规则合并成多选择器规则，按规则键比对会误报。</li>
<li>必须<strong>产物比产物</strong>：压缩器会改写颜色/数值（<code>#ffffff</code>→<code>#fff</code>），拿源码比产物同样会误报。</li>
<li>结果：文章页 392 个单选择器 / 关于页 323 / 留言板 326 / 其它页型 317，<strong>声明不一致均为 0</strong>；「仅拆分前有」的全部是该页不该加载的页级包。</li>
</ul>
</li>
<li><strong>不丢样式</strong>（丢样式）：逐个已构建页面检查「该页含某页级包选择器所需的类名/id，却没有加载该包」—— 判据取源码侧规则（含 <code>@media</code> 上下文），对每个页面 × 桌面/移动两种视口核验。结果：<strong>0 处</strong>。</li>
</ol>
<p>外加工具侧不变式：<code>node scripts/css-split-audit.mjs</code> 现在会展开每个页级包的 <code>@import</code> 链，检查空包、与全站包冲突、页级包两两冲突。之所以必须零冲突 —— swup 无刷新切页时样式表<strong>只增不减</strong>，访问过文章页再切到关于页，<code>pages/post.css</code> 会一直留在 DOM 里；一旦有冲突，同一个页面的样子就会随「你从哪一页走过来」而变。</p>
<h2 id="收益">收益</h2>
<table>
	<thead>
			<tr>
					<th>页型</th>
					<th>之前</th>
					<th>之后</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>首页 / 归档 / 标签 / 友链 / 说说 / 音乐 / 隐私 / 统计 / 404 / 更新日志 / 相册 …</td>
					<td>43.5 KB（1 个请求）</td>
					<td><strong>29.6 KB（1 个请求）</strong></td>
			</tr>
			<tr>
					<td>文章页</td>
					<td>43.5 KB</td>
					<td>29.6 KB + 11.7 KB（2 个并行请求）</td>
			</tr>
			<tr>
					<td>关于页 / 留言板</td>
					<td>43.5 KB</td>
					<td>29.6 KB + 1.0 KB / 1.3 KB</td>
			</tr>
	</tbody>
</table>
<p>首屏渲染阻塞 CSS 在绝大多数页型上减少约 14 KB。</p>
<h2 id="其他">其他</h2>
<ul>
<li>审计工具新增 <code>expandCssFiles</code> / <code>auditPageScope</code> / <code>splitSelectorList</code> / <code>explodeDeclarations</code>，测试 39 → 47 pass。</li>
<li>构建 0 error；CI 双产物断言通过；样式分片审计 <code>[ok]</code>；主题页级 CSS 与本轮改动零冲突。</li>
</ul>
]]></content></entry><entry><title>站点样式按页面与组件拆分为分片，新增分片审计工具</title><link href="" rel="alternate"/><id/><published>2026-10-10T14:00:00+08:00</published><updated>2026-10-10T14:00:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">为何要拆 assets/css/custom.css 是站点纸张风格覆盖层，此前是 2016 行 / 59.4 KB 的单体文件，导航、卡片、页脚、移动端、文章排版、关于页、留言板、投票评分全混在一起。改一处字体大小要在两千行里找归属，新加页面样式也没有明确落点，几周就会继续膨胀。
拆分方式 按文件里既有的分区注释逐字节切片，落到 …</summary><content type="html"><![CDATA[<h2 id="为何要拆">为何要拆</h2>
<p><code>assets/css/custom.css</code> 是站点纸张风格覆盖层，此前是 <strong>2016 行 / 59.4 KB 的单体文件</strong>，导航、卡片、页脚、移动端、文章排版、关于页、留言板、投票评分全混在一起。改一处字体大小要在两千行里找归属，新加页面样式也没有明确落点，几周就会继续膨胀。</p>
<h2 id="拆分方式">拆分方式</h2>
<ul>
<li>按文件里<strong>既有的分区注释</strong>逐字节切片，落到 <code>assets/css/custom/{foundation,layout,components,pages}/</code>，共 29 个分片（目录分类与主题 <code>assets/css/solitude/</code> 同构）。</li>
<li><code>assets/css/custom.css</code> 退化为<strong>聚合根</strong>：只保留 <code>@charset</code> 与 29 条 <code>@import</code>，导入顺序严格等于拆分前的行序。<code>css.Build</code> 会把 <code>@import</code> 内联，因此构建产物仍是<strong>一个</strong> <code>css/custom.&lt;hash&gt;.css</code> —— 请求数、首屏字节、层叠顺序全都不变。</li>
<li>主题的 <code>styles.html</code> 一行未改（<code>resources.Get &quot;css/custom.css&quot;</code> 与 targetPath 都保持原样），改的是源文件组织方式。</li>
</ul>
<p><strong>为什么坚持「顺序即语义」</strong>：这条覆盖层是<strong>无 layer</strong> 的，靠「最后加载 + 不分层」压过主题全部 <code>@layer solitude.*</code>，中间还隔着主题里未分层的 vendor 与 <code>overrides.css</code> 规则。分片一旦重排或改成 <code>@layer</code>，同权重选择器的胜负会静默翻转 —— 所以聚合根里写明了「勿重排、勿增删、勿分层」。</p>
<h2 id="等价性验证">等价性验证</h2>
<ul>
<li><strong>生产产物逐字节一致</strong>：拆分前后各跑一次 <code>hugo --minify</code>，产物文件名（内容哈希）与 SHA-256 完全相同 —— 浏览器收到的字节没变，层叠结果可证明未变。</li>
<li><strong>开发模式规则序列一致</strong>：243 条规则对 243 条，唯一差异是构建管线按 target 自动补的 <code>-webkit-backdrop-filter</code>（拆分前的产物里也有同一条前缀），属管线行为而非拆分差异。</li>
<li><strong>无路径泄漏</strong>：生产产物确认不含开发模式的 <code>ns-hugo-imp</code> 批注、Windows 绝对路径与残留 <code>@import</code>。</li>
</ul>
<h2 id="新增工具scriptscss-split-auditmjs">新增工具：<code>scripts/css-split-audit.mjs</code></h2>
<p>拆分会引入一类肉眼极难发现的问题（分片漏引、文件放错位置、顺序被改），所以配了机械审计：</p>
<ul>
<li>默认模式：查「聚合根 ↔ 分片目录」是否一一对应 —— 缺失 / 空片 / 孤儿文件 / 重复 <code>@import</code> / 分片内 <code>@charset</code> 或嵌套 <code>@import</code> / 聚合根里被直接写了规则。</li>
<li><code>--compare A B</code>：比对两份 CSS 产物是否等价，先比字节，不等时退化为「有序规则序列」比对，并区分「缺规则 / 多规则 / 仅顺序不同」。</li>
<li><code>--overlap A B</code>：列出两组分片共享的选择器。这条是为将来「真实按页加载」把关 —— 两组选择器不相交，它们的相对加载顺序才与层叠无关。</li>
</ul>
<p>工具内置一个极简 CSS 扫描器（正确处理注释、字符串、未加引号的 <code>url()</code>、<code>@media</code>/<code>@keyframes</code> 嵌套），并附带 26 个 <code>node:test</code> 用例（含属性选择器引号、An+B 空白、函数参数内空白等易误报形态的归一化）。</p>
<h2 id="兼容性与维护约定">兼容性与维护约定</h2>
<ul>
<li>未引入新依赖，未改主题，未改任何模板；CSS 片段内容除文件末尾补了一个换行外逐字节继承。</li>
<li>之后新增样式：先找归属分片，确实要新建分片就<strong>追加到聚合根 <code>@import</code> 末尾</strong>，并跑一次 <code>node scripts/css-split-audit.mjs</code>。约定与分片清单已写入 <code>docs/agents.md</code>。</li>
</ul>
]]></content></entry><entry><title>Cookie 同意真正生效：拒绝非必要即拦截统计与访客定位</title><link href="" rel="alternate"/><id/><published>2026-10-10T13:00:00+08:00</published><updated>2026-10-10T13:00:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">问题 Cookie 弹窗的「拒绝非必要」此前只是写了一条 solitude_cookie_consent Cookie 就把弹窗关掉，页面上其余脚本照常加载：
Umami 统计脚本写在 hugo.yaml 的 extends.head 里，是普通的 defer 脚本，渲染在 &amp;amp;lt;head&amp;amp;gt;；而弹窗逻辑在 body 末尾的 js/solitude.js 里，同样是 deferred。按规范 deferred 脚本严格按文档顺序执 …</summary><content type="html"><![CDATA[<h2 id="问题">问题</h2>
<p>Cookie 弹窗的「拒绝非必要」此前只是写了一条 <code>solitude_cookie_consent</code> Cookie 就把弹窗关掉，页面上其余脚本照常加载：</p>
<ul>
<li>Umami 统计脚本写在 <code>hugo.yaml</code> 的 <code>extends.head</code> 里，是<strong>普通的 <code>defer</code> 脚本</strong>，渲染在 <code>&lt;head&gt;</code>；而弹窗逻辑在 body 末尾的 <code>js/solitude.js</code> 里，同样是 deferred。按规范 deferred 脚本严格按文档顺序执行，<strong>统计永远先于弹窗判定跑完</strong>，事后知道用户拒绝了也没有意义。</li>
<li>访客定位卡（ip-welcome）在页面加载时就直接请求 <code>60s-api</code>、<code>ipify</code>、<code>ipapi.co</code> 等接口，完全不看同意状态。</li>
</ul>
<h2 id="修复">修复</h2>
<h3 id="拦截前移到-head">拦截前移到 <code>&lt;head&gt;</code></h3>
<ul>
<li><code>extends.head</code> 里的 Umami 标签改为惰性标记：<code>&lt;script type=&quot;text/plain&quot; data-consent=&quot;analytics&quot; data-consent-src=&quot;…&quot;&gt;</code>。浏览器既不执行也不发起请求。</li>
<li>新增 <code>_partials/consent-bootstrap.html</code>：渲染在 <code>extends.head</code> 之后的同步内联脚本，解析同意 Cookie 并决定是否把惰性标记「复活」成真实 <code>&lt;script&gt;</code>。选择「拒绝非必要」时脚本节点根本不会被创建，统计请求无从发出。</li>
<li>该脚本对外暴露 <code>window.SolitudeConsent</code>，bundle 侧的读写与激活全部复用它，避免两套实现漂移。</li>
</ul>
<h3 id="同意契约集中到站点配置">同意契约集中到站点配置</h3>
<ul>
<li>新增 <code>params.solitude.cookie_consent</code>（Cookie 名、契约版本、有效期、参与拦截的分类），弹窗逻辑与 <code>&lt;head&gt;</code> 引导脚本共用同一份配置。</li>
<li>契约版本升到 <code>v2</code>：老访客会被重新询问一次，因为「接受全部」的含义已从「仅统计」扩大到「统计 + 访客定位」。</li>
</ul>
<h3 id="访客定位卡接入门禁">访客定位卡接入门禁</h3>
<ul>
<li>新增 <code>assets/ts/core/consent.ts</code> 作为弹窗侧的同意状态中心（读写、分类判定、<code>solitude:consent</code> 事件、非必要残留清理）。</li>
<li>访客定位卡改为按 <code>optional</code> 分类门禁：未获同意（含尚未选择）时不发起任何外部请求，只显示站内兜底欢迎语，并在卡片内给出改选入口；同意后立即恢复定位，改选为拒绝时收起已展示的位置。</li>
</ul>
<h3 id="拒绝留下可验证的后果">「拒绝」留下可验证的后果</h3>
<ul>
<li>拒绝时清除 <code>umami</code> 开头的 Cookie 与本地存储、<code>meowloge-ip-welcome-*</code> 定位缓存，已拒绝的访客每次加载都会再清理一次，不留旧数据。</li>
<li>接受时当场激活被拦脚本，无需刷新页面。</li>
</ul>
<h3 id="随时可以改选">随时可以改选</h3>
<ul>
<li>隐私说明页「你的选择与权利」新增 Cookie 选择面板：回显当前选择，并提供「更改 Cookie 选择」按钮重开弹窗。</li>
<li>重开弹窗时回显上次选择，接受与拒绝之间可双向切换。</li>
</ul>
<h2 id="隐私说明页同步">隐私说明页同步</h2>
<ul>
<li>Cookie 表格改为真实键名与分类：唯一 Cookie 是 <code>solitude_cookie_consent</code>，其余界面偏好都在浏览器本地存储中。</li>
<li>Umami 章节改为「按 Cookie 选择加载」，并说明拒绝时不会创建脚本、会清除本机已有记录。</li>
<li>第三方资源章节补充访客定位所请求的接口，并说明该请求仅在「接受全部」后发出。</li>
</ul>
<h2 id="兼容性">兼容性</h2>
<ul>
<li>未引入新依赖；改动沿用主题现有构建链（<code>js.Build</code> / <code>resources.Get</code>）与令牌体系。</li>
<li>弹窗仍在 <code>#body-wrap</code> 之外，swup 无刷新切页后依然常驻；改选入口与状态回显走 <code>document</code> 事件委托。</li>
<li>已验证：Hugo 0.166.0 构建 0 error；本地 <code>1313</code> 端口用真实浏览器逐条核对 —— 全新访客、拒绝后刷新、接受后刷新、接受与拒绝互相切换、隐私页与定位卡两处重开入口，并在网络层确认拒绝时 <code>um.081531.xyz</code> 与各 IP 查询接口均无请求发出。</li>
</ul>
]]></content></entry><entry><title>最近评论样式全局化与纸张样式整理</title><link href="" rel="alternate"/><id/><published>2026-10-09T22:00:00+08:00</published><updated>2026-10-09T22:00:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">最近评论样式全局化 修复中控台、侧栏与留言板页等非归档页面中最近评论卡片无样式、头像以原始整图显示的问题。 将 console_recentcomments、comment-card 等评论卡片样式从按页加载的 taxonomy-archives.css 迁入全局加载的 aside-console.css，一处定义、全站生效，归档页行为保持不变。 纸张风格定制层整理 为 assets/css/custom.css 补充分节注释，把令牌、 …</summary><content type="html"><![CDATA[<h2 id="最近评论样式全局化">最近评论样式全局化</h2>
<ul>
<li>修复中控台、侧栏与留言板页等非归档页面中最近评论卡片无样式、头像以原始整图显示的问题。</li>
<li>将 <code>console_recentcomments</code>、<code>comment-card</code> 等评论卡片样式从按页加载的 <code>taxonomy-archives.css</code> 迁入全局加载的 <code>aside-console.css</code>，一处定义、全站生效，归档页行为保持不变。</li>
</ul>
<h2 id="纸张风格定制层整理">纸张风格定制层整理</h2>
<ul>
<li>为 <code>assets/css/custom.css</code> 补充分节注释，把令牌、导航、个人卡片、音乐播放器、正文纸面、目录浮层、滚动性能等模块分区梳理。</li>
<li>新增 <code>--paper-mask</code> 纸张遮罩令牌，移动端赞赏弹层的半透明遮罩改用该变量，不再散落硬编码色值。</li>
<li>导航菜单选中态选择器统一为 <code>:is(.active, [aria-current=&quot;page&quot;], [aria-current=&quot;true&quot;])</code>，补全 <code>aria-current=&quot;true&quot;</code> 状态支持。</li>
<li>重写中控台按钮的明暗状态链：浅色模式默认与普通导航按钮同色、打开后进入强调态；深色模式默认墨色文字、打开后使用纸张浅底，图标跟随文字色。</li>
<li>返回顶部按钮的纸面底、透明边框与圆角并入基础规则，hover/focus 时才显示边框强调与抬升反馈。</li>
<li>下拉菜单 hover 规则补齐子菜单项整体的覆盖，避免遗漏中间层。</li>
<li>移除移动端 body 遗留的背景层尺寸设置，背景保持单一纯色，毛玻璃背后画面稳定不掉帧。</li>
<li>移动端目录浮层、滚动中毛玻璃降级、留言板弹幕开关等规则块统一重排，行为保持不变。</li>
</ul>
<h2 id="主题样式清理">主题样式清理</h2>
<ul>
<li>音乐页满屏背景遮罩的毛玻璃由 <code>blur(200px)</code> 降为 <code>blur(24px)</code>，消除全站最重的单层绘制开销。</li>
<li><code>--efu-shadow-light2black</code> 改为引用 <code>--efu-shadow-lightblack</code>，消除重复的阴影字面量（明暗两套令牌同步）。</li>
<li>移除全局 <code>input::placeholder</code> 颜色覆盖，占位符颜色交由组件与浏览器默认。</li>
<li>删除导航中重复的 <code>#nav-totop #percent</code> 规则、首页分类栏重复的 <code>border: none</code> 与友链卡片的重复背景声明。</li>
<li>合并隐藏侧栏布局中两处宽度选择器，以及关于页重复的移动端媒体块。</li>
</ul>
<h2 id="兼容性">兼容性</h2>
<ul>
<li>全部改动沿用主题现有 CSS 变量与构建链，未引入新依赖与硬编码色值。</li>
<li>已通过 Hugo 构建检查，并在本地 <code>1313</code> 端口验证导航、中控台与最近评论卡片输出。</li>
</ul>
]]></content></entry><entry><title>Telegram 数据源与互动投票</title><link href="" rel="alternate"/><id/><published>2026-10-02T19:00:00+08:00</published><updated>2026-10-02T19:00:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">Telegram 数据源 说说页新增 tg 数据源，接入自托管的 koharu-suite Telegram API。 支持按 Telegram 频道拉取消息，并复用主题现有的瀑布流卡片布局。 支持 Telegram 正文 HTML 与 Markdown 两种渲染模式，可在 hugo.yaml 中配置。 支持 Telegram 图片媒体、附件、原文链接和频道信息展示。 支持隐藏正文开头或结尾用于分类的连续 Hashtag，同时保留标签栏 …</summary><content type="html"><![CDATA[<h2 id="telegram-数据源">Telegram 数据源</h2>
<ul>
<li>说说页新增 <code>tg</code> 数据源，接入自托管的 koharu-suite Telegram API。</li>
<li>支持按 Telegram 频道拉取消息，并复用主题现有的瀑布流卡片布局。</li>
<li>支持 Telegram 正文 HTML 与 Markdown 两种渲染模式，可在 <code>hugo.yaml</code> 中配置。</li>
<li>支持 Telegram 图片媒体、附件、原文链接和频道信息展示。</li>
<li>支持隐藏正文开头或结尾用于分类的连续 Hashtag，同时保留标签栏和标签筛选功能。</li>
<li>支持 Telegram 消息搜索和 RSS 订阅入口，仅在启用 <code>tg</code> 数据源时显示。</li>
<li>Telegram 卡片显示名称支持通过 <code>hugo.yaml</code> 自定义配置。</li>
</ul>
<h2 id="文章投票">文章投票</h2>
<ul>
<li>文章页接入自托管 <code>star-vote</code> 投票服务。</li>
<li>支持文章进行 1—5 星评分。</li>
<li>投票配置统一放在 <code>hugo.yaml</code>，可独立控制投票功能和服务地址。</li>
<li>投票组件继续使用主题现有的卡片布局、颜色变量和响应式样式。</li>
</ul>
<h2 id="说说点赞与踩">说说点赞与踩</h2>
<ul>
<li>Telegram 说说卡片新增点赞和踩功能。</li>
<li>使用 Telegram 消息 ID 作为稳定投票标识，分页、搜索后仍对应同一条消息。</li>
<li>显示点赞与踩的实时数量。</li>
<li>使用本地存储记录当前浏览器的投票状态，避免重复操作。</li>
<li>搜索结果中的 Telegram 说说卡片同样支持点赞和踩。</li>
<li>投票接口异常时不影响说说正文、媒体和评论功能。</li>
</ul>
<h2 id="安全与兼容性">安全与兼容性</h2>
<ul>
<li>Telegram HTML 内容经过 DOMPurify 净化后再渲染，避免远程内容直接注入脚本。</li>
<li>Memos 和 Ech0 数据源保持原有行为，不会加载 Telegram 搜索、RSS 或投票接口。</li>
<li>继续使用主题已有的 CSS 变量、间距、圆角和瀑布流布局，避免新增硬编码页面样式。</li>
<li>已通过 Hugo 构建检查，并在本地 <code>1313</code> 端口验证 Telegram 卡片、搜索和投票控件输出。</li>
</ul>
]]></content></entry><entry><title>性能优化与聊天 Shortcode</title><link href="" rel="alternate"/><id/><published>2026-10-02T07:00:00+08:00</published><updated>2026-10-02T07:00:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">性能优化 优化文章页资源加载与样式分片，减少不必要的页面级 CSS 负担。 继续沿用主题 CSS 变量、响应式布局和现有资源构建流程，避免新增硬编码页面尺寸。 优化文章内容组件的渲染结构，降低短代码对正文布局和主题样式的影响。 完善 Hugo 构建验证，确保新增内容在开发环境和生产构建中均可正常生成。 聊天记录 Shortcode 新增组件 新增 chat Hugo shortcode，用于在文章中展示聊天记录界面。 支持左右两侧消息气 …</summary><content type="html"><![CDATA[<h2 id="性能优化">性能优化</h2>
<ul>
<li>优化文章页资源加载与样式分片，减少不必要的页面级 CSS 负担。</li>
<li>继续沿用主题 CSS 变量、响应式布局和现有资源构建流程，避免新增硬编码页面尺寸。</li>
<li>优化文章内容组件的渲染结构，降低短代码对正文布局和主题样式的影响。</li>
<li>完善 Hugo 构建验证，确保新增内容在开发环境和生产构建中均可正常生成。</li>
</ul>
<h2 id="聊天记录-shortcode">聊天记录 Shortcode</h2>
<h3 id="新增组件">新增组件</h3>
<ul>
<li>新增 <code>chat</code> Hugo shortcode，用于在文章中展示聊天记录界面。</li>
<li>支持左右两侧消息气泡、发送者名称、时间提示和系统消息。</li>
<li>左右消息使用不同的对齐方式、气泡圆角和主题色，适配明暗主题。</li>
<li>左侧消息使用独立的卡片背景与边框，确保与文章背景保持清晰对比。</li>
<li>支持连续消息、消息内换行和基本 Markdown 内容。</li>
</ul>
<h3 id="使用语法">使用语法</h3>
<figure class="code-block" data-code-block data-language="go-html-template">
  <figcaption class="code-block__toolbar">
    <span class="code-block__language">go-html-template</span>
      <button class="code-block__copy" type="button" data-code-copy>复制</button>
  </figcaption>
  <div class="code-block__viewport">
    <pre class="code-block__fallback" tabindex="0"><code class="language-go-html-template">{{&lt; chat &gt;}}
{:2024-11-09 23:39:30}
{.}
也许
我们可以聊聊天
{.纸鹿}
我还可以有名字
{:纸鹿撤回了一条消息}
{用户 1}
有趣\\
我学到了。
{{&lt; /chat &gt;}}</code></pre>
  </div>
  <button class="code-block__expand" type="button" data-code-expand hidden>展开全部</button>
</figure>
<ul>
<li><code>{:内容}</code>：居中的时间或系统提示。</li>
<li><code>{.名称}</code>：切换到右侧发送者。</li>
<li><code>{名称}</code>：切换到左侧发送者。</li>
<li><code>{.}</code>：切换到右侧但不显示发送者名称。</li>
<li>普通文本：渲染为当前发送者的消息气泡。</li>
<li><code>\\</code>：在同一条消息中换行。</li>
</ul>
<h2 id="文档与样式">文档与样式</h2>
<ul>
<li>在 <code>shortcodes.md</code> 中新增聊天组件的语法说明和可构建示例。</li>
<li>新增主题聊天样式，并接入 Solitude 核心 CSS 构建链。</li>
<li>组件样式限定在文章容器内，不影响站点其他模块。</li>
<li>桌面端与移动端均支持自适应宽度，避免长文本溢出。</li>
</ul>
<h2 id="日期与时区">日期与时区</h2>
<ul>
<li>统一使用站点配置中的 <code>timeZone: Asia/Shanghai</code> 处理日期显示。</li>
<li>更新日志、文章详情页、首页文章卡片、归档列表和相关文章中的日期均显式转换为东八区。</li>
<li>修复带有 <code>+08:00</code> 时区的日期被按 UTC 显示、时间倒退 8 小时的问题。</li>
<li>保留 <code>&lt;time&gt;</code> 元素中的标准时区偏移信息，兼顾页面展示与语义化时间数据。</li>
</ul>
<h2 id="兼容性">兼容性</h2>
<ul>
<li>不引入额外 JavaScript 依赖，聊天记录在 Hugo 构建阶段静态生成。</li>
<li>继续使用主题现有颜色、间距、圆角和字体变量。</li>
<li>保留 Markdown 正文的正常渲染能力，不影响已有 shortcode。</li>
<li>已通过 Hugo 构建检查，并在本地 <code>1313</code> 端口验证页面输出。</li>
</ul>
]]></content></entry><entry><title>移动端导航与 Atom 订阅页优化</title><link href="" rel="alternate"/><id/><published>2026-09-28T19:30:00+08:00</published><updated>2026-09-28T19:30:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">移动端导航 紧凑顶栏 ≤1024px 的首页、文章页、归档、分类、标签、友链与普通页面统一使用固定顶栏 移动端隐藏站名文字，保留首页图标，避免功能按钮过多时发生挤压 左侧新增独立的站点导航入口，读取 hugo.yaml 中 params.solitude.nav.group 的分组链接 右侧按「搜索 → 中控台 → 目录 → 回到顶部 → 菜单」排列，目录入口只在当前页面存在目录时显示 桌面端 &amp;amp;gt;1024px 的站名、主导航、左上 …</summary><content type="html"><![CDATA[<h2 id="移动端导航">移动端导航</h2>
<h3 id="紧凑顶栏">紧凑顶栏</h3>
<ul>
<li><code>≤1024px</code> 的首页、文章页、归档、分类、标签、友链与普通页面统一使用固定顶栏</li>
<li>移动端隐藏站名文字，保留首页图标，避免功能按钮过多时发生挤压</li>
<li>左侧新增独立的站点导航入口，读取 <code>hugo.yaml</code> 中 <code>params.solitude.nav.group</code> 的分组链接</li>
<li>右侧按「搜索 → 中控台 → 目录 → 回到顶部 → 菜单」排列，目录入口只在当前页面存在目录时显示</li>
<li>桌面端 <code>&gt;1024px</code> 的站名、主导航、左上角快捷分组和原有右下角按钮保持不变</li>
<li>移动端菜单图标统一按钮尺寸与视觉基线，修复 bars 图标低于其它按钮的问题</li>
<li>浅色模式下移动顶栏使用纸张主题变量，避免浅色背景上出现白色图标或文字</li>
</ul>
<h3 id="双侧导航面板">双侧导航面板</h3>
<ul>
<li>左侧站点导航面板独立展示“主页 / 项目”等外部快捷链接</li>
<li>右侧菜单面板继续复用项目原有的手机端侧栏，保留统计、显示模式、主导航、标签云和站点信息</li>
<li>两个面板互斥打开，分别从左侧和右侧进入</li>
<li>支持遮罩、关闭按钮、<code>Esc</code> 和页面跳转后自动关闭</li>
<li>打开面板时锁定页面滚动，关闭后恢复原来的滚动状态</li>
</ul>
<h2 id="阅读与交互优化">阅读与交互优化</h2>
<h3 id="目录面板">目录面板</h3>
<ul>
<li>移除底部与音乐播放器并列的 <code>#toc-fab</code> 悬浮目录按钮</li>
<li>目录入口迁移到移动端顶栏</li>
<li>目录面板改为从顶栏目录按钮下方展开，动画起点位于右上角目录按钮附近</li>
<li>保留目录层级高亮、内部滚动、遮罩和滚动锁定</li>
</ul>
<h3 id="音乐播放器">音乐播放器</h3>
<ul>
<li>移动端未播放时收起为左下角圆形播放按钮，减少底部占位</li>
<li>播放后展开为完整音乐胶囊，暂停后重新收起</li>
<li>打开目录、菜单、中控台或搜索时，音乐播放器降到遮罩下方并暂时不可点击</li>
<li>桌面端音乐播放器的原有尺寸和交互不变</li>
</ul>
<h3 id="中控台">中控台</h3>
<ul>
<li>移动端不再隐藏中控台内容卡片</li>
<li>手机端以纵向可滚动布局展示最近评论、标签、归档和操作按钮</li>
<li>内容与桌面端使用同一套数据和模板</li>
<li>浅色模式下中控台按钮默认与搜索、随机文章使用普通按钮色，只有打开后才进入强调色状态</li>
</ul>
<h3 id="回到顶部">回到顶部</h3>
<ul>
<li>移动端滚动后显示回到顶部按钮</li>
<li>恢复与桌面端一致的滚动百分比显示</li>
<li>页面位于顶部时按钮自动收起</li>
</ul>
<h2 id="atom-订阅">Atom 订阅</h2>
<h3 id="atom-10-输出">Atom 1.0 输出</h3>
<ul>
<li>订阅输出从 RSS 文件名方案切换为真正的 Atom 1.0</li>
<li>首页、栏目、分类与标签订阅统一生成 <code>atom.xml</code></li>
<li>Atom 文档包含标题、描述、作者、发布时间、更新时间、摘要、正文、分类、系列和标签</li>
<li>带有文章封面的条目输出 Atom enclosure 链接</li>
<li>更新页头 alternate 链接、页脚订阅入口、友链申请表单中的 feed 地址和文章订阅配置</li>
</ul>
<h3 id="浏览器订阅页">浏览器订阅页</h3>
<ul>
<li>新增 <code>static/atom.xsl</code>，参考 <code>hexo-pretty-feed</code> 和 LiuShen 的 Atom 页面设计</li>
<li>直接打开 <code>atom.xml</code> 时显示纸张手账风格的订阅页面，而不是原始 XML</li>
<li>页面包含站点标题、描述、文章数量、最近更新时间和订阅地址复制按钮</li>
<li>文章卡片显示封面、标题、发布时间、更新时间、摘要、分类、系列和标签</li>
<li>有封面的文章使用图文卡片布局，没有封面的文章自动使用无图布局</li>
<li>适配移动端窄屏，并支持系统浅色 / 深色模式</li>
<li>订阅器读取的仍是标准 Atom 数据，不影响 Feedly、Inoreader 等阅读器使用</li>
</ul>
<h3 id="摘要清理">摘要清理</h3>
<ul>
<li>Atom 卡片摘要改为纯文本输出，移除正文中的 HTML 标签</li>
<li>摘要源头限制为约 220 个字符，避免代码块、标题和长正文撑高卡片</li>
<li>XSL 页面在桌面端和移动端继续使用行数限制，保证文章列表紧凑</li>
<li>Atom 的 <code>content</code> 仍保留完整 HTML，订阅器需要阅读全文时不受影响</li>
</ul>
<h2 id="友链与站点信息">友链与站点信息</h2>
<ul>
<li>友链申请页面的本站信息新增站点快照地址：
<code>https://openlist.081531.xyz/d/lsky-git/lsky/2026/09/26/6ab767e613867.webp</code></li>
<li>友链申请页面的本站信息新增 Atom feed 地址：
<code>https://blog.518339.xyz/atom.xml</code></li>
<li>站点页脚和友链申请信息统一使用新的 Atom 订阅地址</li>
</ul>
<h2 id="兼容性">兼容性</h2>
<ul>
<li>桌面端原有导航、按钮、侧栏和播放器行为保持不变</li>
<li>Atom 数据格式与浏览器 XSL 展示层分离，XSL 不可用时仍可直接读取标准 XML</li>
<li>移动端面板支持键盘 <code>Esc</code>、可见焦点和减少动态效果设置</li>
<li>所有样式继续使用项目现有纸张主题变量与响应式布局，不引入固定页面宽度</li>
</ul>
]]></content></entry><entry><title>全站正文换用朱雀仿宋</title><link href="" rel="alternate"/><id/><published>2026-09-26T21:30:00+08:00</published><updated>2026-09-26T21:30:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">排版 正文主字体改为朱雀仿宋 全站正文、标题、导航、标签与卡片文字统一使用朱雀仿宋，此前为思源黑体系；等宽字体（代码、行内代码）保持不变 这是开源字体朱雀仿宋的「technical preview」版，遵循 OFL-1.1，由 TrionesType 设计 兜底顺序为「朱雀仿宋 → 思源宋体 → 宋体 → 思源黑体」，字体未就位时仍回落到衬线字体，与纸张手账的观感一致 该字体仅提供常规字重，标题与加粗由浏览器合成，字号与行高维持原有层级 …</summary><content type="html"><![CDATA[<h2 id="排版">排版</h2>
<h3 id="正文主字体改为朱雀仿宋">正文主字体改为朱雀仿宋</h3>
<ul>
<li>全站正文、标题、导航、标签与卡片文字统一使用朱雀仿宋，此前为思源黑体系；等宽字体（代码、行内代码）保持不变</li>
<li>这是开源字体朱雀仿宋的「technical preview」版，遵循 OFL-1.1，由 TrionesType 设计</li>
<li>兜底顺序为「朱雀仿宋 → 思源宋体 → 宋体 → 思源黑体」，字体未就位时仍回落到衬线字体，与纸张手账的观感一致</li>
<li>该字体仅提供常规字重，标题与加粗由浏览器合成，字号与行高维持原有层级</li>
</ul>
<h3 id="分片字体按需下载">分片字体按需下载</h3>
<ul>
<li>字体表由 cn-font-split 按字符集切成 52 个片段，每片用 <code>unicode-range</code> 声明覆盖范围，浏览器只下载当前页面真正用到的分片</li>
<li>实测：首页加载 17 片、文章页 23 片、关于页 22 片、中转页 11 片，而不是一次性下载整包字体</li>
<li>字体描述文件托管在 CDN 上并锁定 commit，避免上游更新导致样式漂移；同时对字体域名做 DNS 与连接预热</li>
<li>字体片段使用 <code>font-display: swap</code>，字体到位前先用兜底字体渲染，不会出现空白文字</li>
</ul>
<h2 id="功能优化">功能优化</h2>
<h3 id="字体族收归站点配置">字体族收归站点配置</h3>
<ul>
<li>新增主题配置项 <code>params.solitude.font.webfont_css</code>，用于声明网络字体的 CSS 地址，留空则只用系统字体</li>
<li>字体族与网络字体地址都集中在 <code>hugo.yaml</code> 的 <code>params.solitude.font</code> 下，改字体只需动这一处，无需再改样式表</li>
<li>纸张主题的字体令牌改为引用主题配置注入的变量，样式层只负责消费、不再重复定义字体族</li>
</ul>
<h3 id="外链中转页字体缺失">外链中转页字体缺失</h3>
<ul>
<li>中转页是独立的轻量骨架，此前不走站点的页面头部注入，导致这一页仍然使用兜底字体，与全站不一致</li>
<li>网络字体的加载移到两个骨架共用的样式入口，中转页现在与其它页面使用同一套字体</li>
</ul>
<h2 id="兼容性">兼容性</h2>
<ul>
<li>字体加载失败时自动回落到系统衬线字体，页面结构、字号与行高不受影响</li>
<li>字体片段跨域下载，字体文件的加载失败被静默吞掉，不会阻塞页面渲染</li>
<li>等宽字体族同样由站点配置提供，代码块的字体与改动前一致</li>
</ul>
]]></content></entry><entry><title>站外链接中转页与纸张细节修复</title><link href="" rel="alternate"/><id/><published>2026-09-25T09:30:00+08:00</published><updated>2026-09-25T09:30:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">新增功能 站外链接安全中转页 参考 hexo-safego 的思路实现，开关、跳转路径、倒计时、覆盖容器、白名单与全部文案都由站点配置控制 覆盖范围：文章正文、友链页顶部横幅、友链列表；命中站外链接后在运行时改写成中转页地址 改写后的链接标记为不参与站内无刷新路由，确保中转页按整页加载（否则页头脚本会被丢弃、页面失效） 中转页为独立的轻量骨架，不加载导航、侧栏、音乐播放器与评论区 车票造型：顶部斜贴胶带、票根与票面之间的虚线撕票线、两侧 …</summary><content type="html"><![CDATA[<h2 id="新增功能">新增功能</h2>
<h3 id="站外链接安全中转页">站外链接安全中转页</h3>
<ul>
<li>参考 hexo-safego 的思路实现，开关、跳转路径、倒计时、覆盖容器、白名单与全部文案都由站点配置控制</li>
<li>覆盖范围：文章正文、友链页顶部横幅、友链列表；命中站外链接后在运行时改写成中转页地址</li>
<li>改写后的链接标记为不参与站内无刷新路由，确保中转页按整页加载（否则页头脚本会被丢弃、页面失效）</li>
<li>中转页为独立的轻量骨架，不加载导航、侧栏、音乐播放器与评论区</li>
<li>车票造型：顶部斜贴胶带、票根与票面之间的虚线撕票线、两侧半圆打孔、票面展示目标站点域名与完整地址</li>
<li>交互：倒计时自动前往、手动「继续前往」、一键复制目标地址；鼠标悬停或键盘聚焦即暂停倒计时，避免被强行送走</li>
<li>「返回上一页」回到点击外链的位置：站外链接多数在新标签中打开，此时浏览历史只剩一步，因此改写时额外记录来源页，无历史可退时回到该页而不是首页；来源地址做同站校验，防止被构造成任意外部地址</li>
<li>本站与自有服务域名在放行名单内，不触发中转</li>
<li>中转页不进站点地图、订阅源与站内搜索索引，并标记为不收录、不追踪来源</li>
</ul>
<h2 id="问题修复">问题修复</h2>
<h3 id="移动端导航栏横向溢出">移动端导航栏横向溢出</h3>
<ul>
<li>窄屏下导航按被撑大的宽度居中，左右两边被裁切；根因是纸张样式无条件声明的定位方式覆盖了主题在窄屏把目录卡片改为浮层的规则，导致侧栏残留宽度把整行撑开</li>
<li>定位上下文按断点拆分，窄屏下排除目录卡片并清理其浮层残留状态</li>
<li>10 路由 × 9 断点全部通过，页面宽度不再溢出</li>
</ul>
<h3 id="首页分类栏整屏长条">首页分类栏整屏长条</h3>
<ul>
<li>顶部出现一条横贯整屏的纸张长条，实际上是样式覆盖凭空造出来的，主题基线本身是透明的</li>
<li>统一去掉分类栏容器的纸面、边框、圆角与阴影，只保留独立的纸张按钮</li>
<li>分类归档页的吸顶分类栏依赖实底遮挡滚动内容，单独保留，不受影响</li>
</ul>
<h3 id="侧栏抽屉配色">侧栏抽屉配色</h3>
<ul>
<li>深色模式下拉开侧栏是一整片近黑面板，与页面底色割裂</li>
<li>抽屉面板改为不透明纸张底并关闭背景模糊，内部各小块改用次级纸色以保持层次</li>
<li>深色下显示模式开关的橙色底改为纸张强调色，悬停文字同步调整</li>
</ul>
<h3 id="深色模式下实心按钮文字发糊">深色模式下实心按钮文字发糊</h3>
<ul>
<li>实心强调底上的文字使用了更亮的强调色，深色下对比度仅 1.28:1，浅色同样撞色</li>
<li>前景改用深墨色语义令牌，一处生效全站，深色提升到 8.30:1、浅色 4.25:1</li>
</ul>
<h3 id="关于页为ta充电闪电图标">关于页「为TA充电」闪电图标</h3>
<ul>
<li>原字符在默认字体下渲染成一条细折线，与参考实现的实心闪电观感不一致</li>
<li>改为纯 CSS 三角形拼出的实心闪电，几何按半宽等比推导，桌面与移动两档尺寸；形状、尺寸与文字间距均与参考实现对齐</li>
</ul>
<h3 id="中转页卡片贴顶">中转页卡片贴顶</h3>
<ul>
<li>中转页的垂直居中原本完全依赖外部样式表的解析时机，样式未就位时卡片会退回贴顶</li>
<li>在页面头部内联关键布局作为兜底，样式表同步为同一套写法；逐帧采样确认从首帧起即为居中，无贴顶瞬间</li>
</ul>
<h2 id="兼容性">兼容性</h2>
<ul>
<li>亮色与暗色分别定义纸张画布、表面、文字与强调色，组件内不硬编码明暗颜色</li>
<li>通过 <code>prefers-reduced-motion</code> 为减少动态效果的用户关闭入场动画</li>
<li>功能关闭时页面不产生额外请求，外链行为与改动前一致</li>
</ul>
]]></content></entry><entry><title>纸张手账主题重构与风格优化</title><link href="" rel="alternate"/><id/><published>2026-09-23T00:00:00+08:00</published><updated>2026-09-23T00:00:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">视觉重构 使用 Solitude 上游布局与功能结构作为基础，统一引入纸张手账设计令牌 参考 FlatPaper Demo 的纸张质感、正文排版和手账元素 引入暖纸色、网格纸、横向纸纹、轻阴影、胶带和不规则纸张边角 文章卡片、侧栏卡片、导航、代码块、归档和特殊页面统一使用纸张语义令牌 统一收敛全站纸张手账视觉，避免不同页面之间的风格割裂 导航与组件优化 将导航链接、站点名称、首页分类栏、标签栏和下拉子菜单从胶囊形改为矩形小圆角 将返回首 …</summary><content type="html"><![CDATA[<h2 id="视觉重构">视觉重构</h2>
<ul>
<li>使用 Solitude 上游布局与功能结构作为基础，统一引入纸张手账设计令牌</li>
<li>参考 FlatPaper Demo 的纸张质感、正文排版和手账元素</li>
<li>引入暖纸色、网格纸、横向纸纹、轻阴影、胶带和不规则纸张边角</li>
<li>文章卡片、侧栏卡片、导航、代码块、归档和特殊页面统一使用纸张语义令牌</li>
<li>统一收敛全站纸张手账视觉，避免不同页面之间的风格割裂</li>
</ul>
<h2 id="导航与组件优化">导航与组件优化</h2>
<ul>
<li>将导航链接、站点名称、首页分类栏、标签栏和下拉子菜单从胶囊形改为矩形小圆角</li>
<li>将返回首页菜单、滚动导航、页面标题和上一篇 / 下一篇分页统一为纸张卡片背景</li>
<li>将音乐播放器及其控制层改为矩形纸张组件，保留原有播放、进度和悬停交互</li>
<li>优化个人资料卡片的纸张装饰，修复卡片上方书签被父容器裁切的问题</li>
<li>保留文末打赏在桌面悬停、触屏点击和键盘聚焦下的展开行为</li>
</ul>
<h2 id="明暗模式适配">明暗模式适配</h2>
<ul>
<li>亮色与暗色分别定义纸张画布、表面、文字、边框和强调色，切换时不依赖硬编码覆盖</li>
<li>统一使用 <code>--paper-*</code> 与 <code>--efu-*</code> 语义变量，不在组件中硬编码明暗模式颜色</li>
<li>修复浅色模式下返回顶部、个人资料卡片和导航组件仍显示深色背景或白色文字的问题</li>
<li>修复深色模式下滚动导航、分页、音乐播放器和侧栏组件背景不一致的问题</li>
<li>参考 Solitude 原生导航状态链，覆盖中控台默认、悬停、聚焦和打开状态</li>
<li>修复文章页、音乐页中主题原生规则对中控台图标颜色的强制覆盖</li>
<li>浅色模式中控台图标使用强调色，深色模式使用纸张文字色，鼠标悬停后保持一致</li>
</ul>
<h2 id="动效与兼容性">动效与兼容性</h2>
<ul>
<li>通过 <code>prefers-reduced-motion</code> 为减少动态效果的用户关闭过渡动画</li>
<li>保留 Solitude 的响应式布局、PJAX、搜索、评论、音乐和菜单功能</li>
<li>未修改现有 API 地址、端口配置及第三方服务配置</li>
<li>Hugo 构建、首页、文章页和音乐页的明暗模式及中控台交互均完成验证</li>
</ul>
]]></content></entry><entry><title>文章 AI 摘要组件</title><link href="" rel="alternate"/><id/><published>2026-09-22T23:30:00+08:00</published><updated>2026-09-22T23:30:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">变更详情 摘要卡片 文章 front matter 存在 ai_summary 时，在正文前渲染摘要卡；没有该字段的文章不输出任何占位内容 卡片包含图标标题、右侧模型标签与摘要正文，配色、圆角与描边全部复用 --efu-* / --paper-* 主题令牌，明暗模式自适应 摘要默认带打字机效果，浏览器开启「减少动态效果」时直接显示全文 移动端与超窄屏下模型标签自动换行并截断，不产生横向滚动 模板只读取 …</summary><content type="html"><![CDATA[<h2 id="变更详情">变更详情</h2>
<h3 id="摘要卡片">摘要卡片</h3>
<ul>
<li>文章 front matter 存在 <code>ai_summary</code> 时，在正文前渲染摘要卡；没有该字段的文章不输出任何占位内容</li>
<li>卡片包含图标标题、右侧模型标签与摘要正文，配色、圆角与描边全部复用 <code>--efu-*</code> / <code>--paper-*</code> 主题令牌，明暗模式自适应</li>
<li>摘要默认带打字机效果，浏览器开启「减少动态效果」时直接显示全文</li>
<li>移动端与超窄屏下模型标签自动换行并截断，不产生横向滚动</li>
<li>模板只读取 <code>.Params.ai_summary</code> 与站点 <code>params.solitude.ai_summary</code> 配置，旧文章无需批量修改</li>
</ul>
<h3 id="摘要生成脚本">摘要生成脚本</h3>
<ul>
<li>新增 <code>scripts/generate-ai-summary.mjs</code>：遍历 <code>content/posts/</code> 下的 Markdown，把生成的摘要写回 front matter</li>
<li>使用 OpenAI 兼容的 Chat Completions 接口，默认对接 SiliconFlow 的 <code>Qwen/Qwen3-8B</code></li>
<li>发送前清理正文：剥离 front matter、代码块、图片与链接地址、HTML 标签与 Markdown 符号，并按输入上限截断（默认 12000 字符）</li>
<li>默认跳过已有摘要的文章，<code>--force</code> 或 <code>AI_SUMMARY_COVER_ALL=true</code> 可覆盖；<code>--dry-run</code> 只列出待处理文章不写文件</li>
<li>支持并发数、处理间隔、自定义摘要字段与自定义提示词</li>
<li>Qwen3 默认开启的思考模式已固定关闭（<code>enable_thinking: false</code> 且 <code>stream: false</code>），直接返回普通 JSON 摘要</li>
<li>单篇请求失败只记录文件名与原因并继续后续文章，不会写入半截摘要；存在失败项时返回非零退出码</li>
</ul>
<h3 id="密钥与构建流程">密钥与构建流程</h3>
<ul>
<li>接口地址、密钥与模型名统一从博客根目录 <code>.env</code> 读取，提供 <code>.env.example</code> 示例并把 <code>.env</code> 加入 <code>.gitignore</code></li>
<li>终端已有的同名环境变量优先于 <code>.env</code>，可临时覆盖而不改动文件</li>
<li>摘要生成与站点构建保持分离：<code>hugo</code> 不会调用 AI 接口，需要更新摘要时先运行脚本再构建，发布过程可控</li>
</ul>
<h3 id="配套适配">配套适配</h3>
<ul>
<li>摘要组件接入主题 PJAX 生命周期：切页前清理打字机定时器，切页后重新初始化，避免定时器泄漏与半截文本残留</li>
<li>新增一篇接入说明文章，记录组件位置、配置项、脚本用法与常见错误排查</li>
</ul>
]]></content></entry><entry><title>渲染与交互修复</title><link href="" rel="alternate"/><id/><published>2026-09-22T20:30:00+08:00</published><updated>2026-09-22T20:30:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">变更详情 问题修复 修复侧栏目录在无目录卡片的页面上抛出的 null.querySelector 崩溃：容器改为可选链读取并在缺失时提前返回 修复暗色模式下侧栏按钮 hover 状态不可见：待机态规则限定在 :not(:hover):not(:focus-visible)，避免与 hover 规则同特异度互相覆盖 修复站外封面图取色失败：跨域图片不再以 crossOrigin=&amp;amp;quot;anonymous&amp;amp;quot; 读取像素，直接回 …</summary><content type="html"><![CDATA[<h2 id="变更详情">变更详情</h2>
<h3 id="问题修复">问题修复</h3>
<ul>
<li>修复侧栏目录在无目录卡片的页面上抛出的 <code>null.querySelector</code> 崩溃：容器改为可选链读取并在缺失时提前返回</li>
<li>修复暗色模式下侧栏按钮 hover 状态不可见：待机态规则限定在 <code>:not(:hover):not(:focus-visible)</code>，避免与 hover 规则同特异度互相覆盖</li>
<li>修复站外封面图取色失败：跨域图片不再以 <code>crossOrigin=&quot;anonymous&quot;</code> 读取像素，直接回退主题色，控制台不再反复输出取色错误</li>
<li>修复 PJAX 切页后 <code>Failed to fetch</code> 未捕获异常：评论初始化与周边增强全部补上失败兜底，页面刷新逻辑用 try/catch 包裹，单个可选模块出错不再阻塞新页面渲染</li>
<li>主动取消（<code>AbortError</code>）等预期场景降级为调试日志，正常浏览不再产生红色报错</li>
</ul>
<h3 id="行内代码与代码块排版">行内代码与代码块排版</h3>
<ul>
<li>行内代码改为轻量标签样式：缩小字号与内边距，改用次级背景配卡片描边，去掉此前厚重的阴影与描边</li>
<li>跨行换行的行内代码使用 <code>box-decoration-break: clone</code>，两端圆角保持一致</li>
<li>代码块去掉整圈细边框，改为左侧竖条强调加纸张色偏移阴影；图注、工具条与行号槽的横线一并移除</li>
<li>行内代码与代码块的选择器相互隔离（<code>:not(pre) &gt; code</code>），修复行内样式污染代码块的问题</li>
</ul>
]]></content></entry><entry><title>隐私合规与页面体验重做</title><link href="" rel="alternate"/><id/><published>2026-09-22T16:30:00+08:00</published><updated>2026-09-22T16:30:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">变更详情 Cookie 同意弹窗 页面底部新增 Cookie 使用说明弹窗，提供「接受全部」与「拒绝非必要」两个选择 选择结果写入 solitude_cookie_consent Cookie，携带版本号、一年有效期、Path=/ 与 SameSite=Lax，HTTPS 下自动附加 Secure 已做出选择的访客不再重复弹出；版本号变化时旧选择自动失效并重新询问 弹窗使用 role=&amp;amp;quot;dialog&amp;amp;quot; 与 …</summary><content type="html"><![CDATA[<h2 id="变更详情">变更详情</h2>
<h3 id="cookie-同意弹窗">Cookie 同意弹窗</h3>
<ul>
<li>页面底部新增 Cookie 使用说明弹窗，提供「接受全部」与「拒绝非必要」两个选择</li>
<li>选择结果写入 <code>solitude_cookie_consent</code> Cookie，携带版本号、一年有效期、<code>Path=/</code> 与 <code>SameSite=Lax</code>，HTTPS 下自动附加 <code>Secure</code></li>
<li>已做出选择的访客不再重复弹出；版本号变化时旧选择自动失效并重新询问</li>
<li>弹窗使用 <code>role=&quot;dialog&quot;</code> 与 <code>aria-labelledby</code> / <code>aria-describedby</code>，隐藏态走 <code>hidden</code> 属性而非仅靠 CSS</li>
<li>文案接入多语言（简体 / 繁体 / 英文 / 西班牙文），配色与圆角全部复用主题令牌，明暗模式自适应</li>
</ul>
<h3 id="隐私说明页重构">隐私说明页重构</h3>
<ul>
<li><code>/privacy/</code> 重写为结构化说明页，按「说明范围 / 处理哪些信息 / Cookie 与本地存储 / 访问统计 / 第三方资源 / 评论与主动提交 / 你的选择与权利 / 联系站长」分章</li>
<li>页首新增状态摘要卡：站点统计与评论服务是否启用，直接读取配置文件中的 Umami 与评论设置，不写死</li>
<li>左侧锚点导航跟随滚动高亮，移动端可折叠，长页面也能快速跳转</li>
<li>明确声明不做用户画像、不出售数据，并列出可行使的权利与撤回方式</li>
<li>隐私页自身关闭侧栏与目录，避免与锚点导航重复</li>
</ul>
<h3 id="404-页面重做">404 页面重做</h3>
<ul>
<li>404 页改为纯 CSS / 纯文字实现，不再依赖静态图片素材</li>
<li>新增主标题、说明文案、品牌贴纸与「返回首页 / 返回上一页」双按钮，返回上一页在无历史记录时回退到首页</li>
<li>页面底部展示最近 3 篇文章推荐，是否展示由配置项控制</li>
<li>标题、文案、品牌标记与推荐开关均在站点配置中可调，未配置时回退多语言文案</li>
</ul>
<h3 id="交互自由">交互自由</h3>
<ul>
<li>新增按住 <code>Ctrl</code> + 右键使用浏览器原生菜单的开关，不再拦截事件</li>
<li>首次触发时用提示条说明一次该操作方式，之后不再打扰</li>
<li>未按住修饰键时保持原有的自定义右键菜单行为不变</li>
</ul>
]]></content></entry><entry><title>AI 日报近 7 日聚合</title><link href="" rel="alternate"/><id/><published>2026-09-21T18:00:00+08:00</published><updated>2026-09-21T18:00:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">变更详情 AI 日报聚合展示 AI 日报页面从单日展示升级为最近 7 日聚合，按日期从新到旧分组呈现有内容的日报 自动跳过 news 为空的日期，连续几天没有新资讯时仍能展示最近可用的日报内容 每个日期分组显示日报日期、新闻数量、来源状态与新闻卡片 支持展开与收起日期分组，保留桌面端和移动端的可读布局 页面顶部新增聚合统计：最近查询范围、已找到的日报期数与最近更新时间 数据请求与缓存 按站点配置的 Asia/Shanghai 时区计算最 …</summary><content type="html"><![CDATA[<h2 id="变更详情">变更详情</h2>
<h3 id="ai-日报聚合展示">AI 日报聚合展示</h3>
<ul>
<li>AI 日报页面从单日展示升级为最近 7 日聚合，按日期从新到旧分组呈现有内容的日报</li>
<li>自动跳过 <code>news</code> 为空的日期，连续几天没有新资讯时仍能展示最近可用的日报内容</li>
<li>每个日期分组显示日报日期、新闻数量、来源状态与新闻卡片</li>
<li>支持展开与收起日期分组，保留桌面端和移动端的可读布局</li>
<li>页面顶部新增聚合统计：最近查询范围、已找到的日报期数与最近更新时间</li>
</ul>
<h3 id="数据请求与缓存">数据请求与缓存</h3>
<ul>
<li>按站点配置的 <code>Asia/Shanghai</code> 时区计算最近 7 个自然日</li>
<li>使用接口的 <code>date</code> 参数逐日请求数据，并发请求减少页面等待时间</li>
<li>单个日期请求失败不会阻断其他日期的展示</li>
<li>新增按日期保存的 v2 本地缓存，只缓存有内容的日报，避免空数据污染历史内容</li>
<li>请求失败或接口返回空日报时，优先回退对应日期的非空缓存，并标注缓存状态</li>
<li>校验接口返回日期，避免将错误日期的数据伪装成请求日期</li>
<li>缓存损坏、过期或版本不匹配时安全降级，不影响页面初始化</li>
</ul>
<h3 id="空数据与异常状态">空数据与异常状态</h3>
<ul>
<li>最近 7 天全部没有资讯时，显示准确的“最近 N 天暂无 AI 日报”状态</li>
<li>部分日期请求失败时保留其他日期内容，并提示仍有日期未能更新</li>
<li>无可用缓存且请求失败时提供清晰的错误说明与重试入口</li>
<li>兼容新闻条目缺少摘要、来源、日期或原文链接的情况</li>
</ul>
<h3 id="主题与体验">主题与体验</h3>
<ul>
<li>延续 Solitude 纸张手账视觉体系，复用主题明暗模式与 CSS 设计令牌</li>
<li>日期分组、统计信息、状态提示和新闻卡片适配移动端换行与窄屏布局</li>
<li>远程内容继续使用安全文本渲染，原文链接限制为 HTTP/HTTPS 并启用安全外链属性</li>
<li>支持 PJAX 生命周期清理、请求中止、键盘焦点和 <code>prefers-reduced-motion</code> 降级</li>
</ul>
]]></content></entry><entry><title>赞助申请工作流与说说多图体验优化</title><link href="" rel="alternate"/><id/><published>2026-09-19T18:00:00+08:00</published><updated>2026-09-19T18:00:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">变更详情 赞助申请与审核工作流 新增赞助申请 Issue 表单（sponsor.yml）：填写名单名称、赞助金额、赞助日期与支付截图链接四个必填项，标题带 [赞助申请] 前缀并默认打上 赞助申请 标签 表单内明确隐私边界：支付截图仅用于站长审核核对，不写入公开名单 新增独立的赞助审核工作流与数据处理脚本：仅在议题「已关闭 + 含 赞助申请 标签 + 不含 赞助失败 标签」时，把名单名称、金额、日期追加到 /about/ 的赞赏名单末尾 …</summary><content type="html"><![CDATA[<h2 id="变更详情">变更详情</h2>
<h3 id="赞助申请与审核工作流">赞助申请与审核工作流</h3>
<ul>
<li>新增赞助申请 Issue 表单（sponsor.yml）：填写名单名称、赞助金额、赞助日期与支付截图链接四个必填项，标题带 <code>[赞助申请]</code> 前缀并默认打上 <code>赞助申请</code> 标签</li>
<li>表单内明确隐私边界：支付截图仅用于站长审核核对，不写入公开名单</li>
<li>新增独立的赞助审核工作流与数据处理脚本：仅在议题「已关闭 + 含 <code>赞助申请</code> 标签 + 不含 <code>赞助失败</code> 标签」时，把名单名称、金额、日期追加到 <code>/about/</code> 的赞赏名单末尾</li>
<li><code>赞助失败</code> 标签优先阻断：存在该标签时不修改任何名单数据，并回写未通过的说明评论</li>
<li>以议题编号做幂等判断，同一议题重复触发不会重复追加；仅在数据真正变化时才提交变更</li>
<li>审核通过后自动回写成功评论并保持议题关闭；未关闭的议题只提示「审核待生效」，不改动数据</li>
</ul>
<h3 id="关于页赞助组件">关于页赞助组件</h3>
<ul>
<li>新增手机端通栏「为 TA 充电」按钮：坐在赞助声明与赞赏名单之间，点击弹出微信 / 支付宝收款码</li>
<li>桌面端保持原有的闪电按钮与连接动画不变；手机端按钮文案由独立配置项控制，未配置时自动回退</li>
<li>赞助联系渠道由邮箱改为 GitHub Issue：声明文案改为「通过 GitHub Issue 提交申请」并新标签页打开，原邮箱保留为回退</li>
</ul>
<h3 id="说说页多图体验">说说页多图体验</h3>
<ul>
<li>多图说说改为智能网格布局：单图通栏限高、两图两列、三图及以上统一三列方形缩略</li>
<li>超过 9 张自动折叠，末格叠加「+N」遮罩，点击可原地展开全部图片与收起，避免长图堆叠把卡片撑高</li>
<li>正文内联图片统一抽离到网格并去重，图片失效时显示占位，点击缩略图进灯箱浏览原图</li>
<li>兼容多种图片来源：外链、协议相对地址、<code>data:</code> / <code>blob:</code> 内联图均正常解析</li>
</ul>
<h3 id="说说页标签去重">说说页标签去重</h3>
<ul>
<li>Memos 的标签由正文 <code>#标签</code> 解析而来，此前正文文本与卡片底部标签 chip 会重复显示</li>
<li>现在自动隐藏正文「开头或结尾」与标签字段匹配的 <code>#标签</code> 文本，正文中的普通井号表达不受影响</li>
<li>卡片底部的可点击标签保持不变，标签筛选与分页行为不受影响；仅对 Memos 数据源生效</li>
</ul>
]]></content></entry><entry><title>统计页接入 Umami 站访数据</title><link href="" rel="alternate"/><id/><published>2026-09-16T16:00:00+08:00</published><updated>2026-09-16T16:00:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">变更详情 统计页新增「站访数据」卡片：展示最近 30 天的总访问量、独立访客、浏览量与跳出率 各指标与上一周期对比并显示升降差异，无对比数据时自动隐藏差异标注 新增访问趋势柱状图与热门页面 Top 8，实时反映站内流量走向 数据来自 Umami 公开分享接口（两步认证），无需自建代理或后端 接口异常、无数据与请求超时（8 秒）均有兜底状态，页面切换时自动中止请求 卡片配色与布局全部走主题 token，明暗双模式与移动端单列自适应 统计区 …</summary><content type="html"><![CDATA[<h2 id="变更详情">变更详情</h2>
<ul>
<li>统计页新增「站访数据」卡片：展示最近 30 天的总访问量、独立访客、浏览量与跳出率</li>
<li>各指标与上一周期对比并显示升降差异，无对比数据时自动隐藏差异标注</li>
<li>新增访问趋势柱状图与热门页面 Top 8，实时反映站内流量走向</li>
<li>数据来自 Umami 公开分享接口（两步认证），无需自建代理或后端</li>
<li>接口异常、无数据与请求超时（8 秒）均有兜底状态，页面切换时自动中止请求</li>
<li>卡片配色与布局全部走主题 token，明暗双模式与移动端单列自适应</li>
<li>统计区间按访客浏览器本地时区换算，跨时区展示口径一致</li>
</ul>
]]></content></entry><entry><title>侧栏时钟与访客欢迎卡片</title><link href="" rel="alternate"/><id/><published>2026-09-15T14:00:00+08:00</published><updated>2026-09-15T14:00:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">变更详情 新增侧栏时钟卡片：走时按指定时区刷新，叠加和风天气实时温度与天气文案 天气结果走 localStorage 缓存（默认 30 分钟）并静默降级，接口异常时只保留纯时钟 新增侧栏访客欢迎卡片：按访客 IP 解析省市，展示欢迎语、所在地与到站长的直线距离 定位接口多级降级（ip.sb 主源 → ipify / icanhazip 取 IP → ipapi.co 查位置），全失败时回退兜底欢迎语 地区文案支持按「省-市」「市」「省 …</summary><content type="html"><![CDATA[<h2 id="变更详情">变更详情</h2>
<ul>
<li>新增侧栏时钟卡片：走时按指定时区刷新，叠加和风天气实时温度与天气文案</li>
<li>天气结果走 localStorage 缓存（默认 30 分钟）并静默降级，接口异常时只保留纯时钟</li>
<li>新增侧栏访客欢迎卡片：按访客 IP 解析省市，展示欢迎语、所在地与到站长的直线距离</li>
<li>定位接口多级降级（ip.sb 主源 → ipify / icanhazip 取 IP → ipapi.co 查位置），全失败时回退兜底欢迎语</li>
<li>地区文案支持按「省-市」「市」「省」「国家」逐级回退，命中后展示对应 Tip</li>
<li>定位结果本地缓存 24 小时，页面不输出完整 IP，两次外部请求均无需 Key</li>
<li>两张卡片的配色与尺寸全部走主题 token，明暗双模式与多端自适应</li>
<li>画廊视频移除自定义播放与全屏按钮，保留原生视频控件，避免多余控件元素遮挡画面</li>
</ul>
]]></content></entry><entry><title>更新日志页面上线</title><link href="" rel="alternate"/><id/><published>2026-09-15T10:00:00+08:00</published><updated>2026-09-15T10:00:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">变更详情 新增 /changelog/ 页面：按时间倒序展示版本动态，每条含类型徽标、版本号与摘要 支持全文搜索与类型筛选（功能新增 / 功能优化 / 问题修复） 最新版本默认展开，其余可点击展开查看变更详情 页面配色全走主题 token，明暗双模式自适应，纸张手账风格</summary><content type="html"><![CDATA[<h2 id="变更详情">变更详情</h2>
<ul>
<li>新增 /changelog/ 页面：按时间倒序展示版本动态，每条含类型徽标、版本号与摘要</li>
<li>支持全文搜索与类型筛选（功能新增 / 功能优化 / 问题修复）</li>
<li>最新版本默认展开，其余可点击展开查看变更详情</li>
<li>页面配色全走主题 token，明暗双模式自适应，纸张手账风格</li>
</ul>
]]></content></entry><entry><title>导航面板统一与中控台双主题</title><link href="" rel="alternate"/><id/><published>2026-09-14T16:30:00+08:00</published><updated>2026-09-14T16:30:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">变更详情 固定导航的站名、右侧按钮组、页面名三块面板统一为同一种底色 中控台（#console）卡片与按钮补齐暗色分支，不再混出三种面 暗色下以暖棕描边替代不可见的黑色阴影，分隔效果与亮色等价</summary><content type="html"><![CDATA[<h2 id="变更详情">变更详情</h2>
<ul>
<li>固定导航的站名、右侧按钮组、页面名三块面板统一为同一种底色</li>
<li>中控台（#console）卡片与按钮补齐暗色分支，不再混出三种面</li>
<li>暗色下以暖棕描边替代不可见的黑色阴影，分隔效果与亮色等价</li>
</ul>
]]></content></entry><entry><title>弹幕留言板页面上线</title><link href="" rel="alternate"/><id/><published>2026-09-14T10:00:00+08:00</published><updated>2026-09-14T10:00:00+08:00</updated><author><name>克喵Moretti</name><email>mcy@kemiaosw.top</email></author><summary type="text">变更详情 接入 Artalk 最新评论接口，留言以弹幕形式飘过页面 弹幕开关按钮接入纸张控件规范（卡面底 + 细描边 + 轻阴影，hover 强调色底） 弹幕条目文字与描边在明暗两种主题下均可读</summary><content type="html"><![CDATA[<h2 id="变更详情">变更详情</h2>
<ul>
<li>接入 Artalk 最新评论接口，留言以弹幕形式飘过页面</li>
<li>弹幕开关按钮接入纸张控件规范（卡面底 + 细描边 + 轻阴影，hover 强调色底）</li>
<li>弹幕条目文字与描边在明暗两种主题下均可读</li>
</ul>
]]></content></entry></feed>