blodemd 0.0.5 → 0.0.7

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 (187) hide show
  1. package/README.md +2 -2
  2. package/dev-server/app/[[...slug]]/page.tsx +139 -0
  3. package/dev-server/app/blodemd-dev/invalidate/route.ts +12 -0
  4. package/dev-server/app/blodemd-dev/static/[...path]/route.ts +32 -0
  5. package/dev-server/app/blodemd-dev/version/route.ts +14 -0
  6. package/dev-server/app/blodemd-internal/proxy/route.ts +86 -0
  7. package/dev-server/app/error.tsx +24 -0
  8. package/dev-server/app/favicon.ico +0 -0
  9. package/dev-server/app/globals.css +4 -0
  10. package/dev-server/app/layout.tsx +38 -0
  11. package/dev-server/app/not-found.tsx +18 -0
  12. package/dev-server/app/search/route.ts +17 -0
  13. package/dev-server/components/dev-reload-script.tsx +86 -0
  14. package/dev-server/components/providers.tsx +15 -0
  15. package/dev-server/lib/dev-state.ts +8 -0
  16. package/dev-server/lib/local-content-source.ts +103 -0
  17. package/dev-server/lib/local-runtime.tsx +558 -0
  18. package/dev-server/next-env.d.ts +5 -0
  19. package/dev-server/next.config.js +46 -0
  20. package/dev-server/package.json +57 -0
  21. package/dev-server/postcss.config.mjs +7 -0
  22. package/dev-server/public/glide-variable.woff2 +0 -0
  23. package/dev-server/tsconfig.json +50 -0
  24. package/dist/cli.mjs +311 -86
  25. package/dist/cli.mjs.map +1 -1
  26. package/docs/app/globals.css +457 -0
  27. package/docs/components/api/api-playground.tsx +295 -0
  28. package/docs/components/api/api-reference.tsx +121 -0
  29. package/docs/components/content/collection-index.tsx +114 -0
  30. package/docs/components/docs/contextual-menu.tsx +406 -0
  31. package/docs/components/docs/copy-page-menu.tsx +255 -0
  32. package/docs/components/docs/doc-header.tsx +210 -0
  33. package/docs/components/docs/doc-shell.tsx +313 -0
  34. package/docs/components/docs/doc-sidebar.tsx +211 -0
  35. package/docs/components/docs/doc-toc.tsx +45 -0
  36. package/docs/components/docs/mobile-nav.tsx +205 -0
  37. package/docs/components/icons/doc-icon.tsx +96 -0
  38. package/docs/components/mdx/accordion.tsx +83 -0
  39. package/docs/components/mdx/badge.tsx +79 -0
  40. package/docs/components/mdx/callout.tsx +88 -0
  41. package/docs/components/mdx/card.tsx +110 -0
  42. package/docs/components/mdx/code-block.tsx +75 -0
  43. package/docs/components/mdx/code-group.tsx +94 -0
  44. package/docs/components/mdx/color.tsx +87 -0
  45. package/docs/components/mdx/columns.tsx +25 -0
  46. package/docs/components/mdx/expandable.tsx +45 -0
  47. package/docs/components/mdx/field-layout.tsx +77 -0
  48. package/docs/components/mdx/frame.tsx +23 -0
  49. package/docs/components/mdx/get-text-content.ts +18 -0
  50. package/docs/components/mdx/icon.tsx +12 -0
  51. package/docs/components/mdx/index.tsx +107 -0
  52. package/docs/components/mdx/installer.tsx +20 -0
  53. package/docs/components/mdx/panel.tsx +11 -0
  54. package/docs/components/mdx/param-field.tsx +56 -0
  55. package/docs/components/mdx/preview.tsx +36 -0
  56. package/docs/components/mdx/prompt.tsx +63 -0
  57. package/docs/components/mdx/request-example.tsx +27 -0
  58. package/docs/components/mdx/response-field.tsx +42 -0
  59. package/docs/components/mdx/steps.tsx +92 -0
  60. package/docs/components/mdx/tabs.tsx +88 -0
  61. package/docs/components/mdx/tile.tsx +43 -0
  62. package/docs/components/mdx/tooltip.tsx +71 -0
  63. package/docs/components/mdx/tree.tsx +120 -0
  64. package/docs/components/mdx/type-table.tsx +71 -0
  65. package/docs/components/mdx/update.tsx +44 -0
  66. package/docs/components/mdx/video.tsx +12 -0
  67. package/docs/components/mdx/view.tsx +66 -0
  68. package/docs/components/providers.tsx +15 -0
  69. package/docs/components/ui/breadcrumb.tsx +92 -0
  70. package/docs/components/ui/button.tsx +90 -0
  71. package/docs/components/ui/card.tsx +92 -0
  72. package/docs/components/ui/command.tsx +139 -0
  73. package/docs/components/ui/dialog.tsx +97 -0
  74. package/docs/components/ui/field.tsx +237 -0
  75. package/docs/components/ui/input.tsx +105 -0
  76. package/docs/components/ui/label.tsx +22 -0
  77. package/docs/components/ui/popover.tsx +72 -0
  78. package/docs/components/ui/search.tsx +384 -0
  79. package/docs/components/ui/separator.tsx +26 -0
  80. package/docs/components/ui/sheet.tsx +104 -0
  81. package/docs/components/ui/sidebar.tsx +433 -0
  82. package/docs/components/ui/theme-toggle.tsx +62 -0
  83. package/docs/components/ui/tooltip.tsx +53 -0
  84. package/docs/lib/contextual-options.ts +193 -0
  85. package/docs/lib/docs-collection.ts +22 -0
  86. package/docs/lib/mdx.ts +87 -0
  87. package/docs/lib/navigation.ts +288 -0
  88. package/docs/lib/openapi.ts +158 -0
  89. package/docs/lib/routes.ts +44 -0
  90. package/docs/lib/server-cache.ts +83 -0
  91. package/docs/lib/shiki.ts +40 -0
  92. package/docs/lib/theme.ts +29 -0
  93. package/docs/lib/toc.ts +2 -0
  94. package/docs/lib/utils.ts +5 -0
  95. package/package.json +43 -6
  96. package/packages/@repo/common/dist/index.d.ts +9 -0
  97. package/packages/@repo/common/dist/index.d.ts.map +1 -0
  98. package/packages/@repo/common/dist/index.js +42 -0
  99. package/packages/@repo/common/package.json +34 -0
  100. package/packages/@repo/common/src/index.ts +51 -0
  101. package/packages/@repo/contracts/dist/api-key.d.ts +30 -0
  102. package/packages/@repo/contracts/dist/api-key.d.ts.map +1 -0
  103. package/packages/@repo/contracts/dist/api-key.js +20 -0
  104. package/packages/@repo/contracts/dist/dates.d.ts +4 -0
  105. package/packages/@repo/contracts/dist/dates.d.ts.map +1 -0
  106. package/packages/@repo/contracts/dist/dates.js +2 -0
  107. package/packages/@repo/contracts/dist/deployment.d.ts +71 -0
  108. package/packages/@repo/contracts/dist/deployment.d.ts.map +1 -0
  109. package/packages/@repo/contracts/dist/deployment.js +46 -0
  110. package/packages/@repo/contracts/dist/domain.d.ts +94 -0
  111. package/packages/@repo/contracts/dist/domain.d.ts.map +1 -0
  112. package/packages/@repo/contracts/dist/domain.js +36 -0
  113. package/packages/@repo/contracts/dist/ids.d.ts +14 -0
  114. package/packages/@repo/contracts/dist/ids.d.ts.map +1 -0
  115. package/packages/@repo/contracts/dist/ids.js +10 -0
  116. package/packages/@repo/contracts/dist/index.d.ts +10 -0
  117. package/packages/@repo/contracts/dist/index.d.ts.map +1 -0
  118. package/packages/@repo/contracts/dist/index.js +11 -0
  119. package/packages/@repo/contracts/dist/pagination.d.ts +23 -0
  120. package/packages/@repo/contracts/dist/pagination.d.ts.map +1 -0
  121. package/packages/@repo/contracts/dist/pagination.js +15 -0
  122. package/packages/@repo/contracts/dist/project.d.ts +25 -0
  123. package/packages/@repo/contracts/dist/project.d.ts.map +1 -0
  124. package/packages/@repo/contracts/dist/project.js +23 -0
  125. package/packages/@repo/contracts/dist/tenant.d.ts +111 -0
  126. package/packages/@repo/contracts/dist/tenant.d.ts.map +1 -0
  127. package/packages/@repo/contracts/dist/tenant.js +56 -0
  128. package/packages/@repo/contracts/dist/user.d.ts +9 -0
  129. package/packages/@repo/contracts/dist/user.d.ts.map +1 -0
  130. package/packages/@repo/contracts/dist/user.js +9 -0
  131. package/packages/@repo/contracts/package.json +37 -0
  132. package/packages/@repo/contracts/src/api-key.ts +27 -0
  133. package/packages/@repo/contracts/src/dates.ts +4 -0
  134. package/packages/@repo/contracts/src/deployment.ts +73 -0
  135. package/packages/@repo/contracts/src/domain.ts +51 -0
  136. package/packages/@repo/contracts/src/ids.ts +22 -0
  137. package/packages/@repo/contracts/src/index.ts +11 -0
  138. package/packages/@repo/contracts/src/pagination.ts +21 -0
  139. package/packages/@repo/contracts/src/project.ts +30 -0
  140. package/packages/@repo/contracts/src/tenant.ts +92 -0
  141. package/packages/@repo/contracts/src/user.ts +12 -0
  142. package/packages/@repo/models/dist/docs-config.d.ts +985 -0
  143. package/packages/@repo/models/dist/docs-config.d.ts.map +1 -0
  144. package/packages/@repo/models/dist/docs-config.js +548 -0
  145. package/packages/@repo/models/dist/index.d.ts +3 -0
  146. package/packages/@repo/models/dist/index.d.ts.map +1 -0
  147. package/packages/@repo/models/dist/index.js +3 -0
  148. package/packages/@repo/models/dist/tenant.d.ts +25 -0
  149. package/packages/@repo/models/dist/tenant.d.ts.map +1 -0
  150. package/packages/@repo/models/dist/tenant.js +1 -0
  151. package/packages/@repo/models/package.json +37 -0
  152. package/packages/@repo/models/src/docs-config.ts +648 -0
  153. package/packages/@repo/models/src/index.ts +3 -0
  154. package/packages/@repo/models/src/tenant.ts +29 -0
  155. package/packages/@repo/prebuild/dist/index.d.ts +2 -0
  156. package/packages/@repo/prebuild/dist/index.d.ts.map +1 -0
  157. package/packages/@repo/prebuild/dist/index.js +2 -0
  158. package/packages/@repo/prebuild/dist/openapi.d.ts +43 -0
  159. package/packages/@repo/prebuild/dist/openapi.d.ts.map +1 -0
  160. package/packages/@repo/prebuild/dist/openapi.js +58 -0
  161. package/packages/@repo/prebuild/package.json +39 -0
  162. package/packages/@repo/prebuild/src/index.ts +2 -0
  163. package/packages/@repo/prebuild/src/openapi.ts +116 -0
  164. package/packages/@repo/previewing/dist/blob-source.d.ts +16 -0
  165. package/packages/@repo/previewing/dist/blob-source.d.ts.map +1 -0
  166. package/packages/@repo/previewing/dist/blob-source.js +110 -0
  167. package/packages/@repo/previewing/dist/content-source.d.ts +12 -0
  168. package/packages/@repo/previewing/dist/content-source.d.ts.map +1 -0
  169. package/packages/@repo/previewing/dist/content-source.js +1 -0
  170. package/packages/@repo/previewing/dist/fs-source.d.ts +11 -0
  171. package/packages/@repo/previewing/dist/fs-source.d.ts.map +1 -0
  172. package/packages/@repo/previewing/dist/fs-source.js +72 -0
  173. package/packages/@repo/previewing/dist/index.d.ts +120 -0
  174. package/packages/@repo/previewing/dist/index.d.ts.map +1 -0
  175. package/packages/@repo/previewing/dist/index.js +984 -0
  176. package/packages/@repo/previewing/package.json +41 -0
  177. package/packages/@repo/previewing/src/blob-source.ts +167 -0
  178. package/packages/@repo/previewing/src/content-source.ts +12 -0
  179. package/packages/@repo/previewing/src/fs-source.ts +104 -0
  180. package/packages/@repo/previewing/src/index.ts +1490 -0
  181. package/packages/@repo/validation/dist/index.d.ts +12 -0
  182. package/packages/@repo/validation/dist/index.d.ts.map +1 -0
  183. package/packages/@repo/validation/dist/index.js +30 -0
  184. package/packages/@repo/validation/package.json +37 -0
  185. package/packages/@repo/validation/src/index.ts +59 -0
  186. package/packages/@repo/validation/src/mintlify-docs-schema.json +5016 -0
  187. package/scripts/prepare-package.mjs +39 -0
@@ -0,0 +1,23 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ interface FrameProps {
4
+ caption?: string;
5
+ hint?: string;
6
+ children: ReactNode;
7
+ }
8
+
9
+ export const Frame = ({ caption, hint, children }: FrameProps) => (
10
+ <figure className="my-4">
11
+ {hint ? (
12
+ <div className="mb-2 text-sm text-muted-foreground">{hint}</div>
13
+ ) : null}
14
+ <div className="overflow-hidden rounded-xl border border-border bg-card">
15
+ {children}
16
+ </div>
17
+ {caption ? (
18
+ <figcaption className="mt-2 text-center text-sm text-muted-foreground">
19
+ {caption}
20
+ </figcaption>
21
+ ) : null}
22
+ </figure>
23
+ );
@@ -0,0 +1,18 @@
1
+ import { isValidElement } from "react";
2
+ import type { ReactNode } from "react";
3
+
4
+ export const getTextContent = (node: ReactNode): string => {
5
+ if (typeof node === "string") {
6
+ return node;
7
+ }
8
+ if (typeof node === "number") {
9
+ return String(node);
10
+ }
11
+ if (Array.isArray(node)) {
12
+ return node.map(getTextContent).join("");
13
+ }
14
+ if (isValidElement<{ children?: ReactNode }>(node)) {
15
+ return getTextContent(node.props.children);
16
+ }
17
+ return "";
18
+ };
@@ -0,0 +1,12 @@
1
+ import { DocIcon } from "@/components/icons/doc-icon";
2
+
3
+ interface IconProps {
4
+ icon: string;
5
+ color?: string;
6
+ size?: number;
7
+ className?: string;
8
+ }
9
+
10
+ export const Icon = ({ icon, color, size = 16, className }: IconProps) => (
11
+ <DocIcon className={className} color={color} icon={icon} size={size} />
12
+ );
@@ -0,0 +1,107 @@
1
+ import type { MDXComponents } from "mdx/types";
2
+ import Link from "next/link";
3
+ import type { ComponentProps } from "react";
4
+
5
+ import { Accordion, AccordionGroup } from "./accordion";
6
+ import { Badge } from "./badge";
7
+ import { Callout, Check, Danger, Info, Note, Tip, Warning } from "./callout";
8
+ import { Card } from "./card";
9
+ import { CodeBlock } from "./code-block";
10
+ import { CodeGroup } from "./code-group";
11
+ import { Color } from "./color";
12
+ import { Column, Columns } from "./columns";
13
+ import { Expandable } from "./expandable";
14
+ import { Frame } from "./frame";
15
+ import { Icon } from "./icon";
16
+ import { Installer } from "./installer";
17
+ import { Panel } from "./panel";
18
+ import { ParamField } from "./param-field";
19
+ import { Preview } from "./preview";
20
+ import { Prompt } from "./prompt";
21
+ import { RequestExample, ResponseExample } from "./request-example";
22
+ import { ResponseField } from "./response-field";
23
+ import { Step, Steps } from "./steps";
24
+ import { Tab, Tabs } from "./tabs";
25
+ import { Tile } from "./tile";
26
+ import { Tooltip } from "./tooltip";
27
+ import { Tree as TreeRoot, TreeFile, TreeFolder } from "./tree";
28
+ import { TypeTable } from "./type-table";
29
+ import { Update } from "./update";
30
+ import { Video } from "./video";
31
+ import { View, ViewGroup } from "./view";
32
+
33
+ // Rebuild dotted MDX exports in the server module so Next's client references
34
+ // still expose `Tree.Folder` and `Tree.File` during MDX evaluation.
35
+ const Tree = Object.assign(
36
+ (props: ComponentProps<typeof TreeRoot>) => <TreeRoot {...props} />,
37
+ {
38
+ File: TreeFile,
39
+ Folder: TreeFolder,
40
+ }
41
+ );
42
+
43
+ const MdxLink = ({
44
+ href,
45
+ children,
46
+ ...props
47
+ }: React.AnchorHTMLAttributes<HTMLAnchorElement>) => {
48
+ if (!href) {
49
+ return <a {...props}>{children}</a>;
50
+ }
51
+ const isExternal = href.startsWith("http");
52
+ if (isExternal) {
53
+ return (
54
+ <a {...props} href={href} rel="noopener noreferrer" target="_blank">
55
+ {children}
56
+ </a>
57
+ );
58
+ }
59
+ return (
60
+ <Link href={href} {...props}>
61
+ {children}
62
+ </Link>
63
+ );
64
+ };
65
+
66
+ export const mdxComponents: MDXComponents = {
67
+ Accordion,
68
+ AccordionGroup,
69
+ Badge,
70
+ Callout,
71
+ Card,
72
+ Check,
73
+ CodeGroup,
74
+ Color,
75
+ Column,
76
+ Columns,
77
+ Danger,
78
+ Expandable,
79
+ Frame,
80
+ Icon,
81
+ Info,
82
+ Installer,
83
+ Note,
84
+ Panel,
85
+ ParamField,
86
+ Preview,
87
+ Prompt,
88
+ RequestExample,
89
+ ResponseExample,
90
+ ResponseField,
91
+ Step,
92
+ Steps,
93
+ Tab,
94
+ Tabs,
95
+ Tile,
96
+ Tip,
97
+ Tooltip,
98
+ Tree,
99
+ TypeTable,
100
+ Update,
101
+ Video,
102
+ View,
103
+ ViewGroup,
104
+ Warning,
105
+ a: MdxLink,
106
+ pre: CodeBlock,
107
+ };
@@ -0,0 +1,20 @@
1
+ export const Installer = ({
2
+ path,
3
+ command,
4
+ }: {
5
+ path: string;
6
+ command?: string;
7
+ }) => {
8
+ const resolvedCommand =
9
+ command ?? `npx @vercel/platform-elements@latest ${path}`;
10
+ return (
11
+ <div className="my-4 grid gap-2.5 rounded-xl border border-border bg-background/70 p-3.5">
12
+ <div className="text-xs uppercase tracking-widest text-muted-foreground">
13
+ Install
14
+ </div>
15
+ <pre className="m-0 overflow-x-auto rounded-lg bg-code p-3 font-mono text-sm text-code-foreground">
16
+ <code>{resolvedCommand}</code>
17
+ </pre>
18
+ </div>
19
+ );
20
+ };
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ interface PanelProps {
4
+ children: ReactNode;
5
+ }
6
+
7
+ export const Panel = ({ children }: PanelProps) => (
8
+ <aside className="my-4 rounded-xl border border-border bg-card p-4">
9
+ {children}
10
+ </aside>
11
+ );
@@ -0,0 +1,56 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ import { FieldLayout } from "./field-layout";
4
+
5
+ interface ParamFieldProps {
6
+ path?: string;
7
+ query?: string;
8
+ body?: string;
9
+ header?: string;
10
+ type?: string;
11
+ required?: boolean;
12
+ deprecated?: boolean;
13
+ default?: string;
14
+ placeholder?: string;
15
+ children?: ReactNode;
16
+ }
17
+
18
+ export const ParamField = ({
19
+ path,
20
+ query,
21
+ body,
22
+ header,
23
+ type,
24
+ required,
25
+ deprecated,
26
+ default: defaultValue,
27
+ children,
28
+ }: ParamFieldProps) => {
29
+ const name = path ?? query ?? body ?? header ?? "";
30
+
31
+ let location: string | undefined;
32
+ if (path) {
33
+ location = "path";
34
+ } else if (query) {
35
+ location = "query";
36
+ } else if (body) {
37
+ location = "body";
38
+ } else if (header) {
39
+ location = "header";
40
+ }
41
+
42
+ const badges = location ? [{ label: location }] : undefined;
43
+
44
+ return (
45
+ <FieldLayout
46
+ badges={badges}
47
+ defaultValue={defaultValue}
48
+ deprecated={deprecated}
49
+ name={name}
50
+ required={required}
51
+ type={type}
52
+ >
53
+ {children}
54
+ </FieldLayout>
55
+ );
56
+ };
@@ -0,0 +1,36 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ export const Preview = ({
4
+ example,
5
+ source,
6
+ title = "Preview",
7
+ children,
8
+ }: {
9
+ example?: string;
10
+ source?: string;
11
+ title?: string;
12
+ children?: ReactNode;
13
+ }) => (
14
+ <div className="my-4 grid gap-3 rounded-xl border border-border bg-surface p-4">
15
+ <div className="flex flex-wrap items-center gap-2 text-xs uppercase tracking-wider text-muted-foreground">
16
+ <span className="font-semibold">{title}</span>
17
+ {example ? (
18
+ <span className="rounded-full border border-border bg-accent px-2.5 py-1 text-primary">
19
+ {example}
20
+ </span>
21
+ ) : null}
22
+ {source ? (
23
+ <span className="rounded-full border border-border bg-accent px-2.5 py-1 text-primary">
24
+ {source}
25
+ </span>
26
+ ) : null}
27
+ </div>
28
+ <div className="rounded-xl border border-dashed border-border bg-gradient-to-br from-primary/15 to-primary/5 p-4.5">
29
+ {children ?? (
30
+ <div className="text-sm text-muted-foreground">
31
+ Interactive preview placeholder
32
+ </div>
33
+ )}
34
+ </div>
35
+ </div>
36
+ );
@@ -0,0 +1,63 @@
1
+ "use client";
2
+
3
+ import { CheckIcon, ClipboardIcon } from "blode-icons-react";
4
+ import { useCallback, useEffect, useMemo, useState } from "react";
5
+ import type { ReactNode } from "react";
6
+
7
+ import { getTextContent } from "./get-text-content";
8
+
9
+ interface PromptProps {
10
+ description: string;
11
+ icon?: ReactNode;
12
+ children: ReactNode;
13
+ }
14
+
15
+ export const Prompt = ({ description, icon, children }: PromptProps) => {
16
+ const [copied, setCopied] = useState(false);
17
+ const text = useMemo(() => getTextContent(children), [children]);
18
+
19
+ useEffect(() => {
20
+ if (copied) {
21
+ const timer = setTimeout(() => setCopied(false), 2000);
22
+ return () => clearTimeout(timer);
23
+ }
24
+ }, [copied]);
25
+
26
+ const handleCopy = useCallback(async () => {
27
+ if (!text) {
28
+ return;
29
+ }
30
+ await navigator.clipboard.writeText(text);
31
+ setCopied(true);
32
+ }, [text]);
33
+
34
+ return (
35
+ <div className="my-4 rounded-xl border border-border bg-card">
36
+ <div className="flex items-start gap-3 border-b border-border p-4">
37
+ {icon ? (
38
+ <span className="mt-0.5 flex size-5 shrink-0 items-center justify-center text-muted-foreground">
39
+ {icon}
40
+ </span>
41
+ ) : null}
42
+ <div className="min-w-0 flex-1 text-sm text-foreground">
43
+ {description}
44
+ </div>
45
+ <button
46
+ className="inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
47
+ onClick={handleCopy}
48
+ type="button"
49
+ >
50
+ <span className="sr-only">{copied ? "Copied" : "Copy prompt"}</span>
51
+ {copied ? (
52
+ <CheckIcon aria-hidden className="size-3.5" />
53
+ ) : (
54
+ <ClipboardIcon aria-hidden className="size-3.5" />
55
+ )}
56
+ </button>
57
+ </div>
58
+ <div className="p-4 text-sm text-muted-foreground [&>p:first-child]:mt-0 [&>p:last-child]:mb-0">
59
+ {children}
60
+ </div>
61
+ </div>
62
+ );
63
+ };
@@ -0,0 +1,27 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ interface RequestExampleProps {
4
+ children: ReactNode;
5
+ }
6
+
7
+ export const RequestExample = ({ children }: RequestExampleProps) => (
8
+ <div className="my-4">
9
+ <div className="mb-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
10
+ Request
11
+ </div>
12
+ <div className="[&>pre]:mt-0 [&>[data-rehype-pretty-code-figure]]:mt-0">
13
+ {children}
14
+ </div>
15
+ </div>
16
+ );
17
+
18
+ export const ResponseExample = ({ children }: RequestExampleProps) => (
19
+ <div className="my-4">
20
+ <div className="mb-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
21
+ Response
22
+ </div>
23
+ <div className="[&>pre]:mt-0 [&>[data-rehype-pretty-code-figure]]:mt-0">
24
+ {children}
25
+ </div>
26
+ </div>
27
+ );
@@ -0,0 +1,42 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ import { FieldLayout } from "./field-layout";
4
+
5
+ interface ResponseFieldProps {
6
+ name: string;
7
+ type: string;
8
+ default?: string;
9
+ required?: boolean;
10
+ deprecated?: boolean;
11
+ pre?: string[];
12
+ post?: string[];
13
+ children?: ReactNode;
14
+ }
15
+
16
+ export const ResponseField = ({
17
+ name,
18
+ type,
19
+ default: defaultValue,
20
+ required,
21
+ deprecated,
22
+ pre,
23
+ post,
24
+ children,
25
+ }: ResponseFieldProps) => {
26
+ const preBadges = pre?.map((label) => ({ label })) ?? [];
27
+ const postBadges = post?.map((label) => ({ label })) ?? [];
28
+ const badges = [...preBadges, ...postBadges];
29
+
30
+ return (
31
+ <FieldLayout
32
+ badges={badges.length > 0 ? badges : undefined}
33
+ defaultValue={defaultValue}
34
+ deprecated={deprecated}
35
+ name={name}
36
+ required={required}
37
+ type={type}
38
+ >
39
+ {children}
40
+ </FieldLayout>
41
+ );
42
+ };
@@ -0,0 +1,92 @@
1
+ import type { ReactNode } from "react";
2
+ import { isValidElement } from "react";
3
+
4
+ import { cn } from "@/lib/utils";
5
+
6
+ interface StepProps {
7
+ title: string;
8
+ children?: ReactNode;
9
+ icon?: ReactNode;
10
+ stepNumber?: number;
11
+ titleSize?: "p" | "h2" | "h3" | "h4";
12
+ id?: string;
13
+ }
14
+
15
+ export const Step = ({
16
+ title,
17
+ children,
18
+ icon,
19
+ stepNumber,
20
+ titleSize = "p",
21
+ id,
22
+ }: StepProps) => {
23
+ const TitleTag = titleSize === "p" ? "div" : titleSize;
24
+ const anchorId = id ?? title.toLowerCase().replaceAll(/\s+/g, "-");
25
+
26
+ return (
27
+ <div className="relative pb-8 pl-10 last:pb-0" id={anchorId}>
28
+ <div
29
+ aria-hidden
30
+ className="absolute left-0 flex size-7 items-center justify-center rounded-full border border-border bg-muted font-mono text-xs font-medium"
31
+ >
32
+ {icon ?? stepNumber ?? null}
33
+ </div>
34
+ <TitleTag
35
+ className={cn(
36
+ "font-semibold leading-7",
37
+ titleSize === "h2" && "text-xl",
38
+ titleSize === "h3" && "text-lg",
39
+ titleSize === "h4" && "text-base",
40
+ titleSize === "p" && "text-base"
41
+ )}
42
+ >
43
+ {title}
44
+ </TitleTag>
45
+ {children ? (
46
+ <div className="mt-2 text-sm text-muted-foreground">{children}</div>
47
+ ) : null}
48
+ </div>
49
+ );
50
+ };
51
+
52
+ interface StepsProps {
53
+ children: ReactNode;
54
+ titleSize?: "p" | "h2" | "h3" | "h4";
55
+ }
56
+
57
+ export const Steps = ({ children, titleSize }: StepsProps) => {
58
+ const items = Array.isArray(children) ? children : [children];
59
+
60
+ const hasStepChildren = items.some(
61
+ (child) => isValidElement(child) && child.type === Step
62
+ );
63
+
64
+ if (!hasStepChildren) {
65
+ return (
66
+ <div className="steps mb-12 [counter-reset:step] md:ml-4 md:border-l md:pl-8 [&>h3]:step">
67
+ {children}
68
+ </div>
69
+ );
70
+ }
71
+
72
+ let counter = 0;
73
+ const numberedChildren = items.map((child) => {
74
+ if (isValidElement<StepProps>(child) && child.type === Step) {
75
+ counter += 1;
76
+ const stepNumber = child.props.stepNumber ?? counter;
77
+ return (
78
+ <Step
79
+ key={child.props.id ?? child.props.title}
80
+ {...child.props}
81
+ stepNumber={stepNumber}
82
+ titleSize={child.props.titleSize ?? titleSize}
83
+ />
84
+ );
85
+ }
86
+ return child;
87
+ });
88
+
89
+ return (
90
+ <div className="my-6 border-l border-border pl-2">{numberedChildren}</div>
91
+ );
92
+ };
@@ -0,0 +1,88 @@
1
+ "use client";
2
+
3
+ import { isValidElement, useCallback, useMemo, useState } from "react";
4
+ import type { MouseEvent, ReactElement, ReactNode } from "react";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ interface TabProps {
9
+ title?: string;
10
+ label?: string;
11
+ icon?: ReactNode;
12
+ id?: string;
13
+ children: ReactNode;
14
+ }
15
+
16
+ export const Tab = ({ children }: TabProps) => (
17
+ <div className="p-4">{children}</div>
18
+ );
19
+
20
+ interface TabsProps {
21
+ children: ReactNode;
22
+ defaultTabIndex?: number;
23
+ borderBottom?: boolean;
24
+ }
25
+
26
+ export const Tabs = ({
27
+ children,
28
+ defaultTabIndex = 0,
29
+ borderBottom,
30
+ }: TabsProps) => {
31
+ const items = useMemo(() => {
32
+ const nodes = Array.isArray(children) ? children : [children];
33
+ return nodes.filter((child): child is ReactElement<TabProps> =>
34
+ isValidElement<TabProps>(child)
35
+ );
36
+ }, [children]);
37
+
38
+ const [active, setActive] = useState(defaultTabIndex);
39
+ const activeItem = items[active];
40
+ const handleTabClick = useCallback((event: MouseEvent<HTMLButtonElement>) => {
41
+ const index = Number(event.currentTarget.dataset.index ?? "0");
42
+ setActive(index);
43
+ }, []);
44
+
45
+ if (!items.length) {
46
+ return null;
47
+ }
48
+
49
+ return (
50
+ <div
51
+ className={cn(
52
+ "overflow-hidden rounded-xl border border-border bg-surface",
53
+ borderBottom && "border-b-2"
54
+ )}
55
+ >
56
+ <div className="flex gap-2 bg-muted p-2" role="tablist">
57
+ {items.map((item, index) => {
58
+ const tabLabel =
59
+ item.props.title ?? item.props.label ?? `Tab ${index + 1}`;
60
+ return (
61
+ <button
62
+ aria-selected={index === active}
63
+ className={cn(
64
+ "inline-flex items-center gap-1.5 rounded-full border-none bg-transparent px-3 py-2 text-sm cursor-pointer transition-colors",
65
+ index === active
66
+ ? "bg-primary text-primary-foreground"
67
+ : "text-muted-foreground hover:text-foreground"
68
+ )}
69
+ data-index={index}
70
+ key={String(item.key ?? tabLabel)}
71
+ onClick={handleTabClick}
72
+ role="tab"
73
+ type="button"
74
+ >
75
+ {item.props.icon ? (
76
+ <span className="shrink-0">{item.props.icon}</span>
77
+ ) : null}
78
+ {tabLabel}
79
+ </button>
80
+ );
81
+ })}
82
+ </div>
83
+ <div className="p-4" role="tabpanel">
84
+ {activeItem}
85
+ </div>
86
+ </div>
87
+ );
88
+ };
@@ -0,0 +1,43 @@
1
+ import Link from "next/link";
2
+ import type { ReactNode } from "react";
3
+
4
+ interface TileProps {
5
+ href: string;
6
+ title?: string;
7
+ description?: string;
8
+ children: ReactNode;
9
+ }
10
+
11
+ export const Tile = ({ href, title, description, children }: TileProps) => {
12
+ const isExternal = href.startsWith("http");
13
+
14
+ const inner = (
15
+ <div className="group/tile overflow-hidden rounded-xl border border-border bg-card transition-colors hover:border-primary/30 hover:bg-accent/50">
16
+ <div className="flex items-center justify-center bg-muted/50 p-6">
17
+ {children}
18
+ </div>
19
+ {title || description ? (
20
+ <div className="border-t border-border p-3">
21
+ {title ? (
22
+ <div className="text-sm font-medium text-foreground">{title}</div>
23
+ ) : null}
24
+ {description ? (
25
+ <div className="mt-0.5 text-xs text-muted-foreground">
26
+ {description}
27
+ </div>
28
+ ) : null}
29
+ </div>
30
+ ) : null}
31
+ </div>
32
+ );
33
+
34
+ if (isExternal) {
35
+ return (
36
+ <a href={href} rel="noopener noreferrer" target="_blank">
37
+ {inner}
38
+ </a>
39
+ );
40
+ }
41
+
42
+ return <Link href={href}>{inner}</Link>;
43
+ };