@pheem49/mint 1.5.3 → 1.5.4

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.
@@ -0,0 +1,227 @@
1
+ const { execFile } = require('child_process');
2
+ const os = require('os');
3
+
4
+ const MAX_TEXT_LENGTH = 2000;
5
+ const BROWSER_NAMES = [
6
+ 'chrome',
7
+ 'chromium',
8
+ 'brave',
9
+ 'firefox',
10
+ 'edge',
11
+ 'safari',
12
+ 'opera',
13
+ 'vivaldi'
14
+ ];
15
+
16
+ function run(command, args = [], options = {}) {
17
+ return new Promise((resolve) => {
18
+ execFile(command, args, { timeout: options.timeout || 1200 }, (error, stdout) => {
19
+ if (error) {
20
+ resolve(null);
21
+ return;
22
+ }
23
+ resolve(String(stdout || '').trim() || null);
24
+ });
25
+ });
26
+ }
27
+
28
+ function truncateText(value, maxLength = MAX_TEXT_LENGTH) {
29
+ const text = String(value || '').replace(/\0/g, '').trim();
30
+ if (text.length <= maxLength) return text;
31
+ return `${text.slice(0, maxLength)}\n[truncated ${text.length - maxLength} chars]`;
32
+ }
33
+
34
+ function normalizeProcessName(value) {
35
+ return String(value || '').trim().replace(/\.exe$/i, '');
36
+ }
37
+
38
+ function isBrowserProcess(name = '') {
39
+ const normalized = normalizeProcessName(name).toLowerCase();
40
+ return BROWSER_NAMES.some(browser => normalized.includes(browser));
41
+ }
42
+
43
+ async function getLinuxActiveWindow() {
44
+ const windowId = await run('xdotool', ['getactivewindow']);
45
+ if (!windowId) return null;
46
+
47
+ const [title, pid] = await Promise.all([
48
+ run('xdotool', ['getwindowname', windowId]),
49
+ run('xdotool', ['getwindowpid', windowId])
50
+ ]);
51
+
52
+ let processName = '';
53
+ if (pid) {
54
+ processName = await run('ps', ['-p', pid, '-o', 'comm=']) || '';
55
+ }
56
+
57
+ return {
58
+ id: windowId,
59
+ title: title || '',
60
+ appName: normalizeProcessName(processName),
61
+ processName: normalizeProcessName(processName),
62
+ pid: pid ? Number(pid) : null,
63
+ platform: 'linux'
64
+ };
65
+ }
66
+
67
+ async function getMacActiveWindow() {
68
+ const script = [
69
+ 'tell application "System Events"',
70
+ 'set frontApp to first application process whose frontmost is true',
71
+ 'set appName to name of frontApp',
72
+ 'set windowTitle to ""',
73
+ 'try',
74
+ 'set windowTitle to name of front window of frontApp',
75
+ 'end try',
76
+ 'return appName & linefeed & windowTitle',
77
+ 'end tell'
78
+ ].join('\n');
79
+ const output = await run('osascript', ['-e', script]);
80
+ if (!output) return null;
81
+ const [appName = '', title = ''] = output.split(/\r?\n/);
82
+ return {
83
+ title,
84
+ appName,
85
+ processName: appName,
86
+ pid: null,
87
+ platform: 'darwin'
88
+ };
89
+ }
90
+
91
+ async function getWindowsActiveWindow() {
92
+ const script = [
93
+ 'Add-Type @\'',
94
+ 'using System;',
95
+ 'using System.Runtime.InteropServices;',
96
+ 'using System.Text;',
97
+ 'public class Win {',
98
+ '[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();',
99
+ '[DllImport("user32.dll")] public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);',
100
+ '[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint pid);',
101
+ '}',
102
+ '\'@',
103
+ '$hwnd = [Win]::GetForegroundWindow()',
104
+ '$builder = New-Object System.Text.StringBuilder 1024',
105
+ '[void][Win]::GetWindowText($hwnd, $builder, $builder.Capacity)',
106
+ '$pid = 0',
107
+ '[void][Win]::GetWindowThreadProcessId($hwnd, [ref]$pid)',
108
+ '$proc = Get-Process -Id $pid -ErrorAction SilentlyContinue',
109
+ '[PSCustomObject]@{ title = $builder.ToString(); appName = $proc.ProcessName; pid = $pid } | ConvertTo-Json -Compress'
110
+ ].join('\n');
111
+ const output = await run('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], { timeout: 1800 });
112
+ if (!output) return null;
113
+ try {
114
+ const parsed = JSON.parse(output);
115
+ return {
116
+ title: parsed.title || '',
117
+ appName: normalizeProcessName(parsed.appName),
118
+ processName: normalizeProcessName(parsed.appName),
119
+ pid: parsed.pid ? Number(parsed.pid) : null,
120
+ platform: 'win32'
121
+ };
122
+ } catch (_) {
123
+ return null;
124
+ }
125
+ }
126
+
127
+ async function getActiveWindowContext(platform = process.platform) {
128
+ try {
129
+ if (platform === 'darwin') return await getMacActiveWindow();
130
+ if (platform === 'win32') return await getWindowsActiveWindow();
131
+ return await getLinuxActiveWindow();
132
+ } catch (_) {
133
+ return null;
134
+ }
135
+ }
136
+
137
+ async function getLinuxSelectedText() {
138
+ const attempts = [
139
+ ['wl-paste', ['--primary', '--no-newline']],
140
+ ['xclip', ['-selection', 'primary', '-out']],
141
+ ['xsel', ['--primary', '--output']]
142
+ ];
143
+
144
+ for (const [command, args] of attempts) {
145
+ const text = await run(command, args);
146
+ if (text) return truncateText(text);
147
+ }
148
+ return '';
149
+ }
150
+
151
+ async function getSelectedText(platform = process.platform) {
152
+ if (platform === 'linux') return getLinuxSelectedText();
153
+ return '';
154
+ }
155
+
156
+ async function getMacBrowserContext(appName) {
157
+ const normalized = String(appName || '').toLowerCase();
158
+ let script = '';
159
+ if (normalized.includes('safari')) {
160
+ script = 'tell application "Safari" to return name of front document & linefeed & URL of front document';
161
+ } else if (normalized.includes('chrome') || normalized.includes('chromium') || normalized.includes('brave') || normalized.includes('edge')) {
162
+ script = `tell application "${appName}" to return title of active tab of front window & linefeed & URL of active tab of front window`;
163
+ }
164
+ if (!script) return null;
165
+ const output = await run('osascript', ['-e', script], { timeout: 1500 });
166
+ if (!output) return null;
167
+ const [title = '', url = ''] = output.split(/\r?\n/);
168
+ return { title, url };
169
+ }
170
+
171
+ async function getBrowserContext(activeWindow, platform = process.platform) {
172
+ if (!activeWindow || !isBrowserProcess(activeWindow.appName || activeWindow.processName)) {
173
+ return null;
174
+ }
175
+ if (platform === 'darwin') {
176
+ const browser = await getMacBrowserContext(activeWindow.appName);
177
+ if (browser) return browser;
178
+ }
179
+ return {
180
+ title: activeWindow.title || '',
181
+ url: '',
182
+ urlUnavailableReason: 'Browser URL is not available from the current OS context without browser integration.'
183
+ };
184
+ }
185
+
186
+ async function getSmartContext(options = {}) {
187
+ const platform = options.platform || process.platform;
188
+ const clipboard = options.clipboard || null;
189
+ const [activeWindow, selectedText] = await Promise.all([
190
+ getActiveWindowContext(platform),
191
+ getSelectedText(platform)
192
+ ]);
193
+
194
+ let clipboardText = '';
195
+ try {
196
+ clipboardText = clipboard && typeof clipboard.readText === 'function'
197
+ ? truncateText(clipboard.readText())
198
+ : '';
199
+ } catch (_) {
200
+ clipboardText = '';
201
+ }
202
+
203
+ const browser = await getBrowserContext(activeWindow, platform);
204
+ return {
205
+ capturedAt: new Date().toISOString(),
206
+ platform,
207
+ host: os.hostname(),
208
+ activeWindow,
209
+ currentApp: activeWindow ? {
210
+ name: activeWindow.appName || activeWindow.processName || '',
211
+ processName: activeWindow.processName || activeWindow.appName || '',
212
+ pid: activeWindow.pid || null
213
+ } : null,
214
+ browser,
215
+ selectedText,
216
+ clipboardText
217
+ };
218
+ }
219
+
220
+ module.exports = {
221
+ getSmartContext,
222
+ getActiveWindowContext,
223
+ getBrowserContext,
224
+ getSelectedText,
225
+ truncateText,
226
+ isBrowserProcess
227
+ };
@@ -2,6 +2,7 @@ const { contextBridge, ipcRenderer } = require('electron');
2
2
 
3
3
  contextBridge.exposeInMainWorld('spotlightAPI', {
4
4
  submit: (query) => ipcRenderer.send('spotlight-submit', query),
5
+ executeAction: (action) => ipcRenderer.invoke('spotlight-action', action),
5
6
  close: () => ipcRenderer.send('spotlight-close'),
6
7
  hide: () => ipcRenderer.send('spotlight-hide'),
7
8
  resize: (width, height) => ipcRenderer.send('spotlight-resize', width, height),