8bitworkshop/electron-preload.js

140 lines
4.2 KiB
JavaScript

"use strict";
// preload.js for Electron app
const { ipcRenderer } = require('electron');
const fs = require('fs');
const _path = require('path');
const chokidar = require('chokidar');
const homeDirectory = require('os').homedir();
const wsroot = process.env.HOME_8BITWORKSHOP || _path.join(homeDirectory, '8bitworkshop');
function getLocalDir() {
const _ui = window.ui_1 || window.ui || window; // TODO: module naming
var dir = _ui.repo_id || _ui.platform_id || 'default';
return _path.join(wsroot, dir);
}
function getLocalFilePath(path) {
return _path.join(getLocalDir(), path);
}
function isProbablyBinary(path, data) {
var score = 0;
// decode as UTF-8
for (var i = 0; i < (data?data.length:0);) {
let c = data[i++];
if ((c & 0x80) == 0) {
// more likely binary if we see a NUL or obscure control character
if (c < 9 || (c >= 14 && c < 26) || c == 0x7f) {
score++;
break;
}
} else {
// look for invalid unicode sequences
var nextra = 0;
if ((c & 0xe0) == 0xc0) nextra = 1;
else if ((c & 0xf0) == 0xe0) nextra = 2;
else if ((c & 0xf8) == 0xf0) nextra = 3;
else if (c < 0xa0) score++;
else if (c == 0xff) score++;
while (nextra--) {
if (i >= data.length || (data[i++] & 0xc0) != 0x80) {
score++;
break;
}
}
}
}
return score > 0;
}
// this is before startUI is called
process.once('loaded', () => {
var watcher;
var allfiles = new Set();
// get workspace root dir
// from browser: read workspace files asynchronously
window.alternateLocalFilesystem = {
getFileData: (path) => {
let fullpath = getLocalFilePath(path);
allfiles.add(fullpath);
if (!fs.existsSync(fullpath)) {
return null;
}
return new Promise( (resolve, reject) => {
fs.readFile(fullpath, (err,buf) => {
if (err) {
resolve(err);
} else {
let isBinary = isProbablyBinary(path, buf);
let data = isBinary ? buf : buf.toString('utf-8');
console.log("getWorkspaceFile", path, isBinary, data.length);
resolve(data);
}
});
});
},
setFileData: (path, data) => {
let fullpath = getLocalFilePath(path);
allfiles.add(fullpath);
let encoding = typeof data === 'string' ? 'utf8' : null;
console.log("setWorkspaceFile", fullpath, data.length);
return new Promise( (resolve, reject) => {
fs.mkdir(_path.dirname(fullpath), {recursive:true}, (err,dirpath) => {
fs.writeFile(fullpath, data, {encoding:encoding}, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
});
}
}
// watch/unwatch files as browser loses/gains focus
window.addEventListener("blur", (e) => {
let localdir = getLocalDir();
if (!watcher && localdir) {
// watch files for changes
watcher = chokidar.watch(Array.from(allfiles), {
//awaitWriteFinish: {pollInterval:50,stabilityThreshold:300},
ignoreInitial: true
});
watcher.on('all', (event, path) => {
path = path.substring(localdir.length + 1); //strip off prefix
console.log('WATCH', event, path);
// we don't use awaitWriteFinish b/c we might close() the watcher
setTimeout(() => {
window.reloadWorkspaceFile(path);
}, 300);
});
console.log('watching', getLocalDir());
}
});
window.addEventListener("focus", (e) => {
if (watcher) {
watcher.close();
watcher = null;
}
console.log('stopped watching');
});
// from electron.js: set workspace root directory
/*
TODO
ipcRenderer.on('setWorkspaceRoot', (event, data) => {
wsroot = data.root;
var binpath = _path.join(wsroot, 'bin');
if (!fs.existsSync(binpath)) fs.mkdirSync(binpath, {});
console.log('setWorkspaceRoot', wsroot);
});
// from electron.js: file changed
ipcRenderer.on('fileChanged', (event, data) => {
var path = data.path;
window.reloadWorkspaceFile(path);
});
*/
console.log('loaded preload', window.alternateLocalFilesystem, window.startUI);
});