taleem-player 0.0.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/favicon.ico ADDED
File without changes
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "taleem-player",
3
+ "version": "0.0.1",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "directories": {
7
+ "test": "tests"
8
+ },
9
+ "scripts": {
10
+ "test": "vitest"
11
+ },
12
+ "author": "",
13
+ "license": "ISC",
14
+ "devDependencies": {
15
+ "vitest": "^4.0.16"
16
+ }
17
+ }
package/src/Player.js ADDED
@@ -0,0 +1,52 @@
1
+ import { slideTemplates } from "./templates/index.js";
2
+
3
+ export class Player {
4
+ constructor(deck) {
5
+ this.deck = deck;
6
+ this.slides = deck.deck;
7
+ }
8
+
9
+ // ===== OLD (kept for later timeline mode)
10
+ getSlideAt(time) {
11
+ return this.slides.find(
12
+ s => time >= s.start && time <= s.end
13
+ );
14
+ }
15
+
16
+ getFrame(time) {
17
+ const slide = this.getSlideAt(time);
18
+ if (!slide) return null;
19
+
20
+ return this._buildFrame(slide, time);
21
+ }
22
+
23
+ // ===== NEW (for manual navigation)
24
+ getFrameByIndex(index) {
25
+ const slide = this.slides[index];
26
+ if (!slide) return null;
27
+
28
+ // use slide.start to reveal everything
29
+ return this._buildFrame(slide, slide.end);
30
+ }
31
+
32
+ // ===== shared internal logic
33
+ _buildFrame(slide, time) {
34
+ const template = slideTemplates[slide.type];
35
+ if (!template) {
36
+ throw new Error(`Unsupported slide type: ${slide.type}`);
37
+ }
38
+
39
+ const visibleData = slide.data.filter(
40
+ item => item.showAt === undefined || item.showAt <= time
41
+ );
42
+
43
+ return {
44
+ type: slide.type,
45
+ regions: template.group(visibleData),
46
+ meta: {
47
+ start: slide.start,
48
+ end: slide.end
49
+ }
50
+ };
51
+ }
52
+ }
package/src/index.js ADDED
@@ -0,0 +1 @@
1
+ export { Player } from "./Player.js";
@@ -0,0 +1,14 @@
1
+ export const barChart = {
2
+ group(data) {
3
+ return {
4
+ title: data.find(d => d.name === "title")?.content ?? "",
5
+ bars: data
6
+ .filter(d => d.name === "bar")
7
+ .map(d => ({
8
+ label: d.label,
9
+ value: d.value
10
+ }))
11
+ };
12
+ }
13
+ };
14
+
@@ -0,0 +1,10 @@
1
+ export const bulletList = {
2
+ group(data) {
3
+ return {
4
+ bullets: data
5
+ .filter(d => d.name === "bullet")
6
+ .map(d => d.content)
7
+ };
8
+ }
9
+ };
10
+
@@ -0,0 +1,26 @@
1
+
2
+ export const eq = {
3
+ group(data) {
4
+ const lines = [];
5
+ let current = null;
6
+
7
+ for (const item of data) {
8
+ if (!item.type.startsWith("sp")) {
9
+ current = {
10
+ type: item.type,
11
+ content: item.content,
12
+ spItems: []
13
+ };
14
+ lines.push(current);
15
+ } else if (current) {
16
+ current.spItems.push({
17
+ type: item.type,
18
+ content: item.content
19
+ });
20
+ }
21
+ }
22
+
23
+ return { lines };
24
+ }
25
+ };
26
+
@@ -0,0 +1,10 @@
1
+
2
+ export const imageSlide = {
3
+ group(data) {
4
+ const img = data.find(d => d.name === "image");
5
+ return {
6
+ image: img?.content ?? null
7
+ };
8
+ }
9
+ };
10
+
@@ -0,0 +1,16 @@
1
+
2
+ import { titleSlide } from "./titleSlide.js";
3
+ import { barChart } from "./barChart.js";
4
+ import { bulletList } from "./bulletList.js";
5
+ import { twoColumnText } from "./twoColumnText.js";
6
+ import { eq } from "./eq.js";
7
+ import { imageSlide } from "./imageSlide.js";
8
+
9
+ export const slideTemplates = {
10
+ titleSlide,
11
+ bulletList,
12
+ twoColumnText,
13
+ eq,
14
+ barChart,
15
+ imageSlide
16
+ };
@@ -0,0 +1,8 @@
1
+ export const titleSlide = {
2
+ group(data) {
3
+ return {
4
+ title: data.find(d => d.name === "title")?.content ?? ""
5
+ };
6
+ }
7
+ };
8
+
@@ -0,0 +1,11 @@
1
+
2
+ export const twoColumnText = {
3
+ group(data) {
4
+ return {
5
+ title: data.find(d => d.name === "title")?.content ?? "",
6
+ left: data.filter(d => d.name === "left").map(d => d.content),
7
+ right: data.filter(d => d.name === "right").map(d => d.content)
8
+ };
9
+ }
10
+ };
11
+
@@ -0,0 +1,22 @@
1
+ {
2
+ "version": "deck-v1",
3
+ "name": "demo-minimal",
4
+ "deck": [
5
+ {
6
+ "start": 0,
7
+ "end": 10,
8
+ "type": "titleSlide",
9
+ "data": [
10
+ { "name": "title", "content": "Angles and Transversals", "showAt": 0 }
11
+ ]
12
+ },
13
+ {
14
+ "start": 10,
15
+ "end": 20,
16
+ "type": "titleSlide",
17
+ "data": [
18
+ { "name": "title", "content": "Parallel Lines and Transversals", "showAt": 0 }
19
+ ]
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,13 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { Player } from "../src/Player.js";
3
+ import demoDeck from "./fixtures/demoDeck.json";
4
+
5
+ describe("Player basic behavior", () => {
6
+ it("renders first slide by index", () => {
7
+ const player = new Player(demoDeck);
8
+ const frame = player.getFrameByIndex(0);
9
+
10
+ expect(frame.type).toBe("titleSlide");
11
+ expect(frame.regions.title).toBe("Angles and Transversals");
12
+ });
13
+ });
@@ -0,0 +1,28 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { Player } from "../src/Player.js";
3
+
4
+ const eqDeck = {
5
+ version: "deck-v1",
6
+ deck: [
7
+ {
8
+ start: 0,
9
+ end: 10,
10
+ type: "eq",
11
+ data: [
12
+ { type: "math", content: "E = mc^2", showAt: 0 },
13
+ { type: "spText", content: "Einstein" },
14
+ { type: "text", content: "Final", showAt: 5 }
15
+ ]
16
+ }
17
+ ]
18
+ };
19
+
20
+ describe("EQ grouping", () => {
21
+ it("attaches spItems to previous line", () => {
22
+ const player = new Player(eqDeck);
23
+ const frame = player.getFrameByIndex(0);
24
+
25
+ expect(frame.regions.lines.length).toBe(2);
26
+ expect(frame.regions.lines[0].spItems.length).toBe(1);
27
+ });
28
+ });