diff --git a/materialious/electron/.gitignore b/materialious/electron/.gitignore new file mode 100644 index 00000000..c340937a --- /dev/null +++ b/materialious/electron/.gitignore @@ -0,0 +1,8 @@ +# NPM renames .gitignore to .npmignore +# In order to prevent that, we remove the initial "." +# And the CLI then renames it +app +node_modules +build +dist +logs diff --git a/materialious/electron/assets/appIcon.ico b/materialious/electron/assets/appIcon.ico new file mode 100644 index 00000000..1aea5a5c Binary files /dev/null and b/materialious/electron/assets/appIcon.ico differ diff --git a/materialious/electron/assets/appIcon.png b/materialious/electron/assets/appIcon.png new file mode 100644 index 00000000..7e8612e1 Binary files /dev/null and b/materialious/electron/assets/appIcon.png differ diff --git a/materialious/electron/assets/splash.gif b/materialious/electron/assets/splash.gif new file mode 100644 index 00000000..5ee8351b Binary files /dev/null and b/materialious/electron/assets/splash.gif differ diff --git a/materialious/electron/assets/splash.png b/materialious/electron/assets/splash.png new file mode 100644 index 00000000..ce554d18 Binary files /dev/null and b/materialious/electron/assets/splash.png differ diff --git a/materialious/electron/capacitor.config.ts b/materialious/electron/capacitor.config.ts new file mode 100644 index 00000000..22795755 --- /dev/null +++ b/materialious/electron/capacitor.config.ts @@ -0,0 +1,15 @@ +import type { CapacitorConfig } from '@capacitor/cli'; + +const config: CapacitorConfig = { + appId: 'us.materialio.app', + appName: 'Materialious', + webDir: 'build', + bundledWebRuntime: false, + plugins: { + CapacitorHttp: { + enabled: true + } + }, +}; + +export default config; diff --git a/materialious/electron/electron-builder.config.json b/materialious/electron/electron-builder.config.json new file mode 100644 index 00000000..4d454bba --- /dev/null +++ b/materialious/electron/electron-builder.config.json @@ -0,0 +1,28 @@ +{ + "appId": "com.yourdoamnin.yourapp", + "directories": { + "buildResources": "resources" + }, + "files": [ + "assets/**/*", + "build/**/*", + "capacitor.config.*", + "app/**/*" + ], + "publish": { + "provider": "github" + }, + "nsis": { + "allowElevation": true, + "oneClick": false, + "allowToChangeInstallationDirectory": true + }, + "win": { + "target": "nsis", + "icon": "assets/appIcon.ico" + }, + "mac": { + "category": "your.app.category.type", + "target": "dmg" + } +} \ No newline at end of file diff --git a/materialious/electron/live-runner.js b/materialious/electron/live-runner.js new file mode 100644 index 00000000..84c3ea73 --- /dev/null +++ b/materialious/electron/live-runner.js @@ -0,0 +1,75 @@ +/* eslint-disable no-undef */ +/* eslint-disable @typescript-eslint/no-var-requires */ +const cp = require('child_process'); +const chokidar = require('chokidar'); +const electron = require('electron'); + +let child = null; +const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'; +const reloadWatcher = { + debouncer: null, + ready: false, + watcher: null, + restarting: false, +}; + +///* +function runBuild() { + return new Promise((resolve, _reject) => { + let tempChild = cp.spawn(npmCmd, ['run', 'build']); + tempChild.once('exit', () => { + resolve(); + }); + tempChild.stdout.pipe(process.stdout); + }); +} +//*/ + +async function spawnElectron() { + if (child !== null) { + child.stdin.pause(); + child.kill(); + child = null; + await runBuild(); + } + child = cp.spawn(electron, ['--inspect=5858', './']); + child.on('exit', () => { + if (!reloadWatcher.restarting) { + process.exit(0); + } + }); + child.stdout.pipe(process.stdout); +} + +function setupReloadWatcher() { + reloadWatcher.watcher = chokidar + .watch('./src/**/*', { + ignored: /[/\\]\./, + persistent: true, + }) + .on('ready', () => { + reloadWatcher.ready = true; + }) + .on('all', (_event, _path) => { + if (reloadWatcher.ready) { + clearTimeout(reloadWatcher.debouncer); + reloadWatcher.debouncer = setTimeout(async () => { + console.log('Restarting'); + reloadWatcher.restarting = true; + await spawnElectron(); + reloadWatcher.restarting = false; + reloadWatcher.ready = false; + clearTimeout(reloadWatcher.debouncer); + reloadWatcher.debouncer = null; + reloadWatcher.watcher = null; + setupReloadWatcher(); + }, 500); + } + }); +} + +(async () => { + await runBuild(); + await spawnElectron(); + setupReloadWatcher(); +})(); diff --git a/materialious/electron/package.json b/materialious/electron/package.json new file mode 100644 index 00000000..592d802e --- /dev/null +++ b/materialious/electron/package.json @@ -0,0 +1,41 @@ +{ + "name": "Materialious", + "version": "1.0.0", + "description": "An Amazing Capacitor App", + "author": { + "name": "", + "email": "" + }, + "repository": { + "type": "git", + "url": "" + }, + "license": "MIT", + "main": "build/src/index.js", + "scripts": { + "build": "tsc && electron-rebuild", + "electron:start-live": "node ./live-runner.js", + "electron:start": "npm run build && electron --inspect=5858 ./", + "electron:pack": "npm run build && electron-builder build --dir -c ./electron-builder.config.json", + "electron:make": "npm run build && electron-builder build -c ./electron-builder.config.json -p always" + }, + "dependencies": { + "@capacitor-community/electron": "^5.0.0", + "chokidar": "~3.5.3", + "electron-is-dev": "~2.0.0", + "electron-serve": "~1.1.0", + "electron-unhandled": "~4.0.1", + "electron-updater": "^5.3.0", + "electron-window-state": "^5.0.3" + }, + "devDependencies": { + "electron": "^26.2.2", + "electron-builder": "~23.6.0", + "electron-rebuild": "^3.2.9", + "typescript": "^5.0.4" + }, + "keywords": [ + "capacitor", + "electron" + ] +} diff --git a/materialious/electron/resources/electron-publisher-custom.js b/materialious/electron/resources/electron-publisher-custom.js new file mode 100644 index 00000000..6e0821ef --- /dev/null +++ b/materialious/electron/resources/electron-publisher-custom.js @@ -0,0 +1,10 @@ +/* eslint-disable no-undef */ +/* eslint-disable @typescript-eslint/no-var-requires */ +const electronPublish = require('electron-publish'); + +class Publisher extends electronPublish.Publisher { + async upload(task) { + console.log('electron-publisher-custom', task.file); + } +} +module.exports = Publisher; diff --git a/materialious/electron/src/index.ts b/materialious/electron/src/index.ts new file mode 100644 index 00000000..e23f2bb3 --- /dev/null +++ b/materialious/electron/src/index.ts @@ -0,0 +1,70 @@ +import type { CapacitorElectronConfig } from '@capacitor-community/electron'; +import { getCapacitorElectronConfig, setupElectronDeepLinking } from '@capacitor-community/electron'; +import type { MenuItemConstructorOptions } from 'electron'; +import { app, MenuItem } from 'electron'; +import electronIsDev from 'electron-is-dev'; +import unhandled from 'electron-unhandled'; +import { autoUpdater } from 'electron-updater'; + +import { ElectronCapacitorApp, setupContentSecurityPolicy, setupReloadWatcher } from './setup'; + +// Graceful handling of unhandled errors. +unhandled(); + +// Define our menu templates (these are optional) +const trayMenuTemplate: (MenuItemConstructorOptions | MenuItem)[] = [new MenuItem({ label: 'Quit App', role: 'quit' })]; +const appMenuBarMenuTemplate: (MenuItemConstructorOptions | MenuItem)[] = [ + { role: process.platform === 'darwin' ? 'appMenu' : 'fileMenu' }, + { role: 'viewMenu' }, +]; + +// Get Config options from capacitor.config +const capacitorFileConfig: CapacitorElectronConfig = getCapacitorElectronConfig(); + +// Initialize our app. You can pass menu templates into the app here. +// const myCapacitorApp = new ElectronCapacitorApp(capacitorFileConfig); +const myCapacitorApp = new ElectronCapacitorApp(capacitorFileConfig, trayMenuTemplate, appMenuBarMenuTemplate); + +// If deeplinking is enabled then we will set it up here. +if (capacitorFileConfig.electron?.deepLinkingEnabled) { + setupElectronDeepLinking(myCapacitorApp, { + customProtocol: capacitorFileConfig.electron.deepLinkingCustomProtocol ?? 'mycapacitorapp', + }); +} + +// If we are in Dev mode, use the file watcher components. +if (electronIsDev) { + setupReloadWatcher(myCapacitorApp); +} + +// Run Application +(async () => { + // Wait for electron app to be ready. + await app.whenReady(); + // Security - Set Content-Security-Policy based on whether or not we are in dev mode. + setupContentSecurityPolicy(myCapacitorApp.getCustomURLScheme()); + // Initialize our app, build windows, and load content. + await myCapacitorApp.init(); + // Check for updates if we are in a packaged app. + autoUpdater.checkForUpdatesAndNotify(); +})(); + +// Handle when all of our windows are close (platforms have their own expectations). +app.on('window-all-closed', function () { + // On OS X it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== 'darwin') { + app.quit(); + } +}); + +// When the dock icon is clicked. +app.on('activate', async function () { + // On OS X it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (myCapacitorApp.getMainWindow().isDestroyed()) { + await myCapacitorApp.init(); + } +}); + +// Place all ipc or other electron api calls and custom functionality under this line diff --git a/materialious/electron/src/preload.ts b/materialious/electron/src/preload.ts new file mode 100644 index 00000000..c817d3b7 --- /dev/null +++ b/materialious/electron/src/preload.ts @@ -0,0 +1,4 @@ +require('./rt/electron-rt'); +////////////////////////////// +// User Defined Preload scripts below +console.log('User Preload!'); diff --git a/materialious/electron/src/rt/electron-plugins.js b/materialious/electron/src/rt/electron-plugins.js new file mode 100644 index 00000000..b33b2826 --- /dev/null +++ b/materialious/electron/src/rt/electron-plugins.js @@ -0,0 +1,4 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ + +module.exports = { +} \ No newline at end of file diff --git a/materialious/electron/src/rt/electron-rt.ts b/materialious/electron/src/rt/electron-rt.ts new file mode 100644 index 00000000..55d67c30 --- /dev/null +++ b/materialious/electron/src/rt/electron-rt.ts @@ -0,0 +1,88 @@ +import { randomBytes } from 'crypto'; +import { ipcRenderer, contextBridge } from 'electron'; +import { EventEmitter } from 'events'; + +//////////////////////////////////////////////////////// +// eslint-disable-next-line @typescript-eslint/no-var-requires +const plugins = require('./electron-plugins'); + +const randomId = (length = 5) => randomBytes(length).toString('hex'); + +const contextApi: { + [plugin: string]: { [functionName: string]: () => Promise }; +} = {}; + +Object.keys(plugins).forEach((pluginKey) => { + Object.keys(plugins[pluginKey]) + .filter((className) => className !== 'default') + .forEach((classKey) => { + const functionList = Object.getOwnPropertyNames(plugins[pluginKey][classKey].prototype).filter( + (v) => v !== 'constructor' + ); + + if (!contextApi[classKey]) { + contextApi[classKey] = {}; + } + + functionList.forEach((functionName) => { + if (!contextApi[classKey][functionName]) { + contextApi[classKey][functionName] = (...args) => ipcRenderer.invoke(`${classKey}-${functionName}`, ...args); + } + }); + + // Events + if (plugins[pluginKey][classKey].prototype instanceof EventEmitter) { + const listeners: { [key: string]: { type: string; listener: (...args: any[]) => void } } = {}; + const listenersOfTypeExist = (type) => + !!Object.values(listeners).find((listenerObj) => listenerObj.type === type); + + Object.assign(contextApi[classKey], { + addListener(type: string, callback: (...args) => void) { + const id = randomId(); + + // Deduplicate events + if (!listenersOfTypeExist(type)) { + ipcRenderer.send(`event-add-${classKey}`, type); + } + + const eventHandler = (_, ...args) => callback(...args); + + ipcRenderer.addListener(`event-${classKey}-${type}`, eventHandler); + listeners[id] = { type, listener: eventHandler }; + + return id; + }, + removeListener(id: string) { + if (!listeners[id]) { + throw new Error('Invalid id'); + } + + const { type, listener } = listeners[id]; + + ipcRenderer.removeListener(`event-${classKey}-${type}`, listener); + + delete listeners[id]; + + if (!listenersOfTypeExist(type)) { + ipcRenderer.send(`event-remove-${classKey}-${type}`); + } + }, + removeAllListeners(type: string) { + Object.entries(listeners).forEach(([id, listenerObj]) => { + if (!type || listenerObj.type === type) { + ipcRenderer.removeListener(`event-${classKey}-${listenerObj.type}`, listenerObj.listener); + ipcRenderer.send(`event-remove-${classKey}-${listenerObj.type}`); + delete listeners[id]; + } + }); + }, + }); + } + }); +}); + +contextBridge.exposeInMainWorld('CapacitorCustomPlatform', { + name: 'electron', + plugins: contextApi, +}); +//////////////////////////////////////////////////////// diff --git a/materialious/electron/src/setup.ts b/materialious/electron/src/setup.ts new file mode 100644 index 00000000..c0d4963f --- /dev/null +++ b/materialious/electron/src/setup.ts @@ -0,0 +1,229 @@ +import type { CapacitorElectronConfig } from '@capacitor-community/electron'; +import { + CapElectronEventEmitter, + CapacitorSplashScreen, + setupCapacitorElectronPlugins, +} from '@capacitor-community/electron'; +import chokidar from 'chokidar'; +import type { MenuItemConstructorOptions } from 'electron'; +import { BrowserWindow, Menu, MenuItem, Tray, app, nativeImage, session } from 'electron'; +import electronIsDev from 'electron-is-dev'; +import electronServe from 'electron-serve'; +import windowStateKeeper from 'electron-window-state'; +import { join } from 'path'; + +// Define components for a watcher to detect when the webapp is changed so we can reload in Dev mode. +const reloadWatcher = { + debouncer: null, + ready: false, + watcher: null, +}; +export function setupReloadWatcher(electronCapacitorApp: ElectronCapacitorApp): void { + reloadWatcher.watcher = chokidar + .watch(join(app.getAppPath(), 'app'), { + ignored: /[/\\]\./, + persistent: true, + }) + .on('ready', () => { + reloadWatcher.ready = true; + }) + .on('all', (_event, _path) => { + if (reloadWatcher.ready) { + clearTimeout(reloadWatcher.debouncer); + reloadWatcher.debouncer = setTimeout(async () => { + electronCapacitorApp.getMainWindow().webContents.reload(); + reloadWatcher.ready = false; + clearTimeout(reloadWatcher.debouncer); + reloadWatcher.debouncer = null; + reloadWatcher.watcher = null; + setupReloadWatcher(electronCapacitorApp); + }, 1500); + } + }); +} + +// Define our class to manage our app. +export class ElectronCapacitorApp { + private MainWindow: BrowserWindow | null = null; + private SplashScreen: CapacitorSplashScreen | null = null; + private TrayIcon: Tray | null = null; + private CapacitorFileConfig: CapacitorElectronConfig; + private TrayMenuTemplate: (MenuItem | MenuItemConstructorOptions)[] = [ + new MenuItem({ label: 'Quit App', role: 'quit' }), + ]; + private AppMenuBarMenuTemplate: (MenuItem | MenuItemConstructorOptions)[] = [ + { role: process.platform === 'darwin' ? 'appMenu' : 'fileMenu' }, + { role: 'viewMenu' }, + ]; + private mainWindowState; + private loadWebApp; + private customScheme: string; + + constructor( + capacitorFileConfig: CapacitorElectronConfig, + trayMenuTemplate?: (MenuItemConstructorOptions | MenuItem)[], + appMenuBarMenuTemplate?: (MenuItemConstructorOptions | MenuItem)[] + ) { + this.CapacitorFileConfig = capacitorFileConfig; + + this.customScheme = this.CapacitorFileConfig.electron?.customUrlScheme ?? 'capacitor-electron'; + + if (trayMenuTemplate) { + this.TrayMenuTemplate = trayMenuTemplate; + } + + if (appMenuBarMenuTemplate) { + this.AppMenuBarMenuTemplate = appMenuBarMenuTemplate; + } + + // Setup our web app loader, this lets us load apps like react, vue, and angular without changing their build chains. + this.loadWebApp = electronServe({ + directory: join(app.getAppPath(), 'app'), + scheme: this.customScheme, + }); + } + + // Helper function to load in the app. + private async loadMainWindow(thisRef: any) { + await thisRef.loadWebApp(thisRef.MainWindow); + } + + // Expose the mainWindow ref for use outside of the class. + getMainWindow(): BrowserWindow { + return this.MainWindow; + } + + getCustomURLScheme(): string { + return this.customScheme; + } + + async init(): Promise { + const icon = nativeImage.createFromPath( + join(app.getAppPath(), 'assets', process.platform === 'win32' ? 'appIcon.ico' : 'appIcon.png') + ); + this.mainWindowState = windowStateKeeper({ + defaultWidth: 1000, + defaultHeight: 800, + }); + // Setup preload script path and construct our main window. + const preloadPath = join(app.getAppPath(), 'build', 'src', 'preload.js'); + this.MainWindow = new BrowserWindow({ + icon, + show: false, + x: this.mainWindowState.x, + y: this.mainWindowState.y, + width: this.mainWindowState.width, + height: this.mainWindowState.height, + webPreferences: { + nodeIntegration: true, + contextIsolation: true, + webSecurity: false, + // Use preload to inject the electron varriant overrides for capacitor plugins. + // preload: join(app.getAppPath(), "node_modules", "@capacitor-community", "electron", "dist", "runtime", "electron-rt.js"), + preload: preloadPath, + }, + }); + this.mainWindowState.manage(this.MainWindow); + + if (this.CapacitorFileConfig.backgroundColor) { + this.MainWindow.setBackgroundColor(this.CapacitorFileConfig.electron.backgroundColor); + } + + // If we close the main window with the splashscreen enabled we need to destory the ref. + this.MainWindow.on('closed', () => { + if (this.SplashScreen?.getSplashWindow() && !this.SplashScreen.getSplashWindow().isDestroyed()) { + this.SplashScreen.getSplashWindow().close(); + } + }); + + // When the tray icon is enabled, setup the options. + if (this.CapacitorFileConfig.electron?.trayIconAndMenuEnabled) { + this.TrayIcon = new Tray(icon); + this.TrayIcon.on('double-click', () => { + if (this.MainWindow) { + if (this.MainWindow.isVisible()) { + this.MainWindow.hide(); + } else { + this.MainWindow.show(); + this.MainWindow.focus(); + } + } + }); + this.TrayIcon.on('click', () => { + if (this.MainWindow) { + if (this.MainWindow.isVisible()) { + this.MainWindow.hide(); + } else { + this.MainWindow.show(); + this.MainWindow.focus(); + } + } + }); + this.TrayIcon.setToolTip(app.getName()); + this.TrayIcon.setContextMenu(Menu.buildFromTemplate(this.TrayMenuTemplate)); + } + + // Setup the main manu bar at the top of our window. + Menu.setApplicationMenu(Menu.buildFromTemplate(this.AppMenuBarMenuTemplate)); + + // If the splashscreen is enabled, show it first while the main window loads then switch it out for the main window, or just load the main window from the start. + if (this.CapacitorFileConfig.electron?.splashScreenEnabled) { + this.SplashScreen = new CapacitorSplashScreen({ + imageFilePath: join( + app.getAppPath(), + 'assets', + this.CapacitorFileConfig.electron?.splashScreenImageName ?? 'splash.png' + ), + windowWidth: 400, + windowHeight: 400, + }); + this.SplashScreen.init(this.loadMainWindow, this); + } else { + this.loadMainWindow(this); + } + + // Security + this.MainWindow.webContents.setWindowOpenHandler((details) => { + if (!details.url.includes(this.customScheme)) { + return { action: 'deny' }; + } else { + return { action: 'allow' }; + } + }); + this.MainWindow.webContents.on('will-navigate', (event, _newURL) => { + if (!this.MainWindow.webContents.getURL().includes(this.customScheme)) { + event.preventDefault(); + } + }); + + // Link electron plugins into the system. + setupCapacitorElectronPlugins(); + + // When the web app is loaded we hide the splashscreen if needed and show the mainwindow. + this.MainWindow.webContents.on('dom-ready', () => { + if (this.CapacitorFileConfig.electron?.splashScreenEnabled) { + this.SplashScreen.getSplashWindow().hide(); + } + if (!this.CapacitorFileConfig.electron?.hideMainWindowOnLaunch) { + this.MainWindow.show(); + } + setTimeout(() => { + if (electronIsDev) { + this.MainWindow.webContents.openDevTools(); + } + CapElectronEventEmitter.emit('CAPELECTRON_DeeplinkListenerInitialized', ''); + }, 400); + }); + } +} + +// Set a CSP up for our application based on the custom scheme +export function setupContentSecurityPolicy(customScheme: string): void { + session.defaultSession.webRequest.onHeadersReceived((details, callback) => { + callback({ + responseHeaders: { + ...details.responseHeaders, + }, + }); + }); +} diff --git a/materialious/electron/tsconfig.json b/materialious/electron/tsconfig.json new file mode 100644 index 00000000..ef90ab39 --- /dev/null +++ b/materialious/electron/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compileOnSave": true, + "include": ["./src/**/*", "./capacitor.config.ts", "./capacitor.config.js"], + "compilerOptions": { + "outDir": "./build", + "importHelpers": true, + "target": "ES2017", + "module": "CommonJS", + "moduleResolution": "node", + "esModuleInterop": true, + "typeRoots": ["./node_modules/@types"], + "allowJs": true, + "rootDir": "." + } +} diff --git a/materialious/package-lock.json b/materialious/package-lock.json index 1e2278da..25436804 100644 --- a/materialious/package-lock.json +++ b/materialious/package-lock.json @@ -8,6 +8,7 @@ "name": "materialious", "version": "0.0.1", "dependencies": { + "@capacitor-community/electron": "^5.0.1", "@capacitor/android": "^6.0.0", "@capacitor/app": "^6.0.0", "@capacitor/browser": "^6.0.0", @@ -1995,6 +1996,36 @@ "node": ">=6.9.0" } }, + "node_modules/@capacitor-community/electron": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@capacitor-community/electron/-/electron-5.0.1.tgz", + "integrity": "sha512-4/x12ycTq0Kq8JIn/BmIBdFVP5Cqw8iA6SU6YfFjmONfjW3OELwsB3zwLxOwAjLxnjyCMOBHl4ci9E5jLgZgAQ==", + "dependencies": { + "@capacitor/cli": ">=5.4.0", + "@capacitor/core": ">=5.4.0", + "@ionic/utils-fs": "~3.1.6", + "chalk": "^4.1.2", + "electron-is-dev": "~2.0.0", + "events": "~3.3.0", + "fs-extra": "~11.1.1", + "keyv": "^4.5.2", + "mime-types": "~2.1.35", + "ora": "^5.4.1" + } + }, + "node_modules/@capacitor-community/electron/node_modules/fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/@capacitor/android": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@capacitor/android/-/android-6.0.0.tgz", @@ -4238,6 +4269,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/bplist-parser": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", @@ -4301,6 +4342,29 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -4412,7 +4476,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4481,6 +4544,36 @@ "node": ">=0.10" } }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/code-red": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", @@ -4721,6 +4814,17 @@ "node": ">=0.10.0" } }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -4845,6 +4949,14 @@ "node": ">=0.10.0" } }, + "node_modules/electron-is-dev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-2.0.0.tgz", + "integrity": "sha512-3X99K852Yoqu9AcW50qz3ibYBWY79/pBhlMCab8ToEWS48R0T9tyxRiQhwylE7zQdXrMnx2JKqUJyMPmt5FBqA==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.722", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.722.tgz", @@ -5399,6 +5511,14 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/ext": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", @@ -5903,7 +6023,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -5994,6 +6113,25 @@ "dev": true, "peer": true }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -6244,6 +6382,14 @@ "node": ">=0.10.0" } }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", @@ -6424,6 +6570,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -6553,8 +6710,7 @@ "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, "node_modules/json-schema": { "version": "0.4.0", @@ -6613,7 +6769,6 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, "dependencies": { "json-buffer": "3.0.1" } @@ -6717,6 +6872,21 @@ "dev": true, "peer": true }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -6842,6 +7012,14 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -7074,6 +7252,20 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/open": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", @@ -7107,6 +7299,28 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -7679,6 +7893,18 @@ "node": ">=4" } }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -8269,7 +8495,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -9464,6 +9689,14 @@ } } }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dependencies": { + "defaults": "^1.0.3" + } + }, "node_modules/webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", diff --git a/materialious/package.json b/materialious/package.json index a925d5a6..223fdbb6 100644 --- a/materialious/package.json +++ b/materialious/package.json @@ -36,6 +36,7 @@ }, "type": "module", "dependencies": { + "@capacitor-community/electron": "^5.0.1", "@capacitor/android": "^6.0.0", "@capacitor/app": "^6.0.0", "@capacitor/browser": "^6.0.0", diff --git a/materialious/src/lib/Settings.svelte b/materialious/src/lib/Settings.svelte index e09eecdd..35979121 100644 --- a/materialious/src/lib/Settings.svelte +++ b/materialious/src/lib/Settings.svelte @@ -229,7 +229,7 @@ - {#if !Capacitor.isNativePlatform()} + {#if Capacitor.getPlatform() !== 'android'}
- {#if !Capacitor.isNativePlatform()} + {#if Capacitor.getPlatform() !== 'android'}