> ## Documentation Index
> Fetch the complete documentation index at: https://felimet-hub.jmcores.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 技術筆記：AI、CUDA、Python 與寫作

> 技術與學習筆記彙整，涵蓋 Windows CUDA 與 cuDNN 開發環境建置、Conda 與 Python 虛擬環境管理、繁體中文技術文稿撰寫指南，提供工程師與研究者實作參考。

export const notes = [{
  "title": "Windows CUDA & cuDNN 開發環境建置指南",
  "description": "完整收錄 Windows 平台的 NVIDIA 驅動程式更新、CUDA Toolkit 安裝與 cuDNN 配置流程，附環境變數設定、PATH 設置、版本相容性檢查與 nvcc / nvidia-smi 驗證步驟，是專為深度學習開發者打造的 Windows GPU 環境建置攻略。",
  "href": "/notes/ai-core/environment/cuda-setup",
  "tags": ["CUDA", "NVIDIA", "DL", "EnvSetup"],
  "featured": false,
  "order": 1,
  "date": "2025-03-18",
  "pubType": "",
  "venue": "",
  "confType": "",
  "award": ""
}, {
  "title": "WSL2 CUDA Toolkit 安裝手冊",
  "description": "在 WSL2 + Ubuntu 安裝 CUDA Toolkit：系統層級 apt 與使用者層級 runfile 兩種方式對照、環境變數配置、使用者切換與常見錯誤排查，涵蓋無 sudo 權限情境。",
  "href": "/notes/ai-core/environment/wsl-cuda-setup",
  "tags": ["CUDA", "WSL", "NVIDIA", "EnvSetup"],
  "featured": false,
  "order": 2,
  "date": "2026-07-05",
  "pubType": "",
  "venue": "",
  "confType": "",
  "award": ""
}, {
  "title": "在 Windows 安裝 Docker（WSL 2 後端）",
  "description": "Docker 筆記的起點：在 Windows 以 WSL 2 安裝 Docker，並通往指令、設定、Compose 與 Dockerfile 各章。",
  "href": "/notes/docker/install-windows",
  "tags": ["Docker", "WSL", "Windows", "容器"],
  "featured": false,
  "order": 1,
  "date": "2026-06-10",
  "pubType": "",
  "venue": "",
  "confType": "",
  "award": ""
}, {
  "title": "技術文稿撰寫指南與文稿素養",
  "description": "繁體中文技術文稿撰寫指南，整合學術寫作標準、論文格式與工程文件實踐，涵蓋中英文空格、全形與半形標點、專有名詞、引文規範、Word 排版自動化、版本控制協作與 AI 輔助寫作審閱的完整工作流程。",
  "href": "/notes/knowledge/writing-skills/zhtw-writing",
  "tags": ["Writing", "Documentation", "BestPractice"],
  "featured": false,
  "order": 999,
  "date": "2025-02-13",
  "pubType": "",
  "venue": "",
  "confType": "",
  "award": ""
}, {
  "title": "Synology NAS OpenVPN 伺服器設定指南",
  "description": "在 DSM 7 的 Synology NAS 上以 VPN Server 套件架設 OpenVPN 伺服器，涵蓋帳號權限、伺服器設定、DDNS、路由器連接埠轉發、防火牆、設定檔匯出與各平台用戶端連線。",
  "href": "/notes/network/synology-openvpn",
  "tags": ["OpenVPN", "Synology", "VPN", "網路"],
  "featured": false,
  "order": 999,
  "date": "2025-06-09",
  "pubType": "",
  "venue": "",
  "confType": "",
  "award": ""
}, {
  "title": "Windows 11 多人遠端桌面 (RDP) 設定",
  "description": "突破 Windows 11 內建遠端桌面單一連線限制，以 RDP Wrapper 實現多人同時連線，涵蓋安裝驗證、NLA 與會話安全設定、路由器連接埠轉發、Cloudflare 對外存取與疑難排解。",
  "href": "/notes/network/windows-multi-rdp",
  "tags": ["RDP", "Windows", "遠端桌面", "網路"],
  "featured": false,
  "order": 999,
  "date": "2025-06-09",
  "pubType": "",
  "venue": "",
  "confType": "",
  "award": ""
}, {
  "title": "Conda 安裝與配置指南",
  "description": "從零開始的 Conda 套件管理與配置指南，內容涵蓋 Anaconda 與 Miniconda 的差異、Windows 安裝流程、自訂安裝路徑、Channel 機制、套件來源設定，以及 Python 虛擬環境建立、切換、輸出與跨機遷移實務，適合資料科學與機器學習初學者。",
  "href": "/notes/software/languages/python/conda-guide",
  "tags": ["Python", "Conda", "Miniconda", "Anaconda", "EnvSetup"],
  "featured": false,
  "order": 999,
  "date": "2024-12-06",
  "pubType": "",
  "venue": "",
  "confType": "",
  "award": ""
}, {
  "title": "WSL Ubuntu 系統搬遷指南",
  "description": "把佔用 C 槽空間的 WSL Ubuntu 完整搬遷至其他磁碟機：以 wsl --export / --import 封裝與重新部署，保留所有軟體與設定，並釋放系統碟空間。",
  "href": "/notes/software/wsl-ubuntu-migration",
  "tags": ["WSL", "Ubuntu", "Windows", "系統管理"],
  "featured": false,
  "order": 999,
  "date": "2025-07-11",
  "pubType": "",
  "venue": "",
  "confType": "",
  "award": ""
}];

export const Gallery = ({items = [], type = "projects", lang = "zh"}) => {
  const TAG_LIMIT = 12;
  const t = lang === "en" ? {
    all: "All",
    more: n => `+${n} more`,
    less: "Show less",
    filter: "Filter",
    unit: n => `${n} items`,
    featured: "Featured first",
    date: "Newest first",
    title: "Title A→Z"
  } : {
    all: "全部",
    more: n => `+${n} 更多`,
    less: "收起",
    filter: "篩選",
    unit: n => `${n} 篇`,
    featured: "精選優先",
    date: "日期新→舊",
    title: "標題 A→Z"
  };
  const toTags = v => Array.isArray(v) ? v : typeof v === "string" && v.trim().startsWith("[") ? (() => {
    try {
      return JSON.parse(v);
    } catch {
      return [];
    }
  })() : v ? [String(v)] : [];
  const tagCount = {};
  for (const it of items) for (const tg of toTags(it.tags)) tagCount[tg] = (tagCount[tg] || 0) + 1;
  const sortedTags = Object.keys(tagCount).sort((a, b) => tagCount[b] - tagCount[a]);
  const [tag, setTag] = useState("all");
  const [sort, setSort] = useState(type === "publications" ? "date" : "featured");
  const [showAllTags, setShowAllTags] = useState(false);
  const visibleTags = showAllTags ? sortedTags : sortedTags.slice(0, TAG_LIMIT);
  const filtered = tag === "all" ? items : items.filter(i => toTags(i.tags).includes(tag));
  const shown = [...filtered].sort((a, b) => {
    if (sort === "title") return a.title.localeCompare(b.title, lang === "en" ? "en" : "zh-TW");
    if (sort === "date") return String(b.date).localeCompare(String(a.date));
    return (b.featured ? 1 : 0) - (a.featured ? 1 : 0) || a.order - b.order;
  });
  return <div>
      <div style={{
    marginBottom: "1.75rem"
  }}>
        <div style={{
    display: "flex",
    justifyContent: "space-between",
    alignItems: "center",
    marginBottom: "0.6rem"
  }}>
          <span className="g-filter-label">{t.filter} · {t.unit(shown.length)}</span>
          <select className="g-sort" value={sort} onChange={e => setSort(e.target.value)}>
            <option value="featured">{t.featured}</option>
            <option value="date">{t.date}</option>
            <option value="title">{t.title}</option>
          </select>
        </div>
        <div style={{
    display: "flex",
    flexWrap: "wrap",
    gap: "0.4rem",
    alignItems: "center"
  }}>
          <button className={"g-chip" + (tag === "all" ? " g-chip-on" : "")} onClick={() => setTag("all")}>{t.all}</button>
          {visibleTags.map(tg => <button key={tg} className={"g-chip" + (tag === tg ? " g-chip-on" : "")} onClick={() => setTag(tg)}>{tg}</button>)}
          {sortedTags.length > TAG_LIMIT && <button className="g-more" onClick={() => setShowAllTags(!showAllTags)}>
              {showAllTags ? t.less : t.more(sortedTags.length - TAG_LIMIT)}
            </button>}
        </div>
      </div>

      <div style={{
    display: "grid",
    gridTemplateColumns: "repeat(auto-fill, minmax(290px, 1fr))",
    gap: "1rem"
  }}>
        {shown.map(i => {
    const tags = toTags(i.tags);
    return <a key={i.href} href={i.href} className="g-card">
              <div className="g-card-title">
                {i.featured && <span style={{
      color: "#bf7551",
      marginRight: "0.3rem"
    }}>★</span>}
                {i.title}
              </div>
              <div className="g-card-desc">
                {i.description.length > 88 ? i.description.slice(0, 88) + "…" : i.description}
              </div>
              {(i.venue || tags.length > 0) && <div className="g-card-meta">
                  {i.venue && <div className="g-card-venue">{i.venue}</div>}
                  <div style={{
      display: "flex",
      flexWrap: "wrap",
      gap: "0.3rem"
    }}>
                    {tags.slice(0, 3).map(tg => <span key={tg} className="g-tag">{tg}</span>)}
                    {tags.length > 3 && <span className="g-tag-more">+{tags.length - 3}</span>}
                  </div>
                </div>}
            </a>;
  })}
      </div>
    </div>;
};

<Gallery items={notes} type="notes" />
