presently 0.26.1 → 0.28.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc69b4f81a2143dbbccc8d591c1fb675bb447057b77ffde4420f1ad1d7b3819a
4
- data.tar.gz: b49c223f245c7949d72d8510d41d59f3e43efd6eb33b22bfe931bc84c0e1473d
3
+ metadata.gz: 19e0176d3b7e17bd06f07d79e3ae4379d1992f3b75beb87b7725e47a713961be
4
+ data.tar.gz: 1b3bbd3f6a2c170f833a913cf1a0de5afb8030f3330773e62f1eba72153612c9
5
5
  SHA512:
6
- metadata.gz: '00686d6b384cdbbd81b27cc96e33e5301222c4b14a54c18f9221c2795f2b1eb0175ca4fbfc8caf10c3f2bb463df7ea85ba169b76b9b9e7c75049b419663b8be6'
7
- data.tar.gz: d5348d5212bdde155fd4dcd7ea284606ff893716144fa8b04f202f3c18f0f18688431fe7ca59320c32c521baab047419b80170e31950fc57fe9dbcbaa545f95f
6
+ metadata.gz: 0e9e35182f5d1fcd2a4e0a625ebfe3d0f8d743ece09335a1a399c82b5f720138ef62044e070ca54c01995b685dd970956fe15600f3b2176aee318226c569030c
7
+ data.tar.gz: d53ee079a8042e0abbcde56bb92d262084c2a8ea695dd4c41dec203bcd5d2a4050ba69557d2456cfcf5f85ddb48b8989bfe186938d8cda9ce6669e21d43cff99
checksums.yaml.gz.sig CHANGED
Binary file
@@ -231,11 +231,31 @@ The presenter view at `/presenter` provides:
231
231
 
232
232
  - **Current and next slide previews** — see what's coming without switching windows.
233
233
  - **Presenter notes** — notes from the slide's `---` separator section.
234
- - **Timer controls** — Start, Pause, Resume, and Reset buttons.
234
+ - **Timer controls** — Start when ready, Pause while running, and Resume or Reset while paused.
235
235
  - **Pacing indicator** — shows whether you're on time, ahead, or behind based on per-slide `duration` metadata.
236
236
  - **Progress bar** — visual indicator of time consumed for the current slide.
237
237
  - **Reload button** — reload slides from disk without restarting the server.
238
238
 
239
+ ### Timer Controls
240
+
241
+ Pause the timer before changing its elapsed time, whether you are rehearsing a section or preparing for another presentation.
242
+
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.
244
+
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.
258
+
239
259
  ### Starting the Timer from a Title Slide
240
260
 
241
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:
@@ -252,7 +272,7 @@ timer: start
252
272
  We'll begin shortly.
253
273
  ```
254
274
 
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.
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.
256
276
 
257
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.
258
278
 
@@ -260,9 +280,9 @@ The `timer` field supports these actions:
260
280
 
261
281
  | Value | Effect when advancing from this slide |
262
282
  |---|---|
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. |
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. |
266
286
 
267
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.
268
288
 
@@ -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
- # The clock accumulates elapsed time while running and freezes it when paused.
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 stopped state.
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 has been started at least once.
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 elapsed time to the given value.
82
- # If running, continues from the new value. If paused, sets the frozen value.
83
- # @parameter elapsed [Numeric] The new elapsed time in seconds.
84
- def reset!(elapsed = 0)
85
- @elapsed = elapsed
86
- @last_tick = Time.now if @running
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 the timer so that elapsed time matches the expected time for the current slide.
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.reset!(@presentation.expected_time_at(@current_index))
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
- # Push an update to just the timing section.
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
- builder.tag(:button,
115
- class: "pause-button",
116
- onClick: forward_event(action: "pause")
117
- ) do
118
- label = if !@controller.clock.started?
119
- "▶ Start"
120
- elsif @controller.clock.paused?
121
- "▶ Resume"
122
- else
123
- "⏸ Pause"
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
- builder.tag(:button,
129
- class: "pause-button",
130
- onClick: forward_event(action: "reset")
131
- ) do
132
- builder.text("↺ Reset")
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.tag(:button,
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
@@ -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 clock running state.
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)
@@ -5,5 +5,5 @@
5
5
 
6
6
  # @namespace
7
7
  module Presently
8
- VERSION = "0.26.1"
8
+ VERSION = "0.28.0"
9
9
  end
@@ -8,6 +8,7 @@
8
8
  --slide-text: #eee;
9
9
  --accent: #e94560;
10
10
  --accent-light: #ff6b81;
11
+ --text-emphasis: #6bb8ff;
11
12
  --surface: #16213e;
12
13
  --surface-light: #0f3460;
13
14
  --font-sans: "Inter", "Helvetica Neue", Arial, sans-serif;
@@ -189,7 +190,7 @@ a {
189
190
  ======================== */
190
191
 
191
192
  strong, b {
192
- color: var(--accent-light);
193
+ color: var(--text-emphasis);
193
194
  }
194
195
 
195
196
  code {
@@ -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
@@ -69,6 +69,12 @@ The task records the presentation at 1920×1080 and 30 frames per second by defa
69
69
 
70
70
  Please see the [project releases](https://socketry.github.io/presently/releases/index) for all releases.
71
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
+
72
78
  ### v0.26.0
73
79
 
74
80
  - Add `timer: start`, `timer: pause`, and `timer: resume` slide metadata, applied when advancing away from a slide in the presenter or display.
@@ -122,10 +128,6 @@ Please see the [project releases](https://socketry.github.io/presently/releases/
122
128
  - Truncate long slide paths responsively while preserving their filenames in presenter controls.
123
129
  - Preserve the complete saved presentation state when restoring the controller.
124
130
 
125
- ### v0.17.2
126
-
127
- - Fix slide rendering events for generated view identifiers that begin with a digit.
128
-
129
131
  ## See Also
130
132
 
131
133
  - [lively](https://github.com/socketry/lively) — The real-time application framework that powers Presently.
@@ -153,12 +155,14 @@ bundle exec sus
153
155
 
154
156
  ### Making Releases
155
157
 
156
- To make a new release:
158
+ To prepare a release branch and open a pull request from an up-to-date `main`:
157
159
 
158
160
  ``` shell
159
- bundle exec bake gem:release:patch # or minor or major
161
+ bundle exec bake gem:github:release:patch # or minor or major
160
162
  ```
161
163
 
164
+ See [bake-gem-github](https://github.com/socketry/bake-gem-github) for setup, remote releases, and recovery.
165
+
162
166
  ### Developer Certificate of Origin
163
167
 
164
168
  In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
data/release.cert ADDED
@@ -0,0 +1,24 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEFDCCAnygAwIBAgIBATANBgkqhkiG9w0BAQsFADAwMREwDwYDVQQKDAhTb2Nr
3
+ ZXRyeTEbMBkGA1UEAwwSU29ja2V0cnkgUnVieSBHZW1zMB4XDTI2MDkyMTA3NTgx
4
+ NFoXDTI3MDkyMTA3NTgxNFowMDERMA8GA1UECgwIU29ja2V0cnkxGzAZBgNVBAMM
5
+ ElNvY2tldHJ5IFJ1YnkgR2VtczCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoC
6
+ ggGBAM/QgjVgDzDo/xJEQoFvAzcVFP7msnswkhPJB2UDsbxXCZmers5jV512TM5s
7
+ NwLHfzJiC4DcI5ax9ZYKM9Q+dS21YYagNqjtg3YPyDqR6phoibEA0VoMuInUQW68
8
+ i5OkCJzYKGD2pYYVZnuFNqyM2ECUvXh/fBmvPoHbncAHhWPaCBH8mQJ8sRNc6+RV
9
+ SoZZu1Yo/aW1zQ+SpZwad7s1ZmigfDNHKhJ22KwZHk+/5Zw1QXVfcajjswV0Mm2P
10
+ irgjKN3fyNVkwlly63EBlR4VydT+g9QtJtqh7ee0ThhI+v4wYovf4dbLNcEBTHu1
11
+ x1pGbXGx/2fBAST5eVwaXIx1V8VU22AJcBk1H2o5/1F0HfbD2HQjmFOA112KUwBW
12
+ 9TtspZD3g6rCQFX53XvL9h6j2y1ukl7s5AdEzOe/x6r1MO8tSDtgcYoF89wgF/88
13
+ Q/Eski0FckQ+znfESZcFcpzs2sPDoYW6SNVX4tZcNty85Y6WBHfHKio14x63PeyC
14
+ CG1gMwIDAQABozkwNzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU
15
+ SuMc0x24Xshtaa8SQVkBr2Z5WzIwDQYJKoZIhvcNAQELBQADggGBABI62ey5lDWm
16
+ j8jsTQaNBMBb7LbKS3XkBwyL1UNGEwlicp399rJDeuNgVSHqBCZ/nE4yleNjgW9Y
17
+ N6DKdxXCvXmFUoHvUINeAvThHxmlEGhSfXl1x55xHEig0rEP4jgdjnYdQI4fqeOM
18
+ zIDN0F3ruArke4Y62SQgWVN7vspdAA41hBIRl1vsvs0KWG8DIVQ8cmR+TG6zjOmK
19
+ iUiUZGNPnStV1O1xW83c+Ba4Am0krP6/forxDPhRvTehqkVYvMdPfYcICNb5IM58
20
+ 5vaYvuj5AtfZuZWN0F/0KtSphhNC17rh6h8QmuYdMJ1clvlrkiEaO1UjPVwjgtxC
21
+ x/ARh5X6q0/YTXl4r7EfSJEeqW7qhgu9EUj3mGIPHYdX0Dqih/NgK20F3GvQtTfB
22
+ 7sse0duKBHXONYVwKAsceUVvqtCfyF608bCilCkbhw3QaInRj+BkRQKJ5Tuj2PZR
23
+ 76a/hwaV2wWn5RtAFBaUQzQdo/xTqJ7IkwaFjOZe/QyvyoRjCK9Rdg==
24
+ -----END CERTIFICATE-----
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.26.1
4
+ version: 0.28.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -9,32 +9,28 @@ bindir: bin
9
9
  cert_chain:
10
10
  - |
11
11
  -----BEGIN CERTIFICATE-----
12
- MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
13
- ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
14
- CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
15
- MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
16
- MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
17
- bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
18
- igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
19
- 9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
20
- sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
21
- e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
22
- XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
23
- RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
24
- tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
25
- zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
26
- xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
27
- BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
28
- aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
29
- aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
30
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
38
34
  -----END CERTIFICATE-----
39
35
  date: 1980-01-02 00:00:00.000000000 Z
40
36
  dependencies:
@@ -265,6 +261,7 @@ files:
265
261
  - public/playback.js
266
262
  - public/recording_controls.js
267
263
  - readme.md
264
+ - release.cert
268
265
  - releases.md
269
266
  - templates/code.xrb
270
267
  - templates/default.xrb
@@ -295,7 +292,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
295
292
  - !ruby/object:Gem::Version
296
293
  version: '0'
297
294
  requirements: []
298
- rubygems_version: 4.0.16
295
+ rubygems_version: 4.0.21
299
296
  specification_version: 4
300
297
  summary: A web-based presentation tool built with Lively.
301
298
  test_files: []
metadata.gz.sig CHANGED
Binary file