zudoku 0.34.4 → 0.35.0
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/config/config.d.ts +13 -8
- package/dist/config/validators/common.d.ts +977 -111
- package/dist/config/validators/common.js +26 -1
- package/dist/config/validators/common.js.map +1 -1
- package/dist/config/validators/validate.d.ts +357 -42
- package/dist/lib/authentication/providers/clerk.d.ts +2 -2
- package/dist/lib/authentication/providers/supabase.d.ts +4 -0
- package/dist/lib/authentication/providers/supabase.js +112 -0
- package/dist/lib/authentication/providers/supabase.js.map +1 -0
- package/dist/lib/components/Header.js +3 -3
- package/dist/lib/components/Header.js.map +1 -1
- package/dist/lib/components/InlineCode.js +1 -1
- package/dist/lib/components/InlineCode.js.map +1 -1
- package/dist/lib/components/Layout.js +4 -10
- package/dist/lib/components/Layout.js.map +1 -1
- package/dist/lib/components/Main.d.ts +2 -0
- package/dist/lib/components/Main.js +15 -0
- package/dist/lib/components/Main.js.map +1 -0
- package/dist/lib/components/context/ViewportAnchorContext.d.ts +2 -4
- package/dist/lib/components/context/ViewportAnchorContext.js +2 -4
- package/dist/lib/components/context/ViewportAnchorContext.js.map +1 -1
- package/dist/lib/components/index.d.ts +2 -1
- package/dist/lib/components/navigation/Sidebar.d.ts +3 -1
- package/dist/lib/components/navigation/Sidebar.js +2 -4
- package/dist/lib/components/navigation/Sidebar.js.map +1 -1
- package/dist/lib/components/navigation/SidebarWrapper.d.ts +8 -6
- package/dist/lib/components/navigation/SidebarWrapper.js +1 -2
- package/dist/lib/components/navigation/SidebarWrapper.js.map +1 -1
- package/dist/lib/core/RouteGuard.js +2 -1
- package/dist/lib/core/RouteGuard.js.map +1 -1
- package/dist/lib/oas/graphql/index.js +34 -0
- package/dist/lib/oas/graphql/index.js.map +1 -1
- package/dist/lib/plugins/markdown/MdxPage.d.ts +1 -1
- package/dist/lib/plugins/markdown/MdxPage.js +1 -1
- package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
- package/dist/lib/plugins/openapi/OperationList.js +2 -1
- package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
- package/dist/lib/plugins/openapi/Sidecar.js +9 -4
- package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
- package/dist/lib/plugins/openapi/SimpleSelect.js +1 -1
- package/dist/lib/plugins/openapi/SimpleSelect.js.map +1 -1
- package/dist/lib/plugins/openapi/graphql/gql.d.ts +1 -1
- package/dist/lib/plugins/openapi/graphql/gql.js +1 -1
- package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
- package/dist/lib/plugins/openapi/graphql/graphql.d.ts +6 -0
- package/dist/lib/plugins/openapi/graphql/graphql.js +2 -0
- package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
- package/dist/lib/plugins/openapi/index.d.ts +1 -1
- package/dist/lib/plugins/openapi/index.js +8 -8
- package/dist/lib/plugins/openapi/index.js.map +1 -1
- package/dist/lib/plugins/openapi/interfaces.d.ts +7 -10
- package/dist/lib/ui/Callout.d.ts +2 -1
- package/dist/lib/ui/Callout.js +3 -2
- package/dist/lib/ui/Callout.js.map +1 -1
- package/dist/lib/util/MdxComponents.d.ts +2 -1
- package/dist/vite/plugin-api.js +5 -9
- package/dist/vite/plugin-api.js.map +1 -1
- package/lib/{Callout-B_sEhkYd.js → Callout-B2vsR09t.js} +70 -52
- package/lib/{Callout-B_sEhkYd.js.map → Callout-B2vsR09t.js.map} +1 -1
- package/lib/Drawer-kDAfOq_2.js +1133 -0
- package/lib/Drawer-kDAfOq_2.js.map +1 -0
- package/lib/{Markdown-DZXjQjpH.js → Markdown-D1Y3cd9l.js} +4471 -3315
- package/lib/Markdown-D1Y3cd9l.js.map +1 -0
- package/lib/{MdxPage-52vRwa_7.js → MdxPage-CUL_SQzW.js} +9 -9
- package/lib/MdxPage-CUL_SQzW.js.map +1 -0
- package/lib/{OasProvider-CDyf845G.js → OasProvider-DEL8ulKm.js} +2 -2
- package/lib/{OasProvider-CDyf845G.js.map → OasProvider-DEL8ulKm.js.map} +1 -1
- package/lib/{OperationList-DdCWaqeE.js → OperationList-D6goKbzX.js} +179 -179
- package/lib/OperationList-D6goKbzX.js.map +1 -0
- package/lib/{SlotletProvider-TydSHROc.js → SlotletProvider-iDmNlxD5.js} +2 -2
- package/lib/{SlotletProvider-TydSHROc.js.map → SlotletProvider-iDmNlxD5.js.map} +1 -1
- package/lib/{createServer-DmusVVsi.js → createServer-BNBGpbLa.js} +1764 -1734
- package/lib/{createServer-DmusVVsi.js.map → createServer-BNBGpbLa.js.map} +1 -1
- package/lib/{index-BO-sA1cw.js → index-PKytqmuw.js} +348 -346
- package/lib/index-PKytqmuw.js.map +1 -0
- package/lib/prism-bash.min-HHIMdNJ_.js.map +1 -1
- package/lib/prism-csharp.min-bQAo2pmx.js.map +1 -1
- package/lib/prism-java.min-BpvsOuIa.js.map +1 -1
- package/lib/prism-javascript.min-CEqHqgbm.js.map +1 -1
- package/lib/prism-json.min-B1GJqK1k.js.map +1 -1
- package/lib/prism-markdown.min-C0Qn0m-5.js.map +1 -1
- package/lib/prism-markup-BNGj0Tvm.js.map +1 -1
- package/lib/prism-objectivec.min-BXSWqpJJ.js.map +1 -1
- package/lib/prism-ruby.min-Dx9KO9ds.js.map +1 -1
- package/lib/prism-typescript.min-CD7H2IYQ.js.map +1 -1
- package/lib/ui/Callout.js +39 -21
- package/lib/ui/Callout.js.map +1 -1
- package/lib/ui/Drawer.js +14 -1129
- package/lib/ui/Drawer.js.map +1 -1
- package/lib/zudoku.auth-clerk.js.map +1 -1
- package/lib/zudoku.components.js +675 -621
- package/lib/zudoku.components.js.map +1 -1
- package/lib/zudoku.plugin-api-catalog.js +1 -1
- package/lib/zudoku.plugin-api-keys.js +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 +1 -1
- package/lib/zudoku.plugin-search-pagefind.js +1 -1
- package/package.json +11 -6
- package/src/app/main.css +5 -3
- package/src/lib/authentication/providers/clerk.tsx +2 -2
- package/src/lib/authentication/providers/supabase.tsx +151 -0
- package/src/lib/components/Header.tsx +10 -6
- package/src/lib/components/InlineCode.tsx +1 -1
- package/src/lib/components/Layout.tsx +5 -40
- package/src/lib/components/Main.tsx +47 -0
- package/src/lib/components/context/ViewportAnchorContext.tsx +3 -9
- package/src/lib/components/navigation/Sidebar.tsx +7 -9
- package/src/lib/components/navigation/SidebarWrapper.tsx +13 -15
- package/src/lib/core/RouteGuard.tsx +2 -1
- package/src/lib/oas/graphql/index.ts +35 -0
- package/src/lib/plugins/markdown/MdxPage.tsx +2 -2
- package/src/lib/plugins/openapi/OperationList.tsx +3 -2
- package/src/lib/plugins/openapi/Sidecar.tsx +11 -4
- package/src/lib/plugins/openapi/SimpleSelect.tsx +1 -1
- package/src/lib/plugins/openapi/graphql/gql.ts +3 -3
- package/src/lib/plugins/openapi/graphql/graphql.ts +8 -0
- package/src/lib/plugins/openapi/index.tsx +10 -11
- package/src/lib/plugins/openapi/interfaces.ts +11 -15
- package/src/lib/ui/Callout.tsx +28 -10
- package/lib/Markdown-DZXjQjpH.js.map +0 -1
- package/lib/MdxPage-52vRwa_7.js.map +0 -1
- package/lib/OperationList-DdCWaqeE.js.map +0 -1
- package/lib/index-BO-sA1cw.js.map +0 -1
|
@@ -5,7 +5,7 @@ import { j as d } from "./joinUrl-10po2Jdj.js";
|
|
|
5
5
|
import { u as j, b as y } from "./hook-CfCFKZ-2.js";
|
|
6
6
|
import { H as v } from "./index.esm-CltAN0Tf.js";
|
|
7
7
|
import { Link as N } from "./zudoku.components.js";
|
|
8
|
-
import { H as S, M as w } from "./Markdown-
|
|
8
|
+
import { H as S, M as w } from "./Markdown-D1Y3cd9l.js";
|
|
9
9
|
const H = ({
|
|
10
10
|
items: s,
|
|
11
11
|
filterCatalogItems: l = (n) => n,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { j as e } from "./jsx-runtime-CYK1ROHF.js";
|
|
2
2
|
import { RotateCwIcon as j, TrashIcon as v, EyeOffIcon as w, EyeIcon as K, CheckIcon as b, CopyIcon as k, FileKey2Icon as N } from "lucide-react";
|
|
3
|
-
import { D as I, S as x, R as S } from "./SlotletProvider-
|
|
3
|
+
import { D as I, S as x, R as S } from "./SlotletProvider-iDmNlxD5.js";
|
|
4
4
|
import { i as c } from "./invariant-Caa8-XvF.js";
|
|
5
5
|
import { u as h } from "./useQuery-CQUwWR9i.js";
|
|
6
6
|
import { u as d, S as A, a as C, b as E, c as P, d as D, e as p } from "./Select-FAYHOYTy.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { j as o } from "./jsx-runtime-CYK1ROHF.js";
|
|
2
2
|
import a from "react";
|
|
3
|
-
import { P as n } from "./Markdown-
|
|
3
|
+
import { P as n } from "./Markdown-D1Y3cd9l.js";
|
|
4
4
|
import { c } from "./cn-qaFjX9_3.js";
|
|
5
5
|
import { u as p } from "./useExposedProps-BslIn-FE.js";
|
|
6
6
|
const u = ({
|
|
@@ -53,7 +53,7 @@ const P = (e) => ({
|
|
|
53
53
|
const u = {
|
|
54
54
|
path: r,
|
|
55
55
|
lazy: async () => {
|
|
56
|
-
const { MdxPage: p } = await import("./MdxPage-
|
|
56
|
+
const { MdxPage: p } = await import("./MdxPage-CUL_SQzW.js"), { default: f, ...l } = await i();
|
|
57
57
|
return {
|
|
58
58
|
element: /* @__PURE__ */ d.jsx(
|
|
59
59
|
p,
|
|
@@ -5,7 +5,7 @@ import "./chunk-HA7DTUK3-ZGg2W6yV.js";
|
|
|
5
5
|
import "./hook-CfCFKZ-2.js";
|
|
6
6
|
import "./ui/Button.js";
|
|
7
7
|
import "./joinUrl-10po2Jdj.js";
|
|
8
|
-
import { U as n, o as s } from "./index-
|
|
8
|
+
import { U as n, o as s } from "./index-PKytqmuw.js";
|
|
9
9
|
export {
|
|
10
10
|
n as UNTAGGED_PATH,
|
|
11
11
|
s as openApiPlugin
|
|
@@ -3,7 +3,7 @@ import { C as x } from "./ClientOnly-E7hGysn1.js";
|
|
|
3
3
|
import { VisuallyHidden as y } from "@radix-ui/react-visually-hidden";
|
|
4
4
|
import { d as f, k as j } from "./useQuery-CQUwWR9i.js";
|
|
5
5
|
import { useCallback as S, useState as b } from "react";
|
|
6
|
-
import { C as k, a as u, b as h, c as C, d as N, e as v, f as w } from "./Callout-
|
|
6
|
+
import { C as k, a as u, b as h, c as C, d as N, e as v, f as w } from "./Callout-B2vsR09t.js";
|
|
7
7
|
import { b as _ } from "./Dialog-sbgekbjb.js";
|
|
8
8
|
import { j as L } from "./joinUrl-10po2Jdj.js";
|
|
9
9
|
import { FileTextIcon as F } from "lucide-react";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zudoku",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"homepage": "https://zudoku.dev",
|
|
6
6
|
"repository": {
|
|
@@ -54,6 +54,10 @@
|
|
|
54
54
|
"import": "./lib/zudoku.auth-openid.js",
|
|
55
55
|
"types": "./dist/lib/authentication/providers/openid.d.ts"
|
|
56
56
|
},
|
|
57
|
+
"./auth/supabase": {
|
|
58
|
+
"import": "./lib/zudoku.auth-supabase.js",
|
|
59
|
+
"types": "./dist/lib/authentication/providers/supabase.d.ts"
|
|
60
|
+
},
|
|
57
61
|
"./plugins": {
|
|
58
62
|
"import": "./lib/zudoku.plugins.js",
|
|
59
63
|
"types": "./dist/lib/core/plugins.d.ts"
|
|
@@ -126,7 +130,7 @@
|
|
|
126
130
|
"@apidevtools/json-schema-ref-parser": "11.9.1",
|
|
127
131
|
"@envelop/core": "5.0.3",
|
|
128
132
|
"@graphql-typed-document-node/core": "3.2.0",
|
|
129
|
-
"@hookform/resolvers": "4.1.
|
|
133
|
+
"@hookform/resolvers": "4.1.3",
|
|
130
134
|
"@lekoarts/rehype-meta-as-attributes": "3.0.2",
|
|
131
135
|
"@mdx-js/react": "3.1.0",
|
|
132
136
|
"@mdx-js/rollup": "3.1.0",
|
|
@@ -179,7 +183,7 @@
|
|
|
179
183
|
"graphql-type-json": "0.3.2",
|
|
180
184
|
"graphql-yoga": "5.12.0",
|
|
181
185
|
"gray-matter": "4.0.3",
|
|
182
|
-
"hast-util-to-jsx-runtime": "^2.3.
|
|
186
|
+
"hast-util-to-jsx-runtime": "^2.3.6",
|
|
183
187
|
"hast-util-to-string": "3.0.1",
|
|
184
188
|
"html-url-attributes": "^3.0.1",
|
|
185
189
|
"http-terminator": "3.2.0",
|
|
@@ -197,7 +201,7 @@
|
|
|
197
201
|
"postcss": "8.5.3",
|
|
198
202
|
"posthog-node": "4.8.1",
|
|
199
203
|
"prism-react-renderer": "2.4.1",
|
|
200
|
-
"prismjs": "1.
|
|
204
|
+
"prismjs": "1.30.0",
|
|
201
205
|
"react-error-boundary": "5.0.0",
|
|
202
206
|
"react-hook-form": "7.54.2",
|
|
203
207
|
"react-is": "19.0.0",
|
|
@@ -213,7 +217,7 @@
|
|
|
213
217
|
"remark-mdx-frontmatter": "5.0.0",
|
|
214
218
|
"remark-parse": "^11.0.0",
|
|
215
219
|
"remark-rehype": "^11.1.1",
|
|
216
|
-
"rollup": "4.
|
|
220
|
+
"rollup": "4.35.0",
|
|
217
221
|
"semver": "7.7.1",
|
|
218
222
|
"sitemap": "8.0.0",
|
|
219
223
|
"spin-delay": "2.0.1",
|
|
@@ -266,7 +270,8 @@
|
|
|
266
270
|
},
|
|
267
271
|
"optionalDependencies": {
|
|
268
272
|
"@clerk/clerk-js": "^5.52.3",
|
|
269
|
-
"@sentry/react": "^9.1.0"
|
|
273
|
+
"@sentry/react": "^9.1.0",
|
|
274
|
+
"@supabase/supabase-js": "^2.49.1"
|
|
270
275
|
},
|
|
271
276
|
"scripts": {
|
|
272
277
|
"build": "tsc --project tsconfig.app.json",
|
package/src/app/main.css
CHANGED
|
@@ -82,10 +82,11 @@
|
|
|
82
82
|
--top-nav-height: 50px;
|
|
83
83
|
--banner-height: 40px;
|
|
84
84
|
--header-height: calc(
|
|
85
|
-
var(--top-header-height) + var(--top-nav-height) + var(--banner-height)
|
|
85
|
+
var(--top-header-height) + var(--top-nav-height) + var(--banner-height) -
|
|
86
|
+
/* borders */ 2px
|
|
86
87
|
);
|
|
87
88
|
--scroll-padding: calc(var(--header-height) + 10px);
|
|
88
|
-
--side-nav-width: theme("spacing.
|
|
89
|
+
--side-nav-width: theme("spacing.72");
|
|
89
90
|
--padding-content-top: theme("spacing.6");
|
|
90
91
|
--padding-content-bottom: theme("spacing.12");
|
|
91
92
|
--padding-nav-item: theme("spacing[2.5]");
|
|
@@ -108,8 +109,9 @@
|
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
#root {
|
|
111
|
-
@apply min-h-screen w-full;
|
|
112
|
+
@apply min-h-screen w-full grid grid-rows-[auto_1fr];
|
|
112
113
|
}
|
|
114
|
+
|
|
113
115
|
* {
|
|
114
116
|
@apply border-border;
|
|
115
117
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Clerk } from "@clerk/clerk-js";
|
|
2
|
-
import { ClerkAuthenticationConfig } from "../../../config/config.js";
|
|
3
|
-
import { AuthenticationProviderInitializer } from "../authentication.js";
|
|
2
|
+
import { type ClerkAuthenticationConfig } from "../../../config/config.js";
|
|
3
|
+
import { type AuthenticationProviderInitializer } from "../authentication.js";
|
|
4
4
|
import { AuthenticationPlugin } from "../AuthenticationPlugin.js";
|
|
5
5
|
import { useAuthState } from "../state.js";
|
|
6
6
|
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createClient,
|
|
3
|
+
type Provider,
|
|
4
|
+
type Session,
|
|
5
|
+
type SupabaseClient,
|
|
6
|
+
} from "@supabase/supabase-js";
|
|
7
|
+
import { type SupabaseAuthenticationConfig } from "../../../config/config.js";
|
|
8
|
+
import {
|
|
9
|
+
type AuthenticationProvider,
|
|
10
|
+
type AuthenticationProviderInitializer,
|
|
11
|
+
} from "../authentication.js";
|
|
12
|
+
import { AuthenticationPlugin } from "../AuthenticationPlugin.js";
|
|
13
|
+
import { AuthorizationError } from "../errors.js";
|
|
14
|
+
import { useAuthState, type UserProfile } from "../state.js";
|
|
15
|
+
|
|
16
|
+
class SupabaseAuthPlugin extends AuthenticationPlugin {}
|
|
17
|
+
|
|
18
|
+
class SupabaseAuthenticationProvider implements AuthenticationProvider {
|
|
19
|
+
private readonly client: SupabaseClient;
|
|
20
|
+
private readonly provider: Provider;
|
|
21
|
+
private readonly redirectToAfterSignUp: string;
|
|
22
|
+
private readonly redirectToAfterSignIn: string;
|
|
23
|
+
private readonly redirectToAfterSignOut: string;
|
|
24
|
+
|
|
25
|
+
constructor({
|
|
26
|
+
supabaseUrl,
|
|
27
|
+
supabaseKey,
|
|
28
|
+
provider,
|
|
29
|
+
redirectToAfterSignUp,
|
|
30
|
+
redirectToAfterSignIn,
|
|
31
|
+
redirectToAfterSignOut,
|
|
32
|
+
basePath,
|
|
33
|
+
}: SupabaseAuthenticationConfig) {
|
|
34
|
+
this.provider = provider;
|
|
35
|
+
this.client = createClient(supabaseUrl, supabaseKey, {
|
|
36
|
+
auth: {
|
|
37
|
+
autoRefreshToken: true,
|
|
38
|
+
persistSession: true,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const root = basePath ?? "/";
|
|
43
|
+
|
|
44
|
+
this.redirectToAfterSignUp = redirectToAfterSignUp ?? root;
|
|
45
|
+
this.redirectToAfterSignIn = redirectToAfterSignIn ?? root;
|
|
46
|
+
this.redirectToAfterSignOut = redirectToAfterSignOut ?? root;
|
|
47
|
+
|
|
48
|
+
this.client.auth.onAuthStateChange(async (event, session) => {
|
|
49
|
+
if (session && (event === "SIGNED_IN" || event === "TOKEN_REFRESHED")) {
|
|
50
|
+
await this.updateUserState(session);
|
|
51
|
+
} else if (event === "SIGNED_OUT") {
|
|
52
|
+
useAuthState.setState({
|
|
53
|
+
isAuthenticated: false,
|
|
54
|
+
isPending: false,
|
|
55
|
+
profile: undefined,
|
|
56
|
+
providerData: undefined,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private async updateUserState(session: Session) {
|
|
63
|
+
const { user } = session;
|
|
64
|
+
|
|
65
|
+
const profile: UserProfile = {
|
|
66
|
+
sub: user.id,
|
|
67
|
+
email: user.email,
|
|
68
|
+
name: user.user_metadata.full_name || user.user_metadata.name,
|
|
69
|
+
emailVerified: user.email_confirmed_at != null,
|
|
70
|
+
pictureUrl: user.user_metadata.avatar_url,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
useAuthState.setState({
|
|
74
|
+
isAuthenticated: true,
|
|
75
|
+
isPending: false,
|
|
76
|
+
profile,
|
|
77
|
+
providerData: { session },
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async getAccessToken(): Promise<string> {
|
|
82
|
+
const { data, error } = await this.client.auth.getSession();
|
|
83
|
+
|
|
84
|
+
if (error || !data.session) {
|
|
85
|
+
throw new AuthorizationError("User is not authenticated");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return data.session.access_token;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
signUp = async ({ redirectTo }: { redirectTo?: string }) => {
|
|
92
|
+
const finalRedirectTo = redirectTo ?? this.redirectToAfterSignUp;
|
|
93
|
+
|
|
94
|
+
// Open Supabase Auth UI in a new window
|
|
95
|
+
await this.client.auth.signInWithOAuth({
|
|
96
|
+
provider: this.provider,
|
|
97
|
+
options: {
|
|
98
|
+
redirectTo: window.location.origin + finalRedirectTo,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
signIn = async ({ redirectTo }: { redirectTo?: string }) => {
|
|
104
|
+
const finalRedirectTo = redirectTo ?? this.redirectToAfterSignIn;
|
|
105
|
+
|
|
106
|
+
await this.client.auth.signInWithOAuth({
|
|
107
|
+
provider: this.provider,
|
|
108
|
+
options: {
|
|
109
|
+
redirectTo: window.location.origin + finalRedirectTo,
|
|
110
|
+
queryParams: {
|
|
111
|
+
access_type: "offline",
|
|
112
|
+
prompt: "consent",
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
signOut = async () => {
|
|
119
|
+
await new Promise<void>((resolve) => {
|
|
120
|
+
const { data } = this.client.auth.onAuthStateChange(async (event) => {
|
|
121
|
+
if (event !== "SIGNED_OUT") return;
|
|
122
|
+
data.subscription.unsubscribe();
|
|
123
|
+
resolve();
|
|
124
|
+
});
|
|
125
|
+
void this.client.auth.signOut();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
useAuthState.setState({
|
|
129
|
+
isAuthenticated: false,
|
|
130
|
+
isPending: false,
|
|
131
|
+
profile: undefined,
|
|
132
|
+
providerData: undefined,
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
getAuthenticationPlugin = () => new SupabaseAuthPlugin();
|
|
137
|
+
|
|
138
|
+
onPageLoad = async () => {
|
|
139
|
+
const { data, error } = await this.client.auth.getSession();
|
|
140
|
+
|
|
141
|
+
if (!error && data.session) {
|
|
142
|
+
await this.updateUserState(data.session);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const supabaseAuth: AuthenticationProviderInitializer<
|
|
148
|
+
SupabaseAuthenticationConfig
|
|
149
|
+
> = (options) => new SupabaseAuthenticationProvider(options);
|
|
150
|
+
|
|
151
|
+
export default supabaseAuth;
|
|
@@ -3,7 +3,10 @@ import { Link } from "react-router";
|
|
|
3
3
|
import { Button } from "zudoku/ui/Button.js";
|
|
4
4
|
import { Skeleton } from "zudoku/ui/Skeleton.js";
|
|
5
5
|
import { useAuth } from "../authentication/hook.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
isProfileMenuPlugin,
|
|
8
|
+
type ProfileNavigationItem,
|
|
9
|
+
} from "../core/plugins.js";
|
|
7
10
|
import {
|
|
8
11
|
DropdownMenu,
|
|
9
12
|
DropdownMenuContent,
|
|
@@ -66,7 +69,7 @@ export const Header = memo(function HeaderInner() {
|
|
|
66
69
|
<header className="sticky lg:top-0 z-10 bg-background/80 backdrop-blur w-full">
|
|
67
70
|
<Banner />
|
|
68
71
|
<div className="border-b">
|
|
69
|
-
<div className="max-w-screen-2xl 2xl:border-x mx-auto
|
|
72
|
+
<div className="max-w-screen-2xl 2xl:border-x mx-auto flex relative items-center justify-between px-4 lg:px-8 h-[--top-header-height]">
|
|
70
73
|
<div className="flex">
|
|
71
74
|
<Link to="/">
|
|
72
75
|
<div className="flex items-center gap-3.5">
|
|
@@ -108,11 +111,12 @@ export const Header = memo(function HeaderInner() {
|
|
|
108
111
|
</div>
|
|
109
112
|
</Link>
|
|
110
113
|
</div>
|
|
111
|
-
<div className="grid grid-cols-1 lg:grid-cols-[--sidecar-grid-cols] items-center gap-8">
|
|
112
|
-
<div className="w-full justify-center hidden lg:flex">
|
|
113
|
-
<Search />
|
|
114
|
-
</div>
|
|
115
114
|
|
|
115
|
+
<div className="absolute inset-x-0 justify-center items-center hidden lg:flex w-full pointer-events-none">
|
|
116
|
+
<Search className="pointer-events-auto" />
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div className="flex items-center gap-8">
|
|
116
120
|
<MobileTopNavigation />
|
|
117
121
|
<div className="hidden lg:flex items-center justify-self-end text-sm gap-2">
|
|
118
122
|
<Slotlet name="head-navigation-start" />
|
|
@@ -20,7 +20,7 @@ export const InlineCode = ({
|
|
|
20
20
|
selection?.addRange(range);
|
|
21
21
|
}}
|
|
22
22
|
className={cn(
|
|
23
|
-
"font-mono border p-1 py-0.5 rounded bg-border/50 dark:bg-border/70
|
|
23
|
+
"font-mono border p-1 py-0.5 rounded bg-border/50 dark:bg-border/70 [overflow-wrap:anywhere]",
|
|
24
24
|
className,
|
|
25
25
|
)}
|
|
26
26
|
>
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import { Helmet } from "@zudoku/react-helmet-async";
|
|
2
|
-
import {
|
|
3
|
-
import { Suspense, useEffect, useRef, useState, type ReactNode } from "react";
|
|
2
|
+
import { Suspense, useEffect, useRef, type ReactNode } from "react";
|
|
4
3
|
import { Outlet, useLocation, useNavigation } from "react-router";
|
|
5
4
|
import { useSpinDelay } from "spin-delay";
|
|
6
|
-
import { Drawer, DrawerTrigger } from "../ui/Drawer.js";
|
|
7
|
-
import { cn } from "../util/cn.js";
|
|
8
5
|
import { useScrollToAnchor } from "../util/useScrollToAnchor.js";
|
|
9
6
|
import { useScrollToTop } from "../util/useScrollToTop.js";
|
|
10
7
|
import { useViewportAnchor } from "./context/ViewportAnchorContext.js";
|
|
11
8
|
import { useZudoku } from "./context/ZudokuContext.js";
|
|
12
9
|
import { Header } from "./Header.js";
|
|
13
|
-
import {
|
|
10
|
+
import { Main } from "./Main.js";
|
|
14
11
|
import { Slotlet } from "./SlotletProvider.js";
|
|
15
12
|
import { Spinner } from "./Spinner.js";
|
|
16
13
|
|
|
17
14
|
const LoadingFallback = () => (
|
|
18
|
-
<main className="
|
|
15
|
+
<main className="col-span-full grid place-items-center">
|
|
19
16
|
<Spinner />
|
|
20
17
|
</main>
|
|
21
18
|
);
|
|
@@ -49,7 +46,6 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
|
49
46
|
delay: 300,
|
|
50
47
|
minDuration: 500,
|
|
51
48
|
});
|
|
52
|
-
const [isDrawerOpen, setDrawerOpen] = useState(false);
|
|
53
49
|
|
|
54
50
|
return (
|
|
55
51
|
<>
|
|
@@ -66,43 +62,12 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
|
66
62
|
<Header />
|
|
67
63
|
<Slotlet name="layout-after-head" />
|
|
68
64
|
|
|
69
|
-
<div className="
|
|
65
|
+
<div className="grid lg:grid-cols-[var(--side-nav-width)_1fr] max-w-screen-2xl w-full lg:mx-auto px-4 lg:px-8 2xl:border-x">
|
|
70
66
|
{showSpinner ? (
|
|
71
67
|
<LoadingFallback />
|
|
72
68
|
) : (
|
|
73
69
|
<Suspense fallback={<LoadingFallback />}>
|
|
74
|
-
<
|
|
75
|
-
direction="left"
|
|
76
|
-
open={isDrawerOpen}
|
|
77
|
-
onOpenChange={(open) => setDrawerOpen(open)}
|
|
78
|
-
>
|
|
79
|
-
<Sidebar onRequestClose={() => setDrawerOpen(false)} />
|
|
80
|
-
<div
|
|
81
|
-
className={cn(
|
|
82
|
-
"lg:hidden -mx-4 px-4 py-2 sticky bg-background/80 backdrop-blur z-10 top-0 left-0 right-0 border-b",
|
|
83
|
-
"peer-data-[navigation=false]:hidden",
|
|
84
|
-
)}
|
|
85
|
-
>
|
|
86
|
-
<DrawerTrigger className="flex items-center gap-2">
|
|
87
|
-
<PanelLeftIcon size={16} strokeWidth={1.5} />
|
|
88
|
-
<span className="text-sm">Menu</span>
|
|
89
|
-
</DrawerTrigger>
|
|
90
|
-
</div>
|
|
91
|
-
<main
|
|
92
|
-
data-pagefind-body
|
|
93
|
-
className={cn(
|
|
94
|
-
"h-full dark:border-white/10 translate-x-0",
|
|
95
|
-
"lg:overflow-visible",
|
|
96
|
-
// This works in tandem with the `SidebarWrapper` component
|
|
97
|
-
"lg:peer-data-[navigation=true]:w-[calc(100%-var(--side-nav-width))]",
|
|
98
|
-
"lg:peer-data-[navigation=true]:translate-x-[--side-nav-width] lg:peer-data-[navigation=true]:pl-12",
|
|
99
|
-
)}
|
|
100
|
-
>
|
|
101
|
-
<Slotlet name="zudoku-before-content" />
|
|
102
|
-
{children ?? <Outlet />}
|
|
103
|
-
<Slotlet name="zudoku-after-content" />
|
|
104
|
-
</main>
|
|
105
|
-
</Drawer>
|
|
70
|
+
<Main>{children ?? <Outlet />}</Main>
|
|
106
71
|
</Suspense>
|
|
107
72
|
)}
|
|
108
73
|
</div>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { PanelLeftIcon } from "lucide-react";
|
|
2
|
+
import { type PropsWithChildren, useState } from "react";
|
|
3
|
+
import { Drawer, DrawerTrigger } from "zudoku/ui/Drawer.js";
|
|
4
|
+
import { cn } from "../util/cn.js";
|
|
5
|
+
import { useCurrentNavigation } from "./context/ZudokuContext.js";
|
|
6
|
+
import { Sidebar } from "./navigation/Sidebar.js";
|
|
7
|
+
import { Slotlet } from "./SlotletProvider.js";
|
|
8
|
+
|
|
9
|
+
export const Main = ({ children }: PropsWithChildren) => {
|
|
10
|
+
const [isDrawerOpen, setDrawerOpen] = useState(false);
|
|
11
|
+
const { sidebar } = useCurrentNavigation();
|
|
12
|
+
const hasSidebar = sidebar.length > 0;
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Drawer
|
|
16
|
+
direction="left"
|
|
17
|
+
open={isDrawerOpen}
|
|
18
|
+
onOpenChange={(open) => setDrawerOpen(open)}
|
|
19
|
+
>
|
|
20
|
+
{hasSidebar && (
|
|
21
|
+
<Sidebar
|
|
22
|
+
onRequestClose={() => setDrawerOpen(false)}
|
|
23
|
+
sidebar={sidebar}
|
|
24
|
+
/>
|
|
25
|
+
)}
|
|
26
|
+
{hasSidebar && (
|
|
27
|
+
<div className="lg:hidden -mx-4 px-4 py-2 sticky bg-background/80 backdrop-blur z-10 top-0 left-0 right-0 border-b">
|
|
28
|
+
<DrawerTrigger className="flex items-center gap-2">
|
|
29
|
+
<PanelLeftIcon size={16} strokeWidth={1.5} />
|
|
30
|
+
<span className="text-sm">Menu</span>
|
|
31
|
+
</DrawerTrigger>
|
|
32
|
+
</div>
|
|
33
|
+
)}
|
|
34
|
+
<main
|
|
35
|
+
data-pagefind-body
|
|
36
|
+
className={cn(
|
|
37
|
+
"h-auto dark:border-white/10 translate-x-0",
|
|
38
|
+
hasSidebar ? "lg:pl-12" : "col-span-full",
|
|
39
|
+
)}
|
|
40
|
+
>
|
|
41
|
+
<Slotlet name="zudoku-before-content" />
|
|
42
|
+
{children}
|
|
43
|
+
<Slotlet name="zudoku-after-content" />
|
|
44
|
+
</main>
|
|
45
|
+
</Drawer>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type ReactNode,
|
|
3
2
|
createContext,
|
|
4
3
|
useCallback,
|
|
5
4
|
useContext,
|
|
@@ -7,6 +6,7 @@ import {
|
|
|
7
6
|
useMemo,
|
|
8
7
|
useRef,
|
|
9
8
|
useState,
|
|
9
|
+
type PropsWithChildren,
|
|
10
10
|
} from "react";
|
|
11
11
|
|
|
12
12
|
type AnchorContextType = {
|
|
@@ -55,11 +55,7 @@ export const useRegisterAnchorElement = () => {
|
|
|
55
55
|
return { ref: setRef };
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
-
export const ViewportAnchorProvider = ({
|
|
59
|
-
children,
|
|
60
|
-
}: {
|
|
61
|
-
children: ReactNode;
|
|
62
|
-
}) => {
|
|
58
|
+
export const ViewportAnchorProvider = ({ children }: PropsWithChildren) => {
|
|
63
59
|
const [activeAnchor, setActiveAnchor] = useState("");
|
|
64
60
|
const observerRef = useRef<IntersectionObserver | null>(null);
|
|
65
61
|
const registeredElements = useRef(new Set<HTMLElement>());
|
|
@@ -75,9 +71,7 @@ export const ViewportAnchorProvider = ({
|
|
|
75
71
|
}
|
|
76
72
|
},
|
|
77
73
|
{
|
|
78
|
-
|
|
79
|
-
// see --header-height in `main.css`
|
|
80
|
-
rootMargin: "115px 0px -80% 0px",
|
|
74
|
+
rootMargin: "0px 0px -80% 0px",
|
|
81
75
|
threshold: 0.75,
|
|
82
76
|
},
|
|
83
77
|
);
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { useEffect, useRef } from "react";
|
|
2
2
|
|
|
3
3
|
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
|
4
|
+
import type { SidebarItem as SidebarItemType } from "../../../config/validators/SidebarSchema.js";
|
|
4
5
|
import { DrawerContent, DrawerTitle } from "../../ui/Drawer.js";
|
|
5
6
|
import { scrollIntoViewIfNeeded } from "../../util/scrollIntoViewIfNeeded.js";
|
|
6
|
-
import { useCurrentNavigation } from "../context/ZudokuContext.js";
|
|
7
7
|
import { Slotlet } from "../SlotletProvider.js";
|
|
8
8
|
import { SidebarItem } from "./SidebarItem.js";
|
|
9
9
|
import { SidebarWrapper } from "./SidebarWrapper.js";
|
|
10
10
|
|
|
11
11
|
export const Sidebar = ({
|
|
12
12
|
onRequestClose,
|
|
13
|
+
sidebar,
|
|
13
14
|
}: {
|
|
14
15
|
onRequestClose?: () => void;
|
|
16
|
+
sidebar: SidebarItemType[];
|
|
15
17
|
}) => {
|
|
16
|
-
const navRef = useRef<HTMLDivElement
|
|
17
|
-
const navigation = useCurrentNavigation();
|
|
18
|
+
const navRef = useRef<HTMLDivElement>(null);
|
|
18
19
|
|
|
19
20
|
useEffect(() => {
|
|
20
21
|
const active = navRef.current?.querySelector('[aria-current="page"]');
|
|
@@ -23,12 +24,9 @@ export const Sidebar = ({
|
|
|
23
24
|
|
|
24
25
|
return (
|
|
25
26
|
<>
|
|
26
|
-
<SidebarWrapper
|
|
27
|
-
ref={navRef}
|
|
28
|
-
pushMainContent={navigation.sidebar.length > 0}
|
|
29
|
-
>
|
|
27
|
+
<SidebarWrapper ref={navRef}>
|
|
30
28
|
<Slotlet name="zudoku-before-navigation" />
|
|
31
|
-
{
|
|
29
|
+
{sidebar.map((item) => (
|
|
32
30
|
<SidebarItem key={item.label} item={item} />
|
|
33
31
|
))}
|
|
34
32
|
<Slotlet name="zudoku-after-navigation" />
|
|
@@ -41,7 +39,7 @@ export const Sidebar = ({
|
|
|
41
39
|
<VisuallyHidden>
|
|
42
40
|
<DrawerTitle>Sidebar</DrawerTitle>
|
|
43
41
|
</VisuallyHidden>
|
|
44
|
-
{
|
|
42
|
+
{sidebar.map((item) => (
|
|
45
43
|
<SidebarItem
|
|
46
44
|
key={item.label}
|
|
47
45
|
item={item}
|
|
@@ -1,27 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type PropsWithChildren, type Ref } from "react";
|
|
2
2
|
import { cn } from "../../util/cn.js";
|
|
3
3
|
|
|
4
|
-
export const SidebarWrapper =
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
export const SidebarWrapper = ({
|
|
5
|
+
children,
|
|
6
|
+
className,
|
|
7
|
+
ref,
|
|
8
|
+
}: PropsWithChildren<{
|
|
9
|
+
className?: string;
|
|
10
|
+
ref?: Ref<HTMLDivElement>;
|
|
11
|
+
}>) => (
|
|
8
12
|
<nav
|
|
9
|
-
// this data attribute is used in `Layout.tsx` to determine if side navigation
|
|
10
|
-
// is present for the current page so the main content is pushed to the right
|
|
11
|
-
// it's also important to set `peer` class here.
|
|
12
|
-
// maybe this could be simplified by adjusting the layout
|
|
13
|
-
data-navigation={String(pushMainContent)}
|
|
14
13
|
className={cn(
|
|
15
|
-
"
|
|
16
|
-
"-
|
|
17
|
-
"w-[--side-nav-width]
|
|
18
|
-
!pushMainContent && "border-r-0",
|
|
14
|
+
"hidden lg:flex h-full scrollbar peer flex-col overflow-y-auto shrink-0 text-sm border-r pr-6",
|
|
15
|
+
"sticky top-[--header-height] h-[calc(100vh-var(--header-height))]",
|
|
16
|
+
"-mx-[--padding-nav-item] max-w-[--side-nav-width] pb-20 pt-[--padding-content-top] scroll-pt-2 gap-2",
|
|
19
17
|
className,
|
|
20
18
|
)}
|
|
21
19
|
ref={ref}
|
|
22
20
|
>
|
|
23
21
|
{children}
|
|
24
22
|
</nav>
|
|
25
|
-
)
|
|
23
|
+
);
|
|
26
24
|
|
|
27
25
|
SidebarWrapper.displayName = "SidebarWrapper";
|
|
@@ -18,8 +18,9 @@ export const RouteGuard = () => {
|
|
|
18
18
|
const navigate = useNavigate();
|
|
19
19
|
const location = useLocation();
|
|
20
20
|
const latestPath = useLatest(location.pathname);
|
|
21
|
+
const { protectedRoutes = [] } = zudoku.options;
|
|
21
22
|
|
|
22
|
-
const isProtected =
|
|
23
|
+
const isProtected = protectedRoutes.some((path) =>
|
|
23
24
|
matchPath({ path, end: true }, location.pathname),
|
|
24
25
|
);
|
|
25
26
|
|