Start to remove workspace & move theme to root package

This commit is contained in:
Zoe Roux 2025-02-02 00:24:34 +01:00
parent 274e4b75dc
commit 6548fde324
No known key found for this signature in database
9 changed files with 314 additions and 314 deletions

View File

@ -1,57 +1,55 @@
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { Slot } from "one"; import { Slot } from "one";
// import { WebTooltip } from "@kyoo/primitives/src/tooltip.web";
// import { HiddenIfNoJs, SkeletonCss, TouchOnlyCss } from "@kyoo/primitives";
import { useServerHeadInsertion } from "one"; import { useServerHeadInsertion } from "one";
import { StyleRegistryProvider, createStyleRegistry, useTheme } from "yoshiki/web"; import { StyleRegistryProvider, createStyleRegistry, useTheme } from "yoshiki/web";
import { Providers } from "~/providers"; import { Providers } from "~/providers";
const GlobalCssTheme = () => { const GlobalCssTheme = () => {
const theme = useTheme(); // const theme = useTheme();
// TODO: add fonts here // TODO: add fonts here
// body {font-family: ${font.style.fontFamily};} // body {font-family: ${font.style.fontFamily};}
// background-color: ${theme.background};
return ( return (
<> <>
{/* <style jsx global>{` */} <style>{`
{/* body { */} body {
{/* margin: 0px; */} margin: 0px;
{/* padding: 0px; */} padding: 0px;
{/* overflow: "hidden"; */} overflow: "hidden";
{/* background-color: ${theme.background}; */} }
{/* } */}
{/**/} *::-webkit-scrollbar {
{/* *::-webkit-scrollbar { */} height: 6px;
{/* height: 6px; */} width: 6px;
{/* width: 6px; */} background: transparent;
{/* background: transparent; */} }
{/* } */}
{/**/} *::-webkit-scrollbar-thumb {
{/* *::-webkit-scrollbar-thumb { */} background-color: #999;
{/* background-color: #999; */} border-radius: 90px;
{/* border-radius: 90px; */} }
{/* } */} *:hover::-webkit-scrollbar-thumb {
{/* *:hover::-webkit-scrollbar-thumb { */} background-color: rgb(134, 127, 127);
{/* background-color: rgb(134, 127, 127); */} }
{/* } */}
{/**/} #__next {
{/* #__next { */} height: 100vh;
{/* height: 100vh; */} }
{/* } */}
{/**/} .infinite-scroll-component__outerdiv {
{/* .infinite-scroll-component__outerdiv { */} width: 100%;
{/* width: 100%; */} height: 100%;
{/* height: 100%; */} }
{/* } */}
{/**/} ::cue {
{/* ::cue { */} background-color: transparent;
{/* background-color: transparent; */} text-shadow:
{/* text-shadow: */} -1px -1px 0 #000,
{/* -1px -1px 0 #000, */} 1px -1px 0 #000,
{/* 1px -1px 0 #000, */} -1px 1px 0 #000,
{/* -1px 1px 0 #000, */} 1px 1px 0 #000;
{/* 1px 1px 0 #000; */} }
{/* } */} `}</style>
{/* `}</style> */}
{/* <WebTooltip theme={theme} /> */} {/* <WebTooltip theme={theme} /> */}
{/* <SkeletonCss /> */} {/* <SkeletonCss /> */}
{/* <TouchOnlyCss /> */} {/* <TouchOnlyCss /> */}
@ -76,7 +74,7 @@ export default function Layout() {
<link rel="icon" type="image/png" sizes="64x64" href="/icon-64x64.png" /> <link rel="icon" type="image/png" sizes="64x64" href="/icon-64x64.png" />
<link rel="icon" type="image/png" sizes="128x128" href="/icon-128x128.png" /> <link rel="icon" type="image/png" sizes="128x128" href="/icon-128x128.png" />
<link rel="icon" type="image/png" sizes="256x256" href="/icon-256x256.png" /> <link rel="icon" type="image/png" sizes="256x256" href="/icon-256x256.png" />
{/* <GlobalCssTheme /> */} <GlobalCssTheme />
</head> </head>
<body className="hoverEnabled"> <body className="hoverEnabled">

View File

@ -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() { export default function MyApp() {
const { css } = useYoshiki();
return ( return (
<View <View
style={{ {...css({
flex: 1, flex: 1,
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
minHeight: "100%", minHeight: "100%",
}} })}
> >
<Text>Hello from One</Text> <Text>Hello from One</Text>
</View> </View>

View File

@ -12,10 +12,7 @@
"format": "biome format .", "format": "biome format .",
"format:fix": "biome format . --write" "format:fix": "biome format . --write"
}, },
"workspaces": ["packages/*"],
"dependencies": { "dependencies": {
"@kyoo/models": "workspace:*",
"@kyoo/primitives": "workspace:*",
"@tanstack/react-query": "^5.66.0", "@tanstack/react-query": "^5.66.0",
"caniuse-api": "^3.0.0", "caniuse-api": "^3.0.0",
"expo": "~52.0.28", "expo": "~52.0.28",

View File

@ -17,73 +17,3 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>. * along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/ */
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",
},
},
};

View File

@ -18,195 +18,3 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>. * along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/ */
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<Exclude<TextStyle["fontWeight"], null | undefined | number>, 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<Mode, "contrast" | "mode" | "themeOverlay"> & { default: Variant };
dark: Omit<Mode, "contrast" | "mode" | "themeOverlay"> & { 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 <ThemeProvider theme={newTheme}>{children as any}</ThemeProvider>;
};
export type YoshikiFunc<T> = (props: ReturnType<typeof useYoshiki>) => T;
const YoshikiProvider = ({ children }: { children: YoshikiFunc<ReactNode> }) => {
const yoshiki = useYoshiki();
return <>{children(yoshiki)}</>;
};
export const SwitchVariant = ({ children }: { children: ReactNode | YoshikiFunc<ReactNode> }) => {
const theme = useTheme();
return (
<ThemeProvider theme={switchVariant(theme)}>
{typeof children === "function" ? (
<YoshikiProvider>{children}</YoshikiProvider>
) : (
(children as any)
)}
</ThemeProvider>
);
};
export const ContrastArea = ({
children,
mode = "dark",
contrastText,
}: {
children: ReactNode | YoshikiFunc<ReactNode>;
mode?: "light" | "dark" | "user" | "alternate";
contrastText?: boolean;
}) => {
const oldTheme = useTheme();
const theme: Theme = { ...oldTheme, ...oldTheme[mode] };
return (
<ThemeProvider
theme={
contrastText
? {
...theme,
// Keep the same skeletons, it looks weird otherwise.
overlay0: theme.user.overlay0,
overlay1: theme.user.overlay1,
heading: theme.contrast,
paragraph: theme.heading,
}
: theme
}
>
{typeof children === "function" ? (
<YoshikiProvider>{children}</YoshikiProvider>
) : (
(children as any)
)}
</ThemeProvider>
);
};
export const alpha = (color: Property.Color, alpha: number) => {
return color + Math.round(alpha * 255).toString(16);
};

View File

@ -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",
},
},
};

View File

@ -0,0 +1 @@
export * from "./theme";

View File

@ -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<Exclude<TextStyle["fontWeight"], null | undefined | number>, 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<Mode, "contrast" | "mode" | "themeOverlay"> & { default: Variant };
dark: Omit<Mode, "contrast" | "mode" | "themeOverlay"> & { 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 <ThemeProvider theme={newTheme}>{children as any}</ThemeProvider>;
};
export type YoshikiFunc<T> = (props: ReturnType<typeof useYoshiki>) => T;
const YoshikiProvider = ({ children }: { children: YoshikiFunc<ReactNode> }) => {
const yoshiki = useYoshiki();
return <>{children(yoshiki)}</>;
};
export const SwitchVariant = ({ children }: { children: ReactNode | YoshikiFunc<ReactNode> }) => {
const theme = useTheme();
return (
<ThemeProvider theme={switchVariant(theme)}>
{typeof children === "function" ? (
<YoshikiProvider>{children}</YoshikiProvider>
) : (
(children as any)
)}
</ThemeProvider>
);
};
export const ContrastArea = ({
children,
mode = "dark",
contrastText,
}: {
children: ReactNode | YoshikiFunc<ReactNode>;
mode?: "light" | "dark" | "user" | "alternate";
contrastText?: boolean;
}) => {
const oldTheme = useTheme();
const theme: Theme = { ...oldTheme, ...oldTheme[mode] };
return (
<ThemeProvider
theme={
contrastText
? {
...theme,
// Keep the same skeletons, it looks weird otherwise.
overlay0: theme.user.overlay0,
overlay1: theme.user.overlay1,
heading: theme.contrast,
paragraph: theme.heading,
}
: theme
}
>
{typeof children === "function" ? (
<YoshikiProvider>{children}</YoshikiProvider>
) : (
(children as any)
)}
</ThemeProvider>
);
};
export const alpha = (color: Property.Color, alpha: number) => {
return color + Math.round(alpha * 255).toString(16);
};

View File

@ -1,7 +1,8 @@
// import { useUserTheme } from "@kyoo/models";
import { ThemeSelector } from "@kyoo/primitives";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ComponentType, type ReactNode, useState } from "react"; 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 QueryProvider = ({ children }: { children: ReactNode }) => {
// const [queryClient] = useState(() => createQueryClient()); // const [queryClient] = useState(() => createQueryClient());