Skip to content

Commit

Permalink
feat: execution feature
Browse files Browse the repository at this point in the history
  • Loading branch information
exKAZUu committed Jun 17, 2024
1 parent 065a201 commit 3524f1f
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 64 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
# exercode-viewer
:100: A viewer for scoring learner's submissions on Exercode.

## Requirements

- Node.js
- Yarn
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"version": "0.0.0-semantically-released",
"description": "A viewer for scoring learner's submissions on Exercode.",
"license": "UNLICENSED",
"type": "module",
"main": "src/main.mjs",
"type": "commonjs",
"main": "src/main.cjs",
"scripts": {
"cleanup": "yarn format && yarn lint-fix",
"format": "sort-package-json && yarn prettify",
Expand Down
99 changes: 99 additions & 0 deletions src/main.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
const child_process = require('node:child_process');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');

const { app, BrowserWindow, ipcMain } = require('electron');
const fflate = require('fflate');

const createWindow = () => {
const { screen } = require('electron');
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
const mainWindow = new BrowserWindow({
width: width,
height: height,
webPreferences: {
preload: path.join(__dirname, 'preload.cjs'),
devTools: false,
},
});
void mainWindow.loadURL('https://exercode.willbooster.com/');
mainWindow.webContents.openDevTools();

mainWindow.webContents.on('did-finish-load', () => {
void mainWindow.webContents.executeJavaScript(`
setInterval(function() {
const chakraStackElem = document.querySelector('.chakra-stack');
if (chakraStackElem && !document.querySelector('#back')) {
const backButton = document.createElement('button');
backButton.textContent = 'Back';
backButton.id = 'back';
backButton.onclick = () => window.history.back();
const firstChild = chakraStackElem.firstChild;
chakraStackElem.insertBefore(backButton, firstChild);
const forwardButton = document.createElement('button');
forwardButton.textContent = 'Forward';
forwardButton.onclick = () => window.history.forward();
chakraStackElem.insertBefore(forwardButton, firstChild);
}
const downloadElem = document.querySelector('#download');
const executionCommandElem = document.querySelector('#execution');
if (downloadElem && executionCommandElem) {
const zipUrl = downloadElem.href;
downloadElem.onclick = async () => {
const response = await fetch(zipUrl);
const buffer = await response.arrayBuffer();
window.electron.execute(zipUrl, buffer, executionCommandElem.textContent);
};
downloadElem.removeAttribute('href');
downloadElem.id = 'execute';
downloadElem.textContent = 'Execute on Your PC';
}
}, 100);
`);
});
};

app.whenReady().then(() => {
ipcMain.on('execute', async (_, zipUrl, buffer, executionCommand) => {
const tempDir = path.join(os.tmpdir(), 'exercode');
fs.rmSync(tempDir, { force: true, recursive: true });

const uint8Array = new Uint8Array(buffer);
const files = fflate.unzipSync(uint8Array);
for (const [filename, fileData] of Object.entries(files)) {
const filePath = path.join(tempDir, filename);
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, fileData);
}

const dirName = new URL(zipUrl).pathname.split('/').at(-1);
const dirPath = path.join(tempDir, dirName);

let command;
let args;
executionCommand = executionCommand.split(' && ').slice(2).join(' && ');
switch (os.platform()) {
case 'win32': // Windows
command = 'start';
args = ['cmd.exe', '/K', `cd ${dirPath} && ${executionCommand}`];
break;
case 'darwin': // macOS
command = 'osascript';
args = ['-e', `tell app "Terminal" to do script "cd ${dirPath} && ${executionCommand.replaceAll('"', '\'')}"`, '-e', 'tell app "Terminal" to activate'];
break;
default: // Linux
command = 'gnome-terminal';
args = ['--', 'bash', '-c', `cd ${dirPath} && ${executionCommand}`];
break;
}
child_process.spawn(command, args, { shell: false });
});
createWindow();
});

app.on('window-all-closed', () => {
app.quit();
});
61 changes: 0 additions & 61 deletions src/main.mjs

This file was deleted.

2 changes: 1 addition & 1 deletion src/preload.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electron', {
execute: (url) => ipcRenderer.send('execute', url),
execute: (zipUrl, buffer, executionCommand) => ipcRenderer.send('execute', zipUrl, buffer, executionCommand),
});

0 comments on commit 3524f1f

Please sign in to comment.