studio-engine 0.79.0 → 0.81.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 +33 -0
- data/README.md +30 -0
- data/app/assets/tailwind/studio_engine/engine.css +8 -2
- data/app/views/components/_admin_dropdown.html.erb +5 -1
- data/app/views/components/_user_nav.html.erb +11 -5
- data/app/views/layouts/_navbar.html.erb +1 -1
- data/app/views/style/_theme.html.erb +2 -2
- data/app/views/theme_settings/edit.html.erb +7 -0
- data/lib/studio/navbar_identity.rb +91 -0
- data/lib/studio/theme_resolver.rb +26 -10
- data/lib/studio/version.rb +1 -1
- data/lib/studio.rb +33 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3ba5fe314a5d1bbb6b2b95d12a2561bf3f87c2e302ced48eb365e2587c7ba7ee
|
|
4
|
+
data.tar.gz: de2c065be4ed44da716d10331c23edfda0ca08c05f333b0324457b860d4db073
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1eac79405823182fa089630acf41324de6238af3dead7f97136a97db2a0062f1443ae2538446cecace96bbe30655c3b889dbf1bba05478492d59c514bbcb3238
|
|
7
|
+
data.tar.gz: 56637c4be0db6c8701eaab6397bd6631f34f609ba986574d48382b458e52e415b3854598efc24b1894b1cfd43fdb8e81298754b57026657d8816a4e351a2bdad
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,39 @@ The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This pro
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.81.0 — 2026-09-30
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Default warning and danger buttons clear WCAG AA under their white label.**
|
|
12
|
+
While an app leaves its warning at the engine default `#FF7C47` (2.55:1 under
|
|
13
|
+
white), the resolver emits `--color-warning-fill` (`#B85933`, 4.65:1); while it
|
|
14
|
+
leaves danger at `#EF4444` (3.76:1), it emits `--color-danger-fill`
|
|
15
|
+
(`#D73D3D`, 4.54:1). `btn-warning` and `btn-danger` paint the fill and fall back
|
|
16
|
+
to `--color-warning` / `--color-danger`, so a configured colour paints as
|
|
17
|
+
before. The role colours themselves, their borders and rings, and the
|
|
18
|
+
`--color-warning-ink` / `--color-danger-ink` text inks are unchanged. Labels
|
|
19
|
+
stay white.
|
|
20
|
+
- **An admin sees one admin cog at every width.** `components/_user_nav` draws its
|
|
21
|
+
admin menu from `md` up only, as it already did the theme toggle: below `md` the
|
|
22
|
+
navbar's phone row draws the cog, so an admin's phone showed two.
|
|
23
|
+
`components/_admin_dropdown` takes an optional `display_class:` for this. Apps
|
|
24
|
+
that shadow `_user_nav` (mcritchie-studio, turf-monster, cyvasse) are unaffected
|
|
25
|
+
until they drop their copy.
|
|
26
|
+
|
|
27
|
+
## 0.80.0 — 2026-09-30
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- **`Studio.navbar_user_name` and `Studio.sign_in_label` set the navbar's two
|
|
32
|
+
identity words.** `navbar_user_name` takes a method name (`:player_name`) or a
|
|
33
|
+
callable `->(user, view)` and replaces `display_name` in the signed-in user
|
|
34
|
+
nav; a blank answer or a raise falls back to `display_name`, and a raise is
|
|
35
|
+
reported once rather than 500ing the layout. `sign_in_label` (default
|
|
36
|
+
`"Log in"`) labels the signed-out button in `layouts/_navbar` and
|
|
37
|
+
`components/_user_nav`. An app that sets neither renders the same navbar bytes
|
|
38
|
+
as before. See the README, *Navbar identity*.
|
|
39
|
+
|
|
7
40
|
## 0.79.0 — 2026-09-30
|
|
8
41
|
|
|
9
42
|
### Changed
|
data/README.md
CHANGED
|
@@ -611,6 +611,36 @@ end
|
|
|
611
611
|
- A bad entry raises `Studio::NavbarLinks::InvalidLink` naming its index: at
|
|
612
612
|
assignment for a static Array, at render for a callable.
|
|
613
613
|
|
|
614
|
+
### Navbar identity — `Studio.navbar_user_name` and `Studio.sign_in_label`
|
|
615
|
+
|
|
616
|
+
Two words the navbar says about the viewer. Both defaults render the navbar
|
|
617
|
+
byte-identical to before, so an app that sets neither sees no change. Rules:
|
|
618
|
+
`lib/studio/navbar_identity.rb`.
|
|
619
|
+
|
|
620
|
+
```ruby
|
|
621
|
+
Studio.configure do |config|
|
|
622
|
+
# The signed-in name in components/_user_nav. nil (the default) is display_name.
|
|
623
|
+
config.navbar_user_name = :player_name # a method on the user
|
|
624
|
+
# config.navbar_user_name = ->(user, view) { user.player_name } # or a callable
|
|
625
|
+
|
|
626
|
+
# The signed-out button in layouts/_navbar and components/_user_nav.
|
|
627
|
+
config.sign_in_label = "Sign in" # default "Log in"
|
|
628
|
+
end
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
- `navbar_user_name` is `nil`, a method name (Symbol or String), or a callable.
|
|
632
|
+
A two-argument callable gets `(user, view)`; a one-argument callable gets the
|
|
633
|
+
user alone.
|
|
634
|
+
- The name is always escaped, even when the method or callable returns an
|
|
635
|
+
`html_safe` string.
|
|
636
|
+
- A method or callable that answers blank falls back to `display_name`. One
|
|
637
|
+
that raises also falls back, and the error is reported once per process per
|
|
638
|
+
error class (to `ErrorLog` when the app has it, else the Rails log), so a
|
|
639
|
+
broken setting never 500s the layout.
|
|
640
|
+
- The engine has no I18n catalogue, so the label is plain config, not a locale
|
|
641
|
+
key. A blank or non-String label, or a `navbar_user_name` of any other type,
|
|
642
|
+
raises `Studio::NavbarIdentity::InvalidConfig` at assignment.
|
|
643
|
+
|
|
614
644
|
### User nav slots
|
|
615
645
|
|
|
616
646
|
`components/_user_nav.html.erb` renders the right-side navbar user section.
|
|
@@ -265,17 +265,23 @@ body.modal-open .studio-app-banner {
|
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
+
/* The fill is --color-danger-fill when the resolver emits one (only while the
|
|
269
|
+
app leaves its danger colour at the engine default, which fails AA under the
|
|
270
|
+
white label), otherwise the app's own --color-danger. */
|
|
268
271
|
@utility btn-danger {
|
|
269
272
|
@apply text-white;
|
|
270
|
-
background-color: var(--color-danger);
|
|
273
|
+
background-color: var(--color-danger-fill, var(--color-danger));
|
|
271
274
|
&:hover {
|
|
272
275
|
filter: brightness(0.9);
|
|
273
276
|
}
|
|
274
277
|
}
|
|
275
278
|
|
|
279
|
+
/* The fill is --color-warning-fill when the resolver emits one (only while the
|
|
280
|
+
app leaves its warning colour at the engine default, which fails AA under the
|
|
281
|
+
white label), otherwise the app's own --color-warning. */
|
|
276
282
|
@utility btn-warning {
|
|
277
283
|
@apply text-white;
|
|
278
|
-
background-color: var(--color-warning);
|
|
284
|
+
background-color: var(--color-warning-fill, var(--color-warning));
|
|
279
285
|
&:hover {
|
|
280
286
|
filter: brightness(0.9);
|
|
281
287
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
<%# ADMIN chrome: renders only for admin? viewers. Signed-out and
|
|
2
2
|
non-admin visitors get nothing — this partial once leaked admin
|
|
3
3
|
links onto a public landing page. %>
|
|
4
|
+
<%# display_class: optional display utilities for the root. The user nav
|
|
5
|
+
passes "hidden md:block" because below md the navbar's phone row draws
|
|
6
|
+
its own cog, and two read as a double gear (engine-button-contrast-admin-cog). %>
|
|
7
|
+
<% display_class = local_assigns.fetch(:display_class, nil) %>
|
|
4
8
|
<% if respond_to?(:admin?) && admin? %>
|
|
5
|
-
<div x-data="{ open: false }" class="relative leading-none">
|
|
9
|
+
<div x-data="{ open: false }" class="<%= ["relative leading-none", display_class].compact.join(" ") %>">
|
|
6
10
|
<button @click="open = !open" class="flex text-secondary hover:text-heading transition" title="Admin">
|
|
7
11
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
|
8
12
|
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" />
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
address + level progress bar) entirely
|
|
12
12
|
(e.g. a seeds bar)
|
|
13
13
|
|
|
14
|
+
Config, not locals: the signed-in name is Studio.navbar_user_name
|
|
15
|
+
(default display_name) and the signed-out button reads
|
|
16
|
+
Studio.sign_in_label (default "Log in"); lib/studio/navbar_identity.rb.
|
|
17
|
+
|
|
14
18
|
Other locals:
|
|
15
19
|
show_logout_link — boolean, show explicit "Log out" link in the
|
|
16
20
|
second row (Studio uses this)
|
|
@@ -82,8 +86,10 @@
|
|
|
82
86
|
<div class="flex items-center gap-2 ml-auto self-end">
|
|
83
87
|
<%= studio_nav_slot.call(extra_icons_slot, extra_icons_html) %>
|
|
84
88
|
<%# Skipped when the link sidebar carries the admin menu — two cog
|
|
85
|
-
glyphs side by side read as a double gear.
|
|
86
|
-
|
|
89
|
+
glyphs side by side read as a double gear. Desktop only, like
|
|
90
|
+
the theme toggle: below md the navbar's phone row draws the cog,
|
|
91
|
+
so a second one here put two on an admin's phone. %>
|
|
92
|
+
<%= render "components/admin_dropdown", display_class: "hidden md:block" unless respond_to?(:studio_sidebar_replaces_admin_menu?) && studio_sidebar_replaces_admin_menu? %>
|
|
87
93
|
<%# Desktop only. Below md the engine navbar's phone row draws the
|
|
88
94
|
theme toggle, so a second one here put two on every phone. The
|
|
89
95
|
signed-out bar has always done it this way (layouts/_navbar). %>
|
|
@@ -204,7 +210,7 @@
|
|
|
204
210
|
the link's accessible name. %>
|
|
205
211
|
<% account_classes = "group flex items-center gap-2 flex-shrink-0" %>
|
|
206
212
|
<% account_inner = capture do %>
|
|
207
|
-
<span class="sr-only md:not-sr-only md:max-w-40"><span class="block truncate text-right text-heading font-semibold text-base leading-none transition<%= " group-hover:text-primary" if studio_nav_destination %>" data-nav-name><%= current_user
|
|
213
|
+
<span class="sr-only md:not-sr-only md:max-w-40"><span class="block truncate text-right text-heading font-semibold text-base leading-none transition<%= " group-hover:text-primary" if studio_nav_destination %>" data-nav-name><%= Studio.navbar_user_name_for(current_user, self) %></span></span>
|
|
208
214
|
<span class="flex-shrink-0 flex items-center" aria-hidden="true">
|
|
209
215
|
<%= render "components/avatar", user: current_user, size: "nav" %>
|
|
210
216
|
</span>
|
|
@@ -215,11 +221,11 @@
|
|
|
215
221
|
<span class="<%= account_classes %>" data-nav-account x-bind:style="$store.devMode && 'background: lightgreen'"><%= account_inner %></span>
|
|
216
222
|
<% end %>
|
|
217
223
|
<% else %>
|
|
218
|
-
<%= render "components/admin_dropdown" unless respond_to?(:studio_sidebar_replaces_admin_menu?) && studio_sidebar_replaces_admin_menu? %>
|
|
224
|
+
<%= render "components/admin_dropdown", display_class: "hidden md:block" unless respond_to?(:studio_sidebar_replaces_admin_menu?) && studio_sidebar_replaces_admin_menu? %>
|
|
219
225
|
<%# Desktop only, as above: the phone row carries the toggle below md. %>
|
|
220
226
|
<span class="hidden md:flex" data-nav-theme-toggle="desktop"><%= render "components/theme_toggle_morph" %></span>
|
|
221
227
|
<div class="flex items-center gap-3 ml-auto">
|
|
222
|
-
<%= link_to
|
|
228
|
+
<%= link_to Studio.sign_in_label, login_path, class: "text-heading hover:text-primary text-sm font-semibold transition" %>
|
|
223
229
|
<%= link_to "Sign up", signup_path, class: "btn btn-primary btn-sm" %>
|
|
224
230
|
</div>
|
|
225
231
|
<% end %>
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
<div class="flex items-center justify-end gap-3">
|
|
163
163
|
<%= sidebar_trigger_desktop if sidebar_active %>
|
|
164
164
|
<span class="hidden md:flex"><%= render "components/theme_toggle_morph" %></span>
|
|
165
|
-
<%= link_to
|
|
165
|
+
<%= link_to Studio.sign_in_label, login_path, class: "btn btn-primary" %>
|
|
166
166
|
</div>
|
|
167
167
|
<% end %>
|
|
168
168
|
</div>
|
|
@@ -191,8 +191,8 @@
|
|
|
191
191
|
d.setProperty('--color-cta', this.colors.primary);
|
|
192
192
|
d.setProperty('--color-cta-fill', this.colors.primary);
|
|
193
193
|
if (this.colors.success) d.setProperty('--color-success', this.colors.success);
|
|
194
|
-
if (this.colors.warning) d.setProperty('--color-warning', this.colors.warning);
|
|
195
|
-
if (this.colors.danger) d.setProperty('--color-danger', this.colors.danger);
|
|
194
|
+
if (this.colors.warning) { d.setProperty('--color-warning', this.colors.warning); d.setProperty('--color-warning-fill', this.colors.warning); }
|
|
195
|
+
if (this.colors.danger) { d.setProperty('--color-danger', this.colors.danger); d.setProperty('--color-danger-fill', this.colors.danger); }
|
|
196
196
|
},
|
|
197
197
|
persist(form, label) {
|
|
198
198
|
if (this.saving) return;
|
|
@@ -337,6 +337,13 @@
|
|
|
337
337
|
document.documentElement.style.setProperty('--color-success', this.colors.success);
|
|
338
338
|
document.documentElement.style.setProperty('--color-warning', this.colors.warning || '#FF7C47');
|
|
339
339
|
document.documentElement.style.setProperty('--color-danger', this.colors.danger || '#EF4444');
|
|
340
|
+
// btn-warning/btn-danger paint --color-<role>-fill first (the resolver
|
|
341
|
+
// emits it for the engine defaults), so a typed colour must set it too
|
|
342
|
+
// or the specimens keep the default fill. Cleared: drop the override.
|
|
343
|
+
['warning', 'danger'].forEach((role) => {
|
|
344
|
+
if (this.colors[role]) document.documentElement.style.setProperty(`--color-${role}-fill`, this.colors[role]);
|
|
345
|
+
else document.documentElement.style.removeProperty(`--color-${role}-fill`);
|
|
346
|
+
});
|
|
340
347
|
},
|
|
341
348
|
|
|
342
349
|
darkPreviewStyle() {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Resolves the two words the engine navbar says about who is (or is not) signed
|
|
4
|
+
# in: the signed-in user's name (Studio.navbar_user_name) and the signed-out
|
|
5
|
+
# button's label (Studio.sign_in_label). Pure Ruby, so the unit suite covers it
|
|
6
|
+
# without the dummy app.
|
|
7
|
+
#
|
|
8
|
+
# Studio.navbar_user_name is one of:
|
|
9
|
+
#
|
|
10
|
+
# nil the default: user.display_name, as before
|
|
11
|
+
# :player_name a method called on the user ("player_name" works too)
|
|
12
|
+
# ->(user, view) { ... } a callable; a one-argument callable gets the user only
|
|
13
|
+
#
|
|
14
|
+
# A layout must not 500 over a name. When the configured method or callable
|
|
15
|
+
# RAISES, the navbar prints display_name and the failure is reported once per
|
|
16
|
+
# process per error class (ErrorLog when the host has it, else Rails.logger),
|
|
17
|
+
# not once per page view. A BLANK answer falls back to display_name quietly:
|
|
18
|
+
# a user with no player name yet is data, not a fault.
|
|
19
|
+
#
|
|
20
|
+
# The name comes back as a plain String, never an html_safe buffer, so ERB
|
|
21
|
+
# escapes whatever a host's method or callable returned.
|
|
22
|
+
module Studio
|
|
23
|
+
module NavbarIdentity
|
|
24
|
+
class InvalidConfig < ArgumentError; end
|
|
25
|
+
|
|
26
|
+
DEFAULT_SIGN_IN_LABEL = "Log in"
|
|
27
|
+
|
|
28
|
+
@reported = {}
|
|
29
|
+
@reported_lock = Mutex.new
|
|
30
|
+
|
|
31
|
+
module_function
|
|
32
|
+
|
|
33
|
+
def user_name(user, view = nil, config: Studio.navbar_user_name)
|
|
34
|
+
fallback = user.display_name # outside the rescue: the default path is not ours to swallow
|
|
35
|
+
return fallback if config.nil?
|
|
36
|
+
|
|
37
|
+
begin
|
|
38
|
+
text = plain_text(configured_name(config, user, view))
|
|
39
|
+
rescue StandardError => e
|
|
40
|
+
report_once(e)
|
|
41
|
+
return fallback
|
|
42
|
+
end
|
|
43
|
+
text.empty? ? fallback : text
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def validate_name!(config)
|
|
47
|
+
return nil if config.nil? || config.respond_to?(:call)
|
|
48
|
+
return nil if (config.is_a?(Symbol) || config.is_a?(String)) && !config.to_s.strip.empty?
|
|
49
|
+
|
|
50
|
+
raise InvalidConfig, "Studio.navbar_user_name must be nil, a method name (Symbol) or a callable " \
|
|
51
|
+
"->(user, view) (got #{config.inspect})"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def validate_label!(label)
|
|
55
|
+
return nil if label.is_a?(String) && !label.strip.empty?
|
|
56
|
+
|
|
57
|
+
raise InvalidConfig, "Studio.sign_in_label must be non-blank text (got #{label.inspect})"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Forget what was reported, so a reconfigured name reports its own failure.
|
|
61
|
+
def reset_reported!
|
|
62
|
+
@reported_lock.synchronize { @reported.clear }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def configured_name(config, user, view)
|
|
66
|
+
return user.public_send(config.to_sym) unless config.respond_to?(:call)
|
|
67
|
+
|
|
68
|
+
config.respond_to?(:arity) && config.arity == 1 ? config.call(user) : config.call(user, view)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# String.new drops an html_safe flag: SafeBuffer#to_s returns the buffer
|
|
72
|
+
# itself, which ERB would print raw.
|
|
73
|
+
def plain_text(value)
|
|
74
|
+
String.new(value.to_s).strip
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def report_once(error)
|
|
78
|
+
first = @reported_lock.synchronize { @reported[error.class.name] ? false : (@reported[error.class.name] = true) }
|
|
79
|
+
return unless first
|
|
80
|
+
|
|
81
|
+
if defined?(::ErrorLog) && ::ErrorLog.respond_to?(:capture!)
|
|
82
|
+
::ErrorLog.capture!(error)
|
|
83
|
+
elsif defined?(::Rails) && ::Rails.respond_to?(:logger) && ::Rails.logger
|
|
84
|
+
::Rails.logger.warn("[studio-navbar] Studio.navbar_user_name failed, showing display_name: " \
|
|
85
|
+
"#{error.class}: #{error.message}")
|
|
86
|
+
end
|
|
87
|
+
rescue StandardError
|
|
88
|
+
nil # reporting is best-effort: the navbar still renders
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -9,6 +9,12 @@ module Studio
|
|
|
9
9
|
# green measured 2.78:1 under the white label btn-success draws on it. This
|
|
10
10
|
# is the same hue at 5.0:1 (the shade cyvasse chose for its own success).
|
|
11
11
|
DEFAULT_SUCCESS = "#367E3A"
|
|
12
|
+
# The default warning and danger stay these hexes as ROLE colours (borders,
|
|
13
|
+
# rings, the -ink derivations start from them). Their button fills are
|
|
14
|
+
# derived separately, see #fill_vars: #FF7C47 is 2.55:1 and #EF4444 3.76:1
|
|
15
|
+
# under the white label btn-warning and btn-danger draw.
|
|
16
|
+
DEFAULT_WARNING = "#FF7C47"
|
|
17
|
+
DEFAULT_DANGER = "#EF4444"
|
|
12
18
|
|
|
13
19
|
# WCAG AA for normal-size text. Every filled button draws a 14px bold white
|
|
14
20
|
# label, which is not "large" text, so its fill owes 4.5:1 against white.
|
|
@@ -44,8 +50,8 @@ module Studio
|
|
|
44
50
|
primary = colors[:primary] || DEFAULT_PRIMARY
|
|
45
51
|
border_rgb = ColorScale.lighten(dark_base, 0.30)
|
|
46
52
|
success = colors[:success] || DEFAULT_SUCCESS
|
|
47
|
-
warning = colors[:warning] ||
|
|
48
|
-
danger = colors[:danger] ||
|
|
53
|
+
warning = colors[:warning] || DEFAULT_WARNING
|
|
54
|
+
danger = colors[:danger] || DEFAULT_DANGER
|
|
49
55
|
surfaces = dark_surfaces(dark_base)
|
|
50
56
|
|
|
51
57
|
secondary_ink = contrast_ink(dark_base, direction: :lighten, start: 0.70, target: 4.5, against: surfaces)
|
|
@@ -114,7 +120,7 @@ module Studio
|
|
|
114
120
|
"--color-warning-ink" => status_ink(warning, surfaces, direction: :lighten),
|
|
115
121
|
"--color-success-ink" => status_ink(success, surfaces, direction: :lighten),
|
|
116
122
|
"--color-accent" => colors[:accent] || "#F72585"
|
|
117
|
-
}.merge(fill_vars(primary,
|
|
123
|
+
}.merge(fill_vars(primary, warning, danger))
|
|
118
124
|
end
|
|
119
125
|
|
|
120
126
|
# Every dark-mode background a text var can sit on (page, surface,
|
|
@@ -197,13 +203,23 @@ module Studio
|
|
|
197
203
|
# label with its orange (a darkened fill would drop it to 3.3:1), and
|
|
198
204
|
# cyvasse sets --color-cta itself.
|
|
199
205
|
#
|
|
206
|
+
# --color-warning-fill and --color-danger-fill are the same contract for
|
|
207
|
+
# btn-warning and btn-danger (engine-button-contrast-admin-cog): emitted
|
|
208
|
+
# only while the role is at its engine default (#FF7C47 at 2.55:1, #EF4444
|
|
209
|
+
# at 3.76:1 under white), so a CONFIGURED colour paints as-is through the
|
|
210
|
+
# var(--color-<role>) fallback — mcritchie-industries' yellow warning is
|
|
211
|
+
# its own call. The role colour itself is not darkened: it is also a
|
|
212
|
+
# border, a ring and the start of its -ink search, which text reads.
|
|
213
|
+
#
|
|
200
214
|
# Success needs no fill var: its DEFAULT is already AA under white
|
|
201
215
|
# (DEFAULT_SUCCESS), and a configured success colour is the app's own —
|
|
202
216
|
# turf-monster's suite pins that a configured #4BAF50 still paints as-is.
|
|
203
|
-
def fill_vars(primary,
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
217
|
+
def fill_vars(primary, warning, danger)
|
|
218
|
+
{
|
|
219
|
+
"--color-cta-fill" => (white_label_fill(primary) if primary.to_s.casecmp?(DEFAULT_PRIMARY)),
|
|
220
|
+
"--color-warning-fill" => (white_label_fill(warning) if warning.to_s.casecmp?(DEFAULT_WARNING)),
|
|
221
|
+
"--color-danger-fill" => (white_label_fill(danger) if danger.to_s.casecmp?(DEFAULT_DANGER))
|
|
222
|
+
}.compact
|
|
207
223
|
end
|
|
208
224
|
|
|
209
225
|
# The hover fill is always derived, so it is always held to AA under white:
|
|
@@ -251,8 +267,8 @@ module Studio
|
|
|
251
267
|
light_base = colors[:light] || "#f8fafc"
|
|
252
268
|
primary = colors[:primary] || DEFAULT_PRIMARY
|
|
253
269
|
success = colors[:success] || DEFAULT_SUCCESS
|
|
254
|
-
warning = colors[:warning] ||
|
|
255
|
-
danger = colors[:danger] ||
|
|
270
|
+
warning = colors[:warning] || DEFAULT_WARNING
|
|
271
|
+
danger = colors[:danger] || DEFAULT_DANGER
|
|
256
272
|
surfaces = light_surfaces(light_base)
|
|
257
273
|
|
|
258
274
|
secondary_ink = contrast_ink(light_base, direction: :darken, start: 0.55, target: 4.5, against: surfaces)
|
|
@@ -303,7 +319,7 @@ module Studio
|
|
|
303
319
|
"--color-warning-ink" => status_ink(warning, surfaces, direction: :darken),
|
|
304
320
|
"--color-success-ink" => status_ink(success, surfaces, direction: :darken),
|
|
305
321
|
"--color-accent" => colors[:accent] || "#F72585"
|
|
306
|
-
}.merge(fill_vars(primary,
|
|
322
|
+
}.merge(fill_vars(primary, warning, danger))
|
|
307
323
|
end
|
|
308
324
|
end
|
|
309
325
|
end
|
data/lib/studio/version.rb
CHANGED
data/lib/studio.rb
CHANGED
|
@@ -13,6 +13,7 @@ require "studio/js_identifier"
|
|
|
13
13
|
require "studio/partial_block"
|
|
14
14
|
require "studio/sidebar_sections"
|
|
15
15
|
require "studio/navbar_links"
|
|
16
|
+
require "studio/navbar_identity"
|
|
16
17
|
require "studio/profile_sections"
|
|
17
18
|
require "studio/profile_image"
|
|
18
19
|
require "studio/oauth_identity"
|
|
@@ -192,6 +193,33 @@ module Studio
|
|
|
192
193
|
@@navbar_links = links
|
|
193
194
|
end
|
|
194
195
|
|
|
196
|
+
# ---- Navbar identity ----
|
|
197
|
+
# The name the signed-in user nav prints, and the signed-out button's label.
|
|
198
|
+
# Both defaults render the navbar byte-identical to before. The engine has no
|
|
199
|
+
# I18n catalogue, so the label is plain config rather than a locale key.
|
|
200
|
+
# Rules: lib/studio/navbar_identity.rb.
|
|
201
|
+
#
|
|
202
|
+
# config.navbar_user_name = :player_name # a method on the user
|
|
203
|
+
# config.navbar_user_name = ->(user, view) { user.player_name } # or a callable
|
|
204
|
+
# config.sign_in_label = "Sign in"
|
|
205
|
+
#
|
|
206
|
+
# nil means user.display_name. A method or callable that raises, or answers
|
|
207
|
+
# blank, falls back to display_name; a raise is reported once, never a 500.
|
|
208
|
+
mattr_reader :navbar_user_name, default: nil
|
|
209
|
+
|
|
210
|
+
def self.navbar_user_name=(config)
|
|
211
|
+
NavbarIdentity.validate_name!(config)
|
|
212
|
+
NavbarIdentity.reset_reported!
|
|
213
|
+
@@navbar_user_name = config
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
mattr_reader :sign_in_label, default: NavbarIdentity::DEFAULT_SIGN_IN_LABEL
|
|
217
|
+
|
|
218
|
+
def self.sign_in_label=(label)
|
|
219
|
+
NavbarIdentity.validate_label!(label)
|
|
220
|
+
@@sign_in_label = label
|
|
221
|
+
end
|
|
222
|
+
|
|
195
223
|
# ---- The shared profile page (/profile) ----
|
|
196
224
|
# The rows that make up /profile. `nil` — the default — means "the engine's
|
|
197
225
|
# standard page", NOT "no rows": a brand-new app with an empty initializer gets
|
|
@@ -902,6 +930,11 @@ module Studio
|
|
|
902
930
|
NavbarLinks.resolve(navbar_links, view)
|
|
903
931
|
end
|
|
904
932
|
|
|
933
|
+
# The signed-in user's navbar name for a view (lib/studio/navbar_identity.rb).
|
|
934
|
+
def self.navbar_user_name_for(user, view = nil)
|
|
935
|
+
NavbarIdentity.user_name(user, view, config: navbar_user_name)
|
|
936
|
+
end
|
|
937
|
+
|
|
905
938
|
# The engine's standard /profile rows. Hosts compose against this rather than
|
|
906
939
|
# restating a literal list, so a later release that adds a standard row
|
|
907
940
|
# delivers it to every app that used the seam as intended.
|
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.81.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex McRitchie
|
|
@@ -573,6 +573,7 @@ files:
|
|
|
573
573
|
- lib/studio/log_rotation.rb
|
|
574
574
|
- lib/studio/mail_transport.rb
|
|
575
575
|
- lib/studio/name_parts.rb
|
|
576
|
+
- lib/studio/navbar_identity.rb
|
|
576
577
|
- lib/studio/navbar_links.rb
|
|
577
578
|
- lib/studio/newsletter.rb
|
|
578
579
|
- lib/studio/oauth_identity.rb
|