MDX 內容編輯與維護指南
這一頁是本網站唯一的內容編輯手冊。新增或修改作業、專案、報告前,先依本頁的資料夾規則建立檔案,再更新 config/AllData.ts。
快速流程
- 在
markdown/對應課程資料夾新增.mdx。 - 將一般圖片放入
public/images/,banner 放入public/thumbs/,可下載檔案放入public/files/。 - 在
config/AllData.ts新增或更新該頁的name、navName、slug、image。 - 以
pnpm dev開啟預覽,確認網址、banner、圖片與連結。 - 停止開發伺服器後再執行
pnpm build。
不要同時執行
pnpm dev與pnpm build。兩者同時寫入.next時,可能出現Cannot find module './xx.js'。若開發畫面或 chunk 異常,停止服務後執行pnpm dev:clean。
資料夾規則
markdown/
homework/computer-animation/
project/computer-animation/
paper/computer-animation/
public/
images/ 一般內容圖片
thumbs/ 每個頁面最上方的 banner
files/ 可直接下載的 PDF、blend、stl、zip 等檔案
建議依內容分類,並讓資料夾名稱與 AllData.ts 的內容相對應:
public/images/homework/computer-animation/hw15/
public/thumbs/homework/computer-animation/hw15/
public/files/homework/
public/images/project/computer-animation/project02-w16-final/
public/thumbs/project/computer-animation/project02-w16-final/
public/files/paper/computer-animation/paper03/
瀏覽器使用的網址不需要寫 public:
public/images/example.png -> /images/example.png
public/files/example.blend -> /files/example.blend
AllData 與路由
config/AllData.ts 決定 navbar、頁面標題、banner 與網址。slug 必須和 MDX 檔名一致,但不包含 .mdx。
Homework 與 Paper
{
name: "Homework 15_完整頁面標題",
navName: "HW15",
slug: "CG_HW15",
image: "/thumbs/homework/computer-animation/hw15/banner.jpg"
}
產生的頁面網址:
/homework/computer-animation/CG_HW15
name:banner 與頁面顯示的完整標題。navName:navbar 內較短的名稱,例如HW15。slug:網址最後一段,也是 MDX 檔名,例如CG_HW15.mdx。image:banner 路徑,必須位於public/thumbs/。
Project 的多層選單
Project 可用 children 建立第三層詳細頁。父層不可直接點擊,詳細頁放在 children 中。
{
name: "PROJECT02",
navName: "PROJECT02",
slug: "project02",
image: "/thumbs/project/computer-animation/project02-w11/banner.jpg",
children: [
{
name: "PROJECT02_W11",
navName: "PROJECT02_W11",
slug: "CG_Proj02_W11",
image: "/thumbs/project/computer-animation/project02-w11/banner.jpg"
}
]
}
詳細頁 MDX 仍放在課程資料夾,不需要再建立 project02/ 子資料夾:
markdown/project/computer-animation/CG_Proj02_W11.mdx
新增檢查清單
- MDX 檔案已建立,檔名與
slug一致。 - banner 位於
public/thumbs/。 - 一般圖片位於
public/images/。 - 下載檔案位於
public/files/。 -
AllData.ts已新增對應資料。 - 手機與桌面 navbar 都能找到該頁。
頁面建議結構
# 是頁面主標題,課程頁的 banner 已經會顯示主標題,因此一般不要在 MDX 再寫 # Homework 02。
<MarkdownMeta hours={6} />
## 作品技術概念
說明作品使用的技術與設計想法。
<ContentImages
src="/images/homework/computer-animation/hw15/overview.png"
alt="作品總覽"
maxWidth="2xl"
/>
## 節點概念說明(附上實際節點、面數等截圖)
<ContentImages
src="/images/homework/computer-animation/hw15/node-count.png"
alt="模型面數"
caption="模型面數"
maxWidth={200}
/>
### step 1-1:建立基本形狀
1. 先完成基本模型。
- 用第一個節點建立輪廓。
- 再調整尺寸。
## 自我評量與作品建議
<RatingSummary score={["8/10"]} />
## 解決問題之能力與參考資源
補上參考資料、問題與解法。
Markdown 基本語法
標題
# H1:一般頁面不要使用,banner 已有主標題
## H2:主要章節,例如作品技術概念、過程記錄、自我評量
### H3:章節中的步驟,例如 step 1-1
#### H4:必要時才使用的更小分類
文字樣式與段落
一般文字。
空一行會形成新段落。
**粗體**
*斜體*
***粗斜體***
~~刪除線~~
`inline code`
需要在同一段換行時使用:<br/>
連結與下載檔案
[一般連結](https://example.com)
[官方文件](https://docs.blender.org/)
[下載練習檔](/files/homework/repeat___join.blend)
雲端 Google Drive、YouTube、Gemini 等連結可直接保留外部網址。網站內的檔案則放入 public/files/,再使用 /files/... 路徑。
清單與正確縮排
- 第一個主要項目
- 第二個主要項目
1. 第一個步驟
2. 第二個步驟
- 這是第二個步驟底下的小項目。
- 小項目要縮排四個空白。
- 再下一層使用八個空白。
3. 第三個步驟
- 若屬於某個數字步驟的說明,一定要縮排四個空白;獨立的主清單則不要縮排。
引言、分隔線與跳脫字元
> 這是一段引用或重點提醒。
---
\*這不是斜體\*
程式碼區塊
```ts
const score = ["8/10"];
```
可用的常見語言標示有 ts、tsx、js、jsx、json、bash、txt、mdx。
表格、核取方塊與自動連結
本網站已啟用 GitHub Flavored Markdown(GFM)。
| 欄位 | 說明 |
| :--- | :--- |
| `name` | 完整標題 |
| `navName` | navbar 短標題 |
- [x] 已完成
- [ ] 尚未完成
https://example.com
數學公式
本網站已啟用 remark-math 與 rehype-katex。
行內公式:$E = mc^2$
區塊公式:
$$
\int_a^b f(x)\,dx = F(b) - F(a)
$$
MDX 元件總覽
以下元件已在 mdx-components.tsx 全域註冊,直接在 MDX 使用即可,不需要 import。
| 元件 | 用途 |
|---|---|
MarkdownMeta | 製作時數與檔案連結 |
ContentImages | 單張圖片、多張 grid、圖片比例與說明 |
ImageCarousel | 可自動播放的輪播圖 |
YouTubeEmbed | YouTube 單支或 grid 影片 |
RatingSummary | 自我評量分數 |
ScrollReveal | 進入視窗時的淡入動畫 |
Image | Next.js 原生圖片元件;一般情況優先使用 ContentImages |
MarkdownMeta
用於頁面最上方的製作時數與檔案按鈕。
可用參數
| 參數 | 型別 | 預設 | 說明 |
|---|---|---|---|
hours | number、string、string[] | 不顯示 | 製作時數。數字會顯示為小時;多週資料用陣列。 |
links | { label, text, href }[] | [] | 檔案或外部連結按鈕。 |
links[].label | string | 必填 | 按鈕左側的標籤。 |
links[].text | string | 必填 | 按鈕內文字。 |
links[].href | string | 必填 | 外部網址或 /files/... 本機檔案路徑。 |
<MarkdownMeta
hours={6}
links={[
{
label: "Blender 檔案",
text: "File Link",
href: "https://drive.google.com/"
},
{
label: "練習檔案",
text: "Download",
href: "/files/homework/repeat___join.blend"
}
]}
/>
多週時數:
<MarkdownMeta
hours={[
"[week06] 7 小時",
"[week08] 9 小時"
]}
/>
Paper 沒有製作時數時,只傳入 links,頁面只會顯示按鈕。
<MarkdownMeta
links={[
{
label: "File Download",
text: "File Download",
href: "/files/paper/computer-animation/paper01/report.pdf"
}
]}
/>
ContentImages
所有一般圖片優先使用此元件。可呈現單張圖片、grid、caption、指定最大寬度,以及統一圖片比例。
可用參數
| 參數 | 型別/可用值 | 預設 | 說明 |
|---|---|---|---|
src | string | 無 | 單張圖片路徑。與 images 二擇一。 |
images | 圖片物件陣列 | 無 | 多張圖片 grid。若有值會優先使用。 |
alt | string | 圖片 | 單張圖片的替代文字。 |
caption | string | 無 | 單張圖片說明;不需要就不要傳。 |
width | number | 900 | 圖片原始寬度資訊。主要顯示尺寸請用 maxWidth。 |
height | number | 506 | 圖片原始高度資訊。主要顯示尺寸請用 maxWidth。 |
columns | 1、2、3、4 | 1 | grid 欄數;手機會自動縮為較少欄。 |
maxWidth | 尺寸名稱或 number | "xs" | 單張圖片最大寬度。數字單位為 px。 |
aspectRatio | "auto"、"square"、"video"、"4/3"、"3/4" | "auto" | 固定圖片框比例。 |
fit | "contain"、"cover" | "contain" | 固定比例框內的圖片填滿方式。 |
images 陣列內每一張可使用:src(必填)、alt、caption、width、height、aspectRatio、fit。單張設定會覆蓋外層的 aspectRatio 與 fit。
maxWidth 尺寸表
| 值 | 實際最大寬度 |
|---|---|
"xs" | 20rem,320px |
"sm" | 24rem,384px |
"md" | 28rem,448px |
"lg" | 48rem,768px |
"xl" | 64rem,1024px |
"2xl" | 80rem,1280px;仍受頁面容器限制 |
"full" | 父容器全寬 |
maxWidth={300} | 300px |
單張一般大圖
<ContentImages
src="/images/homework/computer-animation/hw15/overview.png"
alt="作品總覽"
maxWidth="2xl"
/>
節點概念的模型面數圖
<ContentImages
src="/images/homework/computer-animation/hw15/node-count.png"
alt="模型面數"
caption="模型面數"
maxWidth={200}
/>
多張圖片 grid
<ContentImages
columns={2}
maxWidth="2xl"
images={[
{ src: "/images/example-1.jpg", alt: "步驟一", caption: "步驟一" },
{ src: "/images/example-2.jpg", alt: "步驟二", caption: "步驟二" }
]}
/>
統一比例,但保留完整圖片
<ContentImages
columns={3}
maxWidth="2xl"
aspectRatio="video"
fit="contain"
images={[
{ src: "/images/example-1.jpg", alt: "分鏡一" },
{ src: "/images/example-2.jpg", alt: "分鏡二" },
{ src: "/images/example-3.jpg", alt: "分鏡三" }
]}
/>
contain 會完整顯示圖片,比例不同時可能有留白;cover 會填滿比例框,但可能裁切邊緣。
每一張使用不同設定
<ContentImages
columns={2}
images={[
{
src: "/images/example-1.jpg",
alt: "方形裁切圖",
aspectRatio: "square",
fit: "cover"
},
{
src: "/images/example-2.jpg",
alt: "完整影片比例圖",
aspectRatio: "video",
fit: "contain"
}
]}
/>
ImageCarousel
適合連續步驟截圖。輪播固定為 16:9 顯示框、圖片完整呈現、可手動切換與暫停。若有多張圖片,預設每 6000ms 自動播放。
可用參數
| 參數 | 型別/可用值 | 預設 | 說明 |
|---|---|---|---|
images | { src, alt?, caption? }[] | 必填 | 輪播圖片。沒有有效 src 時不顯示。 |
images[].src | string | 必填 | 圖片路徑。 |
images[].alt | string | 無 | 替代文字。 |
images[].caption | string | 無 | 圖片下方說明。沒有說明文字時使用 " "。 |
label | string | "Image carousel" | 無障礙標籤。 |
autoPlay | true、false | true | 是否自動播放。 |
interval | number | 6000 | 自動切換間隔,單位為毫秒。 |
<ImageCarousel
label="HW15 製作步驟"
interval={8000}
images={[
{
src: "/images/homework/computer-animation/hw15/step-1.png",
alt: "建立基本形狀",
caption: "建立基本形狀"
},
{
src: "/images/homework/computer-animation/hw15/step-2.png",
alt: "調整節點",
caption: " "
}
]}
/>
停止自動播放:
<ImageCarousel
autoPlay={false}
images={[
{ src: "/images/example-1.png", caption: " " },
{ src: "/images/example-2.png", caption: " " }
]}
/>
YouTubeEmbed
YouTube 連結必須使用 /embed/ 格式,例如 https://www.youtube.com/embed/VIDEO_ID。
可用參數
| 參數 | 型別/可用值 | 預設 | 說明 |
|---|---|---|---|
src | string | 無 | 單支影片網址。與 videos 擇一使用。 |
title | string | "YouTube video" | 單支影片標題。 |
videos | { src, title? }[] | 無 | 多支影片;有值時會優先於 src。 |
videos[].src | string | 必填 | YouTube embed 網址。 |
videos[].title | string | 自動編號 | iframe 標題。 |
columns | 1、2、3 | 1 | 影片 grid 欄數。手機自動單欄。 |
單支影片:
<YouTubeEmbed
src="https://www.youtube.com/embed/ag5sb85o8jQ"
title="成果影片"
/>
兩支連續影片並排:
<YouTubeEmbed
columns={2}
videos={[
{ src: "https://www.youtube.com/embed/video-id-1", title: "製作過程" },
{ src: "https://www.youtube.com/embed/video-id-2", title: "成果展示" }
]}
/>
三支影片:
<YouTubeEmbed
columns={3}
videos={[
{ src: "https://www.youtube.com/embed/video-id-1" },
{ src: "https://www.youtube.com/embed/video-id-2" },
{ src: "https://www.youtube.com/embed/video-id-3" }
]}
/>
RatingSummary
用於「自我評量與作品建議」的分數。score 必須是陣列,即使只有一個分數也要用 [] 包起來。
可用參數
| 參數 | 型別 | 預設 | 說明 |
|---|---|---|---|
title | string | "自我評量與作品建議" | 顯示在左側的標題。 |
score | string[] | 必填 | 每個字串會顯示為一行分數。 |
<RatingSummary score={["8/10"]} />
多週評分:
<RatingSummary
title="自我評量與作品建議"
score={[
"[week06] 9/10",
"[week08] 8/10"
]}
/>
ScrollReveal
讓包住的內容在進入視窗時淡入上移。一般 Markdown 的標題、段落與上述常用元件已自動套用,不需要再包一次;只在自訂 JSX 區塊需要額外動畫時使用。
可用參數
| 參數 | 型別 | 預設 | 說明 |
|---|---|---|---|
children | JSX 內容 | 必填 | 要產生動畫的內容。 |
className | string | 無 | 加在外層動畫容器的 class。 |
delay | number | 0 | 動畫延遲秒數,例如 0.2。 |
<ScrollReveal delay={0.2}>
<div className="not-prose border border-neutral-200 p-4">
自訂內容
</div>
</ScrollReveal>
Image
Image 是 Next.js 原生圖片元件,已全域提供。除非需要 Next.js 的特殊 props,否則優先使用 ContentImages,因為它已處理圖片寬度、caption、grid 與比例。
常用參數:
<Image
src="/images/example.png"
alt="圖片說明"
width={900}
height={506}
className="h-auto w-full object-contain"
/>
src、alt、width、height 為最常用欄位。使用 fill 時,父容器必須有 relative 與固定高度或 aspect-ratio。
開發與部署
# 一般開發預覽
pnpm dev
# `.next` 快取異常時,清除後重啟
pnpm dev:clean
# production build;先停止 pnpm dev
pnpm build
部署到 Vercel 前確認:
package.json與pnpm-lock.yaml都已提交。- 所有
/images、/thumbs、/files路徑的檔案都在public/。 - MDX 檔名、
slug、動態網址一致。 pnpm build成功。
常見問題
圖片大小沒有改變
width、height 主要提供圖片原始比例與最佳化資訊;畫面寬度應調整 maxWidth。
maxWidth={200}
maxWidth="2xl"
圖片比例不一致
使用 aspectRatio,並決定要完整顯示或裁切:
aspectRatio="video"
fit="contain"
輪播圖沒有說明文字
輪播圖片仍要傳入空白 caption:
{ src: "/images/example.png", caption: " " }
單張圖片沒有說明文字
不要傳 caption,不會自動顯示 Picture 或其他替代文字。
內容更新後出現 Cannot find module './xx.js'
停止正在執行的 pnpm dev,然後執行:
pnpm dev:clean