@kingsleyweb/bun-ui 0.0.0-stage → 3.0.0-rc.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 (240) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -2
  3. package/app/docs/App.tsx +72 -0
  4. package/app/docs/api/client.ts +303 -0
  5. package/app/docs/api/contract.ts +24 -0
  6. package/app/docs/api/docs.ts +38 -0
  7. package/app/docs/api/errors.ts +144 -0
  8. package/app/docs/boot.tsx +58 -0
  9. package/app/docs/components/Badge.tsx +65 -0
  10. package/app/docs/components/Button.tsx +37 -0
  11. package/app/docs/components/Card.tsx +41 -0
  12. package/app/docs/components/ConfirmDialog.tsx +173 -0
  13. package/app/docs/components/CopyButton.tsx +68 -0
  14. package/app/docs/components/Dialog.tsx +230 -0
  15. package/app/docs/components/EmptyState.tsx +34 -0
  16. package/app/docs/components/ErrorView.tsx +70 -0
  17. package/app/docs/components/Field.tsx +93 -0
  18. package/app/docs/components/JsonEditor.tsx +151 -0
  19. package/app/docs/components/JsonView.tsx +338 -0
  20. package/app/docs/components/KeyValue.tsx +63 -0
  21. package/app/docs/components/ProblemBanner.tsx +116 -0
  22. package/app/docs/components/ScreenErrorBoundary.tsx +141 -0
  23. package/app/docs/components/Spinner.tsx +33 -0
  24. package/app/docs/components/Table.tsx +33 -0
  25. package/app/docs/components/Tabs.tsx +226 -0
  26. package/app/docs/components/classNames.ts +4 -0
  27. package/app/docs/components/components.css +691 -0
  28. package/app/docs/components/copy.ts +89 -0
  29. package/app/docs/components/fieldContext.ts +29 -0
  30. package/app/docs/components/inputValues.ts +142 -0
  31. package/app/docs/components/inputs.tsx +387 -0
  32. package/app/docs/components/jsonParse.ts +338 -0
  33. package/app/docs/components/listKeys.ts +30 -0
  34. package/app/docs/config.ts +86 -0
  35. package/app/docs/context.ts +33 -0
  36. package/app/docs/embed.ts +74 -0
  37. package/app/docs/env.d.ts +2 -0
  38. package/app/docs/format.ts +9 -0
  39. package/app/docs/hooks/useUrlTab.ts +16 -0
  40. package/app/docs/main.tsx +11 -0
  41. package/app/docs/queryClient.ts +54 -0
  42. package/app/docs/router.tsx +194 -0
  43. package/app/docs/routing.ts +194 -0
  44. package/app/docs/screens/docs/document.ts +27 -0
  45. package/app/docs/screens/docs/http/OperationView.tsx +407 -0
  46. package/app/docs/screens/docs/http/Sidebar.tsx +190 -0
  47. package/app/docs/screens/docs/http/TryItPanel.tsx +603 -0
  48. package/app/docs/screens/docs/http/http.css +405 -0
  49. package/app/docs/screens/docs/http/index.tsx +395 -0
  50. package/app/docs/screens/docs/http/model.ts +604 -0
  51. package/app/docs/screens/docs/http/tryIt.ts +455 -0
  52. package/app/docs/screens/docs/permissions.ts +6 -0
  53. package/app/docs/screens/docs/schema/Prose.tsx +43 -0
  54. package/app/docs/screens/docs/schema/SchemaTree.tsx +319 -0
  55. package/app/docs/screens/docs/schema/index.ts +30 -0
  56. package/app/docs/screens/docs/schema/resolve.ts +466 -0
  57. package/app/docs/screens/docs/schema/schema.css +137 -0
  58. package/app/docs/screens/docs/schema/skeleton.ts +92 -0
  59. package/app/docs/screens/docs/viewer.tsx +154 -0
  60. package/app/docs/screens/docs/ws/InlineText.tsx +25 -0
  61. package/app/docs/screens/docs/ws/PayloadSchema.tsx +39 -0
  62. package/app/docs/screens/docs/ws/format.ts +82 -0
  63. package/app/docs/screens/docs/ws/index.tsx +286 -0
  64. package/app/docs/screens/docs/ws/items.tsx +786 -0
  65. package/app/docs/screens/docs/ws/model.ts +1178 -0
  66. package/app/docs/screens/docs/ws/panels.tsx +440 -0
  67. package/app/docs/screens/docs/ws/ws.css +231 -0
  68. package/app/docs/shortcuts.ts +94 -0
  69. package/app/docs/styles/base.css +847 -0
  70. package/app/docs/styles/tokens.css +143 -0
  71. package/app/docs/theme.ts +56 -0
  72. package/app/kit/unauthorized.ts +222 -0
  73. package/dist/docs/assets/main-0rchxtv5.js +18 -0
  74. package/dist/docs/assets/main-0rchxtv5.js.map +99 -0
  75. package/dist/docs/assets/main-cqgyvs86.css +1 -0
  76. package/dist/docs/manifest.json +26 -0
  77. package/dist/jobs/assets/AddFlowDialog-h6xkgx9b.js +6 -0
  78. package/dist/jobs/assets/AddFlowDialog-h6xkgx9b.js.map +11 -0
  79. package/dist/jobs/assets/AddFlowDialog-n7gtc84v.css +1 -0
  80. package/dist/jobs/assets/added-cbwsxv3e.js +4 -0
  81. package/dist/jobs/assets/added-cbwsxv3e.js.map +11 -0
  82. package/dist/jobs/assets/bodies-yyw5hmrp.js +6 -0
  83. package/dist/jobs/assets/bodies-yyw5hmrp.js.map +10 -0
  84. package/dist/jobs/assets/channel-tgkvqm8y.js +4 -0
  85. package/dist/jobs/assets/channel-tgkvqm8y.js.map +10 -0
  86. package/dist/jobs/assets/channelChecks-s4yz4yhw.js +4 -0
  87. package/dist/jobs/assets/channelChecks-s4yz4yhw.js.map +10 -0
  88. package/dist/jobs/assets/index-02427smp.css +1 -0
  89. package/dist/jobs/assets/index-09z2cjft.js +4 -0
  90. package/dist/jobs/assets/index-09z2cjft.js.map +16 -0
  91. package/dist/jobs/assets/index-18bn0gvy.css +1 -0
  92. package/dist/jobs/assets/index-47pxmyxn.js +4 -0
  93. package/dist/jobs/assets/index-47pxmyxn.js.map +10 -0
  94. package/dist/jobs/assets/index-4e55z986.js +4 -0
  95. package/dist/jobs/assets/index-4e55z986.js.map +11 -0
  96. package/dist/jobs/assets/index-5c933424.js +4 -0
  97. package/dist/jobs/assets/index-5c933424.js.map +18 -0
  98. package/dist/jobs/assets/index-5kbyqspm.css +1 -0
  99. package/dist/jobs/assets/index-5ng37hk6.css +1 -0
  100. package/dist/jobs/assets/index-5w8w7fvc.css +1 -0
  101. package/dist/jobs/assets/index-71qs5qyx.css +1 -0
  102. package/dist/jobs/assets/index-btpg4r8b.css +1 -0
  103. package/dist/jobs/assets/index-d5r5n0mm.js +4 -0
  104. package/dist/jobs/assets/index-d5r5n0mm.js.map +23 -0
  105. package/dist/jobs/assets/index-e0yy2skq.css +1 -0
  106. package/dist/jobs/assets/index-hs60b25v.js +10 -0
  107. package/dist/jobs/assets/index-hs60b25v.js.map +62 -0
  108. package/dist/jobs/assets/index-k2smrr70.js +4 -0
  109. package/dist/jobs/assets/index-k2smrr70.js.map +9 -0
  110. package/dist/jobs/assets/index-q24jw3w9.js +4 -0
  111. package/dist/jobs/assets/index-q24jw3w9.js.map +32 -0
  112. package/dist/jobs/assets/index-qfb80xen.css +1 -0
  113. package/dist/jobs/assets/index-rtynx6wj.js +4 -0
  114. package/dist/jobs/assets/index-rtynx6wj.js.map +10 -0
  115. package/dist/jobs/assets/live-6k7s6358.js +4 -0
  116. package/dist/jobs/assets/live-6k7s6358.js.map +10 -0
  117. package/dist/jobs/assets/main-0jmeh6mq.js +6 -0
  118. package/dist/jobs/assets/main-0jmeh6mq.js.map +12 -0
  119. package/dist/jobs/assets/main-3yj6bv90.js +5 -0
  120. package/dist/jobs/assets/main-3yj6bv90.js.map +10 -0
  121. package/dist/jobs/assets/main-4g69fn3x.js +5 -0
  122. package/dist/jobs/assets/main-4g69fn3x.js.map +10 -0
  123. package/dist/jobs/assets/main-4gte3eqq.js +5 -0
  124. package/dist/jobs/assets/main-4gte3eqq.js.map +10 -0
  125. package/dist/jobs/assets/main-4j1fcevh.js +5 -0
  126. package/dist/jobs/assets/main-4j1fcevh.js.map +10 -0
  127. package/dist/jobs/assets/main-54qwkz28.css +1 -0
  128. package/dist/jobs/assets/main-5grjvsy7.js +5 -0
  129. package/dist/jobs/assets/main-5grjvsy7.js.map +11 -0
  130. package/dist/jobs/assets/main-5zbx60gq.js +5 -0
  131. package/dist/jobs/assets/main-5zbx60gq.js.map +11 -0
  132. package/dist/jobs/assets/main-9prgjhg4.js +5 -0
  133. package/dist/jobs/assets/main-9prgjhg4.js.map +10 -0
  134. package/dist/jobs/assets/main-a250ffk7.js +17 -0
  135. package/dist/jobs/assets/main-a250ffk7.js.map +115 -0
  136. package/dist/jobs/assets/main-d6ge7w08.js +5 -0
  137. package/dist/jobs/assets/main-d6ge7w08.js.map +10 -0
  138. package/dist/jobs/assets/main-gngzht43.js +5 -0
  139. package/dist/jobs/assets/main-gngzht43.js.map +10 -0
  140. package/dist/jobs/assets/main-hg0hrg8j.js +5 -0
  141. package/dist/jobs/assets/main-hg0hrg8j.js.map +10 -0
  142. package/dist/jobs/assets/main-hm1ng3yg.js +5 -0
  143. package/dist/jobs/assets/main-hm1ng3yg.js.map +12 -0
  144. package/dist/jobs/assets/main-jkvd92z9.js +5 -0
  145. package/dist/jobs/assets/main-jkvd92z9.js.map +10 -0
  146. package/dist/jobs/assets/main-jv5xqd13.js +5 -0
  147. package/dist/jobs/assets/main-jv5xqd13.js.map +21 -0
  148. package/dist/jobs/assets/main-jxre7w93.js +5 -0
  149. package/dist/jobs/assets/main-jxre7w93.js.map +23 -0
  150. package/dist/jobs/assets/main-kwmqkjfx.js +5 -0
  151. package/dist/jobs/assets/main-kwmqkjfx.js.map +11 -0
  152. package/dist/jobs/assets/main-n9cf6bxm.js +5 -0
  153. package/dist/jobs/assets/main-n9cf6bxm.js.map +11 -0
  154. package/dist/jobs/assets/main-pegp0ygb.js +5 -0
  155. package/dist/jobs/assets/main-pegp0ygb.js.map +10 -0
  156. package/dist/jobs/assets/main-qr573853.js +5 -0
  157. package/dist/jobs/assets/main-qr573853.js.map +12 -0
  158. package/dist/jobs/assets/main-rq97rdr4.js +4 -0
  159. package/dist/jobs/assets/main-rq97rdr4.js.map +22 -0
  160. package/dist/jobs/assets/main-szjdmydc.js +5 -0
  161. package/dist/jobs/assets/main-szjdmydc.js.map +13 -0
  162. package/dist/jobs/assets/main-tnbhrm9n.js +7 -0
  163. package/dist/jobs/assets/main-tnbhrm9n.js.map +11 -0
  164. package/dist/jobs/assets/main-ts19j4n0.js +5 -0
  165. package/dist/jobs/assets/main-ts19j4n0.js.map +25 -0
  166. package/dist/jobs/assets/main-w93x1dzs.js +5 -0
  167. package/dist/jobs/assets/main-w93x1dzs.js.map +10 -0
  168. package/dist/jobs/assets/sections-0ksdxs8m.css +1 -0
  169. package/dist/jobs/assets/sections-0y7w4fb4.js +4 -0
  170. package/dist/jobs/assets/sections-0y7w4fb4.js.map +10 -0
  171. package/dist/jobs/assets/sse-mn0t0fh7.js +5 -0
  172. package/dist/jobs/assets/sse-mn0t0fh7.js.map +11 -0
  173. package/dist/jobs/assets/sseChecks-t7tcm7ra.js +4 -0
  174. package/dist/jobs/assets/sseChecks-t7tcm7ra.js.map +10 -0
  175. package/dist/jobs/assets/validation-ysjfy072.js +4 -0
  176. package/dist/jobs/assets/validation-ysjfy072.js.map +9 -0
  177. package/dist/jobs/manifest.json +511 -0
  178. package/dts/docs/assets.d.ts +144 -0
  179. package/dts/docs/assets.d.ts.map +1 -0
  180. package/dts/docs/browser/config.d.ts +61 -0
  181. package/dts/docs/browser/config.d.ts.map +1 -0
  182. package/dts/docs/browser/index.d.ts +12 -0
  183. package/dts/docs/browser/index.d.ts.map +1 -0
  184. package/dts/docs/docsUi.d.ts +42 -0
  185. package/dts/docs/docsUi.d.ts.map +1 -0
  186. package/dts/docs/hosted.d.ts +22 -0
  187. package/dts/docs/hosted.d.ts.map +1 -0
  188. package/dts/docs/index.d.ts +18 -0
  189. package/dts/docs/index.d.ts.map +1 -0
  190. package/dts/docs/shell.d.ts +57 -0
  191. package/dts/docs/shell.d.ts.map +1 -0
  192. package/dts/docs/types.d.ts +137 -0
  193. package/dts/docs/types.d.ts.map +1 -0
  194. package/dts/index.d.ts +21 -0
  195. package/dts/index.d.ts.map +1 -0
  196. package/dts/jobs/assets.d.ts +157 -0
  197. package/dts/jobs/assets.d.ts.map +1 -0
  198. package/dts/jobs/browser/config.d.ts +67 -0
  199. package/dts/jobs/browser/config.d.ts.map +1 -0
  200. package/dts/jobs/browser/index.d.ts +10 -0
  201. package/dts/jobs/browser/index.d.ts.map +1 -0
  202. package/dts/jobs/browser/unauthorized.d.ts +9 -0
  203. package/dts/jobs/browser/unauthorized.d.ts.map +1 -0
  204. package/dts/jobs/index.d.ts +24 -0
  205. package/dts/jobs/index.d.ts.map +1 -0
  206. package/dts/jobs/jobsUi.d.ts +63 -0
  207. package/dts/jobs/jobsUi.d.ts.map +1 -0
  208. package/dts/jobs/shell.d.ts +77 -0
  209. package/dts/jobs/shell.d.ts.map +1 -0
  210. package/dts/jobs/types.d.ts +146 -0
  211. package/dts/jobs/types.d.ts.map +1 -0
  212. package/dts/kit/browser/shared.d.ts +10 -0
  213. package/dts/kit/browser/shared.d.ts.map +1 -0
  214. package/dts/kit/browser/unauthorized.d.ts +74 -0
  215. package/dts/kit/browser/unauthorized.d.ts.map +1 -0
  216. package/dts/kit/unauthorized.d.ts +47 -0
  217. package/dts/kit/unauthorized.d.ts.map +1 -0
  218. package/guides/docs.md +170 -0
  219. package/guides/jobs.md +1120 -0
  220. package/lib/docs/assets.ts +427 -0
  221. package/lib/docs/browser/config.ts +66 -0
  222. package/lib/docs/browser/index.ts +24 -0
  223. package/lib/docs/docsUi.ts +623 -0
  224. package/lib/docs/hosted.ts +31 -0
  225. package/lib/docs/index.ts +37 -0
  226. package/lib/docs/shell.ts +156 -0
  227. package/lib/docs/types.ts +149 -0
  228. package/lib/index.ts +27 -0
  229. package/lib/jobs/assets.ts +448 -0
  230. package/lib/jobs/browser/config.ts +65 -0
  231. package/lib/jobs/browser/index.ts +9 -0
  232. package/lib/jobs/browser/unauthorized.ts +11 -0
  233. package/lib/jobs/index.ts +41 -0
  234. package/lib/jobs/jobsUi.ts +709 -0
  235. package/lib/jobs/shell.ts +205 -0
  236. package/lib/jobs/types.ts +162 -0
  237. package/lib/kit/browser/shared.ts +11 -0
  238. package/lib/kit/browser/unauthorized.ts +114 -0
  239. package/lib/kit/unauthorized.ts +222 -0
  240. package/package.json +198 -4
@@ -0,0 +1,395 @@
1
+ /** The HTTP API reference (OpenAPI 3.1): `/http` and `/http/:operationId` under the mount. */
2
+ import type { DocsClient } from "../../../api/client";
3
+ import type { SpecDocument } from "../../../api/docs";
4
+ import type { PermissionProbe } from "../permissions";
5
+ import type { DocInfo, DocSection, DocTagGroup } from "./model";
6
+ import { useMemo, useState } from "react";
7
+ import { createDocsClient } from "../../../api/client";
8
+ import { Badge } from "../../../components/Badge";
9
+ import { Card } from "../../../components/Card";
10
+ import { CopyButton } from "../../../components/CopyButton";
11
+ import { Dialog } from "../../../components/Dialog";
12
+ import { EmptyState } from "../../../components/EmptyState";
13
+ import {
14
+ DocsClientContext,
15
+ useDocsFetch,
16
+ useDocsUiConfig,
17
+ } from "../../../context";
18
+ import { Link } from "../../../router";
19
+ import { useParams, useQueryParam } from "../../../routing";
20
+ import { Prose } from "../schema/Prose";
21
+ import { refName } from "../schema/resolve";
22
+ import { SchemaTree } from "../schema/SchemaTree";
23
+ import {
24
+ componentSchemaNames,
25
+ filterGroups,
26
+ groupBySection,
27
+ keyedSections,
28
+ groupByTag,
29
+ listOperations,
30
+ readInfo,
31
+ resolveServerUrl,
32
+ } from "./model";
33
+ import { OperationView } from "./OperationView";
34
+ import { Sidebar } from "./Sidebar";
35
+ import "./http.css";
36
+
37
+ /** The app path of an operation, keeping a search; `base` is the docs section's path within the host app ("" for a standalone viewer). */
38
+ function operationHref(operationId: string, query: string, base = ""): string {
39
+ const search = query ? `?q=${encodeURIComponent(query)}` : "";
40
+ return `${base}/http/${encodeURIComponent(operationId)}${search}`;
41
+ }
42
+
43
+ /** The document's title, version, description and servers. */
44
+ function InfoHeader({
45
+ info,
46
+ serverBase,
47
+ }: {
48
+ /** The header. */
49
+ info: DocInfo;
50
+ /** The resolved URL requests are sent to. */
51
+ serverBase: string;
52
+ }) {
53
+ const config = useDocsUiConfig();
54
+ const origin = typeof window === "undefined" ? "" : window.location.origin;
55
+ return (
56
+ <Card className="http-info">
57
+ <div className="http-info-line">
58
+ <Badge tone="accent">{`OpenAPI ${info.openapi}`}</Badge>
59
+ {info.version && <Badge tone="neutral">{`v${info.version}`}</Badge>}
60
+ </div>
61
+ {info.description && (
62
+ <div className="http-description">
63
+ <Prose text={info.description} />
64
+ </div>
65
+ )}
66
+ <dl
67
+ className="http-servers"
68
+ data-testid="http-servers"
69
+ >
70
+ {info.servers.map((server) => {
71
+ const resolved = resolveServerUrl(
72
+ server.url,
73
+ config.documentUrl,
74
+ origin,
75
+ );
76
+ return (
77
+ <div key={server.url}>
78
+ <dt>
79
+ Server <code>{server.url}</code>
80
+ {server.description ? ` (${server.description})` : ""}
81
+ </dt>
82
+ <dd>
83
+ <code data-testid="http-server-url">{resolved}</code>
84
+ <CopyButton
85
+ text={resolved}
86
+ ariaLabel="Copy the server URL"
87
+ />
88
+ </dd>
89
+ </div>
90
+ );
91
+ })}
92
+ <div>
93
+ <dt>Requests are sent to</dt>
94
+ <dd>
95
+ <code data-testid="http-request-base">{serverBase}</code>
96
+ </dd>
97
+ </div>
98
+ </dl>
99
+ <p className="http-muted">Paths below are relative to the server.</p>
100
+ </Card>
101
+ );
102
+ }
103
+
104
+ /** One tag's description and its operations' links, on the landing view. */
105
+ function OverviewTag({
106
+ group,
107
+ query,
108
+ pathBase,
109
+ }: {
110
+ /** The tag and its operations. */
111
+ group: DocTagGroup;
112
+ /** The search, kept on links. */
113
+ query: string;
114
+ /** The docs section's path within the host app. */
115
+ pathBase: string;
116
+ }) {
117
+ return (
118
+ <>
119
+ {group.description && <p className="http-muted">{group.description}</p>}
120
+ <ul className="http-overview-ops">
121
+ {group.operations.map((operation) => (
122
+ <li key={operation.id}>
123
+ <Link to={operationHref(operation.id, query, pathBase)}>
124
+ <code>{operation.method}</code> {operation.path}
125
+ </Link>
126
+ {operation.summary && (
127
+ <span className="http-muted">{` ${operation.summary}`}</span>
128
+ )}
129
+ </li>
130
+ ))}
131
+ </ul>
132
+ </>
133
+ );
134
+ }
135
+
136
+ /** The landing view: tags (in their sections, when the document has them) with their operations, and the component schemas. */
137
+ function Overview({
138
+ doc,
139
+ groups,
140
+ sections,
141
+ query,
142
+ onRef,
143
+ pathBase,
144
+ }: {
145
+ /** The document. */
146
+ doc: SpecDocument;
147
+ /** Every tag group (unfiltered). */
148
+ groups: readonly DocTagGroup[];
149
+ /** The document's sections over them (`x-tagGroups`), when it declares any: one card each, its tags inside. */
150
+ sections: readonly DocSection[] | undefined;
151
+ /** The search, kept on links. */
152
+ query: string;
153
+ /** Opens a component schema. */
154
+ onRef: (ref: string, name: string) => void;
155
+ /** The docs section's path within the host app. */
156
+ pathBase: string;
157
+ }) {
158
+ const names = componentSchemaNames(doc);
159
+ return (
160
+ <div className="http-overview">
161
+ {sections === undefined
162
+ ? groups.map((group) => (
163
+ <Card
164
+ key={group.name}
165
+ title={group.name}
166
+ >
167
+ <OverviewTag
168
+ group={group}
169
+ query={query}
170
+ pathBase={pathBase}
171
+ />
172
+ </Card>
173
+ ))
174
+ : keyedSections(sections).map(({ key, section }) => (
175
+ <Card
176
+ key={key}
177
+ title={section.name}
178
+ className="http-overview-section"
179
+ >
180
+ {section.tags.map((group) => (
181
+ <section
182
+ key={group.name}
183
+ className="http-overview-tag"
184
+ aria-label={group.name}
185
+ >
186
+ <h3 className="http-overview-tag-name">{group.name}</h3>
187
+ <OverviewTag
188
+ group={group}
189
+ query={query}
190
+ pathBase={pathBase}
191
+ />
192
+ </section>
193
+ ))}
194
+ </Card>
195
+ ))}
196
+ {names.length > 0 && (
197
+ <Card title="Schemas">
198
+ <ul
199
+ className="http-components"
200
+ data-testid="http-components"
201
+ >
202
+ {names.map((name) => (
203
+ <li
204
+ key={name}
205
+ id={`schema-${name}`}
206
+ >
207
+ <button
208
+ type="button"
209
+ className="schema-ref schema-ref-link"
210
+ onClick={() => onRef(`#/components/schemas/${name}`, name)}
211
+ >
212
+ {name}
213
+ </button>
214
+ </li>
215
+ ))}
216
+ </ul>
217
+ </Card>
218
+ )}
219
+ </div>
220
+ );
221
+ }
222
+
223
+ /** The side panel showing one component schema; a `$ref` inside it replaces it. */
224
+ function SchemaPanel({
225
+ doc,
226
+ refValue,
227
+ onClose,
228
+ onRef,
229
+ }: {
230
+ /** The document. */
231
+ doc: SpecDocument;
232
+ /** The component shown, or `null` when closed. */
233
+ refValue: string | null;
234
+ /** Closes it. */
235
+ onClose: () => void;
236
+ /** Shows another component. */
237
+ onRef: (ref: string, name: string) => void;
238
+ }) {
239
+ return (
240
+ <Dialog
241
+ open={refValue !== null}
242
+ onClose={onClose}
243
+ title={refValue === null ? "" : refName(refValue)}
244
+ description={refValue ?? undefined}
245
+ size="lg"
246
+ className="http-schema-panel"
247
+ >
248
+ {refValue !== null && (
249
+ <SchemaTree
250
+ key={refValue}
251
+ schema={{ $ref: refValue }}
252
+ root={doc}
253
+ depth={2}
254
+ onRef={onRef}
255
+ label={`Schema ${refName(refValue)}`}
256
+ />
257
+ )}
258
+ </Dialog>
259
+ );
260
+ }
261
+
262
+ /** Props of {@link HttpReference}. */
263
+ export interface HttpReferenceProps {
264
+ /** The OpenAPI document. */
265
+ doc: SpecDocument;
266
+ /** The extension prefixes to read; defaults to the configured ones. */
267
+ extensions?: readonly string[];
268
+ /** The client the try-it panel sends with. Default: built on the document's resolved first server. */
269
+ client?: DocsClient;
270
+ /** Answers "does the caller hold this permission" (the have/lack badge and the try-it gate). */
271
+ probe?: PermissionProbe;
272
+ /** Whether the API is read-only. Defaults to `false`. */
273
+ readOnly?: boolean;
274
+ /** After a successful mutation (the host refreshes what it shows). */
275
+ onChanged?: () => void;
276
+ /** The docs section's path within the host app ("" for a standalone viewer). */
277
+ pathBase?: string;
278
+ }
279
+
280
+ /** The loaded reference: sidebar, info or operation, and the schema panel. */
281
+ export function HttpReference({
282
+ doc,
283
+ extensions,
284
+ client: clientOverride,
285
+ probe,
286
+ readOnly = false,
287
+ onChanged,
288
+ pathBase = "",
289
+ }: HttpReferenceProps) {
290
+ const config = useDocsUiConfig();
291
+ const prefixes = extensions ?? config.extensions;
292
+ const { operationId } = useParams<{ operationId?: string }>();
293
+ const [query, setQuery] = useQueryParam("q");
294
+ const [panel, setPanel] = useState<string | null>(null);
295
+ const info = useMemo(() => readInfo(doc), [doc]);
296
+ const operations = useMemo(
297
+ () => listOperations(doc, { extensions: prefixes }),
298
+ [doc, prefixes],
299
+ );
300
+ const groups = useMemo(() => groupByTag(doc, operations), [doc, operations]);
301
+ const visible = useMemo(() => filterGroups(groups, query), [groups, query]);
302
+ // `undefined` without `x-tagGroups`: the sidebar is then one level, as before.
303
+ const sections = useMemo(() => groupBySection(doc, groups), [doc, groups]);
304
+ const visibleSections = useMemo(
305
+ () => groupBySection(doc, visible),
306
+ [doc, visible],
307
+ );
308
+ const selected =
309
+ operationId === undefined
310
+ ? undefined
311
+ : operations.find((operation) => operation.id === operationId);
312
+ const openRef = (ref: string) => setPanel(ref);
313
+
314
+ // The try-it panel and the snippets send to the document's first server,
315
+ // resolved against the origin the document was fetched from.
316
+ const origin = typeof window === "undefined" ? "" : window.location.origin;
317
+ const serverBase = resolveServerUrl(
318
+ info.servers[0]?.url ?? "/",
319
+ config.documentUrl,
320
+ origin,
321
+ );
322
+ const fetcher = useDocsFetch();
323
+ const client = useMemo(
324
+ () =>
325
+ clientOverride ??
326
+ createDocsClient({
327
+ base: serverBase,
328
+ csrfHeader: config.csrfHeader,
329
+ fetch: fetcher,
330
+ }),
331
+ [clientOverride, serverBase, config.csrfHeader, fetcher],
332
+ );
333
+
334
+ return (
335
+ <div
336
+ className="screen http-docs"
337
+ data-testid="http-docs"
338
+ >
339
+ <div className="http-docs-title">
340
+ <h1 className="screen-title">{info.title}</h1>
341
+ </div>
342
+ <div className="http-docs-layout">
343
+ <Sidebar
344
+ groups={visible}
345
+ sections={visibleSections}
346
+ query={query}
347
+ onQuery={(next) => setQuery(next)}
348
+ hrefOf={(id) => operationHref(id, query, pathBase)}
349
+ />
350
+ <div className="http-docs-main">
351
+ {operationId === undefined ? (
352
+ <>
353
+ <InfoHeader
354
+ info={info}
355
+ serverBase={serverBase}
356
+ />
357
+ <Overview
358
+ doc={doc}
359
+ groups={groups}
360
+ sections={sections}
361
+ query={query}
362
+ onRef={openRef}
363
+ pathBase={pathBase}
364
+ />
365
+ </>
366
+ ) : selected ? (
367
+ <DocsClientContext value={client}>
368
+ <OperationView
369
+ key={selected.id}
370
+ operation={selected}
371
+ root={doc}
372
+ onRef={openRef}
373
+ probe={probe}
374
+ readOnly={readOnly}
375
+ onChanged={onChanged}
376
+ />
377
+ </DocsClientContext>
378
+ ) : (
379
+ <EmptyState
380
+ title="No such operation"
381
+ description={`This document has no operation "${operationId}".`}
382
+ action={<Link to={`${pathBase}/http`}>All operations</Link>}
383
+ />
384
+ )}
385
+ </div>
386
+ </div>
387
+ <SchemaPanel
388
+ doc={doc}
389
+ refValue={panel}
390
+ onClose={() => setPanel(null)}
391
+ onRef={openRef}
392
+ />
393
+ </div>
394
+ );
395
+ }