taleem-player 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -0,0 +1,243 @@
1
+
2
+ # 📦 taleem-player
3
+
4
+ **taleem-player** is a **headless, time-driven playback engine** for Taleem slide decks.
5
+
6
+ It is designed for:
7
+
8
+ * timed presentations
9
+ * narrated lessons
10
+ * audio / video–synced slides
11
+ * external playback control
12
+
13
+ At its core, `taleem-player` does one thing well:
14
+
15
+ > **Given a valid deck and a clock, decide which slide should be active and when.**
16
+
17
+ It does **not** render slides.
18
+ It does **not** define layouts.
19
+ It does **not** ship CSS.
20
+
21
+ Those responsibilities belong elsewhere.
22
+
23
+ ---
24
+
25
+ ## Core idea
26
+
27
+ A slide deck can be interpreted in **time**, not just order.
28
+
29
+ `taleem-player` treats a deck as a **timeline**, where each slide occupies a
30
+ known interval:
31
+
32
+ ```text
33
+ [start ─────────── end)
34
+ ```
35
+
36
+ At any given moment:
37
+
38
+ * exactly one slide may be active
39
+ * or no slide (before / after the timeline)
40
+
41
+ The player’s job is to **resolve time → slide** and notify a renderer.
42
+
43
+ ---
44
+
45
+ ## What this library does
46
+
47
+ `taleem-player`:
48
+
49
+ * Accepts a **deck-v1 JSON object**
50
+ * Enforces **strict timing validity**
51
+ * Owns a single DOM stage
52
+ * Resolves the active slide for a given time
53
+ * Calls an injected **renderer**
54
+ * Supports **scrubbing, autoplay, pause, stop**
55
+ * Is completely **renderer-agnostic**
56
+
57
+ The public API is intentionally small:
58
+
59
+ ```js
60
+ player.renderAt(time)
61
+ player.destroy()
62
+ ```
63
+
64
+ Higher-level controls (play, pause, keyboard, UI) are built **on top**, not inside.
65
+
66
+ ---
67
+
68
+ ## What this library intentionally does NOT do
69
+
70
+ This is not accidental — it is a design boundary.
71
+
72
+ `taleem-player` does **not**:
73
+
74
+ * define slide layouts
75
+ * ship or inject CSS
76
+ * depend on `taleem-slides`
77
+ * interpret slide content
78
+ * infer or auto-fix timings
79
+ * manage audio or narration
80
+ * expose internal state
81
+ * depend on any framework
82
+
83
+ If a deck is invalid, the player **throws**.
84
+ If rendering looks wrong, the renderer is responsible.
85
+
86
+ ---
87
+
88
+ ## Strict timing model (important)
89
+
90
+ `taleem-player` only accepts **player-ready decks**.
91
+
92
+ Every slide **must** define:
93
+
94
+ ```json
95
+ {
96
+ "start": number,
97
+ "end": number
98
+ }
99
+ ```
100
+
101
+ Rules:
102
+
103
+ * `start` and `end` are **absolute seconds**
104
+ * `end` must be greater than `start`
105
+ * No implicit inheritance
106
+ * No auto-injection
107
+ * No browser-style forgiveness
108
+
109
+ This strictness is intentional and non-negotiable.
110
+
111
+ ---
112
+
113
+ ## Renderer contract
114
+
115
+ `taleem-player` does not know how slides look.
116
+
117
+ Instead, it calls a renderer with this contract:
118
+
119
+ ```js
120
+ renderer.render({
121
+ mount, // HTMLElement
122
+ slide, // full slide JSON
123
+ time // absolute time (seconds)
124
+ })
125
+ ```
126
+
127
+ That’s it.
128
+
129
+ How the slide is rendered — HTML, SVG, Canvas, WebGL — is **not the player’s concern**.
130
+
131
+ ---
132
+
133
+ ## Relationship to other Taleem libraries
134
+
135
+ `taleem-player` is part of a **layered system**.
136
+
137
+ ### Lower-level
138
+
139
+ **taleem-slides**
140
+ Pure slide renderer.
141
+ Converts slide JSON into HTML + CSS.
142
+
143
+ ### Higher-level
144
+
145
+ **Taleem demo app**
146
+ Wires together:
147
+
148
+ * `taleem-player`
149
+ * `taleem-slides`
150
+ * playback controls
151
+ * UI
152
+
153
+ ### Sibling
154
+
155
+ **taleem-browser**
156
+ Index-based slide viewer (no time).
157
+
158
+ Each library has **one responsibility**.
159
+ They are composed — never merged.
160
+
161
+ ---
162
+
163
+ ## Why this library exists
164
+
165
+ `taleem-browser` treats slides as a **document**.
166
+ `taleem-player` treats slides as a **timeline**.
167
+
168
+ Both are valid interpretations.
169
+
170
+ By separating them:
171
+
172
+ * timed playback does not pollute browsing logic
173
+ * rendering does not pollute playback logic
174
+ * decks remain portable JSON documents
175
+
176
+ This separation allows:
177
+
178
+ * narration sync
179
+ * recorded lessons
180
+ * adaptive playback
181
+ * multiple renderers
182
+
183
+ without rewriting the core.
184
+
185
+ ---
186
+
187
+ ## Typical usage (composition layer)
188
+
189
+ ```js
190
+ import { createTaleemPlayer } from "taleem-player";
191
+ import { createSlidesRenderer } from "taleem-slides";
192
+ import "taleem-slides/dist/taleem.css";
193
+
194
+ const renderer = createSlidesRenderer();
195
+
196
+ const player = createTaleemPlayer({
197
+ mount: "#app",
198
+ deck,
199
+ renderer
200
+ });
201
+
202
+ // external clock
203
+ player.renderAt(12.5);
204
+ ```
205
+
206
+ The **player** never imports slides.
207
+ The **slides** never import the player.
208
+ The **app** composes them.
209
+
210
+ ---
211
+
212
+ ## Design philosophy (locked)
213
+
214
+ * Time is external
215
+ * Rendering is replaceable
216
+ * Strictness beats convenience
217
+ * Libraries stay small
218
+ * Composition happens at the edge
219
+
220
+ > **A player decides *when*.
221
+ > A renderer decides *how*.
222
+ > An app decides *why*.**
223
+
224
+ ---
225
+
226
+ ## Project status
227
+
228
+ **Core complete and stable.**
229
+
230
+ Future work belongs in:
231
+
232
+ * demo applications
233
+ * playback UI layers
234
+ * renderers
235
+ * authoring tools
236
+
237
+ The player itself should change rarely.
238
+
239
+ ---
240
+
241
+ ## License
242
+
243
+ MIT
@@ -0,0 +1,92 @@
1
+ // src/core/stage.js
2
+ function createStage(mount) {
3
+ if (!mount) throw new Error("taleem-player: mount is required");
4
+ const root = typeof mount === "string" ? document.querySelector(mount) : mount;
5
+ if (!root) throw new Error("taleem-player: mount element not found");
6
+ root.innerHTML = "";
7
+ const stage = document.createElement("div");
8
+ stage.className = "taleem-player-stage";
9
+ stage.style.position = "relative";
10
+ stage.style.width = "100%";
11
+ stage.style.height = "100%";
12
+ root.appendChild(stage);
13
+ function clear() {
14
+ stage.innerHTML = "";
15
+ }
16
+ function destroy() {
17
+ root.innerHTML = "";
18
+ }
19
+ return {
20
+ el: stage,
21
+ clear,
22
+ destroy
23
+ };
24
+ }
25
+
26
+ // src/utils/timing.js
27
+ function getDeckDuration(deck) {
28
+ if (!deck?.deck?.length) return 0;
29
+ return Math.max(...deck.deck.map((s) => s.end));
30
+ }
31
+ function getSlideAtTime(deck, time) {
32
+ return deck.deck.find((s) => time >= s.start && time < s.end) || null;
33
+ }
34
+
35
+ // src/utils/assertPlayable.js
36
+ function assertDeckPlayable(deck) {
37
+ if (!deck || deck.version !== "deck-v1") {
38
+ throw new Error("Invalid deck version");
39
+ }
40
+ if (!Array.isArray(deck.deck)) {
41
+ throw new Error("Deck must contain slides");
42
+ }
43
+ deck.deck.forEach((s, i) => {
44
+ if (typeof s.start !== "number" || typeof s.end !== "number") {
45
+ throw new Error(`Slide ${i} has invalid timing`);
46
+ }
47
+ if (s.end <= s.start) {
48
+ throw new Error(`Slide ${i} end must be > start`);
49
+ }
50
+ });
51
+ return true;
52
+ }
53
+
54
+ // src/core/player.js
55
+ function createTaleemPlayer({ mount, deck, renderer }) {
56
+ if (!renderer || typeof renderer.render !== "function") {
57
+ throw new Error("taleem-player: renderer with render() required");
58
+ }
59
+ assertDeckPlayable(deck);
60
+ const stage = createStage(mount);
61
+ let lastSlide = null;
62
+ function renderAt(time) {
63
+ const slide = getSlideAtTime(deck, time);
64
+ if (!slide) {
65
+ stage.clear();
66
+ lastSlide = null;
67
+ return;
68
+ }
69
+ if (slide !== lastSlide) {
70
+ stage.clear();
71
+ lastSlide = slide;
72
+ }
73
+ renderer.render({
74
+ mount: stage.el,
75
+ slide,
76
+ time
77
+ });
78
+ }
79
+ function destroy() {
80
+ stage.destroy();
81
+ }
82
+ return {
83
+ renderAt,
84
+ destroy
85
+ };
86
+ }
87
+ export {
88
+ assertDeckPlayable,
89
+ createTaleemPlayer,
90
+ getDeckDuration,
91
+ getSlideAtTime
92
+ };
@@ -0,0 +1,116 @@
1
+ var TaleemBrowser = (() => {
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.js
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ assertDeckPlayable: () => assertDeckPlayable,
24
+ createTaleemPlayer: () => createTaleemPlayer,
25
+ getDeckDuration: () => getDeckDuration,
26
+ getSlideAtTime: () => getSlideAtTime
27
+ });
28
+
29
+ // src/core/stage.js
30
+ function createStage(mount) {
31
+ if (!mount) throw new Error("taleem-player: mount is required");
32
+ const root = typeof mount === "string" ? document.querySelector(mount) : mount;
33
+ if (!root) throw new Error("taleem-player: mount element not found");
34
+ root.innerHTML = "";
35
+ const stage = document.createElement("div");
36
+ stage.className = "taleem-player-stage";
37
+ stage.style.position = "relative";
38
+ stage.style.width = "100%";
39
+ stage.style.height = "100%";
40
+ root.appendChild(stage);
41
+ function clear() {
42
+ stage.innerHTML = "";
43
+ }
44
+ function destroy() {
45
+ root.innerHTML = "";
46
+ }
47
+ return {
48
+ el: stage,
49
+ clear,
50
+ destroy
51
+ };
52
+ }
53
+
54
+ // src/utils/timing.js
55
+ function getDeckDuration(deck) {
56
+ if (!deck?.deck?.length) return 0;
57
+ return Math.max(...deck.deck.map((s) => s.end));
58
+ }
59
+ function getSlideAtTime(deck, time) {
60
+ return deck.deck.find((s) => time >= s.start && time < s.end) || null;
61
+ }
62
+
63
+ // src/utils/assertPlayable.js
64
+ function assertDeckPlayable(deck) {
65
+ if (!deck || deck.version !== "deck-v1") {
66
+ throw new Error("Invalid deck version");
67
+ }
68
+ if (!Array.isArray(deck.deck)) {
69
+ throw new Error("Deck must contain slides");
70
+ }
71
+ deck.deck.forEach((s, i) => {
72
+ if (typeof s.start !== "number" || typeof s.end !== "number") {
73
+ throw new Error(`Slide ${i} has invalid timing`);
74
+ }
75
+ if (s.end <= s.start) {
76
+ throw new Error(`Slide ${i} end must be > start`);
77
+ }
78
+ });
79
+ return true;
80
+ }
81
+
82
+ // src/core/player.js
83
+ function createTaleemPlayer({ mount, deck, renderer }) {
84
+ if (!renderer || typeof renderer.render !== "function") {
85
+ throw new Error("taleem-player: renderer with render() required");
86
+ }
87
+ assertDeckPlayable(deck);
88
+ const stage = createStage(mount);
89
+ let lastSlide = null;
90
+ function renderAt(time) {
91
+ const slide = getSlideAtTime(deck, time);
92
+ if (!slide) {
93
+ stage.clear();
94
+ lastSlide = null;
95
+ return;
96
+ }
97
+ if (slide !== lastSlide) {
98
+ stage.clear();
99
+ lastSlide = slide;
100
+ }
101
+ renderer.render({
102
+ mount: stage.el,
103
+ slide,
104
+ time
105
+ });
106
+ }
107
+ function destroy() {
108
+ stage.destroy();
109
+ }
110
+ return {
111
+ renderAt,
112
+ destroy
113
+ };
114
+ }
115
+ return __toCommonJS(index_exports);
116
+ })();
package/package.json CHANGED
@@ -1,38 +1,22 @@
1
-
2
1
  {
3
- "name": "taleem-player",
4
- "version": "0.1.0",
5
- "description": "Headless time-based player for deck-v1 (play, pause, scrub, no renderer)",
6
- "type": "module",
7
- "main": "./dist/taleem-player.umd.js",
8
- "module": "./dist/taleem-player.esm.js",
9
- "exports": {
10
- ".": {
11
- "import": "./dist/taleem-player.esm.js",
12
- "require": "./dist/taleem-player.umd.js"
13
- }
14
- },
15
- "files": [
16
- "dist"
17
- ],
18
- "scripts": {
19
- "build": "node ./scripts/build.js",
20
- "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
21
- },
22
- "keywords": [
23
- "taleem",
24
- "presentation",
25
- "player",
26
- "deck-v1",
27
- "timeline",
28
- "education"
29
- ],
30
- "author": "Taleem.Help",
31
- "license": "MIT",
32
- "devDependencies": {
33
- "esbuild": "^0.27.2",
34
- "jest": "^29.7.0",
35
- "jest-environment-jsdom": "^30.2.0"
2
+ "name": "taleem-player",
3
+ "version": "0.2.1",
4
+ "type": "module",
5
+
6
+ "main": "./dist/taleem-player.umd.js",
7
+ "module": "./dist/taleem-player.esm.js",
8
+
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/taleem-player.esm.js",
12
+ "require": "./dist/taleem-player.umd.js"
36
13
  }
14
+ },
15
+
16
+ "files": ["dist"],
17
+
18
+ "scripts": {
19
+ "build": "node ./scripts/build.js",
20
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
37
21
  }
38
-
22
+ }