mani-calc 1.2.1 → 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/src/index.js CHANGED
@@ -3,6 +3,11 @@ const NLPParser = require('./core/nlp-parser');
3
3
  const UnitConverter = require('./core/unit-converter');
4
4
  const HistoryManager = require('./core/history-manager');
5
5
  const ClipboardManager = require('./core/clipboard-manager');
6
+ const CurrencyConverter = require('./core/currency-converter');
7
+ const DateTimeCalculator = require('./core/date-time-calculator');
8
+ const ProgrammerCalculator = require('./core/programmer-calc');
9
+ const SettingsManager = require('./core/settings-manager');
10
+ const UtilitiesModule = require('./core/utilities');
6
11
  const WindowsSearchIntegration = require('./integration/windows-search');
7
12
  const chalk = require('chalk');
8
13
 
@@ -13,22 +18,30 @@ class ManiCalc {
13
18
  this.unitConverter = new UnitConverter();
14
19
  this.historyManager = new HistoryManager();
15
20
  this.clipboardManager = new ClipboardManager();
21
+ this.currencyConverter = new CurrencyConverter();
22
+ this.dateTimeCalculator = new DateTimeCalculator();
23
+ this.programmerCalculator = new ProgrammerCalculator();
24
+ this.settingsManager = new SettingsManager();
25
+ this.utilities = new UtilitiesModule();
16
26
  this.windowsSearch = new WindowsSearchIntegration(this);
17
27
  }
18
28
 
19
29
  async initialize() {
20
30
  console.log(chalk.cyan('šŸš€ Mani-Calc initializing...'));
21
-
31
+
22
32
  try {
23
33
  await this.windowsSearch.register();
24
34
  console.log(chalk.green('āœ“ Windows Search integration registered'));
25
35
  console.log(chalk.yellow('\nšŸ“ You can now use mani-calc directly from Windows Search!'));
26
36
  console.log(chalk.gray(' Examples:'));
27
37
  console.log(chalk.gray(' - calc: 2 + 3 * 5'));
28
- console.log(chalk.gray(' - calc: what is 25 percent of 200'));
38
+ console.log(chalk.gray(' - calc: 100 USD to INR'));
39
+ console.log(chalk.gray(' - calc: time in tokyo'));
40
+ console.log(chalk.gray(' - calc: today + 30 days'));
41
+ console.log(chalk.gray(' - calc: 255 to hex'));
29
42
  console.log(chalk.gray(' - calc: 10 km to miles'));
30
43
  console.log(chalk.gray(' - calc: history\n'));
31
-
44
+
32
45
  return true;
33
46
  } catch (error) {
34
47
  console.error(chalk.red('āœ— Failed to initialize:'), error.message);
@@ -38,68 +51,175 @@ class ManiCalc {
38
51
 
39
52
  async processQuery(query) {
40
53
  try {
54
+ const lowerQuery = query.toLowerCase().trim();
55
+
41
56
  // Check for special commands
42
- if (query.toLowerCase().trim() === 'history') {
57
+ if (lowerQuery === 'history') {
43
58
  return this.historyManager.getHistory();
44
59
  }
45
60
 
46
- if (query.toLowerCase().trim() === 'clear history') {
61
+ if (lowerQuery === 'clear history') {
47
62
  this.historyManager.clearHistory();
48
- return { result: 'History cleared', type: 'info' };
63
+ return { result: 'History cleared', type: 'info', formatted: 'History cleared' };
64
+ }
65
+
66
+ // Check for settings commands
67
+ if (lowerQuery === 'settings' || lowerQuery === 'config') {
68
+ const settings = this.settingsManager.getAll();
69
+ return {
70
+ result: settings,
71
+ type: 'settings',
72
+ formatted: `Theme: ${settings.theme}, Hotkey: ${settings.hotkey}, Auto-start: ${settings.autoStart}`
73
+ };
74
+ }
75
+
76
+ // Check for theme commands
77
+ if (lowerQuery.startsWith('theme ') || lowerQuery === 'themes') {
78
+ if (lowerQuery === 'themes') {
79
+ const themes = this.settingsManager.getThemes();
80
+ return {
81
+ result: themes.map(t => t.name).join(', '),
82
+ type: 'info',
83
+ formatted: `Available themes: ${themes.map(t => t.id).join(', ')}`
84
+ };
85
+ }
86
+ const themeId = lowerQuery.replace('theme ', '').trim();
87
+ const theme = this.settingsManager.setTheme(themeId);
88
+ if (theme) {
89
+ return {
90
+ result: theme.name,
91
+ type: 'theme',
92
+ themeId: theme.id,
93
+ formatted: `Theme changed to: ${theme.name}`
94
+ };
95
+ }
96
+ }
97
+
98
+ // Check for auto-start command
99
+ if (lowerQuery === 'autostart on' || lowerQuery === 'auto start on') {
100
+ const result = await this.settingsManager.setAutoStart(true);
101
+ return { result: result.message, type: 'info', formatted: result.message };
102
+ }
103
+ if (lowerQuery === 'autostart off' || lowerQuery === 'auto start off') {
104
+ const result = await this.settingsManager.setAutoStart(false);
105
+ return { result: result.message, type: 'info', formatted: result.message };
106
+ }
107
+
108
+ // Try currency conversion: "100 USD to INR", "100 dollars to rupees"
109
+ const currencyResult = await this.tryCurrencyConversion(query);
110
+ if (currencyResult) {
111
+ this.historyManager.addEntry(currencyResult);
112
+ await this.clipboardManager.copy(currencyResult.result.toString());
113
+ return currencyResult;
114
+ }
115
+
116
+ // Try date/time calculation
117
+ const dateTimeResult = this.dateTimeCalculator.parse(query);
118
+ if (dateTimeResult && !dateTimeResult.error) {
119
+ const response = {
120
+ query,
121
+ result: dateTimeResult.result,
122
+ type: dateTimeResult.type,
123
+ formatted: dateTimeResult.formatted
124
+ };
125
+ this.historyManager.addEntry(response);
126
+ await this.clipboardManager.copy(dateTimeResult.result.toString());
127
+ return response;
128
+ }
129
+
130
+ // Try programmer calculation
131
+ const programmerResult = this.programmerCalculator.parse(query);
132
+ if (programmerResult && !programmerResult.error) {
133
+ const response = {
134
+ query,
135
+ result: programmerResult.result,
136
+ type: 'programmer',
137
+ formatted: programmerResult.formatted,
138
+ conversions: programmerResult.conversions
139
+ };
140
+ this.historyManager.addEntry(response);
141
+ await this.clipboardManager.copy(programmerResult.result.toString());
142
+ return response;
49
143
  }
50
144
 
51
- // Try natural language parsing first
145
+ // Try utilities (password, random, color, text, emoji, search)
146
+ const utilityResult = this.utilities.parse(query);
147
+ if (utilityResult) {
148
+ // Handle web search - open browser
149
+ if (utilityResult.type === 'search' && utilityResult.url) {
150
+ const { exec } = require('child_process');
151
+ exec(`start "" "${utilityResult.url}"`);
152
+ return {
153
+ query,
154
+ result: utilityResult.result,
155
+ type: 'search',
156
+ formatted: utilityResult.formatted
157
+ };
158
+ }
159
+
160
+ const response = {
161
+ query,
162
+ result: utilityResult.result,
163
+ type: utilityResult.type,
164
+ formatted: utilityResult.formatted
165
+ };
166
+ this.historyManager.addEntry(response);
167
+ await this.clipboardManager.copy(utilityResult.result.toString());
168
+ return response;
169
+ }
170
+
171
+ // Try natural language parsing
52
172
  const nlpResult = this.nlpParser.parse(query);
53
-
173
+
54
174
  if (nlpResult.type === 'conversion') {
55
175
  const result = this.unitConverter.convert(
56
176
  nlpResult.value,
57
177
  nlpResult.fromUnit,
58
178
  nlpResult.toUnit
59
179
  );
60
-
180
+
61
181
  const response = {
62
182
  query,
63
183
  result,
64
184
  type: 'conversion',
65
185
  formatted: `${nlpResult.value} ${nlpResult.fromUnit} = ${result} ${nlpResult.toUnit}`
66
186
  };
67
-
187
+
68
188
  this.historyManager.addEntry(response);
69
189
  await this.clipboardManager.copy(result.toString());
70
-
190
+
71
191
  return response;
72
192
  }
73
193
 
74
194
  if (nlpResult.type === 'natural_language') {
75
195
  const mathResult = this.mathEngine.evaluate(nlpResult.expression);
76
-
196
+
77
197
  const response = {
78
198
  query,
79
199
  result: mathResult,
80
200
  type: 'natural_language',
81
201
  formatted: `${query} = ${mathResult}`
82
202
  };
83
-
203
+
84
204
  this.historyManager.addEntry(response);
85
205
  await this.clipboardManager.copy(mathResult.toString());
86
-
206
+
87
207
  return response;
88
208
  }
89
209
 
90
210
  // Direct math evaluation
91
211
  const mathResult = this.mathEngine.evaluate(query);
92
-
212
+
93
213
  const response = {
94
214
  query,
95
215
  result: mathResult,
96
216
  type: 'math',
97
217
  formatted: `${query} = ${mathResult}`
98
218
  };
99
-
219
+
100
220
  this.historyManager.addEntry(response);
101
221
  await this.clipboardManager.copy(mathResult.toString());
102
-
222
+
103
223
  return response;
104
224
 
105
225
  } catch (error) {
@@ -112,6 +232,43 @@ class ManiCalc {
112
232
  }
113
233
  }
114
234
 
235
+ /**
236
+ * Try to parse as currency conversion
237
+ */
238
+ async tryCurrencyConversion(query) {
239
+ // Pattern: "100 USD to INR", "100 dollars to rupees", "$100 to euros"
240
+ const patterns = [
241
+ /^(\d+(?:\.\d+)?)\s*([a-zA-Z$€£„₹]+)\s+(?:to|in|=)\s+([a-zA-Z$€£„₹]+)$/i,
242
+ /^convert\s+(\d+(?:\.\d+)?)\s*([a-zA-Z$€£„₹]+)\s+to\s+([a-zA-Z$€£„₹]+)$/i
243
+ ];
244
+
245
+ for (const pattern of patterns) {
246
+ const match = query.match(pattern);
247
+ if (match) {
248
+ try {
249
+ const amount = parseFloat(match[1]);
250
+ const fromCurrency = match[2];
251
+ const toCurrency = match[3];
252
+
253
+ const conversion = await this.currencyConverter.convert(amount, fromCurrency, toCurrency);
254
+
255
+ return {
256
+ query,
257
+ result: conversion.result,
258
+ type: 'currency',
259
+ formatted: this.currencyConverter.formatResult(conversion),
260
+ rate: conversion.rate
261
+ };
262
+ } catch (error) {
263
+ // Not a valid currency conversion
264
+ return null;
265
+ }
266
+ }
267
+ }
268
+
269
+ return null;
270
+ }
271
+
115
272
  async shutdown() {
116
273
  console.log(chalk.cyan('šŸ‘‹ Mani-Calc shutting down...'));
117
274
  await this.windowsSearch.unregister();
@@ -74,7 +74,13 @@ class FloatingSearchBox {
74
74
  if (systemResult) {
75
75
  console.log('System command executed:', systemResult);
76
76
  event.reply('query-result', systemResult);
77
- setTimeout(() => this.hide(), 1000);
77
+ // Don't auto-hide for info commands like 'help' - let user read the content
78
+ // Only auto-hide for action commands that perform an action (sleep, lock, etc.)
79
+ const actionCommands = ['sleep', 'shutdown', 'restart', 'lock', 'logout', 'quit', 'exit'];
80
+ const cmd = query.toLowerCase().trim();
81
+ if (actionCommands.includes(cmd)) {
82
+ setTimeout(() => this.hide(), 1500);
83
+ }
78
84
  return;
79
85
  }
80
86
 
@@ -96,9 +102,344 @@ class FloatingSearchBox {
96
102
  ipcMain.on('hide-window', () => {
97
103
  this.hide();
98
104
  });
105
+
106
+ // Handle resize request
107
+ ipcMain.on('resize-window', (event, height) => {
108
+ if (this.window) {
109
+ this.window.setSize(600, height);
110
+ }
111
+ });
99
112
  }
100
113
 
101
- // ... (rest of methods) ...
114
+ async handleSystemCommand(query) {
115
+ const cmd = query.toLowerCase().trim();
116
+ const { exec } = require('child_process');
117
+ const { promisify } = require('util');
118
+ const execAsync = promisify(exec);
119
+
120
+ const commands = {
121
+ 'help': {
122
+ action: async () => {
123
+ return `šŸ“Š MATH: 2+3, sqrt(16), 15% of 200
124
+ šŸ’± CURRENCY: 100 USD to INR, 50 euros to dollars
125
+ šŸ“… DATE/TIME: time in tokyo, today + 30 days
126
+ šŸ’» PROGRAMMER: 255 to hex, 0xFF to decimal
127
+ šŸ“ UNITS: 10 km to miles, 100 kg to pounds
128
+ šŸ” PASSWORD: password, password 16
129
+ šŸŽ² RANDOM: random, dice, coin, uuid
130
+ šŸŽØ COLOR: #FF5733 to rgb, rgb(255,0,0) to hex
131
+ šŸ“ TEXT: upper hello, lower HELLO, count text
132
+ šŸ˜€ EMOJI: emoji happy, emoji heart
133
+ šŸ”’ HASH: md5 hello, sha256 hello
134
+ šŸ”¤ BASE64: base64 encode/decode text
135
+ šŸ” SEARCH: google AI, youtube music, wiki topic
136
+ āš™ļø SYSTEM: sleep, lock, mute, screenshot, battery
137
+ šŸŽØ THEMES: theme dark, theme neon, themes
138
+ ⚔ SETTINGS: autostart on/off, settings, history`;
139
+ },
140
+ description: 'Show available commands'
141
+ },
142
+ 'quit': {
143
+ action: async () => {
144
+ setTimeout(() => app.quit(), 500);
145
+ return 'Quitting Mani-Calc...';
146
+ },
147
+ description: 'Quit application'
148
+ },
149
+ 'exit': {
150
+ action: async () => {
151
+ setTimeout(() => app.quit(), 500);
152
+ return 'Quitting Mani-Calc...';
153
+ },
154
+ description: 'Quit application'
155
+ },
156
+ 'theme': {
157
+ action: async () => {
158
+ this.window.webContents.send('toggle-theme');
159
+ return 'Toggled theme';
160
+ },
161
+ description: 'Toggle Light/Dark mode'
162
+ },
163
+ 'sleep': {
164
+ action: async () => {
165
+ await execAsync('rundll32.exe powrprof.dll,SetSuspendState 0,1,0');
166
+ return 'System going to sleep...';
167
+ },
168
+ description: 'Put computer to sleep'
169
+ },
170
+ 'shutdown': {
171
+ action: async () => {
172
+ await execAsync('shutdown /s /t 0');
173
+ return 'Shutting down...';
174
+ },
175
+ description: 'Shutdown computer'
176
+ },
177
+ 'restart': {
178
+ action: async () => {
179
+ await execAsync('shutdown /r /t 0');
180
+ return 'Restarting...';
181
+ },
182
+ description: 'Restart computer'
183
+ },
184
+ 'lock': {
185
+ action: async () => {
186
+ await execAsync('rundll32.exe user32.dll,LockWorkStation');
187
+ return 'Locking computer...';
188
+ },
189
+ description: 'Lock computer'
190
+ },
191
+ 'logout': {
192
+ action: async () => {
193
+ await execAsync('shutdown /l');
194
+ return 'Logging out...';
195
+ },
196
+ description: 'Log out current user'
197
+ },
198
+ 'empty recycle bin': {
199
+ action: async () => {
200
+ await execAsync('rd /s /q %systemdrive%\\$Recycle.bin');
201
+ return 'Recycle bin emptied';
202
+ },
203
+ description: 'Empty recycle bin'
204
+ },
205
+ 'volume up': {
206
+ action: async () => {
207
+ // Use PowerShell to simulate volume up key press
208
+ await execAsync('powershell -Command "(New-Object -ComObject WScript.Shell).SendKeys([char]175)"');
209
+ return 'Volume increased';
210
+ },
211
+ description: 'Increase volume'
212
+ },
213
+ 'volume down': {
214
+ action: async () => {
215
+ // Use PowerShell to simulate volume down key press
216
+ await execAsync('powershell -Command "(New-Object -ComObject WScript.Shell).SendKeys([char]174)"');
217
+ return 'Volume decreased';
218
+ },
219
+ description: 'Decrease volume'
220
+ },
221
+ 'mute': {
222
+ action: async () => {
223
+ // Use PowerShell to simulate mute key press
224
+ await execAsync('powershell -Command "(New-Object -ComObject WScript.Shell).SendKeys([char]173)"');
225
+ return 'Volume muted/unmuted';
226
+ },
227
+ description: 'Toggle mute'
228
+ },
229
+ 'unmute': {
230
+ action: async () => {
231
+ // Same as mute - it's a toggle
232
+ await execAsync('powershell -Command "(New-Object -ComObject WScript.Shell).SendKeys([char]173)"');
233
+ return 'Volume muted/unmuted';
234
+ },
235
+ description: 'Toggle mute'
236
+ },
237
+ // Screenshot
238
+ 'screenshot': {
239
+ action: async () => {
240
+ await execAsync('snippingtool');
241
+ return 'Opening Snipping Tool...';
242
+ },
243
+ description: 'Take a screenshot'
244
+ },
245
+ 'snip': {
246
+ action: async () => {
247
+ await execAsync('snippingtool');
248
+ return 'Opening Snipping Tool...';
249
+ },
250
+ description: 'Take a screenshot'
251
+ },
252
+ // Brightness controls
253
+ 'brightness up': {
254
+ action: async () => {
255
+ await execAsync('powershell -Command "(Get-WmiObject -Namespace root/WMI -Class WmiMonitorBrightnessMethods).WmiSetBrightness(1, [Math]::Min(100, (Get-WmiObject -Namespace root/WMI -Class WmiMonitorBrightness).CurrentBrightness + 10))"');
256
+ return 'Brightness increased';
257
+ },
258
+ description: 'Increase screen brightness'
259
+ },
260
+ 'brightness down': {
261
+ action: async () => {
262
+ await execAsync('powershell -Command "(Get-WmiObject -Namespace root/WMI -Class WmiMonitorBrightnessMethods).WmiSetBrightness(1, [Math]::Max(0, (Get-WmiObject -Namespace root/WMI -Class WmiMonitorBrightness).CurrentBrightness - 10))"');
263
+ return 'Brightness decreased';
264
+ },
265
+ description: 'Decrease screen brightness'
266
+ },
267
+ // WiFi toggle
268
+ 'wifi on': {
269
+ action: async () => {
270
+ await execAsync('netsh interface set interface "Wi-Fi" enable');
271
+ return 'WiFi enabled';
272
+ },
273
+ description: 'Enable WiFi'
274
+ },
275
+ 'wifi off': {
276
+ action: async () => {
277
+ await execAsync('netsh interface set interface "Wi-Fi" disable');
278
+ return 'WiFi disabled';
279
+ },
280
+ description: 'Disable WiFi'
281
+ },
282
+ // Bluetooth toggle
283
+ 'bluetooth on': {
284
+ action: async () => {
285
+ await execAsync('powershell -Command "Start-Service bthserv"');
286
+ return 'Bluetooth service started';
287
+ },
288
+ description: 'Enable Bluetooth'
289
+ },
290
+ 'bluetooth off': {
291
+ action: async () => {
292
+ await execAsync('powershell -Command "Stop-Service bthserv"');
293
+ return 'Bluetooth service stopped';
294
+ },
295
+ description: 'Disable Bluetooth'
296
+ },
297
+ // Quick app launchers
298
+ 'notepad': {
299
+ action: async () => {
300
+ await execAsync('notepad');
301
+ return 'Opening Notepad...';
302
+ },
303
+ description: 'Open Notepad'
304
+ },
305
+ 'calculator': {
306
+ action: async () => {
307
+ await execAsync('calc');
308
+ return 'Opening Calculator...';
309
+ },
310
+ description: 'Open Windows Calculator'
311
+ },
312
+ 'calc app': {
313
+ action: async () => {
314
+ await execAsync('calc');
315
+ return 'Opening Calculator...';
316
+ },
317
+ description: 'Open Windows Calculator'
318
+ },
319
+ 'explorer': {
320
+ action: async () => {
321
+ await execAsync('explorer');
322
+ return 'Opening File Explorer...';
323
+ },
324
+ description: 'Open File Explorer'
325
+ },
326
+ 'files': {
327
+ action: async () => {
328
+ await execAsync('explorer');
329
+ return 'Opening File Explorer...';
330
+ },
331
+ description: 'Open File Explorer'
332
+ },
333
+ 'task manager': {
334
+ action: async () => {
335
+ await execAsync('taskmgr');
336
+ return 'Opening Task Manager...';
337
+ },
338
+ description: 'Open Task Manager'
339
+ },
340
+ 'taskmgr': {
341
+ action: async () => {
342
+ await execAsync('taskmgr');
343
+ return 'Opening Task Manager...';
344
+ },
345
+ description: 'Open Task Manager'
346
+ },
347
+ 'control panel': {
348
+ action: async () => {
349
+ await execAsync('control');
350
+ return 'Opening Control Panel...';
351
+ },
352
+ description: 'Open Control Panel'
353
+ },
354
+ 'settings app': {
355
+ action: async () => {
356
+ await execAsync('start ms-settings:');
357
+ return 'Opening Windows Settings...';
358
+ },
359
+ description: 'Open Windows Settings'
360
+ },
361
+ 'cmd': {
362
+ action: async () => {
363
+ await execAsync('start cmd');
364
+ return 'Opening Command Prompt...';
365
+ },
366
+ description: 'Open Command Prompt'
367
+ },
368
+ 'powershell': {
369
+ action: async () => {
370
+ await execAsync('start powershell');
371
+ return 'Opening PowerShell...';
372
+ },
373
+ description: 'Open PowerShell'
374
+ },
375
+ 'browser': {
376
+ action: async () => {
377
+ await execAsync('start chrome || start msedge || start firefox');
378
+ return 'Opening Browser...';
379
+ },
380
+ description: 'Open default browser'
381
+ },
382
+ // System info
383
+ 'ip': {
384
+ action: async () => {
385
+ const { stdout } = await execAsync('powershell -Command "(Get-NetIPAddress -AddressFamily IPv4 | Where-Object {$_.InterfaceAlias -notlike \'*Loopback*\'} | Select-Object -First 1).IPAddress"');
386
+ return `Your IP: ${stdout.trim()}`;
387
+ },
388
+ description: 'Show IP address'
389
+ },
390
+ 'battery': {
391
+ action: async () => {
392
+ const { stdout } = await execAsync('powershell -Command "(Get-WmiObject Win32_Battery).EstimatedChargeRemaining"');
393
+ const percent = stdout.trim();
394
+ return `šŸ”‹ Battery: ${percent}%`;
395
+ },
396
+ description: 'Show battery level'
397
+ }
398
+ };
399
+
400
+ if (commands[cmd]) {
401
+ try {
402
+ const message = await commands[cmd].action();
403
+ return {
404
+ result: message,
405
+ type: 'system_command',
406
+ formatted: message
407
+ };
408
+ } catch (error) {
409
+ return {
410
+ error: `Failed to execute: ${error.message}`,
411
+ type: 'error'
412
+ };
413
+ }
414
+ }
415
+
416
+ return null;
417
+ }
418
+
419
+ show() {
420
+ if (this.window) {
421
+ this.window.show();
422
+ this.window.focus();
423
+ this.isVisible = true;
424
+ this.window.webContents.send('focus-input');
425
+ }
426
+ }
427
+
428
+ hide() {
429
+ if (this.window) {
430
+ this.window.hide();
431
+ this.isVisible = false;
432
+ this.window.webContents.send('clear-input');
433
+ }
434
+ }
435
+
436
+ toggle() {
437
+ if (this.isVisible) {
438
+ this.hide();
439
+ } else {
440
+ this.show();
441
+ }
442
+ }
102
443
 
103
444
  destroy() {
104
445
  if (this.window) {