zudoku 0.3.0-dev.116 → 0.3.0-dev.119

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 (157) hide show
  1. package/dist/app/demo.js +3 -1
  2. package/dist/app/demo.js.map +1 -1
  3. package/dist/app/main.js +1 -1
  4. package/dist/app/main.js.map +1 -1
  5. package/dist/app/standalone.js +3 -1
  6. package/dist/app/standalone.js.map +1 -1
  7. package/dist/cli/dev/handler.js +1 -1
  8. package/dist/lib/components/Markdown.js +3 -2
  9. package/dist/lib/components/Markdown.js.map +1 -1
  10. package/dist/lib/components/navigation/SidebarCategory.js +6 -2
  11. package/dist/lib/components/navigation/SidebarCategory.js.map +1 -1
  12. package/dist/lib/components/navigation/SidebarItem.js +3 -2
  13. package/dist/lib/components/navigation/SidebarItem.js.map +1 -1
  14. package/dist/lib/oas/graphql/index.js +17 -2
  15. package/dist/lib/oas/graphql/index.js.map +1 -1
  16. package/dist/lib/plugins/markdown/MdxPage.js +1 -1
  17. package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
  18. package/dist/lib/plugins/openapi/OperationListItem.js +6 -4
  19. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  20. package/dist/lib/plugins/openapi/ParameterList.js +3 -1
  21. package/dist/lib/plugins/openapi/ParameterList.js.map +1 -1
  22. package/dist/lib/plugins/openapi/ResponsesSidecarBox.d.ts +3 -1
  23. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js +5 -9
  24. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js.map +1 -1
  25. package/dist/lib/plugins/openapi/Sidecar.d.ts +3 -1
  26. package/dist/lib/plugins/openapi/Sidecar.js +6 -4
  27. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  28. package/dist/lib/plugins/openapi/schema/SchemaComponents.d.ts +13 -0
  29. package/dist/lib/plugins/openapi/schema/SchemaComponents.js +28 -0
  30. package/dist/lib/plugins/openapi/schema/SchemaComponents.js.map +1 -0
  31. package/dist/lib/plugins/openapi/schema/SchemaView.d.ts +6 -0
  32. package/dist/lib/plugins/openapi/schema/SchemaView.js +44 -0
  33. package/dist/lib/plugins/openapi/schema/SchemaView.js.map +1 -0
  34. package/dist/lib/plugins/openapi/schema/utils.d.ts +3 -0
  35. package/dist/lib/plugins/openapi/schema/utils.js +6 -0
  36. package/dist/lib/plugins/openapi/schema/utils.js.map +1 -0
  37. package/dist/lib/themeToggle.d.ts +1 -0
  38. package/dist/lib/themeToggle.js +7 -0
  39. package/dist/lib/themeToggle.js.map +1 -0
  40. package/dist/lib/util/MdxComponents.js +4 -4
  41. package/dist/lib/util/MdxComponents.js.map +1 -1
  42. package/dist/vite/config.d.ts +4 -5
  43. package/dist/vite/config.js +7 -4
  44. package/dist/vite/config.js.map +1 -1
  45. package/dist/vite/config.test.js +2 -2
  46. package/dist/vite/config.test.js.map +1 -1
  47. package/dist/vite/dev-server.d.ts +0 -1
  48. package/dist/vite/dev-server.js +2 -21
  49. package/dist/vite/dev-server.js.map +1 -1
  50. package/dist/vite/html.js +2 -11
  51. package/dist/vite/html.js.map +1 -1
  52. package/dist/vite/plugin-api-keys.d.ts +3 -3
  53. package/dist/vite/plugin-api-keys.js +2 -1
  54. package/dist/vite/plugin-api-keys.js.map +1 -1
  55. package/dist/vite/plugin-api.d.ts +3 -3
  56. package/dist/vite/plugin-api.js +2 -1
  57. package/dist/vite/plugin-api.js.map +1 -1
  58. package/dist/vite/plugin-auth.d.ts +3 -3
  59. package/dist/vite/plugin-auth.js +2 -1
  60. package/dist/vite/plugin-auth.js.map +1 -1
  61. package/dist/vite/plugin-component.d.ts +3 -3
  62. package/dist/vite/plugin-component.js +17 -14
  63. package/dist/vite/plugin-component.js.map +1 -1
  64. package/dist/vite/plugin-config-reload.d.ts +4 -0
  65. package/dist/vite/plugin-config-reload.js +24 -0
  66. package/dist/vite/plugin-config-reload.js.map +1 -0
  67. package/dist/vite/plugin-config.d.ts +2 -2
  68. package/dist/vite/plugin-config.js.map +1 -1
  69. package/dist/vite/plugin-custom-css.d.ts +3 -3
  70. package/dist/vite/plugin-custom-css.js +2 -1
  71. package/dist/vite/plugin-custom-css.js.map +1 -1
  72. package/dist/vite/plugin-docs.d.ts +3 -3
  73. package/dist/vite/plugin-docs.js +3 -2
  74. package/dist/vite/plugin-docs.js.map +1 -1
  75. package/dist/vite/plugin-html-transform.d.ts +2 -0
  76. package/dist/vite/plugin-html-transform.js +15 -0
  77. package/dist/vite/plugin-html-transform.js.map +1 -0
  78. package/dist/vite/plugin-mdx.d.ts +3 -3
  79. package/dist/vite/plugin-mdx.js +2 -1
  80. package/dist/vite/plugin-mdx.js.map +1 -1
  81. package/dist/vite/plugin-metadata.d.ts +1 -1
  82. package/dist/vite/plugin-redirect.d.ts +3 -3
  83. package/dist/vite/plugin-redirect.js +2 -1
  84. package/dist/vite/plugin-redirect.js.map +1 -1
  85. package/dist/vite/plugin-sidebar.d.ts +3 -3
  86. package/dist/vite/plugin-sidebar.js +2 -1
  87. package/dist/vite/plugin-sidebar.js.map +1 -1
  88. package/dist/vite/plugin.d.ts +3 -2
  89. package/dist/vite/plugin.js +16 -11
  90. package/dist/vite/plugin.js.map +1 -1
  91. package/lib/{CategoryHeading-BWq12Bfa.js → CategoryHeading-z15xh7Jb.js} +2 -2
  92. package/lib/{CategoryHeading-BWq12Bfa.js.map → CategoryHeading-z15xh7Jb.js.map} +1 -1
  93. package/lib/{Combination-D-9IH0zy.js → Combination-DTfV-c98.js} +2 -2
  94. package/lib/{Combination-D-9IH0zy.js.map → Combination-DTfV-c98.js.map} +1 -1
  95. package/lib/{Input-HmAaR6kw.js → Input-DB9VROFR.js} +3 -3
  96. package/lib/{Input-HmAaR6kw.js.map → Input-DB9VROFR.js.map} +1 -1
  97. package/lib/Markdown-CEccPMI_.js +20508 -0
  98. package/lib/Markdown-CEccPMI_.js.map +1 -0
  99. package/lib/{MdxPage-oN3huD58.js → MdxPage-CnqOoqvp.js} +12 -15
  100. package/lib/MdxPage-CnqOoqvp.js.map +1 -0
  101. package/lib/OperationList-MFaKgMyg.js +414 -0
  102. package/lib/OperationList-MFaKgMyg.js.map +1 -0
  103. package/lib/{Route-DAF15JAU.js → Route-CL9hUSJ2.js} +2 -2
  104. package/lib/{Route-DAF15JAU.js.map → Route-CL9hUSJ2.js.map} +1 -1
  105. package/lib/{SlotletProvider-BG2PU1Mf.js → SlotletProvider-ByLSCZQa.js} +3 -3
  106. package/lib/{SlotletProvider-BG2PU1Mf.js.map → SlotletProvider-ByLSCZQa.js.map} +1 -1
  107. package/lib/{Spinner-BCz1kNGw.js → Spinner-BT_AYFrA.js} +3 -3
  108. package/lib/{Spinner-BCz1kNGw.js.map → Spinner-BT_AYFrA.js.map} +1 -1
  109. package/lib/assets/{worker-CR7aeKop.js → worker-TEvS6miZ.js} +709 -702
  110. package/lib/assets/{worker-CR7aeKop.js.map → worker-TEvS6miZ.js.map} +1 -1
  111. package/lib/{index-CtKkHGcd.js → index-Bf5yypxf.js} +671 -640
  112. package/lib/{index-CtKkHGcd.js.map → index-Bf5yypxf.js.map} +1 -1
  113. package/lib/{index-D-9Z7HSn.js → index-Dz4LyXZI.js} +3 -3
  114. package/lib/{index-D-9Z7HSn.js.map → index-Dz4LyXZI.js.map} +1 -1
  115. package/lib/zudoku.components.js +494 -481
  116. package/lib/zudoku.components.js.map +1 -1
  117. package/lib/zudoku.openapi-worker.js +733 -726
  118. package/lib/zudoku.openapi-worker.js.map +1 -1
  119. package/lib/zudoku.plugin-api-keys.js +4 -4
  120. package/lib/zudoku.plugin-custom-page.js +1 -1
  121. package/lib/zudoku.plugin-markdown.js +1 -1
  122. package/lib/zudoku.plugin-openapi.js +3 -3
  123. package/package.json +2 -1
  124. package/src/app/demo.tsx +4 -1
  125. package/src/app/main.tsx +1 -1
  126. package/src/app/standalone.tsx +4 -1
  127. package/src/lib/components/Markdown.tsx +3 -2
  128. package/src/lib/components/navigation/SidebarCategory.tsx +13 -2
  129. package/src/lib/components/navigation/SidebarItem.tsx +3 -2
  130. package/src/lib/oas/graphql/index.ts +24 -5
  131. package/src/lib/plugins/markdown/MdxPage.tsx +0 -1
  132. package/src/lib/plugins/openapi/OperationListItem.tsx +18 -8
  133. package/src/lib/plugins/openapi/ParameterList.tsx +10 -8
  134. package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +51 -39
  135. package/src/lib/plugins/openapi/Sidecar.tsx +13 -4
  136. package/src/lib/plugins/openapi/schema/SchemaComponents.tsx +126 -0
  137. package/src/lib/plugins/openapi/schema/SchemaView.tsx +112 -0
  138. package/src/lib/plugins/openapi/schema/utils.ts +10 -0
  139. package/src/lib/themeToggle.ts +7 -0
  140. package/src/lib/util/MdxComponents.tsx +12 -12
  141. package/dist/lib/plugins/openapi/SchemaListView.d.ts +0 -7
  142. package/dist/lib/plugins/openapi/SchemaListView.js +0 -27
  143. package/dist/lib/plugins/openapi/SchemaListView.js.map +0 -1
  144. package/dist/lib/plugins/openapi/SchemaListViewItem.d.ts +0 -8
  145. package/dist/lib/plugins/openapi/SchemaListViewItem.js +0 -25
  146. package/dist/lib/plugins/openapi/SchemaListViewItem.js.map +0 -1
  147. package/dist/lib/plugins/openapi/SchemaListViewItemGroup.d.ts +0 -8
  148. package/dist/lib/plugins/openapi/SchemaListViewItemGroup.js +0 -17
  149. package/dist/lib/plugins/openapi/SchemaListViewItemGroup.js.map +0 -1
  150. package/lib/Markdown-B_Gax7at.js +0 -14108
  151. package/lib/Markdown-B_Gax7at.js.map +0 -1
  152. package/lib/MdxPage-oN3huD58.js.map +0 -1
  153. package/lib/OperationList-Ctj0ihBN.js +0 -448
  154. package/lib/OperationList-Ctj0ihBN.js.map +0 -1
  155. package/src/lib/plugins/openapi/SchemaListView.tsx +0 -75
  156. package/src/lib/plugins/openapi/SchemaListViewItem.tsx +0 -125
  157. package/src/lib/plugins/openapi/SchemaListViewItemGroup.tsx +0 -63
@@ -1,12 +1,12 @@
1
1
  import { j as e } from "./jsx-runtime-B6kdoens.js";
2
- import { S as p, R as f } from "./SlotletProvider-BG2PU1Mf.js";
3
- import { u as g, a as u, I as j, S as k, b as v, c as w, d as b, e as K, f as y } from "./Input-HmAaR6kw.js";
2
+ import { S as p, R as f } from "./SlotletProvider-ByLSCZQa.js";
3
+ import { u as g, a as u, I as j, S as k, b as v, c as w, d as b, e as K, f as y } from "./Input-DB9VROFR.js";
4
4
  import { b as N, L as x, O as E } from "./index-7kcHaXD6.js";
5
5
  import { u as h, t as A, j as S } from "./ZudokuContext-BIZ8zHbZ.js";
6
- import { B as l, p as I } from "./Combination-D-9IH0zy.js";
6
+ import { B as l, p as I } from "./Combination-DTfV-c98.js";
7
7
  import { D as P } from "./DeveloperHint-BQSFXH01.js";
8
8
  import { useState as C } from "react";
9
- import { c as d, a as D } from "./Markdown-B_Gax7at.js";
9
+ import { c as d, a as D } from "./Markdown-CEccPMI_.js";
10
10
  /**
11
11
  * @license lucide-react v0.378.0 - ISC
12
12
  *
@@ -1,5 +1,5 @@
1
1
  import { j as m } from "./jsx-runtime-B6kdoens.js";
2
- import { P as o } from "./Markdown-B_Gax7at.js";
2
+ import { P as o } from "./Markdown-CEccPMI_.js";
3
3
  const l = (s) => ({
4
4
  getRoutes: () => s.map(({ path: e, element: t }) => ({
5
5
  path: e,
@@ -6,7 +6,7 @@ const u = (t, e) => Object.entries(t).flatMap(([a, r]) => {
6
6
  return {
7
7
  path: s.at(-1) === "index" ? s.slice(0, -1).join("/") : o,
8
8
  lazy: async () => {
9
- const { MdxPage: i } = await import("./MdxPage-oN3huD58.js"), { default: p, ...c } = await r();
9
+ const { MdxPage: i } = await import("./MdxPage-CnqOoqvp.js"), { default: p, ...c } = await r();
10
10
  return {
11
11
  element: /* @__PURE__ */ m.jsx(
12
12
  i,
@@ -1,10 +1,10 @@
1
1
  import "./jsx-runtime-B6kdoens.js";
2
- import { o as l } from "./index-CtKkHGcd.js";
2
+ import { o as l } from "./index-Bf5yypxf.js";
3
3
  import "./urql-DrBfkb92.js";
4
4
  import "./ZudokuContext-BIZ8zHbZ.js";
5
5
  import "zudoku/openapi-worker";
6
- import "./Combination-D-9IH0zy.js";
7
- import "./Markdown-B_Gax7at.js";
6
+ import "./Combination-DTfV-c98.js";
7
+ import "./Markdown-CEccPMI_.js";
8
8
  import "./joinPath-B7kNnUX4.js";
9
9
  import "./router-BiRCp01d.js";
10
10
  import "./index-7kcHaXD6.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.3.0-dev.116",
3
+ "version": "0.3.0-dev.119",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -128,6 +128,7 @@
128
128
  "react-is": "18.3.1",
129
129
  "react-markdown": "9.0.1",
130
130
  "react-router-dom": "6.25.1",
131
+ "rehype-raw": "7.0.0",
131
132
  "rehype-slug": "6.0.0",
132
133
  "remark-comment": "1.0.0",
133
134
  "remark-directive": "3.0.0",
package/src/app/demo.tsx CHANGED
@@ -4,6 +4,7 @@ import { createBrowserRouter } from "react-router-dom";
4
4
  import { Bootstrap } from "zudoku/components";
5
5
  import type { ZudokuConfig } from "../config/validators/validate.js";
6
6
  import { openApiPlugin } from "../lib/plugins/openapi/index.js";
7
+ import { themeToggle } from "../lib/themeToggle.js";
7
8
  import "../lib/util/logInit.js";
8
9
  import "./main.css";
9
10
  import { getRoutesByConfig } from "./main.js";
@@ -16,6 +17,8 @@ if (!apiUrl) {
16
17
  );
17
18
  }
18
19
 
20
+ themeToggle();
21
+
19
22
  logger.info(`API URL: ${apiUrl}`);
20
23
 
21
24
  const root =
@@ -51,6 +54,6 @@ const config = {
51
54
 
52
55
  const routes = getRoutesByConfig(config);
53
56
  const router = createBrowserRouter(routes, {
54
- basename: "/demo",
57
+ basename: window.location.pathname,
55
58
  });
56
59
  createRoot(root).render(<Bootstrap router={router} />);
package/src/app/main.tsx CHANGED
@@ -9,9 +9,9 @@ import "virtual:zudoku-theme.css";
9
9
  import { DevPortal, Layout, RouterError } from "zudoku/components";
10
10
  import { isNavigationPlugin } from "zudoku/internal";
11
11
  import { customPagePlugin } from "zudoku/plugins/custom-page";
12
+ import { inkeepSearchPlugin } from "zudoku/plugins/search-inkeep";
12
13
  import type { ZudokuConfig } from "../config/config.js";
13
14
  import type { ZudokuContextOptions } from "../lib/core/DevPortalContext.js";
14
- import { inkeepSearchPlugin } from "../lib/plugins/search-inkeep/index.js";
15
15
 
16
16
  export const convertZudokuConfigToOptions = (
17
17
  config: ZudokuConfig,
@@ -3,6 +3,7 @@ import { createBrowserRouter } from "react-router-dom";
3
3
  import { Bootstrap } from "zudoku/components";
4
4
  import type { ZudokuConfig } from "../config/validators/validate.js";
5
5
  import { openApiPlugin } from "../lib/plugins/openapi/index.js";
6
+ import { themeToggle } from "../lib/themeToggle.js";
6
7
  import "../lib/util/logInit.js";
7
8
  import "./main.css";
8
9
  import { getRoutesByConfig } from "./main.js";
@@ -12,6 +13,8 @@ if (!root) {
12
13
  throw new Error("No div found with attribute data-api-url");
13
14
  }
14
15
 
16
+ themeToggle();
17
+
15
18
  const apiUrl = root.getAttribute("data-api-url");
16
19
  const pageTitle = document.getElementsByTagName("title")[0].innerText;
17
20
  const logoUrl = root.getAttribute("data-logo-url");
@@ -50,6 +53,6 @@ const config = {
50
53
 
51
54
  const routes = getRoutesByConfig(config);
52
55
  const router = createBrowserRouter(routes, {
53
- basename: "/standalone",
56
+ basename: window.location.pathname,
54
57
  });
55
58
  createRoot(root).render(<Bootstrap router={router} />);
@@ -1,4 +1,5 @@
1
1
  import ReactMarkdown from "react-markdown";
2
+ import rehypeRaw from "rehype-raw";
2
3
  import remarkGfm from "remark-gfm";
3
4
  import { visit } from "unist-util-visit";
4
5
  import { MdxComponents } from "../util/MdxComponents.js";
@@ -8,13 +9,13 @@ import { MdxComponents } from "../util/MdxComponents.js";
8
9
  const rehypeCodeBlockPlugin = () => (tree: any) => {
9
10
  visit(tree, "element", (node, _index, parent) => {
10
11
  if (node.tagName === "code") {
11
- node.properties.inline = parent?.tagName !== "pre";
12
+ node.properties.inline = String(parent?.tagName !== "pre");
12
13
  }
13
14
  });
14
15
  };
15
16
 
16
17
  const remarkPlugins = [remarkGfm];
17
- const rehypePlugins = [rehypeCodeBlockPlugin];
18
+ const rehypePlugins = [rehypeCodeBlockPlugin, rehypeRaw];
18
19
 
19
20
  // other styles are defined in main.css .prose
20
21
  export const ProseClasses = "prose dark:prose-invert prose-neutral";
@@ -18,6 +18,7 @@ export const SidebarCategory = ({
18
18
  }) => {
19
19
  const topNavItem = useTopNavigationItem();
20
20
  const isCategoryOpen = useIsCategoryOpen(category);
21
+ const [hasInteracted, setHasInteracted] = useState(false);
21
22
 
22
23
  const isCollapsible = category.collapsible ?? true;
23
24
  const isCollapsed = category.collapsed ?? true;
@@ -40,11 +41,15 @@ export const SidebarCategory = ({
40
41
  onClick={(e) => {
41
42
  e.preventDefault();
42
43
  setOpen((prev) => !prev);
44
+ setHasInteracted(true);
43
45
  }}
44
46
  >
45
47
  <ChevronRightIcon
46
48
  size={16}
47
- className="transition shrink-0 group-data-[state=open]:rotate-90"
49
+ className={cn(
50
+ hasInteracted && "transition",
51
+ "shrink-0 group-data-[state=open]:rotate-90",
52
+ )}
48
53
  />
49
54
  </button>
50
55
  );
@@ -88,7 +93,13 @@ export const SidebarCategory = ({
88
93
  </div>
89
94
  )}
90
95
  </Collapsible.Trigger>
91
- <Collapsible.Content className="CollapsibleContent ms-[calc(var(--padding-nav-item)*1.125)]">
96
+ <Collapsible.Content
97
+ className={cn(
98
+ // CollapsibleContent class is used to animate and it should only be applied when the user has triggered the toggle
99
+ hasInteracted && "CollapsibleContent",
100
+ "ms-[calc(var(--padding-nav-item)*1.125)]",
101
+ )}
102
+ >
92
103
  <ul className="mt-1 border-l ps-2">
93
104
  {category.items.map((item) => (
94
105
  <SidebarItem
@@ -1,6 +1,6 @@
1
1
  import { cva } from "class-variance-authority";
2
2
  import { ExternalLinkIcon } from "lucide-react";
3
- import { NavLink } from "react-router-dom";
3
+ import { NavLink, useSearchParams } from "react-router-dom";
4
4
 
5
5
  import type { SidebarItem as SidebarItemType } from "../../../config/validators/SidebarSchema.js";
6
6
  import { cn } from "../../util/cn.js";
@@ -42,6 +42,7 @@ export const SidebarItem = ({
42
42
  }) => {
43
43
  const topNavItem = useTopNavigationItem();
44
44
  const { activeAnchor } = useViewportAnchor();
45
+ const [searchParams] = useSearchParams();
45
46
 
46
47
  switch (item.type) {
47
48
  case "category":
@@ -69,7 +70,7 @@ export const SidebarItem = ({
69
70
  case "link":
70
71
  return item.href.startsWith("#") ? (
71
72
  <AnchorLink
72
- to={item.href}
73
+ to={{ hash: item.href, search: searchParams.toString() }}
73
74
  {...{ [DATA_ANCHOR_ATTR]: item.href.slice(1) }}
74
75
  className={cn(
75
76
  "flex gap-2.5 justify-between",
@@ -68,6 +68,25 @@ const builder = new SchemaBuilder<{
68
68
  const JSONScalar = builder.addScalarType("JSON", GraphQLJSON);
69
69
  const JSONObjectScalar = builder.addScalarType("JSONObject", GraphQLJSONObject);
70
70
 
71
+ const getAllTags = (schema: OpenAPIDocument): TagObject[] => {
72
+ const tags = schema.tags ?? [];
73
+
74
+ // Extract tags from operations
75
+ const operationTags = Object.values(schema.paths ?? {})
76
+ .flatMap((path) => Object.values(path ?? {}))
77
+ .flatMap((operation) =>
78
+ typeof operation === "object" && "tags" in operation
79
+ ? operation.tags ?? []
80
+ : [],
81
+ );
82
+
83
+ // Remove duplicates and tags that appear in the schema
84
+ const uniqueOperationTags = [...new Set(operationTags)].filter(
85
+ (tag) => !tags.some((rootTag) => rootTag.name === tag),
86
+ );
87
+ return [...tags, ...uniqueOperationTags.map((tag) => ({ name: tag }))];
88
+ };
89
+
71
90
  const getAllOperations = (paths?: PathsObject, tag?: string) => {
72
91
  return Object.entries(paths ?? {}).flatMap(([path, value]) =>
73
92
  HttpMethods.flatMap((method) => {
@@ -117,7 +136,7 @@ const SchemaTag = builder.objectRef<TagObject>("SchemaTag").implement({
117
136
  operations: t.field({
118
137
  type: [OperationItem],
119
138
  resolve: (parent, _args, ctx) => {
120
- const rootTags = ctx.schema.tags?.map((tag) => tag.name) ?? [];
139
+ const rootTags = getAllTags(ctx.schema).map((tag) => tag.name);
121
140
 
122
141
  return getAllOperations(ctx.schema.paths, parent.name).filter((item) =>
123
142
  parent.name
@@ -363,10 +382,10 @@ const Schema = builder.objectRef<OpenAPIDocument>("Schema").implement({
363
382
  name: t.arg.string(),
364
383
  },
365
384
  type: [SchemaTag],
366
- resolve: (root, args) =>
367
- [...(root.tags ?? []), { name: "" }].filter(
368
- (tag) => !args.name || args.name === tag.name,
369
- ),
385
+ resolve: (root, args) => {
386
+ const tags = [...getAllTags(root), { name: "" }];
387
+ return args.name ? tags.filter((tag) => tag.name === args.name) : tags;
388
+ },
370
389
  }),
371
390
  operations: t.field({
372
391
  type: [OperationItem],
@@ -23,7 +23,6 @@ const MarkdownHeadings = {
23
23
  ),
24
24
  h3: ({ children, id }) => (
25
25
  <Heading level={3} id={id} registerSidebarAnchor>
26
- {" "}
27
26
  {children}
28
27
  </Heading>
29
28
  ),
@@ -1,3 +1,4 @@
1
+ import { useState } from "react";
1
2
  import { Heading } from "../../components/Heading.js";
2
3
  import { Markdown } from "../../components/Markdown.js";
3
4
  import { Card } from "../../ui/Card.js";
@@ -6,9 +7,9 @@ import { groupBy } from "../../util/groupBy.js";
6
7
  import { renderIf } from "../../util/renderIf.js";
7
8
  import { OperationsFragment } from "./OperationList.js";
8
9
  import { ParameterList } from "./ParameterList.js";
9
- import { SchemaListView } from "./SchemaListView.js";
10
10
  import { Sidecar } from "./Sidecar.js";
11
11
  import { FragmentType, useFragment } from "./graphql/index.js";
12
+ import { SchemaView } from "./schema/SchemaView.js";
12
13
  import { SchemaProseClasses } from "./util/prose.js";
13
14
 
14
15
  export const PARAM_GROUPS = ["path", "query", "header", "cookie"] as const;
@@ -26,6 +27,8 @@ export const OperationListItem = ({
26
27
  );
27
28
 
28
29
  const first = operation.responses.at(0);
30
+ const [selectedResponse, setSelectedResponse] = useState(first?.statusCode);
31
+
29
32
  return (
30
33
  <div
31
34
  key={operation.operationId}
@@ -62,7 +65,7 @@ export const OperationListItem = ({
62
65
  <Heading level={3} className="capitalize">
63
66
  Request Body
64
67
  </Heading>
65
- <SchemaListView schema={schema} />
68
+ <SchemaView schema={schema} />
66
69
  </div>
67
70
  ))}
68
71
  {operation.responses.length > 0 && (
@@ -70,12 +73,15 @@ export const OperationListItem = ({
70
73
  <Heading level={3} className="capitalize mt-8 pt-8 border-t">
71
74
  Responses
72
75
  </Heading>
73
- <Tabs defaultValue={`${first?.statusCode}${first?.description}`}>
76
+ <Tabs
77
+ onValueChange={(value) => setSelectedResponse(value)}
78
+ value={selectedResponse}
79
+ >
74
80
  {operation.responses.length > 1 && (
75
81
  <TabsList>
76
82
  {operation.responses.map((response) => (
77
83
  <TabsTrigger
78
- value={response.statusCode + response.description}
84
+ value={response.statusCode}
79
85
  key={response.statusCode}
80
86
  title={response.description}
81
87
  >
@@ -87,16 +93,16 @@ export const OperationListItem = ({
87
93
  <ul className="list-none m-0 px-0 overflow-hidden">
88
94
  {operation.responses.map((response) => (
89
95
  <TabsContent
90
- value={response.statusCode + response.description}
96
+ value={response.statusCode}
91
97
  key={response.statusCode}
92
98
  >
93
99
  {renderIf(
94
100
  response.content?.find((content) => content.schema),
95
101
  (content) => {
96
- return <SchemaListView schema={content.schema} />;
102
+ return <SchemaView schema={content.schema} />;
97
103
  },
98
104
  ) ?? (
99
- <Card className="font-mono text-sm p-4">
105
+ <Card className="font-mono text-sm p-4 italic bg-border/20">
100
106
  No response body
101
107
  </Card>
102
108
  )}
@@ -108,7 +114,11 @@ export const OperationListItem = ({
108
114
  )}
109
115
  </div>
110
116
 
111
- <Sidecar operation={operation} />
117
+ <Sidecar
118
+ selectedResponse={selectedResponse}
119
+ onSelectResponse={setSelectedResponse}
120
+ operation={operation}
121
+ />
112
122
  </div>
113
123
  );
114
124
  };
@@ -21,14 +21,16 @@ export const ParameterList = ({
21
21
  </Heading>
22
22
  <Card>
23
23
  <ul className="list-none m-0 px-0 divide-y ">
24
- {parameters.map((parameter) => (
25
- <ParameterListItem
26
- key={`${parameter.name}-${parameter.in}`}
27
- parameter={parameter}
28
- id={id}
29
- group={group}
30
- />
31
- ))}
24
+ {parameters
25
+ .sort((a, b) => (a.required === b.required ? 0 : a.required ? -1 : 1))
26
+ .map((parameter) => (
27
+ <ParameterListItem
28
+ key={`${parameter.name}-${parameter.in}`}
29
+ parameter={parameter}
30
+ id={id}
31
+ group={group}
32
+ />
33
+ ))}
32
34
  </ul>
33
35
  </Card>
34
36
  </>
@@ -1,4 +1,4 @@
1
- import { useState } from "react";
1
+ import * as Tabs from "@radix-ui/react-tabs";
2
2
  import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
3
3
  import { type SchemaObject } from "../../oas/graphql/index.js";
4
4
  import { cn } from "../../util/cn.js";
@@ -9,52 +9,64 @@ import { generateSchemaExample } from "./util/generateSchemaExample.js";
9
9
  type Responses = OperationListItemResult["responses"];
10
10
  export const ResponsesSidecarBox = ({
11
11
  responses,
12
+ selectedResponse,
13
+ onSelectResponse,
12
14
  }: {
13
15
  responses: Responses;
14
- }) => {
15
- const [tabIndex, setTabIndex] = useState(0);
16
-
17
- const activeTab = responses[tabIndex];
18
- const schema = activeTab.content?.[0]?.schema as SchemaObject | undefined;
19
-
20
- return (
21
- <SidecarBox.Root>
22
- <SidecarBox.Head className="text-xs grid grid-rows-2 pb-0">
16
+ selectedResponse?: string;
17
+ onSelectResponse: (response: string) => void;
18
+ }) => (
19
+ <SidecarBox.Root>
20
+ <Tabs.Root
21
+ defaultValue={responses[0]?.statusCode}
22
+ value={selectedResponse}
23
+ onValueChange={(value) => onSelectResponse(value)}
24
+ >
25
+ <SidecarBox.Head className="text-xs flex flex-col gap-2 pb-0">
23
26
  <span className="font-mono">Example Responses</span>
24
- <div className="flex gap-2">
25
- {responses.map((response, index) => (
26
- <div
27
+ <Tabs.List className="flex gap-2">
28
+ {responses.map((response) => (
29
+ <Tabs.Trigger
27
30
  key={response.statusCode}
28
- onClick={() => setTabIndex(index)}
31
+ value={response.statusCode}
29
32
  className={cn(
30
33
  "text-xs font-mono px-1.5 py-1 pb-px translate-y-px border-b-2 border-transparent rounded-t cursor-pointer",
31
- tabIndex === index
32
- ? "text-primary dark:text-inherit border-primary"
33
- : "hover:border-accent-foreground/25",
34
+ "data-[state=active]:text-primary data-[state=active]:dark:text-inherit data-[state=active]:border-primary",
35
+ "hover:border-accent-foreground/25",
34
36
  )}
35
37
  >
36
38
  {response.statusCode}
37
- </div>
39
+ </Tabs.Trigger>
38
40
  ))}
39
- </div>
41
+ </Tabs.List>
40
42
  </SidecarBox.Head>
41
- <SidecarBox.Body>
42
- {schema ? (
43
- <SyntaxHighlight
44
- language="json"
45
- noBackground
46
- className="text-xs"
47
- code={JSON.stringify(generateSchemaExample(schema), null, 2)}
48
- />
49
- ) : (
50
- <span className="text-muted-foreground font-mono italic text-xs">
51
- Empty Response
52
- </span>
53
- )}
54
- </SidecarBox.Body>
55
- <SidecarBox.Footer className="flex justify-end text-xs">
56
- {responses[tabIndex].description}
57
- </SidecarBox.Footer>
58
- </SidecarBox.Root>
59
- );
60
- };
43
+ {responses.map((response) => {
44
+ const schema = response.content?.[0]?.schema as
45
+ | SchemaObject
46
+ | undefined;
47
+
48
+ return (
49
+ <Tabs.Content key={response.statusCode} value={response.statusCode}>
50
+ <SidecarBox.Body>
51
+ {schema ? (
52
+ <SyntaxHighlight
53
+ language="json"
54
+ noBackground
55
+ className="text-xs"
56
+ code={JSON.stringify(generateSchemaExample(schema), null, 2)}
57
+ />
58
+ ) : (
59
+ <span className="text-muted-foreground font-mono italic text-xs">
60
+ Empty Response
61
+ </span>
62
+ )}
63
+ </SidecarBox.Body>
64
+ <SidecarBox.Footer className="flex justify-end text-xs">
65
+ {response.description}
66
+ </SidecarBox.Footer>
67
+ </Tabs.Content>
68
+ );
69
+ })}
70
+ </Tabs.Root>
71
+ </SidecarBox.Root>
72
+ );
@@ -84,8 +84,12 @@ const methodToColor = {
84
84
 
85
85
  export const Sidecar = ({
86
86
  operation,
87
+ selectedResponse,
88
+ onSelectResponse,
87
89
  }: {
88
90
  operation: OperationListItemResult;
91
+ selectedResponse?: string;
92
+ onSelectResponse: (response: string) => void;
89
93
  }) => {
90
94
  const oasConfig = useOasConfig();
91
95
  const [result] = useQuery({
@@ -105,8 +109,9 @@ export const Sidecar = ({
105
109
 
106
110
  const requestBodyContent = operation.requestBody?.content;
107
111
 
108
- const path = operation.path.split("/").map((part) => (
109
- <Fragment key={part}>
112
+ const path = operation.path.split("/").map((part, i, arr) => (
113
+ // eslint-disable-next-line react/no-array-index-key
114
+ <Fragment key={part + i}>
110
115
  {part.startsWith("{") && part.endsWith("}") ? (
111
116
  <ColorizedParam
112
117
  name={part.slice(1, -1)}
@@ -119,7 +124,7 @@ export const Sidecar = ({
119
124
  ) : (
120
125
  part
121
126
  )}
122
- /
127
+ {i < arr.length - 1 ? "/" : null}
123
128
  <wbr />
124
129
  </Fragment>
125
130
  ));
@@ -200,7 +205,11 @@ export const Sidecar = ({
200
205
  <RequestBodySidecarBox content={requestBodyContent} />
201
206
  )}
202
207
  {operation.responses.length > 0 && (
203
- <ResponsesSidecarBox responses={operation.responses} />
208
+ <ResponsesSidecarBox
209
+ selectedResponse={selectedResponse}
210
+ onSelectResponse={onSelectResponse}
211
+ responses={operation.responses}
212
+ />
204
213
  )}
205
214
  </aside>
206
215
  );