@txnlab/use-wallet-ui-react 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +95 -2
  2. package/dist/cjs/chunks/passkey-C8Dw6sNL.cjs +2 -0
  3. package/dist/cjs/chunks/passkey-C8Dw6sNL.cjs.map +1 -0
  4. package/dist/cjs/index.cjs +14 -14
  5. package/dist/cjs/index.cjs.map +1 -1
  6. package/dist/cjs/plugins/passkey.cjs +2 -0
  7. package/dist/cjs/plugins/passkey.cjs.map +1 -0
  8. package/dist/esm/chunks/passkey-BbgvIGki.js +44 -0
  9. package/dist/esm/chunks/passkey-BbgvIGki.js.map +1 -0
  10. package/dist/esm/index.js +2966 -2679
  11. package/dist/esm/index.js.map +1 -1
  12. package/dist/esm/plugins/passkey.js +578 -0
  13. package/dist/esm/plugins/passkey.js.map +1 -0
  14. package/dist/style.css +152 -0
  15. package/dist/types/components/ConnectWalletMenu.d.ts +7 -1
  16. package/dist/types/components/ConnectWalletMenu.d.ts.map +1 -1
  17. package/dist/types/components/ConnectedWalletMenu.d.ts.map +1 -1
  18. package/dist/types/components/PasskeyConnect.d.ts +29 -0
  19. package/dist/types/components/PasskeyConnect.d.ts.map +1 -0
  20. package/dist/types/components/WalletButton.d.ts +7 -1
  21. package/dist/types/components/WalletButton.d.ts.map +1 -1
  22. package/dist/types/components/WalletGroupLabel.d.ts +6 -0
  23. package/dist/types/components/WalletGroupLabel.d.ts.map +1 -0
  24. package/dist/types/components/WalletIcon.d.ts +6 -0
  25. package/dist/types/components/WalletIcon.d.ts.map +1 -0
  26. package/dist/types/components/WalletList.d.ts.map +1 -1
  27. package/dist/types/index.d.cts +5 -0
  28. package/dist/types/index.d.ts +5 -0
  29. package/dist/types/index.d.ts.map +1 -1
  30. package/dist/types/plugins/passkey/PasskeyDisclosure.d.ts +11 -0
  31. package/dist/types/plugins/passkey/PasskeyDisclosure.d.ts.map +1 -0
  32. package/dist/types/plugins/passkey/PasskeyRecoveryDialog.d.ts +10 -0
  33. package/dist/types/plugins/passkey/PasskeyRecoveryDialog.d.ts.map +1 -0
  34. package/dist/types/plugins/passkey/PasskeyRecoveryMenuButton.d.ts +5 -0
  35. package/dist/types/plugins/passkey/PasskeyRecoveryMenuButton.d.ts.map +1 -0
  36. package/dist/types/plugins/passkey/PasskeySessionControl.d.ts +6 -0
  37. package/dist/types/plugins/passkey/PasskeySessionControl.d.ts.map +1 -0
  38. package/dist/types/plugins/passkey/constants.d.ts +3 -0
  39. package/dist/types/plugins/passkey/constants.d.ts.map +1 -0
  40. package/dist/types/plugins/passkey/index.d.cts +36 -0
  41. package/dist/types/plugins/passkey/index.d.ts +36 -0
  42. package/dist/types/plugins/passkey/index.d.ts.map +1 -0
  43. package/dist/types/plugins/passkey/index.test.d.ts +2 -0
  44. package/dist/types/plugins/passkey/index.test.d.ts.map +1 -0
  45. package/dist/types/plugins/passkey/storage.d.ts +5 -0
  46. package/dist/types/plugins/passkey/storage.d.ts.map +1 -0
  47. package/dist/types/plugins/passkey/storage.test.d.ts +2 -0
  48. package/dist/types/plugins/passkey/storage.test.d.ts.map +1 -0
  49. package/dist/types/plugins/passkey/usePasskeyAdapter.d.ts +10 -0
  50. package/dist/types/plugins/passkey/usePasskeyAdapter.d.ts.map +1 -0
  51. package/dist/types/providers/WalletUIProvider.d.ts +15 -1
  52. package/dist/types/providers/WalletUIProvider.d.ts.map +1 -1
  53. package/dist/types/utils/passkey.d.ts +59 -0
  54. package/dist/types/utils/passkey.d.ts.map +1 -0
  55. package/dist/types/utils/passkey.test.d.ts +2 -0
  56. package/dist/types/utils/passkey.test.d.ts.map +1 -0
  57. package/package.json +13 -2
package/README.md CHANGED
@@ -18,6 +18,7 @@ Ready-to-use React UI components for Algorand wallet integration, built as a com
18
18
  - [Hooks](#hooks)
19
19
  - [Tanstack Query Integration](#tanstack-query-integration)
20
20
  - [Plugins](#plugins)
21
+ - [Passkey Wallet](#passkey-wallet)
21
22
  - [Migration from v0.x](#migration-from-v0x)
22
23
  - [License](#license)
23
24
 
@@ -322,6 +323,7 @@ Required wrapper that enables theming, NFD lookups, and data prefetching.
322
323
  enablePrefetching={true} // Prefetch data for all wallet accounts
323
324
  prefetchNfdView="thumbnail" // NFD data view: 'tiny' | 'thumbnail' | 'brief' | 'full'
324
325
  queryClient={queryClient} // Optional: your existing Tanstack Query client
326
+ passkeyLayout="list" // Passkey wallet layout: 'list' | 'featured'
325
327
  >
326
328
  {children}
327
329
  </WalletUIProvider>
@@ -336,6 +338,7 @@ All-in-one wallet connection component.
336
338
  size="md" // 'sm' | 'md' | 'lg'
337
339
  className="..." // Additional CSS classes
338
340
  style={{...}} // Inline styles (supports CSS variable overrides)
341
+ passkeyLayout="featured" // Optional: overrides WalletUIProvider's passkeyLayout
339
342
  />
340
343
  ```
341
344
 
@@ -356,6 +359,9 @@ Dropdown menu for wallet selection (disconnected state).
356
359
  <ConnectWalletMenu>
357
360
  <button>My Custom Button</button>
358
361
  </ConnectWalletMenu>
362
+
363
+ // With its own passkey layout (overrides WalletUIProvider's passkeyLayout)
364
+ <ConnectWalletMenu passkeyLayout="featured" />
359
365
  ```
360
366
 
361
367
  ### ConnectWalletButton
@@ -426,12 +432,31 @@ function Profile() {
426
432
 
427
433
  ### WalletList
428
434
 
429
- Renders a list of available wallets (used internally by ConnectWalletMenu).
435
+ Renders a list of wallets (used internally by ConnectWalletMenu). The dialog renders the passkey wallet in its own section above the list, so pass the other wallets here.
436
+
437
+ ```jsx
438
+ <WalletList
439
+ wallets={wallets}
440
+ handleWalletClick={(wallet) => wallet.connect()}
441
+ />
442
+ ```
443
+
444
+ ### PasskeyConnect
445
+
446
+ The passkey section of the connect dialog, for custom connect menus. Signing in (**Continue with passkey**, or **Sign in with passkey** in the list layout) calls `handleWalletClick(wallet)`, and **Create a passkey** calls `handleWalletClick(wallet, { create: true })`. Forward `args` to `wallet.connect(args)` before any `await`, or Safari and iOS browsers drop the passkey prompt. Pass `status` (`{ walletId, pending, error }`) to disable the controls while the prompt is open and to show an error.
430
447
 
431
448
  ```jsx
432
- <WalletList onWalletSelected={() => closeMenu()} />
449
+ <PasskeyConnect
450
+ wallet={passkeyWallet}
451
+ handleWalletClick={(wallet, args) => wallet.connect(args)}
452
+ layout="list" // 'list' (default) | 'featured'
453
+ showLabel={true} // 'list' only: show the "Passkey" group label
454
+ />
433
455
  ```
434
456
 
457
+ In the list layout, label your own wallet group to match (the dialog uses "Wallets").
458
+
459
+
435
460
  ## Hooks
436
461
 
437
462
  ### useNfd
@@ -581,6 +606,74 @@ function App() {
581
606
 
582
607
  See the [Plugin Architecture Guide](PLUGINS.md) for full documentation.
583
608
 
609
+ ## Passkey Wallet
610
+
611
+ [`@txnlab/use-wallet-passkey`](https://github.com/TxnLab/use-wallet/tree/main/packages/wallets/passkey#readme) (use-wallet 5.2+) derives an app-scoped Falcon-1024 account from a passkey. Register it, and the connect dialog shows a Passkey group with **Sign in with passkey** and **Create a passkey**, above your other wallets. The [`passkeyLayout`](#passkey-layout) option can lead with a **Continue with passkey** button instead. A missing passkey and a cancelled prompt raise the same browser error, so creating stays a separate choice.
612
+
613
+ ```bash
614
+ npm install @txnlab/use-wallet-passkey
615
+ ```
616
+
617
+ ```tsx
618
+ import { lute } from '@galaxypay/use-wallet-lute'
619
+ import { pera } from '@perawallet/use-wallet-pera'
620
+ import { passkey } from '@txnlab/use-wallet-passkey'
621
+ import { WalletManager, WalletProvider } from '@txnlab/use-wallet-react'
622
+ import { WalletButton, WalletUIProvider } from '@txnlab/use-wallet-ui-react'
623
+ import { passkeyPlugin } from '@txnlab/use-wallet-ui-react/plugins/passkey'
624
+
625
+ const manager = new WalletManager({
626
+ wallets: [passkey({ session: true }), lute(), pera()],
627
+ })
628
+
629
+ const plugins = [passkeyPlugin({ session: true })]
630
+
631
+ function App() {
632
+ return (
633
+ <WalletProvider manager={manager}>
634
+ <WalletUIProvider plugins={plugins}>
635
+ <WalletButton />
636
+ </WalletUIProvider>
637
+ </WalletProvider>
638
+ )
639
+ }
640
+ ```
641
+
642
+ The connect dialog:
643
+
644
+ - Starts the passkey prompt straight from the tap, as Safari and iOS browsers require
645
+ - While the prompt is open, shows "Waiting for your passkey…" on the control the user pressed and dims the other
646
+ - Stays open on passkey errors and explains them: a cancelled or timed-out prompt (or no passkey found), an in-app browser (open the page in Safari or Chrome), or a passkey provider without the PRF extension (try another provider)
647
+
648
+ The connected wallet menu tags a passkey account with its domain (for example, `Passkey · localhost`), since the account belongs to the site that created it.
649
+
650
+ `passkeyPlugin()` adds to the connected wallet menu, only while the passkey wallet is active:
651
+
652
+ - **Back up account**: a dialog that reveals the account's 25-word recovery phrase after a fresh passkey prompt, and tells users to import it as a Falcon / quantum account (in Lute, Add account → Falcon25 account → Import; in Pera, the quantum import). It hides the phrase after a timeout and clears the clipboard when it closes, if it can.
653
+ - **Session status** (with `session: true`): Locked or Unlocked, with Unlock and Lock buttons
654
+ - **A note on whose account it is** (with `disclosure: true`): "This account is created by and for example.com. Keep only what you need here, and move larger amounts to a wallet you control." It shows until the user chooses **Got it**, once per account.
655
+
656
+ The plugin uses `useWalletManager()`, so it requires `@txnlab/use-wallet-react` 5.2 or later, which any app with the passkey wallet has. The main entry still supports v4.6+. See [Passkey Plugin](PLUGINS.md#passkey-plugin) for options, and the adapter's [security model](https://github.com/TxnLab/use-wallet/tree/main/packages/wallets/passkey#security-model) before you ship it.
657
+
658
+ For your own connect UI, `getPasskeyErrorMessage(error)` returns a message for a `PasskeyError`, or `null` for any other error.
659
+
660
+ ### Passkey layout
661
+
662
+ Choose how the connect dialog presents the passkey wallet with `passkeyLayout`:
663
+
664
+ - **`'list'`** (default): **Sign in with passkey** and **Create a passkey** rows in a "Passkey" group, above a "Wallets" group. For crypto-native apps where most users bring a wallet like Pera or Lute, and the passkey is one choice among many.
665
+ - **`'featured'`**: a full-width **Continue with passkey** button and a "New here? **Create a passkey**" link, then the other wallets under "or connect a wallet". For consumer apps where most users have no wallet, so the passkey is the main way in.
666
+
667
+ ```tsx
668
+ // App-wide
669
+ <WalletUIProvider passkeyLayout="featured">
670
+
671
+ // Per menu: a WalletButton or ConnectWalletMenu prop wins over the provider
672
+ <WalletButton passkeyLayout="list" />
673
+ ```
674
+
675
+ With only the passkey wallet configured, neither layout shows the other-wallets heading. Without a passkey wallet, the dialog is the same in both.
676
+
584
677
  ## License
585
678
 
586
679
  MIT
@@ -0,0 +1,2 @@
1
+ "use strict";const n="passkey";function a(e,s){return e??s??"list"}function i(e){return(e==null?void 0:e.id)===n}function t(e){if(!(e instanceof Error)||e.name!=="PasskeyError")return null;const{code:s}=e;return typeof s=="string"?s:null}function u(e){const s=t(e);if(!s)return null;switch(s){case"cancelled":return"The passkey request was cancelled or timed out, or no passkey was found.";case"unsupported":return"This browser can't use passkeys here. If you opened this page inside another app, open it in Safari or Chrome instead.";case"prf-unsupported":return"Your passkey provider doesn't support passkey accounts. Try a different provider, such as iCloud Keychain or Google Password Manager.";case"key-mismatch":return"This passkey belongs to a different account. Choose the passkey you used for this account.";default:return e.message||"The passkey request failed."}}function c(e){const r=(e==null?void 0:e.lastIndexOf(" · "))??-1;if(e&&r!==-1){const o=e.slice(r+3).trim();if(o)return o}return typeof location<"u"?location.hostname:""}exports.getPasskeyAccountDomain=c;exports.getPasskeyErrorCode=t;exports.getPasskeyErrorMessage=u;exports.isPasskeyWallet=i;exports.resolvePasskeyLayout=a;
2
+ //# sourceMappingURL=passkey-C8Dw6sNL.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passkey-C8Dw6sNL.cjs","sources":["../../../src/utils/passkey.ts"],"sourcesContent":["/** The wallet id registered by `passkey()` from `@txnlab/use-wallet-passkey` */\nexport const PASSKEY_WALLET_ID = 'passkey'\n\n/**\n * The `PasskeyAdapter` members this library uses. Typed structurally so the\n * library needs no dependency on `@txnlab/use-wallet-passkey`.\n */\nexport interface PasskeyWalletAdapter {\n /** True when the session key is unlocked */\n readonly isUnlocked: boolean\n /** Unlock the session with one passkey prompt */\n unlock: () => Promise<void>\n /** Drop the session key; the account stays connected */\n lock: () => void\n /** Return the account's recovery phrase after a fresh passkey prompt */\n exportRecoveryPhrase: () => Promise<{ scheme: 'falcon-1024'; phrase: string }>\n}\n\n/** The status of a passkey connect request, shown under the passkey section */\nexport interface PasskeyConnectStatus {\n walletId: string\n pending: boolean\n error: string | null\n /** Which control started the request, so it can show the pending state */\n action?: 'sign-in' | 'create'\n}\n\n/**\n * How the connect dialog presents a passkey wallet:\n * - `'list'` (default): \"Sign in with passkey\" and \"Create a passkey\" rows in\n * a Passkey group above a Wallets group. Suits apps where most users bring\n * a wallet.\n * - `'featured'`: a \"Continue with passkey\" button leads, with the other\n * wallets below. Suits apps where most users have no wallet.\n */\nexport type PasskeyConnectLayout = 'featured' | 'list'\n\n/** A menu's own layout wins over the provider's; the default is `'list'` */\nexport function resolvePasskeyLayout(\n menuLayout?: PasskeyConnectLayout,\n providerLayout?: PasskeyConnectLayout,\n): PasskeyConnectLayout {\n return menuLayout ?? providerLayout ?? 'list'\n}\n\n/** Arguments for connecting the passkey wallet */\nexport interface PasskeyConnectArgs {\n /** Create a new passkey (and account) instead of signing in */\n create?: boolean\n}\n\nexport function isPasskeyWallet(\n wallet: { id: string } | null | undefined,\n): boolean {\n return wallet?.id === PASSKEY_WALLET_ID\n}\n\n/** Returns a `PasskeyError`'s code, or null for any other error */\nexport function getPasskeyErrorCode(error: unknown): string | null {\n if (!(error instanceof Error) || error.name !== 'PasskeyError') return null\n const { code } = error as Error & { code?: unknown }\n return typeof code === 'string' ? code : null\n}\n\n/**\n * Returns a message to show users for a `PasskeyError`, or null for any other\n * error.\n */\nexport function getPasskeyErrorMessage(error: unknown): string | null {\n const code = getPasskeyErrorCode(error)\n if (!code) return null\n\n switch (code) {\n case 'cancelled':\n return 'The passkey request was cancelled or timed out, or no passkey was found.'\n case 'unsupported':\n return \"This browser can't use passkeys here. If you opened this page inside another app, open it in Safari or Chrome instead.\"\n case 'prf-unsupported':\n return \"Your passkey provider doesn't support passkey accounts. Try a different provider, such as iCloud Keychain or Google Password Manager.\"\n case 'key-mismatch':\n return 'This passkey belongs to a different account. Choose the passkey you used for this account.'\n default:\n return (error as Error).message || 'The passkey request failed.'\n }\n}\n\n/**\n * The domain a passkey account belongs to. The adapter names the account\n * `Passkey · <domain>`; fall back to the page's hostname.\n */\nexport function getPasskeyAccountDomain(accountName?: string | null): string {\n const separator = ' · '\n const index = accountName?.lastIndexOf(separator) ?? -1\n if (accountName && index !== -1) {\n const domain = accountName.slice(index + separator.length).trim()\n if (domain) return domain\n }\n return typeof location !== 'undefined' ? location.hostname : ''\n}\n"],"names":["PASSKEY_WALLET_ID","resolvePasskeyLayout","menuLayout","providerLayout","isPasskeyWallet","wallet","getPasskeyErrorCode","error","code","getPasskeyErrorMessage","getPasskeyAccountDomain","accountName","index","domain"],"mappings":"aACO,MAAMA,EAAoB,UAqC1B,SAASC,EACdC,EACAC,EACsB,CACtB,OAAOD,GAAcC,GAAkB,MACzC,CAQO,SAASC,EACdC,EACS,CACT,OAAOA,GAAA,YAAAA,EAAQ,MAAOL,CACxB,CAGO,SAASM,EAAoBC,EAA+B,CACjE,GAAI,EAAEA,aAAiB,QAAUA,EAAM,OAAS,eAAgB,OAAO,KACvE,KAAM,CAAE,KAAAC,GAASD,EACjB,OAAO,OAAOC,GAAS,SAAWA,EAAO,IAC3C,CAMO,SAASC,EAAuBF,EAA+B,CACpE,MAAMC,EAAOF,EAAoBC,CAAK,EACtC,GAAI,CAACC,EAAM,OAAO,KAElB,OAAQA,EAAA,CACN,IAAK,YACH,MAAO,2EACT,IAAK,cACH,MAAO,yHACT,IAAK,kBACH,MAAO,wIACT,IAAK,eACH,MAAO,6FACT,QACE,OAAQD,EAAgB,SAAW,6BAAA,CAEzC,CAMO,SAASG,EAAwBC,EAAqC,CAE3E,MAAMC,GAAQD,GAAA,YAAAA,EAAa,YAAY,SAAc,GACrD,GAAIA,GAAeC,IAAU,GAAI,CAC/B,MAAMC,EAASF,EAAY,MAAMC,EAAQ,CAAgB,EAAE,KAAA,EAC3D,GAAIC,EAAQ,OAAOA,CACrB,CACA,OAAO,OAAO,SAAa,IAAc,SAAS,SAAW,EAC/D"}