From df132c36d03b3f5c32ba4c0a329c4e8a29dbde19 Mon Sep 17 00:00:00 2001 From: Ivan Date: Tue, 6 Sep 2022 20:47:42 +0300 Subject: [PATCH] refactor: Refactors the code --- app.js | 652 +++++++++++++++++++++------------------ package-lock.json | 174 ++++++++++- package.json | 1 + web/assets/checkmark.css | 13 +- web/assets/error.css | 7 + web/assets/style.css | 15 +- web/error.html | 86 ++---- web/index.html | 152 +++++---- 8 files changed, 651 insertions(+), 449 deletions(-) diff --git a/app.js b/app.js index 89f26e0..3158956 100644 --- a/app.js +++ b/app.js @@ -9,23 +9,13 @@ const { Menu, Tray, BrowserWindow, -} = require("electron"); -const { autoUpdater } = require("electron-updater"); -const AutoLaunch = require("auto-launch"); -const Positioner = require("electron-traywindow-positioner"); -const Store = require("electron-store"); -const bonjour = require("bonjour")(); +} = require('electron'); +const { autoUpdater } = require('electron-updater'); +const AutoLaunch = require('auto-launch'); +const Positioner = require('electron-traywindow-positioner'); +const Store = require('electron-store'); +const bonjour = require('bonjour')(); const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); -const registerKeyboardShortcut = () => { - globalShortcut.register("CommandOrControl+Alt+X", () => { - if (window.isVisible()) window.hide(); - else showWindow(); - }); -}; - -const unregisterKeyboardShortcut = () => { - globalShortcut.unregisterAll(); -}; app.allowRendererProcessReuse = true; @@ -33,16 +23,20 @@ app.allowRendererProcessReuse = true; if (!app.requestSingleInstanceLock()) { app.quit(); } else { - app.on("second-instance", () => { - if (window) showWindow(); + app.on('second-instance', () => { + if (window) { + showWindow(); + } }); } // hide dock icon on macOS -if (process.platform === "darwin") app.dock.hide(); +if (process.platform === 'darwin') { + app.dock.hide(); +} const store = new Store(); -const autoLauncher = new AutoLaunch({ name: "Home Assistant Desktop" }); +const autoLauncher = new AutoLaunch({ name: 'Home Assistant Desktop' }); const indexFile = `file://${__dirname}/web/index.html`; const errorFile = `file://${__dirname}/web/error.html`; @@ -51,25 +45,43 @@ let autostartEnabled = false; let forceQuit = false; let resizeEvent = false; -const useAutoUpdater = () => { - autoUpdater.on("error", (message) => { - console.error("There was a problem updating the application"); +function registerKeyboardShortcut() { + globalShortcut.register('CommandOrControl+Alt+X', () => { + if (window.isVisible()) { + window.hide(); + } else { + showWindow(); + } + }); +} + +function unregisterKeyboardShortcut() { + globalShortcut.unregisterAll(); +} + +function useAutoUpdater() { + autoUpdater.on('error', (message) => { + console.error('There was a problem updating the application'); console.error(message); }); - autoUpdater.on("update-downloaded", () => { + autoUpdater.on('update-downloaded', () => { forceQuit = true; autoUpdater.quitAndInstall(); }); setInterval(() => { - if (store.get("autoUpdate")) autoUpdater.checkForUpdates(); + if (store.get('autoUpdate')) { + autoUpdater.checkForUpdates(); + } }, 1000 * 60 * 60); - if (store.get("autoUpdate")) autoUpdater.checkForUpdates(); -}; + if (store.get('autoUpdate')) { + autoUpdater.checkForUpdates(); + } +} -const checkAutoStart = () => { +function checkAutoStart() { autoLauncher .isEnabled() .then((isEnabled) => { @@ -78,23 +90,31 @@ const checkAutoStart = () => { .catch((err) => { console.error(err); }); -}; +} -const startAvailabilityCheck = () => { - setInterval(() => { +function startAvailabilityCheck() { + let interval; + + function availabilityCheck() { const request = net.request(`${currentInstance()}/auth/providers`); - request.on("response", (response) => { + request.on('response', (response) => { showError(response.statusCode !== 200); }); - request.on("error", (error) => { + request.on('error', (error) => { + clearInterval(interval); showError(true); - if (store.get("automaticSwitching")) checkForAvailableInstance(); + + if (store.get('automaticSwitching')) { + checkForAvailableInstance(); + } }); request.end(); - }, 3000); + } + + interval = setInterval(availabilityCheck, 3000); }; -const changePosition = () => { +function changePosition() { const trayBounds = tray.getBounds(); const windowBounds = window.getBounds(); const displayWorkArea = screen.getDisplayNearestPoint({ @@ -103,77 +123,62 @@ const changePosition = () => { }).workArea; const taskBarPosition = Positioner.getTaskbarPosition(trayBounds); - if (taskBarPosition == "top" || taskBarPosition == "bottom") { + if (taskBarPosition === 'top' || taskBarPosition === 'bottom') { const alignment = { - x: "center", - y: taskBarPosition == "top" ? "up" : "down", + x: 'center', + y: taskBarPosition === 'top' ? 'up' : 'down', }; - if ( - trayBounds.x + (trayBounds.width + windowBounds.width) / 2 < - displayWorkArea.width - ) + + if (trayBounds.x + (trayBounds.width + windowBounds.width) / 2 < displayWorkArea.width) { Positioner.position(window, trayBounds, alignment); - else { - const { y } = Positioner.calculate( - window.getBounds(), - trayBounds, - alignment - ); + } else { + const { y } = Positioner.calculate(window.getBounds(), trayBounds, alignment); window.setPosition( displayWorkArea.width - windowBounds.width + displayWorkArea.x, - y + (taskBarPosition == "bottom" && displayWorkArea.y), - false + y + (taskBarPosition === 'bottom' && displayWorkArea.y), + false, ); } } else { - const alignment = { x: taskBarPosition, y: "center" }; - if ( - trayBounds.y + (trayBounds.height + windowBounds.height) / 2 < - displayWorkArea.height - ) { - const { x, y } = Positioner.calculate( - window.getBounds(), - trayBounds, - alignment - ); - window.setPosition( - x + (taskBarPosition == "right" && displayWorkArea.x), - y - ); + const alignment = { + x: taskBarPosition, + y: 'center', + }; + + if (trayBounds.y + (trayBounds.height + windowBounds.height) / 2 < displayWorkArea.height) { + const { x, y } = Positioner.calculate(window.getBounds(), trayBounds, alignment); + window.setPosition(x + (taskBarPosition === 'right' && displayWorkArea.x), y); } else { - const { x } = Positioner.calculate( - window.getBounds(), - trayBounds, - alignment - ); - window.setPosition( - x, - displayWorkArea.y + displayWorkArea.height - windowBounds.height, - false - ); + const { x } = Positioner.calculate(window.getBounds(), trayBounds, alignment); + window.setPosition(x, displayWorkArea.y + displayWorkArea.height - windowBounds.height, false); } } -}; +} + +function checkForAvailableInstance() { + const instances = store.get('allInstances'); -const checkForAvailableInstance = () => { - const instances = store.get("allInstances"); if (instances?.length > 1) { - bonjour.find({ type: "home-assistant" }, (instance) => { - if (instances.indexOf(instance.txt.internal_url) !== -1) + bonjour.find({ type: 'home-assistant' }, (instance) => { + if (instance.txt.internal_url && instances.indexOf(instance.txt.internal_url) !== -1) { return currentInstance(instance.txt.internal_url); - if (instances.indexOf(instance.txt.external_url) !== -1) + } + + if (instance.txt.external_url && instances.indexOf(instance.txt.external_url) !== -1) { return currentInstance(instance.txt.external_url); + } }); let found; for (let instance of instances.filter((e) => e.url !== currentInstance())) { const request = net.request(`${instance}/auth/providers`); - request.on("response", (response) => { + request.on('response', (response) => { if (response.statusCode === 200) { found = instance; } }); - request.on("error", (error) => {}); + request.on('error', (_) => { + }); request.end(); if (found) { @@ -182,29 +187,29 @@ const checkForAvailableInstance = () => { } } } -}; +} -const getMenu = () => { +function getMenu() { let instancesMenu = [ { - label: "Open in Browser", + label: 'Open in Browser', enabled: currentInstance(), click: () => { shell.openExternal(currentInstance()); }, }, { - type: "separator", + type: 'separator', }, ]; - const allInstances = store.get("allInstances"); + const allInstances = store.get('allInstances'); if (allInstances) { allInstances.forEach((e) => { instancesMenu.push({ label: e, - type: "checkbox", + type: 'checkbox', checked: currentInstance() === e, click: () => { currentInstance(e); @@ -216,137 +221,150 @@ const getMenu = () => { instancesMenu.push( { - type: "separator", + type: 'separator', }, { - label: "Add another Instance...", + label: 'Add another Instance...', click: () => { - store.delete("currentInstance"); + store.delete('currentInstance'); window.loadURL(indexFile); window.show(); }, }, { - label: "Automatic Switching", - type: "checkbox", - enabled: - store.has("allInstances") && store.get("allInstances").length > 1, - checked: store.get("automaticSwitching"), + label: 'Automatic Switching', + type: 'checkbox', + enabled: store.has('allInstances') && store.get('allInstances').length > 1, + checked: store.get('automaticSwitching'), click: () => { - store.set("automaticSwitching", !store.get("automaticSwitching")); + store.set('automaticSwitching', !store.get('automaticSwitching')); }, - } + }, ); } else { - instancesMenu.push({ label: "Not Connected...", enabled: false }); + instancesMenu.push({ label: 'Not Connected...', enabled: false }); } return Menu.buildFromTemplate([ { - label: "Show/Hide Window", - visible: process.platform === "linux", + label: 'Show/Hide Window', + visible: process.platform === 'linux', click: () => { - if (window.isVisible()) window.hide(); - else showWindow(); + if (window.isVisible()) { + window.hide(); + } else { + showWindow(); + } }, }, { - visible: process.platform === "linux", - type: "separator", + visible: process.platform === 'linux', + type: 'separator', }, ...instancesMenu, { - type: "separator", + type: 'separator', }, { - label: "Hover to Show", - visible: process.platform !== "linux" && !store.get("detachedMode"), - enabled: !store.get("detachedMode"), - type: "checkbox", - checked: !store.get("disableHover"), + label: 'Hover to Show', + visible: process.platform !== 'linux' && !store.get('detachedMode'), + enabled: !store.get('detachedMode'), + type: 'checkbox', + checked: !store.get('disableHover'), click: () => { - store.set("disableHover", !store.get("disableHover")); + store.set('disableHover', !store.get('disableHover')); }, }, { - label: "Stay on Top", - type: "checkbox", - checked: store.get("stayOnTop"), + label: 'Stay on Top', + type: 'checkbox', + checked: store.get('stayOnTop'), click: () => { - store.set("stayOnTop", !store.get("stayOnTop")); - window.setAlwaysOnTop(store.get("stayOnTop")); - if (window.isAlwaysOnTop()) showWindow(); + store.set('stayOnTop', !store.get('stayOnTop')); + window.setAlwaysOnTop(store.get('stayOnTop')); + + if (window.isAlwaysOnTop()) { + showWindow(); + } }, }, { - label: "Start at Login", - type: "checkbox", + label: 'Start at Login', + type: 'checkbox', checked: autostartEnabled, click: () => { - if (autostartEnabled) autoLauncher.disable(); - else autoLauncher.enable(); + if (autostartEnabled) { + autoLauncher.disable(); + } else { + autoLauncher.enable(); + } + checkAutoStart(); }, }, { - label: `Enable Shortcut`, - type: "checkbox", - accelerator: "CommandOrControl+Alt+X", - checked: store.get("shortcutEnabled"), + label: 'Enable Shortcut', + type: 'checkbox', + accelerator: 'CommandOrControl+Alt+X', + checked: store.get('shortcutEnabled'), click: () => { - store.set("shortcutEnabled", !store.get("shortcutEnabled")); - if (store.get("shortcutEnabled")) registerKeyboardShortcut(); - else unregisterKeyboardShortcut(); + store.set('shortcutEnabled', !store.get('shortcutEnabled')); + + if (store.get('shortcutEnabled')) { + registerKeyboardShortcut(); + } else { + unregisterKeyboardShortcut(); + } }, }, { - type: "separator", + type: 'separator', }, { - label: "Use detached Window", - type: "checkbox", - checked: store.get("detachedMode"), + label: 'Use detached Window', + type: 'checkbox', + checked: store.get('detachedMode'), click: () => { - store.set("detachedMode", !store.get("detachedMode")); + store.set('detachedMode', !store.get('detachedMode')); window.hide(); - createMainWindow(store.get("detachedMode")); + createMainWindow(store.get('detachedMode')); }, }, { - label: "Use Fullscreen", - type: "checkbox", - checked: store.get("fullScreen"), - accelerator: "CommandOrControl+Alt+Return", + label: 'Use Fullscreen', + type: 'checkbox', + checked: store.get('fullScreen'), + accelerator: 'CommandOrControl+Alt+Return', click: () => { toggleFullScreen(); }, }, { - type: "separator", + type: 'separator', }, { label: `v${app.getVersion()}`, enabled: false, }, { - label: "Automatic Updates", - type: "checkbox", - checked: store.get("autoUpdate"), + label: 'Automatic Updates', + type: 'checkbox', + checked: store.get('autoUpdate'), click: () => { - store.set("autoUpdate", !store.get("autoUpdate")); + store.set('autoUpdate', !store.get('autoUpdate')); }, }, { - label: "Open on github.com", + label: 'Open on github.com', click: () => { - shell.openExternal("https://github.com/mrvnklm/homeassistant-desktop"); + shell.openExternal('https://github.com/iprodanovbg/homeassistant-desktop'); }, }, { - type: "separator", + type: 'separator', }, { - label: "Reload Window", + label: 'Reload Window', click: () => { window.reload(); window.show(); @@ -354,12 +372,12 @@ const getMenu = () => { }, }, { - label: "Reset Application...", + label: 'Reset Application...', click: () => { dialog .showMessageBox({ - message: "Are you sure you want to reset Home Assistant Desktop?", - buttons: ["Reset Everything!", "Reset Windows", "Cancel"], + message: 'Are you sure you want to reset Home Assistant Desktop?', + buttons: ['Reset Everything!', 'Reset Windows', 'Cancel'], }) .then((res) => { if (res.response === 0) { @@ -369,12 +387,13 @@ const getMenu = () => { app.relaunch(); app.exit(); } + if (res.response === 1) { - store.delete("windowSizeDetached"); - store.delete("windowSize"); - store.delete("windowPosition"); - store.delete("fullScreen"); - store.delete("detachedMode"); + store.delete('windowSizeDetached'); + store.delete('windowSize'); + store.delete('windowPosition'); + store.delete('fullScreen'); + store.delete('detachedMode'); app.relaunch(); app.exit(); } @@ -382,19 +401,19 @@ const getMenu = () => { }, }, { - type: "separator", + type: 'separator', }, { - label: "Quit", + label: 'Quit', click: () => { forceQuit = true; app.quit(); }, }, ]); -}; +} -const createMainWindow = (show = false) => { +function createMainWindow(show = false) { window = new BrowserWindow({ width: 420, height: 420, @@ -411,87 +430,103 @@ const createMainWindow = (show = false) => { // window.webContents.openDevTools(); window.loadURL(indexFile); - // open extenal links in default browser - window.webContents.on("new-window", function (e, url) { - e.preventDefault(); - require("electron").shell.openExternal(url); + // open external links in default browser + window.webContents.setWindowOpenHandler(({ url }) => { + shell.openExternal(url); + return { action: 'deny' }; }); // hide scrollbar - window.webContents.on("did-finish-load", function () { - window.webContents.insertCSS( - "::-webkit-scrollbar { display: none; } body { -webkit-user-select: none; }" - ); + window.webContents.on('did-finish-load', function () { + window.webContents.insertCSS('::-webkit-scrollbar { display: none; } body { -webkit-user-select: none; }'); - if (store.get("detachedMode") && process.platform === "darwin") { - window.webContents.insertCSS("body { -webkit-app-region: drag; }"); + if (store.get('detachedMode') && process.platform === 'darwin') { + window.webContents.insertCSS('body { -webkit-app-region: drag; }'); } - // let code = `document.addEventListener("mousemove", () => { ipcRenderer.send("mousemove"); });`; + // let code = `document.addEventListener('mousemove', () => { ipcRenderer.send('mousemove'); });`; // window.webContents.executeJavaScript(code); }); - if (store.get("detachedMode")) { - if (store.has("windowPosition")) - window.setSize(...store.get("windowSizeDetached")); - else store.set("windowPosition", window.getPosition()); - if (store.has("windowSizeDetached")) - window.setPosition(...store.get("windowPosition")); - else store.set("windowSizeDetached", window.getSize()); + if (store.get('detachedMode')) { + if (store.has('windowPosition')) { + window.setSize(...store.get('windowSizeDetached')); + } else { + store.set('windowPosition', window.getPosition()); + } + + if (store.has('windowSizeDetached')) { + window.setPosition(...store.get('windowPosition')); + } else { + store.set('windowSizeDetached', window.getSize()); + } + } else if (store.has('windowSize')) { + window.setSize(...store.get('windowSize')); } else { - if (store.has("windowSize")) window.setSize(...store.get("windowSize")); - else store.set("windowSize", window.getSize()); + store.set('windowSize', window.getSize()); } - window.on("resize", (e) => { + window.on('resize', (e) => { // ignore resize event when using fullscreen mode - if (window.isFullScreen()) return e; + if (window.isFullScreen()) { + return e; + } - if (!store.get("disableHover") || resizeEvent) { - store.set("disableHover", true); + if (!store.get('disableHover') || resizeEvent) { + store.set('disableHover', true); resizeEvent = e; setTimeout(() => { if (resizeEvent === e) { - store.set("disableHover", false); + store.set('disableHover', false); resizeEvent = false; } }, 600); } - if (store.get("detachedMode")) { - store.set("windowSizeDetached", window.getSize()); + if (store.get('detachedMode')) { + store.set('windowSizeDetached', window.getSize()); } else { - if (process.platform !== "linux") changePosition(); + if (process.platform !== 'linux') { + changePosition(); + } - store.set("windowSize", window.getSize()); + store.set('windowSize', window.getSize()); } }); - window.on("move", () => { - if (store.get("detachedMode")) { - store.set("windowPosition", window.getPosition()); + window.on('move', () => { + if (store.get('detachedMode')) { + store.set('windowPosition', window.getPosition()); } }); - window.on("close", (e) => { + window.on('close', (e) => { if (!forceQuit) { window.hide(); e.preventDefault(); } }); - window.on("blur", () => { - if (!store.get("detachedMode") && !window.isAlwaysOnTop()) window.hide(); + window.on('blur', () => { + if (!store.get('detachedMode') && !window.isAlwaysOnTop()) { + window.hide(); + } }); - window.setAlwaysOnTop(!!store.get("stayOnTop")); - if (window.isAlwaysOnTop() || show) showWindow(); + window.setAlwaysOnTop(!!store.get('stayOnTop')); - toggleFullScreen(!!store.get("fullScreen")); -}; + if (window.isAlwaysOnTop() || show) { + showWindow(); + } + + toggleFullScreen(!!store.get('fullScreen')); +} + +function showWindow() { + if (!store.get('detachedMode')) { + changePosition(); + } -const showWindow = () => { - if (!store.get("detachedMode")) changePosition(); if (!window.isVisible()) { window.setVisibleOnAllWorkspaces(true); // put the window on all screens window.show(); @@ -500,160 +535,189 @@ const showWindow = () => { } }; -const createTray = () => { +function createTray() { tray = new Tray( - ["win32", "linux"].includes(process.platform) - ? `${__dirname}/assets/IconWin.png` - : `${__dirname}/assets/IconTemplate.png` + ['win32', 'linux'].includes(process.platform) ? `${__dirname}/assets/IconWin.png` : `${__dirname}/assets/IconTemplate.png`, ); - tray.on("click", () => { - if (window.isVisible()) window.hide(); - else showWindow(); + tray.on('click', () => { + if (window.isVisible()) { + window.hide(); + } else { + showWindow(); + } }); - tray.on("right-click", () => { - if (!store.get("detachedMode")) window.hide(); + tray.on('right-click', () => { + if (!store.get('detachedMode')) { + window.hide(); + } + tray.popUpContextMenu(getMenu()); }); let timer = undefined; - tray.on("mouse-move", (e) => { - if ( - store.get("detachedMode") || - window.isAlwaysOnTop() || - store.get("disableHover") - ) { + tray.on('mouse-move', () => { + if (store.get('detachedMode') || window.isAlwaysOnTop() || store.get('disableHover')) { return; } + if (!window.isVisible()) { showWindow(); } - if (timer) clearTimeout(timer); + if (timer) { + clearTimeout(timer); + } + timer = setTimeout(() => { let mousePos = screen.getCursorScreenPoint(); let trayBounds = tray.getBounds(); + if ( - !( - mousePos.x >= trayBounds.x && - mousePos.x <= trayBounds.x + trayBounds.width - ) || - !( - mousePos.y >= trayBounds.y && - mousePos.y <= trayBounds.y + trayBounds.height - ) + !(mousePos.x >= trayBounds.x && mousePos.x <= trayBounds.x + trayBounds.width) || + !(mousePos.y >= trayBounds.y && mousePos.y <= trayBounds.y + trayBounds.height) ) { setWindowFocusTimer(); } }, 100); }); -}; +} -const setWindowFocusTimer = () => { - let timer = setTimeout(() => { +function setWindowFocusTimer() { + setTimeout(() => { let mousePos = screen.getCursorScreenPoint(); let windowPosition = window.getPosition(); let windowSize = window.getSize(); + if ( !resizeEvent && - (!( - mousePos.x >= windowPosition[0] && - mousePos.x <= windowPosition[0] + windowSize[0] - ) || - !( - mousePos.y >= windowPosition[1] && - mousePos.y <= windowPosition[1] + windowSize[1] - )) + ( + !(mousePos.x >= windowPosition[ 0 ] && mousePos.x <= windowPosition[ 0 ] + windowSize[ 0 ]) || + !(mousePos.y >= windowPosition[ 1 ] && mousePos.y <= windowPosition[ 1 ] + windowSize[ 1 ]) + ) ) { window.hide(); } else { setWindowFocusTimer(); } }, 110); -}; +} -app.on("ready", async () => { - checkAutoStart(); +function toggleFullScreen(mode = !window.isFullScreen()) { + store.set('fullScreen', mode); + window.setFullScreen(mode); + + if (mode) { + window.setAlwaysOnTop(true); + } else { + window.setAlwaysOnTop(store.get('stayOnTop')); + } +} + +function currentInstance(url = null) { + if (url) { + store.set('currentInstance', store.get('allInstances').indexOf(url)); + } + + if (store.has('currentInstance')) { + return store.get('allInstances')[ store.get('currentInstance') ]; + } + + return false; +} + +function addInstance(url) { + if (!store.has('allInstances')) { + store.set('allInstances', []); + } + + let instances = store.get('allInstances'); + + if (instances.find((e) => e === url)) { + currentInstance(url); + + return; + } + + // active hover by default after adding first instance + if (!instances.length) { + store.set('disableHover', false); + } + + instances.push(url); + store.set('allInstances', instances); + currentInstance(url); +} + +function showError(isError) { + if (!isError && window.webContents.getURL().includes('error.html')) { + window.loadURL(indexFile); + } + + if (isError && currentInstance() && !window.webContents.getURL().includes('error.html')) { + window.loadURL(errorFile); + } +} + +app.on('ready', async () => { useAutoUpdater(); + checkAutoStart(); createTray(); // workaround for initial window misplacement due to traybounds being incorrect while (tray.getBounds().x === 0 && process.uptime() <= 1) { await delay(15); } - createMainWindow(!store.has("currentInstance")); + createMainWindow(!store.has('currentInstance')); + + if (process.platform === 'linux') { + tray.setContextMenu(getMenu()); + } - if (process.platform === "linux") tray.setContextMenu(getMenu()); startAvailabilityCheck(); // register shortcut - if (store.get("shortcutEnabled")) registerKeyboardShortcut(); + if (store.get('shortcutEnabled')) { + registerKeyboardShortcut(); + } - globalShortcut.register("CommandOrControl+Alt+Return", () => { + globalShortcut.register('CommandOrControl+Alt+Return', () => { toggleFullScreen(); }); // disable hover for first start - if (!store.has("currentInstance")) store.set("disableHover", true); + if (!store.has('currentInstance')) { + store.set('disableHover', true); + } + // enable auto update by default - if (!store.has("autoUpdate")) store.set("autoUpdate", true); + if (!store.has('autoUpdate')) { + store.set('autoUpdate', true); + } }); -app.on("will-quit", () => { +app.on('will-quit', () => { unregisterKeyboardShortcut(); }); -const toggleFullScreen = (mode = !window.isFullScreen()) => { - store.set("fullScreen", mode); - window.setFullScreen(mode); - if (mode) window.setAlwaysOnTop(true); - else window.setAlwaysOnTop(store.get("stayOnTop")); -}; +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit(); + } +}); -const currentInstance = (url = null) => { +ipcMain.on('get-instances', (event) => { + event.reply('get-instances', store.get('allInstances') || []); +}); + +ipcMain.on('ha-instance', (event, url) => { if (url) { - store.set("currentInstance", store.get("allInstances").indexOf(url)); - } - if (store.has("currentInstance")) { - return store.get("allInstances")[store.get("currentInstance")]; - } - return false; -}; - -const addInstance = (url) => { - if (!store.has("allInstances")) store.set("allInstances", []); - let instances = store.get("allInstances"); - if (instances.find((e) => e === url)) { - currentInstance(url); - return; + addInstance(url); } - // active hover by default after adding first instance - if (!instances.length) store.set("disableHover", false); - - instances.push(url); - store.set("allInstances", instances); - currentInstance(url); -}; - -const showError = (isError) => { - if (!isError && window.webContents.getURL().includes("error.html")) - window.loadURL(indexFile); - if ( - isError && - currentInstance() && - !window.webContents.getURL().includes("error.html") - ) - window.loadURL(errorFile); -}; - -ipcMain.on("get-instances", (event) => { - event.reply("get-instances", store.get("allInstances") || []); -}); - -ipcMain.on("ha-instance", (event, url) => { - if (url) addInstance(url); - if (currentInstance()) event.reply("ha-instance", currentInstance()); + if (currentInstance()) { + event.reply('ha-instance', currentInstance()); + } }); diff --git a/package-lock.json b/package-lock.json index ec2c4c3..161d77f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -415,6 +415,11 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, "asar": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/asar/-/asar-3.2.0.tgz", @@ -510,6 +515,19 @@ "bluebird": "^3.5.5" } }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, "boolean": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", @@ -602,6 +620,11 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + }, "builder-util": { "version": "23.3.3", "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-23.3.3.tgz", @@ -697,6 +720,15 @@ } } }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -954,6 +986,19 @@ "mimic-response": "^1.0.0" } }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -970,8 +1015,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, - "optional": true, "requires": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" @@ -1083,6 +1126,28 @@ "verror": "^1.10.0" } }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" + }, + "dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==", + "requires": { + "buffer-indexof": "^1.0.0" + } + }, "dot-prop": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", @@ -1511,9 +1576,12 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true, - "optional": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" }, "get-caller-file": { "version": "2.0.5", @@ -1525,8 +1593,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "optional": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -1657,8 +1723,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "optional": true, "requires": { "function-bind": "^1.1.1" } @@ -1673,8 +1737,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "optional": true, "requires": { "get-intrinsic": "^1.1.1" } @@ -1682,9 +1744,15 @@ "has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, - "optional": true + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } }, "has-yarn": { "version": "2.1.0", @@ -1789,6 +1857,20 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-ci": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", @@ -1798,6 +1880,14 @@ "ci-info": "^3.2.0" } }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1831,6 +1921,15 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -2073,6 +2172,20 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==" + }, "node-addon-api": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", @@ -2097,12 +2210,19 @@ "pify": "^3.0.0" } }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "optional": true + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "once": { "version": "1.4.0", @@ -2282,6 +2402,16 @@ "lazy-val": "^1.0.4" } }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, "registry-auth-token": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", @@ -2353,6 +2483,11 @@ "tslib": "^2.1.0" } }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -2588,6 +2723,11 @@ } } }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, "tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", diff --git a/package.json b/package.json index 9e46bc1..ea2811b 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "auto-launch": "^5.0.5", + "bonjour": "^3.5.0", "electron-log": "^4.4.8", "electron-store": "^8.1.0", "electron-traywindow-positioner": "^1.1.1", diff --git a/web/assets/checkmark.css b/web/assets/checkmark.css index c55ac1a..31fea39 100644 --- a/web/assets/checkmark.css +++ b/web/assets/checkmark.css @@ -7,6 +7,7 @@ height: 80px; margin: 0 auto; } + .success-checkmark .check-icon { width: 80px; height: 80px; @@ -15,6 +16,7 @@ box-sizing: content-box; border: 4px solid #4caf50; } + .success-checkmark .check-icon::before { top: 3px; left: -2px; @@ -22,6 +24,7 @@ transform-origin: 100% 50%; border-radius: 100px 0 0 100px; } + .success-checkmark .check-icon::after { top: 0; left: 30px; @@ -30,6 +33,7 @@ border-radius: 0 100px 100px 0; animation: rotate-circle 4.25s ease-in; } + .success-checkmark .check-icon::before, .success-checkmark .check-icon::after { content: ""; @@ -38,6 +42,7 @@ background: #ffffff; transform: rotate(-45deg); } + .success-checkmark .check-icon .icon-line { height: 5px; background-color: #4caf50; @@ -46,6 +51,7 @@ position: absolute; z-index: 10; } + .success-checkmark .check-icon .icon-line.line-tip { top: 46px; left: 14px; @@ -53,6 +59,7 @@ transform: rotate(45deg); animation: icon-line-tip 0.75s; } + .success-checkmark .check-icon .icon-line.line-long { top: 38px; right: 8px; @@ -60,6 +67,7 @@ transform: rotate(-45deg); animation: icon-line-long 0.75s; } + .success-checkmark .check-icon .icon-circle { top: -4px; left: -4px; @@ -71,6 +79,7 @@ box-sizing: content-box; border: 4px solid rgba(76, 175, 80, 0.5); } + .success-checkmark .check-icon .icon-fix { top: 8px; width: 5px; @@ -96,6 +105,7 @@ transform: rotate(-405deg); } } + @keyframes icon-line-tip { 0% { width: 0; @@ -123,6 +133,7 @@ top: 45px; } } + @keyframes icon-line-long { 0% { width: 0; @@ -136,7 +147,7 @@ } 84% { width: 55px; - right: 0px; + right: 0; top: 35px; } 100% { diff --git a/web/assets/error.css b/web/assets/error.css index 9975821..f2c9c31 100644 --- a/web/assets/error.css +++ b/web/assets/error.css @@ -3,27 +3,33 @@ svg { display: block; margin: 40px auto 0; } + .path { stroke-dasharray: 1000; stroke-dashoffset: 0; } + .path.circle { -webkit-animation: dash 0.9s ease-in-out; animation: dash 0.9s ease-in-out; } + .path.line { stroke-dashoffset: 1000; -webkit-animation: dash 0.9s 0.35s ease-in-out forwards; animation: dash 0.9s 0.35s ease-in-out forwards; } + p { text-align: center; margin: 20px 0 60px; font-size: 1.1em; } + p.error { color: #d06079; } + @-webkit-keyframes dash { 0% { stroke-dashoffset: 1000; @@ -32,6 +38,7 @@ p.error { stroke-dashoffset: 0; } } + @keyframes dash { 0% { stroke-dashoffset: 1000; diff --git a/web/assets/style.css b/web/assets/style.css index aaf208a..5775189 100644 --- a/web/assets/style.css +++ b/web/assets/style.css @@ -44,8 +44,7 @@ body { text-overflow: ellipsis; color: rgb(var(--pure-material-onprimary-rgb, 255, 255, 255)); background-color: rgb(var(--pure-material-primary-rgb, 3, 169, 244)); - box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), - 0 1px 5px 0 rgba(0, 0, 0, 0.12); + box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12); font-family: var( --pure-material-font, "Roboto", @@ -100,8 +99,7 @@ body { /* Hover, Focus */ .pure-material-button-contained:hover, .pure-material-button-contained:focus { - box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), - 0 1px 10px 0 rgba(0, 0, 0, 0.12); + box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12); } .pure-material-button-contained:hover::before { @@ -118,8 +116,7 @@ body { /* Active */ .pure-material-button-contained:active { - box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), - 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12); + box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12); } .pure-material-button-contained:active::after { @@ -190,7 +187,7 @@ p { input { background: none; font-size: 18px; - padding: 10px 0px 10px 0px; + padding: 10px 0 10px 0; display: block; width: 320px; border: none; @@ -234,9 +231,9 @@ label { content: ""; height: 2px; width: 0; - bottom: 0px; + bottom: 0; position: absolute; background: #03a9f4; transition: 300ms ease all; - left: 0%; + left: 0; } diff --git a/web/error.html b/web/error.html index 658e8b3..e73e17f 100644 --- a/web/error.html +++ b/web/error.html @@ -1,65 +1,31 @@ - - Home Assistant - - - - - + + Home Assistant + + + + + - -
-
-
- - - - - -
-
-

- Home Assistant instance is not available, please check your - connection. -

-
-
+ +
+
+
+ + + + +
- +
+

+ Home Assistant instance is not available, please check your connection. +

+
+
+
+ diff --git a/web/index.html b/web/index.html index 823e304..1665aee 100644 --- a/web/index.html +++ b/web/index.html @@ -8,49 +8,60 @@ -
-
-
- Home Assistant -
- -
- +
+
+
+ Home Assistant +
+ +
- \ No newline at end of file +