<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>JavaScript on MoeJueのブログ</title>
    <link>https://ja.moejue.cn/zh-hant/tags/javascript/</link>
    <description>Recent content in JavaScript on MoeJueのブログ</description>
    <generator>Hugo</generator>
    <language>zh-hant</language>
    <lastBuildDate>Sat, 23 Aug 2025 12:34:26 +0000</lastBuildDate>
    <atom:link href="https://ja.moejue.cn/zh-hant/tags/javascript/index.xml" rel="self" type="application/rss+xml" />
    <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>多平台文章同步瀏覽器外掛程式 - 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>
  </channel>
</rss>
