taleem-slides 0.3.0 → 0.4.0

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.
Files changed (51) hide show
  1. package/README.md +159 -136
  2. package/package.json +2 -2
  3. package/src/{registry.js → SlideTemplates.js} +1 -1
  4. package/src/getSlideTemplate.js +12 -0
  5. package/src/index.js +2 -3
  6. package/src/slides/BarChartSlide.js +44 -42
  7. package/src/slides/BigNumberSlide.js +25 -23
  8. package/src/slides/BulletListSlide.js +36 -24
  9. package/src/slides/ContactSlide.js +23 -20
  10. package/src/slides/CornerWordsSlide.js +28 -25
  11. package/src/slides/DonutChartSlide.js +39 -26
  12. package/src/slides/EqSlide.js +33 -20
  13. package/src/slides/FillImageSlide.js +20 -22
  14. package/src/slides/ImageLeftBulletsRightSlide.js +39 -26
  15. package/src/slides/ImageRightBulletsLeftSlide.js +40 -26
  16. package/src/slides/ImageSlide.js +21 -21
  17. package/src/slides/ImageWithCaptionSlide.js +25 -24
  18. package/src/slides/ImageWithTitleSlide.js +25 -24
  19. package/src/slides/QuoteSlide.js +24 -23
  20. package/src/slides/QuoteWithImageSlide.js +31 -28
  21. package/src/slides/StatisticSlide.js +26 -24
  22. package/src/slides/SvgPointerSlide.js +21 -21
  23. package/src/slides/TableSlide.js +38 -25
  24. package/src/slides/TitleAndParaSlide.js +26 -24
  25. package/src/slides/TitleAndSubtitleSlide.js +27 -24
  26. package/src/slides/TitleSlide.js +20 -38
  27. package/src/slides/TwoColumnTextSlide.js +58 -25
  28. package/tests/slides.barChart.test.js +7 -42
  29. package/tests/slides.bigNumber.test.js +16 -16
  30. package/tests/slides.bulletList.test.js +19 -20
  31. package/tests/slides.contactSlide.test.js +12 -32
  32. package/tests/slides.cornerWordsSlide.test.js +12 -16
  33. package/tests/slides.donutChart.test.js +3 -2
  34. package/tests/slides.eq.test.js +10 -7
  35. package/tests/slides.fillImage.test.js +13 -17
  36. package/tests/slides.imageLeftBulletsRight.test.js +5 -11
  37. package/tests/slides.imageRightBulletsLeft.test.js +3 -8
  38. package/tests/slides.imageSlide.test.js +14 -17
  39. package/tests/slides.imageWithCaption.test.js +10 -26
  40. package/tests/slides.imageWithTitle.test.js +9 -26
  41. package/tests/slides.quoteSlide.test.js +17 -17
  42. package/tests/slides.quoteWithImage.test.js +9 -27
  43. package/tests/slides.statistic.test.js +10 -26
  44. package/tests/slides.svgPointer.test.js +7 -19
  45. package/tests/slides.test.js +18 -32
  46. package/tests/slides.titleAndPara.test.js +19 -25
  47. package/tests/slides.titleAndSubtitle.test.js +14 -17
  48. package/tests/slides.twoColumnText.test.js +14 -20
  49. package/src/interpreter/slideBuilder.js +0 -65
  50. package/src/slideManager/SlideManager.js +0 -62
  51. package/tests/interpreter.test.js +0 -47
package/README.md CHANGED
@@ -1,224 +1,247 @@
1
1
 
2
2
  # 📦 taleem-slides
3
3
 
4
- > **Deterministic slide interpretation & rendering engine for deck-v1**
4
+ ## ⚠️ Warning : Work in Progress expect breaking changes
5
5
 
6
- `taleem-slides` is a **pure, test-locked rendering layer**.
7
- It takes a validated `deck-v1` JSON and produces **deterministic HTML output** through a strict public API.
6
+ > **Pure slide template library for Taleem decks**
8
7
 
9
- This project does **not**:
8
+ `taleem-slides` is a **simple, deterministic template library** that turns
9
+ **deck-style slide JSON** into **HTML**.
10
10
 
11
- * build decks
12
- * mutate data
13
- * manage timing state
14
- * expose slide internals
11
+ It does **one thing only**:
12
+
13
+ > **Given slide data + render state → return HTML**
15
14
 
16
- It only **interprets** and **renders**.
15
+ It does **not** manage time, indexes, playback, or decks.
17
16
 
18
17
  ---
19
18
 
20
- ## 🧠 Mental Model
19
+ ## 🌐 Live Display Center (Important)
21
20
 
22
- ```
23
- deck-v1 JSON
24
-
25
- slideBuilder()
26
-
27
- SlideManager
28
-
29
- renderSlide(index [, showAt])
30
- ```
21
+ 👉 **Official live display & reference implementation**
22
+ **[https://bilza2023.github.io/taleem/](https://bilza2023.github.io/taleem/)**
31
23
 
32
- Key idea:
33
- **Slides are private. Rendering is the only contract.**
24
+ This is **not a mock demo**.This link is the **active display center** where:
34
25
 
26
+ - slide templates are rendered in real browsers
27
+ - visual behavior is validated
28
+ - browser/player integration is tested
35
29
  ---
36
30
 
37
- ## What This Project Guarantees
31
+ ## Taleem.help Philosophy
38
32
 
39
- * Deterministic rendering (same input same HTML)
40
- * Strict validation per slide type
41
- * Zero mutation after build
42
- * No access to internal slide objects
43
- * Full test coverage across all slide types
33
+ **Taleem.help** is an educational technology initiative focused on making
34
+ **content-first learning tools**.
44
35
 
45
- ---
36
+ The goal of the `taleem-*` libraries is simple:
46
37
 
47
- ## 🔑 Public API
38
+ > Enable educators to create **JSON-based presentations**
39
+ > and display them online using **free, open tools**.
48
40
 
49
- ### `slideBuilder(deckV1Json) → SlideManager`
41
+ Key ideas:
50
42
 
51
- Builds and validates a deck, returning a `SlideManager`.
43
+ * Slides already encode *layout + structure*
44
+ * Users provide **content only**
45
+ * There are **no configuration knobs**
46
+ * What you see is what the template decides
52
47
 
53
- ```js
54
- import { slideBuilder } from "taleem-slides";
48
+ This removal of choice is **intentional**.
55
49
 
56
- const manager = slideBuilder(deckJson);
57
- ```
50
+ It makes the system:
58
51
 
59
- Throws immediately on:
52
+ * easy to learn
53
+ * hard to misuse
54
+ * consistent across platforms
60
55
 
61
- * invalid deck structure
62
- * unsupported slide types
63
- * invalid slide data
56
+ If a different layout is needed, the solution is **not configuration** —
57
+ it is **a new slide template**.
58
+
59
+ Templates are cheap.
60
+ Even hundreds of templates add no runtime cost.
64
61
 
65
62
  ---
66
63
 
67
- ### `SlideManager.renderSlide(index, showAt?) string`
64
+ ## What this library is
68
65
 
69
- Renders **one slide** to HTML.
66
+ * A collection of **slide templates**
67
+ * Each template:
70
68
 
71
- ```js
72
- const html = manager.renderSlide(0);
73
- ```
69
+ * reads slide JSON
70
+ * renders HTML
71
+ * applies CSS classes based on a given state
72
+ * Fully **stateless** and **pure**
74
73
 
75
- Notes:
74
+ Think of it as:
76
75
 
77
- * `index` is zero-based
78
- * `showAt` is optional (for time-aware slides)
79
- * return value is **plain HTML string**
76
+ > *Handlebars / JSX for Taleem slides*
80
77
 
81
78
  ---
82
79
 
83
- ### `SlideManager.renderAll() string`
80
+ ## What this library is NOT
84
81
 
85
- Renders **all slides** as a static HTML dump.
82
+ `taleem-slides` does **not**:
86
83
 
87
- ```js
88
- const fullHtml = manager.renderAll();
89
- ```
84
+ * build decks
85
+ * validate full decks
86
+ * manage timing (`showAt`)
87
+ * decide which slide is active
88
+ * manage playback
89
+ * mutate data
90
+
91
+ All of that belongs elsewhere.
90
92
 
91
93
  ---
92
94
 
93
- ## 🔒 Encapsulation Rules (By Design)
95
+ ## 🧠 Mental Model
94
96
 
95
- * `SlideManager` does **not** expose slides
96
- * Slides are frozen internally
97
- * Rendered output is a string (immutable by nature)
97
+ ```
98
+ slide JSON + render state
99
+
100
+ slide template
101
+
102
+ HTML
103
+ ```
98
104
 
99
- If you want to *inspect structure*, that belongs in **taleem-core**, not here.
105
+ How the state is calculated is **not this library’s concern**.
100
106
 
101
107
  ---
102
108
 
103
- ## 🧪 Testing Philosophy
109
+ ## 📦 Installation
104
110
 
105
- This project has **56 passing tests** covering:
111
+ ```bash
112
+ npm install taleem-slides
113
+ ```
106
114
 
107
- * slideBuilder validation
108
- * every slide type
109
- * deterministic rendering
110
- * error handling
111
- * edge cases
115
+ ---
112
116
 
113
- Tests assert **behavior**, not snapshots.
117
+ ## 🚀 Basic Usage
114
118
 
115
- This test suite is the **living specification**.
119
+ ### 1️⃣ Import a template
116
120
 
117
- ---
121
+ ```js
122
+ import { getSlideTemplate } from "taleem-slides";
123
+ ```
118
124
 
119
- ## 🎞️ Supported Slide Types
125
+ ---
120
126
 
121
- `taleem-slides` supports all canonical `deck-v1` slide types, including:
127
+ ### 2️⃣ Load slide data (once)
122
128
 
123
- * titleSlide
124
- * titleAndSubtitle
125
- * titleAndPara
126
- * bulletList
127
- * twoColumnText
128
- * imageSlide
129
- * imageWithTitle
130
- * imageWithCaption
131
- * imageLeftBulletsRight
132
- * imageRightBulletsLeft
133
- * table
134
- * statistic
135
- * donutChart
136
- * barChart
137
- * bigNumber
138
- * quoteSlide
139
- * quoteWithImage
140
- * cornerWordsSlide
141
- * contactSlide
142
- * fillImage
143
- * eq
144
- * svgPointer
129
+ ```js
130
+ const SlideTemplate = getSlideTemplate("bulletList");
131
+
132
+ const slide = SlideTemplate.fromJSON({
133
+ type: "bulletList",
134
+ data: [
135
+ { name: "bullet", content: "First point" },
136
+ { name: "bullet", content: "Second point" },
137
+ { name: "bullet", content: "Third point" }
138
+ ]
139
+ });
140
+ ```
145
141
 
146
- All validation rules are enforced at build time.
142
+ > `fromJSON()` only **reads and stores structure**.
143
+ > No timing. No logic.
147
144
 
148
145
  ---
149
146
 
150
- ## 🧊 Versioning & Stability
147
+ ### 3️⃣ Render with state
151
148
 
152
- * This project targets **deck-v1 only**
153
- * No breaking changes without `deck-v2`
154
- * Rendering output is intentionally simple HTML
155
- * Styling is the responsibility of the consuming app
149
+ ```js
150
+ const html = slide.render({
151
+ visibleCount: 2,
152
+ activeIndex: 1
153
+ });
154
+ ```
155
+
156
+ This will:
157
+
158
+ * render first 2 bullets
159
+ * highlight the second bullet
160
+ * dim the first
156
161
 
157
162
  ---
158
163
 
159
- ## 📍 When to Use This
164
+ ## 🎨 Render State Contract
160
165
 
161
- Use `taleem-slides` when you want:
166
+ Templates accept a **render state object**.
162
167
 
163
- * a **trustworthy rendering engine**
164
- * clean separation from content generation
165
- * confidence that slides behave exactly as specified
168
+ Common fields:
166
169
 
167
- Do **not** use it for:
170
+ ```ts
171
+ {
172
+ visibleCount?: number; // how many items exist
173
+ activeIndex?: number; // which item is highlighted
174
+ }
175
+ ```
168
176
 
169
- * authoring decks
170
- * editing slides
171
- * managing playback state
177
+ Slides may choose to use one or both.
172
178
 
173
179
  ---
174
180
 
181
+ ## 🎯 Class Name Contract
182
+
183
+ Templates apply **standard class names only**:
184
+
185
+ ```text
186
+ .is-active
187
+ .is-dim
188
+ .is-hidden
189
+ ```
190
+
191
+ Styling is handled entirely by the consuming app.
192
+
175
193
  ---
176
194
 
177
- # 🧠 taleem-core (Contextual Overview)
195
+ ## 🧭 How this fits in the ecosystem
178
196
 
179
- > **Authoring & specification layer for deck-v1**
197
+ `taleem-slides` is intentionally **small** and **focused**.
180
198
 
181
- `taleem-core` is responsible for **creating valid decks**.
182
- `taleem-slides` is responsible for **rendering them**.
199
+ It is used by higher-level projects:
183
200
 
184
- They are intentionally separate.
201
+ ### 🧩 Sister Projects
185
202
 
186
- ---
203
+ * **taleem-browser**
204
+ Index-based slide viewer (manual navigation)
205
+
206
+ * **taleem-player**
207
+ Time-based slide player (audio / video synced)
187
208
 
188
- ## Responsibility Split
209
+ Both projects:
189
210
 
190
- | Concern | taleem-core | taleem-slides |
191
- | ------------------- | ----------- | ------------- |
192
- | Deck creation | ✅ | ❌ |
193
- | Schema validation | ✅ | ❌ |
194
- | EQ expansion | ✅ | ❌ |
195
- | Timing rules | ✅ | ❌ |
196
- | Rendering HTML | ❌ | ✅ |
197
- | Slide encapsulation | ❌ | ✅ |
211
+ * compute render state (`activeIndex`, `visibleCount`)
212
+ * pass it to `taleem-slides`
213
+ * receive consistent HTML output
198
214
 
199
215
  ---
200
216
 
201
- ## Core Artifacts
217
+ ## 🧪 Demo & Reference Projects
202
218
 
203
- From the docs you uploaded:
219
+ * 🌐 **Live Display Center**
220
+ [https://bilza2023.github.io/taleem/](https://bilza2023.github.io/taleem/)
204
221
 
205
- * `api.md` defines **deck-v1 contract**
206
- * `eq.md` defines **EQ slide expansion rules**
207
- * `timings.md` → defines **global timing semantics**
222
+ * 📁 **GitHub Demo / Playground**
223
+ *(link can be added here when ready)*
208
224
 
209
- `taleem-slides` **trusts** these documents.
210
- It does not reinterpret them.
225
+ ---
226
+
227
+ ## 🧊 Stability & Versioning
228
+
229
+ * Targets **deck-v1**
230
+ * Breaking changes allowed during WIP phase
231
+ * HTML output is intentionally simple and predictable
211
232
 
212
233
  ---
213
234
 
214
- ## Architectural Law (Important)
235
+ ## 🧠 Design Principle (Locked)
215
236
 
216
- > **Authoring and rendering must never mix**
237
+ > **taleem-slides renders HTML.
238
+ > It does not decide *when* or *why*.**
239
+
240
+ ---
217
241
 
218
- Once a deck enters `taleem-slides`, it is:
242
+ If you want, next logical steps are:
219
243
 
220
- * assumed valid
221
- * treated as immutable
222
- * rendered deterministically
244
+ * rewrite one slide as the **canonical reference**
245
+ * or update taleem-browser to consume the new API cleanly
223
246
 
224
- This is why the system scales cleanly.
247
+ This README now correctly **anchors the entire ecosystem**.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "taleem-slides",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
- "description": "Pure slide interpretation engine for deck-v1 (no DOM, no UI)",
5
+ "description": "Convert json taleem schema into html for slides",
6
6
 
7
7
  "exports": {
8
8
  ".": "./src/index.js"
@@ -27,7 +27,7 @@ import { ContactSlide } from "./slides/ContactSlide.js";
27
27
  import { EqSlide } from "./slides/EqSlide.js";
28
28
  import { SvgPointerSlide } from "./slides/SvgPointerSlide.js";
29
29
 
30
- export const registry = {
30
+ export const SlideTemplates = {
31
31
  titleSlide: TitleSlide,
32
32
  titleAndSubtitle: TitleAndSubtitleSlide,
33
33
  titleAndPara: TitleAndParaSlide,
@@ -0,0 +1,12 @@
1
+ // src/getSlideTemplate.js
2
+ import { SlideTemplates } from "./SlideTemplates.js";
3
+
4
+ export function getSlideTemplate(type) {
5
+ const template = SlideTemplates[type];
6
+
7
+ if (!template) {
8
+ throw new Error(`Unknown slide template type "${type}"`);
9
+ }
10
+
11
+ return template;
12
+ }
package/src/index.js CHANGED
@@ -1,5 +1,4 @@
1
-
2
1
  // src/index.js
3
2
 
4
- export { slideBuilder } from "./interpreter/slideBuilder.js";
5
-
3
+ export { SlideTemplates } from "./SlideTemplates.js";
4
+ export { getSlideTemplate } from "./getSlideTemplate.js";
@@ -1,44 +1,46 @@
1
+ // BarChartSlide.js
1
2
  export const BarChartSlide = {
2
- type: "barChart",
3
-
4
- fromJSON(raw) {
5
- const bars = raw.data?.filter(d => d.name === "bar");
6
-
7
- if (!bars || bars.length === 0) {
8
- throw new Error("barChart: requires at least one bar");
9
- }
10
-
11
- // validate bar structure
12
- bars.forEach((b, i) => {
13
- if (
14
- typeof b.content !== "object" ||
15
- typeof b.content.label !== "string" ||
16
- typeof b.content.value !== "number"
17
- ) {
18
- throw new Error(`barChart: invalid bar at index ${i}`);
19
- }
20
- });
21
-
22
- return Object.freeze({
23
- type: "barChart",
24
-
25
- render() {
26
- return `
27
- <section class="slide barChart">
28
- <ul class="bars">
29
- ${bars.map(
30
- b => `
31
- <li class="bar">
32
- <span class="bar-label">${b.content.label}</span>
33
- <span class="bar-value">${b.content.value}</span>
34
- </li>
35
- `
36
- ).join("")}
37
- </ul>
38
- </section>
39
- `;
40
- }
41
- });
3
+ type: "barChart",
4
+
5
+ fromJSON(raw) {
6
+ const bars = raw.data
7
+ ?.filter(d => d.name === "bar")
8
+ .map(d => ({
9
+ label: d.content.label,
10
+ value: d.content.value
11
+ }));
12
+
13
+ if (!bars?.length) {
14
+ throw new Error("barChart: requires at least one bar");
42
15
  }
43
- };
44
-
16
+
17
+ return Object.freeze({
18
+ type: "barChart",
19
+ bars,
20
+
21
+ render({ visibleCount = bars.length, activeIndex = null } = {}) {
22
+ return `
23
+ <section class="slide barChart">
24
+ <ul class="bars">
25
+ ${bars.map((b, i) => {
26
+ if (i >= visibleCount) return "";
27
+ const cls =
28
+ i === activeIndex
29
+ ? "is-active"
30
+ : i < activeIndex
31
+ ? "is-dim"
32
+ : "";
33
+ return `
34
+ <li class="bar ${cls}">
35
+ <span class="bar-label">${b.label}</span>
36
+ <span class="bar-value">${b.value}</span>
37
+ </li>
38
+ `;
39
+ }).join("")}
40
+ </ul>
41
+ </section>
42
+ `;
43
+ }
44
+ });
45
+ }
46
+ };
@@ -1,24 +1,26 @@
1
+ // BigNumberSlide.js
1
2
  export const BigNumberSlide = {
2
- type: "bigNumber",
3
-
4
- fromJSON(raw) {
5
- const value = raw.data?.find(d => d.name === "number")?.content;
6
- const label = raw.data?.find(d => d.name === "label")?.content;
7
-
8
- if (!value) throw new Error("bigNumber: number required");
9
-
10
- return Object.freeze({
11
- type: "bigNumber",
12
- render() {
13
- return `
14
- <section class="slide bigNumber">
15
- <div class="number">${value}</div>
16
- ${label ? `<div class="label">${label}</div>` : ""}
17
- </section>
18
- `;
19
- }
20
-
21
- });
22
- }
23
- };
24
-
3
+ type: "bigNumber",
4
+
5
+ fromJSON(raw) {
6
+ const value = raw.data?.find(d => d.name === "number")?.content;
7
+ const label = raw.data?.find(d => d.name === "label")?.content;
8
+
9
+ if (!value) throw new Error("bigNumber: number required");
10
+
11
+ return Object.freeze({
12
+ type: "bigNumber",
13
+ value,
14
+ label,
15
+
16
+ render() {
17
+ return `
18
+ <section class="slide bigNumber">
19
+ <div class="number">${value}</div>
20
+ ${label ? `<div class="label">${label}</div>` : ""}
21
+ </section>
22
+ `;
23
+ }
24
+ });
25
+ }
26
+ };
@@ -1,26 +1,38 @@
1
+ // BulletListSlide.js
1
2
  export const BulletListSlide = {
2
- type: "bulletList",
3
-
4
- fromJSON(raw) {
5
- const bullets = raw.data?.filter(d => d.name === "bullet").map(d => d.content);
6
-
7
- if (!bullets || bullets.length === 0) {
8
- throw new Error("bulletList: requires at least one bullet");
9
- }
10
-
11
- return Object.freeze({
12
- type: "bulletList",
13
- render() {
14
- return `
15
- <section class="slide bulletList">
16
- <ul>
17
- ${bullets.map(b => `<li>${b}</li>`).join("")}
18
- </ul>
19
- </section>
20
- `;
21
- }
22
-
23
- });
3
+ type: "bulletList",
4
+
5
+ fromJSON(raw) {
6
+ const bullets = raw.data
7
+ ?.filter(d => d.name === "bullet")
8
+ .map(d => ({ content: d.content }));
9
+
10
+ if (!bullets?.length) {
11
+ throw new Error("bulletList: requires at least one bullet");
24
12
  }
25
- };
26
-
13
+
14
+ return Object.freeze({
15
+ type: "bulletList",
16
+ bullets,
17
+
18
+ render({ visibleCount = bullets.length, activeIndex = null } = {}) {
19
+ return `
20
+ <section class="slide bulletList">
21
+ <ul>
22
+ ${bullets.map((b, i) => {
23
+ if (i >= visibleCount) return "";
24
+ const cls =
25
+ i === activeIndex
26
+ ? "is-active"
27
+ : i < activeIndex
28
+ ? "is-dim"
29
+ : "";
30
+ return `<li class="${cls}">${b.content}</li>`;
31
+ }).join("")}
32
+ </ul>
33
+ </section>
34
+ `;
35
+ }
36
+ });
37
+ }
38
+ };
@@ -1,22 +1,25 @@
1
+ // ContactSlide.js
1
2
  export const ContactSlide = {
2
- type: "contactSlide",
3
-
4
- fromJSON(raw) {
5
- const items = raw.data?.map(d => `<div>${d.content}</div>`);
6
-
7
- if (!items?.length) throw new Error("contactSlide: content required");
8
-
9
- return Object.freeze({
10
- type: "contactSlide",
11
- render() {
12
- return `
13
- <section class="slide contactSlide">
14
- ${items.join("")}
15
- </section>
16
- `;
17
- }
18
-
19
- });
3
+ type: "contactSlide",
4
+
5
+ fromJSON(raw) {
6
+ const items = raw.data?.map(d => ({ content: d.content }));
7
+
8
+ if (!items?.length) {
9
+ throw new Error("contactSlide: content required");
20
10
  }
21
- };
22
-
11
+
12
+ return Object.freeze({
13
+ type: "contactSlide",
14
+ items,
15
+
16
+ render() {
17
+ return `
18
+ <section class="slide contactSlide">
19
+ ${items.map(i => `<div>${i.content}</div>`).join("")}
20
+ </section>
21
+ `;
22
+ }
23
+ });
24
+ }
25
+ };