import path from 'node:path' import { app, BrowserWindow, dialog, ipcMain } from 'electron'; import isDev from 'electron-is-dev'; import * as colorette from 'colorette'; import * as fs from 'node:fs' import * as os from 'node:os' import { exec, spawn } from 'node:child_process'; function createWindow() { const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, contextIsolation: false }, icon: './icons/icon.png', show: false, autoHideMenuBar: true, title: 'sern', }); if (isDev) { mainWindow.loadURL('http://localhost:5173'); } else { mainWindow.loadFile(path.join(__dirname, '../build/index.html')); } mainWindow.on('ready-to-show', () => { mainWindow.show(); }); mainWindow.on('page-title-updated', function (e) { e.preventDefault(); }); ipcMain.on('openFolder', (event, _arg) => { dialog .showOpenDialog({ properties: ['openDirectory'], }) .then((result) => { event.reply('folderData', result.filePaths); }) .catch((error) => { console.error(error); event.reply('folderData', []); }); }); ipcMain.on('submitForm', async (event, data) => { const fileName = createRandomFileName('txt') // Process the submitted data here writeLineToLogAndConsole(`${colorette.green('✓')} Received sern init submit form data:`, fileName); writeLineToLogAndConsole(JSON.stringify(data), fileName) writeLineToLogAndConsole(`${colorette.cyan('🛈')} Current OS: ${currentOS}`, fileName); let packageManagerCommand: string switch (data.chosenPackageManager) { case 'npm': packageManagerCommand = `npm create @sern/bot@latest -- --template=${data.chosenTemplate} --name="${data.projectName}" --install=npm` break; case 'yarn': packageManagerCommand = `npm create @sern/bot@latest -- --template=${data.chosenTemplate} --name="${data.projectName}" --install=yarn` break; case 'pnpm': packageManagerCommand = `npm create @sern/bot@latest -- --template=${data.chosenTemplate} --name="${data.projectName}" --install=pnpm` break; default: packageManagerCommand = `npm create @sern/bot@latest -- --template=${data.chosenTemplate} --name="${data.projectName}"` break; } let commandToRun: string; switch (currentOS) { case 'linux': commandToRun = `cd ${data.selectedPath};${packageManagerCommand}` break; case 'windows': commandToRun = `cd /D ${data.selectedPath} && ${packageManagerCommand}` break; case 'macOS': commandToRun = `cd ${data.selectedPath};${packageManagerCommand}` break; default: // defaulting for linux (most probable command syntax) commandToRun = `cd ${data.selectedPath};${packageManagerCommand}` break; } writeLineToLogAndConsole(`${colorette.cyan('🛈')} About to execute command: ${commandToRun}`, fileName); const cmd = exec(commandToRun) cmd.stdout!.on('data', (data) => { writeLineToLogAndConsole(`${colorette.cyan('🛈')} ${data}`, fileName); }); cmd.stderr!.on('data', (data) => { writeLineToLogAndConsole(`${colorette.red('×')} stderr: ${data}`, fileName); }); cmd.on('close', (code) => { writeLineToLogAndConsole(`${colorette.cyan('🛈')} Command exited with status code ${code}. Now notifying frontend...`, fileName); event.reply('submitForm', { exitCode: code, logFileName: fileName }) }); }); ipcMain.on('openTxtFile', (event, args) => { console.log('heya', args) openTempTextFile(args) event.reply('openTxtFile') }) } app.whenReady().then(createWindow); app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } }); let currentOS: string switch (process.platform) { case 'linux': currentOS = 'linux' break; case 'win32': currentOS = 'windows' break; case 'darwin': currentOS = 'macOS' break; default: // defaulting for linux (most probable command syntax) currentOS = 'linux' break; } const asciiart = ` .:-=-:. .:=+++++++++=-. :-=+++++++++++++++++=-: =++++++++++++++++++++=:. =+++++++++++++++=-: =++++++++++++-. ###### ######## ######## ## ## ###### ## ## #### =++++++++++++*+=:. ## ## ## ## ## ### ## ## ## ## ## ## =++++++++++++******=-. ## ## ## ## #### ## ## ## ## ## :=+++++++++++**********+- ###### ###### ######## ## ## ## ## #### ## ## ## .:-+++++++********###* ## ## ## ## ## #### ## ## ## ## ## :-=++***########* ## ## ## ## ## ## ### ## ## ## ## ## .-*###########* ###### ######## ## ## ## ## ###### ####### #### :=+###############* .-=*###################* .:=*#################*=-. :=+#########+=: ` console.log(asciiart) // from now on will be functions that are used in the above code function createRandomFileName(extension: string) { return `sern-gui-${randomstring(8)}.${extension}` } function writeLineToLogAndConsole(text: string, logfilename: string) { console.log(text) fs.appendFileSync(`${os.tmpdir()}/${logfilename}`, `\n${text}`, { encoding: 'utf-8' }) } function openTempTextFile(filename: string) { const completeFilename = `${os.tmpdir()}/${filename}` switch (currentOS) { case 'macOS': return spawn('open', [completeFilename]) case 'windows': return spawn('notepad', [completeFilename]) case 'linux': return spawn('xdg-open', [completeFilename]) } } function randomstring(length: number) { let result = ''; const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; const charactersLength = characters.length; let counter = 0; while (counter < length) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); counter += 1; } return result; }