sourcey 3.6.2 → 3.6.4
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/README.md +24 -13
- package/dist/client/scroll-tracker.js +3 -3
- package/dist/client/tabs.js +62 -36
- package/dist/components/layout/Sidebar.d.ts.map +1 -1
- package/dist/components/layout/Sidebar.js +1 -1
- package/dist/components/openapi/CodeSamples.d.ts.map +1 -1
- package/dist/components/openapi/CodeSamples.js +4 -4
- package/dist/components/openapi/Operation.js +1 -1
- package/dist/components/openapi/RequestBody.d.ts.map +1 -1
- package/dist/components/openapi/RequestBody.js +5 -7
- package/dist/components/openapi/Responses.d.ts.map +1 -1
- package/dist/components/openapi/Responses.js +14 -24
- package/dist/components/schema/ExampleView.d.ts +24 -1
- package/dist/components/schema/ExampleView.d.ts.map +1 -1
- package/dist/components/schema/ExampleView.js +29 -8
- package/dist/core/navigation.d.ts +4 -0
- package/dist/core/navigation.d.ts.map +1 -1
- package/dist/core/navigation.js +7 -1
- package/dist/core/sourcey-rustdoc/Cargo.lock +14 -18
- package/dist/core/sourcey-rustdoc/Cargo.toml +1 -1
- package/dist/core/sourcey-rustdoc/README.md +1 -1
- package/dist/themes/default/sourcey.css +32 -1
- package/dist/utils/example-variants.d.ts +31 -0
- package/dist/utils/example-variants.d.ts.map +1 -0
- package/dist/utils/example-variants.js +99 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,18 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
> Docs straight from the source.
|
|
4
4
|
|
|
5
|
-
Sourcey
|
|
5
|
+
Sourcey turns OpenAPI, MCP, Doxygen XML, godoc, rustdoc, and Markdown into one static HTML site you own: reference, guides, changelog, roadmap, and llms.txt from the same build, deployable anywhere.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Everything renders at build time: no dashboard, no runtime, no API calls to render your own documentation.
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/sourcey)
|
|
10
10
|
[](https://github.com/sourcey/sourcey/actions)
|
|
11
11
|
[](https://nodejs.org)
|
|
12
12
|
[](LICENSE)
|
|
13
|
-
[](https://pkg.go.dev/github.com/sourcey/sourcey/go/sourcey-godoc)
|
|
14
|
-
[](https://goreportcard.com/report/github.com/sourcey/sourcey/go/sourcey-godoc)
|
|
15
|
-
[](https://crates.io/crates/sourcey)
|
|
16
|
-
[](https://docs.rs/sourcey)
|
|
17
13
|
|
|
18
14
|
```bash
|
|
19
15
|
npx sourcey init
|
|
@@ -60,6 +56,9 @@ Then `sourcey init` to scaffold a new project, or `sourcey build` against an exi
|
|
|
60
56
|
|
|
61
57
|
### Standalone Go docs generator
|
|
62
58
|
|
|
59
|
+
[](https://pkg.go.dev/github.com/sourcey/sourcey/go/sourcey-godoc)
|
|
60
|
+
[](https://goreportcard.com/report/github.com/sourcey/sourcey/go/sourcey-godoc)
|
|
61
|
+
|
|
63
62
|
For Go-only consumers without a JavaScript toolchain, `sourcey-godoc` ships as a separate native binary. It produces static Go docs sites or portable `godoc.json` snapshots.
|
|
64
63
|
|
|
65
64
|
| Path | Command |
|
|
@@ -68,19 +67,31 @@ For Go-only consumers without a JavaScript toolchain, `sourcey-godoc` ships as a
|
|
|
68
67
|
| Homebrew | `brew install sourcey/tap/sourcey-godoc` |
|
|
69
68
|
| Scoop | `scoop bucket add sourcey https://github.com/sourcey/scoop-bucket && scoop install sourcey-godoc` |
|
|
70
69
|
|
|
70
|
+
### Rust snapshot converter
|
|
71
|
+
|
|
72
|
+
[](https://crates.io/crates/sourcey)
|
|
73
|
+
[](https://docs.rs/sourcey)
|
|
74
|
+
|
|
75
|
+
The `rustdoc()` adapter consumes nightly rustdoc JSON through the [`sourcey`](https://crates.io/crates/sourcey) companion crate and its `sourcey-rustdoc` converter, so CI can build Rust API docs from a committed snapshot on a stable toolchain. See [docs/adapters/rustdoc.md](docs/adapters/rustdoc.md).
|
|
76
|
+
|
|
71
77
|
## Quick start
|
|
72
78
|
|
|
73
79
|
```bash
|
|
74
|
-
#
|
|
75
|
-
sourcey
|
|
76
|
-
|
|
77
|
-
# Multi-page site (reads sourcey.config.ts)
|
|
78
|
-
sourcey build -o docs/
|
|
80
|
+
# Scaffold a project (detects OpenAPI specs and Doxyfiles)
|
|
81
|
+
npx sourcey init
|
|
79
82
|
|
|
80
83
|
# Dev server with hot reload
|
|
81
84
|
sourcey dev
|
|
85
|
+
|
|
86
|
+
# Build static HTML to dist/
|
|
87
|
+
sourcey build
|
|
88
|
+
|
|
89
|
+
# Quick build from a single OpenAPI spec
|
|
90
|
+
sourcey build api.yaml -o dist/
|
|
82
91
|
```
|
|
83
92
|
|
|
93
|
+
In CI, the [`sourcey/build-docs`](https://github.com/sourcey/build-docs) GitHub Action runs the build and deploys to GitHub Pages; see [deploying](https://sourcey.com/docs/deploying).
|
|
94
|
+
|
|
84
95
|
## Configuration
|
|
85
96
|
|
|
86
97
|
Create `sourcey.config.ts` in your project root:
|
|
@@ -168,8 +179,8 @@ cd sourcey && npm install
|
|
|
168
179
|
npm run build && npm test
|
|
169
180
|
cd go/sourcey-godoc && go test ./... && go vet ./...
|
|
170
181
|
|
|
171
|
-
# Run the
|
|
172
|
-
cd
|
|
182
|
+
# Run the in-repo docs site against your working copy
|
|
183
|
+
cd docs && npx tsx ../src/cli.ts dev
|
|
173
184
|
```
|
|
174
185
|
|
|
175
186
|
## License
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Scroll tracker — highlights the active sidebar nav link as user scrolls.
|
|
2
2
|
//
|
|
3
3
|
// Observes [data-traverse-target] section anchors and toggles .active
|
|
4
|
-
// on the matching #nav
|
|
4
|
+
// on the matching #nav fragment link. All visual styling is in sourcey.css;
|
|
5
5
|
// this file only manages the class.
|
|
6
6
|
(function () {
|
|
7
7
|
function init() {
|
|
8
8
|
var navbar = document.getElementById('navbar');
|
|
9
|
-
var navLinks = document.querySelectorAll('#nav .nav-link');
|
|
9
|
+
var navLinks = document.querySelectorAll('#nav .nav-link, #nav .nav-group-link');
|
|
10
10
|
var targets = document.querySelectorAll('[data-traverse-target]');
|
|
11
11
|
|
|
12
12
|
// For prose pages, also track heading elements that TOC links point to
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
if (tocEl) tocEl.addEventListener('click', function (e) { handleAnchorClick(e, '.toc-item'); });
|
|
114
114
|
|
|
115
115
|
var navEl = document.getElementById('nav');
|
|
116
|
-
if (navEl) navEl.addEventListener('click', function (e) { handleAnchorClick(e, '.nav-link'); });
|
|
116
|
+
if (navEl) navEl.addEventListener('click', function (e) { handleAnchorClick(e, '.nav-link, .nav-group-link'); });
|
|
117
117
|
|
|
118
118
|
// Use scroll event for reliable activation at all positions.
|
|
119
119
|
function onScroll() {
|
package/dist/client/tabs.js
CHANGED
|
@@ -34,13 +34,13 @@
|
|
|
34
34
|
trigger.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
// Select a language from the dropdown
|
|
37
|
+
// Select a language / example variant from the dropdown
|
|
38
38
|
document.addEventListener('click', function (e) {
|
|
39
39
|
var option = e.target.closest('.code-lang-option');
|
|
40
40
|
if (!option) return;
|
|
41
41
|
|
|
42
|
-
var
|
|
43
|
-
var
|
|
42
|
+
var dropdown = option.closest('.code-lang-dropdown');
|
|
43
|
+
var canSync = !!(dropdown && dropdown.hasAttribute('data-lang-sync'));
|
|
44
44
|
var lang = option.textContent.trim();
|
|
45
45
|
|
|
46
46
|
// Close the menu
|
|
@@ -52,17 +52,16 @@
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
var scrollY = window.scrollY;
|
|
55
|
-
activateLang(
|
|
55
|
+
activateLang(option);
|
|
56
56
|
|
|
57
|
-
// Sync language across
|
|
58
|
-
if (lang !== selectedLang) {
|
|
57
|
+
// Sync language across code-sample groups only (not example variants).
|
|
58
|
+
if (canSync && lang !== selectedLang) {
|
|
59
59
|
selectedLang = lang;
|
|
60
|
+
var ownGroup = option.closest('.code-group');
|
|
60
61
|
document.querySelectorAll('.code-group').forEach(function (group) {
|
|
61
|
-
if (group ===
|
|
62
|
+
if (group === ownGroup || !group.querySelector('.code-lang-dropdown[data-lang-sync]')) return;
|
|
62
63
|
group.querySelectorAll('.code-lang-option').forEach(function (opt) {
|
|
63
|
-
if (opt.textContent.trim() === lang)
|
|
64
|
-
activateLang(group, opt.getAttribute('data-lang-index'));
|
|
65
|
-
}
|
|
64
|
+
if (opt.textContent.trim() === lang) activateLang(opt);
|
|
66
65
|
});
|
|
67
66
|
});
|
|
68
67
|
}
|
|
@@ -70,32 +69,51 @@
|
|
|
70
69
|
window.scrollTo(0, scrollY);
|
|
71
70
|
});
|
|
72
71
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
72
|
+
// The panels a dropdown controls. A header variant dropdown (in the shared
|
|
73
|
+
// card header) drives the matching response status panel; otherwise panels
|
|
74
|
+
// live in the dropdown's own `.code-lang-scope` or `.code-group`.
|
|
75
|
+
function panelScopeForOption(option) {
|
|
76
|
+
var headerWrap = option.closest('[data-response-dropdown]');
|
|
77
|
+
if (headerWrap) {
|
|
78
|
+
var idx = headerWrap.getAttribute('data-response-dropdown');
|
|
79
|
+
var group = headerWrap.closest('.code-group');
|
|
80
|
+
var panel = group && group.querySelector('.response-panel[data-response-panel="' + idx + '"]');
|
|
81
|
+
if (panel) return panel;
|
|
82
|
+
}
|
|
83
|
+
return option.closest('.code-lang-scope') || option.closest('.code-group');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function activateLang(option) {
|
|
87
|
+
var dropdown = option.closest('.code-lang-dropdown');
|
|
88
|
+
var scope = panelScopeForOption(option);
|
|
89
|
+
var index = option.getAttribute('data-lang-index');
|
|
90
|
+
|
|
91
|
+
// Update the dropdown label + options (label/trigger live in the dropdown,
|
|
92
|
+
// which for a header variant switcher is separate from the panel scope).
|
|
93
|
+
if (dropdown) {
|
|
94
|
+
var label = dropdown.querySelector('.code-lang-label');
|
|
95
|
+
dropdown.querySelectorAll('.code-lang-option').forEach(function (opt) {
|
|
96
|
+
var isActive = opt.getAttribute('data-lang-index') === index;
|
|
97
|
+
opt.setAttribute('aria-selected', isActive ? 'true' : 'false');
|
|
98
|
+
opt.className = opt.className.replace(/(dark:)?text-\[rgb\([^\]]+\)\]/g, '').trim();
|
|
99
|
+
if (isActive) {
|
|
100
|
+
opt.classList.add('text-[rgb(var(--color-primary))]', 'dark:text-[rgb(var(--color-primary-light))]');
|
|
101
|
+
if (label) label.textContent = opt.textContent.trim();
|
|
102
|
+
var triggerIcon = dropdown.querySelector('.code-lang-trigger .code-lang-icon');
|
|
103
|
+
var optIcon = opt.querySelector('.lang-icon');
|
|
104
|
+
if (triggerIcon && optIcon) triggerIcon.innerHTML = optIcon.outerHTML;
|
|
105
|
+
} else {
|
|
106
|
+
opt.classList.add('text-[rgb(var(--color-stone-600))]', 'dark:text-[rgb(var(--color-stone-400))]');
|
|
89
107
|
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
94
110
|
|
|
95
|
-
// Update panels
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
111
|
+
// Update panels within the controlled scope.
|
|
112
|
+
if (scope) {
|
|
113
|
+
scope.querySelectorAll('.code-lang-panel').forEach(function (p) {
|
|
114
|
+
p.classList.toggle('active', p.getAttribute('data-lang-panel') === index);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
99
117
|
}
|
|
100
118
|
|
|
101
119
|
// ── Response Tabs ──────────────────────────────────────────────────
|
|
@@ -120,6 +138,11 @@
|
|
|
120
138
|
p.classList.toggle('active', p.getAttribute('data-response-panel') === index);
|
|
121
139
|
});
|
|
122
140
|
|
|
141
|
+
// Show only the active status's variant switcher in the header.
|
|
142
|
+
container.querySelectorAll('[data-response-dropdown]').forEach(function (d) {
|
|
143
|
+
d.classList.toggle('hidden', d.getAttribute('data-response-dropdown') !== index);
|
|
144
|
+
});
|
|
145
|
+
|
|
123
146
|
window.scrollTo(0, scrollY);
|
|
124
147
|
});
|
|
125
148
|
|
|
@@ -132,8 +155,11 @@
|
|
|
132
155
|
var container = btn.closest('.code-group') || btn.closest('.prose-code-block');
|
|
133
156
|
if (!container) return;
|
|
134
157
|
|
|
135
|
-
// Find the active panel's code, or the nearest code element
|
|
136
|
-
|
|
158
|
+
// Find the active panel's code, or the nearest code element. With nested
|
|
159
|
+
// panels (a variant switcher inside a response tab), prefer the deepest
|
|
160
|
+
// active panel, which is last in document order.
|
|
161
|
+
var activePanels = container.querySelectorAll('.code-lang-panel.active, .response-panel.active');
|
|
162
|
+
var activePanel = activePanels.length ? activePanels[activePanels.length - 1] : null;
|
|
137
163
|
var codeEl = activePanel
|
|
138
164
|
? activePanel.querySelector('code, .code-block, .font-mono')
|
|
139
165
|
: container.querySelector('code, .code-block, .font-mono');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../../src/components/layout/Sidebar.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../../src/components/layout/Sidebar.tsx"],"names":[],"mappings":"AAsGA;;GAEG;AACH,wBAAgB,OAAO,wCA2GtB"}
|
|
@@ -32,7 +32,7 @@ function MethodPill({ method }) {
|
|
|
32
32
|
* Shared nav group rendering used by both desktop sidebar and mobile drawer.
|
|
33
33
|
*/
|
|
34
34
|
function NavGroups({ groups, activePageSlug, base }) {
|
|
35
|
-
return (_jsx(_Fragment, { children: groups.map((group, gi) => (_jsxs("div", { class: gi > 0 ? "mt-6 lg:mt-8" : "", children: [group.label && (_jsx("h5", { class: "nav-group-label", children: group.label })), _jsx("ul", { class: "space-y-px", children: group.items.map((item) => {
|
|
35
|
+
return (_jsx(_Fragment, { children: groups.map((group, gi) => (_jsxs("div", { class: gi > 0 ? "mt-6 lg:mt-8" : "", children: [group.label && (group.href ? (_jsx("a", { href: `${base}${group.href}`, class: `nav-group-label nav-group-link${group.id === activePageSlug ? " active" : ""}`, children: group.label })) : (_jsx("h5", { class: "nav-group-label", children: group.label }))), _jsx("ul", { class: "space-y-px", children: group.items.map((item) => {
|
|
36
36
|
const isActive = item.id === activePageSlug;
|
|
37
37
|
return (_jsx("li", { children: _jsxs("a", { href: `${base}${item.href}`, class: `nav-link${isActive ? " active" : ""}`, children: [item.method && _jsx(MethodPill, { method: item.method }), _jsx("span", { class: "flex-1 break-words [word-break:break-word]", children: item.label })] }) }, item.id));
|
|
38
38
|
}) })] }, group.label))) }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeSamples.d.ts","sourceRoot":"","sources":["../../../src/components/openapi/CodeSamples.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAM/D,UAAU,gBAAgB;IACxB,SAAS,EAAE,mBAAmB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"CodeSamples.d.ts","sourceRoot":"","sources":["../../../src/components/openapi/CodeSamples.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAM/D,UAAU,gBAAgB;IACxB,SAAS,EAAE,mBAAmB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,gBAAgB,uCAyE9F"}
|
|
@@ -12,8 +12,8 @@ export function CodeSamplesExamples({ operation, serverUrl, codeSampleLangs }) {
|
|
|
12
12
|
if (!samples.length)
|
|
13
13
|
return null;
|
|
14
14
|
const title = operation.summary ?? `${operation.method.toUpperCase()} ${operation.path}`;
|
|
15
|
-
return (_jsxs("div", { class: "code-group not-prose", children: [_jsxs("div", { class: "relative flex items-center justify-between gap-2 px-3", children: [_jsx("div", { class: "flex min-w-0 items-center gap-1.5 font-medium text-xs leading-6 my-1 mb-1.5", children: _jsx("span", { class: "truncate text-[rgb(var(--color-stone-950))] dark:text-[rgb(var(--color-stone-50))]", children: title }) }), _jsxs("div", { class: "flex shrink-0 items-center justify-end gap-1.5", children: [_jsxs("div", { class: "code-lang-dropdown relative", children: [_jsx("button", { type: "button", class: "code-lang-trigger group relative my-1 mb-1.5 flex items-center whitespace-nowrap font-medium leading-6 outline-0 text-[rgb(var(--color-stone-500))] dark:text-[rgb(var(--color-stone-400))] cursor-pointer text-xs", "aria-expanded": "false", "aria-haspopup": "listbox", children: _jsxs("div", { class: "z-10 flex items-center gap-1 rounded-lg px-1.5 group-hover:bg-[rgb(var(--color-stone-200)/0.5)] group-hover:text-[rgb(var(--color-primary))] dark:group-hover:bg-[rgb(var(--color-stone-700)/0.7)] dark:group-hover:text-[rgb(var(--color-primary-light))]", children: [_jsx("span", { class: "code-lang-icon", dangerouslySetInnerHTML: { __html: langIcon(samples[0].lang) } }), _jsx("span", { class: "code-lang-label truncate", children: samples[0].label ?? samples[0].lang }), _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", class: "size-3.5 shrink-0", children: [_jsx("path", { d: "m7 15 5 5 5-5" }), _jsx("path", { d: "m7 9 5-5 5 5" })] })] }) }), _jsx("div", { class: "code-lang-menu hidden absolute right-0 top-full mt-1 z-20 min-w-[120px] rounded-lg border border-[rgb(var(--color-stone-200))] dark:border-[rgb(255_255_255/0.1)] bg-[rgb(var(--color-background-light))] dark:bg-[rgb(var(--color-stone-900))] shadow-lg py-1", role: "listbox", children: samples.map((sample, i) => (_jsxs("button", { role: "option", "aria-selected": i === 0 ? "true" : "false", class: `code-lang-option w-full text-left px-3 py-1.5 text-xs font-medium flex items-center gap-1.5 hover:bg-[rgb(var(--color-stone-100))] dark:hover:bg-[rgb(255_255_255/0.05)] ${i === 0 ? "text-[rgb(var(--color-primary))] dark:text-[rgb(var(--color-primary-light))]" : "text-[rgb(var(--color-stone-600))] dark:text-[rgb(var(--color-stone-400))]"}`, "data-lang-index": String(i), children: [_jsx("span", { dangerouslySetInnerHTML: { __html: langIcon(sample.lang) } }), sample.label ?? sample.lang] }, i))) })] }), _jsx(CopyButton, {})] })] }), samples.map((sample, i) => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
return (_jsxs("div", { class: "code-group not-prose", children: [_jsxs("div", { class: "relative flex items-center justify-between gap-2 px-3", children: [_jsx("div", { class: "flex min-w-0 items-center gap-1.5 font-medium text-xs leading-6 my-1 mb-1.5", children: _jsx("span", { class: "truncate text-[rgb(var(--color-stone-950))] dark:text-[rgb(var(--color-stone-50))]", children: title }) }), _jsxs("div", { class: "flex shrink-0 items-center justify-end gap-1.5", children: [_jsxs("div", { class: "code-lang-dropdown relative", "data-lang-sync": true, children: [_jsx("button", { type: "button", class: "code-lang-trigger group relative my-1 mb-1.5 flex items-center whitespace-nowrap font-medium leading-6 outline-0 text-[rgb(var(--color-stone-500))] dark:text-[rgb(var(--color-stone-400))] cursor-pointer text-xs", "aria-expanded": "false", "aria-haspopup": "listbox", children: _jsxs("div", { class: "z-10 flex items-center gap-1 rounded-lg px-1.5 group-hover:bg-[rgb(var(--color-stone-200)/0.5)] group-hover:text-[rgb(var(--color-primary))] dark:group-hover:bg-[rgb(var(--color-stone-700)/0.7)] dark:group-hover:text-[rgb(var(--color-primary-light))]", children: [_jsx("span", { class: "code-lang-icon", dangerouslySetInnerHTML: { __html: langIcon(samples[0].lang) } }), _jsx("span", { class: "code-lang-label truncate", children: samples[0].label ?? samples[0].lang }), _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", class: "size-3.5 shrink-0", children: [_jsx("path", { d: "m7 15 5 5 5-5" }), _jsx("path", { d: "m7 9 5-5 5 5" })] })] }) }), _jsx("div", { class: "code-lang-menu hidden absolute right-0 top-full mt-1 z-20 min-w-[120px] rounded-lg border border-[rgb(var(--color-stone-200))] dark:border-[rgb(255_255_255/0.1)] bg-[rgb(var(--color-background-light))] dark:bg-[rgb(var(--color-stone-900))] shadow-lg py-1", role: "listbox", children: samples.map((sample, i) => (_jsxs("button", { role: "option", "aria-selected": i === 0 ? "true" : "false", class: `code-lang-option w-full text-left px-3 py-1.5 text-xs font-medium flex items-center gap-1.5 hover:bg-[rgb(var(--color-stone-100))] dark:hover:bg-[rgb(255_255_255/0.05)] ${i === 0 ? "text-[rgb(var(--color-primary))] dark:text-[rgb(var(--color-primary-light))]" : "text-[rgb(var(--color-stone-600))] dark:text-[rgb(var(--color-stone-400))]"}`, "data-lang-index": String(i), children: [_jsx("span", { dangerouslySetInnerHTML: { __html: langIcon(sample.lang) } }), sample.label ?? sample.lang] }, i))) })] }), _jsx(CopyButton, {})] })] }), _jsx("div", { class: "code-card-body", children: samples.map((sample, i) => {
|
|
16
|
+
const html = highlightCode(sample.source, sample.lang);
|
|
17
|
+
return (_jsx("div", { class: `code-lang-panel${i === 0 ? " active" : ""}`, "data-lang-panel": String(i), children: _jsx("div", { class: "relative w-full px-4 py-3.5 text-sm leading-6 bg-[rgb(var(--color-code-block-light))] dark:bg-[rgb(var(--color-code-block-dark))] overflow-x-auto", style: "font-variant-ligatures: none", children: _jsx("div", { class: "font-mono whitespace-pre text-xs leading-[1.35rem] code-block", dangerouslySetInnerHTML: { __html: html } }) }) }, i));
|
|
18
|
+
}) })] }));
|
|
19
19
|
}
|
|
@@ -26,7 +26,7 @@ export function Operation({ operation: op, serverUrl }) {
|
|
|
26
26
|
const hasParams = op.parameters.length > 0;
|
|
27
27
|
const hasBody = !!op.requestBody;
|
|
28
28
|
const mcp = op.mcpExtras;
|
|
29
|
-
return (_jsxs("div", { id: id, class: "py-8 border-t border-[rgb(var(--color-gray-100))] dark:border-[rgb(var(--color-gray-800))]", "data-traverse-target": id, children: [_jsx("header", { class: "mb-6", children: op.summary && (_jsxs("div", { class: "flex items-baseline gap-2 flex-wrap", children: [_jsx("h2", { class: "text-2xl sm:text-3xl text-[rgb(var(--color-gray-900))] dark:text-[rgb(var(--color-gray-200))] tracking-tight font-bold mb-2", children: _jsx(Markdown, { content: op.summary, inline: true }) }), op.deprecated && _jsx(DeprecatedBadge, {}), mcp?.annotations && _jsx(AnnotationBadges, { annotations: mcp.annotations })] })) }), mcp ? (_jsx(McpEndpointBar, { method: op.method, path: op.path })) : (_jsx(EndpointBar, { method: op.method, path: op.path, serverUrl: serverUrl })), _jsxs("div", { class: `flex flex-col ${apiFirst ? "lg:flex-row" : "xl:flex-row"} gap-8`, children: [_jsxs("div", { class: "flex-1 min-w-0", children: [op.description && (_jsx(Markdown, { content: op.description, class: "mb-6" })), hasBody && (_jsxs("div", { class: "mt-6", children: [_jsx(SectionLabel, { meta: bodyMediaType(op.requestBody), children: "Body" }), _jsx(RequestBody, { body: op.requestBody })] })), hasParams && (_jsxs("div", { class: "mt-6", children: [_jsx(SectionLabel, { children: "Parameters" }), _jsx(Parameters, { parameters: op.parameters })] })), mcp ? (_jsx(McpReturnsCopy, { schema: mcp.outputSchema })) : (op.responses.length > 0 && (_jsxs("div", { class: "mt-6", children: [_jsx(SectionLabel, { children: "Response" }), _jsx(ResponsesCopy, { responses: op.responses })] }))), !mcp && _jsx(SecurityCopy, { security: op.security })] }), _jsxs("aside", { class: `hidden ${apiFirst ? "lg:block" : "xl:block"} w-[28rem] shrink-0 sticky self-start overflow-y-auto space-y-4`, style: "top: calc(var(--header-height) + 2.5rem); max-height: calc(100vh - var(--header-height) - 5rem)", children: [_jsx(CodeSamplesExamples, { operation: op, serverUrl: serverUrl, codeSampleLangs: site.codeSamples }), hasBody && _jsx(RequestBodyExample, { body: op.requestBody }), mcp ? (_jsx(McpReturnsExample, { schema: mcp.outputSchema })) : (_jsx(ResponsesExamples, { responses: op.responses }))] })] }), _jsxs("div", { class: `${apiFirst ? "lg:hidden" : "xl:hidden"} mt-8 space-y-4`, children: [_jsx(CodeSamplesExamples, { operation: op, serverUrl: serverUrl, codeSampleLangs: site.codeSamples }), hasBody && _jsx(RequestBodyExample, { body: op.requestBody }), mcp ? (_jsx(McpReturnsExample, { schema: mcp.outputSchema })) : (_jsx(ResponsesExamples, { responses: op.responses }))] })] }));
|
|
29
|
+
return (_jsxs("div", { id: id, class: "py-8 border-t border-[rgb(var(--color-gray-100))] dark:border-[rgb(var(--color-gray-800))]", "data-traverse-target": id, children: [_jsx("header", { class: "mb-6", children: op.summary && (_jsxs("div", { class: "flex items-baseline gap-2 flex-wrap", children: [_jsx("h2", { class: "text-2xl sm:text-3xl text-[rgb(var(--color-gray-900))] dark:text-[rgb(var(--color-gray-200))] tracking-tight font-bold mb-2", children: _jsx(Markdown, { content: op.summary, inline: true }) }), op.deprecated && _jsx(DeprecatedBadge, {}), mcp?.annotations && _jsx(AnnotationBadges, { annotations: mcp.annotations })] })) }), mcp ? (_jsx(McpEndpointBar, { method: op.method, path: op.path })) : (_jsx(EndpointBar, { method: op.method, path: op.path, serverUrl: serverUrl })), _jsxs("div", { class: `flex flex-col ${apiFirst ? "lg:flex-row" : "xl:flex-row"} gap-8`, children: [_jsxs("div", { class: "flex-1 min-w-0", children: [op.description && (_jsx(Markdown, { content: op.description, class: "mb-6 max-w-none" })), hasBody && (_jsxs("div", { class: "mt-6", children: [_jsx(SectionLabel, { meta: bodyMediaType(op.requestBody), children: "Body" }), _jsx(RequestBody, { body: op.requestBody })] })), hasParams && (_jsxs("div", { class: "mt-6", children: [_jsx(SectionLabel, { children: "Parameters" }), _jsx(Parameters, { parameters: op.parameters })] })), mcp ? (_jsx(McpReturnsCopy, { schema: mcp.outputSchema })) : (op.responses.length > 0 && (_jsxs("div", { class: "mt-6", children: [_jsx(SectionLabel, { children: "Response" }), _jsx(ResponsesCopy, { responses: op.responses })] }))), !mcp && _jsx(SecurityCopy, { security: op.security })] }), _jsxs("aside", { class: `hidden ${apiFirst ? "lg:block" : "xl:block"} w-[28rem] shrink-0 sticky self-start overflow-y-auto space-y-4`, style: "top: calc(var(--header-height) + 2.5rem); max-height: calc(100vh - var(--header-height) - 5rem)", children: [_jsx(CodeSamplesExamples, { operation: op, serverUrl: serverUrl, codeSampleLangs: site.codeSamples }), hasBody && _jsx(RequestBodyExample, { body: op.requestBody }), mcp ? (_jsx(McpReturnsExample, { schema: mcp.outputSchema })) : (_jsx(ResponsesExamples, { responses: op.responses }))] })] }), _jsxs("div", { class: `${apiFirst ? "lg:hidden" : "xl:hidden"} mt-8 space-y-4`, children: [_jsx(CodeSamplesExamples, { operation: op, serverUrl: serverUrl, codeSampleLangs: site.codeSamples }), hasBody && _jsx(RequestBodyExample, { body: op.requestBody }), mcp ? (_jsx(McpReturnsExample, { schema: mcp.outputSchema })) : (_jsx(ResponsesExamples, { responses: op.responses }))] })] }));
|
|
30
30
|
}
|
|
31
31
|
function bodyMediaType(body) {
|
|
32
32
|
const types = Object.keys(body.content);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestBody.d.ts","sourceRoot":"","sources":["../../../src/components/openapi/RequestBody.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"RequestBody.d.ts","sourceRoot":"","sources":["../../../src/components/openapi/RequestBody.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAOjE,UAAU,gBAAgB;IACxB,IAAI,EAAE,qBAAqB,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,gBAAgB,uCA4BrD;AAED,UAAU,uBAAuB;IAC/B,IAAI,EAAE,qBAAqB,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,IAAI,EAAE,EAAE,uBAAuB,uCAKnE"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { SchemaView } from "../schema/SchemaView.js";
|
|
3
|
-
import {
|
|
3
|
+
import { ExampleVariantsCard } from "../schema/ExampleView.js";
|
|
4
4
|
import { Markdown } from "../ui/Markdown.js";
|
|
5
5
|
import { MediaTypeDetails } from "./MediaTypeDetails.js";
|
|
6
|
+
import { buildExampleVariants } from "../../utils/example-variants.js";
|
|
6
7
|
/**
|
|
7
8
|
* Request body content (rendered in the main content column).
|
|
8
9
|
*/
|
|
@@ -19,11 +20,8 @@ export function RequestBody({ body }) {
|
|
|
19
20
|
* Request body example (rendered in the sticky code panel).
|
|
20
21
|
*/
|
|
21
22
|
export function RequestBodyExample({ body }) {
|
|
22
|
-
const
|
|
23
|
-
if (!
|
|
24
|
-
return null;
|
|
25
|
-
const [, content] = mediaTypes[0];
|
|
26
|
-
if (!content.schema)
|
|
23
|
+
const variants = buildExampleVariants(body.content);
|
|
24
|
+
if (!variants.length)
|
|
27
25
|
return null;
|
|
28
|
-
return _jsx(
|
|
26
|
+
return _jsx(ExampleVariantsCard, { variants: variants, title: "Request Body" });
|
|
29
27
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Responses.d.ts","sourceRoot":"","sources":["../../../src/components/openapi/Responses.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"Responses.d.ts","sourceRoot":"","sources":["../../../src/components/openapi/Responses.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAQ9D,UAAU,cAAc;IACtB,SAAS,EAAE,kBAAkB,EAAE,CAAC;CACjC;AAUD;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,EAAE,cAAc,uCAoC1D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,EAAE,cAAc,uCAkF9D"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { httpStatusText } from "../../utils/http.js";
|
|
3
3
|
import { SchemaDatatype } from "../schema/SchemaDatatype.js";
|
|
4
|
-
import {
|
|
4
|
+
import { ExampleVariantsCard, VariantBody, VariantDropdown } from "../schema/ExampleView.js";
|
|
5
5
|
import { Markdown } from "../ui/Markdown.js";
|
|
6
6
|
import { CopyButton } from "../ui/CopyButton.js";
|
|
7
|
-
import {
|
|
8
|
-
import { highlightCode } from "../../utils/highlighter.js";
|
|
7
|
+
import { buildExampleVariants } from "../../utils/example-variants.js";
|
|
9
8
|
function statusColorClass(code) {
|
|
10
9
|
if (code.startsWith("2"))
|
|
11
10
|
return "bg-green-100 text-green-800 dark:bg-green-400/20 dark:text-green-300";
|
|
@@ -32,29 +31,20 @@ export function ResponsesCopy({ responses }) {
|
|
|
32
31
|
export function ResponsesExamples({ responses }) {
|
|
33
32
|
if (!responses.length)
|
|
34
33
|
return null;
|
|
35
|
-
const
|
|
36
|
-
.map((r) => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return null;
|
|
40
|
-
const example = schema.example ?? generateExample(schema);
|
|
41
|
-
if (example === undefined)
|
|
42
|
-
return null;
|
|
43
|
-
const json = JSON.stringify(example, null, 2);
|
|
44
|
-
const html = highlightCode(json, "json");
|
|
45
|
-
return { statusCode: r.statusCode, html };
|
|
46
|
-
})
|
|
47
|
-
.filter(Boolean);
|
|
48
|
-
if (!examples.length)
|
|
34
|
+
const groups = responses
|
|
35
|
+
.map((r) => ({ statusCode: r.statusCode, variants: buildExampleVariants(r.content) }))
|
|
36
|
+
.filter((g) => g.variants.length > 0);
|
|
37
|
+
if (!groups.length)
|
|
49
38
|
return null;
|
|
50
|
-
// Single
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return _jsx(ExampleView, { schema: schema, title: `${examples[0].statusCode}` });
|
|
39
|
+
// Single status code: a standalone card, with a variant switcher when the
|
|
40
|
+
// response carries more than one example (named examples / content types).
|
|
41
|
+
if (groups.length === 1) {
|
|
42
|
+
return _jsx(ExampleVariantsCard, { variants: groups[0].variants, title: groups[0].statusCode });
|
|
55
43
|
}
|
|
56
|
-
// Multiple
|
|
57
|
-
|
|
44
|
+
// Multiple status codes: status tabs on the left of the header; on the right,
|
|
45
|
+
// a per-status variant switcher (shown for the active status) next to copy,
|
|
46
|
+
// matching the language select. Panels live in the clipped card body.
|
|
47
|
+
return (_jsxs("div", { class: "response-tabs code-group not-prose", children: [_jsxs("div", { class: "relative flex items-center justify-between gap-2 px-3", children: [_jsx("div", { class: "response-tab-list flex gap-1 overflow-x-auto text-xs leading-6", role: "tablist", children: groups.map((g, i) => (_jsx("button", { type: "button", role: "tab", "aria-selected": i === 0 ? "true" : "false", class: `response-tab group relative my-1 mb-1.5 flex items-center gap-1.5 whitespace-nowrap font-medium outline-0${i === 0 ? " active" : ""}`, "data-response-index": String(i), children: _jsx("div", { class: "z-10 flex items-center gap-1.5 rounded-lg px-1.5 group-hover:bg-[rgb(var(--color-stone-200)/0.5)] group-hover:text-[rgb(var(--color-primary))] dark:group-hover:bg-[rgb(var(--color-stone-700)/0.7)] dark:group-hover:text-[rgb(var(--color-primary-light))]", children: g.statusCode }) }, g.statusCode))) }), _jsxs("div", { class: "flex shrink-0 items-center justify-end gap-1.5", children: [groups.map((g, i) => g.variants.length > 1 ? (_jsx("div", { "data-response-dropdown": String(i), class: i === 0 ? undefined : "hidden", children: _jsx(VariantDropdown, { variants: g.variants }) }, g.statusCode)) : null), _jsx(CopyButton, {})] })] }), _jsx("div", { class: "code-card-body", children: groups.map((g, i) => (_jsx("div", { class: `response-panel${i === 0 ? " active" : ""}`, role: "tabpanel", "data-response-panel": String(i), children: g.variants.length > 1 ? (g.variants.map((v, j) => (_jsx("div", { class: `code-lang-panel${j === 0 ? " active" : ""}`, "data-lang-panel": String(j), children: _jsx(VariantBody, { variant: v }) }, j)))) : (_jsx(VariantBody, { variant: g.variants[0] })) }, g.statusCode))) })] }));
|
|
58
48
|
}
|
|
59
49
|
function renderResponseType(r) {
|
|
60
50
|
const schema = getResponseSchema(r);
|
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
import type { NormalizedSchema } from "../../core/types.js";
|
|
2
|
+
import type { ExampleVariant } from "../../utils/example-variants.js";
|
|
3
|
+
/** Render one example body (highlighted JSON or an external-example link). */
|
|
4
|
+
export declare function VariantBody({ variant, showCopy }: {
|
|
5
|
+
variant: ExampleVariant;
|
|
6
|
+
showCopy?: boolean;
|
|
7
|
+
}): import("preact").JSX.Element;
|
|
8
|
+
/**
|
|
9
|
+
* Variant switcher reusing the code-sample language dropdown markup/JS, minus
|
|
10
|
+
* the cross-group language sync (no `data-lang-sync`). Sits in the card header
|
|
11
|
+
* to the left of the copy button, identical to the language select.
|
|
12
|
+
*/
|
|
13
|
+
export declare function VariantDropdown({ variants }: {
|
|
14
|
+
variants: ExampleVariant[];
|
|
15
|
+
}): import("preact").JSX.Element;
|
|
16
|
+
interface ExampleVariantsCardProps {
|
|
17
|
+
variants: ExampleVariant[];
|
|
18
|
+
title?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Standalone example card: a `.code-group` with an optional title, a variant
|
|
22
|
+
* switcher when more than one example exists, and a copy button.
|
|
23
|
+
*/
|
|
24
|
+
export declare function ExampleVariantsCard({ variants, title }: ExampleVariantsCardProps): import("preact").JSX.Element | null;
|
|
2
25
|
interface ExampleViewProps {
|
|
3
26
|
schema: NormalizedSchema;
|
|
4
27
|
title?: string;
|
|
5
28
|
}
|
|
6
29
|
/**
|
|
7
|
-
* Renders a JSON example in a stone-themed code card
|
|
30
|
+
* Renders a schema-derived JSON example in a stone-themed code card.
|
|
8
31
|
*/
|
|
9
32
|
export declare function ExampleView({ schema, title }: ExampleViewProps): import("preact").JSX.Element | null;
|
|
10
33
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExampleView.d.ts","sourceRoot":"","sources":["../../../src/components/schema/ExampleView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"ExampleView.d.ts","sourceRoot":"","sources":["../../../src/components/schema/ExampleView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAQtE,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;IAAE,OAAO,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,gCA8BjG;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,cAAc,EAAE,CAAA;CAAE,gCAgC3E;AAED,UAAU,wBAAwB;IAChC,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,wBAAwB,uCAsChF;AAED,UAAU,gBAAgB;IACxB,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,gBAAgB,uCAI9D"}
|
|
@@ -1,15 +1,36 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { highlightCode } from "../../utils/highlighter.js";
|
|
2
|
+
import { variantFromSchema } from "../../utils/example-variants.js";
|
|
4
3
|
import { CopyButton } from "../ui/CopyButton.js";
|
|
4
|
+
const BODY_WRAP = "relative w-full px-4 py-3.5 text-sm leading-6 bg-[rgb(var(--color-code-block-light))] dark:bg-[rgb(var(--color-code-block-dark))] overflow-x-auto";
|
|
5
|
+
const BODY_CODE = "font-mono whitespace-pre text-xs leading-[1.35rem]";
|
|
6
|
+
/** Render one example body (highlighted JSON or an external-example link). */
|
|
7
|
+
export function VariantBody({ variant, showCopy }) {
|
|
8
|
+
return (_jsxs("div", { class: BODY_WRAP, style: "font-variant-ligatures: none", children: [showCopy && (_jsx("div", { class: "absolute top-3 right-4 z-10", children: _jsx(CopyButton, {}) })), variant.summary && (_jsx("p", { class: "mb-2 text-xs text-[rgb(var(--color-gray-500))] dark:text-[rgb(var(--color-gray-400))]", children: variant.summary })), variant.externalValue ? (_jsxs("p", { class: "text-xs", children: [_jsx("span", { class: "text-[rgb(var(--color-gray-500))]", children: "External example: " }), _jsx("a", { href: variant.externalValue, target: "_blank", rel: "noopener noreferrer", class: "break-all text-[rgb(var(--color-primary))] hover:underline", children: variant.externalValue })] })) : (_jsx("div", { class: BODY_CODE, dangerouslySetInnerHTML: { __html: variant.html } }))] }));
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Variant switcher reusing the code-sample language dropdown markup/JS, minus
|
|
12
|
+
* the cross-group language sync (no `data-lang-sync`). Sits in the card header
|
|
13
|
+
* to the left of the copy button, identical to the language select.
|
|
14
|
+
*/
|
|
15
|
+
export function VariantDropdown({ variants }) {
|
|
16
|
+
return (_jsxs("div", { class: "code-lang-dropdown relative", children: [_jsx("button", { type: "button", class: "code-lang-trigger group relative my-1 mb-1.5 flex items-center whitespace-nowrap font-medium leading-6 outline-0 text-[rgb(var(--color-stone-500))] dark:text-[rgb(var(--color-stone-400))] cursor-pointer text-xs", "aria-expanded": "false", "aria-haspopup": "listbox", children: _jsxs("div", { class: "z-10 flex items-center gap-1 rounded-lg px-1.5 group-hover:bg-[rgb(var(--color-stone-200)/0.5)] group-hover:text-[rgb(var(--color-primary))] dark:group-hover:bg-[rgb(var(--color-stone-700)/0.7)] dark:group-hover:text-[rgb(var(--color-primary-light))]", children: [_jsx("span", { class: "code-lang-label truncate", children: variants[0].label }), _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", class: "size-3.5 shrink-0", children: [_jsx("path", { d: "m7 15 5 5 5-5" }), _jsx("path", { d: "m7 9 5-5 5 5" })] })] }) }), _jsx("div", { class: "code-lang-menu hidden absolute right-0 top-full mt-1 z-20 min-w-[140px] rounded-lg border border-[rgb(var(--color-stone-200))] dark:border-[rgb(255_255_255/0.1)] bg-[rgb(var(--color-background-light))] dark:bg-[rgb(var(--color-stone-900))] shadow-lg py-1", role: "listbox", children: variants.map((v, i) => (_jsx("button", { role: "option", "aria-selected": i === 0 ? "true" : "false", class: `code-lang-option w-full text-left px-3 py-1.5 text-xs font-medium flex items-center gap-1.5 hover:bg-[rgb(var(--color-stone-100))] dark:hover:bg-[rgb(255_255_255/0.05)] ${i === 0 ? "text-[rgb(var(--color-primary))] dark:text-[rgb(var(--color-primary-light))]" : "text-[rgb(var(--color-stone-600))] dark:text-[rgb(var(--color-stone-400))]"}`, "data-lang-index": String(i), children: v.label }, i))) })] }));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Standalone example card: a `.code-group` with an optional title, a variant
|
|
20
|
+
* switcher when more than one example exists, and a copy button.
|
|
21
|
+
*/
|
|
22
|
+
export function ExampleVariantsCard({ variants, title }) {
|
|
23
|
+
if (!variants.length)
|
|
24
|
+
return null;
|
|
25
|
+
const multi = variants.length > 1;
|
|
26
|
+
return (_jsxs("div", { class: "code-group not-prose", children: [(title || multi) && (_jsxs("div", { class: "relative flex items-center justify-between gap-2 px-3", children: [_jsx("div", { class: "flex min-w-0 items-center gap-1.5 font-medium text-xs leading-6 my-1 mb-1.5", children: title && (_jsx("span", { class: "truncate text-[rgb(var(--color-stone-950))] dark:text-[rgb(var(--color-stone-50))]", children: title })) }), _jsxs("div", { class: "flex shrink-0 items-center justify-end gap-1.5", children: [multi && _jsx(VariantDropdown, { variants: variants }), _jsx(CopyButton, {})] })] })), _jsx("div", { class: "code-card-body", children: multi ? (variants.map((v, i) => (_jsx("div", { class: `code-lang-panel${i === 0 ? " active" : ""}`, "data-lang-panel": String(i), children: _jsx(VariantBody, { variant: v }) }, i)))) : (_jsx(VariantBody, { variant: variants[0], showCopy: !title })) })] }));
|
|
27
|
+
}
|
|
5
28
|
/**
|
|
6
|
-
* Renders a JSON example in a stone-themed code card
|
|
29
|
+
* Renders a schema-derived JSON example in a stone-themed code card.
|
|
7
30
|
*/
|
|
8
31
|
export function ExampleView({ schema, title }) {
|
|
9
|
-
const
|
|
10
|
-
if (
|
|
32
|
+
const variant = variantFromSchema(schema, title ?? "Example");
|
|
33
|
+
if (!variant)
|
|
11
34
|
return null;
|
|
12
|
-
|
|
13
|
-
const html = highlightCode(json, "json");
|
|
14
|
-
return (_jsxs("div", { class: "code-group not-prose", children: [title && (_jsxs("div", { class: "relative flex items-center justify-between gap-2 px-3", children: [_jsx("div", { class: "flex min-w-0 items-center gap-1.5 font-medium text-xs leading-6 my-1 mb-1.5", children: _jsx("span", { class: "truncate text-[rgb(var(--color-stone-950))] dark:text-[rgb(var(--color-stone-50))]", children: title }) }), _jsx("div", { class: "flex shrink-0 items-center justify-end gap-1.5", children: _jsx(CopyButton, {}) })] })), _jsxs("div", { class: "relative w-full px-4 py-3.5 text-sm leading-6 bg-[rgb(var(--color-code-block-light))] dark:bg-[rgb(var(--color-code-block-dark))] overflow-x-auto", style: "font-variant-ligatures: none", children: [!title && (_jsx("div", { class: "absolute top-3 right-4 z-10", children: _jsx(CopyButton, {}) })), _jsx("div", { class: "font-mono whitespace-pre text-xs leading-[1.35rem]", dangerouslySetInnerHTML: { __html: html } })] })] }));
|
|
35
|
+
return _jsx(ExampleVariantsCard, { variants: [variant], title: title });
|
|
15
36
|
}
|
|
@@ -17,6 +17,10 @@ export interface SiteTab {
|
|
|
17
17
|
}
|
|
18
18
|
export interface SiteNavGroup {
|
|
19
19
|
label: string;
|
|
20
|
+
/** Optional section link for generated groups such as OpenAPI tag intros. */
|
|
21
|
+
href?: string;
|
|
22
|
+
/** Unique ID for section-level active-state matching. */
|
|
23
|
+
id?: string;
|
|
20
24
|
items: SiteNavItem[];
|
|
21
25
|
}
|
|
22
26
|
export interface SiteNavItem {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/core/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAgB/D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,MAAM,EACf,UAAU,GAAE,UAAkB,GAC7B,OAAO,
|
|
1
|
+
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/core/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAgB/D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,MAAM,EACf,UAAU,GAAE,UAAkB,GAC7B,OAAO,CAuDT;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,WAAW,EAChB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EAClC,UAAU,GAAE,UAAkB,GAC7B,OAAO,CA8BT;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,cAAc,CASnE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,cAAc,CAEhB"}
|
package/dist/core/navigation.js
CHANGED
|
@@ -40,6 +40,7 @@ export function buildNavFromSpec(spec, tabSlug, prettyUrls = false) {
|
|
|
40
40
|
for (const tag of spec.tags) {
|
|
41
41
|
if (tag.hidden)
|
|
42
42
|
continue;
|
|
43
|
+
const tagId = `tag-${htmlId(tag.name)}`;
|
|
43
44
|
const items = tag.operations.map((op) => ({
|
|
44
45
|
label: op.summary ?? `${op.method.toUpperCase()} ${op.path}`,
|
|
45
46
|
href: `${basePath}#operation-${htmlId(op.path)}-${htmlId(op.method)}`,
|
|
@@ -48,7 +49,12 @@ export function buildNavFromSpec(spec, tabSlug, prettyUrls = false) {
|
|
|
48
49
|
}));
|
|
49
50
|
if (!items.length)
|
|
50
51
|
continue;
|
|
51
|
-
groups.push({
|
|
52
|
+
groups.push({
|
|
53
|
+
label: tagNavigationLabel(tag, tagsByName),
|
|
54
|
+
href: `${basePath}#${tagId}`,
|
|
55
|
+
id: tagId,
|
|
56
|
+
items,
|
|
57
|
+
});
|
|
52
58
|
}
|
|
53
59
|
// Models group
|
|
54
60
|
const schemaNames = Object.keys(spec.schemas);
|
|
@@ -66,15 +66,15 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
|
|
66
66
|
|
|
67
67
|
[[package]]
|
|
68
68
|
name = "bitflags"
|
|
69
|
-
version = "2.
|
|
69
|
+
version = "2.13.0"
|
|
70
70
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
71
|
-
checksum = "
|
|
71
|
+
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
|
72
72
|
|
|
73
73
|
[[package]]
|
|
74
74
|
name = "camino"
|
|
75
|
-
version = "1.2.
|
|
75
|
+
version = "1.2.3"
|
|
76
76
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
77
|
-
checksum = "
|
|
77
|
+
checksum = "b4ce8d3bd5823c7504d3f579f13e7b2f3da252fcb938c594d5680ee508bf846f"
|
|
78
78
|
dependencies = [
|
|
79
79
|
"serde_core",
|
|
80
80
|
]
|
|
@@ -180,9 +180,6 @@ name = "deranged"
|
|
|
180
180
|
version = "0.5.8"
|
|
181
181
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
182
182
|
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
|
183
|
-
dependencies = [
|
|
184
|
-
"powerfmt",
|
|
185
|
-
]
|
|
186
183
|
|
|
187
184
|
[[package]]
|
|
188
185
|
name = "equivalent"
|
|
@@ -245,9 +242,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|
|
245
242
|
|
|
246
243
|
[[package]]
|
|
247
244
|
name = "memchr"
|
|
248
|
-
version = "2.8.
|
|
245
|
+
version = "2.8.2"
|
|
249
246
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
250
|
-
checksum = "
|
|
247
|
+
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
|
251
248
|
|
|
252
249
|
[[package]]
|
|
253
250
|
name = "miniz_oxide"
|
|
@@ -452,9 +449,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|
|
452
449
|
|
|
453
450
|
[[package]]
|
|
454
451
|
name = "syn"
|
|
455
|
-
version = "2.0.
|
|
452
|
+
version = "2.0.118"
|
|
456
453
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
457
|
-
checksum = "
|
|
454
|
+
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
|
458
455
|
dependencies = [
|
|
459
456
|
"proc-macro2",
|
|
460
457
|
"quote",
|
|
@@ -503,12 +500,11 @@ dependencies = [
|
|
|
503
500
|
|
|
504
501
|
[[package]]
|
|
505
502
|
name = "time"
|
|
506
|
-
version = "0.3.
|
|
503
|
+
version = "0.3.49"
|
|
507
504
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
508
|
-
checksum = "
|
|
505
|
+
checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469"
|
|
509
506
|
dependencies = [
|
|
510
507
|
"deranged",
|
|
511
|
-
"itoa",
|
|
512
508
|
"num-conv",
|
|
513
509
|
"powerfmt",
|
|
514
510
|
"serde_core",
|
|
@@ -518,15 +514,15 @@ dependencies = [
|
|
|
518
514
|
|
|
519
515
|
[[package]]
|
|
520
516
|
name = "time-core"
|
|
521
|
-
version = "0.1.
|
|
517
|
+
version = "0.1.9"
|
|
522
518
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
523
|
-
checksum = "
|
|
519
|
+
checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
|
|
524
520
|
|
|
525
521
|
[[package]]
|
|
526
522
|
name = "time-macros"
|
|
527
|
-
version = "0.2.
|
|
523
|
+
version = "0.2.29"
|
|
528
524
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
529
|
-
checksum = "
|
|
525
|
+
checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d"
|
|
530
526
|
dependencies = [
|
|
531
527
|
"num-conv",
|
|
532
528
|
"time-core",
|
|
@@ -6,7 +6,7 @@ description = "Convert nightly rustdoc JSON into a stable RustdocSpec snapshot.
|
|
|
6
6
|
license = "AGPL-3.0-only"
|
|
7
7
|
repository = "https://github.com/sourcey/sourcey"
|
|
8
8
|
homepage = "https://sourcey.com"
|
|
9
|
-
documentation = "https://sourcey.com/docs/
|
|
9
|
+
documentation = "https://sourcey.com/docs/rustdoc-integration"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
keywords = ["rustdoc", "documentation", "json", "rust-api"]
|
|
12
12
|
categories = ["development-tools", "command-line-utilities"]
|
|
@@ -16,7 +16,7 @@ this crate directly. It is published for:
|
|
|
16
16
|
- CI tools that drive snapshot generation from Rust
|
|
17
17
|
|
|
18
18
|
If you just want sourcey for your project, see
|
|
19
|
-
[sourcey.com/docs/
|
|
19
|
+
[sourcey.com/docs/rustdoc-integration](https://sourcey.com/docs/rustdoc-integration).
|
|
20
20
|
|
|
21
21
|
## CLI
|
|
22
22
|
|
|
@@ -218,6 +218,7 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
|
|
|
218
218
|
/* ── Nav Links ────────────────────────────────────────────────────── */
|
|
219
219
|
|
|
220
220
|
#sourcey .nav-group-label {
|
|
221
|
+
display: block;
|
|
221
222
|
padding: 0 0.75rem 0.25rem 1rem;
|
|
222
223
|
margin-bottom: 0.625rem;
|
|
223
224
|
font-size: inherit;
|
|
@@ -227,6 +228,24 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
|
|
|
227
228
|
.dark #sourcey .nav-group-label {
|
|
228
229
|
color: rgb(var(--color-gray-200));
|
|
229
230
|
}
|
|
231
|
+
#sourcey .nav-group-link {
|
|
232
|
+
cursor: pointer;
|
|
233
|
+
border-radius: 0.75rem;
|
|
234
|
+
text-decoration: none;
|
|
235
|
+
transition: color 0.15s, background-color 0.15s;
|
|
236
|
+
}
|
|
237
|
+
#sourcey .nav-group-link:hover {
|
|
238
|
+
color: rgb(var(--color-primary-ink));
|
|
239
|
+
}
|
|
240
|
+
.dark #sourcey .nav-group-link:hover {
|
|
241
|
+
color: rgb(var(--color-primary-light));
|
|
242
|
+
}
|
|
243
|
+
#sourcey .nav-group-link.active {
|
|
244
|
+
color: rgb(var(--color-primary-ink));
|
|
245
|
+
}
|
|
246
|
+
.dark #sourcey .nav-group-link.active {
|
|
247
|
+
color: rgb(var(--color-primary-light));
|
|
248
|
+
}
|
|
230
249
|
|
|
231
250
|
#sourcey .nav-tab-label {
|
|
232
251
|
display: block;
|
|
@@ -1425,7 +1444,9 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
|
|
|
1425
1444
|
position: relative;
|
|
1426
1445
|
display: flex;
|
|
1427
1446
|
flex-direction: column;
|
|
1428
|
-
|
|
1447
|
+
/* Visible so an open header dropdown menu is not clipped by the card; the
|
|
1448
|
+
code body's rounded corners are clipped by .code-card-body instead. */
|
|
1449
|
+
overflow: visible;
|
|
1429
1450
|
border-radius: var(--radius);
|
|
1430
1451
|
border: 1px solid rgb(var(--color-stone-950) / 0.1);
|
|
1431
1452
|
}
|
|
@@ -1435,11 +1456,21 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
|
|
|
1435
1456
|
|
|
1436
1457
|
#sourcey .code-group > div:first-child {
|
|
1437
1458
|
background: rgb(var(--color-stone-50));
|
|
1459
|
+
border-top-left-radius: var(--radius);
|
|
1460
|
+
border-top-right-radius: var(--radius);
|
|
1438
1461
|
}
|
|
1439
1462
|
.dark #sourcey .code-group > div:first-child {
|
|
1440
1463
|
background: rgb(255 255 255 / 0.03);
|
|
1441
1464
|
}
|
|
1442
1465
|
|
|
1466
|
+
/* Clips the code panels' rounded bottom corners now that the card itself does
|
|
1467
|
+
not clip. Horizontal code scrolling stays on the inner body element. */
|
|
1468
|
+
#sourcey .code-card-body {
|
|
1469
|
+
overflow: hidden;
|
|
1470
|
+
border-bottom-left-radius: var(--radius);
|
|
1471
|
+
border-bottom-right-radius: var(--radius);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1443
1474
|
/* ── Selection ─────────────────────────────────────────────────────── */
|
|
1444
1475
|
|
|
1445
1476
|
#sourcey ::selection { background: rgb(var(--color-primary) / 0.12); }
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MediaTypeContent, NormalizedSchema } from "../core/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* A single, render-ready example. One media type can contribute several
|
|
4
|
+
* (named `examples`); each becomes its own variant so the UI can offer a
|
|
5
|
+
* switcher between them.
|
|
6
|
+
*/
|
|
7
|
+
export interface ExampleVariant {
|
|
8
|
+
/** Label shown in the variant switcher (example name, media type, or "Example"). */
|
|
9
|
+
label: string;
|
|
10
|
+
/** Optional caption from a named example's `summary`/`description`. */
|
|
11
|
+
summary?: string;
|
|
12
|
+
/** Media type the example came from (e.g. `application/json`). */
|
|
13
|
+
mediaType: string;
|
|
14
|
+
/** Syntax-highlighted HTML body, empty when the example is external-only. */
|
|
15
|
+
html: string;
|
|
16
|
+
/** Raw text body (for copy + tests), or the URL for an external example. */
|
|
17
|
+
raw: string;
|
|
18
|
+
/** Set when the example is referenced via `externalValue` rather than inline. */
|
|
19
|
+
externalValue?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Build a single variant from a schema, falling back to a generated example. */
|
|
22
|
+
export declare function variantFromSchema(schema: NormalizedSchema, label?: string, mediaType?: string): ExampleVariant | null;
|
|
23
|
+
/**
|
|
24
|
+
* Collect every render-ready example for a content map (a response, request
|
|
25
|
+
* body, or parameter). Provided examples win over the schema: named `examples`
|
|
26
|
+
* first, then a single `example`, and only then a schema-generated fallback.
|
|
27
|
+
* Every media type contributes, so multi-content-type responses surface them
|
|
28
|
+
* all rather than just the first.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildExampleVariants(content?: Record<string, MediaTypeContent>): ExampleVariant[];
|
|
31
|
+
//# sourceMappingURL=example-variants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example-variants.d.ts","sourceRoot":"","sources":["../../src/utils/example-variants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAI1F;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AA2DD,iFAAiF;AACjF,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,gBAAgB,EACxB,KAAK,SAAY,EACjB,SAAS,SAAqB,GAC7B,cAAc,GAAG,IAAI,CAIvB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACzC,cAAc,EAAE,CAiClB"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { generateExample } from "./example-generator.js";
|
|
2
|
+
import { highlightCode } from "./highlighter.js";
|
|
3
|
+
/** Map a media type to a highlighter language, defaulting to JSON. */
|
|
4
|
+
function langForMediaType(mediaType) {
|
|
5
|
+
const m = mediaType.toLowerCase();
|
|
6
|
+
if (m.includes("xml"))
|
|
7
|
+
return "xml";
|
|
8
|
+
if (m.includes("yaml") || m.includes("yml"))
|
|
9
|
+
return "yaml";
|
|
10
|
+
if (m.includes("html"))
|
|
11
|
+
return "html";
|
|
12
|
+
if (m.includes("json"))
|
|
13
|
+
return "json";
|
|
14
|
+
if (m.startsWith("text/"))
|
|
15
|
+
return "text";
|
|
16
|
+
return "json";
|
|
17
|
+
}
|
|
18
|
+
/** Build a variant from a concrete example value. */
|
|
19
|
+
function variantFromValue(value, opts) {
|
|
20
|
+
const lang = langForMediaType(opts.mediaType);
|
|
21
|
+
// JSON media encodes any value (a bare string "hi" renders as "hi"); other
|
|
22
|
+
// media types keep a provided string body verbatim (pre-formatted XML/text).
|
|
23
|
+
const raw = typeof value === "string" && lang !== "json"
|
|
24
|
+
? value
|
|
25
|
+
: JSON.stringify(value, null, 2);
|
|
26
|
+
return {
|
|
27
|
+
label: opts.label,
|
|
28
|
+
summary: opts.summary,
|
|
29
|
+
mediaType: opts.mediaType,
|
|
30
|
+
raw,
|
|
31
|
+
html: highlightCode(raw, lang),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Build a variant from a named OpenAPI example object. */
|
|
35
|
+
function variantFromExample(name, example, mediaType) {
|
|
36
|
+
if (example.value !== undefined) {
|
|
37
|
+
return variantFromValue(example.value, {
|
|
38
|
+
label: example.summary ?? name,
|
|
39
|
+
summary: example.description,
|
|
40
|
+
mediaType,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (example.externalValue) {
|
|
44
|
+
return {
|
|
45
|
+
label: example.summary ?? name,
|
|
46
|
+
summary: example.description,
|
|
47
|
+
mediaType,
|
|
48
|
+
raw: example.externalValue,
|
|
49
|
+
html: "",
|
|
50
|
+
externalValue: example.externalValue,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
/** Build a single variant from a schema, falling back to a generated example. */
|
|
56
|
+
export function variantFromSchema(schema, label = "Example", mediaType = "application/json") {
|
|
57
|
+
const value = schema.example ?? generateExample(schema);
|
|
58
|
+
if (value === undefined)
|
|
59
|
+
return null;
|
|
60
|
+
return variantFromValue(value, { label, mediaType });
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Collect every render-ready example for a content map (a response, request
|
|
64
|
+
* body, or parameter). Provided examples win over the schema: named `examples`
|
|
65
|
+
* first, then a single `example`, and only then a schema-generated fallback.
|
|
66
|
+
* Every media type contributes, so multi-content-type responses surface them
|
|
67
|
+
* all rather than just the first.
|
|
68
|
+
*/
|
|
69
|
+
export function buildExampleVariants(content) {
|
|
70
|
+
if (!content)
|
|
71
|
+
return [];
|
|
72
|
+
const entries = Object.entries(content);
|
|
73
|
+
const multiMedia = entries.length > 1;
|
|
74
|
+
const variants = [];
|
|
75
|
+
for (const [mediaType, media] of entries) {
|
|
76
|
+
const named = media.examples ? Object.entries(media.examples) : [];
|
|
77
|
+
if (named.length) {
|
|
78
|
+
for (const [name, example] of named) {
|
|
79
|
+
const variant = variantFromExample(name, example, mediaType);
|
|
80
|
+
if (variant)
|
|
81
|
+
variants.push(variant);
|
|
82
|
+
}
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (media.example !== undefined) {
|
|
86
|
+
variants.push(variantFromValue(media.example, {
|
|
87
|
+
label: multiMedia ? mediaType : "Example",
|
|
88
|
+
mediaType,
|
|
89
|
+
}));
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (media.schema) {
|
|
93
|
+
const variant = variantFromSchema(media.schema, multiMedia ? mediaType : "Example", mediaType);
|
|
94
|
+
if (variant)
|
|
95
|
+
variants.push(variant);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return variants;
|
|
99
|
+
}
|