turbo_desktop-rails 0.1.1 → 0.2.3

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: d6f899e2fab9c576ab27546066a9b1b28d38a7ec488b0ead48b3d42c2586007d
4
- data.tar.gz: 16d18b4814b4fbc97355affe91a24b147e6fec059e818065234c39516cbaa784
3
+ metadata.gz: 70be4836df6cf6ff273081949ad0ee0c42bf155357525f73467d47000d18dfe0
4
+ data.tar.gz: 67f724b78eb69afa13d18bcf7844c0e63d56c2a6b9fdd3738f0e7a7466c72135
5
5
  SHA512:
6
- metadata.gz: ae547aa2c461c50bb2b920d581c02219ea0a981b3a2d7720a4c00b6b6f234172234913319fda4e22ab47bbf5aa6eaf0376286c0f4ffc984fb8d66ed33bb82587
7
- data.tar.gz: 5ed76a338f38a62c6f850aa76def83eaf88226ba16eb69ba7b690a62db7e0c2a4f0e9a9484b2b050d0c557b1e5f2b0b0fa2686cc36f2ae2f9fffb16ddcb7c1d5
6
+ metadata.gz: 6b136e03d0f7d1b15abfc88b322139abbfd5575f5a3ac6e0353b38223773b083eb9dbc3d7eb139811b9b2a92cbff40fc3dede0e577e3eeef6ba942f747f78968
7
+ data.tar.gz: 4c536312a7cfe78b51897f70a8117f3fa134dca8be0759add4ec65280af2d7326974cd7b8398fc78eabd21e280d97f26862f162a92d294f5075b09242ef45ade
data/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.3 (2026-09-27)
4
+
5
+ ### Fixed
6
+
7
+ - The Dev Inspector now loads in apps with forgery protection on, which is
8
+ every app that uses `load_defaults`. Its modules are fetched with `import()`,
9
+ a plain GET for JavaScript, which Rails refused with a 422 as a cross-origin
10
+ script embed. The inspector's controller serves a fixed list of public,
11
+ static files and no longer takes part in that check.
12
+
13
+ ### Changed
14
+
15
+ - The README shows the Gemfile line pinned (`"~> 0.2"`), states Ruby >= 3.2 to
16
+ match the gemspec, and quotes a current User-Agent.
17
+
18
+ ## 0.2.2 (2026-09-27)
19
+
20
+ Version aligned with the desktop shell's 0.2.2 release. The CLI now scaffolds
21
+ new apps with `gem "turbo_desktop-rails", "~> 0.2"`; it previously wrote
22
+ `"~> 0.1"`, which resolved to 0.1.1. First 0.2.x version published to RubyGems.
23
+ No gem-side API changes.
24
+
25
+ ## 0.2.1 (2026-07-29)
26
+
27
+ Version aligned with the desktop shell's 0.2.1 release, which fixes bridge
28
+ events (shell/sudo output streaming, drag & drop, component onReceive) never
29
+ reaching pages loaded from the app server. No gem-side API changes.
30
+
31
+ ## 0.2.0 (2026-07-29)
32
+
33
+ Version aligned with the desktop shell's 0.2.0 release (server auto-start,
34
+ Windows support, cross-platform sudo, drag & drop, file associations,
35
+ clipboard, launch-at-login). No gem-side API changes.
36
+
3
37
  ## 0.1.1 (2026-07-27)
4
38
 
5
39
  ### Added
data/README.md CHANGED
@@ -9,7 +9,7 @@ This gem gives your Rails app awareness of the Turbo Desktop shell, exactly like
9
9
  Add to your Gemfile:
10
10
 
11
11
  ```ruby
12
- gem "turbo_desktop-rails"
12
+ gem "turbo_desktop-rails", "~> 0.2"
13
13
  ```
14
14
 
15
15
  Then run:
@@ -23,7 +23,7 @@ rails generate turbo_desktop:install
23
23
 
24
24
  ### Detection
25
25
 
26
- The gem detects Turbo Desktop requests via the User-Agent header (`Turbo Desktop/0.0.1 (macOS; aarch64)`).
26
+ The gem detects Turbo Desktop requests via the User-Agent header (`Turbo Desktop/0.2.3 (macOS; aarch64)`).
27
27
 
28
28
  ```ruby
29
29
  # In controllers
@@ -56,6 +56,33 @@ turbo_desktop_arch # => "aarch64", "x86_64", or nil
56
56
  ) %>
57
57
  ```
58
58
 
59
+ ### Desktop-only templates
60
+
61
+ Requests from the desktop app are marked with a Rails variant, so a whole
62
+ template can be written for it rather than branching inside a shared one:
63
+
64
+ ```
65
+ app/views/orders/show.html.erb # everyone
66
+ app/views/orders/show.html+desktop.erb # the desktop app
67
+ ```
68
+
69
+ Layouts work the same way — `app/views/layouts/application.html+desktop.erb`.
70
+ Rails falls back to the plain template wherever no variant exists, so this costs
71
+ nothing until you add one.
72
+
73
+ The block helpers above are still the right tool for a button or a nav bar. Reach
74
+ for a variant when the whole page differs.
75
+
76
+ Rename it, or turn it off, in the initializer:
77
+
78
+ ```ruby
79
+ TurboDesktop.configure do |config|
80
+ config.variant = :desktop # nil leaves variants alone
81
+ end
82
+ ```
83
+
84
+ It is added to any variants you have already set rather than replacing them.
85
+
59
86
  ### Path Configuration
60
87
 
61
88
  The gem mounts a path configuration endpoint at `/turbo-desktop/path-configuration.json`:
@@ -76,7 +103,7 @@ end
76
103
 
77
104
  ## Requirements
78
105
 
79
- - Ruby >= 3.1.0
106
+ - Ruby >= 3.2
80
107
  - Rails >= 7.0
81
108
  - turbo-rails >= 1.0
82
109
 
@@ -10,6 +10,12 @@ module TurboDesktop
10
10
  # path traversal). Enabled implicitly by mounting the engine; the meta-tag
11
11
  # helper points the shell at these URLs.
12
12
  class InspectorAssetsController < ActionController::Base
13
+ # These are public, static modules fetched by import(), which is a plain
14
+ # GET for JavaScript. Rails reads that as a cross-origin <script> embed and
15
+ # refuses it with a 422. Nothing here depends on the session or carries
16
+ # per-user data, so there is nothing for that check to protect.
17
+ skip_forgery_protection
18
+
13
19
  ASSET_ROOT = TurboDesktop::Engine.root.join("lib/turbo_desktop/inspector_assets").freeze
14
20
 
15
21
  # Relative paths (as requested by the browser) → allowed. Anything else 404s.
@@ -17,6 +17,17 @@ TurboDesktop.configure do |config|
17
17
  # presentation. Enable it in development only:
18
18
  config.inspector_enabled = Rails.env.development?
19
19
 
20
+ # Requests from the desktop app are marked with a Rails variant, so you can
21
+ # write a whole template for it instead of branching inside a shared one:
22
+ #
23
+ # app/views/orders/show.html.erb # everyone
24
+ # app/views/orders/show.html+desktop.erb # the desktop app
25
+ #
26
+ # Layouts work the same way (layouts/application.html+desktop.erb), and Rails
27
+ # falls back to the plain template wherever no variant exists. Rename it, or
28
+ # set it to nil to leave variants alone.
29
+ # config.variant = :desktop
30
+
20
31
  config.path_configuration = {
21
32
  settings: {
22
33
  screenshots_enabled: false
@@ -1,11 +1,15 @@
1
1
  module TurboDesktop
2
2
  class Configuration
3
- attr_accessor :path_configuration, :user_agent_pattern, :inspector_enabled, :inspector_mount_path
3
+ attr_accessor :path_configuration, :user_agent_pattern, :inspector_enabled,
4
+ :inspector_mount_path, :variant
4
5
 
5
6
  def initialize
6
7
  @path_configuration = default_path_configuration
7
8
  @user_agent_pattern = /Turbo Desktop/
8
9
  @inspector_enabled = false
10
+ # Rails variant set on requests from the desktop app, so views can be
11
+ # written as show.html+desktop.erb. Set to nil to leave variants alone.
12
+ @variant = :desktop
9
13
  # Where the engine is mounted; the inspector meta tag advertises assets
10
14
  # under this prefix. Override if you mount the engine elsewhere.
11
15
  @inspector_mount_path = "/turbo-desktop"
@@ -11,6 +11,23 @@ module TurboDesktop
11
11
 
12
12
  included do
13
13
  helper_method :turbo_desktop_app?, :turbo_desktop_platform, :turbo_desktop_arch
14
+
15
+ before_action :set_turbo_desktop_variant
16
+ end
17
+
18
+ # The variant list a request should end up with, or nil to leave it alone.
19
+ #
20
+ # Kept separate from the controller so the decision can be exercised on its
21
+ # own. Adds to whatever variants are already set rather than replacing them:
22
+ # an app may well be using variants for something else.
23
+ def self.variant_for(current:, desktop:, configured:)
24
+ return nil if configured.blank? || !desktop
25
+
26
+ current = Array(current).map(&:to_sym)
27
+ configured = configured.to_sym
28
+ return nil if current.include?(configured)
29
+
30
+ current + [ configured ]
14
31
  end
15
32
 
16
33
  # Returns true if the request is from a Turbo Desktop app.
@@ -31,6 +48,27 @@ module TurboDesktop
31
48
  end
32
49
  end
33
50
 
51
+ # Mark desktop requests with a Rails variant, so a whole template can be
52
+ # written for the desktop app instead of branching inside a shared one:
53
+ #
54
+ # app/views/orders/show.html.erb # everyone
55
+ # app/views/orders/show.html+desktop.erb # the desktop app
56
+ #
57
+ # Layouts pick it up too — layouts/application.html+desktop.erb. Rails falls
58
+ # back to the plain template wherever no variant exists, so this costs
59
+ # nothing until you add one.
60
+ #
61
+ # Set config.variant to nil to turn it off.
62
+ def set_turbo_desktop_variant
63
+ variant = TurboDesktop::Detection.variant_for(
64
+ current: request.variant,
65
+ desktop: turbo_desktop_app?,
66
+ configured: TurboDesktop.configuration.variant
67
+ )
68
+
69
+ request.variant = variant if variant
70
+ end
71
+
34
72
  # Returns the architecture: "aarch64", "x86_64", or nil.
35
73
  def turbo_desktop_arch
36
74
  return nil unless turbo_desktop_app?
@@ -1,3 +1,3 @@
1
1
  module TurboDesktop
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.3"
3
3
  end
@@ -26,12 +26,20 @@ module TurboDesktop
26
26
  end
27
27
  end
28
28
 
29
+ # Attribute name fragments we are willing to build a data-* attribute from.
30
+ # Values are escaped by the tag helpers, but names are interpolated, so they
31
+ # are restricted rather than escaped.
32
+ BRIDGE_NAME_PATTERN = /\A[a-zA-Z0-9_-]+\z/
33
+
29
34
  # Renders a bridge component data attribute.
30
35
  #
31
36
  # <%= tag.button "Export", **turbo_desktop_bridge("menu-item", title: "Export PDF", shortcut: "Cmd+E") %>
32
37
  def turbo_desktop_bridge(component, **options)
33
- attrs = { "data-turbo-desktop-bridge" => component }
38
+ validate_bridge_name!(component, "component")
39
+
40
+ attrs = { "data-turbo-desktop-bridge" => component.to_s }
34
41
  options.each do |key, value|
42
+ validate_bridge_name!(key, "option name")
35
43
  attrs["data-turbo-desktop-bridge-#{key}"] = value.to_s
36
44
  end
37
45
  attrs
@@ -73,5 +81,15 @@ module TurboDesktop
73
81
  def turbo_desktop_inspector_url
74
82
  "#{TurboDesktop.configuration.inspector_mount_path.chomp("/")}/inspector.js"
75
83
  end
84
+
85
+ private
86
+
87
+ def validate_bridge_name!(name, label)
88
+ return if name.to_s.match?(BRIDGE_NAME_PATTERN)
89
+
90
+ raise ArgumentError,
91
+ "turbo_desktop_bridge #{label} #{name.inspect} may only contain " \
92
+ "letters, numbers, hyphens and underscores"
93
+ end
76
94
  end
77
95
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbo_desktop-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
- - RaiderHQ
7
+ - aguspe
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
10
+ date: 2026-09-27 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails
@@ -80,14 +80,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
80
80
  requirements:
81
81
  - - ">="
82
82
  - !ruby/object:Gem::Version
83
- version: 3.3.0
83
+ version: 3.2.0
84
84
  required_rubygems_version: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - ">="
87
87
  - !ruby/object:Gem::Version
88
88
  version: '0'
89
89
  requirements: []
90
- rubygems_version: 4.0.6
90
+ rubygems_version: 3.6.2
91
91
  specification_version: 4
92
92
  summary: Turbo Native for Desktop — Rails integration
93
93
  test_files: []