presently 0.26.0 → 0.27.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +51 -193
- data/context/index.yaml +4 -0
- data/context/templates.md +207 -0
- data/lib/presently/clock.rb +15 -10
- data/lib/presently/presentation_controller.rb +7 -4
- data/lib/presently/presenter_view.rb +91 -22
- data/lib/presently/state.rb +3 -1
- data/lib/presently/version.rb +1 -1
- data/public/_static/presenter.css +29 -0
- data/readme.md +8 -4
- data/releases.md +6 -0
- data.tar.gz.sig +0 -0
- metadata +2 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d8b031e64df9f01cd66eb315891ac4bf2b851f78355025afdf3f849a3a7513e5
|
|
4
|
+
data.tar.gz: 860837d5f75beaa2d814e388b401a297ce0af0ec4eb8e96eb99459fd79fa5460
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 62a0c52ebb93318dd390592ea580b1fa4883891b36d66339669bdbb98dc8e202af97517f375d463fac720034ec90e2b36f62ef1f5784fba29103e49eb57c4b55
|
|
7
|
+
data.tar.gz: f93b4b3b5a00275be37ec3a1a4d4760c41e270193a501a8f9a3030849e0765dcf6dab04cc3c7f92519fdc80b7950a52b259e9864f583e09b397dcbc6cd102a1c
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/context/getting-started.md
CHANGED
|
@@ -188,173 +188,9 @@ The defaults are 1920×1080 at up to 30 frames per second. The exporter starts a
|
|
|
188
188
|
|
|
189
189
|
## Templates
|
|
190
190
|
|
|
191
|
-
Templates define the visual layout of each slide. Select
|
|
191
|
+
Templates define the visual layout of each slide. Select one with the `template` field in the frontmatter; slides without it use `default`. Common choices include `title` for an opening slide, `two_column` for comparisons, and `code` for code walkthroughs.
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
A general-purpose content slide. An H1 becomes the slide title and the remaining document becomes its body.
|
|
196
|
-
|
|
197
|
-
``` markdown
|
|
198
|
-
---
|
|
199
|
-
template: default
|
|
200
|
-
duration: 60
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
# Key points
|
|
204
|
-
|
|
205
|
-
- First point
|
|
206
|
-
- Second point
|
|
207
|
-
- Third point
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Title
|
|
211
|
-
|
|
212
|
-
A large title with a short body, centered on the slide.
|
|
213
|
-
|
|
214
|
-
``` markdown
|
|
215
|
-
---
|
|
216
|
-
template: title
|
|
217
|
-
duration: 30
|
|
218
|
-
---
|
|
219
|
-
|
|
220
|
-
# My Presentation Title
|
|
221
|
-
|
|
222
|
-
A subtitle or tagline
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### Section
|
|
226
|
-
|
|
227
|
-
A section divider slide with a large heading, optional supporting body, and accent background.
|
|
228
|
-
|
|
229
|
-
``` markdown
|
|
230
|
-
---
|
|
231
|
-
template: section
|
|
232
|
-
duration: 15
|
|
233
|
-
---
|
|
234
|
-
|
|
235
|
-
# Part Two
|
|
236
|
-
|
|
237
|
-
Architecture and design
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### Two Column
|
|
241
|
-
|
|
242
|
-
A side-by-side layout with `left` and `right` sections.
|
|
243
|
-
|
|
244
|
-
``` markdown
|
|
245
|
-
---
|
|
246
|
-
template: two_column
|
|
247
|
-
duration: 90
|
|
248
|
-
---
|
|
249
|
-
|
|
250
|
-
# Client and server responsibilities
|
|
251
|
-
|
|
252
|
-
The application is split across two cooperating environments.
|
|
253
|
-
|
|
254
|
-
## Left
|
|
255
|
-
|
|
256
|
-
**Server Side**
|
|
257
|
-
|
|
258
|
-
- Ruby + Lively
|
|
259
|
-
- WebSocket connections
|
|
260
|
-
|
|
261
|
-
## Right
|
|
262
|
-
|
|
263
|
-
**Client Side**
|
|
264
|
-
|
|
265
|
-
- Live DOM updates
|
|
266
|
-
- CSS animations
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
### Code
|
|
270
|
-
|
|
271
|
-
A syntax-highlighted code slide with optional focus regions for code walkthroughs. Use the `focus` frontmatter to specify which lines to highlight (1-based). Lines outside the focus range are dimmed, and the code scrolls to center the focused region.
|
|
272
|
-
|
|
273
|
-
``` markdown
|
|
274
|
-
---
|
|
275
|
-
template: code
|
|
276
|
-
duration: 60
|
|
277
|
-
focus: 2-8
|
|
278
|
-
---
|
|
279
|
-
|
|
280
|
-
# Constructor
|
|
281
|
-
|
|
282
|
-
```ruby
|
|
283
|
-
class Presentation
|
|
284
|
-
def initialize
|
|
285
|
-
@slides = []
|
|
286
|
-
@current_index = 0
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
def advance!
|
|
290
|
-
@current_index += 1
|
|
291
|
-
end
|
|
292
|
-
end
|
|
293
|
-
```
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
Create animated walkthroughs by using multiple slides with the same code but different `focus` ranges. The transition between them smoothly scrolls and shifts the dim overlays.
|
|
297
|
-
|
|
298
|
-
### Statement
|
|
299
|
-
|
|
300
|
-
A prominent statement or quote, centered on the slide. Supports an optional `## Translation` placeholder.
|
|
301
|
-
|
|
302
|
-
``` markdown
|
|
303
|
-
---
|
|
304
|
-
template: statement
|
|
305
|
-
duration: 30
|
|
306
|
-
---
|
|
307
|
-
|
|
308
|
-
The best way to predict the future is to create it.
|
|
309
|
-
|
|
310
|
-
## Translation
|
|
311
|
-
|
|
312
|
-
未来を予測する最善の方法は、それを創ることである。
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
### Translations
|
|
316
|
-
|
|
317
|
-
Templates can extract an optional `## Translation` section and position it independently from the main document. Every standard template displays it separately in a lighter style.
|
|
318
|
-
|
|
319
|
-
### Image
|
|
320
|
-
|
|
321
|
-
A centered image with an optional caption.
|
|
322
|
-
|
|
323
|
-
``` markdown
|
|
324
|
-
---
|
|
325
|
-
template: image
|
|
326
|
-
duration: 30
|
|
327
|
-
---
|
|
328
|
-
|
|
329
|
-

|
|
330
|
-
|
|
331
|
-
## Caption
|
|
332
|
-
|
|
333
|
-
System architecture overview
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
### Diagram
|
|
337
|
-
|
|
338
|
-
A centered canvas for diagrams and other custom visual layouts, with an optional title. A single grid or flex container is usually enough to create a diagram that remains centered as the slide scales:
|
|
339
|
-
|
|
340
|
-
``` markdown
|
|
341
|
-
---
|
|
342
|
-
template: diagram
|
|
343
|
-
duration: 60
|
|
344
|
-
---
|
|
345
|
-
|
|
346
|
-
# Request lifecycle
|
|
347
|
-
|
|
348
|
-
<div style="display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 2em; width: 80%;">
|
|
349
|
-
<div>Browser</div>
|
|
350
|
-
<div>→</div>
|
|
351
|
-
<div>Server</div>
|
|
352
|
-
</div>
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
For coordinate-based layouts, wrap the elements in `<div class="diagram-freeform">`. The wrapper fills the canvas and absolutely positions each direct child.
|
|
356
|
-
|
|
357
|
-
All other templates also support absolutely positioned overlays since the slide container is `position: relative`. This lets you add callouts, badges, or annotations on top of any template's normal content.
|
|
193
|
+
See the [Templates guide](../templates/index) for built-in layouts, examples, translations, and custom templates.
|
|
358
194
|
|
|
359
195
|
## Transitions
|
|
360
196
|
|
|
@@ -395,40 +231,62 @@ The presenter view at `/presenter` provides:
|
|
|
395
231
|
|
|
396
232
|
- **Current and next slide previews** — see what's coming without switching windows.
|
|
397
233
|
- **Presenter notes** — notes from the slide's `---` separator section.
|
|
398
|
-
- **Timer controls** — Start, Pause
|
|
234
|
+
- **Timer controls** — Start when ready, Pause while running, and Resume or Reset while paused.
|
|
399
235
|
- **Pacing indicator** — shows whether you're on time, ahead, or behind based on per-slide `duration` metadata.
|
|
400
236
|
- **Progress bar** — visual indicator of time consumed for the current slide.
|
|
401
237
|
- **Reload button** — reload slides from disk without restarting the server.
|
|
402
238
|
|
|
403
|
-
|
|
239
|
+
### Timer Controls
|
|
404
240
|
|
|
405
|
-
|
|
241
|
+
Pause the timer before changing its elapsed time, whether you are rehearsing a section or preparing for another presentation.
|
|
406
242
|
|
|
407
|
-
|
|
408
|
-
# In your environment configuration:
|
|
409
|
-
service "presently" do
|
|
410
|
-
include Presently::Environment::Application
|
|
243
|
+
A **ready** timer is waiting to start from its current timestamp. It can be ready at `0:00` or at a later position after Reset.
|
|
411
244
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
245
|
+
| Timer state | Available controls |
|
|
246
|
+
|---|---|
|
|
247
|
+
| Ready | **Start** begins timing, or **Auto-start** indicates that advancing will begin timing. |
|
|
248
|
+
| Running | **Pause** freezes elapsed time. |
|
|
249
|
+
| Paused | **Resume** continues timing, and **Reset** prepares the current slide to begin again. |
|
|
250
|
+
|
|
251
|
+
**Reset** always sets elapsed time to the sum of durations before the current slide. It normally leaves the timer paused; press **Resume** when ready to continue. On a slide with `timer: start`, Reset returns the timer to the ready state at that same timestamp, so advancing starts from the slide's expected time. Reset leaves the current slide unchanged and is only available while paused.
|
|
252
|
+
|
|
253
|
+
After a rehearsal, return to your waiting slide, pause, and press **Reset**. Advancing from that `timer: start` slide starts timing from its expected timestamp, regardless of its position in the deck. For example, a waiting slide after five minutes of allocated content resets to `5:00`, waits, and starts from `5:00` when you advance. The timestamp and ready state are also preserved when saving and restoring the presentation.
|
|
254
|
+
|
|
255
|
+
For slides with recognized timer metadata and a following slide, the **Next** tooltip describes what advancing will actually do. For example, a manually paused `timer: start` slide shows “Timer is paused. Advancing will leave it paused.” After Reset, the tooltip changes to “Advancing will start the timer.” The tooltip also reflects pause and resume actions, including when they would leave the clock unchanged.
|
|
256
|
+
|
|
257
|
+
While the timer is ready, an **Auto-start** indicator replaces **Start** on a `timer: start` slide with a following slide. Its play icon gently pulses, and its tooltip says “Advancing will start the timer.” Advance to begin timing; **Pause** and **Resume** are available while running and paused respectively. The animation respects reduced-motion preferences.
|
|
417
258
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
259
|
+
### Starting the Timer from a Title Slide
|
|
260
|
+
|
|
261
|
+
A title slide can stay on screen while the audience settles. Add `timer: start` to its frontmatter to start the presentation timer when you advance to the next slide:
|
|
262
|
+
|
|
263
|
+
``` markdown
|
|
264
|
+
---
|
|
265
|
+
template: title
|
|
266
|
+
duration: 0
|
|
267
|
+
timer: start
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
# My Presentation
|
|
271
|
+
|
|
272
|
+
We'll begin shortly.
|
|
429
273
|
```
|
|
430
274
|
|
|
431
|
-
|
|
275
|
+
The timer stays ready while the title is displayed. Advancing from it in `/presenter` or `/display` starts the timer before showing the next slide. Setting `duration: 0` excludes the waiting slide from the expected presentation duration and pacing calculations. The `title` template itself does not control the timer.
|
|
276
|
+
|
|
277
|
+
Durations are read as floating-point seconds, including numeric strings. Negative, invalid, or non-finite values are treated as `0.0`. An unspecified or null duration defaults to `0.0` seconds, meaning no time has been allocated to that slide. Set explicit durations, or apply recorded narration durations, to establish a pacing schedule. When the total allocated duration is zero, the presenter shows elapsed time without pacing indicators, a progress bar, or a remaining-time estimate.
|
|
278
|
+
|
|
279
|
+
The `timer` field supports these actions:
|
|
280
|
+
|
|
281
|
+
| Value | Effect when advancing from this slide |
|
|
282
|
+
|---|---|
|
|
283
|
+
| `start` | Starts a ready timer from its current timestamp. Revisiting the slide does not reset elapsed time or resume a manually paused timer. |
|
|
284
|
+
| `pause` | Pauses the timer, preserving elapsed time. Has no effect while ready. |
|
|
285
|
+
| `resume` | Resumes a paused timer, preserving elapsed time. Has no effect while ready or already running. |
|
|
286
|
+
|
|
287
|
+
For a break, put `timer: pause` on the slide immediately before the break slide, and `timer: resume` on the break slide itself. Give the break slide `duration: 0` to exclude the break from pacing calculations. Advancing into the break pauses timing; advancing out resumes it.
|
|
288
|
+
|
|
289
|
+
Timer actions run only when **Next** successfully moves to another slide. Going backwards, jumping directly to a slide, reloading, reconnecting, and restoring saved state do not trigger them. Neither does navigation in `/record`, or recorded playback. Slides without a recognized timer action leave the timer unchanged; the manual timer controls remain available.
|
|
432
290
|
|
|
433
291
|
## Customizing the Application
|
|
434
292
|
|
|
@@ -438,8 +296,8 @@ For advanced customization, create an `application.rb` and run with `presently a
|
|
|
438
296
|
#!/usr/bin/env presently
|
|
439
297
|
|
|
440
298
|
class Application < Presently::Application
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
299
|
+
def title
|
|
300
|
+
"My Conference Talk"
|
|
301
|
+
end
|
|
444
302
|
end
|
|
445
303
|
```
|
data/context/index.yaml
CHANGED
|
@@ -10,6 +10,10 @@ files:
|
|
|
10
10
|
title: Getting Started
|
|
11
11
|
description: This guide explains how to use `presently` to create and deliver web-based
|
|
12
12
|
presentations using Markdown slides.
|
|
13
|
+
- path: templates.md
|
|
14
|
+
title: Templates
|
|
15
|
+
description: This guide explains how to choose slide templates and create custom
|
|
16
|
+
layouts in Presently.
|
|
13
17
|
- path: animating-slides.md
|
|
14
18
|
title: Animating Slides
|
|
15
19
|
description: This guide explains how to animate content within slides using the
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Templates
|
|
2
|
+
|
|
3
|
+
This guide explains how to choose slide templates and create custom layouts in Presently.
|
|
4
|
+
|
|
5
|
+
## Built-in Templates
|
|
6
|
+
|
|
7
|
+
Templates define the visual layout of each slide. Select a template using the `template` field in the frontmatter.
|
|
8
|
+
|
|
9
|
+
### Default
|
|
10
|
+
|
|
11
|
+
A general-purpose content slide. An H1 becomes the slide title and the remaining document becomes its body.
|
|
12
|
+
|
|
13
|
+
``` markdown
|
|
14
|
+
---
|
|
15
|
+
template: default
|
|
16
|
+
duration: 60
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Key points
|
|
20
|
+
|
|
21
|
+
- First point
|
|
22
|
+
- Second point
|
|
23
|
+
- Third point
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Title
|
|
27
|
+
|
|
28
|
+
A large title with a short body, centered on the slide.
|
|
29
|
+
|
|
30
|
+
``` markdown
|
|
31
|
+
---
|
|
32
|
+
template: title
|
|
33
|
+
duration: 30
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
# My Presentation Title
|
|
37
|
+
|
|
38
|
+
A subtitle or tagline
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Section
|
|
42
|
+
|
|
43
|
+
A section divider slide with a large heading, optional supporting body, and accent background.
|
|
44
|
+
|
|
45
|
+
``` markdown
|
|
46
|
+
---
|
|
47
|
+
template: section
|
|
48
|
+
duration: 15
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
# Part Two
|
|
52
|
+
|
|
53
|
+
Architecture and design
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Two Column
|
|
57
|
+
|
|
58
|
+
A side-by-side layout with `left` and `right` sections.
|
|
59
|
+
|
|
60
|
+
``` markdown
|
|
61
|
+
---
|
|
62
|
+
template: two_column
|
|
63
|
+
duration: 90
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
# Client and server responsibilities
|
|
67
|
+
|
|
68
|
+
The application is split across two cooperating environments.
|
|
69
|
+
|
|
70
|
+
## Left
|
|
71
|
+
|
|
72
|
+
**Server Side**
|
|
73
|
+
|
|
74
|
+
- Ruby + Lively
|
|
75
|
+
- WebSocket connections
|
|
76
|
+
|
|
77
|
+
## Right
|
|
78
|
+
|
|
79
|
+
**Client Side**
|
|
80
|
+
|
|
81
|
+
- Live DOM updates
|
|
82
|
+
- CSS animations
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Code
|
|
86
|
+
|
|
87
|
+
A syntax-highlighted code slide with optional focus regions for code walkthroughs. Use the `focus` frontmatter to specify which lines to highlight (1-based). Lines outside the focus range are dimmed, and the code scrolls to center the focused region.
|
|
88
|
+
|
|
89
|
+
```` markdown
|
|
90
|
+
---
|
|
91
|
+
template: code
|
|
92
|
+
duration: 60
|
|
93
|
+
focus: 2-8
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
# Constructor
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
class Presentation
|
|
100
|
+
def initialize
|
|
101
|
+
@slides = []
|
|
102
|
+
@current_index = 0
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def advance!
|
|
106
|
+
@current_index += 1
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
```
|
|
110
|
+
````
|
|
111
|
+
|
|
112
|
+
Create animated walkthroughs by using multiple slides with the same code but different `focus` ranges. The transition between them smoothly scrolls and shifts the dim overlays.
|
|
113
|
+
|
|
114
|
+
### Statement
|
|
115
|
+
|
|
116
|
+
A prominent statement or quote, centered on the slide. Supports an optional `## Translation` placeholder.
|
|
117
|
+
|
|
118
|
+
``` markdown
|
|
119
|
+
---
|
|
120
|
+
template: statement
|
|
121
|
+
duration: 30
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
The best way to predict the future is to create it.
|
|
125
|
+
|
|
126
|
+
## Translation
|
|
127
|
+
|
|
128
|
+
未来を予測する最善の方法は、それを創ることである。
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Image
|
|
132
|
+
|
|
133
|
+
A centered image with an optional caption.
|
|
134
|
+
|
|
135
|
+
``` markdown
|
|
136
|
+
---
|
|
137
|
+
template: image
|
|
138
|
+
duration: 30
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+

|
|
142
|
+
|
|
143
|
+
## Caption
|
|
144
|
+
|
|
145
|
+
System architecture overview
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Diagram
|
|
149
|
+
|
|
150
|
+
A centered canvas for diagrams and other custom visual layouts, with an optional title. A single grid or flex container is usually enough to create a diagram that remains centered as the slide scales:
|
|
151
|
+
|
|
152
|
+
``` markdown
|
|
153
|
+
---
|
|
154
|
+
template: diagram
|
|
155
|
+
duration: 60
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
# Request lifecycle
|
|
159
|
+
|
|
160
|
+
<div style="display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 2em; width: 80%;">
|
|
161
|
+
<div>Browser</div>
|
|
162
|
+
<div>→</div>
|
|
163
|
+
<div>Server</div>
|
|
164
|
+
</div>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
For coordinate-based layouts, wrap the elements in `<div class="diagram-freeform">`. The wrapper fills the canvas and absolutely positions each direct child.
|
|
168
|
+
|
|
169
|
+
All other templates also support absolutely positioned overlays since the slide container is `position: relative`. This lets you add callouts, badges, or annotations on top of any template's normal content.
|
|
170
|
+
|
|
171
|
+
## Translations
|
|
172
|
+
|
|
173
|
+
Templates can extract an optional `## Translation` section and position it independently from the main document. Every standard template displays it separately in a lighter style.
|
|
174
|
+
|
|
175
|
+
## Custom Templates
|
|
176
|
+
|
|
177
|
+
For layouts specific to your presentation, put `.xrb` files in a `templates/` directory alongside `slides/`. Presently searches this directory before its bundled templates, so you can add new layouts or override individual built-in templates.
|
|
178
|
+
|
|
179
|
+
To search additional directories, configure `templates_roots`, which returns an ordered array of paths:
|
|
180
|
+
|
|
181
|
+
``` ruby
|
|
182
|
+
# In your environment configuration:
|
|
183
|
+
service "presently" do
|
|
184
|
+
include Presently::Environment::Application
|
|
185
|
+
|
|
186
|
+
def templates_roots
|
|
187
|
+
[File.expand_path("shared-templates", self.root)] + super
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
For example, save the following template as `templates/custom.xrb` and select it with `template: custom` in a slide's frontmatter.
|
|
193
|
+
|
|
194
|
+
Templates receive a {ruby Presently::TemplateScope}. `self.slide_header` renders the semantic H1 title and optional section metadata, while `self.document` renders the remaining slide body. `self.extract(name)` removes an H2 placeholder with that exact heading text from the body and returns its rendered content. Extract placeholders before rendering the remaining document:
|
|
195
|
+
|
|
196
|
+
``` xrb
|
|
197
|
+
<?r translation = self.extract("Translation") ?>
|
|
198
|
+
#{self.slide_header}
|
|
199
|
+
<div class="slide-body">
|
|
200
|
+
#{self.document}
|
|
201
|
+
</div>
|
|
202
|
+
<?r if translation ?>
|
|
203
|
+
<div class="slide-translation">#{translation}</div>
|
|
204
|
+
<?r end ?>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Extraction stops at the next heading of the same or a higher level, so lower-level headings remain inside the extracted fragment. Placeholder names are case-sensitive and must match the heading text exactly. Only placeholders requested by the template are removed; other headings remain ordinary document content.
|
data/lib/presently/clock.rb
CHANGED
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
module Presently
|
|
7
7
|
# A simple clock that tracks elapsed time with start, pause, resume, and reset.
|
|
8
8
|
#
|
|
9
|
-
#
|
|
9
|
+
# A ready clock waits to start at its current elapsed time. It accumulates time
|
|
10
|
+
# while running and freezes it when paused.
|
|
10
11
|
class Clock
|
|
11
|
-
# Initialize a new clock in the
|
|
12
|
+
# Initialize a new clock in the ready state at zero.
|
|
12
13
|
def initialize
|
|
13
14
|
@elapsed = 0
|
|
14
15
|
@started = false
|
|
@@ -16,7 +17,7 @@ module Presently
|
|
|
16
17
|
@last_tick = nil
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
# Whether the clock
|
|
20
|
+
# Whether the clock is running or paused; false when ready.
|
|
20
21
|
# @returns [Boolean]
|
|
21
22
|
def started?
|
|
22
23
|
@started
|
|
@@ -35,7 +36,7 @@ module Presently
|
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
# The total elapsed time in seconds.
|
|
38
|
-
# Includes time accumulated up to now if running, or frozen time if paused.
|
|
39
|
+
# Includes time accumulated up to now if running, or frozen time if paused or ready.
|
|
39
40
|
# @returns [Numeric] The elapsed time in seconds.
|
|
40
41
|
def elapsed
|
|
41
42
|
if @running
|
|
@@ -78,12 +79,16 @@ module Presently
|
|
|
78
79
|
@last_tick = Time.now
|
|
79
80
|
end
|
|
80
81
|
|
|
81
|
-
# Reset the
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
# Reset the clock to its initial, ready state, or set its elapsed time.
|
|
83
|
+
# A numeric value preserves whether the clock is running; a ready clock becomes paused.
|
|
84
|
+
# Pass `started: false` to make the clock ready at the given elapsed time.
|
|
85
|
+
# @parameter elapsed [Numeric | Nil] The elapsed time in seconds, or `nil` to clear the clock.
|
|
86
|
+
# @parameter started [Boolean] Whether the clock is running or paused at the reset position; false makes it ready.
|
|
87
|
+
def reset!(elapsed = nil, started: !elapsed.nil?)
|
|
88
|
+
@elapsed = elapsed || 0
|
|
89
|
+
@started = started
|
|
90
|
+
@running = @started && @running
|
|
91
|
+
@last_tick = @running ? Time.now : nil
|
|
87
92
|
end
|
|
88
93
|
end
|
|
89
94
|
end
|
|
@@ -105,9 +105,14 @@ module Presently
|
|
|
105
105
|
end
|
|
106
106
|
end
|
|
107
107
|
|
|
108
|
-
# Reset
|
|
108
|
+
# Reset a paused timer to begin the current slide again.
|
|
109
|
+
# All slides reset to their expected start time. A `timer: start` slide becomes
|
|
110
|
+
# ready to start when advancing; other slides remain paused.
|
|
109
111
|
def reset_timer!
|
|
110
|
-
@clock.
|
|
112
|
+
return unless @clock.paused? && current_slide
|
|
113
|
+
|
|
114
|
+
@clock.reset!(@presentation.expected_time_at(@current_index), started: current_slide.timer != "start")
|
|
115
|
+
|
|
111
116
|
notify_listeners!
|
|
112
117
|
end
|
|
113
118
|
|
|
@@ -133,8 +138,6 @@ module Presently
|
|
|
133
138
|
# The estimated time remaining in the presentation.
|
|
134
139
|
# @returns [Numeric] The remaining time in seconds.
|
|
135
140
|
def time_remaining
|
|
136
|
-
return total_duration unless @clock.started?
|
|
137
|
-
|
|
138
141
|
expected_remaining = @presentation.expected_time_at(slide_count) - @clock.elapsed
|
|
139
142
|
|
|
140
143
|
[expected_remaining, 0].max
|
|
@@ -21,6 +21,7 @@ module Presently
|
|
|
21
21
|
super(id, data)
|
|
22
22
|
@controller = controller
|
|
23
23
|
@clock_task = nil
|
|
24
|
+
@timing_state = nil
|
|
24
25
|
@preview_renderer = SlideRenderer.new(css_class: "slide preview-slide", templates: controller.templates)
|
|
25
26
|
end
|
|
26
27
|
|
|
@@ -51,11 +52,22 @@ module Presently
|
|
|
51
52
|
self.render_slide!
|
|
52
53
|
end
|
|
53
54
|
|
|
54
|
-
#
|
|
55
|
+
# Update the timing section and the Next tooltip when the clock state changes.
|
|
56
|
+
# Leave unchanged, paused controls in place to preserve keyboard focus.
|
|
55
57
|
def update_timing!
|
|
58
|
+
clock = @controller.clock
|
|
59
|
+
state_changed = @timing_state != [clock.started?, clock.running?]
|
|
60
|
+
return unless clock.running? || state_changed
|
|
61
|
+
|
|
56
62
|
replace(".timing") do |builder|
|
|
57
63
|
render_timing(builder, @controller.current_slide)
|
|
58
64
|
end
|
|
65
|
+
|
|
66
|
+
if state_changed
|
|
67
|
+
replace(".next-button") do |builder|
|
|
68
|
+
render_next_button(builder)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
59
71
|
end
|
|
60
72
|
|
|
61
73
|
# Handle an event from the client.
|
|
@@ -77,6 +89,7 @@ module Presently
|
|
|
77
89
|
@controller.clock.pause!
|
|
78
90
|
end
|
|
79
91
|
@controller.save_state!
|
|
92
|
+
update_timing! if @page
|
|
80
93
|
when "reset"
|
|
81
94
|
@controller.reset_timer!
|
|
82
95
|
when "reload"
|
|
@@ -96,13 +109,53 @@ module Presently
|
|
|
96
109
|
Editor.url_for(path, line)
|
|
97
110
|
end
|
|
98
111
|
|
|
112
|
+
# Describe the actual effect of the outgoing slide's timer metadata.
|
|
113
|
+
# @returns [String | Nil] The hint, or `nil` when there is no timer action or next slide.
|
|
114
|
+
def timer_action_hint
|
|
115
|
+
return unless @controller.next_slide
|
|
116
|
+
|
|
117
|
+
clock = @controller.clock
|
|
118
|
+
case @controller.current_slide.timer
|
|
119
|
+
when "start"
|
|
120
|
+
return "Advancing will start the timer." unless clock.started?
|
|
121
|
+
when "pause"
|
|
122
|
+
return "Advancing will pause the timer." if clock.running?
|
|
123
|
+
when "resume"
|
|
124
|
+
return "Advancing will resume the timer." if clock.paused?
|
|
125
|
+
else
|
|
126
|
+
return
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
if clock.running?
|
|
130
|
+
"Advancing will leave the timer running."
|
|
131
|
+
elsif clock.paused?
|
|
132
|
+
"Timer is paused. Advancing will leave it paused."
|
|
133
|
+
else
|
|
134
|
+
"Advancing will leave the timer stopped."
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Render the Next button with a tooltip describing its timer action.
|
|
139
|
+
# @parameter builder [XRB::Builder] The HTML builder.
|
|
140
|
+
def render_next_button(builder)
|
|
141
|
+
builder.tag(:button,
|
|
142
|
+
class: "next-button",
|
|
143
|
+
title: timer_action_hint,
|
|
144
|
+
onClick: forward_event(action: "next")
|
|
145
|
+
) do
|
|
146
|
+
builder.text("Next →")
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
99
150
|
# Render the timing bar with controls, elapsed/remaining time, and pacing.
|
|
100
151
|
# @parameter builder [XRB::Builder] The HTML builder.
|
|
101
152
|
# @parameter slide [Slide | Nil] The current slide.
|
|
102
153
|
def render_timing(builder, slide)
|
|
154
|
+
@timing_state = [@controller.clock.started?, @controller.clock.running?]
|
|
103
155
|
pacing = @controller.pacing
|
|
104
156
|
progress = pacing ? (@controller.slide_progress * 100).round(1) : 0.0
|
|
105
157
|
next_slide = @controller.next_slide
|
|
158
|
+
wait_for_advance = !@controller.clock.started? && slide&.timer == "start" && !!next_slide
|
|
106
159
|
builder.tag(:div, class: "timing", style: "--slide-progress: #{progress}%") do
|
|
107
160
|
pacing_class = case pacing
|
|
108
161
|
when :behind then "behind"
|
|
@@ -111,25 +164,45 @@ module Presently
|
|
|
111
164
|
end
|
|
112
165
|
|
|
113
166
|
builder.tag(:div, class: "toolbar timing-info #{pacing_class}") do
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
"
|
|
122
|
-
|
|
123
|
-
|
|
167
|
+
if wait_for_advance
|
|
168
|
+
builder.tag(:span,
|
|
169
|
+
class: "auto-start",
|
|
170
|
+
role: "status",
|
|
171
|
+
title: "Advancing will start the timer."
|
|
172
|
+
) do
|
|
173
|
+
builder.tag(:span, class: "auto-start-icon", "aria-hidden": "true"){builder.text("▶")}
|
|
174
|
+
builder.text("Auto-start")
|
|
175
|
+
end
|
|
176
|
+
else
|
|
177
|
+
builder.tag(:button,
|
|
178
|
+
class: "pause-button",
|
|
179
|
+
onClick: forward_event(action: "pause")
|
|
180
|
+
) do
|
|
181
|
+
label = if !@controller.clock.started?
|
|
182
|
+
"▶ Start"
|
|
183
|
+
elsif @controller.clock.paused?
|
|
184
|
+
"▶ Resume"
|
|
185
|
+
else
|
|
186
|
+
"⏸ Pause"
|
|
187
|
+
end
|
|
188
|
+
builder.text(label)
|
|
124
189
|
end
|
|
125
|
-
builder.text(label)
|
|
126
190
|
end
|
|
127
191
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
192
|
+
if @controller.clock.paused? && slide
|
|
193
|
+
title = if slide.timer == "start"
|
|
194
|
+
"Reset to this slide's timestamp and wait to start"
|
|
195
|
+
else
|
|
196
|
+
"Reset to this slide's timestamp and stay paused"
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
builder.tag(:button,
|
|
200
|
+
class: "pause-button",
|
|
201
|
+
title: title,
|
|
202
|
+
onClick: forward_event(action: "reset")
|
|
203
|
+
) do
|
|
204
|
+
builder.text("↺ Reset")
|
|
205
|
+
end
|
|
133
206
|
end
|
|
134
207
|
|
|
135
208
|
builder.tag(:span, class: "elapsed") do
|
|
@@ -200,11 +273,7 @@ module Presently
|
|
|
200
273
|
builder.text("← Previous")
|
|
201
274
|
end
|
|
202
275
|
|
|
203
|
-
builder
|
|
204
|
-
onClick: forward_event(action: "next")
|
|
205
|
-
) do
|
|
206
|
-
builder.text("Next →")
|
|
207
|
-
end
|
|
276
|
+
render_next_button(builder)
|
|
208
277
|
|
|
209
278
|
builder.tag(:span, class: "slide-info") do
|
|
210
279
|
builder.tag(:span, class: "slide-position") do
|
data/lib/presently/state.rb
CHANGED
|
@@ -9,7 +9,7 @@ require "json"
|
|
|
9
9
|
module Presently
|
|
10
10
|
# Persists and restores presentation controller state to/from a JSON file.
|
|
11
11
|
#
|
|
12
|
-
# Tracks the current slide index, clock elapsed time, and
|
|
12
|
+
# Tracks the current slide index, clock elapsed time, and whether it is ready, running, or paused.
|
|
13
13
|
# This allows the presentation to survive server restarts without losing position.
|
|
14
14
|
class State
|
|
15
15
|
# The default state file path.
|
|
@@ -54,6 +54,8 @@ module Presently
|
|
|
54
54
|
# Restore clock state:
|
|
55
55
|
if data[:started]
|
|
56
56
|
controller.clock.restore!(data[:elapsed].to_f, running: data[:running])
|
|
57
|
+
else
|
|
58
|
+
controller.clock.reset!(data[:elapsed].to_f, started: false)
|
|
57
59
|
end
|
|
58
60
|
rescue => error
|
|
59
61
|
Console.warn(self, "Failed to restore state", exception: error)
|
data/lib/presently/version.rb
CHANGED
|
@@ -106,6 +106,35 @@
|
|
|
106
106
|
background: var(--accent);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
.timing-info .auto-start {
|
|
110
|
+
display: inline-flex;
|
|
111
|
+
align-items: center;
|
|
112
|
+
gap: 0.5rem;
|
|
113
|
+
height: 2.25rem;
|
|
114
|
+
padding: 0.3rem 0.75rem;
|
|
115
|
+
border: 1px solid rgba(233, 69, 96, 0.35);
|
|
116
|
+
border-radius: 6px;
|
|
117
|
+
background: rgba(233, 69, 96, 0.08);
|
|
118
|
+
color: var(--accent-light);
|
|
119
|
+
font-size: 0.9rem;
|
|
120
|
+
white-space: nowrap;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.auto-start-icon {
|
|
124
|
+
animation: auto-start-pulse 2.4s ease-in-out infinite;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@keyframes auto-start-pulse {
|
|
128
|
+
0%, 100% { opacity: 0.5; transform: scale(0.9); }
|
|
129
|
+
50% { opacity: 1; transform: scale(1.1); }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@media (prefers-reduced-motion: reduce) {
|
|
133
|
+
.auto-start-icon {
|
|
134
|
+
animation: none;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
109
138
|
.timing-info .elapsed {
|
|
110
139
|
font-variant-numeric: tabular-nums;
|
|
111
140
|
}
|
data/readme.md
CHANGED
|
@@ -26,6 +26,8 @@ Please see the [project documentation](https://socketry.github.io/presently/) fo
|
|
|
26
26
|
|
|
27
27
|
- [Getting Started](https://socketry.github.io/presently/guides/getting-started/index) - This guide explains how to use `presently` to create and deliver web-based presentations using Markdown slides.
|
|
28
28
|
|
|
29
|
+
- [Templates](https://socketry.github.io/presently/guides/templates/index) - This guide explains how to choose slide templates and create custom layouts in Presently.
|
|
30
|
+
|
|
29
31
|
- [Animating Slides](https://socketry.github.io/presently/guides/animating-slides/index) - This guide explains how to animate content within slides using the slide scripting system.
|
|
30
32
|
|
|
31
33
|
- [Animated Diagrams](https://socketry.github.io/presently/guides/animated-diagrams/index) - This guide explains how to design responsive, lifecycle-safe animated diagrams in Presently using semantic markup, slide-specific CSS, and Anime.js choreography.
|
|
@@ -67,6 +69,12 @@ The task records the presentation at 1920×1080 and 30 frames per second by defa
|
|
|
67
69
|
|
|
68
70
|
Please see the [project releases](https://socketry.github.io/presently/releases/index) for all releases.
|
|
69
71
|
|
|
72
|
+
### v0.27.0
|
|
73
|
+
|
|
74
|
+
- Show **Reset** only while paused. Reset always sets elapsed time to the current slide's expected start. It stays paused on ordinary slides, or becomes ready at that timestamp on a `timer: start` slide. Advancing starts from the preserved timestamp, which also survives saving and restoring the presentation.
|
|
75
|
+
- Describe the effect of slide timer metadata in the **Next** tooltip. Show an animated **Auto-start** indicator when advancing from the current slide will start the timer, with a tooltip explaining how to begin.
|
|
76
|
+
- Make `Clock#reset!` (or `reset!(nil)`) clear the clock to its initial, ready state. Passing a numeric elapsed time preserves whether it is running, or puts a ready clock into the paused state. Pass `started: false` to make the clock ready at a nonzero elapsed time.
|
|
77
|
+
|
|
70
78
|
### v0.26.0
|
|
71
79
|
|
|
72
80
|
- Add `timer: start`, `timer: pause`, and `timer: resume` slide metadata, applied when advancing away from a slide in the presenter or display.
|
|
@@ -120,10 +128,6 @@ Please see the [project releases](https://socketry.github.io/presently/releases/
|
|
|
120
128
|
- Truncate long slide paths responsively while preserving their filenames in presenter controls.
|
|
121
129
|
- Preserve the complete saved presentation state when restoring the controller.
|
|
122
130
|
|
|
123
|
-
### v0.17.2
|
|
124
|
-
|
|
125
|
-
- Fix slide rendering events for generated view identifiers that begin with a digit.
|
|
126
|
-
|
|
127
131
|
## See Also
|
|
128
132
|
|
|
129
133
|
- [lively](https://github.com/socketry/lively) — The real-time application framework that powers Presently.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.27.0
|
|
4
|
+
|
|
5
|
+
- Show **Reset** only while paused. Reset always sets elapsed time to the current slide's expected start. It stays paused on ordinary slides, or becomes ready at that timestamp on a `timer: start` slide. Advancing starts from the preserved timestamp, which also survives saving and restoring the presentation.
|
|
6
|
+
- Describe the effect of slide timer metadata in the **Next** tooltip. Show an animated **Auto-start** indicator when advancing from the current slide will start the timer, with a tooltip explaining how to begin.
|
|
7
|
+
- Make `Clock#reset!` (or `reset!(nil)`) clear the clock to its initial, ready state. Passing a numeric elapsed time preserves whether it is running, or puts a ready clock into the paused state. Pass `started: false` to make the clock ready at a nonzero elapsed time.
|
|
8
|
+
|
|
3
9
|
## v0.26.0
|
|
4
10
|
|
|
5
11
|
- Add `timer: start`, `timer: pause`, and `timer: resume` slide metadata, applied when advancing away from a slide in the presenter or display.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: presently
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.27.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -122,6 +122,7 @@ files:
|
|
|
122
122
|
- context/animating-slides.md
|
|
123
123
|
- context/getting-started.md
|
|
124
124
|
- context/index.yaml
|
|
125
|
+
- context/templates.md
|
|
125
126
|
- lib/presently.rb
|
|
126
127
|
- lib/presently/application.rb
|
|
127
128
|
- lib/presently/clock.rb
|
metadata.gz.sig
CHANGED
|
Binary file
|