studio-engine 0.4.8 → 0.4.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f8c3f3f0fc28618c7548ed3cc81722c97c9d0ec8aa2b2e80c8b66a659b12d31
4
- data.tar.gz: 80d1c6c2caf077f2134155386ee0e239e7c110dad60aa5309b4f73de3c4941d9
3
+ metadata.gz: 1815c9f9ea9e2b75d83b6873802726bd1a3aeee90ac556db5454c0f98adb9c26
4
+ data.tar.gz: 70ef1cfae8480f366fd135544ff20453ecb939c30e8f584878beb06c1cbb8efc
5
5
  SHA512:
6
- metadata.gz: 14e91053bde82771db8a5d2034688efd1ccf08802bc29c2b95d27a5b875fb93c281bb3cb511cf53e6f29f201a96bbc6ef932ed59160ca3c10efa7b342c26a813
7
- data.tar.gz: e2bbf87278406c6ab7e7a4953d2f409f2407eda0f03439fc514c5db00c263d8660215ce3379f4114d7b3cfdbf44ca943d35f2a5563394c422be5661ae2ab8389
6
+ metadata.gz: 8046beba0b56403dcb54557949b64f9779b581b0c82f729a96fccfe562bedb8fd168345b84fe9c2ce9be3683d743a8f9d6102b2147615f33fc81d9e4a1c95ccd
7
+ data.tar.gz: 4e3a7f33ae0b7d9b549f1c9dd9443de68511c66eee29a43a9e296c235adddf38f4c9ba1b13521f8f8aec9581e9ef098381b4cbbc68e59c0f20ab9933e50ecb1f
data/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) — `MAJOR.MINOR.PATCH`. Both consumer Rails apps pin to a tag in their `Gemfile`; bumping the tag is a release.
4
4
 
5
+ ## v0.4.9 (2026-05-23)
6
+
7
+ Modal success_card upgrades — canonical "celebration" look for any modal that needs an Entry / Action / Payment confirmed card. All options additive; existing callers unaffected.
8
+
9
+ ### Added
10
+ - **`_success_card` — `large_title:`** boolean. Bumps the headline from `text-lg` to `text-3xl` (the celebration look).
11
+ - **`_success_card` — `title_key:` / `message_key:`** Alpine-expression locals for live-driven headline + subtitle. Lets a card track a store as labels mutate without re-mounting (paired with the same option on `_processing_card` in v0.4.6).
12
+ - **`_success_card` — `tx_solana:`** boolean. Upgrades the tx-signature explorer link from the plain underlined-hash style to a boxed pill with the Solana brand mark (gradient SVG, three diagonal bars) on the left and a launch arrow on the right. Same `tx_signature_key` Alpine expression drives both variants.
13
+ - **`_success_card` — `cta_drain:`** boolean. When paired with `auto_redirect_url_key`, the countdown drains as a translucent overlay on the CTA button itself — no separate progress bar / "Redirecting in Xs…" text. The celebration look. Uses the new `@keyframes studio-modal-drain` defined in the host's style block.
14
+ - **`_success_card` — yield block.** Callers can pass an inline block; the card renders it below the CTA. Used by turf-monster's onchain-tx modal to slot in the seeds-bar + level-up celebration without forking the partial.
15
+
16
+ ### Migration
17
+ None required. Apps that don't set the new options keep the existing default look.
18
+
5
19
  ## v0.4.8 (2026-05-23)
6
20
 
7
21
  Bugfix follow-up to v0.4.7. The v0.4.7 fix removed the ERB-escape example from the doc comment, but the same comment still referenced the bug it had just fixed using literal ERB-tag characters (the words "ERB <%# %> terminates at the first %> sequence" sit inside an ERB comment that uses `%>` as its terminator — recursive footgun). The first inline `%>` ended the outer comment and the rest leaked again.
@@ -13,6 +13,14 @@
13
13
  Combined with the fixed backdrop on the host below, this keeps the
14
14
  page beneath any modal from scrolling on wheel / touch / spacebar. */
15
15
  body.modal-open { overflow: hidden; }
16
+
17
+ /* Drain-bar keyframe used by studio/modals/blocks/_success_card when
18
+ cta_drain is set — the CTA button hosts a translucent overlay that
19
+ shrinks from full to empty over the auto-redirect duration. */
20
+ @keyframes studio-modal-drain {
21
+ from { transform: scaleX(1); }
22
+ to { transform: scaleX(0); }
23
+ }
16
24
  </style>
17
25
 
18
26
  <script>
@@ -1,53 +1,75 @@
1
1
  <%#
2
- Reusable success card the green-check celebration screen used by
3
- the auth wizard's tokens-submitted step, the solanaModal success
4
- state, and any future "X confirmed!" modal. Composable building
5
- block; the host shell (backdrop, p-6, max-w-sm) is provided by
6
- shared/_modal_host so this partial only renders inside-the-card
7
- content.
8
-
9
- Locals — every one is optional EXCEPT title:
10
-
11
- title: (required) headline. Bold, prominent.
12
- message: sub-headline. One sentence.
2
+ Reusable success card. The default style is the studio-canonical
3
+ "Entry / Action confirmed" celebration screen: green check (or
4
+ custom icon), title, message, optional explorer link, primary CTA
5
+ with optional drain-bar countdown, optional confetti. Apps
6
+ compose this inside a modal content partial; the host shell
7
+ (backdrop, card chrome, transitions) is provided by the modal host.
8
+
9
+ Locals — every one is optional EXCEPT title (or title_key):
10
+
11
+ title: static headline string
12
+ title_key: Alpine expression for the headline
13
+ (e.g. "$store.solanaModal.title") rendered
14
+ via x-text. Takes priority over title.
15
+ message: static sub-headline string
16
+ message_key: Alpine expression for the message
17
+ (e.g. "$store.solanaModal.message").
18
+ large_title: boolean. Bigger text-3xl headline; the
19
+ default is text-lg.
13
20
 
14
21
  icon_emoji: renders the given emoji as the icon instead
15
- of the default green check (e.g. "📍").
16
- icon_color: Tailwind color token for the default check;
17
- one of 'primary' (default), 'success', 'warning'.
18
-
19
- tx_signature_key: Alpine expression that resolves to a Solana
20
- tx signature (e.g. "props.txSignature"). When
21
- truthy, renders the truncated explorer link.
22
- Uses the global clusterParam set by the
23
- solanaModal init in application.html.erb.
22
+ of the default green check.
23
+ icon_color: color token for the default check; one of
24
+ 'primary' (default), 'success', 'warning',
25
+ 'mint' (when an explicit Solana-ish accent
26
+ is wanted).
27
+
28
+ tx_signature_key: Alpine expression for a Solana tx signature
29
+ (e.g. "props.txSignature"). When truthy,
30
+ renders an explorer link. Default style is
31
+ a plain underlined hash; set tx_solana to
32
+ upgrade to the boxed branded variant.
33
+ tx_solana: boolean. Renders the tx_signature_key link
34
+ as a boxed pill with the Solana brand mark
35
+ on the left and a launch arrow on the
36
+ right. The "canonical" tx link.
24
37
 
25
38
  cta_label: primary CTA button text.
26
- cta_href_key: Alpine expression for the button's href
27
- (e.g. "props.redirectUrl || '/'"). Mutually
28
- exclusive with cta_event.
29
- cta_event: window event name to dispatch on click
30
- (e.g. "auth-go-now"). Mutually exclusive
31
- with cta_href_key.
39
+ cta_href_key: Alpine expression for the button's href.
40
+ Mutually exclusive with cta_event.
41
+ cta_event: window event name dispatched on click.
42
+ Mutually exclusive with cta_href_key.
43
+ cta_drain: boolean. When set together with
44
+ auto_redirect_url_key, the countdown
45
+ drains as a translucent overlay on the CTA
46
+ button itself (no separate progress bar /
47
+ "Redirecting in Xs…" line). The canonical
48
+ celebration look.
32
49
 
33
50
  secondary_label: optional secondary action label.
34
51
  secondary_event: optional secondary action dispatch event.
35
52
 
36
- auto_redirect_url_key: Alpine expression for the redirect URL. When
37
- set, an internal countdown ticks down and
38
- navigates when it hits zero. Renders the
39
- progress bar + "Redirecting in Xs…" text.
53
+ auto_redirect_url_key: Alpine expression for the redirect URL.
54
+ When set, an internal countdown ticks down
55
+ and navigates when it hits zero. Pairs
56
+ with cta_drain.
40
57
  auto_redirect_seconds: total seconds for the countdown. Default 5.
41
58
 
42
- confetti: boolean. When true, fires
43
- window.fireSuccessConfetti() ~100ms after
44
- mount so the card is visible before the burst.
59
+ confetti: fires window.fireSuccessConfetti() ~100ms
60
+ after the card mounts.
61
+
62
+ Block: callers can pass an inline block that the card renders below
63
+ the CTA — used by turf-monster's onchain-tx modal to inject the
64
+ seeds bar + level-up celebration, which is product-specific UI.
45
65
  %>
46
66
  <%
47
67
  icon_color = local_assigns[:icon_color] || 'primary'
48
68
  redirect_secs = local_assigns[:auto_redirect_seconds] || 5
49
69
  has_redirect = !!local_assigns[:auto_redirect_url_key]
50
70
  fire_confetti = !!local_assigns[:confetti]
71
+ large_title = !!local_assigns[:large_title]
72
+ use_drain = has_redirect && !!local_assigns[:cta_drain]
51
73
 
52
74
  data_attr = "{
53
75
  _remaining: #{redirect_secs},
@@ -92,29 +114,66 @@
92
114
  </div>
93
115
  <% end %>
94
116
 
95
- <p class="text-lg font-bold text-heading mb-1"><%= title %></p>
96
- <% if local_assigns[:message] %>
97
- <p class="text-xs text-secondary mb-4"><%= message %></p>
117
+ <%# Title — large_title swaps text-lg for text-3xl. Either static or
118
+ Alpine-expression-driven via title_key. %>
119
+ <% title_class = large_title ? "text-3xl font-bold text-heading mb-2" : "text-lg font-bold text-heading mb-1" %>
120
+ <% if local_assigns[:title_key] %>
121
+ <p class="<%= title_class %>" x-text="<%= title_key %>"></p>
122
+ <% else %>
123
+ <p class="<%= title_class %>"><%= title %></p>
124
+ <% end %>
125
+
126
+ <% if local_assigns[:message_key] %>
127
+ <p class="text-sm text-secondary mb-4" x-text="<%= message_key %>"></p>
128
+ <% elsif local_assigns[:message] %>
129
+ <p class="text-sm text-secondary mb-4"><%= message %></p>
98
130
  <% end %>
99
131
 
100
- <%# Explorer link — only rendered when the tx_signature_key expression
101
- resolves to a truthy value (e.g. props.txSignature is set). %>
132
+ <%# Explorer link — boxed branded variant when tx_solana is set,
133
+ plain underlined hash otherwise. Both controlled by the same
134
+ tx_signature_key. %>
102
135
  <% if local_assigns[:tx_signature_key] %>
103
- <div x-show="<%= tx_signature_key %>" class="mb-5">
104
- <a :href="'https://explorer.solana.com/tx/' + (<%= tx_signature_key %>) + (typeof clusterParam !== 'undefined' ? clusterParam : '')"
105
- target="_blank" rel="noopener"
106
- class="inline-flex items-center gap-1.5 text-xs font-mono text-secondary hover:text-primary underline underline-offset-2">
107
- <span x-text="(<%= tx_signature_key %>) ? ((<%= tx_signature_key %>).slice(0, 8) + '…' + (<%= tx_signature_key %>).slice(-4)) : ''"></span>
108
- <svg class="w-3 h-3" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
109
- <path stroke-linecap="round" stroke-linejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3"/>
110
- </svg>
111
- </a>
112
- </div>
136
+ <% if local_assigns[:tx_solana] %>
137
+ <div x-show="<%= tx_signature_key %>" class="mb-3">
138
+ <a :href="'https://explorer.solana.com/tx/' + (<%= tx_signature_key %>) + (typeof clusterParam !== 'undefined' ? clusterParam : '')"
139
+ target="_blank" rel="noopener"
140
+ class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg border border-subtle hover:border-primary/50 transition group"
141
+ style="background: rgba(var(--color-primary-500-rgb), 0.06);">
142
+ <svg width="14" height="11" viewBox="0 0 397 311" fill="none">
143
+ <defs>
144
+ <linearGradient id="studio-solana-grad" x1="361" y1="-9" x2="153" y2="389" gradientUnits="userSpaceOnUse">
145
+ <stop offset="0" stop-color="#00FFA3"/>
146
+ <stop offset="1" stop-color="#DC1FFF"/>
147
+ </linearGradient>
148
+ </defs>
149
+ <path d="M65 234c2-2 6-4 9-4h317c6 0 9 7 5 11l-63 63c-2 2-6 4-9 4H6c-6 0-9-7-5-11l64-63z" fill="url(#studio-solana-grad)"/>
150
+ <path d="M65 4c2-2 6-4 9-4h317c6 0 9 7 5 11l-63 63c-2 2-6 4-9 4H6c-6 0-9-7-5-11L65 4z" fill="url(#studio-solana-grad)"/>
151
+ <path d="M333 119c-2-2-6-4-9-4H7c-6 0-9 7-5 11l63 63c2 2 6 4 9 4h317c6 0 9-7 5-11l-63-63z" fill="url(#studio-solana-grad)"/>
152
+ </svg>
153
+ <span class="font-mono text-[11px] text-secondary group-hover:text-primary transition" x-text="(<%= tx_signature_key %>) ? ((<%= tx_signature_key %>).substring(0, 16) + '…') : ''"></span>
154
+ <svg class="w-3 h-3 text-muted group-hover:text-primary transition" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/></svg>
155
+ </a>
156
+ </div>
157
+ <% else %>
158
+ <div x-show="<%= tx_signature_key %>" class="mb-5">
159
+ <a :href="'https://explorer.solana.com/tx/' + (<%= tx_signature_key %>) + (typeof clusterParam !== 'undefined' ? clusterParam : '')"
160
+ target="_blank" rel="noopener"
161
+ class="inline-flex items-center gap-1.5 text-xs font-mono text-secondary hover:text-primary underline underline-offset-2">
162
+ <span x-text="(<%= tx_signature_key %>) ? ((<%= tx_signature_key %>).slice(0, 8) + '…' + (<%= tx_signature_key %>).slice(-4)) : ''"></span>
163
+ <svg class="w-3 h-3" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
164
+ <path stroke-linecap="round" stroke-linejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3"/>
165
+ </svg>
166
+ </a>
167
+ </div>
168
+ <% end %>
113
169
  <% end %>
114
170
 
115
- <%# Auto-redirect countdown — bar + "Redirecting in Xs…" text. Reads
116
- from the partial's own _remaining/_total (started by x-init above). %>
117
- <% if has_redirect %>
171
+ <%# Auto-redirect countdown — two layouts.
172
+ cta_drain: countdown drains as overlay on the CTA button itself
173
+ (the celebration look, no separate progress bar).
174
+ default: separate progress bar above + "Redirecting in Xs…"
175
+ text + standard CTA below. %>
176
+ <% if has_redirect && !use_drain %>
118
177
  <div class="w-full rounded-full overflow-hidden mb-2" style="height:6px;background:rgba(128,128,128,0.2)">
119
178
  <div :style="'height:100%;border-radius:9999px;background:var(--color-cta);transition:width 1s linear;width:' + (_remaining / _total * 100) + '%'"></div>
120
179
  </div>
@@ -123,9 +182,30 @@
123
182
  </p>
124
183
  <% end %>
125
184
 
126
- <%# Primary CTA — either an <a href=...> or a <button> dispatching an event. %>
185
+ <%# Primary CTA — three flavors:
186
+ 1. cta_drain + auto_redirect: countdown drains over the button
187
+ 2. cta_href_key: anchor
188
+ 3. cta_event: button dispatching event %>
127
189
  <% if local_assigns[:cta_label] %>
128
- <% if local_assigns[:cta_href_key] %>
190
+ <% if use_drain && local_assigns[:cta_href_key] %>
191
+ <a :href="<%= cta_href_key %>"
192
+ class="relative overflow-hidden block w-full px-4 py-2.5 rounded-lg font-bold text-sm text-white text-center transition no-underline"
193
+ style="background: var(--color-cta);">
194
+ <div class="absolute inset-0 pointer-events-none origin-left"
195
+ style="background: rgba(255,255,255,0.18);"
196
+ :style="{ animation: 'studio-modal-drain ' + _total + 's linear forwards' }"></div>
197
+ <span class="relative z-10"><%= cta_label %></span>
198
+ </a>
199
+ <% elsif use_drain && local_assigns[:cta_event] %>
200
+ <button @click="$dispatch('<%= cta_event %>')"
201
+ class="relative overflow-hidden block w-full px-4 py-2.5 rounded-lg font-bold text-sm text-white text-center transition no-underline"
202
+ style="background: var(--color-cta);">
203
+ <div class="absolute inset-0 pointer-events-none origin-left"
204
+ style="background: rgba(255,255,255,0.18);"
205
+ :style="{ animation: 'studio-modal-drain ' + _total + 's linear forwards' }"></div>
206
+ <span class="relative z-10"><%= cta_label %></span>
207
+ </button>
208
+ <% elsif local_assigns[:cta_href_key] %>
129
209
  <a :href="<%= cta_href_key %>" class="btn btn-primary w-full"><%= cta_label %></a>
130
210
  <% elsif local_assigns[:cta_event] %>
131
211
  <button @click="$dispatch('<%= cta_event %>')" class="btn btn-primary w-full"><%= cta_label %></button>
@@ -138,4 +218,11 @@
138
218
  <%= secondary_label %>
139
219
  </button>
140
220
  <% end %>
221
+
222
+ <%# Optional inline block — appended below the CTA so callers can
223
+ slot in product-specific celebration UI (seeds bar, badges,
224
+ stat increments) without forking the partial. %>
225
+ <% if block_given? %>
226
+ <div class="mt-3"><%= yield %></div>
227
+ <% end %>
141
228
  </div>
@@ -1,3 +1,3 @@
1
1
  module Studio
2
- VERSION = "0.4.8"
2
+ VERSION = "0.4.9"
3
3
  end
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.8
4
+ version: 0.4.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex McRitchie