wm 1.0.1, crash handler demo, basic bundling support
This commit is contained in:
parent
349a9ddc0f
commit
ac2b338f13
10 changed files with 244 additions and 191 deletions
23
package.json
23
package.json
|
@ -2,17 +2,30 @@
|
||||||
"name": "neofunkin-1",
|
"name": "neofunkin-1",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "dist/index.js",
|
||||||
|
"build": {
|
||||||
|
"appId": "asper.games.neofunkin",
|
||||||
|
"productName": "NeoFunkin [EARLY ALPHA]",
|
||||||
|
"files": [
|
||||||
|
"dist/**/*"
|
||||||
|
],
|
||||||
|
"win": {
|
||||||
|
"target": "portable"
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"target": "AppImage"
|
||||||
|
}
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc && node scripts/finalizeBuild.js",
|
"build": "tsc && node scripts/finalizeBuild.js",
|
||||||
"electron": "electron dist"
|
"electron": "electron dist",
|
||||||
|
"bundle": "electron-builder"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"packageManager": "pnpm@10.4.1",
|
"packageManager": "pnpm@10.4.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"electron": "^35.0.0",
|
|
||||||
"express": "^4.21.2"
|
"express": "^4.21.2"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
|
@ -21,8 +34,10 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/electron": "^1.6.12",
|
"electron": "^35.0.0",
|
||||||
|
"@types/express": "^5.0.0",
|
||||||
"@types/node": "^22.13.9",
|
"@types/node": "^22.13.9",
|
||||||
|
"electron-builder": "^25.1.8",
|
||||||
"fs-extra": "^11.3.0",
|
"fs-extra": "^11.3.0",
|
||||||
"typescript": "^5.8.2"
|
"typescript": "^5.8.2"
|
||||||
}
|
}
|
||||||
|
|
124
src/index.ts
124
src/index.ts
|
@ -1,60 +1,72 @@
|
||||||
import wm from './lib/windowManager';
|
import wm from './lib/windowManager';
|
||||||
import { easeInOutQuad } from './lib/animations';
|
import { wait } from './lib/utils';
|
||||||
import { app } from 'electron';
|
import { app } from 'electron';
|
||||||
|
import displayCrashScreen from './lib/crash';
|
||||||
|
import express from 'express';
|
||||||
|
import path from 'path';
|
||||||
|
import { createServer } from 'http';
|
||||||
|
|
||||||
app.on('ready', () => {
|
declare global {
|
||||||
try {
|
namespace NodeJS {
|
||||||
const mw = wm.create({
|
interface Process {
|
||||||
w: 10,
|
nfenv?: any;
|
||||||
h: 10,
|
}
|
||||||
whp: true,
|
|
||||||
onCreate: (win: any) => {
|
|
||||||
win.loadURL('https://example.com');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
await wait(200);
|
|
||||||
wm.resize({ id: mw, w: 25, h: 25, smooth: true, p: true, fromCenter: true, ease: easeInOutQuad});
|
|
||||||
await wait(1000);
|
|
||||||
wm.resize({ id: mw, w: 50, h: 50, smooth: true, p: true, fromCenter: true, ease: easeInOutQuad});
|
|
||||||
await wait(650);
|
|
||||||
wm.move({ id: mw, x: 0, y: 50, p: true, fromCenter: true});
|
|
||||||
wm.resize({ id: mw, w: 33, h: 100, p: true });
|
|
||||||
wm.resize({ id: mw, w: 33, h: 10, smooth: true, p: true, ease: easeInOutQuad, anchor: 'bottom'});
|
|
||||||
await wait(500);
|
|
||||||
wm.move({ id: mw, x: 50, y: 50, p: true, fromCenter: true});
|
|
||||||
wm.resize({ id: mw, w: 33, h: 100, p: true });
|
|
||||||
wm.resize({ id: mw, w: 33, h: 10, smooth: true, p: true, ease: easeInOutQuad, anchor: 'bottom'});
|
|
||||||
await wait(500);
|
|
||||||
wm.move({ id: mw, x: 100, y: 50, p: true, fromCenter: true});
|
|
||||||
wm.resize({ id: mw, w: 33, h: 100, p: true });
|
|
||||||
wm.resize({ id: mw, w: 33, h: 10, smooth: true, p: true, ease: easeInOutQuad, anchor: 'bottom'});
|
|
||||||
await wait(500);
|
|
||||||
wm.resize({ id: mw, w: 100, h: 100, smooth: true, p: true, fromCenter: true, ease: easeInOutQuad});
|
|
||||||
await wait(550);
|
|
||||||
wm.resize({ id: mw, w: 100, h: 10, smooth: true, p: true, ease: easeInOutQuad, anchor: 'bottom'});
|
|
||||||
await wait(550);
|
|
||||||
wm.resize({ id: mw, w: 33, h: 10, smooth: true, p: true, fromCenter: true, ease: easeInOutQuad, anchor: 'bottom'});
|
|
||||||
await wait(500);
|
|
||||||
wm.move({ id: mw, x: 50, y: 50, p: true, fromCenter: true, ease: easeInOutQuad, smooth: true});
|
|
||||||
await wait(500);
|
|
||||||
wm.resize({ id: mw, w: 50, h: 50, smooth: true, p: true, fromCenter: true, ease: easeInOutQuad});
|
|
||||||
await wait(510);
|
|
||||||
wm.eval(mw, `document.querySelector('body').innerHTML = '<h1>NeoFunkin</h1>'`);
|
|
||||||
await wait(500);
|
|
||||||
wm.eval(mw, `document.querySelector('body').innerHTML = '<h1>MrpGimlom</h1>'`);
|
|
||||||
await wait(100);
|
|
||||||
wm.eval(mw, `document.querySelector('body').innerHTML = '<h1>NeoFunkin: Window manager</h1>'`);
|
|
||||||
await wait(500);
|
|
||||||
wm.eval(mw, `document.querySelector('body').innerHTML = '<h1>NeoFunkin: Window manager</h1><br>DEMO BY @TRUE1ANN, NEOFUNKIN IS PROPERTY OF ASPER'`);
|
|
||||||
await wait(1500);
|
|
||||||
wm.eval(mw, `document.querySelector('body').innerHTML = '<h1>NeoFunkin: Window manager</h1><br>DEMO BY @TRUE1ANN, NEOFUNKIN IS PROPERTY OF ASPER<br><small>this took so much to do omfg</small>'`);
|
|
||||||
})();
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
process.nfenv = {
|
||||||
|
serverPort: 65034 as number, // todo: pick a random one instead, not for now bcuz easier to debug and use /eval endpoint
|
||||||
|
crash: function (reason?: string) {
|
||||||
|
console.error('[nfcrash] Simulating a crash with reason:', reason || '*no reason*');
|
||||||
|
displayCrashScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startWebRoot() {
|
||||||
|
const app = express();
|
||||||
|
const server = createServer(app);
|
||||||
|
|
||||||
|
app.use(express.static(path.join(__dirname, 'webroot')));
|
||||||
|
|
||||||
|
app.get('/eval', (req: express.Request, res: express.Response): any => {
|
||||||
|
const code = req.headers['x-code'] as string;
|
||||||
|
|
||||||
|
if (!code) return res.status(400).send('No code provided');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = (function(require) {
|
||||||
|
return eval(code);
|
||||||
|
})(require);
|
||||||
|
res.status(200).send(result as string);
|
||||||
|
} catch (error: any) {
|
||||||
|
res.status(500).send(error.message as string);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// to-do: add echo webhook endpoint and endpoints to create more webhook endpoints (and also delete those)
|
||||||
|
|
||||||
|
server.listen(process.nfenv.serverPort, 'localhost');
|
||||||
|
};
|
||||||
|
|
||||||
|
startWebRoot().then(() => {
|
||||||
|
app.on('ready', async () => {
|
||||||
|
try {
|
||||||
|
const mw = wm.create({
|
||||||
|
w: 20,
|
||||||
|
h: 20,
|
||||||
|
whp: true,
|
||||||
|
onCreate: (win: any) => {
|
||||||
|
win.loadURL(`http://localhost:${process.nfenv.serverPort}/screens/mainmenu/test.html`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
wm.move({ id: mw, x: 50, y: 50, p: true, fromCenter: true })
|
||||||
|
await wait(500);
|
||||||
|
wm.resize({ id: mw, w: 40, h: 40, smooth: true, p: true});
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
process.nfenv.crash();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
34
src/lib/crash.ts
Normal file
34
src/lib/crash.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { wm, wdata } from './windowManager';
|
||||||
|
import { easeInOutQuad } from './animations';
|
||||||
|
import { wait, toP } from './utils'
|
||||||
|
import { app, screen } from 'electron';
|
||||||
|
|
||||||
|
export default async function displayCrashScreen() {
|
||||||
|
const bwdata = { ...wdata };
|
||||||
|
const win = wm.create({
|
||||||
|
w: 40,
|
||||||
|
h: 40,
|
||||||
|
whp: true,
|
||||||
|
onCreate: (win: any) => {
|
||||||
|
win.loadURL(`http://localhost:${process.nfenv.serverPort}/screens/crash/index.html`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const button = wm.create({
|
||||||
|
w: 10,
|
||||||
|
h: 5,
|
||||||
|
whp: true,
|
||||||
|
noBorder: true,
|
||||||
|
noBackground: true,
|
||||||
|
onCreate: (win: any) => {
|
||||||
|
win.loadURL(`http://localhost:${process.nfenv.serverPort}/screens/crash/exit.html`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await wait(300);
|
||||||
|
console.log('[nfcrash] Deleted all windows');
|
||||||
|
Object.keys(bwdata).forEach(async (id) => {
|
||||||
|
bwdata[id].win.destroy(); // Note: We dont use wm.destroy(...) so ...onDestroy wont get called and cause trouble
|
||||||
|
});
|
||||||
|
const { width: sw, height: sh } = screen.getPrimaryDisplay().bounds;
|
||||||
|
wm.move({ id: button, y: 75, x: 50, p: true, fromCenter: true });
|
||||||
|
wm.follow.start(button, win);
|
||||||
|
};
|
4
src/lib/utils.ts
Normal file
4
src/lib/utils.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
export function toP(value: number, percentage: number) {
|
||||||
|
return value * (percentage / 100) as number;
|
||||||
|
}
|
|
@ -1,17 +1,16 @@
|
||||||
import { BrowserWindow, screen } from 'electron';
|
import { BrowserWindow, Rectangle, screen } from 'electron';
|
||||||
import { wm_create_conf, wm_move_conf, wm_resize_conf } from './../types/wm';
|
import { wm_create_conf, wm_move_conf, wm_resize_conf } from './../types/wm';
|
||||||
|
import { toP } from './utils';
|
||||||
|
|
||||||
interface WindowData {
|
interface WindowData {
|
||||||
win: BrowserWindow;
|
win: BrowserWindow;
|
||||||
conf: wm_create_conf;
|
conf: wm_create_conf;
|
||||||
|
follows?: string;
|
||||||
|
followStuff?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
let wdata: { [key: string]: WindowData } = {};
|
let wdata: { [key: string]: WindowData } = {};
|
||||||
|
|
||||||
function toP(value: number, percentage: number) {
|
|
||||||
return value * (percentage / 100) as number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const wm = {
|
const wm = {
|
||||||
create: function (c: wm_create_conf) {
|
create: function (c: wm_create_conf) {
|
||||||
const wid = Array.from({ length: 8 }, () => '0123456789abcdef'[Math.floor(Math.random() * 16)]).join('');
|
const wid = Array.from({ length: 8 }, () => '0123456789abcdef'[Math.floor(Math.random() * 16)]).join('');
|
||||||
|
@ -21,7 +20,7 @@ const wm = {
|
||||||
width: c.whp ? toP(sw, c.w!) : c.w,
|
width: c.whp ? toP(sw, c.w!) : c.w,
|
||||||
height: c.whp ? toP(sh, c.h!) : c.h,
|
height: c.whp ? toP(sh, c.h!) : c.h,
|
||||||
x: c.xyp ? toP(sw, c.x!) : c.x,
|
x: c.xyp ? toP(sw, c.x!) : c.x,
|
||||||
y: c.xyp ? toP(sh, c.y!) : c.x,
|
y: c.xyp ? toP(sh, c.y!) : c.y,
|
||||||
frame: !c.noBorder,
|
frame: !c.noBorder,
|
||||||
transparent: c.noBackground,
|
transparent: c.noBackground,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
|
@ -30,7 +29,7 @@ const wm = {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
wdata[wid] = { win: win, conf: c }; // for further pullin
|
wdata[wid] = { win: win, conf: c };
|
||||||
win.setMenuBarVisibility(false); // tell me WHY should i NOT make this the default
|
win.setMenuBarVisibility(false); // tell me WHY should i NOT make this the default
|
||||||
|
|
||||||
if (typeof c.onMinimize === 'function') win.on('minimize', () => c.onMinimize!(win));
|
if (typeof c.onMinimize === 'function') win.on('minimize', () => c.onMinimize!(win));
|
||||||
|
@ -43,7 +42,7 @@ const wm = {
|
||||||
if (c.onCreate) c.onCreate(win);
|
if (c.onCreate) c.onCreate(win);
|
||||||
return wid // just so you can reference it later on
|
return wid // just so you can reference it later on
|
||||||
},
|
},
|
||||||
destroy: async function (id: number) {
|
destroy: async function (id: string) {
|
||||||
console.log('[wm] destroy', id);
|
console.log('[wm] destroy', id);
|
||||||
const win = wdata[id];
|
const win = wdata[id];
|
||||||
if (typeof win.conf.onDestroy === 'function') await win.conf.onDestroy!(win.win);
|
if (typeof win.conf.onDestroy === 'function') await win.conf.onDestroy!(win.win);
|
||||||
|
@ -108,86 +107,85 @@ const wm = {
|
||||||
resize: function (c: wm_resize_conf) {
|
resize: function (c: wm_resize_conf) {
|
||||||
console.log('[wm] resize', c.id);
|
console.log('[wm] resize', c.id);
|
||||||
const win = wdata[c.id];
|
const win = wdata[c.id];
|
||||||
let wb = win.win.getBounds();
|
if (!win || !win.win) {
|
||||||
|
console.error('Window data not found for id:', c.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const wb = win.win.getBounds();
|
||||||
const sW = wb.width, sH = wb.height;
|
const sW = wb.width, sH = wb.height;
|
||||||
|
// Use the given dimensions if provided; otherwise fall back to the current window size
|
||||||
c.w = c.w === undefined ? sW : c.w;
|
c.w = c.w === undefined ? sW : c.w;
|
||||||
c.h = c.h === undefined ? sH : c.h;
|
c.h = c.h === undefined ? sH : c.h;
|
||||||
const duration = c.duration || 500;
|
const duration = c.duration || 500;
|
||||||
const tick = c.tick || 16;
|
const tick = c.tick || 16;
|
||||||
const totalSteps = Math.floor(duration / tick);
|
const totalSteps = Math.floor(duration / tick);
|
||||||
|
// Get primary display bounds – make sure that screen is imported from Electron
|
||||||
const { width: sw, height: sh } = screen.getPrimaryDisplay().bounds;
|
const { width: sw, height: sh } = screen.getPrimaryDisplay().bounds;
|
||||||
let targetW = toP(c.p ? sw : 1, c.w);
|
// Calculate target width and height using percentage conversion if needed.
|
||||||
let targetH = toP(c.p ? sh : 1, c.h);
|
// Assuming toP converts a percentage value to pixels
|
||||||
|
const targetW = toP(c.p ? sw : 1, c.w);
|
||||||
|
const targetH = toP(c.p ? sh : 1, c.h);
|
||||||
|
|
||||||
|
// Compute window center for centering if needed
|
||||||
const centerX = wb.x + wb.width / 2;
|
const centerX = wb.x + wb.width / 2;
|
||||||
const centerY = wb.y + wb.height / 2;
|
const centerY = wb.y + wb.height / 2;
|
||||||
|
|
||||||
|
// Adjust position based on the chosen anchor.
|
||||||
const getAnchoredPosition = (curWidth: number, curHeight: number) => {
|
const getAnchoredPosition = (curWidth: number, curHeight: number) => {
|
||||||
let newX = wb.x;
|
let newX = wb.x;
|
||||||
let newY = wb.y;
|
let newY = wb.y;
|
||||||
if (!c.fromCenter && c.anchor) {
|
switch (c.anchor) {
|
||||||
switch (c.anchor) {
|
case 'top':
|
||||||
case 'top':
|
// Align to top – x remains same
|
||||||
newX = wb.x;
|
newY = wb.y;
|
||||||
newY = wb.y;
|
break;
|
||||||
break;
|
case 'bottom':
|
||||||
case 'bottom':
|
// Align to bottom
|
||||||
newX = wb.x;
|
newY = wb.y + wb.height - curHeight;
|
||||||
newY = wb.y + wb.height - curHeight;
|
break;
|
||||||
break;
|
case 'left':
|
||||||
case 'left':
|
// Align to left – y remains same
|
||||||
newX = wb.x;
|
newX = wb.x;
|
||||||
newY = wb.y;
|
break;
|
||||||
break;
|
case 'right':
|
||||||
case 'right':
|
// Align to right
|
||||||
newX = wb.x + wb.width - curWidth;
|
newX = wb.x + wb.width - curWidth;
|
||||||
newY = wb.y;
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
// Default: center the resized window relative to the original bounds
|
||||||
newX = wb.x;
|
newX = centerX - curWidth / 2;
|
||||||
newY = wb.y;
|
newY = centerY - curHeight / 2;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return { newX, newY };
|
return { newX, newY };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Animation function to smooth the resize
|
||||||
const animate = () => {
|
const animate = () => {
|
||||||
let currentStep = 0;
|
let currentStep = 0;
|
||||||
const startW = wb.width;
|
const startW = wb.width;
|
||||||
const startH = wb.height;
|
const startH = wb.height;
|
||||||
const step = () => {
|
const step = () => {
|
||||||
currentStep++;
|
currentStep++;
|
||||||
const t = currentStep / totalSteps;
|
const t = Math.min(currentStep / totalSteps, 1);
|
||||||
const eT = c.ease ? c.ease(t) : t;
|
const eT = c.ease ? c.ease(t) : t;
|
||||||
const newW = startW + (targetW - startW) * eT;
|
const newW = startW + (targetW - startW) * eT;
|
||||||
const newH = startH + (targetH - startH) * eT;
|
const newH = startH + (targetH - startH) * eT;
|
||||||
let newX: number, newY: number;
|
// Get new positions based on the current width and height
|
||||||
if (c.fromCenter) {
|
const pos = getAnchoredPosition(Math.round(newW), Math.round(newH));
|
||||||
newX = Math.round(centerX - newW / 2);
|
|
||||||
newY = Math.round(centerY - newH / 2);
|
|
||||||
} else {
|
|
||||||
const pos = getAnchoredPosition(Math.round(newW), Math.round(newH));
|
|
||||||
newX = pos.newX;
|
|
||||||
newY = pos.newY;
|
|
||||||
}
|
|
||||||
win.win.setBounds({
|
win.win.setBounds({
|
||||||
x: newX,
|
x: pos.newX,
|
||||||
y: newY,
|
y: pos.newY,
|
||||||
width: Math.round(newW),
|
width: Math.round(newW),
|
||||||
height: Math.round(newH)
|
height: Math.round(newH)
|
||||||
});
|
});
|
||||||
if (currentStep < totalSteps) {
|
if (currentStep < totalSteps) {
|
||||||
setTimeout(step, tick);
|
setTimeout(step, tick);
|
||||||
} else {
|
} else {
|
||||||
let finalX: number, finalY: number;
|
// Make sure to set final bounds exactly
|
||||||
if (c.fromCenter) {
|
const finalPos = getAnchoredPosition(Math.round(targetW), Math.round(targetH));
|
||||||
finalX = Math.round(centerX - targetW / 2);
|
|
||||||
finalY = Math.round(centerY - targetH / 2);
|
|
||||||
} else {
|
|
||||||
const pos = getAnchoredPosition(Math.round(targetW), Math.round(targetH));
|
|
||||||
finalX = pos.newX;
|
|
||||||
finalY = pos.newY;
|
|
||||||
}
|
|
||||||
win.win.setBounds({
|
win.win.setBounds({
|
||||||
x: finalX,
|
x: finalPos.newX,
|
||||||
y: finalY,
|
y: finalPos.newY,
|
||||||
width: Math.round(targetW),
|
width: Math.round(targetW),
|
||||||
height: Math.round(targetH)
|
height: Math.round(targetH)
|
||||||
});
|
});
|
||||||
|
@ -195,21 +193,15 @@ const wm = {
|
||||||
};
|
};
|
||||||
step();
|
step();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Use animate if smooth animation is requested, otherwise set bounds immediately.
|
||||||
if (c.smooth) {
|
if (c.smooth) {
|
||||||
animate();
|
animate();
|
||||||
} else {
|
} else {
|
||||||
let finalX: number, finalY: number;
|
const finalPos = getAnchoredPosition(Math.round(targetW), Math.round(targetH));
|
||||||
if (c.fromCenter) {
|
|
||||||
finalX = Math.round(centerX - targetW / 2);
|
|
||||||
finalY = Math.round(centerY - targetH / 2);
|
|
||||||
} else {
|
|
||||||
const pos = getAnchoredPosition(Math.round(targetW), Math.round(targetH));
|
|
||||||
finalX = pos.newX;
|
|
||||||
finalY = pos.newY;
|
|
||||||
}
|
|
||||||
win.win.setBounds({
|
win.win.setBounds({
|
||||||
x: finalX,
|
x: finalPos.newX,
|
||||||
y: finalY,
|
y: finalPos.newY,
|
||||||
width: Math.round(targetW),
|
width: Math.round(targetW),
|
||||||
height: Math.round(targetH)
|
height: Math.round(targetH)
|
||||||
});
|
});
|
||||||
|
@ -222,6 +214,42 @@ const wm = {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error executing JavaScript:', error);
|
console.error('Error executing JavaScript:', error);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
follow: {
|
||||||
|
INTERNAL_follow: function (id: string) {
|
||||||
|
const leadWin = wdata[wdata[id].follows!].win;
|
||||||
|
const followWin = wdata[id].win;
|
||||||
|
const leadBounds = leadWin.getBounds();
|
||||||
|
const followBounds = followWin.getBounds();
|
||||||
|
|
||||||
|
if (wdata[id].followStuff === undefined) {
|
||||||
|
wdata[id].followStuff = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wdata[id].followStuff.offsetX === undefined || wdata[id].followStuff.offsetY === undefined) {
|
||||||
|
wdata[id].followStuff.offsetX = followBounds.x - leadBounds.x;
|
||||||
|
wdata[id].followStuff.offsetY = followBounds.y - leadBounds.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newX = leadBounds.x + wdata[id].followStuff.offsetX;
|
||||||
|
const newY = leadBounds.y + wdata[id].followStuff.offsetY;
|
||||||
|
|
||||||
|
followWin.setPosition(newX, newY);
|
||||||
|
followWin.show();
|
||||||
|
},
|
||||||
|
INTERNAL_followfocus: function (id: string) {
|
||||||
|
wdata[id].win.show();
|
||||||
|
},
|
||||||
|
start: function (id: string, follows: string) {
|
||||||
|
console.log('[wm]', id, 'follows', follows);
|
||||||
|
wdata[id].follows = follows;
|
||||||
|
wdata[follows].win.on('move', this.INTERNAL_follow.bind(this, id));
|
||||||
|
wdata[follows].win.on('focus', this.INTERNAL_followfocus.bind(this, id));
|
||||||
|
},
|
||||||
|
stop: function (id: string) {
|
||||||
|
wdata[wdata[id].follows!].win.removeListener('move', this.INTERNAL_follow.bind(this, id));
|
||||||
|
wdata[wdata[id].follows!].win.removeListener('focus', this.INTERNAL_followfocus.bind(this, id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
60
src/types/wm.d.ts
vendored
60
src/types/wm.d.ts
vendored
|
@ -1,60 +0,0 @@
|
||||||
import { BrowserWindow } from 'electron';
|
|
||||||
|
|
||||||
export type wm_create_conf = {
|
|
||||||
w?: number; // Width
|
|
||||||
h?: number; // Height
|
|
||||||
x?: number; // X start pos
|
|
||||||
y?: number; // Y start pos
|
|
||||||
whp?: boolean; // is w and h in percentages
|
|
||||||
xyp?: boolean; // is x and y in percentages
|
|
||||||
noBorder?: boolean; // Hide OS window decorations
|
|
||||||
noBackground?: boolean; // Hide window background
|
|
||||||
onCreate?: Function; // wm.create(...)
|
|
||||||
onMinimize?: Function; // USER pressed '_'
|
|
||||||
onMaximize?: Function; // USER pressed '[]'
|
|
||||||
onRestore?: Function; // USER pressed '[]]'
|
|
||||||
onUnfocus?: Function; // USER unfocused on the window
|
|
||||||
onFocus?: Function; // USER focused on the window
|
|
||||||
onClose?: Function; // USER pressed 'x'
|
|
||||||
onDestroy?: Function; // USER pressed 'x' OR wm.destroy(...)
|
|
||||||
};
|
|
||||||
|
|
||||||
export type wm_move_conf = {
|
|
||||||
id: string; // ID of the window to perform everythin on
|
|
||||||
x?: number; // New X
|
|
||||||
y?: number; // New Y
|
|
||||||
p?: boolean; // Use percentages
|
|
||||||
fromCenter?: boolean; // Should be performed from the center of the window?
|
|
||||||
smooth?: boolean; // Should it be smooth?
|
|
||||||
ease?: Function; // Calculates the easing (lib/animations.ts)
|
|
||||||
duration?: number; // (ms) The total duration of the animation
|
|
||||||
tick?: number; // (ms) Delay between window movenments
|
|
||||||
}
|
|
||||||
|
|
||||||
export type wm_resize_conf = {
|
|
||||||
id: string; // ID of the window to perform everythin on
|
|
||||||
w?: number; // New width
|
|
||||||
h?: number; // New height
|
|
||||||
p?: boolean; // Use percentages
|
|
||||||
fromCenter?: boolean; // Should be performed from the center of the window?
|
|
||||||
smooth?: boolean; // Should it be smooth?
|
|
||||||
ease?: Function; // Calculates the easing (lib/animations.ts)
|
|
||||||
duration?: number; // (ms) The total duration of the animation
|
|
||||||
tick?: number; // (ms) Delay between window resizings
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WindowData {
|
|
||||||
win: BrowserWindow;
|
|
||||||
conf: wm_create_conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare const wm: {
|
|
||||||
create: (c: wm_create_conf) => string;
|
|
||||||
destroy: (id: string) => Promise<void>;
|
|
||||||
move: (c: wm_move_conf) => void;
|
|
||||||
resize: (c: wm_resize_conf) => void;
|
|
||||||
eval: (id: string, code: Function) => Promise<any>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default wm;
|
|
||||||
export { WindowData, wm };
|
|
|
@ -1,6 +1,6 @@
|
||||||
export type wm_create_conf = {
|
export type wm_create_conf = {
|
||||||
w?: number; // Width
|
w: number; // Width
|
||||||
h?: number; // Height
|
h: number; // Height
|
||||||
x?: number; // X start pos
|
x?: number; // X start pos
|
||||||
y?: number; // Y start pos
|
y?: number; // Y start pos
|
||||||
whp?: boolean; // is w and h in percentages
|
whp?: boolean; // is w and h in percentages
|
||||||
|
@ -34,10 +34,9 @@ export type wm_resize_conf = {
|
||||||
w?: number; // New width
|
w?: number; // New width
|
||||||
h?: number; // New height
|
h?: number; // New height
|
||||||
p?: boolean; // Use percentages
|
p?: boolean; // Use percentages
|
||||||
fromCenter?: boolean; // Should be performed from the center of the window?
|
|
||||||
smooth?: boolean; // Should it be smooth?
|
smooth?: boolean; // Should it be smooth?
|
||||||
ease?: Function; // Calculates the easing (lib/animations.ts)
|
ease?: Function; // Calculates the easing (lib/animations.ts)
|
||||||
duration?: number; // (ms) The total duration of the animation
|
duration?: number; // (ms) The total duration of the animation
|
||||||
tick?: number; // (ms) Delay between window resizings
|
tick?: number; // (ms) Delay between window resizings
|
||||||
anchor?: string; // bottom, top, left, right
|
anchor?: string; // bottom, top, left, right, center
|
||||||
}
|
}
|
18
src/webroot/screens/crash/exit.html
Normal file
18
src/webroot/screens/crash/exit.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>NeoFunkin</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<button style="width: 100vw; height: 100vh; margin: 0;" onclick="fetch('/eval', { headers: { 'x-code': 'process.exit(1)' } });">Exit</button>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -5,5 +5,6 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Uh oh.</h1>
|
<h1>Uh oh.</h1>
|
||||||
|
NeoFunkin crashed. Check logs for more details.
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -5,5 +5,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Hello, World!</h1>
|
<h1>Hello, World!</h1>
|
||||||
|
<button onclick="fetch('/eval', { headers: { 'x-code': 'process.nfenv.crash(`funny`);' } });">Crash</button>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
Add table
Reference in a new issue