42 Commits

Author SHA1 Message Date
6e04dc1c74 CJ kill my Self 2025-12-04 14:57:10 -05:00
34b2aed773 Helper 2025-12-04 14:52:39 -05:00
a967176454 hurr 2025-12-04 14:48:36 -05:00
56bc506aae Piss and cum
Some checks failed
Windows Release / build-windows (push) Failing after 2m36s
2025-12-04 14:45:28 -05:00
fd36c61f8f Penits
Some checks failed
Windows Release / build-windows (push) Failing after 1m3s
2025-12-04 14:43:37 -05:00
f017b1322c Create Release 2025-12-04 14:32:51 -05:00
3c293dbca7 RC 1
All checks were successful
Windows Release / build-windows (push) Successful in 3m0s
2025-12-04 14:27:40 -05:00
d5ead6deaf woooooww 2025-12-04 14:10:30 -05:00
4488008494 fix 2025-12-04 14:05:10 -05:00
f0190b3d20 grrrr 2025-12-04 13:59:11 -05:00
1b6a90ce8f no more icons 2025-12-04 13:55:19 -05:00
507fa7d492 wholy dick 2025-12-04 13:49:27 -05:00
29d4068e29 fix: action targets 2025-12-04 13:42:58 -05:00
ab663e7d2b LGTM 2025-12-04 13:38:58 -05:00
4a1b5bfc24 bug: fix build issues, fix datatypes 2025-12-04 13:05:07 -05:00
54d8c96c7f Cleared the cacge 2025-12-04 11:58:33 -05:00
483d1286df asd 2025-12-04 11:50:22 -05:00
6716891197 asd 2025-12-04 11:38:25 -05:00
f293bfc8cd test 2025-12-04 11:19:31 -05:00
ec15c7f74e ashdasd 2025-12-03 23:05:35 -05:00
ab23debac5 uhib 2025-12-03 23:01:39 -05:00
4f78e77579 789ty 2025-12-03 22:57:53 -05:00
9466ee7d9d hiuybv 2025-12-03 22:54:42 -05:00
3382a52e83 grr 2025-12-03 22:51:49 -05:00
afc2ebcfb5 cite 2025-12-03 22:49:00 -05:00
9e1fa511b1 asd 2025-12-03 22:47:06 -05:00
30fafb6630 keep ur dick in my vice 2025-12-03 22:40:59 -05:00
4c720a2923 yhuiuy 2025-12-03 22:36:02 -05:00
a2725c114a g 2025-12-03 22:34:46 -05:00
d432a8cac3 fuck you 2025-12-03 22:33:01 -05:00
63f1ede907 npx 2025-12-03 22:28:52 -05:00
a3cc75e506 workaround 2025-12-03 22:27:29 -05:00
be86c8c224 m5 2025-12-03 22:25:58 -05:00
53e3a390ed suppuku 2025-12-03 22:23:44 -05:00
9372c0fee5 im gonna kill myself you see 2025-12-03 22:20:26 -05:00
f81378d23b golly 2025-12-03 22:16:44 -05:00
316745f716 jordan 2025-12-03 22:15:31 -05:00
65fbe77baf oh no ci 2025-12-03 22:13:35 -05:00
2056e68c53 crafting 2025-12-03 22:12:19 -05:00
4d26048e52 include env 2025-12-03 22:11:25 -05:00
73b9cfefa9 Man Trig 2025-12-03 22:06:40 -05:00
828ad8d97f send 2025-12-03 22:05:52 -05:00
21 changed files with 8291 additions and 142 deletions

View File

@@ -0,0 +1,55 @@
name: Manual Dev Build
on:
workflow_dispatch:
jobs:
build-dev:
runs-on: windows
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Verify Node.js
run: node -v
- name: Install electron-app dependencies
working-directory: electron-app
run: npm ci
- name: Build TypeScript (main + preload)
working-directory: electron-app
run: npm run build:main
- name: Build Renderer (Vite)
working-directory: electron-app
run: npm run build:renderer
- name: List dist directory
working-directory: electron-app
run: |
Write-Host "=== Dist Directory Structure ==="
Get-ChildItem -Recurse dist | Select-Object FullName
- name: Package with electron-builder (unpacked only)
working-directory: electron-app
env:
CSC_IDENTITY_AUTO_DISCOVERY: false
run: npx electron-builder --win --dir
- name: List release directory
working-directory: electron-app
run: |
Write-Host "=== Release Directory ==="
if (Test-Path "release") {
Get-ChildItem -Recurse release | Select-Object FullName, Length
} else {
Write-Host "Release directory not found"
}
- name: Upload unpacked build
uses: actions/upload-artifact@v4
with:
name: rmtPocketWatcher-Windows-Unpacked
path: electron-app/release/win-unpacked/
retention-days: 7

View File

@@ -1,96 +1,57 @@
name: Build and Release
name: Windows Release
on:
workflow_dispatch:
push:
tags:
- 'v*'
paths:
- 'electron-app/package.json'
branches:
- main
jobs:
build-windows:
runs-on: windows-latest
runs-on: windows
steps:
- name: Checkout code
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
# Node 20 should be preinstalled on the Windows host runner; skipping setup-node avoids 7zip download issues.
- name: Verify Node.js
run: node -v
- name: Install electron-app dependencies
working-directory: electron-app
run: npm ci
- name: Build and package for Windows
- name: Create production .env file
working-directory: electron-app
run: npm run electron:build -- --win
run: |
echo "WS_URL=${{ secrets.WS_URL }}" > .env
echo "API_URL=${{ secrets.API_URL }}" >> .env
echo "NODE_ENV=production" >> .env
- name: Upload Windows artifacts
uses: actions/upload-artifact@v4
with:
name: windows-build
path: electron-app/release/*.exe
retention-days: 7
build-linux:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install electron-app dependencies
- name: Build TypeScript
working-directory: electron-app
run: npm ci
run: npm run build
- name: Build and package for Linux
- name: Build Windows portable executable (skip signing)
working-directory: electron-app
run: npm run electron:build -- --linux
env:
CSC_IDENTITY_AUTO_DISCOVERY: false
run: npx electron-builder --win portable --config electron-builder.yml
- name: Upload Linux AppImage
uses: actions/upload-artifact@v4
with:
name: linux-appimage
path: electron-app/release/*.AppImage
retention-days: 7
- name: Get version from package.json
id: version
working-directory: electron-app
run: node scripts/get-version.cjs
- name: Upload Linux deb
uses: actions/upload-artifact@v4
with:
name: linux-deb
path: electron-app/release/*.deb
retention-days: 7
create-release:
needs: [build-windows, build-linux]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
- name: Display structure of downloaded files
run: ls -R ./artifacts
- name: Create Release
- name: Create Release and Upload exe
uses: softprops/action-gh-release@v1
with:
files: |
artifacts/windows-build/*.exe
artifacts/linux-appimage/*.AppImage
artifacts/linux-deb/*.deb
tag_name: v${{ steps.version.outputs.VERSION }}
name: rmtPocketWatcher v${{ steps.version.outputs.VERSION }}
draft: false
prerelease: false
generate_release_notes: true
files: electron-app/release/rmtPocketWatcher-*.exe
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "price_index" ALTER COLUMN "lowest_price" SET DATA TYPE DECIMAL(13,9);
-- AlterTable
ALTER TABLE "raw_vendor_prices" ALTER COLUMN "usd_per_million" SET DATA TYPE DECIMAL(13,9);

View File

@@ -17,7 +17,7 @@ model VendorPrice {
sellerName String? @map("seller_name")
usdPrice Decimal @map("usd_price") @db.Decimal(12, 2)
auecAmount BigInt @map("auec_amount")
usdPerMillion Decimal @map("usd_per_million") @db.Decimal(12, 8)
usdPerMillion Decimal @map("usd_per_million") @db.Decimal(13, 9)
deliveryTime String? @map("delivery_time")
url String
@@ -31,7 +31,7 @@ model VendorPrice {
model PriceIndex {
id String @id @default(uuid())
timestamp DateTime @default(now()) @db.Timestamptz(3)
lowestPrice Decimal @map("lowest_price") @db.Decimal(12, 8)
lowestPrice Decimal @map("lowest_price") @db.Decimal(13, 9)
vendor String
sellerName String? @map("seller_name")

View File

@@ -1,5 +1,8 @@
# WebSocket connection URL
WS_URL=ws://localhost:3000/ws
# API URL
API_URL=http://localhost:3000
# Development mode
NODE_ENV=development

View File

@@ -1,12 +1,12 @@
# Dependencies
node_modules/
package-lock.json
# Build outputs
dist/
dist-electron/
out/
build/
build/*
!build/icon.ico
# Environment variables
.env

BIN
electron-app/build/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@@ -0,0 +1,25 @@
appId: com.lambdabanking.rmtpocketwatcher
productName: rmtPocketWatcher
directories:
output: release
buildResources: build
files:
- dist/**/*
- package.json
extraResources:
- from: .env
to: .env
win:
target:
- portable
artifactName: ${productName}-${version}-${arch}.${ext}
icon: null
mac:
target:
- dmg
identity: null
linux:
target:
- AppImage
- deb
category: Finance

7932
electron-app/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,24 +1,26 @@
{
"name": "rmtpocketwatcher",
"version": "1.0.0",
"version": "1.0.3",
"description": "Real-time AUEC price tracking desktop application",
"type": "module",
"main": "dist/main/index.js",
"scripts": {
"dev": "vite",
"build:main": "tsc --project tsconfig.main.json && tsc --project tsconfig.preload.json",
"build:renderer": "vite build",
"build:main": "npx tsc --project tsconfig.main.json && npx tsc --project tsconfig.preload.json",
"build:renderer": "npx vite build",
"build": "npm run build:main && npm run build:renderer",
"build:portable": "npm run build && npx electron-builder --win portable",
"clean": "rimraf dist",
"clean:dev": "npm run clean && npm run build:main && concurrently \"npm run dev\" \"wait-on http://localhost:5173 && cross-env NODE_ENV=development electron dist/main/index.js\"",
"electron:dev": "concurrently \"npm run dev\" \"wait-on http://localhost:5173 && cross-env NODE_ENV=development electron dist/main/index.js\"",
"electron:build": "npm run build && electron-builder",
"publish": "npm run build && electron-builder --publish always",
"rebuild": "electron-rebuild",
"postinstall": "electron-rebuild",
"test": "jest"
},
"dependencies": {
"better-sqlite3": "^12.5.0",
"dotenv": "^17.2.3",
"electron": "^30.0.0",
"electron-updater": "^6.1.0",
"recharts": "^3.5.1",
"ws": "^8.16.0"
@@ -31,44 +33,14 @@
"@vitejs/plugin-react": "^5.1.1",
"concurrently": "^8.2.0",
"cross-env": "^10.1.0",
"electron": "^30.0.0",
"electron-builder": "^24.9.0",
"electron-rebuild": "^3.2.9",
"react": "^19.2.1",
"react-dom": "^19.2.1",
"rimraf": "^6.1.2",
"typescript": "^5.3.0",
"vite": "^5.0.0",
"wait-on": "^7.2.0"
},
"build": {
"appId": "com.lambdabanking.rmtpocketwatcher",
"productName": "rmtPocketWatcher",
"directories": {
"output": "release"
},
"files": [
"dist/**/*",
"package.json"
],
"publish": {
"provider": "generic",
"url": "${env.RELEASE_URL}"
},
"win": {
"target": [
"nsis"
]
},
"mac": {
"target": [
"dmg"
]
},
"linux": {
"target": [
"AppImage",
"deb"
],
"category": "Finance"
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -0,0 +1,12 @@
const fs = require('fs');
const path = require('path');
const pkgPath = path.join(__dirname, '..', 'package.json');
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
const outputFile = process.env.GITHUB_OUTPUT;
if (outputFile) {
fs.appendFileSync(outputFile, `VERSION=${pkg.version}\n`);
}
console.log(pkg.version);

View File

@@ -33,6 +33,14 @@ export function initDatabase() {
)
`);
// Create settings table for custom AUEC amount
db.exec(`
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
)
`);
console.log('Database initialized at:', dbPath);
}
@@ -80,6 +88,26 @@ export function deleteAlert(id: string): void {
stmt.run(id);
}
export function getCustomAuecAmount(): number | null {
if (!db) throw new Error('Database not initialized');
const stmt = db.prepare('SELECT value FROM settings WHERE key = ?');
const row = stmt.get('customAuecAmount') as any;
return row ? parseFloat(row.value) : null;
}
export function setCustomAuecAmount(amount: number): void {
if (!db) throw new Error('Database not initialized');
const stmt = db.prepare(`
INSERT OR REPLACE INTO settings (key, value)
VALUES (?, ?)
`);
stmt.run('customAuecAmount', amount.toString());
}
export function closeDatabase(): void {
if (db) {
db.close();

View File

@@ -1,8 +1,13 @@
import { app, BrowserWindow, Tray, Menu, nativeImage } from 'electron';
import * as path from 'path';
import { fileURLToPath } from 'url';
import * as dotenv from 'dotenv';
import { setupIpcHandlers, cleanupIpcHandlers } from './ipc-handlers';
import { initDatabase, closeDatabase } from './database';
import { setupIpcHandlers, cleanupIpcHandlers } from './ipc-handlers.js';
import { initDatabase, closeDatabase } from './database.js';
// ES module equivalent of __dirname
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Load environment variables from .env file
// In dev: __dirname = dist/main, so go up to electron-app root
@@ -18,13 +23,22 @@ console.log('API_URL:', process.env.API_URL);
let mainWindow: BrowserWindow | null = null;
let tray: Tray | null = null;
const isDev = process.env.NODE_ENV === 'development';
function getIconPath(): string {
if (isDev) {
return process.platform === 'win32'
? path.join(__dirname, '../../resources/icons/icon.ico')
: path.join(__dirname, '../../resources/icons/logo.png');
}
return process.platform === 'win32'
? path.join(process.resourcesPath, 'icons', 'icon.ico')
: path.join(process.resourcesPath, 'icons', 'logo.png');
}
function createWindow(): void {
// In dev: __dirname = dist/main, logo is at root
// In prod: __dirname = resources/app.asar/dist/main
const iconPath = process.env.NODE_ENV === 'development'
? path.join(__dirname, '../../logo.png')
: path.join(__dirname, '../assets/logo.png');
const iconPath = getIconPath();
console.log('Window icon path:', iconPath);
@@ -36,11 +50,13 @@ function createWindow(): void {
frame: false,
backgroundColor: '#0a0e27',
icon: iconPath,
show: false, // Don't show until loaded
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true,
sandbox: true,
devTools: true, // Enable dev tools in production for debugging
},
title: 'rmtPocketWatcher',
});
@@ -49,13 +65,27 @@ function createWindow(): void {
setupIpcHandlers(mainWindow);
// Load the app
if (process.env.NODE_ENV === 'development') {
if (isDev) {
mainWindow.loadURL('http://localhost:5173');
mainWindow.webContents.openDevTools();
} else {
mainWindow.loadFile(path.join(__dirname, '../../renderer/index.html'));
const rendererPath = path.join(__dirname, '../renderer/index.html');
console.log('Loading renderer from:', rendererPath);
mainWindow.loadFile(rendererPath).catch(err => {
console.error('Failed to load renderer:', err);
});
}
// Show window and open dev tools to see errors
mainWindow.webContents.on('did-fail-load', (_event, errorCode, errorDescription) => {
console.error('Failed to load:', errorCode, errorDescription);
});
mainWindow.webContents.on('did-finish-load', () => {
console.log('Window loaded successfully');
mainWindow?.show();
});
mainWindow.on('closed', () => {
cleanupIpcHandlers();
mainWindow = null;
@@ -65,13 +95,14 @@ function createWindow(): void {
function createTray(): void {
// In dev: __dirname = dist/main, logo is at root
// In prod: __dirname = resources/app.asar/dist/main
const iconPath = process.env.NODE_ENV === 'development'
? path.join(__dirname, '../../logo.png')
: path.join(__dirname, '../assets/logo.png');
const iconPath = getIconPath();
console.log('Tray icon path:', iconPath);
const icon = nativeImage.createFromPath(iconPath).resize({ width: 16, height: 16 });
let icon = nativeImage.createFromPath(iconPath);
if (process.platform !== 'win32') {
icon = icon.resize({ width: 16, height: 16 });
}
tray = new Tray(icon);
@@ -85,6 +116,22 @@ function createTray(): void {
}
}
},
{
label: 'Open DevTools',
click: () => {
if (mainWindow) {
mainWindow.webContents.openDevTools();
}
}
},
{
label: 'Reload',
click: () => {
if (mainWindow) {
mainWindow.reload();
}
}
},
{
label: 'Quit',
click: () => {

View File

@@ -1,7 +1,7 @@
import { ipcMain, BrowserWindow } from 'electron';
import { WebSocketClient } from './websocket-client';
import { PriceIndex } from '../shared/types';
import * as db from './database';
import { WebSocketClient } from './websocket-client.js';
import { PriceIndex } from '../shared/types.js';
import * as db from './database.js';
let wsClient: WebSocketClient | null = null;
@@ -53,6 +53,8 @@ export function setupIpcHandlers(mainWindow: BrowserWindow): void {
try { ipcMain.removeHandler('alerts:add'); } catch (e) { /* ignore */ }
try { ipcMain.removeHandler('alerts:update'); } catch (e) { /* ignore */ }
try { ipcMain.removeHandler('alerts:delete'); } catch (e) { /* ignore */ }
try { ipcMain.removeHandler('settings:getCustomAuecAmount'); } catch (e) { /* ignore */ }
try { ipcMain.removeHandler('settings:setCustomAuecAmount'); } catch (e) { /* ignore */ }
// IPC handlers for renderer requests
ipcMain.handle('ws:connect', async () => {
@@ -155,6 +157,26 @@ export function setupIpcHandlers(mainWindow: BrowserWindow): void {
}
});
// Custom AUEC amount handlers
ipcMain.handle('settings:getCustomAuecAmount', async () => {
try {
return db.getCustomAuecAmount();
} catch (error) {
console.error('Failed to get custom AUEC amount:', error);
return null;
}
});
ipcMain.handle('settings:setCustomAuecAmount', async (_event, amount: number) => {
try {
db.setCustomAuecAmount(amount);
return { success: true };
} catch (error) {
console.error('Failed to set custom AUEC amount:', error);
return { success: false, error: error instanceof Error ? error.message : 'Unknown error' };
}
});
// Auto-connect on startup
wsClient.connect();
}
@@ -181,6 +203,8 @@ export function cleanupIpcHandlers(): void {
ipcMain.removeHandler('alerts:add');
ipcMain.removeHandler('alerts:update');
ipcMain.removeHandler('alerts:delete');
ipcMain.removeHandler('settings:getCustomAuecAmount');
ipcMain.removeHandler('settings:setCustomAuecAmount');
} catch (error) {
// Handlers may not exist, ignore
}

View File

@@ -1,6 +1,6 @@
import WebSocket from 'ws';
import { EventEmitter } from 'events';
import { PriceIndex, WebSocketMessage, HistoricalData } from '../shared/types';
import { PriceIndex, WebSocketMessage, HistoricalData } from '../shared/types.js';
export class WebSocketClient extends EventEmitter {
private ws: WebSocket | null = null;

View File

@@ -52,6 +52,10 @@ export function App() {
const [alertNotification, setAlertNotification] = useState<AlertNotification | null>(null);
const alertAudioRef = useRef<HTMLAudioElement | null>(null);
// Custom AUEC Amount State
const [customAuecAmount, setCustomAuecAmount] = useState<number | null>(null);
const [customAuecInput, setCustomAuecInput] = useState('');
// Load alerts from database on mount
useEffect(() => {
const loadAlerts = async () => {
@@ -65,6 +69,22 @@ export function App() {
loadAlerts();
}, []);
// Load custom AUEC amount from database on mount
useEffect(() => {
const loadCustomAmount = async () => {
try {
const amount = await window.electron.ipcRenderer.invoke('settings:getCustomAuecAmount');
if (amount) {
setCustomAuecAmount(amount);
setCustomAuecInput(amount.toString());
}
} catch (error) {
console.error('Failed to load custom AUEC amount:', error);
}
};
loadCustomAmount();
}, []);
// Fetch initial data on mount
useEffect(() => {
fetchInitialData();
@@ -374,6 +394,21 @@ export function App() {
}
};
const saveCustomAuecAmount = async () => {
const amount = parseFloat(customAuecInput);
if (isNaN(amount) || amount <= 0) {
alert('Please enter a valid AUEC amount');
return;
}
try {
await window.electron.ipcRenderer.invoke('settings:setCustomAuecAmount', amount);
setCustomAuecAmount(amount);
} catch (error) {
console.error('Failed to save custom AUEC amount:', error);
}
};
const dismissNotification = () => {
setAlertNotification(null);
};
@@ -465,7 +500,7 @@ export function App() {
<div style={{ flex: 1, backgroundColor: '#1a1f3a', padding: '15px', borderRadius: '8px' }}>
<div style={{ fontSize: '12px', color: '#888', marginBottom: '5px' }}>LOWEST PRICE</div>
<div style={{ fontSize: '24px', fontWeight: 'bold', color: '#50e3c2' }}>
${latestPrice.lowestPrice.toFixed(4)}
${latestPrice.lowestPrice.toFixed(9)}
</div>
<div style={{ fontSize: '12px', color: '#888' }}>per 1M AUEC</div>
</div>
@@ -552,7 +587,7 @@ export function App() {
{alert.auecAmount.toLocaleString()} AUEC for ${alert.maxPrice.toFixed(2)}
</div>
<div style={{ fontSize: '12px', color: '#888' }}>
${(alert.maxPrice / (alert.auecAmount / 1000000)).toFixed(4)} per 1M AUEC
${(alert.maxPrice / (alert.auecAmount / 1000000)).toFixed(9)} per 1M AUEC
</div>
</div>
<div style={{ display: 'flex', gap: '8px' }}>
@@ -727,7 +762,7 @@ export function App() {
}
return label;
}}
formatter={(value: any, name: string) => [`$${Number(value).toFixed(4)}`, name]}
formatter={(value: any, name: string) => [`$${Number(value).toFixed(9)}`, name]}
wrapperStyle={{ zIndex: 1000 }}
/>
<Legend
@@ -769,7 +804,41 @@ export function App() {
{latestPrice && (
<div style={{ backgroundColor: '#1a1f3a', padding: '20px', borderRadius: '8px' }}>
<h2 style={{ margin: '0 0 15px 0' }}>Current Listings ({latestPrice.allPrices.length})</h2>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '15px' }}>
<h2 style={{ margin: 0 }}>Current Listings ({latestPrice.allPrices.length})</h2>
<div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
<input
type="number"
placeholder="Enter AUEC amount"
value={customAuecInput}
onChange={(e) => setCustomAuecInput(e.target.value)}
style={{
padding: '8px 12px',
backgroundColor: '#0a0e27',
border: '1px solid #2a2f4a',
borderRadius: '4px',
color: '#fff',
fontSize: '14px',
width: '180px',
}}
/>
<button
onClick={saveCustomAuecAmount}
style={{
padding: '8px 16px',
backgroundColor: '#50e3c2',
color: '#0a0e27',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
fontWeight: 'bold',
fontSize: '14px',
}}
>
Set Amount
</button>
</div>
</div>
<div style={{
overflowX: 'auto',
overflowY: 'auto',
@@ -783,16 +852,28 @@ export function App() {
<th style={{ textAlign: 'left', padding: '12px', color: '#888', fontWeight: 'normal' }}>Platform</th>
<th style={{ textAlign: 'left', padding: '12px', color: '#888', fontWeight: 'normal' }}>Seller</th>
<th style={{ textAlign: 'right', padding: '12px', color: '#888', fontWeight: 'normal' }}>Price/1M AUEC</th>
{customAuecAmount && (
<th style={{ textAlign: 'right', padding: '12px', color: '#888', fontWeight: 'normal' }}>
Price for {(customAuecAmount / 1000000).toLocaleString()}M AUEC
</th>
)}
</tr>
</thead>
<tbody>
{latestPrice.allPrices.map((price) => (
{[...latestPrice.allPrices]
.sort((a, b) => a.pricePerMillion - b.pricePerMillion)
.map((price) => (
<tr key={price.id} style={{ borderBottom: '1px solid #2a2f4a' }}>
<td style={{ padding: '12px' }}>{price.platform}</td>
<td style={{ padding: '12px' }}>{price.sellerName}</td>
<td style={{ textAlign: 'right', padding: '12px', color: '#50e3c2', fontWeight: 'bold' }}>
${price.pricePerMillion.toFixed(4)}
${price.pricePerMillion.toFixed(9)}
</td>
{customAuecAmount && (
<td style={{ textAlign: 'right', padding: '12px', color: '#50e3c2', fontWeight: 'bold' }}>
${((price.pricePerMillion * customAuecAmount) / 1000000).toFixed(2)}
</td>
)}
</tr>
))}
</tbody>

View File

@@ -1,17 +1,19 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"module": "ES2020",
"lib": ["ES2020", "DOM"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"noImplicitAny": false,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"jsx": "react-jsx",
"allowImportingTsExtensions": false,
"types": ["node", "electron"],
"allowSyntheticDefaultImports": true
},

View File

@@ -2,7 +2,8 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./dist/main",
"rootDir": "./src"
"rootDir": "./src",
"module": "commonjs"
},
"include": ["src/preload.ts", "src/shared/**/*"]
}

View File

@@ -4,6 +4,7 @@ import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
root: '.',
base: './', // Use relative paths for Electron
build: {
outDir: 'dist/renderer',
emptyOutDir: true,