Add extension
This commit is contained in:
		
							
								
								
									
										225
									
								
								crypto_vault_extension/background.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								crypto_vault_extension/background.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,225 @@ | ||||
| let vault = null; | ||||
| let isInitialized = false; | ||||
| let currentSession = null; | ||||
|  | ||||
| // Utility function to convert Uint8Array to hex | ||||
| function toHex(uint8Array) { | ||||
|   return Array.from(uint8Array) | ||||
|     .map(b => b.toString(16).padStart(2, '0')) | ||||
|     .join(''); | ||||
| } | ||||
|  | ||||
| // Session persistence functions | ||||
| async function saveSession(keyspace) { | ||||
|   currentSession = { keyspace, timestamp: Date.now() }; | ||||
|   await chrome.storage.session.set({ cryptoVaultSession: currentSession }); | ||||
|   console.log('Session saved:', currentSession); | ||||
| } | ||||
|  | ||||
| async function loadSession() { | ||||
|   try { | ||||
|     const result = await chrome.storage.session.get(['cryptoVaultSession']); | ||||
|     if (result.cryptoVaultSession) { | ||||
|       currentSession = result.cryptoVaultSession; | ||||
|       console.log('Session loaded:', currentSession); | ||||
|       return currentSession; | ||||
|     } | ||||
|   } catch (error) { | ||||
|     console.error('Failed to load session:', error); | ||||
|   } | ||||
|   return null; | ||||
| } | ||||
|  | ||||
| async function clearSession() { | ||||
|   currentSession = null; | ||||
|   await chrome.storage.session.remove(['cryptoVaultSession']); | ||||
|   console.log('Session cleared'); | ||||
| } | ||||
|  | ||||
| async function restoreSession() { | ||||
|   const session = await loadSession(); | ||||
|   if (session && vault) { | ||||
|     try { | ||||
|       // Check if the session is still valid by testing if vault is unlocked | ||||
|       const isUnlocked = vault.is_unlocked(); | ||||
|       if (isUnlocked) { | ||||
|         console.log('Session restored successfully for keyspace:', session.keyspace); | ||||
|         return session; | ||||
|       } else { | ||||
|         console.log('Session expired, clearing...'); | ||||
|         await clearSession(); | ||||
|       } | ||||
|     } catch (error) { | ||||
|       console.error('Error checking session validity:', error); | ||||
|       await clearSession(); | ||||
|     } | ||||
|   } | ||||
|   return null; | ||||
| } | ||||
|  | ||||
| // Import WASM module functions | ||||
| import init, { | ||||
|   create_keyspace, | ||||
|   init_session, | ||||
|   is_unlocked, | ||||
|   add_keypair, | ||||
|   list_keypairs, | ||||
|   select_keypair, | ||||
|   current_keypair_metadata, | ||||
|   current_keypair_public_key, | ||||
|   sign, | ||||
|   lock_session | ||||
| } from './wasm/wasm_app.js'; | ||||
|  | ||||
| // Initialize WASM module | ||||
| async function initVault() { | ||||
|   try { | ||||
|     if (vault && isInitialized) return vault; | ||||
|  | ||||
|     // Initialize with the WASM file | ||||
|     const wasmUrl = chrome.runtime.getURL('wasm/wasm_app_bg.wasm'); | ||||
|     await init(wasmUrl); | ||||
|  | ||||
|     // Create a vault object with all the imported functions | ||||
|     vault = { | ||||
|       create_keyspace, | ||||
|       init_session, | ||||
|       is_unlocked, | ||||
|       add_keypair, | ||||
|       list_keypairs, | ||||
|       select_keypair, | ||||
|       current_keypair_metadata, | ||||
|       current_keypair_public_key, | ||||
|       sign, | ||||
|       lock_session | ||||
|     }; | ||||
|  | ||||
|     isInitialized = true; | ||||
|     console.log('CryptoVault initialized successfully'); | ||||
|  | ||||
|     // Try to restore previous session | ||||
|     await restoreSession(); | ||||
|  | ||||
|     return vault; | ||||
|   } catch (error) { | ||||
|     console.error('Failed to initialize CryptoVault:', error); | ||||
|     throw error; | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Handle messages from popup and content scripts | ||||
| chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { | ||||
|   const handleRequest = async () => { | ||||
|     try { | ||||
|       if (!vault) { | ||||
|         await initVault(); | ||||
|       } | ||||
|  | ||||
|       switch (request.action) { | ||||
|         case 'createKeyspace': | ||||
|           await vault.create_keyspace(request.keyspace, request.password); | ||||
|           return { success: true }; | ||||
|  | ||||
|         case 'initSession': | ||||
|           await vault.init_session(request.keyspace, request.password); | ||||
|           await saveSession(request.keyspace); | ||||
|           return { success: true }; | ||||
|  | ||||
|         case 'isUnlocked': | ||||
|           const unlocked = vault.is_unlocked(); | ||||
|           return { success: true, unlocked }; | ||||
|  | ||||
|         case 'addKeypair': | ||||
|           const result = await vault.add_keypair(request.keyType, request.metadata); | ||||
|           return { success: true, result }; | ||||
|  | ||||
|         case 'listKeypairs': | ||||
|           console.log('Background: listing keypairs...'); | ||||
|           console.log('Background: vault object:', vault); | ||||
|           console.log('Background: vault.list_keypairs function:', vault.list_keypairs); | ||||
|  | ||||
|           // Check if session is unlocked first | ||||
|           const isUnlocked = vault.is_unlocked(); | ||||
|           console.log('Background: is session unlocked?', isUnlocked); | ||||
|  | ||||
|           if (!isUnlocked) { | ||||
|             console.log('Background: Session is not unlocked, cannot list keypairs'); | ||||
|             return { success: false, error: 'Session is not unlocked' }; | ||||
|           } | ||||
|  | ||||
|           try { | ||||
|             const keypairsRaw = await vault.list_keypairs(); | ||||
|             console.log('Background: keypairs raw result:', keypairsRaw); | ||||
|             console.log('Background: keypairs type:', typeof keypairsRaw); | ||||
|  | ||||
|             // Parse JSON string if needed | ||||
|             let keypairs; | ||||
|             if (typeof keypairsRaw === 'string') { | ||||
|               console.log('Background: Parsing JSON string...'); | ||||
|               keypairs = JSON.parse(keypairsRaw); | ||||
|             } else { | ||||
|               keypairs = keypairsRaw; | ||||
|             } | ||||
|  | ||||
|             console.log('Background: parsed keypairs:', keypairs); | ||||
|             console.log('Background: parsed keypairs type:', typeof keypairs); | ||||
|             console.log('Background: keypairs array length:', Array.isArray(keypairs) ? keypairs.length : 'not an array'); | ||||
|  | ||||
|             return { success: true, keypairs }; | ||||
|           } catch (listError) { | ||||
|             console.error('Background: Error calling list_keypairs:', listError); | ||||
|             throw listError; | ||||
|           } | ||||
|  | ||||
|         case 'selectKeypair': | ||||
|           vault.select_keypair(request.keyId); | ||||
|           return { success: true }; | ||||
|  | ||||
|         case 'getCurrentKeypairMetadata': | ||||
|           const metadata = vault.current_keypair_metadata(); | ||||
|           return { success: true, metadata }; | ||||
|  | ||||
|         case 'getCurrentKeypairPublicKey': | ||||
|           const publicKey = vault.current_keypair_public_key(); | ||||
|           const hexKey = toHex(publicKey); | ||||
|           return { success: true, publicKey: hexKey }; | ||||
|  | ||||
|         case 'sign': | ||||
|           const signature = await vault.sign(new Uint8Array(request.message)); | ||||
|           return { success: true, signature }; | ||||
|  | ||||
|         case 'lockSession': | ||||
|           vault.lock_session(); | ||||
|           await clearSession(); | ||||
|           return { success: true }; | ||||
|  | ||||
|         case 'getStatus': | ||||
|           const status = vault ? vault.is_unlocked() : false; | ||||
|           const session = await loadSession(); | ||||
|           return { | ||||
|             success: true, | ||||
|             status, | ||||
|             session: session ? { keyspace: session.keyspace } : null | ||||
|           }; | ||||
|  | ||||
|         default: | ||||
|           throw new Error('Unknown action: ' + request.action); | ||||
|       } | ||||
|     } catch (error) { | ||||
|       console.error('Background script error:', error); | ||||
|       return { success: false, error: error.message }; | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   handleRequest().then(sendResponse); | ||||
|   return true; // Keep the message channel open for async response | ||||
| }); | ||||
|  | ||||
| // Initialize vault when extension starts | ||||
| chrome.runtime.onStartup.addListener(() => { | ||||
|   initVault(); | ||||
| }); | ||||
|  | ||||
| chrome.runtime.onInstalled.addListener(() => { | ||||
|   initVault(); | ||||
| }); | ||||
							
								
								
									
										89
									
								
								crypto_vault_extension/content.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								crypto_vault_extension/content.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| // Content script for potential webpage integration | ||||
| // This can be used to inject CryptoVault functionality into webpages | ||||
|  | ||||
| (function() { | ||||
|   'use strict'; | ||||
|  | ||||
|   // Create a bridge between webpage and extension | ||||
|   const cryptoVaultBridge = { | ||||
|     async isAvailable() { | ||||
|       try { | ||||
|         const response = await chrome.runtime.sendMessage({ action: 'getStatus' }); | ||||
|         return response.success && response.status; | ||||
|       } catch (error) { | ||||
|         return false; | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     async sign(message) { | ||||
|       try { | ||||
|         const response = await chrome.runtime.sendMessage({  | ||||
|           action: 'sign',  | ||||
|           message: Array.from(new TextEncoder().encode(message)) | ||||
|         }); | ||||
|         return response.success ? response.signature : null; | ||||
|       } catch (error) { | ||||
|         console.error('CryptoVault sign error:', error); | ||||
|         return null; | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     async getPublicKey() { | ||||
|       try { | ||||
|         const response = await chrome.runtime.sendMessage({ action: 'getCurrentKeypairPublicKey' }); | ||||
|         return response.success ? response.publicKey : null; | ||||
|       } catch (error) { | ||||
|         console.error('CryptoVault getPublicKey error:', error); | ||||
|         return null; | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   // Expose to window for webpage access (optional) | ||||
|   if (window.location.protocol === 'https:' || window.location.hostname === 'localhost') { | ||||
|     window.cryptoVault = cryptoVaultBridge; | ||||
|      | ||||
|     // Dispatch event to let webpage know CryptoVault is available | ||||
|     window.dispatchEvent(new CustomEvent('cryptovault-ready', { | ||||
|       detail: { available: true } | ||||
|     })); | ||||
|   } | ||||
|  | ||||
|   // Listen for messages from webpage | ||||
|   window.addEventListener('message', async (event) => { | ||||
|     if (event.source !== window || !event.data.cryptoVault) return; | ||||
|  | ||||
|     const { action, id, data } = event.data; | ||||
|     let result; | ||||
|  | ||||
|     try { | ||||
|       switch (action) { | ||||
|         case 'sign': | ||||
|           result = await cryptoVaultBridge.sign(data.message); | ||||
|           break; | ||||
|         case 'getPublicKey': | ||||
|           result = await cryptoVaultBridge.getPublicKey(); | ||||
|           break; | ||||
|         case 'isAvailable': | ||||
|           result = await cryptoVaultBridge.isAvailable(); | ||||
|           break; | ||||
|         default: | ||||
|           throw new Error('Unknown action: ' + action); | ||||
|       } | ||||
|  | ||||
|       window.postMessage({ | ||||
|         cryptoVaultResponse: true, | ||||
|         id, | ||||
|         success: true, | ||||
|         result | ||||
|       }, '*'); | ||||
|     } catch (error) { | ||||
|       window.postMessage({ | ||||
|         cryptoVaultResponse: true, | ||||
|         id, | ||||
|         success: false, | ||||
|         error: error.message | ||||
|       }, '*'); | ||||
|     } | ||||
|   }); | ||||
| })(); | ||||
							
								
								
									
										
											BIN
										
									
								
								crypto_vault_extension/icons/icon128.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								crypto_vault_extension/icons/icon128.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								crypto_vault_extension/icons/icon16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								crypto_vault_extension/icons/icon16.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 454 B | 
							
								
								
									
										
											BIN
										
									
								
								crypto_vault_extension/icons/icon32.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								crypto_vault_extension/icons/icon32.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 712 B | 
							
								
								
									
										
											BIN
										
									
								
								crypto_vault_extension/icons/icon48.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								crypto_vault_extension/icons/icon48.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.1 KiB | 
							
								
								
									
										46
									
								
								crypto_vault_extension/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								crypto_vault_extension/manifest.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| { | ||||
|   "manifest_version": 3, | ||||
|   "name": "CryptoVault", | ||||
|   "version": "1.0.0", | ||||
|   "description": "Secure cryptographic key management and signing in your browser", | ||||
|  | ||||
|   "permissions": [ | ||||
|     "storage", | ||||
|     "activeTab" | ||||
|   ], | ||||
|  | ||||
|   "background": { | ||||
|     "service_worker": "background.js", | ||||
|     "type": "module" | ||||
|   }, | ||||
|  | ||||
|   "content_scripts": [ | ||||
|     { | ||||
|       "matches": ["<all_urls>"], | ||||
|       "js": ["content.js"] | ||||
|     } | ||||
|   ], | ||||
|  | ||||
|   "action": { | ||||
|     "default_popup": "popup.html", | ||||
|     "default_icon": { | ||||
|       "16": "icons/icon16.png", | ||||
|       "48": "icons/icon48.png", | ||||
|       "128": "icons/icon128.png" | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   "web_accessible_resources": [ | ||||
|     { | ||||
|       "resources": [ | ||||
|         "wasm/*.wasm", | ||||
|         "wasm/*.js" | ||||
|       ], | ||||
|       "matches": ["<all_urls>"] | ||||
|     } | ||||
|   ], | ||||
|  | ||||
|   "content_security_policy": { | ||||
|     "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'; style-src 'self' 'unsafe-inline';" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										138
									
								
								crypto_vault_extension/popup.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								crypto_vault_extension/popup.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,138 @@ | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
| <head> | ||||
|   <meta charset="utf-8"> | ||||
|   <link rel="stylesheet" href="styles/popup.css"> | ||||
| </head> | ||||
| <body> | ||||
|   <div class="container"> | ||||
|     <header class="header"> | ||||
|       <div class="logo"> | ||||
|         <div class="logo-icon">🔐</div> | ||||
|         <h1>CryptoVault</h1> | ||||
|       </div> | ||||
|       <div class="status-indicator" id="statusIndicator"> | ||||
|         <div class="status-dot"></div> | ||||
|         <span id="statusText">Initializing...</span> | ||||
|       </div> | ||||
|     </header> | ||||
|  | ||||
|     <!-- Create/Login Section --> | ||||
|     <section class="section" id="authSection"> | ||||
|       <div class="card"> | ||||
|         <h2>Access Your Vault</h2> | ||||
|         <div class="form-group"> | ||||
|           <label for="keyspaceInput">Keyspace Name</label> | ||||
|           <input type="text" id="keyspaceInput" placeholder="Enter keyspace name"> | ||||
|         </div> | ||||
|         <div class="form-group"> | ||||
|           <label for="passwordInput">Password</label> | ||||
|           <input type="password" id="passwordInput" placeholder="Enter password"> | ||||
|         </div> | ||||
|         <div class="button-group"> | ||||
|           <button id="createKeyspaceBtn" class="btn btn-secondary">Create New</button> | ||||
|           <button id="loginBtn" class="btn btn-primary">Unlock</button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </section> | ||||
|  | ||||
|     <!-- Main Vault Section --> | ||||
|     <section class="section hidden" id="vaultSection"> | ||||
|       <div class="vault-header"> | ||||
|         <h2>Your Keypairs</h2> | ||||
|         <button id="lockBtn" class="btn btn-ghost">🔒 Lock</button> | ||||
|       </div> | ||||
|  | ||||
|       <!-- Add Keypair Toggle Button --> | ||||
|       <div class="add-keypair-toggle"> | ||||
|         <button id="toggleAddKeypairBtn" class="btn btn-primary"> | ||||
|           <span class="btn-icon">+</span> | ||||
|           Add Keypair | ||||
|         </button> | ||||
|       </div> | ||||
|  | ||||
|       <!-- Add Keypair Form (Hidden by default) --> | ||||
|       <div class="card add-keypair-form hidden" id="addKeypairCard"> | ||||
|         <div class="form-header"> | ||||
|           <h3>Add New Keypair</h3> | ||||
|           <button id="cancelAddKeypairBtn" class="btn-close" title="Close">×</button> | ||||
|         </div> | ||||
|         <div class="form-content"> | ||||
|           <div class="form-group"> | ||||
|             <label for="keyTypeSelect">Key Type</label> | ||||
|             <select id="keyTypeSelect" class="select"> | ||||
|               <option value="Secp256k1">Secp256k1</option> | ||||
|               <option value="Ed25519">Ed25519</option> | ||||
|             </select> | ||||
|           </div> | ||||
|           <div class="form-group"> | ||||
|             <label for="keyNameInput">Keypair Name</label> | ||||
|             <input type="text" id="keyNameInput" placeholder="Enter a name for your keypair"> | ||||
|           </div> | ||||
|           <div class="form-actions"> | ||||
|             <button id="addKeypairBtn" class="btn btn-primary">Create Keypair</button> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <!-- Keypairs List --> | ||||
|       <div class="card"> | ||||
|         <h3>Keypairs</h3> | ||||
|         <div id="keypairsList" class="keypairs-list"> | ||||
|           <div class="loading">Loading keypairs...</div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <!-- Selected Keypair Info - Hidden from user --> | ||||
|       <div class="card hidden completely-hidden" id="selectedKeypairCard"> | ||||
|         <h3>Selected Keypair</h3> | ||||
|         <div class="keypair-info"> | ||||
|           <div class="info-row"> | ||||
|             <label>Name:</label> | ||||
|             <span id="selectedName">-</span> | ||||
|           </div> | ||||
|           <div class="info-row"> | ||||
|             <label>Type:</label> | ||||
|             <span id="selectedType">-</span> | ||||
|           </div> | ||||
|           <div class="info-row"> | ||||
|             <label>Public Key:</label> | ||||
|             <div class="public-key-container"> | ||||
|               <code id="selectedPublicKey">-</code> | ||||
|               <button id="copyPublicKeyBtn" class="btn-copy" title="Copy to clipboard">📋</button> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <!-- Sign Message --> | ||||
|       <div class="card"> | ||||
|         <h3>Sign Message</h3> | ||||
|         <div class="form-group"> | ||||
|           <label for="messageInput">Message (hex or text)</label> | ||||
|           <textarea id="messageInput" placeholder="Enter message to sign" rows="3"></textarea> | ||||
|         </div> | ||||
|         <button id="signBtn" class="btn btn-primary" disabled>Sign Message</button> | ||||
|         <div id="signatureResult" class="signature-result hidden"> | ||||
|           <label>Signature:</label> | ||||
|           <div class="signature-container"> | ||||
|             <code id="signatureValue"></code> | ||||
|             <button id="copySignatureBtn" class="btn-copy" title="Copy to clipboard">📋</button> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </section> | ||||
|  | ||||
|     <!-- Loading Overlay --> | ||||
|     <div class="loading-overlay hidden" id="loadingOverlay"> | ||||
|       <div class="spinner"></div> | ||||
|       <p>Processing...</p> | ||||
|     </div> | ||||
|  | ||||
|     <!-- Toast Notifications --> | ||||
|     <div id="toast" class="toast hidden"></div> | ||||
|   </div> | ||||
|  | ||||
|   <script src="popup.js"></script> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										648
									
								
								crypto_vault_extension/popup.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										648
									
								
								crypto_vault_extension/popup.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,648 @@ | ||||
| // Utility functions | ||||
| function showToast(message, type = 'info') { | ||||
|   const toast = document.getElementById('toast'); | ||||
|   toast.textContent = message; | ||||
|   toast.className = `toast ${type}`; | ||||
|   setTimeout(() => toast.classList.add('hidden'), 3000); | ||||
| } | ||||
|  | ||||
| function showLoading(show = true) { | ||||
|   const overlay = document.getElementById('loadingOverlay'); | ||||
|   overlay.classList.toggle('hidden', !show); | ||||
| } | ||||
|  | ||||
| // Enhanced loading states for buttons | ||||
| function setButtonLoading(button, loading = true, originalText = null) { | ||||
|   if (loading) { | ||||
|     button.dataset.originalText = button.textContent; | ||||
|     button.classList.add('loading'); | ||||
|     button.disabled = true; | ||||
|   } else { | ||||
|     button.classList.remove('loading'); | ||||
|     button.disabled = false; | ||||
|     if (originalText) { | ||||
|       button.textContent = originalText; | ||||
|     } else if (button.dataset.originalText) { | ||||
|       button.textContent = button.dataset.originalText; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Show inline loading for specific operations | ||||
| function showInlineLoading(element, message = 'Processing...') { | ||||
|   element.innerHTML = ` | ||||
|     <div class="inline-loading"> | ||||
|       <div class="inline-spinner"></div> | ||||
|       <span>${message}</span> | ||||
|     </div> | ||||
|   `; | ||||
| } | ||||
|  | ||||
| // Enhanced error handling utility | ||||
| function getErrorMessage(error, fallback = 'An unexpected error occurred') { | ||||
|   if (!error) return fallback; | ||||
|  | ||||
|   // If it's a string, return it | ||||
|   if (typeof error === 'string') { | ||||
|     return error.trim() || fallback; | ||||
|   } | ||||
|  | ||||
|   // If it's an Error object | ||||
|   if (error instanceof Error) { | ||||
|     return error.message || fallback; | ||||
|   } | ||||
|  | ||||
|   // If it's an object with error property | ||||
|   if (error.error) { | ||||
|     return getErrorMessage(error.error, fallback); | ||||
|   } | ||||
|  | ||||
|   // If it's an object with message property | ||||
|   if (error.message) { | ||||
|     return error.message || fallback; | ||||
|   } | ||||
|  | ||||
|   // Try to stringify if it's an object | ||||
|   if (typeof error === 'object') { | ||||
|     try { | ||||
|       const stringified = JSON.stringify(error); | ||||
|       if (stringified && stringified !== '{}') { | ||||
|         return stringified; | ||||
|       } | ||||
|     } catch (e) { | ||||
|       // Ignore JSON stringify errors | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return fallback; | ||||
| } | ||||
|  | ||||
| // Enhanced response error handling | ||||
| function getResponseError(response, operation = 'operation') { | ||||
|   if (!response) { | ||||
|     return `Failed to ${operation}: No response received`; | ||||
|   } | ||||
|  | ||||
|   if (response.success === false || response.error) { | ||||
|     const errorMsg = getErrorMessage(response.error, `${operation} failed`); | ||||
|  | ||||
|     // Handle specific error types | ||||
|     if (errorMsg.includes('decryption error') || errorMsg.includes('aead::Error')) { | ||||
|       return 'Invalid password or corrupted keyspace data'; | ||||
|     } | ||||
|  | ||||
|     if (errorMsg.includes('Crypto error')) { | ||||
|       return 'Keyspace not found or corrupted. Try creating a new one.'; | ||||
|     } | ||||
|  | ||||
|     if (errorMsg.includes('not unlocked') || errorMsg.includes('session')) { | ||||
|       return 'Session expired. Please login again.'; | ||||
|     } | ||||
|  | ||||
|     return errorMsg; | ||||
|   } | ||||
|  | ||||
|   return `Failed to ${operation}: Unknown error`; | ||||
| } | ||||
|  | ||||
| function showSection(sectionId) { | ||||
|   document.querySelectorAll('.section').forEach(s => s.classList.add('hidden')); | ||||
|   document.getElementById(sectionId).classList.remove('hidden'); | ||||
| } | ||||
|  | ||||
| function setStatus(text, isConnected = false) { | ||||
|   document.getElementById('statusText').textContent = text; | ||||
|   const indicator = document.getElementById('statusIndicator'); | ||||
|   indicator.classList.toggle('connected', isConnected); | ||||
| } | ||||
|  | ||||
| // Message handling | ||||
| async function sendMessage(action, data = {}) { | ||||
|   return new Promise((resolve) => { | ||||
|     chrome.runtime.sendMessage({ action, ...data }, resolve); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // Copy to clipboard | ||||
| async function copyToClipboard(text) { | ||||
|   try { | ||||
|     await navigator.clipboard.writeText(text); | ||||
|     showToast('Copied to clipboard!', 'success'); | ||||
|   } catch (err) { | ||||
|     showToast('Failed to copy', 'error'); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Convert string to Uint8Array | ||||
| function stringToUint8Array(str) { | ||||
|   if (str.match(/^[0-9a-fA-F]+$/)) { | ||||
|     // Hex string | ||||
|     const bytes = []; | ||||
|     for (let i = 0; i < str.length; i += 2) { | ||||
|       bytes.push(parseInt(str.substr(i, 2), 16)); | ||||
|     } | ||||
|     return bytes; | ||||
|   } else { | ||||
|     // Regular string | ||||
|     return Array.from(new TextEncoder().encode(str)); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // DOM Elements | ||||
| const elements = { | ||||
|   keyspaceInput: document.getElementById('keyspaceInput'), | ||||
|   passwordInput: document.getElementById('passwordInput'), | ||||
|   createKeyspaceBtn: document.getElementById('createKeyspaceBtn'), | ||||
|   loginBtn: document.getElementById('loginBtn'), | ||||
|   lockBtn: document.getElementById('lockBtn'), | ||||
|   toggleAddKeypairBtn: document.getElementById('toggleAddKeypairBtn'), | ||||
|   addKeypairCard: document.getElementById('addKeypairCard'), | ||||
|   keyTypeSelect: document.getElementById('keyTypeSelect'), | ||||
|   keyNameInput: document.getElementById('keyNameInput'), | ||||
|   addKeypairBtn: document.getElementById('addKeypairBtn'), | ||||
|   cancelAddKeypairBtn: document.getElementById('cancelAddKeypairBtn'), | ||||
|   keypairsList: document.getElementById('keypairsList'), | ||||
|   selectedKeypairCard: document.getElementById('selectedKeypairCard'), | ||||
|   messageInput: document.getElementById('messageInput'), | ||||
|   signBtn: document.getElementById('signBtn'), | ||||
|   signatureResult: document.getElementById('signatureResult'), | ||||
|   copyPublicKeyBtn: document.getElementById('copyPublicKeyBtn'), | ||||
|   copySignatureBtn: document.getElementById('copySignatureBtn'), | ||||
| }; | ||||
|  | ||||
| let currentKeyspace = null; | ||||
| let selectedKeypairId = null; | ||||
|  | ||||
| // Initialize | ||||
| document.addEventListener('DOMContentLoaded', async function() { | ||||
|   setStatus('Initializing...', false); | ||||
|  | ||||
|   // Event listeners | ||||
|   elements.createKeyspaceBtn.addEventListener('click', createKeyspace); | ||||
|   elements.loginBtn.addEventListener('click', login); | ||||
|   elements.lockBtn.addEventListener('click', lockSession); | ||||
|   elements.toggleAddKeypairBtn.addEventListener('click', toggleAddKeypairForm); | ||||
|   elements.addKeypairBtn.addEventListener('click', addKeypair); | ||||
|   elements.cancelAddKeypairBtn.addEventListener('click', hideAddKeypairForm); | ||||
|   elements.signBtn.addEventListener('click', signMessage); | ||||
|   elements.copyPublicKeyBtn.addEventListener('click', () => { | ||||
|     const publicKey = document.getElementById('selectedPublicKey').textContent; | ||||
|     copyToClipboard(publicKey); | ||||
|   }); | ||||
|   elements.copySignatureBtn.addEventListener('click', () => { | ||||
|     const signature = document.getElementById('signatureValue').textContent; | ||||
|     copyToClipboard(signature); | ||||
|   }); | ||||
|  | ||||
|   // Enable sign button when message is entered | ||||
|   elements.messageInput.addEventListener('input', () => { | ||||
|     elements.signBtn.disabled = !elements.messageInput.value.trim() || !selectedKeypairId; | ||||
|   }); | ||||
|  | ||||
|   // Keyboard shortcuts | ||||
|   document.addEventListener('keydown', (e) => { | ||||
|     // Escape key closes the add keypair form | ||||
|     if (e.key === 'Escape' && !elements.addKeypairCard.classList.contains('hidden')) { | ||||
|       hideAddKeypairForm(); | ||||
|     } | ||||
|     // Enter key in the name input submits the form | ||||
|     if (e.key === 'Enter' && e.target === elements.keyNameInput) { | ||||
|       e.preventDefault(); | ||||
|       if (elements.keyNameInput.value.trim()) { | ||||
|         addKeypair(); | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   // Check for existing session | ||||
|   await checkExistingSession(); | ||||
| }); | ||||
|  | ||||
| async function checkExistingSession() { | ||||
|   try { | ||||
|     const response = await sendMessage('getStatus'); | ||||
|     if (response && response.success && response.status && response.session) { | ||||
|       // Session is active | ||||
|       currentKeyspace = response.session.keyspace; | ||||
|       elements.keyspaceInput.value = currentKeyspace; | ||||
|       setStatus(`Connected to ${currentKeyspace}`, true); | ||||
|       showSection('vaultSection'); | ||||
|       await loadKeypairs(); | ||||
|       showToast('Session restored!', 'success'); | ||||
|     } else { | ||||
|       // No active session | ||||
|       setStatus('Ready', true); | ||||
|       showSection('authSection'); | ||||
|     } | ||||
|   } catch (error) { | ||||
|     console.error('Error checking session:', error); | ||||
|     setStatus('Ready', true); | ||||
|     showSection('authSection'); | ||||
|     // Don't show toast for session check errors as it's not user-initiated | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Toggle add keypair form | ||||
| function toggleAddKeypairForm() { | ||||
|   const isHidden = elements.addKeypairCard.classList.contains('hidden'); | ||||
|   if (isHidden) { | ||||
|     showAddKeypairForm(); | ||||
|   } else { | ||||
|     hideAddKeypairForm(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function showAddKeypairForm() { | ||||
|   elements.addKeypairCard.classList.remove('hidden'); | ||||
|  | ||||
|   // Rotate the + icon to × when form is open | ||||
|   const icon = elements.toggleAddKeypairBtn.querySelector('.btn-icon'); | ||||
|   icon.style.transform = 'rotate(45deg)'; | ||||
|  | ||||
|   // Focus on the name input after animation | ||||
|   setTimeout(() => { | ||||
|     elements.keyNameInput.focus(); | ||||
|   }, 300); | ||||
| } | ||||
|  | ||||
| function hideAddKeypairForm() { | ||||
|   elements.addKeypairCard.classList.add('hidden'); | ||||
|  | ||||
|   // Rotate the icon back to + | ||||
|   const icon = elements.toggleAddKeypairBtn.querySelector('.btn-icon'); | ||||
|   icon.style.transform = 'rotate(0deg)'; | ||||
|  | ||||
|   // Clear the form | ||||
|   elements.keyNameInput.value = ''; | ||||
|   elements.keyTypeSelect.selectedIndex = 0; | ||||
| } | ||||
|  | ||||
| // Clear all vault-related state and UI | ||||
| function clearVaultState() { | ||||
|   // Clear message input and signature result | ||||
|   elements.messageInput.value = ''; | ||||
|   elements.signatureResult.classList.add('hidden'); | ||||
|   document.getElementById('signatureValue').textContent = ''; | ||||
|  | ||||
|   // Clear selected keypair state | ||||
|   selectedKeypairId = null; | ||||
|   elements.signBtn.disabled = true; | ||||
|  | ||||
|   // Clear selected keypair info (hidden elements) | ||||
|   document.getElementById('selectedName').textContent = '-'; | ||||
|   document.getElementById('selectedType').textContent = '-'; | ||||
|   document.getElementById('selectedPublicKey').textContent = '-'; | ||||
|  | ||||
|   // Hide add keypair form if open | ||||
|   hideAddKeypairForm(); | ||||
|  | ||||
|   // Clear keypairs list | ||||
|   elements.keypairsList.innerHTML = '<div class="loading">Loading keypairs...</div>'; | ||||
| } | ||||
|  | ||||
| async function createKeyspace() { | ||||
|   const keyspace = elements.keyspaceInput.value.trim(); | ||||
|   const password = elements.passwordInput.value.trim(); | ||||
|  | ||||
|   if (!keyspace || !password) { | ||||
|     showToast('Please enter keyspace name and password', 'error'); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   setButtonLoading(elements.createKeyspaceBtn, true); | ||||
|   try { | ||||
|     const response = await sendMessage('createKeyspace', { keyspace, password }); | ||||
|     if (response && response.success) { | ||||
|       showToast('Keyspace created successfully!', 'success'); | ||||
|       // Clear any existing state before auto-login | ||||
|       clearVaultState(); | ||||
|       await login(); // Auto-login after creation | ||||
|     } else { | ||||
|       const errorMsg = getResponseError(response, 'create keyspace'); | ||||
|       showToast(errorMsg, 'error'); | ||||
|     } | ||||
|   } catch (error) { | ||||
|     const errorMsg = getErrorMessage(error, 'Failed to create keyspace'); | ||||
|     console.error('Create keyspace error:', error); | ||||
|     showToast(errorMsg, 'error'); | ||||
|   } finally { | ||||
|     setButtonLoading(elements.createKeyspaceBtn, false); | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function login() { | ||||
|   const keyspace = elements.keyspaceInput.value.trim(); | ||||
|   const password = elements.passwordInput.value.trim(); | ||||
|  | ||||
|   if (!keyspace || !password) { | ||||
|     showToast('Please enter keyspace name and password', 'error'); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   setButtonLoading(elements.loginBtn, true); | ||||
|   try { | ||||
|     const response = await sendMessage('initSession', { keyspace, password }); | ||||
|     if (response && response.success) { | ||||
|       currentKeyspace = keyspace; | ||||
|       setStatus(`Connected to ${keyspace}`, true); | ||||
|       showSection('vaultSection'); | ||||
|  | ||||
|       // Clear any previous vault state before loading new keyspace | ||||
|       clearVaultState(); | ||||
|       await loadKeypairs(); | ||||
|  | ||||
|       showToast('Logged in successfully!', 'success'); | ||||
|     } else { | ||||
|       const errorMsg = getResponseError(response, 'login'); | ||||
|       showToast(errorMsg, 'error'); | ||||
|     } | ||||
|   } catch (error) { | ||||
|     const errorMsg = getErrorMessage(error, 'Failed to login'); | ||||
|     console.error('Login error:', error); | ||||
|     showToast(errorMsg, 'error'); | ||||
|   } finally { | ||||
|     setButtonLoading(elements.loginBtn, false); | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function lockSession() { | ||||
|   showLoading(true); | ||||
|   try { | ||||
|     await sendMessage('lockSession'); | ||||
|     currentKeyspace = null; | ||||
|     selectedKeypairId = null; | ||||
|     setStatus('Locked', false); | ||||
|     showSection('authSection'); | ||||
|  | ||||
|     // Clear all form inputs | ||||
|     elements.keyspaceInput.value = ''; | ||||
|     elements.passwordInput.value = ''; | ||||
|     clearVaultState(); | ||||
|  | ||||
|     showToast('Session locked', 'info'); | ||||
|   } catch (error) { | ||||
|     showToast('Error: ' + error.message, 'error'); | ||||
|   } finally { | ||||
|     showLoading(false); | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function addKeypair() { | ||||
|   const keyType = elements.keyTypeSelect.value; | ||||
|   const keyName = elements.keyNameInput.value.trim(); | ||||
|  | ||||
|   if (!keyName) { | ||||
|     showToast('Please enter a name for the keypair', 'error'); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   // Use button loading instead of full overlay | ||||
|   setButtonLoading(elements.addKeypairBtn, true); | ||||
|  | ||||
|   try { | ||||
|     console.log('Adding keypair:', { keyType, keyName }); | ||||
|     const metadata = JSON.stringify({ name: keyName }); | ||||
|     console.log('Metadata:', metadata); | ||||
|  | ||||
|     const response = await sendMessage('addKeypair', { keyType, metadata }); | ||||
|     console.log('Add keypair response:', response); | ||||
|  | ||||
|     if (response && response.success) { | ||||
|       console.log('Keypair added successfully, clearing input and reloading list...'); | ||||
|       hideAddKeypairForm(); // Hide the form after successful addition | ||||
|  | ||||
|       // Show inline loading in keypairs list while reloading | ||||
|       showInlineLoading(elements.keypairsList, 'Adding keypair...'); | ||||
|       await loadKeypairs(); | ||||
|  | ||||
|       showToast('Keypair added successfully!', 'success'); | ||||
|     } else { | ||||
|       const errorMsg = getResponseError(response, 'add keypair'); | ||||
|       console.error('Failed to add keypair:', response); | ||||
|       showToast(errorMsg, 'error'); | ||||
|     } | ||||
|   } catch (error) { | ||||
|     const errorMsg = getErrorMessage(error, 'Failed to add keypair'); | ||||
|     console.error('Error adding keypair:', error); | ||||
|     showToast(errorMsg, 'error'); | ||||
|   } finally { | ||||
|     setButtonLoading(elements.addKeypairBtn, false); | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function loadKeypairs() { | ||||
|   try { | ||||
|     console.log('Loading keypairs...'); | ||||
|     const response = await sendMessage('listKeypairs'); | ||||
|     console.log('Keypairs response:', response); | ||||
|  | ||||
|     if (response && response.success) { | ||||
|       console.log('Keypairs data:', response.keypairs); | ||||
|       console.log('Keypairs data type:', typeof response.keypairs); | ||||
|       renderKeypairs(response.keypairs); | ||||
|     } else { | ||||
|       const errorMsg = getResponseError(response, 'load keypairs'); | ||||
|       console.error('Failed to load keypairs:', response); | ||||
|       const container = elements.keypairsList; | ||||
|       container.innerHTML = '<div class="empty-state">Failed to load keypairs. Try refreshing.</div>'; | ||||
|       showToast(errorMsg, 'error'); | ||||
|     } | ||||
|   } catch (error) { | ||||
|     const errorMsg = getErrorMessage(error, 'Failed to load keypairs'); | ||||
|     console.error('Error loading keypairs:', error); | ||||
|     const container = elements.keypairsList; | ||||
|     container.innerHTML = '<div class="empty-state">Error loading keypairs. Try refreshing.</div>'; | ||||
|     showToast(errorMsg, 'error'); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function renderKeypairs(keypairs) { | ||||
|   console.log('Rendering keypairs:', keypairs); | ||||
|   console.log('Keypairs type:', typeof keypairs); | ||||
|   console.log('Keypairs is array:', Array.isArray(keypairs)); | ||||
|  | ||||
|   const container = elements.keypairsList; | ||||
|  | ||||
|   // Handle different data types that might be returned | ||||
|   let keypairArray = []; | ||||
|  | ||||
|   if (Array.isArray(keypairs)) { | ||||
|     keypairArray = keypairs; | ||||
|   } else if (keypairs && typeof keypairs === 'object') { | ||||
|     // If it's an object, try to extract array from common properties | ||||
|     if (keypairs.keypairs && Array.isArray(keypairs.keypairs)) { | ||||
|       keypairArray = keypairs.keypairs; | ||||
|     } else if (keypairs.data && Array.isArray(keypairs.data)) { | ||||
|       keypairArray = keypairs.data; | ||||
|     } else { | ||||
|       console.log('Keypairs object structure:', Object.keys(keypairs)); | ||||
|       // Try to convert object to array if it has numeric keys | ||||
|       const keys = Object.keys(keypairs); | ||||
|       if (keys.length > 0 && keys.every(key => !isNaN(key))) { | ||||
|         keypairArray = Object.values(keypairs); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   console.log('Final keypair array:', keypairArray); | ||||
|   console.log('Array length:', keypairArray.length); | ||||
|  | ||||
|   if (!keypairArray || keypairArray.length === 0) { | ||||
|     console.log('No keypairs to render'); | ||||
|     container.innerHTML = '<div class="empty-state">No keypairs found. Add one above.</div>'; | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   console.log('Rendering', keypairArray.length, 'keypairs'); | ||||
|   container.innerHTML = keypairArray.map((keypair, index) => { | ||||
|     console.log('Processing keypair:', keypair); | ||||
|     const metadata = typeof keypair.metadata === 'string' | ||||
|       ? JSON.parse(keypair.metadata) | ||||
|       : keypair.metadata; | ||||
|  | ||||
|     return ` | ||||
|       <div class="keypair-item" data-id="${keypair.id}"> | ||||
|         <div class="keypair-info"> | ||||
|           <div class="keypair-name">${metadata.name || 'Unnamed'}</div> | ||||
|           <div class="keypair-type">${keypair.key_type}</div> | ||||
|         </div> | ||||
|         <button class="btn btn-small select-btn" data-keypair-id="${keypair.id}" data-index="${index}"> | ||||
|           Select | ||||
|         </button> | ||||
|       </div> | ||||
|     `; | ||||
|   }).join(''); | ||||
|  | ||||
|   // Add event listeners to all select buttons | ||||
|   const selectButtons = container.querySelectorAll('.select-btn'); | ||||
|   selectButtons.forEach(button => { | ||||
|     button.addEventListener('click', (e) => { | ||||
|       e.preventDefault(); // Prevent any default button behavior | ||||
|       e.stopPropagation(); // Stop event bubbling | ||||
|  | ||||
|       const keypairId = e.target.getAttribute('data-keypair-id'); | ||||
|       console.log('Select button clicked for keypair:', keypairId); | ||||
|       selectKeypair(keypairId); | ||||
|     }); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| async function selectKeypair(keyId) { | ||||
|   console.log('Selecting keypair:', keyId); | ||||
|  | ||||
|   // Don't show loading overlay for selection - it's too disruptive | ||||
|   try { | ||||
|     // Update visual state immediately for better UX | ||||
|     updateKeypairSelection(keyId); | ||||
|  | ||||
|     await sendMessage('selectKeypair', { keyId }); | ||||
|     selectedKeypairId = keyId; | ||||
|  | ||||
|     // Get keypair details for internal use (but don't show the card) | ||||
|     const metadataResponse = await sendMessage('getCurrentKeypairMetadata'); | ||||
|     const publicKeyResponse = await sendMessage('getCurrentKeypairPublicKey'); | ||||
|  | ||||
|     if (metadataResponse && metadataResponse.success && publicKeyResponse && publicKeyResponse.success) { | ||||
|       const metadata = metadataResponse.metadata; | ||||
|  | ||||
|       // Store the details in hidden elements for internal use | ||||
|       document.getElementById('selectedName').textContent = metadata.name || 'Unnamed'; | ||||
|       document.getElementById('selectedType').textContent = metadata.key_type; | ||||
|       document.getElementById('selectedPublicKey').textContent = publicKeyResponse.publicKey; | ||||
|  | ||||
|       // Enable sign button if message is entered | ||||
|       elements.signBtn.disabled = !elements.messageInput.value.trim(); | ||||
|  | ||||
|       // Show a subtle success message without toast | ||||
|       console.log(`Keypair "${metadata.name}" selected successfully`); | ||||
|     } else { | ||||
|       // Handle metadata or public key fetch failure | ||||
|       const metadataError = getResponseError(metadataResponse, 'get keypair metadata'); | ||||
|       const publicKeyError = getResponseError(publicKeyResponse, 'get public key'); | ||||
|       const errorMsg = metadataResponse && !metadataResponse.success ? metadataError : publicKeyError; | ||||
|  | ||||
|       console.error('Failed to get keypair details:', { metadataResponse, publicKeyResponse }); | ||||
|       updateKeypairSelection(null); | ||||
|       showToast(errorMsg, 'error'); | ||||
|     } | ||||
|   } catch (error) { | ||||
|     const errorMsg = getErrorMessage(error, 'Failed to select keypair'); | ||||
|     console.error('Error selecting keypair:', error); | ||||
|     // Revert visual state if there was an error | ||||
|     updateKeypairSelection(null); | ||||
|     showToast(errorMsg, 'error'); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function updateKeypairSelection(selectedId) { | ||||
|   // Remove previous selection styling | ||||
|   const allKeypairs = document.querySelectorAll('.keypair-item'); | ||||
|   allKeypairs.forEach(item => { | ||||
|     item.classList.remove('selected'); | ||||
|     const button = item.querySelector('.select-btn'); | ||||
|     button.textContent = 'Select'; | ||||
|     button.classList.remove('selected'); | ||||
|   }); | ||||
|  | ||||
|   // Add selection styling to the selected keypair (if any) | ||||
|   if (selectedId) { | ||||
|     const selectedKeypair = document.querySelector(`[data-id="${selectedId}"]`); | ||||
|     if (selectedKeypair) { | ||||
|       selectedKeypair.classList.add('selected'); | ||||
|       const button = selectedKeypair.querySelector('.select-btn'); | ||||
|       button.textContent = 'Selected'; | ||||
|       button.classList.add('selected'); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function signMessage() { | ||||
|   const messageText = elements.messageInput.value.trim(); | ||||
|   if (!messageText || !selectedKeypairId) { | ||||
|     showToast('Please enter a message and select a keypair', 'error'); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   // Use button loading and show inline loading in signature area | ||||
|   setButtonLoading(elements.signBtn, true); | ||||
|  | ||||
|   // Show loading in signature result area | ||||
|   elements.signatureResult.classList.remove('hidden'); | ||||
|   showInlineLoading(elements.signatureResult, 'Signing message...'); | ||||
|  | ||||
|   try { | ||||
|     const messageBytes = stringToUint8Array(messageText); | ||||
|     const response = await sendMessage('sign', { message: messageBytes }); | ||||
|  | ||||
|     if (response && response.success) { | ||||
|       // Restore signature result structure and show signature | ||||
|       elements.signatureResult.innerHTML = ` | ||||
|         <label>Signature:</label> | ||||
|         <div class="signature-container"> | ||||
|           <code id="signatureValue">${response.signature}</code> | ||||
|           <button id="copySignatureBtn" class="btn-copy" title="Copy to clipboard">📋</button> | ||||
|         </div> | ||||
|       `; | ||||
|  | ||||
|       // Re-attach copy event listener | ||||
|       document.getElementById('copySignatureBtn').addEventListener('click', () => { | ||||
|         copyToClipboard(response.signature); | ||||
|       }); | ||||
|  | ||||
|       showToast('Message signed successfully!', 'success'); | ||||
|     } else { | ||||
|       const errorMsg = getResponseError(response, 'sign message'); | ||||
|       elements.signatureResult.classList.add('hidden'); | ||||
|       showToast(errorMsg, 'error'); | ||||
|     } | ||||
|   } catch (error) { | ||||
|     const errorMsg = getErrorMessage(error, 'Failed to sign message'); | ||||
|     console.error('Sign message error:', error); | ||||
|     elements.signatureResult.classList.add('hidden'); | ||||
|     showToast(errorMsg, 'error'); | ||||
|   } finally { | ||||
|     setButtonLoading(elements.signBtn, false); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // selectKeypair is now handled via event listeners, no need for global access | ||||
							
								
								
									
										654
									
								
								crypto_vault_extension/styles/popup.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										654
									
								
								crypto_vault_extension/styles/popup.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,654 @@ | ||||
| * { | ||||
|   margin: 0; | ||||
|   padding: 0; | ||||
|   box-sizing: border-box; | ||||
| } | ||||
|  | ||||
| body { | ||||
|   width: 400px; | ||||
|   min-height: 600px; | ||||
|   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | ||||
|   background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | ||||
|   background-attachment: fixed; | ||||
|   color: #333; | ||||
|   line-height: 1.6; | ||||
|   margin: 0; | ||||
|   padding: 0; | ||||
|   overflow-x: hidden; | ||||
| } | ||||
|  | ||||
| .container { | ||||
|   position: relative; | ||||
|   min-height: 100vh; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
| } | ||||
|  | ||||
| /* Header */ | ||||
| .header { | ||||
|   background: rgba(255, 255, 255, 0.95); | ||||
|   backdrop-filter: blur(20px); | ||||
|   padding: 20px; | ||||
|   border-bottom: 1px solid rgba(255, 255, 255, 0.3); | ||||
| } | ||||
|  | ||||
| .logo { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   margin-bottom: 12px; | ||||
| } | ||||
|  | ||||
| .logo-icon { | ||||
|   font-size: 24px; | ||||
|   margin-right: 12px; | ||||
| } | ||||
|  | ||||
| .logo h1 { | ||||
|   font-size: 20px; | ||||
|   font-weight: 600; | ||||
|   color: #2d3748; | ||||
| } | ||||
|  | ||||
| .status-indicator { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   font-size: 14px; | ||||
|   color: #666; | ||||
| } | ||||
|  | ||||
| .status-dot { | ||||
|   width: 8px; | ||||
|   height: 8px; | ||||
|   border-radius: 50%; | ||||
|   background-color: #fbbf24; | ||||
|   margin-right: 8px; | ||||
|   animation: pulse 2s infinite; | ||||
| } | ||||
|  | ||||
| .status-indicator.connected .status-dot { | ||||
|   background-color: #10b981; | ||||
|   animation: none; | ||||
| } | ||||
|  | ||||
| @keyframes pulse { | ||||
|   0%, 100% { opacity: 1; } | ||||
|   50% { opacity: 0.5; } | ||||
| } | ||||
|  | ||||
| /* Sections */ | ||||
| .section { | ||||
|   padding: 20px; | ||||
|   flex: 1; | ||||
| } | ||||
|  | ||||
| .section:last-child { | ||||
|   padding-bottom: 20px; | ||||
| } | ||||
|  | ||||
| .section.hidden { | ||||
|   display: none; | ||||
| } | ||||
|  | ||||
| .completely-hidden { | ||||
|   display: none !important; | ||||
| } | ||||
|  | ||||
| /* Cards */ | ||||
| .card { | ||||
|   background: rgba(255, 255, 255, 0.95); | ||||
|   backdrop-filter: blur(20px); | ||||
|   border-radius: 16px; | ||||
|   padding: 20px; | ||||
|   margin-bottom: 16px; | ||||
|   border: 1px solid rgba(255, 255, 255, 0.3); | ||||
|   box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); | ||||
| } | ||||
|  | ||||
| .card h2, .card h3 { | ||||
|   margin-bottom: 16px; | ||||
|   color: #2d3748; | ||||
|   font-weight: 600; | ||||
| } | ||||
|  | ||||
| .card h2 { | ||||
|   font-size: 18px; | ||||
| } | ||||
|  | ||||
| .card h3 { | ||||
|   font-size: 16px; | ||||
| } | ||||
|  | ||||
| /* Forms */ | ||||
| .form-group { | ||||
|   margin-bottom: 16px; | ||||
| } | ||||
|  | ||||
| .form-row { | ||||
|   display: flex; | ||||
|   gap: 8px; | ||||
|   align-items: end; | ||||
| } | ||||
|  | ||||
| label { | ||||
|   display: block; | ||||
|   margin-bottom: 6px; | ||||
|   font-weight: 500; | ||||
|   color: #4a5568; | ||||
|   font-size: 14px; | ||||
| } | ||||
|  | ||||
| input, select, textarea { | ||||
|   width: 100%; | ||||
|   padding: 12px 16px; | ||||
|   border: 2px solid rgba(255, 255, 255, 0.3); | ||||
|   border-radius: 12px; | ||||
|   background: rgba(255, 255, 255, 0.8); | ||||
|   font-size: 14px; | ||||
|   transition: all 0.2s ease; | ||||
| } | ||||
|  | ||||
| input:focus, select:focus, textarea:focus { | ||||
|   outline: none; | ||||
|   border-color: #667eea; | ||||
|   background: rgba(255, 255, 255, 0.95); | ||||
|   box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); | ||||
| } | ||||
|  | ||||
| .select { | ||||
|   flex: 1; | ||||
| } | ||||
|  | ||||
| /* Buttons */ | ||||
| .btn { | ||||
|   padding: 12px 24px; | ||||
|   border: none; | ||||
|   border-radius: 12px; | ||||
|   font-size: 14px; | ||||
|   font-weight: 500; | ||||
|   cursor: pointer; | ||||
|   transition: all 0.2s ease; | ||||
|   text-decoration: none; | ||||
|   display: inline-flex; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
| } | ||||
|  | ||||
| .btn-primary { | ||||
|   background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | ||||
|   color: white; | ||||
| } | ||||
|  | ||||
| .btn-primary:hover { | ||||
|   transform: translateY(-1px); | ||||
|   box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3); | ||||
| } | ||||
|  | ||||
| .btn-secondary { | ||||
|   background: rgba(255, 255, 255, 0.8); | ||||
|   color: #4a5568; | ||||
|   border: 2px solid rgba(102, 126, 234, 0.2); | ||||
| } | ||||
|  | ||||
| .btn-secondary:hover { | ||||
|   background: rgba(255, 255, 255, 0.95); | ||||
|   border-color: rgba(102, 126, 234, 0.4); | ||||
| } | ||||
|  | ||||
| .btn-ghost { | ||||
|   background: transparent; | ||||
|   color: #667eea; | ||||
|   border: 1px solid rgba(102, 126, 234, 0.3); | ||||
| } | ||||
|  | ||||
| .btn-ghost:hover { | ||||
|   background: rgba(102, 126, 234, 0.1); | ||||
| } | ||||
|  | ||||
| .btn-small { | ||||
|   padding: 8px 16px; | ||||
|   font-size: 12px; | ||||
| } | ||||
|  | ||||
| .btn:disabled { | ||||
|   opacity: 0.5; | ||||
|   cursor: not-allowed; | ||||
|   transform: none !important; | ||||
| } | ||||
|  | ||||
| .btn.loading { | ||||
|   position: relative; | ||||
|   color: transparent !important; | ||||
|   pointer-events: none; | ||||
| } | ||||
|  | ||||
| .btn.loading::after { | ||||
|   content: ''; | ||||
|   position: absolute; | ||||
|   top: 50%; | ||||
|   left: 50%; | ||||
|   width: 16px; | ||||
|   height: 16px; | ||||
|   margin: -8px 0 0 -8px; | ||||
|   border: 2px solid transparent; | ||||
|   border-top: 2px solid currentColor; | ||||
|   border-radius: 50%; | ||||
|   animation: btn-spin 0.8s linear infinite; | ||||
|   color: white; | ||||
| } | ||||
|  | ||||
| .btn-secondary.loading::after { | ||||
|   color: #4a5568; | ||||
| } | ||||
|  | ||||
| @keyframes btn-spin { | ||||
|   0% { transform: rotate(0deg); } | ||||
|   100% { transform: rotate(360deg); } | ||||
| } | ||||
|  | ||||
| /* Inline Loading Components */ | ||||
| .inline-loading { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
|   gap: 8px; | ||||
|   padding: 8px; | ||||
|   color: #666; | ||||
|   font-size: 14px; | ||||
| } | ||||
|  | ||||
| .inline-spinner { | ||||
|   width: 16px; | ||||
|   height: 16px; | ||||
|   border: 2px solid rgba(102, 126, 234, 0.2); | ||||
|   border-top: 2px solid #667eea; | ||||
|   border-radius: 50%; | ||||
|   animation: btn-spin 0.8s linear infinite; | ||||
| } | ||||
|  | ||||
| .button-group { | ||||
|   display: flex; | ||||
|   gap: 12px; | ||||
| } | ||||
|  | ||||
| .btn-copy { | ||||
|   background: transparent; | ||||
|   border: none; | ||||
|   cursor: pointer; | ||||
|   padding: 4px; | ||||
|   border-radius: 6px; | ||||
|   transition: background 0.2s ease; | ||||
| } | ||||
|  | ||||
| .btn-copy:hover { | ||||
|   background: rgba(102, 126, 234, 0.1); | ||||
| } | ||||
|  | ||||
| /* Vault Header */ | ||||
| .vault-header { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
|   margin-bottom: 20px; | ||||
| } | ||||
|  | ||||
| .vault-header h2 { | ||||
|   color: white; | ||||
|   font-size: 20px; | ||||
|   font-weight: 600; | ||||
| } | ||||
|  | ||||
| /* Add Keypair Toggle */ | ||||
| .add-keypair-toggle { | ||||
|   margin-bottom: 16px; | ||||
|   text-align: center; | ||||
| } | ||||
|  | ||||
| .btn-icon { | ||||
|   margin-right: 8px; | ||||
|   font-weight: bold; | ||||
|   transition: transform 0.2s ease; | ||||
| } | ||||
|  | ||||
| .add-keypair-form { | ||||
|   transform: translateY(-10px); | ||||
|   opacity: 0; | ||||
|   transition: all 0.3s ease; | ||||
|   max-height: 0; | ||||
|   overflow: hidden; | ||||
|   padding: 0; | ||||
|   margin-bottom: 0; | ||||
| } | ||||
|  | ||||
| .add-keypair-form:not(.hidden) { | ||||
|   transform: translateY(0); | ||||
|   opacity: 1; | ||||
|   max-height: 300px; | ||||
|   padding: 20px; | ||||
|   margin-bottom: 16px; | ||||
| } | ||||
|  | ||||
| .form-header { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
|   margin-bottom: 16px; | ||||
|   padding-bottom: 12px; | ||||
|   border-bottom: 1px solid rgba(102, 126, 234, 0.1); | ||||
| } | ||||
|  | ||||
| .form-header h3 { | ||||
|   margin: 0; | ||||
|   color: #2d3748; | ||||
| } | ||||
|  | ||||
| .btn-close { | ||||
|   background: none; | ||||
|   border: none; | ||||
|   font-size: 20px; | ||||
|   color: #666; | ||||
|   cursor: pointer; | ||||
|   padding: 4px 8px; | ||||
|   border-radius: 6px; | ||||
|   transition: all 0.2s ease; | ||||
|   line-height: 1; | ||||
| } | ||||
|  | ||||
| .btn-close:hover { | ||||
|   background: rgba(239, 68, 68, 0.1); | ||||
|   color: #ef4444; | ||||
| } | ||||
|  | ||||
| .form-content { | ||||
|   animation: slideInUp 0.3s ease-out; | ||||
| } | ||||
|  | ||||
| .form-actions { | ||||
|   margin-top: 16px; | ||||
|   display: flex; | ||||
|   justify-content: flex-end; | ||||
|   gap: 8px; | ||||
| } | ||||
|  | ||||
| /* Keypairs List */ | ||||
| .keypairs-list { | ||||
|   max-height: 200px; | ||||
|   overflow-y: auto; | ||||
|   overflow-x: hidden; | ||||
| } | ||||
|  | ||||
| .keypair-item { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
|   padding: 12px; | ||||
|   border-radius: 8px; | ||||
|   margin-bottom: 8px; | ||||
|   background: rgba(102, 126, 234, 0.05); | ||||
|   border: 1px solid rgba(102, 126, 234, 0.1); | ||||
|   transition: all 0.2s ease; | ||||
|   min-width: 0; /* Allow flex items to shrink */ | ||||
| } | ||||
|  | ||||
| .keypair-item:hover { | ||||
|   background: rgba(102, 126, 234, 0.1); | ||||
|   transform: translateX(4px); | ||||
| } | ||||
|  | ||||
| .keypair-item.selected { | ||||
|   background: rgba(16, 185, 129, 0.1); | ||||
|   border-color: rgba(16, 185, 129, 0.3); | ||||
|   transform: translateX(4px); | ||||
| } | ||||
|  | ||||
| .keypair-item.selected .keypair-name { | ||||
|   color: #065f46; | ||||
|   font-weight: 600; | ||||
| } | ||||
|  | ||||
| .keypair-item.selected .select-btn { | ||||
|   background: rgba(16, 185, 129, 0.2); | ||||
|   color: #065f46; | ||||
|   border-color: rgba(16, 185, 129, 0.3); | ||||
| } | ||||
|  | ||||
| .keypair-item.selected .select-btn:hover { | ||||
|   background: rgba(16, 185, 129, 0.3); | ||||
| } | ||||
|  | ||||
| .keypair-info { | ||||
|   flex: 1; | ||||
|   min-width: 0; /* Allow shrinking */ | ||||
|   margin-right: 12px; | ||||
| } | ||||
|  | ||||
| .keypair-name { | ||||
|   font-weight: 500; | ||||
|   color: #2d3748; | ||||
|   word-break: break-word; /* Break long names */ | ||||
|   overflow: hidden; | ||||
|   text-overflow: ellipsis; | ||||
| } | ||||
|  | ||||
| .keypair-type { | ||||
|   font-size: 12px; | ||||
|   color: #666; | ||||
|   background: rgba(102, 126, 234, 0.1); | ||||
|   padding: 2px 8px; | ||||
|   border-radius: 12px; | ||||
|   margin-top: 4px; | ||||
| } | ||||
|  | ||||
| .empty-state, .loading { | ||||
|   text-align: center; | ||||
|   color: #666; | ||||
|   font-style: italic; | ||||
|   padding: 20px; | ||||
| } | ||||
|  | ||||
| /* Selected Keypair Info */ | ||||
| .keypair-info .info-row { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
|   margin-bottom: 8px; | ||||
|   padding: 8px 0; | ||||
|   border-bottom: 1px solid rgba(102, 126, 234, 0.1); | ||||
| } | ||||
|  | ||||
| .keypair-info .info-row:last-child { | ||||
|   border-bottom: none; | ||||
| } | ||||
|  | ||||
| .keypair-info label { | ||||
|   font-weight: 500; | ||||
|   color: #4a5568; | ||||
|   margin: 0; | ||||
| } | ||||
|  | ||||
| .public-key-container, .signature-container { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   gap: 8px; | ||||
|   flex: 1; | ||||
|   margin-left: 12px; | ||||
| } | ||||
|  | ||||
| .keypair-info code, .signature-result code { | ||||
|   background: rgba(102, 126, 234, 0.1); | ||||
|   padding: 6px 10px; | ||||
|   border-radius: 6px; | ||||
|   font-size: 11px; | ||||
|   word-break: break-all; | ||||
|   flex: 1; | ||||
|   color: #2d3748; | ||||
| } | ||||
|  | ||||
| /* Signature Result */ | ||||
| .signature-result { | ||||
|   margin-top: 16px; | ||||
|   padding: 16px; | ||||
|   background: rgba(16, 185, 129, 0.1); | ||||
|   border-radius: 12px; | ||||
|   border: 1px solid rgba(16, 185, 129, 0.2); | ||||
| } | ||||
|  | ||||
| .signature-result.hidden { | ||||
|   display: none; | ||||
| } | ||||
|  | ||||
| .signature-result label { | ||||
|   color: #065f46; | ||||
|   font-weight: 500; | ||||
|   margin-bottom: 8px; | ||||
| } | ||||
|  | ||||
| /* Loading Overlay */ | ||||
| .loading-overlay { | ||||
|   position: absolute; | ||||
|   top: 0; | ||||
|   left: 0; | ||||
|   right: 0; | ||||
|   bottom: 0; | ||||
|   background: rgba(255, 255, 255, 0.95); | ||||
|   backdrop-filter: blur(8px); | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
|   z-index: 1000; | ||||
| } | ||||
|  | ||||
| .loading-overlay.hidden { | ||||
|   display: none; | ||||
| } | ||||
|  | ||||
| .spinner { | ||||
|   width: 40px; | ||||
|   height: 40px; | ||||
|   border: 3px solid rgba(102, 126, 234, 0.3); | ||||
|   border-top: 3px solid #667eea; | ||||
|   border-radius: 50%; | ||||
|   animation: spin 1s linear infinite; | ||||
|   margin-bottom: 16px; | ||||
| } | ||||
|  | ||||
| @keyframes spin { | ||||
|   0% { transform: rotate(0deg); } | ||||
|   100% { transform: rotate(360deg); } | ||||
| } | ||||
|  | ||||
| /* Toast Notifications */ | ||||
| .toast { | ||||
|   position: fixed; | ||||
|   top: 20px; | ||||
|   left: 20px; | ||||
|   right: 20px; | ||||
|   max-width: 360px; | ||||
|   margin: 0 auto; | ||||
|   padding: 12px 16px; | ||||
|   border-radius: 12px; | ||||
|   font-size: 14px; | ||||
|   font-weight: 500; | ||||
|   text-align: center; | ||||
|   z-index: 1001; | ||||
|   transform: translateY(-100px); | ||||
|   opacity: 0; | ||||
|   transition: all 0.3s ease; | ||||
|   backdrop-filter: blur(20px); | ||||
|   box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); | ||||
| } | ||||
|  | ||||
| .toast:not(.hidden) { | ||||
|   transform: translateY(0); | ||||
|   opacity: 1; | ||||
| } | ||||
|  | ||||
| .toast.success { | ||||
|   background: rgba(16, 185, 129, 0.9); | ||||
|   color: white; | ||||
|   border: 1px solid rgba(16, 185, 129, 1); | ||||
| } | ||||
|  | ||||
| .toast.error { | ||||
|   background: rgba(239, 68, 68, 0.9); | ||||
|   color: white; | ||||
|   border: 1px solid rgba(239, 68, 68, 1); | ||||
| } | ||||
|  | ||||
| .toast.info { | ||||
|   background: rgba(59, 130, 246, 0.9); | ||||
|   color: white; | ||||
|   border: 1px solid rgba(59, 130, 246, 1); | ||||
| } | ||||
|  | ||||
| /* Scrollbar Styles */ | ||||
| .keypairs-list::-webkit-scrollbar { | ||||
|   width: 6px; | ||||
| } | ||||
|  | ||||
| .keypairs-list::-webkit-scrollbar-track { | ||||
|   background: rgba(102, 126, 234, 0.1); | ||||
|   border-radius: 3px; | ||||
| } | ||||
|  | ||||
| .keypairs-list::-webkit-scrollbar-thumb { | ||||
|   background: rgba(102, 126, 234, 0.3); | ||||
|   border-radius: 3px; | ||||
| } | ||||
|  | ||||
| .keypairs-list::-webkit-scrollbar-thumb:hover { | ||||
|   background: rgba(102, 126, 234, 0.5); | ||||
| } | ||||
|  | ||||
| /* Responsive adjustments */ | ||||
| @media (max-width: 400px) { | ||||
|   body { | ||||
|     width: 350px; | ||||
|   } | ||||
|  | ||||
|   .form-row { | ||||
|     flex-direction: column; | ||||
|     gap: 12px; | ||||
|   } | ||||
|  | ||||
|   .button-group { | ||||
|     flex-direction: column; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* Animation for card entrance */ | ||||
| .card { | ||||
|   animation: slideInUp 0.3s ease-out; | ||||
| } | ||||
|  | ||||
| @keyframes slideInUp { | ||||
|   from { | ||||
|     transform: translateY(20px); | ||||
|     opacity: 0; | ||||
|   } | ||||
|   to { | ||||
|     transform: translateY(0); | ||||
|     opacity: 1; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* Hover effects for better UX */ | ||||
| .keypair-item { | ||||
|   position: relative; | ||||
|   overflow: hidden; | ||||
| } | ||||
|  | ||||
| .keypair-item::before { | ||||
|   content: ''; | ||||
|   position: absolute; | ||||
|   top: 0; | ||||
|   left: -100%; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); | ||||
|   transition: left 0.5s; | ||||
| } | ||||
|  | ||||
| .keypair-item:hover::before { | ||||
|   left: 100%; | ||||
| } | ||||
							
								
								
									
										822
									
								
								crypto_vault_extension/wasm/wasm_app.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										822
									
								
								crypto_vault_extension/wasm/wasm_app.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,822 @@ | ||||
| let wasm; | ||||
|  | ||||
| function addToExternrefTable0(obj) { | ||||
|     const idx = wasm.__externref_table_alloc(); | ||||
|     wasm.__wbindgen_export_2.set(idx, obj); | ||||
|     return idx; | ||||
| } | ||||
|  | ||||
| function handleError(f, args) { | ||||
|     try { | ||||
|         return f.apply(this, args); | ||||
|     } catch (e) { | ||||
|         const idx = addToExternrefTable0(e); | ||||
|         wasm.__wbindgen_exn_store(idx); | ||||
|     } | ||||
| } | ||||
|  | ||||
| const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); | ||||
|  | ||||
| if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; | ||||
|  | ||||
| let cachedUint8ArrayMemory0 = null; | ||||
|  | ||||
| function getUint8ArrayMemory0() { | ||||
|     if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { | ||||
|         cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); | ||||
|     } | ||||
|     return cachedUint8ArrayMemory0; | ||||
| } | ||||
|  | ||||
| function getStringFromWasm0(ptr, len) { | ||||
|     ptr = ptr >>> 0; | ||||
|     return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); | ||||
| } | ||||
|  | ||||
| function isLikeNone(x) { | ||||
|     return x === undefined || x === null; | ||||
| } | ||||
|  | ||||
| function getArrayU8FromWasm0(ptr, len) { | ||||
|     ptr = ptr >>> 0; | ||||
|     return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len); | ||||
| } | ||||
|  | ||||
| let WASM_VECTOR_LEN = 0; | ||||
|  | ||||
| const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); | ||||
|  | ||||
| const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' | ||||
|     ? function (arg, view) { | ||||
|     return cachedTextEncoder.encodeInto(arg, view); | ||||
| } | ||||
|     : function (arg, view) { | ||||
|     const buf = cachedTextEncoder.encode(arg); | ||||
|     view.set(buf); | ||||
|     return { | ||||
|         read: arg.length, | ||||
|         written: buf.length | ||||
|     }; | ||||
| }); | ||||
|  | ||||
| function passStringToWasm0(arg, malloc, realloc) { | ||||
|  | ||||
|     if (realloc === undefined) { | ||||
|         const buf = cachedTextEncoder.encode(arg); | ||||
|         const ptr = malloc(buf.length, 1) >>> 0; | ||||
|         getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); | ||||
|         WASM_VECTOR_LEN = buf.length; | ||||
|         return ptr; | ||||
|     } | ||||
|  | ||||
|     let len = arg.length; | ||||
|     let ptr = malloc(len, 1) >>> 0; | ||||
|  | ||||
|     const mem = getUint8ArrayMemory0(); | ||||
|  | ||||
|     let offset = 0; | ||||
|  | ||||
|     for (; offset < len; offset++) { | ||||
|         const code = arg.charCodeAt(offset); | ||||
|         if (code > 0x7F) break; | ||||
|         mem[ptr + offset] = code; | ||||
|     } | ||||
|  | ||||
|     if (offset !== len) { | ||||
|         if (offset !== 0) { | ||||
|             arg = arg.slice(offset); | ||||
|         } | ||||
|         ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; | ||||
|         const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); | ||||
|         const ret = encodeString(arg, view); | ||||
|  | ||||
|         offset += ret.written; | ||||
|         ptr = realloc(ptr, len, offset, 1) >>> 0; | ||||
|     } | ||||
|  | ||||
|     WASM_VECTOR_LEN = offset; | ||||
|     return ptr; | ||||
| } | ||||
|  | ||||
| let cachedDataViewMemory0 = null; | ||||
|  | ||||
| function getDataViewMemory0() { | ||||
|     if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) { | ||||
|         cachedDataViewMemory0 = new DataView(wasm.memory.buffer); | ||||
|     } | ||||
|     return cachedDataViewMemory0; | ||||
| } | ||||
|  | ||||
| const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') | ||||
|     ? { register: () => {}, unregister: () => {} } | ||||
|     : new FinalizationRegistry(state => { | ||||
|     wasm.__wbindgen_export_5.get(state.dtor)(state.a, state.b) | ||||
| }); | ||||
|  | ||||
| function makeMutClosure(arg0, arg1, dtor, f) { | ||||
|     const state = { a: arg0, b: arg1, cnt: 1, dtor }; | ||||
|     const real = (...args) => { | ||||
|         // First up with a closure we increment the internal reference | ||||
|         // count. This ensures that the Rust closure environment won't | ||||
|         // be deallocated while we're invoking it. | ||||
|         state.cnt++; | ||||
|         const a = state.a; | ||||
|         state.a = 0; | ||||
|         try { | ||||
|             return f(a, state.b, ...args); | ||||
|         } finally { | ||||
|             if (--state.cnt === 0) { | ||||
|                 wasm.__wbindgen_export_5.get(state.dtor)(a, state.b); | ||||
|                 CLOSURE_DTORS.unregister(state); | ||||
|             } else { | ||||
|                 state.a = a; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     real.original = state; | ||||
|     CLOSURE_DTORS.register(real, state, state); | ||||
|     return real; | ||||
| } | ||||
|  | ||||
| function debugString(val) { | ||||
|     // primitive types | ||||
|     const type = typeof val; | ||||
|     if (type == 'number' || type == 'boolean' || val == null) { | ||||
|         return  `${val}`; | ||||
|     } | ||||
|     if (type == 'string') { | ||||
|         return `"${val}"`; | ||||
|     } | ||||
|     if (type == 'symbol') { | ||||
|         const description = val.description; | ||||
|         if (description == null) { | ||||
|             return 'Symbol'; | ||||
|         } else { | ||||
|             return `Symbol(${description})`; | ||||
|         } | ||||
|     } | ||||
|     if (type == 'function') { | ||||
|         const name = val.name; | ||||
|         if (typeof name == 'string' && name.length > 0) { | ||||
|             return `Function(${name})`; | ||||
|         } else { | ||||
|             return 'Function'; | ||||
|         } | ||||
|     } | ||||
|     // objects | ||||
|     if (Array.isArray(val)) { | ||||
|         const length = val.length; | ||||
|         let debug = '['; | ||||
|         if (length > 0) { | ||||
|             debug += debugString(val[0]); | ||||
|         } | ||||
|         for(let i = 1; i < length; i++) { | ||||
|             debug += ', ' + debugString(val[i]); | ||||
|         } | ||||
|         debug += ']'; | ||||
|         return debug; | ||||
|     } | ||||
|     // Test for built-in | ||||
|     const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); | ||||
|     let className; | ||||
|     if (builtInMatches && builtInMatches.length > 1) { | ||||
|         className = builtInMatches[1]; | ||||
|     } else { | ||||
|         // Failed to match the standard '[object ClassName]' | ||||
|         return toString.call(val); | ||||
|     } | ||||
|     if (className == 'Object') { | ||||
|         // we're a user defined class or Object | ||||
|         // JSON.stringify avoids problems with cycles, and is generally much | ||||
|         // easier than looping through ownProperties of `val`. | ||||
|         try { | ||||
|             return 'Object(' + JSON.stringify(val) + ')'; | ||||
|         } catch (_) { | ||||
|             return 'Object'; | ||||
|         } | ||||
|     } | ||||
|     // errors | ||||
|     if (val instanceof Error) { | ||||
|         return `${val.name}: ${val.message}\n${val.stack}`; | ||||
|     } | ||||
|     // TODO we could test for more things here, like `Set`s and `Map`s. | ||||
|     return className; | ||||
| } | ||||
| /** | ||||
|  * Initialize the scripting environment (must be called before run_rhai) | ||||
|  */ | ||||
| export function init_rhai_env() { | ||||
|     wasm.init_rhai_env(); | ||||
| } | ||||
|  | ||||
| function takeFromExternrefTable0(idx) { | ||||
|     const value = wasm.__wbindgen_export_2.get(idx); | ||||
|     wasm.__externref_table_dealloc(idx); | ||||
|     return value; | ||||
| } | ||||
| /** | ||||
|  * Securely run a Rhai script in the extension context (must be called only after user approval) | ||||
|  * @param {string} script | ||||
|  * @returns {any} | ||||
|  */ | ||||
| export function run_rhai(script) { | ||||
|     const ptr0 = passStringToWasm0(script, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); | ||||
|     const len0 = WASM_VECTOR_LEN; | ||||
|     const ret = wasm.run_rhai(ptr0, len0); | ||||
|     if (ret[2]) { | ||||
|         throw takeFromExternrefTable0(ret[1]); | ||||
|     } | ||||
|     return takeFromExternrefTable0(ret[0]); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Create and unlock a new keyspace with the given name and password | ||||
|  * @param {string} keyspace | ||||
|  * @param {string} password | ||||
|  * @returns {Promise<void>} | ||||
|  */ | ||||
| export function create_keyspace(keyspace, password) { | ||||
|     const ptr0 = passStringToWasm0(keyspace, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); | ||||
|     const len0 = WASM_VECTOR_LEN; | ||||
|     const ptr1 = passStringToWasm0(password, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); | ||||
|     const len1 = WASM_VECTOR_LEN; | ||||
|     const ret = wasm.create_keyspace(ptr0, len0, ptr1, len1); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Initialize session with keyspace and password | ||||
|  * @param {string} keyspace | ||||
|  * @param {string} password | ||||
|  * @returns {Promise<void>} | ||||
|  */ | ||||
| export function init_session(keyspace, password) { | ||||
|     const ptr0 = passStringToWasm0(keyspace, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); | ||||
|     const len0 = WASM_VECTOR_LEN; | ||||
|     const ptr1 = passStringToWasm0(password, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); | ||||
|     const len1 = WASM_VECTOR_LEN; | ||||
|     const ret = wasm.init_session(ptr0, len0, ptr1, len1); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Lock the session (zeroize password and session) | ||||
|  */ | ||||
| export function lock_session() { | ||||
|     wasm.lock_session(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Get metadata of the currently selected keypair | ||||
|  * @returns {any} | ||||
|  */ | ||||
| export function current_keypair_metadata() { | ||||
|     const ret = wasm.current_keypair_metadata(); | ||||
|     if (ret[2]) { | ||||
|         throw takeFromExternrefTable0(ret[1]); | ||||
|     } | ||||
|     return takeFromExternrefTable0(ret[0]); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Get public key of the currently selected keypair as Uint8Array | ||||
|  * @returns {any} | ||||
|  */ | ||||
| export function current_keypair_public_key() { | ||||
|     const ret = wasm.current_keypair_public_key(); | ||||
|     if (ret[2]) { | ||||
|         throw takeFromExternrefTable0(ret[1]); | ||||
|     } | ||||
|     return takeFromExternrefTable0(ret[0]); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Returns true if a keyspace is currently unlocked | ||||
|  * @returns {boolean} | ||||
|  */ | ||||
| export function is_unlocked() { | ||||
|     const ret = wasm.is_unlocked(); | ||||
|     return ret !== 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Get all keypairs from the current session | ||||
|  * Returns an array of keypair objects with id, type, and metadata | ||||
|  * Select keypair for the session | ||||
|  * @param {string} key_id | ||||
|  */ | ||||
| export function select_keypair(key_id) { | ||||
|     const ptr0 = passStringToWasm0(key_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); | ||||
|     const len0 = WASM_VECTOR_LEN; | ||||
|     const ret = wasm.select_keypair(ptr0, len0); | ||||
|     if (ret[1]) { | ||||
|         throw takeFromExternrefTable0(ret[0]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * List keypairs in the current session's keyspace | ||||
|  * @returns {Promise<any>} | ||||
|  */ | ||||
| export function list_keypairs() { | ||||
|     const ret = wasm.list_keypairs(); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Add a keypair to the current keyspace | ||||
|  * @param {string | null} [key_type] | ||||
|  * @param {string | null} [metadata] | ||||
|  * @returns {Promise<any>} | ||||
|  */ | ||||
| export function add_keypair(key_type, metadata) { | ||||
|     var ptr0 = isLikeNone(key_type) ? 0 : passStringToWasm0(key_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); | ||||
|     var len0 = WASM_VECTOR_LEN; | ||||
|     var ptr1 = isLikeNone(metadata) ? 0 : passStringToWasm0(metadata, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); | ||||
|     var len1 = WASM_VECTOR_LEN; | ||||
|     const ret = wasm.add_keypair(ptr0, len0, ptr1, len1); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| function passArray8ToWasm0(arg, malloc) { | ||||
|     const ptr = malloc(arg.length * 1, 1) >>> 0; | ||||
|     getUint8ArrayMemory0().set(arg, ptr / 1); | ||||
|     WASM_VECTOR_LEN = arg.length; | ||||
|     return ptr; | ||||
| } | ||||
| /** | ||||
|  * Sign message with current session | ||||
|  * @param {Uint8Array} message | ||||
|  * @returns {Promise<any>} | ||||
|  */ | ||||
| export function sign(message) { | ||||
|     const ptr0 = passArray8ToWasm0(message, wasm.__wbindgen_malloc); | ||||
|     const len0 = WASM_VECTOR_LEN; | ||||
|     const ret = wasm.sign(ptr0, len0); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| function __wbg_adapter_32(arg0, arg1, arg2) { | ||||
|     wasm.closure89_externref_shim(arg0, arg1, arg2); | ||||
| } | ||||
|  | ||||
| function __wbg_adapter_35(arg0, arg1, arg2) { | ||||
|     wasm.closure133_externref_shim(arg0, arg1, arg2); | ||||
| } | ||||
|  | ||||
| function __wbg_adapter_38(arg0, arg1, arg2) { | ||||
|     wasm.closure188_externref_shim(arg0, arg1, arg2); | ||||
| } | ||||
|  | ||||
| function __wbg_adapter_135(arg0, arg1, arg2, arg3) { | ||||
|     wasm.closure1847_externref_shim(arg0, arg1, arg2, arg3); | ||||
| } | ||||
|  | ||||
| const __wbindgen_enum_IdbTransactionMode = ["readonly", "readwrite", "versionchange", "readwriteflush", "cleanup"]; | ||||
|  | ||||
| async function __wbg_load(module, imports) { | ||||
|     if (typeof Response === 'function' && module instanceof Response) { | ||||
|         if (typeof WebAssembly.instantiateStreaming === 'function') { | ||||
|             try { | ||||
|                 return await WebAssembly.instantiateStreaming(module, imports); | ||||
|  | ||||
|             } catch (e) { | ||||
|                 if (module.headers.get('Content-Type') != 'application/wasm') { | ||||
|                     console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); | ||||
|  | ||||
|                 } else { | ||||
|                     throw e; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         const bytes = await module.arrayBuffer(); | ||||
|         return await WebAssembly.instantiate(bytes, imports); | ||||
|  | ||||
|     } else { | ||||
|         const instance = await WebAssembly.instantiate(module, imports); | ||||
|  | ||||
|         if (instance instanceof WebAssembly.Instance) { | ||||
|             return { instance, module }; | ||||
|  | ||||
|         } else { | ||||
|             return instance; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| function __wbg_get_imports() { | ||||
|     const imports = {}; | ||||
|     imports.wbg = {}; | ||||
|     imports.wbg.__wbg_buffer_609cc3eee51ed158 = function(arg0) { | ||||
|         const ret = arg0.buffer; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_call_672a4d21634d4a24 = function() { return handleError(function (arg0, arg1) { | ||||
|         const ret = arg0.call(arg1); | ||||
|         return ret; | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_call_7cccdd69e0791ae2 = function() { return handleError(function (arg0, arg1, arg2) { | ||||
|         const ret = arg0.call(arg1, arg2); | ||||
|         return ret; | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_createObjectStore_d2f9e1016f4d81b9 = function() { return handleError(function (arg0, arg1, arg2, arg3) { | ||||
|         const ret = arg0.createObjectStore(getStringFromWasm0(arg1, arg2), arg3); | ||||
|         return ret; | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_crypto_574e78ad8b13b65f = function(arg0) { | ||||
|         const ret = arg0.crypto; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_error_524f506f44df1645 = function(arg0) { | ||||
|         console.error(arg0); | ||||
|     }; | ||||
|     imports.wbg.__wbg_error_ff4ddaabdfc5dbb3 = function() { return handleError(function (arg0) { | ||||
|         const ret = arg0.error; | ||||
|         return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_getRandomValues_3c9c0d586e575a16 = function() { return handleError(function (arg0, arg1) { | ||||
|         globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1)); | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_getRandomValues_b8f5dbd5f3995a9e = function() { return handleError(function (arg0, arg1) { | ||||
|         arg0.getRandomValues(arg1); | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_getTime_46267b1c24877e30 = function(arg0) { | ||||
|         const ret = arg0.getTime(); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_get_4f73335ab78445db = function(arg0, arg1, arg2) { | ||||
|         const ret = arg1[arg2 >>> 0]; | ||||
|         var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); | ||||
|         var len1 = WASM_VECTOR_LEN; | ||||
|         getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); | ||||
|         getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); | ||||
|     }; | ||||
|     imports.wbg.__wbg_get_67b2ba62fc30de12 = function() { return handleError(function (arg0, arg1) { | ||||
|         const ret = Reflect.get(arg0, arg1); | ||||
|         return ret; | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_get_8da03f81f6a1111e = function() { return handleError(function (arg0, arg1) { | ||||
|         const ret = arg0.get(arg1); | ||||
|         return ret; | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_instanceof_IdbDatabase_a3ef009ca00059f9 = function(arg0) { | ||||
|         let result; | ||||
|         try { | ||||
|             result = arg0 instanceof IDBDatabase; | ||||
|         } catch (_) { | ||||
|             result = false; | ||||
|         } | ||||
|         const ret = result; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_instanceof_IdbFactory_12eaba3366f4302f = function(arg0) { | ||||
|         let result; | ||||
|         try { | ||||
|             result = arg0 instanceof IDBFactory; | ||||
|         } catch (_) { | ||||
|             result = false; | ||||
|         } | ||||
|         const ret = result; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_instanceof_IdbOpenDbRequest_a3416e156c9db893 = function(arg0) { | ||||
|         let result; | ||||
|         try { | ||||
|             result = arg0 instanceof IDBOpenDBRequest; | ||||
|         } catch (_) { | ||||
|             result = false; | ||||
|         } | ||||
|         const ret = result; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_instanceof_IdbRequest_4813c3f207666aa4 = function(arg0) { | ||||
|         let result; | ||||
|         try { | ||||
|             result = arg0 instanceof IDBRequest; | ||||
|         } catch (_) { | ||||
|             result = false; | ||||
|         } | ||||
|         const ret = result; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_length_52b6c4580c5ec934 = function(arg0) { | ||||
|         const ret = arg0.length; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_msCrypto_a61aeb35a24c1329 = function(arg0) { | ||||
|         const ret = arg0.msCrypto; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_new0_f788a2397c7ca929 = function() { | ||||
|         const ret = new Date(); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_new_23a2665fac83c611 = function(arg0, arg1) { | ||||
|         try { | ||||
|             var state0 = {a: arg0, b: arg1}; | ||||
|             var cb0 = (arg0, arg1) => { | ||||
|                 const a = state0.a; | ||||
|                 state0.a = 0; | ||||
|                 try { | ||||
|                     return __wbg_adapter_135(a, state0.b, arg0, arg1); | ||||
|                 } finally { | ||||
|                     state0.a = a; | ||||
|                 } | ||||
|             }; | ||||
|             const ret = new Promise(cb0); | ||||
|             return ret; | ||||
|         } finally { | ||||
|             state0.a = state0.b = 0; | ||||
|         } | ||||
|     }; | ||||
|     imports.wbg.__wbg_new_405e22f390576ce2 = function() { | ||||
|         const ret = new Object(); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_new_78feb108b6472713 = function() { | ||||
|         const ret = new Array(); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_new_a12002a7f91c75be = function(arg0) { | ||||
|         const ret = new Uint8Array(arg0); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_newnoargs_105ed471475aaf50 = function(arg0, arg1) { | ||||
|         const ret = new Function(getStringFromWasm0(arg0, arg1)); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_newwithbyteoffsetandlength_d97e637ebe145a9a = function(arg0, arg1, arg2) { | ||||
|         const ret = new Uint8Array(arg0, arg1 >>> 0, arg2 >>> 0); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_newwithlength_a381634e90c276d4 = function(arg0) { | ||||
|         const ret = new Uint8Array(arg0 >>> 0); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_node_905d3e251edff8a2 = function(arg0) { | ||||
|         const ret = arg0.node; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_now_d18023d54d4e5500 = function(arg0) { | ||||
|         const ret = arg0.now(); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_objectStoreNames_9bb1ab04a7012aaf = function(arg0) { | ||||
|         const ret = arg0.objectStoreNames; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_objectStore_21878d46d25b64b6 = function() { return handleError(function (arg0, arg1, arg2) { | ||||
|         const ret = arg0.objectStore(getStringFromWasm0(arg1, arg2)); | ||||
|         return ret; | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_open_88b1390d99a7c691 = function() { return handleError(function (arg0, arg1, arg2) { | ||||
|         const ret = arg0.open(getStringFromWasm0(arg1, arg2)); | ||||
|         return ret; | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_open_e0c0b2993eb596e1 = function() { return handleError(function (arg0, arg1, arg2, arg3) { | ||||
|         const ret = arg0.open(getStringFromWasm0(arg1, arg2), arg3 >>> 0); | ||||
|         return ret; | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_process_dc0fbacc7c1c06f7 = function(arg0) { | ||||
|         const ret = arg0.process; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_push_737cfc8c1432c2c6 = function(arg0, arg1) { | ||||
|         const ret = arg0.push(arg1); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_put_066faa31a6a88f5b = function() { return handleError(function (arg0, arg1, arg2) { | ||||
|         const ret = arg0.put(arg1, arg2); | ||||
|         return ret; | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_put_9ef5363941008835 = function() { return handleError(function (arg0, arg1) { | ||||
|         const ret = arg0.put(arg1); | ||||
|         return ret; | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_queueMicrotask_97d92b4fcc8a61c5 = function(arg0) { | ||||
|         queueMicrotask(arg0); | ||||
|     }; | ||||
|     imports.wbg.__wbg_queueMicrotask_d3219def82552485 = function(arg0) { | ||||
|         const ret = arg0.queueMicrotask; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_randomFillSync_ac0988aba3254290 = function() { return handleError(function (arg0, arg1) { | ||||
|         arg0.randomFillSync(arg1); | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_require_60cc747a6bc5215a = function() { return handleError(function () { | ||||
|         const ret = module.require; | ||||
|         return ret; | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_resolve_4851785c9c5f573d = function(arg0) { | ||||
|         const ret = Promise.resolve(arg0); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_result_f29afabdf2c05826 = function() { return handleError(function (arg0) { | ||||
|         const ret = arg0.result; | ||||
|         return ret; | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_set_65595bdd868b3009 = function(arg0, arg1, arg2) { | ||||
|         arg0.set(arg1, arg2 >>> 0); | ||||
|     }; | ||||
|     imports.wbg.__wbg_setonerror_d7e3056cc6e56085 = function(arg0, arg1) { | ||||
|         arg0.onerror = arg1; | ||||
|     }; | ||||
|     imports.wbg.__wbg_setonsuccess_afa464ee777a396d = function(arg0, arg1) { | ||||
|         arg0.onsuccess = arg1; | ||||
|     }; | ||||
|     imports.wbg.__wbg_setonupgradeneeded_fcf7ce4f2eb0cb5f = function(arg0, arg1) { | ||||
|         arg0.onupgradeneeded = arg1; | ||||
|     }; | ||||
|     imports.wbg.__wbg_static_accessor_GLOBAL_88a902d13a557d07 = function() { | ||||
|         const ret = typeof global === 'undefined' ? null : global; | ||||
|         return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); | ||||
|     }; | ||||
|     imports.wbg.__wbg_static_accessor_GLOBAL_THIS_56578be7e9f832b0 = function() { | ||||
|         const ret = typeof globalThis === 'undefined' ? null : globalThis; | ||||
|         return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); | ||||
|     }; | ||||
|     imports.wbg.__wbg_static_accessor_SELF_37c5d418e4bf5819 = function() { | ||||
|         const ret = typeof self === 'undefined' ? null : self; | ||||
|         return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); | ||||
|     }; | ||||
|     imports.wbg.__wbg_static_accessor_WINDOW_5de37043a91a9c40 = function() { | ||||
|         const ret = typeof window === 'undefined' ? null : window; | ||||
|         return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); | ||||
|     }; | ||||
|     imports.wbg.__wbg_subarray_aa9065fa9dc5df96 = function(arg0, arg1, arg2) { | ||||
|         const ret = arg0.subarray(arg1 >>> 0, arg2 >>> 0); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_target_0a62d9d79a2a1ede = function(arg0) { | ||||
|         const ret = arg0.target; | ||||
|         return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); | ||||
|     }; | ||||
|     imports.wbg.__wbg_then_44b73946d2fb3e7d = function(arg0, arg1) { | ||||
|         const ret = arg0.then(arg1); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbg_transaction_d6d07c3c9963c49e = function() { return handleError(function (arg0, arg1, arg2) { | ||||
|         const ret = arg0.transaction(arg1, __wbindgen_enum_IdbTransactionMode[arg2]); | ||||
|         return ret; | ||||
|     }, arguments) }; | ||||
|     imports.wbg.__wbg_versions_c01dfd4722a88165 = function(arg0) { | ||||
|         const ret = arg0.versions; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_cb_drop = function(arg0) { | ||||
|         const obj = arg0.original; | ||||
|         if (obj.cnt-- == 1) { | ||||
|             obj.a = 0; | ||||
|             return true; | ||||
|         } | ||||
|         const ret = false; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_closure_wrapper288 = function(arg0, arg1, arg2) { | ||||
|         const ret = makeMutClosure(arg0, arg1, 90, __wbg_adapter_32); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_closure_wrapper518 = function(arg0, arg1, arg2) { | ||||
|         const ret = makeMutClosure(arg0, arg1, 134, __wbg_adapter_35); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_closure_wrapper776 = function(arg0, arg1, arg2) { | ||||
|         const ret = makeMutClosure(arg0, arg1, 189, __wbg_adapter_38); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { | ||||
|         const ret = debugString(arg1); | ||||
|         const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); | ||||
|         const len1 = WASM_VECTOR_LEN; | ||||
|         getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); | ||||
|         getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_init_externref_table = function() { | ||||
|         const table = wasm.__wbindgen_export_2; | ||||
|         const offset = table.grow(4); | ||||
|         table.set(0, undefined); | ||||
|         table.set(offset + 0, undefined); | ||||
|         table.set(offset + 1, null); | ||||
|         table.set(offset + 2, true); | ||||
|         table.set(offset + 3, false); | ||||
|         ; | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_is_function = function(arg0) { | ||||
|         const ret = typeof(arg0) === 'function'; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_is_null = function(arg0) { | ||||
|         const ret = arg0 === null; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_is_object = function(arg0) { | ||||
|         const val = arg0; | ||||
|         const ret = typeof(val) === 'object' && val !== null; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_is_string = function(arg0) { | ||||
|         const ret = typeof(arg0) === 'string'; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_is_undefined = function(arg0) { | ||||
|         const ret = arg0 === undefined; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_json_parse = function(arg0, arg1) { | ||||
|         const ret = JSON.parse(getStringFromWasm0(arg0, arg1)); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_json_serialize = function(arg0, arg1) { | ||||
|         const obj = arg1; | ||||
|         const ret = JSON.stringify(obj === undefined ? null : obj); | ||||
|         const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); | ||||
|         const len1 = WASM_VECTOR_LEN; | ||||
|         getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); | ||||
|         getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_memory = function() { | ||||
|         const ret = wasm.memory; | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_string_new = function(arg0, arg1) { | ||||
|         const ret = getStringFromWasm0(arg0, arg1); | ||||
|         return ret; | ||||
|     }; | ||||
|     imports.wbg.__wbindgen_throw = function(arg0, arg1) { | ||||
|         throw new Error(getStringFromWasm0(arg0, arg1)); | ||||
|     }; | ||||
|  | ||||
|     return imports; | ||||
| } | ||||
|  | ||||
| function __wbg_init_memory(imports, memory) { | ||||
|  | ||||
| } | ||||
|  | ||||
| function __wbg_finalize_init(instance, module) { | ||||
|     wasm = instance.exports; | ||||
|     __wbg_init.__wbindgen_wasm_module = module; | ||||
|     cachedDataViewMemory0 = null; | ||||
|     cachedUint8ArrayMemory0 = null; | ||||
|  | ||||
|  | ||||
|     wasm.__wbindgen_start(); | ||||
|     return wasm; | ||||
| } | ||||
|  | ||||
| function initSync(module) { | ||||
|     if (wasm !== undefined) return wasm; | ||||
|  | ||||
|  | ||||
|     if (typeof module !== 'undefined') { | ||||
|         if (Object.getPrototypeOf(module) === Object.prototype) { | ||||
|             ({module} = module) | ||||
|         } else { | ||||
|             console.warn('using deprecated parameters for `initSync()`; pass a single object instead') | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     const imports = __wbg_get_imports(); | ||||
|  | ||||
|     __wbg_init_memory(imports); | ||||
|  | ||||
|     if (!(module instanceof WebAssembly.Module)) { | ||||
|         module = new WebAssembly.Module(module); | ||||
|     } | ||||
|  | ||||
|     const instance = new WebAssembly.Instance(module, imports); | ||||
|  | ||||
|     return __wbg_finalize_init(instance, module); | ||||
| } | ||||
|  | ||||
| async function __wbg_init(module_or_path) { | ||||
|     if (wasm !== undefined) return wasm; | ||||
|  | ||||
|  | ||||
|     if (typeof module_or_path !== 'undefined') { | ||||
|         if (Object.getPrototypeOf(module_or_path) === Object.prototype) { | ||||
|             ({module_or_path} = module_or_path) | ||||
|         } else { | ||||
|             console.warn('using deprecated parameters for the initialization function; pass a single object instead') | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (typeof module_or_path === 'undefined') { | ||||
|         module_or_path = new URL('wasm_app_bg.wasm', import.meta.url); | ||||
|     } | ||||
|     const imports = __wbg_get_imports(); | ||||
|  | ||||
|     if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { | ||||
|         module_or_path = fetch(module_or_path); | ||||
|     } | ||||
|  | ||||
|     __wbg_init_memory(imports); | ||||
|  | ||||
|     const { instance, module } = await __wbg_load(await module_or_path, imports); | ||||
|  | ||||
|     return __wbg_finalize_init(instance, module); | ||||
| } | ||||
|  | ||||
| export { initSync }; | ||||
| export default __wbg_init; | ||||
							
								
								
									
										
											BIN
										
									
								
								crypto_vault_extension/wasm/wasm_app_bg.wasm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								crypto_vault_extension/wasm/wasm_app_bg.wasm
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user