<?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>Posts on 瞬息科技 blogs</title>
    <link>https://blog.ouob.net/posts/</link>
    <description>Recent content in Posts on 瞬息科技 blogs</description>
    <generator>Hugo -- 0.152.2</generator>
    <language>zh-hant</language>
    <lastBuildDate>Thu, 27 Nov 2025 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://blog.ouob.net/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>一鍵自動開關USB偵錯模式！</title>
      <link>https://blog.ouob.net/posts/auto-enable-or-disable-android-usb-debugging/</link>
      <pubDate>Thu, 27 Nov 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ouob.net/posts/auto-enable-or-disable-android-usb-debugging/</guid>
      <description>&lt;p&gt;現在 Android 的自由真的越收越緊&lt;/p&gt;
&lt;p&gt;很多 支付/銀行軟體 連 USB偵錯權限也要管&lt;/p&gt;
&lt;p&gt;都怪台灣詐騙罰太輕&amp;hellip;&lt;/p&gt;
&lt;p&gt;不給用就不用了啊，算了&lt;/p&gt;
&lt;p&gt;但有時還是需要那些討厭的軟體&lt;/p&gt;
&lt;p&gt;比如去全聯就需要全支付🤬&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>現在 Android 的自由真的越收越緊</p>
<p>很多 支付/銀行軟體 連 USB偵錯權限也要管</p>
<p>都怪台灣詐騙罰太輕&hellip;</p>
<p>不給用就不用了啊，算了</p>
<p>但有時還是需要那些討厭的軟體</p>
<p>比如去全聯就需要全支付🤬</p>
<p>導致我時常要關閉USB偵錯模式，回家再重新打開。</p>
<p>至於你說打開偵錯模式要幹嘛</p>
<p>當然是有些寶貝軟體要更高的權限</p>
<p>像是跳過開屏廣告、權限管理、自動化腳本&hellip;</p>
<p>也是多虧了 MacroDroid 這個簡單又強大的自動化程式，讓我很直觀的創建腳本。</p>
<p>反觀老牌的Tasker，你不看說明書根本不會用😂</p>
<p>總之，先來看看成效！</p>
<p>一鍵自動開關USB偵錯模式：</p>
<p><video width="400" controls style="max-width: 100%;"><source src="/videos/one_click_to_enable_usb_debugging.mp4"></video></p>
<h2 id="腳本截圖">腳本截圖</h2>
<p>小米用戶又可以來抄作業了🐶</p>
<p>開啟USB debugging：
<img src="/images/Screenshot_2025-11-24-00-00-48-225_com.arlosoft.macrodroid-edit.jpg" width="300px" /></p>
<p>開啟網路ADB：</p>
<p>這裡我是把網路偵錯開關直接放到下拉選單，這樣操作更快。
<img src="/images/Screenshot_2025-11-24-00-01-22-603_com.arlosoft.macrodroid.jpg" width="300px" /></p>
<p>關閉USB 偵錯：
<img src="/images/Screenshot_2025-11-24-00-00-06-980_com.arlosoft.macrodroid-edit.jpg" width="300px" /></p>
<p>這裡用到的大部分功能，在上篇 『自動解鎖Links to Windows』有詳細介紹，所以就不重複撰寫了～</p>
<p>MacroDroid 系列預計還會寫幾篇，有興趣的可以先追蹤。</p>
<p>那就這樣，下篇見～</p>
]]></content:encoded>
    </item>
    <item>
      <title>Seamless Link Android to Windows 自動解鎖手機並點擊確認連接</title>
      <link>https://blog.ouob.net/posts/seamless-link-android-to-windows/</link>
      <pubDate>Tue, 04 Nov 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ouob.net/posts/seamless-link-android-to-windows/</guid>
      <description>&lt;p&gt;用過iPhone mirroring的都知道，在Mac上控制iPhone不需要手動解鎖手機，整個連接的體驗可說是十分絲滑。（這就是封閉系統的好處嗎😆&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>用過iPhone mirroring的都知道，在Mac上控制iPhone不需要手動解鎖手機，整個連接的體驗可說是十分絲滑。（這就是封閉系統的好處嗎😆</p>
<p>然而，PC陣營就沒有這個待遇了。</p>
<p>因為Window和Android是不同廠商，彼此有安全性上的考量，所以可能永遠也不會有自動解鎖的功能。</p>
<p>但是！這兩個系統權限都足夠開放、自由，我們完全可以自己寫腳本，實現自動解鎖手機，媲美iPhone mirroring的絲滑體驗！😎</p>
<h2 id="前置要求">前置要求</h2>
<ul>
<li>電腦：Link to Windows 配對好手機</li>
<li>手機：Play Store 搜尋並安裝 MacroDroid</li>
</ul>
<h2 id="link-to-windows-遠控手機流程">Link to Windows 遠控手機流程</h2>

<div class="mermaid">flowchart LR
	A[電腦]
	B[手機]
	C[用戶點擊同意投影畫面]
	D{手機為鎖定狀態？}
	E[用戶解鎖手機]
	Z[成功投影畫面]
	
	A--發送控制請求-->B --> C --> D
	D --是--> E --> Z
	D --否--> Z</div>
 
<p>需要自動化的部分：</p>
<ul>
<li>點擊同意投影畫面</li>
<li>解鎖手機</li>
</ul>
<h2 id="設定流程">設定流程</h2>
<p>以下流程基於小米手機，其他品牌要自己微調腳本。</p>
<p>米粉可以直接抄作業了😇
<div class="embedded-file" style="border: 1px solid var(--border); border-radius: 8px; padding: 20px; margin: 20px 0; background-color: var(--code-bg);">
    <div style="display: flex; align-items: center; margin-bottom: 15px;"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#28a745" stroke-width="2" style="margin-right: 10px;">
                <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon>
            </svg>
        <div>
            <h4 style="margin: 0; color: var(--primary);">Auto_confirm_Link_to_windows MacroDroid 腳本</h4><p style="margin: 5px 0 0 0; color: var(--secondary); font-size: 14px;">MacroDroid 自動化腳本檔案</p>
        </div>
    </div>
    
    <div style="display: flex; gap: 10px;">
        <a href="/files/Auto_confirm_Link_to_windows.macro" download style="display: inline-flex; align-items: center; background-color: #007acc; color: white; padding: 8px 16px; text-decoration: none; border-radius: 5px; font-size: 14px; transition: opacity 0.2s;">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 6px;">
                <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
                <polyline points="7,10 12,15 17,10"></polyline>
                <line x1="12" y1="15" x2="12" y2="3"></line>
            </svg>
            下載檔案
        </a>
        <button onclick="previewFile('\/files\/Auto_confirm_Link_to_windows.macro', 'Auto_confirm_Link_to_windows.macro')" style="display: inline-flex; align-items: center; background-color: transparent; color: var(--primary); padding: 8px 16px; text-decoration: none; border-radius: 5px; font-size: 14px; border: 1px solid var(--border); transition: background-color 0.2s; cursor: pointer;">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 6px;">
                <path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"></path>
                <path d="M15 3h6v6"></path>
                <path d="M10 14L21 3"></path>
            </svg>
            預覽
        </button>
    </div>

    <script>
    function previewFile(fileUrl, filename) {
        fetch(fileUrl)
            .then(response => response.text())
            .then(content => {
                const html = `<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>${filename}</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        html, body {
            width: 100%;
            height: 100%;
            overflow: auto;
        }
        body {
            font-family: 'Monaco', 'Courier New', monospace;
            font-size: 14px;
            line-height: 1.5;
            padding: 20px;
            background-color: #f5f5f5;
            color: #333;
        }
        pre {
            background-color: white;
            padding: 20px;
            border-radius: 4px;
            border: 1px solid #ddd;
            white-space: pre-wrap;
            word-wrap: break-word;
            overflow-x: auto;
        }
        @media (prefers-color-scheme: dark) {
            body {
                background-color: #1e1e1e;
                color: #e0e0e0;
            }
            pre {
                background-color: #2d2d2d;
                border-color: #444;
                color: #e0e0e0;
            }
        }
    </style>
</head>
<body>
    <pre>${escapeHtml(content)}</pre>
</body>
</html>`;
                const blob = new Blob([html], { type: 'text/html; charset=utf-8' });
                const url = URL.createObjectURL(blob);
                window.open(url, '_blank');
            })
            .catch(err => {
                console.error('Preview failed:', err);
                alert('無法預覽檔案');
            });
    }

    function escapeHtml(text) {
        const map = {
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;',
            "'": '&#039;'
        };
        return text.replace(/[&<>"']/g, m => map[m]);
    }
    </script>
</div>
<div class="embedded-file" style="border: 1px solid var(--border); border-radius: 8px; padding: 20px; margin: 20px 0; background-color: var(--code-bg);">
    <div style="display: flex; align-items: center; margin-bottom: 15px;"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="var(--secondary)" stroke-width="2" style="margin-right: 10px;">
                <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                <polyline points="14,2 14,8 20,8"></polyline>
            </svg>
        <div>
            <h4 style="margin: 0; color: var(--primary);">Unlock_phone.ablock</h4><p style="margin: 5px 0 0 0; color: var(--secondary); font-size: 14px;">檔案下載</p>
        </div>
    </div>
    
    <div style="display: flex; gap: 10px;">
        <a href="/files/Unlock_phone.ablock" download style="display: inline-flex; align-items: center; background-color: #007acc; color: white; padding: 8px 16px; text-decoration: none; border-radius: 5px; font-size: 14px; transition: opacity 0.2s;">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 6px;">
                <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
                <polyline points="7,10 12,15 17,10"></polyline>
                <line x1="12" y1="15" x2="12" y2="3"></line>
            </svg>
            下載檔案
        </a>
        <button onclick="previewFile('\/files\/Unlock_phone.ablock', 'Unlock_phone.ablock')" style="display: inline-flex; align-items: center; background-color: transparent; color: var(--primary); padding: 8px 16px; text-decoration: none; border-radius: 5px; font-size: 14px; border: 1px solid var(--border); transition: background-color 0.2s; cursor: pointer;">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 6px;">
                <path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"></path>
                <path d="M15 3h6v6"></path>
                <path d="M10 14L21 3"></path>
            </svg>
            預覽
        </button>
    </div>

    <script>
    function previewFile(fileUrl, filename) {
        fetch(fileUrl)
            .then(response => response.text())
            .then(content => {
                const html = `<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>${filename}</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        html, body {
            width: 100%;
            height: 100%;
            overflow: auto;
        }
        body {
            font-family: 'Monaco', 'Courier New', monospace;
            font-size: 14px;
            line-height: 1.5;
            padding: 20px;
            background-color: #f5f5f5;
            color: #333;
        }
        pre {
            background-color: white;
            padding: 20px;
            border-radius: 4px;
            border: 1px solid #ddd;
            white-space: pre-wrap;
            word-wrap: break-word;
            overflow-x: auto;
        }
        @media (prefers-color-scheme: dark) {
            body {
                background-color: #1e1e1e;
                color: #e0e0e0;
            }
            pre {
                background-color: #2d2d2d;
                border-color: #444;
                color: #e0e0e0;
            }
        }
    </style>
</head>
<body>
    <pre>${escapeHtml(content)}</pre>
</body>
</html>`;
                const blob = new Blob([html], { type: 'text/html; charset=utf-8' });
                const url = URL.createObjectURL(blob);
                window.open(url, '_blank');
            })
            .catch(err => {
                console.error('Preview failed:', err);
                alert('無法預覽檔案');
            });
    }

    function escapeHtml(text) {
        const map = {
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;',
            "'": '&#039;'
        };
        return text.replace(/[&<>"']/g, m => map[m]);
    }
    </script>
</div>
腳本截圖：
<img src="/images/Screenshot_2025-07-13-00-18-28-770_com.arlosoft.macrodroid.jpg" width="400px" />
<img src="/images/Screenshot_2025-07-13-00-17-28-157_com.arlosoft.macrodroid-edit%201.jpg" width="400px" /></p>
<h3 id="設置macrodroid">設置MacroDroid</h3>
<ol>
<li>
<p>自動同意投影畫面</p>
<p>我首先給予這兩個系統應用 project_media 權限：</p>
<ul>
<li>連接至Windows</li>
<li>Device Integration Service（負責用戶確認介面）</li>
</ul>
<p>測試後還是會顯示要求權限的介面，因此可以得知這個介面是他們魔改過的，不是直接調用系統權限的介面。</p>
<p>所以，改用無障礙權限直接點擊。</p>
<p>在macrodroid中創建腳本並新增trigger，檢測螢幕內容，檢測範圍只選擇Device Integration Service 這個系統應用，沒限制的話會一直檢測螢幕內容很耗電哦
<img src="/images/Screenshot_2025-07-12-23-48-04-541_com.arlosoft.macrodroid.jpg" width="400px" /></p>
<pre tabindex="0"><code>要透過連結至 Windows 開始進行錄製或投放內容嗎
</code></pre><p>Include overlays 要打勾 ✅</p>
<p>這樣檢測到該文本後就可以自動執行點擊Action了</p>
</li>
<li>
<p>自動解除鎖定畫面</p>
<p>當手機處於鎖定狀態，Link to Windows連接時會自動轉跳至輸入密碼頁面</p>
<p>所以，加入一個wait until trigger 檢測鎖定畫面的文字，小米手機是：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">請用數字密碼或指紋解鎖
</span></span></code></pre></div><p>並設定cancel after timeout，因為沒有檢測到文字，就表示手機處於解鎖狀態，不需要持續檢測。</p>
 <img src="/images/Screenshot_2025-07-13-00-18-24-337_com.arlosoft.macrodroid.jpg" width="400px" />
<p>剩下的解鎖action我放到action block裡面，也方便其他腳本調用。</p>
<pre tabindex="0"><code>📚 action block的概念就類似程式中的函數
</code></pre><pre tabindex="0"><code>💡 如果你只有一個腳本要用話，就直接把輸入密碼的action放到同個腳本就好。
</code></pre> <img src="/images/Screenshot_2025-07-13-00-17-28-157_com.arlosoft.macrodroid-edit%201.jpg" width="400px" />
<p>在這個action block 定義兩個輸入變量，
是否亮屏和切換至輸入密碼介面，用於執行特定操作。
不過在我們這個場景中都用不到，所以忽略即可。</p>
<p>後面的action group 就是放輸入密碼的action。</p>
<pre tabindex="0"><code>📚 action group 的用途就是把很多action放到一個群組裡面，方便移動位置。
</code></pre><pre tabindex="0"><code>💡 如果你用的不是數字密碼，就用UI Interaction 裡面的paste 把你的密碼輸入進去就好。
或者直接用作者的pin unlock的action，但輸入密碼中間的間隔會比較久，因該有兼容性考量，我就沒有使用。
</code></pre></li>
<li>
<p>腳本設置完成！</p>
<p>可以來測試效果了，沒意外的話，你應該可以做到像我影片中所展示的效果。</p>
</li>
</ol>
<p><video width="400" controls style="max-width: 100%;"><source src="/videos/auto_link2windows_mute_.mp4"></video></p>
]]></content:encoded>
    </item>
    <item>
      <title>打造專屬知識網站：Obsidian 筆記秒變 Hugo 網站</title>
      <link>https://blog.ouob.net/posts/blogging---obsidian-to-hugo/</link>
      <pubDate>Mon, 28 Apr 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ouob.net/posts/blogging---obsidian-to-hugo/</guid>
      <description>&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;💡 此文章主要是紀錄建站過程，不是教學。
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;目標&#34;&gt;目標&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;在obsidian寫作；博客網站展示&lt;/li&gt;
&lt;li&gt;一鍵發布文章（自動化流程）&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;前置要求&#34;&gt;前置要求&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;筆者使用macOS&lt;/li&gt;
&lt;li&gt;已安裝好docker&lt;/li&gt;
&lt;li&gt;遠端Linux主機的SSH&lt;/li&gt;
&lt;li&gt;遠端Linux主機公開的port端口&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;建置網站&#34;&gt;建置網站&lt;/h1&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;💡 影片中將網站部屬至Hostinger；本文將使用自己主機部屬。
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;主要參考此影片來建置：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<pre tabindex="0"><code>💡 此文章主要是紀錄建站過程，不是教學。
</code></pre><h1 id="目標">目標</h1>
<ul>
<li>在obsidian寫作；博客網站展示</li>
<li>一鍵發布文章（自動化流程）</li>
</ul>
<h1 id="前置要求">前置要求</h1>
<ul>
<li>筆者使用macOS</li>
<li>已安裝好docker</li>
<li>遠端Linux主機的SSH</li>
<li>遠端Linux主機公開的port端口</li>
</ul>
<h1 id="建置網站">建置網站</h1>
<pre tabindex="0"><code>💡 影片中將網站部屬至Hostinger；本文將使用自己主機部屬。
</code></pre><p>主要參考此影片來建置：</p>
<p><a href="https://www.youtube.com/watch?v=dnE7c0ELEH8">I started a blog&hellip;..in 2024 (why you should too) - YouTube</a>。</p>
<p>影片中的內容這裡就不重複撰寫了，直接看他的筆記 <a href="https://blog.networkchuck.com/posts/my-insane-blog-pipeline/">My Insane Blog Pipeline :: Terminal</a>。</p>
<h2 id="主要步驟">主要步驟</h2>
<ol>
<li>Obsidian準備好一個資料夾（放置要公開的文章）</li>
<li>安裝hugo</li>
<li>產生hugo專案檔案</li>
<li>安裝hugo主題</li>
<li>編寫圖片遷移腳本</li>
<li>編寫自動化流程腳本</li>
<li>部屬至主機</li>
</ol>
<h1 id="我的設定">我的設定</h1>
<h3 id="主題">主題</h3>
<p>筆者使用<a href="https://adityatelange.github.io/hugo-PaperMod/">PaperMod</a>，官網教學推薦使用git submodule安裝</p>
<pre tabindex="0"><code>git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod
</code></pre><details>
<summary>什麼是git submodule? 🤖GPT explain</summary>
Git submodule 是 Git 版本控制系統中的一個功能，它允許你將一個 Git 儲存庫作為另一個 Git 儲存庫的子目錄。這樣做的主要好處是可以在你的專案中包含和使用其他專案的程式碼，同時保持兩個專案的獨立性。
<p>這個命令的作用是：</p>
<ol>
<li>將 hugo-PaperMod 這個 Hugo 主題的 Git 儲存庫添加為你當前專案的一個子模組</li>
<li>子模組會被克隆到 themes/PaperMod 目錄下</li>
<li><code>--depth=1</code> 參數表示只克隆最新的一個版本的代碼，不包含完整的歷史記錄，這樣可以節省空間和下載時間</li>
</ol>
<p>使用 submodule 的優點：</p>
<ul>
<li>可以在你的專案中使用其他專案的程式碼，而不需要複製貼上或手動更新</li>
<li>當原始專案（在這個例子中是 hugo-PaperMod 主題）更新時，你可以輕鬆地更新你的子模組</li>
<li>你可以保持對使用的特定版本的控制</li>
</ul>
</details>
<hr>
<p>作者推薦使用yaml來設定，筆者還是習慣使用toml</p>
<p>使用 <a href="https://transform.tools/yaml-to-toml">YAML to TOML</a> 來轉換格式</p>
<p>如果是複製 <a href="https://adityatelange.github.io/hugo-PaperMod/posts/papermod/papermod-installation/#sample-configyml">PaperMod 的範例設定檔</a>，<br>
記得<code>hugo.toml</code> 要移除<code>paginate = 5</code>這行設定，因新版hugo已經棄用，不然會報錯。</p>
<h2 id="自動化腳本">自動化腳本</h2>
<p>筆者在影片提供的腳本基礎上修改。</p>
<h3 id="圖片遷移腳本">圖片遷移腳本</h3>
<ul>
<li>修改了regex，支援png、jpg</li>
<li>顯示Markdown檔案修改統計與圖片數量</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">shutil</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Paths</span>
</span></span><span class="line"><span class="cl"><span class="n">BASE_PATH</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="s2">&#34;/Users/rrrfff/&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">POSTS_DIR</span> <span class="o">=</span> <span class="n">BASE_PATH</span> <span class="o">/</span> <span class="s2">&#34;ouob-hugo/content/posts/&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">ATTACHMENTS_DIR</span> <span class="o">=</span> <span class="n">BASE_PATH</span> <span class="o">/</span> <span class="s2">&#34;Resilio/Obsidian/z_attach_img/&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">STATIC_IMAGES_DIR</span> <span class="o">=</span> <span class="n">BASE_PATH</span> <span class="o">/</span> <span class="s2">&#34;ouob-blogs/static/images/&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 1: Process each markdown file in the posts directory</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">file_path</span> <span class="ow">in</span> <span class="n">POSTS_DIR</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="s2">&#34;*.md&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_path</span><span class="p">,</span> <span class="s2">&#34;r&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">content</span> <span class="o">=</span> <span class="n">file</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Step 2: Find all image links in the format ![Image Description](/images/Pasted%20image%20...%20.png)</span>
</span></span><span class="line"><span class="cl">        <span class="n">images</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s2">&#34;\[\[([^\]]+\.(?:png|jpg|jpeg))\]\]&#34;</span><span class="p">,</span> <span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Step 3: Replace image links and ensure URLs are correctly formatted</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">image</span> <span class="ow">in</span> <span class="n">images</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># Prepare the Markdown-compatible link with %20 replacing spaces</span>
</span></span><span class="line"><span class="cl">            <span class="n">markdown_image</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;[Image Description](/images/</span><span class="si">{</span><span class="n">image</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">,</span> <span class="s1">&#39;%20&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2">)&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="n">content</span> <span class="o">=</span> <span class="n">content</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;[[</span><span class="si">{</span><span class="n">image</span><span class="si">}</span><span class="s2">]]&#34;</span><span class="p">,</span> <span class="n">markdown_image</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1"># Step 4: Copy the image to the Hugo static/images directory if it exists</span>
</span></span><span class="line"><span class="cl">            <span class="n">image_source</span> <span class="o">=</span> <span class="n">ATTACHMENTS_DIR</span> <span class="o">/</span> <span class="n">image</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">image_source</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">                <span class="n">shutil</span><span class="o">.</span><span class="n">copy</span><span class="p">(</span><span class="n">image_source</span><span class="p">,</span> <span class="n">STATIC_IMAGES_DIR</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Step 5: Write the updated content back to the markdown file</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_path</span><span class="p">,</span> <span class="s2">&#34;w&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">file</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">count</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;No images found in the markdown files.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Processed </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">POSTS_DIR</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="s1">&#39;*.md&#39;</span><span class="p">)))</span><span class="si">}</span><span class="s2"> markdown files.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Copied </span><span class="si">{</span><span class="n">count</span><span class="si">}</span><span class="s2"> images to the Hugo static/images directory.&#34;</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="一鍵發布腳本">一鍵發布腳本</h3>
<ul>
<li>根據筆者自己的流程修改</li>
</ul>
<p>步驟：</p>
<ol>
<li>設定變量</li>
<li>驗證所需工具</li>
<li>驗證SSH連線</li>
<li>驗證路徑已存在</li>
<li>rsync: Obsidian文章 至 公開庫 與 hugo專案</li>
<li>圖片遷移</li>
<li>hugo建構靜態檔案</li>
<li>公開庫: git add</li>
<li>公開庫: git commit</li>
<li>公開庫: git push</li>
<li>rsync: hugo靜態檔案 至 遠端伺服器</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="nb">set</span> -euo pipefail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Change to the script&#39;s directory</span>
</span></span><span class="line"><span class="cl"><span class="nv">SCRIPT_DIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span><span class="nb">cd</span> <span class="s2">&#34;</span><span class="k">$(</span>dirname <span class="s2">&#34;</span><span class="si">${</span><span class="nv">BASH_SOURCE</span><span class="p">[0]</span><span class="si">}</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span> <span class="o">&amp;&amp;</span> <span class="nb">pwd</span><span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> <span class="s2">&#34;</span><span class="nv">$SCRIPT_DIR</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Set variables for Obsidian to Hugo copy</span>
</span></span><span class="line"><span class="cl"><span class="nv">basePath</span><span class="o">=</span><span class="s2">&#34;/Users/rrrfff&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">sourcePath</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">basePath</span><span class="si">}</span><span class="s2">/Resilio/Obsidian/ouob_posts&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">publicPostRepoPath</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">basePath</span><span class="si">}</span><span class="s2">/ouob-blogs/posts&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">destinationPath</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">basePath</span><span class="si">}</span><span class="s2">/ouob-hugo/content/posts&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">hugoPublicPath</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">basePath</span><span class="si">}</span><span class="s2">/ouob-hugo/public&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Set GitHub Repo</span>
</span></span><span class="line"><span class="cl"><span class="nv">publicRepo</span><span class="o">=</span><span class="s2">&#34;git@github.com:swy641205/ouob-blogs.git&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">publicRepoPath</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">basePath</span><span class="si">}</span><span class="s2">/ouob-blogs&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">deployRepo</span><span class="o">=</span><span class="s2">&#34;git@github.com:swy641205/ouob-hugo.git&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">sshName</span><span class="o">=</span><span class="s2">&#34;s5nuuvm_o&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Check for required commands</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> cmd in git rsync python3 hugo<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> ! <span class="nb">command</span> -v <span class="nv">$cmd</span> <span class="p">&amp;</span>&gt; /dev/null<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$cmd</span><span class="s2"> is not installed or not in PATH.&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># check ssh connection</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! ssh -o <span class="nv">BatchMode</span><span class="o">=</span>yes -o <span class="nv">ConnectTimeout</span><span class="o">=</span><span class="m">5</span> <span class="si">${</span><span class="nv">sshName</span><span class="si">}</span> exit<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;SSH connection failed.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 2: Sync posts from Obsidian to Hugo content folder</span>
</span></span><span class="line"><span class="cl"><span class="c1"># and puvblicRepo path using rsync</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Syncing posts from Obsidian...&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> ! -d <span class="s2">&#34;</span><span class="nv">$sourcePath</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Source path does not exist: </span><span class="nv">$sourcePath</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> ! -d <span class="s2">&#34;</span><span class="nv">$publicPostRepoPath</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Public Repo path does not exist: </span><span class="nv">$publicPostRepoPath</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> ! -d <span class="s2">&#34;</span><span class="nv">$destinationPath</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Destination path does not exist: </span><span class="nv">$destinationPath</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 源路徑 / 代表複製資料夾裡面的檔案 不是直接複製資料夾本身</span>
</span></span><span class="line"><span class="cl">rsync -avuz --delete <span class="s2">&#34;</span><span class="nv">$sourcePath</span><span class="s2">/&#34;</span> <span class="s2">&#34;</span><span class="nv">$destinationPath</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">rsync -avuz --delete <span class="s2">&#34;</span><span class="nv">$sourcePath</span><span class="s2">/&#34;</span> <span class="s2">&#34;</span><span class="nv">$publicPostRepoPath</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 3: Process Markdown files with Python script to handle image links</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Processing image links in Markdown files...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> ! -f <span class="s2">&#34;image.py&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Python script images.py not found.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! python3 image.py<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Failed to process image links.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 4: Build the Hugo site</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Building the Hugo site...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! hugo --minify --enableGitInfo<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Hugo build failed.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 5: Add changes to Git</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Staging changes for Git...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 只有當「工作目錄」與「暫存區」完全無差異、</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 且「暫存區」與「最新的 commit」完全無差異 才回傳 true</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> git -C <span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span> diff --quiet <span class="o">&amp;&amp;</span> git diff --cached --quiet<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;No changes to stage.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">    git -C <span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span> add .
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 6: Commit changes with a dynamic message</span>
</span></span><span class="line"><span class="cl"><span class="nv">commit_message</span><span class="o">=</span><span class="s2">&#34;New Blog Post on </span><span class="k">$(</span>date +<span class="s1">&#39;%Y-%m-%d %H:%M:%S&#39;</span><span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> git -C <span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span> diff --cached --quiet<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span><span class="s2">: No changes to commit.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span><span class="s2">: Committing changes to ...&#34;</span>
</span></span><span class="line"><span class="cl">    git -C <span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span> commit -m <span class="s2">&#34;</span><span class="nv">$commit_message</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 7: Push all changes to the main branch</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span><span class="s2">: Deploying to GitHub Main...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! git -C <span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span> push origin main<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Failed to push to main branch.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 8: rsync the public folder to host</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Deploying to Host...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 複製 public 資料夾裡面的資料 （/）</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! rsync -avuz --delete <span class="s2">&#34;</span><span class="si">${</span><span class="nv">hugoPublicPath</span><span class="si">}</span><span class="s2">/&#34;</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">sshName</span><span class="si">}</span><span class="s2">:/home/swy/ouob-hugo/public&#34;</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Failed to deploy to host.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;All done! Site synced, processed, committed, built, and deployed.&#34;</span>
</span></span></code></pre></div><h2 id="部屬">部屬</h2>
<p>可以參考<a href="https://gohugo.io/host-and-deploy/">Host and deploy</a> 選擇適合自己的方案來部屬，筆者選擇部屬到自己主機上。</p>
<p>筆者這裡直接用<a href="https://docker.hugomods.com/docs/ci-cd/nginx/">hugomods提供的nginx映像檔</a>來部屬：</p>
<pre tabindex="0"><code>services:
  hugo-site:
    image: hugomods/hugo:nginx
    restart: unless-stopped
    ports:
      - &#34;${HUGO_PORT:-80}:80&#34;
    volumes:
      - ./nginx/nginx-custom.conf:/etc/nginx/conf.d/default.conf:ro # 可選：自定義 Nginx 配置
      - ./hugo_logs:/var/log/nginx # 持久化 Nginx 日誌
      - ./public:/site
    environment:
      - NGINX_ENTRYPOINT_QUIET_LOGS=1 # 減少 Nginx 啟動時的日誌輸出
    networks:
      - hugo_network
    healthcheck:
      test: [&#34;CMD&#34;, &#34;curl&#34;, &#34;-f&#34;, &#34;http://localhost:80&#34;]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s
    deploy:
      resources:
        limits:
          cpus: &#34;0.5&#34;
          memory: 256M
        reservations:
          memory: 128M

  # 可選：添加 Watchtower 自動更新容器
  watchtower:
    image: containrrr/watchtower
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --interval 86400 --cleanup # 每天檢查一次更新
    networks:
      - hugo_network

networks:
  hugo_network:
    driver: bridge
</code></pre><h2 id="支援mermaid流程圖顯示">支援Mermaid流程圖顯示</h2>
<p>看了<a href="https://gohugo.io/render-hooks/code-blocks/">Code block render hooks</a>，但還是不確定怎麼改，就先用claude回答的來改，有更好的辦法可以告訴我🤗</p>
<p>此腳本會查看頁面是否有mermaid的代碼塊，如果有就插入mermaid渲染腳本</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p layouts/_default/_markup/ <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">nano layouts/_default/_markup/render-codeblock-mermaid.html
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{{ if eq .Type &#34;mermaid&#34; }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;mermaid&#34;</span><span class="p">&gt;</span>{{- .Inner | safeHTML }}<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ if not (.Page.Scratch.Get &#34;mermaid&#34;) }} {{
</span></span><span class="line"><span class="cl">.Page.Scratch.Set &#34;mermaid&#34; true }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">mermaidLoaded</span> <span class="o">===</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">window</span><span class="p">.</span><span class="nx">mermaidLoaded</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;script src=&#34;https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js&#34;&gt;&lt;\/script&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;script&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        mermaid.initialize({
</span></span></span><span class="line"><span class="cl"><span class="sb">          startOnLoad: true, 
</span></span></span><span class="line"><span class="cl"><span class="sb">          theme: &#34;dark&#34;,
</span></span></span><span class="line"><span class="cl"><span class="sb">          align: &#34;center&#34;
</span></span></span><span class="line"><span class="cl"><span class="sb">        });
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;\/script&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">    `</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ end }} {{ else }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">pre</span><span class="p">&gt;&lt;</span><span class="nt">code</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;{{ .Type }}&#34;</span><span class="p">&gt;</span>{{ .Inner }}<span class="p">&lt;/</span><span class="nt">code</span><span class="p">&gt;&lt;/</span><span class="nt">pre</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ end }}
</span></span></code></pre></div><p>這樣網站就可以渲染流程圖了，如下：</p>

<div class="mermaid">graph TD
	A-->B</div>
 
<h2 id="加入giscus留言板">加入Giscus留言板</h2>
<p>👍 Giscus 開源免費，依託GitHub Discussion。</p>
<p>👎 disqus 有廣告，可能拖慢網頁載入速度，需要設定延後載入。</p>
<p>安照官網<a href="https://github.com/apps/giscus">giscus</a>教學設定即可，也可看看其他人寫的教學：</p>
<ul>
<li><a href="https://tunan.org/posts/add-comment-system-to-hugopapermo/">给Hugo PaperMod增加giscus评论系统 | 图南博客</a></li>
<li><a href="https://ljhero.github.io/posts/2022-05-02-support-comment-using-giscus/">使用 giscus 给博客添加评论功能 | Ljhero&rsquo;s blog</a></li>
<li><a href="https://jessewei.dev/blog/2023/papermod/#comments">Overview of Hugo/PaperMod, modifying PaperMod, and comparison to al-folio | Jesse Wei</a></li>
</ul>
<p>首先，確保<code>config.toml</code>裡面<code>[params]</code>的 <code>comments = true</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">mkdir -p layouts/partials/ <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">nano layouts/partials/comments.html
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">&lt;script <span class="nv">src</span><span class="o">=</span><span class="s2">&#34;https://giscus.app/client.js&#34;</span>
</span></span><span class="line"><span class="cl">        data-repo<span class="o">=</span><span class="s2">&#34;ouob-tw/ouob-blogs&#34;</span>
</span></span><span class="line"><span class="cl">        data-repo-id<span class="o">=</span><span class="s2">&#34;xxxxxxxxxxxx&#34;</span>
</span></span><span class="line"><span class="cl">        data-category<span class="o">=</span><span class="s2">&#34;Announcements&#34;</span>
</span></span><span class="line"><span class="cl">        data-category-id<span class="o">=</span><span class="s2">&#34;xxxxxxxxxxxx&#34;</span>
</span></span><span class="line"><span class="cl">        data-mapping<span class="o">=</span><span class="s2">&#34;pathname&#34;</span>
</span></span><span class="line"><span class="cl">        data-strict<span class="o">=</span><span class="s2">&#34;0&#34;</span>
</span></span><span class="line"><span class="cl">        data-reactions-enabled<span class="o">=</span><span class="s2">&#34;1&#34;</span>
</span></span><span class="line"><span class="cl">        data-emit-metadata<span class="o">=</span><span class="s2">&#34;0&#34;</span>
</span></span><span class="line"><span class="cl">        data-input-position<span class="o">=</span><span class="s2">&#34;top&#34;</span>
</span></span><span class="line"><span class="cl">        data-theme<span class="o">=</span><span class="s2">&#34;preferred_color_scheme&#34;</span>
</span></span><span class="line"><span class="cl">        data-lang<span class="o">=</span><span class="s2">&#34;zh-TW&#34;</span>
</span></span><span class="line"><span class="cl">        data-loading<span class="o">=</span><span class="s2">&#34;lazy&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">crossorigin</span><span class="o">=</span><span class="s2">&#34;anonymous&#34;</span>
</span></span><span class="line"><span class="cl">        async&gt;
</span></span><span class="line"><span class="cl">&lt;/script&gt;
</span></span></code></pre></div>]]></content:encoded>
    </item>
  </channel>
</rss>
