Skip to content

Commit

Permalink
[UX] CustomCSS field for advance persistent styling (#4282)
Browse files Browse the repository at this point in the history
CustomCSS field for advance persistent styling

Co-authored-by: Flávio Fearn <[email protected]>
  • Loading branch information
arielj and flavioislima authored Feb 25, 2025
1 parent 60afb3c commit 78151d5
Show file tree
Hide file tree
Showing 12 changed files with 99 additions and 0 deletions.
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
<body>
<div id="root"></div>
<div class="simple-keyboard"></div>
<style id="customCSS"></style>
</body>
</html>
6 changes: 6 additions & 0 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,9 @@
"change-target-exe": "Select an alternative EXE to run",
"checkForUpdatesOnStartup": "Check for Heroic Updates on Startup",
"crossover-version": "Crossover/Wine Version",
"custom_css": {
"warning": "Warning: this applies to the whole frontend. That means the wrong style can render Heroic unusable. If needed, the setting can be changes manually in `~/.config/heroic/config.json`. Do NOT copy styles from sources you don't trust."
},
"custom_themes_path": "Custom Themes Path",
"customWineProton": "Custom Wine/Proton Paths",
"darktray": "Use Dark Tray Icon",
Expand Down Expand Up @@ -778,6 +781,9 @@
"clear-cache": "Clear Heroic Cache",
"copiedToClipboard": "Copied to Clipboard!",
"copyToClipboard": "Copy All Settings to Clipboard",
"custom_css": {
"title": "Custom CSS Style"
},
"default_hint": "Changes in this section only apply as default values when installing games. If you want to change the settings of an already installed game, use the Settings button in the game page.",
"eacRuntime": {
"gameModeRequired": {
Expand Down
2 changes: 2 additions & 0 deletions src/backend/api/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ export const getThemeCSS = async (theme: string) =>

export const getCustomThemes = async () => ipcRenderer.invoke('getCustomThemes')

export const getCustomCSS = async () => ipcRenderer.invoke('getCustomCSS')

export const setTitleBarOverlay = (options: TitleBarOverlay) =>
ipcRenderer.send('setTitleBarOverlay', options)

Expand Down
4 changes: 4 additions & 0 deletions src/backend/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,10 @@ ipcMain.handle('getThemeCSS', async (event, theme) => {
return readFileSync(cssPath, 'utf-8')
})

ipcMain.handle('getCustomCSS', async () => {
return GlobalConfig.get().getSettings().customCSS
})

ipcMain.on('setTitleBarOverlay', (e, args) => {
const mainWindow = getMainWindow()
if (typeof mainWindow?.['setTitleBarOverlay'] === 'function') {
Expand Down
1 change: 1 addition & 0 deletions src/common/typedefs/ipcBridge.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ interface AsyncIPCFunctions {
) => Promise<false | [string, UploadedLogData]>
deleteUploadedLogFile: (url: string) => Promise<boolean>
getUploadedLogFiles: () => Promise<Record<string, UploadedLogData>>
getCustomCSS: () => Promise<string>
}

// This is quite ugly & throws a lot of errors in a regular .ts file
Expand Down
1 change: 1 addition & 0 deletions src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export interface AppSettings extends GameSettings {
autoUpdateGames: boolean
checkForUpdatesOnStartup: boolean
checkUpdatesInterval: number
customCSS: string
customThemesPath: string
customWinePaths: string[]
darkTrayIcon: boolean
Expand Down
11 changes: 11 additions & 0 deletions src/frontend/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ const languageCode: string =
configStore.get_nodefault('language') ?? storage.getItem('language') ?? 'en'
configStore.set('language', languageCode)

window.setCustomCSS = (cssString: string) => {
const style = document.createElement('style')
style.innerHTML = cssString
document.getElementById('customCSS')!.innerText = style.innerText
}

window.api
.getCustomCSS()
.then(window.setCustomCSS)
.catch(() => {})

i18next
// load translation using http -> see /public/locales
// learn more: https://github.com/i18next/i18next-http-backend
Expand Down
34 changes: 34 additions & 0 deletions src/frontend/screens/Settings/components/CustomCSS.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { ChangeEvent } from 'react'
import { useTranslation } from 'react-i18next'
import useSetting from 'frontend/hooks/useSetting'

const CustomCSS = () => {
const { t } = useTranslation()
const [customCSS, setCustomCSS] = useSetting('customCSS', '')

const handleCustomCSS = (event: ChangeEvent<HTMLTextAreaElement>) => {
setCustomCSS(event.currentTarget.value)
window.setCustomCSS(event.currentTarget.value)
}

return (
<>
<h3>{t('settings.custom_css.title', 'Custom CSS Style')}</h3>

<textarea
defaultValue={customCSS}
className="customCSSArea"
onChange={handleCustomCSS}
rows={5}
/>
<div className="customCSSWarning">
{t(
'setting.custom_css.warning',
"Warning: this applies to the whole frontend. That means the wrong style can render Heroic unusable. If needed, the setting can be changes manually in `~/.config/heroic/config.json`. Do NOT copy styles from sources you don't trust."
)}
</div>
</>
)
}

export default CustomCSS
1 change: 1 addition & 0 deletions src/frontend/screens/Settings/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export { default as BattlEyeRuntime } from './BattlEyeRuntime'
export { default as CheckUpdatesOnStartup } from './CheckUpdatesOnStartup'
export { default as CrossoverBottle } from './CrossoverBottle'
export { default as CustomWineProton } from './CustomWineProton'
export { default as CustomCSS } from './CustomCSS'
export { default as DefaultInstallPath } from './DefaultInstallPath'
export { default as DefaultSteamPath } from './DefaultSteamPath'
export { default as DisableController } from './DisableController'
Expand Down
31 changes: 31 additions & 0 deletions src/frontend/screens/Settings/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,34 @@ a {
.infoBox.saves-warning svg {
padding-inline-end: 10px;
}

.customCSSArea {
border-radius: var(--space-3xs);
width: 100%;
min-height: 200px;
background: var(--input-background);
font-family: var(--primary-font-family), 'Noto Color Emoji';
font-weight: normal;
font-size: var(--text-md);
line-height: 19px;
color: var(--text-secondary);
border: none;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
cursor: pointer;
margin: 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
min-width: 100px;
padding: 0.5rem;
}

.customCSSWarning {
background: var(--anticheat-denied, var(--status-danger));
color: var(--brand-text-01);
text-align: start;
padding: var(--space-md);
display: flex;
gap: var(--space-md);
border-radius: var(--space-3xs);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
AltLegendaryBin,
AltNileBin,
ClearCache,
CustomCSS,
DisableLogs,
DownloadNoHTTPS,
ExperimentalFeatures,
Expand Down Expand Up @@ -299,6 +300,11 @@ export default function AdvancedSetting() {
<hr />
</div>

<div className="advancedSetting">
<CustomCSS />
<hr />
</div>

<div className="advancedSetting">
<ClearCache />
<hr />
Expand Down
1 change: 1 addition & 0 deletions src/frontend/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ declare global {
setTheme: (themeClass: string) => void
isSteamDeckGameMode: boolean
platform: NodeJS.Platform
setCustomCSS: (cssString: string) => void
}

interface WindowEventMap {
Expand Down

0 comments on commit 78151d5

Please sign in to comment.