taleem-player 0.6.0-rc.1 β 0.7.1-rc
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 +155 -125
- package/dist/css/taleem.css +347 -0
- package/dist/css/themes/dark.css +6 -0
- package/dist/css/themes/light.css +6 -0
- package/dist/css/themes/paper.css +6 -0
- package/dist/taleem-player.esm.js +120 -11
- package/dist/taleem-player.umd.js +120 -11
- package/package.json +11 -7
package/README.md
CHANGED
|
@@ -1,215 +1,245 @@
|
|
|
1
1
|
|
|
2
|
-
#
|
|
2
|
+
# Taleem Player
|
|
3
3
|
|
|
4
|
-
**taleem-player** is a **time-driven player** for **Taleem slides**.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
<img src="./docs/images/taleem.webp" alt="Taleem Player β JSON to Web Presentations" />
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
**Taleem Player** is a JavaScript library that converts **JSON slide data** into **web-based presentations**.
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
* timed presentations
|
|
12
|
-
* audio / videoβsynced slides
|
|
13
|
-
* external playback control
|
|
9
|
+
It provides multiple **modes** to display the same JSON presentation in different ways on the web.
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
###### Work in progress. Expect minor bugs, but no API breakages.
|
|
12
|
+
---
|
|
16
13
|
|
|
17
|
-
|
|
14
|
+
Demo and Documentation - See Taleem Player in action:
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
It does **not** fix data.
|
|
21
|
-
It does **not** manage time.
|
|
16
|
+
π https://bilza2023.github.io/taleem/
|
|
22
17
|
|
|
23
|
-
|
|
18
|
+
This live demo lets you explore:
|
|
24
19
|
|
|
25
|
-
|
|
20
|
+
- Browser Mode β instant, index-based slide rendering
|
|
26
21
|
|
|
27
|
-
|
|
22
|
+
- Player Mode β time-driven, progressive presentations
|
|
28
23
|
|
|
29
|
-
|
|
24
|
+
- Real Taleem slide JSON used in production
|
|
30
25
|
|
|
31
|
-
|
|
26
|
+
- Shared CSS system powering all display modes
|
|
32
27
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
**No screenshots. No mock data.What you see is the real engine running in the browser.**
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## What it does
|
|
37
32
|
|
|
38
|
-
|
|
33
|
+
* Takes **Taleem slide JSON**
|
|
34
|
+
* Renders it as a **web presentation**
|
|
35
|
+
* Supports **multiple display modes**
|
|
36
|
+
* Ships **ready-to-use CSS**
|
|
37
|
+
* Includes **utilities** for real-world usage
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
---
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install taleem-player
|
|
45
|
+
```
|
|
44
46
|
|
|
45
47
|
---
|
|
46
48
|
|
|
47
|
-
##
|
|
49
|
+
## Display Modes
|
|
48
50
|
|
|
49
|
-
|
|
51
|
+
### 1. Browser Mode
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
**Index-based slide viewer**.
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
Use this when you want:
|
|
56
|
+
|
|
57
|
+
* manual navigation
|
|
58
|
+
* previews
|
|
59
|
+
* galleries
|
|
60
|
+
* syllabus / editor views
|
|
61
|
+
|
|
62
|
+
#### API
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
import { createTaleemBrowser } from "taleem-player";
|
|
56
66
|
|
|
57
|
-
|
|
67
|
+
const browser = createTaleemBrowser({
|
|
68
|
+
mount: "#app",
|
|
69
|
+
deck
|
|
70
|
+
});
|
|
58
71
|
|
|
59
|
-
|
|
60
|
-
|
|
72
|
+
browser.render(0); // render slide by index
|
|
73
|
+
browser.getTotal(); // total number of slides
|
|
74
|
+
```
|
|
61
75
|
|
|
62
|
-
|
|
76
|
+
Characteristics:
|
|
63
77
|
|
|
64
|
-
|
|
78
|
+
* slide index driven
|
|
79
|
+
* no timing
|
|
80
|
+
* deterministic rendering
|
|
81
|
+
* same slide JSON as other modes
|
|
65
82
|
|
|
66
83
|
---
|
|
67
84
|
|
|
68
|
-
|
|
85
|
+
### 2. Player Mode
|
|
69
86
|
|
|
70
|
-
|
|
87
|
+
**Time-based slide player**.
|
|
71
88
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* Exposes a **minimal API**
|
|
89
|
+
Use this when you want:
|
|
90
|
+
|
|
91
|
+
* narrated lessons
|
|
92
|
+
* video / audio sync
|
|
93
|
+
* timed presentations
|
|
78
94
|
|
|
79
|
-
|
|
95
|
+
#### API
|
|
80
96
|
|
|
81
97
|
```js
|
|
82
|
-
player
|
|
83
|
-
player.destroy()
|
|
84
|
-
```
|
|
98
|
+
import { createTaleemPlayer } from "taleem-player";
|
|
85
99
|
|
|
86
|
-
|
|
100
|
+
const player = createTaleemPlayer({
|
|
101
|
+
mount: "#app",
|
|
102
|
+
});
|
|
87
103
|
|
|
88
|
-
|
|
104
|
+
player.renderAt(12.5); // render slide at time (seconds)
|
|
105
|
+
player.destroy();
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Characteristics:
|
|
89
109
|
|
|
90
|
-
|
|
110
|
+
* time driven
|
|
111
|
+
* external clock control
|
|
112
|
+
* no play / pause logic
|
|
113
|
+
* one active slide at a time
|
|
91
114
|
|
|
92
|
-
|
|
115
|
+
---
|
|
116
|
+
## Browser Mode vs Player Mode
|
|
93
117
|
|
|
94
|
-
|
|
95
|
-
* auto-generate timings
|
|
96
|
-
* fix broken decks
|
|
97
|
-
* define slide layouts
|
|
98
|
-
* ship CSS
|
|
99
|
-
* manage audio or narration
|
|
100
|
-
* play / pause / autoplay
|
|
101
|
-
* own clocks or intervals
|
|
118
|
+
Both modes render the same JSON presentation, but they serve very different purposes.
|
|
102
119
|
|
|
103
|
-
|
|
104
|
-
|
|
120
|
+
| Feature | Browser Mode | Player Mode |
|
|
121
|
+
|-------|-------------|-------------|
|
|
122
|
+
| Rendering model | Index-based | Time-based |
|
|
123
|
+
| Navigation | Manual (by slide index) | Progressive (by time) |
|
|
124
|
+
| Timing required | No | Yes (required) |
|
|
125
|
+
| Rendering behavior | One slide at a time | Slides change over time |
|
|
126
|
+
| Control source | Application-driven | External clock / media |
|
|
127
|
+
| Best suited for | Previews, galleries, editors | Narration, video, audio sync |
|
|
105
128
|
|
|
106
129
|
---
|
|
107
130
|
|
|
108
|
-
|
|
131
|
+
### Browser Mode
|
|
109
132
|
|
|
110
|
-
|
|
133
|
+
Use Browser Mode when you want direct access to slides.
|
|
111
134
|
|
|
112
|
-
|
|
135
|
+
**Characteristics**
|
|
136
|
+
- Index-based rendering
|
|
137
|
+
- No timing data required
|
|
138
|
+
- Deterministic output
|
|
113
139
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
```
|
|
140
|
+
**Ideal for**
|
|
141
|
+
- previews
|
|
142
|
+
- galleries
|
|
143
|
+
- editors
|
|
144
|
+
- syllabus pages
|
|
120
145
|
|
|
121
|
-
Rules:
|
|
122
146
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
* timings must be monotonic
|
|
127
|
-
* no auto-injection
|
|
128
|
-
* no browser forgiveness
|
|
129
|
-
|
|
130
|
-
Deck preparation happens **before** the player
|
|
131
|
-
(e.g. via tooling like `assignMockTimings`).
|
|
147
|
+
β οΈ Important:
|
|
148
|
+
In Player Mode, the user must provide valid and ordered timings (start, end).
|
|
149
|
+
The library does not auto-fix or guess timings.
|
|
132
150
|
|
|
133
151
|
---
|
|
134
152
|
|
|
135
|
-
##
|
|
153
|
+
## Utilities
|
|
136
154
|
|
|
137
|
-
|
|
155
|
+
Taleem Player includes small helper utilities for preparing decks.
|
|
138
156
|
|
|
139
|
-
|
|
157
|
+
### assignMockTimings
|
|
140
158
|
|
|
141
|
-
|
|
142
|
-
2. Computes minimal render state
|
|
143
|
-
3. Uses `taleem-slides` to generate HTML
|
|
144
|
-
4. Injects HTML into a single stage
|
|
159
|
+
Convert a non-timed deck into a player-ready deck.
|
|
145
160
|
|
|
146
|
-
|
|
161
|
+
```js
|
|
162
|
+
import { assignMockTimings } from "taleem-player";
|
|
163
|
+
|
|
164
|
+
const timedDeck = assignMockTimings(deck, 5);
|
|
165
|
+
```
|
|
147
166
|
|
|
148
167
|
---
|
|
149
168
|
|
|
150
|
-
|
|
169
|
+
### resolveAssetPaths
|
|
151
170
|
|
|
152
|
-
|
|
171
|
+
Resolve image paths for deployment.
|
|
153
172
|
|
|
154
|
-
|
|
173
|
+
```js
|
|
174
|
+
import { resolveAssetPaths } from "taleem-player";
|
|
155
175
|
|
|
156
|
-
|
|
176
|
+
resolveAssetPaths(deck, "/images/");
|
|
177
|
+
```
|
|
157
178
|
|
|
158
|
-
|
|
179
|
+
---
|
|
159
180
|
|
|
160
|
-
|
|
181
|
+
### resolveBackground
|
|
161
182
|
|
|
162
|
-
|
|
183
|
+
Normalize and resolve background configuration.
|
|
163
184
|
|
|
164
|
-
|
|
185
|
+
```js
|
|
186
|
+
import { resolveBackground } from "taleem-player";
|
|
165
187
|
|
|
166
|
-
|
|
188
|
+
resolveBackground(deck, "/images/");
|
|
189
|
+
```
|
|
167
190
|
|
|
168
|
-
|
|
191
|
+
---
|
|
169
192
|
|
|
170
|
-
|
|
171
|
-
They are composed, not mixed.
|
|
193
|
+
## CSS
|
|
172
194
|
|
|
173
|
-
|
|
195
|
+
Taleem Player ships with built-in styles.
|
|
174
196
|
|
|
175
|
-
|
|
197
|
+
### Base styles
|
|
176
198
|
|
|
177
199
|
```js
|
|
178
|
-
import
|
|
200
|
+
import "taleem-player/css";
|
|
201
|
+
```
|
|
179
202
|
|
|
180
|
-
|
|
181
|
-
mount: "#app",
|
|
182
|
-
deck // must be player-ready
|
|
183
|
-
});
|
|
203
|
+
### Themes
|
|
184
204
|
|
|
185
|
-
|
|
186
|
-
player
|
|
205
|
+
```js
|
|
206
|
+
import "taleem-player/css/dark";
|
|
207
|
+
import "taleem-player/css/light";
|
|
208
|
+
import "taleem-player/css/paper";
|
|
187
209
|
```
|
|
188
210
|
|
|
189
|
-
|
|
190
|
-
|
|
211
|
+
CSS controls layout, visibility, and visual behavior.
|
|
212
|
+
Modes share the same CSS.
|
|
191
213
|
|
|
192
214
|
---
|
|
193
215
|
|
|
194
|
-
##
|
|
216
|
+
## Input format
|
|
217
|
+
|
|
218
|
+
Taleem Player does **not** define slide structure.
|
|
195
219
|
|
|
196
|
-
|
|
197
|
-
* Slides are deterministic
|
|
198
|
-
* Seconds are enough
|
|
199
|
-
* Libraries stay small
|
|
200
|
-
* Control lives at the top
|
|
220
|
+
It renders JSON produced for **taleem-slides**.
|
|
201
221
|
|
|
202
|
-
|
|
203
|
-
|
|
222
|
+
* Player Mode requires slides with `start` / `end`
|
|
223
|
+
* Browser Mode only needs ordered slides
|
|
204
224
|
|
|
205
225
|
---
|
|
206
226
|
|
|
207
|
-
##
|
|
227
|
+
## What Taleem Player does NOT do
|
|
228
|
+
|
|
229
|
+
* create slides
|
|
230
|
+
* edit JSON
|
|
231
|
+
* validate schemas
|
|
232
|
+
* manage time or playback
|
|
233
|
+
* handle audio or narration
|
|
234
|
+
* provide UI controls
|
|
235
|
+
|
|
236
|
+
Those belong to the **application**, not the library.
|
|
237
|
+
|
|
238
|
+
---
|
|
208
239
|
|
|
209
|
-
|
|
240
|
+
## Status
|
|
210
241
|
|
|
211
|
-
|
|
212
|
-
No new concepts belong here.
|
|
242
|
+
**Release Candidate (API stable)**
|
|
213
243
|
|
|
214
244
|
---
|
|
215
245
|
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
|
|
2
|
+
/* =====================================================
|
|
3
|
+
TALEEM β Single Public Stylesheet
|
|
4
|
+
|
|
5
|
+
/* -------------------------------
|
|
6
|
+
Global Page Reset
|
|
7
|
+
------------------------------- */
|
|
8
|
+
|
|
9
|
+
html,body {
|
|
10
|
+
height: 100%;
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
margin: 0;
|
|
13
|
+
font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
|
|
14
|
+
background: var(--backgroundColor, #0b1220);
|
|
15
|
+
color: var(--primaryColor, #e6e9ff);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#app {
|
|
19
|
+
position: relative;
|
|
20
|
+
width: 100vw;
|
|
21
|
+
min-height: 100vh;
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* -------------------------------
|
|
26
|
+
Browser / Player Layers
|
|
27
|
+
------------------------------- */
|
|
28
|
+
.taleem-browser-bg {
|
|
29
|
+
position: absolute;
|
|
30
|
+
inset: 0;
|
|
31
|
+
z-index: 0;
|
|
32
|
+
background-size: cover;
|
|
33
|
+
background-position: center;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.taleem-browser-slide {
|
|
37
|
+
position: relative;
|
|
38
|
+
z-index: 1;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* -------------------------------
|
|
42
|
+
Base Slide Grammar
|
|
43
|
+
------------------------------- */
|
|
44
|
+
.slide {
|
|
45
|
+
height: 100vh;
|
|
46
|
+
box-sizing: border-box;
|
|
47
|
+
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
justify-content: center;
|
|
51
|
+
align-items: center;
|
|
52
|
+
|
|
53
|
+
padding: 64px 80px;
|
|
54
|
+
gap: 32px;
|
|
55
|
+
|
|
56
|
+
overflow: hidden;
|
|
57
|
+
background: transparent;
|
|
58
|
+
color: var(--primaryColor, #e6e9ff);
|
|
59
|
+
|
|
60
|
+
font-size: 2.4rem;
|
|
61
|
+
line-height: 1.6;
|
|
62
|
+
letter-spacing: 0.01em;
|
|
63
|
+
text-align: center;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* -------------------------------
|
|
67
|
+
Global Image Safety
|
|
68
|
+
------------------------------- */
|
|
69
|
+
.slide img {
|
|
70
|
+
max-width: 100%;
|
|
71
|
+
max-height: 100%;
|
|
72
|
+
height: auto;
|
|
73
|
+
width: auto;
|
|
74
|
+
display: block;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* -------------------------------
|
|
78
|
+
Headings
|
|
79
|
+
------------------------------- */
|
|
80
|
+
.slide h1 {
|
|
81
|
+
margin: 0;
|
|
82
|
+
letter-spacing: -0.015em;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* -------------------------------
|
|
86
|
+
Slide Types
|
|
87
|
+
------------------------------- */
|
|
88
|
+
.slide.titleSlide h1 {
|
|
89
|
+
font-size: 5.6rem;
|
|
90
|
+
font-weight: 700;
|
|
91
|
+
line-height: 1.2;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.slide.titleAndSubtitle h1 {
|
|
95
|
+
font-size: 5.8rem;
|
|
96
|
+
font-weight: 700;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.slide.titleAndSubtitle h2 {
|
|
100
|
+
font-size: 3.8rem;
|
|
101
|
+
font-weight: 400;
|
|
102
|
+
opacity: 0.8;
|
|
103
|
+
margin: 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.slide.titleAndPara h1 {
|
|
107
|
+
font-size: 4.8rem;
|
|
108
|
+
font-weight: 600;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.slide.titleAndPara p {
|
|
112
|
+
font-size: 3rem;
|
|
113
|
+
max-width: 70ch;
|
|
114
|
+
opacity: 0.9;
|
|
115
|
+
margin: 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* -------------------------------
|
|
119
|
+
Bullet List
|
|
120
|
+
------------------------------- */
|
|
121
|
+
.slide.bulletList ul {
|
|
122
|
+
list-style: disc;
|
|
123
|
+
padding-left: 2rem;
|
|
124
|
+
margin: 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.slide.bulletList li {
|
|
128
|
+
font-size: 3.6rem;
|
|
129
|
+
margin-bottom: 1rem;
|
|
130
|
+
text-align: left;
|
|
131
|
+
font-weight: 500;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* -------------------------------
|
|
135
|
+
Image Variants
|
|
136
|
+
------------------------------- */
|
|
137
|
+
.slide.imageSlide {
|
|
138
|
+
padding: 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.slide.imageSlide img {
|
|
142
|
+
object-fit: contain;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.slide.imageWithTitle {
|
|
146
|
+
position: relative;
|
|
147
|
+
padding: 48px;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.slide.imageWithTitle img {
|
|
151
|
+
height: calc(100vh - 96px);
|
|
152
|
+
border-radius: 12px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.slide.imageWithTitle h1 {
|
|
156
|
+
position: absolute;
|
|
157
|
+
bottom: 64px;
|
|
158
|
+
left: 50%;
|
|
159
|
+
transform: translateX(-50%);
|
|
160
|
+
font-size: 4.6rem;
|
|
161
|
+
background: rgba(0, 0, 0, 0.45);
|
|
162
|
+
padding: 0.4em 0.8em;
|
|
163
|
+
border-radius: 6px;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.slide.imageWithCaption {
|
|
167
|
+
display: flex;
|
|
168
|
+
flex-direction: column;
|
|
169
|
+
justify-content: center;
|
|
170
|
+
gap: 32px;
|
|
171
|
+
height: 100vh;
|
|
172
|
+
overflow: hidden;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.slide.imageWithCaption img {
|
|
176
|
+
max-height: 55vh; /* reduce slightly */
|
|
177
|
+
object-fit: contain;
|
|
178
|
+
flex-shrink: 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.slide.imageWithCaption p {
|
|
182
|
+
max-height: 20vh; /* hard cap text */
|
|
183
|
+
overflow: hidden;
|
|
184
|
+
text-overflow: ellipsis;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* -------------------------------
|
|
188
|
+
Two Column Text
|
|
189
|
+
------------------------------- */
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
.slide.twoColumnText {
|
|
193
|
+
flex-direction: row;
|
|
194
|
+
justify-content: center; /* center the columns block */
|
|
195
|
+
align-items: center;
|
|
196
|
+
gap: 64px;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.slide.twoColumnText > div {
|
|
200
|
+
flex: 1;
|
|
201
|
+
max-width: 520px; /* keeps columns visually centered */
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
/* QUOTE SLIDE */
|
|
206
|
+
.slide.quoteSlide {
|
|
207
|
+
display: flex;
|
|
208
|
+
flex-direction: column;
|
|
209
|
+
justify-content: center;
|
|
210
|
+
gap: 32px;
|
|
211
|
+
height: 100vh;
|
|
212
|
+
overflow: hidden;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.slide.quoteSlide blockquote {
|
|
216
|
+
max-height: 60vh;
|
|
217
|
+
overflow: hidden;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.slide.quoteSlide cite {
|
|
221
|
+
flex-shrink: 0;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* -------------------------------
|
|
225
|
+
Quote Slide
|
|
226
|
+
------------------------------- */
|
|
227
|
+
.slide.quoteSlide blockquote {
|
|
228
|
+
font-size: 3.8rem;
|
|
229
|
+
font-style: italic;
|
|
230
|
+
max-width: 60ch;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.slide.quoteSlide cite {
|
|
234
|
+
font-size: 2.4rem;
|
|
235
|
+
opacity: 0.75;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* -------------------------------
|
|
239
|
+
Big Number
|
|
240
|
+
------------------------------- */
|
|
241
|
+
.slide.bigNumber .number {
|
|
242
|
+
font-size: 9rem;
|
|
243
|
+
font-weight: 700;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* -------------------------------
|
|
247
|
+
Fill Image (Full Bleed)
|
|
248
|
+
------------------------------- */
|
|
249
|
+
.slide.fillImage {
|
|
250
|
+
padding: 0;
|
|
251
|
+
height: 100vh;
|
|
252
|
+
display: block;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.slide.fillImage img {
|
|
256
|
+
width: 100%;
|
|
257
|
+
height: 100%;
|
|
258
|
+
object-fit: cover;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/* -------------------------------
|
|
262
|
+
Demo / Player UI (Optional)
|
|
263
|
+
------------------------------- */
|
|
264
|
+
#timebar {
|
|
265
|
+
position: fixed;
|
|
266
|
+
bottom: 0;
|
|
267
|
+
left: 0;
|
|
268
|
+
right: 0;
|
|
269
|
+
height: 32px;
|
|
270
|
+
background: rgba(0, 0, 0, 0.6);
|
|
271
|
+
backdrop-filter: blur(6px);
|
|
272
|
+
z-index: 10;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
/* ///////fixes */
|
|
278
|
+
|
|
279
|
+
/* -------------------------------
|
|
280
|
+
Image + Bullets (Side by Side)
|
|
281
|
+
------------------------------- */
|
|
282
|
+
|
|
283
|
+
.slide.imageRightBulletsLeft,
|
|
284
|
+
.slide.imageLeftBulletsRight {
|
|
285
|
+
display: flex;
|
|
286
|
+
flex-direction: row;
|
|
287
|
+
align-items: center;
|
|
288
|
+
gap: 64px;
|
|
289
|
+
text-align: left;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* image on right */
|
|
293
|
+
.slide.imageRightBulletsLeft img {
|
|
294
|
+
order: 2;
|
|
295
|
+
}
|
|
296
|
+
/* -------------------------------
|
|
297
|
+
Table Slide
|
|
298
|
+
------------------------------- */
|
|
299
|
+
|
|
300
|
+
.slide.table {
|
|
301
|
+
display: flex;
|
|
302
|
+
justify-content: center;
|
|
303
|
+
align-items: center;
|
|
304
|
+
|
|
305
|
+
width: 100%;
|
|
306
|
+
height: 100vh;
|
|
307
|
+
|
|
308
|
+
padding: 64px 80px;
|
|
309
|
+
box-sizing: border-box;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* actual table */
|
|
313
|
+
.slide.table table {
|
|
314
|
+
border-collapse: collapse;
|
|
315
|
+
width: 100%;
|
|
316
|
+
max-width: 1200px;
|
|
317
|
+
|
|
318
|
+
font-size: 2.8rem;
|
|
319
|
+
line-height: 1.4;
|
|
320
|
+
text-align: center;
|
|
321
|
+
|
|
322
|
+
color: var(--primaryColor, #e6e9ff);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/* header cells (title row) */
|
|
326
|
+
.slide.table thead th {
|
|
327
|
+
font-weight: 700; /* title row bold */
|
|
328
|
+
padding: 20px 24px;
|
|
329
|
+
|
|
330
|
+
border: 2px solid rgba(255, 255, 255, 0.35);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* body cells */
|
|
334
|
+
.slide.table tbody td {
|
|
335
|
+
padding: 20px 24px;
|
|
336
|
+
|
|
337
|
+
border: 2px solid rgba(255, 255, 255, 0.25);
|
|
338
|
+
}
|
|
339
|
+
/* hide empty table header row */
|
|
340
|
+
.slide.table thead th:empty {
|
|
341
|
+
display: none;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.slide.table thead tr:has(th:empty) {
|
|
345
|
+
display: none;
|
|
346
|
+
}
|
|
347
|
+
|
|
@@ -274,22 +274,19 @@ var ImageRightBulletsLeftSlide = {
|
|
|
274
274
|
var TableSlide = {
|
|
275
275
|
type: "table",
|
|
276
276
|
fromJSON(raw) {
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
const rows = raw.data;
|
|
278
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
279
|
+
console.warn("Empty table skipped", raw);
|
|
280
|
+
return null;
|
|
281
281
|
}
|
|
282
282
|
return Object.freeze({
|
|
283
283
|
type: "table",
|
|
284
284
|
render() {
|
|
285
285
|
return `
|
|
286
286
|
<table class="slide table">
|
|
287
|
-
<thead>
|
|
288
|
-
<tr>${headers.map((h) => `<th>${h}</th>`).join("")}</tr>
|
|
289
|
-
</thead>
|
|
290
287
|
<tbody>
|
|
291
288
|
${rows.map(
|
|
292
|
-
(
|
|
289
|
+
(row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join("")}</tr>`
|
|
293
290
|
).join("")}
|
|
294
291
|
</tbody>
|
|
295
292
|
</table>
|
|
@@ -593,7 +590,7 @@ function getSlideTemplate(type) {
|
|
|
593
590
|
return template;
|
|
594
591
|
}
|
|
595
592
|
|
|
596
|
-
// src/
|
|
593
|
+
// src/engines/player/stage.js
|
|
597
594
|
function createStage(mount) {
|
|
598
595
|
if (!mount) throw new Error("taleem-player: mount is required");
|
|
599
596
|
const root = typeof mount === "string" ? document.querySelector(mount) : mount;
|
|
@@ -618,7 +615,7 @@ function createStage(mount) {
|
|
|
618
615
|
};
|
|
619
616
|
}
|
|
620
617
|
|
|
621
|
-
// src/
|
|
618
|
+
// src/engines/player/player.js
|
|
622
619
|
function createTaleemPlayer({ mount, deck }) {
|
|
623
620
|
const stage = createStage(mount);
|
|
624
621
|
let lastSlide = null;
|
|
@@ -680,6 +677,118 @@ function createTaleemPlayer({ mount, deck }) {
|
|
|
680
677
|
destroy
|
|
681
678
|
};
|
|
682
679
|
}
|
|
680
|
+
|
|
681
|
+
// src/engines/browser/browser.js
|
|
682
|
+
function createTaleemBrowser({ mount, deck }) {
|
|
683
|
+
if (!mount) {
|
|
684
|
+
throw new Error("taleem-browser: mount is required");
|
|
685
|
+
}
|
|
686
|
+
if (!deck || !Array.isArray(deck.deck)) {
|
|
687
|
+
throw new Error("taleem-browser: valid deck-v1 required");
|
|
688
|
+
}
|
|
689
|
+
const root = typeof mount === "string" ? document.querySelector(mount) : mount;
|
|
690
|
+
if (!root) {
|
|
691
|
+
throw new Error("taleem-browser: mount not found");
|
|
692
|
+
}
|
|
693
|
+
root.innerHTML = `
|
|
694
|
+
<div class="taleem-browser-bg"></div>
|
|
695
|
+
<div class="taleem-browser-slide"></div>
|
|
696
|
+
`;
|
|
697
|
+
const bgEl = root.querySelector(".taleem-browser-bg");
|
|
698
|
+
const slideEl = root.querySelector(".taleem-browser-slide");
|
|
699
|
+
applyBackground(bgEl, deck.background);
|
|
700
|
+
const slides = deck.deck.map((slideJSON, i) => {
|
|
701
|
+
if (!slideJSON.type) {
|
|
702
|
+
throw new Error(`taleem-browser: slide ${i} missing type`);
|
|
703
|
+
}
|
|
704
|
+
const Template = getSlideTemplate(slideJSON.type);
|
|
705
|
+
if (!Template) {
|
|
706
|
+
throw new Error(`taleem-browser: unknown slide type "${slideJSON.type}"`);
|
|
707
|
+
}
|
|
708
|
+
return Template.fromJSON(slideJSON);
|
|
709
|
+
});
|
|
710
|
+
const total = slides.length;
|
|
711
|
+
function render(index, renderState = {}) {
|
|
712
|
+
if (index < 0 || index >= total) return;
|
|
713
|
+
const slide = slides[index];
|
|
714
|
+
slideEl.innerHTML = slide.render(renderState);
|
|
715
|
+
}
|
|
716
|
+
return {
|
|
717
|
+
render,
|
|
718
|
+
getTotal() {
|
|
719
|
+
return total;
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
function applyBackground(el, bg = {}) {
|
|
724
|
+
el.style.position = "absolute";
|
|
725
|
+
el.style.inset = "0";
|
|
726
|
+
el.style.zIndex = "0";
|
|
727
|
+
el.style.backgroundColor = bg.backgroundColor || "#000";
|
|
728
|
+
el.style.backgroundImage = bg.backgroundImage ? `url(${bg.backgroundImage})` : "none";
|
|
729
|
+
el.style.backgroundSize = "cover";
|
|
730
|
+
el.style.backgroundPosition = "center";
|
|
731
|
+
el.style.opacity = bg.backgroundImageOpacity !== void 0 ? bg.backgroundImageOpacity : 1;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// src/utils/assignMockTimings.js
|
|
735
|
+
function assignMockTimings(goldenDeck, slideDuration = 5) {
|
|
736
|
+
if (!goldenDeck || !Array.isArray(goldenDeck.deck) || typeof slideDuration !== "number") {
|
|
737
|
+
throw new Error("assignMockTimings: invalid deck or slideDuration");
|
|
738
|
+
}
|
|
739
|
+
let currentTime = 0;
|
|
740
|
+
const deckWithTimings = {
|
|
741
|
+
...goldenDeck,
|
|
742
|
+
deck: goldenDeck.deck.map((slide) => {
|
|
743
|
+
const start = currentTime;
|
|
744
|
+
const end = start + slideDuration;
|
|
745
|
+
currentTime = end;
|
|
746
|
+
const data = Array.isArray(slide.data) ? slide.data.map((item) => ({
|
|
747
|
+
...item,
|
|
748
|
+
showAt: start + (typeof item.showAt === "number" ? item.showAt : 0)
|
|
749
|
+
})) : slide.data;
|
|
750
|
+
return {
|
|
751
|
+
...slide,
|
|
752
|
+
start,
|
|
753
|
+
end,
|
|
754
|
+
data
|
|
755
|
+
};
|
|
756
|
+
})
|
|
757
|
+
};
|
|
758
|
+
return deckWithTimings;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// src/utils/resolveAssetPaths.js
|
|
762
|
+
function resolveAssetPaths(deck, IMG_BASE) {
|
|
763
|
+
if (deck.background?.backgroundImage && typeof deck.background.backgroundImage === "string") {
|
|
764
|
+
deck.background.backgroundImage = IMG_BASE + deck.background.backgroundImage.split("/").pop();
|
|
765
|
+
}
|
|
766
|
+
deck.deck.forEach((slide) => {
|
|
767
|
+
slide.data?.forEach((item) => {
|
|
768
|
+
if (item.name === "image" && typeof item.content === "string") {
|
|
769
|
+
item.content = IMG_BASE + item.content.split("/").pop();
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
});
|
|
773
|
+
return deck;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// src/utils/resolveBackground.js
|
|
777
|
+
function resolveBackground2(deck, ASSET_BASE) {
|
|
778
|
+
if (!deck || !deck.background) return deck;
|
|
779
|
+
const bg = deck.background;
|
|
780
|
+
if (typeof bg.backgroundImage === "string" && bg.backgroundImage.length > 0) {
|
|
781
|
+
bg.backgroundImage = ASSET_BASE + bg.backgroundImage.split("/").pop();
|
|
782
|
+
}
|
|
783
|
+
if (bg.backgroundImageOpacity === void 0) {
|
|
784
|
+
bg.backgroundImageOpacity = 1;
|
|
785
|
+
}
|
|
786
|
+
return deck;
|
|
787
|
+
}
|
|
683
788
|
export {
|
|
684
|
-
|
|
789
|
+
assignMockTimings,
|
|
790
|
+
createTaleemBrowser,
|
|
791
|
+
createTaleemPlayer,
|
|
792
|
+
resolveAssetPaths,
|
|
793
|
+
resolveBackground2 as resolveBackground
|
|
685
794
|
};
|
|
@@ -20,7 +20,11 @@ var TaleemPlayer = (() => {
|
|
|
20
20
|
// src/index.js
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
|
|
23
|
+
assignMockTimings: () => assignMockTimings,
|
|
24
|
+
createTaleemBrowser: () => createTaleemBrowser,
|
|
25
|
+
createTaleemPlayer: () => createTaleemPlayer,
|
|
26
|
+
resolveAssetPaths: () => resolveAssetPaths,
|
|
27
|
+
resolveBackground: () => resolveBackground2
|
|
24
28
|
});
|
|
25
29
|
|
|
26
30
|
// node_modules/taleem-slides/src/slides/TitleSlide.js
|
|
@@ -299,22 +303,19 @@ var TaleemPlayer = (() => {
|
|
|
299
303
|
var TableSlide = {
|
|
300
304
|
type: "table",
|
|
301
305
|
fromJSON(raw) {
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
+
const rows = raw.data;
|
|
307
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
308
|
+
console.warn("Empty table skipped", raw);
|
|
309
|
+
return null;
|
|
306
310
|
}
|
|
307
311
|
return Object.freeze({
|
|
308
312
|
type: "table",
|
|
309
313
|
render() {
|
|
310
314
|
return `
|
|
311
315
|
<table class="slide table">
|
|
312
|
-
<thead>
|
|
313
|
-
<tr>${headers.map((h) => `<th>${h}</th>`).join("")}</tr>
|
|
314
|
-
</thead>
|
|
315
316
|
<tbody>
|
|
316
317
|
${rows.map(
|
|
317
|
-
(
|
|
318
|
+
(row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join("")}</tr>`
|
|
318
319
|
).join("")}
|
|
319
320
|
</tbody>
|
|
320
321
|
</table>
|
|
@@ -618,7 +619,7 @@ var TaleemPlayer = (() => {
|
|
|
618
619
|
return template;
|
|
619
620
|
}
|
|
620
621
|
|
|
621
|
-
// src/
|
|
622
|
+
// src/engines/player/stage.js
|
|
622
623
|
function createStage(mount) {
|
|
623
624
|
if (!mount) throw new Error("taleem-player: mount is required");
|
|
624
625
|
const root = typeof mount === "string" ? document.querySelector(mount) : mount;
|
|
@@ -643,7 +644,7 @@ var TaleemPlayer = (() => {
|
|
|
643
644
|
};
|
|
644
645
|
}
|
|
645
646
|
|
|
646
|
-
// src/
|
|
647
|
+
// src/engines/player/player.js
|
|
647
648
|
function createTaleemPlayer({ mount, deck }) {
|
|
648
649
|
const stage = createStage(mount);
|
|
649
650
|
let lastSlide = null;
|
|
@@ -705,5 +706,113 @@ var TaleemPlayer = (() => {
|
|
|
705
706
|
destroy
|
|
706
707
|
};
|
|
707
708
|
}
|
|
709
|
+
|
|
710
|
+
// src/engines/browser/browser.js
|
|
711
|
+
function createTaleemBrowser({ mount, deck }) {
|
|
712
|
+
if (!mount) {
|
|
713
|
+
throw new Error("taleem-browser: mount is required");
|
|
714
|
+
}
|
|
715
|
+
if (!deck || !Array.isArray(deck.deck)) {
|
|
716
|
+
throw new Error("taleem-browser: valid deck-v1 required");
|
|
717
|
+
}
|
|
718
|
+
const root = typeof mount === "string" ? document.querySelector(mount) : mount;
|
|
719
|
+
if (!root) {
|
|
720
|
+
throw new Error("taleem-browser: mount not found");
|
|
721
|
+
}
|
|
722
|
+
root.innerHTML = `
|
|
723
|
+
<div class="taleem-browser-bg"></div>
|
|
724
|
+
<div class="taleem-browser-slide"></div>
|
|
725
|
+
`;
|
|
726
|
+
const bgEl = root.querySelector(".taleem-browser-bg");
|
|
727
|
+
const slideEl = root.querySelector(".taleem-browser-slide");
|
|
728
|
+
applyBackground(bgEl, deck.background);
|
|
729
|
+
const slides = deck.deck.map((slideJSON, i) => {
|
|
730
|
+
if (!slideJSON.type) {
|
|
731
|
+
throw new Error(`taleem-browser: slide ${i} missing type`);
|
|
732
|
+
}
|
|
733
|
+
const Template = getSlideTemplate(slideJSON.type);
|
|
734
|
+
if (!Template) {
|
|
735
|
+
throw new Error(`taleem-browser: unknown slide type "${slideJSON.type}"`);
|
|
736
|
+
}
|
|
737
|
+
return Template.fromJSON(slideJSON);
|
|
738
|
+
});
|
|
739
|
+
const total = slides.length;
|
|
740
|
+
function render(index, renderState = {}) {
|
|
741
|
+
if (index < 0 || index >= total) return;
|
|
742
|
+
const slide = slides[index];
|
|
743
|
+
slideEl.innerHTML = slide.render(renderState);
|
|
744
|
+
}
|
|
745
|
+
return {
|
|
746
|
+
render,
|
|
747
|
+
getTotal() {
|
|
748
|
+
return total;
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
function applyBackground(el, bg = {}) {
|
|
753
|
+
el.style.position = "absolute";
|
|
754
|
+
el.style.inset = "0";
|
|
755
|
+
el.style.zIndex = "0";
|
|
756
|
+
el.style.backgroundColor = bg.backgroundColor || "#000";
|
|
757
|
+
el.style.backgroundImage = bg.backgroundImage ? `url(${bg.backgroundImage})` : "none";
|
|
758
|
+
el.style.backgroundSize = "cover";
|
|
759
|
+
el.style.backgroundPosition = "center";
|
|
760
|
+
el.style.opacity = bg.backgroundImageOpacity !== void 0 ? bg.backgroundImageOpacity : 1;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// src/utils/assignMockTimings.js
|
|
764
|
+
function assignMockTimings(goldenDeck, slideDuration = 5) {
|
|
765
|
+
if (!goldenDeck || !Array.isArray(goldenDeck.deck) || typeof slideDuration !== "number") {
|
|
766
|
+
throw new Error("assignMockTimings: invalid deck or slideDuration");
|
|
767
|
+
}
|
|
768
|
+
let currentTime = 0;
|
|
769
|
+
const deckWithTimings = {
|
|
770
|
+
...goldenDeck,
|
|
771
|
+
deck: goldenDeck.deck.map((slide) => {
|
|
772
|
+
const start = currentTime;
|
|
773
|
+
const end = start + slideDuration;
|
|
774
|
+
currentTime = end;
|
|
775
|
+
const data = Array.isArray(slide.data) ? slide.data.map((item) => ({
|
|
776
|
+
...item,
|
|
777
|
+
showAt: start + (typeof item.showAt === "number" ? item.showAt : 0)
|
|
778
|
+
})) : slide.data;
|
|
779
|
+
return {
|
|
780
|
+
...slide,
|
|
781
|
+
start,
|
|
782
|
+
end,
|
|
783
|
+
data
|
|
784
|
+
};
|
|
785
|
+
})
|
|
786
|
+
};
|
|
787
|
+
return deckWithTimings;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// src/utils/resolveAssetPaths.js
|
|
791
|
+
function resolveAssetPaths(deck, IMG_BASE) {
|
|
792
|
+
if (deck.background?.backgroundImage && typeof deck.background.backgroundImage === "string") {
|
|
793
|
+
deck.background.backgroundImage = IMG_BASE + deck.background.backgroundImage.split("/").pop();
|
|
794
|
+
}
|
|
795
|
+
deck.deck.forEach((slide) => {
|
|
796
|
+
slide.data?.forEach((item) => {
|
|
797
|
+
if (item.name === "image" && typeof item.content === "string") {
|
|
798
|
+
item.content = IMG_BASE + item.content.split("/").pop();
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
});
|
|
802
|
+
return deck;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// src/utils/resolveBackground.js
|
|
806
|
+
function resolveBackground2(deck, ASSET_BASE) {
|
|
807
|
+
if (!deck || !deck.background) return deck;
|
|
808
|
+
const bg = deck.background;
|
|
809
|
+
if (typeof bg.backgroundImage === "string" && bg.backgroundImage.length > 0) {
|
|
810
|
+
bg.backgroundImage = ASSET_BASE + bg.backgroundImage.split("/").pop();
|
|
811
|
+
}
|
|
812
|
+
if (bg.backgroundImageOpacity === void 0) {
|
|
813
|
+
bg.backgroundImageOpacity = 1;
|
|
814
|
+
}
|
|
815
|
+
return deck;
|
|
816
|
+
}
|
|
708
817
|
return __toCommonJS(index_exports);
|
|
709
818
|
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taleem-player",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1-rc",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/taleem-player.umd.js",
|
|
6
6
|
"module": "./dist/taleem-player.esm.js",
|
|
@@ -8,22 +8,26 @@
|
|
|
8
8
|
".": {
|
|
9
9
|
"import": "./dist/taleem-player.esm.js",
|
|
10
10
|
"require": "./dist/taleem-player.umd.js"
|
|
11
|
-
}
|
|
11
|
+
},
|
|
12
|
+
"./css": "./dist/css/taleem.css",
|
|
13
|
+
"./css/dark": "./dist/css/themes/dark.css",
|
|
14
|
+
"./css/light": "./dist/css/themes/light.css",
|
|
15
|
+
"./css/paper": "./dist/css/themes/paper.css"
|
|
12
16
|
},
|
|
13
17
|
"files": [
|
|
14
18
|
"dist"
|
|
15
19
|
],
|
|
16
20
|
"scripts": {
|
|
17
21
|
"build": "node ./scripts/build.js",
|
|
18
|
-
"test": "
|
|
22
|
+
"test": "vitest run"
|
|
19
23
|
},
|
|
20
24
|
"dependencies": {
|
|
21
|
-
"taleem-core": "^1.3
|
|
22
|
-
"taleem-slides": "^0.6.
|
|
25
|
+
"taleem-core": "^1.4.3",
|
|
26
|
+
"taleem-slides": "^0.6.2-rc.6"
|
|
23
27
|
},
|
|
24
28
|
"devDependencies": {
|
|
25
29
|
"esbuild": "^0.27.2",
|
|
26
|
-
"
|
|
27
|
-
"
|
|
30
|
+
"jsdom": "^22.1.0",
|
|
31
|
+
"vitest": "^3.2.4"
|
|
28
32
|
}
|
|
29
33
|
}
|