diff --git a/postcss.config.js b/postcss.config.js
index 33ad091..80917b6 100644
--- a/postcss.config.js
+++ b/postcss.config.js
@@ -1,6 +1,41 @@
-module.exports = {
- plugins: {
- tailwindcss: {},
- autoprefixer: {},
- },
+/* eslint-disable @typescript-eslint/no-var-requires */
+const { colord } = require('colord')
+const colors = require('tailwindcss/colors')
+
+const replaceBrandColorWithCssVar = () => {
+ const blues = Object.entries(colors.blue).map(([key, value]) => {
+ const c = colord(value).toRgb()
+ return {
+ key,
+ rgb: `${c.r} ${c.g} ${c.b}`,
+ }
+ })
+ return {
+ postcssPlugin: 'replaceBrandColorWithCssVar',
+ Declaration(decl) {
+ let value = decl.value
+ blues.forEach(blue => {
+ value = value.replace(
+ `rgb(${blue.rgb}`,
+ `rgb(var(--brand-color-${blue.key})`
+ )
+ })
+ // if (decl.value !== value) {
+ // console.log({
+ // before: decl.value,
+ // after: value,
+ // })
+ // }
+ decl.value = value
+ },
+ }
+}
+replaceBrandColorWithCssVar.postcss = true
+
+module.exports = {
+ plugins: [
+ require('tailwindcss'),
+ require('autoprefixer'),
+ replaceBrandColorWithCssVar,
+ ],
}
diff --git a/scripts/generate.accent.color.css.js b/scripts/generate.accent.color.css.js
new file mode 100644
index 0000000..cfbe278
--- /dev/null
+++ b/scripts/generate.accent.color.css.js
@@ -0,0 +1,48 @@
+/* eslint-disable @typescript-eslint/no-var-requires */
+const colors = require('tailwindcss/colors')
+const { colord } = require('colord')
+const prettier = require('prettier')
+const fs = require('fs')
+const prettierConfig = require('../prettier.config.js')
+
+const pickedColors = {
+ blue: colors.blue,
+ red: colors.red,
+ orange: colors.orange,
+ amber: colors.amber,
+ yellow: colors.yellow,
+ lime: colors.lime,
+ green: colors.green,
+ emerald: colors.emerald,
+ teal: colors.teal,
+ cyan: colors.cyan,
+ sky: colors.sky,
+ indigo: colors.indigo,
+ violet: colors.violet,
+ purple: colors.purple,
+ fuchsia: colors.fuchsia,
+ pink: colors.pink,
+ rose: colors.rose,
+}
+
+const colorsCss = {}
+Object.entries(pickedColors).forEach(([name, colors]) => {
+ let tmp = ''
+ Object.entries(colors).map(([key, value]) => {
+ const c = colord(value).toRgb()
+ tmp = `${tmp}
+--brand-color-${key}: ${c.r} ${c.g} ${c.b};`
+ })
+ colorsCss[name] = tmp
+})
+
+let css = ''
+Object.entries(colorsCss).forEach(([name, color]) => {
+ css = `${css}
+${name === 'blue' ? ':root' : `[data-accent-color='${name}']`} {${color}
+}
+`
+})
+
+const formatted = prettier.format(css, { ...prettierConfig, parser: 'css' })
+fs.writeFileSync('./src/renderer/styles/accentColor.scss', formatted)
diff --git a/src/renderer/components/Avatar.tsx b/src/renderer/components/Avatar.tsx
new file mode 100644
index 0000000..24b754b
--- /dev/null
+++ b/src/renderer/components/Avatar.tsx
@@ -0,0 +1,39 @@
+import { resizeImage } from '../utils/common'
+import useUser from '../hooks/useUser'
+import SvgIcon from './SvgIcon'
+
+const Avatar = ({ size }: { size?: string }) => {
+ const navigate = useNavigate()
+ const { data: user } = useUser()
+
+ const avatarUrl = user?.profile?.avatarUrl
+ ? resizeImage(user?.profile?.avatarUrl ?? '', 'sm')
+ : ''
+
+ return (
+ <>
+ {avatarUrl ? (
+
navigate('/login')}
+ className={classNames(
+ 'app-region-no-drag rounded-full bg-gray-100 dark:bg-gray-700',
+ size || 'h-9 w-9'
+ )}
+ />
+ ) : (
+
toast('施工中...')}
+ onClick={() => navigate('/settings')}
className='app-region-no-drag btn-hover-animation rounded-lg p-2.5 text-gray-500 transition duration-300 after:rounded-full after:bg-black/[.06] hover:text-gray-900 dark:text-gray-300 dark:after:bg-white/10 dark:hover:text-gray-200'
>
@@ -77,34 +77,6 @@ const Settings = () => {
)
}
-const Avatar = () => {
- const navigate = useNavigate()
- const { data: user } = useUser()
-
- const avatarUrl = user?.profile?.avatarUrl
- ? resizeImage(user?.profile?.avatarUrl ?? '', 'sm')
- : ''
-
- return (
- <>
- {avatarUrl ? (
-

navigate('/login')}
- className='app-region-no-drag h-9 w-9 rounded-full bg-gray-100 dark:bg-gray-700'
- />
- ) : (
-
navigate('/login')}>
-
-
- )}
- >
- )
-}
-
const Topbar = () => {
/**
* Show topbar background when scroll down
diff --git a/src/renderer/main.tsx b/src/renderer/main.tsx
index 1d519e1..aa596aa 100644
--- a/src/renderer/main.tsx
+++ b/src/renderer/main.tsx
@@ -1,4 +1,5 @@
import './utils/initLog'
+import './utils/theme'
import { StrictMode } from 'react'
import * as ReactDOMClient from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
@@ -6,6 +7,7 @@ import * as Sentry from '@sentry/react'
import { BrowserTracing } from '@sentry/tracing'
import 'virtual:svg-icons-register'
import './styles/global.scss'
+import './styles/accentColor.scss'
import App from './App'
import pkg from '../../package.json'
import ReactGA from 'react-ga4'
diff --git a/src/renderer/pages/Settings.tsx b/src/renderer/pages/Settings.tsx
deleted file mode 100644
index ce9bc33..0000000
--- a/src/renderer/pages/Settings.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-const Settings = () => {
- return
-}
-
-export default Settings
diff --git a/src/renderer/pages/Settings/Appearance.tsx b/src/renderer/pages/Settings/Appearance.tsx
new file mode 100644
index 0000000..b11a315
--- /dev/null
+++ b/src/renderer/pages/Settings/Appearance.tsx
@@ -0,0 +1,53 @@
+import { state } from '@/renderer/store'
+import { changeAccentColor } from '@/renderer/utils/theme'
+
+const AccentColor = () => {
+ const colors = {
+ red: 'bg-red-500',
+ orange: 'bg-orange-500',
+ amber: 'bg-amber-500',
+ yellow: 'bg-yellow-500',
+ lime: 'bg-lime-500',
+ green: 'bg-green-500',
+ emerald: 'bg-emerald-500',
+ teal: 'bg-teal-500',
+ cyan: 'bg-cyan-500',
+ sky: 'bg-sky-500',
+ blue: 'bg-blue-500',
+ indigo: 'bg-indigo-500',
+ violet: 'bg-violet-500',
+ purple: 'bg-purple-500',
+ fuchsia: 'bg-fuchsia-500',
+ pink: 'bg-pink-500',
+ rose: 'bg-rose-500',
+ }
+
+ const changeColor = (color: string) => {
+ state.settings.accentColor = color
+ changeAccentColor(color)
+ }
+ return (
+
+ {Object.entries(colors).map(([color, bg]) => (
+
changeColor(color)}
+ >
+ ))}
+
+ )
+}
+
+const Appearance = () => {
+ return (
+
+ )
+}
+
+export default Appearance
diff --git a/src/renderer/pages/Settings/Settings.tsx b/src/renderer/pages/Settings/Settings.tsx
new file mode 100644
index 0000000..ebb9dca
--- /dev/null
+++ b/src/renderer/pages/Settings/Settings.tsx
@@ -0,0 +1,80 @@
+import Avatar from '@/renderer/components/Avatar'
+import SvgIcon from '@/renderer/components/SvgIcon'
+import useUser from '@/renderer/hooks/useUser'
+import Appearance from './Appearance'
+
+const UserCard = () => {
+ const { data: user } = useUser()
+
+ return (
+
+
+
+
+
+ {user?.profile?.nickname ?? '未登录'}
+
+ {(user?.profile?.signature || user?.profile?.vipType) && (
+
+ {user?.profile?.vipType ? (
+

+ ) : null}
+ {user?.profile?.signature}
+
+ )}
+
+
+
+
+
+
+
+ )
+}
+
+const Sidebar = () => {
+ const categories = ['外观', '播放', '歌词', '其他', '试验性功能']
+ const active = '外观'
+ return (
+
+ {categories.map(category => (
+
+ {category}
+
+ ))}
+
+ )
+}
+
+const Settings = () => {
+ return (
+
+ )
+}
+
+export default Settings
diff --git a/src/renderer/pages/Settings/Sidebar.tsx b/src/renderer/pages/Settings/Sidebar.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/src/renderer/pages/Settings/index.ts b/src/renderer/pages/Settings/index.ts
new file mode 100644
index 0000000..d04fd35
--- /dev/null
+++ b/src/renderer/pages/Settings/index.ts
@@ -0,0 +1,3 @@
+import Settings from './Settings'
+
+export default Settings
diff --git a/src/renderer/store.ts b/src/renderer/store.ts
index 6e3c352..e2aa757 100644
--- a/src/renderer/store.ts
+++ b/src/renderer/store.ts
@@ -9,6 +9,7 @@ interface Store {
}
settings: {
showSidebar: boolean
+ accentColor: string
}
}
@@ -19,6 +20,7 @@ const initialState: Store = {
},
settings: {
showSidebar: true,
+ accentColor: 'blue',
},
}
diff --git a/src/renderer/styles/accentColor.scss b/src/renderer/styles/accentColor.scss
new file mode 100644
index 0000000..de57185
--- /dev/null
+++ b/src/renderer/styles/accentColor.scss
@@ -0,0 +1,220 @@
+:root {
+ --brand-color-50: 239 246 255;
+ --brand-color-100: 219 234 254;
+ --brand-color-200: 191 219 254;
+ --brand-color-300: 147 197 253;
+ --brand-color-400: 96 165 250;
+ --brand-color-500: 59 130 246;
+ --brand-color-600: 37 99 235;
+ --brand-color-700: 29 78 216;
+ --brand-color-800: 30 64 175;
+ --brand-color-900: 30 58 138;
+}
+
+[data-accent-color='red'] {
+ --brand-color-50: 254 242 242;
+ --brand-color-100: 254 226 226;
+ --brand-color-200: 254 202 202;
+ --brand-color-300: 252 165 165;
+ --brand-color-400: 248 113 113;
+ --brand-color-500: 239 68 68;
+ --brand-color-600: 220 38 38;
+ --brand-color-700: 185 28 28;
+ --brand-color-800: 153 27 27;
+ --brand-color-900: 127 29 29;
+}
+
+[data-accent-color='orange'] {
+ --brand-color-50: 255 247 237;
+ --brand-color-100: 255 237 213;
+ --brand-color-200: 254 215 170;
+ --brand-color-300: 253 186 116;
+ --brand-color-400: 251 146 60;
+ --brand-color-500: 249 115 22;
+ --brand-color-600: 234 88 12;
+ --brand-color-700: 194 65 12;
+ --brand-color-800: 154 52 18;
+ --brand-color-900: 124 45 18;
+}
+
+[data-accent-color='amber'] {
+ --brand-color-50: 255 251 235;
+ --brand-color-100: 254 243 199;
+ --brand-color-200: 253 230 138;
+ --brand-color-300: 252 211 77;
+ --brand-color-400: 251 191 36;
+ --brand-color-500: 245 158 11;
+ --brand-color-600: 217 119 6;
+ --brand-color-700: 180 83 9;
+ --brand-color-800: 146 64 14;
+ --brand-color-900: 120 53 15;
+}
+
+[data-accent-color='yellow'] {
+ --brand-color-50: 254 252 232;
+ --brand-color-100: 254 249 195;
+ --brand-color-200: 254 240 138;
+ --brand-color-300: 253 224 71;
+ --brand-color-400: 250 204 21;
+ --brand-color-500: 234 179 8;
+ --brand-color-600: 202 138 4;
+ --brand-color-700: 161 98 7;
+ --brand-color-800: 133 77 14;
+ --brand-color-900: 113 63 18;
+}
+
+[data-accent-color='lime'] {
+ --brand-color-50: 247 254 231;
+ --brand-color-100: 236 252 203;
+ --brand-color-200: 217 249 157;
+ --brand-color-300: 190 242 100;
+ --brand-color-400: 163 230 53;
+ --brand-color-500: 132 204 22;
+ --brand-color-600: 101 163 13;
+ --brand-color-700: 77 124 15;
+ --brand-color-800: 63 98 18;
+ --brand-color-900: 54 83 20;
+}
+
+[data-accent-color='green'] {
+ --brand-color-50: 240 253 244;
+ --brand-color-100: 220 252 231;
+ --brand-color-200: 187 247 208;
+ --brand-color-300: 134 239 172;
+ --brand-color-400: 74 222 128;
+ --brand-color-500: 34 197 94;
+ --brand-color-600: 22 163 74;
+ --brand-color-700: 21 128 61;
+ --brand-color-800: 22 101 52;
+ --brand-color-900: 20 83 45;
+}
+
+[data-accent-color='emerald'] {
+ --brand-color-50: 236 253 245;
+ --brand-color-100: 209 250 229;
+ --brand-color-200: 167 243 208;
+ --brand-color-300: 110 231 183;
+ --brand-color-400: 52 211 153;
+ --brand-color-500: 16 185 129;
+ --brand-color-600: 5 150 105;
+ --brand-color-700: 4 120 87;
+ --brand-color-800: 6 95 70;
+ --brand-color-900: 6 78 59;
+}
+
+[data-accent-color='teal'] {
+ --brand-color-50: 240 253 250;
+ --brand-color-100: 204 251 241;
+ --brand-color-200: 153 246 228;
+ --brand-color-300: 94 234 212;
+ --brand-color-400: 45 212 191;
+ --brand-color-500: 20 184 166;
+ --brand-color-600: 13 148 136;
+ --brand-color-700: 15 118 110;
+ --brand-color-800: 17 94 89;
+ --brand-color-900: 19 78 74;
+}
+
+[data-accent-color='cyan'] {
+ --brand-color-50: 236 254 255;
+ --brand-color-100: 207 250 254;
+ --brand-color-200: 165 243 252;
+ --brand-color-300: 103 232 249;
+ --brand-color-400: 34 211 238;
+ --brand-color-500: 6 182 212;
+ --brand-color-600: 8 145 178;
+ --brand-color-700: 14 116 144;
+ --brand-color-800: 21 94 117;
+ --brand-color-900: 22 78 99;
+}
+
+[data-accent-color='sky'] {
+ --brand-color-50: 240 249 255;
+ --brand-color-100: 224 242 254;
+ --brand-color-200: 186 230 253;
+ --brand-color-300: 125 211 252;
+ --brand-color-400: 56 189 248;
+ --brand-color-500: 14 165 233;
+ --brand-color-600: 2 132 199;
+ --brand-color-700: 3 105 161;
+ --brand-color-800: 7 89 133;
+ --brand-color-900: 12 74 110;
+}
+
+[data-accent-color='indigo'] {
+ --brand-color-50: 238 242 255;
+ --brand-color-100: 224 231 255;
+ --brand-color-200: 199 210 254;
+ --brand-color-300: 165 180 252;
+ --brand-color-400: 129 140 248;
+ --brand-color-500: 99 102 241;
+ --brand-color-600: 79 70 229;
+ --brand-color-700: 67 56 202;
+ --brand-color-800: 55 48 163;
+ --brand-color-900: 49 46 129;
+}
+
+[data-accent-color='violet'] {
+ --brand-color-50: 245 243 255;
+ --brand-color-100: 237 233 254;
+ --brand-color-200: 221 214 254;
+ --brand-color-300: 196 181 253;
+ --brand-color-400: 167 139 250;
+ --brand-color-500: 139 92 246;
+ --brand-color-600: 124 58 237;
+ --brand-color-700: 109 40 217;
+ --brand-color-800: 91 33 182;
+ --brand-color-900: 76 29 149;
+}
+
+[data-accent-color='purple'] {
+ --brand-color-50: 250 245 255;
+ --brand-color-100: 243 232 255;
+ --brand-color-200: 233 213 255;
+ --brand-color-300: 216 180 254;
+ --brand-color-400: 192 132 252;
+ --brand-color-500: 168 85 247;
+ --brand-color-600: 147 51 234;
+ --brand-color-700: 126 34 206;
+ --brand-color-800: 107 33 168;
+ --brand-color-900: 88 28 135;
+}
+
+[data-accent-color='fuchsia'] {
+ --brand-color-50: 253 244 255;
+ --brand-color-100: 250 232 255;
+ --brand-color-200: 245 208 254;
+ --brand-color-300: 240 171 252;
+ --brand-color-400: 232 121 249;
+ --brand-color-500: 217 70 239;
+ --brand-color-600: 192 38 211;
+ --brand-color-700: 162 28 175;
+ --brand-color-800: 134 25 143;
+ --brand-color-900: 112 26 117;
+}
+
+[data-accent-color='pink'] {
+ --brand-color-50: 253 242 248;
+ --brand-color-100: 252 231 243;
+ --brand-color-200: 251 207 232;
+ --brand-color-300: 249 168 212;
+ --brand-color-400: 244 114 182;
+ --brand-color-500: 236 72 153;
+ --brand-color-600: 219 39 119;
+ --brand-color-700: 190 24 93;
+ --brand-color-800: 157 23 77;
+ --brand-color-900: 131 24 67;
+}
+
+[data-accent-color='rose'] {
+ --brand-color-50: 255 241 242;
+ --brand-color-100: 255 228 230;
+ --brand-color-200: 254 205 211;
+ --brand-color-300: 253 164 175;
+ --brand-color-400: 251 113 133;
+ --brand-color-500: 244 63 94;
+ --brand-color-600: 225 29 72;
+ --brand-color-700: 190 18 60;
+ --brand-color-800: 159 18 57;
+ --brand-color-900: 136 19 55;
+}
diff --git a/src/renderer/utils/theme.ts b/src/renderer/utils/theme.ts
new file mode 100644
index 0000000..e30e175
--- /dev/null
+++ b/src/renderer/utils/theme.ts
@@ -0,0 +1,8 @@
+export const changeAccentColor = (color: string) => {
+ document.body.setAttribute('data-accent-color', color)
+}
+
+const stateString = localStorage.getItem('state')
+const state = stateString ? JSON.parse(stateString) : {}
+
+changeAccentColor(state.settings.accentColor || 'blue')
diff --git a/tailwind.config.js b/tailwind.config.js
index 9cb771c..2a8057b 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -10,6 +10,7 @@ module.exports = {
theme: {
extend: {
colors: {
+ ...colors,
brand: colors.blue,
gray: colors.neutral,
},