mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-03-01 03:51:59 +01:00
103 lines
3.4 KiB
TypeScript
103 lines
3.4 KiB
TypeScript
// Copyright 2025, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0s
|
|
|
|
import { getWebServerEndpoint } from "@/util/endpoints";
|
|
import { boundNumber, isBlank } from "@/util/util";
|
|
import { generate as generateCSS, parse as parseCSS, walk as walkCSS } from "css-tree";
|
|
|
|
function encodeFileURL(file: string) {
|
|
const webEndpoint = getWebServerEndpoint();
|
|
const fileUri = formatRemoteUri(file, "local");
|
|
const rtn = webEndpoint + `/wave/stream-file?path=${encodeURIComponent(fileUri)}&no404=1`;
|
|
return rtn;
|
|
}
|
|
|
|
export function processBackgroundUrls(cssText: string): string {
|
|
if (isBlank(cssText)) {
|
|
return null;
|
|
}
|
|
cssText = cssText.trim();
|
|
if (cssText.endsWith(";")) {
|
|
cssText = cssText.slice(0, -1);
|
|
}
|
|
const attrRe = /^background(-image)?\s*:\s*/i;
|
|
cssText = cssText.replace(attrRe, "");
|
|
const ast = parseCSS("background: " + cssText, {
|
|
context: "declaration",
|
|
});
|
|
let hasUnsafeUrl = false;
|
|
walkCSS(ast, {
|
|
visit: "Url",
|
|
enter(node) {
|
|
const originalUrl = node.value.trim();
|
|
if (
|
|
originalUrl.startsWith("http:") ||
|
|
originalUrl.startsWith("https:") ||
|
|
originalUrl.startsWith("data:")
|
|
) {
|
|
return;
|
|
}
|
|
// allow file:/// urls (if they are absolute)
|
|
if (originalUrl.startsWith("file://")) {
|
|
const path = originalUrl.slice(7);
|
|
if (!path.startsWith("/")) {
|
|
console.log(`Invalid background, contains a non-absolute file URL: ${originalUrl}`);
|
|
hasUnsafeUrl = true;
|
|
return;
|
|
}
|
|
const newUrl = encodeFileURL(path);
|
|
node.value = newUrl;
|
|
return;
|
|
}
|
|
// allow absolute paths
|
|
if (originalUrl.startsWith("/") || originalUrl.startsWith("~/") || /^[a-zA-Z]:(\/|\\)/.test(originalUrl)) {
|
|
const newUrl = encodeFileURL(originalUrl);
|
|
node.value = newUrl;
|
|
return;
|
|
}
|
|
hasUnsafeUrl = true;
|
|
console.log(`Invalid background, contains an unsafe URL scheme: ${originalUrl}`);
|
|
},
|
|
});
|
|
if (hasUnsafeUrl) {
|
|
return null;
|
|
}
|
|
const rtnStyle = generateCSS(ast);
|
|
if (rtnStyle == null) {
|
|
return null;
|
|
}
|
|
return rtnStyle.replace(/^background:\s*/, "");
|
|
}
|
|
|
|
export function computeBgStyleFromMeta(meta: MetaType, defaultOpacity: number = null): React.CSSProperties {
|
|
const bgAttr = meta?.["bg"];
|
|
if (isBlank(bgAttr)) {
|
|
return null;
|
|
}
|
|
try {
|
|
const processedBg = processBackgroundUrls(bgAttr);
|
|
const rtn: React.CSSProperties = {};
|
|
rtn.background = processedBg;
|
|
rtn.opacity = boundNumber(meta["bg:opacity"], 0, 1) ?? defaultOpacity;
|
|
if (!isBlank(meta?.["bg:blendmode"])) {
|
|
rtn.backgroundBlendMode = meta["bg:blendmode"];
|
|
}
|
|
return rtn;
|
|
} catch (e) {
|
|
console.error("error processing background", e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function formatRemoteUri(path: string, connection: string): string {
|
|
connection = connection ?? "local";
|
|
// TODO: We need a better way to handle s3 paths
|
|
let retVal: string;
|
|
if (connection.startsWith("aws:")) {
|
|
retVal = `${connection}:s3://${path ?? ""}`;
|
|
} else {
|
|
retVal = `wsh://${connection}/${path}`;
|
|
}
|
|
return retVal;
|
|
}
|