shogun-button-react 6.3.7 β†’ 6.4.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.
package/README.md CHANGED
@@ -9,7 +9,9 @@ A comprehensive React component library for seamless integration of Shogun authe
9
9
  - πŸš€ **Easy Integration** - Simple setup with minimal configuration
10
10
  - 🎨 **Customizable UI** - Modern, responsive design with dark mode support
11
11
  - πŸ”’ **Multi-Authentication** - Support for Password, MetaMask, WebAuthn, Nostr, and ZK-Proof
12
+ - πŸ›‘οΈ **WebAuthn Recovery** - Restore hardware credentials on new devices with saved seed phrases
12
13
  - πŸ”‘ **Account Management** - Export/import Gun pairs for account backup and recovery
14
+ - πŸ•΅οΈ **ZK-Proof Trapdoor Handoff** - Display and copy the generated trapdoor during signup to keep anonymous identities portable
13
15
  - πŸ“± **Responsive Design** - Works seamlessly across all device sizes
14
16
  - 🌍 **TypeScript Support** - Full type safety and IntelliSense support
15
17
  - πŸ”Œ **Plugin System** - Advanced Gun operations with custom hooks
@@ -170,6 +172,21 @@ const { core, options } = shogunConnector({
170
172
  });
171
173
  ```
172
174
 
175
+ ## πŸ”‘ Recovery Flows
176
+
177
+ ### WebAuthn Multi-Device Restore
178
+
179
+ - Users now see a **Restore with Recovery Code** option when choosing WebAuthn login.
180
+ - Enter the username plus the stored seed phrase to recreate the credential on a new browser.
181
+ - The button calls `webauthnPlugin.signUp(username, { seedPhrase, generateSeedPhrase: false })` behind the scenes, leveraging the core plugin’s `importFromSeed` flow.
182
+ - After a successful restore the seed phrase is shown one more time so the user can double-check or re-copy it before the modal closes.
183
+
184
+ ### ZK-Proof Trapdoor Delivery
185
+
186
+ - Upon successful `zkproof` signup, the modal switches to a confirmation screen that displays the generated trapdoor (also returned as `seedPhrase`).
187
+ - A **Copy Trapdoor** helper copies the phrase to the clipboard, with inline feedback when the copy succeeds.
188
+ - The user must acknowledge with **I Saved My Trapdoor** before returning to the main UI, reducing the risk of losing the anonymous identity.
189
+
173
190
  ## 🎯 API Reference
174
191
 
175
192
  ### ShogunButtonProvider
@@ -38,7 +38,6 @@ export function ShogunButtonProvider({ children, core, options, onLoginSuccess,
38
38
  const [isLoggedIn, setIsLoggedIn] = useState(false);
39
39
  const [userPub, setUserPub] = useState(null);
40
40
  const [username, setUsername] = useState(null);
41
- const [pendingSignupData, setPendingSignupData] = useState(null);
42
41
  // Effetto per gestire l'inizializzazione e pulizia
43
42
  useEffect(() => {
44
43
  var _a, _b;
@@ -221,7 +220,7 @@ export function ShogunButtonProvider({ children, core, options, onLoginSuccess,
221
220
  };
222
221
  // Unified signup
223
222
  const signUp = async (method, ...args) => {
224
- var _a, _b;
223
+ var _a, _b, _c;
225
224
  try {
226
225
  if (!core) {
227
226
  throw new Error("SDK not initialized");
@@ -253,18 +252,39 @@ export function ShogunButtonProvider({ children, core, options, onLoginSuccess,
253
252
  throw error;
254
253
  }
255
254
  break;
256
- case "webauthn":
257
- username = args[0];
255
+ case "webauthn": {
256
+ username = typeof args[0] === "string" ? args[0].trim() : "";
257
+ const webauthnOptions = args.length > 1 && typeof args[1] === "object" && args[1] !== null
258
+ ? args[1]
259
+ : {};
260
+ if (!username) {
261
+ throw new Error("Username is required for WebAuthn registration");
262
+ }
258
263
  if (isShogunCore(core)) {
259
264
  const webauthn = core.getPlugin("webauthn");
260
265
  if (!webauthn)
261
266
  throw new Error("WebAuthn plugin not available");
262
- result = await webauthn.signUp(username, { generateSeedPhrase: true });
267
+ const pluginOptions = {};
268
+ if (webauthnOptions.seedPhrase) {
269
+ pluginOptions.seedPhrase = webauthnOptions.seedPhrase.trim();
270
+ pluginOptions.generateSeedPhrase =
271
+ (_a = webauthnOptions.generateSeedPhrase) !== null && _a !== void 0 ? _a : false;
272
+ }
273
+ else if (typeof webauthnOptions.generateSeedPhrase === "boolean") {
274
+ pluginOptions.generateSeedPhrase =
275
+ webauthnOptions.generateSeedPhrase;
276
+ }
277
+ if (pluginOptions.generateSeedPhrase === undefined &&
278
+ !pluginOptions.seedPhrase) {
279
+ pluginOptions.generateSeedPhrase = true;
280
+ }
281
+ result = await webauthn.signUp(username, pluginOptions);
263
282
  }
264
283
  else {
265
284
  throw new Error("WebAuthn requires ShogunCore");
266
285
  }
267
286
  break;
287
+ }
268
288
  case "web3":
269
289
  if (isShogunCore(core)) {
270
290
  const web3 = core.getPlugin("web3");
@@ -321,7 +341,7 @@ export function ShogunButtonProvider({ children, core, options, onLoginSuccess,
321
341
  if (result.success) {
322
342
  let userPub = result.userPub || "";
323
343
  if (!userPub && isShogunCore(core)) {
324
- userPub = ((_b = (_a = core.gun.user()) === null || _a === void 0 ? void 0 : _a.is) === null || _b === void 0 ? void 0 : _b.pub) || "";
344
+ userPub = ((_c = (_b = core.gun.user()) === null || _b === void 0 ? void 0 : _b.is) === null || _c === void 0 ? void 0 : _c.pub) || "";
325
345
  }
326
346
  const displayName = result.alias || username || userPub.slice(0, 8) + "...";
327
347
  setIsLoggedIn(true);
@@ -333,12 +353,7 @@ export function ShogunButtonProvider({ children, core, options, onLoginSuccess,
333
353
  seedPhrase: result.seedPhrase,
334
354
  authMethod: authMethod,
335
355
  };
336
- if (authMethod === "zkproof" && onSignupSuccess) {
337
- setPendingSignupData(signupPayload);
338
- }
339
- else {
340
- onSignupSuccess === null || onSignupSuccess === void 0 ? void 0 : onSignupSuccess(signupPayload);
341
- }
356
+ onSignupSuccess === null || onSignupSuccess === void 0 ? void 0 : onSignupSuccess(signupPayload);
342
357
  }
343
358
  else {
344
359
  onError === null || onError === void 0 ? void 0 : onError(result.error);
@@ -358,7 +373,6 @@ export function ShogunButtonProvider({ children, core, options, onLoginSuccess,
358
373
  setIsLoggedIn(false);
359
374
  setUserPub(null);
360
375
  setUsername(null);
361
- setPendingSignupData(null);
362
376
  };
363
377
  // Implementazione del metodo setProvider
364
378
  const setProvider = (provider) => {
@@ -472,13 +486,8 @@ export function ShogunButtonProvider({ children, core, options, onLoginSuccess,
472
486
  const gunPlugin = null;
473
487
  // Plugin hooks removed - GunAdvancedPlugin no longer available
474
488
  const pluginHooks = {};
475
- const completePendingSignup = React.useCallback(() => {
476
- if (pendingSignupData) {
477
- const dataToEmit = pendingSignupData;
478
- setPendingSignupData(null);
479
- onSignupSuccess === null || onSignupSuccess === void 0 ? void 0 : onSignupSuccess(dataToEmit);
480
- }
481
- }, [pendingSignupData, onSignupSuccess]);
489
+ const completePendingSignup = React.useCallback(() => { }, []);
490
+ const hasPendingSignup = false;
482
491
  // Create a properly typed context value
483
492
  const contextValue = React.useMemo(() => ({
484
493
  core,
@@ -497,7 +506,7 @@ export function ShogunButtonProvider({ children, core, options, onLoginSuccess,
497
506
  setProvider,
498
507
  gunPlugin,
499
508
  completePendingSignup,
500
- hasPendingSignup: Boolean(pendingSignupData),
509
+ hasPendingSignup,
501
510
  put: async (path, data) => {
502
511
  if (isShogunCore(core)) {
503
512
  if (!core.gun)
@@ -556,7 +565,6 @@ export function ShogunButtonProvider({ children, core, options, onLoginSuccess,
556
565
  importGunPair,
557
566
  gunPlugin,
558
567
  pluginHooks,
559
- pendingSignupData,
560
568
  completePendingSignup,
561
569
  ]);
562
570
  // Provide the context value to children
@@ -607,7 +615,7 @@ const ExportIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org
607
615
  // Component for Shogun login button
608
616
  export const ShogunButton = (() => {
609
617
  const Button = () => {
610
- const { isLoggedIn, username, logout, login, signUp, core, options, exportGunPair, importGunPair, hasPlugin, completePendingSignup, hasPendingSignup, } = useShogun();
618
+ const { isLoggedIn, username, logout, login, signUp, core, options, exportGunPair, importGunPair, hasPlugin, } = useShogun();
611
619
  // Form states
612
620
  const [modalIsOpen, setModalIsOpen] = useState(false);
613
621
  const [formUsername, setFormUsername] = useState("");
@@ -629,10 +637,11 @@ export const ShogunButton = (() => {
629
637
  const [showCopySuccess, setShowCopySuccess] = useState(false);
630
638
  const [showImportSuccess, setShowImportSuccess] = useState(false);
631
639
  const [zkTrapdoor, setZkTrapdoor] = useState("");
632
- const [zkGeneratedTrapdoor, setZkGeneratedTrapdoor] = useState("");
640
+ const [zkSignupTrapdoor, setZkSignupTrapdoor] = useState("");
641
+ const [showZkTrapdoorCopySuccess, setShowZkTrapdoorCopySuccess] = useState(false);
633
642
  const [webauthnSeedPhrase, setWebauthnSeedPhrase] = useState("");
643
+ const [webauthnRecoverySeed, setWebauthnRecoverySeed] = useState("");
634
644
  const dropdownRef = useRef(null);
635
- const [pendingZkConfirmation, setPendingZkConfirmation] = useState(false);
636
645
  // Handle click outside to close dropdown
637
646
  useEffect(() => {
638
647
  const handleClickOutside = (event) => {
@@ -768,6 +777,41 @@ export const ShogunButton = (() => {
768
777
  }
769
778
  setAuthView("webauthn-username");
770
779
  };
780
+ const handleWebauthnImport = async () => {
781
+ setError("");
782
+ setLoading(true);
783
+ try {
784
+ const username = formUsername.trim();
785
+ const recoveryCode = webauthnRecoverySeed.trim();
786
+ if (!username) {
787
+ throw new Error("Please enter your username");
788
+ }
789
+ if (!recoveryCode) {
790
+ throw new Error("Please enter your recovery code");
791
+ }
792
+ if (!isShogunCore(core)) {
793
+ throw new Error("WebAuthn recovery requires ShogunCore");
794
+ }
795
+ const result = await signUp("webauthn", username, {
796
+ seedPhrase: recoveryCode,
797
+ generateSeedPhrase: false,
798
+ });
799
+ if (!result || !result.success) {
800
+ throw new Error((result === null || result === void 0 ? void 0 : result.error) || "Failed to restore account");
801
+ }
802
+ const seedToDisplay = result.seedPhrase || recoveryCode;
803
+ setWebauthnSeedPhrase(seedToDisplay);
804
+ setWebauthnRecoverySeed("");
805
+ setShowCopySuccess(false);
806
+ setAuthView("webauthn-signup-result");
807
+ }
808
+ catch (e) {
809
+ setError(e.message || "Failed to restore WebAuthn account");
810
+ }
811
+ finally {
812
+ setLoading(false);
813
+ }
814
+ };
771
815
  const handleZkProofAuth = () => {
772
816
  if (!hasPlugin("zkproof")) {
773
817
  setError("ZK-Proof plugin not available");
@@ -803,14 +847,18 @@ export const ShogunButton = (() => {
803
847
  setLoading(true);
804
848
  try {
805
849
  const result = await signUp("zkproof");
806
- if (result && result.success && result.seedPhrase) {
807
- // Show the trapdoor to the user - CRITICAL for account recovery!
808
- setZkGeneratedTrapdoor(result.seedPhrase);
850
+ if (!result || !result.success) {
851
+ throw new Error((result === null || result === void 0 ? void 0 : result.error) || "ZK-Proof signup failed");
852
+ }
853
+ const trapdoorValue = result.seedPhrase || result.trapdoor || "";
854
+ if (trapdoorValue) {
855
+ setZkSignupTrapdoor(trapdoorValue);
856
+ setShowZkTrapdoorCopySuccess(false);
809
857
  setAuthView("zkproof-signup-result");
810
- setPendingZkConfirmation(true);
811
858
  }
812
- else if (result && !result.success) {
813
- throw new Error(result.error || "ZK-Proof signup failed");
859
+ else {
860
+ setAuthView("options");
861
+ setModalIsOpen(false);
814
862
  }
815
863
  }
816
864
  catch (e) {
@@ -930,9 +978,10 @@ export const ShogunButton = (() => {
930
978
  setShowImportSuccess(false);
931
979
  setRecoveredHint("");
932
980
  setZkTrapdoor("");
933
- setZkGeneratedTrapdoor("");
981
+ setZkSignupTrapdoor("");
982
+ setShowZkTrapdoorCopySuccess(false);
934
983
  setWebauthnSeedPhrase("");
935
- setPendingZkConfirmation(false);
984
+ setWebauthnRecoverySeed("");
936
985
  };
937
986
  const openModal = () => {
938
987
  resetForm();
@@ -940,17 +989,12 @@ export const ShogunButton = (() => {
940
989
  setModalIsOpen(true);
941
990
  };
942
991
  const closeModal = () => {
943
- if (pendingZkConfirmation || hasPendingSignup) {
944
- setError("Please save your trapdoor and confirm before leaving this screen.");
945
- return;
946
- }
992
+ setError("");
947
993
  setModalIsOpen(false);
948
994
  };
949
995
  const finalizeZkProofSignup = () => {
950
- completePendingSignup();
951
996
  setError("");
952
- setPendingZkConfirmation(false);
953
- setAuthView("options");
997
+ resetForm();
954
998
  setModalIsOpen(false);
955
999
  };
956
1000
  const toggleMode = () => {
@@ -1063,7 +1107,52 @@ export const ShogunButton = (() => {
1063
1107
  React.createElement("input", { type: "text", id: "username", value: formUsername, onChange: (e) => setFormUsername(e.target.value), disabled: loading, required: true, placeholder: "Enter your username", autoFocus: true })),
1064
1108
  React.createElement("button", { type: "button", className: "shogun-submit-button", onClick: () => handleAuth("webauthn", formUsername), disabled: loading || !formUsername.trim() }, loading ? "Processing..." : `Continue with WebAuthn`),
1065
1109
  React.createElement("div", { className: "shogun-form-footer" },
1066
- React.createElement("button", { type: "button", className: "shogun-back-button", onClick: () => setAuthView("options"), disabled: loading }, "\u2190 Back to Options"))));
1110
+ React.createElement("button", { type: "button", className: "shogun-back-button", onClick: () => setAuthView("options"), disabled: loading }, "\u2190 Back to Options"),
1111
+ formMode === "login" && (React.createElement("button", { type: "button", className: "shogun-toggle-mode", onClick: () => setAuthView("webauthn-recovery"), disabled: loading }, "Restore with Recovery Code")))));
1112
+ const renderWebauthnRecoveryForm = () => (React.createElement("div", { className: "shogun-auth-form" },
1113
+ React.createElement("h3", null, "Restore WebAuthn Account"),
1114
+ React.createElement("div", { style: {
1115
+ backgroundColor: "#fef3c7",
1116
+ padding: "12px",
1117
+ borderRadius: "8px",
1118
+ marginBottom: "16px",
1119
+ border: "1px solid #f59e0b",
1120
+ } },
1121
+ React.createElement("p", { style: {
1122
+ fontSize: "14px",
1123
+ color: "#92400e",
1124
+ margin: "0",
1125
+ fontWeight: "500",
1126
+ } }, "\u26A0\uFE0F Recovery Required"),
1127
+ React.createElement("p", { style: {
1128
+ fontSize: "13px",
1129
+ color: "#a16207",
1130
+ margin: "4px 0 0 0",
1131
+ } }, "Enter the username and recovery code saved during signup to restore access on this device.")),
1132
+ React.createElement("div", { className: "shogun-form-group" },
1133
+ React.createElement("label", { htmlFor: "recoveryUsername" },
1134
+ React.createElement(UserIcon, null),
1135
+ React.createElement("span", null, "Username")),
1136
+ React.createElement("input", { type: "text", id: "recoveryUsername", value: formUsername, onChange: (e) => setFormUsername(e.target.value), disabled: loading, placeholder: "Enter your username", autoFocus: true })),
1137
+ React.createElement("div", { className: "shogun-form-group" },
1138
+ React.createElement("label", { htmlFor: "recoverySeed" },
1139
+ React.createElement(KeyIcon, null),
1140
+ React.createElement("span", null, "Recovery Code")),
1141
+ React.createElement("textarea", { id: "recoverySeed", value: webauthnRecoverySeed, onChange: (e) => setWebauthnRecoverySeed(e.target.value), disabled: loading, placeholder: "Enter your WebAuthn seed phrase...", rows: 4, style: {
1142
+ fontFamily: "monospace",
1143
+ fontSize: "12px",
1144
+ width: "100%",
1145
+ padding: "8px",
1146
+ border: "1px solid #f59e0b",
1147
+ borderRadius: "4px",
1148
+ backgroundColor: "#fffbeb",
1149
+ } })),
1150
+ React.createElement("button", { type: "button", className: "shogun-submit-button", onClick: handleWebauthnImport, disabled: loading }, loading ? "Restoring..." : "Restore Account"),
1151
+ React.createElement("div", { className: "shogun-form-footer" },
1152
+ React.createElement("button", { type: "button", className: "shogun-back-button", onClick: () => {
1153
+ setError("");
1154
+ setAuthView("webauthn-username");
1155
+ }, disabled: loading }, "\u2190 Back to WebAuthn"))));
1067
1156
  const renderRecoveryForm = () => (React.createElement("div", { className: "shogun-auth-form" },
1068
1157
  React.createElement("div", { className: "shogun-form-group" },
1069
1158
  React.createElement("label", { htmlFor: "username" },
@@ -1176,8 +1265,8 @@ export const ShogunButton = (() => {
1176
1265
  React.createElement("button", { type: "button", className: "shogun-submit-button", onClick: handleZkProofLogin, disabled: loading || !zkTrapdoor.trim() }, loading ? "Processing..." : "Login Anonymously"),
1177
1266
  React.createElement("div", { className: "shogun-form-footer" },
1178
1267
  React.createElement("button", { className: "shogun-toggle-mode", onClick: () => setAuthView("options"), disabled: loading }, "Back to Login Options"))));
1179
- const renderWebauthnSignupResult = () => (React.createElement("div", { className: "shogun-auth-form" },
1180
- React.createElement("h3", null, "WebAuthn Account Created!"),
1268
+ const renderZkProofSignupResult = () => (React.createElement("div", { className: "shogun-auth-form" },
1269
+ React.createElement("h3", null, "ZK-Proof Account Created!"),
1181
1270
  React.createElement("div", { style: {
1182
1271
  backgroundColor: "#fef3c7",
1183
1272
  padding: "12px",
@@ -1190,11 +1279,11 @@ export const ShogunButton = (() => {
1190
1279
  color: "#92400e",
1191
1280
  margin: "0",
1192
1281
  fontWeight: "500",
1193
- } }, "\u26A0\uFE0F Important: Save Your Recovery Code"),
1194
- React.createElement("p", { style: { fontSize: "13px", color: "#a16207", margin: "4px 0 0 0" } }, "This seed phrase lets you add new devices or recover your WebAuthn account. Keep it private and store it securely.")),
1282
+ } }, "\u26A0\uFE0F Important: Save Your Trapdoor"),
1283
+ React.createElement("p", { style: { fontSize: "13px", color: "#a16207", margin: "4px 0 0 0" } }, "This trapdoor lets you restore your anonymous identity on new devices. Store it securely and never share it.")),
1195
1284
  React.createElement("div", { className: "shogun-form-group" },
1196
- React.createElement("label", null, "Your WebAuthn Recovery Code (Seed Phrase):"),
1197
- React.createElement("textarea", { value: webauthnSeedPhrase, readOnly: true, rows: 4, style: {
1285
+ React.createElement("label", null, "Your Trapdoor (Recovery Phrase):"),
1286
+ React.createElement("textarea", { value: zkSignupTrapdoor, readOnly: true, rows: 4, style: {
1198
1287
  fontFamily: "monospace",
1199
1288
  fontSize: "12px",
1200
1289
  width: "100%",
@@ -1204,26 +1293,29 @@ export const ShogunButton = (() => {
1204
1293
  backgroundColor: "#fffbeb",
1205
1294
  } }),
1206
1295
  React.createElement("button", { type: "button", className: "shogun-submit-button", style: { marginTop: "8px" }, onClick: async () => {
1207
- if (navigator.clipboard && webauthnSeedPhrase) {
1208
- await navigator.clipboard.writeText(webauthnSeedPhrase);
1209
- setShowCopySuccess(true);
1210
- setTimeout(() => setShowCopySuccess(false), 3000);
1296
+ if (!zkSignupTrapdoor) {
1297
+ return;
1211
1298
  }
1212
- }, disabled: !webauthnSeedPhrase }, showCopySuccess ? "βœ… Copied!" : "πŸ“‹ Copy Recovery Code"),
1213
- !navigator.clipboard && (React.createElement("p", { style: { fontSize: "12px", color: "#666", marginTop: "8px" } }, "\u26A0\uFE0F Please manually copy the code above for safekeeping."))),
1214
- React.createElement("div", { style: {
1215
- backgroundColor: "#dcfce7",
1216
- color: "#166534",
1217
- padding: "12px",
1218
- borderRadius: "8px",
1219
- marginTop: "16px",
1220
- fontSize: "14px",
1221
- border: "1px solid #22c55e",
1222
- textAlign: "center",
1223
- } }, "\u2705 You're now logged in with WebAuthn!"),
1224
- React.createElement("button", { type: "button", className: "shogun-submit-button", style: { marginTop: "16px" }, onClick: finalizeZkProofSignup }, "Close and Start Using App")));
1225
- const renderZkProofSignupResult = () => (React.createElement("div", { className: "shogun-auth-form" },
1226
- React.createElement("h3", null, "ZK-Proof Account Created!"),
1299
+ try {
1300
+ if (navigator.clipboard) {
1301
+ await navigator.clipboard.writeText(zkSignupTrapdoor);
1302
+ setShowZkTrapdoorCopySuccess(true);
1303
+ setTimeout(() => setShowZkTrapdoorCopySuccess(false), 3000);
1304
+ }
1305
+ }
1306
+ catch (copyError) {
1307
+ console.warn("Failed to copy trapdoor:", copyError);
1308
+ }
1309
+ }, disabled: !zkSignupTrapdoor }, "Copy Trapdoor"),
1310
+ showZkTrapdoorCopySuccess && (React.createElement("p", { style: {
1311
+ color: "#047857",
1312
+ fontSize: "12px",
1313
+ marginTop: "6px",
1314
+ } }, "Trapdoor copied to clipboard!"))),
1315
+ React.createElement("div", { className: "shogun-form-footer" },
1316
+ React.createElement("button", { type: "button", className: "shogun-submit-button", onClick: finalizeZkProofSignup }, "I Saved My Trapdoor"))));
1317
+ const renderWebauthnSignupResult = () => (React.createElement("div", { className: "shogun-auth-form" },
1318
+ React.createElement("h3", null, "WebAuthn Account Created!"),
1227
1319
  React.createElement("div", { style: {
1228
1320
  backgroundColor: "#fef3c7",
1229
1321
  padding: "12px",
@@ -1236,11 +1328,11 @@ export const ShogunButton = (() => {
1236
1328
  color: "#92400e",
1237
1329
  margin: "0",
1238
1330
  fontWeight: "500",
1239
- } }, "\u26A0\uFE0F CRITICAL: Save Your Trapdoor!"),
1240
- React.createElement("p", { style: { fontSize: "13px", color: "#a16207", margin: "4px 0 0 0" } }, "This is your ONLY way to recover your anonymous account. Save it in a secure location. If you lose it, you will lose access to your account permanently.")),
1331
+ } }, "\u26A0\uFE0F Important: Save Your Recovery Code"),
1332
+ React.createElement("p", { style: { fontSize: "13px", color: "#a16207", margin: "4px 0 0 0" } }, "This seed phrase lets you add new devices or recover your WebAuthn account. Keep it private and store it securely.")),
1241
1333
  React.createElement("div", { className: "shogun-form-group" },
1242
- React.createElement("label", null, "Your Trapdoor (Recovery Phrase):"),
1243
- React.createElement("textarea", { value: zkGeneratedTrapdoor, readOnly: true, rows: 4, style: {
1334
+ React.createElement("label", null, "Your WebAuthn Recovery Code (Seed Phrase):"),
1335
+ React.createElement("textarea", { value: webauthnSeedPhrase, readOnly: true, rows: 4, style: {
1244
1336
  fontFamily: "monospace",
1245
1337
  fontSize: "12px",
1246
1338
  width: "100%",
@@ -1250,13 +1342,13 @@ export const ShogunButton = (() => {
1250
1342
  backgroundColor: "#fffbeb",
1251
1343
  } }),
1252
1344
  React.createElement("button", { type: "button", className: "shogun-submit-button", style: { marginTop: "8px" }, onClick: async () => {
1253
- if (navigator.clipboard) {
1254
- await navigator.clipboard.writeText(zkGeneratedTrapdoor);
1345
+ if (navigator.clipboard && webauthnSeedPhrase) {
1346
+ await navigator.clipboard.writeText(webauthnSeedPhrase);
1255
1347
  setShowCopySuccess(true);
1256
1348
  setTimeout(() => setShowCopySuccess(false), 3000);
1257
1349
  }
1258
- } }, showCopySuccess ? "βœ… Copied!" : "πŸ“‹ Copy Trapdoor"),
1259
- !navigator.clipboard && (React.createElement("p", { style: { fontSize: "12px", color: "#666", marginTop: "8px" } }, "\u26A0\uFE0F Please manually copy the trapdoor above."))),
1350
+ }, disabled: !webauthnSeedPhrase }, showCopySuccess ? "βœ… Copied!" : "πŸ“‹ Copy Recovery Code"),
1351
+ !navigator.clipboard && (React.createElement("p", { style: { fontSize: "12px", color: "#666", marginTop: "8px" } }, "\u26A0\uFE0F Please manually copy the code above for safekeeping."))),
1260
1352
  React.createElement("div", { style: {
1261
1353
  backgroundColor: "#dcfce7",
1262
1354
  color: "#166534",
@@ -1266,7 +1358,7 @@ export const ShogunButton = (() => {
1266
1358
  fontSize: "14px",
1267
1359
  border: "1px solid #22c55e",
1268
1360
  textAlign: "center",
1269
- } }, "\u2705 You're now logged in anonymously!"),
1361
+ } }, "\u2705 You're now logged in with WebAuthn!"),
1270
1362
  React.createElement("button", { type: "button", className: "shogun-submit-button", style: { marginTop: "16px" }, onClick: finalizeZkProofSignup }, "Close and Start Using App")));
1271
1363
  const renderImportForm = () => (React.createElement("div", { className: "shogun-auth-form" },
1272
1364
  React.createElement("h3", null, "Import Gun Pair"),
@@ -1327,13 +1419,7 @@ export const ShogunButton = (() => {
1327
1419
  React.createElement("button", { className: "shogun-connect-button", onClick: openModal },
1328
1420
  React.createElement(WalletIcon, null),
1329
1421
  React.createElement("span", null, "Login / Sign Up")),
1330
- modalIsOpen && (React.createElement("div", { className: "shogun-modal-overlay", onClick: () => {
1331
- if (pendingZkConfirmation || hasPendingSignup) {
1332
- setError("Please copy your trapdoor and press Continue before closing.");
1333
- return;
1334
- }
1335
- closeModal();
1336
- } },
1422
+ modalIsOpen && (React.createElement("div", { className: "shogun-modal-overlay", onClick: closeModal },
1337
1423
  React.createElement("div", { className: "shogun-modal", onClick: (e) => e.stopPropagation() },
1338
1424
  React.createElement("div", { className: "shogun-modal-header" },
1339
1425
  React.createElement("h2", null, authView === "recover"
@@ -1348,12 +1434,10 @@ export const ShogunButton = (() => {
1348
1434
  ? "WebAuthn"
1349
1435
  : authView === "zkproof-login"
1350
1436
  ? "ZK-Proof Login"
1351
- : authView === "zkproof-signup-result"
1352
- ? "ZK-Proof Account"
1353
- : formMode === "login"
1354
- ? "Login"
1355
- : "Sign Up"),
1356
- React.createElement("button", { className: "shogun-close-button", onClick: closeModal, "aria-label": "Close", disabled: pendingZkConfirmation || hasPendingSignup },
1437
+ : formMode === "login"
1438
+ ? "Login"
1439
+ : "Sign Up"),
1440
+ React.createElement("button", { className: "shogun-close-button", onClick: closeModal, "aria-label": "Close" },
1357
1441
  React.createElement(CloseIcon, null))),
1358
1442
  React.createElement("div", { className: "shogun-modal-content" },
1359
1443
  error && React.createElement("div", { className: "shogun-error-message" }, error),
@@ -1372,6 +1456,8 @@ export const ShogunButton = (() => {
1372
1456
  authView === "import" && renderImportForm(),
1373
1457
  authView === "webauthn-username" &&
1374
1458
  renderWebAuthnUsernameForm(),
1459
+ authView === "webauthn-recovery" &&
1460
+ renderWebauthnRecoveryForm(),
1375
1461
  authView === "webauthn-signup-result" &&
1376
1462
  renderWebauthnSignupResult(),
1377
1463
  authView === "zkproof-login" && renderZkProofLoginForm(),