studio-engine 0.19.0 → 0.21.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 +119 -0
- data/app/assets/tailwind/studio_engine/engine-motion.css +58 -0
- data/app/views/studio/_age_verify_assets.html.erb +120 -0
- data/app/views/studio/modals/blocks/_age_verify.html.erb +106 -0
- data/app/views/studio/modals/blocks/_digit_reel.html.erb +41 -0
- data/app/views/studio/modals/blocks/_entry_confirmed.html.erb +86 -0
- data/app/views/studio/modals/blocks/_free_entry_earned.html.erb +46 -0
- data/app/views/studio/modals/blocks/_seeds_bar.html.erb +125 -0
- data/app/views/studio/modals/blocks/_wallet_brand_sprite.html.erb +40 -0
- data/app/views/style/_modals.html.erb +178 -6
- data/app/views/style/modals/_age_verify.html.erb +17 -0
- data/app/views/style/modals/_entry_tokens.html.erb +123 -0
- data/app/views/style/modals/_wallet_connect.html.erb +33 -6
- data/lib/studio/version.rb +1 -1
- metadata +15 -3
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Seeds progress bar — the leveling celebration's animated 5-segment fill + rolling
|
|
3
|
+
digit reel, with a 4-phase level-up sequence (fill to full → level pop → drain to
|
|
4
|
+
empty → refill toward the next level). Ported from turf-monster's entry-confirmed
|
|
5
|
+
inline bar and generalized: seed counts / level are APP-SUPPLIED (Alpine
|
|
6
|
+
expressions), and Turf-specific copy is a parameter. The engine renders the bar;
|
|
7
|
+
the app feeds the numbers.
|
|
8
|
+
|
|
9
|
+
Capability: this is a :leveling primitive. Callers render it only under
|
|
10
|
+
Studio.feature?(:leveling) (the engine's _entry_confirmed does exactly that), so
|
|
11
|
+
a web3-only app (leveling off) never mounts it.
|
|
12
|
+
|
|
13
|
+
Single root: the outer <div> is one element so a <template x-if> clones it whole.
|
|
14
|
+
|
|
15
|
+
Locals:
|
|
16
|
+
seeds_earned_key — Alpine expr: seeds awarded this event (drives whether a
|
|
17
|
+
level was crossed). Required.
|
|
18
|
+
seeds_total_key — Alpine expr: the user's new total seeds. Required.
|
|
19
|
+
seeds_per_level — Integer: seeds per level (default 100). The bar fills to
|
|
20
|
+
(total % per_level) / per_level; a crossed multiple triggers
|
|
21
|
+
the level-up flourish.
|
|
22
|
+
free_entry_label — String shown when a level is crossed (default
|
|
23
|
+
"Free Entry Earned 🎟️"). Parameterized so the reward copy
|
|
24
|
+
is not hardcoded to any one app.
|
|
25
|
+
|
|
26
|
+
The co-located x-data owns its own _timers array; destroy() clears every pending
|
|
27
|
+
setTimeout so a mid-celebration modal close leaks nothing.
|
|
28
|
+
%>
|
|
29
|
+
<%
|
|
30
|
+
seeds_earned_key = local_assigns.fetch(:seeds_earned_key)
|
|
31
|
+
seeds_total_key = local_assigns.fetch(:seeds_total_key)
|
|
32
|
+
seeds_per_level = local_assigns.fetch(:seeds_per_level, 100)
|
|
33
|
+
free_entry_label = local_assigns.fetch(:free_entry_label, "Free Entry Earned 🎟️")
|
|
34
|
+
%>
|
|
35
|
+
<div x-data="{
|
|
36
|
+
displaySeeds: 0,
|
|
37
|
+
displayLevel: 1,
|
|
38
|
+
levelingUp: false,
|
|
39
|
+
showFreeEntryBadge: false,
|
|
40
|
+
_timers: [],
|
|
41
|
+
init() {
|
|
42
|
+
var self = this;
|
|
43
|
+
var perLevel = <%= seeds_per_level %>;
|
|
44
|
+
var total = (<%= seeds_total_key %>) || 0;
|
|
45
|
+
var earned = (<%= seeds_earned_key %>) || 0;
|
|
46
|
+
var oldSeeds = Math.max(0, total - earned);
|
|
47
|
+
var oldLevel = Math.floor(oldSeeds / perLevel) + 1;
|
|
48
|
+
var newLevel = Math.floor(total / perLevel) + 1;
|
|
49
|
+
var oldToward = oldSeeds % perLevel;
|
|
50
|
+
var newToward = total % perLevel;
|
|
51
|
+
self.displaySeeds = oldToward;
|
|
52
|
+
self.displayLevel = oldLevel;
|
|
53
|
+
var schedule = function (fn, ms) { self._timers.push(setTimeout(fn, ms)); };
|
|
54
|
+
schedule(function () {
|
|
55
|
+
if (newLevel > oldLevel) {
|
|
56
|
+
self.levelingUp = true;
|
|
57
|
+
self.displaySeeds = perLevel;
|
|
58
|
+
schedule(function () {
|
|
59
|
+
self.displayLevel = newLevel;
|
|
60
|
+
self.showFreeEntryBadge = true;
|
|
61
|
+
}, 1900);
|
|
62
|
+
schedule(function () { self.displaySeeds = 0; }, 3400);
|
|
63
|
+
schedule(function () {
|
|
64
|
+
self.displaySeeds = newToward;
|
|
65
|
+
self.levelingUp = false;
|
|
66
|
+
}, 3600);
|
|
67
|
+
} else {
|
|
68
|
+
self.displaySeeds = newToward;
|
|
69
|
+
}
|
|
70
|
+
}, 2000);
|
|
71
|
+
},
|
|
72
|
+
destroy() {
|
|
73
|
+
this._timers.forEach(clearTimeout);
|
|
74
|
+
this._timers = [];
|
|
75
|
+
}
|
|
76
|
+
}"
|
|
77
|
+
class="mx-auto mb-1" style="max-width: 100%;">
|
|
78
|
+
|
|
79
|
+
<%# The bar (hidden once the level-up swaps to the reward line). Single root. %>
|
|
80
|
+
<template x-if="!showFreeEntryBadge">
|
|
81
|
+
<%# --seeds-fill-dur cascades to the bar (.seeds-bar-continuous) AND the reel
|
|
82
|
+
(.seeds-reel-track) so both share the exact 1.9s fill; the transition
|
|
83
|
+
PROPERTY itself lives on the classes so reduced-motion can cancel it. %>
|
|
84
|
+
<div class="relative" style="--seeds-fill-dur: 1.9s;">
|
|
85
|
+
<div class="seeds-bar-continuous flex gap-1.5"
|
|
86
|
+
style="height: 22px;"
|
|
87
|
+
:style="{ '--bar-progress': (displaySeeds / <%= seeds_per_level %> * 100) }">
|
|
88
|
+
<template x-for="i in 5" :key="i">
|
|
89
|
+
<div class="flex-1 relative overflow-hidden rounded-md"
|
|
90
|
+
style="background: rgb(var(--color-primary-500-rgb) / 0.06); border: 1.5px solid rgb(var(--color-primary-500-rgb) / 0.2);">
|
|
91
|
+
<div class="absolute inset-0 pointer-events-none"
|
|
92
|
+
:style="{
|
|
93
|
+
backgroundPosition: ((i - 1) * 25) + '% 0',
|
|
94
|
+
clipPath: 'inset(0 calc(100% - clamp(0%, calc((var(--bar-progress) - ' + ((i - 1) * 20) + ') * 5%), 100%)) 0 0)'
|
|
95
|
+
}"
|
|
96
|
+
style="background-image: linear-gradient(90deg, color-mix(in srgb, var(--color-cta) 30%, transparent), color-mix(in srgb, var(--color-cta) 60%, transparent)); background-size: 500% 100%; background-repeat: no-repeat;"></div>
|
|
97
|
+
<div class="seeds-shimmer absolute top-0 bottom-0 pointer-events-none"
|
|
98
|
+
:style="{ left: -(i - 1) * 100 + '%', width: '500%' }"
|
|
99
|
+
style="background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.55) 50%, transparent 100%);"></div>
|
|
100
|
+
</div>
|
|
101
|
+
</template>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="absolute inset-y-0 right-2 flex items-center pointer-events-none">
|
|
104
|
+
<span class="inline-flex items-baseline gap-1 font-mono font-bold text-sm" style="color: white; text-shadow: 0 1px 2px rgba(0,0,0,0.7);">
|
|
105
|
+
<%# track_class hands the reel a class-based transition (reduced-motion
|
|
106
|
+
reachable) instead of an inline one; duration via --seeds-fill-dur. %>
|
|
107
|
+
<%= render "studio/modals/blocks/digit_reel",
|
|
108
|
+
value: "displaySeeds", max: seeds_per_level, width: "3.2ch", cell_em: 1.25,
|
|
109
|
+
track_class: "seeds-reel-track" %>
|
|
110
|
+
<span class="text-[10px] font-normal opacity-90">seeds</span>
|
|
111
|
+
</span>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</template>
|
|
115
|
+
|
|
116
|
+
<%# Level-up reward — a plain text line sized to the same 22px height as the bar
|
|
117
|
+
it replaces, so the card does not shift when the bar swaps to this. Single
|
|
118
|
+
root. Copy is caller-parameterized (free_entry_label). %>
|
|
119
|
+
<template x-if="showFreeEntryBadge">
|
|
120
|
+
<div class="level-up-pop flex items-center justify-center w-full font-bold text-sm"
|
|
121
|
+
style="height: 22px; color: var(--color-cta);">
|
|
122
|
+
<span><%= free_entry_label %></span>
|
|
123
|
+
</div>
|
|
124
|
+
</template>
|
|
125
|
+
</div>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Wallet brand-icon sprite — inline brand marks for the wallets the Connect-Wallet
|
|
3
|
+
picker knows about (Phantom, Solflare, Backpack), OWNED BY THE ENGINE so every
|
|
4
|
+
consumer inherits crisp brand icons with ZERO per-app asset files. Before this,
|
|
5
|
+
the picker referenced app-served PNGs (/wallet-phantom.png, ...), so a fresh app
|
|
6
|
+
that had not copied those into its own public/ showed 404 letter tiles (P/S/B).
|
|
7
|
+
|
|
8
|
+
Render this ONCE inside a picker's single root, then reference a mark by name:
|
|
9
|
+
|
|
10
|
+
<svg class="w-9 h-9"><use href="#se-wallet-phantom"></use></svg>
|
|
11
|
+
|
|
12
|
+
or, in an Alpine x-for over detected wallets, resolve the id from the name:
|
|
13
|
+
|
|
14
|
+
<use :href="'#se-wallet-' + brandIcon(w.name)"></use> (brandIcon() lowercases
|
|
15
|
+
the name and returns it only for a known brand, else null → letter fallback).
|
|
16
|
+
|
|
17
|
+
The sprite is visually hidden (zero-box, absolute) and aria-hidden; only the
|
|
18
|
+
<use> references paint. Each <symbol> carries the OFFICIAL brand asset embedded
|
|
19
|
+
inline as a base64 PNG data-URI <image> — byte-identical to the real logos each
|
|
20
|
+
wallet ships (matching Turf Monster's public/wallet-*.png), so no hand-drawn
|
|
21
|
+
approximation drifts from the brand. The letter tile stays the fallback for any
|
|
22
|
+
wallet not in this set.
|
|
23
|
+
%>
|
|
24
|
+
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"
|
|
25
|
+
style="position:absolute;width:0;height:0;overflow:hidden" data-wallet-brand-sprite>
|
|
26
|
+
<%# Phantom — official brand mark (light-purple tile, cream ghost). %>
|
|
27
|
+
<symbol id="se-wallet-phantom" viewBox="0 0 460 460">
|
|
28
|
+
<image width="460" height="460" href="data:image/png;base64,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"/>
|
|
29
|
+
</symbol>
|
|
30
|
+
|
|
31
|
+
<%# Solflare — official brand mark (yellow tile, black blackletter S). %>
|
|
32
|
+
<symbol id="se-wallet-solflare" viewBox="0 0 361 361">
|
|
33
|
+
<image width="361" height="361" href="data:image/png;base64,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"/>
|
|
34
|
+
</symbol>
|
|
35
|
+
|
|
36
|
+
<%# Backpack — official brand mark (red tile, white pack). %>
|
|
37
|
+
<symbol id="se-wallet-backpack" viewBox="0 0 128 128">
|
|
38
|
+
<image width="128" height="128" href="data:image/png;base64,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"/>
|
|
39
|
+
</symbol>
|
|
40
|
+
</svg>
|
|
@@ -14,11 +14,16 @@
|
|
|
14
14
|
card behind ours. So this section owns a tiny, self-contained store + overlay
|
|
15
15
|
and registers all specimen content itself.
|
|
16
16
|
|
|
17
|
-
Groups: Auth (the sign-in step machine),
|
|
18
|
-
|
|
17
|
+
Groups: Auth (the sign-in step machine), Eligibility and entry (the
|
|
18
|
+
app-supplied-policy age-gate DOB primitive, gated by Studio.feature?(:age_gate),
|
|
19
|
+
plus the app-specific entry-tokens purchase flow documented via engine chrome),
|
|
20
|
+
Profile (crop / upload, opened
|
|
21
|
+
through studio/cropper_assets), Web3 (wallet-connect with engine-owned inline
|
|
22
|
+
brand icons / on-chain-tx / deposit / the generic entry-confirmed celebration,
|
|
19
23
|
gated by Studio.feature?(:web3) — disabled-but-present-yet-openable when off),
|
|
20
24
|
System and status (the reusable card blocks), Templates (the copy-from
|
|
21
|
-
archetypes), and Rewards (level-up
|
|
25
|
+
archetypes), and Rewards (level-up + the entry-confirmed seeds bar + the
|
|
26
|
+
Free Entry Earned reward, gated by :leveling).
|
|
22
27
|
|
|
23
28
|
The modal motion (modal-card-* / modal-backdrop-* keyframes + studio-modal-
|
|
24
29
|
drain + body.modal-open) ships in engine-motion.css, so a consumer that
|
|
@@ -26,6 +31,7 @@
|
|
|
26
31
|
<%
|
|
27
32
|
web3_on = Studio.feature?(:web3)
|
|
28
33
|
leveling_on = Studio.feature?(:leveling)
|
|
34
|
+
age_gate_on = Studio.feature?(:age_gate)
|
|
29
35
|
|
|
30
36
|
# Auth method-toggle defaults — mirror the app's Studio.auth_method? config;
|
|
31
37
|
# Solana Wallet also needs the web3 capability, so it defaults OFF on an app
|
|
@@ -49,8 +55,17 @@
|
|
|
49
55
|
expr
|
|
50
56
|
end
|
|
51
57
|
%>
|
|
58
|
+
<%# DEMO flow wiring (style-page only): the age-gate primitive fires an
|
|
59
|
+
'age-verified' window event on a successful confirm — its designed handoff
|
|
60
|
+
hook, the same one a production app listens for to resume the entry. Here the
|
|
61
|
+
style page plays that "app": on age-verified it opens the entry-token purchase
|
|
62
|
+
modal, so the DS walks eligibility to purchase in sequence. The engine
|
|
63
|
+
primitive is untouched; its real submit still posts to the app-supplied
|
|
64
|
+
submit_url. Kept on the section (always mounted) so it fires even as the
|
|
65
|
+
age-gate card closes. %>
|
|
52
66
|
<section id="modals" class="space-y-10" style="scroll-margin-top: calc(var(--nav-h, 0px) + 5rem)"
|
|
53
|
-
x-data="{}" @ds-modal-close.window="$store.dsModals.close()"
|
|
67
|
+
x-data="{}" @ds-modal-close.window="$store.dsModals.close()"
|
|
68
|
+
@age-verified.window="$store.dsModals.open('entry-tokens', { step: 'picker' })">
|
|
54
69
|
<div class="space-y-1">
|
|
55
70
|
<h2 class="text-2xl font-bold text-heading">Modals</h2>
|
|
56
71
|
<p class="text-muted text-sm">
|
|
@@ -235,6 +250,11 @@
|
|
|
235
250
|
defines the factory inline; no importmap change). %>
|
|
236
251
|
<%= render "studio/cropper_assets" %>
|
|
237
252
|
|
|
253
|
+
<%# ageVerifyModal factory — rendered here at page level (never inside the
|
|
254
|
+
overlay template, where a cloned script would not run) so the age-gate
|
|
255
|
+
specimen opens live. A consumer app renders this in its layout the same way. %>
|
|
256
|
+
<%= render "studio/age_verify_assets" %>
|
|
257
|
+
|
|
238
258
|
<%# ---- The overlay: one backdrop + card; every specimen's real content is a
|
|
239
259
|
single-root <template x-if> gated on the current modal id. Faithful to the
|
|
240
260
|
shared host — backdrop fade, scroll lock, escape + click-self dismissal
|
|
@@ -255,6 +275,14 @@
|
|
|
255
275
|
<div><%= render "style/modals/auth" %></div>
|
|
256
276
|
</template>
|
|
257
277
|
|
|
278
|
+
<%# --- Eligibility + entry --- %>
|
|
279
|
+
<template x-if="$store.dsModals.current().id === 'age-verify'">
|
|
280
|
+
<div><%= render "style/modals/age_verify" %></div>
|
|
281
|
+
</template>
|
|
282
|
+
<template x-if="$store.dsModals.current().id === 'entry-tokens'">
|
|
283
|
+
<div><%= render "style/modals/entry_tokens" %></div>
|
|
284
|
+
</template>
|
|
285
|
+
|
|
258
286
|
<%# --- Profile (real engine partials, opened on dsModals) --- %>
|
|
259
287
|
<template x-if="$store.dsModals.current().id === 'crop-photo'">
|
|
260
288
|
<div><%= render "studio/modals/crop_photo", store: "dsModals" %></div>
|
|
@@ -273,6 +301,15 @@
|
|
|
273
301
|
<template x-if="$store.dsModals.current().id === 'wallet-deposit'">
|
|
274
302
|
<div><%= render "style/modals/wallet_deposit" %></div>
|
|
275
303
|
</template>
|
|
304
|
+
<%# entry-confirmed — the engine celebration composed via _success_card's
|
|
305
|
+
yield. One registration serves the generic (web3) + seeds (leveling)
|
|
306
|
+
specimens; the seeds bar self-gates on Studio.feature?(:leveling). %>
|
|
307
|
+
<template x-if="$store.dsModals.current().id === 'entry-confirmed'">
|
|
308
|
+
<div><%= render "studio/modals/blocks/entry_confirmed",
|
|
309
|
+
modal_store: "dsModals",
|
|
310
|
+
cta_label: "Contest Lobby ⚔️",
|
|
311
|
+
free_entry_label: "Free Entry Earned 🎟️" %></div>
|
|
312
|
+
</template>
|
|
276
313
|
|
|
277
314
|
<%# --- System and status (reusable card blocks) --- %>
|
|
278
315
|
<template x-if="$store.dsModals.current().id === 'ds-processing'">
|
|
@@ -332,6 +369,12 @@
|
|
|
332
369
|
</div>
|
|
333
370
|
<% end %></div>
|
|
334
371
|
</template>
|
|
372
|
+
<%# free-entry-earned — the standalone level-up reward modal (:leveling). %>
|
|
373
|
+
<template x-if="$store.dsModals.current().id === 'free-entry-earned'">
|
|
374
|
+
<div><%= render "studio/modals/blocks/free_entry_earned",
|
|
375
|
+
modal_store: "dsModals",
|
|
376
|
+
subtitle: "You leveled up! A Free Entry lands in your account soon. Keep leveling for more." %></div>
|
|
377
|
+
</template>
|
|
335
378
|
|
|
336
379
|
</div>
|
|
337
380
|
</div>
|
|
@@ -415,6 +458,68 @@
|
|
|
415
458
|
</div>
|
|
416
459
|
</section>
|
|
417
460
|
|
|
461
|
+
<%# ===================================================================== %>
|
|
462
|
+
<%# 1b. ELIGIBILITY + ENTRY %>
|
|
463
|
+
<%# ===================================================================== %>
|
|
464
|
+
<section class="space-y-5">
|
|
465
|
+
<div class="space-y-1">
|
|
466
|
+
<div class="flex flex-wrap items-center gap-3">
|
|
467
|
+
<h3 class="text-xl font-bold text-heading">Eligibility & entry</h3>
|
|
468
|
+
<% if age_gate_on %>
|
|
469
|
+
<span class="badge" style="color: var(--color-success); border-color: var(--color-success)">age gate on</span>
|
|
470
|
+
<% else %>
|
|
471
|
+
<span class="badge" style="color: var(--color-warning); border-color: var(--color-warning)">age gate off</span>
|
|
472
|
+
<% end %>
|
|
473
|
+
</div>
|
|
474
|
+
<p class="text-muted text-sm">
|
|
475
|
+
The pre-entry gate and the entry-purchase flow. The <strong>age gate</strong>
|
|
476
|
+
(<code class="font-mono text-2xs">studio/modals/blocks/_age_verify</code>) is a
|
|
477
|
+
NEW engine primitive gated by
|
|
478
|
+
<code class="font-mono text-2xs">Studio.feature?(:age_gate)</code> — a DOB
|
|
479
|
+
modal whose minimum age, jurisdiction label, endpoint, and legal copy are all
|
|
480
|
+
<strong>app-supplied</strong> (the engine hardcodes no policy). It is the heavier
|
|
481
|
+
sibling of the <code class="font-mono text-2xs">shared/_age_attestation</code>
|
|
482
|
+
checkbox inside Sign in above — <strong>run one or the other</strong>. The
|
|
483
|
+
<strong>entry-tokens</strong> flow is <strong>app-specific</strong> (payments
|
|
484
|
+
product): documented here composing engine chrome, but the packs, rails, and
|
|
485
|
+
the on-chain mint stay app-owned.
|
|
486
|
+
</p>
|
|
487
|
+
</div>
|
|
488
|
+
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
489
|
+
<%# Age gate — engine primitive, gated on :age_gate (disabled-but-present). %>
|
|
490
|
+
<%= render layout: "style/modal_specimen", locals: {
|
|
491
|
+
label: "Age gate (DOB)",
|
|
492
|
+
reference: %(the "Age gate" DOB modal (studio-engine studio/modals/blocks/_age_verify) — a NEW engine primitive; the app supplies min_age / state / submit_url / fine_print and the engine hardcodes no legal policy. In this demo, Confirm & Continue advances to the Entry tokens purchase (page-level dsModals wiring on the primitive's 'age-verified' hook; the primitive's real submit posts to submit_url). Open with $store.dsModals.open('age-verify')),
|
|
493
|
+
open_expr: "$store.dsModals.open('age-verify')",
|
|
494
|
+
glow_when: ds_glow.call("age-verify"),
|
|
495
|
+
disabled: !age_gate_on, openable: true, disabled_label: "age gate off" } do %>
|
|
496
|
+
<div class="pointer-events-none w-40 rounded-lg bg-surface border border-subtle shadow p-4 space-y-2 text-center">
|
|
497
|
+
<span class="block h-2 w-24 mx-auto rounded" style="background: var(--color-text); opacity: .18"></span>
|
|
498
|
+
<div class="grid grid-cols-3 gap-1">
|
|
499
|
+
<span class="h-6 rounded border" style="border-color: var(--color-border-strong)"></span>
|
|
500
|
+
<span class="h-6 rounded border" style="border-color: var(--color-border-strong)"></span>
|
|
501
|
+
<span class="h-6 rounded border" style="border-color: var(--color-border-strong)"></span>
|
|
502
|
+
</div>
|
|
503
|
+
<span class="block h-5 w-full rounded" style="background: var(--color-cta)"></span>
|
|
504
|
+
</div>
|
|
505
|
+
<% end %>
|
|
506
|
+
|
|
507
|
+
<%# Entry tokens — app-specific flow, documented (not capability-gated). %>
|
|
508
|
+
<%= render layout: "style/modal_specimen", locals: {
|
|
509
|
+
label: "Entry tokens",
|
|
510
|
+
reference: %(the "Entry tokens" purchase flow (app-specific — documented in the engine style guide composing studio/modals/blocks chrome). The packs, pricing, rails, and on-chain mint stay app-owned. Open with $store.dsModals.open('entry-tokens', { step: 'picker' })),
|
|
511
|
+
open_expr: "$store.dsModals.open('entry-tokens', { step: 'picker' })",
|
|
512
|
+
glow_when: ds_glow.call("entry-tokens") } do %>
|
|
513
|
+
<div class="pointer-events-none w-40 rounded-lg bg-surface border border-subtle shadow p-4 space-y-2">
|
|
514
|
+
<span class="block h-2 w-20 mx-auto rounded" style="background: var(--color-text); opacity: .18"></span>
|
|
515
|
+
<span class="block h-6 w-full rounded border" style="border-color: var(--color-border-strong)"></span>
|
|
516
|
+
<span class="block h-6 w-full rounded border" style="border-color: var(--color-primary)"></span>
|
|
517
|
+
<span class="block h-6 w-full rounded border" style="border-color: var(--color-border-strong)"></span>
|
|
518
|
+
</div>
|
|
519
|
+
<% end %>
|
|
520
|
+
</div>
|
|
521
|
+
</section>
|
|
522
|
+
|
|
418
523
|
<%# ===================================================================== %>
|
|
419
524
|
<%# 2. PROFILE — crop / upload, live via cropper_assets %>
|
|
420
525
|
<%# ===================================================================== %>
|
|
@@ -494,6 +599,10 @@
|
|
|
494
599
|
<code class="font-mono text-2xs">Studio.feature?(:web3)</code>. Web3 is
|
|
495
600
|
<strong><%= web3_on ? "on" : "off" %></strong> here, so these render
|
|
496
601
|
<%= web3_on ? "live." : "greyed and badged — but STILL openable as a preview." %>
|
|
602
|
+
The Connect-wallet picker ships its <strong>Phantom / Solflare / Backpack
|
|
603
|
+
brand marks inline from the engine</strong>
|
|
604
|
+
(<code class="font-mono text-2xs">studio/modals/blocks/_wallet_brand_sprite</code>),
|
|
605
|
+
so every app inherits them with no per-app icon files.
|
|
497
606
|
</p>
|
|
498
607
|
</div>
|
|
499
608
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
@@ -555,6 +664,25 @@
|
|
|
555
664
|
<span class="block h-5 w-full rounded border" style="border-color: var(--color-border-strong)"></span>
|
|
556
665
|
</div>
|
|
557
666
|
<% end %>
|
|
667
|
+
|
|
668
|
+
<%# Entry confirmed — the GENERIC web3 celebration (tx link + heading + drain
|
|
669
|
+
CTA). seedsEarned: 0 so no seeds bar even where leveling is on: this card
|
|
670
|
+
demos the clean success a web3-only app gets. The seeds variant lives in
|
|
671
|
+
Rewards below (gated by :leveling). %>
|
|
672
|
+
<%= render layout: "style/modal_specimen", locals: {
|
|
673
|
+
label: "Entry confirmed",
|
|
674
|
+
reference: %(the Web3 "Entry confirmed" celebration (studio/modals/blocks/_entry_confirmed, composed via _success_card's yield — branded tx link + heading + drain CTA; the seeds bar is gated off by :leveling and stays hidden here) — open with $store.dsModals.open('entry-confirmed', { txSignature: '…', lobbyUrl: null, seedsEarned: 0 })),
|
|
675
|
+
open_expr: "$store.dsModals.open('entry-confirmed', { txSignature: '5xTrDemoSignature1234567890abcXYZ', lobbyUrl: null, seedsEarned: 0, seedsTotal: 40 })",
|
|
676
|
+
disabled: !web3_on, openable: true } do %>
|
|
677
|
+
<div class="pointer-events-none w-40 rounded-lg bg-surface border border-subtle shadow p-4 text-center space-y-2">
|
|
678
|
+
<div class="mx-auto w-7 h-7 rounded-full flex items-center justify-center" style="background: color-mix(in srgb, var(--color-primary) 15%, transparent)">
|
|
679
|
+
<svg class="w-4 h-4" style="color: var(--color-primary)" fill="none" stroke="currentColor" stroke-width="3" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
|
|
680
|
+
</div>
|
|
681
|
+
<span class="block h-2 w-20 mx-auto rounded" style="background: var(--color-text); opacity: .18"></span>
|
|
682
|
+
<span class="block h-1.5 w-14 mx-auto rounded" style="background: var(--color-text); opacity: .12"></span>
|
|
683
|
+
<span class="block h-4 w-full rounded" style="background: var(--color-cta)"></span>
|
|
684
|
+
</div>
|
|
685
|
+
<% end %>
|
|
558
686
|
</div>
|
|
559
687
|
</section>
|
|
560
688
|
|
|
@@ -668,9 +796,16 @@
|
|
|
668
796
|
<% end %>
|
|
669
797
|
</div>
|
|
670
798
|
<p class="text-muted text-sm">
|
|
671
|
-
The level-up
|
|
672
|
-
|
|
799
|
+
The seeds bar + level-up celebrations, gated by
|
|
800
|
+
<code class="font-mono text-2xs">Studio.feature?(:leveling)</code> — an
|
|
801
|
+
app-level setting <strong>independent of :web3</strong> (some apps have
|
|
802
|
+
Solana but no leveling). Leveling is <strong><%= leveling_on ? "on" : "off" %></strong>
|
|
803
|
+
here, so these render
|
|
673
804
|
<%= leveling_on ? "live." : "greyed and badged — but STILL openable as a preview." %>
|
|
805
|
+
The seed counts, level, and free entry are <strong>app-supplied props</strong>
|
|
806
|
+
(<code class="font-mono text-2xs">seedsEarned</code>,
|
|
807
|
+
<code class="font-mono text-2xs">seedsTotal</code>, …) — the engine renders
|
|
808
|
+
the bar, the app feeds the numbers.
|
|
674
809
|
</p>
|
|
675
810
|
</div>
|
|
676
811
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
@@ -685,6 +820,43 @@
|
|
|
685
820
|
<span class="block h-4 w-full rounded" style="background: var(--color-cta)"></span>
|
|
686
821
|
</div>
|
|
687
822
|
<% end %>
|
|
823
|
+
|
|
824
|
+
<%# Entry + seeds — the SAME _entry_confirmed card, now with seedsEarned
|
|
825
|
+
crossing a level so the :leveling seeds bar mounts in the _success_card
|
|
826
|
+
yield and plays the 4-phase level-up (fill → pop → drain → refill), then
|
|
827
|
+
swaps to the Free Entry Earned reward line. %>
|
|
828
|
+
<%= render layout: "style/modal_specimen", locals: {
|
|
829
|
+
label: "Entry + seeds level-up",
|
|
830
|
+
reference: %(the Rewards "Entry + seeds" celebration (studio/modals/blocks/_entry_confirmed — the :leveling seeds bar + Free Entry Earned reward injected into _success_card's yield) — open with $store.dsModals.open('entry-confirmed', { txSignature: '…', lobbyUrl: null, seedsEarned: 30, seedsTotal: 100 })),
|
|
831
|
+
open_expr: "$store.dsModals.open('entry-confirmed', { txSignature: '5xTrDemoSignature1234567890abcXYZ', lobbyUrl: null, seedsEarned: 30, seedsTotal: 100 })",
|
|
832
|
+
disabled: !leveling_on, openable: true } do %>
|
|
833
|
+
<div class="pointer-events-none w-40 rounded-lg bg-surface border border-subtle shadow p-4 text-center space-y-2">
|
|
834
|
+
<div class="mx-auto w-7 h-7 rounded-full flex items-center justify-center" style="background: color-mix(in srgb, var(--color-primary) 15%, transparent)">
|
|
835
|
+
<svg class="w-4 h-4" style="color: var(--color-primary)" fill="none" stroke="currentColor" stroke-width="3" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
|
|
836
|
+
</div>
|
|
837
|
+
<div class="flex gap-1">
|
|
838
|
+
<span class="flex-1 h-2.5 rounded" style="background: var(--color-cta)"></span>
|
|
839
|
+
<span class="flex-1 h-2.5 rounded" style="background: var(--color-cta); opacity:.6"></span>
|
|
840
|
+
<span class="flex-1 h-2.5 rounded" style="background: var(--color-cta); opacity:.25"></span>
|
|
841
|
+
<span class="flex-1 h-2.5 rounded" style="background: var(--color-text); opacity:.12"></span>
|
|
842
|
+
<span class="flex-1 h-2.5 rounded" style="background: var(--color-text); opacity:.12"></span>
|
|
843
|
+
</div>
|
|
844
|
+
<span class="block h-4 w-full rounded" style="background: var(--color-cta)"></span>
|
|
845
|
+
</div>
|
|
846
|
+
<% end %>
|
|
847
|
+
|
|
848
|
+
<%# Free Entry Earned — the standalone level-up reward modal (:leveling). %>
|
|
849
|
+
<%= render layout: "style/modal_specimen", locals: {
|
|
850
|
+
label: "Free Entry Earned",
|
|
851
|
+
reference: %(the Rewards "Free Entry Earned" level-up modal (studio/modals/blocks/_free_entry_earned) — open with $store.dsModals.open('free-entry-earned', { level: 3 })),
|
|
852
|
+
open_expr: "$store.dsModals.open('free-entry-earned', { level: 3 })",
|
|
853
|
+
disabled: !leveling_on, openable: true } do %>
|
|
854
|
+
<div class="pointer-events-none w-40 rounded-lg bg-surface border border-subtle shadow p-4 text-center space-y-2">
|
|
855
|
+
<div class="text-2xl leading-none">🎉</div>
|
|
856
|
+
<span class="block h-2 w-20 mx-auto rounded" style="background: var(--color-text); opacity: .18"></span>
|
|
857
|
+
<span class="block h-1.5 w-24 mx-auto rounded" style="background: var(--color-text); opacity: .12"></span>
|
|
858
|
+
</div>
|
|
859
|
+
<% end %>
|
|
688
860
|
</div>
|
|
689
861
|
</section>
|
|
690
862
|
</section>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Living style guide specimen for the engine age-gate DOB modal
|
|
3
|
+
(studio/modals/blocks/_age_verify). Opened on the page-scoped dsModals host.
|
|
4
|
+
|
|
5
|
+
The whole point of this specimen is the SEAM: every legal value here is a DEMO
|
|
6
|
+
the app feeds — the engine primitive hardcodes none of it. So we pass an
|
|
7
|
+
illustrative policy (21+ in CA) and a demo fine_print that SAYS so, and run in
|
|
8
|
+
demo mode (no backend — submit resolves locally). A real app swaps these for
|
|
9
|
+
its own min_age / state / submit_url / legal copy resolved server-side.
|
|
10
|
+
%>
|
|
11
|
+
<%= render "studio/modals/blocks/age_verify",
|
|
12
|
+
min_age: 21,
|
|
13
|
+
state: "CA",
|
|
14
|
+
submit_url: "#demo-age-verify",
|
|
15
|
+
modal_store: "dsModals",
|
|
16
|
+
demo: true,
|
|
17
|
+
fine_print: "Demo policy the app supplies: 21+ in CA. The engine hardcodes no age and no state table — your app passes min_age, the jurisdiction label, this copy, and the endpoint that recomputes it server-side." %>
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Living style guide specimen — the entry-token purchase flow. This documents an
|
|
3
|
+
APP-SPECIFIC flow (Turf Monster's "Get Entry Tokens"); the engine deliberately
|
|
4
|
+
does NOT own the packs, pricing, or payment rails. The specimen composes only
|
|
5
|
+
engine CHROME (studio/modals/blocks/shell, card_header, progress_pill) and
|
|
6
|
+
represents the packs as an illustrative demo — the app owns the payment logic
|
|
7
|
+
and the on-chain mint.
|
|
8
|
+
|
|
9
|
+
Opened on the page-scoped dsModals host with an internal step machine on
|
|
10
|
+
props.step: picker -> confirming -> minted. Single-root templates throughout.
|
|
11
|
+
|
|
12
|
+
On-chain model (documented, not owned here): an entry token is a PROGRAM-OWNED
|
|
13
|
+
prepaid entry credit recorded on-chain (a per-token account), NOT a fungible
|
|
14
|
+
SPL token held in the user's wallet. Two distinct on-chain moments: (1) MINT at
|
|
15
|
+
purchase, server-signed after the payment clears — not on the buyer's click;
|
|
16
|
+
(2) CONSUME at entry, when the app's hold-to-confirm gesture spends one token to
|
|
17
|
+
create the contest entry.
|
|
18
|
+
%>
|
|
19
|
+
<%
|
|
20
|
+
# Illustrative packs only — a real app reads these from its own config
|
|
21
|
+
# (e.g. StripePurchase.available_packs), and they vary by funding mode.
|
|
22
|
+
demo_packs = [
|
|
23
|
+
{ tokens: 1, price: "$5", note: nil },
|
|
24
|
+
{ tokens: 3, price: "$12", note: "Popular" },
|
|
25
|
+
{ tokens: 10, price: "$35", note: "Best value" }
|
|
26
|
+
]
|
|
27
|
+
%>
|
|
28
|
+
<div x-data="{
|
|
29
|
+
get props() { var c = Alpine.store('dsModals').current(); return (c && c.props) || {}; },
|
|
30
|
+
buy(count, balance) {
|
|
31
|
+
Alpine.store('dsModals').advance({ step: 'confirming', mintedCount: count, mintedBalance: balance });
|
|
32
|
+
setTimeout(function () {
|
|
33
|
+
var c = Alpine.store('dsModals').current();
|
|
34
|
+
if (c && c.id === 'entry-tokens') Alpine.store('dsModals').advance({ step: 'minted' });
|
|
35
|
+
}, 1300);
|
|
36
|
+
}
|
|
37
|
+
}" class="relative">
|
|
38
|
+
|
|
39
|
+
<%# Shared close — top-right, above whichever step is active. %>
|
|
40
|
+
<button @click="$store.dsModals.close()"
|
|
41
|
+
class="absolute top-0 right-0 -mt-2 -mr-2 text-secondary hover:text-heading text-xl leading-none z-10"
|
|
42
|
+
aria-label="Close">×</button>
|
|
43
|
+
|
|
44
|
+
<%# ===== PICKER ===== %>
|
|
45
|
+
<template x-if="(props.step || 'picker') === 'picker'">
|
|
46
|
+
<div>
|
|
47
|
+
<div class="text-center pt-1 mb-3">
|
|
48
|
+
<h3 class="text-heading font-bold text-lg leading-tight">Get Entry Tokens</h3>
|
|
49
|
+
</div>
|
|
50
|
+
<%= render "studio/modals/blocks/progress_pill", current: 2, total: 3 %>
|
|
51
|
+
<p class="text-xs text-secondary mb-4 text-center">
|
|
52
|
+
1 token = 1 contest entry. Pick a pack to finish.
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
<div class="space-y-2.5">
|
|
56
|
+
<% demo_packs.each do |pack| %>
|
|
57
|
+
<button type="button"
|
|
58
|
+
@click="buy(<%= pack[:tokens] %>, '<%= pack[:tokens] %>')"
|
|
59
|
+
class="w-full flex items-center justify-between gap-3 rounded-lg border border-strong bg-inset px-4 py-3 text-left hover:border-primary transition">
|
|
60
|
+
<span class="flex items-center gap-2">
|
|
61
|
+
<span class="text-heading font-semibold"><%= pack[:tokens] %> token<%= "s" unless pack[:tokens] == 1 %></span>
|
|
62
|
+
<% if pack[:note] %>
|
|
63
|
+
<span class="badge" style="color: var(--color-primary); border-color: var(--color-primary)"><%= pack[:note] %></span>
|
|
64
|
+
<% end %>
|
|
65
|
+
</span>
|
|
66
|
+
<span class="text-heading font-mono font-bold"><%= pack[:price] %></span>
|
|
67
|
+
</button>
|
|
68
|
+
<% end %>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<%# Rails — token-funded only. The picker documents which rails mint tokens.
|
|
72
|
+
The USDC-direct path is intentionally NOT a pack here (it's a separate
|
|
73
|
+
rail that transfers USDC and mints no token). %>
|
|
74
|
+
<div class="mt-4 flex flex-wrap items-center justify-center gap-x-4 gap-y-1 text-xs text-muted">
|
|
75
|
+
<span>Pay by card</span>
|
|
76
|
+
<span aria-hidden="true">·</span>
|
|
77
|
+
<span>PayPal / Venmo</span>
|
|
78
|
+
</div>
|
|
79
|
+
<p class="text-center text-2xs text-muted mt-1">USDC-direct entry is a separate rail — it funds an entry with no token.</p>
|
|
80
|
+
|
|
81
|
+
<%# The seam callout — what the engine owns vs what the app owns. %>
|
|
82
|
+
<div class="mt-4 rounded-lg border border-subtle bg-surface p-3 text-2xs text-secondary leading-snug">
|
|
83
|
+
<p class="text-heading font-semibold mb-1">App-specific flow, engine chrome</p>
|
|
84
|
+
<p class="mb-1">
|
|
85
|
+
The engine supplies only the shell, header, and progress pill. The packs,
|
|
86
|
+
pricing, payment rails, and the on-chain mint are <strong>app-owned</strong> —
|
|
87
|
+
this specimen represents them as a demo.
|
|
88
|
+
</p>
|
|
89
|
+
<p>
|
|
90
|
+
On-chain, an entry token is a <strong>prepaid entry credit recorded on-chain</strong>
|
|
91
|
+
(not a transferable token held in your wallet): minted server-side once payment
|
|
92
|
+
clears, then <strong>consumed</strong> when the app's hold-to-confirm creates the entry.
|
|
93
|
+
</p>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</template>
|
|
97
|
+
|
|
98
|
+
<%# ===== CONFIRMING (mint-at-purchase) ===== %>
|
|
99
|
+
<template x-if="props.step === 'confirming'">
|
|
100
|
+
<div x-init="setTimeout(function () { var c = Alpine.store('dsModals').current(); if (c && c.id === 'entry-tokens') Alpine.store('dsModals').advance({ step: 'minted' }); }, 1300)">
|
|
101
|
+
<%= render "studio/modals/blocks/card_header",
|
|
102
|
+
spinner: true,
|
|
103
|
+
title: "Confirming your purchase…",
|
|
104
|
+
subtitle: "Payment cleared — a server job mints one prepaid entry credit per token on-chain." %>
|
|
105
|
+
</div>
|
|
106
|
+
</template>
|
|
107
|
+
|
|
108
|
+
<%# ===== MINTED (ready to consume at entry) ===== %>
|
|
109
|
+
<template x-if="props.step === 'minted'">
|
|
110
|
+
<div>
|
|
111
|
+
<%= render "studio/modals/blocks/card_header",
|
|
112
|
+
icon_color: 'primary',
|
|
113
|
+
title_key: "(props.mintedCount === 1 ? 'Entry Token Minted' : 'Entry Tokens Minted')" do %>
|
|
114
|
+
You now have <span class="font-bold text-primary font-mono" x-text="props.mintedBalance"></span> available.
|
|
115
|
+
<% end %>
|
|
116
|
+
<p class="text-xs text-secondary text-center mb-4 leading-snug">
|
|
117
|
+
In the app, holding to confirm now <strong>consumes one token on-chain</strong> to
|
|
118
|
+
create your contest entry, then resolves to the shared Entry Confirmed card.
|
|
119
|
+
</p>
|
|
120
|
+
<button @click="$store.dsModals.close()" class="btn btn-primary btn-lg w-full">Done</button>
|
|
121
|
+
</div>
|
|
122
|
+
</template>
|
|
123
|
+
</div>
|