solana-studio 0.6.0 → 0.6.1
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 +6 -1
- data/app/views/solana_studio/modals/_web3_step_up.html.erb +91 -28
- data/lib/solana_studio/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b69fe07789c9b9f7f1db1627257d1bf48b9f4a0c8008d041737c56eeba6cf39c
|
|
4
|
+
data.tar.gz: 73c86733f7b77f5ace0cec51a59c5cc003d2fc728ce45d868b429abd0963bf45
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ba01f48f7ae0f245db23c4d782b7b195f7cc7bea2e6dc59c8e657a2226e59d616fe09cc2ac88cc8024575bb5835576b4f50e5d1c4fdf0275e97e0d417cb320f8
|
|
7
|
+
data.tar.gz: ee63a339452249039f48ab8ce681d45e308a17c334230ec2cfd7887a05327529d9973694288a94d06f32333450a4a04ab2f7f26f9e5d5f4ef0c147db59c67754
|
data/CHANGELOG.md
CHANGED
|
@@ -10,10 +10,13 @@ The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This pro
|
|
|
10
10
|
- **The web3 modals move here** — `solana_studio/modals/wallet_connect`, `solana_studio/modals/web3_step_up`, `solana_studio/phantom_deeplink` and `solana_studio/deeplink_assets`, promoted out of studio-engine byte for byte. They are Solana-specific to the last line, and studio-engine is the engine EVERY app bundles, including apps that ship no chain feature at all. Only the home changes: the markup, the locals and the behaviour are identical, and the four files are added here before studio-engine drops its copies, so no consumer is ever without a home for them.
|
|
11
11
|
|
|
12
12
|
### Changed
|
|
13
|
+
- **The wallet step-up card leads with the WALLET, not a padlock** (`app/views/solana_studio/modals/_web3_step_up.html.erb`). Four operator edits in one pass. (1) The card's mark is now the remembered brand's own sprite at 56px, falling back to the neutral billfold this file already draws for its no-brand button — it replaces a lock emoji, on the reasoning that a card asking someone to reach for a specific object should show them the object. Hand-rolled rather than passed to `studio/modals/blocks/_card_header`, because that block takes an emoji, a spinner or one of two fixed icons and has NO slot for a brand mark; its else-branch paints a check tinted with the host's `primary` token. Per the modal-lifecycle module the trigger for a primitive is a second consumer or a third copy, and this is the first — when a second card wants a brand header, add an icon slot to `card_header` rather than a second hand-rolled one. (2) The default `subtext` drops from four lines to one: *This account is secured by a Solana wallet. You are signed in, but this session can't sign on-chain — so on-chain actions still need your wallet.* becomes *Your account is secured by a Solana wallet.* **A host that overrides `subtext` never sees this** — see Notes for hosts. (3) The truncated address moved UP out of the footnote into the body, inside an `x-if` on `walletHint`, so the card names the wallet it is asking for where the sentence is rather than under the button. The footnote it replaces is gone. (4) The **full-width "Use a different wallet" row is gone**, replaced by a quiet **"Not your wallet?" link beside the address** — same `openPicker()` handler, a fraction of the layout, and the card still runs CTA → `Not now`. Both are operator calls. The link carries TWO gates and they answer different questions: the `x-if` is `walletHint` (no address, no sentence to correct) and the `x-show` is `canOneClick` (with no remembered brand the PRIMARY button is already the picker, so the link would be a second door into the same room). **Those two states are not the same** — an account that linked its wallet before the host stored the brand has an address and no brand — which is why the link needs both. Without it the address made a mismatch VISIBLE without making it ACTIONABLE: `providerMissing` is written in exactly one place, inside `signIn()` after the `reachable()` guard, so nothing auto-flips, and a remembered wallet that was simply the WRONG one had no route to the picker at all.
|
|
14
|
+
- **The body block carries `mb-5`, and that is a FIX rather than a taste change.** The old callsite passed a BLOCK to `card_header`, which wraps `yield` in a `<p>` of its own — so the rendered card emitted `<p class="text-xs text-secondary mb-5"><p class="text-sm text-body">` and the HTML parser auto-closed it into a stray EMPTY paragraph. That empty paragraph WAS the body-to-CTA gap. Hand-rolling the header removed the accident, so the margin now has to be declared. Both new icon branches also carry `aria-hidden`, which `card_header`'s svg did not.
|
|
13
15
|
- **The wallet button's visibility gate tolerates a host that never defined `methodOn`.** `x-show="methodOn('wallet')"` became `x-show="typeof methodOn === 'function' ? methodOn('wallet') : true"`. A bare call in a host without the member THROWS, Alpine grades the throw as falsy, and the button **silently never renders** — no error a user can see, no missing asset, nothing in the page to debug. That was the second, independent blocker on Turf Monster's adoption, and it is invisible by construction. MEASURED in Chromium against the vendored Alpine 3.16.1, driving the real rendered markup: without the guard the console carries `methodOn is not defined` and the button resolves to `display:none`; with it the button shows, and a host that DOES define `methodOn` still follows the toggle in **both** directions. The fallback is `true` rather than false because bundling the gem has already answered *is wallet implemented* — a host with no toggle has expressed no opinion about showing it, and defaulting to false rebuilds the same invisible button from the other side. The test stays in **Alpine** deliberately: Ruby decides whether the partial EXISTS and Alpine decides whether it SHOWS, and folding the visibility into Ruby collapses that split and brings back the floating-divider bug on a toggle page.
|
|
14
16
|
- `.github/workflows/gem-ci.yml` asserts all four new partials are inside the BUILT gem, not just the manifest. `test/gemspec_test.rb` already asserts the invariant that every `app/` file reaches `spec.files`; the artifact check is a different claim, and only the artifact is what a consumer installs.
|
|
15
17
|
|
|
16
18
|
### Tests
|
|
19
|
+
- **`test/views/web3_step_up_modal_test.rb` (17 -> 24)** — seven tests for the polish pass above, one of them the CONTROL that keeps the other six honest. **The first version of them was INERT and this is recorded because the failure mode is invisible from inside the file**: they asserted `'#se-wallet-' + provider` and `canOneClick` against the WHOLE rendered card, and both strings already lived in it — the first in the pre-existing 36px CTA row, the second in the x-data getter. Deleting the ENTIRE new brand header left the suite at 22 runs / 118 assertions / **0 failures** while a probe on the same tree confirmed the header was gone from the HTML (header div present true->false, `w-14 h-14` tiles 3->0, sprite-binding sites 2->1). **Six of nine mutants survived.** The fix is STRUCTURAL rather than a longer string: `brand_header` slices the element immediately ABOVE the heading and `header_branch` slices one `x-if` branch of it by its GUARD, so no assertion can be satisfied by markup elsewhere in the card and a swap of the two branches moves the markup to the other guard. `test_the_header_slice_can_come_up_empty` is the control — strip the header from the rendered HTML and the slice must find no branch — and it is not decorative: widening `brand_header` to the whole card takes it RED against the CTA row, which is the original bug reproduced. Re-measured on the SHIPPED tree across **20 mutants, every one KILLED, no survivors**, each shown to reach the file by its own diffstat against a baseline of **24 runs / 151 assertions / 0 failures**, restored clean after every one. The card: delete the whole header (+0/-15, 2 failures), swap its two branches (+5/-5), delete the billfold fallback (+0/-8), hardcode the mark to `#se-wallet-phantom` (+1/-1), drop both `x-if` guards (+0/-4), an HTML-entity padlock (+1/-6) and a literal-codepoint one (+1/-6) — **both** forms, because `🔒` renders identically and sails past a codepoint-only refutation — restore the alternate-wallet row (+4/-0), `mb-5` -> `mb-0` (+1/-1), and the margin drifting onto an empty neighbour (+2/-1, 4 failures). The correction link: delete it (+1/-3), move it OUTSIDE the `walletHint` guard (+4/-3), point it at `dismiss()` (+1/-1), drop the `canOneClick` gate (+1/-1), INVERT that gate (+1/-1), relabel it as the row it replaced (+1/-1, 2 failures), let it grow back to `w-full` (+1/-1), delete the address and keep the link (+1/-2, 2 failures), split the nowrap group so the link wraps onto its own line (+1/-1), and unguard the address line entirely (+0/-1, 2 failures). The link and the address are asserted SEPARATELY even though they share one block, because an assertion matching the block as a whole cannot say which half broke — the same shape as the inert-header bug.
|
|
17
20
|
- **`test/views/auth_wallet_credential_test.rb`** (14, new) — the RENDER tier for the credential button, which this gem never had. It is the tier that can see this change at all: the default handler is assembled by ERB, so the string hosts depend on appears ZERO times in the template source and a source grep stays green on a partial whose default never reaches the attribute. Pins the default handler against the literal previous bytes, the default tracking `modal_store` (a hardcoded `"modals"` would give the style guide a dead button), the override replacing the default, the override leaving **no** trace of it, the age gate LEADING an override — narrowly, and the assertion now says so, because `start_with?` cannot see whether the gate also CONTROLS the tail — the handler reaching the page **unescaped** — read from the RAW html, never the parsed attribute, because Nokogiri decodes entities and the assertion would otherwise pass on the very bug it exists to catch — the default carrying no double quote that would close the attribute, and both halves of the visibility gate. Mutation-proven five ways, every mode red: dropping `raw`, hardcoding the store name, reverting the gate to the bare call, letting `on_click` swallow the age gate, and dropping the picker's props. A **behavioural tier** was added alongside the string assertions: it runs the rendered handler in a real JS engine, built the way Alpine builds it, and asserts the gate CONTROLS the tail for every top-level operator that binds looser than `&&`. It carries its own control — the pre-fix spelling, asserted to STILL bypass — so a harness that quietly stopped seeing anything goes red instead of green. Both were mutation-proven: blanking the harness took three tests red, pinning `attested()` to a constant took two.
|
|
18
21
|
- `test/auth_credential_test.rb` (7 -> 11): the partial sits at the path the engine resolves, the click handler consults the modal's age gate, visibility binds to the shared `methodOn('wallet')` toggle, `modal_store` is required with no default, the swap carries the picker's `backTo`/`ageAttested` contract, the brand gradient id is namespaced against collisions, and the mark stays inline rather than becoming a sprockets asset request. Every assertion reads ERB-comment-stripped source, because this file documents its own contract in prose and a bare grep would match the paragraph instead of the code — proven by mutating the gate into a comment and watching it go red. The `on_click` assertion is bound to the CONCERN rather than one spelling of it: it pinned `fetch(:on_click) do`, which went red when the precedence fix traded that for a `key?` branch while preserving every property the assertion existed to protect. It now asserts the local is read through an accessor that tolerates its ABSENCE, which is the thing that actually matters. A new assertion pins the parentheses around an overridden tail, labelled as the WEAKER copy — it matches characters, and the render tier next door is the authority.
|
|
19
22
|
- `test/gemspec_test.rb` and `.github/workflows/gem-ci.yml` name the new partial alongside the modal, so a `spec.files` glob that stopped matching it fails at build rather than in a consumer.
|
|
@@ -21,7 +24,9 @@ The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This pro
|
|
|
21
24
|
- **`test/erb_comment_leak_test.rb`** (6) — ported from studio-engine, whose glob stops covering these files the moment they leave it. An ERB comment ends at its FIRST close sequence, so a comment quoting a tag leaks its tail into the page as visible prose; the two most comment-dense files in the move (`_wallet_connect`, `_web3_step_up`, 8 comments each) were exactly the ones losing the guard. All three leak signatures ported with their guard-the-guard probes, and mutation-proved on a real moved file.
|
|
22
25
|
|
|
23
26
|
### Notes for hosts
|
|
24
|
-
- **
|
|
27
|
+
- **The "Use a different wallet" row is GONE, and a host bound to it goes red at its next lock bump, not at this PR.** This gem ships only `.github/workflows/gem-ci.yml` — there is no consumer lane — so a green CI here cannot see a consumer at all. turf-monster bound to that row in TWO places: `test/controllers/web3_step_up_gallery_test.rb` ("a remembered wallet still offers a way to use another one", asserting both the label and `openPicker()`) and `e2e/web3_step_up.spec.js`, which does not merely assert copy — it clicks the row and walks step-up -> wallet-connect -> Back -> step-up, checking the props survive the round trip. It breaks at `bump_consumer_locks_for_qa` during the next `qa-release` sweep, which runs `bundle lock --update solana-studio --conservative` on every consumer. **THE ROUTE SURVIVES, ITS SPELLING DOES NOT.** A quiet **"Not your wallet?"** link beside the address calls the same `openPicker()` the row called, so a consumer's picker round trip is REPOINTED rather than deleted. Write that locator to accept EITHER spelling (`/Use a different wallet|Not your wallet\?/`) if the spec must be green on both sides of the bump — a consumer's own CI resolves the PUBLISHED gem, so a spec pinned to the new label is red on that consumer's PR and a spec pinned to the old one is red at the sweep. turf-monster's two files now do exactly that. The other unchanged route is the no-remembered-brand one: the "Connect your wallet" button IS `openPicker()` in both releases. Why the link exists at all, since the acceptance never named the cost: `providerMissing` is written in exactly ONE place (inside `signIn()`, after the `reachable()` guard) and nothing in `init()`, `refresh()` or the poll touches it, so there is no auto-flip — without the link a remembered-but-UNREACHABLE wallet had to press a button that could not work and read an error before the no-brand row appeared, and a remembered, REACHABLE, but WRONG wallet had no route to the picker at all. It is advisory either way: the session stays usable, `Not now` exits, and the help line still reaches a human.
|
|
28
|
+
- **The default `subtext` is one line now, and a host that OVERRIDES it sees none of that.** turf-monster passed its own four-line `subtext:`, so the gem's default changed and the card a player actually met did not — it kept the long copy AND gained the new address line, which is more copy rather than less. If you override `subtext`, this release is the moment to decide whether you still mean to. This gem's own `test/views/web3_step_up_modal_test.rb` refutes the string `this session can`, which was live in a consumer's override at the time this shipped.
|
|
29
|
+
- **RESOLVED — studio-engine's style-guide specimen no longer pins the OLD gate string.** This entry warned that `test/views/style_web3_specimens_test.rb` asserted the literal `x-show="methodOn('wallet')"`, which the new gate does not contain, and that it would go red the moment the engine bumped past this gem. Both halves have since moved and the warning is kept as provenance rather than as live guidance. Re-derived 2026-09-06: studio-engine's `Gemfile.lock` now resolves **solana-studio 0.6.0** (not the 0.5.3 this note was written against), and its assertion is now `assert_includes gate, "methodOn('wallet')"` — the SUBSTRING form this note asked for, landed by its own commit *Bind the wallet gate assertion to the CTA element*. The gate it reads, `typeof methodOn === 'function' ? methodOn('wallet') : true`, contains that substring, so the engine is green on the current pin. Nothing to do; the recorded reasoning stands, which is why it is corrected here rather than deleted.
|
|
25
30
|
- **These require studio-engine at render time** — they render its modal host and its shared blocks by name, and paint with its utilities and theme tokens. `solana_studio/modals/network_mismatch` already shipped on exactly these terms, so this is the established arrangement rather than new coupling. studio-engine stays a DEVELOPMENT dependency; a runtime one would drag Rails into every plain-Ruby consumer.
|
|
26
31
|
- **`phantom_deeplink` still emits `Studio.wallet_sign_in_statement`, deliberately.** studio-engine's `solana_sessions/phantom_callback` rebuilds the signed message to verify it, so both halves read one accessor precisely so they cannot drift. Parameterising it would decouple the gem, read as an improvement, and break the signature check on every mobile sign-in. Pinned from both directions in `test/web3_modals_test.rb`.
|
|
27
32
|
- **The wallet sign-in button needs studio-engine >= 0.68.0.** 0.68.0 is the first engine whose `style/modals/_auth` looks up `solana_studio/auth/wallet_credential`; until it, the engine drew that button INLINE and never consulted the path at all, so this gem shipped the partial, its tests and its README section for a button no host rendered. Below the floor the partial is inert and NOTHING SAYS SO — no render, no button, no error — so the symptom is silence rather than a stack trace, which is the failure mode this ecosystem's Gemfile pin comments exist to prevent. DERIVED by unpacking the published gems, not read off a changelog: 0.67.2 carries no `solana_studio/auth` reference anywhere in `app/`, and 0.68.0 gates the render on `lookup_context.exists?("wallet_credential", ["solana_studio/auth"], true)`. Match on the NAMESPACE, not the basename — `style/modals/_wallet_connect` ships unchanged in both releases and answers an unrelated question. It pairs with a floor on THIS gem: 0.5.2 shipped the credential partial WITHOUT `solana_studio/modals/_wallet_connect`, which the engine's `web3_gem` capability gate requires, so on 0.5.2 the button is suppressed either side of the floor and 0.5.3 is the first version that can draw it. Full matrix in the README.
|
|
@@ -27,9 +27,13 @@
|
|
|
27
27
|
help_url — the escape hatch for a user who cannot reach their wallet
|
|
28
28
|
(default "/help"). Set to nil to drop the line entirely.
|
|
29
29
|
help_label — (default "Get help")
|
|
30
|
-
picker_modal_id — the host's brand picker
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
picker_modal_id — the host's brand picker (default "wallet-connect"). TWO
|
|
31
|
+
routes reach it: the no-remembered-brand button, which IS
|
|
32
|
+
the picker's opener, and the quiet "Not your wallet?" link
|
|
33
|
+
beside the address. (Both replaced a full-width "Use a
|
|
34
|
+
different wallet" row dropped on 2026-09-06.) The picker
|
|
35
|
+
is expected to honour a `backTo` prop pointing back at
|
|
36
|
+
this card.
|
|
33
37
|
modal_id — this card's own id, passed to the picker as backTo
|
|
34
38
|
(default "web3-step-up")
|
|
35
39
|
modal_store — Alpine store name (default "modals"; the living style
|
|
@@ -69,8 +73,7 @@
|
|
|
69
73
|
<%
|
|
70
74
|
heading = local_assigns.fetch(:heading, "Sign in with your wallet")
|
|
71
75
|
subtext = local_assigns.fetch(:subtext,
|
|
72
|
-
"
|
|
73
|
-
"session can’t sign on-chain — so on-chain actions still need your wallet.")
|
|
76
|
+
"Your account is secured by a Solana wallet.")
|
|
74
77
|
help_url = local_assigns.fetch(:help_url, "/help")
|
|
75
78
|
help_label = local_assigns.fetch(:help_label, "Get help")
|
|
76
79
|
picker_modal_id = local_assigns.fetch(:picker_modal_id, "wallet-connect")
|
|
@@ -83,9 +86,16 @@
|
|
|
83
86
|
connecting: false,
|
|
84
87
|
error: '',
|
|
85
88
|
// Set when the remembered wallet turns out not to be reachable in THIS
|
|
86
|
-
// browser (a different machine, the extension removed).
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
+
// browser (a different machine, the extension removed). WRITTEN IN ONE
|
|
90
|
+
// PLACE ONLY — inside signIn(), after the reachable() guard — so it
|
|
91
|
+
// never pre-empts: init(), refresh() and the poll below all leave it
|
|
92
|
+
// alone. The user presses the brand button, reads the error, and only
|
|
93
|
+
// THEN does the card fall back to the no-brand row. That row is the
|
|
94
|
+
// BUTTON that opens the picker, not the picker itself. Since 2026-09-06
|
|
95
|
+
// that is no longer the ONLY way out of a wallet we remember wrongly:
|
|
96
|
+
// the address line carries a link to the picker, so an unreachable or
|
|
97
|
+
// simply WRONG remembered wallet is correctable without first pressing
|
|
98
|
+
// a button that cannot work.
|
|
89
99
|
providerMissing: false,
|
|
90
100
|
// Which wallets this document can actually see. Read on a poll, NOT once
|
|
91
101
|
// at mount: wallet provider registration fills in asynchronously, and
|
|
@@ -191,9 +201,80 @@
|
|
|
191
201
|
aria-label="Close">×</button>
|
|
192
202
|
</div>
|
|
193
203
|
|
|
194
|
-
|
|
204
|
+
<%# NOT blocks/_card_header, and the reason is a missing slot rather than a
|
|
205
|
+
preference. That block takes an emoji, a spinner, or one of two fixed
|
|
206
|
+
icons — there is no slot for a BRAND MARK, and its else-branch paints a
|
|
207
|
+
CHECK MARK tinted with the theme's own primary token: line 63-64 of
|
|
208
|
+
_card_header.html.erb draws bg-<icon_color>/15 with icon_color defaulting
|
|
209
|
+
to 'primary', so the colour follows the host's theme and is green only
|
|
210
|
+
where primary is. Either way it cannot render this card's icon at all,
|
|
211
|
+
which is the point. One card needing
|
|
212
|
+
a brand header is not yet a primitive: per the modal-lifecycle module the
|
|
213
|
+
trigger is a second consumer or a third copy, and this is the first. When a
|
|
214
|
+
second card wants one, add an icon slot to card_header rather than a
|
|
215
|
+
second hand-rolled header here.
|
|
216
|
+
|
|
217
|
+
THE MARK IS THE WALLET, not a padlock. The card is asking the user to reach
|
|
218
|
+
for a specific object; showing them the object beats a generic security
|
|
219
|
+
glyph, and where we remember WHICH wallet we show that brand — the same
|
|
220
|
+
mark the button below carries, so the card names one thing twice rather
|
|
221
|
+
than two things once. With no remembered brand it falls back to the neutral
|
|
222
|
+
billfold this file already draws for its no-brand button. %>
|
|
223
|
+
<div class="text-center mb-4">
|
|
224
|
+
<template x-if="canOneClick">
|
|
225
|
+
<span class="inline-flex w-14 h-14 rounded-2xl overflow-hidden items-center justify-center">
|
|
226
|
+
<svg class="w-14 h-14" aria-hidden="true"><use :href="'#se-wallet-' + provider"></use></svg>
|
|
227
|
+
</span>
|
|
228
|
+
</template>
|
|
229
|
+
<template x-if="!canOneClick">
|
|
230
|
+
<span class="inline-flex w-14 h-14 rounded-2xl bg-inset items-center justify-center" aria-hidden="true">
|
|
231
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" class="w-7 h-7 text-secondary">
|
|
232
|
+
<rect x="3" y="6.5" width="18" height="11" rx="2.5" />
|
|
233
|
+
<circle cx="16.6" cy="12" r="1.3" fill="currentColor" stroke="none" />
|
|
234
|
+
</svg>
|
|
235
|
+
</span>
|
|
236
|
+
</template>
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
<h3 class="text-heading font-bold text-lg leading-tight text-center mb-2"><%= heading %></h3>
|
|
240
|
+
|
|
241
|
+
<%# ONE LINE, plus a second only when we can name the wallet. The previous copy
|
|
242
|
+
ran four lines explaining that the session cannot sign on-chain — true, and
|
|
243
|
+
more than a person standing in front of a button needs. The address is the
|
|
244
|
+
part that earns its place: it is how the user confirms the card is asking
|
|
245
|
+
for the wallet they think it is, which is why it moved UP here from the
|
|
246
|
+
footnote under the button.
|
|
247
|
+
|
|
248
|
+
AND THE ONE THING THE ADDRESS ALONE CANNOT DO IS CORRECT IT. Naming the
|
|
249
|
+
wallet makes a mismatch VISIBLE without making it ACTIONABLE, which is
|
|
250
|
+
worse than not naming it — so the address carries a quiet link that opens
|
|
251
|
+
the picker. It replaces a full-width alternate-wallet row (dropped as an
|
|
252
|
+
operator call): same handler, a fraction of the layout, and the card still
|
|
253
|
+
runs CTA then Not now.
|
|
254
|
+
|
|
255
|
+
TWO GATES, and they answer different questions. The x-if is walletHint —
|
|
256
|
+
no address means no sentence to correct. The x-show is canOneClick,
|
|
257
|
+
because when there is no remembered brand the PRIMARY button is already
|
|
258
|
+
the picker: the link would be a second door into the same room, competing
|
|
259
|
+
with the one action this card exists to offer. Note that these two states
|
|
260
|
+
are NOT the same — an account that linked its wallet before the host
|
|
261
|
+
stored the brand has an address and no brand — which is exactly why the
|
|
262
|
+
link needs both. %>
|
|
263
|
+
<div class="text-center mb-5">
|
|
195
264
|
<p class="text-sm text-body"><%= subtext %></p>
|
|
196
|
-
|
|
265
|
+
<template x-if="walletHint">
|
|
266
|
+
<p class="text-sm text-body mt-1">Please sign in with wallet
|
|
267
|
+
<%# BESIDE the address, and the nowrap is what keeps it there. Without it
|
|
268
|
+
the link wraps onto a line of its own, centred under the sentence —
|
|
269
|
+
which is visually the row this replaced, at a smaller size. The group
|
|
270
|
+
breaks as ONE unit, so a narrow card moves the address and the link
|
|
271
|
+
down together instead of splitting them. %>
|
|
272
|
+
<span class="whitespace-nowrap"><span class="font-mono text-secondary"
|
|
273
|
+
x-text="walletHint"></span><button type="button" @click="openPicker()"
|
|
274
|
+
:disabled="connecting" x-show="canOneClick"
|
|
275
|
+
class="ml-1 text-xs text-muted underline hover:text-secondary disabled:opacity-50">Not your wallet?</button></span></p>
|
|
276
|
+
</template>
|
|
277
|
+
</div>
|
|
197
278
|
|
|
198
279
|
<%# PRIMARY — THE STANDARD WEB3 AUTH BUTTON: a wallet row, not a filled CTA.
|
|
199
280
|
Same shape the connect picker uses — brand mark, the wallet's own name, an
|
|
@@ -219,16 +300,6 @@
|
|
|
219
300
|
</svg>
|
|
220
301
|
</span>
|
|
221
302
|
</button>
|
|
222
|
-
<%# One line on what pressing it DOES. A signature prompt is alarming
|
|
223
|
-
without it, and "no funds move" is the difference between clicking and
|
|
224
|
-
bailing. The address rides here so the user can confirm the card is
|
|
225
|
-
asking for the wallet they think it is. %>
|
|
226
|
-
<p class="text-[11px] text-muted mt-1.5 px-1">
|
|
227
|
-
Signing proves the wallet is yours — it does not move any funds.
|
|
228
|
-
<template x-if="walletHint">
|
|
229
|
-
<span>Wallet <span class="font-mono text-secondary" x-text="walletHint"></span></span>
|
|
230
|
-
</template>
|
|
231
|
-
</p>
|
|
232
303
|
</div>
|
|
233
304
|
</template>
|
|
234
305
|
|
|
@@ -264,14 +335,6 @@
|
|
|
264
335
|
<p role="alert" class="text-red-400 text-sm mt-3 text-center" x-text="error"></p>
|
|
265
336
|
</template>
|
|
266
337
|
|
|
267
|
-
<%# SECONDARY — always reachable, so a user whose remembered wallet is the
|
|
268
|
-
wrong one is never cornered by our memory of it. %>
|
|
269
|
-
<template x-if="canOneClick">
|
|
270
|
-
<button type="button" @click="openPicker()" :disabled="connecting"
|
|
271
|
-
class="mt-3 block w-full text-center text-sm text-secondary hover:text-heading disabled:opacity-50">
|
|
272
|
-
Use a different wallet
|
|
273
|
-
</button>
|
|
274
|
-
</template>
|
|
275
338
|
|
|
276
339
|
<div class="mt-4 pt-3 border-t border-strong">
|
|
277
340
|
<button type="button" @click="dismiss()" :disabled="connecting"
|
|
@@ -16,5 +16,5 @@ module SolanaStudio
|
|
|
16
16
|
# through the normal cycle. Splitting the version out is the same shape
|
|
17
17
|
# studio-engine already uses (lib/studio/version.rb) and hands each file back
|
|
18
18
|
# to its real owner: this one to the release, the gemspec to the PR.
|
|
19
|
-
VERSION = "0.6.
|
|
19
|
+
VERSION = "0.6.1"
|
|
20
20
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solana-studio
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex McRitchie
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ed25519
|