mirror of
https://github.com/Myxelium/Bridge-Multi.git
synced 2026-07-07 10:35:09 +00:00
Add custom themes
This commit is contained in:
@@ -35,6 +35,7 @@
|
||||
"@angular/router": "^18.0.6",
|
||||
"bootstrap-icons": "^1.11.3",
|
||||
"bottleneck": "^2.19.5",
|
||||
"culori": "^4.0.1",
|
||||
"dayjs": "^1.11.11",
|
||||
"electron-unhandled": "^5.0.0",
|
||||
"electron-updater": "^6.2.1",
|
||||
@@ -59,6 +60,7 @@
|
||||
"@angular/cli": "^18.0.7",
|
||||
"@angular/compiler-cli": "^18.0.6",
|
||||
"@angular/language-service": "^18.0.6",
|
||||
"@types/culori": "^2.1.1",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/lodash": "^4.17.6",
|
||||
"@types/node": "^18.16.0",
|
||||
|
||||
17
pnpm-lock.yaml
generated
17
pnpm-lock.yaml
generated
@@ -38,6 +38,9 @@ importers:
|
||||
bottleneck:
|
||||
specifier: ^2.19.5
|
||||
version: 2.19.5
|
||||
culori:
|
||||
specifier: ^4.0.1
|
||||
version: 4.0.1
|
||||
dayjs:
|
||||
specifier: ^1.11.11
|
||||
version: 1.11.11
|
||||
@@ -105,6 +108,9 @@ importers:
|
||||
'@angular/language-service':
|
||||
specifier: ^18.0.6
|
||||
version: 18.0.6
|
||||
'@types/culori':
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
'@types/fs-extra':
|
||||
specifier: ^11.0.4
|
||||
version: 11.0.4
|
||||
@@ -1173,6 +1179,9 @@ packages:
|
||||
'@types/cacheable-request@6.0.3':
|
||||
resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==}
|
||||
|
||||
'@types/culori@2.1.1':
|
||||
resolution: {integrity: sha512-NzLYD0vNHLxTdPp8+RlvGbR2NfOZkwxcYGFwxNtm+WH2NuUNV8785zv1h0sulFQ5aFQ9n/jNDUuJeo3Bh7+oFA==}
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||
|
||||
@@ -1824,6 +1833,10 @@ packages:
|
||||
resolution: {integrity: sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
culori@4.0.1:
|
||||
resolution: {integrity: sha512-LSnjA6HuIUOlkfKVbzi2OlToZE8OjFi667JWN9qNymXVXzGDmvuP60SSgC+e92sd7B7158f7Fy3Mb6rXS5EDPw==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
daisyui@4.12.10:
|
||||
resolution: {integrity: sha512-jp1RAuzbHhGdXmn957Z2XsTZStXGHzFfF0FgIOZj3Wv9sH7OZgLfXTRZNfKVYxltGUOBsG1kbWAdF5SrqjebvA==}
|
||||
engines: {node: '>=16.9.0'}
|
||||
@@ -5161,6 +5174,8 @@ snapshots:
|
||||
'@types/node': 18.16.0
|
||||
'@types/responselike': 1.0.3
|
||||
|
||||
'@types/culori@2.1.1': {}
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
dependencies:
|
||||
'@types/ms': 0.7.34
|
||||
@@ -5946,6 +5961,8 @@ snapshots:
|
||||
|
||||
culori@3.3.0: {}
|
||||
|
||||
culori@4.0.1: {}
|
||||
|
||||
daisyui@4.12.10(postcss@8.4.39):
|
||||
dependencies:
|
||||
css-selector-tokenizer: 0.8.0
|
||||
|
||||
@@ -17,11 +17,24 @@
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full max-w-xs">
|
||||
<div class="form-control w-full max-w-xs">
|
||||
<div class="label">
|
||||
<span class="label-text">Appearance</span>
|
||||
</div>
|
||||
<div class="flex gap-3 items-center">
|
||||
<div class="join join-vertical">
|
||||
<div class="tooltip tooltip-right" data-tip="Zoom In (ctrl +)">
|
||||
<button class="join-item btn btn-square btn-neutral btn-xs" (click)="settingsService.zoomIn()">
|
||||
<i class="bi bi-zoom-in"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="tooltip tooltip-right" data-tip="Zoom Out (ctrl -)">
|
||||
<button class="join-item btn btn-square btn-neutral btn-xs" (click)="settingsService.zoomOut()">
|
||||
<i class="bi bi-zoom-out"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@if (!settingsService.customTheme) {
|
||||
<div class="dropdown dropdown-hover">
|
||||
<label tabindex="0" class="btn btn-neutral">{{ capitalize(settingsService.theme) }}</label>
|
||||
<ul tabindex="0" class="dropdown-content z-[2] menu p-2 shadow bg-neutral text-neutral-content rounded-box w-36">
|
||||
@@ -49,20 +62,12 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="join join-vertical">
|
||||
<div class="tooltip tooltip-right" data-tip="Zoom In (ctrl +)">
|
||||
<button class="join-item btn btn-square btn-neutral btn-xs" (click)="settingsService.zoomIn()">
|
||||
<i class="bi bi-zoom-in"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="tooltip tooltip-right" data-tip="Zoom Out (ctrl -)">
|
||||
<button class="join-item btn btn-square btn-neutral btn-xs" (click)="settingsService.zoomOut()">
|
||||
<i class="bi bi-zoom-out"></i>
|
||||
</button>
|
||||
<button (click)="getCustomTheme()" class="btn btn-primary">Use custom theme</button>
|
||||
} @else {
|
||||
<button (click)="clearCustomTheme()" class="btn btn-neutral">Clear custom theme</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div class="form-control">
|
||||
<div class="label">
|
||||
|
||||
@@ -76,6 +76,31 @@ export class SettingsComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
async getCustomTheme() {
|
||||
const result = await window.electron.invoke.showOpenDialog({
|
||||
title: 'Choose custom theme',
|
||||
defaultPath: this.settingsService.libraryDirectory || '',
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{ name: "Themes", extensions: ["json"] },
|
||||
],
|
||||
})
|
||||
|
||||
if (result.canceled === false) {
|
||||
const newThemeColors = await window.electron.invoke.getThemeColors(result.filePaths[0])
|
||||
|
||||
if (newThemeColors) {
|
||||
this.settingsService.customTheme = newThemeColors
|
||||
} else {
|
||||
alert(`ERROR: ${result.filePaths[0]} was not a valid JSON file.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearCustomTheme() {
|
||||
this.settingsService.customTheme = null
|
||||
}
|
||||
|
||||
openLibraryDirectory() {
|
||||
if (this.settingsService.libraryDirectory) {
|
||||
window.electron.emit.showFolder(this.settingsService.libraryDirectory)
|
||||
|
||||
@@ -4,7 +4,9 @@ import { Inject, Injectable } from '@angular/core'
|
||||
import _ from 'lodash'
|
||||
import { Difficulty, Instrument } from 'scan-chart'
|
||||
|
||||
import { ThemeColors } from '../../../../src-shared/interfaces/theme.interface.js'
|
||||
import { Settings, themes } from '../../../../src-shared/Settings.js'
|
||||
import { colorNames, convertColorFormat } from '../../../../src-shared/UtilFunctions.js'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -19,18 +21,20 @@ export class SettingsService {
|
||||
|
||||
async loadSettings() {
|
||||
this.settings = await window.electron.invoke.getSettings()
|
||||
if (!themes.includes(this.settings.theme)) {
|
||||
if (this.settings.customTheme) {
|
||||
setThemeColors(this.settings.customTheme)
|
||||
} else if (!themes.includes(this.settings.theme)) {
|
||||
this.changeTheme('dark')
|
||||
} else {
|
||||
this.changeTheme(this.settings.theme)
|
||||
}
|
||||
}
|
||||
|
||||
saveSettings() {
|
||||
private saveSettings() {
|
||||
window.electron.emit.setSettings(this.settings)
|
||||
}
|
||||
|
||||
changeTheme(theme: typeof themes[number]) {
|
||||
private changeTheme(theme: typeof themes[number]) {
|
||||
this.document.documentElement.setAttribute('data-theme', theme)
|
||||
}
|
||||
|
||||
@@ -50,7 +54,6 @@ export class SettingsService {
|
||||
this.saveSettings()
|
||||
}
|
||||
|
||||
// Individual getters/setters
|
||||
get libraryDirectory() {
|
||||
return this.settings.libraryPath
|
||||
}
|
||||
@@ -70,9 +73,27 @@ export class SettingsService {
|
||||
get theme() {
|
||||
return this.settings.theme
|
||||
}
|
||||
set theme(newValue: typeof themes[number]) {
|
||||
this.settings.theme = newValue
|
||||
this.changeTheme(newValue)
|
||||
set theme(value: typeof themes[number]) {
|
||||
this.settings.theme = value
|
||||
if (!this.settings.customTheme) {
|
||||
this.changeTheme(value)
|
||||
}
|
||||
this.saveSettings()
|
||||
}
|
||||
get customTheme() {
|
||||
return this.settings.customTheme
|
||||
}
|
||||
set customTheme(value: ThemeColors | null) {
|
||||
if (value) {
|
||||
const failed = setThemeColors(value)
|
||||
if (failed) { return }
|
||||
} else {
|
||||
for (const themeColor in colorNames) {
|
||||
document.documentElement.style.removeProperty(colorNames[themeColor])
|
||||
}
|
||||
this.changeTheme(this.settings.theme)
|
||||
}
|
||||
this.settings.customTheme = value
|
||||
this.saveSettings()
|
||||
}
|
||||
|
||||
@@ -108,3 +129,16 @@ export class SettingsService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setThemeColors(themeColors: ThemeColors) {
|
||||
try {
|
||||
const result = convertColorFormat(themeColors)
|
||||
for (const cssKey in result) {
|
||||
document.documentElement.style.setProperty(cssKey, result[cssKey])
|
||||
}
|
||||
return false
|
||||
} catch (err) {
|
||||
alert(`ERROR: the provided color theme is improperly formatted.`)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { IpcInvokeHandlers, IpcToMainEmitHandlers } from '../src-shared/interfac
|
||||
import { download } from './ipc/DownloadHandler.ipc.js'
|
||||
import { getSettings, setSettings } from './ipc/SettingsHandler.ipc.js'
|
||||
import { downloadUpdate, getCurrentVersion, getUpdateAvailable, quitAndInstall, retryUpdate } from './ipc/UpdateHandler.ipc.js'
|
||||
import { getPlatform, isMaximized, maximize, minimize, openUrl, quit, restore, showFile, showFolder, showOpenDialog, toggleDevTools } from './ipc/UtilHandlers.ipc.js'
|
||||
import { getPlatform, getThemeColors, isMaximized, maximize, minimize, openUrl, quit, restore, showFile, showFolder, showOpenDialog, toggleDevTools } from './ipc/UtilHandlers.ipc.js'
|
||||
|
||||
export function getIpcInvokeHandlers(): IpcInvokeHandlers {
|
||||
return {
|
||||
@@ -12,6 +12,7 @@ export function getIpcInvokeHandlers(): IpcInvokeHandlers {
|
||||
getUpdateAvailable,
|
||||
isMaximized,
|
||||
showOpenDialog,
|
||||
getThemeColors,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { app, dialog, OpenDialogOptions, shell } from 'electron'
|
||||
import fsExtra from 'fs-extra'
|
||||
|
||||
import { ThemeColors } from '../../src-shared/interfaces/theme.interface.js'
|
||||
import { mainWindow } from '../main.js'
|
||||
|
||||
/**
|
||||
@@ -48,3 +50,11 @@ export function showFile(filePath: string) {
|
||||
export async function getPlatform() {
|
||||
return process.platform
|
||||
}
|
||||
|
||||
export async function getThemeColors(path: string) {
|
||||
try {
|
||||
return await fsExtra.readJson(path) as ThemeColors
|
||||
} catch (err) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ const electronApi: ContextBridgeApi = {
|
||||
getUpdateAvailable: getInvoker('getUpdateAvailable'),
|
||||
isMaximized: getInvoker('isMaximized'),
|
||||
showOpenDialog: getInvoker('showOpenDialog'),
|
||||
getThemeColors: getInvoker('getThemeColors'),
|
||||
},
|
||||
emit: {
|
||||
download: getEmitter('download'),
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Difficulty, Instrument } from 'scan-chart'
|
||||
|
||||
import { ThemeColors } from './interfaces/theme.interface'
|
||||
|
||||
export const themes = [
|
||||
'business',
|
||||
'dark',
|
||||
@@ -23,6 +25,7 @@ export const themes = [
|
||||
export interface Settings {
|
||||
downloadVideos: boolean // If background videos should be downloaded
|
||||
theme: typeof themes[number] // The name of the currently enabled UI theme
|
||||
customTheme: ThemeColors | null // The colors of a custom theme
|
||||
libraryPath: string | undefined // The path to the user's library
|
||||
isSng: boolean // If the chart should be downloaded as a .sng file or as a chart folder
|
||||
isCompactTable: boolean // If the search result table should have reduced padding
|
||||
@@ -37,6 +40,7 @@ export interface Settings {
|
||||
export const defaultSettings: Settings = {
|
||||
downloadVideos: true,
|
||||
theme: 'dark',
|
||||
customTheme: null,
|
||||
libraryPath: undefined,
|
||||
isSng: false,
|
||||
isCompactTable: false,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { interpolate as culoriInterpolate, oklch, wcagContrast } from 'culori'
|
||||
import _ from 'lodash'
|
||||
import { Difficulty, Instrument } from 'scan-chart'
|
||||
|
||||
import { ChartData } from './interfaces/search.interface'
|
||||
import { ThemeColors } from './interfaces/theme.interface'
|
||||
|
||||
// WARNING: do not import anything related to Electron; the code will not compile correctly.
|
||||
|
||||
@@ -166,3 +168,179 @@ export function hasIssues(chart: Pick<ChartData, 'metadataIssues' | 'folderIssue
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
export const colorNames = {
|
||||
"primary": "--p",
|
||||
"primary-content": "--pc",
|
||||
|
||||
"secondary": "--s",
|
||||
"secondary-content": "--sc",
|
||||
|
||||
"accent": "--a",
|
||||
"accent-content": "--ac",
|
||||
|
||||
"neutral": "--n",
|
||||
"neutral-content": "--nc",
|
||||
|
||||
"base-100": "--b1",
|
||||
"base-200": "--b2",
|
||||
"base-300": "--b3",
|
||||
"base-content": "--bc",
|
||||
|
||||
"info": "--in",
|
||||
"info-content": "--inc",
|
||||
|
||||
"success": "--su",
|
||||
"success-content": "--suc",
|
||||
|
||||
"warning": "--wa",
|
||||
"warning-content": "--wac",
|
||||
|
||||
"error": "--er",
|
||||
"error-content": "--erc",
|
||||
} as { [colorName: string]: string }
|
||||
const defaultVariables = {
|
||||
"--rounded-box": "1rem",
|
||||
"--rounded-btn": "0.5rem",
|
||||
"--rounded-badge": "1.9rem",
|
||||
"--animation-btn": "0.25s",
|
||||
"--animation-input": ".2s",
|
||||
"--btn-focus-scale": "0.95",
|
||||
"--border-btn": "1px",
|
||||
"--tab-border": "1px",
|
||||
"--tab-radius": "0.5rem",
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
|
||||
export function convertColorFormat(input: ThemeColors) {
|
||||
if (typeof input !== "object" || input === null) {
|
||||
return input
|
||||
}
|
||||
|
||||
const resultObj: { [cssKey: string]: string } = {}
|
||||
|
||||
for (const [rule, value] of Object.entries(input)) {
|
||||
if (Object.hasOwn(colorNames, rule)) {
|
||||
const colorObj = oklch(value)
|
||||
resultObj[colorNames[rule]] = colorObjToString(colorObj!)
|
||||
} else {
|
||||
resultObj[rule] = value
|
||||
}
|
||||
|
||||
// auto generate base colors
|
||||
if (!Object.hasOwn(input, "base-100")) {
|
||||
resultObj["--b1"] = "100% 0 0"
|
||||
}
|
||||
if (!Object.hasOwn(input, "base-200")) {
|
||||
resultObj["--b2"] = generateDarkenColorFrom(input["base-100"], 0.07)
|
||||
}
|
||||
if (!Object.hasOwn(input, "base-300")) {
|
||||
if (Object.hasOwn(input, "base-200")) {
|
||||
resultObj["--b3"] = generateDarkenColorFrom(input["base-200"], 0.07)
|
||||
} else {
|
||||
resultObj["--b3"] = generateDarkenColorFrom(input["base-100"], 0.14)
|
||||
}
|
||||
}
|
||||
|
||||
// auto generate state colors
|
||||
if (!Object.hasOwn(input, "info")) {
|
||||
resultObj["--in"] = "72.06% 0.191 231.6"
|
||||
}
|
||||
if (!Object.hasOwn(input, "success")) {
|
||||
resultObj["--su"] = "64.8% 0.150 160"
|
||||
}
|
||||
if (!Object.hasOwn(input, "warning")) {
|
||||
resultObj["--wa"] = "84.71% 0.199 83.87"
|
||||
}
|
||||
if (!Object.hasOwn(input, "error")) {
|
||||
resultObj["--er"] = "71.76% 0.221 22.18"
|
||||
}
|
||||
|
||||
// auto generate content colors
|
||||
if (!Object.hasOwn(input, "base-content")) {
|
||||
resultObj["--bc"] = generateForegroundColorFrom(input["base-100"], 0.8)
|
||||
}
|
||||
if (!Object.hasOwn(input, "primary-content")) {
|
||||
resultObj["--pc"] = generateForegroundColorFrom(input.primary, 0.8)
|
||||
}
|
||||
if (!Object.hasOwn(input, "secondary-content")) {
|
||||
resultObj["--sc"] = generateForegroundColorFrom(input.secondary, 0.8)
|
||||
}
|
||||
if (!Object.hasOwn(input, "accent-content")) {
|
||||
resultObj["--ac"] = generateForegroundColorFrom(input.accent, 0.8)
|
||||
}
|
||||
if (!Object.hasOwn(input, "neutral-content")) {
|
||||
resultObj["--nc"] = generateForegroundColorFrom(input.neutral, 0.8)
|
||||
}
|
||||
if (!Object.hasOwn(input, "info-content")) {
|
||||
if (Object.hasOwn(input, "info")) {
|
||||
resultObj["--inc"] = generateForegroundColorFrom(input.info, 0.8)
|
||||
} else {
|
||||
resultObj["--inc"] = "0% 0 0"
|
||||
}
|
||||
}
|
||||
if (!Object.hasOwn(input, "success-content")) {
|
||||
if (Object.hasOwn(input, "success")) {
|
||||
resultObj["--suc"] = generateForegroundColorFrom(input.success, 0.8)
|
||||
} else {
|
||||
resultObj["--suc"] = "0% 0 0"
|
||||
}
|
||||
}
|
||||
if (!Object.hasOwn(input, "warning-content")) {
|
||||
if (Object.hasOwn(input, "warning")) {
|
||||
resultObj["--wac"] = generateForegroundColorFrom(input.warning, 0.8)
|
||||
} else {
|
||||
resultObj["--wac"] = "0% 0 0"
|
||||
}
|
||||
}
|
||||
if (!Object.hasOwn(input, "error-content")) {
|
||||
if (Object.hasOwn(input, "error")) {
|
||||
resultObj["--erc"] = generateForegroundColorFrom(input.error, 0.8)
|
||||
} else {
|
||||
resultObj["--erc"] = "0% 0 0"
|
||||
}
|
||||
}
|
||||
|
||||
// add css variables if not exist
|
||||
for (const item of Object.entries(defaultVariables)) {
|
||||
const [variable, value] = item
|
||||
if (!Object.hasOwn(input, variable)) {
|
||||
resultObj[variable] = value
|
||||
}
|
||||
}
|
||||
|
||||
// add other custom styles
|
||||
if (!Object.hasOwn(colorNames, rule)) {
|
||||
resultObj[rule] = value
|
||||
}
|
||||
}
|
||||
|
||||
return resultObj
|
||||
}
|
||||
|
||||
function generateForegroundColorFrom(input: string, percentage = 0.8) {
|
||||
const result = culoriInterpolate([input, isDark(input) ? "white" : "black"], "oklch")(percentage)
|
||||
return colorObjToString(result)
|
||||
}
|
||||
|
||||
function generateDarkenColorFrom(input: string, percentage = 0.07) {
|
||||
const result = culoriInterpolate([input, "black"], "oklch")(percentage)
|
||||
return colorObjToString(result)
|
||||
}
|
||||
|
||||
function colorObjToString(input: { l: number; c: number; h?: number }) {
|
||||
const { l, c, h } = input
|
||||
return `${Number.parseFloat((cutNumber(l) * 100).toFixed(6))}% ${cutNumber(c)} ${cutNumber(h ?? 0)}`
|
||||
}
|
||||
|
||||
function cutNumber(number: number) {
|
||||
if (number) {
|
||||
return +number.toFixed(6)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
function isDark(color: string) {
|
||||
return wcagContrast(color, "black") < wcagContrast(color, "white")
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { UpdateInfo } from 'electron-updater'
|
||||
|
||||
import { Settings } from '../Settings.js'
|
||||
import { Download, DownloadProgress } from './download.interface.js'
|
||||
import { ThemeColors } from './theme.interface.js'
|
||||
import { UpdateProgress } from './update.interface.js'
|
||||
|
||||
export interface ContextBridgeApi {
|
||||
@@ -45,6 +46,10 @@ export interface IpcInvokeEvents {
|
||||
input: OpenDialogOptions
|
||||
output: OpenDialogReturnValue
|
||||
}
|
||||
getThemeColors: {
|
||||
input: string
|
||||
output: ThemeColors | null
|
||||
}
|
||||
}
|
||||
|
||||
export type IpcInvokeHandlers = {
|
||||
|
||||
28
src-shared/interfaces/theme.interface.ts
Normal file
28
src-shared/interfaces/theme.interface.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
||||
export interface ThemeColors {
|
||||
'primary': string
|
||||
'primary-focus': string
|
||||
'primary-content': string
|
||||
'secondary': string
|
||||
'secondary-focus': string
|
||||
'secondary-content': string
|
||||
'accent': string
|
||||
'accent-focus': string
|
||||
'accent-content': string
|
||||
'neutral': string
|
||||
'neutral-focus': string
|
||||
'neutral-content': string
|
||||
'base-100': string
|
||||
'base-200': string
|
||||
'base-300': string
|
||||
'base-content': string
|
||||
'info': string
|
||||
'info-content': string
|
||||
'success': string
|
||||
'success-content': string
|
||||
'warning': string
|
||||
'warning-content': string
|
||||
'error': string
|
||||
'error-content': string
|
||||
}
|
||||
Reference in New Issue
Block a user