OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-wallet-desktop / desktop / index.html

1231 lines · html ref:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.coingecko.com">
  <title>Hyper Wallet Desktop - P2P Cryptocurrency Wallet</title>
  <link rel="stylesheet" href="wallet.css">
</head>
<body>
  <!-- Setup Wizard (shown when wallet not initialized) -->
  <div id="setup-wizard" class="setup-wizard" style="display: none;">
    <div class="setup-container">
      <!-- Welcome Screen -->
      <div id="setup-welcome" class="setup-screen">
        <h1>💰 Welcome to Hyper Wallet</h1>
        <p class="setup-subtitle">P2P Multi-Chain Cryptocurrency Wallet</p>
        <div class="setup-content">
          <p>To get started, you can create a new wallet, restore an existing one, or join an existing wallet from another device.</p>
          <div class="setup-actions">
            <button class="btn-primary btn-large" onclick="showCreateWallet()">🆕 Create New Wallet</button>
            <button class="btn-secondary btn-large" onclick="showRestoreWallet()">🔄 Restore Existing Wallet</button>
            <button class="btn-secondary btn-large" onclick="showJoinWallet()">🔗 Join Existing Wallet</button>
          </div>
        </div>
      </div>

      <!-- Create Wallet Screen -->
      <div id="setup-create" class="setup-screen" style="display: none;">
        <h2>Create New Wallet</h2>
        <div class="setup-content">
          <div class="setup-warning">
            <h3>⚠️ CRITICAL: Backup Your Recovery Phrases</h3>
            <p>You will be shown TWO sets of recovery words:</p>
            <ul>
              <li><strong>Identity (24 words)</strong> - For device attestation and P2P sync</li>
              <li><strong>Wallet (12 words)</strong> - For cryptocurrency keys</li>
            </ul>
            <p><strong>Both are required to fully restore your wallet!</strong></p>
            <ul>
              <li>Write them down on paper (separately)</li>
              <li>Store them in a safe place</li>
              <li>Never share them with anyone</li>
              <li>Never store them digitally (no screenshots, no cloud storage)</li>
            </ul>
          </div>
          <div id="mnemonic-display" class="mnemonic-display" style="display: none;">
            <div class="mnemonic-section">
              <h3>🔐 Identity Mnemonic (24 words)</h3>
              <p class="mnemonic-subtitle">For device attestation and multi-device sync</p>
              <div id="identity-mnemonic-words" class="mnemonic-words mnemonic-words-24"></div>
            </div>

            <div class="mnemonic-section">
              <h3>💰 Wallet Mnemonic (12 words)</h3>
              <p class="mnemonic-subtitle">For cryptocurrency private keys</p>
              <div id="wallet-mnemonic-words" class="mnemonic-words"></div>
            </div>

            <div class="mnemonic-confirm">
              <label>
                <input type="checkbox" id="mnemonic-confirm-check">
                I have written down BOTH recovery phrases and stored them safely
              </label>
            </div>
          </div>

          <div id="password-section" class="password-section">
            <h3>🔒 Set Encryption Password</h3>
            <p class="password-hint">Your recovery phrases will be encrypted with this password. You'll need it to access your wallet.</p>
            <div class="form-group">
              <label for="create-password">Password (min 8 characters)</label>
              <input type="password" id="create-password" placeholder="Enter strong password" minlength="8">
            </div>
            <div class="form-group">
              <label for="create-password-confirm">Confirm Password</label>
              <input type="password" id="create-password-confirm" placeholder="Re-enter password" minlength="8">
            </div>
            <div id="create-password-error" class="form-error" style="display: none;"></div>
            <div class="password-warning">
              <p>⚠️ <strong>IMPORTANT</strong>: If you forget this password, you can only restore using your recovery phrases!</p>
            </div>
          </div>

          <div class="setup-actions">
            <button class="btn-secondary" onclick="showSetupWelcome()">← Back</button>
            <button id="btn-generate-mnemonic" class="btn-primary" onclick="generateNewWallet()">Generate Recovery Phrases</button>
            <button id="btn-confirm-mnemonic" class="btn-primary" onclick="confirmMnemonicBackup()" style="display: none;" disabled>Continue →</button>
          </div>
        </div>
      </div>

      <!-- Restore Wallet Screen -->
      <div id="setup-restore" class="setup-screen" style="display: none;">
        <h2>Restore Existing Wallet</h2>
        <div class="setup-content">
          <p>Enter both your identity and wallet recovery phrases to restore your system.</p>

          <div class="mnemonic-input-container">
            <label for="restore-identity-mnemonic"><strong>Identity Mnemonic (24 words)</strong></label>
            <p class="input-help">For device attestation and P2P sync</p>
            <textarea id="restore-identity-mnemonic" class="mnemonic-input" placeholder="Enter your 24-word identity mnemonic (separated by spaces)" rows="4"></textarea>
          </div>

          <div class="mnemonic-input-container">
            <label for="restore-wallet-mnemonic"><strong>Wallet Mnemonic (12 words)</strong></label>
            <p class="input-help">For cryptocurrency private keys</p>
            <textarea id="restore-wallet-mnemonic" class="mnemonic-input" placeholder="Enter your 12-word wallet mnemonic (separated by spaces)" rows="3"></textarea>
          </div>

          <div class="password-section">
            <h3>🔒 Set Encryption Password</h3>
            <p class="password-hint">Set a password to encrypt your restored mnemonics.</p>
            <div class="form-group">
              <label for="restore-password">Password (min 8 characters)</label>
              <input type="password" id="restore-password" placeholder="Enter strong password" minlength="8">
            </div>
            <div class="form-group">
              <label for="restore-password-confirm">Confirm Password</label>
              <input type="password" id="restore-password-confirm" placeholder="Re-enter password" minlength="8">
            </div>
          </div>

          <div id="restore-error" class="form-error"></div>

          <div class="setup-actions">
            <button class="btn-secondary" onclick="showSetupWelcome()">← Back</button>
            <button class="btn-primary" onclick="restoreWallet()">Restore System</button>
          </div>
        </div>
      </div>

      <!-- Join Existing Wallet Screen -->
      <div id="setup-join" class="setup-screen" style="display: none;">
        <h2>Join Existing Wallet</h2>
        <div class="setup-content">
          <p>Join an existing wallet from another device using an invite code.</p>

          <div class="setup-info">
            <h3>ℹ️ How it Works</h3>
            <ol>
              <li>On your primary device (e.g., tabertron), click "Add Device" in Settings</li>
              <li>Copy the invite code or scan the QR code</li>
              <li>Paste the invite code below</li>
              <li>Both identity and wallet keys will sync automatically via P2P</li>
            </ol>
          </div>

          <div class="mnemonic-input-container">
            <label for="join-invite-code"><strong>Invite Code</strong></label>
            <p class="input-help">Paste the long invite string from your primary device</p>
            <textarea id="join-invite-code" class="mnemonic-input" placeholder="Paste invite code here..." rows="3"></textarea>
          </div>

          <div class="mnemonic-input-container">
            <label for="join-device-name"><strong>Device Name</strong></label>
            <p class="input-help">Give this device a name (e.g., "ashatron", "laptop", "phone")</p>
            <input type="text" id="join-device-name" class="text-input" placeholder="Enter device name">
          </div>

          <div id="join-status" class="sync-status" style="display: none;">
            <div class="spinner"></div>
            <p id="join-status-text">Connecting to primary device...</p>
          </div>

          <div id="join-error" class="form-error"></div>

          <div class="setup-actions">
            <button class="btn-secondary" onclick="showSetupWelcome()">← Back</button>
            <button id="btn-join-wallet" class="btn-primary" onclick="joinExistingWallet()">Join Wallet</button>
          </div>
        </div>
      </div>
    </div>
  </div>

  <!-- Main App (shown when wallet initialized) -->
  <div class="container" id="main-app">
    <!-- Header -->
    <header>
      <h1>💰 Hyper Wallet</h1>
      <div class="header-actions">
        <div class="network-badge" id="network-badge">
          <span class="status-dot"></span>
          <span id="network-name">Loading...</span>
        </div>
        <button class="btn-icon" onclick="showContacts()" title="Contacts">📇</button>
        <button class="btn-icon" onclick="showSettings()" title="Settings">⚙️</button>
      </div>
    </header>

    <!-- Status Bar -->
    <div class="status-bar" id="status-bar">
      <div class="status-item">
        <span class="status-label">Status:</span>
        <span id="wallet-status">Checking...</span>
      </div>
      <div class="status-item">
        <span class="status-label">Network:</span>
        <div class="network-selector">
          <button class="network-btn active" data-network="testnet" onclick="setNetwork('testnet')">Testnet</button>
          <button class="network-btn" data-network="mainnet" onclick="setNetwork('mainnet')">Mainnet</button>
        </div>
      </div>
      <div class="status-item">
        <button class="btn-small" onclick="refreshAll()">🔄 Refresh</button>
      </div>
    </div>

    <!-- Currency Selector -->
    <section class="currency-selector-section">
      <div class="selector-header">
        <h2>Select Currency</h2>
      </div>
      <select id="currency-selector" class="currency-select" onchange="switchCurrency()">
        <!-- Options populated dynamically from SUPPORTED_CHAINS -->
      </select>
    </section>

    <!-- Wallet View -->
    <section class="wallet-view">
      <div class="wallet-container">
        <div class="wallet-info-box">
          <div class="wallet-header">
            <div class="wallet-icon" id="wallet-icon">₿</div>
            <div class="wallet-info">
              <h2 id="wallet-name">Bitcoin</h2>
              <div class="wallet-network" id="wallet-network">Testnet</div>
            </div>
            <div class="wallet-address-selector">
              <label for="wallet-address-select">Active Address:</label>
              <select id="wallet-address-select" onchange="onWalletAddressChange()">
                <option value="">Loading...</option>
              </select>
            </div>
          </div>

          <div class="wallet-balance">
            <div id="wallet-balance-display">
              <div class="spinner"></div>
            </div>
          </div>

          <div class="wallet-actions">
            <button class="btn-secondary" onclick="showReceiveForCurrent()">📥 Receive</button>
            <button class="btn-secondary" onclick="showSendForCurrent()">📤 Send</button>
            <button class="btn-secondary" onclick="showManageAddresses()">💼 Manage Addresses</button>
          </div>
        </div>

        <div class="wallet-qr-box">
          <h3>Receive Address</h3>
          <div class="qr-container" id="wallet-qr-container">
            <div class="spinner"></div>
          </div>
          <div class="wallet-address" id="wallet-address-display">
            Loading address...
          </div>
        </div>
      </div>
    </section>

    <!-- Transactions -->
    <section class="transactions">
      <div class="section-header">
        <h2>Recent Transactions</h2>
        <div>
          <button class="btn-small" onclick="syncTransactions()" id="sync-btn">🔄 Sync</button>
          <button class="btn-small" onclick="exportTransactions()">💾 Export</button>
          <button class="btn-small" onclick="loadTransactions()">↻ Refresh</button>
        </div>
      </div>

      <div id="tx-list" class="tx-list">
        <div class="spinner"></div>
      </div>
    </section>

    <!-- Footer -->
    <footer>
      <p>
        Hyper Wallet v7.0-beta |
        <a href="https://github.com/tetherto/wdk-core" target="_blank">WDK</a> |
        P2P Multi-Device Sync
      </p>
    </footer>
  </div>

  <!-- Receive Modal -->
  <dialog id="receive-modal">
    <div class="modal-header">
      <h3>📥 Receive <span id="receive-chain"></span></h3>
      <button class="modal-close" onclick="closeReceive()">×</button>
    </div>
    <div class="modal-body">
      <div class="qr-placeholder">
        <div id="qr-code">QR Code Here</div>
      </div>
      <div class="address-display">
        <input type="text" id="receive-address" readonly onclick="this.select()">
        <button class="btn-primary" onclick="copyAddress()">📋 Copy</button>
      </div>
      <p class="help-text" id="receive-address-label"></p>

      <!-- Wallet Address Management -->
      <div class="wallet-addresses-section">
        <div class="section-header">
          <h4>💼 My Wallet Addresses</h4>
          <button class="btn-secondary btn-sm" onclick="deriveNewAddress()">+ New Address</button>
        </div>
        <div id="wallet-addresses-list" class="addresses-list">
          <p class="loading-text">Loading addresses...</p>
        </div>
      </div>
    </div>
  </dialog>

  <!-- Send Modal -->
  <dialog id="send-modal">
    <div class="modal-header">
      <h3>📤 Send <span id="send-chain"></span></h3>
      <button class="modal-close" onclick="closeSend()">×</button>
    </div>
    <div class="modal-body">
      <form id="send-form" onsubmit="return false;">
        <div class="form-group">
          <label for="send-to">Recipient Address</label>
          <input type="text" id="send-to" placeholder="Enter address" required>
          <span class="form-error" id="send-to-error"></span>
        </div>

        <div class="form-group">
          <label for="send-amount">Amount</label>
          <input type="text" id="send-amount" placeholder="0.00" required>
          <div class="input-helper">
            <span id="send-amount-helper"></span>
            <button type="button" class="btn-link" onclick="setMaxAmount()">Max</button>
          </div>
          <span class="form-error" id="send-amount-error"></span>
        </div>

        <div class="form-group">
          <label>Fee Estimate</label>
          <div class="fee-display" id="fee-display">
            <div class="spinner"></div>
          </div>
        </div>

        <div class="form-actions">
          <button type="button" class="btn-secondary" onclick="closeSend()">Cancel</button>
          <button type="submit" class="btn-primary" onclick="confirmSend()">Review Transaction</button>
        </div>
      </form>
    </div>
  </dialog>

  <!-- Confirmation Modal -->
  <dialog id="confirm-modal">
    <div class="modal-header">
      <h3>⚠️ Confirm Transaction</h3>
      <button class="modal-close" onclick="closeConfirm()">×</button>
    </div>
    <div class="modal-body">
      <div id="confirm-content"></div>
      <div class="form-actions">
        <button class="btn-secondary" onclick="closeConfirm()">Cancel</button>
        <button class="btn-primary" onclick="executeSend()">Send Transaction</button>
      </div>
    </div>
  </dialog>

  <!-- Transaction Details Modal -->
  <dialog id="tx-details-modal">
    <div class="modal-header">
      <h3>📋 Transaction Details</h3>
      <button class="modal-close" onclick="closeTxDetails()">×</button>
    </div>
    <div class="modal-body">
      <div id="tx-details-content"></div>
    </div>
  </dialog>

  <!-- Manage Addresses Modal -->
  <dialog id="manage-addresses-modal" class="large-modal">
    <div class="modal-header">
      <h3>💼 Manage Addresses <span id="manage-addresses-chain"></span></h3>
      <button class="modal-close" onclick="closeManageAddresses()">×</button>
    </div>
    <div class="modal-body">
      <div class="section-header">
        <p class="help-text">Manage your wallet addresses. You can derive new addresses, give them labels (pet-names), and select which one to use for receiving.</p>
        <button class="btn-primary btn-sm" onclick="deriveNewAddressFromManage()">+ Derive New Address</button>
      </div>
      <div id="manage-addresses-list" class="addresses-list">
        <p class="loading-text">Loading addresses...</p>
      </div>
    </div>
  </dialog>

  <!-- Settings Modal -->
  <dialog id="settings-modal" class="settings-modal">
    <div class="modal-header">
      <h3>⚙️ Settings</h3>
      <button class="modal-close" onclick="closeSettings()">×</button>
    </div>
    <div class="modal-body settings-body">

      <!-- Identity Section -->
      <section class="settings-section">
        <h4>🔐 Device Identity</h4>
        <div class="settings-card">
          <div class="settings-row">
            <span class="settings-label">Public Key:</span>
            <span class="settings-value" id="identity-public-key">Loading...</span>
          </div>
          <div class="settings-row">
            <span class="settings-label">Device Name:</span>
            <span class="settings-value" id="identity-device-name">Loading...</span>
          </div>
          <div class="settings-row">
            <span class="settings-label">Status:</span>
            <span class="settings-value" id="identity-status">
              <span class="status-badge" id="identity-status-badge">Active</span>
            </span>
          </div>
        </div>
        <div class="settings-actions">
          <button class="btn-secondary btn-small" onclick="exportIdentity()">📥 Export Identity Mnemonic</button>
          <button class="btn-secondary btn-small" onclick="showWhoAmI()">👤 Show Details</button>
        </div>
      </section>

      <!-- P2P Sync Section -->
      <section class="settings-section">
        <h4>🔄 P2P Multi-Device Sync</h4>
        <div class="settings-card">
          <div class="settings-row">
            <span class="settings-label">Sync Status:</span>
            <span class="settings-value">
              <span class="status-badge status-syncing" id="sync-status-badge">Syncing...</span>
            </span>
          </div>
          <div class="settings-row">
            <span class="settings-label">Connected Devices:</span>
            <span class="settings-value" id="sync-device-count">0</span>
          </div>
          <div class="settings-row">
            <span class="settings-label">Last Sync:</span>
            <span class="settings-value" id="sync-last-time">Never</span>
          </div>
        </div>
        <div class="settings-actions">
          <button class="btn-secondary btn-small" onclick="addDevice()">➕ Add Device</button>
          <button class="btn-secondary btn-small" onclick="joinDevice()">🔗 Join Device</button>
          <button class="btn-secondary btn-small" onclick="showDevices()">📱 Manage Devices</button>
          <button class="btn-secondary btn-small" onclick="forceSyncNow()">🔄 Sync Now</button>
        </div>
      </section>

      <!-- Security Section -->
      <section class="settings-section">
        <h4>🔒 Security</h4>
        <div class="settings-card">
          <div class="settings-info">
            <p>Your wallet mnemonics are encrypted with a password.</p>
            <p>You can change your password below. You'll need your current password to make changes.</p>
          </div>
          <div class="settings-row">
            <span class="settings-label">Encryption:</span>
            <span class="settings-value">
              <span class="status-badge status-active">Enabled</span>
            </span>
          </div>
          <div class="settings-row">
            <span class="settings-label">Password:</span>
            <span class="settings-value monospace">••••••••</span>
          </div>
        </div>
        <div class="settings-actions">
          <button class="btn-secondary btn-small" onclick="showChangePassword()">🔑 Change Password</button>
        </div>
      </section>

      <!-- Wallet Backup Section -->
      <section class="settings-section">
        <h4>💾 Backup & Recovery</h4>
        <div class="settings-card">
          <div class="settings-info">
            <p>Your wallet uses two recovery phrases:</p>
            <ul>
              <li><strong>Identity (24 words)</strong> - For device attestation and sync</li>
              <li><strong>Wallet (12 words)</strong> - For cryptocurrency keys</li>
            </ul>
            <p class="warning-text">⚠️ Never share these with anyone!</p>
          </div>
        </div>
        <div class="settings-actions">
          <button class="btn-secondary btn-small" onclick="exportWalletMnemonic()">📥 Export Wallet Mnemonic</button>
          <button class="btn-secondary btn-small" onclick="verifyBackup()">✓ Verify Backup</button>
        </div>
      </section>

      <!-- Network Settings -->
      <section class="settings-section">
        <h4>🌐 Network Settings</h4>
        <div class="settings-card">
          <div class="settings-row">
            <span class="settings-label">Default Network:</span>
            <span class="settings-value">
              <select id="default-network-select" class="settings-select" onchange="changeDefaultNetwork()">
                <option value="testnet">Testnet (Recommended)</option>
                <option value="mainnet">Mainnet (Real Funds)</option>
              </select>
            </span>
          </div>
          <div class="settings-row">
            <span class="settings-label">Server URL:</span>
            <span class="settings-value monospace">http://localhost:4004</span>
          </div>
        </div>
      </section>

      <!-- Blockchain Providers -->
      <section class="settings-section">
        <h4>🔗 Blockchain Providers</h4>
        <div class="settings-card">
          <div class="settings-info">
            <p>Configure which API providers to use for blockchain synchronization.</p>
          </div>
          <div class="settings-row">
            <span class="settings-label">Bitcoin Provider:</span>
            <span class="settings-value">
              <select id="bitcoin-provider-select" class="settings-select" onchange="changeBitcoinProvider()">
                <option value="electrum">Electrum (Native WDK)</option>
                <option value="blockstream">Blockstream API</option>
                <option value="mempool">Mempool.space</option>
              </select>
            </span>
          </div>
          <div class="settings-row">
            <span class="settings-label">Ethereum Provider:</span>
            <span class="settings-value">
              <select id="ethereum-provider-select" class="settings-select" onchange="changeEthereumProvider()">
                <option value="blockscout">Blockscout (No API Key)</option>
                <option value="etherscan">Etherscan (Requires Key)</option>
                <option value="alchemy">Alchemy (Requires Key)</option>
              </select>
            </span>
          </div>
          <div id="ethereum-api-key-row" class="settings-row" style="display: none;">
            <span class="settings-label">Ethereum API Key:</span>
            <span class="settings-value">
              <input type="text" id="ethereum-api-key-input" class="settings-input" placeholder="Enter API key">
              <button class="btn-small" onclick="saveEthereumApiKey()">Save</button>
            </span>
          </div>
        </div>
      </section>

      <!-- System Info -->
      <section class="settings-section">
        <h4>ℹ️ System Information</h4>
        <div class="settings-card">
          <div class="settings-row">
            <span class="settings-label">Version:</span>
            <span class="settings-value" id="system-version">7.0.0-beta</span>
          </div>
          <div class="settings-row">
            <span class="settings-label">Data Directory:</span>
            <span class="settings-value monospace">~/.hyper-wallet</span>
          </div>
          <div class="settings-row">
            <span class="settings-label">Storage:</span>
            <span class="settings-value" id="storage-info">Autobase + Hyperbee</span>
          </div>
        </div>
      </section>

      <!-- Danger Zone -->
      <section class="settings-section danger-section">
        <h4>⚠️ Danger Zone</h4>
        <div class="settings-card danger-card">
          <div class="settings-info">
            <p><strong>Reset Wallet:</strong> This will delete all local data. Make sure you have backed up your recovery phrases!</p>
          </div>
        </div>
        <div class="settings-actions">
          <button class="btn-danger btn-small" onclick="resetWallet()">🗑️ Reset Wallet</button>
        </div>
      </section>

    </div>
  </dialog>

  <!-- Label Input Modal -->
  <dialog id="label-input-modal">
    <div class="modal-header">
      <h3>🏷️ Address Label</h3>
      <button class="modal-close" onclick="closeLabelInput()">×</button>
    </div>
    <div class="modal-body">
      <p class="help-text">Enter a label for this address (optional):</p>
      <p class="help-text" style="font-size: 0.85em; color: #888;">Examples: "Hardware wallet", "Hot wallet", "MetaMask", "Savings"</p>
      <input type="text" id="label-input-field" class="form-control" placeholder="Enter label" style="margin: 1rem 0;">
      <div class="form-actions">
        <button class="btn-secondary" onclick="closeLabelInput()">Cancel</button>
        <button class="btn-primary" onclick="confirmLabelInput()">Confirm</button>
      </div>
    </div>
  </dialog>

  <!-- Device Add Modal -->
  <dialog id="device-add-modal" class="modal-dialog">
    <div class="modal-header">
      <h3>➕ Add Device</h3>
      <button class="modal-close" onclick="closeDeviceAdd()">×</button>
    </div>
    <div class="modal-body">
      <div class="device-add-content">
        <div class="pairing-status waiting" id="device-pairing-status">
          ⏳ Waiting for device to connect...
        </div>

        <div class="invite-section">
          <label><strong>Invite Code</strong></label>
          <p class="help-text">Share this code with your other device:</p>
          <div class="invite-code-display">
            <textarea id="device-invite-code" readonly onclick="this.select()"></textarea>
            <button class="btn-primary" onclick="copyInviteCode()">📋 Copy</button>
          </div>
        </div>

        <div class="pairing-instructions">
          <h4>On the other device:</h4>
          <ol>
            <li>Open the wallet web UI</li>
            <li>Go to Settings → P2P Multi-Device Sync</li>
            <li>Click "Join Device"</li>
            <li>Paste the invite code</li>
          </ol>
          <p class="help-text">The pairing will happen automatically over the P2P network. Both mnemonics (identity + wallet) will be securely transmitted.</p>
        </div>

        <div id="device-pairing-result" style="display: none;" class="pairing-result">
          <!-- Populated when pairing completes -->
        </div>
      </div>
    </div>
  </dialog>

  <!-- Join Device Modal -->
  <dialog id="device-join-modal" class="modal-dialog">
    <div class="modal-header">
      <h3>🔗 Join Device</h3>
      <button class="modal-close" onclick="closeDeviceJoin()">×</button>
    </div>
    <div class="modal-body">
      <div class="device-join-content">
        <p class="section-description">
          Enter the invite code from your primary device to join this device to the wallet.
          You will receive both mnemonics (identity + wallet) securely over the P2P network.
        </p>

        <div class="form-group">
          <label for="join-invite-code"><strong>Invite Code</strong></label>
          <textarea id="join-invite-code" class="mnemonic-input" placeholder="Paste invite code here..." rows="6"></textarea>
        </div>

        <div class="form-group">
          <label for="join-device-name"><strong>Device Name</strong></label>
          <input type="text" id="join-device-name" placeholder="My Phone" />
        </div>

        <div id="join-error" class="form-error"></div>

        <div class="form-actions">
          <button class="btn-secondary" onclick="closeDeviceJoin()">Cancel</button>
          <button class="btn-primary" onclick="executeDeviceJoin()">Join Wallet</button>
        </div>
      </div>
    </div>
  </dialog>

  <!-- Devices List Modal -->
  <dialog id="devices-modal" class="modal-dialog">
    <div class="modal-header">
      <h3>📱 Manage Devices</h3>
      <button class="modal-close" onclick="closeDevices()">×</button>
    </div>
    <div class="modal-body">
      <div class="devices-content">
        <div class="section-header-row">
          <p class="section-description">
            Devices with access to this wallet. Click keys to copy full value.
          </p>
          <button class="btn-secondary btn-small" onclick="deduplicateDevices()">🧹 Remove Duplicates</button>
        </div>

        <div class="device-keys-legend">
          <div><strong>Identity Key:</strong> Shared across all your devices (from 24-word mnemonic)</div>
          <div><strong>Writer Key:</strong> Unique per device (Autobase writer)</div>
        </div>

        <div id="devices-list" class="devices-list">
          <div class="spinner"></div>
        </div>
      </div>
    </div>
  </dialog>

  <!-- Contacts Modal -->
  <dialog id="contacts-modal" class="modal-dialog">
    <div class="modal-header">
      <h3>📇 Contact Book</h3>
      <button class="modal-close" onclick="closeContacts()">×</button>
    </div>
    <div class="modal-body">
      <div class="contacts-content">
        <div class="section-header-row">
          <p class="section-description">
            Manage your blockchain contacts. Send transactions by name instead of typing addresses.
          </p>
          <button class="btn-primary btn-small" onclick="showAddContact()">➕ Add Contact</button>
        </div>

        <div id="contacts-list" class="contacts-list">
          <div class="spinner"></div>
        </div>
      </div>
    </div>
  </dialog>

  <!-- Add Contact Modal -->
  <dialog id="add-contact-modal" class="modal-dialog">
    <div class="modal-header">
      <h3>➕ Add Contact</h3>
      <button class="modal-close" onclick="closeAddContact()">×</button>
    </div>
    <div class="modal-body">
      <form id="add-contact-form" onsubmit="submitAddContact(event)">
        <div class="form-group">
          <label for="contact-name">Name *</label>
          <input type="text" id="contact-name" required placeholder="Alice">
        </div>

        <div class="form-group">
          <label for="contact-chain">Chain *</label>
          <select id="contact-chain" required>
            <option value="bitcoin">Bitcoin</option>
            <option value="ethereum">Ethereum</option>
          </select>
        </div>

        <div class="form-group">
          <label for="contact-address">Address *</label>
          <input type="text" id="contact-address" required placeholder="bc1q... or 0x...">
          <small class="form-hint">Enter the blockchain address</small>
        </div>

        <div class="form-group">
          <label for="contact-notes">Notes (optional)</label>
          <textarea id="contact-notes" rows="3" placeholder="Friend from college"></textarea>
        </div>

        <div class="form-actions">
          <button type="submit" class="btn-primary">Add Contact</button>
          <button type="button" class="btn-secondary" onclick="closeAddContact()">Cancel</button>
        </div>

        <div id="add-contact-error" class="form-error" style="display: none;"></div>
      </form>
    </div>
  </dialog>

  <!-- Edit Contact Modal -->
  <dialog id="edit-contact-modal" class="modal-dialog">
    <div class="modal-header">
      <h3>✏️ Edit Contact</h3>
      <button class="modal-close" onclick="closeEditContact()">×</button>
    </div>
    <div class="modal-body">
      <form id="edit-contact-form" onsubmit="submitEditContact(event)">
        <input type="hidden" id="edit-contact-id">

        <div class="form-group">
          <label for="edit-contact-name">Name *</label>
          <input type="text" id="edit-contact-name" required>
        </div>

        <div class="form-group">
          <label>Chain</label>
          <input type="text" id="edit-contact-chain" disabled>
        </div>

        <div class="form-group">
          <label>Address</label>
          <input type="text" id="edit-contact-address" disabled>
          <small class="form-hint">Address cannot be changed</small>
        </div>

        <div class="form-group">
          <label for="edit-contact-notes">Notes</label>
          <textarea id="edit-contact-notes" rows="3"></textarea>
        </div>

        <div class="form-actions">
          <button type="submit" class="btn-primary">Save Changes</button>
          <button type="button" class="btn-secondary" onclick="closeEditContact()">Cancel</button>
        </div>

        <div id="edit-contact-error" class="form-error" style="display: none;"></div>
      </form>
    </div>
  </dialog>

  <!-- Add Wallet to Contact Modal -->
  <dialog id="add-wallet-modal" class="modal-dialog">
    <div class="modal-header">
      <h3>➕ Add Wallet to Contact</h3>
      <button class="modal-close" onclick="closeAddWallet()">×</button>
    </div>
    <div class="modal-body">
      <p class="modal-info">
        Adding wallet for: <strong id="add-wallet-contact-name"></strong>
      </p>

      <form id="add-wallet-form" onsubmit="submitAddWallet(event)">
        <div class="form-group">
          <label for="add-wallet-chain">Blockchain *</label>
          <select id="add-wallet-chain" required>
            <option value="bitcoin">₿ Bitcoin</option>
            <option value="ethereum">Ξ Ethereum</option>
            <option value="ethereum-usdt">₮ Ethereum - USDT</option>
          </select>
        </div>

        <div class="form-group">
          <label for="add-wallet-address">Wallet Address *</label>
          <input type="text" id="add-wallet-address" required placeholder="bc1q... or 0x...">
          <small class="form-hint">Enter the blockchain address for this wallet</small>
        </div>

        <div class="form-group">
          <label for="add-wallet-label">Label (optional)</label>
          <input type="text" id="add-wallet-label" placeholder="e.g., Hardware wallet, Hot wallet, MetaMask">
          <small class="form-hint">Optional label to identify this wallet (helpful when you have multiple addresses for the same chain)</small>
        </div>

        <div class="form-actions">
          <button type="submit" class="btn-primary">Add Wallet</button>
          <button type="button" class="btn-secondary" onclick="closeAddWallet()">Cancel</button>
        </div>

        <div id="add-wallet-error" class="form-error" style="display: none;"></div>
      </form>
    </div>
  </dialog>

  <!-- Password Setup/Unlock Dialog -->
  <dialog id="password-dialog">
    <div class="modal-header">
      <h3 id="password-dialog-title">🔐 Unlock Wallet</h3>
    </div>
    <div class="modal-body">
      <div id="password-dialog-warning" class="warning-box" style="display: none; background: #fff3cd; border: 2px solid #ffc107; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
        <strong style="color: #856404;">⚠️ IMPORTANT - SET ENCRYPTION PASSWORD:</strong>
        <ul style="margin: 10px 0; padding-left: 20px; text-align: left; color: #856404;">
          <li>This password will <strong>ENCRYPT</strong> your wallet mnemonics</li>
          <li>You <strong>MUST remember</strong> this password</li>
          <li>Required <strong>every time</strong> you access the wallet</li>
          <li><strong>Cannot be recovered</strong> if forgotten</li>
        </ul>
      </div>

      <div class="form-group">
        <label for="password-input">Password (min 8 characters):</label>
        <input
          type="password"
          id="password-input"
          class="input-field"
          placeholder="Enter password"
          autocomplete="new-password"
          style="width: 100%; padding: 10px; font-size: 16px; border: 2px solid #ddd; border-radius: 6px;"
        />
      </div>

      <div class="form-group" id="password-confirm-group">
        <label for="password-confirm-input">Confirm Password:</label>
        <input
          type="password"
          id="password-confirm-input"
          class="input-field"
          placeholder="Re-enter password"
          autocomplete="new-password"
          style="width: 100%; padding: 10px; font-size: 16px; border: 2px solid #ddd; border-radius: 6px;"
        />
      </div>

      <div id="password-match-error" style="color: #d32f2f; font-weight: bold; display: none; margin-top: 10px;">
        ❌ Passwords do not match
      </div>

      <div id="password-strength" style="margin-top: 15px; display: none;">
        <div style="display: flex; justify-content: space-between; margin-bottom: 5px; font-size: 14px;">
          <span>Strength:</span>
          <span id="password-strength-text" style="font-weight: bold;">Weak</span>
        </div>
        <div style="height: 6px; background: #e0e0e0; border-radius: 3px; overflow: hidden;">
          <div id="password-strength-bar" style="height: 100%; width: 0%; transition: width 0.3s, background 0.3s;"></div>
        </div>
      </div>

      <div class="button-row" style="margin-top: 25px; display: flex; gap: 10px; justify-content: flex-end;">
        <button id="password-cancel-btn" class="secondary-btn" style="padding: 10px 20px; font-size: 16px;">Cancel</button>
        <button id="password-submit-btn" class="primary-btn" disabled style="padding: 10px 20px; font-size: 16px;">Submit</button>
      </div>

      <div id="password-attempts" style="margin-top: 15px; color: #666; font-size: 0.9em; text-align: center;">
      </div>
    </div>
  </dialog>

  <!-- Change Password Dialog -->
  <dialog id="change-password-dialog">
    <div class="modal-header">
      <h3>🔑 Change Password</h3>
    </div>
    <div class="modal-body">
      <div class="warning-box" style="background: #fff3cd; border: 2px solid #ffc107; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
        <strong style="color: #856404;">⚠️ IMPORTANT:</strong>
        <ul style="margin: 10px 0; padding-left: 20px; text-align: left; color: #856404;">
          <li>You'll need your <strong>current password</strong> to change it</li>
          <li><strong>Remember your new password</strong> - it cannot be recovered</li>
          <li>All devices will need the new password on next access</li>
        </ul>
      </div>

      <div class="form-group">
        <label for="current-password-input">Current Password:</label>
        <input
          type="password"
          id="current-password-input"
          class="input-field"
          placeholder="Enter current password"
          autocomplete="current-password"
          style="width: 100%; padding: 10px; font-size: 16px; border: 2px solid #ddd; border-radius: 6px;"
        />
      </div>

      <div class="form-group">
        <label for="new-password-input">New Password (min 8 characters):</label>
        <input
          type="password"
          id="new-password-input"
          class="input-field"
          placeholder="Enter new password"
          autocomplete="new-password"
          style="width: 100%; padding: 10px; font-size: 16px; border: 2px solid #ddd; border-radius: 6px;"
        />
      </div>

      <div class="form-group">
        <label for="new-password-confirm-input">Confirm New Password:</label>
        <input
          type="password"
          id="new-password-confirm-input"
          class="input-field"
          placeholder="Re-enter new password"
          autocomplete="new-password"
          style="width: 100%; padding: 10px; font-size: 16px; border: 2px solid #ddd; border-radius: 6px;"
        />
      </div>

      <div id="change-password-match-error" style="color: #d32f2f; font-weight: bold; display: none; margin-top: 10px;">
        ❌ New passwords do not match
      </div>

      <div id="change-password-strength" style="margin-top: 15px;">
        <div style="display: flex; justify-content: space-between; margin-bottom: 5px; font-size: 14px;">
          <span>Strength:</span>
          <span id="change-password-strength-text" style="font-weight: bold;">Weak</span>
        </div>
        <div style="height: 6px; background: #e0e0e0; border-radius: 3px; overflow: hidden;">
          <div id="change-password-strength-bar" style="height: 100%; width: 0%; transition: width 0.3s, background 0.3s;"></div>
        </div>
      </div>

      <div class="button-row" style="margin-top: 25px; display: flex; gap: 10px; justify-content: flex-end;">
        <button id="change-password-cancel-btn" class="secondary-btn" style="padding: 10px 20px; font-size: 16px;">Cancel</button>
        <button id="change-password-submit-btn" class="primary-btn" disabled style="padding: 10px 20px; font-size: 16px;">Change Password</button>
      </div>

      <div id="change-password-error" style="margin-top: 15px; color: #d32f2f; font-weight: bold; text-align: center; display: none;">
      </div>
    </div>
  </dialog>

  <!-- Password Prompt Modal -->
  <dialog id="password-prompt-modal" class="modal-dialog">
    <div class="modal-header">
      <h3>🔒 Enter Password</h3>
      <button class="modal-close" onclick="closePasswordPrompt()">×</button>
    </div>
    <div class="modal-body">
      <p id="password-prompt-message">Enter your encryption password:</p>
      <div class="form-group">
        <input type="password" id="password-prompt-input" class="form-control" placeholder="Password" autocomplete="current-password" style="width: 100%; padding: 10px; font-size: 16px;">
      </div>
      <div class="form-actions">
        <button class="btn-secondary" onclick="closePasswordPrompt()">Cancel</button>
        <button class="btn-primary" onclick="submitPasswordPrompt()">Submit</button>
      </div>
    </div>
  </dialog>

  <!-- Mnemonic Display Modal -->
  <dialog id="mnemonic-display-modal" class="modal-dialog">
    <div class="modal-header">
      <h3>🔐 Wallet Mnemonic</h3>
      <button class="modal-close" onclick="closeMnemonicDisplay()">×</button>
    </div>
    <div class="modal-body">
      <div class="warning-box" style="background: #fff3cd; border: 2px solid #ffc107; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
        <strong style="color: #856404;">⚠️ CRITICAL: KEEP THIS SAFE!</strong>
        <ul style="margin: 10px 0; padding-left: 20px; text-align: left; color: #856404;">
          <li>This mnemonic controls your cryptocurrency funds</li>
          <li>Anyone with these words can steal your funds</li>
          <li>Write them down on paper and store securely</li>
          <li>Never share with anyone, never store digitally</li>
        </ul>
      </div>

      <div class="mnemonic-display" style="background: #f5f5f5; padding: 20px; border-radius: 8px; margin-bottom: 20px;">
        <pre id="mnemonic-display-text" style="font-size: 16px; font-weight: bold; line-height: 1.8; word-wrap: break-word; white-space: pre-wrap;"></pre>
      </div>

      <div class="form-actions">
        <button class="btn-secondary" onclick="copyMnemonicToClipboard()">📋 Copy to Clipboard</button>
        <button class="btn-primary" onclick="closeMnemonicDisplay()">Close</button>
      </div>
    </div>
  </dialog>

  <!-- Error Toast -->
  <div id="error-toast" class="toast"></div>

  <!-- IPC Communication Layer -->
  <script>
    // Define window.ipcFetch IMMEDIATELY (before wallet.js loads)
    // Queue requests until Pear.ipc is ready

    (function() {
      let requestId = 0
      const pending = new Map()
      const queue = []
      let ipcReady = false

      // Define window.ipcFetch synchronously (queues until ready)
      window.ipcFetch = function(url, options = {}) {
        return new Promise((resolve, reject) => {
          const id = ++requestId
          const request = {
            id,
            url,
            method: options.method || 'GET',
            headers: options.headers || {},
            body: options.body
          }

          pending.set(id, { resolve, reject })

          if (ipcReady && window._ipcPipe) {
            // IPC ready - send immediately (newline-delimited)
            console.log(`[IPC →] Request ${id}: ${request.method} ${request.url}`)
            window._ipcPipe.write(JSON.stringify(request) + '\n')
          } else {
            // IPC not ready - queue request
            console.log(`[IPC ⏳] Queued ${id}: ${request.method} ${request.url}`)
            queue.push(request)
          }

          // Timeout after 30 seconds
          setTimeout(() => {
            if (pending.has(id)) {
              pending.delete(id)
              console.error(`[IPC] Timeout for request ${id}`)
              reject(new Error('IPC timeout'))
            }
          }, 30000)
        }).then(response => {
          // Convert IPC response to fetch-like response
          return {
            ok: response.status >= 200 && response.status < 300,
            status: response.status,
            headers: response.headers,
            json: async () => JSON.parse(response.body),
            text: async () => response.body
          }
        })
      }

      console.log('✅ window.ipcFetch defined (queuing until IPC ready)')

      // Initialize IPC when Pear is available
      const initIPC = () => {
        if (typeof Pear === 'undefined') {
          console.log('⏳ Waiting for Pear...')
          setTimeout(initIPC, 50)
          return
        }

        console.log('🔧 Initializing IPC client...')
        console.log('   Pear object:', Pear)
        console.log('   Pear.constructor:', Pear.constructor)
        console.log('   Pear.constructor.IPC:', Pear.constructor.IPC)

        // Get the pipe from IPC (same way backend does)
        const ipc = Pear[Pear.constructor.IPC]
        const pipe = ipc.pipe()
        console.log('   Got pipe from ipc.pipe()')
        console.log('   Pipe object:', pipe)

        // Buffer for incomplete messages
        let buffer = ''

        // Handle responses from main process via pipe
        pipe.on('data', (data) => {
          try {
            // Convert Buffer/Uint8Array to string properly
            let dataStr
            if (typeof data === 'string') {
              dataStr = data
            } else if (data instanceof Uint8Array || data instanceof Buffer || ArrayBuffer.isView(data)) {
              // Use TextDecoder for proper UTF-8 conversion
              const decoder = new TextDecoder('utf-8')
              dataStr = decoder.decode(data)
            } else {
              console.error('[IPC] Unexpected data type:', typeof data, data)
              return
            }

            console.log('[IPC ← RAW] Received data chunk (string):', dataStr.substring(0, 200))
            console.log('[IPC ← RAW] Data chunk length:', dataStr.length)

            // Append to buffer
            buffer += dataStr
            console.log('[IPC] Buffer now:', buffer.length, 'chars')

            // Process complete messages (newline-delimited)
            const lines = buffer.split('\n')
            console.log('[IPC] Split into', lines.length, 'lines')

            // Keep the last incomplete line in buffer
            buffer = lines.pop() || ''
            console.log('[IPC] Buffer remainder:', buffer.length, 'chars')

            // Process each complete line
            for (let i = 0; i < lines.length; i++) {
              const line = lines[i]
              console.log(`[IPC] Processing line ${i}:`, line.substring(0, 100))

              if (!line.trim()) {
                console.log(`[IPC] Line ${i} is empty, skipping`)
                continue
              }

              try {
                console.log(`[IPC] Attempting to parse line ${i}`)
                const response = JSON.parse(line)
                console.log(`[IPC ←] Response ${response.id}:`, response.status)

                const resolver = pending.get(response.id)
                if (resolver) {
                  pending.delete(response.id)
                  if (response.error) {
                    resolver.reject(new Error(response.error))
                  } else {
                    resolver.resolve(response)
                  }
                } else {
                  console.warn('[IPC] No pending request for ID:', response.id)
                }
              } catch (err) {
                console.error('[IPC] Parse error for line:', line.substring(0, 100), err)
              }
            }
          } catch (err) {
            console.error('[IPC] Fatal error:', err)
          }
        })

        // Add error handlers
        pipe.on('error', (err) => {
          console.error('[IPC] Pipe error:', err)
        })

        pipe.on('end', () => {
          console.log('[IPC] Pipe ended')
        })

        pipe.on('close', () => {
          console.log('[IPC] Pipe closed')
        })

        // Mark IPC as ready
        ipcReady = true
        console.log('✅ IPC ready - processing queued requests')
        console.log('✅ Pipe listening for data events')

        // Process queued requests
        while (queue.length > 0) {
          const request = queue.shift()
          console.log(`[IPC →] Sending queued ${request.id}: ${request.method} ${request.url}`)
          pipe.write(JSON.stringify(request) + '\n')
        }

        // Store pipe for future use
        window._ipcPipe = pipe

        console.log('✅ IPC client initialized')
      }

      // Start initialization immediately
      initIPC()
    })()
  </script>

  <script type="module" src="wallet.js"></script>
</body>
</html>