diff --git a/src/ScriptEditor/ui/OptionsModal.tsx b/src/ScriptEditor/ui/OptionsModal.tsx index 65e12db1f..70fb9e361 100644 --- a/src/ScriptEditor/ui/OptionsModal.tsx +++ b/src/ScriptEditor/ui/OptionsModal.tsx @@ -9,10 +9,6 @@ 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; @@ -27,7 +23,6 @@ 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({ @@ -48,10 +43,6 @@ export function OptionsModal(props: IProps): React.ReactElement { return ( - setThemeEditorOpen(false)} - /> Theme: - @@ -93,7 +80,7 @@ export function OptionsModal(props: IProps): React.ReactElement {
- +
); } diff --git a/src/ScriptEditor/ui/ScriptEditorRoot.tsx b/src/ScriptEditor/ui/ScriptEditorRoot.tsx index 2dc20212b..1e3d529d9 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, sanitizeTheme } from "./themes"; +import { loadThemes } from "./themes"; import { GetServer } from "../../Server/AllServers"; import Button from "@mui/material/Button"; @@ -362,8 +362,6 @@ 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)); } // When the editor is mounted @@ -995,11 +993,7 @@ export function Root(props: IProps): React.ReactElement { { - sanitizeTheme(Settings.EditorTheme); - monacoRef.current?.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme)); - setOptionsOpen(false); - }} + onClose={() => setOptionsOpen(false)} options={{ theme: Settings.MonacoTheme, insertSpaces: Settings.MonacoInsertSpaces, @@ -1008,8 +1002,6 @@ 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; Settings.MonacoInsertSpaces = options.insertSpaces; diff --git a/src/ScriptEditor/ui/ThemeEditorModal.tsx b/src/ScriptEditor/ui/ThemeEditorModal.tsx deleted file mode 100644 index 0bf75ed03..000000000 --- a/src/ScriptEditor/ui/ThemeEditorModal.tsx +++ /dev/null @@ -1,276 +0,0 @@ -import React, { useState } from "react"; -import { Modal } from "../../ui/React/Modal"; -import { defaultMonacoTheme, IScriptEditorTheme } 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 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'; -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 ( - { - setThemeCopy(Settings.EditorTheme); - props.onClose(); - }}> - 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 - - - - - - - - - - - - - - - - - - - - - - ) -} diff --git a/src/ScriptEditor/ui/themes.ts b/src/ScriptEditor/ui/themes.ts index d9f35d100..79a698727 100644 --- a/src/ScriptEditor/ui/themes.ts +++ b/src/ScriptEditor/ui/themes.ts @@ -1,216 +1,3 @@ -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; - regexp: string; - markup: string; - keyword: string; - comment: string; - constant: string; - error: string; - }; - ui: { - [key: string]: any; - line: string; - panel: { - [key: string]: string; - bg: string; - selected: string; - border: string; - }; - selection: { - [key: string]: string; - 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: "569CD6", - error: "F44747" - }, - ui: { - line: "1E1E1E", - panel: { - bg: "252526", - selected: "252526", - border: "1E1E1E" - }, - selection: { - bg: "ADD6FF26" - } - } -} - -// 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 = [ - { - token: "", - background: theme.ui.line, - foreground: theme.common.fg - }, - { - token: "identifier", - foreground: theme.common.accent - }, - { - 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: "type", - foreground: theme.syntax.tag - }, - { - 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 - }, - // 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.selected], - - ["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 c8d1cfd0c..906c9ca2d 100644 --- a/src/Settings/Settings.ts +++ b/src/Settings/Settings.ts @@ -5,7 +5,6 @@ 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. @@ -158,11 +157,6 @@ interface IDefaultSettings { * If the game's sidebar is opened */ IsSidebarOpened: boolean; - - /** - * Script editor theme data - */ - EditorTheme: IScriptEditorTheme; } /** @@ -222,8 +216,6 @@ export const defaultSettings: IDefaultSettings = { theme: defaultTheme, styles: defaultStyles, overview: { x: 0, y: 0, opened: true }, - - EditorTheme: defaultMonacoTheme, }; /** @@ -270,7 +262,6 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = { theme: { ...defaultTheme }, styles: { ...defaultStyles }, overview: defaultSettings.overview, - EditorTheme: { ...defaultMonacoTheme }, init() { Object.assign(Settings, defaultSettings); }, @@ -282,8 +273,6 @@ 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); }, }; diff --git a/src/Themes/ui/ThemeEditorModal.tsx b/src/Themes/ui/ThemeEditorModal.tsx index 882b8d761..602c45655 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={10} + maxRows={3} label={"import / export theme"} - value={JSON.stringify(customTheme, undefined, 2)} + value={JSON.stringify(customTheme)} onChange={onThemeChange} /> <>