studio-engine 0.17.0 → 0.18.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 +67 -0
- data/app/assets/tailwind/studio_engine/engine-motion.css +620 -5
- data/app/controllers/style_controller.rb +25 -0
- data/app/views/studio/modals/_crop_photo.html.erb +5 -1
- data/app/views/studio/modals/_host.html.erb +5 -0
- data/app/views/studio/modals/_image_upload.html.erb +16 -5
- data/app/views/studio/modals/_saving.html.erb +6 -2
- data/app/views/studio/modals/auth/_resend_footer.html.erb +25 -0
- data/app/views/studio/modals/blocks/_card_header.html.erb +83 -0
- data/app/views/studio/modals/blocks/_cta_redirect.html.erb +73 -0
- data/app/views/studio/modals/blocks/_onchain_success.html.erb +52 -0
- data/app/views/studio/modals/blocks/_progress_pill.html.erb +23 -0
- data/app/views/studio/modals/blocks/_shell.html.erb +35 -0
- data/app/views/studio/modals/blocks/_solana_tx_link.html.erb +42 -0
- data/app/views/studio/modals/shared/_age_attestation.html.erb +34 -0
- data/app/views/studio/modals/shared/_email_field.html.erb +70 -0
- data/app/views/studio/modals/templates/_action.html.erb +25 -0
- data/app/views/studio/modals/templates/_form.html.erb +25 -0
- data/app/views/studio/modals/templates/_status.html.erb +45 -0
- data/app/views/studio/modals/templates/_success.html.erb +23 -0
- data/app/views/studio/modals/templates/_wizard.html.erb +75 -0
- data/app/views/style/_modal_specimen.html.erb +120 -0
- data/app/views/style/_modals.html.erb +690 -0
- data/app/views/style/_specimen.html.erb +64 -0
- data/app/views/style/_tasks.html.erb +112 -0
- data/app/views/style/_theme.html.erb +222 -0
- data/app/views/style/_tricks.html.erb +323 -0
- data/app/views/style/index.html.erb +60 -0
- data/app/views/style/modals/_auth.html.erb +252 -0
- data/app/views/style/modals/_onchain_tx.html.erb +62 -0
- data/app/views/style/modals/_wallet_connect.html.erb +138 -0
- data/app/views/style/modals/_wallet_deposit.html.erb +56 -0
- data/lib/studio/version.rb +1 -1
- data/lib/studio.rb +27 -1
- metadata +32 -6
- data/app/controllers/design_system_controller.rb +0 -12
- data/app/views/design_system/_specimen.html.erb +0 -27
- data/app/views/design_system/index.html.erb +0 -232
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
On-chain transaction modal — design-system port of turf-monster's
|
|
3
|
+
modals/_onchain_tx, wired to the living style guide's page-scoped host. Reads
|
|
4
|
+
display state from $store.dsSolanaModal (a stateless proxy over $store.dsModals
|
|
5
|
+
that keeps the show/success/error API stable — the same façade shape the real
|
|
6
|
+
app ships). The proxy's methods push/pop the dsModals stack so this partial
|
|
7
|
+
renders when opened.
|
|
8
|
+
|
|
9
|
+
Three states — processing / success / error — cover the whole non-domain-
|
|
10
|
+
specific tx flow. The DS preview always uses the GENERIC success card
|
|
11
|
+
(studio/modals/blocks/_onchain_success); the rich contest-entry celebration
|
|
12
|
+
(seeds bar + level-up + lobby drain) is turf-specific and stays in the
|
|
13
|
+
consumer app.
|
|
14
|
+
|
|
15
|
+
Single root: content lives inside one <div> so Alpine's <template x-if> clones
|
|
16
|
+
it whole.
|
|
17
|
+
%>
|
|
18
|
+
<div x-data="{
|
|
19
|
+
init() {
|
|
20
|
+
this.$watch('$store.dsSolanaModal.state', function(val) {
|
|
21
|
+
if (val === 'success') {
|
|
22
|
+
setTimeout(function() {
|
|
23
|
+
try { if (window.fireSuccessConfetti) window.fireSuccessConfetti(); } catch (_) {}
|
|
24
|
+
}, 100);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}">
|
|
29
|
+
|
|
30
|
+
<%# === PROCESSING ====================================================== %>
|
|
31
|
+
<template x-if="$store.dsSolanaModal.state === 'processing'">
|
|
32
|
+
<%= render "studio/modals/blocks/card_header",
|
|
33
|
+
spinner: true,
|
|
34
|
+
title_key: "$store.dsSolanaModal.title",
|
|
35
|
+
subtitle_key: "$store.dsSolanaModal.message" %>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<%# === SUCCESS (generic) =============================================== %>
|
|
39
|
+
<template x-if="$store.dsSolanaModal.state === 'success'">
|
|
40
|
+
<%= render "studio/modals/blocks/onchain_success",
|
|
41
|
+
tx_signature_key: "$store.dsSolanaModal.txSignature",
|
|
42
|
+
title_key: "$store.dsSolanaModal.successTitle",
|
|
43
|
+
subtitle_key: "$store.dsSolanaModal.successSubtitle",
|
|
44
|
+
cta_label_key: "$store.dsSolanaModal.ctaLabel",
|
|
45
|
+
cta_href_key: "$store.dsSolanaModal.ctaHref",
|
|
46
|
+
modal_store: "dsModals" %>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<%# === ERROR =========================================================== %>
|
|
50
|
+
<template x-if="$store.dsSolanaModal.state === 'error'">
|
|
51
|
+
<div>
|
|
52
|
+
<%= render "studio/modals/blocks/card_header",
|
|
53
|
+
icon: :error,
|
|
54
|
+
title_key: "$store.dsSolanaModal.title",
|
|
55
|
+
subtitle_key: "$store.dsSolanaModal.errorMessage" %>
|
|
56
|
+
<button @click="$store.dsSolanaModal.close()"
|
|
57
|
+
class="block mx-auto mt-3 text-sm text-secondary hover:text-heading transition">
|
|
58
|
+
Close
|
|
59
|
+
</button>
|
|
60
|
+
</div>
|
|
61
|
+
</template>
|
|
62
|
+
</div>
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
"Connect Wallet" picker — design-system port of turf-monster's
|
|
3
|
+
modals/_wallet_connect, wired to the living style guide's page-scoped host
|
|
4
|
+
($store.dsModals). Faithful reown-style picker: detected wallets on top
|
|
5
|
+
(click to connect), featured-but-missing wallets below (install links), a
|
|
6
|
+
mobile deep-link row, and the legal-age attestation for guests.
|
|
7
|
+
|
|
8
|
+
Self-contained: window.walletProvider (a page stub) supplies the detected
|
|
9
|
+
wallet list; pick() runs a brief connecting state then closes (no real
|
|
10
|
+
connect). Opened via open('wallet-connect', {...}) or swap()ed to from auth.
|
|
11
|
+
|
|
12
|
+
Single root: the outer <div> is the host's required root.
|
|
13
|
+
%>
|
|
14
|
+
<div x-data="{
|
|
15
|
+
get props() { var c = Alpine.store('dsModals').current(); return (c && c.props) || {}; },
|
|
16
|
+
wallets: [],
|
|
17
|
+
installs: [
|
|
18
|
+
{ name: 'Phantom', url: 'https://phantom.app/download', icon: '/wallet-phantom.png' },
|
|
19
|
+
{ name: 'Solflare', url: 'https://solflare.com/download', icon: '/wallet-solflare.png' },
|
|
20
|
+
{ name: 'Backpack', url: 'https://backpack.app/downloads', icon: '/wallet-backpack.png' }
|
|
21
|
+
],
|
|
22
|
+
connecting: false,
|
|
23
|
+
picking: '',
|
|
24
|
+
error: '',
|
|
25
|
+
ageAttested: false,
|
|
26
|
+
ageError: false,
|
|
27
|
+
get needsAttestation() { return !this.props.linkMode; },
|
|
28
|
+
attested() {
|
|
29
|
+
if (!this.needsAttestation) return true;
|
|
30
|
+
if (this.ageAttested) return true;
|
|
31
|
+
this.ageError = true;
|
|
32
|
+
return false;
|
|
33
|
+
},
|
|
34
|
+
init() {
|
|
35
|
+
this.ageAttested = this.props.ageAttested === true;
|
|
36
|
+
this.refresh();
|
|
37
|
+
},
|
|
38
|
+
refresh() {
|
|
39
|
+
this.wallets = (window.walletProvider && window.walletProvider.available && window.walletProvider.available()) || [];
|
|
40
|
+
},
|
|
41
|
+
get isMobile() { return !!(window.walletProvider && window.walletProvider.isMobile && window.walletProvider.isMobile()); },
|
|
42
|
+
hasWallet(name) {
|
|
43
|
+
var n = ('' + name).toLowerCase();
|
|
44
|
+
return (this.wallets || []).some(function(w) { return w.name && w.name.toLowerCase() === n; });
|
|
45
|
+
},
|
|
46
|
+
get missingInstalls() {
|
|
47
|
+
var self = this;
|
|
48
|
+
return this.installs.filter(function(i) { return !self.hasWallet(i.name); });
|
|
49
|
+
},
|
|
50
|
+
pick(name) {
|
|
51
|
+
if (this.connecting) return;
|
|
52
|
+
if (!this.attested()) return;
|
|
53
|
+
this.connecting = true; this.picking = name; this.error = '';
|
|
54
|
+
var self = this;
|
|
55
|
+
setTimeout(function () { self.connecting = false; self.picking = ''; Alpine.store('dsModals').close(); }, 1100);
|
|
56
|
+
},
|
|
57
|
+
deepLink() {
|
|
58
|
+
if (!this.attested()) return;
|
|
59
|
+
this.pick('Phantom');
|
|
60
|
+
},
|
|
61
|
+
back() {
|
|
62
|
+
if (this.connecting) return;
|
|
63
|
+
if (this.props.backTo === 'auth') {
|
|
64
|
+
Alpine.store('dsModals').swap('auth',
|
|
65
|
+
{ step: 'credentials', submitting: null, formError: '' },
|
|
66
|
+
{ direction: 'back' });
|
|
67
|
+
} else {
|
|
68
|
+
Alpine.store('dsModals').close();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}"
|
|
72
|
+
class="relative">
|
|
73
|
+
|
|
74
|
+
<div class="relative mb-4">
|
|
75
|
+
<h3 class="text-heading font-bold text-lg text-center pt-1">Connect Wallet</h3>
|
|
76
|
+
<button @click="$store.dsModals.close()"
|
|
77
|
+
class="absolute top-0 right-0 -mr-1 text-secondary hover:text-heading text-xl leading-none"
|
|
78
|
+
aria-label="Close">×</button>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<template x-if="needsAttestation">
|
|
82
|
+
<div class="mb-3">
|
|
83
|
+
<%= render "studio/modals/shared/age_attestation" %>
|
|
84
|
+
</div>
|
|
85
|
+
</template>
|
|
86
|
+
|
|
87
|
+
<div class="space-y-2">
|
|
88
|
+
<%# Detected wallets — click to connect. %>
|
|
89
|
+
<template x-for="w in wallets" :key="w.name">
|
|
90
|
+
<button type="button" @click="pick(w.name)" :disabled="connecting"
|
|
91
|
+
class="w-full flex items-center gap-3 p-3 rounded-xl bg-surface-alt border border-strong hover:bg-surface transition text-left disabled:opacity-60 disabled:cursor-wait">
|
|
92
|
+
<span class="w-9 h-9 rounded-lg bg-inset flex items-center justify-center text-sm font-bold text-heading" x-text="w.name.slice(0,1)"></span>
|
|
93
|
+
<span class="font-semibold text-heading" x-text="w.name"></span>
|
|
94
|
+
<span class="ml-auto flex items-center gap-2">
|
|
95
|
+
<span x-show="picking === w.name" class="inline-flex items-center gap-1.5 text-xs text-secondary"><span class="spinner" aria-hidden="true"></span>Connecting…</span>
|
|
96
|
+
<span x-show="picking !== w.name" class="badge border-primary text-primary">Installed</span>
|
|
97
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4 text-muted">
|
|
98
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
|
|
99
|
+
</svg>
|
|
100
|
+
</span>
|
|
101
|
+
</button>
|
|
102
|
+
</template>
|
|
103
|
+
|
|
104
|
+
<%# Featured wallets that aren't installed — open the install page. %>
|
|
105
|
+
<template x-for="i in missingInstalls" :key="i.name">
|
|
106
|
+
<a :href="i.url" target="_blank" rel="noopener noreferrer"
|
|
107
|
+
class="w-full flex items-center gap-3 p-3 rounded-xl bg-surface-alt border border-strong hover:bg-surface transition no-underline">
|
|
108
|
+
<span class="w-9 h-9 rounded-lg bg-inset flex items-center justify-center text-sm font-bold text-muted" x-text="i.name.slice(0,1)"></span>
|
|
109
|
+
<span class="font-semibold text-heading" x-text="i.name"></span>
|
|
110
|
+
<span class="ml-auto flex items-center gap-2">
|
|
111
|
+
<span class="text-xs text-muted uppercase tracking-wide">Install</span>
|
|
112
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4 text-muted">
|
|
113
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
|
|
114
|
+
</svg>
|
|
115
|
+
</span>
|
|
116
|
+
</a>
|
|
117
|
+
</template>
|
|
118
|
+
|
|
119
|
+
<%# Mobile (no extension) — deep-link into the Phantom app. %>
|
|
120
|
+
<button x-show="isMobile" type="button" @click="deepLink()"
|
|
121
|
+
class="w-full flex items-center gap-3 p-3 rounded-xl bg-surface-alt border border-strong hover:bg-surface transition text-left">
|
|
122
|
+
<span class="w-9 h-9 rounded-lg bg-inset flex items-center justify-center text-sm font-bold text-heading">P</span>
|
|
123
|
+
<span class="font-semibold text-heading">Open in Phantom app</span>
|
|
124
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="ml-auto w-4 h-4 text-muted">
|
|
125
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
|
|
126
|
+
</svg>
|
|
127
|
+
</button>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<template x-if="error">
|
|
131
|
+
<p class="text-red-400 text-sm mt-3 text-center" x-text="error"></p>
|
|
132
|
+
</template>
|
|
133
|
+
|
|
134
|
+
<button type="button" @click="back()" :disabled="connecting"
|
|
135
|
+
class="mt-4 w-full text-center text-sm text-secondary hover:text-heading disabled:opacity-50">
|
|
136
|
+
← Back
|
|
137
|
+
</button>
|
|
138
|
+
</div>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Wallet deposit / top-up modal — design-system port of turf-monster's
|
|
3
|
+
modals/_wallet_deposit, wired to the living style guide's page-scoped host
|
|
4
|
+
($store.dsModals). Single-card design (no step machinery): the shortfall
|
|
5
|
+
math, a "Pay with Card" CTA, and a copy-your-address self-fund row.
|
|
6
|
+
|
|
7
|
+
Self-contained: the production Stripe form + CDP onramp button are replaced
|
|
8
|
+
with a neutral demo CTA, and the wallet address reads from props (a demo
|
|
9
|
+
value) rather than $store.session. Opened via
|
|
10
|
+
open('wallet-deposit', { neededCents, usdcCents, usdtCents, address }).
|
|
11
|
+
|
|
12
|
+
Single root: the outer <div> is the host's required root.
|
|
13
|
+
%>
|
|
14
|
+
<div x-data="{ get props() { return (Alpine.store('dsModals').current() || {}).props || {} } }"
|
|
15
|
+
class="relative">
|
|
16
|
+
|
|
17
|
+
<button @click="$store.dsModals.close()"
|
|
18
|
+
class="absolute top-0 right-0 -mt-2 -mr-2 text-secondary hover:text-heading text-xl leading-none z-10"
|
|
19
|
+
aria-label="Close">×</button>
|
|
20
|
+
|
|
21
|
+
<div class="text-center pt-1 mb-3">
|
|
22
|
+
<h3 class="text-heading font-bold text-lg leading-tight">Top Up Your Wallet</h3>
|
|
23
|
+
</div>
|
|
24
|
+
<p class="text-xs text-secondary mb-4 text-center">
|
|
25
|
+
You need $<span class="font-mono" x-text="((props.neededCents || 0) / 100).toFixed(2)"></span>
|
|
26
|
+
to enter.
|
|
27
|
+
You have $<span class="font-mono" x-text="((props.usdcCents || 0) / 100).toFixed(2)"></span> USDC
|
|
28
|
+
+ $<span class="font-mono" x-text="((props.usdtCents || 0) / 100).toFixed(2)"></span> USDT.
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
<div class="space-y-3">
|
|
32
|
+
<%# Demo pay CTA — the production modal posts to Stripe checkout in a new
|
|
33
|
+
tab; here it just closes (no real payment). %>
|
|
34
|
+
<button type="button" @click="$store.dsModals.close()" class="btn btn-primary btn-lg w-full">
|
|
35
|
+
Pay with Card
|
|
36
|
+
</button>
|
|
37
|
+
|
|
38
|
+
<%# Send-to-address — self-fund from another wallet. Address from props. %>
|
|
39
|
+
<div class="border-t border-strong pt-3" x-data="{ copied: false }">
|
|
40
|
+
<p class="text-xs text-secondary text-center mb-2">Or send USDC/USDT to your wallet:</p>
|
|
41
|
+
<div class="flex items-center gap-2">
|
|
42
|
+
<code class="flex-1 px-2 py-1 bg-surface rounded text-xs font-mono truncate"
|
|
43
|
+
x-text="props.address || 'Fo1L5…demo…9xQ2'"></code>
|
|
44
|
+
<button type="button"
|
|
45
|
+
@click="navigator.clipboard && navigator.clipboard.writeText(props.address || ''); copied = true; setTimeout(() => copied = false, 1500)"
|
|
46
|
+
class="btn btn-sm">
|
|
47
|
+
<span x-text="copied ? 'Copied' : 'Copy'"></span>
|
|
48
|
+
</button>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<p class="text-center text-xs text-muted mt-4">
|
|
54
|
+
Come back here once your deposit lands — we'll refresh the balance.
|
|
55
|
+
</p>
|
|
56
|
+
</div>
|
data/lib/studio/version.rb
CHANGED
data/lib/studio.rb
CHANGED
|
@@ -33,6 +33,20 @@ module Studio
|
|
|
33
33
|
# contract check — see validate_user_contract!).
|
|
34
34
|
mattr_accessor :auth_methods, default: %i[magic_link google wallet]
|
|
35
35
|
|
|
36
|
+
# ---- Capability features --------------------------------------------------
|
|
37
|
+
# Coarse app-level capability switches an app opts into. Distinct from
|
|
38
|
+
# auth_methods (which sign-in methods the login views render) — these gate
|
|
39
|
+
# whole product surfaces (e.g. :web3 on-chain features, :leveling XP/levels).
|
|
40
|
+
# Default [] means every capability is OFF; an app turns on what it ships in
|
|
41
|
+
# config/initializers/studio.rb:
|
|
42
|
+
#
|
|
43
|
+
# Studio.configure { |config| config.features = %i[leveling web3] }
|
|
44
|
+
#
|
|
45
|
+
# Gate a surface with Studio.feature?(:leveling). The admin/style page uses this
|
|
46
|
+
# to render capability-gated specimens "disabled but present" on apps with the
|
|
47
|
+
# feature off (e.g. McRitchie Studio, which ships neither).
|
|
48
|
+
mattr_accessor :features, default: []
|
|
49
|
+
|
|
36
50
|
# Magic-link (passwordless email) tuning. token_name keys the MessageVerifier
|
|
37
51
|
# purpose; bump it to invalidate every outstanding link. See MagicLink service.
|
|
38
52
|
mattr_accessor :magic_link_ttl, default: 15.minutes
|
|
@@ -152,6 +166,14 @@ module Studio
|
|
|
152
166
|
auth_methods.include?(method.to_sym)
|
|
153
167
|
end
|
|
154
168
|
|
|
169
|
+
# True when the given capability feature is enabled for this app. Mirrors
|
|
170
|
+
# auth_method? — apps opt in via config.features in their initializer (see the
|
|
171
|
+
# Studio.features accessor). Tolerates String or Symbol entries and any
|
|
172
|
+
# Enumerable (Array or Set), so `feature?("web3")` and `feature?(:web3)` agree.
|
|
173
|
+
def self.feature?(name)
|
|
174
|
+
features.any? { |f| f.to_sym == name.to_sym }
|
|
175
|
+
end
|
|
176
|
+
|
|
155
177
|
# True when the engine login should render a PASSWORD field/form: passwords are enabled
|
|
156
178
|
# (`:password` in auth_methods) AND the host User model actually supports them (responds to
|
|
157
179
|
# `authenticate` — i.e. `has_secure_password`). Both are required, and the second is the
|
|
@@ -324,7 +346,11 @@ module Studio
|
|
|
324
346
|
patch "admin/theme", to: "theme_settings#update", as: :admin_theme_update
|
|
325
347
|
post "admin/theme/regenerate", to: "theme_settings#regenerate", as: :admin_theme_regenerate
|
|
326
348
|
get "admin/schema", to: "schema#index", as: :admin_schema
|
|
327
|
-
|
|
349
|
+
# The living style guide. Canonical at /admin/style (StyleController#index);
|
|
350
|
+
# /admin/design_system redirects here but KEEPS its admin_design_system_path
|
|
351
|
+
# helper so a shipped host sidebar link on the old helper still resolves.
|
|
352
|
+
get "admin/style", to: "style#index", as: :admin_style
|
|
353
|
+
get "admin/design_system", to: redirect("/admin/style"), as: :admin_design_system
|
|
328
354
|
|
|
329
355
|
# Admin-managed transactional-email banner images (Studio::EmailImage).
|
|
330
356
|
# index lists each managed email variant + its current banner; update
|
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: studio-engine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.18.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex McRitchie
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-07-27 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: rails
|
|
@@ -178,7 +179,6 @@ files:
|
|
|
178
179
|
- app/controllers/concerns/studio/error_handling.rb
|
|
179
180
|
- app/controllers/concerns/studio/impersonation.rb
|
|
180
181
|
- app/controllers/concerns/studio/link_consumption.rb
|
|
181
|
-
- app/controllers/design_system_controller.rb
|
|
182
182
|
- app/controllers/error_logs_controller.rb
|
|
183
183
|
- app/controllers/magic_links_controller.rb
|
|
184
184
|
- app/controllers/navbar_controller.rb
|
|
@@ -191,6 +191,7 @@ files:
|
|
|
191
191
|
- app/controllers/studio/links_controller.rb
|
|
192
192
|
- app/controllers/studio/local_emails_controller.rb
|
|
193
193
|
- app/controllers/studio/models_controller.rb
|
|
194
|
+
- app/controllers/style_controller.rb
|
|
194
195
|
- app/controllers/theme_settings_controller.rb
|
|
195
196
|
- app/helpers/studio/admin_models_table_helper.rb
|
|
196
197
|
- app/helpers/studio_email_delivery_helper.rb
|
|
@@ -228,8 +229,6 @@ files:
|
|
|
228
229
|
- app/views/components/_theme_toggle.html.erb
|
|
229
230
|
- app/views/components/_theme_toggle_morph.html.erb
|
|
230
231
|
- app/views/components/_user_nav.html.erb
|
|
231
|
-
- app/views/design_system/_specimen.html.erb
|
|
232
|
-
- app/views/design_system/index.html.erb
|
|
233
232
|
- app/views/error_logs/index.html.erb
|
|
234
233
|
- app/views/error_logs/show.html.erb
|
|
235
234
|
- app/views/layouts/_navbar.html.erb
|
|
@@ -261,11 +260,36 @@ files:
|
|
|
261
260
|
- app/views/studio/modals/_host.html.erb
|
|
262
261
|
- app/views/studio/modals/_image_upload.html.erb
|
|
263
262
|
- app/views/studio/modals/_saving.html.erb
|
|
263
|
+
- app/views/studio/modals/auth/_resend_footer.html.erb
|
|
264
|
+
- app/views/studio/modals/blocks/_card_header.html.erb
|
|
265
|
+
- app/views/studio/modals/blocks/_cta_redirect.html.erb
|
|
264
266
|
- app/views/studio/modals/blocks/_error_card.html.erb
|
|
267
|
+
- app/views/studio/modals/blocks/_onchain_success.html.erb
|
|
265
268
|
- app/views/studio/modals/blocks/_processing_card.html.erb
|
|
266
269
|
- app/views/studio/modals/blocks/_progress_countdown.html.erb
|
|
270
|
+
- app/views/studio/modals/blocks/_progress_pill.html.erb
|
|
271
|
+
- app/views/studio/modals/blocks/_shell.html.erb
|
|
272
|
+
- app/views/studio/modals/blocks/_solana_tx_link.html.erb
|
|
267
273
|
- app/views/studio/modals/blocks/_success_card.html.erb
|
|
274
|
+
- app/views/studio/modals/shared/_age_attestation.html.erb
|
|
275
|
+
- app/views/studio/modals/shared/_email_field.html.erb
|
|
276
|
+
- app/views/studio/modals/templates/_action.html.erb
|
|
277
|
+
- app/views/studio/modals/templates/_form.html.erb
|
|
278
|
+
- app/views/studio/modals/templates/_status.html.erb
|
|
279
|
+
- app/views/studio/modals/templates/_success.html.erb
|
|
280
|
+
- app/views/studio/modals/templates/_wizard.html.erb
|
|
268
281
|
- app/views/studio/models/show.html.erb
|
|
282
|
+
- app/views/style/_modal_specimen.html.erb
|
|
283
|
+
- app/views/style/_modals.html.erb
|
|
284
|
+
- app/views/style/_specimen.html.erb
|
|
285
|
+
- app/views/style/_tasks.html.erb
|
|
286
|
+
- app/views/style/_theme.html.erb
|
|
287
|
+
- app/views/style/_tricks.html.erb
|
|
288
|
+
- app/views/style/index.html.erb
|
|
289
|
+
- app/views/style/modals/_auth.html.erb
|
|
290
|
+
- app/views/style/modals/_onchain_tx.html.erb
|
|
291
|
+
- app/views/style/modals/_wallet_connect.html.erb
|
|
292
|
+
- app/views/style/modals/_wallet_deposit.html.erb
|
|
269
293
|
- app/views/theme_settings/edit.html.erb
|
|
270
294
|
- app/views/user_mailer/magic_link.html.erb
|
|
271
295
|
- app/views/user_mailer/magic_link.text.erb
|
|
@@ -301,6 +325,7 @@ metadata:
|
|
|
301
325
|
source_code_uri: https://github.com/amcritchie/studio-engine/tree/main
|
|
302
326
|
bug_tracker_uri: https://github.com/amcritchie/studio-engine/issues
|
|
303
327
|
changelog_uri: https://github.com/amcritchie/studio-engine/blob/main/CHANGELOG.md
|
|
328
|
+
post_install_message:
|
|
304
329
|
rdoc_options: []
|
|
305
330
|
require_paths:
|
|
306
331
|
- lib
|
|
@@ -315,7 +340,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
315
340
|
- !ruby/object:Gem::Version
|
|
316
341
|
version: '0'
|
|
317
342
|
requirements: []
|
|
318
|
-
rubygems_version:
|
|
343
|
+
rubygems_version: 3.5.22
|
|
344
|
+
signing_key:
|
|
319
345
|
specification_version: 4
|
|
320
346
|
summary: Shared Rails engine providing auth, SSO, error logging, theming, and S3-backed
|
|
321
347
|
image caching
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# The living style guide — a single admin-gated page that renders the engine's
|
|
2
|
-
# shared UI primitives (buttons, surfaces, the seven motion primitives, and the
|
|
3
|
-
# theme role tokens) live in the host app's theme. Mirrors ThemeSettingsController:
|
|
4
|
-
# a plain host-inherited controller, gated by `require_admin` from the already-
|
|
5
|
-
# included Studio::ErrorHandling concern. The view is a bare content wrapper that
|
|
6
|
-
# renders inside each host's application.html.erb, so it inherits that app's
|
|
7
|
-
# navbar + theme automatically.
|
|
8
|
-
class DesignSystemController < ApplicationController
|
|
9
|
-
before_action :require_admin
|
|
10
|
-
|
|
11
|
-
def index; end
|
|
12
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
<%# Specimen card for the living style guide (admin/design_system).
|
|
2
|
-
Reusable chrome around ONE primitive:
|
|
3
|
-
stage - the rendered primitive (yielded from the caller block)
|
|
4
|
-
meta - its class name and a copyable usage snippet
|
|
5
|
-
locals: klass (String, required) and usage (String, optional).
|
|
6
|
-
Rendered via `render layout: "design_system/specimen"` so the caller's
|
|
7
|
-
block becomes the staged primitive. Checks local_assigns (never
|
|
8
|
-
block_given?) so a shared partial does not inherit the host layout yield. %>
|
|
9
|
-
<article class="card overflow-hidden flex flex-col">
|
|
10
|
-
<div class="flex flex-1 flex-wrap items-center justify-center gap-4 p-6 bg-surface-alt min-h-32">
|
|
11
|
-
<%= yield %>
|
|
12
|
-
</div>
|
|
13
|
-
<div class="border-t border-subtle p-3 space-y-2">
|
|
14
|
-
<code class="block font-mono text-2xs text-heading break-all"><%= local_assigns[:klass] %></code>
|
|
15
|
-
<% snippet = local_assigns[:usage] %>
|
|
16
|
-
<% if snippet.present? %>
|
|
17
|
-
<div class="relative" x-data="{ copied: false }">
|
|
18
|
-
<pre class="bg-inset text-body text-2xs rounded-lg px-3 py-2 pr-16 overflow-x-auto"><code x-ref="snippet"><%= snippet %></code></pre>
|
|
19
|
-
<button type="button"
|
|
20
|
-
class="btn btn-neutral btn-sm absolute top-2 right-2"
|
|
21
|
-
@click="navigator.clipboard?.writeText($refs.snippet.innerText); copied = true; setTimeout(() => copied = false, 1200)">
|
|
22
|
-
<span x-text="copied ? 'Copied' : 'Copy'">Copy</span>
|
|
23
|
-
</button>
|
|
24
|
-
</div>
|
|
25
|
-
<% end %>
|
|
26
|
-
</div>
|
|
27
|
-
</article>
|