175 lines
6.8 KiB
JavaScript
175 lines
6.8 KiB
JavaScript
const fallbackDashboard = {
|
|
active_account: {
|
|
name: 'Main Wallet',
|
|
address: '0x1111111111111111111111111111111111111111',
|
|
source: 'SeedPhrase'
|
|
},
|
|
chains: [
|
|
{ id: 1, name: 'Ethereum' },
|
|
{ id: 8453, name: 'Base' }
|
|
],
|
|
balances: [
|
|
{ chain_id: 1, symbol: 'ETH', amount: 1.24, usd_value: 4200 },
|
|
{ chain_id: 8453, symbol: 'USDC', amount: 1280, usd_value: 1280 }
|
|
],
|
|
approvals: [
|
|
{ chain_id: 1, asset_symbol: 'USDC', spender: '0x2222222222222222222222222222222222222222', allowance: 'unlimited', risk: 'High' }
|
|
],
|
|
activities: [
|
|
{ label: 'Swap ETH → USDC', status: 'Confirmed', chain_id: 1 }
|
|
],
|
|
settings: {
|
|
theme: 'System',
|
|
language: 'en',
|
|
currency: 'USD',
|
|
auto_lock_minutes: 15,
|
|
prefer_rabby_over_metamask: true
|
|
}
|
|
};
|
|
|
|
const fallbackFeatures = [
|
|
['Create/unlock wallet', 'Password, lock/unlock, encrypted local state', 'RuntimeImplemented'],
|
|
['Import/create accounts', 'Seed phrase, private key, JSON, watch-only', 'Modeled'],
|
|
['Multi-chain networks', 'Built-in, custom RPC, testnet, offline chain flags', 'Modeled'],
|
|
['Portfolio dashboard', 'Balances, NFTs, DeFi positions, activity', 'UiPrototype'],
|
|
['Dapp provider and permissions', 'Origin/account/chain permissions', 'Modeled'],
|
|
['Signing and security previews', 'Typed data/message/transaction previews and risk', 'Modeled'],
|
|
['Settings and customization', 'Theme, language, currency, auto-lock, default wallet mode', 'UiPrototype']
|
|
];
|
|
|
|
function money(value) {
|
|
return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(value || 0);
|
|
}
|
|
|
|
function shortAddress(address) {
|
|
if (!address || address.length < 12) return address || '';
|
|
return `${address.slice(0, 6)}…${address.slice(-4)}`;
|
|
}
|
|
|
|
function statusLabel(value) {
|
|
if (typeof value === 'string') return value;
|
|
if (value && typeof value === 'object') return Object.keys(value)[0] || 'Modeled';
|
|
return 'Modeled';
|
|
}
|
|
|
|
async function invokeOrFallback(command, fallback) {
|
|
try {
|
|
const tauri = window.__TAURI__?.core;
|
|
if (!tauri) return fallback;
|
|
return await tauri.invoke(command);
|
|
} catch (_) {
|
|
return fallback;
|
|
}
|
|
}
|
|
|
|
function renderDashboard(dashboard) {
|
|
const total = dashboard.balances.reduce((sum, token) => sum + token.usd_value, 0);
|
|
const highRisk = dashboard.approvals.filter((approval) => ['High', 'Critical'].includes(statusLabel(approval.risk))).length;
|
|
|
|
document.getElementById('account-address').textContent = shortAddress(dashboard.active_account.address);
|
|
document.getElementById('portfolio-total').textContent = money(total);
|
|
document.getElementById('hero-total').textContent = money(total);
|
|
document.getElementById('chain-count').textContent = dashboard.chains.length;
|
|
document.getElementById('risk-count').textContent = highRisk;
|
|
document.getElementById('approval-risk').textContent = highRisk;
|
|
|
|
document.getElementById('token-list').innerHTML = dashboard.balances.map((token) => `
|
|
<div class="asset-row">
|
|
<div class="coin-mark">${token.symbol.slice(0, 1)}</div>
|
|
<div><strong>${token.symbol}</strong><span>Chain ${token.chain_id}</span></div>
|
|
<div class="asset-value"><strong>${money(token.usd_value)}</strong><span>${token.amount}</span></div>
|
|
</div>
|
|
`).join('');
|
|
|
|
document.getElementById('approval-list').innerHTML = dashboard.approvals.map((approval) => `
|
|
<div class="approval-row">
|
|
<div><strong>${approval.asset_symbol} allowance</strong><span>${shortAddress(approval.spender)}</span></div>
|
|
<span class="risk ${statusLabel(approval.risk).toLowerCase()}">${statusLabel(approval.risk)}</span>
|
|
</div>
|
|
`).join('');
|
|
|
|
document.getElementById('activity-list').innerHTML = dashboard.activities.map((activity) => `
|
|
<div class="activity-row"><span>↗</span><div><strong>${activity.label}</strong><small>${statusLabel(activity.status)} · Chain ${activity.chain_id}</small></div></div>
|
|
`).join('');
|
|
|
|
const settings = dashboard.settings;
|
|
document.getElementById('settings-summary').innerHTML = [
|
|
['Theme', statusLabel(settings.theme)],
|
|
['Language', settings.language],
|
|
['Currency', settings.currency],
|
|
['Auto-lock', `${settings.auto_lock_minutes} min`],
|
|
['Default wallet', settings.prefer_rabby_over_metamask ? 'Rabby' : 'Browser default']
|
|
].map(([label, value]) => `<div><span>${label}</span><strong>${value}</strong></div>`).join('');
|
|
}
|
|
|
|
function renderFeatures(features) {
|
|
document.getElementById('wallet-features').innerHTML = features.map((feature) => {
|
|
const title = Array.isArray(feature) ? feature[0] : feature.feature;
|
|
const acceptance = Array.isArray(feature) ? feature[1] : feature.acceptance;
|
|
const status = Array.isArray(feature) ? feature[2] : statusLabel(feature.status);
|
|
return `
|
|
<article class="feature-card">
|
|
<span class="feature-status">${status}</span>
|
|
<strong>${title}</strong>
|
|
<p>${acceptance}</p>
|
|
</article>
|
|
`;
|
|
}).join('');
|
|
}
|
|
|
|
async function saveDemoVault() {
|
|
const passphrase = document.getElementById('vault-passphrase').value;
|
|
const output = document.getElementById('vault-status');
|
|
output.textContent = 'Encrypting demo vault…';
|
|
try {
|
|
const tauri = window.__TAURI__?.core;
|
|
if (!tauri) throw new Error('Tauri bridge unavailable in browser preview');
|
|
const status = await tauri.invoke('save_demo_wallet_vault', { passphrase, path: null });
|
|
output.textContent = `Encrypted with ${status.cipher} + ${status.kdf}: ${status.path}`;
|
|
} catch (error) {
|
|
output.textContent = String(error);
|
|
}
|
|
}
|
|
|
|
function wireVaultActions() {
|
|
const button = document.getElementById('save-vault');
|
|
if (button) button.addEventListener('click', saveDemoVault);
|
|
}
|
|
|
|
async function runLocalTerminalCommand() {
|
|
const command = document.getElementById('terminal-command').value;
|
|
const output = document.getElementById('terminal-output');
|
|
output.textContent = 'Running…';
|
|
try {
|
|
const tauri = window.__TAURI__?.core;
|
|
if (!tauri) throw new Error('Tauri bridge unavailable in browser preview');
|
|
const result = await tauri.invoke('run_local_command', { command, cwd: null });
|
|
output.textContent = [
|
|
`$ ${command}`,
|
|
`exit ${result.exit_code}`,
|
|
result.stdout ? `stdout:\n${result.stdout}` : '',
|
|
result.stderr ? `stderr:\n${result.stderr}` : ''
|
|
].filter(Boolean).join('\n');
|
|
} catch (error) {
|
|
output.textContent = String(error);
|
|
}
|
|
}
|
|
|
|
function wireTerminalActions() {
|
|
const button = document.getElementById('run-terminal');
|
|
if (button) button.addEventListener('click', runLocalTerminalCommand);
|
|
}
|
|
|
|
async function boot() {
|
|
const [dashboard, features] = await Promise.all([
|
|
invokeOrFallback('wallet_dashboard', fallbackDashboard),
|
|
invokeOrFallback('wallet_mvp_summary', fallbackFeatures)
|
|
]);
|
|
renderDashboard(dashboard);
|
|
renderFeatures(features);
|
|
wireVaultActions();
|
|
wireTerminalActions();
|
|
}
|
|
|
|
boot();
|