product_tours 1.2.0 → 1.3.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: 9645f38a9a88cddf4cb0d47618fdda9501208b758c057c74a857ccba51eefd21
4
- data.tar.gz: 3279fad69985b9fe6c3716bbe1f052854f63f6dbd4e6758b9da6a5838c9a6aba
3
+ metadata.gz: 7c62b8b42042732f293443f7cdfc154b1679fd7b0ba0c6774aca4ade157a54fa
4
+ data.tar.gz: 88dc14f258d8e5bd14260fe40dbf77c0c58e8adc7816f26a6bb31c4cbd4122b2
5
5
  SHA512:
6
- metadata.gz: 752b2ca7338b6387836d595641bb004ccfe50eb2dcbfdfea132547b0e3f929ae13b3d69d262e52c94f39d5600b4a986795ef200effc92bd4c0fa57c24fee27c3
7
- data.tar.gz: 5bfec7949de1cea9beebb1d95ec9b28867d999827587303cd2c486b0c1aefc08feda7146d7c4ddd05528324d558ffa854f43f12c9d62f05c4b756cc450303bd8
6
+ metadata.gz: 705534e0de88c34651aa56751e663f4a2d31830b935d09e65a0d8a3c86bc56026630e55e009c5ad82489a6684c693a6395a5f7b9c855c263bee33295fd6510ab
7
+ data.tar.gz: 4804cc836bac346588ccbab6c547b205fb6e17d45163b02d34cab395d6c272cabce2870793abaaca128691973845ff1ab19b0565f32cfe978ddd4e7a875a124c
data/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.3.0] - 2026-11-09
6
+
7
+ - **A direct video can be a path on the host app**, such as
8
+ `/tutorials/billing.en.mp4`, like the poster and captions already could. The
9
+ tour then plays from whichever environment holds the record, rather than
10
+ pointing a copied staging or development database at production media.
11
+ Protocol-relative (`//host/…`) and bare relative paths are still rejected.
12
+ - The dashboard's video field is now a text field, since the browser's URL
13
+ field refuses a path. It costs the browser's own URL check; the model's
14
+ validation still decides.
15
+
5
16
  ## [1.2.0] - 2026-11-03
6
17
 
7
18
  - **Direct and uploaded videos can carry a poster and captions.** A tutorial
@@ -242,7 +253,8 @@
242
253
  - Removed generic tutorial duplication now that translations provide the only
243
254
  intentional content-copying workflow.
244
255
 
245
- [Unreleased]: https://github.com/yshmarov/product_tours/compare/v1.2.0...HEAD
256
+ [Unreleased]: https://github.com/yshmarov/product_tours/compare/v1.3.0...HEAD
257
+ [1.3.0]: https://github.com/yshmarov/product_tours/compare/v1.2.0...v1.3.0
246
258
  [1.2.0]: https://github.com/yshmarov/product_tours/compare/v1.1.0...v1.2.0
247
259
  [1.1.0]: https://github.com/yshmarov/product_tours/compare/v1.0.0...v1.1.0
248
260
  [1.0.0]: https://github.com/yshmarov/product_tours/compare/v0.3.3...v1.0.0
data/README.md CHANGED
@@ -144,7 +144,7 @@ Paste any supported HTTPS URL:
144
144
  | Loom | Share and embed links |
145
145
  | Tella | Video links |
146
146
  | Voomly | Share, video, and embed links |
147
- | Direct | URLs ending in `.mp4` or `.webm` |
147
+ | Direct | HTTPS URLs or paths on your app (`/videos/intro.mp4`) ending in `.mp4` or `.webm` |
148
148
 
149
149
  YouTube, Vimeo, and Loom metadata comes from their oEmbed endpoints. Metadata is
150
150
  best-effort: a timeout never prevents you from saving a valid URL. YouTube uses
@@ -110,7 +110,8 @@ module ProductTours
110
110
  def video_url_is_supported
111
111
  return if video_url.blank? || VideoResolver.resolve(video_url)
112
112
 
113
- errors.add(:video_url, 'must be an HTTPS YouTube, Vimeo, Loom, Tella, Voomly, MP4, or WebM URL')
113
+ errors.add(:video_url,
114
+ 'must be an HTTPS YouTube, Vimeo, Loom, Tella, Voomly, MP4, or WebM URL, or an MP4 or WebM path')
114
115
  end
115
116
 
116
117
  def action_url_is_safe
@@ -89,9 +89,9 @@
89
89
  <div data-video-source-fields="url" <%= "hidden" unless video_source == "url" %>>
90
90
  <div class="field">
91
91
  <%= form.label :video_url, t("product_tours.dashboard.video_url", default: "Video URL") %>
92
- <%= form.url_field :video_url, placeholder: "https://www.loom.com/share/...",
93
- disabled: video_source != "url", data: { video_preview_input: true } %>
94
- <small><%= t("product_tours.dashboard.video_hint", default: "YouTube, Vimeo, Loom, Tella, Voomly, or a direct MP4/WebM URL.") %></small>
92
+ <%= form.text_field :video_url, placeholder: "https://www.loom.com/share/...",
93
+ disabled: video_source != "url", data: { video_preview_input: true } %>
94
+ <small><%= t("product_tours.dashboard.video_hint", default: "YouTube, Vimeo, Loom, Tella, Voomly, or a direct MP4/WebM URL or path.") %></small>
95
95
  </div>
96
96
  <div class="form-video-preview" data-video-preview
97
97
  data-endpoint="<%= video_preview_tours_path %>"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ProductTours
4
- VERSION = '1.2.0'
4
+ VERSION = '1.3.0'
5
5
  end
@@ -33,6 +33,8 @@ module ProductTours
33
33
  end
34
34
 
35
35
  def resolve
36
+ return relative if @url.start_with?('/') && !@url.start_with?('//')
37
+
36
38
  uri = URI.parse(@url)
37
39
  return unless uri.is_a?(URI::HTTPS) && uri.host.present? && uri.userinfo.nil?
38
40
 
@@ -110,6 +112,14 @@ module ProductTours
110
112
  Result.new(kind: 'iframe', provider: 'voomly', provider_id: id, url: embed)
111
113
  end
112
114
 
115
+ # A path on the host app's own origin, so a tour plays from whichever environment holds the record.
116
+ def relative
117
+ uri = URI.parse(@url)
118
+ direct(uri) if uri.host.nil? && uri.scheme.nil?
119
+ rescue URI::InvalidURIError
120
+ nil
121
+ end
122
+
113
123
  def direct(uri)
114
124
  return unless uri.path.match?(/\.(?:mp4|webm)\z/i)
115
125
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: product_tours
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Shmarov