diff --git a/jest.config.js b/jest.config.js index 2be488205..af66d7809 100644 --- a/jest.config.js +++ b/jest.config.js @@ -8,4 +8,8 @@ module.exports = { '.cypress', 'node_modules', 'dist', ], testEnvironment: "jsdom", + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/test/__mocks__/fileMock.js", + "\\.(css|less)$": "/test/__mocks__/styleMock.js" + } }; diff --git a/package.json b/package.json index d0b6701c3..6164663fa 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "electron": "^14.0.2", "electron-packager": "^15.4.0", "eslint": "^7.24.0", + "file-loader": "^6.2.0", "fork-ts-checker-webpack-plugin": "^6.3.3", "html-webpack-plugin": "^3.2.0", "http-server": "^13.0.1", diff --git a/package.sh b/package.sh index 5659c0016..1ea44de58 100755 --- a/package.sh +++ b/package.sh @@ -11,6 +11,7 @@ cp index.html .package cp -r electron/* .package cp -r dist/ext .package/dist cp -r dist/icons .package/dist +cp -r dist/images .package/dist # The css files cp dist/vendor.css .package/dist diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 2eed8c77e..8840ea1b8 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -1,2 +1,8 @@ // Defined by webpack on startup or compilation declare let __COMMIT_HASH__: string; + +// When using file-loader, we'll get a path to the resource +declare module "*.png" { + const value: string; + export default value; +} diff --git a/src/NetscriptFunctions/UserInterface.ts b/src/NetscriptFunctions/UserInterface.ts index 55827c334..7b342a9a7 100644 --- a/src/NetscriptFunctions/UserInterface.ts +++ b/src/NetscriptFunctions/UserInterface.ts @@ -4,9 +4,9 @@ import { IPlayer } from "../PersonObjects/IPlayer"; import { getRamCost } from "../Netscript/RamCostGenerator"; import { GameInfo, IStyleSettings, UserInterface as IUserInterface, UserInterfaceTheme } from "../ScriptEditor/NetscriptDefinitions"; import { Settings } from "../Settings/Settings"; -import { ThemeEvents } from "../ui/React/Theme"; -import { defaultTheme } from "../Settings/Themes"; -import { defaultStyles } from "../Settings/Styles"; +import { ThemeEvents } from "../Themes/ui/Theme"; +import { defaultTheme } from "../Themes/Themes"; +import { defaultStyles } from "../Themes/Styles"; import { CONSTANTS } from "../Constants"; import { hash } from "../hash/hash"; diff --git a/src/Settings/Settings.ts b/src/Settings/Settings.ts index ddb18d8c1..77c29fdb9 100644 --- a/src/Settings/Settings.ts +++ b/src/Settings/Settings.ts @@ -1,7 +1,7 @@ import { ISelfInitializer, ISelfLoading } from "../types"; import { OwnedAugmentationsOrderSetting, PurchaseAugmentationsOrderSetting } from "./SettingEnums"; -import { defaultTheme, ITheme } from "./Themes"; -import { defaultStyles } from "./Styles"; +import { defaultTheme, ITheme } from "../Themes/Themes"; +import { defaultStyles } from "../Themes/Styles"; import { WordWrapOptions } from "../ScriptEditor/ui/Options"; import { OverviewSettings } from "../ui/React/Overview"; import { IStyleSettings } from "../ScriptEditor/NetscriptDefinitions"; diff --git a/src/Settings/Themes.ts b/src/Settings/Themes.ts deleted file mode 100644 index eed026a57..000000000 --- a/src/Settings/Themes.ts +++ /dev/null @@ -1,613 +0,0 @@ -import { IMap } from "../types"; - -export interface ITheme { - [key: string]: string | undefined; - primarylight: string; - primary: string; - primarydark: string; - successlight: string; - success: string; - successdark: string; - errorlight: string; - error: string; - errordark: string; - secondarylight: string; - secondary: string; - secondarydark: string; - warninglight: string; - warning: string; - warningdark: string; - infolight: string; - info: string; - infodark: string; - welllight: string; - well: string; - white: string; - black: string; - hp: string; - money: string; - hack: string; - combat: string; - cha: string; - int: string; - rep: string; - disabled: string; - backgroundprimary: string; - backgroundsecondary: string; - button: string; -} - -export interface IPredefinedTheme { - colors: ITheme; - name?: string; - credit?: string; - description?: string; - reference?: string; -} - -export const defaultTheme: ITheme = { - primarylight: "#0f0", - primary: "#0c0", - primarydark: "#090", - successlight: "#0f0", - success: "#0c0", - successdark: "#090", - errorlight: "#f00", - error: "#c00", - errordark: "#900", - secondarylight: "#AAA", - secondary: "#888", - secondarydark: "#666", - warninglight: "#ff0", - warning: "#cc0", - warningdark: "#990", - infolight: "#69f", - info: "#36c", - infodark: "#039", - welllight: "#444", - well: "#222", - white: "#fff", - black: "#000", - hp: "#dd3434", - money: "#ffd700", - hack: "#adff2f", - combat: "#faffdf", - cha: "#a671d1", - int: "#6495ed", - rep: "#faffdf", - disabled: "#66cfbc", - backgroundprimary: "#000", - backgroundsecondary: "#000", - button: "#333", -}; - -export const getPredefinedThemes = (): IMap => ({ - Default: { - colors: defaultTheme, - }, - Monokai: { - description: "Monokai'ish", - colors: { - primarylight: "#FFF", - primary: "#F8F8F2", - primarydark: "#FAFAEB", - successlight: "#ADE146", - success: "#A6E22E", - successdark: "#98E104", - errorlight: "#FF69A0", - error: "#F92672", - errordark: "#D10F56", - secondarylight: "#AAA", - secondary: "#888", - secondarydark: "#666", - warninglight: "#E1D992", - warning: "#E6DB74", - warningdark: "#EDDD54", - infolight: "#92E1F1", - info: "#66D9EF", - infodark: "#31CDED", - welllight: "#444", - well: "#222", - white: "#fff", - black: "#000", - hp: "#F92672", - money: "#E6DB74", - hack: "#A6E22E", - combat: "#75715E", - cha: "#AE81FF", - int: "#66D9EF", - rep: "#E69F66", - disabled: "#66cfbc", - backgroundprimary: "#272822", - backgroundsecondary: "#1B1C18", - button: "#333", - }, - }, - - Warmer: { - credit: "hexnaught", - description: "Warmer, softer theme", - reference: "https://discord.com/channels/415207508303544321/921991895230611466/921999581020028938", - colors: { - primarylight: "#EA9062", - primary: "#DD7B4A", - primarydark: "#D3591C", - successlight: "#6ACF6A", - success: "#43BF43", - successdark: "#3E913E", - errorlight: "#C15757", - error: "#B34141", - errordark: "#752525", - secondarylight: "#AAA", - secondary: "#888", - secondarydark: "#666", - warninglight: "#E6E69D", - warning: "#DADA56", - warningdark: "#A1A106", - infolight: "#69f", - info: "#36c", - infodark: "#039", - welllight: "#444", - well: "#222", - white: "#fff", - black: "#000", - hp: "#dd3434", - money: "#ffd700", - hack: "#adff2f", - combat: "#faffdf", - cha: "#AD84CF", - int: "#6495ed", - rep: "#faffdf", - disabled: "#76C6B7", - backgroundprimary: "#000", - backgroundsecondary: "#000", - button: "#333", - }, - }, - - "Dark+": { - credit: "LoganMD", - description: "VSCode Dark+", - reference: "https://discord.com/channels/415207508303544321/921991895230611466/921999975867617310", - colors: { - primarylight: "#E0E0BC", - primary: "#CCCCAE", - primarydark: "#B8B89C", - successlight: "#00F000", - success: "#00D200", - successdark: "#00B400", - errorlight: "#F00000", - error: "#C80000", - errordark: "#A00000", - secondarylight: "#B4AEAE", - secondary: "#969090", - secondarydark: "#787272", - warninglight: "#F0F000", - warning: "#C8C800", - warningdark: "#A0A000", - infolight: "#69f", - info: "#36c", - infodark: "#039", - welllight: "#444", - well: "#222", - white: "#fff", - black: "#1E1E1E", - hp: "#dd3434", - money: "#ffd700", - hack: "#adff2f", - combat: "#faffdf", - cha: "#a671d1", - int: "#6495ed", - rep: "#faffdf", - disabled: "#66cfbc", - backgroundprimary: "#1E1E1E", - backgroundsecondary: "#252525", - button: "#333", - }, - }, - - "Mayukai Dark": { - credit: "Festive Noire", - description: "Mayukai Dark-esque", - reference: "https://discord.com/channels/415207508303544321/921991895230611466/922037502334889994", - colors: { - primarylight: "#DDDFC5", - primary: "#CDCFB6", - primarydark: "#9D9F8C", - successlight: "#00EF00", - success: "#00A500", - successdark: "#007A00", - errorlight: "#F92672", - error: "#CA1C5C", - errordark: "#90274A", - secondarylight: "#AAA", - secondary: "#888", - secondarydark: "#666", - warninglight: "#D3D300", - warning: "#cc0", - warningdark: "#990", - infolight: "#69f", - info: "#36c", - infodark: "#039", - welllight: "#444", - well: "#00010A", - white: "#fff", - black: "#020509", - hp: "#dd3434", - money: "#ffd700", - hack: "#8CCF27", - combat: "#faffdf", - cha: "#a671d1", - int: "#6495ed", - rep: "#faffdf", - disabled: "#66cfbc", - backgroundprimary: "#080C11", - backgroundsecondary: "#03080F", - button: "#00010A", - }, - }, - - Purple: { - credit: "zer0ney", - description: "Essentially all defaults except for purple replacing the main colors", - reference: "https://discord.com/channels/415207508303544321/921991895230611466/922091815849570395", - colors: { - primarylight: "#BA55D3", - primary: "#9370DB", - primarydark: "#8A2BE2", - successlight: "#BA55D3", - success: "#9370DB", - successdark: "#8A2BE2", - errorlight: "#f00", - error: "#c00", - errordark: "#900", - secondarylight: "#AAA", - secondary: "#888", - secondarydark: "#666", - warninglight: "#ff0", - warning: "#cc0", - warningdark: "#990", - infolight: "#69f", - info: "#36c", - infodark: "#039", - welllight: "#444", - well: "#222", - white: "#fff", - black: "#000", - hp: "#dd3434", - money: "#ffd700", - hack: "#adff2f", - combat: "#faffdf", - cha: "#a671d1", - int: "#6495ed", - rep: "#faffdf", - disabled: "#66cfbc", - backgroundprimary: "#000", - backgroundsecondary: "#000", - button: "#333", - }, - }, - - "Smooth Green": { - credit: "Swidt", - description: "A nice green theme that doesn't hurt your eyes.", - reference: "https://discord.com/channels/415207508303544321/921991895230611466/922243957986033725", - colors: { - primarylight: "#E0E0BC", - primary: "#B0D9A3", - primarydark: "#B8B89C", - successlight: "#00F000", - success: "#6BC16B", - successdark: "#00B400", - errorlight: "#F00000", - error: "#3D713D", - errordark: "#A00000", - secondarylight: "#B4AEAE", - secondary: "#8FAF85", - secondarydark: "#787272", - warninglight: "#F0F000", - warning: "#38F100", - warningdark: "#A0A000", - infolight: "#69f", - info: "#36c", - infodark: "#039", - welllight: "#444", - well: "#2F3C2B", - white: "#fff", - black: "#1E1E1E", - hp: "#dd3434", - money: "#4AA52E", - hack: "#adff2f", - combat: "#faffdf", - cha: "#a671d1", - int: "#6495ed", - rep: "#35A135", - disabled: "#66cfbc", - backgroundprimary: "#1E1E1E", - backgroundsecondary: "#252525", - button: "#2F3C2B", - }, - }, - - Dracula: { - credit: "H3draut3r", - reference: "https://discord.com/channels/415207508303544321/921991895230611466/922296307836678144", - colors: { - primarylight: "#7082B8", - primary: "#F8F8F2", - primarydark: "#FF79C6", - successlight: "#0f0", - success: "#0c0", - successdark: "#090", - errorlight: "#FD4545", - error: "#FF2D2D", - errordark: "#C62424", - secondarylight: "#AAA", - secondary: "#8BE9FD", - secondarydark: "#666", - warninglight: "#FFC281", - warning: "#FFB86C", - warningdark: "#E6A055", - infolight: "#A0A0FF", - info: "#7070FF", - infodark: "#4040FF", - welllight: "#44475A", - well: "#363948", - white: "#fff", - black: "#282A36", - hp: "#D34448", - money: "#50FA7B", - hack: "#F1FA8C", - combat: "#BD93F9", - cha: "#FF79C6", - int: "#6495ed", - rep: "#faffdf", - disabled: "#66cfbc", - backgroundprimary: "#282A36", - backgroundsecondary: "#21222C", - button: "#21222C", - }, - }, - - "Dark Blue": { - credit: "Saynt_Garmo", - reference: "https://discord.com/channels/415207508303544321/921991895230611466/923084732718264340", - colors: { - primarylight: "#023DDE", - primary: "#4A41C8", - primarydark: "#005299", - successlight: "#00FF00", - success: "#D1DAD1", - successdark: "#BFCABF", - errorlight: "#f00", - error: "#c00", - errordark: "#900", - secondarylight: "#AAA", - secondary: "#888", - secondarydark: "#666", - warninglight: "#ff0", - warning: "#cc0", - warningdark: "#990", - infolight: "#69f", - info: "#36c", - infodark: "#039", - welllight: "#444", - well: "#040505", - white: "#fff", - black: "#000000", - hp: "#dd3434", - money: "#ffd700", - hack: "#adff2f", - combat: "#faffdf", - cha: "#a671d1", - int: "#6495ed", - rep: "#faffdf", - disabled: "#66cfbc", - backgroundprimary: "#091419", - backgroundsecondary: "#000000", - button: "#000000", - }, - }, - - Discord: { - credit: "Thermite", - description: "Discord inspired theme", - reference: "https://discord.com/channels/415207508303544321/921991895230611466/924305252017143818", - colors: { - primarylight: "#7389DC", - primary: "#7389DC", - primarydark: "#5964F1", - successlight: "#00CC00", - success: "#20DF20", - successdark: "#0CB80C", - errorlight: "#EA5558", - error: "#EC4145", - errordark: "#E82528", - secondarylight: "#C3C3C3", - secondary: "#9C9C9C", - secondarydark: "#4E4E4E", - warninglight: "#ff0", - warning: "#cc0", - warningdark: "#990", - infolight: "#69f", - info: "#36c", - infodark: "#1C4FB3", - welllight: "#999999", - well: "#35383C", - white: "#FFFFFF", - black: "#202225", - hp: "#FF5656", - money: "#43FF43", - hack: "#FFAB3D", - combat: "#8A90FD", - cha: "#FF51D9", - int: "#6495ed", - rep: "#FFFF30", - disabled: "#474B51", - backgroundprimary: "#2F3136", - backgroundsecondary: "#35393E", - button: "#333", - }, - }, - - "One Dark": { - credit: "Dexalt142", - reference: "https://discord.com/channels/415207508303544321/921991895230611466/924650660694208512", - colors: { - primarylight: "#98C379", - primary: "#98C379", - primarydark: "#98C379", - successlight: "#98C379", - success: "#98C379", - successdark: "#98C379", - errorlight: "#E06C75", - error: "#BE5046", - errordark: "#BE5046", - secondarylight: "#AAA", - secondary: "#888", - secondarydark: "#666", - warninglight: "#E5C07B", - warning: "#E5C07B", - warningdark: "#D19A66", - infolight: "#61AFEF", - info: "#61AFEF", - infodark: "#61AFEF", - welllight: "#4B5263", - well: "#282C34", - white: "#ABB2BF", - black: "#282C34", - hp: "#E06C75", - money: "#E5C07B", - hack: "#98C379", - combat: "#ABB2BF", - cha: "#C678DD", - int: "#61AFEF", - rep: "#ABB2BF", - disabled: "#56B6C2", - backgroundprimary: "#282C34", - backgroundsecondary: "#21252B", - button: "#4B5263", - }, - }, - - "Muted Gold & Blue": { - credit: "Sloth", - reference: "https://discord.com/channels/415207508303544321/921991895230611466/924672660758208563", - colors: { - primarylight: "#E3B54A", - primary: "#CAA243", - primarydark: "#7E6937", - successlight: "#82FF82", - success: "#6FDA6F", - successdark: "#64C364", - errorlight: "#FD5555", - error: "#D84A4A", - errordark: "#AC3939", - secondarylight: "#D8D0B8", - secondary: "#B1AA95", - secondarydark: "#736E5E", - warninglight: "#ff0", - warning: "#cc0", - warningdark: "#990", - infolight: "#69f", - info: "#36c", - infodark: "#039", - welllight: "#444", - well: "#111111", - white: "#fff", - black: "#070300", - hp: "#dd3434", - money: "#ffd700", - hack: "#adff2f", - combat: "#faffdf", - cha: "#a671d1", - int: "#6495ed", - rep: "#faffdf", - disabled: "#66cfbc", - backgroundprimary: "#0A0A0E", - backgroundsecondary: "#0E0E10", - button: "#222222", - }, - }, - - "Default Lite": { - credit: "NmuGmu", - description: "Less eye-straining default theme", - reference: "https://discord.com/channels/415207508303544321/921991895230611466/925263801564151888", - colors: { - primarylight: "#28CF28", - primary: "#21A821", - primarydark: "#177317", - successlight: "#1CFF1C", - success: "#16CA16", - successdark: "#0D910D", - errorlight: "#FF3B3B", - error: "#C32D2D", - errordark: "#8E2121", - secondarylight: "#B3B3B3", - secondary: "#838383", - secondarydark: "#676767", - warninglight: "#FFFF3A", - warning: "#C3C32A", - warningdark: "#8C8C1E", - infolight: "#64CBFF", - info: "#3399CC", - infodark: "#246D91", - welllight: "#404040", - well: "#1C1C1C", - white: "#C3C3C3", - black: "#0A0B0B", - hp: "#C62E2E", - money: "#D6BB27", - hack: "#ADFF2F", - combat: "#E8EDCD", - cha: "#8B5FAF", - int: "#537CC8", - rep: "#E8EDCD", - disabled: "#5AB5A5", - backgroundprimary: "#0C0D0E", - backgroundsecondary: "#121415", - button: "#252829", - }, - }, - - Light: { - credit: "matt", - reference: "https://discord.com/channels/415207508303544321/921991895230611466/926114005456658432", - colors: { - primarylight: "#535353", - primary: "#1A1A1A", - primarydark: "#0d0d0d", - successlight: "#63c439", - success: "#428226", - successdark: "#2E5A1B", - errorlight: "#df7051", - error: "#C94824", - errordark: "#91341B", - secondarylight: "#b3b3b3", - secondary: "#9B9B9B", - secondarydark: "#7A7979", - warninglight: "#e8d464", - warning: "#C6AD20", - warningdark: "#9F8A16", - infolight: "#6299cf", - info: "#3778B7", - infodark: "#30689C", - welllight: "#f9f9f9", - well: "#eaeaea", - white: "#F7F7F7", - black: "#F7F7F7", - hp: "#BF5C41", - money: "#E1B121", - hack: "#47BC38", - combat: "#656262", - cha: "#A568AC", - int: "#889BCF", - rep: "#656262", - disabled: "#70B4BF", - backgroundprimary: "#F7F7F7", - backgroundsecondary: "#f9f9f9", - button: "#eaeaea", - }, - }, -}); diff --git a/src/Themes/README.md b/src/Themes/README.md new file mode 100644 index 000000000..3dffe710d --- /dev/null +++ b/src/Themes/README.md @@ -0,0 +1,18 @@ +# Themes + +Feel free to contribute a new theme by submitting a pull request to the game! + +See [CONTRIBUTING.md](/CONTRIBUTING.md) for details. + +## How create a new theme + +1. Duplicate one of the folders in `/src/Themes/data` and give it a new name (keep the hyphenated format) +2. Modify the data in the new `/src/Themes/data/new-folder/index.ts` file +3. Replace the screenshot.png with one of your theme +4. Add the import/export into the `/src/Themes/data/index.ts` file + +The themes are ordered according to the export order in `index.ts` + +## Other resources + +There is an external script called `theme-browser` which may include more themes than those shown here. Head over the [bitpacker](https://github.com/davidsiems/bitpacker) repository for details. diff --git a/src/Settings/Styles.ts b/src/Themes/Styles.ts similarity index 100% rename from src/Settings/Styles.ts rename to src/Themes/Styles.ts diff --git a/src/Themes/Themes.ts b/src/Themes/Themes.ts new file mode 100644 index 000000000..847c6fb3e --- /dev/null +++ b/src/Themes/Themes.ts @@ -0,0 +1,56 @@ +import { IMap } from "../types"; +import * as predefined from "./data"; + +export interface ITheme { + [key: string]: string | undefined; + primarylight: string; + primary: string; + primarydark: string; + successlight: string; + success: string; + successdark: string; + errorlight: string; + error: string; + errordark: string; + secondarylight: string; + secondary: string; + secondarydark: string; + warninglight: string; + warning: string; + warningdark: string; + infolight: string; + info: string; + infodark: string; + welllight: string; + well: string; + white: string; + black: string; + hp: string; + money: string; + hack: string; + combat: string; + cha: string; + int: string; + rep: string; + disabled: string; + backgroundprimary: string; + backgroundsecondary: string; + button: string; +} + +export interface IPredefinedTheme { + colors: ITheme; + name: string; + credit: string; + screenshot: string; + description: string; + reference?: string; +} + +export const defaultTheme: ITheme = { + ...predefined.Default.colors, +}; + +export const getPredefinedThemes = (): IMap => ({ + ...predefined, +}); diff --git a/src/Themes/data/dark-blue/index.ts b/src/Themes/data/dark-blue/index.ts new file mode 100644 index 000000000..b6cf0a623 --- /dev/null +++ b/src/Themes/data/dark-blue/index.ts @@ -0,0 +1,45 @@ +import { IPredefinedTheme } from "../../Themes"; +import img1 from "./screenshot.png"; + +export const Theme: IPredefinedTheme = { + name: "Dark Blue", + description: "Very dark with a blue/purplelly primary", + credit: "Saynt_Garmo", + reference: "https://discord.com/channels/415207508303544321/921991895230611466/923084732718264340", + screenshot: img1, + colors: { + primarylight: "#023DDE", + primary: "#4A41C8", + primarydark: "#005299", + successlight: "#00FF00", + success: "#D1DAD1", + successdark: "#BFCABF", + errorlight: "#f00", + error: "#c00", + errordark: "#900", + secondarylight: "#AAA", + secondary: "#888", + secondarydark: "#666", + warninglight: "#ff0", + warning: "#cc0", + warningdark: "#990", + infolight: "#69f", + info: "#36c", + infodark: "#039", + welllight: "#444", + well: "#040505", + white: "#fff", + black: "#000000", + hp: "#dd3434", + money: "#ffd700", + hack: "#adff2f", + combat: "#faffdf", + cha: "#a671d1", + int: "#6495ed", + rep: "#faffdf", + disabled: "#66cfbc", + backgroundprimary: "#091419", + backgroundsecondary: "#000000", + button: "#000000", + }, +}; diff --git a/src/Themes/data/dark-blue/screenshot.png b/src/Themes/data/dark-blue/screenshot.png new file mode 100644 index 000000000..9233080f3 Binary files /dev/null and b/src/Themes/data/dark-blue/screenshot.png differ diff --git a/src/Themes/data/dark-plus/index.ts b/src/Themes/data/dark-plus/index.ts new file mode 100644 index 000000000..84ebb6dfe --- /dev/null +++ b/src/Themes/data/dark-plus/index.ts @@ -0,0 +1,45 @@ +import { IPredefinedTheme } from "../../Themes"; +import img1 from "./screenshot.png"; + +export const Theme: IPredefinedTheme = { + name: "Dark+", + credit: "LoganMD", + description: "VSCode Dark+", + reference: "https://discord.com/channels/415207508303544321/921991895230611466/921999975867617310", + screenshot: img1, + colors: { + primarylight: "#E0E0BC", + primary: "#CCCCAE", + primarydark: "#B8B89C", + successlight: "#00F000", + success: "#00D200", + successdark: "#00B400", + errorlight: "#F00000", + error: "#C80000", + errordark: "#A00000", + secondarylight: "#B4AEAE", + secondary: "#969090", + secondarydark: "#787272", + warninglight: "#F0F000", + warning: "#C8C800", + warningdark: "#A0A000", + infolight: "#69f", + info: "#36c", + infodark: "#039", + welllight: "#444", + well: "#222", + white: "#fff", + black: "#1E1E1E", + hp: "#dd3434", + money: "#ffd700", + hack: "#adff2f", + combat: "#faffdf", + cha: "#a671d1", + int: "#6495ed", + rep: "#faffdf", + disabled: "#66cfbc", + backgroundprimary: "#1E1E1E", + backgroundsecondary: "#252525", + button: "#333", + }, +}; diff --git a/src/Themes/data/dark-plus/screenshot.png b/src/Themes/data/dark-plus/screenshot.png new file mode 100644 index 000000000..44324a041 Binary files /dev/null and b/src/Themes/data/dark-plus/screenshot.png differ diff --git a/src/Themes/data/default-lite/index.ts b/src/Themes/data/default-lite/index.ts new file mode 100644 index 000000000..788428186 --- /dev/null +++ b/src/Themes/data/default-lite/index.ts @@ -0,0 +1,45 @@ +import { IPredefinedTheme } from "../../Themes"; +import img1 from "./screenshot.png"; + +export const Theme: IPredefinedTheme = { + name: "Default-lite", + description: "Less eye-straining default theme", + credit: "NmuGmu", + reference: "https://discord.com/channels/415207508303544321/921991895230611466/925263801564151888", + screenshot: img1, + colors: { + primarylight: "#28CF28", + primary: "#21A821", + primarydark: "#177317", + successlight: "#1CFF1C", + success: "#16CA16", + successdark: "#0D910D", + errorlight: "#FF3B3B", + error: "#C32D2D", + errordark: "#8E2121", + secondarylight: "#B3B3B3", + secondary: "#838383", + secondarydark: "#676767", + warninglight: "#FFFF3A", + warning: "#C3C32A", + warningdark: "#8C8C1E", + infolight: "#64CBFF", + info: "#3399CC", + infodark: "#246D91", + welllight: "#404040", + well: "#1C1C1C", + white: "#C3C3C3", + black: "#0A0B0B", + hp: "#C62E2E", + money: "#D6BB27", + hack: "#ADFF2F", + combat: "#E8EDCD", + cha: "#8B5FAF", + int: "#537CC8", + rep: "#E8EDCD", + disabled: "#5AB5A5", + backgroundprimary: "#0C0D0E", + backgroundsecondary: "#121415", + button: "#252829", + }, +}; diff --git a/src/Themes/data/default-lite/screenshot.png b/src/Themes/data/default-lite/screenshot.png new file mode 100644 index 000000000..e9c348227 Binary files /dev/null and b/src/Themes/data/default-lite/screenshot.png differ diff --git a/src/Themes/data/default/index.ts b/src/Themes/data/default/index.ts new file mode 100644 index 000000000..723a14724 --- /dev/null +++ b/src/Themes/data/default/index.ts @@ -0,0 +1,44 @@ +import { IPredefinedTheme } from "../../Themes"; +import img1 from "./screenshot.png"; + +export const Theme: IPredefinedTheme = { + name: 'Default', + description: 'Default game theme, most supported', + credit: 'hydroflame', + screenshot: img1, + colors: { + primarylight: "#0f0", + primary: "#0c0", + primarydark: "#090", + successlight: "#0f0", + success: "#0c0", + successdark: "#090", + errorlight: "#f00", + error: "#c00", + errordark: "#900", + secondarylight: "#AAA", + secondary: "#888", + secondarydark: "#666", + warninglight: "#ff0", + warning: "#cc0", + warningdark: "#990", + infolight: "#69f", + info: "#36c", + infodark: "#039", + welllight: "#444", + well: "#222", + white: "#fff", + black: "#000", + hp: "#dd3434", + money: "#ffd700", + hack: "#adff2f", + combat: "#faffdf", + cha: "#a671d1", + int: "#6495ed", + rep: "#faffdf", + disabled: "#66cfbc", + backgroundprimary: "#000", + backgroundsecondary: "#000", + button: "#333", + }, +}; diff --git a/src/Themes/data/default/screenshot.png b/src/Themes/data/default/screenshot.png new file mode 100644 index 000000000..bc32b47aa Binary files /dev/null and b/src/Themes/data/default/screenshot.png differ diff --git a/src/Themes/data/discord-like/index.ts b/src/Themes/data/discord-like/index.ts new file mode 100644 index 000000000..0962d83a8 --- /dev/null +++ b/src/Themes/data/discord-like/index.ts @@ -0,0 +1,45 @@ +import { IPredefinedTheme } from "../../Themes"; +import img1 from "./screenshot.png"; + +export const Theme: IPredefinedTheme = { + name: "Discord-like", + description: "Discord inspired theme", + credit: "Thermite", + reference: "https://discord.com/channels/415207508303544321/921991895230611466/924305252017143818", + screenshot: img1, + colors: { + primarylight: "#7389DC", + primary: "#7389DC", + primarydark: "#5964F1", + successlight: "#00CC00", + success: "#20DF20", + successdark: "#0CB80C", + errorlight: "#EA5558", + error: "#EC4145", + errordark: "#E82528", + secondarylight: "#C3C3C3", + secondary: "#9C9C9C", + secondarydark: "#4E4E4E", + warninglight: "#ff0", + warning: "#cc0", + warningdark: "#990", + infolight: "#69f", + info: "#36c", + infodark: "#1C4FB3", + welllight: "#999999", + well: "#35383C", + white: "#FFFFFF", + black: "#202225", + hp: "#FF5656", + money: "#43FF43", + hack: "#FFAB3D", + combat: "#8A90FD", + cha: "#FF51D9", + int: "#6495ed", + rep: "#FFFF30", + disabled: "#474B51", + backgroundprimary: "#2F3136", + backgroundsecondary: "#35393E", + button: "#333", + }, +}; diff --git a/src/Themes/data/discord-like/screenshot.png b/src/Themes/data/discord-like/screenshot.png new file mode 100644 index 000000000..16b9ef558 Binary files /dev/null and b/src/Themes/data/discord-like/screenshot.png differ diff --git a/src/Themes/data/dracula/index.ts b/src/Themes/data/dracula/index.ts new file mode 100644 index 000000000..c1cdf19e8 --- /dev/null +++ b/src/Themes/data/dracula/index.ts @@ -0,0 +1,45 @@ +import { IPredefinedTheme } from "../../Themes"; +import img1 from "./screenshot.png"; + +export const Theme: IPredefinedTheme = { + name: "Dracula", + description: "Dracula Look-alike", + credit: "H3draut3r", + reference: "https://discord.com/channels/415207508303544321/921991895230611466/922296307836678144", + screenshot: img1, + colors: { + primarylight: "#7082B8", + primary: "#F8F8F2", + primarydark: "#FF79C6", + successlight: "#0f0", + success: "#0c0", + successdark: "#090", + errorlight: "#FD4545", + error: "#FF2D2D", + errordark: "#C62424", + secondarylight: "#AAA", + secondary: "#8BE9FD", + secondarydark: "#666", + warninglight: "#FFC281", + warning: "#FFB86C", + warningdark: "#E6A055", + infolight: "#A0A0FF", + info: "#7070FF", + infodark: "#4040FF", + welllight: "#44475A", + well: "#363948", + white: "#fff", + black: "#282A36", + hp: "#D34448", + money: "#50FA7B", + hack: "#F1FA8C", + combat: "#BD93F9", + cha: "#FF79C6", + int: "#6495ed", + rep: "#faffdf", + disabled: "#66cfbc", + backgroundprimary: "#282A36", + backgroundsecondary: "#21222C", + button: "#21222C", + }, +}; diff --git a/src/Themes/data/dracula/screenshot.png b/src/Themes/data/dracula/screenshot.png new file mode 100644 index 000000000..51264adb1 Binary files /dev/null and b/src/Themes/data/dracula/screenshot.png differ diff --git a/src/Themes/data/index.ts b/src/Themes/data/index.ts new file mode 100644 index 000000000..cef542782 --- /dev/null +++ b/src/Themes/data/index.ts @@ -0,0 +1,14 @@ +export { Theme as Default } from "./default"; +export { Theme as DefaultLite } from "./default-lite"; +export { Theme as Monokai } from "./monokai-ish"; +export { Theme as Warmer } from "./warmer"; +export { Theme as DarkPlus } from "./dark-plus"; +export { Theme as MayukaiDark } from "./mayukai-dark"; +export { Theme as Purple } from "./purple"; +export { Theme as SmoothGreen } from "./smooth-green"; +export { Theme as Dracula } from "./dracula"; +export { Theme as DarkBlue } from "./dark-blue"; +export { Theme as DiscordLike } from "./discord-like"; +export { Theme as OneDark } from "./one-dark"; +export { Theme as MutedGoldBlue } from "./muted-gold-blue"; +export { Theme as Light } from "./light"; diff --git a/src/Themes/data/light/index.ts b/src/Themes/data/light/index.ts new file mode 100644 index 000000000..7755efb70 --- /dev/null +++ b/src/Themes/data/light/index.ts @@ -0,0 +1,45 @@ +import { IPredefinedTheme } from "../../Themes"; +import img1 from "./screenshot.png"; + +export const Theme: IPredefinedTheme = { + name: "Light", + description: "Cobbled Together Light Theme", + credit: "matt", + reference: "https://discord.com/channels/415207508303544321/921991895230611466/926114005456658432", + screenshot: img1, + colors: { + primarylight: "#535353", + primary: "#1A1A1A", + primarydark: "#0d0d0d", + successlight: "#63c439", + success: "#428226", + successdark: "#2E5A1B", + errorlight: "#df7051", + error: "#C94824", + errordark: "#91341B", + secondarylight: "#b3b3b3", + secondary: "#9B9B9B", + secondarydark: "#7A7979", + warninglight: "#e8d464", + warning: "#C6AD20", + warningdark: "#9F8A16", + infolight: "#6299cf", + info: "#3778B7", + infodark: "#30689C", + welllight: "#f9f9f9", + well: "#eaeaea", + white: "#F7F7F7", + black: "#F7F7F7", + hp: "#BF5C41", + money: "#E1B121", + hack: "#47BC38", + combat: "#656262", + cha: "#A568AC", + int: "#889BCF", + rep: "#656262", + disabled: "#70B4BF", + backgroundprimary: "#F7F7F7", + backgroundsecondary: "#f9f9f9", + button: "#eaeaea", + }, +}; diff --git a/src/Themes/data/light/screenshot.png b/src/Themes/data/light/screenshot.png new file mode 100644 index 000000000..3f03552d7 Binary files /dev/null and b/src/Themes/data/light/screenshot.png differ diff --git a/src/Themes/data/mayukai-dark/index.ts b/src/Themes/data/mayukai-dark/index.ts new file mode 100644 index 000000000..427c8f971 --- /dev/null +++ b/src/Themes/data/mayukai-dark/index.ts @@ -0,0 +1,45 @@ +import { IPredefinedTheme } from "../../Themes"; +import img1 from "./screenshot.png"; + +export const Theme: IPredefinedTheme = { + name: "Mayukai Dark", + description: "Mayukai Dark-esque", + credit: "Festive Noire", + reference: "https://discord.com/channels/415207508303544321/921991895230611466/922037502334889994", + screenshot: img1, + colors: { + primarylight: "#DDDFC5", + primary: "#CDCFB6", + primarydark: "#9D9F8C", + successlight: "#00EF00", + success: "#00A500", + successdark: "#007A00", + errorlight: "#F92672", + error: "#CA1C5C", + errordark: "#90274A", + secondarylight: "#AAA", + secondary: "#888", + secondarydark: "#666", + warninglight: "#D3D300", + warning: "#cc0", + warningdark: "#990", + infolight: "#69f", + info: "#36c", + infodark: "#039", + welllight: "#444", + well: "#00010A", + white: "#fff", + black: "#020509", + hp: "#dd3434", + money: "#ffd700", + hack: "#8CCF27", + combat: "#faffdf", + cha: "#a671d1", + int: "#6495ed", + rep: "#faffdf", + disabled: "#66cfbc", + backgroundprimary: "#080C11", + backgroundsecondary: "#03080F", + button: "#00010A", + }, +}; diff --git a/src/Themes/data/mayukai-dark/screenshot.png b/src/Themes/data/mayukai-dark/screenshot.png new file mode 100644 index 000000000..9e3040dce Binary files /dev/null and b/src/Themes/data/mayukai-dark/screenshot.png differ diff --git a/src/Themes/data/monokai-ish/index.ts b/src/Themes/data/monokai-ish/index.ts new file mode 100644 index 000000000..7497852aa --- /dev/null +++ b/src/Themes/data/monokai-ish/index.ts @@ -0,0 +1,44 @@ +import { IPredefinedTheme } from "../../Themes"; +import img1 from "./screenshot.png"; + +export const Theme: IPredefinedTheme = { + name: "Monokai'ish", + description: "Monokai'ish", + credit: "eltea", + screenshot: img1, + colors: { + primarylight: "#FFF", + primary: "#F8F8F2", + primarydark: "#FAFAEB", + successlight: "#ADE146", + success: "#A6E22E", + successdark: "#98E104", + errorlight: "#FF69A0", + error: "#F92672", + errordark: "#D10F56", + secondarylight: "#AAA", + secondary: "#888", + secondarydark: "#666", + warninglight: "#E1D992", + warning: "#E6DB74", + warningdark: "#EDDD54", + infolight: "#92E1F1", + info: "#66D9EF", + infodark: "#31CDED", + welllight: "#444", + well: "#222", + white: "#fff", + black: "#000", + hp: "#F92672", + money: "#E6DB74", + hack: "#A6E22E", + combat: "#75715E", + cha: "#AE81FF", + int: "#66D9EF", + rep: "#E69F66", + disabled: "#66cfbc", + backgroundprimary: "#272822", + backgroundsecondary: "#1B1C18", + button: "#333", + }, +}; diff --git a/src/Themes/data/monokai-ish/screenshot.png b/src/Themes/data/monokai-ish/screenshot.png new file mode 100644 index 000000000..be9f68545 Binary files /dev/null and b/src/Themes/data/monokai-ish/screenshot.png differ diff --git a/src/Themes/data/muted-gold-blue/index.ts b/src/Themes/data/muted-gold-blue/index.ts new file mode 100644 index 000000000..1d5adf3a2 --- /dev/null +++ b/src/Themes/data/muted-gold-blue/index.ts @@ -0,0 +1,45 @@ +import { IPredefinedTheme } from "../../Themes"; +import img1 from "./screenshot.png"; + +export const Theme: IPredefinedTheme = { + name: "Muted Gold & Blue", + description: "Muted gold with blue accents.", + credit: "Sloth", + reference: "https://discord.com/channels/415207508303544321/921991895230611466/924672660758208563", + screenshot: img1, + colors: { + primarylight: "#E3B54A", + primary: "#CAA243", + primarydark: "#7E6937", + successlight: "#82FF82", + success: "#6FDA6F", + successdark: "#64C364", + errorlight: "#FD5555", + error: "#D84A4A", + errordark: "#AC3939", + secondarylight: "#D8D0B8", + secondary: "#B1AA95", + secondarydark: "#736E5E", + warninglight: "#ff0", + warning: "#cc0", + warningdark: "#990", + infolight: "#69f", + info: "#36c", + infodark: "#039", + welllight: "#444", + well: "#111111", + white: "#fff", + black: "#070300", + hp: "#dd3434", + money: "#ffd700", + hack: "#adff2f", + combat: "#faffdf", + cha: "#a671d1", + int: "#6495ed", + rep: "#faffdf", + disabled: "#66cfbc", + backgroundprimary: "#0A0A0E", + backgroundsecondary: "#0E0E10", + button: "#222222", + }, +}; diff --git a/src/Themes/data/muted-gold-blue/screenshot.png b/src/Themes/data/muted-gold-blue/screenshot.png new file mode 100644 index 000000000..325587072 Binary files /dev/null and b/src/Themes/data/muted-gold-blue/screenshot.png differ diff --git a/src/Themes/data/one-dark/index.ts b/src/Themes/data/one-dark/index.ts new file mode 100644 index 000000000..4111d380e --- /dev/null +++ b/src/Themes/data/one-dark/index.ts @@ -0,0 +1,45 @@ +import { IPredefinedTheme } from "../../Themes"; +import img1 from "./screenshot.png"; + +export const Theme: IPredefinedTheme = { + name: "One Dark", + description: "Dark with a greenish tint", + credit: "Dexalt142", + reference: "https://discord.com/channels/415207508303544321/921991895230611466/924650660694208512", + screenshot: img1, + colors: { + primarylight: "#98C379", + primary: "#98C379", + primarydark: "#98C379", + successlight: "#98C379", + success: "#98C379", + successdark: "#98C379", + errorlight: "#E06C75", + error: "#BE5046", + errordark: "#BE5046", + secondarylight: "#AAA", + secondary: "#888", + secondarydark: "#666", + warninglight: "#E5C07B", + warning: "#E5C07B", + warningdark: "#D19A66", + infolight: "#61AFEF", + info: "#61AFEF", + infodark: "#61AFEF", + welllight: "#4B5263", + well: "#282C34", + white: "#ABB2BF", + black: "#282C34", + hp: "#E06C75", + money: "#E5C07B", + hack: "#98C379", + combat: "#ABB2BF", + cha: "#C678DD", + int: "#61AFEF", + rep: "#ABB2BF", + disabled: "#56B6C2", + backgroundprimary: "#282C34", + backgroundsecondary: "#21252B", + button: "#4B5263", + }, +}; diff --git a/src/Themes/data/one-dark/screenshot.png b/src/Themes/data/one-dark/screenshot.png new file mode 100644 index 000000000..7a418256c Binary files /dev/null and b/src/Themes/data/one-dark/screenshot.png differ diff --git a/src/Themes/data/purple/index.ts b/src/Themes/data/purple/index.ts new file mode 100644 index 000000000..1a2cd5d25 --- /dev/null +++ b/src/Themes/data/purple/index.ts @@ -0,0 +1,45 @@ +import { IPredefinedTheme } from "../../Themes"; +import img1 from "./screenshot.png"; + +export const Theme: IPredefinedTheme = { + name: "Purple", + credit: "zer0ney", + description: "Essentially all defaults except for purple replacing the main colors", + reference: "https://discord.com/channels/415207508303544321/921991895230611466/922091815849570395", + screenshot: img1, + colors: { + primarylight: "#BA55D3", + primary: "#9370DB", + primarydark: "#8A2BE2", + successlight: "#BA55D3", + success: "#9370DB", + successdark: "#8A2BE2", + errorlight: "#f00", + error: "#c00", + errordark: "#900", + secondarylight: "#AAA", + secondary: "#888", + secondarydark: "#666", + warninglight: "#ff0", + warning: "#cc0", + warningdark: "#990", + infolight: "#69f", + info: "#36c", + infodark: "#039", + welllight: "#444", + well: "#222", + white: "#fff", + black: "#000", + hp: "#dd3434", + money: "#ffd700", + hack: "#adff2f", + combat: "#faffdf", + cha: "#a671d1", + int: "#6495ed", + rep: "#faffdf", + disabled: "#66cfbc", + backgroundprimary: "#000", + backgroundsecondary: "#000", + button: "#333", + }, +}; diff --git a/src/Themes/data/purple/screenshot.png b/src/Themes/data/purple/screenshot.png new file mode 100644 index 000000000..3b3cc2456 Binary files /dev/null and b/src/Themes/data/purple/screenshot.png differ diff --git a/src/Themes/data/smooth-green/index.ts b/src/Themes/data/smooth-green/index.ts new file mode 100644 index 000000000..a228d11ca --- /dev/null +++ b/src/Themes/data/smooth-green/index.ts @@ -0,0 +1,45 @@ +import { IPredefinedTheme } from "../../Themes"; +import img1 from "./screenshot.png"; + +export const Theme: IPredefinedTheme = { + name: "Smooth Green", + description: "A nice green theme that doesn't hurt your eyes.", + credit: "Swidt", + reference: "https://discord.com/channels/415207508303544321/921991895230611466/922243957986033725", + screenshot: img1, + colors: { + primarylight: "#E0E0BC", + primary: "#B0D9A3", + primarydark: "#B8B89C", + successlight: "#00F000", + success: "#6BC16B", + successdark: "#00B400", + errorlight: "#F00000", + error: "#3D713D", + errordark: "#A00000", + secondarylight: "#B4AEAE", + secondary: "#8FAF85", + secondarydark: "#787272", + warninglight: "#F0F000", + warning: "#38F100", + warningdark: "#A0A000", + infolight: "#69f", + info: "#36c", + infodark: "#039", + welllight: "#444", + well: "#2F3C2B", + white: "#fff", + black: "#1E1E1E", + hp: "#dd3434", + money: "#4AA52E", + hack: "#adff2f", + combat: "#faffdf", + cha: "#a671d1", + int: "#6495ed", + rep: "#35A135", + disabled: "#66cfbc", + backgroundprimary: "#1E1E1E", + backgroundsecondary: "#252525", + button: "#2F3C2B", + }, +}; diff --git a/src/Themes/data/smooth-green/screenshot.png b/src/Themes/data/smooth-green/screenshot.png new file mode 100644 index 000000000..d74fcec6c Binary files /dev/null and b/src/Themes/data/smooth-green/screenshot.png differ diff --git a/src/Themes/data/warmer/index.ts b/src/Themes/data/warmer/index.ts new file mode 100644 index 000000000..b513cd8b9 --- /dev/null +++ b/src/Themes/data/warmer/index.ts @@ -0,0 +1,45 @@ +import { IPredefinedTheme } from "../../Themes"; +import img1 from "./screenshot.png"; + +export const Theme: IPredefinedTheme = { + name: "Warmer", + credit: "hexnaught", + description: "Warmer, softer theme", + reference: "https://discord.com/channels/415207508303544321/921991895230611466/921999581020028938", + screenshot: img1, + colors: { + primarylight: "#EA9062", + primary: "#DD7B4A", + primarydark: "#D3591C", + successlight: "#6ACF6A", + success: "#43BF43", + successdark: "#3E913E", + errorlight: "#C15757", + error: "#B34141", + errordark: "#752525", + secondarylight: "#AAA", + secondary: "#888", + secondarydark: "#666", + warninglight: "#E6E69D", + warning: "#DADA56", + warningdark: "#A1A106", + infolight: "#69f", + info: "#36c", + infodark: "#039", + welllight: "#444", + well: "#222", + white: "#fff", + black: "#000", + hp: "#dd3434", + money: "#ffd700", + hack: "#adff2f", + combat: "#faffdf", + cha: "#AD84CF", + int: "#6495ed", + rep: "#faffdf", + disabled: "#76C6B7", + backgroundprimary: "#000", + backgroundsecondary: "#000", + button: "#333", + }, +}; diff --git a/src/Themes/data/warmer/screenshot.png b/src/Themes/data/warmer/screenshot.png new file mode 100644 index 000000000..c8cf0257c Binary files /dev/null and b/src/Themes/data/warmer/screenshot.png differ diff --git a/src/Themes/ui/StyleEditorButton.tsx b/src/Themes/ui/StyleEditorButton.tsx new file mode 100644 index 000000000..4fc7648ea --- /dev/null +++ b/src/Themes/ui/StyleEditorButton.tsx @@ -0,0 +1,19 @@ +import React, { useState } from "react"; +import Button from "@mui/material/Button"; +import Tooltip from "@mui/material/Tooltip"; +import TextFormatIcon from "@mui/icons-material/TextFormat"; +import { StyleEditorModal } from "./StyleEditorModal"; + +export function StyleEditorButton(): React.ReactElement { + const [styleEditorOpen, setStyleEditorOpen] = useState(false); + return ( + <> + + + + setStyleEditorOpen(false)} /> + + ); +} diff --git a/src/ui/React/StyleEditorModal.tsx b/src/Themes/ui/StyleEditorModal.tsx similarity index 98% rename from src/ui/React/StyleEditorModal.tsx rename to src/Themes/ui/StyleEditorModal.tsx index 94b9950c6..b5b7bec4c 100644 --- a/src/ui/React/StyleEditorModal.tsx +++ b/src/Themes/ui/StyleEditorModal.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { Modal } from "./Modal"; +import { Modal } from "../../ui/React/Modal"; import Button from "@mui/material/Button"; import ButtonGroup from "@mui/material/ButtonGroup"; @@ -11,7 +11,7 @@ import SaveIcon from "@mui/icons-material/Save"; import { ThemeEvents } from "./Theme"; import { Settings } from "../../Settings/Settings"; -import { defaultStyles } from "../../Settings/Styles"; +import { defaultStyles } from "../Styles"; import { Tooltip } from "@mui/material"; import { IStyleSettings } from "../../ScriptEditor/NetscriptDefinitions"; diff --git a/src/ui/React/Theme.tsx b/src/Themes/ui/Theme.tsx similarity index 100% rename from src/ui/React/Theme.tsx rename to src/Themes/ui/Theme.tsx diff --git a/src/Themes/ui/ThemeBrowser.tsx b/src/Themes/ui/ThemeBrowser.tsx new file mode 100644 index 000000000..cef35e63d --- /dev/null +++ b/src/Themes/ui/ThemeBrowser.tsx @@ -0,0 +1,93 @@ +import React, { useEffect, useState } from "react"; +import Typography from "@mui/material/Typography"; +import Paper from "@mui/material/Paper"; +import { ThemeEvents } from "./Theme"; +import { Settings } from "../../Settings/Settings"; +import { getPredefinedThemes, IPredefinedTheme } from "../Themes"; +import { Box, ButtonGroup, Button } from "@mui/material"; +import { IRouter } from "../../ui/Router"; +import { ThemeEditorButton } from "./ThemeEditorButton"; +import { StyleEditorButton } from "./StyleEditorButton"; +import { ThemeEntry } from "./ThemeEntry"; +import { ThemeCollaborate } from "./ThemeCollaborate"; +import { Modal } from "../../ui/React/Modal"; +import { SnackbarEvents } from "../../ui/React/Snackbar"; + +interface IProps { + router: IRouter; +} + +// Everything dies when the theme gets reloaded, so we'll keep the current scroll to not jump around. +let previousScrollY = 0; + +export function ThemeBrowser({ router }: IProps): React.ReactElement { + const [modalOpen, setModalOpen] = useState(false); + const [modalImageSrc, setModalImageSrc] = useState(); + const predefinedThemes = getPredefinedThemes(); + const themes = (predefinedThemes && + Object.entries(predefinedThemes).map(([key, templateTheme]) => ( + setTheme(templateTheme)} + onImageClick={handleZoom} + /> + ))) || <>; + + function setTheme(theme: IPredefinedTheme): void { + previousScrollY = window.scrollY; + const previousColors = { ...Settings.theme }; + Object.assign(Settings.theme, theme.colors); + ThemeEvents.emit(); + SnackbarEvents.emit( + <> + Updated theme to "{theme.name}" + + , + "info", + 30000, + ); + } + + function handleZoom(src: string): void { + previousScrollY = window.scrollY; + setModalImageSrc(src); + setModalOpen(true); + } + + function handleCloseZoom(): void { + previousScrollY = window.scrollY; + setModalOpen(false); + } + + useEffect(() => { + requestAnimationFrame(() => window.scrollTo(0, previousScrollY)); + }); + + return ( + + Theme Browser + + + + + + + {themes} + + + + + + ); +} diff --git a/src/Themes/ui/ThemeCollaborate.tsx b/src/Themes/ui/ThemeCollaborate.tsx new file mode 100644 index 000000000..9058e64c5 --- /dev/null +++ b/src/Themes/ui/ThemeCollaborate.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import Typography from "@mui/material/Typography"; +import { Link } from "@mui/material"; + +export function ThemeCollaborate(): React.ReactElement { + return ( + <> + + If you've created a theme that you believe should be added in game's theme browser, feel free to{" "} + + create a pull request + + . + + + Head over to the{" "} + + theme-sharing + {" "} + discord channel for more. + + + ); +} diff --git a/src/Themes/ui/ThemeEditorButton.tsx b/src/Themes/ui/ThemeEditorButton.tsx new file mode 100644 index 000000000..e2509802f --- /dev/null +++ b/src/Themes/ui/ThemeEditorButton.tsx @@ -0,0 +1,24 @@ +import React, { useState } from "react"; +import Button from "@mui/material/Button"; +import Tooltip from "@mui/material/Tooltip"; +import { ThemeEditorModal } from "./ThemeEditorModal"; +import { IRouter } from "../../ui/Router"; +import ColorizeIcon from "@mui/icons-material/Colorize"; + +interface IProps { + router: IRouter; +} + +export function ThemeEditorButton({ router }: IProps): React.ReactElement { + const [themeEditorOpen, setThemeEditorOpen] = useState(false); + return ( + <> + + + + setThemeEditorOpen(false)} router={router} /> + + ); +} diff --git a/src/ui/React/ThemeEditorModal.tsx b/src/Themes/ui/ThemeEditorModal.tsx similarity index 90% rename from src/ui/React/ThemeEditorModal.tsx rename to src/Themes/ui/ThemeEditorModal.tsx index 299471e4e..986f7420d 100644 --- a/src/ui/React/ThemeEditorModal.tsx +++ b/src/Themes/ui/ThemeEditorModal.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; -import { Modal } from "./Modal"; +import { Modal } from "../../ui/React/Modal"; import Button from "@mui/material/Button"; +import ButtonGroup from "@mui/material/ButtonGroup"; import Typography from "@mui/material/Typography"; import Tooltip from "@mui/material/Tooltip"; import Paper from "@mui/material/Paper"; @@ -8,15 +9,19 @@ import TextField from "@mui/material/TextField"; import IconButton from "@mui/material/IconButton"; import ReplyIcon from "@mui/icons-material/Reply"; import PaletteSharpIcon from "@mui/icons-material/PaletteSharp"; +import HistoryIcon from '@mui/icons-material/History'; import { Color, ColorPicker } from "material-ui-color"; import { ThemeEvents } from "./Theme"; import { Settings, defaultSettings } from "../../Settings/Settings"; -import { getPredefinedThemes } from "../../Settings/Themes"; +import { defaultTheme } from "../Themes"; import { UserInterfaceTheme } from "../../ScriptEditor/NetscriptDefinitions"; +import { IRouter } from "../../ui/Router"; +import { ThemeCollaborate } from "./ThemeCollaborate"; interface IProps { open: boolean; onClose: () => void; + router: IRouter; } interface IColorEditorProps { @@ -68,28 +73,6 @@ export function ThemeEditorModal(props: IProps): React.ReactElement { ...Settings.theme, }); - const predefinedThemes = getPredefinedThemes(); - const themes = predefinedThemes && Object.entries(predefinedThemes) - .map(([key, templateTheme]) => { - const name = templateTheme.name || key; - let inner = {name}; - let toolTipTitle; - if (templateTheme.credit) { - toolTipTitle = {templateTheme.description || name} by {templateTheme.credit}; - } else if (templateTheme.description) { - toolTipTitle = {templateTheme.description}; - } - if (toolTipTitle) { - inner = {inner} - } - return ( - - ); - }) || <>; - function setTheme(theme: UserInterfaceTheme): void { setCustomTheme(theme); Object.assign(Settings.theme, theme); @@ -372,8 +355,18 @@ export function ThemeEditorModal(props: IProps): React.ReactElement { /> <> Backup your theme or share it with others by copying the string above. - Replace the current theme with a pre-built template using the buttons below. - {themes} + + + + + + + + + diff --git a/src/Themes/ui/ThemeEntry.tsx b/src/Themes/ui/ThemeEntry.tsx new file mode 100644 index 000000000..019f54e59 --- /dev/null +++ b/src/Themes/ui/ThemeEntry.tsx @@ -0,0 +1,77 @@ +import React from "react"; +import Typography from "@mui/material/Typography"; +import Tooltip from "@mui/material/Tooltip"; +import PaletteSharpIcon from "@mui/icons-material/PaletteSharp"; +import { Settings } from "../../Settings/Settings"; +import { IPredefinedTheme } from "../Themes"; +import { Link, Card, CardHeader, CardContent, CardMedia, Button } from "@mui/material"; + +interface IProps { + theme: IPredefinedTheme; + onActivated: () => void; + onImageClick: (src: string) => void; +} + +export function ThemeEntry({ theme, onActivated, onImageClick }: IProps): React.ReactElement { + if (!theme) return <>; + return ( + + + + + } + title={theme.name} + subheader={ + <> + by {theme.credit}{" "} + {theme.reference && ( + <> + ( + + ref + + ) + + )} + + } + sx={{ + color: Settings.theme.primary, + "& .MuiCardHeader-subheader": { + color: Settings.theme.secondarydark, + }, + "& .MuiButton-outlined": { + backgroundColor: "transparent", + }, + }} + /> + onImageClick(theme.screenshot)} + /> + + + {theme.description} + + + + ); +} diff --git a/src/engine.tsx b/src/engine.tsx index f660beb39..e3f67223f 100644 --- a/src/engine.tsx +++ b/src/engine.tsx @@ -30,7 +30,7 @@ import { Player } from "./Player"; import { saveObject, loadGame } from "./SaveObject"; import { initForeignServers } from "./Server/AllServers"; import { Settings } from "./Settings/Settings"; -import { ThemeEvents } from "./ui/React/Theme"; +import { ThemeEvents } from "./Themes/ui/Theme"; import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags"; import { initSymbolToStockMap, processStockPrices } from "./StockMarket/StockMarket"; import { Terminal } from "./Terminal"; diff --git a/src/index.tsx b/src/index.tsx index 962bfc469..1c15adcde 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,7 +1,7 @@ import React from "react"; import ReactDOM from "react-dom"; -import { TTheme as Theme, ThemeEvents, refreshTheme } from "./ui/React/Theme"; +import { TTheme as Theme, ThemeEvents, refreshTheme } from "./Themes/ui/Theme"; import { LoadingScreen } from "./ui/LoadingScreen"; import { initElectron } from "./Electron"; initElectron(); diff --git a/src/ui/GameRoot.tsx b/src/ui/GameRoot.tsx index 2e7a265cc..86c64c018 100644 --- a/src/ui/GameRoot.tsx +++ b/src/ui/GameRoot.tsx @@ -79,6 +79,7 @@ import { RecoveryMode, RecoveryRoot } from "./React/RecoveryRoot"; import { AchievementsRoot } from "../Achievements/AchievementsRoot"; import { ErrorBoundary } from "./ErrorBoundary"; import { Settings } from "../Settings/Settings"; +import { ThemeBrowser } from "../Themes/ui/ThemeBrowser"; const htmlLocation = location; @@ -194,6 +195,9 @@ export let Router: IRouter = { toAchievements: () => { throw new Error("Router called before initialization"); }, + toThemeBrowser: () => { + throw new Error("Router called before initialization"); + }, }; function determineStartPage(player: IPlayer): Page { @@ -307,6 +311,9 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme toAchievements: () => { setPage(Page.Achievements); }, + toThemeBrowser: () => { + setPage(Page.ThemeBrowser); + } }; useEffect(() => { @@ -471,6 +478,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme mainPage = ( saveObject.saveGame()} export={() => { // Apply the export bonus before saving the game @@ -503,6 +511,10 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme mainPage = ; break; } + case Page.ThemeBrowser: { + mainPage = ; + break; + } } return ( diff --git a/src/ui/React/GameOptionsRoot.tsx b/src/ui/React/GameOptionsRoot.tsx index cff5167e0..03b75d060 100644 --- a/src/ui/React/GameOptionsRoot.tsx +++ b/src/ui/React/GameOptionsRoot.tsx @@ -22,12 +22,11 @@ import TextField from "@mui/material/TextField"; import DownloadIcon from "@mui/icons-material/Download"; import UploadIcon from "@mui/icons-material/Upload"; import SaveIcon from "@mui/icons-material/Save"; +import PaletteIcon from '@mui/icons-material/Palette'; import { FileDiagnosticModal } from "../../Diagnostic/FileDiagnosticModal"; import { dialogBoxCreate } from "./DialogBox"; import { ConfirmationModal } from "./ConfirmationModal"; -import { ThemeEditorModal } from "./ThemeEditorModal"; -import { StyleEditorModal } from "./StyleEditorModal"; import { SnackbarEvents } from "./Snackbar"; @@ -37,6 +36,9 @@ import { formatTime } from "../../utils/helpers/formatTime"; import { OptionSwitch } from "./OptionSwitch"; import { DeleteGameButton } from "./DeleteGameButton"; import { SoftResetButton } from "./SoftResetButton"; +import { IRouter } from "../Router"; +import { ThemeEditorButton } from "../../Themes/ui/ThemeEditorButton"; +import { StyleEditorButton } from "../../Themes/ui/StyleEditorButton"; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -50,6 +52,7 @@ const useStyles = makeStyles((theme: Theme) => interface IProps { player: IPlayer; + router: IRouter; save: () => void; export: () => void; forceKill: () => void; @@ -74,8 +77,6 @@ export function GameOptionsRoot(props: IProps): React.ReactElement { const [timestampFormat, setTimestampFormat] = useState(Settings.TimestampsFormat); const [locale, setLocale] = useState(Settings.Locale); const [diagnosticOpen, setDiagnosticOpen] = useState(false); - const [themeEditorOpen, setThemeEditorOpen] = useState(false); - const [styleEditorOpen, setStyleEditorOpen] = useState(false); const [importSaveOpen, setImportSaveOpen] = useState(false); const [importData, setImportData] = useState(null); @@ -636,9 +637,14 @@ export function GameOptionsRoot(props: IProps): React.ReactElement { - - - + + + + + + @@ -663,8 +669,6 @@ export function GameOptionsRoot(props: IProps): React.ReactElement { setDiagnosticOpen(false)} /> - setThemeEditorOpen(false)} /> - setStyleEditorOpen(false)} /> ); } diff --git a/src/ui/React/Snackbar.tsx b/src/ui/React/Snackbar.tsx index 68b98e945..d1648b027 100644 --- a/src/ui/React/Snackbar.tsx +++ b/src/ui/React/Snackbar.tsx @@ -14,6 +14,10 @@ const useStyles = makeStyles(() => ({ snackbar: { // Log popup z-index increments, so let's add a padding to be well above them. zIndex: `${logBoxBaseZIndex + 1000} !important` as any, + + "& .MuiAlert-icon": { + alignSelf: 'center', + }, } })); @@ -27,7 +31,7 @@ export function SnackbarProvider(props: IProps): React.ReactElement { ); } -export const SnackbarEvents = new EventEmitter<[string, "success" | "warning" | "error" | "info", number]>(); +export const SnackbarEvents = new EventEmitter<[string | React.ReactNode, "success" | "warning" | "error" | "info", number]>(); export function Snackbar(): React.ReactElement { const { enqueueSnackbar, closeSnackbar } = useSnackbar(); diff --git a/src/ui/Router.ts b/src/ui/Router.ts index 4056a44ac..7ebeb31d8 100644 --- a/src/ui/Router.ts +++ b/src/ui/Router.ts @@ -37,6 +37,7 @@ export enum Page { StaneksGift, Recovery, Achievements, + ThemeBrowser, } export interface ScriptEditorRouteOptions { @@ -82,4 +83,5 @@ export interface IRouter { toLocation(location: Location): void; toStaneksGift(): void; toAchievements(): void; + toThemeBrowser(): void; } diff --git a/test/__mocks__/fileMock.js b/test/__mocks__/fileMock.js new file mode 100644 index 000000000..86059f362 --- /dev/null +++ b/test/__mocks__/fileMock.js @@ -0,0 +1 @@ +module.exports = 'test-file-stub'; diff --git a/test/__mocks__/styleMock.js b/test/__mocks__/styleMock.js new file mode 100644 index 000000000..e69de29bb diff --git a/webpack.config.js b/webpack.config.js index 2b72bb10e..4a62292dd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -156,6 +156,14 @@ module.exports = (env, argv) => { test: /\.s?css$/, use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"], }, + { + test: /\.(png|jpe?g|gif|jp2|webp)$/, + loader: 'file-loader', + options: { + name: '[contenthash].[ext]', + outputPath: 'dist/images', + }, + }, ], }, optimization: {