<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Macos on Tarragon</title><link>https://tarrragon.github.io/blog/tags/macos/</link><description>Recent content in Macos on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 29 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/macos/index.xml" rel="self" type="application/rss+xml"/><item><title>Homebrew</title><link>https://tarrragon.github.io/blog/llm/knowledge-cards/homebrew/</link><pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/llm/knowledge-cards/homebrew/</guid><description>&lt;p>Homebrew 的核心概念是「macOS 的社群套件管理器、用 &lt;code>brew install&lt;/code> 一行裝完 CLI 工具或 GUI 程式」。對本地 LLM 場景的角色是「Ollama、llama.cpp 等命令列工具的標準安裝入口」、把編譯、依賴管理、PATH 設定、二進位放置位置都自動化。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Homebrew 在 macOS 跟使用者要安裝的工具之間、扮演「公開 registry + 本地套件管理」的角色。它維護一份名為「formula」的 Ruby 腳本清單、每個 formula 描述某個工具怎麼下載、編譯、安裝。執行 &lt;code>brew install ollama&lt;/code> 時、Homebrew 找到 ollama formula、下載對應 bottle（預編譯二進位）、放到 &lt;code>/opt/homebrew/&lt;/code>（Apple Silicon）或 &lt;code>/usr/local/&lt;/code>（Intel Mac）、再把可執行檔 symlink 到 &lt;code>/opt/homebrew/bin/&lt;/code>。新機從零的完整安裝順序（含第一次裝 Homebrew、PATH 設定與晶片前綴差異）見 &lt;a href="https://tarrragon.github.io/blog/other/macos-%E6%96%B0%E6%A9%9F%E5%9F%BA%E7%A4%8E%E5%BB%BA%E8%A8%AD%E5%A5%97%E4%BB%B6%E7%AE%A1%E7%90%86%E8%88%87%E5%80%8B%E4%BA%BA-bin-%E7%9A%84%E8%A8%AD%E5%AE%9A%E9%A0%86%E5%BA%8F/" data-link-title="macOS 新機基礎建設：套件管理與個人 bin 的設定順序" data-link-desc="重灌或換機後底層基礎建設的依賴順序，免得後面工具裝不起來或路徑互相找不到。">macOS 新機基礎建設&lt;/a>。&lt;/p>
&lt;p>&lt;code>brew services&lt;/code> 是 Homebrew 附帶的服務管理子命令、把指令封裝成 macOS 原生的 &lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/launchd-service/" data-link-title="launchd Service" data-link-desc="macOS 原生的服務管理機制、把 process 註冊成自動啟動的 daemon 或 agent">launchd service&lt;/a>、處理「開機自動啟動 / 停止 / 重啟」需求。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>日常會碰到的 brew 指令：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>指令&lt;/th>
 &lt;th>用途&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>brew install &amp;lt;pkg&amp;gt;&lt;/code>&lt;/td>
 &lt;td>安裝套件&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>brew upgrade &amp;lt;pkg&amp;gt;&lt;/code>&lt;/td>
 &lt;td>升級單一套件&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>brew services start&lt;/code>&lt;/td>
 &lt;td>把套件註冊成 launchd service、立刻啟動&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>brew services list&lt;/code>&lt;/td>
 &lt;td>列出目前由 brew 管理的常駐服務&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>which &amp;lt;bin&amp;gt;&lt;/code>&lt;/td>
 &lt;td>確認可執行檔在 PATH 上的實際路徑&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>brew --prefix&lt;/code>&lt;/td>
 &lt;td>查 Homebrew 的安裝根目錄&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Apple Silicon Mac 上的關鍵路徑是 &lt;code>/opt/homebrew/&lt;/code>、子資料夾各有角色：&lt;code>bin/&lt;/code>（可執行檔）、&lt;code>var/log/&lt;/code>（服務 log）、&lt;code>Cellar/&lt;/code>（套件實際內容）、&lt;code>opt/&lt;/code>（版本無關的 symlink）。看到「&lt;code>/opt/homebrew/var/log/ollama.log&lt;/code>」時、就是 brew 管理的 Ollama 服務 log 位置。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>用 brew 安裝 vs 用官方 .dmg / .pkg 的取捨：CLI 工具（ollama、llama.cpp、git 等）走 brew、好處是統一升級路徑；GUI 應用（LM Studio、Docker Desktop 等）多半改下載官方安裝包、因為 brew cask 不一定即時跟上版本。第一次裝 Homebrew 自己用官方 install script（在 &lt;a href="https://brew.sh">brew.sh&lt;/a>）、之後其他工具都從 brew 走。&lt;/p></description><content:encoded><![CDATA[<p>Homebrew 的核心概念是「macOS 的社群套件管理器、用 <code>brew install</code> 一行裝完 CLI 工具或 GUI 程式」。對本地 LLM 場景的角色是「Ollama、llama.cpp 等命令列工具的標準安裝入口」、把編譯、依賴管理、PATH 設定、二進位放置位置都自動化。</p>
<h2 id="概念位置">概念位置</h2>
<p>Homebrew 在 macOS 跟使用者要安裝的工具之間、扮演「公開 registry + 本地套件管理」的角色。它維護一份名為「formula」的 Ruby 腳本清單、每個 formula 描述某個工具怎麼下載、編譯、安裝。執行 <code>brew install ollama</code> 時、Homebrew 找到 ollama formula、下載對應 bottle（預編譯二進位）、放到 <code>/opt/homebrew/</code>（Apple Silicon）或 <code>/usr/local/</code>（Intel Mac）、再把可執行檔 symlink 到 <code>/opt/homebrew/bin/</code>。新機從零的完整安裝順序（含第一次裝 Homebrew、PATH 設定與晶片前綴差異）見 <a href="/blog/other/macos-%E6%96%B0%E6%A9%9F%E5%9F%BA%E7%A4%8E%E5%BB%BA%E8%A8%AD%E5%A5%97%E4%BB%B6%E7%AE%A1%E7%90%86%E8%88%87%E5%80%8B%E4%BA%BA-bin-%E7%9A%84%E8%A8%AD%E5%AE%9A%E9%A0%86%E5%BA%8F/" data-link-title="macOS 新機基礎建設：套件管理與個人 bin 的設定順序" data-link-desc="重灌或換機後底層基礎建設的依賴順序，免得後面工具裝不起來或路徑互相找不到。">macOS 新機基礎建設</a>。</p>
<p><code>brew services</code> 是 Homebrew 附帶的服務管理子命令、把指令封裝成 macOS 原生的 <a href="/blog/llm/knowledge-cards/launchd-service/" data-link-title="launchd Service" data-link-desc="macOS 原生的服務管理機制、把 process 註冊成自動啟動的 daemon 或 agent">launchd service</a>、處理「開機自動啟動 / 停止 / 重啟」需求。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>日常會碰到的 brew 指令：</p>
<table>
  <thead>
      <tr>
          <th>指令</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>brew install &lt;pkg&gt;</code></td>
          <td>安裝套件</td>
      </tr>
      <tr>
          <td><code>brew upgrade &lt;pkg&gt;</code></td>
          <td>升級單一套件</td>
      </tr>
      <tr>
          <td><code>brew services start</code></td>
          <td>把套件註冊成 launchd service、立刻啟動</td>
      </tr>
      <tr>
          <td><code>brew services list</code></td>
          <td>列出目前由 brew 管理的常駐服務</td>
      </tr>
      <tr>
          <td><code>which &lt;bin&gt;</code></td>
          <td>確認可執行檔在 PATH 上的實際路徑</td>
      </tr>
      <tr>
          <td><code>brew --prefix</code></td>
          <td>查 Homebrew 的安裝根目錄</td>
      </tr>
  </tbody>
</table>
<p>Apple Silicon Mac 上的關鍵路徑是 <code>/opt/homebrew/</code>、子資料夾各有角色：<code>bin/</code>（可執行檔）、<code>var/log/</code>（服務 log）、<code>Cellar/</code>（套件實際內容）、<code>opt/</code>（版本無關的 symlink）。看到「<code>/opt/homebrew/var/log/ollama.log</code>」時、就是 brew 管理的 Ollama 服務 log 位置。</p>
<h2 id="設計責任">設計責任</h2>
<p>用 brew 安裝 vs 用官方 .dmg / .pkg 的取捨：CLI 工具（ollama、llama.cpp、git 等）走 brew、好處是統一升級路徑；GUI 應用（LM Studio、Docker Desktop 等）多半改下載官方安裝包、因為 brew cask 不一定即時跟上版本。第一次裝 Homebrew 自己用官方 install script（在 <a href="https://brew.sh">brew.sh</a>）、之後其他工具都從 brew 走。</p>
]]></content:encoded></item><item><title>launchd Service</title><link>https://tarrragon.github.io/blog/llm/knowledge-cards/launchd-service/</link><pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/llm/knowledge-cards/launchd-service/</guid><description>&lt;p>launchd Service 的核心概念是「macOS 用來管理常駐 process 生命週期的原生機制」。launchd 本身是 macOS 啟動後的第一個 process（PID 1）、由它負責拉起其他系統服務跟使用者註冊的背景任務。本地 LLM 場景中、Ollama 等推論伺服器透過 launchd 設定成「開機自動啟動、登入時自動拉起」、就不需要每次重開機都手動跑 &lt;code>ollama serve&lt;/code>。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>launchd service 用一份 plist（property list、XML 格式設定檔）描述「要跑哪個程式、何時啟動、出問題時要不要重啟、log 寫到哪裡」。plist 放在三個位置之一、決定服務的觸發範圍：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>路徑&lt;/th>
 &lt;th>角色&lt;/th>
 &lt;th>何時觸發&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>~/Library/LaunchAgents/&lt;/code>&lt;/td>
 &lt;td>使用者 agent&lt;/td>
 &lt;td>該使用者登入時&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>/Library/LaunchAgents/&lt;/code>&lt;/td>
 &lt;td>全機所有使用者 agent&lt;/td>
 &lt;td>任何使用者登入時&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>/Library/LaunchDaemons/&lt;/code>&lt;/td>
 &lt;td>系統 daemon、需 root&lt;/td>
 &lt;td>macOS 開機時、不需登入&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/homebrew/" data-link-title="Homebrew" data-link-desc="macOS 上社群維護的套件管理器、用一行指令安裝 CLI 工具與背景服務">Homebrew&lt;/a> 的 &lt;code>brew services&lt;/code> 子命令是 launchd 的 wrapper、產生 plist 並放進 &lt;code>~/Library/LaunchAgents/&lt;/code>、避免使用者直接手寫 XML。Apple Silicon Mac 上產生的檔名形式是 &lt;code>homebrew.mxcl.&amp;lt;service&amp;gt;.plist&lt;/code>。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>執行 &lt;code>brew services start ollama&lt;/code> 後可以驗證實際發生的事：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 看 plist 內容&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">cat ~/Library/LaunchAgents/homebrew.mxcl.ollama.plist
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 用 launchctl 看服務狀態&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">launchctl list &lt;span class="p">|&lt;/span> grep ollama
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 看服務 log（Apple Silicon）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">tail -f /opt/homebrew/var/log/ollama.log&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>plist 內常見的鍵：&lt;code>ProgramArguments&lt;/code>（要跑哪個指令）、&lt;code>RunAtLoad&lt;/code>（開機就啟動）、&lt;code>KeepAlive&lt;/code>（crash 後自動拉回）、&lt;code>StandardOutPath&lt;/code> / &lt;code>StandardErrorPath&lt;/code>（log 路徑）。出問題時先看 log 路徑指向的檔案、能直接看到 service 的 stdout / stderr。&lt;/p>
&lt;p>服務管理常用指令：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">brew services list &lt;span class="c1"># 列出所有由 brew 管理的服務&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">brew services start ollama &lt;span class="c1"># 啟動 + 註冊自動啟動&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">brew services stop ollama &lt;span class="c1"># 停掉服務、保留 plist&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">brew services restart ollama &lt;span class="c1"># 升級套件後重啟&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>直接用系統的 &lt;code>launchctl&lt;/code> 也行、但語意較底層、實務上有 brew 包裝就用 brew。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>選擇「launchd service」vs「前景手動跑 &lt;code>ollama serve&lt;/code>」的判讀：日常用機建議用 launchd service、好處是重開機自動拉起、出問題的 log 有固定位置可看；只在偶爾用本地 LLM 的場景、保持手動跑反而省記憶體（沒在用就停掉）。升級套件後記得 &lt;code>brew services restart&lt;/code>、否則跑的還是舊版二進位。&lt;/p></description><content:encoded><![CDATA[<p>launchd Service 的核心概念是「macOS 用來管理常駐 process 生命週期的原生機制」。launchd 本身是 macOS 啟動後的第一個 process（PID 1）、由它負責拉起其他系統服務跟使用者註冊的背景任務。本地 LLM 場景中、Ollama 等推論伺服器透過 launchd 設定成「開機自動啟動、登入時自動拉起」、就不需要每次重開機都手動跑 <code>ollama serve</code>。</p>
<h2 id="概念位置">概念位置</h2>
<p>launchd service 用一份 plist（property list、XML 格式設定檔）描述「要跑哪個程式、何時啟動、出問題時要不要重啟、log 寫到哪裡」。plist 放在三個位置之一、決定服務的觸發範圍：</p>
<table>
  <thead>
      <tr>
          <th>路徑</th>
          <th>角色</th>
          <th>何時觸發</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>~/Library/LaunchAgents/</code></td>
          <td>使用者 agent</td>
          <td>該使用者登入時</td>
      </tr>
      <tr>
          <td><code>/Library/LaunchAgents/</code></td>
          <td>全機所有使用者 agent</td>
          <td>任何使用者登入時</td>
      </tr>
      <tr>
          <td><code>/Library/LaunchDaemons/</code></td>
          <td>系統 daemon、需 root</td>
          <td>macOS 開機時、不需登入</td>
      </tr>
  </tbody>
</table>
<p><a href="/blog/llm/knowledge-cards/homebrew/" data-link-title="Homebrew" data-link-desc="macOS 上社群維護的套件管理器、用一行指令安裝 CLI 工具與背景服務">Homebrew</a> 的 <code>brew services</code> 子命令是 launchd 的 wrapper、產生 plist 並放進 <code>~/Library/LaunchAgents/</code>、避免使用者直接手寫 XML。Apple Silicon Mac 上產生的檔名形式是 <code>homebrew.mxcl.&lt;service&gt;.plist</code>。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>執行 <code>brew services start ollama</code> 後可以驗證實際發生的事：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 看 plist 內容</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">cat ~/Library/LaunchAgents/homebrew.mxcl.ollama.plist
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 用 launchctl 看服務狀態</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">launchctl list <span class="p">|</span> grep ollama
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 看服務 log（Apple Silicon）</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">tail -f /opt/homebrew/var/log/ollama.log</span></span></code></pre></div><p>plist 內常見的鍵：<code>ProgramArguments</code>（要跑哪個指令）、<code>RunAtLoad</code>（開機就啟動）、<code>KeepAlive</code>（crash 後自動拉回）、<code>StandardOutPath</code> / <code>StandardErrorPath</code>（log 路徑）。出問題時先看 log 路徑指向的檔案、能直接看到 service 的 stdout / stderr。</p>
<p>服務管理常用指令：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">brew services list             <span class="c1"># 列出所有由 brew 管理的服務</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">brew services start ollama     <span class="c1"># 啟動 + 註冊自動啟動</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">brew services stop ollama      <span class="c1"># 停掉服務、保留 plist</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">brew services restart ollama   <span class="c1"># 升級套件後重啟</span></span></span></code></pre></div><p>直接用系統的 <code>launchctl</code> 也行、但語意較底層、實務上有 brew 包裝就用 brew。</p>
<h2 id="設計責任">設計責任</h2>
<p>選擇「launchd service」vs「前景手動跑 <code>ollama serve</code>」的判讀：日常用機建議用 launchd service、好處是重開機自動拉起、出問題的 log 有固定位置可看；只在偶爾用本地 LLM 的場景、保持手動跑反而省記憶體（沒在用就停掉）。升級套件後記得 <code>brew services restart</code>、否則跑的還是舊版二進位。</p>
]]></content:encoded></item><item><title>Shell 背景 Process</title><link>https://tarrragon.github.io/blog/llm/knowledge-cards/shell-background-process/</link><pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/llm/knowledge-cards/shell-background-process/</guid><description>&lt;p>Shell 背景 Process 的核心概念是「terminal 啟動的程式何時跟 shell 綁定、何時可以脫離、被 shell 用什麼方式管理」。本地 LLM 場景中、&lt;code>ollama serve&lt;/code> 這類常駐 server 需要持續跑、放前景會把 terminal 卡住、放背景才能繼續打其他指令、或關掉 terminal 後讓服務改交給 &lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/launchd-service/" data-link-title="launchd Service" data-link-desc="macOS 原生的服務管理機制、把 process 註冊成自動啟動的 daemon 或 agent">launchd service&lt;/a> 接手。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Shell（zsh / bash）執行一個程式時、預設讓程式佔住 terminal、stdin / stdout / stderr 直接連到使用者眼前的視窗、稱為&lt;strong>前景 process&lt;/strong>。指令尾巴加 &lt;code>&amp;amp;&lt;/code> 改成&lt;strong>背景 process&lt;/strong>、shell 立刻拿回 prompt 控制權、程式繼續跑但不佔住 terminal。背景 process 仍綁在當前 shell session、關掉 terminal 視窗時通常會被 SIGHUP 終止；要完全脫離 shell 生命週期、得改用 &lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/launchd-service/" data-link-title="launchd Service" data-link-desc="macOS 原生的服務管理機制、把 process 註冊成自動啟動的 daemon 或 agent">launchd service&lt;/a> 或 &lt;code>nohup&lt;/code> / &lt;code>disown&lt;/code> 等機制。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>shell 控制 process 的關鍵操作：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>動作&lt;/th>
 &lt;th>指令 / 按鍵&lt;/th>
 &lt;th>效果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>前景跑&lt;/td>
 &lt;td>&lt;code>ollama serve&lt;/code>&lt;/td>
 &lt;td>terminal 被卡住、看到 process stdout&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>背景跑&lt;/td>
 &lt;td>&lt;code>ollama serve &amp;amp;&lt;/code>&lt;/td>
 &lt;td>拿回 prompt、程式仍在跑&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>中止前景 process&lt;/td>
 &lt;td>&lt;code>Ctrl+C&lt;/code>&lt;/td>
 &lt;td>送 SIGINT、多數程式收到後優雅退出&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>暫停前景 process&lt;/td>
 &lt;td>&lt;code>Ctrl+Z&lt;/code>&lt;/td>
 &lt;td>送 SIGTSTP、process 進 stopped 狀態&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>列出當前 shell jobs&lt;/td>
 &lt;td>&lt;code>jobs&lt;/code>&lt;/td>
 &lt;td>看 shell 管理的背景 / 暫停 job&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>把 job 拉回前景&lt;/td>
 &lt;td>&lt;code>fg %1&lt;/code>&lt;/td>
 &lt;td>1 號 job 變前景&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>把暫停 job 改背景&lt;/td>
 &lt;td>&lt;code>bg %1&lt;/code>&lt;/td>
 &lt;td>1 號 job 改背景繼續跑&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>排錯常用的兩個工具（兩者跟 shell job 不直接相關、是 macOS 系統工具）：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>指令&lt;/th>
 &lt;th>用途&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>lsof -i :11434&lt;/code>&lt;/td>
 &lt;td>找出哪個 process 在聽 11434 &lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/port-and-localhost/" data-link-title="Port 與 Localhost" data-link-desc="TCP port 與 listen address 如何決定 API server 的對外暴露範圍">port&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>pkill -f &amp;quot;ollama serve&amp;quot;&lt;/code>&lt;/td>
 &lt;td>用 pattern 匹配 process 命令列、送 SIGTERM 終止&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>ps aux | grep ollama&lt;/code>&lt;/td>
 &lt;td>列出所有跟 ollama 有關的 process&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>對 macOS 新手最常遇到的兩個事故：一個是「前景跑 server 後不知道怎麼脫身」、解法是 &lt;code>Ctrl+Z&lt;/code> 暫停 + &lt;code>bg&lt;/code> 改背景、或下次改用 &lt;code>&amp;amp;&lt;/code> 啟動；另一個是「pkill 沒指定夠精確的 pattern、誤殺其他 process」、解法是先用 &lt;code>ps aux&lt;/code> 加 &lt;code>grep&lt;/code> 確認 PID 再 kill。&lt;/p></description><content:encoded><![CDATA[<p>Shell 背景 Process 的核心概念是「terminal 啟動的程式何時跟 shell 綁定、何時可以脫離、被 shell 用什麼方式管理」。本地 LLM 場景中、<code>ollama serve</code> 這類常駐 server 需要持續跑、放前景會把 terminal 卡住、放背景才能繼續打其他指令、或關掉 terminal 後讓服務改交給 <a href="/blog/llm/knowledge-cards/launchd-service/" data-link-title="launchd Service" data-link-desc="macOS 原生的服務管理機制、把 process 註冊成自動啟動的 daemon 或 agent">launchd service</a> 接手。</p>
<h2 id="概念位置">概念位置</h2>
<p>Shell（zsh / bash）執行一個程式時、預設讓程式佔住 terminal、stdin / stdout / stderr 直接連到使用者眼前的視窗、稱為<strong>前景 process</strong>。指令尾巴加 <code>&amp;</code> 改成<strong>背景 process</strong>、shell 立刻拿回 prompt 控制權、程式繼續跑但不佔住 terminal。背景 process 仍綁在當前 shell session、關掉 terminal 視窗時通常會被 SIGHUP 終止；要完全脫離 shell 生命週期、得改用 <a href="/blog/llm/knowledge-cards/launchd-service/" data-link-title="launchd Service" data-link-desc="macOS 原生的服務管理機制、把 process 註冊成自動啟動的 daemon 或 agent">launchd service</a> 或 <code>nohup</code> / <code>disown</code> 等機制。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>shell 控制 process 的關鍵操作：</p>
<table>
  <thead>
      <tr>
          <th>動作</th>
          <th>指令 / 按鍵</th>
          <th>效果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>前景跑</td>
          <td><code>ollama serve</code></td>
          <td>terminal 被卡住、看到 process stdout</td>
      </tr>
      <tr>
          <td>背景跑</td>
          <td><code>ollama serve &amp;</code></td>
          <td>拿回 prompt、程式仍在跑</td>
      </tr>
      <tr>
          <td>中止前景 process</td>
          <td><code>Ctrl+C</code></td>
          <td>送 SIGINT、多數程式收到後優雅退出</td>
      </tr>
      <tr>
          <td>暫停前景 process</td>
          <td><code>Ctrl+Z</code></td>
          <td>送 SIGTSTP、process 進 stopped 狀態</td>
      </tr>
      <tr>
          <td>列出當前 shell jobs</td>
          <td><code>jobs</code></td>
          <td>看 shell 管理的背景 / 暫停 job</td>
      </tr>
      <tr>
          <td>把 job 拉回前景</td>
          <td><code>fg %1</code></td>
          <td>1 號 job 變前景</td>
      </tr>
      <tr>
          <td>把暫停 job 改背景</td>
          <td><code>bg %1</code></td>
          <td>1 號 job 改背景繼續跑</td>
      </tr>
  </tbody>
</table>
<p>排錯常用的兩個工具（兩者跟 shell job 不直接相關、是 macOS 系統工具）：</p>
<table>
  <thead>
      <tr>
          <th>指令</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>lsof -i :11434</code></td>
          <td>找出哪個 process 在聽 11434 <a href="/blog/llm/knowledge-cards/port-and-localhost/" data-link-title="Port 與 Localhost" data-link-desc="TCP port 與 listen address 如何決定 API server 的對外暴露範圍">port</a></td>
      </tr>
      <tr>
          <td><code>pkill -f &quot;ollama serve&quot;</code></td>
          <td>用 pattern 匹配 process 命令列、送 SIGTERM 終止</td>
      </tr>
      <tr>
          <td><code>ps aux | grep ollama</code></td>
          <td>列出所有跟 ollama 有關的 process</td>
      </tr>
  </tbody>
</table>
<p>對 macOS 新手最常遇到的兩個事故：一個是「前景跑 server 後不知道怎麼脫身」、解法是 <code>Ctrl+Z</code> 暫停 + <code>bg</code> 改背景、或下次改用 <code>&amp;</code> 啟動；另一個是「pkill 沒指定夠精確的 pattern、誤殺其他 process」、解法是先用 <code>ps aux</code> 加 <code>grep</code> 確認 PID 再 kill。</p>
<h2 id="設計責任">設計責任</h2>
<p>選前景 vs 背景的判讀：debug 場景前景跑、能直接看到 log；日常使用改 <a href="/blog/llm/knowledge-cards/launchd-service/" data-link-title="launchd Service" data-link-desc="macOS 原生的服務管理機制、把 process 註冊成自動啟動的 daemon 或 agent">launchd service</a> 跑、跟 shell session 完全脫鉤。<code>&amp;</code> 適合「terminal 開著就讓它跑、關掉也沒關係」的臨時場景、不適合需要長期穩定的服務。排錯時養成「先 <code>lsof</code> 找誰佔資源、再 <code>ps</code> 確認身分、最後才 kill」的順序、避免誤殺。</p>
]]></content:encoded></item><item><title>macOS 視窗管理工具鏈</title><link>https://tarrragon.github.io/blog/linux/dotfile/04-window-management/macos-window-tools/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/04-window-management/macos-window-tools/</guid><description>&lt;p>macOS 的視窗系統由 WindowServer 控制，第三方工具能做的主要是「排列邏輯」——決定視窗的位置和大小。視覺效果（動畫、模糊、圓角）由系統控制，第三方工具改不了。這是跟 Linux tiling WM 最大的差異。&lt;/p>
&lt;h2 id="macos-原生-window-tilingmacos-15">macOS 原生 Window Tiling（macOS 15+）&lt;/h2>
&lt;p>macOS Sequoia（15，2024 年 9 月）內建了 window tiling 功能：鍵盤快捷鍵把視窗貼到螢幕的半邊或四分之一、拖拉到邊緣自動貼齊（edge snap）、相鄰視窗可以組成 tile group 一起調整比例。&lt;/p>
&lt;p>原生 tiling 的邊界：沒有多工作區管理、快捷鍵自訂空間有限（只能用系統偏好設定裡的固定選項）、不支援自動平鋪（仍然是手動觸發的 snap，不會在開新視窗時自動重排）。&lt;/p>
&lt;p>如果「貼到半邊 + 邊緣吸附」就足夠，原生功能免安裝即可使用。以下第三方工具解決的是原生功能做不到的事：更多排列選項（Rectangle）、自動平鋪（Amethyst）、完整的鍵盤工作流加多工作區（AeroSpace / yabai）。&lt;/p>
&lt;h2 id="rectangle">Rectangle&lt;/h2>
&lt;p>免費、開源。用快捷鍵把視窗貼到螢幕的半邊、三分之一、角落。不是自動平鋪——每個視窗都要你主動下指令。安裝後開箱即用，學習成本最低。&lt;/p>
&lt;p>適合的情境：只需要快速排版、不想花時間學新操作邏輯、偶爾分割就滿足需求。&lt;/p>
&lt;p>配置檔位置：&lt;code>~/Library/Preferences/com.knollsoft.Rectangle.plist&lt;/code>（macOS plist 格式，不太適合手動編輯，通常用 GUI 設定）。&lt;/p>
&lt;h2 id="amethyst">Amethyst&lt;/h2>
&lt;p>自動平鋪，安裝後視窗就會自動排列。提供多種 layout（tall, wide, fullscreen, column 等）可以用快捷鍵切換。設定比 Rectangle 多但比 yabai 少，是「想要自動平鋪但不想深度折騰」的選擇。&lt;/p>
&lt;p>配置檔：&lt;code>~/.amethyst.yml&lt;/code>，YAML 格式，可以版控。&lt;/p>
&lt;h2 id="aerospace">AeroSpace&lt;/h2>
&lt;p>近年最受歡迎的選擇。核心優勢是&lt;strong>不需要關閉 SIP&lt;/strong>（System Integrity Protection）——它用自己實作的虛擬工作區概念，不依賴 macOS 原生的 Spaces，因此繞過了很多系統層的限制。&lt;/p>
&lt;p>配置是純文字的 TOML 檔 &lt;code>~/.aerospace.toml&lt;/code>，改完即時生效。工作區模型靈活，多螢幕支援被普遍認為比 yabai 穩定。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># ~/.aerospace.toml 片段&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nx">after-startup-command&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;workspace 1&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">gaps&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="nx">inner&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">horizontal&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mi">10&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">inner&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">vertical&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mi">10&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">outer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">left&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mi">10&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">outer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">right&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mi">10&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">mode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">main&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">binding&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">alt-h&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;focus left&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">alt-j&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;focus down&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nx">alt-k&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;focus up&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="nx">alt-l&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;focus right&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="nx">alt-shift-h&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;move left&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nx">alt-shift-j&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;move down&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="nx">alt-shift-k&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;move up&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="nx">alt-shift-l&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;move right&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="nx">alt-1&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;workspace 1&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="nx">alt-2&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;workspace 2&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="nx">alt-3&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;workspace 3&amp;#39;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>配置結構直覺：gaps 控制視窗間距、binding 定義快捷鍵對應的動作。整份檔案進 dotfile repo 就能跨機器還原操作習慣。&lt;/p>
&lt;h2 id="yabai--skhd">yabai + skhd&lt;/h2>
&lt;p>功能最完整的 macOS tiling WM。yabai 負責視窗管理，skhd 負責快捷鍵綁定。支援 BSP（binary space partitioning）樹狀分割——每次開新視窗都是把現有空間二分，形成一棵樹，你可以操作樹的節點來旋轉、交換、調整比例。&lt;/p>
&lt;p>代價是部分進階功能（某些視窗操作、取消動畫）需要部分關閉 SIP。對某些人這是門檻，對另一些人不是問題。&lt;/p>
&lt;p>配置檔是 shell script：&lt;code>.yabairc&lt;/code>（yabai 設定）和 &lt;code>.skhdrc&lt;/code>（快捷鍵設定），進 dotfile repo 管理。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># .yabairc 片段&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">yabai -m config layout bsp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">yabai -m config window_gap &lt;span class="m">10&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">yabai -m config top_padding &lt;span class="m">10&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">yabai -m config bottom_padding &lt;span class="m">10&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 某些 app 不適合平鋪，設為浮動&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">yabai -m rule --add &lt;span class="nv">app&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;System Preferences&amp;#34;&lt;/span> &lt;span class="nv">manage&lt;/span>&lt;span class="o">=&lt;/span>off
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">yabai -m rule --add &lt;span class="nv">app&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Calculator&amp;#34;&lt;/span> &lt;span class="nv">manage&lt;/span>&lt;span class="o">=&lt;/span>off
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">yabai -m rule --add &lt;span class="nv">app&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Finder&amp;#34;&lt;/span> &lt;span class="nv">title&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Info&amp;#34;&lt;/span> &lt;span class="nv">manage&lt;/span>&lt;span class="o">=&lt;/span>off&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="選型判讀">選型判讀&lt;/h2>
&lt;p>選工具的判準不是「哪個最強」，而是「你願意花多少時間、想要多少控制權」。&lt;/p>
&lt;p>只需要快速排視窗、不想改工作習慣，Rectangle 足夠。想要自動平鋪但學習曲線要短，Amethyst 是進入點。想要完整的平鋪工作流、多工作區管理、純文字配置、又不想動系統安全設定，AeroSpace 是目前多數人推薦的首選。想要最大的控制權、願意處理 SIP 和更複雜的配置，yabai 給你最多彈性。&lt;/p>
&lt;p>從 Rectangle 跳到 AeroSpace 或 yabai 是一次操作思維的轉換——從「我指定每個視窗去哪」變成「我操作版面結構、WM 負責排列」。這個轉換需要一兩週的適應期，適應期內效率會暫時下降。&lt;/p></description><content:encoded><![CDATA[<p>macOS 的視窗系統由 WindowServer 控制，第三方工具能做的主要是「排列邏輯」——決定視窗的位置和大小。視覺效果（動畫、模糊、圓角）由系統控制，第三方工具改不了。這是跟 Linux tiling WM 最大的差異。</p>
<h2 id="macos-原生-window-tilingmacos-15">macOS 原生 Window Tiling（macOS 15+）</h2>
<p>macOS Sequoia（15，2024 年 9 月）內建了 window tiling 功能：鍵盤快捷鍵把視窗貼到螢幕的半邊或四分之一、拖拉到邊緣自動貼齊（edge snap）、相鄰視窗可以組成 tile group 一起調整比例。</p>
<p>原生 tiling 的邊界：沒有多工作區管理、快捷鍵自訂空間有限（只能用系統偏好設定裡的固定選項）、不支援自動平鋪（仍然是手動觸發的 snap，不會在開新視窗時自動重排）。</p>
<p>如果「貼到半邊 + 邊緣吸附」就足夠，原生功能免安裝即可使用。以下第三方工具解決的是原生功能做不到的事：更多排列選項（Rectangle）、自動平鋪（Amethyst）、完整的鍵盤工作流加多工作區（AeroSpace / yabai）。</p>
<h2 id="rectangle">Rectangle</h2>
<p>免費、開源。用快捷鍵把視窗貼到螢幕的半邊、三分之一、角落。不是自動平鋪——每個視窗都要你主動下指令。安裝後開箱即用，學習成本最低。</p>
<p>適合的情境：只需要快速排版、不想花時間學新操作邏輯、偶爾分割就滿足需求。</p>
<p>配置檔位置：<code>~/Library/Preferences/com.knollsoft.Rectangle.plist</code>（macOS plist 格式，不太適合手動編輯，通常用 GUI 設定）。</p>
<h2 id="amethyst">Amethyst</h2>
<p>自動平鋪，安裝後視窗就會自動排列。提供多種 layout（tall, wide, fullscreen, column 等）可以用快捷鍵切換。設定比 Rectangle 多但比 yabai 少，是「想要自動平鋪但不想深度折騰」的選擇。</p>
<p>配置檔：<code>~/.amethyst.yml</code>，YAML 格式，可以版控。</p>
<h2 id="aerospace">AeroSpace</h2>
<p>近年最受歡迎的選擇。核心優勢是<strong>不需要關閉 SIP</strong>（System Integrity Protection）——它用自己實作的虛擬工作區概念，不依賴 macOS 原生的 Spaces，因此繞過了很多系統層的限制。</p>
<p>配置是純文字的 TOML 檔 <code>~/.aerospace.toml</code>，改完即時生效。工作區模型靈活，多螢幕支援被普遍認為比 yabai 穩定。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># ~/.aerospace.toml 片段</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">after-startup-command</span> <span class="p">=</span> <span class="p">[</span><span class="s1">&#39;workspace 1&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="p">[</span><span class="nx">gaps</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">inner</span><span class="p">.</span><span class="nx">horizontal</span> <span class="p">=</span> <span class="mi">10</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">inner</span><span class="p">.</span><span class="nx">vertical</span> <span class="p">=</span> <span class="mi">10</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">outer</span><span class="p">.</span><span class="nx">left</span> <span class="p">=</span> <span class="mi">10</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">outer</span><span class="p">.</span><span class="nx">right</span> <span class="p">=</span> <span class="mi">10</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">[</span><span class="nx">mode</span><span class="p">.</span><span class="nx">main</span><span class="p">.</span><span class="nx">binding</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">alt-h</span> <span class="p">=</span> <span class="s1">&#39;focus left&#39;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">alt-j</span> <span class="p">=</span> <span class="s1">&#39;focus down&#39;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">alt-k</span> <span class="p">=</span> <span class="s1">&#39;focus up&#39;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="nx">alt-l</span> <span class="p">=</span> <span class="s1">&#39;focus right&#39;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">alt-shift-h</span> <span class="p">=</span> <span class="s1">&#39;move left&#39;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nx">alt-shift-j</span> <span class="p">=</span> <span class="s1">&#39;move down&#39;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="nx">alt-shift-k</span> <span class="p">=</span> <span class="s1">&#39;move up&#39;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="nx">alt-shift-l</span> <span class="p">=</span> <span class="s1">&#39;move right&#39;</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nx">alt-1</span> <span class="p">=</span> <span class="s1">&#39;workspace 1&#39;</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="nx">alt-2</span> <span class="p">=</span> <span class="s1">&#39;workspace 2&#39;</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="nx">alt-3</span> <span class="p">=</span> <span class="s1">&#39;workspace 3&#39;</span></span></span></code></pre></div><p>配置結構直覺：gaps 控制視窗間距、binding 定義快捷鍵對應的動作。整份檔案進 dotfile repo 就能跨機器還原操作習慣。</p>
<h2 id="yabai--skhd">yabai + skhd</h2>
<p>功能最完整的 macOS tiling WM。yabai 負責視窗管理，skhd 負責快捷鍵綁定。支援 BSP（binary space partitioning）樹狀分割——每次開新視窗都是把現有空間二分，形成一棵樹，你可以操作樹的節點來旋轉、交換、調整比例。</p>
<p>代價是部分進階功能（某些視窗操作、取消動畫）需要部分關閉 SIP。對某些人這是門檻，對另一些人不是問題。</p>
<p>配置檔是 shell script：<code>.yabairc</code>（yabai 設定）和 <code>.skhdrc</code>（快捷鍵設定），進 dotfile repo 管理。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># .yabairc 片段</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">yabai -m config layout bsp
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">yabai -m config window_gap <span class="m">10</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">yabai -m config top_padding <span class="m">10</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">yabai -m config bottom_padding <span class="m">10</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 某些 app 不適合平鋪，設為浮動</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">yabai -m rule --add <span class="nv">app</span><span class="o">=</span><span class="s2">&#34;System Preferences&#34;</span> <span class="nv">manage</span><span class="o">=</span>off
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">yabai -m rule --add <span class="nv">app</span><span class="o">=</span><span class="s2">&#34;Calculator&#34;</span> <span class="nv">manage</span><span class="o">=</span>off
</span></span><span class="line"><span class="ln">10</span><span class="cl">yabai -m rule --add <span class="nv">app</span><span class="o">=</span><span class="s2">&#34;Finder&#34;</span> <span class="nv">title</span><span class="o">=</span><span class="s2">&#34;Info&#34;</span> <span class="nv">manage</span><span class="o">=</span>off</span></span></code></pre></div><h2 id="選型判讀">選型判讀</h2>
<p>選工具的判準不是「哪個最強」，而是「你願意花多少時間、想要多少控制權」。</p>
<p>只需要快速排視窗、不想改工作習慣，Rectangle 足夠。想要自動平鋪但學習曲線要短，Amethyst 是進入點。想要完整的平鋪工作流、多工作區管理、純文字配置、又不想動系統安全設定，AeroSpace 是目前多數人推薦的首選。想要最大的控制權、願意處理 SIP 和更複雜的配置，yabai 給你最多彈性。</p>
<p>從 Rectangle 跳到 AeroSpace 或 yabai 是一次操作思維的轉換——從「我指定每個視窗去哪」變成「我操作版面結構、WM 負責排列」。這個轉換需要一兩週的適應期，適應期內效率會暫時下降。</p>
]]></content:encoded></item><item><title>Kando 滑鼠手勢選單的應用場景</title><link>https://tarrragon.github.io/blog/other/kando-%E6%BB%91%E9%BC%A0%E6%89%8B%E5%8B%A2%E9%81%B8%E5%96%AE%E7%9A%84%E6%87%89%E7%94%A8%E5%A0%B4%E6%99%AF/</link><pubDate>Sun, 28 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/other/kando-%E6%BB%91%E9%BC%A0%E6%89%8B%E5%8B%A2%E9%81%B8%E5%96%AE%E7%9A%84%E6%87%89%E7%94%A8%E5%A0%B4%E6%99%AF/</guid><description>&lt;h2 id="kando-是什麼">Kando 是什麼&lt;/h2>
&lt;p>Kando 是一個跨平台的**圓盤選單（pie menu / marking menu）**工具：綁一個快捷鍵後，選單會在游標位置彈出，用滑鼠往某個方向滑動或點擊就能選到項目。每個項目可以執行指令、模擬快捷鍵、開啟網址/檔案、貼上文字或串成巨集。&lt;/p>
&lt;ul>
&lt;li>專案：&lt;a href="https://github.com/kando-menu/kando">https://github.com/kando-menu/kando&lt;/a>&lt;/li>
&lt;li>使用說明：&lt;a href="https://kando.menu/usage/">https://kando.menu/usage/&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>它最關鍵的特性是&lt;strong>標記模式（marking mode）&lt;/strong>：選單結構固定後，可以靠肌肉記憶往某個方向「甩」一下盲選，不需要看著選單找項目。這跟「打字找東西」的啟動器（Raycast / Alfred / Spotlight）是不同的互動範式。&lt;/p>
&lt;hr>
&lt;h2 id="適用判準先判斷再設計">適用判準（先判斷再設計）&lt;/h2>
&lt;p>Kando 補的是一個特定空缺：&lt;strong>手已經在滑鼠/觸控板上、不想切回鍵盤、而且動作有空間對應&lt;/strong>。它跟鍵盤快捷鍵或啟動器是並存關係，不是替代。判斷一個動作該不該交給 Kando，用以下三條：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>動作數量超過快捷鍵好記的上限&lt;/strong>。同類動作一旦超過七八個，組合鍵就開始互相打架、記不住；圓盤選單用空間位置取代記憶。&lt;/li>
&lt;li>&lt;strong>當下手在滑鼠、切回鍵盤是中斷&lt;/strong>。瀏覽、看文件、看設計稿時，打字啟動器反而要先把手移開；圓盤選單原地彈出。&lt;/li>
&lt;li>&lt;strong>動作有方向語意，盲操作就能命中&lt;/strong>。例如「左半邊視窗」往左滑、「下一個螢幕」往外圈滑。有方向對應的動作練熟後可以完全不看選單。&lt;/li>
&lt;/ol>
&lt;p>三條不必同時滿足，但命中越多越適合。反過來說，低頻、無方向語意、又需要打字輸入參數的動作，留在啟動器或 CLI 比較好。&lt;/p>
&lt;hr>
&lt;h2 id="macos-工程師的應用場景">macOS 工程師的應用場景&lt;/h2>
&lt;p>以下幾組依「契合 Kando 的程度」排序，越前面方向語意越強、回報越快。&lt;/p>
&lt;h3 id="1-視窗排版最契合">1. 視窗排版（最契合）&lt;/h3>
&lt;p>方向天然對應位置，這是 Kando 比 Rectangle 純快捷鍵更直覺的地方：&lt;/p>
&lt;ul>
&lt;li>往左滑放左半螢幕、往右放右半、往上全螢幕、往下置中浮動&lt;/li>
&lt;li>對角線放四分之一角落&lt;/li>
&lt;li>外圈再包一層：丟到下一個螢幕、移到上一個 Space&lt;/li>
&lt;/ul>
&lt;p>底層用 simulate hotkey 去觸發 Rectangle 或 yabai。練熟後完全盲操作，不用記「左半邊是哪組三鍵組合」這種反直覺對應。&lt;/p>
&lt;h3 id="2-瀏覽器內當前頁面接我的工作流">2. 瀏覽器內「當前頁面接我的工作流」&lt;/h3>
&lt;p>看 PR、issue、文件時手在滑鼠上，這時甩一下選單把當前頁面導進後續流程：&lt;/p>
&lt;ul>
&lt;li>複製當前 URL 成 Markdown 連結&lt;/li>
&lt;li>把選取文字丟進 AI（觸發 Raycast AI / ChatGPT 的 hotkey，或貼到剪貼簿）&lt;/li>
&lt;li>存進筆記軟體&lt;/li>
&lt;li>在編輯器開對應的 local repo&lt;/li>
&lt;/ul>
&lt;h3 id="3-開發環境一鍵啟動巢狀選單">3. 開發環境一鍵啟動（巢狀選單）&lt;/h3>
&lt;p>用 run command，適合放每天重複開的那幾個專案：&lt;/p>
&lt;ul>
&lt;li>外圈按專案分&lt;/li>
&lt;li>內圈每個專案：用 IDE 開、開 terminal 到該目錄、開 PR 頁、開 CI dashboard、&lt;code>docker compose up&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="4-常用-snippet--樣板貼上">4. 常用 snippet / 樣板貼上&lt;/h3>
&lt;p>用 paste text，放那些「記不得但常用」的東西：&lt;/p>
&lt;ul>
&lt;li>Email 簽名、PR 描述模板、commit message 前綴&lt;/li>
&lt;li>常用 emoji / 符號&lt;/li>
&lt;li>長的 shell one-liner&lt;/li>
&lt;/ul>
&lt;h3 id="5-系統狀態切換">5. 系統狀態切換&lt;/h3>
&lt;p>toggle 類，平常要進設定點半天的：&lt;/p>
&lt;ul>
&lt;li>勿擾模式、深色/淺色主題、防睡眠、切換音訊輸出裝置、螢幕錄製/截圖&lt;/li>
&lt;/ul>
&lt;h3 id="6-git--容器-context-切換">6. Git / 容器 context 切換&lt;/h3>
&lt;p>git 日常走 CLI，但&lt;strong>切 context&lt;/strong> 這種低頻又記不住名字的適合放選單：&lt;/p>
&lt;ul>
&lt;li>&lt;code>kubectl&lt;/code> context / namespace 切換&lt;/li>
&lt;li>AWS profile 切換&lt;/li>
&lt;li>docker 清理、重啟某個 compose service&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="建構自己模式的起步建議">建構自己模式的起步建議&lt;/h2>
&lt;p>別一次設計大選單。先只做視窗排版那組，因為方向對應最直覺、回報最快、最容易養成「想到就甩一下」的習慣。等這個習慣固定了，再依「這禮拜我重複做了什麼、又懶得記快捷鍵」逐步往其他組長。一開始就塞滿，反而會因為記不住結構而棄用。&lt;/p>
&lt;hr>
&lt;h2 id="設定範例視窗排版magnet-版">設定範例：視窗排版（Magnet 版）&lt;/h2>
&lt;p>底層用 &lt;code>hotkey&lt;/code> 型項目去打 Magnet 的預設快捷鍵（Magnet 的修飾鍵是 Control+Option，Option 在按鍵碼裡是 &lt;code>AltLeft&lt;/code>）。八個項目用 &lt;code>angle&lt;/code> 釘死在八個方向，方向語意對齊位置：左滑放左半、右滑放右半、上滑最大化、下滑置中、四個對角放四分之一角落。練熟後可以盲操作。&lt;/p>
&lt;p>Kando 的角度規則是 &lt;code>0&lt;/code> 在正上方、順時針遞增（&lt;code>90&lt;/code> 右、&lt;code>180&lt;/code> 下、&lt;code>270&lt;/code> 左）。每個 &lt;code>hotkey&lt;/code> 都設 &lt;code>delayed: true&lt;/code>，讓按鍵在選單關閉後才送出，這樣 Magnet 作用的是「開選單前那個聚焦視窗」而不是 Kando 的 overlay。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;menus&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;root&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;submenu&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;視窗排版&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;grid_view&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;children&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 10&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 11&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 12&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+Enter&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 13&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;最大化&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 14&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;fullscreen&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 15&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 16&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;angle&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 17&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 18&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 19&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 20&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+KeyI&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 21&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;右上&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 22&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;north_east&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 23&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 24&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;angle&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">45&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 25&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 26&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 27&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 28&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+ArrowRight&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 29&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;右半&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 30&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;east&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 31&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 32&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;angle&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">90&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 33&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 34&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 35&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 36&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+KeyK&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 37&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;右下&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 38&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;south_east&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 39&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 40&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;angle&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">135&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 41&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 42&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 43&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 44&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+KeyC&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 45&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;置中&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 46&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;filter_center_focus&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 47&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 48&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;angle&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">180&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 49&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 50&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 51&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 52&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+KeyJ&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 53&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;左下&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 54&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;south_west&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 55&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 56&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;angle&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">225&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 57&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 58&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 59&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 60&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+ArrowLeft&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 61&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;左半&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 62&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;west&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 63&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 64&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;angle&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">270&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 65&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 66&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 67&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 68&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+KeyU&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 69&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;左上&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 70&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;north_west&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 71&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 72&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;angle&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">315&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 73&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 74&lt;/span>&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 75&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 76&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;shortcut&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Control+Shift+Space&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 77&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;shortcutID&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 78&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;centered&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 79&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;anchored&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 80&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;hoverMode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 81&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 82&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 83&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;root&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 84&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;submenu&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 85&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;視窗進階&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 86&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;view_column&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 87&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 88&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;children&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 89&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 90&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 91&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+KeyD&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 92&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;左三分之一&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 93&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;first_page&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 94&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 95&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 96&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 97&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 98&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+KeyF&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 99&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;中三分之一&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">100&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;vertical_align_center&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">101&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">102&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">103&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">104&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">105&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+KeyG&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">106&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;右三分之一&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">107&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;last_page&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">108&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">109&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">110&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">111&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">112&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+KeyE&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">113&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;左三分之二&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">114&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;align_horizontal_left&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">115&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">116&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">117&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">118&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">119&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+KeyT&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">120&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;右三分之二&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">121&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;align_horizontal_right&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">122&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">123&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">124&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">125&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">126&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+MetaLeft+ArrowLeft&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">127&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;上一個螢幕&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">128&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;arrow_circle_left&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">129&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">130&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">131&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">132&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">133&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+MetaLeft+ArrowRight&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">134&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;下一個螢幕&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">135&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;arrow_circle_right&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">136&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">137&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">138&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">139&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">140&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;hotkey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ControlLeft+AltLeft+Backspace&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delayed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">141&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;還原&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">142&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;settings_backup_restore&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">143&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconTheme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;material-symbols-rounded&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">144&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">145&lt;/span>&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">146&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">147&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;shortcut&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Control+Shift+Alt+Space&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">148&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;shortcutID&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">149&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;centered&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">150&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;anchored&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">151&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;hoverMode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">152&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">153&lt;/span>&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">154&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>套用方式：&lt;/p></description><content:encoded><![CDATA[<h2 id="kando-是什麼">Kando 是什麼</h2>
<p>Kando 是一個跨平台的**圓盤選單（pie menu / marking menu）**工具：綁一個快捷鍵後，選單會在游標位置彈出，用滑鼠往某個方向滑動或點擊就能選到項目。每個項目可以執行指令、模擬快捷鍵、開啟網址/檔案、貼上文字或串成巨集。</p>
<ul>
<li>專案：<a href="https://github.com/kando-menu/kando">https://github.com/kando-menu/kando</a></li>
<li>使用說明：<a href="https://kando.menu/usage/">https://kando.menu/usage/</a></li>
</ul>
<p>它最關鍵的特性是<strong>標記模式（marking mode）</strong>：選單結構固定後，可以靠肌肉記憶往某個方向「甩」一下盲選，不需要看著選單找項目。這跟「打字找東西」的啟動器（Raycast / Alfred / Spotlight）是不同的互動範式。</p>
<hr>
<h2 id="適用判準先判斷再設計">適用判準（先判斷再設計）</h2>
<p>Kando 補的是一個特定空缺：<strong>手已經在滑鼠/觸控板上、不想切回鍵盤、而且動作有空間對應</strong>。它跟鍵盤快捷鍵或啟動器是並存關係，不是替代。判斷一個動作該不該交給 Kando，用以下三條：</p>
<ol>
<li><strong>動作數量超過快捷鍵好記的上限</strong>。同類動作一旦超過七八個，組合鍵就開始互相打架、記不住；圓盤選單用空間位置取代記憶。</li>
<li><strong>當下手在滑鼠、切回鍵盤是中斷</strong>。瀏覽、看文件、看設計稿時，打字啟動器反而要先把手移開；圓盤選單原地彈出。</li>
<li><strong>動作有方向語意，盲操作就能命中</strong>。例如「左半邊視窗」往左滑、「下一個螢幕」往外圈滑。有方向對應的動作練熟後可以完全不看選單。</li>
</ol>
<p>三條不必同時滿足，但命中越多越適合。反過來說，低頻、無方向語意、又需要打字輸入參數的動作，留在啟動器或 CLI 比較好。</p>
<hr>
<h2 id="macos-工程師的應用場景">macOS 工程師的應用場景</h2>
<p>以下幾組依「契合 Kando 的程度」排序，越前面方向語意越強、回報越快。</p>
<h3 id="1-視窗排版最契合">1. 視窗排版（最契合）</h3>
<p>方向天然對應位置，這是 Kando 比 Rectangle 純快捷鍵更直覺的地方：</p>
<ul>
<li>往左滑放左半螢幕、往右放右半、往上全螢幕、往下置中浮動</li>
<li>對角線放四分之一角落</li>
<li>外圈再包一層：丟到下一個螢幕、移到上一個 Space</li>
</ul>
<p>底層用 simulate hotkey 去觸發 Rectangle 或 yabai。練熟後完全盲操作，不用記「左半邊是哪組三鍵組合」這種反直覺對應。</p>
<h3 id="2-瀏覽器內當前頁面接我的工作流">2. 瀏覽器內「當前頁面接我的工作流」</h3>
<p>看 PR、issue、文件時手在滑鼠上，這時甩一下選單把當前頁面導進後續流程：</p>
<ul>
<li>複製當前 URL 成 Markdown 連結</li>
<li>把選取文字丟進 AI（觸發 Raycast AI / ChatGPT 的 hotkey，或貼到剪貼簿）</li>
<li>存進筆記軟體</li>
<li>在編輯器開對應的 local repo</li>
</ul>
<h3 id="3-開發環境一鍵啟動巢狀選單">3. 開發環境一鍵啟動（巢狀選單）</h3>
<p>用 run command，適合放每天重複開的那幾個專案：</p>
<ul>
<li>外圈按專案分</li>
<li>內圈每個專案：用 IDE 開、開 terminal 到該目錄、開 PR 頁、開 CI dashboard、<code>docker compose up</code></li>
</ul>
<h3 id="4-常用-snippet--樣板貼上">4. 常用 snippet / 樣板貼上</h3>
<p>用 paste text，放那些「記不得但常用」的東西：</p>
<ul>
<li>Email 簽名、PR 描述模板、commit message 前綴</li>
<li>常用 emoji / 符號</li>
<li>長的 shell one-liner</li>
</ul>
<h3 id="5-系統狀態切換">5. 系統狀態切換</h3>
<p>toggle 類，平常要進設定點半天的：</p>
<ul>
<li>勿擾模式、深色/淺色主題、防睡眠、切換音訊輸出裝置、螢幕錄製/截圖</li>
</ul>
<h3 id="6-git--容器-context-切換">6. Git / 容器 context 切換</h3>
<p>git 日常走 CLI，但<strong>切 context</strong> 這種低頻又記不住名字的適合放選單：</p>
<ul>
<li><code>kubectl</code> context / namespace 切換</li>
<li>AWS profile 切換</li>
<li>docker 清理、重啟某個 compose service</li>
</ul>
<hr>
<h2 id="建構自己模式的起步建議">建構自己模式的起步建議</h2>
<p>別一次設計大選單。先只做視窗排版那組，因為方向對應最直覺、回報最快、最容易養成「想到就甩一下」的習慣。等這個習慣固定了，再依「這禮拜我重複做了什麼、又懶得記快捷鍵」逐步往其他組長。一開始就塞滿，反而會因為記不住結構而棄用。</p>
<hr>
<h2 id="設定範例視窗排版magnet-版">設定範例：視窗排版（Magnet 版）</h2>
<p>底層用 <code>hotkey</code> 型項目去打 Magnet 的預設快捷鍵（Magnet 的修飾鍵是 Control+Option，Option 在按鍵碼裡是 <code>AltLeft</code>）。八個項目用 <code>angle</code> 釘死在八個方向，方向語意對齊位置：左滑放左半、右滑放右半、上滑最大化、下滑置中、四個對角放四分之一角落。練熟後可以盲操作。</p>
<p>Kando 的角度規則是 <code>0</code> 在正上方、順時針遞增（<code>90</code> 右、<code>180</code> 下、<code>270</code> 左）。每個 <code>hotkey</code> 都設 <code>delayed: true</code>，讓按鍵在選單關閉後才送出，這樣 Magnet 作用的是「開選單前那個聚焦視窗」而不是 Kando 的 overlay。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln">  1</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">  2</span><span class="cl">  <span class="nt">&#34;menus&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">  3</span><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="ln">  4</span><span class="cl">      <span class="nt">&#34;root&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">  5</span><span class="cl">        <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;submenu&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">  6</span><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;視窗排版&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">  7</span><span class="cl">        <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;grid_view&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">  8</span><span class="cl">        <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">  9</span><span class="cl">        <span class="nt">&#34;children&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 10</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 11</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 12</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+Enter&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 13</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;最大化&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 14</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;fullscreen&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 15</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 16</span><span class="cl">            <span class="nt">&#34;angle&#34;</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln"> 17</span><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 18</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 19</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 20</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+KeyI&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 21</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;右上&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 22</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;north_east&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 23</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 24</span><span class="cl">            <span class="nt">&#34;angle&#34;</span><span class="p">:</span> <span class="mi">45</span>
</span></span><span class="line"><span class="ln"> 25</span><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 26</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 27</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 28</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+ArrowRight&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 29</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;右半&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 30</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;east&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 31</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 32</span><span class="cl">            <span class="nt">&#34;angle&#34;</span><span class="p">:</span> <span class="mi">90</span>
</span></span><span class="line"><span class="ln"> 33</span><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 34</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 35</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 36</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+KeyK&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 37</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;右下&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 38</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;south_east&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 39</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 40</span><span class="cl">            <span class="nt">&#34;angle&#34;</span><span class="p">:</span> <span class="mi">135</span>
</span></span><span class="line"><span class="ln"> 41</span><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 42</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 43</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 44</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+KeyC&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 45</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;置中&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 46</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;filter_center_focus&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 47</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 48</span><span class="cl">            <span class="nt">&#34;angle&#34;</span><span class="p">:</span> <span class="mi">180</span>
</span></span><span class="line"><span class="ln"> 49</span><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 50</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 51</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 52</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+KeyJ&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 53</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;左下&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 54</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;south_west&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 55</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 56</span><span class="cl">            <span class="nt">&#34;angle&#34;</span><span class="p">:</span> <span class="mi">225</span>
</span></span><span class="line"><span class="ln"> 57</span><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 58</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 59</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 60</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+ArrowLeft&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 61</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;左半&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 62</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;west&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 63</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 64</span><span class="cl">            <span class="nt">&#34;angle&#34;</span><span class="p">:</span> <span class="mi">270</span>
</span></span><span class="line"><span class="ln"> 65</span><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 66</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 67</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 68</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+KeyU&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 69</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;左上&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 70</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;north_west&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 71</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 72</span><span class="cl">            <span class="nt">&#34;angle&#34;</span><span class="p">:</span> <span class="mi">315</span>
</span></span><span class="line"><span class="ln"> 73</span><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 74</span><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="ln"> 75</span><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 76</span><span class="cl">      <span class="nt">&#34;shortcut&#34;</span><span class="p">:</span> <span class="s2">&#34;Control+Shift+Space&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 77</span><span class="cl">      <span class="nt">&#34;shortcutID&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 78</span><span class="cl">      <span class="nt">&#34;centered&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 79</span><span class="cl">      <span class="nt">&#34;anchored&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 80</span><span class="cl">      <span class="nt">&#34;hoverMode&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="ln"> 81</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 82</span><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 83</span><span class="cl">      <span class="nt">&#34;root&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 84</span><span class="cl">        <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;submenu&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 85</span><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;視窗進階&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 86</span><span class="cl">        <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;view_column&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 87</span><span class="cl">        <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 88</span><span class="cl">        <span class="nt">&#34;children&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 89</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 90</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 91</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+KeyD&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 92</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;左三分之一&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 93</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;first_page&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 94</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span>
</span></span><span class="line"><span class="ln"> 95</span><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 96</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 97</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 98</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+KeyF&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 99</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;中三分之一&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">100</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;vertical_align_center&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">101</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span>
</span></span><span class="line"><span class="ln">102</span><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="ln">103</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln">104</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">105</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+KeyG&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln">106</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;右三分之一&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">107</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;last_page&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">108</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span>
</span></span><span class="line"><span class="ln">109</span><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="ln">110</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln">111</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">112</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+KeyE&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln">113</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;左三分之二&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">114</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;align_horizontal_left&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">115</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span>
</span></span><span class="line"><span class="ln">116</span><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="ln">117</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln">118</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">119</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+KeyT&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln">120</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;右三分之二&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">121</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;align_horizontal_right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">122</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span>
</span></span><span class="line"><span class="ln">123</span><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="ln">124</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln">125</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">126</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+MetaLeft+ArrowLeft&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln">127</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;上一個螢幕&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">128</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;arrow_circle_left&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">129</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span>
</span></span><span class="line"><span class="ln">130</span><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="ln">131</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln">132</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">133</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+MetaLeft+ArrowRight&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln">134</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;下一個螢幕&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">135</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;arrow_circle_right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">136</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span>
</span></span><span class="line"><span class="ln">137</span><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="ln">138</span><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="ln">139</span><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;hotkey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">140</span><span class="cl">            <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;hotkey&#34;</span><span class="p">:</span> <span class="s2">&#34;ControlLeft+AltLeft+Backspace&#34;</span><span class="p">,</span> <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln">141</span><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;還原&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">142</span><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;settings_backup_restore&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">143</span><span class="cl">            <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span>
</span></span><span class="line"><span class="ln">144</span><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="ln">145</span><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="ln">146</span><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="ln">147</span><span class="cl">      <span class="nt">&#34;shortcut&#34;</span><span class="p">:</span> <span class="s2">&#34;Control+Shift+Alt+Space&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">148</span><span class="cl">      <span class="nt">&#34;shortcutID&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">149</span><span class="cl">      <span class="nt">&#34;centered&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">150</span><span class="cl">      <span class="nt">&#34;anchored&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">151</span><span class="cl">      <span class="nt">&#34;hoverMode&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="ln">152</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">153</span><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="ln">154</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>套用方式：</p>
<ol>
<li>先<strong>完全結束 Kando</strong>（Kando 會在離開時把記憶體裡的設定寫回檔案、開著編輯會被覆蓋）。</li>
<li>編輯 macOS 的設定檔 <code>~/Library/Application Support/kando/menus.json</code>。若已有自訂選單，把 <code>menus</code> 陣列裡的兩個物件貼進去、不要整檔覆蓋。</li>
<li>重新開 Kando。觸發鍵 <code>Control+Shift+Space</code>（進階組 <code>Control+Shift+Alt+Space</code>）若跟既有快捷鍵衝突，直接在 Kando 設定 GUI 改最保險。</li>
</ol>
<p>對照表（方向 → Magnet 動作 → 快捷鍵）：</p>
<table>
  <thead>
      <tr>
          <th>方向</th>
          <th>動作</th>
          <th>Magnet 快捷鍵</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>上</td>
          <td>最大化</td>
          <td>⌃⌥↵</td>
      </tr>
      <tr>
          <td>右</td>
          <td>右半</td>
          <td>⌃⌥→</td>
      </tr>
      <tr>
          <td>下</td>
          <td>置中</td>
          <td>⌃⌥C</td>
      </tr>
      <tr>
          <td>左</td>
          <td>左半</td>
          <td>⌃⌥←</td>
      </tr>
      <tr>
          <td>右上</td>
          <td>右上角</td>
          <td>⌃⌥I</td>
      </tr>
      <tr>
          <td>右下</td>
          <td>右下角</td>
          <td>⌃⌥K</td>
      </tr>
      <tr>
          <td>左下</td>
          <td>左下角</td>
          <td>⌃⌥J</td>
      </tr>
      <tr>
          <td>左上</td>
          <td>左上角</td>
          <td>⌃⌥U</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="設定範例自訂腳本叫出-ghostty-執行command-型">設定範例：自訂腳本叫出 Ghostty 執行（command 型）</h2>
<p><code>command</code> 型項目直接把指令交給作業系統執行，它本身不開終端機視窗。要「叫出 Ghostty 並在裡面跑腳本」，得用 Ghostty 的 <code>-e</code> 旗標開一個新視窗執行指定指令。下面這組放兩個需要看畫面的磁碟工具：<code>mole</code>（互動式清理 TUI）跟 <a href="/blog/other/macos-%E7%A3%81%E7%A2%9F%E7%A9%BA%E9%96%93%E8%A2%AB%E5%90%83%E5%85%89%E7%9A%84%E8%A8%BA%E6%96%B7%E6%B5%81%E7%A8%8B/" data-link-title="macOS 磁碟空間被吃光的診斷流程" data-link-desc="Mac 空間莫名歸零、清 cache 沒救、或空間掉了又回來時的排查順序。避開 sparse 假大小和本地快照浮動的誤判。含 disk-report 腳本。"><code>disk-report</code></a>（印出硬碟空間報告，安裝方式見該篇）。</p>
<p>兩個寫法的共同骨架是 <code>ghostty -e zsh -lc &quot;&lt;腳本&gt;; exec zsh&quot;</code>：</p>
<ul>
<li><strong><code>zsh -lc</code></strong>：用 login shell 執行，載入 <code>.zprofile</code> / <code>.zshrc</code> 的 PATH（<a href="/blog/other/macos-%E6%96%B0%E6%A9%9F%E5%9F%BA%E7%A4%8E%E5%BB%BA%E8%A8%AD%E5%A5%97%E4%BB%B6%E7%AE%A1%E7%90%86%E8%88%87%E5%80%8B%E4%BA%BA-bin-%E7%9A%84%E8%A8%AD%E5%AE%9A%E9%A0%86%E5%BA%8F/" data-link-title="macOS 新機基礎建設：套件管理與個人 bin 的設定順序" data-link-desc="重灌或換機後底層基礎建設的依賴順序，免得後面工具裝不起來或路徑互相找不到。">PATH 設定見新機基礎建設</a>），<code>mole</code> 跟 <code>~/.local/bin</code> 底下的腳本才解析得到。少了這層，Kando 走 <code>/bin/sh</code>、沒有自訂 PATH，會找不到指令。</li>
<li><strong><code>; exec zsh</code></strong>：腳本結束後把視窗留在一個互動 shell。mole 退出或 disk-report 印完，畫面都還在，不會瞬間關掉。</li>
</ul>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="nt">&#34;root&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;submenu&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;磁碟工具&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;hard_drive&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="nt">&#34;children&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;command&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">          <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;/Applications/Ghostty.app/Contents/MacOS/ghostty -e zsh -lc \&#34;mole; exec zsh\&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">          <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">          <span class="nt">&#34;isolateProcess&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;mole 清理&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;cleaning_services&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">        <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">        <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;command&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">          <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;/Applications/Ghostty.app/Contents/MacOS/ghostty -e zsh -lc \&#34;disk-report; exec zsh\&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">          <span class="nt">&#34;delayed&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">          <span class="nt">&#34;isolateProcess&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;硬碟空間報告&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">        <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;monitoring&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">        <span class="nt">&#34;iconTheme&#34;</span><span class="p">:</span> <span class="s2">&#34;material-symbols-rounded&#34;</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">  <span class="nt">&#34;shortcut&#34;</span><span class="p">:</span> <span class="s2">&#34;Control+Shift+KeyD&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">  <span class="nt">&#34;shortcutID&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">  <span class="nt">&#34;centered&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">  <span class="nt">&#34;anchored&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">  <span class="nt">&#34;hoverMode&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>把上面這個物件加進 <code>menus.json</code> 的 <code>menus</code> 陣列（跟前面視窗那兩個並排，逗號分隔）。</p>
<p>兩個延伸調整：</p>
<ul>
<li><strong>純背景、不用看畫面的腳本</strong>：不必開 Ghostty，<code>command</code> 直接寫 <code>zsh -lc &quot;$HOME/.local/bin/某腳本&quot;</code> 就好，腳本在背景跑完無聲結束。</li>
<li><strong>若 <code>ghostty</code> 二進位路徑直接呼叫沒反應</strong>（Ghostty 已在執行時偶有此狀況）：改用 <code>open -na Ghostty --args -e zsh -lc &quot;mole; exec zsh&quot;</code>，強制由 <code>open</code> 帶起一個新視窗。</li>
</ul>
<hr>
<h2 id="我的自訂模式紀錄">我的自訂模式紀錄</h2>
<p>（待補：實際用了一段時間後保留與淘汰的項目、其他組選單的 JSON、踩到的坑。）</p>
]]></content:encoded></item><item><title>macOS 每個 App 到底吃多少空間：聚合佔用的 app-report 腳本</title><link>https://tarrragon.github.io/blog/other/macos-%E6%AF%8F%E5%80%8B-app-%E5%88%B0%E5%BA%95%E5%90%83%E5%A4%9A%E5%B0%91%E7%A9%BA%E9%96%93%E8%81%9A%E5%90%88%E4%BD%94%E7%94%A8%E7%9A%84-app-report-%E8%85%B3%E6%9C%AC/</link><pubDate>Sat, 27 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/other/macos-%E6%AF%8F%E5%80%8B-app-%E5%88%B0%E5%BA%95%E5%90%83%E5%A4%9A%E5%B0%91%E7%A9%BA%E9%96%93%E8%81%9A%E5%90%88%E4%BD%94%E7%94%A8%E7%9A%84-app-report-%E8%85%B3%E6%9C%AC/</guid><description>&lt;p>&lt;code>du ~/Library/*&lt;/code> 只能列出 Caches、Containers 這些目錄各佔多少，答不出「Steam 這個 App 一共吃了多少」。原因是一個 App 的資料散落在 &lt;code>~/Library&lt;/code> 好幾個不同位置，按目錄統計就拆不回它名下。這篇記錄一個把這些散落佔用聚合回各 App 的 &lt;code>app-report&lt;/code> 腳本——搭配磁碟層的 &lt;a href="../macos_disk_space_diagnosis/">disk-report&lt;/a>，後者找出哪棵子樹最大，這篇把子樹拆到 App。&lt;/p>
&lt;h2 id="一個-app-的真實佔用不等於它的-app-大小">一個 App 的真實佔用不等於它的 .app 大小&lt;/h2>
&lt;p>判斷一個 App 吃多少空間，要算的是它的總足跡（footprint），而不是 &lt;code>/Applications&lt;/code> 裡那顆 &lt;code>.app&lt;/code> 的大小。&lt;code>.app&lt;/code> 只是程式本體，App 跑起來產生的資料（下載內容、快取、登入狀態、設定、日誌）絕大多數寫在 &lt;code>~/Library&lt;/code> 底下的好幾個不同位置，跟 &lt;code>.app&lt;/code> 完全分家。&lt;/p>
&lt;p>這台機器上最極端的例子是 Steam：它的 &lt;code>.app&lt;/code> 只有 10.8M，但遊戲資料佔了 8.1G，兩者差了近 800 倍。只看 &lt;code>/Applications&lt;/code> 的大小排序，Steam 會排在很後面，完全看不出它是全機第一大戶。同樣地，Amazon Kindle 的 &lt;code>.app&lt;/code> 才 138M，書庫卻在沙箱容器裡佔了 3.2G。這就是為什麼「按目錄統計」和「按 App 統計」會給出完全不同的排行；要回答「哪個 App 該清」，必須把佔用聚合回 App。&lt;/p>
&lt;h2 id="佔用散落在-library-的哪些地方">佔用散落在 ~/Library 的哪些地方&lt;/h2>
&lt;p>聚合的第一步是知道一個 App 會把資料寫到哪些固定位置。下表只列與空間相關的主要位置（非 &lt;code>~/Library&lt;/code> 全量），macOS 對它們有約定，每個位置承擔不同責任，也決定了它能不能安全清掉。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>位置&lt;/th>
 &lt;th>放什麼&lt;/th>
 &lt;th>清掉的後果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>/Applications/*.app&lt;/code>&lt;/td>
 &lt;td>程式本體&lt;/td>
 &lt;td>等於移除 App&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/Library/Caches/&lt;/code>&lt;/td>
 &lt;td>快取&lt;/td>
 &lt;td>下次自動重建，安全&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/Library/HTTPStorages/&lt;/code>&lt;/td>
 &lt;td>網路快取（cookie / 暫存）&lt;/td>
 &lt;td>多半要重新登入，大致安全&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/Library/Application Support/&lt;/code>&lt;/td>
 &lt;td>設定與使用者資料&lt;/td>
 &lt;td>掉資料&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/Library/Containers/&lt;/code>&lt;/td>
 &lt;td>沙箱 App 的完整家目錄&lt;/td>
 &lt;td>掉資料&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/Library/Group Containers/&lt;/code>&lt;/td>
 &lt;td>同廠商 App 共享的資料&lt;/td>
 &lt;td>掉資料、可能影響多個 App&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/Library/Saved Application State/&lt;/code>&lt;/td>
 &lt;td>視窗位置與復原狀態&lt;/td>
 &lt;td>下次開窗位置重置，無傷&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/Library/Logs/&lt;/code>&lt;/td>
 &lt;td>日誌&lt;/td>
 &lt;td>安全&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這張表的關鍵分界是「快取」與「資料」。&lt;code>Caches&lt;/code> 和 &lt;code>HTTPStorages&lt;/code> 是純衍生物，清掉只是讓 App 下次重新下載或重建，最多重新登入一次，所以是回收空間時的首選。&lt;code>Application Support&lt;/code>、&lt;code>Containers&lt;/code>、&lt;code>Group Containers&lt;/code> 則是使用者資料，Steam 的遊戲、Kindle 的書庫、聊天記錄都在這裡，刪了就真的沒了。&lt;code>Group Containers&lt;/code> 還要多一層留意：它是同一個開發商旗下多個 App 共享的目錄，動它可能同時影響好幾個 App。&lt;/p>
&lt;p>腳本對每個 App 把上面這些位置全部找出來、用 &lt;code>du&lt;/code> 量實際佔用、加總成一個數字，再附上逐項明細，讓人一眼看出「這 4G 裡有多少是可清的快取、多少是動不得的資料」。&lt;/p>
&lt;h2 id="命名不一致是聚合的主要難點">命名不一致是聚合的主要難點&lt;/h2>
&lt;p>把資料夾正確歸給某個 App 的難點在於：macOS 對這些目錄沒有統一的命名規則。有些 App 用它的 bundle id（例如 &lt;code>com.valvesoftware.steam&lt;/code>）當目錄名，有些直接用 App 的顯示名稱（例如 &lt;code>Steam&lt;/code>），同一個 App 的不同位置甚至各用一種。&lt;/p>
&lt;p>腳本的做法是對每個 App 先讀出它的 bundle id，然後 &lt;code>Caches&lt;/code>、&lt;code>Application Support&lt;/code>、&lt;code>Logs&lt;/code> 這幾個位置兩種命名都比對一次，bundle id 專屬的位置（&lt;code>Containers&lt;/code>、&lt;code>HTTPStorages&lt;/code>、&lt;code>Saved Application State&lt;/code>）則用 bundle id 找。&lt;code>Group Containers&lt;/code> 又是另一種格式，名稱前面多一段開發商的 team id（10 碼英數，像 &lt;code>ABCDE12345.group.com.foo&lt;/code>），因此改用 bundle id 做子字串比對。這套規則涵蓋了絕大多數 App，但用罕見自訂命名的資料仍可能漏抓，這是聚合式估算的固有邊界，腳本在輸出裡據實標明「可能漏抓」而不假裝是精確值。&lt;/p>
&lt;h2 id="homebrew-要分開算">Homebrew 要分開算&lt;/h2>
&lt;p>透過 Homebrew 裝的工具不在 &lt;code>/Applications&lt;/code>，需要獨立統計。佔用分兩類（概念詳見 &lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/homebrew/" data-link-title="Homebrew" data-link-desc="macOS 上社群維護的套件管理器、用一行指令安裝 CLI 工具與背景服務">Homebrew 知識卡&lt;/a>）：命令列工具與函式庫（&lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/homebrew/" data-link-title="Homebrew" data-link-desc="macOS 上社群維護的套件管理器、用一行指令安裝 CLI 工具與背景服務">formula&lt;/a>）在 &lt;code>Cellar/&lt;/code>，GUI App 的下載 artifact 與 metadata（&lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/homebrew/" data-link-title="Homebrew" data-link-desc="macOS 上社群維護的套件管理器、用一行指令安裝 CLI 工具與背景服務">cask&lt;/a>）在 &lt;code>Caskroom/&lt;/code>。cask 安裝的 &lt;code>.app&lt;/code> 本體實際放在 &lt;code>/Applications&lt;/code>，已被前面的 App 聚合排行計入；&lt;code>Caskroom/&lt;/code> 存的是安裝來源與版本資訊，體積通常遠小於 App 本體，兩邊不重複計。&lt;/p></description><content:encoded><![CDATA[<p><code>du ~/Library/*</code> 只能列出 Caches、Containers 這些目錄各佔多少，答不出「Steam 這個 App 一共吃了多少」。原因是一個 App 的資料散落在 <code>~/Library</code> 好幾個不同位置，按目錄統計就拆不回它名下。這篇記錄一個把這些散落佔用聚合回各 App 的 <code>app-report</code> 腳本——搭配磁碟層的 <a href="../macos_disk_space_diagnosis/">disk-report</a>，後者找出哪棵子樹最大，這篇把子樹拆到 App。</p>
<h2 id="一個-app-的真實佔用不等於它的-app-大小">一個 App 的真實佔用不等於它的 .app 大小</h2>
<p>判斷一個 App 吃多少空間，要算的是它的總足跡（footprint），而不是 <code>/Applications</code> 裡那顆 <code>.app</code> 的大小。<code>.app</code> 只是程式本體，App 跑起來產生的資料（下載內容、快取、登入狀態、設定、日誌）絕大多數寫在 <code>~/Library</code> 底下的好幾個不同位置，跟 <code>.app</code> 完全分家。</p>
<p>這台機器上最極端的例子是 Steam：它的 <code>.app</code> 只有 10.8M，但遊戲資料佔了 8.1G，兩者差了近 800 倍。只看 <code>/Applications</code> 的大小排序，Steam 會排在很後面，完全看不出它是全機第一大戶。同樣地，Amazon Kindle 的 <code>.app</code> 才 138M，書庫卻在沙箱容器裡佔了 3.2G。這就是為什麼「按目錄統計」和「按 App 統計」會給出完全不同的排行；要回答「哪個 App 該清」，必須把佔用聚合回 App。</p>
<h2 id="佔用散落在-library-的哪些地方">佔用散落在 ~/Library 的哪些地方</h2>
<p>聚合的第一步是知道一個 App 會把資料寫到哪些固定位置。下表只列與空間相關的主要位置（非 <code>~/Library</code> 全量），macOS 對它們有約定，每個位置承擔不同責任，也決定了它能不能安全清掉。</p>
<table>
  <thead>
      <tr>
          <th>位置</th>
          <th>放什麼</th>
          <th>清掉的後果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>/Applications/*.app</code></td>
          <td>程式本體</td>
          <td>等於移除 App</td>
      </tr>
      <tr>
          <td><code>~/Library/Caches/</code></td>
          <td>快取</td>
          <td>下次自動重建，安全</td>
      </tr>
      <tr>
          <td><code>~/Library/HTTPStorages/</code></td>
          <td>網路快取（cookie / 暫存）</td>
          <td>多半要重新登入，大致安全</td>
      </tr>
      <tr>
          <td><code>~/Library/Application Support/</code></td>
          <td>設定與使用者資料</td>
          <td>掉資料</td>
      </tr>
      <tr>
          <td><code>~/Library/Containers/</code></td>
          <td>沙箱 App 的完整家目錄</td>
          <td>掉資料</td>
      </tr>
      <tr>
          <td><code>~/Library/Group Containers/</code></td>
          <td>同廠商 App 共享的資料</td>
          <td>掉資料、可能影響多個 App</td>
      </tr>
      <tr>
          <td><code>~/Library/Saved Application State/</code></td>
          <td>視窗位置與復原狀態</td>
          <td>下次開窗位置重置，無傷</td>
      </tr>
      <tr>
          <td><code>~/Library/Logs/</code></td>
          <td>日誌</td>
          <td>安全</td>
      </tr>
  </tbody>
</table>
<p>這張表的關鍵分界是「快取」與「資料」。<code>Caches</code> 和 <code>HTTPStorages</code> 是純衍生物，清掉只是讓 App 下次重新下載或重建，最多重新登入一次，所以是回收空間時的首選。<code>Application Support</code>、<code>Containers</code>、<code>Group Containers</code> 則是使用者資料，Steam 的遊戲、Kindle 的書庫、聊天記錄都在這裡，刪了就真的沒了。<code>Group Containers</code> 還要多一層留意：它是同一個開發商旗下多個 App 共享的目錄，動它可能同時影響好幾個 App。</p>
<p>腳本對每個 App 把上面這些位置全部找出來、用 <code>du</code> 量實際佔用、加總成一個數字，再附上逐項明細，讓人一眼看出「這 4G 裡有多少是可清的快取、多少是動不得的資料」。</p>
<h2 id="命名不一致是聚合的主要難點">命名不一致是聚合的主要難點</h2>
<p>把資料夾正確歸給某個 App 的難點在於：macOS 對這些目錄沒有統一的命名規則。有些 App 用它的 bundle id（例如 <code>com.valvesoftware.steam</code>）當目錄名，有些直接用 App 的顯示名稱（例如 <code>Steam</code>），同一個 App 的不同位置甚至各用一種。</p>
<p>腳本的做法是對每個 App 先讀出它的 bundle id，然後 <code>Caches</code>、<code>Application Support</code>、<code>Logs</code> 這幾個位置兩種命名都比對一次，bundle id 專屬的位置（<code>Containers</code>、<code>HTTPStorages</code>、<code>Saved Application State</code>）則用 bundle id 找。<code>Group Containers</code> 又是另一種格式，名稱前面多一段開發商的 team id（10 碼英數，像 <code>ABCDE12345.group.com.foo</code>），因此改用 bundle id 做子字串比對。這套規則涵蓋了絕大多數 App，但用罕見自訂命名的資料仍可能漏抓，這是聚合式估算的固有邊界，腳本在輸出裡據實標明「可能漏抓」而不假裝是精確值。</p>
<h2 id="homebrew-要分開算">Homebrew 要分開算</h2>
<p>透過 Homebrew 裝的工具不在 <code>/Applications</code>，需要獨立統計。佔用分兩類（概念詳見 <a href="/blog/llm/knowledge-cards/homebrew/" data-link-title="Homebrew" data-link-desc="macOS 上社群維護的套件管理器、用一行指令安裝 CLI 工具與背景服務">Homebrew 知識卡</a>）：命令列工具與函式庫（<a href="/blog/llm/knowledge-cards/homebrew/" data-link-title="Homebrew" data-link-desc="macOS 上社群維護的套件管理器、用一行指令安裝 CLI 工具與背景服務">formula</a>）在 <code>Cellar/</code>，GUI App 的下載 artifact 與 metadata（<a href="/blog/llm/knowledge-cards/homebrew/" data-link-title="Homebrew" data-link-desc="macOS 上社群維護的套件管理器、用一行指令安裝 CLI 工具與背景服務">cask</a>）在 <code>Caskroom/</code>。cask 安裝的 <code>.app</code> 本體實際放在 <code>/Applications</code>，已被前面的 App 聚合排行計入；<code>Caskroom/</code> 存的是安裝來源與版本資訊，體積通常遠小於 App 本體，兩邊不重複計。</p>
<p>這台機器的 formula 前幾名是開發語言執行環境：<code>dotnet@9</code> 634M、兩個版本的 <code>openjdk</code> 合計 600M、<code>mysql</code> 292M、<code>go</code> 258M。formula 會多版本並存（例如 <code>python@3.13</code> 和 <code>python@3.14</code> 各算各的），所以腳本把整個 formula 目錄一起計。除了已安裝的部分，腳本還列出 <code>brew --cache</code> 的下載快取，以及 <code>brew cleanup -n</code> 預估可回收的舊版本（<code>-n</code> 是 dry-run，只報告不刪），跟整支腳本的唯讀原則一致。</p>
<h2 id="聚合一律用-du-取實際佔用">聚合一律用 du 取實際佔用</h2>
<p>App 各位置的聚合一律用 <code>du -skx</code> 取實際佔用，而不是 <code>ls</code> / <code>find -size</code> 的邏輯大小。sparse 檔（稀疏檔）只有寫入過的區塊才真正佔磁碟，宣告的邏輯大小可能是實際佔用的數十倍；容器與資料目錄裡正好常有 VM 映像、容器磁碟這類 sparse 檔，拿邏輯大小加總會把整份聚合排行灌水。完整的 sparse 踩坑案例見 <a href="../macos_disk_space_diagnosis/">disk-report 那篇</a>。</p>
<p><code>-x</code> 讓 <code>du</code> 不跨越檔案系統邊界，避免把掛載進來的卷重複計入；<code>-k</code> 統一用 KB 當單位，方便把各位置的數字加總後再換算成人類可讀的 G / M。</p>
<h2 id="實測結果">實測結果</h2>
<p>下面是這台機器的實測排行（名次因個人使用習慣而異）；要看的是聚合排行和「按目錄統計」給的印象差多少：</p>
<table>
  <thead>
      <tr>
          <th>App</th>
          <th>總佔用</th>
          <th>主要落點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Steam</td>
          <td>8.1G</td>
          <td>data 8.1G（<code>.app</code> 只有 10.8M）</td>
      </tr>
      <tr>
          <td>Xcode</td>
          <td>4.8G</td>
          <td>bundle 4.8G</td>
      </tr>
      <tr>
          <td>Readmoo 看書</td>
          <td>4.6G</td>
          <td>data 3.8G + bundle 816M</td>
      </tr>
      <tr>
          <td>Dia</td>
          <td>4.1G</td>
          <td>cache 1.6G + bundle 1.3G + data 1.1G</td>
      </tr>
      <tr>
          <td>Amazon Kindle</td>
          <td>3.3G</td>
          <td>container 3.2G（<code>.app</code> 才 138M）</td>
      </tr>
  </tbody>
</table>
<p>全機掃到 65 個 App、聚合總計 48.2G。這份排行的價值在於它直接指向「該從哪裡下手」，而判讀邏輯可以套到任何人的排行上：本體小、資料大的 App（這台是 Steam、Kindle）要回收就得處理書庫與遊戲本身；純快取大的（這台是 Dia 的 1.6G）清掉零風險；本體就大的開發工具（Xcode、Android Studio）除非不再開發否則動不得。同一個總數字底下，可清的比例天差地別，這正是逐項明細要回答的問題。</p>
<h2 id="聚合的邊界總計不等於整機">聚合的邊界：總計不等於整機</h2>
<p>這個 48.2G 是「能歸屬到已安裝 App 的部分」之和，不是 <code>~/Library</code> 的全量。<a href="../macos_disk_space_diagnosis/">disk-report 那篇</a>量到的 <code>~/Library</code> 約 70G，差額落在幾類刻意不歸進單一 App 的位置。</p>
<p>最大的一塊是 <code>~/Library/Developer</code>（這台約 5.5G，幾乎全是 Xcode 的 DerivedData、CoreSimulator 與 iOS DeviceSupport）。它們是 Xcode 與模擬器產生的共用產物，硬塞給 Xcode 會誇大這顆 App、塞給別人又不對，app-report 比照 Homebrew 把它單獨列成一段（<code>app-report --dev</code>）。也因為這樣，上面排行裡的 Xcode 只算到 <code>.app</code> 本體，它的建置產物要看 Developer 那段——這也是為什麼 disk-report 會把「Xcode DeviceSupport」列為大戶，而逐 App 排行卻看不到：那筆資料正住在這個不歸單一 App 的位置。</p>
<p>其餘排除的還有 iCloud 與雲端硬碟的本地鏡像（<code>Mobile Documents</code>、<code>CloudStorage</code>）、已移除 App 留下的孤兒資料夾、以及 <code>Preferences</code>。排行掃的是 <code>/Applications</code>、<code>~/Applications</code>、<code>/Applications/Utilities</code> 與 Setapp、Mac App Store 裝的 App；直接從 DMG 跑、沒搬進 Applications 的 App 不會出現在排行，但它的 <code>~/Library</code> 資料若命名對得上仍可能部分計入。</p>
<p>還有一個方向相反的誤差要記得：這是估算不是精算。同一份資料若以 APFS clone 出現在多個被聚合的位置，逐位置分開跑 <code>du</code> 會各自計入（<code>du</code> 只在單次執行內對硬連結以 inode 去重，對 APFS clone 不去重），聚合值可能偏高。要看整個 <code>~/Library</code> 到底多大、由誰佔，回到 disk-report 的逐層 <code>du</code>。</p>
<h2 id="固化成-app-report-腳本">固化成 app-report 腳本</h2>
<p>把這套聚合邏輯寫成腳本，往後想知道「誰在吃空間」就一行重跑，不必每次重想要比對哪些目錄、要怎麼處理命名差異。腳本和 <code>disk-report</code> 收在同一個公開 repo <a href="https://github.com/tarrragon/scripts">tarrragon/scripts</a> 裡，維持「跟專案無關的系統工具放個人 bin」的一致做法。</p>
<p>兩支腳本在同一個 repo；若已為 <code>disk-report</code> clone 過 <code>~/Projects/scripts</code>，跳過 clone、只做 symlink。首次安裝則把 repo clone 下來，再把腳本本體 symlink 到個人的 <code>~/.local/bin</code>，這樣本機呼叫的永遠是 repo 的最新版：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">git clone https://github.com/tarrragon/scripts.git ~/Projects/scripts
</span></span><span class="line"><span class="ln">2</span><span class="cl">ln -s ~/Projects/scripts/app-report/app-report ~/.local/bin/app-report</span></span></code></pre></div><p>PATH 設定同 disk-report（見 <a href="../macos_new_machine_setup/">macOS 新機基礎建設</a>）。裝好後直接呼叫：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">app-report           <span class="c1"># 完整報告：App 聚合排行 + Developer + Homebrew</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">app-report --apps    <span class="c1"># 只看 App 聚合排行（預設前 30）</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">app-report --apps <span class="m">50</span> <span class="c1"># 排行顯示前 50</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">app-report --dev     <span class="c1"># 只看 ~/Library/Developer 開發工具共用資料</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">app-report --brew    <span class="c1"># 只看 Homebrew</span></span></span></code></pre></div><p>要清哪個 App，看完明細再動手：移掉 <code>.app</code> 並清對應的 <code>~/Library</code> 資料夾（報告每個 App 下方列的路徑就是清除對象；先從 <code>Caches</code> / <code>HTTPStorages</code> 開始，確認再考慮資料目錄），Homebrew 用 <code>brew cleanup -s</code>。</p>
<h2 id="兩支腳本的分工">兩支腳本的分工</h2>
<p><code>disk-report</code> 與 <code>app-report</code> 是磁碟清理的兩個接力棒。前者在卷與目錄層找出最大的子樹，通常落在 <code>~/Library</code>；後者接手把那棵子樹拆到 App，看出具體是誰佔的、各自有多少是可清的快取。先 disk 找方向、再 app 定位到人，兩支都唯讀，回收的最後一步都留在人這一端。</p>
]]></content:encoded></item><item><title>macOS 新機基礎建設：套件管理與個人 bin 的設定順序</title><link>https://tarrragon.github.io/blog/other/macos-%E6%96%B0%E6%A9%9F%E5%9F%BA%E7%A4%8E%E5%BB%BA%E8%A8%AD%E5%A5%97%E4%BB%B6%E7%AE%A1%E7%90%86%E8%88%87%E5%80%8B%E4%BA%BA-bin-%E7%9A%84%E8%A8%AD%E5%AE%9A%E9%A0%86%E5%BA%8F/</link><pubDate>Sat, 27 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/other/macos-%E6%96%B0%E6%A9%9F%E5%9F%BA%E7%A4%8E%E5%BB%BA%E8%A8%AD%E5%A5%97%E4%BB%B6%E7%AE%A1%E7%90%86%E8%88%87%E5%80%8B%E4%BA%BA-bin-%E7%9A%84%E8%A8%AD%E5%AE%9A%E9%A0%86%E5%BA%8F/</guid><description>&lt;p>重灌或換機後要補的設定很多，但有個底層順序不能跳：套件管理工具要先到位，後面的補強才裝得起來。這篇聚焦最底層的三項基礎建設（Homebrew、bash、個人 bin），按依賴順序排列。重點不只是「裝什麼」，而是「為什麼這個順序」；之後遇到的新需求會接在後面繼續增補。&lt;/p>
&lt;h2 id="先裝-homebrew它是後面一切的基礎">先裝 Homebrew，它是後面一切的基礎&lt;/h2>
&lt;p>macOS 本身沒有內建的第三方套件管理工具，而後面幾乎每一項補強（命令列工具、開發語言、甚至部分 GUI App）都靠它安裝。沒有 Homebrew，這份清單的其他項目全部無從裝起，所以它排第一。&lt;/p>
&lt;p>安裝過程會要求輸入登入密碼（sudo），並自動下載 Xcode Command Line Tools，畫面可能停住數分鐘屬正常。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">/bin/bash -c &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>裝完還要把 Homebrew 的執行檔目錄加進 PATH，shell 才找得到 &lt;code>brew&lt;/code> 與之後用它裝的工具。Homebrew 的安裝前綴依晶片而異：Apple Silicon 機器裝在 &lt;code>/opt/homebrew&lt;/code>、Intel 機器裝在 &lt;code>/usr/local&lt;/code>。安裝腳本結尾會印出對應這台機器的設定指令，照它印的路徑寫進 &lt;code>~/.zprofile&lt;/code> 讓每次開 shell 都生效。以下以 Apple Silicon 為例，Intel 機器把前綴換成 &lt;code>/usr/local&lt;/code> 即可：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;eval &amp;#34;$(/opt/homebrew/bin/brew shellenv)&amp;#34;&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zprofile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">eval&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>/opt/homebrew/bin/brew shellenv&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這一步做完，驗證安裝成功：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">brew --version &lt;span class="c1"># 應印出 Homebrew 4.x.x&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>版本號印出來，&lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/homebrew/" data-link-title="Homebrew" data-link-desc="macOS 上社群維護的套件管理器、用一行指令安裝 CLI 工具與背景服務">Homebrew&lt;/a> 就能當後面所有項目的安裝來源。&lt;/p>
&lt;h2 id="把-bash-更新到-5x">把 bash 更新到 5.x&lt;/h2>
&lt;p>bash 是裝完 Homebrew 後最值得優先換掉的內建工具。macOS 的 &lt;code>/bin/bash&lt;/code> 長年凍結在 3.2 系列（2006 年釋出，目前是 patchlevel 57），Apple 不再更新它，原因是 bash 4 改用 GPLv3 授權、Apple 不願隨系統散布。對寫腳本的人來說，這代表 associative array、&lt;code>${var,,}&lt;/code> 大小寫轉換、&lt;code>mapfile&lt;/code> 等近二十年的語法都用不了。&lt;/p>
&lt;p>正確做法是用 Homebrew 另外裝一份新版並排存在，而不是覆寫系統版。&lt;code>/bin/bash&lt;/code> 在唯讀的系統卷上、受 SIP（System Integrity Protection）保護，覆寫會被擋下：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">brew install bash&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這會把 bash 5.x 裝到 &lt;code>/opt/homebrew/bin/bash&lt;/code>，完全不碰 &lt;code>/bin/bash&lt;/code>。因為前一步已經把 &lt;code>/opt/homebrew/bin&lt;/code> 排在 PATH 前面，用 &lt;code>#!/usr/bin/env bash&lt;/code> 起手的腳本就會自動改用新版。裝完驗證一下版本確實切過去：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">env bash --version &lt;span class="c1"># 應顯示 5.x&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">/bin/bash --version &lt;span class="c1"># 系統版仍是 3.2.57，沒被動&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>要留意的是互動 shell 在現代 macOS 預設是 zsh，這一步不影響它。更新 bash 的目的是給 &lt;code>#!/usr/bin/env bash&lt;/code> 腳本一個現代執行環境，不是換登入 shell。真要把新版 bash 當登入 shell，才需要額外把它加進 &lt;code>/etc/shells&lt;/code> 再 &lt;code>chsh&lt;/code>。&lt;/p>
&lt;h2 id="把-localbin-加進-path放個人腳本">把 ~/.local/bin 加進 PATH，放個人腳本&lt;/h2>
&lt;p>跟專案無關的小工具（例如 &lt;a href="../macos_disk_space_diagnosis/">disk-report&lt;/a> 與 &lt;a href="../macos_app_footprint_report/">app-report&lt;/a> 這類系統診斷腳本）需要一個能在任何地方直接呼叫、又不污染專案 repo 的家。慣例是個人的 &lt;code>~/.local/bin&lt;/code>，把它建好並掛上 PATH。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">mkdir -p ~/.local/bin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;export PATH=&amp;#34;$HOME/.local/bin:$PATH&amp;#34;&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zprofile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/.local/bin:&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>目錄建好、PATH 掛上後，確認它確實生效：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> tr &lt;span class="s1">&amp;#39;:&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;\n&amp;#39;&lt;/span> &lt;span class="p">|&lt;/span> grep &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/.local/bin&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>之後把腳本 symlink 進這個目錄就能直接當指令用。&lt;/p>
&lt;h2 id="後續項目">後續項目&lt;/h2>
&lt;p>基礎建設到位後，第一個掛上去的實用腳本就是系統診斷：&lt;a href="../macos_disk_space_diagnosis/">磁碟空間診斷的 disk-report&lt;/a> 與 &lt;a href="../macos_app_footprint_report/">按 App 聚合佔用的 app-report&lt;/a>，兩支都 symlink 進 &lt;code>~/.local/bin&lt;/code> 直接當指令用。&lt;/p>
&lt;p>這份清單會隨著之後遇到的需求往下增補，新項目接在這裡。原則維持不變：基礎建設排前面，依賴它的補強排後面，每一項都寫清楚「為什麼要做」而不只是貼指令。&lt;/p></description><content:encoded><![CDATA[<p>重灌或換機後要補的設定很多，但有個底層順序不能跳：套件管理工具要先到位，後面的補強才裝得起來。這篇聚焦最底層的三項基礎建設（Homebrew、bash、個人 bin），按依賴順序排列。重點不只是「裝什麼」，而是「為什麼這個順序」；之後遇到的新需求會接在後面繼續增補。</p>
<h2 id="先裝-homebrew它是後面一切的基礎">先裝 Homebrew，它是後面一切的基礎</h2>
<p>macOS 本身沒有內建的第三方套件管理工具，而後面幾乎每一項補強（命令列工具、開發語言、甚至部分 GUI App）都靠它安裝。沒有 Homebrew，這份清單的其他項目全部無從裝起，所以它排第一。</p>
<p>安裝過程會要求輸入登入密碼（sudo），並自動下載 Xcode Command Line Tools，畫面可能停住數分鐘屬正常。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">/bin/bash -c <span class="s2">&#34;</span><span class="k">$(</span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class="k">)</span><span class="s2">&#34;</span></span></span></code></pre></div><p>裝完還要把 Homebrew 的執行檔目錄加進 PATH，shell 才找得到 <code>brew</code> 與之後用它裝的工具。Homebrew 的安裝前綴依晶片而異：Apple Silicon 機器裝在 <code>/opt/homebrew</code>、Intel 機器裝在 <code>/usr/local</code>。安裝腳本結尾會印出對應這台機器的設定指令，照它印的路徑寫進 <code>~/.zprofile</code> 讓每次開 shell 都生效。以下以 Apple Silicon 為例，Intel 機器把前綴換成 <code>/usr/local</code> 即可：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;eval &#34;$(/opt/homebrew/bin/brew shellenv)&#34;&#39;</span> &gt;&gt; ~/.zprofile
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>/opt/homebrew/bin/brew shellenv<span class="k">)</span><span class="s2">&#34;</span></span></span></code></pre></div><p>這一步做完，驗證安裝成功：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">brew --version   <span class="c1"># 應印出 Homebrew 4.x.x</span></span></span></code></pre></div><p>版本號印出來，<a href="/blog/llm/knowledge-cards/homebrew/" data-link-title="Homebrew" data-link-desc="macOS 上社群維護的套件管理器、用一行指令安裝 CLI 工具與背景服務">Homebrew</a> 就能當後面所有項目的安裝來源。</p>
<h2 id="把-bash-更新到-5x">把 bash 更新到 5.x</h2>
<p>bash 是裝完 Homebrew 後最值得優先換掉的內建工具。macOS 的 <code>/bin/bash</code> 長年凍結在 3.2 系列（2006 年釋出，目前是 patchlevel 57），Apple 不再更新它，原因是 bash 4 改用 GPLv3 授權、Apple 不願隨系統散布。對寫腳本的人來說，這代表 associative array、<code>${var,,}</code> 大小寫轉換、<code>mapfile</code> 等近二十年的語法都用不了。</p>
<p>正確做法是用 Homebrew 另外裝一份新版並排存在，而不是覆寫系統版。<code>/bin/bash</code> 在唯讀的系統卷上、受 SIP（System Integrity Protection）保護，覆寫會被擋下：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">brew install bash</span></span></code></pre></div><p>這會把 bash 5.x 裝到 <code>/opt/homebrew/bin/bash</code>，完全不碰 <code>/bin/bash</code>。因為前一步已經把 <code>/opt/homebrew/bin</code> 排在 PATH 前面，用 <code>#!/usr/bin/env bash</code> 起手的腳本就會自動改用新版。裝完驗證一下版本確實切過去：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">env bash --version   <span class="c1"># 應顯示 5.x</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">/bin/bash --version  <span class="c1"># 系統版仍是 3.2.57，沒被動</span></span></span></code></pre></div><p>要留意的是互動 shell 在現代 macOS 預設是 zsh，這一步不影響它。更新 bash 的目的是給 <code>#!/usr/bin/env bash</code> 腳本一個現代執行環境，不是換登入 shell。真要把新版 bash 當登入 shell，才需要額外把它加進 <code>/etc/shells</code> 再 <code>chsh</code>。</p>
<h2 id="把-localbin-加進-path放個人腳本">把 ~/.local/bin 加進 PATH，放個人腳本</h2>
<p>跟專案無關的小工具（例如 <a href="../macos_disk_space_diagnosis/">disk-report</a> 與 <a href="../macos_app_footprint_report/">app-report</a> 這類系統診斷腳本）需要一個能在任何地方直接呼叫、又不污染專案 repo 的家。慣例是個人的 <code>~/.local/bin</code>，把它建好並掛上 PATH。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">mkdir -p ~/.local/bin
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;export PATH=&#34;$HOME/.local/bin:$PATH&#34;&#39;</span> &gt;&gt; ~/.zprofile
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.local/bin:</span><span class="nv">$PATH</span><span class="s2">&#34;</span></span></span></code></pre></div><p>目錄建好、PATH 掛上後，確認它確實生效：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$PATH</span><span class="s2">&#34;</span> <span class="p">|</span> tr <span class="s1">&#39;:&#39;</span> <span class="s1">&#39;\n&#39;</span> <span class="p">|</span> grep <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.local/bin&#34;</span></span></span></code></pre></div><p>之後把腳本 symlink 進這個目錄就能直接當指令用。</p>
<h2 id="後續項目">後續項目</h2>
<p>基礎建設到位後，第一個掛上去的實用腳本就是系統診斷：<a href="../macos_disk_space_diagnosis/">磁碟空間診斷的 disk-report</a> 與 <a href="../macos_app_footprint_report/">按 App 聚合佔用的 app-report</a>，兩支都 symlink 進 <code>~/.local/bin</code> 直接當指令用。</p>
<p>這份清單會隨著之後遇到的需求往下增補，新項目接在這裡。原則維持不變：基礎建設排前面，依賴它的補強排後面，每一項都寫清楚「為什麼要做」而不只是貼指令。</p>
]]></content:encoded></item><item><title>macOS 磁碟空間被吃光的診斷流程</title><link>https://tarrragon.github.io/blog/other/macos-%E7%A3%81%E7%A2%9F%E7%A9%BA%E9%96%93%E8%A2%AB%E5%90%83%E5%85%89%E7%9A%84%E8%A8%BA%E6%96%B7%E6%B5%81%E7%A8%8B/</link><pubDate>Fri, 26 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/other/macos-%E7%A3%81%E7%A2%9F%E7%A9%BA%E9%96%93%E8%A2%AB%E5%90%83%E5%85%89%E7%9A%84%E8%A8%BA%E6%96%B7%E6%B5%81%E7%A8%8B/</guid><description>&lt;p>一台原本還有約 30G 餘裕的 Mac，使用幾小時後空間全部歸零，清過系統各種 cache 也沒有改善。這次排查的重點是順序與判讀依據：用什麼順序找、用哪個數字判斷，最後刪了什麼反而次要。順序對了，就能避開兩個讓人空轉的陷阱。&lt;/p>
&lt;p>最後把整套診斷固化成一個唯讀的 &lt;code>disk-report&lt;/code> 腳本，往後同類情況可以一行指令重跑。&lt;/p>
&lt;h2 id="先確認問題是真的滿還是浮動的假象">先確認問題是「真的滿」還是「浮動的假象」&lt;/h2>
&lt;p>排查磁碟的第一步是分辨空間到底去哪：是被真實檔案佔走，還是被系統的快照與 purgeable（系統可隨時回收的緩衝空間）暫時佔住。這兩者的處理方式完全不同，先分清楚才不會白清。&lt;/p>
&lt;p>在 APFS（Apple File System，macOS 的預設檔案系統）上，根目錄 &lt;code>/&lt;/code> 是唯讀的系統封印卷，真正存放使用者資料的是 &lt;code>/System/Volumes/Data&lt;/code>，而它們和其他卷（Preboot、Recovery、VM、模擬器 runtime）共用同一個 container（容器，APFS 管理空間的最上層單位）的空間池。判斷「還剩多少」要看整個 container 的可用空間，而不是單一卷的數字。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">df -h /System/Volumes/Data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">diskutil info /System/Volumes/Data &lt;span class="p">|&lt;/span> grep -iE &lt;span class="s2">&amp;#34;Container Free Space|Container Total Space&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這次的結果是資料卷 100% 滿、整個 container 只剩約 591MB。確認確實滿載、不是顯示誤差，後面才值得花力氣找佔用大戶。&lt;/p>
&lt;h2 id="空間掉了又回來的根因本地快照與-purgeable">「空間掉了又回來」的根因：本地快照與 purgeable&lt;/h2>
&lt;p>空間在幾小時內反覆消長、清 cache 卻無效，最常見的原因是 Time Machine 的本地快照（local snapshots）加上 macOS 的 purgeable 空間，而不是某個看得見的檔案。這是排查時要先排除的一條線。&lt;/p>
&lt;p>本地快照的運作方式是：Time Machine 啟用時，系統約每小時自動建立一張快照「凍結」當下狀態，好讓本地也能做時光機回溯。這些被凍結的資料，正是先前以為已刪除、卻怎麼清都不會釋放的空間。快照保留約 24 小時（Apple 的 thinning 策略，觀察值），或在磁碟空間壓力過大時提前清除；後者正是「過一陣子空間又回來」的來源。若從未設定 Time Machine，這條線可跳過——沒啟用就不會有 local snapshot。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">tmutil listlocalsnapshots /System/Volumes/Data&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這次查的時候快照數是 0，但這不代表它不是元兇——恰恰相反，是磁碟已經滿到讓系統把快照全數清光了。判讀訊號是：若這個指令平常列出多筆快照、且磁碟空間在數字上頻繁浮動，浮動量就來自這裡，跟手動清的 cache 無關。根治方向是把總用量降下來、讓磁碟保有餘裕，系統就不會一直貼著上限狂建狂清快照。&lt;/p>
&lt;p>purgeable 是同一條線的另一半，但它沒有好用的精確讀數。&lt;code>diskutil apfs list&lt;/code> 能看 container 層的概況，而 purgeable 主要由快照與系統快取構成、本來就會自己浮動。處理方式跟快照一樣：把總用量降下來、讓系統在空間有壓力時自行釋放，而不是找指令直接清它。「沒有直接讀數」本身就是判讀邊界——看到可用空間和「實際檔案總和」對不上時，差額多半就在這塊浮動緩衝，不必懷疑是哪個檔案在搞鬼。&lt;/p>
&lt;h2 id="用實際佔用值找大戶避開-sparse-假大小">用實際佔用值找大戶，避開 sparse 假大小&lt;/h2>
&lt;p>找佔用大戶要用 &lt;code>du&lt;/code>（實際佔用的磁碟區塊）排序，不能依賴 &lt;code>ls -l&lt;/code> 顯示、或 &lt;code>find -size&lt;/code> 篩選所用的邏輯大小。對一般檔案兩者相同，但對 sparse 檔（稀疏檔）差距可以是好幾十倍，誤判會追錯目標。&lt;/p>
&lt;p>這次就踩到這個陷阱。&lt;code>find&lt;/code> 列出近期修改的大檔時，OrbStack（一套容器與 VM 執行環境）的虛擬磁碟映像顯示為 228G，看起來像頭號兇手；但用 &lt;code>du&lt;/code> 一量，實際佔用只有 1.9G。同樣地，macOS Podcasts 在 tmp 塞的一堆 &lt;code>.tmp.resize.img&lt;/code> 顯示有數十個檔，實際只佔 3.5M。這些都是 sparse 檔：宣告了很大的邏輯大小，但只有寫入過的區塊才真正佔磁碟。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 實際佔用（正確）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">du -sh ~/some/large.img
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 顯示大小（對 sparse 檔會嚴重高估，誤判用）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">ls -lh ~/some/large.img&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>定位順序是由外往內逐層收斂：先看家目錄前 20 大，鎖定最大的子樹（這次是 &lt;code>~/Library&lt;/code> 70G 左右），再往下展開 &lt;code>~/Library/Application Support&lt;/code>、&lt;code>~/Library/Containers&lt;/code>，直到找到具體的檔案或目錄。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">du -shx ~/* ~/.&lt;span class="o">[&lt;/span>!.&lt;span class="o">]&lt;/span>* 2&amp;gt;/dev/null &lt;span class="p">|&lt;/span> sort -rh &lt;span class="p">|&lt;/span> head -20
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">du -shx ~/Library/* 2&amp;gt;/dev/null &lt;span class="p">|&lt;/span> sort -rh &lt;span class="p">|&lt;/span> head -12&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>-x&lt;/code> 讓 &lt;code>du&lt;/code> 不跨越檔案系統邊界，避免把掛載進來的唯讀卷（例如 iOS 模擬器 runtime）重複計入；&lt;code>~/.[!.]*&lt;/code> 這個寫法只展開以單一點開頭的隱藏檔，排除掉 &lt;code>.&lt;/code> 和 &lt;code>..&lt;/code> 兩個會被一般 &lt;code>.*&lt;/code> 誤抓進來、算出整個家目錄大小的假項目。&lt;/p>
&lt;h2 id="這次找到的佔用大戶與處理">這次找到的佔用大戶與處理&lt;/h2>
&lt;p>定位出來的大戶集中在開發工具鏈與閒置的本地資料，多數可逆、刪了之後需要時會自動重建或可重新下載。下面的項目與數字都是這台機器的實測，換一台機器組成會完全不同；值得帶走的是每一項背後的判讀問題，不是這份清單本身。具體刪除指令因工具而異（Android Studio GUI、&lt;code>rm -rf&lt;/code>、&lt;code>ollama rm&lt;/code>），本文只做診斷與定位，刪除操作留給各工具自身的文件。以下逐項說明判讀依據。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>項目&lt;/th>
 &lt;th>實際佔用&lt;/th>
 &lt;th>處理判斷&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>舊版 Android NDK&lt;/td>
 &lt;td>約 3G&lt;/td>
 &lt;td>裝了多版、保留專案實際引用的版本，刪最舊&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>用不到的 AVD + system-image&lt;/td>
 &lt;td>約 3G&lt;/td>
 &lt;td>一個 API 版本一組、停用的版本連 AVD 帶映像一起刪&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Claude 桌面 Cowork 沙箱 VM&lt;/td>
 &lt;td>約 11G&lt;/td>
 &lt;td>只在使用桌面 App 的本地 agent 功能時才佈建，不用則可刪&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>ollama 本地模型&lt;/td>
 &lt;td>約 9G&lt;/td>
 &lt;td>改用雲端後閒置的大模型可刪，小的 embedding 模型常是依賴&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Xcode iOS DeviceSupport&lt;/td>
 &lt;td>約 4.5G&lt;/td>
 &lt;td>實體裝置接線除錯的符號快取，重連會自動重建&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Android NDK 的判讀要回到「誰在用它」：這次專案是 Flutter，NDK 版本由 &lt;code>flutter.ndkVersion&lt;/code> 決定，而不是專案自己 pin。查當前 Flutter 要求的版本後發現，本機裝的兩版都是舊 Flutter 留下的殘留，於是保留較新的一版、刪掉最舊的。判斷可不可刪的關鍵是先確認「現在到底用哪版」，而不是看修改日期就動手。&lt;/p></description><content:encoded><![CDATA[<p>一台原本還有約 30G 餘裕的 Mac，使用幾小時後空間全部歸零，清過系統各種 cache 也沒有改善。這次排查的重點是順序與判讀依據：用什麼順序找、用哪個數字判斷，最後刪了什麼反而次要。順序對了，就能避開兩個讓人空轉的陷阱。</p>
<p>最後把整套診斷固化成一個唯讀的 <code>disk-report</code> 腳本，往後同類情況可以一行指令重跑。</p>
<h2 id="先確認問題是真的滿還是浮動的假象">先確認問題是「真的滿」還是「浮動的假象」</h2>
<p>排查磁碟的第一步是分辨空間到底去哪：是被真實檔案佔走，還是被系統的快照與 purgeable（系統可隨時回收的緩衝空間）暫時佔住。這兩者的處理方式完全不同，先分清楚才不會白清。</p>
<p>在 APFS（Apple File System，macOS 的預設檔案系統）上，根目錄 <code>/</code> 是唯讀的系統封印卷，真正存放使用者資料的是 <code>/System/Volumes/Data</code>，而它們和其他卷（Preboot、Recovery、VM、模擬器 runtime）共用同一個 container（容器，APFS 管理空間的最上層單位）的空間池。判斷「還剩多少」要看整個 container 的可用空間，而不是單一卷的數字。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">df -h /System/Volumes/Data
</span></span><span class="line"><span class="ln">2</span><span class="cl">diskutil info /System/Volumes/Data <span class="p">|</span> grep -iE <span class="s2">&#34;Container Free Space|Container Total Space&#34;</span></span></span></code></pre></div><p>這次的結果是資料卷 100% 滿、整個 container 只剩約 591MB。確認確實滿載、不是顯示誤差，後面才值得花力氣找佔用大戶。</p>
<h2 id="空間掉了又回來的根因本地快照與-purgeable">「空間掉了又回來」的根因：本地快照與 purgeable</h2>
<p>空間在幾小時內反覆消長、清 cache 卻無效，最常見的原因是 Time Machine 的本地快照（local snapshots）加上 macOS 的 purgeable 空間，而不是某個看得見的檔案。這是排查時要先排除的一條線。</p>
<p>本地快照的運作方式是：Time Machine 啟用時，系統約每小時自動建立一張快照「凍結」當下狀態，好讓本地也能做時光機回溯。這些被凍結的資料，正是先前以為已刪除、卻怎麼清都不會釋放的空間。快照保留約 24 小時（Apple 的 thinning 策略，觀察值），或在磁碟空間壓力過大時提前清除；後者正是「過一陣子空間又回來」的來源。若從未設定 Time Machine，這條線可跳過——沒啟用就不會有 local snapshot。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">tmutil listlocalsnapshots /System/Volumes/Data</span></span></code></pre></div><p>這次查的時候快照數是 0，但這不代表它不是元兇——恰恰相反，是磁碟已經滿到讓系統把快照全數清光了。判讀訊號是：若這個指令平常列出多筆快照、且磁碟空間在數字上頻繁浮動，浮動量就來自這裡，跟手動清的 cache 無關。根治方向是把總用量降下來、讓磁碟保有餘裕，系統就不會一直貼著上限狂建狂清快照。</p>
<p>purgeable 是同一條線的另一半，但它沒有好用的精確讀數。<code>diskutil apfs list</code> 能看 container 層的概況，而 purgeable 主要由快照與系統快取構成、本來就會自己浮動。處理方式跟快照一樣：把總用量降下來、讓系統在空間有壓力時自行釋放，而不是找指令直接清它。「沒有直接讀數」本身就是判讀邊界——看到可用空間和「實際檔案總和」對不上時，差額多半就在這塊浮動緩衝，不必懷疑是哪個檔案在搞鬼。</p>
<h2 id="用實際佔用值找大戶避開-sparse-假大小">用實際佔用值找大戶，避開 sparse 假大小</h2>
<p>找佔用大戶要用 <code>du</code>（實際佔用的磁碟區塊）排序，不能依賴 <code>ls -l</code> 顯示、或 <code>find -size</code> 篩選所用的邏輯大小。對一般檔案兩者相同，但對 sparse 檔（稀疏檔）差距可以是好幾十倍，誤判會追錯目標。</p>
<p>這次就踩到這個陷阱。<code>find</code> 列出近期修改的大檔時，OrbStack（一套容器與 VM 執行環境）的虛擬磁碟映像顯示為 228G，看起來像頭號兇手；但用 <code>du</code> 一量，實際佔用只有 1.9G。同樣地，macOS Podcasts 在 tmp 塞的一堆 <code>.tmp.resize.img</code> 顯示有數十個檔，實際只佔 3.5M。這些都是 sparse 檔：宣告了很大的邏輯大小，但只有寫入過的區塊才真正佔磁碟。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 實際佔用（正確）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">du -sh ~/some/large.img
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 顯示大小（對 sparse 檔會嚴重高估，誤判用）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">ls -lh ~/some/large.img</span></span></code></pre></div><p>定位順序是由外往內逐層收斂：先看家目錄前 20 大，鎖定最大的子樹（這次是 <code>~/Library</code> 70G 左右），再往下展開 <code>~/Library/Application Support</code>、<code>~/Library/Containers</code>，直到找到具體的檔案或目錄。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">du -shx ~/* ~/.<span class="o">[</span>!.<span class="o">]</span>* 2&gt;/dev/null <span class="p">|</span> sort -rh <span class="p">|</span> head -20
</span></span><span class="line"><span class="ln">2</span><span class="cl">du -shx ~/Library/* 2&gt;/dev/null <span class="p">|</span> sort -rh <span class="p">|</span> head -12</span></span></code></pre></div><p><code>-x</code> 讓 <code>du</code> 不跨越檔案系統邊界，避免把掛載進來的唯讀卷（例如 iOS 模擬器 runtime）重複計入；<code>~/.[!.]*</code> 這個寫法只展開以單一點開頭的隱藏檔，排除掉 <code>.</code> 和 <code>..</code> 兩個會被一般 <code>.*</code> 誤抓進來、算出整個家目錄大小的假項目。</p>
<h2 id="這次找到的佔用大戶與處理">這次找到的佔用大戶與處理</h2>
<p>定位出來的大戶集中在開發工具鏈與閒置的本地資料，多數可逆、刪了之後需要時會自動重建或可重新下載。下面的項目與數字都是這台機器的實測，換一台機器組成會完全不同；值得帶走的是每一項背後的判讀問題，不是這份清單本身。具體刪除指令因工具而異（Android Studio GUI、<code>rm -rf</code>、<code>ollama rm</code>），本文只做診斷與定位，刪除操作留給各工具自身的文件。以下逐項說明判讀依據。</p>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>實際佔用</th>
          <th>處理判斷</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>舊版 Android NDK</td>
          <td>約 3G</td>
          <td>裝了多版、保留專案實際引用的版本，刪最舊</td>
      </tr>
      <tr>
          <td>用不到的 AVD + system-image</td>
          <td>約 3G</td>
          <td>一個 API 版本一組、停用的版本連 AVD 帶映像一起刪</td>
      </tr>
      <tr>
          <td>Claude 桌面 Cowork 沙箱 VM</td>
          <td>約 11G</td>
          <td>只在使用桌面 App 的本地 agent 功能時才佈建，不用則可刪</td>
      </tr>
      <tr>
          <td>ollama 本地模型</td>
          <td>約 9G</td>
          <td>改用雲端後閒置的大模型可刪，小的 embedding 模型常是依賴</td>
      </tr>
      <tr>
          <td>Xcode iOS DeviceSupport</td>
          <td>約 4.5G</td>
          <td>實體裝置接線除錯的符號快取，重連會自動重建</td>
      </tr>
  </tbody>
</table>
<p>Android NDK 的判讀要回到「誰在用它」：這次專案是 Flutter，NDK 版本由 <code>flutter.ndkVersion</code> 決定，而不是專案自己 pin。查當前 Flutter 要求的版本後發現，本機裝的兩版都是舊 Flutter 留下的殘留，於是保留較新的一版、刪掉最舊的。判斷可不可刪的關鍵是先確認「現在到底用哪版」，而不是看修改日期就動手。</p>
<p>Claude 桌面的 <code>vm_bundles</code> 是最大單一項目（11G）。它是桌面 App 的 Cowork 功能在本地沙箱 VM 裡執行程式用的根檔案系統映像。關鍵判讀是：它不是每次開 App 就重建——映像的修改日期停在數月前，是一次性佈建、之後沿用。只有實際使用 Cowork 沙箱時才會佈建和更新。所以對只用終端機 CLI、桌面 App 僅拿來聊天的人，這 11G 是純佔用，可以安全刪除；唯一後果是哪天實際開了 Cowork session，它會重新佈建。</p>
<p>剩下三項的判讀各有自己的關鍵問題。閒置的 AVD 與 system-image 是「一個 API 版本一組」的綁定，停用某個 Android 版本時要連 AVD 帶它依賴的系統映像一起刪，只刪一邊會留下半套。ollama 本地模型的判斷是「改用雲端後還會不會在本地跑」，閒置的大模型可刪，但小的 embedding 模型常被其他工具當依賴、刪了會牽連（ollama 模型的累積速度與專屬清理 idiom，見 <a href="/blog/llm/01-local-llm-services/hands-on/resource-management/" data-link-title="Hands-on：LLM 運行中 &#43; 結束的資源管理" data-link-desc="RAM / 磁碟 / port 三個 dimension 的觀察跟釋放、Ollama keep_alive 跟 ComfyUI 兩種 lifecycle 對比、實測釋放數字">本地 LLM 的資源管理</a>）。Xcode 的 iOS DeviceSupport 則是實體裝置接線除錯時產生的符號快取，可以放心刪——下次接上同一台裝置除錯時 Xcode 會自動重建。</p>
<p>這幾項合計回收約 17G，可用空間從約 591MB 拉回到 18G，磁碟脫離滿載。</p>
<h2 id="把診斷固化成-disk-report-腳本">把診斷固化成 disk-report 腳本</h2>
<p>一次性查完之後，把這套順序寫成腳本的價值是：下次同類情況不必重新回想指令與判讀順序，一行就能重跑，而且固定先看快照、再用實際佔用值，不會又掉進 sparse 假大小的陷阱。</p>
<p>腳本收在公開 repo <a href="https://github.com/tarrragon/scripts">tarrragon/scripts</a>，而不是放進某個專案的 <code>bin/</code>。它跟任何專案無關，連到個人 bin 才能在任何地方直接呼叫，也不會污染專案 repo。安裝方式是 clone 下來、把腳本本體 symlink 到 <code>~/.local/bin</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">git clone https://github.com/tarrragon/scripts.git ~/Projects/scripts
</span></span><span class="line"><span class="ln">2</span><span class="cl">ln -s ~/Projects/scripts/disk-report/disk-report ~/.local/bin/disk-report</span></span></code></pre></div><p>這一步預設 <code>~/.local/bin</code> 已在 PATH 上。若還沒設定，做法見 <a href="../macos_new_machine_setup/">macOS 新機基礎建設</a> 的對應項目。腳本刻意設計成唯讀：只報告、不刪除，刪什麼由人看完報告再決定。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">disk-report              <span class="c1"># 完整診斷：總覽 + 快照狀態 + 各層大戶 + 開發環境可清項</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">disk-report --growing    <span class="c1"># 只看過去 180 分鐘內長大的大檔（抓動態暴增最快）</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">disk-report --growing <span class="m">60</span> <span class="c1"># 改成過去 60 分鐘</span></span></span></code></pre></div><p><code>--growing</code> 模式對應的是本文開頭那個「幾小時內暴增」的情境：當空間正在快速消失、想抓現行犯時，直接列出近期被寫入的大檔，比逐層 <code>du</code> 更快定位。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">find <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">&#34;</span> -type f -size +50M -mmin -180 2&gt;/dev/null <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>  -exec du -h <span class="o">{}</span> <span class="se">\;</span> 2&gt;/dev/null <span class="p">|</span> sort -rh <span class="p">|</span> head -25</span></span></code></pre></div><p>50M 的下限是為了過濾日常小檔雜訊、鎖定單一大檔暴增；若懷疑是大量小檔累積吃空間（如快取碎片），這個門檻抓不到，要回逐層 <code>du</code> 看目錄總量。排序依據同樣是 <code>du</code> 的實際佔用值，而不是 <code>find -size</code> 的邏輯大小門檻，理由和前面一致：避免 sparse 檔的邏輯大小把排序帶歪。</p>
<h2 id="排查順序總結">排查順序總結</h2>
<p>這次的方法可以收斂成一條固定順序，往後遇到任何「磁碟莫名變滿」都先照這條走：</p>
<ol>
<li>先看 container 可用空間，確認是真滿還是顯示誤差。</li>
<li>再查本地快照與 purgeable，排除「掉了又回來」的浮動來源。</li>
<li>用 <code>du -shx</code> 由外往內逐層找大戶，全程以實際佔用值判斷，不信 <code>ls</code> / <code>find</code> 的顯示大小。</li>
<li>對每個大戶問「現在誰在用它」再決定刪不刪，可逆的優先清。</li>
<li>把整套順序固化成唯讀腳本，下次一行重跑。</li>
</ol>
<p>第 3 步若收斂到 <code>~/Library</code> 這種多個 App 共用的大目錄，按目錄統計只能看出 Caches、Containers 各多大，看不出是哪幾個 App 佔的。把這棵子樹再按 App 拆開的做法，見 <a href="../macos_app_footprint_report/">macOS App 聚合佔用報告</a>。</p>
]]></content:encoded></item><item><title>SSH Key 設定筆記（macOS / Linux / Windows）</title><link>https://tarrragon.github.io/blog/work-log/ssh-key-%E8%A8%AD%E5%AE%9A%E7%AD%86%E8%A8%98macos-/-linux-/-windows/</link><pubDate>Thu, 05 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/work-log/ssh-key-%E8%A8%AD%E5%AE%9A%E7%AD%86%E8%A8%98macos-/-linux-/-windows/</guid><description>&lt;h2 id="0-產生金鑰如果還沒有的話">0. 產生金鑰（如果還沒有的話）&lt;/h2>
&lt;p>目前推薦使用 &lt;strong>Ed25519&lt;/strong> 演算法，相比 RSA 更安全、金鑰更短、驗證速度更快：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">ssh-keygen -t ed25519 -C &lt;span class="s2">&amp;#34;your_email@example.com&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>若需相容較舊的系統（不支援 Ed25519），可退而使用 RSA-4096：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">ssh-keygen -t rsa -b &lt;span class="m">4096&lt;/span> -C &lt;span class="s2">&amp;#34;your_email@example.com&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/blockquote>
&lt;p>產生時會提示設定 &lt;strong>passphrase（密碼短語）&lt;/strong>，強烈建議設定。即使私鑰外洩，攻擊者仍需要密碼才能使用。&lt;/p>
&lt;hr>
&lt;h2 id="1-寫入金鑰檔案">1. 寫入金鑰檔案&lt;/h2>
&lt;h3 id="macos--linux">macOS / Linux&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">cat &amp;gt; ~/.ssh/&amp;lt;key_name&amp;gt; &lt;span class="s">&amp;lt;&amp;lt; &amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="s">（貼上金鑰內容）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="s">EOF&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>&lt;code>'EOF'&lt;/code> 加單引號 → 防止 shell 解析內容中的特殊字元（如 &lt;code>$&lt;/code>、&lt;code>`&lt;/code>）&lt;/p>&lt;/blockquote>
&lt;h3 id="windowspowershell">Windows（PowerShell）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nb">New-Item&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$env:USERPROFILE&lt;/span>&lt;span class="s2">\.ssh&amp;#34;&lt;/span> &lt;span class="n">-ItemType&lt;/span> &lt;span class="n">Directory&lt;/span> &lt;span class="n">-Force&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">Set-Content&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$env:USERPROFILE&lt;/span>&lt;span class="s2">\.ssh\&amp;lt;key_name&amp;gt;&amp;#34;&lt;/span> &lt;span class="n">-Value&lt;/span> &lt;span class="sh">@&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="sh">（貼上金鑰內容）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="sh">&amp;#34;@&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Windows 的 SSH 金鑰預設路徑為 &lt;code>C:\Users\&amp;lt;使用者&amp;gt;\.ssh\&lt;/code>&lt;/p>&lt;/blockquote>
&lt;hr>
&lt;h2 id="2-設定權限">2. 設定權限&lt;/h2>
&lt;h3 id="macos--linux-1">macOS / Linux&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">chmod &lt;span class="m">600&lt;/span> ~/.ssh/&amp;lt;key_name&amp;gt;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>&lt;code>chmod 600&lt;/code> → 僅擁有者可讀寫，SSH 要求私鑰權限不可過於開放，否則會拒絕使用。&lt;/p>&lt;/blockquote>
&lt;h3 id="windowspowershell以系統管理員執行">Windows（PowerShell，以系統管理員執行）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="n">icacls&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$env:USERPROFILE&lt;/span>&lt;span class="s2">\.ssh\&amp;lt;key_name&amp;gt;&amp;#34;&lt;/span> &lt;span class="p">/&lt;/span>&lt;span class="n">inheritance&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="nb">r &lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">grant&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="nb">r &lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">$(&lt;/span>&lt;span class="nv">$env:USERNAME&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="s2">:(R)&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Windows 需透過 &lt;code>icacls&lt;/code> 移除繼承權限，並限制為只有當前使用者可讀取。
若權限過於開放，OpenSSH 同樣會拒絕載入金鑰。&lt;/p>&lt;/blockquote>
&lt;hr>
&lt;h2 id="3-加入-ssh-agent">3. 加入 SSH Agent&lt;/h2>
&lt;h3 id="macos">macOS&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 啟動 agent（通常 macOS 已自動啟動）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">eval&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>ssh-agent -s&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 加入金鑰，並存入 Keychain 避免重開機後失效&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">ssh-add --apple-use-keychain ~/.ssh/&amp;lt;key_name&amp;gt;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>若要讓金鑰在每次登入時自動載入，可在 &lt;code>~/.ssh/config&lt;/code> 中加入：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">Host *
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> AddKeysToAgent yes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> UseKeychain yes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> IdentityFile ~/.ssh/&amp;lt;key_name&amp;gt;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="linux">Linux&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 啟動 agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">eval&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>ssh-agent -s&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 加入金鑰&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">ssh-add ~/.ssh/&amp;lt;key_name&amp;gt;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Linux 重開機後 agent 會重置。可將 &lt;code>eval &amp;quot;$(ssh-agent -s)&amp;quot;&lt;/code> 加入 &lt;code>~/.bashrc&lt;/code> 或 &lt;code>~/.zshrc&lt;/code> 讓它自動啟動。&lt;/p>&lt;/blockquote>
&lt;h3 id="windowspowershell以系統管理員執行-1">Windows（PowerShell，以系統管理員執行）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c"># 啟用 ssh-agent 服務（預設為停用）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">Get-Service&lt;/span> &lt;span class="nb">ssh-agent&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="nb">Set-Service&lt;/span> &lt;span class="n">-StartupType&lt;/span> &lt;span class="n">Automatic&lt;/span> &lt;span class="n">-PassThru&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="nb">Start-Service&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c"># 加入金鑰&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="nb">ssh-add&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$env:USERPROFILE&lt;/span>&lt;span class="s2">\.ssh\&amp;lt;key_name&amp;gt;&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Windows 的 &lt;code>ssh-agent&lt;/code> 是系統服務，啟用後重開機也會自動執行，不需額外設定。&lt;/p>&lt;/blockquote>
&lt;hr>
&lt;h2 id="4-測試連線">4. 測試連線&lt;/h2>
&lt;p>三個平台指令相同：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">ssh -i ~/.ssh/&amp;lt;key_name&amp;gt; -T git@&amp;lt;host&amp;gt;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Windows 請在 PowerShell 或 Git Bash 中執行，路徑會自動對應到 &lt;code>$env:USERPROFILE\.ssh\&lt;/code>。&lt;/p>&lt;/blockquote>
&lt;hr>
&lt;h2 id="備註">備註&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>項目&lt;/th>
 &lt;th>macOS&lt;/th>
 &lt;th>Linux&lt;/th>
 &lt;th>Windows&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>金鑰路徑&lt;/td>
 &lt;td>&lt;code>~/.ssh/&lt;/code>&lt;/td>
 &lt;td>&lt;code>~/.ssh/&lt;/code>&lt;/td>
 &lt;td>&lt;code>C:\Users\&amp;lt;使用者&amp;gt;\.ssh\&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>權限設定&lt;/td>
 &lt;td>&lt;code>chmod 600&lt;/code>&lt;/td>
 &lt;td>&lt;code>chmod 600&lt;/code>&lt;/td>
 &lt;td>&lt;code>icacls&lt;/code> 移除繼承&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Agent 持久化&lt;/td>
 &lt;td>Keychain&lt;/td>
 &lt;td>需加入 shell rc&lt;/td>
 &lt;td>系統服務，自動持久&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>預裝 SSH&lt;/td>
 &lt;td>是&lt;/td>
 &lt;td>大多數發行版已預裝&lt;/td>
 &lt;td>Windows 10 1809+ 內建 OpenSSH&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="安全性建議">安全性建議&lt;/h2>
&lt;h3 id="優先使用-ed25519">優先使用 Ed25519&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>演算法&lt;/th>
 &lt;th>金鑰長度&lt;/th>
 &lt;th>安全性&lt;/th>
 &lt;th>效能&lt;/th>
 &lt;th>相容性&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>Ed25519&lt;/strong>&lt;/td>
 &lt;td>256 bit&lt;/td>
 &lt;td>高&lt;/td>
 &lt;td>最快&lt;/td>
 &lt;td>2014 年後的 OpenSSH 6.5+&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>RSA-4096&lt;/td>
 &lt;td>4096 bit&lt;/td>
 &lt;td>高&lt;/td>
 &lt;td>較慢&lt;/td>
 &lt;td>最廣泛，適合舊系統&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>ECDSA&lt;/td>
 &lt;td>256/384/521 bit&lt;/td>
 &lt;td>中高&lt;/td>
 &lt;td>快&lt;/td>
 &lt;td>已被 Ed25519 取代&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>DSA&lt;/td>
 &lt;td>1024 bit&lt;/td>
 &lt;td>低&lt;/td>
 &lt;td>-&lt;/td>
 &lt;td>已棄用，OpenSSH 7.0+ 預設停用&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="為私鑰設定-passphrase">為私鑰設定 Passphrase&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 為已存在的金鑰補設或更換 passphrase&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">ssh-keygen -p -f ~/.ssh/&amp;lt;key_name&amp;gt;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>即使私鑰檔案被他人取得，沒有 passphrase 就無法使用。搭配 SSH Agent 後只需輸入一次，不影響日常使用體驗。&lt;/p></description><content:encoded><![CDATA[<h2 id="0-產生金鑰如果還沒有的話">0. 產生金鑰（如果還沒有的話）</h2>
<p>目前推薦使用 <strong>Ed25519</strong> 演算法，相比 RSA 更安全、金鑰更短、驗證速度更快：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh-keygen -t ed25519 -C <span class="s2">&#34;your_email@example.com&#34;</span></span></span></code></pre></div><blockquote>
<p>若需相容較舊的系統（不支援 Ed25519），可退而使用 RSA-4096：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh-keygen -t rsa -b <span class="m">4096</span> -C <span class="s2">&#34;your_email@example.com&#34;</span></span></span></code></pre></div></blockquote>
<p>產生時會提示設定 <strong>passphrase（密碼短語）</strong>，強烈建議設定。即使私鑰外洩，攻擊者仍需要密碼才能使用。</p>
<hr>
<h2 id="1-寫入金鑰檔案">1. 寫入金鑰檔案</h2>
<h3 id="macos--linux">macOS / Linux</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">cat &gt; ~/.ssh/&lt;key_name&gt; <span class="s">&lt;&lt; &#39;EOF&#39;
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="s">（貼上金鑰內容）
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="s">EOF</span></span></span></code></pre></div><blockquote>
<p><code>'EOF'</code> 加單引號 → 防止 shell 解析內容中的特殊字元（如 <code>$</code>、<code>`</code>）</p></blockquote>
<h3 id="windowspowershell">Windows（PowerShell）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="ln">1</span><span class="cl"><span class="nb">New-Item</span> <span class="n">-Path</span> <span class="s2">&#34;</span><span class="nv">$env:USERPROFILE</span><span class="s2">\.ssh&#34;</span> <span class="n">-ItemType</span> <span class="n">Directory</span> <span class="n">-Force</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">Set-Content</span> <span class="n">-Path</span> <span class="s2">&#34;</span><span class="nv">$env:USERPROFILE</span><span class="s2">\.ssh\&lt;key_name&gt;&#34;</span> <span class="n">-Value</span> <span class="sh">@&#34;
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="sh">（貼上金鑰內容）
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="sh">&#34;@</span></span></span></code></pre></div><blockquote>
<p>Windows 的 SSH 金鑰預設路徑為 <code>C:\Users\&lt;使用者&gt;\.ssh\</code></p></blockquote>
<hr>
<h2 id="2-設定權限">2. 設定權限</h2>
<h3 id="macos--linux-1">macOS / Linux</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">chmod <span class="m">600</span> ~/.ssh/&lt;key_name&gt;</span></span></code></pre></div><blockquote>
<p><code>chmod 600</code> → 僅擁有者可讀寫，SSH 要求私鑰權限不可過於開放，否則會拒絕使用。</p></blockquote>
<h3 id="windowspowershell以系統管理員執行">Windows（PowerShell，以系統管理員執行）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">icacls</span> <span class="s2">&#34;</span><span class="nv">$env:USERPROFILE</span><span class="s2">\.ssh\&lt;key_name&gt;&#34;</span> <span class="p">/</span><span class="n">inheritance</span><span class="err">:</span><span class="nb">r </span><span class="p">/</span><span class="n">grant</span><span class="err">:</span><span class="nb">r </span><span class="s2">&#34;</span><span class="p">$(</span><span class="nv">$env:USERNAME</span><span class="p">)</span><span class="s2">:(R)&#34;</span></span></span></code></pre></div><blockquote>
<p>Windows 需透過 <code>icacls</code> 移除繼承權限，並限制為只有當前使用者可讀取。
若權限過於開放，OpenSSH 同樣會拒絕載入金鑰。</p></blockquote>
<hr>
<h2 id="3-加入-ssh-agent">3. 加入 SSH Agent</h2>
<h3 id="macos">macOS</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 啟動 agent（通常 macOS 已自動啟動）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>ssh-agent -s<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 加入金鑰，並存入 Keychain 避免重開機後失效</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">ssh-add --apple-use-keychain ~/.ssh/&lt;key_name&gt;</span></span></code></pre></div><p>若要讓金鑰在每次登入時自動載入，可在 <code>~/.ssh/config</code> 中加入：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Host *
</span></span><span class="line"><span class="ln">2</span><span class="cl">  AddKeysToAgent yes
</span></span><span class="line"><span class="ln">3</span><span class="cl">  UseKeychain yes
</span></span><span class="line"><span class="ln">4</span><span class="cl">  IdentityFile ~/.ssh/&lt;key_name&gt;</span></span></code></pre></div><h3 id="linux">Linux</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 啟動 agent</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>ssh-agent -s<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 加入金鑰</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">ssh-add ~/.ssh/&lt;key_name&gt;</span></span></code></pre></div><blockquote>
<p>Linux 重開機後 agent 會重置。可將 <code>eval &quot;$(ssh-agent -s)&quot;</code> 加入 <code>~/.bashrc</code> 或 <code>~/.zshrc</code> 讓它自動啟動。</p></blockquote>
<h3 id="windowspowershell以系統管理員執行-1">Windows（PowerShell，以系統管理員執行）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="ln">1</span><span class="cl"><span class="c"># 啟用 ssh-agent 服務（預設為停用）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">Get-Service</span> <span class="nb">ssh-agent</span> <span class="p">|</span> <span class="nb">Set-Service</span> <span class="n">-StartupType</span> <span class="n">Automatic</span> <span class="n">-PassThru</span> <span class="p">|</span> <span class="nb">Start-Service</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c"># 加入金鑰</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nb">ssh-add</span> <span class="s2">&#34;</span><span class="nv">$env:USERPROFILE</span><span class="s2">\.ssh\&lt;key_name&gt;&#34;</span></span></span></code></pre></div><blockquote>
<p>Windows 的 <code>ssh-agent</code> 是系統服務，啟用後重開機也會自動執行，不需額外設定。</p></blockquote>
<hr>
<h2 id="4-測試連線">4. 測試連線</h2>
<p>三個平台指令相同：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh -i ~/.ssh/&lt;key_name&gt; -T git@&lt;host&gt;</span></span></code></pre></div><blockquote>
<p>Windows 請在 PowerShell 或 Git Bash 中執行，路徑會自動對應到 <code>$env:USERPROFILE\.ssh\</code>。</p></blockquote>
<hr>
<h2 id="備註">備註</h2>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>macOS</th>
          <th>Linux</th>
          <th>Windows</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>金鑰路徑</td>
          <td><code>~/.ssh/</code></td>
          <td><code>~/.ssh/</code></td>
          <td><code>C:\Users\&lt;使用者&gt;\.ssh\</code></td>
      </tr>
      <tr>
          <td>權限設定</td>
          <td><code>chmod 600</code></td>
          <td><code>chmod 600</code></td>
          <td><code>icacls</code> 移除繼承</td>
      </tr>
      <tr>
          <td>Agent 持久化</td>
          <td>Keychain</td>
          <td>需加入 shell rc</td>
          <td>系統服務，自動持久</td>
      </tr>
      <tr>
          <td>預裝 SSH</td>
          <td>是</td>
          <td>大多數發行版已預裝</td>
          <td>Windows 10 1809+ 內建 OpenSSH</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="安全性建議">安全性建議</h2>
<h3 id="優先使用-ed25519">優先使用 Ed25519</h3>
<table>
  <thead>
      <tr>
          <th>演算法</th>
          <th>金鑰長度</th>
          <th>安全性</th>
          <th>效能</th>
          <th>相容性</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Ed25519</strong></td>
          <td>256 bit</td>
          <td>高</td>
          <td>最快</td>
          <td>2014 年後的 OpenSSH 6.5+</td>
      </tr>
      <tr>
          <td>RSA-4096</td>
          <td>4096 bit</td>
          <td>高</td>
          <td>較慢</td>
          <td>最廣泛，適合舊系統</td>
      </tr>
      <tr>
          <td>ECDSA</td>
          <td>256/384/521 bit</td>
          <td>中高</td>
          <td>快</td>
          <td>已被 Ed25519 取代</td>
      </tr>
      <tr>
          <td>DSA</td>
          <td>1024 bit</td>
          <td>低</td>
          <td>-</td>
          <td>已棄用，OpenSSH 7.0+ 預設停用</td>
      </tr>
  </tbody>
</table>
<h3 id="為私鑰設定-passphrase">為私鑰設定 Passphrase</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 為已存在的金鑰補設或更換 passphrase</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">ssh-keygen -p -f ~/.ssh/&lt;key_name&gt;</span></span></code></pre></div><p>即使私鑰檔案被他人取得，沒有 passphrase 就無法使用。搭配 SSH Agent 後只需輸入一次，不影響日常使用體驗。</p>
<h3 id="定期輪換金鑰">定期輪換金鑰</h3>
<p>建議每 1-2 年輪換一次 SSH 金鑰。可在金鑰名稱中加入年份作為提醒，例如 <code>id_ed25519_2026</code>。</p>
<h3 id="其他注意事項">其他注意事項</h3>
<ul>
<li><strong>不要將私鑰上傳到雲端同步服務</strong>（如 iCloud、Google Drive、OneDrive），除非經過加密</li>
<li><strong>不要在多台機器之間複製同一把私鑰</strong>，應為每台機器各自產生獨立金鑰</li>
<li><strong>避免使用已棄用的 DSA 金鑰</strong>，部分新版 OpenSSH 已預設拒絕 DSA</li>
<li><strong><code>~/.ssh/</code> 目錄本身權限應為 <code>700</code></strong>，<code>authorized_keys</code> 應為 <code>600</code></li>
</ul>
<hr>
<h2 id="常見問題排查">常見問題排查</h2>
<h3 id="permission-denied-publickey">Permission denied (publickey)</h3>
<ol>
<li>確認私鑰權限：<code>chmod 600 ~/.ssh/&lt;key_name&gt;</code></li>
<li>確認 <code>~/.ssh/</code> 目錄權限：<code>chmod 700 ~/.ssh</code></li>
<li>確認上傳的是 <strong>公鑰</strong>（<code>.pub</code>）而非私鑰</li>
<li>使用 verbose 模式查看詳細錯誤：<code>ssh -vvv user@host</code></li>
</ol>
<h3 id="agent-中沒有金鑰">Agent 中沒有金鑰</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 確認 agent 是否正在運行</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">ssh-add -l
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 如果顯示 &#34;Could not open a connection to your authentication agent&#34;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>ssh-agent -s<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">ssh-add ~/.ssh/&lt;key_name&gt;</span></span></code></pre></div><h3 id="金鑰類型被伺服器拒絕">金鑰類型被伺服器拒絕</h3>
<p>部分較新的伺服器已停用 DSA 和較短的 RSA 金鑰。檢查方式：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh -vvv user@host 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="p">|</span> grep <span class="s2">&#34;Offering&#34;</span></span></span></code></pre></div><p>如果你的金鑰類型不在伺服器接受的清單中，需要重新產生 Ed25519 金鑰。</p>
]]></content:encoded></item><item><title>如果mac多桌面錯置如何重置</title><link>https://tarrragon.github.io/blog/other/%E5%A6%82%E6%9E%9Cmac%E5%A4%9A%E6%A1%8C%E9%9D%A2%E9%8C%AF%E7%BD%AE%E5%A6%82%E4%BD%95%E9%87%8D%E7%BD%AE/</link><pubDate>Thu, 18 Sep 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/other/%E5%A6%82%E6%9E%9Cmac%E5%A4%9A%E6%A1%8C%E9%9D%A2%E9%8C%AF%E7%BD%AE%E5%A6%82%E4%BD%95%E9%87%8D%E7%BD%AE/</guid><description>&lt;h2 id="what-happen-">What happen ?&lt;/h2>
&lt;p>Macos 不同螢幕的多桌面跑到錯誤的螢幕。&lt;/p>
&lt;h2 id="why-">Why ?&lt;/h2>
&lt;p>因為我會用並行裝置連接平板當成額外的螢幕，但是如果中斷再連接有時候會造成MACOS辨識多桌面出錯，導致無法正常切換桌面。&lt;/p>
&lt;h2 id="how-to-solve-this-">How to solve this ?&lt;/h2>
&lt;h3 id="方法一重置-mission-control-偏好設定">方法一：重置 Mission Control 偏好設定&lt;/h3>
&lt;p>這會清除所有顯示器的 Spaces 配置（等於重置多桌面設定）。&lt;/p>
&lt;p>打開 終端機（Terminal）&lt;/p>
&lt;p>輸入以下指令並按 Enter：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">defaults delete com.apple.spaces &lt;span class="c1">#清除設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">killall Dock &lt;span class="c1">#重啟 Dock&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Dock 重啟後，Mission Control 的桌面配置會被重置，所有螢幕會只剩下「一個桌面」。
注意：這會刪除所有已建立的桌面（Spaces），但不會影響 App 或檔案。&lt;/p>
&lt;h3 id="方法二暫時停用顯示器有單獨的空間">方法二：暫時停用「顯示器有單獨的空間」&lt;/h3>
&lt;p>macOS 會為每個螢幕建立自己的桌面空間，這個設定有時會造成錯亂。&lt;/p>
&lt;p>前往 系統設定 &amp;gt; 桌面與 Dock&lt;/p>
&lt;p>找到 「顯示器有單獨的空間」（英文為 Displays have separate Spaces）&lt;/p>
&lt;p>取消勾選&lt;/p>
&lt;p>登出並重新登入你的帳號&lt;/p>
&lt;p>重新勾選回來（如果你平常需要它）&lt;/p></description><content:encoded><![CDATA[<h2 id="what-happen-">What happen ?</h2>
<p>Macos 不同螢幕的多桌面跑到錯誤的螢幕。</p>
<h2 id="why-">Why ?</h2>
<p>因為我會用並行裝置連接平板當成額外的螢幕，但是如果中斷再連接有時候會造成MACOS辨識多桌面出錯，導致無法正常切換桌面。</p>
<h2 id="how-to-solve-this-">How to solve this ?</h2>
<h3 id="方法一重置-mission-control-偏好設定">方法一：重置 Mission Control 偏好設定</h3>
<p>這會清除所有顯示器的 Spaces 配置（等於重置多桌面設定）。</p>
<p>打開 終端機（Terminal）</p>
<p>輸入以下指令並按 Enter：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">defaults delete com.apple.spaces <span class="c1">#清除設定</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">killall Dock                     <span class="c1">#重啟 Dock</span></span></span></code></pre></div><p>Dock 重啟後，Mission Control 的桌面配置會被重置，所有螢幕會只剩下「一個桌面」。
注意：這會刪除所有已建立的桌面（Spaces），但不會影響 App 或檔案。</p>
<h3 id="方法二暫時停用顯示器有單獨的空間">方法二：暫時停用「顯示器有單獨的空間」</h3>
<p>macOS 會為每個螢幕建立自己的桌面空間，這個設定有時會造成錯亂。</p>
<p>前往 系統設定 &gt; 桌面與 Dock</p>
<p>找到 「顯示器有單獨的空間」（英文為 Displays have separate Spaces）</p>
<p>取消勾選</p>
<p>登出並重新登入你的帳號</p>
<p>重新勾選回來（如果你平常需要它）</p>
]]></content:encoded></item></channel></rss>