sourcey 3.6.0 → 3.6.1

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.
Files changed (82) hide show
  1. package/README.md +5 -2
  2. package/dist/adapters/index.d.ts +3 -2
  3. package/dist/adapters/index.d.ts.map +1 -1
  4. package/dist/adapters/index.js +35 -3
  5. package/dist/adapters/types.d.ts +8 -2
  6. package/dist/adapters/types.d.ts.map +1 -1
  7. package/dist/cli.js +28 -34
  8. package/dist/client/search.js +181 -119
  9. package/dist/client/tabs.js +23 -0
  10. package/dist/components/layout/Head.js +2 -2
  11. package/dist/components/openapi/Introduction.d.ts.map +1 -1
  12. package/dist/components/openapi/Introduction.js +16 -1
  13. package/dist/components/openapi/Security.d.ts.map +1 -1
  14. package/dist/components/openapi/Security.js +18 -2
  15. package/dist/config.d.ts +63 -0
  16. package/dist/config.d.ts.map +1 -1
  17. package/dist/config.js +21 -7
  18. package/dist/core/api-rendering.d.ts +47 -0
  19. package/dist/core/api-rendering.d.ts.map +1 -0
  20. package/dist/core/api-rendering.js +76 -0
  21. package/dist/core/doxygen-loader.d.ts.map +1 -1
  22. package/dist/core/doxygen-loader.js +35 -18
  23. package/dist/core/godoc-loader.d.ts.map +1 -1
  24. package/dist/core/godoc-loader.js +17 -6
  25. package/dist/core/markdown-loader.d.ts.map +1 -1
  26. package/dist/core/markdown-loader.js +8 -5
  27. package/dist/core/mcp-normalizer.d.ts.map +1 -1
  28. package/dist/core/mcp-normalizer.js +48 -34
  29. package/dist/core/parser.d.ts.map +1 -1
  30. package/dist/core/parser.js +75 -4
  31. package/dist/core/rustdoc-introspector.d.ts +29 -0
  32. package/dist/core/rustdoc-introspector.d.ts.map +1 -0
  33. package/dist/core/rustdoc-introspector.js +236 -0
  34. package/dist/core/rustdoc-loader.d.ts +25 -0
  35. package/dist/core/rustdoc-loader.d.ts.map +1 -0
  36. package/dist/core/rustdoc-loader.js +469 -0
  37. package/dist/core/rustdoc-render.d.ts +76 -0
  38. package/dist/core/rustdoc-render.d.ts.map +1 -0
  39. package/dist/core/rustdoc-render.js +785 -0
  40. package/dist/core/rustdoc-types.d.ts +314 -0
  41. package/dist/core/rustdoc-types.d.ts.map +1 -0
  42. package/dist/core/rustdoc-types.js +18 -0
  43. package/dist/core/search-indexer.js +1 -1
  44. package/dist/core/sourcey-rustdoc/Cargo.lock +705 -0
  45. package/dist/core/sourcey-rustdoc/Cargo.toml +38 -0
  46. package/dist/core/sourcey-rustdoc/README.md +59 -0
  47. package/dist/core/sourcey-rustdoc/src/diagnostics.rs +42 -0
  48. package/dist/core/sourcey-rustdoc/src/doctest.rs +268 -0
  49. package/dist/core/sourcey-rustdoc/src/extract.rs +492 -0
  50. package/dist/core/sourcey-rustdoc/src/lib.rs +58 -0
  51. package/dist/core/sourcey-rustdoc/src/links.rs +126 -0
  52. package/dist/core/sourcey-rustdoc/src/main.rs +256 -0
  53. package/dist/core/sourcey-rustdoc/src/signature.rs +415 -0
  54. package/dist/core/sourcey-rustdoc/src/spec.rs +390 -0
  55. package/dist/core/sourcey-rustdoc/tests/format_version.rs +45 -0
  56. package/dist/dev-server.d.ts.map +1 -1
  57. package/dist/dev-server.js +49 -0
  58. package/dist/index.d.ts +4 -2
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +22 -13
  61. package/dist/init.js +2 -2
  62. package/dist/renderer/changelog-feed.d.ts.map +1 -1
  63. package/dist/renderer/changelog-feed.js +27 -19
  64. package/dist/renderer/html-builder.d.ts.map +1 -1
  65. package/dist/renderer/html-builder.js +4 -3
  66. package/dist/renderer/llms.js +57 -2
  67. package/dist/site-assembly.d.ts +5 -0
  68. package/dist/site-assembly.d.ts.map +1 -1
  69. package/dist/site-assembly.js +55 -1
  70. package/dist/themes/default/sourcey.css +300 -0
  71. package/dist/utils/code-samples.d.ts +1 -0
  72. package/dist/utils/code-samples.d.ts.map +1 -1
  73. package/dist/utils/code-samples.js +30 -22
  74. package/dist/utils/html.d.ts +10 -0
  75. package/dist/utils/html.d.ts.map +1 -0
  76. package/dist/utils/html.js +152 -0
  77. package/dist/utils/http.d.ts +0 -2
  78. package/dist/utils/http.d.ts.map +1 -1
  79. package/dist/utils/http.js +19 -34
  80. package/dist/utils/markdown.d.ts.map +1 -1
  81. package/dist/utils/markdown.js +102 -37
  82. package/package.json +9 -4
@@ -1,10 +1,10 @@
1
1
  // Search — client-side search dialog with keyboard navigation
2
2
  (function () {
3
3
  function init() {
4
- var dialog = document.getElementById('search-dialog');
5
- var input = document.getElementById('search-input');
6
- var results = document.getElementById('search-results');
7
- var openBtn = document.getElementById('search-open');
4
+ var dialog = document.getElementById("search-dialog");
5
+ var input = document.getElementById("search-input");
6
+ var results = document.getElementById("search-results");
7
+ var openBtn = document.getElementById("search-open");
8
8
  if (!dialog || !input || !results) return;
9
9
 
10
10
  var entries = [];
@@ -16,104 +16,126 @@
16
16
  var searchMeta = document.querySelector('meta[name="sourcey-search"]');
17
17
 
18
18
  function loadJsonIndex(callback) {
19
- if (indexLoaded) { callback(); return; }
20
- if (!searchMeta) { indexLoaded = true; callback(); return; }
21
- var url = searchMeta.getAttribute('content');
22
- fetch(url).then(function (r) { return r.json(); }).then(function (data) {
23
- entries = data.map(function (e) {
24
- var title = e.title || '';
25
- var qualifiedName = e.qualifiedName || '';
26
- var owner = e.owner || '';
27
- var tab = e.tab || '';
28
- return {
29
- url: e.url,
30
- method: e.method || '',
31
- path: e.path || '',
32
- title: title,
33
- summary: qualifiedName || title,
34
- tag: owner ? owner : tab,
35
- content: e.content || '',
36
- category: e.category || '',
37
- featured: !!e.featured,
38
- symbolKind: e.symbolKind || '',
39
- owner: owner,
40
- ownerKind: e.ownerKind || '',
41
- namespace: e.namespace || '',
42
- qualifiedName: qualifiedName,
43
- titleLower: title.toLowerCase(),
44
- pathLower: (e.path || '').toLowerCase(),
45
- qualifiedLower: qualifiedName.toLowerCase(),
46
- ownerLower: owner.toLowerCase(),
47
- searchText: [
48
- e.method || '',
49
- e.path || '',
50
- title,
51
- qualifiedName,
52
- owner,
53
- e.ownerKind || '',
54
- e.namespace || '',
55
- e.symbolKind || '',
56
- e.content || '',
57
- tab
58
- ].join(' ').toLowerCase()
59
- };
60
- });
61
- indexLoaded = true;
19
+ if (indexLoaded) {
62
20
  callback();
63
- }).catch(function () {
21
+ return;
22
+ }
23
+ if (!searchMeta) {
64
24
  indexLoaded = true;
65
25
  callback();
66
- });
26
+ return;
27
+ }
28
+ var url = searchMeta.getAttribute("content");
29
+ fetch(url)
30
+ .then(function (r) {
31
+ return r.json();
32
+ })
33
+ .then(function (data) {
34
+ entries = data.map(function (e) {
35
+ var title = e.title || "";
36
+ var qualifiedName = e.qualifiedName || "";
37
+ var owner = e.owner || "";
38
+ var tab = e.tab || "";
39
+ return {
40
+ url: e.url,
41
+ method: e.method || "",
42
+ path: e.path || "",
43
+ title: title,
44
+ summary: qualifiedName || title,
45
+ tag: owner ? owner : tab,
46
+ content: e.content || "",
47
+ category: e.category || "",
48
+ featured: !!e.featured,
49
+ symbolKind: e.symbolKind || "",
50
+ owner: owner,
51
+ ownerKind: e.ownerKind || "",
52
+ namespace: e.namespace || "",
53
+ qualifiedName: qualifiedName,
54
+ titleLower: title.toLowerCase(),
55
+ pathLower: (e.path || "").toLowerCase(),
56
+ qualifiedLower: qualifiedName.toLowerCase(),
57
+ ownerLower: owner.toLowerCase(),
58
+ searchText: [
59
+ e.method || "",
60
+ e.path || "",
61
+ title,
62
+ qualifiedName,
63
+ owner,
64
+ e.ownerKind || "",
65
+ e.namespace || "",
66
+ e.symbolKind || "",
67
+ e.content || "",
68
+ tab,
69
+ ]
70
+ .join(" ")
71
+ .toLowerCase(),
72
+ };
73
+ });
74
+ indexLoaded = true;
75
+ callback();
76
+ })
77
+ .catch(function () {
78
+ indexLoaded = true;
79
+ callback();
80
+ });
67
81
  }
68
82
 
69
- var dialogInner = dialog.querySelector('.search-dialog-inner');
83
+ var dialogInner = dialog.querySelector(".search-dialog-inner");
70
84
 
71
85
  function positionDialog() {
72
86
  if (!openBtn || !dialogInner) return;
73
87
  var rect = openBtn.getBoundingClientRect();
74
- dialogInner.style.position = 'absolute';
75
- dialogInner.style.top = (rect.top - 4) + 'px';
88
+ dialogInner.style.position = "absolute";
89
+ dialogInner.style.top = rect.top - 4 + "px";
76
90
  var extraWidth = Math.min(rect.width * 0.5, 200);
77
- dialogInner.style.left = (rect.left - extraWidth / 2) + 'px';
78
- dialogInner.style.width = (rect.width + extraWidth) + 'px';
79
- dialogInner.style.maxWidth = 'none';
80
- dialogInner.style.transform = 'none';
81
- dialogInner.style.margin = '0';
91
+ dialogInner.style.left = rect.left - extraWidth / 2 + "px";
92
+ dialogInner.style.width = rect.width + extraWidth + "px";
93
+ dialogInner.style.maxWidth = "none";
94
+ dialogInner.style.transform = "none";
95
+ dialogInner.style.margin = "0";
82
96
  }
83
97
 
84
98
  function open() {
85
99
  positionDialog();
86
- dialog.classList.add('open');
87
- input.value = '';
100
+ dialog.classList.add("open");
101
+ input.value = "";
88
102
  input.focus();
89
103
  if (!indexLoaded) {
90
104
  results.innerHTML = '<div class="search-loading">Loading…</div>';
91
- loadJsonIndex(function () { showResults(''); });
105
+ loadJsonIndex(function () {
106
+ showResults("");
107
+ });
92
108
  } else {
93
- showResults('');
109
+ showResults("");
94
110
  }
95
- document.addEventListener('keydown', onDialogKey);
111
+ document.addEventListener("keydown", onDialogKey);
96
112
  }
97
113
 
98
114
  function close() {
99
- dialog.classList.remove('open');
100
- document.removeEventListener('keydown', onDialogKey);
115
+ dialog.classList.remove("open");
116
+ document.removeEventListener("keydown", onDialogKey);
101
117
  }
102
118
 
103
119
  function showResults(query) {
104
120
  var q = query.toLowerCase().trim();
105
121
  if (!q) {
106
122
  // Show featured pages first, then endpoints
107
- var featured = entries.filter(function (e) { return e.featured; });
108
- var rest = entries.filter(function (e) { return !e.featured && e.category !== 'Sections'; });
123
+ var featured = entries.filter(function (e) {
124
+ return e.featured;
125
+ });
126
+ var rest = entries.filter(function (e) {
127
+ return !e.featured && e.category !== "Sections";
128
+ });
109
129
  filtered = featured.concat(rest).slice(0, 30);
110
130
  } else {
111
131
  var terms = q.split(/\s+/);
112
- filtered = entries.map(function (e) {
113
- return { entry: e, score: scoreEntry(e, q, terms) };
114
- }).filter(function (result) {
115
- return result.score > 0;
116
- });
132
+ filtered = entries
133
+ .map(function (e) {
134
+ return { entry: e, score: scoreEntry(e, q, terms) };
135
+ })
136
+ .filter(function (result) {
137
+ return result.score > 0;
138
+ });
117
139
  var categoryOrder = {
118
140
  Pages: 0,
119
141
  Endpoints: 1,
@@ -121,18 +143,22 @@
121
143
  Functions: 3,
122
144
  Types: 4,
123
145
  Enums: 5,
124
- 'Enum Values': 6,
146
+ "Enum Values": 6,
125
147
  Variables: 7,
126
148
  Friends: 8,
127
149
  Properties: 9,
128
150
  Members: 10,
129
- Sections: 20
151
+ Sections: 20,
130
152
  };
131
153
  filtered.sort(function (a, b) {
132
154
  if (b.score !== a.score) return b.score - a.score;
133
155
  return (categoryOrder[a.entry.category] || 19) - (categoryOrder[b.entry.category] || 19);
134
156
  });
135
- filtered = filtered.map(function (result) { return result.entry; }).slice(0, 50);
157
+ filtered = filtered
158
+ .map(function (result) {
159
+ return result.entry;
160
+ })
161
+ .slice(0, 50);
136
162
  }
137
163
 
138
164
  activeIndex = filtered.length ? 0 : -1;
@@ -140,72 +166,104 @@
140
166
  }
141
167
 
142
168
  function scoreEntry(e, q, terms) {
143
- if (!terms.every(function (t) { return e.searchText.indexOf(t) !== -1; })) return 0;
169
+ if (
170
+ !terms.every(function (t) {
171
+ return e.searchText.indexOf(t) !== -1;
172
+ })
173
+ )
174
+ return 0;
144
175
 
145
- var compactQuery = q.replace(/\s+/g, '');
146
- var qualified = e.qualifiedLower || '';
147
- var title = e.titleLower || '';
148
- var path = e.pathLower || '';
149
- var owner = e.ownerLower || '';
176
+ var compactQuery = q.replace(/\s+/g, "");
177
+ var qualified = e.qualifiedLower || "";
178
+ var title = e.titleLower || "";
179
+ var path = e.pathLower || "";
180
+ var owner = e.ownerLower || "";
150
181
  var score = 1;
151
182
 
152
183
  if (qualified && qualified === compactQuery) score += 1200;
153
184
  if (title && title === q) score += 900;
154
185
  if (path && path === q) score += 850;
155
- if (qualified && qualified.endsWith('::' + compactQuery)) score += 780;
156
- if (owner && compactQuery === owner + '::' + title) score += 760;
157
- if (qualified && compactQuery.indexOf('::') !== -1 && qualified.indexOf(compactQuery) !== -1) score += 700;
186
+ if (qualified && qualified.endsWith("::" + compactQuery)) score += 780;
187
+ if (owner && compactQuery === owner + "::" + title) score += 760;
188
+ if (qualified && compactQuery.indexOf("::") !== -1 && qualified.indexOf(compactQuery) !== -1)
189
+ score += 700;
158
190
  if (title && title.indexOf(q) === 0) score += 500;
159
191
  if (qualified && qualified.indexOf(compactQuery) !== -1) score += 420;
160
192
  if (owner && owner.indexOf(compactQuery) !== -1) score += 240;
161
193
  if (path && path.indexOf(q) !== -1) score += 180;
162
194
  if (e.content.toLowerCase().indexOf(q) !== -1) score += 80;
163
195
 
164
- if (e.category === 'Sections') score -= 70;
196
+ if (e.category === "Sections") score -= 70;
165
197
  if (e.symbolKind) score += 35;
166
198
  if (e.featured) score += 20;
167
199
  return score;
168
200
  }
169
201
 
170
202
  function render() {
171
- var html = '';
172
- var lastCategory = '';
203
+ var html = "";
204
+ var lastCategory = "";
173
205
 
174
206
  for (var i = 0; i < filtered.length; i++) {
175
207
  var e = filtered[i];
176
- var cat = e.category || 'Results';
208
+ var cat = e.category || "Results";
177
209
 
178
210
  if (cat !== lastCategory) {
179
- html += '<div class="search-category">' + escapeHtml(cat) + '</div>';
211
+ html += '<div class="search-category">' + escapeHtml(cat) + "</div>";
180
212
  lastCategory = cat;
181
213
  }
182
214
 
183
- var cls = 'search-result' + (i === activeIndex ? ' active' : '');
215
+ var cls = "search-result" + (i === activeIndex ? " active" : "");
184
216
  var label = e.method
185
- ? '<span class="search-result-method method-' + e.method.toLowerCase() + '">' + e.method + '</span> ' +
186
- '<span class="search-result-path">' + escapeHtml(e.path) + '</span>'
187
- : '<span class="search-result-path">' + escapeHtml(e.summary) + '</span>';
188
- var tagLine = e.tag ? '<span class="search-result-tag">' + escapeHtml(e.tag) + '</span>' : '';
189
- var summaryLine = e.method && e.summary ? '<span class="search-result-summary">' + escapeHtml(e.summary) + '</span>' : '';
190
-
191
- html += '<a href="' + e.url + '" class="' + cls + '" data-index="' + i + '">' +
192
- '<div class="search-result-main">' + label + summaryLine + '</div>' +
193
- tagLine + '</a>';
217
+ ? '<span class="search-result-method method-' +
218
+ e.method.toLowerCase() +
219
+ '">' +
220
+ e.method +
221
+ "</span> " +
222
+ '<span class="search-result-path">' +
223
+ escapeHtml(e.path) +
224
+ "</span>"
225
+ : '<span class="search-result-path">' + escapeHtml(e.summary) + "</span>";
226
+ var tagLine = e.tag
227
+ ? '<span class="search-result-tag">' + escapeHtml(e.tag) + "</span>"
228
+ : "";
229
+ var summaryLine =
230
+ e.method && e.summary
231
+ ? '<span class="search-result-summary">' + escapeHtml(e.summary) + "</span>"
232
+ : "";
233
+
234
+ html +=
235
+ '<a href="' +
236
+ escapeAttr(e.url) +
237
+ '" class="' +
238
+ cls +
239
+ '" data-index="' +
240
+ i +
241
+ '">' +
242
+ '<div class="search-result-main">' +
243
+ label +
244
+ summaryLine +
245
+ "</div>" +
246
+ tagLine +
247
+ "</a>";
194
248
  }
195
249
 
196
250
  results.innerHTML = html;
197
251
 
198
252
  // Scroll active result into view
199
- var activeEl = results.querySelector('.search-result.active');
200
- if (activeEl) activeEl.scrollIntoView({ block: 'nearest' });
253
+ var activeEl = results.querySelector(".search-result.active");
254
+ if (activeEl) activeEl.scrollIntoView({ block: "nearest" });
201
255
  }
202
256
 
203
257
  function escapeHtml(s) {
204
- var el = document.createElement('span');
258
+ var el = document.createElement("span");
205
259
  el.textContent = s;
206
260
  return el.innerHTML;
207
261
  }
208
262
 
263
+ function escapeAttr(s) {
264
+ return escapeHtml(s).replace(/"/g, "&quot;").replace(/'/g, "&#39;");
265
+ }
266
+
209
267
  function navigate(index) {
210
268
  if (index < 0 || index >= filtered.length) return;
211
269
  var entry = filtered[index];
@@ -214,71 +272,75 @@
214
272
  }
215
273
 
216
274
  function onDialogKey(e) {
217
- if (e.key === 'Escape') { close(); e.preventDefault(); return; }
218
- if (e.key === 'ArrowDown') {
275
+ if (e.key === "Escape") {
276
+ close();
277
+ e.preventDefault();
278
+ return;
279
+ }
280
+ if (e.key === "ArrowDown") {
219
281
  e.preventDefault();
220
282
  activeIndex = Math.min(activeIndex + 1, filtered.length - 1);
221
283
  render();
222
284
  return;
223
285
  }
224
- if (e.key === 'ArrowUp') {
286
+ if (e.key === "ArrowUp") {
225
287
  e.preventDefault();
226
288
  activeIndex = Math.max(activeIndex - 1, 0);
227
289
  render();
228
290
  return;
229
291
  }
230
- if (e.key === 'Enter') {
292
+ if (e.key === "Enter") {
231
293
  e.preventDefault();
232
294
  if (activeIndex >= 0) navigate(activeIndex);
233
295
  return;
234
296
  }
235
297
  }
236
298
 
237
- input.addEventListener('input', function () {
299
+ input.addEventListener("input", function () {
238
300
  showResults(input.value);
239
301
  });
240
302
 
241
- results.addEventListener('click', function (e) {
242
- var result = e.target.closest('.search-result');
303
+ results.addEventListener("click", function (e) {
304
+ var result = e.target.closest(".search-result");
243
305
  if (result) {
244
306
  e.preventDefault();
245
- navigate(parseInt(result.getAttribute('data-index'), 10));
307
+ navigate(parseInt(result.getAttribute("data-index"), 10));
246
308
  }
247
309
  });
248
310
 
249
311
  // Open search
250
- if (openBtn) openBtn.addEventListener('click', open);
312
+ if (openBtn) openBtn.addEventListener("click", open);
251
313
 
252
314
  // Also bind mobile search button
253
- var mobileBtn = document.getElementById('search-open-mobile');
254
- if (mobileBtn) mobileBtn.addEventListener('click', open);
315
+ var mobileBtn = document.getElementById("search-open-mobile");
316
+ if (mobileBtn) mobileBtn.addEventListener("click", open);
255
317
 
256
318
  // Keyboard shortcut: Ctrl+K or /
257
- document.addEventListener('keydown', function (e) {
258
- if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
319
+ document.addEventListener("keydown", function (e) {
320
+ if ((e.ctrlKey || e.metaKey) && e.key === "k") {
259
321
  e.preventDefault();
260
322
  open();
261
323
  }
262
- if (e.key === '/' && !isEditable(e.target)) {
324
+ if (e.key === "/" && !isEditable(e.target)) {
263
325
  e.preventDefault();
264
326
  open();
265
327
  }
266
328
  });
267
329
 
268
330
  // Close on backdrop click
269
- dialog.addEventListener('click', function (e) {
331
+ dialog.addEventListener("click", function (e) {
270
332
  if (e.target === dialog) close();
271
333
  });
272
334
 
273
335
  function isEditable(el) {
274
336
  var tag = el.tagName;
275
- return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || el.isContentEditable;
337
+ return tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || el.isContentEditable;
276
338
  }
277
339
  }
278
340
 
279
- if ('requestIdleCallback' in window) {
341
+ if ("requestIdleCallback" in window) {
280
342
  window.requestIdleCallback(init, { timeout: 250 });
281
343
  } else {
282
- window.addEventListener('load', init, { once: true });
344
+ window.addEventListener("load", init, { once: true });
283
345
  }
284
346
  })();
@@ -156,6 +156,29 @@
156
156
  });
157
157
  });
158
158
 
159
+ // ── Rust Doctest Hidden-Line Toggle ──────────────────────────────
160
+ // (Copy is handled by the shared .copy-btn inside each code block.)
161
+
162
+ document.addEventListener('click', function (e) {
163
+ var toggleBtn = e.target.closest('.rust-doctest-toggle-hidden');
164
+ if (!toggleBtn) return;
165
+ var fullSel = toggleBtn.getAttribute('data-target');
166
+ var displaySel = toggleBtn.getAttribute('data-display');
167
+ var fullEl = fullSel ? document.getElementById(fullSel.replace(/^#/, '')) : null;
168
+ var displayEl = displaySel ? document.getElementById(displaySel.replace(/^#/, '')) : null;
169
+ if (!fullEl || !displayEl) return;
170
+ var fullHidden = fullEl.hasAttribute('hidden');
171
+ if (fullHidden) {
172
+ fullEl.removeAttribute('hidden');
173
+ displayEl.setAttribute('hidden', '');
174
+ toggleBtn.textContent = 'Hide hidden lines';
175
+ } else {
176
+ fullEl.setAttribute('hidden', '');
177
+ displayEl.removeAttribute('hidden');
178
+ toggleBtn.textContent = 'Show hidden lines';
179
+ }
180
+ });
181
+
159
182
  // Close dropdowns on Escape
160
183
  document.addEventListener('keydown', function (e) {
161
184
  if (e.key === 'Escape') {
@@ -17,7 +17,7 @@ export function Head() {
17
17
  : page.kind === "changelog"
18
18
  ? (() => {
19
19
  const baseTitle = changelogVersion
20
- ? `${changelogVersion.version ?? "Unreleased"} ${page.changelog.title}`
20
+ ? `${changelogVersion.version ?? "Unreleased"} - ${page.changelog.title}`
21
21
  : page.changelog.title;
22
22
  return composePageTitle(baseTitle, siteName);
23
23
  })()
@@ -63,7 +63,7 @@ function composePageTitle(title, siteName) {
63
63
  return title;
64
64
  if (!title)
65
65
  return siteName;
66
- return sameTitle(title, siteName) ? title : `${title} ${siteName}`;
66
+ return sameTitle(title, siteName) ? title : `${title} - ${siteName}`;
67
67
  }
68
68
  function sameTitle(left, right) {
69
69
  return normalizeTitle(left) === normalizeTitle(right);
@@ -1 +1 @@
1
- {"version":3,"file":"Introduction.d.ts","sourceRoot":"","sources":["../../../src/components/openapi/Introduction.tsx"],"names":[],"mappings":"AAIA;;;GAGG;AACH,wBAAgB,YAAY,iCAqC3B"}
1
+ {"version":3,"file":"Introduction.d.ts","sourceRoot":"","sources":["../../../src/components/openapi/Introduction.tsx"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,YAAY,iCAmE3B"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { useContext } from "preact/hooks";
3
3
  import { SpecContext } from "../../renderer/context.js";
4
+ import { safeUrl } from "../../utils/html.js";
4
5
  import { Markdown } from "../ui/Markdown.js";
5
6
  /**
6
7
  * API introduction: description, contact info, server URLs.
@@ -9,5 +10,19 @@ import { Markdown } from "../ui/Markdown.js";
9
10
  export function Introduction() {
10
11
  const spec = useContext(SpecContext);
11
12
  const { info, servers } = spec;
12
- return (_jsxs("div", { id: "introduction", "data-traverse-target": "introduction", class: "mb-8", children: [info.termsOfService && (_jsx("p", { class: "mt-4 text-sm text-[rgb(var(--color-gray-500))]", children: _jsx("a", { href: info.termsOfService, class: "text-[rgb(var(--color-primary-ink))] dark:text-[rgb(var(--color-primary-light))]", children: "Terms of Service" }) })), info.contact?.email && (_jsxs("p", { class: "mt-2 text-sm text-[rgb(var(--color-gray-500))]", children: ["Contact: ", _jsx("a", { href: `mailto:${info.contact.email}`, class: "text-[rgb(var(--color-primary-ink))] dark:text-[rgb(var(--color-primary-light))]", children: info.contact.email })] })), servers.length > 0 && (_jsxs("div", { class: "mt-6 rounded-[var(--radius)] border border-[rgb(var(--color-gray-200)/0.7)] dark:border-[rgb(var(--color-border-dark-subtle)/0.1)] overflow-x-auto", children: [_jsxs("div", { class: "px-4 py-2.5 text-xs font-semibold text-[rgb(var(--color-gray-600))] dark:text-[rgb(var(--color-gray-300))] border-b border-[rgb(var(--color-gray-200)/0.7)] dark:border-[rgb(var(--color-border-dark-subtle)/0.1)]", children: ["Base URL", servers.length > 1 ? "s" : ""] }), servers.map((s, i) => (_jsxs("div", { class: `flex items-baseline gap-3 px-4 py-2 ${i > 0 ? "border-t border-[rgb(var(--color-gray-100))] dark:border-[rgb(var(--color-gray-800))]" : ""}`, children: [_jsx("code", { class: "font-mono text-sm text-[rgb(var(--color-gray-800))] dark:text-[rgb(var(--color-gray-200))]", children: s.url }), s.description && (_jsx("span", { class: "text-xs text-[rgb(var(--color-gray-500))]", children: s.description }))] }, i)))] })), info.description && _jsx(Markdown, { content: info.description, class: "max-w-none mt-8" })] }));
13
+ const termsUrl = info.termsOfService
14
+ ? safeUrl(info.termsOfService, {
15
+ allowedProtocols: ["http:", "https:"],
16
+ allowRelative: false,
17
+ allowAnchor: false,
18
+ })
19
+ : null;
20
+ const contactEmailUrl = info.contact?.email
21
+ ? safeUrl(`mailto:${info.contact.email}`, {
22
+ allowedProtocols: ["mailto:"],
23
+ allowRelative: false,
24
+ allowAnchor: false,
25
+ })
26
+ : null;
27
+ return (_jsxs("div", { id: "introduction", "data-traverse-target": "introduction", class: "mb-8", children: [termsUrl && (_jsx("p", { class: "mt-4 text-sm text-[rgb(var(--color-gray-500))]", children: _jsx("a", { href: termsUrl, class: "text-[rgb(var(--color-primary-ink))] dark:text-[rgb(var(--color-primary-light))]", children: "Terms of Service" }) })), info.contact?.email && contactEmailUrl && (_jsxs("p", { class: "mt-2 text-sm text-[rgb(var(--color-gray-500))]", children: ["Contact:", " ", _jsx("a", { href: contactEmailUrl, class: "text-[rgb(var(--color-primary-ink))] dark:text-[rgb(var(--color-primary-light))]", children: info.contact.email })] })), servers.length > 0 && (_jsxs("div", { class: "mt-6 rounded-[var(--radius)] border border-[rgb(var(--color-gray-200)/0.7)] dark:border-[rgb(var(--color-border-dark-subtle)/0.1)] overflow-x-auto", children: [_jsxs("div", { class: "px-4 py-2.5 text-xs font-semibold text-[rgb(var(--color-gray-600))] dark:text-[rgb(var(--color-gray-300))] border-b border-[rgb(var(--color-gray-200)/0.7)] dark:border-[rgb(var(--color-border-dark-subtle)/0.1)]", children: ["Base URL", servers.length > 1 ? "s" : ""] }), servers.map((s, i) => (_jsxs("div", { class: `flex items-baseline gap-3 px-4 py-2 ${i > 0 ? "border-t border-[rgb(var(--color-gray-100))] dark:border-[rgb(var(--color-gray-800))]" : ""}`, children: [_jsx("code", { class: "font-mono text-sm text-[rgb(var(--color-gray-800))] dark:text-[rgb(var(--color-gray-200))]", children: s.url }), s.description && (_jsx("span", { class: "text-xs text-[rgb(var(--color-gray-500))]", children: s.description }))] }, i)))] })), info.description && _jsx(Markdown, { content: info.description, class: "max-w-none mt-8" })] }));
13
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Security.d.ts","sourceRoot":"","sources":["../../../src/components/openapi/Security.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAK/D,UAAU,aAAa;IACrB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CACjC;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE,aAAa,uCA+CvD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,wCA8FlC"}
1
+ {"version":3,"file":"Security.d.ts","sourceRoot":"","sources":["../../../src/components/openapi/Security.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAM/D,UAAU,aAAa;IACrB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CACjC;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE,aAAa,uCAmDvD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,wCAoIlC"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
2
2
  import { useContext } from "preact/hooks";
3
3
  import { SpecContext } from "../../renderer/context.js";
4
+ import { safeUrl } from "../../utils/html.js";
4
5
  import { SectionLabel } from "../ui/SectionLabel.js";
5
6
  import { Markdown } from "../ui/Markdown.js";
6
7
  import { DeprecatedBadge } from "../ui/Badge.js";
@@ -16,7 +17,7 @@ export function SecurityCopy({ security }) {
16
17
  return null;
17
18
  return (_jsxs("div", { class: "mt-6", children: [_jsx(SectionLabel, { children: "Authorization" }), _jsx("div", { children: security.map((req, i) => (_jsx("div", { class: "params-list", children: Object.entries(req).map(([name, scopes]) => {
18
19
  const scheme = spec.securitySchemes[name];
19
- return (_jsxs("div", { class: "param-item", children: [_jsxs("div", { class: "param-header", children: [_jsx("code", { class: "param-name", children: name }), scheme && (_jsx("span", { class: "param-type", children: _jsxs("span", { class: "json-property-type", children: [scheme.type, scheme.scheme && ` (${scheme.scheme})`, scheme.in && ` in ${scheme.in}`] }) })), scheme?.deprecated && _jsx(DeprecatedBadge, {})] }), (scheme?.description || scopes.length > 0) && (_jsxs("div", { class: "param-description", children: [scheme?.description && _jsx(Markdown, { content: scheme.description, class: "prose-sm" }), scopes.length > 0 && (_jsxs("p", { class: "mt-1", children: ["Scopes: ", _jsx("code", { class: "text-xs font-medium", children: scopes.join(", ") })] }))] }))] }, name));
20
+ return (_jsxs("div", { class: "param-item", children: [_jsxs("div", { class: "param-header", children: [_jsx("code", { class: "param-name", children: name }), scheme && (_jsx("span", { class: "param-type", children: _jsxs("span", { class: "json-property-type", children: [scheme.type, scheme.scheme && ` (${scheme.scheme})`, scheme.in && ` in ${scheme.in}`] }) })), scheme?.deprecated && _jsx(DeprecatedBadge, {})] }), (scheme?.description || scopes.length > 0) && (_jsxs("div", { class: "param-description", children: [scheme?.description && (_jsx(Markdown, { content: scheme.description, class: "prose-sm" })), scopes.length > 0 && (_jsxs("p", { class: "mt-1", children: ["Scopes: ", _jsx("code", { class: "text-xs font-medium", children: scopes.join(", ") })] }))] }))] }, name));
20
21
  }) }, i))) })] }));
21
22
  }
22
23
  /**
@@ -27,5 +28,20 @@ export function SecurityDefinitions() {
27
28
  const schemes = Object.entries(spec.securitySchemes);
28
29
  if (!schemes.length)
29
30
  return null;
30
- return (_jsxs("div", { id: "authentication", class: "py-8 border-t border-[rgb(var(--color-gray-100))] dark:border-[rgb(var(--color-gray-800))]", "data-traverse-target": "authentication", children: [_jsx("h2", { class: "text-xl font-bold text-[rgb(var(--color-gray-900))] dark:text-[rgb(var(--color-gray-200))] mb-4", children: "Authentication" }), _jsx("div", { class: "params-list", children: schemes.map(([name, scheme]) => (_jsxs("div", { class: "param-item", children: [_jsxs("div", { class: "param-header", children: [_jsx("code", { class: "param-name", children: name }), _jsx("span", { class: "param-type", children: _jsx("span", { class: "json-property-type", children: scheme.type }) }), scheme.deprecated && _jsx(DeprecatedBadge, {})] }), _jsxs("div", { class: "param-description", children: [scheme.description && _jsx(Markdown, { content: scheme.description, class: "prose-sm" }), scheme.type === "apiKey" && (_jsxs("p", { class: "mt-1", children: ["API Key: ", _jsx("code", { class: "text-xs font-medium", children: scheme.name }), " in ", scheme.in] })), scheme.type === "http" && (_jsxs("p", { class: "mt-1", children: ["Scheme: ", scheme.scheme, scheme.bearerFormat && ` (${scheme.bearerFormat})`] })), scheme.type === "oauth2" && scheme.oauth2MetadataUrl && (_jsxs("p", { class: "mt-1", children: ["OAuth2 Metadata:", " ", _jsx("a", { href: scheme.oauth2MetadataUrl, class: "text-[rgb(var(--color-primary-ink))] dark:text-[rgb(var(--color-primary-light))]", children: scheme.oauth2MetadataUrl })] })), scheme.type === "oauth2" && scheme.flows && (_jsx("div", { class: "mt-2 space-y-4", children: Object.entries(scheme.flows).map(([flowType, flow]) => (_jsxs("div", { children: [_jsx("strong", { class: "text-[rgb(var(--color-gray-900))] dark:text-[rgb(var(--color-gray-200))]", children: flowType }), _jsxs("dl", { class: "mt-2 text-sm", children: [flow?.authorizationUrl && (_jsxs(_Fragment, { children: [_jsx("dt", { class: "text-[rgb(var(--color-gray-500))] mt-2", children: "Authorization URL" }), _jsx("dd", { children: _jsx("code", { class: "text-xs", children: flow.authorizationUrl }) })] })), flow?.deviceAuthorizationUrl && (_jsxs(_Fragment, { children: [_jsx("dt", { class: "text-[rgb(var(--color-gray-500))] mt-2", children: "Device Authorization URL" }), _jsx("dd", { children: _jsx("code", { class: "text-xs", children: flow.deviceAuthorizationUrl }) })] })), flow?.tokenUrl && (_jsxs(_Fragment, { children: [_jsx("dt", { class: "text-[rgb(var(--color-gray-500))] mt-2", children: "Token URL" }), _jsx("dd", { children: _jsx("code", { class: "text-xs", children: flow.tokenUrl }) })] }))] }), flow?.scopes && Object.keys(flow.scopes).length > 0 && (_jsxs("div", { class: "mt-3", children: [_jsx("span", { class: "text-sm text-[rgb(var(--color-gray-500))]", children: "Scopes" }), _jsx("ul", { class: "mt-1 space-y-1", children: Object.entries(flow.scopes).map(([s, desc]) => (_jsxs("li", { class: "text-sm", children: [_jsx("code", { class: "text-xs font-medium text-[rgb(var(--color-gray-800))] dark:text-[rgb(var(--color-gray-200))]", children: s }), _jsxs("span", { class: "text-[rgb(var(--color-gray-500))] ml-2", children: ["\u2014 ", desc] })] }, s))) })] }))] }, flowType))) })), scheme.type === "openIdConnect" && scheme.openIdConnectUrl && (_jsxs("p", { class: "mt-1", children: ["OpenID Connect:", " ", _jsx("a", { href: scheme.openIdConnectUrl, class: "text-[rgb(var(--color-primary-ink))] dark:text-[rgb(var(--color-primary-light))]", children: scheme.openIdConnectUrl })] }))] })] }, name))) })] }));
31
+ return (_jsxs("div", { id: "authentication", class: "py-8 border-t border-[rgb(var(--color-gray-100))] dark:border-[rgb(var(--color-gray-800))]", "data-traverse-target": "authentication", children: [_jsx("h2", { class: "text-xl font-bold text-[rgb(var(--color-gray-900))] dark:text-[rgb(var(--color-gray-200))] mb-4", children: "Authentication" }), _jsx("div", { class: "params-list", children: schemes.map(([name, scheme]) => {
32
+ const oauth2MetadataUrl = scheme.oauth2MetadataUrl
33
+ ? safeExternalSpecUrl(scheme.oauth2MetadataUrl)
34
+ : null;
35
+ const openIdConnectUrl = scheme.openIdConnectUrl
36
+ ? safeExternalSpecUrl(scheme.openIdConnectUrl)
37
+ : null;
38
+ return (_jsxs("div", { class: "param-item", children: [_jsxs("div", { class: "param-header", children: [_jsx("code", { class: "param-name", children: name }), _jsx("span", { class: "param-type", children: _jsx("span", { class: "json-property-type", children: scheme.type }) }), scheme.deprecated && _jsx(DeprecatedBadge, {})] }), _jsxs("div", { class: "param-description", children: [scheme.description && _jsx(Markdown, { content: scheme.description, class: "prose-sm" }), scheme.type === "apiKey" && (_jsxs("p", { class: "mt-1", children: ["API Key: ", _jsx("code", { class: "text-xs font-medium", children: scheme.name }), " in ", scheme.in] })), scheme.type === "http" && (_jsxs("p", { class: "mt-1", children: ["Scheme: ", scheme.scheme, scheme.bearerFormat && ` (${scheme.bearerFormat})`] })), scheme.type === "oauth2" && oauth2MetadataUrl && (_jsxs("p", { class: "mt-1", children: ["OAuth2 Metadata:", " ", _jsx("a", { href: oauth2MetadataUrl, class: "text-[rgb(var(--color-primary-ink))] dark:text-[rgb(var(--color-primary-light))]", children: scheme.oauth2MetadataUrl })] })), scheme.type === "oauth2" && scheme.flows && (_jsx("div", { class: "mt-2 space-y-4", children: Object.entries(scheme.flows).map(([flowType, flow]) => (_jsxs("div", { children: [_jsx("strong", { class: "text-[rgb(var(--color-gray-900))] dark:text-[rgb(var(--color-gray-200))]", children: flowType }), _jsxs("dl", { class: "mt-2 text-sm", children: [flow?.authorizationUrl && (_jsxs(_Fragment, { children: [_jsx("dt", { class: "text-[rgb(var(--color-gray-500))] mt-2", children: "Authorization URL" }), _jsx("dd", { children: _jsx("code", { class: "text-xs", children: flow.authorizationUrl }) })] })), flow?.deviceAuthorizationUrl && (_jsxs(_Fragment, { children: [_jsx("dt", { class: "text-[rgb(var(--color-gray-500))] mt-2", children: "Device Authorization URL" }), _jsx("dd", { children: _jsx("code", { class: "text-xs", children: flow.deviceAuthorizationUrl }) })] })), flow?.tokenUrl && (_jsxs(_Fragment, { children: [_jsx("dt", { class: "text-[rgb(var(--color-gray-500))] mt-2", children: "Token URL" }), _jsx("dd", { children: _jsx("code", { class: "text-xs", children: flow.tokenUrl }) })] }))] }), flow?.scopes && Object.keys(flow.scopes).length > 0 && (_jsxs("div", { class: "mt-3", children: [_jsx("span", { class: "text-sm text-[rgb(var(--color-gray-500))]", children: "Scopes" }), _jsx("ul", { class: "mt-1 space-y-1", children: Object.entries(flow.scopes).map(([s, desc]) => (_jsxs("li", { class: "text-sm", children: [_jsx("code", { class: "text-xs font-medium text-[rgb(var(--color-gray-800))] dark:text-[rgb(var(--color-gray-200))]", children: s }), _jsxs("span", { class: "text-[rgb(var(--color-gray-500))] ml-2", children: ["- ", desc] })] }, s))) })] }))] }, flowType))) })), scheme.type === "openIdConnect" && openIdConnectUrl && (_jsxs("p", { class: "mt-1", children: ["OpenID Connect:", " ", _jsx("a", { href: openIdConnectUrl, class: "text-[rgb(var(--color-primary-ink))] dark:text-[rgb(var(--color-primary-light))]", children: scheme.openIdConnectUrl })] }))] })] }, name));
39
+ }) })] }));
40
+ }
41
+ function safeExternalSpecUrl(value) {
42
+ return safeUrl(value, {
43
+ allowedProtocols: ["http:", "https:"],
44
+ allowRelative: false,
45
+ allowAnchor: false,
46
+ });
31
47
  }