diff --git a/frontend/config.json b/frontend/config.json index 87e1488..b793d51 100644 --- a/frontend/config.json +++ b/frontend/config.json @@ -3,6 +3,7 @@ "files": "ts/files.ts", "layout": "sass/layout.scss", "index": "sass/index.scss", + "error": "sass/error.scss", "service-settings": "sass/service-settings.scss" } } \ No newline at end of file diff --git a/frontend/error.html b/frontend/error.html new file mode 100644 index 0000000..39173a3 --- /dev/null +++ b/frontend/error.html @@ -0,0 +1,22 @@ + + + + + An error occured + + + + + + + + + +

Oops

+

An error has occurred while loading this document.

+

Please check your internet connection and this service's URL.

+

If you can load the actual URL in your favorite web browser, please file us a bug report.

+ + diff --git a/frontend/sass/error.scss b/frontend/sass/error.scss new file mode 100644 index 0000000..3529fc9 --- /dev/null +++ b/frontend/sass/error.scss @@ -0,0 +1,9 @@ +html, body { + height: 100%; +} +body { + display: flex; + flex-direction: column; + justify-content: center; + align-self: center; +} \ No newline at end of file diff --git a/frontend/sass/index.scss b/frontend/sass/index.scss index 4e663c7..fbd5acf 100644 --- a/frontend/sass/index.scss +++ b/frontend/sass/index.scss @@ -7,6 +7,16 @@ body { flex-direction: row; } +#service-buttons { + flex-grow: 1; + + overflow: hidden auto; +} + +*:focus { + outline-color: rgb(118, 93, 176); +} + #navigation { display: flex; flex-direction: column; @@ -16,16 +26,43 @@ body { body.fullscreen & { display: none; } -} -#navigation > :not(#service-buttons) { - flex-shrink: 0; -} + > :not(#service-buttons) { + flex-shrink: 0; + } -#service-buttons { - flex-grow: 1; + button { + position: relative; + display: block; + width: var(--nav-width); + height: var(--nav-width); + margin: 0; + padding: 0; - overflow: hidden auto; + color: #fff; + border: 0; + background: transparent; + + cursor: pointer; + border-radius: 0; + + &:focus { + outline: none; + background: #fff3 !important; + } + + &:hover { + background-color: #fff3; + } + + i { + font-size: calc(var(--nav-width) / 2); + } + + img { + width: calc(var(--nav-width) / 2); + } + } } #service-selector { @@ -34,20 +71,76 @@ body { padding: 0; list-style: none; -} -#service-selector::-webkit-scrollbar { - width: 6px; -} + &::-webkit-scrollbar { + width: 6px; + } -#service-selector [draggable] { - user-select: none; - background-color: rgb(43, 43, 43); -} + li { + position: relative; -#service-selector [draggable] img { - -webkit-user-drag: none; - user-drag: none; + button { + border-radius: 0; + } + + &.active button { + background-color: #fff2; + } + + &.loading, &.loaded { + button::before { + content: ""; + display: block; + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); + height: 75%; + border-right: 4px solid #ffffff3e; + } + } + + &.loading button::before { + animation: loading-button-after linear 500ms infinite alternate; + height: 45%; + + @keyframes loading-button-after { + from { + opacity: 0.1; + } + to { + opacity: 0.5; + } + } + } + } + + [draggable] { + user-select: none; + background-color: rgb(43, 43, 43); + + img { + -webkit-user-drag: none; + user-drag: none; + } + } + + .drag-target-self button::after { + height: var(--nav-width); + border: 1px dashed #fff; + transform: none; + } + + .drag-target button::after { + position: absolute; + left: 0; + top: 0; + width: 100%; + } + + .drag-target::after { + top: 75% !important; + } } #service-selector .drag-target button::after, @@ -60,96 +153,6 @@ body { background: #fff9; } -#service-selector .drag-target-self button::after { - height: var(--nav-width); - border: 1px dashed #fff; - transform: none; -} - -#service-selector .drag-target button::after { - position: absolute; - left: 0; - top: 0; - width: 100%; -} - -#service-selector .drag-target::after { - top: 75% !important; -} - -*:focus { - outline-color: rgb(118, 93, 176); -} - -#navigation button { - position: relative; - display: block; - width: var(--nav-width); - height: var(--nav-width); - margin: 0; - padding: 0; - - color: #fff; - border: 0; - background: transparent; - - cursor: pointer; -} - -#navigation button:focus { - outline: none; - background: #fff3 !important; -} - -#navigation button img { - width: calc(var(--nav-width) / 2); -} - -#navigation button i { - font-size: calc(var(--nav-width) / 2); -} - -#service-selector li { - position: relative; -} - -#service-selector li button, -#navigation > button { - border-radius: 0; -} - -#service-selector li.active button { - background-color: #fff2; -} - -#service-selector li.loaded button::before { - content: ""; - display: block; - position: absolute; - right: 0; - top: 50%; - transform: translateY(-50%); - height: 75%; - border-right: 4px solid #ffffff3e; -} - -/*#service-selector li.loaded::after {*/ -/* content: "";*/ -/* position: absolute;*/ -/* top: 50%;*/ -/* right: 2px;*/ -/* transform: translateY(-50%);*/ -/* width: 4px;*/ -/* height: 4px;*/ - -/* background-color: #fff;*/ -/* border-radius: 100%;*/ -/*}*/ - -#navigation button:hover { - background-color: #fff3; -} - #history { display: flex; flex-direction: column; diff --git a/frontend/sass/service-settings.scss b/frontend/sass/service-settings.scss index 732ea91..159cc35 100644 --- a/frontend/sass/service-settings.scss +++ b/frontend/sass/service-settings.scss @@ -71,21 +71,27 @@ form { margin: 8px; grid-template-columns: 0fr auto 0fr; -} -.form-group > * { - margin: 8px; - padding: 8px; - white-space: nowrap; - align-self: center; -} + &.no-expand { + display: flex; + flex-direction: row; + justify-content: center; + } -.form-group > :first-child { - justify-self: end; -} + > * { + margin: 8px; + padding: 8px; + white-space: nowrap; + align-self: center; + } -.form-group > :not(:first-child) { - margin-left: 8px; + > :first-child { + justify-self: end; + } + + > :not(:first-child) { + margin-left: 8px; + } } label.form-group { diff --git a/frontend/service-settings.html b/frontend/service-settings.html index bd0fd68..c2673cd 100644 --- a/frontend/service-settings.html +++ b/frontend/service-settings.html @@ -42,11 +42,18 @@ -
- - - -
+
+ Disguise +
+ + +
+ +
+ + +
+
diff --git a/frontend/ts/index.ts b/frontend/ts/index.ts index 6f70f70..28e51ee 100644 --- a/frontend/ts/index.ts +++ b/frontend/ts/index.ts @@ -26,7 +26,7 @@ let securityButton: HTMLElement | null, backButton: HTMLElement | null, refreshButton: HTMLElement | null; let addButton, settingsButton; -let emptyPage: string; +let emptyPage: string, errorPage: string; let urlPreview: HTMLElement | null; let serviceSelector: HTMLElement | null; @@ -128,7 +128,7 @@ function openServiceContextMenu(event: Event, serviceId: number) { } -ipcRenderer.on('data', (event, appData, iconSets, actualSelectedService, emptyUrl, config) => { +ipcRenderer.on('data', (event, appData, iconSets, actualSelectedService, emptyUrl, errorUrl, config) => { // App info appInfo.title = appData.title; @@ -179,6 +179,7 @@ ipcRenderer.on('data', (event, appData, iconSets, actualSelectedService, emptyUr // Empty emptyPage = emptyUrl; + errorPage = errorUrl; // Url preview element urlPreview = document.getElementById("url-preview"); @@ -212,13 +213,15 @@ ipcRenderer.on('data', (event, appData, iconSets, actualSelectedService, emptyUr document.documentElement.style.setProperty('--nav-width', config.bigNavBar ? '64px' : '48px'); }); -function removeServiceFeatures(id: number): HTMLElement | null { +function removeServiceFeatures(id: number): Element | null { // Remove nav const nav = document.querySelector('#service-selector'); let oldNavButton: HTMLElement | null = null; + let nextSibling: Element | null = null; if (nav) { oldNavButton = nav.querySelector('li:nth-of-type(' + (id + 1) + ')'); if (oldNavButton) { + nextSibling = oldNavButton.nextElementSibling; nav.removeChild(oldNavButton); } } @@ -228,19 +231,21 @@ function removeServiceFeatures(id: number): HTMLElement | null { document.querySelector('#services')?.removeChild(services[id].view); } - return oldNavButton; + return nextSibling; } ipcRenderer.on('updateService', (e, id, data) => { if (id === null) { + console.log('Adding new service'); services.push(data); createService(services.length - 1); } else { - const oldNavButton = removeServiceFeatures(id); + console.log('Updating existing service', id); + const nextSibling = removeServiceFeatures(id); // Create new service services[id] = data; - createService(id, oldNavButton ? oldNavButton.nextElementSibling : null); + createService(id, nextSibling); if (parseInt(selectedService) === id) { setActiveService(id); } @@ -324,6 +329,8 @@ function createService(index: number, nextNavButton?: Element | null) { iconProperties.faIcon.split(' ').forEach((cl: string) => { icon.classList.add(cl); }); + } else { + icon.classList.add('fas', 'fa-circle'); } } @@ -464,6 +471,7 @@ function loadService(serviceId: number, service: any) { document.querySelector('#services > .loader')?.classList.remove('hidden'); service.view = document.createElement('webview'); + updateNavigation(); // Start loading animation service.view.setAttribute('enableRemoteModule', 'false'); service.view.setAttribute('partition', 'persist:service_' + service.partition); service.view.setAttribute('autosize', 'true'); @@ -473,6 +481,11 @@ function loadService(serviceId: number, service: any) { // eventual future human mistakes. service.view.setAttribute('webpreferences', 'contextIsolation=yes'); + // Error handling + service.view.addEventListener('did-fail-load', (e: Event) => { + service.view.setAttribute('src', errorPage); + }); + // Append element to DOM document.querySelector('#services')?.appendChild(service.view); @@ -643,10 +656,16 @@ function updateNavigation() { for (let i = 0; i < services.length; i++) { const service = services[i]; + if (!service.li) continue; + // Active? if (parseInt(selectedService) === i) service.li.classList.add('active'); else service.li.classList.remove('active'); + // Loading? + if (service.view && !service.viewReady) service.li.classList.add('loading'); + else service.li.classList.remove('loading'); + // Loaded? if (service.viewReady) service.li.classList.add('loaded'); else service.li.classList.remove('loaded'); diff --git a/frontend/ts/service-settings.ts b/frontend/ts/service-settings.ts index bbe591c..179f0f8 100644 --- a/frontend/ts/service-settings.ts +++ b/frontend/ts/service-settings.ts @@ -72,10 +72,16 @@ document.addEventListener('DOMContentLoaded', () => { ipcRenderer.send('sync-settings'); - document.getElementById('userAgentAutoFill')?.addEventListener('click', () => { + document.getElementById('userAgentAutoFillFirefox')?.addEventListener('click', () => { let customUserAgent = document.getElementById('custom-user-agent'); if (customUserAgent) { - (customUserAgent).value = 'Mozilla/5.0 (X11; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0'; + (customUserAgent).value = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0'; + } + }); + document.getElementById('userAgentAutoFillChrome')?.addEventListener('click', () => { + let customUserAgent = document.getElementById('custom-user-agent'); + if (customUserAgent) { + (customUserAgent).value = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'; } }); }); @@ -200,7 +206,7 @@ function save() { ipcRenderer.send('saveService', serviceId, service); remote.getCurrentWindow().close(); -}; +} function isValid() { if (typeof service.name !== 'string' || service.name.length === 0) { diff --git a/package.json b/package.json index bdf1955..cf07a1c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tabs", - "version": "1.1.3", + "version": "1.1.4", "description": "Persistent and separate browser tabs in one window.", "author": { "name": "Alice Gaudon", diff --git a/src/windows/MainWindow.ts b/src/windows/MainWindow.ts index 7e91f82..776a072 100644 --- a/src/windows/MainWindow.ts +++ b/src/windows/MainWindow.ts @@ -147,6 +147,7 @@ export default class MainWindow extends Window { Meta.ICON_SETS, this.activeService, path.resolve(Meta.RESOURCES_PATH, 'empty.html'), + path.resolve(Meta.RESOURCES_PATH, 'error.html'), this.config ); }