taleem-slides 0.4.0 β†’ 0.6.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 (64) hide show
  1. package/README.md +90 -131
  2. package/package.json +5 -4
  3. package/src/SlideTemplates.js +0 -1
  4. package/src/index.js +1 -0
  5. package/src/resolveBackground.js +10 -0
  6. package/src/slides/BarChartSlide.js +5 -5
  7. package/src/slides/BigNumberSlide.js +7 -6
  8. package/src/slides/BulletListSlide.js +5 -6
  9. package/src/slides/ContactSlide.js +3 -4
  10. package/src/slides/CornerWordsSlide.js +15 -10
  11. package/src/slides/DonutChartSlide.js +16 -16
  12. package/src/slides/EqSlide.js +13 -9
  13. package/src/slides/FillImageSlide.js +4 -2
  14. package/src/slides/ImageLeftBulletsRightSlide.js +6 -14
  15. package/src/slides/ImageRightBulletsLeftSlide.js +6 -14
  16. package/src/slides/ImageSlide.js +0 -1
  17. package/src/slides/ImageWithCaptionSlide.js +2 -1
  18. package/src/slides/ImageWithTitleSlide.js +2 -1
  19. package/src/slides/QuoteSlide.js +8 -5
  20. package/src/slides/QuoteWithImageSlide.js +2 -3
  21. package/src/slides/StatisticSlide.js +5 -6
  22. package/src/slides/TableSlide.js +17 -22
  23. package/src/slides/TitleAndParaSlide.js +1 -4
  24. package/src/slides/TitleAndSubtitleSlide.js +1 -6
  25. package/src/slides/TitleSlide.js +5 -5
  26. package/src/slides/TwoColumnTextSlide.js +7 -38
  27. package/tests/slide.title.test.js +15 -0
  28. package/tests/slides.barChart.test.js +15 -20
  29. package/tests/slides.bigNumber.test.js +8 -19
  30. package/tests/slides.bulletList.test.js +9 -23
  31. package/tests/slides.contactSlide.test.js +8 -18
  32. package/tests/slides.cornerWordsSlide.test.js +8 -14
  33. package/tests/slides.donutChart.test.js +10 -20
  34. package/tests/slides.eq.test.js +9 -20
  35. package/tests/slides.fillImage.test.js +8 -14
  36. package/tests/slides.imageLeftBulletsRight.test.js +9 -12
  37. package/tests/slides.imageRightBulletsLeft.test.js +9 -12
  38. package/tests/slides.imageSlide.test.js +8 -15
  39. package/tests/slides.imageWithCaption.test.js +8 -18
  40. package/tests/slides.imageWithTitle.test.js +8 -18
  41. package/tests/slides.quoteSlide.test.js +8 -19
  42. package/tests/slides.quoteWithImage.test.js +9 -18
  43. package/tests/slides.statistic.test.js +8 -18
  44. package/tests/slides.table.test.js +8 -33
  45. package/tests/slides.titleAndPara.test.js +8 -20
  46. package/tests/slides.titleAndSubtitle.test.js +8 -20
  47. package/tests/slides.twoColumnText.test.js +7 -20
  48. package/decks/angles_and_transversals.json +0 -85
  49. package/decks/congruent_triangles.json +0 -169
  50. package/decks/demo_deck.json +0 -22
  51. package/decks/eq_28aug2025.json +0 -67
  52. package/decks/goldstandar_eq_28aug25.json +0 -69
  53. package/decks/parallelogram_properties.json +0 -164
  54. package/decks/parallelogram_properties_no_sound.json +0 -164
  55. package/decks/posultate_and_SAS_postulate.json +0 -76
  56. package/decks/qanoon.md +0 -136
  57. package/decks/theorem_revision_ch10_11.fixed.json +0 -265
  58. package/decks/theorem_revision_ch10_11.json +0 -269
  59. package/decks/theorems9old_11.1.1.json +0 -382
  60. package/decks/theorems9old_11.1.2.json +0 -162
  61. package/decks/theorems9old_11.1.3.json +0 -857
  62. package/index.html +0 -88
  63. package/tests/slides.svgPointer.test.js +0 -25
  64. package/tests/slides.test.js +0 -28
package/README.md CHANGED
@@ -1,108 +1,71 @@
1
1
 
2
2
  # πŸ“¦ taleem-slides
3
3
 
4
- ## ⚠️ Warning : Work in Progress β€” expect breaking changes
4
+ > **Simple HTML slide templates for JSON-based presentations**
5
5
 
6
- > **Pure slide template library for Taleem decks**
6
+ `taleem-slides` is a **small, focused library** that turns
7
+ **plain slide JSON** into **HTML**.
7
8
 
8
- `taleem-slides` is a **simple, deterministic template library** that turns
9
- **deck-style slide JSON** into **HTML**.
9
+ You give it slide data.
10
+ It gives you HTML.
10
11
 
11
- It does **one thing only**:
12
+ That’s it.
12
13
 
13
- > **Given slide data + render state β†’ return HTML**
14
-
15
- It does **not** manage time, indexes, playback, or decks.
16
-
17
- ---
18
-
19
- ## 🌐 Live Display Center (Important)
20
-
21
- πŸ‘‰ **Official live display & reference implementation**
22
- **[https://bilza2023.github.io/taleem/](https://bilza2023.github.io/taleem/)**
23
-
24
- This is **not a mock demo**.This link is the **active display center** where:
25
-
26
- - slide templates are rendered in real browsers
27
- - visual behavior is validated
28
- - browser/player integration is tested
29
14
  ---
30
15
 
31
- ## ✨ Taleem.help Philosophy
16
+ ## 🌐 Live Docs, Demo & Reference (START HERE)
32
17
 
33
- **Taleem.help** is an educational technology initiative focused on making
34
- **content-first learning tools**.
18
+ πŸ‘‰ **https://bilza2023.github.io/taleem**
35
19
 
36
- The goal of the `taleem-*` libraries is simple:
20
+ This site is the **official reference** for:
37
21
 
38
- > Enable educators to create **JSON-based presentations**
39
- > and display them online using **free, open tools**.
22
+ - all supported slide types
23
+ - visual behavior
24
+ - real rendered output
25
+ - examples and demos
40
26
 
41
- Key ideas:
42
-
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
47
-
48
- This removal of choice is **intentional**.
49
-
50
- It makes the system:
51
-
52
- * easy to learn
53
- * hard to misuse
54
- * consistent across platforms
55
-
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.
27
+ If something is unclear in this README,
28
+ **the website is the final authority**.
61
29
 
62
30
  ---
63
31
 
64
- ## ✨ What this library is
65
-
66
- * A collection of **slide templates**
67
- * Each template:
32
+ ## βœ… What this library does
68
33
 
69
- * reads slide JSON
70
- * renders HTML
71
- * applies CSS classes based on a given state
72
- * Fully **stateless** and **pure**
73
-
74
- Think of it as:
75
-
76
- > *Handlebars / JSX for Taleem slides*
34
+ - Converts slide JSON into HTML
35
+ - Uses fixed, opinionated layouts
36
+ - Applies simple state-based CSS classes
37
+ - Works in any JS environment (browser, player, SSR)
77
38
 
78
39
  ---
79
40
 
80
- ## ❌ What this library is NOT
41
+ ## ❌ What this library does NOT do
81
42
 
82
43
  `taleem-slides` does **not**:
83
44
 
84
- * build decks
85
- * validate full decks
86
- * manage timing (`showAt`)
87
- * decide which slide is active
88
- * manage playback
89
- * mutate data
45
+ - play slides
46
+ - manage time or animations
47
+ - navigate slides
48
+ - validate full decks
49
+ - touch the DOM
50
+ - apply CSS styles
90
51
 
91
- All of that belongs elsewhere.
52
+ It only returns HTML strings.
92
53
 
93
54
  ---
94
55
 
95
56
  ## 🧠 Mental Model
96
57
 
97
58
  ```
98
- slide JSON + render state
99
- ↓
100
- slide template
101
- ↓
102
- HTML
103
- ```
104
59
 
105
- How the state is calculated is **not this library’s concern**.
60
+ slide data + render state
61
+ ↓
62
+ taleem-slides
63
+ ↓
64
+ HTML
65
+
66
+ ````
67
+
68
+ How slides are shown, timed, or styled is **your responsibility**.
106
69
 
107
70
  ---
108
71
 
@@ -110,77 +73,69 @@ How the state is calculated is **not this library’s concern**.
110
73
 
111
74
  ```bash
112
75
  npm install taleem-slides
113
- ```
76
+ ````
114
77
 
115
78
  ---
116
79
 
117
80
  ## πŸš€ Basic Usage
118
81
 
119
- ### 1️⃣ Import a template
82
+ ### 1️⃣ Get a slide template
120
83
 
121
84
  ```js
122
85
  import { getSlideTemplate } from "taleem-slides";
86
+
87
+ const Slide = getSlideTemplate("bulletList");
123
88
  ```
124
89
 
125
90
  ---
126
91
 
127
- ### 2️⃣ Load slide data (once)
92
+ ### 2️⃣ Load slide data
128
93
 
129
94
  ```js
130
- const SlideTemplate = getSlideTemplate("bulletList");
131
-
132
- const slide = SlideTemplate.fromJSON({
95
+ const slide = Slide.fromJSON({
133
96
  type: "bulletList",
134
97
  data: [
135
98
  { name: "bullet", content: "First point" },
136
- { name: "bullet", content: "Second point" },
137
- { name: "bullet", content: "Third point" }
99
+ { name: "bullet", content: "Second point" }
138
100
  ]
139
101
  });
140
102
  ```
141
103
 
142
- > `fromJSON()` only **reads and stores structure**.
143
- > No timing. No logic.
104
+ `fromJSON()` reads and stores structure only.
144
105
 
145
106
  ---
146
107
 
147
- ### 3️⃣ Render with state
108
+ ### 3️⃣ Render HTML
148
109
 
149
110
  ```js
150
111
  const html = slide.render({
151
- visibleCount: 2,
152
- activeIndex: 1
112
+ visibleCount: 1,
113
+ activeIndex: 0
153
114
  });
154
115
  ```
155
116
 
156
- This will:
157
-
158
- * render first 2 bullets
159
- * highlight the second bullet
160
- * dim the first
161
-
162
117
  ---
163
118
 
164
- ## 🎨 Render State Contract
119
+ ## 🎨 Render State (Simple)
165
120
 
166
- Templates accept a **render state object**.
121
+ Render state is just a plain object.
167
122
 
168
123
  Common fields:
169
124
 
170
125
  ```ts
171
126
  {
172
- visibleCount?: number; // how many items exist
173
- activeIndex?: number; // which item is highlighted
127
+ visibleCount?: number
128
+ activeIndex?: number
174
129
  }
175
130
  ```
176
131
 
177
- Slides may choose to use one or both.
132
+ Each slide uses only what it needs.
178
133
 
179
134
  ---
180
135
 
181
- ## 🎯 Class Name Contract
136
+ ## 🎯 CSS Class Contract
182
137
 
183
- Templates apply **standard class names only**:
138
+ Slides emit only these state classes:
184
139
 
185
140
  ```text
186
141
  .is-active
@@ -188,60 +143,64 @@ Templates apply **standard class names only**:
188
143
  .is-hidden
189
144
  ```
190
145
 
191
- Styling is handled entirely by the consuming app.
146
+ You control **all styling**.
192
147
 
193
148
  ---
194
149
 
195
- ## 🧭 How this fits in the ecosystem
196
-
197
- `taleem-slides` is intentionally **small** and **focused**.
150
+ ## 🧩 Supported Slide Types
198
151
 
199
- It is used by higher-level projects:
152
+ See the **live site** for visuals and examples:
200
153
 
201
- ### 🧩 Sister Projects
154
+ πŸ‘‰ [https://bilza2023.github.io/taleem](https://bilza2023.github.io/taleem)
202
155
 
203
- * **taleem-browser**
204
- Index-based slide viewer (manual navigation)
156
+ Supported categories include:
205
157
 
206
- * **taleem-player**
207
- Time-based slide player (audio / video synced)
158
+ * titles and text slides
159
+ * bullet lists and columns
160
+ * images and image+text layouts
161
+ * tables and charts
162
+ * quotes and stats
163
+ * equation slides
208
164
 
209
- Both projects:
165
+ Layouts are **fixed by design**.
210
166
 
211
- * compute render state (`activeIndex`, `visibleCount`)
212
- * pass it to `taleem-slides`
213
- * receive consistent HTML output
167
+ If you need a new layout, you add a new template.
214
168
 
215
169
  ---
216
170
 
217
- ## πŸ§ͺ Demo & Reference Projects
171
+ ## πŸ§ͺ Stability & Guarantees
218
172
 
219
- * 🌐 **Live Display Center**
220
- [https://bilza2023.github.io/taleem/](https://bilza2023.github.io/taleem/)
221
-
222
- * πŸ“ **GitHub Demo / Playground**
223
- *(link can be added here when ready)*
173
+ * Deterministic output
174
+ * No hidden state
175
+ * No configuration knobs
176
+ * Same input β†’ same HTML
224
177
 
225
178
  ---
226
179
 
227
- ## 🧊 Stability & Versioning
180
+ ## πŸ”’ Design Principle
228
181
 
229
- * Targets **deck-v1**
230
- * Breaking changes allowed during WIP phase
231
- * HTML output is intentionally simple and predictable
182
+ > **This library renders HTML only.
183
+ > It does not decide when or how slides appear.**
232
184
 
233
185
  ---
234
186
 
235
- ## 🧠 Design Principle (Locked)
187
+ ## πŸ“ Where this fits
188
+
189
+ `taleem-slides` is meant to be used by:
190
+
191
+ * slide viewers
192
+ * presentation players
193
+ * educational tools
194
+ * static or dynamic renderers
236
195
 
237
- > **taleem-slides renders HTML.
238
- > It does not decide *when* or *why*.**
196
+ It is intentionally small so it can be reused everywhere.
239
197
 
240
198
  ---
241
199
 
242
- If you want, next logical steps are:
200
+ ## βœ… Status
243
201
 
244
- * rewrite one slide as the **canonical reference**
245
- * or update taleem-browser to consume the new API cleanly
202
+ * Actively used
203
+ * Fully tested
204
+ * Backed by live reference site
205
+ * Ready for production
246
206
 
247
- This README now correctly **anchors the entire ecosystem**.
package/package.json CHANGED
@@ -1,22 +1,23 @@
1
1
  {
2
2
  "name": "taleem-slides",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "description": "Convert json taleem schema into html for slides",
6
-
7
6
  "exports": {
8
7
  ".": "./src/index.js"
9
8
  },
10
-
11
9
  "scripts": {
12
10
  "test": "vitest run",
13
11
  "test:watch": "vitest",
14
12
  "test:ui": "vitest --ui",
15
13
  "lint": "echo \"no lint yet\""
16
14
  },
17
-
18
15
  "devDependencies": {
19
16
  "vite": "^5.0.0",
20
17
  "vitest": "^1.5.0"
18
+ },
19
+ "dependencies": {
20
+ "taleem-core": "^1.3.2",
21
+ "zod": "^4.3.5"
21
22
  }
22
23
  }
@@ -53,5 +53,4 @@ export const SlideTemplates = {
53
53
  contactSlide: ContactSlide,
54
54
 
55
55
  eq: EqSlide,
56
- svgPointer: SvgPointerSlide
57
56
  };
package/src/index.js CHANGED
@@ -2,3 +2,4 @@
2
2
 
3
3
  export { SlideTemplates } from "./SlideTemplates.js";
4
4
  export { getSlideTemplate } from "./getSlideTemplate.js";
5
+ export { resolveBackground } from "./resolveBackground.js";
@@ -0,0 +1,10 @@
1
+ export function resolveBackground({ deckBackground, theme }) {
2
+ if (deckBackground) return deckBackground;
3
+
4
+ return {
5
+ backgroundColor: theme.surfaceColor,
6
+ backgroundImage: null,
7
+ backgroundImageOpacity: 1
8
+ };
9
+ }
10
+
@@ -1,16 +1,16 @@
1
- // BarChartSlide.js
2
1
  export const BarChartSlide = {
3
2
  type: "barChart",
4
3
 
5
4
  fromJSON(raw) {
5
+ // Consume canonical deck-v1 shape directly
6
6
  const bars = raw.data
7
7
  ?.filter(d => d.name === "bar")
8
8
  .map(d => ({
9
- label: d.content.label,
10
- value: d.content.value
9
+ label: d.label,
10
+ value: d.value
11
11
  }));
12
12
 
13
- if (!bars?.length) {
13
+ if (!bars || bars.length === 0) {
14
14
  throw new Error("barChart: requires at least one bar");
15
15
  }
16
16
 
@@ -27,7 +27,7 @@ export const BarChartSlide = {
27
27
  const cls =
28
28
  i === activeIndex
29
29
  ? "is-active"
30
- : i < activeIndex
30
+ : activeIndex !== null && i < activeIndex
31
31
  ? "is-dim"
32
32
  : "";
33
33
  return `
@@ -1,22 +1,23 @@
1
- // BigNumberSlide.js
2
1
  export const BigNumberSlide = {
3
2
  type: "bigNumber",
4
3
 
5
4
  fromJSON(raw) {
6
- const value = raw.data?.find(d => d.name === "number")?.content;
7
- const label = raw.data?.find(d => d.name === "label")?.content;
5
+ const number = raw.data?.find(d => d.name === "number")?.content;
6
+ const label = raw.data?.find(d => d.name === "label")?.content;
8
7
 
9
- if (!value) throw new Error("bigNumber: number required");
8
+ if (!number) {
9
+ throw new Error("bigNumber: number required");
10
+ }
10
11
 
11
12
  return Object.freeze({
12
13
  type: "bigNumber",
13
- value,
14
+ number,
14
15
  label,
15
16
 
16
17
  render() {
17
18
  return `
18
19
  <section class="slide bigNumber">
19
- <div class="number">${value}</div>
20
+ <div class="number">${number}</div>
20
21
  ${label ? `<div class="label">${label}</div>` : ""}
21
22
  </section>
22
23
  `;
@@ -1,13 +1,12 @@
1
- // BulletListSlide.js
2
1
  export const BulletListSlide = {
3
2
  type: "bulletList",
4
3
 
5
4
  fromJSON(raw) {
6
5
  const bullets = raw.data
7
6
  ?.filter(d => d.name === "bullet")
8
- .map(d => ({ content: d.content }));
7
+ .map(d => d.content);
9
8
 
10
- if (!bullets?.length) {
9
+ if (!bullets || bullets.length === 0) {
11
10
  throw new Error("bulletList: requires at least one bullet");
12
11
  }
13
12
 
@@ -19,15 +18,15 @@ export const BulletListSlide = {
19
18
  return `
20
19
  <section class="slide bulletList">
21
20
  <ul>
22
- ${bullets.map((b, i) => {
21
+ ${bullets.map((text, i) => {
23
22
  if (i >= visibleCount) return "";
24
23
  const cls =
25
24
  i === activeIndex
26
25
  ? "is-active"
27
- : i < activeIndex
26
+ : activeIndex !== null && i < activeIndex
28
27
  ? "is-dim"
29
28
  : "";
30
- return `<li class="${cls}">${b.content}</li>`;
29
+ return `<li class="${cls}">${text}</li>`;
31
30
  }).join("")}
32
31
  </ul>
33
32
  </section>
@@ -1,11 +1,10 @@
1
- // ContactSlide.js
2
1
  export const ContactSlide = {
3
2
  type: "contactSlide",
4
3
 
5
4
  fromJSON(raw) {
6
- const items = raw.data?.map(d => ({ content: d.content }));
5
+ const items = raw.data?.map(d => d.content);
7
6
 
8
- if (!items?.length) {
7
+ if (!items || items.length === 0) {
9
8
  throw new Error("contactSlide: content required");
10
9
  }
11
10
 
@@ -16,7 +15,7 @@ export const ContactSlide = {
16
15
  render() {
17
16
  return `
18
17
  <section class="slide contactSlide">
19
- ${items.map(i => `<div>${i.content}</div>`).join("")}
18
+ ${items.map(text => `<div>${text}</div>`).join("")}
20
19
  </section>
21
20
  `;
22
21
  }
@@ -1,26 +1,31 @@
1
- // CornerWordsSlide.js
1
+
2
2
  export const CornerWordsSlide = {
3
3
  type: "cornerWordsSlide",
4
4
 
5
5
  fromJSON(raw) {
6
- const words = raw.data
7
- ?.filter(d => d.name === "word")
8
- .map(d => ({ content: d.content }));
6
+ const cards = raw.data
7
+ ?.filter(d => d.name === "card")
8
+ .map(d => ({ icon: d.icon, label: d.label }));
9
9
 
10
- if (!words?.length) {
11
- throw new Error("cornerWordsSlide: requires at least one word");
10
+ if (!cards || cards.length === 0) {
11
+ throw new Error("cornerWordsSlide: requires at least one card");
12
12
  }
13
13
 
14
14
  return Object.freeze({
15
15
  type: "cornerWordsSlide",
16
- words,
16
+ cards,
17
17
 
18
- render({ visibleCount = words.length } = {}) {
18
+ render({ visibleCount = cards.length } = {}) {
19
19
  return `
20
20
  <section class="slide cornerWordsSlide">
21
- ${words.map((w, i) => {
21
+ ${cards.map((c, i) => {
22
22
  if (i >= visibleCount) return "";
23
- return `<span class="corner-word corner-${i + 1}">${w.content}</span>`;
23
+ return `
24
+ <span class="corner-card corner-${i + 1}">
25
+ <span class="icon">${c.icon}</span>
26
+ <span class="label">${c.label}</span>
27
+ </span>
28
+ `;
24
29
  }).join("")}
25
30
  </section>
26
31
  `;
@@ -1,16 +1,22 @@
1
- // DonutChartSlide.js
2
1
  export const DonutChartSlide = {
3
2
  type: "donutChart",
4
3
 
5
4
  fromJSON(raw) {
6
- const segments = raw.data
7
- ?.filter(d => d.name === "segment")
8
- .map(d => ({
9
- label: d.content.label,
10
- value: d.content.value
11
- }));
5
+ const segments = [];
6
+ let current = null;
12
7
 
13
- if (!segments?.length) {
8
+ for (const d of raw.data || []) {
9
+ if (d.name === "percent") {
10
+ current = { percent: d.content };
11
+ segments.push(current);
12
+ } else if (current && d.name === "label") {
13
+ current.label = d.content;
14
+ } else if (current && d.name === "color") {
15
+ current.color = d.content;
16
+ }
17
+ }
18
+
19
+ if (!segments.length) {
14
20
  throw new Error("donutChart: requires at least one segment");
15
21
  }
16
22
 
@@ -18,19 +24,13 @@ export const DonutChartSlide = {
18
24
  type: "donutChart",
19
25
  segments,
20
26
 
21
- render({ visibleCount = segments.length, activeIndex = null } = {}) {
27
+ render({ visibleCount = segments.length } = {}) {
22
28
  return `
23
29
  <section class="slide donutChart">
24
30
  <ul>
25
31
  ${segments.map((s, i) => {
26
32
  if (i >= visibleCount) return "";
27
- const cls =
28
- i === activeIndex
29
- ? "is-active"
30
- : i < activeIndex
31
- ? "is-dim"
32
- : "";
33
- return `<li class="${cls}">${s.label}: ${s.value}</li>`;
33
+ return `<li>${s.label}: ${s.percent}%</li>`;
34
34
  }).join("")}
35
35
  </ul>
36
36
  </section>
@@ -1,28 +1,32 @@
1
- // EqSlide.js
1
+
2
2
  export const EqSlide = {
3
3
  type: "eq",
4
4
 
5
5
  fromJSON(raw) {
6
- if (!Array.isArray(raw.data)) {
7
- throw new Error("eq: data must be array");
8
- }
6
+ const lines = raw.data
7
+ ?.filter(d => d.name === "line")
8
+ .map(d => ({
9
+ type: d.type,
10
+ content: d.content
11
+ }));
9
12
 
10
- const lines = raw.data.map(d => ({
11
- content: d.content
12
- }));
13
+ if (!lines || lines.length === 0) {
14
+ throw new Error("eq: requires at least one line");
15
+ }
13
16
 
14
17
  return Object.freeze({
15
18
  type: "eq",
16
19
  lines,
17
20
 
18
- render({ activeIndex = null } = {}) {
21
+ render({ visibleCount = lines.length, activeIndex = null } = {}) {
19
22
  return `
20
23
  <section class="slide eq">
21
24
  ${lines.map((l, i) => {
25
+ if (i >= visibleCount) return "";
22
26
  const cls =
23
27
  i === activeIndex
24
28
  ? "is-active"
25
- : i < activeIndex
29
+ : activeIndex !== null && i < activeIndex
26
30
  ? "is-dim"
27
31
  : "";
28
32
  return `<div class="eq-line ${cls}">${l.content}</div>`;
@@ -1,10 +1,12 @@
1
- // FillImageSlide.js
2
1
  export const FillImageSlide = {
3
2
  type: "fillImage",
4
3
 
5
4
  fromJSON(raw) {
6
5
  const image = raw.data?.find(d => d.name === "image")?.content;
7
- if (!image) throw new Error("fillImage: image required");
6
+
7
+ if (!image) {
8
+ throw new Error("fillImage: image required");
9
+ }
8
10
 
9
11
  return Object.freeze({
10
12
  type: "fillImage",