<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh_TW">
  <title>ねこの部屋</title>
  <subtitle>今天喝的是芋頭鮮奶 ~(￣▽￣)~</subtitle>
  <link href="https://moe.lolicon.io/" rel="alternate" type="text/html"/>
  <link href="https://moe.lolicon.io/atom.xml" rel="self" type="application/atom+xml"/>
  <id>https://moe.lolicon.io/</id>
  <updated>2026-08-22T00:00:00.000Z</updated>
  <entry>
    <title>Python 代碼優化の心得</title>
    <link href="https://moe.lolicon.io/posts/tech-posts/python-code-optimization-tips/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/tech-posts/python-code-optimization-tips/</id>
    <published>2026-08-21T00:00:00.000Z</published>
    <updated>2026-08-22T00:00:00.000Z</updated>
    <summary>告別寫不完的 If-Else! 只需一行神奇數學公式便足夠了</summary>
    <content type="html"><![CDATA[<h1>前言</h1>
<p>嗨各位！久違了⋯ 最近都在忙着研究 Arch 還有重構一堆幾年前寫下的屎山代碼，所以一直也沒空來打理這裏⋯⋯</p>
<p>這篇可以說是某次重構期間的意外發現，本不打算公開的。但考慮到網絡上的資料比較零散，對初心者而言未必能夠即時理解，所以還是決定把它肝出來了 (</p>
<p> </p>
<h1>引文</h1>
<p>前陣子在重構 <code>discord-py</code>  機器人裏的音樂播放器，先不說裏面 700 多行屎山搞得我快要頭暈與否⋯⋯</p>
<p>平常我們在寫程式時，如果遇到「循環、轉圈圈」的邏輯，最直覺的方法就是狂寫 <code>if-else</code>。舉個例子，當你在寫一個音樂播放器，使用者按「上一首」時，在某些情況下你得判斷：</p>
<p>「如果現在是第一首（Index 是 0），按上一首就要跳到最後一首；否則，就把 Index 減 1。」</p>
<p>傳統寫法大概長這樣：</p>
<pre><code>if index == 0:
    index = historySize - 1

else:
    index -= 1
</code></pre>
<p>好吧當然上面的那個只是例子，筆者自己的那屎山也不是這樣寫的</p>
<p>那還是給一點實質的東西你們看吧 (？</p>
<p><img src="./vscode.png" alt="Some code from my project" /></p>
<p>這是機器人裏移除曲目的功能 <code>/remove</code>。其可以直接呼叫，也可以指定所需移除的曲目編號</p>
<p>如果沒有指定曲目編號，會預設為佇列中的最後一首曲目</p>
<p>眼尖的你們可以看到，裏面有用到了類似三元運算子 (Ternary Operator) 的東西，像醬~</p>
<pre><code>index -= 1 if index else player.queue.historySize - 1
</code></pre>
<p>這種本來就已經夠簡潔了，直到筆者發現了下面的寫法</p>
<pre><code>index = (index - 1) % player.queue.historySize
</code></pre>
<p>什麼？這到底是何方神聖？ :spoiler[別急⋯ 下面會一一揭曉]</p>
<p> </p>
<h1>突如其來的回憶殺</h1>
<p>這不禁讓筆者憶起了去年的某次評估，當時的題目要求我們做一個單人猜拳遊戲 (石頭剪刀布) 的函數</p>
<p>而我當時是把題目完成了，且得了滿分。但為了判斷誰輸誰贏，寫了九種組合的 <code>if-else</code> 判斷式</p>
<p>結果教授的答案一出，我呆了</p>
<pre><code>def rock_paper_scissors(player, computer):
    return (player - computer) % 3
</code></pre>
<p>算出來是 0 👉 平手</p>
<p>1 👉 玩家贏了</p>
<p>2 👉 電腦贏了</p>
<p>這解法搞得當時的筆者頓時毫無頭緒⋯⋯ 所以這是什麼？到底是什麼來的？</p>
<p> </p>
<h1>原理</h1>
<p>其實這一切魔法的起源，要由模除 (Modulo) 以及 Python 對其的處理方法說起</p>
<p>在數學上，欲要計算一 <code>被除數 (Dividend) % 除數 (Divisor)</code> 所得的 <code>餘數 (Remainder)</code> ，公式如下：</p>
<pre><code>商 (Quotient) = 被除數 (Dividend) / 除數 (Divisor)
</code></pre>
<p> </p>
<pre><code>餘數 (Remainder) = 被除數 (Dividend) - [除數 (Divisor) * 商 (Quotient)]
</code></pre>
<p>其中 <code>商 (Quotient)</code> 若為正數需先作下捨入 (Round down) 處理，否則需作上捨入 (Round up) 處理</p>
<p>譬如說我們要計算 <code>-1 % 5</code> 的數值</p>
<pre><code>-1 / 5 = -0.2
</code></pre>
<p>因為得到的商是負數，上捨入後得出 0</p>
<pre><code>-1 - (5 * 0) = -1
</code></pre>
<p>繼而得出  <code>-1 % 5</code> = -1</p>
<p>然而在 Python 裏，其計算 <code>a % b</code> 的公式如下：</p>
<pre><code>a % b = a - (b * (a // b))
</code></pre>
<p>兩者分別在於 <code>//</code> (floor division) 。 而根據 Python 的定義，會一律把裏面所得的值下捨入並趨向至負無限 (negative infinity, -∞)</p>
<p>我們再透過 Python 的算法計算 <code>-1 % 5</code> 的數值</p>
<pre><code>-1 / 5 = -0.2
</code></pre>
<p>下捨入後得出 -1</p>
<pre><code>-1 - (5 * -1) = 4
</code></pre>
<p>最終得出  <code>-1 % 5</code> = 4</p>
<p>相信聰明的人已經明白我在說什麼了。但倘若你還沒明白的，也不要緊~</p>
<p> </p>
<h1>這又代表了什麼</h1>
<p>可能你們剛剛聽完上面的理論反被弄得一頭霧水了⋯⋯ 沒關係，我們可以來換個說法~</p>
<h2>時鐘的隱諭</h2>
<p>我們可以把整件事想像成為一個12小時制的時鐘</p>
<p>如果現在的時間為凌晨2 時，然後我問你：「6小時前會是幾時了？」</p>
<p>理論上你會很自然的看一眼時鐘，然後回答我：「晚上8 時」</p>
<p>然而在 Python 的角度來看，你剛剛不只是單純看了一眼，其實你的大腦已經在背後做出了</p>
<pre><code>(2 - 6) % 12 = -4 % 12
-&gt; -4 % 12 = 8 (in python)
</code></pre>
<p>這一串動作。</p>
<p>基本上一般 Python 的 <code>%</code> 就好像一部「自動包裝機」，會把數字限制在一個可循環的圓圈裏。</p>
<h2>石頭剪刀布</h2>
<p>我們可以把三者想像為一個順時針的循環徊圈</p>
<p>假設 0 為石頭、1 為布、2 為剪刀，可以得出下圖：</p>
<pre><code>graph TD
    0[0: ROCK] --&gt;|贏| 1[1: PAPER]
    1 --&gt;|贏| 2[2: SCISSORS]
    2 --&gt;|贏| 0
</code></pre>
<p>記住這個圓圈的「遊戲規則」：</p>
<p>只要順時針 (向右轉) 走 1 格，你就能贏過原本那一格。</p>
<ul>
<li>
<p>石頭 (0) 順時針走 1 格 -&gt; 抵達 布 (1) -&gt;  布贏石頭。</p>
</li>
<li>
<p>布 (1) 順時針走 1 格 -&gt; 抵達 剪刀 (2) -&gt; 剪刀贏布。</p>
</li>
<li>
<p>剪刀 (2) 順時針走 1 格 -&gt; 繞回起點 抵達 石頭 (0) -&gt; 石頭贏剪刀。</p>
</li>
</ul>
<p>問題來了，我們要怎麼知道「玩家有沒有比電腦順時針領先剛好 1 格」呢？</p>
<p>答案就是靠公式得出的啦：</p>
<pre><code>d = player_choice - computer_choice
</code></pre>
<p>我們再來舉個例子</p>
<p>就是當玩家出石頭 (0)，電腦出剪刀 (2) 的時候，結果會是怎樣的呢？</p>
<p>我們先數字帶進去相減：</p>
<pre><code>d = 0 - 2 = -2
</code></pre>
<p>這時候你一定會想：「-2 是什麼鬼？怎麼會是負的？」</p>
<p>先別焦急，在圓圈的邏輯裡，減法代表「從電腦的位置出發，要走幾步才會到玩家的位置」</p>
<p>換句話說，負號 (-) 代表往逆時針方向走，而 2 則代表走 2 步。</p>
<p>所以 -2 的意思就是：「從電腦 (剪刀 2) 的位置出發，逆時針往回走 2 步，就會走到玩家 (石頭 0) 的位置。」</p>
<p>這就是 -2 在圓圈上的真正含義。</p>
<p>現在神奇的事情來了，如果我們改成從電腦 (剪刀 2) 往順時針方向走 1 步，在一個只有 3 格的圓圈裡，不也會走到玩家 (石頭 0) 的位置嗎？</p>
<p>既然位置相通，那我們剛剛說過的遊戲規則是什麼？</p>
<pre><code>「只要順時針領先 1 格，就是玩家贏。」
</code></pre>
<p>因此，雖然數學上算出來的是 -2，但它在這循環圓圈上的靈魂其實就是 1</p>
<p>而 Python 的 <code>%</code> 只是幫你把這個藏在 -2 背後的 1 給算出來而已</p>
<pre><code>-2 % 3 = 1 (in python)
</code></pre>
<p> </p>
<h1>回到原點</h1>
<p>現在我們回來看dc機器人音樂播放器的那句代碼</p>
<p>設 <code>player.queue.historySize = 5</code>：</p>
<pre><code>index -= 1 if index else player.queue.historySize - 1
</code></pre>
<p>假設 <code>index = (1-5)</code> ，其會執行前者：</p>
<pre><code>index -= 1

# for index = 1
-&gt; index = 1 - 1
-&gt; index = 0

# for index = 2
-&gt; index = 2 - 1
-&gt; index = 1

# for index = 3
-&gt; index = 3 - 1
-&gt; index = 2

# for index = 4
-&gt; index = 4 - 1
-&gt; index = 3

# for index = 5
-&gt; index = 5 - 1
-&gt; index = 4
</code></pre>
<p>假設 <code>index == 0</code> ，其會執行後者：</p>
<pre><code>index = player.queue.historySize - 1
-&gt; index = 5 - 1
-&gt; index = 4
</code></pre>
<p>我們再看看那種用 <code>%</code> 的 Python 寫法：</p>
<pre><code>index = (index - 1) % player.queue.historySize
</code></pre>
<p>假設 <code>index = (0-5)</code>：</p>
<pre><code>index = (index - 1) % player.queue.historySize

# for index = 0
index = (0 - 1) % 5
-&gt; index = -1 % 5
-&gt; index = 4

# for index = 1
index = (1 - 1) % 5
-&gt; index = 0 % 5
-&gt; index = 0

# for index = 2
index = (2 - 1) % 5
-&gt; index = 1 % 5
-&gt; index = 1

# for index = 3
index = (3 - 1) % 5
-&gt; index = 2 % 5
-&gt; index = 2

# for index = 4
index = (4 - 1) % 5
-&gt; index = 3 % 5
-&gt; index = 3

# for index = 5
index = (5 - 1) % 5
-&gt; index = 4 % 5
-&gt; index = 4
</code></pre>
<p>我們可以看到，兩者的邏輯和輸出基本上完全一樣。且後者只需進行數學運算便可以了，無需進行多重的條件式判斷。</p>
<p>考慮到要處理具「循環性」的資料時，這的確為一個比較理想的替代方案。</p>
<p> </p>
<h1>結論</h1>
<p>說實話這個技巧真的超好用，但有以下幾點需要注意：</p>
<p>首先資料必須具有「循環性」，像是播放清單、星期幾、時鐘、猜拳這種會「繞回起點」的邏輯才適用。</p>
<p>如果是判斷年齡、考試分數等那些的，還是乖乖的用 <code>if-else</code> 吧！</p>
<p>其次這可以說是 Python 限定的特異功能。因為如上所述，Python 在處理負數餘數 (e.g. -1 % 5)時，會自動向下捨入從而得到正數的 4。</p>
<p>但在 JavaScript、Java 或 C++ 裡，算出來會是 -1。若強行帶入，程式會直接崩潰喵~</p>
<p>據聞若要在其他語言上使用好像需要另外加上底數。以石頭剪刀布為例，像醬</p>
<pre><code>let result = ((player_choice - computer_choice) + 3) % 3;
</code></pre>
<p>但這個筆者還沒試過，有興趣的小伙伴歡迎自己來試試</p>
<p> </p>
<h1>後記</h1>
<p>好啦，不說太多了 (？</p>
<p>所謂活學活用，我要繼續透過今天的所學，去優化那個播放器了</p>
<p>各位我們下次見啦~</p>
]]></content>
    <author><name>ゆき</name></author>
    <category term="技術文"/>
  </entry>
  <entry>
    <title>部落格搬家啦！一次從 Hexo 到 Astro 的大遷徙記~</title>
    <link href="https://moe.lolicon.io/posts/blog-deployments/migrating-from-hexo-to-astro/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/blog-deployments/migrating-from-hexo-to-astro/</id>
    <published>2026-03-01T00:00:00.000Z</published>
    <updated>2026-04-29T00:00:00.000Z</updated>
    <summary>記錄從 Hexo + ShokaX 遷移到 Astro + Mizuki 的完整過程</summary>
    <content type="html"><![CDATA[<p>沒錯，我的部落格搬新家了 :)</p>
<p>這個部落格原本是基於 Hexo 搭配 ShokaX 主題所建立的，不過最近我把它整個用 <a href="https://astro.build/">Astro</a> 這個現代的 SSG 框架重寫了一遍。因此這邊文章會記錄一下我遷移的過程和一些心得。</p>
<p> </p>
<h1>為何要遷移 ?</h1>
<p>其實真要細說原因的話⋯⋯ 老實講我也不太清楚</p>
<p>但一般來說，主要歸類成以下幾點</p>
<h2>1. 原域名被學校封鎖</h2>
<p>如果你和筆者有過幾分謀面，想必也應該知道 <code>lolicon.wtf</code> 這 domain name 吧</p>
<p>這個 domain name 是筆者在去年年頭買下的，當下根本沒有留意其他因素，只是看到覺得有趣便買了</p>
<p>然而眼睛亮的你們也看到了，這個域名，是以 wtf 作結的</p>
<p>起初我是不怎相信的，直到發了這篇脆文</p>
<p>:::chat
[芋泥|2026-02-23|right]
我的domain name再度成功被學校ban掉了</p>
<p>[脆友1|2026-02-23]
這是Palo Alto防火牆的URL Filtering 功能阻擋的，那個domain name應該會被各大資安大廠列在Adult才對😑</p>
<p>你可以到這個網站查查：
https://urlfiltering.paloaltonetworks.com</p>
<p>[脆友2|2026-02-23]
單純他看你不爽吧</p>
<p>[脆友3|2026-02-23]
TLD 的問題？</p>
<p>[脆友4|2026-02-24]
誰叫你要用這麼名字🤣…</p>
<p>[脆友5|2026-02-24]
因為網址有WTF字眼？</p>
<p>[脆友6|2026-02-24]
用個正常啲嘅 domain name 唔得嘅
:::</p>
<p>好吧⋯ 徹底被打臉了⋯⋯</p>
<p>這 TLD 很常見於那些成人影片網站上，其內容主要以 18+ 為重。而學校網絡一般都會把不當內容全封，所以⋯⋯ 對，我的網站在上線首天便已被學校的 RADIUS 封掉了⋯⋯</p>
<p>唉⋯ 年少無知啊⋯⋯</p>
<h2>2. 維護性問題</h2>
<h3>停止更新</h3>
<p>Hexo 的生態系雖然成熟，但許多套件的維護狀況已經不太樂觀。</p>
<p>比如  <code>hexo-admin</code> ，它裏面發文的 API 已經沒有維護了。隨著 Hexo 日益更新下已經完全失效了</p>
<p>而我在用的 <code>hexo-theme-shokax</code> ，也將在不遲於2026年中，宣布停止更新了</p>
<h3>ShokaX 主題的為人垢病</h3>
<p>ShokaX 可是一款讓我又愛又恨的主題，其繼承自 Shoka 。雖則好看，設定上並不簡單</p>
<p>首先它雖是屬於 Hexo 的主題之一，然而在安裝上並不能透過  <code>git clone</code>  直接複製。而是需要透過 <code>npm install</code> 安裝。我也明白是因為它直接重寫了整個渲染組件而且新增了一堆自家的邏輯，但這無擬令初學入門的技術門檻大幅提高。</p>
<p>再來我通常都會對正在用的主題大修小補，正因為它不能  <code>git clone</code> ，這些修改我一般也只能在主機上執行，直接深入主題的原始碼。而當每次主題要更新時，也只能先一一備份修改好的設定，待更新後再一一還原。有時候還需要注意上游的修改是否與現有的更改有所衝突，對於我一個不怎打理依賴的人來說可是惡夢。</p>
<p>然後說到更新，這個主題每次一來就是很大的更新，而且每次更新伴隨着不少由輕到重的漏洞，需要花費大量時間慢慢磨合。例如：</p>
<ul>
<li>
<p>0.4.22 更新重寫了 AI Summary</p>
</li>
<li>
<p>0.5.0 更新觸發了底層邏輯錯誤，VM 佔用了全部資源，還搞得我整台 Proxmox 伺服器卡死了</p>
</li>
<li>
<p>0.5.2 更新直接把整個渲染器重寫了，伴隨着CSS錯位問題</p>
</li>
</ul>
<p>說實話這樣的一個主題，我是不怎樣放心繼續用下去的。縱然我的主題版本停留在較為穩定的 0.4.25，仍出現過不少次因為主題原因導致 <code>HTTP 503</code> 的問題。所以便決定藉着這次換域名的機會，把部落格整個重寫一遍了。</p>
<h2>3. 現代化的開發體驗</h2>
<p>身為一個前端開發者，我還是希望能用 TypeScript、JSX 這些現代化的工具來開發，而不是傳統的 template engine。</p>
<p>其實我是不怎討好 Hexo 的整套插件系統的，因為他每個插件都需要透過 inject 的方式來運作，不能直接集成到主題內。而 Hexo 在 JS 方面的支援度上也比較欠缺。像是筆者經常會在文章內使用 <code>jQuery</code> ，然而 Hexo 並沒有內建這東西，需要自行從 HTML 引入。</p>
<h2>4. Hexo 自身的問題</h2>
<p>說到底其實 Hexo 本身也有很多讓我不解的地方。比如它可以直接用 markdown 寫文章，然而早期版本並不支援透過 markdown 語法插入圖片，需要 enable <code>post_asset_folder</code> 然後透過 <code>custom tag</code> 方能插入。</p>
<p>比如我現在要把 <code>image.jpg</code> 放進文章 <code>hello-world</code> 裏，首先要去 <code>_config.yml</code> 裏 enable <code>post_asset_folder</code></p>
<pre><code>post_asset_folder: true
</code></pre>
<p>然後要在 <code>hello-world</code> 需插入的位置新增下面的 <code>custom tag</code></p>
<pre><code>{% asset_img image.jpg This is the image %}
</code></pre>
<p>再把圖片上傳到 <code>post_asset_folder</code></p>
<pre><code>/blog-root/_posts/hello-world/image.jpg
</code></pre>
<p>這樣圖片才能正常的在文章內顯示</p>
<p>然而問題來了，這種方法雖可行，但這也意味着我無法把它們像一般 markdown 那樣在各種 MD 文件編輯器或是平台中正確顯示。對於筆者這些要常在文章內插入圖片的人來講可謂不便。</p>
<p> </p>
<h1>為什麼選擇 Astro ？</h1>
<p>說實話其實筆者也有考慮過其他 generator，像是 Hugo, Next.js 那些的。但看過網上有指 Hugo 在 Markdown parser 的客製化上限制比較大，且有機會把 <code>$$</code> 和 <code>_</code> 這些 Latex 會用到的 special characters 漏掉了。而在實測後也發現問題的確存在，故終決定排除在外。</p>
<p>而 Next.js ，我不能說它不香。畢竟它是個基於 React 且同時支援 SSR 和 SSG 兩者的框架。</p>
<p>起初我也有想過把網站遷到 Next.js 去的，但考慮過後發現，就目前的內容而言，SSR 對本站的用處不大。真要是強行遷過去的話反而會覺得有點小題大做了⋯⋯</p>
<p>至於 Astro ，經了解後，發現它是個基於 Vite、能夠生成 MPA 的 SSG 框架。從路徑到各種 Markdown parser 插件等都能夠自訂。簡單來講，只要願意寫 code 基本上沒什麼做不到的東西。</p>
<p>而且大部分的 code 都能透過 TypeScript 完成，有靜態類型的優勢。這點也是深得筆者的喜歡。</p>
<p> </p>
<h1>遷移需求</h1>
<p>在開始動工之前，我列出了幾個必須達成的需求：</p>
<ul>
<li><strong>完全 Static Site</strong> - 可以部署到 GitHub Pages、Cloudflare Pages 等平台</li>
<li><strong>即開即食</strong> - 盡量避免主題所需以外的 additional dependencies</li>
<li><strong>效能不能變差</strong> - Lighthouse Performance 桌面版分數要和舊站相若</li>
<li><strong>新舊結合</strong> - 盡可能保留舊站元素，再融合新站特色</li>
</ul>
<p> </p>
<h1>遷移過程</h1>
<h2>重構插件</h2>
<p>正如前文所說，Hexo 有它獨立的插件引入方式。一般來說，需要用其 inject 方式來引入。而通常這類插件都只是含有短短幾句的 JS 代碼。</p>
<p>所以很簡單，只需要把上面 hexo inject 的部份刪掉</p>
<pre><code>hexo.extend.filter.register('before_post_render', function(data){

    data.content = data.content.replace(/!{1}\[([^\[\]]*)\]\((.*)\s?(?:".*")?\)/g
    // ...

    return data;
});
</code></pre>
<p>然後略為修飾，把剩下的整段 JS 搬進 <code>./script</code> 裏面便可以了。</p>
<h2>重構 renderer</h2>
<p>好，重頭戲來了。這也是整個重構過程中耗時最長的部分。</p>
<p>還記得上面說過 ShokaX 是一款讓我又愛又恨的主題嗎？沒錯，它自己擁有另一套獨有的 renderer ，其中之一名為 <code>hexo-renderer-multi-next-markdown-it</code> （有好幾個但功能大同小異）。</p>
<p>而他們全都是以 <code>markdown-it</code> parser 來做基底，內含一大堆 <code>markdown-it</code> 插件。然而 Astro 的 markdown 支援並不是靠<code>markdown-it</code>，而是 <code>remark</code>。</p>
<p>問題來了， <code>remark</code> 這 parser 主打的是 AST，和<code>markdown-it</code> 完全不同。這意味着單靠直接遷移是完全行不通的。</p>
<p>幸好兩者有一個共通點，那就是它們皆支援 custom plugin，也就是理論上若能把這堆插件全部 rewrite 做 <code>remark</code> plugin 的話，剩下的便好處理了。</p>
<p>於是又上網了解一下 <code>remark</code>  的 plugin 結構，然後請教了 Claude 大師：</p>
<p>:::chat
[芋泥|2026-03-02|right]
用 markdown-it 寫的插件能否遷到 remark 去呢</p>
<p>[芋泥|2026-03-02|right]
我要換 Astro 了但它用不了 markdown it TT</p>
<p>[Claude 4.5 Sonnet|2026-03–02]
這個… 難度有點大呢~</p>
<p>[Claude 4.5 Sonnet|2026-03–02]
畢竟兩者的架構差很遠了…</p>
<p>[Claude 4.5 Sonnet|2026-03–02]
要不您把 repo 發過來我看看再能怎樣搞吧</p>
<p>[芋泥|2026-03-02|right]
好的 等我一下
:::</p>
<p>就這樣過了兩天</p>
<p>:::chat
[芋泥|2026-03-04|right]
hexo-theme-shokax/hexo-renderer-multi-markdown-it hexo-theme-shokax/hexo-renderer-multi-next-markdown-it hexo-theme-shokax/hexo-renderer-aether</p>
<p>[芋泥|2026-03-04|right]
就是這些了 它們功能上差不多</p>
<p>[芋泥|2026-03-04|right]
你先替我看看它們是如何工作的 然後順着其邏輯改一改試試</p>
<p>[Claude 4.5 Sonnet|2026-03–04]
好的，這是其中一個經修改後的插件：
:::</p>
<p>隨即試了一下，果然出 error 了</p>
<p>:::chat
[芋泥|2026-03-05|right]
出 error 了</p>
<p>[芋泥|2026-03-05|right]
一堆 unsupported operations</p>
<p>[Claude 4.5 Sonnet|2026-03–05]
看來還是得重構了…</p>
<p>[Claude 4.5 Sonnet|2026-03–05]
這個要 patch 的話有點難</p>
<p>:::</p>
<p>然後因為要段考了，又過了一個星期</p>
<p>:::chat
[芋泥|2026-03-13|right]
我研究了 發現它是走 AST 路線的</p>
<p>[芋泥|2026-03-13|right]
要搞清楚 mdast 才可以開弄</p>
<p>[芋泥|2026-03-13|right]
你先想一想它們在 mdast 的 syntax 和 logic 下該如何處理 然後再和我一起重構</p>
<p>[Claude 4.5 Sonnet|2026-03–13]
好的。那 css 的部分你希望該怎樣？</p>
<p>[芋泥|2026-03-13|right]
盡量保留吧 有需要的才考慮 refactor</p>
<p>:::</p>
<p>於是便開展了一場大型的重構激戰，過了幾天</p>
<p>:::chat
[芋泥|2026-03-19|right]
render 出來了</p>
<p>[芋泥|2026-03-19|right]
目前版面沒什麼問題</p>
<p>[芋泥|2026-03-19|right]
嗚嗚嗚終於搞好了 TT…</p>
<p>[Claude 4.5 Sonnet|2026-03–19]
恭喜！也真是辛苦你了…</p>
<p>:::</p>
<p>然而你們以為這樣便結束了？非也…</p>
<p>你們上面看到的 chatbox ，那個並不是原本 renderer 內有的，而是借鍳某位大佬的 source code 改造而成的。</p>
<p>其實那個是筆者還在用 Hexo 的時候便一直以來都想要的東西，只是在 Hexo 和 <code>markdown-it</code> 的架構下要放這種東西的話幾乎不可能。所以便趁着這次重構的機會一同把它帶進來了。</p>
<p>只不過，要處理那東西，可謂又是一場惡戰。礙於篇幅所限，這個留在下一篇文再說。</p>
<h2>全自動化 deploy</h2>
<p>以前用 Hexo 每當要出新文章時，都要先把文章 <code>git push</code> 到 repo 上，然後再另外從 server 上面 <code>git pull</code> 以取得最新變更 （除非你只是打算透過 <code>hexo-deploy-git</code> 把網頁 deploy 到 GitHub Pages 上）。某程度上或會顯得有些不便。</p>
<p>所以這也是本次重構重點更新的項目之一，那就是自動在我 push to repo 後直接從 server 上面 <code>git pull</code>， 在毋須任何手動操作的情況下全自動更新網站。</p>
<p>為此我在目標 server 上安裝了 GitHub self-hosted runner，透過 SSH 來登入 GitHub，再把其連接到 <code>github-actions</code> 裏，隨後便寫出了下面這段 YAML 代碼：</p>
<pre><code>name: Deploy to Production Server

on:
  push:
    branches: [ master ]
  workflow_dispatch:
  repository_dispatch:
    types: [ content-updated ]  # Must match event-type from trigger

jobs:
  build-and-deploy:
    runs-on: self-hosted
    defaults:
      run:
        working-directory: /home/neko/srv/blog/Mizuki-Revanced
    steps:
      - name: SSH Pre-Test
        run: |
          whoami
          ssh -T git@github.com || true
          
      - name: Pull latest changes
        run: |
          git fetch origin master
          git reset --hard origin/master
          git submodule update --init --recursive
          
      - name: Install dependencies
        run: pnpm install --no-frozen-lockfile
      
      - name: Clean cache and backup dist, without removing the dist folder itself
        run: |
          rm -rf .astro node_modules/.astro
          if [ -d "dist" ]; then
            rm -rf dist_backup
            cp -r dist dist_backup
            rm -rf dist/*
            echo "Existing dist backed up to dist_backup."
          fi
      
      - name: Build site with no caches
        id: build
        run: pnpm run build --force
        env:
          ENABLE_CONTENT_SYNC: true
          CONTENT_REPO_URL: ${{ secrets.CONTENT_REPO_URL }}
          USE_SUBMODULE: true

      - name: Cleanup backup on success
        if: success()
        run: |
          rm -rf dist_backup
          echo "Build succeeded, backup removed."

      - name: Restore backup on failure
        if: failure()
        run: |
          rm -rf dist/*
          if [ -d "dist_backup" ]; then
            mv dist_backup/* dist/
            rm -rf dist_backup
            echo "ERROR: pnpm build failed, usually this is due to a syntax error in the markdown files."
            echo "For safety, the previous dist has been restored. Please check the build logs for details."
          fi
          exit 1

      # Our Docker Container will automatically serve the lastest files in real time.
</code></pre>
<p>簡單來說整個流程如下：</p>
<pre><code>flowchart TD
    A[/"Trigger Event"/]
    A1["Push to master"]
    A2["Manual dispatch"]
    A3["Repository dispatch\n(content-updated)"]

    A1 --&gt; A
    A2 --&gt; A
    A3 --&gt; A

    A --&gt; B["Runner: self-hosted"]

    B --&gt; C["SSH Pre-Test"]

    C --&gt; D["Pull Latest Changes from Content Repository"]

    D --&gt; E["Install dependencies"]

    E --&gt; F["Clean Cache &amp; Backup dist\nrm -rf .astro node_modules/.astro"]

    F --&gt; G{"dist/ exists?"}

    G -- Yes --&gt; H["cp -r dist dist_backup\nrm -rf dist/*"]
    G -- No --&gt; I["Skip backup"]

    H --&gt; J["Build Site"]
    I --&gt; J

    J --&gt; K{"Build\nSucceeded?"}

    K -- "Success" --&gt; L["Cleanup Backup\nrm -rf dist_backup"]
    L --&gt; M["Docker serves latest dist/ automatically"]

    K -- "Failure" --&gt; N["Restore Backup\nrm -rf dist/*\nmv dist_backup/* dist/"]
    N --&gt; O["Exit with error code 1\nPrevious dist restored"]

</code></pre>
<h2>Collection</h2>
<p>Astro 中有個 collection 機制可以用來管理類似性質的一些資料，例如 blog post 這種同質性很高的東西。</p>
<p>目前的話在格式方面的變化，其實只是從原本 Hexo 的</p>
<pre><code>source/_posts
├── post-1
│   ├── index.md
│   └── cover.png
├── post-2
│   ├── index.md
│   └── cover.png
</code></pre>
<p>變成了這樣</p>
<pre><code>src/content/posts
├── post-1
│   ├── index.md
│   └── cover.png
├── post-2
│   ├── index.md
│   └── cover.png
</code></pre>
<p>這個設計也解決了我在 Hexo 的一個痛點：Markdown 中引用圖片的 relative path 問題。現在圖片就放在文章旁邊，編輯器的 preview 也能正常顯示了。</p>
<p> </p>
<h1>主題</h1>
<p>主題部分其實沒什麼好說的，這次的重構可謂非常突然，也沒有事先作深入考慮。你要說筆者能夠在短時間內且同時兼顧學業的情況下把整個 layout 用 Astro 從零開始寫起那只會是無稽之談⋯⋯</p>
<p>反正就是在某次機緣巧合之下，發現了 <a href="https://github.com/LyraVoid/Mizuki">Mizuki</a> 這個主題。它可以說跟 ShokaX 長得 87 分像，基本上那邊具備的功能這邊也大同小異。這點說實話深得筆者喜歡，同時也能省下了不少遷移上的麻煩。</p>
<p>於是便決定了以其作為基礎，然後再根據自己的需求做調整。像是一大堆上述的 remark plugin 全是筆者自己後來寫好再加上去的。</p>
<p>但每個主題也不一定十全十美，像是這主題本來也有一些 <a href="https://github.com/LyraVoid/Mizuki/pull/441">讓人不解的地方</a> 和 <a href="https://github.com/LyraVoid/Mizuki/pull/434">少量漏洞</a>，所以筆者作為一個開發者，在完善自己的網站的同時，也有把一些基於非個人化的修改回饋予作者，藉以為大家未來在使用上，帶來更美好的體驗。 (在 <a href="https://github.com/LyraVoid/Mizuki/issues?q=author%3AHoshinowo-Yuki">這裏</a> 可以找到一些出自筆者的 contributions)</p>
<p>而 Astro 的 component 架構也讓 customization 變得非常容易，基本上就像寫 React / Vue 一樣。</p>
<h3>其他功能</h3>
<p>其他像是 RSS、Sitemap、搜尋功能等，Astro 的生態系都有現成的 integration 可以使用，整合起來也是非常順利。</p>
<p>（待續）</p>
]]></content>
    <author><name>ゆき</name></author>
    <category term="Blog 生成和部署系列"/>
  </entry>
  <entry>
    <title>「It&apos;s now safe to turn off your computer.」</title>
    <link href="https://moe.lolicon.io/posts/tech-posts/its-now-safe-to-turn-off-your-computer/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/tech-posts/its-now-safe-to-turn-off-your-computer/</id>
    <published>2025-03-22T00:00:00.000Z</published>
    <updated>2026-03-07T00:00:00.000Z</updated>
    <summary>從一句話淺談科技歷史的回憶</summary>
    <content type="html"><![CDATA[<h1>背景</h1>
<p>近日筆者在折騰自己的 Flipper。碰巧有一次關機時忘了拔 USB 線，然後它跳出了這個畫面來：</p>
<p><img src="./flipper_zero_shutdown.jpg" alt="Its now safe to unplug the USB cable" /></p>
<p>一𣊬間便引起了筆者的注意：這不就跟「It's now safe to turn off your computer.」一樣概念嗎？於是做了點功課，把這篇文肝了出來......</p>
<p> </p>
<h1>Epilogue</h1>
<p>如果你曾經使用過 1990 年代和 2000 年代初期的電腦，你一定對那句經典提示不陌生：</p>
<p>:spoiler[(或是像筆者一樣愛研究一些古科技啦 XD)]</p>
<p><img src="./Its_now_safe_to_turn_off_your_computer_Screen.webp" alt="Its now safe to turn off your computer" /></p>
<p>這句話曾經是 Windows 作業系統用戶的日常，代表著一個時代的科技特色</p>
<p>隨著電腦技術的進步，這句話逐漸消失在我們的生活中，但它背後所蘊含的歷史與技術背景，值得我們細細回味</p>
<p>:spoiler[(說實話筆者在小學時期折騰一台老 CRT Dell 時也有看到過這個畫面 XD，不過也就只有幾次罷了 TT...)]</p>
<p> </p>
<h1>那句話出現的原因: 早期電腦與 AT 電源設計</h1>
<p>在 1990 年代，電腦的硬體設計與現代相比有很大的不同。當時的電腦大多採用 AT (Advanced Technology) 電源架構。即機箱具有一個直接連到電源供應器的電源開關。而開關的主要形式，是一個雙極式的開關。其四條針腳焊接到對應的電線。這種設計下無法進行軟件關機。需要用戶手動按下實體電源按鈕來關閉電腦</p>
<p>所以，早前 Windows 95丶Windows 98 等作業系統在結束運行時，由於無法直接控制硬體電源的開關，當你在系統中點擊「關機」之後，作業系統會將所有的程序結束、保存數據並安全退出，隨後顯示「It's now safe to turn off your computer. (您現在可以放心關機)」的提示，告訴用戶可以放心按下電源按鈕了</p>
<p><img src="./Its_now_safe_to_turn_off_your_computer_zh_TW.png" alt="您現在可以放心關機" /></p>
<p>這段流程的設計，是為了確保在硬碟寫入操作結束後再關閉電腦，避免數據損壞或系統損壞的風險</p>
<p> </p>
<h1>技術的進步</h1>
<h2>從 AT 到 ATX 架構</h2>
<p>1995 年，Intel 制定了 ATX (Advanced Technology Extended) 架構，用於取代 AT 架構。其最大的改進之一就是讓電源供應器無須再以電線㶥接至電腦機箱的電源開關，並且引入了軟體控制電源的能力。這意味著作業系統可以直接控制電腦的開關，而不需要用戶手動按下實體按鈕</p>
<p>在 Windows 2000 和 Windows XP 中，這一技術得到了廣泛應用。當用戶點擊「關機」時，作業系統不再顯示「It's now safe to turn off your computer.」，而是直接關閉電腦電源，讓整個過程更加自動化和方便</p>
<p><img src="./windows-xp-shutdown.png" alt="Windows XP shutdown screen" /></p>
<p>此後，這項技術也就成為了當今所有主流作業系統的標配</p>
<h2>ACPI 的問世</h2>
<p>1996 年，Intel、Microsoft 和 Toshiba 等科技公司聯合推出了 ACPI (Advanced Configuration and Power Interface，進階組態與電源介面)，成為現代電腦電源管理的一個重要里程碑。它能夠讓作業系統直接管理電腦硬體的電源功能，而不再依賴 BIOS 或硬體來進行操作</p>
<p>ACPI 的一大進步是實現了軟體控制電源關閉。作業系統可以在完成所有操作後，自動切斷電腦電源，取代了早期需要用戶手動按下實體電源按鈕的麻煩</p>
<p>ACPI 定義了多種電腦電源狀態，例如工作狀態（S0）、睡眠模式（S3）、休眠模式（S4）及完全關機狀態（S5），從而使電腦能夠根據需求進行節能操作。這些功能不僅讓桌上型電腦更加便利，也提升了筆記型電腦的能源效率，延長了電池壽命</p>
<p>ACPI 還支援硬體設備的即時管理，允許作業系統根據需要啟用或關閉特定的硬體設備，例如硬碟或顯示器，進一步降低能源消耗</p>
<p> </p>
<h1>一段早已消失的記憶，卻充滿幾代人的回憶</h1>
<p>對於許多經歷過早期電腦時代的人來說，「It's now safe to turn off your computer.」這句話不僅是一個提示，更是一種懷舊的象徵。它代表了那個時代的技術限制，也提醒我們科技的進步如何改變了我們的日常生活</p>
<p><img src="./Its_now_safe_to_turn_off_your_computer_XP.png" alt="It is now safe to turn off your computer" /></p>
<p>如今，我們的電腦和智慧裝置幾乎可以隨時進入休眠或關機狀態，而不需要擔心數據損壞的問題。然而，那句簡單的提示語，卻像是時光機一樣，把我們帶回到一個需要耐心等待的時代</p>
<p><img src="./Windows-11-shutdown.png" alt="Windows 11 shutdown screen" /></p>
<p> </p>
<h1>Prologue</h1>
<p>「It's now safe to turn off your computer.」的消失，標誌著電腦硬體與軟體設計的進步。從需要手動關機，到如今自動化的流程，這一變化體現了科技如何讓使用者體驗變得更簡單、更人性化</p>
<p>雖然這句話已經成為歷史，但它仍然是許多人心中的一段美好回憶。當我們回顧那個時代，不僅僅是在緬懷過去的科技，更是在反思我們如何靠著創新一步步走向今天的便利生活</p>
<p>你還記得這句話嗎？這句曾經讓人熟悉的提示，是否也勾起了你對過去的回憶呢？</p>
]]></content>
    <author><name>ゆき</name></author>
    <category term="隨筆"/>
  </entry>
  <entry>
    <title>如何在 Windows 11 中更改使用者個人資料夾名稱</title>
    <link href="https://moe.lolicon.io/posts/tech-posts/change-name-of-user-profile-folder-in-windows-11/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/tech-posts/change-name-of-user-profile-folder-in-windows-11/</id>
    <published>2025-03-12T00:00:00.000Z</published>
    <updated>2026-03-08T00:00:00.000Z</updated>
    <summary>除了在設置時選擇使用本機帳戶登入外，真的別無他法了嗎？</summary>
    <content type="html"><![CDATA[<p>在 Windows 11 中，當我們建立新的使用者帳戶時，系統會自動分配一個個人資料資料夾名稱。</p>
<p>如果你是選擇以 Microsoft 帳戶登入的話，那麼你的使用者個人資料夾名稱通常是根據你的帳戶名稱或電子郵件的前5個字元來命名。</p>
<p>然而，有時候這個名稱可能並不是你想要的。尤其如果你是一個隨便設置電郵的人，那名稱一定會顯得十分精彩......</p>
<p>那麼，除了在設置時選擇使用本機帳戶登入外，真的別無他法了嗎？ :thinking:</p>
<p>今天筆者就和大家聊聊如何安全地更改 Windows 11 中的使用者個人資料資料夾名稱，確保不會影響系統的正常運行 :)</p>
<p>:::warning
在開始之前，請留意</p>
<ul>
<li>
<p>你<strong>必須</strong>具備管理員權限才能執行此操作</p>
</li>
<li>
<p>更改<strong>個人資料資料夾名稱</strong> <strong>不會</strong> 自動更改 <strong>使用者帳戶名稱</strong></p>
</li>
<li>
<p>此過程涉及修改<strong>Windows 註冊表 (Registry)</strong>，請<strong>務必謹慎操作</strong>，並建議<strong>先備份重要資料</strong>
:::</p>
</li>
</ul>
<p> </p>
<h1>Step 1：登出目標帳戶</h1>
<ol>
<li>
<p>先<strong>登出</strong>你想要更改個人資料資料夾名稱的帳戶</p>
</li>
<li>
<p>使用<strong>其他管理員帳戶</strong>登入 Windows 11。如果你沒有其他管理員帳戶，可以像筆者一樣，<strong>啟用內建的 Administrator 帳戶</strong>來執行此操作。</p>
</li>
</ol>
<p> </p>
<h1>Step 2：查找帳戶的 SID</h1>
<ol>
<li>
<p>按 :keyboard[Win]{theme} + :keyboard[X]，選擇「終端機 (管理員)」(Terminal (Admin))</p>
</li>
<li>
<p>執行以下任一指令來查找目標帳戶的<strong>SID（安全識別碼）</strong>：</p>
<pre><code>powershell "Get-LocalUser | Select-Object -Property @('Name', 'SID')"
</code></pre>
<p>或</p>
<pre><code>wmic useraccount get name,SID
</code></pre>
</li>
<li>
<p>記下<strong>對應於你要更改的帳戶</strong>的 SID，例如 <code>S-1-5-21-2212846312-626644311-134141314-</code></p>
</li>
</ol>
<p> </p>
<h1>Step 3：修改 Windows 註冊表 (Registry)</h1>
<ol>
<li>
<p>開啟「登錄編輯程式」(:keyboard[Win]{theme} + :keyboard[R] → 輸入 <code>regedit</code> → 按「確定」(OK))</p>
</li>
<li>
<p>前往以下路徑：</p>
<pre><code>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\S-1-5-21-xxxxxxxxxx
</code></pre>
<p><strong>（將 <code>S-1-5-21-xxxxxxxxxx</code> 替換為你的 SID）</strong></p>
</li>
<li>
<p>找到 <strong>ProfileImagePath</strong>，雙擊打開並修改路徑 e.g. 將 <code>C:\Users\&lt;OLD_NAME&gt;</code> 改為 <code>C:\Users\&lt;NEW_NAME&gt;</code></p>
</li>
<li>
<p>按「確定」(OK)，然後關閉登錄編輯程式</p>
</li>
</ol>
<h1>Step 4：重新命名個人資料資料夾</h1>
<ol>
<li>
<p>開啟檔案總管（:keyboard[Win]{theme} + :keyboard[E]）</p>
</li>
<li>
<p>前往 <code>C:\Users</code>，找到<strong>舊的個人資料資料夾</strong> (e.g. <code>&lt;OLD_NAME&gt;</code>)</p>
</li>
<li>
<p>右鍵點擊 → 選擇「重新命名」，將其更改為新名稱 (e.g. <code>&lt;NEW_NAME&gt;</code>)</p>
</li>
<li>
<p>如果出現「此資料夾正在使用中」(Folder in use . . . file or folder is open in another program) 的錯誤：</p>
<ul>
<li>
<p>請確保目標帳戶已完全登出</p>
</li>
<li>
<p>重新啟動電腦後再嘗試重新命名</p>
</li>
</ul>
</li>
</ol>
<p> </p>
<h1>Optional 5：建立符號連結（Symbolic Link）</h1>
<p>某些應用程式或系統設定仍可能引用舊的資料夾名稱，你可以使用<strong>符號連結</strong>來避免錯誤：</p>
<ol>
<li>
<p>開啟命令提示字元 (管理員模式)</p>
</li>
<li>
<p>執行以下指令來建立符號連結：</p>
<pre><code>mklink /d "C:\Users\&lt;OLD_NAME&gt;" "C:\Users\&lt;NEW_NAME&gt;"
</code></pre>
</li>
</ol>
<p>任何仍尋找 <code>C:\Users\&lt;OLD_NAME&gt;</code> 的程式都會被自動導向 <code>C:\Users\&lt;NEW_NAME&gt;</code></p>
<p> </p>
<h1>Optional 6：修正 OneDrive 及 Windows 搜尋索引</h1>
<ol>
<li>
<p>如果你使用 OneDrive，請手動變更 OneDrive 資料夾路徑至新的個人資料資料夾名稱</p>
</li>
<li>
<p>重建 Windows 搜尋索引，以確保搜尋功能不會指向舊的資料夾名稱</p>
</li>
</ol>
<p> </p>
<p>這樣你就可以在不影響系統的穩定性下，更改 Windows 11 使用者個人資料資料夾的名稱啦~</p>
]]></content>
    <author><name>ゆき</name></author>
    <category term="技術文"/>
  </entry>
  <entry>
    <title>Understanding Temperature and Heat Capacity</title>
    <link href="https://moe.lolicon.io/posts/physics/temperature-and-heat-capacity/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/physics/temperature-and-heat-capacity/</id>
    <published>2020-09-10T00:00:00.000Z</published>
    <updated>2026-03-08T00:00:00.000Z</updated>
    <summary>Temperature is a fundamental physical quantity that describes the degree of hotness or coldness of an object. Heat capacity, on the other hand, is a measure of how much heat energy is required to change the temperature of an object by a certain amount.</summary>
    <content type="html"><![CDATA[<h1>Definition of Temperature</h1>
<p>:::note
Temperature is <strong>a measure of degree of hotness</strong> of <strong>an object</strong>. (Oxford NSS Physics TB 2nd edition, 2014)
:::</p>
<p> </p>
<h1>Thermometer</h1>
<h2>Type of Thermometers</h2>
<ul>
<li>
<p>Infrared thermometer</p>
</li>
<li>
<p>Resistance thermometer</p>
</li>
<li>
<p>Mercury-in-glass thermometer</p>
</li>
</ul>
<h2>Thermometer Calibration</h2>
<p>Thermometer calibration ensures accurate temperature measurement. It involves adjusting a thermometer to match a known standard.</p>
<h3>Steps for Calibration:</h3>
<ol>
<li>
<p>Ice Point Calibration (0°C)</p>
<ul>
<li>
<p>Place the thermometer in a mixture of <strong>crushed ice and water</strong>.</p>
</li>
<li>
<p>Wait for it to stabilize, then adjust if needed to read <strong>0°C</strong>.</p>
</li>
</ul>
</li>
<li>
<p>Boiling Point Calibration (100°C)</p>
<ul>
<li>
<p>Place the thermometer in <strong>boiling water</strong> (at sea level).</p>
</li>
<li>
<p>Ensure it reads <strong>100°C</strong>; adjust if necessary.</p>
</li>
</ul>
</li>
<li>
<p>Using a Reference Thermometer</p>
<ul>
<li>Compare readings with a <strong>calibrated standard thermometer</strong> at different temperatures.</li>
</ul>
</li>
<li>
<p>Adjust and Record</p>
<ul>
<li>
<p>If there is a deviation, apply a correction factor.</p>
</li>
<li>
<p>Document the calibration results for future reference.</p>
</li>
</ul>
</li>
</ol>
<p> </p>
<h1>Heat Capacity</h1>
<h2>Heat Capacity (C)</h2>
<p>$$Q = C \Delta T$$</p>
<p>where</p>
<ul>
<li>
<p>$Q$ = Heat Energy (Unit: $J$)</p>
</li>
<li>
<p>$C$ = Heat Capacity (Unit: $J K⁻¹$ / $J °C⁻¹$)</p>
</li>
</ul>
<h2>Specific Heat Capacity (c)</h2>
<p>$$Q = mc \Delta T$$</p>
<p>where</p>
<ul>
<li>
<p>$Q$ = Heat Energy (Unit: $J$)</p>
</li>
<li>
<p>$c$ = Specific Heat Capacity (Unit: $J kg⁻¹$ $K⁻¹$ / $J kg⁻¹$ $°C⁻¹$)</p>
</li>
<li>
<p>$m$ = mass of an object (unit: kg)</p>
</li>
<li>
<p>$ΔT$ = change in temperature (unit: °C)</p>
</li>
</ul>
<p>:::warning
The letter 'c' in specific heat capacity <strong>must always be lowercase</strong>!
:::</p>
<h2>Molar Heat Capacity (Cₘ)</h2>
<p>$$Q = nCₘ \Delta T$$</p>
<p>where</p>
<ul>
<li>
<p>$Q$ = Heat Energy (Unit: $J$)</p>
</li>
<li>
<p>$Cₘ$ = Molar Heat Capacity (Unit: $J mol⁻¹ K⁻¹$ / $J mol⁻¹ °C⁻¹$)</p>
</li>
<li>
<p>$n$ = number of moles of a substance (unit: mol)</p>
</li>
<li>
<p>$ΔT$ = change in temperature (unit: °C)</p>
</li>
</ul>
<p> </p>
<h1>Heat Capacity vs. Specific Heat Capacity</h1>
<ul>
<li>
<p><strong>Heat Capacity (C)</strong> is an extensive property that depends on the amount of substance. It is the total heat energy required to raise the temperature of an object by 1 degree Celsius or 1 Kelvin.</p>
</li>
<li>
<p><strong>Specific Heat Capacity (c)</strong> is an intensive property that does not depend on the amount of substance. It is the heat energy required to raise the temperature of 1 kilogram of a substance by 1 degree Celsius or 1 Kelvin.</p>
</li>
</ul>
<p>The relationship between heat capacity and specific heat capacity can be expressed as:
$$C = mc$$</p>
<p>where</p>
<ul>
<li>
<p>$C$ = Heat Capacity (Unit: $J K⁻¹$ / $J °C⁻¹$)</p>
</li>
<li>
<p>$m$ = mass of an object (unit: kg)</p>
</li>
<li>
<p>$c$ = Specific Heat Capacity (Unit: $J kg⁻¹ K⁻¹$ / $J kg⁻¹ °C⁻¹$)</p>
</li>
</ul>
<p> </p>
<h1>Heat Capacity vs. Molar Heat Capacity</h1>
<ul>
<li>
<p><strong>Heat Capacity (C)</strong> is an extensive property that depends on the amount of substance. It
is the total heat energy required to raise the temperature of an object by 1 degree Celsius or 1 Kelvin.</p>
</li>
<li>
<p><strong>Molar Heat Capacity (Cₘ)</strong> is an intensive property that does not depend on the amount of substance. It is the heat energy required to raise the temperature of 1 mole of a substance by 1 degree Celsius or 1 Kelvin.</p>
</li>
</ul>
<p>The relationship between specific heat capacity and molar heat capacity can be expressed as:</p>
<p>$$Cₘ = c \cdot M$$</p>
<p>where</p>
<ul>
<li>
<p>$Cₘ$ = Molar Heat Capacity (Unit: $J mol⁻¹ K⁻¹$ / $J mol⁻¹ °C⁻¹$)</p>
</li>
<li>
<p>$c$ = Specific Heat Capacity (Unit: $J kg⁻¹ K⁻¹$ / $J kg⁻¹ °C⁻¹$)</p>
</li>
<li>
<p>$M$ = Molar Mass of the substance (Unit: $kg mol⁻¹$)</p>
</li>
</ul>
]]></content>
    <author><name>ゆき</name></author>
    <category term="Physics"/>
  </entry>
  <entry>
    <title>解決 Windows XP 虛擬機音效問題</title>
    <link href="https://moe.lolicon.io/posts/tech-posts/windows-xp-vm-sound-distortion-fix/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/tech-posts/windows-xp-vm-sound-distortion-fix/</id>
    <published>2025-03-14T00:00:00.000Z</published>
    <updated>2025-03-14T00:00:00.000Z</updated>
    <summary>那台 Windows XP VM 不管怎樣弄，開機音樂還是破破的，伴隨着失真</summary>
    <content type="html"><![CDATA[<p>是這樣的，筆者一直以來都在搞虛擬機器，起初還好好的，直到最近電腦壞了重灌後便開始出現問題了。</p>
<p>就是那台 Windows XP VM 不管怎樣弄，開機音樂還是破破的，伴隨着不少失真......</p>
<p>找了 Google 大神上很多方法也無解。直到突然想起了以前是透過在主機 (Host) 上改登錄檔解決的，隨即問起了 GPT ，結果一下子就搞定了</p>
<p>今天就來記錄一下筆者當時是如何解決問題的，順便當給自己和大家一些參考</p>
<p> </p>
<h1>Step 1: 開啟登錄編輯程式 (Registry Editor)</h1>
<ol>
<li>按 [Win]{.kbd} + [R]{.kbd .red} 打開 「執行」 (Run) ，然後輸入 <code>regedit</code> 再按 [Enter]{.kbd}</li>
</ol>
<p><img src="./regedit1.png" alt="Opening Registry Editor" /></p>
<ol>
<li>使用系統管理員帳戶驗證登入</li>
</ol>
<p> </p>
<h1>Step 2: 新增登錄檔 (Registry)</h1>
<p><img src="./regedit2.png" alt="Registry Editor" /></p>
<ol>
<li>移至</li>
</ol>
<pre><code>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\kernel
</code></pre>
<p><img src="./regedit3.png" alt="Registry Editor" /></p>
<ol>
<li>右鍵按 New -&gt; DWORD (32-bit) value</li>
</ol>
<p><img src="./regedit4.png" alt="Registry Editor" /></p>
<ol>
<li>按 [F2]{.kbd} 重新命名為 <code>GlobalTimerResolutionRequests</code>，然後按 [Enter]{.kbd} 進入設定頁面</li>
</ol>
<p><img src="./regedit5.png" alt="Registry Editor" /></p>
<p><img src="./regedit6.png" alt="Registry Editor" /></p>
<ol>
<li>修改其 Value data 為 1 並選擇 Base 為 <code>Hexadecimal</code>，完成後按 [Enter]{.kbd} 儲存</li>
</ol>
<p><img src="./regedit7.png" alt="Registry Editor" /></p>
<p> </p>
<p>完成！請關閉登錄編輯程式 (Registry Editor) 並重啟電腦以應用新設定</p>
<p>倘若一正常的話，恭喜你！ 你已經成功解決腦人的音效問題了，盡情享受你的 VM 吧！</p>
]]></content>
    <author><name>ゆき</name></author>
    <category term="技術文"/>
  </entry>
  <entry>
    <title>什麼? Linux 也能用RDP?</title>
    <link href="https://moe.lolicon.io/posts/tech-posts/linux-xrdp/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/tech-posts/linux-xrdp/</id>
    <published>2025-02-24T00:00:00.000Z</published>
    <updated>2025-02-24T00:00:00.000Z</updated>
    <summary>對，我們平常在 Windows 上用的遠端桌面在 Linux 原來能通過 Xrdp 實現。</summary>
    <content type="html"><![CDATA[<p>對，你沒聽錯，我們平常在 Windows 上用的遠端桌面在 Linux 原來也能通過一些插件實現。到底是什麼插件如此強大呢？這裏便不多賣關子了</p>
<p>今天就和大家聊聊如何在 Linux 安裝和配置 Xrdp 服務器，以及一些可能會遇到的問題和解決方法</p>
<p> </p>
<h1>什麼是 Xrdp？</h1>
<p>顧名思義，Xrdp 是一個微軟 (Microsoft) 遠端桌面協定（RDP）的開源實現，至今已有超過20多年歷史。能夠提供一個在功能還是兼容性方面和RDP一致的遠端桌面體驗。</p>
<p>它允許你透過圖形介面控制遠端系統。你可以透過RDP協定登入遠端機器，並且創建一個真實的桌面會話，整體上就像你登入自己的電腦一樣。</p>
<p> </p>
<h1>Step 1: 安裝</h1>
<h2>桌面環境</h2>
<p>大部份Linux主機通常並沒有預先安裝桌面環境。因此在繼續前，我們需要先給主機安裝一個桌面環境 (Desktop Environment，俗稱 DE)，用作 Xrdp 的後端</p>
<p>通常像 Ubuntu Debian 這類 distro 的 Source repo 中有幾個 DE 可選，例如 XFCE ､ GNOME 等。筆者這邊打算安裝 KDE Plasma</p>
<pre><code>sudo apt-get install kde-plasma-desktop
</code></pre>
<p>完成後便可以開始裝 Xrdp 了</p>
<h2>Xrdp</h2>
<p>我們首先在 terminal 中輸入以下指令</p>
<pre><code>sudo apt-get install xrdp
</code></pre>
<p>接着分別更改 <code>/etc/xrdp/xrdp.ini</code></p>
<pre><code>sudo sed -e 's/^new_cursors=true/new_cursors=false/g' \
           -i /etc/xrdp/xrdp.ini
</code></pre>
<p>和 <code>~/.xsession</code> 裏的參數，像醬</p>
<pre><code>echo "/usr/bin/startplasma-x11" &gt; ~/.xsession
D=/usr/share/plasma:/usr/local/share:/usr/share:/var/lib/snapd/desktop
C=/etc/xdg/xdg-plasma:/etc/xdg
C=${C}:/usr/share/kubuntu-default-settings/kf5-settings
cat &lt;&lt;EOF &gt; ~/.xsessionrc
export XDG_SESSION_DESKTOP=KDE
export XDG_DATA_DIRS=${D}
export XDG_CONFIG_DIRS=${C}
EOF
</code></pre>
<p>如果你使用的版本是 Ubuntu 20 或以上，便需要提供憑證存取權給 Xrdp 使用者以確保 Xrdp 能夠正常運作</p>
<pre><code>sudo adduser xrdp ssl-cert
</code></pre>
<p>然後在 <code>systemctl</code> 中啟用 <code>xrdp</code>，讓其在我們每次去開機時自動啟動</p>
<pre><code>sudo systemctl enable xrdp
</code></pre>
<p>如果你有啟用防火牆的話，可以允許 Xrdp 來自某個 IP 位置，或某個 IP 範圍的訪問 (CIDR)，這邊筆者以 <code>ufw</code> 為例</p>
<pre><code>sudo ufw allow from &lt;YOUR IP OR IP RANGE IN CIDR&gt; to any port 3389
</code></pre>
<p>:::warning
基於安全考慮，極度不建議直接用 <code>sudo ufw allow 3389</code> 來允許從所有地方訪問
:::</p>
<p>最後輸入以下指令來重新啟動 Xrdp，讓變更生效</p>
<pre><code>sudo systemctl restart xrdp
</code></pre>
<p>這樣便完成 Xrdp 的基本安裝了</p>
<p> </p>
<h1>Optional 2: 進階設定</h1>
<h2>更改默認端口號 (Port)</h2>
<p>我們先打開 <code>/etc/xrdp/xrdp.ini</code></p>
<pre><code>sudo nano /etc/xrdp/xrdp.ini
</code></pre>
<p>然後在裏面找到 <code>port=3389</code></p>
<pre><code>[Globals]
; xrdp.ini file version number
ini_version=1

; fork a new process for each incoming connection
fork=true

; ports to listen on, number alone means listen on all interfaces
; 0.0.0.0 or :: if ipv6 is configured
; space between multiple occurrences
; ALL specified interfaces must be UP when xrdp starts, otherwise xrdp will fail to start
;
; Examples:
;   port=3389
;   port=unix://./tmp/xrdp.socket
;   port=tcp://.:3389                           127.0.0.1:3389
;   port=tcp://:3389                            *:3389
;   port=tcp://&lt;any ipv4 format addr&gt;:3389      192.168.1.1:3389
;   port=tcp6://.:3389                          ::1:3389
;   port=tcp6://:3389                           *:3389
;   port=tcp6://{&lt;any ipv6 format addr&gt;}:3389   {FC00:0:0:0:0:0:0:1}:3389
;   port=vsock://&lt;cid&gt;:&lt;port&gt;
port=3389

; 'port' above should be connected to with vsock instead of tcp
; use this only with number alone in port above
; prefer use vsock://&lt;cid&gt;:&lt;port&gt; above
use_vsock=false
.
.
.
</code></pre>
<p>並把它更改為你想要且未被佔用的端口</p>
<pre><code>; Examples:
;   port=3389
;   port=unix://./tmp/xrdp.socket
;   port=tcp://.:3389                           127.0.0.1:3389
;   port=tcp://:3389                            *:3389
;   port=tcp://&lt;any ipv4 format addr&gt;:3389      192.168.1.1:3389
;   port=tcp6://.:3389                          ::1:3389
;   port=tcp6://:3389                           *:3389
;   port=tcp6://{&lt;any ipv6 format addr&gt;}:3389   {FC00:0:0:0:0:0:0:1}:3389
;   port=vsock://&lt;cid&gt;:&lt;port&gt;
port=&lt; YOUR PORT&gt;
</code></pre>
<p>按 :keyboard[Ctrl]{theme} + :keyboard[X] ，輸入 <code>y</code> / <code>yes</code> 並按 :keyboard[Enter]{theme} 儲存</p>
<p>最後輸入以下指令來重新啟動 Xrdp，讓變更生效</p>
<pre><code>sudo systemctl restart xrdp
</code></pre>
<p>:::note
如果你有啟用防火牆的話，記得更新一下允許新的端口。否則無法連線喔~
:::</p>
<p> </p>
<h1>Step 3: 連線至主機</h1>
<p>現在，我們可以先測試一下 Xrdp 是否正常運作。</p>
<ol>
<li>
<p>如連線至 Windows 般打開遠端桌面 (Remote Desktop) 並填寫主機 IP 或域名，然後按「連接」 (Connect)
<img src="./xrdp1.png" alt="Connecting to Linux Xrdp 1" /></p>
</li>
<li>
<p>假如出現憑證警告，按「是」 (Yes) 繼續
<img src="./xrdp2.png" alt="Connecting to Linux Xrdp 2" /></p>
</li>
<li>
<p>輸入目標主機的用戶名稱 (Username) 和密碼 (Password) ，完成後請按 [Login]{.label}
<img src="./xrdp3.png" alt="Connecting to Linux Xrdp 3" /></p>
</li>
<li>
<p>倘若沒問題的話，恭喜你！你已經成功安裝和設置 Xrdp 了，盡情享受吧！
<img src="./xrdp4.png" alt="Connecting to Linux Xrdp 4" /></p>
</li>
</ol>
<p>假若無法連上主機，或是連接後在某部份出現異常的話，該怎樣辦？沒關係。請繼續看下去</p>
<h1>疑難排解</h1>
<h2>無法識別主機</h2>
<p>大多數屬於網絡問題，請檢查</p>
<ul>
<li>
<p>Xrdp 是否已啟用並正常運行</p>
</li>
<li>
<p>防火牆是否已允許端口 (Port)</p>
</li>
</ul>
<h2>輸入憑據後按登入後即時閃退</h2>
<p>這是一個在任何DE也最常遇到的問題，但解決方法也很簡單</p>
<p>先在 terminal 中輸入以下指令</p>
<pre><code>sudo service xrdp stop
</code></pre>
<p>然後編輯 Xrdp 啟動腳本</p>
<pre><code>sudo nano /etc/xrdp/startwm.sh
</code></pre>
<p>把腳本內的以下幾行</p>
<pre><code>test -x /etc/X11/Xsession &amp;&amp; exec /etc/X11/Xsession
exec /bin/sh /etc/X11/Xsession
</code></pre>
<p>更改為</p>
<pre><code>#(@obsolete)startxfce4
/usr/bin/startplasma-x11
</code></pre>
<p>按 :keyboard[Ctrl] + :keyboard[X]{theme} ，輸入 <code>y</code> / <code>yes</code> 並按 :keyboard[Enter]{theme} 儲存</p>
<p>最後輸入以下指令來重新啟動 Xrdp 便可</p>
<pre><code>sudo service xrdp start
</code></pre>
<h2>在 KDE 中無法移動或調整視窗大小</h2>
<p>我們在 terminal 中輸入以下指令</p>
<pre><code>sudo service xrdp stop
</code></pre>
<p>並編輯 Xrdp 啟動腳本</p>
<pre><code>sudo nano /etc/xrdp/startwm.sh
</code></pre>
<p>在腳本底下新增以下內容</p>
<pre><code>#(@obsolete)killall kwin
killall kwin_x11
#(@obsolete)kwrapper kwin -replace &amp;
kwrapper5 kwin_x11 --replace &amp;
</code></pre>
<p>按 :keyboard[Ctrl]{theme} + :keyboard[X] ，輸入 <code>y</code> / <code>yes</code> 並按 :keyboard[Enter]{theme} 儲存</p>
<p>最後輸入以下指令來重新啟動 Xrdp 便可</p>
<pre><code>sudo service xrdp start
</code></pre>
<h2>KDE 內沒有桌面，只顯示黑畫面</h2>
<p>這問題在KDE中也算常見，其解決方法並不複雜</p>
<p>先同樣地在 terminal 中輸入以下指令</p>
<pre><code>sudo service xrdp stop
</code></pre>
<p>並編輯 Xrdp 啟動腳本</p>
<pre><code>sudo nano /etc/xrdp/startwm.sh
</code></pre>
<p>在腳本底下新增以下內容</p>
<pre><code>#(@Obsolete)killall plasma-desktop
#(@Obsolete)kstart plasma-desktop
#(@Obsolete)killall plasmashell #to stop it
#(@Obsolete)kstart plasmashell #to restart it
kquitapp5 plasmashell
kstart5 plasmashell
</code></pre>
<p>按 :keyboard[Ctrl]{theme} + :keyboard[X] ，輸入 <code>y</code> / <code>yes</code> 並按 :keyboard[Enter]{theme} 儲存</p>
<p>最後輸入以下指令來重新啟動 Xrdp 便可</p>
<pre><code>sudo service xrdp start
</code></pre>
<h1>結語</h1>
<p>想不到 Linux 還有這樣的連接方式啊，筆者也是最近才知道.......</p>
<p>看到這裏，想必各位應該 GET 到一些新技能了吧</p>
<p>不過在這裏提醒大家，在享受 Xrdp 帶來的便利時，也請謹記做好網絡保安。</p>
<p>現時<a href="https://www.google.com/search?q=%E9%81%A0%E7%AB%AF%E9%80%A3%E6%8E%A5%E9%A8%99%E5%B1%80">遠端連接騙局</a>極為普遍，切忌讓其他人士肆意存取你的主機。相信沒人願意成為下一位被騙的受害者吧 XD</p>
<p>希望大家設置成功，為你的 Linux 主機打開新的大門！</p>
]]></content>
    <author><name>ゆき</name></author>
    <category term="技術文"/>
  </entry>
  <entry>
    <title>還在路由器弄VPN？ 15分鐘帶你搞定Tailscale！</title>
    <link href="https://moe.lolicon.io/posts/tech-posts/tailscale-vpn/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/tech-posts/tailscale-vpn/</id>
    <published>2025-02-22T00:00:00.000Z</published>
    <updated>2026-05-03T00:00:00.000Z</updated>
    <summary>不論身處何地，你的裝置都可以透過安全的私人網路互相連接。</summary>
    <content type="html"><![CDATA[<p>在 &lt;a href="javascript:void(0)" onclick="window.location.href=window.location.origin + '/posts/tech-posts/wake-on-lan';"&gt;上一篇文章&lt;/a&gt;，筆者提到如何在家中透過 Wake On LAN 來喚醒電腦。然而很多人可能會問，如果人在外面又該怎麼辦？</p>
<p>辨法倒是有的，例如 DDNS，或是固定IP等等 (下幾期會提到)。不過，這些方式雖說能夠解一時燃眉之急，卻無法保護您所傳輸的數據......</p>
<p>VPN 正是為此而誕生，但是，相信弄過 VPN 的都會明白，傳統的 OpenVPN 要在路由器上配置很多東西 (新款 TP-Link 路由器倒是把程序簡化了不少)......</p>
<p>倒底有沒有辦法能輕鬆建立自己的私人 VPN，而不需要煩人的網路設定？這時候，有請 Tailscale 出場！</p>
<p> </p>
<h2>為什麼選擇 Tailscale？</h2>
<p>如果你曾經嘗試過設定傳統 VPN（如 OpenVPN 或 WireGuard），你可能會發現它們通常需要：</p>
<ul>
<li>
<p>手動配置防火牆與 NAT</p>
</li>
<li>
<p>設定伺服器、管理憑證</p>
</li>
<li>
<p>處理繁瑣的 IP 配置與存取權限</p>
</li>
</ul>
<p>而 Tailscale 透過 WireGuard 提供 <strong>零配置（zero-config）VPN</strong>，並且擁有以下優點：</p>
<ul>
<li>
<p>免開放端口，不需調整路由器</p>
</li>
<li>
<p>自動穿透防火牆與 NAT（適用於遠端存取）</p>
</li>
<li>
<p>支援多種設備（Windows、macOS、Linux、iOS、Android）</p>
</li>
<li>
<p>內建 <code>MagicDNS</code>，讓設備之間可以用名稱直接連線</p>
</li>
<li>
<p>免費個人計畫，適合個人與小型團隊</p>
</li>
</ul>
<p>準備好了嗎？ 讓我們開始吧！</p>
<p> </p>
<p>:::note
Tailscale 支援 Windows、macOS、Linux、iOS 和 Android，請選擇適合你的設備來安裝。
:::</p>
<h2>Step 1: 註冊 Tailscale</h2>
<p>首先，我們需要一個 Tailscale 帳號：</p>
<ol>
<li>
<p>打開 <a href="https://tailscale.com/">Tailscale 官方網站</a></p>
</li>
<li>
<p>點擊 「Sign up」（註冊）</p>
</li>
<li>
<p>使用 Google、Microsoft 或 GitHub 登入</p>
</li>
<li>
<p>完成註冊後，你將會自動進入 Tailscale 控制台</p>
</li>
</ol>
<h2>Step 2: 安裝 Tailscale</h2>
<h3>Linux</h3>
<h4>主流 distro</h4>
<p>如果你是懶人，且所使用的 distro 具有 <code>apt</code>, <code>yum</code>,<code>zipper </code> 這類 package manager 的話，例如</p>
<ul>
<li>
<p>Ubuntu</p>
</li>
<li>
<p>Debian</p>
</li>
<li>
<p>Red Hat® Enterprise Linux (RHEL), CentOS, Fedora 以及其引申的 distro</p>
</li>
<li>
<p>Raspberry Pi OS</p>
</li>
<li>
<p>Amazon Linux</p>
</li>
<li>
<p>openSUSE 和 SUSE Linux Enterprise</p>
</li>
<li>
<p>Oracle Linux</p>
</li>
<li>
<p>VMware Photon OS</p>
</li>
</ul>
<p>那麼只要把下面的通用命令 copy and paste 便成事了</p>
<pre><code>curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
</code></pre>
<h4>Arch Linux</h4>
<p>如果你和筆者一樣，都是在用 Arch Linux 的話</p>
<pre><code>sudo pacman -S tailscale
</code></pre>
<p> </p>
<pre><code>sudo systemctl enable --now tailscaled
sudo tailscale up
</code></pre>
<h4>NixOS</h4>
<p>打開 <code>/etc/nixos/configuration.nix</code>，新增下列內容</p>
<pre><code>{
  services.tailscale = {
    enable = true;
    # Enable tailscale at startup

    # If you would like to use a preauthorized key
    #authKeyFile = "/run/secrets/tailscale_key";

  };
}
</code></pre>
<h3>Windows</h3>
<ol>
<li>
<p>前往 <a href="https://tailscale.com/download">Tailscale 下載頁面</a></p>
</li>
<li>
<p>下載 <strong>Windows 安裝程式</strong></p>
</li>
<li>
<p>執行安裝程式，完成後開啟 <strong>Tailscale</strong></p>
</li>
</ol>
<h3>macOS</h3>
<ol>
<li>
<p>下載 <strong>Tailscale for macOS</strong>：<a href="https://tailscale.com/download">點此下載</a></p>
</li>
<li>
<p>安裝並開啟 <strong>Tailscale</strong></p>
</li>
</ol>
<h3>Android</h3>
<ol>
<li>前往 Google Play Store 搜尋 <strong>Tailscale</strong></li>
</ol>
<p><img src="./android1.jpg" alt="" /></p>
<ol>
<li>安裝</li>
</ol>
<p><img src="./android2.jpg" alt="" /></p>
<p>並開啟 App</p>
<p><img src="./android3.jpg" alt="" /></p>
<h3>iOS</h3>
<ol>
<li>
<p><strong>iOS/iPadOS</strong>：前往 <strong>App Store</strong> 搜尋 <strong>Tailscale</strong></p>
</li>
<li>
<p>安裝</p>
</li>
</ol>
<p><img src="./iOS_setup1.PNG" alt="" /></p>
<p>並開啟 App</p>
<p><img src="./iOS_setup2.PNG" alt="" /></p>
<h2>Step 3: 連接至VPN</h2>
<h3>Linux</h3>
<p>安裝完成後，啟動並登入：</p>
<pre><code>sudo tailscale up
</code></pre>
<p>這時候應該會有一條連結顯示在terminal上，像醬</p>
<pre><code>To authenticate, visit:

        https://login.tailscale.com/a/&lt;SOMETHING&gt;
</code></pre>
<p>在瀏覽器中輸入該連結並完成登入流程，然後點擊「Connect」</p>
<p>一切順利的話，terminal會如下圖般顯示"Success."</p>
<pre><code>To authenticate, visit:

        https://login.tailscale.com/a/&lt;SOMETHING&gt;

Success.
</code></pre>
<p>這樣便代表您的Linux設備已經成功連上VPN了。</p>
<h3>Windows</h3>
<ol>
<li>
<p>點擊 <strong>「Log in」</strong>（登入），使用剛剛註冊的帳號登入</p>
</li>
<li>
<p>成功登入後，Tailscale 會自動連接到 VPN</p>
</li>
</ol>
<h3>macOS</h3>
<ol>
<li>
<p>點擊 <strong>「Log in」</strong>，使用你的帳號登入</p>
</li>
<li>
<p>成功登入後，你的 Mac 也加入 VPN 了</p>
</li>
</ol>
<h3>Android</h3>
<ol>
<li>
<p>開啟 Tailscale</p>
</li>
<li>
<p>完成初始化</p>
</li>
</ol>
<p><img src="./android4.jpg" alt="" /></p>
<ol>
<li>點擊 「Connect」</li>
</ol>
<p><img src="./android5.jpg" alt="" /></p>
<ol>
<li>點擊 <strong>「Log in」</strong>，並使用你的帳號登入。筆者這邊使用 GitHub 登入</li>
</ol>
<p><img src="./android6.jpg" alt="" /></p>
<p><img src="./android7.jpg" alt="" /></p>
<p><img src="./android8.jpg" alt="" /></p>
<ol>
<li>在瀏覽器點擊 「Connect」</li>
</ol>
<p><img src="./android9.jpg" alt="" /></p>
<ol>
<li>看到以下畫面，即代表登入成功。請返回 Tailscale 應用程式繼續</li>
</ol>
<p><img src="./android10.jpg" alt="" /></p>
<ol>
<li>假若跳出 Notifications 介面，請按照指示允許通知</li>
</ol>
<p><img src="./android11.jpg" alt="" /></p>
<p><img src="./android12.jpg" alt="" /></p>
<ol>
<li>點擊畫面頂部開關，成功後設備將加入 VPN</li>
</ol>
<p><img src="./android13.jpg" alt="" /></p>
<h3>iOS</h3>
<ol>
<li>
<p>開啟 Tailscale</p>
</li>
<li>
<p>按頁面指示完成初始化</p>
</li>
</ol>
<p><img src="./iOS_setup3.PNG" alt="" /></p>
<p><img src="./iOS_setup4.PNG" alt="" /></p>
<p><img src="./iOS_setup5.PNG" alt="" /></p>
<p><img src="./iOS_setup6.PNG" alt="" /></p>
<p><img src="./iOS_setup7.PNG" alt="" /></p>
<p><img src="./iOS_setup8.PNG" alt="" /></p>
<ol>
<li>點擊 <strong>「Log in」</strong>，並使用你的帳號登入</li>
</ol>
<p><img src="./iOS_setup9.PNG" alt="" /></p>
<p><img src="./iOS_setup10.PNG" alt="" /></p>
<p><img src="./iOS_setup11.PNG" alt="" /></p>
<ol>
<li>
<p>授權 VPN 連線</p>
</li>
<li>
<p>點擊畫面頂部開關，成功後設備將加入 VPN</p>
</li>
</ol>
<p><img src="./iOS_setup12.PNG" alt="" /></p>
<p> </p>
<h2>Step 4: 使用 Tailsacale IP 連接你的設備</h2>
<p>現在，我們的所有裝置均已加入 <strong>Tailscale 網路</strong>，只要透過 <strong>Tailscale IP</strong> 便能把它們連接起來了！</p>
<h3>如何查詢 Tailscale IP</h3>
<p>在終端機輸入：</p>
<pre><code>tailscale ip -4
</code></pre>
<p>我們會看到一個類似 <code>100.101.102.103</code> 的 IP，這就是你裝置上的 <strong>Tailscale VPN IP</strong>。</p>
<h3>透過 SSH 連線</h3>
<p>假若你有兩台設備都安裝了 Tailscale，例如：</p>
<ul>
<li>
<p><strong>Ubuntu 伺服器</strong>（IP：100.101.102.103）</p>
</li>
<li>
<p><strong>Windows 電腦</strong>（IP：100.102.103.104）</p>
</li>
</ul>
<p>你可以像筆者一樣，從 Windows 使用 <strong>SSH 連線到 Ubuntu</strong>：</p>
<pre><code>ssh user@100.101.102.103
</code></pre>
<h3>遠端桌面（Windows RDP）</h3>
<p>如果你想從 筆電<strong>遠端連接</strong>家裡的 Windows 電腦：</p>
<pre><code>mstsc /v:100.101.102.103
</code></pre>
<p>這樣就能輕鬆連線啦！</p>
<p>恭喜你！你已經完成所有 Tailscale 的基本設定了。如果你沒有必要用其他進階功能，停在這裏便行了</p>
<h2>Step 5: 進階設置</h2>
<h3>MagicDNS：使用設備名稱連線</h3>
<p>每台 Tailscale 設備都會有一個 <strong>Tailscale 名稱</strong>，例如：</p>
<pre><code>my-laptop
home-server
</code></pre>
<p>如上所述，我們得知可以透過 Tailscale IP 互連設備 :spoiler[(但是筆者並不喜歡長期牢記這一串IP啊，怎樣辦)]</p>
<p>如果你像筆者一樣，不想記住 <strong>IP 地址</strong>， <strong>MagicDNS</strong>定能幫你一個大忙</p>
<ol>
<li>
<p>進入 Tailscale 控制台</p>
</li>
<li>
<p>點擊 <code>DNS 設定</code></p>
</li>
<li>
<p>啟用 <code>MagicDNS</code></p>
</li>
</ol>
<p>搞定之後，你就可以直接輸入設備名稱來連線，像醬</p>
<pre><code>ssh user@home-server
</code></pre>
<p>是不是更方便了？</p>
<h3>子網路路由（Subnet Routing）</h3>
<p>我們也知道並不是所有設備皆能支援 Tailscale，難道說你能把 Tailscale 裝到 <a href="https://www.mi.com/hk/product/xiaomi-smart-multifunctional-rice-cooker/">Wi-Fi 智能電飯鍋</a> 上嗎 XD？</p>
<p>這個時候，我們可以透過一台支援 Tailscale 的 Linux 裝置設定 <strong>子網路路由</strong>，來幫助其他設備連接到您的 Tailnet。筆者的做法是在 PVE 中長開一個 lxc container 並做好相應配置，然後在 terminal 輸入這一串命令</p>
<pre><code>sudo tailscale up --advertise-routes=192.168.1.0/24
</code></pre>
<p>其中 <code>192.168.1.0/24</code> 更改為路由器的子網 (Subnet)</p>
<p>接著到 <strong>Tailscale 控制台</strong> 中啟用這個路由。</p>
<p>這樣，VPN 內的設備就可以存取家裡的 <strong>NAS、印表機或其他 IoT 設備</strong>！</p>
<h3>ACL（存取控制）</h3>
<p>如果你想限制哪些設備可以互相連接，可以編輯 ACL（存取控制清單）：</p>
<ol>
<li>
<p>進入 Tailscale 控制台</p>
</li>
<li>
<p>點擊 <code>Access Control</code></p>
</li>
<li>
<p>編輯 <code>tailnet</code> 設定，例如：</p>
<pre><code>{
  "ACLs": [
    {
      "Action": "accept",
      "Sources": ["100.101.102.103"],
      "Destinations": ["100.102.103.104"]
    }
  ]
}
</code></pre>
</li>
<li>
<p>儲存設定，Tailscale 會自動應用變更</p>
</li>
</ol>
<h2>結語</h2>
<p>Tailscale 讓 VPN 設置變得超級簡單。看到這裏，你現在應該已經：</p>
<ul>
<li>
<p>設置並登入了 Tailscale</p>
</li>
<li>
<p>讓你的設備透過 Tailscale 互相連接</p>
</li>
<li>
<p>了解了進階功能，如 <strong>MagicDNS、子網路路由、ACL</strong></p>
</li>
</ul>
<p>現在，你可以透過 Tailscale 安全地存取你的伺服器、遠端桌面，以及整個家庭網路了！</p>
<p>如果你有任何問題，可以查看 <a href="https://tailscale.com/kb/">Tailscale 官方文件</a> 或加入 <a href="https://forum.tailscale.com/">Tailscale 社群</a>。</p>
<p>試試看吧，展開你的 Tailscale 之旅!</p>
]]></content>
    <author><name>ゆき</name></author>
    <category term="技術文"/>
  </entry>
  <entry>
    <title>Markdown Tutorial</title>
    <link href="https://moe.lolicon.io/posts/markdown-tutorial/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/markdown-tutorial/</id>
    <published>2025-01-20T00:00:00.000Z</published>
    <updated>2025-01-20T00:00:00.000Z</updated>
    <summary>A simple example of a Markdown blog post.</summary>
    <content type="html"><![CDATA[<h1>Markdown Tutorial</h1>
<p>A markdown example shows how to write a markdown file. This document integrates core syntax and extensions (GMF).</p>
<ul>
<li><a href="#block-elements">Block Elements</a>
<ul>
<li><a href="#paragraphs-and-line-breaks">Paragraphs and Line Breaks</a></li>
<li><a href="#headers">Headers</a></li>
<li><a href="#blockquotes">Blockquotes</a></li>
<li><a href="#lists">Lists</a></li>
<li><a href="#code-blocks">Code Blocks</a></li>
<li><a href="#horizontal-rules">Horizontal Rules</a></li>
<li><a href="#table">Table</a></li>
</ul>
</li>
<li><a href="#span-elements">Span Elements</a>
<ul>
<li><a href="#links">Links</a></li>
<li><a href="#emphasis">Emphasis</a></li>
<li><a href="#code">Code</a></li>
<li><a href="#images">Images</a></li>
<li><a href="#strikethrough">Strikethrough</a></li>
</ul>
</li>
<li><a href="#miscellaneous">Miscellaneous</a>
<ul>
<li><a href="#automatic-links">Automatic Links</a></li>
<li><a href="#backslash-escapes">Backslash Escapes</a></li>
</ul>
</li>
<li><a href="#inline-html">Inline HTML</a></li>
</ul>
<h2>Block Elements</h2>
<h3>Paragraphs and Line Breaks</h3>
<h4>Paragraphs</h4>
<p>HTML Tag: <code>&lt;p&gt;</code></p>
<p>One or more blank lines. (A blank line is a line containing nothing but <strong>spaces</strong> or <strong>tabs</strong> is considered blank.)</p>
<p>Code:</p>
<pre><code>This will be
inline.

This is second paragraph.
</code></pre>
<p>Preview:</p>
<hr />
<p>This will be
inline.</p>
<p>This is second paragraph.</p>
<hr />
<h4>Line Breaks</h4>
<p>HTML Tag: <code>&lt;br /&gt;</code></p>
<p>End a line with <strong>two or more spaces</strong>.</p>
<p>Code:</p>
<pre><code>This will be not
inline.
</code></pre>
<p>Preview:</p>
<hr />
<p>This will be not<br />
inline.</p>
<hr />
<h3>Headers</h3>
<p>Markdown supports two styles of headers, Setext and atx.</p>
<h4>Setext</h4>
<p>HTML Tags: <code>&lt;h1&gt;</code>, <code>&lt;h2&gt;</code></p>
<p>"Underlined" using <strong>equal signs (=)</strong> as <code>&lt;h1&gt;</code> and <strong>dashes (-)</strong> as <code>&lt;h2&gt;</code> in any number.</p>
<p>Code:</p>
<pre><code>This is an H1
=============
This is an H2
-------------
</code></pre>
<p>Preview:</p>
<hr />
<h1>This is an H1</h1>
<h2>This is an H2</h2>
<hr />
<h4>atx</h4>
<p>HTML Tags: <code>&lt;h1&gt;</code>, <code>&lt;h2&gt;</code>, <code>&lt;h3&gt;</code>, <code>&lt;h4&gt;</code>, <code>&lt;h5&gt;</code>, <code>&lt;h6&gt;</code></p>
<p>Uses 1-6 <strong>hash characters (#)</strong> at the start of the line, corresponding to <code>&lt;h1&gt;</code> - <code>&lt;h6&gt;</code>.</p>
<p>Code:</p>
<pre><code># This is an H1
## This is an H2
###### This is an H6
</code></pre>
<p>Preview:</p>
<hr />
<h1>This is an H1</h1>
<h2>This is an H2</h2>
<h6>This is an H6</h6>
<hr />
<p>Optionally, you may "close" atx-style headers. The closing hashes <strong>don't need to match</strong> the number of hashes used to open the header.</p>
<p>Code:</p>
<pre><code># This is an H1 #
## This is an H2 ##
### This is an H3 ######
</code></pre>
<p>Preview:</p>
<hr />
<h1>This is an H1</h1>
<h2>This is an H2</h2>
<h3>This is an H3</h3>
<hr />
<h3>Blockquotes</h3>
<p>HTML Tag: <code>&lt;blockquote&gt;</code></p>
<p>Markdown uses email-style <strong>&gt;</strong> characters for blockquoting. It looks best if you hard wrap the text and put a &gt; before every line.</p>
<p>Code:</p>
<pre><code>&gt; This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
&gt; consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
&gt; Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
&gt;
&gt; Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
&gt; id sem consectetuer libero luctus adipiscing.
</code></pre>
<p>Preview:</p>
<hr />
<blockquote>
<p>This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.</p>
<p>Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
id sem consectetuer libero luctus adipiscing.</p>
</blockquote>
<hr />
<p>Markdown allows you to be lazy and only put the &gt; before the first line of a hard-wrapped paragraph.</p>
<p>Code:</p>
<pre><code>&gt; This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.

&gt; Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
id sem consectetuer libero luctus adipiscing.
</code></pre>
<p>Preview:</p>
<hr />
<blockquote>
<p>This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.</p>
</blockquote>
<blockquote>
<p>Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
id sem consectetuer libero luctus adipiscing.</p>
</blockquote>
<hr />
<p>Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by adding additional levels of &gt;.</p>
<p>Code:</p>
<pre><code>&gt; This is the first level of quoting.
&gt;
&gt; &gt; This is nested blockquote.
&gt;
&gt; Back to the first level.
</code></pre>
<p>Preview:</p>
<hr />
<blockquote>
<p>This is the first level of quoting.</p>
<blockquote>
<p>This is nested blockquote.</p>
</blockquote>
<p>Back to the first level.</p>
</blockquote>
<hr />
<p>Blockquotes can contain other Markdown elements, including headers, lists, and code blocks.</p>
<p>Code:</p>
<pre><code>&gt; ## This is a header.
&gt;
&gt; 1.   This is the first list item.
&gt; 2.   This is the second list item.
&gt;
&gt; Here's some example code:
&gt;
&gt;     return shell_exec("echo $input | $markdown_script");
</code></pre>
<p>Preview:</p>
<hr />
<blockquote>
<h2>This is a header.</h2>
<ol>
<li>This is the first list item.</li>
<li>This is the second list item.</li>
</ol>
<p>Here's some example code:</p>
<pre><code>return shell_exec("echo $input | $markdown_script");
</code></pre>
</blockquote>
<hr />
<h3>Lists</h3>
<p>Markdown supports ordered (numbered) and unordered (bulleted) lists.</p>
<h4>Unordered</h4>
<p>HTML Tag: <code>&lt;ul&gt;</code></p>
<p>Unordered lists use <strong>asterisks (*)</strong>, <strong>pluses (+)</strong>, and <strong>hyphens (-)</strong>.</p>
<p>Code:</p>
<pre><code>*   Red
*   Green
*   Blue
</code></pre>
<p>Preview:</p>
<hr />
<ul>
<li>Red</li>
<li>Green</li>
<li>Blue</li>
</ul>
<hr />
<p>is equivalent to:</p>
<p>Code:</p>
<pre><code>+   Red
+   Green
+   Blue
</code></pre>
<p>and:</p>
<p>Code:</p>
<pre><code>-   Red
-   Green
-   Blue
</code></pre>
<h4>Ordered</h4>
<p>HTML Tag: <code>&lt;ol&gt;</code></p>
<p>Ordered lists use numbers followed by periods:</p>
<p>Code:</p>
<pre><code>1.  Bird
2.  McHale
3.  Parish
</code></pre>
<p>Preview:</p>
<hr />
<ol>
<li>Bird</li>
<li>McHale</li>
<li>Parish</li>
</ol>
<hr />
<p>It's possible to trigger an ordered list by accident, by writing something like this:</p>
<p>Code:</p>
<pre><code>1986. What a great season.
</code></pre>
<p>Preview:</p>
<hr />
<ol>
<li>What a great season.</li>
</ol>
<hr />
<p>You can <strong>backslash-escape (\)</strong> the period:</p>
<p>Code:</p>
<pre><code>1986\. What a great season.
</code></pre>
<p>Preview:</p>
<hr />
<p>1986. What a great season.</p>
<hr />
<h4>Indented</h4>
<h5>Blockquote</h5>
<p>To put a blockquote within a list item, the blockquote's &gt; delimiters need to be indented:</p>
<p>Code:</p>
<pre><code>*   A list item with a blockquote:

    &gt; This is a blockquote
    &gt; inside a list item.
</code></pre>
<p>Preview:</p>
<hr />
<ul>
<li>
<p>A list item with a blockquote:</p>
<blockquote>
<p>This is a blockquote
inside a list item.</p>
</blockquote>
</li>
</ul>
<hr />
<h5>Code Block</h5>
<p>To put a code block within a list item, the code block needs to be indented twice — <strong>8 spaces</strong> or <strong>two tabs</strong>:</p>
<p>Code:</p>
<pre><code>*   A list item with a code block:

        &lt;code goes here&gt;
</code></pre>
<p>Preview:</p>
<hr />
<ul>
<li>
<p>A list item with a code block:</p>
<pre><code>&lt;code goes here&gt;
</code></pre>
</li>
</ul>
<hr />
<h5>Nested List</h5>
<p>Code:</p>
<pre><code>* A
  * A1
  * A2
* B
* C
</code></pre>
<p>Preview:</p>
<hr />
<ul>
<li>A
<ul>
<li>A1</li>
<li>A2</li>
</ul>
</li>
<li>B</li>
<li>C</li>
</ul>
<hr />
<h3>Code Blocks</h3>
<p>HTML Tag: <code>&lt;pre&gt;</code></p>
<p>Indent every line of the block by at least <strong>4 spaces</strong> or <strong>1 tab</strong>.</p>
<p>Code:</p>
<pre><code>This is a normal paragraph:

    This is a code block.
</code></pre>
<p>Preview:</p>
<hr />
<p>This is a normal paragraph:</p>
<pre><code>This is a code block.
</code></pre>
<hr />
<p>A code block continues until it reaches a line that is not indented (or the end of the article).</p>
<p>Within a code block, <strong><em>ampersands (&amp;)</em></strong> and angle <strong>brackets (&lt; and &gt;)</strong> are automatically converted into HTML entities.</p>
<p>Code:</p>
<pre><code>    &lt;div class="footer"&gt;
        &amp;copy; 2004 Foo Corporation
    &lt;/div&gt;
</code></pre>
<p>Preview:</p>
<hr />
<pre><code>&lt;div class="footer"&gt;
    &amp;copy; 2004 Foo Corporation
&lt;/div&gt;
</code></pre>
<hr />
<p>Following sections Fenced Code Blocks and Syntax Highlighting are extensions, you can use the other way to write the code block.</p>
<h4>Fenced Code Blocks</h4>
<p>Just wrap your code in <code>```</code> (as shown below) and you won't need to indent it by four spaces.</p>
<p>Code:</p>
<pre><code>Here's an example:

```
function test() {
  console.log("notice the blank line before this function?");
}
```
</code></pre>
<p>Preview:</p>
<hr />
<p>Here's an example:</p>
<pre><code>function test() {
  console.log("notice the blank line before this function?");
}
</code></pre>
<hr />
<h4>Syntax Highlighting</h4>
<p>In your fenced block, add an optional language identifier and we'll run it through syntax highlighting (<a href="https://github.com/github/linguist/blob/master/lib/linguist/languages.yml">Support Languages</a>).</p>
<p>Code:</p>
<pre><code>```ruby
require 'redcarpet'
markdown = Redcarpet.new("Hello World!")
puts markdown.to_html
```
</code></pre>
<p>Preview:</p>
<hr />
<pre><code>require 'redcarpet'
markdown = Redcarpet.new("Hello World!")
puts markdown.to_html
</code></pre>
<hr />
<h3>Horizontal Rules</h3>
<p>HTML Tag: <code>&lt;hr /&gt;</code>
Places <strong>three or more hyphens (-), asterisks (*), or underscores (_)</strong> on a line by themselves. You may use spaces between the hyphens or asterisks.</p>
<p>Code:</p>
<pre><code>* * *
***
*****
- - -
---------------------------------------
___
</code></pre>
<p>Preview:</p>
<hr />
<hr />
<hr />
<hr />
<hr />
<hr />
<hr />
<hr />
<h3>Table</h3>
<p>HTML Tag: <code>&lt;table&gt;</code></p>
<p>It's an extension.</p>
<p>Separates column by <strong>pipe (|)</strong> and header by <strong>dashes (-)</strong>, and uses <strong>colon (:)</strong> for alignment.</p>
<p>The outer <strong>pipes (|)</strong> and alignment are optional. There are <strong>3 delimiters</strong> each cell at least for separating header.</p>
<p>Code:</p>
<pre><code>| Left | Center | Right |
|:-----|:------:|------:|
|aaa   |bbb     |ccc    |
|ddd   |eee     |fff    |

 A | B
---|---
123|456


A |B
--|--
12|45
</code></pre>
<p>Preview:</p>
<hr />
<table>
<thead>
<tr>
<th>Left</th>
<th>Center</th>
<th>Right</th>
</tr>
</thead>
<tbody>
<tr>
<td>aaa</td>
<td>bbb</td>
<td>ccc</td>
</tr>
<tr>
<td>ddd</td>
<td>eee</td>
<td>fff</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>A</th>
<th>B</th>
</tr>
</thead>
<tbody>
<tr>
<td>123</td>
<td>456</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>A</th>
<th>B</th>
</tr>
</thead>
<tbody>
<tr>
<td>12</td>
<td>45</td>
</tr>
</tbody>
</table>
<hr />
<h2>Span Elements</h2>
<h3>Links</h3>
<p>HTML Tag: <code>&lt;a&gt;</code></p>
<p>Markdown supports two style of links: inline and reference.</p>
<h4>Inline</h4>
<p>Inline link format like this: <code>[Link Text](URL "Title")</code></p>
<p>Title is optional.</p>
<p>Code:</p>
<pre><code>This is [an example](http://example.com/ "Title") inline link.

[This link](http://example.net/) has no title attribute.
</code></pre>
<p>Preview:</p>
<hr />
<p>This is <a href="http://example.com/">an example</a> inline link.</p>
<p><a href="http://example.net/">This link</a> has no title attribute.</p>
<hr />
<p>If you're referring to a local resource on the same server, you can use relative paths:</p>
<p>Code:</p>
<pre><code>See my [About](/about/) page for details.
</code></pre>
<p>Preview:</p>
<hr />
<p>See my <a href="/about/">About</a> page for details.</p>
<hr />
<h4>Reference</h4>
<p>You could predefine link references. Format like this: <code>[id]: URL "Title"</code></p>
<p>Title is also optional. And the you refer the link, format like this: <code>[Link Text][id]</code></p>
<p>Code:</p>
<pre><code>[id]: http://example.com/  "Optional Title Here"
This is [an example][id] reference-style link.
</code></pre>
<p>Preview:</p>
<hr />
<p>This is <a href="http://example.com/">an example</a> reference-style link.</p>
<hr />
<p>That is:</p>
<ul>
<li>Square brackets containing the link identifier (<strong>not case sensitive</strong>, optionally indented from the left margin using up to three spaces);</li>
<li>followed by a colon;</li>
<li>followed by one or more spaces (or tabs);</li>
<li>followed by the URL for the link;</li>
<li>The link URL may, optionally, be surrounded by angle brackets.</li>
<li>optionally followed by a title attribute for the link, enclosed in double or single quotes, or enclosed in parentheses.</li>
</ul>
<p>The following three link definitions are equivalent:</p>
<p>Code:</p>
<pre><code>[foo]: http://example.com/  "Optional Title Here"
[foo]: http://example.com/  'Optional Title Here'
[foo]: http://example.com/  (Optional Title Here)
[foo]: &lt;http://example.com/&gt;  "Optional Title Here"
</code></pre>
<p>Uses an empty set of square brackets, the link text itself is used as the name.</p>
<p>Code:</p>
<pre><code>[Google]: http://google.com/
[Google][]
</code></pre>
<p>Preview:</p>
<hr />
<p><a href="http://google.com/">Google</a></p>
<hr />
<h3>Emphasis</h3>
<p>HTML Tags: <code>&lt;em&gt;</code>, <code>&lt;strong&gt;</code></p>
<p>Markdown treats <strong>asterisks (*)</strong> and <strong>underscores (_)</strong> as indicators of emphasis. <strong>One delimiter</strong> will be <code>&lt;em&gt;</code>; *<em>double delimiters</em> will be <code>&lt;strong&gt;</code>.</p>
<p>Code:</p>
<pre><code>*single asterisks*

_single underscores_

**double asterisks**

__double underscores__
</code></pre>
<p>Preview:</p>
<hr />
<p><em>single asterisks</em></p>
<p><em>single underscores</em></p>
<p><strong>double asterisks</strong></p>
<p><strong>double underscores</strong></p>
<hr />
<p>But if you surround an * or _ with spaces, it'll be treated as a literal asterisk or underscore.</p>
<p>You can backslash escape it:</p>
<p>Code:</p>
<pre><code>\*this text is surrounded by literal asterisks\*
</code></pre>
<p>Preview:</p>
<hr />
<p>*this text is surrounded by literal asterisks*</p>
<hr />
<h3>Code</h3>
<p>HTML Tag: <code>&lt;code&gt;</code></p>
<p>Wraps it with <strong>backtick quotes (`)</strong>.</p>
<p>Code:</p>
<pre><code>Use the `printf()` function.
</code></pre>
<p>Preview:</p>
<hr />
<p>Use the <code>printf()</code> function.</p>
<hr />
<p>To include a literal backtick character within a code span, you can use <strong>multiple backticks</strong> as the opening and closing delimiters:</p>
<p>Code:</p>
<pre><code>``There is a literal backtick (`) here.``
</code></pre>
<p>Preview:</p>
<hr />
<p><code>There is a literal backtick (`) here.</code></p>
<hr />
<p>The backtick delimiters surrounding a code span may include spaces — one after the opening, one before the closing. This allows you to place literal backtick characters at the beginning or end of a code span:</p>
<p>Code:</p>
<pre><code>A single backtick in a code span: `` ` ``

A backtick-delimited string in a code span: `` `foo` ``
</code></pre>
<p>Preview:</p>
<hr />
<p>A single backtick in a code span: <code>`</code></p>
<p>A backtick-delimited string in a code span: <code>`foo`</code></p>
<hr />
<h3>Images</h3>
<p>HTML Tag: <code>&lt;img /&gt;</code></p>
<p>Markdown uses an image syntax that is intended to resemble the syntax for links, allowing for two styles: inline and reference.</p>
<h4>Inline</h4>
<p>Inline image syntax looks like this: <code>![Alt text](URL "Title")</code></p>
<p>Title is optional.</p>
<p>Code:</p>
<pre><code>![Alt text](/path/to/img.jpg)

![Alt text](/path/to/img.jpg "Optional title")
</code></pre>
<p>Preview:</p>
<hr />
<p><img src="https://s2.loli.net/2024/08/20/5fszgXeOxmL3Wdv.webp" alt="Alt text" /></p>
<p><img src="https://s2.loli.net/2024/08/20/5fszgXeOxmL3Wdv.webp" alt="Alt text" title="Optional title" /></p>
<hr />
<p>That is:</p>
<ul>
<li>An exclamation mark: !;</li>
<li>followed by a set of square brackets, containing the alt attribute text for the image;</li>
<li>followed by a set of parentheses, containing the URL or path to the image, and an optional title attribute enclosed in double or single quotes.</li>
</ul>
<h4>Reference</h4>
<p>Reference-style image syntax looks like this: <code>![Alt text][id]</code></p>
<p>Code:</p>
<pre><code>[img id]: https://s2.loli.net/2024/08/20/5fszgXeOxmL3Wdv.webp  "Optional title attribute"
![Alt text][img id]
</code></pre>
<p>Preview:</p>
<hr />
<p><img src="https://s2.loli.net/2024/08/20/5fszgXeOxmL3Wdv.webp" alt="Alt text" title="Optional title attribute" /></p>
<hr />
<h3>Strikethrough</h3>
<p>HTML Tag: <code>&lt;del&gt;</code></p>
<p>It's an extension.</p>
<p>GFM adds syntax to strikethrough text.</p>
<p>Code:</p>
<pre><code>~~Mistaken text.~~
</code></pre>
<p>Preview:</p>
<hr />
<p><s>Mistaken text.</s></p>
<hr />
<h2>Miscellaneous</h2>
<h3>Automatic Links</h3>
<p>Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets.</p>
<p>Code:</p>
<pre><code>&lt;http://example.com/&gt;

&lt;address@example.com&gt;
</code></pre>
<p>Preview:</p>
<hr />
<p><a href="http://example.com/">http://example.com/</a></p>
<p><a href="mailto:address@example.com">address@example.com</a></p>
<hr />
<p>GFM will autolink standard URLs.</p>
<p>Code:</p>
<pre><code>https://github.com/emn178/markdown
</code></pre>
<p>Preview:</p>
<hr />
<p>https://github.com/emn178/markdown</p>
<hr />
<h3>Backslash Escapes</h3>
<p>Markdown allows you to use backslash escapes to generate literal characters which would otherwise have special meaning in Markdown's formatting syntax.</p>
<p>Code:</p>
<pre><code>\*literal asterisks\*
</code></pre>
<p>Preview:</p>
<hr />
<p>*literal asterisks*</p>
<hr />
<p>Markdown provides backslash escapes for the following characters:</p>
<p>Code:</p>
<pre><code>\   backslash
`   backtick
*   asterisk
_   underscore
{}  curly braces
[]  square brackets
()  parentheses
#   hash mark
+   plus sign
-   minus sign (hyphen)
.   dot
!   exclamation mark
</code></pre>
<h2>Inline HTML</h2>
<p>For any markup that is not covered by Markdown's syntax, you simply use HTML itself. There's no need to preface it or delimit it to indicate that you're switching from Markdown to HTML; you just use the tags.</p>
<p>Code:</p>
<pre><code>This is a regular paragraph.

&lt;table&gt;
    &lt;tr&gt;
        &lt;td&gt;Foo&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;

This is another regular paragraph.
</code></pre>
<p>Preview:</p>
<hr />
<p>This is a regular paragraph.</p>
<p>&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;Foo&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;</p>
<p>This is another regular paragraph.</p>
<hr />
<p>Note that Markdown formatting syntax is <strong>not processed within block-level HTML tags</strong>.</p>
<p>Unlike block-level HTML tags, Markdown syntax is <strong>processed within span-level tags</strong>.</p>
<p>Code:</p>
<pre><code>&lt;span&gt;**Work**&lt;/span&gt;

&lt;div&gt;
    **No Work**
&lt;/div&gt;
</code></pre>
<p>Preview:</p>
<hr />
<p>&lt;span&gt;<strong>Work</strong>&lt;/span&gt;</p>
<p>&lt;div&gt;
<strong>No Work</strong>
&lt;/div&gt;</p>
<hr />
]]></content>
    <author><name>ゆき</name></author>
    <category term="Examples"/>
  </entry>
  <entry>
    <title>懶得去按電源鍵了？Wake On LAN 完整設定教學</title>
    <link href="https://moe.lolicon.io/posts/tech-posts/wake-on-lan/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/tech-posts/wake-on-lan/</id>
    <published>2025-01-19T00:00:00.000Z</published>
    <updated>2026-03-08T00:00:00.000Z</updated>
    <summary>當你不在電腦前需要存取裡面的資料時，電腦卻沒有開機，該怎樣做？</summary>
    <content type="html"><![CDATA[<p>當你不在電腦前卻需要存取裡面的資料時，你會怎樣做？</p>
<p>很多朋友都會回答筆者使用遠端連線解決，因為這是一個非常便捷的選擇</p>
<p>然而，試想一下，假如電腦沒有開機呢？又該怎樣做？ :spoiler[(真的…每次打開遠端桌面連接電腦時，卻發現電腦老是關機了QAQ…)]</p>
<p>這時候，Wake On LAN 便派上用場了！</p>
<p>所以今天就要來教大家如何設定 Wake On LAN（網路喚醒），讓您只要透過手機就能遠端開啟電腦！</p>
<p>&lt;iframe id="video" width="100%" height="450" src="https://www.youtube.com/embed/VG6sCC4afRI?controls=1" frameborder="0" scrolling="no" allowfullscreen&gt;&lt;/iframe&gt;</p>
<p> </p>
<h2>什麼是 Wake On LAN？</h2>
<p>Wake On LAN（簡稱 WOL）是一項允許您透過網路封包遠端開啟電腦的技術。簡單而言，就是發送一個特殊的「魔術封包」（Magic Packet）到目標電腦，讓其從關機狀態實時自動開機。</p>
<p>要讓 WOL 能夠正常運作，需要設定以下三個部分：</p>
<ul>
<li>
<p>網路卡 (Network Interface Card)</p>
</li>
<li>
<p>路由器 (Router)</p>
</li>
<li>
<p>BIOS</p>
</li>
</ul>
<p>讓我們一步一步來看該怎麼設定。</p>
<p> </p>
<h2>Step 1：設定網路卡 (NIC)</h2>
<p>首先要確認您的網路卡支援 WOL 功能，並進行正確設定。</p>
<p>:::warning
無線網路卡並不支援網絡喚醒 (WOL) 功能
:::</p>
<p>以 Windows 為例：</p>
<ol>
<li>首先於鍵盤按下 :keyboard[Win] + :keyboard[X]{theme}，並選擇 <strong>「裝置管理員」(Device Manager)</strong></li>
</ol>
<p><img src="./configwindows1.png" alt="Windows Configuration 1" /></p>
<ol>
<li>展開 「網路介面卡」(Network adapters)</li>
</ol>
<p><img src="./configwindows2.png" alt="Windows Configuration 2" /></p>
<ol>
<li>找到您使用的網路卡，按右鍵選擇 「內容」(Properties)。筆者這邊使用的是 <code>Intel(R) Ethernet Controller I226-V</code></li>
</ol>
<p><img src="./configwindows3.png" alt="Windows Configuration 3" /></p>
<ol>
<li>接着應該會看到以下畫面：</li>
</ol>
<p><img src="./configwindows4.png" alt="Windows Configuration 4" /></p>
<ol>
<li>移至 「電源管理」(Power Management)，啟用 「允許電腦關閉這個裝置以節省電源」(Allow the computer to turn off this device to save power) 和 「允許這個裝置喚醒電腦」(Allow this device to wake the computer)</li>
</ol>
<p><img src="./configwindows5.png" alt="Windows Configuration 5" /></p>
<p>:::note
如果找不到這些選項，可能是您的網路卡不支援 WOL 功能喔！建議更換支援的網卡~
:::</p>
<p>:::note
進階設定 (Optional)</p>
<p>可移至 「進階」(Advanced) 找到以下選項並啟用 :)</p>
<ul>
<li>
<p><code>Wake on Magic Packet</code></p>
</li>
<li>
<p><code>Wake on Pattern Match</code>
:::</p>
</li>
</ul>
<p> </p>
<h2>Step 2：設定路由器</h2>
<p>要讓外網也能喚醒電腦，路由器設定是關鍵！</p>
<p>不同品牌的路由器的設定方式有所參差，但基本上其概念也大同小異~ 以下筆者以 TP-Link 路由器作示例：</p>
<ol>
<li>開啟瀏覽器，輸入 <code>192.168.0.1</code> / <code>192.168.1.1</code> 或您的預設閘道進入管理介面&lt;br&gt;因為筆者已經更改預設閘道為 <code>192.168.217.100</code>，所以這邊輸入 <code>192.168.217.100</code>。</li>
</ol>
<p><img src="./chrome.png" alt="Chrome" /></p>
<p><img src="./tplinklogin1.png" alt="TP-Link Router Login Page" /></p>
<ol>
<li>輸入您的管理員密碼</li>
</ol>
<p><img src="./tplinklogin2.png" alt="TP-Link Router Login Page" /></p>
<p>:::note
建議更改預設的路由器登入密碼，防止被他人入侵。
:::</p>
<ol>
<li>登入後會看到以下介面</li>
</ol>
<p><img src="./routerconfig1.jpg" alt="TP-Link Router User Interface" /></p>
<ol>
<li>進入 <code>Advanced</code> → <code>Network</code> → <code>DHCP Server</code>，在下面的 <code>Address Reservation</code> 按 <code>+ Add</code></li>
</ol>
<p><img src="./routerconfig2.jpg" alt="TP-Link Router User Interface" /></p>
<ol>
<li>輸入目標裝置的 <code>IPv4</code> 和 <code>MAC</code>，然後按 :keyboard[Save]{theme} 保存</li>
</ol>
<p><img src="./routerconfig3.jpg" alt="TP-Link Router User Interface" /></p>
<ol>
<li>移至 <code>Security</code> → <code>IP &amp; MAC Binding</code> 並啟用，於下方 <code>Binding List</code> 按 <code>+ Add</code></li>
</ol>
<p><img src="./routerconfig4.jpg" alt="TP-Link Router User Interface" /></p>
<ol>
<li>輸入目標裝置的 <code>MAC</code> 和 <code>IPv4</code>，然後按 :keyboard[Save]{theme} 保存</li>
</ol>
<p><img src="./routerconfig5.jpg" alt="TP-Link Router User Interface" /></p>
<ol>
<li>移至 <code>NAT Forwarding</code> → <code>Port Forwarding</code> ， 按 <code>+ Add</code></li>
</ol>
<p><img src="./routerconfig6.jpg" alt="TP-Link Router User Interface" /></p>
<ol>
<li>
<p>設定連接埠轉發：</p>
<ul>
<li>
<p>協定：<code>All</code></p>
</li>
<li>
<p>外部埠：<strong>您要使用的埠</strong></p>
</li>
<li>
<p>內部埠：<strong>您要使用的埠</strong></p>
</li>
<li>
<p>內部 IP：<strong>您要喚醒的電腦 IP</strong></p>
</li>
</ul>
</li>
</ol>
<p>完成後按 :keyboard[Save]{theme} 保存</p>
<p><img src="./routerconfig7.jpg" alt="TP-Link Router User Interface" /></p>
<h2>Step 3：主機板 BIOS 設定</h2>
<p>這是最後但也是不可或缺的一步！下面筆者以 MSI 主機板為例：</p>
<p>進入 BIOS 的辦法有很多，以下是其中一種：</p>
<ul>
<li>開機時瘋狂按 :keyboard[Delete]{theme} / :keyboard[F2]{theme} / :keyboard[F12]{theme} 鍵進入 BIOS</li>
</ul>
<p>然而筆者啟用了<strong>快速啟動 (MSI Fast Boot)</strong>，跳過了鍵盤偵測的程序，故需要從<strong>Windows 復原介面</strong>進入</p>
<ol>
<li>於復原/恢復介面中，選擇 「疑難排解」 (Troubleshooting) → 「進階選項」 (Advanced options)</li>
</ol>
<p><img src="./recovery1.jpg" alt="Windows Recovery Screen" /></p>
<p><img src="./recovery2.jpg" alt="Windows Recovery Screen" /></p>
<ol>
<li>選擇 「UEFI 韌體設定」 (UEFI Firmware Settings) → 然後按 「重新啟動」 (Restart)</li>
</ol>
<p><img src="./recovery3.jpg" alt="Windows Recovery Screen" /></p>
<p><img src="./recovery4.jpg" alt="Windows Recovery Screen" /></p>
<p>進入 BIOS 後，進行如下設定：</p>
<ol>
<li>於主選單中，移至 <code>SETTINGS</code> → <code>Advanced</code></li>
</ol>
<p><img src="./bios1.jpg" alt="MSI BIOS Configuration" /></p>
<p><img src="./bios2.jpg" alt="MSI BIOS Configuration" /></p>
<ol>
<li>選擇 <code>Wake Up Event Setup</code></li>
</ol>
<p><img src="./bios3.jpg" alt="MSI BIOS Configuration" /></p>
<ol>
<li>在 <code>Resume By PCI-E/Networking Device</code> 中，將設定更改為 <code>Enable</code></li>
</ol>
<p><img src="./bios4.jpg" alt="MSI BIOS Configuration" /></p>
<p><img src="./bios5.jpg" alt="MSI BIOS Configuration" /></p>
<p><img src="./bios6.jpg" alt="MSI BIOS Configuration" /></p>
<ol>
<li>大功告成！請按 :keyboard[X]{theme} 儲存設定並重新開機</li>
</ol>
<p>:::note
同樣地，不同品牌的主機板的設定方式有所參差，但基本上都能在進階選項中找到 WOL 設定。
:::</p>
<h2>實際測試</h2>
<p>設定完成後，建議先在區網內測試：</p>
<ol>
<li>
<p>關閉目標電腦</p>
</li>
<li>
<p>使用手機下載 WOL App（Android 推薦 <a href="https://play.google.com/store/apps/details?id=co.uk.mrwebb.wakeonlan&amp;hl=en_US">Wake On LAN</a>, iOS 推薦 <a href="https://apps.apple.com/au/app/wolow-./id1500970060">Wolow - Wake on LAN</a>）</p>
</li>
<li>
<p>輸入電腦的 <code>MAC</code> 和 <code>IPv4 廣播位址</code></p>
</li>
</ol>
<p>:::note[wiki]
什麼是廣播位址？</p>
<p>廣播位址(Broadcast Address)是專門用來同時傳送到網路中所有工作站的一個位址。在使用TCP/IP協定的網路中，主機識別段host ID 為全1的IP位址為廣播位址… （背後原理由於較複雜，在此並不詳述）</p>
<p> </p>
<p>一般而言，廣播位址通常為路由器網段中的最後一個IP位置</p>
<p>例如： 若網段為 <code>192.168.217.0</code> - <code>192.168.217.255</code>，廣播位址為 <code>192.168.217.255</code>
:::</p>
<ol>
<li>發送喚醒訊號</li>
</ol>
<p>如果電腦能成功開機，就代表設定成功了！</p>
<p>附送一個 Python 小程式供各位方便食用。祝用餐愉快 😄</p>
<pre><code>import socket
import struct

class Computer:
    def __init__(self, mac_address, hostname) -&gt; None:
        self.mac_address = mac_address
        self.hostname = hostname

    def wake(self, port=9):
        # Create a socket for sending the magic packet
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # Bind the socket to a local address and port
        sock.bind(('', 0))

        # Construct the magic packet
        mac_bytes_list = self.mac_address.split(':')
        mac_bytes = struct.pack('BBBBBB', int(mac_bytes_list[0],16),
            int(mac_bytes_list[1],16),
            int(mac_bytes_list[2],16),
            int(mac_bytes_list[3],16),
            int(mac_bytes_list[4],16),
            int(mac_bytes_list[5],16))
        magic_packet = b'\xff' * 6 + mac_bytes * 16

        # Send the magic packet to the IP address and port
        sock.sendto(magic_packet, (self.hostname, port))
        sock.close()
        return
</code></pre>
<p>食用方法：</p>
<pre><code>from &lt;your-script-name&gt; import Computer
# Specify the MAC address, and the public IP address or domain name that points to your router of your PC
# e.g.
computer1 = Computer(mac_address1, ip_address1)
computer2 = Computer(mac_address2, ip_address2)
# ...

# Wake up the PC(s)
computer1.wake()
computer2.wake()
# ...

</code></pre>
<h2>常見問題 Q&amp;A</h2>
<p> </p>
<p><strong>Q: 為什麼設定都正確但還是無法喚醒？</strong></p>
<p>A: 最常見的原因是：</p>
<ul>
<li>
<p>網路線沒接好</p>
</li>
<li>
<p>電源供應器沒有支援</p>
</li>
<li>
<p>防火牆/殺毒軟件擋住了喚醒封包</p>
</li>
<li>
<p>快速啟動功能已啟用。移至「控制台」 → 「電源管理」 → 「選擇按下電源按鈕時的行為」, 關閉「快速啟動」。</p>
</li>
</ul>
<p> </p>
<p><strong>Q: 筆電可以用 WOL 嗎？</strong></p>
<p>A: 大部份筆電以使用無線網絡為主。新款筆電甚至不提供網絡線插口，故並不支援LAN網絡喚醒。</p>
<p>少部份舊款機型可以，但需要確保BIOS內有支援，其次需要特別注意電源管理設定，且最好使用原廠電源適配器。</p>
<p> </p>
<h2>總結</h2>
<p>WOL 的功能設定可謂繁複，但設定好之後真的非常方便！再也不用擔心忘了開電腦而無法遠端工作了⋯⋯</p>
<p>不過要提醒大家，使用 WOL 時還是要注意資安問題，建議：</p>
<ul>
<li>
<p>定期更新韌體</p>
</li>
<li>
<p>更改預設密碼</p>
</li>
<li>
<p>必要時才開啟 WOL 功能</p>
</li>
</ul>
<p>您也打算設定 WOL 嗎？歡迎在下方留言分享您的使用經驗！</p>
]]></content>
    <author><name>ゆき</name></author>
    <category term="技術文"/>
  </entry>
  <entry>
    <title>おおみそか 有感</title>
    <link href="https://moe.lolicon.io/posts/free-writing/omisoka/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/free-writing/omisoka/</id>
    <published>2024-12-31T00:00:00.000Z</published>
    <updated>2024-12-31T00:00:00.000Z</updated>
    <summary>新的一年，新的開始。</summary>
    <content type="html"><![CDATA[<p>:::note
本文大部分內容均由人工智能助理生成
:::</p>
<p>一年一度的除夕，總是帶著濃濃的年味和家的溫暖。對於許多人來說，除夕不僅為每一年的最後一天，更是告別過去、迎接未來希望的象徵。</p>
<h2>團聚的時刻</h2>
<p>新年除夕的核心是「團聚」。無論身在何處，人們總是盡可能與家人或朋友相聚，共度這特別的一夜。無論是與親人共享溫馨的家庭晚宴，還是與朋友舉杯歡慶，這一天的重點都在於聯繫彼此，創造珍貴的回憶。</p>
<p>餐桌上的美食成為慶祝的焦點，無論是象徵幸福的菜餚、甜點，還是手中的香檳，每一口都寄託著對新年的美好願望。隨著夜晚的推進，大家分享過去一年的點點滴滴，無論是成功還是挑戰，都成為連結彼此的重要話題。</p>
<h2>倒數的魔力</h2>
<p>除夕夜最令人期待的時刻莫過於午夜的倒數。無論是在城市廣場、派對現場，還是家中的電視機前，全球各地的人們都用心參與這一刻的儀式感。</p>
<p>「$10$！ $9$！ $8$！...」</p>
<p>倒數最後十秒響起時，心中充滿了期待、興奮與團結。</p>
<p>午夜的時刻到來，新年的煙火點亮夜空，象徵著嶄新的開始。人們相擁祝福，親朋好友互道「新年快樂！」，這一刻充滿了希望與溫暖。</p>
<h2>定立目標與新希望</h2>
<p>新年除夕不僅僅是歡慶的日子，也是反思的好時機。它提醒我們停下腳步，回顧過去一年的收穫與教訓，並且為新的一年設定新的目標。無論是追求健康的生活方式、學習新的技能，還是實現長久以來的夢想，新年的到來總讓人充滿幹勁。</p>
<p>這些目標與決心無論大小，都是我們邁向更好自己的開始。除夕夜的熱鬧氣氛，總能激勵我們相信未來充滿無限可能。</p>
<h2>展望未來</h2>
<p>除夕不僅僅是一場慶典，更是反思與感恩的時刻。它提醒我們珍惜身邊的人，感謝過去的成長，同時對未來充滿希望。這一天是一個新的起點，為我們帶來信心與勇氣，迎接即將到來的一切。</p>
<p>願新的一年，為你帶來幸福、健康與成功。讓我們滿懷希望地邁向未來，並努力實現我們的夢想！</p>
<p> </p>
<p>2025 新年快樂！🎆</p>
<h2>後記 (2025 年更新)</h2>
<p>新的一年，象徵住一個新的開始。在此願諸位事事順心，確立目標並付諸實行 💪</p>
]]></content>
    <author><name>ゆき</name></author>
    <category term="隨筆"/>
  </entry>
  <entry>
    <title>Embedding video or iframe in your posts</title>
    <link href="https://moe.lolicon.io/posts/embedding-video-or-iframe/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/embedding-video-or-iframe/</id>
    <published>2024-08-01T00:00:00.000Z</published>
    <updated>2026-03-08T00:00:00.000Z</updated>
    <summary>This post demonstrates how to include embedded video in a blog post.</summary>
    <content type="html"><![CDATA[<p>You may copy the embed iframe from Internet, and paste it inside the markdown file you are working on.</p>
<p>Here is an example:</p>
<pre><code>---
title: My cool post
published: 2023-12-05
// ...
---

## YouTube

&lt;iframe width="100%" height="468" src="https://www.youtube.com/embed/5gIf0_xpFPI?si=N1WTorLKL0uwLsU_" title="YouTube video player" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;

## Google Maps

&lt;iframe width="100%" height="468" src="https://www.google.com/maps/embed?pb=!1m10!1m8!1m3!1d2860.284021870435!2d114.25985502110306!3d22.308945085830434!3m2!1i1024!2i768!4f13.1!5e0!3m2!1szh-TW!2shk!4v1772951982812!5m2!1szh-TW!2shk" width="600" height="450" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"&gt;&lt;/iframe&gt;

// ...
</code></pre>
<h2>YouTube</h2>
<p>&lt;iframe width="100%" height="468" src="https://www.youtube.com/embed/5gIf0_xpFPI?si=N1WTorLKL0uwLsU_" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen&gt;&lt;/iframe&gt;</p>
<h2>Google Maps</h2>
<p>&lt;iframe width="100%" height="468" src="https://www.google.com/maps/embed?pb=!1m10!1m8!1m3!1d2860.284021870435!2d114.25985502110306!3d22.308945085830434!3m2!1i1024!2i768!4f13.1!5e0!3m2!1szh-TW!2shk!4v1772951982812!5m2!1szh-TW!2shk" width="600" height="450" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"&gt;&lt;/iframe&gt;</p>
<h2>Bilibili</h2>
<p>&lt;iframe width="100%" height="468" src="//player.bilibili.com/player.html?bvid=BV1fK4y1s7Qf&amp;p=1&amp;autoplay=0" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" &amp;autoplay=0&gt; &lt;/iframe&gt;</p>
]]></content>
    <author><name>ゆき</name></author>
    <category term="Examples"/>
  </entry>
  <entry>
    <title>Simple Guides for Mizuki</title>
    <link href="https://moe.lolicon.io/posts/guide/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/guide/</id>
    <published>2024-04-01T00:00:00.000Z</published>
    <updated>2024-04-01T00:00:00.000Z</updated>
    <summary>How to use this blog template.</summary>
    <content type="html"><![CDATA[<p>This blog template is built with <a href="https://astro.build/">Astro</a>. For the things that are not mentioned in this guide, you may find the answers in the <a href="https://docs.astro.build/">Astro Docs</a>.</p>
<h2>Front-matter of Posts</h2>
<pre><code>---
title: My First Blog Post
published: 2023-09-09
description: This is the first post of my new Astro blog.
image: ./cover.jpg
tags: [Foo, Bar]
category: Front-end
draft: false
---
</code></pre>
<table>
<thead>
<tr>
<th>Attribute</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>title</code></td>
<td>The title of the post.</td>
</tr>
<tr>
<td><code>published</code></td>
<td>The date the post was published.</td>
</tr>
<tr>
<td><code>pinned</code></td>
<td>Whether this post is pinned to the top of the post list.</td>
</tr>
<tr>
<td><code>description</code></td>
<td>A short description of the post. Displayed on index page.</td>
</tr>
<tr>
<td><code>image</code></td>
<td>The cover image path of the post.&lt;br/&gt;1. Start with <code>http://</code> or <code>https://</code>: Use web image&lt;br/&gt;2. Start with <code>/</code>: For image in <code>public</code> dir&lt;br/&gt;3. With none of the prefixes: Relative to the markdown file</td>
</tr>
<tr>
<td><code>tags</code></td>
<td>The tags of the post.</td>
</tr>
<tr>
<td><code>category</code></td>
<td>The category of the post.</td>
</tr>
<tr>
<td><code>licenseName</code></td>
<td>The license name for the post content.</td>
</tr>
<tr>
<td><code>author</code></td>
<td>The author of the post.</td>
</tr>
<tr>
<td><code>sourceLink</code></td>
<td>The source link or reference for the post content.</td>
</tr>
<tr>
<td><code>draft</code></td>
<td>If this post is still a draft, which won't be displayed.</td>
</tr>
</tbody>
</table>
<h2>Where to Place the Post Files</h2>
<p>Your post files should be placed in <code>src/content/posts/</code> directory. You can also create sub-directories to better organize your posts and assets.</p>
<pre><code>src/content/posts/
├── post-1.md
└── post-2/
    ├── cover.png
    └── index.md
</code></pre>
]]></content>
    <author><name>ゆき</name></author>
    <category term="Guides"/>
  </entry>
  <entry>
    <title>🔒 Encrypted Post</title>
    <link href="https://moe.lolicon.io/posts/encrypted-post/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/encrypted-post/</id>
    <published>2024-01-15T00:00:00.000Z</published>
    <updated>2024-01-15T00:00:00.000Z</updated>
    <summary>該文章內容已加密，請輸入密碼後查看。</summary>
    <content type="html"><![CDATA[<p><em>🔒 本文已加密保護，請訪問部落格網站輸入密碼閱讀全文。</em></p>]]></content>
    <author><name>ゆき</name></author>
    <category term="Technology"/>
  </entry>
  <entry>
    <title>Markdown Mermaid</title>
    <link href="https://moe.lolicon.io/posts/markdown-mermaid/" rel="alternate" type="text/html"/>
    <id>https://moe.lolicon.io/posts/markdown-mermaid/</id>
    <published>2023-10-01T00:00:00.000Z</published>
    <updated>2023-10-01T00:00:00.000Z</updated>
    <summary>A simple example of a Markdown blog post with Mermaid.</summary>
    <content type="html"><![CDATA[<h1>Complete Guide to Markdown with Mermaid Diagrams</h1>
<p>This article demonstrates how to create various complex diagrams using Mermaid in Markdown documents, including flowcharts, sequence diagrams, Gantt charts, class diagrams, and state diagrams.</p>
<h2>Flowchart Example</h2>
<p>Flowcharts are excellent for representing processes or algorithm steps.</p>
<pre><code>graph TD
    A[Start] --&gt; B{Condition Check}
    B --&gt;|Yes| C[Process Step 1]
    B --&gt;|No| D[Process Step 2]
    C --&gt; E[Subprocess]
    D --&gt; E
    subgraph E [Subprocess Details]
        E1[Substep 1] --&gt; E2[Substep 2]
        E2 --&gt; E3[Substep 3]
    end
    E --&gt; F{Another Decision}
    F --&gt;|Option 1| G[Result 1]
    F --&gt;|Option 2| H[Result 2]
    F --&gt;|Option 3| I[Result 3]
    G --&gt; J[End]
    H --&gt; J
    I --&gt; J
</code></pre>
<h2>Sequence Diagram Example</h2>
<p>Sequence diagrams show interactions between objects over time.</p>
<pre><code>sequenceDiagram
    participant User
    participant WebApp
    participant Server
    participant Database

    User-&gt;&gt;WebApp: Submit Login Request
    WebApp-&gt;&gt;Server: Send Auth Request
    Server-&gt;&gt;Database: Query User Credentials
    Database--&gt;&gt;Server: Return User Data
    Server--&gt;&gt;WebApp: Return Auth Result
    
    alt Auth Successful
        WebApp-&gt;&gt;User: Show Welcome Page
        WebApp-&gt;&gt;Server: Request User Data
        Server-&gt;&gt;Database: Get User Preferences
        Database--&gt;&gt;Server: Return Preferences
        Server--&gt;&gt;WebApp: Return User Data
        WebApp-&gt;&gt;User: Load Personalized Interface
    else Auth Failed
        WebApp-&gt;&gt;User: Show Error Message
        WebApp-&gt;&gt;User: Prompt Re-entry
    end
</code></pre>
<h2>Gantt Chart Example</h2>
<p>Gantt charts are perfect for displaying project schedules and timelines.</p>
<pre><code>gantt
    title Website Development Project Timeline
    dateFormat  YYYY-MM-DD
    axisFormat  %m/%d
    
    section Design Phase
    Requirements Analysis      :a1, 2023-10-01, 7d
    UI Design                 :a2, after a1, 10d
    Prototype Creation        :a3, after a2, 5d
    
    section Development Phase
    Frontend Development      :b1, 2023-10-20, 15d
    Backend Development       :b2, after a2, 18d
    Database Design           :b3, after a1, 12d
    
    section Testing Phase
    Unit Testing              :c1, after b1, 8d
    Integration Testing       :c2, after b2, 10d
    User Acceptance Testing   :c3, after c2, 7d
    
    section Deployment
    Production Deployment     :d1, after c3, 3d
    Launch                    :milestone, after d1, 0d
</code></pre>
<h2>Class Diagram Example</h2>
<p>Class diagrams show the static structure of a system, including classes, attributes, methods, and their relationships.</p>
<pre><code>classDiagram
    class User {
        +String username
        +String password
        +String email
        +Boolean active
        +login()
        +logout()
        +updateProfile()
    }
    
    class Article {
        +String title
        +String content
        +Date publishDate
        +Boolean published
        +publish()
        +edit()
        +delete()
    }
    
    class Comment {
        +String content
        +Date commentDate
        +addComment()
        +deleteComment()
    }
    
    class Category {
        +String name
        +String description
        +addArticle()
        +removeArticle()
    }
    
    User "1" -- "*" Article : writes
    User "1" -- "*" Comment : posts
    Article "1" -- "*" Comment : has
    Article "1" -- "*" Category : belongs to
</code></pre>
<h2>State Diagram Example</h2>
<p>State diagrams show the sequence of states an object goes through during its life cycle.</p>
<pre><code>stateDiagram-v2
    [*] --&gt; Draft
    
    Draft --&gt; UnderReview : submit
    UnderReview --&gt; Draft : reject
    UnderReview --&gt; Approved : approve
    Approved --&gt; Published : publish
    Published --&gt; Archived : archive
    Published --&gt; Draft : retract
    
    state Published {
        [*] --&gt; Active
        Active --&gt; Hidden : temporarily hide
        Hidden --&gt; Active : restore
        Active --&gt; [*]
        Hidden --&gt; [*]
    }
    
    Archived --&gt; [*]
</code></pre>
<h2>Pie Chart Example</h2>
<p>Pie charts are ideal for displaying proportions and percentage data.</p>
<pre><code>pie title Website Traffic Sources Analysis
    "Search Engines" : 45.6
    "Direct Access" : 30.1
    "Social Media" : 15.3
    "Referral Links" : 6.4
    "Other Sources" : 2.6
</code></pre>
<h2>Conclusion</h2>
<p>Mermaid is a powerful tool for creating various types of diagrams in Markdown documents. This article demonstrated how to use flowcharts, sequence diagrams, Gantt charts, class diagrams, state diagrams, and pie charts. These diagrams can help you express complex concepts, processes, and data structures more clearly.</p>
<p>To use Mermaid, simply specify the mermaid language in a code block and describe the diagram using concise text syntax. Mermaid will automatically convert these descriptions into beautiful visual diagrams.</p>
<p>Try using Mermaid diagrams in your next technical blog post or project documentation - they will make your content more professional and easier to understand!</p>
]]></content>
    <author><name>ゆき</name></author>
    <category term="Examples"/>
  </entry>
</feed>
