<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Hedon Garden</title>
    <link>https://hd-garden-pr74.pages.dev</link>
    <atom:link href="https://hd-garden-pr74.pages.dev/rss.xml" rel="self" type="application/rss+xml" />
    <description>文章、随想和专栏。</description>
    <language>zh-CN</language>
    <lastBuildDate>Thu, 15 Oct 2026 16:00:00 GMT</lastBuildDate>
    <item>
      <title>窗台香草丨01丨播种到第一次采摘</title>
      <link>https://hd-garden-pr74.pages.dev/blog/windowsill-herbs/</link>
      <guid isPermaLink="true">https://hd-garden-pr74.pages.dev/blog/windowsill-herbs/</guid>
      <pubDate>Thu, 15 Oct 2026 16:00:00 GMT</pubDate>
      <description>一盆罗勒从播种、间苗到第一次剪叶，按周记下高度和浇水量。</description>
      <category>香草</category><category>窗台</category>
      <content:encoded><![CDATA[<p>北向窗台上有三只盆。这季种了罗勒和薄荷，第三只空着，留给秋天的香菜。</p>
<h2>两种香草</h2>
<p>罗勒要见光，薄荷更耐阴。浇法也不一样。</p>
<pre><code class="language-garden">items:
  - title: 罗勒
    text: 土面发白再浇透，托盘里的水倒掉。
  - title: 薄荷
    text: 土保持微湿，避开正午的直射光。
</code></pre>
<h2>这季记下的数</h2>
<p>种子是三月中旬播的。盆不深，第一次剪叶等到第六周。</p>
<pre><code class="language-garden">items:
  - label: 发芽
    value: 7
    unit: 天
  - label: 盆深
    value: 15
    unit: 厘米
  - label: 第一次剪叶
    value: 6
    unit: 周
</code></pre>
<h2>从播种到剪叶</h2>
<pre><code class="language-garden">items:
  - date: 3月12日
    title: 播种
    text: 罗勒和薄荷各一盆，覆土不超过半厘米。
  - date: 3月19日
    title: 出苗
    text: 罗勒先露头，薄荷晚了两天。
  - date: 4月2日
    title: 间苗
    text: 每盆留下最壮的两株。
  - date: 5月1日
    title: 第一次剪叶
    text: 只剪顶端，留下至少四片叶。
</code></pre>
<blockquote>
<p>[!NOTE]
北向窗冬天光照不够，罗勒会徒长。把盆移到能见到上午光的那一格。</p>
</blockquote>
<h2>每周的株高</h2>
<p>从第二周起，每周一量一次最高的那片叶。</p>
<pre><code class="language-garden">type: bar
title: 罗勒株高
x: [&quot;第2周&quot;, &quot;第3周&quot;, &quot;第4周&quot;, &quot;第5周&quot;, &quot;第6周&quot;]
series:
  - name: 株高
    data: [4, 8, 13, 18, 22]
unit: cm
caption: 第六周剪叶前，罗勒大约 22 厘米。
</code></pre>
<h2>窗台怎么摆</h2>
<p>靠左的那格上午有光，罗勒放那里。薄荷在中间。右边的空盆先不浇。</p>
<p><img src="/media/posts/windowsill-herbs/sill.svg" alt="朝北的窗台，从左到右是罗勒、薄荷和一只空盆。"></p>
<pre><code class="language-garden">caption: 朝北的窗台，从左到右是罗勒、薄荷和一只空盆。
parts:
  - name: 全部
    text: 三只盆并排，靠左的光照最好。
  - name: 罗勒
    text: 靠左，每天都够到上午的光。
  - name: 薄荷
    text: 中间，土干得慢一些。
  - name: 空盆
    text: 右边空着，留给秋天的香菜。
---
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 640 220&quot;&gt;
  &lt;rect data-p=&quot;全部&quot; x=&quot;20&quot; y=&quot;16&quot; width=&quot;600&quot; height=&quot;150&quot; rx=&quot;10&quot; fill=&quot;#d5e3ea&quot;/&gt;
  &lt;rect data-p=&quot;罗勒&quot; x=&quot;70&quot; y=&quot;96&quot; width=&quot;90&quot; height=&quot;58&quot; rx=&quot;8&quot; fill=&quot;#c4a574&quot;/&gt;
  &lt;circle data-p=&quot;罗勒&quot; cx=&quot;115&quot; cy=&quot;78&quot; r=&quot;30&quot; fill=&quot;#3f7d4e&quot;/&gt;
  &lt;rect data-p=&quot;薄荷&quot; x=&quot;270&quot; y=&quot;104&quot; width=&quot;90&quot; height=&quot;50&quot; rx=&quot;8&quot; fill=&quot;#c4a574&quot;/&gt;
  &lt;circle data-p=&quot;薄荷&quot; cx=&quot;315&quot; cy=&quot;88&quot; r=&quot;26&quot; fill=&quot;#6aa87a&quot;/&gt;
  &lt;rect data-p=&quot;空盆&quot; x=&quot;470&quot; y=&quot;118&quot; width=&quot;90&quot; height=&quot;36&quot; rx=&quot;8&quot; fill=&quot;#e4d3b8&quot;/&gt;
&lt;/svg&gt;
</code></pre>
<h2>今天浇不浇</h2>
<p>土面还湿就先不动。干了再浇，浇到盆底出水，然后把托盘倒掉。</p>
<pre><code class="language-mermaid">flowchart TD
  A[看土面] --&gt; B{还湿吗}
  B --&gt;|湿| C[今天不浇]
  B --&gt;|干| D[浇到盆底出水]
  D --&gt; E[倒掉托盘]
</code></pre>
<blockquote>
<p>[!WARNING]
霜前把盆搬进屋。罗勒叶子一碰霜就发黑，回不来。</p>
</blockquote>
<h2>盆口和浇水量</h2>
<p>盆口越大，一次浇得越多。12 厘米的盆大约是 70 毫升。</p>
<pre><code class="language-garden">caption: 盆口越大，一次浇得越多。12 厘米大约是 70 毫升。
intent: |
  让读者拧盆口直径，看这一次该浇多少毫升。
---
&lt;State diameter={12} /&gt;
&lt;Slider bind=&quot;diameter&quot; label=&quot;盆口直径&quot; min={8} max={24} unit=&quot;cm&quot; /&gt;
&lt;Let water={round(diameter * diameter * 0.35 + 20)} /&gt;
&lt;Stat label=&quot;这一次&quot; value={water} unit=&quot;ml&quot; /&gt;
</code></pre>
<h2>水怎么往下走</h2>
<p>浇下去的水不会停在表面。它慢慢渗过土层，最后从排水孔滴出来。</p>
<pre><code class="language-garden">title: 水从土面渗到排水孔，大约要一分钟。
code: |
  &lt;style&gt;
    html, body { margin: 0; background: transparent; color: #3c342c; font: 15px/1.5 ui-sans-serif, sans-serif; }
    svg { display: block; width: 100%; height: auto; }
  &lt;/style&gt;
  &lt;svg viewBox=&quot;0 0 640 280&quot; role=&quot;img&quot; aria-label=&quot;水从土面渗到盆底&quot;&gt;
    &lt;rect x=&quot;180&quot; y=&quot;36&quot; width=&quot;280&quot; height=&quot;200&quot; rx=&quot;18&quot; fill=&quot;#efe6d6&quot; stroke=&quot;#cbbfa8&quot;/&gt;
    &lt;rect id=&quot;water&quot; x=&quot;196&quot; y=&quot;48&quot; width=&quot;248&quot; height=&quot;16&quot; fill=&quot;#7ea4c4&quot; opacity=&quot;0.85&quot;/&gt;
    &lt;rect x=&quot;196&quot; y=&quot;150&quot; width=&quot;248&quot; height=&quot;70&quot; fill=&quot;#8b6a45&quot;/&gt;
    &lt;circle cx=&quot;320&quot; cy=&quot;214&quot; r=&quot;8&quot; fill=&quot;#5c4632&quot;/&gt;
    &lt;text x=&quot;320&quot; y=&quot;268&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; fill=&quot;#3c342c&quot;&gt;排水孔&lt;/text&gt;
  &lt;/svg&gt;
  &lt;script&gt;
    var water = document.getElementById(&quot;water&quot;);
    var y = 48;
    var reduce = window.matchMedia(&quot;(prefers-reduced-motion: reduce)&quot;).matches;
    if (reduce) {
      water.setAttribute(&quot;y&quot;, &quot;168&quot;);
      water.setAttribute(&quot;height&quot;, &quot;36&quot;);
    } else {
      (function tick() {
        y += 0.45;
        if (y &gt; 168) y = 48;
        water.setAttribute(&quot;y&quot;, String(y));
        water.setAttribute(&quot;height&quot;, String(Math.max(16, 204 - y)));
        requestAnimationFrame(tick);
      })();
    }
  &lt;/script&gt;
</code></pre>
<h2>浇水纸条</h2>
<p>纸条上先记下这一周。周四那次少了，周日又给薄荷补了一次。</p>
<pre><code class="language-text">周一  罗勒  80 ml
周四  罗勒  80 ml
</code></pre>
<pre><code class="language-diff"> 周一  罗勒  80 ml
-周四  罗勒  80 ml
+周四  罗勒  120 ml
+周日  薄荷  60 ml
</code></pre>
<h2>六周记录</h2>
<table>
<thead>
<tr>
<th>周</th>
<th>株高</th>
<th>新叶</th>
<th>浇水</th>
<th>土面</th>
<th>气温</th>
<th>日照</th>
<th>备注</th>
</tr>
</thead>
<tbody><tr>
<td>第1周</td>
<td>未见</td>
<td>无</td>
<td>每天喷湿</td>
<td>湿</td>
<td>16°C</td>
<td>上午</td>
<td>覆土很薄</td>
</tr>
<tr>
<td>第2周</td>
<td>4 cm</td>
<td>2 片</td>
<td>80 ml</td>
<td>干</td>
<td>18°C</td>
<td>上午</td>
<td>罗勒先出</td>
</tr>
<tr>
<td>第3周</td>
<td>8 cm</td>
<td>4 片</td>
<td>80 ml</td>
<td>干</td>
<td>19°C</td>
<td>上午</td>
<td>薄荷跟上</td>
</tr>
<tr>
<td>第4周</td>
<td>13 cm</td>
<td>6 片</td>
<td>100 ml</td>
<td>干</td>
<td>21°C</td>
<td>上午</td>
<td>间苗，留两株</td>
</tr>
<tr>
<td>第5周</td>
<td>18 cm</td>
<td>8 片</td>
<td>120 ml</td>
<td>干</td>
<td>22°C</td>
<td>上午</td>
<td>叶色转深</td>
</tr>
<tr>
<td>第6周</td>
<td>22 cm</td>
<td>10 片</td>
<td>120 ml</td>
<td>干</td>
<td>23°C</td>
<td>上午</td>
<td>剪了顶端</td>
</tr>
</tbody></table>
<p>想核对叶子是不是对生、花是不是穗状时，看这一页。</p>
<pre><code class="language-garden">url: https://zh.wikipedia.org/wiki/%E7%BD%97%E5%8B%92
title: 罗勒
description: 叶子对生，花排成穗。窗台上的那盆对得上。
</code></pre>
<blockquote>
<p>[!TIP]
剪叶之后把盆转半圈，让另一面也能见到上午的光。</p>
</blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>窗台香草丨附录丨记录表与资料下载</title>
      <link>https://hd-garden-pr74.pages.dev/blog/windowsill-herbs-kit/</link>
      <guid isPermaLink="true">https://hd-garden-pr74.pages.dev/blog/windowsill-herbs-kit/</guid>
      <pubDate>Wed, 14 Oct 2026 16:00:00 GMT</pubDate>
      <description>播种指南、浇水记录表和排盆小工具，整理成可以下载和预览的文件。</description>
      <category>香草</category><category>窗台</category>
      <content:encoded><![CDATA[<p>种了一季，用到的表格和小工具都放在这里。想照着种，先下载整包。</p>
<ul>
<li><a href="https://hd-garden-pr74.pages.dev/media/posts/windowsill-herbs-kit/herb-kit.zip">窗台香草资料包</a>（ZIP · 1.8 KB）：浇水记录表（CSV 和 Markdown）和排盆小工具，解压就能用</li>
</ul>
<h2>单独的文件</h2>
<p>每份文件也可以单独看。Markdown、HTML 和 PDF 点「预览」，在页面右侧打开，不用离开这篇。</p>
<p><strong>种植资料</strong></p>
<ul>
<li><a href="https://hd-garden-pr74.pages.dev/media/posts/windowsill-herbs-kit/watering-log.md">watering-log.md</a>（MD · 846 B）：前四周的浇水量和土况</li>
<li><a href="https://hd-garden-pr74.pages.dev/media/posts/windowsill-herbs-kit/sill-planner.html">窗台排盆</a>（HTML · 1.8 KB）：拖动滑块，看一条窗台放几只盆合适</li>
<li><a href="https://hd-garden-pr74.pages.dev/media/posts/windowsill-herbs-kit/seed-guide.pdf">播种指南</a>（PDF · 204 KB）：从准备盆土到第一次采摘，一页纸</li>
</ul>
<h2>放在对象存储里的大文件</h2>
<p>原图这类大文件不进仓库，放到对象存储，<code>src</code> 写完整地址。这样的文件只能下载或在新标签页打开。</p>
<ul>
<li><a href="https://upload.wikimedia.org/wikipedia/commons/9/90/Basil-Basilico-Ocimum_basilicum-albahaca.jpg">罗勒原图</a>（JPG · 4.3 MB）：Castielli 摄，来自 Wikimedia Commons，CC BY-SA 3.0</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Intelligent UI 实验室</title>
      <link>https://hd-garden-pr74.pages.dev/blog/intelligent-ui-lab/</link>
      <guid isPermaLink="true">https://hd-garden-pr74.pages.dev/blog/intelligent-ui-lab/</guid>
      <pubDate>Fri, 09 Oct 2026 16:00:00 GMT</pubDate>
      <description>用一段自然语言写下想法，让 AI 编译成可以拖、点、翻的交互块。</description>
      <category>Garden</category><category>Intelligent UI</category>
      <content:encoded><![CDATA[<p>这篇文章演示 Garden 的 Intelligent UI：作者在稿里写一段 <code>intent</code>，AI 把它编译成 <code>ui</code> 块，站点画成可交互的组件。下面每一块都写在它要讲的那一段旁边。</p>
<h2>KV cache 到底占多少显存</h2>
<p>每个 token 在每一层都要存一份 K 和一份 V。以 32 层、8 个 KV 头、每头 128 维、fp16 的模型为例：</p>
<figure><p>batch：1（网页中可调，范围 1–32）</p><p>序列长度：4,096（网页中可调，范围 1,024–32,768）</p><p>batch 为 1、序列 4,096 时，KV cache 要 <strong>512 MiB</strong>。</p><p>（网页中为可交互图表）</p><table><thead><tr><th>序列长度</th><th>KV 显存</th></tr></thead><tbody><tr><td>1,024</td><td>128 MiB</td></tr><tr><td>2,048</td><td>256 MiB</td></tr><tr><td>4,096</td><td>512 MiB</td></tr><tr><td>8,192</td><td>1 GiB</td></tr><tr><td>16,384</td><td>2 GiB</td></tr><tr><td>32,768</td><td>4 GiB</td></tr></tbody></table><figcaption>显存与 batch、序列长度都成正比；batch 8、序列 32K 就要 32 GiB。 · <a href="https://hd-garden-pr74.pages.dev/blog/intelligent-ui-lab/#ui-kv-memory">在网页中交互</a></figcaption></figure>

<p>所以长上下文推理的瓶颈往往不是算力，而是显存。</p>
<h2>Prefill 和 Decode 有什么不同</h2>
<p>Prefill 阶段一次处理整段 prompt；之后每生成一个 token，都只为这个新 token 算 K/V。</p>
<figure><ol><li><p><strong>Prefill</strong>：一次算完 3 个 prompt token 的 K/V，全部写进缓存。</p></li><li><p><strong>Decode 1</strong>：新 token 只算自己的 K/V，追加第 4 行；前 3 行直接读缓存。</p></li><li><p><strong>Decode 2</strong>：再追加一行。缓存里已有 5 行，本步新算 1 行。</p></li></ol><table><thead><tr><th></th><th>K</th><th>V</th></tr></thead><tbody><tr><td>t1</td><td>k1（本步新算）</td><td>v1（本步新算）</td></tr><tr><td>t2</td><td>k2（本步新算）</td><td>v2（本步新算）</td></tr><tr><td>t3</td><td>k3（本步新算）</td><td>v3（本步新算）</td></tr></tbody></table><figcaption>Prefill 一次写满 3 行 K/V；之后每步只新算 1 行，其余读缓存。 · <a href="https://hd-garden-pr74.pages.dev/blog/intelligent-ui-lab/#ui-prefill-decode">在网页中交互</a></figcaption></figure>

<p>这就是 decode 阶段每步计算量和序列长度无关的原因。</p>
<h2>读多写少时选哪把锁</h2>
<p>假设每次读和写都要 10 ms，几个读者和一个写者同时到达。</p>
<figure><p>同时到达的读者：3 个（网页中可调，范围 1–8）</p><p><strong>Mutex</strong></p><p>读者和写者一个接一个，共 <strong>40 ms</strong>。</p><p><strong>RwLock</strong></p><p>3 个读者一起读，写者再独占，共 <strong>20 ms</strong>。</p><figcaption>读者越多，Mutex 越慢；RwLock 让读者并行，总耗时不变。 · <a href="https://hd-garden-pr74.pages.dev/blog/intelligent-ui-lab/#ui-mutex-vs-rwlock">在网页中交互</a></figcaption></figure>

<p>代价是 RwLock 自身更重，写多的场景反而更慢。</p>
<h2>为什么 ECB 会泄露图案</h2>
<figure><p>模式：ECB（网页中可选：ECB / CBC）</p><table><thead><tr><th></th><th>块1</th><th>块2</th><th>块3</th><th>块4</th></tr></thead><tbody><tr><td>明文</td><td>A</td><td>B</td><td>A</td><td>A</td></tr><tr><td>密文</td><td>X（密文与别的块相同）</td><td>Y</td><td>X（密文与别的块相同）</td><td>X（密文与别的块相同）</td></tr></tbody></table><p>密文 · 块1：和块3、块4相同</p><p>密文 · 块3：和块1相同</p><p>密文 · 块4：和块1相同</p><figcaption>ECB 下相同的明文块得到相同的密文块；CBC 把上一块的密文混进来，重复就消失了。 · <a href="https://hd-garden-pr74.pages.dev/blog/intelligent-ui-lab/#ui-ecb-vs-cbc">在网页中交互</a></figcaption></figure>

<p>相同的明文块在 ECB 下总是得到相同的密文块，图案就这样漏了出来。</p>
<h2>人数一变，采购量跟着变</h2>
<figure><p>人数：5 人（网页中可调，范围 1–12）</p><p>5 人，带骨羊腿大约 2.0 kg。</p><p>来源 USDA</p><hr><p><strong>这一桌的构成</strong></p><ul><li><p>羊腿：2</p></li><li><p>土豆：1.25</p></li><li><p>蔬菜：0.75</p></li></ul><p><strong>周日时间表</strong></p><ul><li><p>☐ 12:30 处理羊腿 —— 蒜、迷迭香、柠檬</p></li><li><p>☐ 15:00 入烤箱 —— 用温度计，不单看时间</p></li><li><p>☐ 17:00 上桌</p></li></ul><figcaption>人数用加减号调，羊腿重量和圆环占比一起变。 · <a href="https://hd-garden-pr74.pages.dev/blog/intelligent-ui-lab/#ui-sunday-shop">在网页中交互</a></figcaption></figure>]]></content:encoded>
    </item>
    <item>
      <title>Markdown 复杂语法实验场</title>
      <link>https://hd-garden-pr74.pages.dev/blog/markdown-lab/</link>
      <guid isPermaLink="true">https://hd-garden-pr74.pages.dev/blog/markdown-lab/</guid>
      <pubDate>Fri, 24 Jul 2026 16:00:00 GMT</pubDate>
      <description>用一篇真实文章验证 Typora 常用语法、数学公式、Mermaid、脚注、表格与原生 HTML。</description>
      <category>Markdown</category><category>Typora</category><category>Mermaid</category><category>KaTeX</category>
      <content:encoded><![CDATA[<p>这篇文章不是功能清单，而是一份可以长期保留的<strong>渲染验收样本</strong>。我会在 Typora 中常用的语法和网页输出之间建立一条可重复验证的路径。</p>
<blockquote>
<p>写作格式的价值，不在于语法多，而在于文件离开编辑器之后仍能完整表达。</p>
</blockquote>
<h2>基础排版与 GFM</h2>
<p>实现**掩码（Masking）<strong>操作，引入</strong>多头注意力（Multi-Head Attention）<strong>机制；前</strong>（中文括号）**后也能正常加粗。</p>
<p><u>新的向量知道前面有 <code>Your journey</code>，后面有 <code>with one step</code>，高亮应连续。</u></p>
<p>一段文字可以同时包含<strong>粗体</strong>、<em>斜体</em>、<del>删除线</del>、<code>inline code</code> 和 <a href="https://typora.io/">外部链接</a>。</p>
<ul>
<li>第一层列表<ul>
<li>第二层列表<ul>
<li>第三层列表</li>
</ul>
</li>
</ul>
</li>
<li>列表之后仍然保持稳定的段落节奏</li>
</ul>
<ol>
<li>写作</li>
<li>本地预览</li>
<li>发布与验证</li>
</ol>
<ul>
<li><input checked="" disabled="" type="checkbox"> 标题、引用、列表和代码</li>
<li><input checked="" disabled="" type="checkbox"> 表格、删除线与任务列表</li>
<li><input checked="" disabled="" type="checkbox"> 数学公式与 Mermaid</li>
<li><input disabled="" type="checkbox"> 用新的真实文章继续回归测试</li>
</ul>
<table>
<thead>
<tr>
<th>能力</th>
<th align="center">写法</th>
<th align="right">网页表现</th>
</tr>
</thead>
<tbody><tr>
<td>GFM</td>
<td align="center"><code>remark-gfm</code></td>
<td align="right">已验证</td>
</tr>
<tr>
<td>数学公式</td>
<td align="center"><code>$...$</code> / <code>$$...$$</code></td>
<td align="right">KaTeX</td>
</tr>
<tr>
<td>图表</td>
<td align="center">Mermaid fenced code</td>
<td align="right">SVG</td>
</tr>
</tbody></table>
<h2>语义警告框</h2>
<blockquote>
<p>[!NOTE]
普通补充信息会显示为「提醒」。</p>
</blockquote>
<blockquote>
<p>[!TIP]
推荐做法会显示为「建议」。</p>
</blockquote>
<blockquote>
<p>[!IMPORTANT]
读者不能漏掉的要点会显示为「重要」。</p>
</blockquote>
<blockquote>
<p>[!WARNING]
需要关注的风险会显示为「警告」。</p>
</blockquote>
<blockquote>
<p>[!CAUTION]
可能造成严重后果的操作会显示为「注意」。</p>
</blockquote>
<h2>链接卡片</h2>
<p>在链接后面紧跟 <code>{card}</code>，网页里会多出一张卡片，写着对方页面的标题、简介和配图。链接写在句子里时，文字照常可点，卡片跟在这一段后面：表格、任务列表和脚注都来自 <a href="https://github.com/remarkjs/remark-gfm">remark-gfm</a>，它让 GFM 语法在这里生效。</p>
<p>链接单独占一行时，卡片就放在这一行的位置：</p>
<p><a href="https://katex.org/docs/supported.html">KaTeX 支持的函数</a></p>
<p>写在代码里的 <code>[文字](https://example.com){card}</code> 不会变成卡片，原样显示。</p>
<h3>标题里的 <a href="https://typora.io/">Typora</a> 外链</h3>
<p>标题本身点了会定位到这里，不带下划线；标题里的外链照常带虚线下划线。</p>
<h2>数学公式</h2>
<p><strong>求和部分：$\sum_{j=1}^{N}$（上下限）</strong>。</p>
<p>$$
C_i = \sum_{j=1}^N w_{ij} \cdot V_j
$$</p>
<p>行内公式会和文字一起流动，例如注意力的缩放项 $\frac{QK^\top}{\sqrt{d_k}}$。</p>
<p>块级公式支持多行对齐：</p>
<p>$$
\begin{aligned}
\operatorname{Attention}(Q,K,V)
  &amp;= \operatorname{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)V \
K_{\text{cache}}^{(t)}
  &amp;= [K_{\text{cache}}^{(t-1)}; K_t] \
V_{\text{cache}}^{(t)}
  &amp;= [V_{\text{cache}}^{(t-1)}; V_t]
\end{aligned}
$$</p>
<p>矩阵也能正常显示：</p>
<p>$$
X =
\begin{bmatrix}
1 &amp; 0 &amp; 1 \
0 &amp; 1 &amp; 0 \
1 &amp; 1 &amp; 1
\end{bmatrix}
$$</p>
<h2>Mermaid 流程与时序</h2>
<p>所有 Mermaid 图表固定使用「中性」主题。每张图都可以在「图表」与「Code」之间切换：前者用于阅读，后者用于检查或复制原始 Mermaid 语法。</p>
<p>下面的流程图描述本地写作到发布的闭环：</p>
<pre><code class="language-mermaid">flowchart LR
  A[Typora 写作] --&gt; B{本地验证}
  B --&gt;|通过| C[生成内容索引]
  B --&gt;|失败| A
  C --&gt; D[构建网站]
  D --&gt; E[浏览器验收]
  E --&gt; F[发布]
</code></pre>
<p>同一篇文章里可以继续放更复杂的时序图：</p>
<pre><code class="language-mermaid">sequenceDiagram
  autonumber
  participant W as Writer
  participant M as Markdown
  participant S as Site
  participant R as Reader
  W-&gt;&gt;M: 保存 .md 与媒体
  M-&gt;&gt;S: 构建内容与目录
  S--&gt;&gt;W: 返回预览
  W-&gt;&gt;S: 发布已验证版本
  R-&gt;&gt;S: 阅读文章
  S--&gt;&gt;R: HTML + KaTeX + Mermaid
</code></pre>
<h2>多语言代码高亮</h2>
<p>同一种内容结构，在不同语言里应该保留各自可辨识的关键字、类型、字符串、注释与数字层级。</p>
<h3>TypeScript</h3>
<pre><code class="language-typescript">type Post&lt;TMeta extends Record&lt;string, unknown&gt;&gt; = {
  slug: string;
  meta: TMeta;
};

async function publish&lt;TMeta extends Record&lt;string, unknown&gt;&gt;(
  post: Post&lt;TMeta&gt;,
): Promise&lt;string&gt; {
  const response = await fetch(`/api/posts/${post.slug}`, {
    method: &quot;POST&quot;,
    body: JSON.stringify(post.meta),
  });
  return response.ok ? &quot;published&quot; : &quot;failed&quot;;
}
</code></pre>
<h3>Python</h3>
<pre><code class="language-python">from dataclasses import dataclass
from pathlib import Path

@dataclass(frozen=True)
class Article:
    &quot;&quot;&quot;选中这行文字时，深浅主题都应保持清晰。&quot;&quot;&quot;
    title: str
    source: Path

def published_titles(articles: list[Article]) -&gt; list[str]:
    return [article.title for article in articles if article.source.exists()]
</code></pre>
<h3>Rust</h3>
<pre><code class="language-rust">#[derive(Debug)]
struct Entry&lt;&#39;a&gt; {
    title: &amp;&#39;a str,
    pinned: bool,
}

fn pinned_titles(entries: &amp;[Entry&lt;&#39;_&gt;]) -&gt; Vec&lt;&amp;str&gt; {
    entries.iter()
        .filter(|entry| entry.pinned)
        .map(|entry| entry.title)
        .collect()
}
</code></pre>
<h3>Go</h3>
<pre><code class="language-go">func renderAll(ctx context.Context, slugs []string) &lt;-chan Result {
	results := make(chan Result)
	go func() {
		defer close(results)
		for _, slug := range slugs {
			results &lt;- render(ctx, slug)
		}
	}()
	return results
}
</code></pre>
<h3>SQL</h3>
<pre><code class="language-sql">WITH ranked_posts AS (
  SELECT
    title,
    topic,
    ROW_NUMBER() OVER (PARTITION BY topic ORDER BY published_at DESC) AS rank
  FROM posts
  WHERE status = &#39;published&#39;
)
SELECT title, topic
FROM ranked_posts
WHERE rank &lt;= 3;
</code></pre>
<h3>Swift</h3>
<pre><code class="language-swift">actor ReadingProgress {
    private var offsets: [URL: Double] = [:]

    func save(_ offset: Double, for article: URL) {
        offsets[article] = max(0, offset)
    }

    func restore(for article: URL) -&gt; Double {
        offsets[article, default: 0]
    }
}
</code></pre>
<h3>C#</h3>
<pre><code class="language-c#">public record Article(string Title, DateOnly Published);

public static class Archive
{
    public static IEnumerable&lt;string&gt; Titles(IEnumerable&lt;Article&gt; articles) =&gt;
        articles.OrderByDescending(a =&gt; a.Published).Select(a =&gt; a.Title);
}
</code></pre>
<h3>C++</h3>
<pre><code class="language-c++">#include &lt;string&gt;
#include &lt;vector&gt;

struct Article {
    std::string title;
    int words;
};

int totalWords(const std::vector&lt;Article&gt;&amp; articles) {
    int total = 0;
    for (const auto&amp; article : articles) total += article.words;
    return total;
}
</code></pre>
<h3>Shell</h3>
<pre><code class="language-sh">npx garden check &amp;&amp; npm run build
</code></pre>
<pre><code class="language-bash">for file in content/posts/*.md; do
  wc -m &quot;$file&quot;
done
</code></pre>
<h2>Diff、图片与原生 HTML</h2>
<pre><code class="language-diff">- const publishing = &quot;copy and paste&quot;;
+ const publishing = &quot;content as source&quot;;
</code></pre>
<p><img src="https://images.unsplash.com/photo-1501525771695-688643efeea4?auto=format&fit=crop&w=1400&q=82" alt="木桌上的笔记本与钢笔"></p>
<details>
  <summary>展开原生 HTML 内容</summary>
  <p>Typora 文件中的 details / summary 会保留为可交互的折叠区域。</p>
</details>

<pre><code class="language-garden">code: |
  &lt;p class=&quot;garden-html-probe&quot;&gt;静态 HTML 块&lt;/p&gt;
</code></pre>
<pre><code class="language-garden">items:
  - &quot;**程序化**：结构化、强状态、可以穷举的场景。&quot;
  - &quot;大模型: 非结构化、需要理解语境的场景。&quot;
</code></pre>
<hr>
<p>最后用一个脚注说明这篇文章的角色：它既是示例，也是未来升级 Markdown 依赖时的回归测试。[^regression]</p>
<p>[^regression]: 页面构建测试会检查公式与示例内容，浏览器验收会检查 Mermaid 是否被转换为 SVG。</p>
]]></content:encoded>
    </item>
    <item>
      <title>雨后的城市</title>
      <link>https://hd-garden-pr74.pages.dev/thoughts/rainy-night/</link>
      <guid isPermaLink="true">https://hd-garden-pr74.pages.dev/thoughts/rainy-night/</guid>
      <pubDate>Fri, 24 Jul 2026 14:36:00 GMT</pubDate>
      <description>下过雨的路面会把城市复制一遍。上面那个版本负责赶路，下面那个版本只负责发光。 这一条随想故意把三张照片、一段声音、一段视频和一个链接放在一起，用来确认日常记录不需要被单一媒体类型限制。</description>
      <category>摄影</category>
      <content:encoded><![CDATA[<p>下过雨的路面会把城市复制一遍。上面那个版本负责赶路，下面那个版本只负责发光。</p>
<p>这一条随想故意把三张照片、一段声音、一段视频和一个链接放在一起，用来确认日常记录不需要被单一媒体类型限制。</p>
]]></content:encoded>
    </item>
    <item>
      <title>今天的声音</title>
      <link>https://hd-garden-pr74.pages.dev/thoughts/field-recording/</link>
      <guid isPermaLink="true">https://hd-garden-pr74.pages.dev/thoughts/field-recording/</guid>
      <pubDate>Wed, 22 Jul 2026 10:10:00 GMT</pubDate>
      <description>写代码时循环播放的一段声音。相比歌单，我最近更喜欢没有明确歌词的背景，它不会替思考决定方向。</description>
      <category>声音</category>
      <content:encoded><![CDATA[<p>写代码时循环播放的一段声音。相比歌单，我最近更喜欢没有明确歌词的背景，它不会替思考决定方向。</p>
]]></content:encoded>
    </item>
    <item>
      <title>注意力也是一种界面</title>
      <link>https://hd-garden-pr74.pages.dev/blog/attention-is-an-interface/</link>
      <guid isPermaLink="true">https://hd-garden-pr74.pages.dev/blog/attention-is-an-interface/</guid>
      <pubDate>Mon, 20 Jul 2026 16:00:00 GMT</pubDate>
      <description>当工具越来越聪明，真正稀缺的不是功能，而是我们把注意力放在哪里。</description>
      <category>产品设计</category><category>注意力</category>
      <content:encoded><![CDATA[<p>工具总是在争取更短的路径：少一次点击、少一个页面、少等一秒。但如果只把效率当作终点，我们很容易做出一种<strong>没有摩擦，也没有方向</strong>的界面。</p>
<blockquote>
<p>好的界面不只是让事情更快发生，也应该帮助人知道什么值得发生。</p>
</blockquote>
<h2>界面在分配什么</h2>
<p>每一个高亮、通知和默认选项，都在替用户分配注意力。它们看上去只是视觉决策，实际上却在参与判断。</p>
<table>
<thead>
<tr>
<th>界面动作</th>
<th>隐含的决定</th>
</tr>
</thead>
<tbody><tr>
<td>默认展开</td>
<td>这件事更值得先看</td>
</tr>
<tr>
<td>红色角标</td>
<td>这件事不能等</td>
</tr>
<tr>
<td>无限滚动</td>
<td>这里没有自然的终点</td>
</tr>
</tbody></table>
<h2>摩擦不是敌人</h2>
<p>有些摩擦应该被删掉，例如重复输入和无意义的确认；另一些摩擦则应该被保留，例如发布前的停顿、删除前的复核，以及阅读长文时清晰的段落边界。</p>
<pre><code class="language-ts">type Friction = {
  cost: number;
  protectsIntent: boolean;
};

const shouldRemove = (friction: Friction) =&gt;
  friction.cost &gt; 0 &amp;&amp; !friction.protectsIntent;
</code></pre>
<h2>把节奏设计进去</h2>
<p>我现在更愿意先问三个问题：</p>
<ol>
<li>用户来到这里时，最需要看见什么？</li>
<li>哪些信息可以晚一点出现？</li>
<li>这个流程应该在哪里自然结束？</li>
</ol>
<p>这些问题没有让界面变得更复杂，反而让许多元素失去了存在的理由。</p>
<h2>最后的判断</h2>
<p>注意力不是取之不尽的资源。产品真正的克制，不是少用一种颜色，而是少向用户索取一次不必要的注意。</p>
<p>脚注也应该像在 Typora 中一样自然工作。<a href="%E8%BF%99%E4%B9%9F%E6%98%AF%E6%9C%AC%E7%AB%99%E9%80%89%E6%8B%A9%E5%AE%89%E9%9D%99%E6%8E%92%E7%89%88%E3%80%81%E6%9C%89%E9%99%90%E5%BC%BA%E8%B0%83%E8%89%B2%E5%92%8C%E6%98%8E%E7%A1%AE%E9%98%85%E8%AF%BB%E8%BE%B9%E7%95%8C%E7%9A%84%E5%8E%9F%E5%9B%A0%E3%80%82">^attention</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>慢一点看</title>
      <link>https://hd-garden-pr74.pages.dev/thoughts/slow-motion/</link>
      <guid isPermaLink="true">https://hd-garden-pr74.pages.dev/thoughts/slow-motion/</guid>
      <pubDate>Sun, 19 Jul 2026 01:20:00 GMT</pubDate>
      <description>很多细节不是不存在，只是平常的速度太快了。</description>
      <category>影像</category>
      <content:encoded><![CDATA[<p>很多细节不是不存在，只是平常的速度太快了。</p>
]]></content:encoded>
    </item>
    <item>
      <title>03 · 带着证据发布</title>
      <link>https://hd-garden-pr74.pages.dev/blog/building-in-public/ship-with-evidence/</link>
      <guid isPermaLink="true">https://hd-garden-pr74.pages.dev/blog/building-in-public/ship-with-evidence/</guid>
      <pubDate>Fri, 17 Jul 2026 16:00:00 GMT</pubDate>
      <description>“我改完了”不是完成，“用户可以确认它好了”才是。</description>
      <category>验证</category><category>发布</category>
      <content:encoded><![CDATA[<p>发布前最重要的工作不是再写一段漂亮总结，而是找到足以支持结论的证据。</p>
<h2>验证真实路径</h2>
<p>测试最小单元很重要，但用户真正经历的是一条完整路径。至少有一次验证应该从入口走到结果。</p>
<h2>不隐藏边界</h2>
<p>没有验证的部分要明确说出来。诚实的边界比虚假的完整更有价值。</p>
<h2>留下下一步</h2>
<p>一次发布应该让下一次开发更容易开始，而不是重新理解所有上下文。</p>
]]></content:encoded>
    </item>
    <item>
      <title>链接不是收藏，是路径</title>
      <link>https://hd-garden-pr74.pages.dev/thoughts/links-are-paths/</link>
      <guid isPermaLink="true">https://hd-garden-pr74.pages.dev/thoughts/links-are-paths/</guid>
      <pubDate>Thu, 16 Jul 2026 04:05:00 GMT</pubDate>
      <description>想把最近读到的好东西放回来。收藏夹很容易变成仓库，链接出现在上下文里，才重新变成一条路。</description>
      <category>阅读</category>
      <content:encoded><![CDATA[<p>想把最近读到的好东西放回来。收藏夹很容易变成仓库，链接出现在上下文里，才重新变成一条路。</p>
]]></content:encoded>
    </item>
    <item>
      <title>我为什么重新选择本地写作</title>
      <link>https://hd-garden-pr74.pages.dev/blog/local-first-writing/</link>
      <guid isPermaLink="true">https://hd-garden-pr74.pages.dev/blog/local-first-writing/</guid>
      <pubDate>Sat, 11 Jul 2026 16:00:00 GMT</pubDate>
      <description>Markdown 文件、可迁移的媒体资源，以及不被平台锁住的长期写作。</description>
      <category>Markdown</category><category>Local-first</category>
      <content:encoded><![CDATA[<p>我仍然在 Typora 里写下第一版。不是因为它有最多的功能，而是因为一个普通的 Markdown 文件让我知道：十年后，我仍然能打开这些文字。</p>
<h2>文件就是作品</h2>
<p>文章不应该只存在于某个编辑器的数据库里。标题、发布日期和主题放进 YAML front matter，正文保持标准 Markdown，图片则和文章一起归档。</p>
<h2>发布只是一次编译</h2>
<p>写作和发布可以分离。写作阶段只关心内容；网站读取文件，生成目录、搜索索引和 RSS。</p>
<ul>
<li><input checked="" disabled="" type="checkbox"> 在 Typora 中完成正文</li>
<li><input checked="" disabled="" type="checkbox"> 提交 Markdown 与媒体资源</li>
<li><input disabled="" type="checkbox"> 发布后继续修正</li>
</ul>
<h2>可迁移才有长期主义</h2>
<p>真正的长期主义不是选中一个“永远不会倒闭”的平台，而是让迁移成本始终足够低。</p>
]]></content:encoded>
    </item>
    <item>
      <title>02 · 让状态变得可靠</title>
      <link>https://hd-garden-pr74.pages.dev/blog/building-in-public/stable-state/</link>
      <guid isPermaLink="true">https://hd-garden-pr74.pages.dev/blog/building-in-public/stable-state/</guid>
      <pubDate>Thu, 09 Jul 2026 16:00:00 GMT</pubDate>
      <description>状态设计的关键，是明确谁拥有事实。</description>
      <category>状态管理</category>
      <content:encoded><![CDATA[<p>状态问题很少只是“少刷新了一次”。更常见的原因是多个地方都以为自己拥有最终事实。</p>
<h2>唯一事实来源</h2>
<p>先确定数据由谁拥有，再决定界面如何派生。临时交互状态可以留在视图，持久事实必须有明确边界。</p>
<h2>乐观更新</h2>
<p>乐观更新让操作显得即时，但失败时必须能够回滚，并且不能让选择状态丢失。</p>
<h2>可恢复</h2>
<p>真正可靠的状态不是永不失败，而是在失败以后仍然知道应该回到哪里。</p>
]]></content:encoded>
    </item>
    <item>
      <title>01 · 从最小闭环开始</title>
      <link>https://hd-garden-pr74.pages.dev/blog/building-in-public/smallest-closed-loop/</link>
      <guid isPermaLink="true">https://hd-garden-pr74.pages.dev/blog/building-in-public/smallest-closed-loop/</guid>
      <pubDate>Fri, 03 Jul 2026 16:00:00 GMT</pubDate>
      <description>先让最重要的一条路径真正跑通。</description>
      <category>产品</category><category>工程</category>
      <content:encoded><![CDATA[<p>一个人的时间有限，所以“完整”必须被重新定义。对我来说，完整不是一次做完所有功能，而是先让最重要的一条路径首尾相接。</p>
<h2>什么是最小闭环</h2>
<p>它必须包含触发、完成和反馈。只有按钮不算闭环，只有数据结构也不算。</p>
<h2>把想法切成动作</h2>
<p>以一个文件浏览器为例，第一步不是标签、搜索或批量操作，而是：</p>
<ol>
<li>创建文件夹；</li>
<li>创建 Markdown 文件；</li>
<li>刷新列表；</li>
<li>自动选中新项目。</li>
</ol>
<h2>可观察的完成</h2>
<p>闭环需要能被人看见，也能被测试验证。每次完成之后，都应该留下足够明确的证据。</p>
<h2>下一步</h2>
<p>当闭环成立，再进入命名、删除和上下文菜单。功能扩展不再是猜测，而是沿着已经成立的路径自然生长。</p>
]]></content:encoded>
    </item>
    <item>
      <title>做一个 PDF 阅读器之后，我重新理解了「位置」</title>
      <link>https://hd-garden-pr74.pages.dev/blog/building-a-pdf-reader/</link>
      <guid isPermaLink="true">https://hd-garden-pr74.pages.dev/blog/building-a-pdf-reader/</guid>
      <pubDate>Sat, 27 Jun 2026 16:00:00 GMT</pubDate>
      <description>阅读位置不是页码，而是一段连续体验的锚点。</description>
      <category>SwiftUI</category><category>阅读体验</category>
      <content:encoded><![CDATA[<p>“打开侧栏不能让正文跳一下”看起来是一条微小的要求，真正实现时却牵动了布局、缩放、滚动和状态恢复。</p>
<h2>页码不等于位置</h2>
<p>同一页里也有阅读进度。只记录页码，恢复后仍然可能让人失去上下文。</p>
<h2>变化前先保存锚点</h2>
<p>布局变化之前，保存视口中心所对应的文档坐标；布局完成以后，再把这个坐标映射回新的视口。</p>
<h2>两条交互路径</h2>
<p>侧栏开关是离散变化，分隔线拖拽是连续变化。它们应该共享“保持锚点”的原则，但不应该共享完全相同的更新频率。</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
