mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
169 lines
5.8 KiB
TypeScript
169 lines
5.8 KiB
TypeScript
// Copyright 2024, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import * as electron from "electron";
|
|
import { getWebServerEndpoint } from "../frontend/util/endpoints";
|
|
|
|
export const WaveAppPathVarName = "WAVETERM_APP_PATH";
|
|
|
|
export function delay(ms): Promise<void> {
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
}
|
|
|
|
function setCtrlShift(wc: Electron.WebContents, state: boolean) {
|
|
wc.send("control-shift-state-update", state);
|
|
}
|
|
|
|
export function handleCtrlShiftFocus(sender: Electron.WebContents, focused: boolean) {
|
|
if (!focused) {
|
|
setCtrlShift(sender, false);
|
|
}
|
|
}
|
|
|
|
export function handleCtrlShiftState(sender: Electron.WebContents, waveEvent: WaveKeyboardEvent) {
|
|
if (waveEvent.type == "keyup") {
|
|
if (waveEvent.key === "Control" || waveEvent.key === "Shift") {
|
|
setCtrlShift(sender, false);
|
|
}
|
|
if (waveEvent.key == "Meta") {
|
|
if (waveEvent.control && waveEvent.shift) {
|
|
setCtrlShift(sender, true);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (waveEvent.type == "keydown") {
|
|
if (waveEvent.key === "Control" || waveEvent.key === "Shift" || waveEvent.key === "Meta") {
|
|
if (waveEvent.control && waveEvent.shift && !waveEvent.meta) {
|
|
// Set the control and shift without the Meta key
|
|
setCtrlShift(sender, true);
|
|
} else {
|
|
// Unset if Meta is pressed
|
|
setCtrlShift(sender, false);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
export function shNavHandler(event: Electron.Event<Electron.WebContentsWillNavigateEventParams>, url: string) {
|
|
if (url.startsWith("http://127.0.0.1:5173/index.html") || url.startsWith("http://localhost:5173/index.html")) {
|
|
// this is a dev-mode hot-reload, ignore it
|
|
console.log("allowing hot-reload of index.html");
|
|
return;
|
|
}
|
|
event.preventDefault();
|
|
if (url.startsWith("https://") || url.startsWith("http://") || url.startsWith("file://")) {
|
|
console.log("open external, shNav", url);
|
|
electron.shell.openExternal(url);
|
|
} else {
|
|
console.log("navigation canceled", url);
|
|
}
|
|
}
|
|
|
|
export function shFrameNavHandler(event: Electron.Event<Electron.WebContentsWillFrameNavigateEventParams>) {
|
|
if (!event.frame?.parent) {
|
|
// only use this handler to process iframe events (non-iframe events go to shNavHandler)
|
|
return;
|
|
}
|
|
const url = event.url;
|
|
console.log(`frame-navigation url=${url} frame=${event.frame.name}`);
|
|
if (event.frame.name == "webview") {
|
|
// "webview" links always open in new window
|
|
// this will *not* effect the initial load because srcdoc does not count as an electron navigation
|
|
console.log("open external, frameNav", url);
|
|
event.preventDefault();
|
|
electron.shell.openExternal(url);
|
|
return;
|
|
}
|
|
if (
|
|
event.frame.name == "pdfview" &&
|
|
(url.startsWith("blob:file:///") || url.startsWith(getWebServerEndpoint() + "/wave/stream-file?"))
|
|
) {
|
|
// allowed
|
|
return;
|
|
}
|
|
event.preventDefault();
|
|
console.log("frame navigation canceled");
|
|
}
|
|
|
|
function isWindowFullyVisible(bounds: electron.Rectangle): boolean {
|
|
const displays = electron.screen.getAllDisplays();
|
|
|
|
// Helper function to check if a point is inside any display
|
|
function isPointInDisplay(x: number, y: number) {
|
|
for (const display of displays) {
|
|
const { x: dx, y: dy, width, height } = display.bounds;
|
|
if (x >= dx && x < dx + width && y >= dy && y < dy + height) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Check all corners of the window
|
|
const topLeft = isPointInDisplay(bounds.x, bounds.y);
|
|
const topRight = isPointInDisplay(bounds.x + bounds.width, bounds.y);
|
|
const bottomLeft = isPointInDisplay(bounds.x, bounds.y + bounds.height);
|
|
const bottomRight = isPointInDisplay(bounds.x + bounds.width, bounds.y + bounds.height);
|
|
|
|
return topLeft && topRight && bottomLeft && bottomRight;
|
|
}
|
|
|
|
function findDisplayWithMostArea(bounds: electron.Rectangle): electron.Display {
|
|
const displays = electron.screen.getAllDisplays();
|
|
let maxArea = 0;
|
|
let bestDisplay = null;
|
|
|
|
for (let display of displays) {
|
|
const { x, y, width, height } = display.bounds;
|
|
const overlapX = Math.max(0, Math.min(bounds.x + bounds.width, x + width) - Math.max(bounds.x, x));
|
|
const overlapY = Math.max(0, Math.min(bounds.y + bounds.height, y + height) - Math.max(bounds.y, y));
|
|
const overlapArea = overlapX * overlapY;
|
|
|
|
if (overlapArea > maxArea) {
|
|
maxArea = overlapArea;
|
|
bestDisplay = display;
|
|
}
|
|
}
|
|
|
|
return bestDisplay;
|
|
}
|
|
|
|
function adjustBoundsToFitDisplay(bounds: electron.Rectangle, display: electron.Display): electron.Rectangle {
|
|
const { x: dx, y: dy, width: dWidth, height: dHeight } = display.workArea;
|
|
let { x, y, width, height } = bounds;
|
|
|
|
// Adjust width and height to fit within the display's work area
|
|
width = Math.min(width, dWidth);
|
|
height = Math.min(height, dHeight);
|
|
|
|
// Adjust x to ensure the window fits within the display
|
|
if (x < dx) {
|
|
x = dx;
|
|
} else if (x + width > dx + dWidth) {
|
|
x = dx + dWidth - width;
|
|
}
|
|
|
|
// Adjust y to ensure the window fits within the display
|
|
if (y < dy) {
|
|
y = dy;
|
|
} else if (y + height > dy + dHeight) {
|
|
y = dy + dHeight - height;
|
|
}
|
|
return { x, y, width, height };
|
|
}
|
|
|
|
export function ensureBoundsAreVisible(bounds: electron.Rectangle): electron.Rectangle {
|
|
if (!isWindowFullyVisible(bounds)) {
|
|
let targetDisplay = findDisplayWithMostArea(bounds);
|
|
|
|
if (!targetDisplay) {
|
|
targetDisplay = electron.screen.getPrimaryDisplay();
|
|
}
|
|
|
|
return adjustBoundsToFitDisplay(bounds, targetDisplay);
|
|
}
|
|
return bounds;
|
|
}
|