From 16e0145338a689fd83ad6a323a0f7a9010ec3738 Mon Sep 17 00:00:00 2001 From: NeoZ666 Date: Fri, 20 Mar 2026 05:56:08 +0530 Subject: [PATCH 1/3] Removed redundant sync apis Signed-off-by: NeoZ666 --- api1.js | 87 +++++++-------------- package-lock.json | 8 +- preload.js | 4 +- src/components/market/Market.js | 32 +++----- src/components/taker/TakerInitialization.js | 4 +- 5 files changed, 44 insertions(+), 91 deletions(-) diff --git a/api1.js b/api1.js index 2736f59..25b8d87 100644 --- a/api1.js +++ b/api1.js @@ -404,50 +404,36 @@ function registerTakerHandlers() { source: source, }); - // ✅ Trigger manual sync - api1State.takerInstance.runOfferSyncNow(); + // Trigger sync and wait for completion in the native layer. + api1State.takerInstance.syncOfferbookAndWait(); - // Monitor until complete - const checkInterval = setInterval(() => { - try { - const isSyncing = api1State.takerInstance.isOfferbookSyncing(); - - if (!isSyncing) { - clearInterval(checkInterval); - - console.log(`✅ Offerbook sync completed (${source})`); - api1State.activeSyncs.set(syncId, { - ...api1State.activeSyncs.get(syncId), - status: 'completed', - completedAt: Date.now(), - }); - - // Clear flags - api1State.syncState.isRunning = false; - api1State.syncState.currentSyncId = null; - api1State.syncState.lastSyncTime = Date.now(); - } - } catch (err) { - clearInterval(checkInterval); - console.error(`❌ Sync check failed:`, err); - - api1State.syncState.isRunning = false; - api1State.syncState.currentSyncId = null; - - api1State.activeSyncs.set(syncId, { - ...api1State.activeSyncs.get(syncId), - status: 'failed', - error: err.message, - }); - } - }, 1000); + const completedAt = Date.now(); + console.log(`✅ Offerbook sync completed (${source})`); + api1State.activeSyncs.set(syncId, { + ...api1State.activeSyncs.get(syncId), + status: 'completed', + completedAt, + }); + api1State.syncState.lastSyncTime = completedAt; return { success: true, syncId, source }; } catch (error) { console.error('❌ Sync offerbook failed:', error); + + if (api1State.syncState.currentSyncId) { + const failedSyncId = api1State.syncState.currentSyncId; + api1State.activeSyncs.set(failedSyncId, { + ...api1State.activeSyncs.get(failedSyncId), + status: 'failed', + error: error.message, + completedAt: Date.now(), + }); + } + + return { success: false, error: error.message }; + } finally { api1State.syncState.isRunning = false; api1State.syncState.currentSyncId = null; - return { success: false, error: error.message }; } } @@ -654,25 +640,6 @@ function registerTakerHandlers() { } }); - // Check if offerbook is syncing - ipcMain.handle('taker:isOfferbookSyncing', async () => { - try { - if (!api1State.takerInstance) { - return { success: false, error: 'Taker not initialized' }; - } - - const isSyncing = api1State.takerInstance.isOfferbookSyncing(); - - return { - success: true, - isSyncing: isSyncing, - }; - } catch (error) { - console.error('Failed to check offerbook sync status:', error); - return { success: false, error: error.message, isSyncing: false }; - } - }); - // Get UTXOs ipcMain.handle('taker:getUtxos', async () => { try { @@ -865,8 +832,8 @@ function registerTakerHandlers() { } ); - // Sync offerbook - ipcMain.handle('taker:syncOfferbook', async () => { + // Sync offerbook and wait for completion + ipcMain.handle('taker:syncOfferbookAndWait', async () => { return await startOfferbookSync('manual'); }); @@ -1047,8 +1014,8 @@ function registerCoinswapHandlers() { while (retries < maxRetries) { try { - // Check if sync is still running - const isSyncing = api1State.takerInstance.isOfferbookSyncing(); + // Check if app-level sync is still running. + const isSyncing = api1State.syncState.isRunning; if (!isSyncing) { // Sync complete - now check if we have enough makers diff --git a/package-lock.json b/package-lock.json index 82b229f..5b1a254 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "taker-app", - "version": "1.0.0", + "name": "coinswap-taker", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "taker-app", - "version": "1.0.0", + "name": "coinswap-taker", + "version": "0.2.0", "license": "ISC", "dependencies": { "@tailwindcss/cli": "^4.1.14", diff --git a/preload.js b/preload.js index 3fb88ec..869c472 100644 --- a/preload.js +++ b/preload.js @@ -12,7 +12,8 @@ contextBridge.exposeInMainWorld('api', { getBalance: () => ipcRenderer.invoke('taker:getBalance'), getNextAddress: () => ipcRenderer.invoke('taker:getNextAddress'), sync: () => ipcRenderer.invoke('taker:sync'), - syncOfferbook: () => ipcRenderer.invoke('taker:syncOfferbook'), + syncOfferbookAndWait: () => + ipcRenderer.invoke('taker:syncOfferbookAndWait'), getSyncStatus: (syncId) => ipcRenderer.invoke('taker:getSyncStatus', syncId), getOffers: () => ipcRenderer.invoke('taker:getOffers'), @@ -30,7 +31,6 @@ contextBridge.exposeInMainWorld('api', { testTorConnection: (config) => ipcRenderer.invoke('tor:testConnection', config), getProtocol: () => ipcRenderer.invoke('taker:getProtocol'), - isOfferbookSyncing: () => ipcRenderer.invoke('taker:isOfferbookSyncing'), setupLogging: (dataDir, level) => ipcRenderer.invoke('taker:setupLogging', { dataDir, level }), diff --git a/src/components/market/Market.js b/src/components/market/Market.js index 85f6272..3520588 100644 --- a/src/components/market/Market.js +++ b/src/components/market/Market.js @@ -152,7 +152,7 @@ export function Market(container) { } } - async function syncOfferbook() { + async function syncOfferbookAndWait() { try { // Check if sync is already running const activeSyncId = localStorage.getItem('active_sync_id'); @@ -169,7 +169,7 @@ export function Market(container) { console.log('🔄 Starting offerbook sync...'); - const result = await window.api.taker.syncOfferbook(); + const result = await window.api.taker.syncOfferbookAndWait(); if (!result.success) { throw new Error(result.error || 'Failed to start sync'); @@ -286,7 +286,7 @@ export function Market(container) { updateUI(); try { - const result = await window.api.taker.syncOfferbook(); + const result = await window.api.taker.syncOfferbookAndWait(); if (!result.success) { throw new Error(result.error || 'Failed to start sync'); @@ -296,20 +296,6 @@ export function Market(container) { console.log('📡 Sync started:', syncId); localStorage.setItem('active_sync_id', syncId); - // Poll until sync completes - let isSyncing = true; - while (isSyncing) { - await new Promise((resolve) => setTimeout(resolve, 1000)); - - const statusResult = await window.api.taker.isOfferbookSyncing(); - if (statusResult.success) { - isSyncing = statusResult.isSyncing; - if (isSyncing) { - console.log('⏳ Still syncing...'); - } - } - } - // Sync is done - wait for file write console.log('✅ Offerbook synced - waiting for file write...'); await new Promise((resolve) => setTimeout(resolve, 2000)); @@ -361,11 +347,11 @@ export function Market(container) { } banner.classList.remove('hidden'); - // ✅ CHECK: Is offerbook currently syncing? + // Check if app-level sync is currently running. try { - const syncingResult = await window.api.taker.isOfferbookSyncing(); + const syncingResult = await window.api.taker.getCurrentSyncState(); - if (syncingResult.success && syncingResult.isSyncing) { + if (syncingResult.success && syncingResult.isRunning) { console.log('⏳ Background sync in progress, waiting...'); isLoading = true; updateUI(); @@ -418,15 +404,15 @@ export function Market(container) { isLoading = true; updateUI(); - // ✅ SIMPLE: Just poll until sync is done + // Poll app sync state until sync is done. let isSyncing = true; while (isSyncing) { await new Promise((resolve) => setTimeout(resolve, 1000)); try { - const result = await window.api.taker.isOfferbookSyncing(); + const result = await window.api.taker.getCurrentSyncState(); if (result.success) { - isSyncing = result.isSyncing; + isSyncing = result.isRunning; if (isSyncing) { console.log('⏳ Still syncing...'); } diff --git a/src/components/taker/TakerInitialization.js b/src/components/taker/TakerInitialization.js index 6cae7c9..39ec0a7 100644 --- a/src/components/taker/TakerInitialization.js +++ b/src/components/taker/TakerInitialization.js @@ -325,8 +325,8 @@ export function sendToAddress(address, amount) { return window.api.taker.sendToAddress(address, amount); } -export function syncOfferbook() { - return window.api.taker.fetchOffers(); +export function syncOfferbookAndWait() { + return window.api.taker.syncOfferbookAndWait(); } export function getOfferbook() { From adffef5fce9c4a62c53661de66ad0872304e015c Mon Sep 17 00:00:00 2001 From: iAmKeralis1131f Date: Mon, 23 Mar 2026 15:46:21 +0530 Subject: [PATCH 2/3] update api for sync --- api1.js | 178 +++++++------------- offerbook-worker.js | 39 +++++ src/components/market/Market.js | 75 +++++---- src/components/taker/TakerInitialization.js | 34 +++- src/js/app.js | 48 +++++- src/styles/output.css | 76 --------- 6 files changed, 220 insertions(+), 230 deletions(-) create mode 100644 offerbook-worker.js diff --git a/api1.js b/api1.js index 25b8d87..b0d8f14 100644 --- a/api1.js +++ b/api1.js @@ -284,8 +284,8 @@ function registerTakerHandlers() { // ✅ START BACKGROUND SERVICES setTimeout(async () => { console.log('🔄 Starting background services...'); - await startOfferbookSync('auto'); - startPeriodicSync(); + // Offerbook sync is triggered explicitly on launch and manually by the user. + // The Rust backend keeps offerbook.json up to date internally. startPeriodicWalletSync(); console.log('✅ Background services started'); }, 2000); @@ -320,9 +320,6 @@ function registerTakerHandlers() { console.log('🛑 Shutting down taker...'); console.trace('Shutdown called from:'); // ← ADD THIS to see who called it - // Stop periodic syncs - stopPeriodicSync(); - // Stop wallet sync if (api1State.walletSyncInterval) { clearInterval(api1State.walletSyncInterval); @@ -345,124 +342,80 @@ function registerTakerHandlers() { } }); - async function startOfferbookSync(source = 'manual') { - try { - console.log(`🚀 startOfferbookSync called with source: ${source}`); - - if (!api1State.takerInstance) { - console.log('❌ No taker instance!'); - return { success: false, error: 'Taker not initialized' }; - } - - console.log('✅ Taker instance exists'); - - // Check if sync already running - if (api1State.syncState.isRunning) { - console.log(`⏭️ Sync already running (${source}), skipping`); - return { - success: false, - duplicate: true, - existingSyncId: api1State.syncState.currentSyncId, - }; - } - - const syncId = `${source}_${Date.now()}_${Math.random().toString(36).substring(7)}`; - console.log( - `🔄 [${syncId}] Triggering manual offerbook sync (${source})...` - ); + /** + * Spawn a worker thread to run syncOfferbookAndWait(). + * + * The sync MUST run off the main thread — calling it on the main thread + * blocks Electron's entire event loop and triggers the OS "not responding" + * dialog. The worker creates its own Taker instance. At launch this matches + * the main Taker (both cold). Mid-session the worker's Tor circuits warm up + * quickly because the Tor daemon reuses circuits it already established for + * the main Taker. + * + * Returns { success, syncId } immediately. Caller polls getSyncStatus(syncId). + */ + function startSyncWorker(source = 'manual') { + if (!api1State.takerInstance || !api1State.storedTakerConfig) { + return { success: false, error: 'Taker not initialized' }; + } - // Set flags - api1State.syncState.isRunning = true; - api1State.syncState.currentSyncId = syncId; + if (api1State.syncState.isRunning) { + console.log(`⏭️ Sync already running (${source}), skipping`); + return { success: false, duplicate: true }; + } - // ✅ FIX: Delete old offerbook and create fresh empty one - const offerbookPath = path.join( - api1State.storedTakerConfig.dataDir, - 'offerbook.json' - ); + const syncId = `${source}_${Date.now()}_${Math.random().toString(36).substring(7)}`; + api1State.syncState.isRunning = true; + api1State.syncState.currentSyncId = syncId; + api1State.activeSyncs.set(syncId, { + status: 'syncing', + startedAt: Date.now(), + source, + }); - if (fs.existsSync(offerbookPath)) { - try { - const content = fs.readFileSync(offerbookPath, 'utf8'); - JSON.parse(content); // Test if valid - console.log('✅ Existing offerbook is valid'); - } catch (parseError) { - console.log('⚠️ Corrupted offerbook detected, recreating...'); - fs.writeFileSync( - offerbookPath, - JSON.stringify({ makers: [] }), - 'utf8' - ); - } - } else { - console.log('📝 Creating initial offerbook.json...'); - fs.writeFileSync(offerbookPath, JSON.stringify({ makers: [] }), 'utf8'); - } - api1State.activeSyncs.set(syncId, { - status: 'syncing', - startedAt: Date.now(), - source: source, - }); + const workerConfig = { + dataDir: api1State.DATA_DIR, + walletName: api1State.currentWalletName || api1State.DEFAULT_WALLET_NAME, + rpcConfig: api1State.storedTakerConfig.rpcConfig, + zmqAddr: api1State.storedTakerConfig.zmqAddr, + controlPort: api1State.storedTakerConfig.controlPort || 9051, + torAuthPassword: api1State.storedTakerConfig.torAuthPassword, + password: api1State.storedTakerConfig.password || '', + protocol: api1State.protocolVersion || 'v1', + }; - // Trigger sync and wait for completion in the native layer. - api1State.takerInstance.syncOfferbookAndWait(); + const worker = new Worker(path.join(__dirname, 'offerbook-worker.js'), { + workerData: { config: workerConfig }, + }); - const completedAt = Date.now(); - console.log(`✅ Offerbook sync completed (${source})`); + const finish = (status, extra = {}) => { api1State.activeSyncs.set(syncId, { ...api1State.activeSyncs.get(syncId), - status: 'completed', - completedAt, + ...extra, + status, + completedAt: Date.now(), }); - - api1State.syncState.lastSyncTime = completedAt; - return { success: true, syncId, source }; - } catch (error) { - console.error('❌ Sync offerbook failed:', error); - - if (api1State.syncState.currentSyncId) { - const failedSyncId = api1State.syncState.currentSyncId; - api1State.activeSyncs.set(failedSyncId, { - ...api1State.activeSyncs.get(failedSyncId), - status: 'failed', - error: error.message, - completedAt: Date.now(), - }); - } - - return { success: false, error: error.message }; - } finally { + if (status === 'completed') api1State.syncState.lastSyncTime = Date.now(); api1State.syncState.isRunning = false; api1State.syncState.currentSyncId = null; - } - } - - function startPeriodicSync() { - // Clear any existing interval - if (api1State.syncState.periodicInterval) { - clearInterval(api1State.syncState.periodicInterval); - } + }; - console.log('⏰ Starting periodic sync scheduler (every 15 minutes)'); + worker.on('message', (msg) => { + if (msg.type === 'completed') { + console.log(`✅ [${syncId}] Offerbook sync completed`); + finish('completed'); + } else if (msg.type === 'error') { + console.error(`❌ [${syncId}] Offerbook sync failed:`, msg.error); + finish('failed', { error: msg.error }); + } + }); - api1State.syncState.periodicInterval = setInterval( - async () => { - console.log('⏰ Periodic sync triggered'); - await startOfferbookSync('periodic'); - }, - 15 * 60 * 1000 - ); // 15 minutes - } + worker.on('error', (err) => { + console.error(`❌ [${syncId}] Offerbook worker error:`, err.message); + finish('failed', { error: err.message }); + }); - /** - * Stop periodic offerbook syncs - */ - function stopPeriodicSync() { - if (api1State.syncState.periodicInterval) { - clearInterval(api1State.syncState.periodicInterval); - api1State.syncState.periodicInterval = null; - console.log('⏰ Periodic sync scheduler stopped'); - } + return { success: true, syncId }; } // Get wallet info @@ -832,9 +785,8 @@ function registerTakerHandlers() { } ); - // Sync offerbook and wait for completion - ipcMain.handle('taker:syncOfferbookAndWait', async () => { - return await startOfferbookSync('manual'); + ipcMain.handle('taker:syncOfferbookAndWait', () => { + return startSyncWorker('manual'); }); // Get sync status diff --git a/offerbook-worker.js b/offerbook-worker.js new file mode 100644 index 0000000..3ff2bb0 --- /dev/null +++ b/offerbook-worker.js @@ -0,0 +1,39 @@ +const { parentPort, workerData } = require('worker_threads'); + +/** + * Worker thread for running offerbook sync operations. + * Creates its own Taker instance so the sync doesn't block the main process IPC. + */ + +(async () => { + try { + const coinswapNapi = require('coinswap-napi'); + const { config } = workerData; + + const protocol = config.protocol || 'v1'; + const TakerClass = + protocol === 'v2' ? coinswapNapi.TaprootTaker : coinswapNapi.Taker; + + if (!TakerClass) { + throw new Error( + `${protocol === 'v2' ? 'TaprootTaker' : 'Taker'} class not found. Please rebuild coinswap-napi.` + ); + } + + const taker = new TakerClass( + config.dataDir, + config.walletName || 'taker-wallet', + config.rpcConfig, + config.controlPort || 9051, + config.torAuthPassword || undefined, + config.zmqAddr, + config.password || '' + ); + + taker.syncOfferbookAndWait(); + + parentPort.postMessage({ type: 'completed' }); + } catch (err) { + parentPort.postMessage({ type: 'error', error: err.message }); + } +})(); diff --git a/src/components/market/Market.js b/src/components/market/Market.js index 3520588..588b7d5 100644 --- a/src/components/market/Market.js +++ b/src/components/market/Market.js @@ -8,6 +8,7 @@ export function Market(container) { let syncProgress = null; let currentMakerStatus = 'good'; // 'good', 'bad', or 'unresponsive' let syncCheckInterval = null; + let periodicRefreshInterval = null; // Check sync state every second function startSyncStateMonitor() { @@ -254,59 +255,40 @@ export function Market(container) { async function handleRefresh() { const refreshBtn = content.querySelector('#refresh-market-btn'); - // Check if sync is already running - const activeSyncId = localStorage.getItem('active_sync_id'); - if (activeSyncId) { - try { - const status = await window.api.taker.getSyncStatus(activeSyncId); - if ( - status.success && - (status.sync.status === 'syncing' || - status.sync.status === 'starting') - ) { - showError('Sync already in progress'); - return; - } - } catch (err) { - localStorage.removeItem('active_sync_id'); - } + // Guard against double-click if sync already running + const stateCheck = await window.api.taker.getCurrentSyncState(); + if (stateCheck.success && stateCheck.isRunning) { + showError('Sync already in progress'); + return; } const originalText = refreshBtn.innerHTML; - refreshBtn.disabled = true; refreshBtn.innerHTML = 'Syncing...'; - // ✅ Show sync progress bar ONLY - syncProgress = { - percent: 50, - status: 'syncing', - message: 'Syncing market data...', - }; + syncProgress = { percent: 50, status: 'syncing', message: 'Syncing market data...' }; updateUI(); try { const result = await window.api.taker.syncOfferbookAndWait(); - if (!result.success) { throw new Error(result.error || 'Failed to start sync'); } const syncId = result.syncId; - console.log('📡 Sync started:', syncId); - localStorage.setItem('active_sync_id', syncId); - - // Sync is done - wait for file write - console.log('✅ Offerbook synced - waiting for file write...'); - await new Promise((resolve) => setTimeout(resolve, 2000)); + await new Promise((resolve, reject) => { + const poll = setInterval(async () => { + try { + const status = await window.api.taker.getSyncStatus(syncId); + if (!status.success) { clearInterval(poll); reject(new Error('Failed to get sync status')); return; } + if (status.sync.status === 'completed') { clearInterval(poll); resolve(); } + else if (status.sync.status === 'failed') { clearInterval(poll); reject(new Error(status.sync.error || 'Sync failed')); } + } catch (err) { clearInterval(poll); reject(err); } + }, 1000); + }); - // Clear sync progress syncProgress = null; - - // NOW fetch fresh makers - console.log('✅ Now fetching fresh makers...'); - localStorage.removeItem('active_sync_id'); - await fetchMakers(); // This sets isLoading = false and updates UI + await fetchMakers(); refreshBtn.innerHTML = '✅ Synced!'; setTimeout(() => { @@ -314,7 +296,7 @@ export function Market(container) { refreshBtn.innerHTML = originalText; }, 2000); } catch (error) { - syncProgress = null; + syncProgress = null; updateUI(); refreshBtn.innerHTML = '❌ Failed'; showError(error.message); @@ -395,6 +377,25 @@ export function Market(container) { await fetchMakers(); isLoading = false; updateUI(); + + // Refresh offerbook data every 15 minutes. The Rust backend keeps + // offerbook.json up to date; this just re-reads the file for the UI. + periodicRefreshInterval = setInterval(async () => { + const syncState = await window.api.taker.getCurrentSyncState(); + if (!syncState.isRunning) { + await fetchMakers(); + } + }, 15 * 60 * 1000); + + // Clean up the interval when this component is removed from the DOM + const observer = new MutationObserver(() => { + if (!document.body.contains(content)) { + clearInterval(periodicRefreshInterval); + periodicRefreshInterval = null; + observer.disconnect(); + } + }); + observer.observe(document.body, { childList: true, subtree: true }); } async function monitorExistingSync() { diff --git a/src/components/taker/TakerInitialization.js b/src/components/taker/TakerInitialization.js index 39ec0a7..07c1067 100644 --- a/src/components/taker/TakerInitialization.js +++ b/src/components/taker/TakerInitialization.js @@ -28,6 +28,12 @@ export function TakerInitializationComponent(container, config, onInitialized) { Initializing taker (creates wallet) +
+
+ 3 +
+ Syncing offerbook +
@@ -120,7 +126,7 @@ export function TakerInitializationComponent(container, config, onInitialized) { break; default: icon.className += ' bg-gray-600'; - const num = stepId.includes('tor') ? '1' : '2'; + const num = stepId.includes('tor') ? '1' : stepId.includes('taker') ? '2' : '3'; icon.innerHTML = `${num}`; textEl.className = 'text-gray-400 text-sm'; } @@ -261,8 +267,32 @@ export function TakerInitializationComponent(container, config, onInitialized) { } updateStep('step-taker', 'complete', 'Taker ready (wallet loaded)'); - updateProgress(100, 'Initialization complete'); + updateProgress(66, 'Syncing offerbook...'); + updateStep('step-offerbook', 'active', 'Syncing offerbook...'); + const syncResult = await window.api.taker.syncOfferbookAndWait(); + if (!syncResult.success) { + console.warn('⚠️ Offerbook sync failed on launch:', syncResult.error); + updateStep('step-offerbook', 'error', 'Sync failed (will retry later)'); + } else { + const syncId = syncResult.syncId; + await new Promise((resolve) => { + const poll = setInterval(async () => { + try { + const status = await window.api.taker.getSyncStatus(syncId); + const done = !status.success || status.sync.status === 'completed' || status.sync.status === 'failed'; + if (done) { + clearInterval(poll); + const ok = status.success && status.sync.status === 'completed'; + updateStep('step-offerbook', ok ? 'complete' : 'error', ok ? 'Offerbook ready' : 'Sync failed (will retry later)'); + resolve(); + } + } catch { clearInterval(poll); updateStep('step-offerbook', 'error', 'Sync failed (will retry later)'); resolve(); } + }, 1000); + }); + } + + updateProgress(100, 'Initialization complete'); showSuccess(); } catch (error) { console.error('Initialization failed:', error); diff --git a/src/js/app.js b/src/js/app.js index 7b1451f..1052931 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -184,7 +184,7 @@ async function checkTakerInitialization(config) { if (result.success) { console.log('✅ Taker initialized'); - startMainApp(); + await performLaunchSync(startMainApp); } else { console.error('❌ Taker initialization failed:', result.error); alert('Failed to initialize: ' + result.error); @@ -283,7 +283,7 @@ async function showPasswordPrompt(config) { if (result.success) { modal.remove(); resolve(true); - startMainApp(); + await performLaunchSync(startMainApp); } else if (result.wrongPassword) { errorDiv.classList.remove('hidden'); errorDiv.querySelector('p').textContent = @@ -317,6 +317,50 @@ async function showPasswordPrompt(config) { }); } +/** + * Show an offerbook sync overlay, wait for sync to complete, then call onComplete. + * Used on launch so the user sees makers as soon as the app opens. + */ +async function performLaunchSync(onComplete) { + const overlay = document.createElement('div'); + overlay.id = 'launch-sync-overlay'; + overlay.className = 'fixed inset-0 bg-[#0f1419] flex items-center justify-center z-50'; + overlay.innerHTML = ` +
+
+ +
+

Syncing Market Data

+

Discovering available makers via Tor. This may take a minute...

+
+
+
+
+ `; + document.body.appendChild(overlay); + + try { + const syncResult = await window.api.taker.syncOfferbookAndWait(); + if (syncResult.success) { + const syncId = syncResult.syncId; + await new Promise((resolve) => { + const poll = setInterval(async () => { + try { + const status = await window.api.taker.getSyncStatus(syncId); + const done = !status.success || status.sync.status === 'completed' || status.sync.status === 'failed'; + if (done) { clearInterval(poll); resolve(); } + } catch { clearInterval(poll); resolve(); } + }, 1000); + }); + } + } catch (err) { + console.warn('⚠️ Launch offerbook sync error:', err.message); + } + + overlay.remove(); + onComplete(); +} + // Start the main app after bitcoind connection is established async function startMainApp() { const activeSwap = await SwapStateManager.getActiveSwap(); diff --git a/src/styles/output.css b/src/styles/output.css index 3d9f3c8..9e7d8cb 100644 --- a/src/styles/output.css +++ b/src/styles/output.css @@ -254,9 +254,6 @@ .-top-1 { top: calc(var(--spacing) * -1); } - .top-1 { - top: calc(var(--spacing) * 1); - } .top-1\/2 { top: calc(1/2 * 100%); } @@ -323,9 +320,6 @@ .mx-auto { margin-inline: auto; } - .mt-0 { - margin-top: calc(var(--spacing) * 0); - } .mt-0\.5 { margin-top: calc(var(--spacing) * 0.5); } @@ -539,27 +533,13 @@ .flex-1 { flex: 1; } - .flex-shrink { - flex-shrink: 1; - } .flex-shrink-0 { flex-shrink: 0; } - .border-collapse { - border-collapse: collapse; - } - .-translate-x-1 { - --tw-translate-x: calc(var(--spacing) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } .-translate-x-1\/2 { --tw-translate-x: calc(calc(1/2 * 100%) * -1); translate: var(--tw-translate-x) var(--tw-translate-y); } - .-translate-y-1 { - --tw-translate-y: calc(var(--spacing) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } .-translate-y-1\/2 { --tw-translate-y: calc(calc(1/2 * 100%) * -1); translate: var(--tw-translate-x) var(--tw-translate-y); @@ -585,9 +565,6 @@ .cursor-pointer { cursor: pointer; } - .resize { - resize: both; - } .grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } @@ -816,9 +793,6 @@ .border-\[\#ff6b35\] { border-color: #ff6b35; } - .border-blue-500 { - border-color: var(--color-blue-500); - } .border-blue-500\/30 { border-color: color-mix(in srgb, oklch(62.3% 0.214 259.815) 30%, transparent); @supports (color: color-mix(in lab, red, red)) { @@ -828,9 +802,6 @@ .border-gray-400 { border-color: var(--color-gray-400); } - .border-gray-500 { - border-color: var(--color-gray-500); - } .border-gray-500\/30 { border-color: color-mix(in srgb, oklch(55.1% 0.027 264.364) 30%, transparent); @supports (color: color-mix(in lab, red, red)) { @@ -846,9 +817,6 @@ .border-gray-800 { border-color: var(--color-gray-800); } - .border-green-500 { - border-color: var(--color-green-500); - } .border-green-500\/30 { border-color: color-mix(in srgb, oklch(72.3% 0.219 149.579) 30%, transparent); @supports (color: color-mix(in lab, red, red)) { @@ -864,27 +832,18 @@ .border-orange-400 { border-color: var(--color-orange-400); } - .border-orange-500 { - border-color: var(--color-orange-500); - } .border-orange-500\/30 { border-color: color-mix(in srgb, oklch(70.5% 0.213 47.604) 30%, transparent); @supports (color: color-mix(in lab, red, red)) { border-color: color-mix(in oklab, var(--color-orange-500) 30%, transparent); } } - .border-purple-500 { - border-color: var(--color-purple-500); - } .border-purple-500\/30 { border-color: color-mix(in srgb, oklch(62.7% 0.265 303.9) 30%, transparent); @supports (color: color-mix(in lab, red, red)) { border-color: color-mix(in oklab, var(--color-purple-500) 30%, transparent); } } - .border-red-500 { - border-color: var(--color-red-500); - } .border-red-500\/30 { border-color: color-mix(in srgb, oklch(63.7% 0.237 25.331) 30%, transparent); @supports (color: color-mix(in lab, red, red)) { @@ -894,9 +853,6 @@ .border-transparent { border-color: transparent; } - .border-yellow-500 { - border-color: var(--color-yellow-500); - } .border-yellow-500\/30 { border-color: color-mix(in srgb, oklch(79.5% 0.184 86.047) 30%, transparent); @supports (color: color-mix(in lab, red, red)) { @@ -981,18 +937,12 @@ background-color: color-mix(in oklab, var(--color-blue-500) 20%, transparent); } } - .bg-cyan-500 { - background-color: var(--color-cyan-500); - } .bg-cyan-500\/20 { background-color: color-mix(in srgb, oklch(71.5% 0.143 215.221) 20%, transparent); @supports (color: color-mix(in lab, red, red)) { background-color: color-mix(in oklab, var(--color-cyan-500) 20%, transparent); } } - .bg-gray-500 { - background-color: var(--color-gray-500); - } .bg-gray-500\/20 { background-color: color-mix(in srgb, oklch(55.1% 0.027 264.364) 20%, transparent); @supports (color: color-mix(in lab, red, red)) { @@ -1083,9 +1033,6 @@ .bg-yellow-400 { background-color: var(--color-yellow-400); } - .bg-yellow-500 { - background-color: var(--color-yellow-500); - } .bg-yellow-500\/10 { background-color: color-mix(in srgb, oklch(79.5% 0.184 86.047) 10%, transparent); @supports (color: color-mix(in lab, red, red)) { @@ -1169,9 +1116,6 @@ .px-6 { padding-inline: calc(var(--spacing) * 6); } - .py-0 { - padding-block: calc(var(--spacing) * 0); - } .py-0\.5 { padding-block: calc(var(--spacing) * 0.5); } @@ -1434,22 +1378,12 @@ --tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 8px 10px -6px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); } - .shadow-black { - --tw-shadow-color: #000; - @supports (color: color-mix(in lab, red, red)) { - --tw-shadow-color: color-mix(in oklab, var(--color-black) var(--tw-shadow-alpha), transparent); - } - } .shadow-black\/30 { --tw-shadow-color: color-mix(in srgb, #000 30%, transparent); @supports (color: color-mix(in lab, red, red)) { --tw-shadow-color: color-mix(in oklab, color-mix(in oklab, var(--color-black) 30%, transparent) var(--tw-shadow-alpha), transparent); } } - .outline { - outline-style: var(--tw-outline-style); - outline-width: 1px; - } .filter { filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); } @@ -1458,10 +1392,6 @@ -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); } - .backdrop-filter { - -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); - backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); - } .transition { transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events; transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); @@ -1984,11 +1914,6 @@ inherits: false; initial-value: 0 0 #0000; } -@property --tw-outline-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} @property --tw-blur { syntax: "*"; inherits: false; @@ -2162,7 +2087,6 @@ --tw-ring-offset-width: 0px; --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; - --tw-outline-style: solid; --tw-blur: initial; --tw-brightness: initial; --tw-contrast: initial; From 4ab7c32efeafd7e1a787eb255c274bd6062f9496 Mon Sep 17 00:00:00 2001 From: iAmKeralis1131f Date: Mon, 23 Mar 2026 15:53:08 +0530 Subject: [PATCH 3/3] remove claude code --- .github/workflows/claude-code-review.yml | 45 ------------------------ 1 file changed, 45 deletions(-) delete mode 100644 .github/workflows/claude-code-review.yml diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml deleted file mode 100644 index d39cc8a..0000000 --- a/.github/workflows/claude-code-review.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Claude Code Review -on: - pull_request_target: - types: [opened, synchronize, ready_for_review, reopened] - # Optional: Only run on specific file changes - # paths: - # - "src/**/*.ts" - # - "src/**/*.tsx" - # - "src/**/*.js" - # - "src/**/*.jsx" -jobs: - claude-review: - # Optional: Filter by PR author - # if: | - # github.event.pull_request.user.login == 'external-contributor' || - # github.event.pull_request.user.login == 'new-developer' || - # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - issues: read - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: 0 - - name: Run Claude Code Review - id: claude-review - uses: anthropics/claude-code-action@v1 - with: - claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - github_token: ${{ secrets.GITHUB_TOKEN }} - # Allow workflow to run for all users, including those without write access - # ⚠️ Security Note: This bypasses the write permission requirement check. - # The workflow is still secured by pull_request_target which runs in the base repo context - # and only reviews code without executing it. See: - # https://github.com/anthropics/claude-code-action/blob/main/docs/security.md#access-control - allowed_non_write_users: "*" - plugin_marketplaces: 'https://github.com/anthropics/claude-code.git' - plugins: 'code-review@claude-code-plugins' - prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}' - # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md - # or https://code.claude.com/docs/en/cli-reference for available options \ No newline at end of file