mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-22 09:13:07 +02:00
CODEBASE: Validate theme, editor theme, and styles (#1789)
This commit is contained in:
+5
-1
@@ -9619,7 +9619,6 @@ interface InvestmentOffer {
|
||||
* @public
|
||||
*/
|
||||
interface UserInterfaceTheme {
|
||||
[key: string]: string | undefined;
|
||||
primarylight: string;
|
||||
primary: string;
|
||||
primarydark: string;
|
||||
@@ -9653,6 +9652,11 @@ interface UserInterfaceTheme {
|
||||
backgroundprimary: string;
|
||||
backgroundsecondary: string;
|
||||
button: string;
|
||||
maplocation: string;
|
||||
bnlvl0: string;
|
||||
bnlvl1: string;
|
||||
bnlvl2: string;
|
||||
bnlvl3: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ContentFilePath } from "../Paths/ContentFile";
|
||||
|
||||
import { EventEmitter } from "../utils/EventEmitter";
|
||||
import * as monaco from "monaco-editor";
|
||||
import { loadThemes, makeTheme, sanitizeTheme } from "./ui/themes";
|
||||
import { loadThemes, makeTheme } from "./ui/themes";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { NetscriptExtra } from "../NetscriptFunctions/Extra";
|
||||
import * as enums from "../Enums";
|
||||
@@ -132,7 +132,6 @@ export class ScriptEditor {
|
||||
});
|
||||
// Load themes
|
||||
loadThemes(monaco.editor.defineTheme);
|
||||
sanitizeTheme(Settings.EditorTheme);
|
||||
monaco.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { OptionSwitch } from "../../ui/React/OptionSwitch";
|
||||
|
||||
import { defaultMonacoTheme } from "./themes";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { assertAndSanitizeEditorTheme } from "../../JsonSchema/JSONSchemaAssertion";
|
||||
|
||||
type ColorEditorProps = {
|
||||
label: string;
|
||||
@@ -74,21 +75,18 @@ export function ThemeEditorModal(props: ThemeEditorProps): React.ReactElement {
|
||||
}
|
||||
|
||||
function onThemeChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
let themeData: unknown;
|
||||
try {
|
||||
const importedTheme = JSON.parse(event.target.value) as typeof Settings.EditorTheme;
|
||||
if (importedTheme == null) {
|
||||
throw new Error("Theme data must not be null or undefined.");
|
||||
}
|
||||
if (typeof importedTheme !== "object") {
|
||||
throw new Error(`Theme data is invalid.`);
|
||||
}
|
||||
Settings.EditorTheme = importedTheme;
|
||||
props.onChange();
|
||||
themeData = JSON.parse(event.target.value);
|
||||
assertAndSanitizeEditorTheme(themeData);
|
||||
} catch (error) {
|
||||
console.error(`Theme data is invalid. Data: ${event.target.value}.`);
|
||||
console.error(error);
|
||||
dialogBoxCreate(`Invalid theme. ${error}`);
|
||||
console.error("Theme data is invalid. Data:", event.target.value);
|
||||
dialogBoxCreate(`Invalid theme. Errors: ${error}.`);
|
||||
return;
|
||||
}
|
||||
Object.assign(Settings.EditorTheme, themeData);
|
||||
props.onChange();
|
||||
}
|
||||
|
||||
const onResetToDefault = () => {
|
||||
|
||||
@@ -12,7 +12,7 @@ import Typography from "@mui/material/Typography";
|
||||
|
||||
import SettingsIcon from "@mui/icons-material/Settings";
|
||||
|
||||
import { makeTheme, sanitizeTheme } from "./themes";
|
||||
import { makeTheme } from "./themes";
|
||||
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { Page } from "../../ui/Router";
|
||||
@@ -54,7 +54,6 @@ export function Toolbar({ editor, onSave }: IProps) {
|
||||
};
|
||||
|
||||
const onThemeChange = () => {
|
||||
sanitizeTheme(Settings.EditorTheme);
|
||||
monaco.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme));
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import type { editor } from "monaco-editor";
|
||||
import { getRecordKeys } from "../../Types/Record";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
type DefineThemeFn = typeof editor.defineTheme;
|
||||
|
||||
export const validEditorThemeBases = ["vs", "vs-dark", "hc-black", "hc-light"] as const;
|
||||
|
||||
/**
|
||||
* If we change this interface, we must change EditorThemeSchema.
|
||||
*/
|
||||
export interface IScriptEditorTheme {
|
||||
base: "vs" | "vs-dark" | "hc-black";
|
||||
base: (typeof validEditorThemeBases)[number];
|
||||
inherit: boolean;
|
||||
common: {
|
||||
accent: string;
|
||||
@@ -67,45 +70,6 @@ export const defaultMonacoTheme: IScriptEditorTheme = {
|
||||
},
|
||||
};
|
||||
|
||||
// Regex used for token color validation
|
||||
// https://github.com/microsoft/vscode/blob/973684056e67153952f495fce93bf50d0ec0b892/src/vs/editor/common/languages/supports/tokenization.ts#L153
|
||||
const colorRegExp = /^#?([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?$/;
|
||||
|
||||
// Recursively sanitize the theme data to prevent errors
|
||||
// Invalid data will be replaced with FF0000 (bright red)
|
||||
export const sanitizeTheme = (theme: IScriptEditorTheme): void => {
|
||||
if (typeof theme !== "object") {
|
||||
Settings.EditorTheme = structuredClone(defaultMonacoTheme);
|
||||
return;
|
||||
}
|
||||
for (const themeKey of getRecordKeys(theme)) {
|
||||
if (typeof theme[themeKey] !== "object") {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete theme[themeKey];
|
||||
}
|
||||
switch (themeKey) {
|
||||
case "base":
|
||||
if (!["vs-dark", "vs"].includes(theme.base)) theme.base = "vs-dark";
|
||||
continue;
|
||||
case "inherit":
|
||||
if (typeof theme.inherit !== "boolean") theme.inherit = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const block = theme[themeKey];
|
||||
const repairBlock = <T extends Record<string, unknown>>(block: T) => {
|
||||
for (const [blockKey, blockValue] of Object.entries(block) as [keyof T, unknown][]) {
|
||||
if (!blockValue || (typeof blockValue !== "string" && typeof blockValue !== "object"))
|
||||
(block[blockKey] as string) = "FF0000";
|
||||
else if (typeof blockValue === "object") repairBlock(blockValue as Record<string, unknown>);
|
||||
else if (!blockValue.match(colorRegExp)) (block[blockKey] as string) = "FF0000";
|
||||
}
|
||||
};
|
||||
// Type assertion is to something less specific.
|
||||
repairBlock(block);
|
||||
}
|
||||
};
|
||||
|
||||
export function makeTheme(theme: IScriptEditorTheme): editor.IStandaloneThemeData {
|
||||
const themeRules = [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user