Appearance
Demo data
approvedThe versioned demo library: video documents, configuration shapes, and the dummy feed, with media served from a remote bucket.
Purpose
Two layers with distinct jobs: videos own the facts, configs own the shape, and the demo pages marry them. A developer copies a fully expanded configuration from the showcase and sees the player. The player itself never knows this folder exists; it only ever receives complete configurations.
Folder layout
text
data/
README.md how this folder works, how to add a video or a config
videos/
index.json ordered array of video ids
<id>/video.json one video document per id
configs/
index.json ordered array of config ids
<id>.json one configuration shape per id
feeds/
shorts.json the dummy feed: ordered portrait video idsVideo documents
The content library. Each document carries facts only: the config contract's content section plus the share URLs. It never contains layout, controls, capabilities, or strings decisions.
json
{
"id": "quantify-genocide-impact",
"content": {
"title": "How can we quantify the immediate and lingering impact of genocide?",
"author": "Diego Alburez-Gutierrez",
"description": "A short synopsis used by share targets.",
"poster": "https://demo.lt.org/videos/portrait/quantify-genocide-impact/poster.jpg",
"duration": 609.4,
"sources": [
{
"src": "https://demo.lt.org/videos/portrait/quantify-genocide-impact/video.mp4",
"type": "video/mp4"
}
],
"tracks": [
{
"kind": "subtitles",
"label": "English",
"srclang": "en",
"src": "https://demo.lt.org/videos/portrait/quantify-genocide-impact/en.vtt",
"default": true
}
],
"chapters": [{ "name": "Question", "start": 9 }],
"metadata": {
"inANutshell": { "generatedByAi": true, "keyTakeaway": "…", "summary": "…" }
}
},
"share": {
"canonicalUrl": "https://lt.org/publication/example",
"embedUrl": "https://lt.org/embed/example"
}
}Library composition:
- Ten portrait videos, each carrying the full metadata set: every context panel view populated (in a nutshell, key concepts, research field, transcript, underlying publication, related videos), so each short demonstrates the panel completely, including its refresh across feed swipes.
- Three landscape videos: one with chapters and two subtitle languages, one with the full metadata set, one minimal.
The library
Every video has a dedicated page with the playing video, its document, and copyable bucket links. Portrait titles and metadata are invented demo fiction over real media; chapters are the real ones. Captions and transcripts for portrait videos arrive together with the AI transcription pass.
Portrait
- How Can Kilometre-Scale Models Change What We Know About the Ocean? · Thomas Rackow
- How Predictable Are West African Monsoon Rains Years in Advance? · Elsa Mohino
- What Role Does Desert Dust Play in Weather and Climate? · Martina Klose
- How Do Warmer Oceans Reshape Tropical Rainfall Extremes? · Yuting Wu
- Why Are Clouds the Hardest Part of the Climate Puzzle? · Frank Kuhn
- What Drives the Seasonal March of the Monsoons? · Simona Bordoni
- How Does the Amazon Make Its Own Rain? · Hans Segura
- Why Do Thunderstorms Cluster Over Tropical Oceans? · Divya Sri Praturi
- Can We Blame Climate Change for a Single Heatwave? · Isabel Gelfert
- What Does the Earth's Energy Imbalance Tell Us About Our Future? · Hugo Müller
Landscape
- What Happens When Success Becomes a Measure of Worth? · Yannic Vitz
- What Is the Relationship Between Market Share and Financial Firm Performance? · A. Edeling and A. Himme
- Do Infants Understand Others as Mental Agents and Communicate Meaningfully Before They Acquire Language? · Ulf Liszkowski
Configuration shapes
Each file in configs/ is final-form in everything except content, and sources its content one of two ways:
"video": "<id>": the configuration borrows that video document'scontentand its share URLs.- Inline
content: the configuration stands entirely on its own, used verbatim.
json
{
"id": "portrait-standalone",
"video": "quantify-genocide-impact",
"config": {
"version": 3,
"layout": "portrait",
"controls": { "fullscreen": false },
"capabilities": { "chapters": true }
}
}Expansion rule: the demo tooling produces the final configuration by taking config, injecting the referenced video's content, and injecting its share URLs into capabilities.share (unless the configuration sets its own). The id and video keys are demo bookkeeping and are never part of the expanded configuration.
The configs page
The showcase renders each configuration shape as a live player next to its fully expanded JSON with a copy control. A video picker over the library re-expands the configuration with another video's data on the spot. What is displayed, and what copy copies, is always the complete, final-form configuration: paste it into any page, get this player.
The embed page
The showcase route /embed/<video-id>/ resolves <video-id> against the same data/videos/<video-id>/video.json library and mounts one landscape player that fills the viewport. The static deployment rewrites those document-shaped URLs to the shared embed entry without changing the browser URL. Showcase share configuration points its embed URL at this route so the generated iframe snippet completes the round trip on both local and deployed showcase origins.
The dummy feed
feeds/shorts.json holds { "items": [ …the ten portrait video ids… ] } in feed order. The feed page resolves the ids to documents, composes complete portrait configurations, and hands the feed module exactly its contract input (see the shorts document).
Media bucket
- All binary assets (video files, posters, subtitle tracks) live in a Cloudflare R2 bucket, grouped by orientation:
videos/<orientation>/<id>/poster.jpg,video.mp4,<lang>.vtt, with<orientation>beinglandscapeorportrait. The repository library stays flat (data/videos/<id>/); ids are unique across orientations, and documents carry full URLs, so the grouping is a bucket-side convention only. - The bucket is managed as infrastructure, outside this repository:
lt-demo-media, served at the public base URLhttps://demo.lt.org(see lt-infradocs/cloudflare-r2.mdfor the module definition and upload runbook). - CORS is a hard requirement: subtitle tracks are fetched with CORS, so the bucket must send
Access-Control-Allow-Originfor the showcase origin and local development, and the player setscrossoriginon its media element. Plain video playback would work without it; captions would silently fail.
Fetching model
- Documents, configuration shapes, and the feed file are fetched as static JSON from the showcase itself; the folder ships with the demo build. Media streams from the bucket.
- Automated tests import the same files and treat media URLs as opaque values; tests never fetch media, so test determinism does not depend on the bucket.
- Documents and bucket can drift (a document referencing an object that was never uploaded). The demo build guards against this: it HEAD-requests every media URL the documents reference and fails on a missing object.
Adding a video
- Create
videos/<id>/video.jsonwith the document. - Upload the media to the bucket under
videos/<orientation>/<id>/. - Append the id to
videos/index.json, and tofeeds/shorts.jsonif it is a portrait short (portrait shorts must carry the full metadata set).
Adding a configuration shape
- Create
configs/<id>.json, referencing a video or inlining content. - Append the id to
configs/index.json.