refactor: migrate extension to React and update build configuration
This commit is contained in:
parent
1f2d7e3fec
commit
1e52c572d2
1
Makefile
1
Makefile
@ -30,4 +30,3 @@ build-extension-all: build-wasm-app
|
|||||||
cp wasm_app/pkg/wasm_app.js extension/public/wasm/wasm_app.js
|
cp wasm_app/pkg/wasm_app.js extension/public/wasm/wasm_app.js
|
||||||
cp wasm_app/pkg/wasm_app_bg.wasm extension/public/wasm/wasm_app_bg.wasm
|
cp wasm_app/pkg/wasm_app_bg.wasm extension/public/wasm/wasm_app_bg.wasm
|
||||||
cd extension && npm run build
|
cd extension && npm run build
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ web-sys = { version = "0.3", features = ["console"] }
|
|||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
getrandom = { version = "0.3", features = ["wasm_js"] }
|
getrandom = { version = "0.3", features = ["wasm_js"] }
|
||||||
getrandom_02 = { package = "getrandom", version = "0.2.16", features = ["js"] }
|
getrandom_02 = { package = "getrandom", version = "0.2.16", features = ["js"] }
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = { version = "0.2.92", features = ["serde-serialize"] }
|
||||||
js-sys = "0.3"
|
js-sys = "0.3"
|
||||||
# console_error_panic_hook = "0.1"
|
# console_error_panic_hook = "0.1"
|
||||||
gloo-net = { version = "0.5", features = ["http"] }
|
gloo-net = { version = "0.5", features = ["http"] }
|
||||||
|
72
extension/dist/assets/popup.js
vendored
72
extension/dist/assets/popup.js
vendored
File diff suppressed because one or more lines are too long
BIN
extension/dist/assets/wasm_app_bg.wasm
vendored
Normal file
BIN
extension/dist/assets/wasm_app_bg.wasm
vendored
Normal file
Binary file not shown.
6
extension/dist/popup/index.html
vendored
6
extension/dist/popup/index.html
vendored
@ -4,10 +4,12 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Modular Vault Extension</title>
|
<title>Modular Vault Extension</title>
|
||||||
<link rel="stylesheet" href="popup.css">
|
|
||||||
|
<script type="module" crossorigin src="/assets/popup.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script src="popup.js"></script>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
306
extension/dist/popup/popup.js
vendored
306
extension/dist/popup/popup.js
vendored
@ -1,306 +0,0 @@
|
|||||||
// Simple non-module JavaScript for browser extension popup
|
|
||||||
document.addEventListener('DOMContentLoaded', async function() {
|
|
||||||
const root = document.getElementById('root');
|
|
||||||
root.innerHTML = `
|
|
||||||
<div class="container">
|
|
||||||
<h1>Modular Vault Extension</h1>
|
|
||||||
<div id="status" class="status">Loading WASM module...</div>
|
|
||||||
|
|
||||||
<div id="session-controls">
|
|
||||||
<div id="keyspace-form" class="form-section">
|
|
||||||
<h2>Session</h2>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="keyspace">Keyspace:</label>
|
|
||||||
<input type="text" id="keyspace" placeholder="Enter keyspace name">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="password">Password:</label>
|
|
||||||
<input type="password" id="password" placeholder="Enter password">
|
|
||||||
</div>
|
|
||||||
<div class="button-group">
|
|
||||||
<button id="unlock-btn">Unlock</button>
|
|
||||||
<button id="create-btn">Create New</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="session-info" class="session-info hidden">
|
|
||||||
<h2>Active Session</h2>
|
|
||||||
<p>Current keyspace: <span id="current-keyspace"></span></p>
|
|
||||||
<button id="lock-btn">Lock Session</button>
|
|
||||||
|
|
||||||
<div id="keypair-section" class="form-section">
|
|
||||||
<h2>Keypairs</h2>
|
|
||||||
<button id="create-keypair-btn">Create New Keypair</button>
|
|
||||||
<div id="keypair-list" class="list"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="sign-section" class="form-section hidden">
|
|
||||||
<h2>Sign Message</h2>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="message">Message:</label>
|
|
||||||
<textarea id="message" placeholder="Enter message to sign"></textarea>
|
|
||||||
</div>
|
|
||||||
<button id="sign-btn">Sign</button>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="signature">Signature:</label>
|
|
||||||
<textarea id="signature" readonly></textarea>
|
|
||||||
<button id="copy-btn" class="small">Copy</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// DOM elements
|
|
||||||
const statusEl = document.getElementById('status');
|
|
||||||
const keyspaceFormEl = document.getElementById('keyspace-form');
|
|
||||||
const sessionInfoEl = document.getElementById('session-info');
|
|
||||||
const currentKeyspaceEl = document.getElementById('current-keyspace');
|
|
||||||
const keyspaceInput = document.getElementById('keyspace');
|
|
||||||
const passwordInput = document.getElementById('password');
|
|
||||||
const unlockBtn = document.getElementById('unlock-btn');
|
|
||||||
const createBtn = document.getElementById('create-btn');
|
|
||||||
const lockBtn = document.getElementById('lock-btn');
|
|
||||||
const createKeypairBtn = document.getElementById('create-keypair-btn');
|
|
||||||
const keypairListEl = document.getElementById('keypair-list');
|
|
||||||
const signSectionEl = document.getElementById('sign-section');
|
|
||||||
const messageInput = document.getElementById('message');
|
|
||||||
const signBtn = document.getElementById('sign-btn');
|
|
||||||
const signatureOutput = document.getElementById('signature');
|
|
||||||
const copyBtn = document.getElementById('copy-btn');
|
|
||||||
|
|
||||||
// State
|
|
||||||
let wasmModule = null;
|
|
||||||
let currentKeyspace = null;
|
|
||||||
let keypairs = [];
|
|
||||||
let selectedKeypairId = null;
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
init();
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
try {
|
|
||||||
// Get session state from background
|
|
||||||
const sessionState = await getSessionState();
|
|
||||||
|
|
||||||
if (sessionState.currentKeyspace) {
|
|
||||||
// We have an active session
|
|
||||||
currentKeyspace = sessionState.currentKeyspace;
|
|
||||||
keypairs = sessionState.keypairs || [];
|
|
||||||
selectedKeypairId = sessionState.selectedKeypair;
|
|
||||||
|
|
||||||
updateUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
statusEl.textContent = 'Ready';
|
|
||||||
} catch (error) {
|
|
||||||
statusEl.textContent = 'Error: ' + (error.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateUI() {
|
|
||||||
if (currentKeyspace) {
|
|
||||||
// Show session info
|
|
||||||
keyspaceFormEl.classList.add('hidden');
|
|
||||||
sessionInfoEl.classList.remove('hidden');
|
|
||||||
currentKeyspaceEl.textContent = currentKeyspace;
|
|
||||||
|
|
||||||
// Update keypair list
|
|
||||||
updateKeypairList();
|
|
||||||
|
|
||||||
// Show/hide sign section based on selected keypair
|
|
||||||
if (selectedKeypairId) {
|
|
||||||
signSectionEl.classList.remove('hidden');
|
|
||||||
} else {
|
|
||||||
signSectionEl.classList.add('hidden');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Show keyspace form
|
|
||||||
keyspaceFormEl.classList.remove('hidden');
|
|
||||||
sessionInfoEl.classList.add('hidden');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateKeypairList() {
|
|
||||||
// Clear list
|
|
||||||
keypairListEl.innerHTML = '';
|
|
||||||
|
|
||||||
// Add each keypair
|
|
||||||
keypairs.forEach(keypair => {
|
|
||||||
const item = document.createElement('div');
|
|
||||||
item.className = 'list-item' + (selectedKeypairId === keypair.id ? ' selected' : '');
|
|
||||||
item.innerHTML = `
|
|
||||||
<span>${keypair.label || keypair.id}</span>
|
|
||||||
<button class="select-btn" data-id="${keypair.id}">Select</button>
|
|
||||||
`;
|
|
||||||
keypairListEl.appendChild(item);
|
|
||||||
|
|
||||||
// Add select handler
|
|
||||||
item.querySelector('.select-btn').addEventListener('click', async () => {
|
|
||||||
try {
|
|
||||||
statusEl.textContent = 'Selecting keypair...';
|
|
||||||
// Use background service to select keypair for now
|
|
||||||
await chrome.runtime.sendMessage({
|
|
||||||
action: 'update_session',
|
|
||||||
type: 'keypair_selected',
|
|
||||||
data: keypair.id
|
|
||||||
});
|
|
||||||
selectedKeypairId = keypair.id;
|
|
||||||
updateUI();
|
|
||||||
statusEl.textContent = 'Keypair selected: ' + keypair.id;
|
|
||||||
} catch (error) {
|
|
||||||
statusEl.textContent = 'Error selecting keypair: ' + (error.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get session state from background
|
|
||||||
async function getSessionState() {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
chrome.runtime.sendMessage({ action: 'get_session' }, (response) => {
|
|
||||||
resolve(response || { currentKeyspace: null, keypairs: [], selectedKeypair: null });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event handlers
|
|
||||||
unlockBtn.addEventListener('click', async () => {
|
|
||||||
const keyspace = keyspaceInput.value.trim();
|
|
||||||
const password = passwordInput.value;
|
|
||||||
|
|
||||||
if (!keyspace || !password) {
|
|
||||||
statusEl.textContent = 'Please enter keyspace and password';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
statusEl.textContent = 'Unlocking session...';
|
|
||||||
|
|
||||||
try {
|
|
||||||
// For now, use the background service worker mock
|
|
||||||
await chrome.runtime.sendMessage({
|
|
||||||
action: 'update_session',
|
|
||||||
type: 'keyspace',
|
|
||||||
data: keyspace
|
|
||||||
});
|
|
||||||
|
|
||||||
currentKeyspace = keyspace;
|
|
||||||
updateUI();
|
|
||||||
statusEl.textContent = 'Session unlocked!';
|
|
||||||
|
|
||||||
// Refresh state
|
|
||||||
const state = await getSessionState();
|
|
||||||
keypairs = state.keypairs || [];
|
|
||||||
selectedKeypairId = state.selectedKeypair;
|
|
||||||
updateUI();
|
|
||||||
} catch (error) {
|
|
||||||
statusEl.textContent = 'Error unlocking session: ' + (error.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createBtn.addEventListener('click', async () => {
|
|
||||||
const keyspace = keyspaceInput.value.trim();
|
|
||||||
const password = passwordInput.value;
|
|
||||||
|
|
||||||
if (!keyspace || !password) {
|
|
||||||
statusEl.textContent = 'Please enter keyspace and password';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
statusEl.textContent = 'Creating keyspace...';
|
|
||||||
|
|
||||||
try {
|
|
||||||
// For now, use the background service worker mock
|
|
||||||
await chrome.runtime.sendMessage({
|
|
||||||
action: 'update_session',
|
|
||||||
type: 'keyspace',
|
|
||||||
data: keyspace
|
|
||||||
});
|
|
||||||
|
|
||||||
currentKeyspace = keyspace;
|
|
||||||
updateUI();
|
|
||||||
statusEl.textContent = 'Keyspace created and unlocked!';
|
|
||||||
} catch (error) {
|
|
||||||
statusEl.textContent = 'Error creating keyspace: ' + (error.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
lockBtn.addEventListener('click', async () => {
|
|
||||||
statusEl.textContent = 'Locking session...';
|
|
||||||
|
|
||||||
try {
|
|
||||||
await chrome.runtime.sendMessage({
|
|
||||||
action: 'update_session',
|
|
||||||
type: 'session_locked'
|
|
||||||
});
|
|
||||||
|
|
||||||
currentKeyspace = null;
|
|
||||||
keypairs = [];
|
|
||||||
selectedKeypairId = null;
|
|
||||||
updateUI();
|
|
||||||
statusEl.textContent = 'Session locked';
|
|
||||||
} catch (error) {
|
|
||||||
statusEl.textContent = 'Error locking session: ' + (error.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createKeypairBtn.addEventListener('click', async () => {
|
|
||||||
statusEl.textContent = 'Creating keypair...';
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Generate a mock keypair ID
|
|
||||||
const keyId = 'key-' + Date.now().toString(16);
|
|
||||||
const newKeypair = {
|
|
||||||
id: keyId,
|
|
||||||
label: `Secp256k1-Key-${keypairs.length + 1}`
|
|
||||||
};
|
|
||||||
|
|
||||||
await chrome.runtime.sendMessage({
|
|
||||||
action: 'update_session',
|
|
||||||
type: 'keypair_added',
|
|
||||||
data: newKeypair
|
|
||||||
});
|
|
||||||
|
|
||||||
// Refresh state
|
|
||||||
const state = await getSessionState();
|
|
||||||
keypairs = state.keypairs || [];
|
|
||||||
updateUI();
|
|
||||||
|
|
||||||
statusEl.textContent = 'Keypair created: ' + keyId;
|
|
||||||
} catch (error) {
|
|
||||||
statusEl.textContent = 'Error creating keypair: ' + (error.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
signBtn.addEventListener('click', async () => {
|
|
||||||
const message = messageInput.value.trim();
|
|
||||||
|
|
||||||
if (!message) {
|
|
||||||
statusEl.textContent = 'Please enter a message to sign';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!selectedKeypairId) {
|
|
||||||
statusEl.textContent = 'Please select a keypair first';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
statusEl.textContent = 'Signing message...';
|
|
||||||
|
|
||||||
try {
|
|
||||||
// For now, generate a mock signature
|
|
||||||
const mockSignature = Array.from({length: 64}, () => Math.floor(Math.random() * 16).toString(16)).join('');
|
|
||||||
signatureOutput.value = mockSignature;
|
|
||||||
statusEl.textContent = 'Message signed!';
|
|
||||||
} catch (error) {
|
|
||||||
statusEl.textContent = 'Error signing message: ' + (error.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
copyBtn.addEventListener('click', () => {
|
|
||||||
signatureOutput.select();
|
|
||||||
document.execCommand('copy');
|
|
||||||
statusEl.textContent = 'Signature copied to clipboard!';
|
|
||||||
});
|
|
||||||
});
|
|
BIN
extension/dist/wasm/wasm_app_bg.wasm
vendored
BIN
extension/dist/wasm/wasm_app_bg.wasm
vendored
Binary file not shown.
@ -1,3 +1,5 @@
|
|||||||
|
import init, * as wasmModuleImport from '@wasm/wasm_app.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Browser extension-friendly WebAssembly loader and helper functions
|
* Browser extension-friendly WebAssembly loader and helper functions
|
||||||
* This handles loading the WebAssembly module without relying on ES modules
|
* This handles loading the WebAssembly module without relying on ES modules
|
||||||
@ -21,54 +23,10 @@ export async function loadWasmModule() {
|
|||||||
if (state.initialized || state.loading) {
|
if (state.initialized || state.loading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.loading = true;
|
state.loading = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get paths to WebAssembly files
|
await init();
|
||||||
const wasmJsPath = chrome.runtime.getURL('wasm/wasm_app.js');
|
window.wasm_app = wasmModuleImport;
|
||||||
const wasmBinaryPath = chrome.runtime.getURL('wasm/wasm_app_bg.wasm');
|
|
||||||
|
|
||||||
console.log('Loading WASM JS from:', wasmJsPath);
|
|
||||||
console.log('Loading WASM binary from:', wasmBinaryPath);
|
|
||||||
|
|
||||||
// Create a container for our temporary WebAssembly globals
|
|
||||||
window.__wasmApp = {};
|
|
||||||
|
|
||||||
// Create a script element to load the JS file
|
|
||||||
const script = document.createElement('script');
|
|
||||||
script.src = wasmJsPath;
|
|
||||||
|
|
||||||
// Wait for the script to load
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
script.onload = resolve;
|
|
||||||
script.onerror = () => reject(new Error('Failed to load WASM JavaScript file'));
|
|
||||||
document.head.appendChild(script);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check if the wasm_app global was created
|
|
||||||
if (!window.wasm_app && !window.__wbg_init) {
|
|
||||||
throw new Error('WASM module did not export expected functions');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the initialization function
|
|
||||||
const init = window.__wbg_init || (window.wasm_app && window.wasm_app.default);
|
|
||||||
|
|
||||||
if (!init || typeof init !== 'function') {
|
|
||||||
throw new Error('WASM init function not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch the WASM binary file
|
|
||||||
const response = await fetch(wasmBinaryPath);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Failed to fetch WASM binary: ${response.status} ${response.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the binary data
|
|
||||||
const wasmBinary = await response.arrayBuffer();
|
|
||||||
|
|
||||||
// Initialize the WASM module
|
|
||||||
await init(wasmBinary);
|
|
||||||
|
|
||||||
// Debug logging for available functions in the WebAssembly module
|
// Debug logging for available functions in the WebAssembly module
|
||||||
console.log('Available WebAssembly functions:');
|
console.log('Available WebAssembly functions:');
|
||||||
@ -105,10 +63,8 @@ export async function loadWasmModule() {
|
|||||||
if (typeof wasmModule.init_rhai_env === 'function') {
|
if (typeof wasmModule.init_rhai_env === 'function') {
|
||||||
wasmModule.init_rhai_env();
|
wasmModule.init_rhai_env();
|
||||||
}
|
}
|
||||||
|
|
||||||
state.initialized = true;
|
state.initialized = true;
|
||||||
console.log('WASM module loaded and initialized successfully');
|
console.log('WASM module loaded and initialized successfully');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load WASM module:', error);
|
console.error('Failed to load WASM module:', error);
|
||||||
state.error = error.message || 'Unknown error loading WebAssembly module';
|
state.error = error.message || 'Unknown error loading WebAssembly module';
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script src="popup.js"></script>
|
|
||||||
|
<script type="module" src="./main.jsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
8
extension/popup/main.jsx
Normal file
8
extension/popup/main.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import App from './App';
|
||||||
|
import './style.css';
|
||||||
|
|
||||||
|
// Render the React app
|
||||||
|
const root = createRoot(document.getElementById('root'));
|
||||||
|
root.render(<App />);
|
@ -1,306 +0,0 @@
|
|||||||
// Simple non-module JavaScript for browser extension popup
|
|
||||||
document.addEventListener('DOMContentLoaded', async function() {
|
|
||||||
const root = document.getElementById('root');
|
|
||||||
root.innerHTML = `
|
|
||||||
<div class="container">
|
|
||||||
<h1>Modular Vault Extension</h1>
|
|
||||||
<div id="status" class="status">Loading WASM module...</div>
|
|
||||||
|
|
||||||
<div id="session-controls">
|
|
||||||
<div id="keyspace-form" class="form-section">
|
|
||||||
<h2>Session</h2>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="keyspace">Keyspace:</label>
|
|
||||||
<input type="text" id="keyspace" placeholder="Enter keyspace name">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="password">Password:</label>
|
|
||||||
<input type="password" id="password" placeholder="Enter password">
|
|
||||||
</div>
|
|
||||||
<div class="button-group">
|
|
||||||
<button id="unlock-btn">Unlock</button>
|
|
||||||
<button id="create-btn">Create New</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="session-info" class="session-info hidden">
|
|
||||||
<h2>Active Session</h2>
|
|
||||||
<p>Current keyspace: <span id="current-keyspace"></span></p>
|
|
||||||
<button id="lock-btn">Lock Session</button>
|
|
||||||
|
|
||||||
<div id="keypair-section" class="form-section">
|
|
||||||
<h2>Keypairs</h2>
|
|
||||||
<button id="create-keypair-btn">Create New Keypair</button>
|
|
||||||
<div id="keypair-list" class="list"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="sign-section" class="form-section hidden">
|
|
||||||
<h2>Sign Message</h2>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="message">Message:</label>
|
|
||||||
<textarea id="message" placeholder="Enter message to sign"></textarea>
|
|
||||||
</div>
|
|
||||||
<button id="sign-btn">Sign</button>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="signature">Signature:</label>
|
|
||||||
<textarea id="signature" readonly></textarea>
|
|
||||||
<button id="copy-btn" class="small">Copy</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// DOM elements
|
|
||||||
const statusEl = document.getElementById('status');
|
|
||||||
const keyspaceFormEl = document.getElementById('keyspace-form');
|
|
||||||
const sessionInfoEl = document.getElementById('session-info');
|
|
||||||
const currentKeyspaceEl = document.getElementById('current-keyspace');
|
|
||||||
const keyspaceInput = document.getElementById('keyspace');
|
|
||||||
const passwordInput = document.getElementById('password');
|
|
||||||
const unlockBtn = document.getElementById('unlock-btn');
|
|
||||||
const createBtn = document.getElementById('create-btn');
|
|
||||||
const lockBtn = document.getElementById('lock-btn');
|
|
||||||
const createKeypairBtn = document.getElementById('create-keypair-btn');
|
|
||||||
const keypairListEl = document.getElementById('keypair-list');
|
|
||||||
const signSectionEl = document.getElementById('sign-section');
|
|
||||||
const messageInput = document.getElementById('message');
|
|
||||||
const signBtn = document.getElementById('sign-btn');
|
|
||||||
const signatureOutput = document.getElementById('signature');
|
|
||||||
const copyBtn = document.getElementById('copy-btn');
|
|
||||||
|
|
||||||
// State
|
|
||||||
let wasmModule = null;
|
|
||||||
let currentKeyspace = null;
|
|
||||||
let keypairs = [];
|
|
||||||
let selectedKeypairId = null;
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
init();
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
try {
|
|
||||||
// Get session state from background
|
|
||||||
const sessionState = await getSessionState();
|
|
||||||
|
|
||||||
if (sessionState.currentKeyspace) {
|
|
||||||
// We have an active session
|
|
||||||
currentKeyspace = sessionState.currentKeyspace;
|
|
||||||
keypairs = sessionState.keypairs || [];
|
|
||||||
selectedKeypairId = sessionState.selectedKeypair;
|
|
||||||
|
|
||||||
updateUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
statusEl.textContent = 'Ready';
|
|
||||||
} catch (error) {
|
|
||||||
statusEl.textContent = 'Error: ' + (error.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateUI() {
|
|
||||||
if (currentKeyspace) {
|
|
||||||
// Show session info
|
|
||||||
keyspaceFormEl.classList.add('hidden');
|
|
||||||
sessionInfoEl.classList.remove('hidden');
|
|
||||||
currentKeyspaceEl.textContent = currentKeyspace;
|
|
||||||
|
|
||||||
// Update keypair list
|
|
||||||
updateKeypairList();
|
|
||||||
|
|
||||||
// Show/hide sign section based on selected keypair
|
|
||||||
if (selectedKeypairId) {
|
|
||||||
signSectionEl.classList.remove('hidden');
|
|
||||||
} else {
|
|
||||||
signSectionEl.classList.add('hidden');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Show keyspace form
|
|
||||||
keyspaceFormEl.classList.remove('hidden');
|
|
||||||
sessionInfoEl.classList.add('hidden');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateKeypairList() {
|
|
||||||
// Clear list
|
|
||||||
keypairListEl.innerHTML = '';
|
|
||||||
|
|
||||||
// Add each keypair
|
|
||||||
keypairs.forEach(keypair => {
|
|
||||||
const item = document.createElement('div');
|
|
||||||
item.className = 'list-item' + (selectedKeypairId === keypair.id ? ' selected' : '');
|
|
||||||
item.innerHTML = `
|
|
||||||
<span>${keypair.label || keypair.id}</span>
|
|
||||||
<button class="select-btn" data-id="${keypair.id}">Select</button>
|
|
||||||
`;
|
|
||||||
keypairListEl.appendChild(item);
|
|
||||||
|
|
||||||
// Add select handler
|
|
||||||
item.querySelector('.select-btn').addEventListener('click', async () => {
|
|
||||||
try {
|
|
||||||
statusEl.textContent = 'Selecting keypair...';
|
|
||||||
// Use background service to select keypair for now
|
|
||||||
await chrome.runtime.sendMessage({
|
|
||||||
action: 'update_session',
|
|
||||||
type: 'keypair_selected',
|
|
||||||
data: keypair.id
|
|
||||||
});
|
|
||||||
selectedKeypairId = keypair.id;
|
|
||||||
updateUI();
|
|
||||||
statusEl.textContent = 'Keypair selected: ' + keypair.id;
|
|
||||||
} catch (error) {
|
|
||||||
statusEl.textContent = 'Error selecting keypair: ' + (error.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get session state from background
|
|
||||||
async function getSessionState() {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
chrome.runtime.sendMessage({ action: 'get_session' }, (response) => {
|
|
||||||
resolve(response || { currentKeyspace: null, keypairs: [], selectedKeypair: null });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event handlers
|
|
||||||
unlockBtn.addEventListener('click', async () => {
|
|
||||||
const keyspace = keyspaceInput.value.trim();
|
|
||||||
const password = passwordInput.value;
|
|
||||||
|
|
||||||
if (!keyspace || !password) {
|
|
||||||
statusEl.textContent = 'Please enter keyspace and password';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
statusEl.textContent = 'Unlocking session...';
|
|
||||||
|
|
||||||
try {
|
|
||||||
// For now, use the background service worker mock
|
|
||||||
await chrome.runtime.sendMessage({
|
|
||||||
action: 'update_session',
|
|
||||||
type: 'keyspace',
|
|
||||||
data: keyspace
|
|
||||||
});
|
|
||||||
|
|
||||||
currentKeyspace = keyspace;
|
|
||||||
updateUI();
|
|
||||||
statusEl.textContent = 'Session unlocked!';
|
|
||||||
|
|
||||||
// Refresh state
|
|
||||||
const state = await getSessionState();
|
|
||||||
keypairs = state.keypairs || [];
|
|
||||||
selectedKeypairId = state.selectedKeypair;
|
|
||||||
updateUI();
|
|
||||||
} catch (error) {
|
|
||||||
statusEl.textContent = 'Error unlocking session: ' + (error.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createBtn.addEventListener('click', async () => {
|
|
||||||
const keyspace = keyspaceInput.value.trim();
|
|
||||||
const password = passwordInput.value;
|
|
||||||
|
|
||||||
if (!keyspace || !password) {
|
|
||||||
statusEl.textContent = 'Please enter keyspace and password';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
statusEl.textContent = 'Creating keyspace...';
|
|
||||||
|
|
||||||
try {
|
|
||||||
// For now, use the background service worker mock
|
|
||||||
await chrome.runtime.sendMessage({
|
|
||||||
action: 'update_session',
|
|
||||||
type: 'keyspace',
|
|
||||||
data: keyspace
|
|
||||||
});
|
|
||||||
|
|
||||||
currentKeyspace = keyspace;
|
|
||||||
updateUI();
|
|
||||||
statusEl.textContent = 'Keyspace created and unlocked!';
|
|
||||||
} catch (error) {
|
|
||||||
statusEl.textContent = 'Error creating keyspace: ' + (error.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
lockBtn.addEventListener('click', async () => {
|
|
||||||
statusEl.textContent = 'Locking session...';
|
|
||||||
|
|
||||||
try {
|
|
||||||
await chrome.runtime.sendMessage({
|
|
||||||
action: 'update_session',
|
|
||||||
type: 'session_locked'
|
|
||||||
});
|
|
||||||
|
|
||||||
currentKeyspace = null;
|
|
||||||
keypairs = [];
|
|
||||||
selectedKeypairId = null;
|
|
||||||
updateUI();
|
|
||||||
statusEl.textContent = 'Session locked';
|
|
||||||
} catch (error) {
|
|
||||||
statusEl.textContent = 'Error locking session: ' + (error.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createKeypairBtn.addEventListener('click', async () => {
|
|
||||||
statusEl.textContent = 'Creating keypair...';
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Generate a mock keypair ID
|
|
||||||
const keyId = 'key-' + Date.now().toString(16);
|
|
||||||
const newKeypair = {
|
|
||||||
id: keyId,
|
|
||||||
label: `Secp256k1-Key-${keypairs.length + 1}`
|
|
||||||
};
|
|
||||||
|
|
||||||
await chrome.runtime.sendMessage({
|
|
||||||
action: 'update_session',
|
|
||||||
type: 'keypair_added',
|
|
||||||
data: newKeypair
|
|
||||||
});
|
|
||||||
|
|
||||||
// Refresh state
|
|
||||||
const state = await getSessionState();
|
|
||||||
keypairs = state.keypairs || [];
|
|
||||||
updateUI();
|
|
||||||
|
|
||||||
statusEl.textContent = 'Keypair created: ' + keyId;
|
|
||||||
} catch (error) {
|
|
||||||
statusEl.textContent = 'Error creating keypair: ' + (error.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
signBtn.addEventListener('click', async () => {
|
|
||||||
const message = messageInput.value.trim();
|
|
||||||
|
|
||||||
if (!message) {
|
|
||||||
statusEl.textContent = 'Please enter a message to sign';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!selectedKeypairId) {
|
|
||||||
statusEl.textContent = 'Please select a keypair first';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
statusEl.textContent = 'Signing message...';
|
|
||||||
|
|
||||||
try {
|
|
||||||
// For now, generate a mock signature
|
|
||||||
const mockSignature = Array.from({length: 64}, () => Math.floor(Math.random() * 16).toString(16)).join('');
|
|
||||||
signatureOutput.value = mockSignature;
|
|
||||||
statusEl.textContent = 'Message signed!';
|
|
||||||
} catch (error) {
|
|
||||||
statusEl.textContent = 'Error signing message: ' + (error.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
copyBtn.addEventListener('click', () => {
|
|
||||||
signatureOutput.select();
|
|
||||||
document.execCommand('copy');
|
|
||||||
statusEl.textContent = 'Signature copied to clipboard!';
|
|
||||||
});
|
|
||||||
});
|
|
@ -22,8 +22,8 @@ const copyExtensionFiles = () => {
|
|||||||
const wasmJsDest = resolve(wasmDistDir, 'wasm_app.js');
|
const wasmJsDest = resolve(wasmDistDir, 'wasm_app.js');
|
||||||
fs.copyFileSync(wasmJsSource, wasmJsDest);
|
fs.copyFileSync(wasmJsSource, wasmJsDest);
|
||||||
|
|
||||||
// Copy the wasm binary file
|
// Copy the wasm binary file from the pkg output
|
||||||
const wasmBinSource = resolve(__dirname, 'wasm/wasm_app_bg.wasm');
|
const wasmBinSource = resolve(__dirname, '../wasm_app/pkg/wasm_app_bg.wasm');
|
||||||
const wasmBinDest = resolve(wasmDistDir, 'wasm_app_bg.wasm');
|
const wasmBinDest = resolve(wasmDistDir, 'wasm_app_bg.wasm');
|
||||||
fs.copyFileSync(wasmBinSource, wasmBinDest);
|
fs.copyFileSync(wasmBinSource, wasmBinDest);
|
||||||
|
|
||||||
@ -43,15 +43,7 @@ const copyExtensionFiles = () => {
|
|||||||
fs.mkdirSync(popupDistDir, { recursive: true });
|
fs.mkdirSync(popupDistDir, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy HTML file
|
|
||||||
const htmlSource = resolve(__dirname, 'popup/index.html');
|
|
||||||
const htmlDest = resolve(popupDistDir, 'index.html');
|
|
||||||
fs.copyFileSync(htmlSource, htmlDest);
|
|
||||||
|
|
||||||
// Copy JS file
|
|
||||||
const jsSource = resolve(__dirname, 'popup/popup.js');
|
|
||||||
const jsDest = resolve(popupDistDir, 'popup.js');
|
|
||||||
fs.copyFileSync(jsSource, jsDest);
|
|
||||||
|
|
||||||
// Copy CSS file
|
// Copy CSS file
|
||||||
const cssSource = resolve(__dirname, 'popup/popup.css');
|
const cssSource = resolve(__dirname, 'popup/popup.css');
|
||||||
@ -79,12 +71,22 @@ const copyExtensionFiles = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
console.log('Extension files copied to dist directory');
|
console.log('Extension files copied to dist directory');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@wasm': path.resolve(__dirname, '../wasm_app/pkg')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
react(),
|
react(),
|
||||||
wasm(),
|
wasm(),
|
||||||
|
@ -11,7 +11,7 @@ tokio = { version = "1.37", features = ["rt", "macros"] }
|
|||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
|
|
||||||
getrandom = { version = "0.3", features = ["wasm_js"] }
|
getrandom = { version = "0.3", features = ["wasm_js"] }
|
||||||
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
|
wasm-bindgen = { version = "0.2.92", features = ["serde-serialize"] }
|
||||||
wasm-bindgen-futures = "0.4"
|
wasm-bindgen-futures = "0.4"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ getrandom = { version = "0.3", features = ["wasm_js"] }
|
|||||||
getrandom_02 = { package = "getrandom", version = "0.2.16", features = ["js"] }
|
getrandom_02 = { package = "getrandom", version = "0.2.16", features = ["js"] }
|
||||||
idb = { version = "0.6" }
|
idb = { version = "0.6" }
|
||||||
wasm-bindgen-test = "0.3"
|
wasm-bindgen-test = "0.3"
|
||||||
|
wasm-bindgen = { version = "0.2.92", features = ["serde-serialize"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
@ -34,6 +34,7 @@ rhai = "1.21.0"
|
|||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
wasm-bindgen = { version = "0.2.92", features = ["serde-serialize"] }
|
||||||
wasm-bindgen-test = "0.3"
|
wasm-bindgen-test = "0.3"
|
||||||
# console_error_panic_hook = "0.1"
|
# console_error_panic_hook = "0.1"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user