293 lines
6.8 KiB
Markdown
293 lines
6.8 KiB
Markdown
|
|
# Slide Preview Integration Guide
|
|||
|
|
|
|||
|
|
This guide is for integrating an authoritative slide preview into another React + Vite UI, using the real `GlitchPlayer` runtime rather than a custom thumbnail or placeholder renderer.
|
|||
|
|
|
|||
|
|
## Goal
|
|||
|
|
|
|||
|
|
When the editor user selects a slide, the preview should be:
|
|||
|
|
|
|||
|
|
- the real `GlitchPlayer`
|
|||
|
|
- moved to the selected slide
|
|||
|
|
- paused on that slide
|
|||
|
|
- rendered with the same layout, video, timing, and slide runtime as the final presentation
|
|||
|
|
|
|||
|
|
This is the correct preview surface when fidelity matters more than efficiency.
|
|||
|
|
|
|||
|
|
## Use This, Not A Custom Thumbnail
|
|||
|
|
|
|||
|
|
For the main preview panel:
|
|||
|
|
|
|||
|
|
- use `GlitchPlayer`
|
|||
|
|
- with `mode="slide-preview"`
|
|||
|
|
- and `targetSlideId`
|
|||
|
|
|
|||
|
|
Do not use:
|
|||
|
|
|
|||
|
|
- `GlitchSlideThumbnail`
|
|||
|
|
- `GlitchSlideRenderer`
|
|||
|
|
- a hand-built editor preview
|
|||
|
|
|
|||
|
|
Those are useful for slide lists and lightweight surfaces, but not for the authoritative preview.
|
|||
|
|
|
|||
|
|
## Built Files To Consume
|
|||
|
|
|
|||
|
|
If the integration agent is only allowed to fetch built artifacts from this repo, use:
|
|||
|
|
|
|||
|
|
- [dist/vendor/gnommoplayer.js](/Users/jenstandstad/Projects/gnommoplayer/dist/vendor/gnommoplayer.js)
|
|||
|
|
- [dist/vendor/gnommoplayer.css](/Users/jenstandstad/Projects/gnommoplayer/dist/vendor/gnommoplayer.css)
|
|||
|
|
|
|||
|
|
Build them with:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm run build
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
That command now produces both the demo app build and the vendor bundle.
|
|||
|
|
|
|||
|
|
## What To Import
|
|||
|
|
|
|||
|
|
From `gnommoplayer.js`, the preview integration should import:
|
|||
|
|
|
|||
|
|
- `GlitchPlayer`
|
|||
|
|
- `defaultSlideRegistry`
|
|||
|
|
|
|||
|
|
From `gnommoplayer.css`, import the player stylesheet once near the app root.
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
import "../vendor/gnommoplayer/gnommoplayer.css";
|
|||
|
|
import {
|
|||
|
|
GlitchPlayer,
|
|||
|
|
defaultSlideRegistry,
|
|||
|
|
} from "../vendor/gnommoplayer/gnommoplayer.js";
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Required Data
|
|||
|
|
|
|||
|
|
The preview must receive a full presentation object, not just one slide.
|
|||
|
|
|
|||
|
|
That presentation should include:
|
|||
|
|
|
|||
|
|
- `id`
|
|||
|
|
- `title`
|
|||
|
|
- `version`
|
|||
|
|
- `durationSec`
|
|||
|
|
- `segments`
|
|||
|
|
- `slides`
|
|||
|
|
|
|||
|
|
Why:
|
|||
|
|
|
|||
|
|
- the player needs the full timeline
|
|||
|
|
- the player needs the talking-head video segments
|
|||
|
|
- the player derives the active slide from presentation timing
|
|||
|
|
- the player must match the final runtime, not a simplified editor view
|
|||
|
|
|
|||
|
|
## Preview Contract
|
|||
|
|
|
|||
|
|
The preview panel should render the player like this:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
<GlitchPlayer
|
|||
|
|
presentation={presentation}
|
|||
|
|
slideRegistry={defaultSlideRegistry}
|
|||
|
|
mode="slide-preview"
|
|||
|
|
targetSlideId={selectedSlideId}
|
|||
|
|
initialSessionId={`editor-preview-${presentation.id}`}
|
|||
|
|
/>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Behavior of this mode:
|
|||
|
|
|
|||
|
|
- the player jumps to the targeted slide’s `startTimeSec`
|
|||
|
|
- playback starts paused
|
|||
|
|
- the real player UI and layout are used
|
|||
|
|
- the talking-head video is still part of the runtime surface
|
|||
|
|
|
|||
|
|
## Recommended Host Component
|
|||
|
|
|
|||
|
|
The host app should wrap this in a dedicated preview component.
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
import { useEffect, useState } from "react";
|
|||
|
|
import "../vendor/gnommoplayer/gnommoplayer.css";
|
|||
|
|
import {
|
|||
|
|
GlitchPlayer,
|
|||
|
|
defaultSlideRegistry,
|
|||
|
|
} from "../vendor/gnommoplayer/gnommoplayer.js";
|
|||
|
|
|
|||
|
|
export function SlidePreviewPanel({
|
|||
|
|
presentationId,
|
|||
|
|
selectedSlideId,
|
|||
|
|
}: {
|
|||
|
|
presentationId: string;
|
|||
|
|
selectedSlideId: string | null;
|
|||
|
|
}) {
|
|||
|
|
const [presentation, setPresentation] = useState(null);
|
|||
|
|
const [error, setError] = useState(null);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
let cancelled = false;
|
|||
|
|
|
|||
|
|
fetch(`/api/videos/${presentationId}/export`)
|
|||
|
|
.then((response) => {
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error(`Preview fetch failed: ${response.status}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return response.json();
|
|||
|
|
})
|
|||
|
|
.then((data) => {
|
|||
|
|
if (!cancelled) {
|
|||
|
|
setPresentation(data);
|
|||
|
|
setError(null);
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
.catch((caughtError) => {
|
|||
|
|
if (!cancelled) {
|
|||
|
|
setError(String(caughtError));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return () => {
|
|||
|
|
cancelled = true;
|
|||
|
|
};
|
|||
|
|
}, [presentationId]);
|
|||
|
|
|
|||
|
|
if (error) {
|
|||
|
|
return <div>Preview error: {error}</div>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!presentation || !selectedSlideId) {
|
|||
|
|
return <div>Select a slide to preview.</div>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div style={{ width: "100%", height: "100%" }}>
|
|||
|
|
<GlitchPlayer
|
|||
|
|
presentation={presentation}
|
|||
|
|
slideRegistry={defaultSlideRegistry}
|
|||
|
|
mode="slide-preview"
|
|||
|
|
targetSlideId={selectedSlideId}
|
|||
|
|
initialSessionId={`editor-preview-${presentation.id}`}
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## How The Editor Should Drive It
|
|||
|
|
|
|||
|
|
The editor should keep one piece of selection state:
|
|||
|
|
|
|||
|
|
- `selectedSlideId`
|
|||
|
|
|
|||
|
|
When the user clicks a slide in the editor:
|
|||
|
|
|
|||
|
|
1. update `selectedSlideId`
|
|||
|
|
2. keep the same `presentation`
|
|||
|
|
3. re-render `GlitchPlayer` with the new `targetSlideId`
|
|||
|
|
|
|||
|
|
That is enough for the player to move to the correct slide.
|
|||
|
|
|
|||
|
|
## Important Implementation Notes
|
|||
|
|
|
|||
|
|
### 1. Use The Full Presentation
|
|||
|
|
|
|||
|
|
Do not try to construct a fake one-slide presentation for preview.
|
|||
|
|
|
|||
|
|
That creates drift in:
|
|||
|
|
|
|||
|
|
- timing
|
|||
|
|
- fullscreen transitions
|
|||
|
|
- video positioning
|
|||
|
|
- segment masking
|
|||
|
|
- variant behavior
|
|||
|
|
|
|||
|
|
The preview should be the real timeline.
|
|||
|
|
|
|||
|
|
### 2. It Is Fine If The Video Loads Again
|
|||
|
|
|
|||
|
|
That is an acceptable tradeoff for now.
|
|||
|
|
|
|||
|
|
The goal of the editor preview is correctness:
|
|||
|
|
|
|||
|
|
- same player
|
|||
|
|
- same slide runtime
|
|||
|
|
- same layout
|
|||
|
|
|
|||
|
|
Not maximum efficiency.
|
|||
|
|
|
|||
|
|
### 3. Keep Thumbnails Separate
|
|||
|
|
|
|||
|
|
This preview guide is only for the main preview panel.
|
|||
|
|
|
|||
|
|
For slide list cards:
|
|||
|
|
|
|||
|
|
- `GlitchSlideThumbnail` is still appropriate
|
|||
|
|
|
|||
|
|
For the large “truth” preview:
|
|||
|
|
|
|||
|
|
- use `GlitchPlayer`
|
|||
|
|
|
|||
|
|
### 4. Prefer Stable Slide Ids
|
|||
|
|
|
|||
|
|
The editor should pass a stable `selectedSlideId`, ideally the same id used in the exported presentation payload.
|
|||
|
|
|
|||
|
|
The preview mechanism depends on exact id matching:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
targetSlideId={selectedSlideId}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5. Do Not Replace The Player’s Internal Registry Logic
|
|||
|
|
|
|||
|
|
Use:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
slideRegistry={defaultSlideRegistry}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
unless the host intentionally provides a superset registry.
|
|||
|
|
|
|||
|
|
The preview should not invent its own runtime component resolution.
|
|||
|
|
|
|||
|
|
## When To Use `GlitchSlideThumbnail` Instead
|
|||
|
|
|
|||
|
|
Use `GlitchSlideThumbnail` only for:
|
|||
|
|
|
|||
|
|
- slide list rows
|
|||
|
|
- compact cards
|
|||
|
|
- mini previews in inspector sidebars
|
|||
|
|
|
|||
|
|
Do not use it as a substitute for the main preview panel.
|
|||
|
|
|
|||
|
|
## Recommended Replacement Strategy
|
|||
|
|
|
|||
|
|
If the current editor preview is “kind of not working,” replace it with this sequence:
|
|||
|
|
|
|||
|
|
1. Keep the slide list as-is for now
|
|||
|
|
2. Remove the custom preview widget
|
|||
|
|
3. Add a dedicated preview panel component
|
|||
|
|
4. Fetch the full exported presentation JSON
|
|||
|
|
5. Render:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
<GlitchPlayer
|
|||
|
|
presentation={presentation}
|
|||
|
|
slideRegistry={defaultSlideRegistry}
|
|||
|
|
mode="slide-preview"
|
|||
|
|
targetSlideId={selectedSlideId}
|
|||
|
|
/>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
That gives you a preview surface that is as close to final runtime as possible.
|
|||
|
|
|
|||
|
|
## Short Handoff Note
|
|||
|
|
|
|||
|
|
If you need the shortest possible instruction for the other agent, use this:
|
|||
|
|
|
|||
|
|
Use the real player for slide preview, not the thumbnail renderer. Import `GlitchPlayer` and `defaultSlideRegistry` from `dist/vendor/gnommoplayer.js`, import `dist/vendor/gnommoplayer.css`, fetch the full presentation JSON, and render `GlitchPlayer` with `mode="slide-preview"` and `targetSlideId={selectedSlideId}`.
|