Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions ui/localization/messages/cy.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"action_bar_copy_text": "Copïo testun",
"action_bar_extension": "Estyniad",
"action_bar_fullscreen": "Sgrin Lawn",
"action_bar_screenshot": "Ciplun",
"action_bar_settings": "Gosodiadau",
"action_bar_virtual_keyboard": "Bysellfwrdd Rhithwir",
"action_bar_virtual_media": "Cyfrwng Rhithwir",
Expand All @@ -74,6 +75,7 @@
"advanced_error_download_diagnostics": "Methwyd llwytho diagnosteg i lawr: {error}",
"advanced_error_loopback_disable": "Methwyd analluogi modd loopback yn unig: {error}",
"advanced_error_loopback_enable": "Methwyd galluogi modd loopback yn unig: {error}",
"advanced_error_reset_config": "Methwyd ailosod y ffurfweddiad: {error}",
"advanced_error_set_dev_channel": "Methwyd gosod cyflwr sianel ddatblygu: {error}",
"advanced_error_set_dev_mode": "Methwyd gosod modd datblygwr: {error}",
"advanced_error_set_log_level": "Methwyd gosod lefel log: {error}",
Expand Down Expand Up @@ -102,6 +104,9 @@
"advanced_loopback_warning_description": "RHYBUDD: Bydd hyn yn cyfyngu mynediad rhyngwyneb gwe i localhost (127.0.0.1) yn unig.",
"advanced_loopback_warning_ssh": "Mynediad SSH wedi'i ffurfweddu a'i brofi",
"advanced_loopback_warning_title": "Galluogi Modd Loopback yn Unig?",
"advanced_reset_config_button": "Ailosod Ffurfweddiad",
"advanced_reset_config_description": "Ailosod y ffurfweddiad i'r rhagosodiad. Bydd hyn yn eich allgofnodi.",
"advanced_reset_config_title": "Ailosod y Ffurfweddiad",
"advanced_ssh_access_description": "Ychwanegwch eich allwedd gyhoeddus SSH i alluogi mynediad diogel o bell i'r ddyfais",
"advanced_ssh_access_title": "Mynediad SSH",
"advanced_ssh_default_user": "Y defnyddiwr SSH rhagosodedig yw",
Expand All @@ -111,6 +116,7 @@
"advanced_success_download_diagnostics": "Diagnosteg wedi'i llwytho i lawr yn llwyddiannus",
"advanced_success_loopback_disabled": "Modd loopback yn unig wedi'i analluogi. Ailgychwynnwch eich dyfais i'w gymhwyso.",
"advanced_success_loopback_enabled": "Modd loopback yn unig wedi'i alluogi. Ailgychwynnwch eich dyfais i'w gymhwyso.",
"advanced_success_reset_config": "Ailosodiad y ffurfweddiad i'r rhagosodiad yn llwyddiannus",
"advanced_success_update_ssh_key": "Allwedd SSH wedi'i diweddaru'n llwyddiannus",
"advanced_title": "Uwch",
"advanced_troubleshooting_log_level_description": "Addaswch fanylder log ar gyfer diagnosteg. Bydd yn ailosod i Rhybudd ar ôl ailgychwyn",
Expand Down Expand Up @@ -779,11 +785,19 @@
"network_settings_load_error": "Methwyd â llwytho gosodiadau rhwydwaith: {error}",
"network_static_ipv4_header": "Ffurfweddiad IPv4 Statig",
"network_static_ipv6_header": "Ffurfweddiad IPv6 Statig",
"network_time_sync_add_http_url": "Ychwanegu URL HTTP",
"network_time_sync_add_ntp_server": "Ychwanegu Gweinydd NTP",
"network_time_sync_config_header": "Cydamseru Amser Personol",
"network_time_sync_custom": "Personol",
"network_time_sync_description": "Ffurfweddwch osodiadau cydamseru amser",
"network_time_sync_http_only": "HTTP yn unig",
"network_time_sync_http_url_invalid": "URL annilys. Rhaid iddo ddechrau gyda http:// neu https://",
"network_time_sync_ntp_and_http": "NTP a HTTP",
"network_time_sync_ntp_only": "NTP yn unig",
"network_time_sync_ntp_server_invalid": "Gweinydd NTP annilys. Rhowch enw gwesteiwr neu gyfeiriad IP",
"network_time_sync_title": "Cydamseru amser",
"network_time_sync_user_http_urls_label": "URLau HTTP",
"network_time_sync_user_ntp_servers_label": "Gweinyddion NTP",
"network_title": "Rhwydwaith",
"never_seen_online": "Byth wedi'i weld ar-lein",
"next": "Nesaf",
Expand Down
1 change: 1 addition & 0 deletions ui/localization/messages/da.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"action_bar_copy_text": "Kopiér tekst",
"action_bar_extension": "Udvidelse",
"action_bar_fullscreen": "Fuldskærm",
"action_bar_screenshot": "Skærmbillede",
"action_bar_settings": "Indstillinger",
"action_bar_virtual_keyboard": "Virtuelt tastatur",
"action_bar_virtual_media": "Virtuelle medier",
Expand Down
1 change: 1 addition & 0 deletions ui/localization/messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"action_bar_copy_text": "Text kopieren",
"action_bar_extension": "Erweiterung",
"action_bar_fullscreen": "Vollbild",
"action_bar_screenshot": "Screenshot",
"action_bar_settings": "Einstellungen",
"action_bar_virtual_keyboard": "Virtuelle Tastatur",
"action_bar_virtual_media": "Virtuelle Medien",
Expand Down
1 change: 1 addition & 0 deletions ui/localization/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"action_bar_copy_text": "Copy text",
"action_bar_extension": "Extension",
"action_bar_fullscreen": "Fullscreen",
"action_bar_screenshot": "Screenshot",
"action_bar_settings": "Settings",
"action_bar_virtual_keyboard": "Virtual Keyboard",
"action_bar_virtual_media": "Virtual Media",
Expand Down
1 change: 1 addition & 0 deletions ui/localization/messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"action_bar_copy_text": "Copiar texto",
"action_bar_extension": "Extensión",
"action_bar_fullscreen": "Pantalla completa",
"action_bar_screenshot": "Captura de pantalla",
"action_bar_settings": "Ajustes",
"action_bar_virtual_keyboard": "Teclado virtual",
"action_bar_virtual_media": "Medios virtuales",
Expand Down
1 change: 1 addition & 0 deletions ui/localization/messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"action_bar_copy_text": "Copier le texte",
"action_bar_extension": "Extension",
"action_bar_fullscreen": "Plein écran",
"action_bar_screenshot": "Capture d'écran",
"action_bar_settings": "Paramètres",
"action_bar_virtual_keyboard": "Clavier virtuel",
"action_bar_virtual_media": "Médias virtuels",
Expand Down
1 change: 1 addition & 0 deletions ui/localization/messages/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"action_bar_copy_text": "Copia il testo",
"action_bar_extension": "Estensione",
"action_bar_fullscreen": "A schermo intero",
"action_bar_screenshot": "Screenshot",
"action_bar_settings": "Impostazioni",
"action_bar_virtual_keyboard": "Tastiera virtuale",
"action_bar_virtual_media": "Media virtuali",
Expand Down
1 change: 1 addition & 0 deletions ui/localization/messages/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"action_bar_copy_text": "テキストをコピー",
"action_bar_extension": "拡張機能",
"action_bar_fullscreen": "全画面",
"action_bar_screenshot": "スクリーンショット",
"action_bar_settings": "設定",
"action_bar_virtual_keyboard": "仮想キーボード",
"action_bar_virtual_media": "仮想メディア",
Expand Down
1 change: 1 addition & 0 deletions ui/localization/messages/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"action_bar_copy_text": "Kopier tekst",
"action_bar_extension": "Forlengelse",
"action_bar_fullscreen": "Fullskjerm",
"action_bar_screenshot": "Skjermbilde",
"action_bar_settings": "Innstillinger",
"action_bar_virtual_keyboard": "Virtuelt tastatur",
"action_bar_virtual_media": "Virtuelle medier",
Expand Down
1 change: 1 addition & 0 deletions ui/localization/messages/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"action_bar_copy_text": "Copiar texto",
"action_bar_extension": "Extensão",
"action_bar_fullscreen": "Tela Cheia",
"action_bar_screenshot": "Captura de tela",
"action_bar_settings": "Configurações",
"action_bar_virtual_keyboard": "Teclado Virtual",
"action_bar_virtual_media": "Mídia Virtual",
Expand Down
1 change: 1 addition & 0 deletions ui/localization/messages/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"action_bar_copy_text": "Копировать текст",
"action_bar_extension": "Расширение",
"action_bar_fullscreen": "Полный экран",
"action_bar_screenshot": "Скриншот",
"action_bar_settings": "Настройки",
"action_bar_virtual_keyboard": "Виртуальная клавиатура",
"action_bar_virtual_media": "Виртуальный носитель",
Expand Down
1 change: 1 addition & 0 deletions ui/localization/messages/sv.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"action_bar_copy_text": "Kopiera text",
"action_bar_extension": "Förlängning",
"action_bar_fullscreen": "Helskärm",
"action_bar_screenshot": "Skärmdump",
"action_bar_settings": "Inställningar",
"action_bar_virtual_keyboard": "Virtuellt tangentbord",
"action_bar_virtual_media": "Virtuella medier",
Expand Down
1 change: 1 addition & 0 deletions ui/localization/messages/zh-tw.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"action_bar_copy_text": "複製文字",
"action_bar_extension": "擴充功能",
"action_bar_fullscreen": "全螢幕",
"action_bar_screenshot": "螢幕截圖",
"action_bar_settings": "設定",
"action_bar_virtual_keyboard": "虛擬鍵盤",
"action_bar_virtual_media": "虛擬媒體",
Expand Down
1 change: 1 addition & 0 deletions ui/localization/messages/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"action_bar_copy_text": "复制文本",
"action_bar_extension": "扩展功能",
"action_bar_fullscreen": "全屏",
"action_bar_screenshot": "截屏",
"action_bar_settings": "设置",
"action_bar_virtual_keyboard": "虚拟键盘",
"action_bar_virtual_media": "虚拟介质",
Expand Down
10 changes: 10 additions & 0 deletions ui/src/components/ActionBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Fragment, useCallback, useEffect, useRef } from "react";
import { MdOutlineContentPasteGo } from "react-icons/md";
import {
LuCable,
LuCamera,
LuExternalLink,
LuHardDrive,
LuMaximize,
Expand Down Expand Up @@ -37,8 +38,10 @@ import { m } from "@localizations/messages.js";

export default function Actionbar({
requestFullscreen,
takeScreenshot,
}: {
requestFullscreen: () => Promise<void>;
takeScreenshot: () => void;
}) {
const { navigateTo } = useDeviceUiNavigation();
const { isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } = useHidStore();
Expand Down Expand Up @@ -338,6 +341,13 @@ export default function Actionbar({

<div className="hidden items-center gap-x-2 lg:flex">
<div className="h-4 w-px bg-slate-300 dark:bg-slate-600" />
<Button
size="XS"
theme="light"
text={m.action_bar_screenshot()}
LeadingIcon={LuCamera}
onClick={() => takeScreenshot()}
/>
{isEmbedMode ? (
<Button
size="XS"
Expand Down
63 changes: 39 additions & 24 deletions ui/src/components/WebRTCVideo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,31 @@ export default function WebRTCVideo({
});
}, [isFullscreenEnabled, requestKeyboardLock, requestPointerLock]);

const takeScreenshot = useCallback(() => {
const video = videoElm.current;
if (!video || video.videoWidth === 0 || video.videoHeight === 0) return;

const canvas = document.createElement("canvas");
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext("2d");
if (!ctx) return;
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

const now = new Date();
const pad = (n: number) => String(n).padStart(2, "0");
const stamp =
`${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}` +
` at ${pad(now.getHours())}.${pad(now.getMinutes())}.${pad(now.getSeconds())}`;

const a = document.createElement("a");
a.download = `JetKVM ${canvas.width}x${canvas.height} ${stamp}.png`;
a.href = canvas.toDataURL("image/png");
document.body.appendChild(a);
a.click();
a.remove();
}, []);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot ignores user video display filter settings

Medium Severity

The takeScreenshot callback draws the raw video frame via ctx.drawImage but does not apply the user's videoSaturation, videoBrightness, or videoContrast CSS filters that are actively applied to the <video> element through videoStyle. When a user has adjusted these display settings, the downloaded screenshot will look different from what they see on screen. The canvas 2D context's filter property can replicate the same CSS filter string before drawing. The useCallback dependency array [] would also need to include those settings values.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f4b54f5. Configure here.


// setup to release the keyboard lock anytime the fullscreen ends
useEffect(() => {
if (!videoElm.current) return;
Expand Down Expand Up @@ -606,13 +631,8 @@ export default function WebRTCVideo({
<div className="grid h-full w-full grid-rows-(--grid-layout)">
<div className="flex min-h-[39.5px] flex-col">
<div className="flex flex-col">
<fieldset
disabled={peerConnection?.connectionState !== "connected"}
className="contents"
>
<Actionbar
requestFullscreen={requestFullscreen}
/>
<fieldset disabled={peerConnection?.connectionState !== "connected"} className="contents">
<Actionbar requestFullscreen={requestFullscreen} takeScreenshot={takeScreenshot} />
<MacroBar />
</fieldset>
</div>
Expand All @@ -634,9 +654,7 @@ export default function WebRTCVideo({
<div className="grid grow grid-rows-(--grid-bodyFooter) overflow-hidden">
{/* In relative mouse mode and under https, we enable the pointer lock, and to do so we need a bar to show the user to click on the video to enable mouse control */}
<PointerLockBar show={showPointerLockBar} />
<div
className="relative mx-4 my-2 flex items-center justify-center overflow-hidden"
>
<div className="relative mx-4 my-2 flex items-center justify-center overflow-hidden">
<div
ref={fullscreenContainerRef}
className="relative flex h-full w-full items-center justify-center"
Expand All @@ -652,20 +670,17 @@ export default function WebRTCVideo({
disablePictureInPicture
controlsList="nofullscreen"
style={videoStyle}
className={cx(
"h-full w-full object-contain transition-all duration-1000",
{
"cursor-none": settings.isCursorHidden,
"pointer-events-none": isOcrMode,
"opacity-0!":
isVideoLoading ||
hdmiError ||
hasConnectionIssues ||
peerConnectionState !== "connected",
"opacity-60!": showPointerLockBar,
"animate-slideUpFade": isPlaying,
},
)}
className={cx("h-full w-full object-contain transition-all duration-1000", {
"cursor-none": settings.isCursorHidden,
"pointer-events-none": isOcrMode,
"opacity-0!":
isVideoLoading ||
hdmiError ||
hasConnectionIssues ||
peerConnectionState !== "connected",
"opacity-60!": showPointerLockBar,
"animate-slideUpFade": isPlaying,
})}
/>
<OcrOverlay />
{peerConnection?.connectionState == "connected" && !hasConnectionIssues && (
Expand Down
Loading