680 lines
17 KiB
Markdown
680 lines
17 KiB
Markdown
|
|
# Deploying GlitchPlayer Into Another React + Vite App
|
|||
|
|
|
|||
|
|
This guide explains how to include `GlitchPlayer` inside another user interface that is also built with React and Vite.
|
|||
|
|
|
|||
|
|
The target mental model is:
|
|||
|
|
|
|||
|
|
- your host app owns routing, fetching, auth, persistence, and editor UI
|
|||
|
|
- `GlitchPlayer` owns presentation playback, responsive layout, slide rendering, gesture handling, pause/resume behavior, and voting UI
|
|||
|
|
|
|||
|
|
In other words, `GlitchPlayer` should behave like an embeddable runtime surface inside a larger product such as GnommoEditor.
|
|||
|
|
|
|||
|
|
For editor integrations, this package also exposes:
|
|||
|
|
|
|||
|
|
- `GlitchSlideRenderer`
|
|||
|
|
- `GlitchSlideThumbnail`
|
|||
|
|
|
|||
|
|
For template pickers, prefer the normalized availability exports:
|
|||
|
|
|
|||
|
|
- `slideTemplateAvailabilityList`
|
|||
|
|
- `slideTemplateAvailabilityByDisplayMode`
|
|||
|
|
|
|||
|
|
These expand templates by display mode. If a template supports both `square` and `fullscreen`, it appears once for each supported format.
|
|||
|
|
|
|||
|
|
## 1. What The Host App Must Provide
|
|||
|
|
|
|||
|
|
To render `GlitchPlayer`, the host app needs to provide:
|
|||
|
|
|
|||
|
|
- a `presentation` object
|
|||
|
|
- a `slideRegistry`
|
|||
|
|
- an optional player mode
|
|||
|
|
- an optional target slide id
|
|||
|
|
- an optional vote handler
|
|||
|
|
- an optional player event handler
|
|||
|
|
- an optional session id
|
|||
|
|
|
|||
|
|
Today, the public player surface is exported from:
|
|||
|
|
|
|||
|
|
- [src/player/index.ts](/Users/jenstandstad/Projects/gnommoplayer/src/player/index.ts)
|
|||
|
|
|
|||
|
|
The primary component is:
|
|||
|
|
|
|||
|
|
- `GlitchPlayer`
|
|||
|
|
- `GlitchSlideRenderer`
|
|||
|
|
- `GlitchSlideThumbnail`
|
|||
|
|
|
|||
|
|
The key types are:
|
|||
|
|
|
|||
|
|
- `PresentationDefinition`
|
|||
|
|
- `SlideRegistry`
|
|||
|
|
- `VoteRequest`
|
|||
|
|
- `ClientEvent`
|
|||
|
|
|
|||
|
|
For editor-driven slide forms and template pickers, this package also exports:
|
|||
|
|
|
|||
|
|
- `slideTemplateDefinitions`
|
|||
|
|
- `slideTemplateDefinitionList`
|
|||
|
|
- `slideTemplateAvailabilityList`
|
|||
|
|
- `slideTemplateAvailabilityByDisplayMode`
|
|||
|
|
- `slideTemplateDrafts`
|
|||
|
|
- `slideTemplateBudgets`
|
|||
|
|
|
|||
|
|
For hosted interactive Glitch components, the player also supports a base URL convention:
|
|||
|
|
|
|||
|
|
- `VITE_GLITCH_COMPONENT_BASE_URL`
|
|||
|
|
|
|||
|
|
If a `GlitchComponentFrame` slide omits `componentUrl`, the player resolves the iframe source as:
|
|||
|
|
|
|||
|
|
- `${VITE_GLITCH_COMPONENT_BASE_URL || "/glitch"}/glitch_${componentId.replace(/-/g, "_")}/index.html`
|
|||
|
|
|
|||
|
|
That matches the pattern where built Glitch component assets are copied into `public/glitch/...`.
|
|||
|
|
|
|||
|
|
## 2. Public API
|
|||
|
|
|
|||
|
|
Current host usage looks like this:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
import {
|
|||
|
|
GlitchPlayer,
|
|||
|
|
GlitchSlideThumbnail,
|
|||
|
|
createSessionId,
|
|||
|
|
defaultSlideRegistry,
|
|||
|
|
type PresentationDefinition,
|
|||
|
|
type Slide,
|
|||
|
|
type VoteRequest,
|
|||
|
|
type ClientEvent
|
|||
|
|
} from "gnommoplayer";
|
|||
|
|
import "gnommoplayer/player/styles.css";
|
|||
|
|
|
|||
|
|
interface PlayerHostProps {
|
|||
|
|
presentation: PresentationDefinition;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function PlayerHost({ presentation }: PlayerHostProps) {
|
|||
|
|
async function handleVoteCommit(vote: VoteRequest) {
|
|||
|
|
await fetch(`/api/presentations/${presentation.id}/votes`, {
|
|||
|
|
method: "POST",
|
|||
|
|
headers: {
|
|||
|
|
"Content-Type": "application/json"
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify(vote)
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function handlePlayerEvent(event: ClientEvent) {
|
|||
|
|
console.log("[glitch-player-event]", event);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<GlitchPlayer
|
|||
|
|
presentation={presentation}
|
|||
|
|
slideRegistry={defaultSlideRegistry}
|
|||
|
|
mode="playback"
|
|||
|
|
initialSessionId={createSessionId()}
|
|||
|
|
onVoteCommit={handleVoteCommit}
|
|||
|
|
onEvent={handlePlayerEvent}
|
|||
|
|
/>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 2.1 Rendering Slide Thumbnails In An Editor
|
|||
|
|
|
|||
|
|
Use `GlitchSlideThumbnail` for slide lists, cards, and inspector previews. It renders the actual slide component without video playback or player chrome.
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
function SlideCard({
|
|||
|
|
presentationId,
|
|||
|
|
slide,
|
|||
|
|
}: {
|
|||
|
|
presentationId: string;
|
|||
|
|
slide: Slide;
|
|||
|
|
}) {
|
|||
|
|
return (
|
|||
|
|
<GlitchSlideThumbnail
|
|||
|
|
presentationId={presentationId}
|
|||
|
|
slide={slide}
|
|||
|
|
slideRegistry={defaultSlideRegistry}
|
|||
|
|
viewportMode="desktop"
|
|||
|
|
/>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Use `GlitchSlideRenderer` when you want a larger slide-only render surface without the surrounding player.
|
|||
|
|
|
|||
|
|
## 2.1 Previewing A Specific Slide
|
|||
|
|
|
|||
|
|
For editor workflows, `GlitchPlayer` can start in a slide-targeted preview mode.
|
|||
|
|
|
|||
|
|
Use:
|
|||
|
|
|
|||
|
|
- `mode="slide-preview"`
|
|||
|
|
- `targetSlideId="some-slide-id"`
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
<GlitchPlayer
|
|||
|
|
presentation={presentation}
|
|||
|
|
slideRegistry={defaultSlideRegistry}
|
|||
|
|
mode="slide-preview"
|
|||
|
|
targetSlideId="slide-05-chart-single"
|
|||
|
|
initialSessionId={createSessionId()}
|
|||
|
|
/>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
That mode is useful when:
|
|||
|
|
|
|||
|
|
- an editor wants to preview one slide in context
|
|||
|
|
- a template author wants to inspect a specific candidate
|
|||
|
|
- GnommoEditor wants to jump directly to the currently selected slide in the timeline
|
|||
|
|
|
|||
|
|
## 3. Installation Strategy
|
|||
|
|
|
|||
|
|
There are two practical ways to use `GlitchPlayer` inside another Vite app.
|
|||
|
|
|
|||
|
|
### Option A: Local workspace dependency
|
|||
|
|
|
|||
|
|
Use this when the host app and `gnommoplayer` live side-by-side during active development.
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"dependencies": {
|
|||
|
|
"gnommoplayer": "file:../gnommoplayer"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Then install as usual:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm install
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
This is the easiest path while GlitchPlayer is still evolving rapidly.
|
|||
|
|
|
|||
|
|
### Option B: Internal package
|
|||
|
|
|
|||
|
|
Use this when the player should be versioned and consumed more formally.
|
|||
|
|
|
|||
|
|
That usually means:
|
|||
|
|
|
|||
|
|
- adding a proper package export map
|
|||
|
|
- producing a distributable build
|
|||
|
|
- publishing to a private npm registry or workspace package feed
|
|||
|
|
|
|||
|
|
This repo is not fully packaged for that yet, but the code is now organized in a way that makes that a straightforward next step.
|
|||
|
|
|
|||
|
|
### Option C: Built Files Only
|
|||
|
|
|
|||
|
|
Use this when another agent or deployment process is only allowed to fetch compiled assets from this repo.
|
|||
|
|
|
|||
|
|
Build the vendor assets with:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm run build:lib
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Or simply run:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm run build
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
which now builds both the demo app and the vendor assets in one pass.
|
|||
|
|
|
|||
|
|
That produces these stable files:
|
|||
|
|
|
|||
|
|
- `dist/vendor/gnommoplayer.js`
|
|||
|
|
- `dist/vendor/gnommoplayer.css`
|
|||
|
|
|
|||
|
|
This is the best path for GnommoEditor if the integration process is only allowed to pull built artifacts rather than source files.
|
|||
|
|
|
|||
|
|
## 3.1 Local Deployment Commands
|
|||
|
|
|
|||
|
|
This repo includes a deployment helper that copies the latest built vendor bundle into sibling apps.
|
|||
|
|
|
|||
|
|
Available commands:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm run deploy:editor
|
|||
|
|
npm run deploy:web
|
|||
|
|
npm run deploy:all
|
|||
|
|
./deploy.sh editor
|
|||
|
|
./deploy.sh web
|
|||
|
|
./deploy.sh all
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The shell wrapper is useful when you just want a short deploy command from the repo root:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
./deploy.sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
That defaults to:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm run deploy:all
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The npm commands are still available too:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm run deploy:editor
|
|||
|
|
npm run deploy:web
|
|||
|
|
npm run deploy:all
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
These commands:
|
|||
|
|
|
|||
|
|
- run `npm run build:lib`
|
|||
|
|
- ensure the vendor artifacts exist
|
|||
|
|
- copy the latest bundle into the target app
|
|||
|
|
|
|||
|
|
Current target paths:
|
|||
|
|
|
|||
|
|
- `../gnommoeditor/src/vendor/gnommoplayer`
|
|||
|
|
- `../gnommoweb/src/vendor/gnommoplayer`
|
|||
|
|
|
|||
|
|
The underlying script lives at:
|
|||
|
|
|
|||
|
|
- [scripts/deploy-vendor.mjs](/Users/jenstandstad/Projects/gnommoplayer/scripts/deploy-vendor.mjs)
|
|||
|
|
|
|||
|
|
You can also call it directly:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
node scripts/deploy-vendor.mjs editor
|
|||
|
|
node scripts/deploy-vendor.mjs web
|
|||
|
|
node scripts/deploy-vendor.mjs all
|
|||
|
|
node scripts/deploy-vendor.mjs all --skip-build
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Use `--skip-build` only when you know `dist/vendor/` already contains the bundle you want to publish.
|
|||
|
|
|
|||
|
|
## 4. Styling Requirements
|
|||
|
|
|
|||
|
|
Import the player stylesheet once in the host app:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
import "gnommoplayer/player/styles.css";
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
If the host app is consuming built files directly instead of the source package, import the compiled vendor assets instead:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
import "../vendor/gnommoplayer/gnommoplayer.css";
|
|||
|
|
import {
|
|||
|
|
GlitchPlayer,
|
|||
|
|
GlitchSlideThumbnail,
|
|||
|
|
defaultSlideRegistry
|
|||
|
|
} from "../vendor/gnommoplayer/gnommoplayer.js";
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Inside this repo, that stylesheet entry currently lives at:
|
|||
|
|
|
|||
|
|
- [src/player/styles.css](/Users/jenstandstad/Projects/gnommoplayer/src/player/styles.css)
|
|||
|
|
|
|||
|
|
Important notes:
|
|||
|
|
|
|||
|
|
- The player expects to own its own visual surface.
|
|||
|
|
- The host should avoid wrapping it in containers that impose additional padding, border radius, or clipping unless that is intentional.
|
|||
|
|
- The player uses viewport-aware layout logic and expects enough room to center its stage within the available host surface.
|
|||
|
|
|
|||
|
|
Recommended host wrapper:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
export function PlayerScreen({ children }: { children: React.ReactNode }) {
|
|||
|
|
return (
|
|||
|
|
<div
|
|||
|
|
style={{
|
|||
|
|
width: "100%",
|
|||
|
|
minHeight: "100vh",
|
|||
|
|
background: "#6c6c6c"
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
{children}
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
If the host app has its own page chrome, toolbars, or inspector panels, mount the player inside a dedicated content region rather than inside an arbitrary card component.
|
|||
|
|
|
|||
|
|
## 4.1 Built-File Vendor Workflow
|
|||
|
|
|
|||
|
|
For a sibling React + Vite app that is only allowed to fetch built files, the cleanest integration flow is:
|
|||
|
|
|
|||
|
|
1. Run `npm run build:lib` in `gnommoplayer`
|
|||
|
|
2. Copy:
|
|||
|
|
- `dist/vendor/gnommoplayer.js`
|
|||
|
|
- `dist/vendor/gnommoplayer.css`
|
|||
|
|
3. Place them in a vendor folder inside the host app, for example:
|
|||
|
|
- `src/vendor/gnommoplayer/gnommoplayer.js`
|
|||
|
|
- `src/vendor/gnommoplayer/gnommoplayer.css`
|
|||
|
|
4. Import the CSS once near the app root
|
|||
|
|
5. Import the JS module where the host renders the player or thumbnails
|
|||
|
|
|
|||
|
|
The built JS bundle already inlines the dynamic slide imports, so the host does not need to manage extra chunk files for the standard slide set.
|
|||
|
|
|
|||
|
|
## 5. Presentation Data Contract
|
|||
|
|
|
|||
|
|
`GlitchPlayer` expects a `PresentationDefinition`.
|
|||
|
|
|
|||
|
|
That type is currently defined in:
|
|||
|
|
|
|||
|
|
- [src/types/lecture.ts](/Users/jenstandstad/Projects/gnommoplayer/src/types/lecture.ts)
|
|||
|
|
|
|||
|
|
At the moment, `PresentationDefinition` is an alias for the existing internal `LectureDefinition` shape.
|
|||
|
|
|
|||
|
|
The important fields are:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
interface PresentationDefinition {
|
|||
|
|
id: string;
|
|||
|
|
title: string;
|
|||
|
|
version: string;
|
|||
|
|
durationSec: number;
|
|||
|
|
segments: VideoSegment[];
|
|||
|
|
slides: Slide[];
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Each presentation needs:
|
|||
|
|
|
|||
|
|
- ordered video segments
|
|||
|
|
- timed slides
|
|||
|
|
- one or more `glitchSlides` per logical slide
|
|||
|
|
- valid `componentKey` values that exist in the registry supplied by the host
|
|||
|
|
|
|||
|
|
## 6. Loading Presentations In The Host App
|
|||
|
|
|
|||
|
|
The host app should fetch presentation data before rendering `GlitchPlayer`.
|
|||
|
|
|
|||
|
|
Example host loader:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
import { useEffect, useState } from "react";
|
|||
|
|
import { GlitchPlayer, type PresentationDefinition } from "gnommoplayer";
|
|||
|
|
|
|||
|
|
export function PresentationRoute({
|
|||
|
|
presentationId
|
|||
|
|
}: {
|
|||
|
|
presentationId: string;
|
|||
|
|
}) {
|
|||
|
|
const [presentation, setPresentation] =
|
|||
|
|
useState<PresentationDefinition | null>(null);
|
|||
|
|
const [error, setError] = useState<string | null>(null);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
let cancelled = false;
|
|||
|
|
|
|||
|
|
fetch(`/api/presentations/${presentationId}`)
|
|||
|
|
.then(async (response) => {
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error(`Failed to load presentation ${presentationId}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (await response.json()) as PresentationDefinition;
|
|||
|
|
})
|
|||
|
|
.then((data) => {
|
|||
|
|
if (!cancelled) {
|
|||
|
|
setPresentation(data);
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
.catch((caughtError: unknown) => {
|
|||
|
|
if (!cancelled) {
|
|||
|
|
setError(
|
|||
|
|
caughtError instanceof Error
|
|||
|
|
? caughtError.message
|
|||
|
|
: "Could not load presentation."
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return () => {
|
|||
|
|
cancelled = true;
|
|||
|
|
};
|
|||
|
|
}, [presentationId]);
|
|||
|
|
|
|||
|
|
if (error) {
|
|||
|
|
return <div>{error}</div>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!presentation) {
|
|||
|
|
return <div>Loading…</div>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<GlitchPlayer
|
|||
|
|
presentation={presentation}
|
|||
|
|
slideRegistry={defaultSlideRegistry}
|
|||
|
|
/>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 7. Slide Registry Integration
|
|||
|
|
|
|||
|
|
The registry maps `componentKey` strings from presentation data to lazily-loaded React modules.
|
|||
|
|
|
|||
|
|
It looks like this:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
type SlideRegistry = Record<
|
|||
|
|
string,
|
|||
|
|
() => Promise<{ default: React.ComponentType<SlideRuntimeProps> }>
|
|||
|
|
>;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
In this repo, the built-in demo registry lives at:
|
|||
|
|
|
|||
|
|
- [src/lib/slideRegistry.ts](/Users/jenstandstad/Projects/gnommoplayer/src/lib/slideRegistry.ts)
|
|||
|
|
|
|||
|
|
Example host registry:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
import type { SlideRegistry } from "gnommoplayer";
|
|||
|
|
|
|||
|
|
export const gnommoEditorSlideRegistry: SlideRegistry = {
|
|||
|
|
SquareYellow: () => import("./slides/SquareYellow"),
|
|||
|
|
FullscreenSplit: () => import("./slides/FullscreenSplit"),
|
|||
|
|
RevenueChart: () => import("./slides/RevenueChart"),
|
|||
|
|
OrbitalModel3D: () => import("./slides/OrbitalModel3D")
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Rules to keep in mind:
|
|||
|
|
|
|||
|
|
- Every `componentKey` referenced by the presentation must exist in the registry.
|
|||
|
|
- Components should be deterministic and reasonably lightweight.
|
|||
|
|
- Prefer `componentKey + props` over arbitrary runtime code from a database.
|
|||
|
|
- If the host supports custom submitted code, that code should be reviewed or sandboxed before entering the public registry.
|
|||
|
|
|
|||
|
|
## 8. Vote Handling
|
|||
|
|
|
|||
|
|
The player can emit vote requests through `onVoteCommit`.
|
|||
|
|
|
|||
|
|
That callback receives a `VoteRequest`:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
interface VoteRequest {
|
|||
|
|
lectureId: string;
|
|||
|
|
slideId: string;
|
|||
|
|
previousGlitchSlideId: string;
|
|||
|
|
selectedGlitchSlideId: string;
|
|||
|
|
voteDirection?: "up" | "down";
|
|||
|
|
sessionId: string;
|
|||
|
|
timestamp: string;
|
|||
|
|
deviceContext: DeviceContext;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Even though the field is still named `lectureId` internally, treat it as the presentation id for integration purposes.
|
|||
|
|
|
|||
|
|
Recommended host behavior:
|
|||
|
|
|
|||
|
|
- send the vote request to your own API
|
|||
|
|
- include auth/session context on the server side
|
|||
|
|
- do not block the UI longer than necessary
|
|||
|
|
- log failures, but keep playback usable
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
async function handleVoteCommit(vote: VoteRequest) {
|
|||
|
|
const response = await fetch(`/api/presentations/${vote.lectureId}/votes`, {
|
|||
|
|
method: "POST",
|
|||
|
|
headers: {
|
|||
|
|
"Content-Type": "application/json"
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify(vote)
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error("Vote request failed.");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 9. Player Events
|
|||
|
|
|
|||
|
|
The host can also listen to runtime events via `onEvent`.
|
|||
|
|
|
|||
|
|
Current events include things like:
|
|||
|
|
|
|||
|
|
- `lecture_loaded`
|
|||
|
|
- `segment_changed`
|
|||
|
|
- `slide_activated`
|
|||
|
|
- `soft_pause_started`
|
|||
|
|
- `soft_pause_ended`
|
|||
|
|
- `glitch_slide_changed`
|
|||
|
|
- `vote_committed`
|
|||
|
|
|
|||
|
|
This is useful for:
|
|||
|
|
|
|||
|
|
- analytics
|
|||
|
|
- debugging
|
|||
|
|
- editor preview tooling
|
|||
|
|
- QA capture
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
function handlePlayerEvent(event: ClientEvent) {
|
|||
|
|
if (event.type === "slide_activated") {
|
|||
|
|
console.log("Active slide:", event.slideId, event.glitchSlideId);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 10. Recommended Host Layout
|
|||
|
|
|
|||
|
|
For a host app like GnommoEditor, a strong integration pattern is:
|
|||
|
|
|
|||
|
|
- app shell owns toolbar, navigation, inspector, and editor panels
|
|||
|
|
- central preview region owns the player mount
|
|||
|
|
- the player fills that preview region
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
export function EditorPreviewPane({
|
|||
|
|
presentation
|
|||
|
|
}: {
|
|||
|
|
presentation: PresentationDefinition;
|
|||
|
|
}) {
|
|||
|
|
return (
|
|||
|
|
<section
|
|||
|
|
style={{
|
|||
|
|
flex: 1,
|
|||
|
|
minWidth: 0,
|
|||
|
|
minHeight: 0,
|
|||
|
|
background: "#5d5d5d"
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<GlitchPlayer
|
|||
|
|
presentation={presentation}
|
|||
|
|
slideRegistry={defaultSlideRegistry}
|
|||
|
|
/>
|
|||
|
|
</section>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Important:
|
|||
|
|
|
|||
|
|
- make sure the containing region allows height to collapse properly with `min-height: 0` when used inside flex or grid shells
|
|||
|
|
- avoid nesting inside containers with accidental `overflow: hidden` unless intended
|
|||
|
|
- avoid wrapping the player in padded cards, because the player already frames its own stage
|
|||
|
|
|
|||
|
|
## 11. Vite Considerations
|
|||
|
|
|
|||
|
|
If the host app is also using Vite:
|
|||
|
|
|
|||
|
|
- dynamic `import()` for slide components will work well
|
|||
|
|
- CSS import from the player should be straightforward
|
|||
|
|
- local workspace dependency is usually the easiest during development
|
|||
|
|
|
|||
|
|
Potential issues to watch:
|
|||
|
|
|
|||
|
|
- duplicated React versions between host and player
|
|||
|
|
- CSS ordering conflicts if the host imports many global styles after the player stylesheet
|
|||
|
|
- path alias assumptions if you later move this from repo-local import to packaged dependency
|
|||
|
|
|
|||
|
|
Recommended safeguards:
|
|||
|
|
|
|||
|
|
- keep React and ReactDOM deduped in the workspace
|
|||
|
|
- import player styles at the app shell level
|
|||
|
|
- keep slide modules in the host app, not hidden inside the player package, if GnommoEditor should own template evolution
|
|||
|
|
|
|||
|
|
## 12. Minimal GnommoEditor Integration Plan
|
|||
|
|
|
|||
|
|
If the target is GnommoEditor, the simplest inclusion path is:
|
|||
|
|
|
|||
|
|
1. Add `gnommoplayer` as a workspace or file dependency.
|
|||
|
|
2. Import `GlitchPlayer` and `player/styles.css`.
|
|||
|
|
3. Fetch the current presentation from GnommoEditor’s backend.
|
|||
|
|
4. Build a host-owned slide registry matching the templates GnommoEditor supports.
|
|||
|
|
5. Pass vote commits into a GnommoEditor API endpoint.
|
|||
|
|
6. Optionally use `onEvent` to power preview analytics or editor debugging.
|
|||
|
|
|
|||
|
|
That gives GnommoEditor responsibility for:
|
|||
|
|
|
|||
|
|
- data
|
|||
|
|
- registry
|
|||
|
|
- persistence
|
|||
|
|
- product shell
|
|||
|
|
|
|||
|
|
And leaves GlitchPlayer responsible for:
|
|||
|
|
|
|||
|
|
- playback
|
|||
|
|
- timeline
|
|||
|
|
- responsive stage layout
|
|||
|
|
- slide rendering
|
|||
|
|
- drag browsing
|
|||
|
|
- voting interaction UI
|
|||
|
|
|
|||
|
|
## 13. Suggested Next Steps Before Production Embedding
|
|||
|
|
|
|||
|
|
Before using this as a stable shared dependency, I recommend:
|
|||
|
|
|
|||
|
|
1. Rename remaining internal `lecture*` event/type fields to `presentation*` where possible.
|
|||
|
|
2. Add a package export map for `src/player/index.ts`.
|
|||
|
|
3. Add a small integration example app or Storybook-like preview host.
|
|||
|
|
4. Add a few host-level visual regression tests for portrait and landscape framing.
|
|||
|
|
5. Decide whether the player package or the host app owns built-in default slide templates.
|
|||
|
|
|
|||
|
|
## 14. Current Reality Check
|
|||
|
|
|
|||
|
|
Right now, GlitchPlayer is reusable enough to embed in another React + Vite app, especially in a workspace-style setup.
|
|||
|
|
|
|||
|
|
It is not yet a polished published library, but it already has the right architectural boundary:
|
|||
|
|
|
|||
|
|
- host app provides presentation + registry + persistence
|
|||
|
|
- GlitchPlayer provides runtime playback and interaction
|
|||
|
|
|
|||
|
|
That is the correct seam for moving this into GnommoEditor.
|