@clidey/dory 1.0.5 → 1.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.
- package/bin/dist/dory.js +36 -2
- package/bin/dist/dory.js.map +1 -1
- package/index.html +0 -1
- package/package.json +16 -22
- package/src/components/hooks.tsx +2 -1
- package/src/components/store.ts +5 -4
- package/src/config/mdx.js +8 -3
- package/src/config/mdx.ts +8 -3
- package/src/embed/widget-iframe.ts +10 -3
- package/src/entry-server.tsx +11 -7
- package/src/main.tsx +5 -1
- package/src/mdx/accordion.tsx +11 -8
- package/src/mdx/api.tsx +2 -352
- package/src/mdx/async-api.tsx +124 -295
- package/src/mdx/callout.tsx +22 -24
- package/src/mdx/card.tsx +20 -22
- package/src/mdx/chip.tsx +14 -0
- package/src/mdx/code.tsx +133 -110
- package/src/mdx/expandable.tsx +8 -8
- package/src/mdx/fence-meta.test.ts +39 -0
- package/src/mdx/fence-meta.ts +61 -0
- package/src/mdx/fence.tsx +155 -44
- package/src/mdx/icon-grid.tsx +29 -0
- package/src/mdx/icon.tsx +2 -2
- package/src/mdx/latex.tsx +14 -7
- package/src/mdx/list.tsx +3 -3
- package/src/mdx/mdx.tsx +7 -1
- package/src/mdx/mermaid.tsx +81 -11
- package/src/mdx/open-api.tsx +396 -239
- package/src/mdx/source.tsx +148 -40
- package/src/mdx/specimen.tsx +69 -0
- package/src/mdx/step.tsx +22 -30
- package/src/mdx/table.tsx +4 -9
- package/src/mdx/tabs.tsx +39 -0
- package/src/mdx/tag.tsx +13 -21
- package/src/mdx/text.tsx +17 -9
- package/src/mdx/type-scale.tsx +28 -0
- package/src/mdx/websocket-playground.tsx +221 -704
- package/src/plugins/code-meta.ts +23 -0
- package/src/plugins/frontmatter-dev-server.ts +6 -2
- package/src/plugins/html-files-middleware.ts +9 -2
- package/src/plugins/html-metadata-injector.ts +4 -3
- package/src/plugins/llm-txt-dev-server.ts +5 -2
- package/src/plugins/llm-txt-generator.ts +2 -1
- package/src/plugins/prerender.ts +7 -4
- package/src/plugins/relative-assets.test.ts +26 -0
- package/src/plugins/relative-assets.ts +80 -0
- package/src/plugins/robots-generator.ts +6 -4
- package/src/plugins/sanitize.test.ts +5 -0
- package/src/plugins/sanitize.ts +2 -0
- package/src/plugins/shared.ts +17 -0
- package/src/plugins/sitemap-generator.ts +2 -1
- package/src/theme.scss +282 -3
- package/src/types/config.ts +3 -0
- package/src/ui/header.tsx +4 -3
- package/src/ui/layout.tsx +22 -4
- package/src/ui/page-meta.tsx +37 -0
- package/src/ui/prev-next-link.tsx +4 -3
- package/src/ui/table-of-content.tsx +11 -3
- package/src/utils/base-path.test.ts +46 -0
- package/src/utils/base-path.ts +41 -0
- package/vite.config.ssr.ts +3 -0
- package/vite.config.ts +3 -0
package/src/mdx/async-api.tsx
CHANGED
|
@@ -1,48 +1,37 @@
|
|
|
1
|
-
import { useState } from "preact/hooks"
|
|
2
|
-
import { APIGroup } from "./api"
|
|
3
|
-
import { PlayButton } from "./api-playground"
|
|
4
|
-
import { Fence } from "./fence"
|
|
5
1
|
import { Properties, Property } from "./open-api"
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
import { Table, Td, Th } from "./table"
|
|
3
|
+
import { WebSocketPanel, type WebSocketServer } from "./websocket-playground"
|
|
4
|
+
|
|
5
|
+
interface Schema {
|
|
6
|
+
$ref?: string
|
|
7
|
+
type?: string
|
|
8
|
+
format?: string
|
|
9
|
+
enum?: unknown[]
|
|
10
|
+
items?: Schema
|
|
11
|
+
properties?: Record<string, Schema>
|
|
12
|
+
required?: string[]
|
|
13
|
+
description?: string
|
|
14
|
+
default?: unknown
|
|
15
|
+
example?: unknown
|
|
16
|
+
}
|
|
10
17
|
|
|
11
18
|
interface AsyncAPIMessage {
|
|
19
|
+
$ref?: string
|
|
12
20
|
name?: string
|
|
13
21
|
title?: string
|
|
14
22
|
summary?: string
|
|
15
23
|
description?: string
|
|
16
24
|
payload?: Schema
|
|
17
25
|
headers?: Schema
|
|
18
|
-
correlationId?: {
|
|
19
|
-
description?: string
|
|
20
|
-
location?: string
|
|
21
|
-
}
|
|
22
|
-
schemaFormat?: string
|
|
23
26
|
contentType?: string
|
|
24
|
-
examples?:
|
|
27
|
+
examples?: unknown[]
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
interface AsyncAPIOperation {
|
|
28
31
|
operationId?: string
|
|
29
32
|
summary?: string
|
|
30
33
|
description?: string
|
|
31
|
-
|
|
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
|
-
}
|
|
34
|
+
message?: AsyncAPIMessage | { oneOf?: AsyncAPIMessage[] }
|
|
46
35
|
}
|
|
47
36
|
|
|
48
37
|
interface AsyncAPIChannel {
|
|
@@ -52,302 +41,142 @@ interface AsyncAPIChannel {
|
|
|
52
41
|
description?: string
|
|
53
42
|
subscribe?: AsyncAPIOperation
|
|
54
43
|
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
44
|
}
|
|
89
45
|
|
|
90
46
|
interface Components {
|
|
91
47
|
schemas?: Record<string, Schema>
|
|
92
48
|
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
49
|
}
|
|
111
50
|
|
|
112
51
|
export interface AsyncAPIJson {
|
|
113
52
|
asyncapi: string
|
|
114
|
-
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
|
-
}>
|
|
53
|
+
info: { title: string; version: string; description?: string }
|
|
54
|
+
servers?: Record<string, WebSocketServer & { host?: string; pathname?: string; protocol?: string; description?: string; title?: string }>
|
|
131
55
|
channels: Record<string, AsyncAPIChannel>
|
|
132
56
|
components?: Components
|
|
133
|
-
defaultContentType?: string
|
|
134
57
|
}
|
|
135
58
|
|
|
59
|
+
type Direction = 'SEND' | 'RECV'
|
|
60
|
+
|
|
136
61
|
function resolveSchema(schema: Schema | undefined, components: Components): Schema {
|
|
137
62
|
if (!schema) return {}
|
|
138
|
-
|
|
139
63
|
if (schema.$ref) {
|
|
140
|
-
const
|
|
141
|
-
return resolveSchema(components.schemas?.[
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (schema.type === 'array' && schema.items) {
|
|
145
|
-
return {
|
|
146
|
-
...schema,
|
|
147
|
-
items: resolveSchema(schema.items, components)
|
|
148
|
-
}
|
|
64
|
+
const name = schema.$ref.replace(/^#\/components\/schemas\//, '')
|
|
65
|
+
return resolveSchema(components.schemas?.[name], components)
|
|
149
66
|
}
|
|
150
|
-
|
|
67
|
+
if (schema.type === 'array' && schema.items) return { ...schema, items: resolveSchema(schema.items, components) }
|
|
151
68
|
if (schema.properties) {
|
|
152
|
-
const
|
|
153
|
-
for (const [key, value] of Object.entries(schema.properties))
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
return {
|
|
157
|
-
...schema,
|
|
158
|
-
properties: resolvedProps
|
|
159
|
-
}
|
|
69
|
+
const properties: Record<string, Schema> = {}
|
|
70
|
+
for (const [key, value] of Object.entries(schema.properties)) properties[key] = resolveSchema(value, components)
|
|
71
|
+
return { ...schema, properties }
|
|
160
72
|
}
|
|
161
|
-
|
|
162
73
|
return schema
|
|
163
74
|
}
|
|
164
75
|
|
|
165
|
-
|
|
166
|
-
|
|
76
|
+
/** Messages an operation carries, with `$ref` and `oneOf` resolved against components. */
|
|
77
|
+
function messagesOf(operation: AsyncAPIOperation | undefined, components: Components): AsyncAPIMessage[] {
|
|
78
|
+
if (!operation?.message) return []
|
|
79
|
+
const list = 'oneOf' in operation.message && operation.message.oneOf ? operation.message.oneOf : [operation.message as AsyncAPIMessage]
|
|
80
|
+
return list.map((message) => {
|
|
81
|
+
if (!message.$ref) return message
|
|
82
|
+
const name = message.$ref.replace(/^#\/components\/messages\//, '')
|
|
83
|
+
return { name, ...(components.messages?.[name] ?? {}) }
|
|
84
|
+
})
|
|
167
85
|
}
|
|
168
86
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
87
|
+
function typeLabel(schema: Schema): string {
|
|
88
|
+
if (schema.enum) return `enum<${schema.type ?? 'string'}>`
|
|
89
|
+
if (schema.type === 'array') return `array<${schema.items ? typeLabel(schema.items) : 'any'}>`
|
|
90
|
+
if (schema.format) return `${schema.type ?? 'string'} · ${schema.format}`
|
|
91
|
+
return schema.type ?? 'object'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* WebSocket operation page: a numbered "Connect" section with the live panel,
|
|
96
|
+
* a "Message types" table (SEND for publish, RECV for subscribe) and the payload fields.
|
|
97
|
+
*/
|
|
98
|
+
export function AsyncAPI({
|
|
99
|
+
asyncAPIJson,
|
|
100
|
+
channel: selectedChannel,
|
|
101
|
+
operation: selectedOperation,
|
|
102
|
+
}: {
|
|
174
103
|
asyncAPIJson: AsyncAPIJson
|
|
175
104
|
channel: string
|
|
176
105
|
operation: 'subscribe' | 'publish'
|
|
177
106
|
}) {
|
|
178
|
-
const { channels, components, servers } = asyncAPIJson
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
107
|
+
const { channels, components = {}, servers } = asyncAPIJson
|
|
108
|
+
const channel = channels[selectedChannel]
|
|
109
|
+
const operation = channel?.[selectedOperation]
|
|
110
|
+
if (!channel || !operation) return null
|
|
111
|
+
|
|
112
|
+
const wsServers: WebSocketServer[] = servers
|
|
113
|
+
? Object.entries(servers).map(([key, server]) => ({
|
|
114
|
+
url: `${server.protocol}://${server.host}${server.pathname || ''}`,
|
|
115
|
+
description: server.description || server.title || key,
|
|
116
|
+
protocol: server.protocol,
|
|
117
|
+
}))
|
|
118
|
+
: []
|
|
119
|
+
|
|
120
|
+
const rows: Array<{ name: string; direction: Direction; description?: string }> = [
|
|
121
|
+
...messagesOf(channel.publish, components).map((m) => ({ name: m.name ?? m.title ?? 'message', direction: 'SEND' as Direction, description: m.summary ?? m.description })),
|
|
122
|
+
...messagesOf(channel.subscribe, components).map((m) => ({ name: m.name ?? m.title ?? 'message', direction: 'RECV' as Direction, description: m.summary ?? m.description })),
|
|
123
|
+
]
|
|
124
|
+
const primary = messagesOf(operation, components)[0]
|
|
125
|
+
const payload = primary?.payload ? resolveSchema(primary.payload, components) : undefined
|
|
126
|
+
const headerExample = primary?.headers ? resolveSchema(primary.headers, components) : undefined
|
|
127
|
+
const defaultHeaders: Record<string, string> = {}
|
|
128
|
+
for (const [name, schema] of Object.entries(headerExample?.properties ?? {})) {
|
|
129
|
+
if (schema.example !== undefined) defaultHeaders[name] = String(schema.example)
|
|
183
130
|
}
|
|
184
131
|
|
|
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
132
|
return (
|
|
193
|
-
<div className="max-w-none w-full">
|
|
194
|
-
<
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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-muted-foreground 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
|
-
})}
|
|
133
|
+
<div className="dory-asyncapi max-w-none w-full">
|
|
134
|
+
{(operation.description || operation.summary) && <p className="dory-lead -mt-4 mb-6">{operation.description || operation.summary}</p>}
|
|
135
|
+
|
|
136
|
+
<h2 id="connect" className="dory-h2 font-heading mb-3 mt-10"><span>Connect</span></h2>
|
|
137
|
+
<WebSocketPanel url={channel.address ?? selectedChannel} servers={wsServers} headers={defaultHeaders} />
|
|
138
|
+
|
|
139
|
+
{rows.length > 0 && (
|
|
140
|
+
<>
|
|
141
|
+
<h2 id="message-types" className="dory-h2 font-heading mb-3 mt-10"><span>Message types</span></h2>
|
|
142
|
+
<Table>
|
|
143
|
+
<thead>
|
|
144
|
+
<tr>
|
|
145
|
+
<Th>Type</Th>
|
|
146
|
+
<Th>Dir</Th>
|
|
147
|
+
<Th>Description</Th>
|
|
148
|
+
</tr>
|
|
149
|
+
</thead>
|
|
150
|
+
<tbody>
|
|
151
|
+
{rows.map((row) => (
|
|
152
|
+
<tr key={`${row.direction}-${row.name}`}>
|
|
153
|
+
<Td><span className="font-mono text-xs">{row.name}</span></Td>
|
|
154
|
+
<Td>
|
|
155
|
+
<span className={row.direction === 'SEND' ? 'font-mono text-xs font-semibold text-[var(--brand-readable,#b65401)]' : 'font-mono text-xs font-semibold text-[var(--primary)]'}>
|
|
156
|
+
{row.direction}
|
|
157
|
+
</span>
|
|
158
|
+
</Td>
|
|
159
|
+
<Td>{row.description}</Td>
|
|
160
|
+
</tr>
|
|
161
|
+
))}
|
|
162
|
+
</tbody>
|
|
163
|
+
</Table>
|
|
164
|
+
</>
|
|
165
|
+
)}
|
|
166
|
+
|
|
167
|
+
{payload?.properties && (
|
|
168
|
+
<>
|
|
169
|
+
<h2 id="payload" className="dory-h2 font-heading mb-3 mt-10"><span>Payload</span></h2>
|
|
170
|
+
{primary?.contentType && <p className="-mt-2 mb-2 font-mono text-xs text-muted-foreground">{primary.contentType}</p>}
|
|
171
|
+
<Properties>
|
|
172
|
+
{Object.entries(payload.properties).map(([name, schema]) => (
|
|
173
|
+
<Property key={name} name={name} type={typeLabel(schema)} required={payload.required?.includes(name)} defaultValue={schema.default}>
|
|
174
|
+
{schema.description}
|
|
175
|
+
</Property>
|
|
176
|
+
))}
|
|
177
|
+
</Properties>
|
|
178
|
+
</>
|
|
179
|
+
)}
|
|
351
180
|
</div>
|
|
352
181
|
)
|
|
353
|
-
}
|
|
182
|
+
}
|
package/src/mdx/callout.tsx
CHANGED
|
@@ -1,28 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import classNames from 'classnames'
|
|
2
2
|
import { Icon } from "./icon"
|
|
3
3
|
|
|
4
|
-
const typeToVariant = {
|
|
5
|
-
note: 'default',
|
|
6
|
-
warning: 'destructive',
|
|
7
|
-
info: 'default',
|
|
8
|
-
tip: 'default',
|
|
9
|
-
check: 'default',
|
|
10
|
-
} as const
|
|
11
|
-
|
|
12
4
|
const icons = {
|
|
13
|
-
note: '
|
|
5
|
+
note: 'Info',
|
|
14
6
|
warning: 'AlertTriangle',
|
|
15
|
-
info: '
|
|
16
|
-
tip: '
|
|
17
|
-
check: '
|
|
7
|
+
info: 'Info',
|
|
8
|
+
tip: 'Star',
|
|
9
|
+
check: 'Check',
|
|
18
10
|
} as const
|
|
19
11
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
12
|
+
// Each type gets a distinct surface and icon badge shape so they stay distinguishable without color.
|
|
13
|
+
const typeStyles = {
|
|
14
|
+
note: { box: 'bg-muted border-[var(--border)]', badge: 'bg-card border border-[var(--border)] text-foreground rounded-md' },
|
|
15
|
+
warning: { box: 'bg-background border-[var(--highlight)]', badge: 'bg-[var(--highlight)] text-[var(--highlight-foreground)] rounded-[5px]' },
|
|
16
|
+
info: { box: 'bg-accent border-transparent', badge: 'bg-[var(--primary)] text-white rounded-[5px]' },
|
|
17
|
+
tip: { box: 'bg-[var(--success-muted)] border-transparent', badge: 'border border-foreground/70 text-foreground rounded-md' },
|
|
18
|
+
check: { box: 'bg-background border-[var(--border)]', badge: 'bg-[var(--success-muted)] text-foreground rounded-[5px]' },
|
|
26
19
|
}
|
|
27
20
|
|
|
28
21
|
export function Callout({
|
|
@@ -34,12 +27,17 @@ export function Callout({
|
|
|
34
27
|
children: React.ReactNode
|
|
35
28
|
type?: keyof typeof icons
|
|
36
29
|
}) {
|
|
30
|
+
const styles = typeStyles[type]
|
|
37
31
|
return (
|
|
38
|
-
<
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
32
|
+
<div role="note" className={classNames('dory-callout not-prose my-5 flex gap-3 rounded-lg border px-4 py-3 text-foreground', styles.box)}>
|
|
33
|
+
<span className={classNames('mt-px flex size-6 shrink-0 items-center justify-center', styles.badge)}>
|
|
34
|
+
<Icon icon={icons[type]} className="h-3 w-3 !stroke-current" />
|
|
35
|
+
</span>
|
|
36
|
+
<div className="min-w-0 flex-1">
|
|
37
|
+
<p className="font-semibold leading-snug">{title}</p>
|
|
38
|
+
<div className="leading-relaxed [&_p]:mb-0">{children}</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
43
41
|
)
|
|
44
42
|
}
|
|
45
43
|
|
package/src/mdx/card.tsx
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
import classNames from "classnames";
|
|
2
|
-
import {
|
|
3
|
-
Card as UXCard,
|
|
4
|
-
CardHeader,
|
|
5
|
-
CardTitle,
|
|
6
|
-
CardDescription,
|
|
7
|
-
CardContent
|
|
8
|
-
} from '@clidey/ux'
|
|
2
|
+
import { withBase } from '../utils/base-path';
|
|
9
3
|
import { Icon } from "./icon";
|
|
10
4
|
import type { ComponentChildren } from "preact";
|
|
11
5
|
|
|
12
|
-
export function CardGroup({ children, cols = 2 }: { children: React.ReactNode; cols?: number }) {
|
|
6
|
+
export function CardGroup({ children, cols = 2, numbered = false }: { children: React.ReactNode; cols?: number; numbered?: boolean }) {
|
|
13
7
|
return (
|
|
14
8
|
<div className={classNames(
|
|
15
|
-
'not-prose my-
|
|
9
|
+
'not-prose my-6 grid grid-cols-1 gap-3',
|
|
10
|
+
numbered && 'dory-cards-numbered',
|
|
16
11
|
cols === 2 && 'sm:grid-cols-2',
|
|
17
12
|
cols === 3 && 'sm:grid-cols-3',
|
|
18
13
|
cols === 4 && 'sm:grid-cols-4'
|
|
@@ -36,19 +31,22 @@ export function Card({
|
|
|
36
31
|
children?: ComponentChildren
|
|
37
32
|
}) {
|
|
38
33
|
return (
|
|
39
|
-
<
|
|
40
|
-
{href && <a href={href} className="absolute inset-0 z-10" aria-label={title} />}
|
|
41
|
-
|
|
42
|
-
<div className="flex items-
|
|
43
|
-
{icon
|
|
44
|
-
|
|
34
|
+
<div className="dory-card group relative flex flex-col rounded-lg border border-[var(--border)] bg-card p-4 transition-shadow hover:shadow-[0_2px_10px_rgb(14_34_64/0.07)]">
|
|
35
|
+
{href && <a href={withBase(href)} className="absolute inset-0 z-10 rounded-lg" aria-label={title} />}
|
|
36
|
+
{(icon || href) && (
|
|
37
|
+
<div className="mb-3 flex items-start justify-between">
|
|
38
|
+
{icon ? (
|
|
39
|
+
<span className="flex size-8 items-center justify-center rounded-md bg-accent text-[var(--primary)]">
|
|
40
|
+
<Icon icon={icon} className="h-4 w-4 !stroke-current" />
|
|
41
|
+
</span>
|
|
42
|
+
) : <span />}
|
|
43
|
+
{href && <Icon icon="ExternalLink" className="h-3.5 w-3.5 !stroke-[var(--muted-foreground)]" />}
|
|
45
44
|
</div>
|
|
46
|
-
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
</UXCard>
|
|
45
|
+
)}
|
|
46
|
+
<p className="font-heading text-[15px] font-semibold tracking-tight text-foreground">{title}</p>
|
|
47
|
+
<div className="mt-1 text-[13px] leading-normal text-muted-foreground [&_p]:mb-0">
|
|
48
|
+
{description ?? children}
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
53
51
|
)
|
|
54
52
|
}
|
package/src/mdx/chip.tsx
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Wrapping row of Chips with even gaps. */
|
|
2
|
+
export function ChipGroup({ children }: { children: preact.ComponentChildren }) {
|
|
3
|
+
return <div className="not-prose my-4 flex flex-wrap gap-2">{children}</div>
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/** Pill label with a brand-colored dot, for short lists such as supported databases. Links when `href` is set. */
|
|
7
|
+
export function Chip({ children, href }: { children: preact.ComponentChildren; href?: string }) {
|
|
8
|
+
const className = 'inline-flex items-center gap-2 rounded-full border border-[var(--border)] bg-card px-3.5 py-1.5 text-[13px] font-medium text-foreground'
|
|
9
|
+
const dot = <span aria-hidden="true" className="size-1.5 shrink-0 rounded-full bg-[var(--primary)]" />
|
|
10
|
+
if (href) {
|
|
11
|
+
return <a href={href} className={`${className} hover:border-[var(--primary)]`}>{dot}{children}</a>
|
|
12
|
+
}
|
|
13
|
+
return <span className={className}>{dot}{children}</span>
|
|
14
|
+
}
|