zudoku 0.0.0-f9d5b02 → 0.0.0-fb7d300
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/entry.client.js +14 -0
- package/dist/app/entry.client.js.map +1 -1
- package/dist/config/validators/validate.d.ts +8 -8
- package/dist/lib/authentication/AuthenticationPlugin.d.ts +4 -2
- package/dist/lib/authentication/AuthenticationPlugin.js +3 -0
- package/dist/lib/authentication/AuthenticationPlugin.js.map +1 -1
- package/dist/lib/authentication/authentication.d.ts +1 -1
- package/dist/lib/authentication/hook.d.ts +5 -4
- package/dist/lib/authentication/hook.js +1 -3
- package/dist/lib/authentication/hook.js.map +1 -1
- package/dist/lib/authentication/providers/auth0.js +2 -2
- package/dist/lib/authentication/providers/auth0.js.map +1 -1
- package/dist/lib/authentication/providers/openid.d.ts +0 -1
- package/dist/lib/authentication/providers/openid.js +11 -26
- package/dist/lib/authentication/providers/openid.js.map +1 -1
- package/dist/lib/authentication/state.d.ts +25 -4
- package/dist/lib/authentication/state.js +28 -5
- package/dist/lib/authentication/state.js.map +1 -1
- package/dist/lib/components/Bootstrap.js +9 -5
- package/dist/lib/components/Bootstrap.js.map +1 -1
- package/dist/lib/components/Header.js +12 -4
- package/dist/lib/components/Header.js.map +1 -1
- package/dist/lib/components/Layout.js +12 -4
- package/dist/lib/components/Layout.js.map +1 -1
- package/dist/lib/components/MobileTopNavigation.js +5 -7
- package/dist/lib/components/MobileTopNavigation.js.map +1 -1
- package/dist/lib/components/SyntaxHighlight.js +2 -2
- package/dist/lib/components/SyntaxHighlight.js.map +1 -1
- package/dist/lib/components/ThemeSwitch.js +5 -3
- package/dist/lib/components/ThemeSwitch.js.map +1 -1
- package/dist/lib/components/TopNavigation.d.ts +2 -0
- package/dist/lib/components/TopNavigation.js +13 -7
- package/dist/lib/components/TopNavigation.js.map +1 -1
- package/dist/lib/components/index.d.ts +11 -1
- package/dist/lib/core/plugins.d.ts +6 -0
- package/dist/lib/core/plugins.js.map +1 -1
- package/dist/lib/plugins/api-keys/index.js +3 -0
- package/dist/lib/plugins/api-keys/index.js.map +1 -1
- package/dist/lib/plugins/openapi/ColorizedParam.js +2 -2
- package/dist/lib/plugins/openapi/ColorizedParam.js.map +1 -1
- package/dist/lib/plugins/openapi/schema/SchemaView.js.map +1 -1
- package/dist/vite/remarkStaticGeneration.js +5 -5
- package/dist/vite/remarkStaticGeneration.js.map +1 -1
- package/lib/AuthenticationPlugin-D0Em0SwR.js +59 -0
- package/lib/AuthenticationPlugin-D0Em0SwR.js.map +1 -0
- package/lib/{Markdown-BorQdbxW.js → Markdown-ievDDhFT.js} +2 -2
- package/lib/{Markdown-BorQdbxW.js.map → Markdown-ievDDhFT.js.map} +1 -1
- package/lib/{MdxPage-DFlbtJWi.js → MdxPage-Bwn-VSsH.js} +2 -2
- package/lib/{MdxPage-DFlbtJWi.js.map → MdxPage-Bwn-VSsH.js.map} +1 -1
- package/lib/{OperationList-KshJrrLL.js → OperationList-BwBl1xrD.js} +5 -5
- package/lib/{OperationList-KshJrrLL.js.map → OperationList-BwBl1xrD.js.map} +1 -1
- package/lib/{Select-DP74t8Yy.js → Select-O9ZM3ZgX.js} +2 -2
- package/lib/{Select-DP74t8Yy.js.map → Select-O9ZM3ZgX.js.map} +1 -1
- package/lib/{SlotletProvider-D2v6rJy1.js → SlotletProvider-DyomlzGx.js} +2 -2
- package/lib/{SlotletProvider-D2v6rJy1.js.map → SlotletProvider-DyomlzGx.js.map} +1 -1
- package/lib/{SyntaxHighlight-CBmwwKoM.js → SyntaxHighlight-DkLOsjHS.js} +2 -2
- package/lib/{SyntaxHighlight-CBmwwKoM.js.map → SyntaxHighlight-DkLOsjHS.js.map} +1 -1
- package/lib/{hook-Diu0rqp-.js → hook-hEqe7fPB.js} +12 -14
- package/lib/{hook-Diu0rqp-.js.map → hook-hEqe7fPB.js.map} +1 -1
- package/lib/{index-BcesIHH4.js → index-DNxQ_rCt.js} +7 -7
- package/lib/index-DNxQ_rCt.js.map +1 -0
- package/lib/state-tsXBLONe.js +203 -0
- package/lib/state-tsXBLONe.js.map +1 -0
- package/lib/zudoku.auth-auth0.js +9 -8
- package/lib/zudoku.auth-auth0.js.map +1 -1
- package/lib/zudoku.auth-clerk.js +2 -2
- package/lib/zudoku.auth-openid.js +119 -133
- package/lib/zudoku.auth-openid.js.map +1 -1
- package/lib/zudoku.components.js +596 -530
- package/lib/zudoku.components.js.map +1 -1
- package/lib/zudoku.plugin-api-keys.js +40 -38
- package/lib/zudoku.plugin-api-keys.js.map +1 -1
- package/lib/zudoku.plugin-custom-pages.js +1 -1
- package/lib/zudoku.plugin-markdown.js +1 -1
- package/lib/zudoku.plugin-openapi.js +2 -2
- package/package.json +5 -4
- package/src/app/entry.client.tsx +14 -0
- package/src/lib/authentication/AuthenticationPlugin.tsx +4 -1
- package/src/lib/authentication/authentication.ts +1 -1
- package/src/lib/authentication/hook.ts +1 -3
- package/src/lib/authentication/providers/auth0.tsx +3 -2
- package/src/lib/authentication/providers/openid.tsx +12 -30
- package/src/lib/authentication/state.ts +44 -10
- package/src/lib/components/Bootstrap.tsx +25 -18
- package/src/lib/components/Header.tsx +42 -9
- package/src/lib/components/Layout.tsx +49 -37
- package/src/lib/components/MobileTopNavigation.tsx +11 -18
- package/src/lib/components/SyntaxHighlight.tsx +3 -2
- package/src/lib/components/ThemeSwitch.tsx +6 -4
- package/src/lib/components/TopNavigation.tsx +25 -17
- package/src/lib/core/plugins.ts +8 -0
- package/src/lib/plugins/api-keys/index.tsx +3 -0
- package/src/lib/plugins/openapi/ColorizedParam.tsx +2 -2
- package/src/lib/plugins/openapi/schema/SchemaView.tsx +1 -1
- package/lib/AuthenticationPlugin-DeGDVa1r.js +0 -56
- package/lib/AuthenticationPlugin-DeGDVa1r.js.map +0 -1
- package/lib/index-BcesIHH4.js.map +0 -1
- package/lib/state-BsPrOUAh.js +0 -252
- package/lib/state-BsPrOUAh.js.map +0 -1
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
QueryClientProvider,
|
|
5
5
|
} from "@tanstack/react-query";
|
|
6
6
|
import { type HelmetData, HelmetProvider } from "@zudoku/react-helmet-async";
|
|
7
|
-
import { StrictMode
|
|
7
|
+
import { StrictMode } from "react";
|
|
8
8
|
import { type createBrowserRouter, RouterProvider } from "react-router-dom";
|
|
9
9
|
import {
|
|
10
10
|
type createStaticRouter,
|
|
@@ -13,29 +13,36 @@ import {
|
|
|
13
13
|
} from "react-router-dom/server.js";
|
|
14
14
|
import { StaggeredRenderContext } from "../plugins/openapi/StaggeredRender.js";
|
|
15
15
|
|
|
16
|
+
const queryClient = new QueryClient({
|
|
17
|
+
defaultOptions: {
|
|
18
|
+
queries: {
|
|
19
|
+
staleTime: 1000 * 60 * 5,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
16
24
|
const Bootstrap = ({
|
|
17
25
|
router,
|
|
18
26
|
hydrate = false,
|
|
19
27
|
}: {
|
|
20
28
|
hydrate?: boolean;
|
|
21
29
|
router: ReturnType<typeof createBrowserRouter>;
|
|
22
|
-
}) =>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
};
|
|
30
|
+
}) => (
|
|
31
|
+
<StrictMode>
|
|
32
|
+
<QueryClientProvider client={queryClient}>
|
|
33
|
+
<HydrationBoundary state={hydrate ? (window as any).DATA : undefined}>
|
|
34
|
+
<HelmetProvider>
|
|
35
|
+
<StaggeredRenderContext.Provider value={{ stagger: !hydrate }}>
|
|
36
|
+
<RouterProvider
|
|
37
|
+
router={router}
|
|
38
|
+
future={{ v7_startTransition: true }}
|
|
39
|
+
/>
|
|
40
|
+
</StaggeredRenderContext.Provider>
|
|
41
|
+
</HelmetProvider>
|
|
42
|
+
</HydrationBoundary>
|
|
43
|
+
</QueryClientProvider>
|
|
44
|
+
</StrictMode>
|
|
45
|
+
);
|
|
39
46
|
|
|
40
47
|
const BootstrapStatic = ({
|
|
41
48
|
router,
|
|
@@ -41,7 +41,12 @@ const RecursiveMenu = ({ item }: { item: ProfileNavigationItem }) => {
|
|
|
41
41
|
</DropdownMenuSub>
|
|
42
42
|
) : (
|
|
43
43
|
<Link to={item.path ?? ""}>
|
|
44
|
-
<DropdownMenuItem key={item.label}>
|
|
44
|
+
<DropdownMenuItem key={item.label} className="flex gap-2">
|
|
45
|
+
{item.icon && (
|
|
46
|
+
<item.icon size={16} strokeWidth={1} absoluteStrokeWidth />
|
|
47
|
+
)}
|
|
48
|
+
{item.label}
|
|
49
|
+
</DropdownMenuItem>
|
|
45
50
|
</Link>
|
|
46
51
|
);
|
|
47
52
|
};
|
|
@@ -55,7 +60,7 @@ export const Header = memo(function HeaderInner() {
|
|
|
55
60
|
const accountItems = plugins
|
|
56
61
|
.filter((p) => isProfileMenuPlugin(p))
|
|
57
62
|
.flatMap((p) => p.getProfileMenuItems(context))
|
|
58
|
-
.
|
|
63
|
+
.sort((i) => i.weight ?? 0);
|
|
59
64
|
|
|
60
65
|
return (
|
|
61
66
|
<header className="sticky lg:top-0 z-10 bg-background/80 backdrop-blur w-full">
|
|
@@ -82,7 +87,6 @@ export const Header = memo(function HeaderInner() {
|
|
|
82
87
|
loading="lazy"
|
|
83
88
|
/>
|
|
84
89
|
<img
|
|
85
|
-
data-hide-on-theme="light"
|
|
86
90
|
src={
|
|
87
91
|
/https?:\/\//.test(page.logo.src.dark)
|
|
88
92
|
? page.logo.src.dark
|
|
@@ -93,7 +97,7 @@ export const Header = memo(function HeaderInner() {
|
|
|
93
97
|
}
|
|
94
98
|
alt={page.logo.alt ?? page.pageTitle}
|
|
95
99
|
style={{ width: page.logo.width }}
|
|
96
|
-
className="h-10"
|
|
100
|
+
className="h-10 hidden dark:block"
|
|
97
101
|
loading="lazy"
|
|
98
102
|
/>
|
|
99
103
|
</>
|
|
@@ -121,17 +125,46 @@ export const Header = memo(function HeaderInner() {
|
|
|
121
125
|
Login
|
|
122
126
|
</Button>
|
|
123
127
|
) : (
|
|
124
|
-
accountItems.length > 0 && (
|
|
128
|
+
Object.values(accountItems).length > 0 && (
|
|
125
129
|
<DropdownMenu modal={false}>
|
|
126
130
|
<DropdownMenuTrigger asChild>
|
|
127
131
|
<Button variant="ghost">
|
|
128
|
-
{profile?.
|
|
132
|
+
{profile?.name ? `${profile.name}` : "My Account"}
|
|
129
133
|
</Button>
|
|
130
134
|
</DropdownMenuTrigger>
|
|
131
135
|
<DropdownMenuContent className="w-56">
|
|
132
|
-
<DropdownMenuLabel>
|
|
133
|
-
|
|
134
|
-
|
|
136
|
+
<DropdownMenuLabel>
|
|
137
|
+
{profile?.name ? `${profile.name}` : "My Account"}
|
|
138
|
+
{profile?.email && (
|
|
139
|
+
<div className="font-normal text-muted-foreground">
|
|
140
|
+
{profile.email}
|
|
141
|
+
</div>
|
|
142
|
+
)}
|
|
143
|
+
</DropdownMenuLabel>
|
|
144
|
+
{accountItems.filter((i) => i.category === "top")
|
|
145
|
+
.length > 0 && <DropdownMenuSeparator />}
|
|
146
|
+
{accountItems
|
|
147
|
+
.filter((i) => i.category === "top")
|
|
148
|
+
.map((i) => (
|
|
149
|
+
<RecursiveMenu key={i.label} item={i} />
|
|
150
|
+
))}
|
|
151
|
+
{accountItems.filter(
|
|
152
|
+
(i) => !i.category || i.category === "middle",
|
|
153
|
+
).length > 0 && <DropdownMenuSeparator />}
|
|
154
|
+
{accountItems
|
|
155
|
+
.filter(
|
|
156
|
+
(i) => !i.category || i.category === "middle",
|
|
157
|
+
)
|
|
158
|
+
.map((i) => (
|
|
159
|
+
<RecursiveMenu key={i.label} item={i} />
|
|
160
|
+
))}
|
|
161
|
+
{accountItems.filter((i) => i.category === "bottom")
|
|
162
|
+
.length > 0 && <DropdownMenuSeparator />}
|
|
163
|
+
{accountItems
|
|
164
|
+
.filter((i) => i.category === "bottom")
|
|
165
|
+
.map((i) => (
|
|
166
|
+
<RecursiveMenu key={i.label} item={i} />
|
|
167
|
+
))}
|
|
135
168
|
</DropdownMenuContent>
|
|
136
169
|
</DropdownMenu>
|
|
137
170
|
)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Helmet } from "@zudoku/react-helmet-async";
|
|
2
2
|
import { PanelLeftIcon } from "lucide-react";
|
|
3
3
|
import { Suspense, useEffect, useRef, type ReactNode } from "react";
|
|
4
|
-
import { Outlet, useLocation } from "react-router-dom";
|
|
4
|
+
import { Outlet, useLocation, useNavigation } from "react-router-dom";
|
|
5
|
+
import { useSpinDelay } from "spin-delay";
|
|
5
6
|
import { Drawer, DrawerTrigger } from "../ui/Drawer.js";
|
|
6
7
|
import { cn } from "../util/cn.js";
|
|
7
8
|
import { useScrollToAnchor } from "../util/useScrollToAnchor.js";
|
|
@@ -13,6 +14,12 @@ import { Sidebar } from "./navigation/Sidebar.js";
|
|
|
13
14
|
import { Slotlet } from "./SlotletProvider.js";
|
|
14
15
|
import { Spinner } from "./Spinner.js";
|
|
15
16
|
|
|
17
|
+
const LoadingFallback = () => (
|
|
18
|
+
<main className="grid h-[calc(100vh-var(--header-height))] place-items-center">
|
|
19
|
+
<Spinner />
|
|
20
|
+
</main>
|
|
21
|
+
);
|
|
22
|
+
|
|
16
23
|
export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
17
24
|
const location = useLocation();
|
|
18
25
|
const { setActiveAnchor } = useViewportAnchor();
|
|
@@ -25,7 +32,7 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
|
25
32
|
|
|
26
33
|
useEffect(() => {
|
|
27
34
|
// Initialize the authentication plugin
|
|
28
|
-
authentication?.
|
|
35
|
+
authentication?.onPageLoad?.();
|
|
29
36
|
}, [authentication]);
|
|
30
37
|
|
|
31
38
|
useEffect(() => {
|
|
@@ -36,6 +43,13 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
|
36
43
|
previousLocationPath.current = location.pathname;
|
|
37
44
|
}, [location.pathname, setActiveAnchor]);
|
|
38
45
|
|
|
46
|
+
// Page transition is happening: https://reactrouter.com/start/framework/pending-ui#global-pending-navigation
|
|
47
|
+
const isNavigating = Boolean(useNavigation().location);
|
|
48
|
+
const showSpinner = useSpinDelay(isNavigating, {
|
|
49
|
+
delay: 300,
|
|
50
|
+
minDuration: 500,
|
|
51
|
+
});
|
|
52
|
+
|
|
39
53
|
return (
|
|
40
54
|
<>
|
|
41
55
|
{import.meta.env.MODE === "standalone" && (
|
|
@@ -52,41 +66,39 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
|
52
66
|
<Slotlet name="layout-after-head" />
|
|
53
67
|
|
|
54
68
|
<div className="w-full max-w-screen-2xl mx-auto px-10 lg:px-12">
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
</Drawer>
|
|
89
|
-
</Suspense>
|
|
69
|
+
{showSpinner ? (
|
|
70
|
+
<LoadingFallback />
|
|
71
|
+
) : (
|
|
72
|
+
<Suspense fallback={<LoadingFallback />}>
|
|
73
|
+
<Drawer direction="left">
|
|
74
|
+
<Sidebar />
|
|
75
|
+
<div
|
|
76
|
+
className={cn(
|
|
77
|
+
"lg:hidden -mx-10 px-10 py-2 sticky bg-background/80 backdrop-blur z-10 top-0 left-0 right-0 border-b",
|
|
78
|
+
"peer-data-[navigation=false]:hidden",
|
|
79
|
+
)}
|
|
80
|
+
>
|
|
81
|
+
<DrawerTrigger className="flex items-center gap-2">
|
|
82
|
+
<PanelLeftIcon size={16} strokeWidth={1.5} />
|
|
83
|
+
<span className="text-sm">Menu</span>
|
|
84
|
+
</DrawerTrigger>
|
|
85
|
+
</div>
|
|
86
|
+
<main
|
|
87
|
+
className={cn(
|
|
88
|
+
"h-full dark:border-white/10 translate-x-0",
|
|
89
|
+
"lg:overflow-visible",
|
|
90
|
+
// This works in tandem with the `SidebarWrapper` component
|
|
91
|
+
"lg:peer-data-[navigation=true]:w-[calc(100%-var(--side-nav-width))]",
|
|
92
|
+
"lg:peer-data-[navigation=true]:translate-x-[--side-nav-width] lg:peer-data-[navigation=true]:pl-12",
|
|
93
|
+
)}
|
|
94
|
+
>
|
|
95
|
+
<Slotlet name="zudoku-before-content" />
|
|
96
|
+
{children ?? <Outlet />}
|
|
97
|
+
<Slotlet name="zudoku-after-content" />
|
|
98
|
+
</main>
|
|
99
|
+
</Drawer>
|
|
100
|
+
</Suspense>
|
|
101
|
+
)}
|
|
90
102
|
</div>
|
|
91
103
|
</>
|
|
92
104
|
);
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
|
2
|
-
import { cx } from "class-variance-authority";
|
|
3
2
|
import { MenuIcon } from "lucide-react";
|
|
4
|
-
import {
|
|
3
|
+
import { useState } from "react";
|
|
5
4
|
import { useAuth } from "../authentication/hook.js";
|
|
6
5
|
import {
|
|
7
6
|
Drawer,
|
|
8
|
-
DrawerClose,
|
|
9
7
|
DrawerContent,
|
|
10
8
|
DrawerTitle,
|
|
11
9
|
DrawerTrigger,
|
|
@@ -13,14 +11,19 @@ import {
|
|
|
13
11
|
import { useZudoku } from "./context/ZudokuContext.js";
|
|
14
12
|
import { Search } from "./Search.js";
|
|
15
13
|
import { ThemeSwitch } from "./ThemeSwitch.js";
|
|
16
|
-
import { isHiddenItem } from "./TopNavigation.js";
|
|
14
|
+
import { isHiddenItem, TopNavItem } from "./TopNavigation.js";
|
|
17
15
|
|
|
18
16
|
export const MobileTopNavigation = () => {
|
|
19
17
|
const { topNavigation } = useZudoku();
|
|
20
18
|
const { isAuthenticated } = useAuth();
|
|
19
|
+
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
21
20
|
|
|
22
21
|
return (
|
|
23
|
-
<Drawer
|
|
22
|
+
<Drawer
|
|
23
|
+
direction="right"
|
|
24
|
+
open={drawerOpen}
|
|
25
|
+
onOpenChange={(open) => setDrawerOpen(open)}
|
|
26
|
+
>
|
|
24
27
|
<div className="flex lg:hidden justify-self-end">
|
|
25
28
|
<DrawerTrigger className="lg:hidden">
|
|
26
29
|
<MenuIcon size={22} />
|
|
@@ -42,19 +45,9 @@ export const MobileTopNavigation = () => {
|
|
|
42
45
|
</li>
|
|
43
46
|
{topNavigation.filter(isHiddenItem(isAuthenticated)).map((item) => (
|
|
44
47
|
<li key={item.label}>
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"block font-medium border-b-2",
|
|
49
|
-
isActive
|
|
50
|
-
? "border-primary text-foreground"
|
|
51
|
-
: "border-transparent text-foreground/75 hover:text-foreground hover:border-accent-foreground/25",
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
to={item.id}
|
|
55
|
-
>
|
|
56
|
-
<DrawerClose>{item.label}</DrawerClose>
|
|
57
|
-
</NavLink>
|
|
48
|
+
<button onClick={() => setDrawerOpen(false)}>
|
|
49
|
+
<TopNavItem {...item} />
|
|
50
|
+
</button>
|
|
58
51
|
</li>
|
|
59
52
|
))}
|
|
60
53
|
</ul>
|
|
@@ -56,14 +56,15 @@ export const SyntaxHighlight = ({
|
|
|
56
56
|
language = "plain",
|
|
57
57
|
...props
|
|
58
58
|
}: SyntaxHighlightProps) => {
|
|
59
|
-
const {
|
|
59
|
+
const { resolvedTheme } = useTheme();
|
|
60
60
|
const [isCopied, setIsCopied] = useState(false);
|
|
61
61
|
|
|
62
62
|
if (!props.code) {
|
|
63
63
|
return null;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
const highlightTheme =
|
|
66
|
+
const highlightTheme =
|
|
67
|
+
resolvedTheme === "dark" ? themes.vsDark : themes.github;
|
|
67
68
|
|
|
68
69
|
// hardcoded values from the themes to avoid color flash in SSR
|
|
69
70
|
const themeColorClasses =
|
|
@@ -4,18 +4,20 @@ import { Button } from "zudoku/ui/Button.js";
|
|
|
4
4
|
import { ClientOnly } from "./ClientOnly.js";
|
|
5
5
|
|
|
6
6
|
export const ThemeSwitch = () => {
|
|
7
|
-
const {
|
|
8
|
-
const ThemeIcon =
|
|
7
|
+
const { resolvedTheme, setTheme } = useTheme();
|
|
8
|
+
const ThemeIcon = resolvedTheme === "dark" ? MoonStarIcon : SunIcon;
|
|
9
9
|
|
|
10
10
|
return (
|
|
11
11
|
<ClientOnly>
|
|
12
12
|
<Button
|
|
13
13
|
variant="ghost"
|
|
14
14
|
aria-label={
|
|
15
|
-
|
|
15
|
+
resolvedTheme === "dark"
|
|
16
|
+
? "Switch to light mode"
|
|
17
|
+
: "Switch to dark mode"
|
|
16
18
|
}
|
|
17
19
|
className="p-2.5 -m-2.5 rounded-full"
|
|
18
|
-
onClick={() => setTheme(
|
|
20
|
+
onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
|
|
19
21
|
>
|
|
20
22
|
<ThemeIcon size={18} />
|
|
21
23
|
</Button>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { cx } from "class-variance-authority";
|
|
2
2
|
import { Suspense } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { NavLink, useNavigation } from "react-router-dom";
|
|
4
4
|
import { TopNavigationItem } from "../../config/validators/validate.js";
|
|
5
5
|
import { useAuth } from "../authentication/hook.js";
|
|
6
|
+
import { ZudokuError } from "../util/invariant.js";
|
|
6
7
|
import { joinPath } from "../util/joinPath.js";
|
|
7
8
|
import { useCurrentNavigation, useZudoku } from "./context/ZudokuContext.js";
|
|
8
9
|
import { traverseSidebar } from "./navigation/utils.js";
|
|
@@ -42,10 +43,16 @@ export const TopNavigation = () => {
|
|
|
42
43
|
);
|
|
43
44
|
};
|
|
44
45
|
|
|
45
|
-
const TopNavItem = ({
|
|
46
|
+
export const TopNavItem = ({
|
|
47
|
+
id,
|
|
48
|
+
label,
|
|
49
|
+
default: defaultLink,
|
|
50
|
+
}: TopNavigationItem) => {
|
|
46
51
|
const { sidebars } = useZudoku();
|
|
47
|
-
const nav = useCurrentNavigation();
|
|
48
52
|
const currentSidebar = sidebars[id];
|
|
53
|
+
const currentNav = useCurrentNavigation();
|
|
54
|
+
const isNavigating = Boolean(useNavigation().location);
|
|
55
|
+
const isActive = currentNav.topNavItem?.id === id && !isNavigating;
|
|
49
56
|
|
|
50
57
|
// TODO: This is a bit of a hack to get the first link in the sidebar
|
|
51
58
|
// We should really process this when we load the config so we can validate
|
|
@@ -60,25 +67,26 @@ const TopNavItem = ({ id, label, default: defaultLink }: TopNavigationItem) => {
|
|
|
60
67
|
: joinPath(id));
|
|
61
68
|
|
|
62
69
|
if (!first) {
|
|
63
|
-
throw new
|
|
64
|
-
`No links found in top navigation for
|
|
65
|
-
);
|
|
70
|
+
throw new ZudokuError("Page not found.", {
|
|
71
|
+
developerHint: `No links found in top navigation for '${id}'. Check that the sidebar isn't empty or that a default link is set.`,
|
|
72
|
+
});
|
|
66
73
|
}
|
|
67
74
|
|
|
68
|
-
// Manually set the active sidebar based on our logic of what is active
|
|
69
|
-
const isActive = nav.topNavItem?.id === id;
|
|
70
|
-
|
|
71
75
|
return (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
:
|
|
78
|
-
|
|
76
|
+
// We don't use isActive here because it has to be inside the sidebar,
|
|
77
|
+
// the top nav id doesn't necessarily start with the sidebar id
|
|
78
|
+
<NavLink
|
|
79
|
+
className={({ isPending }) =>
|
|
80
|
+
cx(
|
|
81
|
+
"block lg:py-3.5 font-medium -mb-px border-b-2",
|
|
82
|
+
isActive || isPending
|
|
83
|
+
? "border-primary text-foreground"
|
|
84
|
+
: "border-transparent text-foreground/75 hover:text-foreground hover:border-accent-foreground/25",
|
|
85
|
+
)
|
|
86
|
+
}
|
|
79
87
|
to={first}
|
|
80
88
|
>
|
|
81
89
|
{label}
|
|
82
|
-
</
|
|
90
|
+
</NavLink>
|
|
83
91
|
);
|
|
84
92
|
};
|
package/src/lib/core/plugins.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { LucideProps } from "lucide-react";
|
|
1
2
|
import { type ReactElement } from "react";
|
|
2
3
|
import { type RouteObject } from "react-router-dom";
|
|
3
4
|
import type { Sidebar } from "../../config/validators/SidebarSchema.js";
|
|
@@ -36,7 +37,14 @@ export interface ProfileMenuPlugin {
|
|
|
36
37
|
export type ProfileNavigationItem = {
|
|
37
38
|
label: string;
|
|
38
39
|
path?: string;
|
|
40
|
+
weight?: number;
|
|
41
|
+
category?: "top" | "middle" | "bottom";
|
|
39
42
|
children?: ProfileNavigationItem[];
|
|
43
|
+
icon?: React.ComponentType<
|
|
44
|
+
LucideProps & {
|
|
45
|
+
[key: string]: any;
|
|
46
|
+
}
|
|
47
|
+
>;
|
|
40
48
|
};
|
|
41
49
|
|
|
42
50
|
export interface CommonPlugin {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FileKey2Icon } from "lucide-react";
|
|
1
2
|
import { type RouteObject } from "react-router-dom";
|
|
2
3
|
import { ZudokuContext } from "../../core/ZudokuContext.js";
|
|
3
4
|
import {
|
|
@@ -105,6 +106,8 @@ export const apiKeyPlugin = (
|
|
|
105
106
|
{
|
|
106
107
|
label: "API Keys",
|
|
107
108
|
path: "/settings/api-keys",
|
|
109
|
+
category: "middle",
|
|
110
|
+
icon: FileKey2Icon,
|
|
108
111
|
},
|
|
109
112
|
],
|
|
110
113
|
getIdentities: async (context) => {
|
|
@@ -6,10 +6,10 @@ import { pastellize } from "../../util/pastellize.js";
|
|
|
6
6
|
export const DATA_ATTR = "data-linked-param";
|
|
7
7
|
|
|
8
8
|
export const usePastellizedColor = (name: string) => {
|
|
9
|
-
const {
|
|
9
|
+
const { resolvedTheme } = useTheme();
|
|
10
10
|
return pastellize(
|
|
11
11
|
name,
|
|
12
|
-
|
|
12
|
+
resolvedTheme === "light" ? { saturation: 85, lightness: 50 } : undefined,
|
|
13
13
|
);
|
|
14
14
|
};
|
|
15
15
|
|
|
@@ -73,7 +73,7 @@ export const SchemaView = ({
|
|
|
73
73
|
) {
|
|
74
74
|
return (
|
|
75
75
|
<Card className="p-4 flex gap-2 items-center">
|
|
76
|
-
{"name" in schema && <>{schema.name}</>}
|
|
76
|
+
{"name" in schema && <>{schema.name as string}</>}
|
|
77
77
|
<span className="text-sm text-muted-foreground">object</span>
|
|
78
78
|
{schema.description && (
|
|
79
79
|
<Markdown
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { j as i } from "./jsx-runtime-B6kdoens.js";
|
|
2
|
-
import { useEffect as o } from "react";
|
|
3
|
-
import { u as a } from "./index-Yn8c3UWE.js";
|
|
4
|
-
import { u as s } from "./utils-DcpDOncX.js";
|
|
5
|
-
import { a as u } from "./index-Czzd9rjU.js";
|
|
6
|
-
const r = () => {
|
|
7
|
-
const t = s(), [n] = a();
|
|
8
|
-
return o(() => {
|
|
9
|
-
var e;
|
|
10
|
-
(e = t.authentication) == null || e.signIn({
|
|
11
|
-
redirectTo: n.get("redirect") ?? void 0
|
|
12
|
-
});
|
|
13
|
-
}, [t.authentication, n]), null;
|
|
14
|
-
}, c = () => {
|
|
15
|
-
const t = s(), n = u();
|
|
16
|
-
return o(() => {
|
|
17
|
-
var e;
|
|
18
|
-
(e = t.authentication) == null || e.signOut().then(() => n("/"));
|
|
19
|
-
}, [n, t.authentication]), null;
|
|
20
|
-
}, g = () => {
|
|
21
|
-
const t = s();
|
|
22
|
-
return o(() => {
|
|
23
|
-
var n, e;
|
|
24
|
-
((n = t.authentication) == null ? void 0 : n.signUp()) ?? ((e = t.authentication) == null || e.signIn());
|
|
25
|
-
}, [t.authentication]), null;
|
|
26
|
-
};
|
|
27
|
-
class f {
|
|
28
|
-
getRoutes() {
|
|
29
|
-
return [
|
|
30
|
-
{
|
|
31
|
-
path: "/signout",
|
|
32
|
-
element: /* @__PURE__ */ i.jsx(c, {})
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
path: "/signin",
|
|
36
|
-
element: /* @__PURE__ */ i.jsx(r, {})
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
path: "/signup",
|
|
40
|
-
element: /* @__PURE__ */ i.jsx(g, {})
|
|
41
|
-
}
|
|
42
|
-
];
|
|
43
|
-
}
|
|
44
|
-
getProfileMenuItems() {
|
|
45
|
-
return [
|
|
46
|
-
{
|
|
47
|
-
label: "Logout",
|
|
48
|
-
path: "/signout"
|
|
49
|
-
}
|
|
50
|
-
];
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
export {
|
|
54
|
-
f as A
|
|
55
|
-
};
|
|
56
|
-
//# sourceMappingURL=AuthenticationPlugin-DeGDVa1r.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AuthenticationPlugin-DeGDVa1r.js","sources":["../src/lib/authentication/components/SignIn.tsx","../src/lib/authentication/components/SignOut.tsx","../src/lib/authentication/components/SignUp.tsx","../src/lib/authentication/AuthenticationPlugin.tsx"],"sourcesContent":["import { useEffect } from \"react\";\nimport { useSearchParams } from \"react-router-dom\";\nimport { useZudoku } from \"../../components/context/ZudokuContext.js\";\n\nexport const SignIn = () => {\n const context = useZudoku();\n const [search] = useSearchParams();\n useEffect(() => {\n void context.authentication?.signIn({\n redirectTo: search.get(\"redirect\") ?? undefined,\n });\n }, [context.authentication, search]);\n\n return null;\n};\n","import { useEffect } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useZudoku } from \"../../components/context/ZudokuContext.js\";\n\nexport const SignOut = () => {\n const context = useZudoku();\n const navigate = useNavigate();\n useEffect(() => {\n void context.authentication?.signOut().then(() => navigate(\"/\"));\n }, [navigate, context.authentication]);\n\n return null;\n};\n","import { useEffect } from \"react\";\nimport { useZudoku } from \"../../components/context/ZudokuContext.js\";\n\nexport const SignUp = () => {\n const context = useZudoku();\n useEffect(() => {\n void (context.authentication?.signUp() ?? context.authentication?.signIn());\n }, [context.authentication]);\n\n return null;\n};\n","import {\n CommonPlugin,\n NavigationPlugin,\n ProfileMenuPlugin,\n} from \"../core/plugins.js\";\nimport { SignIn } from \"./components/SignIn.js\";\nimport { SignOut } from \"./components/SignOut.js\";\nimport { SignUp } from \"./components/SignUp.js\";\n\ntype PluginInterface = NavigationPlugin & CommonPlugin & ProfileMenuPlugin;\n\nexport class AuthenticationPlugin implements PluginInterface {\n getRoutes() {\n return [\n {\n path: \"/signout\",\n element: <SignOut />,\n },\n {\n path: \"/signin\",\n element: <SignIn />,\n },\n {\n path: \"/signup\",\n element: <SignUp />,\n },\n ];\n }\n\n getProfileMenuItems() {\n return [\n {\n label: \"Logout\",\n path: \"/signout\",\n },\n ];\n }\n}\n"],"names":["SignIn","context","useZudoku","search","useSearchParams","useEffect","_a","SignOut","navigate","useNavigate","SignUp","_b","AuthenticationPlugin"],"mappings":";;;;;AAIO,MAAMA,IAAS,MAAM;AAC1B,QAAMC,IAAUC,KACV,CAACC,CAAM,IAAIC;AACjB,SAAAC,EAAU,MAAM;;AACT,KAAAC,IAAAL,EAAQ,mBAAR,QAAAK,EAAwB,OAAO;AAAA,MAClC,YAAYH,EAAO,IAAI,UAAU,KAAK;AAAA,IAAA;AAAA,EAEvC,GAAA,CAACF,EAAQ,gBAAgBE,CAAM,CAAC,GAE5B;AACT,GCVaI,IAAU,MAAM;AAC3B,QAAMN,IAAUC,KACVM,IAAWC;AACjB,SAAAJ,EAAU,MAAM;;AACT,KAAAC,IAAAL,EAAQ,mBAAR,QAAAK,EAAwB,UAAU,KAAK,MAAME,EAAS,GAAG;AAAA,EAC7D,GAAA,CAACA,GAAUP,EAAQ,cAAc,CAAC,GAE9B;AACT,GCTaS,IAAS,MAAM;AAC1B,QAAMT,IAAUC;AAChB,SAAAG,EAAU,MAAM;;AACd,MAAMC,IAAAL,EAAQ,mBAAR,gBAAAK,EAAwB,eAAYK,IAAAV,EAAQ,mBAAR,QAAAU,EAAwB;AAAA,EAAO,GACxE,CAACV,EAAQ,cAAc,CAAC,GAEpB;AACT;ACCO,MAAMW,EAAgD;AAAA,EAC3D,YAAY;AACH,WAAA;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,+BAAUL,GAAQ,EAAA;AAAA,MACpB;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,+BAAUP,GAAO,EAAA;AAAA,MACnB;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,+BAAUU,GAAO,EAAA;AAAA,MACnB;AAAA,IAAA;AAAA,EAEJ;AAAA,EAEA,sBAAsB;AACb,WAAA;AAAA,MACL;AAAA,QACE,OAAO;AAAA,QACP,MAAM;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACF;"}
|