studio-engine 0.74.7 → 0.74.8

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.
@@ -1,71 +0,0 @@
1
- <%#
2
- Living style guide specimen — the Add Funds hub, every rail at once.
3
-
4
- APP-SPECIFIC flow, engine chrome throughout (blocks/_close_x + six
5
- blocks/_rail_row). This is the card the rail primitive was extracted FOR: turf
6
- draws these six by hand today, which is six chances for the seventh to drift.
7
-
8
- WHAT TO LOOK AT IS THE RANKING, and this is where it earns its keep. Six rails
9
- is enough that "which one do I press" becomes a real question, and a hub where
10
- every rail carries equal weight answers it by asking the person to compare
11
- payment processors — which is not a question anyone can answer about their own
12
- life. Exactly one rail is :primary. The other five defer to it.
13
-
14
- AND THE THIRD SHAPE. PayPal and Venmo are announced before they are wired, so
15
- they are :soon — not disabled buttons but plain <div>s, with a badge where the
16
- chevron would be. A dimmed chevron would still promise somewhere to go.
17
-
18
- Single root: the outer <div> is the host's required root.
19
- %>
20
- <div class="relative">
21
- <%= render "studio/modals/blocks/close_x", modal_store: "dsModals" %>
22
-
23
- <div class="text-center pt-1 mb-3">
24
- <h3 class="text-heading font-bold text-lg leading-tight">Add Funds</h3>
25
- </div>
26
-
27
- <div class="space-y-3">
28
- <%= render "studio/modals/blocks/rail_row",
29
- emphasis: :primary, on_click: "$store.dsModals.close()",
30
- icon_label: "C", icon_bg: "#0052FF",
31
- title: "Coinbase", subtitle: "Buy USDC with a card or bank",
32
- data: { ds_rail: "coinbase" } %>
33
-
34
- <%= render "studio/modals/blocks/rail_row",
35
- on_click: "$store.dsModals.close()",
36
- icon_label: "CF", icon_bg: "#14B8A6",
37
- title: "Coinflow · $19", subtitle: "Buy 1 entry token with a card",
38
- data: { ds_rail: "coinflow" } %>
39
-
40
- <%= render "studio/modals/blocks/rail_row",
41
- on_click: "$store.dsModals.close()",
42
- icon_label: "A", icon_bg: "#6D28D9",
43
- title: "Aeropay · $19", subtitle: "Buy 1 entry token from your bank",
44
- data: { ds_rail: "aeropay" } %>
45
-
46
- <%= render "studio/modals/blocks/rail_row",
47
- state: :soon,
48
- icon_label: "P", icon_bg: "#003087",
49
- title: "PayPal", subtitle: "Pay with your PayPal balance",
50
- data: { ds_rail: "paypal" } %>
51
-
52
- <%= render "studio/modals/blocks/rail_row",
53
- state: :soon,
54
- icon_label: "V", icon_bg: "#008CFF",
55
- title: "Venmo", subtitle: "Pay from your Venmo balance",
56
- data: { ds_rail: "venmo" } %>
57
-
58
- <%= render "studio/modals/blocks/rail_row",
59
- on_click: "$store.dsModals.close()",
60
- icon_label: "S", icon_bg: "#635BFF",
61
- title: "Card · $19", subtitle: "Buy one entry token with a card",
62
- data: { ds_rail: "stripe" } %>
63
- </div>
64
-
65
- <%# Back, not close: the hub is always reached FROM somewhere, and landing a
66
- player on nothing is not the same as returning them. %>
67
- <button type="button" @click="$store.dsModals.swap('ds-wallet-topup', {})"
68
- class="block mx-auto mt-4 text-sm text-secondary hover:text-heading transition">
69
- &larr; Back
70
- </button>
71
- </div>
@@ -1,69 +0,0 @@
1
- <%#
2
- Living style guide specimen — Top Up Wallet, the FIRST funds modal a player
3
- meets when they try to enter without funds.
4
-
5
- APP-SPECIFIC flow, engine chrome throughout: blocks/_close_x for the mark and
6
- blocks/_rail_row for every option. The engine owns no processors and no funding
7
- policy; what turf supplies is which rails exist and which one leads.
8
-
9
- THE TOGGLE IS THE CARD. This is one modal with two faces, and the fork is a
10
- SAFETY one rather than a preference. A web2 viewer running with
11
- ENABLE_WEB2_USDC_ENTRY off cannot pay an entry with USDC at all — so pitching
12
- "Buy USDC" to them would sell a thing that cannot do the job they came to do.
13
- For exactly that audience the primary rail becomes the entry token instead.
14
- Everyone else — web3, and web2 with the flag on — sees Coinbase.
15
-
16
- Shown as ONE card with a toggle rather than two cards, because two cards would
17
- read as two products. The whole point is that a player never sees both.
18
-
19
- Single root: the outer <div> is the host's required root.
20
- %>
21
- <div class="relative"
22
- x-data="{
23
- get props() { var c = $store.dsModals.current(); return (c && c.props) || {} },
24
- get tokenFallback() { return !!this.props.tokenFallback }
25
- }">
26
- <%= render "studio/modals/blocks/close_x", modal_store: "dsModals" %>
27
-
28
- <div class="text-center pt-1 mb-2">
29
- <h3 class="text-heading font-bold text-lg leading-tight">Top Up Wallet</h3>
30
- </div>
31
-
32
- <%# The balance line follows the fork too — the currency it names has to be the
33
- one that can actually pay for the entry. %>
34
- <p x-show="!tokenFallback" class="text-xs text-body mb-4 text-center">
35
- USDC balance: <span class="font-mono font-semibold text-heading">$12.40</span>
36
- </p>
37
- <p x-show="tokenFallback" x-cloak class="text-xs text-body mb-4 text-center">
38
- Entry tokens: <span class="font-mono font-semibold text-heading">0</span>
39
- </p>
40
-
41
- <template x-if="!tokenFallback">
42
- <%= render "studio/modals/blocks/rail_row",
43
- emphasis: :primary,
44
- on_click: "$store.dsModals.close()",
45
- icon_label: "C",
46
- icon_bg: "#0052FF",
47
- title: "Buy USDC with Coinbase",
48
- subtitle: "Card or bank · funds your entry",
49
- data: { ds_rail: "coinbase" } %>
50
- </template>
51
-
52
- <template x-if="tokenFallback">
53
- <%= render "studio/modals/blocks/rail_row",
54
- emphasis: :primary,
55
- on_click: "$store.dsModals.close()",
56
- icon_label: "🎟️",
57
- icon_class: "text-xl",
58
- title: "Buy Entry Tokens",
59
- subtitle: "1 token = 1 contest entry",
60
- data: { ds_rail: "tokens" } %>
61
- </template>
62
-
63
- <%# The quiet way to the full hub. Deliberately a link, not a rail: it is not
64
- another way to pay, it is a way to see the others. %>
65
- <button type="button" @click="$store.dsModals.swap('ds-onramp-hub', {})"
66
- class="block mx-auto mt-4 text-sm text-secondary hover:text-heading transition">
67
- More ways to add funds &rarr;
68
- </button>
69
- </div>
@@ -1,188 +0,0 @@
1
- <%#
2
- Living style guide specimen — the contest entry & eligibility flow. This
3
- documents an APP-SPECIFIC flow (Turf Monster's "Get Entry Tokens" → enter);
4
- the engine deliberately does NOT own the packs, pricing, payment rails, or any
5
- on-chain write. The specimen composes only engine CHROME (studio/modals/blocks/
6
- shell, card_header, progress_pill, solana_tx_link) and represents the packs and
7
- the on-chain moments as an illustrative demo — the app owns the money + signing.
8
-
9
- Opened on the page-scoped dsModals host with an internal step machine on
10
- props.step, the full web2 walk:
11
-
12
- picker -> confirming -> minted -> entering -> entered
13
-
14
- Single-root templates throughout. The two load steps (confirming, entering)
15
- honor the minimum-visible-duration convention (studio/modals/_load_convention):
16
- the spinner holds at least MIN_LOAD_MS, then auto-advances (demo mode) —
17
- resolveAt = max(min_duration, actual_completion).
18
-
19
- On-chain model (documented, not owned here). An entry token is a PROGRAM-OWNED
20
- prepaid entry credit recorded on-chain (a per-token account), NOT a fungible SPL
21
- token in the user's wallet. WEB2 (card / Stripe / Coinflow / PayPal) has two
22
- on-chain moments: (1) MINT at purchase, server-signed after the payment clears —
23
- not on the buyer's click; (2) CONSUME at entry, when the hold-to-confirm gesture
24
- spends one token to create the contest entry. WEB3 (wallet / USDC on-chain)
25
- funds USDC directly and mints NO token — so the mint→consume prelude is
26
- web2-only. Both paths converge at the SAME on-chain Entry PDA and the SAME
27
- "Entry Confirmed" card.
28
- %>
29
- <%
30
- # Illustrative packs only — a real app reads these from its own config
31
- # (e.g. StripePurchase.available_packs), and they vary by funding mode.
32
- demo_packs = [
33
- { tokens: 1, price: "$5", note: nil },
34
- { tokens: 3, price: "$12", note: "Popular" },
35
- { tokens: 10, price: "$35", note: "Best value" }
36
- ]
37
- %>
38
- <div x-data="{
39
- get props() { var c = Alpine.store('dsModals').current(); return (c && c.props) || {}; },
40
- buy(count, balance) {
41
- Alpine.store('dsModals').advance({ step: 'confirming', mintedCount: count, mintedBalance: balance });
42
- },
43
- confirmEntry() {
44
- Alpine.store('dsModals').advance({ step: 'entering' });
45
- },
46
- // Demo auto-resolve honoring the min-load convention: hold the spinner at
47
- // least MIN_LOAD_MS, then advance — but only while this modal is still on
48
- // the given step (guards against an early close / manual advance).
49
- autoAdvance(fromStep, patch) {
50
- window.StudioModals.holdAtLeast(window.StudioModals.MIN_LOAD_MS).then(function () {
51
- var c = Alpine.store('dsModals').current();
52
- if (c && c.id === 'entry-tokens' && c.props.step === fromStep) {
53
- Alpine.store('dsModals').advance(patch);
54
- }
55
- });
56
- }
57
- }" class="relative">
58
-
59
- <%# Shared close — top-right, above whichever step is active. %>
60
- <button @click="$store.dsModals.close()"
61
- class="absolute top-0 right-0 -mt-2 -mr-2 text-secondary hover:text-heading text-xl leading-none z-10"
62
- aria-label="Close">&times;</button>
63
-
64
- <%# ===== PICKER ===== %>
65
- <template x-if="(props.step || 'picker') === 'picker'">
66
- <div>
67
- <div class="text-center pt-1 mb-3">
68
- <h3 class="text-heading font-bold text-lg leading-tight">Get Entry Tokens</h3>
69
- </div>
70
- <%= render "studio/modals/blocks/progress_pill", current: 2, total: 3 %>
71
- <p class="text-xs text-secondary mb-4 text-center">
72
- 1 token = 1 contest entry. Pick a pack to finish.
73
- </p>
74
-
75
- <div class="space-y-2.5">
76
- <% demo_packs.each do |pack| %>
77
- <button type="button"
78
- @click="buy(<%= pack[:tokens] %>, '<%= pack[:tokens] %>')"
79
- 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">
80
- <span class="flex items-center gap-2">
81
- <span class="text-heading font-semibold"><%= pack[:tokens] %> token<%= "s" unless pack[:tokens] == 1 %></span>
82
- <% if pack[:note] %>
83
- <span class="badge" style="color: var(--color-primary); border-color: var(--color-primary)"><%= pack[:note] %></span>
84
- <% end %>
85
- </span>
86
- <span class="text-heading font-mono font-bold"><%= pack[:price] %></span>
87
- </button>
88
- <% end %>
89
- </div>
90
-
91
- <%# Rails — which funding path mints tokens vs funds an entry directly. %>
92
- <div class="mt-4 flex flex-wrap items-center justify-center gap-x-4 gap-y-1 text-xs text-muted">
93
- <span>Pay by card</span>
94
- <span aria-hidden="true">&middot;</span>
95
- <span>PayPal / Venmo</span>
96
- <span aria-hidden="true">&middot;</span>
97
- <span>Coinflow</span>
98
- </div>
99
- <p class="text-center text-2xs text-muted mt-1">
100
- These are the <strong>web2</strong> rails — they mint prepaid entry tokens.
101
- The <strong>web3</strong> path funds USDC on-chain directly and mints no token.
102
- </p>
103
-
104
- <%# The seam callout — what the engine owns vs the app, plus the honest
105
- web2/web3 map (a real divergence, not invented). %>
106
- <div class="mt-4 rounded-lg border border-subtle bg-surface p-3 text-2xs text-secondary leading-snug">
107
- <p class="text-heading font-semibold mb-1">App-specific flow, engine chrome</p>
108
- <p class="mb-2">
109
- The engine supplies only the shell, header, progress pill, and tx link. The
110
- packs, pricing, payment rails, and every on-chain write are
111
- <strong>app-owned</strong> — this specimen represents them as a demo. On-chain,
112
- an entry token is a <strong>prepaid entry credit recorded on-chain</strong>
113
- (a per-token account), not a wallet-held token.
114
- </p>
115
- <p class="text-heading font-semibold mb-1">Web2 vs web3 — where they diverge and converge</p>
116
- <p class="mb-1">
117
- <strong>Web2</strong> (card / Stripe / Coinflow / PayPal): a server job mints
118
- one prepaid entry credit per token on-chain once payment clears, then
119
- hold-to-confirm <strong>consumes</strong> one token to create the entry.
120
- </p>
121
- <p class="mb-1">
122
- <strong>Web3</strong> (wallet / USDC on-chain): funds USDC directly and
123
- <strong>mints no token</strong> — the mint → consume prelude is web2-only.
124
- </p>
125
- <p>
126
- Both paths converge on the <strong>same on-chain Entry PDA</strong> and the
127
- <strong>same “Entry Confirmed” card</strong> — divergent funding front-end,
128
- one shared entry → entered finish.
129
- </p>
130
- </div>
131
- </div>
132
- </template>
133
-
134
- <%# ===== CONFIRMING (payment processing — mint-at-purchase) ===== %>
135
- <template x-if="props.step === 'confirming'">
136
- <div x-init="autoAdvance('confirming', { step: 'minted' })">
137
- <%= render "studio/modals/blocks/card_header",
138
- spinner: true,
139
- title: "Confirming your purchase…",
140
- subtitle: "Payment cleared — a server job mints one prepaid entry credit per token on-chain." %>
141
- </div>
142
- </template>
143
-
144
- <%# ===== MINTED (ready to consume at entry) ===== %>
145
- <template x-if="props.step === 'minted'">
146
- <div>
147
- <%= render "studio/modals/blocks/card_header",
148
- icon_color: 'primary',
149
- title_key: "(props.mintedCount === 1 ? 'Entry Token Minted' : 'Entry Tokens Minted')" do %>
150
- You now have <span class="font-bold text-primary font-mono" x-text="props.mintedBalance"></span> available.
151
- <% end %>
152
- <p class="text-xs text-secondary text-center mb-4 leading-snug">
153
- Holding to confirm now <strong>consumes one token on-chain</strong> to create
154
- your contest entry.
155
- </p>
156
- <button @click="confirmEntry()" class="btn btn-primary btn-lg w-full">Hold to Confirm Entry</button>
157
- <button @click="$store.dsModals.close()"
158
- class="block mx-auto mt-3 text-xs text-secondary hover:text-heading underline underline-offset-2">
159
- I&rsquo;ll confirm later
160
- </button>
161
- </div>
162
- </template>
163
-
164
- <%# ===== ENTERING (contest enter processing — the on-chain consume) ===== %>
165
- <template x-if="props.step === 'entering'">
166
- <div x-init="autoAdvance('entering', { step: 'entered', txSignature: '5xTrDemoEntrySignature1234567890abc' })">
167
- <%= render "studio/modals/blocks/card_header",
168
- spinner: true,
169
- title: "Confirming your entry…",
170
- subtitle: "Consuming one token on-chain to create your contest entry." %>
171
- </div>
172
- </template>
173
-
174
- <%# ===== ENTERED (contest entered — the shared Entry Confirmed finish) ===== %>
175
- <template x-if="props.step === 'entered'">
176
- <div>
177
- <%= render "studio/modals/blocks/card_header",
178
- size: :lg, icon_color: 'primary',
179
- title: "Good Luck", subtitle: "Entry Confirmed" %>
180
- <%= render "studio/modals/blocks/solana_tx_link", tx_signature_key: "props.txSignature" %>
181
- <button @click="$store.dsModals.close()" class="btn btn-primary btn-lg w-full">Contest Lobby &#x2694;&#xFE0F;</button>
182
- <button @click="$store.dsModals.close()"
183
- class="block mx-auto mt-3 text-sm text-secondary hover:text-heading transition">
184
- Dismiss
185
- </button>
186
- </div>
187
- </template>
188
- </div>
@@ -1,86 +0,0 @@
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
- // Demo auto-resolve (min-load convention): a processing modal opened with
28
- // demoResolve holds its spinner at least MIN_LOAD_MS, then advances to
29
- // success — or to error when demoError is set (the Processing specimen's
30
- // success/error toggle). Guarded to the still-open, still-processing modal.
31
- var entry = Alpine.store('dsModals').current();
32
- if (!entry || entry.id !== 'onchain-tx' || !entry.props ||
33
- entry.props.state !== 'processing' || !entry.props.demoResolve) return;
34
- var minMs = entry.props.minDuration ||
35
- (window.StudioModals && window.StudioModals.MIN_LOAD_MS) || 1400;
36
- window.StudioModals.holdAtLeast(minMs).then(function () {
37
- var cur = Alpine.store('dsModals').current();
38
- if (!cur || cur.id !== 'onchain-tx' || cur.props.state !== 'processing') return;
39
- if (entry.props.demoError) {
40
- Alpine.store('dsModals').advance({ state: 'error',
41
- title: 'Could not confirm',
42
- errorMessage: 'The transaction was rejected. Give it another try.' });
43
- } else {
44
- Alpine.store('dsModals').advance({ state: 'success',
45
- txSignature: '5xTrDemoSignature1234567890abcXYZ',
46
- successTitle: 'Mint complete',
47
- successSubtitle: 'Your on-chain action confirmed.',
48
- ctaLabel: 'View wallet', ctaHref: null });
49
- }
50
- });
51
- }
52
- }">
53
-
54
- <%# === PROCESSING ====================================================== %>
55
- <template x-if="$store.dsSolanaModal.state === 'processing'">
56
- <%= render "studio/modals/blocks/card_header",
57
- spinner: true,
58
- title_key: "$store.dsSolanaModal.title",
59
- subtitle_key: "$store.dsSolanaModal.message" %>
60
- </template>
61
-
62
- <%# === SUCCESS (generic) =============================================== %>
63
- <template x-if="$store.dsSolanaModal.state === 'success'">
64
- <%= render "studio/modals/blocks/onchain_success",
65
- tx_signature_key: "$store.dsSolanaModal.txSignature",
66
- title_key: "$store.dsSolanaModal.successTitle",
67
- subtitle_key: "$store.dsSolanaModal.successSubtitle",
68
- cta_label_key: "$store.dsSolanaModal.ctaLabel",
69
- cta_href_key: "$store.dsSolanaModal.ctaHref",
70
- modal_store: "dsModals" %>
71
- </template>
72
-
73
- <%# === ERROR =========================================================== %>
74
- <template x-if="$store.dsSolanaModal.state === 'error'">
75
- <div>
76
- <%= render "studio/modals/blocks/card_header",
77
- icon: :error,
78
- title_key: "$store.dsSolanaModal.title",
79
- subtitle_key: "$store.dsSolanaModal.errorMessage" %>
80
- <button @click="$store.dsSolanaModal.close()"
81
- class="block mx-auto mt-3 text-sm text-secondary hover:text-heading transition">
82
- Close
83
- </button>
84
- </div>
85
- </template>
86
- </div>