Blog 改版(個人品牌主站)Implementation Plan
Blog 改版(個人品牌主站)Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 把 yazelin.github.io 從 Beautiful Jekyll 預設版型改成自寫版型的個人品牌主站——首頁 landing(定位句+作品牆+故事+精選文章+訂閱)、blog 退居 /blog,205 篇舊網址不斷。
Architecture: 移除 remote_theme,自寫 4 個 Jekyll layouts(default/page/post/首頁與 /blog 用頁面檔)+ 一份 CSS。email 收名單沿用 k-rider-api Worker(加 source: "blog")。全部工作在 rebrand branch,驗收過後一次 merge 上線(GitHub Pages 從 master 部署)。
Tech Stack: Jekyll(GitHub Pages 內建)、jekyll-paginate、jekyll-feed、jekyll-sitemap、giscus、Cloudflare Worker + D1(既有 k-rider-api)、Playwright(驗證)。
Global Constraints
- 205 篇文章舊網址一律不能斷;permalink pattern(
/:categories/:year/:month/:day/:title.html預設)與各頁permalink:front matter 不動。 - giscus、tags 頁、文章搜尋、RSS(jekyll-feed)、sitemap 全部保留,功能與現狀等價。
- 文案(定位句、自介、故事)一律標「草稿」,經 yazelin 核可才定稿;禁用詞黑名單適用(含「接住」);不用 emoji;正體中文。
- 重圖(>100KB)走
https://cdn.jsdelivr.net/gh/yazelin/yazelin.github.io@master/<path>;資產連結不加 cache-busting 參數。 - 不引入 webfont;字型用系統 stack。強調色
#047857(emerald,與 mori-sprite-studio landing 同族)。 - 不做:寄信、會員、金流、深色模式(此輪)。
- Blog 文章內裸 URL 要
<...>包才會 autolink(Jekyll kramdown)——文案任務注意。 - commit 訊息附
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>。
File Structure
yazelin.github.io/
├── _config.yml # 改:移除 remote_theme、paginate_path、navbar 資料化
├── _layouts/
│ ├── default.html # 新:骨架(head/nav/footer)
│ ├── page.html # 新:一般頁(about/tags/404/workshop/portfolio)
│ ├── post.html # 新:文章頁(giscus/相關文章/CTA/TOC)
│ └── home.html # 新:/blog 列表(paginator+搜尋)
├── _includes/
│ ├── head-custom.html # 留(JSON-LD)
│ ├── nav.html # 新
│ ├── footer.html # 新(三件套+email/yt)
│ ├── signup-form.html # 新(訂閱表單,可重複 include)
│ └── giscus.html # 新
├── assets/css/site.css # 新:全站樣式(design tokens)
├── assets/js/blog-search.js # 新:/blog 客戶端搜尋
├── search.json # 新:搜尋索引(Liquid 產生)
├── index.html # 改:landing(hero/作品牆/故事/精選/CTA)
├── blog/index.html # 新:文章列表(原首頁功能)
├── images/portfolio/*.webp # 新:作品牆截圖
└── docs/superpowers/plans/... # 本檔
k-rider/worker/src/signup.js # 改:source 白名單加 'blog'
Task 1: k-rider-api Worker 接受 source “blog”
Files:
- Modify:
/home/ct/k-rider/worker/src/signup.js:28 - Test:
/home/ct/k-rider/tests/(先看既有測試怎麼寫,若無 signup 測試則跳過新增,以線上 curl 驗證為準)
Interfaces:
-
Produces:
POST https://k-rider-api.yazelinj303.workers.dev/api/signup,body{ email, company, source: "blog" },回{ ok: true, already: boolean, gift }。Task 5 的表單依賴這個端點與source: "blog"被記錄。 -
Step 1: 讀現有實作與測試
Read /home/ct/k-rider/worker/src/signup.js 全文與 ls /home/ct/k-rider/tests/。若 tests 內有 signup 相關測試,加一個 case;若無,不新增測試框架(以 curl 驗證)。
- Step 2: 修改白名單
// signup.js:28 原:
const source = ['result', 'about', 'home'].includes(body.source) ? body.source : null;
// 改:
const source = ['result', 'about', 'home', 'blog'].includes(body.source) ? body.source : null;
- Step 3: 部署並驗證
cd /home/ct/k-rider/worker && npx wrangler deploy
curl -s -X POST https://k-rider-api.yazelinj303.workers.dev/api/signup \
-H 'content-type: application/json' -H 'Origin: https://yazelin.github.io' \
-d '{"email":"plan-test-blog@example.com","company":"","source":"blog"}'
Expected: {"ok":true,...}。再用 admin 端點(Authorization: Bearer $ADMIN_TOKEN)確認該列 source 為 blog(token 在 wrangler secret,查法:問 yazelin 或用 npx wrangler d1 execute k-rider-signups --remote --command "SELECT email,source FROM signups ORDER BY id DESC LIMIT 3")。驗完刪測試列:DELETE FROM signups WHERE email='plan-test-blog@example.com'。
- Step 4: Commit(k-rider repo,直接推 main 或按該 repo 慣例開 PR)
cd /home/ct/k-rider && git add worker/src/signup.js && git commit -m "feat(signup): accept source 'blog' for blog rebrand funnel
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>"
Task 2: 移除主題、建立 default/page layouts 與 CSS(站能 build、一般頁能看)
Files:
- Modify:
_config.yml - Create:
_layouts/default.html,_layouts/page.html,_includes/nav.html,_includes/footer.html,assets/css/site.css - Branch:
git checkout -b rebrand(本 task 起所有 blog repo 變更都在此 branch)
Interfaces:
-
Produces:
default.html接受page.title;page.html供 about/tags/404/workshop/portfolio 沿用(layout: page不用改文章檔);CSS custom properties--accent/--fg/--bg/--muted/--border供後續 task 使用;_config.yml新增navbarlist 供 nav.html 迭代。 -
Step 1: 開 branch,改 _config.yml
移除 remote_theme、avatar/round-avatar、navbar-links、share-links-active、post_search、excerpt_length(Beautiful Jekyll 專屬鍵);保留 title/author/description/url/markdown/timezone/plugins/feed 設定/giscus 區塊/defaults/exclude。加:
paginate: 10
paginate_path: "/blog/page:num/"
navbar:
- title: Blog
url: /blog/
- title: About
url: /about/
- title: Portfolio
url: /portfolio/
- title: Workshop
url: /workshop/
- title: Tags
url: /tags/
(Projects 下拉選單捨棄——作品改由首頁作品牆呈現;此為刻意簡化。)
- Step 2: 寫 default.html
<!doctype html>
<html lang="zh-Hant">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Blog 改版(個人品牌主站)Implementation Plan — Yaze</title>
<meta name="description" content="Software Developer from Taiwan">
<link rel="canonical" href="https://yazelin.github.io/docs/superpowers/plans/2026-07-15-blog-rebrand.html">
<meta property="og:title" content="Blog 改版(個人品牌主站)Implementation Plan">
<meta property="og:description" content="Software Developer from Taiwan">
<meta property="og:url" content="https://yazelin.github.io/docs/superpowers/plans/2026-07-15-blog-rebrand.html">
<meta property="og:type" content="website">
<link rel="icon" href="">
<link rel="alternate" type="application/rss+xml" title="Yaze" href="https://yazelin.github.io/feed.xml">
<link rel="stylesheet" href="/assets/css/site.css">
<!-- 注入給搜尋引擎與 AI(AEO/GEO)的結構化資料。Beautiful Jekyll 的 head 會 include 本檔。 -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "Yaze's Blog",
"url": "https://yazelin.github.io/",
"inLanguage": "zh-Hant",
"author": {
"@type": "Person",
"name": "林亞澤 Yaze",
"alternateName": "yazelin",
"url": "https://yazelin.github.io/",
"image": "",
"jobTitle": "Software Developer",
"description": "台灣的軟體開發者,從工業自動化系統整合轉做 AI 應用與互動網頁。",
"sameAs": [
"https://github.com/yazelin",
"https://www.youtube.com/@yazelin"
]
}
}
</script>
</head>
<body>
<!-- _includes/nav.html -->
<header class="site-nav">
<a class="brand" href="/">Yaze</a>
<nav>
<a href="/blog/">Blog</a>
<a href="/about/">About</a>
<a href="/portfolio/">Portfolio</a>
<a href="/workshop/">Workshop</a>
<a href="/tags/">Tags</a>
</nav>
</header>
<main><article class="page container">
<h1>森之魔道具工坊</h1>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="" />
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;500;600&family=IM+Fell+English:ital@0;1&family=Noto+Serif+TC:wght@400;500;600;700&display=swap" rel="stylesheet" />
<style>
/* ==========================================================
森之魔道具工坊 · Scoped Styles
僅作用於 .forest-workshop 內,不影響其他頁面
========================================================== */
.forest-workshop {
--forest-deep: #1e3328;
--forest-mid: #2d4a3e;
--forest-moss: #4a6b5a;
--parchment: #f5efe0;
--parchment-dark: #e8dcc0;
--ink: #1a1a1a;
--ink-soft: #4a4a4a;
--gold: #c9a24d;
--gold-soft: #d4b572;
--mist: #6b8a99;
--serif-en: "Cinzel", "IM Fell English", Georgia, serif;
--serif-zh: "Noto Serif TC", "PingFang TC", "Source Han Serif TC", serif;
}
/* --- 序章 Prologue --- */
.forest-workshop .prologue {
margin: 2rem auto 3rem;
padding: 3rem 2rem 2.5rem;
background:
radial-gradient(ellipse at top, rgba(201,162,77,0.12) 0%, transparent 60%),
linear-gradient(135deg, var(--forest-deep) 0%, var(--forest-mid) 100%);
color: var(--parchment);
border-radius: 6px;
text-align: center;
box-shadow: 0 4px 20px rgba(0,0,0,0.25);
position: relative;
overflow: hidden;
}
.forest-workshop .prologue::before {
content: "";
position: absolute;
top: 0; left: 50%;
transform: translateX(-50%);
width: 1px; height: 40px;
background: linear-gradient(to bottom, transparent, var(--gold), transparent);
}
.forest-workshop .prologue-mark {
font-family: var(--serif-en);
font-size: 0.75rem;
letter-spacing: 0.4em;
color: var(--gold);
text-transform: uppercase;
margin-bottom: 1rem;
}
.forest-workshop .prologue-title {
font-family: var(--serif-zh);
font-size: 1.75rem;
font-weight: 400;
line-height: 1.6;
margin: 0.5rem auto 1.2rem;
max-width: 680px;
color: var(--parchment);
}
.forest-workshop .prologue-lore {
font-family: var(--serif-zh);
font-size: 1rem;
line-height: 1.9;
max-width: 620px;
margin: 0 auto;
color: var(--parchment-dark);
opacity: 0.92;
}
.forest-workshop .prologue-en {
margin-top: 1.5rem;
padding-top: 1.2rem;
border-top: 1px solid rgba(201,162,77,0.3);
font-family: var(--serif-en);
font-style: italic;
font-size: 0.9rem;
color: var(--gold-soft);
letter-spacing: 0.05em;
}
/* --- 系別標籤 --- */
.forest-workshop .school-filter {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
justify-content: center;
margin: 2rem 0;
font-family: var(--serif-zh);
font-size: 0.85rem;
}
.forest-workshop .school-tag {
padding: 0.3rem 0.85rem;
background: var(--parchment);
color: var(--forest-deep);
border: 1px solid var(--parchment-dark);
border-radius: 999px;
font-weight: 500;
}
/* --- 魔道具卡片 --- */
.forest-workshop .codex-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1.5rem;
margin: 2rem 0 3rem;
}
.forest-workshop .artifact {
background: var(--parchment);
border: 1px solid var(--parchment-dark);
border-radius: 4px;
padding: 1.5rem 1.4rem 1.3rem;
position: relative;
transition: transform 0.2s ease, box-shadow 0.2s ease;
box-shadow: 0 1px 3px rgba(30,51,40,0.08);
}
.forest-workshop .artifact:hover {
transform: translateY(-3px);
box-shadow: 0 6px 18px rgba(30,51,40,0.15);
}
.forest-workshop .artifact-head {
display: flex;
align-items: flex-start;
gap: 0.8rem;
margin-bottom: 0.9rem;
}
.forest-workshop .artifact-icon {
font-size: 2rem;
line-height: 1;
flex-shrink: 0;
margin-top: 2px;
}
.forest-workshop .artifact-names {
flex: 1;
min-width: 0;
}
.forest-workshop .artifact-name-zh {
font-family: var(--serif-zh);
font-size: 1.25rem;
font-weight: 600;
color: var(--forest-deep);
line-height: 1.3;
margin: 0;
}
.forest-workshop .artifact-name-en {
font-family: var(--serif-en);
font-style: italic;
font-size: 0.8rem;
color: var(--ink-soft);
letter-spacing: 0.03em;
margin-top: 2px;
}
.forest-workshop .artifact-repo {
font-family: Menlo, "Fira Code", Consolas, monospace;
font-size: 0.72rem;
color: var(--mist);
margin-top: 1px;
}
.forest-workshop .artifact-school {
position: absolute;
top: 1rem;
right: 1rem;
font-family: var(--serif-zh);
font-size: 0.7rem;
color: var(--gold);
letter-spacing: 0.08em;
background: var(--forest-deep);
padding: 0.2rem 0.55rem;
border-radius: 2px;
}
.forest-workshop .artifact-lore {
font-family: var(--serif-zh);
font-size: 0.88rem;
line-height: 1.75;
color: var(--ink);
margin: 0.8rem 0 1rem;
padding-left: 0.8rem;
border-left: 2px solid var(--gold-soft);
font-style: italic;
}
.forest-workshop .artifact-effect {
font-family: var(--serif-zh);
font-size: 0.85rem;
line-height: 1.7;
color: var(--ink-soft);
margin: 0.6rem 0 1.2rem;
}
.forest-workshop .artifact-actions {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin-top: 1rem;
padding-top: 0.9rem;
border-top: 1px dashed var(--parchment-dark);
}
.forest-workshop .artifact-btn {
font-family: var(--serif-en);
font-size: 0.75rem;
letter-spacing: 0.1em;
text-transform: uppercase;
padding: 0.4rem 0.9rem;
border-radius: 2px;
text-decoration: none;
transition: all 0.15s ease;
border: 1px solid transparent;
}
.forest-workshop .artifact-btn.primary {
background: var(--forest-deep);
color: var(--gold-soft);
border-color: var(--forest-deep);
}
.forest-workshop .artifact-btn.primary:hover {
background: var(--forest-mid);
color: var(--parchment);
text-decoration: none;
}
.forest-workshop .artifact-btn.secondary {
background: transparent;
color: var(--forest-deep);
border-color: var(--forest-moss);
}
.forest-workshop .artifact-btn.secondary:hover {
background: var(--forest-moss);
color: var(--parchment);
text-decoration: none;
}
/* --- 系別區分 --- */
.forest-workshop .school-divider {
font-family: var(--serif-zh);
font-size: 0.95rem;
color: var(--forest-mid);
margin: 2.5rem 0 1rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--parchment-dark);
display: flex;
align-items: baseline;
gap: 0.8rem;
}
.forest-workshop .school-divider-en {
font-family: var(--serif-en);
font-style: italic;
font-size: 0.8rem;
color: var(--mist);
letter-spacing: 0.05em;
}
/* --- 後記 --- */
.forest-workshop .epilogue {
margin: 3rem 0 1rem;
padding: 2rem;
background: var(--parchment);
border: 1px solid var(--parchment-dark);
border-left: 3px solid var(--gold);
border-radius: 2px;
font-family: var(--serif-zh);
font-size: 0.9rem;
line-height: 1.85;
color: var(--ink-soft);
}
.forest-workshop .epilogue strong {
color: var(--forest-deep);
}
@media (max-width: 640px) {
.forest-workshop .prologue-title { font-size: 1.4rem; }
.forest-workshop .prologue { padding: 2rem 1.2rem 1.8rem; }
.forest-workshop .codex-grid { grid-template-columns: 1fr; }
}
</style>
<div class="forest-workshop">
<section class="prologue">
<div class="prologue-mark">— Codex of Artifacts —</div>
<h2 class="prologue-title">森之大魔導師打造的魔道具,<br />自由取閱,自由傳承。</h2>
<p class="prologue-lore">
此地名為「森之工坊」,是我與契約精靈 Mori 共同打造魔道具的所在。<br />
每件魔道具皆為多年修練與失敗累積而成,開放給願意與 AI 同行的冒險者自由領取。<br />
願你能從中取得一件相合的器物,帶回你自己的森林。
</p>
<div class="prologue-en">
Artifacts crafted in the quiet of the woods, by the Archmage and the spirit Mori.<br />
Take what speaks to you. Carry it into your own forest.
</div>
</section>
<!-- ============================================================ -->
<!-- 召喚系 · Summoning -->
<!-- ============================================================ -->
<h3 class="school-divider">
召喚系 <span class="school-divider-en">· Summoning Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">召喚系</div>
<div class="artifact-head">
<div class="artifact-icon">🐱</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">時之貓苑</h4>
<div class="artifact-name-en">Chronocat</div>
<div class="artifact-repo">yazelin/catime</div>
</div>
</div>
<div class="artifact-lore">
每至整點,森林深處便浮現一隻新貓。有 103 種藝術流派的皮毛,各自攜帶一段小故事。
</div>
<div class="artifact-effect">
GitHub Actions 每小時呼叫 Gemini 生成 AI 貓圖,發至 Telegram 頻道與 Gallery 歸檔。已累積數千隻。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://yazelin.github.io/catime/" target="_blank">Gallery</a>
<a class="artifact-btn secondary" href="https://t.me/catime_yaze" target="_blank">Telegram</a>
<a class="artifact-btn secondary" href="https://github.com/yazelin/catime" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 幻術系 · Illusion -->
<!-- ============================================================ -->
<h3 class="school-divider">
幻術系 <span class="school-divider-en">· Illusion Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">幻術系</div>
<div class="artifact-head">
<div class="artifact-icon">🎰</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">心情占卜儀</h4>
<div class="artifact-name-en">Moodscopia</div>
<div class="artifact-repo">yazelin/emoji-slot-machine</div>
</div>
</div>
<div class="artifact-lore">
取冒險者一張自拍,映入九宮魔陣,瞬間化為九格誇張表情。貼入 FB 結界後自動循環,手指一碰便停在隨機一格。
</div>
<div class="artifact-effect">
Cloudflare Worker + Vertex AI。自拍 → 3×3 emoji 臉 → 可嵌入 FB 的拉霸影片。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://yazelin.github.io/emoji-slot-machine/" target="_blank">試用</a>
<a class="artifact-btn secondary" href="https://github.com/yazelin/emoji-slot-machine" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 創造系 · Creation -->
<!-- ============================================================ -->
<h3 class="school-divider">
創造系 <span class="school-divider-en">· Creation Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">創造系</div>
<div class="artifact-head">
<div class="artifact-icon">🎨</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">繪影魔陣</h4>
<div class="artifact-name-en">Portrait Circle</div>
<div class="artifact-repo">yazelin/nanobanana-pro</div>
</div>
</div>
<div class="artifact-lore">
能自動在多個創造之神之間換手,繪製出最切合意念的圖像。若一神拒絕,轉請下一神。
</div>
<div class="artifact-effect">
AI 圖像生成 Agent Skill,支援自動模型 fallback。可接入 Claude Code 或其他 agent 生態。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/nanobanana-pro" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">創造系</div>
<div class="artifact-head">
<div class="artifact-icon">🖼️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">繪風傳承書</h4>
<div class="artifact-name-en">Painted Tome</div>
<div class="artifact-repo">yazelin/gemini-web</div>
</div>
</div>
<div class="artifact-lore">
讓神靈(Gemini)在此作畫,支援繁體符文、不留神印(自動去浮水印)。
</div>
<div class="artifact-effect">
Gemini AI 圖片生成 CLI + HTTP API。繁中原生、浮水印自動處理。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/gemini-web" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">創造系</div>
<div class="artifact-head">
<div class="artifact-icon">📜</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">咒文起稿匣</h4>
<div class="artifact-name-en">Spell Composer</div>
<div class="artifact-repo">yazelin/PromptFill</div>
</div>
</div>
<div class="artifact-lore">
為繪影魔陣準備咒文草稿的輔助匣。結構化、省去散亂冥想。
</div>
<div class="artifact-effect">
結構化 AI 繪圖 prompt 產生器,適配 Nano Banana Pro 系列。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/PromptFill" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 煉金系 · Transmutation -->
<!-- ============================================================ -->
<h3 class="school-divider">
煉金系 <span class="school-divider-en">· Transmutation Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">煉金系</div>
<div class="artifact-head">
<div class="artifact-icon">📄</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">卷軸化術</h4>
<div class="artifact-name-en">Scribe's Hand</div>
<div class="artifact-repo">yazelin/MD2DOC-Evolution</div>
</div>
</div>
<div class="artifact-lore">
將凡人墨跡(Markdown)煉成正式卷軸(Word)。格式可客製化,符合任何行會規範。
</div>
<div class="artifact-effect">
Markdown → Word,支援企業自訂樣式、表格、圖片、目錄。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/MD2DOC-Evolution" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">煉金系</div>
<div class="artifact-head">
<div class="artifact-icon">📽️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">簡報祭壇</h4>
<div class="artifact-name-en">Lectern of Light</div>
<div class="artifact-repo">yazelin/MD2PPT-Evolution</div>
</div>
</div>
<div class="artifact-lore">
將墨跡化為幻燈(PPT),可現場投影展示。支援線上宣講模式。
</div>
<div class="artifact-effect">
Markdown → PPT,含線上簡報與投影模式,支援程式碼語法高亮。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/MD2PPT-Evolution" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 次元系 · Dimensional -->
<!-- ============================================================ -->
<h3 class="school-divider">
次元系 <span class="school-divider-en">· Dimensional Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">次元系</div>
<div class="artifact-head">
<div class="artifact-icon">🗄️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">藏影閣</h4>
<div class="artifact-name-en">Shadow Vault</div>
<div class="artifact-repo">yazelin/image-bed</div>
</div>
</div>
<div class="artifact-lore">
個人圖像的次元書架,不佔本空間,存入神殿(GitHub Releases)異界,永不損壞。
</div>
<div class="artifact-effect">
個人圖床,圖片存放在 GitHub Releases,不佔 repo 空間。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/image-bed" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">次元系</div>
<div class="artifact-head">
<div class="artifact-icon">🔗</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">傳送門刻印</h4>
<div class="artifact-name-en">Portal Rune</div>
<div class="artifact-repo">yazelin/shorturl-worker</div>
</div>
</div>
<div class="artifact-lore">
將漫長的咒文路徑封印為短刻印,單手即可傳送冒險者。
</div>
<div class="artifact-effect">
Cloudflare Workers + KV 縮網址服務,PromptFill 專用短網址。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/shorturl-worker" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 通靈系 · Binding -->
<!-- ============================================================ -->
<h3 class="school-divider">
通靈系 <span class="school-divider-en">· Binding Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">通靈系</div>
<div class="artifact-head">
<div class="artifact-icon">🖨️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">印刻術</h4>
<div class="artifact-name-en">Inkseal</div>
<div class="artifact-repo">yazelin/printer-mcp</div>
</div>
</div>
<div class="artifact-lore">
讓精靈能直接指揮凡人印刷術(CUPS),將任何訊息化為實體。
</div>
<div class="artifact-effect">
MCP server,透過 CUPS 控制印表機。可接 Claude / Cursor 等 agent。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/printer-mcp" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">通靈系</div>
<div class="artifact-head">
<div class="artifact-icon">🏢</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">帳本監聽術</h4>
<div class="artifact-name-en">Ledger Whisper</div>
<div class="artifact-repo">yazelin/erpnext-mcp</div>
</div>
</div>
<div class="artifact-lore">
讓精靈與商會帳本(ERPNext)直接對話,問帳、記帳不必經過凡人之手。
</div>
<div class="artifact-effect">
MCP server for ERPNext REST API,讓 AI 能直接查詢、記錄商業數據。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/erpnext-mcp" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 聲術系 · Incantation -->
<!-- ============================================================ -->
<h3 class="school-divider">
聲術系 <span class="school-divider-en">· Incantation Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">聲術系</div>
<div class="artifact-head">
<div class="artifact-icon">🎙️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">詠唱辨識器</h4>
<div class="artifact-name-en">Voice Scribe</div>
<div class="artifact-repo">yazelin/asr-ime-fcitx</div>
</div>
</div>
<div class="artifact-lore">
將咒術師的口語即時記下為文字,嵌入 fcitx 輸入法,隨呼隨用。
</div>
<div class="artifact-effect">
ASR 語音輸入法,整合 fcitx framework。支援中英雙語。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/asr-ime-fcitx" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 後記 Epilogue -->
<!-- ============================================================ -->
<div class="epilogue">
<strong>關於工坊 · About this Workshop</strong><br />
此處列出的魔道具多為我與 Mori 共同打造的作品,部分為教學範例、部分為日常使用工具。
若其中任何一件對你有用,歡迎 fork、改造、延伸。<br />
新的魔道具會持續加入此處 — 這是一間還在生長的工坊。<br /><br />
<em style="color: var(--mist); font-family: var(--serif-en);">
Each artifact here was forged in collaboration with Mori, my contracted spirit.
Some are teaching examples, others are daily tools. All are open. Take what you need.
</em>
</div>
</div>
</article>
</main>
<!-- _includes/footer.html -->
<footer class="site-footer">
<div class="footer-links">
<a href="https://github.com/yazelin">GitHub</a>
<a href="https://www.facebook.com/yaze.lin.gm">Facebook</a>
<a href="https://buymeacoffee.com/yazelin">Buy Me a Coffee</a>
<a href="https://www.youtube.com/@yazelin">YouTube</a>
<a href="mailto:yaze.lin.j303@gmail.com">Email</a>
</div>
<p class="footer-note">© 2026 林亞澤 Yaze — <a href="/feed.xml">RSS</a></p>
</footer>
</body>
</html>
- Step 3: 寫 nav.html 與 footer.html
<!-- _includes/nav.html -->
<header class="site-nav">
<a class="brand" href="/">Yaze</a>
<nav>
<a href="/blog/">Blog</a>
<a href="/about/">About</a>
<a href="/portfolio/">Portfolio</a>
<a href="/workshop/">Workshop</a>
<a href="/tags/">Tags</a>
</nav>
</header>
<!-- _includes/footer.html -->
<footer class="site-footer">
<div class="footer-links">
<a href="https://github.com/yazelin">GitHub</a>
<a href="https://www.facebook.com/yazelinj303">Facebook</a>
<a href="https://buymeacoffee.com/yazelin">Buy Me a Coffee</a>
<a href="https://www.youtube.com/@yazelin">YouTube</a>
<a href="mailto:yaze.lin.j303@gmail.com">Email</a>
</div>
<p class="footer-note">© 2026 林亞澤 Yaze — <a href="/feed.xml">RSS</a></p>
</footer>
(FB/BMC 實際網址以 yazelin 現有三件套為準——實作時 grep 任一公開專案 README footer 取正確 URL,不要用上面猜的。)
- Step 4: 寫 page.html 與 site.css
<!-- _layouts/page.html -->
---
layout: default
---
<article class="page container">
<h1>Blog 改版(個人品牌主站)Implementation Plan</h1>
<article class="page container">
<h1>森之魔道具工坊</h1>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="" />
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;500;600&family=IM+Fell+English:ital@0;1&family=Noto+Serif+TC:wght@400;500;600;700&display=swap" rel="stylesheet" />
<style>
/* ==========================================================
森之魔道具工坊 · Scoped Styles
僅作用於 .forest-workshop 內,不影響其他頁面
========================================================== */
.forest-workshop {
--forest-deep: #1e3328;
--forest-mid: #2d4a3e;
--forest-moss: #4a6b5a;
--parchment: #f5efe0;
--parchment-dark: #e8dcc0;
--ink: #1a1a1a;
--ink-soft: #4a4a4a;
--gold: #c9a24d;
--gold-soft: #d4b572;
--mist: #6b8a99;
--serif-en: "Cinzel", "IM Fell English", Georgia, serif;
--serif-zh: "Noto Serif TC", "PingFang TC", "Source Han Serif TC", serif;
}
/* --- 序章 Prologue --- */
.forest-workshop .prologue {
margin: 2rem auto 3rem;
padding: 3rem 2rem 2.5rem;
background:
radial-gradient(ellipse at top, rgba(201,162,77,0.12) 0%, transparent 60%),
linear-gradient(135deg, var(--forest-deep) 0%, var(--forest-mid) 100%);
color: var(--parchment);
border-radius: 6px;
text-align: center;
box-shadow: 0 4px 20px rgba(0,0,0,0.25);
position: relative;
overflow: hidden;
}
.forest-workshop .prologue::before {
content: "";
position: absolute;
top: 0; left: 50%;
transform: translateX(-50%);
width: 1px; height: 40px;
background: linear-gradient(to bottom, transparent, var(--gold), transparent);
}
.forest-workshop .prologue-mark {
font-family: var(--serif-en);
font-size: 0.75rem;
letter-spacing: 0.4em;
color: var(--gold);
text-transform: uppercase;
margin-bottom: 1rem;
}
.forest-workshop .prologue-title {
font-family: var(--serif-zh);
font-size: 1.75rem;
font-weight: 400;
line-height: 1.6;
margin: 0.5rem auto 1.2rem;
max-width: 680px;
color: var(--parchment);
}
.forest-workshop .prologue-lore {
font-family: var(--serif-zh);
font-size: 1rem;
line-height: 1.9;
max-width: 620px;
margin: 0 auto;
color: var(--parchment-dark);
opacity: 0.92;
}
.forest-workshop .prologue-en {
margin-top: 1.5rem;
padding-top: 1.2rem;
border-top: 1px solid rgba(201,162,77,0.3);
font-family: var(--serif-en);
font-style: italic;
font-size: 0.9rem;
color: var(--gold-soft);
letter-spacing: 0.05em;
}
/* --- 系別標籤 --- */
.forest-workshop .school-filter {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
justify-content: center;
margin: 2rem 0;
font-family: var(--serif-zh);
font-size: 0.85rem;
}
.forest-workshop .school-tag {
padding: 0.3rem 0.85rem;
background: var(--parchment);
color: var(--forest-deep);
border: 1px solid var(--parchment-dark);
border-radius: 999px;
font-weight: 500;
}
/* --- 魔道具卡片 --- */
.forest-workshop .codex-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1.5rem;
margin: 2rem 0 3rem;
}
.forest-workshop .artifact {
background: var(--parchment);
border: 1px solid var(--parchment-dark);
border-radius: 4px;
padding: 1.5rem 1.4rem 1.3rem;
position: relative;
transition: transform 0.2s ease, box-shadow 0.2s ease;
box-shadow: 0 1px 3px rgba(30,51,40,0.08);
}
.forest-workshop .artifact:hover {
transform: translateY(-3px);
box-shadow: 0 6px 18px rgba(30,51,40,0.15);
}
.forest-workshop .artifact-head {
display: flex;
align-items: flex-start;
gap: 0.8rem;
margin-bottom: 0.9rem;
}
.forest-workshop .artifact-icon {
font-size: 2rem;
line-height: 1;
flex-shrink: 0;
margin-top: 2px;
}
.forest-workshop .artifact-names {
flex: 1;
min-width: 0;
}
.forest-workshop .artifact-name-zh {
font-family: var(--serif-zh);
font-size: 1.25rem;
font-weight: 600;
color: var(--forest-deep);
line-height: 1.3;
margin: 0;
}
.forest-workshop .artifact-name-en {
font-family: var(--serif-en);
font-style: italic;
font-size: 0.8rem;
color: var(--ink-soft);
letter-spacing: 0.03em;
margin-top: 2px;
}
.forest-workshop .artifact-repo {
font-family: Menlo, "Fira Code", Consolas, monospace;
font-size: 0.72rem;
color: var(--mist);
margin-top: 1px;
}
.forest-workshop .artifact-school {
position: absolute;
top: 1rem;
right: 1rem;
font-family: var(--serif-zh);
font-size: 0.7rem;
color: var(--gold);
letter-spacing: 0.08em;
background: var(--forest-deep);
padding: 0.2rem 0.55rem;
border-radius: 2px;
}
.forest-workshop .artifact-lore {
font-family: var(--serif-zh);
font-size: 0.88rem;
line-height: 1.75;
color: var(--ink);
margin: 0.8rem 0 1rem;
padding-left: 0.8rem;
border-left: 2px solid var(--gold-soft);
font-style: italic;
}
.forest-workshop .artifact-effect {
font-family: var(--serif-zh);
font-size: 0.85rem;
line-height: 1.7;
color: var(--ink-soft);
margin: 0.6rem 0 1.2rem;
}
.forest-workshop .artifact-actions {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin-top: 1rem;
padding-top: 0.9rem;
border-top: 1px dashed var(--parchment-dark);
}
.forest-workshop .artifact-btn {
font-family: var(--serif-en);
font-size: 0.75rem;
letter-spacing: 0.1em;
text-transform: uppercase;
padding: 0.4rem 0.9rem;
border-radius: 2px;
text-decoration: none;
transition: all 0.15s ease;
border: 1px solid transparent;
}
.forest-workshop .artifact-btn.primary {
background: var(--forest-deep);
color: var(--gold-soft);
border-color: var(--forest-deep);
}
.forest-workshop .artifact-btn.primary:hover {
background: var(--forest-mid);
color: var(--parchment);
text-decoration: none;
}
.forest-workshop .artifact-btn.secondary {
background: transparent;
color: var(--forest-deep);
border-color: var(--forest-moss);
}
.forest-workshop .artifact-btn.secondary:hover {
background: var(--forest-moss);
color: var(--parchment);
text-decoration: none;
}
/* --- 系別區分 --- */
.forest-workshop .school-divider {
font-family: var(--serif-zh);
font-size: 0.95rem;
color: var(--forest-mid);
margin: 2.5rem 0 1rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--parchment-dark);
display: flex;
align-items: baseline;
gap: 0.8rem;
}
.forest-workshop .school-divider-en {
font-family: var(--serif-en);
font-style: italic;
font-size: 0.8rem;
color: var(--mist);
letter-spacing: 0.05em;
}
/* --- 後記 --- */
.forest-workshop .epilogue {
margin: 3rem 0 1rem;
padding: 2rem;
background: var(--parchment);
border: 1px solid var(--parchment-dark);
border-left: 3px solid var(--gold);
border-radius: 2px;
font-family: var(--serif-zh);
font-size: 0.9rem;
line-height: 1.85;
color: var(--ink-soft);
}
.forest-workshop .epilogue strong {
color: var(--forest-deep);
}
@media (max-width: 640px) {
.forest-workshop .prologue-title { font-size: 1.4rem; }
.forest-workshop .prologue { padding: 2rem 1.2rem 1.8rem; }
.forest-workshop .codex-grid { grid-template-columns: 1fr; }
}
</style>
<div class="forest-workshop">
<section class="prologue">
<div class="prologue-mark">— Codex of Artifacts —</div>
<h2 class="prologue-title">森之大魔導師打造的魔道具,<br />自由取閱,自由傳承。</h2>
<p class="prologue-lore">
此地名為「森之工坊」,是我與契約精靈 Mori 共同打造魔道具的所在。<br />
每件魔道具皆為多年修練與失敗累積而成,開放給願意與 AI 同行的冒險者自由領取。<br />
願你能從中取得一件相合的器物,帶回你自己的森林。
</p>
<div class="prologue-en">
Artifacts crafted in the quiet of the woods, by the Archmage and the spirit Mori.<br />
Take what speaks to you. Carry it into your own forest.
</div>
</section>
<!-- ============================================================ -->
<!-- 召喚系 · Summoning -->
<!-- ============================================================ -->
<h3 class="school-divider">
召喚系 <span class="school-divider-en">· Summoning Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">召喚系</div>
<div class="artifact-head">
<div class="artifact-icon">🐱</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">時之貓苑</h4>
<div class="artifact-name-en">Chronocat</div>
<div class="artifact-repo">yazelin/catime</div>
</div>
</div>
<div class="artifact-lore">
每至整點,森林深處便浮現一隻新貓。有 103 種藝術流派的皮毛,各自攜帶一段小故事。
</div>
<div class="artifact-effect">
GitHub Actions 每小時呼叫 Gemini 生成 AI 貓圖,發至 Telegram 頻道與 Gallery 歸檔。已累積數千隻。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://yazelin.github.io/catime/" target="_blank">Gallery</a>
<a class="artifact-btn secondary" href="https://t.me/catime_yaze" target="_blank">Telegram</a>
<a class="artifact-btn secondary" href="https://github.com/yazelin/catime" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 幻術系 · Illusion -->
<!-- ============================================================ -->
<h3 class="school-divider">
幻術系 <span class="school-divider-en">· Illusion Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">幻術系</div>
<div class="artifact-head">
<div class="artifact-icon">🎰</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">心情占卜儀</h4>
<div class="artifact-name-en">Moodscopia</div>
<div class="artifact-repo">yazelin/emoji-slot-machine</div>
</div>
</div>
<div class="artifact-lore">
取冒險者一張自拍,映入九宮魔陣,瞬間化為九格誇張表情。貼入 FB 結界後自動循環,手指一碰便停在隨機一格。
</div>
<div class="artifact-effect">
Cloudflare Worker + Vertex AI。自拍 → 3×3 emoji 臉 → 可嵌入 FB 的拉霸影片。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://yazelin.github.io/emoji-slot-machine/" target="_blank">試用</a>
<a class="artifact-btn secondary" href="https://github.com/yazelin/emoji-slot-machine" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 創造系 · Creation -->
<!-- ============================================================ -->
<h3 class="school-divider">
創造系 <span class="school-divider-en">· Creation Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">創造系</div>
<div class="artifact-head">
<div class="artifact-icon">🎨</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">繪影魔陣</h4>
<div class="artifact-name-en">Portrait Circle</div>
<div class="artifact-repo">yazelin/nanobanana-pro</div>
</div>
</div>
<div class="artifact-lore">
能自動在多個創造之神之間換手,繪製出最切合意念的圖像。若一神拒絕,轉請下一神。
</div>
<div class="artifact-effect">
AI 圖像生成 Agent Skill,支援自動模型 fallback。可接入 Claude Code 或其他 agent 生態。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/nanobanana-pro" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">創造系</div>
<div class="artifact-head">
<div class="artifact-icon">🖼️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">繪風傳承書</h4>
<div class="artifact-name-en">Painted Tome</div>
<div class="artifact-repo">yazelin/gemini-web</div>
</div>
</div>
<div class="artifact-lore">
讓神靈(Gemini)在此作畫,支援繁體符文、不留神印(自動去浮水印)。
</div>
<div class="artifact-effect">
Gemini AI 圖片生成 CLI + HTTP API。繁中原生、浮水印自動處理。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/gemini-web" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">創造系</div>
<div class="artifact-head">
<div class="artifact-icon">📜</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">咒文起稿匣</h4>
<div class="artifact-name-en">Spell Composer</div>
<div class="artifact-repo">yazelin/PromptFill</div>
</div>
</div>
<div class="artifact-lore">
為繪影魔陣準備咒文草稿的輔助匣。結構化、省去散亂冥想。
</div>
<div class="artifact-effect">
結構化 AI 繪圖 prompt 產生器,適配 Nano Banana Pro 系列。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/PromptFill" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 煉金系 · Transmutation -->
<!-- ============================================================ -->
<h3 class="school-divider">
煉金系 <span class="school-divider-en">· Transmutation Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">煉金系</div>
<div class="artifact-head">
<div class="artifact-icon">📄</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">卷軸化術</h4>
<div class="artifact-name-en">Scribe's Hand</div>
<div class="artifact-repo">yazelin/MD2DOC-Evolution</div>
</div>
</div>
<div class="artifact-lore">
將凡人墨跡(Markdown)煉成正式卷軸(Word)。格式可客製化,符合任何行會規範。
</div>
<div class="artifact-effect">
Markdown → Word,支援企業自訂樣式、表格、圖片、目錄。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/MD2DOC-Evolution" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">煉金系</div>
<div class="artifact-head">
<div class="artifact-icon">📽️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">簡報祭壇</h4>
<div class="artifact-name-en">Lectern of Light</div>
<div class="artifact-repo">yazelin/MD2PPT-Evolution</div>
</div>
</div>
<div class="artifact-lore">
將墨跡化為幻燈(PPT),可現場投影展示。支援線上宣講模式。
</div>
<div class="artifact-effect">
Markdown → PPT,含線上簡報與投影模式,支援程式碼語法高亮。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/MD2PPT-Evolution" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 次元系 · Dimensional -->
<!-- ============================================================ -->
<h3 class="school-divider">
次元系 <span class="school-divider-en">· Dimensional Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">次元系</div>
<div class="artifact-head">
<div class="artifact-icon">🗄️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">藏影閣</h4>
<div class="artifact-name-en">Shadow Vault</div>
<div class="artifact-repo">yazelin/image-bed</div>
</div>
</div>
<div class="artifact-lore">
個人圖像的次元書架,不佔本空間,存入神殿(GitHub Releases)異界,永不損壞。
</div>
<div class="artifact-effect">
個人圖床,圖片存放在 GitHub Releases,不佔 repo 空間。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/image-bed" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">次元系</div>
<div class="artifact-head">
<div class="artifact-icon">🔗</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">傳送門刻印</h4>
<div class="artifact-name-en">Portal Rune</div>
<div class="artifact-repo">yazelin/shorturl-worker</div>
</div>
</div>
<div class="artifact-lore">
將漫長的咒文路徑封印為短刻印,單手即可傳送冒險者。
</div>
<div class="artifact-effect">
Cloudflare Workers + KV 縮網址服務,PromptFill 專用短網址。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/shorturl-worker" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 通靈系 · Binding -->
<!-- ============================================================ -->
<h3 class="school-divider">
通靈系 <span class="school-divider-en">· Binding Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">通靈系</div>
<div class="artifact-head">
<div class="artifact-icon">🖨️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">印刻術</h4>
<div class="artifact-name-en">Inkseal</div>
<div class="artifact-repo">yazelin/printer-mcp</div>
</div>
</div>
<div class="artifact-lore">
讓精靈能直接指揮凡人印刷術(CUPS),將任何訊息化為實體。
</div>
<div class="artifact-effect">
MCP server,透過 CUPS 控制印表機。可接 Claude / Cursor 等 agent。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/printer-mcp" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">通靈系</div>
<div class="artifact-head">
<div class="artifact-icon">🏢</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">帳本監聽術</h4>
<div class="artifact-name-en">Ledger Whisper</div>
<div class="artifact-repo">yazelin/erpnext-mcp</div>
</div>
</div>
<div class="artifact-lore">
讓精靈與商會帳本(ERPNext)直接對話,問帳、記帳不必經過凡人之手。
</div>
<div class="artifact-effect">
MCP server for ERPNext REST API,讓 AI 能直接查詢、記錄商業數據。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/erpnext-mcp" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 聲術系 · Incantation -->
<!-- ============================================================ -->
<h3 class="school-divider">
聲術系 <span class="school-divider-en">· Incantation Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">聲術系</div>
<div class="artifact-head">
<div class="artifact-icon">🎙️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">詠唱辨識器</h4>
<div class="artifact-name-en">Voice Scribe</div>
<div class="artifact-repo">yazelin/asr-ime-fcitx</div>
</div>
</div>
<div class="artifact-lore">
將咒術師的口語即時記下為文字,嵌入 fcitx 輸入法,隨呼隨用。
</div>
<div class="artifact-effect">
ASR 語音輸入法,整合 fcitx framework。支援中英雙語。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/asr-ime-fcitx" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 後記 Epilogue -->
<!-- ============================================================ -->
<div class="epilogue">
<strong>關於工坊 · About this Workshop</strong><br />
此處列出的魔道具多為我與 Mori 共同打造的作品,部分為教學範例、部分為日常使用工具。
若其中任何一件對你有用,歡迎 fork、改造、延伸。<br />
新的魔道具會持續加入此處 — 這是一間還在生長的工坊。<br /><br />
<em style="color: var(--mist); font-family: var(--serif-en);">
Each artifact here was forged in collaboration with Mori, my contracted spirit.
Some are teaching examples, others are daily tools. All are open. Take what you need.
</em>
</div>
</div>
</article>
</article>
assets/css/site.css(完整檔;design tokens + 全站基礎 + 之後 task 會用到的 landing/blog/post 區塊都先放齊):
:root {
--fg: #1c1917; --muted: #6b6560; --bg: #fdfcfa; --card: #ffffff;
--border: #e8e5e1; --accent: #047857; --accent-weak: #ecfdf5;
--maxw: 46rem; --maxw-wide: 68rem;
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0; background: var(--bg); color: var(--fg);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans TC", "Microsoft JhengHei", sans-serif;
line-height: 1.75; font-size: 17px; letter-spacing: 0.01em;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
img { max-width: 100%; height: auto; }
h1, h2, h3 { line-height: 1.3; letter-spacing: 0.02em; }
code { background: var(--accent-weak); padding: 0.15em 0.4em; border-radius: 4px; font-size: 0.9em; }
pre { background: #1c1917; color: #e8e5e1; padding: 1rem 1.25rem; border-radius: 8px; overflow-x: auto; line-height: 1.6; }
pre code { background: none; padding: 0; color: inherit; }
blockquote { margin: 1.5rem 0; padding: 0.5rem 1.25rem; border-left: 3px solid var(--accent); background: var(--accent-weak); border-radius: 0 8px 8px 0; }
.container { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem; }
.container-wide { max-width: var(--maxw-wide); margin: 0 auto; padding: 0 1.25rem; }
/* nav */
.site-nav { display: flex; justify-content: space-between; align-items: center; max-width: var(--maxw-wide); margin: 0 auto; padding: 1rem 1.25rem; }
.site-nav .brand { font-weight: 700; font-size: 1.1rem; color: var(--fg); }
.site-nav nav { display: flex; gap: 1.25rem; flex-wrap: wrap; }
.site-nav nav a { color: var(--muted); font-size: 0.95rem; }
.site-nav nav a.active, .site-nav nav a:hover { color: var(--accent); text-decoration: none; }
/* footer */
.site-footer { border-top: 1px solid var(--border); margin-top: 4rem; padding: 2rem 1.25rem 3rem; text-align: center; }
.footer-links { display: flex; gap: 1.5rem; justify-content: center; flex-wrap: wrap; margin-bottom: 0.75rem; }
.footer-links a { color: var(--muted); font-size: 0.95rem; }
.footer-note { color: var(--muted); font-size: 0.85rem; margin: 0; }
/* page & post */
.page, .post { padding-top: 1.5rem; }
.post-header h1 { margin-bottom: 0.25rem; }
.post-meta { color: var(--muted); font-size: 0.9rem; margin-bottom: 2rem; }
.post-meta a { color: var(--muted); }
.post-tags { margin-top: 2.5rem; display: flex; gap: 0.5rem; flex-wrap: wrap; }
.post-tags a, .tag-chip { background: var(--accent-weak); color: var(--accent); padding: 0.1em 0.7em; border-radius: 999px; font-size: 0.85rem; }
.related { border-top: 1px solid var(--border); margin-top: 3rem; padding-top: 1.5rem; }
.related h2 { font-size: 1.1rem; }
.related ul { padding-left: 1.2rem; }
/* blog list */
.post-list { list-style: none; padding: 0; }
.post-list li { border-bottom: 1px solid var(--border); padding: 1.5rem 0; }
.post-list h2 { margin: 0 0 0.35rem; font-size: 1.3rem; }
.post-list h2 a { color: var(--fg); }
.post-list h2 a:hover { color: var(--accent); }
.post-list .excerpt { color: var(--muted); margin: 0.25rem 0 0.5rem; }
.post-list .meta { color: var(--muted); font-size: 0.85rem; }
.pagination { display: flex; justify-content: space-between; padding: 2rem 0; }
#blog-search { width: 100%; padding: 0.6rem 1rem; font-size: 1rem; border: 1px solid var(--border); border-radius: 8px; margin: 1rem 0; background: var(--card); }
/* landing */
.hero { text-align: center; padding: 4.5rem 1.25rem 3.5rem; }
.hero h1 { font-size: clamp(1.8rem, 4.5vw, 2.8rem); max-width: 34ch; margin: 0 auto 1rem; }
.hero .sub { color: var(--muted); font-size: 1.1rem; max-width: 52ch; margin: 0 auto 2rem; }
.section { padding: 3.5rem 0; }
.section-title { text-align: center; font-size: 1.6rem; margin-bottom: 0.5rem; }
.section-lead { text-align: center; color: var(--muted); margin: 0 auto 2.5rem; max-width: 52ch; }
.work-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1.25rem; }
.work-card { background: var(--card); border: 1px solid var(--border); border-radius: 12px; overflow: hidden; transition: transform 0.15s, box-shadow 0.15s; }
.work-card:hover { transform: translateY(-3px); box-shadow: 0 8px 24px rgba(0,0,0,0.08); text-decoration: none; }
.work-card img { display: block; width: 100%; aspect-ratio: 16/10; object-fit: cover; border-bottom: 1px solid var(--border); }
.work-card .body { padding: 0.9rem 1.1rem 1.1rem; }
.work-card h3 { margin: 0 0 0.25rem; font-size: 1.05rem; color: var(--fg); }
.work-card p { margin: 0; color: var(--muted); font-size: 0.9rem; line-height: 1.5; }
.story { background: var(--accent-weak); border-radius: 16px; padding: 2.5rem 2rem; }
.story p { max-width: var(--maxw); margin: 0 auto 1rem; }
.featured-list { list-style: none; padding: 0; max-width: var(--maxw); margin: 0 auto; }
.featured-list li { padding: 0.9rem 0; border-bottom: 1px solid var(--border); }
.featured-list a { font-size: 1.1rem; }
.featured-list .why { color: var(--muted); font-size: 0.9rem; margin: 0.15rem 0 0; }
/* signup */
.signup { max-width: 30rem; margin: 0 auto; text-align: center; }
.signup form { display: flex; gap: 0.5rem; margin-top: 1rem; }
.signup input[type=email] { flex: 1; padding: 0.65rem 1rem; border: 1px solid var(--border); border-radius: 8px; font-size: 1rem; }
.signup button { background: var(--accent); color: #fff; border: 0; border-radius: 8px; padding: 0.65rem 1.4rem; font-size: 1rem; cursor: pointer; }
.signup button:hover { filter: brightness(1.1); }
.signup .msg { min-height: 1.5rem; font-size: 0.9rem; color: var(--accent); margin-top: 0.5rem; }
.signup .hp { position: absolute; left: -9999px; }
@media (max-width: 640px) {
.site-nav { flex-direction: column; gap: 0.5rem; }
.hero { padding: 3rem 1.25rem 2.5rem; }
.signup form { flex-direction: column; }
}
- Step 5: 本機 build 驗證
cd /home/ct/yazelin.github.io && bundle install && bundle exec jekyll serve --port 4400 &
curl -s http://localhost:4400/about/ | grep -c "site-nav"
Expected: build 成功、about 頁有新 nav。若 bundle 環境有問題,改用 gem install jekyll jekyll-paginate jekyll-feed jekyll-sitemap 后 jekyll serve;都不行就以 branch push 到 GitHub 讓 Pages build,用線上 preview 驗(pages 只認 master,則暫時用 Actions 觀察 build 結果——實作者擇一,但必須真的看到 build 成功)。
注意:此時首頁(index.html layout: home)會壞——home layout 到 Task 4 才建,本 task 只驗 page 類頁面。
- Step 6: Commit
git add -A && git commit -m "feat(theme): 移除 Beautiful Jekyll,自建 default/page layouts 與全站樣式
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>"
Task 3: post layout(giscus/相關文章/文末 CTA/TOC)
Files:
- Create:
_layouts/post.html,_includes/giscus.html,_includes/signup-form.html
Interfaces:
- Consumes: Task 2 的 default layout 與 CSS class(
.post/.post-meta/.post-tags/.related/.signup)。 -
Produces:
signup-form.html接受include.source(字串,預設blog),Task 5 首頁重用。 - Step 1: 寫 signup-form.html
<div class="signup">
<h3>訂閱電子報</h3>
<p class="section-lead">甲方思維、AI 發包實戰、新作品上線通知。不灌水。</p>
<form data-signup data-source="blog">
<input type="email" name="email" placeholder="you@example.com" required>
<input type="text" name="company" class="hp" tabindex="-1" autocomplete="off">
<button type="submit">訂閱</button>
</form>
<p class="msg" aria-live="polite"></p>
</div>
<script>
document.querySelectorAll('form[data-signup]').forEach(function (f) {
if (f.dataset.bound) return; f.dataset.bound = '1';
f.addEventListener('submit', async function (e) {
e.preventDefault();
var msg = f.parentElement.querySelector('.msg');
msg.textContent = '送出中…';
try {
var r = await fetch('https://k-rider-api.yazelinj303.workers.dev/api/signup', {
method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({ email: f.email.value, company: f.company.value, source: f.dataset.source })
});
var d = await r.json();
msg.textContent = d.ok ? (d.already ? '這個 email 已經訂閱過了。' : '訂閱成功,之後見。') : '出了點問題,再試一次。';
if (d.ok && !d.already) f.reset();
} catch (err) { msg.textContent = '網路怪怪的,稍後再試。'; }
});
});
</script>
- Step 2: 寫 giscus.html(值全部從 _config.yml 的 giscus 區塊來)
- Step 3: 寫 post.html
---
layout: default
---
<article class="post container">
<header class="post-header">
<h1>Blog 改版(個人品牌主站)Implementation Plan</h1>
<p class="post-meta"></p>
</header>
<article class="page container">
<h1>森之魔道具工坊</h1>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="" />
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;500;600&family=IM+Fell+English:ital@0;1&family=Noto+Serif+TC:wght@400;500;600;700&display=swap" rel="stylesheet" />
<style>
/* ==========================================================
森之魔道具工坊 · Scoped Styles
僅作用於 .forest-workshop 內,不影響其他頁面
========================================================== */
.forest-workshop {
--forest-deep: #1e3328;
--forest-mid: #2d4a3e;
--forest-moss: #4a6b5a;
--parchment: #f5efe0;
--parchment-dark: #e8dcc0;
--ink: #1a1a1a;
--ink-soft: #4a4a4a;
--gold: #c9a24d;
--gold-soft: #d4b572;
--mist: #6b8a99;
--serif-en: "Cinzel", "IM Fell English", Georgia, serif;
--serif-zh: "Noto Serif TC", "PingFang TC", "Source Han Serif TC", serif;
}
/* --- 序章 Prologue --- */
.forest-workshop .prologue {
margin: 2rem auto 3rem;
padding: 3rem 2rem 2.5rem;
background:
radial-gradient(ellipse at top, rgba(201,162,77,0.12) 0%, transparent 60%),
linear-gradient(135deg, var(--forest-deep) 0%, var(--forest-mid) 100%);
color: var(--parchment);
border-radius: 6px;
text-align: center;
box-shadow: 0 4px 20px rgba(0,0,0,0.25);
position: relative;
overflow: hidden;
}
.forest-workshop .prologue::before {
content: "";
position: absolute;
top: 0; left: 50%;
transform: translateX(-50%);
width: 1px; height: 40px;
background: linear-gradient(to bottom, transparent, var(--gold), transparent);
}
.forest-workshop .prologue-mark {
font-family: var(--serif-en);
font-size: 0.75rem;
letter-spacing: 0.4em;
color: var(--gold);
text-transform: uppercase;
margin-bottom: 1rem;
}
.forest-workshop .prologue-title {
font-family: var(--serif-zh);
font-size: 1.75rem;
font-weight: 400;
line-height: 1.6;
margin: 0.5rem auto 1.2rem;
max-width: 680px;
color: var(--parchment);
}
.forest-workshop .prologue-lore {
font-family: var(--serif-zh);
font-size: 1rem;
line-height: 1.9;
max-width: 620px;
margin: 0 auto;
color: var(--parchment-dark);
opacity: 0.92;
}
.forest-workshop .prologue-en {
margin-top: 1.5rem;
padding-top: 1.2rem;
border-top: 1px solid rgba(201,162,77,0.3);
font-family: var(--serif-en);
font-style: italic;
font-size: 0.9rem;
color: var(--gold-soft);
letter-spacing: 0.05em;
}
/* --- 系別標籤 --- */
.forest-workshop .school-filter {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
justify-content: center;
margin: 2rem 0;
font-family: var(--serif-zh);
font-size: 0.85rem;
}
.forest-workshop .school-tag {
padding: 0.3rem 0.85rem;
background: var(--parchment);
color: var(--forest-deep);
border: 1px solid var(--parchment-dark);
border-radius: 999px;
font-weight: 500;
}
/* --- 魔道具卡片 --- */
.forest-workshop .codex-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1.5rem;
margin: 2rem 0 3rem;
}
.forest-workshop .artifact {
background: var(--parchment);
border: 1px solid var(--parchment-dark);
border-radius: 4px;
padding: 1.5rem 1.4rem 1.3rem;
position: relative;
transition: transform 0.2s ease, box-shadow 0.2s ease;
box-shadow: 0 1px 3px rgba(30,51,40,0.08);
}
.forest-workshop .artifact:hover {
transform: translateY(-3px);
box-shadow: 0 6px 18px rgba(30,51,40,0.15);
}
.forest-workshop .artifact-head {
display: flex;
align-items: flex-start;
gap: 0.8rem;
margin-bottom: 0.9rem;
}
.forest-workshop .artifact-icon {
font-size: 2rem;
line-height: 1;
flex-shrink: 0;
margin-top: 2px;
}
.forest-workshop .artifact-names {
flex: 1;
min-width: 0;
}
.forest-workshop .artifact-name-zh {
font-family: var(--serif-zh);
font-size: 1.25rem;
font-weight: 600;
color: var(--forest-deep);
line-height: 1.3;
margin: 0;
}
.forest-workshop .artifact-name-en {
font-family: var(--serif-en);
font-style: italic;
font-size: 0.8rem;
color: var(--ink-soft);
letter-spacing: 0.03em;
margin-top: 2px;
}
.forest-workshop .artifact-repo {
font-family: Menlo, "Fira Code", Consolas, monospace;
font-size: 0.72rem;
color: var(--mist);
margin-top: 1px;
}
.forest-workshop .artifact-school {
position: absolute;
top: 1rem;
right: 1rem;
font-family: var(--serif-zh);
font-size: 0.7rem;
color: var(--gold);
letter-spacing: 0.08em;
background: var(--forest-deep);
padding: 0.2rem 0.55rem;
border-radius: 2px;
}
.forest-workshop .artifact-lore {
font-family: var(--serif-zh);
font-size: 0.88rem;
line-height: 1.75;
color: var(--ink);
margin: 0.8rem 0 1rem;
padding-left: 0.8rem;
border-left: 2px solid var(--gold-soft);
font-style: italic;
}
.forest-workshop .artifact-effect {
font-family: var(--serif-zh);
font-size: 0.85rem;
line-height: 1.7;
color: var(--ink-soft);
margin: 0.6rem 0 1.2rem;
}
.forest-workshop .artifact-actions {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin-top: 1rem;
padding-top: 0.9rem;
border-top: 1px dashed var(--parchment-dark);
}
.forest-workshop .artifact-btn {
font-family: var(--serif-en);
font-size: 0.75rem;
letter-spacing: 0.1em;
text-transform: uppercase;
padding: 0.4rem 0.9rem;
border-radius: 2px;
text-decoration: none;
transition: all 0.15s ease;
border: 1px solid transparent;
}
.forest-workshop .artifact-btn.primary {
background: var(--forest-deep);
color: var(--gold-soft);
border-color: var(--forest-deep);
}
.forest-workshop .artifact-btn.primary:hover {
background: var(--forest-mid);
color: var(--parchment);
text-decoration: none;
}
.forest-workshop .artifact-btn.secondary {
background: transparent;
color: var(--forest-deep);
border-color: var(--forest-moss);
}
.forest-workshop .artifact-btn.secondary:hover {
background: var(--forest-moss);
color: var(--parchment);
text-decoration: none;
}
/* --- 系別區分 --- */
.forest-workshop .school-divider {
font-family: var(--serif-zh);
font-size: 0.95rem;
color: var(--forest-mid);
margin: 2.5rem 0 1rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--parchment-dark);
display: flex;
align-items: baseline;
gap: 0.8rem;
}
.forest-workshop .school-divider-en {
font-family: var(--serif-en);
font-style: italic;
font-size: 0.8rem;
color: var(--mist);
letter-spacing: 0.05em;
}
/* --- 後記 --- */
.forest-workshop .epilogue {
margin: 3rem 0 1rem;
padding: 2rem;
background: var(--parchment);
border: 1px solid var(--parchment-dark);
border-left: 3px solid var(--gold);
border-radius: 2px;
font-family: var(--serif-zh);
font-size: 0.9rem;
line-height: 1.85;
color: var(--ink-soft);
}
.forest-workshop .epilogue strong {
color: var(--forest-deep);
}
@media (max-width: 640px) {
.forest-workshop .prologue-title { font-size: 1.4rem; }
.forest-workshop .prologue { padding: 2rem 1.2rem 1.8rem; }
.forest-workshop .codex-grid { grid-template-columns: 1fr; }
}
</style>
<div class="forest-workshop">
<section class="prologue">
<div class="prologue-mark">— Codex of Artifacts —</div>
<h2 class="prologue-title">森之大魔導師打造的魔道具,<br />自由取閱,自由傳承。</h2>
<p class="prologue-lore">
此地名為「森之工坊」,是我與契約精靈 Mori 共同打造魔道具的所在。<br />
每件魔道具皆為多年修練與失敗累積而成,開放給願意與 AI 同行的冒險者自由領取。<br />
願你能從中取得一件相合的器物,帶回你自己的森林。
</p>
<div class="prologue-en">
Artifacts crafted in the quiet of the woods, by the Archmage and the spirit Mori.<br />
Take what speaks to you. Carry it into your own forest.
</div>
</section>
<!-- ============================================================ -->
<!-- 召喚系 · Summoning -->
<!-- ============================================================ -->
<h3 class="school-divider">
召喚系 <span class="school-divider-en">· Summoning Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">召喚系</div>
<div class="artifact-head">
<div class="artifact-icon">🐱</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">時之貓苑</h4>
<div class="artifact-name-en">Chronocat</div>
<div class="artifact-repo">yazelin/catime</div>
</div>
</div>
<div class="artifact-lore">
每至整點,森林深處便浮現一隻新貓。有 103 種藝術流派的皮毛,各自攜帶一段小故事。
</div>
<div class="artifact-effect">
GitHub Actions 每小時呼叫 Gemini 生成 AI 貓圖,發至 Telegram 頻道與 Gallery 歸檔。已累積數千隻。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://yazelin.github.io/catime/" target="_blank">Gallery</a>
<a class="artifact-btn secondary" href="https://t.me/catime_yaze" target="_blank">Telegram</a>
<a class="artifact-btn secondary" href="https://github.com/yazelin/catime" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 幻術系 · Illusion -->
<!-- ============================================================ -->
<h3 class="school-divider">
幻術系 <span class="school-divider-en">· Illusion Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">幻術系</div>
<div class="artifact-head">
<div class="artifact-icon">🎰</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">心情占卜儀</h4>
<div class="artifact-name-en">Moodscopia</div>
<div class="artifact-repo">yazelin/emoji-slot-machine</div>
</div>
</div>
<div class="artifact-lore">
取冒險者一張自拍,映入九宮魔陣,瞬間化為九格誇張表情。貼入 FB 結界後自動循環,手指一碰便停在隨機一格。
</div>
<div class="artifact-effect">
Cloudflare Worker + Vertex AI。自拍 → 3×3 emoji 臉 → 可嵌入 FB 的拉霸影片。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://yazelin.github.io/emoji-slot-machine/" target="_blank">試用</a>
<a class="artifact-btn secondary" href="https://github.com/yazelin/emoji-slot-machine" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 創造系 · Creation -->
<!-- ============================================================ -->
<h3 class="school-divider">
創造系 <span class="school-divider-en">· Creation Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">創造系</div>
<div class="artifact-head">
<div class="artifact-icon">🎨</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">繪影魔陣</h4>
<div class="artifact-name-en">Portrait Circle</div>
<div class="artifact-repo">yazelin/nanobanana-pro</div>
</div>
</div>
<div class="artifact-lore">
能自動在多個創造之神之間換手,繪製出最切合意念的圖像。若一神拒絕,轉請下一神。
</div>
<div class="artifact-effect">
AI 圖像生成 Agent Skill,支援自動模型 fallback。可接入 Claude Code 或其他 agent 生態。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/nanobanana-pro" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">創造系</div>
<div class="artifact-head">
<div class="artifact-icon">🖼️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">繪風傳承書</h4>
<div class="artifact-name-en">Painted Tome</div>
<div class="artifact-repo">yazelin/gemini-web</div>
</div>
</div>
<div class="artifact-lore">
讓神靈(Gemini)在此作畫,支援繁體符文、不留神印(自動去浮水印)。
</div>
<div class="artifact-effect">
Gemini AI 圖片生成 CLI + HTTP API。繁中原生、浮水印自動處理。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/gemini-web" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">創造系</div>
<div class="artifact-head">
<div class="artifact-icon">📜</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">咒文起稿匣</h4>
<div class="artifact-name-en">Spell Composer</div>
<div class="artifact-repo">yazelin/PromptFill</div>
</div>
</div>
<div class="artifact-lore">
為繪影魔陣準備咒文草稿的輔助匣。結構化、省去散亂冥想。
</div>
<div class="artifact-effect">
結構化 AI 繪圖 prompt 產生器,適配 Nano Banana Pro 系列。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/PromptFill" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 煉金系 · Transmutation -->
<!-- ============================================================ -->
<h3 class="school-divider">
煉金系 <span class="school-divider-en">· Transmutation Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">煉金系</div>
<div class="artifact-head">
<div class="artifact-icon">📄</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">卷軸化術</h4>
<div class="artifact-name-en">Scribe's Hand</div>
<div class="artifact-repo">yazelin/MD2DOC-Evolution</div>
</div>
</div>
<div class="artifact-lore">
將凡人墨跡(Markdown)煉成正式卷軸(Word)。格式可客製化,符合任何行會規範。
</div>
<div class="artifact-effect">
Markdown → Word,支援企業自訂樣式、表格、圖片、目錄。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/MD2DOC-Evolution" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">煉金系</div>
<div class="artifact-head">
<div class="artifact-icon">📽️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">簡報祭壇</h4>
<div class="artifact-name-en">Lectern of Light</div>
<div class="artifact-repo">yazelin/MD2PPT-Evolution</div>
</div>
</div>
<div class="artifact-lore">
將墨跡化為幻燈(PPT),可現場投影展示。支援線上宣講模式。
</div>
<div class="artifact-effect">
Markdown → PPT,含線上簡報與投影模式,支援程式碼語法高亮。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/MD2PPT-Evolution" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 次元系 · Dimensional -->
<!-- ============================================================ -->
<h3 class="school-divider">
次元系 <span class="school-divider-en">· Dimensional Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">次元系</div>
<div class="artifact-head">
<div class="artifact-icon">🗄️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">藏影閣</h4>
<div class="artifact-name-en">Shadow Vault</div>
<div class="artifact-repo">yazelin/image-bed</div>
</div>
</div>
<div class="artifact-lore">
個人圖像的次元書架,不佔本空間,存入神殿(GitHub Releases)異界,永不損壞。
</div>
<div class="artifact-effect">
個人圖床,圖片存放在 GitHub Releases,不佔 repo 空間。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/image-bed" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">次元系</div>
<div class="artifact-head">
<div class="artifact-icon">🔗</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">傳送門刻印</h4>
<div class="artifact-name-en">Portal Rune</div>
<div class="artifact-repo">yazelin/shorturl-worker</div>
</div>
</div>
<div class="artifact-lore">
將漫長的咒文路徑封印為短刻印,單手即可傳送冒險者。
</div>
<div class="artifact-effect">
Cloudflare Workers + KV 縮網址服務,PromptFill 專用短網址。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/shorturl-worker" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 通靈系 · Binding -->
<!-- ============================================================ -->
<h3 class="school-divider">
通靈系 <span class="school-divider-en">· Binding Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">通靈系</div>
<div class="artifact-head">
<div class="artifact-icon">🖨️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">印刻術</h4>
<div class="artifact-name-en">Inkseal</div>
<div class="artifact-repo">yazelin/printer-mcp</div>
</div>
</div>
<div class="artifact-lore">
讓精靈能直接指揮凡人印刷術(CUPS),將任何訊息化為實體。
</div>
<div class="artifact-effect">
MCP server,透過 CUPS 控制印表機。可接 Claude / Cursor 等 agent。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/printer-mcp" target="_blank">Source</a>
</div>
</article>
<article class="artifact">
<div class="artifact-school">通靈系</div>
<div class="artifact-head">
<div class="artifact-icon">🏢</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">帳本監聽術</h4>
<div class="artifact-name-en">Ledger Whisper</div>
<div class="artifact-repo">yazelin/erpnext-mcp</div>
</div>
</div>
<div class="artifact-lore">
讓精靈與商會帳本(ERPNext)直接對話,問帳、記帳不必經過凡人之手。
</div>
<div class="artifact-effect">
MCP server for ERPNext REST API,讓 AI 能直接查詢、記錄商業數據。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/erpnext-mcp" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 聲術系 · Incantation -->
<!-- ============================================================ -->
<h3 class="school-divider">
聲術系 <span class="school-divider-en">· Incantation Arts</span>
</h3>
<div class="codex-grid">
<article class="artifact">
<div class="artifact-school">聲術系</div>
<div class="artifact-head">
<div class="artifact-icon">🎙️</div>
<div class="artifact-names">
<h4 class="artifact-name-zh">詠唱辨識器</h4>
<div class="artifact-name-en">Voice Scribe</div>
<div class="artifact-repo">yazelin/asr-ime-fcitx</div>
</div>
</div>
<div class="artifact-lore">
將咒術師的口語即時記下為文字,嵌入 fcitx 輸入法,隨呼隨用。
</div>
<div class="artifact-effect">
ASR 語音輸入法,整合 fcitx framework。支援中英雙語。
</div>
<div class="artifact-actions">
<a class="artifact-btn primary" href="https://github.com/yazelin/asr-ime-fcitx" target="_blank">Source</a>
</div>
</article>
</div>
<!-- ============================================================ -->
<!-- 後記 Epilogue -->
<!-- ============================================================ -->
<div class="epilogue">
<strong>關於工坊 · About this Workshop</strong><br />
此處列出的魔道具多為我與 Mori 共同打造的作品,部分為教學範例、部分為日常使用工具。
若其中任何一件對你有用,歡迎 fork、改造、延伸。<br />
新的魔道具會持續加入此處 — 這是一間還在生長的工坊。<br /><br />
<em style="color: var(--mist); font-family: var(--serif-en);">
Each artifact here was forged in collaboration with Mori, my contracted spirit.
Some are teaching examples, others are daily tools. All are open. Take what you need.
</em>
</div>
</div>
</article>
<div class="related"><div class="signup">
<h2>保持聯絡</h2>
<p class="section-lead">留個 email,有新文章或新作品上線時告訴你。</p>
<form data-signup data-source="post">
<input type="email" name="email" placeholder="you@example.com" aria-label="Email" required>
<input type="text" name="company" class="hp" tabindex="-1" autocomplete="off" aria-hidden="true">
<button type="submit">訂閱</button>
</form>
<p class="msg" aria-live="polite"></p>
</div>
<script>
document.querySelectorAll('form[data-signup]').forEach(function (f) {
if (f.dataset.bound) return; f.dataset.bound = '1';
f.addEventListener('submit', async function (e) {
e.preventDefault();
var msg = f.parentElement.querySelector('.msg');
msg.textContent = '送出中…';
try {
var r = await fetch('https://k-rider-api.yazelinj303.workers.dev/signup', {
method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({ email: f.email.value, company: f.company.value, source: f.dataset.source })
});
var d = await r.json();
msg.textContent = d.ok ? (d.already ? '這個 email 已經訂閱過了。' : '訂閱成功,之後見。') : '出了點問題,再試一次。';
if (d.ok && !d.already) f.reset();
} catch (err) { msg.textContent = '網路怪怪的,稍後再試。'; }
});
});
</script>
</div>
</article>
注意:source="post" 需回 Task 1 把 'post' 一併加進白名單——實作 Task 1 時白名單直接寫 ['result','about','home','blog','post']。
TOC:kramdown 已支援,長文由文章自行加 * toc\n{:toc},不做 JS 自動 TOC(刻意簡化,YAGNI)。
- Step 4: 本機驗證三篇代表文章
curl -s http://localhost:4400/ai/2026/07/15/taigi-hakka-dictionaries.html | grep -c "giscus\|post-tags"
curl -s http://localhost:4400/2015/11/03/icemore.html | head -5
Expected: 新舊文章都渲染、URL 與現行線上一致(先 curl -sI https://yazelin.github.io/... 對照現行 URL 再驗本機同 path)、giscus script 有出現、相關文章有列出。無 categories 的舊文 path 不含 category 段——以線上現況為準逐篇對照。
- Step 5: Commit
git add -A && git commit -m "feat(post): 文章版型——giscus/相關文章/訂閱 CTA
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>"
Task 4: /blog 列表(分頁+搜尋)與 tags 頁沿用
Files:
- Create:
blog/index.html,_layouts/home.html(可直接把列表寫在 blog/index.html、不建 home layout——擇一,以簡為準),search.json,assets/js/blog-search.js
Interfaces:
- Consumes:
_config.yml的paginate: 10、paginate_path: "/blog/page:num/"(Task 2 已設)。 -
Produces:
/blog/分頁列表 + 即時搜尋;/search.json索引(title/url/date/tags)。 - Step 1: 寫 blog/index.html
---
layout: default
title: Blog
permalink: /blog/
---
<div class="container">
<h1>Blog</h1>
<input id="blog-search" type="search" placeholder="搜尋文章標題或 tag…" autocomplete="off">
<ul class="post-list" id="post-list">
</ul>
<ul class="post-list" id="search-results" hidden></ul>
<nav class="pagination">
<span></span>
</nav>
</div>
<script src="/assets/js/blog-search.js"></script>
(jekyll-paginate 需要 paginator 所在檔為 blog/index.html 且 paginate_path 指向 /blog/page:num/——兩者 Task 2 已就位。若 build 後 paginator 為空,把 front matter 的 permalink 拿掉再試,jekyll-paginate v1 對 permalink + paginator 並用有已知怪癖。)
- Step 2: 寫 search.json 與 blog-search.js
---
layout: null
permalink: /search.json
---
[{"title":"「長頸鹿怎麼沒有?」兩天長出兩本離線辭典","url":"/ai/2026/07/15/taigi-hakka-dictionaries.html","date":"2026-07-15","tags":"mandarin-taigi mandarin-hakka 台語 客語 教育部辭典 PWA 離線優先 甲方思維 wish-pool"},{"title":"LINE 貼圖製造機長大了:從 60 秒產一組,變成一間貼圖工作室","url":"/ai/2026/07/15/line-sticker-studio-workshop.html","date":"2026-07-15","tags":"LINE 貼圖 line-sticker-studio Gemini Cloudflare Workers Turnstile PWA canvas IndexedDB Chroma Key LINE Creators Market"},{"title":"這個 demo 裡 AI 一行新程式都沒寫,但我覺得它比寫程式的 demo 更值得看","url":"/ai/2026/07/09/kb-driven-dev-flow.html","date":"2026-07-09","tags":"知識庫 Claude Code CTOS LINE Bot CLI 甲方思維 AI 開發流程"},{"title":"AI 願望池:一個人許願,一群人(或 AI)幫它成真","url":"/ai/2026/07/07/wish-pool.html","date":"2026-07-07","tags":"wish-pool 許願池 AI 社團 Cloudflare Workers Groq 甲方思維 AI 應用"},{"title":"每週自動把 FB 相簿新風格併進風格庫,人只負責按一下 merge","url":"/ai/2026/07/06/fb-photo-style-consolidate.html","date":"2026-07-06","tags":"fb-photo-dl catime 自動化 cron AI 應用"},{"title":"真。AI。咏唱魔法 系列:完整目錄","url":"/index/2026/07/06/ai-chant-magic-index.html","date":"2026-07-06","tags":"ai-chant-magic 咏唱魔法 目錄 系列文章 遊戲"},{"title":"真。AI。咏唱魔法(續):四關全開,還長出了元素反應與共鳴詠唱","url":"/ai/2026/07/06/ai-chant-magic-four-worlds.html","date":"2026-07-06","tags":"ai-chant-magic 咏唱魔法 Web Speech API 語音辨識 多人連線 甲方思維 遊戲 AI 應用"},{"title":"把一個 2003 年的 IIS 老站,重建成能撐 2 萬件商品的現代電商站","url":"/ai/2026/06/26/fishtool-tw.html","date":"2026-06-26","tags":"fishtool-tw Astro Vercel Cloudflare Workers 電商 老站重建 AI 應用"},{"title":"拍一張零件照片,AI 建模、再渲染成產品圖","url":"/ai/2026/06/26/cad-agent-render-studio.html","date":"2026-06-26","tags":"cad-agent render-studio FreeCAD Blender 3D建模 AI 應用 AgentOS"},{"title":"真。AI。咏唱魔法 —— 用嘴施法的生存遊戲(序章)","url":"/ai/%E5%AF%A6%E9%A9%97/2026/06/26/ai-chant-magic-intro.html","date":"2026-06-26","tags":"ai-chant-magic Web Speech API 語音辨識 多人連線 AI 甲方思維"},{"title":"從一張赤壁,長成一台戰場引擎 —— 換一份資料就換一場戰役","url":"/ai/%E5%AF%A6%E9%A9%97/2026/06/24/battlefield-editor.html","date":"2026-06-24","tags":"battlefield-editor 3D WebGL 歷史戰場 資料驅動 AI 甲方思維 三國"},{"title":"用 AI 兩天做出 790 題的 iPAS 模擬考站(這不是遊戲)","url":"/ai/%E9%96%8B%E7%99%BC%E5%B7%A5%E5%85%B7/2026/06/23/ipas-ai-quiz.html","date":"2026-06-23","tags":"iPAS PWA 純前端 Cloudflare Workers AI 甲方思維"},{"title":"整包都 precache 了,標題畫面還是頓 —— 快取救得了下載,救不了 CPU","url":"/ai/%E9%96%8B%E7%99%BC%E5%B7%A5%E5%85%B7/2026/06/21/pwa-startup-priority.html","date":"2026-06-21","tags":"PWA Performance Code Splitting Web Font vanilla JS AI 甲方思維"},{"title":"我睡覺,它整晚加城市 —— 怎麼排班讓 AI 自己做完一整夜的內容","url":"/ai/%E9%96%8B%E7%99%BC%E5%B7%A5%E5%85%B7/2026/06/19/ai-overnight-loop.html","date":"2026-06-19","tags":"AI 甲方思維 Automation bash Claude Code autopilot"},{"title":"離線能開只是及格 —— 一份 Service Worker 裡的四種快取策略,跟「本機優先」怎麼做對","url":"/ai/%E9%96%8B%E7%99%BC%E5%B7%A5%E5%85%B7/2026/06/17/pwa-swr-local-first.html","date":"2026-06-17","tags":"PWA Service Worker 快取策略 vanilla JS AI 甲方思維"},{"title":"目標九宮格上線:AI 幫你填大谷翔平那張表,課程教你把它做成一條會收名單的漏斗","url":"/ai/2026/06/17/goal-grid-course.html","date":"2026-06-17","tags":"目標九宮格 goal-grid 課程 甲方思維 AI 發包法 行銷漏斗 Cloudflare Workers D1 Turnstile Groq canvas 名單漏斗 AI 應用"},{"title":"從一個點子到一個會動的股價遊戲:我怎麼用「甲方思維」發包給 AI","url":"/ai/2026/06/16/k-rider-case-study.html","date":"2026-06-16","tags":"k-rider K線騎手 課程 甲方思維 AI 發包法 開規格 下發包 做驗收 Matter.js Cloudflare Workers GitHub Actions 名單漏斗 AI 應用 Claude Code"},{"title":"Service Worker 寫好了,離線卻還是恐龍頁 —— 三個 PWA 的離線與安裝踩雷","url":"/ai/%E9%96%8B%E7%99%BC%E5%B7%A5%E5%85%B7/2026/06/15/pwa-offline-install-pitfalls.html","date":"2026-06-15","tags":"PWA Service Worker Offline vanilla JS GitHub Pages AI 甲方思維"},{"title":"給赤壁 3D 加上電影級音訊 —— 讓 AI 代聽海選 CC0 配樂、用同音字替身馴服破音字","url":"/ai/%E5%AF%A6%E9%A9%97/2026/06/13/red-cliffs-audio.html","date":"2026-06-13","tags":"red-cliffs-3d Web Audio TTS AI 代聽 CC0 甲方思維"},{"title":"一段 prompt,讓 AI 做出一個 3D 互動的赤壁之戰","url":"/ai/%E5%AF%A6%E9%A9%97/2026/06/11/red-cliffs-3d.html","date":"2026-06-11","tags":"red-cliffs-3d 赤壁之戰 三國 Three.js WebGL 3D Claude Code Fable 5 AI 生成 GitHub Pages Single HTML 程序化地形 GPU 粒子 Web Audio edge-tts TTS CC0 Freesound Gemini"},{"title":"Mori Canvas:講話,AI 幫你把會議整理成白板","url":"/ai/forest-guild/2026/06/11/mori-canvas.html","date":"2026-06-11","tags":"mori-canvas Mori Whiteboard Meeting Notes AI LLM Groq Ollama Whisper Speech-to-Text Rust yjs CRDT Konva React Self-hosted Tauri forest-guild"},{"title":"ChingTech Case Hub:把我們做過的工具,整理成一個能打開來試的入口","url":"/%E6%93%8E%E6%B7%BB%E5%B7%A5%E6%A5%AD/2026/06/10/chingtech-case-hub.html","date":"2026-06-10","tags":"ChingTech Case Hub 工業自動化 AGV RGV HMI PLC SCADA IO Mapping 產線模擬 製程模擬 CTOS GitHub Pages Tauri"},{"title":"AI 應用實作營:8 個能跑的 starter,先手刻看懂底層,再換框架放大","url":"/ai/%E9%96%8B%E7%99%BC%E5%B7%A5%E5%85%B7/2026/06/09/ai-workshop-starters.html","date":"2026-06-09","tags":"J303 AI MCP Agent LINE Bot Telegram RAG MQTT LLM Gemini FastMCP PydanticAI Starter Template 教學"},{"title":"Web Effects Collector:我把 90 個最驚艷的開源網頁特效彙整成一頁","url":"/ai/%E9%96%8B%E7%99%BC%E5%B7%A5%E5%85%B7/2026/06/08/web-effects-collector.html","date":"2026-06-08","tags":"web-effects-collector Web Effects WebGL GLSL Shader Three.js Particles Vanta tsParticles Generative Art CSS Static Site GitHub Pages 開發工具"},{"title":"行銷頁健檢器:貼個網址,30 秒看你的活動頁哪裡漏分","url":"/ai/%E9%96%8B%E7%99%BC%E5%B7%A5%E5%85%B7/2026/06/07/marketing-page-checker.html","date":"2026-06-07","tags":"marketing-page-checker OG SEO CTA Cloudflare Worker HTMLRewriter GitHub Pages 行銷頁 Landing Page JavaScript Self-hosted"},{"title":"wip:一個指令,讓 AI agent 一秒看懂每個專案做到哪","url":"/ai/%E9%96%8B%E7%99%BC%E5%B7%A5%E5%85%B7/2026/06/06/wip.html","date":"2026-06-06","tags":"wip CLI Rust AI Claude Code Codex Dev Tools Multi-repo Self-hosted"},{"title":"mori-meeting-recorder:一場會議,錄成兩份不同的記錄","url":"/ai/forest-guild/2026/06/04/mori-meeting-recorder.html","date":"2026-06-04","tags":"mori-meeting-recorder Mori Meeting Notes Whisper whisper.cpp Speech-to-Text Groq Ollama Tauri Rust Dual-track Self-hosted forest-guild"},{"title":"exchange:給多 agent、多終端時代的一個 dev 訊息信箱","url":"/ai/%E9%96%8B%E7%99%BC%E5%B7%A5%E5%85%B7/2026/06/03/exchange.html","date":"2026-06-03","tags":"exchange CLI Multi-agent Claude Code Codex Python SQLite Self-hosted Dev Tools"},{"title":"在一台電腦上養多個 Claude / Codex 帳號:cl 與 cx 兩個切換器","url":"/ai/%E9%96%8B%E7%99%BC%E5%B7%A5%E5%85%B7/2026/06/02/auth-switchers.html","date":"2026-06-02","tags":"claude-auth-switcher codex-auth-switcher Claude Code Codex CLI ChatGPT CLI 帳號切換 Auth 開發工具"},{"title":"mori-sprite-studio:給角色一個可見的身體","url":"/ai/forest-guild/2026/05/23/mori-sprite-studio.html","date":"2026-05-23","tags":"mori-sprite-studio Mori Sprite Animation AI Image Generation Character Pack Vercel Vite React TypeScript codex-image-service OpenAI gpt-image Vercel KV FastAPI Pairing forest-guild"},{"title":"Simple Icons:3000+ 品牌 SVG 圖示庫,跟 Lobehub Icons 互補","url":"/ai/2026/05/21/simple-icons.html","date":"2026-05-21","tags":"SVG Icons Simple Icons Lobehub Icons Web Design Brand Assets MIT License Frontend GitHub Pages Open Source"},{"title":"codex-image-service:把 Codex $imagegen 包成內網共用的 HTTP 服務","url":"/ai/2026/05/20/codex-image-service.html","date":"2026-05-20","tags":"ChatGPT Codex CLI FastAPI Docker Image Generation Self-hosted Homelab Bearer Token HTTP API OpenAI"},{"title":"教學:用 Codex CLI 的 $imagegen 在 Claude Code 裡生圖(吃 ChatGPT 訂閱額度)","url":"/ai/2026/05/19/claude-codex-imagegen.html","date":"2026-05-19","tags":"Claude Code Codex CLI Image Generation OpenAI ChatGPT Skill GitHub Pages Shell Script Tutorial"},{"title":"「Hey Mori」:森林精靈長出耳朵 — mori-desktop v0.6.0 喚醒生態","url":"/ai/forest-guild/2026/05/18/mori-desktop-hey-mori.html","date":"2026-05-18","tags":"Mori mori-desktop Wake Word Voice Activity Detection openWakeWord ONNX Whisper Phase 3 Tauri Rust Python forest-guild"},{"title":"yaze-journal:召喚師也得有自己的 vault","url":"/ai/forest-guild/2026/05/14/yaze-journal-init.html","date":"2026-05-14","tags":"Yaze Lin yaze-journal Knowledge Vault Cross-Device Claude Memory Symlink Git forest-guild Bidirectional"},{"title":"宿靈儀式(Dwelling Rite):怎麼把 Mori 請到你的森林","url":"/ai/forest-guild/2026/05/14/world-tree-dwelling-rite.html","date":"2026-05-14","tags":"Mori world-tree Dwelling Rite 宿靈儀式 forest-guild Ritual Level-1 AI Companion Onboarding"},{"title":"Annuli Wave 2:把 2489 行 engine.py 拆成 core / creator 兩個身體","url":"/ai/forest-guild/2026/05/13/annuli-wave-2-refactor.html","date":"2026-05-13","tags":"Annuli Refactor Architecture Python Module Design Modular Monolith Memory Reflection Flask forest-guild"},{"title":"mori-desktop Phase 1:給森林精靈打造一個 Tauri 桌面身體","url":"/ai/forest-guild/2026/05/06/mori-desktop-phase1.html","date":"2026-05-06","tags":"Mori mori-desktop Tauri Rust React Cross-platform Desktop App Architecture forest-guild Phase 1"},{"title":"AI 塔羅心靈陪伴站:把信義公益的提案做出來","url":"/ai/2026/04/30/ai-tarot-companion.html","date":"2026-04-30","tags":"Cloudflare Workers Cloudflare D1 Groq Whisper Llama OpenRouter Gemini OpenCC Service Worker Web Speech API GitHub Pages Prompt Engineering Elderly UX"},{"title":"LINE 貼圖製造機:一張角色圖變一整套可上架的 LINE 貼圖","url":"/ai/2026/04/28/line-sticker-studio.html","date":"2026-04-28","tags":"Gemini Cloudflare Workers LINE Login OAuth PKCE Cloudflare KV Prompt Engineering Chroma Key Canvas IndexedDB JavaScript Vertex AI LINE Creators Market"},{"title":"森之召喚師工坊:我造了一個異世界,把我自己和 Mori 寫進去住","url":"/ai/forest-guild/2026/04/24/forest-summoner-workshop.html","date":"2026-04-24","tags":"森之召喚師工坊 world-tree Mori AI 夥伴 Claude Code Gemini CLI Codex Wunderkammer Level 0-7 異世界 召喚師"},{"title":"telegram-claude-bot:Claude Code 版的極簡 Telegram AI 機器人","url":"/ai/forest-guild/2026/04/23/telegram-claude-bot.html","date":"2026-04-23","tags":"Claude Telegram FastAPI Python CLI Vibe Coding Level-0"},{"title":"mori-journal 落腳:把 Mori 的靈魂搬到她自己的家","url":"/ai/forest-guild/2026/04/23/mori-journal-home.html","date":"2026-04-23","tags":"Mori mori-journal SOUL AI Identity Memory Vault OpenClaw Hermes Claude Code forest-guild Private Repo"},{"title":"表情拉霸機:一張自拍變一支 FB 可自動播放、點擊即停的拉霸影片","url":"/ai/2026/04/21/emoji-slot-machine.html","date":"2026-04-21","tags":"Gemini Cloudflare Workers Prompt Engineering PWA Facebook JavaScript Canvas MediaRecorder Vertex AI"},{"title":"AgentPulse:讓你的 AI CLI 工具有 Dynamic Island","url":"/ai/2026/04/16/agentpulse.html","date":"2026-04-16","tags":"Tauri Rust Cross-platform Claude Code Gemini CLI Codex Copilot Desktop App"},{"title":"Tailscale 入門:SSH Tunnel 的現代替代方案","url":"/linux/2026/04/15/tailscale-vs-ssh-tunnel.html","date":"2026-04-15","tags":"Tailscale WireGuard VPN SSH Network Security"},{"title":"SSH 資安防護指南:你的主機正在被攻擊","url":"/linux/2026/04/15/ssh-security.html","date":"2026-04-15","tags":"SSH Linux Security fail2ban Firewall 2FA"},{"title":"SSH 實戰應用場景:咖啡廳連內網、翻牆、遠端 Demo","url":"/linux/2026/04/15/ssh-scenarios.html","date":"2026-04-15","tags":"SSH Linux Network Tunnel SOCKS Proxy Port Forwarding"},{"title":"Headscale 自架指南:免費、無限裝置的 Tailscale 替代方案","url":"/linux/2026/04/15/headscale-setup.html","date":"2026-04-15","tags":"Headscale Tailscale WireGuard VPN Oracle Cloud DERP ACL Network"},{"title":"SSH 連線參數完整指南:從基礎到進階 Tunnel","url":"/linux/2026/04/14/ssh-cli-guide.html","date":"2026-04-14","tags":"SSH Linux Network Security Terminal Port Forwarding X11"},{"title":"SSH 金鑰設定完整指南:ssh-keygen 從產生到管理","url":"/linux/2026/04/13/ssh-keygen-guide.html","date":"2026-04-13","tags":"SSH Linux Security ssh-keygen Terminal"},{"title":"Annuli(歲月之環):給 AI 一個會長年輪的記憶系統","url":"/ai/forest-guild/2026/04/09/annuli-mvp.html","date":"2026-04-09","tags":"Annuli AI Memory Persona Claude Code Python Tree Rings Reflection Engine Level-2 Mori"},{"title":"OpenClaw + Claude Max 訂閱:為什麼你的 Bot 被 402 擋住?","url":"/ai/2026/04/08/openclaw-claude-cli-fix.html","date":"2026-04-08","tags":"OpenClaw Claude Code Claude Max Shell Script Reverse Engineering Bot"},{"title":"Claude Code 愚人節彩蛋 /buddy — 我幫它做了 RPG 進化系統","url":"/ai/2026/04/01/buddy-evolution.html","date":"2026-04-01","tags":"Claude Code Plugin TypeScript Next.js Supabase April Fools Open Source"},{"title":"gemini-web:用 Playwright 自動化 Gemini 圖片生成 + 去浮水印","url":"/ai/2026/03/31/gemini-web.html","date":"2026-03-31","tags":"AI Python Gemini Playwright 圖片生成 PyPI"},{"title":"ctos-lite:零註冊的 LINE Bot AI 個人助理","url":"/ai/2026/03/31/ctos-lite.html","date":"2026-03-31","tags":"AI LINE Bot Python FastAPI Claude 語音"},{"title":"AI Digest TG","url":"/2026/03/13/ai-digest-tg.html","date":"2026-03-13","tags":"專案 Telegram Cloudflare Workers GitHub Actions AI"},{"title":"AI Coding CLI 指令對照表:Claude Code vs Gemini CLI vs Codex vs Copilot CLI","url":"/ai/2026/03/12/ai-coding-cli-comparison.html","date":"2026-03-12","tags":"Claude Code Gemini CLI Codex CLI Copilot CLI CLI AI Coding MCP Comparison"},{"title":"tg-codex-bot:用 GitHub Codespace + Codex CLI 當 Telegram Bot 的運算後端","url":"/ai/2026/03/11/tg-codex-bot.html","date":"2026-03-11","tags":"GitHub Codespace Codex CLI Cloudflare Workers Telegram OpenAI App Factory"},{"title":"byok-tg-runner:用 GitHub Actions 當 AI Bot 的免費運算環境(實驗紀錄)","url":"/ai/2026/03/10/byok-tg-runner.html","date":"2026-03-10","tags":"GitHub Actions Copilot SDK BYOK Azure AI Foundry Cloudflare Workers Telegram FastAPI App Factory"},{"title":"azure-foundry-demo:用 Copilot SDK 的 BYOK 模式連接 Azure AI Foundry","url":"/ai/2026/03/09/azure-foundry-demo.html","date":"2026-03-09","tags":"Azure AI Foundry Copilot SDK BYOK Python Telegram OpenAI"},{"title":"telegram-gemini-bot:一個檔案的 Telegram AI 機器人","url":"/ai/2026/03/08/telegram-gemini-bot.html","date":"2026-03-08","tags":"Gemini Telegram FastAPI Python CLI Vibe Coding"},{"title":"telegram-copilot-bot:不用 gh-aw 的輕量版 Telegram AI 機器人","url":"/ai/2026/03/05/telegram-copilot-bot-no-ghaw.html","date":"2026-03-05","tags":"GitHub Actions Copilot Telegram Cloudflare Workers App Factory Gemini"},{"title":"aw-telegram-bot 系列目錄:從聊天機器人到 AI App Factory","url":"/index/2026/03/04/aw-telegram-bot-series-index.html","date":"2026-03-04","tags":"gh-aw GitHub Actions Copilot Telegram App Factory 目錄 系列文章"},{"title":"aw-telegram-bot v6:智慧規劃 — 省一半 Premium Request,多了 Fork 和瀏覽器測試","url":"/ai/2026/03/04/aw-telegram-bot-v6-smart-planning.html","date":"2026-03-04","tags":"gh-aw GitHub Actions Copilot Telegram App Factory automation Playwright"},{"title":"aw-telegram-bot v5:App Factory — 用 Telegram 指令讓 AI 自動建網站","url":"/ai/2026/03/04/aw-telegram-bot-v5-app-factory.html","date":"2026-03-04","tags":"gh-aw GitHub Actions Copilot Telegram App Factory automation"},{"title":"aw-telegram-bot v4:影片下載 + 使用者白名單","url":"/ai/2026/03/04/aw-telegram-bot-v4-video-download.html","date":"2026-03-04","tags":"gh-aw GitHub Actions Copilot Telegram yt-dlp safe-inputs security"},{"title":"aw-telegram-bot v3:研究模式 + 指令路由,踩了 concurrency 的坑","url":"/ai/2026/03/03/aw-telegram-bot-v3-research-mode.html","date":"2026-03-03","tags":"gh-aw GitHub Actions Copilot Telegram MCP Tavily concurrency"},{"title":"aw-telegram-bot v2:加上 AI 繪圖功能,踩了 Docker container 的坑","url":"/ai/2026/03/03/aw-telegram-bot-v2-image-generation.html","date":"2026-03-03","tags":"gh-aw GitHub Actions Copilot Telegram MCP nanobanana Gemini Docker"},{"title":"aw-telegram-bot:用 GitHub Agentic Workflows 做 Telegram 聊天機器人","url":"/ai/2026/03/03/aw-telegram-bot-v1-basic-chatbot.html","date":"2026-03-03","tags":"gh-aw GitHub Actions Copilot Telegram Cloudflare Workers MCP safe-inputs"},{"title":"asr-ime-fcitx 更新:Shift+F8 語音指令、砍掉 Vosk、簡化設定","url":"/chingtech%20os/2026/03/03/asr-ime-fcitx-v2.html","date":"2026-03-03","tags":"ASR Fcitx5 IME Speech-to-Text Whisper Python C++ LLM copilot"},{"title":"20 分鐘讀懂改變 AI 的論文:Attention Is All You Need 核心概念全解析","url":"/ai/%E6%8A%80%E8%A1%93%E8%A7%A3%E6%9E%90/2026/02/16/attention-is-all-you-need-explained.html","date":"2026-02-16","tags":"AI Transformer Attention 論文解析 NLP GPT BERT 深度學習"},{"title":"Mosky 的 AI 寫程式心法:用 1% 關鍵知識跨過 99% 的坑","url":"/ai/%E8%A8%AA%E8%AB%87/2026/02/15/mosky-ai-coding-1percent-knowledge.html","date":"2026-02-15","tags":"AI Mosky Prompt Engineering AI 寫程式 測試 Code Review Google Apps Script"},{"title":"他開車也在寫程式,AI 做的產品還被買走了?海總理的 AI 開發實戰","url":"/ai/%E8%A8%AA%E8%AB%87/2026/02/15/hai-interview-ai-development.html","date":"2026-02-15","tags":"AI 開發方法論 Snapshot Claude Code 訪談 海總理 USPACE 測試 Indie Hacker"},{"title":"asr-ime-fcitx:用講的打字,Fcitx5 原生語音輸入法","url":"/chingtech%20os/2026/02/14/asr-ime-fcitx.html","date":"2026-02-14","tags":"ASR Fcitx5 IME Speech-to-Text Whisper Python C++ FIFO LLM copilot"},{"title":"onebot:一天從零到 v0.2.0 的統一 Bot 閘道","url":"/ai/2026/02/12/onebot-unified-gateway.html","date":"2026-02-12","tags":"onebot ACP Telegram LINE FastAPI Python MCP Skills"},{"title":"6 個 Commits 修了半個系統","url":"/chingtech%20os/2026/02/11/ctos-daily-roundup.html","date":"2026-02-11","tags":"CTOS SkillHub ClawHub aiohttp Claude 重構"},{"title":"12 個 PR、90 個 Commits、1 個 Telegram 頻道","url":"/ai/2026/02/11/catime-telegram-and-rwd.html","date":"2026-02-11","tags":"Catime AI Telegram RWD GitHub Actions 自動化"},{"title":"Vite 遷移踩坑記 — IIFE 不是 ES Module","url":"/%E5%89%8D%E7%AB%AF%E9%96%8B%E7%99%BC/2026/02/10/vite-migration-lessons.html","date":"2026-02-10","tags":"Vite JavaScript IIFE ES Module 前端 踩坑記"},{"title":"ChingTech OS:SkillHub 雙來源整合 — 從 Feature Flag 到並行搜尋","url":"/chingtech%20os/2026/02/10/ctos-skillhub-dual-source.html","date":"2026-02-10","tags":"ChingTech OS SkillHub ClawHub FastAPI asyncio Python"},{"title":"Copilot 多模型 Code Review — 讓 AI 互相挑錯","url":"/ai%20%E5%B7%A5%E5%85%B7/2026/02/10/copilot-multi-model-review.html","date":"2026-02-10","tags":"GitHub Copilot Claude GPT Gemini Code Review AI 多模型"},{"title":"我的第一個 ClawHub Skill:nanobanana-pro-fallback","url":"/ai/2026/02/09/nanobanana-pro-clawhub-skill.html","date":"2026-02-09","tags":"ClawHub Agent Skills Gemini 圖片生成 Python OpenClaw"},{"title":"給 AI 貓取名字的學問","url":"/ai/2026/02/09/catime-character-system.html","date":"2026-02-09","tags":"Catime AI Gemini 角色設計 GitHub Actions"},{"title":"兩天 10 個 PR — 一棵樹的開發日記","url":"/ai/2026/02/09/ai-partner-dev-journal.html","date":"2026-02-09","tags":"AI OpenClaw Claude Gemini 開發流程 sub-agent Code Review Mori"},{"title":"ChingTech OS:PostgreSQL Session 與 Thread Pool 優化","url":"/chingtech%20os/2026/02/07/ctos-session-threadpool.html","date":"2026-02-07","tags":"ChingTech OS PostgreSQL Session Thread Pool 效能優化 Python 加密"},{"title":"catime 效能優化:WebP 遷移與資料架構重設計","url":"/ai/2026/02/07/catime-webp-optimization.html","date":"2026-02-07","tags":"AI 效能優化 WebP 前端 catime JavaScript"},{"title":"ralph-any:打造通用 BYOK Coding Agent","url":"/claude%20code/2026/02/06/ralph-any-byok-agent.html","date":"2026-02-06","tags":"Claude Code ACP BYOK Coding Agent AI Python Gemini"},{"title":"ChingTech OS:模組化重構","url":"/chingtech%20os/2026/02/06/ctos-modular-refactor.html","date":"2026-02-06","tags":"ChingTech OS 重構 模組化 Python 架構設計 錯誤處理"},{"title":"catime 新聞貓:用 Google Search Grounding 讓貓咪反映時事","url":"/ai/2026/02/06/catime-news-cat.html","date":"2026-02-06","tags":"AI Gemini Google Search catime Grounding Python"},{"title":"ChingTech OS:移除多租戶架構","url":"/chingtech%20os/2026/02/05/ctos-remove-multi-tenant.html","date":"2026-02-05","tags":"ChingTech OS 架構設計 重構 多租戶 Python"},{"title":"claude-code-acp-py(下):ACP Proxy 橋接多 AI 後端","url":"/claude%20code/2026/02/05/claude-code-acp-py-part2.html","date":"2026-02-05","tags":"Claude Code ACP Python Proxy Gemini Copilot SDK AI"},{"title":"Cloudflare Worker 實戰:CORS Proxy + GitHub Release 代理","url":"/serverless/2026/02/04/shorturl-worker-cors-proxy.html","date":"2026-02-04","tags":"Cloudflare Workers CORS Proxy GitHub Serverless"},{"title":"用 GitHub Release 打造免費圖床","url":"/devops/2026/02/04/image-bed-github-release.html","date":"2026-02-04","tags":"GitHub Cloudflare Workers 圖床 CORS DevOps"},{"title":"claude-code-acp-py(上):ACP 概念與 Client 實作","url":"/claude%20code/2026/02/04/claude-code-acp-py-part1.html","date":"2026-02-04","tags":"Claude Code ACP Python API Claude AI"},{"title":"erpnext-mcp — 讓 AI 操作你的 ERP 系統","url":"/claude%20code/2026/02/03/erpnext-mcp-server.html","date":"2026-02-03","tags":"Claude Code MCP ERPNext Python ERP PyPI"},{"title":"ChingTech OS:整合 Printer MCP 與 ERPNext MCP","url":"/chingtech%20os/2026/02/03/ctos-printer-erpnext-mcp.html","date":"2026-02-03","tags":"ChingTech OS MCP 列印 ERPNext ERP AI Python"},{"title":"catime 故事貓:為每張 AI 貓咪圖加上微故事","url":"/ai/2026/02/03/catime-story.html","date":"2026-02-03","tags":"AI Gemini catime 故事生成 Python"},{"title":"printer-mcp — 讓 AI 幫你列印的 MCP Server","url":"/claude%20code/2026/02/02/printer-mcp-server.html","date":"2026-02-02","tags":"Claude Code MCP Python 列印 PyPI"},{"title":"catime AI Prompt — 用 Gemini Flash 為每隻貓寫創意描述","url":"/ai/2026/02/02/catime-ai-prompt.html","date":"2026-02-02","tags":"AI Gemini Prompt Engineering catime Python"},{"title":"catime Gallery — 打造 kawaii 風格的貓咪展覽館","url":"/ai/2026/02/01/catime-gallery.html","date":"2026-02-01","tags":"AI GitHub Pages Frontend JavaScript catime"},{"title":"ChingTech OS:NAS 電路圖掛載與三階段非同步搜尋","url":"/chingtech%20os/2026/01/31/ctos-nas-search.html","date":"2026-01-31","tags":"ChingTech OS NAS SMB CIFS 非同步 Python 搜尋"},{"title":"ChingTech OS:Telegram Bot 完整實作","url":"/chingtech%20os/2026/01/30/ctos-telegram-bot.html","date":"2026-01-30","tags":"ChingTech OS Telegram Bot Python AI 多平台"},{"title":"catime:自動化每小時 AI 貓咪圖片生成器","url":"/ai/2026/01/30/catime-birth.html","date":"2026-01-30","tags":"AI Python GitHub Actions Gemini 圖片生成 自動化"},{"title":"ChingTech OS:多平台 Bot Adapter 重構","url":"/chingtech%20os/2026/01/29/ctos-bot-adapter.html","date":"2026-01-29","tags":"ChingTech OS 重構 設計模式 Adapter Pattern Python Line Bot Telegram"},{"title":"Telegram Bot 整合 Claude CLI + MCP 圖片生成","url":"/ai/2026/01/28/telegram-bot-claude-mcp.html","date":"2026-01-28","tags":"Telegram Bot Claude MCP Python AI Copilot SDK"},{"title":"nanobanana-py:Python 移植版 + Gemini Model Auto-Fallback","url":"/claude%20code/2026/01/28/nanobanana-py-python-port.html","date":"2026-01-28","tags":"Claude Code MCP Python Nanobanana Gemini AI"},{"title":"ChingTech OS 外部應用整合:postMessage + iframe","url":"/chingtech%20os/2026/01/27/ctos-external-app-integration.html","date":"2026-01-27","tags":"ChingTech OS postMessage iframe 整合 JavaScript Frontend"},{"title":"MD2PPT / MD2DOC 分享功能:shareToken 實作","url":"/chingtech%20os/2026/01/26/md2ppt-doc-sharetoken.html","date":"2026-01-26","tags":"ChingTech OS MD2PPT MD2DOC 分享 JavaScript Vercel"},{"title":"ChingTech OS:AI 圖片生成與記憶功能","url":"/chingtech%20os/2026/01/22/ctos-ai-image-memory.html","date":"2026-01-22","tags":"ChingTech OS AI 圖片生成 Hugging Face FLUX 記憶功能 Line Bot"},{"title":"MeterEye:AI 指示燈與警報燈偵測","url":"/computer%20vision/2026/01/20/metereye-light-detection.html","date":"2026-01-20","tags":"Computer Vision Python AI 工業自動化 MeterEye"},{"title":"ChingTech OS 多租戶架構:從設計到實作","url":"/chingtech%20os/2026/01/20/ctos-multi-tenant.html","date":"2026-01-20","tags":"ChingTech OS 多租戶 SaaS 架構設計 Python FastAPI"},{"title":"ChingTech OS:庫存管理功能開發","url":"/chingtech%20os/2026/01/16/ctos-inventory.html","date":"2026-01-16","tags":"ChingTech OS 庫存管理 廠商管理 ERP Python FastAPI"},{"title":"Nanobanana Shell 版更新:Windows 支援與 Namespace 重構","url":"/claude%20code/2026/01/15/nanobanana-shell-update.html","date":"2026-01-15","tags":"Claude Code MCP Nanobanana Shell Windows PowerShell"},{"title":"ChingTech OS:Line Bot 文件讀取功能(Word/Excel/PDF)","url":"/chingtech%20os/2026/01/15/ctos-document-reading.html","date":"2026-01-15","tags":"ChingTech OS Line Bot Python 文件處理 PDF Word Excel"},{"title":"Nanobanana:讓 Claude Code 生成圖片的 MCP Server","url":"/claude%20code/2026/01/14/nanobanana-image-generation.html","date":"2026-01-14","tags":"Claude Code MCP Nanobanana AI 圖片生成 Gemini"},{"title":"手機版 App 佈局優化實戰","url":"/chingtech%20os/2026/01/13/mobile-layout.html","date":"2026-01-13","tags":"響應式設計 CSS PWA 手機優化 ChingTech OS"},{"title":"知識庫公開分享功能實作","url":"/chingtech%20os/2026/01/12/knowledge-sharing.html","date":"2026-01-12","tags":"分享 Token FastAPI 安全性 Python ChingTech OS"},{"title":"Markdown 知識庫系統設計","url":"/chingtech%20os/2026/01/11/knowledge-base.html","date":"2026-01-11","tags":"知識庫 Markdown YAML Git Python ChingTech OS"},{"title":"專案附件與連結管理","url":"/chingtech%20os/2026/01/10/project-attachments.html","date":"2026-01-10","tags":"專案管理 MCP FastMCP NAS Python ChingTech OS"},{"title":"專案發包期程管理功能實作","url":"/chingtech%20os/2026/01/09/delivery-schedule.html","date":"2026-01-09","tags":"專案管理 MCP FastMCP Python ChingTech OS"},{"title":"內部專案管理系統:資料模型設計","url":"/chingtech%20os/2026/01/08/project-data-model.html","date":"2026-01-08","tags":"專案管理 PostgreSQL 資料庫設計 Python ChingTech OS"},{"title":"MCP 工具權限控制設計","url":"/chingtech%20os/2026/01/07/mcp-permission.html","date":"2026-01-07","tags":"MCP FastMCP 權限控制 Python ChingTech OS"},{"title":"FastMCP 實作:知識庫工具與 Scope 自動判定","url":"/chingtech%20os/2026/01/06/fastmcp-knowledge-tools.html","date":"2026-01-06","tags":"MCP FastMCP 知識庫 Python ChingTech OS"},{"title":"FastMCP 實作:專案管理工具開發","url":"/chingtech%20os/2026/01/05/fastmcp-project-tools.html","date":"2026-01-05","tags":"MCP FastMCP 專案管理 Python ChingTech OS"},{"title":"MCP 協議入門:讓 AI 使用你的工具","url":"/chingtech%20os/2026/01/04/mcp-introduction.html","date":"2026-01-04","tags":"MCP Claude AI FastMCP Python ChingTech OS"},{"title":"Line Bot 整合(五):搜尋並發送 NAS 檔案","url":"/chingtech%20os/2026/01/03/linebot-part5-nas-search.html","date":"2026-01-03","tags":"Line Bot NAS FastAPI MCP Python ChingTech OS"},{"title":"Line Bot 整合(四):群組管理與專案綁定","url":"/chingtech%20os/2026/01/02/linebot-part4-group-project.html","date":"2026-01-02","tags":"Line Bot FastAPI 專案管理 Python ChingTech OS"},{"title":"Line Bot 整合(三):與 Claude AI 對話整合","url":"/chingtech%20os/2026/01/01/linebot-part3-ai-integration.html","date":"2026-01-01","tags":"Line Bot Claude AI MCP FastAPI Python ChingTech OS"},{"title":"Line Bot 整合(二):檔案處理與 NAS 自動儲存","url":"/chingtech%20os/2025/12/31/linebot-part2-file-download.html","date":"2025-12-31","tags":"Line Bot FastAPI NAS 檔案處理 Python ChingTech OS"},{"title":"Line Bot 整合(一):Webhook 架構與訊息接收","url":"/chingtech%20os/2025/12/30/linebot-part1-webhook.html","date":"2025-12-30","tags":"Line Bot FastAPI Webhook Python ChingTech OS"},{"title":"PromptFill - 用填空的方式寫 AI Prompt","url":"/ai/%E5%B7%A5%E5%85%B7/2025/12/29/promptfill-intro.html","date":"2025-12-29","tags":"AI Prompt React 工具推薦 開源"},{"title":"用 Cloudflare Workers 免費架設短網址服務","url":"/serverless/%E6%95%99%E5%AD%B8/2025/12/29/cloudflare-workers-shorturl.html","date":"2025-12-29","tags":"Cloudflare Workers Serverless JavaScript 免費工具"},{"title":"systemd 服務配置","url":"/jaba%20ai/2025/12/25/jaba-ai-part11-systemd.html","date":"2025-12-25","tags":"Linux systemd 部署 DevOps 服務管理"},{"title":"一鍵啟動腳本設計","url":"/jaba%20ai/2025/12/25/jaba-ai-part10-deploy-script.html","date":"2025-12-25","tags":"Bash Docker 部署 DevOps 開發環境"},{"title":"菜單圖片 AI 辨識:上傳照片自動建立菜單","url":"/jaba%20ai/2025/12/24/jaba-ai-part9-menu-ocr.html","date":"2025-12-24","tags":"Python AI Claude OCR 圖片辨識 菜單管理"},{"title":"自然語言點餐:從使用者輸入到訂單建立","url":"/jaba%20ai/2025/12/24/jaba-ai-part8-natural-language.html","date":"2025-12-24","tags":"Python AI Claude 自然語言處理 LINE Bot"},{"title":"LINE 群組權限設計:從申請到審核的完整流程","url":"/jaba%20ai/2025/12/23/jaba-ai-part7-group-permission.html","date":"2025-12-23","tags":"Python LINE Bot 權限設計 狀態機"},{"title":"LINE Bot v3 SDK + FastAPI 非同步整合","url":"/jaba%20ai/2025/12/23/jaba-ai-part6-linebot-v3.html","date":"2025-12-23","tags":"Python LINE Bot FastAPI SDK v3 非同步"},{"title":"AI 日誌系統:追蹤每一次對話","url":"/jaba%20ai/2025/12/22/jaba-ai-part5-ai-logging.html","date":"2025-12-22","tags":"Python AI 日誌 可觀測性 Claude"},{"title":"Prompt Injection 防護實作","url":"/jaba%20ai/2025/12/22/jaba-ai-part4-prompt-injection.html","date":"2025-12-22","tags":"Python AI 安全 Prompt Injection Claude"},{"title":"事件隊列設計:解決 Socket.IO 與 DB Commit 順序問題","url":"/jaba%20ai/2025/12/21/jaba-ai-part3-event-queue.html","date":"2025-12-21","tags":"Python FastAPI Socket.IO 事件驅動 架構設計"},{"title":"Repository Pattern 實作:讓資料層乾淨分離","url":"/jaba%20ai/2025/12/21/jaba-ai-part2-repository.html","date":"2025-12-21","tags":"Python FastAPI SQLAlchemy Repository Pattern 架構設計"},{"title":"從 jaba + jaba-line-bot 到 jaba-ai:專案整合實戰","url":"/jaba%20ai/2025/12/20/jaba-ai-part1-integration.html","date":"2025-12-20","tags":"專案 Python FastAPI 重構 架構設計 LINE Bot"},{"title":"Jaba AI 技術分享系列:完整目錄","url":"/index/2025/12/19/jaba-ai-index.html","date":"2025-12-19","tags":"Jaba AI 目錄 系列文章 LINE Bot FastAPI Claude AI"},{"title":"pymupdf4llm - 把 PDF 轉成 AI 看得懂的 Markdown","url":"/ai/%E5%B7%A5%E5%85%B7/python/2025/12/17/pymupdf4llm-pdf-to-markdown.html","date":"2025-12-17","tags":"Python AI LLM PDF Markdown 工具推薦"},{"title":"C# WinForms 整合 Python OpenCV - Python.NET 完整教學","url":"/c%23/python/2025/12/16/csharp-pythonnet-opencv.html","date":"2025-12-16","tags":"C# Python OpenCV Python.NET WinForms 影像處理"},{"title":"uv 入門:極速 Python 套件管理","url":"/python/2025/12/13/uv-basics.html","date":"2025-12-13","tags":"Python uv 套件管理 虛擬環境"},{"title":"Linux 終端機入門:開發者必備指令","url":"/linux/2025/12/13/linux-basics.html","date":"2025-12-13","tags":"Linux Ubuntu Terminal SSH rsync"},{"title":"Git 入門:版本控制基礎指令","url":"/git/2025/12/13/git-basics.html","date":"2025-12-13","tags":"Git 版本控制 GitHub"},{"title":"Docker 基礎概念與常用指令","url":"/devops/2025/12/13/docker-basics.html","date":"2025-12-13","tags":"Docker Docker Compose 容器化 DevOps"},{"title":"Docker Compose 一鍵啟動開發環境","url":"/devops/2025/12/13/devops-part2-docker.html","date":"2025-12-13","tags":"Docker Docker Compose PostgreSQL DevOps"},{"title":"Alembic 資料庫版本控制:讓 Schema 變更可追蹤","url":"/devops/2025/12/13/devops-part1-alembic.html","date":"2025-12-13","tags":"Python Alembic PostgreSQL Migration"},{"title":"ChingTech OS 技術分享系列:完整目錄","url":"/index/2025/12/13/ching-tech-os-index.html","date":"2025-12-13","tags":"ChingTech OS 目錄 系列文章"},{"title":"檔案管理 API:FastAPI 實作上傳下載刪除","url":"/smb/nas/2025/12/12/smb-nas-part2-api.html","date":"2025-12-12","tags":"FastAPI REST API 檔案管理 Python NAS"},{"title":"SMB 協定入門:用 Python 連接公司 NAS","url":"/smb/nas/2025/12/12/smb-nas-part1-protocol.html","date":"2025-12-12","tags":"Python SMB NAS smbprotocol Synology"},{"title":"登入追蹤:裝置指紋與地理位置記錄","url":"/security/2025/12/12/security-part2-tracking.html","date":"2025-12-12","tags":"安全 GeoIP 指紋 JavaScript Python"},{"title":"認證系統:用 NAS 帳號實現 SSO 效果","url":"/security/2025/12/12/security-part1-auth.html","date":"2025-12-12","tags":"安全 認證 Session FastAPI Python"},{"title":"前端整合:xterm.js 打造完整終端體驗","url":"/web%20terminal/2025/12/11/web-terminal-part3-xtermjs.html","date":"2025-12-11","tags":"JavaScript xterm.js Terminal 前端 Socket.IO"},{"title":"後端架構:FastAPI + Socket.IO 雙向通訊","url":"/web%20terminal/2025/12/11/web-terminal-part2-socketio.html","date":"2025-12-11","tags":"FastAPI Socket.IO WebSocket Python 即時通訊"},{"title":"什麼是 PTY?讓網頁跑出真正的 Terminal","url":"/backend/2025/12/11/web-terminal-part1-pty.html","date":"2025-12-11","tags":"Python PTY Terminal Linux"},{"title":"System Prompt 設計:打造專屬 AI 助手人格","url":"/claude%20ai/2025/12/11/claude-ai-part3-prompt.html","date":"2025-12-11","tags":"AI Claude Prompt Engineering System Prompt"},{"title":"Token 管理:估算、警告與自動壓縮","url":"/claude%20ai/2025/12/11/claude-ai-part2-token.html","date":"2025-12-11","tags":"AI Claude Token 對話壓縮 JavaScript"},{"title":"架構選擇:Claude CLI 整合與對話設計","url":"/claude%20ai/2025/12/11/claude-ai-part1-architecture.html","date":"2025-12-11","tags":"AI Claude Python asyncio 對話系統"},{"title":"視窗系統(下):Window Snap 與 Taskbar 整合","url":"/frontend/2025/12/10/window-system-part3-snap.html","date":"2025-12-10","tags":"JavaScript UX 視窗系統 Window Snap"},{"title":"視窗系統(中):縮放、最大化與多視窗管理","url":"/frontend/2025/12/10/window-system-part2-resize.html","date":"2025-12-10","tags":"JavaScript DOM 視窗系統 縮放"},{"title":"視窗系統(上):讓網頁變成桌面 - 基礎拖曳功能","url":"/frontend/2025/12/10/window-system-part1-drag.html","date":"2025-12-10","tags":"JavaScript DOM 視窗系統 拖曳"},{"title":"為什麼我們選擇不用 React/Vue?談 Vanilla JS 的適用場景","url":"/frontend/2025/12/10/vanilla-js-why-no-framework.html","date":"2025-12-10","tags":"JavaScript IIFE 前端架構 Vanilla JS"},{"title":"CSS 設計系統:一行程式碼切換全站主題","url":"/frontend/2025/12/10/css-design-system-theme.html","date":"2025-12-10","tags":"CSS 設計系統 主題切換 CSS Variables"},{"title":"Certbot SSL 憑證完整教學 - Let's Encrypt 免費 HTTPS","url":"/devops/2025/12/10/certbot-ssl-guide.html","date":"2025-12-10","tags":"教學 SSL HTTPS Certbot Let's Encrypt Nginx Docker 資安"},{"title":"Render 免費部署教學 - 以 LINE Bot 為例","url":"/devops/2025/12/09/render-deploy-guide.html","date":"2025-12-09","tags":"教學 部署 Render Python Flask LINE Bot PaaS"},{"title":"LINE Bot 開發入門 - 以呷爸點餐系統為例","url":"/line%20bot/2025/12/09/line-bot-guide.html","date":"2025-12-09","tags":"教學 LINE Bot Python Flask Messaging API Webhook"},{"title":"Jaba LINE Bot - 讓點餐對話進入 LINE","url":"/line%20bot/2025/12/09/jaba-line-bot.html","date":"2025-12-09","tags":"專案 Python Flask LINE Bot API Claude Code SDD"},{"title":"SDD 規格驅動開發入門(二):指令介紹篇","url":"/sdd/2025/12/08/sdd-commands-guide.html","date":"2025-12-08","tags":"教學 AI Claude Code SDD OpenSpec"},{"title":"呷爸 jaba - 透過 LINE 群組輕鬆訂餐","url":"/projects/2025/12/08/jaba.html","date":"2025-12-08","tags":"專案 Python FastAPI AI Web LINE Bot Claude Code SDD"},{"title":"SDD 規格驅動開發入門(一):環境安裝篇","url":"/sdd/2025/12/07/sdd-setup-guide.html","date":"2025-12-07","tags":"教學 Python FastAPI AI Claude Code SDD"},{"title":"RosAGV - 企業級 AGV 車隊管理系統","url":"/%E5%B7%A5%E6%A5%AD%E8%87%AA%E5%8B%95%E5%8C%96/ros2/agv/2025/12/05/rosagv-fleet-management-system.html","date":"2025-12-05","tags":"ROS2 AGV Docker Zenoh 車隊管理 工業自動化"},{"title":"擎添工業官方網站","url":"/projects/2025/12/05/ching-tech-website.html","date":"2025-12-05","tags":"專案 Python FastAPI Web Robot AGV AI Claude Code SDD"},{"title":"Fish Fry Counter 魚苗計數系統","url":"/projects/2025/11/27/fish-cv.html","date":"2025-11-27","tags":"專案 Python FastAPI OpenCV YOLO AI Claude Code SDD"},{"title":"ROS2 基礎概念入門","url":"/ros2/2025/11/24/ros2-basics.html","date":"2025-11-24","tags":"ROS2 Jazzy 機器人 AGV"},{"title":"WebAGV - AGV 車隊即時監控看板","url":"/web%E9%96%8B%E7%99%BC/agv/%E5%8D%B3%E6%99%82%E7%9B%A3%E6%8E%A7/2025/04/24/webagv-monitoring-dashboard.html","date":"2025-04-24","tags":"Vue3 Node.js Socket.IO Leaflet MQTT AGV 即時監控"},{"title":"AnalyzerUI","url":"/projects/2025/01/08/AnalyzerUI.html","date":"2025-01-08","tags":"產品 C#"},{"title":"CTCUI SMD 全自動智慧檢測系統:WiseTechVision 在 QC 自動化的應用","url":"/computer%20vision/kuka/automation/2023/06/07/ctcui-smd-aoi-system.html","date":"2023-06-07","tags":"WiseTechVision KUKA AOI 品質檢測 自動化 SMD"},{"title":"國立工藝中心 手臂雕刻系統","url":"/2021/06/28/ntcri.html","date":"2021-06-28","tags":"專案 Robot Unity"},{"title":"QRCodeScanner 1922實聯制QRCode掃瞄器","url":"/2021/05/20/QRCodeScanner.html","date":"2021-05-20","tags":"專案 Unity Android"},{"title":"亞洲大學 2020 - KUKA 工業機器手臂 3D 動畫模擬與操作","url":"/%E6%95%99%E5%AD%B8/robotsim/kuka/2020/11/11/asia2020-RobotSim.html","date":"2020-11-11","tags":"教學 Unity Robot RobotSim KUKA"},{"title":"亞洲大學 2020 - KUKA 機器手臂與 Leap Motion 手勢控制","url":"/%E6%95%99%E5%AD%B8/robotsim/kuka/leapmotion/2020/11/11/asia2020-RobotLeapMotion.html","date":"2020-11-11","tags":"教學 Unity Robot LeapMotion KUKA EKI RSI"},{"title":"WiseTechVision 視覺系統整合應用:散亂螺絲自動整列","url":"/computer%20vision/kuka/2020/07/20/wisetech-vision-screw-sorting.html","date":"2020-07-20","tags":"WiseTechVision KUKA 機器視覺 自動化 工業視覺"},{"title":"KUKA 力覺感知應用:Tray 盤取放堆疊自動化","url":"/kuka/automation/2020/06/18/kuka-force-sensing-tray-stack.html","date":"2020-06-18","tags":"KUKA 力覺感知 Force Sensing Tray盤 堆疊自動化"},{"title":"KUKA 力覺感知應用:PCB 來料深度感測與精確吸取","url":"/kuka/automation/2020/06/05/kuka-force-sensing-pcb-depth.html","date":"2020-06-05","tags":"KUKA 力覺感知 Force Sensing PCB 自動化"},{"title":"實踐大學 2019 - 機器手臂程式設計:從模擬到實機","url":"/%E6%95%99%E5%AD%B8/robotsim/kuka/2019/03/13/usc2019-RobotSim.html","date":"2019-03-13","tags":"教學 Unity Robot RobotSim KUKA KRL"},{"title":"嘉南藥理大學 2018 - Unity 互動科技:RobotSim 機器手臂模擬","url":"/%E6%95%99%E5%AD%B8/robotsim/2018/04/16/cnu2018-RobotSim.html","date":"2018-04-16","tags":"教學 Unity Robot RobotSim 互動科技"},{"title":"RobotSim:Unity 3D 機器手臂模擬系統","url":"/kuka/robotsim/2018/03/01/robotsim-unity-robot-simulation.html","date":"2018-03-01","tags":"RobotSim Unity 機器手臂模擬 KUKA 運動學"},{"title":"KUKA 機器手臂力覺感知技術:不外掛 Sensor 的扭力檢測應用","url":"/kuka/automation/2017/08/09/kuka-force-sensing-without-sensor.html","date":"2017-08-09","tags":"KUKA 力覺感知 Force Sensing KRL 自動化"},{"title":"KUKA 力覺感知應用:記憶卡插件力道感測與反向錯放偵測","url":"/kuka/automation/2017/08/09/kuka-force-sensing-memory-card.html","date":"2017-08-09","tags":"KUKA 力覺感知 Force Sensing 插件組裝 品質檢測"},{"title":"實踐大學 2017 - 聯網感測實作","url":"/%E6%95%99%E5%AD%B8/iot/arduino/2017/06/19/usc2017nsp.html","date":"2017-06-19","tags":"教學 Arduino IoT MQTT MySQL"},{"title":"嘉南藥理大學 2017 - Unity 程式設計基本課程","url":"/%E6%95%99%E5%AD%B8/unity/%E9%81%8A%E6%88%B2%E9%96%8B%E7%99%BC/2017/04/24/cnu2017.html","date":"2017-04-24","tags":"教學 Unity C# Networking"},{"title":"五花果的後花園 - 讓小朋友的畫作在大螢幕中活起來","url":"/%E5%B0%88%E6%A1%88/unity/%E4%BA%92%E5%8B%95%E8%97%9D%E8%A1%93/2017/01/14/8boo-land-5flowergod.html","date":"2017-01-14","tags":"專案 Unity 互動藝術 影像處理"},{"title":"嘉南藥理大學 2016 - 互動應用","url":"/%E6%95%99%E5%AD%B8/unity/%E4%BA%92%E5%8B%95%E6%87%89%E7%94%A8/2016/09/21/cnu2016.html","date":"2016-09-21","tags":"教學 Unity AR Kinect LeapMotion"},{"title":"實踐大學 2016 人機互動課程專案 - 雲端相簿 (Web Server 端)","url":"/%E5%B0%88%E6%A1%88/web/backend/2016/03/26/usc2016hci_CloudAlbum.html","date":"2016-03-26","tags":"教學 Web PHP MySQL"},{"title":"實踐大學 2016 人機互動課程專案 - AR 相機 (APP Client 端)","url":"/%E5%B0%88%E6%A1%88/unity/ar/2016/03/26/usc2016hci_ARCamera.html","date":"2016-03-26","tags":"教學 Unity Android AR Vuforia NGUI"},{"title":"實踐大學 2016 - 人機互動技術與應用","url":"/%E6%95%99%E5%AD%B8/unity/hci/2016/02/14/usc2016hci.html","date":"2016-02-14","tags":"教學 Unity AR Kinect LeapMotion Vuforia"},{"title":"實踐大學 2015 - 創意互動導覽行動平台人才培育學程","url":"/%E6%95%99%E5%AD%B8/unity/android/2015/11/26/usc2015.html","date":"2015-11-26","tags":"教學 Android Unity AR Vuforia"},{"title":"艾思摩爾手作坊iCEMORE - 單頁式","url":"/2015/11/12/icemore2.html","date":"2015-11-12","tags":"專案 Web"},{"title":"艾思摩爾手作坊iCEMORE","url":"/2015/11/03/icemore.html","date":"2015-11-03","tags":"專案 Web"},{"title":"打火解油出任務 - Unity + Kinect 大型活動體感遊戲","url":"/%E5%B0%88%E6%A1%88/unity/%E4%BA%92%E5%8B%95%E9%81%8A%E6%88%B2/2015/08/20/kinect-game-dagfire.html","date":"2015-08-20","tags":"專案 Unity Kinect 互動遊戲"},{"title":"無人飛行船地面站即時監控系統","url":"/%E5%B0%88%E6%A1%88/%E5%B5%8C%E5%85%A5%E5%BC%8F%E7%B3%BB%E7%B5%B1/%E5%8D%B3%E6%99%82%E7%9B%A3%E6%8E%A7/2015/06/01/airship-ground-station.html","date":"2015-06-01","tags":"專案 Microchip 嵌入式系統 Flash ActionScript GPRS 即時監控"},{"title":"無人飛行船自動導航系統","url":"/%E8%AB%96%E6%96%87/%E5%B5%8C%E5%85%A5%E5%BC%8F%E7%B3%BB%E7%B5%B1/%E8%87%AA%E5%8B%95%E5%B0%8E%E8%88%AA/2011/06/01/airship-auto-navigation-thesis.html","date":"2011-06-01","tags":"論文 Microchip 嵌入式系統 GPS GPRS 自動導航 飛行控制"}]
// assets/js/blog-search.js
(async function () {
const input = document.getElementById('blog-search');
const list = document.getElementById('post-list');
const out = document.getElementById('search-results');
const pagination = document.querySelector('.pagination');
let idx = null;
input.addEventListener('input', async function () {
const q = input.value.trim().toLowerCase();
if (!q) { out.hidden = true; list.hidden = false; pagination.hidden = false; return; }
if (!idx) idx = await (await fetch('/search.json')).json();
const hits = idx.filter(p => (p.title + ' ' + p.tags).toLowerCase().includes(q)).slice(0, 30);
out.innerHTML = hits.map(p =>
`<li><h2><a href="${p.url}">${p.title}</a></h2><p class="meta">${p.date}</p></li>`
).join('') || '<li><p class="meta">沒有符合的文章。</p></li>';
out.hidden = false; list.hidden = true; pagination.hidden = true;
});
})();
- Step 3: 驗證分頁與搜尋
curl -s http://localhost:4400/blog/ | grep -c "post-list"
curl -s http://localhost:4400/blog/page2/ | grep -c "post-list"
curl -s http://localhost:4400/search.json | python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d))"
Expected: 兩頁都有列表、search.json 有 205 筆。瀏覽器開 /blog/ 打「台語」確認即時搜尋出現該篇。tags 頁 curl -s http://localhost:4400/tags/ | grep -c tag 仍正常(它用 layout: page,Task 2 已覆蓋)。
- Step 4: Commit
git add -A && git commit -m "feat(blog): /blog 分頁列表+客戶端搜尋,tags 頁沿用新版型
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>"
Task 5: 作品牆截圖素材
Files:
- Create:
images/portfolio/{ai-chant-magic,ctos,k-rider,roll-formosa,taigi,sprite-studio,fish-cv,jaba}.webp - Create:
scripts/portfolio-shots.mjs(Playwright 截圖腳本,留在 repo 供日後更新)
Interfaces:
-
Produces: 8 張 16:10 截圖 webp(每張 <150KB),Task 6 的作品卡片以 jsDelivr URL 引用:
https://cdn.jsdelivr.net/gh/yazelin/yazelin.github.io@master/images/portfolio/<name>.webp。 -
Step 1: 確認 8 個作品的線上 URL 都活著
for u in https://yazelin.github.io/ai-chant-magic/ https://yazelin.github.io/k-rider/ https://yazelin.github.io/roll-formosa/ https://yazelin.github.io/mandarin-taigi/ https://yazelin.github.io/mori-sprite-studio/; do curl -s -o /dev/null -w "%{http_code} $u\n" $u; done
CTOS/魚苗/呷爸沒有公開網頁——這三個用 blog 文章內既有的 GitHub Releases 圖(找該篇文章第一張圖的 URL 下載轉 webp),不截圖。
- Step 2: 寫截圖腳本並執行
// scripts/portfolio-shots.mjs
import { chromium } from 'playwright';
const shots = [
['ai-chant-magic', 'https://yazelin.github.io/ai-chant-magic/'],
['k-rider', 'https://yazelin.github.io/k-rider/'],
['roll-formosa', 'https://yazelin.github.io/roll-formosa/'],
['taigi', 'https://yazelin.github.io/mandarin-taigi/'],
['sprite-studio', 'https://yazelin.github.io/mori-sprite-studio/'],
];
const b = await chromium.launch({ channel: 'chrome' });
const page = await b.newPage({ viewport: { width: 1280, height: 800 } });
for (const [name, url] of shots) {
await page.goto(url, { waitUntil: 'networkidle' });
await page.waitForTimeout(2500); // 等動畫/canvas 進場
await page.screenshot({ path: `images/portfolio/${name}.png` });
console.log(name, 'done');
}
await b.close();
cd /home/ct/yazelin.github.io && mkdir -p images/portfolio && node scripts/portfolio-shots.mjs
python3 - <<'EOF'
from PIL import Image; import glob, os
for f in glob.glob('images/portfolio/*.png'):
Image.open(f).convert('RGB').save(f[:-4]+'.webp', quality=82, method=6); os.remove(f)
print(f, os.path.getsize(f[:-4]+'.webp')//1024, 'KB')
EOF
Expected: 每張 <150KB;超過就降 quality 到 75 重轉。人工看一眼每張圖不是空白/載入中畫面(canvas 類站要真的有畫面)。
- Step 3: Commit
git add images/portfolio scripts/portfolio-shots.mjs && git commit -m "assets: 作品牆截圖(webp)+截圖腳本
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>"
Task 6: 首頁 landing(文案全部標草稿)
Files:
- Modify:
index.html(整檔重寫) - Modify: 4–6 篇精選文章 front matter 加
featured: true+featured-why: "一句話"
Interfaces:
- Consumes: Task 2 CSS(hero/work-grid/story/featured-list/signup)、Task 3
signup-form.html、Task 5 截圖。 -
Produces: 完整 landing;文案以 HTML 註解標
<!-- 草稿:待核可 -->。 - Step 1: 挑精選文章並加旗標
從甲方思維/AI 發包相關文章挑 4–6 篇(用 grep -l "甲方思維" _posts/*.md 起手,加上咏唱魔法 index、台語辭典篇),front matter 加:
featured: true
featured-why: "為什麼值得讀的一句話"
- Step 2: 重寫 index.html
---
layout: default
title: null
share-description: 林亞澤 Yaze — 甲方思維:不寫程式也能把軟體做出來。工業自動化 10+ 年轉 AI 應用,一個人 + AI 維護 30+ 個活躍專案。
head-extra:
- head-custom.html
---
<!-- 文案草稿:全部待 yazelin 逐句核可 -->
<section class="hero">
<h1>不寫程式,也能把軟體做出來</h1>
<p class="sub">我用「甲方思維」驅動 AI 開發:開規格、下發包、做驗收。工業自動化 10+ 年,現在一個人 + AI 同時維護 30+ 個上線專案——這個網站會教你同一套方法。</p>
<div class="signup">
<h2>保持聯絡</h2>
<p class="section-lead">留個 email,有新文章或新作品上線時告訴你。</p>
<form data-signup data-source="blog">
<input type="email" name="email" placeholder="you@example.com" aria-label="Email" required>
<input type="text" name="company" class="hp" tabindex="-1" autocomplete="off" aria-hidden="true">
<button type="submit">訂閱</button>
</form>
<p class="msg" aria-live="polite"></p>
</div>
<script>
document.querySelectorAll('form[data-signup]').forEach(function (f) {
if (f.dataset.bound) return; f.dataset.bound = '1';
f.addEventListener('submit', async function (e) {
e.preventDefault();
var msg = f.parentElement.querySelector('.msg');
msg.textContent = '送出中…';
try {
var r = await fetch('https://k-rider-api.yazelinj303.workers.dev/signup', {
method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({ email: f.email.value, company: f.company.value, source: f.dataset.source })
});
var d = await r.json();
msg.textContent = d.ok ? (d.already ? '這個 email 已經訂閱過了。' : '訂閱成功,之後見。') : '出了點問題,再試一次。';
if (d.ok && !d.already) f.reset();
} catch (err) { msg.textContent = '網路怪怪的,稍後再試。'; }
});
});
</script>
</section>
<section class="section container-wide" id="work">
<h2 class="section-title">上線的作品,不是 demo</h2>
<p class="section-lead">每一個都可以點進去用。這是「AI 發包法」的產出證明。</p>
<div class="work-grid">
<a class="work-card" href="https://yazelin.github.io/ai-chant-magic/">
<img src="https://cdn.jsdelivr.net/gh/yazelin/yazelin.github.io@master/images/portfolio/ai-chant-magic.webp" alt="真AI咏唱魔法" loading="lazy">
<div class="body"><h3>真AI咏唱魔法</h3><p>對麥克風唸咒語,AI 即時生成魔法效果的生存遊戲。</p></div>
</a>
<!-- 其餘 7 張卡片同構:ctos / k-rider / roll-formosa / taigi / sprite-studio / fish-cv / jaba,
標題與一句話說明照下表,連結:有站連站、沒站連 blog 該篇文章 -->
</div>
</section>
<section class="section container-wide">
<div class="story">
<h2 class="section-title">從工廠到 AI</h2>
<p>我在工業自動化做了十幾年:機器人、視覺檢測、AGV 車隊。工業現場預算有限,習慣了用軟體取代昂貴的感測器、用不一樣的解法把事情做成。</p>
<p>2024 年起我把同一套「發包給供應商」的功夫,原封不動用在 AI 身上——結果是一個人維護 30+ 個活躍專案。這套方法不需要你會寫程式,需要你會當一個好甲方。</p>
<p style="text-align:center"><a href="/about/">完整的故事 →</a></p>
</div>
</section>
<section class="section container">
<h2 class="section-title">從這幾篇開始讀</h2>
<ul class="featured-list">
<li><a href="/ai/2026/07/15/taigi-hakka-dictionaries.html">「長頸鹿怎麼沒有?」兩天長出兩本離線辭典</a><p class="why">願望池裡的一句話,兩天長成兩本教育部等級的離線辭典。</p></li>
<li><a href="/ai/2026/07/09/kb-driven-dev-flow.html">這個 demo 裡 AI 一行新程式都沒寫,但我覺得它比寫程式的 demo 更值得看</a><p class="why">AI 一行新程式都沒寫的 demo,規格放知識庫、AI 自己接單自己回報。</p></li>
<li><a href="/index/2026/07/06/ai-chant-magic-index.html">真。AI。咏唱魔法 系列:完整目錄</a><p class="why">一款對著麥克風唸咒語的網頁生存遊戲,系列文章總入口。</p></li>
<li><a href="/ai/%E9%96%8B%E7%99%BC%E5%B7%A5%E5%85%B7/2026/06/19/ai-overnight-loop.html">我睡覺,它整晚加城市 —— 怎麼排班讓 AI 自己做完一整夜的內容</a><p class="why">睡一覺起來 AI 自己跑完一整夜,怎麼排班讓它整晚不用人顧。</p></li>
<li><a href="/ai/2026/06/16/k-rider-case-study.html">從一個點子到一個會動的股價遊戲:我怎麼用「甲方思維」發包給 AI</a><p class="why">「甲方思維」怎麼把一個點子發包給 AI,做成真的上線的股價遊戲。</p></li>
</ul>
<p style="text-align:center"><a href="/blog/">全部 207 篇文章 →</a></p>
</section>
<section class="section container"><div class="signup">
<h2>保持聯絡</h2>
<p class="section-lead">留個 email,有新文章或新作品上線時告訴你。</p>
<form data-signup data-source="blog">
<input type="email" name="email" placeholder="you@example.com" aria-label="Email" required>
<input type="text" name="company" class="hp" tabindex="-1" autocomplete="off" aria-hidden="true">
<button type="submit">訂閱</button>
</form>
<p class="msg" aria-live="polite"></p>
</div>
<script>
document.querySelectorAll('form[data-signup]').forEach(function (f) {
if (f.dataset.bound) return; f.dataset.bound = '1';
f.addEventListener('submit', async function (e) {
e.preventDefault();
var msg = f.parentElement.querySelector('.msg');
msg.textContent = '送出中…';
try {
var r = await fetch('https://k-rider-api.yazelinj303.workers.dev/signup', {
method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({ email: f.email.value, company: f.company.value, source: f.dataset.source })
});
var d = await r.json();
msg.textContent = d.ok ? (d.already ? '這個 email 已經訂閱過了。' : '訂閱成功,之後見。') : '出了點問題,再試一次。';
if (d.ok && !d.already) f.reset();
} catch (err) { msg.textContent = '網路怪怪的,稍後再試。'; }
});
});
</script>
</section>
作品卡片文字(草稿,一併待核可):
| name | 標題 | 一句話 | 連結 |
|---|---|---|---|
| ai-chant-magic | 真AI咏唱魔法 | 對麥克風唸咒語,AI 即時判定的生存遊戲 | 站 |
| ctos | ChingTech OS | 中小製造業的 AI 企業系統,商用中 | blog 文 |
| k-rider | K-Rider K線騎手 | 用真實股價 K 線玩的騎乘遊戲 | 站 |
| roll-formosa | Roll Formosa | 把台灣街景滾成一顆球的遊戲 | 站 |
| taigi | 台語/客語辭典 | 兩天長出兩本教育部資料離線辭典 | 站 |
| sprite-studio | Mori Sprite Studio | 一張圖生成完整角色動畫包的工具 | 站 |
| fish-cv | 魚苗計數系統 | 用視覺 AI 數魚苗,取代人工點數 | blog 文 |
| jaba | 呷爸 Jaba | 公司訂便當 LINE Bot,天天在用 | blog 文 |
- Step 3: 本機驗證
瀏覽器開 http://localhost:4400/ :四屏都渲染、8 張卡片圖有出來、精選文章列表非空、兩個訂閱表單都能打 API(打一筆測試 email 後從 D1 刪除)。iPhone 13 viewport(Playwright devices['iPhone 13'] + channel:'chrome' + .tap())確認不破版。
- Step 4: Commit
git add -A && git commit -m "feat(landing): 首頁改個人品牌 landing(文案草稿待核可)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>"
Task 7: 文案核可關卡(HARD GATE:需 yazelin 逐句拍板)
Files:
-
Modify:
index.html(依核可結果修文案)、精選文章 front matter(featured-why) -
Step 1: 把所有草稿文案整理成清單端給 yazelin
定位句、副標、三個 section 標題與 lead、8 張卡片的標題+一句話、故事兩段、訂閱表單文案、精選文章的 featured-why。逐句列出,等回覆。沒核可不得進 Task 8。
- Step 2: 依回覆修改、檢查禁用詞
grep -rn "接住" index.html _includes/ && echo "FAIL: 禁用詞" || echo OK
- Step 3: Commit
git add -A && git commit -m "copy: 首頁文案定稿(yazelin 核可)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>"
Task 8: 驗收與上線(merge 前全部過)
Files:
-
無新檔;驗收 + merge
rebrand→master -
Step 1: 舊網址抽驗(本機 build 對照線上)
# 線上抓 10 個現行 URL(含 2011 最舊與 2026 最新), 本機同 path 必須 200
for p in $(curl -s https://yazelin.github.io/sitemap.xml | grep -o '<loc>[^<]*' | sed 's/<loc>//;s|https://yazelin.github.io||' | shuf -n 10); do
curl -s -o /dev/null -w "%{http_code} $p\n" "http://localhost:4400$p"; done
Expected: 全部 200(landing 換掉的舊首頁分頁 /page2 等除外——那些由 /blog/page2/ 取代,301 無法做,GitHub Pages 不支援,可接受:舊分頁 URL 本來就不該被外部引用;sitemap 會更新)。
- Step 2: 功能等價檢查
curl -s http://localhost:4400/feed.xml | head -5 # RSS
curl -s http://localhost:4400/sitemap.xml | grep -c "<loc>" # sitemap
curl -s http://localhost:4400/tags/ | grep -c "tag" # tags
curl -s http://localhost:4400/404.html | grep -c "找不到" # 404
- Step 3: 行動版 + Lighthouse
Playwright iPhone 13 走首頁四屏截圖人工看;mcp chrome-devtools lighthouse_audit 對本機首頁,performance 分數記錄下來(基準:不低於改版前線上首頁——先對線上跑一次留數字)。
- Step 4: giscus 冒煙
本機或 push 後線上開任一篇文章,giscus iframe 有載入(giscus 認 pathname,本機 localhost 不一定能載——線上驗)。
- Step 5: Merge 上線與線上複驗
git checkout master && git merge --no-ff rebrand -m "feat: 個人品牌主站改版上線
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>" && git push
# 等 Pages 部署後:
for p in / /blog/ /about/ /tags/ /ai/2026/07/15/taigi-hakka-dictionaries.html; do
curl -s -o /dev/null -w "%{http_code} $p\n" "https://yazelin.github.io$p"; done
Expected: 全 200;線上再開首頁確認訂閱表單真的寫進 D1(打一筆再刪)。giscus 線上驗。README 若描述舊主題,一併更新。
Task 9(後續、不擋上線): About 轉型敘事重寫
- 素材:
~/mori-universe/yaze-journal/projects/portfolio-resume/resume-facts.md+ 現有 about.md。 - 產出重寫稿(人-AI 分工署名原則適用),端給 yazelin 逐段核可後才上。
- Lead magnet(甲方思維入門 checklist)同為後續另案,不在本 plan。
Self-Review 紀錄
- Spec 覆蓋:定位三屏(Task 6)、技術路線與硬限制(Task 2–4)、email 漏斗(Task 1/3)、footer 三件套+SHOPLINE 備註(Task 2,SHOPLINE 是未來事不建任務)、驗收六條(Task 8;第 4 條「10 秒測驗」在 Task 7 文案關卡由 yazelin 自查)、About 敘事(Task 9)。
- 型別/名稱一致:
signup-form.html的include.source與 Task 1 白名單(blog/post)對齊;CSS class 名 Task 2 定義、Task 3/4/6 使用一致;jsDelivr path 用@master(本 repo 預設分支是 master 非 main)。 - 已知風險:jekyll-paginate v1 + blog/index.html 的 permalink 並用怪癖(Task 4 Step 1 已註記對策);giscus 本機驗不了(Task 8 Step 4 線上驗)。