Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FXVsD1DgdzZLiPuwtKzMSY
63 lines
4.8 KiB
Markdown
63 lines
4.8 KiB
Markdown
---
|
||
name: slideshow
|
||
description: Build a HyperFrames slideshow to present live - slides, fragment reveals, branching hotspots and presenter mode with speaker notes, served by `hyperframes present` - or turn an existing page into one. Use when the person wants a deck or presentation rather than a rendered video.
|
||
---
|
||
|
||
**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin.
|
||
|
||
# HyperFrames slideshow
|
||
|
||
A slideshow is a normal composition (the `/hyperframes` contract applies) plus one JSON island that says which scenes are slides and how they connect. The deliverable is a running deck, not an MP4.
|
||
|
||
## What changes from a video project
|
||
|
||
- **Each slide is a top-level scene** in the composition file: `data-composition-id`, `data-start`, `data-duration`, `data-label`, `data-width`, `data-height`. There is no master root composition wrapping them.
|
||
- **One island per composition**, near the top of `<body>`, written in the HTML (never generated by script; `present` reads the file statically):
|
||
|
||
```html
|
||
<script type="application/hyperframes-slideshow+json">
|
||
{
|
||
"slides": [
|
||
{ "sceneId": "hook", "notes": "Pause on the number." },
|
||
{
|
||
"sceneId": "problem",
|
||
"fragments": [11.0, 15.0],
|
||
"hotspots": [
|
||
{
|
||
"id": "h1",
|
||
"label": "Where does $40B come from?",
|
||
"target": "market-detail",
|
||
"region": { "x": 55, "y": 60, "w": 40, "h": 20 }
|
||
}
|
||
]
|
||
}
|
||
],
|
||
"slideSequences": [
|
||
{ "id": "market-detail", "label": "Market sizing", "slides": [{ "sceneId": "mkt-math" }] }
|
||
]
|
||
}
|
||
</script>
|
||
```
|
||
|
||
- `sceneId` equals a scene's `data-composition-id` (or give `startTime`/`endTime`). `notes` are presenter-only. `ttsScript`, `ttsAudioUrl` and `ttsDurationMs` are reserved and not wired yet.
|
||
- **Fragments are absolute timeline times** (the same clock as `data-start`, not offsets inside the slide) within the slide's `[start, end]`. Navigation seeks, it never plays: each hold state must look right when the timeline is seeked straight to that time. A slide without fragments enters at a rest frame inside it.
|
||
- **Branch slides** are ordinary scenes listed only in `slideSequences`, never in the main `slides` array. A hotspot's `target` is a sequence `id`; `region` is a percentage box (omit it for a full-slide button). Back returns to the slide that held the hotspot.
|
||
- Text the audience must read is at least 40 px at 1920×1080. Before hand-building any named visual, search `npx hyperframes catalog --query "<the visual, in English>" --json`: it ranks the whole registry with nothing installed.
|
||
|
||
## Porting an existing page
|
||
|
||
- Keep the source's look, motion, interactions and media behaviour; port its widgets, players and visualizers rather than approximating them.
|
||
- Movement the page drove by scroll, wheel or a camera layer becomes a camera move from one focal point to the next on slide navigation, never a page visibly scrolling. Keep each move direct, without reversals the source doesn't make.
|
||
- Screenshots, posts, charts, code and any image with readable text are evidence: keep their aspect (`object-fit: contain`) and check all four edges for crops.
|
||
- Every copied `<video>`/`<audio>` needs `data-start` and `data-duration` (`data-has-audio="true"` when its sound plays), and no `preload="none"`. Custom media chrome reads the native element's events and `currentTime`, never its own timer.
|
||
- Hidden stacked slides need `opacity: 0`, `visibility: hidden` and `pointer-events: none` together, or an invisible layer swallows clicks.
|
||
|
||
## Present and hand off
|
||
|
||
- **Never render a deck to MP4**: `render` keeps only the first scene and truncates silently. For stills, `snapshot --at` each slide.
|
||
- Run `npx hyperframes check --json` until it is clean. The slideshow rule checks that every `sceneId` resolves, every hotspot `target` exists, fragments sit inside their slide, and main-line slides don't overlap.
|
||
- Serve it with `npx hyperframes present <project-dir>` and hand over the URL it prints: "Click Present, or press P, to open the audience tab."
|
||
- The project's root `index.html` should open as a working deck: `<hyperframes-slideshow>` around `<hyperframes-player src="…">`, with the raw composition in a subfolder if needed. The component owns Present (P), Prev/Next, the counter, global mute, fullscreen, swipe, hotspots, presenter notes (editable, kept per deck) and stopping media on slide exit. Don't build your own versions of any of these.
|
||
- Screen sharing: in Google Meet share the audience **tab**, which keeps rendering in the background. In Zoom drag the audience tab into its own window and share that; a fully covered window freezes.
|
||
- A deck opened from the bare player bundle without `present` needs workarounds: `references/standalone-harness.md`.
|