Compare commits
4 Commits
f487c0d959
...
0a4a35a3cd
Author | SHA1 | Date |
---|---|---|
|
0a4a35a3cd | |
|
34dff0a3aa | |
|
66cccfe9e1 | |
|
a42384fed6 |
|
@ -1,7 +1,7 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<!-- These are the fonts used in the app -->
|
<!-- These are the fonts used in the app -->
|
||||||
<link
|
<link
|
||||||
|
@ -27,7 +27,14 @@
|
||||||
/>
|
/>
|
||||||
<title>JetKVM</title>
|
<title>JetKVM</title>
|
||||||
<link rel="stylesheet" href="/fonts/fonts.css" />
|
<link rel="stylesheet" href="/fonts/fonts.css" />
|
||||||
<link rel="icon" href="/favicon.png" />
|
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<link rel="shortcut icon" href="/favicon.ico" />
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||||
|
<meta name="apple-mobile-web-app-title" content="JetKVM" />
|
||||||
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
|
<meta name="theme-color" content="#051946" />
|
||||||
|
<meta name="description" content="A web-based KVM console for managing remote servers." />
|
||||||
<script>
|
<script>
|
||||||
// Initial theme setup
|
// Initial theme setup
|
||||||
document.documentElement.classList.toggle(
|
document.documentElement.classList.toggle(
|
||||||
|
|
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 972 B |
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56ZM11.993 7.895c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56ZM11.993 7.895c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/></svg><style>@media (prefers-color-scheme:light){:root{filter:none}}@media (prefers-color-scheme:dark){:root{filter:none}}</style></svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56ZM11.993 7.895c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56ZM11.993 7.895c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "JetKVM",
|
||||||
|
"short_name": "JetKVM",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/web-app-manifest-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/web-app-manifest-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"theme_color": "#002b36",
|
||||||
|
"background_color": "#051946",
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 7.9 KiB |
|
@ -105,9 +105,6 @@ export interface RTCState {
|
||||||
setRpcDataChannel: (channel: RTCDataChannel) => void;
|
setRpcDataChannel: (channel: RTCDataChannel) => void;
|
||||||
rpcDataChannel: RTCDataChannel | null;
|
rpcDataChannel: RTCDataChannel | null;
|
||||||
|
|
||||||
diskChannel: RTCDataChannel | null;
|
|
||||||
setDiskChannel: (channel: RTCDataChannel) => void;
|
|
||||||
|
|
||||||
peerConnectionState: RTCPeerConnectionState | null;
|
peerConnectionState: RTCPeerConnectionState | null;
|
||||||
setPeerConnectionState: (state: RTCPeerConnectionState) => void;
|
setPeerConnectionState: (state: RTCPeerConnectionState) => void;
|
||||||
|
|
||||||
|
@ -160,9 +157,6 @@ export const useRTCStore = create<RTCState>(set => ({
|
||||||
peerConnectionState: null,
|
peerConnectionState: null,
|
||||||
setPeerConnectionState: (state: RTCPeerConnectionState) => set({ peerConnectionState: state }),
|
setPeerConnectionState: (state: RTCPeerConnectionState) => set({ peerConnectionState: state }),
|
||||||
|
|
||||||
diskChannel: null,
|
|
||||||
setDiskChannel: (channel: RTCDataChannel) => set({ diskChannel: channel }),
|
|
||||||
|
|
||||||
mediaStream: null,
|
mediaStream: null,
|
||||||
setMediaStream: (stream: MediaStream) => set({ mediaStream: stream }),
|
setMediaStream: (stream: MediaStream) => set({ mediaStream: stream }),
|
||||||
|
|
||||||
|
@ -390,9 +384,6 @@ export interface RemoteVirtualMediaState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MountMediaState {
|
export interface MountMediaState {
|
||||||
localFile: File | null;
|
|
||||||
setLocalFile: (file: MountMediaState["localFile"]) => void;
|
|
||||||
|
|
||||||
remoteVirtualMediaState: RemoteVirtualMediaState | null;
|
remoteVirtualMediaState: RemoteVirtualMediaState | null;
|
||||||
setRemoteVirtualMediaState: (state: MountMediaState["remoteVirtualMediaState"]) => void;
|
setRemoteVirtualMediaState: (state: MountMediaState["remoteVirtualMediaState"]) => void;
|
||||||
|
|
||||||
|
@ -410,9 +401,6 @@ export interface MountMediaState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useMountMediaStore = create<MountMediaState>(set => ({
|
export const useMountMediaStore = create<MountMediaState>(set => ({
|
||||||
localFile: null,
|
|
||||||
setLocalFile: (file: MountMediaState["localFile"]) => set({ localFile: file }),
|
|
||||||
|
|
||||||
remoteVirtualMediaState: null,
|
remoteVirtualMediaState: null,
|
||||||
setRemoteVirtualMediaState: (state: MountMediaState["remoteVirtualMediaState"]) => set({ remoteVirtualMediaState: state }),
|
setRemoteVirtualMediaState: (state: MountMediaState["remoteVirtualMediaState"]) => set({ remoteVirtualMediaState: state }),
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,6 @@ export function Dialog({ onClose }: { onClose: () => void }) {
|
||||||
const {
|
const {
|
||||||
modalView,
|
modalView,
|
||||||
setModalView,
|
setModalView,
|
||||||
setLocalFile,
|
|
||||||
setRemoteVirtualMediaState,
|
setRemoteVirtualMediaState,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
setErrorMessage,
|
setErrorMessage,
|
||||||
|
@ -58,7 +57,6 @@ export function Dialog({ onClose }: { onClose: () => void }) {
|
||||||
const [incompleteFileName, setIncompleteFileName] = useState<string | null>(null);
|
const [incompleteFileName, setIncompleteFileName] = useState<string | null>(null);
|
||||||
const [mountInProgress, setMountInProgress] = useState(false);
|
const [mountInProgress, setMountInProgress] = useState(false);
|
||||||
function clearMountMediaState() {
|
function clearMountMediaState() {
|
||||||
setLocalFile(null);
|
|
||||||
setRemoteVirtualMediaState(null);
|
setRemoteVirtualMediaState(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,11 @@ const edids = [
|
||||||
"00FFFFFFFFFFFF0010AC132045393639201E0103803C22782ACD25A3574B9F270D5054A54B00714F8180A9C0D1C00101010101010101023A801871382D40582C450056502100001E000000FF00335335475132330A2020202020000000FC0044454C4C204432373231480A20000000FD00384C1E5311000A202020202020018102031AB14F90050403020716010611121513141F65030C001000023A801871382D40582C450056502100001E011D8018711C1620582C250056502100009E011D007251D01E206E28550056502100001E8C0AD08A20E02D10103E960056502100001800000000000000000000000000000000000000000000000000000000004F",
|
"00FFFFFFFFFFFF0010AC132045393639201E0103803C22782ACD25A3574B9F270D5054A54B00714F8180A9C0D1C00101010101010101023A801871382D40582C450056502100001E000000FF00335335475132330A2020202020000000FC0044454C4C204432373231480A20000000FD00384C1E5311000A202020202020018102031AB14F90050403020716010611121513141F65030C001000023A801871382D40582C450056502100001E011D8018711C1620582C250056502100009E011D007251D01E206E28550056502100001E8C0AD08A20E02D10103E960056502100001800000000000000000000000000000000000000000000000000000000004F",
|
||||||
label: "DELL D2721H, 1920x1080",
|
label: "DELL D2721H, 1920x1080",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value:
|
||||||
|
"00ffffffffffff0010ac0100020000000111010380221bff0a00000000000000000000adce0781800101010101010101010101010101000000ff0030303030303030303030303030000000ff0030303030303030303030303030000000fd00384c1f530b000a000000000000000000fc0044454c4c2049445241430a2020000a",
|
||||||
|
label: "DELL IDRAC EDID, 1280x1024",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const streamQualityOptions = [
|
const streamQualityOptions = [
|
||||||
|
@ -140,7 +145,7 @@ export default function SettingsVideoRoute() {
|
||||||
title="Video Enhancement"
|
title="Video Enhancement"
|
||||||
description="Adjust color settings to make the video output more vibrant and colorful"
|
description="Adjust color settings to make the video output more vibrant and colorful"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="space-y-4 pl-4">
|
<div className="space-y-4 pl-4">
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
title="Saturation"
|
title="Saturation"
|
||||||
|
|
|
@ -29,7 +29,6 @@ import {
|
||||||
USBStates,
|
USBStates,
|
||||||
useDeviceStore,
|
useDeviceStore,
|
||||||
useHidStore,
|
useHidStore,
|
||||||
useMountMediaStore,
|
|
||||||
useNetworkStateStore,
|
useNetworkStateStore,
|
||||||
User,
|
User,
|
||||||
useRTCStore,
|
useRTCStore,
|
||||||
|
@ -132,7 +131,6 @@ export default function KvmIdRoute() {
|
||||||
const {
|
const {
|
||||||
peerConnection, setPeerConnection,
|
peerConnection, setPeerConnection,
|
||||||
peerConnectionState, setPeerConnectionState,
|
peerConnectionState, setPeerConnectionState,
|
||||||
diskChannel, setDiskChannel,
|
|
||||||
setMediaStream,
|
setMediaStream,
|
||||||
setRpcDataChannel,
|
setRpcDataChannel,
|
||||||
isTurnServerInUse, setTurnServerInUse,
|
isTurnServerInUse, setTurnServerInUse,
|
||||||
|
@ -484,18 +482,12 @@ export default function KvmIdRoute() {
|
||||||
setRpcDataChannel(rpcDataChannel);
|
setRpcDataChannel(rpcDataChannel);
|
||||||
};
|
};
|
||||||
|
|
||||||
const diskDataChannel = pc.createDataChannel("disk");
|
|
||||||
diskDataChannel.onopen = () => {
|
|
||||||
setDiskChannel(diskDataChannel);
|
|
||||||
};
|
|
||||||
|
|
||||||
setPeerConnection(pc);
|
setPeerConnection(pc);
|
||||||
}, [
|
}, [
|
||||||
cleanupAndStopReconnecting,
|
cleanupAndStopReconnecting,
|
||||||
iceConfig?.iceServers,
|
iceConfig?.iceServers,
|
||||||
legacyHTTPSignaling,
|
legacyHTTPSignaling,
|
||||||
sendWebRTCSignal,
|
sendWebRTCSignal,
|
||||||
setDiskChannel,
|
|
||||||
setMediaStream,
|
setMediaStream,
|
||||||
setPeerConnection,
|
setPeerConnection,
|
||||||
setPeerConnectionState,
|
setPeerConnectionState,
|
||||||
|
@ -719,25 +711,6 @@ export default function KvmIdRoute() {
|
||||||
}
|
}
|
||||||
}, [navigate, navigateTo, queryParams, setModalView, setQueryParams]);
|
}, [navigate, navigateTo, queryParams, setModalView, setQueryParams]);
|
||||||
|
|
||||||
const { localFile } = useMountMediaStore();
|
|
||||||
useEffect(() => {
|
|
||||||
if (!diskChannel || !localFile) return;
|
|
||||||
diskChannel.onmessage = async e => {
|
|
||||||
console.debug("Received", e.data);
|
|
||||||
const data = JSON.parse(e.data);
|
|
||||||
const blob = localFile.slice(data.start, data.end);
|
|
||||||
const buf = await blob.arrayBuffer();
|
|
||||||
const header = new ArrayBuffer(16);
|
|
||||||
const headerView = new DataView(header);
|
|
||||||
headerView.setBigUint64(0, BigInt(data.start), false); // start offset, big-endian
|
|
||||||
headerView.setBigUint64(8, BigInt(buf.byteLength), false); // length, big-endian
|
|
||||||
const fullData = new Uint8Array(header.byteLength + buf.byteLength);
|
|
||||||
fullData.set(new Uint8Array(header), 0);
|
|
||||||
fullData.set(new Uint8Array(buf), header.byteLength);
|
|
||||||
diskChannel.send(fullData);
|
|
||||||
};
|
|
||||||
}, [diskChannel, localFile]);
|
|
||||||
|
|
||||||
// System update
|
// System update
|
||||||
const [kvmTerminal, setKvmTerminal] = useState<RTCDataChannel | null>(null);
|
const [kvmTerminal, setKvmTerminal] = useState<RTCDataChannel | null>(null);
|
||||||
const [serialConsole, setSerialConsole] = useState<RTCDataChannel | null>(null);
|
const [serialConsole, setSerialConsole] = useState<RTCDataChannel | null>(null);
|
||||||
|
|
|
@ -21,7 +21,6 @@ type Session struct {
|
||||||
ControlChannel *webrtc.DataChannel
|
ControlChannel *webrtc.DataChannel
|
||||||
RPCChannel *webrtc.DataChannel
|
RPCChannel *webrtc.DataChannel
|
||||||
HidChannel *webrtc.DataChannel
|
HidChannel *webrtc.DataChannel
|
||||||
DiskChannel *webrtc.DataChannel
|
|
||||||
shouldUmountVirtualMedia bool
|
shouldUmountVirtualMedia bool
|
||||||
rpcQueue chan webrtc.DataChannelMessage
|
rpcQueue chan webrtc.DataChannelMessage
|
||||||
}
|
}
|
||||||
|
|