@ciderpress/ui 1.0.0-rc.1

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 (428) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -0
  3. package/dist/0~142.js +2 -0
  4. package/dist/0~164.js +2 -0
  5. package/dist/0~2.js +2 -0
  6. package/dist/0~225.js +2 -0
  7. package/dist/0~288.js +2 -0
  8. package/dist/0~299.js +2 -0
  9. package/dist/0~3.js +2 -0
  10. package/dist/0~301.js +2 -0
  11. package/dist/0~311.js +2 -0
  12. package/dist/0~326.js +2 -0
  13. package/dist/0~40.js +2 -0
  14. package/dist/0~43.js +2 -0
  15. package/dist/0~433.js +2 -0
  16. package/dist/0~453.js +2 -0
  17. package/dist/0~457.js +2 -0
  18. package/dist/0~497.js +2 -0
  19. package/dist/0~511.js +2 -0
  20. package/dist/0~603.js +2 -0
  21. package/dist/0~616.js +2 -0
  22. package/dist/0~659.js +2 -0
  23. package/dist/0~670.js +2 -0
  24. package/dist/0~687.js +2 -0
  25. package/dist/0~706.js +2 -0
  26. package/dist/0~729.js +2 -0
  27. package/dist/0~768.js +2 -0
  28. package/dist/0~819.js +2 -0
  29. package/dist/0~830.js +2 -0
  30. package/dist/0~854.js +2 -0
  31. package/dist/0~863.js +2 -0
  32. package/dist/0~879.js +2 -0
  33. package/dist/0~96.js +2 -0
  34. package/dist/0~97.js +2 -0
  35. package/dist/components/FileTree/FileTree.css +90 -0
  36. package/dist/components/FileTree/FileTree.d.ts +7 -0
  37. package/dist/components/FileTree/FileTree.js +531 -0
  38. package/dist/components/FileTree/FileTreeItem.d.ts +8 -0
  39. package/dist/components/FileTree/RemoteSvgIcon.d.ts +11 -0
  40. package/dist/components/FileTree/index.d.ts +4 -0
  41. package/dist/components/folder-icons.d.ts +32 -0
  42. package/dist/components/languages.d.ts +7 -0
  43. package/dist/components/tree-parser/tree-parser.d.ts +12 -0
  44. package/dist/components/tree-parser/types.d.ts +11 -0
  45. package/dist/head/css/loader-apple.css +1 -0
  46. package/dist/head/css/loader-backdrop.css +1 -0
  47. package/dist/head/css/loader-dots.css +1 -0
  48. package/dist/head/css/themes/amber.css +1 -0
  49. package/dist/head/css/themes/arcade.css +1 -0
  50. package/dist/head/css/themes/grannysmith.css +1 -0
  51. package/dist/head/css/themes/honeycrisp.css +1 -0
  52. package/dist/head/css/themes/midnight.css +1 -0
  53. package/dist/head/css/themes/mulled.css +1 -0
  54. package/dist/head/js/loader-dots.js +1 -0
  55. package/dist/head/js/vscode-nav.js +1 -0
  56. package/dist/index.d.ts +43 -0
  57. package/dist/index.mjs +40 -0
  58. package/dist/node.d.ts +81 -0
  59. package/dist/node.mjs +17762 -0
  60. package/dist/plugins/mermaid/MermaidRenderer.tsx +480 -0
  61. package/dist/plugins/mermaid/mermaid.css +199 -0
  62. package/dist/theme/components/announcement/announcement-bar.css +103 -0
  63. package/dist/theme/components/announcement/announcement-bar.tsx +173 -0
  64. package/dist/theme/components/ask-ai/ask-ai-button.css +52 -0
  65. package/dist/theme/components/ask-ai/ask-ai-button.tsx +56 -0
  66. package/dist/theme/components/content-footer/content-footer-portal.tsx +70 -0
  67. package/dist/theme/components/content-footer/feedback.css +47 -0
  68. package/dist/theme/components/content-footer/feedback.tsx +96 -0
  69. package/dist/theme/components/content-footer/meta-actions.css +25 -0
  70. package/dist/theme/components/content-footer/meta-actions.tsx +57 -0
  71. package/dist/theme/components/content-footer/page-pager.css +61 -0
  72. package/dist/theme/components/content-footer/page-pager.tsx +110 -0
  73. package/dist/theme/components/edit-source-button.tsx +97 -0
  74. package/dist/theme/components/footer/site-footer.css +179 -0
  75. package/dist/theme/components/footer/site-footer.tsx +115 -0
  76. package/dist/theme/components/home/cta.css +67 -0
  77. package/dist/theme/components/home/cta.tsx +77 -0
  78. package/dist/theme/components/home/feature-card.css +52 -0
  79. package/dist/theme/components/home/feature-card.tsx +131 -0
  80. package/dist/theme/components/home/feature.tsx +79 -0
  81. package/dist/theme/components/home/hero-demo.css +66 -0
  82. package/dist/theme/components/home/hero-demo.tsx +46 -0
  83. package/dist/theme/components/home/hero.css +96 -0
  84. package/dist/theme/components/home/hero.tsx +111 -0
  85. package/dist/theme/components/home/layout.tsx +183 -0
  86. package/dist/theme/components/home/page-rail.css +21 -0
  87. package/dist/theme/components/home/page-rail.tsx +19 -0
  88. package/dist/theme/components/home/split.css +148 -0
  89. package/dist/theme/components/home/split.tsx +120 -0
  90. package/dist/theme/components/home/trust-strip.css +33 -0
  91. package/dist/theme/components/home/trust-strip.tsx +43 -0
  92. package/dist/theme/components/home/workspaces.tsx +71 -0
  93. package/dist/theme/components/nav/branch-tag.css +54 -0
  94. package/dist/theme/components/nav/branch-tag.tsx +101 -0
  95. package/dist/theme/components/nav/ciderpress-doc-layout.tsx +247 -0
  96. package/dist/theme/components/nav/ciderpress-docs-bar.css +505 -0
  97. package/dist/theme/components/nav/ciderpress-docs-bar.tsx +426 -0
  98. package/dist/theme/components/nav/ciderpress-header.css +172 -0
  99. package/dist/theme/components/nav/ciderpress-header.tsx +113 -0
  100. package/dist/theme/components/nav/ciderpress-layout-context.tsx +49 -0
  101. package/dist/theme/components/nav/ciderpress-nav-hamburger.css +193 -0
  102. package/dist/theme/components/nav/ciderpress-nav-hamburger.tsx +112 -0
  103. package/dist/theme/components/nav/ciderpress-nav-menu.css +130 -0
  104. package/dist/theme/components/nav/ciderpress-nav-menu.tsx +372 -0
  105. package/dist/theme/components/nav/ciderpress-nav-search.css +73 -0
  106. package/dist/theme/components/nav/ciderpress-nav-search.tsx +55 -0
  107. package/dist/theme/components/nav/ciderpress-nav-social-links.css +35 -0
  108. package/dist/theme/components/nav/ciderpress-nav-social-links.tsx +87 -0
  109. package/dist/theme/components/nav/floating-branch-indicator.css +106 -0
  110. package/dist/theme/components/nav/floating-branch-indicator.tsx +109 -0
  111. package/dist/theme/components/nav/layout.tsx +268 -0
  112. package/dist/theme/components/nav/mobile-nav-cta.tsx +79 -0
  113. package/dist/theme/components/nav/nav-divider.css +15 -0
  114. package/dist/theme/components/nav/nav-divider.tsx +19 -0
  115. package/dist/theme/components/nav/nav-logo.css +42 -0
  116. package/dist/theme/components/nav/nav-logo.tsx +262 -0
  117. package/dist/theme/components/nav/theme-switcher.css +92 -0
  118. package/dist/theme/components/nav/theme-switcher.tsx +298 -0
  119. package/dist/theme/components/nav/topbar-cta.css +45 -0
  120. package/dist/theme/components/nav/topbar-cta.tsx +39 -0
  121. package/dist/theme/components/nav/variant-toggle.css +70 -0
  122. package/dist/theme/components/nav/variant-toggle.tsx +83 -0
  123. package/dist/theme/components/nav/version-chip.css +31 -0
  124. package/dist/theme/components/nav/version-chip.tsx +45 -0
  125. package/dist/theme/components/nav/vscode-tag.css +32 -0
  126. package/dist/theme/components/nav/vscode-tag.tsx +20 -0
  127. package/dist/theme/components/openapi/code-sample.tsx +341 -0
  128. package/dist/theme/components/openapi/copy-markdown-button.tsx +41 -0
  129. package/dist/theme/components/openapi/icons.tsx +45 -0
  130. package/dist/theme/components/openapi/index.ts +6 -0
  131. package/dist/theme/components/openapi/method-badge.tsx +24 -0
  132. package/dist/theme/components/openapi/openapi.css +665 -0
  133. package/dist/theme/components/openapi/operation.tsx +200 -0
  134. package/dist/theme/components/openapi/overview.tsx +207 -0
  135. package/dist/theme/components/openapi/parameters-table.tsx +149 -0
  136. package/dist/theme/components/openapi/request-body.tsx +90 -0
  137. package/dist/theme/components/openapi/response-list.tsx +112 -0
  138. package/dist/theme/components/openapi/schema-viewer.tsx +277 -0
  139. package/dist/theme/components/openapi/security-badges.tsx +63 -0
  140. package/dist/theme/components/openapi/spec-utils.ts +122 -0
  141. package/dist/theme/components/shared/accordion.css +118 -0
  142. package/dist/theme/components/shared/accordion.stories.tsx +96 -0
  143. package/dist/theme/components/shared/accordion.tsx +191 -0
  144. package/dist/theme/components/shared/brand-logo.stories.tsx +49 -0
  145. package/dist/theme/components/shared/brand-mark.stories.tsx +53 -0
  146. package/dist/theme/components/shared/browser-window.css +1 -0
  147. package/dist/theme/components/shared/browser-window.stories.tsx +100 -0
  148. package/dist/theme/components/shared/browser-window.tsx +2 -0
  149. package/dist/theme/components/shared/card.stories.tsx +50 -0
  150. package/dist/theme/components/shared/card.tsx +35 -0
  151. package/dist/theme/components/shared/ciderpress-logo.tsx +93 -0
  152. package/dist/theme/components/shared/ciderpress-mark.tsx +77 -0
  153. package/dist/theme/components/shared/color.css +56 -0
  154. package/dist/theme/components/shared/color.stories.tsx +47 -0
  155. package/dist/theme/components/shared/color.tsx +69 -0
  156. package/dist/theme/components/shared/columns.css +37 -0
  157. package/dist/theme/components/shared/columns.stories.tsx +91 -0
  158. package/dist/theme/components/shared/columns.tsx +50 -0
  159. package/dist/theme/components/shared/desktop-window.css +339 -0
  160. package/dist/theme/components/shared/desktop-window.tsx +535 -0
  161. package/dist/theme/components/shared/field.css +158 -0
  162. package/dist/theme/components/shared/field.stories.tsx +100 -0
  163. package/dist/theme/components/shared/field.tsx +186 -0
  164. package/dist/theme/components/shared/frame.css +39 -0
  165. package/dist/theme/components/shared/frame.stories.tsx +49 -0
  166. package/dist/theme/components/shared/frame.tsx +76 -0
  167. package/dist/theme/components/shared/icon.stories.tsx +90 -0
  168. package/dist/theme/components/shared/icon.tsx +41 -0
  169. package/dist/theme/components/shared/prompt.css +178 -0
  170. package/dist/theme/components/shared/prompt.stories.tsx +57 -0
  171. package/dist/theme/components/shared/prompt.tsx +369 -0
  172. package/dist/theme/components/shared/resolve-card-icon.ts +28 -0
  173. package/dist/theme/components/shared/section-card.stories.tsx +51 -0
  174. package/dist/theme/components/shared/section-card.tsx +44 -0
  175. package/dist/theme/components/shared/section-grid.stories.tsx +46 -0
  176. package/dist/theme/components/shared/section-grid.tsx +15 -0
  177. package/dist/theme/components/shared/status-badge.css +37 -0
  178. package/dist/theme/components/shared/status-badge.stories.tsx +61 -0
  179. package/dist/theme/components/shared/status-badge.tsx +74 -0
  180. package/dist/theme/components/shared/steps.css +88 -0
  181. package/dist/theme/components/shared/steps.stories.tsx +68 -0
  182. package/dist/theme/components/shared/steps.tsx +100 -0
  183. package/dist/theme/components/shared/tech-icon-table.stories.tsx +29 -0
  184. package/dist/theme/components/shared/tech-icon-table.tsx +71 -0
  185. package/dist/theme/components/shared/tech-tag.stories.tsx +40 -0
  186. package/dist/theme/components/shared/tech-tag.tsx +31 -0
  187. package/dist/theme/components/shared/theme-color-palette.css +80 -0
  188. package/dist/theme/components/shared/theme-color-palette.stories.tsx +52 -0
  189. package/dist/theme/components/shared/theme-color-palette.tsx +275 -0
  190. package/dist/theme/components/shared/tooltip.css +69 -0
  191. package/dist/theme/components/shared/tooltip.stories.tsx +72 -0
  192. package/dist/theme/components/shared/tooltip.tsx +77 -0
  193. package/dist/theme/components/sidebar/framework-picker.css +77 -0
  194. package/dist/theme/components/sidebar/framework-picker.tsx +78 -0
  195. package/dist/theme/components/sidebar/sidebar-filter.test.ts +140 -0
  196. package/dist/theme/components/sidebar/sidebar-filter.ts +79 -0
  197. package/dist/theme/components/sidebar/sidebar-links.css +102 -0
  198. package/dist/theme/components/sidebar/sidebar-links.tsx +122 -0
  199. package/dist/theme/components/sidebar/sidebar-promo.css +42 -0
  200. package/dist/theme/components/sidebar/sidebar-promo.tsx +52 -0
  201. package/dist/theme/components/sidebar/sidebar-scope.tsx +151 -0
  202. package/dist/theme/components/sidebar/sidebar-toggle.css +181 -0
  203. package/dist/theme/components/sidebar/sidebar-toggle.tsx +127 -0
  204. package/dist/theme/components/theme-provider.tsx +637 -0
  205. package/dist/theme/components/workspaces/card.css +151 -0
  206. package/dist/theme/components/workspaces/card.tsx +166 -0
  207. package/dist/theme/components/workspaces/grid.tsx +62 -0
  208. package/dist/theme/css.d.ts +1 -0
  209. package/dist/theme/env.d.ts +12 -0
  210. package/dist/theme/fonts/GeistMono-Variable.woff2 +0 -0
  211. package/dist/theme/fonts/GeistPixel-Square.woff2 +0 -0
  212. package/dist/theme/fonts/GeistSans-Variable.woff2 +0 -0
  213. package/dist/theme/hooks/use-ciderpress.ts +61 -0
  214. package/dist/theme/hooks/use-nav-items.ts +76 -0
  215. package/dist/theme/icons/index.ts +2 -0
  216. package/dist/theme/icons/tech-map.ts +228 -0
  217. package/dist/theme/index.tsx +180 -0
  218. package/dist/theme/lib/read-social-links.ts +27 -0
  219. package/dist/theme/lib/route-link.tsx +126 -0
  220. package/dist/theme/lib/safe-url.test.ts +0 -0
  221. package/dist/theme/lib/safe-url.ts +68 -0
  222. package/dist/theme/lib/theme-favicon.ts +125 -0
  223. package/dist/theme/lib/user-config-stub.ts +12 -0
  224. package/dist/theme/styles/layers.css +32 -0
  225. package/dist/theme/styles/overrides/badge.css +26 -0
  226. package/dist/theme/styles/overrides/content-footer.css +80 -0
  227. package/dist/theme/styles/overrides/details.css +61 -0
  228. package/dist/theme/styles/overrides/fonts.css +32 -0
  229. package/dist/theme/styles/overrides/home-card.css +133 -0
  230. package/dist/theme/styles/overrides/home.css +243 -0
  231. package/dist/theme/styles/overrides/rail.css +337 -0
  232. package/dist/theme/styles/overrides/rspress.css +62 -0
  233. package/dist/theme/styles/overrides/scrollbar.css +25 -0
  234. package/dist/theme/styles/overrides/section-card.css +114 -0
  235. package/dist/theme/styles/overrides/sidebar.css +144 -0
  236. package/dist/theme/styles/overrides/tokens.css +288 -0
  237. package/dist/theme/styles/overrides/vscode.css +88 -0
  238. package/dist/theme/styles/themes/amber.css +545 -0
  239. package/dist/theme/styles/themes/arcade-fx.css +366 -0
  240. package/dist/theme/styles/themes/arcade.css +273 -0
  241. package/dist/theme/styles/themes/grannysmith.css +545 -0
  242. package/dist/theme/styles/themes/honeycrisp.css +545 -0
  243. package/dist/theme/styles/themes/midnight.css +273 -0
  244. package/dist/theme/styles/themes/mulled.css +817 -0
  245. package/package.json +103 -0
  246. package/src/theme/components/announcement/announcement-bar.css +103 -0
  247. package/src/theme/components/announcement/announcement-bar.tsx +173 -0
  248. package/src/theme/components/ask-ai/ask-ai-button.css +52 -0
  249. package/src/theme/components/ask-ai/ask-ai-button.tsx +56 -0
  250. package/src/theme/components/content-footer/content-footer-portal.tsx +70 -0
  251. package/src/theme/components/content-footer/feedback.css +47 -0
  252. package/src/theme/components/content-footer/feedback.tsx +96 -0
  253. package/src/theme/components/content-footer/meta-actions.css +25 -0
  254. package/src/theme/components/content-footer/meta-actions.tsx +57 -0
  255. package/src/theme/components/content-footer/page-pager.css +61 -0
  256. package/src/theme/components/content-footer/page-pager.tsx +110 -0
  257. package/src/theme/components/edit-source-button.tsx +97 -0
  258. package/src/theme/components/footer/site-footer.css +179 -0
  259. package/src/theme/components/footer/site-footer.tsx +115 -0
  260. package/src/theme/components/home/cta.css +67 -0
  261. package/src/theme/components/home/cta.tsx +77 -0
  262. package/src/theme/components/home/feature-card.css +52 -0
  263. package/src/theme/components/home/feature-card.tsx +131 -0
  264. package/src/theme/components/home/feature.tsx +79 -0
  265. package/src/theme/components/home/hero-demo.css +66 -0
  266. package/src/theme/components/home/hero-demo.tsx +46 -0
  267. package/src/theme/components/home/hero.css +96 -0
  268. package/src/theme/components/home/hero.tsx +111 -0
  269. package/src/theme/components/home/layout.tsx +183 -0
  270. package/src/theme/components/home/page-rail.css +21 -0
  271. package/src/theme/components/home/page-rail.tsx +19 -0
  272. package/src/theme/components/home/split.css +148 -0
  273. package/src/theme/components/home/split.tsx +120 -0
  274. package/src/theme/components/home/trust-strip.css +33 -0
  275. package/src/theme/components/home/trust-strip.tsx +43 -0
  276. package/src/theme/components/home/workspaces.tsx +71 -0
  277. package/src/theme/components/nav/branch-tag.css +54 -0
  278. package/src/theme/components/nav/branch-tag.tsx +101 -0
  279. package/src/theme/components/nav/ciderpress-doc-layout.tsx +247 -0
  280. package/src/theme/components/nav/ciderpress-docs-bar.css +505 -0
  281. package/src/theme/components/nav/ciderpress-docs-bar.tsx +426 -0
  282. package/src/theme/components/nav/ciderpress-header.css +172 -0
  283. package/src/theme/components/nav/ciderpress-header.tsx +113 -0
  284. package/src/theme/components/nav/ciderpress-layout-context.tsx +49 -0
  285. package/src/theme/components/nav/ciderpress-nav-hamburger.css +193 -0
  286. package/src/theme/components/nav/ciderpress-nav-hamburger.tsx +112 -0
  287. package/src/theme/components/nav/ciderpress-nav-menu.css +130 -0
  288. package/src/theme/components/nav/ciderpress-nav-menu.tsx +372 -0
  289. package/src/theme/components/nav/ciderpress-nav-search.css +73 -0
  290. package/src/theme/components/nav/ciderpress-nav-search.tsx +55 -0
  291. package/src/theme/components/nav/ciderpress-nav-social-links.css +35 -0
  292. package/src/theme/components/nav/ciderpress-nav-social-links.tsx +87 -0
  293. package/src/theme/components/nav/floating-branch-indicator.css +106 -0
  294. package/src/theme/components/nav/floating-branch-indicator.tsx +109 -0
  295. package/src/theme/components/nav/layout.tsx +268 -0
  296. package/src/theme/components/nav/mobile-nav-cta.tsx +79 -0
  297. package/src/theme/components/nav/nav-divider.css +15 -0
  298. package/src/theme/components/nav/nav-divider.tsx +19 -0
  299. package/src/theme/components/nav/nav-logo.css +42 -0
  300. package/src/theme/components/nav/nav-logo.tsx +262 -0
  301. package/src/theme/components/nav/theme-switcher.css +92 -0
  302. package/src/theme/components/nav/theme-switcher.tsx +298 -0
  303. package/src/theme/components/nav/topbar-cta.css +45 -0
  304. package/src/theme/components/nav/topbar-cta.tsx +39 -0
  305. package/src/theme/components/nav/variant-toggle.css +70 -0
  306. package/src/theme/components/nav/variant-toggle.tsx +83 -0
  307. package/src/theme/components/nav/version-chip.css +31 -0
  308. package/src/theme/components/nav/version-chip.tsx +45 -0
  309. package/src/theme/components/nav/vscode-tag.css +32 -0
  310. package/src/theme/components/nav/vscode-tag.tsx +20 -0
  311. package/src/theme/components/openapi/code-sample.tsx +341 -0
  312. package/src/theme/components/openapi/copy-markdown-button.tsx +41 -0
  313. package/src/theme/components/openapi/icons.tsx +45 -0
  314. package/src/theme/components/openapi/index.ts +6 -0
  315. package/src/theme/components/openapi/method-badge.tsx +24 -0
  316. package/src/theme/components/openapi/openapi.css +665 -0
  317. package/src/theme/components/openapi/operation.tsx +200 -0
  318. package/src/theme/components/openapi/overview.tsx +207 -0
  319. package/src/theme/components/openapi/parameters-table.tsx +149 -0
  320. package/src/theme/components/openapi/request-body.tsx +90 -0
  321. package/src/theme/components/openapi/response-list.tsx +112 -0
  322. package/src/theme/components/openapi/schema-viewer.tsx +277 -0
  323. package/src/theme/components/openapi/security-badges.tsx +63 -0
  324. package/src/theme/components/openapi/spec-utils.ts +122 -0
  325. package/src/theme/components/shared/accordion.css +118 -0
  326. package/src/theme/components/shared/accordion.stories.tsx +96 -0
  327. package/src/theme/components/shared/accordion.tsx +191 -0
  328. package/src/theme/components/shared/brand-logo.stories.tsx +49 -0
  329. package/src/theme/components/shared/brand-mark.stories.tsx +53 -0
  330. package/src/theme/components/shared/browser-window.css +1 -0
  331. package/src/theme/components/shared/browser-window.stories.tsx +100 -0
  332. package/src/theme/components/shared/browser-window.tsx +2 -0
  333. package/src/theme/components/shared/card.stories.tsx +50 -0
  334. package/src/theme/components/shared/card.tsx +35 -0
  335. package/src/theme/components/shared/ciderpress-logo.tsx +93 -0
  336. package/src/theme/components/shared/ciderpress-mark.tsx +77 -0
  337. package/src/theme/components/shared/color.css +56 -0
  338. package/src/theme/components/shared/color.stories.tsx +47 -0
  339. package/src/theme/components/shared/color.tsx +69 -0
  340. package/src/theme/components/shared/columns.css +37 -0
  341. package/src/theme/components/shared/columns.stories.tsx +91 -0
  342. package/src/theme/components/shared/columns.tsx +50 -0
  343. package/src/theme/components/shared/desktop-window.css +339 -0
  344. package/src/theme/components/shared/desktop-window.tsx +535 -0
  345. package/src/theme/components/shared/field.css +158 -0
  346. package/src/theme/components/shared/field.stories.tsx +100 -0
  347. package/src/theme/components/shared/field.tsx +186 -0
  348. package/src/theme/components/shared/frame.css +39 -0
  349. package/src/theme/components/shared/frame.stories.tsx +49 -0
  350. package/src/theme/components/shared/frame.tsx +76 -0
  351. package/src/theme/components/shared/icon.stories.tsx +90 -0
  352. package/src/theme/components/shared/icon.tsx +41 -0
  353. package/src/theme/components/shared/prompt.css +178 -0
  354. package/src/theme/components/shared/prompt.stories.tsx +57 -0
  355. package/src/theme/components/shared/prompt.tsx +369 -0
  356. package/src/theme/components/shared/resolve-card-icon.ts +28 -0
  357. package/src/theme/components/shared/section-card.stories.tsx +51 -0
  358. package/src/theme/components/shared/section-card.tsx +44 -0
  359. package/src/theme/components/shared/section-grid.stories.tsx +46 -0
  360. package/src/theme/components/shared/section-grid.tsx +15 -0
  361. package/src/theme/components/shared/status-badge.css +37 -0
  362. package/src/theme/components/shared/status-badge.stories.tsx +61 -0
  363. package/src/theme/components/shared/status-badge.tsx +74 -0
  364. package/src/theme/components/shared/steps.css +88 -0
  365. package/src/theme/components/shared/steps.stories.tsx +68 -0
  366. package/src/theme/components/shared/steps.tsx +100 -0
  367. package/src/theme/components/shared/tech-icon-table.stories.tsx +29 -0
  368. package/src/theme/components/shared/tech-icon-table.tsx +71 -0
  369. package/src/theme/components/shared/tech-tag.stories.tsx +40 -0
  370. package/src/theme/components/shared/tech-tag.tsx +31 -0
  371. package/src/theme/components/shared/theme-color-palette.css +80 -0
  372. package/src/theme/components/shared/theme-color-palette.stories.tsx +52 -0
  373. package/src/theme/components/shared/theme-color-palette.tsx +275 -0
  374. package/src/theme/components/shared/tooltip.css +69 -0
  375. package/src/theme/components/shared/tooltip.stories.tsx +72 -0
  376. package/src/theme/components/shared/tooltip.tsx +77 -0
  377. package/src/theme/components/sidebar/framework-picker.css +77 -0
  378. package/src/theme/components/sidebar/framework-picker.tsx +78 -0
  379. package/src/theme/components/sidebar/sidebar-filter.test.ts +140 -0
  380. package/src/theme/components/sidebar/sidebar-filter.ts +79 -0
  381. package/src/theme/components/sidebar/sidebar-links.css +102 -0
  382. package/src/theme/components/sidebar/sidebar-links.tsx +122 -0
  383. package/src/theme/components/sidebar/sidebar-promo.css +42 -0
  384. package/src/theme/components/sidebar/sidebar-promo.tsx +52 -0
  385. package/src/theme/components/sidebar/sidebar-scope.tsx +151 -0
  386. package/src/theme/components/sidebar/sidebar-toggle.css +181 -0
  387. package/src/theme/components/sidebar/sidebar-toggle.tsx +127 -0
  388. package/src/theme/components/theme-provider.tsx +637 -0
  389. package/src/theme/components/workspaces/card.css +151 -0
  390. package/src/theme/components/workspaces/card.tsx +166 -0
  391. package/src/theme/components/workspaces/grid.tsx +62 -0
  392. package/src/theme/css.d.ts +1 -0
  393. package/src/theme/env.d.ts +12 -0
  394. package/src/theme/fonts/GeistMono-Variable.woff2 +0 -0
  395. package/src/theme/fonts/GeistPixel-Square.woff2 +0 -0
  396. package/src/theme/fonts/GeistSans-Variable.woff2 +0 -0
  397. package/src/theme/hooks/use-ciderpress.ts +61 -0
  398. package/src/theme/hooks/use-nav-items.ts +76 -0
  399. package/src/theme/icons/index.ts +2 -0
  400. package/src/theme/icons/tech-map.ts +228 -0
  401. package/src/theme/index.tsx +180 -0
  402. package/src/theme/lib/read-social-links.ts +27 -0
  403. package/src/theme/lib/route-link.tsx +126 -0
  404. package/src/theme/lib/safe-url.test.ts +0 -0
  405. package/src/theme/lib/safe-url.ts +68 -0
  406. package/src/theme/lib/theme-favicon.ts +125 -0
  407. package/src/theme/lib/user-config-stub.ts +12 -0
  408. package/src/theme/styles/layers.css +32 -0
  409. package/src/theme/styles/overrides/badge.css +26 -0
  410. package/src/theme/styles/overrides/content-footer.css +80 -0
  411. package/src/theme/styles/overrides/details.css +61 -0
  412. package/src/theme/styles/overrides/fonts.css +32 -0
  413. package/src/theme/styles/overrides/home-card.css +133 -0
  414. package/src/theme/styles/overrides/home.css +243 -0
  415. package/src/theme/styles/overrides/rail.css +337 -0
  416. package/src/theme/styles/overrides/rspress.css +62 -0
  417. package/src/theme/styles/overrides/scrollbar.css +25 -0
  418. package/src/theme/styles/overrides/section-card.css +114 -0
  419. package/src/theme/styles/overrides/sidebar.css +144 -0
  420. package/src/theme/styles/overrides/tokens.css +288 -0
  421. package/src/theme/styles/overrides/vscode.css +88 -0
  422. package/src/theme/styles/themes/amber.css +545 -0
  423. package/src/theme/styles/themes/arcade-fx.css +366 -0
  424. package/src/theme/styles/themes/arcade.css +273 -0
  425. package/src/theme/styles/themes/grannysmith.css +545 -0
  426. package/src/theme/styles/themes/honeycrisp.css +545 -0
  427. package/src/theme/styles/themes/midnight.css +273 -0
  428. package/src/theme/styles/themes/mulled.css +817 -0
@@ -0,0 +1,200 @@
1
+ import { match, P } from 'massaman/match'
2
+ import type React from 'react'
3
+
4
+ import { CodeSample } from './code-sample'
5
+ import { MethodBadge } from './method-badge'
6
+ import { ParametersTable } from './parameters-table'
7
+ import { RequestBody } from './request-body'
8
+ import { ResponseList } from './response-list'
9
+ import { SecurityBadges } from './security-badges'
10
+ import { resolveBaseUrl, resolveOperation, resolveSecurities } from './spec-utils'
11
+
12
+ export interface OpenAPIOperationProps {
13
+ /**
14
+ * Parsed OpenAPI spec object.
15
+ */
16
+ readonly spec: Record<string, unknown>
17
+ /**
18
+ * HTTP method (get, post, put, patch, delete).
19
+ */
20
+ readonly method: string
21
+ /**
22
+ * URL path (e.g. /users/{id}).
23
+ */
24
+ readonly path: string
25
+ /**
26
+ * Operation ID for identification.
27
+ */
28
+ readonly operationId: string
29
+ /**
30
+ * Pre-rendered markdown for the SSG-MD pass.
31
+ * When provided and `import.meta.env.SSG_MD` is true, this string
32
+ * is rendered instead of the interactive UI.
33
+ */
34
+ readonly markdown?: string
35
+ }
36
+
37
+ /**
38
+ * Main OpenAPI operation page component.
39
+ *
40
+ * Two-column layout: left column shows spec details (header,
41
+ * parameters, request body, responses, security), right column
42
+ * shows auto-generated code examples.
43
+ *
44
+ * @param props - Operation props with spec, method, path, and operationId
45
+ * @returns React element with operation details and code examples
46
+ */
47
+ export function OpenAPIOperation({
48
+ spec,
49
+ method,
50
+ path,
51
+ operationId,
52
+ markdown,
53
+ }: OpenAPIOperationProps): React.ReactElement {
54
+ if (import.meta.env.SSG_MD && markdown) {
55
+ return <>{markdown}</>
56
+ }
57
+
58
+ const operation = resolveOperation({ spec, path, method })
59
+
60
+ return match(operation)
61
+ .with(P.nonNullable, (op) => {
62
+ const parameters = (op['parameters'] ?? []) as readonly Record<string, unknown>[]
63
+ const requestBody = op['requestBody'] as Record<string, unknown> | undefined
64
+ const responses = (op['responses'] ?? {}) as Record<string, unknown>
65
+ const summary = op['summary'] as string | undefined
66
+ const description = op['description'] as string | undefined
67
+ const deprecated = op['deprecated'] === true
68
+ const securities = resolveSecurities({ operation: op, spec })
69
+ const baseUrl = resolveBaseUrl(spec)
70
+
71
+ const displaySummary: string | undefined = match(summary)
72
+ .with(P.nonNullable, (s) => s)
73
+ .otherwise(() =>
74
+ match(description)
75
+ .with(P.nonNullable, (d) => d as string | undefined)
76
+ .otherwise(() => undefined as string | undefined)
77
+ )
78
+
79
+ const parametersEl = match(parameters)
80
+ .with(
81
+ P.when((p): p is readonly Record<string, unknown>[] => p.length > 0),
82
+ (p) => <ParametersTable parameters={p} />
83
+ )
84
+ .otherwise(() => null)
85
+
86
+ const requestBodyEl = match(requestBody)
87
+ .with(P.nonNullable, (rb) => <RequestBody requestBody={rb} />)
88
+ .otherwise(() => null)
89
+
90
+ const responsesEl = match(Object.keys(responses))
91
+ .with(
92
+ P.when((k): k is string[] => k.length > 0),
93
+ () => <ResponseList responses={responses} />
94
+ )
95
+ .otherwise(() => null)
96
+
97
+ const securityEl = match(securities)
98
+ .with(
99
+ P.when((s): s is readonly Record<string, unknown>[] => s.length > 0),
100
+ (s) => <SecurityBadges securities={s} />
101
+ )
102
+ .otherwise(() => null)
103
+
104
+ return (
105
+ <div className="cp-oas-operation">
106
+ <div className="cp-oas-operation-spec">
107
+ <OperationHeader
108
+ method={method}
109
+ path={path}
110
+ operationId={operationId}
111
+ summary={displaySummary}
112
+ deprecated={deprecated}
113
+ />
114
+ {parametersEl}
115
+ {requestBodyEl}
116
+ {responsesEl}
117
+ {securityEl}
118
+ </div>
119
+ <div className="cp-oas-operation-examples">
120
+ <CodeSample
121
+ method={method}
122
+ path={path}
123
+ baseUrl={baseUrl}
124
+ parameters={parameters}
125
+ requestBody={requestBody}
126
+ />
127
+ </div>
128
+ </div>
129
+ )
130
+ })
131
+ .otherwise(() => <NotFound method={method} path={path} />)
132
+ }
133
+
134
+ /**
135
+ * Render the operation header with method badge, path, and optional deprecation tag.
136
+ *
137
+ * @private
138
+ * @param props - Header props including method, path, operationId, summary, and deprecated flag
139
+ * @returns Header element
140
+ */
141
+ function OperationHeader({
142
+ method,
143
+ path,
144
+ operationId,
145
+ summary,
146
+ deprecated,
147
+ }: {
148
+ readonly method: string
149
+ readonly path: string
150
+ readonly operationId: string
151
+ readonly summary: string | undefined
152
+ readonly deprecated: boolean
153
+ }): React.ReactElement {
154
+ const summaryEl = match(summary)
155
+ .with(P.nonNullable, (s) => <div className="cp-oas-operation-summary">{s}</div>)
156
+ .otherwise(() => null)
157
+
158
+ const deprecatedEl = match(deprecated)
159
+ .with(true, () => <span className="cp-oas-operation-deprecated">Deprecated</span>)
160
+ .otherwise(() => null)
161
+
162
+ return (
163
+ <div>
164
+ <div className="cp-oas-operation-header">
165
+ <MethodBadge method={method} />
166
+ <span className="cp-oas-operation-header__path">{path}</span>
167
+ {deprecatedEl}
168
+ </div>
169
+ <div className="cp-oas-operation-header__id">{operationId}</div>
170
+ {summaryEl}
171
+ </div>
172
+ )
173
+ }
174
+
175
+ /**
176
+ * Render a fallback when the operation is not found in the spec.
177
+ *
178
+ * @private
179
+ * @param props - Props with method and path
180
+ * @returns Not-found element
181
+ */
182
+ function NotFound({
183
+ method,
184
+ path,
185
+ }: {
186
+ readonly method: string
187
+ readonly path: string
188
+ }): React.ReactElement {
189
+ return (
190
+ <div className="cp-oas-operation">
191
+ <div className="cp-oas-operation-spec">
192
+ <div className="cp-oas-operation-header">
193
+ <MethodBadge method={method} />
194
+ <span className="cp-oas-operation-header__path">{path}</span>
195
+ </div>
196
+ <div className="cp-oas-operation-summary">Operation not found in the provided spec.</div>
197
+ </div>
198
+ </div>
199
+ )
200
+ }
@@ -0,0 +1,207 @@
1
+ import { countBy } from 'massaman/array'
2
+ import { identity } from 'massaman/function'
3
+ import { match, P } from 'massaman/match'
4
+ import type React from 'react'
5
+ import { useMemo } from 'react'
6
+
7
+ import { LockIcon } from './icons'
8
+ import { HTTP_METHODS } from './spec-utils'
9
+
10
+ export interface OpenAPIOverviewProps {
11
+ /**
12
+ * Parsed OpenAPI spec object.
13
+ */
14
+ readonly spec: Record<string, unknown>
15
+ /**
16
+ * Pre-rendered markdown for the SSG-MD pass.
17
+ * When provided and `import.meta.env.SSG_MD` is true, this string
18
+ * is rendered instead of the interactive UI.
19
+ */
20
+ readonly markdown?: string
21
+ }
22
+
23
+ interface TagInfo {
24
+ readonly name: string
25
+ readonly description: string
26
+ readonly operationCount: number
27
+ }
28
+
29
+ /**
30
+ * API overview page component.
31
+ *
32
+ * Renders the API title, version, description, server URLs,
33
+ * authentication schemes, and tag groups with operation counts.
34
+ *
35
+ * @param props - Overview props with parsed spec
36
+ * @returns React element with full API overview
37
+ */
38
+ export function OpenAPIOverview({ spec, markdown }: OpenAPIOverviewProps): React.ReactElement {
39
+ if (import.meta.env.SSG_MD && markdown) {
40
+ return <>{markdown}</>
41
+ }
42
+
43
+ const info = (spec['info'] ?? {}) as Record<string, unknown>
44
+ const description = info['description'] as string | undefined
45
+ const servers = (spec['servers'] ?? []) as readonly Record<string, unknown>[]
46
+ const components = (spec['components'] ?? {}) as Record<string, unknown>
47
+ const securitySchemes = (components['securitySchemes'] ?? {}) as Record<
48
+ string,
49
+ Record<string, unknown>
50
+ >
51
+ const tags = useMemo(() => collectTags(spec), [spec])
52
+
53
+ const descEl = match(description)
54
+ .with(P.nonNullable, (d) => <div className="cp-oas-overview__description">{d}</div>)
55
+ .otherwise(() => null)
56
+
57
+ return (
58
+ <div className="cp-oas-overview">
59
+ {descEl}
60
+ <ServerList servers={servers} />
61
+ <AuthSchemes schemes={securitySchemes} />
62
+ <TagGroups tags={tags} />
63
+ </div>
64
+ )
65
+ }
66
+
67
+ /**
68
+ * Collect all tags from the spec and count their operations.
69
+ *
70
+ * @private
71
+ * @param spec - Parsed OpenAPI spec object
72
+ * @returns Array of tag info with operation counts
73
+ */
74
+ function collectTags(spec: Record<string, unknown>): readonly TagInfo[] {
75
+ const paths = (spec['paths'] ?? {}) as Record<string, Record<string, unknown>>
76
+
77
+ const allTags = Object.values(paths).flatMap((pathItem) =>
78
+ HTTP_METHODS.filter((method) => pathItem[method] !== undefined).flatMap((method) => {
79
+ const operation = pathItem[method] as Record<string, unknown>
80
+ const tags = (operation['tags'] ?? ['default']) as readonly string[]
81
+ return tags
82
+ })
83
+ )
84
+
85
+ const tagMap = countBy(allTags, identity)
86
+
87
+ const specTags = (spec['tags'] ?? []) as readonly Record<string, unknown>[]
88
+ const specTagMap = Object.fromEntries(
89
+ specTags.map((t) => [String(t['name'] ?? ''), String(t['description'] ?? '')])
90
+ )
91
+
92
+ return Object.entries(tagMap).map(([name, count]) => ({
93
+ name,
94
+ description: specTagMap[name] ?? '',
95
+ operationCount: count,
96
+ }))
97
+ }
98
+
99
+ /**
100
+ * Render the server list section.
101
+ *
102
+ * @private
103
+ * @param props - Props with servers array
104
+ * @returns Server list element or null
105
+ */
106
+ function ServerList({
107
+ servers,
108
+ }: {
109
+ readonly servers: readonly Record<string, unknown>[]
110
+ }): React.ReactElement | null {
111
+ return match(servers)
112
+ .with(
113
+ P.when((s): s is readonly Record<string, unknown>[] => s.length > 0),
114
+ (s) => (
115
+ <div className="cp-oas-servers">
116
+ <div className="cp-oas-overview__section-title">Servers</div>
117
+ {s.map((server) => {
118
+ const url = String(server['url'] ?? '')
119
+ const description = server['description'] as string | undefined
120
+ const descEl = match(description)
121
+ .with(P.nonNullable, (d) => (
122
+ <span className="cp-oas-server__description">{` — ${d}`}</span>
123
+ ))
124
+ .otherwise(() => null)
125
+ return (
126
+ <div key={url} className="cp-oas-server">
127
+ <span className="cp-oas-server__url">{url}</span>
128
+ {descEl}
129
+ </div>
130
+ )
131
+ })}
132
+ </div>
133
+ )
134
+ )
135
+ .otherwise(() => null)
136
+ }
137
+
138
+ /**
139
+ * Render the authentication schemes section.
140
+ *
141
+ * @private
142
+ * @param props - Props with security schemes
143
+ * @returns Auth schemes element or null
144
+ */
145
+ function AuthSchemes({
146
+ schemes,
147
+ }: {
148
+ readonly schemes: Record<string, Record<string, unknown>>
149
+ }): React.ReactElement | null {
150
+ const entries = Object.entries(schemes)
151
+ return match(entries)
152
+ .with(
153
+ P.when((e): e is [string, Record<string, unknown>][] => e.length > 0),
154
+ (e) => (
155
+ <div className="cp-oas-auth-schemes">
156
+ <div className="cp-oas-overview__section-title">Authentication</div>
157
+ {e.map(([name, scheme]) => (
158
+ <div key={name} className="cp-oas-auth-scheme">
159
+ <LockIcon />
160
+ <span className="cp-oas-auth-scheme__name">{name}</span>
161
+ <span className="cp-oas-auth-scheme__type">{String(scheme['type'] ?? '')}</span>
162
+ </div>
163
+ ))}
164
+ </div>
165
+ )
166
+ )
167
+ .otherwise(() => null)
168
+ }
169
+
170
+ /**
171
+ * Render the tag groups section with operation counts.
172
+ *
173
+ * @private
174
+ * @param props - Props with tags array
175
+ * @returns Tag groups element or null
176
+ */
177
+ function TagGroups({ tags }: { readonly tags: readonly TagInfo[] }): React.ReactElement | null {
178
+ return match(tags)
179
+ .with(
180
+ P.when((t): t is readonly TagInfo[] => t.length > 0),
181
+ (t) => (
182
+ <div className="cp-oas-tags">
183
+ <div className="cp-oas-overview__section-title">Operations</div>
184
+ {t.map((tag) => {
185
+ const descEl = match(tag.description)
186
+ .with(
187
+ P.when((d): d is string => d.length > 0),
188
+ (d) => <div className="cp-oas-tag-group__description">{d}</div>
189
+ )
190
+ .otherwise(() => null)
191
+ return (
192
+ <div key={tag.name} className="cp-oas-tag-group">
193
+ <div className="cp-oas-tag-group__header">
194
+ <span className="cp-oas-tag-group__name">{tag.name}</span>
195
+ <span className="cp-oas-tag-group__count">
196
+ {`${String(tag.operationCount)} operations`}
197
+ </span>
198
+ </div>
199
+ {descEl}
200
+ </div>
201
+ )
202
+ })}
203
+ </div>
204
+ )
205
+ )
206
+ .otherwise(() => null)
207
+ }
@@ -0,0 +1,149 @@
1
+ import { match, P } from 'massaman/match'
2
+ import type React from 'react'
3
+
4
+ export interface ParametersTableProps {
5
+ /**
6
+ * OpenAPI parameter objects.
7
+ */
8
+ readonly parameters: readonly Record<string, unknown>[]
9
+ }
10
+
11
+ interface ParameterGroup {
12
+ readonly label: string
13
+ readonly items: readonly Record<string, unknown>[]
14
+ }
15
+
16
+ /**
17
+ * Renders operation parameters grouped by location (path, query, header).
18
+ *
19
+ * Each group displays a table with name, type, required, description, and default columns.
20
+ *
21
+ * @param props - Parameters table props
22
+ * @returns React element or null if no parameters
23
+ */
24
+ export function ParametersTable({ parameters }: ParametersTableProps): React.ReactElement | null {
25
+ const groups = groupByIn(parameters)
26
+
27
+ return match(groups)
28
+ .with(
29
+ P.when((g): g is readonly ParameterGroup[] => g.length > 0),
30
+ (g) => (
31
+ <div className="cp-oas-parameters">
32
+ <div className="cp-oas-parameters__title">Parameters</div>
33
+ {g.map(renderGroup)}
34
+ </div>
35
+ )
36
+ )
37
+ .otherwise(() => null)
38
+ }
39
+
40
+ /**
41
+ * Group parameters by their `in` field (path, query, header, etc.).
42
+ *
43
+ * @private
44
+ * @param params - OpenAPI parameter objects
45
+ * @returns Array of parameter groups
46
+ */
47
+ function groupByIn(params: readonly Record<string, unknown>[]): readonly ParameterGroup[] {
48
+ const grouped = Map.groupBy(params, (param) => String(param['in'] ?? 'other'))
49
+ return [...grouped.entries()].map(([label, items]) => ({ label, items }))
50
+ }
51
+
52
+ /**
53
+ * Render a required badge if the parameter is required.
54
+ *
55
+ * @private
56
+ * @param param - OpenAPI parameter object
57
+ * @returns Required badge element or null
58
+ */
59
+ function renderRequired(param: Record<string, unknown>): React.ReactElement | null {
60
+ return match(param['required'])
61
+ .with(true, () => <span className="cp-oas-parameters__required">required</span>)
62
+ .otherwise(() => null)
63
+ }
64
+
65
+ /**
66
+ * Render a default value badge if one exists in the parameter schema.
67
+ *
68
+ * @private
69
+ * @param param - OpenAPI parameter object
70
+ * @returns Default value element or null
71
+ */
72
+ function renderDefault(param: Record<string, unknown>): React.ReactElement | null {
73
+ const schemaDefault = match(param['schema'])
74
+ .with(P.nonNullable, (schema) => (schema as Record<string, unknown>)['default'])
75
+ .otherwise(() => null)
76
+ const defaultValue = schemaDefault ?? param['default']
77
+
78
+ return match(defaultValue)
79
+ .with(P.nonNullable, (def) => <span className="cp-oas-parameters__default">{String(def)}</span>)
80
+ .otherwise(() => null)
81
+ }
82
+
83
+ /**
84
+ * Extract the type string from a parameter's schema.
85
+ *
86
+ * @private
87
+ * @param param - OpenAPI parameter object
88
+ * @returns Type string or dash placeholder
89
+ */
90
+ function extractType(param: Record<string, unknown>): string {
91
+ const schemaType = match(param['schema'])
92
+ .with(
93
+ P.nonNullable,
94
+ (schema) => (schema as Record<string, unknown>)['type'] as string | undefined
95
+ )
96
+ .otherwise(() => null)
97
+
98
+ return String(schemaType ?? param['type'] ?? '—')
99
+ }
100
+
101
+ /**
102
+ * Render a single parameter row in the table.
103
+ *
104
+ * @private
105
+ * @param param - OpenAPI parameter object
106
+ * @returns Table row element
107
+ */
108
+ function renderRow(param: Record<string, unknown>): React.ReactElement {
109
+ return (
110
+ <tr key={String(param['name'])}>
111
+ <td>
112
+ <span className="cp-oas-parameters__name">{String(param['name'] ?? '')}</span>
113
+ </td>
114
+ <td>
115
+ <span className="cp-oas-parameters__type">{extractType(param)}</span>
116
+ </td>
117
+ <td>{renderRequired(param)}</td>
118
+ <td>{String(param['description'] ?? '')}</td>
119
+ <td>{renderDefault(param)}</td>
120
+ </tr>
121
+ )
122
+ }
123
+
124
+ /**
125
+ * Render a parameter group with its label and table.
126
+ *
127
+ * @private
128
+ * @param group - Parameter group with label and items
129
+ * @returns Group container element
130
+ */
131
+ function renderGroup(group: ParameterGroup): React.ReactElement {
132
+ return (
133
+ <div key={group.label}>
134
+ <div className="cp-oas-parameters__group-label">{group.label}</div>
135
+ <table className="cp-oas-parameters__table">
136
+ <thead>
137
+ <tr>
138
+ <th>Name</th>
139
+ <th>Type</th>
140
+ <th>Required</th>
141
+ <th>Description</th>
142
+ <th>Default</th>
143
+ </tr>
144
+ </thead>
145
+ <tbody>{group.items.map(renderRow)}</tbody>
146
+ </table>
147
+ </div>
148
+ )
149
+ }
@@ -0,0 +1,90 @@
1
+ import { CodeBlockRuntime } from '@rspress/core/theme'
2
+ import { match, P } from 'massaman/match'
3
+ import type React from 'react'
4
+
5
+ import { SchemaViewer } from './schema-viewer'
6
+
7
+ export interface RequestBodyProps {
8
+ /**
9
+ * OpenAPI requestBody object.
10
+ */
11
+ readonly requestBody: Record<string, unknown>
12
+ }
13
+
14
+ interface ParsedContent {
15
+ readonly contentType: string
16
+ readonly schema: Record<string, unknown>
17
+ readonly example: unknown | null
18
+ }
19
+
20
+ /**
21
+ * Renders the request body section of an OpenAPI operation.
22
+ *
23
+ * Shows the content type, description, schema tree, and example payload.
24
+ *
25
+ * @param props - Request body props
26
+ * @returns React element with request body details
27
+ */
28
+ export function RequestBody({ requestBody }: RequestBodyProps): React.ReactElement {
29
+ const description = requestBody['description'] as string | undefined
30
+ const parsed = extractFirstContent(requestBody)
31
+
32
+ const descEl = match(description)
33
+ .with(P.nonNullable, (d) => <div className="cp-oas-request-body__description">{d}</div>)
34
+ .otherwise(() => null)
35
+
36
+ const bodyEl = match(parsed)
37
+ .with(P.nonNullable, (p) => (
38
+ <div>
39
+ <div className="cp-oas-request-body__content-type">{p.contentType}</div>
40
+ <SchemaViewer schema={p.schema} />
41
+ </div>
42
+ ))
43
+ .otherwise(() => null)
44
+
45
+ const exampleEl = match(parsed)
46
+ .with(P.nonNullable, (p) =>
47
+ match(p.example)
48
+ .with(P.nonNullable, (ex) => (
49
+ <CodeBlockRuntime lang="json" code={JSON.stringify(ex, null, 2)} />
50
+ ))
51
+ .otherwise(() => null)
52
+ )
53
+ .otherwise(() => null)
54
+
55
+ return (
56
+ <div className="cp-oas-request-body">
57
+ <div className="cp-oas-request-body__title">Request Body</div>
58
+ {descEl}
59
+ {bodyEl}
60
+ {exampleEl}
61
+ </div>
62
+ )
63
+ }
64
+
65
+ /**
66
+ * Extract the first content type entry from a request body.
67
+ *
68
+ * @private
69
+ * @param requestBody - OpenAPI request body object
70
+ * @returns Parsed content with type, schema, and example, or null
71
+ */
72
+ function extractFirstContent(requestBody: Record<string, unknown>): ParsedContent | null {
73
+ const content = requestBody['content'] as Record<string, Record<string, unknown>> | undefined
74
+ return match(content)
75
+ .with(P.nonNullable, (c) => {
76
+ const entries = Object.entries(c)
77
+ return match(entries)
78
+ .with(
79
+ P.when((e): e is [string, Record<string, unknown>][] => e.length > 0),
80
+ (e) => {
81
+ const [[contentType, mediaType]] = e
82
+ const schema = (mediaType['schema'] ?? {}) as Record<string, unknown>
83
+ const { example } = mediaType as { readonly example?: unknown }
84
+ return { contentType, schema, example: example ?? null }
85
+ }
86
+ )
87
+ .otherwise(() => null)
88
+ })
89
+ .otherwise(() => null)
90
+ }