@bounded-sh/client 0.0.112-ux-audit.2 → 0.0.112

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.
Files changed (50) hide show
  1. package/dist/auth/bounded-widget-modal.d.ts +24 -2
  2. package/dist/{email-auth-provider-C7RXLVd9.mjs → email-auth-provider-BV1Iea4A.mjs} +2 -2
  3. package/dist/{email-auth-provider-C7RXLVd9.mjs.map → email-auth-provider-BV1Iea4A.mjs.map} +1 -1
  4. package/dist/{email-auth-provider-53sA5_i2.js → email-auth-provider-CyFsnSzx.js} +2 -2
  5. package/dist/{email-auth-provider-53sA5_i2.js.map → email-auth-provider-CyFsnSzx.js.map} +1 -1
  6. package/dist/{email-auth-provider-BfCH7K6N.mjs → email-auth-provider-D0_iXVYk.mjs} +2 -2
  7. package/dist/{email-auth-provider-BfCH7K6N.mjs.map → email-auth-provider-D0_iXVYk.mjs.map} +1 -1
  8. package/dist/{email-auth-provider-DTrCfOJd.js → email-auth-provider-OVzQ63dn.js} +2 -2
  9. package/dist/{email-auth-provider-DTrCfOJd.js.map → email-auth-provider-OVzQ63dn.js.map} +1 -1
  10. package/dist/{guest-auth-provider-Tndt6tzJ.js → guest-auth-provider-0WlOutqU.js} +2 -2
  11. package/dist/{guest-auth-provider-Tndt6tzJ.js.map → guest-auth-provider-0WlOutqU.js.map} +1 -1
  12. package/dist/{guest-auth-provider-DjOFjtnk.mjs → guest-auth-provider-AP80a700.mjs} +2 -2
  13. package/dist/{guest-auth-provider-DjOFjtnk.mjs.map → guest-auth-provider-AP80a700.mjs.map} +1 -1
  14. package/dist/{guest-auth-provider-00KkSNpE.js → guest-auth-provider-B4WIWOjV.js} +2 -2
  15. package/dist/{guest-auth-provider-00KkSNpE.js.map → guest-auth-provider-B4WIWOjV.js.map} +1 -1
  16. package/dist/{guest-auth-provider-2J4fnTX5.mjs → guest-auth-provider-Cqx1zH23.mjs} +2 -2
  17. package/dist/{guest-auth-provider-2J4fnTX5.mjs.map → guest-auth-provider-Cqx1zH23.mjs.map} +1 -1
  18. package/dist/{index-DJeEMevo.js → index-BmNNZlTl.js} +127 -44
  19. package/dist/index-BmNNZlTl.js.map +1 -0
  20. package/dist/{index-D3kdhCpH.mjs → index-D76-cZcD.mjs} +127 -44
  21. package/dist/index-D76-cZcD.mjs.map +1 -0
  22. package/dist/index.js +1 -1
  23. package/dist/index.mjs +1 -1
  24. package/dist/{index.native-ingNhWPw.js → index.native-C5glqiTU.js} +59 -14
  25. package/dist/index.native-C5glqiTU.js.map +1 -0
  26. package/dist/{index.native-CAH_ks0s.mjs → index.native-DyR93n8K.mjs} +59 -14
  27. package/dist/index.native-DyR93n8K.mjs.map +1 -0
  28. package/dist/index.native.js +1 -1
  29. package/dist/index.native.mjs +1 -1
  30. package/dist/{injected-evm-wallet-provider-Cao1U0NZ.js → injected-evm-wallet-provider-4ICYUEU4.js} +2 -2
  31. package/dist/{injected-evm-wallet-provider-Cao1U0NZ.js.map → injected-evm-wallet-provider-4ICYUEU4.js.map} +1 -1
  32. package/dist/{injected-evm-wallet-provider-IlkuKffc.js → injected-evm-wallet-provider-C8YItSiB.js} +2 -2
  33. package/dist/{injected-evm-wallet-provider-IlkuKffc.js.map → injected-evm-wallet-provider-C8YItSiB.js.map} +1 -1
  34. package/dist/{injected-evm-wallet-provider-DXjwPjLW.mjs → injected-evm-wallet-provider-CS4R8c51.mjs} +2 -2
  35. package/dist/{injected-evm-wallet-provider-DXjwPjLW.mjs.map → injected-evm-wallet-provider-CS4R8c51.mjs.map} +1 -1
  36. package/dist/{injected-evm-wallet-provider-BtyG1HOX.mjs → injected-evm-wallet-provider-WxUbyMZL.mjs} +2 -2
  37. package/dist/{injected-evm-wallet-provider-BtyG1HOX.mjs.map → injected-evm-wallet-provider-WxUbyMZL.mjs.map} +1 -1
  38. package/dist/{injected-wallet-provider-CfYNIIHd.mjs → injected-wallet-provider-D1ygndjX.mjs} +2 -2
  39. package/dist/{injected-wallet-provider-CfYNIIHd.mjs.map → injected-wallet-provider-D1ygndjX.mjs.map} +1 -1
  40. package/dist/{injected-wallet-provider-BEYwsn6M.js → injected-wallet-provider-D8vIsUHf.js} +2 -2
  41. package/dist/{injected-wallet-provider-BEYwsn6M.js.map → injected-wallet-provider-D8vIsUHf.js.map} +1 -1
  42. package/dist/{injected-wallet-provider-0SSONh1d.mjs → injected-wallet-provider-UuGrzdNW.mjs} +2 -2
  43. package/dist/{injected-wallet-provider-0SSONh1d.mjs.map → injected-wallet-provider-UuGrzdNW.mjs.map} +1 -1
  44. package/dist/{injected-wallet-provider-DSlHfgMU.js → injected-wallet-provider-nwd3ialR.js} +2 -2
  45. package/dist/{injected-wallet-provider-DSlHfgMU.js.map → injected-wallet-provider-nwd3ialR.js.map} +1 -1
  46. package/package.json +2 -2
  47. package/dist/index-D3kdhCpH.mjs.map +0 -1
  48. package/dist/index-DJeEMevo.js.map +0 -1
  49. package/dist/index.native-CAH_ks0s.mjs.map +0 -1
  50. package/dist/index.native-ingNhWPw.js.map +0 -1
@@ -3876,9 +3876,13 @@ async function ensureSolanaMobileWalletRegistered(config, network) {
3876
3876
  }
3877
3877
  catch (_e) {
3878
3878
  throw new WalletConfigError(`Bounded: Solana Mobile wallet support needs the optional peer dependency ` +
3879
- `'@solana-mobile/wallet-standard-mobile', which is not installed. Install it ` +
3880
- `in your app to enable the mobile wallet, or leave the Solana Mobile wallet ` +
3881
- `out of this app's wallet configuration.`);
3879
+ `'@solana-mobile/wallet-standard-mobile', which is not installed. Until it is, ` +
3880
+ `Android users (Seeker, Saga, any phone browser) get NO wallet to connect: ` +
3881
+ `installed wallet apps are never injected into a page there, the mobile adapter ` +
3882
+ `is the only route. Fix: npm install @solana-mobile/wallet-standard-mobile ` +
3883
+ `(a web app also needs legacy-peer-deps=true in its .npmrc, because the adapter's ` +
3884
+ `protocol package declares react-native as a peer). Or leave the Solana Mobile ` +
3885
+ `wallet out of this app's wallet configuration on purpose.`);
3882
3886
  }
3883
3887
  const { registerMwa, createDefaultChainSelector, SolanaMobileWalletAdapterWalletName, SolanaMobileWalletAdapterRemoteWalletName, } = mwa;
3884
3888
  const before = new Set(getWallets().get());
@@ -3965,7 +3969,7 @@ const OVERLAY_CSS = `
3965
3969
  :host{all:initial}
3966
3970
  *{box-sizing:border-box}
3967
3971
  .bw-ov{position:fixed;inset:0;z-index:2147483647;display:none;
3968
- font-family:"Schibsted Grotesk","Helvetica Neue",Arial,sans-serif}
3972
+ font-family:"Bounded Schibsted Grotesk","Schibsted Grotesk","Helvetica Neue",Arial,sans-serif}
3969
3973
  .bw-ov.mounted{display:block}
3970
3974
  .bw-back{position:fixed;inset:0;background:rgba(4,4,5,.62);opacity:0;
3971
3975
  transition:opacity .24s ease;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}
@@ -3998,6 +4002,29 @@ const OVERLAY_CSS = `
3998
4002
  }
3999
4003
  @media(prefers-reduced-motion:reduce){.bw-back,.bw-panel{transition:opacity .12s linear!important}}
4000
4004
  `;
4005
+ /**
4006
+ * Style the shadow tree with `css` as constructed stylesheets, and say whether it could. A host
4007
+ * CSP whose style-src lacks 'unsafe-inline' refuses an inline <style> even inside a shadow root,
4008
+ * which left the widget unstyled (the Bounded router compiles exactly `style-src 'self'` for an
4009
+ * app that declares browser boundaries and names no style sources); CSP does not govern
4010
+ * constructed sheets. A browser without them (they shipped with adoptedStyleSheets everywhere)
4011
+ * gets <style> elements instead (styleTag).
4012
+ */
4013
+ function adoptStyles(shadow, css) {
4014
+ if (!("adoptedStyleSheets" in shadow))
4015
+ return false;
4016
+ shadow.adoptedStyleSheets = css.map((text) => {
4017
+ const sheet = new CSSStyleSheet();
4018
+ sheet.replaceSync(text);
4019
+ return sheet;
4020
+ });
4021
+ return true;
4022
+ }
4023
+ function styleTag(css) {
4024
+ const style = document.createElement("style");
4025
+ style.textContent = css;
4026
+ return style;
4027
+ }
4001
4028
  let modal = null;
4002
4029
  function getModal() {
4003
4030
  if (modal)
@@ -4005,9 +4032,8 @@ function getModal() {
4005
4032
  const root = document.createElement("div");
4006
4033
  root.setAttribute("data-bounded-widget", "");
4007
4034
  const shadow = root.attachShadow({ mode: "open" });
4008
- const style = document.createElement("style");
4009
- style.textContent = OVERLAY_CSS;
4010
- shadow.appendChild(style);
4035
+ if (!adoptStyles(shadow, [OVERLAY_CSS]))
4036
+ shadow.appendChild(styleTag(OVERLAY_CSS));
4011
4037
  const overlay = document.createElement("div");
4012
4038
  overlay.className = "bw-ov";
4013
4039
  const back = document.createElement("div");
@@ -4142,12 +4168,15 @@ function fitPanelToContent(m) {
4142
4168
  m.fitObserver = ro;
4143
4169
  }
4144
4170
  }
4145
- /** Switch the modal into NATIVE-content mode: detach any iframe, mount `node`. */
4146
- function mountContent(m, node) {
4171
+ /** Switch the modal into NATIVE-content mode: detach any iframe, mount `node`, styled by `css` until the next mount. */
4172
+ function mountContent(m, node, css) {
4147
4173
  var _a;
4148
4174
  detachIframe(m);
4149
4175
  (_a = m.fitObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
4150
4176
  m.fitObserver = null;
4177
+ const shadow = m.root.shadowRoot;
4178
+ if (!(shadow && adoptStyles(shadow, [OVERLAY_CSS, css] )) && css)
4179
+ node.prepend(styleTag(css));
4151
4180
  m.content.replaceChildren(node);
4152
4181
  m.content.style.display = "block";
4153
4182
  }
@@ -4209,27 +4238,39 @@ function detachIframe(m) {
4209
4238
  m.iframeContext = "";
4210
4239
  m.onIframeMessage = null;
4211
4240
  }
4212
- // Load the Bounded editorial typefaces (Newsreader display + Schibsted Grotesk body +
4213
- // JetBrains Mono) once at the document level so they apply inside the shadow root. A
4214
- // no-op if already present or off-DOM. Fonts loaded here match the hosted auth page.
4215
- const FONTS_HREF = "https://fonts.googleapis.com/css2?family=Newsreader:opsz,wght@6..72,400;6..72,500&family=Schibsted+Grotesk:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap";
4241
+ /**
4242
+ * The Bounded editorial typefaces the hosted auth page uses (Newsreader display, Schibsted
4243
+ * Grotesk body), registered once at the document level so they apply inside the shadow root.
4244
+ *
4245
+ * The Bounded router serves them same-origin at /__bounded/fonts/*.woff2 on every site it
4246
+ * serves (bounded-router scripts/build-widget-styles.cjs), and they load through the FontFace
4247
+ * API: no stylesheet, so a host CSP's style-src never applies, and the only fetch is same-origin,
4248
+ * which font-src 'self' admits. Google Fonts CSS injected here was refused by strict hosts such
4249
+ * as the OpenApps venue (style-src 'self'), with a CSP error on every page. A page the router
4250
+ * does not serve has none of its /__bounded/ scripts: nothing is fetched there, and the widget's
4251
+ * font stacks fall back to the page's own faces or system ones. The Bounded-prefixed family
4252
+ * names never restyle the host page's own text.
4253
+ */
4254
+ const WIDGET_FONT_FACES = [
4255
+ { family: "Bounded Newsreader", url: "/__bounded/fonts/newsreader.woff2", weight: "200 800" },
4256
+ { family: "Bounded Schibsted Grotesk", url: "/__bounded/fonts/schibsted.woff2", weight: "400 900" },
4257
+ ];
4216
4258
  function ensureFonts() {
4217
- if (!hasDOM$5())
4259
+ if (!hasDOM$5() || typeof FontFace === "undefined" || !document.fonts)
4218
4260
  return;
4219
- if (document.querySelector('link[data-bounded-fonts="1"]'))
4261
+ if (!document.querySelector('script[src^="/__bounded/"]'))
4220
4262
  return;
4221
- const pre1 = document.createElement("link");
4222
- pre1.rel = "preconnect";
4223
- pre1.href = "https://fonts.googleapis.com";
4224
- const pre2 = document.createElement("link");
4225
- pre2.rel = "preconnect";
4226
- pre2.href = "https://fonts.gstatic.com";
4227
- pre2.crossOrigin = "anonymous";
4228
- const link = document.createElement("link");
4229
- link.rel = "stylesheet";
4230
- link.href = FONTS_HREF;
4231
- link.setAttribute("data-bounded-fonts", "1");
4232
- document.head.append(pre1, pre2, link);
4263
+ const registered = new Set();
4264
+ document.fonts.forEach((face) => registered.add(face.family.replace(/^["']|["']$/g, "")));
4265
+ for (const { family, url, weight } of WIDGET_FONT_FACES) {
4266
+ if (registered.has(family))
4267
+ continue;
4268
+ // Against the page's origin, not its base URL: a <base href> elsewhere must not move the fetch off this origin.
4269
+ const face = new FontFace(family, `url("${location.origin}${url}") format("woff2")`, { weight, display: "swap" });
4270
+ document.fonts.add(face);
4271
+ // A face that cannot load is taken back out; the font stack falls back to the next face.
4272
+ face.load().catch(() => { document.fonts.delete(face); });
4273
+ }
4233
4274
  }
4234
4275
 
4235
4276
  // turnkey-wallet-widget.ts - runs one Turnkey signer exchange inside the shared
@@ -6249,7 +6290,7 @@ async function getAuthProvider(config, fromStorage = false) {
6249
6290
  // manager as legacy email sessions. Human login should be started
6250
6291
  // with loginWithRedirect(); this provider is kept as the restore and
6251
6292
  // logout surface for those sessions.
6252
- currentAuthProvider = new (await import('./email-auth-provider-C7RXLVd9.mjs')).EmailAuthProvider();
6293
+ currentAuthProvider = new (await import('./email-auth-provider-BV1Iea4A.mjs')).EmailAuthProvider();
6253
6294
  break;
6254
6295
  case "phantom": {
6255
6296
  // First-class Solana WALLET LOGIN (the poof/TaroBase-style "connect
@@ -6291,7 +6332,7 @@ async function getAuthProvider(config, fromStorage = false) {
6291
6332
  // (bounded-login-widget.ts), so registration never counts as
6292
6333
  // evidence the user has a wallet.
6293
6334
  await ensureSolanaMobileWalletRegistered(config, configuredNetwork);
6294
- const { InjectedWalletProvider } = await import('./injected-wallet-provider-0SSONh1d.mjs');
6335
+ const { InjectedWalletProvider } = await import('./injected-wallet-provider-UuGrzdNW.mjs');
6295
6336
  currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
6296
6337
  break;
6297
6338
  }
@@ -6304,7 +6345,7 @@ async function getAuthProvider(config, fromStorage = false) {
6304
6345
  // Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
6305
6346
  // to point at a specific wallet or set chainId/domain.
6306
6347
  const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
6307
- const { InjectedEvmWalletProvider } = await import('./injected-evm-wallet-provider-BtyG1HOX.mjs');
6348
+ const { InjectedEvmWalletProvider } = await import('./injected-evm-wallet-provider-WxUbyMZL.mjs');
6308
6349
  currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
6309
6350
  break;
6310
6351
  }
@@ -6329,7 +6370,7 @@ async function getAuthProvider(config, fromStorage = false) {
6329
6370
  break;
6330
6371
  case "guest":
6331
6372
  // Anonymous auth: a device-local ed25519 keypair signs the challenge.
6332
- currentAuthProvider = new (await import('./guest-auth-provider-2J4fnTX5.mjs')).GuestAuthProvider();
6373
+ currentAuthProvider = new (await import('./guest-auth-provider-Cqx1zH23.mjs')).GuestAuthProvider();
6333
6374
  break;
6334
6375
  }
6335
6376
  if (!currentAuthProvider) {
@@ -6391,7 +6432,7 @@ async function signInAnonymously() {
6391
6432
  // Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
6392
6433
  // the main bundle for data-only apps. Specifier is a static string literal
6393
6434
  // so bundle-splitting can see it.
6394
- const provider = new (await import('./guest-auth-provider-2J4fnTX5.mjs')).GuestAuthProvider();
6435
+ const provider = new (await import('./guest-auth-provider-Cqx1zH23.mjs')).GuestAuthProvider();
6395
6436
  setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
6396
6437
  // adoptSessionProvider awaits only getConfig() (already resolved here) and then
6397
6438
  // runs every write - provider/method/config/marker/user - in one synchronous
@@ -6562,7 +6603,7 @@ function ensureSharedWalletLoginReady() {
6562
6603
  if (!sharedWalletReady) {
6563
6604
  sharedWalletReady = (async () => {
6564
6605
  await getConfig();
6565
- await import('./injected-wallet-provider-0SSONh1d.mjs');
6606
+ await import('./injected-wallet-provider-UuGrzdNW.mjs');
6566
6607
  })().catch((error) => {
6567
6608
  sharedWalletReady = null;
6568
6609
  throw error;
@@ -6665,7 +6706,7 @@ async function loginWithWallet(options) {
6665
6706
  await ensureSharedWalletLoginReady();
6666
6707
  else
6667
6708
  await ensureWalletLoginReady();
6668
- const { InjectedWalletProvider } = await import('./injected-wallet-provider-0SSONh1d.mjs');
6709
+ const { InjectedWalletProvider } = await import('./injected-wallet-provider-UuGrzdNW.mjs');
6669
6710
  // Keep the RAW wallet provider: the offchain wrapper forwards only a fixed
6670
6711
  // set of methods, so cleanup below must speak to the provider that actually
6671
6712
  // holds the config rather than to whatever is wrapping it.
@@ -7032,6 +7073,13 @@ function registerStorageBridge() {
7032
7073
  void syncStoredSession().catch((error) => console.warn('Bounded session synchronization failed:', error));
7033
7074
  });
7034
7075
  }
7076
+ function sameSessionIdentity(next, previous) {
7077
+ if (!next || !previous)
7078
+ return false;
7079
+ const a = deriveUserIdentityFromIdToken(next);
7080
+ const b = deriveUserIdentityFromIdToken(previous);
7081
+ return !!a.id && a.id === b.id && a.address === b.address && a.email === b.email && a.evmAddress === b.evmAddress;
7082
+ }
7035
7083
  async function syncStoredSession() {
7036
7084
  const config = initializedConfig;
7037
7085
  if (!config)
@@ -7040,6 +7088,18 @@ async function syncStoredSession() {
7040
7088
  const issuer = WebSessionManager.getIssuer();
7041
7089
  if (token === observedIdToken && issuer === observedIssuer)
7042
7090
  return;
7091
+ // Another tab refreshed this same session (tokens rotate every few minutes):
7092
+ // who is signed in did not change, so keep the user and its live reads, whose
7093
+ // sockets take the new token on their own, instead of hiding and restoring it.
7094
+ if (currentUser && issuer === observedIssuer && sameSessionIdentity(token, observedIdToken)) {
7095
+ const record = await WebSessionManager.readSessionWithGeneration();
7096
+ if (config !== initializedConfig || WebSessionManager.getIdToken() !== token)
7097
+ return;
7098
+ if ((record === null || record === void 0 ? void 0 : record.idToken) === token && (!record.appId || record.appId === config.appId)) {
7099
+ observedIdToken = token;
7100
+ return;
7101
+ }
7102
+ }
7043
7103
  observedIdToken = token;
7044
7104
  observedIssuer = issuer;
7045
7105
  const generation = ++initializationGeneration;
@@ -8067,8 +8127,8 @@ const CARD_CSS = `
8067
8127
  --line:rgba(232,230,222,.08);--line-strong:rgba(232,230,222,.16);
8068
8128
  --text:#e8e6de;--dim:#9b9991;--faint:#85837c;
8069
8129
  --green:#58c98f;--green-bg:rgba(88,201,143,.09);--green-line:rgba(88,201,143,.28);--red:#ec8488;
8070
- --disp:"Newsreader","Iowan Old Style",Georgia,serif;
8071
- --body:"Schibsted Grotesk","Helvetica Neue",Arial,sans-serif;
8130
+ --disp:"Bounded Newsreader","Newsreader","Iowan Old Style",Georgia,serif;
8131
+ --body:"Bounded Schibsted Grotesk","Schibsted Grotesk","Helvetica Neue",Arial,sans-serif;
8072
8132
  position:relative;padding:34px 32px 20px;color:var(--text);font-family:var(--body);
8073
8133
  -webkit-font-smoothing:antialiased}
8074
8134
  .x{position:absolute;top:20px;right:24px;border:0;background:transparent;color:var(--faint);
@@ -8189,6 +8249,17 @@ function discoverWallets(configuredNetwork) {
8189
8249
  // beats a click that does nothing, which is what "one at a time" looks like from
8190
8250
  // the outside.
8191
8251
  const BUSY_MSG = "A sign-in is already in progress - finish or cancel it first.";
8252
+ // Shown instead of an empty card when no lane can be offered (wallet-only app,
8253
+ // no wallet in this browser). A phone browser never sees installed wallet apps
8254
+ // as injected providers, so the honest advice there is a different browser,
8255
+ // not "install a wallet".
8256
+ function noLaneCopy(mobile) {
8257
+ return mobile
8258
+ ? "No wallet is available in this browser. Open this page in your wallet app's " +
8259
+ "browser, or on a desktop browser with a wallet extension, then try again."
8260
+ : "No wallet was found in this browser. Install a Solana wallet extension such as " +
8261
+ "Phantom or Solflare, reload the page, then try again.";
8262
+ }
8192
8263
  const METHOD_LABEL = {
8193
8264
  google: "Continue with Google",
8194
8265
  apple: "Continue with Apple",
@@ -8530,12 +8601,9 @@ async function prepareAndOpen(opts, pending) {
8530
8601
  cancelPendingOpens();
8531
8602
  };
8532
8603
  m.onBackdrop = cancel;
8533
- const style = el("style");
8534
- style.textContent = CARD_CSS;
8535
8604
  let emailDraft = "";
8536
8605
  const renderChoose = (initialError) => {
8537
8606
  const card = el("div", "c");
8538
- card.appendChild(style.cloneNode(true));
8539
8607
  const x = el("button", "x", "Close");
8540
8608
  x.type = "button";
8541
8609
  x.setAttribute("aria-label", "Close");
@@ -8752,6 +8820,24 @@ async function prepareAndOpen(opts, pending) {
8752
8820
  });
8753
8821
  };
8754
8822
  }
8823
+ // A card with no lane at all. It happens when an app asks for wallet-only
8824
+ // (`methods: []`, `wallet: true`) and this browser has no wallet to offer:
8825
+ // no injected wallet, and no Solana Mobile wallet (off-Android, or the app
8826
+ // never installed the adapter). A blank card between the title and the
8827
+ // footer was the shipped behaviour, and it reads as "the site is broken"
8828
+ // to the person and tells the developer nothing. Say what is missing on
8829
+ // screen, and name the usual cause once in the console, where the
8830
+ // developer's own preview run will show it.
8831
+ if (!hasEmail && social.length === 0 && !walletEnabled) {
8832
+ card.appendChild(el("p", "sub", noLaneCopy(detectMobile())));
8833
+ console.warn("[Bounded] openBoundedWidget has no sign-in lane to show: the app asked for " +
8834
+ "no email or social method and no wallet is available in this browser. " +
8835
+ (offerMobile
8836
+ ? "On Android the Solana Mobile wallet is offered only when the app installs " +
8837
+ "'@solana-mobile/wallet-standard-mobile' (see the warning above if registration failed); "
8838
+ : "") +
8839
+ "Add a method, or make sure wallet login is enabled and the mobile adapter is installed.");
8840
+ }
8755
8841
  card.appendChild(status);
8756
8842
  const foot = el("div", "foot");
8757
8843
  const poweredBy = el("a", undefined, "Powered by Bounded");
@@ -8798,7 +8884,6 @@ async function prepareAndOpen(opts, pending) {
8798
8884
  const renderSignInGesture = (choice, done) => {
8799
8885
  var _a;
8800
8886
  const card = el("div", "c");
8801
- card.appendChild(style.cloneNode(true));
8802
8887
  const x = el("button", "x", "Close");
8803
8888
  x.type = "button";
8804
8889
  x.setAttribute("aria-label", "Close");
@@ -8819,7 +8904,6 @@ async function prepareAndOpen(opts, pending) {
8819
8904
  };
8820
8905
  const renderWallets = (initialError) => {
8821
8906
  const card = el("div", "c");
8822
- card.appendChild(style.cloneNode(true));
8823
8907
  const back = el("button", "back", "\u2190 Back");
8824
8908
  back.type = "button";
8825
8909
  // Leaving the wallet list supersedes any attempt still preparing, so it
@@ -8899,7 +8983,6 @@ async function prepareAndOpen(opts, pending) {
8899
8983
  // experience still feels native; the code input, sealing, and verify all live behind the frame.
8900
8984
  const renderOtpFrame = (handle) => {
8901
8985
  const card = el("div", "c");
8902
- card.appendChild(style.cloneNode(true));
8903
8986
  const back = el("button", "back", "\u2190 Back");
8904
8987
  back.type = "button";
8905
8988
  card.appendChild(back);
@@ -8946,7 +9029,7 @@ async function prepareAndOpen(opts, pending) {
8946
9029
  // land after a newer open has taken over the DOM.
8947
9030
  if (!owner.owns())
8948
9031
  return;
8949
- mountContent(m, node);
9032
+ mountContent(m, node, CARD_CSS);
8950
9033
  requestAnimationFrame(() => {
8951
9034
  if (owner.owns())
8952
9035
  fitPanelToContent(m);
@@ -11268,4 +11351,4 @@ function init(config = {}) {
11268
11351
  }
11269
11352
 
11270
11353
  export { onrampStatus as $, getAuthProvider as A, signInAnonymously as B, loginWithPrivy as C, loginWithWallet as D, ensureWalletLoginReady as E, ensureSharedWalletLoginReady as F, preloadWalletLogin as G, loginWithRedirect as H, completeLoginFromRedirect as I, getLoginReturnError as J, loginWithPopup as K, openBoundedWidget as L, ensureSignedIn as M, useAuth as N, useQuery as O, useBuildRun as P, apps as Q, signSolanaMessageViaTurnkey as R, getOrCreateTurnkeyWallet as S, openTurnkeyKeyExport as T, getTurnkeyWalletSessionState as U, getTurnkeyWalletRecoveryUrl as V, WalletConfigError as W, startTurnkeyEmailLogin as X, getIdToken as Y, onramp as Z, onrampQuote as _, signMessageWithTurnkey as a, setPlatform as a0, OffchainAuthProvider as a1, PrivyExpoProvider as a2, requestEip6963Providers as a3, connectBroadcast as a4, connectServiceSession as a5, builds as a6, BuildsApiError as a7, mediaAgentTools as a8, NON_TERMINAL_STATES as a9, TERMINAL_STATES as aa, RUN_STATES as ab, PARK_REASONS as ac, FAILURE_REASONS as ad, BUILD_REVIEW_CAPABILITY_SCHEMA as ae, BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA as af, BUILD_DELEGATED_REVIEW_CAPABILITY_SCHEMA as ag, signTransactionWithTurnkey as b, signTransactionV1WithTurnkey as c, signAndSubmitTransactionWithTurnkey as d, bs58 as e, defaultInjectedProvider as f, getPlatform as g, hasTurnkeyWallet as h, firstStandardWalletProvider as i, setAuthLoading as j, bufferExports as k, setStoredAuthMethod as l, confirmAndCheckTransaction as m, normalizeSolanaRpcUrl as n, defaultInjectedEvmProvider as o, init as p, getCurrentUser as q, resolveSolanaRpcUrl as r, setCurrentUser as s, transactionHasSignature as t, onAuthStateChanged as u, onAuthLoadingChanged as v, getAuthLoading as w, waitForAuthReady as x, login as y, logout as z };
11271
- //# sourceMappingURL=index-D3kdhCpH.mjs.map
11354
+ //# sourceMappingURL=index-D76-cZcD.mjs.map