mani-calc 1.2.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FRIEND_SETUP.md +132 -0
- package/README.md +373 -493
- package/SETUP.bat +126 -0
- package/bin/overlay.js +87 -19
- package/package.json +14 -3
- package/scripts/install-autostart.js +84 -0
- package/scripts/uninstall-autostart.js +49 -0
- package/src/core/currency-converter.js +191 -0
- package/src/core/date-time-calculator.js +376 -0
- package/src/core/programmer-calc.js +265 -0
- package/src/core/settings-manager.js +179 -0
- package/src/core/utilities.js +402 -0
- package/src/index.js +174 -17
- package/src/ui/floating-search.js +203 -10
- package/src/ui/overlay.html +178 -11
package/SETUP.bat
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
title Mani-Calc Setup
|
|
3
|
+
color 0B
|
|
4
|
+
echo.
|
|
5
|
+
echo ╔═══════════════════════════════════════════════════════════╗
|
|
6
|
+
echo ║ ║
|
|
7
|
+
echo ║ 🧮 MANI-CALC SETUP WIZARD 🧮 ║
|
|
8
|
+
echo ║ ║
|
|
9
|
+
echo ║ Spotlight-style Calculator for Windows ║
|
|
10
|
+
echo ║ ║
|
|
11
|
+
echo ╚═══════════════════════════════════════════════════════════╝
|
|
12
|
+
echo.
|
|
13
|
+
|
|
14
|
+
:: Check if Node.js is installed
|
|
15
|
+
where node >nul 2>&1
|
|
16
|
+
if %errorlevel% neq 0 (
|
|
17
|
+
color 0C
|
|
18
|
+
echo ❌ ERROR: Node.js is not installed!
|
|
19
|
+
echo.
|
|
20
|
+
echo Please install Node.js from: https://nodejs.org/
|
|
21
|
+
echo Then run this setup again.
|
|
22
|
+
echo.
|
|
23
|
+
pause
|
|
24
|
+
exit /b 1
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
echo ✓ Node.js detected
|
|
28
|
+
echo.
|
|
29
|
+
|
|
30
|
+
:: Get the directory where this batch file is located
|
|
31
|
+
cd /d "%~dp0"
|
|
32
|
+
|
|
33
|
+
:: Check if node_modules exists
|
|
34
|
+
if not exist "node_modules" (
|
|
35
|
+
echo 📦 Installing dependencies...
|
|
36
|
+
echo This may take a minute...
|
|
37
|
+
echo.
|
|
38
|
+
call npm install
|
|
39
|
+
if %errorlevel% neq 0 (
|
|
40
|
+
color 0C
|
|
41
|
+
echo.
|
|
42
|
+
echo ❌ ERROR: Failed to install dependencies!
|
|
43
|
+
pause
|
|
44
|
+
exit /b 1
|
|
45
|
+
)
|
|
46
|
+
echo.
|
|
47
|
+
echo ✓ Dependencies installed
|
|
48
|
+
echo.
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
echo.
|
|
52
|
+
echo ═══════════════════════════════════════════════════════════
|
|
53
|
+
echo.
|
|
54
|
+
echo What would you like to do?
|
|
55
|
+
echo.
|
|
56
|
+
echo [1] Install Auto-Start (run automatically when Windows boots)
|
|
57
|
+
echo [2] Start Mani-Calc now
|
|
58
|
+
echo [3] Both (Install Auto-Start AND Start now)
|
|
59
|
+
echo [4] Exit
|
|
60
|
+
echo.
|
|
61
|
+
echo ═══════════════════════════════════════════════════════════
|
|
62
|
+
echo.
|
|
63
|
+
|
|
64
|
+
set /p choice= Enter your choice (1-4):
|
|
65
|
+
|
|
66
|
+
if "%choice%"=="1" goto install_autostart
|
|
67
|
+
if "%choice%"=="2" goto start_now
|
|
68
|
+
if "%choice%"=="3" goto both
|
|
69
|
+
if "%choice%"=="4" goto end
|
|
70
|
+
|
|
71
|
+
echo.
|
|
72
|
+
echo Invalid choice. Please try again.
|
|
73
|
+
pause
|
|
74
|
+
goto end
|
|
75
|
+
|
|
76
|
+
:install_autostart
|
|
77
|
+
echo.
|
|
78
|
+
echo 📝 Installing Auto-Start...
|
|
79
|
+
echo.
|
|
80
|
+
call npm run install-autostart
|
|
81
|
+
echo.
|
|
82
|
+
echo ═══════════════════════════════════════════════════════════
|
|
83
|
+
echo.
|
|
84
|
+
echo ✅ Setup complete!
|
|
85
|
+
echo.
|
|
86
|
+
echo Mani-Calc will now start automatically when Windows boots.
|
|
87
|
+
echo Press Alt+Space anytime to open the calculator!
|
|
88
|
+
echo.
|
|
89
|
+
pause
|
|
90
|
+
goto end
|
|
91
|
+
|
|
92
|
+
:start_now
|
|
93
|
+
echo.
|
|
94
|
+
echo 🚀 Starting Mani-Calc...
|
|
95
|
+
echo.
|
|
96
|
+
echo Press Alt+Space to open the calculator!
|
|
97
|
+
echo Press Ctrl+C in this window to stop.
|
|
98
|
+
echo.
|
|
99
|
+
call npm run overlay
|
|
100
|
+
goto end
|
|
101
|
+
|
|
102
|
+
:both
|
|
103
|
+
echo.
|
|
104
|
+
echo 📝 Installing Auto-Start...
|
|
105
|
+
echo.
|
|
106
|
+
call npm run install-autostart
|
|
107
|
+
echo.
|
|
108
|
+
echo 🚀 Starting Mani-Calc...
|
|
109
|
+
echo.
|
|
110
|
+
echo Press Alt+Space to open the calculator!
|
|
111
|
+
echo You can close this window - Mani-Calc runs in the background!
|
|
112
|
+
echo.
|
|
113
|
+
start "" npm run overlay
|
|
114
|
+
echo.
|
|
115
|
+
echo ═══════════════════════════════════════════════════════════
|
|
116
|
+
echo.
|
|
117
|
+
echo ✅ Setup complete!
|
|
118
|
+
echo.
|
|
119
|
+
echo • Mani-Calc is now running!
|
|
120
|
+
echo • It will start automatically when Windows boots.
|
|
121
|
+
echo • Press Alt+Space anytime to open the calculator!
|
|
122
|
+
echo.
|
|
123
|
+
pause
|
|
124
|
+
goto end
|
|
125
|
+
|
|
126
|
+
:end
|
package/bin/overlay.js
CHANGED
|
@@ -1,27 +1,95 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const { spawn } = require('child_process');
|
|
4
3
|
const path = require('path');
|
|
5
|
-
const electron = require('electron');
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
// Check if running directly via electron (electron bin/overlay.js)
|
|
6
|
+
// or via node (node bin/overlay.js)
|
|
7
|
+
if (process.versions.electron) {
|
|
8
|
+
// Running in Electron directly - load main-electron.js
|
|
9
|
+
require('../src/ui/main-electron.js');
|
|
10
|
+
} else {
|
|
11
|
+
// Running from Node - spawn electron
|
|
12
|
+
const { spawn, execSync } = require('child_process');
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
// Get electron path (handles both string and object export)
|
|
15
|
+
let electronPath;
|
|
16
|
+
try {
|
|
17
|
+
const electron = require('electron');
|
|
18
|
+
electronPath = typeof electron === 'string' ? electron : electron.toString();
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
// If still not a valid path, try finding electron
|
|
21
|
+
if (!electronPath || typeof electronPath !== 'string') {
|
|
22
|
+
electronPath = require.resolve('electron/cli.js');
|
|
23
|
+
}
|
|
24
|
+
} catch (e) {
|
|
25
|
+
// Fallback: use npx electron
|
|
26
|
+
electronPath = 'npx';
|
|
27
|
+
}
|
|
17
28
|
|
|
18
|
-
|
|
19
|
-
process.on('SIGINT', () => {
|
|
20
|
-
child.kill('SIGINT');
|
|
21
|
-
process.exit(0);
|
|
22
|
-
});
|
|
29
|
+
const mainScript = path.join(__dirname, '../src/ui/main-electron.js');
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
const args = electronPath === 'npx'
|
|
32
|
+
? ['electron', mainScript]
|
|
33
|
+
: [mainScript];
|
|
34
|
+
|
|
35
|
+
const child = spawn(electronPath, args, {
|
|
36
|
+
stdio: 'inherit',
|
|
37
|
+
windowsHide: false,
|
|
38
|
+
shell: electronPath === 'npx'
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
let isExiting = false;
|
|
42
|
+
|
|
43
|
+
function gracefulShutdown(signal) {
|
|
44
|
+
if (isExiting) return;
|
|
45
|
+
isExiting = true;
|
|
46
|
+
|
|
47
|
+
console.log(`\n👋 Shutting down Mani-Calc Overlay (${signal})...`);
|
|
48
|
+
|
|
49
|
+
// Try graceful kill first
|
|
50
|
+
try {
|
|
51
|
+
child.kill('SIGTERM');
|
|
52
|
+
} catch (e) { }
|
|
53
|
+
|
|
54
|
+
// On Windows, also try taskkill as backup
|
|
55
|
+
if (process.platform === 'win32') {
|
|
56
|
+
setTimeout(() => {
|
|
57
|
+
try {
|
|
58
|
+
// Kill any remaining electron processes for this app
|
|
59
|
+
execSync('taskkill /IM electron.exe /F 2>nul', { stdio: 'ignore' });
|
|
60
|
+
} catch (e) {
|
|
61
|
+
// Ignore errors - process may already be dead
|
|
62
|
+
}
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}, 500);
|
|
65
|
+
} else {
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
child.on('close', (code) => {
|
|
71
|
+
process.exit(code || 0);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
child.on('error', (err) => {
|
|
75
|
+
console.error('Failed to start Mani-Calc:', err.message);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Handle termination signals - Windows uses different events
|
|
80
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
81
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
82
|
+
process.on('SIGHUP', () => gracefulShutdown('SIGHUP'));
|
|
83
|
+
|
|
84
|
+
// Windows-specific: handle Ctrl+C and window close
|
|
85
|
+
if (process.platform === 'win32') {
|
|
86
|
+
const readline = require('readline');
|
|
87
|
+
const rl = readline.createInterface({
|
|
88
|
+
input: process.stdin,
|
|
89
|
+
output: process.stdout
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
rl.on('close', () => gracefulShutdown('readline-close'));
|
|
93
|
+
rl.on('SIGINT', () => gracefulShutdown('win-SIGINT'));
|
|
94
|
+
}
|
|
95
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mani-calc",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Spotlight-style instant calculator for Windows
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "Spotlight-style instant calculator for Windows | Math, Currency, Date/Time, Programmer Mode, Password Generator, Color Converter, Text Utils, Emoji, Web Search, 8 Themes | Offline-first productivity tool",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mani-calc": "./bin/cli.js",
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
"scripts": {
|
|
11
11
|
"start": "node src/index.js",
|
|
12
12
|
"overlay": "electron bin/overlay.js",
|
|
13
|
+
"stop": "taskkill /IM electron.exe /F",
|
|
14
|
+
"install-autostart": "node scripts/install-autostart.js",
|
|
15
|
+
"uninstall-autostart": "node scripts/uninstall-autostart.js",
|
|
13
16
|
"install-service": "node scripts/install-service.js",
|
|
14
17
|
"uninstall-service": "node scripts/uninstall-service.js",
|
|
15
18
|
"test": "node test/test.js"
|
|
@@ -22,7 +25,15 @@
|
|
|
22
25
|
"productivity",
|
|
23
26
|
"spotlight",
|
|
24
27
|
"instant-calculation",
|
|
25
|
-
"unit-conversion"
|
|
28
|
+
"unit-conversion",
|
|
29
|
+
"currency-converter",
|
|
30
|
+
"date-calculator",
|
|
31
|
+
"programmer-calculator",
|
|
32
|
+
"hex-converter",
|
|
33
|
+
"system-commands",
|
|
34
|
+
"themes",
|
|
35
|
+
"overlay",
|
|
36
|
+
"hotkey"
|
|
26
37
|
],
|
|
27
38
|
"author": "Manideep Reddy Eevuri",
|
|
28
39
|
"license": "MIT",
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Mani-Calc Auto-Start Installer
|
|
4
|
+
* Adds Mani-Calc to Windows Startup so it runs automatically
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { execSync } = require('child_process');
|
|
10
|
+
|
|
11
|
+
const APP_NAME = 'Mani-Calc';
|
|
12
|
+
const STARTUP_FOLDER = path.join(
|
|
13
|
+
process.env.APPDATA,
|
|
14
|
+
'Microsoft\\Windows\\Start Menu\\Programs\\Startup'
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
function getProjectRoot() {
|
|
18
|
+
return path.resolve(__dirname, '..');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function createStartupShortcut() {
|
|
22
|
+
console.log('\n🚀 Installing Mani-Calc Auto-Start...\n');
|
|
23
|
+
|
|
24
|
+
const projectRoot = getProjectRoot();
|
|
25
|
+
const overlayScript = path.join(projectRoot, 'bin', 'overlay.js');
|
|
26
|
+
const shortcutPath = path.join(STARTUP_FOLDER, `${APP_NAME}.vbs`);
|
|
27
|
+
|
|
28
|
+
// Check if overlay.js exists
|
|
29
|
+
if (!fs.existsSync(overlayScript)) {
|
|
30
|
+
console.error('❌ Error: overlay.js not found!');
|
|
31
|
+
console.error(` Expected at: ${overlayScript}`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Get node path
|
|
36
|
+
const nodePath = process.execPath;
|
|
37
|
+
const npmPath = path.join(path.dirname(nodePath), 'npm.cmd');
|
|
38
|
+
|
|
39
|
+
// Create a VBS script that runs the overlay silently (no console window)
|
|
40
|
+
const vbsContent = `
|
|
41
|
+
' Mani-Calc Auto-Start Script
|
|
42
|
+
' This script launches Mani-Calc Overlay silently at Windows startup
|
|
43
|
+
|
|
44
|
+
Set WshShell = CreateObject("WScript.Shell")
|
|
45
|
+
WshShell.CurrentDirectory = "${projectRoot.replace(/\\/g, '\\\\')}"
|
|
46
|
+
WshShell.Run """${npmPath.replace(/\\/g, '\\\\')}""" & " run overlay", 0, False
|
|
47
|
+
Set WshShell = Nothing
|
|
48
|
+
`.trim();
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
fs.writeFileSync(shortcutPath, vbsContent);
|
|
52
|
+
console.log('✅ Auto-start installed successfully!\n');
|
|
53
|
+
console.log('📁 Startup script created at:');
|
|
54
|
+
console.log(` ${shortcutPath}\n`);
|
|
55
|
+
console.log('📝 What this means:');
|
|
56
|
+
console.log(' • Mani-Calc will start automatically when Windows boots');
|
|
57
|
+
console.log(' • It runs silently in the background (no console window)');
|
|
58
|
+
console.log(' • Press Alt+Space anytime to use the calculator\n');
|
|
59
|
+
console.log('💡 To start it now, run: npm run overlay\n');
|
|
60
|
+
console.log('🗑️ To remove auto-start later, run: npm run uninstall-autostart\n');
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error('❌ Failed to create startup script:', error.message);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function main() {
|
|
68
|
+
// Check if running on Windows
|
|
69
|
+
if (process.platform !== 'win32') {
|
|
70
|
+
console.error('❌ This script only works on Windows!');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Check if Startup folder exists
|
|
75
|
+
if (!fs.existsSync(STARTUP_FOLDER)) {
|
|
76
|
+
console.error('❌ Windows Startup folder not found!');
|
|
77
|
+
console.error(` Expected at: ${STARTUP_FOLDER}`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
createStartupShortcut();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
main();
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Mani-Calc Auto-Start Uninstaller
|
|
4
|
+
* Removes Mani-Calc from Windows Startup
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const APP_NAME = 'Mani-Calc';
|
|
11
|
+
const STARTUP_FOLDER = path.join(
|
|
12
|
+
process.env.APPDATA,
|
|
13
|
+
'Microsoft\\Windows\\Start Menu\\Programs\\Startup'
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
function removeStartupShortcut() {
|
|
17
|
+
console.log('\n🗑️ Removing Mani-Calc Auto-Start...\n');
|
|
18
|
+
|
|
19
|
+
const shortcutPath = path.join(STARTUP_FOLDER, `${APP_NAME}.vbs`);
|
|
20
|
+
|
|
21
|
+
if (!fs.existsSync(shortcutPath)) {
|
|
22
|
+
console.log('ℹ️ Auto-start was not installed (nothing to remove).\n');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
fs.unlinkSync(shortcutPath);
|
|
28
|
+
console.log('✅ Auto-start removed successfully!\n');
|
|
29
|
+
console.log('📝 What this means:');
|
|
30
|
+
console.log(' • Mani-Calc will NO longer start automatically');
|
|
31
|
+
console.log(' • You can still run it manually with: npm run overlay\n');
|
|
32
|
+
console.log('💡 To re-enable auto-start, run: npm run install-autostart\n');
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('❌ Failed to remove startup script:', error.message);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function main() {
|
|
40
|
+
// Check if running on Windows
|
|
41
|
+
if (process.platform !== 'win32') {
|
|
42
|
+
console.error('❌ This script only works on Windows!');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
removeStartupShortcut();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
main();
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Currency Converter Module
|
|
3
|
+
* Supports real-time and offline currency conversion
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class CurrencyConverter {
|
|
7
|
+
constructor() {
|
|
8
|
+
// Fallback exchange rates (USD base) - Updated periodically
|
|
9
|
+
this.fallbackRates = {
|
|
10
|
+
USD: 1,
|
|
11
|
+
EUR: 0.92,
|
|
12
|
+
GBP: 0.79,
|
|
13
|
+
INR: 83.12,
|
|
14
|
+
JPY: 148.50,
|
|
15
|
+
CAD: 1.35,
|
|
16
|
+
AUD: 1.53,
|
|
17
|
+
CHF: 0.88,
|
|
18
|
+
CNY: 7.19,
|
|
19
|
+
HKD: 7.82,
|
|
20
|
+
SGD: 1.34,
|
|
21
|
+
KRW: 1320.50,
|
|
22
|
+
MXN: 17.15,
|
|
23
|
+
BRL: 4.97,
|
|
24
|
+
RUB: 89.50,
|
|
25
|
+
ZAR: 18.75,
|
|
26
|
+
AED: 3.67,
|
|
27
|
+
SAR: 3.75,
|
|
28
|
+
THB: 35.20,
|
|
29
|
+
MYR: 4.72,
|
|
30
|
+
PHP: 55.80,
|
|
31
|
+
IDR: 15650,
|
|
32
|
+
VND: 24350,
|
|
33
|
+
NZD: 1.62,
|
|
34
|
+
SEK: 10.45,
|
|
35
|
+
NOK: 10.65,
|
|
36
|
+
DKK: 6.88,
|
|
37
|
+
PLN: 4.02,
|
|
38
|
+
TRY: 30.25,
|
|
39
|
+
TWD: 31.50,
|
|
40
|
+
PKR: 278.50,
|
|
41
|
+
BDT: 110.25,
|
|
42
|
+
NGN: 890.50,
|
|
43
|
+
EGP: 30.90,
|
|
44
|
+
COP: 3950,
|
|
45
|
+
ARS: 815.50,
|
|
46
|
+
CLP: 885.50,
|
|
47
|
+
PEN: 3.72,
|
|
48
|
+
ILS: 3.65,
|
|
49
|
+
CZK: 22.85,
|
|
50
|
+
HUF: 355.50,
|
|
51
|
+
RON: 4.58
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
this.rates = { ...this.fallbackRates };
|
|
55
|
+
this.lastUpdate = null;
|
|
56
|
+
this.cacheTimeout = 3600000; // 1 hour cache
|
|
57
|
+
|
|
58
|
+
// Currency symbols for display
|
|
59
|
+
this.symbols = {
|
|
60
|
+
USD: '$', EUR: '€', GBP: '£', INR: '₹', JPY: '¥',
|
|
61
|
+
CNY: '¥', KRW: '₩', THB: '฿', RUB: '₽', TRY: '₺',
|
|
62
|
+
BRL: 'R$', PLN: 'zł', ILS: '₪', PHP: '₱', VND: '₫'
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Currency names for NLP
|
|
66
|
+
this.currencyNames = {
|
|
67
|
+
'dollar': 'USD', 'dollars': 'USD', 'usd': 'USD',
|
|
68
|
+
'euro': 'EUR', 'euros': 'EUR', 'eur': 'EUR',
|
|
69
|
+
'pound': 'GBP', 'pounds': 'GBP', 'gbp': 'GBP', 'sterling': 'GBP',
|
|
70
|
+
'rupee': 'INR', 'rupees': 'INR', 'inr': 'INR',
|
|
71
|
+
'yen': 'JPY', 'jpy': 'JPY',
|
|
72
|
+
'yuan': 'CNY', 'cny': 'CNY', 'rmb': 'CNY',
|
|
73
|
+
'won': 'KRW', 'krw': 'KRW',
|
|
74
|
+
'peso': 'MXN', 'pesos': 'MXN',
|
|
75
|
+
'real': 'BRL', 'reais': 'BRL', 'brl': 'BRL',
|
|
76
|
+
'ruble': 'RUB', 'rubles': 'RUB', 'rub': 'RUB',
|
|
77
|
+
'dirham': 'AED', 'aed': 'AED',
|
|
78
|
+
'riyal': 'SAR', 'sar': 'SAR',
|
|
79
|
+
'baht': 'THB', 'thb': 'THB',
|
|
80
|
+
'ringgit': 'MYR', 'myr': 'MYR',
|
|
81
|
+
'lira': 'TRY', 'try': 'TRY',
|
|
82
|
+
'franc': 'CHF', 'chf': 'CHF',
|
|
83
|
+
'canadian': 'CAD', 'cad': 'CAD',
|
|
84
|
+
'australian': 'AUD', 'aud': 'AUD'
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Try to fetch live rates (with fallback to cached rates)
|
|
90
|
+
*/
|
|
91
|
+
async fetchRates() {
|
|
92
|
+
// Check if cache is still valid
|
|
93
|
+
if (this.lastUpdate && (Date.now() - this.lastUpdate) < this.cacheTimeout) {
|
|
94
|
+
return this.rates;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
// Try free API (no key required)
|
|
99
|
+
const response = await fetch('https://api.exchangerate-api.com/v4/latest/USD', {
|
|
100
|
+
timeout: 5000
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (response.ok) {
|
|
104
|
+
const data = await response.json();
|
|
105
|
+
this.rates = data.rates;
|
|
106
|
+
this.lastUpdate = Date.now();
|
|
107
|
+
console.log('Currency rates updated from API');
|
|
108
|
+
}
|
|
109
|
+
} catch (error) {
|
|
110
|
+
// Use fallback rates silently
|
|
111
|
+
console.log('Using offline currency rates');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return this.rates;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Parse currency from text
|
|
119
|
+
*/
|
|
120
|
+
parseCurrency(text) {
|
|
121
|
+
const upper = text.toUpperCase().trim();
|
|
122
|
+
|
|
123
|
+
// Check direct currency code
|
|
124
|
+
if (this.rates[upper]) {
|
|
125
|
+
return upper;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Check currency names
|
|
129
|
+
const lower = text.toLowerCase().trim();
|
|
130
|
+
if (this.currencyNames[lower]) {
|
|
131
|
+
return this.currencyNames[lower];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Convert between currencies
|
|
139
|
+
*/
|
|
140
|
+
async convert(amount, fromCurrency, toCurrency) {
|
|
141
|
+
await this.fetchRates();
|
|
142
|
+
|
|
143
|
+
const from = this.parseCurrency(fromCurrency);
|
|
144
|
+
const to = this.parseCurrency(toCurrency);
|
|
145
|
+
|
|
146
|
+
if (!from || !to) {
|
|
147
|
+
throw new Error(`Unknown currency: ${!from ? fromCurrency : toCurrency}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!this.rates[from] || !this.rates[to]) {
|
|
151
|
+
throw new Error(`Exchange rate not available for ${from} or ${to}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Convert via USD base
|
|
155
|
+
const inUSD = amount / this.rates[from];
|
|
156
|
+
const result = inUSD * this.rates[to];
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
amount: amount,
|
|
160
|
+
from: from,
|
|
161
|
+
to: to,
|
|
162
|
+
result: Math.round(result * 100) / 100,
|
|
163
|
+
rate: Math.round((this.rates[to] / this.rates[from]) * 10000) / 10000,
|
|
164
|
+
symbol: this.symbols[to] || ''
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Format currency result for display
|
|
170
|
+
*/
|
|
171
|
+
formatResult(conversion) {
|
|
172
|
+
const fromSymbol = this.symbols[conversion.from] || '';
|
|
173
|
+
const toSymbol = this.symbols[conversion.to] || '';
|
|
174
|
+
|
|
175
|
+
const formattedResult = conversion.result.toLocaleString('en-US', {
|
|
176
|
+
minimumFractionDigits: 2,
|
|
177
|
+
maximumFractionDigits: 2
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return `${fromSymbol}${conversion.amount} ${conversion.from} = ${toSymbol}${formattedResult} ${conversion.to}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Get list of supported currencies
|
|
185
|
+
*/
|
|
186
|
+
getSupportedCurrencies() {
|
|
187
|
+
return Object.keys(this.rates);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
module.exports = CurrencyConverter;
|