import { MdOutlineContentPasteGo } from "react-icons/md"; import { LuCable, LuHardDrive, LuMaximize, LuSettings, LuSignal } from "react-icons/lu"; import { FaKeyboard } from "react-icons/fa6"; import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react"; import { Fragment, useCallback, useRef, useEffect } from "react"; import { CommandLineIcon, UserGroupIcon } from "@heroicons/react/20/solid"; import { Button } from "@components/Button"; import { useHidStore, useMountMediaStore, useSettingsStore, useUiStore, useRTCStore } from "@/hooks/stores"; import Container from "@components/Container"; import { cx } from "@/cva.config"; import PasteModal from "@/components/popovers/PasteModal"; import WakeOnLanModal from "@/components/popovers/WakeOnLan/Index"; import MountPopopover from "@/components/popovers/MountPopover"; import ExtensionPopover from "@/components/popovers/ExtensionPopover"; import SessionPopover from "@/components/popovers/SessionPopover"; import { useDeviceUiNavigation } from "@/hooks/useAppNavigation"; import { useSessionStore } from "@/stores/sessionStore"; import { usePermissions } from "@/hooks/usePermissions"; import { Permission } from "@/types/permissions"; export default function Actionbar({ requestFullscreen, }: { requestFullscreen: () => Promise; }) { const { navigateTo } = useDeviceUiNavigation(); const { isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } = useHidStore(); const { setDisableVideoFocusTrap, terminalType, setTerminalType, toggleSidebarView } = useUiStore(); const remoteVirtualMediaState = useMountMediaStore( state => state.remoteVirtualMediaState, ); const { developerMode } = useSettingsStore(); const { currentMode, sessions, setSessions } = useSessionStore(); const { rpcDataChannel } = useRTCStore(); const { hasPermission } = usePermissions(); // Fetch sessions on mount if we have an RPC channel useEffect(() => { if (rpcDataChannel?.readyState === "open" && sessions.length === 0) { const id = Math.random().toString(36).substring(2); const message = JSON.stringify({ jsonrpc: "2.0", method: "getSessions", params: {}, id }); const handler = (event: MessageEvent) => { try { const response = JSON.parse(event.data); if (response.id === id && response.result) { setSessions(response.result); } } catch { // Ignore parse errors for non-JSON messages } }; rpcDataChannel.addEventListener("message", handler); rpcDataChannel.send(message); const timeoutId = setTimeout(() => { rpcDataChannel.removeEventListener("message", handler); }, 5000); return () => { clearTimeout(timeoutId); rpcDataChannel.removeEventListener("message", handler); }; } }, [rpcDataChannel, sessions.length, setSessions]); // This is the only way to get a reliable state change for the popover // at time of writing this there is no mount, or unmount event for the popover const isOpen = useRef(false); const checkIfStateChanged = useCallback( (open: boolean) => { if (open !== isOpen.current) { isOpen.current = open; if (!open) { setTimeout(() => { setDisableVideoFocusTrap(false); }, 0); } } }, [setDisableVideoFocusTrap], ); return (
e.stopPropagation()} onKeyDown={e => e.stopPropagation()} className="flex flex-wrap items-center justify-between gap-x-4 gap-y-2 py-1.5" >
{developerMode && hasPermission(Permission.TERMINAL_ACCESS) && (
)} {hasPermission(Permission.KEYBOARD_INPUT) && (
)}
{/* Session Control */}
)}
{/* Only show Settings for sessions with settings access */} {hasPermission(Permission.SETTINGS_ACCESS) && (
)}
); }