shogun-button-react 1.0.0 → 1.3.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.
@@ -1,6 +1,6 @@
1
- import React, { useContext, useState, createContext, useEffect } from "react";
1
+ import React, { useContext, useState, createContext, useEffect, useRef } from "react";
2
2
  import { Observable } from "rxjs";
3
- import "../types.js"; // Import type file to extend definitions
3
+ import "../types/index.js"; // Import type file to extend definitions
4
4
  import "../styles/index.css";
5
5
  // Default context
6
6
  const defaultShogunContext = {
@@ -16,6 +16,8 @@ const defaultShogunContext = {
16
16
  setProvider: () => false,
17
17
  hasPlugin: () => false,
18
18
  getPlugin: () => undefined,
19
+ exportGunPair: async () => "",
20
+ importGunPair: async () => false,
19
21
  };
20
22
  // Create context using React's createContext directly
21
23
  const ShogunContext = createContext(defaultShogunContext);
@@ -24,19 +26,48 @@ export const useShogun = () => useContext(ShogunContext);
24
26
  // Provider component
25
27
  export function ShogunButtonProvider({ children, sdk, options, onLoginSuccess, onSignupSuccess, onError, }) {
26
28
  // Use React's useState directly
27
- const [isLoggedIn, setIsLoggedIn] = useState((sdk === null || sdk === void 0 ? void 0 : sdk.isLoggedIn()) || false);
29
+ const [isLoggedIn, setIsLoggedIn] = useState(false);
28
30
  const [userPub, setUserPub] = useState(null);
29
31
  const [username, setUsername] = useState(null);
30
32
  // Effetto per gestire l'inizializzazione e pulizia
31
33
  useEffect(() => {
32
- // Controlla se l'utente è già autenticato all'avvio
33
- if (sdk === null || sdk === void 0 ? void 0 : sdk.isLoggedIn()) {
34
- setIsLoggedIn(true);
34
+ var _a, _b;
35
+ if (!sdk)
36
+ return;
37
+ const handleLogin = (authResult) => {
38
+ var _a, _b;
39
+ const pub = authResult.pub || ((_b = (_a = sdk.gun.user()) === null || _a === void 0 ? void 0 : _a.is) === null || _b === void 0 ? void 0 : _b.pub);
40
+ if (pub) {
41
+ setIsLoggedIn(true);
42
+ setUserPub(pub);
43
+ setUsername(authResult.alias || pub.slice(0, 8) + '...');
44
+ if (onLoginSuccess && authResult.method !== 'recall') {
45
+ onLoginSuccess({
46
+ userPub: pub,
47
+ username: authResult.alias || pub.slice(0, 8) + '...',
48
+ authMethod: authResult.method,
49
+ });
50
+ }
51
+ }
52
+ };
53
+ const handleLogout = () => {
54
+ setIsLoggedIn(false);
55
+ setUserPub(null);
56
+ setUsername(null);
57
+ };
58
+ if (sdk.isLoggedIn()) {
59
+ const pub = (_b = (_a = sdk.gun.user()) === null || _a === void 0 ? void 0 : _a.is) === null || _b === void 0 ? void 0 : _b.pub;
60
+ if (pub) {
61
+ handleLogin({ pub, method: 'recall' });
62
+ }
35
63
  }
64
+ sdk.on('auth:login', handleLogin);
65
+ sdk.on('auth:logout', handleLogout);
36
66
  return () => {
37
- // Pulizia quando il componente si smonta
67
+ sdk.off('auth:login', handleLogin);
68
+ sdk.off('auth:logout', handleLogout);
38
69
  };
39
- }, [sdk]);
70
+ }, [sdk, onLoginSuccess]);
40
71
  // RxJS observe method
41
72
  const observe = (path) => {
42
73
  if (!sdk) {
@@ -58,6 +89,31 @@ export function ShogunButtonProvider({ children, sdk, options, onLoginSuccess, o
58
89
  username = args[0];
59
90
  result = await sdk.login(args[0], args[1]);
60
91
  break;
92
+ case "pair":
93
+ // New pair authentication method
94
+ const pair = args[0];
95
+ if (!pair || typeof pair !== 'object') {
96
+ throw new Error("Invalid pair data provided");
97
+ }
98
+ result = await new Promise((resolve, reject) => {
99
+ sdk.gun.user().auth(pair, (ack) => {
100
+ if (ack.err) {
101
+ reject(new Error(`Pair authentication failed: ${ack.err}`));
102
+ return;
103
+ }
104
+ const pub = ack.pub || pair.pub;
105
+ const alias = ack.alias || `user_${pub === null || pub === void 0 ? void 0 : pub.substring(0, 8)}`;
106
+ resolve({
107
+ success: true,
108
+ userPub: pub,
109
+ alias: alias,
110
+ method: 'pair'
111
+ });
112
+ });
113
+ });
114
+ username = result.alias;
115
+ authMethod = "pair";
116
+ break;
61
117
  case "webauthn":
62
118
  username = args[0];
63
119
  const webauthn = sdk.getPlugin("webauthn");
@@ -246,6 +302,70 @@ export function ShogunButtonProvider({ children, sdk, options, onLoginSuccess, o
246
302
  const getPlugin = (name) => {
247
303
  return sdk ? sdk.getPlugin(name) : undefined;
248
304
  };
305
+ // Export Gun pair functionality
306
+ const exportGunPair = async (password) => {
307
+ var _a;
308
+ if (!sdk) {
309
+ throw new Error("SDK not initialized");
310
+ }
311
+ if (!isLoggedIn) {
312
+ throw new Error("User not authenticated");
313
+ }
314
+ try {
315
+ const user = sdk.gun.user();
316
+ const pair = (_a = user === null || user === void 0 ? void 0 : user._) === null || _a === void 0 ? void 0 : _a.sea;
317
+ if (!pair) {
318
+ throw new Error("No Gun pair available for current user");
319
+ }
320
+ let pairData = JSON.stringify(pair);
321
+ // If password provided, encrypt the pair
322
+ if (password && password.trim()) {
323
+ // Use Gun's SEA for encryption if available
324
+ if (window.SEA && window.SEA.encrypt) {
325
+ pairData = await window.SEA.encrypt(pairData, password);
326
+ }
327
+ else {
328
+ console.warn("SEA encryption not available, exporting unencrypted");
329
+ }
330
+ }
331
+ return pairData;
332
+ }
333
+ catch (error) {
334
+ throw new Error(`Failed to export Gun pair: ${error.message}`);
335
+ }
336
+ };
337
+ // Import Gun pair functionality
338
+ const importGunPair = async (pairData, password) => {
339
+ if (!sdk) {
340
+ throw new Error("SDK not initialized");
341
+ }
342
+ try {
343
+ let dataString = pairData;
344
+ // If password provided, decrypt the pair
345
+ if (password && password.trim()) {
346
+ if (window.SEA && window.SEA.decrypt) {
347
+ dataString = await window.SEA.decrypt(pairData, password);
348
+ if (!dataString) {
349
+ throw new Error("Failed to decrypt pair data - wrong password?");
350
+ }
351
+ }
352
+ else {
353
+ console.warn("SEA decryption not available, assuming unencrypted data");
354
+ }
355
+ }
356
+ const pair = JSON.parse(dataString);
357
+ // Validate pair structure
358
+ if (!pair.pub || !pair.priv || !pair.epub || !pair.epriv) {
359
+ throw new Error("Invalid pair structure - missing required keys");
360
+ }
361
+ // Authenticate with the imported pair
362
+ const result = await login("pair", pair);
363
+ return result.success;
364
+ }
365
+ catch (error) {
366
+ throw new Error(`Failed to import Gun pair: ${error.message}`);
367
+ }
368
+ };
249
369
  // Provide the context value to children
250
370
  return (React.createElement(ShogunContext.Provider, { value: {
251
371
  sdk,
@@ -260,6 +380,8 @@ export function ShogunButtonProvider({ children, sdk, options, onLoginSuccess, o
260
380
  setProvider,
261
381
  hasPlugin,
262
382
  getPlugin,
383
+ exportGunPair,
384
+ importGunPair,
263
385
  } }, children));
264
386
  }
265
387
  // SVG Icons Components
@@ -298,20 +420,44 @@ const CloseIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/
298
420
  // Component for Shogun login button
299
421
  export const ShogunButton = (() => {
300
422
  const Button = () => {
301
- const { isLoggedIn, username, logout, login, signUp, sdk, options } = useShogun();
423
+ const { isLoggedIn, username, logout, login, signUp, sdk, options, exportGunPair, importGunPair } = useShogun();
302
424
  // Form states
303
425
  const [modalIsOpen, setModalIsOpen] = useState(false);
304
426
  const [formUsername, setFormUsername] = useState("");
305
427
  const [formPassword, setFormPassword] = useState("");
306
428
  const [formPasswordConfirm, setFormPasswordConfirm] = useState("");
429
+ const [formHint, setFormHint] = useState("");
430
+ const [formSecurityQuestion] = useState("What is your favorite color?"); // Hardcoded for now
431
+ const [formSecurityAnswer, setFormSecurityAnswer] = useState("");
307
432
  const [formMode, setFormMode] = useState("login");
433
+ const [authView, setAuthView] = useState("options");
308
434
  const [error, setError] = useState("");
309
435
  const [loading, setLoading] = useState(false);
310
436
  const [dropdownOpen, setDropdownOpen] = useState(false);
437
+ const [recoveredHint, setRecoveredHint] = useState("");
438
+ const [exportPassword, setExportPassword] = useState("");
439
+ const [importPassword, setImportPassword] = useState("");
440
+ const [importPairData, setImportPairData] = useState("");
441
+ const [exportedPair, setExportedPair] = useState("");
442
+ const dropdownRef = useRef(null);
443
+ // Handle click outside to close dropdown
444
+ useEffect(() => {
445
+ const handleClickOutside = (event) => {
446
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
447
+ setDropdownOpen(false);
448
+ }
449
+ };
450
+ if (dropdownOpen) {
451
+ document.addEventListener('mousedown', handleClickOutside);
452
+ return () => {
453
+ document.removeEventListener('mousedown', handleClickOutside);
454
+ };
455
+ }
456
+ }, [dropdownOpen]);
311
457
  // If already logged in, show only logout button
312
458
  if (isLoggedIn && username) {
313
459
  return (React.createElement("div", { className: "shogun-logged-in-container" },
314
- React.createElement("div", { className: "shogun-dropdown" },
460
+ React.createElement("div", { className: "shogun-dropdown", ref: dropdownRef },
315
461
  React.createElement("button", { className: "shogun-button shogun-logged-in", onClick: () => setDropdownOpen(!dropdownOpen) },
316
462
  React.createElement("div", { className: "shogun-avatar" }, username.substring(0, 2).toUpperCase()),
317
463
  React.createElement("span", { className: "shogun-username" }, username.length > 12
@@ -326,13 +472,21 @@ export const ShogunButton = (() => {
326
472
  : username))),
327
473
  React.createElement("div", { className: "shogun-dropdown-item", onClick: logout },
328
474
  React.createElement(LogoutIcon, null),
329
- React.createElement("span", null, "Disconnect")))))));
475
+ React.createElement("span", null, "Disconnect")),
476
+ React.createElement("div", { className: "shogun-dropdown-item", onClick: () => {
477
+ setDropdownOpen(false);
478
+ setAuthView("export");
479
+ setModalIsOpen(true);
480
+ } },
481
+ React.createElement("span", null, "\uD83D\uDCE4"),
482
+ React.createElement("span", null, "Export Pair")))))));
330
483
  }
331
484
  // Event handlers
332
485
  const handleAuth = async (method, ...args) => {
333
486
  setError("");
334
487
  setLoading(true);
335
488
  try {
489
+ // Use formMode to determine whether to call login or signUp
336
490
  const action = formMode === "login" ? login : signUp;
337
491
  const result = await action(method, ...args);
338
492
  if (result && !result.success && result.error) {
@@ -352,9 +506,33 @@ export const ShogunButton = (() => {
352
506
  setLoading(false);
353
507
  }
354
508
  };
355
- const handleSubmit = (e) => {
509
+ const handleSubmit = async (e) => {
356
510
  e.preventDefault();
357
- handleAuth("password", formUsername, formPassword, formPasswordConfirm);
511
+ setError("");
512
+ setLoading(true);
513
+ try {
514
+ if (formMode === "signup") {
515
+ const result = await signUp("password", formUsername, formPassword, formPasswordConfirm);
516
+ if (result && result.success) {
517
+ if (sdk === null || sdk === void 0 ? void 0 : sdk.gundb) {
518
+ await sdk.gundb.setPasswordHint(formUsername, formPassword, formHint, [formSecurityQuestion], [formSecurityAnswer]);
519
+ }
520
+ setModalIsOpen(false);
521
+ }
522
+ else if (result && result.error) {
523
+ setError(result.error);
524
+ }
525
+ }
526
+ else {
527
+ await handleAuth("password", formUsername, formPassword);
528
+ }
529
+ }
530
+ catch (e) {
531
+ setError(e.message || "An unexpected error occurred.");
532
+ }
533
+ finally {
534
+ setLoading(false);
535
+ }
358
536
  };
359
537
  const handleWeb3Auth = () => handleAuth("web3");
360
538
  const handleWebAuthnAuth = () => {
@@ -370,15 +548,89 @@ export const ShogunButton = (() => {
370
548
  };
371
549
  const handleNostrAuth = () => handleAuth("nostr");
372
550
  const handleOAuth = (provider) => handleAuth("oauth", provider);
551
+ const handleRecover = async () => {
552
+ setError("");
553
+ setLoading(true);
554
+ try {
555
+ if (!(sdk === null || sdk === void 0 ? void 0 : sdk.gundb)) {
556
+ throw new Error("SDK not ready");
557
+ }
558
+ const result = await sdk.gundb.forgotPassword(formUsername, [
559
+ formSecurityAnswer,
560
+ ]);
561
+ if (result.success && result.hint) {
562
+ setRecoveredHint(result.hint);
563
+ setAuthView("showHint");
564
+ }
565
+ else {
566
+ setError(result.error || "Could not recover hint.");
567
+ }
568
+ }
569
+ catch (e) {
570
+ setError(e.message || "An unexpected error occurred.");
571
+ }
572
+ finally {
573
+ setLoading(false);
574
+ }
575
+ };
576
+ const handleExportPair = async () => {
577
+ setError("");
578
+ setLoading(true);
579
+ try {
580
+ const pairData = await exportGunPair(exportPassword || undefined);
581
+ setExportedPair(pairData);
582
+ // Copy to clipboard
583
+ if (navigator.clipboard) {
584
+ await navigator.clipboard.writeText(pairData);
585
+ alert("Pair exported and copied to clipboard!");
586
+ }
587
+ else {
588
+ alert("Pair exported! Please copy it manually from the text area.");
589
+ }
590
+ }
591
+ catch (e) {
592
+ setError(e.message || "Failed to export pair");
593
+ }
594
+ finally {
595
+ setLoading(false);
596
+ }
597
+ };
598
+ const handleImportPair = async () => {
599
+ setError("");
600
+ setLoading(true);
601
+ try {
602
+ if (!importPairData.trim()) {
603
+ throw new Error("Please enter pair data");
604
+ }
605
+ const success = await importGunPair(importPairData, importPassword || undefined);
606
+ if (success) {
607
+ setModalIsOpen(false);
608
+ alert("Pair imported successfully! You are now logged in.");
609
+ }
610
+ else {
611
+ throw new Error("Failed to import pair");
612
+ }
613
+ }
614
+ catch (e) {
615
+ setError(e.message || "Failed to import pair");
616
+ }
617
+ finally {
618
+ setLoading(false);
619
+ }
620
+ };
373
621
  const resetForm = () => {
374
622
  setFormUsername("");
375
623
  setFormPassword("");
376
624
  setFormPasswordConfirm("");
625
+ setFormHint("");
626
+ setFormSecurityAnswer("");
377
627
  setError("");
378
628
  setLoading(false);
629
+ setAuthView("options");
379
630
  };
380
631
  const openModal = () => {
381
632
  resetForm();
633
+ setAuthView("options");
382
634
  setModalIsOpen(true);
383
635
  };
384
636
  const closeModal = () => {
@@ -386,62 +638,196 @@ export const ShogunButton = (() => {
386
638
  };
387
639
  const toggleMode = () => {
388
640
  resetForm();
641
+ setAuthView("password");
389
642
  setFormMode((prev) => (prev === "login" ? "signup" : "login"));
390
643
  };
644
+ // Add buttons for both login and signup for alternative auth methods
645
+ const renderAuthOptions = () => (React.createElement("div", { className: "shogun-auth-options" },
646
+ options.showMetamask !== false && (sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("web3")) && (React.createElement("div", { className: "shogun-auth-option-group" },
647
+ React.createElement("button", { type: "button", className: "shogun-auth-option-button", onClick: () => handleAuth("web3"), disabled: loading },
648
+ React.createElement(WalletIcon, null),
649
+ formMode === "login" ? "Login with MetaMask" : "Signup with MetaMask"))),
650
+ options.showWebauthn !== false && (sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("webauthn")) && (React.createElement("div", { className: "shogun-auth-option-group" },
651
+ React.createElement("button", { type: "button", className: "shogun-auth-option-button", onClick: () => {
652
+ if (!formUsername) {
653
+ setError("Username required for WebAuthn");
654
+ return;
655
+ }
656
+ handleAuth("webauthn", formUsername);
657
+ }, disabled: loading },
658
+ React.createElement(WebAuthnIcon, null),
659
+ formMode === "login" ? "Login with WebAuthn" : "Signup with WebAuthn"))),
660
+ options.showNostr !== false && (sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("nostr")) && (React.createElement("div", { className: "shogun-auth-option-group" },
661
+ React.createElement("button", { type: "button", className: "shogun-auth-option-button", onClick: () => handleAuth("nostr"), disabled: loading },
662
+ React.createElement(NostrIcon, null),
663
+ formMode === "login" ? "Login with Nostr" : "Signup with Nostr"))),
664
+ options.showOauth !== false && (sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("oauth")) && (React.createElement("div", { className: "shogun-auth-option-group" },
665
+ React.createElement("button", { type: "button", className: "shogun-auth-option-button shogun-google-button", onClick: () => handleAuth("oauth", "google"), disabled: loading },
666
+ React.createElement(GoogleIcon, null),
667
+ formMode === "login" ? "Login with Google" : "Signup with Google"))),
668
+ React.createElement("div", { className: "shogun-divider" },
669
+ React.createElement("span", null, "or")),
670
+ React.createElement("button", { type: "button", className: "shogun-auth-option-button", onClick: () => setAuthView("password"), disabled: loading },
671
+ React.createElement(LockIcon, null),
672
+ formMode === "login" ? "Login with Password" : "Signup with Password"),
673
+ formMode === "login" && (React.createElement("button", { type: "button", className: "shogun-auth-option-button", onClick: () => setAuthView("import"), disabled: loading },
674
+ React.createElement("span", null, "\uD83D\uDCE5"),
675
+ "Import Gun Pair"))));
676
+ const renderPasswordForm = () => (React.createElement("form", { onSubmit: handleSubmit, className: "shogun-auth-form" },
677
+ React.createElement("div", { className: "shogun-form-group" },
678
+ React.createElement("label", { htmlFor: "username" },
679
+ React.createElement(UserIcon, null),
680
+ React.createElement("span", null, "Username")),
681
+ React.createElement("input", { type: "text", id: "username", value: formUsername, onChange: (e) => setFormUsername(e.target.value), disabled: loading, required: true, placeholder: "Enter your username" })),
682
+ React.createElement("div", { className: "shogun-form-group" },
683
+ React.createElement("label", { htmlFor: "password" },
684
+ React.createElement(LockIcon, null),
685
+ React.createElement("span", null, "Password")),
686
+ React.createElement("input", { type: "password", id: "password", value: formPassword, onChange: (e) => setFormPassword(e.target.value), disabled: loading, required: true, placeholder: "Enter your password" })),
687
+ formMode === "signup" && (React.createElement(React.Fragment, null,
688
+ React.createElement("div", { className: "shogun-form-group" },
689
+ React.createElement("label", { htmlFor: "passwordConfirm" },
690
+ React.createElement(KeyIcon, null),
691
+ React.createElement("span", null, "Confirm Password")),
692
+ React.createElement("input", { type: "password", id: "passwordConfirm", value: formPasswordConfirm, onChange: (e) => setFormPasswordConfirm(e.target.value), disabled: loading, required: true, placeholder: "Confirm your password" })),
693
+ React.createElement("div", { className: "shogun-form-group" },
694
+ React.createElement("label", { htmlFor: "hint" },
695
+ React.createElement(UserIcon, null),
696
+ React.createElement("span", null, "Password Hint")),
697
+ React.createElement("input", { type: "text", id: "hint", value: formHint, onChange: (e) => setFormHint(e.target.value), disabled: loading, required: true, placeholder: "Enter your password hint" })),
698
+ React.createElement("div", { className: "shogun-form-group" },
699
+ React.createElement("label", { htmlFor: "securityQuestion" },
700
+ React.createElement(UserIcon, null),
701
+ React.createElement("span", null, "Security Question")),
702
+ React.createElement("input", { type: "text", id: "securityQuestion", value: formSecurityQuestion, disabled: true })),
703
+ React.createElement("div", { className: "shogun-form-group" },
704
+ React.createElement("label", { htmlFor: "securityAnswer" },
705
+ React.createElement(UserIcon, null),
706
+ React.createElement("span", null, "Security Answer")),
707
+ React.createElement("input", { type: "text", id: "securityAnswer", value: formSecurityAnswer, onChange: (e) => setFormSecurityAnswer(e.target.value), disabled: loading, required: true, placeholder: "Enter your security answer" })))),
708
+ React.createElement("button", { type: "submit", className: "shogun-submit-button", disabled: loading }, loading
709
+ ? "Processing..."
710
+ : formMode === "login"
711
+ ? "Sign In"
712
+ : "Create Account"),
713
+ React.createElement("div", { className: "shogun-form-footer" },
714
+ React.createElement("button", { type: "button", className: "shogun-toggle-mode shogun-prominent-toggle", onClick: toggleMode, disabled: loading }, formMode === "login"
715
+ ? "Don't have an account? Sign up"
716
+ : "Already have an account? Log in"),
717
+ formMode === "login" && (React.createElement("button", { type: "button", className: "shogun-toggle-mode", onClick: () => setAuthView("recover"), disabled: loading }, "Forgot password?")))));
718
+ const renderRecoveryForm = () => (React.createElement("div", { className: "shogun-auth-form" },
719
+ React.createElement("div", { className: "shogun-form-group" },
720
+ React.createElement("label", { htmlFor: "username" },
721
+ React.createElement(UserIcon, null),
722
+ React.createElement("span", null, "Username")),
723
+ React.createElement("input", { type: "text", id: "username", value: formUsername, onChange: (e) => setFormUsername(e.target.value), disabled: loading, required: true, placeholder: "Enter your username" })),
724
+ React.createElement("div", { className: "shogun-form-group" },
725
+ React.createElement("label", null, "Security Question"),
726
+ React.createElement("p", null, formSecurityQuestion)),
727
+ React.createElement("div", { className: "shogun-form-group" },
728
+ React.createElement("label", { htmlFor: "securityAnswer" },
729
+ React.createElement(KeyIcon, null),
730
+ React.createElement("span", null, "Answer")),
731
+ React.createElement("input", { type: "text", id: "securityAnswer", value: formSecurityAnswer, onChange: (e) => setFormSecurityAnswer(e.target.value), disabled: loading, required: true, placeholder: "Enter your answer" })),
732
+ React.createElement("button", { type: "button", className: "shogun-submit-button", onClick: handleRecover, disabled: loading }, loading ? "Recovering..." : "Get Hint"),
733
+ React.createElement("div", { className: "shogun-form-footer" },
734
+ React.createElement("button", { className: "shogun-toggle-mode", onClick: () => setAuthView("password"), disabled: loading }, "Back to Login"))));
735
+ const renderHint = () => (React.createElement("div", { className: "shogun-auth-form" },
736
+ React.createElement("h3", null, "Your Password Hint"),
737
+ React.createElement("p", { className: "shogun-hint" }, recoveredHint),
738
+ React.createElement("button", { className: "shogun-submit-button", onClick: () => {
739
+ setAuthView("password");
740
+ resetForm();
741
+ setFormMode("login");
742
+ } }, "Back to Login")));
743
+ const renderExportForm = () => (React.createElement("div", { className: "shogun-auth-form" },
744
+ React.createElement("h3", null, "Export Gun Pair"),
745
+ React.createElement("p", { style: { fontSize: '14px', color: '#666', marginBottom: '16px' } }, "Export your Gun pair to backup your account. You can use this to login from another device."),
746
+ React.createElement("div", { className: "shogun-form-group" },
747
+ React.createElement("label", { htmlFor: "exportPassword" },
748
+ React.createElement(LockIcon, null),
749
+ React.createElement("span", null, "Encryption Password (optional but recommended)")),
750
+ React.createElement("input", { type: "password", id: "exportPassword", value: exportPassword, onChange: (e) => setExportPassword(e.target.value), disabled: loading, placeholder: "Leave empty to export unencrypted" })),
751
+ exportedPair && (React.createElement("div", { className: "shogun-form-group" },
752
+ React.createElement("label", null, "Your Gun Pair (copy this safely):"),
753
+ React.createElement("textarea", { value: exportedPair, readOnly: true, rows: 6, style: {
754
+ fontFamily: 'monospace',
755
+ fontSize: '12px',
756
+ width: '100%',
757
+ padding: '8px',
758
+ border: '1px solid #ccc',
759
+ borderRadius: '4px'
760
+ } }))),
761
+ React.createElement("button", { type: "button", className: "shogun-submit-button", onClick: handleExportPair, disabled: loading }, loading ? "Exporting..." : "Export Pair"),
762
+ React.createElement("div", { className: "shogun-form-footer" },
763
+ React.createElement("button", { className: "shogun-toggle-mode", onClick: () => {
764
+ setAuthView("options");
765
+ setExportPassword("");
766
+ setExportedPair("");
767
+ }, disabled: loading }, "Back"))));
768
+ const renderImportForm = () => (React.createElement("div", { className: "shogun-auth-form" },
769
+ React.createElement("h3", null, "Import Gun Pair"),
770
+ React.createElement("p", { style: { fontSize: '14px', color: '#666', marginBottom: '16px' } }, "Import a Gun pair to login with your existing account from another device."),
771
+ React.createElement("div", { className: "shogun-form-group" },
772
+ React.createElement("label", { htmlFor: "importPairData" },
773
+ React.createElement("span", null, "\uD83D\uDCC4"),
774
+ React.createElement("span", null, "Gun Pair Data")),
775
+ React.createElement("textarea", { id: "importPairData", value: importPairData, onChange: (e) => setImportPairData(e.target.value), disabled: loading, placeholder: "Paste your Gun pair JSON here...", rows: 6, style: {
776
+ fontFamily: 'monospace',
777
+ fontSize: '12px',
778
+ width: '100%',
779
+ padding: '8px',
780
+ border: '1px solid #ccc',
781
+ borderRadius: '4px'
782
+ } })),
783
+ React.createElement("div", { className: "shogun-form-group" },
784
+ React.createElement("label", { htmlFor: "importPassword" },
785
+ React.createElement(LockIcon, null),
786
+ React.createElement("span", null, "Decryption Password (if encrypted)")),
787
+ React.createElement("input", { type: "password", id: "importPassword", value: importPassword, onChange: (e) => setImportPassword(e.target.value), disabled: loading, placeholder: "Enter password if pair was encrypted" })),
788
+ React.createElement("button", { type: "button", className: "shogun-submit-button", onClick: handleImportPair, disabled: loading }, loading ? "Importing..." : "Import and Login"),
789
+ React.createElement("div", { className: "shogun-form-footer" },
790
+ React.createElement("button", { className: "shogun-toggle-mode", onClick: () => {
791
+ setAuthView("options");
792
+ setImportPassword("");
793
+ setImportPairData("");
794
+ }, disabled: loading }, "Back to Login Options"))));
391
795
  // Render logic
392
796
  return (React.createElement(React.Fragment, null,
393
797
  React.createElement("button", { className: "shogun-connect-button", onClick: openModal },
394
798
  React.createElement(WalletIcon, null),
395
- React.createElement("span", null, "Connect")),
799
+ React.createElement("span", null, "Login / Sign Up")),
396
800
  modalIsOpen && (React.createElement("div", { className: "shogun-modal-overlay", onClick: closeModal },
397
801
  React.createElement("div", { className: "shogun-modal", onClick: (e) => e.stopPropagation() },
398
802
  React.createElement("div", { className: "shogun-modal-header" },
399
- React.createElement("h2", null, formMode === "login" ? "Sign In" : "Create Account"),
400
- React.createElement("button", { className: "shogun-close-button", onClick: closeModal },
803
+ React.createElement("h2", null, authView === "recover"
804
+ ? "Recover Password"
805
+ : authView === "showHint"
806
+ ? "Password Hint"
807
+ : authView === "export"
808
+ ? "Export Gun Pair"
809
+ : authView === "import"
810
+ ? "Import Gun Pair"
811
+ : formMode === "login"
812
+ ? "Login"
813
+ : "Sign Up"),
814
+ React.createElement("button", { className: "shogun-close-button", onClick: closeModal, "aria-label": "Close" },
401
815
  React.createElement(CloseIcon, null))),
402
816
  React.createElement("div", { className: "shogun-modal-content" },
403
817
  error && React.createElement("div", { className: "shogun-error-message" }, error),
404
- React.createElement("div", { className: "shogun-auth-options" },
405
- (options === null || options === void 0 ? void 0 : options.showMetamask) && (sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("web3")) && (React.createElement("button", { className: "shogun-auth-option-button", onClick: handleWeb3Auth, disabled: loading },
406
- React.createElement(WalletIcon, null),
407
- React.createElement("span", null, "Continue with Wallet"))),
408
- (options === null || options === void 0 ? void 0 : options.showWebauthn) && (sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("webauthn")) && (React.createElement("button", { className: "shogun-auth-option-button", onClick: handleWebAuthnAuth, disabled: loading },
409
- React.createElement(WebAuthnIcon, null),
410
- React.createElement("span", null, "Continue with Passkey"))),
411
- (options === null || options === void 0 ? void 0 : options.showNostr) && (sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("nostr")) && (React.createElement("button", { className: "shogun-auth-option-button", onClick: handleNostrAuth, disabled: loading },
412
- React.createElement(NostrIcon, null),
413
- React.createElement("span", null, "Continue with Nostr"))),
414
- (options === null || options === void 0 ? void 0 : options.showOauth) && (sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("oauth")) && (React.createElement("button", { className: "shogun-auth-option-button shogun-google-button", onClick: () => handleOAuth("google"), disabled: loading },
415
- React.createElement(GoogleIcon, null),
416
- React.createElement("span", null, "Continue with Google")))),
417
- React.createElement("div", { className: "shogun-divider" },
418
- React.createElement("span", null, "or continue with password")),
419
- React.createElement("form", { onSubmit: handleSubmit, className: "shogun-auth-form" },
420
- React.createElement("div", { className: "shogun-form-group" },
421
- React.createElement("label", { htmlFor: "username" },
422
- React.createElement(UserIcon, null),
423
- React.createElement("span", null, "Username")),
424
- React.createElement("input", { type: "text", id: "username", value: formUsername, onChange: (e) => setFormUsername(e.target.value), disabled: loading, required: true, placeholder: "Enter your username" })),
425
- React.createElement("div", { className: "shogun-form-group" },
426
- React.createElement("label", { htmlFor: "password" },
427
- React.createElement(LockIcon, null),
428
- React.createElement("span", null, "Password")),
429
- React.createElement("input", { type: "password", id: "password", value: formPassword, onChange: (e) => setFormPassword(e.target.value), disabled: loading, required: true, placeholder: "Enter your password" })),
430
- formMode === "signup" && (React.createElement("div", { className: "shogun-form-group" },
431
- React.createElement("label", { htmlFor: "passwordConfirm" },
432
- React.createElement(KeyIcon, null),
433
- React.createElement("span", null, "Confirm Password")),
434
- React.createElement("input", { type: "password", id: "passwordConfirm", value: formPasswordConfirm, onChange: (e) => setFormPasswordConfirm(e.target.value), disabled: loading, required: true, placeholder: "Confirm your password" }))),
435
- React.createElement("button", { type: "submit", className: "shogun-submit-button", disabled: loading }, loading
436
- ? "Processing..."
437
- : formMode === "login"
438
- ? "Sign In"
439
- : "Create Account")),
440
- React.createElement("div", { className: "shogun-form-footer" },
441
- formMode === "login"
442
- ? "Don't have an account?"
443
- : "Already have an account?",
444
- React.createElement("button", { className: "shogun-toggle-mode", onClick: toggleMode, disabled: loading }, formMode === "login" ? "Sign Up" : "Sign In"))))))));
818
+ authView === "options" && (React.createElement(React.Fragment, null,
819
+ renderAuthOptions(),
820
+ React.createElement("div", { className: "shogun-form-footer" },
821
+ React.createElement("button", { type: "button", className: "shogun-toggle-mode shogun-prominent-toggle", onClick: toggleMode, disabled: loading }, formMode === "login"
822
+ ? "Don't have an account? Sign up"
823
+ : "Already have an account? Log in")))),
824
+ authView === "password" && (React.createElement(React.Fragment, null,
825
+ React.createElement("button", { className: "shogun-back-button", onClick: () => setAuthView("options") }, "\u2190 Back"),
826
+ renderPasswordForm())),
827
+ authView === "recover" && renderRecoveryForm(),
828
+ authView === "showHint" && renderHint(),
829
+ authView === "export" && renderExportForm(),
830
+ authView === "import" && renderImportForm()))))));
445
831
  };
446
832
  Button.displayName = "ShogunButton";
447
833
  return Object.assign(Button, {
@@ -0,0 +1,2 @@
1
+ import { ShogunConnectorOptions, ShogunConnectorResult } from "./types";
2
+ export declare function shogunConnector(options: ShogunConnectorOptions): ShogunConnectorResult;