<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://clina.top/</id>
  <title type="text">Clina 的笔记本</title>
  <subtitle type="text">¡Hola!</subtitle>
  <updated>2026-10-14T00:00:00.000Z</updated>
  <author><name>ClinaSaMa</name></author>
  <link rel="alternate" href="https://clina.top/"/>
  <link rel="self" href="https://clina.top/atom.xml"/>
  <generator uri="https://github.com/CuteLeaf/Firefly">Firefly v6.16.8</generator>
    <entry>
      <id>https://clina.top/posts/firefly/</id>
      <title type="text">Firefly 一款清新美观的 Astro 博客主题模板</title>
      <published>1970-01-02T00:00:00.000Z</published>
      <updated>1970-01-02T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/firefly/"/>
      <summary type="text">Firefly 是一款基于 Astro 框架和 Fuwari 模板开发的清新美观且现代化个人博客主题模板，专为技术爱好者和内容创作者设计。该主题融合了现代 Web 技术栈，提供了丰富的功能模块和高度可定制的界面，让您能够轻松打造出专业且美观的个人博客网站。</summary>
      <content type="html"><![CDATA[<section><h2>🌟 项目概述<a href="#-项目概述"><span>#</span></a></h2><p><strong>Firefly</strong> 是一款基于 Astro 框架和 Fuwari 模板开发的清新美观且现代化个人博客主题模板，专为技术爱好者和内容创作者设计。该主题融合了现代 Web 技术栈，提供了丰富的功能模块和高度可定制的界面，让您能够轻松打造出专业且美观的个人博客网站。</p><p><strong>🖥️在线预览： <a href="https://firefly.cuteleaf.cn/" target="_blank">Firefly - Demo site</a></strong></p><p><strong>📝Firefly使用文档： <a href="https://docs-firefly.cuteleaf.cn/" target="_blank">https://docs-firefly.cuteleaf.cn</a></strong></p><p><strong>⭐Firefly开源地址：<a href="https://github.com/CuteLeaf/Firefly" target="_blank">https://github.com/CuteLeaf/Firefly</a></strong></p><p><strong>⭐Fuwari开源地址：<a href="https://github.com/saicaca/fuwari" target="_blank">https://github.com/saicaca/fuwari</a></strong></p><a href="https://github.com/CuteLeaf/Firefly" target="_blank"><div><div><div><div></div><div>CuteLeaf</div></div><div>/</div><div>Firefly</div></div><div></div></div><div>🍀Firefly, fresh and aesthetic Astro blog theme template. </div><div><div>—</div><div>—</div><div>MIT</div><span>Astro</span></div></a><a href="https://github.com/saicaca/fuwari" target="_blank"><div><div><div><div></div><div>saicaca</div></div><div>/</div><div>fuwari</div></div><div></div></div><div>✨A static blog template built with Astro. </div><div><div>—</div><div>—</div><div>MIT</div><span>Astro</span></div></a><p></p><figure><img alt="Firefly" loading="lazy" width="2192" height="1233" src="/_astro/1.BQ4-k1kz_Z17x8Xz.webp" /><figcaption>Firefly</figcaption></figure><p></p></section>
<section><h2>🚀 技术架构<a href="#-技术架构"><span>#</span></a></h2><ul>
<li><strong>静态站点生成</strong>: 基于 Astro ，提供极快的加载速度和优秀的 SEO 优化</li>
<li><strong>TypeScript 支持</strong>: 完整的类型安全，提升开发体验和代码质量</li>
<li><strong>响应式设计</strong>: 使用 Tailwind CSS 构建，完美适配桌面端和移动端</li>
<li><strong>组件化开发</strong>: 支持 Astro、Svelte 组件，灵活可扩展</li>
</ul></section>
<section><h2>📖 配置说明<a href="#-配置说明"><span>#</span></a></h2><blockquote><p>📚 <strong>详细配置文档</strong>: 查看 <a href="https://docs-firefly.cuteleaf.cn/" target="_blank">Firefly 使用文档</a> 获取完整的配置指南</p></blockquote></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/guide/</id>
      <title type="text">Firefly 简单使用指南</title>
      <published>1970-01-02T00:00:00.000Z</published>
      <updated>1970-01-02T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/guide/"/>
      <summary type="text">如何使用 Firefly 博客模板。</summary>
      <content type="html"><![CDATA[<p>这个博客模板是基于 <a href="https://astro.build/" target="_blank">Astro</a> 构建的。对于本指南中未提及的内容，您可以在 <a href="https://docs.astro.build/" target="_blank">Astro 文档</a> 中找到答案。</p>
<section><h2>文章的 Front-matter<a href="#文章的-front-matter"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>---</span></div></div><div><div><div>2</div></div><div><span>title</span><span>: </span><span>我的第一篇博客文章</span></div></div><div><div><div>3</div></div><div><span>published</span><span>: </span><span>2023-09-09</span></div></div><div><div><div>4</div></div><div><span>description</span><span>: </span><span>这是我新 Astro 博客的第一篇文章。</span></div></div><div><div><div>5</div></div><div><span>image</span><span>: </span><span>./cover.jpg</span></div></div><div><div><div>6</div></div><div><span>tags</span><span>: [</span><span>前端</span><span>, </span><span>开发</span><span>]</span></div></div><div><div><div>7</div></div><div><span>category</span><span>: </span><span>前端开发</span></div></div><div><div><div>8</div></div><div><span>draft</span><span>: </span><span>false</span></div></div><div><div><div>9</div></div><div><span>---</span></div></div></code></pre><div><div></div><div></div></div></figure></div>

<table><thead><tr><th>属性</th><th>描述</th></tr></thead><tbody><tr><td><code>title</code></td><td>文章标题。</td></tr><tr><td><code>published</code></td><td>文章发布日期。</td></tr><tr><td><code>updated</code></td><td>文章更新日期。如果未设置，将默认使用发布日期。</td></tr><tr><td><code>pinned</code></td><td>是否将此文章置顶在文章列表顶部。</td></tr><tr><td><code>description</code></td><td>文章的简短描述。显示在首页上。</td></tr><tr><td><code>image</code></td><td>文章封面图片路径。<br />1. 以 <code>http://</code> 或 <code>https://</code> 开头：使用网络图片<br />2. 以 <code>/</code> 开头：<code>public</code> 目录中的图片<br />3. 不带任何前缀：相对于 markdown 文件的路径</td></tr><tr><td><code>tags</code></td><td>文章标签。</td></tr><tr><td><code>category</code></td><td>文章分类。</td></tr><tr><td><code>lang</code></td><td>文章语言代码（如 <code>zh-CN</code>）。仅当文章语言与站点默认语言不同时设置。</td></tr><tr><td><code>licenseName</code></td><td>文章内容的许可证名称。</td></tr><tr><td><code>licenseUrl</code></td><td>文章内容的许可证链接。</td></tr><tr><td><code>author</code></td><td>文章作者。</td></tr><tr><td><code>sourceLink</code></td><td>文章内容的来源链接或参考。</td></tr><tr><td><code>draft</code></td><td>如果这篇文章仍是草稿，则不会显示。</td></tr><tr><td><code>comment</code></td><td>是否启用此文章的评论功能。默认为 <code>true</code>。</td></tr><tr><td><code>slug</code></td><td>自定义文章 URL 路径。如果不设置，将使用文件名作为 URL。</td></tr><tr><td><code>password</code></td><td>文章密码。设置后文章内容将被 AES-256-GCM 加密，访客需输入密码才能查看。</td></tr><tr><td><code>passwordHint</code></td><td>密码提示。显示在密码输入框上方，帮助访客回忆密码，也可以不加。</td></tr></tbody></table></section>
<section><h2>文章文件的放置位置<a href="#文章文件的放置位置"><span>#</span></a></h2><p>您的文章文件应放置在 <code>src/content/posts/</code> 目录中。您也可以创建子目录来更好地组织您的文章和资源。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>src/content/posts/</span></div></div><div><div><div>2</div></div><div><span>├── post-1.md</span></div></div><div><div><div>3</div></div><div><span>└── post-2/</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>├── cover.png</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>└── index.md</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>自定义文章 URL (Slug)<a href="#自定义文章-url-slug"><span>#</span></a></h2><section><h3>什么是 Slug？<a href="#什么是-slug"><span>#</span></a></h3><p>Slug 是文章 URL 路径的自定义部分。如果不设置 slug，系统将使用文件名作为 URL。</p></section><section><h3>Slug 使用示例<a href="#slug-使用示例"><span>#</span></a></h3><section><h4>示例 1：使用文件名作为 URL<a href="#示例-1使用文件名作为-url"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>---</span></div></div><div><div><div>2</div></div><div><span>title</span><span>: </span><span>我的第一篇博客文章</span></div></div><div><div><div>3</div></div><div><span>published</span><span>: </span><span>2023-09-09</span></div></div><div><div><div>4</div></div><div><span>---</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>文件：<code>src/content/posts/my-first-blog-post.md</code></p><p>URL：<code>/posts/my-first-blog-post</code></p></section><section><h4>示例 2：自定义 Slug<a href="#示例-2自定义-slug"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>---</span></div></div><div><div><div>2</div></div><div><span>title</span><span>: </span><span>我的第一篇博客文章</span></div></div><div><div><div>3</div></div><div><span>published</span><span>: </span><span>2023-09-09</span></div></div><div><div><div>4</div></div><div><span>slug</span><span>: </span><span>hello-world</span></div></div><div><div><div>5</div></div><div><span>---</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>文件：<code>src/content/posts/my-first-blog-post.md</code></p><p>URL：<code>/posts/hello-world</code></p></section><section><h4>示例 3：其他语言文件名使用Slug<a href="#示例-3其他语言文件名使用slug"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>---</span></div></div><div><div><div>2</div></div><div><span>title</span><span>: </span><span>如何使用 Firefly 博客主题</span></div></div><div><div><div>3</div></div><div><span>published</span><span>: </span><span>2023-09-09</span></div></div><div><div><div>4</div></div><div><span>slug</span><span>: </span><span>how-to-use-firefly-blog-theme</span></div></div><div><div><div>5</div></div><div><span>---</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>文件：<code>src/content/posts/如何使用Firefly博客主题.md</code></p><p>URL：<code>/posts/how-to-use-firefly-blog-theme</code></p></section></section><section><h3>Slug 使用建议<a href="#slug-使用建议"><span>#</span></a></h3><ol>
<li><strong>使用英文和连字符</strong>：<code>my-awesome-post</code> 而不是 <code>my awesome post</code></li>
<li><strong>保持简洁</strong>：避免过长的 slug</li>
<li><strong>具有描述性</strong>：让 URL 能够反映文章内容</li>
<li><strong>避免特殊字符</strong>：只使用字母、数字和连字符</li>
<li><strong>保持一致性</strong>：在整个博客中使用相似的命名模式</li>
</ol></section><section><h3>注意事项<a href="#注意事项"><span>#</span></a></h3><ul>
<li>Slug 一旦设置并发布，建议不要随意更改，以免影响 SEO 和已存在的链接</li>
<li>如果多个文章使用相同的 slug，后面的文章会覆盖前面的</li>
<li>Slug 会自动转换为小写</li>
</ul></section></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/review-gpcl/</id>
      <title type="text">对 GPCL 项目的锐评</title>
      <published>2026-10-14T00:00:00.000Z</published>
      <updated>2026-10-14T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/review-gpcl/"/>
      <summary type="text">本文是对 GPCL 项目的锐评。如果你感觉不适，那么请离开这里。</summary>
      <content type="html"><![CDATA[<p>本文是对 GPCL 项目的锐评。</p>
<p>如果你感觉不适，请离开这里。</p>
<p>首先，我们打开 GPCL 项目的 GitHub 仓库首页。</p>
<p>一进来就看笑了，<code>GoodPlanCraftLaunCher</code> 是什么东西？“好计划启动器”？</p>
<p>提醒一下，PCL 全称叫 <code>Plain Craft Launcher</code>。</p>
<p>接下来，我们看看项目的 README。</p>
<p>‘轻量’</p>
<p>‘将启动器自身的资源占用控制在极低水平’</p>
<p>Electron 写的项目你给我谈轻量，占用低？？？</p>
<p>Chrome 可是吃内存大户，轻量到哪里去了？</p>
<p>‘Java 自动补全’</p>
<p><code>Java 自动补全</code> 是哪里来的说法？不应该叫 <code>Java 下载 / Java 运行环境补全</code> 吗？</p>
<p>然后就是 Releases 的命名问题。</p>
<p>这 Releases 都是什么逆天名字啊！</p>
<p>我还去看了一下开发者的 “Wiki” 页面（其实是他自己的 Wiki）</p>
<p>看完后，我只想说：</p>
<p>这一堆奖项有什么含金量？</p>
<p>一堆县级和校级的奖项，加起来含金量怕不是还没我 CSP J 第一轮二等奖的含金量高</p>
<p>然后就是提交记录的问题</p>
<p>这一堆提交记录毫无规范可言</p>
<p>基本就是想到什么写什么</p>
<p>评价未完待续</p>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/blog-author-9-questions-answer/</id>
      <title type="text">博客作者 9 问答卷</title>
      <published>2026-06-20T00:00:00.000Z</published>
      <updated>2026-06-20T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/blog-author-9-questions-answer/"/>
      <summary type="text">给 Another Dayu 博客作者 9 问留的一份答卷</summary>
      <content type="html"><![CDATA[<p>这篇是给 Another Dayu 那份博客作者问卷的答案</p>
<p>原问卷在这里：<a href="https://anotherdayu.com/2024/5962/" target="_blank">博客作者呀，我想采访你这 9 个问题！</a></p>
<section><h2>1.我是谁，以及这个博客是什么<a href="#1我是谁以及这个博客是什么"><span>#</span></a></h2><p>我是 Clina_sama。</p><p>这个博客名叫 <code>Clina_sama Blog</code> ，主要就是我的一些想法，教程类的文章，偶尔看见什么好的资源也会分享在上面，就当是我在互联网上的一个窗户吧。</p></section>
<section><h2>2.为什么开始写博客<a href="#2为什么开始写博客"><span>#</span></a></h2><p>一开始是感觉有趣，后来就把自己想写的写上去了</p></section>
<section><h2>3.我怎么完成一篇文章<a href="#3我怎么完成一篇文章"><span>#</span></a></h2><p>这个吗，我一般是自己先写文章，写完再让 AI 去总结</p><p>对的，就这</p></section>
<section><h2>4. 有没有失去过动力<a href="#4-有没有失去过动力"><span>#</span></a></h2><p>有的，在今年 3 月后短暂失去了动力，现在已经恢复了</p><p>恢复的原因其实就是我又想写博客了:(</p></section>
<section><h2>5. 搭建方式和成本<a href="#5-搭建方式和成本"><span>#</span></a></h2><p>目前大概是：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>VSCodium/NeoVim --&gt; 编写文章</span></div></div><div><div><div>2</div></div><div><span>Astro/Fuwari --&gt; 博客构建</span></div></div><div><div><div>3</div></div><div><span>GitHub &amp;&amp; Codeberg --&gt; 版本记录和远端同步</span></div></div><div><div><div>4</div></div><div><span>Cloudflare Workers &amp;&amp; Codeberg Pages --&gt; 静态部署</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>成本的话，主要就是域名和时间</p><p>域名吗，<code>clina.top</code> 一年大概 30￥，<code>150191.xyz</code> 一年大概 5￥</p><p>就这么简单</p></section>
<section><h2>6. 推荐自己一篇文章，也推荐一个喜欢的博客<a href="#6-推荐自己一篇文章也推荐一个喜欢的博客"><span>#</span></a></h2><p>我的话，会推荐这篇：</p><p><a href="/posts/cotalk-comments">cotalk-comments</a></p><p>原因很简单，因为这篇算是我最用心的一篇文章了，同时也花费了我很长的时间</p><p>喜欢的博客吗，我推荐这个：</p><p><a href="https://rebel1725.codeberg.page/blog" target="_blank">Rebel Zhang’s Blog</a></p></section>
<section><h2>7. 最近喜欢的东西<a href="#7-最近喜欢的东西"><span>#</span></a></h2><p>这个吗，也不好说，无非就是玩玩游戏，写点文章之类的</p></section>
<section><h2>8. 想做但还没做的事<a href="#8-想做但还没做的事"><span>#</span></a></h2><p>那可就多了，把 C++ 学会，学点 Python , Rust 和 PHP ，哦对了，还有学会一点法语</p></section>
<section><h2>9. 写到最后<a href="#9-写到最后"><span>#</span></a></h2><p>写到最后，这篇问卷也就回答完了，我的博客又多了一篇可以展示的文章；而你，也收获了一份真诚的答卷。（双赢啦，嘿嘿！）</p><p>也感谢看到这里的你了！如果你对我感兴趣，欢迎来认识我，可以在 NodeLoc 上向我私信或者尝试直接加我的 XMPP/Matrix 账号哦！</p></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/cotalk-comments/</id>
      <title type="text">在你的 Fuwari 博客中添加 Cotalk 评论系统</title>
      <published>2026-06-18T00:00:00.000Z</published>
      <updated>2026-06-18T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/cotalk-comments/"/>
      <summary type="text">本文详细介绍了为Fuwari博客集成Cotalk评论系统的完整流程：创建公开的Codeberg仓库存储评论，通过Cloudflare Workers搭建OAuth安全代理，配置环境变量与密钥，最后在博客页面嵌入Cotalk组件。整个过程实现了基于Codeberg Issues的轻量级评论功能，支持主题切换与无刷新加载，为静态博客提供了便捷的互动能力。</summary>
      <content type="html"><![CDATA[<p>Cotalk 是一个基于 Codeberg Issues 与 Gitalk 的评论系统，本文旨在介绍如何在 Fuwari 博客中添加这个评论系统。</p>
<section><h2>准备工作<a href="#准备工作"><span>#</span></a></h2><p>你需要：</p><ul>
<li>一个 Cloudflare 账户</li>
<li>一个 Codeberg 账户</li>
<li>一个正常运行的 Fuwari 博客</li>
<li>一个聪明的脑子</li>
</ul></section>
<section><h2>配置<a href="#配置"><span>#</span></a></h2><section><h3>准备 Codeberg 仓库<a href="#准备-codeberg-仓库"><span>#</span></a></h3><p>首先，我们需要在 Codeberg 上创建一个专门用于存储评论的仓库。这个仓库必须是公开的，因为 Cotalk 需要通过访问 Codeberg 仓库来加载和提交评论。</p><ol>
<li>创建新仓库</li>
</ol><ul>
<li>
<p>登录 Codeberg , 点击右上角 ”+” 按钮</p>
</li>
<li>
<p>选择 “创建仓库”</p>
</li>
<li>
<p>填写仓库名称（如 <code>fuwari-comments</code>）和其他必要信息</p>
</li>
<li>
<p>确保仓库可见性设置为 Public</p>
</li>
</ul></section><section><h3>配置 Cotalk<a href="#配置-cotalk"><span>#</span></a></h3><p>接下来，我们需要配置 Cotalk 以将评论与我们的仓库关联起来。</p><ol>
<li>创建 Workers</li>
</ol><p>打开 <a href="https://dash.cloudflare.com" target="_blank">Cloudflare 控制面板</a>，并使用你的 Cloudflare 账户登录。</p><p>点击 “计算”。</p><p></p><figure><img alt="alt text" loading="lazy" width="249" height="404" src="/_astro/cf-panel1.BNj5sGtD_FV4Kh.webp" /><figcaption>alt text</figcaption></figure><p></p><p>点击 “Workers 和 Pages”。</p><p></p><figure><img alt="alt text" loading="lazy" width="247" height="329" src="/_astro/cf-panel2.BoR9xbVr_1HslKk.webp" /><figcaption>alt text</figcaption></figure><p></p><p>点击 “创建应用程序”。</p><p></p><figure><img alt="alt text" loading="lazy" width="1337" height="109" src="/_astro/cf-workers-panel.P_vum9x9_Z1pmGVH.webp" /><figcaption>alt text</figcaption></figure><p></p><p>点击 “从 Hello World! 开始”。</p><p></p><figure><img alt="alt text" loading="lazy" width="1017" height="504" src="/_astro/cf-workers-create.D59NxEx0_1B7HUI.webp" /><figcaption>alt text</figcaption></figure><p></p><p>点击 “部署”。</p><p></p><figure><img alt="alt text" loading="lazy" width="948" height="693" src="/_astro/cf-workers-deploy.C6narcvX_ZgtNL2.webp" /><figcaption>alt text</figcaption></figure><p></p><p>点击 “编辑代码”。</p><p></p><figure><img alt="alt text" loading="lazy" width="1391" height="114" src="/_astro/cf-workers-editcode.bZ9phZZ3_1UkMG7.webp" /><figcaption>alt text</figcaption></figure><p></p><p>在打开的 Code Server 编辑器中填入以下代码。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// Cloudflare Worker：CoTalk OAuth 安全代理</span></div></div><div><div><div>2</div></div><div><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>async</span><span> </span><span>fetch</span><span><span>(</span><span>request</span><span>, </span><span>env</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>        </span><span>const</span><span> </span><span>url</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>URL</span><span><span>(</span><span>request</span><span>.</span></span><span>url</span><span>);</span></div></div><div><div><div>5</div></div><div><span>        </span><span>const</span><span> </span><span>blogOrigin</span><span> </span><span>=</span><span> </span><span>'yourblogdomain'</span><span>; </span><span>// 你的博客域名</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>        </span><span>// 封装 CORS 头部设置，避免遗漏</span></div></div><div><div><div>8</div></div><div><span>        </span><span>const</span><span> </span><span>corsHeaders</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>9</div></div><div><span>            </span><span>'Access-Control-Allow-Origin'</span><span>:</span><span> </span><span>blogOrigin</span><span>,</span></div></div><div><div><div>10</div></div><div><span>            </span><span>'Access-Control-Allow-Methods'</span><span>:</span><span> </span><span>'GET, POST, OPTIONS'</span><span>,</span></div></div><div><div><div>11</div></div><div><span>            </span><span>'Access-Control-Allow-Headers'</span><span>:</span><span> </span><span>'Content-Type'</span><span>,</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>};</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>        </span><span>// CORS 预检</span></div></div><div><div><div>15</div></div><div><span>        </span><span>if</span><span><span> (</span><span>request</span><span>.</span></span><span>method</span><span> </span><span>===</span><span> </span><span>'OPTIONS'</span><span>) {</span></div></div><div><div><div>16</div></div><div><span>            </span><span>return</span><span> </span><span>new</span><span> </span><span>Response</span><span>(</span><span>null</span><span>, { </span><span>headers</span><span>:</span><span> </span><span>corsHeaders</span><span> });</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>        </span><span>// ---------- 1. 授权请求：GET /authorize ----------</span></div></div><div><div><div>20</div></div><div><span>        </span><span>if</span><span><span> (</span><span>request</span><span>.</span></span><span>method</span><span> </span><span>===</span><span> </span><span>'GET'</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>url</span><span>.</span></span><span>pathname</span><span> </span><span>===</span><span> </span><span>'/authorize'</span><span>) {</span></div></div><div><div><div>21</div></div><div><span>            </span><span>const</span><span> </span><span>clientId</span><span> </span><span>=</span><span><span> </span><span>url</span><span>.</span></span><span>searchParams</span><span>.</span><span>get</span><span>(</span><span>'client_id'</span><span>);</span></div></div><div><div><div>22</div></div><div><span>            </span><span>const</span><span> </span><span>redirectUri</span><span> </span><span>=</span><span><span> </span><span>url</span><span>.</span></span><span>searchParams</span><span>.</span><span>get</span><span>(</span><span>'redirect_uri'</span><span>) </span><span>||</span><span> </span><span>blogOrigin</span><span>;</span></div></div><div><div><div>23</div></div><div><span>            </span><span>const</span><span> </span><span>state</span><span> </span><span>=</span><span><span> </span><span>url</span><span>.</span></span><span>searchParams</span><span>.</span><span>get</span><span>(</span><span>'state'</span><span>) </span><span>||</span><span> </span><span>''</span><span>;</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>            </span><span>// 将前端回调地址和状态编码进 state</span></div></div><div><div><div>26</div></div><div><span>            </span><span>const</span><span> </span><span>codebergState</span><span> </span><span>=</span><span> </span><span>JSON</span><span>.</span><span>stringify</span><span>({</span></div></div><div><div><div>27</div></div><div><span>                </span><span>redirect_uri</span><span>:</span><span> </span><span>redirectUri</span><span>,</span></div></div><div><div><div>28</div></div><div><span>                </span><span>frontend_state</span><span>:</span><span> </span><span>state</span><span>,</span></div></div><div><div><div>29</div></div><div><span><span>            </span></span><span>});</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>            </span><span>const</span><span> </span><span>codebergAuthUrl</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>URL</span><span>(</span><span>'https://codeberg.org/login/oauth/authorize'</span><span>);</span></div></div><div><div><div>32</div></div><div><span><span>            </span></span><span>codebergAuthUrl</span><span>.</span><span>searchParams</span><span>.</span><span>set</span><span>(</span><span>'client_id'</span><span><span>, </span><span>env</span><span>.</span></span><span>CLIENT_ID</span><span>);</span></div></div><div><div><div>33</div></div><div><span><span>            </span></span><span>codebergAuthUrl</span><span>.</span><span>searchParams</span><span>.</span><span>set</span><span>(</span><span>'redirect_uri'</span><span>, </span><span>`</span><span>${</span><span>url</span><span>.</span><span>origin</span><span>}</span><span>/callback`</span><span>);</span></div></div><div><div><div>34</div></div><div><span><span>            </span></span><span>codebergAuthUrl</span><span>.</span><span>searchParams</span><span>.</span><span>set</span><span>(</span><span>'response_type'</span><span>, </span><span>'code'</span><span>);</span></div></div><div><div><div>35</div></div><div><span><span>            </span></span><span>codebergAuthUrl</span><span>.</span><span>searchParams</span><span>.</span><span>set</span><span>(</span><span>'state'</span><span>, </span><span>codebergState</span><span>);</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>            </span><span>// ✅ 302 重定向务必带上 CORS 头</span></div></div><div><div><div>38</div></div><div><span>            </span><span>return</span><span> </span><span>new</span><span> </span><span>Response</span><span>(</span><span>null</span><span>, {</span></div></div><div><div><div>39</div></div><div><span>                </span><span>status</span><span>:</span><span> </span><span>302</span><span>,</span></div></div><div><div><div>40</div></div><div><span>                </span><span>headers</span><span>:</span><span> {</span></div></div><div><div><div>41</div></div><div><span><span>                    </span></span><span>...</span><span>corsHeaders</span><span>,               </span><span>// CORS 头</span></div></div><div><div><div>42</div></div><div><span>                    </span><span>'Location'</span><span>:</span><span><span> </span><span>codebergAuthUrl</span><span>.</span></span><span>toString</span><span>(),</span></div></div><div><div><div>43</div></div><div><span><span>                </span></span><span>},</span></div></div><div><div><div>44</div></div><div><span><span>            </span></span><span>});</span></div></div><div><div><div>45</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>        </span><span>// ---------- 2. 回调处理：GET /callback ----------</span></div></div><div><div><div>48</div></div><div><span>        </span><span>if</span><span><span> (</span><span>request</span><span>.</span></span><span>method</span><span> </span><span>===</span><span> </span><span>'GET'</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>url</span><span>.</span></span><span>pathname</span><span> </span><span>===</span><span> </span><span>'/callback'</span><span>) {</span></div></div><div><div><div>49</div></div><div><span>            </span><span>const</span><span> </span><span>code</span><span> </span><span>=</span><span><span> </span><span>url</span><span>.</span></span><span>searchParams</span><span>.</span><span>get</span><span>(</span><span>'code'</span><span>);</span></div></div><div><div><div>50</div></div><div><span>            </span><span>const</span><span> </span><span>stateParam</span><span> </span><span>=</span><span><span> </span><span>url</span><span>.</span></span><span>searchParams</span><span>.</span><span>get</span><span>(</span><span>'state'</span><span>);</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>            </span><span>if</span><span> (</span><span>!</span><span>code</span><span> </span><span>||</span><span> </span><span>!</span><span>stateParam</span><span>) {</span></div></div><div><div><div>53</div></div><div><span>                </span><span>return</span><span> </span><span>new</span><span> </span><span>Response</span><span>(</span><span>'Missing code or state'</span><span>, { </span><span>status</span><span>:</span><span> </span><span>400</span><span>, </span><span>headers</span><span>:</span><span> </span><span>corsHeaders</span><span> });</span></div></div><div><div><div>54</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span>            </span><span>let</span><span> </span><span>parsedState</span><span>;</span></div></div><div><div><div>57</div></div><div><span>            </span><span>try</span><span> {</span></div></div><div><div><div>58</div></div><div><span>                </span><span>parsedState</span><span> </span><span>=</span><span> </span><span>JSON</span><span>.</span><span>parse</span><span>(</span><span>stateParam</span><span>);</span></div></div><div><div><div>59</div></div><div><span><span>            </span></span><span>} </span><span>catch</span><span> {</span></div></div><div><div><div>60</div></div><div><span>                </span><span>return</span><span> </span><span>new</span><span> </span><span>Response</span><span>(</span><span>'Invalid state parameter'</span><span>, { </span><span>status</span><span>:</span><span> </span><span>400</span><span>, </span><span>headers</span><span>:</span><span> </span><span>corsHeaders</span><span> });</span></div></div><div><div><div>61</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>62</div></div><div>
</div></div><div><div><div>63</div></div><div><span>            </span><span>const</span><span> </span><span>redirectUri</span><span> </span><span>=</span><span><span> </span><span>parsedState</span><span>.</span></span><span>redirect_uri</span><span> </span><span>||</span><span> </span><span>blogOrigin</span><span>;</span></div></div><div><div><div>64</div></div><div><span>            </span><span>const</span><span> </span><span>frontendState</span><span> </span><span>=</span><span><span> </span><span>parsedState</span><span>.</span></span><span>frontend_state</span><span> </span><span>||</span><span> </span><span>''</span><span>;</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>            </span><span>try</span><span> {</span></div></div><div><div><div>67</div></div><div><span>                </span><span>const</span><span> </span><span>tokenResponse</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>fetch</span><span>(</span><span>'https://codeberg.org/login/oauth/access_token'</span><span>, {</span></div></div><div><div><div>68</div></div><div><span>                    </span><span>method</span><span>:</span><span> </span><span>'POST'</span><span>,</span></div></div><div><div><div>69</div></div><div><span>                    </span><span>headers</span><span>:</span><span> { </span><span>'Content-Type'</span><span>:</span><span> </span><span>'application/json'</span><span>, </span><span>'Accept'</span><span>:</span><span> </span><span>'application/json'</span><span> },</span></div></div><div><div><div>70</div></div><div><span>                    </span><span>body</span><span>:</span><span> </span><span>JSON</span><span>.</span><span>stringify</span><span>({</span></div></div><div><div><div>71</div></div><div><span>                        </span><span>client_id</span><span>:</span><span><span> </span><span>env</span><span>.</span></span><span>CLIENT_ID</span><span>,</span></div></div><div><div><div>72</div></div><div><span>                        </span><span>client_secret</span><span>:</span><span><span> </span><span>env</span><span>.</span></span><span>CLIENT_SECRET</span><span>,</span></div></div><div><div><div>73</div></div><div><span>                        </span><span>code</span><span>,</span></div></div><div><div><div>74</div></div><div><span>                        </span><span>grant_type</span><span>:</span><span> </span><span>'authorization_code'</span><span>,</span></div></div><div><div><div>75</div></div><div><span>                        </span><span>redirect_uri</span><span>:</span><span> </span><span>`</span><span>${</span><span>url</span><span>.</span><span>origin</span><span>}</span><span>/callback`</span><span>,</span></div></div><div><div><div>76</div></div><div><span><span>                    </span></span><span>}),</span></div></div><div><div><div>77</div></div><div><span><span>                </span></span><span>});</span></div></div><div><div><div>78</div></div><div>
</div></div><div><div><div>79</div></div><div><span>                </span><span>const</span><span> </span><span>tokenData</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>tokenResponse</span><span>.</span></span><span>json</span><span>().</span><span>catch</span><span>(() </span><span>=&gt;</span><span> </span><span>null</span><span>);</span></div></div><div><div><div>80</div></div><div>
</div></div><div><div><div>81</div></div><div><span>                </span><span>if</span><span> (</span><span>!</span><span>tokenData</span><span> </span><span>||</span><span><span> </span><span>tokenData</span><span>.</span></span><span>error</span><span>) {</span></div></div><div><div><div>82</div></div><div><span>                    </span><span>const</span><span> </span><span>errorRedirect</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>URL</span><span>(</span><span>redirectUri</span><span>);</span></div></div><div><div><div>83</div></div><div><span><span>                    </span></span><span>errorRedirect</span><span>.</span><span>hash</span><span> </span><span>=</span><span> </span><span>`error=</span><span>${</span><span>tokenData</span><span>?.</span><span>error</span><span> </span><span>||</span><span> </span><span>'</span><span>unknown</span><span><span>'</span><span>}</span></span><span>&amp;error_description=</span><span>${</span><span>encodeURIComponent</span><span><span>(</span><span>tokenData</span></span><span>?.</span><span>error_description</span><span> </span><span>||</span><span> </span><span>'</span><span>Token exchange failed</span><span>'</span><span>)</span><span>}</span><span>`</span><span>;</span></div></div><div><div><div>84</div></div><div><span>                    </span><span>return</span><span> </span><span>new</span><span> </span><span>Response</span><span>(</span><span>null</span><span>, {</span></div></div><div><div><div>85</div></div><div><span>                        </span><span>status</span><span>:</span><span> </span><span>302</span><span>,</span></div></div><div><div><div>86</div></div><div><span>                        </span><span>headers</span><span>:</span><span> { </span><span>...</span><span>corsHeaders</span><span>, </span><span>'Location'</span><span>:</span><span><span> </span><span>errorRedirect</span><span>.</span></span><span>toString</span><span>() },</span></div></div><div><div><div>87</div></div><div><span><span>                    </span></span><span>});</span></div></div><div><div><div>88</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>89</div></div><div>
</div></div><div><div><div>90</div></div><div><span>                </span><span>if</span><span> (</span><span>!</span><span><span>tokenData</span><span>.</span></span><span>access_token</span><span>) {</span></div></div><div><div><div>91</div></div><div><span>                    </span><span>throw</span><span> </span><span>new</span><span> </span><span>Error</span><span>(</span><span>'No access_token in response'</span><span>);</span></div></div><div><div><div>92</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>93</div></div><div>
</div></div><div><div><div>94</div></div><div><span>                </span><span>const</span><span> </span><span>frontendRedirect</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>URL</span><span>(</span><span>redirectUri</span><span>);</span></div></div><div><div><div>95</div></div><div><span>                </span><span>const</span><span> </span><span>hashParams</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>URLSearchParams</span><span>();</span></div></div><div><div><div>96</div></div><div><span><span>                </span></span><span>hashParams</span><span>.</span><span>set</span><span>(</span><span>'access_token'</span><span><span>, </span><span>tokenData</span><span>.</span></span><span>access_token</span><span>);</span></div></div><div><div><div>97</div></div><div><span><span>                </span></span><span>hashParams</span><span>.</span><span>set</span><span>(</span><span>'token_type'</span><span><span>, </span><span>tokenData</span><span>.</span></span><span>token_type</span><span> </span><span>||</span><span> </span><span>'bearer'</span><span>);</span></div></div><div><div><div>98</div></div><div><span>                </span><span>if</span><span> (</span><span>frontendState</span><span><span>) </span><span>hashParams</span><span>.</span></span><span>set</span><span>(</span><span>'state'</span><span>, </span><span>frontendState</span><span>);</span></div></div><div><div><div>99</div></div><div><span><span>                </span></span><span>frontendRedirect</span><span>.</span><span>hash</span><span> </span><span>=</span><span><span> </span><span>hashParams</span><span>.</span></span><span>toString</span><span>();</span></div></div><div><div><div>100</div></div><div>
</div></div><div><div><div>101</div></div><div><span>                </span><span>return</span><span> </span><span>new</span><span> </span><span>Response</span><span>(</span><span>null</span><span>, {</span></div></div><div><div><div>102</div></div><div><span>                    </span><span>status</span><span>:</span><span> </span><span>302</span><span>,</span></div></div><div><div><div>103</div></div><div><span>                    </span><span>headers</span><span>:</span><span> { </span><span>...</span><span>corsHeaders</span><span>, </span><span>'Location'</span><span>:</span><span><span> </span><span>frontendRedirect</span><span>.</span></span><span>toString</span><span>() },</span></div></div><div><div><div>104</div></div><div><span><span>                </span></span><span>});</span></div></div><div><div><div>105</div></div><div><span><span>            </span></span><span>} </span><span>catch</span><span> (</span><span>err</span><span>) {</span></div></div><div><div><div>106</div></div><div><span>                </span><span>const</span><span> </span><span>errorRedirect</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>URL</span><span>(</span><span>redirectUri</span><span>);</span></div></div><div><div><div>107</div></div><div><span><span>                </span></span><span>errorRedirect</span><span>.</span><span>hash</span><span> </span><span>=</span><span> </span><span>`error=server_error&amp;error_description=</span><span>${</span><span>encodeURIComponent</span><span><span>(</span><span>err</span></span><span>.</span><span>message</span><span>)</span><span>}</span><span>`</span><span>;</span></div></div><div><div><div>108</div></div><div><span>                </span><span>return</span><span> </span><span>new</span><span> </span><span>Response</span><span>(</span><span>null</span><span>, {</span></div></div><div><div><div>109</div></div><div><span>                    </span><span>status</span><span>:</span><span> </span><span>302</span><span>,</span></div></div><div><div><div>110</div></div><div><span>                    </span><span>headers</span><span>:</span><span> { </span><span>...</span><span>corsHeaders</span><span>, </span><span>'Location'</span><span>:</span><span><span> </span><span>errorRedirect</span><span>.</span></span><span>toString</span><span>() },</span></div></div><div><div><div>111</div></div><div><span><span>                </span></span><span>});</span></div></div><div><div><div>112</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>113</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>114</div></div><div>
</div></div><div><div><div>115</div></div><div><span>        </span><span>// ---------- 3. Token 交换：POST /access_token ----------</span></div></div><div><div><div>116</div></div><div><span>        </span><span>if</span><span><span> (</span><span>request</span><span>.</span></span><span>method</span><span> </span><span>===</span><span> </span><span>'POST'</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>url</span><span>.</span></span><span>pathname</span><span> </span><span>===</span><span> </span><span>'/access_token'</span><span>) {</span></div></div><div><div><div>117</div></div><div><span>            </span><span>try</span><span> {</span></div></div><div><div><div>118</div></div><div><span>                </span><span>const</span><span> { </span><span>code</span><span> } </span><span>=</span><span> </span><span>await</span><span><span> </span><span>request</span><span>.</span></span><span>json</span><span>();</span></div></div><div><div><div>119</div></div><div><span>                </span><span>if</span><span> (</span><span>!</span><span>code</span><span>) {</span></div></div><div><div><div>120</div></div><div><span>                    </span><span>return</span><span> </span><span>new</span><span> </span><span>Response</span><span>(</span><span>JSON</span><span>.</span><span>stringify</span><span>({ </span><span>error</span><span>:</span><span> </span><span>'Code missing'</span><span> }), {</span></div></div><div><div><div>121</div></div><div><span>                        </span><span>status</span><span>:</span><span> </span><span>400</span><span>,</span></div></div><div><div><div>122</div></div><div><span>                        </span><span>headers</span><span>:</span><span> { </span><span>...</span><span>corsHeaders</span><span>, </span><span>'Content-Type'</span><span>:</span><span> </span><span>'application/json'</span><span> },</span></div></div><div><div><div>123</div></div><div><span><span>                    </span></span><span>});</span></div></div><div><div><div>124</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>125</div></div><div>
</div></div><div><div><div>126</div></div><div><span>                </span><span>const</span><span> </span><span>tokenResponse</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>fetch</span><span>(</span><span>'https://codeberg.org/login/oauth/access_token'</span><span>, {</span></div></div><div><div><div>127</div></div><div><span>                    </span><span>method</span><span>:</span><span> </span><span>'POST'</span><span>,</span></div></div><div><div><div>128</div></div><div><span>                    </span><span>headers</span><span>:</span><span> { </span><span>'Content-Type'</span><span>:</span><span> </span><span>'application/json'</span><span>, </span><span>'Accept'</span><span>:</span><span> </span><span>'application/json'</span><span> },</span></div></div><div><div><div>129</div></div><div><span>                    </span><span>body</span><span>:</span><span> </span><span>JSON</span><span>.</span><span>stringify</span><span>({</span></div></div><div><div><div>130</div></div><div><span>                        </span><span>client_id</span><span>:</span><span><span> </span><span>env</span><span>.</span></span><span>CLIENT_ID</span><span>,</span></div></div><div><div><div>131</div></div><div><span>                        </span><span>client_secret</span><span>:</span><span><span> </span><span>env</span><span>.</span></span><span>CLIENT_SECRET</span><span>,</span></div></div><div><div><div>132</div></div><div><span>                        </span><span>code</span><span>,</span></div></div><div><div><div>133</div></div><div><span>                        </span><span>grant_type</span><span>:</span><span> </span><span>'authorization_code'</span><span>,</span></div></div><div><div><div>134</div></div><div><span>                        </span><span>redirect_uri</span><span>:</span><span> </span><span>`</span><span>${</span><span>url</span><span>.</span><span>origin</span><span>}</span><span>/callback`</span><span>,</span></div></div><div><div><div>135</div></div><div><span><span>                    </span></span><span>}),</span></div></div><div><div><div>136</div></div><div><span><span>                </span></span><span>});</span></div></div><div><div><div>137</div></div><div>
</div></div><div><div><div>138</div></div><div><span>                </span><span>const</span><span> </span><span>data</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>tokenResponse</span><span>.</span></span><span>json</span><span>().</span><span>catch</span><span>(() </span><span>=&gt;</span><span> ({ </span><span>error</span><span>:</span><span> </span><span>'Invalid JSON'</span><span> }));</span></div></div><div><div><div>139</div></div><div><span>                </span><span>return</span><span> </span><span>new</span><span> </span><span>Response</span><span>(</span><span>JSON</span><span>.</span><span>stringify</span><span>(</span><span>data</span><span>), {</span></div></div><div><div><div>140</div></div><div><span>                    </span><span>status</span><span>:</span><span><span> </span><span>data</span><span>.</span></span><span>error</span><span> </span><span>?</span><span> </span><span>400</span><span> </span><span>:</span><span> </span><span>200</span><span>,</span></div></div><div><div><div>141</div></div><div><span>                    </span><span>headers</span><span>:</span><span> { </span><span>...</span><span>corsHeaders</span><span>, </span><span>'Content-Type'</span><span>:</span><span> </span><span>'application/json'</span><span> },</span></div></div><div><div><div>142</div></div><div><span><span>                </span></span><span>});</span></div></div><div><div><div>143</div></div><div><span><span>            </span></span><span>} </span><span>catch</span><span> (</span><span>err</span><span>) {</span></div></div><div><div><div>144</div></div><div><span>                </span><span>return</span><span> </span><span>new</span><span> </span><span>Response</span><span>(</span><span>JSON</span><span>.</span><span>stringify</span><span>({ </span><span>error</span><span>:</span><span> </span><span>'server_error'</span><span> }), {</span></div></div><div><div><div>145</div></div><div><span>                    </span><span>status</span><span>:</span><span> </span><span>500</span><span>,</span></div></div><div><div><div>146</div></div><div><span>                    </span><span>headers</span><span>:</span><span> { </span><span>...</span><span>corsHeaders</span><span>, </span><span>'Content-Type'</span><span>:</span><span> </span><span>'application/json'</span><span> },</span></div></div><div><div><div>147</div></div><div><span><span>                </span></span><span>});</span></div></div><div><div><div>148</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>149</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>150</div></div><div>
</div></div><div><div><div>151</div></div><div><span>        </span><span>return</span><span> </span><span>new</span><span> </span><span>Response</span><span>(</span><span>'Not Found'</span><span>, { </span><span>status</span><span>:</span><span> </span><span>404</span><span>, </span><span>headers</span><span>:</span><span> </span><span>corsHeaders</span><span> });</span></div></div><div><div><div>152</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>153</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>将 <code>yourblogdomain</code> 字段替换为你的博客域名。</p><p>更改后，点击 “部署”。</p><ol>
<li>配置 OAuth</li>
</ol><p>打开 <a href="https://codeberg.org/user/settings/applications" target="_blank">Codeberg OAuth 应用程序创建界面</a>，创建一个 OAuth 应用，并记下 <code>客户端 ID</code> 与 <code>客户端密钥</code>。</p><p>回到 Cloudflare 控制面板，进入你刚刚创建的 Workers 应用。</p><p>点击 “设置”。</p><p></p><figure><img alt="alt text" loading="lazy" width="1061" height="241" src="/_astro/cf-secrets.CIRTtOPr_Z22j4hr.webp" /><figcaption>alt text</figcaption></figure><p></p><p>点击 “添加”。</p><p>添加一条密钥，名称为 <code>CLIENT_ID</code> ，内容为你刚刚创建的 OAuth 应用的 <code>客户端 ID</code>。</p><p>再添加一条密钥，名称为 <code>CLIENT_SECRET</code> ，内容为你刚刚创建的 OAuth 应用的 <code>客户端密钥</code>。</p><p>创建好后，长这样：</p><p></p><figure><img alt="alt text" loading="lazy" width="954" height="225" src="/_astro/cf-secrets-created.LwlF961x_2p388C.webp" /><figcaption>alt text</figcaption></figure><p></p><p>然后，点击 “域”。</p><p></p><figure><img alt="alt text" loading="lazy" width="452" height="71" src="/_astro/cf-workers-domain.zphgJRZE_1SfJOw.webp" /><figcaption>alt text</figcaption></figure><p></p><p>记住下面的 “Worker URL”。</p><p>回到 Codeberg OAuth 应用程序创建界面，点击你刚才创建的 OAuth 应用，在 “重定向 URI” 一栏填入 <code>刚才的 Worker URL/callback</code> ，点击保存。</p><ol>
<li>获取配置信息</li>
</ol><p>完成以上步骤后，记录以下配置信息，稍后将在 Fuwari 中使用：</p><ul>
<li>仓库名称</li>
<li>客户端 ID</li>
<li>Workers URL</li>
</ul></section><section><h3>添加到 Fuwari<a href="#添加到-fuwari"><span>#</span></a></h3><ol>
<li>创建 Cotalk 组件</li>
</ol><p>为了实现在亮色/暗色下都能使 Cotalk 完美显示,我们需要在 src/components/misc/ 目录下创建 Cotalk.astro 文件，内容如下：</p><div><div><div><figure><figcaption><span>src/components/misc/Cotalk.astro</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>---</span></div></div><div><div><div>2</div></div><div><span>// src/components/comment/Cotalk.astro</span></div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>interface</span><span> </span><span>Props</span><span> {</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>clientID</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>proxy</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>repo</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>owner</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>admin</span><span>:</span><span> </span><span>string</span><span>[];</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>id</span><span><span>?</span><span>:</span></span><span> </span><span>string</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>number</span><span><span>?</span><span>:</span></span><span> </span><span>number</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>labels</span><span><span>?</span><span>:</span></span><span> </span><span>string</span><span>[];</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>title</span><span><span>?</span><span>:</span></span><span> </span><span>string</span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>language</span><span><span>?</span><span>:</span></span><span> </span><span>string</span><span>;</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>perPage</span><span><span>?</span><span>:</span></span><span> </span><span>number</span><span>;</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>distractionFreeMode</span><span><span>?</span><span>:</span></span><span> </span><span>boolean</span><span>;</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>pagerDirection</span><span><span>?</span><span>:</span></span><span> </span><span>'last'</span><span> </span><span>|</span><span> </span><span>'first'</span><span>;</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>createIssueManually</span><span><span>?</span><span>:</span></span><span> </span><span>boolean</span><span>;</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>enableHotKey</span><span><span>?</span><span>:</span></span><span> </span><span>boolean</span><span>;</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>const</span><span> {</span></div></div><div><div><div>22</div></div><div><span>  </span><span>clientID</span><span>,</span></div></div><div><div><div>23</div></div><div><span>  </span><span>proxy</span><span>,</span></div></div><div><div><div>24</div></div><div><span>  </span><span>repo</span><span>,</span></div></div><div><div><div>25</div></div><div><span>  </span><span>owner</span><span>,</span></div></div><div><div><div>26</div></div><div><span>  </span><span>admin</span><span>,</span></div></div><div><div><div>27</div></div><div><span>  </span><span>id</span><span>,</span></div></div><div><div><div>28</div></div><div><span>  </span><span>number</span><span> </span><span>=</span><span> </span><span>-</span><span>1</span><span>,</span></div></div><div><div><div>29</div></div><div><span>  </span><span>labels</span><span> </span><span>=</span><span> [</span><span>'Cotalk'</span><span>],</span></div></div><div><div><div>30</div></div><div><span>  </span><span>title</span><span> </span><span>=</span><span> </span><span>''</span><span>,</span></div></div><div><div><div>31</div></div><div><span>  </span><span>language</span><span> </span><span>=</span><span> </span><span>'zh-CN'</span><span>,</span></div></div><div><div><div>32</div></div><div><span>  </span><span>perPage</span><span> </span><span>=</span><span> </span><span>10</span><span>,</span></div></div><div><div><div>33</div></div><div><span>  </span><span>distractionFreeMode</span><span> </span><span>=</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>34</div></div><div><span>  </span><span>pagerDirection</span><span> </span><span>=</span><span> </span><span>'last'</span><span>,</span></div></div><div><div><div>35</div></div><div><span>  </span><span>createIssueManually</span><span> </span><span>=</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>36</div></div><div><span>  </span><span>enableHotKey</span><span> </span><span>=</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>37</div></div><div><span>} </span><span>=</span><span><span> </span><span>Astro</span><span>.</span></span><span>props</span><span>;</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>// 提前构建配置对象，确保所有解构变量都被使用</span></div></div><div><div><div>40</div></div><div><span>const</span><span> </span><span>config</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>41</div></div><div><span><span>  </span></span><span>clientID</span><span>,</span></div></div><div><div><div>42</div></div><div><span><span>  </span></span><span>proxy</span><span>,</span></div></div><div><div><div>43</div></div><div><span><span>  </span></span><span>repo</span><span>,</span></div></div><div><div><div>44</div></div><div><span><span>  </span></span><span>owner</span><span>,</span></div></div><div><div><div>45</div></div><div><span><span>  </span></span><span>admin</span><span>,</span></div></div><div><div><div>46</div></div><div><span>  </span><span>id</span><span>:</span><span><span> </span><span>id</span><span> </span></span><span>||</span><span> </span><span>null</span><span>,</span></div></div><div><div><div>47</div></div><div><span><span>  </span></span><span>number</span><span>,</span></div></div><div><div><div>48</div></div><div><span><span>  </span></span><span>labels</span><span>,</span></div></div><div><div><div>49</div></div><div><span>  </span><span>title</span><span>:</span><span><span> </span><span>title</span><span> </span></span><span>||</span><span> </span><span>null</span><span>,</span></div></div><div><div><div>50</div></div><div><span><span>  </span></span><span>language</span><span>,</span></div></div><div><div><div>51</div></div><div><span><span>  </span></span><span>perPage</span><span>,</span></div></div><div><div><div>52</div></div><div><span><span>  </span></span><span>distractionFreeMode</span><span>,</span></div></div><div><div><div>53</div></div><div><span><span>  </span></span><span>pagerDirection</span><span>,</span></div></div><div><div><div>54</div></div><div><span><span>  </span></span><span>createIssueManually</span><span>,</span></div></div><div><div><div>55</div></div><div><span><span>  </span></span><span>enableHotKey</span><span>,</span></div></div><div><div><div>56</div></div><div><span>};</span></div></div><div><div><div>57</div></div><div>
</div></div><div><div><div>58</div></div><div><span>// 序列化一次，供模板使用</span></div></div><div><div><div>59</div></div><div><span>const</span><span> </span><span>configJSON</span><span> </span><span>=</span><span> </span><span>JSON</span><span>.</span><span>stringify</span><span><span>(</span><span>config</span><span>);</span></span></div></div><div><div><div>60</div></div><div><span>---</span></div></div><div><div><div>61</div></div><div>
</div></div><div><div><div>62</div></div><div><span>&lt;</span><span>div</span><span> </span><span>id</span><span>=</span><span>"cotalk-container"</span><span> </span><span>class</span><span>=</span><span>"cotalk-root"</span><span>&gt;&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>63</div></div><div>
</div></div><div><div><div>64</div></div><div><span>&lt;!-- 直接输出 JSON，使用 set:html 避免 HTML 转义 --&gt;</span></div></div><div><div><div>65</div></div><div><span>&lt;</span><span>script</span><span> </span><span>type</span><span>=</span><span>"application/json"</span><span> </span><span>id</span><span>=</span><span>"cotalk-config"</span><span> </span><span>set:html</span><span>=</span><span>{</span><span>configJSON</span><span>}</span><span>&gt;&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>66</div></div><div>
</div></div><div><div><div>67</div></div><div><span>&lt;!-- 加载 CoTalk CSS（只加载一次） --&gt;</span></div></div><div><div><div>68</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>69</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>document</span><span>.</span></span><span>querySelector</span><span>(</span><span>'link[href*="cotalk.css"]'</span><span>)) {</span></div></div><div><div><div>70</div></div><div><span>    </span><span>const</span><span> </span><span>link</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>createElement</span><span>(</span><span>'link'</span><span>);</span></div></div><div><div><div>71</div></div><div><span><span>    </span></span><span>link</span><span>.</span><span>rel</span><span> </span><span>=</span><span> </span><span>'stylesheet'</span><span>;</span></div></div><div><div><div>72</div></div><div><span><span>    </span></span><span>link</span><span>.</span><span>href</span><span> </span><span>=</span><span> </span><span>'https://unpkg.com/@clina_z/cotalk@latest/dist/cotalk.css'</span><span>;</span></div></div><div><div><div>73</div></div><div><span><span>    </span></span><span>document</span><span>.</span><span>head</span><span>.</span><span>appendChild</span><span><span>(</span><span>link</span><span>);</span></span></div></div><div><div><div>74</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>75</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>76</div></div><div>
</div></div><div><div><div>77</div></div><div><span>&lt;!-- 核心初始化脚本 --&gt;</span></div></div><div><div><div>78</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>79</div></div><div><span>  </span><span>// 声明全局变量 Cotalk，避免 TypeScript 报错</span></div></div><div><div><div>80</div></div><div><span>  </span><span>declare</span><span> </span><span>var</span><span><span> </span><span>Cotalk</span></span><span>:</span><span> </span><span>any</span><span>;</span></div></div><div><div><div>81</div></div><div>
</div></div><div><div><div>82</div></div><div><span><span>  </span></span><span>(</span><span>function</span><span> () {</span></div></div><div><div><div>83</div></div><div><span>    </span><span>function</span><span> </span><span>getConfig</span><span>() {</span></div></div><div><div><div>84</div></div><div><span>      </span><span>const</span><span> </span><span>configEl</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>getElementById</span><span>(</span><span>'cotalk-config'</span><span>);</span></div></div><div><div><div>85</div></div><div><span>      </span><span>if</span><span> (</span><span>!</span><span><span>configEl</span><span>) </span></span><span>return</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>86</div></div><div><span>      </span><span>try</span><span> {</span></div></div><div><div><div>87</div></div><div><span>        </span><span>return</span><span> </span><span>JSON</span><span>.</span><span>parse</span><span><span>(</span><span>configEl</span><span>.</span></span><span>textContent</span><span>);</span></div></div><div><div><div>88</div></div><div><span><span>      </span></span><span>} </span><span>catch</span><span><span> (</span><span>e</span><span>) {</span></span></div></div><div><div><div>89</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>'解析 CoTalk 配置失败'</span><span><span>, </span><span>e</span><span>);</span></span></div></div><div><div><div>90</div></div><div><span>        </span><span>return</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>91</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>92</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>93</div></div><div>
</div></div><div><div><div>94</div></div><div><span>    </span><span>function</span><span> </span><span>loadCotalkScript</span><span>()</span><span>:</span><span> </span><span>Promise</span><span><span>&lt;</span><span>void</span><span>&gt; {</span></span></div></div><div><div><div>95</div></div><div><span>      </span><span>return</span><span> </span><span>new</span><span> </span><span>Promise</span><span><span>&lt;</span><span>void</span><span>&gt;((</span><span>resolve</span><span>, </span><span>reject</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>96</div></div><div><span>        </span><span>if</span><span><span> ((</span><span>window</span><span> </span><span>as</span><span> </span></span><span>any</span><span>).</span><span>Cotalk</span><span>) </span><span>return</span><span> </span><span>resolve</span><span>();</span></div></div><div><div><div>97</div></div><div><span>        </span><span>const</span><span> </span><span>script</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>createElement</span><span>(</span><span>'script'</span><span>);</span></div></div><div><div><div>98</div></div><div><span><span>        </span></span><span>script</span><span>.</span><span>src</span><span> </span><span>=</span><span> </span><span>'https://unpkg.com/@clina_z/cotalk@latest/dist/cotalk.min.js'</span><span>;</span></div></div><div><div><div>99</div></div><div><span><span>        </span></span><span>script</span><span>.</span><span>onload</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>100</div></div><div><span>          </span><span>if</span><span><span> ((</span><span>window</span><span> </span><span>as</span><span> </span></span><span>any</span><span>).</span><span>Cotalk</span><span>) </span><span>resolve</span><span>();</span></div></div><div><div><div>101</div></div><div><span>          </span><span>else</span><span> </span><span>reject</span><span>(</span><span>new</span><span> </span><span>Error</span><span>(</span><span>'Cotalk 全局变量未暴露'</span><span>));</span></div></div><div><div><div>102</div></div><div><span><span>        </span></span><span>};</span></div></div><div><div><div>103</div></div><div><span><span>        </span></span><span>script</span><span>.</span><span>onerror</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> </span><span>reject</span><span>(</span><span>new</span><span> </span><span>Error</span><span>(</span><span>'Cotalk 脚本加载失败'</span><span>));</span></div></div><div><div><div>104</div></div><div><span><span>        </span></span><span>document</span><span>.</span><span>head</span><span>.</span><span>appendChild</span><span><span>(</span><span>script</span><span>);</span></span></div></div><div><div><div>105</div></div><div><span><span>      </span></span><span>});</span></div></div><div><div><div>106</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>107</div></div><div>
</div></div><div><div><div>108</div></div><div><span>    </span><span>async</span><span> </span><span>function</span><span> </span><span>initCotalk</span><span>()</span><span>:</span><span> </span><span>Promise</span><span><span>&lt;</span><span>void</span><span>&gt; {</span></span></div></div><div><div><div>109</div></div><div><span>      </span><span>try</span><span> {</span></div></div><div><div><div>110</div></div><div><span>        </span><span>const</span><span> </span><span>config</span><span> </span><span>=</span><span> </span><span>getConfig</span><span>();</span></div></div><div><div><div>111</div></div><div><span>        </span><span>if</span><span> (</span><span>!</span><span><span>config</span><span>) </span></span><span>return</span><span>;</span></div></div><div><div><div>112</div></div><div>
</div></div><div><div><div>113</div></div><div><span>        </span><span>await</span><span> </span><span>loadCotalkScript</span><span>();</span></div></div><div><div><div>114</div></div><div><span>        </span><span>const</span><span> </span><span>container</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>getElementById</span><span>(</span><span>'cotalk-container'</span><span>);</span></div></div><div><div><div>115</div></div><div><span>        </span><span>if</span><span> (</span><span>!</span><span><span>container</span><span>) </span></span><span>return</span><span>;</span></div></div><div><div><div>116</div></div><div>
</div></div><div><div><div>117</div></div><div><span>        </span><span>// 清空容器（为 SWUP 切换页面做准备）</span></div></div><div><div><div>118</div></div><div><span><span>        </span></span><span>container</span><span>.</span><span>innerHTML</span><span> </span><span>=</span><span> </span><span>''</span><span>;</span></div></div><div><div><div>119</div></div><div>
</div></div><div><div><div>120</div></div><div><span>        </span><span>const</span><span> </span><span>finalId</span><span> </span><span>=</span><span><span> </span><span>config</span><span>.</span></span><span>id</span><span> </span><span>||</span><span><span> </span><span>window</span><span>.</span></span><span>location</span><span>.</span><span>pathname</span><span>;</span></div></div><div><div><div>121</div></div><div><span>        </span><span>const</span><span> </span><span>finalTitle</span><span> </span><span>=</span><span><span> </span><span>config</span><span>.</span></span><span>title</span><span> </span><span>||</span><span><span> </span><span>document</span><span>.</span></span><span>title</span><span>;</span></div></div><div><div><div>122</div></div><div>
</div></div><div><div><div>123</div></div><div><span>        </span><span>const</span><span> </span><span>cotalk</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Cotalk</span><span>({</span></div></div><div><div><div>124</div></div><div><span>          </span><span>clientID</span><span>:</span><span><span> </span><span>config</span><span>.</span></span><span>clientID</span><span>,</span></div></div><div><div><div>125</div></div><div><span>          </span><span>proxy</span><span>:</span><span><span> </span><span>config</span><span>.</span></span><span>proxy</span><span>,</span></div></div><div><div><div>126</div></div><div><span>          </span><span>repo</span><span>:</span><span><span> </span><span>config</span><span>.</span></span><span>repo</span><span>,</span></div></div><div><div><div>127</div></div><div><span>          </span><span>owner</span><span>:</span><span><span> </span><span>config</span><span>.</span></span><span>owner</span><span>,</span></div></div><div><div><div>128</div></div><div><span>          </span><span>admin</span><span>:</span><span><span> </span><span>config</span><span>.</span></span><span>admin</span><span>,</span></div></div><div><div><div>129</div></div><div><span>          </span><span>id</span><span>:</span><span><span> </span><span>finalId</span><span>,</span></span></div></div><div><div><div>130</div></div><div><span>          </span><span>number</span><span>:</span><span><span> </span><span>config</span><span>.</span></span><span>number</span><span>,</span></div></div><div><div><div>131</div></div><div><span>          </span><span>labels</span><span>:</span><span><span> </span><span>config</span><span>.</span></span><span>labels</span><span>,</span></div></div><div><div><div>132</div></div><div><span>          </span><span>title</span><span>:</span><span><span> </span><span>finalTitle</span><span>,</span></span></div></div><div><div><div>133</div></div><div><span>          </span><span>language</span><span>:</span><span><span> </span><span>config</span><span>.</span></span><span>language</span><span>,</span></div></div><div><div><div>134</div></div><div><span>          </span><span>perPage</span><span>:</span><span><span> </span><span>config</span><span>.</span></span><span>perPage</span><span>,</span></div></div><div><div><div>135</div></div><div><span>          </span><span>distractionFreeMode</span><span>:</span><span><span> </span><span>config</span><span>.</span></span><span>distractionFreeMode</span><span>,</span></div></div><div><div><div>136</div></div><div><span>          </span><span>pagerDirection</span><span>:</span><span><span> </span><span>config</span><span>.</span></span><span>pagerDirection</span><span>,</span></div></div><div><div><div>137</div></div><div><span>          </span><span>createIssueManually</span><span>:</span><span><span> </span><span>config</span><span>.</span></span><span>createIssueManually</span><span>,</span></div></div><div><div><div>138</div></div><div><span>          </span><span>enableHotKey</span><span>:</span><span><span> </span><span>config</span><span>.</span></span><span>enableHotKey</span><span>,</span></div></div><div><div><div>139</div></div><div><span><span>        </span></span><span>});</span></div></div><div><div><div>140</div></div><div>
</div></div><div><div><div>141</div></div><div><span><span>        </span></span><span>cotalk</span><span>.</span><span>render</span><span><span>(</span><span>container</span><span>);</span></span></div></div><div><div><div>142</div></div><div><span><span>      </span></span><span>} </span><span>catch</span><span><span> (</span><span>e</span><span>) {</span></span></div></div><div><div><div>143</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>'CoTalk 初始化失败:'</span><span><span>, </span><span>e</span><span>);</span></span></div></div><div><div><div>144</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>145</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>146</div></div><div>
</div></div><div><div><div>147</div></div><div><span>    </span><span>// 首次加载</span></div></div><div><div><div>148</div></div><div><span>    </span><span>initCotalk</span><span>();</span></div></div><div><div><div>149</div></div><div>
</div></div><div><div><div>150</div></div><div><span>    </span><span>// SWUP 无刷新页面切换</span></div></div><div><div><div>151</div></div><div><span>    </span><span>if</span><span><span> (</span><span>window</span><span>.</span></span><span>swup</span><span>) {</span></div></div><div><div><div>152</div></div><div><span><span>      </span></span><span>window</span><span>.</span><span>swup</span><span>.</span><span>hooks</span><span>.</span><span>on</span><span>(</span><span>'page:view'</span><span><span>, </span><span>initCotalk</span><span>);</span></span></div></div><div><div><div>153</div></div><div><span><span>      </span></span><span>document</span><span>.</span><span>addEventListener</span><span>(</span><span>'swup:contentReplaced'</span><span><span>, </span><span>initCotalk</span><span>);</span></span></div></div><div><div><div>154</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>155</div></div><div>
</div></div><div><div><div>156</div></div><div><span>    </span><span>// 主题切换时重新渲染</span></div></div><div><div><div>157</div></div><div><span>    </span><span>const</span><span> </span><span>observer</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>MutationObserver</span><span><span>((</span><span>mutations</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>158</div></div><div><span>      </span><span>for</span><span> (</span><span>const</span><span> </span><span>mutation</span><span> </span><span>of</span><span><span> </span><span>mutations</span><span>) {</span></span></div></div><div><div><div>159</div></div><div><span>        </span><span>if</span><span><span> (</span><span>mutation</span><span>.</span></span><span>type</span><span> </span><span>===</span><span> </span><span>'attributes'</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>mutation</span><span>.</span></span><span>attributeName</span><span> </span><span>===</span><span> </span><span>'class'</span><span>) {</span></div></div><div><div><div>160</div></div><div><span>          </span><span>initCotalk</span><span>();</span></div></div><div><div><div>161</div></div><div><span>          </span><span>break</span><span>;</span></div></div><div><div><div>162</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>163</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>164</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>165</div></div><div><span><span>    </span></span><span>observer</span><span>.</span><span>observe</span><span><span>(</span><span>document</span><span>.</span></span><span>documentElement</span><span>, {</span></div></div><div><div><div>166</div></div><div><span>      </span><span>attributes</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>167</div></div><div><span>      </span><span>attributeFilter</span><span>:</span><span> [</span><span>'class'</span><span>],</span></div></div><div><div><div>168</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>169</div></div><div><span><span>  </span></span><span>})();</span></div></div><div><div><div>170</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>171</div></div><div>
</div></div><div><div><div>172</div></div><div><span>&lt;</span><span>style</span><span> </span><span>is:global</span><span>&gt;</span></div></div><div><div><div>173</div></div><div><span>  </span><span>#cotalk-container</span><span> {</span></div></div><div><div><div>174</div></div><div><span><span>    </span></span><span>width: </span><span><span>100</span><span>%</span></span><span>;</span></div></div><div><div><div>175</div></div><div><span><span>    </span></span><span>margin-top: </span><span><span>2</span><span>rem</span></span><span>;</span></div></div><div><div><div>176</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>177</div></div><div><span>&lt;/</span><span>style</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>接下来，在 <code>src/pages/posts/[...slug].astro</code> 中引入 Cotalk：</p><div><figure><figcaption><span>src/pages/posts/[...slug].astro</span></figcaption><pre><code><div><div><div>1</div></div><div><span>---</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>path</span><span> </span><span>from</span><span> </span><span>"node:path"</span><span>;</span></div></div><div><div><div>3</div></div><div><span>import</span><span> { </span><span>render</span><span> } </span><span>from</span><span> </span><span>"astro:content"</span><span>;</span></div></div><div><div><div>4</div></div><div><span>import</span><span> </span><span>License</span><span> </span><span>from</span><span> </span><span>"@components/misc/License.astro"</span><span>;</span></div></div><div><div><div>5</div></div><div><span>import</span><span> </span><span>Markdown</span><span> </span><span>from</span><span> </span><span>"@components/misc/Markdown.astro"</span><span>;</span></div></div><div><div><div>6</div></div><div><span>import</span><span> </span><span>Cotalk</span><span> </span><span>from</span><span> </span><span>"../../components/misc/Cotalk.astro"</span><span>;</span></div></div><div><div><div>7</div></div><div><span>import</span><span> </span><span>BlogInvitationCard</span><span> </span><span>from</span><span> </span><span>'@components/BlogInvitationCard.astro'</span><span>;</span></div></div><div><div><div>8</div></div><div><span>import</span><span> </span><span>I18nKey</span><span> </span><span>from</span><span> </span><span>"@i18n/i18nKey"</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后在许可证组件之后添加 Cotalk 组件：</p><div><div><div><figure><figcaption><span>src/pages/posts/[...slug].astro</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>{</span><span>licenseConfig</span><span>.</span><span>enable</span><span> </span><span>&amp;&amp;</span><span> &lt;</span><span>License</span><span> </span><span>title</span><span><span>=</span><span>{</span></span><span>entry</span><span>.</span><span>data</span><span>.</span><span>title</span><span><span>}</span><span> </span></span><span>slug</span><span><span>=</span><span>{</span></span><span>entry</span><span>.</span><span>id</span><span><span>}</span><span> </span></span><span>pubDate</span><span><span>=</span><span>{</span></span><span>entry</span><span>.</span><span>data</span><span>.</span><span>published</span><span><span>}</span><span> </span></span><span>class</span><span>=</span><span>"mb-6 rounded-xl license-container onload-animation"</span><span>&gt;&lt;/</span><span>License</span><span>&gt;</span><span>}</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span><span>   </span></span><span>&lt;</span></span><span>Cotalk</span></div></div><div><div><div>5</div></div><div><span>    </span><span>clientID</span><span>=</span><span>"yourclientid"</span></div></div><div><div><div>6</div></div><div><span>    </span><span>proxy</span><span>=</span><span>"yourworkerurl"</span></div></div><div><div><div>7</div></div><div><span>    </span><span>repo</span><span>=</span><span>"repo"</span></div></div><div><div><div>8</div></div><div><span>    </span><span>owner</span><span>=</span><span>"yourusername"</span></div></div><div><div><div>9</div></div><div><span>    </span><span>admin</span><span>=</span><span>{</span><span>[</span><span>'yourusername'</span><span>]</span><span>}</span></div></div><div><div><div>10</div></div><div><span>    </span><span>id</span><span>=</span><span>{</span><span>Astro</span><span>.</span><span>url</span><span>.</span><span>pathname</span><span>}</span></div></div><div><div><div>11</div></div><div><span>    </span><span>language</span><span>=</span><span>"zh-CN"</span></div></div><div><div><div>12</div></div><div><span><span><span>    </span></span><span>/&gt;</span></span></div></div><div><div><div>13</div></div><div><span><span><span>  </span></span><span>&lt;/</span></span><span>div</span><span>&gt;</span></div></div><div><div><div>14</div></div><div><span><span><span> </span></span><span>&lt;/</span></span><span>div</span><span>&gt;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"flex flex-col md:flex-row justify-between mb-4 gap-4 overflow-hidden w-full"</span><span>&gt;</span></div></div><div><div><div>17</div></div><div><span><span> </span></span><span>&lt;</span><span>a</span><span> </span><span>href</span><span>=</span><span>{</span><span>entry</span><span>.</span><span>data</span><span>.</span><span>nextSlug</span><span> </span><span>?</span><span> </span><span>getPostUrlBySlug</span><span>(</span><span>entry</span><span>.</span><span>data</span><span>.</span><span>nextSlug</span><span>) </span><span>:</span><span> </span><span>"#"</span><span>}</span></div></div><div><div><div>18</div></div><div><span>  </span><span>class:list</span><span>=</span><span>{</span><span>[</span><span>"w-full font-bold overflow-hidden active:scale-95"</span><span>, {</span><span>"pointer-events-none"</span><span>:</span><span> </span><span>!</span><span>entry</span><span>.</span><span>data</span><span>.</span><span>nextSlug</span><span>}]</span><span>}</span><span>&gt;</span></div></div><div><div><div>19</div></div><div><span>  </span><span>{</span><span>entry</span><span>.</span><span>data</span><span>.</span><span>nextSlug</span><span> </span><span>&amp;&amp;</span><span> &lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"btn-card rounded-2xl w-full h-[3.75rem] max-w-full px-4 flex items-center !justify-start gap-4"</span><span> &gt;</span></div></div><div><div><div>20</div></div><div><span><span>   </span></span><span>&lt;</span><span>Icon</span><span> </span><span>name</span><span>=</span><span>"material-symbols:chevron-left-rounded"</span><span> </span><span>class</span><span>=</span><span>"text-[2rem] text-[var(--primary)]"</span><span> /&gt;</span></div></div><div><div><div>21</div></div><div><span><span>   </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"overflow-hidden transition overflow-ellipsis whitespace-nowrap max-w-[calc(100%_-_3rem)] text-black/75 dark:text-white/75"</span><span>&gt;</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>{</span><span>entry</span><span>.</span><span>data</span><span>.</span><span>nextTitle</span><span>}</span></div></div><div><div><div>23</div></div><div><span><span>   </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>&lt;/</span><span>div</span><span>&gt;</span><span>}</span></div></div><div><div><div>25</div></div><div><span><span> </span></span><span>&lt;/</span><span>a</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>保存文件后重新构建项目即可看到评论区。</p><p>通过以上步骤，你就成功为 Fuwari 添加了功能完善的评论系统！</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/cc-license/</id>
      <title type="text">啥是 CC 协议？</title>
      <published>2026-03-26T00:00:00.000Z</published>
      <updated>2026-03-26T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/cc-license/"/>
      <summary type="text">本文介绍CC协议（知识共享许可），一种公共版权许可。核心要素包括：BY（署名）、NC（非商业）、ND（禁止修改）、SA（相同方式共享），其中SA与ND互斥。它们可组合成不同协议，如作者使用的CC BY-NC-SA。另有CC0协议，代表放弃版权，作品进入公共领域。</summary>
      <content type="html"><![CDATA[<p>当你看到这个博客的文章结尾都写着 <code>许可协议 CC BY-SA 4.0</code> 时，你可能会疑惑：CC 协议是什么？</p>
<p>这篇文章就简单的来说一下 CC 协议。</p>
<p>首先，我们要知道 CC 协议是一种公共版权许可。</p>
<p>这是他们的官网：<a href="https://creativecommons.org/" target="_blank">https://creativecommons.org/</a></p>
<p>CC 协议拥有多种授权要素，这里介绍一下几种常用的。</p>
<p>BY(Attribution): 要求在他人在使用某作品时，须注明原作者。</p>
<p>NC(Non Commercial): 要求在他人在使用某作品时，禁止用于商业用途。</p>
<p>ND(No Derivatives): 要求在他人在使用某作品时，禁止修改。</p>
<p>SA(ShareAlike): 要求在他人在使用某作品时，须使用相同协议发布。</p>
<p>注：SA 与 ND 互斥，无法同时使用。</p>
<p>这些授权要素可以相互组合。</p>
<p>比如，我的博客使用 <code>CC BY-A 协议</code>，而自由软件中文网则使用 <code>CC BY-SA 协议</code>，所以，自由软件中文网的文章可以商用，我的博客上面的文章也可以。</p>
<p>还有一种特殊的 CC 协议：CC0 , 它要求原作者放弃在作品上的全部著作权和邻接权，使作品进入公共领域，成为人类共同的知识财产，之后任何⼈都可以在不标示作者和来源的情况下⽆条件地将作品⽤于商业⽤途，⽆需经过任何申请，权利⼈或所有⼈也⽆权再追究责任。</p>
<p>这就是 CC 协议。</p>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/changes-fuwari/</id>
      <title type="text">对 Fuwari 进行亿些改动（已存档）</title>
      <published>2026-03-20T00:00:00.000Z</published>
      <updated>2026-03-20T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/changes-fuwari/"/>
      <summary type="text">对 Fuwari 进行的改动总结</summary>
      <content type="html"><![CDATA[<section><h1>1. 增加评论<a href="#1-增加评论"><span>#</span></a></h1><p>参考<a href="https://blog.tianhw.top/posts/fuwari-giscus/" target="_blank">这篇文章</a></p></section>
<section><h1>2. 增加友链<a href="#2-增加友链"><span>#</span></a></h1><p>参考<a href="https://blog.aulypc0x0.online/posts/website/add_friendspage_in_fuwari/" target="_blank">这篇文章</a></p></section>
<section><h1>3. 增加访问统计卡片<a href="#3-增加访问统计卡片"><span>#</span></a></h1><p>参考<a href="https://blog.tianhw.top/posts/fuwari-umami-stats/" target="_blank">这篇文章</a></p></section>
<section><h1>4. 图片标题<a href="#4-图片标题"><span>#</span></a></h1><p>参考<a href="https://github.com/saicaca/fuwari/pull/351" target="_blank">这个 PR</a></p></section>
<section><h1>5. 音乐播放器<a href="#5-音乐播放器"><span>#</span></a></h1><p>参考<a href="https://ikamusume7.org/posts/frontend/some_small_code_changes2/#%E5%9B%9B%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E5%99%A8" target="_blank">这篇文章</a></p></section>
<section><h1>6. Codeberg 仓库卡片<a href="#6-codeberg-仓库卡片"><span>#</span></a></h1><p>在 <code>src/plugins</code> 新建 <code>rehype-component-codeberg-card.mjs</code> 文件，并写入以下内容：</p><div><div><div><figure><figcaption><span>rehype-component-codeberg-card.mjs</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>/// </span><span>&lt;</span><span>reference</span><span> </span><span>types</span><span>=</span><span>"mdast"</span><span> /&gt;</span></div></div><div><div><div>2</div></div><div><span>import</span><span> { </span><span>h</span><span> } </span><span>from</span><span> </span><span>"hastscript"</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/**</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* Creates a Codeberg Card component.</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>*</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>{Object}</span><span> </span><span>properties</span><span> - The properties of the component.</span></div></div><div><div><div>8</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>{string}</span><span> </span><span>properties.repo</span><span> - The Codeberg repository in the format "owner/repo".</span></div></div><div><div><div>9</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>{import('mdast').RootContent[]}</span><span> </span><span>children</span><span> - The children elements of the component.</span></div></div><div><div><div>10</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>returns</span><span> </span><span>{import('mdast').Parent}</span><span> The created Codeberg Card component.</span></div></div><div><div><div>11</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>12</div></div><div><span>export</span><span> </span><span>function</span><span> </span><span>CodebergCardComponent</span><span><span>(</span><span>properties</span><span>, </span><span>children</span><span>) {</span></span></div></div><div><div><div>13</div></div><div><span> </span><span>if</span><span><span> (</span><span>Array</span><span>.</span></span><span>isArray</span><span><span>(</span><span>children</span><span>) </span></span><span>&amp;&amp;</span><span><span> </span><span>children</span><span>.</span></span><span>length</span><span> </span><span>!==</span><span> </span><span>0</span><span>)</span></div></div><div><div><div>14</div></div><div><span>  </span><span>return</span><span> </span><span>h</span><span>(</span><span>"div"</span><span>, { </span><span>class</span><span>:</span><span> </span><span>"hidden"</span><span> }, [</span></div></div><div><div><div>15</div></div><div><span>   </span><span>'Invalid directive. ("codeberg" directive must be leaf type "::codeberg{repo="owner/repo"}")'</span><span>,</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>]);</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span> </span><span>if</span><span> (</span><span>!</span><span><span>properties</span><span>.</span></span><span>repo</span><span> </span><span>||</span><span> </span><span>!</span><span><span>properties</span><span>.</span></span><span>repo</span><span>.</span><span>includes</span><span>(</span><span>"/"</span><span>))</span></div></div><div><div><div>19</div></div><div><span>  </span><span>return</span><span> </span><span>h</span><span>(</span></div></div><div><div><div>20</div></div><div><span>   </span><span>"div"</span><span>,</span></div></div><div><div><div>21</div></div><div><span><span>   </span></span><span>{ </span><span>class</span><span>:</span><span> </span><span>"hidden"</span><span> },</span></div></div><div><div><div>22</div></div><div><span>   </span><span>'Invalid repository. ("repo" attributte must be in the format "owner/repo")'</span><span>,</span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span> </span><span>const</span><span> </span><span>repo</span><span> </span><span>=</span><span><span> </span><span>properties</span><span>.</span></span><span>repo</span><span>;</span></div></div><div><div><div>26</div></div><div><span> </span><span>const</span><span> </span><span>cardUuid</span><span> </span><span>=</span><span> </span><span>`GC</span><span>${</span><span>Math</span><span>.</span><span>random</span><span>()</span><span>.</span><span>toString</span><span>(</span><span>36</span><span>)</span><span>.</span><span>slice</span><span>(</span><span>-</span><span>6</span><span>)</span><span>}</span><span>`</span><span>; </span><span>// Collisions are not important</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span> </span><span>const</span><span> </span><span>nAvatar</span><span> </span><span>=</span><span> </span><span>h</span><span>(</span><span>`div#</span><span>${</span><span>cardUuid</span><span>}</span><span>-avatar`</span><span>, { </span><span>class</span><span>:</span><span> </span><span>"gc-avatar"</span><span> });</span></div></div><div><div><div>29</div></div><div><span> </span><span>const</span><span> </span><span>nLanguage</span><span> </span><span>=</span><span> </span><span>h</span><span>(</span></div></div><div><div><div>30</div></div><div><span>  </span><span>`span#</span><span>${</span><span>cardUuid</span><span>}</span><span>-language`</span><span>,</span></div></div><div><div><div>31</div></div><div><span><span>  </span></span><span>{ </span><span>class</span><span>:</span><span> </span><span>"gc-language"</span><span> },</span></div></div><div><div><div>32</div></div><div><span>  </span><span>"Waiting..."</span><span>,</span></div></div><div><div><div>33</div></div><div><span><span> </span></span><span>);</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span> </span><span>const</span><span> </span><span>nTitle</span><span> </span><span>=</span><span> </span><span>h</span><span>(</span><span>"div"</span><span>, { </span><span>class</span><span>:</span><span> </span><span>"gc-titlebar"</span><span> }, [</span></div></div><div><div><div>36</div></div><div><span>  </span><span>h</span><span>(</span><span>"div"</span><span>, { </span><span>class</span><span>:</span><span> </span><span>"gc-titlebar-left"</span><span> }, [</span></div></div><div><div><div>37</div></div><div><span>   </span><span>h</span><span>(</span><span>"div"</span><span>, { </span><span>class</span><span>:</span><span> </span><span>"gc-owner"</span><span> }, [</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>nAvatar</span><span>,</span></div></div><div><div><div>39</div></div><div><span>    </span><span>h</span><span>(</span><span>"div"</span><span>, { </span><span>class</span><span>:</span><span> </span><span>"gc-user"</span><span><span> }, </span><span>repo</span><span>.</span></span><span>split</span><span>(</span><span>"/"</span><span>)[</span><span>0</span><span>]),</span></div></div><div><div><div>40</div></div><div><span><span>   </span></span><span>]),</span></div></div><div><div><div>41</div></div><div><span>   </span><span>h</span><span>(</span><span>"div"</span><span>, { </span><span>class</span><span>:</span><span> </span><span>"gc-divider"</span><span> }, </span><span>"/"</span><span>),</span></div></div><div><div><div>42</div></div><div><span>   </span><span>h</span><span>(</span><span>"div"</span><span>, { </span><span>class</span><span>:</span><span> </span><span>"gc-repo"</span><span><span> }, </span><span>repo</span><span>.</span></span><span>split</span><span>(</span><span>"/"</span><span>)[</span><span>1</span><span>]),</span></div></div><div><div><div>43</div></div><div><span><span>  </span></span><span>]),</span></div></div><div><div><div>44</div></div><div><span>  </span><span>h</span><span>(</span><span>"div"</span><span>, { </span><span>class</span><span>:</span><span> </span><span>"codeberg-logo"</span><span> }),</span></div></div><div><div><div>45</div></div><div><span><span> </span></span><span>]);</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span> </span><span>const</span><span> </span><span>nDescription</span><span> </span><span>=</span><span> </span><span>h</span><span>(</span></div></div><div><div><div>48</div></div><div><span>  </span><span>`div#</span><span>${</span><span>cardUuid</span><span>}</span><span>-description`</span><span>,</span></div></div><div><div><div>49</div></div><div><span><span>  </span></span><span>{ </span><span>class</span><span>:</span><span> </span><span>"gc-description"</span><span> },</span></div></div><div><div><div>50</div></div><div><span>  </span><span>"Waiting for codeberg.org/api/v1..."</span><span>,</span></div></div><div><div><div>51</div></div><div><span><span> </span></span><span>);</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span> </span><span>const</span><span> </span><span>nStars</span><span> </span><span>=</span><span> </span><span>h</span><span>(</span><span>`div#</span><span>${</span><span>cardUuid</span><span>}</span><span>-stars`</span><span>, { </span><span>class</span><span>:</span><span> </span><span>"gc-stars"</span><span> }, </span><span>"00K"</span><span>);</span></div></div><div><div><div>54</div></div><div><span> </span><span>const</span><span> </span><span>nForks</span><span> </span><span>=</span><span> </span><span>h</span><span>(</span><span>`div#</span><span>${</span><span>cardUuid</span><span>}</span><span>-forks`</span><span>, { </span><span>class</span><span>:</span><span> </span><span>"gc-forks"</span><span> }, </span><span>"0K"</span><span>);</span></div></div><div><div><div>55</div></div><div><span> </span><span>const</span><span> </span><span>nLicense</span><span> </span><span>=</span><span> </span><span>h</span><span>(</span><span>`div#</span><span>${</span><span>cardUuid</span><span>}</span><span>-license`</span><span>, { </span><span>class</span><span>:</span><span> </span><span>"gc-license"</span><span> }, </span><span>"0K"</span><span>);</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span> </span><span>const</span><span> </span><span>nScript</span><span> </span><span>=</span><span> </span><span>h</span><span>(</span></div></div><div><div><div>58</div></div><div><span>  </span><span>`script#</span><span>${</span><span>cardUuid</span><span>}</span><span>-script`</span><span>,</span></div></div><div><div><div>59</div></div><div><span><span>  </span></span><span>{ </span><span>type</span><span>:</span><span> </span><span>"text/javascript"</span><span>, </span><span>defer</span><span>:</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>60</div></div><div><span>  </span><span>`</span></div></div><div><div><div>61</div></div><div><span><span>      </span></span><span>fetch('https://codeberg.org/api/v1/repos/</span><span>${</span><span>repo</span><span>}</span><span>', { referrerPolicy: "no-referrer" }).then(response =&gt; response.json()).then(data =&gt; {</span></div></div><div><div><div>62</div></div><div><span><span>        </span></span><span>document.getElementById('</span><span>${</span><span>cardUuid</span><span>}</span><span>-description').innerText = data.description?.replace(/:[a-zA-Z0-9_]+:/g, '') || "Description not set";</span></div></div><div><div><div>63</div></div><div><span><span>        </span></span><span>document.getElementById('</span><span>${</span><span>cardUuid</span><span>}</span><span>-language').innerText = data.language;</span></div></div><div><div><div>64</div></div><div><span><span>        </span></span><span>document.getElementById('</span><span>${</span><span>cardUuid</span><span>}</span><span>-forks').innerText = Intl.NumberFormat('en-us', { notation: "compact", maximumFractionDigits: 1 }).format(data.forks_count).replaceAll("</span><span>\u202f</span><span>", '');</span></div></div><div><div><div>65</div></div><div><span><span>        </span></span><span>document.getElementById('</span><span>${</span><span>cardUuid</span><span>}</span><span>-stars').innerText = Intl.NumberFormat('en-us', { notation: "compact", maximumFractionDigits: 1 }).format(data.stars_count).replaceAll("</span><span>\u202f</span><span>", '');</span></div></div><div><div><div>66</div></div><div><span><span>        </span></span><span>const avatarEl = document.getElementById('</span><span>${</span><span>cardUuid</span><span>}</span><span>-avatar');</span></div></div><div><div><div>67</div></div><div><span><span>        </span></span><span>avatarEl.style.backgroundImage = 'url(' + data.owner.avatar_url + ')';</span></div></div><div><div><div>68</div></div><div><span><span>        </span></span><span>avatarEl.style.backgroundColor = 'transparent';</span></div></div><div><div><div>69</div></div><div><span><span>        </span></span><span>// Codeberg API does not provide license information, hiding the license field</span></div></div><div><div><div>70</div></div><div><span><span>        </span></span><span>document.getElementById('</span><span>${</span><span>cardUuid</span><span>}</span><span>-license').style.display = 'none';</span></div></div><div><div><div>71</div></div><div><span><span>        </span></span><span>document.getElementById('</span><span>${</span><span>cardUuid</span><span>}</span><span>-card').classList.remove("fetch-waiting");</span></div></div><div><div><div>72</div></div><div><span><span>        </span></span><span>console.log("[CODEBERG-CARD] Loaded card for </span><span>${</span><span>repo</span><span>}</span><span> | </span><span>${</span><span>cardUuid</span><span>}</span><span>.")</span></div></div><div><div><div>73</div></div><div><span><span>      </span></span><span>}).catch(err =&gt; {</span></div></div><div><div><div>74</div></div><div><span><span>        </span></span><span>const c = document.getElementById('</span><span>${</span><span>cardUuid</span><span>}</span><span>-card');</span></div></div><div><div><div>75</div></div><div><span><span>        </span></span><span>c?.classList.add("fetch-error");</span></div></div><div><div><div>76</div></div><div><span><span>        </span></span><span>console.warn("[CODEBERG-CARD] (Error) Loading card for </span><span>${</span><span>repo</span><span>}</span><span> | </span><span>${</span><span>cardUuid</span><span>}</span><span>.")</span></div></div><div><div><div>77</div></div><div><span><span>      </span></span><span>})</span></div></div><div><div><div>78</div></div><div><span><span>    </span></span><span>`</span><span>,</span></div></div><div><div><div>79</div></div><div><span><span> </span></span><span>);</span></div></div><div><div><div>80</div></div><div>
</div></div><div><div><div>81</div></div><div><span> </span><span>return</span><span> </span><span>h</span><span>(</span></div></div><div><div><div>82</div></div><div><span>  </span><span>`a#</span><span>${</span><span>cardUuid</span><span>}</span><span>-card`</span><span>,</span></div></div><div><div><div>83</div></div><div><span><span>  </span></span><span>{</span></div></div><div><div><div>84</div></div><div><span>   </span><span>class</span><span>:</span><span> </span><span>"card-codeberg fetch-waiting no-styling"</span><span>,</span></div></div><div><div><div>85</div></div><div><span>   </span><span>href</span><span>:</span><span> </span><span>`https://codeberg.org/</span><span>${</span><span>repo</span><span>}</span><span>`</span><span>,</span></div></div><div><div><div>86</div></div><div><span>   </span><span>target</span><span>:</span><span> </span><span>"_blank"</span><span>,</span></div></div><div><div><div>87</div></div><div><span><span>   </span></span><span>repo</span><span>,</span></div></div><div><div><div>88</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>89</div></div><div><span><span>  </span></span><span>[</span></div></div><div><div><div>90</div></div><div><span><span>   </span></span><span>nTitle</span><span>,</span></div></div><div><div><div>91</div></div><div><span><span>   </span></span><span>nDescription</span><span>,</span></div></div><div><div><div>92</div></div><div><span>   </span><span>h</span><span>(</span><span>"div"</span><span>, { </span><span>class</span><span>:</span><span> </span><span>"gc-infobar"</span><span><span> }, [</span><span>nStars</span><span>, </span><span>nForks</span><span>, </span><span>nLicense</span><span>, </span><span>nLanguage</span><span>]),</span></span></div></div><div><div><div>93</div></div><div><span><span>   </span></span><span>nScript</span><span>,</span></div></div><div><div><div>94</div></div><div><span><span>  </span></span><span>],</span></div></div><div><div><div>95</div></div><div><span><span> </span></span><span>);</span></div></div><div><div><div>96</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>然后，在 <code>astro.config.mjs</code> 中添加以下内容：</p><div><div><div><figure><figcaption><span>astro.config.mjs</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div></div></div><div><span></span><span></span><span>20 collapsed lines</span></div></div><div><div><div>1</div></div><div><span>import</span><span> </span><span>sitemap</span><span> </span><span>from</span><span> </span><span>"@astrojs/sitemap"</span><span>;</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>svelte</span><span> </span><span>from</span><span> </span><span>"@astrojs/svelte"</span><span>;</span></div></div><div><div><div>3</div></div><div><span>import</span><span> </span><span>tailwind</span><span> </span><span>from</span><span> </span><span>"@astrojs/tailwind"</span><span>;</span></div></div><div><div><div>4</div></div><div><span>import</span><span> </span><span>mdx</span><span> </span><span>from</span><span> </span><span>'@astrojs/mdx'</span><span>;</span></div></div><div><div><div>5</div></div><div><span>import</span><span> { </span><span>pluginCollapsibleSections</span><span> } </span><span>from</span><span> </span><span>"@expressive-code/plugin-collapsible-sections"</span><span>;</span></div></div><div><div><div>6</div></div><div><span>import</span><span> { </span><span>pluginLineNumbers</span><span> } </span><span>from</span><span> </span><span>"@expressive-code/plugin-line-numbers"</span><span>;</span></div></div><div><div><div>7</div></div><div><span>import</span><span> </span><span>swup</span><span> </span><span>from</span><span> </span><span>"@swup/astro"</span><span>;</span></div></div><div><div><div>8</div></div><div><span>import</span><span> </span><span>expressiveCode</span><span> </span><span>from</span><span> </span><span>"astro-expressive-code"</span><span>;</span></div></div><div><div><div>9</div></div><div><span>import</span><span> </span><span>icon</span><span> </span><span>from</span><span> </span><span>"astro-icon"</span><span>;</span></div></div><div><div><div>10</div></div><div><span>import</span><span> { </span><span>defineConfig</span><span> } </span><span>from</span><span> </span><span>"astro/config"</span><span>;</span></div></div><div><div><div>11</div></div><div><span>import</span><span> </span><span>rehypeAutolinkHeadings</span><span> </span><span>from</span><span> </span><span>"rehype-autolink-headings"</span><span>;</span></div></div><div><div><div>12</div></div><div><span>import</span><span> </span><span>rehypeComponents</span><span> </span><span>from</span><span> </span><span>"rehype-components"</span><span>; </span><span>/* Render the custom directive content */</span></div></div><div><div><div>13</div></div><div><span>import</span><span> </span><span>rehypeKatex</span><span> </span><span>from</span><span> </span><span>"rehype-katex"</span><span>;</span></div></div><div><div><div>14</div></div><div><span>import</span><span> </span><span>rehypeSlug</span><span> </span><span>from</span><span> </span><span>"rehype-slug"</span><span>;</span></div></div><div><div><div>15</div></div><div><span>import</span><span> </span><span>remarkDirective</span><span> </span><span>from</span><span> </span><span>"remark-directive"</span><span>; </span><span>/* Handle directives */</span></div></div><div><div><div>16</div></div><div><span>import</span><span> </span><span>remarkGithubAdmonitionsToDirectives</span><span> </span><span>from</span><span> </span><span>"remark-github-admonitions-to-directives"</span><span>;</span></div></div><div><div><div>17</div></div><div><span>import</span><span> </span><span>remarkMath</span><span> </span><span>from</span><span> </span><span>"remark-math"</span><span>;</span></div></div><div><div><div>18</div></div><div><span>import</span><span> </span><span>remarkSectionize</span><span> </span><span>from</span><span> </span><span>"remark-sectionize"</span><span>;</span></div></div><div><div><div>19</div></div><div><span>import</span><span> { </span><span>expressiveCodeConfig</span><span> } </span><span>from</span><span> </span><span>"./src/config.ts"</span><span>;</span></div></div><div><div><div>20</div></div><div><span>import</span><span> { </span><span>pluginLanguageBadge</span><span> } </span><span>from</span><span> </span><span>"./src/plugins/expressive-code/language-badge.ts"</span><span>;</span></div></div><div><div><div>21</div></div><div><span>import</span><span> { </span><span>AdmonitionComponent</span><span> } </span><span>from</span><span> </span><span>"./src/plugins/rehype-component-admonition.mjs"</span><span>;</span></div></div><div><div><div>22</div></div><div><span>import</span><span> { </span><span>CodebergCardComponent</span><span> } </span><span>from</span><span> </span><span>"./src/plugins/rehype-component-codeberg-card.mjs"</span><span>;</span></div></div><div><div><div>23</div></div><div><span>import</span><span> { </span><span>GithubCardComponent</span><span> } </span><span>from</span><span> </span><span>"./src/plugins/rehype-component-github-card.mjs"</span><span>;</span></div></div><div><div><div></div></div><div><span></span><span></span><span>110 collapsed lines</span></div></div><div><div><div>24</div></div><div><span>import</span><span> { </span><span>parseDirectiveNode</span><span> } </span><span>from</span><span> </span><span>"./src/plugins/remark-directive-rehype.js"</span><span>;</span></div></div><div><div><div>25</div></div><div><span>import</span><span> { </span><span>remarkExcerpt</span><span> } </span><span>from</span><span> </span><span>"./src/plugins/remark-excerpt.js"</span><span>;</span></div></div><div><div><div>26</div></div><div><span>import</span><span> { </span><span>remarkReadingTime</span><span> } </span><span>from</span><span> </span><span>"./src/plugins/remark-reading-time.mjs"</span><span>;</span></div></div><div><div><div>27</div></div><div><span>import</span><span> { </span><span>pluginCustomCopyButton</span><span> } </span><span>from</span><span> </span><span>"./src/plugins/expressive-code/custom-copy-button.js"</span><span>;</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>// https://astro.build/config</span></div></div><div><div><div>30</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>defineConfig</span><span>({</span></div></div><div><div><div>31</div></div><div><span> </span><span>site</span><span>:</span><span> </span><span>"https://cli.loc.cc"</span><span>,</span></div></div><div><div><div>32</div></div><div><span> </span><span>base</span><span>:</span><span> </span><span>"/"</span><span>,</span></div></div><div><div><div>33</div></div><div><span> </span><span>trailingSlash</span><span>:</span><span> </span><span>"always"</span><span>,</span></div></div><div><div><div>34</div></div><div><span> </span><span>image</span><span>:</span><span> {</span></div></div><div><div><div>35</div></div><div><span>  </span><span>// 全局响应式布局</span></div></div><div><div><div>36</div></div><div><span>  </span><span>layout</span><span>:</span><span> </span><span>"constrained"</span><span>,</span></div></div><div><div><div>37</div></div><div><span><span> </span></span><span>},</span></div></div><div><div><div>38</div></div><div><span> </span><span>experimental</span><span>:</span><span> {</span></div></div><div><div><div>39</div></div><div><span>  </span><span>// Rust 编译器以提升构建性能（实验性）</span></div></div><div><div><div>40</div></div><div><span>  </span><span>// 队列渲染以优化性能（实验性）</span></div></div><div><div><div>41</div></div><div><span>  </span><span>queuedRendering</span><span>:</span><span> { </span><span>enabled</span><span>:</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>42</div></div><div><span><span> </span></span><span>},</span></div></div><div><div><div>43</div></div><div><span> </span><span>integrations</span><span>:</span><span> [</span></div></div><div><div><div>44</div></div><div><span>  </span><span>tailwind</span><span>({</span></div></div><div><div><div>45</div></div><div><span>   </span><span>nesting</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>46</div></div><div><span><span>  </span></span><span>}),</span></div></div><div><div><div>47</div></div><div><span>  </span><span>swup</span><span>({</span></div></div><div><div><div>48</div></div><div><span>   </span><span>theme</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>49</div></div><div><span>   </span><span>animationClass</span><span>:</span><span> </span><span>"transition-swup-"</span><span>, </span><span>// see https://swup.js.org/options/#animationselector</span></div></div><div><div><div>50</div></div><div><span>   </span><span>// the default value `transition-` cause transition delay</span></div></div><div><div><div>51</div></div><div><span>   </span><span>// when the Tailwind class `transition-all` is used</span></div></div><div><div><div>52</div></div><div><span>   </span><span>containers</span><span>:</span><span> [</span><span>"main"</span><span>, </span><span>"#toc"</span><span>],</span></div></div><div><div><div>53</div></div><div><span>   </span><span>smoothScrolling</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>54</div></div><div><span>   </span><span>cache</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>55</div></div><div><span>   </span><span>preload</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>56</div></div><div><span>   </span><span>accessibility</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>57</div></div><div><span>   </span><span>updateHead</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>58</div></div><div><span>   </span><span>updateBodyClass</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>59</div></div><div><span>   </span><span>globalInstance</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>60</div></div><div><span><span>  </span></span><span>}),</span></div></div><div><div><div>61</div></div><div><span>  </span><span>icon</span><span>({</span></div></div><div><div><div>62</div></div><div><span>   </span><span>include</span><span>:</span><span> {</span></div></div><div><div><div>63</div></div><div><span>    </span><span>"preprocess: vitePreprocess(),"</span><span>:</span><span> [</span><span>"*"</span><span>],</span></div></div><div><div><div>64</div></div><div><span>    </span><span>"fa6-brands"</span><span>:</span><span> [</span><span>"*"</span><span>],</span></div></div><div><div><div>65</div></div><div><span>    </span><span>"fa6-regular"</span><span>:</span><span> [</span><span>"*"</span><span>],</span></div></div><div><div><div>66</div></div><div><span>    </span><span>"fa6-solid"</span><span>:</span><span> [</span><span>"*"</span><span>],</span></div></div><div><div><div>67</div></div><div><span><span>   </span></span><span>},</span></div></div><div><div><div>68</div></div><div><span><span>  </span></span><span>}),</span></div></div><div><div><div>69</div></div><div><span>  </span><span>expressiveCode</span><span>({</span></div></div><div><div><div>70</div></div><div><span>   </span><span>themes</span><span>:</span><span><span> [</span><span>expressiveCodeConfig</span><span>.</span></span><span>darkTheme</span><span><span>, </span><span>expressiveCodeConfig</span><span>.</span></span><span>lightTheme</span><span>],</span></div></div><div><div><div>71</div></div><div><span>   </span><span>useDarkModeMediaQuery</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>72</div></div><div><span>   </span><span>plugins</span><span>:</span><span> [</span></div></div><div><div><div>73</div></div><div><span>    </span><span>pluginCollapsibleSections</span><span>(),</span></div></div><div><div><div>74</div></div><div><span>    </span><span>pluginLineNumbers</span><span>(),</span></div></div><div><div><div>75</div></div><div><span>    </span><span>pluginLanguageBadge</span><span>(),</span></div></div><div><div><div>76</div></div><div><span>    </span><span>pluginCustomCopyButton</span><span>()</span></div></div><div><div><div>77</div></div><div><span><span>   </span></span><span>],</span></div></div><div><div><div>78</div></div><div><span>   </span><span>defaultProps</span><span>:</span><span> {</span></div></div><div><div><div>79</div></div><div><span>    </span><span>wrap</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>80</div></div><div><span>    </span><span>overridesByLang</span><span>:</span><span> {</span></div></div><div><div><div>81</div></div><div><span>     </span><span>shellsession</span><span>:</span><span> {</span></div></div><div><div><div>82</div></div><div><span>      </span><span>showLineNumbers</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>83</div></div><div><span><span>     </span></span><span>},</span></div></div><div><div><div>84</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>85</div></div><div><span><span>   </span></span><span>},</span></div></div><div><div><div>86</div></div><div><span>   </span><span>styleOverrides</span><span>:</span><span> {</span></div></div><div><div><div>87</div></div><div><span>    </span><span>codeBackground</span><span>:</span><span> </span><span>"var(--codeblock-bg)"</span><span>,</span></div></div><div><div><div>88</div></div><div><span>    </span><span>borderRadius</span><span>:</span><span> </span><span>"0.75rem"</span><span>,</span></div></div><div><div><div>89</div></div><div><span>    </span><span>borderColor</span><span>:</span><span> </span><span>"none"</span><span>,</span></div></div><div><div><div>90</div></div><div><span>    </span><span>codeFontSize</span><span>:</span><span> </span><span>"0.875rem"</span><span>,</span></div></div><div><div><div>91</div></div><div><span>    </span><span>codeFontFamily</span><span>:</span><span> </span><span>"'JetBrains Mono Variable', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace"</span><span>,</span></div></div><div><div><div>92</div></div><div><span>    </span><span>codeLineHeight</span><span>:</span><span> </span><span>"1.5rem"</span><span>,</span></div></div><div><div><div>93</div></div><div><span>    </span><span>frames</span><span>:</span><span> {</span></div></div><div><div><div>94</div></div><div><span>     </span><span>editorBackground</span><span>:</span><span> </span><span>"var(--codeblock-bg)"</span><span>,</span></div></div><div><div><div>95</div></div><div><span>     </span><span>terminalBackground</span><span>:</span><span> </span><span>"var(--codeblock-bg)"</span><span>,</span></div></div><div><div><div>96</div></div><div><span>     </span><span>terminalTitlebarBackground</span><span>:</span><span> </span><span>"var(--codeblock-topbar-bg)"</span><span>,</span></div></div><div><div><div>97</div></div><div><span>     </span><span>editorTabBarBackground</span><span>:</span><span> </span><span>"var(--codeblock-topbar-bg)"</span><span>,</span></div></div><div><div><div>98</div></div><div><span>     </span><span>editorActiveTabBackground</span><span>:</span><span> </span><span>"none"</span><span>,</span></div></div><div><div><div>99</div></div><div><span>     </span><span>editorActiveTabIndicatorBottomColor</span><span>:</span><span> </span><span>"var(--primary)"</span><span>,</span></div></div><div><div><div>100</div></div><div><span>     </span><span>editorActiveTabIndicatorTopColor</span><span>:</span><span> </span><span>"none"</span><span>,</span></div></div><div><div><div>101</div></div><div><span>     </span><span>editorTabBarBorderBottomColor</span><span>:</span><span> </span><span>"var(--codeblock-topbar-bg)"</span><span>,</span></div></div><div><div><div>102</div></div><div><span>     </span><span>terminalTitlebarBorderBottomColor</span><span>:</span><span> </span><span>"none"</span></div></div><div><div><div>103</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>104</div></div><div><span>    </span><span>textMarkers</span><span>:</span><span> {</span></div></div><div><div><div>105</div></div><div><span>     </span><span>delHue</span><span>:</span><span> </span><span>0</span><span>,</span></div></div><div><div><div>106</div></div><div><span>     </span><span>insHue</span><span>:</span><span> </span><span>180</span><span>,</span></div></div><div><div><div>107</div></div><div><span>     </span><span>markHue</span><span>:</span><span> </span><span>250</span></div></div><div><div><div>108</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>109</div></div><div><span><span>   </span></span><span>},</span></div></div><div><div><div>110</div></div><div><span>   </span><span>frames</span><span>:</span><span> {</span></div></div><div><div><div>111</div></div><div><span>    </span><span>showCopyToClipboardButton</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>112</div></div><div><span><span>   </span></span><span>}</span></div></div><div><div><div>113</div></div><div><span><span>  </span></span><span>}),</span></div></div><div><div><div>114</div></div><div><span>        </span><span>svelte</span><span>(),</span></div></div><div><div><div>115</div></div><div><span>  </span><span>sitemap</span><span>(),</span></div></div><div><div><div>116</div></div><div><span>  </span><span>mdx</span><span>(),</span></div></div><div><div><div>117</div></div><div><span><span> </span></span><span>],</span></div></div><div><div><div>118</div></div><div><span> </span><span>markdown</span><span>:</span><span> {</span></div></div><div><div><div>119</div></div><div><span>  </span><span>remarkPlugins</span><span>:</span><span> [</span></div></div><div><div><div>120</div></div><div><span>   </span><span>remarkMath</span><span>,</span></div></div><div><div><div>121</div></div><div><span>   </span><span>remarkReadingTime</span><span>,</span></div></div><div><div><div>122</div></div><div><span>   </span><span>remarkExcerpt</span><span>,</span></div></div><div><div><div>123</div></div><div><span>   </span><span>remarkGithubAdmonitionsToDirectives</span><span>,</span></div></div><div><div><div>124</div></div><div><span>   </span><span>remarkDirective</span><span>,</span></div></div><div><div><div>125</div></div><div><span>   </span><span>remarkSectionize</span><span>,</span></div></div><div><div><div>126</div></div><div><span>   </span><span>parseDirectiveNode</span><span>,</span></div></div><div><div><div>127</div></div><div><span><span>  </span></span><span>],</span></div></div><div><div><div>128</div></div><div><span>  </span><span>rehypePlugins</span><span>:</span><span> [</span></div></div><div><div><div>129</div></div><div><span>   </span><span>rehypeKatex</span><span>,</span></div></div><div><div><div>130</div></div><div><span>   </span><span>rehypeSlug</span><span>,</span></div></div><div><div><div>131</div></div><div><span><span>   </span></span><span>[</span></div></div><div><div><div>132</div></div><div><span>    </span><span>rehypeComponents</span><span>,</span></div></div><div><div><div>133</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>134</div></div><div><span>     </span><span>components</span><span>:</span><span> {</span></div></div><div><div><div>135</div></div><div><span>      </span><span>codeberg</span><span>:</span><span> </span><span>CodebergCardComponent</span><span>,</span></div></div><div><div><div>136</div></div><div><span>      </span><span>github</span><span>:</span><span> </span><span>GithubCardComponent</span><span>,</span></div></div><div><div><div></div></div><div><span></span><span></span><span>50 collapsed lines</span></div></div><div><div><div>137</div></div><div><span>      </span><span>note</span><span>:</span><span><span> (</span><span>x</span><span>, </span><span>y</span><span>) </span></span><span>=&gt;</span><span> </span><span>AdmonitionComponent</span><span>(</span><span>x</span><span>, </span><span>y</span><span>, </span><span>"note"</span><span>),</span></div></div><div><div><div>138</div></div><div><span>      </span><span>tip</span><span>:</span><span><span> (</span><span>x</span><span>, </span><span>y</span><span>) </span></span><span>=&gt;</span><span> </span><span>AdmonitionComponent</span><span>(</span><span>x</span><span>, </span><span>y</span><span>, </span><span>"tip"</span><span>),</span></div></div><div><div><div>139</div></div><div><span>      </span><span>important</span><span>:</span><span><span> (</span><span>x</span><span>, </span><span>y</span><span>) </span></span><span>=&gt;</span><span> </span><span>AdmonitionComponent</span><span>(</span><span>x</span><span>, </span><span>y</span><span>, </span><span>"important"</span><span>),</span></div></div><div><div><div>140</div></div><div><span>      </span><span>caution</span><span>:</span><span><span> (</span><span>x</span><span>, </span><span>y</span><span>) </span></span><span>=&gt;</span><span> </span><span>AdmonitionComponent</span><span>(</span><span>x</span><span>, </span><span>y</span><span>, </span><span>"caution"</span><span>),</span></div></div><div><div><div>141</div></div><div><span>      </span><span>warning</span><span>:</span><span><span> (</span><span>x</span><span>, </span><span>y</span><span>) </span></span><span>=&gt;</span><span> </span><span>AdmonitionComponent</span><span>(</span><span>x</span><span>, </span><span>y</span><span>, </span><span>"warning"</span><span>),</span></div></div><div><div><div>142</div></div><div><span><span>     </span></span><span>},</span></div></div><div><div><div>143</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>144</div></div><div><span><span>   </span></span><span>],</span></div></div><div><div><div>145</div></div><div><span><span>   </span></span><span>[</span></div></div><div><div><div>146</div></div><div><span>    </span><span>rehypeAutolinkHeadings</span><span>,</span></div></div><div><div><div>147</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>148</div></div><div><span>     </span><span>behavior</span><span>:</span><span> </span><span>"append"</span><span>,</span></div></div><div><div><div>149</div></div><div><span>     </span><span>properties</span><span>:</span><span> {</span></div></div><div><div><div>150</div></div><div><span>      </span><span>className</span><span>:</span><span> [</span><span>"anchor"</span><span>],</span></div></div><div><div><div>151</div></div><div><span><span>     </span></span><span>},</span></div></div><div><div><div>152</div></div><div><span>     </span><span>content</span><span>:</span><span> {</span></div></div><div><div><div>153</div></div><div><span>      </span><span>type</span><span>:</span><span> </span><span>"element"</span><span>,</span></div></div><div><div><div>154</div></div><div><span>      </span><span>tagName</span><span>:</span><span> </span><span>"span"</span><span>,</span></div></div><div><div><div>155</div></div><div><span>      </span><span>properties</span><span>:</span><span> {</span></div></div><div><div><div>156</div></div><div><span>       </span><span>className</span><span>:</span><span> [</span><span>"anchor-icon"</span><span>],</span></div></div><div><div><div>157</div></div><div><span>       </span><span>"data-pagefind-ignore"</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>158</div></div><div><span><span>      </span></span><span>},</span></div></div><div><div><div>159</div></div><div><span>      </span><span>children</span><span>:</span><span> [</span></div></div><div><div><div>160</div></div><div><span><span>       </span></span><span>{</span></div></div><div><div><div>161</div></div><div><span>        </span><span>type</span><span>:</span><span> </span><span>"text"</span><span>,</span></div></div><div><div><div>162</div></div><div><span>        </span><span>value</span><span>:</span><span> </span><span>"#"</span><span>,</span></div></div><div><div><div>163</div></div><div><span><span>       </span></span><span>},</span></div></div><div><div><div>164</div></div><div><span><span>      </span></span><span>],</span></div></div><div><div><div>165</div></div><div><span><span>     </span></span><span>},</span></div></div><div><div><div>166</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>167</div></div><div><span><span>   </span></span><span>],</span></div></div><div><div><div>168</div></div><div><span><span>  </span></span><span>],</span></div></div><div><div><div>169</div></div><div><span><span> </span></span><span>},</span></div></div><div><div><div>170</div></div><div><span> </span><span>vite</span><span>:</span><span> {</span></div></div><div><div><div>171</div></div><div><span>  </span><span>build</span><span>:</span><span> {</span></div></div><div><div><div>172</div></div><div><span>   </span><span>rollupOptions</span><span>:</span><span> {</span></div></div><div><div><div>173</div></div><div><span>    </span><span>onwarn</span><span><span>(</span><span>warning</span><span>, </span><span>warn</span><span>) {</span></span></div></div><div><div><div>174</div></div><div><span>     </span><span>// temporarily suppress this warning</span></div></div><div><div><div>175</div></div><div><span>     </span><span>if</span><span> (</span></div></div><div><div><div>176</div></div><div><span><span>      </span></span><span>warning</span><span>.</span><span>message</span><span>.</span><span>includes</span><span>(</span><span>"is dynamically imported by"</span><span>) </span><span>&amp;&amp;</span></div></div><div><div><div>177</div></div><div><span><span>      </span></span><span>warning</span><span>.</span><span>message</span><span>.</span><span>includes</span><span>(</span><span>"but also statically imported by"</span><span>)</span></div></div><div><div><div>178</div></div><div><span><span>     </span></span><span>) {</span></div></div><div><div><div>179</div></div><div><span>      </span><span>return</span><span>;</span></div></div><div><div><div>180</div></div><div><span><span>     </span></span><span>}</span></div></div><div><div><div>181</div></div><div><span>     </span><span>warn</span><span>(</span><span>warning</span><span>);</span></div></div><div><div><div>182</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>183</div></div><div><span><span>   </span></span><span>},</span></div></div><div><div><div>184</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>185</div></div><div><span><span> </span></span><span>},</span></div></div><div><div><div>186</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>然后，在 <code>markdown-extend.styl</code> 添加以下内容：</p><div><div><div><figure><figcaption><span>markdown-extend.styl</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div></div></div><div><span></span><span></span><span>227 collapsed lines</span></div></div><div><div><div>1</div></div><div><span>.custom-md</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>  </span><span>blockquote</span><span>.admonition</span></div></div><div><div><div>4</div></div><div><span>    </span><span>.bdm-title</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>display: </span><span>flex</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>align-items: </span><span>center</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>margin-bottom: -.</span><span><span>9</span><span>rem</span></span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>font-weight: </span><span>bold</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>content: </span><span>' '</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>display: </span><span>inline-block</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>font-size: </span><span>inherit</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>overflow: </span><span>visible</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>margin-right: </span><span><span>.6</span><span>rem</span></span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>height: </span><span><span>1</span><span>em</span></span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>width: </span><span><span>1</span><span>em</span></span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>vertical-align: -.</span><span><span>126</span><span>em</span></span></div></div><div><div><div>19</div></div><div><span>        </span><span>mask-size</span><span><span>: </span><span>contain</span></span></div></div><div><div><div>20</div></div><div><span>        </span><span>mask-position</span><span><span>: </span><span>center</span></span></div></div><div><div><div>21</div></div><div><span>        </span><span>mask-repeat</span><span><span>: </span><span>no-repeat</span></span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>transform: </span><span>translateY</span><span>(</span><span><span>-0.0625</span><span>rem</span></span><span>)</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>&amp;</span><span>.bdm-tip</span></div></div><div><div><div>24</div></div><div><span>      </span><span>.bdm-title</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>color: </span><span>var</span><span>(</span><span>--admonitions-color-tip</span><span>)</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>28</div></div><div><span><span>          </span></span><span>background: </span><span>var</span><span>(</span><span>--admonitions-color-tip</span><span>)</span></div></div><div><div><div>29</div></div><div><span><span>          </span></span><span>mask-image: </span><span>url</span><span>(</span><span>"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'%3E%3Cpath d='M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z'%3E%3C/path%3E%3C/svg%3E"</span><span>)</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span><span>      </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>background: </span><span>var</span><span>(</span><span>--admonitions-color-tip</span><span>)</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>&amp;</span><span>.bdm-note</span></div></div><div><div><div>34</div></div><div><span>      </span><span>.bdm-title</span></div></div><div><div><div>35</div></div><div><span><span>        </span></span><span>color: </span><span>var</span><span>(</span><span>--admonitions-color-note</span><span>)</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span><span>        </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>38</div></div><div><span><span>          </span></span><span>background: </span><span>var</span><span>(</span><span>--admonitions-color-note</span><span>)</span></div></div><div><div><div>39</div></div><div><span><span>          </span></span><span>mask-image: </span><span>url</span><span>(</span><span>"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'%3E%3Cpath fill='var(--admonitions-color-tip)' d='M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z'%3E%3C/path%3E%3C/svg%3E"</span><span>)</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span><span>      </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>42</div></div><div><span><span>        </span></span><span>background: </span><span>var</span><span>(</span><span>--admonitions-color-note</span><span>)</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>&amp;</span><span>.bdm-important</span></div></div><div><div><div>44</div></div><div><span>      </span><span>.bdm-title</span></div></div><div><div><div>45</div></div><div><span><span>        </span></span><span>color: </span><span>var</span><span>(</span><span>--admonitions-color-important</span><span>)</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span><span>        </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>48</div></div><div><span><span>          </span></span><span>background: </span><span>var</span><span>(</span><span>--admonitions-color-important</span><span>)</span></div></div><div><div><div>49</div></div><div><span><span>          </span></span><span>mask-image: </span><span>url</span><span>(</span><span>"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'%3E%3Cpath d='M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z'%3E%3C/path%3E%3C/svg%3E"</span><span>)</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span><span>      </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>52</div></div><div><span><span>        </span></span><span>background: </span><span>var</span><span>(</span><span>--admonitions-color-important</span><span>)</span></div></div><div><div><div>53</div></div><div><span><span>    </span></span><span>&amp;</span><span>.bdm-warning</span></div></div><div><div><div>54</div></div><div><span>      </span><span>.bdm-title</span></div></div><div><div><div>55</div></div><div><span><span>        </span></span><span>color: </span><span>var</span><span>(</span><span>--admonitions-color-warning</span><span>)</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span><span>        </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>58</div></div><div><span><span>          </span></span><span>background: </span><span>var</span><span>(</span><span>--admonitions-color-warning</span><span>)</span></div></div><div><div><div>59</div></div><div><span><span>          </span></span><span>mask-image: </span><span>url</span><span>(</span><span>"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'%3E%3Cpath d='M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z'%3E%3C/path%3E%3C/svg%3E"</span><span>)</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span><span>      </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>62</div></div><div><span><span>        </span></span><span>background: </span><span>var</span><span>(</span><span>--admonitions-color-warning</span><span>)</span></div></div><div><div><div>63</div></div><div><span><span>    </span></span><span>&amp;</span><span>.bdm-caution</span></div></div><div><div><div>64</div></div><div><span>      </span><span>.bdm-title</span></div></div><div><div><div>65</div></div><div><span><span>        </span></span><span>color: </span><span>var</span><span>(</span><span>--admonitions-color-caution</span><span>)</span></div></div><div><div><div>66</div></div><div>
</div></div><div><div><div>67</div></div><div><span><span>        </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>68</div></div><div><span><span>          </span></span><span>background: </span><span>var</span><span>(</span><span>--admonitions-color-caution</span><span>)</span></div></div><div><div><div>69</div></div><div><span><span>          </span></span><span>mask-image: </span><span>url</span><span>(</span><span>"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'%3E%3Cpath d='M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z'%3E%3C/path%3E%3C/svg%3E"</span><span>)</span></div></div><div><div><div>70</div></div><div>
</div></div><div><div><div>71</div></div><div><span><span>      </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>72</div></div><div><span><span>        </span></span><span>background: </span><span>var</span><span>(</span><span>--admonitions-color-caution</span><span>)</span></div></div><div><div><div>73</div></div><div>
</div></div><div><div><div>74</div></div><div><span>  </span><span>img</span></div></div><div><div><div>75</div></div><div><span><span>    </span></span><span>border-radius: </span><span><span>0.75</span><span>rem</span></span></div></div><div><div><div>76</div></div><div>
</div></div><div><div><div>77</div></div><div><span>  </span><span>hr</span></div></div><div><div><div>78</div></div><div><span><span>    </span></span><span>border-color: </span><span>var</span><span>(</span><span>--line-divider</span><span>)</span></div></div><div><div><div>79</div></div><div><span><span>    </span></span><span>border-style: </span><span>dashed</span></div></div><div><div><div>80</div></div><div>
</div></div><div><div><div>81</div></div><div><span>  </span><span>iframe</span></div></div><div><div><div>82</div></div><div><span><span>    </span></span><span>border-radius: </span><span><span>0.75</span><span>rem</span></span></div></div><div><div><div>83</div></div><div><span><span>    </span></span><span>margin-left: </span><span>auto</span></div></div><div><div><div>84</div></div><div><span><span>    </span></span><span>margin-right: </span><span>auto</span></div></div><div><div><div>85</div></div><div><span><span>    </span></span><span>max-width: </span><span><span>100</span><span>%</span></span></div></div><div><div><div>86</div></div><div>
</div></div><div><div><div>87</div></div><div><span>a</span><span>.card-github</span></div></div><div><div><div>88</div></div><div><span><span>  </span></span><span>display: </span><span>block</span></div></div><div><div><div>89</div></div><div><span><span>  </span></span><span>background: </span><span>var</span><span>(</span><span>--license-block-bg</span><span>)</span></div></div><div><div><div>90</div></div><div><span><span>  </span></span><span>position: </span><span>relative</span></div></div><div><div><div>91</div></div><div><span><span>  </span></span><span>margin: </span><span><span>0.5</span><span>rem</span></span><span> </span><span>0</span></div></div><div><div><div>92</div></div><div><span><span>  </span></span><span>padding: </span><span><span>1.1</span><span>rem</span></span><span> </span><span><span>1.5</span><span>rem</span></span><span> </span><span><span>1.1</span><span>rem</span></span><span> </span><span><span>1.5</span><span>rem</span></span></div></div><div><div><div>93</div></div><div><span><span>  </span></span><span>color: </span><span>var</span><span>(</span><span>--tw-prose-body</span><span>)</span></div></div><div><div><div>94</div></div><div><span><span>  </span></span><span>border-radius: </span><span>var</span><span>(</span><span>--radius-large</span><span>)</span></div></div><div><div><div>95</div></div><div><span>  </span><span>text-decoration-thickness</span><span>: </span><span><span>0</span><span>px</span></span></div></div><div><div><div>96</div></div><div><span>  </span><span>text-decoration-line</span><span><span>: </span><span>none</span></span></div></div><div><div><div>97</div></div><div>
</div></div><div><div><div>98</div></div><div><span><span>  </span></span><span>&amp;</span><span>:hover</span></div></div><div><div><div>99</div></div><div><span><span>    </span></span><span>background-color: </span><span>var</span><span>(</span><span>--btn-regular-bg-hover</span><span>)</span></div></div><div><div><div>100</div></div><div>
</div></div><div><div><div>101</div></div><div><span>    </span><span>.gc-titlebar</span></div></div><div><div><div>102</div></div><div><span><span>      </span></span><span>color: </span><span>var</span><span>(</span><span>--btn-content</span><span>)</span></div></div><div><div><div>103</div></div><div>
</div></div><div><div><div>104</div></div><div><span>    </span><span>.gc-stars</span><span>, </span><span>.gc-forks</span><span>, </span><span>.gc-license</span><span>, </span><span>.gc-description</span></div></div><div><div><div>105</div></div><div><span><span>      </span></span><span>color: </span><span>var</span><span>(</span><span>--tw-prose-headings</span><span>)</span></div></div><div><div><div>106</div></div><div>
</div></div><div><div><div>107</div></div><div><span><span>      </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>108</div></div><div><span><span>        </span></span><span>background-color: </span><span>var</span><span>(</span><span>--tw-prose-headings</span><span>)</span></div></div><div><div><div>109</div></div><div>
</div></div><div><div><div>110</div></div><div><span><span>  </span></span><span>&amp;</span><span>:active</span></div></div><div><div><div>111</div></div><div><span>    </span><span>scale</span><span>: </span><span>.98</span></div></div><div><div><div>112</div></div><div><span><span>    </span></span><span>background-color: </span><span>var</span><span>(</span><span>--btn-regular-bg-active</span><span>);</span></div></div><div><div><div>113</div></div><div>
</div></div><div><div><div>114</div></div><div><span>  </span><span>.gc-titlebar</span></div></div><div><div><div>115</div></div><div><span><span>    </span></span><span>display: </span><span>flex</span></div></div><div><div><div>116</div></div><div><span><span>    </span></span><span>align-items: </span><span>center</span></div></div><div><div><div>117</div></div><div><span><span>    </span></span><span>justify-content: </span><span>space-between</span></div></div><div><div><div>118</div></div><div><span><span>    </span></span><span>margin-bottom: </span><span><span>0.5</span><span>rem</span></span></div></div><div><div><div>119</div></div><div><span><span>    </span></span><span>color: </span><span>var</span><span>(</span><span>--tw-prose-headings</span><span>)</span></div></div><div><div><div>120</div></div><div><span><span>    </span></span><span>font-size: </span><span><span>1.25</span><span>rem</span></span></div></div><div><div><div>121</div></div><div><span><span>    </span></span><span>font-weight: </span><span>500</span></div></div><div><div><div>122</div></div><div>
</div></div><div><div><div>123</div></div><div><span>    </span><span>.gc-titlebar-left</span></div></div><div><div><div>124</div></div><div><span><span>      </span></span><span>display: </span><span>flex</span></div></div><div><div><div>125</div></div><div><span><span>      </span></span><span>flex-flow: </span><span>row</span><span> </span><span>nowrap</span></div></div><div><div><div>126</div></div><div><span><span>      </span></span><span>gap: </span><span><span>0.5</span><span>rem</span></span></div></div><div><div><div>127</div></div><div>
</div></div><div><div><div>128</div></div><div><span>    </span><span>.gc-repo</span></div></div><div><div><div>129</div></div><div><span><span>      </span></span><span>font-weight: </span><span>bold</span></div></div><div><div><div>130</div></div><div>
</div></div><div><div><div>131</div></div><div><span>    </span><span>.gc-owner</span></div></div><div><div><div>132</div></div><div><span><span>      </span></span><span>font-weight: </span><span>300</span></div></div><div><div><div>133</div></div><div><span><span>      </span></span><span>position: </span><span>relative</span></div></div><div><div><div>134</div></div><div><span><span>      </span></span><span>display: </span><span>flex</span></div></div><div><div><div>135</div></div><div><span><span>      </span></span><span>flex-flow: </span><span>row</span><span> </span><span>nowrap</span></div></div><div><div><div>136</div></div><div><span><span>      </span></span><span>gap: </span><span><span>0.5</span><span>rem</span></span></div></div><div><div><div>137</div></div><div><span><span>      </span></span><span>align-items: </span><span>center</span></div></div><div><div><div>138</div></div><div>
</div></div><div><div><div>139</div></div><div><span>    </span><span>.gc-avatar</span></div></div><div><div><div>140</div></div><div><span><span>      </span></span><span>display: </span><span>block</span></div></div><div><div><div>141</div></div><div><span><span>      </span></span><span>overflow: </span><span>hidden</span></div></div><div><div><div>142</div></div><div><span><span>      </span></span><span>width: </span><span><span>1.5</span><span>rem</span></span></div></div><div><div><div>143</div></div><div><span><span>      </span></span><span>height: </span><span><span>1.5</span><span>rem</span></span></div></div><div><div><div>144</div></div><div><span><span>      </span></span><span>margin-top: </span><span><span>-0.1</span><span>rem</span></span></div></div><div><div><div>145</div></div><div><span><span>      </span></span><span>background-color: </span><span>var</span><span>(</span><span>--primary</span><span>)</span></div></div><div><div><div>146</div></div><div><span><span>      </span></span><span>background-size: </span><span>cover</span></div></div><div><div><div>147</div></div><div><span><span>      </span></span><span>border-radius: </span><span><span>50</span><span>%</span></span></div></div><div><div><div>148</div></div><div>
</div></div><div><div><div>149</div></div><div><span>  </span><span>.gc-description</span></div></div><div><div><div>150</div></div><div><span><span>    </span></span><span>margin-bottom: </span><span><span>0.7</span><span>rem</span></span></div></div><div><div><div>151</div></div><div><span><span>    </span></span><span>font-size: </span><span><span>1</span><span>rem</span></span></div></div><div><div><div>152</div></div><div><span><span>    </span></span><span>font-weight: </span><span>300</span></div></div><div><div><div>153</div></div><div><span><span>    </span></span><span>line-height: </span><span><span>1.5</span><span>rem</span></span></div></div><div><div><div>154</div></div><div><span><span>    </span></span><span>color: </span><span>var</span><span>(</span><span>--tw-prose-body</span><span>)</span></div></div><div><div><div>155</div></div><div>
</div></div><div><div><div>156</div></div><div><span>  </span><span>.gc-infobar</span></div></div><div><div><div>157</div></div><div><span><span>    </span></span><span>display: </span><span>flex</span></div></div><div><div><div>158</div></div><div><span><span>    </span></span><span>flex-flow: </span><span>row</span><span> </span><span>nowrap</span></div></div><div><div><div>159</div></div><div><span><span>    </span></span><span>gap: </span><span><span>1.5</span><span>rem</span></span></div></div><div><div><div>160</div></div><div><span><span>    </span></span><span>color: </span><span>var</span><span>(</span><span>--tw-prose-body</span><span>)</span></div></div><div><div><div>161</div></div><div><span><span>    </span></span><span>width: fit-content</span></div></div><div><div><div>162</div></div><div>
</div></div><div><div><div>163</div></div><div><span>  </span><span>.gc-language</span></div></div><div><div><div>164</div></div><div><span><span>    </span></span><span>display: </span><span>none</span></div></div><div><div><div>165</div></div><div>
</div></div><div><div><div>166</div></div><div><span>  </span><span>.gc-stars</span><span>, </span><span>.gc-forks</span><span>, </span><span>.gc-license</span><span>, </span><span>.github-logo</span></div></div><div><div><div>167</div></div><div><span><span>    </span></span><span>font-weight: </span><span>500</span></div></div><div><div><div>168</div></div><div><span><span>    </span></span><span>font-size: </span><span><span>0.875</span><span>rem</span></span></div></div><div><div><div>169</div></div><div><span><span>    </span></span><span>opacity: </span><span>0.9</span><span>;</span></div></div><div><div><div>170</div></div><div>
</div></div><div><div><div>171</div></div><div><span><span>    </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>172</div></div><div><span><span>      </span></span><span>content: </span><span>' '</span></div></div><div><div><div>173</div></div><div><span><span>      </span></span><span>display: </span><span>inline-block</span></div></div><div><div><div>174</div></div><div><span><span>      </span></span><span>height: </span><span><span>1.3</span><span>em</span></span></div></div><div><div><div>175</div></div><div><span><span>      </span></span><span>width: </span><span><span>1.3</span><span>em</span></span></div></div><div><div><div>176</div></div><div><span><span>      </span></span><span>margin-right: </span><span><span>.4</span><span>rem</span></span></div></div><div><div><div>177</div></div><div><span><span>      </span></span><span>vertical-align: -.</span><span><span>24</span><span>em</span></span></div></div><div><div><div>178</div></div><div><span><span>      </span></span><span>font-size: </span><span>inherit</span></div></div><div><div><div>179</div></div><div><span><span>      </span></span><span>background-color: </span><span>var</span><span>(</span><span>--tw-prose-body</span><span>)</span></div></div><div><div><div>180</div></div><div><span><span>      </span></span><span>overflow: </span><span>visible</span></div></div><div><div><div>181</div></div><div><span>      </span><span>mask-size</span><span><span>: </span><span>contain</span></span></div></div><div><div><div>182</div></div><div><span>      </span><span>mask-position</span><span><span>: </span><span>center</span></span></div></div><div><div><div>183</div></div><div><span>      </span><span>mask-repeat</span><span><span>: </span><span>no-repeat</span></span></div></div><div><div><div>184</div></div><div><span><span>      </span></span><span>transition-property: background-color, background;</span></div></div><div><div><div>185</div></div><div><span><span>      </span></span><span>transition-timing-function: </span><span>cubic-bezier</span><span>(</span><span>0.4</span><span>, </span><span>0</span><span>, </span><span>0.2</span><span>, </span><span>1</span><span>)</span></div></div><div><div><div>186</div></div><div><span><span>      </span></span><span>transition-duration: </span><span><span>0.15</span><span>s</span></span></div></div><div><div><div>187</div></div><div>
</div></div><div><div><div>188</div></div><div><span>  </span><span>.gc-stars</span></div></div><div><div><div>189</div></div><div><span><span>    </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>190</div></div><div><span><span>      </span></span><span>mask-image: </span><span>url</span><span>(</span><span>"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' height='16' viewBox='0 0 16 16' version='1.1' width='16'%3E%3Cpath d='M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z'%3E%3C/path%3E%3C/svg%3E"</span><span>)</span></div></div><div><div><div>191</div></div><div>
</div></div><div><div><div>192</div></div><div><span>  </span><span>.gc-license</span></div></div><div><div><div>193</div></div><div><span><span>    </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>194</div></div><div><span><span>      </span></span><span>margin-right: </span><span><span>.5</span><span>rem</span></span></div></div><div><div><div>195</div></div><div><span><span>      </span></span><span>mask-image: </span><span>url</span><span>(</span><span>"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' height='16' viewBox='0 0 16 16' version='1.1' width='16'%3E%3Cpath d='M8.75.75V2h.985c.304 0 .603.08.867.231l1.29.736c.038.022.08.033.124.033h2.234a.75.75 0 0 1 0 1.5h-.427l2.111 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.006.005-.01.01-.045.04c-.21.176-.441.327-.686.45C14.556 10.78 13.88 11 13 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L12.178 4.5h-.162c-.305 0-.604-.079-.868-.231l-1.29-.736a.245.245 0 0 0-.124-.033H8.75V13h2.5a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1 0-1.5h2.5V3.5h-.984a.245.245 0 0 0-.124.033l-1.289.737c-.265.15-.564.23-.869.23h-.162l2.112 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.016.015-.045.04c-.21.176-.441.327-.686.45C4.556 10.78 3.88 11 3 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L2.178 4.5H1.75a.75.75 0 0 1 0-1.5h2.234a.249.249 0 0 0 .125-.033l1.288-.737c.265-.15.564-.23.869-.23h.984V.75a.75.75 0 0 1 1.5 0Zm2.945 8.477c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L13 6.327Zm-10 0c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L3 6.327Z'%3E%3C/path%3E%3C/svg%3E"</span><span>)</span></div></div><div><div><div>196</div></div><div>
</div></div><div><div><div>197</div></div><div><span>  </span><span>.gc-forks</span></div></div><div><div><div>198</div></div><div><span><span>    </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>199</div></div><div><span><span>      </span></span><span>mask-image: </span><span>url</span><span>(</span><span>"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' height='16' viewBox='0 0 16 16' version='1.1' width='16'%3E%3Cpath d='M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z'%3E%3C/path%3E%3C/svg%3E"</span><span>)</span></div></div><div><div><div>200</div></div><div>
</div></div><div><div><div>201</div></div><div><span>  </span><span>.github-logo</span></div></div><div><div><div>202</div></div><div><span><span>    </span></span><span>font-size: </span><span><span>1.25</span><span>rem</span></span></div></div><div><div><div>203</div></div><div>
</div></div><div><div><div>204</div></div><div><span><span>    </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>205</div></div><div><span><span>      </span></span><span>background-color: </span><span>var</span><span>(</span><span>--tw-prose-headings</span><span>)</span></div></div><div><div><div>206</div></div><div><span><span>      </span></span><span>margin-right: </span><span>0</span></div></div><div><div><div>207</div></div><div><span><span>      </span></span><span>mask-image: </span><span>url</span><span>(</span><span>"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='31' height='32' viewBox='0 0 496 512'%3E%3Cpath fill='%23a1f7cb' d='M165.9 397.4c0 2-2.3 3.6-5.2 3.6c-3.3.3-5.6-1.3-5.6-3.6c0-2 2.3-3.6 5.2-3.6c3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9c2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9c.3 2 2.9 3.3 5.9 2.6c2.9-.7 4.9-2.6 4.6-4.6c-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2c12.8 2.3 17.3-5.6 17.3-12.1c0-6.2-.3-40.4-.3-61.4c0 0-70 15-84.7-29.8c0 0-11.4-29.1-27.8-36.6c0 0-22.9-15.7 1.6-15.4c0 0 24.9 2 38.6 25.8c21.9 38.6 58.6 27.5 72.9 20.9c2.3-16 8.8-27.1 16-33.7c-55.9-6.2-112.3-14.3-112.3-110.5c0-27.5 7.6-41.3 23.6-58.9c-2.6-6.5-11.1-33.3 2.6-67.9c20.9-6.5 69 27 69 27c20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27c13.7 34.7 5.2 61.4 2.6 67.9c16 17.7 25.8 31.5 25.8 58.9c0 96.5-58.9 104.2-114.8 110.5c9.2 7.9 17 22.9 17 46.4c0 33.7-.3 75.4-.3 83.6c0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252C496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2c1.6 1.6 3.9 2.3 5.2 1c1.3-1 1-3.3-.7-5.2c-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9c1.6 1 3.6.7 4.3-.7c.7-1.3-.3-2.9-2.3-3.9c-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2c2.3 2.3 5.2 2.6 6.5 1c1.3-1.3.7-4.3-1.3-6.2c-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9c1.6 2.3 4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2c-1.4-2.3-4-3.3-5.6-2'/%3E%3C/svg%3E"</span><span>)</span></div></div><div><div><div>208</div></div><div>
</div></div><div><div><div>209</div></div><div><span>a</span><span>.card-github.fetch-waiting</span></div></div><div><div><div>210</div></div><div><span><span>  </span></span><span>pointer-events: </span><span>none</span></div></div><div><div><div>211</div></div><div><span><span>  </span></span><span>opacity: </span><span>0.7</span></div></div><div><div><div>212</div></div><div><span><span>  </span></span><span>transition: </span><span>opacity</span><span> </span><span><span>0.15</span><span>s</span></span><span><span> </span><span>ease-in-out</span></span></div></div><div><div><div>213</div></div><div>
</div></div><div><div><div>214</div></div><div><span>  </span><span>.gc-description</span><span>, </span><span>.gc-infobar</span><span>, </span><span>.gc-avatar</span></div></div><div><div><div>215</div></div><div><span><span>    </span></span><span>background-color: </span><span>var</span><span>(</span><span>--tw-prose-body</span><span>)</span></div></div><div><div><div>216</div></div><div><span><span>    </span></span><span>color: </span><span>transparent</span></div></div><div><div><div>217</div></div><div><span><span>    </span></span><span>opacity: </span><span>0.5</span><span>;</span></div></div><div><div><div>218</div></div><div><span><span>    </span></span><span>animation: pulsate </span><span><span>2</span><span>s</span></span><span><span> </span><span>infinite</span><span> </span><span>linear</span></span></div></div><div><div><div>219</div></div><div><span><span>    </span></span><span>user-select: </span><span>none</span></div></div><div><div><div>220</div></div><div>
</div></div><div><div><div>221</div></div><div><span><span>    </span></span><span>&amp;</span><span>:before</span></div></div><div><div><div>222</div></div><div><span><span>      </span></span><span>background-color: </span><span>transparent</span></div></div><div><div><div>223</div></div><div>
</div></div><div><div><div>224</div></div><div><span>  </span><span>.gc-repo</span></div></div><div><div><div>225</div></div><div><span><span>    </span></span><span>margin-left: </span><span><span>-0.1</span><span>rem</span></span></div></div><div><div><div>226</div></div><div>
</div></div><div><div><div>227</div></div><div><span>  </span><span>.gc-description</span><span>, </span><span>.gc-infobar</span></div></div><div><div><div>228</div></div><div><span><span>    </span></span><span>border-radius: </span><span><span>0.5</span><span>rem</span></span></div></div><div><div><div>229</div></div><div>
</div></div><div><div><div>230</div></div><div><span>a</span><span>.card-codeberg</span></div></div><div><div><div>231</div></div><div><span><span><span>  </span></span><span>display: </span></span><span>block</span></div></div><div><div><div>232</div></div><div><span><span><span>  </span></span><span>background: </span></span><span>var</span><span>(</span><span>--license-block-bg</span><span>)</span></div></div><div><div><div>233</div></div><div><span><span><span>  </span></span><span>position: </span></span><span>relative</span></div></div><div><div><div>234</div></div><div><span><span><span>  </span></span><span>margin: </span></span><span><span>0.5</span><span>rem</span></span><span> </span><span>0</span></div></div><div><div><div>235</div></div><div><span><span><span>  </span></span><span>padding: </span></span><span><span>1.1</span><span>rem</span></span><span> </span><span><span>1.5</span><span>rem</span></span><span> </span><span><span>1.1</span><span>rem</span></span><span> </span><span><span>1.5</span><span>rem</span></span></div></div><div><div><div>236</div></div><div><span><span><span>  </span></span><span>color: </span></span><span>var</span><span>(</span><span>--tw-prose-body</span><span>)</span></div></div><div><div><div>237</div></div><div><span><span><span>  </span></span><span>border-radius: </span></span><span>var</span><span>(</span><span>--radius-large</span><span>)</span></div></div><div><div><div>238</div></div><div><span>  </span><span>text-decoration-thickness</span><span>: </span><span><span>0</span><span>px</span></span></div></div><div><div><div>239</div></div><div><span>  </span><span>text-decoration-line</span><span>: </span><span>none</span></div></div><div><div><div>240</div></div><div>
</div></div><div><div><div>241</div></div><div><span><span>  </span>&amp;</span><span>:hover</span></div></div><div><div><div>242</div></div><div><span><span><span>    </span></span><span>background-color: </span></span><span>var</span><span>(</span><span>--btn-regular-bg-hover</span><span>)</span></div></div><div><div><div>243</div></div><div>
</div></div><div><div><div>244</div></div><div><span>    </span><span>.gc-titlebar</span></div></div><div><div><div>245</div></div><div><span><span><span>      </span></span><span>color: </span></span><span>var</span><span>(</span><span>--btn-content</span><span>)</span></div></div><div><div><div>246</div></div><div>
</div></div><div><div><div>247</div></div><div><span>    </span><span>.gc-stars</span><span>, </span><span>.gc-forks</span><span>, </span><span>.gc-license</span><span>, </span><span>.gc-description</span></div></div><div><div><div>248</div></div><div><span><span><span>      </span></span><span>color: </span></span><span>var</span><span>(</span><span>--tw-prose-headings</span><span>)</span></div></div><div><div><div>249</div></div><div>
</div></div><div><div><div>250</div></div><div><span><span>      </span>&amp;</span><span>:before</span></div></div><div><div><div>251</div></div><div><span><span><span>        </span></span><span>background-color: </span></span><span>var</span><span>(</span><span>--tw-prose-headings</span><span>)</span></div></div><div><div><div>252</div></div><div>
</div></div><div><div><div>253</div></div><div><span><span>  </span>&amp;</span><span>:active</span></div></div><div><div><div>254</div></div><div><span>    </span><span>scale</span><span>: </span><span>.98</span></div></div><div><div><div>255</div></div><div><span><span><span>    </span></span><span>background-color: </span></span><span>var</span><span>(</span><span>--btn-regular-bg-active</span><span>);</span></div></div><div><div><div>256</div></div><div>
</div></div><div><div><div>257</div></div><div><span>  </span><span>.gc-titlebar</span></div></div><div><div><div>258</div></div><div><span><span><span>    </span></span><span>display: </span></span><span>flex</span></div></div><div><div><div>259</div></div><div><span><span><span>    </span></span><span>align-items: </span></span><span>center</span></div></div><div><div><div>260</div></div><div><span><span><span>    </span></span><span>justify-content: </span></span><span>space-between</span></div></div><div><div><div>261</div></div><div><span><span><span>    </span></span><span>margin-bottom: </span></span><span><span>0.5</span><span>rem</span></span></div></div><div><div><div>262</div></div><div><span><span><span>    </span></span><span>color: </span></span><span>var</span><span>(</span><span>--tw-prose-headings</span><span>)</span></div></div><div><div><div>263</div></div><div><span><span><span>    </span></span><span>font-size: </span></span><span><span>1.25</span><span>rem</span></span></div></div><div><div><div>264</div></div><div><span><span><span>    </span></span><span>font-weight: </span></span><span>500</span></div></div><div><div><div>265</div></div><div>
</div></div><div><div><div>266</div></div><div><span>    </span><span>.gc-titlebar-left</span></div></div><div><div><div>267</div></div><div><span><span><span>      </span></span><span>display: </span></span><span>flex</span></div></div><div><div><div>268</div></div><div><span><span><span>      </span></span><span>flex-flow: </span></span><span>row</span><span> </span><span>nowrap</span></div></div><div><div><div>269</div></div><div><span><span><span>      </span></span><span>gap: </span></span><span><span>0.5</span><span>rem</span></span></div></div><div><div><div>270</div></div><div>
</div></div><div><div><div>271</div></div><div><span>    </span><span>.gc-repo</span></div></div><div><div><div>272</div></div><div><span><span><span>      </span></span><span>font-weight: </span></span><span>bold</span></div></div><div><div><div>273</div></div><div>
</div></div><div><div><div>274</div></div><div><span>    </span><span>.gc-owner</span></div></div><div><div><div>275</div></div><div><span><span><span>      </span></span><span>font-weight: </span></span><span>300</span></div></div><div><div><div>276</div></div><div><span><span><span>      </span></span><span>position: </span></span><span>relative</span></div></div><div><div><div>277</div></div><div><span><span><span>      </span></span><span>display: </span></span><span>flex</span></div></div><div><div><div>278</div></div><div><span><span><span>      </span></span><span>flex-flow: </span></span><span>row</span><span> </span><span>nowrap</span></div></div><div><div><div>279</div></div><div><span><span><span>      </span></span><span>gap: </span></span><span><span>0.5</span><span>rem</span></span></div></div><div><div><div>280</div></div><div><span><span><span>      </span></span><span>align-items: </span></span><span>center</span></div></div><div><div><div>281</div></div><div>
</div></div><div><div><div>282</div></div><div><span>    </span><span>.gc-avatar</span></div></div><div><div><div>283</div></div><div><span><span><span>      </span></span><span>display: </span></span><span>block</span></div></div><div><div><div>284</div></div><div><span><span><span>      </span></span><span>overflow: </span></span><span>hidden</span></div></div><div><div><div>285</div></div><div><span><span><span>      </span></span><span>width: </span></span><span><span>1.5</span><span>rem</span></span></div></div><div><div><div>286</div></div><div><span><span><span>      </span></span><span>height: </span></span><span><span>1.5</span><span>rem</span></span></div></div><div><div><div>287</div></div><div><span><span><span>      </span></span><span>margin-top: </span></span><span><span>-0.1</span><span>rem</span></span></div></div><div><div><div>288</div></div><div><span><span><span>      </span></span><span>background-color: </span></span><span>var</span><span>(</span><span>--primary</span><span>)</span></div></div><div><div><div>289</div></div><div><span><span><span>      </span></span><span>background-size: </span></span><span>cover</span></div></div><div><div><div>290</div></div><div><span><span><span>      </span></span><span>border-radius: </span></span><span><span>50</span><span>%</span></span></div></div><div><div><div>291</div></div><div>
</div></div><div><div><div>292</div></div><div><span>  </span><span>.gc-description</span></div></div><div><div><div>293</div></div><div><span><span><span>    </span></span><span>margin-bottom: </span></span><span><span>0.7</span><span>rem</span></span></div></div><div><div><div>294</div></div><div><span><span><span>    </span></span><span>font-size: </span></span><span><span>1</span><span>rem</span></span></div></div><div><div><div>295</div></div><div><span><span><span>    </span></span><span>font-weight: </span></span><span>300</span></div></div><div><div><div>296</div></div><div><span><span><span>    </span></span><span>line-height: </span></span><span><span>1.5</span><span>rem</span></span></div></div><div><div><div>297</div></div><div><span><span><span>    </span></span><span>color: </span></span><span>var</span><span>(</span><span>--tw-prose-body</span><span>)</span></div></div><div><div><div>298</div></div><div>
</div></div><div><div><div>299</div></div><div><span>  </span><span>.gc-infobar</span></div></div><div><div><div>300</div></div><div><span><span><span>    </span></span><span>display: </span></span><span>flex</span></div></div><div><div><div>301</div></div><div><span><span><span>    </span></span><span>flex-flow: </span></span><span>row</span><span> </span><span>nowrap</span></div></div><div><div><div>302</div></div><div><span><span><span>    </span></span><span>gap: </span></span><span><span>1.5</span><span>rem</span></span></div></div><div><div><div>303</div></div><div><span><span><span>    </span></span><span>color: </span></span><span>var</span><span>(</span><span>--tw-prose-body</span><span>)</span></div></div><div><div><div>304</div></div><div><span><span><span>    </span></span><span>width: fit-content</span></span></div></div><div><div><div>305</div></div><div>
</div></div><div><div><div>306</div></div><div><span>  </span><span>.gc-language</span></div></div><div><div><div>307</div></div><div><span><span><span>    </span></span><span>display: </span></span><span>none</span></div></div><div><div><div>308</div></div><div>
</div></div><div><div><div>309</div></div><div><span>  </span><span>.gc-stars</span><span>, </span><span>.gc-forks</span><span>, </span><span>.gc-license</span><span>, </span><span>.codeberg-logo</span></div></div><div><div><div>310</div></div><div><span><span><span>    </span></span><span>font-weight: </span></span><span>500</span></div></div><div><div><div>311</div></div><div><span><span><span>    </span></span><span>font-size: </span></span><span><span>0.875</span><span>rem</span></span></div></div><div><div><div>312</div></div><div><span><span><span>    </span></span><span>opacity: </span></span><span>0.9</span><span>;</span></div></div><div><div><div>313</div></div><div>
</div></div><div><div><div>314</div></div><div><span><span>    </span>&amp;</span><span>:before</span></div></div><div><div><div>315</div></div><div><span><span><span>      </span></span><span>content: </span></span><span>' '</span></div></div><div><div><div>316</div></div><div><span><span><span>      </span></span><span>display: </span></span><span>inline-block</span></div></div><div><div><div>317</div></div><div><span><span><span>      </span></span><span>height: </span></span><span><span>1.3</span><span>em</span></span></div></div><div><div><div>318</div></div><div><span><span><span>      </span></span><span>width: </span></span><span><span>1.3</span><span>em</span></span></div></div><div><div><div>319</div></div><div><span><span><span>      </span></span><span>margin-right: </span></span><span><span>.4</span><span>rem</span></span></div></div><div><div><div>320</div></div><div><span><span><span>      </span></span><span>vertical-align: -.</span></span><span><span>24</span><span>em</span></span></div></div><div><div><div>321</div></div><div><span><span><span>      </span></span><span>font-size: </span></span><span>inherit</span></div></div><div><div><div>322</div></div><div><span><span><span>      </span></span><span>background-color: </span></span><span>var</span><span>(</span><span>--tw-prose-body</span><span>)</span></div></div><div><div><div>323</div></div><div><span><span><span>      </span></span><span>overflow: </span></span><span>visible</span></div></div><div><div><div>324</div></div><div><span>      </span><span>mask-size</span><span>: </span><span>contain</span></div></div><div><div><div>325</div></div><div><span>      </span><span>mask-position</span><span>: </span><span>center</span></div></div><div><div><div>326</div></div><div><span>      </span><span>mask-repeat</span><span>: </span><span>no-repeat</span></div></div><div><div><div>327</div></div><div><span><span><span>      </span></span><span>transition-property: background-color, background;</span></span></div></div><div><div><div>328</div></div><div><span><span><span>      </span></span><span>transition-timing-function: </span></span><span>cubic-bezier</span><span>(</span><span>0.4</span><span>, </span><span>0</span><span>, </span><span>0.2</span><span>, </span><span>1</span><span>)</span></div></div><div><div><div>329</div></div><div><span><span><span>      </span></span><span>transition-duration: </span></span><span><span>0.15</span><span>s</span></span></div></div><div><div><div>330</div></div><div>
</div></div><div><div><div>331</div></div><div><span>  </span><span>.gc-stars</span></div></div><div><div><div>332</div></div><div><span><span>    </span>&amp;</span><span>:before</span></div></div><div><div><div>333</div></div><div><span><span><span>      </span></span><span>mask-image: </span></span><span>url</span><span>(</span><span>"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' height='16' viewBox='0 0 16 16' version='1.1' width='16'%3E%3Cpath d='M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z'%3E%3C/path%3E%3C/svg%3E"</span><span>)</span></div></div><div><div><div>334</div></div><div>
</div></div><div><div><div>335</div></div><div><span>  </span><span>.gc-license</span></div></div><div><div><div>336</div></div><div><span><span><span>    </span></span><span>display: </span></span><span>none</span></div></div><div><div><div>337</div></div><div>
</div></div><div><div><div>338</div></div><div><span>  </span><span>.gc-forks</span></div></div><div><div><div>339</div></div><div><span><span>    </span>&amp;</span><span>:before</span></div></div><div><div><div>340</div></div><div><span><span><span>      </span></span><span>mask-image: </span></span><span>url</span><span>(</span><span>"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' height='16' viewBox='0 0 16 16' version='1.1' width='16'%3E%3Cpath d='M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z'%3E%3C/path%3E%3C/svg%3E"</span><span>)</span></div></div><div><div><div>341</div></div><div>
</div></div><div><div><div>342</div></div><div><span>  </span><span>.codeberg-logo</span></div></div><div><div><div>343</div></div><div><span><span><span>    </span></span><span>font-size: </span></span><span><span>1.25</span><span>rem</span></span></div></div><div><div><div>344</div></div><div>
</div></div><div><div><div>345</div></div><div><span><span>    </span>&amp;</span><span>:before</span></div></div><div><div><div>346</div></div><div><span><span><span>      </span></span><span>background-color: </span></span><span>var</span><span>(</span><span>--tw-prose-headings</span><span>)</span></div></div><div><div><div>347</div></div><div><span><span><span>      </span></span><span>margin-right: </span></span><span>0</span></div></div><div><div><div>348</div></div><div><span><span><span>      </span></span><span>mask-image: </span></span><span>url</span><span>(</span><span>"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 128 128'%3E%3C!-- Icon from Devicon by konpa - https://github.com/devicons/devicon/blob/master/LICENSE --%3E%3Cdefs%3E%3ClinearGradient id='SVG5hsFW4SY' x1='42519.285' x2='42575.336' y1='-7078.789' y2='-6966.931' gradientTransform='translate(-31147.619 5228.94)scale(.73409)' gradientUnits='userSpaceOnUse' href='%23SVGB17cveaH'/%3E%3ClinearGradient id='SVGB17cveaH'%3E%3Cstop offset='0' stop-color='%232185d0' stop-opacity='0'/%3E%3Cstop offset='.495' stop-color='%232185d0' stop-opacity='.3'/%3E%3Cstop offset='1' stop-color='%232185d0' stop-opacity='.3'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath fill='url(%23SVG5hsFW4SY)' d='M65.38 32.468a.558.417 0 0 0-.541.496l24.655 92.413a64 64 0 0 0 28.91-24.785L65.867 32.674a.558.417 0 0 0-.487-.206'/%3E%3Cpath fill='%232185d0' d='M63.998 2.623C28.652 2.623 0 31.28 0 66.625a64.03 64.03 0 0 0 9.77 33.993L63.133 31.63c.378-.495 1.345-.495 1.73 0l53.36 68.987A64 64 0 0 0 128 66.625c0-35.346-28.652-64.002-64.002-64.002'/%3E%3C/svg%3E"</span><span>)</span></div></div><div><div><div>349</div></div><div>
</div></div><div><div><div>350</div></div><div><span>a</span><span>.card-codeberg.fetch-waiting</span></div></div><div><div><div>351</div></div><div><span><span><span>  </span></span><span>pointer-events: </span></span><span>none</span></div></div><div><div><div>352</div></div><div><span><span><span>  </span></span><span>opacity: </span></span><span>0.7</span></div></div><div><div><div>353</div></div><div><span><span><span>  </span></span><span>transition: </span></span><span>opacity</span><span> </span><span><span>0.15</span><span>s</span></span><span> </span><span>ease-in-out</span></div></div><div><div><div>354</div></div><div>
</div></div><div><div><div>355</div></div><div><span>  </span><span>.gc-description</span><span>, </span><span>.gc-infobar</span><span>, </span><span>.gc-avatar</span></div></div><div><div><div>356</div></div><div><span><span><span>    </span></span><span>background-color: </span></span><span>var</span><span>(</span><span>--tw-prose-body</span><span>)</span></div></div><div><div><div>357</div></div><div><span><span><span>    </span></span><span>color: </span></span><span>transparent</span></div></div><div><div><div>358</div></div><div><span><span><span>    </span></span><span>opacity: </span></span><span>0.5</span><span>;</span></div></div><div><div><div>359</div></div><div><span><span><span>    </span></span><span>animation: pulsate </span></span><span><span>2</span><span>s</span></span><span> </span><span>infinite</span><span> </span><span>linear</span></div></div><div><div><div>360</div></div><div><span><span><span>    </span></span><span>user-select: </span></span><span>none</span></div></div><div><div><div>361</div></div><div>
</div></div><div><div><div>362</div></div><div><span><span>    </span>&amp;</span><span>:before</span></div></div><div><div><div>363</div></div><div><span><span><span>      </span></span><span>background-color: </span></span><span>transparent</span></div></div><div><div><div>364</div></div><div>
</div></div><div><div><div>365</div></div><div><span>  </span><span>.gc-repo</span></div></div><div><div><div>366</div></div><div><span><span><span>    </span></span><span>margin-left: </span></span><span><span>-0.1</span><span>rem</span></span></div></div><div><div><div>367</div></div><div>
</div></div><div><div><div>368</div></div><div><span>  </span><span>.gc-description</span><span>, </span><span>.gc-infobar</span></div></div><div><div><div>369</div></div><div><span><span><span>    </span></span><span>border-radius: </span></span><span><span>0.5</span><span>rem</span></span></div></div><div><div><div>370</div></div><div>
</div></div><div><div><div>371</div></div><div><span>a</span><span>.card-codeberg.fetch-error</span></div></div><div><div><div>372</div></div><div><span><span><span>  </span></span><span>pointer-events: </span></span><span>all</span></div></div><div><div><div>373</div></div><div><span><span><span>  </span></span><span>opacity: </span></span><span>1</span></div></div><div><div><div>374</div></div><div>
</div></div><div><div><div>375</div></div><div><span>@keyframes</span><span> </span><span>pulsate</span></div></div><div><div><div></div></div><div><span></span><span></span><span>11 collapsed lines</span></div></div><div><div><div>376</div></div><div><span>  </span><span>0%</span></div></div><div><div><div>377</div></div><div><span><span>    </span></span><span>opacity: </span><span>0.15</span></div></div><div><div><div>378</div></div><div><span>  </span><span>50%</span></div></div><div><div><div>379</div></div><div><span><span>    </span></span><span>opacity: </span><span>0.25</span></div></div><div><div><div>380</div></div><div><span>  </span><span>100%</span></div></div><div><div><div>381</div></div><div><span><span>    </span></span><span>opacity: </span><span>0.15</span></div></div><div><div><div>382</div></div><div>
</div></div><div><div><div>383</div></div><div><span>.card-codeberg</span><span>, </span><span>.card-github</span><span>, </span><span>.gc-description</span><span>, </span><span>.gc-titlebar</span><span>, </span><span>.gc-stars</span><span>, </span><span>.gc-forks</span><span>, </span><span>.gc-license</span><span>, </span><span>.gc-avatar</span><span>, </span><span>.codeberg-logo</span><span>, </span><span>.github-logo</span></div></div><div><div><div>384</div></div><div><span><span>  </span></span><span>transition-property: </span><span>all</span></div></div><div><div><div>385</div></div><div><span><span>  </span></span><span>transition-timing-function: </span><span>cubic-bezier</span><span>(</span><span>0.4</span><span>, </span><span>0</span><span>, </span><span>0.2</span><span>, </span><span>1</span><span>)</span></div></div><div><div><div>386</div></div><div><span><span>  </span></span><span>transition-duration: </span><span><span>0.15</span><span>s</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>效果：</p></section>
<section><h1>7. 增加图片标题<a href="#7-增加图片标题"><span>#</span></a></h1><p>参考<a href="https://github.com/saicaca/fuwari/pull/351" target="_blank">这个 PR</a></p></section>
<section><h1>8. 更改字体<a href="#8-更改字体"><span>#</span></a></h1><p>参考<a href="https://blog.aulypc0x0.online/posts/use_custom_fonts_in_fuwari" target="_blank">这篇文章</a></p></section>
<section><h1>9. 亮色代码块<a href="#9-亮色代码块"><span>#</span></a></h1><p>参考<a href="https://github.com/saicaca/fuwari/pull/593" target="_blank">这个 PR</a></p></section>
<section><h1>10. GitHub 文件卡片<a href="#10-github-文件卡片"><span>#</span></a></h1><p>参考<a href="https://github.com/saicaca/fuwari/pull/700" target="_blank">这个 PR</a></p><p>未完待续…</p></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/free-inputs-mobile/</id>
      <title type="text">自由软件之输入法————手机篇</title>
      <published>2026-03-07T00:00:00.000Z</published>
      <updated>2026-03-07T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/free-inputs-mobile/"/>
      <summary type="text">本文系统介绍多平台自由输入法：Android 推荐同文（高度定制）、小企鹅（功能丰富）、语燕；iOS 与鸿蒙需侧载安装小企鹅；Linux 桌面环境按常规方式配置。各输入法均提供 GitHub 或 F-Droid 下载链接，满足不同用户对自由输入工具的需求。</summary>
      <content type="html"><![CDATA[<p>本篇文章将介绍几款自由的手机输入法。</p>
<section><h1>Android<a href="#android"><span>#</span></a></h1><section><h2>同文输入法 (Trime)<a href="#同文输入法-trime"><span>#</span></a></h2><a href="https://github.com/osfans/trime" target="_blank"><div><div><div><div></div><div>osfans</div></div><div>/</div><div>trime</div></div><div></div></div><div>同文安卓輸入法平臺3.x/Android-rime/Rime Input Method Engine for Android</div><div><div>—</div><div>—</div><div>GPL-3.0</div><span>Kotlin</span></div></a><p>下载链接：<br />
<a href="https://f-droid.org/packages/com.osfans.trime" target="_blank"><img alt="Get it on F-Droid" src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" width="207" height="80" /></a>
<a href="https://github.com/osfans/trime/releases/latest" target="_blank"><img src="https://github.com/rubenpgrady/get-it-on-github/raw/refs/heads/main/get-it-on-github.png" alt="Get it on GitHub" width="207" height="80" /></a></p></section><section><h2>小企鹅输入法 (Fcitx 5 For Android)<a href="#小企鹅输入法-fcitx-5-for-android"><span>#</span></a></h2><a href="https://github.com/fcitx5-android/fcitx5-android" target="_blank"><div><div><div><div></div><div>fcitx5-android</div></div><div>/</div><div>fcitx5-android</div></div><div></div></div><div>Fcitx5 input method framework and engines ported to Android</div><div><div>—</div><div>—</div><div>LGPL-2.1</div><span>Kotlin</span></div></a><p>最新稳定版下载链接：<br />
<a href="https://f-droid.org/packages/org.fcitx.fcitx5.android" target="_blank"><img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" width="207" height="80" /></a>
<a href="https://github.com/fcitx5-android/fcitx5-android/releases/latest" target="_blank"><img src="https://github.com/rubenpgrady/get-it-on-github/raw/refs/heads/main/get-it-on-github.png" alt="Get it on GitHub" width="207" height="80" /></a> <br />
最新构建版下载链接：<br />
<a href="https://jenkins.fcitx-im.org/job/android/job/fcitx5-android/" target="_blank"></a></p><figure><a href="https://jenkins.fcitx-im.org/job/android/job/fcitx5-android/" target="_blank"><img src="https://img.shields.io/jenkins/build.svg?jobUrl=https://jenkins.fcitx-im.org/job/android/job/fcitx5-android/" alt="build status" /><figcaption>build status</figcaption></a></figure><p></p></section><section><h2>语燕输入法 (YuyanIME)<a href="#语燕输入法-yuyanime"><span>#</span></a></h2><a href="https://github.com/gurecn/YuyanIme" target="_blank"><div><div><div><div></div><div>gurecn</div></div><div>/</div><div>YuyanIme</div></div><div></div></div><div>语燕输入法-一款基于Rime定制开发的九键、全拼、双拼、手写、火星文等方案、支持悬浮、单手、数字行等键盘模式的中文输入法</div><div><div>—</div><div>—</div><div>BSD-3-Clause</div><span>Kotlin</span></div></a><p>下载链接：<br />
<a href="https://github.com/gurecn/YuyanIme/releases/latest" target="_blank"><img src="https://github.com/rubenpgrady/get-it-on-github/raw/refs/heads/main/get-it-on-github.png" alt="Get it on GitHub" width="207" height="80" /></a></p></section></section>
<section><h1>iOS<a href="#ios"><span>#</span></a></h1><section><h2>Fcitx 5 For iOS<a href="#fcitx-5-for-ios"><span>#</span></a></h2><a href="https://github.com/fcitx-contrib/fcitx5-ios" target="_blank"><div><div><div><div></div><div>fcitx-contrib</div></div><div>/</div><div>fcitx5-ios</div></div><div></div></div><div>Fcitx5 iOS edition, currently developer beta. 小企鹅输入法 iOS 开发者公测 https://t.me/fcitx5macos/16126</div><div><div>—</div><div>—</div><div>GPL-3.0</div><span>Swift</span></div></a><p>需要侧载安装。</p></section></section>
<section><h1>Harmony OS<a href="#harmony-os"><span>#</span></a></h1><section><h2>小企鹅输入法鸿蒙版 (Fcitx 5 For Harmony OS)<a href="#小企鹅输入法鸿蒙版-fcitx-5-for-harmony-os"><span>#</span></a></h2><a href="https://github.com/fcitx-contrib/fcitx5-harmony" target="_blank"><div><div><div><div></div><div>fcitx-contrib</div></div><div>/</div><div>fcitx5-harmony</div></div><div></div></div><div>Fcitx5 Harmony edition, currently not launched. 小企鹅输入法鸿蒙版 还没上架 https://t.me/fcitx5harmony</div><div><div>—</div><div>—</div><div>GPL-3.0</div><span>C</span></div></a><p>下载链接：<br />
<a href="https://github.com/fcitx-contrib/fcitx5-harmony/releases/tag/latest" target="_blank"><img src="https://github.com/rubenpgrady/get-it-on-github/raw/refs/heads/main/get-it-on-github.png" alt="Git it on GitHub" width="207" height="80" /></a> <br />
同样需要侧载安装。</p></section></section>
<section><h1>postmarket OS / Ubuntu Touch / Mobian / Pure OS<a href="#postmarket-os--ubuntu-touch--mobian--pure-os"><span>#</span></a></h1><p>输入法安装方式与 GNU/Linux 相同，这里不再详细讲解。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/script-language-vscode-extension/</id>
      <title type="text">为自己的脚本语言编写VSCode支持扩展：从零到发布完整指南</title>
      <published>2026-01-02T00:00:00.000Z</published>
      <updated>2026-01-02T00:00:00.000Z</updated>
      <author><name>colaSensei</name></author>
      <link rel="alternate" href="https://clina.top/posts/script-language-vscode-extension/"/>
      <summary type="text">本文详细介绍为自定义脚本语言开发VSCode扩展的全过程，涵盖语法高亮、代码片段等基础功能，并指导打包发布与持续维护。</summary>
      <content type="html"><![CDATA[<section><h1>为自己的脚本语言编写VSCode支持扩展：从零到发布完整指南<a href="#为自己的脚本语言编写vscode支持扩展从零到发布完整指南"><span>#</span></a></h1><blockquote><p>作者：<a href="https://github.com/colaSensei" target="_blank">colaSensei</a> | 发布时间：2026年1月</p></blockquote><p>最近我为自己设计的游戏脚本语言PGNCode开发了VSCode扩展，过程中积累了不少经验。如果你也有自己的DSL（领域特定语言）或脚本语言，这篇文章将带你完整走过为它创建VSCode扩展的每一个步骤。</p><section><h2>为什么需要为自定义语言开发VSCode扩展？<a href="#为什么需要为自定义语言开发vscode扩展"><span>#</span></a></h2><p>在我开发PaperVisualNovel引擎时，设计了PGNCode脚本语言。编写游戏需要编辑<code>.pgn</code>文件，但遇到了一些问题：</p><ol>
<li><strong>没有语法高亮</strong>：所有文本都是同一个颜色，难以区分命令、变量和注释</li>
<li><strong>没有代码提示</strong>：需要记忆所有命令和参数格式</li>
<li><strong>没有错误检查</strong>：语法错误只能在运行时发现</li>
<li><strong>开发体验差</strong>：缺少现代IDE应有的功能</li>
</ol><p>这些问题严重影响了开发效率。于是，我决定为PGNCode创建VSCode扩展。</p></section><section><h2>准备工作<a href="#准备工作"><span>#</span></a></h2><section><h3>技术栈需求<a href="#技术栈需求"><span>#</span></a></h3><ul>
<li><strong>Node.js</strong> (&gt;= 14.x)（这个应该都有吧）</li>
<li><strong>Visual Studio Code</strong> (用于开发和测试，这个不可能没有吧)</li>
<li><strong>基础的前端知识</strong> (JSON, 正则表达式)</li>
</ul></section><section><h3>开发工具<a href="#开发工具"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装VSCode扩展开发工具</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>yo</span><span> </span><span>generator-code</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>第一步：创建扩展项目<a href="#第一步创建扩展项目"><span>#</span></a></h2><p>使用官方生成器创建基础项目：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 创建项目目录</span></div></div><div><div><div>2</div></div><div><span>mkdir</span><span> </span><span>my-language-extension</span></div></div><div><div><div>3</div></div><div><span>cd</span><span> </span><span>my-language-extension</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 运行生成器</span></div></div><div><div><div>6</div></div><div><span>yo</span><span> </span><span>code</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在交互式界面中选择：</p><ul>
<li><strong>New Language Support</strong> (新语言支持)</li>
<li>输入扩展名称、描述</li>
<li>指定语言ID、文件扩展名</li>
</ul><p>这会生成一个基础的项目结构。</p></section><section><h2>第二步：理解项目结构<a href="#第二步理解项目结构"><span>#</span></a></h2><p>生成的项目包含以下核心文件：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>my-language-extension/</span></div></div><div><div><div>2</div></div><div><span>├── package.json              # 扩展清单</span></div></div><div><div><div>3</div></div><div><span>├── syntaxes/</span></div></div><div><div><div>4</div></div><div><span>│   └── language.tmLanguage.json # 语法定义</span></div></div><div><div><div>5</div></div><div><span>├── language-configuration.json  # 语言配置</span></div></div><div><div><div>6</div></div><div><span>└── README.md                 # 说明文档</span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h3>1. package.json - 扩展清单<a href="#1-packagejson---扩展清单"><span>#</span></a></h3><p>这是扩展的”身份证”，定义了扩展的基本信息和功能。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span>    </span><span>"name"</span><span>: </span><span>"my-language-support"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>    </span><span>"displayName"</span><span>: </span><span>"My Language Support"</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>"description"</span><span>: </span><span>"Syntax highlighting for My Language"</span><span>,</span></div></div><div><div><div>5</div></div><div><span>    </span><span>"version"</span><span>: </span><span>"1.0.0"</span><span>,</span></div></div><div><div><div>6</div></div><div><span>    </span><span>"publisher"</span><span>: </span><span>"your-name"</span><span>,</span></div></div><div><div><div>7</div></div><div><span>    </span><span>"engines"</span><span>: { </span><span>"vscode"</span><span>: </span><span>"^1.75.0"</span><span> },</span></div></div><div><div><div>8</div></div><div><span>    </span><span>"categories"</span><span>: [</span><span>"Programming Languages"</span><span>],</span></div></div><div><div><div>9</div></div><div><span>    </span><span>"contributes"</span><span>: {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>"languages"</span><span>: [{</span></div></div><div><div><div>11</div></div><div><span>            </span><span>"id"</span><span>: </span><span>"mylang"</span><span>,</span></div></div><div><div><div>12</div></div><div><span>            </span><span>"aliases"</span><span>: [</span><span>"My Language"</span><span>, </span><span>"mylang"</span><span>],</span></div></div><div><div><div>13</div></div><div><span>            </span><span>"extensions"</span><span>: [</span><span>".mylang"</span><span>, </span><span>".ml"</span><span>],</span></div></div><div><div><div>14</div></div><div><span>            </span><span>"configuration"</span><span>: </span><span>"./language-configuration.json"</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>}],</span></div></div><div><div><div>16</div></div><div><span>        </span><span>"grammars"</span><span>: [{</span></div></div><div><div><div>17</div></div><div><span>            </span><span>"language"</span><span>: </span><span>"mylang"</span><span>,</span></div></div><div><div><div>18</div></div><div><span>            </span><span>"scopeName"</span><span>: </span><span>"source.mylang"</span><span>,</span></div></div><div><div><div>19</div></div><div><span>            </span><span>"path"</span><span>: </span><span>"./syntaxes/mylang.tmLanguage.json"</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>}]</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2. 语法定义文件 (TextMate语法)<a href="#2-语法定义文件-textmate语法"><span>#</span></a></h3><p>这是最核心的部分，定义了如何高亮代码。VSCode使用TextMate语法系统。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span>    </span><span>"$schema"</span><span>: </span><span>"https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>    </span><span>"scopeName"</span><span>: </span><span>"source.mylang"</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>"patterns"</span><span>: [</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>6</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"comment.line.mylang"</span><span>,</span></div></div><div><div><div>7</div></div><div><span>            </span><span>"match"</span><span>: </span><span>"//.*"</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>10</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"keyword.control.mylang"</span><span>,</span></div></div><div><div><div>11</div></div><div><span>            </span><span>"match"</span><span>: </span><span>"</span><span>\\</span><span>b(if|else|while|for|return)</span><span>\\</span><span>b"</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>14</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"constant.numeric.mylang"</span><span>,</span></div></div><div><div><div>15</div></div><div><span>            </span><span>"match"</span><span>: </span><span>"</span><span>\\</span><span>b</span><span>\\</span><span>d+(</span><span>\\</span><span>.</span><span>\\</span><span>d+)?</span><span>\\</span><span>b"</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>]</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>关键概念</strong>：</p><ul>
<li><code>scopeName</code>: 语法的根作用域</li>
<li><code>patterns</code>: 匹配规则数组</li>
<li><code>name</code>: 作用域名称，决定高亮颜色</li>
<li><code>match</code>: 正则表达式匹配模式</li>
</ul></section><section><h3>3. 语言配置文件<a href="#3-语言配置文件"><span>#</span></a></h3><p>定义语言的编辑特性：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span>    </span><span>"comments"</span><span>: {</span></div></div><div><div><div>3</div></div><div><span>        </span><span>"lineComment"</span><span>: </span><span>"//"</span><span>,</span></div></div><div><div><div>4</div></div><div><span>        </span><span>"blockComment"</span><span>: [</span><span>"/*"</span><span>, </span><span>"*/"</span><span>]</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>6</div></div><div><span>    </span><span>"brackets"</span><span>: [</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>[</span><span>"{"</span><span>, </span><span>"}"</span><span>],</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>[</span><span>"["</span><span>, </span><span>"]"</span><span>],</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>[</span><span>"("</span><span>, </span><span>")"</span><span>]</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>],</span></div></div><div><div><div>11</div></div><div><span>    </span><span>"autoClosingPairs"</span><span>: [</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>{ </span><span>"open"</span><span>: </span><span>"{"</span><span>, </span><span>"close"</span><span>: </span><span>"}"</span><span> },</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>{ </span><span>"open"</span><span>: </span><span>"["</span><span>, </span><span>"close"</span><span>: </span><span>"]"</span><span> },</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>{ </span><span>"open"</span><span>: </span><span>"("</span><span>, </span><span>"close"</span><span>: </span><span>")"</span><span> },</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>{ </span><span>"open"</span><span>: </span><span>"</span><span>\"</span><span>"</span><span>, </span><span>"close"</span><span>: </span><span>"</span><span>\"</span><span>"</span><span> }</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>],</span></div></div><div><div><div>17</div></div><div><span>    </span><span>"indentationRules"</span><span>: {</span></div></div><div><div><div>18</div></div><div><span>        </span><span>"increaseIndentPattern"</span><span>: </span><span>"^.*</span><span>\\</span><span>{[^}</span><span>\"</span><span>]*$|^.*</span><span>\\</span><span>([^)</span><span>\"</span><span>]*$"</span><span>,</span></div></div><div><div><div>19</div></div><div><span>        </span><span>"decreaseIndentPattern"</span><span>: </span><span>"^</span><span>\\</span><span>s*</span><span>\\</span><span>}|^</span><span>\\</span><span>s*</span><span>\\</span><span>)"</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>第三步：设计语法高亮规则<a href="#第三步设计语法高亮规则"><span>#</span></a></h2><section><h3>分析语言特性<a href="#分析语言特性"><span>#</span></a></h3><p>以我的PGNCode为例，我需要识别：</p><ol>
<li><strong>注释</strong>：<code>// 这是注释</code></li>
<li><strong>命令</strong>：<code>say</code>, <code>wait</code>, <code>set</code>, <code>if</code> 等</li>
<li><strong>变量</strong>：<code>${variable}</code> 格式</li>
<li><strong>数字</strong>：整数和小数</li>
<li><strong>字符串</strong>：引号内的文本</li>
<li><strong>标签</strong>：<code>LABEL_NAME:</code> 格式</li>
</ol></section><section><h3>编写匹配规则<a href="#编写匹配规则"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span>    </span><span>"scopeName"</span><span>: </span><span>"source.pgn"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>    </span><span>"patterns"</span><span>: [</span></div></div><div><div><div>4</div></div><div><span>        </span><span>// 注释</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>6</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"comment.line.double-slash.pgn"</span><span>,</span></div></div><div><div><div>7</div></div><div><span>            </span><span>"match"</span><span>: </span><span>"//.*"</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>        </span><span>// 命令（小心处理边界）</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>12</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"keyword.control.pgn"</span><span>,</span></div></div><div><div><div>13</div></div><div><span>            </span><span>"match"</span><span>: </span><span>"(?&lt;!</span><span>\\</span><span>$</span><span>\\</span><span>{)</span><span>\\</span><span>b(say|wait|set|if|choose)</span><span>\\</span><span>b(?!</span><span>\\</span><span>})"</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>        </span><span>// 变量引用</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>18</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"variable.parameter.pgn"</span><span>,</span></div></div><div><div><div>19</div></div><div><span>            </span><span>"begin"</span><span>: </span><span>"</span><span>\\</span><span>$</span><span>\\</span><span>{"</span><span>,</span></div></div><div><div><div>20</div></div><div><span>            </span><span>"end"</span><span>: </span><span>"</span><span>\\</span><span>}"</span><span>,</span></div></div><div><div><div>21</div></div><div><span>            </span><span>"patterns"</span><span>: [</span></div></div><div><div><div>22</div></div><div><span><span>                </span></span><span>{</span></div></div><div><div><div>23</div></div><div><span>                    </span><span>"name"</span><span>: </span><span>"variable.other.pgn"</span><span>,</span></div></div><div><div><div>24</div></div><div><span>                    </span><span>"match"</span><span>: </span><span>"[a-zA-Z_][a-zA-Z0-9_]*"</span></div></div><div><div><div>25</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>26</div></div><div><span><span>            </span></span><span>]</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>        </span><span>// 标签定义（行首的单词后跟冒号）</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>31</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"entity.name.tag.pgn"</span><span>,</span></div></div><div><div><div>32</div></div><div><span>            </span><span>"match"</span><span>: </span><span>"^</span><span>\\</span><span>s*[A-Za-z_][A-Za-z0-9_]*:"</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>]</span></div></div><div><div><div>35</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>技巧</strong>：</p><ol>
<li>使用<code>begin</code>/<code>end</code>处理嵌套结构</li>
<li>使用负向零宽断言避免错误匹配</li>
<li>从简单规则开始，逐步完善</li>
</ol></section></section><section><h2>第四步：添加代码片段<a href="#第四步添加代码片段"><span>#</span></a></h2><p>代码片段可以极大提高编码效率：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span>    </span><span>"If Command"</span><span>: {</span></div></div><div><div><div>3</div></div><div><span>        </span><span>"prefix"</span><span>: [</span><span>"if"</span><span>, </span><span>"IF"</span><span>],</span></div></div><div><div><div>4</div></div><div><span>        </span><span>"body"</span><span>: [</span></div></div><div><div><div>5</div></div><div><span>            </span><span>"if ${1:variable} ${2|==,!=,&gt;,&lt;,&gt;=,&lt;=|} ${3:value} ${4:LABEL_NAME}"</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>],</span></div></div><div><div><div>7</div></div><div><span>        </span><span>"description"</span><span>: </span><span>"Conditional jump"</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>9</div></div><div><span>    </span><span>"Say Command"</span><span>: {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>"prefix"</span><span>: </span><span>"say"</span><span>,</span></div></div><div><div><div>11</div></div><div><span>        </span><span>"body"</span><span>: [</span></div></div><div><div><div>12</div></div><div><span>            </span><span>"say ${1:text} ${2:0.5} ${3|white,black,blue,green,red,yellow|}"</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>],</span></div></div><div><div><div>14</div></div><div><span>        </span><span>"description"</span><span>: </span><span>"Display text with typing effect"</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>特性</strong>：</p><ul>
<li><code>prefix</code>: 触发补全的文本</li>
<li><code>body</code>: 插入的代码，支持占位符<code>${n:default}</code></li>
<li>选择列表：<code>${1|option1,option2|}</code></li>
<li>变量：<code>${TM_FILENAME}</code>等内置变量</li>
</ul></section><section><h2>第五步：本地测试<a href="#第五步本地测试"><span>#</span></a></h2><section><h3>启动调试<a href="#启动调试"><span>#</span></a></h3><ol>
<li>在VSCode中打开扩展项目</li>
<li>按 <code>F5</code> 启动扩展开发主机</li>
<li>在新窗口中创建测试文件</li>
<li>验证语法高亮和代码片段</li>
</ol></section><section><h3>常见问题调试<a href="#常见问题调试"><span>#</span></a></h3><p><strong>问题1：语法高亮不工作</strong></p><ul>
<li>检查<code>.tmLanguage.json</code>格式</li>
<li>验证正则表达式是否正确</li>
<li>检查文件扩展名关联</li>
</ul><p><strong>问题2：代码片段不出现</strong></p><ul>
<li>检查<code>snippets/*.json</code>文件格式</li>
<li>确认<code>snippet</code>的<code>language</code>字段正确</li>
<li>重启扩展主机</li>
</ul><p><strong>问题3：扩展无法激活</strong></p><ul>
<li>检查<code>package.json</code>中的<code>activationEvents</code></li>
<li>查看”输出”面板的”扩展主机”日志</li>
</ul></section></section><section><h2>第六步：高级功能<a href="#第六步高级功能"><span>#</span></a></h2><section><h3>1. 添加命令和配置<a href="#1-添加命令和配置"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span>    </span><span>"contributes"</span><span>: {</span></div></div><div><div><div>3</div></div><div><span>        </span><span>"commands"</span><span>: [{</span></div></div><div><div><div>4</div></div><div><span>            </span><span>"command"</span><span>: </span><span>"mylang.formatDocument"</span><span>,</span></div></div><div><div><div>5</div></div><div><span>            </span><span>"title"</span><span>: </span><span>"Format MyLang Document"</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>}],</span></div></div><div><div><div>7</div></div><div><span>        </span><span>"configuration"</span><span>: {</span></div></div><div><div><div>8</div></div><div><span>            </span><span>"title"</span><span>: </span><span>"My Language"</span><span>,</span></div></div><div><div><div>9</div></div><div><span>            </span><span>"properties"</span><span>: {</span></div></div><div><div><div>10</div></div><div><span>                </span><span>"mylang.enableSyntaxHighlighting"</span><span>: {</span></div></div><div><div><div>11</div></div><div><span>                    </span><span>"type"</span><span>: </span><span>"boolean"</span><span>,</span></div></div><div><div><div>12</div></div><div><span>                    </span><span>"default"</span><span>: </span><span>true</span><span>,</span></div></div><div><div><div>13</div></div><div><span>                    </span><span>"description"</span><span>: </span><span>"Enable syntax highlighting"</span></div></div><div><div><div>14</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>18</div></div><div><span>    </span><span>"activationEvents"</span><span>: [</span></div></div><div><div><div>19</div></div><div><span>        </span><span>"onLanguage:mylang"</span><span>,</span></div></div><div><div><div>20</div></div><div><span>        </span><span>"onCommand:mylang.formatDocument"</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>]</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2. 创建语言服务器（可选）<a href="#2-创建语言服务器可选"><span>#</span></a></h3><p>对于复杂的语言功能（代码补全、诊断、跳转定义），可以考虑实现Language Server：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 创建Language Server</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>vscode</span><span> </span><span>from</span><span> </span><span>'vscode'</span><span>;</span></div></div><div><div><div>3</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>lsp</span><span> </span><span>from</span><span> </span><span>'vscode-languageclient'</span><span>;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>export</span><span> </span><span>function</span><span> </span><span>activate</span><span><span>(</span><span>context</span></span><span>:</span><span> </span><span>vscode</span><span>.</span><span>ExtensionContext</span><span>) {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>const</span><span> </span><span>serverOptions</span><span>:</span><span> </span><span>lsp</span><span>.</span><span>ServerOptions</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>7</div></div><div><span>        </span><span>command</span><span>:</span><span> </span><span>'node'</span><span>,</span></div></div><div><div><div>8</div></div><div><span>        </span><span>args</span><span>:</span><span><span> [</span><span>context</span><span>.</span></span><span>asAbsolutePath</span><span>(</span><span>'server/server.js'</span><span>)]</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>const</span><span> </span><span>clientOptions</span><span>:</span><span> </span><span>lsp</span><span>.</span><span>LanguageClientOptions</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>documentSelector</span><span>:</span><span> [{ </span><span>scheme</span><span>:</span><span> </span><span>'file'</span><span>, </span><span>language</span><span>:</span><span> </span><span>'mylang'</span><span> }]</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>const</span><span> </span><span>client</span><span> </span><span>=</span><span> </span><span>new</span><span><span> </span><span>lsp</span><span>.</span></span><span>LanguageClient</span><span>(</span></div></div><div><div><div>16</div></div><div><span>        </span><span>'mylangServer'</span><span>,</span></div></div><div><div><div>17</div></div><div><span>        </span><span>'My Language Server'</span><span>,</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>serverOptions</span><span>,</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>clientOptions</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>client</span><span>.</span><span>start</span><span>();</span></div></div><div><div><div>23</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>第七步：打包和发布<a href="#第七步打包和发布"><span>#</span></a></h2><section><h3>1. 安装打包工具<a href="#1-安装打包工具"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>@vscode/vsce</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2. 准备发布<a href="#2-准备发布"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 更新版本号</span></div></div><div><div><div>2</div></div><div><span># 完善README.md</span></div></div><div><div><div>3</div></div><div><span># 添加LICENSE</span></div></div><div><div><div>4</div></div><div><span># 更新CHANGELOG.md</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3. 打包<a href="#3-打包"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>vsce</span><span> </span><span>package</span></div></div><div><div><div>2</div></div><div><span># 生成: my-language-support-1.0.0.vsix</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>4. 发布到市场<a href="#4-发布到市场"><span>#</span></a></h3><p><strong>方法A：直接发布</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>vsce</span><span> </span><span>publish</span></div></div><div><div><div>2</div></div><div><span># 需要发布者令牌</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>方法B：手动上传</strong></p><ol>
<li>访问 <a href="https://marketplace.visualstudio.com/manage" target="_blank">VS Code Marketplace</a></li>
<li>点击”上传扩展”</li>
<li>选择生成的<code>.vsix</code>文件</li>
</ol></section></section><section><h2>第八步：持续维护<a href="#第八步持续维护"><span>#</span></a></h2><section><h3>监控反馈<a href="#监控反馈"><span>#</span></a></h3><ul>
<li>关注GitHub Issues</li>
<li>查看市场评价</li>
<li>收集用户需求</li>
</ul></section><section><h3>版本更新流程<a href="#版本更新流程"><span>#</span></a></h3><ol>
<li>修改代码</li>
<li>更新版本号</li>
<li>更新CHANGELOG</li>
<li>测试</li>
<li>打包</li>
<li>发布</li>
</ol></section><section><h3>最佳实践<a href="#最佳实践"><span>#</span></a></h3><ol>
<li><strong>渐进增强</strong>：先实现语法高亮，再逐步添加高级功能</li>
<li><strong>测试驱动</strong>：为每个功能创建测试用例</li>
<li><strong>文档完善</strong>：清晰的README和注释</li>
<li><strong>性能优化</strong>：避免复杂的正则表达式</li>
<li><strong>向后兼容</strong>：谨慎修改现有功能</li>
</ol></section></section><section><h2>实战经验分享<a href="#实战经验分享"><span>#</span></a></h2><section><h3>踩过的坑<a href="#踩过的坑"><span>#</span></a></h3><p><strong>正则表达式性能问题</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 错误：过于复杂的正则</span></div></div><div><div><div>2</div></div><div><span>"match"</span><span>: </span><span>"</span><span>\\</span><span>b(say|wait|show|end|set|random|jump|if|choose|endname|SAY|WAIT|SHOW|END|SET|RANDOM|JUMP|IF|CHOOSE|ENDNAME)</span><span>\\</span><span>b"</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 优化：使用忽略大小写标志</span></div></div><div><div><div>5</div></div><div><span>"match"</span><span>: </span><span>"(?i)</span><span>\\</span><span>b(say|wait|show|end|set|random|jump|if|choose|endname)</span><span>\\</span><span>b"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>作用域冲突</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 变量引用内部的文本不应该被当作命令高亮</span></div></div><div><div><div>2</div></div><div><span>// 错误：${say} 中的 "say" 被高亮为命令</span></div></div><div><div><div>3</div></div><div><span>// 解决：在命令匹配中使用负向零宽断言</span></div></div><div><div><div>4</div></div><div><span>"match"</span><span>: </span><span>"(?&lt;!</span><span>\\</span><span>$</span><span>\\</span><span>{)</span><span>\\</span><span>b(say|wait|set)</span><span>\\</span><span>b(?!</span><span>\\</span><span>})"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>效率技巧<a href="#效率技巧"><span>#</span></a></h3><ol>
<li>
<p><strong>使用开发工具</strong>：</p>
<ul>
<li><a href="https://marketplace.visualstudio.com/items?itemName=christian-kohler.textmate-grammar-tester" target="_blank">TextMate Grammar Tester</a></li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-textmate" target="_blank">Scope Inspector</a></li>
</ul>
</li>
<li>
<p><strong>模板化开发</strong>：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 自动生成代码片段的脚本</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>commands</span><span> </span><span>=</span><span> [</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>{ </span><span>name</span><span>:</span><span> </span><span>'say'</span><span>, </span><span>desc</span><span>:</span><span> </span><span>'Display text'</span><span> },</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>{ </span><span>name</span><span>:</span><span> </span><span>'set'</span><span>, </span><span>desc</span><span>:</span><span> </span><span>'Set variable'</span><span> }</span></div></div><div><div><div>5</div></div><div><span>];</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>const</span><span> </span><span>snippets</span><span> </span><span>=</span><span> {};</span></div></div><div><div><div>8</div></div><div><span><span>commands</span><span>.</span></span><span>forEach</span><span><span>(</span><span>cmd</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>snippets</span><span>[</span><span>cmd</span><span>.</span><span>name</span><span>] </span><span>=</span><span> {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>prefix</span><span>:</span><span><span> </span><span>cmd</span><span>.</span></span><span>name</span><span>,</span></div></div><div><div><div>11</div></div><div><span>        </span><span>body</span><span>:</span><span> [</span><span>`</span><span>${</span><span>cmd</span><span>.</span><span>name</span><span>}</span><span> </span><span>\$</span><span>{1:placeholder}`</span><span>],</span></div></div><div><div><div>12</div></div><div><span>        </span><span>description</span><span>:</span><span><span> </span><span>cmd</span><span>.</span></span><span>desc</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>14</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p><strong>测试用例</strong>：
创建测试文件覆盖所有语法特性：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>test-cases.mylang</span></div></div><div><div><div>2</div></div><div><span>├── 注释测试</span></div></div><div><div><div>3</div></div><div><span>├── 命令测试</span></div></div><div><div><div>4</div></div><div><span>├── 变量测试</span></div></div><div><div><div>5</div></div><div><span>└── 边界情况测试</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ol></section></section><section><h2>成果展示<a href="#成果展示"><span>#</span></a></h2><p>完成扩展后，我的PGNCode开发体验得到了极大改善：</p><p><strong>之前</strong>：</p><ul>
<li>纯文本编辑</li>
<li>需要查手册</li>
<li>运行时才发现错误</li>
</ul><p><strong>之后</strong>：</p><ul>
<li>彩色语法高亮</li>
<li>智能代码补全</li>
<li>实时错误提示</li>
<li>开发效率提升300%（哈哈）</li>
</ul></section><section><h2>资源推荐<a href="#资源推荐"><span>#</span></a></h2><ol>
<li><strong>官方文档</strong>：<a href="https://code.visualstudio.com/api" target="_blank">VSCode Extension API</a></li>
<li><strong>示例项目</strong>：<a href="https://github.com/Microsoft/vscode-extension-samples" target="_blank">VSCode Extension Samples</a></li>
<li><strong>语法参考</strong>：<a href="https://macromates.com/manual/en/language_grammars" target="_blank">TextMate Language Grammars</a></li>
<li><strong>正则测试</strong>：<a href="https://regex101.com/" target="_blank">Regex101</a></li>
</ol></section><section><h2>结语<a href="#结语"><span>#</span></a></h2><p>为自己的脚本语言开发VSCode扩展是一项很有价值的工作。它不仅提升了开发体验，也让你的语言看起来更加”专业”。整个过程虽然有些技术细节需要处理，但收获的成就感是巨大的。</p><p><strong>关键点回顾</strong>：</p><ol>
<li>从简单的语法高亮开始</li>
<li>逐步添加代码片段、配置等高级功能</li>
<li>充分测试，特别关注边界情况</li>
<li>文档完善，便于他人使用</li>
<li>持续维护，响应用户反馈</li>
</ol><p>希望这篇文章能帮助你也为自己的语言创建出色的开发工具。</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/fuwari-codeberg-pages/</id>
      <title type="text">如何将你的Fuwari博客部署到Codeberg Pages (Forgejo Actions版)</title>
      <published>2025-12-28T00:00:00.000Z</published>
      <updated>2025-12-28T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/fuwari-codeberg-pages/"/>
      <summary type="text">本文介绍将Fuwari博客部署到Codeberg Pages的方法：先打开Actions，再配置仓库工作流文件，实现代码推送后自动构建并发布至专属页面。</summary>
      <content type="html"><![CDATA[<p>Codeberg Pages 是 Codeberg 官方提供的一个静态网站托管平台，类似于 Github Pages.</p>
<p>今天我们就要将Fuwari博客部署到Codeberg Pages , 使用 Forgejo Actions。</p>
<p>首先，在仓库中点击设置，点击仓库功能 / 概览，打开 Actions，保存设置。</p>
<p>接下来，回到仓库，在仓库根目录新建<code>.forgejo/workflows</code>文件夹.</p>
<p>在文件夹中添加 <code>astro.yml</code> 文件，内容如下：</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>name: Deploy Astro site to Pages</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>on:</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span># Runs on pushes targeting the default branch</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>push:</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>branches:</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span># If you want to build from a different branch, change it here.</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>- main</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span># Allows you to run this workflow manually from the Actions tab</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>workflow_dispatch:</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>jobs:</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>build:</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span># You can find the list of available runners on https://codeberg.org/actions/meta, or run one yourself.</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>runs-on: codeberg-medium-lazy</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>steps:</span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>- name: Clone the repository</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>uses: https://code.forgejo.org/actions/checkout@v4</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>with:</span></div></div><div><div><div>20</div></div><div><span><span>          </span></span><span>submodules: recursive</span></div></div><div><div><div>21</div></div><div><span><span>          </span></span><span>fetch-depth: 0</span></div></div><div><div><div>22</div></div><div><span><span>      </span></span><span>- name: check .domains files</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>run: |</span></div></div><div><div><div>24</div></div><div><span><span>          </span></span><span>git ls-files public/.domains</span></div></div><div><div><div>25</div></div><div><span><span>          </span></span><span>ls -la public/</span></div></div><div><div><div>26</div></div><div><span><span>          </span></span><span>ls -la public/.domains || echo ".domains not found"</span></div></div><div><div><div>27</div></div><div><span><span>      </span></span><span>- name: Setup pnpm</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>uses: pnpm/action-setup@v2</span></div></div><div><div><div>29</div></div><div><span><span>        </span></span><span>with:</span></div></div><div><div><div>30</div></div><div><span><span>          </span></span><span>version: 10</span></div></div><div><div><div>31</div></div><div><span><span>      </span></span><span>- name: Setup Node.js</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>uses: https://code.forgejo.org/actions/setup-node@v4</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>with:</span></div></div><div><div><div>34</div></div><div><span><span>          </span></span><span>node-version: 22</span></div></div><div><div><div>35</div></div><div><span><span>          </span></span><span>cache: 'pnpm'</span></div></div><div><div><div>36</div></div><div><span><span>      </span></span><span>- name: Install dependencies</span></div></div><div><div><div>37</div></div><div><span><span>        </span></span><span>run: |</span></div></div><div><div><div>38</div></div><div><span><span>          </span></span><span>pnpm install</span></div></div><div><div><div>39</div></div><div><span><span>          </span></span><span>pnpm install @astrojs/sitemap</span></div></div><div><div><div>40</div></div><div><span><span>      </span></span><span>- name: Build Astro site</span></div></div><div><div><div>41</div></div><div><span><span>        </span></span><span>run: pnpm run build</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span><span>      </span></span><span>- name: Upload generated files</span></div></div><div><div><div>44</div></div><div><span><span>        </span></span><span>uses: https://code.forgejo.org/actions/upload-artifact@v3</span></div></div><div><div><div>45</div></div><div><span><span>        </span></span><span>with:</span></div></div><div><div><div>46</div></div><div><span><span>          </span></span><span>name: Generated files</span></div></div><div><div><div>47</div></div><div><span><span>          </span></span><span>path: dist/</span></div></div><div><div><div>48</div></div><div><span><span>          </span></span><span>include-hidden-files: true</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span><span>  </span></span><span>deploy:</span></div></div><div><div><div>51</div></div><div><span><span>    </span></span><span>needs: [ build ]</span></div></div><div><div><div>52</div></div><div><span><span>    </span></span><span>runs-on: codeberg-tiny-lazy</span></div></div><div><div><div>53</div></div><div><span><span>    </span></span><span>steps:</span></div></div><div><div><div>54</div></div><div><span><span>      </span></span><span>- name: Clone the repository</span></div></div><div><div><div>55</div></div><div><span><span>        </span></span><span>uses: https://code.forgejo.org/actions/checkout@v4</span></div></div><div><div><div>56</div></div><div><span><span>        </span></span><span>with:</span></div></div><div><div><div>57</div></div><div><span><span>          </span></span><span>submodules: recursive</span></div></div><div><div><div>58</div></div><div><span><span>          </span></span><span>fetch-depth: 0</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span><span>      </span></span><span>- name: Checkout the target branch and clean it up</span></div></div><div><div><div>61</div></div><div><span><span>        </span></span><span>run: |</span></div></div><div><div><div>62</div></div><div><span><span>          </span></span><span>git checkout pages || git switch --orphan pages</span></div></div><div><div><div>63</div></div><div><span><span>          </span></span><span>rm -Rfv $(ls -A | egrep -v '^(</span><span>\.</span><span>git|LICENSE)$')</span></div></div><div><div><div>64</div></div><div>
</div></div><div><div><div>65</div></div><div><span><span>      </span></span><span>- name: Download generated files</span></div></div><div><div><div>66</div></div><div><span><span>        </span></span><span>uses: https://code.forgejo.org/actions/download-artifact@v3</span></div></div><div><div><div>67</div></div><div><span><span>        </span></span><span>with:</span></div></div><div><div><div>68</div></div><div><span><span>          </span></span><span>name: Generated files</span></div></div><div><div><div>69</div></div><div>
</div></div><div><div><div>70</div></div><div><span><span>      </span></span><span>- name: Publish the website</span></div></div><div><div><div>71</div></div><div><span><span>        </span></span><span>run: |</span></div></div><div><div><div>72</div></div><div><span><span>          </span></span><span>git config user.email codeberg-ci</span></div></div><div><div><div>73</div></div><div><span><span>          </span></span><span>git config user.name "Codeberg CI"</span></div></div><div><div><div>74</div></div><div><span><span>          </span></span><span>echo "=== Files being committed ==="</span></div></div><div><div><div>75</div></div><div><span><span>          </span></span><span>ls -la</span></div></div><div><div><div>76</div></div><div><span><span>          </span></span><span>git add .</span></div></div><div><div><div>77</div></div><div><span><span>          </span></span><span>git commit --allow-empty --message "Codeberg build for ${GITHUB_SHA}"</span></div></div><div><div><div>78</div></div><div><span><span>          </span></span><span>git push origin pages</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
<p>然后，就可以愉快的在Codeberg Pages上部署Fuwari博客了！</p>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/free-domain-one/</id>
      <title type="text">FreeDomainOne 域名注册教程</title>
      <published>2025-11-14T00:00:00.000Z</published>
      <updated>2025-11-14T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/free-domain-one/"/>
      <summary type="text">这篇教程介绍了在 FreeDomainOne 注册免费域名的步骤、可用后缀、重要续期规则，并提醒注意注册时的安全风险。</summary>
      <content type="html"><![CDATA[<p>今天推荐的是 FreeDomainOne 的免费域名，他们提供了<code>publicvm.com</code>,<code>run.place</code>,<code>linkpc.net</code>和<code>work.gd</code>后缀的域名.</p>
<p>首先，进入他们的官网：<a href="https://freedomain.one" target="_blank">https://freedomain.one/</a>;</p>
<p>接下来，注册一个账户,并点击发过来的验证邮件里的链接;</p>
<p>然后，回到他们的官网，输入你想要的域名前缀并选择域名后缀，完成后点击<code>Check Availability</code>,检查域名是否可用；</p>
<p>如果可用,点击<code>Claim This Name &gt;&gt;</code>按钮,进入付款页面;</p>
<p>进入付款页面后,保持默认设置,点击<code>SUBMIT &gt;&gt;</code>按钮,注册完成.注意,要记住 Free renew after 后面的日期,以后有用.</p>
<section><h3>注意：注册的域名必须在 Free renew after 后面的日期以后一个月内续期，否则域名会被删除<a href="#注意注册的域名必须在-free-renew-after-后面的日期以后一个月内续期否则域名会被删除"><span>#</span></a></h3><p>教程至此结束.</p><p>补充：推荐使用<a href="https://www.emailtick.com" target="_blank">https://www.emailtick.com/</a> 的临时邮箱注册,因为当你输错用户名和密码时，这个网站会把使用这个用户名的用户注册时用的邮箱直接显示出来,有安全风险.</p></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/osfc-org-cn/</id>
      <title type="text">osfc.org.cn &amp;&amp; osfs.top域名注册教程</title>
      <published>2025-11-14T00:00:00.000Z</published>
      <updated>2025-11-14T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/osfc-org-cn/"/>
      <summary type="text">该教程介绍如何在OSFC注册域名，需使用邀请链接获取积分，每个域名消耗50积分，暂不支持修改NS服务器，仅推荐体验。</summary>
      <content type="html"><![CDATA[<p>如题，今天讲的是这两个域名的注册教程，暂时都无法托管到Cloudflare.<br />
官网地址：<a href="https://registry.osfc.org.cn" target="_blank">https://registry.osfc.org.cn</a><br />
我的邀请链接：<a href="https://registry.osfc.org.cn/register?invite=286" target="_blank">https://registry.osfc.org.cn/register?invite=286</a><br />
推荐使用邀请链接，因为那样你可以拿到20Points奖励<br />
第一步，使用上面两个链接的其中一个，进入官网.<br />
第二步，在右上角找到Sign up按钮，并点击，注册一个账号.<br />
第三步，进入仪表板，点+号添加一个域名.<br />注意，创建一个域名需花费50Points,而注册时会送100Points，也就是可以注册2个域名<br />
这个域名无法修改NS服务器（也许吧），暂时不推荐用作主力域名，只推荐玩一玩.</p>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/zoneabc/</id>
      <title type="text">ZoneABC域名注册教程</title>
      <published>2025-11-14T00:00:00.000Z</published>
      <updated>2025-11-14T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/zoneabc/"/>
      <summary type="text">这篇教程介绍了如何在ZoneABC注册域名，包括可用后缀、注册步骤、积分规则及DNS限制，适合用作备用域名。</summary>
      <content type="html"><![CDATA[<p>如题，今天讲一下ZoneABC域名的注册教程.</p>
<p>ZoneABC提供了<code>webn.cc</code>,<code>zabc.net</code>,<code>sylu.cc</code>,<code>sylu.net</code>,<code>acg.rest</code>,<code>vvvv.ee</code>和<code>ctrl.li</code>后缀的域名.</p>
<p>官网：<a href="https://zoneabc.net" target="_blank">https://zoneabc.net/</a></p>
<p>邀请链接：<a href="https://zoneabc.net/register?invite=cd9373abf598410ea2294f47a1db8d6d" target="_blank">https://zoneabc.net/register?invite=cd9373abf598410ea2294f47a1db8d6d</a></p>
<p>第一步，点击上面两个链接中的其中一个，进入注册界面，推荐使用邀请链接注册，那样你可以获得额外的10积分.</p>
<p>第二步，注册好账户后，进入面板，注册一个域名.<strong>注意，一些前缀比较短的域名会需要积分，经过测试，前缀长度为7及以上的域名无需积分，如果有错误请指正</strong></p>
<p>这个域名比osfc.org.cn良心一点，但DNS记录只可添加20条，适合做备用.</p>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/public-free-suffix/</id>
      <title type="text">PublicFreeSuffix域名申请教程</title>
      <published>2025-10-01T00:00:00.000Z</published>
      <updated>2025-10-01T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/public-free-suffix/"/>
      <summary type="text">本文详细介绍了如何通过GitHub免费申请PublicFreeSuffix域名的详细步骤</summary>
      <content type="html"><![CDATA[<p>如题，此帖讲的是PublicFreeSuffix免费域名的申请教程.</p>
<p>首先，打开他们的GitHub仓库地址：
<a href="https://github.com/PublicFreeSuffix/PublicFreeSuffix" target="_blank">https://github.com/PublicFreeSuffix/PublicFreeSuffix</a></p>
<p>他们提供了<code>pfsdns.org</code>,<code>nastu.net</code>,<code>tun.re</code>,<code>6ti.net</code>和<code>no.kg</code>后缀的免费域名,都无法托管到Cloudflare.</p>
<p>注册域名步骤：</p>
<p>第一步，fork他们的GitHub仓库.</p>
<p>第二步，打开你fork的仓库，在whois文件夹里新建一个json文件，文件名为<code>你想注册的域名.no.kg.json</code>.</p>
<p>这里使用no.kg的域名后缀作为演示，如果想使用其他域名后缀，请把no.kg改为你想使用的域名后缀.</p>
<p>json文件格式：</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span>  </span><span>"registrant"</span><span>: </span><span>"一个可以发邮件的邮箱"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>"domain"</span><span>: </span><span>"域名名字"</span><span>,</span></div></div><div><div><div>4</div></div><div><span>  </span><span>"sld"</span><span>: </span><span>"域名后缀"</span><span>,</span></div></div><div><div><div>5</div></div><div><span>  </span><span>"nameservers"</span><span>: [</span></div></div><div><div><div>6</div></div><div><span>    </span><span>"NS服务器1"</span><span>,</span></div></div><div><div><div>7</div></div><div><span>    </span><span>"NS服务器2"</span><span>,</span></div></div><div><div><div>8</div></div><div><span>    </span><span>"NS服务器3"</span><span>,</span></div></div><div><div><div>9</div></div><div><span>    </span><span>"NS服务器4"</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>],</span></div></div><div><div><div>11</div></div><div><span>  </span><span>"agree_to_agreements"</span><span>: {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>"registration_and_use_agreement"</span><span>: </span><span>true</span><span>,</span></div></div><div><div><div>13</div></div><div><span>    </span><span>"acceptable_use_policy"</span><span>: </span><span>true</span><span>,</span></div></div><div><div><div>14</div></div><div><span>    </span><span>"privacy_policy"</span><span>: </span><span>true</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
<p>第三步，在你fork的仓库里把这个提交创建为一个新分支，名字为<code>域名全称-request-1</code>.</p>
<p>注意：第几次更改就填数字几</p>
<p>第四步，向官方仓库发起一个拉取请求，标题为<code>Registration/Update/Remove: 域名名字.域名后缀</code></p>
<p>内容这样填写：</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&gt; All items should be completed and confirmed to trigger the automatic merge workflow.</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>## Operation Type</span></div></div><div><div><div>4</div></div><div><span><span>-</span><span> </span></span><span>[</span><span>x</span><span>]</span><span> Registration, Register a new domain name.</span></div></div><div><div><div>5</div></div><div><span><span>-</span><span> [ ] Update, Update NS information or registrant email for an existing domain.</span></span></div></div><div><div><div>6</div></div><div><span><span>-</span><span> [ ] Remove, Cancel my domain name.</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>## Domain</span></div></div><div><div><div>9</div></div><div><span><span>-</span><span> </span></span><span>[</span><span>x</span><span>]</span><span> 域名名字.域名后缀</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>## Confirmation Items</span></div></div><div><div><div>12</div></div><div><span><span>-</span><span> </span></span><span>[</span><span>x</span><span>]</span><span> I confirm that I will deploy the website content for this domain name and put it into use within 30 days instead of hoarding and occupying resources.</span></div></div><div><div><div>13</div></div><div><span><span>-</span><span> </span></span><span>[</span><span>x</span><span>]</span><span> I confirm that the title of this Pull Request and the submitted files are in the standard format; otherwise, it will not be processed automatically.</span></div></div><div><div><div>14</div></div><div><span><span>-</span><span> </span></span><span>[</span><span>x</span><span>]</span><span> I confirmed that the name in the whois file meets the standards.</span></div></div><div><div><div>15</div></div><div><span><span>-</span><span> </span></span><span>[</span><span>x</span><span>]</span><span> I confirm that the domain name is at least 3 characters.</span></div></div><div><div><div>16</div></div><div><span><span>-</span><span> </span></span><span>[</span><span>x</span><span>]</span><span> I confirm that I have read and understood the </span><span>[</span><span>Acceptable Use Policy</span><span>]</span><span><span>(</span><span>https://github.com/PublicFreeSuffix/PublicFreeSuffix/blob/main/agreements/acceptable-use-policy.md</span><span>)</span></span><span>.</span></div></div><div><div><div>17</div></div><div><span><span>-</span><span> </span></span><span>[</span><span>x</span><span>]</span><span> I confirm that I have read and agree to the </span><span>[</span><span>Privacy Policy</span><span>]</span><span><span>(</span><span>https://github.com/PublicFreeSuffix/PublicFreeSuffix/blob/main/agreements/privacy-policy.md</span><span>)</span></span><span>.</span></div></div><div><div><div>18</div></div><div><span><span>-</span><span> </span></span><span>[</span><span>x</span><span>]</span><span> I confirm that I have read and agree to the </span><span>[</span><span>Registration And Use Agreement</span><span>]</span><span><span>(</span><span>https://github.com/PublicFreeSuffix/PublicFreeSuffix/blob/main/agreements/registration-and-use-agreement-sokg.md</span><span>)</span></span><span>.</span></div></div><div><div><div>19</div></div><div><span><span>-</span><span> </span></span><span>[</span><span>x</span><span>]</span><span> I confirm that the domain does not contain any reserved terms from the </span><span>[</span><span>Reserved Words List</span><span>]</span><span><span>(</span><span>https://github.com/PublicFreeSuffix/PublicFreeSuffix/blob/main/reserved_words.txt</span><span>)</span></span><span>.</span></div></div><div><div><div>20</div></div><div><span><span>-</span><span> </span></span><span>[</span><span>x</span><span>]</span><span> I confirm that I will complete the registrant's email verification according to </span><span>[</span><span>ARAE Instructions</span><span>]</span><span><span>(</span><span>https://github.com/PublicFreeSuffix/PublicFreeSuffix/blob/main/AUTHORIZATION.md</span><span>)</span></span><span> to complete the merger.</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
<p>如果是更新或删除，就把代码中Registration前面中括号里的x放在Update/Remove前面的中括号里.</p>
<p>第五步：当拉取请求状态为validation-passed后，用你刚才在json文件中填写的邮箱向 <code>pr-authorization@publicfreesuffix.org</code>发送一封邮件，标题为<code>APPROVE_PFS_PR_你的拉取请求ID</code>，内容随便写.</p>
<p>第六步，到你的DNS提供商里添加域名.</p>
<p>至此申请结束.</p>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/privacy-policy/</id>
      <title type="text">隐私政策</title>
      <published>1999-01-01T03:20:15.000Z</published>
      <updated>1999-01-01T03:20:15.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/privacy-policy/"/>
      <summary type="text">本隐私政策适用于 Clina 的笔记本（以下简称“我们”）。</summary>
      <content type="html"><![CDATA[<section><h1>All Cookies<a href="#all-cookies"><span>#</span></a></h1><section><h3>Strictly necessary cookies（必要的cookies）<a href="#strictly-necessary-cookies必要的cookies"><span>#</span></a></h3><ul>
<li><a href="https://cloud.umami.is" target="_blank">Umami</a>：收集站点的基本访问情况，并对外显示浏览量，由本站直接提供</li>
<li><a href="https://giscus.app" target="_blank">Giscus</a>：提供评论功能，由 Github 提供</li>
</ul></section><section><h3>Functionality cookies（功能性cookies）<a href="#functionality-cookies功能性cookies"><span>#</span></a></h3><ul>
<li>无</li>
</ul></section><section><h3>Tracking cookies（追蹤cookies）<a href="#tracking-cookies追蹤cookies"><span>#</span></a></h3><ul>
<li>无</li>
</ul><p><em>latest update: 2026/09/05</em></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/code-examples/</id>
      <title type="text">Firefly 代码块示例</title>
      <published>1970-01-03T00:00:00.000Z</published>
      <updated>1970-01-03T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/code-examples/"/>
      <summary type="text">在Firefly中使用表达性代码的代码块在 Markdown 中的外观。</summary>
      <content type="html"><![CDATA[<p>在这里，我们将探索如何使用 <a href="https://expressive-code.com/" target="_blank">Expressive Code</a> 展示代码块。提供的示例基于官方文档，您可以参考以获取更多详细信息。</p>
<section><h2>表达性代码<a href="#表达性代码"><span>#</span></a></h2><section><h3>语法高亮<a href="#语法高亮"><span>#</span></a></h3><p><a href="https://expressive-code.com/key-features/syntax-highlighting/" target="_blank">语法高亮</a></p><section><h4>常规语法高亮<a href="#常规语法高亮"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>'此代码有语法高亮!'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>渲染 ANSI 转义序列<a href="#渲染-ansi-转义序列"><span>#</span></a></h4><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>Standard ANSI colors:</span></div></div><div><div><div>2</div></div><div><span>- Dimmed:     </span><span> Black </span><span> Red </span><span> Green </span><span> Yellow </span><span> Blue </span><span> Magenta </span><span> Cyan </span><span> White </span></div></div><div><div><div>3</div></div><div><span>- Foreground: </span><span> Black </span><span> Red </span><span> Green </span><span> Yellow </span><span> Blue </span><span> Magenta </span><span> Cyan </span><span> White </span></div></div><div><div><div>4</div></div><div><span>- Background: </span><span> Black </span><span> Red </span><span> Green </span><span> Yellow </span><span> Blue </span><span> Magenta </span><span> Cyan </span><span> White </span></div></div><div><div><div>5</div></div><div><span>- Reversed:   </span><span> Black </span><span> Red </span><span> Green </span><span> Yellow </span><span> Blue </span><span> Magenta </span><span> Cyan </span><span> White </span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>8-bit colors (showing colors 160-171 as an example):</span></div></div><div><div><div>8</div></div><div><span>- Dimmed:     </span><span> 160 </span><span> 161 </span><span> 162 </span><span> 163 </span><span> 164 </span><span> 165 </span><span> 166 </span><span> 167 </span><span> 168 </span><span> 169 </span><span> 170 </span><span> 171 </span></div></div><div><div><div>9</div></div><div><span>- Foreground: </span><span> 160 </span><span> 161 </span><span> 162 </span><span> 163 </span><span> 164 </span><span> 165 </span><span> 166 </span><span> 167 </span><span> 168 </span><span> 169 </span><span> 170 </span><span> 171 </span></div></div><div><div><div>10</div></div><div><span>- Background: </span><span> 160 </span><span> 161 </span><span> 162 </span><span> 163 </span><span> 164 </span><span> 165 </span><span> 166 </span><span> 167 </span><span> 168 </span><span> 169 </span><span> 170 </span><span> 171 </span></div></div><div><div><div>11</div></div><div><span>- Reversed:   </span><span> 160 </span><span> 161 </span><span> 162 </span><span> 163 </span><span> 164 </span><span> 165 </span><span> 166 </span><span> 167 </span><span> 168 </span><span> 169 </span><span> 170 </span><span> 171 </span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>24-bit colors (full RGB):</span></div></div><div><div><div>14</div></div><div><span>- Dimmed:     </span><span> ForestGreen - RGB(34,139,34) </span><span> RebeccaPurple - RGB(102,51,153) </span></div></div><div><div><div>15</div></div><div><span>- Foreground: </span><span> ForestGreen - RGB(34,139,34) </span><span> RebeccaPurple - RGB(102,51,153) </span></div></div><div><div><div>16</div></div><div><span>- Background: </span><span> ForestGreen - RGB(34,139,34) </span><span> RebeccaPurple - RGB(102,51,153) </span></div></div><div><div><div>17</div></div><div><span>- Reversed:   </span><span> ForestGreen - RGB(34,139,34) </span><span> RebeccaPurple - RGB(102,51,153) </span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>Font styles:</span></div></div><div><div><div>20</div></div><div><span>- Default</span></div></div><div><div><div>21</div></div><div><span>- </span><span>Bold</span></div></div><div><div><div>22</div></div><div><span>- </span><span>Dimmed</span></div></div><div><div><div>23</div></div><div><span>- </span><span>Italic</span></div></div><div><div><div>24</div></div><div><span>- </span><span>Underline</span></div></div><div><div><div>25</div></div><div><span>- </span><span>Reversed</span></div></div><div><div><div>26</div></div><div><span>- </span><span>Strikethrough</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h3>编辑器和终端框架<a href="#编辑器和终端框架"><span>#</span></a></h3><p><a href="https://expressive-code.com/key-features/frames/" target="_blank">编辑器和终端框架</a></p><section><h4>代码编辑器框架<a href="#代码编辑器框架"><span>#</span></a></h4><div><figure><figcaption><span>my-test-file.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>'标题属性示例'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /><div><figure><figcaption><span>src/content/index.html</span></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>div</span><span>&gt;文件名注释示例&lt;/</span><span>div</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>终端框架<a href="#终端框架"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>echo</span><span> </span><span>"此终端框架没有标题"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /><div><figure><figcaption><span>PowerShell 终端示例</span></figcaption><pre><code><div><div><div>1</div></div><div><span>Write-Output</span><span> </span><span>"这个有标题!"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>覆盖框架类型<a href="#覆盖框架类型"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>echo</span><span> </span><span>"看，没有框架!"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /><div><figure><figcaption><span>PowerShell Profile.ps1</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 如果不覆盖，这将是一个终端框架</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>Watch-Tail</span><span> { </span><span>Get-Content</span><span><span> </span><span>-</span><span>Tail </span></span><span>20</span><span><span> </span><span>-</span><span>Wait $args }</span></span></div></div><div><div><div>3</div></div><div><span>New-Alias</span><span> tail </span><span>Watch-Tail</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>文本和行标记<a href="#文本和行标记"><span>#</span></a></h3><p><a href="https://expressive-code.com/key-features/text-markers/" target="_blank">文本和行标记</a></p><section><h4>标记整行和行范围<a href="#标记整行和行范围"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 第1行 - 通过行号定位</span></div></div><div><div><div>2</div></div><div><span>// 第2行</span></div></div><div><div><div>3</div></div><div><span>// 第3行</span></div></div><div><div><div>4</div></div><div><span>// 第4行 - 通过行号定位</span></div></div><div><div><div>5</div></div><div><span>// 第5行</span></div></div><div><div><div>6</div></div><div><span>// 第6行</span></div></div><div><div><div>7</div></div><div><span>// 第7行 - 通过范围 "7-8" 定位</span></div></div><div><div><div>8</div></div><div><span>// 第8行 - 通过范围 "7-8" 定位</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>选择行标记类型 (mark, ins, del)<a href="#选择行标记类型-mark-ins-del"><span>#</span></a></h4><div><figure><figcaption><span>line-markers.js</span></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>demo</span><span>() {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'此行标记为已删除'</span><span>)</span></div></div><div><div><div>3</div></div><div><span>  </span><span>// 此行和下一行标记为已插入</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'这是第二个插入行'</span><span>)</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>return</span><span> </span><span>'此行使用中性默认标记类型'</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>为行标记添加标签<a href="#为行标记添加标签"><span>#</span></a></h4><div><figure><figcaption><span>labeled-line-markers.jsx</span></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>button</span></div></div><div><div><div>2</div></div><div><span>  </span><span>role</span><span>=</span><span>"button"</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>{</span><span>...</span><span>props</span><span>}</span></div></div><div><div><div>4</div></div><div><span>  </span><span>value</span><span>=</span><span>{</span><span>value</span><span>}</span></div></div><div><div><div>5</div></div><div><span>  </span><span>className</span><span><span>=</span><span>{</span><span>buttonClassName</span><span>}</span></span></div></div><div><div><div>6</div></div><div><span>  </span><span>disabled</span><span>=</span><span>{</span><span>disabled</span><span>}</span></div></div><div><div><div>7</div></div><div><span>  </span><span>active</span><span>=</span><span>{</span><span>active</span><span>}</span></div></div><div><div><div>8</div></div><div><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>{</span><span>children</span><span> </span><span>&amp;&amp;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>!</span><span>active</span><span> </span><span>&amp;&amp;</span></div></div><div><div><div>11</div></div><div><span><span><span>    </span></span><span>(</span></span><span>typeof</span><span> </span><span>children</span><span> </span><span>===</span><span> </span><span>'string'</span><span> </span><span>?</span><span> &lt;</span><span>span</span><span>&gt;</span><span>{</span><span>children</span><span>}</span><span>&lt;/</span><span>span</span><span>&gt; </span><span>:</span><span> </span><span>children</span><span>)</span><span>}</span></div></div><div><div><div>12</div></div><div><span>&lt;/</span><span>button</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>在单独行上添加长标签<a href="#在单独行上添加长标签"><span>#</span></a></h4><div><div><div><figure><figcaption><span>labeled-line-markers.jsx</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>button</span></div></div><div><div><div>2</div></div><div><span>  </span><span>role</span><span>=</span><span>"button"</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>{</span><span>...</span><span>props</span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>  </span><span>value</span><span>=</span><span>{</span><span>value</span><span>}</span></div></div><div><div><div>6</div></div><div><span>  </span><span>className</span><span><span>=</span><span>{</span><span>buttonClassName</span><span>}</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>  </span><span>disabled</span><span>=</span><span>{</span><span>disabled</span><span>}</span></div></div><div><div><div>9</div></div><div><span>  </span><span>active</span><span>=</span><span>{</span><span>active</span><span>}</span></div></div><div><div><div>10</div></div><div><span>&gt;</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>{</span><span>children</span><span> </span><span>&amp;&amp;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>!</span><span>active</span><span> </span><span>&amp;&amp;</span></div></div><div><div><div>14</div></div><div><span><span><span>    </span></span><span>(</span></span><span>typeof</span><span> </span><span>children</span><span> </span><span>===</span><span> </span><span>'string'</span><span> </span><span>?</span><span> &lt;</span><span>span</span><span>&gt;</span><span>{</span><span>children</span><span>}</span><span>&lt;/</span><span>span</span><span>&gt; </span><span>:</span><span> </span><span>children</span><span>)</span><span>}</span></div></div><div><div><div>15</div></div><div><span>&lt;/</span><span>button</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>使用类似 diff 的语法<a href="#使用类似-diff-的语法"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>此行将标记为已插入</span></div></div><div><div><div>2</div></div><div><span>此行将标记为已删除</span></div></div><div><div><div>3</div></div><div><span>这是常规行</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>--- a/README.md</span></div></div><div><div><div>2</div></div><div><span>+++ b/README.md</span></div></div><div><div><div>3</div></div><div><span>@@ -1,3 +1,4 @@</span></div></div><div><div><div>4</div></div><div><span>+this is an actual diff file</span></div></div><div><div><div>5</div></div><div><span>-all contents will remain unmodified</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>no whitespace will be removed either</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>结合语法高亮和类似 diff 的语法<a href="#结合语法高亮和类似-diff-的语法"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>thisIsJavaScript</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// 整个块都会以 JavaScript 高亮显示，</span></div></div><div><div><div>3</div></div><div><span>  </span><span>// 并且我们仍然可以为其添加 diff 标记！</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'要删除的旧代码'</span><span>)</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'新的闪亮代码！'</span><span>)</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>标记行内的单独文本<a href="#标记行内的单独文本"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>demo</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// 标记行内的任何给定文本</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span> </span><span>'支持给定文本的多个匹配项'</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>正则表达式<a href="#正则表达式"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>'单词 </span><mark><span>yes</span></mark><span> 和 </span><mark><span>yep</span></mark><span> 将被标记。'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>转义正斜杠<a href="#转义正斜杠"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>echo</span><span> </span><span>"Test"</span><span> &gt; </span><mark><span>/home/</span></mark><span>test.txt</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>选择内联标记类型 (mark, ins, del)<a href="#选择内联标记类型-mark-ins-del"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>demo</span><span>() {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'这些是插入和删除的标记类型'</span><span>);</span></div></div><div><div><div>3</div></div><div><span>  </span><span>// return 语句使用默认标记类型</span></div></div><div><div><div>4</div></div><div><span>  </span><mark><span>return</span><span> </span><span>true</span><span>;</span></mark></div></div><div><div><div>5</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>自动换行<a href="#自动换行"><span>#</span></a></h3><p><a href="https://expressive-code.com/key-features/word-wrap/" target="_blank">自动换行</a></p><section><h4>为每个块配置自动换行<a href="#为每个块配置自动换行"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 启用换行的示例</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>getLongString</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span> </span><span>'这是一个非常长的字符串，除非容器极宽，否则很可能无法适应可用空间'</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// wrap=false 的示例</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>getLongString</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span> </span><span>'这是一个非常长的字符串，除非容器极宽，否则很可能无法适应可用空间'</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>配置换行的缩进<a href="#配置换行的缩进"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// preserveIndent 示例（默认启用）</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>getLongString</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span> </span><span>'这是一个非常长的字符串，除非容器极宽，否则很可能无法适应可用空间'</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// preserveIndent=false 的示例</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>getLongString</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span> </span><span>'这是一个非常长的字符串，除非容器极宽，否则很可能无法适应可用空间'</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section></section>
<section><h2>可折叠部分<a href="#可折叠部分"><span>#</span></a></h2><p><a href="https://expressive-code.com/plugins/collapsible-sections/" target="_blank">可折叠部分</a></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div></div></div><div><span></span><span></span><span>5 collapsed lines</span></div></div><div><div><div>1</div></div><div><span>// 所有这些样板设置代码将被折叠</span></div></div><div><div><div>2</div></div><div><span>import</span><span> { </span><span>someBoilerplateEngine</span><span> } </span><span>from</span><span> </span><span>'@example/some-boilerplate'</span></div></div><div><div><div>3</div></div><div><span>import</span><span> { </span><span>evenMoreBoilerplate</span><span> } </span><span>from</span><span> </span><span>'@example/even-more-boilerplate'</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>engine</span><span> </span><span>=</span><span> </span><span>someBoilerplateEngine</span><span>(</span><span>evenMoreBoilerplate</span><span>())</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 这部分代码默认可见</span></div></div><div><div><div>8</div></div><div><span><span>engine</span><span>.</span></span><span>doSomething</span><span>(</span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span><span>, </span><span>calcFn</span><span>)</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>function</span><span> </span><span>calcFn</span><span>() {</span></div></div><div><div><div>11</div></div><div><span>  </span><span>// 您可以有多个折叠部分</span></div></div><div><div><div></div></div><div><span></span><span></span><span>3 collapsed lines</span></div></div><div><div><div>12</div></div><div><span>  </span><span>const</span><span> </span><span>a</span><span> </span><span>=</span><span> </span><span>1</span></div></div><div><div><div>13</div></div><div><span>  </span><span>const</span><span> </span><span>b</span><span> </span><span>=</span><span> </span><span>2</span></div></div><div><div><div>14</div></div><div><span>  </span><span>const</span><span> </span><span>c</span><span> </span><span>=</span><span><span> </span><span>a</span><span> </span></span><span>+</span><span><span> </span><span>b</span></span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>  </span><span>// 这将保持可见</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`计算结果: </span><span>${</span><span>a</span><span>}</span><span> + </span><span>${</span><span>b</span><span>}</span><span> = </span><span>${</span><span>c</span><span>}</span><span>`</span><span>)</span></div></div><div><div><div>18</div></div><div><span>  </span><span>return</span><span><span> </span><span>c</span></span></div></div><div><div><div>19</div></div><div><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div></div></div><div><span></span><span></span><span>4 collapsed lines</span></div></div><div><div><div>21</div></div><div><span>// 直到块末尾的所有代码将再次被折叠</span></div></div><div><div><div>22</div></div><div><span><span>engine</span><span>.</span></span><span>closeConnection</span><span>()</span></div></div><div><div><div>23</div></div><div><span><span>engine</span><span>.</span></span><span>freeMemory</span><span>()</span></div></div><div><div><div>24</div></div><div><span><span>engine</span><span>.</span></span><span>shutdown</span><span>({ </span><span>reason</span><span>:</span><span> </span><span>'示例样板代码结束'</span><span> })</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>行号<a href="#行号"><span>#</span></a></h2><p><a href="https://expressive-code.com/plugins/line-numbers/" target="_blank">行号</a></p><section><h3>为每个块显示行号<a href="#为每个块显示行号"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 此代码块将显示行号</span></div></div><div><div><div>2</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>'来自第2行的问候!'</span><span>)</span></div></div><div><div><div>3</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>'我在第3行'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /><div><figure><figcaption></figcaption><pre><code><div><div><span>// 此块禁用行号</span></div></div><div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>'你好?'</span><span>)</span></div></div><div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>'抱歉，你知道我在第几行吗?'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>更改起始行号<a href="#更改起始行号"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>5</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>'来自第5行的问候!'</span><span>)</span></div></div><div><div><div>6</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>'我在第6行'</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>Tab 代码块<a href="#tab-代码块"><span>#</span></a></h2><p>由 <a href="https://github.com/ITZSHOAIB/rehype-code-group" target="_blank">rehype-code-group</a> 提供，语法与 <a href="https://vitepress.dev/guide/markdown#code-groups" target="_blank">VitePress 代码组</a> 一致：用 <code>::: code-group labels=[...]</code> 包裹多个代码块，即可合并成一组标签页。</p><div><div><div></div><div>Note</div></div><div><p><code>labels=[...]</code> 中的标签按顺序对应组内的代码块，用英文逗号分隔；<code>:::</code> 与 <code>code-group</code> 之间的空格不能省略。</p></div></div><section><h3>基本用法<a href="#基本用法"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>::: code-group labels=[code.js, code.py, code.html]</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>```js</span></div></div><div><div><div>4</div></div><div><span>export</span><span> </span><span>function</span><span> </span><span>greet</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>  </span><span>return</span><span> </span><span>`Hello, </span><span>${</span><span>name</span><span>}</span><span>!`</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div><span>```</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>```py</span></div></div><div><div><div>10</div></div><div><span>def</span><span> </span><span>greet</span><span><span>(</span><span>name</span><span>):</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>return</span><span> </span><span>f</span><span>"Hello, </span><span>{</span><span>name</span><span>}</span><span>!"</span></div></div><div><div><div>12</div></div><div><span>```</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>```html</span></div></div><div><div><div>15</div></div><div><span>&lt;</span><span>p</span><span>&gt;Hello, world!&lt;/</span><span>p</span><span>&gt;</span></div></div><div><div><div>16</div></div><div><span>```</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>:::</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>渲染效果：</p><div><div>code.jscode.pycode.html</div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>function</span><span> </span><span>greet</span><span><span>(</span><span>name</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> </span><span>`Hello, </span><span>${</span><span>name</span><span>}</span><span>!`</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>def</span><span> </span><span>greet</span><span>(</span><span>name</span><span>):</span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span> </span><span>f</span><span>"Hello, </span><span>{</span><span>name</span><span>}</span><span>!"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>p</span><span>&gt;Hello, world!&lt;/</span><span>p</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></div></div></section><section><h3>标签中使用 Emoji<a href="#标签中使用-emoji"><span>#</span></a></h3><p>标签支持 <a href="https://github.com/omnidan/node-emoji#readme" target="_blank">emoji 短代码</a>，构建时会自动转换成 emoji：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>::: code-group labels=[:package: npm, :package: pnpm, :yarn: yarn]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><div>📦 npm📦 pnpm🧶 yarn</div><div><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>create</span><span> </span><span>astro@latest</span></div></div></code></pre><div><div></div><div></div></div></figure></div></div><div><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>pnpm</span><span> </span><span>create</span><span> </span><span>astro@latest</span></div></div></code></pre><div><div></div><div></div></div></figure></div></div><div><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>yarn</span><span> </span><span>create</span><span> </span><span>astro</span></div></div></code></pre><div><div></div><div></div></div></figure></div></div></div></section><section><h3>与其他代码块特性组合<a href="#与其他代码块特性组合"><span>#</span></a></h3><p>组内仍是普通的 Expressive Code 代码块，标题、行号、行标记、折叠、终端框架等特性都可以照常使用。</p><div><div>配置文件终端折叠</div><div><div><figure><figcaption><span>astro.config.mjs</span></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>theme</span><span>:</span><span> </span><span>"firefly"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>codeGroup</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>4</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div></div><div><div><figure><figcaption><span>部署</span></figcaption><pre><code><div><div><div>1</div></div><div><span>pnpm</span><span> </span><span>build</span><span> &amp;&amp; </span><span>pnpm</span><span> </span><span>preview</span></div></div></code></pre><div><div></div><div></div></div></figure></div></div><div><div><figure><figcaption></figcaption><pre><code><div><div><div></div></div><div><span></span><span></span><span>3 collapsed lines</span></div></div><div><div><div>1</div></div><div><span>// 这三行默认折叠</span></div></div><div><div><div>2</div></div><div><span>import</span><span> { </span><span>a</span><span> } </span><span>from</span><span> </span><span>"a"</span><span>;</span></div></div><div><div><div>3</div></div><div><span>import</span><span> { </span><span>b</span><span> } </span><span>from</span><span> </span><span>"b"</span><span>;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>a</span><span>, </span><span>b</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></div></div></section><section><h3>不止是代码块<a href="#不止是代码块"><span>#</span></a></h3><p>标签页内可以放任意内容，例如文字、列表或图片：</p><div><div>说明列表</div><div><p>这是一段普通的段落内容。</p></div><div><ul>
<li>列表项一</li>
<li>列表项二</li>
</ul></div></div><div><div><div></div><div>Tip</div></div><div><p>标签栏在构建期生成，默认展开第一项；支持鼠标点击与键盘 <kbd>←</kbd> / <kbd>→</kbd> / <kbd>Home</kbd> / <kbd>End</kbd> 切换。</p></div></div></section><section><h3>在提醒框内使用（MDX 组件）<a href="#在提醒框内使用mdx-组件"><span>#</span></a></h3><p><code>::: code-group</code> 和 <code>::: tip</code> 等 Docusaurus 风格提醒框都使用 <code>:::</code> 容器语法，而 micromark-directive 规定父子容器的冒号数必须不同，因此 <code>::: code-group</code> <strong>无法嵌套在 <code>::: tip</code> 等提醒框内</strong>（整块会失效）。</p><p>这种情况改用 MDX 组件 <a href="@/components/common/TabGroup.svelte"><code>TabGroup</code></a>：MDX 组件不经过 <code>:::</code> 指令解析，在提醒框内外都能正常使用，也方便日后扩展更多 UI 组件。把文章后缀改为 <code>.mdx</code>，从统一入口 <a href="@/components/firefly-mdx.ts"><code>@/components/firefly-mdx</code></a> 引入（可一次引入多个组件）：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>TabGroup</span><span> } </span><span>from</span><span> </span><span>"@/components/firefly-mdx"</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>:::tip</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>&lt;</span><span>TabGroup</span><span> </span><span>labels</span><span>=</span><span>{</span><span>[</span><span>"test.js"</span><span>, </span><span>"test.py"</span><span>]</span><span>}</span><span> </span><span>client</span><span>:</span><span>load</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span>  </span><span>```</span><span>js</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>1</span><span>)</span></div></div><div><div><div>8</div></div><div><span>  </span><span>```</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>  </span><span>```</span><span>py</span></div></div><div><div><div>11</div></div><div><span>  </span><span>print</span><span>(</span><span>"hello world"</span><span>)</span></div></div><div><div><div>12</div></div><div><span>  </span><span>```</span></div></div><div><div><div>13</div></div><div><span>&lt;/</span><span>TabGroup</span><span>&gt;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>:::</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><div><div><div></div><div>Tip</div></div><div><ul>
<li><code>labels</code> 数组给每个代码块声明标签，顺序一一对应</li>
<li>组件用 <code>client:load</code> 激活标签切换</li>
</ul></div></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/guide/firefly-layout-system/</id>
      <title type="text">Firefly 布局系统详解</title>
      <published>1970-01-03T00:00:00.000Z</published>
      <updated>1970-01-03T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/guide/firefly-layout-system/"/>
      <summary type="text">深入了解 Firefly 的布局系统，包括侧边栏布局（左侧/双侧）和文章列表布局（列表/网格），以及自适应网格列数。</summary>
      <content type="html"><![CDATA[<section><h2>📖 概述<a href="#-概述"><span>#</span></a></h2><p>Firefly 提供了灵活的布局系统，允许您根据内容需求和个人喜好自定义博客的视觉呈现方式。布局系统主要包括<strong>侧边栏布局</strong>和<strong>文章列表布局</strong>两个维度，它们相互配合，共同决定了页面的整体结构。</p><p>本文将详细介绍 Firefly 的各种布局模式、它们的特点、使用场景，以及不同布局组合的效果。</p><hr /><div><figure><img alt="左侧边栏+列表布局" loading="lazy" width="2192" height="1233" src="/_astro/left-list.CdJfuugk_23fiFf.webp" /><figcaption>左侧边栏+列表布局</figcaption></figure><figure><img alt="右侧边栏+网格布局" loading="lazy" width="2192" height="1233" src="/_astro/right-grid2.BDLeNFG9_Z1Ypd95.webp" /><figcaption>右侧边栏+网格布局</figcaption></figure><figure><img alt="左侧边栏+三列网格布局" loading="lazy" width="2192" height="1233" src="/_astro/left-grid3.DCIbm9j3_Z5Vd5j.webp" /><figcaption>左侧边栏+三列网格布局</figcaption></figure></div><div><figure><img alt="双侧边栏+列表布局" loading="lazy" width="2192" height="1233" src="/_astro/both-list.CybtDZTU_xxVkb.webp" /><figcaption>双侧边栏+列表布局</figcaption></figure><figure><img alt="双侧边栏+网格布局" loading="lazy" width="2192" height="1233" src="/_astro/both-grid.B-BFe9Al_1g1tLj.webp" /><figcaption>双侧边栏+网格布局</figcaption></figure><figure><img alt="双侧边栏+网格瀑布流布局" loading="lazy" width="2192" height="1233" src="/_astro/masonry.BgzRsBcp_2mfpqn.webp" /><figcaption>双侧边栏+网格瀑布流布局</figcaption></figure></div></section>
<section><h2>一、侧边栏布局系统<a href="#一侧边栏布局系统"><span>#</span></a></h2><p>侧边栏是博客页面的重要组成部分，用于展示导航、分类、标签、统计信息等辅助内容。Firefly 支持两种侧边栏布局模式。</p><section><h3>1.1 单侧边栏模式<a href="#11-单侧边栏模式"><span>#</span></a></h3><section><h4>左侧边栏 (position: “left”)<a href="#左侧边栏-position-left"><span>#</span></a></h4><p></p><figure><img alt="左侧边栏布局" loading="lazy" width="2192" height="1233" src="/_astro/left-list.CdJfuugk_23fiFf.webp" /><figcaption>左侧边栏布局</figcaption></figure><p></p></section><section><h4>右侧边栏 (position: “right”)<a href="#右侧边栏-position-right"><span>#</span></a></h4><p></p><figure><img alt="右侧边栏布局" loading="lazy" width="2192" height="1233" src="/_astro/right-grid2.BDLeNFG9_Z1Ypd95.webp" /><figcaption>右侧边栏布局</figcaption></figure><p></p></section><section><h4>特点<a href="#特点"><span>#</span></a></h4><ul>
<li>侧边栏固定在页面其中一侧</li>
<li>文章阅读区域体验更佳，更宽敞</li>
<li>更加简约，没有那么紧凑</li>
</ul></section><section><h4>适用场景<a href="#适用场景"><span>#</span></a></h4><ul>
<li>传统博客风格</li>
<li>强调导航和分类的博客</li>
<li>需要突出用户资料的个人博客</li>
<li>内容为主，辅助信息次之的场景</li>
</ul><div><div><div></div><div>Tip</div></div><div><p>文章详情页显示哪几侧，由**各组件的 <code>showOnPostPage</code>（以及 <code>hideOnNonPostPage</code>）**决定：某一侧在当前页型下没有任何可见组件时，该列会自动收起，把空间让给内容栏。</p><p>如果想在文章页同时显示双侧边栏，把 <code>position</code> 设为 <code>"both"</code>，并给两侧组件配好 <code>showOnPostPage</code> 即可。</p></div></div></section><section><h4>配置示例<a href="#配置示例"><span>#</span></a></h4><div><figure><figcaption><span>src/config/sidebarConfig.ts</span></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>sidebarLayoutConfig</span><span>:</span><span> </span><span>SidebarLayoutConfig</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>enable</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>position</span><span>:</span><span> </span><span>"left"</span><span>, </span><span>// 左侧边栏</span></div></div><div><div><div>4</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h3>1.2 双侧边栏模式 (position: “both”)<a href="#12-双侧边栏模式-position-both"><span>#</span></a></h3><section><h4>特点<a href="#特点-1"><span>#</span></a></h4><ul>
<li>左右两侧同时存在侧边栏</li>
<li>主内容区域位于中间</li>
<li>最大化利用屏幕空间</li>
<li>可以展示更多辅助信息</li>
<li>适合宽屏显示器</li>
</ul></section><section><h4>布局结构<a href="#布局结构"><span>#</span></a></h4><p></p><figure><img alt="双侧边栏+列表布局" loading="lazy" width="2192" height="1233" src="/_astro/both-list.CybtDZTU_xxVkb.webp" /><figcaption>双侧边栏+列表布局</figcaption></figure><p></p><p></p><figure><img alt="双侧边栏+网格布局" loading="lazy" width="2192" height="1233" src="/_astro/both-grid.B-BFe9Al_1g1tLj.webp" /><figcaption>双侧边栏+网格布局</figcaption></figure><p></p></section><section><h4>适用场景<a href="#适用场景-1"><span>#</span></a></h4><ul>
<li>宽屏桌面端浏览</li>
<li>信息密集型博客</li>
<li>需要展示大量辅助内容</li>
<li>专业性强的技术博客</li>
</ul></section><section><h4>配置示例<a href="#配置示例-1"><span>#</span></a></h4><div><figure><figcaption><span>src/config/sidebarConfig.ts</span></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>sidebarLayoutConfig</span><span>:</span><span> </span><span>SidebarLayoutConfig</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>enable</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>position</span><span>:</span><span> </span><span>"both"</span><span>, </span><span>// 双侧边栏</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section></section>
<section><h2>二、文章列表布局系统<a href="#二文章列表布局系统"><span>#</span></a></h2><p>文章列表是博客首页和归档页的核心内容，Firefly 提供两种展示方式，并支持多种网格配置。</p><section><h3>2.1 列表模式 (defaultMode: “list”)<a href="#21-列表模式-defaultmode-list"><span>#</span></a></h3><section><h4>特点<a href="#特点-2"><span>#</span></a></h4><ul>
<li>单列纵向排列</li>
<li>显示文章封面图，可配置在左侧或右侧</li>
<li>展示更多文章摘要</li>
<li>适合深度阅读</li>
</ul></section><section><h4>列表布局结构<a href="#列表布局结构"><span>#</span></a></h4><p></p><figure><img alt="列表模式布局" loading="lazy" width="2192" height="1233" src="/_astro/left-list.CdJfuugk_23fiFf.webp" /><figcaption>列表模式布局</figcaption></figure><p></p></section><section><h4>封面位置<a href="#封面位置"><span>#</span></a></h4><p>列表模式的封面图默认在卡片右侧，可以通过 <code>coverPosition</code> 改到左侧。改到左侧后，标题前的主题色竖线会自动隐藏（它是贴着卡片左边缘的设计），腾出的间距留给正文。</p><p>网格模式的封面固定在卡片顶部，不受这项配置影响。</p></section><section><h4>优点<a href="#优点"><span>#</span></a></h4><ul>
<li>✅ 视觉冲击力强，封面图吸引眼球</li>
<li>✅ 可以展示更多文章信息（摘要、标签等）</li>
<li>✅ 适合图片内容丰富的博客</li>
<li>✅ 移动端友好，单列更易阅读</li>
<li>✅ 兼容所有侧边栏配置（单侧、双侧）</li>
</ul></section><section><h4>配置示例<a href="#配置示例-2"><span>#</span></a></h4><div><figure><figcaption><span>src/config/siteConfig.ts</span></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>siteConfig</span><span>:</span><span> </span><span>SiteConfig</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>postListLayout</span><span>:</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>defaultMode</span><span>:</span><span> </span><span>"list"</span><span>,    </span><span>// 列表模式</span></div></div><div><div><div>4</div></div><div><span>    </span><span>coverPosition</span><span>:</span><span> </span><span>"right"</span><span>, </span><span>// 封面图位置："right" 右侧，"left" 左侧</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>6</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h3>2.2 网格模式 (defaultMode: “grid”)<a href="#22-网格模式-defaultmode-grid"><span>#</span></a></h3><section><h4>特点<a href="#特点-3"><span>#</span></a></h4><ul>
<li>自适应列数，根据浏览器宽度自动调整</li>
<li>紧凑布局，信息密度高</li>
<li>适合快速浏览</li>
</ul></section><section><h4>自适应网格<a href="#自适应网格"><span>#</span></a></h4><p>网格模式通过 <code>columnWidth</code> 配置卡片的最小宽度（单位 px），浏览器会根据容器可用宽度自动计算能容纳多少列。</p><p></p><figure><img alt="网格布局" loading="lazy" width="2192" height="1233" src="/_astro/left-grid3.DCIbm9j3_Z5Vd5j.webp" /><figcaption>网格布局</figcaption></figure><p></p></section><section><h4>配置示例<a href="#配置示例-3"><span>#</span></a></h4><div><figure><figcaption><span>src/config/siteConfig.ts</span></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>siteConfig</span><span>:</span><span> </span><span>SiteConfig</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>postListLayout</span><span>:</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>defaultMode</span><span>:</span><span> </span><span>"grid"</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>grid</span><span>:</span><span> {</span></div></div><div><div><div>5</div></div><div><span>      </span><span>masonry</span><span>:</span><span> </span><span>true</span><span>,      </span><span>// 开启瀑布流</span></div></div><div><div><div>6</div></div><div><span>      </span><span>columnWidth</span><span>:</span><span> </span><span>320</span><span>,   </span><span>// 卡片最小宽度(px)，浏览器自动计算列数</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>9</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h3>2.3 瀑布流布局 (Masonry)<a href="#23-瀑布流布局-masonry"><span>#</span></a></h3><p>Firefly 的网格模式内置了智能瀑布流布局支持，解决了网格布局中因图文混合文章导致的卡片高度不一致导致的空白问题。</p><p></p><figure><img alt="瀑布流布局" loading="lazy" width="2192" height="1233" src="/_astro/masonry.BgzRsBcp_2mfpqn.webp" /><figcaption>瀑布流布局</figcaption></figure><p></p><ul>
<li><strong>智能排版</strong>：自动将卡片放置到最短的列，最大化利用垂直空间。</li>
<li><strong>消除空白</strong>：通过绝对定位精确计算每个卡片的位置，让卡片紧贴上方卡片，消除垂直方向的空白间隙。</li>
<li><strong>自适应列数</strong>：瀑布流同样根据 <code>columnWidth</code> 和容器宽度动态计算列数，无需固定配置。</li>
<li><strong>配置灵活</strong>：您可以在 <code>siteConfig.ts</code> 中通过 <code>postListLayout.grid.masonry</code> 选项自由开启或关闭此功能。</li>
</ul><hr /></section></section>
<section><h2>三、布局组合指南<a href="#三布局组合指南"><span>#</span></a></h2><p>Firefly 允许您自由组合侧边栏和文章列表布局。以下是各种组合的效果说明。</p>

<table><thead><tr><th>侧边栏模式</th><th>文章列表模式</th><th>推荐度</th><th>适用场景</th></tr></thead><tbody><tr><td>单侧边栏</td><td>列表模式</td><td>⭐⭐⭐⭐⭐</td><td>摄影、设计、生活类博客，强调图片和沉浸感</td></tr><tr><td>单侧边栏</td><td>网格模式</td><td>⭐⭐⭐⭐⭐</td><td>技术、笔记类博客，平衡阅读与检索效率</td></tr><tr><td>双侧边栏</td><td>列表模式</td><td>⭐⭐⭐⭐⭐</td><td>需要展示大量侧边栏信息的站点</td></tr><tr><td>双侧边栏</td><td>网格模式</td><td>⭐⭐⭐⭐⭐</td><td>极客风格，追求最高信息密度</td></tr></tbody></table><hr /></section>
<section><h2>四、响应式布局行为<a href="#四响应式布局行为"><span>#</span></a></h2><p>Firefly 的布局系统具有智能的响应式设计，会根据屏幕尺寸自动调整。</p><p>为了保证最佳阅读体验，系统会在屏幕变窄时自动调整布局：</p><ol>
<li><strong>网格列数自动减少</strong>：网格模式的列数由 <code>columnWidth</code> 和容器宽度自动决定，屏幕越窄列数越少。</li>
<li><strong>列表模式 -&gt; 网格模式</strong>：当屏幕宽度小于 380px（超小屏设备）时，列表模式会自动切换为网格模式，以保证卡片内容的可读性。</li>
<li><strong>双侧边栏 -&gt; 单侧边栏</strong>：当屏幕宽度小于 1280px 时，会根据<code>tabletSidebar</code>配置显示单侧边栏，隐藏其中一个侧边栏，文章目录导航会切换成浮动目录导航。</li>
</ol><hr /></section>
<section><h2>五、常见问题<a href="#五常见问题"><span>#</span></a></h2><section><h3>Q1: 如何调整网格列数？<a href="#q1-如何调整网格列数"><span>#</span></a></h3><p><strong>A</strong>: 通过 <code>columnWidth</code> 配置卡片最小宽度即可。值越小，同等宽度下列数越多；值越大，列数越少。浏览器会自动根据可用宽度计算最佳列数。</p><hr /></section></section>
<section><h2>六、总结<a href="#六总结"><span>#</span></a></h2><p>Firefly 的布局系统给予了您更大的自由度，您都可以通过简单的配置实现。</p><p>我们建议您根据自己的内容类型和目标读者的设备偏好，尝试不同的组合，找到最适合您的博客形态。</p><hr /></section>
<section><h2>相关链接<a href="#相关链接"><span>#</span></a></h2><ul>
<li>📚 <a href="https://docs-firefly.cuteleaf.cn/config/sidebarConfig-usage/" target="_blank">侧边栏配置文档</a></li>
<li>📚 <a href="https://docs-firefly.cuteleaf.cn/config/siteConfig-usage/" target="_blank">站点配置文档</a></li>
<li>🏠 <a href="https://docs-firefly.cuteleaf.cn/" target="_blank">Firefly 官方文档</a></li>
<li>⭐ <a href="https://github.com/CuteLeaf/Firefly" target="_blank">Firefly GitHub</a></li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/guide/firefly-wiki-link/</id>
      <title type="text">Firefly Wiki Link 内部链接示例</title>
      <published>1970-01-03T00:00:00.000Z</published>
      <updated>1970-01-03T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/guide/firefly-wiki-link/"/>
      <summary type="text">在 Firefly 文章中使用 Obsidian 风格的 Wiki Link 内部链接，并自动生成文章链接卡片。</summary>
      <content type="html"><![CDATA[<p>Firefly 支持在 Markdown、MDX 文章中使用 Obsidian 风格的 Wiki Link 内部链接。链接目标填写文章的 slug 或文件路径，都不需要包含扩展名，具体匹配规则见下文「链接目标的三种写法」。</p>
<section><h2>文章链接卡片<a href="#文章链接卡片"><span>#</span></a></h2><p><code>[[slug]]</code> 单独成段时，会自动读取目标文章的标题、描述、发布时间、分类、标签和封面，渲染为链接卡片：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[</span><span>[</span><span>firefly</span><span>]</span><span>]</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>[</span><span>[</span><span>guide/index</span><span>]</span><span>]</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>[</span><span>[</span><span>markdown-extended</span><span>]</span><span>]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><a href="/posts/firefly/"><div><div>Firefly 一款清新美观的 Astro 博客主题模板</div><div>Firefly 是一款基于 Astro 框架和 Fuwari 模板开发的清新美观且现代化个人博客主题模板，专为技术爱好者和内容创作者设计。该主题融合了现代 Web 技术栈，提供了丰富的功能模块和高度可定制的界面，让您能够轻松打造出专业且美观的个人博客网站。</div><div><span>1970-01-02</span><span>文章示例</span><span><span>#Markdown</span><span>#Firefly</span><span>#博客</span><span>#主题</span><span>#模板</span></span></div></div><div><img alt="" loading="lazy" width="480" height="259" src="/_astro/firefly2.dxmXG905_Z2o5m8L.webp" /></div></a><a href="/posts/guide/"><div><div>Firefly 简单使用指南</div><div>如何使用 Firefly 博客模板。</div><div><span>1970-01-02</span><span>博客指南</span><span><span>#Firefly</span><span>#博客</span><span>#Markdown</span><span>#指南</span></span></div></div><div><img alt="" loading="lazy" width="480" height="270" src="/_astro/cover.bDPQizdp_C8GFs.webp" /></div></a><a href="/posts/markdown-extended/"><div><div>Markdown 扩展功能</div><div>了解 Firefly 中的 Markdown 功能</div><div><span>1970-01-01</span><span>文章示例</span><span><span>#演示</span><span>#示例</span><span>#Markdown</span><span>#Firefly</span></span></div></div></a></section>
<section><h2>行内链接<a href="#行内链接"><span>#</span></a></h2><p><code>[[slug]]</code> 出现在正文中间时，渲染为普通链接，链接文字自动使用目标文章的标题</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>请参阅 [</span><span>[</span><span>firefly</span><span>]</span><span>] 了解主题特性。</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>请参阅 <a href="/posts/firefly/">Firefly 一款清新美观的 Astro 博客主题模板</a> 了解主题特性。</p></section>
<section><h2>自定义显示标题<a href="#自定义显示标题"><span>#</span></a></h2><p>在 <code>|</code> 后填写链接的显示文字。行内链接会用它替换文章标题；单独成段时依然渲染为卡片，卡片标题使用自定义文字，描述、时间、分类、标签和封面仍然读取目标文章：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>请参阅 [</span><span>[</span><span>firefly|主题介绍</span><span>]</span><span>] 了解主题特性。</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>[[firefly|Firefly 主题介绍]]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>请参阅 <a href="/posts/firefly/">主题介绍</a> 了解主题特性。</p><a href="/posts/firefly/"><div><div>Firefly 主题介绍</div><div>Firefly 是一款基于 Astro 框架和 Fuwari 模板开发的清新美观且现代化个人博客主题模板，专为技术爱好者和内容创作者设计。该主题融合了现代 Web 技术栈，提供了丰富的功能模块和高度可定制的界面，让您能够轻松打造出专业且美观的个人博客网站。</div><div><span>1970-01-02</span><span>文章示例</span><span><span>#Markdown</span><span>#Firefly</span><span>#博客</span><span>#主题</span><span>#模板</span></span></div></div><div><img alt="" loading="lazy" width="480" height="259" src="/_astro/firefly2.dxmXG905_Z2o5m8L.webp" /></div></a><p>一个例外：如果 <code>|</code> 后的文字只是把链接目标又抄了一遍（<code>[[guide/index|index]]</code>），会被当作无效别名忽略，仍然显示文章标题。Obsidian 在插入的链接时会自动补上这样的别名，避免笔记里显示一长串路径，这个例外就是为它准备的。</p></section>
<section><h2>链接目标的三种写法<a href="#链接目标的三种写法"><span>#</span></a></h2><p>用 Obsidian 管理文章时，把 <code>src/content/posts</code> 目录本身作为 Obsidian 仓库（vault）打开。下文提到的「仓库根目录」都指这个目录，它正好也是 Firefly 解析链接路径的起点。</p><p>链接目标按以下顺序匹配：</p>

<table><thead><tr><th>写法</th><th>示例</th><th>在 Obsidian 中</th></tr></thead><tbody><tr><td>frontmatter 的 <code>slug</code></td><td><code>[[firefly-wiki-link]]</code></td><td>✗ 不支持</td></tr><tr><td>文件路径（相对仓库根目录）</td><td><code>[[guide/firefly-layout-system]]</code></td><td>✓ 需改设置（推荐）</td></tr><tr><td>裸文件名（仓库内唯一时）</td><td><code>[[firefly-layout-system]]</code></td><td>✓ 默认即是</td></tr></tbody></table><section><h3>第一种：slug<a href="#第一种slug"><span>#</span></a></h3><p><code>slug</code> 写法在 Obsidian 中不支持，<code>slug</code> 是 Firefly 自己的概念，Obsidian 不读取 frontmatter 里的 <code>slug</code>，所以按 slug 写的链接在 Obsidian 里既不会自动补全，也点不动，只有构建出的站点上能正常跳转。如果你主要在 Obsidian 里写作，用下面两种写法。</p></section><section><h3>第二种：文件路径（推荐）<a href="#第二种文件路径推荐"><span>#</span></a></h3><p>文件路径写法需要改 Obsidian 设置，在 <code>设置 → 文件与链接 → 链接 → 内部链接类型</code> 中选择<strong>基于仓库根目录的绝对路径</strong>，Obsidian 插入的链接才会带上目录：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[</span><span>[</span><span>guide/firefly-layout-system|firefly-layout-system</span><span>]</span><span>]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><a href="/posts/guide/firefly-layout-system/"><div><div>Firefly 布局系统详解</div><div>深入了解 Firefly 的布局系统，包括侧边栏布局（左侧/双侧）和文章列表布局（列表/网格），以及自适应网格列数。</div><div><span>1970-01-03</span><span>博客指南</span><span><span>#Firefly</span><span>#布局</span><span>#博客</span><span>#指南</span></span></div></div><div><img alt="" loading="lazy" width="480" height="270" src="/_astro/firefly1.CZCuCN4V_1uGCKh.webp" /></div></a><p>因为仓库根目录就是 <code>src/content/posts</code>，Obsidian 写出的这个路径和 Firefly 需要的路径完全一致，不需要任何额外转换。</p><p>上面这行末尾的 <code>|firefly-layout-system</code> 是 Obsidian 自动补的别名，Firefly 会忽略它，卡片标题仍然取文章的 title，除非你把它修改和文件名不一致，才会变成你修改后的别名。</p><p>内部链接类型下拉框里的<strong>基于当前笔记的相对路径</strong>只在同目录内可用：它给同目录文章生成的是裸文件名，能正常匹配；但跨目录时会生成 <code>../</code> 前缀，Firefly 无法解析，链接会按原文显示。</p></section><section><h3>第三种：裸文件名<a href="#第三种裸文件名"><span>#</span></a></h3><p>「内部链接类型」的默认值就是<strong>尽可能简短的形式</strong>：只要文件名在整个仓库里唯一，Obsidian 插入的链接就只有文件名，不带目录。这种写法不需要改任何设置，效果和写完整路径完全一致：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[</span><span>[</span><span>firefly-layout-system</span><span>]</span><span>]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><a href="/posts/guide/firefly-layout-system/"><div><div>Firefly 布局系统详解</div><div>深入了解 Firefly 的布局系统，包括侧边栏布局（左侧/双侧）和文章列表布局（列表/网格），以及自适应网格列数。</div><div><span>1970-01-03</span><span>博客指南</span><span><span>#Firefly</span><span>#布局</span><span>#博客</span><span>#指南</span></span></div></div><div><img alt="" loading="lazy" width="480" height="270" src="/_astro/firefly1.CZCuCN4V_1uGCKh.webp" /></div></a><p>文件名重名时这种写法会失效，构建日志里会给出提示，改用完整文件路径即可，所以推荐直接修改 Obsidian 内部链接类型设置为<strong>基于仓库根目录的绝对路径</strong>，使用第二种文件路径写法，一劳永逸</p></section></section>
<section><h2>链接到其他文章的标题<a href="#链接到其他文章的标题"><span>#</span></a></h2><p>在文章 slug 后添加 <code>#标题</code>。带标题锚点的链接始终渲染为普通链接：</p><p><a href="/posts/code-examples/#%E8%AF%AD%E6%B3%95%E9%AB%98%E4%BA%AE">查看代码块语法高亮</a></p><p><a href="/posts/guide/firefly-layout-system/#%E7%9B%B8%E5%85%B3%E9%93%BE%E6%8E%A5">Firefly 布局系统详解#相关链接</a></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[</span><span>[</span><span>code-examples#语法高亮|查看代码块语法高亮</span><span>]</span><span>]</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>[</span><span>[</span><span>guide/firefly-layout-system#相关链接|firefly-layout-system</span><span>]</span><span>]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>标题锚点使用与页面标题相同的 slug 规则，因此中文、空格和大小写都会按页面实际生成的 ID 处理。</p></section>
<section><h2>链接到本页标题<a href="#链接到本页标题"><span>#</span></a></h2><p>省略文章 slug，只填写标题即可链接到当前文章：</p><p><a href="#%E6%9C%AC%E9%A1%B5%E7%9B%AE%E6%A0%87">跳转到本页目标</a></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[</span><span>[</span><span>#本页目标|跳转到本页目标</span><span>]</span><span>]</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>本页目标<a href="#本页目标"><span>#</span></a></h2><p>这是本页 Wiki Link 指向的标题。</p></section>
<section><h2>不支持附件嵌入<a href="#不支持附件嵌入"><span>#</span></a></h2><p>附件嵌入语法目前不会被转换，会按原文显示：</p><p>![[image.png]]</p><p>行内代码和代码块中的 <code>[[firefly]]</code> 也不会被转换。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/encrypted-demo/</id>
      <title type="text">Firefly 文章加密</title>
      <published>1970-01-02T00:00:00.000Z</published>
      <updated>1970-01-02T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/encrypted-demo/"/>
      <summary type="text">这是一篇密码保护的示例文章，用于演示文章加密功能。</summary>
      <content type="html"><![CDATA[本文已加密保护，请访问网站查看。]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/katex-math-example/</id>
      <title type="text">KaTeX 数学公式示例</title>
      <published>1970-01-02T00:00:00.000Z</published>
      <updated>1970-01-02T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/katex-math-example/"/>
      <summary type="text">展示 Firefly 主题对 KaTeX 数学公式的支持，包括行内公式、块级公式和复杂数学符号。</summary>
      <content type="html"><![CDATA[<p>本文展示了 <a href="https://github.com/CuteLeaf/Firefly" target="_blank">Firefly</a> 主题对 KaTeX 数学公式的渲染支持。</p>
<section><h2>行内公式 (Inline)<a href="#行内公式-inline"><span>#</span></a></h2><p>行内公式使用单个 <code>$</code> 符号包裹。</p><p>例如：欧拉公式 <span><span>eiπ+1=0e^{i\pi} + 1 = 0</span><span><span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span><span>iπ</span></span></span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>1</span><span></span><span>=</span><span></span></span><span><span></span><span>0</span></span></span></span> 是数学中最优美的公式之一。</p><p>质能方程 <span><span>E=mc2E = mc^2</span><span><span><span></span><span>E</span><span></span><span>=</span><span></span></span><span><span></span><span>m</span><span><span>c</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span></span></span></span></span></span></span></span> 也是家喻户晓。</p></section>
<section><h2>块级公式 (Block)<a href="#块级公式-block"><span>#</span></a></h2><p>块级公式使用两个 <code>$$</code> 符号包裹，会居中显示。</p><span><span><span>∫−∞∞e−x2dx=π\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}</span><span><span><span></span><span><span>∫</span><span><span><span><span><span><span></span><span><span><span>−</span><span>∞</span></span></span></span><span><span></span><span><span><span>∞</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span><span>−</span><span><span>x</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span><span>d</span><span>x</span><span></span><span>=</span><span></span></span><span><span></span><span><span><span><span><span><span></span><span><span>π</span></span></span><span><span></span><span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span><span><span>x=−b±b2−4ac2ax = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}</span><span><span><span></span><span>x</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span><span>a</span></span></span><span><span></span><span></span></span><span><span></span><span><span>−</span><span>b</span><span></span><span>±</span><span></span><span><span><span><span><span><span></span><span><span><span>b</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span></span></span></span></span><span></span><span>−</span><span></span><span>4</span><span>a</span><span>c</span></span></span><span><span></span><span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span></section>
<section><h2>复杂示例<a href="#复杂示例"><span>#</span></a></h2><section><h3>矩阵 (Matrices)<a href="#矩阵-matrices"><span>#</span></a></h3><span><span><span>(abcd)(αβγδ)=(aα+bγaβ+bδcα+dγcβ+dδ)\begin{pmatrix}
a &amp; b \\
c &amp; d
\end{pmatrix}
\begin{pmatrix}
\alpha &amp; \beta \\
\gamma &amp; \delta
\end{pmatrix} =
\begin{pmatrix}
a\alpha + b\gamma &amp; a\beta + b\delta \\
c\alpha + d\gamma &amp; c\beta + d\delta
\end{pmatrix}</span><span><span><span></span><span><span><span>(</span></span><span><span><span><span><span><span><span><span></span><span><span>a</span></span></span><span><span></span><span><span>c</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span></span><span><span><span><span><span><span></span><span><span>b</span></span></span><span><span></span><span><span>d</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span><span><span>)</span></span></span><span></span><span><span><span>(</span></span><span><span><span><span><span><span><span><span></span><span><span>α</span></span></span><span><span></span><span><span>γ</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span></span><span><span><span><span><span><span></span><span><span>β</span></span></span><span><span></span><span><span>δ</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span><span><span>)</span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span><span>(</span></span><span><span><span><span><span><span><span><span></span><span><span>a</span><span>α</span><span></span><span>+</span><span></span><span>bγ</span></span></span><span><span></span><span><span>c</span><span>α</span><span></span><span>+</span><span></span><span>d</span><span>γ</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span></span><span><span><span><span><span><span></span><span><span>a</span><span>β</span><span></span><span>+</span><span></span><span>b</span><span>δ</span></span></span><span><span></span><span><span>c</span><span>β</span><span></span><span>+</span><span></span><span>d</span><span>δ</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span><span><span>)</span></span></span></span></span></span></span></section><section><h3>极限与求和 (Limits and Sums)<a href="#极限与求和-limits-and-sums"><span>#</span></a></h3><span><span><span>∑n=1∞1n2=π26\sum_{n=1}^{\infty} \frac{1}{n^2} = \frac{\pi^2}{6}</span><span><span><span></span><span><span><span><span><span><span></span><span><span><span>n</span><span>=</span><span>1</span></span></span></span><span><span></span><span><span>∑</span></span></span><span><span></span><span><span><span>∞</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>n</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span></span></span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>6</span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>π</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><span><span><span>lim⁡x→0sin⁡xx=1\lim_{x \to 0} \frac{\sin x}{x} = 1</span><span><span><span></span><span><span><span><span><span><span></span><span><span><span>x</span><span>→</span><span>0</span></span></span></span><span><span></span><span><span>lim</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>x</span></span></span><span><span></span><span></span></span><span><span></span><span><span>sin</span><span></span><span>x</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>1</span></span></span></span></span></section><section><h3>麦克斯韦方程组 (Maxwell’s Equations)<a href="#麦克斯韦方程组-maxwells-equations"><span>#</span></a></h3><span><span><span>∇⋅E=ρε0∇⋅B=0∇×E=−∂B∂t∇×B=μ0J+μ0ε0∂E∂t\begin{aligned}
\nabla \cdot \mathbf{E} &amp;= \frac{\rho}{\varepsilon_0} \\
\nabla \cdot \mathbf{B} &amp;= 0 \\
\nabla \times \mathbf{E} &amp;= -\frac{\partial \mathbf{B}}{\partial t} \\
\nabla \times \mathbf{B} &amp;= \mu_0\mathbf{J} + \mu_0\varepsilon_0\frac{\partial \mathbf{E}}{\partial t}
\end{aligned}</span><span><span><span></span><span><span><span><span><span><span><span><span></span><span><span>∇</span><span></span><span>⋅</span><span></span><span>E</span></span></span><span><span></span><span><span>∇</span><span></span><span>⋅</span><span></span><span>B</span></span></span><span><span></span><span><span>∇</span><span></span><span>×</span><span></span><span>E</span></span></span><span><span></span><span><span>∇</span><span></span><span>×</span><span></span><span>B</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span><span><span><span><span><span></span><span><span></span><span></span><span>=</span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>ε</span><span><span><span><span><span><span></span><span><span>0</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span>ρ</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span><span><span></span><span><span></span><span></span><span>=</span><span></span><span>0</span></span></span><span><span></span><span><span></span><span></span><span>=</span><span></span><span>−</span><span><span></span><span><span><span><span><span><span></span><span><span>∂</span><span>t</span></span></span><span><span></span><span></span></span><span><span></span><span><span>∂</span><span>B</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span><span><span></span><span><span></span><span></span><span>=</span><span></span><span><span>μ</span><span><span><span><span><span><span></span><span><span>0</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>J</span><span></span><span>+</span><span></span><span><span>μ</span><span><span><span><span><span><span></span><span><span>0</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span>ε</span><span><span><span><span><span><span></span><span><span>0</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span></span><span><span><span><span><span><span></span><span><span>∂</span><span>t</span></span></span><span><span></span><span></span></span><span><span></span><span><span>∂</span><span>E</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span></span></section><section><h3>化学方程式 (Chemical Equations)<a href="#化学方程式-chemical-equations"><span>#</span></a></h3><span><span><span>\ceCH4+2O2−&gt;CO2+2H2O\ce{CH4 + 2O2 -&gt; CO2 + 2H2O}</span><span><span><span></span><span><span>\ce</span></span><span><span>C</span><span>H</span><span>4</span><span></span><span>+</span><span></span><span>2</span><span>O</span><span>2</span><span>−</span><span></span><span>&gt;</span><span></span><span>C</span><span>O</span><span>2</span><span></span><span>+</span><span></span><span>2</span><span>H</span><span>2</span><span>O</span></span></span></span></span></span></section></section>
<section><h2>更多符号<a href="#更多符号"><span>#</span></a></h2>

<table><thead><tr><th>符号</th><th>代码</th><th>渲染结果</th></tr></thead><tbody><tr><td>Alpha</td><td><code>\alpha</code></td><td><span><span>α\alpha</span><span><span><span></span><span>α</span></span></span></span></td></tr><tr><td>Beta</td><td><code>\beta</code></td><td><span><span>β\beta</span><span><span><span></span><span>β</span></span></span></span></td></tr><tr><td>Gamma</td><td><code>\Gamma</code></td><td><span><span>Γ\Gamma</span><span><span><span></span><span>Γ</span></span></span></span></td></tr><tr><td>Pi</td><td><code>\pi</code></td><td><span><span>π\pi</span><span><span><span></span><span>π</span></span></span></span></td></tr><tr><td>Infinity</td><td><code>\infty</code></td><td><span><span>∞\infty</span><span><span><span></span><span>∞</span></span></span></span></td></tr><tr><td>Right Arrow</td><td><code>\rightarrow</code></td><td><span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span></td></tr><tr><td>Partial</td><td><code>\partial</code></td><td><span><span>∂\partial</span><span><span><span></span><span>∂</span></span></span></span></td></tr></tbody></table><p>更多 KaTeX 语法请参考 <a href="https://katex.org/docs/supported.html" target="_blank">KaTeX Supported Functions</a>。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/mdx-example/</id>
      <title type="text">MDX 格式文章示例</title>
      <published>1970-01-02T00:00:00.000Z</published>
      <updated>1970-01-02T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/mdx-example/"/>
      <summary type="text">这是一个 MDX 格式的示例文章，展示了如何在 Markdown 中使用 JSX。</summary>
      <content type="html"><![CDATA[<div><div><div></div><div>Tip</div></div><div><p><a href="https://github.com/CuteLeaf/Firefly" target="_blank">Firefly</a> 支持 <code>MDX</code> 和 <code>Markdown</code> 两种类型的文章，你可以在文章中混合使用两种格式，如果没有特别复杂内容和需求，推荐使用 Markdown 格式就够了。</p></div></div>
<section><h2>Markdown 和 MDX 的区别<a href="#markdown-和-mdx-的区别"><span>#</span></a></h2><ul>
<li>Markdown (MD) 是一种轻量级标记语言，允许用户使用纯文本格式编写文档，然后将其转换为格式化的HTML。它因其简洁易用的语法而广受欢迎，特别适合编写文档和博客文章。</li>
<li>MDX 是一种扩展了 Markdown 语法的格式，允许在 Markdown 文档中无缝地插入 JSX 代码。通过 MDX，用户可以在文档中嵌入 React 组件，从而实现更丰富的交互性和动态性。</li>
</ul>

<table><thead><tr><th>特性</th><th>Markdown</th><th>MDX</th></tr></thead><tbody><tr><td>基础语法</td><td>支持 (CommonMark)</td><td>支持 (CommonMark)</td></tr><tr><td>HTML 标签</td><td>支持</td><td>支持 (作为 JSX)</td></tr><tr><td>组件导入</td><td>不支持</td><td>支持 (import)</td></tr><tr><td>动态数据</td><td>不支持</td><td>支持 (JS 表达式)</td></tr><tr><td>样式定制</td><td>有限 (class/style)</td><td>灵活 (className/CSS-in-JS)</td></tr></tbody></table><hr /></section>
<section><h2>Firefly 内置的专属 MDX UI 组件<a href="#firefly-内置的专属-mdx-ui-组件"><span>#</span></a></h2><p>这里列出 Firefly 内置的 MDX UI 组件，方便在 MDX 文章中直接使用。</p><section><h3>Tab分组：<code>TabGroup</code><a href="#tab分组tabgroup"><span>#</span></a></h3><p>用于创建可切换的标签页，方便在文章中展示不同语言或不同版本的代码等等。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>TabGroup</span><span> </span><span>labels</span><span>=</span><span>{</span><span>[</span><span>"test.js"</span><span>, </span><span>"test.py"</span><span>]</span><span>}</span><span> </span><span>client</span><span>:</span><span>load</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span>  </span><span>```</span><span>js</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>1</span><span>)</span></div></div><div><div><div>4</div></div><div><span>  </span><span>```</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>```</span><span>py</span></div></div><div><div><div>7</div></div><div><span>  </span><span>print</span><span>(</span><span>"hello world"</span><span>)</span></div></div><div><div><div>8</div></div><div><span>  </span><span>```</span></div></div><div><div><div>9</div></div><div><span>&lt;/</span><span>TabGroup</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><div>test.jstest.py</div> <div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>1</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>print</span><span>(</span><span>"hello world"</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></div></div><hr /></section><section><h3>时间线：<code>Timeline</code> / <code>TimelineItem</code><a href="#时间线timeline--timelineitem"><span>#</span></a></h3>

<table><thead><tr><th>属性</th><th>必填</th><th>说明</th></tr></thead><tbody><tr><td><code>date</code></td><td>否</td><td>时间标签，如 “2024-03-10”</td></tr><tr><td><code>title</code></td><td>否</td><td>条目标题</td></tr><tr><td><code>type</code></td><td>否</td><td>节点颜色，default 为主题色，其余对应提示框色板（tip / note / important / warning / caution）</td></tr><tr><td><code>icon</code></td><td>否</td><td>内置图标名，显示在圆点内：star / rocket / git / flag / book / heart / zap / bug / tag / calendar / code / edit</td></tr><tr><td>正文</td><td>否</td><td><code>&lt;TimelineItem&gt;</code> 标签内的内容，支持 Markdown</td></tr></tbody></table><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>Timeline</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>TimelineItem</span><span> </span><span>date</span><span>=</span><span>"2026-08-16"</span><span> </span><span>title</span><span>=</span><span>"新版本发布"</span><span> </span><span>icon</span><span>=</span><span>"rocket"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>发布了全新版本，带来以下更新：</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>- 新增时间线组件</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>- 改进暗色主题</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>正文里支持 </span><span>**</span><span>Markdown</span><span>**</span><span> 内容。</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>&lt;/</span><span>TimelineItem</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>&lt;</span><span>TimelineItem</span><span> </span><span>date</span><span>=</span><span>"2025-11-02"</span><span> </span><span>title</span><span>=</span><span>"站点上线"</span><span> </span><span>type</span><span>=</span><span>"tip"</span><span> </span><span>icon</span><span>=</span><span>"flag"</span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>欢迎来到我的博客。</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>&lt;/</span><span>TimelineItem</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>&lt;</span><span>TimelineItem</span><span> </span><span>date</span><span>=</span><span>"2025-06-15"</span><span> </span><span>title</span><span>=</span><span>"项目立项"</span><span> </span><span>type</span><span>=</span><span>"note"</span><span>&gt;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>开始规划 Firefly 主题。</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>&lt;/</span><span>TimelineItem</span><span>&gt;</span></div></div><div><div><div>16</div></div><div><span>&lt;/</span><span>Timeline</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><ol><li><div><span></span></div><div><div>2026-08-16</div><div>新版本发布</div><div><p>发布了全新版本，带来以下更新：</p><ul>
<li>新增时间线组件</li>
<li>改进暗色主题</li>
</ul><p>正文里支持 <strong>Markdown</strong> 内容。</p></div></div></li><li><div><span></span></div><div><div>2025-11-02</div><div>站点上线</div><div><p>欢迎来到我的博客。</p></div></div></li><li><div></div><div><div>2025-06-15</div><div>项目立项</div><div><p>开始规划 Firefly 主题。</p></div></div></li></ol><hr /></section><section><h3>步骤条：<code>Steps</code> / <code>StepItem</code><a href="#步骤条steps--stepitem"><span>#</span></a></h3><p>编号步骤条，适合教程、部署流程。编号自动生成，无需手动传序号。</p>

<table><thead><tr><th>属性</th><th>必填</th><th>说明</th></tr></thead><tbody><tr><td><code>title</code></td><td>否</td><td>步骤标题</td></tr><tr><td><code>type</code></td><td>否</td><td>编号圆颜色，default 为主题色，其余对应提示框色板（tip / note / important / warning / caution）</td></tr><tr><td><code>icon</code></td><td>否</td><td>内置图标名，有图标时显示图标而非数字（star / rocket / git / flag / book / heart / zap / bug / tag / calendar / code / edit）</td></tr><tr><td>正文</td><td>否</td><td><code>&lt;StepItem&gt;</code> 标签内的内容，支持 Markdown</td></tr></tbody></table><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>Steps</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>StepItem</span><span> </span><span>title</span><span>=</span><span>"安装依赖"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>运行 </span><span>`</span><span>pnpm install</span><span>`</span><span>。</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>&lt;/</span><span>StepItem</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>&lt;</span><span>StepItem</span><span> </span><span>title</span><span>=</span><span>"启动开发服务器"</span><span> </span><span>type</span><span>=</span><span>"tip"</span><span> </span><span>icon</span><span>=</span><span>"rocket"</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>运行 </span><span>`</span><span>pnpm dev</span><span>`</span><span>，打开 </span><span>`</span><span>localhost:4321</span><span>`</span><span>。</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>&lt;/</span><span>StepItem</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>&lt;</span><span>StepItem</span><span> </span><span>title</span><span>=</span><span>"编写文章"</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>在 </span><span>`</span><span>src/content/posts/</span><span>`</span><span> 新建 </span><span>`</span><span>.mdx</span><span>`</span><span> 文章。</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>&lt;/</span><span>StepItem</span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span>&lt;/</span><span>Steps</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol><li><span></span><div><div>安装依赖</div><div><p>运行 <code>pnpm install</code>。</p></div></div></li><li><span><span></span></span><div><div>启动开发服务器</div><div><p>运行 <code>pnpm dev</code>，打开 <code>localhost:4321</code>。</p></div></div></li><li><span></span><div><div>编写文章</div><div><p>在 <code>src/content/posts/</code> 新建 <code>.mdx</code> 文章。</p></div></div></li></ol><hr /></section><section><h3>徽章：<code>Badge</code><a href="#徽章badge"><span>#</span></a></h3><p>内联彩色小徽章，可嵌在段落或标题里。</p>

<table><thead><tr><th>属性</th><th>必填</th><th>说明</th></tr></thead><tbody><tr><td><code>type</code></td><td>否</td><td>default 为主题色，其余对应提示框色板（tip / note / important / warning / caution）</td></tr></tbody></table><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>Badge</span><span> </span><span>type</span><span>=</span><span>"tip"</span><span>&gt;提示&lt;/</span><span>Badge</span><span>&gt; &lt;</span><span>Badge</span><span> </span><span>type</span><span>=</span><span>"warning"</span><span>&gt;注意&lt;/</span><span>Badge</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><span>新功能</span><span>提示</span><span>笔记</span><span>重要</span><span>注意</span><span>危险</span><hr /><p>更多信息，请查看 <a href="https://mdxjs.com/" target="_blank">MDX 文档</a></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/markdown-extended/</id>
      <title type="text">Markdown 扩展功能</title>
      <published>1970-01-01T00:00:00.000Z</published>
      <updated>1970-01-01T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/markdown-extended/"/>
      <summary type="text">了解 Firefly 中的 Markdown 功能</summary>
      <content type="html"><![CDATA[<section><h2>GitHub 仓库卡片<a href="#github-仓库卡片"><span>#</span></a></h2><p>您可以添加链接到 GitHub 仓库的动态卡片，在页面加载时，仓库信息会从 GitHub API 获取。</p><a href="https://github.com/CuteLeaf/Firefly" target="_blank"><div><div><div><div></div><div>CuteLeaf</div></div><div>/</div><div>Firefly</div></div><div></div></div><div>🍀Firefly, fresh and aesthetic Astro blog theme template. </div><div><div>—</div><div>—</div><div>MIT</div><span>Astro</span></div></a><p>使用代码 <code>::github{repo="CuteLeaf/Firefly"}</code> 创建 GitHub 仓库卡片。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>::github{repo="CuteLeaf/Firefly"}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>提醒框(Admonitions)配置<a href="#提醒框admonitions配置"><span>#</span></a></h2><p>Firefly 采用了 <a href="https://github.com/lin-stephanie/rehype-callouts" target="_blank">rehype-callouts</a> 插件，支持了四种风格的提醒框主题：<code>GitHub</code>、<code>Obsidian</code>、<code>VitePress</code> 和 <code>Docusaurus</code>。您可以在 <code>src/config/siteConfig.ts</code> 中进行配置：</p><div><figure><figcaption><span>src/config/siteConfig.ts</span></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>siteConfig</span><span>:</span><span> </span><span>SiteConfig</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// ...</span></div></div><div><div><div>3</div></div><div><span>  </span><span>rehypeCallouts</span><span>:</span><span> {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>// 选项: "github" | "obsidian" | "vitepress" | "docusaurus"</span></div></div><div><div><div>5</div></div><div><span>    </span><span>theme</span><span>:</span><span> </span><span>"github"</span><span>,</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>7</div></div><div><span>  </span><span>// ...</span></div></div><div><div><div>8</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意：<strong>更改配置后需要重启开发服务器才能生效。</strong></p><p>以下是各个主题支持的类型列表，每个主题风格和语法不同，可根据喜好选择。</p><section><h3>1. GitHub 主题风格<a href="#1-github-主题风格"><span>#</span></a></h3><p>这是 GitHub 官方支持的 5 种基本类型。</p><p></p><figure><img alt="GitHub" loading="lazy" width="2144" height="1086" src="/_astro/github.urcbElKG_Z1bC9wS.webp" /><figcaption>GitHub</figcaption></figure><p></p><p><strong>基本语法</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&gt; </span><span>[</span><span>!NOTE</span><span>]</span><span> NOTE</span></div></div><div><div><div>2</div></div><div><span>&gt; 突出显示用户应该考虑的信息。</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>&gt; </span><span>[</span><span>!TIP</span><span>]</span><span> TIP</span></div></div><div><div><div>5</div></div><div><span>&gt; 可选信息，帮助用户更成功。</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&gt; </span><span>[</span><span>!IMPORTANT</span><span>]</span><span> IMPORTANT</span></div></div><div><div><div>8</div></div><div><span>&gt; 用户成功所必需的关键信息。</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>&gt; </span><span>[</span><span>!WARNING</span><span>]</span><span> WARNING</span></div></div><div><div><div>11</div></div><div><span>&gt; 关键内容，需要立即注意。</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>&gt; </span><span>[</span><span>!CAUTION</span><span>]</span><span> CAUTION</span></div></div><div><div><div>14</div></div><div><span>&gt; 行动的负面潜在后果。</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>&gt; </span><span>[</span><span>!NOTE</span><span>]</span><span> 自定义标题</span></div></div><div><div><div>17</div></div><div><span>&gt; 这是一个带有自定义标题的示例。</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section><section><h3>2. Obsidian 主题风格<a href="#2-obsidian-主题风格"><span>#</span></a></h3><p><a href="https://obsidian.md/" target="_blank">Obsidian</a> 风格支持非常丰富的类型和别名。</p>
点击展开 Obsidian 语法列表<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&gt; </span><span>[</span><span>!NOTE</span><span>]</span><span> NOTE</span></div></div><div><div><div>2</div></div><div><span>&gt; 通用的笔记块。</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>&gt; </span><span>[</span><span>!ABSTRACT</span><span>]</span><span> ABSTRACT</span></div></div><div><div><div>5</div></div><div><span>&gt; 文章的摘要。</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&gt; </span><span>[</span><span>!SUMMARY</span><span>]</span><span> SUMMARY</span></div></div><div><div><div>8</div></div><div><span>&gt; 文章的总结（同 Abstract）。</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>&gt; </span><span>[</span><span>!TLDR</span><span>]</span><span> TLDR</span></div></div><div><div><div>11</div></div><div><span>&gt; 太长不看（同 Abstract）。</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>&gt; </span><span>[</span><span>!INFO</span><span>]</span><span> INFO</span></div></div><div><div><div>14</div></div><div><span>&gt; 提供额外信息。</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>&gt; </span><span>[</span><span>!TODO</span><span>]</span><span> TODO</span></div></div><div><div><div>17</div></div><div><span>&gt; 需要完成的事项。</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>&gt; </span><span>[</span><span>!TIP</span><span>]</span><span> TIP</span></div></div><div><div><div>20</div></div><div><span>&gt; 实用技巧或提示。</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>&gt; </span><span>[</span><span>!HINT</span><span>]</span><span> HINT</span></div></div><div><div><div>23</div></div><div><span>&gt; 暗示（同 Tip）。</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>&gt; </span><span>[</span><span>!IMPORTANT</span><span>]</span><span> IMPORTANT</span></div></div><div><div><div>26</div></div><div><span>&gt; 重要信息（Obsidian 风格通常使用类似的图标）。</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>&gt; </span><span>[</span><span>!SUCCESS</span><span>]</span><span> SUCCESS</span></div></div><div><div><div>29</div></div><div><span>&gt; 操作成功。</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>&gt; </span><span>[</span><span>!CHECK</span><span>]</span><span> CHECK</span></div></div><div><div><div>32</div></div><div><span>&gt; 检查通过（同 Success）。</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>&gt; </span><span>[</span><span>!DONE</span><span>]</span><span> DONE</span></div></div><div><div><div>35</div></div><div><span>&gt; 已完成（同 Success）。</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>&gt; </span><span>[</span><span>!QUESTION</span><span>]</span><span> QUESTION</span></div></div><div><div><div>38</div></div><div><span>&gt; 提出问题。</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>&gt; </span><span>[</span><span>!HELP</span><span>]</span><span> HELP</span></div></div><div><div><div>41</div></div><div><span>&gt; 寻求帮助（同 Question）。</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span>&gt; </span><span>[</span><span>!FAQ</span><span>]</span><span> FAQ</span></div></div><div><div><div>44</div></div><div><span>&gt; 常见问题（同 Question）。</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>&gt; </span><span>[</span><span>!WARNING</span><span>]</span><span> WARNING</span></div></div><div><div><div>47</div></div><div><span>&gt; 警告信息。</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>&gt; </span><span>[</span><span>!CAUTION</span><span>]</span><span> CAUTION</span></div></div><div><div><div>50</div></div><div><span>&gt; 注意事项（同 Warning）。</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>&gt; </span><span>[</span><span>!ATTENTION</span><span>]</span><span> ATTENTION</span></div></div><div><div><div>53</div></div><div><span>&gt; 引起注意（同 Warning）。</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>&gt; </span><span>[</span><span>!FAILURE</span><span>]</span><span> FAILURE</span></div></div><div><div><div>56</div></div><div><span>&gt; 操作失败。</span></div></div><div><div><div>57</div></div><div>
</div></div><div><div><div>58</div></div><div><span>&gt; </span><span>[</span><span>!FAIL</span><span>]</span><span> FAIL</span></div></div><div><div><div>59</div></div><div><span>&gt; 失败（同 Failure）。</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span>&gt; </span><span>[</span><span>!MISSING</span><span>]</span><span> MISSING</span></div></div><div><div><div>62</div></div><div><span>&gt; 缺失内容（同 Failure）。</span></div></div><div><div><div>63</div></div><div>
</div></div><div><div><div>64</div></div><div><span>&gt; </span><span>[</span><span>!DANGER</span><span>]</span><span> DANGER</span></div></div><div><div><div>65</div></div><div><span>&gt; 危险操作警告。</span></div></div><div><div><div>66</div></div><div>
</div></div><div><div><div>67</div></div><div><span>&gt; </span><span>[</span><span>!ERROR</span><span>]</span><span> ERROR</span></div></div><div><div><div>68</div></div><div><span>&gt; 错误信息（同 Danger）。</span></div></div><div><div><div>69</div></div><div>
</div></div><div><div><div>70</div></div><div><span>&gt; </span><span>[</span><span>!BUG</span><span>]</span><span> BUG</span></div></div><div><div><div>71</div></div><div><span>&gt; 报告软件缺陷。</span></div></div><div><div><div>72</div></div><div>
</div></div><div><div><div>73</div></div><div><span>&gt; </span><span>[</span><span>!EXAMPLE</span><span>]</span><span> EXAMPLE</span></div></div><div><div><div>74</div></div><div><span>&gt; 展示一个例子。</span></div></div><div><div><div>75</div></div><div>
</div></div><div><div><div>76</div></div><div><span>&gt; </span><span>[</span><span>!QUOTE</span><span>]</span><span> QUOTE</span></div></div><div><div><div>77</div></div><div><span>&gt; 引用一段话。</span></div></div><div><div><div>78</div></div><div>
</div></div><div><div><div>79</div></div><div><span>&gt; </span><span>[</span><span>!CITE</span><span>]</span><span> CITE</span></div></div><div><div><div>80</div></div><div><span>&gt; 引证（同 Quote）。</span></div></div><div><div><div>81</div></div><div>
</div></div><div><div><div>82</div></div><div><span>&gt; </span><span>[</span><span>!NOTE</span><span>]</span><span> 自定义标题</span></div></div><div><div><div>83</div></div><div><span>&gt; 这是一个带有自定义标题的示例。</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p></p><figure><img alt="Obsidian" loading="lazy" width="2144" height="1320" src="/_astro/obsidian.Cwb2iYzd_1I7Wom.webp" /><figcaption>Obsidian</figcaption></figure><p></p><hr /></section><section><h3>3. VitePress 主题风格<a href="#3-vitepress-主题风格"><span>#</span></a></h3><p><a href="https://vitepress.dev/" target="_blank">VitePress</a> 风格提供了一套现代化的、扁平的默认样式。目前仅包含与 GitHub 一致的 <strong>5 种</strong> 基础类型。</p>
点击展开 VitePress 语法列表<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&gt; </span><span>[</span><span>!NOTE</span><span>]</span><span> NOTE</span></div></div><div><div><div>2</div></div><div><span>&gt; 对应 GitHub 的 Note。</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>&gt; </span><span>[</span><span>!TIP</span><span>]</span><span> TIP</span></div></div><div><div><div>5</div></div><div><span>&gt; 对应 GitHub 的 Tip。</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&gt; </span><span>[</span><span>!IMPORTANT</span><span>]</span><span> IMPORTANT</span></div></div><div><div><div>8</div></div><div><span>&gt; 对应 GitHub 的 Important。</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>&gt; </span><span>[</span><span>!WARNING</span><span>]</span><span> WARNING</span></div></div><div><div><div>11</div></div><div><span>&gt; 对应 GitHub 的 Warning。</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>&gt; </span><span>[</span><span>!CAUTION</span><span>]</span><span> CAUTION</span></div></div><div><div><div>14</div></div><div><span>&gt; 对应 GitHub 的 Caution。</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>&gt; </span><span>[</span><span>!TIP</span><span>]</span><span> 自定义标题</span></div></div><div><div><div>17</div></div><div><span>&gt; VitePress 风格同样支持自定义标题。</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p></p><figure><img alt="VitePress" loading="lazy" width="2144" height="1318" src="/_astro/vitepress.D2YnjBWE_Z1rUl1H.webp" /><figcaption>VitePress</figcaption></figure><p></p><hr /></section><section><h3>4. Docusaurus 主题风格<a href="#4-docusaurus-主题风格"><span>#</span></a></h3><p><a href="https://docusaurus.io/docs/markdown-features/admonitions" target="_blank">Docusaurus</a> 风格提供了一套现代化的提醒框样式，支持 5 种类型。</p>
点击展开 Docusaurus 语法列表 <p>支持以下类型的提醒框：<code>note</code> <code>tip</code> <code>info</code> <code>warning</code> <code>danger</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>:::note</span></div></div><div><div><div>2</div></div><div><span>突出显示用户应该考虑的信息，即使在快速浏览时也是如此。</span></div></div><div><div><div>3</div></div><div><span>:::</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>:::tip</span></div></div><div><div><div>6</div></div><div><span>可选信息，帮助用户更成功。</span></div></div><div><div><div>7</div></div><div><span>:::</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>:::info</span></div></div><div><div><div>10</div></div><div><span>一般信息。</span></div></div><div><div><div>11</div></div><div><span>:::</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>:::warning</span></div></div><div><div><div>14</div></div><div><span>由于潜在风险需要用户立即注意的关键内容。</span></div></div><div><div><div>15</div></div><div><span>:::</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>:::danger</span></div></div><div><div><div>18</div></div><div><span>行动的负面潜在后果。</span></div></div><div><div><div>19</div></div><div><span>:::</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>:::tip</span><span>[</span><span>自定义标题</span><span>]</span></div></div><div><div><div>22</div></div><div><span>可选信息，帮助用户更成功。</span></div></div><div><div><div>23</div></div><div><span>:::</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p></p><figure><img alt="Docusaurus" loading="lazy" width="2158" height="1394" src="/_astro/docusaurus.Cu-On_k2_Zy4rHX.webp" /><figcaption>Docusaurus</figcaption></figure><p></p><hr /></section></section>
<section><h2>剧透<a href="#剧透"><span>#</span></a></h2><p>您可以为文本添加剧透。文本也支持 <strong>Markdown</strong> 语法。</p><p>内容 被隐藏了 <strong>哈哈</strong>！</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>内容 :spoiler[被隐藏了 </span><span>**哈哈**</span><span>]！</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>图片画廊网格 (Image Grid)<a href="#图片画廊网格-image-grid"><span>#</span></a></h2><p>您可以使用 <code>[grid]</code> 和 <code>[/grid]</code> 标签将多张图片纵向并排展示。这对于展示照片画廊或对比图非常有用。系统会自动根据包裹在其中的图片数量（最多支持并排展示4张）以响应式网格进行布局。</p><p><strong>自动补齐图片高度：</strong> 同一排中如果有高度、大小或者比例不一的图片，会像「九宫格画廊相册」一样自动撑满。较短或不协调的图片会自动使用 object-cover 进行完美中心裁剪补充视野。图片边框水平彻底对齐无缝隙，但被裁剪后，只有点击图片通过灯箱才能查看完整图片，所以建议尽量避免使用长宽比例不一致的图片在同一排中。</p><p><strong>图注恒定底端对齐：</strong> 不论上面的图片长宽如何变化，在同一行的所有图像解释文字（图注）都会对标到一条完美的水平基线上了。</p><div><figure><img alt="示例图片一" loading="lazy" width="3328" height="1872" src="/_astro/firefly1.CZCuCN4V_1dgNYg.webp" /><figcaption>示例图片一</figcaption></figure><figure><img alt="示例图片二" loading="lazy" width="4096" height="2212" src="/_astro/firefly2.dxmXG905_ZyJm5m.webp" /><figcaption>示例图片二</figcaption></figure><figure><img alt="示例图片二" loading="lazy" width="3840" height="2160" src="/_astro/firefly3.CGzFXcxi_Z1Lz6q8.webp" /><figcaption>示例图片二</figcaption></figure></div><p><strong>基本语法</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[</span><span>grid</span><span>]</span></div></div><div><div><div>2</div></div><div><span>![</span><span>示例图片一</span><span>]</span><span><span>(</span><span>./images/firefly1.avif</span><span>)</span></span></div></div><div><div><div>3</div></div><div><span>![</span><span>示例图片二</span><span>]</span><span><span>(</span><span>./images/firefly2.avif</span><span>)</span></span></div></div><div><div><div>4</div></div><div><span>![</span><span>示例图片二</span><span>]</span><span><span>(</span><span>./images/firefly3.avif</span><span>)</span></span></div></div><div><div><div>5</div></div><div><span>[</span><span>/grid</span><span>]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/markdown-mermaid/</id>
      <title type="text">Markdown Mermaid 图表</title>
      <published>1970-01-01T00:00:00.000Z</published>
      <updated>1970-01-01T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/markdown-mermaid/"/>
      <summary type="text">一个包含 Mermaid 的 Markdown 博客文章简单示例。</summary>
      <content type="html"><![CDATA[<section><h2>Markdown 中 Mermaid 图表完整指南<a href="#markdown-中-mermaid-图表完整指南"><span>#</span></a></h2><p>本文演示如何在 Markdown 文档中使用 Mermaid 创建各种复杂图表，包括流程图、时序图、ER 图、类图、状态图、XY 图、甘特图、思维导图等。</p><blockquote><p>Mermaid 图表由 <a href="https://github.com/Latias94/merman" target="_blank">Merman</a> 实现。Firefly 在 Astro 构建阶段生成亮色和深色两套静态 SVG，无需在浏览器中加载 Mermaid 渲染运行时。可以前往 <a href="http://frankorz.com/merman/" target="_blank">Merman Playground</a> 实时编辑语法并预览渲染结果。</p></blockquote></section>
<section><h2>流程图示例<a href="#流程图示例"><span>#</span></a></h2><p>流程图非常适合表示流程或算法步骤。</p><div><div><div><div><span></span></div><div><span><p>是</p></span></div><div><span><p>否</p></span></div><div><span><p>选项 1</p></span></div><div><span><p>选项 2</p></span></div><div><span><p>选项 3</p></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span><p>子过程详情</p></span></div><div><span></span></div><div><span></span></div><div><span><p>子步骤 1</p></span></div><div><span><p>子步骤 2</p></span></div><div><span><p>子步骤 3</p></span></div><div><span><p>开始</p></span></div><div><span><p>条件检查</p></span></div><div><span><p>处理步骤 1</p></span></div><div><span><p>处理步骤 2</p></span></div><div><span><p>另一个决策</p></span></div><div><span><p>结果 1</p></span></div><div><span><p>结果 2</p></span></div><div><span><p>结果 3</p></span></div><div><span><p>结束</p></span></div>
</div><div><div><span></span></div><div><span><p>是</p></span></div><div><span><p>否</p></span></div><div><span><p>选项 1</p></span></div><div><span><p>选项 2</p></span></div><div><span><p>选项 3</p></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span><p>子过程详情</p></span></div><div><span></span></div><div><span></span></div><div><span><p>子步骤 1</p></span></div><div><span><p>子步骤 2</p></span></div><div><span><p>子步骤 3</p></span></div><div><span><p>开始</p></span></div><div><span><p>条件检查</p></span></div><div><span><p>处理步骤 1</p></span></div><div><span><p>处理步骤 2</p></span></div><div><span><p>另一个决策</p></span></div><div><span><p>结果 1</p></span></div><div><span><p>结果 2</p></span></div><div><span><p>结果 3</p></span></div><div><span><p>结束</p></span></div>
</div></div></div></section>
<section><h2>时序图示例<a href="#时序图示例"><span>#</span></a></h2><p>时序图显示对象之间随时间的交互。</p><div><div><div>数据库服务器网页应用用户数据库服务器网页应用用户alt[认证成功][认证失败]提交登录请求发送认证请求查询用户凭据返回用户数据返回认证结果显示欢迎页面请求用户数据获取用户偏好返回偏好设置返回用户数据加载个性化界面显示错误消息提示重新输入
</div><div>数据库服务器网页应用用户数据库服务器网页应用用户alt[认证成功][认证失败]提交登录请求发送认证请求查询用户凭据返回用户数据返回认证结果显示欢迎页面请求用户数据获取用户偏好返回偏好设置返回用户数据加载个性化界面显示错误消息提示重新输入
</div></div></div></section>
<section><h2>ER 图示例<a href="#er-图示例"><span>#</span></a></h2><p>ER 图（实体关系图）非常适合表示数据库结构。</p><div><div><div>

<div><span><p>writes</p></span></div><div><span><p>posts</p></span></div><div><span><p>has</p></span></div><div><span><p>belongs to</p></span></div>
<div><span><p>USER</p></span></div><div><span><p>int</p></span></div><div><span><p>id</p></span></div><div><span><p>PK</p></span></div><div><span></span></div><div><span><p>string</p></span></div><div><span><p>username</p></span></div><div><span></span></div><div><span></span></div><div><span><p>string</p></span></div><div><span><p>email</p></span></div><div><span></span></div><div><span></span></div><div><span><p>datetime</p></span></div><div><span><p>created_at</p></span></div><div><span></span></div><div><span></span></div><div><span><p>ARTICLE</p></span></div><div><span><p>int</p></span></div><div><span><p>id</p></span></div><div><span><p>PK</p></span></div><div><span></span></div><div><span><p>string</p></span></div><div><span><p>title</p></span></div><div><span></span></div><div><span></span></div><div><span><p>text</p></span></div><div><span><p>content</p></span></div><div><span></span></div><div><span></span></div><div><span><p>datetime</p></span></div><div><span><p>published</p></span></div><div><span></span></div><div><span></span></div><div><span><p>int</p></span></div><div><span><p>author_id</p></span></div><div><span><p>FK</p></span></div><div><span></span></div><div><span><p>COMMENT</p></span></div><div><span><p>int</p></span></div><div><span><p>id</p></span></div><div><span><p>PK</p></span></div><div><span></span></div><div><span><p>text</p></span></div><div><span><p>content</p></span></div><div><span></span></div><div><span></span></div><div><span><p>datetime</p></span></div><div><span><p>created_at</p></span></div><div><span></span></div><div><span></span></div><div><span><p>int</p></span></div><div><span><p>user_id</p></span></div><div><span><p>FK</p></span></div><div><span></span></div><div><span><p>int</p></span></div><div><span><p>article_id</p></span></div><div><span><p>FK</p></span></div><div><span></span></div><div><span><p>CATEGORY</p></span></div><div><span><p>int</p></span></div><div><span><p>id</p></span></div><div><span><p>PK</p></span></div><div><span></span></div><div><span><p>string</p></span></div><div><span><p>name</p></span></div><div><span></span></div><div><span></span></div><div><span><p>string</p></span></div><div><span><p>description</p></span></div><div><span></span></div><div><span></span></div>

</div><div>

<div><span><p>writes</p></span></div><div><span><p>posts</p></span></div><div><span><p>has</p></span></div><div><span><p>belongs to</p></span></div>
<div><span><p>USER</p></span></div><div><span><p>int</p></span></div><div><span><p>id</p></span></div><div><span><p>PK</p></span></div><div><span></span></div><div><span><p>string</p></span></div><div><span><p>username</p></span></div><div><span></span></div><div><span></span></div><div><span><p>string</p></span></div><div><span><p>email</p></span></div><div><span></span></div><div><span></span></div><div><span><p>datetime</p></span></div><div><span><p>created_at</p></span></div><div><span></span></div><div><span></span></div><div><span><p>ARTICLE</p></span></div><div><span><p>int</p></span></div><div><span><p>id</p></span></div><div><span><p>PK</p></span></div><div><span></span></div><div><span><p>string</p></span></div><div><span><p>title</p></span></div><div><span></span></div><div><span></span></div><div><span><p>text</p></span></div><div><span><p>content</p></span></div><div><span></span></div><div><span></span></div><div><span><p>datetime</p></span></div><div><span><p>published</p></span></div><div><span></span></div><div><span></span></div><div><span><p>int</p></span></div><div><span><p>author_id</p></span></div><div><span><p>FK</p></span></div><div><span></span></div><div><span><p>COMMENT</p></span></div><div><span><p>int</p></span></div><div><span><p>id</p></span></div><div><span><p>PK</p></span></div><div><span></span></div><div><span><p>text</p></span></div><div><span><p>content</p></span></div><div><span></span></div><div><span></span></div><div><span><p>datetime</p></span></div><div><span><p>created_at</p></span></div><div><span></span></div><div><span></span></div><div><span><p>int</p></span></div><div><span><p>user_id</p></span></div><div><span><p>FK</p></span></div><div><span></span></div><div><span><p>int</p></span></div><div><span><p>article_id</p></span></div><div><span><p>FK</p></span></div><div><span></span></div><div><span><p>CATEGORY</p></span></div><div><span><p>int</p></span></div><div><span><p>id</p></span></div><div><span><p>PK</p></span></div><div><span></span></div><div><span><p>string</p></span></div><div><span><p>name</p></span></div><div><span></span></div><div><span></span></div><div><span><p>string</p></span></div><div><span><p>description</p></span></div><div><span></span></div><div><span></span></div>

</div></div></div></section>
<section><h2>类图示例<a href="#类图示例"><span>#</span></a></h2><p>类图显示系统的静态结构，包括类、属性、方法及其关系。</p><div><div><div><div><span><p>写作</p></span></div><div><span><p>发表</p></span></div><div><span><p>拥有</p></span></div><div><span><p>属于</p></span></div><div><span><p>1</p></span></div><div><span><p>1</p></span></div><div><span><p>1</p></span></div><div><span><p>1</p></span></div><div><span><p>*</p></span></div><div><span><p>*</p></span></div><div><span><p>*</p></span></div><div><span><p>*</p></span></div><div><span><p>User</p></span></div><div><span><p>+String username</p></span></div><div><span><p>+String password</p></span></div><div><span><p>+String email</p></span></div><div><span><p>+Boolean active</p></span></div><div><span><p>+login()</p></span></div><div><span><p>+logout()</p></span></div><div><span><p>+updateProfile()</p></span></div><div><span><p>Article</p></span></div><div><span><p>+String title</p></span></div><div><span><p>+String content</p></span></div><div><span><p>+Date publishDate</p></span></div><div><span><p>+Boolean published</p></span></div><div><span><p>+publish()</p></span></div><div><span><p>+edit()</p></span></div><div><span><p>+delete()</p></span></div><div><span><p>Comment</p></span></div><div><span><p>+String content</p></span></div><div><span><p>+Date commentDate</p></span></div><div><span><p>+addComment()</p></span></div><div><span><p>+deleteComment()</p></span></div><div><span><p>Category</p></span></div><div><span><p>+String name</p></span></div><div><span><p>+String description</p></span></div><div><span><p>+addArticle()</p></span></div><div><span><p>+removeArticle()</p></span></div></div><div><div><span><p>写作</p></span></div><div><span><p>发表</p></span></div><div><span><p>拥有</p></span></div><div><span><p>属于</p></span></div><div><span><p>1</p></span></div><div><span><p>1</p></span></div><div><span><p>1</p></span></div><div><span><p>1</p></span></div><div><span><p>*</p></span></div><div><span><p>*</p></span></div><div><span><p>*</p></span></div><div><span><p>*</p></span></div><div><span><p>User</p></span></div><div><span><p>+String username</p></span></div><div><span><p>+String password</p></span></div><div><span><p>+String email</p></span></div><div><span><p>+Boolean active</p></span></div><div><span><p>+login()</p></span></div><div><span><p>+logout()</p></span></div><div><span><p>+updateProfile()</p></span></div><div><span><p>Article</p></span></div><div><span><p>+String title</p></span></div><div><span><p>+String content</p></span></div><div><span><p>+Date publishDate</p></span></div><div><span><p>+Boolean published</p></span></div><div><span><p>+publish()</p></span></div><div><span><p>+edit()</p></span></div><div><span><p>+delete()</p></span></div><div><span><p>Comment</p></span></div><div><span><p>+String content</p></span></div><div><span><p>+Date commentDate</p></span></div><div><span><p>+addComment()</p></span></div><div><span><p>+deleteComment()</p></span></div><div><span><p>Category</p></span></div><div><span><p>+String name</p></span></div><div><span><p>+String description</p></span></div><div><span><p>+addArticle()</p></span></div><div><span><p>+removeArticle()</p></span></div></div></div></div></section>
<section><h2>状态图示例<a href="#状态图示例"><span>#</span></a></h2><p>状态图显示对象在其生命周期中经历的状态序列。</p><div><div><div><div><span></span></div><div><span><p>提交</p></span></div><div><span><p>拒绝</p></span></div><div><span><p>批准</p></span></div><div><span><p>发布</p></span></div><div><span><p>归档</p></span></div><div><span><p>撤回</p></span></div><div><span></span></div><div><span><p>草稿</p></span></div><div><span><p>审核中</p></span></div><div><span><p>已批准</p></span></div><div><span><p>已归档</p></span></div><div><span>已发布</span></div><div><span></span></div><div><span><p>临时隐藏</p></span></div><div><span><p>恢复</p></span></div><div><span></span></div><div><span></span></div><div><span><p>活跃</p></span></div><div><span><p>隐藏</p></span></div>
</div><div><div><span></span></div><div><span><p>提交</p></span></div><div><span><p>拒绝</p></span></div><div><span><p>批准</p></span></div><div><span><p>发布</p></span></div><div><span><p>归档</p></span></div><div><span><p>撤回</p></span></div><div><span></span></div><div><span><p>草稿</p></span></div><div><span><p>审核中</p></span></div><div><span><p>已批准</p></span></div><div><span><p>已归档</p></span></div><div><span>已发布</span></div><div><span></span></div><div><span><p>临时隐藏</p></span></div><div><span><p>恢复</p></span></div><div><span></span></div><div><span></span></div><div><span><p>活跃</p></span></div><div><span><p>隐藏</p></span></div>
</div></div></div></section>
<section><h2>XY 图示例<a href="#xy-图示例"><span>#</span></a></h2><p>XY 图表非常适合展示趋势和对比数据。</p><div><div><div>月度访问量趋势1月2月3月4月5月6月5000450040003500300025002000150010005000访问量
</div><div>月度访问量趋势1月2月3月4月5月6月5000450040003500300025002000150010005000访问量
</div></div></div></section>
<section><h2>饼图示例<a href="#饼图示例"><span>#</span></a></h2><p>饼图适合直观展示各部分在整体中的占比。</p><div><div><div>45%30%15%10%内容类型占比技术文章 [45]项目记录 [30]生活随笔 [15]其他 [10]
</div><div>45%30%15%10%内容类型占比技术文章 [45]项目记录 [30]生活随笔 [15]其他 [10]
</div></div></div></section>
<section><h2>甘特图示例<a href="#甘特图示例"><span>#</span></a></h2><p>甘特图可以按时间轴展示项目阶段、任务依赖和当前进度。</p><div><div><div>07/0107/0307/0507/0707/0907/1107/1307/1507/17需求整理 视觉设计 功能实现 内容迁移 构建检查 正式上线 准备开发发布博客版本发布计划
</div><div>07/0107/0307/0507/0707/0907/1107/1307/1507/17需求整理 视觉设计 功能实现 内容迁移 构建检查 正式上线 准备开发发布博客版本发布计划
</div></div></div></section>
<section><h2>思维导图示例<a href="#思维导图示例"><span>#</span></a></h2><p>思维导图适合梳理主题层级和知识结构。</p><div><div><div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span><p>Firefly</p></span></div><div><span><p>内容</p></span></div><div><span><p>技术文章</p></span></div><div><span><p>生活记录</p></span></div><div><span><p>体验</p></span></div><div><span><p>搜索</p></span></div><div><span><p>深色模式</p></span></div><div><span><p>图表</p></span></div><div><span><p>工程</p></span></div><div><span><p>Astro</p></span></div><div><span><p>Svelte</p></span></div><div><span><p>Merman</p></span></div>
</div><div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span><p>Firefly</p></span></div><div><span><p>内容</p></span></div><div><span><p>技术文章</p></span></div><div><span><p>生活记录</p></span></div><div><span><p>体验</p></span></div><div><span><p>搜索</p></span></div><div><span><p>深色模式</p></span></div><div><span><p>图表</p></span></div><div><span><p>工程</p></span></div><div><span><p>Astro</p></span></div><div><span><p>Svelte</p></span></div><div><span><p>Merman</p></span></div>
</div></div></div></section>
<section><h2>时间线示例<a href="#时间线示例"><span>#</span></a></h2><p>时间线用于按年份或阶段呈现项目的重要事件。</p><div><div><div>2024建立博客完成基础主题2025加入搜索与图库完善内容系统2026升级   Astro   7使用   Merman   渲染图表Firefly 演进时间线
</div><div>2024建立博客完成基础主题2025加入搜索与图库完善内容系统2026升级   Astro   7使用   Merman   渲染图表Firefly 演进时间线
</div></div></div></section>
<section><h2>用户旅程图示例<a href="#用户旅程图示例"><span>#</span></a></h2><p>用户旅程图能够描述用户在不同阶段的行为和体验评分。</p><div><div><div>读者<div><div>发现内容</div></div>发现内容读者<div><div>      打开首页</div></div>      打开首页读者<div><div>      搜索主题</div></div>      搜索主题<div><div>阅读文章</div></div>阅读文章读者<div><div>      浏览正文</div></div>      浏览正文读者<div><div>      查看图表</div></div>      查看图表<div><div>继续探索</div></div>继续探索读者<div><div>      查看相关文章</div></div>      查看相关文章读者<div><div>      分享文章</div></div>      分享文章读者浏览文章的旅程
</div><div>读者<div><div>发现内容</div></div>发现内容读者<div><div>      打开首页</div></div>      打开首页读者<div><div>      搜索主题</div></div>      搜索主题<div><div>阅读文章</div></div>阅读文章读者<div><div>      浏览正文</div></div>      浏览正文读者<div><div>      查看图表</div></div>      查看图表<div><div>继续探索</div></div>继续探索读者<div><div>      查看相关文章</div></div>      查看相关文章读者<div><div>      分享文章</div></div>      分享文章读者浏览文章的旅程
</div></div></div></section>
<section><h2>Git 图示例<a href="#git-图示例"><span>#</span></a></h2><p>Git 图可以清晰展示分支、提交和合并历史。</p><div><div><div>mainfeatureinitadd-diagramspolish-themesmerge-featurerelease
</div><div>mainfeatureinitadd-diagramspolish-themesmerge-featurerelease
</div></div></div></section>
<section><h2>看板示例<a href="#看板示例"><span>#</span></a></h2><p>看板适合展示任务在不同工作阶段之间的分布。</p><div><div><div><div><span><p>待办</p></span></div><div><span><p>进行中</p></span></div><div><span><p>已完成</p></span></div><div><span><p>整理需求</p></span></div><div><span></span></div><div><span></span></div><div><span><p>准备示例</p></span></div><div><span></span></div><div><span></span></div><div><span><p>接入 Merman</p></span></div><div><span></span></div><div><span></span></div><div><span><p>服务端渲染</p></span></div><div><span></span></div><div><span></span></div><div><span><p>亮暗主题</p></span></div><div><span></span></div><div><span></span></div>
</div><div><div><span><p>待办</p></span></div><div><span><p>进行中</p></span></div><div><span><p>已完成</p></span></div><div><span><p>整理需求</p></span></div><div><span></span></div><div><span></span></div><div><span><p>准备示例</p></span></div><div><span></span></div><div><span></span></div><div><span><p>接入 Merman</p></span></div><div><span></span></div><div><span></span></div><div><span><p>服务端渲染</p></span></div><div><span></span></div><div><span></span></div><div><span><p>亮暗主题</p></span></div><div><span></span></div><div><span></span></div>
</div></div></div></section>
<section><h2>Sankey 图示例<a href="#sankey-图示例"><span>#</span></a></h2><p>Sankey 图通过连线宽度展示流量在不同节点之间的流向。</p><div><div><div>Home
1650Post list
1200Search
450Post detail
1220Related posts
260External shares
180</div><div>Home
1650Post list
1200Search
450Post detail
1220Related posts
260External shares
180</div></div></div></section>
<section><h2>总结<a href="#总结"><span>#</span></a></h2><p>Mermaid 是在 Markdown 文档中创建各种类型图表的强大工具。本文演示了流程图、时序图、ER 图、类图、状态图、XY 图、饼图、甘特图、思维导图、时间线、用户旅程图、Git 图、看板和 Sankey 图。这些图表可以帮助您更清晰地表达复杂的概念、流程和数据结构。</p><p>要使用 Mermaid，只需在代码块中指定 mermaid 语言，并使用简洁的文本语法描述图表。图表会在构建时自动渲染为 SVG，无需客户端 JavaScript 加载。</p><p>可以前往 <a href="http://frankorz.com/merman/" target="_blank">Merman Playground</a> 尝试更多语法，再将图表代码粘贴到文章中。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/markdown-plantuml/</id>
      <title type="text">Markdown PlantUML 图表</title>
      <published>1970-01-01T00:00:00.000Z</published>
      <updated>1970-01-01T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/markdown-plantuml/"/>
      <summary type="text">用于验证 Firefly 中 PlantUML 插件渲染、主题切换与交互能力的示例文章。</summary>
      <content type="html"><![CDATA[<section><h2>Markdown 中 PlantUML 图表指南<a href="#markdown-中-plantuml-图表指南"><span>#</span></a></h2><p>PlantUML 是一种使用纯文本描述图表的工具。你只需要写一段结构化语法，就可以生成时序图、类图、用例图、活动图等常见工程图。</p><p>它特别适合写在技术博客和项目文档里：</p><ul>
<li>图表和正文一起版本管理，便于协作与审阅</li>
<li>修改图只需要改文本，适合频繁迭代</li>
<li>能和 Markdown 无缝结合，保持文档统一</li>
</ul><p>在 Firefly 中，<code>plantuml</code> 代码块会在构建阶段编码并生成服务器 SVG 地址，页面端再根据亮暗主题自动切换图源，并支持缩放、拖拽和全屏交互。</p><p>如果你想快速上手，可以记住这个最小模板：</p><div><div><img alt="@startuml
Alice -&gt; Bob: Hello
Bob --&gt; Alice: Hi
@enduml" src="https://www.plantuml.com/plantuml/svg/SoWkIImgAStDuNBCoKnELT2rKt3AJx9Iy4ZDoSddSifFKj3LjLC0orafU6HoEQJcfPJcvW00" loading="lazy" /></div></div></section>
<section><h2>活动图示例<a href="#活动图示例"><span>#</span></a></h2><div><div><img alt="@startuml
start
:用户提交订单;
if (库存充足?) then (是)
	:冻结库存;
	:创建支付单;
	if (支付成功?) then (是)
		:生成发货单;
		:通知仓库拣货;
	else (否)
		:取消订单;
		:释放库存;
	endif
else (否)
	:提示缺货;
endif
stop
@enduml" src="https://www.plantuml.com/plantuml/svg/NOuz2i9048NhtLEaZ5VO57eLmHM56CN4taODYYHYbA15mFz6BHI5ZNYQp6oyXMI38dRpcFTzl9BjbDjEfs5ITL0cWmEwT_JyH6pIqmh6CuFMgveEOWh747gzz7Of5ZIdnYrDn_1Se8H1FvRnD6yOMNQN40iCpaaSAW7937b6ruTXz6SWJ0PHzl4cwNMV8uIzkWiPRHDbnj4wlUuDIhXfSqq7VwSmyEPuSpyxCsOmnE3vdSAjIhrAVnc6dYytGZw4ykK5sscsQ8bRbKx3V0C0" loading="lazy" /></div></div></section>
<section><h2>状态图示例<a href="#状态图示例"><span>#</span></a></h2><div><div><img alt="@startuml
[*] --&gt; 草稿

草稿 --&gt; 待审核 : 提交
待审核 --&gt; 草稿 : 驳回
待审核 --&gt; 已发布 : 审核通过
已发布 --&gt; 已归档 : 到期归档
已发布 --&gt; 草稿 : 撤回修改

state 已发布 {
	[*] --&gt; 可见
	可见 --&gt; 隐藏 : 手动隐藏
	隐藏 --&gt; 可见 : 恢复展示
}

已归档 --&gt; [*]
@enduml" src="https://www.plantuml.com/plantuml/svg/SoWkIImgAStDuOhMYbNGrRLJUD7R-Np5VYuk20qMUhglzUcwXSyMx52mKdZMF-79hYLS22647WKhXPShDp-TFGzDzkdsJK_x9pxTqQnWfG0HVjamwyN-TYw414pPtadF5Yu6AUlOy6pEV0WNJHdShcUJbZoTFU_9_dNFfkpauYekIIn9HRAgceiJvgEd_UjVB6_aueJGOA6NioQycDuFCgIp-sdN2WYNYnCgZD04Kj6uwEcItgSRfpvViekhbeiBuKoGicYjM2w7rBoKqjmS0000" loading="lazy" /></div></div></section>
<section><h2>用例图示例<a href="#用例图示例"><span>#</span></a></h2><div><div><img alt="@startuml
left to right direction
actor 游客
actor 用户
actor 管理员

rectangle 博客系统 {
	usecase &quot;浏览文章&quot; as UC1
	usecase &quot;搜索内容&quot; as UC2
	usecase &quot;发表评论&quot; as UC3
	usecase &quot;点赞收藏&quot; as UC4
	usecase &quot;审核评论&quot; as UC5
	usec" src="https://www.plantuml.com/plantuml/svg/PP3BJi9058RNdASuOSz2Raj3uZFm09CoOfFI9krqPTm88XjJI8WQGv1s8ud9i02qoFFCvJ6CCf8EBh_pvJyvvsz5Z8GixddWqKk6BC3GxLunxBWXTPWR-40S5eGeyrpmp82QBkNeymzugiP3CNa6-8aGl-jH50ylWcTglLVxLxo6KXnHXqGKotARwBUHVBnNxuioaWZR5sT5FPwfJIQ6TuBlZAuMj4WcEbtgrK3pBwDh1QrkTtexbzCF_PGONI-cUIeNkPLksCj5thTEQy8DmE5rh5JEVu-ri0g7BexMmXDRix4En-hCfF5lqeGMzJjnp_i6" loading="lazy" /></div></div></section>
<section><h2>组件图示例<a href="#组件图示例"><span>#</span></a></h2><div><div><img alt="@startuml
package &quot;Firefly Site&quot; {
	[Astro App] as App
	[Markdown Parser] as Parser
	[PlantUML Encoder] as Encoder
	[Theme Switcher] as Theme
	[Search Indexer] as Search
}

cloud &quot;PlantUML Server&quot; as " src="https://www.plantuml.com/plantuml/svg/JL51QiCm43nxNp7u7dht8IIK5WeD15oVJ0-oj8sDPKcLvAIbzEz5YX9wqZ2pxCoiTY4A7zT5anDo5YT6_Jnv_j3VQAV8DNwewlSXUekzS-yG8RrKzGVXPsKl1aVX0_kiN25L_L4B4xl3AvwCjAge1LFLlusyCDhB5ELOn4nHrRSil1pnOXH_5UbAqI-Hr7PLgE_RM_PdzdMsxaY9A0OH6FMZDP5DH1kjvomNXcZl73QRROcA1Yu1BAKD5JwDbBnem1d19TzrqHYqbNEWsq3Urw61vy-LGqGud-v6rwuDGcuB9VpyeAVJ616yl4-LsWs6TT8ABdt4b6xmFz2jLZ8I2iw666Z7HgsB_WC0" loading="lazy" /></div></div></section>
<section><h2>部署图示例<a href="#部署图示例"><span>#</span></a></h2><div><div><img alt="@startuml
node &quot;User Device&quot; {
	artifact &quot;Browser&quot;
}

node &quot;CDN / Edge&quot; {
	artifact &quot;Static Assets&quot;
}

node &quot;Cloudflare Worker&quot; {
	artifact &quot;SSR Handler&quot;
}

node &quot;PlantUML Service&quot; {
	artifact &quot;SVG Re" src="https://www.plantuml.com/plantuml/svg/ROz1Jy9044RFxg_uqZlfdOD1WS11r53Hyz2TraAxYpDJE1Z_kqb3J6azxtkpxvkfaLZRr2v4pqXsoe85dwkS4toxEnAh2ieDoQF4Yx8axiTTsVdY6IcMlXoYcP5LEHvKsRGlrB7rHKt2-8XoP1bwsHPh2hw--UQrfc2xpHCobd-wilSLjXmyozNoPBGdPIGl-mFdXioYq2XoGtBqyH8mZy4uMEV-hSHaSZySWYbMopSSDCrLqwefHtYl7rC8VxMiXXELx6uVEtnKq3daSOfgezlzcPYYOCi_uIigXHhekNGp3hvjwby0" loading="lazy" /></div></div></section>
<section><h2>ER 图示例<a href="#er-图示例"><span>#</span></a></h2><div><div><img alt="@startuml
entity User {
	*id : uuid &lt;&lt;PK&gt;&gt;
	--
	username : varchar
	email : varchar
	created_at : datetime
}

entity Post {
	*id : uuid &lt;&lt;PK&gt;&gt;
	--
	author_id : uuid &lt;&lt;FK&gt;&gt;
	title : varchar
	content : " src="https://www.plantuml.com/plantuml/svg/VP2x2W9134NhoLUa5lO71X51iB6niPRiJc07vY6JZ0_KVvTTKHGVRKwIavkvA1MjCG0dzNh2ZN31CvY9TsYnLkzmEbslPZCmJGEc2fT4aT7YdahNKm73aNnuBNI5ITbjIT6Y8sNraU4A3ySwY_vnKDK-b-qhN8vGlOOtSvUJSXeioaS5iwjjyDB_KIzoZCF4R_ikYtvn3y6_bDliJa_zpzpZKo-NfiddUtYBX-ALvOqyRhDuNoGmzdxGdWJcd5oDuGO0" loading="lazy" /></div></div></section>
<section><h2>时序图示例（登录与刷新令牌）<a href="#时序图示例登录与刷新令牌"><span>#</span></a></h2><div><div><img alt="@startuml
autonumber
actor User as 用户
participant Web as 前端页面
participant API as 网关接口
participant Auth as 认证服务
database Redis as 会话缓存

用户 -&gt; 前端页面 : 输入账号密码并提交
前端页面 -&gt; 网关接口 : POST /login
网关接口 -&gt; 认证服务 : " src="https://www.plantuml.com/plantuml/svg/bP3BJi9068NtynJ_thGIbrqqk7GbyHANPYYZ4A4bxVG1M70D5GrAn42u2CPee2HUY0BwCioql8L1t0ma1jVVEJFVVu8MnIQr4t64RMheTY9CJ8GrQfXmQ14Ji0NUrGFF3L4IcpIcnP9Oft14mdF2yextvCvQhxDwM-3RePsVvkIIfP_vUPULxaHkq-WyuFSxlflYDOSLMYY2AGvZYy0UYSIiEPwERtstxetBh7U3qC8395Nu5nJmFyiitVLVxbbfoDoCrqonzpTUkfX-T90GbLH12HG8xUuVm6RSE8tfI42IAiY10hpPcZqMMRR7dJuIcAGAfg00orHPkWicEJ695JscnXdHbpghBbZJY6Kjmh2nL1RbLZVO2WJ0guxOv9hdBbYXWP0io-AJ_bUMrneWop9QRvPVW_M6-VFObO7yIPyxxNyEn2i3uQArHk6LmNJKyV9552Hwn4x4lm40" loading="lazy" /></div></div></section>
<section><h2>C4 风格容器图示例<a href="#c4-风格容器图示例"><span>#</span></a></h2><div><div><img alt="@startuml
!includeurl https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml

Person(user, &quot;博客访客&quot;, &quot;阅读文章与搜索内容&quot;)

System_Boundary(system, &quot;Firefly Blog&quot;) {
	Container(we" src="https://www.plantuml.com/plantuml/svg/PL7BJjj055tNNp7robP3l67L5I7b9GKHnQ6egyYE9udBn8x6Oq9KLQeeeLMbGDHIYWfL6fTAIAXs7w2cGOAVoTZENrHZfsaYTZFtddlkkUVCMKJ1nAuYxhvk596jGHiZK26aPZsK9ApKasMTL6pLjY0kcWQ11aaMpQfKGuh1ncOieY5TbTApCrbMMb_DI5N58X1BwTb2sZI8eXiG9sji1PU5s38DWP4b04zR7wdhXEujTHq-0VZXSJFqhlsZLy75vq5lts-V1fSEtMjIzmylSfpSi0Yi5kPDsz0Kt12iw9y0_AAEOGarm3mooxm8dd7tndk5EbGJWD-0AaZLQcn9oYBO10-0l0KHWQpYztxwdNV3xjNmapFexG_wPuF-MrwSOZ7nPYHPbdDW8_gmoJGoRQs450n7HOjLwTbkqDxpJrlqJJUuy6BwqFlj_zYPOdsa2YD76LNy0Z8nSQHgLS6RcbatW0JMrAUm6BUKSjIatuxfpldOgV1gr--tQVEb__wxttAdjLjGmSKAqnuzm8gXmMr6abNAiAGRMkJ1GNjmSqARZDF_yYAuT7YHUpxsV66R2FyIPvEZf84CyLPiHN3OyL-tWVnu2T2Jcx1xpXBBGJJAEeuXT6-77rm6Nyxdip8lHeYeDtOuDWeme-D8xi17oF_UXTurFJYAtPZa75_kTxw6daERluQ7RkYr9Z4JLyMRO_Ni47P19lLaRJrVo2mivWkvbQNbl21oSz3Gx2hw2m00" loading="lazy" /></div></div></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/markdown-tutorial/</id>
      <title type="text">Markdown 教程</title>
      <published>1970-01-01T00:00:00.000Z</published>
      <updated>1970-01-01T00:00:00.000Z</updated>
      <author><name>emn178</name></author>
      <link rel="alternate" href="https://clina.top/posts/markdown-tutorial/"/>
      <summary type="text">一个简明的 Markdown 博客示例。</summary>
      <content type="html"><![CDATA[<p>这是一个展示如何编写 Markdown 文件的示例。本文档汇总了核心语法与常见扩展（GFM）。</p>
<ul>
<li><a href="#block-elements">块级元素</a>
<ul>
<li><a href="#paragraphs-and-line-breaks">段落与换行</a></li>
<li><a href="#headers">标题</a></li>
<li><a href="#blockquotes">引用</a></li>
<li><a href="#lists">列表</a></li>
<li><a href="#code-blocks">代码块</a></li>
<li><a href="#horizontal-rules">分割线</a></li>
<li><a href="#table">表格</a></li>
</ul>
</li>
<li><a href="#span-elements">内联元素</a>
<ul>
<li><a href="#links">链接</a></li>
<li><a href="#emphasis">强调</a></li>
<li><a href="#code">行内代码</a></li>
<li><a href="#images">图片</a></li>
<li><a href="#strikethrough">删除线</a></li>
</ul>
</li>
<li><a href="#miscellaneous">杂项</a>
<ul>
<li><a href="#automatic-links">自动链接</a></li>
<li><a href="#backslash-escapes">反斜杠转义</a></li>
</ul>
</li>
<li><a href="#inline-html">内联 HTML</a></li>
</ul>
<p><a></a></p>
<section><h2>块级元素<a href="#块级元素"><span>#</span></a></h2><p><a></a></p><section><h3>段落与换行<a href="#段落与换行"><span>#</span></a></h3><section><h4>段落<a href="#段落"><span>#</span></a></h4><p>HTML 标签：<code>&lt;p&gt;</code></p><p>使用一个或多个空行分隔段落。（仅包含<strong>空格</strong>或<strong>制表符</strong>的行也视为空行。）</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>This will be</span></div></div><div><div><div>2</div></div><div><span>inline.</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>This is second paragraph.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p>This will be
inline.</p><p>This is second paragraph.</p><hr /></section><section><h4>换行<a href="#换行"><span>#</span></a></h4><p>HTML 标签：<code>&lt;br /&gt;</code></p><p>在行末添加<strong>两个或更多空格</strong>来产生换行。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>This will be not</span></div></div><div><div><div>2</div></div><div><span>inline.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p>This will be not<br />
inline.</p><hr /><p><a></a></p></section></section><section><h3>标题<a href="#标题"><span>#</span></a></h3><p>Markdown 支持两种标题样式：Setext 与 atx。</p><section><h4>Setext<a href="#setext"><span>#</span></a></h4><p>HTML 标签：<code>&lt;h1&gt;</code>，<code>&lt;h2&gt;</code></p><p>使用<strong>等号 (=)</strong> 表示 <code>&lt;h1&gt;</code>、使用<strong>短横线 (-)</strong> 表示 <code>&lt;h2&gt;</code>，数量不限，作为“下划线”。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>This is an H1</span></div></div><div><div><div>2</div></div><div><span>=============</span></div></div><div><div><div>3</div></div><div><span>This is an H2</span></div></div><div><div><div>4</div></div><div><span>-------------</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /></section></section></section>
<section><h1>This is an H1<a href="#this-is-an-h1"><span>#</span></a></h1><section><h2>This is an H2<a href="#this-is-an-h2"><span>#</span></a></h2><hr /><section><h4>atx<a href="#atx"><span>#</span></a></h4><p>HTML 标签：<code>&lt;h1&gt;</code>，<code>&lt;h2&gt;</code>，<code>&lt;h3&gt;</code>，<code>&lt;h4&gt;</code>，<code>&lt;h5&gt;</code>，<code>&lt;h6&gt;</code></p><p>在行首使用 1-6 个<strong>井号 (#)</strong>，对应 <code>&lt;h1&gt;</code> 至 <code>&lt;h6&gt;</code>。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># This is an H1</span></div></div><div><div><div>2</div></div><div><span>## This is an H2</span></div></div><div><div><div>3</div></div><div><span>###### This is an H6</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /></section></section></section>
<section><h1>This is an H1<a href="#this-is-an-h1-1"><span>#</span></a></h1><section><h2>This is an H2<a href="#this-is-an-h2-1"><span>#</span></a></h2><section><h6>This is an H6<a href="#this-is-an-h6"><span>#</span></a></h6><hr /><p>可选：你可以在行尾“闭合” atx 标题。末尾的井号数量<strong>不必与</strong>开头一致。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># This is an H1 #</span></div></div><div><div><div>2</div></div><div><span>## This is an H2 ##</span></div></div><div><div><div>3</div></div><div><span>### This is an H3 ######</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /></section></section></section>
<section><h1>This is an H1<a href="#this-is-an-h1-2"><span>#</span></a></h1><section><h2>This is an H2<a href="#this-is-an-h2-2"><span>#</span></a></h2><section><h3>This is an H3<a href="#this-is-an-h3"><span>#</span></a></h3><hr /><p><a></a></p></section><section><h3>引用<a href="#引用"><span>#</span></a></h3><p>HTML 标签：<code>&lt;blockquote&gt;</code></p><p>Markdown 使用邮件风格的 <strong>&gt;</strong> 作为引用符号。若手动换行并在每行前加 &gt;，显示效果最佳。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&gt; This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,</span></div></div><div><div><div>2</div></div><div><span>&gt; consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.</span></div></div><div><div><div>3</div></div><div><span>&gt; Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.</span></div></div><div><div><div>4</div></div><div><span>&gt;</span></div></div><div><div><div>5</div></div><div><span>&gt; Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse</span></div></div><div><div><div>6</div></div><div><span>&gt; id sem consectetuer libero luctus adipiscing.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><blockquote><p>This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.</p><p>Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
id sem consectetuer libero luctus adipiscing.</p></blockquote><hr /><p>Markdown 允许“偷懒”：在一个硬换行段落中，只在第一行前加 &gt; 即可。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&gt; This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,</span></div></div><div><div><div>2</div></div><div><span>consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.</span></div></div><div><div><div>3</div></div><div><span>Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>&gt; Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse</span></div></div><div><div><div>6</div></div><div><span>id sem consectetuer libero luctus adipiscing.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><blockquote><p>This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.</p></blockquote><blockquote><p>Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
id sem consectetuer libero luctus adipiscing.</p></blockquote><hr /><p>引用可以嵌套（引用中的引用），通过增加 &gt; 层级实现。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&gt; This is the first level of quoting.</span></div></div><div><div><div>2</div></div><div><span>&gt;</span></div></div><div><div><div>3</div></div><div><span>&gt; &gt; This is nested blockquote.</span></div></div><div><div><div>4</div></div><div><span>&gt;</span></div></div><div><div><div>5</div></div><div><span>&gt; Back to the first level.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><blockquote><p>This is the first level of quoting.</p><blockquote><p>This is nested blockquote.</p></blockquote><p>Back to the first level.</p></blockquote><hr /><p>引用内可包含其他 Markdown 元素，包括标题、列表与代码块。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&gt; ## This is a header.</span></div></div><div><div><div>2</div></div><div><span>&gt;</span></div></div><div><div><div>3</div></div><div><span>&gt; 1.   This is the first list item.</span></div></div><div><div><div>4</div></div><div><span>&gt; 2.   This is the second list item.</span></div></div><div><div><div>5</div></div><div><span>&gt;</span></div></div><div><div><div>6</div></div><div><span>&gt; Here's some example code:</span></div></div><div><div><div>7</div></div><div><span>&gt;</span></div></div><div><div><div>8</div></div><div><span>&gt;     return shell_exec("echo $input | $markdown_script");</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><blockquote><h2>This is a header.<a href="#this-is-a-header"><span>#</span></a></h2><ol>
<li>This is the first list item.</li>
<li>This is the second list item.</li>
</ol><p>Here’s some example code:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>return shell_exec("echo $input | $markdown_script");</span></div></div></code></pre><div><div></div><div></div></div></figure></div></blockquote><hr /><p><a></a></p></section><section><h3>列表<a href="#列表"><span>#</span></a></h3><p>Markdown 支持有序（数字）与无序（圆点）列表。</p><section><h4>无序列表<a href="#无序列表"><span>#</span></a></h4><p>HTML 标签：<code>&lt;ul&gt;</code></p><p>无序列表可使用 <strong>星号 (*)</strong>、<strong>加号 (+)</strong> 或 <strong>短横线 (-)</strong>。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>*   Red</span></div></div><div><div><div>2</div></div><div><span>*   Green</span></div></div><div><div><div>3</div></div><div><span>*   Blue</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><ul>
<li>Red</li>
<li>Green</li>
<li>Blue</li>
</ul><hr /><p>等价于：</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>+   Red</span></div></div><div><div><div>2</div></div><div><span>+   Green</span></div></div><div><div><div>3</div></div><div><span>+   Blue</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>或者：</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>-   Red</span></div></div><div><div><div>2</div></div><div><span>-   Green</span></div></div><div><div><div>3</div></div><div><span>-   Blue</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>有序列表<a href="#有序列表"><span>#</span></a></h4><p>HTML 标签：<code>&lt;ol&gt;</code></p><p>有序列表使用数字加英文句点：</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>1.  Bird</span></div></div><div><div><div>2</div></div><div><span>2.  McHale</span></div></div><div><div><div>3</div></div><div><span>3.  Parish</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><ol>
<li>Bird</li>
<li>McHale</li>
<li>Parish</li>
</ol><hr /><p>注意：像下面这样可能会“意外触发”有序列表：</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>1986. What a great season.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><ol>
<li>What a great season.</li>
</ol><hr /><p>你可以用<strong>反斜杠转义 (\)</strong> 句点：</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>1986\. What a great season.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p>1986. What a great season.</p><hr /></section><section><h4>列表中的缩进内容<a href="#列表中的缩进内容"><span>#</span></a></h4><section><h5>列表项里的引用<a href="#列表项里的引用"><span>#</span></a></h5><p>在列表项内放置引用，需要将 &gt; 符号整体缩进：</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>*   A list item with a blockquote:</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&gt; This is a blockquote</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&gt; inside a list item.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><ul>
<li>
<p>A list item with a blockquote:</p>
<blockquote><p>This is a blockquote
inside a list item.</p></blockquote>
</li>
</ul><hr /></section><section><h5>列表项里的代码块<a href="#列表项里的代码块"><span>#</span></a></h5><p>在列表项内放置代码块，需要缩进两层——<strong>8 个空格</strong>或<strong>两个 Tab</strong>：</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>*   A list item with a code block:</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>&lt;code goes here&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><ul>
<li>
<p>A list item with a code block:</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;code goes here&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul><hr /></section><section><h5>嵌套列表<a href="#嵌套列表"><span>#</span></a></h5><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>* A</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>* A1</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>* A2</span></div></div><div><div><div>4</div></div><div><span>* B</span></div></div><div><div><div>5</div></div><div><span>* C</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><ul>
<li>A
<ul>
<li>A1</li>
<li>A2</li>
</ul>
</li>
<li>B</li>
<li>C</li>
</ul><hr /><p><a></a></p></section></section></section><section><h3>代码块<a href="#代码块"><span>#</span></a></h3><p>HTML 标签：<code>&lt;pre&gt;</code></p><p>将代码块中的每行缩进至少<strong>4 个空格</strong>或<strong>1 个制表符</strong>。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>This is a normal paragraph:</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>This is a code block.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p>This is a normal paragraph:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>This is a code block.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /><p>代码块会一直持续，直到遇到未缩进的行（或文末）。</p><p>在代码块内，<strong>与号 (&amp;)</strong> 和尖括号 <strong>(&lt; &gt;)</strong> 会自动转为 HTML 实体。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>&lt;div class="footer"&gt;</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>&amp;copy; 2004 Foo Corporation</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;/div&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;div class="footer"&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&amp;copy; 2004 Foo Corporation</span></div></div><div><div><div>3</div></div><div><span>&lt;/div&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /><p>下文的“围栏代码块”和“语法高亮”属于扩展语法，你也可以用它们来书写代码块。</p><section><h4>围栏代码块<a href="#围栏代码块"><span>#</span></a></h4><p>使用成对的反引号围起来（如下所示），就不需要四空格缩进了。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Here's an example:</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>```</span></div></div><div><div><div>4</div></div><div><span>function test() {</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>console.log("notice the blank line before this function?");</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div><span>```</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p>Here’s an example:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function test() {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>console.log("notice the blank line before this function?");</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section><section><h4>语法高亮<a href="#语法高亮"><span>#</span></a></h4><p>在围栏代码块后添加可选的语言标识，即可启用语法高亮（参见支持语言列表）。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>```ruby</span></div></div><div><div><div>2</div></div><div><span>require 'redcarpet'</span></div></div><div><div><div>3</div></div><div><span>markdown = Redcarpet.new("Hello World!")</span></div></div><div><div><div>4</div></div><div><span>puts markdown.to_html</span></div></div><div><div><div>5</div></div><div><span>```</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>require </span><span>'redcarpet'</span></div></div><div><div><div>2</div></div><div><span>markdown</span><span><span> </span><span>=</span><span> </span></span><span>Redcarpet</span><span>.</span><span>new</span><span>(</span><span>"Hello World!"</span><span>)</span></div></div><div><div><div>3</div></div><div><span>puts</span><span> markdown.</span><span>to_html</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /><p><a></a></p></section></section><section><h3>分割线（水平线）<a href="#分割线水平线"><span>#</span></a></h3><p>HTML 标签：<code>&lt;hr /&gt;</code>
一行中放置<strong>三个或以上的短横线 (-)、星号 (*) 或下划线 (_)</strong>。符号之间允许有空格。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>* * *</span></div></div><div><div><div>2</div></div><div><span>***</span></div></div><div><div><div>3</div></div><div><span>*****</span></div></div><div><div><div>4</div></div><div><span>- - -</span></div></div><div><div><div>5</div></div><div><span>---------------------------------------</span></div></div><div><div><div>6</div></div><div><span>___</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><hr /><hr /><hr /><hr /><hr /><hr /><hr /><p><a></a></p></section><section><h3>表格<a href="#表格"><span>#</span></a></h3><p>HTML 标签：<code>&lt;table&gt;</code></p><p>这是扩展语法。</p><p>用<strong>竖线 (|)</strong> 分隔列，用<strong>短横线 (-)</strong> 分隔表头，使用<strong>冒号 (:)</strong> 指定对齐方式。</p><p>两侧的<strong>竖线 (|)</strong> 与对齐可选。用于表头分隔时，每列至少需要 <strong>3 个短横线</strong>。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>| Left | Center | Right |</span></div></div><div><div><div>2</div></div><div><span>|:-----|:------:|------:|</span></div></div><div><div><div>3</div></div><div><span>|aaa   |bbb     |ccc    |</span></div></div><div><div><div>4</div></div><div><span>|ddd   |eee     |fff    |</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span> </span></span><span>A | B</span></div></div><div><div><div>7</div></div><div><span>---|---</span></div></div><div><div><div>8</div></div><div><span>123|456</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>A |B</span></div></div><div><div><div>12</div></div><div><span>--|--</span></div></div><div><div><div>13</div></div><div><span>12|45</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr />

<table><thead><tr><th>Left</th><th>Center</th><th>Right</th></tr></thead><tbody><tr><td>aaa</td><td>bbb</td><td>ccc</td></tr><tr><td>ddd</td><td>eee</td><td>fff</td></tr></tbody></table>

<table><thead><tr><th>A</th><th>B</th></tr></thead><tbody><tr><td>123</td><td>456</td></tr></tbody></table>

<table><thead><tr><th>A</th><th>B</th></tr></thead><tbody><tr><td>12</td><td>45</td></tr></tbody></table><hr /><p><a></a></p></section></section><section><h2>内联元素<a href="#内联元素"><span>#</span></a></h2><p><a></a></p><section><h3>链接<a href="#链接"><span>#</span></a></h3><p>HTML 标签：<code>&lt;a&gt;</code></p><p>Markdown 支持两种链接样式：行内链接与引用式链接。</p><section><h4>行内链接<a href="#行内链接"><span>#</span></a></h4><p>行内链接格式：<code>[文本](URL "标题")</code></p><p>标题可选。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>This is [an example](http://example.com/ "Title") inline link.</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>[This link](http://example.net/) has no title attribute.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p>This is <a href="http://example.com/" target="_blank">an example</a> inline link.</p><p><a href="http://example.net/" target="_blank">This link</a> has no title attribute.</p><hr /><p>如果引用同一站点的本地资源，可以使用相对路径：</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>See my [About](/about/) page for details.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p>See my <a href="/about/">About</a> page for details.</p><hr /></section><section><h4>引用式链接<a href="#引用式链接"><span>#</span></a></h4><p>可以预定义链接引用。定义格式：<code>[id]: URL "标题"</code></p><p>标题同样可选。引用时使用：<code>[文本][id]</code></p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[id]: http://example.com/  "Optional Title Here"</span></div></div><div><div><div>2</div></div><div><span>This is [an example][id] reference-style link.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p>This is <a href="http://example.com/" target="_blank">an example</a> reference-style link.</p><hr /><p>说明：</p><ul>
<li>方括号中包含链接标识（<strong>不区分大小写</strong>，可在左侧缩进最多三格空格）；</li>
<li>随后是冒号；</li>
<li>再跟一个或多个空格（或 tab）；</li>
<li>然后是链接 URL；</li>
<li>URL 可选地用尖括号包裹；</li>
<li>可选地跟随标题属性，用引号或圆括号包裹。</li>
</ul><p>以下三种定义等价：</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[foo]: http://example.com/  "Optional Title Here"</span></div></div><div><div><div>2</div></div><div><span>[foo]: http://example.com/  'Optional Title Here'</span></div></div><div><div><div>3</div></div><div><span>[foo]: http://example.com/  (Optional Title Here)</span></div></div><div><div><div>4</div></div><div><span>[foo]: &lt;http://example.com/&gt;  "Optional Title Here"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果使用空的方括号，则链接文本本身会作为名称。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[Google]: http://google.com/</span></div></div><div><div><div>2</div></div><div><span>[Google][]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p><a href="http://google.com/" target="_blank">Google</a></p><hr /><p><a></a></p></section></section><section><h3>强调<a href="#强调"><span>#</span></a></h3><p>HTML 标签：<code>&lt;em&gt;</code>，<code>&lt;strong&gt;</code></p><p>Markdown 使用 <strong>星号 (*)</strong> 或 <strong>下划线 (_)</strong> 表示强调。<strong>一个分隔符</strong>对应 <code>&lt;em&gt;</code>；<strong>两个分隔符</strong>对应 <code>&lt;strong&gt;</code>。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>*single asterisks*</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>_single underscores_</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>**double asterisks**</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>__double underscores__</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p><em>single asterisks</em></p><p><em>single underscores</em></p><p><strong>double asterisks</strong></p><p><strong>double underscores</strong></p><hr /><p>但如果两侧有空格，则会被视作普通字符而非强调语法。</p><p>你可以使用反斜杠进行转义：</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>\*this text is surrounded by literal asterisks\*</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p>*this text is surrounded by literal asterisks*</p><hr /><p><a></a></p></section><section><h3>行内代码<a href="#行内代码"><span>#</span></a></h3><p>HTML 标签：<code>&lt;code&gt;</code></p><p>用<strong>反引号 (`)</strong> 包裹。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Use the `printf()` function.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p>Use the <code>printf()</code> function.</p><hr /><p>若行内代码中需要包含反引号字符，可使用<strong>多重反引号</strong>作为定界符：</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>``There is a literal backtick (`) here.``</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p><code>There is a literal backtick (`) here.</code></p><hr /><p>行内代码两侧的定界符允许包含空格（开头一个、结尾一个），方便在代码起始或结尾放置反引号字符：</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>A single backtick in a code span: `` ` ``</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>A backtick-delimited string in a code span: `` `foo` ``</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p>A single backtick in a code span: <code>`</code></p><p>A backtick-delimited string in a code span: <code>`foo`</code></p><hr /><p><a></a></p></section><section><h3>图片<a href="#图片"><span>#</span></a></h3><p>HTML 标签：<code>&lt;img /&gt;</code></p><p>Markdown 的图片语法与链接类似，支持行内与引用两种方式。</p><section><h4>行内图片<a href="#行内图片"><span>#</span></a></h4><p>行内图片语法：<code>![替代文本](URL "标题")</code></p><p>标题可选。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>![Alt text](/path/to/img.jpg)</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>![Alt text](/path/to/img.jpg "Optional title")</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p></p><figure><img src="https://s2.loli.net/2024/08/20/5fszgXeOxmL3Wdv.webp" alt="Alt text" /><figcaption>Alt text</figcaption></figure><p></p><p></p><figure><img src="https://s2.loli.net/2024/08/20/5fszgXeOxmL3Wdv.webp" alt="Alt text" title="Optional title" /><figcaption>Alt text</figcaption></figure><p></p><hr /><p>说明：</p><ul>
<li>一个感叹号 !；</li>
<li>后接方括号，放置图片的替代文本；</li>
<li>再接圆括号，内含图片 URL/路径，及可选的标题（引号包裹）。</li>
</ul></section><section><h4>引用式图片<a href="#引用式图片"><span>#</span></a></h4><p>引用式图片语法：<code>![替代文本][id]</code></p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[img id]: https://s2.loli.net/2024/08/20/5fszgXeOxmL3Wdv.webp  "Optional title attribute"</span></div></div><div><div><div>2</div></div><div><span>![Alt text][img id]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p></p><figure><img src="https://s2.loli.net/2024/08/20/5fszgXeOxmL3Wdv.webp" alt="Alt text" title="Optional title attribute" /><figcaption>Alt text</figcaption></figure><p></p><hr /><p><a></a></p></section></section><section><h3>删除线<a href="#删除线"><span>#</span></a></h3><p>HTML 标签：<code>&lt;del&gt;</code></p><p>这是扩展语法。</p><p>GFM 增加了删除线语法。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>~~Mistaken text.~~</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p>Mistaken text.</p><hr /><p><a></a></p></section></section><section><h2>杂项<a href="#杂项"><span>#</span></a></h2><p><a></a></p><section><h3>自动链接<a href="#自动链接"><span>#</span></a></h3><p>Markdown 支持一种便捷写法来创建“自动链接”（URL 与邮箱地址）：只需用尖括号将其包住即可。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;http://example.com/&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>&lt;address@example.com&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p><a href="http://example.com/" target="_blank">http://example.com/</a></p><p><a href="#">address@example.com</a></p><hr /><p>GFM 会自动识别标准 URL 并转换为链接。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>https://github.com/emn178/markdown</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p><a href="https://github.com/emn178/markdown" target="_blank">https://github.com/emn178/markdown</a></p><hr /><p><a></a></p></section><section><h3>反斜杠转义<a href="#反斜杠转义"><span>#</span></a></h3><p>Markdown 允许使用反斜杠来转义那些本用于 Markdown 语法的特殊字符，使其按字面显示。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>\*literal asterisks\*</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p>*literal asterisks*</p><hr /><p>以下字符可通过反斜杠转义以按字面量输出：</p><p>Code:</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>\   backslash</span></div></div><div><div><div>2</div></div><div><span>`   backtick</span></div></div><div><div><div>3</div></div><div><span>*   asterisk</span></div></div><div><div><div>4</div></div><div><span>_   underscore</span></div></div><div><div><div>5</div></div><div><span>{}  curly braces</span></div></div><div><div><div>6</div></div><div><span>[]  square brackets</span></div></div><div><div><div>7</div></div><div><span>()  parentheses</span></div></div><div><div><div>8</div></div><div><span>#   hash mark</span></div></div><div><div><div>9</div></div><div><span>+   plus sign</span></div></div><div><div><div>10</div></div><div><span>-   minus sign (hyphen)</span></div></div><div><div><div>11</div></div><div><span>.   dot</span></div></div><div><div><div>12</div></div><div><span>!   exclamation mark</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><a></a></p></section></section><section><h2>内联 HTML<a href="#内联-html"><span>#</span></a></h2><p>对于 Markdown 语法未覆盖的标记，直接使用原生 HTML 即可。无需特别声明从 Markdown 切换到 HTML，直接写标签就行。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>This is a regular paragraph.</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>&lt;table&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;tr&gt;</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>&lt;td&gt;Foo&lt;/td&gt;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>&lt;/tr&gt;</span></div></div><div><div><div>7</div></div><div><span>&lt;/table&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>This is another regular paragraph.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p>This is a regular paragraph.</p><table>
    <tbody><tr>
        <td>Foo</td>
    </tr>
</tbody></table><p>This is another regular paragraph.</p><hr /><p>请注意：在<strong>块级 HTML 标签</strong>内不会处理 Markdown 语法。</p><p>与块级标签不同，在<strong>行内级标签</strong>内会处理 Markdown 语法。</p><p>代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;span&gt;**Work**&lt;/span&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>&lt;div&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>**No Work**</span></div></div><div><div><div>5</div></div><div><span>&lt;/div&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>预览：</p><hr /><p><span><strong>Work</strong></span></p><div>
  **No Work**
</div>
***</section></section>]]></content>
    </entry>
    <entry>
      <id>https://clina.top/posts/video/</id>
      <title type="text">在文章中嵌入视频</title>
      <published>1970-01-01T00:00:00.000Z</published>
      <updated>1970-01-01T00:00:00.000Z</updated>
      <author><name>ClinaSaMa</name></author>
      <link rel="alternate" href="https://clina.top/posts/video/"/>
      <summary type="text">这篇文章演示如何在博客文章中嵌入视频。</summary>
      <content type="html"><![CDATA[<p>只需从 YouTube 或其他平台复制嵌入代码，然后将其粘贴到 markdown 文件中。</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>---</span></div></div><div><div><div>2</div></div><div><span>title</span><span>: </span><span>在文章中嵌入视频</span></div></div><div><div><div>3</div></div><div><span>published</span><span>: </span><span>2023-10-19</span></div></div><div><div><div>4</div></div><div><span>// ...</span></div></div><div><div><div>5</div></div><div><span>---</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&lt;iframe width="100%" height="468" src="https://www.youtube.com/embed/5gIf0_xpFPI?si=N1WTorLKL0uwLsU_" title="YouTube video player" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<section><h2>YouTube<a href="#youtube"><span>#</span></a></h2></section>
<section><h2>Bilibili<a href="#bilibili"><span>#</span></a></h2> </section>]]></content>
    </entry>
</feed>
