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),
});
Gestire i Link nel Renderer
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);
}
});