open-slide 的定位是「為 agent 而生的簡報框架」:一頁 slide 就是一個填滿 1920×1080 畫布的 React 元件,沒有 DSL、沒有版面模板約束,框架負責畫布、縮放、導航、hot reload、present mode 與匯出,內容交給你和你的 coding agent。這個設計取向決定了它的優勢與代價,兩者都很明確。
檔案契約
一份 deck 住在 slides/<kebab-case-id>/,入口是 index.tsx,資產放在同層的 assets/。模組的預設匯出是頁面陣列,順序就是播放順序:
import { type Page, type SlideMeta, useSlidePageNumber } from '@open-slide/core';
const Cover: Page = () => {
const { current, total } = useSlidePageNumber();
return (
<section
style={{
width: '100%',
height: '100%',
background: '#08090a',
color: '#f7f8f8',
position: 'relative',
overflow: 'hidden',
}}
>
<p>{current} / {total}</p>
<h1>Q2 launch</h1>
</section>
);
};
export const meta: SlideMeta = { title: 'Q2 launch', theme: 'corporate' };
export default [Cover] satisfies Page[];契約只有四條:
Page不接受 props。 需要頁面脈絡就用 hook(useSlidePageNumber())。- 預設匯出是顯示順序:
export default [Cover, Body] satisfies Page[]。 - 根元素要填滿容器:
width: '100%'、height: '100%'。 - 每頁都渲染進固定的 1920×1080 畫布,runtime 均勻縮放。
可選匯出三個:meta(title、theme、createdAt)、notes(與頁面索引對齊的字串陣列,見 14)、transition(deck 層預設轉場)。
固定畫布為什麼是對的設計
網頁不敢用固定座標,因為視口尺寸未知;簡報敢,因為投影的長寬比實質上被鎖定在 16:9,而且畫面的目標是「所有人看到同一個構圖」而非「適應每個裝置」。open-slide 因此選擇了與遊戲相同的策略:固定設計解析度 + 均勻縮放取景(letterbox),而不是重排。這條分野在 全響應式專欄的第一篇 有完整的推導。
代價與紅利都很具體:
- 紅利:你寫下的每一個像素座標就是最終座標,在預覽、present mode、縮圖、總覽與匯出中完全一致。絕對定位變成安全的工具,而不是技術債。
- 紅利:畫布不捲動,所以「內容塞不下」變成一個編譯期就會被發現的設計問題,而不是上台才發現的意外。
/slide-authoring的規則是寧可拆成兩頁也不縮字級——這條約束直接執行了「一頁一重點」。 - 代價:不能依賴視口。頁面可能同時被掛載在多個表面(當前畫布、縮圖列、總覽格、theme 預覽、匯出),所以渲染必須是決定性的,瀏覽器專屬的副作用要加防護,否則匯出會壞掉。
與其他工具的實際取捨
| 工具 | 內容形式 | 版面自由度 | 版控/review | agent 友善度 | 適合 |
|---|---|---|---|---|---|
| PowerPoint / Keynote | 二進位檔 | 高(拖曳) | 幾乎無法 diff | 低 | 對方要可編輯檔、公司範本硬性規定 |
| Google Slides | 雲端文件 | 高 | 版本歷史但無 diff | 低 | 多人同時編輯、非技術團隊 |
| Reveal.js | HTML/Markdown | 中(受 CSS 框架約束) | 可 diff | 中 | 網頁原生、需要嵌入式互動 |
| Slidev | Markdown + Vue | 中高(Markdown 優先) | 可 diff,Markdown 極易讀 | 中高 | 開發者演講、內容為主、以文字撰寫為主力 |
| open-slide | React .tsx | 極高(任意元件於固定畫布) | 可 diff,但差異是 JSX | 高(內建 skills + AGENTS.md) | 版面需要客製、要 agent 產出、要跟前端專案共用元件與資料 |
選擇準則直白:
- 內容以文字為主、版面接受框架預設 → Slidev 的 Markdown 撰寫速度勝過寫 JSX。
- 每一頁的版面都不一樣、需要真正的資料視覺化或互動元件 → open-slide 的自由度是實質差異。
- 要讓 AI agent 一次產出整份 deck,再用視覺工具微調 → open-slide 是目前設計上最直接對準這個工作流的。
- 對方要求
.pptx且要能改 → 沒有一個 code-first 工具能滿足;open-slide 的 PPTX 匯出目前是「每頁一張圖」,原生可編輯匯出尚未提供。這是硬限制,不是可以繞過的。
slides-as-code 的真正價值不在寫,在改
用程式碼寫簡報的第一直覺反對意見是「打字比拖曳慢」,這對第一版通常成立。價值出現在第二版之後:
- 結構改動是陣列重排,diff 一眼看得懂,PR 可以 review「這次改了論證順序」而不是「這次改了 8 頁」。
- 共用元件真的共用。定義一次
SectionTitle、Metric、Comparison,全 deck 一致;改一次全改。圖形工具的「範本」做不到這種一致性。 - 資料可以是活的。營收數字從一個
data.ts匯入,更新一次即可,不會有「第 12 頁還是舊數字」的經典事故。 - 審查可以自動化。要檢查「有沒有哪一頁字級低於 32」,是一次 grep,不是人工翻 40 頁。
workspace 的硬規則
npx @open-slide/cli init 會在根目錄產生 AGENTS.md(Claude Code 用的 CLAUDE.md 是它的 symlink,Windows 上是複本),內含框架的硬規則:
- slides 放在
slides/<kebab-case-id>/,入口index.tsx,資產在slides/<id>/assets/。 - 不要動
package.json、open-slide.config.ts或其他 deck。 - 不要新增相依套件,只用 React 與標準 Web API。
最後一條看似限制,實際上是這個框架能穩定跑 agent 工作流的原因:它讓 agent 無法「順手裝一個 UI library」,deck 因此永遠是可移植的純 React。代價是圖表要手寫 SVG,見 07。
五個 skills 放在 workspace 內的 .agents/skills/(Claude Code 另有 .claude/skills/ symlink),隨專案版控;升級套件後用 open-slide sync:skills 重新同步。
專案結構與指令
my-deck/
├── slides/ # 每個 deck 一個資料夾
├── themes/ # 設計系統(.md 配方 + .demo.tsx 預覽)
├── .agents/skills/ # 內建的 agent skills
├── open-slide.config.ts
├── AGENTS.md # agent 硬規則
└── package.json| 指令 | 作用 |
|---|---|
npx @open-slide/cli init | 建立 workspace(pnpm/yarn/bun 有對應寫法) |
npm run dev | 開發伺服器(預設從 5173 起找空 port),含 hot reload、inspector、資產面板 |
npm run build | 產出 dist/ 靜態站,純客戶端渲染 |
npm run preview | 本機服務 dist/,上台前的最終驗證 |
open-slide sync:skills | 升級後重新同步內建 skills |
open-slide.config.ts 全部欄位可選,常用的是 base(部署在子路徑時,前後都要有斜線)與 build 底下的三個開關(showSlideBrowser、showSlideUi、allowHtmlDownload),後者只在靜態建置生效,dev server 永遠顯示完整 UI。