1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-11-24 12:31:25 +00:00

electron: open workspace w/ metadata file, write files

This commit is contained in:
Steven Hugg 2020-08-26 20:42:18 -05:00
parent 13a4570745
commit 8b801e39df
12 changed files with 336 additions and 161 deletions

View File

@ -1,6 +1,6 @@
[![Build Status](https://travis-ci.org/sehugg/8bitworkshop.svg?branch=master)](https://travis-ci.org/sehugg/8bitworkshop) [![Build Status](https://travis-ci.org/sehugg/8bitworkshop.svg?branch=master)](https://travis-ci.org/sehugg/8bitworkshop)
The latest release is online at http://8bitworkshop.com/ The latest release is online at https://8bitworkshop.com/
## Install ## Install

View File

@ -15,20 +15,15 @@ TODO:
- watchpoints - watchpoints
- step over (line, instruction) - step over (line, instruction)
- slowdown beam for all platforms? - slowdown beam for all platforms?
- show errors in list (maybe window list?) - intro/help text for each platform
- click to go to error
- what if error in include file you can't edit b/c it never appears?
- online help
- show self-modifying code insns left of editor - show self-modifying code insns left of editor
- update Javatari version? (and others?) - update Javatari version? (and others?)
- sound mute? - sound mute?
- $error updates source editor - $error updates source editor
- online tools for music etc - online tools for music etc
- text log debugging script - text log debugging script
- intro/help text for each platform
- vscode/atom extension? - vscode/atom extension?
- click to break on raster position - click to break on raster position
- restructure src/ folders
- debug bankswitching for funky formats - debug bankswitching for funky formats
- 'undefined' for bitmap replacer - 'undefined' for bitmap replacer
- requestInterrupt needs to be disabled after breakpoint? - requestInterrupt needs to be disabled after breakpoint?

View File

@ -24,9 +24,18 @@ process.once('loaded', () => {
return null; return null;
} }
} }
// from browser: put workspace file
window.putWorkspaceFile = function(path, data) {
if (wsroot == null) throw Error("no workspace root set");
var fullpath = modpath.join(wsroot, modpath.normalize(path));
var encoding = typeof data === 'string' ? 'utf8' : null;
fs.writeFileSync(fullpath, data, {encoding:encoding});
}
// from electron.js: set workspace root directory // from electron.js: set workspace root directory
ipcRenderer.on('setWorkspaceRoot', (event, data) => { ipcRenderer.on('setWorkspaceRoot', (event, data) => {
wsroot = data.root; wsroot = data.root;
var binpath = modpath.join(wsroot, 'bin');
if (!fs.existsSync(binpath)) fs.mkdirSync(binpath, {});
console.log('setWorkspaceRoot', wsroot); console.log('setWorkspaceRoot', wsroot);
}); });
// from electron.js: file changed // from electron.js: file changed

View File

@ -14,6 +14,29 @@ body {
</head> </head>
<body> <body>
<!-- Sentry error reporting -->
<script defer
src="https://browser.sentry-cdn.com/5.22.3/bundle.tracing.min.js"
integrity="sha384-HfEJlGrJtFM0B01Wt4sGzTbxWqLMcMeGAXCbyQyB+iK9BhnDmNAtIGovhekIQOa2"
crossorigin="anonymous"
></script>
<script defer>
Sentry.init({
dsn: 'https://bf329df3d1b34afa9f5b5e8ecd80ad11@o320878.ingest.sentry.io/1813925',
beforeBreadcrumb(breadcrumb, hint) {
if (breadcrumb.category === 'xhr' && typeof breadcrumb.data.url === 'string') {
if (breadcrumb.data.url.startsWith('http')) return null; // discard external scripts
}
return breadcrumb;
},
beforeSend(event, hint) {
const error = hint.originalException;
if (error instanceof EmuHalt) return null; // ignore EmuHalt
return event;
},
});
</script>
<!-- for file upload --> <!-- for file upload -->
<input type="file" id="uploadFileElem" multiple accept="*" style="display:none" onchange="handleFileUpload(this.files)"> <input type="file" id="uploadFileElem" multiple accept="*" style="display:none" onchange="handleFileUpload(this.files)">

View File

@ -6,16 +6,33 @@ const isMac = process.platform === 'darwin'
const chokidar = require('chokidar') const chokidar = require('chokidar')
const Store = require('electron-store'); const Store = require('electron-store');
const store = new Store(); const store = new Store();
const KEY_lastWorkspaceFilePath = "lastWorkspaceFilePath"; const WSMETA_FILENAME = "8bitworkshop.json"
const README_FILENAME = "README.md"
// call updater
require('update-electron-app')()
// show error dialog
function showError(msg, detail) {
msg = msg.message || msg;
dialog.showMessageBoxSync({
type: "error",
message: msg,
detail: detail
});
}
// file watcher // file watcher
// TODO: add workspace metadata for platform, ROM output, README, etc.
class Workspace { class Workspace {
constructor(directory, mainfile, wnd) { constructor(directory, meta, wnd) {
this.directory = directory; this.directory = directory;
this.mainfile = mainfile; this.mainfile = meta.mainfile;
wnd.webContents.send('setWorkspaceRoot', {root:this.directory}); this.platform = meta.platform;
this.watcher = chokidar.watch(modpath.join(directory, mainfile)); if (!this.mainfile) throw new Error(`The "mainfile" key is missing in ${WSMETA_FILENAME}.`)
if (!this.platform) throw new Error(`The "platform" key is missing in ${WSMETA_FILENAME}.`)
var mainfilepath = modpath.join(directory, this.mainfile);
if (!fs.existsSync(mainfilepath)) throw new Error(`The file "${mainfilepath}" is missing.`);
this.watcher = chokidar.watch(mainfilepath);
this.watcher.on('all', (event, path) => { this.watcher.on('all', (event, path) => {
console.log(event, path); console.log(event, path);
switch (event) { switch (event) {
@ -38,34 +55,51 @@ class Workspace {
} }
} }
function openFile(path, wnd) { function readWorkspaceMetadata(directory) {
if (!fs.existsSync(path)) { // check README file
dialog.showMessageBox({ var readmepath = modpath.join(directory, README_FILENAME);
type: "error", if (fs.existsSync(readmepath)) {
message: "File not found.", let readme = fs.readFileSync(readmepath, 'utf8');
detail: path let sess = {};
}); // check README for main file
return; const re8main = /8bitworkshop.com[^)]+file=([^)&]+)/;
m = re8main.exec(readme);
if (m && m[1]) {
sess.mainfile = m[1];
}
// check README for proper platform
// unless we use githubURL=
const re8plat = /8bitworkshop.com[^)]+platform=([A-Za-z0-9._\-]+)/;
m = re8plat.exec(readme);
if (m) {
sess.platform = m[1];
}
if (sess.mainfile != null && sess.platform != null) return sess;
} }
var dirname = modpath.dirname(path); // check JSON file
var filename = modpath.basename(path); var metapath = modpath.join(directory, WSMETA_FILENAME);
if (!wnd) wnd = BrowserWindow.getFocusedWindow(); if (fs.existsSync(metapath)) {
var ws = new Workspace(dirname, filename, wnd); return JSON.parse(fs.readFileSync(metapath, 'utf8'));
}
}
function writeWorkspaceMetadata(directory, meta) {
var metapath = modpath.join(directory, WSMETA_FILENAME);
fs.writeFileSync(metapath, JSON.stringify(meta), 'utf8');
}
function openWorkspace(wnd, ws) {
if (wnd.workspace) { wnd.workspace.close(); } if (wnd.workspace) { wnd.workspace.close(); }
wnd.workspace = ws; wnd.workspace = ws;
wnd.on('closed', () => { wnd.on('closed', () => {
ws.close(); ws.close();
}); });
openWorkspace(wnd, ws);
app.addRecentDocument(path);
store.set(KEY_lastWorkspaceFilePath, path);
}
function openWorkspace(wnd, ws) {
var qs = new URLSearchParams(); var qs = new URLSearchParams();
qs.set('electron_ws', 1); qs.set('electron_ws', 1);
qs.set('repo', ws.directory); qs.set('repo', ws.directory);
qs.set('file', ws.mainfile); qs.set('file', ws.mainfile);
qs.set('platform', ws.platform);
console.log(qs);
wnd.loadURL(`file://${__dirname}/electron.html?${qs}`).then(() => { wnd.loadURL(`file://${__dirname}/electron.html?${qs}`).then(() => {
wnd.webContents.send('setWorkspaceRoot', {root:ws.directory}); wnd.webContents.send('setWorkspaceRoot', {root:ws.directory});
}); });
@ -81,42 +115,88 @@ function reloadCurrentWindow() {
} }
} }
function openFileDialog() { // TODO: better way to get this?
function getCurrentPlatform(wnd) {
var url = wnd.webContents.getURL();
if (url != null) {
console.log(url);
var m = /platform=([^&]+)/.exec(url);
if (m) return m[1];
}
}
function openWorkspaceWindow(wspath) {
try {
// replace current window
var wnd = BrowserWindow.getFocusedWindow() || createWindow();
// read metadata file
var meta = readWorkspaceMetadata(wspath);
// does it exist?
if (meta == null) {
// create a new workspace?
var cancel = dialog.showMessageBoxSync(wnd, {
type: 'question',
title: 'Create Workspace?',
message: `Project metadata not found. Create new ${getCurrentPlatform(wnd)||""} project in this directory?`,
detail: wspath,
buttons: ['Create', 'Cancel'],
});
if (!cancel) {
var platform = getCurrentPlatform(wnd);
// choose main file
var files = dialog.showOpenDialogSync({
message: `Choose the main file of your ${platform} project, or create one.`,
defaultPath: wspath,
properties: ['openFile','promptToCreate'],
});
if (files != null) {
var mainfile = modpath.relative(wspath, files[0]);
// write new metadata
meta = {
platform: platform,
mainfile: mainfile,
};
console.log(meta);
if (meta.platform == null) {
showError("Can't determine current platform.");
} else {
writeWorkspaceMetadata(wspath, meta);
openWorkspaceWindow(wspath);
}
}
}
} else {
console.log(meta);
var ws = new Workspace(wspath, meta, wnd);
openWorkspace(wnd, ws);
app.addRecentDocument(wspath);
}
} catch (e) {
showError(e);
}
}
function openDefaultWorkspace() {
if (process.argv.length >= 3) {
openWorkspaceWindow(modpath.resolve(process.argv[2]));
} else {
createWindow();
}
}
function openWorkspaceDialog() {
dialog.showOpenDialog({ dialog.showOpenDialog({
title: "Open File", title: "Open Workspace",
properties: ['openFile','promptToCreate'], properties: ['openDirectory'],
message: "Choose the directory that holds your source files.",
}).then((rtn) => { }).then((rtn) => {
if (!rtn.canceled && rtn.filePaths && rtn.filePaths.length > 0) { if (!rtn.canceled && rtn.filePaths && rtn.filePaths.length > 0) {
var path = rtn.filePaths[0]; var path = rtn.filePaths[0];
openFile(path); openWorkspaceWindow(path);
} }
}); });
} }
function openDefaultFile() {
var wnd = createWindow();
if (process.argv.length >= 3) {
openFile(process.argv[2], wnd);
}
/* TODO
var lastfile = store.get(KEY_lastWorkspaceFilePath);
if (lastfile != null) {
openFile(lastfile);
}
*/
}
/*
function openWorkspace() {
const { dialog } = require('electron')
console.log(dialog.showOpenDialog({
title: "Open Workspace",
properties: ['openDirectory','createDirectory','promptToCreate'],
message: "Choose a directory that holds your source files.",
}))
}
*/
function openURL(url) { function openURL(url) {
return async () => { return async () => {
const { shell } = require('electron') const { shell } = require('electron')
@ -147,8 +227,13 @@ function buildMenu() {
label: 'File', label: 'File',
submenu: [ submenu: [
{ {
label: 'Open File...', label: 'New Playground',
click: openFileDialog, click: openDefaultWorkspace,
accelerator: 'CmdOrCtrl+N',
},
{
label: 'Open Workspace...',
click: openWorkspaceDialog,
accelerator: 'CmdOrCtrl+O', accelerator: 'CmdOrCtrl+O',
}, },
// When a file is requested from the recent documents menu, the open-file event of app module will be emitted for it. // When a file is requested from the recent documents menu, the open-file event of app module will be emitted for it.
@ -239,13 +324,18 @@ function buildMenu() {
label: 'Latest News', label: 'Latest News',
click: openURL('https://8bitworkshop.com/blog/') click: openURL('https://8bitworkshop.com/blog/')
}, },
{
label: 'Report an Issue',
click: openURL('https://github.com/sehugg/8bitworkshop/issues/new')
},
{ type: 'separator' },
{ {
label: 'Follow @8bitworkshop on Twitter', label: 'Follow @8bitworkshop on Twitter',
click: openURL('https://twitter.com/8bitworkshop') click: openURL('https://twitter.com/8bitworkshop')
}, },
{ {
label: 'Report an Issue', label: 'Become a Patreon',
click: openURL('https://github.com/sehugg/8bitworkshop/issues/new') click: openURL('https://www.patreon.com/8bitworkshop')
}, },
] ]
} }
@ -286,7 +376,7 @@ function createWindow () {
// This method will be called when Electron has finished // This method will be called when Electron has finished
// initialization and is ready to create browser windows. // initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs. // Some APIs can only be used after this event occurs.
app.whenReady().then(buildMenu).then(openDefaultFile) app.whenReady().then(buildMenu).then(openDefaultWorkspace)
// Quit when all windows are closed, except on macOS. There, it's common // Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits // for applications and their menu bar to stay active until the user quits
@ -301,12 +391,12 @@ app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the // On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open. // dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) { if (BrowserWindow.getAllWindows().length === 0) {
createWindow() openDefaultWorkspace()
} }
}) })
app.on('open-file', (event, path) => { app.on('open-file', (event, path) => {
openFile(path); openWorkspaceWindow(path);
}) })
// In this file you can include the rest of your app's specific main process // In this file you can include the rest of your app's specific main process

BIN
images/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -14,6 +14,7 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="msapplication-starturl" content="/redir.html"> <meta name="msapplication-starturl" content="/redir.html">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=yes"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=yes">
<link rel="apple-touch-icon" href="/images/icon.png">
<style type="text/css" media="screen"> <style type="text/css" media="screen">
body { body {
overflow: hidden; overflow: hidden;
@ -38,14 +39,15 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
<script defer src="config.js"></script> <script defer src="config.js"></script>
<!-- Sentry error reporting --> <!-- Sentry error reporting -->
<script <script defer
src="https://browser.sentry-cdn.com/5.20.1/bundle.min.js" src="https://browser.sentry-cdn.com/5.22.3/bundle.tracing.min.js"
integrity="sha384-O8HdAJg1h8RARFowXd2J/r5fIWuinSBtjhwQoPesfVILeXzGpJxvyY/77OaPPXUo" integrity="sha384-HfEJlGrJtFM0B01Wt4sGzTbxWqLMcMeGAXCbyQyB+iK9BhnDmNAtIGovhekIQOa2"
crossorigin="anonymous"></script> crossorigin="anonymous"
<script> ></script>
<script defer>
if (window.location.host.endsWith('8bitworkshop.com')) { if (window.location.host.endsWith('8bitworkshop.com')) {
Sentry.init({ Sentry.init({
dsn: 'https://bf329df3d1b34afa9f5b5e8ecd80ad11@sentry.io/1813925', dsn: 'https://bf329df3d1b34afa9f5b5e8ecd80ad11@o320878.ingest.sentry.io/1813925',
beforeBreadcrumb(breadcrumb, hint) { beforeBreadcrumb(breadcrumb, hint) {
if (breadcrumb.category === 'xhr' && typeof breadcrumb.data.url === 'string') { if (breadcrumb.category === 'xhr' && typeof breadcrumb.data.url === 'string') {
if (breadcrumb.data.url.startsWith('http')) return null; // discard external scripts if (breadcrumb.data.url.startsWith('http')) return null; // discard external scripts

View File

@ -1,6 +1,6 @@
--- index.html 2020-08-11 11:10:30.000000000 -0500 --- index.html 2020-08-29 13:22:47.000000000 -0500
+++ electron.html 2020-08-11 14:47:38.000000000 -0500 +++ electron.html 2020-08-29 13:22:40.000000000 -0500
@@ -3,16 +3,6 @@ @@ -3,18 +3,7 @@
<head> <head>
<title>8bitworkshop IDE</title> <title>8bitworkshop IDE</title>
@ -15,9 +15,11 @@
-<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> -<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
-<meta name="msapplication-starturl" content="/redir.html"> -<meta name="msapplication-starturl" content="/redir.html">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=yes"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=yes">
-<link rel="apple-touch-icon" href="/images/icon.png">
<style type="text/css" media="screen"> <style type="text/css" media="screen">
body { body {
@@ -21,46 +11,6 @@ overflow: hidden;
@@ -22,50 +11,32 @@
} }
</style> </style>
<link rel="stylesheet" href="css/ui.css"> <link rel="stylesheet" href="css/ui.css">
@ -36,16 +38,22 @@
-<script defer src="https://www.gstatic.com/firebasejs/5.11.1/firebase-app.js"></script> -<script defer src="https://www.gstatic.com/firebasejs/5.11.1/firebase-app.js"></script>
-<script defer src="https://www.gstatic.com/firebasejs/5.11.1/firebase-auth.js"></script> -<script defer src="https://www.gstatic.com/firebasejs/5.11.1/firebase-auth.js"></script>
-<script defer src="config.js"></script> -<script defer src="config.js"></script>
- +</head>
-<!-- Sentry error reporting --> +<body>
-<script
- src="https://browser.sentry-cdn.com/5.20.1/bundle.min.js" <!-- Sentry error reporting -->
- integrity="sha384-O8HdAJg1h8RARFowXd2J/r5fIWuinSBtjhwQoPesfVILeXzGpJxvyY/77OaPPXUo" <script defer
- crossorigin="anonymous"></script> - src="https://browser.sentry-cdn.com/5.22.3/bundle.tracing.min.js"
-<script> - integrity="sha384-HfEJlGrJtFM0B01Wt4sGzTbxWqLMcMeGAXCbyQyB+iK9BhnDmNAtIGovhekIQOa2"
- crossorigin="anonymous"
+src="https://browser.sentry-cdn.com/5.22.3/bundle.tracing.min.js"
+integrity="sha384-HfEJlGrJtFM0B01Wt4sGzTbxWqLMcMeGAXCbyQyB+iK9BhnDmNAtIGovhekIQOa2"
+crossorigin="anonymous"
></script>
<script defer>
-if (window.location.host.endsWith('8bitworkshop.com')) { -if (window.location.host.endsWith('8bitworkshop.com')) {
- Sentry.init({ - Sentry.init({
- dsn: 'https://bf329df3d1b34afa9f5b5e8ecd80ad11@sentry.io/1813925', - dsn: 'https://bf329df3d1b34afa9f5b5e8ecd80ad11@o320878.ingest.sentry.io/1813925',
- beforeBreadcrumb(breadcrumb, hint) { - beforeBreadcrumb(breadcrumb, hint) {
- if (breadcrumb.category === 'xhr' && typeof breadcrumb.data.url === 'string') { - if (breadcrumb.category === 'xhr' && typeof breadcrumb.data.url === 'string') {
- if (breadcrumb.data.url.startsWith('http')) return null; // discard external scripts - if (breadcrumb.data.url.startsWith('http')) return null; // discard external scripts
@ -59,12 +67,29 @@
- }, - },
- }); - });
-} -}
-</script> +Sentry.init({
- + dsn: 'https://bf329df3d1b34afa9f5b5e8ecd80ad11@o320878.ingest.sentry.io/1813925',
</head> + beforeBreadcrumb(breadcrumb, hint) {
<body> + if (breadcrumb.category === 'xhr' && typeof breadcrumb.data.url === 'string') {
+ if (breadcrumb.data.url.startsWith('http')) return null; // discard external scripts
+ }
+ return breadcrumb;
+ },
+ beforeSend(event, hint) {
+ const error = hint.originalException;
+ if (error instanceof EmuHalt) return null; // ignore EmuHalt
+ return event;
+ },
+});
</script>
@@ -88,26 +38,6 @@ -</head>
-<body>
-
<!-- for file upload -->
<input type="file" id="uploadFileElem" multiple accept="*" style="display:none" onchange="handleFileUpload(this.files)">
@@ -90,26 +61,6 @@
<hr> <hr>
<li><a class="dropdown-item" href="#" id="item_addfile_include">Add Include File...</a></li> <li><a class="dropdown-item" href="#" id="item_addfile_include">Add Include File...</a></li>
<li><a class="dropdown-item" href="#" id="item_addfile_link">Add Linked File...</a></li> <li><a class="dropdown-item" href="#" id="item_addfile_link">Add Linked File...</a></li>
@ -91,7 +116,7 @@
</ul> </ul>
</li> </li>
<li class="dropdown dropdown-submenu"> <li class="dropdown dropdown-submenu">
@@ -133,36 +63,6 @@ @@ -135,36 +86,6 @@
<li><a class="dropdown-item" href="#" id="item_debug_expr">Break Expression...</a></li> <li><a class="dropdown-item" href="#" id="item_debug_expr">Break Expression...</a></li>
</ul> </ul>
</li> </li>
@ -128,7 +153,7 @@
</ul> </ul>
</span> </span>
@@ -258,39 +158,6 @@ @@ -260,39 +181,6 @@
<span class="label"><span id="settle_label"></span> evals/clk</span> <span class="label"><span id="settle_label"></span> evals/clk</span>
</span> </span>
@ -168,7 +193,7 @@
<!-- 8bitworkshop logo --> <!-- 8bitworkshop logo -->
<span class="logo-gradient hidden-xs hidden-sm hidden-md pull-right" style="margin-left:auto" onclick="window.open('/','_8bitws');">8bitworkshop</span> <span class="logo-gradient hidden-xs hidden-sm hidden-md pull-right" style="margin-left:auto" onclick="window.open('/','_8bitws');">8bitworkshop</span>
@@ -477,73 +344,6 @@ @@ -479,73 +367,6 @@
</div> </div>
</div> </div>
</div> </div>
@ -242,16 +267,3 @@
<script src="jquery/jquery.min.js"></script> <script src="jquery/jquery.min.js"></script>
@@ -635,12 +435,5 @@
startUI();
</script>
-<script>
-/*
- var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
- if (!isFirefox && platform_id != 'vcs') { $("#best_in_firefox").show(); }
-*/
-</script>
-
</body>
</html>

102
package-lock.json generated
View File

@ -268,9 +268,9 @@
"optional": true "optional": true
}, },
"@types/node": { "@types/node": {
"version": "14.0.27", "version": "14.6.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.2.tgz",
"integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g==", "integrity": "sha512-onlIwbaeqvZyniGPfdw/TEhKIh79pz66L1q06WUQqJLnAb6wbjvOtepLYTGHTqzdXgBYIE3ZdmqHDGsRsbBz7A==",
"dev": true "dev": true
}, },
"@types/sizzle": { "@types/sizzle": {
@ -606,9 +606,9 @@
}, },
"dependencies": { "dependencies": {
"get-stream": { "get-stream": {
"version": "5.1.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"dev": true, "dev": true,
"requires": { "requires": {
"pump": "^3.0.0" "pump": "^3.0.0"
@ -678,9 +678,9 @@
} }
}, },
"chokidar": { "chokidar": {
"version": "3.4.1", "version": "3.4.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.1.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
"integrity": "sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==", "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
"requires": { "requires": {
"anymatch": "~3.1.1", "anymatch": "~3.1.1",
"braces": "~3.0.2", "braces": "~3.0.2",
@ -886,6 +886,13 @@
"safe-buffer": "~5.1.1" "safe-buffer": "~5.1.1"
} }
}, },
"core-js": {
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
"integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==",
"dev": true,
"optional": true
},
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@ -1123,9 +1130,9 @@
} }
}, },
"electron": { "electron": {
"version": "9.1.2", "version": "9.2.1",
"resolved": "https://registry.npmjs.org/electron/-/electron-9.1.2.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-9.2.1.tgz",
"integrity": "sha512-xEYadr3XqIqJ4ktBPo0lhzPdovv4jLCpiUUGc2M1frUhFhwqXokwhPaTUcE+zfu5+uf/ONDnQApwjzznBsRrgQ==", "integrity": "sha512-ZsetaQjXB8+9/EFW1FnfK4ukpkwXCxMEaiKiUZhZ0ZLFlLnFCpe0Bg4vdDf7e4boWGcnlgN1jAJpBw7w0eXuqA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@electron/get": "^1.0.1", "@electron/get": "^1.0.1",
@ -1134,13 +1141,18 @@
}, },
"dependencies": { "dependencies": {
"@types/node": { "@types/node": {
"version": "12.12.53", "version": "12.12.54",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.53.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.54.tgz",
"integrity": "sha512-51MYTDTyCziHb70wtGNFRwB4l+5JNvdqzFSkbDvpbftEgVUBEE+T5f7pROhWMp/fxp07oNIEQZd5bbfAH22ohQ==", "integrity": "sha512-ge4xZ3vSBornVYlDnk7yZ0gK6ChHf/CHB7Gl1I0Jhah8DDnEQqBzgohYG4FX4p81TNirSETOiSyn+y1r9/IR6w==",
"dev": true "dev": true
} }
} }
}, },
"electron-is-dev": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-0.3.0.tgz",
"integrity": "sha1-FOb9pcaOnk7L7/nM8DfL18BcWv4="
},
"electron-notarize": { "electron-notarize": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-1.0.0.tgz", "resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-1.0.0.tgz",
@ -1219,9 +1231,9 @@
} }
}, },
"electron-packager": { "electron-packager": {
"version": "15.0.0", "version": "15.1.0",
"resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-15.0.0.tgz", "resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-15.1.0.tgz",
"integrity": "sha512-J0yQP7/fKPkjxo9Yz5+vsQVig0dBbSXW8LQYA1pvNMvi+bL00hfI2SAyORP6EU7XaeiXGUIBSG2Px01EkKfGCw==", "integrity": "sha512-THNm4bz1DfvR9f0g51+NjuAYELflM8+1vhQ/iv/G8vyZNKzSMuFd5doobngQKq3rRsLdPNZVnGqDdgS884d7Og==",
"dev": true, "dev": true,
"requires": { "requires": {
"@electron/get": "^1.6.0", "@electron/get": "^1.6.0",
@ -1240,7 +1252,7 @@
"rcedit": "^2.0.0", "rcedit": "^2.0.0",
"resolve": "^1.1.6", "resolve": "^1.1.6",
"semver": "^7.1.3", "semver": "^7.1.3",
"yargs-parser": "^18.0.0" "yargs-parser": "^19.0.1"
}, },
"dependencies": { "dependencies": {
"extract-zip": { "extract-zip": {
@ -1268,9 +1280,9 @@
} }
}, },
"get-stream": { "get-stream": {
"version": "5.1.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"dev": true, "dev": true,
"requires": { "requires": {
"pump": "^3.0.0" "pump": "^3.0.0"
@ -1299,14 +1311,10 @@
"dev": true "dev": true
}, },
"yargs-parser": { "yargs-parser": {
"version": "18.1.3", "version": "19.0.4",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-19.0.4.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "integrity": "sha512-eXeQm7yXRjPFFyf1voPkZgXQZJjYfjgQUmGPbD2TLtZeIYzvacgWX7sQ5a1HsRgVP+pfKAkRZDNtTGev4h9vhw==",
"dev": true, "dev": true
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
} }
} }
}, },
@ -1790,6 +1798,14 @@
"assert-plus": "^1.0.0" "assert-plus": "^1.0.0"
} }
}, },
"github-url-to-object": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/github-url-to-object/-/github-url-to-object-4.0.4.tgz",
"integrity": "sha512-1Ri1pR8XTfzLpbtPz5MlW/amGNdNReuExPsbF9rxLsBfO1GH9RtDBamhJikd0knMWq3RTTQDbTtw0GGvvEAJEA==",
"requires": {
"is-url": "^1.1.0"
}
},
"glob": { "glob": {
"version": "7.1.6", "version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
@ -1828,13 +1844,6 @@
"serialize-error": "^7.0.1" "serialize-error": "^7.0.1"
}, },
"dependencies": { "dependencies": {
"core-js": {
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
"integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==",
"dev": true,
"optional": true
},
"semver": { "semver": {
"version": "7.3.2", "version": "7.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
@ -2165,6 +2174,11 @@
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
"dev": true "dev": true
}, },
"is-url": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
"integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww=="
},
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -2934,8 +2948,7 @@
"ms": { "ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
"dev": true
}, },
"nested-error-stacks": { "nested-error-stacks": {
"version": "2.1.0", "version": "2.1.0",
@ -4140,6 +4153,17 @@
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true "dev": true
}, },
"update-electron-app": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/update-electron-app/-/update-electron-app-1.5.0.tgz",
"integrity": "sha512-g7noW9JfQ8Hwq6zw9lmZei+R/ikOIBcaZ04TbmIcU5zNfv23HkN80QLLAyiR/47KvfS4sjnh2/wuDq5nh8+0mQ==",
"requires": {
"electron-is-dev": "^0.3.0",
"github-url-to-object": "^4.0.4",
"is-url": "^1.2.4",
"ms": "^2.1.1"
}
},
"uri-js": { "uri-js": {
"version": "4.2.2", "version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",

View File

@ -9,24 +9,25 @@
}, },
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"chokidar": "^3.4.1", "chokidar": "^3.4.2",
"electron-store": "^6.0.0", "electron-store": "^6.0.0",
"jquery": "^3.5.1", "jquery": "^3.5.1",
"reflect-metadata": "^0.1.13" "reflect-metadata": "^0.1.13",
"update-electron-app": "^1.5.0"
}, },
"devDependencies": { "devDependencies": {
"@types/bootbox": "^4.4.36", "@types/bootbox": "^4.4.36",
"@types/bootstrap": "^3.4.0", "@types/bootstrap": "^3.4.0",
"@types/file-saver": "^2.0.1", "@types/file-saver": "^2.0.1",
"@types/jquery": "^3.5.1", "@types/jquery": "^3.5.1",
"@types/node": "^14.0.27", "@types/node": "^14.6.2",
"atob": "^2.1.x", "atob": "^2.1.x",
"bootstrap": "^3.4.1", "bootstrap": "^3.4.1",
"bootstrap-tourist": "^0.2.1", "bootstrap-tourist": "^0.2.1",
"btoa": "^1.2.x", "btoa": "^1.2.x",
"clipboard": "^2.0.6", "clipboard": "^2.0.6",
"electron": "^9.1.2", "electron": "^9.2.1",
"electron-packager": "^15.0.0", "electron-packager": "^15.1.0",
"file-saver": "^2.0.2", "file-saver": "^2.0.2",
"jsdom": "^12.2.0", "jsdom": "^12.2.0",
"jsfuzz": "^1.0.14", "jsfuzz": "^1.0.14",

View File

@ -19,17 +19,19 @@ export class CodeProject {
mainPath : string; mainPath : string;
pendingWorkerMessages = 0; pendingWorkerMessages = 0;
tools_preloaded = {}; tools_preloaded = {};
callbackBuildResult : BuildResultCallback;
callbackBuildStatus : BuildStatusCallback;
worker : Worker; worker : Worker;
platform_id : string; platform_id : string;
platform : Platform; platform : Platform;
store : any; store : any;
callbackGetRemote : GetRemoteCallback;
isCompiling : boolean = false; isCompiling : boolean = false;
filename2path = {}; // map stripped paths to full paths filename2path = {}; // map stripped paths to full paths
persistent : boolean = true; // set to true and won't modify store persistent : boolean = true; // set to true and won't modify store
callbackBuildResult : BuildResultCallback;
callbackBuildStatus : BuildStatusCallback;
callbackGetRemote : GetRemoteCallback;
callbackStoreFile : IterateFilesCallback;
constructor(worker, platform_id:string, platform, store) { constructor(worker, platform_id:string, platform, store) {
this.worker = worker; this.worker = worker;
this.platform_id = platform_id; this.platform_id = platform_id;
@ -155,6 +157,9 @@ export class CodeProject {
if (this.persistent && !isEmptyString(text)) { if (this.persistent && !isEmptyString(text)) {
this.store.setItem(path, text); this.store.setItem(path, text);
} }
if (this.callbackStoreFile != null) {
this.callbackStoreFile(path, text);
}
} }
// TODO: test duplicate files, local paths mixed with presets // TODO: test duplicate files, local paths mixed with presets

View File

@ -114,7 +114,7 @@ function newWorker() : Worker {
return new Worker("./src/worker/loader.js"); return new Worker("./src/worker/loader.js");
} }
var hasLocalStorage : boolean = function() { const hasLocalStorage : boolean = function() {
try { try {
const key = "__some_random_key_you_are_not_going_to_use__"; const key = "__some_random_key_you_are_not_going_to_use__";
localStorage.setItem(key, key); localStorage.setItem(key, key);
@ -151,7 +151,7 @@ function getCurrentPresetTitle() : string {
function setLastPreset(id:string) { function setLastPreset(id:string) {
if (hasLocalStorage) { if (hasLocalStorage) {
if (repo_id && platform_id) if (repo_id && platform_id && !isElectron)
localStorage.setItem("__lastrepo_" + platform_id, repo_id); localStorage.setItem("__lastrepo_" + platform_id, repo_id);
else else
localStorage.removeItem("__lastrepo_" + platform_id); localStorage.removeItem("__lastrepo_" + platform_id);
@ -170,10 +170,10 @@ function unsetLastPreset() {
function initProject() { function initProject() {
current_project = new CodeProject(newWorker(), platform_id, platform, store); current_project = new CodeProject(newWorker(), platform_id, platform, store);
projectWindows = new ProjectWindows($("#workspace")[0] as HTMLElement, current_project); projectWindows = new ProjectWindows($("#workspace")[0] as HTMLElement, current_project);
if (qs['electron_ws']) { if (isElectronWorkspace) {
current_project.callbackGetRemote = getElectronFile;
current_project.persistent = false; current_project.persistent = false;
// TODO: save file when edited current_project.callbackGetRemote = getElectronFile;
current_project.callbackStoreFile = putWorkspaceFile;
} else { } else {
current_project.callbackGetRemote = getWithBinary; current_project.callbackGetRemote = getWithBinary;
} }
@ -942,9 +942,11 @@ function _downloadROMImage(e) {
var suffix = (platform.getROMExtension && platform.getROMExtension(current_output)) var suffix = (platform.getROMExtension && platform.getROMExtension(current_output))
|| "-" + getBasePlatform(platform_id) + ".bin"; || "-" + getBasePlatform(platform_id) + ".bin";
saveAs(blob, prefix + suffix); saveAs(blob, prefix + suffix);
} else { } else if (current_output.code != null) {
var blob = new Blob([(<VerilogOutput>current_output).code], {type: "text/plain"}); var blob = new Blob([(<VerilogOutput>current_output).code], {type: "text/plain"});
saveAs(blob, prefix + ".js"); saveAs(blob, prefix + ".js");
} else {
alertError(`The "${platform_id}" platform doesn't have downloadable ROMs.`);
} }
} }
@ -1003,7 +1005,7 @@ function populateExamples(sel) {
} }
function populateRepos(sel) { function populateRepos(sel) {
if (hasLocalStorage) { if (hasLocalStorage && !isElectron) {
var n = 0; var n = 0;
var repos = getRepos(); var repos = getRepos();
if (repos) { if (repos) {
@ -1052,7 +1054,7 @@ async function updateSelector() {
await populateFiles(sel, "Local Files", "", foundFiles); await populateFiles(sel, "Local Files", "", foundFiles);
finishSelector(sel); finishSelector(sel);
} else { } else {
if (!qs['electron_ws']) { if (!isElectronWorkspace) {
sel.append($("<option />").val('/').text('Leave Repository')); sel.append($("<option />").val('/').text('Leave Repository'));
} }
$("#repo_name").text(getFilenameForPath(repo_id)+'/').show(); $("#repo_name").text(getFilenameForPath(repo_id)+'/').show();
@ -1145,6 +1147,7 @@ function setCompileOutput(data: WorkerResult) {
current_output = rom; current_output = rom;
if (!userPaused) _resume(); if (!userPaused) _resume();
measureBuildTime(); measureBuildTime();
writeOutputROMFile();
} catch (e) { } catch (e) {
console.log(e); console.log(e);
toolbar.addClass("has-errors"); toolbar.addClass("has-errors");
@ -1909,6 +1912,9 @@ var qs = (function (a : string[]) {
return b; return b;
})(window.location.search.substr(1).split('&')); })(window.location.search.substr(1).split('&'));
const isElectronWorkspace = qs['electron_ws'];
const isElectron = qs['electron'] || isElectronWorkspace;
function globalErrorHandler(msgevent) { function globalErrorHandler(msgevent) {
var msg = (msgevent.message || msgevent.error || msgevent)+""; var msg = (msgevent.message || msgevent.error || msgevent)+"";
// storage quota full? (Chrome) try to expand it // storage quota full? (Chrome) try to expand it
@ -2263,7 +2269,7 @@ redirectToHTTPS();
//// ELECTRON STUFF //// ELECTRON STUFF
// get remote file from local fs // get remote file from local fs
declare var getWorkspaceFile; declare var getWorkspaceFile, putWorkspaceFile;
export function getElectronFile(url:string, success:(text:string|Uint8Array)=>void, datatype:'text'|'arraybuffer') { export function getElectronFile(url:string, success:(text:string|Uint8Array)=>void, datatype:'text'|'arraybuffer') {
// TODO: we have to split() to strip off presets/platform, yukky // TODO: we have to split() to strip off presets/platform, yukky
var contents = getWorkspaceFile(url.split('/',3)[2], datatype); var contents = getWorkspaceFile(url.split('/',3)[2], datatype);
@ -2277,3 +2283,11 @@ export function reloadWorkspaceFile(path: string) {
var datatype = typeof current_project.filedata[path] == 'string' ? 'text' : 'arraybuffer'; var datatype = typeof current_project.filedata[path] == 'string' ? 'text' : 'arraybuffer';
projectWindows.updateFile(path, getWorkspaceFile(path, datatype)); projectWindows.updateFile(path, getWorkspaceFile(path, datatype));
} }
function writeOutputROMFile() {
if (isElectronWorkspace && current_output instanceof Uint8Array) {
var prefix = getFilenamePrefix(getCurrentMainFilename());
var suffix = (platform.getROMExtension && platform.getROMExtension(current_output))
|| "-" + getBasePlatform(platform_id) + ".bin";
putWorkspaceFile(`bin/${prefix}${suffix}`, current_output);
}
}