diff --git a/src/renderer/src/app.css.ts b/src/renderer/src/app.css.ts new file mode 100644 index 00000000..25c453c8 --- /dev/null +++ b/src/renderer/src/app.css.ts @@ -0,0 +1,134 @@ +import { + ComplexStyleRule, + createContainer, + globalStyle, + style, +} from "@vanilla-extract/css"; +import { SPACING_UNIT, vars } from "./theme.css"; + +export const appContainer = createContainer(); + +globalStyle("*", { + boxSizing: "border-box", +}); + +globalStyle("::-webkit-scrollbar", { + width: "9px", + backgroundColor: vars.color.darkBackground, +}); + +globalStyle("::-webkit-scrollbar-track", { + backgroundColor: "rgba(255, 255, 255, 0.03)", +}); + +globalStyle("::-webkit-scrollbar-thumb", { + backgroundColor: "rgba(255, 255, 255, 0.08)", + borderRadius: "24px", +}); + +globalStyle("::-webkit-scrollbar-thumb:hover", { + backgroundColor: "rgba(255, 255, 255, 0.16)", +}); + +globalStyle("html, body, #root, main", { + height: "100%", +}); + +globalStyle("body", { + overflow: "hidden", + userSelect: "none", + fontFamily: "Noto Sans, sans-serif", + fontSize: vars.size.body, + color: vars.color.body, + margin: "0", +}); + +globalStyle("button", { + padding: "0", + backgroundColor: "transparent", + border: "none", + fontFamily: "inherit", +}); + +globalStyle("h1, h2, h3, h4, h5, h6, p", { + margin: 0, +}); + +globalStyle("p", { + lineHeight: "20px", +}); + +globalStyle("#root, main", { + display: "flex", +}); + +globalStyle("#root", { + flexDirection: "column", +}); + +globalStyle("main", { + overflow: "hidden", +}); + +globalStyle( + "input::-webkit-outer-spin-button, input::-webkit-inner-spin-button", + { + WebkitAppearance: "none", + margin: "0", + } +); + +globalStyle("label", { + fontSize: vars.size.body, +}); + +globalStyle("input[type=number]", { + MozAppearance: "textfield", +}); + +globalStyle("img", { + WebkitUserDrag: "none", +} as Record); + +globalStyle("progress[value]", { + WebkitAppearance: "none", +}); + +export const container = style({ + width: "100%", + height: "100%", + overflow: "hidden", + display: "flex", + flexDirection: "column", + containerName: appContainer, + containerType: "inline-size", +}); + +export const content = style({ + overflowY: "auto", + alignItems: "center", + display: "flex", + flexDirection: "column", + position: "relative", + height: "100%", + background: `linear-gradient(0deg, ${vars.color.darkBackground} 50%, ${vars.color.background} 100%)`, +}); + +export const titleBar = style({ + display: "flex", + width: "100%", + height: "35px", + minHeight: "35px", + backgroundColor: vars.color.darkBackground, + alignItems: "center", + padding: `0 ${SPACING_UNIT * 2}px`, + WebkitAppRegion: "drag", + zIndex: "4", + borderBottom: `1px solid ${vars.color.border}`, +} as ComplexStyleRule); + +export const cloudText = style({ + background: "linear-gradient(270deg, #16B195 50%, #3E62C0 100%)", + backgroundClip: "text", + color: "transparent", +}); diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index b011b196..2237cb8a 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -30,17 +30,19 @@ import { UserFriendModal } from "./pages/shared-modals/user-friend-modal"; import { downloadSourcesWorker } from "./workers"; import { repacksContext } from "./context"; import { logger } from "./logger"; +import { insertCustomStyles } from "./helpers"; export interface AppProps { children: React.ReactNode; } -console.log(import.meta.env); - Intercom({ app_id: import.meta.env.RENDERER_VITE_INTERCOM_APP_ID, }); +const customStyles = window.localStorage.getItem("customStyles"); +insertCustomStyles(customStyles || ""); + export function App() { const contentRef = useRef(null); const { updateLibrary, library } = useLibrary(); diff --git a/src/renderer/src/components/sidebar/sidebar.css.ts b/src/renderer/src/components/sidebar/sidebar.css.ts new file mode 100644 index 00000000..4fd4b981 --- /dev/null +++ b/src/renderer/src/components/sidebar/sidebar.css.ts @@ -0,0 +1,152 @@ +import { style } from "@vanilla-extract/css"; +import { recipe } from "@vanilla-extract/recipes"; + +import { SPACING_UNIT, vars } from "../../theme.css"; + +export const sidebar = recipe({ + base: { + backgroundColor: vars.color.darkBackground, + color: vars.color.muted, + flexDirection: "column", + display: "flex", + transition: "opacity ease 0.2s", + borderRight: `solid 1px ${vars.color.border}`, + position: "relative", + overflow: "hidden", + justifyContent: "space-between", + }, + variants: { + resizing: { + true: { + opacity: vars.opacity.active, + pointerEvents: "none", + }, + }, + darwin: { + true: { + paddingTop: `${SPACING_UNIT * 6}px`, + }, + false: { + paddingTop: `${SPACING_UNIT}px`, + }, + }, + }, +}); + +export const content = style({ + display: "flex", + flexDirection: "column", + padding: `${SPACING_UNIT * 2}px`, + gap: `${SPACING_UNIT * 2}px`, + width: "100%", + overflow: "auto", +}); + +export const handle = style({ + width: "5px", + height: "100%", + cursor: "col-resize", + position: "absolute", + right: "0", +}); + +export const menu = style({ + listStyle: "none", + padding: "0", + margin: "0", + gap: `${SPACING_UNIT / 2}px`, + display: "flex", + flexDirection: "column", + overflow: "hidden", +}); + +export const menuItem = recipe({ + base: { + transition: "all ease 0.1s", + cursor: "pointer", + textWrap: "nowrap", + display: "flex", + color: vars.color.muted, + borderRadius: "4px", + ":hover": { + backgroundColor: "rgba(255, 255, 255, 0.15)", + }, + }, + variants: { + active: { + true: { + backgroundColor: "rgba(255, 255, 255, 0.1)", + }, + }, + muted: { + true: { + opacity: vars.opacity.disabled, + ":hover": { + opacity: "1", + }, + }, + }, + }, +}); + +export const menuItemButton = style({ + color: "inherit", + display: "flex", + alignItems: "center", + gap: `${SPACING_UNIT}px`, + cursor: "pointer", + overflow: "hidden", + width: "100%", + padding: `9px ${SPACING_UNIT}px`, +}); + +export const menuItemButtonLabel = style({ + textOverflow: "ellipsis", + overflow: "hidden", +}); + +export const gameIcon = style({ + width: "20px", + height: "20px", + minWidth: "20px", + minHeight: "20px", + borderRadius: "4px", + backgroundSize: "cover", +}); + +export const sectionTitle = style({ + textTransform: "uppercase", + fontWeight: "bold", +}); + +export const section = style({ + gap: `${SPACING_UNIT * 2}px`, + display: "flex", + flexDirection: "column", + paddingBottom: `${SPACING_UNIT}px`, +}); + +export const helpButton = style({ + color: vars.color.muted, + padding: `${SPACING_UNIT}px ${SPACING_UNIT * 2}px`, + gap: "9px", + display: "flex", + alignItems: "center", + cursor: "pointer", + borderTop: `solid 1px ${vars.color.border}`, + transition: "background-color ease 0.1s", + ":hover": { + backgroundColor: "rgba(255, 255, 255, 0.15)", + }, +}); + +export const helpButtonIcon = style({ + background: "linear-gradient(0deg, #16B195 50%, #3E62C0 100%)", + width: "24px", + height: "24px", + display: "flex", + alignItems: "center", + justifyContent: "center", + color: "#fff", + borderRadius: "50%", +}); diff --git a/src/renderer/src/helpers.ts b/src/renderer/src/helpers.ts index a241bf47..16504081 100644 --- a/src/renderer/src/helpers.ts +++ b/src/renderer/src/helpers.ts @@ -50,3 +50,16 @@ export const buildGameAchievementPath = ( export const darkenColor = (color: string, amount: number, alpha: number = 1) => new Color(color).darken(amount).alpha(alpha).toString(); + +export const insertCustomStyles = (styles: string) => { + const existingStyles = document.getElementById("custom-styles"); + + if (existingStyles) { + existingStyles.innerHTML = styles; + } else { + const style = document.createElement("style"); + style.id = "custom-styles"; + style.innerHTML = styles; + document.head.appendChild(style); + } +}; diff --git a/src/renderer/src/pages/settings/settings-general.tsx b/src/renderer/src/pages/settings/settings-general.tsx index 6737c4b7..2f53b6ab 100644 --- a/src/renderer/src/pages/settings/settings-general.tsx +++ b/src/renderer/src/pages/settings/settings-general.tsx @@ -11,6 +11,7 @@ import { changeLanguage } from "i18next"; import languageResources from "@locales"; import { orderBy } from "lodash-es"; import { settingsContext } from "@renderer/context"; +import { insertCustomStyles } from "@renderer/helpers"; interface LanguageOption { option: string; @@ -32,6 +33,8 @@ export function SettingsGeneral() { repackUpdatesNotificationsEnabled: false, achievementNotificationsEnabled: false, language: "", + + customStyles: window.localStorage.getItem("customStyles") || "", }); const [languageOptions, setLanguageOptions] = useState([]); @@ -111,6 +114,19 @@ export function SettingsGeneral() { } } + const handleSaveStylesClick = () => { + const existingStyles = document.getElementById("custom-styles"); + if (existingStyles) { + existingStyles.remove(); + } + + const css = document.querySelector("textarea")?.value; + if (css) { + insertCustomStyles(css); + window.localStorage.setItem("customStyles", css); + } + }; + return ( <> + +