Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
c708fedca3
|
|||
|
690015aa40
|
|||
|
6e04dc1c74
|
|||
|
34b2aed773
|
|||
|
a967176454
|
|||
|
56bc506aae
|
|||
|
fd36c61f8f
|
@@ -25,10 +25,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Create production .env file
|
- name: Create production .env file
|
||||||
working-directory: electron-app
|
working-directory: electron-app
|
||||||
run: |
|
env:
|
||||||
echo "WS_URL=${{ secrets.WS_URL }}" > .env
|
WS_URL: ${{ secrets.WS_URL }}
|
||||||
echo "API_URL=${{ secrets.API_URL }}" >> .env
|
API_URL: ${{ secrets.API_URL }}
|
||||||
echo "NODE_ENV=production" >> .env
|
run: node scripts/create-env.cjs
|
||||||
|
|
||||||
|
- name: Verify .env file
|
||||||
|
working-directory: electron-app
|
||||||
|
run: type .env
|
||||||
|
|
||||||
- name: Build TypeScript
|
- name: Build TypeScript
|
||||||
working-directory: electron-app
|
working-directory: electron-app
|
||||||
@@ -43,9 +47,7 @@ jobs:
|
|||||||
- name: Get version from package.json
|
- name: Get version from package.json
|
||||||
id: version
|
id: version
|
||||||
working-directory: electron-app
|
working-directory: electron-app
|
||||||
run: |
|
run: node scripts/get-version.cjs
|
||||||
$version = (Get-Content package.json | ConvertFrom-Json).version
|
|
||||||
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Create Release and Upload exe
|
- name: Create Release and Upload exe
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; media-src 'self' data:; img-src 'self' data:">
|
||||||
<title>rmtPocketWatcher</title>
|
<title>rmtPocketWatcher</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "rmtpocketwatcher",
|
"name": "rmtpocketwatcher",
|
||||||
"version": "1.0.2",
|
"version": "1.0.4",
|
||||||
"description": "Real-time AUEC price tracking desktop application",
|
"description": "Real-time AUEC price tracking desktop application",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/main/index.js",
|
"main": "dist/main/index.js",
|
||||||
|
|||||||
BIN
electron-app/public/notifcation.mp3
Normal file
BIN
electron-app/public/notifcation.mp3
Normal file
Binary file not shown.
14
electron-app/scripts/create-env.cjs
Normal file
14
electron-app/scripts/create-env.cjs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const envPath = path.join(__dirname, '..', '.env');
|
||||||
|
|
||||||
|
const content = `WS_URL=${process.env.WS_URL || ''}
|
||||||
|
API_URL=${process.env.API_URL || ''}
|
||||||
|
NODE_ENV=production
|
||||||
|
`;
|
||||||
|
|
||||||
|
fs.writeFileSync(envPath, content, 'utf8');
|
||||||
|
console.log('.env file created at:', envPath);
|
||||||
|
console.log('Contents:');
|
||||||
|
console.log(content);
|
||||||
12
electron-app/scripts/get-version.cjs
Normal file
12
electron-app/scripts/get-version.cjs
Normal 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);
|
||||||
@@ -13,29 +13,40 @@ export function setupIpcHandlers(mainWindow: BrowserWindow): void {
|
|||||||
wsClient = new WebSocketClient(wsUrl);
|
wsClient = new WebSocketClient(wsUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper to safely send to renderer (window may be destroyed or hidden)
|
||||||
|
const safeSend = (channel: string, ...args: any[]) => {
|
||||||
|
try {
|
||||||
|
if (mainWindow && !mainWindow.isDestroyed() && mainWindow.webContents && !mainWindow.webContents.isDestroyed()) {
|
||||||
|
mainWindow.webContents.send(channel, ...args);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Window was destroyed, ignore
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Forward WebSocket events to renderer
|
// Forward WebSocket events to renderer
|
||||||
wsClient.on('connected', () => {
|
wsClient.on('connected', () => {
|
||||||
mainWindow.webContents.send('ws:connected');
|
safeSend('ws:connected');
|
||||||
});
|
});
|
||||||
|
|
||||||
wsClient.on('disconnected', () => {
|
wsClient.on('disconnected', () => {
|
||||||
mainWindow.webContents.send('ws:disconnected');
|
safeSend('ws:disconnected');
|
||||||
});
|
});
|
||||||
|
|
||||||
wsClient.on('priceUpdate', (data: PriceIndex) => {
|
wsClient.on('priceUpdate', (data: PriceIndex) => {
|
||||||
mainWindow.webContents.send('ws:priceUpdate', data);
|
safeSend('ws:priceUpdate', data);
|
||||||
});
|
});
|
||||||
|
|
||||||
wsClient.on('historyData', (data: any) => {
|
wsClient.on('historyData', (data: any) => {
|
||||||
mainWindow.webContents.send('ws:historyData', data);
|
safeSend('ws:historyData', data);
|
||||||
});
|
});
|
||||||
|
|
||||||
wsClient.on('error', (error: Error) => {
|
wsClient.on('error', (error: Error) => {
|
||||||
mainWindow.webContents.send('ws:error', error.message);
|
safeSend('ws:error', error.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
wsClient.on('maxReconnectAttemptsReached', () => {
|
wsClient.on('maxReconnectAttemptsReached', () => {
|
||||||
mainWindow.webContents.send('ws:maxReconnectAttemptsReached');
|
safeSend('ws:maxReconnectAttemptsReached');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove existing handlers
|
// Remove existing handlers
|
||||||
|
|||||||
@@ -416,21 +416,17 @@ export function App() {
|
|||||||
const handleZoomIn = () => {
|
const handleZoomIn = () => {
|
||||||
if (!fullChartData.length) return;
|
if (!fullChartData.length) return;
|
||||||
|
|
||||||
|
// Keep current X range, only adjust Y max
|
||||||
const currentXStart = zoomState?.xStart ?? fullChartData[0].timestamp;
|
const currentXStart = zoomState?.xStart ?? fullChartData[0].timestamp;
|
||||||
const currentXEnd = zoomState?.xEnd ?? fullChartData[fullChartData.length - 1].timestamp;
|
const currentXEnd = zoomState?.xEnd ?? fullChartData[fullChartData.length - 1].timestamp;
|
||||||
const currentYMax = zoomState?.yMax ?? yAxisDomain[1];
|
const currentYMax = zoomState?.yMax ?? yAxisDomain[1];
|
||||||
|
|
||||||
const xRange = currentXEnd - currentXStart;
|
// Y-axis: decrease max to zoom in (show less range)
|
||||||
const newXRange = xRange * 0.8;
|
|
||||||
|
|
||||||
const xCenter = (currentXStart + currentXEnd) / 2;
|
|
||||||
|
|
||||||
// Y-axis: zoom from 0
|
|
||||||
const newYMax = currentYMax * 0.8;
|
const newYMax = currentYMax * 0.8;
|
||||||
|
|
||||||
setZoomState({
|
setZoomState({
|
||||||
xStart: xCenter - newXRange / 2,
|
xStart: currentXStart,
|
||||||
xEnd: xCenter + newXRange / 2,
|
xEnd: currentXEnd,
|
||||||
yMin: 0,
|
yMin: 0,
|
||||||
yMax: newYMax,
|
yMax: newYMax,
|
||||||
});
|
});
|
||||||
@@ -439,29 +435,83 @@ export function App() {
|
|||||||
const handleZoomOut = () => {
|
const handleZoomOut = () => {
|
||||||
if (!fullChartData.length) return;
|
if (!fullChartData.length) return;
|
||||||
|
|
||||||
|
// Keep current X range, only adjust Y max
|
||||||
|
const currentXStart = zoomState?.xStart ?? fullChartData[0].timestamp;
|
||||||
|
const currentXEnd = zoomState?.xEnd ?? fullChartData[fullChartData.length - 1].timestamp;
|
||||||
|
const currentYMax = zoomState?.yMax ?? yAxisDomain[1];
|
||||||
|
|
||||||
|
// Y-axis: increase max to zoom out (show more range)
|
||||||
|
const newYMax = currentYMax * 1.25;
|
||||||
|
|
||||||
|
setZoomState({
|
||||||
|
xStart: currentXStart,
|
||||||
|
xEnd: currentXEnd,
|
||||||
|
yMin: 0,
|
||||||
|
yMax: newYMax,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTimelineCompress = () => {
|
||||||
|
if (!fullChartData.length) return;
|
||||||
|
|
||||||
const currentXStart = zoomState?.xStart ?? fullChartData[0].timestamp;
|
const currentXStart = zoomState?.xStart ?? fullChartData[0].timestamp;
|
||||||
const currentXEnd = zoomState?.xEnd ?? fullChartData[fullChartData.length - 1].timestamp;
|
const currentXEnd = zoomState?.xEnd ?? fullChartData[fullChartData.length - 1].timestamp;
|
||||||
const currentYMax = zoomState?.yMax ?? yAxisDomain[1];
|
const currentYMax = zoomState?.yMax ?? yAxisDomain[1];
|
||||||
|
|
||||||
const xRange = currentXEnd - currentXStart;
|
const xRange = currentXEnd - currentXStart;
|
||||||
const newXRange = xRange * 1.25;
|
const newXRange = xRange * 0.8; // Compress = show less time
|
||||||
|
|
||||||
const xCenter = (currentXStart + currentXEnd) / 2;
|
const xCenter = (currentXStart + currentXEnd) / 2;
|
||||||
|
|
||||||
|
// Constrain to data bounds
|
||||||
|
const dataXStart = fullChartData[0].timestamp;
|
||||||
|
const dataXEnd = fullChartData[fullChartData.length - 1].timestamp;
|
||||||
|
|
||||||
|
let newXStart = xCenter - newXRange / 2;
|
||||||
|
let newXEnd = xCenter + newXRange / 2;
|
||||||
|
|
||||||
|
// Ensure we stay within data bounds
|
||||||
|
if (newXStart < dataXStart) {
|
||||||
|
newXEnd += dataXStart - newXStart;
|
||||||
|
newXStart = dataXStart;
|
||||||
|
}
|
||||||
|
if (newXEnd > dataXEnd) {
|
||||||
|
newXStart -= newXEnd - dataXEnd;
|
||||||
|
newXEnd = dataXEnd;
|
||||||
|
}
|
||||||
|
newXStart = Math.max(dataXStart, newXStart);
|
||||||
|
newXEnd = Math.min(dataXEnd, newXEnd);
|
||||||
|
|
||||||
|
setZoomState({
|
||||||
|
xStart: newXStart,
|
||||||
|
xEnd: newXEnd,
|
||||||
|
yMin: 0,
|
||||||
|
yMax: currentYMax,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTimelineExpand = () => {
|
||||||
|
if (!fullChartData.length) return;
|
||||||
|
|
||||||
|
const currentXStart = zoomState?.xStart ?? fullChartData[0].timestamp;
|
||||||
|
const currentXEnd = zoomState?.xEnd ?? fullChartData[fullChartData.length - 1].timestamp;
|
||||||
|
const currentYMax = zoomState?.yMax ?? yAxisDomain[1];
|
||||||
|
|
||||||
|
const xRange = currentXEnd - currentXStart;
|
||||||
|
const newXRange = xRange * 1.25; // Expand = show more time
|
||||||
|
const xCenter = (currentXStart + currentXEnd) / 2;
|
||||||
|
|
||||||
|
// Constrain to data bounds
|
||||||
const dataXStart = fullChartData[0].timestamp;
|
const dataXStart = fullChartData[0].timestamp;
|
||||||
const dataXEnd = fullChartData[fullChartData.length - 1].timestamp;
|
const dataXEnd = fullChartData[fullChartData.length - 1].timestamp;
|
||||||
|
|
||||||
const newXStart = Math.max(dataXStart, xCenter - newXRange / 2);
|
const newXStart = Math.max(dataXStart, xCenter - newXRange / 2);
|
||||||
const newXEnd = Math.min(dataXEnd, xCenter + newXRange / 2);
|
const newXEnd = Math.min(dataXEnd, xCenter + newXRange / 2);
|
||||||
|
|
||||||
// Y-axis: zoom from 0
|
|
||||||
const newYMax = currentYMax * 1.25;
|
|
||||||
|
|
||||||
setZoomState({
|
setZoomState({
|
||||||
xStart: newXStart,
|
xStart: newXStart,
|
||||||
xEnd: newXEnd,
|
xEnd: newXEnd,
|
||||||
yMin: 0,
|
yMin: 0,
|
||||||
yMax: newYMax,
|
yMax: currentYMax,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -474,6 +524,63 @@ export function App() {
|
|||||||
return fullXRange / currentXRange;
|
return fullXRange / currentXRange;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Timeline slider position (0-100)
|
||||||
|
const sliderPosition = useMemo(() => {
|
||||||
|
if (!fullChartData.length) return 50;
|
||||||
|
|
||||||
|
const dataXStart = fullChartData[0].timestamp;
|
||||||
|
const dataXEnd = fullChartData[fullChartData.length - 1].timestamp;
|
||||||
|
const fullRange = dataXEnd - dataXStart;
|
||||||
|
|
||||||
|
if (!zoomState || fullRange === 0) return 50;
|
||||||
|
|
||||||
|
const viewCenter = (zoomState.xStart + zoomState.xEnd) / 2;
|
||||||
|
const position = ((viewCenter - dataXStart) / fullRange) * 100;
|
||||||
|
|
||||||
|
return Math.max(0, Math.min(100, position));
|
||||||
|
}, [fullChartData, zoomState]);
|
||||||
|
|
||||||
|
const handleSliderChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (!fullChartData.length) return;
|
||||||
|
|
||||||
|
const position = parseFloat(e.target.value);
|
||||||
|
const dataXStart = fullChartData[0].timestamp;
|
||||||
|
const dataXEnd = fullChartData[fullChartData.length - 1].timestamp;
|
||||||
|
const fullRange = dataXEnd - dataXStart;
|
||||||
|
|
||||||
|
// Calculate new center based on slider position
|
||||||
|
const newCenter = dataXStart + (position / 100) * fullRange;
|
||||||
|
|
||||||
|
// Keep current view width or use full range if no zoom
|
||||||
|
const currentXStart = zoomState?.xStart ?? dataXStart;
|
||||||
|
const currentXEnd = zoomState?.xEnd ?? dataXEnd;
|
||||||
|
const currentYMax = zoomState?.yMax ?? yAxisDomain[1];
|
||||||
|
const viewWidth = currentXEnd - currentXStart;
|
||||||
|
|
||||||
|
// Calculate new bounds centered on slider position
|
||||||
|
let newXStart = newCenter - viewWidth / 2;
|
||||||
|
let newXEnd = newCenter + viewWidth / 2;
|
||||||
|
|
||||||
|
// Constrain to data bounds
|
||||||
|
if (newXStart < dataXStart) {
|
||||||
|
newXEnd += dataXStart - newXStart;
|
||||||
|
newXStart = dataXStart;
|
||||||
|
}
|
||||||
|
if (newXEnd > dataXEnd) {
|
||||||
|
newXStart -= newXEnd - dataXEnd;
|
||||||
|
newXEnd = dataXEnd;
|
||||||
|
}
|
||||||
|
newXStart = Math.max(dataXStart, newXStart);
|
||||||
|
newXEnd = Math.min(dataXEnd, newXEnd);
|
||||||
|
|
||||||
|
setZoomState({
|
||||||
|
xStart: newXStart,
|
||||||
|
xEnd: newXEnd,
|
||||||
|
yMin: 0,
|
||||||
|
yMax: currentYMax,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<div style={{
|
||||||
fontFamily: 'system-ui',
|
fontFamily: 'system-ui',
|
||||||
@@ -716,6 +823,38 @@ export function App() {
|
|||||||
>
|
>
|
||||||
Reset (×{getZoomLevel().toFixed(1)})
|
Reset (×{getZoomLevel().toFixed(1)})
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleTimelineExpand}
|
||||||
|
style={{
|
||||||
|
padding: '6px 12px',
|
||||||
|
backgroundColor: '#2a2f4a',
|
||||||
|
color: '#fff',
|
||||||
|
border: '1px solid #50e3c2',
|
||||||
|
borderRadius: '4px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '14px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
}}
|
||||||
|
title="Expand Timeline"
|
||||||
|
>
|
||||||
|
<
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleTimelineCompress}
|
||||||
|
style={{
|
||||||
|
padding: '6px 12px',
|
||||||
|
backgroundColor: '#2a2f4a',
|
||||||
|
color: '#fff',
|
||||||
|
border: '1px solid #50e3c2',
|
||||||
|
borderRadius: '4px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '14px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
}}
|
||||||
|
title="Compress Timeline"
|
||||||
|
>
|
||||||
|
>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ color: '#888', fontSize: '11px', fontStyle: 'italic' }}>
|
<div style={{ color: '#888', fontSize: '11px', fontStyle: 'italic' }}>
|
||||||
@@ -789,6 +928,37 @@ export function App() {
|
|||||||
</LineChart>
|
</LineChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Timeline Slider */}
|
||||||
|
<div style={{
|
||||||
|
marginTop: '15px',
|
||||||
|
padding: '10px 15px',
|
||||||
|
backgroundColor: '#0f1329',
|
||||||
|
borderRadius: '4px',
|
||||||
|
border: '1px solid #2a2f4a',
|
||||||
|
}}>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: '15px' }}>
|
||||||
|
<span style={{ color: '#888', fontSize: '12px', minWidth: '60px' }}>
|
||||||
|
{fullChartData.length > 0 ? new Date(fullChartData[0].timestamp).toLocaleDateString() : ''}
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
value={sliderPosition}
|
||||||
|
onChange={handleSliderChange}
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
height: '8px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
accentColor: '#50e3c2',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span style={{ color: '#888', fontSize: '12px', minWidth: '60px', textAlign: 'right' }}>
|
||||||
|
{fullChartData.length > 0 ? new Date(fullChartData[fullChartData.length - 1].timestamp).toLocaleDateString() : ''}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ textAlign: 'center', padding: '40px', color: '#ff6b9d' }}>
|
<div style={{ textAlign: 'center', padding: '40px', color: '#ff6b9d' }}>
|
||||||
@@ -854,7 +1024,7 @@ export function App() {
|
|||||||
<th style={{ textAlign: 'right', padding: '12px', color: '#888', fontWeight: 'normal' }}>Price/1M AUEC</th>
|
<th style={{ textAlign: 'right', padding: '12px', color: '#888', fontWeight: 'normal' }}>Price/1M AUEC</th>
|
||||||
{customAuecAmount && (
|
{customAuecAmount && (
|
||||||
<th style={{ textAlign: 'right', padding: '12px', color: '#888', fontWeight: 'normal' }}>
|
<th style={{ textAlign: 'right', padding: '12px', color: '#888', fontWeight: 'normal' }}>
|
||||||
Price for {(customAuecAmount / 1000000).toLocaleString()}M AUEC
|
Price for {customAuecAmount.toLocaleString()} AUEC
|
||||||
</th>
|
</th>
|
||||||
)}
|
)}
|
||||||
</tr>
|
</tr>
|
||||||
@@ -928,10 +1098,7 @@ export function App() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Alert Audio */}
|
{/* Alert Audio */}
|
||||||
<audio
|
<audio ref={alertAudioRef} src="./notifcation.mp3" />
|
||||||
ref={alertAudioRef}
|
|
||||||
src="data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBSuBzvLZiTYIGGS57OihUBELTKXh8bllHAU2jdXvz3oqBSh+zPDajzsKElyx6OyrWBUIQ5zd8sFuJAUuhM/z24k2CBhku+zooVARC0yl4fG5ZRwFNo3V7896KgUofszw2o87ChJcsevsq1gVCEOc3fLBbiQFLoTP89uJNggYZLvs6KFQEQtMpeHxuWUcBTaN1e/PeioFKH7M8NqPOwsSXLHr7KtYFQhDnN3ywW4kBS6Ez/PbiTYIGGS77OihUBELTKXh8bllHAU2jdXvz3oqBSh+zPDajzsKElyx6+yrWBUIQ5zd8sFuJAUuhM/z24k2CBhku+zooVARC0yl4fG5ZRwFNo3V7896KgUofszw2o87ChJcsevsq1gVCEOc3fLBbiQFLoTP89uJNggYZLvs6KFQEQtMpeHxuWUcBTaN1e/PeioFKH7M8NqPOwsSXLHr7KtYFQhDnN3ywW4kBS6Ez/PbiTYIGGS77OihUBELTKXh8bllHAU2jdXvz3oqBSh+zPDajzsKElyx6+yrWBUIQ5zd8sFuJAUuhM/z24k2CBhku+zooVARC0yl4fG5ZRwFNo3V7896KgUofszw2o87ChJcsevsq1gVCEOc3fLBbiQFLoTP89uJNggYZLvs6KFQEQtMpeHxuWUcBTaN1e/PeioFKH7M8NqPOwsSXLHr7KtYFQhDnN3ywW4kBS6Ez/PbiTYIGGS77OihUBELTKXh8bllHAU2jdXvz3oqBSh+zPDajzsKElyx6+yrWBUIQ5zd8sFuJAUuhM/z24k2CBhku+zooVARC0yl4fG5ZRwFNo3V7896KgUofszw2o87ChJcsevsq1gVCEOc3fLBbiQFLoTP89uJNggYZLvs6KFQEQtMpeHxuWUcBTaN1e/PeioFKH7M8NqPOwsSXLHr7KtYFQhDnN3ywW4kBS6Ez/PbiTYIGGS77OihUBELTKXh8bllHAU2jdXvz3oqBSh+zPDajzsKElyx6+yrWBUIQ5zd8sFuJAUuhM/z24k2CBhku+zooVARC0yl4fG5ZRwFNo3V7896KgUofszw2o87ChJcsevsq1gVCEOc3fLBbiQFLoTP89uJNggYZLvs6KFQEQtMpeHxuWUcBTaN1e/PeioFKH7M8NqPOwsSXLHr7KtYFQhDnN3ywW4kBS6Ez/PbiTYIGGS77OihUBELTKXh8bllHAU2jdXvz3oqBSh+zPDajzsKElyx6+yrWBUIQ5zd8sFuJAUuhM/z24k2CBhku+zooVARC0yl4fG5ZRwFNo3V7896KgUofszw2o87ChJcsevsq1gVCEOc3fLBbiQFLoTP89uJNggYZLvs6KFQEQtMpeHxuWUcBTaN1e/PeioFKH7M8NqPOwsSXLHr7KtYFQhDnN3ywW4kBS6Ez/PbiTYIGGS77OihUBELTKXh8bllHAU2jdXvz3oqBSh+zPDajzsKElyx6+yrWBUIQ5zd8sFuJAUuhM/z24k2CBhku+zooVARC0yl4fG5ZRwFNo3V7896KgUofszw2o87ChJcsevsq1gVCEOc3fLBbiQFLoTP89uJNggYZLvs6KFQEQtMpeHxuWQ="
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export default defineConfig({
|
|||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
root: '.',
|
root: '.',
|
||||||
base: './', // Use relative paths for Electron
|
base: './', // Use relative paths for Electron
|
||||||
|
publicDir: 'public',
|
||||||
build: {
|
build: {
|
||||||
outDir: 'dist/renderer',
|
outDir: 'dist/renderer',
|
||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user