From 4b521b2845291fd52e594ff2c93e83d891bf1147 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Thu, 10 Mar 2022 23:23:51 -0600 Subject: [PATCH 01/15] Fix trailing whitespace From ec0f20b14fb257210d764bd77fba06974c7099d5 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 29 Jan 2022 19:27:49 -0600 Subject: [PATCH 02/15] Framework for custom theme --- src/ScriptEditor/ui/themes.ts | 179 ++++++++++++++++++++++++++++++++++ src/Settings/Settings.ts | 13 ++- 2 files changed, 191 insertions(+), 1 deletion(-) diff --git a/src/ScriptEditor/ui/themes.ts b/src/ScriptEditor/ui/themes.ts index 79a698727..da9bd672f 100644 --- a/src/ScriptEditor/ui/themes.ts +++ b/src/ScriptEditor/ui/themes.ts @@ -1,3 +1,182 @@ +export interface IScriptEditorTheme { + base: string; + inherit: boolean; + common: { + accent: string; + bg: string; + fg: string; + }; + syntax: { + tag: string; + entity: string; + string: string; + regexp: string; + markup: string; + keyword: string; + comment: string; + constant: string; + operator: string; + error: string; + }; + ui: { + line: string; + panel: { + bg: string; + shadow: string; + border: string; + }; + selection: { + bg: string; + }; + }; +} + +export const defaultMonacoTheme: IScriptEditorTheme = { + base: "vs-dark", + inherit: true, + common: { + accent: "b5cea8", + bg: "1E1E1E", + fg: "D4D4D4", + }, + syntax: { + tag: "569cd6", + entity: "569cd6", + string: "ce9178", + regexp: "646695", + markup: "569cd6", + keyword: "569cd6", + comment: "6A9955", + constant: "1E1E1E", + operator: "d4d4d4", + error: "f44747" + }, + ui: { + line: "1E1E1E", + panel: { + bg: "252526", + shadow: "252526", + border: "1E1E1E" + }, + selection: { + bg: "ADD6FF26" + } + } +} + +export function makeTheme(theme: IScriptEditorTheme): any { + const themeRules = [ + { + token: "", + background: theme.ui.line, + foreground: theme.common.fg + }, + { + token: "identifier", + foreground: theme.common.accent + }, + { + token: "operators", + foreground: theme.syntax.operator + }, + { + token: "keyword", + foreground: theme.syntax.keyword + }, + { + token: "string", + foreground: theme.syntax.string + }, + { + token: "string.escape", + foreground: theme.syntax.regexp + }, + { + token: "comment", + foreground: theme.syntax.comment + }, + { + token: "constant", + foreground: theme.syntax.constant + }, + { + token: "entity", + foreground: theme.syntax.entity + }, + { + token: "tag", + foreground: theme.syntax.tag + }, + { + token: "regexp", + foreground: theme.syntax.regexp + }, + { + token: "attribute", + foreground: theme.syntax.tag + }, + { + token: "constructor", + foreground: theme.syntax.markup + }, + { + token: "invalid", + foreground: theme.syntax.error + }, + { + token: "number", + foreground: theme.common.accent + }, + { + token: "delimiter", + foreground: theme.common.fg + "B3" + }, + // Custom tokens + { + token: "ns", + foreground: theme.syntax.tag + }, + { + token: "netscriptfunction", + foreground: theme.syntax.markup + }, + { + token: "otherkeywords", + foreground: theme.syntax.keyword + }, + { + token: "otherkeyvars", + foreground: theme.common.accent + }, + { + token: "this", + foreground: theme.syntax.tag + } + ]; + + const themeColors = Object.fromEntries([ + ["editor.background", theme.common.bg], + ["editor.foreground", theme.common.fg], + ["editor.lineHighlightBackground", theme.ui.line], + ["editor.selectionBackground", theme.ui.selection.bg], + + ["editorSuggestWidget.background", theme.ui.panel.bg], + ["editorSuggestWidget.border", theme.ui.panel.border], + ["editorSuggestWidget.selectedBackground", theme.ui.panel.shadow], + + ["editorHoverWidget.background", theme.ui.panel.bg], + ["editorHoverWidget.border", theme.ui.panel.border], + + ["editorWidget.background", theme.ui.panel.bg], + ["editorWidget.border", theme.ui.panel.border], + + ["input.background", theme.ui.panel.bg], + ["input.border", theme.ui.panel.border] + ].map(([k, v]) => [k, "#" + v])); + + return { base: theme.base, inherit: theme.inherit, rules: themeRules, colors: themeColors } +} + export async function loadThemes(monaco: { editor: any }): Promise { monaco.editor.defineTheme("monokai", { base: "vs-dark", diff --git a/src/Settings/Settings.ts b/src/Settings/Settings.ts index 906c9ca2d..202f49fcd 100644 --- a/src/Settings/Settings.ts +++ b/src/Settings/Settings.ts @@ -5,6 +5,7 @@ import { defaultStyles } from "../Themes/Styles"; import { WordWrapOptions } from "../ScriptEditor/ui/Options"; import { OverviewSettings } from "../ui/React/Overview"; import { IStyleSettings } from "../ScriptEditor/NetscriptDefinitions"; +import { defaultMonacoTheme, IScriptEditorTheme } from "../ScriptEditor/ui/themes"; /** * Represents the default settings the player could customize. @@ -157,6 +158,11 @@ interface IDefaultSettings { * If the game's sidebar is opened */ IsSidebarOpened: boolean; + + /** + * Script editor theme data + */ + EditorTheme: IScriptEditorTheme; } /** @@ -216,6 +222,8 @@ export const defaultSettings: IDefaultSettings = { theme: defaultTheme, styles: defaultStyles, overview: { x: 0, y: 0, opened: true }, + + EditorTheme: defaultMonacoTheme, }; /** @@ -258,10 +266,11 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = { MonacoFontSize: 20, MonacoVim: false, MonacoWordWrap: "off", - + theme: { ...defaultTheme }, styles: { ...defaultStyles }, overview: defaultSettings.overview, + EditorTheme: { ...defaultMonacoTheme }, init() { Object.assign(Settings, defaultSettings); }, @@ -273,6 +282,8 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = { delete save.styles; Object.assign(Settings.overview, save.overview); delete save.overview; + Object.assign(Settings.EditorTheme, save.EditorTheme); + delete save.EditorTheme; Object.assign(Settings, save); }, }; From 881d4816cc5374d0ad437b23a71fab26be24b119 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 29 Jan 2022 22:39:13 -0600 Subject: [PATCH 03/15] Theme attribute cleanup --- src/ScriptEditor/ui/themes.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/ScriptEditor/ui/themes.ts b/src/ScriptEditor/ui/themes.ts index da9bd672f..0ed7b9d6f 100644 --- a/src/ScriptEditor/ui/themes.ts +++ b/src/ScriptEditor/ui/themes.ts @@ -15,14 +15,13 @@ export interface IScriptEditorTheme { keyword: string; comment: string; constant: string; - operator: string; error: string; }; ui: { line: string; panel: { bg: string; - shadow: string; + selected: string; border: string; }; selection: { @@ -47,15 +46,14 @@ export const defaultMonacoTheme: IScriptEditorTheme = { markup: "569cd6", keyword: "569cd6", comment: "6A9955", - constant: "1E1E1E", - operator: "d4d4d4", + constant: "569cd6", error: "f44747" }, ui: { line: "1E1E1E", panel: { bg: "252526", - shadow: "252526", + selected: "252526", border: "1E1E1E" }, selection: { @@ -75,10 +73,6 @@ export function makeTheme(theme: IScriptEditorTheme): any { token: "identifier", foreground: theme.common.accent }, - { - token: "operators", - foreground: theme.syntax.operator - }, { token: "keyword", foreground: theme.syntax.keyword @@ -129,7 +123,7 @@ export function makeTheme(theme: IScriptEditorTheme): any { }, { token: "delimiter", - foreground: theme.common.fg + "B3" + foreground: theme.common.fg }, // Custom tokens { @@ -162,7 +156,7 @@ export function makeTheme(theme: IScriptEditorTheme): any { ["editorSuggestWidget.background", theme.ui.panel.bg], ["editorSuggestWidget.border", theme.ui.panel.border], - ["editorSuggestWidget.selectedBackground", theme.ui.panel.shadow], + ["editorSuggestWidget.selectedBackground", theme.ui.panel.selected], ["editorHoverWidget.background", theme.ui.panel.bg], ["editorHoverWidget.border", theme.ui.panel.border], From b3f37b25833191cb0ec038962b482c50b9d3ea0f Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 29 Jan 2022 23:11:21 -0600 Subject: [PATCH 04/15] Add types token --- src/ScriptEditor/ui/themes.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ScriptEditor/ui/themes.ts b/src/ScriptEditor/ui/themes.ts index 0ed7b9d6f..bc99bbada 100644 --- a/src/ScriptEditor/ui/themes.ts +++ b/src/ScriptEditor/ui/themes.ts @@ -97,6 +97,10 @@ export function makeTheme(theme: IScriptEditorTheme): any { token: "entity", foreground: theme.syntax.entity }, + { + token: "type", + foreground: theme.syntax.tag + }, { token: "tag", foreground: theme.syntax.tag From 16cf227498adb64741a289b0baa654d74038ce14 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 29 Jan 2022 23:17:57 -0600 Subject: [PATCH 05/15] Implement Monaco theme editor --- src/ScriptEditor/ui/OptionsModal.tsx | 15 +- src/ScriptEditor/ui/ScriptEditorRoot.tsx | 4 +- src/ScriptEditor/ui/ThemeEditorModal.tsx | 268 +++++++++++++++++++++++ 3 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 src/ScriptEditor/ui/ThemeEditorModal.tsx diff --git a/src/ScriptEditor/ui/OptionsModal.tsx b/src/ScriptEditor/ui/OptionsModal.tsx index 70fb9e361..65e12db1f 100644 --- a/src/ScriptEditor/ui/OptionsModal.tsx +++ b/src/ScriptEditor/ui/OptionsModal.tsx @@ -9,6 +9,10 @@ import Select from "@mui/material/Select"; import Switch from "@mui/material/Switch"; import MenuItem from "@mui/material/MenuItem"; import TextField from "@mui/material/TextField"; +import EditIcon from '@mui/icons-material/Edit'; +import SaveIcon from '@mui/icons-material/Save'; + +import { ThemeEditorModal } from "./ThemeEditorModal"; interface IProps { options: Options; @@ -23,6 +27,7 @@ export function OptionsModal(props: IProps): React.ReactElement { const [fontSize, setFontSize] = useState(props.options.fontSize); const [wordWrap, setWordWrap] = useState(props.options.wordWrap); const [vim, setVim] = useState(props.options.vim); + const [themeEditorOpen, setThemeEditorOpen] = useState(false); function save(): void { props.save({ @@ -43,6 +48,10 @@ export function OptionsModal(props: IProps): React.ReactElement { return ( + setThemeEditorOpen(false)} + /> Theme: + @@ -80,7 +93,7 @@ export function OptionsModal(props: IProps): React.ReactElement {
- +
); } diff --git a/src/ScriptEditor/ui/ScriptEditorRoot.tsx b/src/ScriptEditor/ui/ScriptEditorRoot.tsx index 1e3d529d9..b4579ea0b 100644 --- a/src/ScriptEditor/ui/ScriptEditorRoot.tsx +++ b/src/ScriptEditor/ui/ScriptEditorRoot.tsx @@ -25,7 +25,7 @@ import { Settings } from "../../Settings/Settings"; import { iTutorialNextStep, ITutorial, iTutorialSteps } from "../../InteractiveTutorial"; import { debounce } from "lodash"; import { saveObject } from "../../SaveObject"; -import { loadThemes } from "./themes"; +import { loadThemes, makeTheme } from "./themes"; import { GetServer } from "../../Server/AllServers"; import Button from "@mui/material/Button"; @@ -362,6 +362,7 @@ export function Root(props: IProps): React.ReactElement { monaco.languages.typescript.javascriptDefaults.addExtraLib(source, "netscript.d.ts"); monaco.languages.typescript.typescriptDefaults.addExtraLib(source, "netscript.d.ts"); loadThemes(monaco); + monaco.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme)); } // When the editor is mounted @@ -1002,6 +1003,7 @@ export function Root(props: IProps): React.ReactElement { vim: Settings.MonacoVim, }} save={(options: Options) => { + monacoRef.current?.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme)); setOptions(options); Settings.MonacoTheme = options.theme; Settings.MonacoInsertSpaces = options.insertSpaces; diff --git a/src/ScriptEditor/ui/ThemeEditorModal.tsx b/src/ScriptEditor/ui/ThemeEditorModal.tsx new file mode 100644 index 000000000..c3f105441 --- /dev/null +++ b/src/ScriptEditor/ui/ThemeEditorModal.tsx @@ -0,0 +1,268 @@ +import React, { useState } from "react"; +import { Modal } from "../../ui/React/Modal"; +import { defaultMonacoTheme } from "./themes"; + +import Typography from "@mui/material/Typography"; +import Button from "@mui/material/Button"; +import Box from "@mui/material/Box"; +import Tooltip from "@mui/material/Tooltip"; +import TextField from "@mui/material/TextField"; +import IconButton from "@mui/material/IconButton"; +import ReplyIcon from "@mui/icons-material/Reply"; +import HistoryIcon from '@mui/icons-material/History'; +import SaveIcon from '@mui/icons-material/Save'; + +import { Settings } from "../../Settings/Settings"; +import { OptionSwitch } from "../../ui/React/OptionSwitch"; +import _ from "lodash"; + +import { Color, ColorPicker } from "material-ui-color"; + +interface IProps { + onClose: () => void; + open: boolean; +} + +interface IColorEditorProps { + label: string; + themePath: string; + color: string | undefined; + onColorChange: (name: string, value: string) => void; + defaultColor: string; +} + +// Slightly tweaked version of the same function found in game options +function ColorEditor({ label, themePath, onColorChange, color, defaultColor }: IColorEditorProps): React.ReactElement { + if (color === undefined) { + console.error(`color ${themePath} was undefined, reverting to default`); + color = defaultColor; + } + + return ( + <> + + + + onColorChange(themePath, newColor.hex)} + disableAlpha + /> + + ), + endAdornment: ( + <> + onColorChange(themePath, defaultColor)}> + + + + ), + }} + /> + + + + ); +} + +export function ThemeEditorModal(props: IProps): React.ReactElement { + const setRerender = useState(false)[1]; + function rerender(): void { + setRerender((o) => !o); + } + + // Need to deep copy the object since it has nested attributes + const [themeCopy, setThemeCopy] = useState(JSON.parse(JSON.stringify(Settings.EditorTheme))); + + function onColorChange(name: string, value: string): void { + setThemeCopy(_.set(themeCopy, name, value)); + rerender(); + } + + function onThemeChange(event: React.ChangeEvent): void { + try { + const importedTheme = JSON.parse(event.target.value); + if (typeof importedTheme !== "object") return; + setThemeCopy(importedTheme); + } catch (err) { + // ignore + } + } + + return ( + + Customize Editor theme + Hover over input boxes for more information + { + setThemeCopy(_.set(themeCopy, "base", val ? "vs" : "vs-dark")); + rerender(); + }} + text="Use light theme as base" + tooltip={ + <> + If enabled, the vs light theme will be used as the + theme base, otherwise, vs-dark will be used. + + } + /> + + + UI + + + + + + + + + + Syntax + + + + + + + + + + + + + + + + + + + ) +} \ No newline at end of file From 10513ba5bcc83d24d1325d3ef586718e818715b5 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 29 Jan 2022 23:19:24 -0600 Subject: [PATCH 06/15] Update label for common.accent --- src/ScriptEditor/ui/ThemeEditorModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ScriptEditor/ui/ThemeEditorModal.tsx b/src/ScriptEditor/ui/ThemeEditorModal.tsx index c3f105441..1feb6dcbb 100644 --- a/src/ScriptEditor/ui/ThemeEditorModal.tsx +++ b/src/ScriptEditor/ui/ThemeEditorModal.tsx @@ -171,7 +171,7 @@ export function ThemeEditorModal(props: IProps): React.ReactElement { Syntax Date: Sat, 29 Jan 2022 23:19:58 -0600 Subject: [PATCH 07/15] Change global theme import rows to 10 and format --- src/Themes/ui/ThemeEditorModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Themes/ui/ThemeEditorModal.tsx b/src/Themes/ui/ThemeEditorModal.tsx index 602c45655..882b8d761 100644 --- a/src/Themes/ui/ThemeEditorModal.tsx +++ b/src/Themes/ui/ThemeEditorModal.tsx @@ -366,9 +366,9 @@ export function ThemeEditorModal(props: IProps): React.ReactElement { sx={{ mb: 1 }} multiline fullWidth - maxRows={3} + maxRows={10} label={"import / export theme"} - value={JSON.stringify(customTheme)} + value={JSON.stringify(customTheme, undefined, 2)} onChange={onThemeChange} /> <> From 6f60589779afebab6aed0908916f2e9c555e4a7c Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 29 Jan 2022 23:44:31 -0600 Subject: [PATCH 08/15] Tie up loose ends - Ensure that customTheme is redefined whenever the options modal is closed, regardless of saved or not - Reset the pending value for the theme editor whenever modal is clsoed --- src/ScriptEditor/ui/ScriptEditorRoot.tsx | 5 ++++- src/ScriptEditor/ui/ThemeEditorModal.tsx | 9 ++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/ScriptEditor/ui/ScriptEditorRoot.tsx b/src/ScriptEditor/ui/ScriptEditorRoot.tsx index b4579ea0b..da1da5b77 100644 --- a/src/ScriptEditor/ui/ScriptEditorRoot.tsx +++ b/src/ScriptEditor/ui/ScriptEditorRoot.tsx @@ -994,7 +994,10 @@ export function Root(props: IProps): React.ReactElement { setOptionsOpen(false)} + onClose={() => { + monacoRef.current?.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme)); + setOptionsOpen(false); + }} options={{ theme: Settings.MonacoTheme, insertSpaces: Settings.MonacoInsertSpaces, diff --git a/src/ScriptEditor/ui/ThemeEditorModal.tsx b/src/ScriptEditor/ui/ThemeEditorModal.tsx index 1feb6dcbb..bf979359d 100644 --- a/src/ScriptEditor/ui/ThemeEditorModal.tsx +++ b/src/ScriptEditor/ui/ThemeEditorModal.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { Modal } from "../../ui/React/Modal"; -import { defaultMonacoTheme } from "./themes"; +import { defaultMonacoTheme, IScriptEditorTheme } from "./themes"; import Typography from "@mui/material/Typography"; import Button from "@mui/material/Button"; @@ -80,7 +80,7 @@ export function ThemeEditorModal(props: IProps): React.ReactElement { } // Need to deep copy the object since it has nested attributes - const [themeCopy, setThemeCopy] = useState(JSON.parse(JSON.stringify(Settings.EditorTheme))); + const [themeCopy, setThemeCopy] = useState(JSON.parse(JSON.stringify(Settings.EditorTheme))); function onColorChange(name: string, value: string): void { setThemeCopy(_.set(themeCopy, name, value)); @@ -98,7 +98,10 @@ export function ThemeEditorModal(props: IProps): React.ReactElement { } return ( - + { + setThemeCopy(Settings.EditorTheme); + props.onClose(); + }}> Customize Editor theme Hover over input boxes for more information Date: Sat, 29 Jan 2022 23:54:36 -0600 Subject: [PATCH 09/15] Add Paper styling to modal --- src/ScriptEditor/ui/ThemeEditorModal.tsx | 325 ++++++++++++----------- 1 file changed, 165 insertions(+), 160 deletions(-) diff --git a/src/ScriptEditor/ui/ThemeEditorModal.tsx b/src/ScriptEditor/ui/ThemeEditorModal.tsx index bf979359d..327731414 100644 --- a/src/ScriptEditor/ui/ThemeEditorModal.tsx +++ b/src/ScriptEditor/ui/ThemeEditorModal.tsx @@ -7,6 +7,7 @@ import Button from "@mui/material/Button"; import Box from "@mui/material/Box"; import Tooltip from "@mui/material/Tooltip"; import TextField from "@mui/material/TextField"; +import Paper from "@mui/material/Paper"; import IconButton from "@mui/material/IconButton"; import ReplyIcon from "@mui/icons-material/Reply"; import HistoryIcon from '@mui/icons-material/History'; @@ -104,168 +105,172 @@ export function ThemeEditorModal(props: IProps): React.ReactElement { }}> Customize Editor theme Hover over input boxes for more information - { - setThemeCopy(_.set(themeCopy, "base", val ? "vs" : "vs-dark")); - rerender(); - }} - text="Use light theme as base" - tooltip={ - <> - If enabled, the vs light theme will be used as the - theme base, otherwise, vs-dark will be used. - - } - /> - - - UI - - - - - - - - - - Syntax - - - - - - - - - - - - - - - - - + text="Use light theme as base" + tooltip={ + <> + If enabled, the vs light theme will be used as the + theme base, otherwise, vs-dark will be used. + + } + /> + + + UI + + + + + + + + + + Syntax + + + + + + + + + + + + + + + + + + + + ) } \ No newline at end of file From d1edbe9a43a6aa5e69c2feeaa4e93595ae037659 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sun, 30 Jan 2022 10:38:58 -0600 Subject: [PATCH 10/15] Save data sanitization - Run in `Editor.beforeMount`, as well as when the options modal is closed - Recursively validates all token colors and replaces them with bright red if they're invalid --- src/ScriptEditor/ui/ScriptEditorRoot.tsx | 5 ++- src/ScriptEditor/ui/themes.ts | 52 ++++++++++++++++++++---- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/ScriptEditor/ui/ScriptEditorRoot.tsx b/src/ScriptEditor/ui/ScriptEditorRoot.tsx index da1da5b77..2dc20212b 100644 --- a/src/ScriptEditor/ui/ScriptEditorRoot.tsx +++ b/src/ScriptEditor/ui/ScriptEditorRoot.tsx @@ -25,7 +25,7 @@ import { Settings } from "../../Settings/Settings"; import { iTutorialNextStep, ITutorial, iTutorialSteps } from "../../InteractiveTutorial"; import { debounce } from "lodash"; import { saveObject } from "../../SaveObject"; -import { loadThemes, makeTheme } from "./themes"; +import { loadThemes, makeTheme, sanitizeTheme } from "./themes"; import { GetServer } from "../../Server/AllServers"; import Button from "@mui/material/Button"; @@ -362,6 +362,7 @@ export function Root(props: IProps): React.ReactElement { monaco.languages.typescript.javascriptDefaults.addExtraLib(source, "netscript.d.ts"); monaco.languages.typescript.typescriptDefaults.addExtraLib(source, "netscript.d.ts"); loadThemes(monaco); + sanitizeTheme(Settings.EditorTheme); monaco.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme)); } @@ -995,6 +996,7 @@ export function Root(props: IProps): React.ReactElement { { + sanitizeTheme(Settings.EditorTheme); monacoRef.current?.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme)); setOptionsOpen(false); }} @@ -1006,6 +1008,7 @@ export function Root(props: IProps): React.ReactElement { vim: Settings.MonacoVim, }} save={(options: Options) => { + sanitizeTheme(Settings.EditorTheme); monacoRef.current?.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme)); setOptions(options); Settings.MonacoTheme = options.theme; diff --git a/src/ScriptEditor/ui/themes.ts b/src/ScriptEditor/ui/themes.ts index bc99bbada..d9f35d100 100644 --- a/src/ScriptEditor/ui/themes.ts +++ b/src/ScriptEditor/ui/themes.ts @@ -1,12 +1,15 @@ export interface IScriptEditorTheme { + [key: string]: any; base: string; inherit: boolean; common: { + [key: string]: string; accent: string; bg: string; fg: string; }; syntax: { + [key: string]: string; tag: string; entity: string; string: string; @@ -18,13 +21,16 @@ export interface IScriptEditorTheme { error: string; }; ui: { + [key: string]: any; line: string; panel: { + [key: string]: string; bg: string; selected: string; border: string; }; selection: { + [key: string]: string; bg: string; }; }; @@ -34,20 +40,20 @@ export const defaultMonacoTheme: IScriptEditorTheme = { base: "vs-dark", inherit: true, common: { - accent: "b5cea8", + accent: "B5CEA8", bg: "1E1E1E", fg: "D4D4D4", }, syntax: { - tag: "569cd6", - entity: "569cd6", - string: "ce9178", + tag: "569CD6", + entity: "569CD6", + string: "CE9178", regexp: "646695", - markup: "569cd6", - keyword: "569cd6", + markup: "569CD6", + keyword: "569CD6", comment: "6A9955", - constant: "569cd6", - error: "f44747" + constant: "569CD6", + error: "F44747" }, ui: { line: "1E1E1E", @@ -62,6 +68,36 @@ 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 => { + for (const [k, v] of Object.entries(theme)) { + switch (k) { + 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 repairBlock = (block: { [key: string]: any }): void => { + for (const [k, v] of Object.entries(block)) { + if (typeof v === "object") { + repairBlock(v as { [key: string]: string }); + } else { + if (!v.match(colorRegExp)) block[k] = "FF0000"; + } + } + } + repairBlock(v); + } +} + export function makeTheme(theme: IScriptEditorTheme): any { const themeRules = [ { From 137539c1db3b9fb689de1489debe1c8c51756d13 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sun, 20 Feb 2022 09:03:16 -0600 Subject: [PATCH 11/15] Add missing newline at end of file --- src/ScriptEditor/ui/ThemeEditorModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ScriptEditor/ui/ThemeEditorModal.tsx b/src/ScriptEditor/ui/ThemeEditorModal.tsx index 327731414..0bf75ed03 100644 --- a/src/ScriptEditor/ui/ThemeEditorModal.tsx +++ b/src/ScriptEditor/ui/ThemeEditorModal.tsx @@ -273,4 +273,4 @@ export function ThemeEditorModal(props: IProps): React.ReactElement { ) -} \ No newline at end of file +} From 1f9414fd0e5d9051493d6669655276bd0fc71d26 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Thu, 10 Mar 2022 23:11:09 -0600 Subject: [PATCH 12/15] Resolve conflict --- src/ScriptEditor/ui/themes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ScriptEditor/ui/themes.ts b/src/ScriptEditor/ui/themes.ts index d9f35d100..f1eceed3b 100644 --- a/src/ScriptEditor/ui/themes.ts +++ b/src/ScriptEditor/ui/themes.ts @@ -474,6 +474,7 @@ export async function loadThemes(monaco: { editor: any }): Promise { foreground: "FFB86C", fontStyle: "italic", }, + { token: "netscriptfunction", foreground: "FF79C6", From 567fcf8fb63bc469ae61013cca511a76eb6f9086 Mon Sep 17 00:00:00 2001 From: nickofolas <60761231+nickofolas@users.noreply.github.com> Date: Wed, 13 Apr 2022 14:42:07 -0500 Subject: [PATCH 13/15] prettier --- src/ScriptEditor/ui/OptionsModal.tsx | 13 ++-- src/ScriptEditor/ui/ThemeEditorModal.tsx | 38 ++++++---- src/ScriptEditor/ui/themes.ts | 94 ++++++++++++------------ src/Settings/Settings.ts | 2 +- 4 files changed, 79 insertions(+), 68 deletions(-) diff --git a/src/ScriptEditor/ui/OptionsModal.tsx b/src/ScriptEditor/ui/OptionsModal.tsx index 65e12db1f..1e0d622f4 100644 --- a/src/ScriptEditor/ui/OptionsModal.tsx +++ b/src/ScriptEditor/ui/OptionsModal.tsx @@ -9,8 +9,8 @@ import Select from "@mui/material/Select"; import Switch from "@mui/material/Switch"; import MenuItem from "@mui/material/MenuItem"; import TextField from "@mui/material/TextField"; -import EditIcon from '@mui/icons-material/Edit'; -import SaveIcon from '@mui/icons-material/Save'; +import EditIcon from "@mui/icons-material/Edit"; +import SaveIcon from "@mui/icons-material/Save"; import { ThemeEditorModal } from "./ThemeEditorModal"; @@ -48,10 +48,7 @@ export function OptionsModal(props: IProps): React.ReactElement { return ( - setThemeEditorOpen(false)} - /> + setThemeEditorOpen(false)} /> Theme: