@clidey/dory 0.27.0 → 0.30.0

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 (95) hide show
  1. package/bin/dist/dory.js +115 -29
  2. package/bin/dist/dory.js.map +1 -1
  3. package/package.json +12 -4
  4. package/.dockerignore +0 -4
  5. package/CLAUDE.md +0 -178
  6. package/CODE_OF_CONDUCT.md +0 -128
  7. package/CONTRIBUTING.md +0 -91
  8. package/SECURITY.md +0 -11
  9. package/bin/dory.ts +0 -433
  10. package/dist/favicon.svg +0 -75
  11. package/dist/frontmatter.json +0 -77
  12. package/dist/index.html +0 -55
  13. package/dist/llms.txt +0 -561
  14. package/dist/logo/logo.svg +0 -18
  15. package/docs/api-playground-demo.mdx +0 -119
  16. package/docs/api-reference/asyncapi.json +0 -324
  17. package/docs/api-reference/openapi.json +0 -366
  18. package/docs/api-reference/operations/create.mdx +0 -5
  19. package/docs/api-reference/operations/delete.mdx +0 -5
  20. package/docs/api-reference/operations/get.mdx +0 -5
  21. package/docs/api-reference/operations/update.mdx +0 -5
  22. package/docs/api-reference/websocket-operations/receive-chat-message.mdx +0 -5
  23. package/docs/api-reference/websocket-operations/receive-notifications.mdx +0 -5
  24. package/docs/api-reference/websocket-operations/receive-presence-update.mdx +0 -5
  25. package/docs/api-reference/websocket-operations/send-chat-message.mdx +0 -5
  26. package/docs/api-reference/websocket-operations/send-presence-update.mdx +0 -5
  27. package/docs/dory.json +0 -64
  28. package/docs/favicon.svg +0 -75
  29. package/docs/features.mdx +0 -440
  30. package/docs/introduction.mdx +0 -44
  31. package/docs/logo/logo.svg +0 -18
  32. package/docs/websocket-playground-demo.mdx +0 -135
  33. package/index.html +0 -54
  34. package/scripts/generate-frontmatter-json.js +0 -154
  35. package/scripts/generate-llm-txt.js +0 -115
  36. package/src/app.css +0 -5
  37. package/src/app.tsx +0 -13
  38. package/src/components/dropdown.tsx +0 -53
  39. package/src/components/hooks.tsx +0 -28
  40. package/src/components/loading.tsx +0 -21
  41. package/src/components/mdx-provider.tsx +0 -13
  42. package/src/components/navigation.tsx +0 -58
  43. package/src/components/notification.tsx +0 -13
  44. package/src/components/routes.tsx +0 -92
  45. package/src/components/search-index.ts +0 -43
  46. package/src/components/search.tsx +0 -435
  47. package/src/components/store.ts +0 -216
  48. package/src/config/mdx.js +0 -12
  49. package/src/config/mdx.ts +0 -12
  50. package/src/main.tsx +0 -22
  51. package/src/mdx/accordion.tsx +0 -41
  52. package/src/mdx/api-playground.tsx +0 -659
  53. package/src/mdx/api.tsx +0 -354
  54. package/src/mdx/async-api.tsx +0 -353
  55. package/src/mdx/callout.tsx +0 -64
  56. package/src/mdx/card.tsx +0 -54
  57. package/src/mdx/code.tsx +0 -289
  58. package/src/mdx/expandable.tsx +0 -36
  59. package/src/mdx/fence.tsx +0 -74
  60. package/src/mdx/icon.tsx +0 -73
  61. package/src/mdx/latex.tsx +0 -26
  62. package/src/mdx/list.tsx +0 -7
  63. package/src/mdx/mdx.tsx +0 -47
  64. package/src/mdx/mermaid.tsx +0 -147
  65. package/src/mdx/open-api.tsx +0 -311
  66. package/src/mdx/processor.ts +0 -174
  67. package/src/mdx/source.tsx +0 -58
  68. package/src/mdx/step.tsx +0 -51
  69. package/src/mdx/table.tsx +0 -20
  70. package/src/mdx/tag.tsx +0 -53
  71. package/src/mdx/text.tsx +0 -99
  72. package/src/mdx/websocket-playground.tsx +0 -824
  73. package/src/plugins/docs-assets.ts +0 -79
  74. package/src/plugins/frontmatter-dev-server.ts +0 -158
  75. package/src/plugins/frontmatter-generator.ts +0 -157
  76. package/src/plugins/llm-txt-dev-server.ts +0 -183
  77. package/src/plugins/llm-txt-generator.ts +0 -186
  78. package/src/plugins/sanitize.ts +0 -163
  79. package/src/plugins/unified-mdx-plugin.ts +0 -35
  80. package/src/theme.scss +0 -72
  81. package/src/types/config.ts +0 -47
  82. package/src/ui/header.tsx +0 -91
  83. package/src/ui/layout.tsx +0 -193
  84. package/src/ui/mobile-navigation.tsx +0 -64
  85. package/src/ui/prev-next-link.tsx +0 -74
  86. package/src/ui/table-of-content.tsx +0 -166
  87. package/src/utils/colors.ts +0 -54
  88. package/src/utils/fonts.ts +0 -23
  89. package/src/utils/functions.ts +0 -12
  90. package/src/vite-env.d.ts +0 -1
  91. package/tsconfig.app.json +0 -33
  92. package/tsconfig.bin.json +0 -24
  93. package/tsconfig.json +0 -7
  94. package/tsconfig.node.json +0 -26
  95. package/vite.config.ts +0 -41
package/src/mdx/api.tsx DELETED
@@ -1,354 +0,0 @@
1
- import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
2
- import classNames from 'classnames'
3
- import {
4
- Children,
5
- createContext,
6
- isValidElement,
7
- useContext,
8
- useEffect,
9
- useRef,
10
- useState,
11
- } from 'react'
12
- import { Tag } from './tag'
13
- import { CheckIcon } from 'lucide-react'
14
- import { Button, Card } from '@clidey/ux'
15
-
16
- const languageNames: Record<string, string> = {
17
- js: 'JavaScript',
18
- ts: 'TypeScript',
19
- javascript: 'JavaScript',
20
- typescript: 'TypeScript',
21
- php: 'PHP',
22
- python: 'Python',
23
- ruby: 'Ruby',
24
- go: 'Go',
25
- }
26
-
27
- function getPanelTitle({
28
- title,
29
- language,
30
- }: {
31
- title?: string
32
- language?: string
33
- }) {
34
- if (title) {
35
- return title
36
- }
37
- if (language && language in languageNames) {
38
- return languageNames[language]
39
- }
40
- return 'API'
41
- }
42
-
43
- function ClipboardIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
44
- return (
45
- <svg viewBox="0 0 20 20" aria-hidden="true" {...props}>
46
- <path
47
- strokeWidth="0"
48
- d="M5.5 13.5v-5a2 2 0 0 1 2-2l.447-.894A2 2 0 0 1 9.737 4.5h.527a2 2 0 0 1 1.789 1.106l.447.894a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-5a2 2 0 0 1-2-2Z"
49
- />
50
- <path
51
- fill="none"
52
- strokeLinejoin="round"
53
- d="M12.5 6.5a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-5a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2m5 0-.447-.894a2 2 0 0 0-1.79-1.106h-.527a2 2 0 0 0-1.789 1.106L7.5 6.5m5 0-1 1h-3l-1-1"
54
- />
55
- </svg>
56
- )
57
- }
58
-
59
- function CopyButton({ code }: { code: string }) {
60
- const [copyCount, setCopyCount] = useState(0)
61
- const copied = copyCount > 0
62
-
63
- useEffect(() => {
64
- if (copyCount > 0) {
65
- const timeout = setTimeout(() => setCopyCount(0), 1000)
66
- return () => {
67
- clearTimeout(timeout)
68
- }
69
- }
70
- }, [copyCount])
71
-
72
- return (
73
- <Button
74
- className={classNames(
75
- 'group/button absolute top-3.5 right-4 overflow-hidden rounded-full py-1 pr-3 pl-2 text-2xs font-medium opacity-0 backdrop-blur-sm transition group-hover:opacity-100 focus:opacity-100',
76
- copied
77
- ? 'bg-sky-400/10 ring-1 ring-sky-400/20 ring-inset'
78
- : 'bg-white/5 hover:bg-white/7.5 dark:bg-white/2.5 dark:hover:bg-white/5',
79
- )}
80
- onClick={() => {
81
- window.navigator.clipboard.writeText(code).then(() => {
82
- setCopyCount((count) => count + 1)
83
- })
84
- }}
85
- >
86
- <span
87
- aria-hidden={copied}
88
- className={classNames(
89
- 'pointer-events-none flex items-center gap-0.5 text-zinc-400 transition duration-300',
90
- copied && '-translate-y-1.5 opacity-0',
91
- )}
92
- >
93
- <ClipboardIcon className="h-4 w-4 fill-zinc-500/20 stroke-zinc-500 transition-colors group-hover/button:stroke-zinc-400" />
94
- </span>
95
- <span
96
- aria-hidden={!copied}
97
- className={classNames(
98
- 'pointer-events-none absolute inset-0 flex items-center justify-center text-sky-400 transition duration-300',
99
- !copied && 'translate-y-1.5 opacity-0',
100
- )}
101
- >
102
- <CheckIcon className="h-4 w-4" />
103
- </span>
104
- </Button>
105
- )
106
- }
107
-
108
- function APIPanelHeader({ tag, label }: { tag?: string; label?: string }) {
109
- if (!tag && !label) {
110
- return null
111
- }
112
-
113
- return (
114
- <div className="flex h-9 items-center gap-2 px-4">
115
- {tag && (
116
- <div className="dark flex">
117
- <Tag variant="small">{tag}</Tag>
118
- </div>
119
- )}
120
- {tag && label && (
121
- <span className="h-0.5 w-0.5 rounded-full bg-zinc-500" />
122
- )}
123
- {label && (
124
- <span className="font-mono text-xs text-zinc-400">{label}</span>
125
- )}
126
- </div>
127
- )
128
- }
129
-
130
- function APIPanel({
131
- children,
132
- tag,
133
- label,
134
- code,
135
- }: {
136
- children: React.ReactNode
137
- tag?: string
138
- label?: string
139
- code?: string
140
- }) {
141
- const child = Children.only(children)
142
-
143
- if (isValidElement(child)) {
144
- // @ts-expect-error child.props is not typed
145
- tag = child.props.tag ?? tag
146
- // @ts-expect-error child.props is not typed
147
- label = child.props.label ?? label
148
- // @ts-expect-error child.props is not typed
149
- code = child.props.code ?? code
150
- }
151
-
152
- return (
153
- <div className="group dark:bg-white/2.5">
154
- <APIPanelHeader tag={tag} label={label} />
155
- <div className="relative">
156
- <pre className="overflow-x-auto p-4 text-xs">{children}</pre>
157
- {/* @ts-expect-error code is not typed */}
158
- <CopyButton code={code} />
159
- </div>
160
- </div>
161
- )
162
- }
163
-
164
- function APIGroupHeader({
165
- title,
166
- children,
167
- selectedIndex,
168
- }: {
169
- title: string
170
- children: React.ReactNode
171
- selectedIndex: number
172
- }) {
173
- const hasTabs = Children.count(children) > 1
174
-
175
- if (!title && !hasTabs) {
176
- return null
177
- }
178
-
179
- return (
180
- <div className="flex min-h-[calc(--spacing(12)+1px)] flex-wrap items-start gap-x-4 px-4 pt-2">
181
- {title && (
182
- <h3 className="mr-auto pt-3 text-xs font-semibold">
183
- {title}
184
- </h3>
185
- )}
186
- {hasTabs && (
187
- <TabList className="-mb-px flex gap-4 text-xs font-medium">
188
- {Children.map(children, (child, childIndex) => (
189
- <Tab
190
- className={classNames(
191
- 'pt-2 transition data-selected:not-data-focus:outline-hidden',
192
- childIndex === selectedIndex
193
- ? 'border-sky-500 text-sky-400'
194
- : 'border-transparent text-zinc-400 hover:text-zinc-300',
195
- )}
196
- >
197
- {/* @ts-expect-error child.props is not typed */}
198
- {getPanelTitle(isValidElement(child) ? child.props : {})}
199
- </Tab>
200
- ))}
201
- </TabList>
202
- )}
203
- </div>
204
- )
205
- }
206
-
207
- function APIGroupPanels({
208
- children,
209
- ...props
210
- }: React.ComponentPropsWithoutRef<typeof APIPanel>) {
211
- const hasTabs = Children.count(children) > 1
212
-
213
- if (hasTabs) {
214
- return (
215
- <TabPanels>
216
- {Children.map(children, (child) => (
217
- <TabPanel>
218
- <APIPanel {...props}>{child}</APIPanel>
219
- </TabPanel>
220
- ))}
221
- </TabPanels>
222
- )
223
- }
224
-
225
- return <APIPanel {...props}>{children}</APIPanel>
226
- }
227
- function usePreventLayoutShift() {
228
- const positionRef = useRef<HTMLElement>(null)
229
- const rafRef = useRef<number>()
230
-
231
- useEffect(() => {
232
- return () => {
233
- if (typeof rafRef.current !== 'undefined') {
234
- window.cancelAnimationFrame(rafRef.current)
235
- }
236
- }
237
- }, [])
238
-
239
- return {
240
- positionRef,
241
- preventLayoutShift(callback: () => void) {
242
- if (!positionRef.current) {
243
- return
244
- }
245
-
246
- const initialTop = positionRef.current.getBoundingClientRect().top
247
-
248
- callback()
249
-
250
- rafRef.current = window.requestAnimationFrame(() => {
251
- const newTop =
252
- positionRef.current?.getBoundingClientRect().top ?? initialTop
253
- window.scrollBy(0, newTop - initialTop)
254
- })
255
- },
256
- }
257
- }
258
-
259
- function useTabGroupProps(availableLanguages: Array<string>) {
260
- const [selectedIndex, setSelectedIndex] = useState(0)
261
- const [preferredLanguages, setPreferredLanguages] = useState<string[]>([])
262
-
263
- const activeLanguage = [...availableLanguages].sort(
264
- (a, z) => preferredLanguages.indexOf(z) - preferredLanguages.indexOf(a),
265
- )[0]
266
- const languageIndex = availableLanguages.indexOf(activeLanguage)
267
- const newSelectedIndex = languageIndex === -1 ? selectedIndex : languageIndex
268
-
269
- if (newSelectedIndex !== selectedIndex) {
270
- setSelectedIndex(newSelectedIndex)
271
- }
272
-
273
- const { positionRef, preventLayoutShift } = usePreventLayoutShift()
274
-
275
- return {
276
- as: 'div' as const,
277
- ref: positionRef,
278
- selectedIndex,
279
- onChange: (newSelectedIndex: number) => {
280
- preventLayoutShift(() => {
281
- const newLanguage = availableLanguages[newSelectedIndex]
282
- setPreferredLanguages(prev => [
283
- ...prev.filter(lang => lang !== newLanguage),
284
- newLanguage
285
- ])
286
- })
287
- },
288
- }
289
- }
290
-
291
- const APIGroupContext = createContext(false)
292
-
293
- export function APIGroup({
294
- children,
295
- title,
296
- ...props
297
- }: React.ComponentPropsWithoutRef<typeof APIGroupPanels> & { title: string }) {
298
- const languages =
299
- Children.map(children, (child) =>
300
- // @ts-expect-error child.props is not typed
301
- getPanelTitle(isValidElement(child) ? child.props : {}),
302
- ) ?? []
303
- const tabGroupProps = useTabGroupProps(languages)
304
- const hasTabs = Children.count(children) > 1
305
-
306
- const header = (
307
- <APIGroupHeader title={title} selectedIndex={tabGroupProps.selectedIndex}>
308
- {children}
309
- </APIGroupHeader>
310
- )
311
- const panels = <APIGroupPanels {...props}>{children}</APIGroupPanels>
312
-
313
- return (
314
- <APIGroupContext.Provider value={true}>
315
- {hasTabs ? (
316
- <Card className="py-0 border-black/10 bg-none overflow-hidden mb-4">
317
- <TabGroup {...tabGroupProps}>
318
- <div className="not-prose">
319
- {header}
320
- {panels}
321
- </div>
322
- </TabGroup>
323
- </Card>
324
- ) : (
325
- <Card className="py-0 border-black/10 bg-none overflow-hidden mb-4">
326
- <div className="not-prose">
327
- {header}
328
- {panels}
329
- </div>
330
- </Card>
331
- )}
332
- </APIGroupContext.Provider>
333
- )
334
- }
335
-
336
- export function API({
337
- children,
338
- ...props
339
- }: React.ComponentPropsWithoutRef<'code'>) {
340
- const isGrouped = useContext(APIGroupContext)
341
-
342
- if (isGrouped) {
343
- if (typeof children !== 'string') {
344
- throw new Error(
345
- '`API` children must be a string when nested inside a `APIGroup`.',
346
- )
347
- }
348
- return <code {...props}>
349
- {children}
350
- </code>
351
- }
352
-
353
- return <code {...props}>{children}</code>
354
- }
@@ -1,353 +0,0 @@
1
- import { useState } from "preact/hooks"
2
- import { APIGroup } from "./api"
3
- import { PlayButton } from "./api-playground"
4
- import { Fence } from "./fence"
5
- import { Properties, Property } from "./open-api"
6
- import { Tag } from "./tag"
7
- import { Col, Row } from "./text"
8
- import { WebSocketPlayground, type WebSocketServer } from "./websocket-playground"
9
- import { Button, Sheet, SheetContent, SheetHeader, SheetTitle } from "@clidey/ux"
10
-
11
- interface AsyncAPIMessage {
12
- name?: string
13
- title?: string
14
- summary?: string
15
- description?: string
16
- payload?: Schema
17
- headers?: Schema
18
- correlationId?: {
19
- description?: string
20
- location?: string
21
- }
22
- schemaFormat?: string
23
- contentType?: string
24
- examples?: any[]
25
- }
26
-
27
- interface AsyncAPIOperation {
28
- operationId?: string
29
- summary?: string
30
- description?: string
31
- tags?: Array<{
32
- name: string
33
- description?: string
34
- }>
35
- message?: AsyncAPIMessage | {
36
- oneOf?: AsyncAPIMessage[]
37
- }
38
- bindings?: {
39
- ws?: {
40
- method?: string
41
- query?: Schema
42
- headers?: Schema
43
- bindingVersion?: string
44
- }
45
- }
46
- }
47
-
48
- interface AsyncAPIChannel {
49
- address?: string
50
- title?: string
51
- summary?: string
52
- description?: string
53
- subscribe?: AsyncAPIOperation
54
- publish?: AsyncAPIOperation
55
- parameters?: Record<string, Parameter>
56
- messages?: Record<string, AsyncAPIMessage>
57
- bindings?: {
58
- ws?: {
59
- method?: string
60
- query?: Schema
61
- headers?: Schema
62
- bindingVersion?: string
63
- }
64
- }
65
- }
66
-
67
- interface Schema {
68
- $ref?: string
69
- type?: string
70
- items?: Schema
71
- properties?: Record<string, Schema>
72
- required?: string[]
73
- description?: string
74
- default?: any
75
- example?: any
76
- format?: string
77
- enum?: any[]
78
- oneOf?: Schema[]
79
- anyOf?: Schema[]
80
- allOf?: Schema[]
81
- }
82
-
83
- interface Parameter {
84
- description?: string
85
- schema?: Schema
86
- location?: string
87
- default?: any
88
- }
89
-
90
- interface Components {
91
- schemas?: Record<string, Schema>
92
- messages?: Record<string, AsyncAPIMessage>
93
- parameters?: Record<string, Parameter>
94
- }
95
-
96
- interface Info {
97
- title: string
98
- version: string
99
- description?: string
100
- termsOfService?: string
101
- contact?: {
102
- name?: string
103
- url?: string
104
- email?: string
105
- }
106
- license?: {
107
- name: string
108
- url?: string
109
- }
110
- }
111
-
112
- export interface AsyncAPIJson {
113
- asyncapi: string
114
- info: Info
115
- servers?: Record<string, WebSocketServer & {
116
- host?: string
117
- pathname?: string
118
- protocol?: string
119
- protocolVersion?: string
120
- description?: string
121
- title?: string
122
- summary?: string
123
- variables?: Record<string, any>
124
- security?: any[]
125
- tags?: Array<{
126
- name: string
127
- description?: string
128
- }>
129
- bindings?: any
130
- }>
131
- channels: Record<string, AsyncAPIChannel>
132
- components?: Components
133
- defaultContentType?: string
134
- }
135
-
136
- function resolveSchema(schema: Schema | undefined, components: Components): Schema {
137
- if (!schema) return {}
138
-
139
- if (schema.$ref) {
140
- const refPath = schema.$ref.replace(/^#\/components\/schemas\//, '')
141
- return resolveSchema(components.schemas?.[refPath], components)
142
- }
143
-
144
- if (schema.type === 'array' && schema.items) {
145
- return {
146
- ...schema,
147
- items: resolveSchema(schema.items, components)
148
- }
149
- }
150
-
151
- if (schema.properties) {
152
- const resolvedProps: Record<string, Schema> = {}
153
- for (const [key, value] of Object.entries(schema.properties)) {
154
- resolvedProps[key] = resolveSchema(value, components)
155
- }
156
- return {
157
- ...schema,
158
- properties: resolvedProps
159
- }
160
- }
161
-
162
- return schema
163
- }
164
-
165
- function isDirectMessage(message: AsyncAPIMessage | { oneOf?: AsyncAPIMessage[] } | undefined): message is AsyncAPIMessage {
166
- return message != null && !('oneOf' in message);
167
- }
168
-
169
- export function AsyncAPI({
170
- asyncAPIJson,
171
- channel: selectedChannel,
172
- operation: selectedOperation
173
- }: {
174
- asyncAPIJson: AsyncAPIJson
175
- channel: string
176
- operation: 'subscribe' | 'publish'
177
- }) {
178
- const { channels, components, servers } = asyncAPIJson;
179
- const [showPlayground, setShowPlayground] = useState(false)
180
-
181
- const handleShowPlayground = () => {
182
- setShowPlayground(true)
183
- }
184
-
185
- // Convert AsyncAPI servers to WebSocket servers
186
- const wsServers: WebSocketServer[] = servers ? Object.entries(servers).map(([key, server]) => ({
187
- url: `${server.protocol}://${server.host}${server.pathname || ''}`,
188
- description: server.description || server.title || key,
189
- protocol: server.protocol
190
- })) : [];
191
-
192
- return (
193
- <div className="max-w-none w-full">
194
- <Sheet open={showPlayground} onOpenChange={setShowPlayground} modal={true}>
195
- <SheetContent side="bottom" className="h-[90vh] w-full overflow-y-auto p-8">
196
- <SheetHeader className="p-0">
197
- <SheetTitle>WebSocket Playground</SheetTitle>
198
- </SheetHeader>
199
- <WebSocketPlayground
200
- url={selectedChannel}
201
- servers={wsServers}
202
- />
203
- </SheetContent>
204
- </Sheet>
205
-
206
- {Object.entries(channels).map(([channelPath, channelDetails]) => {
207
- if (channelPath !== selectedChannel) {
208
- return null
209
- }
210
-
211
- const operation = channelDetails[selectedOperation]
212
- if (!operation) {
213
- return null
214
- }
215
-
216
- const { summary, description, message } = operation
217
- const resolvedMessage = isDirectMessage(message) ? message :
218
- (message?.oneOf && message.oneOf.length > 0 ? message.oneOf[0] : undefined)
219
-
220
- return (
221
- <div key={channelPath} className="my-8">
222
- <Row cols={2}>
223
- <Col>
224
- <h2 className="flex items-center mt-4">
225
- <Tag variant="medium" className="mr-2 py-1 px-4 rounded-lg">
226
- {selectedOperation.toUpperCase()}
227
- </Tag>
228
- <span className="ml-2 font-mono">{channelPath}</span>
229
- </h2>
230
-
231
- {(summary || description) && (
232
- <p>{summary || description}</p>
233
- )}
234
-
235
- {resolvedMessage && (
236
- <div className="mt-4">
237
- <h3>Message</h3>
238
- {resolvedMessage.title && <h4>{resolvedMessage.title}</h4>}
239
- {resolvedMessage.description && <p>{resolvedMessage.description}</p>}
240
-
241
- {resolvedMessage.payload && (
242
- <div className="mt-4">
243
- <h4>Payload</h4>
244
- {(() => {
245
- const resolvedPayload = resolveSchema(resolvedMessage.payload, components || {})
246
- return (
247
- <Properties>
248
- {Object.entries(resolvedPayload.properties || {}).map(([propName, prop]) => (
249
- <Property
250
- key={propName}
251
- name={propName}
252
- type={prop.type || 'string'}
253
- required={resolvedPayload.required?.includes(propName)}
254
- >
255
- {prop.description}
256
- {prop.default !== undefined && (
257
- <span className="text-gray-500 ml-1">(default: {JSON.stringify(prop.default)})</span>
258
- )}
259
- </Property>
260
- ))}
261
- </Properties>
262
- )
263
- })()}
264
- </div>
265
- )}
266
-
267
- {resolvedMessage.headers && (
268
- <div className="mt-4">
269
- <h4>Headers</h4>
270
- {(() => {
271
- const resolvedHeaders = resolveSchema(resolvedMessage.headers, components || {})
272
- return (
273
- <Properties>
274
- {Object.entries(resolvedHeaders.properties || {}).map(([headerName, header]) => (
275
- <Property
276
- key={headerName}
277
- name={headerName}
278
- type={header.type || 'string'}
279
- required={resolvedHeaders.required?.includes(headerName)}
280
- >
281
- {header.description}
282
- </Property>
283
- ))}
284
- </Properties>
285
- )
286
- })()}
287
- </div>
288
- )}
289
-
290
- {resolvedMessage?.examples && resolvedMessage.examples.length > 0 && (
291
- <div className="mt-4">
292
- <h4>Examples</h4>
293
- {resolvedMessage.examples.map((example: any, idx: number) => (
294
- <Fence key={idx} language="json">
295
- {JSON.stringify(example, null, 2)}
296
- </Fence>
297
- ))}
298
- </div>
299
- )}
300
- </div>
301
- )}
302
-
303
- {components?.schemas && (
304
- <div className="mt-4">
305
- <h3>Schemas</h3>
306
- {Object.entries(components.schemas).filter(([, schema]) => schema.properties).map(([name, schema]) => (
307
- <div key={name} className="mt-4">
308
- <h4>{name}</h4>
309
- <Properties>
310
- {Object.entries(schema.properties || {}).map(([propName, prop]) => (
311
- <Property
312
- key={propName}
313
- name={propName}
314
- type={prop.type || 'string'}
315
- required={schema.required?.includes(propName)}
316
- >
317
- {prop.description}
318
- </Property>
319
- ))}
320
- </Properties>
321
- </div>
322
- ))}
323
- </div>
324
- )}
325
- </Col>
326
- <Col sticky>
327
- <Button
328
- onClick={handleShowPlayground}
329
- className='ml-auto mt-3 flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium'
330
- >
331
- Try
332
- <PlayButton className="h-4 w-4" />
333
- </Button>
334
- <APIGroup title="Message" tag={selectedOperation.toUpperCase()} label={channelPath}>
335
- <>
336
- {resolvedMessage?.payload && (() => {
337
- const resolvedPayload = resolveSchema(resolvedMessage.payload, components || {})
338
- return (
339
- <Fence language="json">
340
- {JSON.stringify(resolvedPayload, null, 2)}
341
- </Fence>
342
- )
343
- })()}
344
- </>
345
- </APIGroup>
346
- </Col>
347
- </Row>
348
- </div>
349
- )
350
- })}
351
- </div>
352
- )
353
- }