Skip to content

Commit 75fb49a

Browse files
committed
add video scale (browser)
1 parent a73c3dc commit 75fb49a

File tree

12 files changed

+102
-8
lines changed

12 files changed

+102
-8
lines changed

browser/src/App.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ import { Keyboard } from '@/components/keyboard';
1010
import { Menu } from '@/components/menu';
1111
import { Mouse } from '@/components/mouse';
1212
import { VirtualKeyboard } from '@/components/virtual-keyboard';
13-
import { resolutionAtom, serialStateAtom, videoStateAtom } from '@/jotai/device.ts';
13+
import {
14+
resolutionAtom,
15+
serialStateAtom,
16+
videoScaleAtom,
17+
videoStateAtom
18+
} from '@/jotai/device.ts';
1419
import { isKeyboardEnableAtom } from '@/jotai/keyboard.ts';
1520
import { mouseStyleAtom } from '@/jotai/mouse.ts';
1621
import { camera } from '@/libs/camera';
@@ -23,6 +28,7 @@ const App = () => {
2328
const isBigScreen = useMediaQuery({ minWidth: 850 });
2429

2530
const mouseStyle = useAtomValue(mouseStyleAtom);
31+
const videoScale = useAtomValue(videoScaleAtom);
2632
const videoState = useAtomValue(videoStateAtom);
2733
const serialState = useAtomValue(serialStateAtom);
2834
const isKeyboardEnable = useAtomValue(isKeyboardEnableAtom);
@@ -107,7 +113,13 @@ const App = () => {
107113
<video
108114
id="video"
109115
className={clsx('block min-h-[480px] min-w-[640px] select-none', mouseStyle)}
110-
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'scale-down' }}
116+
style={{
117+
transform: `scale(${videoScale})`,
118+
transformOrigin: 'center',
119+
maxWidth: '100%',
120+
maxHeight: '100%',
121+
objectFit: 'scale-down'
122+
}}
111123
autoPlay
112124
playsInline
113125
/>

browser/src/components/menu/video/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import { MonitorIcon } from 'lucide-react';
33

44
import { Device } from './device.tsx';
55
import { Resolution } from './resolution.tsx';
6+
import { Scale } from './scale.tsx';
67

78
export const Video = () => {
89
const content = (
910
<div className="flex flex-col space-y-1">
1011
<Resolution />
12+
<Scale />
1113
<Device />
1214
</div>
1315
);
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { ReactElement, useEffect } from 'react'
2+
import { Popover, Slider } from 'antd'
3+
import { useAtom } from 'jotai'
4+
import { ScalingIcon } from 'lucide-react'
5+
import { useTranslation } from 'react-i18next'
6+
7+
import { videoScaleAtom } from '@/jotai/device.ts';
8+
import * as storage from '@/libs/storage';
9+
10+
export const Scale = (): ReactElement => {
11+
const { t } = useTranslation()
12+
13+
const [videoScale, setVideoScale] = useAtom(videoScaleAtom)
14+
15+
useEffect(() => {
16+
const scale = storage.getVideoScale()
17+
if (scale) {
18+
setVideoScale(scale)
19+
}
20+
}, [])
21+
22+
async function updateScale(scale: number): Promise<void> {
23+
setVideoScale(scale)
24+
storage.setVideoScale(scale)
25+
}
26+
27+
const content = (
28+
<div className="h-[150px] w-[60px] py-3">
29+
<Slider
30+
vertical
31+
marks={{
32+
0.5: <span>x0.5</span>,
33+
1: <span>x1.0</span>,
34+
1.5: <span>x1.5</span>,
35+
2: <span>x2.0</span>
36+
}}
37+
range={false}
38+
included={false}
39+
min={0.5}
40+
max={2}
41+
step={0.1}
42+
defaultValue={videoScale}
43+
onChange={updateScale}
44+
/>
45+
</div>
46+
)
47+
48+
return (
49+
<Popover content={content} placement="rightTop" arrow={false} align={{ offset: [13, 0] }}>
50+
<div className="flex h-[30px] cursor-pointer items-center space-x-1 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
51+
<div className="flex h-[14px] w-[20px] items-end">
52+
<ScalingIcon size={16} />
53+
</div>
54+
<span>{t('video.scale')}</span>
55+
</div>
56+
</Popover>
57+
)
58+
}

browser/src/i18n/locales/be.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const be = {
2424
},
2525
video: {
2626
resolution: 'Resolutie',
27+
scale: 'Schaal',
2728
customResolution: 'Aangepast',
2829
device: 'Toestel',
2930
custom: {
@@ -62,5 +63,5 @@ const be = {
6263
}
6364
}
6465
};
65-
66-
export default be;
66+
67+
export default be;

browser/src/i18n/locales/de.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const de = {
2424
},
2525
video: {
2626
resolution: 'Auflösung',
27+
scale: 'Skalierung',
2728
customResolution: 'Benutzerdefiniert',
2829
device: 'Gerät',
2930
custom: {
@@ -62,5 +63,5 @@ const de = {
6263
}
6364
}
6465
};
65-
66-
export default de;
66+
67+
export default de;

browser/src/i18n/locales/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const en = {
2424
},
2525
video: {
2626
resolution: 'Resolution',
27+
scale: 'Scale',
2728
customResolution: 'Custom',
2829
device: 'Device',
2930
custom: {

browser/src/i18n/locales/kr.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const ko = {
2424
},
2525
video: {
2626
resolution: '해상도',
27+
scale: '배율',
2728
customResolution: '사용자 정의',
2829
device: '장치',
2930
custom: {

browser/src/i18n/locales/nl.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const nl = {
2424
},
2525
video: {
2626
resolution: 'Resolutie',
27+
scale: 'Schaal',
2728
customResolution: 'Aangepast',
2829
device: 'Apparaat',
2930
custom: {
@@ -62,5 +63,5 @@ const nl = {
6263
}
6364
}
6465
};
65-
66-
export default nl;
66+
67+
export default nl;

browser/src/i18n/locales/ru.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const ru = {
2424
},
2525
video: {
2626
resolution: 'Разрешение',
27+
scale: 'Масштаб',
2728
customResolution: 'Пользовательское',
2829
device: 'Видеоустройство',
2930
custom: {

browser/src/i18n/locales/zh.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const zh = {
2222
},
2323
video: {
2424
resolution: '分辨率',
25+
scale: '缩放',
2526
customResolution: '自定义',
2627
device: '设备',
2728
custom: {

browser/src/jotai/device.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export const resolutionAtom = atom<Resolution>({
1010
height: 1080
1111
});
1212

13+
export const videoScaleAtom = atom<number>(1.0)
14+
1315
export const videoDeviceIdAtom = atom('');
1416
export const videoStateAtom = atom<VideoState>('disconnected');
1517

browser/src/libs/storage/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const LANGUAGE_KEY = 'nanokvm-usb-language';
44
const VIDEO_DEVICE_ID_KEY = 'nanokvm-usb-video-device-id';
55
const VIDEO_RESOLUTION_KEY = 'nanokvm-usb-video-resolution';
66
const CUSTOM_RESOLUTION_KEY = 'nanokvm-usb-custom-resolution';
7+
const VIDEO_SCALE_KEY = 'nanokvm-usb-video-scale'
78
const IS_MENU_OPEN_KEY = 'nanokvm-is-menu-open';
89
const MOUSE_STYLE_KEY = 'nanokvm-usb-mouse-style';
910
const MOUSE_MODE_KEY = 'nanokvm-usb-mouse-mode';
@@ -56,6 +57,18 @@ export function removeCustomResolutions() {
5657
localStorage.removeItem(CUSTOM_RESOLUTION_KEY);
5758
}
5859

60+
export function getVideoScale(): number | null {
61+
const scale = localStorage.getItem(VIDEO_SCALE_KEY)
62+
if (scale && Number(scale)) {
63+
return Number(scale)
64+
}
65+
return null
66+
}
67+
68+
export function setVideoScale(scale: number): void {
69+
localStorage.setItem(VIDEO_SCALE_KEY, String(scale))
70+
}
71+
5972
export function getIsMenuOpen(): boolean {
6073
const state = localStorage.getItem(IS_MENU_OPEN_KEY);
6174
if (!state) {

0 commit comments

Comments
 (0)