首先要保持此技能最新 — 在執行前先與使用者確認:
npx hyperframes skills update slideshow。若一切已是最新,此指令會快速執行且不造成任何變動;否則它會先更新此技能及其依賴的核心領域技能,讓你後續能依賴它們。
figma 來源:如果簡報的內容或分鏡來自 figma.com 網址,請先執行
/figma— 包括素材匯出、品牌權杖以及分鏡重建(如果來源是一串場景畫格),然後再根據其輸出建立簡報。不要直接透過原始的 MCP 工具操作 Figma:那樣會跳過 SVG 清理、.media/manifest.jsonl來源追溯以及品牌權杖的var()綁定,導致後續品牌變更無法自動傳播,必須重新匯入。
投影片創作合約
HyperFrames 投影片其實就是一個標準的 HyperFrames 組合 — 包含場景、片段、GSAP 時間軸 — 但多了一個額外成分:一個 JSON 島,用來宣告哪些場景是投影片以及它們如何連接。播放器的 SlideshowController 會讀取這個島,並將連續的 GSAP 時間軸轉換成離散、可導覽的簡報。
先閱讀 /hyperframes-core 以了解基礎組合合約(片段、軌道、data-* 屬性、確定性規則)。此技能僅涵蓋新增的部分:島的結構、投影片撰寫規則、片段動畫、分支、驗證及包裹元件。
輸出 — 可導覽的簡報,而非線性 MP4
投影片的輸出是執行中的簡報:使用 hyperframes present <project-dir>(或 Studio 簡報模式)來提供服務 — 播放器的 SlideshowController 會讀取島並驅動導覽、片段動畫、分支及簡報者模式。請參閱下方的 簡報與交付。
不要對投影片執行 hyperframes render 來產生單一 MP4。 簡報是以數個頂層場景組合(每個投影片一個 data-composition-id)來創作,且沒有包裹它們的主根組合,因此 render 只會解析第一個組合,並產生靜默截斷的 MP4(例如一個 40 秒的簡報只輸出 6 秒)。線性的主線匯出(僅主要投影片,不包括分支序列)目前尚未實作 — 在此功能推出前,支援的輸出是即時 present 簡報以及每張投影片的 snapshot 靜態圖片。如果使用者需要線性 MP4,請說明此限制,而非將 render 指向此簡報。
意圖確認
如果使用者明確要求「投影片」、「幻燈片」或「HyperFrames 投影片」,則直接使用此技能。當請求是透過 /hyperframes 到達時,意圖層的分類會負責此確認 — 路由到此處表示已經確認,因此不必再次詢問;該層的執行形狀問題不適用(交付物是簡報,而非渲染影片)。如果存在 BRIEF.md,它承載了已確認的意圖 — 請讀取它。
如果此技能是從相鄰的請求觸發,例如「簡報」、「募資簡報」、「deck」、「互動 deck」或「轉換此頁面」,請在創作前暫停,並先說明選擇,再請求確認。簡短說明 HyperFrames 投影片意味著一個可執行的簡報,具備離散投影片、內建導覽與簡報者模式、可編輯的發言筆記、共享的媒體處理以及交付前的驗證。對於來源頁面轉換,也請說明目標是保留原始頁面的視覺設計、互動、動態及媒體行為,同時將頁面移動轉換為投影片之間的轉場效果。
然後詢問一個簡短的確認問題:
你想要這個作為 HyperFrames 投影片嗎?
在環境提供選擇 UI 時使用是/否選擇;否則以純文字提出問題。
在使用者回答「是」之前,不要實作投影片。如果他們說「不」,請停止使用此技能 — 讀取 /hyperframes 並讓意圖層重新路由。此確認是一個路由決策,而非偏好閘門 — 根據 ../hyperframes/references/brief-contract.md § 1,它在自主模式(「隨便」不會跳過它)中仍然有效:建立錯誤的交付物類型是品質故障,而非創意決定。
兩個部分
1. 場景 — 以標準方式宣告
每張投影片都對應一個場景。使用 data-composition-id、data-start、data-duration 和 data-label 宣告場景:
<div
data-composition-id="problem"
data-start="0"
data-duration="8"
data-label="The problem"
data-width="1920"
data-height="1080"
>
<!-- 片段放在這裡 -->
</div>
分支投影片(僅可透過熱區到達,不包含在主線中)以完全相同的方式宣告 — 它們只會出現在島的 slideSequences 條目中,而非主 slides 陣列中。
2. JSON 島 — 每個組合一個 script 區塊
在組合 HTML 中新增恰好一個 <script type="application/hyperframes-slideshow+json"> 區塊。它包含所有投影片元資料:
<script type="application/hyperframes-slideshow+json">
{
"slides": [...],
"slideSequences": [...]
}
</script>
島是投影片順序、備忘稿、片段動畫停頓點、熱區及分支序列的唯一真實來源。將其放在 <body> 頂部附近,在場景 div 之前,以便於尋找。
不要將投影片清單隱藏在替代的 <script type="application/json"> 區塊加上建立島的執行時期程式碼之後。present 指令會靜態讀取組合 HTML,並預期真正的 application/hyperframes-slideshow+json 島已經存在。
結構
SlideshowManifest(頂層島物件)
{
"slides": [
/* SlideRef[] — 主線,依序 */
],
"slideSequences": [
/* SlideSequence[] — 離線分支序列 */
]
}
SlideRef
{
"sceneId": "problem",
"notes": "先從痛點開始,而不是公司介紹。",
"fragments": [3.5, 5.2, 7.0],
"hotspots": [
/* SlideHotspot[] */
],
"ttsScript": null,
"ttsAudioUrl": null,
"ttsDurationMs": null
}
| 欄位 | 必填 | 說明 |
|---|---|---|
sceneId | 是 | 必須與場景的 data-composition-id 完全相符(或提供明確的 startTime/endTime)。lint 規則透過 data-composition-id 解析場景。 |
notes | 否 | 僅供簡報者使用的文字。絕不顯示給觀眾。 |
fragments | 否 | 投影片 [start, end] 範圍內的時間點(秒)陣列 — 請參閱下方的片段動畫。 |
hotspots | 否 | 觸發分支的互動式疊加層 — 請參閱下方的分支。 |
startTime | 否 | 選填。覆蓋比對到的場景時間範圍;預設為場景的開始/結束。 |
endTime | 否 | 選填。覆蓋比對到的場景時間範圍;預設為場景的開始/結束。 |
ttsScript, ttsAudioUrl, ttsDurationMs | 否 | 保留。 結構欄位存在但 TTS 播放尚未實作。除非你正在為未來建置預先填入資料,否則請省略。 |
SlideHotspot
{
"id": "h1",
"label": "我們是如何計算出這個數字的?",
"target": "market-deep-dive",
"region": { "x": 60, "y": 10, "w": 35, "h": 20 }
}
| 欄位 | 必填 | 說明 |
|---|---|---|
id | 是 | 在投影片內唯一。 |
label | 是 | 顯示給觀眾的工具提示/按鈕文字。 |
target | 是 | 必須與 slideSequences 中的 SlideSequence.id 相符。 |
region | 否 | 投影片百分比邊界框:{x, y, w, h},範圍 0–100。省略時,熱區會渲染為全投影片的標籤按鈕。 |
SlideSequence
{
"id": "market-deep-dive",
"label": "市場規模估算方法",
"slides": [{ "sceneId": "mkt-1" }, { "sceneId": "mkt-2" }]
}
序列內部的 slides 使用與主線相同的 SlideRef 形狀。允許使用片段動畫和巢狀熱區。
投影片撰寫規則
這些是硬性限制,而非建議。違反規則的投影片在被審查者看到時會直接被置換。
- 標題是完整句子的主張,而非標籤。 寫「SMB 每週花 14 小時在人工排程上」,而不是「排程問題」。這句話應該在忽略視覺元素時也能獨立成立。
- 每張投影片一個想法 + 一個視覺。 如果你忍不住想加上第二個項目符號群或第二張圖表,請將投影片分拆。
- 結論先行。 最強論點放在最前面 — 在投影片上和簡報順序中都是。投資人從左到右、從上到下閱讀,而且他們會停下來。
- 僅限由下而上的市場規模估算。 在沒有顯示計算過程的情況下,切勿撰寫「500 億美元 TAM」。從單位經濟學向上建立:帳戶數 × ACV,或交易數 × 抽成比率。
- 字體最小 30pt 等效。 在 1920×1080 解析度下,標題為 72–96px;內文為 48px。對於觀眾必須閱讀的任何文字,絕不低於 40px。
- 在從頭建立任何具名視覺元素之前,先搜尋即時目錄。 對於投影片所需的每種外觀、效果、圖表、處理或轉場 — 「CRT 掃描線」、「故障」、「長條圖競賽」、「閃光掃過」、「終端機視窗」 — 請先執行
npx hyperframes catalog --query "<以純英文描述視覺效果>" --json並閱讀最佳結果,然後再創作投影片的片段。此搜尋無需安裝任何東西:不需專案、不需事先add、不需帳號。它會從任何目錄對整個託管註冊表(約 400 個區塊和元件)進行排名。npx hyperframes add <名稱>會將區塊的原始碼放入簡報中,你可以就地自訂它。此規則在移植來源頁面時特別重要:真實的區塊優於下方移植規則所禁止的簡化近似物。
移植來源頁面
將現有頁面轉換為投影片時,來源忠實度是合約的一部分。除非使用者明確要求重新設計,否則不要用簡化的近似物取代來源特定的 widget。
- 盡可能實際地保留原始頁面的視覺設計、動態語言、互動行為、媒體行為及簡報呈現方式。當投影片系統支援簡報者模式時,請使用共享的可編輯備忘稿行為包含發言筆記,而非投影片特定的實作。
- 盡可能精確地移植來源 DOM/CSS/JS 中的機械式視覺元素:自訂播放器、畫布視覺化工具、時間軸、播放頭、音莖、展開的圓形、懸停狀態及其他互動細節,都應在轉換後保留下來。
- 將原生
<video>/<audio>元素視為任何自訂媒體 chrome、畫布視覺化工具、波形、節奏網格或播放頭的真實來源。連接來源的媒體事件(play、pause、timeupdate、seeking、seeked、ended、ratechange、volumechange),並從media.currentTime推導視覺狀態;不要執行可能與實際播放不同步的獨立定時器。 - 每個帶有
src的複製<video>或<audio>必須在 lint 之前具有 HyperFrames 時間屬性:data-start和data-duration,以及在應保留可聽到的原生音訊時加上data-has-audio="true"。對投影片特定的媒體使用場景的時間範圍;對於可能從多張焦點投影片播放的、由使用者控制的證據影片,則使用簡報範圍的時間範圍。不要在媒體上留下preload="none";應使用metadata或auto。 - 在驗證之前解析來源字體權杖。如果要保留自訂來源字體,請為本機/擷取的字體檔案新增
@font-face規則。如果使用系統備用字型,請將權杖化的宣告(如font-family: var(--f-body))替換為具體的渲染安全字型堆疊,例如system-ui, sans-serif或ui-monospace, monospace;不要讓var(...)作為字體系列的值。 - 稽核來源中非典型的頁面移動,特別是受捲動、滾輪、觸控、雜湊狀態、調整大小或 requestAnimationFrame 迴圈驅動的行為。將具有平移/縮放「世界」圖層的固定視口、視差、固定面板、水平捲動器、捲動刷時間軸、章節對齊以及縮放至元素的攝影機視為來源行為。捲動通常是來源的轉場觸發器,因此請透過提取其進度停止點、緩動以及攝影機/焦點狀態來保留轉場,然後將該動態重新託管在投影片導覽上,透過時間軸位置、片段或可重複使用的播放器/掛鉤。獨立的包裝器如果跳到投影片的停頓點,仍然需要明確的導覽-攝影機轉場掛鉤;僅計算每張投影片的攝影機變換是不夠的。不要在投影片內部模擬逐頁向下捲動的轉場;觀眾應該感受到從一個焦點到另一個焦點的攝影機移動/縮放,而不是看到網頁被捲動。保持每個投影片間的攝影機移動連續:避免中間路線停止點導致 x/y 方向反轉或在抵達新投影片視口之前縮放取消。
片段動畫
片段動畫讓一張投影片的內容逐步顯現。SlideRef.fragments 陣列包含投影片 [start, end] 範圍內的時間點(秒)。當簡報者按下前進時,播放器會跳到清單中的下一個時間點。最後一個片段之後的下一個前進動作會進入下一張投影片。
片段動畫時間點必須完全對應 GSAP 時間軸中特定動畫發生的時刻。不要使用近似值:如果「漸入」動畫在 t=3.5 時結束,則 fragments[0] 必須是 3.5,而不是 3.2 或 3.499。
不要僅包含一個片段時間點 — 它應該是要嘛為零個(無片段動畫),要嘛為兩個或以上。單一片段時間點會建立一個在 t=0 時隱藏所有內容、然後在該時間點顯示所有內容的初始狀態,這通常會產生使用者在互動時不預期的斷裂效果。
present 指令會驗證每個片段時間點都落在其投影片場景的資料持續時間內。present 指令會驗證每個片段時間點皆落在其投影片場景所定義的時間範圍內,且在島內任何兩個片段時間點之間沒有完全相同的值。
分支
SlideRef.hotspots 陣列定義了互動式疊加層。每個熱區都會將觀眾導向一個 SlideSequence。進入序列後,觀眾會按順序瀏覽序列中的投影片,然後回到觸發該分支的原始投影片(就像函式呼叫 — 進入、線性進行、返回)。
熱區類型
- 有
region的熱區:定義一個點擊目標(按鈕或可點擊區域)。當region.w和region.h存在且 > 0 時,播放器會建立一個隱形點擊目標,定位在該區域參數提供的百分比內。 - 無
region的熱區:渲染為一個全投影片按鈕,位於投影片右下角附近。這是一個邊緣情況,適用於序列只有一張投影片且該投影片本身是一個單頁證明或參考資料時。
序列導覽
序列導覽與主線導覽相同:觀眾使用前進/後退在序列投影片之間移動。序列的開頭和結尾會自動約束;當觀眾到達序列的最後一張投影片並再次按前進時,序列結束,他們會回到呼叫點。
驗證與 Lint
執行 hyperframes lint <project-dir> 來檢查投影片特定的規則。它會檢查:
- 每個
SlideRef.sceneId都能解析到一個場景的data-composition-id。 - 每個
SlideHotspot.target都能解析到一個SlideSequence.id。 - 主
slides陣列中的所有投影片,其data-start/data-duration時間範圍沒有重疊。 - 所有片段時間點都落在其場景的時間範圍內。
- 沒有重複的
sceneId。 - 分支序列中定義的場景不能同時存在於主
slides陣列中。 - 解析字體權杖標示(
var(--f-...)等)。 - 驗證媒體時間屬性(
data-start、data-duration)是否存在於<video>和<audio>元素上。 - 如果存在
BRIEF.md,則檢查投影片順序是否符合簡報摘要。
簡報與交付
交付物是目錄,而非單一檔案。使用 hyperframes present <project-dir> 或將目錄載入 HyperFrames Studio。
播放器會:
- 讀取組合 HTML 並找到
application/hyperframes-slideshow+json島。 - 透過
data-composition-id將slides中的每個sceneId對應到場景元素。 - 建構一個離散的投影片導覽模型,依序呈現主線投影片,並可選擇性地導覽到分支序列。
- 啟用簡報者模式(如果使用者的環境支援),顯示當前的
notes文字、進度指示器以及投影片切換控制項。 - 在投影片之間提供連續的攝影機移動,而不是跳切。
當沒有現有簡報時,以下是指令的使用方式:
hyperframes init my-deck
cd my-deck
# 在此處創作組合(scene divs + JSON 島)
hyperframes lint .
hyperframes present .
如果使用者已經有一個 HyperFrames 專案,並且想要將一個場景轉換為投影片簡報,請在該場景的 HTML 中建立 JSON 島。
為了在過程中最終進行手動審查,請使用 npx hyperframes snapshot <project-dir> 來為每張投影片產生 PNG 預覽圖。