radiant-docs 0.1.59 → 0.1.61
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/package.json +1 -1
- package/template/astro.config.mjs +24 -2
- package/template/package-lock.json +121 -508
- package/template/package.json +3 -2
- package/template/scripts/generate-og-images.mjs +338 -6
- package/template/scripts/generate-og-metadata.mjs +29 -0
- package/template/src/components/Footer.astro +1 -1
- package/template/src/components/Header.astro +3 -10
- package/template/src/components/OpenApiPage.astro +181 -843
- package/template/src/components/SidebarGroup.astro +1 -1
- package/template/src/components/ThemeSwitcher.astro +5 -15
- package/template/src/components/chat/AssistantEmbedPanel.tsx +18 -7
- package/template/src/components/endpoint/PlaygroundBar.astro +54 -9
- package/template/src/components/endpoint/PlaygroundField.astro +1 -1
- package/template/src/components/endpoint/PlaygroundForm.astro +9 -5
- package/template/src/components/endpoint/RequestSnippets.astro +6 -1
- package/template/src/components/endpoint/ResponseFieldTree.astro +17 -13
- package/template/src/components/endpoint/ResponseFields.astro +4 -6
- package/template/src/components/endpoint/ResponseSnippets.astro +6 -1
- package/template/src/components/sidebar/SidebarEndpointLink.astro +9 -12
- package/template/src/components/sidebar/SidebarOpenApi.astro +3 -9
- package/template/src/components/ui/Field.astro +18 -15
- package/template/src/components/ui/Tag.astro +16 -2
- package/template/src/layouts/Layout.astro +6 -12
- package/template/src/lib/ai-artifacts.ts +792 -0
- package/template/src/lib/mdx/remark-resolve-internal-links.ts +22 -8
- package/template/src/lib/oas.ts +5 -1
- package/template/src/lib/openapi/operation-doc.ts +1150 -0
- package/template/src/lib/page-description.ts +20 -0
- package/template/src/lib/routes.ts +73 -18
- package/template/src/lib/utils.ts +11 -0
- package/template/src/pages/[...slug]/index.md.ts +35 -0
- package/template/src/pages/[...spec].json.ts +33 -0
- package/template/src/pages/[...spec].yaml.ts +33 -0
- package/template/src/pages/[...spec].yml.ts +33 -0
- package/template/src/pages/index.md.ts +17 -0
- package/template/src/pages/llms-full.txt.ts +11 -0
- package/template/src/pages/llms.txt.ts +11 -0
- package/template/src/styles/global.css +18 -15
- package/template/src/styles/vaul.css +0 -255
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import Layout from "../layouts/Layout.astro";
|
|
3
3
|
import type { OpenApiRoute } from "../lib/routes";
|
|
4
|
-
import { loadOpenApiSpec } from "../lib/validation";
|
|
5
4
|
import RequestSnippets from "./endpoint/RequestSnippets.astro";
|
|
6
|
-
import Oas from "oas";
|
|
7
|
-
import type { HttpMethods } from "oas/types";
|
|
8
5
|
import ResponseSnippets from "./endpoint/ResponseSnippets.astro";
|
|
9
6
|
import { renderMarkdown } from "../lib/utils";
|
|
10
7
|
import ListChevronsToggle from "./ui/ListChevronsToggle.astro";
|
|
@@ -13,842 +10,35 @@ import ResponseFieldTree from "./endpoint/ResponseFieldTree.astro";
|
|
|
13
10
|
import PlaygroundBar from "./endpoint/PlaygroundBar.astro";
|
|
14
11
|
import PlaygroundForm from "./endpoint/PlaygroundForm.astro";
|
|
15
12
|
import PlaygroundButton from "./endpoint/PlaygroundButton.astro";
|
|
16
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
getOpenApiOperationDoc,
|
|
15
|
+
OPENAPI_REQUEST_SECTION_LABELS,
|
|
16
|
+
type OpenApiRequestFields,
|
|
17
|
+
} from "../lib/openapi/operation-doc";
|
|
17
18
|
|
|
18
19
|
interface Props {
|
|
19
20
|
route: OpenApiRoute;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// Load and parse the OpenAPI file (handles both URLs and local files)
|
|
25
|
-
const api = await getOasInstance(route.filePath);
|
|
26
|
-
|
|
27
|
-
const definition = api.getDefinition();
|
|
28
|
-
const serverUrl = definition.servers?.[0]?.url;
|
|
29
|
-
|
|
30
|
-
const operation = api.operation(
|
|
31
|
-
route.openApiPath,
|
|
32
|
-
route.openApiMethod as HttpMethods,
|
|
33
|
-
);
|
|
23
|
+
type RequestFields = OpenApiRequestFields;
|
|
34
24
|
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
25
|
+
const { route } = Astro.props;
|
|
26
|
+
const operationDoc = await getOpenApiOperationDoc(route);
|
|
27
|
+
const {
|
|
28
|
+
api,
|
|
29
|
+
responses,
|
|
30
|
+
requestFields,
|
|
31
|
+
requestSectionVariants,
|
|
32
|
+
bodyDescription,
|
|
33
|
+
bodyDefaultKind,
|
|
34
|
+
serverUrl,
|
|
35
|
+
} = operationDoc;
|
|
36
|
+
const title = operationDoc.title;
|
|
37
|
+
const description = operationDoc.description;
|
|
38
|
+
const headers: Record<string, string> = OPENAPI_REQUEST_SECTION_LABELS;
|
|
38
39
|
const formattedDescription = description
|
|
39
40
|
? await renderMarkdown(description)
|
|
40
41
|
: null;
|
|
41
|
-
|
|
42
|
-
export const headers: { [key: string]: string } = {
|
|
43
|
-
header: "Header",
|
|
44
|
-
cookie: "Cookie",
|
|
45
|
-
path: "Path Parameters",
|
|
46
|
-
query: "Query Parameters",
|
|
47
|
-
body: "Body",
|
|
48
|
-
};
|
|
49
|
-
export interface FieldVariant {
|
|
50
|
-
label: string;
|
|
51
|
-
fields: Field[];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface Field {
|
|
55
|
-
name: string;
|
|
56
|
-
required: boolean;
|
|
57
|
-
type: string;
|
|
58
|
-
description: string;
|
|
59
|
-
enum?: (string | number)[];
|
|
60
|
-
minLength?: number;
|
|
61
|
-
maxLength?: number;
|
|
62
|
-
minimum?: number;
|
|
63
|
-
maximum?: number;
|
|
64
|
-
exclusiveMinimum?: number;
|
|
65
|
-
exclusiveMaximum?: number;
|
|
66
|
-
hasDefault?: boolean;
|
|
67
|
-
defaultValue?: unknown;
|
|
68
|
-
isArray?: boolean;
|
|
69
|
-
style?: string;
|
|
70
|
-
explode?: boolean;
|
|
71
|
-
nested?: Field[];
|
|
72
|
-
variants?: FieldVariant[];
|
|
73
|
-
variantType?: "oneOf" | "anyOf";
|
|
74
|
-
isAdditionalProperty?: boolean;
|
|
75
|
-
mapKnownKeys?: string[];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export interface RequestFields {
|
|
79
|
-
header: Field[];
|
|
80
|
-
cookie: Field[];
|
|
81
|
-
path: Field[];
|
|
82
|
-
query: Field[];
|
|
83
|
-
body: Field[];
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export interface RequestSectionVariantData {
|
|
87
|
-
variants: FieldVariant[];
|
|
88
|
-
variantType: "oneOf" | "anyOf";
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
type BodyDefaultKind = "object" | "array";
|
|
92
|
-
|
|
93
|
-
const requestFields: RequestFields = {
|
|
94
|
-
header: [] as Field[],
|
|
95
|
-
cookie: [] as Field[],
|
|
96
|
-
path: [] as Field[],
|
|
97
|
-
query: [] as Field[],
|
|
98
|
-
body: [] as Field[],
|
|
99
|
-
};
|
|
100
|
-
const requestSectionVariants: Partial<
|
|
101
|
-
Record<keyof RequestFields, RequestSectionVariantData>
|
|
102
|
-
> = {};
|
|
103
|
-
let bodyDescription = "";
|
|
104
|
-
let bodyDefaultKind: BodyDefaultKind | undefined = undefined;
|
|
105
|
-
|
|
106
|
-
function getSchemaVariants(schema: any): any[] {
|
|
107
|
-
if (!schema) return [];
|
|
108
|
-
if (Array.isArray(schema.oneOf)) return schema.oneOf;
|
|
109
|
-
if (Array.isArray(schema.anyOf)) return schema.anyOf;
|
|
110
|
-
return [];
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function getSchemaVariantType(schema: any): "oneOf" | "anyOf" | undefined {
|
|
114
|
-
if (Array.isArray(schema?.oneOf) && schema.oneOf.length > 0) return "oneOf";
|
|
115
|
-
if (Array.isArray(schema?.anyOf) && schema.anyOf.length > 0) return "anyOf";
|
|
116
|
-
return undefined;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function getAdditionalPropertiesSchema(schema: any): any {
|
|
120
|
-
if (!schema || typeof schema !== "object") return undefined;
|
|
121
|
-
|
|
122
|
-
let additionalProperties = schema.additionalProperties;
|
|
123
|
-
if (Array.isArray(schema.allOf)) {
|
|
124
|
-
schema.allOf.forEach((part: any) => {
|
|
125
|
-
const partAdditional = getAdditionalPropertiesSchema(part);
|
|
126
|
-
if (partAdditional !== undefined) {
|
|
127
|
-
additionalProperties = partAdditional;
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return additionalProperties;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function getSchemaEnumValues(schema: any): (string | number)[] | undefined {
|
|
136
|
-
if (!schema) return undefined;
|
|
137
|
-
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
138
|
-
const values = schema.enum.filter(
|
|
139
|
-
(value: unknown): value is string | number =>
|
|
140
|
-
typeof value === "string" || typeof value === "number",
|
|
141
|
-
);
|
|
142
|
-
return values.length > 0 ? values : undefined;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const variants = getSchemaVariants(schema);
|
|
146
|
-
if (variants.length === 0) return undefined;
|
|
147
|
-
|
|
148
|
-
const deduped = new Map<string, string | number>();
|
|
149
|
-
variants.forEach((variant) => {
|
|
150
|
-
const variantEnum = getSchemaEnumValues(variant);
|
|
151
|
-
variantEnum?.forEach((value) => {
|
|
152
|
-
deduped.set(`${typeof value}:${String(value)}`, value);
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
return deduped.size > 0 ? Array.from(deduped.values()) : undefined;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function parseFiniteNumber(value: unknown): number | undefined {
|
|
160
|
-
return typeof value === "number" && Number.isFinite(value)
|
|
161
|
-
? value
|
|
162
|
-
: undefined;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function parseNonNegativeInteger(value: unknown): number | undefined {
|
|
166
|
-
if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
|
|
167
|
-
if (!Number.isInteger(value) || value < 0) return undefined;
|
|
168
|
-
return value;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function getDirectSchemaNumericConstraints(schema: any): {
|
|
172
|
-
minimum?: number;
|
|
173
|
-
maximum?: number;
|
|
174
|
-
exclusiveMinimum?: number;
|
|
175
|
-
exclusiveMaximum?: number;
|
|
176
|
-
} {
|
|
177
|
-
const minimum = parseFiniteNumber(schema?.minimum);
|
|
178
|
-
const maximum = parseFiniteNumber(schema?.maximum);
|
|
179
|
-
const exclusiveMinimumValue = parseFiniteNumber(schema?.exclusiveMinimum);
|
|
180
|
-
const exclusiveMaximumValue = parseFiniteNumber(schema?.exclusiveMaximum);
|
|
181
|
-
|
|
182
|
-
const exclusiveMinimum =
|
|
183
|
-
exclusiveMinimumValue !== undefined
|
|
184
|
-
? exclusiveMinimumValue
|
|
185
|
-
: schema?.exclusiveMinimum === true
|
|
186
|
-
? minimum
|
|
187
|
-
: undefined;
|
|
188
|
-
const exclusiveMaximum =
|
|
189
|
-
exclusiveMaximumValue !== undefined
|
|
190
|
-
? exclusiveMaximumValue
|
|
191
|
-
: schema?.exclusiveMaximum === true
|
|
192
|
-
? maximum
|
|
193
|
-
: undefined;
|
|
194
|
-
|
|
195
|
-
const constraints: {
|
|
196
|
-
minimum?: number;
|
|
197
|
-
maximum?: number;
|
|
198
|
-
exclusiveMinimum?: number;
|
|
199
|
-
exclusiveMaximum?: number;
|
|
200
|
-
} = {};
|
|
201
|
-
|
|
202
|
-
if (exclusiveMinimum !== undefined) {
|
|
203
|
-
constraints.exclusiveMinimum = exclusiveMinimum;
|
|
204
|
-
} else if (minimum !== undefined) {
|
|
205
|
-
constraints.minimum = minimum;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (exclusiveMaximum !== undefined) {
|
|
209
|
-
constraints.exclusiveMaximum = exclusiveMaximum;
|
|
210
|
-
} else if (maximum !== undefined) {
|
|
211
|
-
constraints.maximum = maximum;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return constraints;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function mergeConjunctiveNumericConstraints(
|
|
218
|
-
base: {
|
|
219
|
-
minimum?: number;
|
|
220
|
-
maximum?: number;
|
|
221
|
-
exclusiveMinimum?: number;
|
|
222
|
-
exclusiveMaximum?: number;
|
|
223
|
-
},
|
|
224
|
-
next: {
|
|
225
|
-
minimum?: number;
|
|
226
|
-
maximum?: number;
|
|
227
|
-
exclusiveMinimum?: number;
|
|
228
|
-
exclusiveMaximum?: number;
|
|
229
|
-
},
|
|
230
|
-
): {
|
|
231
|
-
minimum?: number;
|
|
232
|
-
maximum?: number;
|
|
233
|
-
exclusiveMinimum?: number;
|
|
234
|
-
exclusiveMaximum?: number;
|
|
235
|
-
} {
|
|
236
|
-
const toLowerBound = (constraint: typeof base) => {
|
|
237
|
-
if (constraint.exclusiveMinimum !== undefined) {
|
|
238
|
-
return { value: constraint.exclusiveMinimum, exclusive: true };
|
|
239
|
-
}
|
|
240
|
-
if (constraint.minimum !== undefined) {
|
|
241
|
-
return { value: constraint.minimum, exclusive: false };
|
|
242
|
-
}
|
|
243
|
-
return undefined;
|
|
244
|
-
};
|
|
245
|
-
const toUpperBound = (constraint: typeof base) => {
|
|
246
|
-
if (constraint.exclusiveMaximum !== undefined) {
|
|
247
|
-
return { value: constraint.exclusiveMaximum, exclusive: true };
|
|
248
|
-
}
|
|
249
|
-
if (constraint.maximum !== undefined) {
|
|
250
|
-
return { value: constraint.maximum, exclusive: false };
|
|
251
|
-
}
|
|
252
|
-
return undefined;
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
const pickStricterLower = (
|
|
256
|
-
first?: { value: number; exclusive: boolean },
|
|
257
|
-
second?: { value: number; exclusive: boolean },
|
|
258
|
-
) => {
|
|
259
|
-
if (!first) return second;
|
|
260
|
-
if (!second) return first;
|
|
261
|
-
if (first.value > second.value) return first;
|
|
262
|
-
if (second.value > first.value) return second;
|
|
263
|
-
return {
|
|
264
|
-
value: first.value,
|
|
265
|
-
exclusive: first.exclusive || second.exclusive,
|
|
266
|
-
};
|
|
267
|
-
};
|
|
268
|
-
const pickStricterUpper = (
|
|
269
|
-
first?: { value: number; exclusive: boolean },
|
|
270
|
-
second?: { value: number; exclusive: boolean },
|
|
271
|
-
) => {
|
|
272
|
-
if (!first) return second;
|
|
273
|
-
if (!second) return first;
|
|
274
|
-
if (first.value < second.value) return first;
|
|
275
|
-
if (second.value < first.value) return second;
|
|
276
|
-
return {
|
|
277
|
-
value: first.value,
|
|
278
|
-
exclusive: first.exclusive || second.exclusive,
|
|
279
|
-
};
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
const lower = pickStricterLower(toLowerBound(base), toLowerBound(next));
|
|
283
|
-
const upper = pickStricterUpper(toUpperBound(base), toUpperBound(next));
|
|
284
|
-
|
|
285
|
-
const merged: {
|
|
286
|
-
minimum?: number;
|
|
287
|
-
maximum?: number;
|
|
288
|
-
exclusiveMinimum?: number;
|
|
289
|
-
exclusiveMaximum?: number;
|
|
290
|
-
} = {};
|
|
291
|
-
if (lower) {
|
|
292
|
-
if (lower.exclusive) merged.exclusiveMinimum = lower.value;
|
|
293
|
-
else merged.minimum = lower.value;
|
|
294
|
-
}
|
|
295
|
-
if (upper) {
|
|
296
|
-
if (upper.exclusive) merged.exclusiveMaximum = upper.value;
|
|
297
|
-
else merged.maximum = upper.value;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
return merged;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function getSchemaNumericConstraints(
|
|
304
|
-
schema: any,
|
|
305
|
-
seen = new Set<any>(),
|
|
306
|
-
): {
|
|
307
|
-
minimum?: number;
|
|
308
|
-
maximum?: number;
|
|
309
|
-
exclusiveMinimum?: number;
|
|
310
|
-
exclusiveMaximum?: number;
|
|
311
|
-
} {
|
|
312
|
-
if (!schema || typeof schema !== "object") return {};
|
|
313
|
-
if (seen.has(schema)) return {};
|
|
314
|
-
|
|
315
|
-
const nextSeen = new Set(seen);
|
|
316
|
-
nextSeen.add(schema);
|
|
317
|
-
|
|
318
|
-
let constraints = getDirectSchemaNumericConstraints(schema);
|
|
319
|
-
|
|
320
|
-
if (Array.isArray(schema.allOf)) {
|
|
321
|
-
schema.allOf.forEach((part: any) => {
|
|
322
|
-
constraints = mergeConjunctiveNumericConstraints(
|
|
323
|
-
constraints,
|
|
324
|
-
getSchemaNumericConstraints(part, nextSeen),
|
|
325
|
-
);
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const variants = getSchemaVariants(schema);
|
|
330
|
-
if (variants.length > 0) {
|
|
331
|
-
const variantConstraints = variants.map((variant) =>
|
|
332
|
-
getSchemaNumericConstraints(variant, nextSeen),
|
|
333
|
-
);
|
|
334
|
-
const shared: {
|
|
335
|
-
minimum?: number;
|
|
336
|
-
maximum?: number;
|
|
337
|
-
exclusiveMinimum?: number;
|
|
338
|
-
exclusiveMaximum?: number;
|
|
339
|
-
} = {};
|
|
340
|
-
const keys = [
|
|
341
|
-
"minimum",
|
|
342
|
-
"maximum",
|
|
343
|
-
"exclusiveMinimum",
|
|
344
|
-
"exclusiveMaximum",
|
|
345
|
-
] as const;
|
|
346
|
-
|
|
347
|
-
keys.forEach((key) => {
|
|
348
|
-
const firstValue = variantConstraints[0]?.[key];
|
|
349
|
-
if (firstValue === undefined) return;
|
|
350
|
-
const allMatch = variantConstraints.every(
|
|
351
|
-
(variantConstraint) => variantConstraint[key] === firstValue,
|
|
352
|
-
);
|
|
353
|
-
if (allMatch) {
|
|
354
|
-
shared[key] = firstValue;
|
|
355
|
-
}
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
constraints = mergeConjunctiveNumericConstraints(constraints, shared);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
return constraints;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
function getDirectSchemaStringLengthConstraints(schema: any): {
|
|
365
|
-
minLength?: number;
|
|
366
|
-
maxLength?: number;
|
|
367
|
-
} {
|
|
368
|
-
const minLength = parseNonNegativeInteger(schema?.minLength);
|
|
369
|
-
const maxLength = parseNonNegativeInteger(schema?.maxLength);
|
|
370
|
-
|
|
371
|
-
const constraints: {
|
|
372
|
-
minLength?: number;
|
|
373
|
-
maxLength?: number;
|
|
374
|
-
} = {};
|
|
375
|
-
if (minLength !== undefined) constraints.minLength = minLength;
|
|
376
|
-
if (maxLength !== undefined) constraints.maxLength = maxLength;
|
|
377
|
-
|
|
378
|
-
return constraints;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
function mergeConjunctiveStringLengthConstraints(
|
|
382
|
-
base: {
|
|
383
|
-
minLength?: number;
|
|
384
|
-
maxLength?: number;
|
|
385
|
-
},
|
|
386
|
-
next: {
|
|
387
|
-
minLength?: number;
|
|
388
|
-
maxLength?: number;
|
|
389
|
-
},
|
|
390
|
-
): {
|
|
391
|
-
minLength?: number;
|
|
392
|
-
maxLength?: number;
|
|
393
|
-
} {
|
|
394
|
-
const merged: {
|
|
395
|
-
minLength?: number;
|
|
396
|
-
maxLength?: number;
|
|
397
|
-
} = {};
|
|
398
|
-
|
|
399
|
-
const minLengthCandidates = [base.minLength, next.minLength].filter(
|
|
400
|
-
(value): value is number => value !== undefined,
|
|
401
|
-
);
|
|
402
|
-
const maxLengthCandidates = [base.maxLength, next.maxLength].filter(
|
|
403
|
-
(value): value is number => value !== undefined,
|
|
404
|
-
);
|
|
405
|
-
|
|
406
|
-
if (minLengthCandidates.length > 0) {
|
|
407
|
-
merged.minLength = Math.max(...minLengthCandidates);
|
|
408
|
-
}
|
|
409
|
-
if (maxLengthCandidates.length > 0) {
|
|
410
|
-
merged.maxLength = Math.min(...maxLengthCandidates);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
return merged;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
function getSchemaStringLengthConstraints(
|
|
417
|
-
schema: any,
|
|
418
|
-
seen = new Set<any>(),
|
|
419
|
-
): {
|
|
420
|
-
minLength?: number;
|
|
421
|
-
maxLength?: number;
|
|
422
|
-
} {
|
|
423
|
-
if (!schema || typeof schema !== "object") return {};
|
|
424
|
-
if (seen.has(schema)) return {};
|
|
425
|
-
|
|
426
|
-
const nextSeen = new Set(seen);
|
|
427
|
-
nextSeen.add(schema);
|
|
428
|
-
|
|
429
|
-
let constraints = getDirectSchemaStringLengthConstraints(schema);
|
|
430
|
-
|
|
431
|
-
if (Array.isArray(schema.allOf)) {
|
|
432
|
-
schema.allOf.forEach((part: any) => {
|
|
433
|
-
constraints = mergeConjunctiveStringLengthConstraints(
|
|
434
|
-
constraints,
|
|
435
|
-
getSchemaStringLengthConstraints(part, nextSeen),
|
|
436
|
-
);
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
const variants = getSchemaVariants(schema);
|
|
441
|
-
if (variants.length > 0) {
|
|
442
|
-
const variantConstraints = variants.map((variant) =>
|
|
443
|
-
getSchemaStringLengthConstraints(variant, nextSeen),
|
|
444
|
-
);
|
|
445
|
-
const shared: {
|
|
446
|
-
minLength?: number;
|
|
447
|
-
maxLength?: number;
|
|
448
|
-
} = {};
|
|
449
|
-
|
|
450
|
-
(["minLength", "maxLength"] as const).forEach((key) => {
|
|
451
|
-
const firstValue = variantConstraints[0]?.[key];
|
|
452
|
-
if (firstValue === undefined) return;
|
|
453
|
-
const allMatch = variantConstraints.every(
|
|
454
|
-
(variantConstraint) => variantConstraint[key] === firstValue,
|
|
455
|
-
);
|
|
456
|
-
if (allMatch) {
|
|
457
|
-
shared[key] = firstValue;
|
|
458
|
-
}
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
constraints = mergeConjunctiveStringLengthConstraints(constraints, shared);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
return constraints;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
function getSchemaTypeLabel(schema: any): string {
|
|
468
|
-
if (!schema) return "unknown";
|
|
469
|
-
|
|
470
|
-
const variants = getSchemaVariants(schema);
|
|
471
|
-
if (variants.length > 0) {
|
|
472
|
-
const labels = Array.from(
|
|
473
|
-
new Set(
|
|
474
|
-
variants
|
|
475
|
-
.map((variant) => getSchemaTypeLabel(variant))
|
|
476
|
-
.filter((label) => label.length > 0),
|
|
477
|
-
),
|
|
478
|
-
);
|
|
479
|
-
if (labels.length === 0) return "unknown";
|
|
480
|
-
return labels.length === 1 ? labels[0] : labels.join(" | ");
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
if (schema.type === "array") {
|
|
484
|
-
const itemSchema = schema.items;
|
|
485
|
-
if (!itemSchema) return "unknown[]";
|
|
486
|
-
const itemType = getSchemaTypeLabel(itemSchema);
|
|
487
|
-
if (itemType.includes(" | ")) return `(${itemType})[]`;
|
|
488
|
-
return `${itemType}[]`;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
492
|
-
return `enum<${schema.type || "string"}>`;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
if (schema.format && schema.type) {
|
|
496
|
-
return `${schema.type} (${schema.format})`;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
if (schema.type) return schema.type;
|
|
500
|
-
if (schema.properties || schema.allOf || schema.oneOf || schema.anyOf) {
|
|
501
|
-
return "object";
|
|
502
|
-
}
|
|
503
|
-
return "unknown";
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
function getSchemaStructuralType(schema: any): BodyDefaultKind | undefined {
|
|
507
|
-
if (!schema || typeof schema !== "object") return undefined;
|
|
508
|
-
|
|
509
|
-
if (schema.type === "object") return "object";
|
|
510
|
-
if (schema.type === "array") return "array";
|
|
511
|
-
|
|
512
|
-
const variants = getSchemaVariants(schema);
|
|
513
|
-
if (variants.length > 0) {
|
|
514
|
-
const variantTypes = Array.from(
|
|
515
|
-
new Set(
|
|
516
|
-
variants
|
|
517
|
-
.map((variant) => getSchemaStructuralType(variant))
|
|
518
|
-
.filter(
|
|
519
|
-
(variantType): variantType is BodyDefaultKind =>
|
|
520
|
-
variantType === "object" || variantType === "array",
|
|
521
|
-
),
|
|
522
|
-
),
|
|
523
|
-
);
|
|
524
|
-
if (variantTypes.length === 1) return variantTypes[0];
|
|
525
|
-
return undefined;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (
|
|
529
|
-
(schema.properties && typeof schema.properties === "object") ||
|
|
530
|
-
schema.additionalProperties !== undefined
|
|
531
|
-
) {
|
|
532
|
-
return "object";
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
if (schema.items) return "array";
|
|
536
|
-
|
|
537
|
-
if (Array.isArray(schema.allOf) && schema.allOf.length > 0) {
|
|
538
|
-
const allOfTypes = Array.from(
|
|
539
|
-
new Set(
|
|
540
|
-
schema.allOf
|
|
541
|
-
.map((part: any) => getSchemaStructuralType(part))
|
|
542
|
-
.filter(
|
|
543
|
-
(partType): partType is BodyDefaultKind =>
|
|
544
|
-
partType === "object" || partType === "array",
|
|
545
|
-
),
|
|
546
|
-
),
|
|
547
|
-
);
|
|
548
|
-
if (allOfTypes.length === 1) return allOfTypes[0];
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
return undefined;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
function getTopLevelObjectShape(schema: any): {
|
|
555
|
-
properties: Record<string, any>;
|
|
556
|
-
required: Set<string>;
|
|
557
|
-
hasObjectShape: boolean;
|
|
558
|
-
} {
|
|
559
|
-
if (!schema) {
|
|
560
|
-
return {
|
|
561
|
-
properties: {},
|
|
562
|
-
required: new Set<string>(),
|
|
563
|
-
hasObjectShape: false,
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
let properties: Record<string, any> = {};
|
|
568
|
-
const required = new Set<string>(
|
|
569
|
-
Array.isArray(schema.required) ? schema.required : [],
|
|
570
|
-
);
|
|
571
|
-
let hasObjectShape = false;
|
|
572
|
-
|
|
573
|
-
if (schema.properties && typeof schema.properties === "object") {
|
|
574
|
-
properties = { ...properties, ...schema.properties };
|
|
575
|
-
hasObjectShape = true;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
if (Array.isArray(schema.allOf)) {
|
|
579
|
-
schema.allOf.forEach((part: any) => {
|
|
580
|
-
const partShape = getTopLevelObjectShape(part);
|
|
581
|
-
if (!partShape.hasObjectShape) return;
|
|
582
|
-
|
|
583
|
-
properties = { ...properties, ...partShape.properties };
|
|
584
|
-
partShape.required.forEach((name) => required.add(name));
|
|
585
|
-
hasObjectShape = true;
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
const variants = getSchemaVariants(schema);
|
|
590
|
-
if (variants.length > 0) {
|
|
591
|
-
const variantShapes = variants
|
|
592
|
-
.map((variant) => getTopLevelObjectShape(variant))
|
|
593
|
-
.filter((shape) => shape.hasObjectShape);
|
|
594
|
-
|
|
595
|
-
if (variantShapes.length > 0) {
|
|
596
|
-
variantShapes.forEach((shape) => {
|
|
597
|
-
properties = { ...properties, ...shape.properties };
|
|
598
|
-
});
|
|
599
|
-
|
|
600
|
-
const [firstShape, ...restShapes] = variantShapes;
|
|
601
|
-
const requiredAcrossVariants = new Set<string>(firstShape.required);
|
|
602
|
-
restShapes.forEach((shape) => {
|
|
603
|
-
Array.from(requiredAcrossVariants).forEach((name) => {
|
|
604
|
-
if (!shape.required.has(name)) {
|
|
605
|
-
requiredAcrossVariants.delete(name);
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
});
|
|
609
|
-
requiredAcrossVariants.forEach((name) => required.add(name));
|
|
610
|
-
hasObjectShape = true;
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
return { properties, required, hasObjectShape };
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
function extractRequestSchemaContent(
|
|
618
|
-
schema: any,
|
|
619
|
-
seen = new Set<any>(),
|
|
620
|
-
): {
|
|
621
|
-
fields: Field[];
|
|
622
|
-
variants?: FieldVariant[];
|
|
623
|
-
variantType?: "oneOf" | "anyOf";
|
|
624
|
-
} {
|
|
625
|
-
if (!schema || typeof schema !== "object") return { fields: [] };
|
|
626
|
-
if (seen.has(schema)) return { fields: [] };
|
|
627
|
-
|
|
628
|
-
const nextSeen = new Set(seen);
|
|
629
|
-
nextSeen.add(schema);
|
|
630
|
-
|
|
631
|
-
if (schema.type === "array" && schema.items) {
|
|
632
|
-
return extractRequestSchemaContent(schema.items, nextSeen);
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
const baseSchema = { ...schema };
|
|
636
|
-
delete baseSchema.oneOf;
|
|
637
|
-
delete baseSchema.anyOf;
|
|
638
|
-
|
|
639
|
-
const shape = getTopLevelObjectShape(baseSchema);
|
|
640
|
-
|
|
641
|
-
const fields: Field[] = Object.entries(shape.properties)
|
|
642
|
-
.sort(([nameA], [nameB]) => {
|
|
643
|
-
const aRequired = shape.required.has(nameA);
|
|
644
|
-
const bRequired = shape.required.has(nameB);
|
|
645
|
-
if (aRequired && !bRequired) return -1;
|
|
646
|
-
if (!aRequired && bRequired) return 1;
|
|
647
|
-
return 0;
|
|
648
|
-
})
|
|
649
|
-
.map(([name, propertySchema]: [string, any]) => {
|
|
650
|
-
const nestedContent = extractRequestSchemaContent(
|
|
651
|
-
propertySchema,
|
|
652
|
-
nextSeen,
|
|
653
|
-
);
|
|
654
|
-
return {
|
|
655
|
-
name,
|
|
656
|
-
required: shape.required.has(name),
|
|
657
|
-
type: getSchemaTypeLabel(propertySchema),
|
|
658
|
-
description: propertySchema?.description || "",
|
|
659
|
-
enum: getSchemaEnumValues(propertySchema),
|
|
660
|
-
...getSchemaStringLengthConstraints(propertySchema, nextSeen),
|
|
661
|
-
...getSchemaNumericConstraints(propertySchema, nextSeen),
|
|
662
|
-
hasDefault: Object.prototype.hasOwnProperty.call(
|
|
663
|
-
propertySchema || {},
|
|
664
|
-
"default",
|
|
665
|
-
),
|
|
666
|
-
defaultValue: propertySchema?.default,
|
|
667
|
-
isArray: propertySchema?.type === "array",
|
|
668
|
-
nested:
|
|
669
|
-
nestedContent.fields.length > 0 ? nestedContent.fields : undefined,
|
|
670
|
-
variants: nestedContent.variants,
|
|
671
|
-
variantType: nestedContent.variantType,
|
|
672
|
-
};
|
|
673
|
-
});
|
|
674
|
-
|
|
675
|
-
const additionalProperties = getAdditionalPropertiesSchema(baseSchema);
|
|
676
|
-
const supportsAdditionalProperties =
|
|
677
|
-
additionalProperties === true ||
|
|
678
|
-
(additionalProperties &&
|
|
679
|
-
typeof additionalProperties === "object" &&
|
|
680
|
-
!Array.isArray(additionalProperties));
|
|
681
|
-
|
|
682
|
-
if (supportsAdditionalProperties) {
|
|
683
|
-
const additionalSchema =
|
|
684
|
-
additionalProperties && typeof additionalProperties === "object"
|
|
685
|
-
? additionalProperties
|
|
686
|
-
: null;
|
|
687
|
-
const additionalContent = additionalSchema
|
|
688
|
-
? extractRequestSchemaContent(additionalSchema, nextSeen)
|
|
689
|
-
: { fields: [] as Field[] };
|
|
690
|
-
|
|
691
|
-
fields.push({
|
|
692
|
-
name: "[key: string]",
|
|
693
|
-
required: false,
|
|
694
|
-
type: additionalSchema ? getSchemaTypeLabel(additionalSchema) : "any",
|
|
695
|
-
description: additionalSchema?.description || "",
|
|
696
|
-
enum: additionalSchema
|
|
697
|
-
? getSchemaEnumValues(additionalSchema)
|
|
698
|
-
: undefined,
|
|
699
|
-
...getSchemaStringLengthConstraints(additionalSchema, nextSeen),
|
|
700
|
-
...getSchemaNumericConstraints(additionalSchema, nextSeen),
|
|
701
|
-
hasDefault: Object.prototype.hasOwnProperty.call(
|
|
702
|
-
additionalSchema || {},
|
|
703
|
-
"default",
|
|
704
|
-
),
|
|
705
|
-
defaultValue: additionalSchema?.default,
|
|
706
|
-
isArray: additionalSchema?.type === "array",
|
|
707
|
-
nested:
|
|
708
|
-
additionalContent.fields.length > 0
|
|
709
|
-
? additionalContent.fields
|
|
710
|
-
: undefined,
|
|
711
|
-
variants: additionalContent.variants,
|
|
712
|
-
variantType: additionalContent.variantType,
|
|
713
|
-
isAdditionalProperty: true,
|
|
714
|
-
mapKnownKeys: Object.keys(shape.properties),
|
|
715
|
-
});
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
const variantType = getSchemaVariantType(schema);
|
|
719
|
-
const variantSchemas =
|
|
720
|
-
variantType === "oneOf"
|
|
721
|
-
? schema.oneOf || []
|
|
722
|
-
: variantType === "anyOf"
|
|
723
|
-
? schema.anyOf || []
|
|
724
|
-
: [];
|
|
725
|
-
const variants =
|
|
726
|
-
variantType && variantSchemas.length > 0
|
|
727
|
-
? variantSchemas
|
|
728
|
-
.map((variantSchema: any, index: number) => ({
|
|
729
|
-
label: `Variant ${index + 1}`,
|
|
730
|
-
fields: extractRequestSchemaContent(variantSchema, nextSeen).fields,
|
|
731
|
-
}))
|
|
732
|
-
.filter((variant: FieldVariant) => variant.fields.length > 0)
|
|
733
|
-
: [];
|
|
734
|
-
|
|
735
|
-
return {
|
|
736
|
-
fields,
|
|
737
|
-
variants: variants.length > 0 ? variants : undefined,
|
|
738
|
-
variantType: variants.length > 0 ? variantType : undefined,
|
|
739
|
-
};
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
// Authorization
|
|
743
|
-
const securityRequirements = operation.getSecurity();
|
|
744
|
-
const schemes = definition.components?.securitySchemes || {};
|
|
745
|
-
|
|
746
|
-
if (securityRequirements && securityRequirements.length > 0) {
|
|
747
|
-
const firstOption = securityRequirements[0];
|
|
748
|
-
|
|
749
|
-
Object.keys(firstOption).forEach((key) => {
|
|
750
|
-
const scheme = schemes[key];
|
|
751
|
-
if (!scheme) return;
|
|
752
|
-
|
|
753
|
-
// Common defaults for all auth types
|
|
754
|
-
const fieldData: Field = {
|
|
755
|
-
name: "", // Will be determined below
|
|
756
|
-
required: true, // If it's in the requirement list, it is required
|
|
757
|
-
type: "string", // Auth credentials are essentially always strings
|
|
758
|
-
description: scheme.description || "", // Fallback description logic below
|
|
759
|
-
};
|
|
760
|
-
|
|
761
|
-
// --- CASE 1: API KEY (Explicit Location) ---
|
|
762
|
-
if (scheme.type === "apiKey") {
|
|
763
|
-
fieldData.name = scheme.name; // e.g. 'x-api-key' or 'api_token'
|
|
764
|
-
|
|
765
|
-
if (!fieldData.description) {
|
|
766
|
-
fieldData.description = `API Key required in ${scheme.in}.`;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
// Sort into buckets
|
|
770
|
-
if (scheme.in === "header") requestFields.header.push(fieldData);
|
|
771
|
-
else if (scheme.in === "query") requestFields.query.push(fieldData);
|
|
772
|
-
else if (scheme.in === "cookie") requestFields.cookie.push(fieldData);
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
// --- CASE 2: HTTP (Bearer / Basic) ---
|
|
776
|
-
// These are implicit headers.
|
|
777
|
-
else if (scheme.type === "http") {
|
|
778
|
-
fieldData.name = "Authorization";
|
|
779
|
-
|
|
780
|
-
if (scheme.scheme === "bearer") {
|
|
781
|
-
fieldData.description =
|
|
782
|
-
fieldData.description || "Bearer token authentication.";
|
|
783
|
-
} else if (scheme.scheme === "basic") {
|
|
784
|
-
fieldData.description =
|
|
785
|
-
fieldData.description ||
|
|
786
|
-
"Basic authentication (Base64 encoded username:password).";
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
requestFields.header.push(fieldData);
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
// --- CASE 3: OAUTH2 / OPENID ---
|
|
793
|
-
// Standard practice is a Bearer header.
|
|
794
|
-
else if (scheme.type === "oauth2" || scheme.type === "openIdConnect") {
|
|
795
|
-
fieldData.name = "Authorization";
|
|
796
|
-
fieldData.description = fieldData.description || "OAuth2 Bearer Token.";
|
|
797
|
-
|
|
798
|
-
requestFields.header.push(fieldData);
|
|
799
|
-
}
|
|
800
|
-
});
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
// --- Regular Parameters (Path, Query, Header, Cookie) ---
|
|
804
|
-
const parameters = operation.getParameters();
|
|
805
|
-
parameters.forEach((param) => {
|
|
806
|
-
const schema = param.schema as any;
|
|
807
|
-
const isArray = schema?.type === "array";
|
|
808
|
-
const schemaContent = extractRequestSchemaContent(schema);
|
|
809
|
-
|
|
810
|
-
const field = {
|
|
811
|
-
name: param.name,
|
|
812
|
-
required: param.required || false,
|
|
813
|
-
type: getSchemaTypeLabel(schema),
|
|
814
|
-
description: param.description || "",
|
|
815
|
-
enum: getSchemaEnumValues(schema),
|
|
816
|
-
...getSchemaStringLengthConstraints(schema),
|
|
817
|
-
...getSchemaNumericConstraints(schema),
|
|
818
|
-
hasDefault: Object.prototype.hasOwnProperty.call(schema || {}, "default"),
|
|
819
|
-
defaultValue: schema?.default,
|
|
820
|
-
isArray,
|
|
821
|
-
style: param.style,
|
|
822
|
-
explode: param.explode,
|
|
823
|
-
nested: schemaContent.fields.length > 0 ? schemaContent.fields : undefined,
|
|
824
|
-
variants: schemaContent.variants,
|
|
825
|
-
variantType: schemaContent.variantType,
|
|
826
|
-
};
|
|
827
|
-
|
|
828
|
-
if (param.in === "path") requestFields.path.push(field);
|
|
829
|
-
if (param.in === "query") requestFields.query.push(field);
|
|
830
|
-
if (param.in === "header") requestFields.header.push(field);
|
|
831
|
-
if (param.in === "cookie") requestFields.cookie.push(field);
|
|
832
|
-
});
|
|
833
|
-
|
|
834
|
-
// --- 3. Body Parameters ---
|
|
835
|
-
if (operation.hasRequestBody()) {
|
|
836
|
-
const requestBody = operation.getRequestBody("application/json");
|
|
837
|
-
const bodySchema = (requestBody as any)?.schema as any;
|
|
838
|
-
bodyDescription =
|
|
839
|
-
(requestBody as any)?.description || bodySchema?.description || "";
|
|
840
|
-
bodyDefaultKind = getSchemaStructuralType(bodySchema);
|
|
841
|
-
const bodyContent = extractRequestSchemaContent(bodySchema);
|
|
842
|
-
requestFields.body.push(...bodyContent.fields);
|
|
843
|
-
|
|
844
|
-
if (bodyContent.variants && bodyContent.variantType) {
|
|
845
|
-
requestSectionVariants.body = {
|
|
846
|
-
variants: bodyContent.variants,
|
|
847
|
-
variantType: bodyContent.variantType,
|
|
848
|
-
};
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
|
|
852
42
|
const formattedBodyDescription = bodyDescription
|
|
853
43
|
? await renderMarkdown(bodyDescription)
|
|
854
44
|
: null;
|
|
@@ -921,13 +111,29 @@ const formattedBodyDescription = bodyDescription
|
|
|
921
111
|
/>
|
|
922
112
|
)
|
|
923
113
|
}
|
|
924
|
-
<div class="xl:hidden space-y-6 mt-6">
|
|
925
|
-
<
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
114
|
+
<div data-inline-snippet-stack class="xl:hidden space-y-6 mt-6">
|
|
115
|
+
<div
|
|
116
|
+
data-snippet-slot
|
|
117
|
+
data-inline-snippet-slot
|
|
118
|
+
class="min-h-0 min-w-0 overflow-hidden transition-[height,max-height] duration-[360ms] ease-[cubic-bezier(0.22,1,0.36,1)]"
|
|
119
|
+
>
|
|
120
|
+
<RequestSnippets
|
|
121
|
+
api={api}
|
|
122
|
+
method={route.openApiMethod}
|
|
123
|
+
path={route.openApiPath}
|
|
124
|
+
/>
|
|
125
|
+
</div>
|
|
126
|
+
{
|
|
127
|
+
responses && (
|
|
128
|
+
<div
|
|
129
|
+
data-snippet-slot
|
|
130
|
+
data-inline-snippet-slot
|
|
131
|
+
class="min-h-0 min-w-0 overflow-hidden transition-[height,max-height] duration-[360ms] ease-[cubic-bezier(0.22,1,0.36,1)]"
|
|
132
|
+
>
|
|
133
|
+
<ResponseSnippets responses={responses} />
|
|
134
|
+
</div>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
931
137
|
</div>
|
|
932
138
|
<div class="mt-10">
|
|
933
139
|
<!-- Request -->
|
|
@@ -967,10 +173,9 @@ const formattedBodyDescription = bodyDescription
|
|
|
967
173
|
/>
|
|
968
174
|
)}
|
|
969
175
|
{hasCommonFields && (
|
|
970
|
-
<div x-data="{ expanded:
|
|
176
|
+
<div x-data="{ expanded: true }" class="mt-4">
|
|
971
177
|
<div
|
|
972
|
-
class="w-full overflow-hidden rounded-xl border border-neutral-
|
|
973
|
-
x-bind:class="expanded ? 'border-neutral-300 dark:border-neutral-700' : 'border-neutral-200 dark:border-neutral-800'"
|
|
178
|
+
class="w-full overflow-hidden rounded-xl border-[0.5px] border-neutral-900/8 bg-white shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-6px_rgba(0,0,0,0.08)] transition-colors duration-200 dark:border-white/6 dark:bg-(--rd-code-surface) dark:shadow-[0_-.5px_1px_rgba(255,255,255,0.15),0_5px_12px_-6px_rgba(0,0,0,0.2)]"
|
|
974
179
|
>
|
|
975
180
|
<button
|
|
976
181
|
type="button"
|
|
@@ -1011,10 +216,9 @@ const formattedBodyDescription = bodyDescription
|
|
|
1011
216
|
<div class="mb-2 text-xs font-medium text-neutral-600 dark:text-neutral-400">
|
|
1012
217
|
{variant.label}
|
|
1013
218
|
</div>
|
|
1014
|
-
<div x-data="{ expanded:
|
|
219
|
+
<div x-data="{ expanded: true }">
|
|
1015
220
|
<div
|
|
1016
|
-
class="w-full overflow-hidden rounded-lg border border-neutral-
|
|
1017
|
-
x-bind:class="expanded ? 'border-neutral-300 dark:border-neutral-700' : 'border-neutral-200 dark:border-neutral-800'"
|
|
221
|
+
class="w-full overflow-hidden rounded-lg border-[0.5px] border-neutral-900/8 bg-white shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-6px_rgba(0,0,0,0.08)] transition-colors duration-200 dark:border-white/6 dark:bg-(--rd-code-surface) dark:shadow-[0_-.5px_1px_rgba(255,255,255,0.15),0_5px_12px_-6px_rgba(0,0,0,0.2)]"
|
|
1018
222
|
>
|
|
1019
223
|
<button
|
|
1020
224
|
type="button"
|
|
@@ -1295,4 +499,138 @@ const formattedBodyDescription = bodyDescription
|
|
|
1295
499
|
);
|
|
1296
500
|
applySlotHeights();
|
|
1297
501
|
}
|
|
502
|
+
|
|
503
|
+
const inlineStack = document.querySelector("[data-inline-snippet-stack]");
|
|
504
|
+
|
|
505
|
+
if (inlineStack instanceof HTMLElement) {
|
|
506
|
+
const inlineSlots = Array.from(
|
|
507
|
+
inlineStack.querySelectorAll("[data-inline-snippet-slot]"),
|
|
508
|
+
).filter((slot) => slot instanceof HTMLElement);
|
|
509
|
+
|
|
510
|
+
const getInlineViewportHeight = () => {
|
|
511
|
+
if (window.visualViewport?.height) {
|
|
512
|
+
return Math.floor(window.visualViewport.height);
|
|
513
|
+
}
|
|
514
|
+
return Math.floor(window.innerHeight);
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
const getInlineSlotHeightCap = () => {
|
|
518
|
+
const viewportCap = Math.floor(getInlineViewportHeight() * 0.7);
|
|
519
|
+
return Math.max(0, Math.min(448, viewportCap));
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
const inlinePrefersReducedMotion =
|
|
523
|
+
typeof window.matchMedia === "function" &&
|
|
524
|
+
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
525
|
+
const inlineSlotHeightTransitionMs = 360;
|
|
526
|
+
let isAnimatingInlineSlotHeights = false;
|
|
527
|
+
let inlineSlotHeightAnimationTimeoutId = null;
|
|
528
|
+
|
|
529
|
+
const resetInlineSlotHeights = () => {
|
|
530
|
+
for (const slot of inlineSlots) {
|
|
531
|
+
slot.style.height = "";
|
|
532
|
+
slot.style.maxHeight = "";
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
const applyInlineSlotHeights = ({ animate = false } = {}) => {
|
|
537
|
+
if (!inlineSlots.length) return;
|
|
538
|
+
if (isAnimatingInlineSlotHeights && !animate) return;
|
|
539
|
+
|
|
540
|
+
if (window.getComputedStyle(inlineStack).display === "none") {
|
|
541
|
+
resetInlineSlotHeights();
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const shouldAnimate = animate && !inlinePrefersReducedMotion;
|
|
546
|
+
const previousHeights = shouldAnimate
|
|
547
|
+
? inlineSlots.map((slot) =>
|
|
548
|
+
Math.ceil(slot.getBoundingClientRect().height),
|
|
549
|
+
)
|
|
550
|
+
: [];
|
|
551
|
+
const previousTransitions = shouldAnimate
|
|
552
|
+
? inlineSlots.map((slot) => slot.style.transition)
|
|
553
|
+
: [];
|
|
554
|
+
const heightCap = getInlineSlotHeightCap();
|
|
555
|
+
|
|
556
|
+
if (shouldAnimate) {
|
|
557
|
+
for (const slot of inlineSlots) {
|
|
558
|
+
slot.style.transition = "none";
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
for (const slot of inlineSlots) {
|
|
563
|
+
slot.style.height = "auto";
|
|
564
|
+
slot.style.maxHeight = "none";
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const targetHeights = inlineSlots.map((slot) => {
|
|
568
|
+
const naturalHeight = Math.ceil(slot.scrollHeight);
|
|
569
|
+
return Math.min(naturalHeight, heightCap);
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
const setInlineSlotHeights = () => {
|
|
573
|
+
for (let i = 0; i < inlineSlots.length; i += 1) {
|
|
574
|
+
const height = targetHeights[i];
|
|
575
|
+
inlineSlots[i].style.height = `${height}px`;
|
|
576
|
+
inlineSlots[i].style.maxHeight = `${height}px`;
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
if (!shouldAnimate) {
|
|
581
|
+
setInlineSlotHeights();
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
isAnimatingInlineSlotHeights = true;
|
|
586
|
+
if (inlineSlotHeightAnimationTimeoutId) {
|
|
587
|
+
window.clearTimeout(inlineSlotHeightAnimationTimeoutId);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
for (let i = 0; i < inlineSlots.length; i += 1) {
|
|
591
|
+
const height = previousHeights[i];
|
|
592
|
+
inlineSlots[i].style.height = `${height}px`;
|
|
593
|
+
inlineSlots[i].style.maxHeight = `${height}px`;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
void inlineStack.offsetHeight;
|
|
597
|
+
|
|
598
|
+
for (let i = 0; i < inlineSlots.length; i += 1) {
|
|
599
|
+
inlineSlots[i].style.transition = previousTransitions[i];
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
window.requestAnimationFrame(setInlineSlotHeights);
|
|
603
|
+
|
|
604
|
+
inlineSlotHeightAnimationTimeoutId = window.setTimeout(() => {
|
|
605
|
+
isAnimatingInlineSlotHeights = false;
|
|
606
|
+
inlineSlotHeightAnimationTimeoutId = null;
|
|
607
|
+
applyInlineSlotHeights();
|
|
608
|
+
}, inlineSlotHeightTransitionMs + 80);
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
let inlineSlotAnimationFrameId = null;
|
|
612
|
+
const scheduleInlineSlotHeights = (options = {}) => {
|
|
613
|
+
if (inlineSlotAnimationFrameId) {
|
|
614
|
+
window.cancelAnimationFrame(inlineSlotAnimationFrameId);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
inlineSlotAnimationFrameId = window.requestAnimationFrame(() => {
|
|
618
|
+
inlineSlotAnimationFrameId = window.requestAnimationFrame(() => {
|
|
619
|
+
inlineSlotAnimationFrameId = null;
|
|
620
|
+
applyInlineSlotHeights(options);
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
const inlineResizeObserver = new ResizeObserver(scheduleInlineSlotHeights);
|
|
626
|
+
inlineResizeObserver.observe(inlineStack);
|
|
627
|
+
|
|
628
|
+
window.addEventListener("resize", scheduleInlineSlotHeights);
|
|
629
|
+
window.visualViewport?.addEventListener("resize", scheduleInlineSlotHeights);
|
|
630
|
+
window.addEventListener(
|
|
631
|
+
"rd:snippet-content-change",
|
|
632
|
+
() => scheduleInlineSlotHeights({ animate: true }),
|
|
633
|
+
);
|
|
634
|
+
scheduleInlineSlotHeights();
|
|
635
|
+
}
|
|
1298
636
|
</script>
|