<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>折腾 — mizore / notes</title>
    <link>https://mizore.blog/categories/折腾/</link>
    <link xmlns="http://www.w3.org/2005/Atom" href="https://mizore.blog/categories/折腾/feed.xml" rel="self" type="application/rss+xml"></link>
    <description>3 篇</description>
    <language>zh-CN</language>
    <lastBuildDate>Mon, 22 Jun 2026 00:00:00 +0000</lastBuildDate>
    <item>
      <title>博客新功能：语言图标、源码引用、行情与图表</title>
      <link>https://mizore.blog/posts/blog-git-metadata-and-icons/</link>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;这篇写于旧博客（Next.js + MDX）时期。迁移到现在的静态站后，这些功能都在构建时完成：TradingView 变成行情卡片，点「实时图表」才加载实时行情；ECharts 变成静态 SVG 图表，悬停显示数值，源码引用是带行号的固定 commit 片段，语言图标换成了代码块角落的语言名。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;这次主要把博客的写作体验继续往前推了一步：代码块更容易扫读，代码引用能直接指向 GitHub，金融类内容可以用很短的语法插入 TradingView 图表。&lt;/p&gt;&#xA;&lt;h2 id=&#34;先看新功能&#34;&gt;先看新功能&lt;/h2&gt;&#xA;&lt;h3 id=&#34;代码块有语言图标&#34;&gt;代码块有语言图标&lt;/h3&gt;&#xA;&lt;p&gt;代码块右上角一直浮着语言 logo 和复制按钮，没有标题栏、没有徽章边框，不占额外空间。写了 &lt;code&gt;title&lt;/code&gt; 或者代码来自 GitHub 时，代码上方只会多一行小字路径，GitHub 跳转入口收进右上角的一个小图标里。&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;feature&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;语言图标&amp;#39;&lt;/span&gt;&#xA;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;theme&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;catppuccin&amp;#39;&lt;/span&gt;&#xA;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;feature&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;theme&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;ts&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;inline-code-重新做了主题&#34;&gt;inline code 重新做了主题&lt;/h3&gt;&#xA;&lt;p&gt;正文里的 &lt;code&gt;git log --follow&lt;/code&gt;、&lt;code&gt;$AAPL&lt;/code&gt;、&lt;code&gt;::tv AAPL&lt;/code&gt; 不再像一块突兀的亮色贴片。明亮模式下它更接近 Catppuccin Latte 的纸面色，黑暗模式下则压进 Mocha 的低亮度底色。&lt;/p&gt;&#xA;&lt;h3 id=&#34;直接引用-github-代码&#34;&gt;直接引用 GitHub 代码&lt;/h3&gt;&#xA;&lt;p&gt;写作时输入：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;::github-code repo=&amp;#34;mizorewww/blog&amp;#34; ref=&amp;#34;c067042276d4b4b384c66c0c61fcd4f6716eb599&amp;#34; path=&amp;#34;contentlayer.config.ts&amp;#34; lines=&amp;#34;251-253&amp;#34; lang=&amp;#34;ts&amp;#34; title=&amp;#34;contentlayer.config.ts&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;md&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;渲染出来就是带 Shiki 高亮、语言图标和 GitHub 链接的代码块：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;contentlayer.config.ts&lt;/b&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/blog/blob/c067042276d4b4b384c66c0c61fcd4f6716eb599/contentlayer.config.ts#L251-L253&#34;&gt;L251–253 · c067042 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;251&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;githubEmbedPattern&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;sr&#34;&gt;/^::github-(code|diff)\s+(.+)$/&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;252&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;tradingViewMiniPattern&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;sr&#34;&gt;/^\$([A-Za-z0-9._:-]+)$/&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;253&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;tradingViewAdvancedPattern&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;sr&#34;&gt;/^::(?:tv|tv-advanced|tradingview)\s+(.+)$/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;diff-像-github-一样分行染色&#34;&gt;diff 像 GitHub 一样分行染色&lt;/h3&gt;&#xA;&lt;p&gt;写作时输入：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;::github-diff repo=&amp;#34;mizorewww/blog&amp;#34; ref=&amp;#34;c067042276d4b4b384c66c0c61fcd4f6716eb599&amp;#34; path=&amp;#34;css/prism.css&amp;#34; lines=&amp;#34;1-15&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;md&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;渲染效果：&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/mizorewww/blog/commit/c067042276d4b4b384c66c0c61fcd4f6716eb599&#34;&gt;&lt;code&gt;css/prism.css&lt;/code&gt; 在 c067042 中的改动&lt;/a&gt;&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;gu&#34;&gt;@@ -8,6 +8,44 @@ figure[data-rehype-pretty-code-figure] [data-rehype-pretty-code-title] {&lt;/span&gt;&#xA;   @apply flex items-center justify-between gap-3 border-b border-slate-200 bg-white/70 px-4 py-2 text-xs text-slate-600 dark:border-[#405064] dark:bg-white/[0.035] dark:text-white/65;&#xA; }&#xA; &#xA;&lt;span class=&#34;gi&#34;&gt;+figure[data-rehype-pretty-code-figure] .code-title-main {&lt;/span&gt;&#xA;&lt;span class=&#34;gi&#34;&gt;+  @apply inline-flex min-w-0 items-center gap-2;&lt;/span&gt;&#xA;&lt;span class=&#34;gi&#34;&gt;+}&lt;/span&gt;&#xA;&lt;span class=&#34;gi&#34;&gt;+&lt;/span&gt;&#xA;&lt;span class=&#34;gi&#34;&gt;+figure[data-rehype-pretty-code-figure] .code-language-icon {&lt;/span&gt;&#xA;&lt;span class=&#34;gi&#34;&gt;+  @apply inline-flex h-5 min-w-5 shrink-0 items-center justify-center rounded-[5px] border border-slate-300 bg-slate-100 px-1 font-sans text-[0.62rem] leading-none font-semibold text-slate-600 dark:border-white/15 dark:bg-white/10 dark:text-white/70;&lt;/span&gt;&#xA;&lt;span class=&#34;gi&#34;&gt;+}&lt;/span&gt;&#xA;&lt;span class=&#34;gi&#34;&gt;+&lt;/span&gt;&#xA;&lt;span class=&#34;gi&#34;&gt;+figure[data-rehype-pretty-code-figure] .code-language-icon[data-code-language=&amp;#39;ts&amp;#39;],&lt;/span&gt;&#xA;&lt;span class=&#34;gi&#34;&gt;+figure[data-rehype-pretty-code-figure] .code-language-icon[data-code-language=&amp;#39;tsx&amp;#39;] {&lt;/span&gt;&#xA;&lt;span class=&#34;gi&#34;&gt;+  @apply border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-400/25 dark:bg-sky-400/10 dark:text-sky-200;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;diff&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;单行-ticker-自动变成-mini-chart&#34;&gt;单行 ticker 自动变成 Mini Chart&lt;/h3&gt;&#xA;&lt;p&gt;写作时单独一行输入：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;$AAPL&#xA;$BINANCE:BTCUSDT.P&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;md&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;旧站上这两行会各变成一张 Mini Chart。现在的静态站不加载 TradingView 脚本，改成一张链到 TradingView 的行情卡片：&lt;/p&gt;&#xA;&lt;div class=&#34;ticker&#34; data-symbol=&#34;NASDAQ:AAPL&#34; data-interval=&#34;&#34; data-height=&#34;&#34;&gt;&lt;span&gt;&lt;b&gt;NASDAQ:AAPL&lt;/b&gt;&lt;span class=&#34;ticker-label&#34;&gt;苹果公司&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;ticker-go&#34;&gt;&lt;button type=&#34;button&#34;&gt;实时图表&lt;/button&gt;&lt;a href=&#34;https://www.tradingview.com/symbols/NASDAQ-AAPL/&#34;&gt;TradingView ↗&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xA;&lt;div class=&#34;ticker&#34; data-symbol=&#34;BINANCE:BTCUSDT.P&#34; data-interval=&#34;&#34; data-height=&#34;&#34;&gt;&lt;span&gt;&lt;b&gt;BINANCE:BTCUSDT.P&lt;/b&gt;&lt;span class=&#34;ticker-label&#34;&gt;比特币 / USDT 永续合约&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;ticker-go&#34;&gt;&lt;button type=&#34;button&#34;&gt;实时图表&lt;/button&gt;&lt;a href=&#34;https://www.tradingview.com/symbols/BINANCE-BTCUSDT.P/&#34;&gt;TradingView ↗&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xA;&lt;h3 id=&#34;advanced-chart-也有短语法&#34;&gt;Advanced Chart 也有短语法&lt;/h3&gt;&#xA;&lt;p&gt;写作时输入：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;::tv AAPL interval=60 height=460&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;md&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;旧站上这里是一张可交互的 Advanced Chart，现在同样是一张行情卡片。&lt;/p&gt;&#xA;&lt;h3 id=&#34;代码块直接渲染-echarts&#34;&gt;代码块直接渲染 ECharts&lt;/h3&gt;&#xA;&lt;p&gt;写文章时用 &lt;code&gt;```echarts&lt;/code&gt; 代码块，内容是图表的 option JSON,meta 支持 &lt;code&gt;title&lt;/code&gt; 和 &lt;code&gt;height&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;``&lt;span class=&#34;sb&#34;&gt;`echarts title=&amp;#34;每周访问量&amp;#34; height=320&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;  &amp;#34;tooltip&amp;#34;: { &amp;#34;trigger&amp;#34;: &amp;#34;axis&amp;#34; },&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;  &amp;#34;xAxis&amp;#34;: {&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;    &amp;#34;type&amp;#34;: &amp;#34;category&amp;#34;,&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;    &amp;#34;data&amp;#34;: [&amp;#34;周一&amp;#34;, &amp;#34;周二&amp;#34;, &amp;#34;周三&amp;#34;, &amp;#34;周四&amp;#34;, &amp;#34;周五&amp;#34;, &amp;#34;周六&amp;#34;, &amp;#34;周日&amp;#34;]&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;  },&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;  &amp;#34;yAxis&amp;#34;: { &amp;#34;type&amp;#34;: &amp;#34;value&amp;#34; },&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;  &amp;#34;series&amp;#34;: [&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;    {&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;      &amp;#34;name&amp;#34;: &amp;#34;访问量&amp;#34;,&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;      &amp;#34;type&amp;#34;: &amp;#34;line&amp;#34;,&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;      &amp;#34;smooth&amp;#34;: true,&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;      &amp;#34;areaStyle&amp;#34;: {},&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;      &amp;#34;data&amp;#34;: [120, 200, 150, 80, 70, 110, 130]&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;    }&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;  ]&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;``&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;md&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;旧站上这里是两张 ECharts 图。现在的站点在构建时把同样的数据画成静态 SVG；饼图没有对应的图表类型，写成 &lt;code&gt;data&lt;/code&gt; 表格：&lt;/p&gt;&#xA;&lt;figure class=&#34;chart&#34;&gt;&lt;figcaption&gt;&lt;span&gt;每周访问量&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&#34;plot&#34; data-tip=&#34;{&amp;#34;x&amp;#34;:[7.83,22.79,37.75,52.71,67.67,82.63,97.59],&amp;#34;l&amp;#34;:[&amp;#34;周一&amp;#34;,&amp;#34;周二&amp;#34;,&amp;#34;周三&amp;#34;,&amp;#34;周四&amp;#34;,&amp;#34;周五&amp;#34;,&amp;#34;周六&amp;#34;,&amp;#34;周日&amp;#34;],&amp;#34;s&amp;#34;:[{&amp;#34;n&amp;#34;:&amp;#34;访问量&amp;#34;,&amp;#34;c&amp;#34;:&amp;#34;var(--green)&amp;#34;,&amp;#34;v&amp;#34;:[&amp;#34;120&amp;#34;,&amp;#34;200&amp;#34;,&amp;#34;150&amp;#34;,&amp;#34;80&amp;#34;,&amp;#34;70&amp;#34;,&amp;#34;110&amp;#34;,&amp;#34;130&amp;#34;]}]}&#34; style=&#34;--h:274&#34;&gt;&lt;svg viewBox=&#34;0 0 664 274&#34; preserveAspectRatio=&#34;none&#34; role=&#34;img&#34; aria-label=&#34;每周访问量&#34;&gt;&lt;path d=&#34;M52 190H648M52 130H648M52 70H648M52 10H648&#34; fill=&#34;none&#34; style=&#34;stroke:var(--grid)&#34; vector-effect=&#34;non-scaling-stroke&#34;/&gt;&lt;path d=&#34;M52 106L151.3 10L250.7 70L350 154L449.3 166L548.7 118L648 94L648 250L52 250Z&#34; fill-opacity=&#34;0.16&#34; style=&#34;fill:var(--green)&#34; vector-effect=&#34;non-scaling-stroke&#34;/&gt;&lt;path d=&#34;M52 106L151.3 10L250.7 70L350 154L449.3 166L548.7 118L648 94&#34; stroke-width=&#34;1.25&#34; fill=&#34;none&#34; stroke-linejoin=&#34;round&#34; style=&#34;stroke:var(--green)&#34; vector-effect=&#34;non-scaling-stroke&#34;/&gt;&lt;path d=&#34;M52 250H648&#34; fill=&#34;none&#34; style=&#34;stroke:var(--faint)&#34; vector-effect=&#34;non-scaling-stroke&#34;/&gt;&lt;/svg&gt;&lt;span class=&#34;yl&#34; style=&#34;left:0.000%;top:91.241%;width:6.627%&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;yl&#34; style=&#34;left:0.000%;top:69.343%;width:6.627%&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;yl&#34; style=&#34;left:0.000%;top:47.445%;width:6.627%&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;yl&#34; style=&#34;left:0.000%;top:25.547%;width:6.627%&#34;&gt;150&lt;/span&gt;&lt;span class=&#34;yl&#34; style=&#34;left:0.000%;top:3.650%;width:6.627%&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;xl&#34; style=&#34;left:22.791%;top:96.350%&#34;&gt;周二&lt;/span&gt;&lt;span class=&#34;xl&#34; style=&#34;left:52.711%;top:96.350%&#34;&gt;周四&lt;/span&gt;&lt;span class=&#34;xl&#34; style=&#34;left:82.631%;top:96.350%&#34;&gt;周六&lt;/span&gt;&lt;span class=&#34;dl&#34; style=&#34;left:22.791%;top:3.650%;transform:translate(6px,-50%);color:var(--ink)&#34;&gt;200 · 周二&lt;/span&gt;&lt;/div&gt;&lt;/figure&gt;&#xA;&lt;figure class=&#34;data&#34;&gt;&lt;figcaption&gt;文章分类占比&lt;/figcaption&gt;&lt;div class=&#34;data-scroll&#34; tabindex=&#34;0&#34;&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;分类&lt;/th&gt;&lt;th class=&#34;num&#34;&gt;篇数&lt;/th&gt;&lt;th class=&#34;num&#34;&gt;占比&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;折腾&lt;/td&gt;&lt;td class=&#34;num&#34;&gt;40&lt;/td&gt;&lt;td class=&#34;num bar&#34; style=&#34;--a:0.0%;--b:100.0%&#34;&gt;40%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;开发&lt;/td&gt;&lt;td class=&#34;num&#34;&gt;32&lt;/td&gt;&lt;td class=&#34;num bar&#34; style=&#34;--a:0.0%;--b:80.0%&#34;&gt;32%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;笔记&lt;/td&gt;&lt;td class=&#34;num&#34;&gt;18&lt;/td&gt;&lt;td class=&#34;num bar&#34; style=&#34;--a:0.0%;--b:45.0%&#34;&gt;18%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;其他&lt;/td&gt;&lt;td class=&#34;num&#34;&gt;10&lt;/td&gt;&lt;td class=&#34;num bar&#34; style=&#34;--a:0.0%;--b:25.0%&#34;&gt;10%&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&#xA;&lt;p&gt;JSON 不合法时会回退成普通代码块，不会把构建搞挂。需要函数等 JSON 表达不了的配置时，也可以直接用 &lt;code&gt;&amp;lt;ECharts&amp;gt;&lt;/code&gt; 组件传 JS 对象。图表跟随站点明暗主题，echarts 库按需懒加载，不进首屏包。&lt;/p&gt;&#xA;&lt;h2 id=&#34;代码逻辑&#34;&gt;代码逻辑&lt;/h2&gt;&#xA;&lt;h3 id=&#34;短语法先在-remark-阶段变成组件&#34;&gt;短语法先在 remark 阶段变成组件&lt;/h3&gt;&#xA;&lt;p&gt;这两个正则负责识别行情图表写法。&lt;code&gt;$AAPL&lt;/code&gt; 是 Mini Chart，&lt;code&gt;::tv AAPL&lt;/code&gt; 是 Advanced Chart。&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;contentlayer.config.ts&lt;/b&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/blog/blob/c067042276d4b4b384c66c0c61fcd4f6716eb599/contentlayer.config.ts#L251-L253&#34;&gt;L251–253 · c067042 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;251&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;githubEmbedPattern&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;sr&#34;&gt;/^::github-(code|diff)\s+(.+)$/&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;252&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;tradingViewMiniPattern&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;sr&#34;&gt;/^\$([A-Za-z0-9._:-]+)$/&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;253&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;tradingViewAdvancedPattern&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;sr&#34;&gt;/^::(?:tv|tv-advanced|tradingview)\s+(.+)$/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Mini Chart 的转换很克制：只有整段内容就是一个 ticker 时才会替换，避免正文里随手提到 &lt;code&gt;$AAPL&lt;/code&gt; 就插入一个大图表。&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;contentlayer.config.ts&lt;/b&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/blog/blob/c067042276d4b4b384c66c0c61fcd4f6716eb599/contentlayer.config.ts#L288-L295&#34;&gt;L288–295 · c067042 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;288&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;createTradingViewMiniNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MdastNode&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;289&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;isTradingViewTicker&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;290&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;291&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;292&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;293&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;createMdxFlowNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;TradingViewMiniChart&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;294&lt;/span&gt;    &lt;span class=&#34;kt&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;normalizeTradingViewSymbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;295&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Advanced Chart 多一步解析参数。比如 &lt;code&gt;height=460&lt;/code&gt;、&lt;code&gt;interval=60&lt;/code&gt; 会作为 JSX attribute 传给组件。&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;contentlayer.config.ts&lt;/b&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/blog/blob/c067042276d4b4b384c66c0c61fcd4f6716eb599/contentlayer.config.ts#L298-L313&#34;&gt;L298–313 · c067042 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;298&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;createTradingViewAdvancedNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;source&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MdastNode&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;299&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;rawSymbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attrParts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;trim&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sr&#34;&gt;/\s+/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;300&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;301&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;rawSymbol&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;isTradingViewTicker&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;rawSymbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;302&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;303&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;304&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;305&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parseEmbedAttributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attrParts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;TradingViewAttrs&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;306&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;307&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;createMdxFlowNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;TradingViewAdvancedChart&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;308&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;height&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;attrs.height&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;309&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;interval&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;attrs.interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;310&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;locale&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;attrs.locale&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;311&lt;/span&gt;    &lt;span class=&#34;kt&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;normalizeTradingViewSymbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;rawSymbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;312&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;timezone&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;attrs.timezone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;313&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;最后把插件挂进 Contentlayer 的 remark 链路里。这样短语法发生在 MDX 编译前，浏览器里不会再扫字符串。&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;contentlayer.config.ts&lt;/b&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/blog/blob/c067042276d4b4b384c66c0c61fcd4f6716eb599/contentlayer.config.ts#L1063-L1069&#34;&gt;L1063–1069 · c067042 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;1063&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;makeSource&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;1064&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;contentDirPath&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;1065&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;documentTypes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Blog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Authors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;1066&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;mdx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;1067&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;cwd&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;process.cwd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;1068&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;remarkPlugins&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;remarkGfm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;remarkTradingViewWidgets&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;remarkIconShortcodes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;remarkGitHubEmbeds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;1069&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;rehypePlugins&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;tradingview-只在客户端加载&#34;&gt;TradingView 只在客户端加载&lt;/h3&gt;&#xA;&lt;p&gt;TradingView 官方 widget 需要插入外部 script，所以组件保持为 client component。渲染时先清空容器，再放入 widget 容器和配置 script；主题或参数变化时重新生成。&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;components/TradingViewWidgets.tsx&lt;/b&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/blog/blob/c067042276d4b4b384c66c0c61fcd4f6716eb599/components/TradingViewWidgets.tsx#L83-L101&#34;&gt;L83–101 · c067042 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;83&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;useEffect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;84&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;container&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;containerRef&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;current&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;85&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;86&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;container&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;87&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;88&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;89&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;90&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;container&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;innerHTML&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;lt;div class=&amp;#34;tradingview-widget-container__widget&amp;#34;&amp;gt;&amp;lt;/div&amp;gt;&amp;#39;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;91&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;92&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;script&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;createElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;93&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;94&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;src&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;scriptSrc&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;95&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;JSON&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stringify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;96&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;container&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;appendChild&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;97&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;98&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;99&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;container&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;innerHTML&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;100&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;101&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;scriptSrc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;股票代码会先规范化。&lt;code&gt;AAPL&lt;/code&gt; 默认映射成 &lt;code&gt;NASDAQ:AAPL&lt;/code&gt;，如果你写 &lt;code&gt;NYSE:IBM&lt;/code&gt; 这种完整 symbol，就不会改动。&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;lib/tradingview.ts&lt;/b&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/blog/blob/c067042276d4b4b384c66c0c61fcd4f6716eb599/lib/tradingview.ts#L4-L20&#34;&gt;L4–20 · c067042 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;normalizeTradingViewSymbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;defaultExchange&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;NASDAQ&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;5&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;symbol&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;trim&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sr&#34;&gt;/^\$/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toUpperCase&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;6&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;7&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;8&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;9&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;10&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;11&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;explicitTradingViewSymbolPattern&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;12&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;symbol&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;13&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;14&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;15&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;plainTickerPattern&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;16&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;defaultExchange&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;17&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;18&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;19&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;symbol&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;代码块标题栏统一增强&#34;&gt;代码块标题栏统一增强&lt;/h3&gt;&#xA;&lt;p&gt;代码块语言图标没有在每篇文章里手写，而是在 rehype 阶段统一补。它会先找到 Shiki 生成的 &lt;code&gt;figure&lt;/code&gt;，再保证有一个标题栏。&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;contentlayer.config.ts&lt;/b&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/blog/blob/c067042276d4b4b384c66c0c61fcd4f6716eb599/contentlayer.config.ts#L850-L868&#34;&gt;L850–868 · c067042 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;850&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;enhanceCodeTitle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;node&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;HastNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;851&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;852&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;tagName&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;figure&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;853&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;properties&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;data-rehype-pretty-code-figure&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;undefined&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;854&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;855&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;856&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;857&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;858&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;title&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ensureCodeTitle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;859&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;code&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;findDescendantElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;code&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;860&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sourceUrl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;typeof&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;githubSourceUrl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;string&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;githubSourceUrl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;861&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;language&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;getCodeLanguage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;862&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;titleText&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;getNodeText&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;getLanguageName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;language&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;863&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;864&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;children&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;createCodeTitleNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;titleText&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;language&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;865&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;866&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sourceUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;867&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;children&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;createCodeSourceLinkNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sourceUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;language&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;868&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;标题栏左侧由语言图标和标题文本组成。没有显式 title 时，就回退到语言名，比如 &lt;code&gt;TypeScript&lt;/code&gt;、&lt;code&gt;Diff&lt;/code&gt;、&lt;code&gt;Markdown&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;contentlayer.config.ts&lt;/b&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/blog/blob/c067042276d4b4b384c66c0c61fcd4f6716eb599/contentlayer.config.ts#L787-L799&#34;&gt;L787–799 · c067042 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;787&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;createCodeTitleNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;titleText&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;language&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;HastNode&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;788&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;789&lt;/span&gt;    &lt;span class=&#34;kr&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;element&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;790&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;tagName&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;span&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;791&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;properties&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;className&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;code-title-main&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;792&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;children&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;793&lt;/span&gt;      &lt;span class=&#34;nx&#34;&gt;createCodeLanguageIconNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;language&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;794&lt;/span&gt;      &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;795&lt;/span&gt;        &lt;span class=&#34;kr&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;element&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;796&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;tagName&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;span&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;797&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;properties&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;className&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;code-title-text&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;798&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;children&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[{&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;text&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;titleText&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;getLanguageName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;language&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}],&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;799&lt;/span&gt;      &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;视觉上 logo 直接来自 simple-icons 的品牌图标，按需 tree-shake，不会把首屏 JavaScript 变重；没有品牌图标的语言才回退到简短的文字标注。&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;css/prism.css&lt;/b&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/blog/blob/c067042276d4b4b384c66c0c61fcd4f6716eb599/css/prism.css#L11-L17&#34;&gt;L11–17 · c067042 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;figure&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;data-rehype-pretty-code-figure&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;code-title-main&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;12&lt;/span&gt;  @apply inline-flex min-w-0 items-center gap-2&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;14&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;figure&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;data-rehype-pretty-code-figure&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;code-language-icon&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;16&lt;/span&gt;  @apply inline-flex h-5 min-w-5 shrink-0 items-center justify-center rounded-[5px] border border-slate-300 bg-slate-100 px-1 font-sans text-[0.62rem] leading-none font-semibold text-slate-600 &lt;span class=&#34;n&#34;&gt;dark&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;border&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;white&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dark&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bg-white&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dark&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;white&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;70&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;inline-code-不再抢正文注意力&#34;&gt;inline code 不再抢正文注意力&lt;/h3&gt;&#xA;&lt;p&gt;inline code 这次改成了低对比度、有边界的 token。它仍然能被识别为代码，但不会像按钮一样跳出来。&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;css/tailwind.css&lt;/b&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/blog/blob/c067042276d4b4b384c66c0c61fcd4f6716eb599/css/tailwind.css#L110-L124&#34;&gt;L110–124 · c067042 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;110&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;where&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;not&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;pre&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;111&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;border&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;px&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;solid&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;color-mix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;oklab&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;slate&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;-300&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;72&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;transparent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;112&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;border-radius&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;113&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;background&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;linear-gradient&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;114&lt;/span&gt;    &lt;span class=&#34;kc&#34;&gt;to&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;bottom&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;115&lt;/span&gt;    &lt;span class=&#34;nf&#34;&gt;color-mix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;oklab&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;white&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;92&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;slate&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;-100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;116&lt;/span&gt;    &lt;span class=&#34;nf&#34;&gt;color-mix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;oklab&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;slate&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;-100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;88&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;white&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;117&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;118&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;box-shadow&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;inset&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;-1&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;px&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;color-mix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;oklab&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;slate&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;-300&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;45&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;transparent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;119&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;#4c4f69&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;120&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;padding&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.08&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;rem&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.34&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;rem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;121&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;font-size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.86&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;em&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;122&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;font-weight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;500&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;123&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;line-height&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1.75&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;124&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;word-break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;break-word&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;github-代码和-diff-仍然走-shiki&#34;&gt;GitHub 代码和 diff 仍然走 Shiki&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;::github-code&lt;/code&gt; 和 &lt;code&gt;::github-diff&lt;/code&gt; 在构建时会拉取真实代码或 diff，再生成普通 code node。也就是说它们不需要特殊渲染器，最后仍然交给 Shiki 高亮。&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;contentlayer.config.ts&lt;/b&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/blog/blob/c067042276d4b4b384c66c0c61fcd4f6716eb599/contentlayer.config.ts#L543-L552&#34;&gt;L543–552 · c067042 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;543&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;kind&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;code&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;544&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;selectCodeLines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;getGitHubFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;545&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;title&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;546&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;title&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;547&lt;/span&gt;    &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;normalizeGitHubRepo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;repo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lines&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`#L&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lines&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;548&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;549&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;createCodeNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lang&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;text&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;550&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;showLineNumbers&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;551&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;sourceUrl&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;getGitHubCodeUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;552&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;diff 的路径也一样，只是数据源换成 patch，语言固定成 &lt;code&gt;diff&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;contentlayer.config.ts&lt;/b&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/blog/blob/c067042276d4b4b384c66c0c61fcd4f6716eb599/contentlayer.config.ts#L555-L563&#34;&gt;L555–563 · c067042 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;555&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;selectCodeLines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;getGitHubDiff&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;556&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;title&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;557&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;title&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;558&lt;/span&gt;  &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;normalizeGitHubRepo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;repo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;base&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ref&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;head&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;559&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;560&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;createCodeNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;No diff matched this query.&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lang&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;diff&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;561&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;showLineNumbers&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;562&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;sourceUrl&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;getGitHubDiffUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;563&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;这也是为什么 GitHub 引用块能同时拥有：Shiki 颜色主题、语言图标、复制按钮、GitHub 跳转和 diff 背景。&lt;/p&gt;&#xA;&lt;h2 id=&#34;现在写博客的心智模型&#34;&gt;现在写博客的心智模型&lt;/h2&gt;&#xA;&lt;p&gt;普通文章只管写 Markdown。需要图标时写 &lt;code&gt;:icon-code:&lt;/code&gt;，需要行情时写 &lt;code&gt;$AAPL&lt;/code&gt; 或 &lt;code&gt;::tv AAPL&lt;/code&gt;，需要数据图表时写 &lt;code&gt;```echarts&lt;/code&gt; 代码块，需要引用源码时写 &lt;code&gt;::github-code&lt;/code&gt;，需要引用改动时写 &lt;code&gt;::github-diff&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;构建阶段会把这些短语法转换成稳定的 MDX 组件或 Shiki 代码块；运行时只负责交互和外部 widget 加载。这样写作语法短，页面输出也可控。&lt;/p&gt;&#xA;</description>
      <category>折腾</category>
      <pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://mizore.blog/zh/%E6%8A%80%E6%9C%AF/blog-git-metadata-and-icons/</guid>
    </item>
    <item>
      <title>解决 KDE Plasma Wayland 下 Obsidian Web Clipper 无法使用的问题</title>
      <link>https://mizore.blog/posts/kde-plasma-obsidian-web-clipper/</link>
      <description>&lt;p&gt;我在 Plasma Wayland 上用 Obsidian 时发现：Obsidian 已经打开的情况下，在 Chrome 里点 &lt;strong&gt;Obsidian Web Clipper&lt;/strong&gt; 的“添加到 Obsidian”，创建出来的页面是空的。&lt;/p&gt;&#xA;&lt;p&gt;只有先完全关闭 Obsidian，再从 Web Clipper 添加，内容才会正常出现。&lt;/p&gt;&#xA;&lt;h2 id=&#34;原因&#34;&gt;原因&lt;/h2&gt;&#xA;&lt;p&gt;这不是 Web Clipper 没抓到网页内容，而是 KDE 阻止了已经运行中的 Obsidian 正确获得焦点。&lt;/p&gt;&#xA;&lt;p&gt;Web Clipper 把网页内容传给 Obsidian 时，需要 Obsidian 被正确唤起并读取传入的数据。但在 KDE 的窗口管理规则下，Obsidian 在后台运行时可能无法获得焦点，内容没有被写入，结果就变成了空页面。&lt;/p&gt;&#xA;&lt;h2 id=&#34;分析&#34;&gt;分析&lt;/h2&gt;&#xA;&lt;p&gt;现象的关键点是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Obsidian 关闭时添加内容正常；&lt;/li&gt;&#xA;&lt;li&gt;Obsidian 已打开时添加内容为空；&lt;/li&gt;&#xA;&lt;li&gt;说明浏览器插件本身大概率是正常的；&lt;/li&gt;&#xA;&lt;li&gt;问题出在 Obsidian 已运行时，KDE 没有让它正确响应 Web Clipper 的调用。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;所以这是 KDE 的窗口焦点和激活权限问题，不是 Obsidian 笔记库、模板或 Chrome 插件的配置错误。&lt;/p&gt;&#xA;&lt;h2 id=&#34;解决方案&#34;&gt;解决方案&lt;/h2&gt;&#xA;&lt;p&gt;在 KDE 里给 Obsidian 添加一条窗口规则：打开 &lt;code&gt;系统设置 → 窗口管理 → 窗口规则&lt;/code&gt;，按下图设置即可。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/639f87fb1000edf73702913b297ee8295880b29d636cd55533af044ea6b0d2c5.webp&#34; alt=&#34;KDE 系统设置中 Obsidian 的窗口规则配置界面&#34; decoding=&#34;async&#34; fetchpriority=&#34;high&#34; width=&#34;1600&#34; height=&#34;1167&#34; srcset=&#34;https://mizore.blog/images/605a9074e4b97eb9a9e494b9842199c8879802dcf3e731bd960c1ecd3a66dc58.webp 720w, https://mizore.blog/images/639f87fb1000edf73702913b297ee8295880b29d636cd55533af044ea6b0d2c5.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;</description>
      <category>折腾</category>
      <pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mizore.blog/zh/%E6%8A%98%E8%85%BE/kde-plasma-obsdian-web-clipper/</guid>
    </item>
    <item>
      <title>小米 Book Pro 14 Linux 使用体验</title>
      <link>https://mizore.blog/posts/xiaomi-book-pro-14/</link>
      <description>&lt;p&gt;入手这台 Xiaomi Book Pro 14 的理由其实挺简单：我已经有一台 MacBook Pro 了，日常用着很顺手，但总有些事是 Mac 干不了、或者干起来别扭的。我需要一台 x86 的轻薄本，来补上那些“只有 x86 才好办”的活儿。&lt;/p&gt;&#xA;&lt;p&gt;作为一个不折腾不舒服的人，拿到新机器的第一件事，自然是给它装上 Arch Linux。过程中踩了几个坑，也把指纹这个我从没用过的功能认真折腾了一遍，现在整理出来，给自己留个记录，也给遇到类似问题的人一个参考。&lt;/p&gt;&#xA;&lt;p&gt;我这台的配置先放在这儿：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;SoC：Intel Ultra X7 358H&lt;/li&gt;&#xA;&lt;li&gt;内存：32GB LPDDR5x 9600MT/s（板载）&lt;/li&gt;&#xA;&lt;li&gt;GPU：Intel Arc B390&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/b5d09a616b4aa066b319f1f0025d0b697b73dd46c282c049256b89395a24afb6.webp&#34; alt=&#34;装好 Arch Linux 的 Xiaomi Book Pro 14，终端里是 fastfetch 的系统信息&#34; decoding=&#34;async&#34; fetchpriority=&#34;high&#34; width=&#34;1600&#34; height=&#34;1200&#34; srcset=&#34;https://mizore.blog/images/7ca7c73067278d7a82fe9e0e1a8ae3a98b66a535ce28bf1b4b2af3bd29ab5c10.webp 720w, https://mizore.blog/images/b5d09a616b4aa066b319f1f0025d0b697b73dd46c282c049256b89395a24afb6.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;问题一屏幕花屏&#34;&gt;问题一：屏幕花屏&lt;/h2&gt;&#xA;&lt;p&gt;刚装好系统就发现屏幕会花。查了一圈，原因是 Intel 的 Panel Self Refresh 技术。&lt;/p&gt;&#xA;&lt;p&gt;解决方法也很直接，在内核参数里加上这一行就行：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;xe.enable_psr=0&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;h2 id=&#34;问题二键盘不识别&#34;&gt;问题二：键盘不识别&lt;/h2&gt;&#xA;&lt;p&gt;这个问题更诡异一点：在 BIOS 和 GRUB 里面键盘明明能用，一进入系统，键盘就完全没反应了。装系统的时候卡在这一步会非常难受。&lt;/p&gt;&#xA;&lt;p&gt;解决方案同样是加内核参数：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;i8042.nopnp=1 i8042.dumbkbd=1&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;屏幕和键盘搞定之后，系统基本就可用了。接下来是我花时间最多的部分——指纹。&lt;/p&gt;&#xA;&lt;h2 id=&#34;指纹这次折腾的重头戏&#34;&gt;指纹：这次折腾的重头戏&lt;/h2&gt;&#xA;&lt;p&gt;坦白说，我之前从没用过带指纹的设备，所以这一块我摸索得比较细，也踩了不少坑，值得单独写一写。&lt;/p&gt;&#xA;&lt;h3 id=&#34;为什么-sudo-不能像-macos-那样弹出指纹-gui&#34;&gt;为什么 &lt;code&gt;sudo&lt;/code&gt; 不能像 macOS 那样弹出指纹 GUI？&lt;/h3&gt;&#xA;&lt;p&gt;这是这次折腾里最让我不爽的地方。macOS 上 &lt;code&gt;sudo&lt;/code&gt; 可以用 Touch ID，为什么 Linux 就不行？&lt;/p&gt;&#xA;&lt;p&gt;不是“没人想过”，而是架构本身就不一样。&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo&lt;/code&gt; 的认证链路是这样的：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;sudo → PAM → 终端会话或 askpass&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;而 KDE 那个漂亮的指纹认证弹窗，走的是另一条路：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;应用 → Polkit → KDE Polkit Agent → PAM → fprintd&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;那个 GUI 是 Polkit agent 提供的，不是 PAM 自己弹出来的，也不是 &lt;code&gt;sudo&lt;/code&gt; 自带的。PAM 模块只能参与认证流程，没法凭空把终端里的 &lt;code&gt;sudo&lt;/code&gt; 变成一个 KDE 风格的身份验证弹窗。&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo -A&lt;/code&gt; 配合 &lt;code&gt;SUDO_ASKPASS&lt;/code&gt; 确实能弹出图形密码框，但那依然是 sudo 自己的 askpass 路径，跟 KDE Polkit agent 不是一回事。所以它顶多让“输密码”这一步变成 GUI，指纹那套体验是带不进来的。&lt;/p&gt;&#xA;&lt;p&gt;macOS 能做到，是因为 Apple 把 &lt;code&gt;sudo&lt;/code&gt;、PAM、Touch ID、Secure Enclave、LocalAuthentication 这些组件从系统层面就集成到了一起。Linux 这边，这些组件分属不同项目，天然就不会有一致的体验。&lt;/p&gt;&#xA;&lt;h3 id=&#34;pam_fprintd-的关键限制pam-栈是串行的&#34;&gt;&lt;code&gt;pam_fprintd&lt;/code&gt; 的关键限制：PAM 栈是串行的&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;pam_fprintd&lt;/code&gt; 还有一个很根本的限制：在普通的 PAM 栈里，它和密码认证是串行执行的。&lt;/p&gt;&#xA;&lt;p&gt;我一开始理想中的体验是这样的：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;同一个 sudo 提示下，同时等我输密码和按指纹，哪个先完成就用哪个。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;但实际上做不到。登录管理器可以用更复杂的方式启多条 PAM 会话来模拟并行，但普通的 &lt;code&gt;sudo&lt;/code&gt; 并不会这么干。&lt;/p&gt;&#xA;&lt;p&gt;所以现实选择只能在下面几种体验里挑：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;方案 A：先指纹，失败后密码&lt;/li&gt;&#xA;&lt;li&gt;方案 B：先密码，空回车后指纹&lt;/li&gt;&#xA;&lt;li&gt;方案 C：只用 Polkit GUI，不在 sudo 上强求指纹&lt;/li&gt;&#xA;&lt;li&gt;方案 D：用第三方 hack 模块（不推荐，别拿 root 认证开玩笑）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;我最后选的是方案 B，但方案 A 也试过，先说说它为什么翻车了。&lt;/p&gt;&#xA;&lt;h3 id=&#34;方案-a先指纹失败后密码翻车记录&#34;&gt;方案 A：先指纹，失败后密码（翻车记录）&lt;/h3&gt;&#xA;&lt;p&gt;一开始的直觉配置是这样的，&lt;code&gt;/etc/pam.d/sudo&lt;/code&gt;：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;#%PAM-1.0&#xA;&#xA;auth        sufficient  pam_fprintd.so max-tries=3 timeout=8&#xA;auth        include     system-auth&#xA;account     include     system-auth&#xA;session     include     system-auth&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;逻辑上看起来没毛病：先试指纹，成功就直接过，失败再让你输密码。&lt;/p&gt;&#xA;&lt;p&gt;但实际用起来就出问题了。我故意让指纹识别失败，然后输入正确的密码，&lt;code&gt;sudo&lt;/code&gt; 还是过不去。日志里出现了这样的东西：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;pam_unix(sudo:auth): conversation failed&#xA;pam_unix(sudo:auth): auth could not identify password for [...]&#xA;pam_faillock(sudo:auth): Consecutive login failures ... account temporarily locked&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;同时 &lt;code&gt;fprintd&lt;/code&gt; 那边也在报：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;Device was already claimed&#xA;Verification was in progress, stopping it&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;看起来是 &lt;code&gt;sudo&lt;/code&gt;、终端 PAM conversation、&lt;code&gt;pam_fprintd&lt;/code&gt; 和后续的密码栈之间交互状态乱了。指纹失败之后，密码 fallback 并没有干净利落地接上，还顺手触发了 &lt;code&gt;faillock&lt;/code&gt; 的失败计数，导致账号被暂时锁定。&lt;/p&gt;&#xA;&lt;p&gt;这类问题最危险的地方在于，你改的是 root 提权入口。如果把 &lt;code&gt;/etc/pam.d/sudo&lt;/code&gt; 改坏了，又没有别的 root 通道，就很容易把自己锁在外面。&lt;/p&gt;&#xA;&lt;p&gt;我当时恢复的方法，是走还能用的 Polkit：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;pkexec sh -c &lt;span class=&#34;s1&#34;&gt;&amp;#39;cat &amp;gt; /etc/pam.d/sudo &amp;lt;&amp;lt;EOF&lt;/span&gt;&#xA;&lt;span class=&#34;s1&#34;&gt;#%PAM-1.0&lt;/span&gt;&#xA;&lt;span class=&#34;s1&#34;&gt;auth        include     system-auth&lt;/span&gt;&#xA;&lt;span class=&#34;s1&#34;&gt;account     include     system-auth&lt;/span&gt;&#xA;&lt;span class=&#34;s1&#34;&gt;session     include     system-auth&lt;/span&gt;&#xA;&lt;span class=&#34;s1&#34;&gt;EOF&lt;/span&gt;&#xA;&lt;span class=&#34;s1&#34;&gt;faillock --user 用户名 --reset&lt;/span&gt;&#xA;&lt;span class=&#34;s1&#34;&gt;systemctl restart fprintd&lt;/span&gt;&#xA;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;bash&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;这个回滚思路建议记一下：先把 sudo 恢复到 Arch 默认的密码路径，再清理 &lt;code&gt;faillock&lt;/code&gt;，最后重启 &lt;code&gt;fprintd&lt;/code&gt; 释放可能残留的设备占用。关键时刻能救命。&lt;/p&gt;&#xA;&lt;h3 id=&#34;方案-b先密码空回车后指纹目前采用的方案&#34;&gt;方案 B：先密码，空回车后指纹（目前采用的方案）&lt;/h3&gt;&#xA;&lt;p&gt;后来换了一种思路：正常情况就是普通的 sudo 密码提示，如果直接按 Enter 提交空密码，再进入指纹验证。&lt;/p&gt;&#xA;&lt;p&gt;当前的 &lt;code&gt;/etc/pam.d/sudo&lt;/code&gt; 配置：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;#%PAM-1.0&#xA;&#xA;# 先提示输密码，按 Enter 空密码则尝试指纹&#xA;auth        sufficient  pam_unix.so try_first_pass likeauth nullok&#xA;&#xA;# 避免在非 tty 的后台 sudo 里弹指纹&#xA;auth        [success=1 default=ignore] pam_succeed_if.so service in sudo:su:su-l tty in :unknown&#xA;-auth       sufficient  pam_fprintd.so max-tries=3 timeout=8&#xA;&#xA;# Arch 默认的 fallback&#xA;auth        include     system-auth&#xA;account     include     system-auth&#xA;session     include     system-auth&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;交互效果是这样的：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;[sudo] password for user:&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;想输密码就直接输，想用指纹就空回车，这时候会变成：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;Place your right index finger on the fingerprint reader&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;这个方案比“先指纹后密码”更符合终端里的直觉，但它也有自己的小毛病。&lt;/p&gt;&#xA;&lt;h3 id=&#34;方案-b-的坑空回车可能留下失败记录&#34;&gt;方案 B 的坑：空回车可能留下失败记录&lt;/h3&gt;&#xA;&lt;p&gt;空回车这一步，本质上是先经过了 &lt;code&gt;pam_unix.so&lt;/code&gt;。从 &lt;code&gt;pam_unix&lt;/code&gt; 的视角看，空密码不是正确密码，所以它会先产生一次认证失败记录，然后 PAM 流程才走到 &lt;code&gt;pam_fprintd&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;实际检查 &lt;code&gt;faillock&lt;/code&gt; 的时候能看到类似这样的记录：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;user:&#xA;When                Type  Source       Valid&#xA;2026-05-08 ...      TTY   /dev/pts/1   V&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;也就是说，即使后面指纹认证通过了，空回车这一步也可能被当成一次失败尝试记下来。&lt;/p&gt;&#xA;&lt;p&gt;所以用这个方案的话，有几点要注意：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;code&gt;faillock&lt;/code&gt; 的失败阈值别设太低。&lt;/li&gt;&#xA;&lt;li&gt;调试 PAM 的时候一定留一个可用的 root 后门，比如当前还能用的 &lt;code&gt;pkexec&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;连续失败之后记得检查：&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;faillock --user &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$USER&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;bash&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;必要的时候手动重置：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;faillock --user &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$USER&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; --reset&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;bash&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;另外说一句，如果追求最稳的图形提权体验，日常还是优先用 Polkit，不用把所有事情都压在 &lt;code&gt;sudo&lt;/code&gt; 上。&lt;/p&gt;&#xA;&lt;h3 id=&#34;kwallet为什么不能用指纹直接解锁&#34;&gt;KWallet：为什么不能用指纹直接解锁&lt;/h3&gt;&#xA;&lt;p&gt;这是另一个容易误解的地方。&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;kwallet-pam&lt;/code&gt; 的作用是，在你用密码登录的时候，把登录密码传给 KWallet，用同一个密码自动解锁钱包。&lt;/p&gt;&#xA;&lt;p&gt;日志里有这样一句：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;pam_kwallet5(plasmalogin-autologin:auth): Empty or missing password, doing nothing&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;这句话已经把问题说清楚了：自动登录或者指纹登录，都没有提供“用户密码”这个明文材料，KWallet 拿不到它需要的密钥。&lt;/p&gt;&#xA;&lt;p&gt;指纹认证只回答一个问题：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;这个人是不是这个用户？&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;KWallet 解密需要的是另一个问题的答案：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;用来打开钱包的密钥或密码是什么？&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;指纹设备不会把用户的登录密码吐出来，它只告诉系统认证是否成功。所以，KWallet 没有办法单靠指纹解锁。&lt;/p&gt;&#xA;&lt;p&gt;可选的解决思路有几个：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;关掉自动登录，用密码登录，并且让 KWallet 密码和登录密码保持一致。&lt;/li&gt;&#xA;&lt;li&gt;接受现实，首次进桌面后手动输一次 KWallet 密码。&lt;/li&gt;&#xA;&lt;li&gt;降低钱包安全性，比如用空密码钱包——但这绝对不建议，尤其是存重要密钥的时候。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;所以这里不是 KDE 没接好，而是“认证”和“解密”在性质上就是两回事。&lt;/p&gt;&#xA;&lt;h3 id=&#34;fprintd-设备占用问题&#34;&gt;&lt;code&gt;fprintd&lt;/code&gt; 设备占用问题&lt;/h3&gt;&#xA;&lt;p&gt;折腾过程中还遇到过这种报错：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;Device was already claimed&#xA;Verification was in progress, stopping it&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;这通常说明上一次验证没干净结束，或者好几个认证请求同时在抢同一个指纹设备。&lt;/p&gt;&#xA;&lt;p&gt;处理方法也不复杂：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;systemctl restart fprintd&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;bash&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;如果普通用户权限不够，用确认可用的 Polkit 来操作：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;pkexec systemctl restart fprintd&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;bash&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;这也是为什么我不建议把太多认证入口都配置成自动去抢指纹。指纹设备一次只能服务一个验证请求，多个 PAM 或 Polkit 请求搅在一起的时候，体验会变得非常诡异。&lt;/p&gt;&#xA;&lt;h2 id=&#34;最终状态整理&#34;&gt;最终状态整理&lt;/h2&gt;&#xA;&lt;p&gt;折腾完一圈，现在这台机器的指纹和认证相关配置是这样的：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;fprintd&lt;/strong&gt;：能正常识别 Goodix 设备，已录入右手食指&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;KDE 用户设置&lt;/strong&gt;：可以通过 kcm_users 管理指纹&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Polkit&lt;/strong&gt;：优先指纹，失败后回退密码&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;kio-admin&lt;/strong&gt;：已安装，可以用 &lt;code&gt;admin://&lt;/code&gt; 做图形化 root 文件操作&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;sudo&lt;/strong&gt;：先密码，空回车后指纹，最多尝试 3 次&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Plasma Login Manager&lt;/strong&gt;：PAM 已接入指纹，但自动登录会绕过&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;KWallet&lt;/strong&gt;：不能单靠指纹解锁&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;&#xA;&lt;p&gt;这台小米 Book Pro 14 的 Linux 兼容性比我想象中好很多，几个问题都有清晰的原因和解决路径，整体用下来挺顺手。&lt;/p&gt;&#xA;&lt;p&gt;还有一个感受是，现在调系统比以前快太多了。以前遇到问题要翻一堆 ArchWiki 和 man 文档，现在可以直接问 ChatGPT“xxx 问题怎么解决”，效率完全不一样。这次连 OpenAI Codex 也用上了，整体调试过程比过去顺畅了不止一个量级。&lt;/p&gt;&#xA;</description>
      <category>折腾</category>
      <pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mizore.blog/zh/%E6%8A%98%E8%85%BE/xiaomi-book-pro-14/</guid>
    </item>
  </channel>
</rss>