2026-03-09 14:23:35 +01:00
|
|
|
const { app, BrowserWindow, ipcMain, shell } = require('electron')
|
|
|
|
|
const path = require('path')
|
|
|
|
|
const { execFile } = require('child_process')
|
|
|
|
|
const fs = require('fs')
|
2026-03-09 14:31:40 +01:00
|
|
|
const os = require('os')
|
2026-03-09 14:23:35 +01:00
|
|
|
|
|
|
|
|
const isDev = process.env.NODE_ENV === 'development'
|
|
|
|
|
const VITE_PORT = 5173
|
|
|
|
|
|
|
|
|
|
let mainWindow = null
|
|
|
|
|
|
2026-03-09 14:31:40 +01:00
|
|
|
// ── Wallet storage directory ───────────────────────────────────────────────────
|
|
|
|
|
const WALLET_DIR = path.join(os.homedir(), '.wallet-generator', 'wallet')
|
|
|
|
|
|
|
|
|
|
function ensureWalletDir() {
|
|
|
|
|
fs.mkdirSync(WALLET_DIR, { recursive: true })
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 14:23:35 +01:00
|
|
|
// ── Resolve Python executable ──────────────────────────────────────────────────
|
|
|
|
|
function findPython() {
|
|
|
|
|
const repoRoot = path.join(__dirname, '..', '..')
|
|
|
|
|
const venvPython = path.join(repoRoot, 'venv', 'bin', 'python')
|
|
|
|
|
return fs.existsSync(venvPython) ? venvPython : 'python3'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Call Python CLI ────────────────────────────────────────────────────────────
|
|
|
|
|
function callPython(command, args = {}) {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const python = findPython()
|
|
|
|
|
const repoRoot = path.join(__dirname, '..', '..')
|
|
|
|
|
const cliPath = path.join(repoRoot, 'src', 'cli.py')
|
|
|
|
|
|
2026-03-09 14:31:40 +01:00
|
|
|
execFile(python, [cliPath, command, JSON.stringify(args)], { cwd: repoRoot }, (err, stdout, stderr) => {
|
2026-03-09 14:23:35 +01:00
|
|
|
if (err) { reject(new Error(stderr || err.message)); return }
|
|
|
|
|
try {
|
|
|
|
|
const result = JSON.parse(stdout.trim())
|
|
|
|
|
if (result.ok) resolve(result.data)
|
|
|
|
|
else reject(new Error(result.error))
|
|
|
|
|
} catch {
|
|
|
|
|
reject(new Error('Invalid Python output: ' + stdout))
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── IPC handlers ───────────────────────────────────────────────────────────────
|
|
|
|
|
ipcMain.handle('hd-generate', (_, args) => callPython('hd_generate', args))
|
|
|
|
|
ipcMain.handle('hd-encrypt', (_, args) => callPython('hd_encrypt', args))
|
|
|
|
|
ipcMain.handle('hd-decrypt', (_, args) => callPython('hd_decrypt', args))
|
|
|
|
|
ipcMain.handle('p2pk', (_, args) => callPython('p2pk', args))
|
|
|
|
|
ipcMain.handle('p2pkh', (_, args) => callPython('p2pkh', args))
|
|
|
|
|
ipcMain.handle('p2sh', (_, args) => callPython('p2sh', args))
|
|
|
|
|
ipcMain.handle('p2wpkh', (_, args) => callPython('p2wpkh', args))
|
|
|
|
|
ipcMain.handle('p2tr', (_, args) => callPython('p2tr', args))
|
|
|
|
|
|
2026-03-09 14:31:40 +01:00
|
|
|
ipcMain.handle('save-wallet', (_, { filename, data }) => {
|
|
|
|
|
ensureWalletDir()
|
|
|
|
|
const filePath = path.join(WALLET_DIR, filename)
|
|
|
|
|
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8')
|
|
|
|
|
return filePath
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ipcMain.handle('get-wallet-dir', () => {
|
|
|
|
|
ensureWalletDir()
|
|
|
|
|
return WALLET_DIR
|
|
|
|
|
})
|
|
|
|
|
|
2026-03-09 14:23:35 +01:00
|
|
|
// ── Create window ──────────────────────────────────────────────────────────────
|
|
|
|
|
function createWindow() {
|
|
|
|
|
mainWindow = new BrowserWindow({
|
|
|
|
|
width: 1200,
|
|
|
|
|
height: 750,
|
|
|
|
|
minWidth: 900,
|
|
|
|
|
minHeight: 600,
|
2026-03-09 14:31:40 +01:00
|
|
|
title: 'Wallet Generator',
|
2026-03-09 14:23:35 +01:00
|
|
|
backgroundColor: '#0f1117',
|
|
|
|
|
webPreferences: {
|
|
|
|
|
nodeIntegration: false,
|
|
|
|
|
contextIsolation: true,
|
|
|
|
|
preload: path.join(__dirname, 'preload.cjs'),
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
mainWindow.setMenuBarVisibility(false)
|
|
|
|
|
|
|
|
|
|
const url = isDev
|
|
|
|
|
? `http://localhost:${VITE_PORT}`
|
|
|
|
|
: `file://${path.join(__dirname, '..', 'dist', 'index.html')}`
|
|
|
|
|
|
|
|
|
|
mainWindow.loadURL(url)
|
|
|
|
|
|
|
|
|
|
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
|
|
|
|
shell.openExternal(url)
|
|
|
|
|
return { action: 'deny' }
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
mainWindow.on('closed', () => { mainWindow = null })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── App lifecycle ──────────────────────────────────────────────────────────────
|
|
|
|
|
app.whenReady().then(createWindow)
|
|
|
|
|
|
|
|
|
|
app.on('window-all-closed', () => {
|
|
|
|
|
if (process.platform !== 'darwin') app.quit()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
app.on('activate', () => {
|
|
|
|
|
if (mainWindow === null) createWindow()
|
|
|
|
|
})
|