Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at theme-changes 286 lines 6.9 kB view raw
1import {createContext, useCallback, useContext, useMemo, useState} from 'react' 2import {createTheme, type Theme, type ThemeName} from '@bsky.app/alf' 3import chroma from 'chroma-js' 4 5import {useThemePrefs} from '#/state/shell/color-mode' 6import { 7 computeFontScaleMultiplier, 8 getFontFamily, 9 getFontScale, 10 setFontFamily as persistFontFamily, 11 setFontScale as persistFontScale, 12} from '#/alf/fonts' 13import { 14 blackskyscheme, 15 blueskyscheme, 16 catppuccinscheme, 17 cyanscheme, 18 deerscheme, 19 evergardenscheme, 20 kittyscheme, 21 type Palette, 22 reddwarfscheme, 23 themes, 24 witchskyscheme, 25 zeppelinscheme, 26} from '#/alf/themes' 27import {type Device} from '#/storage' 28import {getMaterial3Colors} from './util/material3Theme' 29import { 30 MaterialYouPaletteProvider, 31 useMaterialYouPalette, 32} from './util/materialYou' 33 34export { 35 type TextStyleProp, 36 type Theme, 37 utils, 38 type ViewStyleProp, 39} from '@bsky.app/alf' 40export {atoms} from '#/alf/atoms' 41export * from '#/alf/breakpoints' 42export * from '#/alf/fonts' 43export * as tokens from '#/alf/tokens' 44export * from '#/alf/util/flatten' 45export * from '#/alf/util/platform' 46export * from '#/alf/util/themeSelector' 47export * from '#/alf/util/useGutters' 48 49export type Alf = { 50 themeName: ThemeName 51 theme: Theme 52 themes: typeof themes 53 fonts: { 54 scale: Exclude<Device['fontScale'], undefined> 55 scaleMultiplier: number 56 family: Device['fontFamily'] 57 setFontScale: (fontScale: Exclude<Device['fontScale'], undefined>) => void 58 setFontFamily: (fontFamily: Device['fontFamily']) => void 59 } 60 /** 61 * Feature flags or other gated options 62 */ 63 flags: {} 64} 65 66/* 67 * Context 68 */ 69export const Context = createContext<Alf>({ 70 themeName: 'light', 71 theme: themes.light, 72 themes, 73 fonts: { 74 scale: getFontScale(), 75 scaleMultiplier: computeFontScaleMultiplier(getFontScale()), 76 family: getFontFamily(), 77 setFontScale: () => {}, 78 setFontFamily: () => {}, 79 }, 80 flags: {}, 81}) 82Context.displayName = 'AlfContext' 83 84export type SchemeType = typeof themes 85 86export function changeHue(colorStr: string, hueShift: number) { 87 if (!hueShift || hueShift === 0) return colorStr 88 89 const color = chroma(colorStr).oklch() 90 91 const newHue = (color[2] + hueShift + 360) % 360 92 93 return chroma.oklch(color[0], color[1], newHue).hex() 94} 95 96export function shiftPalette(palette: Palette, hueShift: number): Palette { 97 const newPalette = {...palette} 98 const keys = Object.keys(newPalette) as Array<keyof Palette> 99 100 keys.forEach(key => { 101 if ( 102 key.startsWith('positive_') || 103 key.startsWith('negative_') || 104 key === 'like' || 105 key === 'pink' || 106 key === 'yellow' 107 ) { 108 return 109 } 110 newPalette[key] = changeHue(newPalette[key], hueShift) 111 }) 112 113 return newPalette 114} 115 116export function hueShifter(scheme: SchemeType, hueShift: number): SchemeType { 117 if (!hueShift || hueShift === 0) { 118 return scheme 119 } 120 121 const lightPalette = shiftPalette(scheme.lightPalette, hueShift) 122 const darkPalette = shiftPalette(scheme.darkPalette, hueShift) 123 const dimPalette = shiftPalette(scheme.dimPalette, hueShift) 124 125 const light = createTheme({ 126 scheme: 'light', 127 name: 'light', 128 palette: lightPalette, 129 }) 130 131 const dark = createTheme({ 132 scheme: 'dark', 133 name: 'dark', 134 palette: darkPalette, 135 options: { 136 shadowOpacity: 0.4, 137 }, 138 }) 139 140 const dim = createTheme({ 141 scheme: 'dark', 142 name: 'dim', 143 palette: dimPalette, 144 options: { 145 shadowOpacity: 0.4, 146 }, 147 }) 148 149 return { 150 lightPalette, 151 darkPalette, 152 dimPalette, 153 light, 154 dark, 155 dim, 156 } 157} 158 159export function useScheme(): SchemeType { 160 const {hue, colorScheme} = useThemePrefs() 161 const palette = useMaterialYouPalette() 162 163 return useMemo(() => { 164 let currentScheme = themes 165 switch (colorScheme) { 166 case 'witchsky': 167 currentScheme = witchskyscheme 168 break 169 case 'bluesky': 170 currentScheme = blueskyscheme 171 break 172 case 'blacksky': 173 currentScheme = blackskyscheme 174 break 175 case 'deer': 176 currentScheme = deerscheme 177 break 178 case 'zeppelin': 179 currentScheme = zeppelinscheme 180 break 181 case 'kitty': 182 currentScheme = kittyscheme 183 break 184 case 'reddwarf': 185 currentScheme = reddwarfscheme 186 break 187 case 'catppuccin': 188 currentScheme = catppuccinscheme 189 break 190 case 'evergarden': 191 currentScheme = evergardenscheme 192 break 193 case 'cyan base': 194 currentScheme = cyanscheme 195 break 196 case 'material3': 197 currentScheme = getMaterial3Colors(palette).scheme 198 break 199 default: 200 currentScheme = themes 201 break 202 } 203 204 return hueShifter(currentScheme, hue) 205 }, [colorScheme, hue, palette]) 206} 207 208function ThemeProviderInner({ 209 children, 210 theme: themeName, 211}: React.PropsWithChildren<{theme: ThemeName}>) { 212 const currentScheme = useScheme() 213 const [fontScale, setFontScale] = useState<Alf['fonts']['scale']>(() => 214 getFontScale(), 215 ) 216 const [fontScaleMultiplier, setFontScaleMultiplier] = useState(() => 217 computeFontScaleMultiplier(fontScale), 218 ) 219 const setFontScaleAndPersist = useCallback<Alf['fonts']['setFontScale']>( 220 fs => { 221 setFontScale(fs) 222 persistFontScale(fs) 223 setFontScaleMultiplier(computeFontScaleMultiplier(fs)) 224 }, 225 [setFontScale], 226 ) 227 const [fontFamily, setFontFamily] = useState<Alf['fonts']['family']>(() => 228 getFontFamily(), 229 ) 230 const setFontFamilyAndPersist = useCallback<Alf['fonts']['setFontFamily']>( 231 ff => { 232 setFontFamily(ff) 233 persistFontFamily(ff) 234 }, 235 [setFontFamily], 236 ) 237 238 const value = useMemo<Alf>(() => { 239 return { 240 themes: currentScheme, 241 themeName: themeName, 242 theme: currentScheme[themeName], 243 fonts: { 244 scale: fontScale, 245 scaleMultiplier: fontScaleMultiplier, 246 family: fontFamily, 247 setFontScale: setFontScaleAndPersist, 248 setFontFamily: setFontFamilyAndPersist, 249 }, 250 flags: {}, 251 } 252 }, [ 253 currentScheme, 254 themeName, 255 fontScale, 256 fontScaleMultiplier, 257 fontFamily, 258 setFontScaleAndPersist, 259 setFontFamilyAndPersist, 260 ]) 261 262 return <Context.Provider value={value}>{children}</Context.Provider> 263} 264 265export function ThemeProvider({ 266 children, 267 theme: themeName, 268}: React.PropsWithChildren<{theme: ThemeName}>) { 269 const {material3Accent, material3Style} = useThemePrefs() 270 return ( 271 <MaterialYouPaletteProvider accent={material3Accent} style={material3Style}> 272 <ThemeProviderInner theme={themeName}>{children}</ThemeProviderInner> 273 </MaterialYouPaletteProvider> 274 ) 275} 276 277export function useAlf() { 278 return useContext(Context) 279} 280 281export function useTheme(theme?: ThemeName) { 282 const alf = useAlf() 283 return useMemo(() => { 284 return theme ? alf.themes[theme] : alf.theme 285 }, [theme, alf]) 286}