Files
rabby/ui/app.js
T
Tom You fb5a02821e
test / workspace (push) Successful in 12m10s
feat(runtime): add SSH command bridge
2026-07-09 00:20:43 -05:00

223 lines
8.6 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 loadAppConfig() {
return invokeOrFallback('load_app_config', { theme_id: 'rabby-light', restore_workspace: true, profiles: [], shortcuts: [] });
}
async function saveSettings() {
const theme = document.getElementById('theme-select').value;
const output = document.getElementById('settings-status');
output.textContent = 'Saving settings…';
try {
const tauri = window.__TAURI__?.core;
if (!tauri) throw new Error('Tauri bridge unavailable in browser preview');
const config = await loadAppConfig();
config.theme_id = theme;
const saved = await tauri.invoke('save_app_config', { config, path: null });
output.textContent = `Saved ${saved.theme_id}`;
} catch (error) {
output.textContent = String(error);
}
}
function wireSettingsActions() {
const button = document.getElementById('save-settings');
if (button) button.addEventListener('click', saveSettings);
}
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 runSshCommand() {
const host = document.getElementById('ssh-host').value;
const user = document.getElementById('ssh-user').value || null;
const command = document.getElementById('ssh-command').value;
const output = document.getElementById('ssh-output');
output.textContent = 'Connecting…';
try {
const tauri = window.__TAURI__?.core;
if (!tauri) throw new Error('Tauri bridge unavailable in browser preview');
const result = await tauri.invoke('run_ssh_command', { host, user, port: 22, command });
output.textContent = [`ssh ${host} ${command}`, `exit ${result.exit_code}`, result.stdout, result.stderr].filter(Boolean).join('\n');
} catch (error) {
output.textContent = String(error);
}
}
function wireSshActions() {
const button = document.getElementById('run-ssh');
if (button) button.addEventListener('click', runSshCommand);
}
async function boot() {
const [dashboard, features] = await Promise.all([
invokeOrFallback('wallet_dashboard', fallbackDashboard),
invokeOrFallback('wallet_mvp_summary', fallbackFeatures)
]);
renderDashboard(dashboard);
renderFeatures(features);
wireVaultActions();
wireSettingsActions();
wireTerminalActions();
wireSshActions();
}
boot();