studio-engine 0.5.4 → 0.5.6
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 +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +3 -1
- data/app/controllers/concerns/studio/error_handling.rb +1 -1
- data/app/controllers/studio/local_emails_controller.rb +108 -0
- data/app/models/session_context.rb +2 -0
- data/app/models/studio/email_delivery.rb +2 -1
- data/app/views/studio/local_emails/index.html.erb +100 -0
- data/lib/studio/version.rb +1 -1
- data/lib/studio.rb +33 -0
- metadata +5 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7dde49831782823962437f0efab2c8334fe0efc4376873ee9b9c4c2486aa4d1f
|
|
4
|
+
data.tar.gz: c9cb4750ad4298869623db8a98e93662b101a1b2ce055fb779d15a3095ba1529
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb82ea299130783431b2dfea4fac8ddece0728b7f49d6ea15c1cff49d819fb6687a5a03f4eb80d4a0f8cb590cf78e45ccb1cdec410bc042ba6caed95bdde24b0
|
|
7
|
+
data.tar.gz: e93039393ac8e344f7eef097ce55629f41cdefdb8f48be418ad3f8a7228e1c01363a3646ecd23708cffa182d3e905e17a8c1b2f8eccbfe6b97a3c9ef203a855b
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,19 @@ The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This pro
|
|
|
6
6
|
|
|
7
7
|
No entries yet.
|
|
8
8
|
|
|
9
|
+
## v0.5.6 (2026-06-14)
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **`Studio.wallet_address_method` / `Studio.user_wallet_address(user)`** —
|
|
13
|
+
shared wallet-address adapter for SSO/session awareness. Defaults support
|
|
14
|
+
`wallet_address` and `solana_address`; apps can configure another method.
|
|
15
|
+
|
|
16
|
+
## v0.5.5 (2026-06-14)
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- **Local email inbox** at `/_studio/local_emails` for non-production localhost requests. It lists recent outbox rows and exposes local proof links for magic-link, email-verification, wallet-export, and email-change emails.
|
|
20
|
+
- **`Studio.local_email_capture?`** — shared capture switch for local/worktree stacks. `LOCAL_EMAIL_CAPTURE=1` or `AGENT_WORKTREE=1` records delivery rows without enqueueing external sends.
|
|
21
|
+
|
|
9
22
|
## v0.5.4 (2026-06-14)
|
|
10
23
|
|
|
11
24
|
### Changed
|
data/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Shared Rails engine for McRitchie apps. Provides authentication, error handling,
|
|
|
11
11
|
gem "studio-engine", "~> 0.5"
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Then `bundle install`. The current release is **v0.5.
|
|
14
|
+
Then `bundle install`. The current release is **v0.5.5**; see [`CHANGELOG.md`](./CHANGELOG.md) for the history.
|
|
15
15
|
|
|
16
16
|
> Published to RubyGems as of v0.4.0 (2026-05-17). New installs should use the RubyGems form, which the consumer Rails apps (`mcritchie-studio`, `turf-monster`) already use.
|
|
17
17
|
|
|
@@ -64,6 +64,8 @@ end
|
|
|
64
64
|
|
|
65
65
|
This draws the enabled auth routes (`/login`, `/signup`, `/logout`, magic-link request/confirm/consume routes, Solana routes), OAuth callbacks, optional SSO routes, `/error_logs`, and `/admin/theme`. Magic-link emails point at the inert GET confirmation route; the single-use token is consumed only by the CSRF-protected POST to `magic_link_consume_path`.
|
|
66
66
|
|
|
67
|
+
In non-production local requests, this also draws `/_studio/local_emails`, a local email inbox for agent/worktree proof flows. Set `LOCAL_EMAIL_CAPTURE=1` or run with `AGENT_WORKTREE=1` to record outbox rows without sending real email.
|
|
68
|
+
|
|
67
69
|
## Overriding Views
|
|
68
70
|
|
|
69
71
|
This is a non-isolated engine -- app views at the same path automatically override engine views. For example, placing `app/views/sessions/new.html.erb` in the consuming app replaces the engine's login page.
|
|
@@ -49,7 +49,7 @@ module Studio
|
|
|
49
49
|
session[:sso_name] = user.try(:name)
|
|
50
50
|
session[:sso_provider] = user.provider
|
|
51
51
|
session[:sso_uid] = user.uid
|
|
52
|
-
session[:sso_wallet] =
|
|
52
|
+
session[:sso_wallet] = Studio.user_wallet_address(user)
|
|
53
53
|
session[:sso_source] = Studio.app_name
|
|
54
54
|
session[:sso_logo] = Studio.sso_logo
|
|
55
55
|
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
module Studio
|
|
2
|
+
class LocalEmailsController < ApplicationController
|
|
3
|
+
skip_before_action :require_authentication, raise: false
|
|
4
|
+
layout false
|
|
5
|
+
|
|
6
|
+
before_action :require_local_development!
|
|
7
|
+
|
|
8
|
+
def index
|
|
9
|
+
@deliveries = delivery_records.map { |record| serialize_delivery(record) }
|
|
10
|
+
|
|
11
|
+
respond_to do |format|
|
|
12
|
+
format.html
|
|
13
|
+
format.json do
|
|
14
|
+
render json: {
|
|
15
|
+
capture_enabled: Studio.local_email_capture?,
|
|
16
|
+
inbox_url: request.original_url.sub(/\.json\z/, ""),
|
|
17
|
+
deliveries: @deliveries
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def require_local_development!
|
|
26
|
+
head :not_found if Rails.env.production? || !request.local?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def delivery_records
|
|
30
|
+
klass = delivery_class
|
|
31
|
+
return [] unless klass
|
|
32
|
+
|
|
33
|
+
klass.recent.limit(50)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def delivery_class
|
|
37
|
+
if Object.const_defined?(:EmailDelivery, false)
|
|
38
|
+
return Object.const_get(:EmailDelivery, false)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if Studio.const_defined?(:EmailDelivery, false) && Studio::EmailDelivery.respond_to?(:available?) && Studio::EmailDelivery.available?
|
|
42
|
+
return Studio::EmailDelivery
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def serialize_delivery(record)
|
|
49
|
+
args = deserialize_args(record.args)
|
|
50
|
+
kwargs = deserialize_kwargs(record.kwargs)
|
|
51
|
+
{
|
|
52
|
+
id: record.id,
|
|
53
|
+
email_key: record.email_key,
|
|
54
|
+
mailer: record.mailer,
|
|
55
|
+
action: record.action,
|
|
56
|
+
to: record.to,
|
|
57
|
+
sent: record.sent?,
|
|
58
|
+
sent_at: record.sent_at,
|
|
59
|
+
error: record.error,
|
|
60
|
+
created_at: record.created_at,
|
|
61
|
+
action_url: local_action_url(record, args),
|
|
62
|
+
args_preview: args.map { |arg| preview_value(arg) },
|
|
63
|
+
kwargs_preview: kwargs.transform_values { |value| preview_value(value) }
|
|
64
|
+
}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def deserialize_args(value)
|
|
68
|
+
ActiveJob::Arguments.deserialize(value || [])
|
|
69
|
+
rescue StandardError
|
|
70
|
+
[]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def deserialize_kwargs(value)
|
|
74
|
+
deserialized = ActiveJob::Arguments.deserialize([value || {}]).first
|
|
75
|
+
deserialized.respond_to?(:symbolize_keys) ? deserialized.symbolize_keys : {}
|
|
76
|
+
rescue StandardError
|
|
77
|
+
{}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def local_action_url(record, args)
|
|
81
|
+
token = args[1].to_s
|
|
82
|
+
return if token.empty?
|
|
83
|
+
|
|
84
|
+
path =
|
|
85
|
+
case record.email_key
|
|
86
|
+
when /#magic_link\z/
|
|
87
|
+
"/magic_link/#{ERB::Util.url_encode(token)}"
|
|
88
|
+
when /#email_verification\z/
|
|
89
|
+
"/email_verification/#{ERB::Util.url_encode(token)}"
|
|
90
|
+
when /#wallet_export\z/
|
|
91
|
+
"/account/wallet/export/#{ERB::Util.url_encode(token)}"
|
|
92
|
+
when /#email_change_confirmation\z/
|
|
93
|
+
"/account/email/confirm/#{ERB::Util.url_encode(token)}"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
"#{request.base_url}#{path}" if path
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def preview_value(value)
|
|
100
|
+
case value
|
|
101
|
+
when String, Numeric, TrueClass, FalseClass, NilClass
|
|
102
|
+
value
|
|
103
|
+
else
|
|
104
|
+
value.respond_to?(:to_global_id) ? value.to_global_id.to_s : value.to_s
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -63,6 +63,8 @@ class SessionContext
|
|
|
63
63
|
|
|
64
64
|
# Primary wallet address (web3 preferred), or nil when logged out / wallet-less.
|
|
65
65
|
def address
|
|
66
|
+
return Studio.user_wallet_address(user) if defined?(Studio) && Studio.respond_to?(:user_wallet_address)
|
|
67
|
+
|
|
66
68
|
return nil unless user.respond_to?(:solana_address)
|
|
67
69
|
user.solana_address
|
|
68
70
|
end
|
|
@@ -23,12 +23,13 @@ module Studio
|
|
|
23
23
|
args: ActiveJob::Arguments.serialize(args),
|
|
24
24
|
kwargs: ActiveJob::Arguments.serialize([kwargs]).first
|
|
25
25
|
)
|
|
26
|
-
Studio::EmailDeliveryJob.perform_later(record.id)
|
|
26
|
+
Studio::EmailDeliveryJob.perform_later(record.id) unless Studio.local_email_capture?
|
|
27
27
|
record
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def deliver_now!
|
|
31
31
|
return if sent?
|
|
32
|
+
return update!(error: "local email capture enabled; not sent") if Studio.local_email_capture?
|
|
32
33
|
|
|
33
34
|
pos = ActiveJob::Arguments.deserialize(args)
|
|
34
35
|
kw = ActiveJob::Arguments.deserialize([kwargs]).first.symbolize_keys
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>Local Emails - <%= Studio.app_name %></title>
|
|
7
|
+
<style>
|
|
8
|
+
* { box-sizing: border-box; }
|
|
9
|
+
body {
|
|
10
|
+
margin: 0;
|
|
11
|
+
background: #10101f;
|
|
12
|
+
color: #f8fafc;
|
|
13
|
+
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
14
|
+
}
|
|
15
|
+
main { width: min(1180px, calc(100% - 32px)); margin: 32px auto; }
|
|
16
|
+
header { display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; margin-bottom: 24px; }
|
|
17
|
+
h1 { margin: 0 0 8px; font-size: 28px; line-height: 1.2; }
|
|
18
|
+
p { margin: 0; color: #b6bdd3; }
|
|
19
|
+
.pill { display: inline-flex; align-items: center; border: 1px solid #3b3b62; border-radius: 999px; padding: 6px 12px; color: #d8dcf0; background: #18182b; font-size: 13px; white-space: nowrap; }
|
|
20
|
+
.notice { margin-bottom: 18px; border: 1px solid #3b3b62; border-radius: 8px; background: #18182b; padding: 14px 16px; color: #cbd2e8; }
|
|
21
|
+
table { width: 100%; border-collapse: collapse; overflow: hidden; border-radius: 8px; background: #18182b; }
|
|
22
|
+
th, td { padding: 12px 14px; border-bottom: 1px solid #2f2f4d; text-align: left; vertical-align: top; }
|
|
23
|
+
th { color: #9fa7c3; font-size: 12px; text-transform: uppercase; }
|
|
24
|
+
td { color: #eef2ff; font-size: 14px; }
|
|
25
|
+
tr:last-child td { border-bottom: 0; }
|
|
26
|
+
code { color: #c4b5fd; word-break: break-word; }
|
|
27
|
+
.muted { color: #9fa7c3; }
|
|
28
|
+
.error { color: #fda4af; }
|
|
29
|
+
.button { display: inline-flex; align-items: center; justify-content: center; border-radius: 8px; padding: 8px 12px; background: #4baf50; color: #fff; font-weight: 700; text-decoration: none; white-space: nowrap; }
|
|
30
|
+
.empty { border: 1px solid #3b3b62; border-radius: 8px; background: #18182b; padding: 24px; color: #cbd2e8; }
|
|
31
|
+
@media (max-width: 760px) {
|
|
32
|
+
header { display: block; }
|
|
33
|
+
.pill { margin-top: 14px; }
|
|
34
|
+
table, thead, tbody, tr, th, td { display: block; }
|
|
35
|
+
thead { display: none; }
|
|
36
|
+
tr { border-bottom: 1px solid #2f2f4d; padding: 10px 0; }
|
|
37
|
+
td { border-bottom: 0; padding: 8px 14px; }
|
|
38
|
+
td::before { content: attr(data-label); display: block; color: #9fa7c3; font-size: 12px; text-transform: uppercase; margin-bottom: 4px; }
|
|
39
|
+
}
|
|
40
|
+
</style>
|
|
41
|
+
</head>
|
|
42
|
+
<body>
|
|
43
|
+
<main>
|
|
44
|
+
<header>
|
|
45
|
+
<div>
|
|
46
|
+
<h1>Local Emails</h1>
|
|
47
|
+
<p>Recent outbox rows for this local <%= Studio.app_name %> stack.</p>
|
|
48
|
+
</div>
|
|
49
|
+
<span class="pill">Capture <%= Studio.local_email_capture? ? "enabled" : "disabled" %></span>
|
|
50
|
+
</header>
|
|
51
|
+
|
|
52
|
+
<% if Studio.local_email_capture? %>
|
|
53
|
+
<div class="notice">This stack records email intents and does not send them to external providers.</div>
|
|
54
|
+
<% else %>
|
|
55
|
+
<div class="notice">Capture is disabled. This page still shows recent delivery rows, but this stack may send real email when workers run.</div>
|
|
56
|
+
<% end %>
|
|
57
|
+
|
|
58
|
+
<% if @deliveries.empty? %>
|
|
59
|
+
<div class="empty">No email deliveries have been recorded yet.</div>
|
|
60
|
+
<% else %>
|
|
61
|
+
<table>
|
|
62
|
+
<thead>
|
|
63
|
+
<tr>
|
|
64
|
+
<th>When</th>
|
|
65
|
+
<th>Message</th>
|
|
66
|
+
<th>To</th>
|
|
67
|
+
<th>Status</th>
|
|
68
|
+
<th>Proof URL</th>
|
|
69
|
+
</tr>
|
|
70
|
+
</thead>
|
|
71
|
+
<tbody>
|
|
72
|
+
<% @deliveries.each do |delivery| %>
|
|
73
|
+
<tr>
|
|
74
|
+
<td data-label="When"><%= delivery[:created_at]&.strftime("%Y-%m-%d %H:%M:%S") %></td>
|
|
75
|
+
<td data-label="Message"><code><%= delivery[:email_key] %></code></td>
|
|
76
|
+
<td data-label="To"><%= delivery[:to] %></td>
|
|
77
|
+
<td data-label="Status">
|
|
78
|
+
<% if delivery[:sent] %>
|
|
79
|
+
Sent
|
|
80
|
+
<% elsif delivery[:error].present? %>
|
|
81
|
+
<span class="error"><%= delivery[:error] %></span>
|
|
82
|
+
<% else %>
|
|
83
|
+
<span class="muted">Captured</span>
|
|
84
|
+
<% end %>
|
|
85
|
+
</td>
|
|
86
|
+
<td data-label="Proof URL">
|
|
87
|
+
<% if delivery[:action_url].present? %>
|
|
88
|
+
<a class="button" href="<%= delivery[:action_url] %>">Open link</a>
|
|
89
|
+
<% else %>
|
|
90
|
+
<span class="muted">No local action URL</span>
|
|
91
|
+
<% end %>
|
|
92
|
+
</td>
|
|
93
|
+
</tr>
|
|
94
|
+
<% end %>
|
|
95
|
+
</tbody>
|
|
96
|
+
</table>
|
|
97
|
+
<% end %>
|
|
98
|
+
</main>
|
|
99
|
+
</body>
|
|
100
|
+
</html>
|
data/lib/studio/version.rb
CHANGED
data/lib/studio.rb
CHANGED
|
@@ -16,6 +16,7 @@ module Studio
|
|
|
16
16
|
mattr_accessor :configure_new_user, default: ->(user) {}
|
|
17
17
|
mattr_accessor :configure_sso_user, default: ->(user) {}
|
|
18
18
|
mattr_accessor :sso_logo, default: nil
|
|
19
|
+
mattr_accessor :wallet_address_method, default: nil
|
|
19
20
|
mattr_accessor :theme_logos, default: []
|
|
20
21
|
|
|
21
22
|
# ---- Authentication ------------------------------------------------------
|
|
@@ -41,6 +42,10 @@ module Studio
|
|
|
41
42
|
# verified sending address in config/initializers/studio.rb.
|
|
42
43
|
mattr_accessor :mailer_from, default: nil
|
|
43
44
|
|
|
45
|
+
# Local/worktree email capture. nil means "auto": enabled when AGENT_WORKTREE
|
|
46
|
+
# is truthy, otherwise disabled. Production always disables capture.
|
|
47
|
+
mattr_accessor :local_email_capture, default: nil
|
|
48
|
+
|
|
44
49
|
# Theme role colors (7 roles)
|
|
45
50
|
mattr_accessor :theme_primary, default: "#8E82FE"
|
|
46
51
|
mattr_accessor :theme_dark, default: "#1A1535"
|
|
@@ -88,6 +93,26 @@ module Studio
|
|
|
88
93
|
auth_methods.include?(method.to_sym)
|
|
89
94
|
end
|
|
90
95
|
|
|
96
|
+
def self.local_email_capture?
|
|
97
|
+
return false if defined?(Rails) && Rails.respond_to?(:env) && Rails.env.production?
|
|
98
|
+
return !!local_email_capture unless local_email_capture.nil?
|
|
99
|
+
|
|
100
|
+
env_truthy?(ENV["LOCAL_EMAIL_CAPTURE"]) || env_truthy?(ENV["AGENT_WORKTREE"])
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def self.user_wallet_address(user)
|
|
104
|
+
return nil unless user
|
|
105
|
+
|
|
106
|
+
[wallet_address_method, :wallet_address, :solana_address].compact.each do |method|
|
|
107
|
+
next unless user.respond_to?(method)
|
|
108
|
+
|
|
109
|
+
value = user.public_send(method)
|
|
110
|
+
return value if value && !(value.respond_to?(:empty?) && value.empty?)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
nil
|
|
114
|
+
end
|
|
115
|
+
|
|
91
116
|
# Verifies that the host app's User model satisfies the engine's expected
|
|
92
117
|
# contract. Raises Studio::UserContractError with a clear pointer to
|
|
93
118
|
# docs/USER_CONTRACT.md if anything required is missing. Called from
|
|
@@ -146,6 +171,10 @@ module Studio
|
|
|
146
171
|
entry ? "/#{entry[:file]}" : nil
|
|
147
172
|
end
|
|
148
173
|
|
|
174
|
+
def self.env_truthy?(value)
|
|
175
|
+
%w[1 true yes on].include?(value.to_s.strip.downcase)
|
|
176
|
+
end
|
|
177
|
+
|
|
149
178
|
def self.routes(router)
|
|
150
179
|
router.instance_exec do
|
|
151
180
|
get "login", to: "sessions#new"
|
|
@@ -158,6 +187,10 @@ module Studio
|
|
|
158
187
|
get "auth/:provider/callback", to: "omniauth_callbacks#create"
|
|
159
188
|
get "auth/failure", to: "omniauth_callbacks#failure"
|
|
160
189
|
|
|
190
|
+
unless defined?(Rails) && Rails.env.production?
|
|
191
|
+
get "_studio/local_emails", to: "studio/local_emails#index", as: :studio_local_emails
|
|
192
|
+
end
|
|
193
|
+
|
|
161
194
|
# Passwordless email (magic link). Helpers: magic_link_request_path (POST
|
|
162
195
|
# to request a link), magic_link_path(token) / magic_link_url(token:)
|
|
163
196
|
# for the emailed GET confirmation page, and magic_link_consume_path(token)
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: studio-engine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex McRitchie
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rails
|
|
@@ -150,6 +149,7 @@ files:
|
|
|
150
149
|
- app/controllers/schema_controller.rb
|
|
151
150
|
- app/controllers/sessions_controller.rb
|
|
152
151
|
- app/controllers/solana_sessions_controller.rb
|
|
152
|
+
- app/controllers/studio/local_emails_controller.rb
|
|
153
153
|
- app/controllers/theme_settings_controller.rb
|
|
154
154
|
- app/helpers/studio_theme_helper.rb
|
|
155
155
|
- app/jobs/error_log_cleanup_job.rb
|
|
@@ -191,6 +191,7 @@ files:
|
|
|
191
191
|
- app/views/sessions/_sso_continue.html.erb
|
|
192
192
|
- app/views/sessions/new.html.erb
|
|
193
193
|
- app/views/studio/_cropper_assets.html.erb
|
|
194
|
+
- app/views/studio/local_emails/index.html.erb
|
|
194
195
|
- app/views/studio/modals/_crop_photo.html.erb
|
|
195
196
|
- app/views/studio/modals/_host.html.erb
|
|
196
197
|
- app/views/studio/modals/_image_upload.html.erb
|
|
@@ -225,7 +226,6 @@ metadata:
|
|
|
225
226
|
source_code_uri: https://github.com/amcritchie/studio-engine/tree/main
|
|
226
227
|
bug_tracker_uri: https://github.com/amcritchie/studio-engine/issues
|
|
227
228
|
changelog_uri: https://github.com/amcritchie/studio-engine/blob/main/CHANGELOG.md
|
|
228
|
-
post_install_message:
|
|
229
229
|
rdoc_options: []
|
|
230
230
|
require_paths:
|
|
231
231
|
- lib
|
|
@@ -240,8 +240,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
240
240
|
- !ruby/object:Gem::Version
|
|
241
241
|
version: '0'
|
|
242
242
|
requirements: []
|
|
243
|
-
rubygems_version:
|
|
244
|
-
signing_key:
|
|
243
|
+
rubygems_version: 4.0.9
|
|
245
244
|
specification_version: 4
|
|
246
245
|
summary: Shared Rails engine providing auth, SSO, error logging, theming, and S3-backed
|
|
247
246
|
image caching
|