Skip to content

Commit 168dcaf

Browse files
authored
Merge pull request #7 from arduino/develop
Merging develop to main
2 parents f5b7c16 + 1e019d1 commit 168dcaf

File tree

500 files changed

+8006
-3074
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

500 files changed

+8006
-3074
lines changed

README.md

+78-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,80 @@
1-
# Arduino MicroPython Lab
1+
# Arduino Lab for MicroPython
22

3-
A lightweight editor for MicroPython supporting connection with a board, code upload, file transfer, interactive REPL shell.
3+
Arduino Lab for MicroPython is an Integrated Development Environment (IDE) for MicroPython.
4+
5+
## Features
6+
- MicroPython's Read Eval Print Loop (REPL)
7+
- Enter paste mode
8+
- Enter raw repl
9+
- Software reset
10+
- File system management (Disk and MicroPython File System)
11+
- Create
12+
- Rename
13+
- Remove
14+
- Upload
15+
- Download
16+
- Text editor with Python syntax highlight
17+
- Code execution controls
18+
- Run what's on text editor
19+
- Stop (keyboard interrupt)
20+
- Soft reset
21+
22+
## Technical
23+
24+
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`.
25+
26+
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/murilopolese/micropython.js) but for the sake of simplicity and transparency, `micropython.js` is committed as source code.
27+
28+
The User Interface (UI) source code stays inside `/ui` folder and is completely independent of the Electron code.
29+
30+
The communication between interface and Electron app is accomplished by using the methods and events specified by `/preload.js`.
31+
32+
## Folder structure
33+
34+
At the root of the repository you will find:
35+
36+
- `/.github`: Github's workflow configuration.
37+
- `/build_resources`: Icons and other assets used during the build process.
38+
- `/scripts`: Scripts executed during the build process.
39+
- `/ui`: Available user interfaces.
40+
- `/index.js`: Main Electron code.
41+
- `/micropython.js`: Serial connection abstraction.
42+
- `/preload.js`: Creates Disk and Serial APIs on Electron's main process and exposes it to Electron's renderer process (context bridge).
43+
44+
## Arduino UI
45+
46+
Default UI is a [choo-choo](https://github.com/choojs/choo) app. It has pre-built dependencies so no build process is required for the interface.
47+
48+
The dependencies and source code are included manually in the `/ui/arduino/index.html` file.
49+
50+
The app is a standard [choo-choo](https://github.com/choojs/choo) app and it has:
51+
52+
- `/ui/arduino/app.js`: A router deciding which view to load.
53+
- `/ui/arduino/components`: HTML templates and components.
54+
- `/ui/arduino/store.js`: A "store" that handles events emitted by the views, change the app state and orchestrate re-rendering.
55+
- `/ui/arduino/libs`: Prebuilt dependencies.
56+
57+
It can be useful to learn more about [Choo](https://github.com/choojs/choo) or the [Elm Architecture](https://guide.elm-lang.org/architecture/).
58+
59+
## Disk and Serial API
60+
61+
In order for the UI code to be independent of Electron code, there is an API defined at `/preload.js` that describes all the allowed operations.
62+
63+
There are 2 main operation "channels": Serial communication and local Filesystem operations. Both channels offer methods that always return promises and are used mostly through [`async`/`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function).
64+
65+
While the serial communication is mediated by `/micropython.js`, the local filesystem operations are done through Electron's `ipcRenderer` calls. The handlers for these calls are defined at `/index.js`
66+
67+
## Running Arduino Lab for MicroPython from source code
68+
69+
1. Clone this repository: `git clone https://github.com/arduino/MicroPython_Lab.git`
70+
2. Navigate to repository folder: `cd MicroPython_Lab`
71+
3. Install dependencies: `npm install`
72+
4. Run dev mode: `npm run dev`
73+
74+
Some changes on the Electron code will require reopening the app but all UI changes will only require refreshing the window (ctrl-r/cmd-r).
75+
76+
77+
## Trademarks
78+
79+
"Python" and the Python Logo are trademarks of the Python Software Foundation.
480

build_resources/icon.icns

3.01 KB
Binary file not shown.

build_resources/icon.png

4.34 KB
Loading

index.js

+164-8
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,45 @@
1-
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
1+
const { app, BrowserWindow, Menu, ipcMain, dialog } = require('electron')
22
const path = require('path')
33
const fs = require('fs')
4+
const join = require('path').join
5+
const openAboutWindow = require('about-window').default
46

57
let win = null // main window
68

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

16+
function listFolder(folder) {
17+
files = fs.readdirSync(path.resolve(folder))
18+
// Filter out directories
19+
files = files.filter(f => {
20+
let filePath = path.resolve(folder, f)
21+
return !fs.lstatSync(filePath).isDirectory()
22+
})
23+
return files
24+
}
25+
26+
// LOCAL FILE SYSTEM ACCESS
1327
ipcMain.handle('open-folder', async (event) => {
1428
console.log('ipcMain', 'open-folder')
1529
const folder = await openFolderDialog()
1630
let files = []
1731
if (folder) {
18-
files = fs.readdirSync(path.resolve(folder))
19-
// Filter out directories
20-
files = files.filter(f => {
21-
let filePath = path.resolve(folder, f)
22-
return !fs.lstatSync(filePath).isDirectory()
23-
})
32+
files = listFolder(folder)
2433
}
2534
return { folder, files }
2635
})
2736

37+
ipcMain.handle('list-files', async (event, folder) => {
38+
console.log('ipcMain', 'list-files', folder)
39+
if (!folder) return []
40+
return listFolder(folder)
41+
})
42+
2843
ipcMain.handle('load-file', (event, folder, filename) => {
2944
console.log('ipcMain', 'load-file', folder, filename )
3045
let filePath = path.resolve(folder, filename)
@@ -40,6 +55,7 @@ ipcMain.handle('save-file', (event, folder, filename, content) => {
4055
})
4156

4257
ipcMain.handle('update-folder', (event, folder) => {
58+
console.log('ipcMain', 'update-folder', folder)
4359
let files = fs.readdirSync(path.resolve(folder))
4460
// Filter out directories
4561
files = files.filter(f => {
@@ -64,6 +80,7 @@ ipcMain.handle('rename-file', (event, folder, filename, newFilename) => {
6480
return newFilename
6581
})
6682

83+
// START APP
6784
function createWindow () {
6885
// Create the browser window.
6986
win = new BrowserWindow({
@@ -77,7 +94,146 @@ function createWindow () {
7794
}
7895
})
7996
// and load the index.html of the app.
80-
win.loadFile('ui/blank/index.html')
97+
win.loadFile('ui/arduino/index.html')
8198
}
8299

100+
// TODO: Loading splash screen
101+
102+
const isMac = process.platform === 'darwin'
103+
const isDev = !app.isPackaged
104+
const template = [
105+
...(isMac ? [{
106+
label: app.name,
107+
submenu: [
108+
{ role: 'about'},
109+
{ type: 'separator' },
110+
{ role: 'services' },
111+
{ type: 'separator' },
112+
{ role: 'hide' },
113+
{ role: 'hideOthers' },
114+
{ role: 'unhide' },
115+
{ type: 'separator' },
116+
{ role: 'quit' }
117+
]
118+
}] : []),
119+
{
120+
label: 'File',
121+
submenu: [
122+
isMac ? { role: 'close' } : { role: 'quit' }
123+
]
124+
},
125+
{
126+
label: 'Edit',
127+
submenu: [
128+
{ role: 'undo' },
129+
{ role: 'redo' },
130+
{ type: 'separator' },
131+
{ role: 'cut' },
132+
{ role: 'copy' },
133+
{ role: 'paste' },
134+
...(isMac ? [
135+
{ role: 'pasteAndMatchStyle' },
136+
{ role: 'selectAll' },
137+
{ type: 'separator' },
138+
{
139+
label: 'Speech',
140+
submenu: [
141+
{ role: 'startSpeaking' },
142+
{ role: 'stopSpeaking' }
143+
]
144+
}
145+
] : [
146+
{ type: 'separator' },
147+
{ role: 'selectAll' }
148+
])
149+
]
150+
},
151+
{
152+
label: 'View',
153+
submenu: [
154+
{ role: 'reload' },
155+
{ type: 'separator' },
156+
{ role: 'resetZoom' },
157+
{ role: 'zoomIn' },
158+
{ role: 'zoomOut' },
159+
{ type: 'separator' },
160+
{ role: 'togglefullscreen' },
161+
...(isDev ? [
162+
{ type: 'separator' },
163+
{ role: 'toggleDevTools' },
164+
]:[
165+
])
166+
]
167+
},
168+
{
169+
label: 'Window',
170+
submenu: [
171+
{ role: 'minimize' },
172+
{ role: 'zoom' },
173+
...(isMac ? [
174+
{ type: 'separator' },
175+
{ role: 'front' },
176+
{ type: 'separator' },
177+
{ role: 'window' }
178+
] : [
179+
{ role: 'close' }
180+
])
181+
]
182+
},
183+
{
184+
role: 'help',
185+
submenu: [
186+
{
187+
label: 'Learn More',
188+
click: async () => {
189+
const { shell } = require('electron')
190+
await shell.openExternal('https://github.com/arduino/MicroPython_Lab')
191+
}
192+
},
193+
{
194+
label: 'Report an issue',
195+
click: async () => {
196+
const { shell } = require('electron')
197+
await shell.openExternal('https://github.com/arduino/MicroPython_Lab/issues')
198+
}
199+
},
200+
{
201+
label:'Info about this app',
202+
click: () => {
203+
openAboutWindow({
204+
icon_path: join(__dirname, 'ui/arduino/assets/about_image.png'),
205+
css_path: join(__dirname, 'ui/arduino/about.css'),
206+
copyright: '© Arduino SA 2022',
207+
package_json_dir: __dirname,
208+
bug_report_url: "https://github.com/arduino/MicroPython_Lab/issues",
209+
bug_link_text: "report an issue",
210+
homepage: "https://labs.arduino.cc",
211+
use_version_info: false,
212+
win_options: {
213+
parent: win,
214+
modal: true,
215+
},
216+
show_close_button: 'Close',
217+
})
218+
}
219+
},
220+
]
221+
}
222+
]
223+
224+
const menu = Menu.buildFromTemplate(template)
225+
226+
app.setAboutPanelOptions({
227+
applicationName: app.name,
228+
applicationVersion: app.getVersion(),
229+
copyright: app.copyright,
230+
credits: '(See "Info about this app" in the Help menu)',
231+
authors: ['Arduino'],
232+
website: 'https://arduino.cc',
233+
iconPath: path.join(__dirname, '../assets/image.png'),
234+
})
235+
236+
Menu.setApplicationMenu(menu)
237+
238+
83239
app.whenReady().then(createWindow)

0 commit comments

Comments
 (0)