diff --git a/.eslintrc b/.eslintrc index 7797028e9..10e15cec3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -26,10 +26,13 @@ "brace-style": ["error", "1tbs", { "allowSingleLine": true }], "indent": ["error", 4, { "SwitchCase": 1, + "VariableDeclarator": "first", "FunctionDeclaration": { "parameters": "first" }, + "FunctionExpression": { "parameters": "first" }, "CallExpression": { "arguments": "first" }, "ArrayExpression": "first", "ObjectExpression": "first", + "ImportDeclaration": "first", "ignoreComments": true }], "comma-spacing": ["error"], "comma-style": ["error"], diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 715c98538..84e634d2f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -67,27 +67,30 @@ jobs: path: ${{ steps.snapcraft.outputs.snap }} - uses: snapcore/action-publish@v1 with: - store_login: ${{ secrets.SNAPCRAFT_LOGIN }} snap: ${{ steps.snapcraft.outputs.snap }} release: stable + env: + SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_LOGIN }} if: | github.repository == 'novnc/noVNC' && github.event_name == 'release' && !github.event.release.prerelease - uses: snapcore/action-publish@v1 with: - store_login: ${{ secrets.SNAPCRAFT_LOGIN }} snap: ${{ steps.snapcraft.outputs.snap }} release: beta + env: + SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_LOGIN }} if: | github.repository == 'novnc/noVNC' && github.event_name == 'release' && github.event.release.prerelease - uses: snapcore/action-publish@v1 with: - store_login: ${{ secrets.SNAPCRAFT_LOGIN }} snap: ${{ steps.snapcraft.outputs.snap }} release: edge + env: + SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_LOGIN }} if: | github.repository == 'novnc/noVNC' && github.event_name == 'push' && diff --git a/AUTHORS b/AUTHORS index dec0e8932..e8fb12404 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,9 +1,9 @@ maintainers: -- Joel Martin (@kanaka) -- Solly Ross (@directxman12) - Samuel Mannehed for Cendio AB (@samhed) - Pierre Ossman for Cendio AB (@CendioOssman) maintainersEmeritus: +- Joel Martin (@kanaka) +- Solly Ross (@directxman12) - @astrand contributors: # There are a bunch of people that should be here. diff --git a/LICENSE.txt b/LICENSE.txt index ee81d2029..37efdcdba 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -noVNC is Copyright (C) 2019 The noVNC Authors +noVNC is Copyright (C) 2022 The noVNC Authors (./AUTHORS) The noVNC core library files are licensed under the MPL 2.0 (Mozilla diff --git a/README.md b/README.md index c94e13c2c..a771cb438 100644 --- a/README.md +++ b/README.md @@ -65,10 +65,14 @@ Please tweet [@noVNC](http://www.twitter.com/noVNC) if you do. ### Features * Supports all modern browsers including mobile (iOS, Android) -* Supported VNC encodings: raw, copyrect, rre, hextile, tight, tightPNG +* Supported authentication methods: none, classical VNC, RealVNC's + RSA-AES, Tight, VeNCrypt Plain, XVP, Apple's Diffie-Hellman, + UltraVNC's MSLogonII +* Supported VNC encodings: raw, copyrect, rre, hextile, tight, tightPNG, + ZRLE, JPEG * Supports scaling, clipping and resizing the desktop * Local cursor rendering -* Clipboard copy/paste +* Clipboard copy/paste with full Unicode support * Translations * Touch gestures for emulating common mouse actions * Licensed mainly under the [MPL 2.0](http://www.mozilla.org/MPL/2.0/), see @@ -203,11 +207,13 @@ See [AUTHORS](AUTHORS) for a (full-ish) list of authors. If you're not on that list and you think you should be, feel free to send a PR to fix that. * Core team: - * [Joel Martin](https://github.com/kanaka) * [Samuel Mannehed](https://github.com/samhed) (Cendio) - * [Solly Ross](https://github.com/DirectXMan12) (Red Hat / OpenStack) * [Pierre Ossman](https://github.com/CendioOssman) (Cendio) +* Previous core contributors: + * [Joel Martin](https://github.com/kanaka) (Project founder) + * [Solly Ross](https://github.com/DirectXMan12) (Red Hat / OpenStack) + * Notable contributions: * UI and Icons : Pierre Ossman, Chris Gordon * Original Logo : Michael Sersen diff --git a/app/images/icons/Makefile b/app/images/icons/Makefile index be564b43b..03eaed071 100644 --- a/app/images/icons/Makefile +++ b/app/images/icons/Makefile @@ -1,42 +1,42 @@ -ICONS := \ - novnc-16x16.png \ - novnc-24x24.png \ - novnc-32x32.png \ - novnc-48x48.png \ - novnc-64x64.png - -ANDROID_LAUNCHER := \ - novnc-48x48.png \ - novnc-72x72.png \ - novnc-96x96.png \ - novnc-144x144.png \ - novnc-192x192.png - -IPHONE_LAUNCHER := \ - novnc-60x60.png \ - novnc-120x120.png - -IPAD_LAUNCHER := \ - novnc-76x76.png \ - novnc-152x152.png - -ALL_ICONS := $(ICONS) $(ANDROID_LAUNCHER) $(IPHONE_LAUNCHER) $(IPAD_LAUNCHER) +BROWSER_SIZES := 16 24 32 48 64 +#ANDROID_SIZES := 72 96 144 192 +# FIXME: The ICO is limited to 8 icons due to a Chrome bug: +# https://bugs.chromium.org/p/chromium/issues/detail?id=1381393 +ANDROID_SIZES := 96 144 192 +WEB_ICON_SIZES := $(BROWSER_SIZES) $(ANDROID_SIZES) + +#IOS_1X_SIZES := 20 29 40 76 # No such devices exist anymore +IOS_2X_SIZES := 40 58 80 120 152 167 +IOS_3X_SIZES := 60 87 120 180 +ALL_IOS_SIZES := $(IOS_1X_SIZES) $(IOS_2X_SIZES) $(IOS_3X_SIZES) + +ALL_ICONS := \ + $(ALL_IOS_SIZES:%=novnc-ios-%.png) \ + novnc.ico all: $(ALL_ICONS) -novnc-16x16.png: novnc-icon-sm.svg - convert -density 90 \ - -background transparent "$<" "$@" -novnc-24x24.png: novnc-icon-sm.svg - convert -density 135 \ - -background transparent "$<" "$@" -novnc-32x32.png: novnc-icon-sm.svg - convert -density 180 \ - -background transparent "$<" "$@" +# Our testing shows that the ICO file need to be sorted in largest to +# smallest to get the apporpriate behviour +WEB_ICON_SIZES_REVERSE := $(shell echo $(WEB_ICON_SIZES) | tr ' ' '\n' | sort -nr | tr '\n' ' ') +WEB_BASE_ICONS := $(WEB_ICON_SIZES_REVERSE:%=novnc-%.png) +.INTERMEDIATE: $(WEB_BASE_ICONS) +novnc.ico: $(WEB_BASE_ICONS) + convert $(WEB_BASE_ICONS) "$@" + +# General conversion novnc-%.png: novnc-icon.svg - convert -density $$[`echo $* | cut -d x -f 1` * 90 / 48] \ - -background transparent "$<" "$@" + convert -depth 8 -background transparent \ + -size $*x$* "$(lastword $^)" "$@" + +# iOS icons use their own SVG +novnc-ios-%.png: novnc-ios-icon.svg + convert -depth 8 -background transparent \ + -size $*x$* "$(lastword $^)" "$@" + +# The smallest sizes are generated using a different SVG +novnc-16.png novnc-24.png novnc-32.png: novnc-icon-sm.svg clean: rm -f *.png diff --git a/app/images/icons/novnc-120x120.png b/app/images/icons/novnc-120x120.png deleted file mode 100644 index 40823efba..000000000 Binary files a/app/images/icons/novnc-120x120.png and /dev/null differ diff --git a/app/images/icons/novnc-144x144.png b/app/images/icons/novnc-144x144.png deleted file mode 100644 index eee71f11c..000000000 Binary files a/app/images/icons/novnc-144x144.png and /dev/null differ diff --git a/app/images/icons/novnc-152x152.png b/app/images/icons/novnc-152x152.png deleted file mode 100644 index 0694b2de3..000000000 Binary files a/app/images/icons/novnc-152x152.png and /dev/null differ diff --git a/app/images/icons/novnc-16x16.png b/app/images/icons/novnc-16x16.png deleted file mode 100644 index 42108f409..000000000 Binary files a/app/images/icons/novnc-16x16.png and /dev/null differ diff --git a/app/images/icons/novnc-192x192.png b/app/images/icons/novnc-192x192.png deleted file mode 100644 index ef9201f43..000000000 Binary files a/app/images/icons/novnc-192x192.png and /dev/null differ diff --git a/app/images/icons/novnc-24x24.png b/app/images/icons/novnc-24x24.png deleted file mode 100644 index 110613594..000000000 Binary files a/app/images/icons/novnc-24x24.png and /dev/null differ diff --git a/app/images/icons/novnc-32x32.png b/app/images/icons/novnc-32x32.png deleted file mode 100644 index ff00dc305..000000000 Binary files a/app/images/icons/novnc-32x32.png and /dev/null differ diff --git a/app/images/icons/novnc-48x48.png b/app/images/icons/novnc-48x48.png deleted file mode 100644 index f24cd6cc9..000000000 Binary files a/app/images/icons/novnc-48x48.png and /dev/null differ diff --git a/app/images/icons/novnc-60x60.png b/app/images/icons/novnc-60x60.png deleted file mode 100644 index 06b0d609a..000000000 Binary files a/app/images/icons/novnc-60x60.png and /dev/null differ diff --git a/app/images/icons/novnc-64x64.png b/app/images/icons/novnc-64x64.png deleted file mode 100644 index 6d0fb3418..000000000 Binary files a/app/images/icons/novnc-64x64.png and /dev/null differ diff --git a/app/images/icons/novnc-72x72.png b/app/images/icons/novnc-72x72.png deleted file mode 100644 index 23163a22d..000000000 Binary files a/app/images/icons/novnc-72x72.png and /dev/null differ diff --git a/app/images/icons/novnc-76x76.png b/app/images/icons/novnc-76x76.png deleted file mode 100644 index aef61c480..000000000 Binary files a/app/images/icons/novnc-76x76.png and /dev/null differ diff --git a/app/images/icons/novnc-96x96.png b/app/images/icons/novnc-96x96.png deleted file mode 100644 index 1a77c53f4..000000000 Binary files a/app/images/icons/novnc-96x96.png and /dev/null differ diff --git a/app/images/icons/novnc-ios-120.png b/app/images/icons/novnc-ios-120.png new file mode 100644 index 000000000..8da7bab3d Binary files /dev/null and b/app/images/icons/novnc-ios-120.png differ diff --git a/app/images/icons/novnc-ios-152.png b/app/images/icons/novnc-ios-152.png new file mode 100644 index 000000000..60b2bcef5 Binary files /dev/null and b/app/images/icons/novnc-ios-152.png differ diff --git a/app/images/icons/novnc-ios-167.png b/app/images/icons/novnc-ios-167.png new file mode 100644 index 000000000..98fade2e2 Binary files /dev/null and b/app/images/icons/novnc-ios-167.png differ diff --git a/app/images/icons/novnc-ios-180.png b/app/images/icons/novnc-ios-180.png new file mode 100644 index 000000000..5d24df70a Binary files /dev/null and b/app/images/icons/novnc-ios-180.png differ diff --git a/app/images/icons/novnc-ios-40.png b/app/images/icons/novnc-ios-40.png new file mode 100644 index 000000000..cf14894da Binary files /dev/null and b/app/images/icons/novnc-ios-40.png differ diff --git a/app/images/icons/novnc-ios-58.png b/app/images/icons/novnc-ios-58.png new file mode 100644 index 000000000..f6dfbebd2 Binary files /dev/null and b/app/images/icons/novnc-ios-58.png differ diff --git a/app/images/icons/novnc-ios-60.png b/app/images/icons/novnc-ios-60.png new file mode 100644 index 000000000..8cda29530 Binary files /dev/null and b/app/images/icons/novnc-ios-60.png differ diff --git a/app/images/icons/novnc-ios-80.png b/app/images/icons/novnc-ios-80.png new file mode 100644 index 000000000..6c417c47e Binary files /dev/null and b/app/images/icons/novnc-ios-80.png differ diff --git a/app/images/icons/novnc-ios-87.png b/app/images/icons/novnc-ios-87.png new file mode 100644 index 000000000..4377d874b Binary files /dev/null and b/app/images/icons/novnc-ios-87.png differ diff --git a/app/images/icons/novnc-ios-icon.svg b/app/images/icons/novnc-ios-icon.svg new file mode 100644 index 000000000..009452ac6 --- /dev/null +++ b/app/images/icons/novnc-ios-icon.svg @@ -0,0 +1,183 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/images/icons/novnc.ico b/app/images/icons/novnc.ico new file mode 100644 index 000000000..c3bc58e38 Binary files /dev/null and b/app/images/icons/novnc.ico differ diff --git a/app/locale/fr.json b/app/locale/fr.json index 19e8255b3..22531f73b 100644 --- a/app/locale/fr.json +++ b/app/locale/fr.json @@ -1,21 +1,22 @@ { + "HTTPS is required for full functionality": "", "Connecting...": "En cours de connexion...", "Disconnecting...": "Déconnexion en cours...", "Reconnecting...": "Reconnexion en cours...", "Internal error": "Erreur interne", "Must set host": "Doit définir l'hôte", - "Connected (encrypted) to ": "Connecté (crypté) à ", - "Connected (unencrypted) to ": "Connecté (non crypté) à ", - "Something went wrong, connection is closed": "Quelque chose est arrivé, la connexion est fermée", + "Connected (encrypted) to ": "Connecté (chiffré) à ", + "Connected (unencrypted) to ": "Connecté (non chiffré) à ", + "Something went wrong, connection is closed": "Quelque chose s'est mal passé, la connexion a été fermée", "Failed to connect to server": "Échec de connexion au serveur", "Disconnected": "Déconnecté", - "New connection has been rejected with reason: ": "Une nouvelle connexion a été rejetée avec raison: ", + "New connection has been rejected with reason: ": "Une nouvelle connexion a été rejetée avec motif : ", "New connection has been rejected": "Une nouvelle connexion a été rejetée", "Credentials are required": "Les identifiants sont requis", - "noVNC encountered an error:": "noVNC a rencontré une erreur:", + "noVNC encountered an error:": "noVNC a rencontré une erreur :", "Hide/Show the control bar": "Masquer/Afficher la barre de contrôle", "Drag": "Faire glisser", - "Move/Drag Viewport": "Déplacer/faire glisser Viewport", + "Move/Drag Viewport": "Déplacer/faire glisser le Viewport", "Keyboard": "Clavier", "Show Keyboard": "Afficher le clavier", "Extra keys": "Touches supplémentaires", @@ -39,34 +40,39 @@ "Reboot": "Redémarrer", "Reset": "Réinitialiser", "Clipboard": "Presse-papiers", - "Clear": "Effacer", - "Fullscreen": "Plein écran", + "Edit clipboard content in the textarea below.": "", "Settings": "Paramètres", "Shared Mode": "Mode partagé", "View Only": "Afficher uniquement", "Clip to Window": "Clip à fenêtre", - "Scaling Mode:": "Mode mise à l'échelle:", + "Scaling Mode:": "Mode mise à l'échelle :", "None": "Aucun", "Local Scaling": "Mise à l'échelle locale", "Remote Resizing": "Redimensionnement à distance", "Advanced": "Avancé", - "Quality:": "Qualité:", - "Compression level:": "Niveau de compression:", - "Repeater ID:": "ID Répéteur:", + "Quality:": "Qualité :", + "Compression level:": "Niveau de compression :", + "Repeater ID:": "ID Répéteur :", "WebSocket": "WebSocket", - "Encrypt": "Crypter", - "Host:": "Hôte:", - "Port:": "Port:", - "Path:": "Chemin:", + "Encrypt": "Chiffrer", + "Host:": "Hôte :", + "Port:": "Port :", + "Path:": "Chemin :", "Automatic Reconnect": "Reconnecter automatiquemen", - "Reconnect Delay (ms):": "Délai de reconnexion (ms):", + "Reconnect Delay (ms):": "Délai de reconnexion (ms) :", "Show Dot when No Cursor": "Afficher le point lorsqu'il n'y a pas de curseur", - "Logging:": "Se connecter:", - "Version:": "Version:", + "Logging:": "Se connecter :", + "Version:": "Version :", "Disconnect": "Déconnecter", "Connect": "Connecter", - "Username:": "Nom d'utilisateur:", - "Password:": "Mot de passe:", + "Server identity": "", + "The server has provided the following identifying information:": "", + "Fingerprint:": "", + "Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "", + "Approve": "", + "Reject": "", + "Username:": "Nom d'utilisateur :", + "Password:": "Mot de passe :", "Send Credentials": "Envoyer les identifiants", "Cancel": "Annuler" } \ No newline at end of file diff --git a/app/locale/sv.json b/app/locale/sv.json index e46df45b5..077ef42c8 100644 --- a/app/locale/sv.json +++ b/app/locale/sv.json @@ -1,4 +1,5 @@ { + "HTTPS is required for full functionality": "HTTPS krävs för full funktionalitet", "Connecting...": "Ansluter...", "Disconnecting...": "Kopplar ner...", "Reconnecting...": "Återansluter...", @@ -39,8 +40,8 @@ "Reboot": "Boota om", "Reset": "Återställ", "Clipboard": "Urklipp", - "Clear": "Rensa", - "Fullscreen": "Fullskärm", + "Edit clipboard content in the textarea below.": "Redigera urklippets innehåll i fältet nedan.", + "Full Screen": "Fullskärm", "Settings": "Inställningar", "Shared Mode": "Delat Läge", "View Only": "Endast Visning", @@ -65,6 +66,13 @@ "Version:": "Version:", "Disconnect": "Koppla från", "Connect": "Anslut", + "Server identity": "Server-identitet", + "The server has provided the following identifying information:": "Servern har gett följande identifierande information:", + "Fingerprint:": "Fingeravtryck:", + "Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "Kontrollera att informationen är korrekt och tryck sedan \"Godkänn\". Tryck annars \"Neka\".", + "Approve": "Godkänn", + "Reject": "Neka", + "Credentials": "Användaruppgifter", "Username:": "Användarnamn:", "Password:": "Lösenord:", "Send Credentials": "Skicka Användaruppgifter", diff --git a/app/localization.js b/app/localization.js index 100901c9d..7d7e6e6af 100644 --- a/app/localization.js +++ b/app/localization.js @@ -16,13 +16,19 @@ export class Localizer { this.language = 'en'; // Current dictionary of translations - this.dictionary = undefined; + this._dictionary = undefined; } // Configure suitable language based on user preferences - setup(supportedLanguages) { + async setup(supportedLanguages, baseURL) { this.language = 'en'; // Default: US English + this._dictionary = undefined; + this._setupLanguage(supportedLanguages); + await this._setupDictionary(baseURL); + } + + _setupLanguage(supportedLanguages) { /* * Navigator.languages only available in Chrome (32+) and FireFox (32+) * Fall back to navigator.language for other browsers @@ -40,12 +46,6 @@ export class Localizer { .replace("_", "-") .split("-"); - // Built-in default? - if ((userLang[0] === 'en') && - ((userLang[1] === undefined) || (userLang[1] === 'us'))) { - return; - } - // First pass: perfect match for (let j = 0; j < supportedLanguages.length; j++) { const supLang = supportedLanguages[j] @@ -64,7 +64,12 @@ export class Localizer { return; } - // Second pass: fallback + // Second pass: English fallback + if (userLang[0] === 'en') { + return; + } + + // Third pass pass: other fallback for (let j = 0;j < supportedLanguages.length;j++) { const supLang = supportedLanguages[j] .toLowerCase() @@ -84,10 +89,32 @@ export class Localizer { } } + async _setupDictionary(baseURL) { + if (baseURL) { + if (!baseURL.endsWith("/")) { + baseURL = baseURL + "/"; + } + } else { + baseURL = ""; + } + + if (this.language === "en") { + return; + } + + let response = await fetch(baseURL + this.language + ".json"); + if (!response.ok) { + throw Error("" + response.status + " " + response.statusText); + } + + this._dictionary = await response.json(); + } + // Retrieve localised text get(id) { - if (typeof this.dictionary !== 'undefined' && this.dictionary[id]) { - return this.dictionary[id]; + if (typeof this._dictionary !== 'undefined' && + this._dictionary[id]) { + return this._dictionary[id]; } else { return id; } @@ -103,13 +130,20 @@ export class Localizer { return items.indexOf(searchElement) !== -1; } + function translateString(str) { + // We assume surrounding whitespace, and whitespace around line + // breaks is just for source formatting + str = str.split("\n").map(s => s.trim()).join(" ").trim(); + return self.get(str); + } + function translateAttribute(elem, attr) { - const str = self.get(elem.getAttribute(attr)); + const str = translateString(elem.getAttribute(attr)); elem.setAttribute(attr, str); } function translateTextNode(node) { - const str = self.get(node.data.trim()); + const str = translateString(node.data); node.data = str; } diff --git a/app/styles/base.css b/app/styles/base.css index bf1fef953..f83ad4b93 100644 --- a/app/styles/base.css +++ b/app/styles/base.css @@ -22,8 +22,6 @@ /* * State variables (set on :root): * - * noVNC_touch: Device has touch input - * * noVNC_loading: Page is still loading * noVNC_connecting: Connecting to server * noVNC_reconnecting: Re-establishing a connection @@ -321,16 +319,20 @@ html { .noVNC_right #noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after { transform: none; } +/* Larger touch area for the handle, used when a touch screen is available */ #noVNC_control_bar_handle div { position: absolute; right: -35px; top: 0; width: 50px; height: 100%; -} -:root:not(.noVNC_touch) #noVNC_control_bar_handle div { display: none; } +@media (any-pointer: coarse) { + #noVNC_control_bar_handle div { + display: initial; + } +} .noVNC_right #noVNC_control_bar_handle div { left: -35px; right: auto; @@ -389,39 +391,41 @@ html { vertical-align: middle; border:1px solid rgba(255, 255, 255, 0.2); border-radius: 6px; + background-color: transparent; + background-image: unset; /* we don't want the gradiant from input.css */ } #noVNC_control_bar .noVNC_button.noVNC_selected { border-color: rgba(0, 0, 0, 0.8); - background: rgba(0, 0, 0, 0.5); + background-color: rgba(0, 0, 0, 0.5); } -#noVNC_control_bar .noVNC_button:disabled { - opacity: 0.4; - /* See firefox bug regarding cursor on disabled : - https://bugzilla.mozilla.org/show_bug.cgi?id=1798304 */ - cursor: default; +#noVNC_control_bar .noVNC_button.noVNC_selected:not(:disabled):hover { + border-color: rgba(0, 0, 0, 0.4); + background-color: rgba(0, 0, 0, 0.2); } -#noVNC_control_bar .noVNC_button:focus { - outline: none; +#noVNC_control_bar .noVNC_button:not(:disabled):hover { + background-color: rgba(255, 255, 255, 0.2); } #noVNC_control_bar .noVNC_button:not(:disabled):active { padding-top: 5px; padding-bottom: 3px; } -/* Android browsers don't properly update hover state if touch events - * are intercepted, but focus should be safe to display */ -:root:not(.noVNC_touch) #noVNC_control_bar .noVNC_button.noVNC_selected:not(:disabled):hover, -#noVNC_control_bar .noVNC_button.noVNC_selected:not(:disabled):focus { - border-color: rgba(0, 0, 0, 0.4); - background: rgba(0, 0, 0, 0.2); -} -:root:not(.noVNC_touch) #noVNC_control_bar .noVNC_button:not(:disabled):hover, -#noVNC_control_bar .noVNC_button:not(:disabled):focus { - background: rgba(255, 255, 255, 0.2); -} #noVNC_control_bar .noVNC_button.noVNC_hidden { display: none !important; } +/* Android browsers don't properly update hover state if touch events are + * intercepted, like they are when clicking on the remote screen. */ +@media (any-pointer: coarse) { + #noVNC_control_bar .noVNC_button:not(:disabled):hover { + background-color: transparent; + } + #noVNC_control_bar .noVNC_button.noVNC_selected:not(:disabled):hover { + border-color: rgba(0, 0, 0, 0.8); + background-color: rgba(0, 0, 0, 0.5); + } +} + + /* Panels */ .noVNC_panel { transform: translateX(25px); @@ -549,8 +553,15 @@ html { :root:not(.noVNC_connected) #noVNC_mobile_buttons { display: none; } -:root:not(.noVNC_touch) #noVNC_mobile_buttons { - display: none; +@media not all and (any-pointer: coarse) { + /* FIXME: The button for the virtual keyboard is the only button in this + group of "mobile buttons". It is bad to assume that no touch + devices have physical keyboards available. Hopefully we can get + a media query for this: + https://github.com/w3c/csswg-drafts/issues/3871 */ + :root.noVNC_connected #noVNC_mobile_buttons { + display: none; + } } /* Extra manual keys */ @@ -586,6 +597,7 @@ html { width: 360px; min-width: 150px; height: 160px; + min-height: 70px; box-sizing: border-box; max-width: 100%; @@ -649,7 +661,7 @@ html { justify-content: center; align-content: center; - line-height: 25px; + line-height: 1.6; word-wrap: break-word; color: #fff; @@ -723,36 +735,32 @@ html { font-size: calc(25vw - 30px); } } -#noVNC_connect_button { - cursor: pointer; - - padding: 10px; +#noVNC_connect_dlg div { + padding: 12px; - color: white; background-color: rgb(110, 132, 163); border-radius: 12px; - text-align: center; font-size: 20px; box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); } -#noVNC_connect_button div { - margin: 2px; +#noVNC_connect_button { + width: 100%; padding: 5px 30px; - border: 1px solid rgb(83, 99, 122); - border-bottom-width: 2px; + + cursor: pointer; + + border-color: rgb(83, 99, 122); border-radius: 5px; + background: linear-gradient(to top, rgb(110, 132, 163), rgb(99, 119, 147)); + color: white; /* This avoids it jumping around when :active */ vertical-align: middle; } -#noVNC_connect_button div:active { - border-bottom-width: 1px; - margin-top: 3px; -} -:root:not(.noVNC_touch) #noVNC_connect_button div:hover { +#noVNC_connect_button:hover { background: linear-gradient(to top, rgb(110, 132, 163), rgb(105, 125, 155)); } @@ -879,7 +887,7 @@ html { .noVNC_logo { color:yellow; font-family: 'Orbitron', 'OrbitronTTF', sans-serif; - line-height:90%; + line-height: 0.9; text-shadow: 0.1em 0.1em 0 black; } .noVNC_logo span{ diff --git a/app/styles/input.css b/app/styles/input.css index d6499a664..dc345aabc 100644 --- a/app/styles/input.css +++ b/app/styles/input.css @@ -5,25 +5,13 @@ * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). */ -input, select, textarea { +/* + * Common for all inputs + */ +input, input::file-selector-button, button, select, textarea { /* Respect standard font settings */ font: inherit; -} -input:not([type]), -input[type=date], -input[type=datetime-local], -input[type=email], -input[type=month], -input[type=number], -input[type=password], -input[type=search], -input[type=tel], -input[type=text], -input[type=time], -input[type=url], -input[type=week], -textarea { /* Disable default rendering */ appearance: none; background: none; @@ -32,125 +20,262 @@ textarea { border: 1px solid rgb(192, 192, 192); border-radius: 5px; color: black; - background: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240)); + --bg-gradient: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240)); + background-image: var(--bg-gradient); } +/* + * Buttons + */ input[type=button], input[type=color], +input[type=image], input[type=reset], input[type=submit], +input::file-selector-button, button, select { - /* Disable default rendering */ - appearance: none; - background: none; - - padding: 5px; - border: 1px solid rgb(192, 192, 192); border-bottom-width: 2px; - border-radius: 5px; - color: black; - background: linear-gradient(to top, rgb(255, 255, 255), rgb(240, 240, 240)); /* This avoids it jumping around when :active */ vertical-align: middle; -} + margin-top: 0; -input[type=button], -input[type=color], -input[type=reset], -input[type=submit], -button { padding-left: 20px; padding-right: 20px; + + /* Disable Chrome's touch tap highlight */ + -webkit-tap-highlight-color: transparent; } +/* + * Select dropdowns + */ select { - --select-arrow: calc(100% - 7px) no-repeat url('data:image/svg+xml;utf8, \ + --select-arrow: url('data:image/svg+xml;utf8, \ \ \ '); - background: var(--select-arrow), - linear-gradient(to top, rgb(255, 255, 255), rgb(240, 240, 240)); + background-image: var(--select-arrow), var(--bg-gradient); + background-position: calc(100% - 7px), left top; + background-repeat: no-repeat; padding-right: calc(2*7px + 8px); padding-left: 7px; } -:root:not(.noVNC_touch) select:hover:not(:disabled) { - background: var(--select-arrow), - linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250)); +/* FIXME: :active isn't set when the