mani-calc 1.2.2 → 2.1.1
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/ARCHITECTURE.md +0 -249
- package/CHANGELOG.md +0 -69
- package/COMPLETION_SUMMARY.md +0 -304
- package/CONTRIBUTING.md +0 -110
- package/EXAMPLES.md +0 -220
- package/OVERLAY_MODE.md +0 -364
- package/PUBLISHING_GUIDE.md +0 -291
- package/QUICK_PUBLISH.md +0 -108
- package/RELEASE_NOTES_v1.1.0.md +0 -262
- package/test/test.js +0 -133
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings Manager Module
|
|
3
|
+
* Handles app settings, themes, hotkeys, and auto-start
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
10
|
+
class SettingsManager {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.settingsDir = path.join(os.homedir(), '.mani-calc');
|
|
13
|
+
this.settingsFile = path.join(this.settingsDir, 'settings.json');
|
|
14
|
+
|
|
15
|
+
// Default settings
|
|
16
|
+
this.defaults = {
|
|
17
|
+
hotkey: 'Alt+Space',
|
|
18
|
+
theme: 'dark',
|
|
19
|
+
autoStart: false,
|
|
20
|
+
soundEnabled: true,
|
|
21
|
+
historyLimit: 100,
|
|
22
|
+
clipboardEnabled: true,
|
|
23
|
+
showHints: true,
|
|
24
|
+
compactMode: false,
|
|
25
|
+
customAccentColor: '#00D9FF',
|
|
26
|
+
animationsEnabled: true,
|
|
27
|
+
position: 'center', // center, top, bottom
|
|
28
|
+
opacity: 0.95
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
this.settings = { ...this.defaults };
|
|
32
|
+
this.load();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Ensure settings directory exists
|
|
37
|
+
*/
|
|
38
|
+
ensureDir() {
|
|
39
|
+
if (!fs.existsSync(this.settingsDir)) {
|
|
40
|
+
fs.mkdirSync(this.settingsDir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Load settings from file
|
|
46
|
+
*/
|
|
47
|
+
load() {
|
|
48
|
+
try {
|
|
49
|
+
if (fs.existsSync(this.settingsFile)) {
|
|
50
|
+
const data = fs.readFileSync(this.settingsFile, 'utf8');
|
|
51
|
+
const saved = JSON.parse(data);
|
|
52
|
+
this.settings = { ...this.defaults, ...saved };
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error('Failed to load settings:', error.message);
|
|
56
|
+
this.settings = { ...this.defaults };
|
|
57
|
+
}
|
|
58
|
+
return this.settings;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Save settings to file
|
|
63
|
+
*/
|
|
64
|
+
save() {
|
|
65
|
+
try {
|
|
66
|
+
this.ensureDir();
|
|
67
|
+
fs.writeFileSync(this.settingsFile, JSON.stringify(this.settings, null, 2));
|
|
68
|
+
return true;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('Failed to save settings:', error.message);
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get a setting
|
|
77
|
+
*/
|
|
78
|
+
get(key) {
|
|
79
|
+
return this.settings[key] ?? this.defaults[key];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Set a setting
|
|
84
|
+
*/
|
|
85
|
+
set(key, value) {
|
|
86
|
+
this.settings[key] = value;
|
|
87
|
+
this.save();
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get all settings
|
|
93
|
+
*/
|
|
94
|
+
getAll() {
|
|
95
|
+
return { ...this.settings };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Reset to defaults
|
|
100
|
+
*/
|
|
101
|
+
reset() {
|
|
102
|
+
this.settings = { ...this.defaults };
|
|
103
|
+
this.save();
|
|
104
|
+
return this.settings;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Configure auto-start on Windows boot
|
|
109
|
+
*/
|
|
110
|
+
async setAutoStart(enabled) {
|
|
111
|
+
const { exec } = require('child_process');
|
|
112
|
+
const { promisify } = require('util');
|
|
113
|
+
const execAsync = promisify(exec);
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const appPath = process.execPath;
|
|
117
|
+
const startupFolder = path.join(os.homedir(), 'AppData', 'Roaming', 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
|
|
118
|
+
const shortcutPath = path.join(startupFolder, 'Mani-Calc.lnk');
|
|
119
|
+
|
|
120
|
+
if (enabled) {
|
|
121
|
+
// Create shortcut using PowerShell
|
|
122
|
+
const psCommand = `
|
|
123
|
+
$WshShell = New-Object -comObject WScript.Shell
|
|
124
|
+
$Shortcut = $WshShell.CreateShortcut("${shortcutPath.replace(/\\/g, '\\\\')}")
|
|
125
|
+
$Shortcut.TargetPath = "npx"
|
|
126
|
+
$Shortcut.Arguments = "electron ${path.join(__dirname, '..', 'ui', 'main-electron.js').replace(/\\/g, '\\\\')}"
|
|
127
|
+
$Shortcut.WorkingDirectory = "${path.join(__dirname, '..', '..').replace(/\\/g, '\\\\')}"
|
|
128
|
+
$Shortcut.Description = "Mani-Calc Overlay"
|
|
129
|
+
$Shortcut.Save()
|
|
130
|
+
`;
|
|
131
|
+
await execAsync(`powershell -Command "${psCommand.replace(/"/g, '\\"').replace(/\n/g, ' ')}"`);
|
|
132
|
+
this.set('autoStart', true);
|
|
133
|
+
return { success: true, message: 'Auto-start enabled! Mani-Calc will start with Windows.' };
|
|
134
|
+
} else {
|
|
135
|
+
// Remove shortcut
|
|
136
|
+
if (fs.existsSync(shortcutPath)) {
|
|
137
|
+
fs.unlinkSync(shortcutPath);
|
|
138
|
+
}
|
|
139
|
+
this.set('autoStart', false);
|
|
140
|
+
return { success: true, message: 'Auto-start disabled.' };
|
|
141
|
+
}
|
|
142
|
+
} catch (error) {
|
|
143
|
+
return { success: false, message: `Failed: ${error.message}` };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get available themes
|
|
149
|
+
*/
|
|
150
|
+
getThemes() {
|
|
151
|
+
return [
|
|
152
|
+
{ id: 'dark', name: 'Dark Mode', colors: { bg: '#1e1e1e', accent: '#00D9FF' } },
|
|
153
|
+
{ id: 'light', name: 'Light Mode', colors: { bg: '#ffffff', accent: '#0066cc' } },
|
|
154
|
+
{ id: 'midnight', name: 'Midnight Blue', colors: { bg: '#0a1628', accent: '#4a9eff' } },
|
|
155
|
+
{ id: 'forest', name: 'Forest Green', colors: { bg: '#1a2f1a', accent: '#4caf50' } },
|
|
156
|
+
{ id: 'sunset', name: 'Sunset Orange', colors: { bg: '#2d1a1a', accent: '#ff6b35' } },
|
|
157
|
+
{ id: 'purple', name: 'Royal Purple', colors: { bg: '#1a1a2e', accent: '#9c27b0' } },
|
|
158
|
+
{ id: 'neon', name: 'Neon Glow', colors: { bg: '#0d0d0d', accent: '#39ff14' } },
|
|
159
|
+
{ id: 'ocean', name: 'Deep Ocean', colors: { bg: '#0a1929', accent: '#00bcd4' } }
|
|
160
|
+
];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Set theme
|
|
165
|
+
*/
|
|
166
|
+
setTheme(themeId) {
|
|
167
|
+
const themes = this.getThemes();
|
|
168
|
+
const theme = themes.find(t => t.id === themeId);
|
|
169
|
+
|
|
170
|
+
if (theme) {
|
|
171
|
+
this.set('theme', themeId);
|
|
172
|
+
return theme;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
module.exports = SettingsManager;
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities Module
|
|
3
|
+
* Password Generator, Random Generator, Text Utils, Emoji Search, Encoding
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const crypto = require('crypto');
|
|
7
|
+
|
|
8
|
+
class UtilitiesModule {
|
|
9
|
+
constructor() {
|
|
10
|
+
// Emoji database
|
|
11
|
+
this.emojis = {
|
|
12
|
+
happy: ['😀', '😃', '😄', '😁', '😆', '😊', '🙂', '😇', '🥰', '😍'],
|
|
13
|
+
sad: ['😢', '😭', '😿', '😞', '😔', '🥺', '😥', '😰', '💔'],
|
|
14
|
+
love: ['❤️', '💕', '💖', '💗', '💘', '💝', '💞', '💓', '💟', '🥰', '😍', '😘'],
|
|
15
|
+
heart: ['❤️', '🧡', '💛', '💚', '💙', '💜', '🖤', '🤍', '🤎', '💔', '💕', '💖'],
|
|
16
|
+
angry: ['😠', '😡', '🤬', '👿', '💢', '😤', '🔥'],
|
|
17
|
+
laugh: ['😂', '🤣', '😆', '😹', '😁', '🤭', '😸'],
|
|
18
|
+
cool: ['😎', '🆒', '🤙', '👊', '✌️', '🕶️', '🧊'],
|
|
19
|
+
fire: ['🔥', '💥', '⚡', '✨', '💫', '🌟', '⭐'],
|
|
20
|
+
music: ['🎵', '🎶', '🎼', '🎹', '🎸', '🎺', '🥁', '🎷', '🎻'],
|
|
21
|
+
food: ['🍕', '🍔', '🍟', '🌭', '🍿', '🥗', '🍣', '🍜', '🍩', '🍪'],
|
|
22
|
+
drink: ['☕', '🍵', '🧃', '🥤', '🍺', '🍷', '🍸', '🧋', '🥛'],
|
|
23
|
+
animal: ['🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼', '🐨', '🦁'],
|
|
24
|
+
nature: ['🌸', '🌺', '🌻', '🌹', '🌷', '🌱', '🌲', '🌳', '🍀', '🌈'],
|
|
25
|
+
weather: ['☀️', '🌤️', '⛅', '🌧️', '⛈️', '🌩️', '❄️', '🌨️', '🌪️', '🌈'],
|
|
26
|
+
sport: ['⚽', '🏀', '🏈', '⚾', '🎾', '🏐', '🏉', '🎱', '🏓', '🏸'],
|
|
27
|
+
travel: ['✈️', '🚗', '🚕', '🚌', '🚂', '🚀', '🛸', '🚁', '⛵', '🗺️'],
|
|
28
|
+
work: ['💼', '📊', '📈', '💻', '🖥️', '⌨️', '🖱️', '📱', '📧', '📝'],
|
|
29
|
+
money: ['💰', '💵', '💴', '💶', '💷', '💎', '🏦', '💳', '🪙', '📈'],
|
|
30
|
+
celebrate: ['🎉', '🎊', '🥳', '🎈', '🎁', '🎂', '🍾', '🥂', '🎆', '🎇'],
|
|
31
|
+
thumbs: ['👍', '👎', '👊', '✊', '🤛', '🤜', '👏', '🙌', '🤝', '✋'],
|
|
32
|
+
hand: ['👋', '🤚', '🖐️', '✋', '🖖', '👌', '🤌', '🤏', '✌️', '🤞'],
|
|
33
|
+
face: ['🙂', '😐', '🙄', '😏', '😬', '🤔', '🤨', '😑', '😶', '🫥'],
|
|
34
|
+
star: ['⭐', '🌟', '✨', '💫', '🌠', '⚡', '🔥', '💥', '✴️', '❇️'],
|
|
35
|
+
check: ['✅', '✓', '☑️', '✔️', '👍', '👌', '🆗', '💯'],
|
|
36
|
+
cross: ['❌', '✖️', '❎', '🚫', '⛔', '🔴', '👎'],
|
|
37
|
+
time: ['⏰', '⏱️', '⏲️', '🕐', '🕑', '🕒', '🕓', '🕔', '📅', '📆'],
|
|
38
|
+
arrow: ['➡️', '⬅️', '⬆️', '⬇️', '↗️', '↘️', '↙️', '↖️', '🔄', '🔃']
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Notes storage
|
|
42
|
+
this.notes = [];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Generate secure password
|
|
47
|
+
*/
|
|
48
|
+
generatePassword(length = 12) {
|
|
49
|
+
const lowercase = 'abcdefghijklmnopqrstuvwxyz';
|
|
50
|
+
const uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
51
|
+
const numbers = '0123456789';
|
|
52
|
+
const symbols = '!@#$%^&*()_+-=[]{}|;:,.<>?';
|
|
53
|
+
const allChars = lowercase + uppercase + numbers + symbols;
|
|
54
|
+
|
|
55
|
+
let password = '';
|
|
56
|
+
|
|
57
|
+
// Ensure at least one of each type
|
|
58
|
+
password += lowercase[Math.floor(Math.random() * lowercase.length)];
|
|
59
|
+
password += uppercase[Math.floor(Math.random() * uppercase.length)];
|
|
60
|
+
password += numbers[Math.floor(Math.random() * numbers.length)];
|
|
61
|
+
password += symbols[Math.floor(Math.random() * symbols.length)];
|
|
62
|
+
|
|
63
|
+
// Fill the rest
|
|
64
|
+
for (let i = password.length; i < length; i++) {
|
|
65
|
+
password += allChars[Math.floor(Math.random() * allChars.length)];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Shuffle the password
|
|
69
|
+
return password.split('').sort(() => Math.random() - 0.5).join('');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generate random number
|
|
74
|
+
*/
|
|
75
|
+
randomNumber(min = 1, max = 100) {
|
|
76
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Roll dice
|
|
81
|
+
*/
|
|
82
|
+
rollDice(sides = 6) {
|
|
83
|
+
return Math.floor(Math.random() * sides) + 1;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Flip coin
|
|
88
|
+
*/
|
|
89
|
+
flipCoin() {
|
|
90
|
+
return Math.random() < 0.5 ? 'Heads' : 'Tails';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Generate UUID
|
|
95
|
+
*/
|
|
96
|
+
generateUUID() {
|
|
97
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
98
|
+
const r = Math.random() * 16 | 0;
|
|
99
|
+
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
100
|
+
return v.toString(16);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Convert color formats
|
|
106
|
+
*/
|
|
107
|
+
convertColor(input) {
|
|
108
|
+
const hexMatch = input.match(/^#?([0-9a-f]{6})$/i);
|
|
109
|
+
if (hexMatch) {
|
|
110
|
+
const hex = hexMatch[1];
|
|
111
|
+
const r = parseInt(hex.substr(0, 2), 16);
|
|
112
|
+
const g = parseInt(hex.substr(2, 2), 16);
|
|
113
|
+
const b = parseInt(hex.substr(4, 2), 16);
|
|
114
|
+
return {
|
|
115
|
+
hex: `#${hex.toUpperCase()}`,
|
|
116
|
+
rgb: `rgb(${r}, ${g}, ${b})`,
|
|
117
|
+
hsl: this.rgbToHsl(r, g, b)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const rgbMatch = input.match(/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i);
|
|
122
|
+
if (rgbMatch) {
|
|
123
|
+
const r = parseInt(rgbMatch[1]);
|
|
124
|
+
const g = parseInt(rgbMatch[2]);
|
|
125
|
+
const b = parseInt(rgbMatch[3]);
|
|
126
|
+
const hex = '#' + [r, g, b].map(x => x.toString(16).padStart(2, '0')).join('').toUpperCase();
|
|
127
|
+
return {
|
|
128
|
+
hex: hex,
|
|
129
|
+
rgb: `rgb(${r}, ${g}, ${b})`,
|
|
130
|
+
hsl: this.rgbToHsl(r, g, b)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
rgbToHsl(r, g, b) {
|
|
138
|
+
r /= 255; g /= 255; b /= 255;
|
|
139
|
+
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
140
|
+
let h, s, l = (max + min) / 2;
|
|
141
|
+
|
|
142
|
+
if (max === min) {
|
|
143
|
+
h = s = 0;
|
|
144
|
+
} else {
|
|
145
|
+
const d = max - min;
|
|
146
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
147
|
+
switch (max) {
|
|
148
|
+
case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
|
|
149
|
+
case g: h = ((b - r) / d + 2) / 6; break;
|
|
150
|
+
case b: h = ((r - g) / d + 4) / 6; break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return `hsl(${Math.round(h * 360)}, ${Math.round(s * 100)}%, ${Math.round(l * 100)}%)`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Text utilities
|
|
159
|
+
*/
|
|
160
|
+
textUtils(operation, text) {
|
|
161
|
+
switch (operation) {
|
|
162
|
+
case 'upper':
|
|
163
|
+
return text.toUpperCase();
|
|
164
|
+
case 'lower':
|
|
165
|
+
return text.toLowerCase();
|
|
166
|
+
case 'reverse':
|
|
167
|
+
return text.split('').reverse().join('');
|
|
168
|
+
case 'count':
|
|
169
|
+
const chars = text.length;
|
|
170
|
+
const words = text.trim() ? text.trim().split(/\s+/).length : 0;
|
|
171
|
+
const lines = text.split('\n').length;
|
|
172
|
+
return { chars, words, lines };
|
|
173
|
+
case 'capitalize':
|
|
174
|
+
return text.split(' ').map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' ');
|
|
175
|
+
case 'camelcase':
|
|
176
|
+
return text.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase());
|
|
177
|
+
case 'snakecase':
|
|
178
|
+
return text.toLowerCase().replace(/\s+/g, '_');
|
|
179
|
+
case 'kebabcase':
|
|
180
|
+
return text.toLowerCase().replace(/\s+/g, '-');
|
|
181
|
+
default:
|
|
182
|
+
return text;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Base64 encoding/decoding
|
|
188
|
+
*/
|
|
189
|
+
base64Encode(text) {
|
|
190
|
+
return Buffer.from(text).toString('base64');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
base64Decode(encoded) {
|
|
194
|
+
return Buffer.from(encoded, 'base64').toString('utf8');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Generate hash
|
|
199
|
+
*/
|
|
200
|
+
generateHash(algorithm, text) {
|
|
201
|
+
return crypto.createHash(algorithm).update(text).digest('hex');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Search emojis
|
|
206
|
+
*/
|
|
207
|
+
searchEmoji(keyword) {
|
|
208
|
+
const key = keyword.toLowerCase();
|
|
209
|
+
if (this.emojis[key]) {
|
|
210
|
+
return this.emojis[key];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Search in all categories
|
|
214
|
+
const results = [];
|
|
215
|
+
for (const [category, emojis] of Object.entries(this.emojis)) {
|
|
216
|
+
if (category.includes(key)) {
|
|
217
|
+
results.push(...emojis);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return results.length > 0 ? results.slice(0, 10) : ['No emojis found'];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Get web search URL
|
|
226
|
+
*/
|
|
227
|
+
getSearchUrl(engine, query) {
|
|
228
|
+
const engines = {
|
|
229
|
+
google: `https://www.google.com/search?q=${encodeURIComponent(query)}`,
|
|
230
|
+
youtube: `https://www.youtube.com/results?search_query=${encodeURIComponent(query)}`,
|
|
231
|
+
bing: `https://www.bing.com/search?q=${encodeURIComponent(query)}`,
|
|
232
|
+
duckduckgo: `https://duckduckgo.com/?q=${encodeURIComponent(query)}`,
|
|
233
|
+
wiki: `https://en.wikipedia.org/wiki/Special:Search?search=${encodeURIComponent(query)}`,
|
|
234
|
+
wikipedia: `https://en.wikipedia.org/wiki/Special:Search?search=${encodeURIComponent(query)}`,
|
|
235
|
+
github: `https://github.com/search?q=${encodeURIComponent(query)}`,
|
|
236
|
+
stackoverflow: `https://stackoverflow.com/search?q=${encodeURIComponent(query)}`,
|
|
237
|
+
amazon: `https://www.amazon.com/s?k=${encodeURIComponent(query)}`,
|
|
238
|
+
twitter: `https://twitter.com/search?q=${encodeURIComponent(query)}`,
|
|
239
|
+
reddit: `https://www.reddit.com/search/?q=${encodeURIComponent(query)}`,
|
|
240
|
+
maps: `https://www.google.com/maps/search/${encodeURIComponent(query)}`
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
return engines[engine.toLowerCase()] || engines.google;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Parse query and execute utility function
|
|
248
|
+
*/
|
|
249
|
+
parse(query) {
|
|
250
|
+
const q = query.toLowerCase().trim();
|
|
251
|
+
|
|
252
|
+
// Password generator: "password", "password 16"
|
|
253
|
+
const passMatch = q.match(/^password(?:\s+(\d+))?$/);
|
|
254
|
+
if (passMatch) {
|
|
255
|
+
const length = passMatch[1] ? parseInt(passMatch[1]) : 12;
|
|
256
|
+
const password = this.generatePassword(Math.min(Math.max(length, 8), 64));
|
|
257
|
+
return {
|
|
258
|
+
result: password,
|
|
259
|
+
formatted: `🔐 Password: ${password}`,
|
|
260
|
+
type: 'password'
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Random number: "random", "random 1 100"
|
|
265
|
+
const randomMatch = q.match(/^random(?:\s+(\d+)\s+(\d+))?$/);
|
|
266
|
+
if (randomMatch) {
|
|
267
|
+
const min = randomMatch[1] ? parseInt(randomMatch[1]) : 1;
|
|
268
|
+
const max = randomMatch[2] ? parseInt(randomMatch[2]) : 100;
|
|
269
|
+
const num = this.randomNumber(min, max);
|
|
270
|
+
return {
|
|
271
|
+
result: num,
|
|
272
|
+
formatted: `🎲 Random (${min}-${max}): ${num}`,
|
|
273
|
+
type: 'random'
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Dice roll: "dice", "roll", "d6", "d20"
|
|
278
|
+
const diceMatch = q.match(/^(?:dice|roll|d(\d+))$/);
|
|
279
|
+
if (diceMatch) {
|
|
280
|
+
const sides = diceMatch[1] ? parseInt(diceMatch[1]) : 6;
|
|
281
|
+
const result = this.rollDice(sides);
|
|
282
|
+
return {
|
|
283
|
+
result: result,
|
|
284
|
+
formatted: `🎲 Dice (d${sides}): ${result}`,
|
|
285
|
+
type: 'dice'
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Coin flip: "coin", "flip"
|
|
290
|
+
if (q === 'coin' || q === 'flip' || q === 'flip coin') {
|
|
291
|
+
const result = this.flipCoin();
|
|
292
|
+
const emoji = result === 'Heads' ? '🪙' : '🔴';
|
|
293
|
+
return {
|
|
294
|
+
result: result,
|
|
295
|
+
formatted: `${emoji} Coin Flip: ${result}`,
|
|
296
|
+
type: 'coin'
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// UUID: "uuid"
|
|
301
|
+
if (q === 'uuid' || q === 'guid') {
|
|
302
|
+
const uuid = this.generateUUID();
|
|
303
|
+
return {
|
|
304
|
+
result: uuid,
|
|
305
|
+
formatted: `🔑 UUID: ${uuid}`,
|
|
306
|
+
type: 'uuid'
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Color conversion: "#FF5733 to rgb", "rgb(255,0,0) to hex"
|
|
311
|
+
const colorMatch = query.match(/^(#?[0-9a-f]{6}|rgb\s*\([^)]+\))\s*(?:to\s+(\w+))?$/i);
|
|
312
|
+
if (colorMatch) {
|
|
313
|
+
const color = this.convertColor(colorMatch[1]);
|
|
314
|
+
if (color) {
|
|
315
|
+
const format = colorMatch[2]?.toLowerCase();
|
|
316
|
+
let result = format ? color[format] || color.hex : `${color.hex} | ${color.rgb} | ${color.hsl}`;
|
|
317
|
+
return {
|
|
318
|
+
result: result,
|
|
319
|
+
formatted: `🎨 Color: ${result}`,
|
|
320
|
+
type: 'color'
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Text utilities: "upper hello", "lower HELLO", "reverse hello"
|
|
326
|
+
const textMatch = q.match(/^(upper|lower|reverse|capitalize|camelcase|snakecase|kebabcase)\s+(.+)$/);
|
|
327
|
+
if (textMatch) {
|
|
328
|
+
const result = this.textUtils(textMatch[1], textMatch[2]);
|
|
329
|
+
return {
|
|
330
|
+
result: result,
|
|
331
|
+
formatted: `📝 ${textMatch[1]}: ${result}`,
|
|
332
|
+
type: 'text'
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Count: "count hello world"
|
|
337
|
+
const countMatch = q.match(/^count\s+(.+)$/);
|
|
338
|
+
if (countMatch) {
|
|
339
|
+
const stats = this.textUtils('count', countMatch[1]);
|
|
340
|
+
return {
|
|
341
|
+
result: stats,
|
|
342
|
+
formatted: `📊 Count: ${stats.chars} chars, ${stats.words} words`,
|
|
343
|
+
type: 'count'
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Base64 encode/decode
|
|
348
|
+
const base64Match = query.match(/^base64\s+(encode|decode)\s+(.+)$/i);
|
|
349
|
+
if (base64Match) {
|
|
350
|
+
const operation = base64Match[1].toLowerCase();
|
|
351
|
+
const text = base64Match[2];
|
|
352
|
+
const result = operation === 'encode' ? this.base64Encode(text) : this.base64Decode(text);
|
|
353
|
+
return {
|
|
354
|
+
result: result,
|
|
355
|
+
formatted: `🔤 Base64 ${operation}: ${result}`,
|
|
356
|
+
type: 'base64'
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Hash: "md5 hello", "sha256 hello"
|
|
361
|
+
const hashMatch = q.match(/^(md5|sha1|sha256|sha512)\s+(.+)$/);
|
|
362
|
+
if (hashMatch) {
|
|
363
|
+
const hash = this.generateHash(hashMatch[1], hashMatch[2]);
|
|
364
|
+
return {
|
|
365
|
+
result: hash,
|
|
366
|
+
formatted: `🔒 ${hashMatch[1].toUpperCase()}: ${hash}`,
|
|
367
|
+
type: 'hash'
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Emoji search: "emoji happy", "emoji heart"
|
|
372
|
+
const emojiMatch = q.match(/^emoji\s+(.+)$/);
|
|
373
|
+
if (emojiMatch) {
|
|
374
|
+
const emojis = this.searchEmoji(emojiMatch[1]);
|
|
375
|
+
return {
|
|
376
|
+
result: emojis.join(' '),
|
|
377
|
+
formatted: `😀 Emojis: ${emojis.join(' ')}`,
|
|
378
|
+
type: 'emoji'
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Web search: "google AI", "youtube music", "wiki einstein"
|
|
383
|
+
const searchEngines = ['google', 'youtube', 'bing', 'duckduckgo', 'wiki', 'wikipedia', 'github', 'stackoverflow', 'amazon', 'twitter', 'reddit', 'maps'];
|
|
384
|
+
for (const engine of searchEngines) {
|
|
385
|
+
if (q.startsWith(engine + ' ')) {
|
|
386
|
+
const searchQuery = query.slice(engine.length + 1);
|
|
387
|
+
const url = this.getSearchUrl(engine, searchQuery);
|
|
388
|
+
return {
|
|
389
|
+
result: url,
|
|
390
|
+
formatted: `🔍 Search ${engine}: ${searchQuery}`,
|
|
391
|
+
type: 'search',
|
|
392
|
+
url: url,
|
|
393
|
+
engine: engine
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
module.exports = UtilitiesModule;
|