From 5a19e9fd129b0fb1daaa6c0daa02f0d78160670e Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Wed, 29 Jan 2025 03:46:22 -0300 Subject: [PATCH] feat: add theme editor with Monaco and custom CSS injection --- package.json | 1 + src/locales/en/translation.json | 20 ++- src/main/events/index.ts | 5 + src/main/events/themes/css-injector.ts | 12 ++ .../events/themes/get-active-custom-theme.ts | 10 ++ .../events/themes/get-custom-theme-by-id.ts | 8 ++ src/main/events/themes/open-editor-window.ts | 8 ++ src/main/events/themes/update-custom-theme.ts | 13 ++ src/main/services/window-manager.ts | 52 +++++++ src/preload/index.ts | 15 ++ src/renderer/src/app.tsx | 25 ++++ src/renderer/src/declaration.d.ts | 8 ++ src/renderer/src/helpers.ts | 26 ++++ src/renderer/src/main.tsx | 6 + src/renderer/src/pages/editor/editor.scss | 74 ++++++++++ src/renderer/src/pages/editor/editor.tsx | 131 ++++++++++++++++++ .../aparence/components/theme-actions.tsx | 2 +- .../aparence/components/theme-card.tsx | 64 ++++++++- .../aparence/modals/add-theme-modal.tsx | 14 +- .../aparence/modals/delete-theme-modal.tsx | 4 +- .../settings/aparence/settings-appearance.tsx | 16 ++- src/types/theme.types.ts | 3 +- yarn.lock | 19 +++ 23 files changed, 516 insertions(+), 20 deletions(-) create mode 100644 src/main/events/themes/css-injector.ts create mode 100644 src/main/events/themes/get-active-custom-theme.ts create mode 100644 src/main/events/themes/get-custom-theme-by-id.ts create mode 100644 src/main/events/themes/open-editor-window.ts create mode 100644 src/main/events/themes/update-custom-theme.ts create mode 100644 src/renderer/src/pages/editor/editor.scss create mode 100644 src/renderer/src/pages/editor/editor.tsx diff --git a/package.json b/package.json index f7fac4ca..b69699b1 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@electron-toolkit/utils": "^3.0.0", "@fontsource/noto-sans": "^5.1.0", "@hookform/resolvers": "^3.9.1", + "@monaco-editor/react": "^4.6.0", "@primer/octicons-react": "^19.9.0", "@radix-ui/react-dropdown-menu": "^2.1.2", "@reduxjs/toolkit": "^2.2.3", diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 43e98033..7419694c 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -297,7 +297,25 @@ "subscription_renew_cancelled": "Automatic renewal is disabled", "subscription_renews_on": "Your subscription renews on {{date}}", "bill_sent_until": "Your next bill will be sent until this day", - "no_themes": "Seems like you don't have any themes yet, but no worries, click here to create your first masterpiece." + "no_themes": "Seems like you don't have any themes yet, but no worries, click here to create your first masterpiece.", + "editor_tab_code": "Code", + "editor_tab_info": "Info", + "editor_tab_save": "Save", + "web_store": "Web store", + "clear_themes": "Clear", + "add_theme": "Add", + "add_theme_modal_title": "Add custom theme", + "add_theme_modal_description": "Create a new theme to customize Hydra's appearance", + "theme_name": "Name", + "insert_theme_name": "Insert theme name", + "set_theme": "Set theme", + "unset_theme": "Unset theme", + "delete_theme": "Delete theme", + "edit_theme": "Edit theme", + "delete_all_themes": "Delete all themes", + "delete_all_themes_description": "This will delete all your custom themes", + "delete_theme_description": "This will delete the theme {{theme}}", + "cancel": "Cancel" }, "notifications": { "download_complete": "Download complete", diff --git a/src/main/events/index.ts b/src/main/events/index.ts index c4dc0202..fbf49ac5 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -78,6 +78,11 @@ import "./themes/add-custom-theme"; import "./themes/delete-custom-theme"; import "./themes/get-all-custom-themes"; import "./themes/delete-all-custom-themes"; +import "./themes/update-custom-theme"; +import "./themes/open-editor-window"; +import "./themes/get-custom-theme-by-id"; +import "./themes/get-active-custom-theme"; +import "./themes/css-injector"; import { isPortableVersion } from "@main/helpers"; ipcMain.handle("ping", () => "pong"); diff --git a/src/main/events/themes/css-injector.ts b/src/main/events/themes/css-injector.ts new file mode 100644 index 00000000..86ddc431 --- /dev/null +++ b/src/main/events/themes/css-injector.ts @@ -0,0 +1,12 @@ +import { registerEvent } from "../register-event"; +import { WindowManager } from "@main/services"; + +const injectCSS = async ( + _event: Electron.IpcMainInvokeEvent, + cssString: string +) => { + WindowManager.mainWindow?.webContents.send("css-injected", cssString); + return; +}; + +registerEvent("injectCSS", injectCSS); \ No newline at end of file diff --git a/src/main/events/themes/get-active-custom-theme.ts b/src/main/events/themes/get-active-custom-theme.ts new file mode 100644 index 00000000..6b8f2f2d --- /dev/null +++ b/src/main/events/themes/get-active-custom-theme.ts @@ -0,0 +1,10 @@ +import { registerEvent } from "../register-event"; +import { themes } from "@main/level/sublevels/themes"; +import { Theme } from "@types"; + +const getActiveCustomTheme = async () => { + const allThemes = await themes.values().all(); + return allThemes.find((theme: Theme) => theme.isActive); +}; + +registerEvent("getActiveCustomTheme", getActiveCustomTheme); diff --git a/src/main/events/themes/get-custom-theme-by-id.ts b/src/main/events/themes/get-custom-theme-by-id.ts new file mode 100644 index 00000000..792df84d --- /dev/null +++ b/src/main/events/themes/get-custom-theme-by-id.ts @@ -0,0 +1,8 @@ +import { themes } from "@main/level/sublevels/themes"; +import { registerEvent } from "../register-event"; + +const getCustomThemeById = async (_event: Electron.IpcMainInvokeEvent, themeId: string) => { + return await themes.get(themeId); +}; + +registerEvent("getCustomThemeById", getCustomThemeById); diff --git a/src/main/events/themes/open-editor-window.ts b/src/main/events/themes/open-editor-window.ts new file mode 100644 index 00000000..2cb630d1 --- /dev/null +++ b/src/main/events/themes/open-editor-window.ts @@ -0,0 +1,8 @@ +import { WindowManager } from "@main/services"; +import { registerEvent } from "../register-event"; + +const openEditorWindow = async (_event: Electron.IpcMainInvokeEvent, themeId: string) => { + WindowManager.openEditorWindow(themeId); +}; + +registerEvent("openEditorWindow", openEditorWindow); diff --git a/src/main/events/themes/update-custom-theme.ts b/src/main/events/themes/update-custom-theme.ts new file mode 100644 index 00000000..4773cfbf --- /dev/null +++ b/src/main/events/themes/update-custom-theme.ts @@ -0,0 +1,13 @@ +import { themes } from "@main/level/sublevels/themes"; +import { registerEvent } from "../register-event"; +import { Theme } from "@types"; + +const updateCustomTheme = async ( + _event: Electron.IpcMainInvokeEvent, + themeId: string, + theme: Theme +) => { + await themes.put(themeId, theme); +}; + +registerEvent("updateCustomTheme", updateCustomTheme); diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index cf9089b7..1e199f08 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -194,6 +194,58 @@ export class WindowManager { } } + public static openEditorWindow(themeId: string) { + if (this.mainWindow) { + const editorWindow = new BrowserWindow({ + width: 600, + height: 720, + minWidth: 600, + minHeight: 540, + backgroundColor: "#1c1c1c", + titleBarStyle: process.platform === "linux" ? "default" : "hidden", + ...(process.platform === "linux" ? { icon } : {}), + trafficLightPosition: { x: 16, y: 16 }, + titleBarOverlay: { + symbolColor: "#DADBE1", + color: "#151515", + height: 34, + }, + webPreferences: { + preload: path.join(__dirname, "../preload/index.mjs"), + sandbox: false, + }, + show: false, + }); + + if (!app.isPackaged) { + editorWindow.webContents.openDevTools(); + } else { + this.mainWindow?.webContents.openDevTools(); + } + + editorWindow.removeMenu(); + + if (is.dev && process.env["ELECTRON_RENDERER_URL"]) { + editorWindow.loadURL( + `${process.env["ELECTRON_RENDERER_URL"]}#/editor?themeId=${themeId}` + ); + } else { + editorWindow.loadFile( + path.join(__dirname, "../renderer/index.html"), + { + hash: "editor", + } + ); + } + + + + editorWindow.once("ready-to-show", () => { + editorWindow.show(); + }); + } + } + public static redirect(hash: string) { if (!this.mainWindow) this.createMainWindow(); this.loadMainWindowURL(hash); diff --git a/src/preload/index.ts b/src/preload/index.ts index e49b76ef..2d40de0c 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -342,4 +342,19 @@ contextBridge.exposeInMainWorld("electron", { deleteAllCustomThemes: () => ipcRenderer.invoke("deleteAllCustomThemes"), deleteCustomTheme: (themeId: string) => ipcRenderer.invoke("deleteCustomTheme", themeId), + updateCustomTheme: (themeId: string, theme: Theme) => + ipcRenderer.invoke("updateCustomTheme", themeId, theme), + getCustomThemeById: (themeId: string) => + ipcRenderer.invoke("getCustomThemeById", themeId), + getActiveCustomTheme: () => ipcRenderer.invoke("getActiveCustomTheme"), + + /* Editor */ + openEditorWindow: (themeId: string) => ipcRenderer.invoke("openEditorWindow", themeId), + injectCSS: (cssString: string) => + ipcRenderer.invoke("injectCSS", cssString), + onCssInjected: (cb: (cssString: string) => void) => { + const listener = (_event: Electron.IpcRendererEvent, cssString: string) => cb(cssString); + ipcRenderer.on("css-injected", listener); + return () => ipcRenderer.removeListener("css-injected", listener); + }, }); diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index f7adac49..8a681b7e 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -28,7 +28,9 @@ import { downloadSourcesTable } from "./dexie"; import { useSubscription } from "./hooks/use-subscription"; import { HydraCloudModal } from "./pages/shared-modals/hydra-cloud/hydra-cloud-modal"; +import { injectCustomCss } from "./helpers"; import "./app.scss"; +import { Theme } from "@types"; export interface AppProps { children: React.ReactNode; @@ -233,6 +235,29 @@ export function App() { downloadSourcesWorker.postMessage(["SYNC_DOWNLOAD_SOURCES", id]); }, [updateRepacks]); + useEffect(() => { + const loadAndApplyTheme = async () => { + const activeTheme: Theme = await window.electron.getActiveCustomTheme(); + + if (activeTheme.code) { + injectCustomCss(activeTheme.code); + } + }; + loadAndApplyTheme(); + }, []); + + useEffect(() => { + const unsubscribe = window.electron.onCssInjected((cssString) => { + if (cssString) { + injectCustomCss(cssString); + } + }); + + return () => { + unsubscribe(); + }; + }, []); + const handleToastClose = useCallback(() => { dispatch(closeToast()); }, [dispatch]); diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 7017f018..ccb66ad0 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -275,6 +275,14 @@ declare global { getAllCustomThemes: () => Promise; deleteAllCustomThemes: () => Promise; deleteCustomTheme: (themeId: string) => Promise; + updateCustomTheme: (themeId: string, theme: Theme) => Promise; + getCustomThemeById: (themeId: string) => Promise; + getActiveCustomTheme: () => Promise; + + /* Editor */ + openEditorWindow: (themeId: string) => Promise; + injectCSS: (cssString: string) => Promise; + onCssInjected: (cb: (cssString: string) => void) => () => Electron.IpcRenderer; } interface Window { diff --git a/src/renderer/src/helpers.ts b/src/renderer/src/helpers.ts index 972cdfc9..43531a0c 100644 --- a/src/renderer/src/helpers.ts +++ b/src/renderer/src/helpers.ts @@ -53,3 +53,29 @@ export const buildGameAchievementPath = ( export const darkenColor = (color: string, amount: number, alpha: number = 1) => new Color(color).darken(amount).alpha(alpha).toString(); + +export const injectCustomCss = (css: string) => { + try { + const currentCustomCss = document.getElementById("custom-css"); + if (currentCustomCss) { + currentCustomCss.remove(); + } + + const style = document.createElement("style"); + style.id = "custom-css"; + style.type = "text/css"; + style.textContent = ` + ${css} + `; + document.head.appendChild(style); + } catch (error) { + console.error("failed to inject custom css:", error); + } +}; + +export const removeCustomCss = () => { + const currentCustomCss = document.getElementById("custom-css"); + if (currentCustomCss) { + currentCustomCss.remove(); + } +}; diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index 61c561f1..509d744d 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -33,6 +33,7 @@ const Profile = React.lazy(() => import("./pages/profile/profile")); const Achievements = React.lazy( () => import("./pages/achievements/achievements") ); +const Editor = React.lazy(() => import("./pages/editor/editor")); import * as Sentry from "@sentry/react"; @@ -104,6 +105,11 @@ ReactDOM.createRoot(document.getElementById("root")!).render( element={} /> + + } + /> diff --git a/src/renderer/src/pages/editor/editor.scss b/src/renderer/src/pages/editor/editor.scss new file mode 100644 index 00000000..09c58612 --- /dev/null +++ b/src/renderer/src/pages/editor/editor.scss @@ -0,0 +1,74 @@ +@use "../../scss/globals.scss" as globals; + +.editor { + display: flex; + flex-direction: column; + height: 100%; + width: 100%; + + &__header { + height: 35px; + display: flex; + align-items: center; + padding: 10px; + background-color: globals.$dark-background-color; + font-size: 8px; + z-index: 50; + -webkit-app-region: drag; + gap: 8px; + + h1 { + margin: 0; + line-height: 1; + } + + &__status { + display: flex; + width: 9px; + height: 9px; + background-color: globals.$muted-color; + border-radius: 50%; + margin-top: 3px; + } + } + + &__footer { + background-color: globals.$dark-background-color; + padding: globals.$spacing-unit globals.$spacing-unit * 2; + position: absolute; + bottom: 0; + left: 0; + right: 0; + z-index: 50; + + &-actions { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + + &__tabs { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 8px; + + .active { + background-color: darken(globals.$dark-background-color, 2%); + } + } + } + } + + &__info { + padding: 16px; + + p { + font-size: 16px; + font-weight: 600; + color: globals.$muted-color; + margin-bottom: 8px; + } + } +} \ No newline at end of file diff --git a/src/renderer/src/pages/editor/editor.tsx b/src/renderer/src/pages/editor/editor.tsx new file mode 100644 index 00000000..22a8f3e9 --- /dev/null +++ b/src/renderer/src/pages/editor/editor.tsx @@ -0,0 +1,131 @@ +import { useEffect, useState } from 'react'; +import "./editor.scss"; +import Editor from '@monaco-editor/react'; +import { Theme } from '@types'; +import { useSearchParams } from 'react-router-dom'; +import { Button } from '@renderer/components'; +import { CheckIcon, CodeIcon, ProjectRoadmapIcon } from '@primer/octicons-react'; +import { useTranslation } from 'react-i18next'; + +const EditorPage = () => { + const [searchParams] = useSearchParams(); + const [theme, setTheme] = useState(null); + const [code, setCode] = useState(''); + const [activeTab, setActiveTab] = useState('code'); + const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + + const themeId = searchParams.get('themeId'); + + const { t } = useTranslation('settings'); + + const handleTabChange = (tab: string) => { + setActiveTab(tab); + }; + + useEffect(() => { + if (themeId) { + window.electron.getCustomThemeById(themeId).then(loadedTheme => { + if (loadedTheme) { + setTheme(loadedTheme); + setCode(loadedTheme.code); + } + }); + } + }, [themeId]); + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if ((event.ctrlKey || event.metaKey) && event.key === 's') { + event.preventDefault(); + handleSave(); + } + }; + + window.addEventListener('keydown', handleKeyDown); + + return () => { + window.removeEventListener('keydown', handleKeyDown); + }; + }, [code, theme]); + + const handleEditorChange = (value: string | undefined) => { + if (value !== undefined) { + setCode(value); + setHasUnsavedChanges(true); + } + }; + + const handleSave = async () => { + if (theme) { + const updatedTheme = { + ...theme, + code: code, + updatedAt: new Date() + }; + + await window.electron.updateCustomTheme(theme.id, updatedTheme); + setHasUnsavedChanges(false); + + if (theme.isActive) { + window.electron.injectCSS(code); + } + } + }; + + return ( +
+
+

{theme?.name}

+ {hasUnsavedChanges && ( +
+
+ )} +
+ + {activeTab === 'code' && ( + + )} + + {activeTab === 'info' && ( +
+ entao mano eu ate fiz isso aqui mas tava feio dms ai deu vergonha e removi kkkk +
+ )} + +
+
+
+ + +
+ + + +
+
+
+ ); +}; + +export default EditorPage; diff --git a/src/renderer/src/pages/settings/aparence/components/theme-actions.tsx b/src/renderer/src/pages/settings/aparence/components/theme-actions.tsx index 589559f6..b9186666 100644 --- a/src/renderer/src/pages/settings/aparence/components/theme-actions.tsx +++ b/src/renderer/src/pages/settings/aparence/components/theme-actions.tsx @@ -11,7 +11,7 @@ interface ThemeActionsProps { } export const ThemeActions = ({ onListUpdated }: ThemeActionsProps) => { - const { t } = useTranslation(); + const { t } = useTranslation('settings'); const [addThemeModalVisible, setAddThemeModalVisible] = useState(false); const [deleteAllThemesModalVisible, setDeleteAllThemesModalVisible] = diff --git a/src/renderer/src/pages/settings/aparence/components/theme-card.tsx b/src/renderer/src/pages/settings/aparence/components/theme-card.tsx index 545aca8a..f15d3f22 100644 --- a/src/renderer/src/pages/settings/aparence/components/theme-card.tsx +++ b/src/renderer/src/pages/settings/aparence/components/theme-card.tsx @@ -6,6 +6,7 @@ import { useNavigate } from "react-router-dom"; import "./theme-card.scss"; import { useState } from "react"; import { DeleteThemeModal } from "../modals/delete-theme-modal"; +import { injectCustomCss, removeCustomCss } from "@renderer/helpers"; interface ThemeCardProps { theme: Theme; @@ -13,11 +14,53 @@ interface ThemeCardProps { } export const ThemeCard = ({ theme, onListUpdated }: ThemeCardProps) => { - const { t } = useTranslation(); + const { t } = useTranslation('settings'); const navigate = useNavigate(); const [deleteThemeModalVisible, setDeleteThemeModalVisible] = useState(false); + const handleSetTheme = async () => { + try { + const currentTheme = await window.electron.getCustomThemeById(theme.id); + + if (!currentTheme) return; + + const activeTheme = await window.electron.getActiveCustomTheme(); + + if (activeTheme) { + removeCustomCss(); + await window.electron.updateCustomTheme(activeTheme.id, { + ...activeTheme, + isActive: false + }); + } + + injectCustomCss(currentTheme.code); + await window.electron.updateCustomTheme(currentTheme.id, { + ...currentTheme, + isActive: true + }); + + onListUpdated(); + } catch (error) { + console.error(error); + } + }; + + const handleUnsetTheme = async () => { + try { + removeCustomCss(); + await window.electron.updateCustomTheme(theme.id, { + ...theme, + isActive: false + }); + + onListUpdated(); + } catch (error) { + console.error(error); + } + }; + return ( <> { onClose={() => setDeleteThemeModalVisible(false)} onThemeDeleted={onListUpdated} themeId={theme.id} + themeName={theme.name} />
{
- {theme.author && theme.author && ( + {theme.authorName && (

{t("by")} @@ -56,7 +100,7 @@ export const ThemeCard = ({ theme, onListUpdated }: ThemeCardProps) => { className="theme-card__author__name" onClick={() => navigate(`/profile/${theme.author}`)} > - {theme.author} + {theme.authorName}

)} @@ -64,14 +108,22 @@ export const ThemeCard = ({ theme, onListUpdated }: ThemeCardProps) => {
{theme.isActive ? ( - + ) : ( - + )}
- diff --git a/src/renderer/src/pages/settings/aparence/modals/add-theme-modal.tsx b/src/renderer/src/pages/settings/aparence/modals/add-theme-modal.tsx index c1005510..eb61c139 100644 --- a/src/renderer/src/pages/settings/aparence/modals/add-theme-modal.tsx +++ b/src/renderer/src/pages/settings/aparence/modals/add-theme-modal.tsx @@ -4,6 +4,8 @@ import { Button } from "@renderer/components/button/button"; import { useTranslation } from "react-i18next"; import { useState } from "react"; import "./modals.scss"; +import { useUserDetails } from "@renderer/hooks"; +import { Theme } from "@types"; interface AddThemeModalProps { visible: boolean; @@ -17,6 +19,7 @@ export const AddThemeModal = ({ onThemeAdded, }: AddThemeModalProps) => { const { t } = useTranslation("settings"); + const { userDetails } = useUserDetails(); const [name, setName] = useState(""); const [error, setError] = useState(""); @@ -32,15 +35,20 @@ export const AddThemeModal = ({ return; } - const theme = { + const theme: Theme = { id: crypto.randomUUID(), name, isActive: false, + author: userDetails?.id || undefined, + authorName: userDetails?.username || undefined, colors: { accent: "#c0c1c7", background: "#1c1c1c", surface: "#151515", }, + code: "", + createdAt: new Date(), + updatedAt: new Date(), }; await window.electron.addCustomTheme(theme); @@ -53,8 +61,8 @@ export const AddThemeModal = ({ return (
diff --git a/src/renderer/src/pages/settings/aparence/modals/delete-theme-modal.tsx b/src/renderer/src/pages/settings/aparence/modals/delete-theme-modal.tsx index 0a339627..e0baa25e 100644 --- a/src/renderer/src/pages/settings/aparence/modals/delete-theme-modal.tsx +++ b/src/renderer/src/pages/settings/aparence/modals/delete-theme-modal.tsx @@ -8,6 +8,7 @@ interface DeleteThemeModalProps { onClose: () => void; themeId: string; onThemeDeleted: () => void; + themeName: string; } export const DeleteThemeModal = ({ @@ -15,6 +16,7 @@ export const DeleteThemeModal = ({ onClose, themeId, onThemeDeleted, + themeName, }: DeleteThemeModalProps) => { const { t } = useTranslation("settings"); @@ -27,7 +29,7 @@ export const DeleteThemeModal = ({
diff --git a/src/renderer/src/pages/settings/aparence/settings-appearance.tsx b/src/renderer/src/pages/settings/aparence/settings-appearance.tsx index 636a424a..3c038d99 100644 --- a/src/renderer/src/pages/settings/aparence/settings-appearance.tsx +++ b/src/renderer/src/pages/settings/aparence/settings-appearance.tsx @@ -25,13 +25,15 @@ export const SettingsAppearance = () => { {!themes.length ? ( ) : ( - themes.map((theme) => ( - - )) + [...themes] + .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()) + .map((theme) => ( + + )) )}
diff --git a/src/types/theme.types.ts b/src/types/theme.types.ts index bd77bb75..6db5eb84 100644 --- a/src/types/theme.types.ts +++ b/src/types/theme.types.ts @@ -15,7 +15,8 @@ export interface Theme { optional2?: HexColorType; }; description?: string; - author: number; + author: string | undefined; + authorName: string | undefined; isActive: boolean; code: string; createdAt: Date; diff --git a/yarn.lock b/yarn.lock index 3a4656b4..3493d75e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1917,6 +1917,20 @@ lodash "^4.17.15" tmp-promise "^3.0.2" +"@monaco-editor/loader@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.4.0.tgz#f08227057331ec890fa1e903912a5b711a2ad558" + integrity sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg== + dependencies: + state-local "^1.0.6" + +"@monaco-editor/react@^4.6.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.6.0.tgz#bcc68671e358a21c3814566b865a54b191e24119" + integrity sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw== + dependencies: + "@monaco-editor/loader" "^1.4.0" + "@napi-rs/nice-android-arm-eabi@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.0.1.tgz#9a0cba12706ff56500df127d6f4caf28ddb94936" @@ -8719,6 +8733,11 @@ stat-mode@^1.0.0: resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-1.0.0.tgz#68b55cb61ea639ff57136f36b216a291800d1465" integrity sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg== +state-local@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/state-local/-/state-local-1.0.7.tgz#da50211d07f05748d53009bee46307a37db386d5" + integrity sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w== + "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"