studio-engine 0.117.0 → 0.119.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 +4 -4
- data/CHANGELOG.md +57 -0
- data/README.md +43 -1
- data/app/assets/tailwind/studio_engine/engine-motion.css +323 -0
- data/app/assets/tailwind/studio_engine/engine.css +44 -0
- data/app/controllers/omniauth_callbacks_controller.rb +15 -2
- data/app/controllers/studio/google_setup_requests_controller.rb +66 -0
- data/app/helpers/studio/loader_helper.rb +79 -0
- data/app/helpers/studio_theme_helper.rb +1 -1
- data/app/javascript/studio/application.js +2 -0
- data/app/javascript/studio/loader.js +214 -0
- data/app/mailers/studio/google_setup_mailer.rb +29 -0
- data/app/models/concerns/studio/omniauth_user.rb +153 -0
- data/app/views/registrations/new.html.erb +3 -5
- data/app/views/sessions/new.html.erb +3 -5
- data/app/views/studio/google_setup_mailer/setup_requested.html.erb +18 -0
- data/app/views/studio/google_setup_mailer/setup_requested.text.erb +23 -0
- data/app/views/studio/google_sign_in/_button.html.erb +52 -0
- data/app/views/studio/google_sign_in/_setup_modal.html.erb +55 -0
- data/app/views/studio/loader/_loader.html.erb +75 -0
- data/app/views/studio/loader/_overlay.html.erb +16 -0
- data/app/views/studio/profiles/_google_section.html.erb +5 -5
- data/app/views/style/_tricks.html.erb +59 -0
- data/lib/studio/engine.rb +25 -0
- data/lib/studio/google_sign_in.rb +361 -0
- data/lib/studio/loader.rb +158 -0
- data/lib/studio/type_scale.rb +64 -0
- data/lib/studio/version.rb +1 -1
- data/lib/studio.rb +63 -0
- data/studio-engine.gemspec +8 -0
- metadata +75 -1
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Studio.loader: the branded loader every app draws from its OWN icon and theme
|
|
4
|
+
# colours (docs/LOADER.md). Pure Ruby apart from the art reader, so the unit
|
|
5
|
+
# suite covers the config without the dummy app.
|
|
6
|
+
#
|
|
7
|
+
# The config is a style name, or a Hash when the style needs more:
|
|
8
|
+
#
|
|
9
|
+
# config.loader = :breathe # the default
|
|
10
|
+
# config.loader = { style: :orbit, icon: "/brand/icon-dark.svg" }
|
|
11
|
+
# config.loader = { style: :sparkle, art: "/brand/loader-sparkle.svg" }
|
|
12
|
+
#
|
|
13
|
+
# GENERIC styles draw the icon as an image and nothing else, so they work on any
|
|
14
|
+
# app with no per-app art: breathe, liquid, orbit, steel.
|
|
15
|
+
#
|
|
16
|
+
# SIGNATURE styles animate PARTS of the icon, which no image can expose, so the
|
|
17
|
+
# app names an ART file: an SVG in the icon's own viewBox holding only the
|
|
18
|
+
# animated layers, each marked with data-loader:
|
|
19
|
+
#
|
|
20
|
+
# weld_bead one <path data-loader="outline" d="..."/> the line the bead runs
|
|
21
|
+
# sparkle each <path data-loader="spark" d="..."/> in the sweep order
|
|
22
|
+
#
|
|
23
|
+
# The icon is drawn underneath as an image; the art is inlined over it. An art
|
|
24
|
+
# file that is missing, outside public/, or has no marked layer falls back to
|
|
25
|
+
# breathe, so a typo can never blank the loader.
|
|
26
|
+
module Studio
|
|
27
|
+
module Loader
|
|
28
|
+
class InvalidConfig < ArgumentError; end
|
|
29
|
+
|
|
30
|
+
SETTING = "Studio.loader"
|
|
31
|
+
GENERIC_STYLES = %i[breathe liquid orbit steel].freeze
|
|
32
|
+
ART_STYLES = { weld_bead: "outline", sparkle: "spark" }.freeze
|
|
33
|
+
STYLES = (GENERIC_STYLES + ART_STYLES.keys).freeze
|
|
34
|
+
DEFAULT_STYLE = :breathe
|
|
35
|
+
KEYS = %i[style icon art].freeze
|
|
36
|
+
|
|
37
|
+
# The icon the loader draws when the app names none: the navbar brand's
|
|
38
|
+
# logo, else the theme's "Navbar Logo" (Studio.logo_for), which every app
|
|
39
|
+
# already declares.
|
|
40
|
+
def self.default_icon
|
|
41
|
+
brand = Studio.respond_to?(:navbar_brand) ? Studio.navbar_brand : nil
|
|
42
|
+
(brand && brand[:logo]) || Studio.logo_for("Navbar Logo")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
module_function
|
|
46
|
+
|
|
47
|
+
# The config as { style:, icon:, art: } (icon and art nil when not given).
|
|
48
|
+
def normalize(config)
|
|
49
|
+
config = { style: config } if config.is_a?(Symbol) || config.is_a?(String)
|
|
50
|
+
config = { style: DEFAULT_STYLE } if config.nil?
|
|
51
|
+
raise InvalidConfig, "#{SETTING} must be a style Symbol or a Hash (got #{config.class})" unless config.is_a?(Hash)
|
|
52
|
+
|
|
53
|
+
config = config.to_h { |key, value| [key.to_sym, value] }
|
|
54
|
+
unknown = config.keys - KEYS
|
|
55
|
+
raise InvalidConfig, "#{SETTING} has unknown key :#{unknown.first} (takes #{KEYS.join(', ')})" if unknown.any?
|
|
56
|
+
|
|
57
|
+
style = style!(config[:style] || DEFAULT_STYLE)
|
|
58
|
+
icon = path!(config[:icon], "#{SETTING}[:icon]")
|
|
59
|
+
art = path!(config[:art], "#{SETTING}[:art]")
|
|
60
|
+
if ART_STYLES.key?(style) && art.nil?
|
|
61
|
+
raise InvalidConfig, "#{SETTING} style :#{style} animates parts of the icon, so it needs an art: file " \
|
|
62
|
+
"(an SVG of <path data-loader=\"#{ART_STYLES[style]}\"> layers; docs/LOADER.md)"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
{ style: style, icon: icon, art: art }.freeze
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def style!(value)
|
|
69
|
+
style = value.to_s.strip.tr("-", "_").to_sym
|
|
70
|
+
return style if STYLES.include?(style)
|
|
71
|
+
|
|
72
|
+
raise InvalidConfig, "#{SETTING} style must be one of #{STYLES.map(&:inspect).join(', ')} (got #{value.inspect})"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def path!(value, label)
|
|
76
|
+
return nil if value.nil?
|
|
77
|
+
return value.strip if value.is_a?(String) && !value.strip.empty?
|
|
78
|
+
|
|
79
|
+
raise InvalidConfig, "#{label} must be a non-blank path (got #{value.inspect})"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def art_style?(style) = ART_STYLES.key?(style)
|
|
83
|
+
|
|
84
|
+
# ---- The art file ------------------------------------------------------
|
|
85
|
+
#
|
|
86
|
+
# Read from the host's public/ (the path is the URL path the app would serve
|
|
87
|
+
# it at), parsed once per file version. Returns
|
|
88
|
+
# { view_box: "0 0 w h", width: Float, layers: [Nokogiri::XML::Element] }
|
|
89
|
+
# with only the layers marked for `style`, or nil when there are none.
|
|
90
|
+
@art_cache = {}
|
|
91
|
+
@art_mutex = Mutex.new
|
|
92
|
+
|
|
93
|
+
class << self
|
|
94
|
+
attr_reader :art_cache
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def art(path, style, root: public_root)
|
|
98
|
+
mark = ART_STYLES[style]
|
|
99
|
+
file = public_file(path, root)
|
|
100
|
+
return nil unless mark && file
|
|
101
|
+
|
|
102
|
+
key = [file, File.mtime(file).to_f, mark]
|
|
103
|
+
Loader.instance_variable_get(:@art_mutex).synchronize do
|
|
104
|
+
Loader.art_cache[key] ||= parse_art(File.read(file), mark)
|
|
105
|
+
end
|
|
106
|
+
rescue SystemCallError
|
|
107
|
+
nil
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def parse_art(source, mark)
|
|
111
|
+
require "nokogiri"
|
|
112
|
+
doc = Nokogiri::XML(source)
|
|
113
|
+
svg = doc.root
|
|
114
|
+
return nil unless svg&.name == "svg"
|
|
115
|
+
|
|
116
|
+
layers = doc.xpath("//*[@data-loader='#{mark}']").select { |node| node.name == "path" && node["d"].to_s.strip != "" }
|
|
117
|
+
return nil if layers.empty?
|
|
118
|
+
|
|
119
|
+
view_box = svg["viewBox"].to_s.strip
|
|
120
|
+
numbers = view_box.split(/[\s,]+/).map { |n| Float(n, exception: false) }
|
|
121
|
+
return nil unless numbers.size == 4 && numbers.all? && numbers[2].positive? && numbers[3].positive?
|
|
122
|
+
|
|
123
|
+
{
|
|
124
|
+
view_box: numbers.map { |n| format_number(n) }.join(" "),
|
|
125
|
+
width: numbers[2],
|
|
126
|
+
layers: layers.map { |node| { d: node["d"].strip, transform: inherited_transform(node) } }.freeze
|
|
127
|
+
}.freeze
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# The transforms of the layer and every group above it, outermost first, so a
|
|
131
|
+
# layer copied out of a <g transform> keeps its place.
|
|
132
|
+
def inherited_transform(node)
|
|
133
|
+
chain = []
|
|
134
|
+
while node.respond_to?(:name) && node.name != "svg" && node.name != "document"
|
|
135
|
+
chain.unshift(node["transform"]) if node["transform"].to_s.strip != ""
|
|
136
|
+
node = node.parent
|
|
137
|
+
end
|
|
138
|
+
chain.empty? ? nil : chain.join(" ")
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def format_number(n) = (n % 1).zero? ? n.to_i.to_s : n.to_s
|
|
142
|
+
|
|
143
|
+
def public_root
|
|
144
|
+
defined?(::Rails) && ::Rails.respond_to?(:public_path) && ::Rails.public_path ? ::Rails.public_path.to_s : nil
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# The file behind a public URL path, refusing anything that escapes public/.
|
|
148
|
+
def public_file(path, root)
|
|
149
|
+
return nil if root.nil? || path.nil? || path.match?(%r{\A[a-z]+:}i)
|
|
150
|
+
|
|
151
|
+
clean = path.split(/[?#]/).first.to_s.delete_prefix("/")
|
|
152
|
+
full = File.expand_path(clean, root)
|
|
153
|
+
return nil unless full.start_with?(File.expand_path(root) + File::SEPARATOR)
|
|
154
|
+
|
|
155
|
+
File.file?(full) ? full : nil
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Studio
|
|
4
|
+
# THE TYPE SCALE — the one set of text sizes every Studio app draws from
|
|
5
|
+
# (task engine-type-scale-roles). Alex McRitchie ruled it the standard for
|
|
6
|
+
# every app on 2026-10-28; the hub's /logos/<brand> Type Scale card shows the
|
|
7
|
+
# same multiples per brand.
|
|
8
|
+
#
|
|
9
|
+
# Each role is a multiple of one base: title 6x, heading 3x, subhead 1.5x,
|
|
10
|
+
# body 1x, fine print 0.75x. engine.css draws the roles as the utilities
|
|
11
|
+
# `type-title`, `type-heading`, `type-subhead`, `type-body` and `type-fine`
|
|
12
|
+
# (not `text-heading` / `text-body`: those are colour utilities).
|
|
13
|
+
#
|
|
14
|
+
# An app has TWO bases:
|
|
15
|
+
# Studio.type_base (16) public and marketing pages: 96/48/24/16/12
|
|
16
|
+
# Studio.type_dense_base (14) admin and ops pages, under a `type-dense`
|
|
17
|
+
# container: 84/42/21/14/10.5
|
|
18
|
+
# The ratios never change; a page picks its base by where it sits.
|
|
19
|
+
#
|
|
20
|
+
# This module emits the per-app variables the utilities read, in the theme
|
|
21
|
+
# tag (StudioThemeHelper#studio_theme_css_tag):
|
|
22
|
+
#
|
|
23
|
+
# Studio::TypeScale.css
|
|
24
|
+
# # => ":root {\n --type-base: 16px;\n --type-dense-base: 14px; ..."
|
|
25
|
+
module TypeScale
|
|
26
|
+
# [utility, multiple, weight] — weight :display or :text picks the app's
|
|
27
|
+
# Studio.type_display_weight or Studio.type_text_weight.
|
|
28
|
+
ROLES = [
|
|
29
|
+
["type-title", 6, :display],
|
|
30
|
+
["type-heading", 3, :display],
|
|
31
|
+
["type-subhead", 1.5, :text],
|
|
32
|
+
["type-body", 1, :text],
|
|
33
|
+
["type-fine", 0.75, :text]
|
|
34
|
+
].freeze
|
|
35
|
+
|
|
36
|
+
WEIGHTS = (100..900).step(100).to_a.freeze
|
|
37
|
+
|
|
38
|
+
def self.css
|
|
39
|
+
<<~CSS
|
|
40
|
+
:root {
|
|
41
|
+
--type-base: #{px(Studio.type_base, 'type_base')};
|
|
42
|
+
--type-dense-base: #{px(Studio.type_dense_base, 'type_dense_base')};
|
|
43
|
+
--type-display-weight: #{weight(Studio.type_display_weight, 'type_display_weight')};
|
|
44
|
+
--type-text-weight: #{weight(Studio.type_text_weight, 'type_text_weight')};
|
|
45
|
+
}
|
|
46
|
+
CSS
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# The sizes a base gives, in pixels, largest first.
|
|
50
|
+
def self.sizes(base) = ROLES.to_h { |utility, multiple, _| [utility, base * multiple] }
|
|
51
|
+
|
|
52
|
+
def self.px(value, setting)
|
|
53
|
+
raise ArgumentError, "Studio.#{setting} must be a positive number of pixels, got #{value.inspect}" unless value.is_a?(Numeric) && value.positive?
|
|
54
|
+
|
|
55
|
+
"#{value.to_s.delete_suffix('.0')}px"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.weight(value, setting)
|
|
59
|
+
raise ArgumentError, "Studio.#{setting} must be one of #{WEIGHTS.join(', ')}, got #{value.inspect}" unless WEIGHTS.include?(value)
|
|
60
|
+
|
|
61
|
+
value.to_s
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
data/lib/studio/version.rb
CHANGED
data/lib/studio.rb
CHANGED
|
@@ -27,6 +27,7 @@ require "studio/color_scale"
|
|
|
27
27
|
require "studio/environment_banner"
|
|
28
28
|
require "studio/theme_resolver"
|
|
29
29
|
require "studio/ui_primitives"
|
|
30
|
+
require "studio/type_scale"
|
|
30
31
|
require "studio/js_literal"
|
|
31
32
|
require "studio/js_identifier"
|
|
32
33
|
require "studio/hold_button"
|
|
@@ -41,10 +42,12 @@ require "studio/navbar_account_sidebar"
|
|
|
41
42
|
require "studio/navbar_identity"
|
|
42
43
|
require "studio/navbar_wordmark"
|
|
43
44
|
require "studio/navbar_brand"
|
|
45
|
+
require "studio/loader"
|
|
44
46
|
require "studio/auth_labels"
|
|
45
47
|
require "studio/profile_sections"
|
|
46
48
|
require "studio/profile_image"
|
|
47
49
|
require "studio/oauth_identity"
|
|
50
|
+
require "studio/google_sign_in"
|
|
48
51
|
require "studio/newsletter"
|
|
49
52
|
require "studio/health_report"
|
|
50
53
|
require "studio/agent_chat"
|
|
@@ -184,6 +187,26 @@ module Studio
|
|
|
184
187
|
# config.features = %i[web3]
|
|
185
188
|
mattr_accessor :auth_methods, default: %i[magic_link google]
|
|
186
189
|
|
|
190
|
+
# ---- Google sign-in, ready out of the box (Studio::GoogleSignIn) ----------
|
|
191
|
+
# :google in auth_methods draws the button; the engine registers the strategy
|
|
192
|
+
# and a default User.from_omniauth. What is left is the OAuth client, and
|
|
193
|
+
# google_ready? says whether the app has one: GOOGLE_CLIENT_ID and
|
|
194
|
+
# GOOGLE_CLIENT_SECRET both set. While it is false the button opens a modal
|
|
195
|
+
# whose "Request Google setup" mails google_setup_request_to, once per app
|
|
196
|
+
# per day, with the redirect URI to register. docs/GOOGLE_AUTH_SETUP.md.
|
|
197
|
+
def self.google_ready?(env: ENV)
|
|
198
|
+
GoogleSignIn.ready?(env: env)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
mattr_accessor :google_setup_request_to, default: "team@mcritchie.studio"
|
|
202
|
+
|
|
203
|
+
# The host Google's callback lives on, e.g. "voicebox.mcritchie.studio". It
|
|
204
|
+
# names the redirect URI the setup-request email asks ops to register, and in
|
|
205
|
+
# production it pins OmniAuth's callback host. nil, the default, falls back to
|
|
206
|
+
# ENV["APP_HOST"], then the first plain hostname in config.hosts
|
|
207
|
+
# (Studio::GoogleSignIn.canonical_host). Never read from the request.
|
|
208
|
+
mattr_accessor :google_canonical_host, default: nil
|
|
209
|
+
|
|
187
210
|
# ---- Capability features --------------------------------------------------
|
|
188
211
|
# Coarse app-level capability switches an app opts into. Distinct from
|
|
189
212
|
# auth_methods (which sign-in methods the login views render) — these gate
|
|
@@ -282,6 +305,27 @@ module Studio
|
|
|
282
305
|
@@navbar_wordmark = NavbarWordmark.normalize(config)
|
|
283
306
|
end
|
|
284
307
|
|
|
308
|
+
# ---- Branded loader ----
|
|
309
|
+
# The loader every page load, long wait and pending button draws: the app's
|
|
310
|
+
# own icon in its own theme colours, no per-app code. A style name, or a Hash
|
|
311
|
+
# with the icon (default: the navbar brand logo, else theme_logos' "Navbar
|
|
312
|
+
# Logo") and, for a signature style, the art file of marked layers. The
|
|
313
|
+
# default is :breathe. Rules: lib/studio/loader.rb; docs/LOADER.md.
|
|
314
|
+
#
|
|
315
|
+
# config.loader = :orbit
|
|
316
|
+
# config.loader = { style: :sparkle, icon: "/brand/icon-dark.svg",
|
|
317
|
+
# art: "/brand/loader-sparkle.svg" }
|
|
318
|
+
mattr_reader :loader, default: Loader.normalize(nil)
|
|
319
|
+
|
|
320
|
+
def self.loader=(config)
|
|
321
|
+
@@loader = Loader.normalize(config)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# The icon the loader draws: the configured one, else Loader.default_icon.
|
|
325
|
+
def self.loader_icon
|
|
326
|
+
loader[:icon] || Loader.default_icon
|
|
327
|
+
end
|
|
328
|
+
|
|
285
329
|
def self.sign_in_label=(label)
|
|
286
330
|
NavbarIdentity.validate_label!(label)
|
|
287
331
|
@@sign_in_label = label
|
|
@@ -1269,6 +1313,15 @@ module Studio
|
|
|
1269
1313
|
mattr_accessor :theme_danger, default: "#EF4444"
|
|
1270
1314
|
mattr_accessor :theme_accent, default: "#F72585"
|
|
1271
1315
|
|
|
1316
|
+
# Type scale (Studio::TypeScale): the body size every type-* role multiplies.
|
|
1317
|
+
# type_base for public pages, type_dense_base inside a `type-dense` container
|
|
1318
|
+
# (admin and ops pages). Weights: title and heading take the display weight,
|
|
1319
|
+
# subhead, body and fine print the text weight. Set them from the brand kit.
|
|
1320
|
+
mattr_accessor :type_base, default: 16
|
|
1321
|
+
mattr_accessor :type_dense_base, default: 14
|
|
1322
|
+
mattr_accessor :type_display_weight, default: 800
|
|
1323
|
+
mattr_accessor :type_text_weight, default: 400
|
|
1324
|
+
|
|
1272
1325
|
# S3 / object storage — host apps MUST set s3_bucket_prefix explicitly in
|
|
1273
1326
|
# config/initializers/studio.rb before any S3-touching code runs (ImageCache,
|
|
1274
1327
|
# Studio::S3.upload, etc.). Default is nil so external users don't accidentally
|
|
@@ -1726,6 +1779,16 @@ module Studio
|
|
|
1726
1779
|
get "auth/:provider/callback", to: "omniauth_callbacks#create"
|
|
1727
1780
|
get "auth/failure", to: "omniauth_callbacks#failure"
|
|
1728
1781
|
|
|
1782
|
+
# "Request Google setup", the button in the Google modal an app shows
|
|
1783
|
+
# while its OAuth client is unset (Studio::GoogleSignIn). Mails the team
|
|
1784
|
+
# once per app per day, and answers 404 once the
|
|
1785
|
+
# button posts; unauthenticated, since the person asking cannot
|
|
1786
|
+
# sign in yet. Drawn whenever the Google button can be.
|
|
1787
|
+
if Studio.auth_method?(:google)
|
|
1788
|
+
post "_studio/google_setup_request", to: "studio/google_setup_requests#create",
|
|
1789
|
+
as: :studio_google_setup_request
|
|
1790
|
+
end
|
|
1791
|
+
|
|
1729
1792
|
# The daily health report as JSON, for the hub's central 07:00 pull
|
|
1730
1793
|
# (Studio::HealthReportsController). Drawn in EVERY environment, production
|
|
1731
1794
|
# included: the controller is the gate, answering 404 until the app names
|
data/studio-engine.gemspec
CHANGED
|
@@ -63,6 +63,14 @@ Gem::Specification.new do |spec|
|
|
|
63
63
|
# allows accepts redis 6. test/lib/gemspec_redis_bound_test.rb pins it.
|
|
64
64
|
spec.add_dependency "redis", ">= 4.0.1", "< 6"
|
|
65
65
|
spec.add_dependency "turbo-rails", ">= 1.0"
|
|
66
|
+
# Google sign-in ships ready (Studio::GoogleSignIn). Every app draws the
|
|
67
|
+
# "Sign in with Google" button, so every app carries the strategy: the engine
|
|
68
|
+
# registers google_oauth2 itself unless the host's own initializer already did.
|
|
69
|
+
# Bounds match the consumers' locks (omniauth 2.1, google-oauth2 1.2,
|
|
70
|
+
# rails_csrf_protection 1.0 and 2.0).
|
|
71
|
+
spec.add_dependency "omniauth", ">= 2.1", "< 3"
|
|
72
|
+
spec.add_dependency "omniauth-google-oauth2", ">= 1.1", "< 2"
|
|
73
|
+
spec.add_dependency "omniauth-rails_csrf_protection", ">= 1.0", "< 3"
|
|
66
74
|
# IP -> location for Studio::Geo. A dependency rather than a host concern
|
|
67
75
|
# because the point of the geo primitive is that EVERY app has the capacity:
|
|
68
76
|
# an app that has to add a gem before it can place a visitor does not.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: studio-engine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.119.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex McRitchie
|
|
@@ -174,6 +174,66 @@ dependencies:
|
|
|
174
174
|
- - ">="
|
|
175
175
|
- !ruby/object:Gem::Version
|
|
176
176
|
version: '1.0'
|
|
177
|
+
- !ruby/object:Gem::Dependency
|
|
178
|
+
name: omniauth
|
|
179
|
+
requirement: !ruby/object:Gem::Requirement
|
|
180
|
+
requirements:
|
|
181
|
+
- - ">="
|
|
182
|
+
- !ruby/object:Gem::Version
|
|
183
|
+
version: '2.1'
|
|
184
|
+
- - "<"
|
|
185
|
+
- !ruby/object:Gem::Version
|
|
186
|
+
version: '3'
|
|
187
|
+
type: :runtime
|
|
188
|
+
prerelease: false
|
|
189
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
190
|
+
requirements:
|
|
191
|
+
- - ">="
|
|
192
|
+
- !ruby/object:Gem::Version
|
|
193
|
+
version: '2.1'
|
|
194
|
+
- - "<"
|
|
195
|
+
- !ruby/object:Gem::Version
|
|
196
|
+
version: '3'
|
|
197
|
+
- !ruby/object:Gem::Dependency
|
|
198
|
+
name: omniauth-google-oauth2
|
|
199
|
+
requirement: !ruby/object:Gem::Requirement
|
|
200
|
+
requirements:
|
|
201
|
+
- - ">="
|
|
202
|
+
- !ruby/object:Gem::Version
|
|
203
|
+
version: '1.1'
|
|
204
|
+
- - "<"
|
|
205
|
+
- !ruby/object:Gem::Version
|
|
206
|
+
version: '2'
|
|
207
|
+
type: :runtime
|
|
208
|
+
prerelease: false
|
|
209
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
210
|
+
requirements:
|
|
211
|
+
- - ">="
|
|
212
|
+
- !ruby/object:Gem::Version
|
|
213
|
+
version: '1.1'
|
|
214
|
+
- - "<"
|
|
215
|
+
- !ruby/object:Gem::Version
|
|
216
|
+
version: '2'
|
|
217
|
+
- !ruby/object:Gem::Dependency
|
|
218
|
+
name: omniauth-rails_csrf_protection
|
|
219
|
+
requirement: !ruby/object:Gem::Requirement
|
|
220
|
+
requirements:
|
|
221
|
+
- - ">="
|
|
222
|
+
- !ruby/object:Gem::Version
|
|
223
|
+
version: '1.0'
|
|
224
|
+
- - "<"
|
|
225
|
+
- !ruby/object:Gem::Version
|
|
226
|
+
version: '3'
|
|
227
|
+
type: :runtime
|
|
228
|
+
prerelease: false
|
|
229
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
230
|
+
requirements:
|
|
231
|
+
- - ">="
|
|
232
|
+
- !ruby/object:Gem::Version
|
|
233
|
+
version: '1.0'
|
|
234
|
+
- - "<"
|
|
235
|
+
- !ruby/object:Gem::Version
|
|
236
|
+
version: '3'
|
|
177
237
|
- !ruby/object:Gem::Dependency
|
|
178
238
|
name: geocoder
|
|
179
239
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -400,6 +460,7 @@ files:
|
|
|
400
460
|
- app/controllers/studio/component_previews_controller.rb
|
|
401
461
|
- app/controllers/studio/emails_controller.rb
|
|
402
462
|
- app/controllers/studio/geo_settings_controller.rb
|
|
463
|
+
- app/controllers/studio/google_setup_requests_controller.rb
|
|
403
464
|
- app/controllers/studio/health_reports_controller.rb
|
|
404
465
|
- app/controllers/studio/knowledge_docs_controller.rb
|
|
405
466
|
- app/controllers/studio/links_controller.rb
|
|
@@ -419,6 +480,7 @@ files:
|
|
|
419
480
|
- app/helpers/studio/fizz_helper.rb
|
|
420
481
|
- app/helpers/studio/geo_helper.rb
|
|
421
482
|
- app/helpers/studio/link_preview_helper.rb
|
|
483
|
+
- app/helpers/studio/loader_helper.rb
|
|
422
484
|
- app/helpers/studio/public_user_helper.rb
|
|
423
485
|
- app/helpers/studio/site_footer_helper.rb
|
|
424
486
|
- app/helpers/studio_email_delivery_helper.rb
|
|
@@ -466,6 +528,7 @@ files:
|
|
|
466
528
|
- app/javascript/studio/leveling_activity.js
|
|
467
529
|
- app/javascript/studio/link_preview_card.js
|
|
468
530
|
- app/javascript/studio/link_sidebar.js
|
|
531
|
+
- app/javascript/studio/loader.js
|
|
469
532
|
- app/javascript/studio/local_path.js
|
|
470
533
|
- app/javascript/studio/modal_countdown.js
|
|
471
534
|
- app/javascript/studio/modal_host.js
|
|
@@ -486,12 +549,14 @@ files:
|
|
|
486
549
|
- app/jobs/studio/email_delivery_job.rb
|
|
487
550
|
- app/jobs/studio/health_report_job.rb
|
|
488
551
|
- app/mailers/application_mailer.rb
|
|
552
|
+
- app/mailers/studio/google_setup_mailer.rb
|
|
489
553
|
- app/mailers/studio/health_report_mailer.rb
|
|
490
554
|
- app/mailers/studio/newsletter_mailer.rb
|
|
491
555
|
- app/mailers/studio/profile_mailer.rb
|
|
492
556
|
- app/mailers/user_mailer.rb
|
|
493
557
|
- app/models/concerns/sluggable.rb
|
|
494
558
|
- app/models/concerns/studio/board/rankable.rb
|
|
559
|
+
- app/models/concerns/studio/omniauth_user.rb
|
|
495
560
|
- app/models/current.rb
|
|
496
561
|
- app/models/error_log.rb
|
|
497
562
|
- app/models/image_cache.rb
|
|
@@ -591,6 +656,10 @@ files:
|
|
|
591
656
|
- app/views/studio/emails/show.html.erb
|
|
592
657
|
- app/views/studio/fields/_date_of_birth.html.erb
|
|
593
658
|
- app/views/studio/geo_settings/edit.html.erb
|
|
659
|
+
- app/views/studio/google_setup_mailer/setup_requested.html.erb
|
|
660
|
+
- app/views/studio/google_setup_mailer/setup_requested.text.erb
|
|
661
|
+
- app/views/studio/google_sign_in/_button.html.erb
|
|
662
|
+
- app/views/studio/google_sign_in/_setup_modal.html.erb
|
|
594
663
|
- app/views/studio/health_report_mailer/daily.html.erb
|
|
595
664
|
- app/views/studio/health_report_mailer/daily.text.erb
|
|
596
665
|
- app/views/studio/knowledge_docs/_access_chips.html.erb
|
|
@@ -610,6 +679,8 @@ files:
|
|
|
610
679
|
- app/views/studio/knowledge_docs/preview.html.erb
|
|
611
680
|
- app/views/studio/knowledge_docs/show.html.erb
|
|
612
681
|
- app/views/studio/links/confirm.html.erb
|
|
682
|
+
- app/views/studio/loader/_loader.html.erb
|
|
683
|
+
- app/views/studio/loader/_overlay.html.erb
|
|
613
684
|
- app/views/studio/local_emails/index.html.erb
|
|
614
685
|
- app/views/studio/mailers/_layered_banner.html.erb
|
|
615
686
|
- app/views/studio/modals/_crop_photo.html.erb
|
|
@@ -755,6 +826,7 @@ files:
|
|
|
755
826
|
- lib/studio/geo.rb
|
|
756
827
|
- lib/studio/geo/countries.rb
|
|
757
828
|
- lib/studio/geo/lookup.rb
|
|
829
|
+
- lib/studio/google_sign_in.rb
|
|
758
830
|
- lib/studio/health_report.rb
|
|
759
831
|
- lib/studio/hold_button.rb
|
|
760
832
|
- lib/studio/host_schema.rb
|
|
@@ -772,6 +844,7 @@ files:
|
|
|
772
844
|
- lib/studio/link_preview.rb
|
|
773
845
|
- lib/studio/link_resolution.rb
|
|
774
846
|
- lib/studio/link_token.rb
|
|
847
|
+
- lib/studio/loader.rb
|
|
775
848
|
- lib/studio/local_path.rb
|
|
776
849
|
- lib/studio/log_rotation.rb
|
|
777
850
|
- lib/studio/mail_sender.rb
|
|
@@ -812,6 +885,7 @@ files:
|
|
|
812
885
|
- lib/studio/survey/export.rb
|
|
813
886
|
- lib/studio/tailwind_entry.rb
|
|
814
887
|
- lib/studio/theme_resolver.rb
|
|
888
|
+
- lib/studio/type_scale.rb
|
|
815
889
|
- lib/studio/ui_primitives.rb
|
|
816
890
|
- lib/studio/username_generator.rb
|
|
817
891
|
- lib/studio/version.rb
|