V2 monorepo (#285)
* refactor(project structure): UI * refactor(project structure): extract utilities * refactor(project structure): remove unused * refactor(project structure): electron * refactor(project structure): server refactor: migrate to vitest refactor: monorepo config * refactor: extract application menu * refactor: exit process * refactor: extract tray menu * chore: electron build * Added Seconds in studio clock #282 --------- Co-authored-by: Fabian Posenau <fabian@fphome.de> --------- Co-authored-by: Fabian Posenau <fabian.p99@gmx.de> Co-authored-by: Fabian Posenau <fabian@fphome.de>
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
dist
|
||||
assets
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
},
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended"
|
||||
],
|
||||
"plugins": [],
|
||||
"rules": {}
|
||||
}
|
||||
|
After Width: | Height: | Size: 567 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 47 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="275" height="275" fill="none" viewBox="0 0 275 275">
|
||||
<path fill="#4D4D4D" d="M49.83 133.919c0-18.343 3.531-34.692 10.595-49.048 7.064-14.355 17.204-25.464 30.42-33.325 13.33-7.861 28.768-11.792 46.313-11.792 24.952 0 45.288 7.633 61.011 22.9 15.837 15.267 24.666 36.003 26.489 62.207l.342 12.647c0 28.369-7.918 51.155-23.755 68.359-15.836 17.09-37.085 25.635-63.745 25.635-26.66 0-47.965-8.545-63.916-25.635-15.836-17.09-23.755-40.332-23.755-69.726v-2.222zm49.389 3.589c0 17.545 3.304 30.989 9.912 40.332 6.608 9.228 16.064 13.843 28.369 13.843 11.963 0 21.305-4.558 28.028-13.672 6.722-9.229 10.083-23.926 10.083-44.092 0-17.204-3.361-30.534-10.083-39.99-6.723-9.457-16.179-14.185-28.37-14.185-12.076 0-21.419 4.728-28.027 14.185-6.608 9.342-9.912 23.869-9.912 43.579z"/>
|
||||
<mask id="a" width="177" height="193" x="49" y="39" maskUnits="userSpaceOnUse">
|
||||
<path fill="#fff" d="M49.83 133.919c0-18.343 3.531-34.692 10.595-49.048 7.064-14.355 17.204-25.464 30.42-33.325 13.33-7.861 28.768-11.792 46.313-11.792 24.952 0 45.288 7.633 61.011 22.9 15.837 15.267 24.666 36.003 26.489 62.207l.342 12.647c0 28.369-7.918 51.155-23.755 68.359-15.836 17.09-37.085 25.635-63.745 25.635-26.66 0-47.965-8.545-63.916-25.635-15.836-17.09-23.755-40.332-23.755-69.726v-2.222zm49.389 3.589c0 17.545 3.304 30.989 9.912 40.332 6.608 9.228 16.064 13.843 28.369 13.843 11.963 0 21.305-4.558 28.028-13.672 6.722-9.229 10.083-23.926 10.083-44.092 0-17.204-3.361-30.534-10.083-39.99-6.723-9.457-16.179-14.185-28.37-14.185-12.076 0-21.419 4.728-28.027 14.185-6.608 9.342-9.912 23.869-9.912 43.579z"/>
|
||||
</mask>
|
||||
<g mask="url(#a)">
|
||||
<path fill="url(#paint0_linear)" d="M19.07 95.347c15.556-6.524 14.288-34.989 22.08-30.11 9.74 6.099 0-23.084 13.549-16.56 21.714 10.455-10.977 18.645 12.296 32.007 23.274 13.362 117.508 24.346 150.898 90.045 12.812 25.21 19.341 32.653 21.836 34.543 1.508-.13 1.78 1.349 0 0-.38.033-.838.168-1.362.476-5.516 3.245-106.876 85.867-148.54 34.124C48.162 188.129 2.25 140.221 14.31 127.463c9.648-10.205 1.393-25.103 4.76-32.116z"/>
|
||||
<path fill="url(#paint1_linear)" d="M42.153 69.754c-7.791-4.88-.412 13.939-24.087 22.582-3.366 7.013 4.63 19.904-5.018 30.109-12.06 12.757 37.623 69.197 79.288 120.94 41.664 51.743 123.453-24.356 128.969-27.6 5.515-3.245 14.092 10.618-10.114-37.154-33.382-65.882-126.481-75.197-149.59-98.582-20.018-20.26 33.92-42.547 10.662-37.896-30.11 6.022-20.37 33.7-30.11 27.6z"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="93.96" x2="67.62" y1="-33.993" y2="351.251" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-color="#414141" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="88.433" x2="62.092" y1="-37.321" y2="347.924" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF005C" stop-opacity=".74"/>
|
||||
<stop offset="0"/>
|
||||
<stop offset="1" stop-color="#242424" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 151 KiB |
@@ -0,0 +1,18 @@
|
||||
module.exports = {
|
||||
appIni: {
|
||||
shutdownCode: 99,
|
||||
},
|
||||
reactAppUrl: {
|
||||
development: 'http://localhost:3000/editor',
|
||||
production: 'http://localhost:4001/editor',
|
||||
},
|
||||
externalUrls: {
|
||||
help: 'https://cpvalente.gitbook.io/ontime/',
|
||||
},
|
||||
server: {
|
||||
pathToEntrypoint: '../extraResources/server/index.cjs'
|
||||
},
|
||||
assets: {
|
||||
pathToAssets: './assets/',
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,272 @@
|
||||
const {
|
||||
app,
|
||||
BrowserWindow,
|
||||
Menu,
|
||||
globalShortcut,
|
||||
Tray,
|
||||
dialog,
|
||||
ipcMain,
|
||||
shell,
|
||||
Notification,
|
||||
} = require('electron');
|
||||
const path = require('path');
|
||||
const electronConfig = require('./electron.config');
|
||||
|
||||
const env = process.env.NODE_ENV || 'production';
|
||||
const isProduction = env === 'production';
|
||||
const isMac = process.platform === 'darwin';
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
// path to server
|
||||
const nodePath = isProduction
|
||||
? path.join(__dirname, electronConfig.server.pathToEntrypoint)
|
||||
: path.join(__dirname, '../server/dist/index.cjs');
|
||||
|
||||
if (!isProduction) {
|
||||
console.log(`Electron running in ${env} environment`);
|
||||
console.log(`Ontime server at ${nodePath}`);
|
||||
process.traceProcessWarnings = true;
|
||||
}
|
||||
|
||||
// path to icons
|
||||
const trayIcon = path.join(__dirname, electronConfig.assets.pathToAssets, 'background.png');
|
||||
const appIcon = path.join(__dirname, electronConfig.assets.pathToAssets, 'logo.png');
|
||||
let loaded = 'Nothing loaded';
|
||||
let isQuitting = false;
|
||||
|
||||
// initialise
|
||||
let win;
|
||||
let splash;
|
||||
let tray = null;
|
||||
|
||||
(async () => {
|
||||
|
||||
// in dev mode, we expect both UI and server to be running
|
||||
if (!isProduction) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const ontimeServer = require(nodePath)
|
||||
const { startServer, startOSCServer } = ontimeServer;
|
||||
|
||||
// Start express server
|
||||
loaded = await startServer();
|
||||
|
||||
// Start OSC Server
|
||||
await startOSCServer();
|
||||
} catch (error) {
|
||||
loaded = error;
|
||||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* @description utility function to create a notification
|
||||
* @param title
|
||||
* @param text
|
||||
*/
|
||||
function showNotification(title, text) {
|
||||
new Notification({
|
||||
title,
|
||||
body: text,
|
||||
silent: true,
|
||||
}).show();
|
||||
}
|
||||
|
||||
function appShutdown() {
|
||||
// terminate node service
|
||||
(async () => {
|
||||
console.log('asking for shutdown 1', nodePath)
|
||||
const ontimeServer = require(nodePath)
|
||||
console.log('asking for shutdown 2', ontimeServer)
|
||||
const { shutdown } = ontimeServer;
|
||||
await shutdown(electronConfig.appIni.shutdownCode);
|
||||
})();
|
||||
|
||||
isQuitting = true;
|
||||
tray.destroy();
|
||||
win.destroy();
|
||||
app.quit();
|
||||
}
|
||||
|
||||
function bringToFront() {
|
||||
win.show();
|
||||
win.focus();
|
||||
}
|
||||
|
||||
function askToQuit() {
|
||||
bringToFront();
|
||||
win.send('user-request-shutdown');
|
||||
}
|
||||
|
||||
// Ensure there isn't another instance of the app running already
|
||||
const lock = app.requestSingleInstanceLock();
|
||||
if (!lock) {
|
||||
dialog.showErrorBox('Multiple instances', 'An instance of the App is already running.');
|
||||
app.quit();
|
||||
} else {
|
||||
app.on('second-instance', () => {
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (win) {
|
||||
if (win.isMinimized()) {
|
||||
win.restore();
|
||||
}
|
||||
bringToFront();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
splash = new BrowserWindow({
|
||||
width: 333,
|
||||
height: 333,
|
||||
transparent: true,
|
||||
icon: appIcon,
|
||||
resizable: false,
|
||||
frame: false,
|
||||
alwaysOnTop: true,
|
||||
focusable: false,
|
||||
skipTaskbar: true,
|
||||
});
|
||||
splash.setIgnoreMouseEvents(true);
|
||||
splash.loadURL(`file://${__dirname}/src/splash/splash.html`);
|
||||
|
||||
win = new BrowserWindow({
|
||||
width: 1920,
|
||||
height: 1000,
|
||||
minWidth: 525,
|
||||
minHeight: 405,
|
||||
maxWidth: 1920,
|
||||
maxHeight: 1440,
|
||||
backgroundColor: '#101010', // $gray-1350
|
||||
icon: appIcon,
|
||||
show: false,
|
||||
textAreasAreResizable: false,
|
||||
enableWebSQL: false,
|
||||
darkTheme: true,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, './src/preload.js'),
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
},
|
||||
});
|
||||
|
||||
win.setMenu(null);
|
||||
}
|
||||
|
||||
app.disableHardwareAcceleration();
|
||||
app.whenReady().then(() => {
|
||||
// Set app title in windows
|
||||
if (isWindows) {
|
||||
app.setAppUserModelId(app.name);
|
||||
}
|
||||
|
||||
createWindow();
|
||||
|
||||
// register global shortcuts
|
||||
// (available regardless of whether app is in focus)
|
||||
// bring focus to window
|
||||
globalShortcut.register('Alt+1', () => {
|
||||
bringToFront();
|
||||
});
|
||||
|
||||
// cheat to schedule process
|
||||
setTimeout(() => {
|
||||
// Load page served by node or use React dev run
|
||||
const clientUrl = isProduction
|
||||
? electronConfig.reactAppUrl.production
|
||||
: electronConfig.reactAppUrl.development;
|
||||
|
||||
win.loadURL(clientUrl).then(() => {
|
||||
win.webContents.setBackgroundThrottling(false);
|
||||
|
||||
win.show();
|
||||
win.focus();
|
||||
|
||||
splash.destroy();
|
||||
|
||||
if (typeof loaded === 'string') {
|
||||
tray.setToolTip(loaded);
|
||||
} else {
|
||||
tray.setToolTip('Initialising error: please restart Ontime');
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
|
||||
// recreate window if no others open
|
||||
app.on('activate', () => {
|
||||
win.show();
|
||||
});
|
||||
|
||||
// Hide on close
|
||||
win.on('close', function (event) {
|
||||
event.preventDefault();
|
||||
if (!isQuitting) {
|
||||
showNotification('Window Closed', 'App running in background');
|
||||
win.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// create tray
|
||||
tray = new Tray(trayIcon);
|
||||
|
||||
// Define context menu
|
||||
const { getTrayMenu } = require('./src/menu/trayMenu.js');
|
||||
const trayMenuTemplate = getTrayMenu(bringToFront, askToQuit)
|
||||
const trayContextMenu = Menu.buildFromTemplate(trayMenuTemplate);
|
||||
tray.setContextMenu(trayContextMenu);
|
||||
});
|
||||
|
||||
const { getApplicationMenu } = require('./src/menu/applicationMenu.js');
|
||||
const template = getApplicationMenu(isMac, askToQuit)
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
Menu.setApplicationMenu(menu);
|
||||
|
||||
// unregister shortcuts before quitting
|
||||
app.once('will-quit', () => {
|
||||
globalShortcut.unregisterAll();
|
||||
});
|
||||
|
||||
// Get messages from react
|
||||
// Test message
|
||||
ipcMain.on('test-message', (event, arg) => {
|
||||
showNotification('Test Message', 'test from react', arg);
|
||||
});
|
||||
|
||||
// Ask for main window reload
|
||||
// Test message
|
||||
ipcMain.on('reload', () => {
|
||||
win?.reload();
|
||||
});
|
||||
|
||||
// Terminate
|
||||
ipcMain.on('shutdown', () => {
|
||||
console.log('Electron got IPC shutdown');
|
||||
appShutdown();
|
||||
});
|
||||
|
||||
// Window manipulation
|
||||
ipcMain.on('set-window', (event, arg) => {
|
||||
switch (arg) {
|
||||
case 'to-max':
|
||||
win.maximize();
|
||||
break;
|
||||
case 'to-tray':
|
||||
win.maximize();
|
||||
break;
|
||||
case 'show-dev':
|
||||
win.webContents.openDevTools({ mode: 'detach' });
|
||||
break;
|
||||
default:
|
||||
console.log('Electron unhandled window request', arg)
|
||||
}
|
||||
});
|
||||
|
||||
// Open links external
|
||||
ipcMain.on('send-to-link', (event, arg) => {
|
||||
if (arg === 'help') {
|
||||
shell.openExternal(electronConfig.externalUrls.help);
|
||||
} else {
|
||||
shell.openExternal(arg);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"name": "ontime",
|
||||
"version": "2.0.0-alpha",
|
||||
"author": "Carlos Valente",
|
||||
"description": "Time keeping for live events",
|
||||
"repository": "https://github.com/cpvalente/ontime",
|
||||
"keywords": [
|
||||
"lighdev",
|
||||
"ontime",
|
||||
"timer"
|
||||
],
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "main.js",
|
||||
"devDependencies": {
|
||||
"electron": "^21.1.0",
|
||||
"electron-builder": "^23.6.0",
|
||||
"eslint": "^8.31.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"prettier": "^2.8.3"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "",
|
||||
"dev:electron": "NODE_ENV=development electron .",
|
||||
"dist-win": "electron-builder --publish=never --x64 --win",
|
||||
"dist-mac": "electron-builder --publish=never --x64 --mac",
|
||||
"dist-linux": "electron-builder --publish=never --x64 --linux",
|
||||
"cleanup": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
||||
},
|
||||
"build": {
|
||||
"productName": "ontime",
|
||||
"appId": "no.lightdev.ontime",
|
||||
"asar": true,
|
||||
"dmg": {
|
||||
"artifactName": "ontime-macOS.dmg",
|
||||
"icon": "icon.icns"
|
||||
},
|
||||
"mac": {
|
||||
"target": "dmg",
|
||||
"category": "public.app-category.productivity",
|
||||
"icon": "icon.icns"
|
||||
},
|
||||
"win": {
|
||||
"target": "nsis"
|
||||
},
|
||||
"nsis": {
|
||||
"artifactName": "ontime-win64.exe",
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"allowElevation": true,
|
||||
"createDesktopShortcut": true,
|
||||
"createStartMenuShortcut": true,
|
||||
"deleteAppDataOnUninstall": true,
|
||||
"runAfterFinish": false,
|
||||
"installerIcon": "icon.ico",
|
||||
"installerSidebar": "ontime-install.bmp",
|
||||
"uninstallerSidebar": "ontime-uninstall.bmp"
|
||||
},
|
||||
"linux": {
|
||||
"target": "AppImage",
|
||||
"artifactName": "ontime-linux.AppImage"
|
||||
},
|
||||
"files": [
|
||||
"**/*",
|
||||
"assets/",
|
||||
"!**/{yarn.lock,yarn-error.log}",
|
||||
"!**/{pnpm-lock.yaml}",
|
||||
"!**/{test,tests,__test__,__tests__}",
|
||||
"!**/{mock,mocks,__mock__,__mocks__}",
|
||||
"!*{.spec.js,*.test.js,*.spec.ts,.test.ts}"
|
||||
],
|
||||
"directories": {
|
||||
"buildResources": "./assets/"
|
||||
},
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "../client/build",
|
||||
"to": "extraResources/client/",
|
||||
"filter": [
|
||||
"**/*",
|
||||
"!**/{yarn.lock,yarn-error.log}",
|
||||
"!**/{pnpm-lock.yaml}",
|
||||
"!**/{test,tests,__test__,__tests__}",
|
||||
"!**/{mock,mocks,__mock__,__mocks__}",
|
||||
"!*{.spec.js,*.test.js,*.spec.ts,.test.ts}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"from": "../server/dist/",
|
||||
"to": "extraResources/server/",
|
||||
"filter": [
|
||||
"**/*",
|
||||
"!**/{yarn.lock,yarn-error.log}",
|
||||
"!**/{pnpm-lock.yaml}",
|
||||
"!**/{test,tests,__test__,__tests__}",
|
||||
"!**/{mock,mocks,__mock__,__mocks__}",
|
||||
"!*{.spec.js,*.test.js,*.spec.ts,.test.ts}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
const { shell } = require('electron');
|
||||
/**
|
||||
* Build description of application menu
|
||||
* @param {boolean} isMac - Whether the target platform is mac
|
||||
* @param {function} askToQuit - function for quitting process
|
||||
*/
|
||||
function getApplicationMenu(isMac, askToQuit) {
|
||||
return [
|
||||
...(isMac
|
||||
? [
|
||||
{
|
||||
label: 'Ontime',
|
||||
submenu: [
|
||||
{ role: 'about' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'hide' },
|
||||
{ role: 'hideOthers' },
|
||||
{ role: 'unhide' },
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'quit',
|
||||
click: () => askToQuit(),
|
||||
accelerator: 'Cmd+Q',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
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: 'delete' },
|
||||
{ role: 'selectAll' },
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Speech',
|
||||
submenu: [{ role: 'startSpeaking' }, { role: 'stopSpeaking' }],
|
||||
},
|
||||
]
|
||||
: [{ role: 'delete' }, { type: 'separator' }, { role: 'selectAll' }]),
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Views',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Ontime Views (opens in browser)',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Timer',
|
||||
accelerator: 'CmdOrCtrl+V',
|
||||
click: async () => {
|
||||
await shell.openExternal('http://localhost:4001/timer');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Clock',
|
||||
click: async () => {
|
||||
await shell.openExternal('http://localhost:4001/clock');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Minimal Timer',
|
||||
click: async () => {
|
||||
await shell.openExternal('http://localhost:4001/minimal');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Backstage',
|
||||
click: async () => {
|
||||
await shell.openExternal('http://localhost:4001/backstage');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Public',
|
||||
click: async () => {
|
||||
await shell.openExternal('http://localhost:4001/public');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Lower Thirds',
|
||||
click: async () => {
|
||||
await shell.openExternal('http://localhost:4001/lower');
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
label: 'PiP',
|
||||
click: async () => {
|
||||
await shell.openExternal('http://localhost:4001/pip');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Studio Clock',
|
||||
click: async () => {
|
||||
await shell.openExternal('http://localhost:4001/studio');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Countdown',
|
||||
click: async () => {
|
||||
await shell.openExternal('http://localhost:4001/countdown');
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Editor',
|
||||
click: async () => {
|
||||
await shell.openExternal('http://localhost:4001/editor');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Cuesheet',
|
||||
click: async () => {
|
||||
await shell.openExternal('http://localhost:4001/cuesheet');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ role: 'forceReload' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'resetZoom' },
|
||||
{ role: 'zoomIn' },
|
||||
{ role: 'zoomOut' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Window',
|
||||
submenu: [
|
||||
{ role: 'minimize' },
|
||||
{ role: 'zoom' },
|
||||
...(isMac
|
||||
? [{ type: 'separator' }, { role: 'front' }, { type: 'separator' }, { role: 'window' }]
|
||||
: [{ role: 'close' }]),
|
||||
],
|
||||
},
|
||||
{
|
||||
role: 'help',
|
||||
submenu: [
|
||||
{
|
||||
label: 'See on github',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://github.com/cpvalente/ontime');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Online documentation',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://cpvalente.gitbook.io/ontime/');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
module.exports = { getApplicationMenu }
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Build description of tray context menu
|
||||
* @param {function} showApp - function for making the window visible
|
||||
* @param {function} askToQuit - function for quitting process
|
||||
*/
|
||||
function getTrayMenu(showApp, askToQuit) {
|
||||
return [
|
||||
{
|
||||
label: 'Show App (Alt + 1)',
|
||||
click: () => showApp(),
|
||||
},
|
||||
{
|
||||
label: 'Shutdown',
|
||||
click: () => askToQuit(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
module.exports = { getTrayMenu }
|
||||
@@ -0,0 +1 @@
|
||||
window.ipcRenderer = require('electron').ipcRenderer;
|
||||
@@ -0,0 +1,104 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; style-src 'unsafe-inline'; script-src 'self'"
|
||||
/>
|
||||
<title>ontime</title>
|
||||
<style>
|
||||
body {
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-app-region: drag;
|
||||
animation: fadein 0s;
|
||||
background-color: #0005;
|
||||
}
|
||||
h1 {
|
||||
font-family: Roboto, sans-serif;
|
||||
font-weight: 100;
|
||||
color: #fffa;
|
||||
font-size: 24px;
|
||||
}
|
||||
.container {
|
||||
margin-top: 5vh;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
.lds-ellipsis {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 71px;
|
||||
}
|
||||
.lds-ellipsis div {
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #ff7597aa;
|
||||
animation-timing-function: cubic-bezier(0, 1, 1, 0);
|
||||
}
|
||||
.lds-ellipsis div:nth-child(1) {
|
||||
left: 8px;
|
||||
animation: lds-ellipsis1 0.6s infinite;
|
||||
}
|
||||
.lds-ellipsis div:nth-child(2) {
|
||||
left: 8px;
|
||||
animation: lds-ellipsis2 0.6s infinite;
|
||||
}
|
||||
.lds-ellipsis div:nth-child(3) {
|
||||
left: 32px;
|
||||
animation: lds-ellipsis2 0.6s infinite;
|
||||
}
|
||||
.lds-ellipsis div:nth-child(4) {
|
||||
left: 56px;
|
||||
animation: lds-ellipsis3 0.6s infinite;
|
||||
}
|
||||
@keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes lds-ellipsis1 {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
@keyframes lds-ellipsis3 {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
@keyframes lds-ellipsis2 {
|
||||
0% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
100% {
|
||||
transform: translate(24px, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<img src="../../assets/logo.png" />
|
||||
<h1>ontime · event timers</h1>
|
||||
<div class="lds-ellipsis">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||