<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>编程 on MoeJueのブログ</title>
    <link>https://ja.moejue.cn/zh-hant/tags/%E7%BC%96%E7%A8%8B/</link>
    <description>Recent content in 编程 on MoeJueのブログ</description>
    <generator>Hugo</generator>
    <language>zh-hant</language>
    <lastBuildDate>Sat, 10 Jan 2026 14:02:35 +0000</lastBuildDate>
    <atom:link href="https://ja.moejue.cn/zh-hant/tags/%E7%BC%96%E7%A8%8B/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Web3 鏈上紅包 DApp</title>
      <link>https://ja.moejue.cn/zh-hant/posts/303/</link>
      <pubDate>Sat, 10 Jan 2026 14:02:35 +0000</pubDate>
      <guid>https://ja.moejue.cn/zh-hant/posts/303/</guid>
      <description>&lt;p&gt;這是一個&lt;strong&gt;100%去中心化&lt;/strong&gt;的區塊鏈紅包應用。用戶可以連接錢包，設定金額和數量發送紅包，然後透過連結分享給他人領取。所有資料和操作都基於區塊鏈，完全不需要中心化伺服器，整個過程安全、透明、可追溯。&lt;/p&gt;&#xA;&lt;p&gt;如果你感興趣，可以在 GitHub 上看看我的程式碼：&lt;a href=&#34;https://github.com/iAJue/redpacket-dapp&#34;target=&#34;_blank&#34;&#xA;  class=&#34;inline-flex items-center gap-1&#34;&#xA;&gt;redpacket‑dapp&#xA;  &lt;svg id=&#34;external-link&#34; class=&#34;h-3 w-3 flex-shrink-0&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M15 3h6v6m-11 5L21 3m-3 10v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&#34;/&gt;&lt;/svg&gt;&#xA;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;功能亮點&#34;&gt;功能亮點&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;🧧 &lt;strong&gt;發送紅包&lt;/strong&gt;: 連接錢包後，設定金額和數量發送紅包到區塊鏈&lt;/li&gt;&#xA;&lt;li&gt;🎁 &lt;strong&gt;領取紅包&lt;/strong&gt;: 透過分享連結，別人可以連接錢包直接從區塊鏈領取紅包&lt;/li&gt;&#xA;&lt;li&gt;🔗 &lt;strong&gt;智能合約&lt;/strong&gt;: 使用 Solidity 編寫的安全智能合約處理所有邏輯&lt;/li&gt;&#xA;&lt;li&gt;🌐 &lt;strong&gt;多鏈支援&lt;/strong&gt;: 支援本地測試網和 BSC 測試網&lt;/li&gt;&#xA;&lt;li&gt;💼 &lt;strong&gt;Web3 整合&lt;/strong&gt;: 使用 MetaMask 錢包連接&lt;/li&gt;&#xA;&lt;li&gt;🚀 &lt;strong&gt;完全去中心化&lt;/strong&gt;: 沒有後端伺服器，所有資料儲存在區塊鏈&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;技術棧&#34;&gt;技術棧&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;前端&lt;/strong&gt;：React 19、React Router 6、TypeScript、Vite、Ethers v6&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;合約層&lt;/strong&gt;：Hardhat、Solidity 0.8.x&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;工具&lt;/strong&gt;：ESLint、Prettier&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;不需要後端&lt;/strong&gt;: 零依賴伺服器 ❌&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;快速啟動&#34;&gt;快速啟動&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code&gt;# 1. 安裝專案依賴&#xA;npm install&#xA;cd react-dapp &amp;amp;&amp;amp; npm install &amp;amp;&amp;amp; cd ..&#xA;&#xA;# 2. 啟動本地區塊鏈節點&#xA;npm run node&#xA;&#xA;# 3. 編譯智能合約&#xA;npx hardhat compile&#xA;&#xA;# 4. 部署智能合約&#xA;npm run deploy&#xA;&#xA;# 5. 啟動前端&#xA;npm run frontend&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2 id=&#34;目錄結構&#34;&gt;目錄結構&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code&gt;redpacket-web3/&#xA;├─ contracts/                # Solidity 智能合約&#xA;│  └─ RedPacket.sol&#xA;├─ react-dapp/               # React + Vite 前端&#xA;│  ├─ src/&#xA;│  │  ├─ components/         # 通用組件（錢包按鈕、創建表單等）&#xA;│  │  ├─ config/             # ABI、合約地址配置&#xA;│  │  ├─ hooks/              # 自定義 hooks（useWallet）&#xA;│  │  ├─ pages/              # 頁面（Home、ClaimPacket）&#xA;│  │  ├─ styles/             # 全域樣式&#xA;│  │  ├─ utils/              # Web3 工具函數&#xA;│  │  └─ App.tsx             # 路由與佈局&#xA;│  ├─ .env.example           # 前端所需環境變數說明&#xA;│  └─ package.json&#xA;├─ scripts/&#xA;│  └─ deploy.js              # Hardhat 部署腳本（會自動寫入前端配置）&#xA;├─ hardhat.config.js&#xA;└─ package.json              # 根目錄腳本（Hardhat + 前端一鍵啟動）&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2 id=&#34;使用指南&#34;&gt;使用指南&lt;/h2&gt;&#xA;&lt;h3 id=&#34;創建紅包&#34;&gt;創建紅包&lt;/h3&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;打開前端並點擊右上角「連接錢包」。&lt;/li&gt;&#xA;&lt;li&gt;填寫紅包總金額與份數，應用會在前端隨機拆分金額。&lt;/li&gt;&#xA;&lt;li&gt;提交後在 MetaMask 中確認交易，等待區塊確認。&lt;/li&gt;&#xA;&lt;li&gt;成功後可複製系統生成的領取連結，分享給朋友。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;領取紅包&#34;&gt;領取紅包&lt;/h3&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;打開分享連結（URL 帶有 &lt;code&gt;/claim/{packetId}&lt;/code&gt;）。&lt;/li&gt;&#xA;&lt;li&gt;同樣先連接錢包，然後點擊「點擊領取」。&lt;/li&gt;&#xA;&lt;li&gt;簽名確認後等待區塊打包，頁面會顯示結果與剩餘份數。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;環境變數&#34;&gt;環境變數&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;dapp/.env&lt;/code&gt;（開發環境會自動忽略，請參考 &lt;code&gt;.env.example&lt;/code&gt;）&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;VITE_RPC_URL&lt;/code&gt;：前端直連節點 RPC，預設為 &lt;code&gt;http://127.0.0.1:8545&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;VITE_CONTRACT_ADDRESS&lt;/code&gt;：可覆蓋部署腳本寫入的地址&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;VITE_CHAIN_ID&lt;/code&gt;：前端提示使用的鏈 ID（十六進制）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;root/.env&lt;/code&gt;：若需要在 Hardhat 中引用私鑰或 BSC RPC，可繼續沿用原有寫法。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;智能合約redpacketsol&#34;&gt;智能合約（RedPacket.sol）&lt;/h2&gt;&#xA;&lt;p&gt;核心方法：&lt;/p&gt;</description>
    </item>
    <item>
      <title>我不會AE,但是我會Code</title>
      <link>https://ja.moejue.cn/zh-hant/posts/280/</link>
      <pubDate>Sat, 23 Aug 2025 12:29:01 +0000</pubDate>
      <guid>https://ja.moejue.cn/zh-hant/posts/280/</guid>
      <description>&lt;h2 id=&#34;-緒山真尋的小窩-&#34;&gt;✨ 緒山真尋的小窩 ✨&lt;/h2&gt;&#xA;&lt;p&gt;&#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://mahiro.moejue.cn/static/images/onimai.png&#34;&#xA;    alt=&#34;緒山まひろ&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://mahiro.moejue.cn/static/images/onimai.png&#34;&#xA;    data-gallery-alt=&#34;緒山まひろ&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;-歡迎來到緒山真尋的小窩-&#34;&gt;🌸 歡迎來到緒山真尋的小窩 🌸&lt;/h3&gt;&#xA;&lt;p&gt;&#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://mahiro.moejue.cn/static/images/Mahiro06.png&#34;&#xA;    alt=&#34;緒山まひろ&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://mahiro.moejue.cn/static/images/Mahiro06.png&#34;&#xA;    data-gallery-alt=&#34;緒山まひろ&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt;&lt;/p&gt;&#xA;&lt;p&gt;哇！你發現了我的秘密基地！(*≧ω≦)&lt;/p&gt;&#xA;&lt;p&gt;這裡是緒山真尋的個人網站，充滿了可愛的動畫和有趣的內容！我會在這裡分享我喜歡的動畫、漫畫、遊戲和一些日常生活中的小確幸～&lt;/p&gt;&#xA;&lt;h3 id=&#34;-關於我-&#34;&gt;💕 關於我 💕&lt;/h3&gt;&#xA;&lt;p&gt;我是緒山真尋，一個熱愛成人遊戲的家裡蹲廢柴尼特族。喜歡動畫、漫畫、輕小說和各種可愛的東西！最喜歡的顏色是粉色和淡藍色！&lt;/p&gt;&#xA;&lt;div&#xA;  class=&#34;code-block-container border-border bg-card my-6 overflow-hidden rounded-xl border shadow-sm transition-all duration-200 ease-out hover:-translate-y-0.5 hover:shadow-md&#34;&gt;&#xA;  &#xA;  &lt;div&#xA;    class=&#34;code-block-header bg-muted/30 border-border flex items-center justify-between border-b px-4 py-3&#34;&gt;&#xA;    &#xA;    &lt;div class=&#34;flex items-center gap-2&#34;&gt;&#xA;      &lt;div class=&#34;text-muted-foreground flex-shrink-0&#34;&gt;&#xA;        &#xA;  &lt;svg class=&#34;h-4 w-4&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4&#34; /&gt;&#xA;&lt;/svg&gt;&#xA;      &lt;/div&gt;&#xA;      &lt;span class=&#34;text-muted-foreground text-sm font-medium&#34;&gt;&#xA;        PLAINTEXT&#xA;      &lt;/span&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;flex items-center gap-2&#34;&gt;&#xA;      &lt;button&#xA;          class=&#34;collapse-code-btn text-muted-foreground hover:text-primary hover:bg-primary/10 focus:ring-primary/20 flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium transition-all duration-200 ease-out focus:ring-2 focus:outline-none&#34;&#xA;          data-code-id=&#34;code-0&#34;&#xA;          data-default-state=&#34;expanded&#34;&#xA;          data-collapsed=&#34;false&#34;&#xA;          data-auto-collapse-lines=&#34;30&#34;&#xA;          data-auto-collapse-height=&#34;400&#34;&#xA;          data-collapsed-height=&#34;120&#34;&#xA;          title=&#34;折りたたむ&#34;&#xA;          aria-label=&#34;折りたたむ&#34;&gt;&#xA;          &lt;span class=&#34;collapse-icon&#34;&gt;&#xA;            &#xA;  &lt;svg class=&#34;h-3 w-3&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6l-6 6z&#34;/&gt;&lt;/svg&gt;&#xA;          &lt;/span&gt;&#xA;          &lt;span class=&#34;collapse-text hidden sm:inline&#34;&#xA;            &gt;折りたたむ&lt;/span&#xA;          &gt;&#xA;        &lt;/button&gt;&#xA;      &lt;button&#xA;        class=&#34;copy-code-btn text-muted-foreground hover:text-primary hover:bg-primary/10 focus:ring-primary/20 flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium transition-all duration-200 ease-out focus:ring-2 focus:outline-none&#34;&#xA;        data-code-id=&#34;code-0&#34;&#xA;        title=&#34;コピー&#34;&#xA;        aria-label=&#34;コピー&#34;&gt;&#xA;        &lt;span class=&#34;copy-icon&#34;&gt;&#xA;          &#xA;  &lt;svg class=&#34;h-3 w-3&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z&#34; /&gt;&#xA;&lt;/svg&gt;&#xA;        &lt;/span&gt;&#xA;        &lt;span class=&#34;copy-text hidden sm:inline&#34;&#xA;          &gt;コピー&lt;/span&#xA;        &gt;&#xA;      &lt;/button&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;&#xA;  &#xA;  &lt;div class=&#34;code-block-content relative&#34; id=&#34;code-0&#34;&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;  ∩∩&#xA;（･ω･）  &amp;lt;- 這是我！&#xA;＿|　⊃／(＿＿&#xA;／ └-(＿＿＿／&lt;/code&gt;&lt;/pre&gt;&#xA;    &#xA;    &lt;div&#xA;      class=&#34;collapse-overlay to-card/90 pointer-events-none absolute inset-0 bg-gradient-to-b from-transparent via-transparent opacity-0 transition-opacity duration-300&#34;&gt;&#xA;      &lt;div&#xA;        class=&#34;text-muted-foreground bg-card/80 border-border/50 hover:bg-primary/10 hover:text-primary hover:border-primary/30 absolute bottom-4 left-1/2 -translate-x-1/2 cursor-pointer rounded-full border px-3 py-1.5 text-xs backdrop-blur-sm transition-all duration-200&#34;&gt;&#xA;        クリックして展開し、詳細を表示&#xA;      &lt;/div&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;script&gt;&#xA;(function() {&#xA;  const codeId = &#39;code-0&#39;;&#xA;  const copyBtn = document.querySelector(&#39;.copy-code-btn[data-code-id=&#34;&#39; + codeId + &#39;&#34;]&#39;);&#xA;  const collapseBtn = document.querySelector(&#39;.collapse-code-btn[data-code-id=&#34;&#39; + codeId + &#39;&#34;]&#39;);&#xA;  const codeContainer = document.getElementById(codeId);&#xA;&#xA;  if (!codeContainer) return;&#xA;&#xA;  &#xA;  if (copyBtn) {&#xA;    const copyIcon = copyBtn.querySelector(&#39;.copy-icon&#39;);&#xA;    const copyText = copyBtn.querySelector(&#39;.copy-text&#39;);&#xA;&#xA;    copyBtn.addEventListener(&#39;click&#39;, async function() {&#xA;      try {&#xA;        &#xA;        let codeText = &#39;&#39;;&#xA;&#xA;        &#xA;        const codeTableCell = codeContainer.querySelector(&#39;.lntd:last-child code&#39;);&#xA;        if (codeTableCell) {&#xA;          codeText = codeTableCell.textContent || codeTableCell.innerText;&#xA;        } else {&#xA;          &#xA;          const codeElement = codeContainer.querySelector(&#39;code&#39;);&#xA;          if (codeElement) {&#xA;            &#xA;            const hasInlineLineNumbers = codeElement.querySelector(&#39;.ln&#39;);&#xA;            if (hasInlineLineNumbers) {&#xA;              &#xA;              const codeLines = codeElement.querySelectorAll(&#39;.cl&#39;);&#xA;              if (codeLines.length &gt; 0) {&#xA;                codeText = Array.from(codeLines)&#xA;                  .map(line =&gt; {&#xA;                    const text = line.textContent || line.innerText;&#xA;                    &#xA;                    return text.replace(/\n+$/, &#39;&#39;);&#xA;                  })&#xA;                  .join(&#39;\n&#39;)&#xA;                  .replace(/\n+$/, &#39;&#39;); &#xA;              } else {&#xA;                &#xA;                const allText = codeElement.textContent || codeElement.innerText;&#xA;                codeText = allText.replace(/^\d+/gm, &#39;&#39;).replace(/^\s+/gm, &#39;&#39;);&#xA;              }&#xA;            } else {&#xA;              &#xA;              codeText = codeElement.textContent || codeElement.innerText;&#xA;            }&#xA;          } else {&#xA;            &#xA;            codeText = codeContainer.textContent || codeContainer.innerText;&#xA;          }&#xA;        }&#xA;&#xA;        &#xA;        codeText = codeText.trim();&#xA;&#xA;        &#xA;        await navigator.clipboard.writeText(codeText);&#xA;&#xA;        &#xA;        copyIcon.innerHTML = `\n  \u003csvg class=\u0022h-3 w-3\u0022\n    fill=\u0022none\u0022\n    stroke=\u0022currentColor\u0022\n    viewBox=\u00220 0 24 24\u0022\u003e\u003cpath stroke-linecap=\u0022round\u0022 stroke-linejoin=\u0022round\u0022 stroke-width=\u00222\u0022 d=\u0022M5 13l4 4L19 7\u0022 \/\u003e\n\u003c\/svg\u003e`;&#xA;        if (copyText) {&#xA;          copyText.textContent = &#39;コピーしました&#39;;&#xA;        }&#xA;        copyBtn.classList.add(&#39;text-green-600&#39;);&#xA;&#xA;        &#xA;        setTimeout(() =&gt; {&#xA;          copyIcon.innerHTML = `\n  \u003csvg class=\u0022h-3 w-3\u0022\n    fill=\u0022none\u0022\n    stroke=\u0022currentColor\u0022\n    viewBox=\u00220 0 24 24\u0022\u003e\u003cpath stroke-linecap=\u0022round\u0022 stroke-linejoin=\u0022round\u0022 stroke-width=\u00222\u0022 d=\u0022M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\u0022 \/\u003e\n\u003c\/svg\u003e`;&#xA;          if (copyText) {&#xA;            copyText.textContent = &#39;コピー&#39;;&#xA;          }&#xA;          copyBtn.classList.remove(&#39;text-green-600&#39;);&#xA;        }, 2000);&#xA;&#xA;      } catch (err) {&#xA;        console.error(&#39;复制失败:&#39;, err);&#xA;&#xA;        &#xA;        const range = document.createRange();&#xA;        const codeElement = codeContainer.querySelector(&#39;code&#39;) || codeContainer;&#xA;        range.selectNodeContents(codeElement);&#xA;        const selection = window.getSelection();&#xA;        selection.removeAllRanges();&#xA;        selection.addRange(range);&#xA;&#xA;        &#xA;        if (copyText) {&#xA;          copyText.textContent = &#39;選択済み&#39;;&#xA;        }&#xA;&#xA;        setTimeout(() =&gt; {&#xA;          if (copyText) {&#xA;            copyText.textContent = &#39;コピー&#39;;&#xA;          }&#xA;          selection.removeAllRanges();&#xA;        }, 2000);&#xA;      }&#xA;    });&#xA;  }&#xA;&#xA;  &#xA;  if (collapseBtn) {&#xA;    const collapseIcon = collapseBtn.querySelector(&#39;.collapse-icon&#39;);&#xA;    const collapseText = collapseBtn.querySelector(&#39;.collapse-text&#39;);&#xA;    const collapseOverlay = codeContainer.querySelector(&#39;.collapse-overlay&#39;);&#xA;&#xA;    &#xA;    let codeElement = codeContainer.querySelector(&#39;pre.chroma&#39;);&#xA;    if (!codeElement) {&#xA;      codeElement = codeContainer.querySelector(&#39;pre&#39;);&#xA;    }&#xA;&#xA;    const defaultState = collapseBtn.dataset.defaultState || &#39;expanded&#39;;&#xA;    const isCollapsedAttr = collapseBtn.dataset.collapsed === &#39;true&#39;;&#xA;    const autoCollapseLines = parseInt(collapseBtn.dataset.autoCollapseLines) || 30;&#xA;    const autoCollapseHeight = parseInt(collapseBtn.dataset.autoCollapseHeight) || 400;&#xA;    const collapsedHeight = parseInt(collapseBtn.dataset.collapsedHeight) || 120;&#xA;&#xA;    let isCollapsed = false;&#xA;&#xA;    &#xA;    function initCollapse() {&#xA;      &#xA;      const shouldCollapse = isCollapsedAttr ||&#xA;                           defaultState === &#39;collapsed&#39; ||&#xA;                           shouldAutoCollapse();&#xA;&#xA;      if (shouldCollapse) {&#xA;        setCollapsed(true, false); &#xA;      }&#xA;    }&#xA;&#xA;    function shouldAutoCollapse() {&#xA;      &#xA;      if (codeElement) {&#xA;        const lines = codeElement.querySelectorAll(&#39;.line, .cl&#39;);&#xA;        const height = codeElement.offsetHeight;&#xA;        return lines.length &gt; autoCollapseLines || height &gt; autoCollapseHeight;&#xA;      }&#xA;&#xA;      &#xA;      const containerHeight = codeContainer.offsetHeight;&#xA;      if (containerHeight &gt; autoCollapseHeight) {&#xA;        return true;&#xA;      }&#xA;&#xA;      &#xA;      const textContent = codeContainer.textContent || codeContainer.innerText || &#39;&#39;;&#xA;      const estimatedLines = textContent.split(&#39;\n&#39;).length;&#xA;      return estimatedLines &gt; autoCollapseLines;&#xA;    }&#xA;&#xA;    function setCollapsed(collapsed, animate = true) {&#xA;      if (!collapseOverlay) return;&#xA;&#xA;      isCollapsed = collapsed;&#xA;&#xA;      if (collapsed) {&#xA;        &#xA;        codeContainer.style.maxHeight = collapsedHeight + &#39;px&#39;;&#xA;        codeContainer.style.overflow = &#39;hidden&#39;;&#xA;        collapseOverlay.style.opacity = &#39;1&#39;;&#xA;        collapseOverlay.style.pointerEvents = &#39;auto&#39;;&#xA;&#xA;        &#xA;        collapseIcon.innerHTML = `\n  \u003csvg class=\u0022h-3 w-3\u0022\n    fill=\u0022none\u0022\n    stroke=\u0022currentColor\u0022\n    viewBox=\u00220 0 24 24\u0022\u003e\u003cpath stroke-linecap=\u0022round\u0022 stroke-linejoin=\u0022round\u0022 stroke-width=\u00222\u0022 d=\u0022M19 9l-7 7-7-7\u0022 \/\u003e\n\u003c\/svg\u003e`;&#xA;        if (collapseText) {&#xA;          collapseText.textContent = &#39;展開&#39;;&#xA;        }&#xA;        collapseBtn.title = &#39;展開&#39;;&#xA;&#xA;      } else {&#xA;        &#xA;        codeContainer.style.maxHeight = &#39;&#39;;&#xA;        codeContainer.style.overflow = &#39;&#39;;&#xA;        collapseOverlay.style.opacity = &#39;0&#39;;&#xA;        collapseOverlay.style.pointerEvents = &#39;none&#39;;&#xA;&#xA;        &#xA;        collapseIcon.innerHTML = `\n  \u003csvg class=\u0022h-3 w-3\u0022\n    fill=\u0022none\u0022\n    stroke=\u0022currentColor\u0022\n    viewBox=\u00220 0 24 24\u0022\u003e\u003cpath fill=\u0022currentColor\u0022 d=\u0022M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6l-6 6z\u0022\/\u003e\u003c\/svg\u003e`;&#xA;        if (collapseText) {&#xA;          collapseText.textContent = &#39;折りたたむ&#39;;&#xA;        }&#xA;        collapseBtn.title = &#39;折りたたむ&#39;;&#xA;      }&#xA;&#xA;      &#xA;      if (animate) {&#xA;        codeContainer.style.transition = &#39;max-height 0.3s ease-out&#39;;&#xA;        setTimeout(() =&gt; {&#xA;          codeContainer.style.transition = &#39;&#39;;&#xA;        }, 300);&#xA;      }&#xA;    }&#xA;&#xA;    function toggleCollapse() {&#xA;      setCollapsed(!isCollapsed, true);&#xA;    }&#xA;&#xA;    &#xA;    collapseBtn.addEventListener(&#39;click&#39;, toggleCollapse);&#xA;&#xA;    &#xA;    if (collapseOverlay) {&#xA;      collapseOverlay.addEventListener(&#39;click&#39;, () =&gt; {&#xA;        if (isCollapsed) {&#xA;          setCollapsed(false, true);&#xA;        }&#xA;      });&#xA;    }&#xA;&#xA;    &#xA;    initCollapse();&#xA;  }&#xA;})();&#xA;&lt;/script&gt;&#xA;&lt;p&gt;&#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://mahiro.moejue.cn/static/images/visual_chara_mahiro-mihari.webp&#34;&#xA;    alt=&#34;緒山まひろ&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://mahiro.moejue.cn/static/images/visual_chara_mahiro-mihari.webp&#34;&#xA;    data-gallery-alt=&#34;緒山まひろ&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>多鏈支援，真沒你想的那麼簡單</title>
      <link>https://ja.moejue.cn/zh-hant/posts/269/</link>
      <pubDate>Sat, 05 Jul 2025 08:20:55 +0000</pubDate>
      <guid>https://ja.moejue.cn/zh-hant/posts/269/</guid>
      <description>&lt;h4 id=&#34;多鏈錢包整合的實踐反思&#34;&gt;多鏈錢包整合的實踐反思&lt;/h4&gt;&#xA;&lt;p&gt;總算是空閒下來可以整理一下程式碼了，Web3 專案接入多鏈錢包連接功能，主要涉及 Ethereum、Polygon、BSC 和 Solana。乍一聽好像只是「多做幾套相容邏輯」的事，但真正落地後才發現，很多東西其實沒想得那麼簡單。&lt;/p&gt;&#xA;&lt;div&#xA;  class=&#34;code-block-container border-border bg-card my-6 overflow-hidden rounded-xl border shadow-sm transition-all duration-200 ease-out hover:-translate-y-0.5 hover:shadow-md&#34;&gt;&#xA;  &#xA;  &lt;div&#xA;    class=&#34;code-block-header bg-muted/30 border-border flex items-center justify-between border-b px-4 py-3&#34;&gt;&#xA;    &#xA;    &lt;div class=&#34;flex items-center gap-2&#34;&gt;&#xA;      &lt;div class=&#34;text-muted-foreground flex-shrink-0&#34;&gt;&#xA;        &#xA;  &lt;svg class=&#34;h-4 w-4&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4&#34; /&gt;&#xA;&lt;/svg&gt;&#xA;      &lt;/div&gt;&#xA;      &lt;span class=&#34;text-muted-foreground text-sm font-medium&#34;&gt;&#xA;        PLAINTEXT&#xA;      &lt;/span&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;flex items-center gap-2&#34;&gt;&#xA;      &lt;button&#xA;          class=&#34;collapse-code-btn text-muted-foreground hover:text-primary hover:bg-primary/10 focus:ring-primary/20 flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium transition-all duration-200 ease-out focus:ring-2 focus:outline-none&#34;&#xA;          data-code-id=&#34;code-0&#34;&#xA;          data-default-state=&#34;expanded&#34;&#xA;          data-collapsed=&#34;false&#34;&#xA;          data-auto-collapse-lines=&#34;30&#34;&#xA;          data-auto-collapse-height=&#34;400&#34;&#xA;          data-collapsed-height=&#34;120&#34;&#xA;          title=&#34;折りたたむ&#34;&#xA;          aria-label=&#34;折りたたむ&#34;&gt;&#xA;          &lt;span class=&#34;collapse-icon&#34;&gt;&#xA;            &#xA;  &lt;svg class=&#34;h-3 w-3&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6l-6 6z&#34;/&gt;&lt;/svg&gt;&#xA;          &lt;/span&gt;&#xA;          &lt;span class=&#34;collapse-text hidden sm:inline&#34;&#xA;            &gt;折りたたむ&lt;/span&#xA;          &gt;&#xA;        &lt;/button&gt;&#xA;      &lt;button&#xA;        class=&#34;copy-code-btn text-muted-foreground hover:text-primary hover:bg-primary/10 focus:ring-primary/20 flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium transition-all duration-200 ease-out focus:ring-2 focus:outline-none&#34;&#xA;        data-code-id=&#34;code-0&#34;&#xA;        title=&#34;コピー&#34;&#xA;        aria-label=&#34;コピー&#34;&gt;&#xA;        &lt;span class=&#34;copy-icon&#34;&gt;&#xA;          &#xA;  &lt;svg class=&#34;h-3 w-3&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z&#34; /&gt;&#xA;&lt;/svg&gt;&#xA;        &lt;/span&gt;&#xA;        &lt;span class=&#34;copy-text hidden sm:inline&#34;&#xA;          &gt;コピー&lt;/span&#xA;        &gt;&#xA;      &lt;/button&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;&#xA;  &#xA;  &lt;div class=&#34;code-block-content relative&#34; id=&#34;code-0&#34;&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;this.networkConfigs = {&#xA;    ethereum: {&#xA;        chainId: &amp;#39;0x1&amp;#39;, // 1&#xA;        chainName: &amp;#39;Ethereum Mainnet&amp;#39;,&#xA;        nativeCurrency: {&#xA;            name: &amp;#39;Ethereum&amp;#39;,&#xA;            symbol: &amp;#39;ETH&amp;#39;,&#xA;            decimals: 18&#xA;        },&#xA;        rpcUrls: [&amp;#39;https://eth-mainnet.public.blastapi.io&amp;#39;],&#xA;        blockExplorerUrls: [&amp;#39;https://etherscan.io&amp;#39;]&#xA;    },&#xA;    polygon: {&#xA;        chainId: &amp;#39;0x89&amp;#39;, // 137&#xA;        chainName: &amp;#39;Polygon Mainnet&amp;#39;,&#xA;        nativeCurrency: {&#xA;            name: &amp;#39;MATIC&amp;#39;,&#xA;            symbol: &amp;#39;MATIC&amp;#39;,&#xA;            decimals: 18&#xA;        },&#xA;        rpcUrls: [&amp;#39;https://polygon-rpc.com&amp;#39;],&#xA;        blockExplorerUrls: [&amp;#39;https://polygonscan.com&amp;#39;]&#xA;    },&#xA;    bsc: {&#xA;        chainId: &amp;#39;0x38&amp;#39;, // 56&#xA;        chainName: &amp;#39;BNB Smart Chain&amp;#39;,&#xA;        nativeCurrency: {&#xA;            name: &amp;#39;BNB&amp;#39;,&#xA;            symbol: &amp;#39;BNB&amp;#39;,&#xA;            decimals: 18&#xA;        },&#xA;        rpcUrls: [&amp;#39;https://bsc-dataseed.binance.org&amp;#39;],&#xA;        blockExplorerUrls: [&amp;#39;https://bscscan.com&amp;#39;]&#xA;    }&#xA;}&lt;/code&gt;&lt;/pre&gt;&#xA;    &#xA;    &lt;div&#xA;      class=&#34;collapse-overlay to-card/90 pointer-events-none absolute inset-0 bg-gradient-to-b from-transparent via-transparent opacity-0 transition-opacity duration-300&#34;&gt;&#xA;      &lt;div&#xA;        class=&#34;text-muted-foreground bg-card/80 border-border/50 hover:bg-primary/10 hover:text-primary hover:border-primary/30 absolute bottom-4 left-1/2 -translate-x-1/2 cursor-pointer rounded-full border px-3 py-1.5 text-xs backdrop-blur-sm transition-all duration-200&#34;&gt;&#xA;        クリックして展開し、詳細を表示&#xA;      &lt;/div&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;script&gt;&#xA;(function() {&#xA;  const codeId = &#39;code-0&#39;;&#xA;  const copyBtn = document.querySelector(&#39;.copy-code-btn[data-code-id=&#34;&#39; + codeId + &#39;&#34;]&#39;);&#xA;  const collapseBtn = document.querySelector(&#39;.collapse-code-btn[data-code-id=&#34;&#39; + codeId + &#39;&#34;]&#39;);&#xA;  const codeContainer = document.getElementById(codeId);&#xA;&#xA;  if (!codeContainer) return;&#xA;&#xA;  &#xA;  if (copyBtn) {&#xA;    const copyIcon = copyBtn.querySelector(&#39;.copy-icon&#39;);&#xA;    const copyText = copyBtn.querySelector(&#39;.copy-text&#39;);&#xA;&#xA;    copyBtn.addEventListener(&#39;click&#39;, async function() {&#xA;      try {&#xA;        &#xA;        let codeText = &#39;&#39;;&#xA;&#xA;        &#xA;        const codeTableCell = codeContainer.querySelector(&#39;.lntd:last-child code&#39;);&#xA;        if (codeTableCell) {&#xA;          codeText = codeTableCell.textContent || codeTableCell.innerText;&#xA;        } else {&#xA;          &#xA;          const codeElement = codeContainer.querySelector(&#39;code&#39;);&#xA;          if (codeElement) {&#xA;            &#xA;            const hasInlineLineNumbers = codeElement.querySelector(&#39;.ln&#39;);&#xA;            if (hasInlineLineNumbers) {&#xA;              &#xA;              const codeLines = codeElement.querySelectorAll(&#39;.cl&#39;);&#xA;              if (codeLines.length &gt; 0) {&#xA;                codeText = Array.from(codeLines)&#xA;                  .map(line =&gt; {&#xA;                    const text = line.textContent || line.innerText;&#xA;                    &#xA;                    return text.replace(/\n+$/, &#39;&#39;);&#xA;                  })&#xA;                  .join(&#39;\n&#39;)&#xA;                  .replace(/\n+$/, &#39;&#39;); &#xA;              } else {&#xA;                &#xA;                const allText = codeElement.textContent || codeElement.innerText;&#xA;                codeText = allText.replace(/^\d+/gm, &#39;&#39;).replace(/^\s+/gm, &#39;&#39;);&#xA;              }&#xA;            } else {&#xA;              &#xA;              codeText = codeElement.textContent || codeElement.innerText;&#xA;            }&#xA;          } else {&#xA;            &#xA;            codeText = codeContainer.textContent || codeContainer.innerText;&#xA;          }&#xA;        }&#xA;&#xA;        &#xA;        codeText = codeText.trim();&#xA;&#xA;        &#xA;        await navigator.clipboard.writeText(codeText);&#xA;&#xA;        &#xA;        copyIcon.innerHTML = `\n  \u003csvg class=\u0022h-3 w-3\u0022\n    fill=\u0022none\u0022\n    stroke=\u0022currentColor\u0022\n    viewBox=\u00220 0 24 24\u0022\u003e\u003cpath stroke-linecap=\u0022round\u0022 stroke-linejoin=\u0022round\u0022 stroke-width=\u00222\u0022 d=\u0022M5 13l4 4L19 7\u0022 \/\u003e\n\u003c\/svg\u003e`;&#xA;        if (copyText) {&#xA;          copyText.textContent = &#39;コピーしました&#39;;&#xA;        }&#xA;        copyBtn.classList.add(&#39;text-green-600&#39;);&#xA;&#xA;        &#xA;        setTimeout(() =&gt; {&#xA;          copyIcon.innerHTML = `\n  \u003csvg class=\u0022h-3 w-3\u0022\n    fill=\u0022none\u0022\n    stroke=\u0022currentColor\u0022\n    viewBox=\u00220 0 24 24\u0022\u003e\u003cpath stroke-linecap=\u0022round\u0022 stroke-linejoin=\u0022round\u0022 stroke-width=\u00222\u0022 d=\u0022M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\u0022 \/\u003e\n\u003c\/svg\u003e`;&#xA;          if (copyText) {&#xA;            copyText.textContent = &#39;コピー&#39;;&#xA;          }&#xA;          copyBtn.classList.remove(&#39;text-green-600&#39;);&#xA;        }, 2000);&#xA;&#xA;      } catch (err) {&#xA;        console.error(&#39;复制失败:&#39;, err);&#xA;&#xA;        &#xA;        const range = document.createRange();&#xA;        const codeElement = codeContainer.querySelector(&#39;code&#39;) || codeContainer;&#xA;        range.selectNodeContents(codeElement);&#xA;        const selection = window.getSelection();&#xA;        selection.removeAllRanges();&#xA;        selection.addRange(range);&#xA;&#xA;        &#xA;        if (copyText) {&#xA;          copyText.textContent = &#39;選択済み&#39;;&#xA;        }&#xA;&#xA;        setTimeout(() =&gt; {&#xA;          if (copyText) {&#xA;            copyText.textContent = &#39;コピー&#39;;&#xA;          }&#xA;          selection.removeAllRanges();&#xA;        }, 2000);&#xA;      }&#xA;    });&#xA;  }&#xA;&#xA;  &#xA;  if (collapseBtn) {&#xA;    const collapseIcon = collapseBtn.querySelector(&#39;.collapse-icon&#39;);&#xA;    const collapseText = collapseBtn.querySelector(&#39;.collapse-text&#39;);&#xA;    const collapseOverlay = codeContainer.querySelector(&#39;.collapse-overlay&#39;);&#xA;&#xA;    &#xA;    let codeElement = codeContainer.querySelector(&#39;pre.chroma&#39;);&#xA;    if (!codeElement) {&#xA;      codeElement = codeContainer.querySelector(&#39;pre&#39;);&#xA;    }&#xA;&#xA;    const defaultState = collapseBtn.dataset.defaultState || &#39;expanded&#39;;&#xA;    const isCollapsedAttr = collapseBtn.dataset.collapsed === &#39;true&#39;;&#xA;    const autoCollapseLines = parseInt(collapseBtn.dataset.autoCollapseLines) || 30;&#xA;    const autoCollapseHeight = parseInt(collapseBtn.dataset.autoCollapseHeight) || 400;&#xA;    const collapsedHeight = parseInt(collapseBtn.dataset.collapsedHeight) || 120;&#xA;&#xA;    let isCollapsed = false;&#xA;&#xA;    &#xA;    function initCollapse() {&#xA;      &#xA;      const shouldCollapse = isCollapsedAttr ||&#xA;                           defaultState === &#39;collapsed&#39; ||&#xA;                           shouldAutoCollapse();&#xA;&#xA;      if (shouldCollapse) {&#xA;        setCollapsed(true, false); &#xA;      }&#xA;    }&#xA;&#xA;    function shouldAutoCollapse() {&#xA;      &#xA;      if (codeElement) {&#xA;        const lines = codeElement.querySelectorAll(&#39;.line, .cl&#39;);&#xA;        const height = codeElement.offsetHeight;&#xA;        return lines.length &gt; autoCollapseLines || height &gt; autoCollapseHeight;&#xA;      }&#xA;&#xA;      &#xA;      const containerHeight = codeContainer.offsetHeight;&#xA;      if (containerHeight &gt; autoCollapseHeight) {&#xA;        return true;&#xA;      }&#xA;&#xA;      &#xA;      const textContent = codeContainer.textContent || codeContainer.innerText || &#39;&#39;;&#xA;      const estimatedLines = textContent.split(&#39;\n&#39;).length;&#xA;      return estimatedLines &gt; autoCollapseLines;&#xA;    }&#xA;&#xA;    function setCollapsed(collapsed, animate = true) {&#xA;      if (!collapseOverlay) return;&#xA;&#xA;      isCollapsed = collapsed;&#xA;&#xA;      if (collapsed) {&#xA;        &#xA;        codeContainer.style.maxHeight = collapsedHeight + &#39;px&#39;;&#xA;        codeContainer.style.overflow = &#39;hidden&#39;;&#xA;        collapseOverlay.style.opacity = &#39;1&#39;;&#xA;        collapseOverlay.style.pointerEvents = &#39;auto&#39;;&#xA;&#xA;        &#xA;        collapseIcon.innerHTML = `\n  \u003csvg class=\u0022h-3 w-3\u0022\n    fill=\u0022none\u0022\n    stroke=\u0022currentColor\u0022\n    viewBox=\u00220 0 24 24\u0022\u003e\u003cpath stroke-linecap=\u0022round\u0022 stroke-linejoin=\u0022round\u0022 stroke-width=\u00222\u0022 d=\u0022M19 9l-7 7-7-7\u0022 \/\u003e\n\u003c\/svg\u003e`;&#xA;        if (collapseText) {&#xA;          collapseText.textContent = &#39;展開&#39;;&#xA;        }&#xA;        collapseBtn.title = &#39;展開&#39;;&#xA;&#xA;      } else {&#xA;        &#xA;        codeContainer.style.maxHeight = &#39;&#39;;&#xA;        codeContainer.style.overflow = &#39;&#39;;&#xA;        collapseOverlay.style.opacity = &#39;0&#39;;&#xA;        collapseOverlay.style.pointerEvents = &#39;none&#39;;&#xA;&#xA;        &#xA;        collapseIcon.innerHTML = `\n  \u003csvg class=\u0022h-3 w-3\u0022\n    fill=\u0022none\u0022\n    stroke=\u0022currentColor\u0022\n    viewBox=\u00220 0 24 24\u0022\u003e\u003cpath fill=\u0022currentColor\u0022 d=\u0022M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6l-6 6z\u0022\/\u003e\u003c\/svg\u003e`;&#xA;        if (collapseText) {&#xA;          collapseText.textContent = &#39;折りたたむ&#39;;&#xA;        }&#xA;        collapseBtn.title = &#39;折りたたむ&#39;;&#xA;      }&#xA;&#xA;      &#xA;      if (animate) {&#xA;        codeContainer.style.transition = &#39;max-height 0.3s ease-out&#39;;&#xA;        setTimeout(() =&gt; {&#xA;          codeContainer.style.transition = &#39;&#39;;&#xA;        }, 300);&#xA;      }&#xA;    }&#xA;&#xA;    function toggleCollapse() {&#xA;      setCollapsed(!isCollapsed, true);&#xA;    }&#xA;&#xA;    &#xA;    collapseBtn.addEventListener(&#39;click&#39;, toggleCollapse);&#xA;&#xA;    &#xA;    if (collapseOverlay) {&#xA;      collapseOverlay.addEventListener(&#39;click&#39;, () =&gt; {&#xA;        if (isCollapsed) {&#xA;          setCollapsed(false, true);&#xA;        }&#xA;      });&#xA;    }&#xA;&#xA;    &#xA;    initCollapse();&#xA;  }&#xA;})();&#xA;&lt;/script&gt;&#xA;&lt;h4 id=&#34;多鏈並非簡單的支援多個錢包&#34;&gt;多鏈並非簡單的「支援多個錢包」&lt;/h4&gt;&#xA;&lt;p&gt;最大的感受是：鏈不一樣，錢包互動方式也不一樣，連 SDK 的思維方式都不一樣。以太坊生態可以用統一的 Web3.js 處理很多邏輯，而到了 Solana，你會發現它完全是另一套系統：Provider 接入、連接流程、PublicKey 建構方式都不太一樣，甚至連網路延遲和穩定性都影響體驗。&lt;/p&gt;</description>
    </item>
    <item>
      <title>打造 macOS 風格的 Web 應用</title>
      <link>https://ja.moejue.cn/zh-hant/posts/260/</link>
      <pubDate>Sun, 11 May 2025 11:46:03 +0000</pubDate>
      <guid>https://ja.moejue.cn/zh-hant/posts/260/</guid>
      <description>&lt;h3 id=&#34;專案概述&#34;&gt;專案概述&lt;/h3&gt;&#xA;&lt;p&gt;在當今的 Web 開發領域，使用者體驗和介面設計變得越來越重要。&lt;/p&gt;&#xA;&lt;p&gt;Mac Web Vue Template 是一個受 macOS 設計啟發的現代優雅的 Vue.js Web 模板，它是一個基於 Vue.js 的現代化 Web 應用模板，其設計靈感來源於 macOS 的優雅介面。這個專案不僅提供了美觀的 UI 設計，還包含了完整的專案結構和最佳實踐。&lt;/p&gt;&#xA;&lt;h4 id=&#34;線上演示&#34;&gt;線上演示&lt;/h4&gt;&#xA;&lt;p&gt;你可以透過造訪 &lt;a href=&#34;https://mac.moejue.cn/&#34;target=&#34;_blank&#34;&#xA;  class=&#34;inline-flex items-center gap-1&#34;&#xA;&gt;WebAi&#xA;  &lt;svg id=&#34;external-link&#34; class=&#34;h-3 w-3 flex-shrink-0&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M15 3h6v6m-11 5L21 3m-3 10v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&#34;/&gt;&lt;/svg&gt;&#xA;&lt;/a&gt; 來體驗這個模板的實際效果。邀請碼(MoeJue)&lt;/p&gt;&#xA;&lt;h3 id=&#34;核心特色&#34;&gt;核心特色&lt;/h3&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;macOS 風格的 UI 設計&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;採用 macOS 的設計語言&lt;/li&gt;&#xA;&lt;li&gt;精緻的毛玻璃效果&lt;/li&gt;&#xA;&lt;li&gt;優雅的動畫過渡&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;現代化的技術棧&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Vue.js 作為核心框架&lt;/li&gt;&#xA;&lt;li&gt;Vite 作為建構工具&lt;/li&gt;&#xA;&lt;li&gt;Pinia 進行狀態管理&lt;/li&gt;&#xA;&lt;li&gt;Vue Router 處理路由&lt;/li&gt;&#xA;&lt;li&gt;SCSS 預處理器&lt;/li&gt;&#xA;&lt;li&gt;ES6+ 現代特性&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;響應式設計&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>萌音商城移動端版本發售</title>
      <link>https://ja.moejue.cn/zh-hant/posts/256/</link>
      <pubDate>Thu, 17 Apr 2025 09:07:11 +0000</pubDate>
      <guid>https://ja.moejue.cn/zh-hant/posts/256/</guid>
      <description>&lt;h3 id=&#34;專案介紹&#34;&gt;專案介紹&lt;/h3&gt;&#xA;&lt;p&gt;萌音商城行動版是一個基於uniapp開發的多端商城系統，支援iOS、Android、H5及小程式等多個平台。系統採用PHP + MySQL + FastAdmin作為後端技術棧，實現了完整的電商功能。&lt;/p&gt;&#xA;&lt;h3 id=&#34;核心功能&#34;&gt;核心功能&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;商品管理：分類、列表、詳情展示&lt;/li&gt;&#xA;&lt;li&gt;購物流程：購物車、結算、支付、訂單管理&lt;/li&gt;&#xA;&lt;li&gt;用戶系統：會員中心、個人資訊管理&lt;/li&gt;&#xA;&lt;li&gt;多商戶模式：商戶獨立管理商品與訂單&lt;/li&gt;&#xA;&lt;li&gt;發貨系統: 支援推送訂單至ERP(管家婆)系統處理&lt;/li&gt;&#xA;&lt;li&gt;雙模式交易：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;線上下單快遞發貨&lt;/li&gt;&#xA;&lt;li&gt;線上下單到店自取/外送員配送&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;系統預覽&#34;&gt;系統預覽&lt;/h3&gt;&#xA;&lt;h4 id=&#34;後台管理介面&#34;&gt;後台管理介面&lt;/h4&gt;&#xA;&lt;p&gt;&#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-111108_04_17_2025.jpg&#34;&#xA;    alt=&#34;&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-111108_04_17_2025.jpg&#34;&#xA;    data-gallery-alt=&#34;&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt; &#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-111207_04_17_2025.jpg&#34;&#xA;    alt=&#34;&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-111207_04_17_2025.jpg&#34;&#xA;    data-gallery-alt=&#34;&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt; &#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-111236_04_17_2025.jpg&#34;&#xA;    alt=&#34;&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-111236_04_17_2025.jpg&#34;&#xA;    data-gallery-alt=&#34;&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt; &#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-111316_04_17_2025.jpg&#34;&#xA;    alt=&#34;&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-111316_04_17_2025.jpg&#34;&#xA;    data-gallery-alt=&#34;&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt; &#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-111333_04_17_2025.jpg&#34;&#xA;    alt=&#34;&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-111333_04_17_2025.jpg&#34;&#xA;    data-gallery-alt=&#34;&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt; &#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-111348_04_17_2025.jpg&#34;&#xA;    alt=&#34;&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-111348_04_17_2025.jpg&#34;&#xA;    data-gallery-alt=&#34;&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt; &#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-111425_04_17_2025.jpg&#34;&#xA;    alt=&#34;&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-111425_04_17_2025.jpg&#34;&#xA;    data-gallery-alt=&#34;&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt; &#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-114400_04_17_2025.jpg&#34;&#xA;    alt=&#34;&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://gh-proxy.com/github.com/iAJue/MoeJueBlogPic/blob/master/Images/2025/04/17/QQ20250417-114400_04_17_2025.jpg&#34;&#xA;    data-gallery-alt=&#34;&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>一款開源簡潔高顏值的酷狗第三方客戶端V1.0.0 Beta</title>
      <link>https://ja.moejue.cn/zh-hant/posts/225/</link>
      <pubDate>Sun, 03 Nov 2024 12:48:04 +0000</pubDate>
      <guid>https://ja.moejue.cn/zh-hant/posts/225/</guid>
      <description>&lt;p&gt;&#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://github.com/iAJue/MoeKoeMusic/raw/main/images/logo.png&#34;&#xA;    alt=&#34;Logo&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://github.com/iAJue/MoeKoeMusic/raw/main/images/logo.png&#34;&#xA;    data-gallery-alt=&#34;Logo&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;moekoe-音樂&#34;&gt;MoeKoe 音樂&lt;/h2&gt;&#xA;&lt;p&gt;一款開源、簡潔、高顏值的酷狗第三方客戶端&lt;br&gt;&#xA;&lt;a href=&#34;https://github.com/iAJue/MoeKoeMusic/&#34;target=&#34;_blank&#34;&#xA;  class=&#34;inline-flex items-center gap-1&#34;&#xA;&gt;&lt;strong&gt;🌎 GitHub 儲存庫&lt;/strong&gt;&#xA;  &lt;svg id=&#34;external-link&#34; class=&#34;h-3 w-3 flex-shrink-0&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M15 3h6v6m-11 5L21 3m-3 10v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&#34;/&gt;&lt;/svg&gt;&#xA;&lt;/a&gt;  |  &lt;a href=&#34;https://github.com/iAJue/MoeKoeMusic/releases&#34;target=&#34;_blank&#34;&#xA;  class=&#34;inline-flex items-center gap-1&#34;&#xA;&gt;&lt;strong&gt;📦️ 下載安裝包&lt;/strong&gt;&#xA;  &lt;svg id=&#34;external-link&#34; class=&#34;h-3 w-3 flex-shrink-0&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M15 3h6v6m-11 5L21 3m-3 10v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&#34;/&gt;&lt;/svg&gt;&#xA;&lt;/a&gt;  |  &lt;a href=&#34;https://MoeJue.cn&#34;target=&#34;_blank&#34;&#xA;  class=&#34;inline-flex items-center gap-1&#34;&#xA;&gt;&lt;strong&gt;💬 瀏覽部落格&lt;/strong&gt;&#xA;  &lt;svg id=&#34;external-link&#34; class=&#34;h-3 w-3 flex-shrink-0&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M15 3h6v6m-11 5L21 3m-3 10v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&#34;/&gt;&lt;/svg&gt;&#xA;&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>多平台文章同步瀏覽器外掛程式 - ArticleSync</title>
      <link>https://ja.moejue.cn/zh-hant/posts/218/</link>
      <pubDate>Wed, 16 Oct 2024 11:02:49 +0000</pubDate>
      <guid>https://ja.moejue.cn/zh-hant/posts/218/</guid>
      <description>&lt;h2 id=&#34;articlesync---多平台文章同步插件&#34;&gt;ArticleSync - 多平台文章同步插件&lt;/h2&gt;&#xA;&lt;p&gt;ArticleSync 是一個瀏覽器擴充功能，幫助使用者輕鬆將文章同步發佈到多個社交平台。支援將文章從本地草稿發佈到各大平台，如知乎、Bilibili 等。它提供了一站式解決方案，讓你在不同的社交媒體平台上同步文章變得簡單高效。&lt;/p&gt;&#xA;&lt;p&gt;基於瀏覽器外掛程式模式，自動偵測本地登入帳號，杜絕帳號洩露、環境異常等風險&lt;/p&gt;&#xA;&lt;p&gt;基於 Chrome Manifest v3 瀏覽器擴充功能標準開發，請注意核心版本要求&lt;/p&gt;&#xA;&lt;h3 id=&#34;背景&#34;&gt;背景&lt;/h3&gt;&#xA;&lt;p&gt;你也知道，我這又一下子多了好幾個部落格平台，和一大堆社交網站，如果我想讓它們之間都能保持活躍的更新怎麼辦。(證明我還活著) 還能一鍵盜文章&lt;/p&gt;&#xA;&lt;p&gt;我最常更新的就是我自己的小破站了，但是其他平台，我可能就只是偶爾更新一下，但是又不想每次都去手動發佈，所以我就想，能不能寫一個外掛程式，自動偵測我本地登入的帳號，然後自動發佈呢。&lt;/p&gt;&#xA;&lt;p&gt;正所謂，自己動手豐衣足食。鼓搗了好幾天，勉強算是能用的樣子，剩下的就有空再更新了。除非你給我錢&lt;/p&gt;&#xA;&lt;p&gt;外掛程式還有很多不完善的地方，我也沒有多平台正式在生產環境中實測，如遇報錯，實屬正常，那就提交 issue 吧，或者自己改改，改好了再提交 PR 吧。嘻嘻~&lt;/p&gt;&#xA;&lt;p&gt;為了不影響我說話，截圖放最後了&lt;/p&gt;&#xA;&lt;p&gt;還有，開源不易，來個 star 吧，嘿嘿嘿~&lt;/p&gt;&#xA;&lt;p&gt;本來想加一點私貨進去的，自動關注我的社群平台&lt;/p&gt;&#xA;&lt;h3 id=&#34;功能特色&#34;&gt;功能特色&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;多平台支援&lt;/strong&gt;：支援知乎、Bilibili 等各大主流平台，支援自建開源 CMS 系統。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;狀態追蹤&lt;/strong&gt;：在外掛程式介面中查看文章的同步狀態。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;帳號管理&lt;/strong&gt;：可查看與外掛程式綁定的各平台帳號資訊。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;可擴展性強&lt;/strong&gt;：支援開發者透過轉接器模式輕鬆擴展到更多平台。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;安全可靠&lt;/strong&gt;：外掛程式基於瀏覽器擴充功能模式，確保帳號安全，避免帳號洩露等風險。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;待辦事項&#34;&gt;待辦事項&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;[ ] 獨立文章編輯器&lt;/li&gt;&#xA;&lt;li&gt;[ ] 圖片一鍵同步&lt;/li&gt;&#xA;&lt;li&gt;[x] Markdown 與 HTML 互轉&lt;/li&gt;&#xA;&lt;li&gt;[ ] 第三方圖床系統&lt;/li&gt;&#xA;&lt;li&gt;[ ] 多帳號管理&lt;/li&gt;&#xA;&lt;li&gt;[ ] 多系統客戶端版本&lt;/li&gt;&#xA;&lt;li&gt;[ ] 一鍵 AI 總結&lt;/li&gt;&#xA;&lt;li&gt;[ ] 影片同步&lt;/li&gt;&#xA;&lt;li&gt;[ ] 標籤、分類的支援&lt;/li&gt;&#xA;&lt;li&gt;[ ] 更友善的錯誤處理&lt;/li&gt;&#xA;&lt;li&gt;[ ] 更多平台的接入&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;支援管道&#34;&gt;支援管道&lt;/h3&gt;&#xA;&lt;p&gt;媒體&lt;/p&gt;</description>
    </item>
    <item>
      <title>打贏復活賽,我活過來了</title>
      <link>https://ja.moejue.cn/zh-hant/posts/206/</link>
      <pubDate>Thu, 03 Oct 2024 02:48:02 +0000</pubDate>
      <guid>https://ja.moejue.cn/zh-hant/posts/206/</guid>
      <description>&lt;h3 id=&#34;近況&#34;&gt;近況&lt;/h3&gt;&#xA;&lt;p&gt;距離上次的閒言碎語已經兩個多月沒說話了，是時候整理整理我的思緒了。上回是發表了一堆感慨，之後就沒聲了，所以是有一點倉促了，後續的活就跟不上了。&lt;/p&gt;&#xA;&lt;p&gt;又是一年國慶，果然我的國慶堪比雙十一。之前旗下的三個域名(52ecy.cn,moeins.cn,moeins.com)，雖然復活賽是打贏了，但是裝備沒有了。&lt;/p&gt;&#xA;&lt;p&gt;所以只能新購域名了，同時又把部落格給恢復了，可能還是想自己折騰吧，掛靠在部落格園還是不大舒服的樣子。&lt;/p&gt;&#xA;&lt;p&gt;之前一直在用的emlog，這會也來場大動作，直接給系統換到了WordPress，同時也換上了新主題。這款主題是由&lt;a href=&#34;https://www.boxmoe.com/&#34;target=&#34;_blank&#34;&#xA;  class=&#34;inline-flex items-center gap-1&#34;&#xA;&gt;專收爆米花&#xA;  &lt;svg id=&#34;external-link&#34; class=&#34;h-3 w-3 flex-shrink-0&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M15 3h6v6m-11 5L21 3m-3 10v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&#34;/&gt;&lt;/svg&gt;&#xA;&lt;/a&gt;在21年前後的時候就發布了，那會我在loc看到的時候我就star了，一直在我GitHub裡躺了4年&lt;a href=&#34;https://github.com/iAJue/lolimeow&#34;target=&#34;_blank&#34;&#xA;  class=&#34;inline-flex items-center gap-1&#34;&#xA;&gt;lolimeow&#xA;  &lt;svg id=&#34;external-link&#34; class=&#34;h-3 w-3 flex-shrink-0&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M15 3h6v6m-11 5L21 3m-3 10v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&#34;/&gt;&lt;/svg&gt;&#xA;&lt;/a&gt;，因為那會一直在用emlog，所以就不願意折騰。乘此契機，乾脆一戰到底，所以也是花了一點大功夫，包括數據的遷移，和主題的適配。&lt;/p&gt;&#xA;&lt;p&gt;雖然作者一直以來都在積極推進主題的更新，也正因此我在使用中沒有遇到太大的麻煩，但也有缺少我之前系統的設定，所以我也一併把這個主題做了更新，並且pull到了原倉庫。上面放的GitHub的連結是我更新後的。&#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://image.baidu.com/search/down?url=https://tva1.sinaimg.cn/large/78350c19ly8hu90no7xm8j20w414kdue.jpg&#34;&#xA;    alt=&#34;&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://image.baidu.com/search/down?url=https://tva1.sinaimg.cn/large/78350c19ly8hu90no7xm8j20w414kdue.jpg&#34;&#xA;    data-gallery-alt=&#34;&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt; 最麻煩的數據遷移部分，我在網上兜兜轉轉了半天，只找到exe版本的，先不說exe版本給人一種莫名的不放心的感覺，況且我現在用的還是Mac的系統，根本就不能直接運行。所謂自己動手豐衣足食，所以我只能花點時間給自己寫一個遷移腳本了。GitHub的地址我放在了文章的底部了，可以完美遷移emlog的數據到WordPress系統中。不愧是我！&#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://image.baidu.com/search/down?url=https://tva1.sinaimg.cn/large/78350c19ly8hu86bsccndj21hc0u0gr1.jpg&#34;&#xA;    alt=&#34;&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://image.baidu.com/search/down?url=https://tva1.sinaimg.cn/large/78350c19ly8hu86bsccndj21hc0u0gr1.jpg&#34;&#xA;    data-gallery-alt=&#34;&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>重生之我要做商城 - 萌音商城V1.0上線</title>
      <link>https://ja.moejue.cn/zh-hant/posts/203/</link>
      <pubDate>Wed, 02 Oct 2024 06:17:50 +0000</pubDate>
      <guid>https://ja.moejue.cn/zh-hant/posts/203/</guid>
      <description>&lt;p&gt;萌音系列的第N+1個項目來了呢。這其實又是一個鴿了好幾年的項目了，這回總算能把坑填上了。&lt;/p&gt;&#xA;&lt;h5 id=&#34;先上項目地址-httpsmoekoecn&#34;&gt;先上項目地址: &lt;a href=&#34;https://MoeKoe.cn&#34;target=&#34;_blank&#34;&#xA;  class=&#34;inline-flex items-center gap-1&#34;&#xA;&gt;https://MoeKoe.cn&#xA;  &lt;svg id=&#34;external-link&#34; class=&#34;h-3 w-3 flex-shrink-0&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M15 3h6v6m-11 5L21 3m-3 10v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&#34;/&gt;&lt;/svg&gt;&#xA;&lt;/a&gt;&lt;/h5&gt;&#xA;&lt;h4 id=&#34;我為什麼一直在做項目&#34;&gt;我為什麼一直在做項目?&lt;/h4&gt;&#xA;&lt;p&gt;這個問題就很有意思了，為什麼一直在做各種項目，而且還是不掙錢的東西。接觸過我之前一些項目的小夥伴都知道我開發什麼項目都是本著自己用的原則在創作的，當然這次也不例外。一直我就想在某個平台賣賣我那些留著沒用，丟了捨不得的小玩意，同時分享好玩有趣的產品給大家（未來可能還有我自己的IP產品）。「想把最美好的事和物分享給最美好的你」就算，本著「別人有我也要有」的原則，我也得有。&lt;/p&gt;&#xA;&lt;p&gt;如果說我只是想賣點小玩意，我為何不直接上淘寶、閒魚這樣的平台呢？退一萬步講，就算不上這些平台，我也可以自己搭建一個現成的商城系統啊，現在市面上成熟好用的系統一抓一大把喔，為何還要自己開發呢？&lt;/p&gt;&#xA;&lt;p&gt;一句話，因為我喜歡，這是我的愛好，我喜歡創造，喜歡做點有意思的東西和事。我想成為一個厲害而又有趣的人，而不是一個只會活著的普通人，我想認識更多有趣的靈魂。厲害對於我的定義並不是說他技術有多厲害、賺錢有多厲害，而是一個人面對挫折和困苦的時候，依然能笑著面對，依然能保持樂觀、不忘初心，這樣的人，我願稱他為厲害的人。&lt;/p&gt;&#xA;&lt;p&gt;明明是一個發布新項目的文章，又給我寫成大白話部落格了，哈哈哈。&lt;/p&gt;&#xA;&lt;h3 id=&#34;項目介紹&#34;&gt;項目介紹&lt;/h3&gt;&#xA;&lt;p&gt;最近在搞什麼？在搞粉嫩粉嫩的東西，一個二次元少女萌系風格的魔法少女商城。&lt;/p&gt;&#xA;&#xA;  &lt;blockquote&#xA;    class=&#34;border-primary bg-muted/30 hover:bg-muted/40 my-6 rounded-r-lg border-l-4 py-4 pr-4 pl-6 italic transition-all duration-200 ease-out hover:translate-x-1&#34;&gt;&#xA;    &lt;div class=&#34;prose prose-sm max-w-none&#34;&gt;&#xA;      &lt;p&gt;歡迎來到魔法少女的奇幻世界，開啟探索二次元無限可能的冒險之旅！這是專為二次元愛好者量身打造的線上商城。在這裡，你能找到最閃耀的魔法道具、最萌系的變身服飾，還有豐富多樣的二次元周邊商品。無論你鍾情於可愛的萌物，還是酷炫的收藏品，這裡都能滿足你對二次元的所有熱愛與期待！&lt;/p&gt;&#xA;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script&gt;&#xA;function toggleAlert(alertId) {&#xA;  const content = document.getElementById(alertId + &#39;-content&#39;);&#xA;  const chevron = document.getElementById(alertId + &#39;-chevron&#39;);&#xA;  const header = content.previousElementSibling;&#xA;  &#xA;  if (content.classList.contains(&#39;hidden&#39;)) {&#xA;    content.classList.remove(&#39;hidden&#39;);&#xA;    chevron.style.transform = &#39;rotate(0deg)&#39;;&#xA;    header.setAttribute(&#39;aria-expanded&#39;, &#39;true&#39;);&#xA;  } else {&#xA;    content.classList.add(&#39;hidden&#39;);&#xA;    chevron.style.transform = &#39;rotate(-90deg)&#39;;&#xA;    header.setAttribute(&#39;aria-expanded&#39;, &#39;false&#39;);&#xA;  }&#xA;}&#xA;&#xA;&#xA;document.addEventListener(&#39;DOMContentLoaded&#39;, function() {&#xA;  const collapsedAlerts = document.querySelectorAll(&#39;.alert-content.hidden&#39;);&#xA;  collapsedAlerts.forEach(function(content) {&#xA;    const alertId = content.id.replace(&#39;-content&#39;, &#39;&#39;);&#xA;    const chevron = document.getElementById(alertId + &#39;-chevron&#39;);&#xA;    if (chevron) {&#xA;      chevron.style.transform = &#39;rotate(-90deg)&#39;;&#xA;    }&#xA;  });&#xA;});&#xA;&lt;/script&gt;&#xA;&#xA;&lt;p&gt;點開網址你會發現一個讓你眼前一亮（粉）的感覺，沒錯，這就是我想要的風格，粉嫩粉嫩的，少女心爆棚，卡哇伊的感覺，這樣才能突出我的個性。&#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://image.baidu.com/search/down?url=https://tva1.sinaimg.cn/large/78350c19ly8hu80pi6l1vj22bm1g2kjp.jpg&#34;&#xA;    alt=&#34;&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://image.baidu.com/search/down?url=https://tva1.sinaimg.cn/large/78350c19ly8hu80pi6l1vj22bm1g2kjp.jpg&#34;&#xA;    data-gallery-alt=&#34;&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>基於Web3.0的區塊鏈圖片上傳</title>
      <link>https://ja.moejue.cn/zh-hant/posts/201/</link>
      <pubDate>Mon, 17 Jun 2024 12:57:43 +0000</pubDate>
      <guid>https://ja.moejue.cn/zh-hant/posts/201/</guid>
      <description>&lt;p&gt;在開始之前，我們先簡單了解一下基本的概念，我大致歸納為以下幾個點 &lt;a href=&#34;https://www.cnblogs.com/Ajue/p/18252827&#34;target=&#34;_blank&#34;&#xA;  class=&#34;inline-flex items-center gap-1&#34;&#xA;&gt;什麼是Web3.0，和區塊鏈又有什麼關係？&#xA;  &lt;svg id=&#34;external-link&#34; class=&#34;h-3 w-3 flex-shrink-0&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M15 3h6v6m-11 5L21 3m-3 10v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&#34;/&gt;&lt;/svg&gt;&#xA;&lt;/a&gt;（上回的文章不就派上用場了）&lt;/p&gt;&#xA;&lt;p&gt;需求：開發一個基於Python的Web 3.0圖片上傳系統。這個系統將允許用戶上傳圖片，並將圖片儲存在去中心化的網路上，同時記錄交易資訊在區塊鏈上。 本來只是寫著玩的，想過要寫成用戶認證、檔案操作集成全套管理的，讓他「終將成為圖片上傳服務的最終解決方案」。實際下來卻發現不是很實際，就作罷了，奈何我一直以來對圖片這麼執著。&lt;/p&gt;&#xA;&lt;h3 id=&#34;步驟概述&#34;&gt;步驟概述&lt;/h3&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;環境設定&lt;/strong&gt;：使用Python開發，安裝必要的Python函式庫。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;IPFS整合&lt;/strong&gt;：將圖片上傳到IPFS，取得圖片的CID（內容識別碼）。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;區塊鏈整合&lt;/strong&gt;：將IPFS CID記錄在區塊鏈上。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Web介面&lt;/strong&gt;：使用Flask建立一個Web介面，允許用戶上傳圖片。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;詳細步驟&#34;&gt;詳細步驟&lt;/h3&gt;&#xA;&lt;h4 id=&#34;1-環境設定&#34;&gt;1. 環境設定&lt;/h4&gt;&#xA;&lt;p&gt;安裝所需的Python函式庫：&lt;/p&gt;&#xA;&lt;div&#xA;  class=&#34;code-block-container border-border bg-card my-6 overflow-hidden rounded-xl border shadow-sm transition-all duration-200 ease-out hover:-translate-y-0.5 hover:shadow-md&#34;&gt;&#xA;  &#xA;  &lt;div&#xA;    class=&#34;code-block-header bg-muted/30 border-border flex items-center justify-between border-b px-4 py-3&#34;&gt;&#xA;    &#xA;    &lt;div class=&#34;flex items-center gap-2&#34;&gt;&#xA;      &lt;div class=&#34;text-muted-foreground flex-shrink-0&#34;&gt;&#xA;        &#xA;  &lt;svg class=&#34;h-4 w-4&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4&#34; /&gt;&#xA;&lt;/svg&gt;&#xA;      &lt;/div&gt;&#xA;      &lt;span class=&#34;text-muted-foreground text-sm font-medium&#34;&gt;&#xA;        PLAINTEXT&#xA;      &lt;/span&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;flex items-center gap-2&#34;&gt;&#xA;      &lt;button&#xA;          class=&#34;collapse-code-btn text-muted-foreground hover:text-primary hover:bg-primary/10 focus:ring-primary/20 flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium transition-all duration-200 ease-out focus:ring-2 focus:outline-none&#34;&#xA;          data-code-id=&#34;code-0&#34;&#xA;          data-default-state=&#34;expanded&#34;&#xA;          data-collapsed=&#34;false&#34;&#xA;          data-auto-collapse-lines=&#34;30&#34;&#xA;          data-auto-collapse-height=&#34;400&#34;&#xA;          data-collapsed-height=&#34;120&#34;&#xA;          title=&#34;折りたたむ&#34;&#xA;          aria-label=&#34;折りたたむ&#34;&gt;&#xA;          &lt;span class=&#34;collapse-icon&#34;&gt;&#xA;            &#xA;  &lt;svg class=&#34;h-3 w-3&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6l-6 6z&#34;/&gt;&lt;/svg&gt;&#xA;          &lt;/span&gt;&#xA;          &lt;span class=&#34;collapse-text hidden sm:inline&#34;&#xA;            &gt;折りたたむ&lt;/span&#xA;          &gt;&#xA;        &lt;/button&gt;&#xA;      &lt;button&#xA;        class=&#34;copy-code-btn text-muted-foreground hover:text-primary hover:bg-primary/10 focus:ring-primary/20 flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium transition-all duration-200 ease-out focus:ring-2 focus:outline-none&#34;&#xA;        data-code-id=&#34;code-0&#34;&#xA;        title=&#34;コピー&#34;&#xA;        aria-label=&#34;コピー&#34;&gt;&#xA;        &lt;span class=&#34;copy-icon&#34;&gt;&#xA;          &#xA;  &lt;svg class=&#34;h-3 w-3&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z&#34; /&gt;&#xA;&lt;/svg&gt;&#xA;        &lt;/span&gt;&#xA;        &lt;span class=&#34;copy-text hidden sm:inline&#34;&#xA;          &gt;コピー&lt;/span&#xA;        &gt;&#xA;      &lt;/button&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;&#xA;  &#xA;  &lt;div class=&#34;code-block-content relative&#34; id=&#34;code-0&#34;&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;pip install flask web3 ipfshttpclient&lt;/code&gt;&lt;/pre&gt;&#xA;    &#xA;    &lt;div&#xA;      class=&#34;collapse-overlay to-card/90 pointer-events-none absolute inset-0 bg-gradient-to-b from-transparent via-transparent opacity-0 transition-opacity duration-300&#34;&gt;&#xA;      &lt;div&#xA;        class=&#34;text-muted-foreground bg-card/80 border-border/50 hover:bg-primary/10 hover:text-primary hover:border-primary/30 absolute bottom-4 left-1/2 -translate-x-1/2 cursor-pointer rounded-full border px-3 py-1.5 text-xs backdrop-blur-sm transition-all duration-200&#34;&gt;&#xA;        クリックして展開し、詳細を表示&#xA;      &lt;/div&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;script&gt;&#xA;(function() {&#xA;  const codeId = &#39;code-0&#39;;&#xA;  const copyBtn = document.querySelector(&#39;.copy-code-btn[data-code-id=&#34;&#39; + codeId + &#39;&#34;]&#39;);&#xA;  const collapseBtn = document.querySelector(&#39;.collapse-code-btn[data-code-id=&#34;&#39; + codeId + &#39;&#34;]&#39;);&#xA;  const codeContainer = document.getElementById(codeId);&#xA;&#xA;  if (!codeContainer) return;&#xA;&#xA;  &#xA;  if (copyBtn) {&#xA;    const copyIcon = copyBtn.querySelector(&#39;.copy-icon&#39;);&#xA;    const copyText = copyBtn.querySelector(&#39;.copy-text&#39;);&#xA;&#xA;    copyBtn.addEventListener(&#39;click&#39;, async function() {&#xA;      try {&#xA;        &#xA;        let codeText = &#39;&#39;;&#xA;&#xA;        &#xA;        const codeTableCell = codeContainer.querySelector(&#39;.lntd:last-child code&#39;);&#xA;        if (codeTableCell) {&#xA;          codeText = codeTableCell.textContent || codeTableCell.innerText;&#xA;        } else {&#xA;          &#xA;          const codeElement = codeContainer.querySelector(&#39;code&#39;);&#xA;          if (codeElement) {&#xA;            &#xA;            const hasInlineLineNumbers = codeElement.querySelector(&#39;.ln&#39;);&#xA;            if (hasInlineLineNumbers) {&#xA;              &#xA;              const codeLines = codeElement.querySelectorAll(&#39;.cl&#39;);&#xA;              if (codeLines.length &gt; 0) {&#xA;                codeText = Array.from(codeLines)&#xA;                  .map(line =&gt; {&#xA;                    const text = line.textContent || line.innerText;&#xA;                    &#xA;                    return text.replace(/\n+$/, &#39;&#39;);&#xA;                  })&#xA;                  .join(&#39;\n&#39;)&#xA;                  .replace(/\n+$/, &#39;&#39;); &#xA;              } else {&#xA;                &#xA;                const allText = codeElement.textContent || codeElement.innerText;&#xA;                codeText = allText.replace(/^\d+/gm, &#39;&#39;).replace(/^\s+/gm, &#39;&#39;);&#xA;              }&#xA;            } else {&#xA;              &#xA;              codeText = codeElement.textContent || codeElement.innerText;&#xA;            }&#xA;          } else {&#xA;            &#xA;            codeText = codeContainer.textContent || codeContainer.innerText;&#xA;          }&#xA;        }&#xA;&#xA;        &#xA;        codeText = codeText.trim();&#xA;&#xA;        &#xA;        await navigator.clipboard.writeText(codeText);&#xA;&#xA;        &#xA;        copyIcon.innerHTML = `\n  \u003csvg class=\u0022h-3 w-3\u0022\n    fill=\u0022none\u0022\n    stroke=\u0022currentColor\u0022\n    viewBox=\u00220 0 24 24\u0022\u003e\u003cpath stroke-linecap=\u0022round\u0022 stroke-linejoin=\u0022round\u0022 stroke-width=\u00222\u0022 d=\u0022M5 13l4 4L19 7\u0022 \/\u003e\n\u003c\/svg\u003e`;&#xA;        if (copyText) {&#xA;          copyText.textContent = &#39;コピーしました&#39;;&#xA;        }&#xA;        copyBtn.classList.add(&#39;text-green-600&#39;);&#xA;&#xA;        &#xA;        setTimeout(() =&gt; {&#xA;          copyIcon.innerHTML = `\n  \u003csvg class=\u0022h-3 w-3\u0022\n    fill=\u0022none\u0022\n    stroke=\u0022currentColor\u0022\n    viewBox=\u00220 0 24 24\u0022\u003e\u003cpath stroke-linecap=\u0022round\u0022 stroke-linejoin=\u0022round\u0022 stroke-width=\u00222\u0022 d=\u0022M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\u0022 \/\u003e\n\u003c\/svg\u003e`;&#xA;          if (copyText) {&#xA;            copyText.textContent = &#39;コピー&#39;;&#xA;          }&#xA;          copyBtn.classList.remove(&#39;text-green-600&#39;);&#xA;        }, 2000);&#xA;&#xA;      } catch (err) {&#xA;        console.error(&#39;复制失败:&#39;, err);&#xA;&#xA;        &#xA;        const range = document.createRange();&#xA;        const codeElement = codeContainer.querySelector(&#39;code&#39;) || codeContainer;&#xA;        range.selectNodeContents(codeElement);&#xA;        const selection = window.getSelection();&#xA;        selection.removeAllRanges();&#xA;        selection.addRange(range);&#xA;&#xA;        &#xA;        if (copyText) {&#xA;          copyText.textContent = &#39;選択済み&#39;;&#xA;        }&#xA;&#xA;        setTimeout(() =&gt; {&#xA;          if (copyText) {&#xA;            copyText.textContent = &#39;コピー&#39;;&#xA;          }&#xA;          selection.removeAllRanges();&#xA;        }, 2000);&#xA;      }&#xA;    });&#xA;  }&#xA;&#xA;  &#xA;  if (collapseBtn) {&#xA;    const collapseIcon = collapseBtn.querySelector(&#39;.collapse-icon&#39;);&#xA;    const collapseText = collapseBtn.querySelector(&#39;.collapse-text&#39;);&#xA;    const collapseOverlay = codeContainer.querySelector(&#39;.collapse-overlay&#39;);&#xA;&#xA;    &#xA;    let codeElement = codeContainer.querySelector(&#39;pre.chroma&#39;);&#xA;    if (!codeElement) {&#xA;      codeElement = codeContainer.querySelector(&#39;pre&#39;);&#xA;    }&#xA;&#xA;    const defaultState = collapseBtn.dataset.defaultState || &#39;expanded&#39;;&#xA;    const isCollapsedAttr = collapseBtn.dataset.collapsed === &#39;true&#39;;&#xA;    const autoCollapseLines = parseInt(collapseBtn.dataset.autoCollapseLines) || 30;&#xA;    const autoCollapseHeight = parseInt(collapseBtn.dataset.autoCollapseHeight) || 400;&#xA;    const collapsedHeight = parseInt(collapseBtn.dataset.collapsedHeight) || 120;&#xA;&#xA;    let isCollapsed = false;&#xA;&#xA;    &#xA;    function initCollapse() {&#xA;      &#xA;      const shouldCollapse = isCollapsedAttr ||&#xA;                           defaultState === &#39;collapsed&#39; ||&#xA;                           shouldAutoCollapse();&#xA;&#xA;      if (shouldCollapse) {&#xA;        setCollapsed(true, false); &#xA;      }&#xA;    }&#xA;&#xA;    function shouldAutoCollapse() {&#xA;      &#xA;      if (codeElement) {&#xA;        const lines = codeElement.querySelectorAll(&#39;.line, .cl&#39;);&#xA;        const height = codeElement.offsetHeight;&#xA;        return lines.length &gt; autoCollapseLines || height &gt; autoCollapseHeight;&#xA;      }&#xA;&#xA;      &#xA;      const containerHeight = codeContainer.offsetHeight;&#xA;      if (containerHeight &gt; autoCollapseHeight) {&#xA;        return true;&#xA;      }&#xA;&#xA;      &#xA;      const textContent = codeContainer.textContent || codeContainer.innerText || &#39;&#39;;&#xA;      const estimatedLines = textContent.split(&#39;\n&#39;).length;&#xA;      return estimatedLines &gt; autoCollapseLines;&#xA;    }&#xA;&#xA;    function setCollapsed(collapsed, animate = true) {&#xA;      if (!collapseOverlay) return;&#xA;&#xA;      isCollapsed = collapsed;&#xA;&#xA;      if (collapsed) {&#xA;        &#xA;        codeContainer.style.maxHeight = collapsedHeight + &#39;px&#39;;&#xA;        codeContainer.style.overflow = &#39;hidden&#39;;&#xA;        collapseOverlay.style.opacity = &#39;1&#39;;&#xA;        collapseOverlay.style.pointerEvents = &#39;auto&#39;;&#xA;&#xA;        &#xA;        collapseIcon.innerHTML = `\n  \u003csvg class=\u0022h-3 w-3\u0022\n    fill=\u0022none\u0022\n    stroke=\u0022currentColor\u0022\n    viewBox=\u00220 0 24 24\u0022\u003e\u003cpath stroke-linecap=\u0022round\u0022 stroke-linejoin=\u0022round\u0022 stroke-width=\u00222\u0022 d=\u0022M19 9l-7 7-7-7\u0022 \/\u003e\n\u003c\/svg\u003e`;&#xA;        if (collapseText) {&#xA;          collapseText.textContent = &#39;展開&#39;;&#xA;        }&#xA;        collapseBtn.title = &#39;展開&#39;;&#xA;&#xA;      } else {&#xA;        &#xA;        codeContainer.style.maxHeight = &#39;&#39;;&#xA;        codeContainer.style.overflow = &#39;&#39;;&#xA;        collapseOverlay.style.opacity = &#39;0&#39;;&#xA;        collapseOverlay.style.pointerEvents = &#39;none&#39;;&#xA;&#xA;        &#xA;        collapseIcon.innerHTML = `\n  \u003csvg class=\u0022h-3 w-3\u0022\n    fill=\u0022none\u0022\n    stroke=\u0022currentColor\u0022\n    viewBox=\u00220 0 24 24\u0022\u003e\u003cpath fill=\u0022currentColor\u0022 d=\u0022M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6l-6 6z\u0022\/\u003e\u003c\/svg\u003e`;&#xA;        if (collapseText) {&#xA;          collapseText.textContent = &#39;折りたたむ&#39;;&#xA;        }&#xA;        collapseBtn.title = &#39;折りたたむ&#39;;&#xA;      }&#xA;&#xA;      &#xA;      if (animate) {&#xA;        codeContainer.style.transition = &#39;max-height 0.3s ease-out&#39;;&#xA;        setTimeout(() =&gt; {&#xA;          codeContainer.style.transition = &#39;&#39;;&#xA;        }, 300);&#xA;      }&#xA;    }&#xA;&#xA;    function toggleCollapse() {&#xA;      setCollapsed(!isCollapsed, true);&#xA;    }&#xA;&#xA;    &#xA;    collapseBtn.addEventListener(&#39;click&#39;, toggleCollapse);&#xA;&#xA;    &#xA;    if (collapseOverlay) {&#xA;      collapseOverlay.addEventListener(&#39;click&#39;, () =&gt; {&#xA;        if (isCollapsed) {&#xA;          setCollapsed(false, true);&#xA;        }&#xA;      });&#xA;    }&#xA;&#xA;    &#xA;    initCollapse();&#xA;  }&#xA;})();&#xA;&lt;/script&gt;&#xA;&lt;h4 id=&#34;2-ipfs整合&#34;&gt;2. IPFS整合&lt;/h4&gt;&#xA;&lt;p&gt;IPFS（星際檔案系統）是一種點對點的檔案儲存協定。我們可以使用&lt;code&gt;ipfshttpclient&lt;/code&gt;函式庫來與IPFS網路互動。&lt;/p&gt;</description>
    </item>
    <item>
      <title>使用ESP8266-NodeMCU開發板顯示一下我的QQ頭像</title>
      <link>https://ja.moejue.cn/zh-hant/posts/200/</link>
      <pubDate>Sun, 16 Jun 2024 12:50:42 +0000</pubDate>
      <guid>https://ja.moejue.cn/zh-hant/posts/200/</guid>
      <description>&lt;p&gt;诶，說好的自己寫esp8266的開發板韌體的我回來了。 20年說好的，今天回來還願了 &lt;a href=&#34;https://www.cnblogs.com/Ajue/p/18202561&#34;target=&#34;_blank&#34;&#xA;  class=&#34;inline-flex items-center gap-1&#34;&#xA;&gt;ESP8266串口WiFi模塊 - WiFi殺手&#xA;  &lt;svg id=&#34;external-link&#34; class=&#34;h-3 w-3 flex-shrink-0&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M15 3h6v6m-11 5L21 3m-3 10v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&#34;/&gt;&lt;/svg&gt;&#xA;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;今天我們把OLED顯示屏也接上，我此次買的是4腳的OLED(128*64)，不支持彩色顯示的。&lt;/p&gt;&#xA;&lt;h3 id=&#34;nodemcu開發板&#34;&gt;NodeMCU開發板&lt;/h3&gt;&#xA;&lt;p&gt;NodeMCU是一個開源的IoT物聯網硬體開發板，由於它支持WIFI功能且使用方法十分類似Arduino開發板，所以近些年獲得了越來越多來自全世界的創客朋友們的青睞。NodeMCU尺寸與Arduino Nano類似。它並不是Arduino團隊開發的，但是我們也可以使用Arduino IDE 對它進行開發。&lt;/p&gt;&#xA;&lt;p&gt;作為萬物互聯的IoT基礎，首先物聯網控制板的成本不能過高。高昂的物聯網控制元件不利於項目成本控制也不利於廣大創客愛好者學習和使用它。在這一點上，NodeMCU比樹莓派以及Arduino家族的IoT平台等更具優勢。&lt;/p&gt;&#xA;&lt;p&gt;細心的朋友可能已經發現了，這會我開發板叫做ESP8266-NodeMCU。然而在其它的網站或資料中，有時是用ESP8266有時是用NodeMCU。那麼ESP8266和NodeMCU之間是什麼關係呢？&lt;/p&gt;&#xA;&lt;p&gt;ESP8266是一塊晶片（被鐵殼子包住的方形的東西），而NodeMCU則是以ESP8266晶片為核心的開發板，如下圖所示。 &#xA;&lt;figure class=&#34;image-figure not-prose my-8&#34; &#xA;        data-lightbox-enabled=&#34;false&#34;&#xA;        data-gallery-type=&#34;auto&#34;&gt;&#xA;  &lt;div class=&#34;image-container&#34;&gt;&#xA;    &lt;img&#xA;    src=&#34;https://image.baidu.com/search/down?url=https://tva1.sinaimg.cn/large/78350c19ly8hu4vo3lpljj21hc140n7w.jpg&#34;&#xA;    alt=&#34;&#34;&#xA;    &#xA;    &#xA;    loading=&#34;lazy&#34;&#xA;    decoding=&#34;async&#34;&#xA;    data-gallery-src=&#34;https://image.baidu.com/search/down?url=https://tva1.sinaimg.cn/large/78350c19ly8hu4vo3lpljj21hc140n7w.jpg&#34;&#xA;    data-gallery-alt=&#34;&#34;&#xA;    data-gallery-title=&#34;&#34; /&gt;&lt;/div&gt;&#xA;&#xA;  &lt;/figure&gt; 要想對ESP8266晶片來進行實驗操作是很困難的，因為我們很難將小小一枚晶片上的引腳與我們的電腦連接起來然後再進行上傳程式等操作。於是便誕生了圍繞ESP8266晶片的各種開發板。NodeMCU就是這些開發板中的一員。&lt;/p&gt;&#xA;&lt;p&gt;NodeMCU開發板上的兩排插針與ESP8266晶片的引腳相連。有了開發板上的兩排插針，我們就可以很輕鬆的使用杜邦線將晶片的引腳接到實驗電路中。NodeMCU開發板上還配有USB接口以及電壓轉換電路。這些為我們提供了很大的便利。我們只要用一根USB數據線就可以輕鬆的實現為ESP8266供電以及上傳程式的操作。當然，NodeMCU開發板上的電路功能還不止這些，我就不再繼續延伸下去了&lt;/p&gt;&#xA;&lt;h3 id=&#34;驅動的安裝&#34;&gt;驅動的安裝&lt;/h3&gt;&#xA;&lt;p&gt;雖然之前已經講過開發板的驅動安裝了，但是不夠詳細。&lt;/p&gt;&#xA;&lt;p&gt;目前市面上的ESP8266驅動有多種，即使是同一塊NodeMcu的開發板驅動也可能是不一樣的。目前主流的是CH340和CP210X的驅動&lt;/p&gt;&#xA;&lt;p&gt;驅動的下載請直接去晶片的製造商的官網下載即可 CP210X：&lt;a href=&#34;https://cn.silabs.com/developers/usb-to-uart-bridge-vcp-drivers&#34;target=&#34;_blank&#34;&#xA;  class=&#34;inline-flex items-center gap-1&#34;&#xA;&gt;https://cn.silabs.com/developers/usb-to-uart-bridge-vcp-drivers&#xA;  &lt;svg id=&#34;external-link&#34; class=&#34;h-3 w-3 flex-shrink-0&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M15 3h6v6m-11 5L21 3m-3 10v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&#34;/&gt;&lt;/svg&gt;&#xA;&lt;/a&gt; CH340C：&lt;a href=&#34;https://www.wch.cn/downloads/CH341SER_EXE.html&#34;target=&#34;_blank&#34;&#xA;  class=&#34;inline-flex items-center gap-1&#34;&#xA;&gt;https://www.wch.cn/downloads/CH341SER_EXE.html&#xA;  &lt;svg id=&#34;external-link&#34; class=&#34;h-3 w-3 flex-shrink-0&#34;&#xA;    fill=&#34;none&#34;&#xA;    stroke=&#34;currentColor&#34;&#xA;    viewBox=&#34;0 0 24 24&#34;&gt;&lt;path fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; stroke-width=&#34;2&#34; d=&#34;M15 3h6v6m-11 5L21 3m-3 10v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&#34;/&gt;&lt;/svg&gt;&#xA;&lt;/a&gt;&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
