zudoku 0.3.0-dev.10 → 0.3.0-dev.12

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 (125) hide show
  1. package/dist/app/App.js +8 -6
  2. package/dist/app/App.js.map +1 -1
  3. package/dist/config/config.d.ts +5 -14
  4. package/dist/lib/authentication/authentication.d.ts +2 -2
  5. package/dist/lib/components/DevPortal.d.ts +2 -20
  6. package/dist/lib/components/DevPortal.js +13 -8
  7. package/dist/lib/components/DevPortal.js.map +1 -1
  8. package/dist/lib/components/Header.js +2 -2
  9. package/dist/lib/components/Header.js.map +1 -1
  10. package/dist/lib/components/Heading.d.ts +9 -4
  11. package/dist/lib/components/Heading.js +17 -2
  12. package/dist/lib/components/Heading.js.map +1 -1
  13. package/dist/lib/components/Layout.js +1 -1
  14. package/dist/lib/components/Layout.js.map +1 -1
  15. package/dist/lib/components/context/DevPortalProvider.d.ts +1 -1
  16. package/dist/lib/components/context/DevPortalProvider.js +2 -2
  17. package/dist/lib/components/context/DevPortalProvider.js.map +1 -1
  18. package/dist/lib/core/DevPortalContext.d.ts +33 -3
  19. package/dist/lib/core/DevPortalContext.js +4 -2
  20. package/dist/lib/core/DevPortalContext.js.map +1 -1
  21. package/dist/lib/core/plugins.d.ts +7 -4
  22. package/dist/lib/core/plugins.js +1 -0
  23. package/dist/lib/core/plugins.js.map +1 -1
  24. package/dist/lib/oas/graphql/index.js +1 -1
  25. package/dist/lib/oas/graphql/index.js.map +1 -1
  26. package/dist/lib/oas/parser/index.js +3 -1
  27. package/dist/lib/oas/parser/index.js.map +1 -1
  28. package/dist/lib/plugins/markdown/MdxPage.d.ts +3 -2
  29. package/dist/lib/plugins/markdown/MdxPage.js +5 -4
  30. package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
  31. package/dist/lib/plugins/markdown/generateRoutes.d.ts +2 -2
  32. package/dist/lib/plugins/markdown/generateRoutes.js +2 -2
  33. package/dist/lib/plugins/markdown/generateRoutes.js.map +1 -1
  34. package/dist/lib/plugins/markdown/index.d.ts +4 -1
  35. package/dist/lib/plugins/markdown/index.js +2 -2
  36. package/dist/lib/plugins/markdown/index.js.map +1 -1
  37. package/dist/lib/plugins/openapi/OperationList.js +4 -3
  38. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  39. package/dist/lib/plugins/openapi/OperationListItem.js +8 -4
  40. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  41. package/dist/lib/plugins/openapi/ParameterList.js +1 -1
  42. package/dist/lib/plugins/openapi/ParameterList.js.map +1 -1
  43. package/dist/lib/plugins/openapi/ParameterListItem.js +1 -1
  44. package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
  45. package/dist/lib/plugins/openapi/{MakeRequest.d.ts → PlaygroundDialogWrapper.d.ts} +1 -1
  46. package/dist/lib/plugins/openapi/{MakeRequest.js → PlaygroundDialogWrapper.js} +4 -4
  47. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js.map +1 -0
  48. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js +1 -9
  49. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js.map +1 -1
  50. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js +1 -1
  51. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js.map +1 -1
  52. package/dist/lib/plugins/openapi/SchemaListView.js +4 -26
  53. package/dist/lib/plugins/openapi/SchemaListView.js.map +1 -1
  54. package/dist/lib/plugins/openapi/SchemaListViewItem.d.ts +7 -0
  55. package/dist/lib/plugins/openapi/SchemaListViewItem.js +16 -0
  56. package/dist/lib/plugins/openapi/SchemaListViewItem.js.map +1 -0
  57. package/dist/lib/plugins/openapi/SchemaListViewItemGroup.d.ts +8 -0
  58. package/dist/lib/plugins/openapi/SchemaListViewItemGroup.js +17 -0
  59. package/dist/lib/plugins/openapi/SchemaListViewItemGroup.js.map +1 -0
  60. package/dist/lib/plugins/openapi/Sidecar.js +2 -2
  61. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  62. package/dist/lib/plugins/openapi/index.js +8 -0
  63. package/dist/lib/plugins/openapi/index.js.map +1 -1
  64. package/dist/lib/plugins/openapi/playground/Playground.d.ts +4 -4
  65. package/dist/lib/plugins/openapi/playground/Playground.js +7 -11
  66. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  67. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.d.ts +3 -0
  68. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js +10 -0
  69. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js.map +1 -0
  70. package/dist/lib/plugins/openapi/util/prose.d.ts +1 -0
  71. package/dist/lib/plugins/openapi/util/prose.js +4 -0
  72. package/dist/lib/plugins/openapi/util/prose.js.map +1 -0
  73. package/dist/lib/plugins/openapi/worker/worker.js +25 -1
  74. package/dist/lib/plugins/openapi/worker/worker.js.map +1 -1
  75. package/dist/lib/ui/button-variants.d.ts +1 -1
  76. package/dist/lib/util/MdxComponents.js +1 -1
  77. package/dist/lib/util/MdxComponents.js.map +1 -1
  78. package/dist/lib/util/objectEntries.d.ts +4 -0
  79. package/dist/lib/util/objectEntries.js +2 -0
  80. package/dist/lib/util/objectEntries.js.map +1 -0
  81. package/dist/lib/util/renderIf.d.ts +1 -0
  82. package/dist/lib/util/renderIf.js +2 -0
  83. package/dist/lib/util/renderIf.js.map +1 -0
  84. package/dist/vite/plugin-docs.js +1 -1
  85. package/dist/vite/plugin-docs.js.map +1 -1
  86. package/lib/{Spinner-DCwVN24H.js → Spinner-CbtkR-Im.js} +3249 -3232
  87. package/lib/assets/{worker-BCcpCNJ7.js → worker-DGvzLstc.js} +9843 -9800
  88. package/lib/zudoku.components.js +174 -161
  89. package/lib/zudoku.openapi-worker.js +12 -12
  90. package/lib/zudoku.plugins.js +3722 -3712
  91. package/package.json +2 -1
  92. package/src/app/App.tsx +9 -7
  93. package/src/lib/authentication/authentication.ts +2 -5
  94. package/src/lib/components/DevPortal.tsx +12 -27
  95. package/src/lib/components/Header.tsx +4 -4
  96. package/src/lib/components/Heading.tsx +26 -7
  97. package/src/lib/components/Layout.tsx +1 -1
  98. package/src/lib/components/context/DevPortalProvider.ts +2 -2
  99. package/src/lib/core/DevPortalContext.ts +38 -10
  100. package/src/lib/core/plugins.ts +10 -5
  101. package/src/lib/oas/graphql/index.ts +2 -2
  102. package/src/lib/oas/parser/index.ts +3 -1
  103. package/src/lib/plugins/markdown/MdxPage.tsx +49 -32
  104. package/src/lib/plugins/markdown/generateRoutes.tsx +12 -2
  105. package/src/lib/plugins/markdown/index.tsx +8 -1
  106. package/src/lib/plugins/openapi/OperationList.tsx +9 -3
  107. package/src/lib/plugins/openapi/OperationListItem.tsx +39 -18
  108. package/src/lib/plugins/openapi/ParameterList.tsx +1 -1
  109. package/src/lib/plugins/openapi/ParameterListItem.tsx +3 -4
  110. package/src/lib/plugins/openapi/{MakeRequest.tsx → PlaygroundDialogWrapper.tsx} +3 -3
  111. package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +2 -17
  112. package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +3 -1
  113. package/src/lib/plugins/openapi/SchemaListView.tsx +15 -182
  114. package/src/lib/plugins/openapi/SchemaListViewItem.tsx +110 -0
  115. package/src/lib/plugins/openapi/SchemaListViewItemGroup.tsx +63 -0
  116. package/src/lib/plugins/openapi/Sidecar.tsx +2 -2
  117. package/src/lib/plugins/openapi/index.tsx +17 -4
  118. package/src/lib/plugins/openapi/playground/Playground.tsx +157 -187
  119. package/src/lib/plugins/openapi/playground/PlaygroundDialog.tsx +34 -0
  120. package/src/lib/plugins/openapi/util/prose.ts +7 -0
  121. package/src/lib/plugins/openapi/worker/worker.ts +27 -1
  122. package/src/lib/util/MdxComponents.tsx +1 -1
  123. package/src/lib/util/objectEntries.ts +5 -0
  124. package/src/lib/util/renderIf.ts +4 -0
  125. package/dist/lib/plugins/openapi/MakeRequest.js.map +0 -1
@@ -1,11 +1,13 @@
1
1
  import { Heading } from "../../components/Heading.js";
2
- import { Markdown, ProseClasses } from "../../components/Markdown.js";
2
+ import { Markdown } from "../../components/Markdown.js";
3
3
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../ui/Tabs.js";
4
+ import { renderIf } from "../../util/renderIf.js";
4
5
  import { OperationsFragment } from "./OperationList.js";
5
6
  import { ParameterList } from "./ParameterList.js";
6
7
  import { SchemaListView } from "./SchemaListView.js";
7
8
  import { Sidecar } from "./Sidecar.js";
8
9
  import { FragmentType, useFragment } from "./graphql/index.js";
10
+ import { SchemaProseClasses } from "./util/prose.js";
9
11
 
10
12
  export const PARAM_GROUPS = ["path", "query", "header", "cookie"] as const;
11
13
  export type ParameterGroup = (typeof PARAM_GROUPS)[number];
@@ -20,24 +22,24 @@ export const OperationListItem = ({
20
22
  operation.parameters ?? [],
21
23
  (param) => param.in,
22
24
  );
23
- const first = operation.responses.find((re) => re.statusCode === "200");
25
+ const first = operation.responses.at(0);
24
26
  return (
25
27
  <div
26
28
  key={operation.operationId}
27
- className="grid grid-cols-2 gap-8 items-start border-b-2 mb-16 pb-16 border-border"
29
+ className="grid grid-cols-[4fr_3fr] gap-8 items-start border-b-2 mb-16 pb-16 border-border"
28
30
  >
29
- <div className={ProseClasses}>
30
- <Heading
31
- level={2}
32
- className="mt-0"
33
- id={operation.slug}
34
- registerSidebarAnchor
35
- >
31
+ <div className="flex flex-col gap-4">
32
+ <Heading level={2} id={operation.slug} registerSidebarAnchor>
36
33
  {operation.summary}
37
34
  </Heading>
38
- {operation.description && <Markdown content={operation.description} />}
35
+ {operation.description && (
36
+ <Markdown
37
+ className={SchemaProseClasses}
38
+ content={operation.description}
39
+ />
40
+ )}
39
41
  {operation.parameters && operation.parameters.length > 0 && (
40
- <div className="mt-4">
42
+ <>
41
43
  {PARAM_GROUPS.flatMap((group) =>
42
44
  groupedParameters[group]?.length ? (
43
45
  <ParameterList
@@ -50,11 +52,22 @@ export const OperationListItem = ({
50
52
  []
51
53
  ),
52
54
  )}
53
- </div>
55
+ </>
54
56
  )}
57
+ {renderIf(operation.requestBody?.content?.at(0)?.schema, (schema) => (
58
+ <div className="mt-4 flex flex-col gap-4">
59
+ <Heading level={3} className="capitalize">
60
+ Request Body
61
+ </Heading>
62
+ <SchemaListView schema={schema} />
63
+ </div>
64
+ ))}
55
65
  {operation.responses.length > 0 && (
56
66
  <>
57
- <Heading level={3} className="capitalize">
67
+ <Heading
68
+ level={3}
69
+ className="capitalize mt-8 pt-8 border-border border-t"
70
+ >
58
71
  Responses
59
72
  </Heading>
60
73
  <Tabs defaultValue={`${first?.statusCode}${first?.description}`}>
@@ -75,10 +88,18 @@ export const OperationListItem = ({
75
88
  value={response.statusCode + response.description}
76
89
  key={response.statusCode}
77
90
  >
78
- <SchemaListView
79
- schema={response.content?.at(0)?.schema}
80
- name=""
81
- />
91
+ {renderIf(
92
+ response.content?.find((content) => content.schema),
93
+ (content) => {
94
+ return (
95
+ <SchemaListView schema={content.schema} name="" />
96
+ );
97
+ },
98
+ ) ?? (
99
+ <div className="border-border font-mono text-sm border rounded p-4">
100
+ No response body
101
+ </div>
102
+ )}
82
103
  </TabsContent>
83
104
  ))}
84
105
  </ul>
@@ -18,7 +18,7 @@ export const ParameterList = ({
18
18
  <Heading level={3} id={`${id}/${group}-parameters`} className="capitalize">
19
19
  {group === "header" ? "Headers" : `${group} Parameters`}
20
20
  </Heading>
21
- <ul className="list-none m-0 px-0 overflow-hidden">
21
+ <ul className="list-none m-0 px-0 overflow-hidden border border-border divide-y divide-border rounded">
22
22
  {parameters.map((parameter) => (
23
23
  <ParameterListItem
24
24
  key={`${parameter.name}-${parameter.in}`}
@@ -28,14 +28,13 @@ export const ParameterListItem = ({
28
28
  group: ParameterGroup;
29
29
  id: string;
30
30
  }) => (
31
- <li className="not-prose px-2 py-4 border-t border-border bg-border/20 text-sm flex flex-col gap-1">
31
+ <li className="p-4 bg-border/20 text-sm flex flex-col gap-1">
32
32
  <div className="flex items-center gap-2">
33
33
  <code>
34
34
  {group === "path" ? (
35
35
  <ColorizedParam
36
36
  name={parameter.name}
37
37
  backgroundOpacity="15%"
38
- className="px-1"
39
38
  slug={id + "-" + parameter.name.toLocaleLowerCase()}
40
39
  />
41
40
  ) : (
@@ -43,12 +42,12 @@ export const ParameterListItem = ({
43
42
  )}
44
43
  </code>
45
44
  {parameter.required && (
46
- <span className="py-px px-1.5 font-medium text-xs bg-primary/75 text-muted rounded-lg">
45
+ <span className="py-px px-1.5 font-medium bg-primary/75 text-muted rounded-lg">
47
46
  required
48
47
  </span>
49
48
  )}
50
49
  {getParameterSchema(parameter).type && (
51
- <span className="text-xs text-muted-foreground">
50
+ <span className="text-muted-foreground">
52
51
  {getParameterSchema(parameter).type}
53
52
  </span>
54
53
  )}
@@ -1,7 +1,7 @@
1
1
  import { graphql } from "./graphql/index.js";
2
2
  import { useOasConfig } from "./index.js";
3
3
  import type { OperationListItemResult } from "./OperationList.js";
4
- import { Playground } from "./playground/Playground.js";
4
+ import { PlaygroundDialog } from "./playground/PlaygroundDialog.js";
5
5
 
6
6
  import { useQuery } from "urql";
7
7
 
@@ -13,7 +13,7 @@ const GetServerQuery = graphql(/* GraphQL */ `
13
13
  }
14
14
  `);
15
15
 
16
- export const MakeRequest = ({
16
+ export const PlaygroundDialogWrapper = ({
17
17
  operation,
18
18
  }: {
19
19
  operation: OperationListItemResult;
@@ -50,7 +50,7 @@ export const MakeRequest = ({
50
50
  false;
51
51
 
52
52
  return (
53
- <Playground
53
+ <PlaygroundDialog
54
54
  host={server.data?.schema.url ?? ""}
55
55
  method={operation.method}
56
56
  url={operation.path}
@@ -1,9 +1,7 @@
1
- import { useState } from "react";
2
1
  import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
3
2
  import { type SchemaObject } from "../../oas/graphql/index.js";
4
3
  import type { OperationListItemResult } from "./OperationList.js";
5
4
  import * as SidecarBox from "./SidecarBox.js";
6
- import { SimpleSelect } from "./SimpleSelect.js";
7
5
  import { generateSchemaExample } from "./util/generateSchemaExample.js";
8
6
 
9
7
  type Content = NonNullable<
@@ -12,24 +10,13 @@ type Content = NonNullable<
12
10
 
13
11
  // @todo should we handle multiple content types?
14
12
  export const RequestBodySidecarBox = ({ content }: { content: Content }) => {
15
- const [selected, setSelected] = useState("example");
16
-
17
13
  if (!content.length) return null;
18
14
 
19
15
  return (
20
16
  <>
21
- <div>lol</div>
22
17
  <SidecarBox.Root>
23
18
  <SidecarBox.Head className="text-xs flex justify-between items-center">
24
- <span className="font-mono">Request Body</span>
25
- <SimpleSelect
26
- value={selected}
27
- onChange={(e) => setSelected(e.target.value)}
28
- options={[
29
- { value: "example", label: "Example" },
30
- { value: "schema", label: "Schema" },
31
- ]}
32
- />
19
+ <span className="font-mono">Request Body Example</span>
33
20
  </SidecarBox.Head>
34
21
  <SidecarBox.Body>
35
22
  <SyntaxHighlight
@@ -38,9 +25,7 @@ export const RequestBodySidecarBox = ({ content }: { content: Content }) => {
38
25
  copyable
39
26
  className="text-xs"
40
27
  code={JSON.stringify(
41
- selected === "example"
42
- ? generateSchemaExample(content[0].schema as SchemaObject)
43
- : content[0].schema,
28
+ generateSchemaExample(content[0].schema as SchemaObject),
44
29
  null,
45
30
  2,
46
31
  )}
@@ -52,7 +52,9 @@ export const ResponsesSidecarBox = ({
52
52
  </span>
53
53
  )}
54
54
  <hr className="border-border my-1" />
55
- <div className="text-xs">{responses[tabIndex].description}</div>
55
+ <div className="text-xs text-muted-foreground">
56
+ {responses[tabIndex].description}
57
+ </div>
56
58
  </SidecarBox.Body>
57
59
  </SidecarBox.Root>
58
60
  );
@@ -1,10 +1,8 @@
1
- import * as Collapsible from "@radix-ui/react-collapsible";
2
- import { ListPlusIcon } from "lucide-react";
3
- import { useState } from "react";
4
1
  import { Markdown } from "../../components/Markdown.js";
5
2
  import { SchemaObject } from "../../oas/parser/index.js";
6
- import { Button } from "../../ui/Button.js";
7
- import { cn } from "../../util/cn.js";
3
+ import { objectEntries } from "../../util/objectEntries.js";
4
+ import { SchemaListViewItemGroup } from "./SchemaListViewItemGroup.js";
5
+ import { SchemaProseClasses } from "./util/prose.js";
8
6
 
9
7
  export const SchemaListView = ({
10
8
  name,
@@ -39,190 +37,25 @@ export const SchemaListView = ({
39
37
  );
40
38
 
41
39
  return (
42
- <div
43
- className={cn(
44
- "not-prose",
45
- level > 0 && "border border-border rounded text-sm",
46
- )}
47
- >
40
+ <div className="flex flex-col gap-2.5">
48
41
  {(schema.title ?? name) && (
49
42
  <div className="ml-2 my-1 font-bold">{schema.title ?? name}</div>
50
43
  )}
51
44
  {level === 0 && schema.description && (
52
- <p className="prose">{schema.description}</p>
53
- )}
54
- <ul>
55
- {Object.entries(groups).map(([group, properties]) => {
56
- return (
57
- <SchemaListViewItemGroup
58
- key={group}
59
- defaultOpen={defaultOpen}
60
- group={group as any}
61
- nestingLevel={level}
62
- properties={properties}
63
- required={schema.required ?? []}
64
- />
65
- );
66
- })}
67
- </ul>
68
- </div>
69
- );
70
- };
71
-
72
- const SchemaListViewItemGroup = ({
73
- group,
74
- properties,
75
- nestingLevel,
76
- required,
77
- defaultOpen = false,
78
- }: {
79
- group: "optional" | "required" | "deprecated";
80
- defaultOpen?: boolean;
81
- properties: [string, SchemaObject][];
82
- nestingLevel: number;
83
- required: string[];
84
- }) => {
85
- const notCollapsible =
86
- defaultOpen ||
87
- group === "required" ||
88
- properties.length === 1 ||
89
- nestingLevel === 0;
90
- const [open, setOpen] = useState(notCollapsible);
91
- if (properties.length === 0) {
92
- return;
93
- }
94
-
95
- return (
96
- <Collapsible.Root
97
- className="CollapsibleRoot"
98
- open={open}
99
- onOpenChange={setOpen}
100
- >
101
- {!open && (
102
- <Collapsible.Trigger
103
- className={cn(
104
- "py-2 hover:bg-muted w-full",
105
- group === "optional" && "font-semibold",
106
- group === "deprecated" && "text-muted-foreground",
107
- )}
108
- >
109
- {properties.length} {group} fields
110
- </Collapsible.Trigger>
45
+ <Markdown className={SchemaProseClasses} content={schema.description} />
111
46
  )}
112
-
113
- <Collapsible.Content>
114
- {properties.map(([propertyName, property]) => (
115
- <SchemaListViewItem
116
- key={propertyName}
117
- property={property}
118
- propertyName={propertyName}
119
- nestingLevel={nestingLevel}
120
- isRequired={required.includes(propertyName)}
47
+ <ul className="border-border border rounded overflow-hidden">
48
+ {objectEntries(groups).map(([group, properties]) => (
49
+ <SchemaListViewItemGroup
50
+ key={group}
51
+ defaultOpen={defaultOpen}
52
+ group={group}
53
+ nestingLevel={level}
54
+ properties={properties ?? []}
55
+ required={schema.required ?? []}
121
56
  />
122
57
  ))}
123
- </Collapsible.Content>
124
- </Collapsible.Root>
125
- );
126
- };
127
-
128
- const SchemaListViewItem = ({
129
- propertyName,
130
- property,
131
- nestingLevel,
132
- isRequired,
133
- }: {
134
- propertyName: string;
135
- isRequired: boolean;
136
- property: SchemaObject;
137
- nestingLevel: number;
138
- }) => {
139
- return (
140
- <div
141
- key={propertyName}
142
- className={cn(
143
- nestingLevel > 0 ? "py-2" : "py-4",
144
- "px-2 border-t border-border bg-border/20 hover:bg-border/30 flex gap-1 flex-col text-sm",
145
- property.deprecated && "opacity-50",
146
- )}
147
- >
148
- <div className="flex items-center gap-2 relative">
149
- <code>
150
- {propertyName} {property.title}
151
- </code>
152
-
153
- {property.type && (
154
- <span className="text-xs text-muted-foreground">{property.type}</span>
155
- )}
156
- {property.deprecated && (
157
- <span className="text-xs text-muted-foreground">Deprecated</span>
158
- )}
159
- {!isRequired && (
160
- <span className="py-px px-1.5 font-medium text-xs border border-border rounded-lg">
161
- optional {property.required}
162
- </span>
163
- )}
164
- </div>
165
- {property.description && (
166
- <Markdown
167
- content={property.description}
168
- className="text-sm leading-normal line-clamp-4 "
169
- />
170
- )}
171
-
172
- {property.enum && (
173
- <span className="text-sm text-muted-foreground flex gap-1 flex-wrap items-center">
174
- <span>Possible values</span>
175
- {property.enum
176
- .filter((value) => value)
177
- .map((value) => (
178
- <span
179
- key={value}
180
- className="font-mono text-xs border-border border bg-muted rounded px-1"
181
- >
182
- {value}
183
- </span>
184
- ))
185
- .slice(0, 4)}
186
- {property.enum.length > 4 && (
187
- <span className="font-mono text-xs border-border border bg-muted rounded px-1">
188
- ...
189
- </span>
190
- )}
191
- </span>
192
- )}
193
-
194
- <Collapsible.Root className="CollapsibleRoot" defaultOpen={false}>
195
- {property.type === "object" ||
196
- (property.type === "array" && property.items.type === "object") ? (
197
- <Collapsible.Trigger asChild>
198
- <Button variant="ghost" size="sm">
199
- Show nested fields
200
- <ListPlusIcon size={18} className="ml-1.5" />
201
- </Button>
202
- </Collapsible.Trigger>
203
- ) : null}
204
-
205
- <Collapsible.Content>
206
- {property.type === "object" && (
207
- <div className="mt-2.5">
208
- <SchemaListView
209
- schema={property}
210
- level={nestingLevel + 1}
211
- defaultOpen
212
- />
213
- </div>
214
- )}
215
- {property.type === "array" && property.items.type === "object" && (
216
- <div className="mt-2.5">
217
- <SchemaListView
218
- schema={property.items}
219
- defaultOpen
220
- level={nestingLevel + 1}
221
- />
222
- </div>
223
- )}
224
- </Collapsible.Content>
225
- </Collapsible.Root>
58
+ </ul>
226
59
  </div>
227
60
  );
228
61
  };
@@ -0,0 +1,110 @@
1
+ import * as Collapsible from "@radix-ui/react-collapsible";
2
+ import { ListPlusIcon } from "lucide-react";
3
+ import { Markdown } from "../../components/Markdown.js";
4
+ import { SchemaObject } from "../../oas/parser/index.js";
5
+ import { Button } from "../../ui/Button.js";
6
+ import { cn } from "../../util/cn.js";
7
+ import { SchemaListView } from "./SchemaListView.js";
8
+
9
+ export const SchemaListViewItem = ({
10
+ propertyName,
11
+ property,
12
+ nestingLevel,
13
+ isRequired,
14
+ }: {
15
+ propertyName: string;
16
+ isRequired: boolean;
17
+ property: SchemaObject;
18
+ nestingLevel: number;
19
+ }) => {
20
+ return (
21
+ <div
22
+ key={propertyName}
23
+ className={cn(
24
+ "p-4 bg-border/20 hover:bg-border/30 flex gap-1 flex-col text-sm",
25
+ property.deprecated && "opacity-50",
26
+ )}
27
+ >
28
+ <div className="flex items-center gap-2 relative">
29
+ <code>
30
+ {propertyName} {property.title}
31
+ </code>
32
+
33
+ {property.type && (
34
+ <span className="text-muted-foreground">{property.type}</span>
35
+ )}
36
+ {property.deprecated && (
37
+ <span className="text-muted-foreground">Deprecated</span>
38
+ )}
39
+ {!isRequired && (
40
+ <span className="py-px px-1.5 font-medium border border-border rounded-lg">
41
+ optional {property.required}
42
+ </span>
43
+ )}
44
+ </div>
45
+ {property.description && (
46
+ <Markdown
47
+ content={property.description}
48
+ className="text-sm leading-normal line-clamp-4 "
49
+ />
50
+ )}
51
+
52
+ {property.enum && (
53
+ <span className="text-sm text-muted-foreground flex gap-1 flex-wrap items-center">
54
+ <span>Possible values</span>
55
+ {/* Make values unique, some schemas have duplicates */}
56
+ {[...new Set(property.enum.filter((value) => value))]
57
+ .map((value) => (
58
+ <span
59
+ key={value}
60
+ className="font-mono text-xs border-border border bg-muted rounded px-1"
61
+ >
62
+ {value}
63
+ </span>
64
+ ))
65
+ .slice(0, 4)}
66
+ {property.enum.length > 4 && (
67
+ <span className="font-mono text-xs border-border border bg-muted rounded px-1">
68
+ ...
69
+ </span>
70
+ )}
71
+ </span>
72
+ )}
73
+
74
+ {(property.type === "object" &&
75
+ (property.properties?.length ??
76
+ Object.entries(property.additionalProperties ?? {}).length > 0)) ||
77
+ (property.type === "array" && property.items.type === "object") ? (
78
+ <Collapsible.Root className="CollapsibleRoot" defaultOpen={false}>
79
+ <Collapsible.Trigger asChild>
80
+ <Button variant="ghost" size="sm">
81
+ Show nested fields
82
+ <ListPlusIcon size={18} className="ml-1.5" />
83
+ </Button>
84
+ </Collapsible.Trigger>
85
+
86
+ <Collapsible.Content>
87
+ {property.type === "object" && (
88
+ <div className="mt-2.5">
89
+ <SchemaListView
90
+ schema={property}
91
+ level={nestingLevel + 1}
92
+ defaultOpen
93
+ />
94
+ </div>
95
+ )}
96
+ {property.type === "array" && property.items.type === "object" && (
97
+ <div className="mt-2.5">
98
+ <SchemaListView
99
+ schema={property.items}
100
+ defaultOpen
101
+ level={nestingLevel + 1}
102
+ />
103
+ </div>
104
+ )}
105
+ </Collapsible.Content>
106
+ </Collapsible.Root>
107
+ ) : null}
108
+ </div>
109
+ );
110
+ };
@@ -0,0 +1,63 @@
1
+ import * as Collapsible from "@radix-ui/react-collapsible";
2
+ import { useState } from "react";
3
+ import { SchemaObject } from "../../oas/parser/index.js";
4
+ import { cn } from "../../util/cn.js";
5
+ import { SchemaListViewItem } from "./SchemaListViewItem.js";
6
+
7
+ export const SchemaListViewItemGroup = ({
8
+ group,
9
+ properties,
10
+ nestingLevel,
11
+ required,
12
+ defaultOpen = false,
13
+ }: {
14
+ group: "optional" | "required" | "deprecated";
15
+ defaultOpen?: boolean;
16
+ properties: [string, SchemaObject][];
17
+ nestingLevel: number;
18
+ required: string[];
19
+ }) => {
20
+ const notCollapsible =
21
+ defaultOpen ||
22
+ group === "required" ||
23
+ properties.length === 1 ||
24
+ nestingLevel === 0;
25
+
26
+ const [open, setOpen] = useState(notCollapsible);
27
+
28
+ if (properties.length === 0) {
29
+ return;
30
+ }
31
+
32
+ return (
33
+ <Collapsible.Root
34
+ className="CollapsibleRoot"
35
+ open={open}
36
+ onOpenChange={setOpen}
37
+ >
38
+ {!open && (
39
+ <Collapsible.Trigger
40
+ className={cn(
41
+ "py-2 hover:bg-muted w-full",
42
+ group === "optional" && "font-semibold",
43
+ group === "deprecated" && "text-muted-foreground",
44
+ )}
45
+ >
46
+ {properties.length} {group} fields
47
+ </Collapsible.Trigger>
48
+ )}
49
+
50
+ <Collapsible.Content className="divide-y divide-border">
51
+ {properties.map(([propertyName, property]) => (
52
+ <SchemaListViewItem
53
+ key={propertyName}
54
+ property={property}
55
+ propertyName={propertyName}
56
+ nestingLevel={nestingLevel}
57
+ isRequired={required.includes(propertyName)}
58
+ />
59
+ ))}
60
+ </Collapsible.Content>
61
+ </Collapsible.Root>
62
+ );
63
+ };
@@ -5,9 +5,9 @@ import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
5
5
  import type { SchemaObject } from "../../oas/parser/index.js";
6
6
  import { cn } from "../../util/cn.js";
7
7
  import { ColorizedParam } from "./ColorizedParam.js";
8
- import { MakeRequest } from "./MakeRequest.js";
9
8
  import { MethodTextColorMap } from "./MethodBadge.js";
10
9
  import type { OperationListItemResult } from "./OperationList.js";
10
+ import { PlaygroundDialogWrapper } from "./PlaygroundDialogWrapper.js";
11
11
  import { RequestBodySidecarBox } from "./RequestBodySidecarBox.js";
12
12
  import { ResponsesSidecarBox } from "./ResponsesSidecarBox.js";
13
13
  import * as SidecarBox from "./SidecarBox.js";
@@ -142,7 +142,7 @@ export const Sidecar = ({
142
142
  { value: "swift", label: "Swift" },
143
143
  ]}
144
144
  />
145
- <MakeRequest operation={operation} />
145
+ <PlaygroundDialogWrapper operation={operation} />
146
146
  </div>
147
147
  </SidecarBox.Head>
148
148
  <SidecarBox.Body>
@@ -15,7 +15,6 @@ import {
15
15
  } from "./util/urql.js";
16
16
 
17
17
  import { createSharedWorkerClient } from "virtual:zudoku-openapi-worker";
18
- import type { createSharedWorkerClient as createSharedWorkerClientType } from "./worker/createSharedWorkerClient.js";
19
18
 
20
19
  const OasContext = createContext<{ config: OasPluginConfig } | undefined>(
21
20
  undefined,
@@ -85,11 +84,25 @@ export const openApiPlugin = (config: OasPluginConfig): DevPortalPlugin => {
85
84
  url: config.server,
86
85
  exchanges: [cacheExchange, fetchExchange],
87
86
  })
88
- : (createSharedWorkerClient() as ReturnType<
89
- typeof createSharedWorkerClientType
90
- >);
87
+ : createSharedWorkerClient();
91
88
 
92
89
  return {
90
+ getHead: () => {
91
+ if (config.type === "url") {
92
+ return (
93
+ <link
94
+ rel="preload"
95
+ href={config.input}
96
+ as="fetch"
97
+ crossOrigin="anonymous"
98
+ />
99
+ );
100
+ }
101
+
102
+ if (config.server) {
103
+ return <link rel="preconnect" href={config.server} />;
104
+ }
105
+ },
93
106
  getNavigation: async (path: string) => {
94
107
  if (!matchPath({ path: basePath, end: false }, path)) {
95
108
  return [];