/
githubmirror
/
edex-ui
Обзор
Документация
Войти
/
githubmirror
/
edex-ui
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v2.2.1
src/_renderer.js
1 004 строки
38 KB
Gabriel SAILLARD (GitSquared)
:sparkles: List available shortcuts with Ctrl+Shift+K
28 апр 2019, 13:54
Не верифицирован
28 апр 2019, 13:54
f317248
Код
Авторство
О чём код?
// Disable eval() window.eval = global.eval = function () { throw new Error("eval() is disabled for security reasons."); }; // Security helper :) window._escapeHtml = text => { let map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, m => {return map[m];}); }; window._purifyCSS = str => { return str.replace(/[<]/g, ""); }; window._delay = ms => { return new Promise((resolve, reject) => { setTimeout(resolve, ms); }); }; // Initiate basic error handling window.onerror = (msg, path, line, col, error) => { document.getElementById("boot_screen").innerHTML += `${error} : ${msg}<br/>==> at ${path} ${line}:${col}`; }; const path = require("path"); const fs = require("fs"); const electron = require("electron"); const ipc = electron.ipcRenderer; const settingsDir = electron.remote.app.getPath("userData"); const themesDir = path.join(settingsDir, "themes"); const keyboardsDir = path.join(settingsDir, "keyboards"); const fontsDir = path.join(settingsDir, "fonts"); const settingsFile = path.join(settingsDir, "settings.json"); // Load config window.settings = require(settingsFile); // Load CLI parameters if (electron.remote.process.argv.includes("--nointro")) { window.settings.nointroOverride = true; } else { window.settings.nointroOverride = false; } if (electron.remote.process.argv.includes("--nocursor")) { window.settings.nocursorOverride = true; } else { window.settings.nocursorOverride = false; } // Retrieve theme override (hotswitch) ipc.once("getThemeOverride", (e, theme) => { if (theme !== null) { window.settings.theme = theme; window.settings.nointroOverride = true; _loadTheme(require(path.join(themesDir, window.settings.theme+".json"))); } else { _loadTheme(require(path.join(themesDir, window.settings.theme+".json"))); } }); ipc.send("getThemeOverride"); // Same for keyboard override/hotswitch ipc.once("getKbOverride", (e, layout) => { if (layout !== null) { window.settings.keyboard = layout; window.settings.nointroOverride = true; } }); ipc.send("getKbOverride"); // Load UI theme window._loadTheme = theme => { if (document.querySelector("style.theming")) { document.querySelector("style.theming").remove(); } // Load fonts let mainFont = new FontFace(theme.cssvars.font_main, `url("${path.join(fontsDir, theme.cssvars.font_main.toLowerCase().replace(/ /g, '_')+'.woff2').replace(/\\/g, '/')}")`); let lightFont = new FontFace(theme.cssvars.font_main_light, `url("${path.join(fontsDir, theme.cssvars.font_main_light.toLowerCase().replace(/ /g, '_')+'.woff2').replace(/\\/g, '/')}")`); let termFont = new FontFace(theme.terminal.fontFamily, `url("${path.join(fontsDir, theme.terminal.fontFamily.toLowerCase().replace(/ /g, '_')+'.woff2').replace(/\\/g, '/')}")`); document.fonts.add(mainFont); document.fonts.load("12px "+theme.cssvars.font_main); document.fonts.add(lightFont); document.fonts.load("12px "+theme.cssvars.font_main_light); document.fonts.add(termFont); document.fonts.load("12px "+theme.terminal.fontFamily); document.querySelector("head").innerHTML += `<style class="theming"> :root { --font_main: "${theme.cssvars.font_main}"; --font_main_light: "${theme.cssvars.font_main_light}"; --color_r: ${theme.colors.r}; --color_g: ${theme.colors.g}; --color_b: ${theme.colors.b}; --color_black: ${theme.colors.black}; --color_light_black: ${theme.colors.light_black}; --color_grey: ${theme.colors.grey}; } body { font-family: var(--font_main), sans-serif; cursor: ${(window.settings.nocursorOverride || window.settings.nocursor) ? "none" : "default"} !important; } * { ${(window.settings.nocursorOverride || window.settings.nocursor) ? "cursor: none !important;" : ""} } ${window._purifyCSS(theme.injectCSS || "")} </style>`; window.theme = theme; window.theme.r = theme.colors.r; window.theme.g = theme.colors.g; window.theme.b = theme.colors.b; }; function initGraphicalErrorHandling() { window.edexErrorsModals = []; window.onerror = (msg, path, line, col, error) => { let errorModal = new Modal({ type: "error", title: error, message: `${msg}<br/> at ${path} ${line}:${col}` }); window.edexErrorsModals.push(errorModal); ipc.send("log", "error", `${error}: ${msg}`); ipc.send("log", "debug", `at ${path} ${line}:${col}`); }; } function waitForFonts() { return new Promise(resolve => { if (document.readyState !== "complete" || document.fonts.status !== "loaded") { document.addEventListener("readystatechange", () => { if (document.readyState === "complete") { if (document.fonts.status === "loaded") { resolve(); } else { document.fonts.onloadingdone = () => { if (document.fonts.status === "loaded") resolve(); }; } } }); } else { resolve(); } }); } // A proxy function used to add multithreading to systeminformation calls - see backend process manager @ _multithread.js function initSystemInformationProxy() { const nanoid = require("nanoid/non-secure"); window.si = new Proxy({}, { apply: () => {throw new Error("Cannot use sysinfo proxy directly as a function")}, set: () => {throw new Error("Cannot set a property on the sysinfo proxy")}, get: (target, prop, receiver) => { return function(...args) { let callback = (typeof args[args.length - 1] === "function") ? true : false; return new Promise((resolve, reject) => { let id = nanoid(); ipc.once("systeminformation-reply-"+id, (e, res) => { if (callback) { args[args.length - 1](res); } resolve(res); }); ipc.send("systeminformation-call", prop, id, ...args); }); }; } }); } // Init audio window.audioManager = new AudioManager(); // See #223 electron.remote.app.focus(); let i = 0; if (window.settings.nointro || window.settings.nointroOverride) { initGraphicalErrorHandling(); initSystemInformationProxy(); document.getElementById("boot_screen").remove(); document.body.setAttribute("class", ""); waitForFonts().then(initUI); } else { displayLine(); } // Startup boot log function displayLine() { let bootScreen = document.getElementById("boot_screen"); let log = fs.readFileSync(path.join(__dirname, "assets", "misc", "boot_log.txt")).toString().split('\n'); function isArchUser() { return require("os").platform() === "linux" && fs.existsSync("/etc/os-release") && fs.readFileSync("/etc/os-release").toString().includes("arch"); } if (typeof log[i] === "undefined") { setTimeout(displayTitleScreen, 300); return; } if (log[i] === "Boot Complete") { window.audioManager.granted.play(); } else { window.audioManager.stdout.play(); } bootScreen.innerHTML += log[i]+"<br/>"; i++; switch(true) { case i === 2: bootScreen.innerHTML += `eDEX-UI Kernel version ${electron.remote.app.getVersion()} boot at ${Date().toString()}; root:xnu-1699.22.73~1/RELEASE_X86_64`; case i === 4: setTimeout(displayLine, 500); break; case i > 4 && i < 25: setTimeout(displayLine, 30); break; case i === 25: setTimeout(displayLine, 400); break; case i === 42: setTimeout(displayLine, 300); break; case i > 42 && i < 82: setTimeout(displayLine, 25); break; case i === 83: if (isArchUser()) bootScreen.innerHTML += "btw i use arch<br/>"; setTimeout(displayLine, 25); break; case i >= log.length-2 && i < log.length: setTimeout(displayLine, 300); break; default: setTimeout(displayLine, Math.pow(1 - (i/1000), 3)*25); } } // Show "logo" and background grid async function displayTitleScreen() { let bootScreen = document.getElementById("boot_screen"); if (bootScreen === null) { bootScreen = document.createElement("section"); bootScreen.setAttribute("id", "boot_screen"); bootScreen.setAttribute("style", "z-index: 9999999"); document.body.appendChild(bootScreen); } bootScreen.innerHTML = ""; window.audioManager.theme.play(); await _delay(400); document.body.setAttribute("class", ""); bootScreen.setAttribute("class", "center"); bootScreen.innerHTML = "<h1>eDEX-UI</h1>"; let title = document.querySelector("section > h1"); await _delay(200); document.body.setAttribute("class", "solidBackground"); await _delay(100); title.setAttribute("style", `background-color: rgb(${window.theme.r}, ${window.theme.g}, ${window.theme.b});border-bottom: 5px solid rgb(${window.theme.r}, ${window.theme.g}, ${window.theme.b});`); await _delay(300); title.setAttribute("style", `border: 5px solid rgb(${window.theme.r}, ${window.theme.g}, ${window.theme.b});`); await _delay(100); title.setAttribute("style", ""); title.setAttribute("class", "glitch"); await _delay(500); document.body.setAttribute("class", ""); title.setAttribute("class", ""); title.setAttribute("style", `border: 5px solid rgb(${window.theme.r}, ${window.theme.g}, ${window.theme.b});`); await _delay(1000); if (window.term) { bootScreen.remove(); return true; } initGraphicalErrorHandling(); initSystemInformationProxy(); waitForFonts().then(() => { bootScreen.remove(); initUI(); }); } // Create the UI's html structure and initialize the terminal client and the keyboard async function initUI() { document.body.innerHTML += `<section class="mod_column" id="mod_column_left"> <h3 class="title"><p>PANEL</p><p>SYSTEM</p></h3> </section> <section id="main_shell" style="height:0%;width:0%;opacity:0;margin-bottom:30vh;"> <h3 class="title" style="opacity:0;"><p>TERMINAL</p><p>MAIN SHELL</p></h3> <h1 id="main_shell_greeting"></h1> </section> <section class="mod_column" id="mod_column_right"> <h3 class="title"><p>PANEL</p><p>NETWORK</p></h3> </section>`; await _delay(10); window.audioManager.expand.play(); document.getElementById("main_shell").setAttribute("style", "height:0%;margin-bottom:30vh;"); await _delay(500); document.getElementById("main_shell").setAttribute("style", "margin-bottom: 30vh;"); document.querySelector("#main_shell > h3.title").setAttribute("style", ""); await _delay(700); document.getElementById("main_shell").setAttribute("style", "opacity: 0;"); document.body.innerHTML += ` <section id="filesystem" style="width: 0px;" class="${window.settings.hideDotfiles ? "hideDotfiles" : ""}"> </section> <section id="keyboard" style="opacity:0;"> </section>`; window.keyboard = new Keyboard({ layout: path.join(keyboardsDir, settings.keyboard+".json"), container: "keyboard" }); await _delay(10); document.getElementById("main_shell").setAttribute("style", ""); await _delay(270); let greeter = document.getElementById("main_shell_greeting"); require("username")().then(user => { greeter.innerHTML += `Welcome back, <em>${user}</em>`; }).catch(() => { greeter.innerHTML += "Welcome back"; }); greeter.setAttribute("style", "opacity: 1;"); document.getElementById("filesystem").setAttribute("style", ""); document.getElementById("keyboard").setAttribute("style", ""); document.getElementById("keyboard").setAttribute("class", "animation_state_1"); window.audioManager.keyboard.play(); await _delay(100); document.getElementById("keyboard").setAttribute("class", "animation_state_1 animation_state_2"); await _delay(1000); greeter.setAttribute("style", "opacity: 0;"); await _delay(100); document.getElementById("keyboard").setAttribute("class", ""); await _delay(400); greeter.remove(); // Initialize modules window.mods = {}; // Left column window.mods.clock = new Clock("mod_column_left"); window.mods.sysinfo = new Sysinfo("mod_column_left"); window.mods.hardwareInspector = new HardwareInspector("mod_column_left"); window.mods.cpuinfo = new Cpuinfo("mod_column_left"); window.mods.ramwatcher = new RAMwatcher("mod_column_left"); window.mods.toplist = new Toplist("mod_column_left"); // Right column window.mods.netstat = new Netstat("mod_column_right"); window.mods.globe = new LocationGlobe("mod_column_right"); window.mods.conninfo = new Conninfo("mod_column_right"); // Fade-in animations document.querySelectorAll(".mod_column").forEach(e => { e.setAttribute("class", "mod_column activated"); }); let i = 0; let left = document.querySelectorAll("#mod_column_left > div"); let right = document.querySelectorAll("#mod_column_right > div"); let x = setInterval(() => { if (!left[i] && !right[i]) { clearInterval(x); } else { window.audioManager.panels.play(); if (left[i]) { left[i].setAttribute("style", "animation-play-state: running;"); } if (right[i]) { right[i].setAttribute("style", "animation-play-state: running;"); } i++; } }, 500); await _delay(100); // Initialize the terminal let shellContainer = document.getElementById("main_shell"); shellContainer.innerHTML += ` <ul id="main_shell_tabs"> <li id="shell_tab0" onclick="window.focusShellTab(0);" class="active"><p>MAIN SHELL</p></li> <li id="shell_tab1" onclick="window.focusShellTab(1);"><p>EMPTY</p></li> <li id="shell_tab2" onclick="window.focusShellTab(2);"><p>EMPTY</p></li> <li id="shell_tab3" onclick="window.focusShellTab(3);"><p>EMPTY</p></li> <li id="shell_tab4" onclick="window.focusShellTab(4);"><p>EMPTY</p></li> </ul> <div id="main_shell_innercontainer"> <pre id="terminal0" class="active"></pre> <pre id="terminal1"></pre> <pre id="terminal2"></pre> <pre id="terminal3"></pre> <pre id="terminal4"></pre> </div>`; window.term = { 0: new Terminal({ role: "client", parentId: "terminal0", port: window.settings.port || 3000 }) }; window.currentTerm = 0; window.term[0].onprocesschange = p => { document.getElementById("shell_tab0").innerHTML = `<p>MAIN - ${p}</p>`; }; // Prevent losing hardware keyboard focus on the terminal when using touch keyboard window.onmouseup = e => { if (window.keyboard.linkedToTerm) window.term[window.currentTerm].term.focus(); }; window.term[0].term.writeln("\033[1m"+`Welcome to eDEX-UI v${electron.remote.app.getVersion()} - Electron v${process.versions.electron}`+"\033[0m"); await _delay(100); window.fsDisp = new FilesystemDisplay({ parentId: "filesystem" }); await _delay(200); document.getElementById("filesystem").setAttribute("style", "opacity: 1;"); // Resend terminal CWD to fsDisp if we're hot reloading if (window.performance.navigation.type === 1) { window.term[window.currentTerm].resendCWD(); } await _delay(200); window.updateCheck = new UpdateChecker(); } window.themeChanger = theme => { ipc.send("setThemeOverride", theme); setTimeout(() => { window.location.reload(true); }, 100); }; window.remakeKeyboard = layout => { document.getElementById("keyboard").innerHTML = ""; window.keyboard = new Keyboard({ layout: path.join(keyboardsDir, layout+".json" || settings.keyboard+".json"), container: "keyboard" }); ipc.send("setKbOverride", layout); }; window.focusShellTab = number => { window.audioManager.folder.play(); if (number !== window.currentTerm && window.term[number]) { window.currentTerm = number; document.querySelectorAll(`ul#main_shell_tabs > li:not(:nth-child(${number+1}))`).forEach(e => { e.setAttribute("class", ""); }); document.getElementById("shell_tab"+number).setAttribute("class", "active"); document.querySelectorAll(`div#main_shell_innercontainer > pre:not(:nth-child(${number+1}))`).forEach(e => { e.setAttribute("class", ""); }); document.getElementById("terminal"+number).setAttribute("class", "active"); window.term[number].fit(); window.term[number].term.focus(); window.term[number].resendCWD(); window.fsDisp.followTab(); } else if (number > 0 && number <= 4 && window.term[number] !== null && typeof window.term[number] !== "object") { window.term[number] = null; document.getElementById("shell_tab"+number).innerHTML = "<p>LOADING...</p>"; ipc.send("ttyspawn", "true"); ipc.once("ttyspawn-reply", (e, r) => { if (r.startsWith("ERROR")) { document.getElementById("shell_tab"+number).innerHTML = "<p>ERROR</p>"; } else if (r.startsWith("SUCCESS")) { let port = Number(r.substr(9)); window.term[number] = new Terminal({ role: "client", parentId: "terminal"+number, port }); window.term[number].onclose = e => { delete window.term[number].onprocesschange; document.getElementById("shell_tab"+number).innerHTML = "<p>EMPTY</p>"; document.getElementById("terminal"+number).innerHTML = ""; delete window.term[number]; window.focusShellTab(0); }; window.term[number].onprocesschange = p => { document.getElementById("shell_tab"+number).innerHTML = `<p>#${number+1} - ${p}</p>`; }; document.getElementById("shell_tab"+number).innerHTML = `<p>::${port}</p>`; setTimeout(() => { window.focusShellTab(number); }, 500); } }); } }; // Settings editor window.openSettings = async () => { // Build lists of available keyboards, themes, monitors let keyboards, themes, monitors, ifaces; fs.readdirSync(keyboardsDir).forEach(kb => { if (!kb.endsWith(".json")) return; kb = kb.replace(".json", ""); if (kb === window.settings.keyboard) return; keyboards += `<option>${kb}</option>`; }); fs.readdirSync(themesDir).forEach(th => { if (!th.endsWith(".json")) return; th = th.replace(".json", ""); if (th === window.settings.theme) return; themes += `<option>${th}</option>`; }); for (let i = 0; i < electron.remote.screen.getAllDisplays().length; i++) { if (i !== window.settings.monitor) monitors += `<option>${i}</option>`; } let nets = await window.si.networkInterfaces(); nets.forEach(net => { if (net.iface !== window.mods.netstat.iface) ifaces += `<option>${net.iface}</option>`; }); // Unlink the tactile keyboard from the terminal emulator to allow filling in the settings fields window.keyboard.detach(); new Modal({ type: "custom", title: `Settings <i>(v${electron.remote.app.getVersion()})</i>`, html: `<table id="settingsEditor"> <tr> <th>Key</th> <th>Description</th> <th>Value</th> </tr> <tr> <td>shell</td> <td>The program to run as a terminal emulator</td> <td><input type="text" id="settingsEditor-shell" value="${window.settings.shell}"></td> </tr> <tr> <td>cwd</td> <td>Working Directory to start in</td> <td><input type="text" id="settingsEditor-cwd" value="${window.settings.cwd}"></td> </tr> <tr> <td>env</td> <td>Custom shell environment override</td> <td><input type="text" id="settingsEditor-env" value="${window.settings.env}"></td> </tr> <tr> <td>keyboard</td> <td>On-screen keyboard layout code</td> <td><select id="settingsEditor-keyboard"> <option>${window.settings.keyboard}</option> ${keyboards} </select></td> </tr> <tr> <td>theme</td> <td>Name of the theme to load</td> <td><select id="settingsEditor-theme"> <option>${window.settings.theme}</option> ${themes} </select></td> </tr> <tr> <td>termFontSize</td> <td>Size of the terminal text in pixels</td> <td><input type="number" id="settingsEditor-termFontSize" value="${window.settings.termFontSize}"></td> </tr> <tr> <td>audio</td> <td>Activate audio sound effects</td> <td><select id="settingsEditor-audio"> <option>${window.settings.audio}</option> <option>${!window.settings.audio}</option> </select></td> </tr> <tr> <td>disableFeedbackAudio</td> <td>Disable recurring feedback sound FX (input/output, mostly)</td> <td><select id="settingsEditor-disableFeedbackAudio"> <option>${window.settings.disableFeedbackAudio}</option> <option>${!window.settings.disableFeedbackAudio}</option> </select></td> </tr> <tr> <td>port</td> <td>Local port to use for UI-shell connection</td> <td><input type="number" id="settingsEditor-port" value="${window.settings.port}"></td> </tr> <tr> <td>pingAddr</td> <td>IPv4 address to test Internet connectivity</td> <td><input type="text" id="settingsEditor-pingAddr" value="${window.settings.pingAddr || "1.1.1.1"}"></td> </tr> <tr> <td>monitor</td> <td>Which monitor to spawn the UI in (defaults to primary display)</td> <td><select id="settingsEditor-monitor"> ${(typeof window.settings.monitor !== "undefined") ? "<option>"+window.settings.monitor+"</option>" : ""} ${monitors} </select></td> </tr> <tr> <td>nointro</td> <td>Skip the intro boot log and logo${(window.settings.nointroOverride) ? " (Currently overridden by CLI flag)" : ""}</td> <td><select id="settingsEditor-nointro"> <option>${window.settings.nointro}</option> <option>${!window.settings.nointro}</option> </select></td> </tr> <tr> <td>nocursor</td> <td>Hide the mouse cursor${(window.settings.nocursorOverride) ? " (Currently overridden by CLI flag)" : ""}</td> <td><select id="settingsEditor-nocursor"> <option>${window.settings.nocursor}</option> <option>${!window.settings.nocursor}</option> </select></td> </tr> <tr> <td>iface</td> <td>Override the interface used for network monitoring</td> <td><select id="settingsEditor-iface"> <option>${window.mods.netstat.iface}</option> ${ifaces} </select></td> </tr> <tr> <td>allowWindowed</td> <td>Allow using F11 key to set the UI in windowed mode</td> <td><select id="settingsEditor-allowWindowed"> <option>${window.settings.allowWindowed}</option> <option>${!window.settings.allowWindowed}</option> </select></td> </tr> <tr> <td>excludeSelfFromToplist</td> <td>Exclude eDEX from top processes monitoring</td> <td><select id="settingsEditor-excludeSelfFromToplist"> <option>${window.settings.excludeSelfFromToplist}</option> <option>${!window.settings.excludeSelfFromToplist}</option> </select></td> </tr> <tr> <td>hideDotfiles</td> <td>Hide files and directories starting with a dot in file display</td> <td><select id="settingsEditor-hideDotfiles"> <option>${window.settings.hideDotfiles}</option> <option>${!window.settings.hideDotfiles}</option> </select></td> </tr> <tr> <td>experimentalGlobeFeatures</td> <td>Toggle experimental features for the network globe</td> <td><select id="settingsEditor-experimentalGlobeFeatures"> <option>${window.settings.experimentalGlobeFeatures}</option> <option>${!window.settings.experimentalGlobeFeatures}</option> </select></td> </tr> <tr> <td>experimentalFeatures</td> <td>Toggle Chrome's experimental web features (DANGEROUS)</td> <td><select id="settingsEditor-experimentalFeatures"> <option>${window.settings.experimentalFeatures}</option> <option>${!window.settings.experimentalFeatures}</option> </select></td> </tr> </table> <h6 id="settingsEditorStatus">Loaded values from memory</h6> <br>`, buttons: [ {label: "Open in External Editor", action:`electron.shell.openExternal('file://${settingsFile}')`}, {label: "Save to Disk", action: "window.writeSettingsFile()"}, {label: "Reload UI", action: "window.location.reload(true);"}, {label: "Restart eDEX", action: "electron.remote.app.relaunch();electron.remote.app.quit();"} ] }, () => { // Link the keyboard back to the terminal window.keyboard.attach(); // Focus back on the term window.term[window.currentTerm].term.focus(); }); }; window.writeSettingsFile = () => { window.settings = { shell: document.getElementById("settingsEditor-shell").value, cwd: document.getElementById("settingsEditor-cwd").value, env: document.getElementById("settingsEditor-env").value, keyboard: document.getElementById("settingsEditor-keyboard").value, theme: document.getElementById("settingsEditor-theme").value, termFontSize: Number(document.getElementById("settingsEditor-termFontSize").value), audio: (document.getElementById("settingsEditor-audio").value === "true"), disableFeedbackAudio: (document.getElementById("settingsEditor-disableFeedbackAudio").value === "true"), pingAddr: document.getElementById("settingsEditor-pingAddr").value, port: Number(document.getElementById("settingsEditor-port").value), monitor: Number(document.getElementById("settingsEditor-monitor").value), nointro: (document.getElementById("settingsEditor-nointro").value === "true"), nocursor: (document.getElementById("settingsEditor-nocursor").value === "true"), iface: document.getElementById("settingsEditor-iface").value, allowWindowed: (document.getElementById("settingsEditor-allowWindowed").value === "true"), excludeSelfFromToplist: (document.getElementById("settingsEditor-excludeSelfFromToplist").value === "true"), hideDotfiles: (document.getElementById("settingsEditor-hideDotfiles").value === "true"), experimentalGlobeFeatures: (document.getElementById("settingsEditor-experimentalGlobeFeatures").value === "true"), experimentalFeatures: (document.getElementById("settingsEditor-experimentalFeatures").value === "true") }; Object.keys(window.settings).forEach(key => { if (window.settings[key] === "undefined") { delete window.settings[key]; } }); fs.writeFileSync(settingsFile, JSON.stringify(window.settings, "", 4)); document.getElementById("settingsEditorStatus").innerText = "New values written to settings.json file at "+new Date().toTimeString(); }; // Display available keyboard shortcuts window.openShortcutsHelp = () => { new Modal({ type: "custom", title: `Available Keyboard Shortcuts <i>(v${electron.remote.app.getVersion()})</i>`, html: `<h5>Using either the on-screen or a physical keyboard, you can use the following shortcuts:</h5> <table id="shortcutsHelp" style="width: 100%;"> <tr> <th>Trigger</th> <th>Action</th> </tr> <tr> <td>${process.platform === "darwin" ? "Command" : "Ctrl + Shift"} + C</td> <td>Copy selected buffer from the terminal.</td> </tr> <tr> <td>${process.platform === "darwin" ? "Command" : "Ctrl + Shift"} + V</td> <td>Paste system clipboard to the terminal.</td> </tr> <tr> <td>${process.platform === "darwin" ? "Command" : "Ctrl"} + Tab</td> <td>Switch to the next opened terminal tab (left to right order).</td> </tr> <tr> <td>${process.platform === "darwin" ? "Command" : "Ctrl"} + Shift + Tab</td> <td>Switch to the previous opened terminal tab (right to left order).</td> </tr> <tr> <td>${process.platform === "darwin" ? "Command" : "Ctrl"} + [1-5]</td> <td>Switch to a specific terminal tab, or create it if it hasn't been opened yet.</td> </tr> <tr> <td>${process.platform === "darwin" ? "Command" : "Ctrl"} + Shift + S</td> <td>Open the settings editor.</td> </tr> <tr> <td>${process.platform === "darwin" ? "Command" : "Ctrl"} + Shift + K</td> <td>List available keyboard shortcuts.</td> </tr> <tr> <td>${process.platform === "darwin" ? "Command" : "Ctrl"} + Shift + H</td> <td>Toggle hidden files and directories in the file browser.</td> </tr> <tr> <td>${process.platform === "darwin" ? "Command" : "Ctrl"} + Shift + P</td> <td>Toggle the on-screen keyboard's "Password Mode", that allows you to safely type<br> sensitive information even if your screen might be recorded (disables visual input feedback).</td> </tr> <tr> <td>${process.platform === "darwin" ? "Command" : "Ctrl"} + Shift + I</td> <td>Open Chromium Dev Tools (for debugging purposes).</td> </tr> </table> <br>` }); }; // Global keyboard shortcuts const globalShortcut = electron.remote.globalShortcut; globalShortcut.unregisterAll(); function registerKeyboardShortcuts() { // Open inspector globalShortcut.register("CommandOrControl+Shift+I", () => { electron.remote.getCurrentWindow().webContents.toggleDevTools(); }); // Open settings globalShortcut.register("CommandOrControl+Shift+S", () => { if (!document.getElementById("settingsEditor")) { window.openSettings(); } }); // Open list of keyboard shortcuts globalShortcut.register("CommandOrControl+Shift+K", () => { if (!document.getElementById("shortcutsHelp")) { window.openShortcutsHelp(); } }); // Copy and paste shortcuts if (process.platform === "darwin") { // See #342, we have an actual available key on macOS to do this globalShortcut.register("Command+C", () => { window.term[window.currentTerm].clipboard.copy(); }); globalShortcut.register("Command+V", () => { window.term[window.currentTerm].clipboard.paste(); }); } else { // Use Ctrl+shift on other OSs globalShortcut.register("Ctrl+Shift+C", () => { window.term[window.currentTerm].clipboard.copy(); }); globalShortcut.register("Ctrl+Shift+V", () => { window.term[window.currentTerm].clipboard.paste(); }); } // Switch tabs // Next globalShortcut.register("CommandOrControl+Tab", () => { if (window.term[window.currentTerm+1]) { window.focusShellTab(window.currentTerm+1); } else if (window.term[window.currentTerm+2]) { window.focusShellTab(window.currentTerm+2); } else if (window.term[window.currentTerm+3]) { window.focusShellTab(window.currentTerm+3); } else if (window.term[window.currentTerm+4]) { window.focusShellTab(window.currentTerm+4); } else { window.focusShellTab(0); } }); // Previous globalShortcut.register("CommandOrControl+Shift+Tab", () => { let i = window.currentTerm ? window.currentTerm : 4; if (window.term[i] && i !== window.currentTerm) { window.focusShellTab(i); } else if (window.term[i-1]) { window.focusShellTab(i-1); } else if (window.term[i-2]) { window.focusShellTab(i-2); } else if (window.term[i-3]) { window.focusShellTab(i-3); } else if (window.term[i-4]) { window.focusShellTab(i-4); } }); // By tab number globalShortcut.register("CommandOrControl+1", () => { window.focusShellTab(0); }); globalShortcut.register("CommandOrControl+2", () => { window.focusShellTab(1); }); globalShortcut.register("CommandOrControl+3", () => { window.focusShellTab(2); }); globalShortcut.register("CommandOrControl+4", () => { window.focusShellTab(3); }); globalShortcut.register("CommandOrControl+5", () => { window.focusShellTab(4); }); // Toggle hiding dotfiles in fsDisp globalShortcut.register("CommandOrControl+Shift+H", () => { window.fsDisp.toggleHidedotfiles(); }); // Hide on-screen keyboard visual feedback (#394) globalShortcut.register("CommandOrControl+Shift+P", () => { window.keyboard.togglePasswordMode(); }); } registerKeyboardShortcuts(); // See #361 window.addEventListener("focus", () => { registerKeyboardShortcuts(); }); window.addEventListener("blur", () => { globalShortcut.unregisterAll(); }); // Prevent showing menu, exiting fullscreen or app with keyboard shortcuts document.addEventListener("keydown", e => { if (e.key === "Alt") { e.preventDefault(); } if (e.key === "F11" && !settings.allowWindowed) { e.preventDefault(); } if (e.code === "KeyD" && e.ctrlKey) { e.preventDefault(); } if (e.code === "KeyA" && e.ctrlKey) { e.preventDefault(); } }); // Fix #265 window.addEventListener("keyup", e => { if (e.key === "F4" && e.altKey === true) { electron.remote.app.quit(); } }); // Fix double-tap zoom on touchscreens electron.webFrame.setVisualZoomLevelLimits(1, 1); // Resize terminal with window window.onresize = () => { if (typeof window.currentTerm !== "undefined") { if (typeof window.term[window.currentTerm] !== "undefined") { window.term[window.currentTerm].fit(); } } }; // See #413 window.resizeTimeout = null; let electronWin = electron.remote.getCurrentWindow(); electronWin.on("resize", () => { clearTimeout(window.resizeTimeout); window.resizeTimeout = setTimeout(() => { let win = electron.remote.getCurrentWindow(); if (win.isFullScreen()) return false; if (win.isMaximized()) { win.unmaximize(); win.setFullScreen(true); return false; } let size = win.getSize(); if (size[0] >= size[1]) { win.setSize(size[0], parseInt(size[0] * 9 / 16)); } else { win.setSize(size[1], parseInt(size[1] * 9 / 16)); } }, 100); }); electronWin.on("leave-full-screen", () => { electron.remote.getCurrentWindow().setSize(960, 540); });