main
ts 103 lines 2.98 KB
Raw
1 // Shared Material UI theme bridge for the React-mounted Shuffle embeds.
2 //
3 // Background: `@shuffleio/shuffle-mcps` is built with `@mui/material`.
4 // Without a `<ThemeProvider>` wrapping the React mount, MUI falls back
5 // to its built-in light-mode palette — which, against CoPilot's dark
6 // backdrop, looks washed-out and transparent (the AppDetailDrawer in
7 // particular ended up unreadable for users on the default theme).
8 //
9 // We expose:
10 // - `getMuiTheme(isDark)` — returns a memoised MUI Theme matching
11 // CoPilot's light/dark mode. We don't pipe every Naive UI token in
12 // (the surface area is huge and the MUI palette has its own
13 // semantics) — just the mode plus a darkened paper background so
14 // drawer/menu surfaces are opaque against the page underneath.
15 // - `MuiProvider` — small React component that wraps children in
16 // `<ThemeProvider value={mui-theme}>`. Each embed wrapper renders
17 // this around the package's exported component.
18
19 import type { Theme } from "@mui/material/styles"
20 import type { ReactNode } from "react"
21 import { createTheme, ThemeProvider } from "@mui/material/styles"
22 import { createElement } from "react"
23 import { useThemeStore } from "@/stores/theme"
24
25 let lightTheme: Theme | null = null
26 let darkTheme: Theme | null = null
27
28 function buildTheme(isDark: boolean): Theme {
29 const themeStore = useThemeStore()
30
31 return createTheme({
32 palette: {
33 mode: isDark ? "dark" : "light",
34 ...(isDark
35 ? {
36 background: {
37 default: "var(--bg-body-color) !important",
38 paper: "var(--bg-default-color) !important"
39 },
40 primary: {
41 main: themeStore.style["primary-color"]
42 }
43 }
44 : {})
45 },
46 components: {
47 MuiPaper: {
48 styleOverrides: {
49 root: {
50 backgroundImage: "none !important",
51 boxShadow: "none !important"
52 }
53 }
54 },
55 MuiCard: {
56 styleOverrides: {
57 root: {
58 border: "1px solid var(--border-color) !important",
59 borderColor: "var(--border-color) !important",
60 backgroundImage: "none !important",
61 backgroundColor: "var(--bg-secondary-color) !important"
62 }
63 }
64 },
65 MuiDrawer: {
66 styleOverrides: {
67 root: {
68 zIndex: "2104 !important"
69 },
70 paper: {
71 borderLeft: "none !important",
72 borderTopLeftRadius: "var(--border-radius) !important",
73 borderBottomLeftRadius: "var(--border-radius) !important"
74 }
75 }
76 },
77 MuiButton: {
78 styleOverrides: {
79 root: {
80 borderRadius: "var(--border-radius) !important",
81 boxShadow: "none !important"
82 }
83 }
84 }
85 },
86 typography: {
87 fontFamily: "var(--font-family)"
88 }
89 })
90 }
91
92 export function getMuiTheme(isDark: boolean): Theme {
93 if (isDark) {
94 if (!darkTheme) darkTheme = buildTheme(true)
95 return darkTheme
96 }
97 if (!lightTheme) lightTheme = buildTheme(false)
98 return lightTheme
99 }
100
101 export function MuiProvider(props: { isDark: boolean; children: ReactNode }) {
102 return createElement(ThemeProvider as never, { theme: getMuiTheme(props.isDark) }, props.children)
103 }