diff --git a/.eslintrc b/.eslintrc index a53bb402b..a34a5136a 100644 --- a/.eslintrc +++ b/.eslintrc @@ -4,6 +4,7 @@ "es6": true }, "parserOptions": { + "ecmaVersion": 2020, "sourceType": "module" }, "extends": "eslint:recommended", diff --git a/app/control-panel-assets.js b/app/control-panel-assets.js new file mode 100644 index 000000000..780e3b361 --- /dev/null +++ b/app/control-panel-assets.js @@ -0,0 +1,82 @@ +/* + * KasmVNC: HTML5 VNC client + * Copyright (C) 2026 Kasm Technologies + * Licensed under MPL 2.0 (see LICENSE.txt) + */ + +import alt from './images/alt.svg'; +import clipboard from './images/clipboard.svg'; +import connect from './images/connect.svg'; +import ctrl from './images/ctrl.svg'; +import ctrlAltDel from './images/ctrlaltdel.svg'; +import desktop from './images/desktop-regular.svg'; +import disconnect from './images/disconnect.svg'; +import drag from './images/drag.svg'; +import esc from './images/esc.svg'; +import expander from './images/expander.svg'; +import fullscreen from './images/fullscreen.svg'; +import gamepad from './images/gamepad.png'; +import handle from './images/handle.svg'; +import handleBackground from './images/handle_bg.svg'; +import kasmLogo from './images/icons/kasm_logo.svg'; +import keyboard from './images/keyboard.svg'; +import power from './images/power.svg'; +import settings from './images/settings.svg'; +import tab from './images/tab.svg'; +import toggleExtraKeys from './images/toggleextrakeys.svg'; +import windows from './images/windows.svg'; +import packageInfo from '../package.json'; + +const controlPanelAssets = { + alt, + clipboard, + connect, + ctrl, + ctrlAltDel, + desktop, + disconnect, + drag, + esc, + fullscreen, + gamepad, + kasmLogo, + keyboard, + power, + settings, + tab, + toggleExtraKeys, + windows, +}; + +function cssUrl(asset) { + return `url(${JSON.stringify(asset)})`; +} + +export function applyControlPanelAssets() { + document.querySelectorAll('[data-control-panel-src]').forEach((element) => { + const asset = controlPanelAssets[element.dataset.controlPanelSrc]; + if (asset) { + element.src = asset; + } + }); + + const style = document.documentElement.style; + style.setProperty('--noVNC-control-bar-handle-background', cssUrl(handleBackground)); + style.setProperty('--noVNC-control-bar-handle', cssUrl(handle)); + style.setProperty('--noVNC-control-bar-expander', cssUrl(expander)); + style.setProperty('--noVNC-control-panel-font-family', "'Orbitron', 'OrbitronTTF', sans-serif"); + + document.querySelectorAll('.noVNC_version') + .forEach(element => element.innerText = packageInfo.version); +} + +export function applyKeyboardControlAssets() { + const style = document.documentElement.style; + style.setProperty('--noVNC-keyboard-control-ctrl', cssUrl(ctrl)); + style.setProperty('--noVNC-keyboard-control-alt', cssUrl(alt)); + style.setProperty('--noVNC-keyboard-control-windows', cssUrl(windows)); + style.setProperty('--noVNC-keyboard-control-tab', cssUrl(tab)); + style.setProperty('--noVNC-keyboard-control-escape', cssUrl(esc)); + style.setProperty('--noVNC-keyboard-control-ctrl-alt-del', cssUrl(ctrlAltDel)); + style.setProperty('--noVNC-keyboard-control-keyboard', cssUrl(keyboard)); +} diff --git a/app/styles/base.css b/app/styles/base.css index 65e10324d..191bed9c1 100644 --- a/app/styles/base.css +++ b/app/styles/base.css @@ -437,14 +437,14 @@ select:active { z-index: -1; cursor: pointer; border-radius: 5px; - background-image: url("../images/handle_bg.svg"); + background-image: var(--noVNC-control-bar-handle-background, none); background-repeat: no-repeat; background-position: right; } #noVNC_control_bar_handle:after { content: ""; transition: transform 0.5s ease-in-out; - background: url("../images/handle.svg"); + background-image: var(--noVNC-control-bar-handle, none); background-repeat: no-repeat; background-position: center; position: absolute; @@ -646,7 +646,7 @@ select:active { } .noVNC_expander::before { - content: url("../images/expander.svg"); + content: var(--noVNC-control-bar-expander, ""); display: inline-block; margin-right: 5px; transition: 0.2s ease-in-out; @@ -937,7 +937,7 @@ select:active { .noVNC_logo { color:yellow; - font-family: 'Orbitron', 'OrbitronTTF', sans-serif; + font-family: var(--noVNC-control-panel-font-family, sans-serif); line-height:90%; text-shadow: 0.1em 0.1em 0 black; margin-bottom: 0px; @@ -1012,38 +1012,38 @@ body { } .keyboard-controls .button.ctrl { - background-image: url("../images/ctrl.svg"); + background-image: var(--noVNC-keyboard-control-ctrl, none); background-size: contain; } .keyboard-controls .button.alt { - background-image: url("../images/alt.svg"); + background-image: var(--noVNC-keyboard-control-alt, none); background-size: contain; } .keyboard-controls .button.windows { background-size: contain; - background-image: url("../images/windows.svg"); + background-image: var(--noVNC-keyboard-control-windows, none); } .keyboard-controls .button.tab { background-size: contain; - background-image: url("../images/tab.svg"); + background-image: var(--noVNC-keyboard-control-tab, none); } .keyboard-controls .button.escape { background-size: contain; - background-image: url("../images/esc.svg"); + background-image: var(--noVNC-keyboard-control-escape, none); } .keyboard-controls .button.ctrlaltdel { background-size: contain; - background-image: url("../images/ctrlaltdel.svg"); + background-image: var(--noVNC-keyboard-control-ctrl-alt-del, none); } .keyboard-controls .button.keyboard { background-size: contain; - background-image: url("../images/keyboard.svg"); + background-image: var(--noVNC-keyboard-control-keyboard, none); } .keyboard-controls .button.selected { diff --git a/app/ui.js b/app/ui.js index 8e1326f37..e2fa65e1f 100644 --- a/app/ui.js +++ b/app/ui.js @@ -103,6 +103,24 @@ const UI = { }, codecDetector: null, forcedCodecs: [], + controlPanelAssetsModule: null, + + getControlPanelAssetsModule() { + if (!UI.controlPanelAssetsModule) { + UI.controlPanelAssetsModule = import('./control-panel-assets.js'); + } + return UI.controlPanelAssetsModule; + }, + + loadControlPanelAssets() { + return UI.getControlPanelAssetsModule() + .then(module => module.applyControlPanelAssets()); + }, + + loadKeyboardControlAssets() { + return UI.getControlPanelAssetsModule() + .then(module => module.applyKeyboardControlAssets()); + }, prime: async () => { await WebUtil.initSettings(); @@ -132,6 +150,13 @@ const UI = { return; } + if (!WebUtil.isInsideKasmVDI() || WebUtil.getConfigVar('show_control_bar')) { + UI.loadControlPanelAssets() + .catch(err => Log.Error(`Couldn't load control panel assets: ${err}`)); + } else { + document.getElementById('noVNC_control_bar_anchor').style.display = 'none'; + } + // Initialize settings then apply quality presents UI.initSettings(); UI.updateQuality(); @@ -139,23 +164,6 @@ const UI = { // Translate the DOM l10n.translateDOM(); - fetch('./package.json') - .then((response) => { - if (!response.ok) { - throw Error("" + response.status + " " + response.statusText); - } - return response.json(); - }) - .then((packageInfo) => { - Array.from(document.getElementsByClassName('noVNC_version')).forEach(el => el.innerText = packageInfo.version); - }) - .catch((err) => { - Log.Error("Couldn't fetch package.json: " + err); - Array.from(document.getElementsByClassName('noVNC_version_wrapper')) - .concat(Array.from(document.getElementsByClassName('noVNC_version_separator'))) - .forEach(el => el.style.display = 'none'); - }); - // Adapt the interface for touch screen devices if (isTouchDevice) { document.documentElement.classList.add("noVNC_touch"); @@ -3279,6 +3287,8 @@ const UI = { }, showKeyboardControls() { + UI.loadKeyboardControlAssets() + .catch(err => Log.Error(`Couldn't load keyboard control assets: ${err}`)); document.getElementById('noVNC_keyboard_control').classList.add("is-visible"); }, diff --git a/index.html b/index.html index ed72baccf..8c2317003 100644 --- a/index.html +++ b/index.html @@ -98,13 +98,13 @@

- KasmVNC Learn More + KasmVNC Learn More

- Drag Viewport @@ -113,29 +113,29 @@

-
- - - - - - -
@@ -145,14 +145,14 @@

-
- Power + Power
@@ -164,14 +164,14 @@

- Clipboard
- Clipboard + Clipboard

@@ -183,7 +183,7 @@

- Fullscreen @@ -191,7 +191,7 @@

- @@ -200,7 +200,7 @@

- Game Cursor Mode @@ -208,14 +208,14 @@

-
  • - Settings + Settings