Skip to content

Fix about page and split index.js into backend helper files #113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ This project is sponsored by Arduino, based on original work by [Murilo Polese](

## Technical overview

Arduino Lab for MicroPython is an [Electron](https://www.electronjs.org/) app that has its main purpose to communicate over serial with a microprocessor running [MicroPython](https://micropython.org/). All Electron code is at `/index.js`.
Arduino Lab for MicroPython is an [Electron](https://www.electronjs.org/) app that has its main purpose to communicate over serial with a microprocessor running [MicroPython](https://micropython.org/). The Electron code is at `/index.js` and inside the folder `/backend`.

All operations over serial are abstracted and packaged on `/micropython.js` which is an attempt of porting `pyboard.py`. The port has its [own repository](https://github.com/arduino/micropython.js) but for the sake of simplicity and transparency, `micropython.js` is committed as source code.
All operations over serial are abstracted and packaged on `micropython.js` which is an attempt of porting `pyboard.py`. The module has its [own repository](https://github.com/arduino/micropython.js) with documentation and examples of usage.

The User Interface (UI) source code stays inside `/ui` folder and is completely independent of the Electron code.

Expand All @@ -49,6 +49,7 @@ At the root of the repository you will find:
- `/build_resources`: Icons and other assets used during the build process.
- `/ui`: Available user interfaces.
- `/index.js`: Main Electron code.
- `/backend`: Electron helpers.
- `/preload.js`: Creates Disk, Serial and Window APIs on Electron's main process and exposes it to Electron's renderer process (context bridge).

## User interface
Expand Down
62 changes: 62 additions & 0 deletions backend/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const { dialog } = require('electron')
const fs = require('fs')
const path = require('path')

async function openFolderDialog(win) {
// https://stackoverflow.com/questions/46027287/electron-open-folder-dialog
let dir = await dialog.showOpenDialog(win, { properties: [ 'openDirectory' ] })
return dir.filePaths[0] || null
}

function listFolder(folder) {
files = fs.readdirSync(path.resolve(folder))
// Filter out directories
files = files.filter(f => {
let filePath = path.resolve(folder, f)
return !fs.lstatSync(filePath).isDirectory()
})
return files
}

function ilistFolder(folder) {
let files = fs.readdirSync(path.resolve(folder))
files = files.filter(f => {
let filePath = path.resolve(folder, f)
return !fs.lstatSync(filePath).isSymbolicLink()
})
files = files.map(f => {
let filePath = path.resolve(folder, f)
return {
path: f,
type: fs.lstatSync(filePath).isDirectory() ? 'folder' : 'file'
}
})
// Filter out dot files
files = files.filter(f => f.path.indexOf('.') !== 0)
return files
}

function getAllFiles(dirPath, arrayOfFiles) {
// https://coderrocketfuel.com/article/recursively-list-all-the-files-in-a-directory-using-node-js
files = ilistFolder(dirPath)
arrayOfFiles = arrayOfFiles || []
files.forEach(function(file) {
const p = path.join(dirPath, file.path)
const stat = fs.statSync(p)
arrayOfFiles.push({
path: p,
type: stat.isDirectory() ? 'folder' : 'file'
})
if (stat.isDirectory()) {
arrayOfFiles = getAllFiles(p, arrayOfFiles)
}
})
return arrayOfFiles
}

module.exports = {
openFolderDialog,
listFolder,
ilistFolder,
getAllFiles
}
110 changes: 110 additions & 0 deletions backend/ipc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
const fs = require('fs')
const {
openFolderDialog,
listFolder,
ilistFolder,
getAllFiles
} = require('./helpers.js')

module.exports = function registerIPCHandlers(win, ipcMain) {
ipcMain.handle('open-folder', async (event) => {
console.log('ipcMain', 'open-folder')
const folder = await openFolderDialog(win)
let files = []
if (folder) {
files = listFolder(folder)
}
return { folder, files }
})

ipcMain.handle('list-files', async (event, folder) => {
console.log('ipcMain', 'list-files', folder)
if (!folder) return []
return listFolder(folder)
})

ipcMain.handle('ilist-files', async (event, folder) => {
console.log('ipcMain', 'ilist-files', folder)
if (!folder) return []
return ilistFolder(folder)
})

ipcMain.handle('ilist-all-files', (event, folder) => {
console.log('ipcMain', 'ilist-all-files', folder)
if (!folder) return []
return getAllFiles(folder)
})

ipcMain.handle('load-file', (event, filePath) => {
console.log('ipcMain', 'load-file', filePath)
let content = fs.readFileSync(filePath)
return content
})

ipcMain.handle('save-file', (event, filePath, content) => {
console.log('ipcMain', 'save-file', filePath, content)
fs.writeFileSync(filePath, content, 'utf8')
return true
})

ipcMain.handle('update-folder', (event, folder) => {
console.log('ipcMain', 'update-folder', folder)
let files = fs.readdirSync(path.resolve(folder))
// Filter out directories
files = files.filter(f => {
let filePath = path.resolve(folder, f)
return !fs.lstatSync(filePath).isDirectory()
})
return { folder, files }
})

ipcMain.handle('remove-file', (event, filePath) => {
console.log('ipcMain', 'remove-file', filePath)
fs.unlinkSync(filePath)
return true
})

ipcMain.handle('rename-file', (event, filePath, newFilePath) => {
console.log('ipcMain', 'rename-file', filePath, newFilePath)
fs.renameSync(filePath, newFilePath)
return true
})

ipcMain.handle('create-folder', (event, folderPath) => {
console.log('ipcMain', 'create-folder', folderPath)
try {
fs.mkdirSync(folderPath, { recursive: true })
} catch(e) {
console.log('error', e)
return false
}
return true
})

ipcMain.handle('remove-folder', (event, folderPath) => {
console.log('ipcMain', 'remove-folder', folderPath)
fs.rmdirSync(folderPath, { recursive: true, force: true })
return true
})

ipcMain.handle('file-exists', (event, filePath) => {
console.log('ipcMain', 'file-exists', filePath)
try {
fs.accessSync(filePath, fs.constants.F_OK)
return true
} catch(err) {
return false
}
})
// WINDOW MANAGEMENT

ipcMain.handle('set-window-size', (event, minWidth, minHeight) => {
console.log('ipcMain', 'set-window-size', minWidth, minHeight)
if (!win) {
console.log('No window defined')
return false
}

win.setMinimumSize(minWidth, minHeight)
})
}
143 changes: 143 additions & 0 deletions backend/menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
const { app, Menu } = require('electron')
const path = require('path')
const openAboutWindow = require('about-window').default

module.exports = function registerMenu(win) {
const isMac = process.platform === 'darwin'
const isDev = !app.isPackaged
const template = [
...(isMac ? [{
label: app.name,
submenu: [
{ role: 'about'},
{ type: 'separator' },
{ role: 'services' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' }
]
}] : []),
{
label: 'File',
submenu: [
isMac ? { role: 'close' } : { role: 'quit' }
]
},
{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
...(isMac ? [
{ role: 'pasteAndMatchStyle' },
{ role: 'selectAll' },
{ type: 'separator' },
{
label: 'Speech',
submenu: [
{ role: 'startSpeaking' },
{ role: 'stopSpeaking' }
]
}
] : [
{ type: 'separator' },
{ role: 'selectAll' }
])
]
},
{
label: 'View',
submenu: [
{ role: 'reload' },
{ type: 'separator' },
{ role: 'resetZoom' },
{ role: 'zoomIn' },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' },
...(isDev ? [
{ type: 'separator' },
{ role: 'toggleDevTools' },
]:[
])
]
},
{
label: 'Window',
submenu: [
{ role: 'minimize' },
{ role: 'zoom' },
...(isMac ? [
{ type: 'separator' },
{ role: 'front' },
{ type: 'separator' },
{ role: 'window' }
] : [
{ role: 'close' }
])
]
},
{
role: 'help',
submenu: [
{
label: 'Learn More',
click: async () => {
const { shell } = require('electron')
await shell.openExternal('https://github.com/arduino/lab-micropython-editor')
}
},
{
label: 'Report an issue',
click: async () => {
const { shell } = require('electron')
await shell.openExternal('https://github.com/arduino/lab-micropython-editor/issues')
}
},
{
label:'Info about this app',
click: () => {
openAboutWindow({
icon_path: path.resolve(__dirname, '../ui/arduino/media/about_image.png'),
css_path: path.resolve(__dirname, '../ui/arduino/views/about.css'),
// about_page_dir: path.resolve(__dirname, '../ui/arduino/views/'),
copyright: '© Arduino SA 2022',
package_json_dir: path.resolve(__dirname, '..'),
bug_report_url: "https://github.com/arduino/lab-micropython-editor/issues",
bug_link_text: "report an issue",
homepage: "https://labs.arduino.cc",
use_version_info: false,
win_options: {
parent: win,
modal: true,
},
show_close_button: 'Close',
})
}
},
]
}
]

const menu = Menu.buildFromTemplate(template)

app.setAboutPanelOptions({
applicationName: app.name,
applicationVersion: app.getVersion(),
copyright: app.copyright,
credits: '(See "Info about this app" in the Help menu)',
authors: ['Arduino'],
website: 'https://arduino.cc',
iconPath: path.join(__dirname, '../assets/image.png'),
})

Menu.setApplicationMenu(menu)

}
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy