@glassnote/client 2.4.2 → 2.4.3

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.
Files changed (3) hide show
  1. package/bin/glassnote.js +118 -0
  2. package/main.js +18 -0
  3. package/package.json +1 -1
package/bin/glassnote.js CHANGED
@@ -29,6 +29,13 @@ const MARK_B = '# <<< glassnote <<<';
29
29
  function npmEnv() {
30
30
  return Object.assign({}, process.env, {
31
31
  PATH: `${mode.BIN_DIR}${path.delimiter}${process.env.PATH || ''}`,
32
+ // Cache propia, bajo ~/.glassnote. Un ~/.npm con archivos de root —herencia de
33
+ // cualquier `sudo npm` viejo— hace que npm muera con EACCES antes de bajar nada,
34
+ // y el usuario tiene que andar con chown. Se pisa a propósito el npm_config_cache
35
+ // heredado: cuando esto corre bajo npx, npm nos pasa el suyo en el entorno, que es
36
+ // justo el que puede estar roto. Para forzar otra: GLASSNOTE_NPM_CACHE.
37
+ npm_config_cache:
38
+ process.env.GLASSNOTE_NPM_CACHE || path.join(mode.HOME_DIR, 'npm-cache'),
32
39
  });
33
40
  }
34
41
 
@@ -284,6 +291,108 @@ function toSpec(arg) {
284
291
  return looksLikeSpec ? arg : `${mode.PKG_NAME}@${arg}`;
285
292
  }
286
293
 
294
+ const MAC_APP_NAME = 'Glassnote.app';
295
+
296
+ // Instalado por npm queda un comando, no una app: cerrada desde la bandeja no hay forma
297
+ // de volver a abrirla desde el Finder, Spotlight o Launchpad. Esto crea un .app minimo
298
+ // —un envoltorio que llama al CLI instalado— para tener de donde relanzarla.
299
+ function macAppDirs() {
300
+ return [
301
+ path.join('/Applications', MAC_APP_NAME),
302
+ path.join(os.homedir(), 'Applications', MAC_APP_NAME),
303
+ ];
304
+ }
305
+
306
+ function instalarAppMac() {
307
+ if (process.platform !== 'darwin') return null;
308
+
309
+ // /Applications pide ser admin; si no se puede escribir, la de usuario sirve igual y
310
+ // Spotlight la indexa lo mismo. Nada de sudo: la instalacion entera va sin root.
311
+ const [global, local] = macAppDirs();
312
+ let appDir = local;
313
+ try {
314
+ fs.accessSync('/Applications', fs.constants.W_OK);
315
+ appDir = global;
316
+ } catch (error) {
317
+ /* sin permiso: se usa ~/Applications */
318
+ }
319
+
320
+ const contents = path.join(appDir, 'Contents');
321
+ const cli = path.join(mode.BIN_DIR, mode.BIN_NAME);
322
+ try {
323
+ fs.mkdirSync(path.join(contents, 'MacOS'), { recursive: true });
324
+ fs.mkdirSync(path.join(contents, 'Resources'), { recursive: true });
325
+
326
+ // El ejecutable del bundle solo delega en el CLI, que ya sabe encontrar electron y
327
+ // arrancar desprendido. Asi el .app no se queda viejo cuando el cliente se actualiza.
328
+ const exe = path.join(contents, 'MacOS', 'Glassnote');
329
+ fs.writeFileSync(
330
+ exe,
331
+ [
332
+ '#!/bin/sh',
333
+ "# Generado por 'glassnote install': se reescribe en cada instalacion.",
334
+ `exec "${cli}" start`,
335
+ '',
336
+ ].join('\n')
337
+ );
338
+ fs.chmodSync(exe, 0o755);
339
+
340
+ // El icono del bundle tiene que ser .icns; sips viene con macOS. Si falla, el .app
341
+ // queda con el icono generico: molesta, pero no impide relanzar.
342
+ const png = path.join(mode.INSTALLED_PACKAGE_DIR, 'images', 'icon-512.png');
343
+ if (fs.existsSync(png)) {
344
+ spawnSync(
345
+ 'sips',
346
+ ['-s', 'format', 'icns', png, '--out', path.join(contents, 'Resources', 'glassnote.icns')],
347
+ { stdio: 'ignore' }
348
+ );
349
+ }
350
+
351
+ // LSUIElement, igual que la app: el envoltorio tampoco tiene que dejar icono en el Dock.
352
+ const version = installedVersion() || '0.0.0';
353
+ fs.writeFileSync(
354
+ path.join(contents, 'Info.plist'),
355
+ `<?xml version="1.0" encoding="UTF-8"?>
356
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
357
+ <plist version="1.0">
358
+ <dict>
359
+ <key>CFBundleName</key><string>Glassnote</string>
360
+ <key>CFBundleDisplayName</key><string>Glassnote</string>
361
+ <key>CFBundleIdentifier</key><string>com.glassnote.launcher</string>
362
+ <key>CFBundleExecutable</key><string>Glassnote</string>
363
+ <key>CFBundleIconFile</key><string>glassnote</string>
364
+ <key>CFBundlePackageType</key><string>APPL</string>
365
+ <key>CFBundleShortVersionString</key><string>${version}</string>
366
+ <key>CFBundleVersion</key><string>${version}</string>
367
+ <key>LSUIElement</key><true/>
368
+ </dict>
369
+ </plist>
370
+ `
371
+ );
372
+
373
+ // El Finder cachea el bundle por fecha: sin esto puede seguir mostrando el icono viejo.
374
+ spawnSync('touch', [appDir], { stdio: 'ignore' });
375
+ return appDir;
376
+ } catch (error) {
377
+ // Que no se pueda crear la app no rompe la instalacion: el comando ya funciona.
378
+ log(` (no pude crear ${MAC_APP_NAME}: ${error.message})`);
379
+ return null;
380
+ }
381
+ }
382
+
383
+ function borrarAppMac() {
384
+ if (process.platform !== 'darwin') return;
385
+ for (const dir of macAppDirs()) {
386
+ if (!fs.existsSync(dir)) continue;
387
+ try {
388
+ step(`borrando ${dir}`);
389
+ fs.rmSync(dir, { recursive: true, force: true });
390
+ } catch (error) {
391
+ log(` (no pude borrar ${dir}: ${error.message})`);
392
+ }
393
+ }
394
+ }
395
+
287
396
  function cmdInstall(args) {
288
397
  const version = args.find((a) => !a.startsWith('-')) || 'latest';
289
398
  const noPath = args.includes('--no-path');
@@ -323,6 +432,9 @@ function cmdInstall(args) {
323
432
  }
324
433
  }
325
434
 
435
+ const appMac = instalarAppMac();
436
+ if (appMac) step(`app creada en ${appMac} (para relanzarlo desde el Finder)`);
437
+
326
438
  let pathInfo = { touched: [], line: '' };
327
439
  if (!noPath) {
328
440
  pathInfo = IS_WIN ? addToPathWindows() : addToPathUnix();
@@ -399,6 +511,10 @@ function cmdUpdate(args) {
399
511
  }
400
512
 
401
513
  olvidarFalloDeUpdate();
514
+ // Se rehace en cada update: quien ya tenia el cliente instalado de antes no paso
515
+ // por el install, y el .app tiene que aparecerle igual (y con la version al dia).
516
+ const appMac = instalarAppMac();
517
+ if (appMac) step(`app actualizada en ${appMac}`);
402
518
  log(`Actualizado a v${installedVersion()}.`);
403
519
  if (relaunch) {
404
520
  launch({ detach: true });
@@ -426,6 +542,8 @@ function cmdUninstall() {
426
542
  step('el PATH de usuario ya no tenía la entrada');
427
543
  }
428
544
 
545
+ borrarAppMac();
546
+
429
547
  if (fs.existsSync(mode.HOME_DIR)) {
430
548
  step(`borrando ${mode.HOME_DIR}`);
431
549
  fs.rmSync(mode.HOME_DIR, { recursive: true, force: true });
package/main.js CHANGED
@@ -4,6 +4,7 @@ const {
4
4
  Notification,
5
5
  Tray,
6
6
  Menu,
7
+ nativeImage,
7
8
  screen,
8
9
  } = require('electron');
9
10
  const packageJson = require('./package.json');
@@ -121,6 +122,23 @@ cleanupOldLogs();
121
122
  createTailUtility(logsDir);
122
123
 
123
124
  app.whenReady().then(() => {
125
+ // macOS: esto es una app de bandeja, se maneja desde la barra de menu. Instalada por
126
+ // npm no hay .app propio, asi que el Dock mostraba el icono por defecto de Electron y
127
+ // una entrada que no sirve para nada. Se saca del Dock, pero antes se le pone el icono
128
+ // de glassnote: si algo lo vuelve a mostrar (un dialogo modal lo hace), que al menos
129
+ // no aparezca como "Electron".
130
+ if (process.platform === 'darwin' && app.dock) {
131
+ try {
132
+ const dockIcon = nativeImage.createFromPath(
133
+ path.join(__dirname, 'images', 'icon-512.png')
134
+ );
135
+ if (!dockIcon.isEmpty()) app.dock.setIcon(dockIcon);
136
+ } catch (error) {
137
+ console.warn('[dock] no se pudo poner el icono:', error.message);
138
+ }
139
+ app.dock.hide();
140
+ }
141
+
124
142
  // Configure auto-start - this will handle shortcut creation
125
143
  //
126
144
  // Corriendo por `npx` no se registra nada: la app vive en la caché de npx, que se
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassnote/client",
3
- "version": "2.4.2",
3
+ "version": "2.4.3",
4
4
  "description": "GlassNote — cliente de escritorio de notas superpuestas. Se instala con npx, sin instaladores nativos ni firmas.",
5
5
  "main": "main.js",
6
6
  "publishConfig": {