docula 0.50.0 → 0.90.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -5
- package/dist/docula.d.ts +221 -15
- package/dist/docula.js +1059 -118
- package/package.json +10 -12
- package/templates/classic/api.hbs +203 -17
- package/templates/classic/changelog-entry.hbs +2 -0
- package/templates/classic/changelog.hbs +2 -0
- package/templates/classic/css/api.css +753 -0
- package/templates/classic/docs.hbs +2 -0
- package/templates/classic/home.hbs +2 -0
- package/templates/classic/includes/api-try-it.hbs +61 -0
- package/templates/classic/js/api.js +282 -0
- package/templates/modern/api.hbs +203 -18
- package/templates/modern/css/api.css +1051 -0
- package/templates/modern/css/styles.css +20 -6
- package/templates/modern/home.hbs +2 -0
- package/templates/modern/includes/api-try-it.hbs +61 -0
- package/templates/modern/includes/header-bar.hbs +3 -3
- package/templates/modern/includes/header.hbs +2 -2
- package/templates/modern/includes/scripts.hbs +20 -17
- package/templates/modern/includes/theme-toggle.hbs +2 -2
- package/templates/modern/js/api.js +300 -0
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
</head>
|
|
8
8
|
|
|
9
9
|
<body>
|
|
10
|
+
{{#if githubPath}}
|
|
10
11
|
<a href="https://github.com/{{ githubPath }}" class="github-corner" aria-label="View source on GitHub"><svg width="80"
|
|
11
12
|
height="80" viewBox="0 0 250 250" style="color:#fff; position: absolute; top: 0; border: 0; right: 0;"
|
|
12
13
|
aria-hidden="true">
|
|
@@ -51,6 +52,7 @@
|
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
</style>
|
|
55
|
+
{{/if}}
|
|
54
56
|
<main class="layout">
|
|
55
57
|
{{> multipage/header }}
|
|
56
58
|
{{> multipage/sidebar }}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
</head>
|
|
8
8
|
|
|
9
9
|
<body>
|
|
10
|
+
{{#if githubPath}}
|
|
10
11
|
<a href="https://github.com/{{ githubPath }}" class="github-corner" aria-label="View source on GitHub"><svg width="80"
|
|
11
12
|
height="80" viewBox="0 0 250 250" style="color:#fff; position: absolute; top: 0; border: 0; right: 0;"
|
|
12
13
|
aria-hidden="true">
|
|
@@ -51,6 +52,7 @@
|
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
</style>
|
|
55
|
+
{{/if}}
|
|
54
56
|
{{#if content}}
|
|
55
57
|
{{> singlepage/hero }}
|
|
56
58
|
{{> singlepage/content }}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<div class="api-try-it" data-method="{{this.methodUpper}}" data-path="{{this.path}}"{{#if this.requestBody}} data-content-type="{{this.requestBody.contentType}}"{{/if}}>
|
|
2
|
+
<span class="api-try-it__title">Test Request</span>
|
|
3
|
+
|
|
4
|
+
<div class="api-try-it__url-bar">
|
|
5
|
+
<span class="method-badge method-badge--{{this.method}}">{{this.methodUpper}}</span>
|
|
6
|
+
<div class="api-try-it__url-display">
|
|
7
|
+
<select class="api-try-it__server-select" data-try-server>
|
|
8
|
+
{{#each @root.apiSpec.servers}}
|
|
9
|
+
<option value="{{this.url}}">{{this.url}}</option>
|
|
10
|
+
{{/each}}
|
|
11
|
+
</select>
|
|
12
|
+
<span class="api-try-it__path-display">{{this.path}}</span>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
{{#if this.parameters.length}}
|
|
17
|
+
<div class="api-try-it__section">
|
|
18
|
+
<div class="api-try-it__section-label">Parameters</div>
|
|
19
|
+
{{#each this.parameters}}
|
|
20
|
+
<div class="api-try-it__param-row" data-param-in="{{this.in}}" data-param-name="{{this.name}}">
|
|
21
|
+
<div class="api-try-it__param-info">
|
|
22
|
+
<span class="api-try-it__param-name">{{this.name}}</span>
|
|
23
|
+
<span class="api-try-it__param-in">{{this.in}}</span>
|
|
24
|
+
{{#if this.required}}<span class="api-param-required">*</span>{{/if}}
|
|
25
|
+
</div>
|
|
26
|
+
{{#if (eq this.in "cookie")}}
|
|
27
|
+
<input type="text" class="api-try-it__param-input" placeholder="Not supported in browser" disabled data-try-param />
|
|
28
|
+
{{else}}
|
|
29
|
+
<input type="text" class="api-try-it__param-input" placeholder="{{this.type}}" data-try-param />
|
|
30
|
+
{{/if}}
|
|
31
|
+
</div>
|
|
32
|
+
{{/each}}
|
|
33
|
+
</div>
|
|
34
|
+
{{/if}}
|
|
35
|
+
|
|
36
|
+
{{#if this.requestBody}}
|
|
37
|
+
<div class="api-try-it__section">
|
|
38
|
+
<div class="api-try-it__section-label">Body</div>
|
|
39
|
+
<textarea class="api-try-it__body-editor" data-try-body rows="8">{{this.requestBody.example}}</textarea>
|
|
40
|
+
</div>
|
|
41
|
+
{{/if}}
|
|
42
|
+
|
|
43
|
+
<button class="api-try-it__send-btn" data-try-send>Send Request</button>
|
|
44
|
+
|
|
45
|
+
<div class="api-try-it__response api-try-it__response--hidden" data-try-response>
|
|
46
|
+
<div class="api-try-it__response-meta">
|
|
47
|
+
<span class="api-try-it__response-status" data-try-status></span>
|
|
48
|
+
<span class="api-try-it__response-time" data-try-time></span>
|
|
49
|
+
</div>
|
|
50
|
+
<div class="api-try-it__response-tabs">
|
|
51
|
+
<button class="api-try-it__rtab api-try-it__rtab--active" data-try-rtab="body">Body</button>
|
|
52
|
+
<button class="api-try-it__rtab" data-try-rtab="headers">Headers</button>
|
|
53
|
+
</div>
|
|
54
|
+
<div class="api-try-it__rpanel api-try-it__rpanel--active" data-try-rpanel="body">
|
|
55
|
+
<pre><code data-try-response-body></code></pre>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="api-try-it__rpanel" data-try-rpanel="headers">
|
|
58
|
+
<pre><code data-try-response-headers></code></pre>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
2
|
+
// Toggle operations
|
|
3
|
+
document.querySelectorAll('[data-toggle="operation"]').forEach(function(header) {
|
|
4
|
+
header.addEventListener('click', function() {
|
|
5
|
+
this.closest('.api-operation').classList.toggle('api-operation--collapsed');
|
|
6
|
+
});
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
// Toggle sidebar groups
|
|
10
|
+
document.querySelectorAll('.api-sidebar__group-toggle').forEach(function(btn) {
|
|
11
|
+
btn.addEventListener('click', function() {
|
|
12
|
+
this.closest('.api-sidebar__group').classList.toggle('api-sidebar__group--collapsed');
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Code example tabs
|
|
17
|
+
document.querySelectorAll('.api-code-tabs').forEach(function(tabs) {
|
|
18
|
+
var container = tabs.closest('.api-code-examples');
|
|
19
|
+
tabs.querySelectorAll('.api-code-tab').forEach(function(tab) {
|
|
20
|
+
tab.addEventListener('click', function() {
|
|
21
|
+
var target = this.getAttribute('data-tab');
|
|
22
|
+
container.querySelectorAll('.api-code-tab').forEach(function(t) { t.classList.remove('api-code-tab--active'); });
|
|
23
|
+
container.querySelectorAll('.api-code-panel').forEach(function(p) { p.classList.remove('api-code-panel--active'); });
|
|
24
|
+
this.classList.add('api-code-tab--active');
|
|
25
|
+
var panel = container.querySelector('[data-panel="' + target + '"]');
|
|
26
|
+
if (panel) panel.classList.add('api-code-panel--active');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Copy to clipboard
|
|
32
|
+
document.querySelectorAll('[data-copy]').forEach(function(btn) {
|
|
33
|
+
btn.addEventListener('click', function() {
|
|
34
|
+
var code = this.closest('.api-code-panel').querySelector('code');
|
|
35
|
+
if (code) {
|
|
36
|
+
navigator.clipboard.writeText(code.textContent).then(function() {
|
|
37
|
+
btn.textContent = 'Copied!';
|
|
38
|
+
setTimeout(function() { btn.textContent = 'Copy'; }, 2000);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Search
|
|
45
|
+
var searchInput = document.getElementById('api-search');
|
|
46
|
+
if (searchInput) {
|
|
47
|
+
searchInput.addEventListener('input', function() {
|
|
48
|
+
var query = this.value.toLowerCase();
|
|
49
|
+
document.querySelectorAll('.api-sidebar__item').forEach(function(item) {
|
|
50
|
+
var path = (item.getAttribute('data-path') || '').toLowerCase();
|
|
51
|
+
var method = (item.getAttribute('data-method') || '').toLowerCase();
|
|
52
|
+
var match = !query || path.indexOf(query) !== -1 || method.indexOf(query) !== -1 || item.textContent.toLowerCase().indexOf(query) !== -1;
|
|
53
|
+
item.style.display = match ? '' : 'none';
|
|
54
|
+
});
|
|
55
|
+
document.querySelectorAll('.api-operation').forEach(function(op) {
|
|
56
|
+
var path = (op.querySelector('.api-operation__path') || {}).textContent || '';
|
|
57
|
+
var method = (op.querySelector('.method-badge') || {}).textContent || '';
|
|
58
|
+
var match = !query || path.toLowerCase().indexOf(query) !== -1 || method.toLowerCase().indexOf(query) !== -1;
|
|
59
|
+
op.style.display = match ? '' : 'none';
|
|
60
|
+
});
|
|
61
|
+
document.querySelectorAll('.api-group').forEach(function(group) {
|
|
62
|
+
var visible = group.querySelectorAll('.api-operation:not([style*="display: none"])');
|
|
63
|
+
group.style.display = visible.length > 0 || !query ? '' : 'none';
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Mobile sidebar toggle
|
|
69
|
+
var sidebarToggle = document.getElementById('api-sidebar-toggle');
|
|
70
|
+
var sidebar = document.getElementById('api-sidebar');
|
|
71
|
+
if (sidebarToggle && sidebar) {
|
|
72
|
+
sidebarToggle.addEventListener('click', function() {
|
|
73
|
+
sidebar.classList.toggle('api-sidebar--mobile-open');
|
|
74
|
+
});
|
|
75
|
+
sidebar.querySelectorAll('.api-sidebar__item').forEach(function(link) {
|
|
76
|
+
link.addEventListener('click', function() {
|
|
77
|
+
sidebar.classList.remove('api-sidebar--mobile-open');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Collapse all sidebar groups by default
|
|
83
|
+
document.querySelectorAll('.api-sidebar__group').forEach(function(group) {
|
|
84
|
+
group.classList.add('api-sidebar__group--collapsed');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Auth type selector: show/hide value input
|
|
88
|
+
var authTypeSelect = document.getElementById('api-auth-type');
|
|
89
|
+
var authValueInput = document.getElementById('api-auth-value');
|
|
90
|
+
if (authTypeSelect && authValueInput) {
|
|
91
|
+
authTypeSelect.addEventListener('change', function() {
|
|
92
|
+
if (this.value === 'none') {
|
|
93
|
+
authValueInput.classList.add('api-auth__value--hidden');
|
|
94
|
+
authValueInput.value = '';
|
|
95
|
+
} else {
|
|
96
|
+
authValueInput.classList.remove('api-auth__value--hidden');
|
|
97
|
+
authValueInput.placeholder = this.value === 'apikey' ? 'Enter API key...' : 'Enter token...';
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Helper: expand an operation and its corresponding sidebar group
|
|
103
|
+
function expandOperationAndGroup(operationEl) {
|
|
104
|
+
if (!operationEl) return;
|
|
105
|
+
operationEl.classList.remove('api-operation--collapsed');
|
|
106
|
+
var contentGroup = operationEl.closest('.api-group');
|
|
107
|
+
if (contentGroup) {
|
|
108
|
+
var groupId = contentGroup.id.replace(/^group-/, '');
|
|
109
|
+
var sidebarGroup = document.querySelector('.api-sidebar__group[data-group="' + groupId + '"]');
|
|
110
|
+
if (sidebarGroup) sidebarGroup.classList.remove('api-sidebar__group--collapsed');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Expand operation via hash, or expand the first operation by default
|
|
115
|
+
if (window.location.hash) {
|
|
116
|
+
var target = document.querySelector(window.location.hash);
|
|
117
|
+
if (target && target.classList.contains('api-operation')) {
|
|
118
|
+
expandOperationAndGroup(target);
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
expandOperationAndGroup(document.querySelector('.api-operation'));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
window.addEventListener('hashchange', function() {
|
|
125
|
+
if (window.location.hash) {
|
|
126
|
+
var target = document.querySelector(window.location.hash);
|
|
127
|
+
if (target && target.classList.contains('api-operation')) {
|
|
128
|
+
expandOperationAndGroup(target);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Try It - Response tab switching
|
|
134
|
+
document.querySelectorAll('.api-try-it__response-tabs').forEach(function(tabs) {
|
|
135
|
+
var container = tabs.closest('.api-try-it__response');
|
|
136
|
+
tabs.querySelectorAll('.api-try-it__rtab').forEach(function(tab) {
|
|
137
|
+
tab.addEventListener('click', function() {
|
|
138
|
+
var target = this.getAttribute('data-try-rtab');
|
|
139
|
+
container.querySelectorAll('.api-try-it__rtab').forEach(function(t) { t.classList.remove('api-try-it__rtab--active'); });
|
|
140
|
+
container.querySelectorAll('.api-try-it__rpanel').forEach(function(p) { p.classList.remove('api-try-it__rpanel--active'); });
|
|
141
|
+
this.classList.add('api-try-it__rtab--active');
|
|
142
|
+
var panel = container.querySelector('[data-try-rpanel="' + target + '"]');
|
|
143
|
+
if (panel) panel.classList.add('api-try-it__rpanel--active');
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Try It - Helpers
|
|
149
|
+
function getStatusClass(status) {
|
|
150
|
+
if (status >= 200 && status < 300) return '2xx';
|
|
151
|
+
if (status >= 300 && status < 400) return '3xx';
|
|
152
|
+
if (status >= 400 && status < 500) return '4xx';
|
|
153
|
+
if (status >= 500) return '5xx';
|
|
154
|
+
return 'error';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function formatBody(text, contentType) {
|
|
158
|
+
if (contentType && contentType.indexOf('json') !== -1) {
|
|
159
|
+
try { return JSON.stringify(JSON.parse(text), null, 2); } catch(e) { /* ignore */ }
|
|
160
|
+
}
|
|
161
|
+
return text;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function resetResponseTabs(responseArea) {
|
|
165
|
+
var tabs = responseArea.querySelectorAll('.api-try-it__rtab');
|
|
166
|
+
var panels = responseArea.querySelectorAll('.api-try-it__rpanel');
|
|
167
|
+
tabs.forEach(function(t) { t.classList.remove('api-try-it__rtab--active'); });
|
|
168
|
+
panels.forEach(function(p) { p.classList.remove('api-try-it__rpanel--active'); });
|
|
169
|
+
tabs[0].classList.add('api-try-it__rtab--active');
|
|
170
|
+
panels[0].classList.add('api-try-it__rpanel--active');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Try It - Send request
|
|
174
|
+
document.querySelectorAll('[data-try-send]').forEach(function(btn) {
|
|
175
|
+
btn.addEventListener('click', function() {
|
|
176
|
+
var tryIt = this.closest('.api-try-it');
|
|
177
|
+
var method = tryIt.getAttribute('data-method');
|
|
178
|
+
var pathTemplate = tryIt.getAttribute('data-path');
|
|
179
|
+
var serverSelect = tryIt.querySelector('[data-try-server]');
|
|
180
|
+
var baseUrl = serverSelect ? serverSelect.value : '';
|
|
181
|
+
|
|
182
|
+
// Substitute path params
|
|
183
|
+
var path = pathTemplate;
|
|
184
|
+
tryIt.querySelectorAll('[data-param-in="path"]').forEach(function(row) {
|
|
185
|
+
var name = row.getAttribute('data-param-name');
|
|
186
|
+
var value = row.querySelector('[data-try-param]').value;
|
|
187
|
+
if (value) {
|
|
188
|
+
path = path.replace('{' + name + '}', encodeURIComponent(value));
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Build query string
|
|
193
|
+
var queryParts = [];
|
|
194
|
+
tryIt.querySelectorAll('[data-param-in="query"]').forEach(function(row) {
|
|
195
|
+
var name = row.getAttribute('data-param-name');
|
|
196
|
+
var value = row.querySelector('[data-try-param]').value;
|
|
197
|
+
if (value) queryParts.push(encodeURIComponent(name) + '=' + encodeURIComponent(value));
|
|
198
|
+
});
|
|
199
|
+
var queryString = queryParts.length ? '?' + queryParts.join('&') : '';
|
|
200
|
+
|
|
201
|
+
// Build headers
|
|
202
|
+
var headers = {};
|
|
203
|
+
tryIt.querySelectorAll('[data-param-in="header"]').forEach(function(row) {
|
|
204
|
+
var name = row.getAttribute('data-param-name');
|
|
205
|
+
var value = row.querySelector('[data-try-param]').value;
|
|
206
|
+
if (value) headers[name] = value;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Inject global auth header
|
|
210
|
+
var authType = document.getElementById('api-auth-type');
|
|
211
|
+
var authValue = document.getElementById('api-auth-value');
|
|
212
|
+
if (authType && authValue) {
|
|
213
|
+
var authVal = authValue.value.trim();
|
|
214
|
+
if (authVal) {
|
|
215
|
+
if (authType.value === 'apikey') {
|
|
216
|
+
headers['x-api-key'] = authVal;
|
|
217
|
+
} else if (authType.value === 'bearer') {
|
|
218
|
+
headers['Authorization'] = 'Bearer ' + authVal;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Body
|
|
224
|
+
var bodyTextarea = tryIt.querySelector('[data-try-body]');
|
|
225
|
+
var body = bodyTextarea ? bodyTextarea.value.trim() : '';
|
|
226
|
+
if (body && !headers['Content-Type']) {
|
|
227
|
+
headers['Content-Type'] = tryIt.getAttribute('data-content-type') || 'application/json';
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
var url = baseUrl + path + queryString;
|
|
231
|
+
var fetchOptions = { method: method, headers: headers };
|
|
232
|
+
if (body && method !== 'GET' && method !== 'HEAD') {
|
|
233
|
+
fetchOptions.body = body;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Loading state
|
|
237
|
+
btn.disabled = true;
|
|
238
|
+
btn.textContent = 'Sending...';
|
|
239
|
+
var startTime = performance.now();
|
|
240
|
+
|
|
241
|
+
var responseArea = tryIt.querySelector('[data-try-response]');
|
|
242
|
+
var statusEl = tryIt.querySelector('[data-try-status]');
|
|
243
|
+
var timeEl = tryIt.querySelector('[data-try-time]');
|
|
244
|
+
var bodyEl = tryIt.querySelector('[data-try-response-body]');
|
|
245
|
+
var headersEl = tryIt.querySelector('[data-try-response-headers]');
|
|
246
|
+
|
|
247
|
+
fetch(url, fetchOptions).then(function(response) {
|
|
248
|
+
var elapsed = Math.round(performance.now() - startTime);
|
|
249
|
+
var statusClass = getStatusClass(response.status);
|
|
250
|
+
|
|
251
|
+
statusEl.textContent = response.status + ' ' + response.statusText;
|
|
252
|
+
statusEl.className = 'api-try-it__response-status api-try-it__response-status--' + statusClass;
|
|
253
|
+
timeEl.textContent = elapsed + 'ms';
|
|
254
|
+
|
|
255
|
+
// Collect response headers
|
|
256
|
+
var headerLines = [];
|
|
257
|
+
response.headers.forEach(function(value, key) {
|
|
258
|
+
headerLines.push(key + ': ' + value);
|
|
259
|
+
});
|
|
260
|
+
headersEl.textContent = headerLines.join('\n') || 'No headers';
|
|
261
|
+
|
|
262
|
+
var ct = response.headers.get('content-type') || '';
|
|
263
|
+
return response.text().then(function(text) {
|
|
264
|
+
bodyEl.textContent = formatBody(text, ct);
|
|
265
|
+
responseArea.classList.remove('api-try-it__response--hidden');
|
|
266
|
+
resetResponseTabs(responseArea);
|
|
267
|
+
});
|
|
268
|
+
}).catch(function(err) {
|
|
269
|
+
statusEl.textContent = 'Error';
|
|
270
|
+
statusEl.className = 'api-try-it__response-status api-try-it__response-status--error';
|
|
271
|
+
timeEl.textContent = '';
|
|
272
|
+
headersEl.textContent = '';
|
|
273
|
+
bodyEl.textContent = 'Request failed: ' + err.message + '\n\nThis may be caused by CORS restrictions. The API server must include appropriate CORS headers to allow browser requests.';
|
|
274
|
+
responseArea.classList.remove('api-try-it__response--hidden');
|
|
275
|
+
resetResponseTabs(responseArea);
|
|
276
|
+
}).finally(function() {
|
|
277
|
+
btn.disabled = false;
|
|
278
|
+
btn.textContent = 'Send Request';
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
});
|
package/templates/modern/api.hbs
CHANGED
|
@@ -3,28 +3,213 @@
|
|
|
3
3
|
|
|
4
4
|
<head>
|
|
5
5
|
{{> header }}
|
|
6
|
-
<
|
|
7
|
-
<
|
|
8
|
-
<
|
|
9
|
-
<style>
|
|
10
|
-
body {
|
|
11
|
-
margin: 0;
|
|
12
|
-
padding: 0;
|
|
13
|
-
}
|
|
14
|
-
</style>
|
|
6
|
+
<link rel="stylesheet" href="/css/api.css">
|
|
7
|
+
<title>API Reference - {{ siteTitle }}</title>
|
|
8
|
+
<meta name="description" content="API Reference for {{ siteTitle }}" />
|
|
15
9
|
</head>
|
|
16
10
|
|
|
17
11
|
<body>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
</
|
|
12
|
+
{{> header-bar }}
|
|
13
|
+
|
|
14
|
+
{{#if apiSpec}}
|
|
15
|
+
<div class="api-mobile-toggle">
|
|
16
|
+
<button id="api-sidebar-toggle">
|
|
17
|
+
<span>API Navigation</span>
|
|
18
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
|
|
19
|
+
</button>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div class="api-reference">
|
|
23
|
+
<aside class="api-sidebar" id="api-sidebar">
|
|
24
|
+
<input type="text" class="api-search" id="api-search" placeholder="Search endpoints..." />
|
|
25
|
+
|
|
26
|
+
{{#each apiSpec.groups}}
|
|
27
|
+
<div class="api-sidebar__group" data-group="{{this.id}}">
|
|
28
|
+
<button class="api-sidebar__group-toggle">
|
|
29
|
+
<span>{{this.name}}</span>
|
|
30
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
|
|
31
|
+
</button>
|
|
32
|
+
<div class="api-sidebar__group-items">
|
|
33
|
+
{{#each this.operations}}
|
|
34
|
+
<a href="#{{this.id}}" class="api-sidebar__item" data-method="{{this.method}}" data-path="{{this.path}}">
|
|
35
|
+
<span class="method-badge method-badge--{{this.method}}">{{this.methodUpper}}</span>
|
|
36
|
+
<span class="api-sidebar__item-path">{{this.path}}</span>
|
|
37
|
+
</a>
|
|
38
|
+
{{/each}}
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
{{/each}}
|
|
42
|
+
</aside>
|
|
43
|
+
|
|
44
|
+
<main class="api-content">
|
|
45
|
+
<section class="api-info">
|
|
46
|
+
<h1 class="api-info__title">{{apiSpec.info.title}}</h1>
|
|
47
|
+
{{#if apiSpec.info.version}}
|
|
48
|
+
<span class="api-info__version">v{{apiSpec.info.version}}</span>
|
|
49
|
+
{{/if}}
|
|
50
|
+
{{#if apiSpec.info.description}}
|
|
51
|
+
<div class="api-info__description">{{{apiSpec.info.description}}}</div>
|
|
52
|
+
{{/if}}
|
|
53
|
+
{{#if apiSpec.servers}}
|
|
54
|
+
<div class="api-info__servers">
|
|
55
|
+
<div class="api-info__server-label">Server</div>
|
|
56
|
+
{{#each apiSpec.servers}}
|
|
57
|
+
<code class="api-info__server-url">{{this.url}}</code>
|
|
58
|
+
{{/each}}
|
|
59
|
+
</div>
|
|
60
|
+
{{/if}}
|
|
61
|
+
<div class="api-auth">
|
|
62
|
+
<div class="api-auth__label">Authorization</div>
|
|
63
|
+
<div class="api-auth__controls">
|
|
64
|
+
<select class="api-auth__type" id="api-auth-type">
|
|
65
|
+
<option value="none">None</option>
|
|
66
|
+
<option value="apikey">API Key (x-api-key)</option>
|
|
67
|
+
<option value="bearer">Bearer Token</option>
|
|
68
|
+
</select>
|
|
69
|
+
<input type="password" class="api-auth__value api-auth__value--hidden" id="api-auth-value" placeholder="Enter value..." />
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</section>
|
|
73
|
+
|
|
74
|
+
{{#each apiSpec.groups}}
|
|
75
|
+
<div class="api-group" id="group-{{this.id}}">
|
|
76
|
+
<div class="api-group__header">
|
|
77
|
+
<h2 class="api-group__title">{{this.name}}</h2>
|
|
78
|
+
{{#if this.description}}
|
|
79
|
+
<div class="api-group__description">{{{this.description}}}</div>
|
|
80
|
+
{{/if}}
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
{{#each this.operations}}
|
|
84
|
+
<div class="api-operation api-operation--collapsed" id="{{this.id}}">
|
|
85
|
+
<div class="api-operation__header" data-toggle="operation">
|
|
86
|
+
<span class="method-badge method-badge--{{this.method}}">{{this.methodUpper}}</span>
|
|
87
|
+
<span class="api-operation__path">{{this.path}}</span>
|
|
88
|
+
{{#if this.summary}}
|
|
89
|
+
<span class="api-operation__summary">{{this.summary}}</span>
|
|
90
|
+
{{/if}}
|
|
91
|
+
<svg class="api-operation__toggle-icon" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="api-operation__body">
|
|
94
|
+
<div class="api-operation__docs">
|
|
95
|
+
{{#if this.description}}
|
|
96
|
+
<div class="api-operation__description">{{{this.description}}}</div>
|
|
97
|
+
{{/if}}
|
|
98
|
+
|
|
99
|
+
{{#if this.parameters.length}}
|
|
100
|
+
<div class="api-section-title">Parameters</div>
|
|
101
|
+
<table class="api-params-table">
|
|
102
|
+
<thead>
|
|
103
|
+
<tr>
|
|
104
|
+
<th>Name</th>
|
|
105
|
+
<th>Type</th>
|
|
106
|
+
<th>In</th>
|
|
107
|
+
<th>Description</th>
|
|
108
|
+
</tr>
|
|
109
|
+
</thead>
|
|
110
|
+
<tbody>
|
|
111
|
+
{{#each this.parameters}}
|
|
112
|
+
<tr>
|
|
113
|
+
<td>
|
|
114
|
+
<span class="api-param-name">{{this.name}}</span>
|
|
115
|
+
{{#if this.required}}<span class="api-param-required">*</span>{{/if}}
|
|
116
|
+
</td>
|
|
117
|
+
<td><span class="api-param-type">{{this.type}}</span></td>
|
|
118
|
+
<td><span class="api-param-in">{{this.in}}</span></td>
|
|
119
|
+
<td><span class="api-param-desc">{{this.description}}</span></td>
|
|
120
|
+
</tr>
|
|
121
|
+
{{/each}}
|
|
122
|
+
</tbody>
|
|
123
|
+
</table>
|
|
124
|
+
{{/if}}
|
|
125
|
+
|
|
126
|
+
{{#if this.requestBody}}
|
|
127
|
+
<div class="api-section-title">Request Body <span class="api-param-type">{{this.requestBody.contentType}}</span></div>
|
|
128
|
+
{{#if this.requestBody.schemaProperties.length}}
|
|
129
|
+
<div class="api-schema">
|
|
130
|
+
<div class="api-schema-props">
|
|
131
|
+
{{#each this.requestBody.schemaProperties}}
|
|
132
|
+
<div class="api-schema-prop">
|
|
133
|
+
<span class="api-schema-prop-name">{{this.name}}{{#if this.required}}<span class="api-param-required">*</span>{{/if}}</span>
|
|
134
|
+
<span class="api-schema-prop-type">{{this.type}}</span>
|
|
135
|
+
<span class="api-schema-prop-desc">{{this.description}}</span>
|
|
136
|
+
</div>
|
|
137
|
+
{{/each}}
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
{{/if}}
|
|
141
|
+
{{#if this.requestBody.example}}
|
|
142
|
+
<div class="api-response__example">
|
|
143
|
+
<pre><code>{{this.requestBody.example}}</code></pre>
|
|
144
|
+
</div>
|
|
145
|
+
{{/if}}
|
|
146
|
+
{{/if}}
|
|
147
|
+
|
|
148
|
+
{{#if this.responses.length}}
|
|
149
|
+
<div class="api-section-title">Responses</div>
|
|
150
|
+
{{#each this.responses}}
|
|
151
|
+
<div class="api-response">
|
|
152
|
+
<div class="api-response__status">
|
|
153
|
+
<span class="api-status-code api-status-code--{{this.statusClass}}">{{this.statusCode}}</span>
|
|
154
|
+
<span class="api-response__desc">{{this.description}}</span>
|
|
155
|
+
</div>
|
|
156
|
+
{{#if this.schemaProperties.length}}
|
|
157
|
+
<div class="api-schema">
|
|
158
|
+
<div class="api-schema-props">
|
|
159
|
+
{{#each this.schemaProperties}}
|
|
160
|
+
<div class="api-schema-prop">
|
|
161
|
+
<span class="api-schema-prop-name">{{this.name}}</span>
|
|
162
|
+
<span class="api-schema-prop-type">{{this.type}}</span>
|
|
163
|
+
<span class="api-schema-prop-desc">{{this.description}}</span>
|
|
164
|
+
</div>
|
|
165
|
+
{{/each}}
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
{{/if}}
|
|
169
|
+
{{#if this.example}}
|
|
170
|
+
<div class="api-response__example">
|
|
171
|
+
<pre><code>{{this.example}}</code></pre>
|
|
172
|
+
</div>
|
|
173
|
+
{{/if}}
|
|
174
|
+
</div>
|
|
175
|
+
{{/each}}
|
|
176
|
+
{{/if}}
|
|
177
|
+
|
|
178
|
+
<div class="api-code-examples">
|
|
179
|
+
<div class="api-code-tabs">
|
|
180
|
+
<button class="api-code-tab api-code-tab--active" data-tab="curl">cURL</button>
|
|
181
|
+
<button class="api-code-tab" data-tab="javascript">JavaScript</button>
|
|
182
|
+
<button class="api-code-tab" data-tab="python">Python</button>
|
|
183
|
+
</div>
|
|
184
|
+
<div class="api-code-panel api-code-panel--active" data-panel="curl">
|
|
185
|
+
<button class="api-copy-btn" data-copy>Copy</button>
|
|
186
|
+
<pre><code>{{this.codeExamples.curl}}</code></pre>
|
|
187
|
+
</div>
|
|
188
|
+
<div class="api-code-panel" data-panel="javascript">
|
|
189
|
+
<button class="api-copy-btn" data-copy>Copy</button>
|
|
190
|
+
<pre><code>{{this.codeExamples.javascript}}</code></pre>
|
|
191
|
+
</div>
|
|
192
|
+
<div class="api-code-panel" data-panel="python">
|
|
193
|
+
<button class="api-copy-btn" data-copy>Copy</button>
|
|
194
|
+
<pre><code>{{this.codeExamples.python}}</code></pre>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
{{> api-try-it }}
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
{{/each}}
|
|
203
|
+
</div>
|
|
204
|
+
{{/each}}
|
|
205
|
+
</main>
|
|
206
|
+
</div>
|
|
207
|
+
{{/if}}
|
|
208
|
+
|
|
209
|
+
{{> footer }}
|
|
27
210
|
{{> scripts }}
|
|
211
|
+
|
|
212
|
+
<script src="/js/api.js"></script>
|
|
28
213
|
</body>
|
|
29
214
|
|
|
30
215
|
</html>
|