1
0
Fork 0
mirror of https://gitlab.com/futo-org/fcast.git synced 2025-06-24 21:25:23 +00:00

Receivers: Reworked network interface UI

This commit is contained in:
Michael Hollister 2025-04-22 17:28:02 -05:00
parent ed89b61cab
commit 28a617daa7
15 changed files with 491 additions and 246 deletions

View file

@ -0,0 +1,10 @@
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256"
width="1em"
height="1em">
<path
fill="white"
d="M232 114h-98V86h10a14 14 0 0 0 14-14V40a14 14 0 0 0-14-14h-32a14 14 0 0 0-14 14v32a14 14 0 0 0 14 14h10v28H24a6 6 0 0 0 0 12h34v36H48a14 14 0 0 0-14 14v32a14 14 0 0 0 14 14h32a14 14 0 0 0 14-14v-32a14 14 0 0 0-14-14H70v-36h116v36h-10a14 14 0 0 0-14 14v32a14 14 0 0 0 14 14h32a14 14 0 0 0 14-14v-32a14 14 0 0 0-14-14h-10v-36h34a6 6 0 0 0 0-12M110 72V40a2 2 0 0 1 2-2h32a2 2 0 0 1 2 2v32a2 2 0 0 1-2 2h-32a2 2 0 0 1-2-2M82 176v32a2 2 0 0 1-2 2H48a2 2 0 0 1-2-2v-32a2 2 0 0 1 2-2h32a2 2 0 0 1 2 2m128 0v32a2 2 0 0 1-2 2h-32a2 2 0 0 1-2-2v-32a2 2 0 0 1 2-2h32a2 2 0 0 1 2 2"
></path>
</svg>

After

Width:  |  Height:  |  Size: 729 B

View file

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.44873 1.9375C7.13805 0.6875 8.86195 0.6875 9.55246 1.9375L15.7599 13.1875C15.9172 13.4725 16 13.7959 16 14.125C16 14.4541 15.9172 14.7774 15.7599 15.0625C15.6027 15.3475 15.3764 15.5842 15.104 15.7488C14.8316 15.9133 14.5226 16 14.2081 16H1.79314C1.47848 16.0002 1.16932 15.9137 0.896742 15.7492C0.624165 15.5848 0.397785 15.3481 0.240368 15.063C0.0829523 14.7779 5.04209e-05 14.4545 2.29922e-08 14.1253C-5.03749e-05 13.7961 0.0827525 13.4726 0.240081 13.1875L6.44873 1.9375Z" fill="#fdb022"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.09511 5C8.31294 5 8.52184 5.08889 8.67587 5.24713C8.8299 5.40536 8.91643 5.61997 8.91643 5.84375V9.21875C8.91643 9.44253 8.8299 9.65714 8.67587 9.81537C8.52184 9.97361 8.31294 10.0625 8.09511 10.0625C7.87728 10.0625 7.66837 9.97361 7.51434 9.81537C7.36031 9.65714 7.27378 9.44253 7.27378 9.21875V5.84375C7.27378 5.61997 7.36031 5.40536 7.51434 5.24713C7.66837 5.08889 7.87728 5 8.09511 5ZM8.09511 14C8.38555 14 8.66409 13.8815 8.86946 13.6705C9.07483 13.4595 9.19021 13.1734 9.19021 12.875C9.19021 12.5766 9.07483 12.2905 8.86946 12.0795C8.66409 11.8685 8.38555 11.75 8.09511 11.75C7.80467 11.75 7.52612 11.8685 7.32075 12.0795C7.11538 12.2905 7 12.5766 7 12.875C7 13.1734 7.11538 13.4595 7.32075 13.6705C7.52612 13.8815 7.80467 14 8.09511 14Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,10 @@
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="1em"
height="1em">
<path
fill="white"
d="M12 3C7.79 3 3.7 4.41.38 7C4.41 12.06 7.89 16.37 12 21.5c4.08-5.08 8.24-10.26 11.65-14.5C20.32 4.41 16.22 3 12 3m0 2c3.07 0 6.09.86 8.71 2.45l-5.1 6.36A8.4 8.4 0 0 0 12 13c-1.25 0-2.5.28-3.61.8L3.27 7.44C5.91 5.85 8.93 5 12 5"
></path>
</svg>

After

Width:  |  Height:  |  Size: 383 B

View file

@ -0,0 +1,10 @@
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="1em"
height="1em">
<path
fill="white"
d="M12 3C7.79 3 3.7 4.41.38 7C4.41 12.06 7.89 16.37 12 21.5c4.08-5.08 8.24-10.26 11.65-14.5C20.32 4.41 16.22 3 12 3m0 2c3.07 0 6.09.86 8.71 2.45l-3.21 3.98C16.26 10.74 14.37 10 12 10c-2.38 0-4.26.75-5.5 1.43L3.27 7.44C5.91 5.85 8.93 5 12 5"
></path>
</svg>

After

Width:  |  Height:  |  Size: 394 B

View file

@ -0,0 +1,10 @@
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="1em"
height="1em">
<path
fill="white"
d="M12 3C7.79 3 3.7 4.41.38 7C4.41 12.06 7.89 16.37 12 21.5c4.08-5.08 8.24-10.26 11.65-14.5C20.32 4.41 16.22 3 12 3m0 2c3.07 0 6.09.86 8.71 2.45l-1.94 2.43C17.26 9 14.88 8 12 8C9 8 6.68 9 5.21 9.84l-1.94-2.4C5.91 5.85 8.93 5 12 5"
></path>
</svg>

After

Width:  |  Height:  |  Size: 384 B

View file

@ -0,0 +1,10 @@
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="1em"
height="1em">
<path
fill="white"
d="M12 3C7.79 3 3.7 4.41.38 7C4.41 12.06 7.89 16.37 12 21.5c4.08-5.08 8.24-10.26 11.65-14.5C20.32 4.41 16.22 3 12 3"
></path>
</svg>

After

Width:  |  Height:  |  Size: 270 B

View file

@ -0,0 +1,10 @@
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="1em"
height="1em">
<path
fill="white"
d="M12 3C7.79 3 3.7 4.41.38 7H.36C4.24 11.83 8.13 16.66 12 21.5c3.89-4.84 7.77-9.67 11.64-14.5h.01C20.32 4.41 16.22 3 12 3m0 2c3.07 0 6.09.86 8.71 2.45L12 18.3L3.27 7.44C5.9 5.85 8.92 5 12 5"
></path>
</svg>

After

Width:  |  Height:  |  Size: 345 B

View file

@ -5,6 +5,7 @@ import * as url from 'url';
import { AddressInfo } from 'modules/ws'; import { AddressInfo } from 'modules/ws';
import { v4 as uuidv4 } from 'modules/uuid'; import { v4 as uuidv4 } from 'modules/uuid';
import { Main } from 'src/Main'; import { Main } from 'src/Main';
import si from 'modules/systeminformation';
export class NetworkService { export class NetworkService {
static key: string = null; static key: string = null;
@ -12,6 +13,8 @@ export class NetworkService {
static proxyServer: http.Server; static proxyServer: http.Server;
static proxyServerAddress: AddressInfo; static proxyServerAddress: AddressInfo;
static proxiedFiles: Map<string, { url: string, headers: { [key: string]: string } }> = new Map(); static proxiedFiles: Map<string, { url: string, headers: { [key: string]: string } }> = new Map();
static networkStateChangeListenerTimeout = 2500;
private static networkStateChangeListenerInterfaces = [];
private static setupProxyServer(): Promise<void> { private static setupProxyServer(): Promise<void> {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
@ -104,21 +107,54 @@ export class NetworkService {
return proxiedUrl; return proxiedUrl;
} }
static getAllIPv4Addresses() { static async networkStateChangeListener(forceUpdate: boolean, networkChangedCb: (networkInfo: any) => void) {
const interfaces = os.networkInterfaces(); const queriedInterfaces: si.Systeminformation.NetworkInterfacesData[] = await new Promise<si.Systeminformation.NetworkInterfacesData[]>((resolve, reject) => {
const ipv4Addresses: string[] = []; si.networkInterfaces((data) => {
// console.log(data);
for (const interfaceName in interfaces) { if (Array.isArray(data)) {
const addresses = interfaces[interfaceName]; resolve(data);
if (!addresses) continue; }
else {
resolve([data]);
}
});
});
for (const addressInfo of addresses) { const wifiConnections: si.Systeminformation.WifiConnectionData[] = await new Promise<si.Systeminformation.WifiConnectionData[]>((resolve, reject) => {
if (addressInfo.family === 'IPv4' && !addressInfo.internal) { si.wifiConnections((data) => {
ipv4Addresses.push(addressInfo.address); // console.log(data);
if (Array.isArray(data)) {
resolve(data);
}
else {
resolve([data]);
}
});
});
const interfaces = [];
for (const iface of queriedInterfaces) {
if (iface.ip4 !== '' && !iface.internal && !iface.virtual) {
const isWireless = wifiConnections.some(e => {
if (e.iface === iface.iface) {
interfaces.push({ type: 'wireless', name: e.ssid, address: iface.ip4, signalLevel: e.quality });
return true;
}
return false;
});
if (!isWireless) {
interfaces.push({ type: 'wired', name: iface.ifaceName, address: iface.ip4 });
} }
} }
} }
return ipv4Addresses; if (forceUpdate || (JSON.stringify(interfaces) !== JSON.stringify(NetworkService.networkStateChangeListenerInterfaces))) {
NetworkService.networkStateChangeListenerInterfaces = interfaces;
networkChangedCb(interfaces);
}
} }
} }

View file

@ -1,5 +1,6 @@
export enum ToastIcon { export enum ToastIcon {
INFO, INFO,
WARNING,
ERROR, ERROR,
} }
@ -40,6 +41,10 @@ function renderToast(message: string, icon: ToastIcon = ToastIcon.INFO, duration
toastIcon.style.backgroundImage = 'url(../assets/icons/app/info.svg)'; toastIcon.style.backgroundImage = 'url(../assets/icons/app/info.svg)';
break; break;
case ToastIcon.WARNING:
toastIcon.style.backgroundImage = 'url(../assets/icons/app/warning.svg)';
break;
case ToastIcon.ERROR: case ToastIcon.ERROR:
toastIcon.style.backgroundImage = 'url(../assets/icons/app/error.svg)'; toastIcon.style.backgroundImage = 'url(../assets/icons/app/error.svg)';
break; break;

View file

@ -7,6 +7,7 @@ const connectionStatusText = document.getElementById("connection-status-text");
const connectionStatusSpinner = document.getElementById("connection-spinner"); const connectionStatusSpinner = document.getElementById("connection-spinner");
const connectionStatusCheck = document.getElementById("connection-check"); const connectionStatusCheck = document.getElementById("connection-check");
let connections = []; let connections = [];
let renderedAddresses = null;
// Window might be re-created while devices are still connected // Window might be re-created while devices are still connected
window.targetAPI.onPing((_event, value: any) => { window.targetAPI.onPing((_event, value: any) => {
@ -56,14 +57,41 @@ function renderIPsAndQRCode() {
const value = window.targetAPI.getDeviceInfo(); const value = window.targetAPI.getDeviceInfo();
console.log("device info", value); console.log("device info", value);
const ipsElement = document.getElementById('ips'); const addresses = [];
if (ipsElement) { value.interfaces.forEach((e) => addresses.push(e.address));
ipsElement.innerHTML = `IPs<br>${value.addresses.join('<br>')}`; const connInfo = document.getElementById('connection-information');
const connError = document.getElementById('connection-error');
if (renderedAddresses !== null && addresses.length > 0) {
toast("Network connections has changed, please reconnect sender devices to receiver if you experience issues", ToastIcon.WARNING);
} }
else if (addresses.length === 0) {
connInfo.setAttribute("style", "display: none");
connError.setAttribute("style", "display: block");
if (renderedAddresses !== null) {
toast("Lost network connection, please reconnect to a network", ToastIcon.ERROR);
}
renderedAddresses = []
return;
}
if (renderedAddresses !== null && renderedAddresses.length === 0) {
connInfo.setAttribute("style", "display: block");
connError.setAttribute("style", "display: none");
}
renderIPs(value.interfaces);
if (JSON.stringify(addresses) === JSON.stringify(renderedAddresses)) {
return;
}
renderedAddresses = addresses;
const fcastConfig = { const fcastConfig = {
name: value.name, name: value.name,
addresses: value.addresses, addresses: addresses,
services: [ services: [
{ port: 46899, type: 0 }, //TCP { port: 46899, type: 0 }, //TCP
{ port: 46898, type: 1 }, //WS { port: 46898, type: 1 }, //WS
@ -98,3 +126,54 @@ function renderIPsAndQRCode() {
onQRCodeRendered(); onQRCodeRendered();
} }
function renderIPs(interfaces: any) {
const ipsElement = document.getElementById('ips');
if (ipsElement) {
const ipsIconColumn = document.getElementById('ips-iface-icon');
ipsIconColumn.innerHTML = '';
const ipsTextColumn = document.getElementById('ips-iface-text');
ipsTextColumn.innerHTML = '';
const ipsNameColumn = document.getElementById('ips-iface-name');
ipsNameColumn.innerHTML = '';
for (const iface of interfaces) {
const ipIcon = document.createElement("div");
let icon = 'iconSize ';
if (iface.type === 'wired') {
icon += 'ip-wired-icon';
}
else if (iface.type === 'wireless' && (iface.signalLevel === 0 || iface.signalLevel >= 90)) {
icon += 'ip-wireless-4-icon';
}
else if (iface.type === 'wireless' && iface.signalLevel >= 70) {
icon += 'ip-wireless-3-icon';
}
else if (iface.type === 'wireless' && iface.signalLevel >= 50) {
icon += 'ip-wireless-2-icon';
}
else if (iface.type === 'wireless' && iface.signalLevel >= 30) {
icon += 'ip-wireless-1-icon';
}
else if (iface.type === 'wireless') {
icon += 'ip-wireless-0-icon';
}
ipIcon.className = icon;
ipsIconColumn.append(ipIcon);
const ipText = document.createElement("div");
ipText.className = 'ip-entry-text';
ipText.textContent = iface.address;
ipsTextColumn.append(ipText);
const ipName = document.createElement("div");
ipName.className = 'ip-entry-text';
ipName.textContent = iface.name;
ipsNameColumn.append(ipName);
}
}
}

View file

@ -45,6 +45,12 @@ body, html {
margin-bottom: 3px; margin-bottom: 3px;
} }
.iconSize {
width: 32px;
height: 32px;
background-size: cover;
}
#ui-container { #ui-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -134,11 +140,85 @@ body, html {
font-weight: bold; font-weight: bold;
} }
#connection-status-text, #ips, #automatic-discovery { #connection-error {
display: none;
}
#connection-error-icon {
width: 84px;
height: 84px;
margin: auto;
margin-top: 20px;
background-image: url(../assets/icons/app/error.svg);
background-size: cover;
}
#connection-error-text {
margin-top: 20px;
font-weight: bold;
}
#connection-status-text {
margin-top: 20px; margin-top: 20px;
white-space: pre; white-space: pre;
} }
#ips {
display: flex;
margin-top: 20px;
white-space: pre;
gap: 10px;
justify-content: center;
}
#ips-iface-icon {
display: flex;
flex-direction: column;
}
#ips-iface-text {
display: flex;
flex-direction: column;
margin-right: 20px;
align-items: start;
}
#ips-iface-name {
display: flex;
flex-direction: column;
align-items: start;
}
.ip-entry-text {
margin-top: 4px;
margin-bottom: 4px;
}
.ip-wired-icon {
background-image: url(../assets/icons/app/network-light.svg);
}
.ip-wireless-4-icon {
background-image: url(../assets/icons/app/wifi-strength-4.svg);
}
.ip-wireless-3-icon {
background-image: url(../assets/icons/app/wifi-strength-3.svg);
}
.ip-wireless-2-icon {
background-image: url(../assets/icons/app/wifi-strength-2.svg);
}
.ip-wireless-1-icon {
background-image: url(../assets/icons/app/wifi-strength-1.svg);
}
.ip-wireless-0-icon {
background-image: url(../assets/icons/app/wifi-strength-outline.svg);
}
#connection-spinner { #connection-spinner {
padding: 20px; padding: 20px;
} }

View file

@ -19,6 +19,7 @@
"https": "^1.0.0", "https": "^1.0.0",
"log4js": "^6.9.1", "log4js": "^6.9.1",
"qrcode": "^1.5.3", "qrcode": "^1.5.3",
"systeminformation": "^5.25.11",
"url": "^0.11.4", "url": "^0.11.4",
"uuid": "^11.0.3", "uuid": "^11.0.3",
"ws": "^8.18.0", "ws": "^8.18.0",
@ -34,7 +35,7 @@
"@electron-forge/plugin-auto-unpack-natives": "^7.8.0", "@electron-forge/plugin-auto-unpack-natives": "^7.8.0",
"@electron-forge/plugin-fuses": "^7.8.0", "@electron-forge/plugin-fuses": "^7.8.0",
"@electron/fuses": "^1.8.0", "@electron/fuses": "^1.8.0",
"@eslint/js": "^9.10.0", "@eslint/js": "^9.25.0",
"@futo/forge-maker-wix-linux": "^7.5.0", "@futo/forge-maker-wix-linux": "^7.5.0",
"@types/electron-json-storage": "^4.5.4", "@types/electron-json-storage": "^4.5.4",
"@types/jest": "^29.5.11", "@types/jest": "^29.5.11",
@ -44,18 +45,18 @@
"@types/workerpool": "^6.1.1", "@types/workerpool": "^6.1.1",
"@types/ws": "^8.5.10", "@types/ws": "^8.5.10",
"@types/yargs": "^17.0.33", "@types/yargs": "^17.0.33",
"copy-webpack-plugin": "^12.0.2", "copy-webpack-plugin": "^13.0.0",
"electron": "^32.2.1", "electron": "^35.2.0",
"eslint": "^9.10.0", "eslint": "^9.25.0",
"globals": "^15.9.0", "globals": "^16.0.0",
"jest": "^29.7.0", "jest": "^29.7.0",
"mdns-js": "github:mdns-js/node-mdns-js", "mdns-js": "github:mdns-js/node-mdns-js",
"ts-jest": "^29.1.1", "ts-jest": "^29.1.1",
"ts-loader": "^9.4.2", "ts-loader": "^9.4.2",
"typescript": "^5.5.4", "typescript": "^5.5.4",
"typescript-eslint": "^8.4.0", "typescript-eslint": "^8.4.0",
"webpack": "^5.75.0", "webpack": "^5.99.6",
"webpack-cli": "^5.0.1" "webpack-cli": "^6.0.1"
} }
}, },
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
@ -598,13 +599,13 @@
"optional": true "optional": true
}, },
"node_modules/@discoveryjs/json-ext": { "node_modules/@discoveryjs/json-ext": {
"version": "0.5.7", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz",
"integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=14.17.0"
} }
}, },
"node_modules/@electron-forge/cli": { "node_modules/@electron-forge/cli": {
@ -1480,9 +1481,9 @@
} }
}, },
"node_modules/@eslint/core": { "node_modules/@eslint/core": {
"version": "0.12.0", "version": "0.13.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
"integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@ -1530,9 +1531,9 @@
} }
}, },
"node_modules/@eslint/js": { "node_modules/@eslint/js": {
"version": "9.24.0", "version": "9.25.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.0.tgz",
"integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", "integrity": "sha512-iWhsUS8Wgxz9AXNfvfOPFSW4VfMXdVhp1hjkZVhXCrpgh/aLcc45rX6MPu+tIVUWDw0HfNwth7O28M1xDxNf9w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -1563,19 +1564,6 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
} }
}, },
"node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
"integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@futo/electron-wix-msi-linux": { "node_modules/@futo/electron-wix-msi-linux": {
"version": "5.1.3", "version": "5.1.3",
"resolved": "https://gitlab.futo.org/api/v4/projects/305/packages/npm/@futo/electron-wix-msi-linux/-/@futo/electron-wix-msi-linux-5.1.3.tgz", "resolved": "https://gitlab.futo.org/api/v4/projects/305/packages/npm/@futo/electron-wix-msi-linux/-/@futo/electron-wix-msi-linux-5.1.3.tgz",
@ -2414,19 +2402,6 @@
"url": "https://github.com/sindresorhus/is?sponsor=1" "url": "https://github.com/sindresorhus/is?sponsor=1"
} }
}, },
"node_modules/@sindresorhus/merge-streams": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
"integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@sinonjs/commons": { "node_modules/@sinonjs/commons": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
@ -3136,45 +3111,45 @@
} }
}, },
"node_modules/@webpack-cli/configtest": { "node_modules/@webpack-cli/configtest": {
"version": "2.1.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz",
"integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=14.15.0" "node": ">=18.12.0"
}, },
"peerDependencies": { "peerDependencies": {
"webpack": "5.x.x", "webpack": "^5.82.0",
"webpack-cli": "5.x.x" "webpack-cli": "6.x.x"
} }
}, },
"node_modules/@webpack-cli/info": { "node_modules/@webpack-cli/info": {
"version": "2.0.2", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz",
"integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=14.15.0" "node": ">=18.12.0"
}, },
"peerDependencies": { "peerDependencies": {
"webpack": "5.x.x", "webpack": "^5.82.0",
"webpack-cli": "5.x.x" "webpack-cli": "6.x.x"
} }
}, },
"node_modules/@webpack-cli/serve": { "node_modules/@webpack-cli/serve": {
"version": "2.0.5", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz",
"integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=14.15.0" "node": ">=18.12.0"
}, },
"peerDependencies": { "peerDependencies": {
"webpack": "5.x.x", "webpack": "^5.82.0",
"webpack-cli": "5.x.x" "webpack-cli": "6.x.x"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"webpack-dev-server": { "webpack-dev-server": {
@ -4434,18 +4409,17 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/copy-webpack-plugin": { "node_modules/copy-webpack-plugin": {
"version": "12.0.2", "version": "13.0.0",
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.0.tgz",
"integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", "integrity": "sha512-FgR/h5a6hzJqATDGd9YG41SeDViH+0bkHn6WNXCi5zKAZkeESeSxLySSsFLHqLEVCh0E+rITmCf0dusXWYukeQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"fast-glob": "^3.3.2",
"glob-parent": "^6.0.1", "glob-parent": "^6.0.1",
"globby": "^14.0.0",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
"schema-utils": "^4.2.0", "schema-utils": "^4.2.0",
"serialize-javascript": "^6.0.2" "serialize-javascript": "^6.0.2",
"tinyglobby": "^0.2.12"
}, },
"engines": { "engines": {
"node": ">= 18.12.0" "node": ">= 18.12.0"
@ -4886,15 +4860,15 @@
} }
}, },
"node_modules/electron": { "node_modules/electron": {
"version": "32.3.3", "version": "35.2.0",
"resolved": "https://registry.npmjs.org/electron/-/electron-32.3.3.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-35.2.0.tgz",
"integrity": "sha512-7FT8tDg+MueAw8dBn5LJqDvlM4cZkKJhXfgB3w7P5gvSoUQVAY6LIQcXJxgL+vw2rIRY/b9ak7ZBFbCMF2Bk4w==", "integrity": "sha512-GHda7oCkN0pA23qzah735DEbRa06IPwlzP3uvjAmf9af8gxdj5i93JEHeQVGVmSVpd7sSb1pfecs9nz7B1q5ag==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@electron/get": "^2.0.0", "@electron/get": "^2.0.0",
"@types/node": "^20.9.0", "@types/node": "^22.7.7",
"extract-zip": "^2.0.1" "extract-zip": "^2.0.1"
}, },
"bin": { "bin": {
@ -5403,16 +5377,6 @@
"global-agent": "^3.0.0" "global-agent": "^3.0.0"
} }
}, },
"node_modules/electron/node_modules/@types/node": {
"version": "20.17.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz",
"integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
}
},
"node_modules/electron/node_modules/fs-extra": { "node_modules/electron/node_modules/fs-extra": {
"version": "8.1.0", "version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
@ -5448,13 +5412,6 @@
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
}, },
"node_modules/electron/node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"dev": true,
"license": "MIT"
},
"node_modules/electron/node_modules/universalify": { "node_modules/electron/node_modules/universalify": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
@ -5641,20 +5598,20 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "9.24.0", "version": "9.25.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.0.tgz",
"integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", "integrity": "sha512-MsBdObhM4cEwkzCiraDv7A6txFXEqtNXOb877TsSp2FCkBNl8JfVQrmiuDqC1IkejT6JLPzYBXx/xAiYhyzgGA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.20.0", "@eslint/config-array": "^0.20.0",
"@eslint/config-helpers": "^0.2.0", "@eslint/config-helpers": "^0.2.1",
"@eslint/core": "^0.12.0", "@eslint/core": "^0.13.0",
"@eslint/eslintrc": "^3.3.1", "@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.24.0", "@eslint/js": "9.25.0",
"@eslint/plugin-kit": "^0.2.7", "@eslint/plugin-kit": "^0.2.8",
"@humanfs/node": "^0.16.6", "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2", "@humanwhocodes/retry": "^0.4.2",
@ -6592,9 +6549,9 @@
} }
}, },
"node_modules/globals": { "node_modules/globals": {
"version": "15.15.0", "version": "16.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz",
"integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -6622,37 +6579,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/globby": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz",
"integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@sindresorhus/merge-streams": "^2.1.0",
"fast-glob": "^3.3.3",
"ignore": "^7.0.3",
"path-type": "^6.0.0",
"slash": "^5.1.0",
"unicorn-magic": "^0.3.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/globby/node_modules/ignore": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.3.tgz",
"integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/gopd": { "node_modules/gopd": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@ -9507,19 +9433,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/path-type": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz",
"integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/pe-library": { "node_modules/pe-library": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/pe-library/-/pe-library-1.0.1.tgz", "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-1.0.1.tgz",
@ -10794,19 +10707,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/slash": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
"integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/slice-ansi": { "node_modules/slice-ansi": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
@ -11231,6 +11131,32 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/systeminformation": {
"version": "5.25.11",
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.25.11.tgz",
"integrity": "sha512-jI01fn/t47rrLTQB0FTlMCC+5dYx8o0RRF+R4BPiUNsvg5OdY0s9DKMFmJGrx5SwMZQ4cag0Gl6v8oycso9b/g==",
"license": "MIT",
"os": [
"darwin",
"linux",
"win32",
"freebsd",
"openbsd",
"netbsd",
"sunos",
"android"
],
"bin": {
"systeminformation": "lib/cli.js"
},
"engines": {
"node": ">=8.0.0"
},
"funding": {
"type": "Buy me a coffee",
"url": "https://www.buymeacoffee.com/systeminfo"
}
},
"node_modules/tapable": { "node_modules/tapable": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@ -11404,6 +11330,51 @@
"license": "MIT", "license": "MIT",
"optional": true "optional": true
}, },
"node_modules/tinyglobby": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.4.4",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tinyglobby/node_modules/fdir": {
"version": "6.4.4",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/tinyglobby/node_modules/picomatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/tmp": { "node_modules/tmp": {
"version": "0.2.3", "version": "0.2.3",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
@ -11718,19 +11689,6 @@
"devOptional": true, "devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/unicorn-magic": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
"integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/unique-filename": { "node_modules/unique-filename": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz",
@ -11940,9 +11898,9 @@
"license": "BSD-2-Clause" "license": "BSD-2-Clause"
}, },
"node_modules/webpack": { "node_modules/webpack": {
"version": "5.99.5", "version": "5.99.6",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.5.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz",
"integrity": "sha512-q+vHBa6H9qwBLUlHL4Y7L0L1/LlyBKZtS9FHNCQmtayxjI5RKC9yD8gpvLeqGv5lCQp1Re04yi0MF40pf30Pvg==", "integrity": "sha512-TJOLrJ6oeccsGWPl7ujCYuc0pIq2cNsuD6GZDma8i5o5Npvcco/z+NKvZSFsP0/x6SShVb0+X2JK/JHUjKY9dQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -11987,43 +11945,40 @@
} }
}, },
"node_modules/webpack-cli": { "node_modules/webpack-cli": {
"version": "5.1.4", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz",
"integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@discoveryjs/json-ext": "^0.5.0", "@discoveryjs/json-ext": "^0.6.1",
"@webpack-cli/configtest": "^2.1.1", "@webpack-cli/configtest": "^3.0.1",
"@webpack-cli/info": "^2.0.2", "@webpack-cli/info": "^3.0.1",
"@webpack-cli/serve": "^2.0.5", "@webpack-cli/serve": "^3.0.1",
"colorette": "^2.0.14", "colorette": "^2.0.14",
"commander": "^10.0.1", "commander": "^12.1.0",
"cross-spawn": "^7.0.3", "cross-spawn": "^7.0.3",
"envinfo": "^7.7.3", "envinfo": "^7.14.0",
"fastest-levenshtein": "^1.0.12", "fastest-levenshtein": "^1.0.12",
"import-local": "^3.0.2", "import-local": "^3.0.2",
"interpret": "^3.1.1", "interpret": "^3.1.1",
"rechoir": "^0.8.0", "rechoir": "^0.8.0",
"webpack-merge": "^5.7.3" "webpack-merge": "^6.0.1"
}, },
"bin": { "bin": {
"webpack-cli": "bin/cli.js" "webpack-cli": "bin/cli.js"
}, },
"engines": { "engines": {
"node": ">=14.15.0" "node": ">=18.12.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/webpack" "url": "https://opencollective.com/webpack"
}, },
"peerDependencies": { "peerDependencies": {
"webpack": "5.x.x" "webpack": "^5.82.0"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@webpack-cli/generators": {
"optional": true
},
"webpack-bundle-analyzer": { "webpack-bundle-analyzer": {
"optional": true "optional": true
}, },
@ -12033,28 +11988,28 @@
} }
}, },
"node_modules/webpack-cli/node_modules/commander": { "node_modules/webpack-cli/node_modules/commander": {
"version": "10.0.1", "version": "12.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=14" "node": ">=18"
} }
}, },
"node_modules/webpack-merge": { "node_modules/webpack-merge": {
"version": "5.10.0", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz",
"integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"clone-deep": "^4.0.1", "clone-deep": "^4.0.1",
"flat": "^5.0.2", "flat": "^5.0.2",
"wildcard": "^2.0.0" "wildcard": "^2.0.1"
}, },
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=18.0.0"
} }
}, },
"node_modules/webpack-sources": { "node_modules/webpack-sources": {

View file

@ -22,7 +22,7 @@
"@electron-forge/plugin-auto-unpack-natives": "^7.8.0", "@electron-forge/plugin-auto-unpack-natives": "^7.8.0",
"@electron-forge/plugin-fuses": "^7.8.0", "@electron-forge/plugin-fuses": "^7.8.0",
"@electron/fuses": "^1.8.0", "@electron/fuses": "^1.8.0",
"@eslint/js": "^9.10.0", "@eslint/js": "^9.25.0",
"@futo/forge-maker-wix-linux": "^7.5.0", "@futo/forge-maker-wix-linux": "^7.5.0",
"@types/electron-json-storage": "^4.5.4", "@types/electron-json-storage": "^4.5.4",
"@types/jest": "^29.5.11", "@types/jest": "^29.5.11",
@ -32,18 +32,18 @@
"@types/workerpool": "^6.1.1", "@types/workerpool": "^6.1.1",
"@types/ws": "^8.5.10", "@types/ws": "^8.5.10",
"@types/yargs": "^17.0.33", "@types/yargs": "^17.0.33",
"copy-webpack-plugin": "^12.0.2", "copy-webpack-plugin": "^13.0.0",
"electron": "^32.2.1", "electron": "^35.2.0",
"eslint": "^9.10.0", "eslint": "^9.25.0",
"globals": "^15.9.0", "globals": "^16.0.0",
"jest": "^29.7.0", "jest": "^29.7.0",
"mdns-js": "github:mdns-js/node-mdns-js", "mdns-js": "github:mdns-js/node-mdns-js",
"ts-jest": "^29.1.1", "ts-jest": "^29.1.1",
"ts-loader": "^9.4.2", "ts-loader": "^9.4.2",
"typescript": "^5.5.4", "typescript": "^5.5.4",
"typescript-eslint": "^8.4.0", "typescript-eslint": "^8.4.0",
"webpack": "^5.75.0", "webpack": "^5.99.6",
"webpack-cli": "^5.0.1" "webpack-cli": "^6.0.1"
}, },
"dependencies": { "dependencies": {
"@vscode/sudo-prompt": "^9.3.1", "@vscode/sudo-prompt": "^9.3.1",
@ -56,6 +56,7 @@
"https": "^1.0.0", "https": "^1.0.0",
"log4js": "^6.9.1", "log4js": "^6.9.1",
"qrcode": "^1.5.3", "qrcode": "^1.5.3",
"systeminformation": "^5.25.11",
"url": "^0.11.4", "url": "^0.11.4",
"uuid": "^11.0.3", "uuid": "^11.0.3",
"ws": "^8.18.0", "ws": "^8.18.0",

View file

@ -206,6 +206,7 @@ export class Main {
ipcMain.on('send-volume-update', (event: IpcMainEvent, value: VolumeUpdateMessage) => { ipcMain.on('send-volume-update', (event: IpcMainEvent, value: VolumeUpdateMessage) => {
l.send(Opcode.VolumeUpdate, value); l.send(Opcode.VolumeUpdate, value);
}); });
});
ipcMain.on('send-download-request', async () => { ipcMain.on('send-download-request', async () => {
if (!Updater.isDownloading) { if (!Updater.isDownloading) {
@ -230,7 +231,6 @@ export class Main {
ipcMain.on('send-restart-request', async () => { ipcMain.on('send-restart-request', async () => {
Updater.restart(); Updater.restart();
}); });
});
ipcMain.handle('updater-progress', async () => { return Updater.updateProgress; }); ipcMain.handle('updater-progress', async () => { return Updater.updateProgress; });
@ -302,16 +302,27 @@ export class Main {
} }
}); });
let networkStateChangeListener = null;
Main.mainWindow.loadFile(path.join(__dirname, 'main/index.html')); Main.mainWindow.loadFile(path.join(__dirname, 'main/index.html'));
Main.mainWindow.on('closed', () => { Main.mainWindow.on('closed', () => {
Main.mainWindow = null; Main.mainWindow = null;
clearInterval(networkStateChangeListener);
}); });
Main.mainWindow.maximize(); Main.mainWindow.maximize();
Main.mainWindow.show(); Main.mainWindow.show();
Main.mainWindow.on('ready-to-show', () => { Main.mainWindow.on('ready-to-show', () => {
Main.mainWindow.webContents.send("device-info", {name: os.hostname(), addresses: NetworkService.getAllIPv4Addresses()}); NetworkService.networkStateChangeListener(true, (interfaces: any) => {
Main.mainWindow.webContents.send("device-info", { name: os.hostname(), interfaces: interfaces });
networkStateChangeListener = setInterval(() => {
NetworkService.networkStateChangeListener(false, (interfaces: any) => {
Main.mainWindow.webContents.send("device-info", { name: os.hostname(), interfaces: interfaces });
});
},
NetworkService.networkStateChangeListenerTimeout);
});
}); });
} }

View file

@ -44,16 +44,30 @@
</div> </div>
</div> </div>
<div id="detail-view" class="card"> <div id="detail-view" class="card">
<div class="non-selectable card-title">Manual connection information</div> <div class="non-selectable card-title">Connection Information</div>
<div class="card-title-separator"></div>
<div id="connection-information">
<div id="scan-to-connect" class="non-selectable">Scan with a FCast sender app</div>
<canvas id="qr-code"></canvas>
<div id="app-download" class="non-selectable app-download">Need a sender app?<br>Download Grayjay at <a href="https://grayjay.app" target="_blank">https://grayjay.app</a></div>
<br />
<div class="non-selectable card-title">Connection Details</div>
<div class="card-title-separator"></div> <div class="card-title-separator"></div>
<div> <div>
<div id="ips">IPs</div><br /> <div id="ips">
<div id="ips-iface-icon"></div>
<div id="ips-iface-text"></div>
<div id="ips-iface-name"></div>
</div><br />
<div id="ip-ports">Port<br>46899 (TCP), 46898 (WS)</div> <div id="ip-ports">Port<br>46899 (TCP), 46898 (WS)</div>
</div> </div>
<div id="automatic-discovery" class="non-selectable">Automatic discovery is available via mDNS</div> </div>
<canvas id="qr-code"></canvas> <div id="connection-error">
<div id="scan-to-connect" class="non-selectable">Scan with a FCast sender app.</div> <div id="connection-error-icon"></div>
<div id="app-download" class="non-selectable app-download">Need a sender app?<br>Download Grayjay at <a href="https://grayjay.app" target="_blank">https://grayjay.app</a></div> <div id="connection-error-text">Device not connected to a network</div>
</div>
</div> </div>
</div> </div>