radiant-docs 0.1.7 → 0.1.8
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/dist/index.js +28 -5
- package/package.json +3 -3
- package/template/astro.config.mjs +76 -3
- package/template/package-lock.json +924 -737
- package/template/package.json +7 -5
- package/template/scripts/generate-og-images.mjs +335 -0
- package/template/scripts/generate-og-metadata.mjs +173 -0
- package/template/scripts/rewrite-static-asset-host.mjs +408 -0
- package/template/scripts/stamp-image-versions.mjs +277 -0
- package/template/scripts/stamp-og-image-versions.mjs +199 -0
- package/template/scripts/stamp-pagefind-runtime-version.mjs +140 -0
- package/template/src/assets/fonts/geist-mono/cyrillic.woff2 +0 -0
- package/template/src/assets/fonts/geist-mono/latin-ext.woff2 +0 -0
- package/template/src/assets/fonts/geist-mono/latin.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/canadian-aboriginal.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/cherokee.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/latin-ext.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/latin.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/math.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/nushu.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/symbols.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/syriac.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/tifinagh.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/vietnamese.woff2 +0 -0
- package/template/src/components/Footer.astro +94 -0
- package/template/src/components/Header.astro +11 -66
- package/template/src/components/LogoLink.astro +103 -0
- package/template/src/components/MdxPage.astro +126 -11
- package/template/src/components/OpenApiPage.astro +1036 -69
- package/template/src/components/Search.astro +0 -2
- package/template/src/components/SidebarDropdown.astro +34 -14
- package/template/src/components/SidebarGroup.astro +3 -6
- package/template/src/components/SidebarLink.astro +22 -12
- package/template/src/components/SidebarMenu.astro +19 -16
- package/template/src/components/SidebarSegmented.astro +99 -0
- package/template/src/components/SidebarSubgroup.astro +12 -12
- package/template/src/components/ThemeSwitcher.astro +30 -7
- package/template/src/components/endpoint/PlaygroundBar.astro +32 -36
- package/template/src/components/endpoint/PlaygroundButton.astro +40 -4
- package/template/src/components/endpoint/PlaygroundField.astro +1068 -22
- package/template/src/components/endpoint/PlaygroundForm.astro +559 -61
- package/template/src/components/endpoint/RequestSnippets.astro +342 -193
- package/template/src/components/endpoint/ResponseDisplay.astro +161 -147
- package/template/src/components/endpoint/ResponseFieldTree.astro +134 -0
- package/template/src/components/endpoint/ResponseFields.astro +711 -68
- package/template/src/components/endpoint/ResponseSnippets.astro +299 -173
- package/template/src/components/sidebar/SidebarEndpointLink.astro +1 -1
- package/template/src/components/ui/CodeLanguageIcon.astro +19 -0
- package/template/src/components/ui/CodeTabEdge.astro +79 -0
- package/template/src/components/ui/Field.astro +103 -20
- package/template/src/components/ui/Icon.astro +32 -0
- package/template/src/components/ui/ListChevronsToggle.astro +31 -0
- package/template/src/components/ui/Tag.astro +1 -1
- package/template/src/components/user/{Accordian.astro → Accordion.astro} +6 -6
- package/template/src/components/user/Callout.astro +5 -9
- package/template/src/components/user/CodeBlock.astro +400 -0
- package/template/src/components/user/CodeGroup.astro +225 -0
- package/template/src/components/user/ComponentPreview.astro +1 -0
- package/template/src/components/user/ComponentPreviewBlock.astro +181 -0
- package/template/src/components/user/Image.astro +132 -0
- package/template/src/components/user/Steps.astro +1 -3
- package/template/src/components/user/Tabs.astro +2 -2
- package/template/src/content.config.ts +1 -0
- package/template/src/layouts/Layout.astro +109 -8
- package/template/src/lib/code/code-block.ts +546 -0
- package/template/src/lib/frontmatter-schema.ts +8 -7
- package/template/src/lib/mdx/remark-code-block-component.ts +342 -0
- package/template/src/lib/mdx/remark-demote-h1.ts +16 -0
- package/template/src/lib/pagefind.ts +19 -5
- package/template/src/lib/routes.ts +49 -31
- package/template/src/lib/utils.ts +20 -0
- package/template/src/lib/validation.ts +638 -200
- package/template/src/pages/[...slug].astro +18 -5
- package/template/src/styles/geist-mono.css +33 -0
- package/template/src/styles/global.css +89 -84
- package/template/src/styles/google-sans-flex.css +143 -0
- package/template/ec.config.mjs +0 -51
- /package/template/src/components/user/{AccordianGroup.astro → AccordionGroup.astro} +0 -0
|
@@ -3,7 +3,11 @@ import * as Sampler from "openapi-sampler";
|
|
|
3
3
|
import type { JSONSchema7 } from "json-schema";
|
|
4
4
|
import type { OpenAPIV3, OpenAPIV3_1 } from "openapi-types";
|
|
5
5
|
import { Icon } from "astro-icon/components";
|
|
6
|
-
import
|
|
6
|
+
import CodeTabEdge from "../ui/CodeTabEdge.astro";
|
|
7
|
+
import {
|
|
8
|
+
getCodeLineTokens,
|
|
9
|
+
normalizeCodeLanguageValue,
|
|
10
|
+
} from "../../lib/code/code-block";
|
|
7
11
|
|
|
8
12
|
interface Props {
|
|
9
13
|
responses: OpenAPIV3.ResponsesObject | OpenAPIV3_1.ResponsesObject;
|
|
@@ -11,16 +15,14 @@ interface Props {
|
|
|
11
15
|
|
|
12
16
|
const { responses } = Astro.props;
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
const responseData: Array<{
|
|
18
|
+
const sampledResponses: Array<{
|
|
16
19
|
statusCode: string;
|
|
17
|
-
description
|
|
18
|
-
example?:
|
|
20
|
+
description: string;
|
|
21
|
+
example?: unknown;
|
|
19
22
|
contentType?: string;
|
|
20
23
|
}> = [];
|
|
21
24
|
|
|
22
25
|
Object.entries(responses).forEach(([statusCode, response]: [string, any]) => {
|
|
23
|
-
// Try to get application/json content first, fallback to first available content type
|
|
24
26
|
const contentTypes = Object.keys(response.content || {});
|
|
25
27
|
const contentType = response.content?.["application/json"]
|
|
26
28
|
? "application/json"
|
|
@@ -29,20 +31,20 @@ Object.entries(responses).forEach(([statusCode, response]: [string, any]) => {
|
|
|
29
31
|
const mediaType = contentType ? response.content?.[contentType] : null;
|
|
30
32
|
const schema = mediaType?.schema;
|
|
31
33
|
|
|
32
|
-
let example:
|
|
34
|
+
let example: unknown = undefined;
|
|
33
35
|
if (schema) {
|
|
34
36
|
try {
|
|
35
37
|
example = Sampler.sample(schema as JSONSchema7, {
|
|
36
38
|
skipReadOnly: false,
|
|
37
39
|
});
|
|
38
|
-
} catch (
|
|
39
|
-
console.error(`Failed to sample response ${statusCode}:`,
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error(`Failed to sample response ${statusCode}:`, error);
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
sampledResponses.push({
|
|
44
46
|
statusCode,
|
|
45
|
-
description: response.description,
|
|
47
|
+
description: response.description ?? "",
|
|
46
48
|
example,
|
|
47
49
|
contentType,
|
|
48
50
|
});
|
|
@@ -58,190 +60,314 @@ const statusCodeStyles = {
|
|
|
58
60
|
dot: "bg-blue-700/70",
|
|
59
61
|
},
|
|
60
62
|
"4": {
|
|
61
|
-
badge: "bg-
|
|
62
|
-
dot: "bg-
|
|
63
|
+
badge: "bg-yellow-50 text-yellow-600/80 border-yellow-600/10",
|
|
64
|
+
dot: "bg-yellow-500/80",
|
|
63
65
|
},
|
|
64
66
|
"5": {
|
|
65
67
|
badge: "bg-red-50 text-red-700/70 border-red-700/10",
|
|
66
68
|
dot: "bg-red-700/70",
|
|
67
69
|
},
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
return
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
70
|
+
} as const;
|
|
71
|
+
|
|
72
|
+
function getStatusCodeClass(statusCode: string, key: "badge" | "dot"): string {
|
|
73
|
+
const family = statusCode[0] as keyof typeof statusCodeStyles;
|
|
74
|
+
return statusCodeStyles[family]?.[key] ?? "";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function buildTokenStyle(token: {
|
|
78
|
+
color?: string;
|
|
79
|
+
bgColor?: string;
|
|
80
|
+
fontStyle?: number;
|
|
81
|
+
htmlStyle?: Record<string, string>;
|
|
82
|
+
}): string | undefined {
|
|
83
|
+
const styleSegments: string[] = [];
|
|
84
|
+
|
|
85
|
+
if (token.color) styleSegments.push(`color:${token.color}`);
|
|
86
|
+
if (token.bgColor) styleSegments.push(`background-color:${token.bgColor}`);
|
|
87
|
+
|
|
88
|
+
const fontStyle = typeof token.fontStyle === "number" ? token.fontStyle : 0;
|
|
89
|
+
if ((fontStyle & 1) === 1) styleSegments.push("font-style:italic");
|
|
90
|
+
if ((fontStyle & 2) === 2) styleSegments.push("font-weight:600");
|
|
91
|
+
if ((fontStyle & 4) === 4) styleSegments.push("text-decoration:underline");
|
|
92
|
+
|
|
93
|
+
if (token.htmlStyle && typeof token.htmlStyle === "object") {
|
|
94
|
+
for (const [property, value] of Object.entries(token.htmlStyle)) {
|
|
95
|
+
styleSegments.push(`${property}:${value}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!styleSegments.length) return undefined;
|
|
100
|
+
return styleSegments.join(";");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function escapeHtml(value: string): string {
|
|
104
|
+
return value
|
|
105
|
+
.replaceAll("&", "&")
|
|
106
|
+
.replaceAll("<", "<")
|
|
107
|
+
.replaceAll(">", ">");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function escapeAttribute(value: string): string {
|
|
111
|
+
return escapeHtml(value).replaceAll('"', """);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function renderCodeLinesHtml(
|
|
115
|
+
code: string,
|
|
116
|
+
language: string,
|
|
117
|
+
): Promise<string> {
|
|
118
|
+
const normalizedCode = code.replace(/\r\n?/g, "\n");
|
|
119
|
+
const { lines: tokenLines } = await getCodeLineTokens({
|
|
120
|
+
code: normalizedCode,
|
|
121
|
+
language: normalizeCodeLanguageValue(language),
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const rawLines = normalizedCode.split("\n");
|
|
125
|
+
const normalizedRawLines = rawLines.length > 0 ? rawLines : [""];
|
|
126
|
+
const lineCount = Math.max(1, normalizedRawLines.length, tokenLines.length);
|
|
127
|
+
const normalizedTokenLines = Array.from({ length: lineCount }, (_, index) => {
|
|
128
|
+
return tokenLines[index] ?? [];
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
return normalizedTokenLines
|
|
132
|
+
.map((lineTokens, lineIndex) => {
|
|
133
|
+
const fallbackLineContent = normalizedRawLines[lineIndex] ?? "";
|
|
134
|
+
const tokenHtml =
|
|
135
|
+
lineTokens.length > 0
|
|
136
|
+
? lineTokens
|
|
137
|
+
.map((token) => {
|
|
138
|
+
const tokenStyle = buildTokenStyle(token);
|
|
139
|
+
const tokenStyleAttribute = tokenStyle
|
|
140
|
+
? ` style="${escapeAttribute(tokenStyle)}"`
|
|
141
|
+
: "";
|
|
142
|
+
return `<span${tokenStyleAttribute}>${escapeHtml(token.content)}</span>`;
|
|
143
|
+
})
|
|
144
|
+
.join("")
|
|
145
|
+
: fallbackLineContent.length > 0
|
|
146
|
+
? escapeHtml(fallbackLineContent)
|
|
147
|
+
: " ";
|
|
148
|
+
|
|
149
|
+
return `<span class="flex min-w-full"><span class="flex-1 whitespace-pre pr-4 pl-4">${tokenHtml}</span></span>`;
|
|
150
|
+
})
|
|
151
|
+
.join("");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const responseSnippetItems = await Promise.all(
|
|
155
|
+
sampledResponses.map(async (response) => {
|
|
156
|
+
const language = response.contentType?.includes("json")
|
|
157
|
+
? "json"
|
|
158
|
+
: "plaintext";
|
|
159
|
+
const code =
|
|
160
|
+
response.example === undefined
|
|
161
|
+
? "This response has no body data."
|
|
162
|
+
: typeof response.example === "string"
|
|
163
|
+
? response.example
|
|
164
|
+
: JSON.stringify(response.example, null, 2);
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
statusCode: response.statusCode,
|
|
168
|
+
description: response.description ?? "",
|
|
169
|
+
badgeClass: getStatusCodeClass(response.statusCode, "badge"),
|
|
170
|
+
dotClass: getStatusCodeClass(response.statusCode, "dot"),
|
|
171
|
+
code,
|
|
172
|
+
renderedCodeLinesHtml: await renderCodeLinesHtml(code, language),
|
|
173
|
+
};
|
|
174
|
+
}),
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (responseSnippetItems.length === 0) {
|
|
178
|
+
const fallbackCode = "This endpoint does not define response samples.";
|
|
179
|
+
responseSnippetItems.push({
|
|
180
|
+
statusCode: "200",
|
|
181
|
+
description: "Response",
|
|
182
|
+
badgeClass: getStatusCodeClass("200", "badge"),
|
|
183
|
+
dotClass: getStatusCodeClass("200", "dot"),
|
|
184
|
+
code: fallbackCode,
|
|
185
|
+
renderedCodeLinesHtml: await renderCodeLinesHtml(fallbackCode, "plaintext"),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const clientResponses = responseSnippetItems.map((item) => ({
|
|
190
|
+
statusCode: item.statusCode,
|
|
191
|
+
description: item.description,
|
|
192
|
+
badgeClass: item.badgeClass,
|
|
193
|
+
dotClass: item.dotClass,
|
|
194
|
+
code: item.code,
|
|
195
|
+
}));
|
|
196
|
+
const responsesData = JSON.stringify(clientResponses).replaceAll("`", "\\`");
|
|
197
|
+
const firstResponse = responseSnippetItems[0];
|
|
198
|
+
const hasMultipleResponses = responseSnippetItems.length > 1;
|
|
83
199
|
---
|
|
84
200
|
|
|
85
201
|
<div
|
|
86
202
|
x-data={`{
|
|
87
|
-
|
|
203
|
+
snippets: ${responsesData},
|
|
88
204
|
selected: 0,
|
|
205
|
+
hasMultiple: ${hasMultipleResponses ? "true" : "false"},
|
|
206
|
+
copied: false,
|
|
207
|
+
pillLeft: 0,
|
|
208
|
+
pillWidth: 0,
|
|
209
|
+
pillVisible: false,
|
|
210
|
+
tabSyncHandler: null,
|
|
211
|
+
copyTimeoutId: null,
|
|
212
|
+
init() {
|
|
213
|
+
if (!this.hasMultiple) return;
|
|
214
|
+
const sync = () => this.syncPill();
|
|
215
|
+
this.tabSyncHandler = sync;
|
|
216
|
+
this.$nextTick(() => {
|
|
217
|
+
this.syncPill();
|
|
218
|
+
this.$refs.tabList?.addEventListener("scroll", sync, {
|
|
219
|
+
passive: true,
|
|
220
|
+
});
|
|
221
|
+
window.addEventListener("resize", sync);
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
syncPill() {
|
|
225
|
+
if (!this.hasMultiple) return;
|
|
226
|
+
const tabs = this.$refs.tabList;
|
|
227
|
+
if (!tabs) return;
|
|
228
|
+
const activeTab = tabs.querySelector(
|
|
229
|
+
'[data-rd-snippet-tab="' + this.selected + '"]',
|
|
230
|
+
);
|
|
231
|
+
if (!activeTab) {
|
|
232
|
+
this.pillVisible = false;
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const tabsRect = tabs.getBoundingClientRect();
|
|
236
|
+
const activeRect = activeTab.getBoundingClientRect();
|
|
237
|
+
this.pillLeft = activeRect.left - tabsRect.left + tabs.scrollLeft;
|
|
238
|
+
this.pillWidth = activeRect.width;
|
|
239
|
+
this.pillVisible = true;
|
|
240
|
+
},
|
|
89
241
|
select(index) {
|
|
90
242
|
this.selected = index;
|
|
91
|
-
this.$
|
|
92
|
-
|
|
243
|
+
this.$nextTick(() => {
|
|
244
|
+
this.syncPill();
|
|
245
|
+
window.dispatchEvent(new CustomEvent("rd:snippet-content-change"));
|
|
246
|
+
});
|
|
93
247
|
},
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
248
|
+
async copySelected() {
|
|
249
|
+
const snippet = this.snippets[this.selected];
|
|
250
|
+
if (!snippet) return;
|
|
251
|
+
if (!navigator?.clipboard?.writeText) {
|
|
252
|
+
this.copied = false;
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
await navigator.clipboard.writeText(snippet.code ?? "");
|
|
257
|
+
this.copied = true;
|
|
258
|
+
if (this.copyTimeoutId) window.clearTimeout(this.copyTimeoutId);
|
|
259
|
+
this.copyTimeoutId = window.setTimeout(() => {
|
|
260
|
+
this.copied = false;
|
|
261
|
+
this.copyTimeoutId = null;
|
|
262
|
+
}, 1200);
|
|
263
|
+
} catch {
|
|
264
|
+
this.copied = false;
|
|
265
|
+
}
|
|
98
266
|
}
|
|
99
267
|
}`}
|
|
100
|
-
class="response-code-snippets
|
|
268
|
+
class="response-code-snippets h-full min-h-0 w-full min-w-0"
|
|
101
269
|
>
|
|
102
|
-
<div class="
|
|
270
|
+
<div class="group/prose-code not-prose relative h-full min-h-0 w-full max-w-full min-w-0">
|
|
103
271
|
<div
|
|
104
|
-
class="flex-
|
|
272
|
+
class="flex h-full min-h-0 w-full max-w-full min-w-0 flex-col overflow-hidden rounded-xl border border-neutral-200 bg-white shadow-xs"
|
|
105
273
|
>
|
|
106
|
-
<
|
|
107
|
-
class
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
x-text="responses[selected].description"
|
|
123
|
-
set:html={responseData[0].description}
|
|
124
|
-
/>
|
|
125
|
-
</div>
|
|
126
|
-
<div
|
|
127
|
-
x-data="{
|
|
128
|
-
open: false,
|
|
129
|
-
toggle() {
|
|
130
|
-
if (this.open) {
|
|
131
|
-
return this.close()
|
|
132
|
-
}
|
|
274
|
+
<div
|
|
275
|
+
class="flex items-center justify-between gap-2 border-b border-neutral-200 rounded-t-xl bg-neutral-50 inset-shadow-sm inset-shadow-neutral-100/80"
|
|
276
|
+
>
|
|
277
|
+
{
|
|
278
|
+
hasMultipleResponses ? (
|
|
279
|
+
<div class="min-w-0 flex-1 overflow-hidden rounded-t-xl">
|
|
280
|
+
<div
|
|
281
|
+
x-ref="tabList"
|
|
282
|
+
class="relative flex min-w-0 items-end gap-1 overflow-x-auto px-1 pr-8 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
|
283
|
+
>
|
|
284
|
+
<div
|
|
285
|
+
aria-hidden="true"
|
|
286
|
+
class="pointer-events-none absolute top-1/2 z-0 h-[28px] -translate-y-1/2 rounded-lg border-[0.5px] border-neutral-200 bg-white shadow-xs transition-[left,width,opacity] duration-200 ease-out"
|
|
287
|
+
x-bind:class="pillVisible ? 'opacity-100' : 'opacity-0'"
|
|
288
|
+
x-bind:style="'left:' + pillLeft + 'px;width:' + pillWidth + 'px;'"
|
|
289
|
+
/>
|
|
133
290
|
|
|
134
|
-
|
|
291
|
+
<template
|
|
292
|
+
x-for="(snippet, index) in snippets"
|
|
293
|
+
:key="snippet.statusCode + index"
|
|
294
|
+
>
|
|
295
|
+
<button
|
|
296
|
+
type="button"
|
|
297
|
+
x-bind:data-rd-snippet-tab="index"
|
|
298
|
+
x-on:click="select(index)"
|
|
299
|
+
class="relative z-10 inline-flex h-9 items-center gap-2 border-0 bg-transparent px-3 py-1.5 text-xs font-medium transition-colors duration-150 focus:outline-none focus-visible:outline-none cursor-pointer"
|
|
300
|
+
x-bind:class="selected === index ? 'text-neutral-900' : 'text-neutral-600'"
|
|
301
|
+
>
|
|
302
|
+
<span
|
|
303
|
+
class="size-[7px] shrink-0 rounded-full transition-opacity duration-150"
|
|
304
|
+
x-bind:class="(selected === index ? 'opacity-100 ' : 'opacity-70 ') + snippet.dotClass"></span>
|
|
305
|
+
<span class="whitespace-pre leading-none" x-text="snippet.statusCode"></span>
|
|
306
|
+
</button>
|
|
307
|
+
</template>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
) : (
|
|
311
|
+
<div class="min-w-0 flex-1">
|
|
312
|
+
<div class="relative h-9 w-fit max-w-full rounded-tl-xl bg-white">
|
|
313
|
+
<div class="absolute inset-x-0 -bottom-px h-px bg-white"></div>
|
|
314
|
+
<CodeTabEdge
|
|
315
|
+
className="pointer-events-none absolute -top-px left-full z-10 h-[calc(100%+2px)]"
|
|
316
|
+
/>
|
|
135
317
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
318
|
+
<div class="relative z-20 inline-flex h-9 max-w-full items-center gap-2 pl-5 pr-3 py-1.5 text-xs font-medium text-neutral-700">
|
|
319
|
+
<span
|
|
320
|
+
class="size-[7px] shrink-0 rounded-full"
|
|
321
|
+
class:list={[firstResponse.dotClass]}></span>
|
|
322
|
+
<span class="truncate leading-none">{firstResponse.statusCode}</span>
|
|
323
|
+
</div>
|
|
324
|
+
</div>
|
|
325
|
+
</div>
|
|
326
|
+
)
|
|
327
|
+
}
|
|
142
328
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
x-on:focusin.window="! $refs.panel.contains($event.target) && close()"
|
|
148
|
-
x-id="['dropdown-button']"
|
|
149
|
-
class="shrink-0 relative max-w-24 w-full"
|
|
150
|
-
>
|
|
151
|
-
<button
|
|
152
|
-
x-ref="button"
|
|
153
|
-
x-on:click="toggle()"
|
|
154
|
-
:aria-expanded="open"
|
|
155
|
-
:aria-controls="$id('dropdown-button')"
|
|
156
|
-
type="button"
|
|
157
|
-
class="flex items-center justify-between px-3 pt-2 pb-1.5 relative border-x border-t border-neutral-200/70 rounded-t-xl w-full text-sm font-medium bg-white shadow-[-1px_0px_2px_0px_rgba(0,0,0,0.01)]. shadow-sm cursor-pointer after:absolute after:-bottom-[1.5px] after:inset-x-0 after:z-10 after:h-[3px] after:bg-white"
|
|
158
|
-
>
|
|
159
|
-
<span class="flex items-center gap-2">
|
|
160
|
-
<span
|
|
161
|
-
class:list={["size-1.5 rounded-full", initialStatusCodeDotClasses]}
|
|
162
|
-
x-bind:class="{
|
|
163
|
-
'bg-green-700/70': responses[selected].statusCode[0] === '2',
|
|
164
|
-
'bg-blue-700/70': responses[selected].statusCode[0] === '3',
|
|
165
|
-
'bg-amber-600/80': responses[selected].statusCode[0] === '4',
|
|
166
|
-
'bg-red-700/70': responses[selected].statusCode[0] === '5'
|
|
167
|
-
}"
|
|
168
|
-
></span>
|
|
169
|
-
<span
|
|
170
|
-
x-text="responses[selected].statusCode"
|
|
171
|
-
set:html={responseData[0].statusCode}
|
|
329
|
+
<div class="relative h-9 w-5 shrink-0 rounded-tr-xl bg-white">
|
|
330
|
+
<div class="absolute inset-x-0 -bottom-px h-px bg-white"></div>
|
|
331
|
+
<CodeTabEdge
|
|
332
|
+
className="pointer-events-none absolute -top-px right-full z-10 h-[calc(100%+2px)] rotate-y-180"
|
|
172
333
|
/>
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
</span>
|
|
208
|
-
<span x-text="response.statusCode"></span>
|
|
209
|
-
</button>
|
|
210
|
-
</li>
|
|
211
|
-
</template>
|
|
212
|
-
</ul>
|
|
213
|
-
</div>
|
|
214
|
-
</div>
|
|
215
|
-
{
|
|
216
|
-
responseData.map((response, index) => (
|
|
217
|
-
<div
|
|
218
|
-
{...(index !== 0 ? { "x-cloak": true } : {})}
|
|
219
|
-
x-show={`selected === ${index}`}
|
|
220
|
-
data-snippet-index={index}
|
|
221
|
-
>
|
|
222
|
-
<Code
|
|
223
|
-
code={
|
|
224
|
-
JSON.stringify(response.example, null, 2) ||
|
|
225
|
-
"This response has no body data."
|
|
334
|
+
<button
|
|
335
|
+
x-on:click="copySelected()"
|
|
336
|
+
type="button"
|
|
337
|
+
class="absolute right-2 top-1/2 z-20 inline-flex size-7 -translate-y-1/2 appearance-none items-center justify-center rounded-md border-0 bg-transparent text-neutral-500/80 shadow-none outline-none ring-0 transition-colors duration-150 hover:bg-neutral-50 hover:text-neutral-600 focus:outline-none focus-visible:outline-none focus:ring-0 focus-visible:ring-0 cursor-pointer"
|
|
338
|
+
aria-label="Copy code"
|
|
339
|
+
>
|
|
340
|
+
<Icon
|
|
341
|
+
name="lucide:copy"
|
|
342
|
+
class="size-3.5 origin-center transition-all duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-transform motion-reduce:transition-none"
|
|
343
|
+
x-bind:class="copied ? 'scale-50 opacity-0 -rotate-6' : 'scale-100 opacity-100 rotate-0'"
|
|
344
|
+
aria-hidden="true"
|
|
345
|
+
/>
|
|
346
|
+
<Icon
|
|
347
|
+
name="lucide:check"
|
|
348
|
+
class="absolute size-3.5 stroke-3 origin-center text-green-700/80 transition-all duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-transform motion-reduce:transition-none"
|
|
349
|
+
x-bind:class="copied ? 'scale-110 opacity-100 rotate-0' : 'scale-25 opacity-0 rotate-6'"
|
|
350
|
+
aria-hidden="true"
|
|
351
|
+
/>
|
|
352
|
+
</button>
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
<div class="relative min-h-0 min-w-0 flex-1 overflow-hidden rounded-b-xl">
|
|
357
|
+
<div class="relative h-full overflow-auto [scrollbar-width:thin] [scrollbar-color:var(--color-neutral-300)_transparent] [&::-webkit-scrollbar]:h-1.5 [&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-neutral-300/70 hover:[&::-webkit-scrollbar-thumb]:bg-neutral-300/90">
|
|
358
|
+
<div class="pointer-events-none sticky top-0 z-10 -mb-4 h-4 w-full bg-linear-to-b from-white to-transparent"></div>
|
|
359
|
+
{
|
|
360
|
+
responseSnippetItems.map((snippet, index) => (
|
|
361
|
+
<pre
|
|
362
|
+
class="relative m-0 min-w-full bg-white p-0 text-[13px] leading-6"
|
|
363
|
+
x-show={`selected === ${index}`}
|
|
364
|
+
{...(index !== 0 ? { "x-cloak": true } : {})}
|
|
365
|
+
data-snippet-index={index}
|
|
366
|
+
><code class="block min-w-full py-2.5 font-mono text-neutral-800"><Fragment set:html={snippet.renderedCodeLinesHtml} /></code></pre>
|
|
367
|
+
))
|
|
226
368
|
}
|
|
227
|
-
|
|
228
|
-
frame="code"
|
|
229
|
-
/>
|
|
369
|
+
</div>
|
|
230
370
|
</div>
|
|
231
|
-
|
|
232
|
-
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
233
373
|
</div>
|
|
234
|
-
|
|
235
|
-
<style>
|
|
236
|
-
@reference "../../styles/global.css";
|
|
237
|
-
:global(.response-code-snippets .expressive-code .frame) {
|
|
238
|
-
@apply shadow-sm rounded-2xl!;
|
|
239
|
-
}
|
|
240
|
-
:global(.response-code-snippets .expressive-code pre) {
|
|
241
|
-
@apply rounded-xl! rounded-tr-none! border-neutral-200/70!;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
:global(.response-code-snippets .expressive-code pre code) {
|
|
245
|
-
@apply max-h-64 overflow-y-auto;
|
|
246
|
-
}
|
|
247
|
-
</style>
|
|
@@ -33,7 +33,7 @@ const isActive = currentPath === targetPath;
|
|
|
33
33
|
<a
|
|
34
34
|
href={href}
|
|
35
35
|
class:list={[
|
|
36
|
-
"flex items-center px-2 py-[7px] text-sm relative z-0
|
|
36
|
+
"flex items-center px-2 py-[7px] text-sm relative z-0 before:-z-10 before:absolute before:inset-x-0 before:inset-y-px before:rounded-md before:duration-150",
|
|
37
37
|
isActive
|
|
38
38
|
? "before:bg-neutral-200/50 dark:before:bg-neutral-800 text-neutral-900 dark:text-neutral-200"
|
|
39
39
|
: "text-neutral-600 dark:text-neutral-400 hover:before:bg-neutral-100/70 dark:hover:before:bg-neutral-800/50 hover:text-neutral-900 dark:hover:text-neutral-300",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getLanguageIconSvg } from "../../lib/code/code-block";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
language: string;
|
|
6
|
+
fileName: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { language, fileName, className = "" } = Astro.props;
|
|
11
|
+
|
|
12
|
+
const iconSvg = await getLanguageIconSvg({
|
|
13
|
+
language,
|
|
14
|
+
fileName,
|
|
15
|
+
className,
|
|
16
|
+
});
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
<Fragment set:html={iconSvg} />
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
className?: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const { className = "" } = Astro.props;
|
|
7
|
+
|
|
8
|
+
const idPrefix = `code_group_tab_edge_${Math.random().toString(36).slice(2, 9)}`;
|
|
9
|
+
const mask0Id = `code_group_tab_edge_mask0_${idPrefix}`;
|
|
10
|
+
const path1OutsideMaskId = `code_group_tab_edge_path_1_outside_1_${idPrefix}`;
|
|
11
|
+
const path3OutsideMaskId = `code_group_tab_edge_path_3_outside_2_${idPrefix}`;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<svg
|
|
15
|
+
width="60"
|
|
16
|
+
height="42"
|
|
17
|
+
viewBox="0 0 60 42"
|
|
18
|
+
fill="none"
|
|
19
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
20
|
+
class:list={["block", className]}
|
|
21
|
+
preserveAspectRatio="none"
|
|
22
|
+
aria-hidden="true"
|
|
23
|
+
data-pagefind-ignore
|
|
24
|
+
>
|
|
25
|
+
<mask
|
|
26
|
+
id={mask0Id}
|
|
27
|
+
maskUnits="userSpaceOnUse"
|
|
28
|
+
x="0"
|
|
29
|
+
y="-1"
|
|
30
|
+
width="60"
|
|
31
|
+
height="43"
|
|
32
|
+
style="mask-type:alpha"
|
|
33
|
+
>
|
|
34
|
+
<mask
|
|
35
|
+
id={path1OutsideMaskId}
|
|
36
|
+
maskUnits="userSpaceOnUse"
|
|
37
|
+
x="0"
|
|
38
|
+
y="-1"
|
|
39
|
+
width="60"
|
|
40
|
+
height="43"
|
|
41
|
+
fill="black"
|
|
42
|
+
>
|
|
43
|
+
<rect fill="white" y="-1" width="60" height="43"></rect>
|
|
44
|
+
<path
|
|
45
|
+
d="M1 0L8.0783 0C15.772 0 22.7836 4.41324 26.111 11.3501L34.8889 29.6498C38.2164 36.5868 45.228 41 52.9217 41H60H1L1 0Z"
|
|
46
|
+
></path>
|
|
47
|
+
</mask>
|
|
48
|
+
<path
|
|
49
|
+
d="M1 0L8.0783 0C15.772 0 22.7836 4.41324 26.111 11.3501L34.8889 29.6498C38.2164 36.5868 45.228 41 52.9217 41H60H1L1 0Z"
|
|
50
|
+
fill="white"></path>
|
|
51
|
+
<path
|
|
52
|
+
d="M1 0V-1H0V0L1 0ZM1 41H0V42H1V41ZM34.8889 29.6498L33.9873 30.0823L34.8889 29.6498ZM26.111 11.3501L27.0127 10.9177L26.111 11.3501ZM1 1H8.0783V-1H1V1ZM60 40H1V42H60V40ZM2 41V0L0 0L0 41H2ZM25.2094 11.7826L33.9873 30.0823L35.7906 29.2174L27.0127 10.9177L25.2094 11.7826ZM52.9217 42H60V40H52.9217V42ZM33.9873 30.0823C37.4811 37.3661 44.8433 42 52.9217 42V40C45.6127 40 38.9517 35.8074 35.7906 29.2174L33.9873 30.0823ZM8.0783 1C15.3873 1 22.0483 5.19257 25.2094 11.7826L27.0127 10.9177C23.5188 3.6339 16.1567 -1 8.0783 -1V1Z"
|
|
53
|
+
fill="black"
|
|
54
|
+
mask={`url(#${path1OutsideMaskId})`}></path>
|
|
55
|
+
</mask>
|
|
56
|
+
<g mask={`url(#${mask0Id})`}>
|
|
57
|
+
<mask
|
|
58
|
+
id={path3OutsideMaskId}
|
|
59
|
+
maskUnits="userSpaceOnUse"
|
|
60
|
+
x="-1"
|
|
61
|
+
y="0.0244141"
|
|
62
|
+
width="60"
|
|
63
|
+
height="43"
|
|
64
|
+
fill="black"
|
|
65
|
+
>
|
|
66
|
+
<rect fill="white" x="-1" y="0.0244141" width="60" height="43"></rect>
|
|
67
|
+
<path
|
|
68
|
+
d="M0 1.02441H7.0783C14.772 1.02441 21.7836 5.43765 25.111 12.3746L33.8889 30.6743C37.2164 37.6112 44.228 42.0244 51.9217 42.0244H59H0L0 1.02441Z"
|
|
69
|
+
></path>
|
|
70
|
+
</mask>
|
|
71
|
+
<path
|
|
72
|
+
d="M0 1.02441H7.0783C14.772 1.02441 21.7836 5.43765 25.111 12.3746L33.8889 30.6743C37.2164 37.6112 44.228 42.0244 51.9217 42.0244H59H0L0 1.02441Z"
|
|
73
|
+
fill="#ffffff"></path>
|
|
74
|
+
<path
|
|
75
|
+
d="M0 1.02441L0 0.0244141H-1V1.02441H0ZM0 42.0244H-1V43.0244H0L0 42.0244ZM33.8889 30.6743L32.9873 31.1068L33.8889 30.6743ZM25.111 12.3746L26.0127 11.9421L25.111 12.3746ZM0 2.02441H7.0783V0.0244141H0L0 2.02441ZM59 41.0244H0L0 43.0244H59V41.0244ZM1 42.0244L1 1.02441H-1L-1 42.0244H1ZM24.2094 12.8071L32.9873 31.1068L34.7906 30.2418L26.0127 11.9421L24.2094 12.8071ZM51.9217 43.0244H59V41.0244H51.9217V43.0244ZM32.9873 31.1068C36.4811 38.3905 43.8433 43.0244 51.9217 43.0244V41.0244C44.6127 41.0244 37.9517 36.8318 34.7906 30.2418L32.9873 31.1068ZM7.0783 2.02441C14.3873 2.02441 21.0483 6.21699 24.2094 12.8071L26.0127 11.9421C22.5188 4.65831 15.1567 0.0244141 7.0783 0.0244141V2.02441Z"
|
|
76
|
+
fill="#e5e5e5"
|
|
77
|
+
mask={`url(#${path3OutsideMaskId})`}></path>
|
|
78
|
+
</g>
|
|
79
|
+
</svg>
|