anim-slides 是做什麼的?
為簡報投影片製作動畫 SVG 插圖,可從現有投影片或文件產生。
輸出永遠是一個片段:一個包含 <style> 和一個 <svg> 的 HTML 檔案,無腳本、無外部檔案、透過 CSS 變數設定主題,可直接放入任何簡報中。
references/fragment-contract.md 是合約文件。在建立任何東西之前請先閱讀它。
使用時機
- 投影片是一排文字卡片、項目符號清單或螢幕截圖,且內容是關於流程、流程圖、依賴結構、時間軸、比較、架構或時間序列。
- 一份文件(Markdown、Confluence 匯出、README、Mermaid 區塊)描述了某個流程或依賴關係,而使用者希望將其以動畫形式顯示在投影片或頁面上。
- 宿主環境是 HTML 簡報(frontend-slides、reveal.js、impress.js、Slidev、手寫)或一般網頁。如果是
.pptx或 Markdown 來源,應先由某個簡報技能(第 0 步)將其轉換為設計好的 HTML 簡報;此技能再負責加入插圖。
工作流程
<skill-dir> 是指包含此 SKILL.md 的資料夾(Claude Code 會將其暴露為 $CLAUDE_SKILL_DIR;在其他實作中,請使用你載入此檔案的路徑)。腳本需要 Python 3.9+,以及用於渲染的 Chrome 或 Chromium 二進位檔。anim/<name>/ 是位於使用者簡報資料夾旁的一個資料夾。
0. 接收
首先要有 HTML 簡報。 anim-slides 是在 HTML 簡報中加入圖形;它不會設計簡報本身。
- 如果來源已經是 HTML 簡報(reveal.js、impress.js、任何帶有
<section>或.slide的簡報):直接進行。 - 如果來源是
.pptx、Markdown、remark、文件或其他非 HTML 簡報格式:請先讓一個簡報技能來建立 HTML 簡報,而不是用 anim-slides。優先順序如下: (a) 使用者自己的或組織的簡報技能(如果已安裝);(b)frontend-slides(如果已安裝,它會將 pptx 和 Markdown 轉換為設計好的 HTML 簡報;當它提供美學預覽時,選擇最接近原始簡報品牌的樣式,除非使用者想要新的外觀);(c) 只有在沒有安裝任何簡報技能時,才使用scripts/md_to_deck.py作為後備方案(請參閱下面的後備說明),並用一句話告訴使用者,它的輸出是樸素的宿主,不是設計好的簡報,在簡報技能或設計師為其設定樣式之前,它讀起來會像一份文件。在情況 (a) 和 (b) 中,原始.pptx仍然是extract_slides.py和render_deck.py分類(圖表、圖形、註解)的來源,但整合目標是該技能產生的簡報,且匹配的外觀來自該簡報的色板(透過host_theme.py)。請要求簡報技能(或在其之後立即執行)將每個候選投影片的內容包裝在一個固定大小的插槽<div id="sN-slot">中,位於<!-- sN-body -->和<!-- /sN-body -->標記之間(這是md_to_deck.py使用的 id 和標記名稱,不過在那裡插槽是內容的兄弟元素):然後measure_slot.py --selector '#sN-slot'的測量結果會是精確的,而integrate_fragment.py --anchor/--replace-until則以這些標記為目標。 簡報技能自己的字型連結(Google Fonts、Fontshare)是簡報的載入資源,而不是 anim-slides 新增的;請保留它,並在交接時使用bundle_deck.py --fetch將其內聯化。
然後決定適用哪個入口點:
- 現有簡報:執行
python3 <skill-dir>/scripts/extract_slides.py deck.pptx|deck.html|doc.md -o anim/outline.json和python3 <skill-dir>/scripts/render_deck.py deck -o anim/render,然後查看sheet-NN.png聯絡表(每張包含 12 張編號投影片),而不是逐頁查看(當投影片在outline.json中的圖片數量與其渲染結果中的圖片數量不同時,請檢查投影片版面配置中的圖片:版面配置上的封面照片很容易被忽略);然後使用references/input-stage.md進行分類,並將結果表寫入anim/triage.md:每張投影片都要有一個決定和一個理由,不要悄悄地跳過(每 30 張投影片正常情況下會有 3 到 8 個插圖;在短簡報中,每個流程、結構、時間軸、比較或數據投影片都會得到一個插圖)。 然後為整個簡報確定一次外觀:匹配(片段採用宿主簡報的色板和字型,這是品牌或企業簡報的預設值)或精簡(片段在深色背景上保留精簡色板,適用於講座和提案)。python3 <skill-dir>/scripts/host_theme.py deck.html --mode match -o anim/host-theme.css從簡報的 CSS 中推導出匹配的膠合樣式(--mode lean會寫入精簡膠合樣式);將推導出的區塊與簡報自己的變數(例如一個暗淡的文字顏色、一個次要背景)比較一次,並在推導結果選取了計算後的混合值時手動修正;然後使用兩種方式渲染第一個片段(render-phases.sh --theme anim/host-theme.css ...),並在建立其餘片段之前顯示兩個靜止畫面,除非使用者已經指定了外觀。兩種模式之間只有顏色和字型不同:references/style.md中的形狀規則在兩種模式下都成立。 控制模式為整個簡報設定為once,除非使用者要求點擊逐步播放:每張投影片的圖形在投影片出現時播放一個平靜的回合,然後靜止。不要自行推薦step。只有在outline.json報告builds大於 0 時(來源已經透過點擊逐步建構:reveal.js 片段、PowerPoint 點擊動畫、impress.js 子步驟、簡報自己的建構步驟),才用一句話提及:「你的簡報透過點擊建構;圖形也可以這樣做 (step),如果你想要的話請告訴我」。在step模式下,入場動畫呈現一次,每次點擊進入下一階段;這些階段就是簡報敘述的階段(每張投影片 3 到 5 個),片段檔案相同,點擊事件則依附在簡報自己的導航上。Step 模式只監聽按鍵,所以 step 簡報需使用鍵盤或簡報筆來呈現(使用滾輪或滑動導航的簡報,如 frontend-slides,在滾動時會跳過階段)。在anim/triage.md的標頭中寫下「外觀」和「控制」的設定。 - 後備方案,沒有安裝簡報技能(Markdown、remark 或從 pptx 萃取出的 Markdown):
python3 <skill-dir>/scripts/md_to_deck.py deck.md -o deck.html會建立一個樸素的、自包含的宿主來整合(每個投影片一個<section>,.visible門控,列印版面配置,每個投影片內容周圍有<!-- sN-body -->標記)。它不是一個設計好的簡報:用一句話告訴使用者。先將 Markdown 引用的圖片(相對路徑)擷取到同一個資料夾中,否則簡報會顯示佔位符;然後從原始來源(如果有.pptx的話)或 Markdown 中萃取內容。 - 文件:閱讀來源,使用
references/archetypes.md中的表格選擇原型,並萃取一個規格(步驟 1)。 - 單張投影片請求:直接跳到該投影片的步驟 1。 只有在來源關於順序、方向或分組不明確時才詢問使用者。否則,在簡報中陳述假設並繼續進行。
1. 規格
按照 references/spec-format.md 撰寫 anim/<name>/spec.yaml:包含名稱、原型、節點、邊、群組、帶有秒數標記的階段、週期(回合長度)、控制(從接收步驟決定的簡報設定:once,一次平靜的回合,持續 4 到 10 秒,當投影片成為當前投影片時播放,結束於靜止影格;step,相同的回合在階段標記處被切分,因此每次點擊會進入下一個階段;loop,僅當使用者要求連續循環時使用)。片段檔案在 once 和 step 模式下相同;階段標記會變成 data-anim-phases。重用來源本身的詞語作為標籤。保持規格中的節點數約在 12 個以下;否則請拆分。
1b. 檢查池
閱讀 <skill-dir>/pool/index.yaml,然後閱讀 $ANIM_POOL/index.yaml(或列出 $ANIM_POOL/*/),如果該變數已設定,並尋找一個具有相同原型和類似故事或標籤的條目。
- 接近匹配:將資料夾複製到
anim/<name>/,將片段檔案和所有前綴重新命名為<name>,然後在步驟 3 中改為調整標籤、數據、時間和 viewBox,而不是從頭建立。它的spec.yaml(如果有的話)是您規格的起點。 - 僅原型相同:像往常一樣建立骨架,並打開池中的片段作為密度、運動和靜止影格的品質基準。
- 沒有符合的:從規格開始建立;最接近的池條目仍然值得看一眼。 切勿將池中的片段原封不動地複製到簡報中:它的故事只是一個範例,而不是使用者的故事。
2. 插槽與簡報
測量投影片上的可用方框:python3 <skill-dir>/scripts/measure_slot.py deck.html --slide N [--selector SEL] [--ignore .body] 會以舞台像素列印方框尺寸、匹配的 viewBox 和檢查器標誌(references/layouts.md 說明了手動方法)。在絕對定位的版面配置上,不使用 --selector 進行測量會傳回無效數據;請測量插槽 div(--selector '#sN-slot',步驟 0)。選擇一個具有相同寬高比的 viewBox,並根據 references/brief-template.md 填入 anim/<name>/brief.md。簡報會固定 viewBox、渲染尺寸、構圖、階段、靜止影格、主題權杖和最小文字大小,並指定片段取代了投影片上的哪些內容(藥丸、方框圖、項目符號列)。片段的品質取決於簡報的品質。
3. 建構
python3 <skill-dir>/scripts/scaffold_from_spec.py anim/<name>/spec.yaml -o anim/<name>/<name>.html --rendered-width <px>
驗證規格並寫入一個符合合約的骨架(佈局、階段、數據包、註解、靜止影格;字型大小根據渲染寬度提升到最小基準),然後對其執行檢查器。輸出頂部的註解列出了需要手動調整的內容。使用 references/archetypes.md 製作構圖和運動,使用 references/motion-rules.md 調整時間和緩動,並使用 references/style.md 調整外觀(精簡模式:環和細線、無方框、一個發光強調色、單色子標籤、樸素的說明文字),將它調整為簡報描述的插圖。對於多個插圖,從共享的簡報中一次建構一個片段,或者如果實作支援,每個片段使用一個平行子代理;整合始終在主執行緒中進行。
4. 透過視覺檢查
python3 <skill-dir>/scripts/check_fragment.py anim/<name>/<name>.html --name <name> --rendered-width <px> --stage-width <deck stage width>必須通過(前綴、權杖、門控、文字大小、禁止內容)。--stage-width設定文字大小下限(1920 解析度為 18 px,1280 解析度為 12 px);outline.json會報告舞台尺寸。<skill-dir>/scripts/render-phases.sh anim/<name> <W> <H> 1.5 4 7 10 rest會將階段 PNG 和一個聯絡表渲染到一個它會列印出的暫存資料夾中(傳遞--out DIR來選擇輸出目錄)。整合後,render-phases.sh --page deck.html 1920 1080 rest檢查宿主頁面的靜止影格;在多投影片頁面上,加入--slide N(或#hash,或 CSS 選擇器)。對於計時標記或階段影格,請使用--no-script,當頁面本身的腳本會切換門控類別時,但永遠不要對rest使用--no-script:在 frontend-slides 簡報中,.visible既是門控也是顯示投影片的類別,rest 渲染會移除它,而投影片之所以還顯示,是因為簡報的腳本也設定了.active;沒有腳本時,rest 影格會是空白的。使用你的圖片檢視工具打開所有 PNG。修正裁切、重疊、無法閱讀的文字、空白面板。至少疊代一次。- 靜止影格必須看起來像一個完整的靜止圖像(PDF 匯出只會顯示這個)。
python3 <skill-dir>/scripts/verify_facts.py anim/<name>/<name>.html --source anim/outline.json --slide <N>(或--source doc.md)必須通過:片段中的每個數字都來自來源或被標記為說明性;閱讀每個 WARN 並將無法追蹤來源的措辭替換為來源本身的用詞或將其移除。規則在references/accuracy.md中。在這個步驟通過之前,圖形不算完成。 絕對不要相信--virtual-time-budget,也不要相信閱讀原始碼可以取代檢查 PNG。
5. 整合
python3 <skill-dir>/scripts/integrate_fragment.py deck.html anim/<name>/<name>.html --name <name> --anchor '<投影片中的唯一字串>' [--replace-until '<字串>'] [--gate 'section.present'] [--control once|step] --backup
將樣式提升到 <head> 中,在 <!-- anim:<name>:start --> 和 <!-- anim:<name>:end --> 標記之間插入片段(樣式則插入 anim-style 標記之間;重新執行會取代這兩個複本),重寫宿主簡報的門控,並為任何潛入的類別加上前綴。對於一般頁面,請將 <skill-dir>/scripts/snippets/observer.html 複製到頁面中。
--control 設定包裝器上的簡報控制;--control step 也會將 scripts/snippets/step-host.html(一個小型純腳本,無函式庫)放入 <head> 中一次,並從 --gate 設定其門控。控制是整合時整個簡報的開關:重新執行並使用另一個值來更改它,片段保持不變。
錨點和 until 字串會被保留;它們之間的內容會被取代。門控是宿主的「當前投影片」標記:reveal.js 使用 section.present,一般頁面使用 .visible(scripts/snippets/observer.html)以及來自 md_to_deck.py 的簡報。在 md_to_deck.py 的簡報中,以及在步驟 0 中設有插槽標記的簡報技能簡報中,片段會取代內容:--anchor '<!-- sN-body -->' --replace-until '<!-- /sN-body -->'。
檢查投影片的標題和內文在沒有簡報腳本的情況下以及在列印時是否仍會顯示。
簡報技能的人場動畫通常只根據當前投影片類別來觸發顯示(frontend-slides 僅在 .slide.visible 下顯示 .reveal 元素),因此 rest 渲染和 PDF 可能會顯示空白投影片;將這些顯示也繫結到簡報的 active 類別(.slide.active)並加入一條列印規則來顯示它們。(類別名稱 .reveal 也可能與 host_theme.py 的投影片選擇器中的 reveal.js 偵測相符;目前為止無害,但請檢查它找到了哪些投影片。)
它還會加入一個列印保護,以便 PDF 匯出(reveal 的 ?print-pdf 會將每張投影片標記為顯示)能顯示靜止影格。
保持工作用簡報精簡:片段是小的 SVG 文字並透過標記插入,但工作時不要內聯字型、圖片、函式庫或樣式表,並且永遠不要將整個簡報讀入上下文;使用 grep -n、sed -n 讀取錨點周圍的片段,並讓腳本進行編輯。
然後在真實的瀏覽器中打開簡報並觀看……
[原文因截斷而省略]