animate_it 0.2.0 → 0.3.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: fb4e829854a53280979963178429b03b60d9deaf1350b26f06d5f30a69cc44fb
4
- data.tar.gz: c1b96fbeb407a50e073a7e2e59df91d9287d3e02f6df7bdf6b2aef7053628e45
3
+ metadata.gz: 95bcfafc0a059c64d5f3a4097a308ba2b7b9fff58cafdb18de720926e02cb07e
4
+ data.tar.gz: 268da7e42d644633ee56bafb7528e7051280474647873d31bad585b07bd472cb
5
5
  SHA512:
6
- metadata.gz: a4cfbbca87fe87459fed715f909d3c80743dc2ff1da0df7fc9acf82daf1e9d9319993a8733d2e417e5b46cd477becd2c7f716947f1fdf33c1707e173626c95b8
7
- data.tar.gz: 034c051d6bb5a16f98a4682caee7c0290fb80ca6a0289735b71447246592ad003eb96f869b7da3d920e30c758cceac88d87d8f3831bbe1c2739366a61ec71d10
6
+ metadata.gz: d4cd9739b39aecc9d9cedf7d159718a768b7ba26ad3c4382e6f1bd96226ba70c4d59d708c57c263124eb07d39f0ff21bf3daab91733e774ffa15457dd8f3cf9e
7
+ data.tar.gz: c6f480ba02780a0c2cff836d061247961985138916607a318e6a4311456e8627684179dd52a81d16ab8fe30b413192f9126e9c3073306799299c8a8ff5ec06ec
data/CHANGELOG.md CHANGED
@@ -6,6 +6,29 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ### Fixed
10
+ - Studio audio is now served with HTTP byte-range support (Accept-Ranges,
11
+ 206 Partial Content). Browsers refuse to seek media served without it, so
12
+ play-pause-play and scrub-then-play restarted clips from 0:00.
13
+
14
+ ## [0.3.1] - 2026-07-22
15
+
16
+ ### Changed
17
+ - Relaxed the HAML dependency from `~> 6.3` to `>= 6.3` so hosts on HAML 7 are
18
+ not forced to downgrade.
19
+
20
+ ## [0.3.0] - 2026-07-22
21
+
22
+ ### Fixed
23
+ - Studio playback started from a scrubbed position could seek the audio before
24
+ its metadata had loaded; the seek silently failed and the clip played from
25
+ 0:00 instead of the playhead offset. The seek is now deferred until
26
+ `loadedmetadata`.
27
+ - Studio playback now stops on Turbo navigation (`turbo:before-visit`,
28
+ `turbo:before-cache`) and `pagehide` — Turbo swaps the body without unloading
29
+ the window, so a detached, still-playing audio element kept sounding under
30
+ the next page.
31
+
9
32
  ## [0.2.0] - 2026-07-09
10
33
 
11
34
  ### Added
@@ -35,6 +58,8 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
35
58
  - `render_animate_it_video` executable and `animate_it:render` rake task.
36
59
  - `animate_it:install` generator.
37
60
 
38
- [Unreleased]: https://github.com/growth-constant/animate_it/compare/v0.2.0...HEAD
61
+ [Unreleased]: https://github.com/joinbuildit/animate_it/compare/v0.3.1...HEAD
62
+ [0.3.1]: https://github.com/joinbuildit/animate_it/compare/v0.3.0...v0.3.1
63
+ [0.3.0]: https://github.com/growth-constant/animate_it/compare/v0.2.0...v0.3.0
39
64
  [0.2.0]: https://github.com/growth-constant/animate_it/compare/v0.1.0...v0.2.0
40
65
  [0.1.0]: https://github.com/growth-constant/animate_it/releases/tag/v0.1.0
data/README.md CHANGED
@@ -176,6 +176,19 @@ end
176
176
  Render concurrency for the Studio's background renderer is tunable via
177
177
  `ANIMATE_IT_RENDER_CONCURRENCY` and `ANIMATE_IT_RENDER_STARTS_PER_SECOND`.
178
178
 
179
+ ## Claude skill
180
+
181
+ If you use [Claude Code](https://claude.com/claude-code), this repo ships an
182
+ Agent Skill that teaches Claude how to author AnimateIt compositions — the DSL,
183
+ the render pipeline, embedding, rendering your app's real partials, motion
184
+ craft, and the gotchas. Install it into your project's `.claude/skills/`:
185
+
186
+ ```bash
187
+ npx animate-it-skills # copies the skill into ./.claude/skills
188
+ ```
189
+
190
+ The skill source lives in [`skills/`](skills/).
191
+
179
192
  ## Development
180
193
 
181
194
  ```bash
@@ -10,12 +10,25 @@ module AnimateIt
10
10
  file_path = resolve_audio_path(segment.source[:path])
11
11
  return head :not_found unless file_path && File.file?(file_path)
12
12
 
13
- send_file file_path, type: Mime::Type.lookup_by_extension(File.extname(file_path).delete(".")),
14
- disposition: "inline"
13
+ serve_with_byte_ranges(file_path)
15
14
  end
16
15
 
17
16
  private
18
17
 
18
+ # send_file ignores Range headers, and browsers refuse to seek media
19
+ # served without byte-range support — every currentTime seek snaps back
20
+ # to 0:00, so the Studio player restarts clips from the beginning.
21
+ # Rack::Files implements range semantics (Accept-Ranges, 206/416).
22
+ def serve_with_byte_ranges(file_path)
23
+ status, headers, body = Rack::Files.new(File.dirname(file_path)).serving(request, file_path)
24
+ headers["content-type"] = Mime::Type.lookup_by_extension(File.extname(file_path).delete(".")).to_s
25
+ headers["content-disposition"] = "inline"
26
+ headers["accept-ranges"] = "bytes"
27
+ self.status = status
28
+ self.response_body = body
29
+ headers.each { |key, value| response.headers[key] = value }
30
+ end
31
+
19
32
  def resolve_audio_path(path)
20
33
  return nil if path.blank?
21
34
 
@@ -141,6 +141,21 @@
141
141
  playheads.forEach((el) => { el.style.left = `${pct}%`; });
142
142
  };
143
143
 
144
+ // Seeking before the clip's metadata has loaded doesn't stick — it would
145
+ // start at 0:00 regardless of the scrubbed position. Defer the seek
146
+ // (recomputed from the then-current frame) until metadata is available.
147
+ const startAudio = (el) => {
148
+ const begin = () => {
149
+ if (timer === null) return; // stopped before metadata arrived
150
+ const { start, len } = audioWindow(el);
151
+ if (frame < start || frame >= start + len) return; // playhead left the window
152
+ el.currentTime = (frame - start) / fps;
153
+ el.play().catch(() => {});
154
+ };
155
+ if (el.readyState >= 1) begin();
156
+ else el.addEventListener("loadedmetadata", begin, { once: true });
157
+ };
158
+
144
159
  // Audio is audible only while actually playing — seeking/scrubbing is
145
160
  // silent. Each element plays only inside its own window, seeked to the
146
161
  // matching offset, so you hear exactly what belongs at the current frame.
@@ -153,10 +168,7 @@
153
168
  // playing, leave it on its own real-time clock: re-seeking every
154
169
  // frame to chase the setInterval-driven frame counter (which lags
155
170
  // real time) is what chops the audio.
156
- if (el.paused) {
157
- el.currentTime = (currentFrame - start) / fps;
158
- el.play().catch(() => {});
159
- }
171
+ if (el.paused) startAudio(el);
160
172
  } else if (!el.paused) {
161
173
  el.pause();
162
174
  }
@@ -264,6 +276,13 @@
264
276
  }
265
277
  });
266
278
 
279
+ // Turbo Drive swaps the body without unloading the window: detached
280
+ // <audio> elements keep playing and the playback loop keeps ticking.
281
+ // Stop this page's playback before navigating away or caching.
282
+ document.addEventListener("turbo:before-visit", stop, { once: true });
283
+ document.addEventListener("turbo:before-cache", stop, { once: true });
284
+ window.addEventListener("pagehide", stop, { once: true });
285
+
267
286
  window.AnimateItStudio = {
268
287
  setFrame,
269
288
  currentFrame: () => frame
@@ -1,3 +1,3 @@
1
1
  module AnimateIt
2
- VERSION = "0.2.0".freeze
2
+ VERSION = "0.3.1".freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: animate_it
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - growth-constant
@@ -13,14 +13,14 @@ dependencies:
13
13
  name: haml
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - "~>"
16
+ - - ">="
17
17
  - !ruby/object:Gem::Version
18
18
  version: '6.3'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - "~>"
23
+ - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '6.3'
26
26
  - !ruby/object:Gem::Dependency