00
:
00
:
00
:
00
Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Shell e Link Esterni

Il modulo shell di Electron fornisce funzioni per interagire con il sistema operativo: aprire URL nel browser predefinito, mostrare file nel file manager, spostare file nel cestino e altro.

Aprire URL nel Browser Predefinito

const { shell } = require('electron');

// Apri un URL nel browser predefinito
shell.openExternal('https://www.electronjs.org');

// Apri un indirizzo email
shell.openExternal('mailto:support@example.com');

// Apri un link tel:
shell.openExternal('tel:+39123456789');

Sicurezza con openExternal

Importante: Non passare mai URL non validati a openExternal. Un URL malevolo potrebbe eseguire comandi sul sistema:

// main.js
ipcMain.handle('shell:openExternal', async (_event, url) => {
  // Valida l'URL prima di aprirlo
  const parsed = new URL(url);
  const allowedProtocols = ['https:', 'http:', 'mailto:'];

  if (allowedProtocols.includes(parsed.protocol)) {
    await shell.openExternal(url);
    return true;
  }

  return false;
});

Mostrare File nel File Manager

// Apri il file manager con il file selezionato
shell.showItemInFolder('/percorso/al/file.txt');
// Su Windows: apre Explorer con il file evidenziato
// Su macOS: apre Finder con il file selezionato
// Su Linux: apre il file manager predefinito

Aprire File con l’Applicazione Predefinita

// Apri il file con l'applicazione associata
shell.openPath('/percorso/al/documento.pdf');
// Apre il PDF con il lettore PDF predefinito

shell.openPath('/percorso/alla/immagine.png');
// Apre l'immagine con il visualizzatore predefinito

Spostare File nel Cestino

// Sposta un file nel cestino (reversibile)
const success = await shell.trashItem('/percorso/al/file.txt');
if (success) {
  console.log('File spostato nel cestino');
}

Emettere un Suono di Sistema

// Emetti il suono di notifica predefinito (solo macOS)
shell.beep();

Integrazione Completa con IPC

// main.js
const { shell, ipcMain } = require('electron');

ipcMain.handle('shell:openExternal', async (_event, url) => {
  const parsed = new URL(url);
  if (['https:', 'http:', 'mailto:'].includes(parsed.protocol)) {
    await shell.openExternal(url);
    return true;
  }
  return false;
});

ipcMain.handle('shell:showInFolder', (_event, filePath) => {
  shell.showItemInFolder(filePath);
});

ipcMain.handle('shell:openFile', async (_event, filePath) => {
  const result = await shell.openPath(filePath);
  return result === '' ? true : result; // '' = successo
});

ipcMain.handle('shell:moveToTrash', async (_event, filePath) => {
  return await shell.trashItem(filePath);
});
// preload.js
contextBridge.exposeInMainWorld('shell', {
  openExternal: (url) => ipcRenderer.invoke('shell:openExternal', url),
  showInFolder: (path) => ipcRenderer.invoke('shell:showInFolder', path),
  openFile: (path) => ipcRenderer.invoke('shell:openFile', path),
  moveToTrash: (path) => ipcRenderer.invoke('shell:moveToTrash', path),
});

Un pattern comune è intercettare tutti i link e aprirli nel browser esterno:

// renderer.js
document.addEventListener('click', (event) => {
  const link = event.target.closest('a[href]');
  if (link) {
    const url = link.getAttribute('href');
    // Apri link esterni nel browser, non nella finestra Electron
    if (url.startsWith('http://') || url.startsWith('https://')) {
      event.preventDefault();
      window.shell.openExternal(url);
    }
  }
});

Oppure puoi gestirlo dal Main Process intercettando la navigazione:

// main.js
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
  // Apri nuove finestre nel browser esterno
  if (url.startsWith('https://') || url.startsWith('http://')) {
    shell.openExternal(url);
    return { action: 'deny' }; // Non aprire una nuova finestra Electron
  }
  return { action: 'allow' };
});

// Impedisci la navigazione della finestra principale verso URL esterni
mainWindow.webContents.on('will-navigate', (event, url) => {
  if (!url.startsWith('file://')) {
    event.preventDefault();
    shell.openExternal(url);
  }
});