diff --git a/browser/src/components/menu/audio/index.tsx b/browser/src/components/menu/audio/index.tsx index 93681c8b..dc450301 100644 --- a/browser/src/components/menu/audio/index.tsx +++ b/browser/src/components/menu/audio/index.tsx @@ -8,6 +8,8 @@ import { videoDeviceIdAtom, videoStateAtom } from '@/jotai/device.ts'; import { camera } from '@/libs/media/camera.ts'; import { checkPermission, requestMicrophonePermission } from '@/libs/media/permission.ts'; +import { MenuTooltip } from '../menu-tooltip'; + export const Audio = () => { const { t } = useTranslation(); @@ -51,12 +53,14 @@ export const Audio = () => { return ( <> -
- -
+ +
+ +
+
{t('audio.permission')}
diff --git a/browser/src/components/menu/fullscreen/index.tsx b/browser/src/components/menu/fullscreen/index.tsx index 467dfe63..00d64da1 100644 --- a/browser/src/components/menu/fullscreen/index.tsx +++ b/browser/src/components/menu/fullscreen/index.tsx @@ -1,7 +1,11 @@ import { useEffect, useState } from 'react'; import { MaximizeIcon, MinimizeIcon } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; + +import { MenuTooltip } from '../menu-tooltip'; export const Fullscreen = () => { + const { t } = useTranslation(); const [isFullscreen, setIsFullscreen] = useState(false); useEffect(() => { @@ -34,11 +38,13 @@ export const Fullscreen = () => { } return ( -
- {isFullscreen ? : } -
+ +
+ {isFullscreen ? : } +
+
); }; diff --git a/browser/src/components/menu/keyboard/index.tsx b/browser/src/components/menu/keyboard/index.tsx index 7a1d4e73..80db2d96 100644 --- a/browser/src/components/menu/keyboard/index.tsx +++ b/browser/src/components/menu/keyboard/index.tsx @@ -1,12 +1,16 @@ import { useState } from 'react'; import { Popover } from 'antd'; import { KeyboardIcon } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; + +import { MenuTooltip } from '../menu-tooltip'; import { Paste } from './paste.tsx'; import { Shortcuts } from './shortcuts'; import { VirtualKeyboard } from './virtual-keyboard.tsx'; export const Keyboard = () => { + const { t } = useTranslation(); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const content = ( @@ -18,17 +22,19 @@ export const Keyboard = () => { ); return ( - -
- -
-
+ + +
+ +
+
+
); }; diff --git a/browser/src/components/menu/menu-tooltip.tsx b/browser/src/components/menu/menu-tooltip.tsx new file mode 100644 index 00000000..6e79f607 --- /dev/null +++ b/browser/src/components/menu/menu-tooltip.tsx @@ -0,0 +1,24 @@ +import { ReactNode } from 'react'; +import { Tooltip } from 'antd'; +import { useAtomValue } from 'jotai'; + +import { showTooltipsAtom } from './settings/tooltips'; + +interface MenuTooltipProps { + title: string; + children: ReactNode; +} + +export const MenuTooltip = ({ title, children }: MenuTooltipProps) => { + const showTooltips = useAtomValue(showTooltipsAtom); + + if (!showTooltips) { + return <>{children}; + } + + return ( + + {children} + + ); +}; diff --git a/browser/src/components/menu/mouse/index.tsx b/browser/src/components/menu/mouse/index.tsx index 4f1dd545..06af6f68 100644 --- a/browser/src/components/menu/mouse/index.tsx +++ b/browser/src/components/menu/mouse/index.tsx @@ -2,6 +2,9 @@ import { useEffect, useState } from 'react'; import { Divider, Popover } from 'antd'; import { useAtom, useSetAtom } from 'jotai'; import { MouseIcon } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; + +import { MenuTooltip } from '../menu-tooltip'; import { mouseJigglerModeAtom, @@ -20,6 +23,7 @@ import { Speed } from './speed.tsx'; import { Style } from './style.tsx'; export const Mouse = () => { + const { t } = useTranslation(); const [mouseStyle, setMouseStyle] = useAtom(mouseStyleAtom); const [mouseMode, setMouseMode] = useAtom(mouseModeAtom); const setScrollDirection = useSetAtom(scrollDirectionAtom); @@ -71,17 +75,19 @@ export const Mouse = () => { ); return ( - -
- -
-
+ + +
+ +
+
+
); }; diff --git a/browser/src/components/menu/recorder/index.tsx b/browser/src/components/menu/recorder/index.tsx index ede78fdc..57b94226 100644 --- a/browser/src/components/menu/recorder/index.tsx +++ b/browser/src/components/menu/recorder/index.tsx @@ -1,9 +1,12 @@ import { useEffect, useRef, useState } from 'react'; import { Video } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; +import { MenuTooltip } from '../menu-tooltip'; import { camera } from '@/libs/media/camera'; export const Recorder = () => { + const { t } = useTranslation(); const [isRecording, setIsRecording] = useState(false); const [elapsedMs, setElapsedMs] = useState(0); @@ -116,11 +119,13 @@ export const Recorder = () => { } return ( -
-
+ +
+
+
); }; diff --git a/browser/src/components/menu/serial-port/index.tsx b/browser/src/components/menu/serial-port/index.tsx index 425e688b..cc93ce15 100644 --- a/browser/src/components/menu/serial-port/index.tsx +++ b/browser/src/components/menu/serial-port/index.tsx @@ -1,9 +1,12 @@ import { useState } from 'react'; import { CpuIcon, Loader2Icon } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; +import { MenuTooltip } from '../menu-tooltip'; import { device } from '@/libs/device'; export const SerialPort = () => { + const { t } = useTranslation(); const [isLoading, setIsLoading] = useState(false); async function selectSerial() { @@ -19,10 +22,12 @@ export const SerialPort = () => { } return ( -
-
- {isLoading ? : } + +
+
+ {isLoading ? : } +
-
+ ); }; diff --git a/browser/src/components/menu/settings/index.tsx b/browser/src/components/menu/settings/index.tsx index 4de22a2e..200b8ebd 100644 --- a/browser/src/components/menu/settings/index.tsx +++ b/browser/src/components/menu/settings/index.tsx @@ -1,8 +1,10 @@ -import { Popover } from 'antd'; +import { Divider, Popover } from 'antd'; import { BookIcon, DownloadIcon, SettingsIcon } from 'lucide-react'; import { useTranslation } from 'react-i18next'; +import { MenuTooltip } from '../menu-tooltip'; import { Language } from './language.tsx'; +import { TooltipsSetting } from './tooltips'; export const Settings = () => { const { t } = useTranslation(); @@ -14,6 +16,9 @@ export const Settings = () => { const content = (
+ + +
{ ); return ( - -
- -
-
+ + +
+ +
+
+
); }; diff --git a/browser/src/components/menu/settings/tooltips.tsx b/browser/src/components/menu/settings/tooltips.tsx new file mode 100644 index 00000000..68bfec9a --- /dev/null +++ b/browser/src/components/menu/settings/tooltips.tsx @@ -0,0 +1,31 @@ +import { Checkbox } from 'antd'; +import { useAtom } from 'jotai'; +import { atom } from 'jotai'; +import { MessageSquareIcon } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; + +import * as storage from '@/libs/storage'; + +// Atom for tooltip visibility +export const showTooltipsAtom = atom(storage.getShowTooltips()); + +export const TooltipsSetting = () => { + const { t } = useTranslation(); + const [showTooltips, setShowTooltips] = useAtom(showTooltipsAtom); + + function handleChange(checked: boolean) { + setShowTooltips(checked); + storage.setShowTooltips(checked); + } + + return ( +
+ + {t('settings.tooltips.title', 'Show Tooltips')}: + handleChange(e.target.checked)} + /> +
+ ); +}; diff --git a/browser/src/components/menu/video/index.tsx b/browser/src/components/menu/video/index.tsx index f2bb0b3f..1600a0a3 100644 --- a/browser/src/components/menu/video/index.tsx +++ b/browser/src/components/menu/video/index.tsx @@ -1,5 +1,8 @@ import { Popover } from 'antd'; import { MonitorIcon } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; + +import { MenuTooltip } from '../menu-tooltip'; import { Device } from './device.tsx'; import { Resolution } from './resolution.tsx'; @@ -7,6 +10,8 @@ import { Rotation } from './rotation.tsx'; import { Scale } from './scale.tsx'; export const Video = () => { + const { t } = useTranslation(); + const content = (
@@ -17,10 +22,12 @@ export const Video = () => { ); return ( - -
- -
-
+ + +
+ +
+
+
); }; diff --git a/browser/src/libs/storage/index.ts b/browser/src/libs/storage/index.ts index 7583d973..c5d0f6f1 100644 --- a/browser/src/libs/storage/index.ts +++ b/browser/src/libs/storage/index.ts @@ -13,6 +13,7 @@ const MOUSE_SCROLL_DIRECTION_KEY = 'nanokvm-usb-mouse-scroll-direction'; const MOUSE_SCROLL_INTERVAL_KEY = 'nanokvm-usb-mouse-scroll-interval'; const MOUSE_JIGGLER_MODE_KEY = 'nanokvm-usb-mouse-jiggler-mode'; const KEYBOARD_SHORTCUT_KEY = 'nanokvm-usb-keyboard-shortcut'; +const SHOW_TOOLTIPS_KEY = 'nanokvm-usb-show-tooltips'; export function getLanguage() { return localStorage.getItem(LANGUAGE_KEY); @@ -155,3 +156,12 @@ export function getMouseJigglerMode(): 'enable' | 'disable' { export function setMouseJigglerMode(jiggler: 'enable' | 'disable'): void { localStorage.setItem(MOUSE_JIGGLER_MODE_KEY, jiggler); } + +export function getShowTooltips(): boolean { + const value = localStorage.getItem(SHOW_TOOLTIPS_KEY); + return value !== 'false'; // Default to true +} + +export function setShowTooltips(show: boolean): void { + localStorage.setItem(SHOW_TOOLTIPS_KEY, show ? 'true' : 'false'); +}