diff --git a/front/app/_layout.tsx b/front/app/_layout.tsx index 5248e002..2eab9e0e 100644 --- a/front/app/_layout.tsx +++ b/front/app/_layout.tsx @@ -1,57 +1,55 @@ import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { Slot } from "one"; -// import { WebTooltip } from "@kyoo/primitives/src/tooltip.web"; -// import { HiddenIfNoJs, SkeletonCss, TouchOnlyCss } from "@kyoo/primitives"; import { useServerHeadInsertion } from "one"; import { StyleRegistryProvider, createStyleRegistry, useTheme } from "yoshiki/web"; import { Providers } from "~/providers"; const GlobalCssTheme = () => { - const theme = useTheme(); + // const theme = useTheme(); // TODO: add fonts here // body {font-family: ${font.style.fontFamily};} + // background-color: ${theme.background}; return ( <> - {/* */} + {/* */} {/* */} {/* */} @@ -76,7 +74,7 @@ export default function Layout() { - {/* */} + diff --git a/front/app/index.tsx b/front/app/index.tsx index 99584d21..a58e035d 100644 --- a/front/app/index.tsx +++ b/front/app/index.tsx @@ -1,14 +1,17 @@ -import { View, Text } from "react-native"; +import { Text, View } from "react-native"; +import { useYoshiki } from "yoshiki/native"; export default function MyApp() { + const { css } = useYoshiki(); + return ( Hello from One diff --git a/front/package.json b/front/package.json index fd63bc78..83d88bce 100644 --- a/front/package.json +++ b/front/package.json @@ -12,10 +12,7 @@ "format": "biome format .", "format:fix": "biome format . --write" }, - "workspaces": ["packages/*"], "dependencies": { - "@kyoo/models": "workspace:*", - "@kyoo/primitives": "workspace:*", "@tanstack/react-query": "^5.66.0", "caniuse-api": "^3.0.0", "expo": "~52.0.28", diff --git a/front/packages/primitives/src/themes/catppuccin.ts b/front/packages/primitives/src/themes/catppuccin.ts index 86988363..609d690d 100644 --- a/front/packages/primitives/src/themes/catppuccin.ts +++ b/front/packages/primitives/src/themes/catppuccin.ts @@ -17,73 +17,3 @@ * You should have received a copy of the GNU General Public License * along with Kyoo. If not, see . */ - -import type { ThemeBuilder } from "./theme"; - -// Ref: https://github.com/catppuccin/catppuccin -export const catppuccin: ThemeBuilder = { - light: { - // Catppuccin latte - overlay0: "#9ca0b0", - overlay1: "#7c7f93", - lightOverlay: "#eff1f599", - darkOverlay: "#4c4f6999", - link: "#1e66f5", - default: { - background: "#eff1f5", - accent: "#e64553", - divider: "#8c8fa1", - heading: "#4c4f69", - paragraph: "#5c5f77", - subtext: "#6c6f85", - }, - variant: { - background: "#e6e9ef", - accent: "#d20f39", - divider: "#dd7878", - heading: "#4c4f69", - paragraph: "#5c5f77", - subtext: "#6c6f85", - }, - colors: { - red: "#d20f39", - green: "#40a02b", - blue: "#1e66f5", - yellow: "#df8e1d", - black: "#4c4f69", - white: "#eff1f5", - }, - }, - dark: { - // Catppuccin mocha - overlay0: "#6c7086", - overlay1: "#9399b2", - lightOverlay: "#f5f0f899", - darkOverlay: "#11111b99", - link: "#89b4fa", - default: { - background: "#1e1e2e", - accent: "#89b4fa", - divider: "#7f849c", - heading: "#cdd6f4", - paragraph: "#bac2de", - subtext: "#a6adc8", - }, - variant: { - background: "#181825", - accent: "#74c7ec", - divider: "#1e1e2e", - heading: "#cdd6f4", - paragraph: "#bac2de", - subtext: "#a6adc8", - }, - colors: { - red: "#f38ba8", - green: "#a6e3a1", - blue: "#89b4fa", - yellow: "#f9e2af", - black: "#11111b", - white: "#f5f0f8", - }, - }, -}; diff --git a/front/packages/primitives/src/themes/theme.tsx b/front/packages/primitives/src/themes/theme.tsx index ffc764ce..b4d49ce9 100644 --- a/front/packages/primitives/src/themes/theme.tsx +++ b/front/packages/primitives/src/themes/theme.tsx @@ -18,195 +18,3 @@ * along with Kyoo. If not, see . */ -import type { Property } from "csstype"; -import type { ReactNode } from "react"; -import { Platform, type TextStyle } from "react-native"; -import { type Theme, ThemeProvider, useAutomaticTheme } from "yoshiki"; -import "yoshiki"; -import { useTheme, useYoshiki } from "yoshiki/native"; -import "yoshiki/native"; -import { catppuccin } from "./catppuccin"; - -type FontList = Partial< - Record, string> ->; - -type Mode = { - mode: "light" | "dark" | "auto"; - overlay0: Property.Color; - overlay1: Property.Color; - lightOverlay: Property.Color; - darkOverlay: Property.Color; - themeOverlay: Property.Color; - link: Property.Color; - contrast: Property.Color; - variant: Variant; - colors: { - red: Property.Color; - green: Property.Color; - blue: Property.Color; - yellow: Property.Color; - white: Property.Color; - black: Property.Color; - }; -}; - -type Variant = { - background: Property.Color; - accent: Property.Color; - divider: Property.Color; - heading: Property.Color; - paragraph: Property.Color; - subtext: Property.Color; -}; - -declare module "yoshiki" { - export interface Theme extends Mode, Variant { - light: Mode & Variant; - dark: Mode & Variant; - user: Mode & Variant; - alternate: Mode & Variant; - font: FontList; - } -} - -export type { Theme } from "yoshiki"; -export type ThemeBuilder = { - light: Omit & { default: Variant }; - dark: Omit & { default: Variant }; -}; - -const selectMode = ( - theme: ThemeBuilder & { font: FontList }, - mode: "light" | "dark" | "auto", -): Theme => { - const { light: lightBuilder, dark: darkBuilder, ...options } = theme; - const light: Mode & Variant = { - ...lightBuilder, - ...lightBuilder.default, - contrast: lightBuilder.colors.black, - themeOverlay: lightBuilder.lightOverlay, - mode: "light", - }; - const dark: Mode & Variant = { - ...darkBuilder, - ...darkBuilder.default, - contrast: darkBuilder.colors.white, - themeOverlay: darkBuilder.darkOverlay, - mode: "dark", - }; - if (Platform.OS !== "web" || mode !== "auto") { - const value = mode === "light" ? light : dark; - const alternate = mode === "light" ? dark : light; - return { - ...options, - ...value, - light, - dark, - user: value, - alternate, - }; - } - - const auto = useAutomaticTheme("theme", { light, dark }); - const alternate = useAutomaticTheme("alternate", { dark: light, light: dark }); - return { - ...options, - ...auto, - mode: "auto", - light, - dark, - user: { ...auto, mode: "auto" }, - alternate: { ...alternate, mode: "auto" }, - }; -}; - -const switchVariant = (theme: Theme) => { - return { - ...theme, - ...theme.variant, - variant: { - background: theme.background, - accent: theme.accent, - divider: theme.divider, - heading: theme.heading, - paragraph: theme.paragraph, - subtext: theme.subtext, - }, - }; -}; - -export const ThemeSelector = ({ - children, - theme, - font, -}: { - children: ReactNode; - theme: "light" | "dark" | "auto"; - font: FontList; -}) => { - const newTheme = selectMode({ ...catppuccin, font }, theme); - - return {children as any}; -}; - -export type YoshikiFunc = (props: ReturnType) => T; - -const YoshikiProvider = ({ children }: { children: YoshikiFunc }) => { - const yoshiki = useYoshiki(); - return <>{children(yoshiki)}; -}; - -export const SwitchVariant = ({ children }: { children: ReactNode | YoshikiFunc }) => { - const theme = useTheme(); - - return ( - - {typeof children === "function" ? ( - {children} - ) : ( - (children as any) - )} - - ); -}; - -export const ContrastArea = ({ - children, - mode = "dark", - contrastText, -}: { - children: ReactNode | YoshikiFunc; - mode?: "light" | "dark" | "user" | "alternate"; - contrastText?: boolean; -}) => { - const oldTheme = useTheme(); - const theme: Theme = { ...oldTheme, ...oldTheme[mode] }; - - return ( - - {typeof children === "function" ? ( - {children} - ) : ( - (children as any) - )} - - ); -}; - -export const alpha = (color: Property.Color, alpha: number) => { - return color + Math.round(alpha * 255).toString(16); -}; diff --git a/front/src/primitives/theme/catppuccin.tsx b/front/src/primitives/theme/catppuccin.tsx new file mode 100644 index 00000000..1c3af6c8 --- /dev/null +++ b/front/src/primitives/theme/catppuccin.tsx @@ -0,0 +1,69 @@ +import type { ThemeBuilder } from "./theme"; + +// Ref: https://github.com/catppuccin/catppuccin +export const catppuccin: ThemeBuilder = { + light: { + // Catppuccin latte + overlay0: "#9ca0b0", + overlay1: "#7c7f93", + lightOverlay: "#eff1f599", + darkOverlay: "#4c4f6999", + link: "#1e66f5", + default: { + background: "#eff1f5", + accent: "#e64553", + divider: "#8c8fa1", + heading: "#4c4f69", + paragraph: "#5c5f77", + subtext: "#6c6f85", + }, + variant: { + background: "#e6e9ef", + accent: "#d20f39", + divider: "#dd7878", + heading: "#4c4f69", + paragraph: "#5c5f77", + subtext: "#6c6f85", + }, + colors: { + red: "#d20f39", + green: "#40a02b", + blue: "#1e66f5", + yellow: "#df8e1d", + black: "#4c4f69", + white: "#eff1f5", + }, + }, + dark: { + // Catppuccin mocha + overlay0: "#6c7086", + overlay1: "#9399b2", + lightOverlay: "#f5f0f899", + darkOverlay: "#11111b99", + link: "#89b4fa", + default: { + background: "#1e1e2e", + accent: "#89b4fa", + divider: "#7f849c", + heading: "#cdd6f4", + paragraph: "#bac2de", + subtext: "#a6adc8", + }, + variant: { + background: "#181825", + accent: "#74c7ec", + divider: "#1e1e2e", + heading: "#cdd6f4", + paragraph: "#bac2de", + subtext: "#a6adc8", + }, + colors: { + red: "#f38ba8", + green: "#a6e3a1", + blue: "#89b4fa", + yellow: "#f9e2af", + black: "#11111b", + white: "#f5f0f8", + }, + }, +}; diff --git a/front/src/primitives/theme/index.ts b/front/src/primitives/theme/index.ts new file mode 100644 index 00000000..3797aeae --- /dev/null +++ b/front/src/primitives/theme/index.ts @@ -0,0 +1 @@ +export * from "./theme"; diff --git a/front/src/primitives/theme/theme.tsx b/front/src/primitives/theme/theme.tsx new file mode 100644 index 00000000..fbd134bb --- /dev/null +++ b/front/src/primitives/theme/theme.tsx @@ -0,0 +1,193 @@ +import type { Property } from "csstype"; +import type { ReactNode } from "react"; +import { Platform, type TextStyle } from "react-native"; +import { type Theme, ThemeProvider, useAutomaticTheme } from "yoshiki"; +import "yoshiki"; +import { useTheme, useYoshiki } from "yoshiki/native"; +import "yoshiki/native"; +import { catppuccin } from "./catppuccin"; + +type FontList = Partial< + Record, string> +>; + +type Mode = { + mode: "light" | "dark" | "auto"; + overlay0: Property.Color; + overlay1: Property.Color; + lightOverlay: Property.Color; + darkOverlay: Property.Color; + themeOverlay: Property.Color; + link: Property.Color; + contrast: Property.Color; + variant: Variant; + colors: { + red: Property.Color; + green: Property.Color; + blue: Property.Color; + yellow: Property.Color; + white: Property.Color; + black: Property.Color; + }; +}; + +type Variant = { + background: Property.Color; + accent: Property.Color; + divider: Property.Color; + heading: Property.Color; + paragraph: Property.Color; + subtext: Property.Color; +}; + +declare module "yoshiki" { + export interface Theme extends Mode, Variant { + light: Mode & Variant; + dark: Mode & Variant; + user: Mode & Variant; + alternate: Mode & Variant; + font: FontList; + } +} + +export type { Theme } from "yoshiki"; +export type ThemeBuilder = { + light: Omit & { default: Variant }; + dark: Omit & { default: Variant }; +}; + +const selectMode = ( + theme: ThemeBuilder & { font: FontList }, + mode: "light" | "dark" | "auto", +): Theme => { + const { light: lightBuilder, dark: darkBuilder, ...options } = theme; + const light: Mode & Variant = { + ...lightBuilder, + ...lightBuilder.default, + contrast: lightBuilder.colors.black, + themeOverlay: lightBuilder.lightOverlay, + mode: "light", + }; + const dark: Mode & Variant = { + ...darkBuilder, + ...darkBuilder.default, + contrast: darkBuilder.colors.white, + themeOverlay: darkBuilder.darkOverlay, + mode: "dark", + }; + if (Platform.OS !== "web" || mode !== "auto") { + const value = mode === "light" ? light : dark; + const alternate = mode === "light" ? dark : light; + return { + ...options, + ...value, + light, + dark, + user: value, + alternate, + }; + } + + const auto = useAutomaticTheme("theme", { light, dark }); + const alternate = useAutomaticTheme("alternate", { dark: light, light: dark }); + return { + ...options, + ...auto, + mode: "auto", + light, + dark, + user: { ...auto, mode: "auto" }, + alternate: { ...alternate, mode: "auto" }, + }; +}; + +const switchVariant = (theme: Theme) => { + return { + ...theme, + ...theme.variant, + variant: { + background: theme.background, + accent: theme.accent, + divider: theme.divider, + heading: theme.heading, + paragraph: theme.paragraph, + subtext: theme.subtext, + }, + }; +}; + +export const ThemeSelector = ({ + children, + theme, + font, +}: { + children: ReactNode; + theme: "light" | "dark" | "auto"; + font: FontList; +}) => { + const newTheme = selectMode({ ...catppuccin, font }, theme); + + return {children as any}; +}; + +export type YoshikiFunc = (props: ReturnType) => T; + +const YoshikiProvider = ({ children }: { children: YoshikiFunc }) => { + const yoshiki = useYoshiki(); + return <>{children(yoshiki)}; +}; + +export const SwitchVariant = ({ children }: { children: ReactNode | YoshikiFunc }) => { + const theme = useTheme(); + + return ( + + {typeof children === "function" ? ( + {children} + ) : ( + (children as any) + )} + + ); +}; + +export const ContrastArea = ({ + children, + mode = "dark", + contrastText, +}: { + children: ReactNode | YoshikiFunc; + mode?: "light" | "dark" | "user" | "alternate"; + contrastText?: boolean; +}) => { + const oldTheme = useTheme(); + const theme: Theme = { ...oldTheme, ...oldTheme[mode] }; + + return ( + + {typeof children === "function" ? ( + {children} + ) : ( + (children as any) + )} + + ); +}; + +export const alpha = (color: Property.Color, alpha: number) => { + return color + Math.round(alpha * 255).toString(16); +}; + diff --git a/front/src/providers.tsx b/front/src/providers.tsx index 313c33eb..7e9a4694 100644 --- a/front/src/providers.tsx +++ b/front/src/providers.tsx @@ -1,7 +1,8 @@ -// import { useUserTheme } from "@kyoo/models"; -import { ThemeSelector } from "@kyoo/primitives"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ComponentType, type ReactNode, useState } from "react"; +// import { useUserTheme } from "@kyoo/models"; +// import { createQueryClient } from "@kyoo/models"; +import { ThemeSelector } from "~/primitives/theme"; const QueryProvider = ({ children }: { children: ReactNode }) => { // const [queryClient] = useState(() => createQueryClient());