zudoku 0.28.1 → 0.28.2

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.
Files changed (83) hide show
  1. package/dist/app/main.js +1 -2
  2. package/dist/app/main.js.map +1 -1
  3. package/dist/lib/components/index.d.ts +4 -0
  4. package/dist/lib/components/index.js +2 -0
  5. package/dist/lib/components/index.js.map +1 -1
  6. package/dist/lib/components/navigation/Sidebar.js +6 -1
  7. package/dist/lib/components/navigation/Sidebar.js.map +1 -1
  8. package/dist/lib/oas/graphql/index.js +11 -10
  9. package/dist/lib/oas/graphql/index.js.map +1 -1
  10. package/dist/lib/plugins/openapi/OperationListItem.d.ts +2 -1
  11. package/dist/lib/plugins/openapi/OperationListItem.js +1 -1
  12. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  13. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js +6 -2
  14. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js.map +1 -1
  15. package/dist/lib/plugins/openapi/playground/Headers.d.ts +2 -3
  16. package/dist/lib/plugins/openapi/playground/Headers.js +2 -2
  17. package/dist/lib/plugins/openapi/playground/Headers.js.map +1 -1
  18. package/dist/lib/plugins/openapi/playground/Playground.d.ts +3 -0
  19. package/dist/lib/plugins/openapi/playground/Playground.js +1 -1
  20. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  21. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js +1 -1
  22. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js.map +1 -1
  23. package/dist/lib/util/scrollIntoViewIfNeeded.d.ts +1 -0
  24. package/dist/lib/util/scrollIntoViewIfNeeded.js +14 -0
  25. package/dist/lib/util/scrollIntoViewIfNeeded.js.map +1 -0
  26. package/dist/lib/util/useScrollToAnchor.js +1 -13
  27. package/dist/lib/util/useScrollToAnchor.js.map +1 -1
  28. package/dist/vite/plugin-api.js +97 -97
  29. package/dist/vite/plugin-api.js.map +1 -1
  30. package/dist/vite/plugin-theme-css.js +7 -4
  31. package/dist/vite/plugin-theme-css.js.map +1 -1
  32. package/lib/{AuthenticationPlugin-Du8cLBSr.js → AuthenticationPlugin-DeEA69mE.js} +3 -3
  33. package/lib/{AuthenticationPlugin-Du8cLBSr.js.map → AuthenticationPlugin-DeEA69mE.js.map} +1 -1
  34. package/lib/{Markdown-Cyrx_JrO.js → Markdown-LcMEZ0Sn.js} +2 -2
  35. package/lib/{Markdown-Cyrx_JrO.js.map → Markdown-LcMEZ0Sn.js.map} +1 -1
  36. package/lib/{MdxPage-BuG8Tuwc.js → MdxPage-DkH3V4hV.js} +5 -5
  37. package/lib/{MdxPage-BuG8Tuwc.js.map → MdxPage-DkH3V4hV.js.map} +1 -1
  38. package/lib/{OpenApiRoute-UrC_t0e5.js → OpenApiRoute-ULLXjfro.js} +3 -3
  39. package/lib/{OpenApiRoute-UrC_t0e5.js.map → OpenApiRoute-ULLXjfro.js.map} +1 -1
  40. package/lib/{OperationList-CDt1xdc4.js → OperationList-wzZNceUl.js} +339 -336
  41. package/lib/{OperationList-CDt1xdc4.js.map → OperationList-wzZNceUl.js.map} +1 -1
  42. package/lib/{Select-CnCZ4WhS.js → Select-DJkXPPD0.js} +3 -3
  43. package/lib/{Select-CnCZ4WhS.js.map → Select-DJkXPPD0.js.map} +1 -1
  44. package/lib/{SlotletProvider-mQiPDQIH.js → SlotletProvider-D1t2ePCI.js} +4 -4
  45. package/lib/{SlotletProvider-mQiPDQIH.js.map → SlotletProvider-D1t2ePCI.js.map} +1 -1
  46. package/lib/{ZudokuContext-BTUJPpQl.js → ZudokuContext-dUyBGMap.js} +2 -2
  47. package/lib/{ZudokuContext-BTUJPpQl.js.map → ZudokuContext-dUyBGMap.js.map} +1 -1
  48. package/lib/{chunk-SYFQ2XB5-BPvC-soB.js → chunk-SYFQ2XB5-QijJrSf0.js} +3 -3
  49. package/lib/{chunk-SYFQ2XB5-BPvC-soB.js.map → chunk-SYFQ2XB5-QijJrSf0.js.map} +1 -1
  50. package/lib/{createServer-CjNktZzL.js → createServer-DIztAu7i.js} +11 -8
  51. package/lib/{createServer-CjNktZzL.js.map → createServer-DIztAu7i.js.map} +1 -1
  52. package/lib/{hook-FT3SJLe_.js → hook-CiX69UZ6.js} +2 -2
  53. package/lib/{hook-FT3SJLe_.js.map → hook-CiX69UZ6.js.map} +1 -1
  54. package/lib/{index-Eb1oiHbM.js → index-DrR58fsJ.js} +234 -223
  55. package/lib/index-DrR58fsJ.js.map +1 -0
  56. package/lib/{useExposedProps-BLKFBylA.js → useExposedProps-Bbf99zic.js} +2 -2
  57. package/lib/{useExposedProps-BLKFBylA.js.map → useExposedProps-Bbf99zic.js.map} +1 -1
  58. package/lib/{useScrollToAnchor-BZsGmBng.js → useScrollToAnchor-DYGn1MT9.js} +11 -10
  59. package/lib/useScrollToAnchor-DYGn1MT9.js.map +1 -0
  60. package/lib/zudoku.auth-clerk.js +1 -1
  61. package/lib/zudoku.auth-openid.js +3 -3
  62. package/lib/zudoku.components.js +530 -443
  63. package/lib/zudoku.components.js.map +1 -1
  64. package/lib/zudoku.plugin-api-catalog.js +3 -3
  65. package/lib/zudoku.plugin-api-keys.js +5 -5
  66. package/lib/zudoku.plugin-custom-pages.js +2 -2
  67. package/lib/zudoku.plugin-markdown.js +1 -1
  68. package/lib/zudoku.plugin-openapi.js +3 -3
  69. package/lib/zudoku.plugin-redirect.js +1 -1
  70. package/package.json +2 -2
  71. package/src/app/main.tsx +7 -2
  72. package/src/lib/components/index.ts +2 -0
  73. package/src/lib/components/navigation/Sidebar.tsx +7 -1
  74. package/src/lib/oas/graphql/index.ts +14 -17
  75. package/src/lib/plugins/openapi/OperationListItem.tsx +1 -1
  76. package/src/lib/plugins/openapi/PlaygroundDialogWrapper.tsx +7 -2
  77. package/src/lib/plugins/openapi/playground/Headers.tsx +5 -9
  78. package/src/lib/plugins/openapi/playground/Playground.tsx +4 -1
  79. package/src/lib/plugins/openapi/playground/PlaygroundDialog.tsx +5 -2
  80. package/src/lib/util/scrollIntoViewIfNeeded.ts +18 -0
  81. package/src/lib/util/useScrollToAnchor.ts +1 -19
  82. package/lib/index-Eb1oiHbM.js.map +0 -1
  83. package/lib/useScrollToAnchor-BZsGmBng.js.map +0 -1
@@ -1,10 +1,10 @@
1
1
  import { j as e } from "./jsx-runtime-Bdg6XQ1m.js";
2
2
  import { s as j } from "./index-CjJS0l4l.js";
3
- import { u as b } from "./ZudokuContext-BTUJPpQl.js";
4
- import { b as y } from "./chunk-SYFQ2XB5-BPvC-soB.js";
3
+ import { u as b } from "./ZudokuContext-dUyBGMap.js";
4
+ import { b as y } from "./chunk-SYFQ2XB5-QijJrSf0.js";
5
5
  import { Head as v, Link as N } from "./zudoku.components.js";
6
6
  import { u as w } from "./state-mM7uaXTW.js";
7
- import { M as C } from "./Markdown-Cyrx_JrO.js";
7
+ import { M as C } from "./Markdown-LcMEZ0Sn.js";
8
8
  import { c as h } from "./cn-qaFjX9_3.js";
9
9
  const f = (r, n) => j(`${r}-${n}`), k = ({
10
10
  items: r,
@@ -1,14 +1,14 @@
1
1
  import { j as e } from "./jsx-runtime-Bdg6XQ1m.js";
2
2
  import { RotateCwIcon as g, TrashIcon as f, EyeOffIcon as j, EyeIcon as v, CheckIcon as w, CopyIcon as b, FileKey2Icon as K } from "lucide-react";
3
- import { D as k, S as m, R as N } from "./SlotletProvider-mQiPDQIH.js";
3
+ import { D as k, S as m, R as N } from "./SlotletProvider-D1t2ePCI.js";
4
4
  import { i as c } from "./invariant-Caa8-XvF.js";
5
- import { u as d, S as I, a as S, b as A, c as C, d as E, e as x } from "./Select-CnCZ4WhS.js";
5
+ import { u as d, S as I, a as S, b as A, c as C, d as E, e as x } from "./Select-DJkXPPD0.js";
6
6
  import { a as P } from "./index.esm-CrSoEshU.js";
7
- import { a as D, L as u, O as R } from "./chunk-SYFQ2XB5-BPvC-soB.js";
8
- import { a as y, e as q, u as O } from "./ZudokuContext-BTUJPpQl.js";
7
+ import { a as D, L as u, O as R } from "./chunk-SYFQ2XB5-QijJrSf0.js";
8
+ import { a as y, e as q, u as O } from "./ZudokuContext-dUyBGMap.js";
9
9
  import { Button as l } from "./ui/Button.js";
10
10
  import { Input as z } from "./ui/Input.js";
11
- import { u as F } from "./hook-FT3SJLe_.js";
11
+ import { u as F } from "./hook-CiX69UZ6.js";
12
12
  import { useState as p } from "react";
13
13
  import { c as T } from "./cn-qaFjX9_3.js";
14
14
  const L = ({ service: t }) => {
@@ -1,8 +1,8 @@
1
1
  import { j as o } from "./jsx-runtime-Bdg6XQ1m.js";
2
2
  import a from "react";
3
- import { P as n } from "./Markdown-Cyrx_JrO.js";
3
+ import { P as n } from "./Markdown-LcMEZ0Sn.js";
4
4
  import { c } from "./cn-qaFjX9_3.js";
5
- import { u as p } from "./useExposedProps-BLKFBylA.js";
5
+ import { u as p } from "./useExposedProps-Bbf99zic.js";
6
6
  const u = ({
7
7
  element: t,
8
8
  render: s,
@@ -74,7 +74,7 @@ const C = (n) => ({
74
74
  const h = {
75
75
  path: r,
76
76
  lazy: async () => {
77
- const { MdxPage: l } = await import("./MdxPage-BuG8Tuwc.js"), { default: p, ...g } = await a();
77
+ const { MdxPage: l } = await import("./MdxPage-DkH3V4hV.js"), { default: p, ...g } = await a();
78
78
  return {
79
79
  element: /* @__PURE__ */ P.jsx(
80
80
  l,
@@ -1,11 +1,11 @@
1
1
  import "./jsx-runtime-Bdg6XQ1m.js";
2
2
  import "./index-CjJS0l4l.js";
3
3
  import "lucide-react";
4
- import "./chunk-SYFQ2XB5-BPvC-soB.js";
5
- import "./hook-FT3SJLe_.js";
4
+ import "./chunk-SYFQ2XB5-QijJrSf0.js";
5
+ import "./hook-CiX69UZ6.js";
6
6
  import "./ui/Button.js";
7
7
  import "./joinUrl-nLx9pD-Z.js";
8
- import { o as f } from "./index-Eb1oiHbM.js";
8
+ import { o as f } from "./index-DrR58fsJ.js";
9
9
  export {
10
10
  f as openApiPlugin
11
11
  };
@@ -1,4 +1,4 @@
1
- import { r as o } from "./chunk-SYFQ2XB5-BPvC-soB.js";
1
+ import { r as o } from "./chunk-SYFQ2XB5-QijJrSf0.js";
2
2
  const a = (r) => ({
3
3
  getRoutes: () => r.redirects.map(({ from: e, to: t }) => ({
4
4
  path: e,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.28.1",
3
+ "version": "0.28.2",
4
4
  "type": "module",
5
5
  "homepage": "https://zudoku.dev",
6
6
  "repository": {
@@ -245,7 +245,7 @@
245
245
  "react-dom": ">=19"
246
246
  },
247
247
  "optionalDependencies": {
248
- "@clerk/clerk-js": "^5.43.4",
248
+ "@clerk/clerk-js": "^5.51.0",
249
249
  "@sentry/react": "^8.50.0"
250
250
  },
251
251
  "scripts": {
package/src/app/main.tsx CHANGED
@@ -11,9 +11,14 @@ import { configuredRedirectPlugin } from "virtual:zudoku-redirect-plugin";
11
11
  import { configuredSearchPlugin } from "virtual:zudoku-search-plugin";
12
12
  import { configuredSidebar } from "virtual:zudoku-sidebar";
13
13
  import "virtual:zudoku-theme.css";
14
- import { Layout, RouteGuard, RouterError, Zudoku } from "zudoku/components";
14
+ import {
15
+ Layout,
16
+ RouteGuard,
17
+ RouterError,
18
+ StatusPage,
19
+ Zudoku,
20
+ } from "zudoku/components";
15
21
  import type { ZudokuConfig } from "../config/config.js";
16
- import { StatusPage } from "../lib/components/StatusPage.js";
17
22
  import type { ZudokuContextOptions } from "../lib/core/ZudokuContext.js";
18
23
  import { isNavigationPlugin } from "../lib/core/plugins.js";
19
24
 
@@ -20,6 +20,7 @@ import { useZudoku as useZudokuImport } from "./context/ZudokuContext.js";
20
20
  import { Layout as LayoutImport } from "./Layout.js";
21
21
  import { Markdown as MarkdownImport } from "./Markdown.js";
22
22
  import { Spinner as SpinnerImport } from "./Spinner.js";
23
+ import { StatusPage as StatusPageImport } from "./StatusPage.js";
23
24
  import { Zudoku as ZudokuImport } from "./Zudoku.js";
24
25
 
25
26
  export const useMDXComponents = /*@__PURE__*/ useMDXComponentsImport;
@@ -38,6 +39,7 @@ export const useCache = /*@__PURE__*/ useCacheImport;
38
39
  export const CACHE_KEYS = /*@__PURE__*/ CACHE_KEYS_IMPORT;
39
40
  export const Zudoku = /*@__PURE__*/ ZudokuImport;
40
41
 
42
+ export const StatusPage = /*@__PURE__*/ StatusPageImport;
41
43
  export const Callout = /*@__PURE__*/ CalloutImport;
42
44
  export const Markdown = /*@__PURE__*/ MarkdownImport;
43
45
  export const Spinner = /*@__PURE__*/ SpinnerImport;
@@ -1,7 +1,8 @@
1
- import { useRef } from "react";
1
+ import { useEffect, useRef } from "react";
2
2
 
3
3
  import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
4
4
  import { DrawerContent, DrawerTitle } from "../../ui/Drawer.js";
5
+ import { scrollIntoViewIfNeeded } from "../../util/scrollIntoViewIfNeeded.js";
5
6
  import { useCurrentNavigation } from "../context/ZudokuContext.js";
6
7
  import { Slotlet } from "../SlotletProvider.js";
7
8
  import { SidebarItem } from "./SidebarItem.js";
@@ -15,6 +16,11 @@ export const Sidebar = ({
15
16
  const navRef = useRef<HTMLDivElement | null>(null);
16
17
  const navigation = useCurrentNavigation();
17
18
 
19
+ useEffect(() => {
20
+ const active = navRef.current?.querySelector('[aria-current="page"]');
21
+ scrollIntoViewIfNeeded(active ?? null);
22
+ }, []);
23
+
18
24
  return (
19
25
  <>
20
26
  <SidebarWrapper
@@ -87,27 +87,24 @@ const JSONObjectScalar = builder.addScalarType("JSONObject", GraphQLJSONObject);
87
87
  const JSONSchemaScalar = builder.addScalarType("JSONSchema", GraphQLJSONSchema);
88
88
 
89
89
  export const getAllTags = (schema: OpenAPIDocument): TagObject[] => {
90
- const tags = schema.tags ?? [];
91
-
92
- // Extract tags from operations
93
- const operationTags = Object.values(schema.paths ?? {})
94
- .flatMap((path) => Object.values(path ?? {}))
95
- .flatMap((operation) =>
96
- typeof operation === "object" && "tags" in operation
97
- ? (operation.tags ?? [])
98
- : [],
99
- );
100
-
101
- // Remove duplicates and tags that appear in the schema
102
- const uniqueOperationTags = [...new Set(operationTags)].filter(
103
- (tag) => !tags.some((rootTag) => rootTag.name === tag),
90
+ const rootTags = schema.tags ?? [];
91
+ const operationTags = new Set(
92
+ Object.values(schema.paths ?? {})
93
+ .flatMap((path) => Object.values(path ?? {}))
94
+ .flatMap((op) => (op as OperationObject).tags ?? []),
104
95
  );
105
- return [...tags, ...uniqueOperationTags.map((tag) => ({ name: tag }))];
96
+
97
+ return [
98
+ // Keep root tags that are actually used in operations
99
+ ...rootTags.filter((tag) => operationTags.has(tag.name)),
100
+ // Add tags found in operations but not defined in root tags
101
+ ...[...operationTags]
102
+ .filter((tag) => !rootTags.some((rt) => rt.name === tag))
103
+ .map((tag) => ({ name: tag })),
104
+ ];
106
105
  };
107
106
 
108
107
  const getAllOperations = (paths?: PathsObject) => {
109
- const start = performance.now();
110
-
111
108
  const operations = Object.entries(paths ?? {}).flatMap(([path, value]) =>
112
109
  HttpMethods.flatMap((method) => {
113
110
  if (!value?.[method]) return [];
@@ -11,7 +11,7 @@ import { FragmentType, useFragment } from "./graphql/index.js";
11
11
  import { SchemaView } from "./schema/SchemaView.js";
12
12
  import { methodForColor } from "./util/methodToColor.js";
13
13
 
14
- export const PARAM_GROUPS = ["path", "query", "header", "cookie"] as const;
14
+ const PARAM_GROUPS = ["path", "query", "header", "cookie"] as const;
15
15
  export type ParameterGroup = (typeof PARAM_GROUPS)[number];
16
16
 
17
17
  export const OperationListItem = ({
@@ -15,10 +15,15 @@ export const PlaygroundDialogWrapper = ({
15
15
  }) => {
16
16
  const headers = operation.parameters
17
17
  ?.filter((p) => p.in === "header")
18
+ .sort((a, b) => (a.required && !b.required ? -1 : 1))
18
19
  .map((p) => ({
19
20
  name: p.name,
20
- defaultValue: p.examples?.find((x) => x.value)?.value ?? "",
21
- defaultActive: false,
21
+ defaultValue:
22
+ p.schema?.default ?? p.examples?.find((x) => x.value)?.value ?? "",
23
+ defaultActive: p.required ?? false,
24
+ isRequired: p.required ?? false,
25
+ enum: p.schema?.type == "array" ? p.schema?.items?.enum : p.schema?.enum,
26
+ type: p.schema?.type ?? "string",
22
27
  }));
23
28
  const queryParams = operation.parameters
24
29
  ?.filter((p) => p.in === "query")
@@ -5,7 +5,6 @@ import {
5
5
  Controller,
6
6
  useFieldArray,
7
7
  useFormContext,
8
- UseFormRegister,
9
8
  } from "react-hook-form";
10
9
  import { Card } from "zudoku/ui/Card.js";
11
10
  import { Checkbox } from "zudoku/ui/Checkbox.js";
@@ -44,13 +43,7 @@ const headerOptions = Object.freeze([
44
43
  "X-Requested-With",
45
44
  ]);
46
45
 
47
- export const Headers = ({
48
- control,
49
- register,
50
- }: {
51
- register: UseFormRegister<PlaygroundForm>;
52
- control: Control<PlaygroundForm>;
53
- }) => {
46
+ export const Headers = ({ control }: { control: Control<PlaygroundForm> }) => {
54
47
  const { fields, append, remove } = useFieldArray<PlaygroundForm>({
55
48
  control,
56
49
  name: "headers",
@@ -81,7 +74,10 @@ export const Headers = ({
81
74
  <Card className="overflow-hidden">
82
75
  <ParamsGrid>
83
76
  {fields.map((header, i) => (
84
- <div key={i} className="group grid col-span-full grid-cols-subgrid">
77
+ <div
78
+ key={header.name}
79
+ className="group grid col-span-full grid-cols-subgrid"
80
+ >
85
81
  <div className="flex items-center gap-2 ">
86
82
  <Controller
87
83
  control={control}
@@ -36,6 +36,9 @@ export type Header = {
36
36
  name: string;
37
37
  defaultValue?: string;
38
38
  defaultActive?: boolean;
39
+ isRequired?: boolean;
40
+ enum?: string[];
41
+ type?: string;
39
42
  };
40
43
 
41
44
  export type QueryParam = {
@@ -337,7 +340,7 @@ export const Playground = ({
337
340
  </TabsList>
338
341
  </div>
339
342
  <TabsContent value="headers">
340
- <Headers control={control} register={register} />
343
+ <Headers control={control} />
341
344
  </TabsContent>
342
345
  <TabsContent value="parameters">
343
346
  {pathParams.length > 0 && (
@@ -39,9 +39,12 @@ const PlaygroundDialog = (props: PlaygroundDialogProps) => {
39
39
  <Dialog onOpenChange={(open) => setOpen(open)}>
40
40
  <DialogTrigger asChild>
41
41
  {props.children ?? (
42
- <button className="flex gap-1 items-center px-2 py-1 rounded-md transition text-xs bg-primary text-primary-foreground shadow-sm hover:bg-primary/80">
42
+ <button
43
+ type="button"
44
+ className="flex gap-1 items-center px-2 py-1 rounded-md transition text-xs bg-primary text-primary-foreground shadow-sm hover:bg-primary/80"
45
+ >
43
46
  Test
44
- <HeroPlayIcon className="" size={14} />
47
+ <HeroPlayIcon size={14} />
45
48
  </button>
46
49
  )}
47
50
  </DialogTrigger>
@@ -0,0 +1,18 @@
1
+ export const scrollIntoViewIfNeeded = (
2
+ element: Element | null,
3
+ options: ScrollIntoViewOptions = { block: "center" },
4
+ ) => {
5
+ if (!element) return;
6
+
7
+ const rect = element.getBoundingClientRect();
8
+ const isInView =
9
+ rect.top >= 0 &&
10
+ rect.left >= 0 &&
11
+ rect.bottom <=
12
+ (window.innerHeight || document.documentElement.clientHeight) &&
13
+ rect.right <= (window.innerWidth || document.documentElement.clientWidth);
14
+
15
+ if (isInView) return;
16
+
17
+ element.scrollIntoView(options);
18
+ };
@@ -2,25 +2,7 @@ import { useCallback, useEffect } from "react";
2
2
  import { useLocation } from "react-router";
3
3
  import { useViewportAnchor } from "../components/context/ViewportAnchorContext.js";
4
4
  import { DATA_ANCHOR_ATTR } from "../components/navigation/SidebarItem.js";
5
-
6
- const scrollIntoViewIfNeeded = (
7
- element: Element | null,
8
- options: ScrollIntoViewOptions = { block: "center" },
9
- ) => {
10
- if (!element) return;
11
-
12
- const rect = element.getBoundingClientRect();
13
- const isInView =
14
- rect.top >= 0 &&
15
- rect.left >= 0 &&
16
- rect.bottom <=
17
- (window.innerHeight || document.documentElement.clientHeight) &&
18
- rect.right <= (window.innerWidth || document.documentElement.clientWidth);
19
-
20
- if (isInView) return;
21
-
22
- element.scrollIntoView(options);
23
- };
5
+ import { scrollIntoViewIfNeeded } from "./scrollIntoViewIfNeeded.js";
24
6
 
25
7
  export const useScrollToHash = () => {
26
8
  const { setActiveAnchor } = useViewportAnchor();