Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 14 additions & 32 deletions src/components/OmniBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { focusStore } from '~/core/FocusStore'

import { messageTable } from '~/core/message/MessageTable'
import { humanizeShortcut } from '~/utils/humanizeShortcut'
import { getThemeKeys, getThemeDisplayNames } from '~/utils/themeConfig'

const isSelected = ({ parent, id }: ActionImpl) => {
if (parent === 'theme') {
Expand Down Expand Up @@ -98,45 +99,26 @@ export function RenderResults() {
}

const useRegisterThemeActions = () => {
const themeKeys = getThemeKeys()
const themeDisplayNames = getThemeDisplayNames()

const themeActions = themeKeys.map(theme => ({
id: theme,
name: themeDisplayNames[theme],
keywords: `${theme} theme`,
section: 'Theme',
perform: async () => settingStore.update({ theme }),
parent: 'theme',
}))

useRegisterActions([
{
id: 'theme',
name: 'Select Theme',
keywords: 'interface color dark light',
section: 'Preferences',
},
{
id: 'dark',
name: 'Dark',
keywords: 'dark theme',
section: 'Theme',
perform: async () => settingStore.update({ theme: 'dark' }),
parent: 'theme',
},
{
id: 'dracula',
name: 'Dracula',
keywords: 'dracula theme',
section: 'Theme',
perform: async () => settingStore.update({ theme: 'dracula' }),
parent: 'theme',
},
{
id: '_system',
name: 'System',
keywords: 'system theme',
section: 'Theme',
perform: async () => settingStore.update({ theme: '_system' }),
parent: 'theme',
},
{
id: 'garden',
name: 'Light',
keywords: 'light garden theme',
section: 'Theme',
perform: async () => settingStore.update({ theme: 'garden' }),
parent: 'theme',
},
...themeActions,
])
}

Expand Down
8 changes: 2 additions & 6 deletions src/components/ThemeSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@ import { twMerge } from 'tailwind-merge'

import { settingStore } from '~/core/setting/SettingStore'
import { Select, SelectItem } from '@heroui/react'
import { getThemeDisplayNames } from '~/utils/themeConfig'

const themes = {
_system: 'System theme',
dark: 'Dark',
dracula: 'Dracula',
garden: 'Light',
}
const themes = getThemeDisplayNames()

const ThemeSelector = () => {
const selectedTheme = settingStore.setting.theme
Expand Down
10 changes: 7 additions & 3 deletions src/containers/DaisyUiThemeProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ import { useEffect, useMemo, type PropsWithChildren } from 'react'
import useMedia from 'use-media'

import { settingStore } from '~/core/setting/SettingStore'
import { resolveThemeAlias, systemThemeConfig } from '~/utils/themeConfig'

const DaisyUiThemeProvider = ({ children }: PropsWithChildren) => {
const selectedTheme = settingStore.setting.theme
const prefersDarkMode = useMedia('(prefers-color-scheme: dark)')

const theme = useMemo(() => {
if (selectedTheme !== '_system') return selectedTheme

return prefersDarkMode ? 'dark' : 'garden'
if (selectedTheme === '_system') {
const systemTheme = prefersDarkMode ? systemThemeConfig.dark : systemThemeConfig.light
return resolveThemeAlias(systemTheme)
} else {
return resolveThemeAlias(selectedTheme)
}
}, [selectedTheme, prefersDarkMode])

useEffect(() => {
Expand Down
8 changes: 7 additions & 1 deletion src/core/BaseTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,13 @@ export abstract class BaseTable<

if (!entity) return undefined

return this.cache.put(this.schema.parse(entity), false)
const result = this.schema.safeParse(entity)

if (result.success) {
return this.cache.put(result.data, false)
} else {
return undefined
}
}

async findByIds(ids: string[]): Promise<Output[]> {
Expand Down
9 changes: 3 additions & 6 deletions src/core/setting/SettingModel.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import z from 'zod'
import moment from 'moment'
import { getThemeKeys } from '~/utils/themeConfig'

export const CURRENT_DB_TIMESTAMP = moment('Oct 28 24', 'MMM DD YY')
export const CURRENT_DB_TIMESTAMP_MILLISECONDS = CURRENT_DB_TIMESTAMP.valueOf()

const ThemeOptions = z.union([
z.literal('_system'),
z.literal('dark'),
z.literal('dracula'),
z.literal('garden'),
])
const themeKeys = getThemeKeys() as [string, ...string[]]
const ThemeOptions = z.enum(themeKeys)

export const SettingModel = z.object({
// setting row will only have one field
Expand Down
131 changes: 131 additions & 0 deletions src/utils/themeConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import allThemes from 'daisyui/src/theming/themes'

export const colors = {
error: 'oklch(51% 0.17 22.1)',
}

export const systemThemeConfig = { light: 'light', dark: 'dark' }

export const themeConfig = {
enabledThemes: [
'dark',
'dracula',
{ light: 'garden' }, // alias: light -> garden
'nord',
'synthwave',
],

defaultTheme: 'dracula',

customizations: {
// Regex patterns for multiple themes (examples)
// '^dark.*': { /* customizations for themes starting with 'dark' */ },
// '.*night.*': { /* customizations for themes containing 'night' */ },
// '(dracula|dark|night|black)': { /* customizations for dark-themed themes */ },
// '^(light|garden|cupcake)$': { /* customizations for specific light themes */ },

'*': {
error: colors.error,
},

garden: {
primary: 'oklch(62.45% 0.1947 3.83636)',
},
},
}

export const getThemeKeys = (): string[] => {
const themes: string[] = []

themes.push('_system')
themeConfig.enabledThemes.forEach(theme => {
if (typeof theme === 'string') {
themes.push(theme)
} else {
themes.push(Object.keys(theme)[0]) // push alias as theme
}
})

return themes
}

export const getThemeDisplayNames = (): Record<string, string> => {
const themes: Record<string, string> = {}

themes['_system'] = 'System Theme'
themeConfig.enabledThemes.forEach(theme => {
if (typeof theme === 'string') {
themes[theme] = theme.charAt(0).toUpperCase() + theme.slice(1)
} else {
const [alias] = Object.keys(theme)
themes[alias] = alias.charAt(0).toUpperCase() + alias.slice(1)
}
})

return themes
}

export const resolveThemeAlias = (themeKey: string): string => {
if (themeKey === '_system') return '_system'

for (const theme of themeConfig.enabledThemes) {
if (typeof theme === 'object') {
const [alias, actualTheme] = Object.entries(theme)[0]
if (alias === themeKey) {
return actualTheme
}
}
}

return themeKey
}

export const getEnabledDaisyThemes = () => {
const enabledThemes: Record<string, any> = {}

themeConfig.enabledThemes.forEach(theme => {
let actualThemeName: string

if (typeof theme === 'string') {
actualThemeName = theme
} else {
// It's an alias object, get the actual theme name
const [alias, actualTheme] = Object.entries(theme)[0]

// Skip _system theme as it's not a real DaisyUI theme
if (alias === '_system') return

actualThemeName = actualTheme as string
}

if ((allThemes as any)[actualThemeName]) {
let themeCustomizations = { ...(allThemes as any)[actualThemeName] }

// Apply customizations in order of specificity (least to most specific)
Object.entries(themeConfig.customizations).forEach(([pattern, customization]) => {
if (pattern === '*') {
// Global wildcard - applies to all themes
themeCustomizations = { ...themeCustomizations, ...customization }
} else if (pattern === actualThemeName) {
// Exact theme name match - most specific, applied last
themeCustomizations = { ...themeCustomizations, ...customization }
} else {
// Try regex pattern matching
try {
const regex = new RegExp(pattern)
if (regex.test(actualThemeName)) {
themeCustomizations = { ...themeCustomizations, ...customization }
}
} catch (e) {
// Invalid regex pattern, skip silently
console.warn(`Invalid regex pattern in theme customizations: ${pattern}`)
}
}
})

enabledThemes[actualThemeName] = themeCustomizations
}
})

return enabledThemes
}
61 changes: 22 additions & 39 deletions tailwind.config.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,37 @@
/** @type {import('tailwindcss').Config} */

import { heroui } from "@heroui/react"
import themes from 'daisyui/src/theming/themes'
import { heroui } from '@heroui/react'
import { getEnabledDaisyThemes, colors } from './src/utils/themeConfig'

const errorColor = 'oklch(51% 0.17 22.1)'
const allThemes = Object.entries(getEnabledDaisyThemes()).map(([name, theme]) => ({
[name]: theme,
}))

module.exports = {
content: [
'src/**/*.{js,ts,jsx,tsx}',
"./node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}",
],
content: ['src/**/*.{js,ts,jsx,tsx}', './node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {},
},
darkMode: 'class',
plugins: [require('@tailwindcss/typography'), require('daisyui'), heroui({
themes: {
light: {
colors: {
danger: errorColor
}
},
dark: {
colors: {
danger: errorColor
}
}
}
})],
daisyui: {
themes: [
{
garden: {
...themes['garden'],
primary: 'oklch(62.45% 0.1947 3.83636)',
error: errorColor
plugins: [
require('@tailwindcss/typography'),
require('daisyui'),
heroui({
themes: {
light: {
colors: {
danger: colors.error,
},
},
},
{
dark: {
...themes['dark'],
error: errorColor
colors: {
danger: colors.error,
},
},
},
{
dracula: {
...themes['dracula'],
error: errorColor
},
},
],
}),
],
daisyui: {
themes: allThemes,
},
}