presently 0.25.1 → 0.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0d45a9f85dc076cd8cb17996e84e0d75298a87ef7c8450d557371b3e680f546f
4
- data.tar.gz: a59eeb936e87a39814699ea2fa08cad1706c0fe74f4a0b1b87a5946bdd4bb45a
3
+ metadata.gz: fc69b4f81a2143dbbccc8d591c1fb675bb447057b77ffde4420f1ad1d7b3819a
4
+ data.tar.gz: b49c223f245c7949d72d8510d41d59f3e43efd6eb33b22bfe931bc84c0e1473d
5
5
  SHA512:
6
- metadata.gz: 2b562e56d739200c76ecb6028376a68f0f5c7a728c24c8547b583a28e73bf3f2ec4229caaf8d3db6b296cf3b93bba55b27f48a22bf8fc535eb00ecac25fb720e
7
- data.tar.gz: 4b649beb7cac24750123ccc3eeaed62377d73326e4030e82c0a086a257a1dfce4d2d2d6be9e15005e1837d991cb675fc6a71f943e3c1ea89113d4edcfa75937a
6
+ metadata.gz: '00686d6b384cdbbd81b27cc96e33e5301222c4b14a54c18f9221c2795f2b1eb0175ca4fbfc8caf10c3f2bb463df7ea85ba169b76b9b9e7c75049b419663b8be6'
7
+ data.tar.gz: d5348d5212bdde155fd4dcd7ea284606ff893716144fa8b04f202f3c18f0f18688431fe7ca59320c32c521baab047419b80170e31950fc57fe9dbcbaa545f95f
checksums.yaml.gz.sig CHANGED
Binary file
@@ -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 a template using the `template` field in the frontmatter.
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
- ### Default
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
- ![Architecture diagram](/images/architecture.png)
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
 
@@ -400,35 +236,37 @@ The presenter view at `/presenter` provides:
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
- ## Custom Templates
239
+ ### Starting the Timer from a Title Slide
404
240
 
405
- You can provide your own `.xrb` template files by configuring the templates root:
241
+ 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:
406
242
 
407
- ``` ruby
408
- # In your environment configuration:
409
- service "presently" do
410
- include Presently::Environment::Application
243
+ ``` markdown
244
+ ---
245
+ template: title
246
+ duration: 0
247
+ timer: start
248
+ ---
411
249
 
412
- def templates_root
413
- File.expand_path("templates", self.root)
414
- end
415
- end
416
- ```
250
+ # My Presentation
417
251
 
418
- 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:
419
-
420
- ``` xrb
421
- <?r translation = self.extract("Translation") ?>
422
- #{self.slide_header}
423
- <div class="slide-body">
424
- #{self.document}
425
- </div>
426
- <?r if translation ?>
427
- <div class="slide-translation">#{translation}</div>
428
- <?r end ?>
252
+ We'll begin shortly.
429
253
  ```
430
254
 
431
- 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.
255
+ The timer stays stopped 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.
256
+
257
+ 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.
258
+
259
+ The `timer` field supports these actions:
260
+
261
+ | Value | Effect when advancing from this slide |
262
+ |---|---|
263
+ | `start` | Starts the timer only if it has never started. Revisiting the slide does not reset elapsed time or resume a manually paused timer. |
264
+ | `pause` | Pauses the timer, preserving elapsed time. Has no effect before the timer starts. |
265
+ | `resume` | Resumes a started timer, preserving elapsed time. Has no effect before the timer starts or while it is already running. |
266
+
267
+ 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.
268
+
269
+ 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
270
 
433
271
  ## Customizing the Application
434
272
 
@@ -438,8 +276,8 @@ For advanced customization, create an `application.rb` and run with `presently a
438
276
  #!/usr/bin/env presently
439
277
 
440
278
  class Application < Presently::Application
441
- def title
442
- "My Conference Talk"
443
- end
279
+ def title
280
+ "My Conference Talk"
281
+ end
444
282
  end
445
283
  ```
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
+ ![Architecture diagram](/images/architecture.png)
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.
@@ -89,6 +89,7 @@ module Presently
89
89
  end
90
90
 
91
91
  # The progress through the current slide's allocated time.
92
+ # Slides with durations of zero or less are complete once their expected start time is reached.
92
93
  # @returns [Float] A value between 0.0 and 1.0.
93
94
  def slide_progress
94
95
  return 0.0 unless @clock.started?
@@ -97,7 +98,11 @@ module Presently
97
98
  return 0.0 unless slide
98
99
 
99
100
  time_into_slide = @clock.elapsed - @presentation.expected_time_at(@current_index)
100
- (time_into_slide / slide.duration).clamp(0.0, 1.0)
101
+ if slide.duration <= 0
102
+ time_into_slide.negative? ? 0.0 : 1.0
103
+ else
104
+ (time_into_slide / slide.duration).clamp(0.0, 1.0)
105
+ end
101
106
  end
102
107
 
103
108
  # Reset the timer so that elapsed time matches the expected time for the current slide.
@@ -107,8 +112,9 @@ module Presently
107
112
  end
108
113
 
109
114
  # The current pacing status relative to the slide timing.
110
- # @returns [Symbol] One of `:on_time`, `:ahead`, or `:behind`.
115
+ # @returns [Symbol | Nil] One of `:on_time`, `:ahead`, or `:behind`, or `nil` when no presentation time is allocated.
111
116
  def pacing
117
+ return unless total_duration.positive?
112
118
  return :on_time unless @clock.started?
113
119
 
114
120
  elapsed = @clock.elapsed
@@ -137,19 +143,27 @@ module Presently
137
143
  # Navigate to a specific slide by index.
138
144
  # Ignores out-of-bounds indices. Notifies listeners on change.
139
145
  # @parameter index [Integer] The slide index to navigate to.
140
- def go_to(index)
141
- return if index < 0 || index >= slide_count
146
+ # @parameter timer [Boolean] Whether to apply the outgoing slide's timer action before notifying listeners.
147
+ # @returns [Boolean] Whether the destination index was valid.
148
+ def go_to(index, timer: false)
149
+ return false if index < 0 || index >= slide_count
142
150
 
151
+ advance_timer! if timer
143
152
  @current_index = index
144
153
  notify_listeners!
154
+ return true
145
155
  end
146
156
 
147
- # Advance to the next slide.
148
- def advance!
149
- go_to(@current_index + 1)
157
+ # Advance to the next slide, applying the current slide's timer action.
158
+ # Timer actions only run when there is a next slide.
159
+ # @parameter timer [Boolean] Whether to apply the outgoing slide's timer action.
160
+ # @returns [Boolean] Whether the presentation advanced to the next slide.
161
+ def advance!(timer: true)
162
+ go_to(@current_index + 1, timer: timer)
150
163
  end
151
164
 
152
165
  # Go back to the previous slide.
166
+ # @returns [Boolean] Whether the presentation moved to the previous slide.
153
167
  def retreat!
154
168
  go_to(@current_index - 1)
155
169
  end
@@ -180,6 +194,18 @@ module Presently
180
194
 
181
195
  private
182
196
 
197
+ # Apply the outgoing slide's timer action without resetting elapsed time.
198
+ def advance_timer!
199
+ case current_slide.timer
200
+ when "start"
201
+ @clock.start! unless @clock.started?
202
+ when "pause"
203
+ @clock.pause!
204
+ when "resume"
205
+ @clock.resume! if @clock.started?
206
+ end
207
+ end
208
+
183
209
  # Notify all registered listeners that the slide has changed, and persist state.
184
210
  def notify_listeners!
185
211
  @state&.save(self)
@@ -100,14 +100,14 @@ module Presently
100
100
  # @parameter builder [XRB::Builder] The HTML builder.
101
101
  # @parameter slide [Slide | Nil] The current slide.
102
102
  def render_timing(builder, slide)
103
- progress = (@controller.slide_progress * 100).round(1)
103
+ pacing = @controller.pacing
104
+ progress = pacing ? (@controller.slide_progress * 100).round(1) : 0.0
104
105
  next_slide = @controller.next_slide
105
106
  builder.tag(:div, class: "timing", style: "--slide-progress: #{progress}%") do
106
- pacing = @controller.pacing
107
107
  pacing_class = case pacing
108
108
  when :behind then "behind"
109
109
  when :ahead then "ahead"
110
- else "on-time"
110
+ when :on_time then "on-time"
111
111
  end
112
112
 
113
113
  builder.tag(:div, class: "toolbar timing-info #{pacing_class}") do
@@ -136,17 +136,19 @@ module Presently
136
136
  builder.text("Elapsed: #{format_duration(@controller.clock.elapsed)}")
137
137
  end
138
138
 
139
- builder.tag(:span, class: "remaining") do
140
- builder.text("Remaining: #{format_duration(@controller.time_remaining)}")
141
- end
142
-
143
- builder.tag(:span, class: "pacing-indicator") do
144
- indicator = case pacing
145
- when :behind then "⏩ Speed up"
146
- when :ahead then "⏪ Slow down"
147
- else "✓ On time"
139
+ if pacing
140
+ builder.tag(:span, class: "remaining") do
141
+ builder.text("Remaining: #{format_duration(@controller.time_remaining)}")
142
+ end
143
+
144
+ builder.tag(:span, class: "pacing-indicator") do
145
+ indicator = case pacing
146
+ when :behind then "⏩ Speed up"
147
+ when :ahead then "⏪ Slow down"
148
+ else "✓ On time"
149
+ end
150
+ builder.text(indicator)
148
151
  end
149
- builder.text(indicator)
150
152
  end
151
153
 
152
154
  if slide
@@ -49,7 +49,7 @@ module Presently
49
49
  def handle(event)
50
50
  case event.dig(:detail, :action)
51
51
  when "next"
52
- @controller.advance!
52
+ @controller.advance!(timer: false)
53
53
  when "previous"
54
54
  @controller.retreat!
55
55
  when "reload"
@@ -349,9 +349,23 @@ module Presently
349
349
  end
350
350
 
351
351
  # The expected duration of this slide in seconds.
352
- # @returns [Integer] The duration from front_matter, or `60`.
352
+ # Negative, invalid, or non-finite values are treated as zero.
353
+ # @returns [Float] A finite duration of at least `0.0`, defaulting to `0.0` when unspecified or null.
353
354
  def duration
354
- @front_matter&.fetch("duration", 60) || 60
355
+ if value = @front_matter&.fetch("duration", nil)
356
+ duration = Float(value, exception: false)
357
+ if duration&.finite?
358
+ return duration.clamp(0.0, nil)
359
+ end
360
+ end
361
+
362
+ return 0.0
363
+ end
364
+
365
+ # The timer action to apply when advancing from this slide.
366
+ # @returns [String | Nil] `"start"`, `"pause"`, `"resume"`, or `nil` when unspecified.
367
+ def timer
368
+ @front_matter&.fetch("timer", nil)
355
369
  end
356
370
 
357
371
  # Update the expected duration in the slide's YAML front matter.
@@ -5,5 +5,5 @@
5
5
 
6
6
  # @namespace
7
7
  module Presently
8
- VERSION = "0.25.1"
8
+ VERSION = "0.26.1"
9
9
  end
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,13 @@ 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.26.0
73
+
74
+ - Add `timer: start`, `timer: pause`, and `timer: resume` slide metadata, applied when advancing away from a slide in the presenter or display.
75
+ - Normalize slide durations to finite, non-negative floating-point seconds. Missing or invalid durations now default to `0.0`, and negative durations are clamped to zero. Presentations relying on the previous 60-second default should set explicit durations.
76
+ - Handle zero-duration slide progress and hide pacing indicators, progress, and remaining-time estimates when a presentation has no allocated duration.
77
+ - Keep recorder navigation from changing the presentation timer.
78
+
70
79
  ### v0.24.1
71
80
 
72
81
  - Use legible, customizable link colors on the default dark slide background.
@@ -117,11 +126,6 @@ Please see the [project releases](https://socketry.github.io/presently/releases/
117
126
 
118
127
  - Fix slide rendering events for generated view identifiers that begin with a digit.
119
128
 
120
- ### v0.17.1
121
-
122
- - Add slide-scoped resource cleanup with `Slide#defer`, `Slide#signal`, and idempotent `Slide#dispose`. Existing tracked timeouts now use the same disposal lifecycle.
123
- - [Web Packages](https://socketry.github.io/presently/releases/index#web-packages)
124
-
125
129
  ## See Also
126
130
 
127
131
  - [lively](https://github.com/socketry/lively) — The real-time application framework that powers Presently.
data/releases.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Releases
2
2
 
3
+ ## v0.26.0
4
+
5
+ - Add `timer: start`, `timer: pause`, and `timer: resume` slide metadata, applied when advancing away from a slide in the presenter or display.
6
+ - Normalize slide durations to finite, non-negative floating-point seconds. Missing or invalid durations now default to `0.0`, and negative durations are clamped to zero. Presentations relying on the previous 60-second default should set explicit durations.
7
+ - Handle zero-duration slide progress and hide pacing indicators, progress, and remaining-time estimates when a presentation has no allocated duration.
8
+ - Keep recorder navigation from changing the presentation timer.
9
+
3
10
  ## v0.24.1
4
11
 
5
12
  - Use legible, customizable link colors on the default dark slide background.
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.25.1
4
+ version: 0.26.1
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
@@ -1,2 +1,3 @@
1
- 4��繼=��h��R4-w4a�E�~9FOTuB��!xb1���u���Ι� ���y�)ҿ��a�)Ү�.���[Śb��K2��vE_�?�Rٝ�A�Q�6JV�]G8Wĩ��Zn�m��p�2���ݿ���#���o@3��n�t��&�*Z�#�˕d�G���'fZS$�Dc��}�N��1%� �'xJ��>�U��?TC���8� y[����r+U
2
- �d�)??u�]���!��|����&��y@��]->[�?U� ��E�{*a�q�Uvp����s=_ ���
3
1
  7St�ϫQ:��H�B�� M��8r\}�9Ҫ ��`e�w�
2
+ 2���$ZD����?bD�l��*��!(M�c�~�%��`|��&M
3
+ @R�(K������}��X�\Ϯ,-sϺ{%����'ߊ�^� ��$����N��`�/�X���!=�hkݯ�HZ
4
+ ^��#�<�h�h��A|��oe��G�B�ݕY��}s��+1 z6���� �L�5�)̄��� ��5�t���X7��|ӳDi]>Y�w�aަ���qf-�X+h��@�FĜb<��V�6Ӟ�]�����t����I�d��R� ݛ���>�!y�/�|��$N�&ҫ:�