@glassnote/client 2.4.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/INSTALACION.md +258 -0
- package/README.md +228 -0
- package/autoStart.js +608 -0
- package/autoupdate.js +473 -0
- package/autoupdate_renderer.js +371 -0
- package/bin/glassnote.js +523 -0
- package/images/demo01.png +0 -0
- package/images/dmg-background.png +0 -0
- package/images/dmg-background.svg +37628 -0
- package/images/icon-16.png +0 -0
- package/images/icon-24.png +0 -0
- package/images/icon-512.png +0 -0
- package/images/icon.ico +0 -0
- package/images/icon.png +0 -0
- package/images/splash.svg +61 -0
- package/localserver.js +297 -0
- package/logUtilities.js +128 -0
- package/main.js +745 -0
- package/npmMode.js +139 -0
- package/npmUpdate.js +152 -0
- package/package.json +141 -0
- package/preload.js +148 -0
- package/userData.js +492 -0
package/autoStart.js
ADDED
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
// electron se carga de forma perezosa: este módulo también lo usa el CLI (node puro),
|
|
2
|
+
// que necesita `removeAutoStart()` para `glassnote uninstall`.
|
|
3
|
+
let app = null;
|
|
4
|
+
try {
|
|
5
|
+
({ app } = require('electron'));
|
|
6
|
+
} catch (error) {
|
|
7
|
+
app = null;
|
|
8
|
+
}
|
|
9
|
+
const npmMode = require('./npmMode');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const { execSync, spawn } = require('child_process');
|
|
14
|
+
|
|
15
|
+
class AutoStart {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.platform = process.platform;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Comando con el que se vuelve a arrancar el cliente.
|
|
21
|
+
//
|
|
22
|
+
// Instalado por npm/npx no hay ningún `glassnote.exe`: el ejecutable ES el binario de
|
|
23
|
+
// electron que bajó npm, y la app va como argumento. Sin esto los tres autoarranques
|
|
24
|
+
// de abajo se salían sin hacer nada (comprueban que el binario se llame glassnote) y
|
|
25
|
+
// el cliente no volvía a abrirse nunca solo.
|
|
26
|
+
getLaunchCommand() {
|
|
27
|
+
if (npmMode.isNpmMode()) {
|
|
28
|
+
// Las banderas del sandbox van también aquí: sin ellas el arranque automático
|
|
29
|
+
// fallaría en las máquinas donde Chromium aborta, y el fallo no lo vería nadie
|
|
30
|
+
// porque nadie mira la salida de una entrada de autoarranque.
|
|
31
|
+
return {
|
|
32
|
+
exec: process.execPath,
|
|
33
|
+
args: [npmMode.PACKAGE_ROOT].concat(npmMode.sandboxFlags(process.execPath)),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return { exec: this.getExecutablePath(), args: [] };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Get the path to the executable (app when packaged, or electron during development)
|
|
40
|
+
getExecutablePath() {
|
|
41
|
+
|
|
42
|
+
if (app && app.isPackaged) {
|
|
43
|
+
const appPath = process.execPath;
|
|
44
|
+
const executableName = path.basename(appPath).toLowerCase();
|
|
45
|
+
|
|
46
|
+
// Platform-specific expected names
|
|
47
|
+
let expectedName = '';
|
|
48
|
+
if (this.platform === 'win32') {
|
|
49
|
+
expectedName = 'glassnote.exe';
|
|
50
|
+
} else if (this.platform === 'darwin') {
|
|
51
|
+
expectedName = 'glassnote'; // macOS - sin extensión
|
|
52
|
+
} else if (this.platform === 'linux') {
|
|
53
|
+
expectedName = 'glassnote'; // Linux - sin extensión
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Check if the executable has the expected name for the platform
|
|
57
|
+
if (executableName !== expectedName) {
|
|
58
|
+
return appPath;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return appPath;
|
|
62
|
+
} else {
|
|
63
|
+
// In development, we need to start the app with electron
|
|
64
|
+
return process.execPath;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Windows auto-start implementation
|
|
69
|
+
setupWindowsAutoStart() {
|
|
70
|
+
try {
|
|
71
|
+
|
|
72
|
+
// Create shortcuts in both locations
|
|
73
|
+
const startupPaths = [
|
|
74
|
+
path.join(
|
|
75
|
+
process.env.APPDATA,
|
|
76
|
+
'Microsoft',
|
|
77
|
+
'Windows',
|
|
78
|
+
'Start Menu',
|
|
79
|
+
'Programs',
|
|
80
|
+
'Startup',
|
|
81
|
+
'glassnote.lnk'
|
|
82
|
+
),
|
|
83
|
+
path.join(
|
|
84
|
+
process.env.APPDATA,
|
|
85
|
+
'Microsoft',
|
|
86
|
+
'Windows',
|
|
87
|
+
'Start Menu',
|
|
88
|
+
'Programs',
|
|
89
|
+
'glassnote.lnk'
|
|
90
|
+
)
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
const { exec: executablePath, args: launchArgs } = this.getLaunchCommand();
|
|
94
|
+
|
|
95
|
+
// Verify the executable exists and is named glassnote.exe
|
|
96
|
+
if (!fs.existsSync(executablePath)) {
|
|
97
|
+
console.error('Executable not found:', executablePath);
|
|
98
|
+
console.error('Directory contents:', fs.readdirSync(path.dirname(executablePath) || '.'));
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const executableName = path.basename(executablePath).toLowerCase();
|
|
103
|
+
|
|
104
|
+
// Only create shortcuts if the executable is named glassnote.exe
|
|
105
|
+
// Don't delete existing shortcuts if the name doesn't match
|
|
106
|
+
// (en modo npm el binario es electron.exe y el nombre no aplica)
|
|
107
|
+
if (!npmMode.isNpmMode() && executableName !== 'glassnote.exe') {
|
|
108
|
+
return true; // Return true to indicate "success" without modifying shortcuts
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// La carpeta de trabajo: la del paquete en modo npm, la del ejecutable en nativo.
|
|
112
|
+
const workingDir = launchArgs.length ? launchArgs[0] : path.dirname(executablePath);
|
|
113
|
+
|
|
114
|
+
let allSuccess = true;
|
|
115
|
+
|
|
116
|
+
for (const startupPath of startupPaths) {
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
// Remove existing shortcut if it exists
|
|
120
|
+
if (fs.existsSync(startupPath)) {
|
|
121
|
+
fs.unlinkSync(startupPath);
|
|
122
|
+
} else {
|
|
123
|
+
}
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error('Could not remove existing shortcut (access may be restricted):', error.message);
|
|
126
|
+
// Continue with creation attempt even if removal failed
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
// Create shortcut using Windows Script Host
|
|
131
|
+
const script = `
|
|
132
|
+
Set oWS = WScript.CreateObject("WScript.Shell")
|
|
133
|
+
Set oLink = oWS.CreateShortcut("${startupPath.replace(/\\/g, '\\\\')}")
|
|
134
|
+
oLink.TargetPath = "${executablePath.replace(/\\/g, '\\\\')}"
|
|
135
|
+
oLink.Arguments = "${launchArgs.map((a) => `""${a.replace(/\\/g, '\\\\')}""`).join(' ')}"
|
|
136
|
+
oLink.WorkingDirectory = "${workingDir.replace(/\\/g, '\\\\')}"
|
|
137
|
+
oLink.WindowStyle = 7
|
|
138
|
+
oLink.Save
|
|
139
|
+
`;
|
|
140
|
+
|
|
141
|
+
// Use temp directory instead of app directory (app.asar is read-only)
|
|
142
|
+
const tempDir = os.tmpdir();
|
|
143
|
+
const tempScriptPath = path.join(tempDir, 'glassnote_create_shortcut.vbs');
|
|
144
|
+
|
|
145
|
+
fs.writeFileSync(tempScriptPath, script);
|
|
146
|
+
|
|
147
|
+
execSync(`cscript //nologo "${tempScriptPath}"`);
|
|
148
|
+
|
|
149
|
+
fs.unlinkSync(tempScriptPath);
|
|
150
|
+
|
|
151
|
+
// Verify the shortcut was created
|
|
152
|
+
if (fs.existsSync(startupPath)) {
|
|
153
|
+
} else {
|
|
154
|
+
// Don't mark as failure if access is restricted
|
|
155
|
+
try {
|
|
156
|
+
if (fs.existsSync(path.dirname(startupPath))) {
|
|
157
|
+
}
|
|
158
|
+
} catch (dirError) {
|
|
159
|
+
console.error('Cannot access directory (access restricted):', dirError.message);
|
|
160
|
+
}
|
|
161
|
+
// Continue without marking as failure
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error('Could not create shortcut (access may be restricted):', error.message);
|
|
165
|
+
// Don't mark as failure if access is restricted
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (allSuccess) {
|
|
170
|
+
return true;
|
|
171
|
+
} else {
|
|
172
|
+
console.error('Windows auto-start configuration partially failed');
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.error('Error configuring Windows auto-start:', error);
|
|
177
|
+
console.error('Error message:', error.message);
|
|
178
|
+
if (error.stdout) console.error('stdout:', error.stdout.toString());
|
|
179
|
+
if (error.stderr) console.error('stderr:', error.stderr.toString());
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// macOS auto-start implementation
|
|
185
|
+
setupMacAutoStart() {
|
|
186
|
+
try {
|
|
187
|
+
|
|
188
|
+
const plistPath = path.join(
|
|
189
|
+
process.env.HOME,
|
|
190
|
+
'Library',
|
|
191
|
+
'LaunchAgents',
|
|
192
|
+
'com.glassnote.app.plist'
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
const { exec: executablePath, args: launchArgs } = this.getLaunchCommand();
|
|
197
|
+
|
|
198
|
+
// Verify the executable exists
|
|
199
|
+
if (!fs.existsSync(executablePath)) {
|
|
200
|
+
console.error('Executable not found:', executablePath);
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Check if the executable is named glassnote (macOS binary name)
|
|
205
|
+
const executableName = path.basename(executablePath).toLowerCase();
|
|
206
|
+
|
|
207
|
+
// Instalado por npm el binario es el Electron de node_modules y no está en
|
|
208
|
+
// /Applications: las dos comprobaciones de abajo solo valen para el .app nativo.
|
|
209
|
+
if (!npmMode.isNpmMode()) {
|
|
210
|
+
// Only create plist if the executable is named glassnote
|
|
211
|
+
// Don't remove existing plist if the name doesn't match
|
|
212
|
+
if (executableName !== 'glassnote') {
|
|
213
|
+
return true; // Return true to indicate "success" without modifying plist
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Only create auto-start if the application is installed in the Applications folder
|
|
217
|
+
const applicationsPath = '/Applications/';
|
|
218
|
+
if (!executablePath.startsWith(applicationsPath)) {
|
|
219
|
+
return true; // Return true to indicate "success" without modifying plist
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Remove existing plist if it exists
|
|
224
|
+
if (fs.existsSync(plistPath)) {
|
|
225
|
+
try {
|
|
226
|
+
// Use spawn instead of execSync to avoid blocking
|
|
227
|
+
const unloadProcess = spawn('launchctl', ['unload', plistPath], {
|
|
228
|
+
stdio: 'ignore',
|
|
229
|
+
timeout: 5000 // 5 second timeout
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
unloadProcess.on('error', (error) => {
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
unloadProcess.on('exit', (code) => {
|
|
236
|
+
if (code !== 0) {
|
|
237
|
+
console.warn('launchctl unload exited with code:', code, '(may not be loaded)');
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.error('Could not unload existing plist:', error.message);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Remove the plist file regardless of launchctl status
|
|
245
|
+
try {
|
|
246
|
+
fs.unlinkSync(plistPath);
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error('Could not remove plist file:', error.message);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
253
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
254
|
+
<plist version="1.0">
|
|
255
|
+
<dict>
|
|
256
|
+
<key>Label</key>
|
|
257
|
+
<string>com.glassnote.app</string>
|
|
258
|
+
<key>ProgramArguments</key>
|
|
259
|
+
<array>
|
|
260
|
+
<string>${executablePath}</string>
|
|
261
|
+
${launchArgs.map((a) => ` <string>${a}</string>`).join('\n')}
|
|
262
|
+
</array>
|
|
263
|
+
<key>RunAtLoad</key>
|
|
264
|
+
<true/>
|
|
265
|
+
<key>KeepAlive</key>
|
|
266
|
+
<false/>
|
|
267
|
+
</dict>
|
|
268
|
+
</plist>`;
|
|
269
|
+
|
|
270
|
+
// Create directory if it doesn't exist
|
|
271
|
+
const plistDir = path.dirname(plistPath);
|
|
272
|
+
if (!fs.existsSync(plistDir)) {
|
|
273
|
+
fs.mkdirSync(plistDir, { recursive: true });
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
fs.writeFileSync(plistPath, plistContent);
|
|
277
|
+
|
|
278
|
+
// Load the launch agent asynchronously
|
|
279
|
+
try {
|
|
280
|
+
const loadProcess = spawn('launchctl', ['load', plistPath], {
|
|
281
|
+
stdio: 'ignore',
|
|
282
|
+
timeout: 5000 // 5 second timeout
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
loadProcess.on('error', (error) => {
|
|
286
|
+
console.error('Error spawning launchctl load:', error.message);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
loadProcess.on('exit', (code) => {
|
|
290
|
+
if (code !== 0) {
|
|
291
|
+
console.warn('launchctl load exited with code:', code);
|
|
292
|
+
} else {
|
|
293
|
+
console.warn('macOS auto-start configured:', plistPath);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
} catch (error) {
|
|
297
|
+
console.error('Error loading launch agent:', error.message);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return true;
|
|
301
|
+
} catch (error) {
|
|
302
|
+
console.error('Error configuring macOS auto-start:', error);
|
|
303
|
+
console.error('Error message:', error.message);
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Linux auto-start implementation
|
|
309
|
+
setupLinuxAutoStart() {
|
|
310
|
+
try {
|
|
311
|
+
|
|
312
|
+
const desktopFilePaths = [
|
|
313
|
+
path.join(process.env.HOME, '.local', 'share', 'applications', 'glassnote.desktop'),
|
|
314
|
+
path.join(process.env.HOME, '.config', 'autostart', 'glassnote.desktop')
|
|
315
|
+
];
|
|
316
|
+
|
|
317
|
+
const { exec: executablePath, args: launchArgs } = this.getLaunchCommand();
|
|
318
|
+
|
|
319
|
+
// Verify the executable exists
|
|
320
|
+
if (!fs.existsSync(executablePath)) {
|
|
321
|
+
console.error('Executable not found:', executablePath);
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Check if the executable is named glassnote (Linux typically uses the binary name)
|
|
326
|
+
const executableName = path.basename(executablePath).toLowerCase();
|
|
327
|
+
|
|
328
|
+
// En modo npm el binario se llama `electron`: la comprobación de nombre solo vale
|
|
329
|
+
// para el empaquetado nativo.
|
|
330
|
+
if (!npmMode.isNpmMode() && executableName !== 'glassnote') {
|
|
331
|
+
return true; // Return true to indicate "success" without modifying desktop files
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Comillas en Exec: la ruta del paquete npm puede tener espacios.
|
|
335
|
+
const execLine = [executablePath].concat(launchArgs).map((a) => `"${a}"`).join(' ');
|
|
336
|
+
const desktopContent = `[Desktop Entry]
|
|
337
|
+
Type=Application
|
|
338
|
+
Name=Glassnote
|
|
339
|
+
Exec=${execLine}
|
|
340
|
+
Icon=${npmMode.isNpmMode() ? path.join(npmMode.PACKAGE_ROOT, 'images', 'icon.png') : 'glassnote'}
|
|
341
|
+
Categories=Utility;
|
|
342
|
+
Terminal=false
|
|
343
|
+
StartupWMClass=glassnote`;
|
|
344
|
+
|
|
345
|
+
for (const desktopFilePath of desktopFilePaths) {
|
|
346
|
+
|
|
347
|
+
// Remove existing desktop file if it exists
|
|
348
|
+
if (fs.existsSync(desktopFilePath)) {
|
|
349
|
+
fs.unlinkSync(desktopFilePath);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const desktopDir = path.dirname(desktopFilePath);
|
|
353
|
+
if (!fs.existsSync(desktopDir)) {
|
|
354
|
+
fs.mkdirSync(desktopDir, { recursive: true });
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
fs.writeFileSync(desktopFilePath, desktopContent);
|
|
358
|
+
|
|
359
|
+
// Set executable permissions
|
|
360
|
+
try {
|
|
361
|
+
execSync(`chmod +x "${desktopFilePath}"`);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
console.error('Could not set executable permissions (may be ignored):', error.message);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Update desktop database
|
|
368
|
+
try {
|
|
369
|
+
execSync('update-desktop-database ~/.local/share/applications');
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.error('Could not update desktop database (may be ignored):', error.message);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return true;
|
|
375
|
+
} catch (error) {
|
|
376
|
+
console.error('Error configuring Linux auto-start:', error);
|
|
377
|
+
console.error('Error message:', error.message);
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Check if auto-start is already configured
|
|
383
|
+
isAutoStartConfigured() {
|
|
384
|
+
try {
|
|
385
|
+
|
|
386
|
+
switch (this.platform) {
|
|
387
|
+
case 'win32':
|
|
388
|
+
try {
|
|
389
|
+
// Check both startup and programs menu shortcuts
|
|
390
|
+
const winPaths = [
|
|
391
|
+
path.join(
|
|
392
|
+
process.env.APPDATA,
|
|
393
|
+
'Microsoft',
|
|
394
|
+
'Windows',
|
|
395
|
+
'Start Menu',
|
|
396
|
+
'Programs',
|
|
397
|
+
'Startup',
|
|
398
|
+
'glassnote.lnk'
|
|
399
|
+
),
|
|
400
|
+
path.join(
|
|
401
|
+
process.env.APPDATA,
|
|
402
|
+
'Microsoft',
|
|
403
|
+
'Windows',
|
|
404
|
+
'Start Menu',
|
|
405
|
+
'Programs',
|
|
406
|
+
'glassnote.lnk'
|
|
407
|
+
)
|
|
408
|
+
];
|
|
409
|
+
|
|
410
|
+
for (const winPath of winPaths) {
|
|
411
|
+
try {
|
|
412
|
+
if (fs.existsSync(winPath)) {
|
|
413
|
+
return true; // Return true if any shortcut exists
|
|
414
|
+
}
|
|
415
|
+
} catch (error) {
|
|
416
|
+
console.error('Cannot check Windows shortcut (access may be restricted):', winPath, error.message);
|
|
417
|
+
// Continue checking other paths
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return false;
|
|
421
|
+
} catch (error) {
|
|
422
|
+
console.error('Cannot check Windows auto-start configuration (access may be restricted):', error.message);
|
|
423
|
+
return false; // Assume not configured if access is restricted
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
case 'darwin':
|
|
427
|
+
try {
|
|
428
|
+
const macPath = path.join(
|
|
429
|
+
process.env.HOME,
|
|
430
|
+
'Library',
|
|
431
|
+
'LaunchAgents',
|
|
432
|
+
'com.glassnote.app.plist'
|
|
433
|
+
);
|
|
434
|
+
const macExists = fs.existsSync(macPath);
|
|
435
|
+
return macExists;
|
|
436
|
+
} catch (error) {
|
|
437
|
+
console.error('Cannot check macOS auto-start configuration (access may be restricted):', error.message);
|
|
438
|
+
return false; // Assume not configured if access is restricted
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
case 'linux':
|
|
442
|
+
try {
|
|
443
|
+
const linuxPath = path.join(
|
|
444
|
+
process.env.HOME,
|
|
445
|
+
'.config',
|
|
446
|
+
'autostart',
|
|
447
|
+
'glassnote.desktop'
|
|
448
|
+
);
|
|
449
|
+
const linuxExists = fs.existsSync(linuxPath);
|
|
450
|
+
return linuxExists;
|
|
451
|
+
} catch (error) {
|
|
452
|
+
console.error('Cannot check Linux auto-start configuration (access may be restricted):', error.message);
|
|
453
|
+
return false; // Assume not configured if access is restricted
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
default:
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
} catch (error) {
|
|
460
|
+
console.error('Error checking auto-start configuration:', error.message);
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Main method to configure auto-start
|
|
466
|
+
configureAutoStart() {
|
|
467
|
+
try {
|
|
468
|
+
let result = false;
|
|
469
|
+
switch (this.platform) {
|
|
470
|
+
case 'win32':
|
|
471
|
+
result = this.setupWindowsAutoStart();
|
|
472
|
+
break;
|
|
473
|
+
|
|
474
|
+
case 'darwin':
|
|
475
|
+
result = this.setupMacAutoStart();
|
|
476
|
+
break;
|
|
477
|
+
|
|
478
|
+
case 'linux':
|
|
479
|
+
result = this.setupLinuxAutoStart();
|
|
480
|
+
break;
|
|
481
|
+
|
|
482
|
+
default:
|
|
483
|
+
result = false;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (!result) {
|
|
487
|
+
console.warn('Auto-start configuration may have failed due to access restrictions, but application will continue running normally');
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return result;
|
|
491
|
+
} catch (error) {
|
|
492
|
+
console.error('Auto-start configuration failed due to error, but application will continue running normally:', error.message);
|
|
493
|
+
return false; // Return false but don't throw error
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Remove auto-start configuration
|
|
498
|
+
removeAutoStart() {
|
|
499
|
+
try {
|
|
500
|
+
switch (this.platform) {
|
|
501
|
+
case 'win32':
|
|
502
|
+
// Remove both startup and programs menu shortcuts
|
|
503
|
+
const winPaths = [
|
|
504
|
+
path.join(
|
|
505
|
+
process.env.APPDATA,
|
|
506
|
+
'Microsoft',
|
|
507
|
+
'Windows',
|
|
508
|
+
'Start Menu',
|
|
509
|
+
'Programs',
|
|
510
|
+
'Startup',
|
|
511
|
+
'glassnote.lnk'
|
|
512
|
+
),
|
|
513
|
+
path.join(
|
|
514
|
+
process.env.APPDATA,
|
|
515
|
+
'Microsoft',
|
|
516
|
+
'Windows',
|
|
517
|
+
'Start Menu',
|
|
518
|
+
'Programs',
|
|
519
|
+
'glassnote.lnk'
|
|
520
|
+
)
|
|
521
|
+
];
|
|
522
|
+
|
|
523
|
+
for (const winPath of winPaths) {
|
|
524
|
+
try {
|
|
525
|
+
if (fs.existsSync(winPath)) {
|
|
526
|
+
fs.unlinkSync(winPath);
|
|
527
|
+
} else {
|
|
528
|
+
}
|
|
529
|
+
} catch (error) {
|
|
530
|
+
console.error('Could not remove Windows shortcut (access may be restricted):', winPath, error.message);
|
|
531
|
+
// Continue without error - removal is optional
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
break;
|
|
535
|
+
|
|
536
|
+
case 'darwin':
|
|
537
|
+
try {
|
|
538
|
+
const macPath = path.join(
|
|
539
|
+
process.env.HOME,
|
|
540
|
+
'Library',
|
|
541
|
+
'LaunchAgents',
|
|
542
|
+
'com.glassnote.app.plist'
|
|
543
|
+
);
|
|
544
|
+
if (fs.existsSync(macPath)) {
|
|
545
|
+
try {
|
|
546
|
+
// Use spawn instead of execSync to avoid blocking
|
|
547
|
+
const unloadProcess = spawn('launchctl', ['unload', macPath], {
|
|
548
|
+
stdio: 'ignore',
|
|
549
|
+
timeout: 5000 // 5 second timeout
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
unloadProcess.on('error', (error) => {
|
|
553
|
+
console.error('Error spawning launchctl unload:', error.message);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
unloadProcess.on('exit', (code) => {
|
|
557
|
+
if (code !== 0) {
|
|
558
|
+
console.warn('launchctl unload exited with code:', code, '(may not be loaded)');
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
} catch (error) {
|
|
562
|
+
console.error('Could not unload plist:', error.message);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Remove the plist file regardless of launchctl status
|
|
566
|
+
try {
|
|
567
|
+
fs.unlinkSync(macPath);
|
|
568
|
+
} catch (error) {
|
|
569
|
+
console.error('Could not remove plist file:', error.message);
|
|
570
|
+
}
|
|
571
|
+
} else {
|
|
572
|
+
console.warn('macOS auto-start plist not found');
|
|
573
|
+
}
|
|
574
|
+
} catch (error) {
|
|
575
|
+
console.error('Could not remove macOS auto-start configuration (access may be restricted):', error.message);
|
|
576
|
+
// Continue without error - removal is optional
|
|
577
|
+
}
|
|
578
|
+
break;
|
|
579
|
+
|
|
580
|
+
case 'linux':
|
|
581
|
+
try {
|
|
582
|
+
const linuxPaths = [
|
|
583
|
+
path.join(process.env.HOME, '.local', 'share', 'applications', 'glassnote.desktop'),
|
|
584
|
+
path.join(process.env.HOME, '.config', 'autostart', 'glassnote.desktop')
|
|
585
|
+
];
|
|
586
|
+
for (const linuxPath of linuxPaths) {
|
|
587
|
+
if (fs.existsSync(linuxPath)) {
|
|
588
|
+
fs.unlinkSync(linuxPath);
|
|
589
|
+
} else {
|
|
590
|
+
console.warn('Linux desktop file not found:', linuxPath);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
} catch (error) {
|
|
594
|
+
console.error('Could not remove Linux auto-start configuration (access may be restricted):', error.message);
|
|
595
|
+
// Continue without error - removal is optional
|
|
596
|
+
}
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return true; // Always return true since removal is optional
|
|
601
|
+
} catch (error) {
|
|
602
|
+
console.error('Error removing auto-start configuration:', error.message);
|
|
603
|
+
return true; // Return true even if there's an error - removal is optional
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
module.exports = AutoStart;
|