Initial commit after recreate
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
# Gnommo Player
|
||||
|
||||
This project is a proof of concept for an interactive lecture player.
|
||||
|
||||
## Reusable Player Surface
|
||||
|
||||
The player is now organized so it can be embedded in a larger React app such as GnommoEditor.
|
||||
|
||||
The intended host integration looks like this:
|
||||
|
||||
```tsx
|
||||
import {
|
||||
GlitchPlayer,
|
||||
defaultSlideRegistry,
|
||||
createSessionId,
|
||||
type PresentationDefinition,
|
||||
type VoteRequest
|
||||
} from "./player";
|
||||
import "./player/styles.css";
|
||||
|
||||
function GnommoEditorPreview({
|
||||
presentation,
|
||||
onVoteCommit
|
||||
}: {
|
||||
presentation: PresentationDefinition;
|
||||
onVoteCommit: (vote: VoteRequest) => Promise<void> | void;
|
||||
}) {
|
||||
return (
|
||||
<GlitchPlayer
|
||||
presentation={presentation}
|
||||
slideRegistry={defaultSlideRegistry}
|
||||
initialSessionId={createSessionId()}
|
||||
onVoteCommit={onVoteCommit}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
The key boundary is:
|
||||
|
||||
- the host app fetches the presentation
|
||||
- the host app provides the slide registry
|
||||
- the host app owns vote/event persistence
|
||||
- the player owns playback, timing, layout, gestures, and rendering
|
||||
|
||||
## Why This Exists
|
||||
|
||||
The goal is not just to make ordinary videos or ordinary slide decks.
|
||||
|
||||
The goal is to build a pipeline for AI-assisted lecture creation, where a lecture can eventually become a normal YouTube video, but starts as a richer structured presentation format.
|
||||
|
||||
The underlying idea is:
|
||||
|
||||
- A creator writes a lecture script in a web-based presentation editor, similar to Keynote.
|
||||
- The creator defines a timed slide show and only decides high-level layout behavior, such as whether a slide should be square or fullscreen.
|
||||
- The creator records talking-head video segments and uploads them.
|
||||
- Those segments are stitched into a continuous lecture timeline and transcribed.
|
||||
- The spoken words become the scaffold for when slide events should happen.
|
||||
- The actual slide contents can then be generated or refined by AI using available templates.
|
||||
|
||||
That slide content does not need to be limited to static slides. A slide may be:
|
||||
|
||||
- a React component
|
||||
- a chart or visualization
|
||||
- an image
|
||||
- a 3D scene
|
||||
- an interactive explanatory surface
|
||||
|
||||
This creates a new kind of lecture format:
|
||||
|
||||
- the talking head provides narrative continuity
|
||||
- the slide layer provides rich explanation
|
||||
- the viewer can inspect, manipulate, and compare alternate variants of the same explanation
|
||||
|
||||
## Product Vision
|
||||
|
||||
The long-term vision is a system for authoring lectures as structured, interactive presentations first, and rendering them into linear video later.
|
||||
|
||||
That means:
|
||||
|
||||
- creators can use AI to help generate visual explanations
|
||||
- multiple candidate slide variants can compete for the same moment in the lecture
|
||||
- viewers can browse or interact with those variants
|
||||
- the format can later crystallize into a fixed canonical video suitable for YouTube
|
||||
|
||||
## Why The Player Matters
|
||||
|
||||
This player is the runtime for that format.
|
||||
|
||||
It proves that a lecture can be represented as:
|
||||
|
||||
- stitched talking-head video segments
|
||||
- timed slide events on a continuous timeline
|
||||
- square or fullscreen slide presentation modes
|
||||
- interactive slide content
|
||||
- variant selection and future preference learning
|
||||
|
||||
In short:
|
||||
|
||||
This project exists to explore an AI-assisted interactive lecture format that can begin as a live structured presentation and later become a standard video.
|
||||
@@ -0,0 +1,378 @@
|
||||
# GlitchSlide Content Budget
|
||||
|
||||
This document defines content budgets for GlitchPlayer slide templates.
|
||||
|
||||
The goal is not just to keep slides short. The goal is to force clarity.
|
||||
|
||||
A good GlitchSlide should:
|
||||
|
||||
- express one visual idea
|
||||
- support the spoken script rather than repeat it
|
||||
- fit the template cleanly
|
||||
- be short enough to scan instantly
|
||||
- be specific enough that an AI can generate it reliably
|
||||
|
||||
These limits are for both human authors and AI agents.
|
||||
|
||||
## Core Principle
|
||||
|
||||
Every slide should answer one of these:
|
||||
|
||||
- What is the one thing I want the viewer to notice?
|
||||
- What is the one comparison I want the viewer to hold?
|
||||
- What is the one definition I want the viewer to remember?
|
||||
- What is the one process I want the viewer to understand?
|
||||
|
||||
If a slide tries to do two or three of these at once, it should usually be split.
|
||||
|
||||
## Global Rules
|
||||
|
||||
Across all templates:
|
||||
|
||||
- Prefer one idea per slide.
|
||||
- Do not restate the exact sentence currently being spoken.
|
||||
- Assume the narration carries detail; the slide carries structure.
|
||||
- Keep wording concrete and visual.
|
||||
- Prefer nouns and verbs over abstract qualifiers.
|
||||
- Avoid paragraphs unless the template is specifically meant for prose.
|
||||
- If a slide uses an image, the image should do explanatory work, not decorative work.
|
||||
|
||||
## Character Budgets
|
||||
|
||||
These are hard or near-hard limits.
|
||||
|
||||
- `header`: target 8-28 characters, hard max 36
|
||||
- `title`: target 12-36 characters, hard max 48
|
||||
- `eyebrow`: target 6-18 characters, hard max 24
|
||||
- `body`: target 18-48 characters, hard max 60
|
||||
- `quote`: target 18-55 characters, hard max 72
|
||||
- `definition`: target 20-55 characters, hard max 70
|
||||
- `example`: target 16-45 characters, hard max 60
|
||||
- `step label`: target 8-24 characters, hard max 32
|
||||
- `chart label`: target 4-12 characters, hard max 14
|
||||
- `latexString`: one expression only, hard max 80 characters unless clearly justified
|
||||
|
||||
Important:
|
||||
|
||||
- The hard max exists only for edge cases.
|
||||
- The target range is the real design intention.
|
||||
|
||||
## Template Budgets
|
||||
|
||||
### 1. `SquareYellow`
|
||||
|
||||
Purpose:
|
||||
|
||||
- one strong image
|
||||
- one short framing phrase
|
||||
|
||||
Fields:
|
||||
|
||||
- `header`
|
||||
- `imageSrc`
|
||||
- `imageAlt`
|
||||
|
||||
Budget:
|
||||
|
||||
- `header`: target 10-24 characters, hard max 32
|
||||
- no body text
|
||||
- image must carry the explanatory weight
|
||||
|
||||
Good:
|
||||
|
||||
- `GROWTH LOOKS FINE`
|
||||
- `LIFE FEELS TIGHT`
|
||||
- `OUTPUT IS NOT SECURITY`
|
||||
|
||||
Bad:
|
||||
|
||||
- `This chart suggests that maybe people do not experience economic growth as safety`
|
||||
|
||||
### 2. `FullscreenSplit`
|
||||
|
||||
Purpose:
|
||||
|
||||
- one image
|
||||
- one short text block
|
||||
- often used for transitions and seam masking
|
||||
|
||||
Fields:
|
||||
|
||||
- `eyebrow`
|
||||
- `title`
|
||||
- `body`
|
||||
- `imageSrc`
|
||||
- `imageAlt`
|
||||
- `imagePosition`
|
||||
|
||||
Budget:
|
||||
|
||||
- `eyebrow`: target 6-14 characters
|
||||
- `title`: target 16-34 characters, hard max 44
|
||||
- `body`: target 18-42 characters, hard max 56
|
||||
- title + body together should usually stay under 75 characters
|
||||
|
||||
Good:
|
||||
|
||||
- title: `Fullscreen hides the stitch`
|
||||
- body: `The talk keeps moving underneath.`
|
||||
|
||||
Bad:
|
||||
|
||||
- title: `A fullscreen explanatory slide can be used to conceal the visible segment transition`
|
||||
|
||||
### 3. `EquationFocus`
|
||||
|
||||
Purpose:
|
||||
|
||||
- one mathematical expression
|
||||
- one short framing line
|
||||
|
||||
Fields:
|
||||
|
||||
- `eyebrow`
|
||||
- `title`
|
||||
- `latexString`
|
||||
- `annotation`
|
||||
|
||||
Budget:
|
||||
|
||||
- `title`: target 14-32 characters
|
||||
- `latexString`: one expression only
|
||||
- `annotation`: target 18-42 characters, hard max 56
|
||||
|
||||
Rules:
|
||||
|
||||
- no derivations
|
||||
- no multi-line proofs
|
||||
- no second equation unless the template changes
|
||||
- use this slide only when the equation itself is the visual object
|
||||
|
||||
Good:
|
||||
|
||||
- title: `Definition of the mean`
|
||||
- latex: `\\bar{x} = \\frac{1}{N}\\sum_{i=0}^{N} x_i`
|
||||
- annotation: `Add values. Divide by count.`
|
||||
|
||||
### 4. `QuoteImage`
|
||||
|
||||
Purpose:
|
||||
|
||||
- a short line worth holding in memory
|
||||
- reinforced by a meaningful image
|
||||
|
||||
Fields:
|
||||
|
||||
- `eyebrow`
|
||||
- `quote`
|
||||
- `attribution`
|
||||
- `imageSrc`
|
||||
- `imageAlt`
|
||||
- `imagePosition`
|
||||
|
||||
Budget:
|
||||
|
||||
- `quote`: target 20-50 characters, hard max 70
|
||||
- `attribution`: target 4-18 characters, hard max 24
|
||||
|
||||
Rules:
|
||||
|
||||
- prefer one sentence
|
||||
- avoid long punctuation-heavy quotes
|
||||
- the quote should be memorable enough to stand alone
|
||||
|
||||
### 5. `ChartSingle`
|
||||
|
||||
Purpose:
|
||||
|
||||
- one simple directional comparison
|
||||
|
||||
Fields:
|
||||
|
||||
- `eyebrow`
|
||||
- `title`
|
||||
- `body`
|
||||
- `points[]`
|
||||
|
||||
Budget:
|
||||
|
||||
- `title`: target 14-30 characters
|
||||
- `body`: target 12-36 characters, hard max 48
|
||||
- number of bars: 3-5 preferred, hard max 6
|
||||
- each `label`: target 4-10 characters
|
||||
|
||||
Rules:
|
||||
|
||||
- if the story requires 2+ caveats, this is the wrong template
|
||||
- if the chart needs a legend, it is probably too complex
|
||||
- if the spoken narration explains the trend, the body can be extremely short
|
||||
|
||||
### 6. `ProcessFlow`
|
||||
|
||||
Purpose:
|
||||
|
||||
- show a sequence
|
||||
- show a pipeline
|
||||
|
||||
Fields:
|
||||
|
||||
- `eyebrow`
|
||||
- `title`
|
||||
- `steps[]`
|
||||
|
||||
Budget:
|
||||
|
||||
- `title`: target 14-34 characters
|
||||
- steps: 3-5 preferred, hard max 6
|
||||
- each step: target 8-22 characters, hard max 30
|
||||
|
||||
Rules:
|
||||
|
||||
- each step should be a short action phrase
|
||||
- do not use full sentences
|
||||
- if the process is too dense, break it into two slides
|
||||
|
||||
Good:
|
||||
|
||||
- `Write the script`
|
||||
- `Record the talk`
|
||||
- `Align slide cues`
|
||||
- `Compare variants`
|
||||
|
||||
### 7. `DefinitionCard`
|
||||
|
||||
Purpose:
|
||||
|
||||
- define one important term
|
||||
|
||||
Fields:
|
||||
|
||||
- `eyebrow`
|
||||
- `term`
|
||||
- `definition`
|
||||
- `example`
|
||||
|
||||
Budget:
|
||||
|
||||
- `term`: target 6-22 characters
|
||||
- `definition`: target 20-50 characters, hard max 65
|
||||
- `example`: target 16-40 characters, hard max 52
|
||||
|
||||
Rules:
|
||||
|
||||
- the definition should feel like a distilled sentence
|
||||
- the example should be concrete, not another abstraction
|
||||
|
||||
## AI Authoring Strategy
|
||||
|
||||
An AI agent that knows the full script should not begin by writing slide text.
|
||||
|
||||
It should do this:
|
||||
|
||||
1. Segment the script into beats.
|
||||
2. Decide whether a beat needs a slide at all.
|
||||
3. For each beat, decide the job of the slide:
|
||||
- emphasize
|
||||
- compare
|
||||
- define
|
||||
- quantify
|
||||
- show process
|
||||
- frame transition
|
||||
4. Choose the simplest template that can do that job.
|
||||
5. Generate the shortest possible text that still makes the visual legible.
|
||||
6. Generate or choose an image only if the image advances the concept.
|
||||
7. Reject outputs that exceed the template budget.
|
||||
|
||||
The AI should think:
|
||||
|
||||
- What information is already handled by the narration?
|
||||
- What should the viewer be able to grasp in under one second?
|
||||
- What can be implied visually instead of written?
|
||||
|
||||
## AI Success Criteria
|
||||
|
||||
An AI agent is successful when it produces slides that:
|
||||
|
||||
- are shorter than a human expects
|
||||
- fit the template cleanly
|
||||
- create contrast with the spoken script instead of duplication
|
||||
- feel intentional at a glance
|
||||
- are consistent across many variants
|
||||
|
||||
The AI is not successful when it:
|
||||
|
||||
- fills every available text field
|
||||
- writes explanatory paragraphs
|
||||
- repeats what the speaker is already saying
|
||||
- chooses verbose abstraction over concrete contrast
|
||||
- generates decorative images unrelated to the spoken idea
|
||||
|
||||
## Content Selection Heuristics
|
||||
|
||||
When an AI has the full script, it should prefer:
|
||||
|
||||
- compression over coverage
|
||||
- framing over redundancy
|
||||
- contrast over completeness
|
||||
- structure over explanation
|
||||
|
||||
Examples:
|
||||
|
||||
- If the script explains a formula, the slide should show the formula and one short gloss.
|
||||
- If the script gives a long example, the slide should usually show only the key noun, image, or comparison.
|
||||
- If the speaker is already listing steps verbally, the slide should show only the short step labels.
|
||||
|
||||
## Image Guidance
|
||||
|
||||
For templates with images:
|
||||
|
||||
- images should represent the concept, not merely the topic
|
||||
- avoid generic “people in office” stock unless the point is collaboration itself
|
||||
- prefer clear compositional silhouettes and readable subjects
|
||||
- image prompts should be optimized for one strong central concept
|
||||
|
||||
Bad image prompt:
|
||||
|
||||
- `A professional office environment showing people working together in a modern setting`
|
||||
|
||||
Better image prompt:
|
||||
|
||||
- `Overhead crowd crossing a city square, motion blur, sense of pressure`
|
||||
|
||||
## Validation Rules
|
||||
|
||||
Before accepting a slide, a human or AI validator should check:
|
||||
|
||||
- does each field stay within budget?
|
||||
- does the text visually fit the template?
|
||||
- is the slide saying one thing?
|
||||
- is the slide too close to the spoken line?
|
||||
- does the image help?
|
||||
- would removing 20% of the words improve it?
|
||||
|
||||
If the answer to the last question is yes, revise it.
|
||||
|
||||
## Recommended Future Enforcement
|
||||
|
||||
Later, GnommoEditor should encode these budgets in schema or validation rules.
|
||||
|
||||
That would let:
|
||||
|
||||
- human authors get inline warnings
|
||||
- AI agents get explicit failure signals
|
||||
- slide generation become more predictable
|
||||
|
||||
Recommended future shape:
|
||||
|
||||
- every template exposes field budgets
|
||||
- editors validate them live
|
||||
- AI generators receive the budgets as part of the prompt contract
|
||||
- ranking systems can penalize overcrowded slides
|
||||
|
||||
## Bottom Line
|
||||
|
||||
The content budget is not an inconvenience.
|
||||
|
||||
It is the mechanism that forces both humans and AI to become visually intelligent.
|
||||
|
||||
Shorter slides are harder to write, but much easier to understand.
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TARGET="${1:-all}"
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
node scripts/deploy-vendor.mjs "$TARGET" "${@:2}"
|
||||
+679
@@ -0,0 +1,679 @@
|
||||
# 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.
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
import{j as a}from"./index-D2K-duz6.js";function h(s){if(!Array.isArray(s)||s.length===0)return[{label:"Security",value:72,color:"#111"},{label:"Trust",value:58,color:"rgba(17, 17, 17, 0.72)"},{label:"Growth",value:44,color:"rgba(17, 17, 17, 0.52)"}];const c=[];return s.forEach((e,n)=>{if(!e||typeof e!="object")return;const l=e,t=Number(l.value);c.push({label:String(l.label??`Series ${n+1}`),value:Number.isFinite(t)?Math.max(0,t):0,color:typeof l.color=="string"?l.color:void 0})}),c.length>0?c:[{label:"Security",value:72,color:"#111"},{label:"Trust",value:58,color:"rgba(17, 17, 17, 0.72)"},{label:"Growth",value:44,color:"rgba(17, 17, 17, 0.52)"}]}function u({props:s,paused:c}){const e=s,n=String(e.header??e.eyebrow??"Chart single"),l=String(e.title??"One chart, one comparison"),t=String(e.body??"Use one chart for one contrast."),o=h(e.points),i=Math.max(...o.map(r=>r.value),1);return a.jsx("article",{className:`chart-single ${c?"chart-single--paused":""}`,children:a.jsxs("div",{className:"chart-single__content",children:[a.jsxs("div",{className:"chart-single__copy",children:[a.jsx("p",{className:"chart-single__eyebrow",children:n}),a.jsx("h2",{className:"chart-single__title",children:l}),a.jsx("p",{className:"chart-single__body",children:t})]}),a.jsx("div",{className:"chart-single__plot",role:"img","aria-label":l,children:o.map(r=>a.jsxs("div",{className:"chart-single__row",children:[a.jsx("span",{className:"chart-single__label",children:r.label}),a.jsx("div",{className:"chart-single__bar-track",children:a.jsx("div",{className:"chart-single__bar-fill",style:{width:`${r.value/i*100}%`,background:r.color??"#111"}})}),a.jsx("span",{className:"chart-single__value",children:r.value})]},r.label))})]})})}export{u as default};
|
||||
+1
@@ -0,0 +1 @@
|
||||
import{j as e}from"./index-D2K-duz6.js";function o({props:n,paused:t}){const i=n,a=String(i.header??i.eyebrow??"Definition"),r=String(i.term??"Crystallization"),d=String(i.definition??"An interactive talk becomes a fixed video."),s=String(i.example??"Same source. Cleaner final cut.");return e.jsx("article",{className:`definition-card ${t?"definition-card--paused":""}`,children:e.jsxs("div",{className:"definition-card__content",children:[e.jsx("p",{className:"definition-card__eyebrow",children:a}),e.jsx("h2",{className:"definition-card__term",children:r}),e.jsx("p",{className:"definition-card__definition",children:d}),e.jsx("p",{className:"definition-card__example",children:s})]})})}export{o as default};
|
||||
+1
@@ -0,0 +1 @@
|
||||
import{r as w,j as i}from"./index-D2K-duz6.js";const y="glitch-player-mathjax",E="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js";let x=null;function M(){var d;return typeof window>"u"||typeof document>"u"||(d=window.MathJax)!=null&&d.typesetPromise?Promise.resolve():x||(x=new Promise((s,u)=>{var l;window.MathJax=window.MathJax??{};const n=document.getElementById(y),a=()=>{var e,c;n&&(n.dataset.loaded="true");const f=(c=(e=window.MathJax)==null?void 0:e.startup)==null?void 0:c.promise;if(f){f.then(()=>s()).catch(u);return}s()};if(n){if(n.dataset.loaded==="true"||(l=window.MathJax)!=null&&l.typesetPromise){a();return}n.addEventListener("load",a,{once:!0}),n.addEventListener("error",()=>u(new Error("Failed to load MathJax.")),{once:!0});return}const t=document.createElement("script");t.id=y,t.async=!0,t.src=E,t.addEventListener("load",()=>{t.dataset.loaded="true",a()},{once:!0}),t.addEventListener("error",()=>u(new Error("Failed to load MathJax.")),{once:!0}),document.head.appendChild(t)}),x)}function g({props:d,latexString:s,displayMode:u,paused:n}){const a=d,t=String(a.header??a.eyebrow??"Equation focus"),l=String(a.title??"Definition of the mean"),f=String(a.annotation??"The mean is the total of all observations divided by the number of observations."),e=w.useRef(null),[c,p]=w.useState("loading");return w.useEffect(()=>{const J=e.current,_=s??String.raw`\bar{x} = \frac{1}{N}\sum_{i=0}^{N} x_i`;if(!J)return;let h=!1;return p("loading"),M().then(async()=>{var r,q,m,v;if(h||!e.current)return;const o=e.current;o.textContent=`\\[${_}\\]`,(q=(r=window.MathJax)==null?void 0:r.typesetClear)==null||q.call(r,[o]),await((v=(m=window.MathJax)==null?void 0:m.typesetPromise)==null?void 0:v.call(m,[o])),h||p("ready")}).catch(()=>{!h&&e.current&&(e.current.textContent=_,p("error"))}),()=>{var o,r;h=!0,e.current&&((r=(o=window.MathJax)==null?void 0:o.typesetClear)==null||r.call(o,[e.current]))}},[s]),i.jsx("article",{className:`equation-focus equation-focus--${u} ${n?"equation-focus--paused":""}`,children:i.jsxs("div",{className:"equation-focus__content",children:[i.jsx("p",{className:"equation-focus__eyebrow",children:t}),i.jsx("h2",{className:"equation-focus__title",children:l}),i.jsx("div",{className:"equation-focus__equation-frame",children:i.jsx("div",{ref:e,className:`equation-focus__equation equation-focus__equation--mathjax ${c==="loading"?"equation-focus__equation--loading":""} ${c==="error"?"equation-focus__equation--error":""}`,"aria-live":"polite"})}),i.jsx("p",{className:"equation-focus__annotation",children:f})]})})}export{g as default};
|
||||
+1
@@ -0,0 +1 @@
|
||||
import{j as e}from"./index-D2K-duz6.js";function m({props:t,paused:i}){const s=t,n=String(s.eyebrow??"Fullscreen split"),l=String(s.title??"One image. One idea."),a=String(s.body??"Fullscreen keeps the seam invisible."),c=String(s.imageSrc??"https://images.unsplash.com/photo-1516321318423-f06f85e504b3?auto=format&fit=crop&w=1200&q=80"),r=String(s.imageAlt??l),p=s.imagePosition==="right"?"right":"left";return e.jsxs("article",{className:`fullscreen-split ${i?"fullscreen-split--paused":""} fullscreen-split--image-${p}`,children:[e.jsx("div",{className:"fullscreen-split__panel fullscreen-split__panel--image",children:e.jsx("img",{className:"fullscreen-split__image",src:c,alt:r})}),e.jsx("div",{className:"fullscreen-split__panel fullscreen-split__panel--text",children:e.jsxs("div",{className:"fullscreen-split__text",children:[e.jsx("p",{className:"fullscreen-split__eyebrow",children:n}),e.jsx("h2",{className:"fullscreen-split__title",children:l}),e.jsx("p",{className:"fullscreen-split__body",children:a})]})})]})}export{m as default};
|
||||
+1
@@ -0,0 +1 @@
|
||||
import{r as m,j as s}from"./index-D2K-duz6.js";function y({props:x,paused:u,onMediaLoadingChange:r}){const l=x,v=String(l.header??"").trim(),S=String(l.caption??"").trim(),c=String(l.videoSrc??"").trim(),_=String(l.posterSrc??"").trim(),f=m.useRef(null);return m.useEffect(()=>{const t=f.current;if(!t)return;if(u){t.pause();return}const p=t.play();p&&typeof p.catch=="function"&&p.catch(()=>{})},[u,c]),m.useEffect(()=>{if(!c){r==null||r(!1);return}return r==null||r(!0),()=>{r==null||r(!1);const t=f.current;t&&(t.pause(),t.currentTime=0)}},[r,c]),s.jsxs("article",{className:`fullscreen-video ${u?"fullscreen-video--paused":""}`,children:[s.jsx("video",{ref:f,className:"fullscreen-video__media",src:c,poster:_||void 0,autoPlay:!0,playsInline:!0,preload:"metadata",controls:!1,onLoadedData:()=>r==null?void 0:r(!1),onCanPlay:()=>r==null?void 0:r(!1),onPlaying:()=>r==null?void 0:r(!1),onLoadStart:()=>r==null?void 0:r(!0),onWaiting:()=>r==null?void 0:r(!0),onStalled:()=>r==null?void 0:r(!0),onSuspend:()=>r==null?void 0:r(!1),onEnded:()=>r==null?void 0:r(!1),onError:()=>r==null?void 0:r(!1)}),v?s.jsx("div",{className:"fullscreen-video__chrome",children:s.jsx("p",{className:"fullscreen-video__header",children:v})}):null,S?s.jsx("p",{className:"fullscreen-video__caption",children:S}):null]})}export{y as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{r as v,j as c}from"./index-D2K-duz6.js";function j({props:S,paused:l,onMediaLoadingChange:t}){const u=S,m=String(u.caption??"").trim(),s=String(u.videoSrc??"").trim(),x=String(u.posterSrc??"").trim(),f=v.useRef(null);return v.useEffect(()=>{const r=f.current;if(!r)return;if(l){r.pause();return}const p=r.play();p&&typeof p.catch=="function"&&p.catch(()=>{})},[l,s]),v.useEffect(()=>{if(!s){t==null||t(!1);return}return t==null||t(!0),()=>{t==null||t(!1);const r=f.current;r&&(r.pause(),r.currentTime=0)}},[t,s]),c.jsxs("article",{className:`fullscreen-video-center-caption ${l?"fullscreen-video-center-caption--paused":""}`,children:[c.jsx("video",{ref:f,className:"fullscreen-video-center-caption__media",src:s,poster:x||void 0,autoPlay:!0,playsInline:!0,preload:"metadata",controls:!1,onLoadedData:()=>t==null?void 0:t(!1),onCanPlay:()=>t==null?void 0:t(!1),onPlaying:()=>t==null?void 0:t(!1),onLoadStart:()=>t==null?void 0:t(!0),onWaiting:()=>t==null?void 0:t(!0),onStalled:()=>t==null?void 0:t(!0),onSuspend:()=>t==null?void 0:t(!1),onEnded:()=>t==null?void 0:t(!1),onError:()=>t==null?void 0:t(!1)}),m?c.jsx("div",{className:"fullscreen-video-center-caption__overlay",children:c.jsx("p",{className:"fullscreen-video-center-caption__caption",children:m})}):null]})}export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{r as p,j as l}from"./index-D2K-duz6.js";const y="No need to be discrete";function _({props:m,paused:c,onMediaLoadingChange:t}){const u=m,S=String(u.title??"").trim()||y,s=String(u.videoSrc??"").trim(),x=String(u.posterSrc??"").trim(),f=p.useRef(null);return p.useEffect(()=>{const r=f.current;if(!r)return;if(c){r.pause();return}const v=r.play();v&&typeof v.catch=="function"&&v.catch(()=>{})},[c,s]),p.useEffect(()=>{if(!s){t==null||t(!1);return}return t==null||t(!0),()=>{t==null||t(!1);const r=f.current;r&&(r.pause(),r.currentTime=0)}},[t,s]),l.jsxs("article",{className:`fullscreen-video-title ${c?"fullscreen-video-title--paused":""}`,children:[l.jsx("video",{ref:f,className:"fullscreen-video-title__media",src:s,poster:x||void 0,autoPlay:!0,playsInline:!0,preload:"metadata",controls:!1,onLoadedData:()=>t==null?void 0:t(!1),onCanPlay:()=>t==null?void 0:t(!1),onPlaying:()=>t==null?void 0:t(!1),onLoadStart:()=>t==null?void 0:t(!0),onWaiting:()=>t==null?void 0:t(!0),onStalled:()=>t==null?void 0:t(!0),onSuspend:()=>t==null?void 0:t(!1),onEnded:()=>t==null?void 0:t(!1),onError:()=>t==null?void 0:t(!1)}),l.jsx("div",{className:"fullscreen-video-title__overlay",children:l.jsx("h2",{className:"fullscreen-video-title__title",children:S})})]})}export{_ as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{j as t}from"./index-D2K-duz6.js";function s(e){return e.replace(/\/+$/,"")}function m(){return s("/glitch")}function p(e){return`glitch_${e.replace(/-/g,"_")}`}function h(e,o){const c=o==null?void 0:o.trim();if(c)return c;const n=m(),l=p(e);return`${n}/${l}/index.html`}function f({props:e,displayMode:o,paused:c}){const n=e,l=String(n.header??"Interactive component"),r=String(n.componentId??"glitch-component"),i=h(r,String(n.componentUrl??"").trim()),a=String(n.caption??"Explore the model directly inside the slide.");return t.jsxs("article",{className:`glitch-component-frame glitch-component-frame--${o} ${c?"glitch-component-frame--paused":""}`,children:[t.jsxs("div",{className:"glitch-component-frame__chrome",children:[t.jsx("p",{className:"glitch-component-frame__eyebrow",children:l}),t.jsx("p",{className:"glitch-component-frame__id",children:r})]}),t.jsx("div",{className:"glitch-component-frame__viewport",children:t.jsx("iframe",{className:"glitch-component-frame__iframe",src:i,title:l||r,loading:"lazy",allow:"fullscreen; autoplay; xr-spatial-tracking",sandbox:"allow-scripts allow-same-origin allow-pointer-lock allow-popups allow-forms"})}),t.jsx("p",{className:"glitch-component-frame__caption",children:a})]})}export{f as default};
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
import{j as s}from"./index-D2K-duz6.js";function i(e){return!Array.isArray(e)||e.length===0?["Record talking-head segments","Align transcript to timeline","Generate slide variants","Play, compare, and refine"]:e.map(r=>String(r))}function d({props:e,paused:r}){const o=e,l=String(o.eyebrow??"Process flow"),a=String(o.title??"How it becomes a video"),n=i(o.steps);return s.jsx("article",{className:`process-flow ${r?"process-flow--paused":""}`,children:s.jsxs("div",{className:"process-flow__content",children:[s.jsx("p",{className:"process-flow__eyebrow",children:l}),s.jsx("h2",{className:"process-flow__title",children:a}),s.jsx("div",{className:"process-flow__steps",children:n.map((t,c)=>s.jsxs("div",{className:"process-flow__step",children:[s.jsx("span",{className:"process-flow__index",children:c+1}),s.jsx("p",{className:"process-flow__label",children:t})]},`${c}-${t}`))})]})})}export{d as default};
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
import{j as e}from"./index-D2K-duz6.js";function l({props:i,paused:o}){const t=i,s=String(t.eyebrow??"Quote image"),a=String(t.quote??"Good explanation makes hard ideas feel graspable."),m=String(t.attribution??"Glitch University"),n=String(t.imageSrc??"https://images.unsplash.com/photo-1516321318423-f06f85e504b3?auto=format&fit=crop&w=1200&q=80"),r=String(t.imageAlt??a),g=t.imagePosition==="right"?"right":"left";return e.jsxs("article",{className:`quote-image ${o?"quote-image--paused":""} quote-image--image-${g}`,children:[e.jsx("div",{className:"quote-image__panel quote-image__panel--image",children:e.jsx("img",{className:"quote-image__image",src:n,alt:r})}),e.jsx("div",{className:"quote-image__panel quote-image__panel--quote",children:e.jsxs("div",{className:"quote-image__content",children:[e.jsx("p",{className:"quote-image__eyebrow",children:s}),e.jsxs("blockquote",{className:"quote-image__quote",children:['"',a,'"']}),e.jsx("p",{className:"quote-image__attribution",children:m})]})})]})}export{l as default};
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
import{r as f,j as p}from"./index-D2K-duz6.js";function y({props:m,paused:c,onMediaLoadingChange:r}){const v=m,s=String(v.videoSrc??"").trim(),S=String(v.posterSrc??"").trim(),u=f.useRef(null);return f.useEffect(()=>{const t=u.current;if(!t)return;if(c){t.pause();return}const l=t.play();l&&typeof l.catch=="function"&&l.catch(()=>{})},[c,s]),f.useEffect(()=>{if(!s){r==null||r(!1);return}return r==null||r(!0),()=>{r==null||r(!1);const t=u.current;t&&(t.pause(),t.currentTime=0)}},[r,s]),p.jsx("article",{className:`square-video ${c?"square-video--paused":""}`,children:p.jsx("div",{className:"square-video__frame",children:p.jsx("video",{ref:u,className:"square-video__media",src:s,poster:S||void 0,autoPlay:!0,preload:"metadata",playsInline:!0,controls:!1,onLoadedData:()=>r==null?void 0:r(!1),onCanPlay:()=>r==null?void 0:r(!1),onPlaying:()=>r==null?void 0:r(!1),onLoadStart:()=>r==null?void 0:r(!0),onWaiting:()=>r==null?void 0:r(!0),onStalled:()=>r==null?void 0:r(!0),onSuspend:()=>r==null?void 0:r(!1),onEnded:()=>r==null?void 0:r(!1),onError:()=>r==null?void 0:r(!1)})})})}export{y as default};
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
import{j as a}from"./index-D2K-duz6.js";function m({props:s,paused:r}){const e=s,t=String(e.header??"Square yellow"),i=String(e.imageSrc??"https://images.unsplash.com/photo-1516321318423-f06f85e504b3?auto=format&fit=crop&w=1200&q=80"),l=String(e.imageAlt??t);return a.jsx("article",{className:`square-slide square-slide--yellow ${r?"square-slide--paused":""}`,children:a.jsx("div",{className:"square-slide__image-frame",children:a.jsx("img",{className:"square-slide__image",src:i,alt:l})})})}export{m as default};
|
||||
Vendored
+40
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
BIN
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
import type { GlitchComponentProps } from './types';
|
||||
export default function AssumptionToggle({ config, onComplete, onProgress, theme, className, host }: GlitchComponentProps): import("react/jsx-runtime").JSX.Element;
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import './dev-theme.css';
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { GlitchHostBridge } from './types';
|
||||
export type DevHostBridgeOptions = {
|
||||
isMuted?: () => boolean;
|
||||
onSound?: (id: string, payload?: Record<string, unknown>) => void;
|
||||
onEmit?: (type: string, payload?: unknown) => void;
|
||||
};
|
||||
export declare function createDevHostBridge(options?: DevHostBridgeOptions): GlitchHostBridge;
|
||||
export declare function attachWindowSoundBridge(host: GlitchHostBridge): () => void;
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"componentId": "assumption-toggle",
|
||||
"displayName": "Assumption Toggle",
|
||||
"version": "1.0.0",
|
||||
"folderName": "glitch_assumption_toggle",
|
||||
"packageName": "@nommo/assumption-toggle",
|
||||
"entry": "dist/assumption-toggle.js",
|
||||
"source": "src/index.tsx",
|
||||
"tags": [
|
||||
"glitch-component",
|
||||
"crt",
|
||||
"logic"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { GlitchHostBridge } from './types';
|
||||
export declare const HOST_SOUND_EVENT = "glitch:play-sound";
|
||||
export declare const HOST_STOP_SOUND_EVENT = "glitch:stop-sound";
|
||||
export declare const HOST_EMIT_PREFIX = "glitch:host:";
|
||||
export declare const SOUND_IDS: {
|
||||
readonly click: "ui.button_click";
|
||||
readonly hover: "ui.button_hover";
|
||||
readonly computeStart: "machine.compute_start";
|
||||
readonly computeStep: "machine.compute_step";
|
||||
readonly computeDone: "machine.compute_done";
|
||||
readonly verdictStable: "machine.verdict_stable";
|
||||
readonly verdictAlert: "machine.verdict_alert";
|
||||
};
|
||||
export type SoundId = (typeof SOUND_IDS)[keyof typeof SOUND_IDS];
|
||||
export declare function safePlaySound(host: GlitchHostBridge | undefined, id: string, payload?: Record<string, unknown>): void;
|
||||
export declare function safeStopSound(host: GlitchHostBridge | undefined, id: string, payload?: Record<string, unknown>): void;
|
||||
export declare function safeEmit(host: GlitchHostBridge | undefined, type: string, payload?: unknown): void;
|
||||
@@ -0,0 +1,6 @@
|
||||
import './styles.css';
|
||||
import Component from './Component';
|
||||
import type { GlitchComponentMetadata } from './types';
|
||||
export default Component;
|
||||
export declare const metadata: GlitchComponentMetadata;
|
||||
export type { GlitchComponentProps, GlitchComponentConfig, GlitchComponentResult } from './types';
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+56
@@ -0,0 +1,56 @@
|
||||
export interface GlitchComponentConfig {
|
||||
id: string;
|
||||
name: string;
|
||||
version: string;
|
||||
params: Record<string, unknown>;
|
||||
}
|
||||
export interface GlitchHostBridge {
|
||||
playSound?: (id: string, payload?: Record<string, unknown>) => void;
|
||||
stopSound?: (id: string, payload?: Record<string, unknown>) => void;
|
||||
emit?: (type: string, payload?: unknown) => void;
|
||||
}
|
||||
export interface GlitchComponentProps {
|
||||
config: GlitchComponentConfig;
|
||||
onComplete: (result: GlitchComponentResult) => void;
|
||||
onProgress?: (percent: number) => void;
|
||||
theme?: GlitchTheme;
|
||||
className?: string;
|
||||
host?: GlitchHostBridge;
|
||||
}
|
||||
export interface GlitchComponentResult {
|
||||
success: boolean;
|
||||
score?: number;
|
||||
data?: unknown;
|
||||
error?: string;
|
||||
}
|
||||
export interface GlitchTheme {
|
||||
primary: string;
|
||||
accent: string;
|
||||
bg: string;
|
||||
bgSecondary: string;
|
||||
text: string;
|
||||
textMuted: string;
|
||||
border: string;
|
||||
}
|
||||
export interface GlitchComponentMetadata {
|
||||
name: string;
|
||||
displayName: string;
|
||||
version: string;
|
||||
paramSchema: ParamSchema;
|
||||
defaultParams: Record<string, unknown>;
|
||||
}
|
||||
export interface ParamSchema {
|
||||
[key: string]: {
|
||||
type: 'number' | 'string' | 'boolean' | 'color' | 'select' | 'range';
|
||||
label?: string;
|
||||
description?: string;
|
||||
default: unknown;
|
||||
options?: {
|
||||
value: string | number;
|
||||
label: string;
|
||||
}[];
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
};
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"componentId": "ccd",
|
||||
"displayName": "CCD Photon Explorer",
|
||||
"version": "0.1.0",
|
||||
"folderName": "glitch_ccd",
|
||||
"packageName": "@glitch-components/ccd",
|
||||
"entry": "dist/index.html",
|
||||
"source": "src/index.tsx",
|
||||
"tags": [
|
||||
"internal",
|
||||
"visualization",
|
||||
"tooling"
|
||||
]
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 124 KiB |
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Glitch CCD</title>
|
||||
<script type="module" crossorigin src="/assets/index-D02Mu-Ij.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Da7gUXQx.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
+439
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"componentId": "chess_analogy",
|
||||
"displayName": "Chess Move Set Lab",
|
||||
"version": "1.0.0",
|
||||
"folderName": "glitch_chess_analogy",
|
||||
"packageName": "@glitch-components/chess-analogy",
|
||||
"entry": "dist/chess_analogy.js",
|
||||
"source": "src/index.tsx",
|
||||
"tags": [
|
||||
"glitch-component",
|
||||
"reference",
|
||||
"2d",
|
||||
"chess"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+125
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{j as a,G as r}from"./index-CEeZxcxj.js";function l({className:e}){return a.jsx("div",{className:e,children:a.jsx(r,{})})}const s={name:"gallery",displayName:"Glitch Gallery",version:"0.1.0",paramSchema:{},defaultParams:{}};export{l as default,s as metadata};
|
||||
+4031
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
._wrapper_61dhl_1{--gc-bg: var(--color-bg, #0a0a0f);--gc-bg-secondary: var(--color-bg-secondary, #12121a);--gc-text: var(--color-text, #e8e8ec);--gc-text-muted: var(--color-text-muted, #9999a8);--gc-primary: var(--color-primary, #6366f1);--gc-accent: var(--color-accent, #22d3ee);--gc-border: var(--color-border, #2a2a3a);--gc-font-main: var(--font-main, "Iceland", -apple-system, BlinkMacSystemFont, sans-serif);--gc-font-display: var(--font-display, "Russo One", -apple-system, BlinkMacSystemFont, sans-serif);--gc-font-text: "VT323", var(--font-mono, monospace);width:100%}._frame_61dhl_15{position:relative;width:100%;height:100dvh;min-height:100vh;overflow:hidden;border:1px solid color-mix(in srgb,var(--gc-border) 60%,transparent);color:var(--gc-text);font-family:var(--gc-font-text);background:radial-gradient(circle at 18% 14%,color-mix(in srgb,var(--gc-accent) 18%,transparent),transparent 35%),radial-gradient(circle at 88% 2%,color-mix(in srgb,var(--gc-primary) 22%,transparent),transparent 30%),linear-gradient(165deg,var(--gc-bg),var(--gc-bg-secondary))}._scan_61dhl_30{position:absolute;inset:0;pointer-events:none;opacity:.18;mix-blend-mode:screen;background:repeating-linear-gradient(to bottom,rgba(255,255,255,.1) 0,rgba(255,255,255,.1) 1px,transparent 1px,transparent 3px);animation:_scanDrift_61dhl_1 8s linear infinite}._stack_61dhl_47{position:relative;z-index:1;height:100%;display:flex;flex-direction:column;justify-content:space-between;padding:22px 16px 18px}._meta_61dhl_57{display:flex;justify-content:space-between;align-items:center;font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:var(--gc-text-muted);font-family:var(--gc-font-main)}._progressTrack_61dhl_68{margin-top:8px;width:100%;height:3px;border-radius:999px;overflow:hidden;background:color-mix(in srgb,var(--gc-accent) 20%,transparent)}._progressFill_61dhl_77{height:100%;transition:width .24s ease;background:linear-gradient(90deg,var(--gc-accent),color-mix(in srgb,var(--gc-primary) 45%,var(--gc-accent)))}._slide_61dhl_87{flex:1;display:flex;flex-direction:column;justify-content:center;gap:20px}._title_61dhl_95{margin:0;width:fit-content;font-family:var(--gc-font-display);text-transform:uppercase;letter-spacing:.02em;line-height:1.04;font-size:clamp(28px,8.5vw,46px)}._rgbGlitch_61dhl_105{position:relative;display:inline-block}._rgbBase_61dhl_110{position:relative;z-index:1;color:color-mix(in srgb,var(--gc-text) 38%,#000)}._rgbLayer_61dhl_116{position:absolute;inset:0;z-index:2;pointer-events:none;opacity:.2}._layerR_61dhl_124{color:#ff3737bf;animation:_layerRShift_61dhl_1 2.4s infinite steps(2,end)}._layerG_61dhl_129{color:#37ff5fbf;animation:_layerGShift_61dhl_1 2.1s infinite steps(2,end)}._layerB_61dhl_134{color:#4182ffbf;animation:_layerBShift_61dhl_1 2.7s infinite steps(2,end)}._content_61dhl_139{display:flex;flex-direction:column;gap:10px;font-family:var(--gc-font-text);font-size:clamp(16px,4.2vw,22px);line-height:1.45}._line_61dhl_148{margin:0;color:var(--gc-text)}._spacer_61dhl_153{margin:0;min-height:.6em}._list_61dhl_158{margin:3px 0 0;padding-left:1.1em;display:flex;flex-direction:column;gap:7px}._warning_61dhl_166{list-style:none;padding-left:0}._warning_61dhl_166 li:before{content:"- ";color:var(--gc-accent)}._cta_61dhl_176{margin-top:10px;width:fit-content;border:1px solid var(--gc-accent);background:color-mix(in srgb,var(--gc-accent) 8%,transparent);color:var(--gc-accent);font:inherit;letter-spacing:.06em;text-transform:uppercase;padding:10px 14px;cursor:pointer;font-family:var(--gc-font-main);transition:transform .14s ease,background-color .14s ease}._cta_61dhl_176:active{transform:scale(.98)}._controls_61dhl_195{display:grid;grid-template-columns:1fr 1fr;gap:8px}._navButton_61dhl_201{border:1px solid color-mix(in srgb,var(--gc-border) 70%,transparent);background:color-mix(in srgb,var(--gc-bg-secondary) 75%,transparent);color:var(--gc-text);font:inherit;text-transform:uppercase;letter-spacing:.08em;padding:10px 12px;cursor:pointer;font-family:var(--gc-font-main)}._navButton_61dhl_201:disabled{opacity:.45;cursor:not-allowed}@keyframes _layerRShift_61dhl_1{0%,81%,to{transform:translate(0)}84%{transform:translate(-4px,2px)}88%{transform:translate(4px,-2px)}92%{transform:translate(2px)}96%{transform:translate(-2px,2px)}}@keyframes _layerGShift_61dhl_1{0%,79%,to{transform:translate(0)}83%{transform:translate(2px,-2px)}87%{transform:translate(-2px,4px)}91%{transform:translateY(-2px)}95%{transform:translate(2px)}}@keyframes _layerBShift_61dhl_1{0%,82%,to{transform:translate(0)}86%{transform:translate(4px)}90%{transform:translate(-4px,-2px)}94%{transform:translate(2px,2px)}98%{transform:translate(-2px)}}@keyframes _scanDrift_61dhl_1{0%{transform:translateY(-10px)}to{transform:translateY(10px)}}@media(min-width:600px){._frame_61dhl_15{max-width:420px;margin:0 auto}}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{r as c,j as s}from"./index-CEeZxcxj.js";const w="_wrapper_61dhl_1",k="_frame_61dhl_15",v="_scan_61dhl_30",S="_stack_61dhl_47",B="_meta_61dhl_57",$="_progressTrack_61dhl_68",C="_progressFill_61dhl_77",E="_slide_61dhl_87",G="_title_61dhl_95",L="_rgbGlitch_61dhl_105",F="_rgbBase_61dhl_110",R="_rgbLayer_61dhl_116",Y="_layerR_61dhl_124",I="_layerG_61dhl_129",P="_layerB_61dhl_134",W="_content_61dhl_139",M="_line_61dhl_148",A="_spacer_61dhl_153",D="_list_61dhl_158",H="_warning_61dhl_166",O="_cta_61dhl_176",q="_controls_61dhl_195",U="_navButton_61dhl_201",e={wrapper:w,frame:k,scan:v,stack:S,meta:B,progressTrack:$,progressFill:C,slide:E,title:G,rgbGlitch:L,rgbBase:F,rgbLayer:R,layerR:Y,layerG:I,layerB:P,content:W,line:M,spacer:A,list:D,warning:H,cta:O,controls:q,navButton:U},z=40,i=[{title:"The Premise",lines:["This facility tests foundational assumptions.","","Not interpretations.","Assumptions."]},{title:"The method",lines:["This is math and philosophy.","Make assumption.","Pretend it's true.","Where does that lead?"]},{title:"The law",lines:["We make predictions.","If they fail, the idea fails."]},{title:"The way",lines:["You will:"],bullets:["Pass challenges","Collect paradoxes","Examine anomalies","Learn to think sharply"]},{title:"Warning",lines:["You may feel:"],bullets:["This is not a game!","Temporary confusion","The urge to defend existing physics!"],bulletStyle:"warning",after:"Good. This is not for everyone."},{title:"How This Works",lines:["Watch a short module","Pass the glitch gate","Unlock the next tech","That's it. For now."]},{title:"Proceed",lines:["You may proceed as an investigator.","Begin at the intersection","Get briefed"],cta:"[ ENTER FACILITY ]"}];function K({config:h,onComplete:u,onProgress:_,theme:n,className:p}){const[l,g]=c.useState(0),o=c.useRef(null),y=c.useMemo(()=>n?{"--gc-primary":n.primary,"--gc-accent":n.accent,"--gc-bg":n.bg,"--gc-bg-secondary":n.bgSecondary,"--gc-text":n.text,"--gc-text-muted":n.textMuted,"--gc-border":n.border}:{},[n]);c.useEffect(()=>{const t=(l+1)/i.length*100;_?.(Math.round(t))},[l,_]);const d=c.useCallback(t=>{if(t>0&&l===i.length-1){g(0);return}const r=l+t;r<0||r>=i.length||g(r)},[l]),b=c.useCallback(t=>{o.current=t.changedTouches[0]?.clientY??null},[]),x=c.useCallback(t=>{if(o.current==null)return;const r=t.changedTouches[0]?.clientY??o.current,m=o.current-r;Math.abs(m)>z&&d(m>0?1:-1),o.current=null},[d]),f=c.useCallback(()=>{u({success:!0,score:100,data:{sequence:"facility-slideshow",configId:h.id,completedAt:new Date().toISOString()}})},[h.id,u]),a=i[l],j=l===i.length-1,N=(l+1)/i.length*100,T=p?`${e.wrapper} ${p}`:e.wrapper;return s.jsx("div",{className:T,style:y,children:s.jsxs("section",{className:e.frame,"aria-label":"Facility onboarding slideshow",onTouchStart:b,onTouchEnd:x,children:[s.jsx("div",{className:e.scan,"aria-hidden":"true"}),s.jsxs("div",{className:e.stack,children:[s.jsxs("header",{children:[s.jsxs("div",{className:e.meta,children:[s.jsx("span",{children:"Glitch Facility"}),s.jsxs("span",{children:[l+1," / ",i.length]})]}),s.jsx("div",{className:e.progressTrack,children:s.jsx("div",{className:e.progressFill,style:{width:`${N}%`}})})]}),s.jsxs("article",{className:e.slide,"aria-live":"polite",children:[s.jsx("h2",{className:e.title,children:s.jsxs("span",{className:e.rgbGlitch,children:[s.jsx("span",{className:e.rgbBase,children:a.title}),s.jsx("span",{className:`${e.rgbLayer} ${e.layerR}`,"aria-hidden":"true",children:a.title}),s.jsx("span",{className:`${e.rgbLayer} ${e.layerG}`,"aria-hidden":"true",children:a.title}),s.jsx("span",{className:`${e.rgbLayer} ${e.layerB}`,"aria-hidden":"true",children:a.title})]})}),s.jsxs("div",{className:e.content,children:[a.lines.map((t,r)=>t===""?s.jsx("p",{className:e.spacer,"aria-hidden":"true"},`line-${r}`):s.jsx("p",{className:e.line,children:t},`line-${r}`)),a.bullets&&s.jsx("ul",{className:`${e.list} ${a.bulletStyle==="warning"?e.warning:""}`,children:a.bullets.map(t=>s.jsx("li",{children:t},t))}),a.after&&s.jsx("p",{className:e.line,children:a.after}),a.cta&&s.jsx("button",{type:"button",className:e.cta,onClick:f,children:a.cta})]})]}),s.jsxs("nav",{className:e.controls,"aria-label":"Slideshow controls",children:[s.jsx("button",{type:"button",className:e.navButton,onClick:()=>d(-1),disabled:l===0,children:"Previous"}),s.jsx("button",{type:"button",className:e.navButton,onClick:()=>d(1),children:j?"Restart":"Next"})]})]})]})})}const Q={name:"onboarding2",displayName:"Facility Slideshow",version:"1.0.0",paramSchema:{},defaultParams:{}};export{K as default,Q as metadata};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
._wrapper_1xs2z_1{--gc-paper: #f2f0ea;box-sizing:border-box;width:100%;height:100%;min-height:100%;margin:0;padding:0;border:1px solid color-mix(in srgb,var(--gc-border, #34516f) 72%,transparent);border-radius:24px;background:radial-gradient(circle at 12% 8%,color-mix(in srgb,var(--gc-primary, #45c4b0) 16%,transparent),transparent 34%),radial-gradient(circle at 88% 10%,color-mix(in srgb,var(--gc-accent, #ff7a59) 14%,transparent),transparent 28%),linear-gradient(160deg,var(--gc-bg, #0d1117),var(--gc-bg-secondary, #182237));color:var(--gc-text, #f1f4ef);box-shadow:0 24px 64px #00000047;display:grid;place-items:center}._wrapper_1xs2z_1,._wrapper_1xs2z_1 *,._wrapper_1xs2z_1 *:before,._wrapper_1xs2z_1 *:after{box-sizing:inherit}._canvasShell_1xs2z_28{position:relative;overflow:hidden;flex:none;border:1px solid rgb(0 0 0 / 22%);background:var(--gc-paper, #f2f0ea);box-shadow:inset 0 0 0 1px #ffffff1f}._canvas_1xs2z_28{width:100%;height:100%}._canvas_1xs2z_28 canvas{display:block;width:100%;height:100%}._viewportLabels_1xs2z_48{position:absolute;inset:0;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));pointer-events:none}._viewportLabels_1xs2z_48[data-layout=portrait]{grid-template-columns:1fr;grid-template-rows:repeat(2,minmax(0,1fr))}._splitLabel_1xs2z_61{display:grid;place-items:center;color:#111827d1;font-size:clamp(1.15rem,2.7vw,2.1rem);font-weight:700;font-family:IBM Plex Sans Condensed,Avenir Next Condensed,sans-serif;text-transform:uppercase;letter-spacing:.34em;text-shadow:0 2px 12px rgb(255 255 255 / 42%)}._controlOverlay_1xs2z_73{position:absolute;right:1rem;bottom:1rem;left:1rem;display:flex;justify-content:center;pointer-events:none}._resolutionCard_1xs2z_83{pointer-events:auto;display:flex;align-items:center;justify-content:center;width:min(100%,26rem);padding:.75rem .9rem;border:1px solid color-mix(in srgb,var(--gc-border, #34516f) 72%,transparent);border-radius:16px;background:#070f199e;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 10px 30px #0000002e}._resolutionControl_1xs2z_97{display:flex;align-items:center;gap:.85rem;width:100%;min-width:0}._slider_1xs2z_105{width:100%;accent-color:var(--gc-primary, #45c4b0);cursor:pointer}._sliderValue_1xs2z_111{min-width:3.75rem;text-align:right;font-family:IBM Plex Mono,JetBrains Mono,monospace;font-size:.95rem}@media(max-width:900px){._resolutionControl_1xs2z_97{min-width:0}}@media(max-width:720px){._wrapper_1xs2z_1{padding:0}._splitLabel_1xs2z_61{font-size:clamp(.95rem,5vw,1.4rem);letter-spacing:.24em}._controlOverlay_1xs2z_73{right:0rem;bottom:0rem;left:0rem}}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 826 KiB |
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"componentId": "gallery",
|
||||
"displayName": "Glitch Gallery",
|
||||
"version": "0.1.0",
|
||||
"folderName": "glitch_gallery",
|
||||
"packageName": "@glitch-components/gallery",
|
||||
"entry": "dist/index.html",
|
||||
"source": "src/index.tsx",
|
||||
"tags": [
|
||||
"internal",
|
||||
"tooling",
|
||||
"preview"
|
||||
]
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Glitch Gallery</title>
|
||||
<script type="module" crossorigin src="/assets/index-CEeZxcxj.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DczjPplt.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"componentId": "level-questions",
|
||||
"displayName": "Level Questions",
|
||||
"version": "1.0.0",
|
||||
"folderName": "glitch_level_questions",
|
||||
"packageName": "level-questions",
|
||||
"entry": "dist/level-questions.js",
|
||||
"source": "src/index.tsx",
|
||||
"tags": [
|
||||
"glitch-component",
|
||||
"quiz",
|
||||
"role-mapping"
|
||||
]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+14
@@ -0,0 +1,14 @@
|
||||
import { type GlitchComponentProps } from './types';
|
||||
type ComponentDevUiOverrides = {
|
||||
showNumericDiagnostics?: boolean;
|
||||
compactNumericDiagnostics?: boolean;
|
||||
onOpenHarness?: () => void;
|
||||
onSetParam?: (name: 'complexity' | 'speedup', value: number) => void;
|
||||
maxComplexity?: number;
|
||||
maxSpeedup?: number;
|
||||
};
|
||||
type ComponentProps = GlitchComponentProps & {
|
||||
devUi?: ComponentDevUiOverrides;
|
||||
};
|
||||
export default function Component(props: ComponentProps): import("react/jsx-runtime").JSX.Element;
|
||||
export {};
|
||||
@@ -0,0 +1,10 @@
|
||||
interface DiophantineVectorsProps {
|
||||
complexity: number;
|
||||
lightlanes?: boolean;
|
||||
radius?: number;
|
||||
distance?: number;
|
||||
allSectors?: boolean;
|
||||
cumulative?: boolean;
|
||||
}
|
||||
export declare function DiophantineVectors({ complexity, lightlanes, radius, distance, allSectors, cumulative }: DiophantineVectorsProps): import("react/jsx-runtime").JSX.Element;
|
||||
export {};
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { GlitchHostBridge } from '../types';
|
||||
interface FanoSweepPanelProps {
|
||||
radius: number;
|
||||
distance: number;
|
||||
chandraDistance: number;
|
||||
allSectors: boolean;
|
||||
cumulative: boolean;
|
||||
theta: number;
|
||||
phi: number;
|
||||
orbitSpeed: number;
|
||||
thetaDrift: number;
|
||||
thetaJitter: number;
|
||||
phiJitter: number;
|
||||
K: number;
|
||||
speedup: number;
|
||||
usePoisson: boolean;
|
||||
bottomInset?: number;
|
||||
onBack?: () => void;
|
||||
host?: GlitchHostBridge;
|
||||
}
|
||||
export declare function FanoSweepPanel(props: FanoSweepPanelProps): import("react/jsx-runtime").JSX.Element;
|
||||
export {};
|
||||
@@ -0,0 +1,12 @@
|
||||
import { type MutableRefObject } from 'react';
|
||||
interface OrbitingObjectProps {
|
||||
radius: number;
|
||||
theta: number;
|
||||
phi: number;
|
||||
orbitTime: MutableRefObject<number>;
|
||||
simTime?: MutableRefObject<number>;
|
||||
thetaDrift?: number;
|
||||
paused?: boolean;
|
||||
}
|
||||
export declare function OrbitingObject({ radius, theta, phi, orbitTime, simTime, thetaDrift, paused }: OrbitingObjectProps): import("react/jsx-runtime").JSX.Element;
|
||||
export {};
|
||||
@@ -0,0 +1,3 @@
|
||||
export declare function PoissonShell({ radius }: {
|
||||
radius: number;
|
||||
}): import("react/jsx-runtime").JSX.Element;
|
||||
@@ -0,0 +1,6 @@
|
||||
interface PulsingSphereProps {
|
||||
radius: number;
|
||||
paused?: boolean;
|
||||
}
|
||||
export declare function PulsingSphere({ radius, paused }: PulsingSphereProps): import("react/jsx-runtime").JSX.Element;
|
||||
export {};
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { GlitchHostBridge, SceneViewMode } from '../types';
|
||||
interface SamplingSystemProps {
|
||||
complexity: number;
|
||||
radius: number;
|
||||
distance: number;
|
||||
theta: number;
|
||||
phi: number;
|
||||
observationMode: 'orbit' | 'randomSphere';
|
||||
allSectors: boolean;
|
||||
cumulative: boolean;
|
||||
K: number;
|
||||
orbitRadius: number;
|
||||
orbitVelocity: number;
|
||||
thetaDrift: number;
|
||||
thetaJitter: number;
|
||||
phiJitter: number;
|
||||
usePoisson: boolean;
|
||||
speedup: number;
|
||||
paused: boolean;
|
||||
sceneView: SceneViewMode;
|
||||
bottomInset?: number;
|
||||
showNumericDiagnostics?: boolean;
|
||||
compactNumericDiagnostics?: boolean;
|
||||
host?: GlitchHostBridge;
|
||||
centerDistribution?: boolean;
|
||||
hideFlyingBars?: boolean;
|
||||
}
|
||||
export declare function SamplingSystem({ complexity, radius, distance, theta, phi, observationMode, allSectors, cumulative, K, orbitRadius, orbitVelocity, thetaDrift, thetaJitter, phiJitter, usePoisson, speedup, paused, sceneView, bottomInset, showNumericDiagnostics, compactNumericDiagnostics, host, centerDistribution, hideFlyingBars }: SamplingSystemProps): import("react/jsx-runtime").JSX.Element;
|
||||
export {};
|
||||
@@ -0,0 +1,6 @@
|
||||
interface TargetDiscProps {
|
||||
radius: number;
|
||||
distance: number;
|
||||
}
|
||||
export declare function TargetDisc({ radius, distance }: TargetDiscProps): import("react/jsx-runtime").JSX.Element;
|
||||
export {};
|
||||
@@ -0,0 +1,7 @@
|
||||
interface VectorArrowProps {
|
||||
vector: [number, number, number];
|
||||
origin?: [number, number, number];
|
||||
color?: string;
|
||||
}
|
||||
export declare function VectorArrow({ vector, origin, color }: VectorArrowProps): import("react/jsx-runtime").JSX.Element;
|
||||
export {};
|
||||
+1
@@ -0,0 +1 @@
|
||||
import './dev-theme.css';
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { GlitchHostBridge } from './types';
|
||||
export type DevHostBridgeOptions = {
|
||||
isMuted?: () => boolean;
|
||||
onSound?: (id: string, payload?: Record<string, unknown>) => void;
|
||||
onEmit?: (type: string, payload?: unknown) => void;
|
||||
};
|
||||
export declare function createDevHostBridge(options?: DevHostBridgeOptions): GlitchHostBridge;
|
||||
export declare function attachWindowSoundBridge(host: GlitchHostBridge): () => void;
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"componentId": "lightlane",
|
||||
"displayName": "Lightlane Challenge",
|
||||
"version": "0.1.0",
|
||||
"folderName": "glitch_lightlane",
|
||||
"packageName": "@nommo/lightlane",
|
||||
"entry": "dist/lightlane.js",
|
||||
"source": "src/index.tsx",
|
||||
"tags": [
|
||||
"glitch-component",
|
||||
"reference",
|
||||
"3d"
|
||||
]
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import type { GlitchHostBridge } from './types';
|
||||
export declare const HOST_SOUND_EVENT = "glitch:play-sound";
|
||||
export declare const HOST_STOP_SOUND_EVENT = "glitch:stop-sound";
|
||||
export declare const HOST_EMIT_PREFIX = "glitch:host:";
|
||||
export declare const SOUND_IDS: {
|
||||
readonly click: "ui.button_click";
|
||||
readonly hover: "ui.button_hover";
|
||||
readonly computeStart: "machine.compute_start";
|
||||
readonly computeStep: "machine.compute_step";
|
||||
readonly computeDone: "machine.compute_done";
|
||||
readonly verdictStable: "machine.verdict_stable";
|
||||
readonly verdictAlert: "machine.verdict_alert";
|
||||
};
|
||||
export type SoundId = (typeof SOUND_IDS)[keyof typeof SOUND_IDS];
|
||||
export declare function safePlaySound(host: GlitchHostBridge | undefined, id: string, payload?: Record<string, unknown>): void;
|
||||
export declare function safeStopSound(host: GlitchHostBridge | undefined, id: string, payload?: Record<string, unknown>): void;
|
||||
export declare function safeEmit(host: GlitchHostBridge | undefined, type: string, payload?: unknown): void;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import Component from './Component';
|
||||
import { type GlitchComponentMetadata } from './types';
|
||||
export default Component;
|
||||
export declare const metadata: GlitchComponentMetadata;
|
||||
export type { GlitchComponentProps, GlitchComponentConfig, GlitchComponentResult, GlitchHostBridge } from './types';
|
||||
+2805
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+94
@@ -0,0 +1,94 @@
|
||||
export interface Config {
|
||||
complexity: number;
|
||||
radius: number;
|
||||
distance: number;
|
||||
lightlanes: boolean;
|
||||
allSectors: boolean;
|
||||
cumulative: boolean;
|
||||
phi: number;
|
||||
theta: number;
|
||||
orbitSpeed?: number;
|
||||
usePoisson?: boolean;
|
||||
}
|
||||
export interface Preset {
|
||||
name: string;
|
||||
description: string;
|
||||
duration: number;
|
||||
start: Config;
|
||||
end: Config;
|
||||
}
|
||||
export declare const presets: Preset[];
|
||||
export declare const namedPresets: {
|
||||
"Iso: High Complexity": {
|
||||
complexity: number;
|
||||
distance: number;
|
||||
orbitSpeed: number;
|
||||
lightlanes: boolean;
|
||||
allSectors: boolean;
|
||||
radius: number;
|
||||
cumulative: boolean;
|
||||
phi: number;
|
||||
theta: number;
|
||||
usePoisson?: boolean;
|
||||
};
|
||||
"Iso: Fast Orbit": {
|
||||
complexity: number;
|
||||
distance: number;
|
||||
orbitSpeed: number;
|
||||
lightlanes: boolean;
|
||||
allSectors: boolean;
|
||||
radius: number;
|
||||
cumulative: boolean;
|
||||
phi: number;
|
||||
theta: number;
|
||||
usePoisson?: boolean;
|
||||
};
|
||||
"Iso: Optimal": {
|
||||
complexity: number;
|
||||
distance: number;
|
||||
orbitSpeed: number;
|
||||
lightlanes: boolean;
|
||||
allSectors: boolean;
|
||||
radius: number;
|
||||
cumulative: boolean;
|
||||
phi: number;
|
||||
theta: number;
|
||||
usePoisson?: boolean;
|
||||
};
|
||||
"Aniso: Low Complexity": {
|
||||
complexity: number;
|
||||
distance: number;
|
||||
orbitSpeed: number;
|
||||
lightlanes: boolean;
|
||||
allSectors: boolean;
|
||||
radius: number;
|
||||
cumulative: boolean;
|
||||
phi: number;
|
||||
theta: number;
|
||||
usePoisson?: boolean;
|
||||
};
|
||||
"Aniso: Slow Speed": {
|
||||
complexity: number;
|
||||
distance: number;
|
||||
orbitSpeed: number;
|
||||
lightlanes: boolean;
|
||||
allSectors: boolean;
|
||||
radius: number;
|
||||
cumulative: boolean;
|
||||
phi: number;
|
||||
theta: number;
|
||||
usePoisson?: boolean;
|
||||
};
|
||||
"Aniso: High Distance": {
|
||||
complexity: number;
|
||||
distance: number;
|
||||
orbitSpeed: number;
|
||||
lightlanes: boolean;
|
||||
allSectors: boolean;
|
||||
radius: number;
|
||||
cumulative: boolean;
|
||||
phi: number;
|
||||
theta: number;
|
||||
usePoisson?: boolean;
|
||||
};
|
||||
};
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
export type SceneViewMode = 'both' | 'poisson' | 'lightlane';
|
||||
export interface LightlaneProps {
|
||||
complexity?: number;
|
||||
radius?: number;
|
||||
distance?: number;
|
||||
chandraDistance?: number;
|
||||
showAxes?: boolean;
|
||||
lightlanes?: boolean;
|
||||
allSectors?: boolean;
|
||||
cumulative?: boolean;
|
||||
phi?: number;
|
||||
theta?: number;
|
||||
observationMode?: 'orbit' | 'randomSphere';
|
||||
orbitSpeed?: number;
|
||||
thetaDrift?: number;
|
||||
thetaJitter?: number;
|
||||
phiJitter?: number;
|
||||
speedup?: number;
|
||||
K?: number;
|
||||
usePoisson?: boolean;
|
||||
sceneView?: SceneViewMode;
|
||||
hideScenePresentation?: boolean;
|
||||
hideFlyingBars?: boolean;
|
||||
hideDataBoxes?: boolean;
|
||||
}
|
||||
export declare const defaultProps: Required<LightlaneProps>;
|
||||
export interface GlitchComponentConfig {
|
||||
id: string;
|
||||
name: string;
|
||||
version: string;
|
||||
params: Record<string, unknown>;
|
||||
}
|
||||
export interface GlitchHostBridge {
|
||||
playSound?: (id: string, payload?: Record<string, unknown>) => void;
|
||||
stopSound?: (id: string, payload?: Record<string, unknown>) => void;
|
||||
emit?: (type: string, payload?: unknown) => void;
|
||||
}
|
||||
export interface GlitchTheme {
|
||||
primary: string;
|
||||
accent: string;
|
||||
bg: string;
|
||||
bgSecondary: string;
|
||||
text: string;
|
||||
textMuted: string;
|
||||
border: string;
|
||||
}
|
||||
export interface GlitchComponentResult {
|
||||
success: boolean;
|
||||
score?: number;
|
||||
data?: unknown;
|
||||
error?: string;
|
||||
}
|
||||
export interface GlitchComponentProps {
|
||||
config: GlitchComponentConfig;
|
||||
onComplete: (result: GlitchComponentResult) => void;
|
||||
onProgress?: (percent: number) => void;
|
||||
theme?: GlitchTheme;
|
||||
className?: string;
|
||||
host?: GlitchHostBridge;
|
||||
}
|
||||
export interface ParamSchema {
|
||||
[key: string]: {
|
||||
type: 'number' | 'string' | 'boolean' | 'color' | 'select' | 'range';
|
||||
label?: string;
|
||||
description?: string;
|
||||
default: unknown;
|
||||
options?: {
|
||||
value: string | number;
|
||||
label: string;
|
||||
}[];
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
};
|
||||
}
|
||||
export interface GlitchComponentMetadata {
|
||||
name: string;
|
||||
displayName: string;
|
||||
version: string;
|
||||
paramSchema: ParamSchema;
|
||||
defaultParams: Record<string, unknown>;
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export type Vector3Tuple = [number, number, number];
|
||||
export declare function getVectors(complexity: number, allSectors?: boolean): Vector3Tuple[];
|
||||
+1
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"componentId": "migration-paradox",
|
||||
"displayName": "The Migration Paradox",
|
||||
"version": "1.0.0",
|
||||
"folderName": "glitch_migration_paradox",
|
||||
"packageName": "migration-paradox",
|
||||
"entry": "dist/migration-paradox.js",
|
||||
"source": "src/index.tsx",
|
||||
"tags": [
|
||||
"glitch-component",
|
||||
"paradox",
|
||||
"narrative"
|
||||
]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"componentId": "onboarding",
|
||||
"displayName": "Glitch University Onboarding",
|
||||
"version": "1.0.0",
|
||||
"folderName": "glitch_onboarding",
|
||||
"packageName": "@glitch-components/onboarding",
|
||||
"entry": "dist/onboarding.js",
|
||||
"source": "src/index.tsx",
|
||||
"tags": [
|
||||
"glitch-component",
|
||||
"onboarding",
|
||||
"narrative"
|
||||
]
|
||||
}
|
||||
+847
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"componentId": "onboarding2",
|
||||
"displayName": "Facility Slideshow",
|
||||
"version": "1.0.0",
|
||||
"folderName": "glitch_onboarding2",
|
||||
"packageName": "@glitch-components/onboarding2",
|
||||
"entry": "dist/onboarding2.js",
|
||||
"source": "src/index.tsx",
|
||||
"tags": [
|
||||
"glitch-component",
|
||||
"onboarding",
|
||||
"slideshow"
|
||||
]
|
||||
}
|
||||
+212
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user