@umami/shiso 1.12.0 → 1.13.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/dist/chunks/App.js +76 -49
- package/dist/chunks/abnfDiagram-VCTEODGH.js +109 -0
- package/dist/chunks/arc.js +130 -0
- package/dist/chunks/architecture-7GRP2DOG.js +4 -0
- package/dist/chunks/architectureDiagram-5GKGNRK7.js +7466 -0
- package/dist/chunks/array.js +8 -0
- package/dist/chunks/blockDiagram-I7D4REHJ.js +2546 -0
- package/dist/chunks/c4Diagram-7LVT6UL2.js +3654 -0
- package/dist/chunks/channel.js +9 -0
- package/dist/chunks/chunk-2Q5K7J3B.js +21 -0
- package/dist/chunks/chunk-4HAMMTFA.js +6106 -0
- package/dist/chunks/chunk-5VM5RSS4.js +21 -0
- package/dist/chunks/chunk-75Z2AOVW.js +2185 -0
- package/dist/chunks/chunk-DU6HZSFF.js +8729 -0
- package/dist/chunks/chunk-F27PBJKO.js +95 -0
- package/dist/chunks/chunk-FOHPRMQF.js +27162 -0
- package/dist/chunks/chunk-GMAD6QVW.js +2369 -0
- package/dist/chunks/chunk-GVQU2GXP.js +58 -0
- package/dist/chunks/chunk-IMKFNOWR.js +2593 -0
- package/dist/chunks/chunk-JWPE2WC7.js +12 -0
- package/dist/chunks/chunk-L3NEJ4N5.js +420 -0
- package/dist/chunks/chunk-OSK3NFVY.js +1029 -0
- package/dist/chunks/chunk-P2QGCYS3.js +95 -0
- package/dist/chunks/chunk-POPQ4Y6H.js +33 -0
- package/dist/chunks/chunk-PWAF6VOD.js +467 -0
- package/dist/chunks/chunk-SHT3W25Y.js +4813 -0
- package/dist/chunks/chunk-SVP7TREG.js +686 -0
- package/dist/chunks/chunk-TICWLB2K.js +3800 -0
- package/dist/chunks/chunk-XXDRQBXY.js +12 -0
- package/dist/chunks/chunk-Y2CYZVJY.js +15 -0
- package/dist/chunks/classDiagram-ZZMXUADV.js +33 -0
- package/dist/chunks/classDiagram-v2-VYDZK3BY.js +33 -0
- package/dist/chunks/cose-bilkent-JH36ORCC.js +4268 -0
- package/dist/chunks/cynefin-OW5HDTMX.js +4 -0
- package/dist/chunks/cynefinDiagram-5FMLGOSQ.js +482 -0
- package/dist/chunks/cytoscape.esm.js +28282 -0
- package/dist/chunks/dagre-GXQ25YYZ.js +583 -0
- package/dist/chunks/dagre.js +4235 -0
- package/dist/chunks/defaultLocale.js +246 -0
- package/dist/chunks/diagram-S7CK7UJ4.js +402 -0
- package/dist/chunks/diagram-UQ7AKVKN.js +291 -0
- package/dist/chunks/diagram-VSXAHHWV.js +553 -0
- package/dist/chunks/diagram-VX7I27RA.js +798 -0
- package/dist/chunks/diagram-Z3DM3KII.js +186 -0
- package/dist/chunks/dist.js +92 -0
- package/dist/chunks/docs.js +987 -91
- package/dist/chunks/ebnfDiagram-PWID7BFC.js +123 -0
- package/dist/chunks/erDiagram-RLTQ6QDP.js +2295 -0
- package/dist/chunks/eventmodeling-NTZA5JFV.js +4 -0
- package/dist/chunks/flowDiagram-HODETNUW.js +17 -0
- package/dist/chunks/ganttDiagram-EL5Y4UJY.js +3928 -0
- package/dist/chunks/gitGraph-4MIJSDKK.js +4 -0
- package/dist/chunks/gitGraphDiagram-WWUBYQGX.js +1153 -0
- package/dist/chunks/graphlib.js +4194 -0
- package/dist/chunks/info-A6RAGUB7.js +4 -0
- package/dist/chunks/infoDiagram-27XIBGKW.js +25 -0
- package/dist/chunks/init.js +14 -0
- package/dist/chunks/ishikawaDiagram-5VMMS53U.js +971 -0
- package/dist/chunks/journeyDiagram-3NMN7TZE.js +1226 -0
- package/dist/chunks/kanban-definition-UXKFOSKX.js +1241 -0
- package/dist/chunks/katex.js +26463 -0
- package/dist/chunks/line.js +48 -0
- package/dist/chunks/linear.js +391 -0
- package/dist/chunks/mermaid-parser.core.js +754 -0
- package/dist/chunks/mermaid.core.js +4898 -0
- package/dist/chunks/mindmap-definition-YA3MSWOX.js +1309 -0
- package/dist/chunks/ordinal.js +84 -0
- package/dist/chunks/packet-AYTQ26CC.js +4 -0
- package/dist/chunks/path.js +106 -0
- package/dist/chunks/pegDiagram-XKGWAZYB.js +115 -0
- package/dist/chunks/pie-WAS4IAKB.js +4 -0
- package/dist/chunks/pieDiagram-E7YTZNPT.js +297 -0
- package/dist/chunks/quadrantDiagram-AXDQQJYC.js +2234 -0
- package/dist/chunks/radar-RG4KPBEZ.js +4 -0
- package/dist/chunks/railroad-74A4TZTK.js +4 -0
- package/dist/chunks/railroad-abnf-HS5TGJTU.js +4 -0
- package/dist/chunks/railroad-ebnf-LZEXJU2U.js +4 -0
- package/dist/chunks/railroad-peg-WCYAUIDC.js +4 -0
- package/dist/chunks/railroadDiagram-O6MQD6OU.js +89 -0
- package/dist/chunks/requirementDiagram-BXWQKSXE.js +2464 -0
- package/dist/chunks/rough.esm.js +1396 -0
- package/dist/chunks/sankeyDiagram-P5KCCOFB.js +1308 -0
- package/dist/chunks/sequenceDiagram-WJ2MYXX4.js +5445 -0
- package/dist/chunks/sizeCapture-INFHLROL.js +56 -0
- package/dist/chunks/src.js +2653 -0
- package/dist/chunks/stateDiagram-D77RDMKH.js +368 -0
- package/dist/chunks/stateDiagram-v2-MP3YSRHH.js +33 -0
- package/dist/chunks/swimlanes-42K2YHIH.js +6925 -0
- package/dist/chunks/swimlanesDiagram-VR7AAH4N.js +32 -0
- package/dist/chunks/timeline-definition-24CTP7MA.js +1507 -0
- package/dist/chunks/treeView-Q6P3EWNA.js +4 -0
- package/dist/chunks/treemap-WGGIJYW6.js +4 -0
- package/dist/chunks/vennDiagram-4TSXK5OY.js +2803 -0
- package/dist/chunks/wardley-WFR3VGLG.js +4 -0
- package/dist/chunks/wardleyDiagram-VM6X3IG4.js +877 -0
- package/dist/chunks/xychartDiagram-S5SC5T6Z.js +2698 -0
- package/dist/components.js +2 -2
- package/dist/entry-client.js +1 -1
- package/dist/entry-server.js +1 -1
- package/docs.schema.json +1 -1
- package/package.json +2 -1
- package/scripts/expand-navigation-globs.mjs +77 -8
- package/src/components/CodeBlock.tsx +36 -0
- package/src/components/Docs.tsx +44 -21
- package/src/components/OpenApiOperation.tsx +28 -20
- package/src/components/SideNav.tsx +8 -3
- package/src/components/docs/Changelog.tsx +163 -0
- package/src/components/docs/Mermaid.tsx +164 -0
- package/src/components/docs/Panel.tsx +23 -0
- package/src/components/docs/ParamField.tsx +17 -5
- package/src/components/docs/PropertiesTable.tsx +2 -2
- package/src/components/docs/ResponseField.tsx +17 -5
- package/src/components/docs/Tiles.tsx +59 -0
- package/src/components/docs/Tree.tsx +359 -0
- package/src/components/docs/Update.tsx +72 -0
- package/src/components/docs/index.ts +7 -0
- package/src/components/docs/panel-context.tsx +25 -0
- package/src/components/docs/styles.ts +49 -0
- package/src/components/icons/index.ts +4 -0
- package/src/lib/rehype-shiki.ts +18 -1
- package/types/components.d.ts +79 -0
package/dist/components.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $t as Accordion, Bt as Card, C as Tooltip, Ct as useSetPanelContent, Et as Frame, Gt as Info, Ht as Callout, Jt as Warning, Kt as Note, Mt as Column, Nt as Columns, Ot as Expandable, Pt as CodeGroup, Rt as Changelog, S as TreeFolder, Sn as mermaidSource, St as usePanelContent, Tt as Icon, Ut as Check, Vt as CardGroup, Wt as Danger, Xt as Button, Yt as WarningBanner, Zt as Badge, _ as FileTree, _t as PropertiesTable, b as Tree, bt as Panel, dt as Tiles, en as AccordionGroup, ft as Tab, gt as ResponseField, ht as Steps, mt as Step, n as ZoomableImage, pt as Tabs, qt as Tip, ut as Tile, v as FileTreeFile, vt as Param, wt as Link, x as TreeFile, xn as Mermaid, xt as PanelProvider, y as FileTreeFolder, yt as ParamField, zt as Update } from "./chunks/docs.js";
|
|
2
2
|
|
|
3
|
-
export { Accordion, AccordionGroup, Badge, Button, Callout, Card, CardGroup, Check, CodeGroup, Column, Columns, Danger, Expandable, Frame, Icon, Info, Link, Note, Param, ParamField, PropertiesTable, ResponseField, Step, Steps, Tab, Tabs, Tip, Tooltip, Warning, WarningBanner, ZoomableImage };
|
|
3
|
+
export { Accordion, AccordionGroup, Badge, Button, Callout, Card, CardGroup, Changelog, Check, CodeGroup, Column, Columns, Danger, Expandable, FileTree, FileTreeFile, FileTreeFolder, Frame, Icon, Info, Link, Mermaid, Note, Panel, PanelProvider, Param, ParamField, PropertiesTable, ResponseField, Step, Steps, Tab, Tabs, Tile, Tiles, Tip, Tooltip, Tree, TreeFile, TreeFolder, Update, Warning, WarningBanner, ZoomableImage, mermaidSource, usePanelContent, useSetPanelContent };
|
package/dist/entry-client.js
CHANGED
package/dist/entry-server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Ar as createPath, Er as Router, Mr as ABSOLUTE_URL_REGEX, jr as parsePath } from "./chunks/docs.js";
|
|
2
2
|
import { a as docsSite, c as getSeo, d as standalonePages, f as getDocModule, g as toAbsoluteUrl, h as BASE_URL, i as docsHomeUrl, l as siteConfig, m as getScopeForPage, n as buildHead, o as getLocaleByPathname, p as getLastModified, r as renderHeadToString, s as getRedirects, t as App, u as siteName } from "./chunks/App.js";
|
|
3
3
|
import * as React$1 from "react";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
package/docs.schema.json
CHANGED
|
@@ -807,7 +807,7 @@
|
|
|
807
807
|
"properties": {
|
|
808
808
|
"glob": {
|
|
809
809
|
"allOf": [{ "$ref": "#/definitions/nonEmptyStringValue" }],
|
|
810
|
-
"description": "Glob relative to contentDir. Matching Markdown and MDX files are expanded into page entries."
|
|
810
|
+
"description": "Glob relative to contentDir. Matching Markdown and MDX files are expanded into page entries; explicit page references take precedence."
|
|
811
811
|
},
|
|
812
812
|
"exclude": {
|
|
813
813
|
"anyOf": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umami/shiso",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Open-source documentation framework for Markdown and MDX sites.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
"github-slugger": "^2.0.0",
|
|
84
84
|
"jiti": "^2.4.0",
|
|
85
85
|
"lucide-react": "^1.28.0",
|
|
86
|
+
"mermaid": "^11.17.2",
|
|
86
87
|
"react-router": "^8.3.0",
|
|
87
88
|
"rehype-autolink-headings": "^7.1.0",
|
|
88
89
|
"rehype-slug": "^6.0.0",
|
|
@@ -111,12 +111,47 @@ function pageCandidate(contentRoot, filePath) {
|
|
|
111
111
|
return { filePath, relativeFile, fileSlug };
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
function normalizePageReference(value) {
|
|
115
|
+
return String(value || '')
|
|
116
|
+
.trim()
|
|
117
|
+
.replace(/\\/g, '/')
|
|
118
|
+
.replace(/^\/+/, '')
|
|
119
|
+
.replace(/^docs\//, '')
|
|
120
|
+
.replace(/\.mdx?$/i, '')
|
|
121
|
+
.replace(/\/+$/, '');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Collects manual page references without crossing a version/language scope boundary. */
|
|
125
|
+
function collectExplicitPages(value, pages = new Set()) {
|
|
126
|
+
if (Array.isArray(value)) {
|
|
127
|
+
for (const item of value) {
|
|
128
|
+
if (typeof item === 'string') {
|
|
129
|
+
pages.add(normalizePageReference(item));
|
|
130
|
+
} else {
|
|
131
|
+
collectExplicitPages(item, pages);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return pages;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!value || typeof value !== 'object' || isGlobItem(value)) return pages;
|
|
138
|
+
|
|
139
|
+
if (typeof value.page === 'string') pages.add(normalizePageReference(value.page));
|
|
140
|
+
if (typeof value.root === 'string') pages.add(normalizePageReference(value.root));
|
|
141
|
+
|
|
142
|
+
for (const key of ['pages', 'groups', 'tabs', 'dropdowns']) {
|
|
143
|
+
if (Array.isArray(value[key])) collectExplicitPages(value[key], pages);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return pages;
|
|
147
|
+
}
|
|
148
|
+
|
|
114
149
|
function matches(candidate, pattern) {
|
|
115
150
|
const target = /\.mdx?$/i.test(pattern) ? candidate.relativeFile : candidate.fileSlug;
|
|
116
151
|
return globRegex(pattern).test(target);
|
|
117
152
|
}
|
|
118
153
|
|
|
119
|
-
async function expandGlob(item, candidates, projectRoot) {
|
|
154
|
+
async function expandGlob(item, candidates, projectRoot, explicitPages) {
|
|
120
155
|
assertGlobItem(item);
|
|
121
156
|
const pattern = globPattern(item.glob);
|
|
122
157
|
const exclusions = (item.exclude || []).map(globPattern);
|
|
@@ -129,8 +164,12 @@ async function expandGlob(item, candidates, projectRoot) {
|
|
|
129
164
|
throw new Error(`Navigation glob "${pattern}" matched no Markdown or MDX files.`);
|
|
130
165
|
}
|
|
131
166
|
|
|
167
|
+
// Manual entries own their page's placement and presentation, regardless of
|
|
168
|
+
// whether they appear before or after the glob that would otherwise include it.
|
|
169
|
+
const discovered = matched.filter(candidate => !explicitPages.has(candidate.fileSlug));
|
|
170
|
+
|
|
132
171
|
const entries = await Promise.all(
|
|
133
|
-
|
|
172
|
+
discovered.map(async candidate => {
|
|
134
173
|
const source = await fs.readFile(candidate.filePath, 'utf8');
|
|
135
174
|
const sourcePath = path.relative(projectRoot, candidate.filePath).replace(/\\/g, '/');
|
|
136
175
|
const frontmatter = navigationFrontmatter(source, sourcePath);
|
|
@@ -181,8 +220,10 @@ export async function expandNavigationGlobs(config, { root, contentDir }) {
|
|
|
181
220
|
);
|
|
182
221
|
}
|
|
183
222
|
|
|
184
|
-
async function expandObject(value) {
|
|
185
|
-
if (Array.isArray(value))
|
|
223
|
+
async function expandObject(value, explicitPages) {
|
|
224
|
+
if (Array.isArray(value)) {
|
|
225
|
+
return Promise.all(value.map(item => expandObject(item, explicitPages)));
|
|
226
|
+
}
|
|
186
227
|
if (!value || typeof value !== 'object') return value;
|
|
187
228
|
|
|
188
229
|
const entries = await Promise.all(
|
|
@@ -191,18 +232,46 @@ export async function expandNavigationGlobs(config, { root, contentDir }) {
|
|
|
191
232
|
const expanded = [];
|
|
192
233
|
for (const item of child) {
|
|
193
234
|
if (isGlobItem(item)) {
|
|
194
|
-
expanded.push(...(await expandGlob(item, candidates, projectRoot)));
|
|
235
|
+
expanded.push(...(await expandGlob(item, candidates, projectRoot, explicitPages)));
|
|
195
236
|
} else {
|
|
196
|
-
expanded.push(await expandObject(item));
|
|
237
|
+
expanded.push(await expandObject(item, explicitPages));
|
|
197
238
|
}
|
|
198
239
|
}
|
|
199
240
|
return [key, expanded];
|
|
200
241
|
}
|
|
201
|
-
return [key, await expandObject(child)];
|
|
242
|
+
return [key, await expandObject(child, explicitPages)];
|
|
202
243
|
}),
|
|
203
244
|
);
|
|
204
245
|
return Object.fromEntries(entries);
|
|
205
246
|
}
|
|
206
247
|
|
|
207
|
-
|
|
248
|
+
async function expandScope(scope) {
|
|
249
|
+
return expandObject(scope, collectExplicitPages(scope));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async function expandNavigation(navigation) {
|
|
253
|
+
if (Array.isArray(navigation.languages)) {
|
|
254
|
+
const languages = await Promise.all(
|
|
255
|
+
navigation.languages.map(async language => {
|
|
256
|
+
if (!Array.isArray(language.versions)) return expandScope(language);
|
|
257
|
+
return {
|
|
258
|
+
...language,
|
|
259
|
+
versions: await Promise.all(language.versions.map(expandScope)),
|
|
260
|
+
};
|
|
261
|
+
}),
|
|
262
|
+
);
|
|
263
|
+
return { ...navigation, languages };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (Array.isArray(navigation.versions)) {
|
|
267
|
+
return {
|
|
268
|
+
...navigation,
|
|
269
|
+
versions: await Promise.all(navigation.versions.map(expandScope)),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return expandScope(navigation);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return { ...config, navigation: await expandNavigation(config.navigation) };
|
|
208
277
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
1
2
|
import { type ComponentProps, type CSSProperties, useRef, useState } from 'react';
|
|
2
3
|
import { CheckIcon, Copy } from '@/components/icons';
|
|
3
4
|
import { Button } from '@/components/ui/button';
|
|
4
5
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
5
6
|
import { cn } from '@/lib/utils';
|
|
7
|
+
import { Mermaid, type MermaidPlacement } from './docs/Mermaid';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Renders a fenced code block. The `data-*` props are produced at build time by
|
|
@@ -15,6 +17,25 @@ export interface CodeBlockProps extends ComponentProps<'pre'> {
|
|
|
15
17
|
'data-line-start'?: string;
|
|
16
18
|
'data-line-count'?: string;
|
|
17
19
|
'data-diff-markers'?: string;
|
|
20
|
+
'data-placement'?: MermaidPlacement;
|
|
21
|
+
'data-actions'?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function reactChildrenToText(node: ReactNode): string {
|
|
25
|
+
if (typeof node === 'string' || typeof node === 'number') {
|
|
26
|
+
return String(node);
|
|
27
|
+
}
|
|
28
|
+
if (Array.isArray(node)) {
|
|
29
|
+
return node.map(reactChildrenToText).join('');
|
|
30
|
+
}
|
|
31
|
+
if (node && typeof node === 'object' && 'props' in (node as object)) {
|
|
32
|
+
const props = (node as { props?: { children?: ReactNode; value?: unknown } }).props;
|
|
33
|
+
if (typeof props?.value === 'string') {
|
|
34
|
+
return props.value;
|
|
35
|
+
}
|
|
36
|
+
return reactChildrenToText(props?.children);
|
|
37
|
+
}
|
|
38
|
+
return '';
|
|
18
39
|
}
|
|
19
40
|
|
|
20
41
|
/**
|
|
@@ -44,11 +65,26 @@ export function CodeBlock({ children, className, style, ...rest }: CodeBlockProp
|
|
|
44
65
|
'data-language': language,
|
|
45
66
|
'data-line-start': lineStart,
|
|
46
67
|
'data-line-count': lineCount,
|
|
68
|
+
'data-placement': placement,
|
|
69
|
+
'data-actions': actions,
|
|
47
70
|
...preProps
|
|
48
71
|
} = rest;
|
|
49
72
|
const textInput = useRef<HTMLPreElement>(null);
|
|
50
73
|
const [copied, setCopied] = useState(false);
|
|
51
74
|
|
|
75
|
+
// ```mermaid fences render as diagrams; the raw definition stays in the
|
|
76
|
+
// markup for search indexing and no-JS fallbacks.
|
|
77
|
+
if (language === 'mermaid') {
|
|
78
|
+
return (
|
|
79
|
+
<Mermaid
|
|
80
|
+
chart={reactChildrenToText(children).replace(/\n$/, '')}
|
|
81
|
+
title={title}
|
|
82
|
+
placement={placement}
|
|
83
|
+
actions={actions === undefined ? undefined : actions !== 'false'}
|
|
84
|
+
/>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
52
88
|
const start = Number(lineStart) || 1;
|
|
53
89
|
const lastLine = start + Math.max(Number(lineCount) || 1, 1) - 1;
|
|
54
90
|
const gutter = `${String(lastLine).length}ch`;
|
package/src/components/Docs.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
import { useLocation, useNavigate } from 'react-router';
|
|
3
3
|
import { DocContent } from '@/components/DocContent';
|
|
4
|
+
import { PanelProvider, usePanelContent } from '@/components/docs/panel-context';
|
|
4
5
|
import { Footer } from '@/components/Footer';
|
|
5
6
|
import { Menu } from '@/components/icons';
|
|
6
7
|
import { LanguageSwitcher } from '@/components/LanguageSwitcher';
|
|
@@ -13,7 +14,7 @@ import { VersionSwitcher } from '@/components/VersionSwitcher';
|
|
|
13
14
|
import { renderInlineMarkdown } from '@/lib/inline-markdown';
|
|
14
15
|
import { getOperation, operationSections } from '@/lib/openapi';
|
|
15
16
|
import { docsHomeUrl, getScopeByPathname } from '@/lib/site-config';
|
|
16
|
-
import type { DocModule, NormalizedDocsPage, SiteModel } from '@/lib/types';
|
|
17
|
+
import type { DocModule, NormalizedDocsPage, SiteModel, TocEntry } from '@/lib/types';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* 404 view driven by the `errors.404` config key. The standard defaults to
|
|
@@ -46,11 +47,46 @@ export interface DocsProps {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
export function Docs({ page, doc, site }: DocsProps) {
|
|
50
|
+
if (!page || !doc) {
|
|
51
|
+
return (
|
|
52
|
+
<div className="flex min-h-full flex-col">
|
|
53
|
+
<div className="grow">
|
|
54
|
+
<NotFound site={site} />
|
|
55
|
+
</div>
|
|
56
|
+
<Footer footer={site.footer} />
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// API reference pages append their generated section anchors to the TOC.
|
|
62
|
+
const operation = getOperation(doc.frontmatter?.openapi);
|
|
63
|
+
const toc = operation ? [...(doc.toc || []), ...operationSections(operation)] : doc.toc;
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<PanelProvider>
|
|
67
|
+
<DocsBody page={page} doc={doc} site={site} toc={toc} />
|
|
68
|
+
</PanelProvider>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function DocsBody({
|
|
73
|
+
page,
|
|
74
|
+
doc,
|
|
75
|
+
site,
|
|
76
|
+
toc,
|
|
77
|
+
}: {
|
|
78
|
+
page: NormalizedDocsPage;
|
|
79
|
+
doc: DocModule;
|
|
80
|
+
site: SiteModel;
|
|
81
|
+
toc: TocEntry[] | undefined;
|
|
82
|
+
}) {
|
|
49
83
|
const { pathname } = useLocation();
|
|
50
84
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
51
85
|
// Navigation follows the scope (version/language) that owns the current page.
|
|
52
86
|
const scopeDocs = getScopeByPathname(pathname).docs;
|
|
53
87
|
const { tabs, navigation } = scopeDocs;
|
|
88
|
+
// A <Panel> in the page replaces the table of contents in the right rail.
|
|
89
|
+
const panel = usePanelContent();
|
|
54
90
|
|
|
55
91
|
// Close the mobile menu and start each newly loaded page at the top. Hash
|
|
56
92
|
// links keep their native section-scrolling behavior.
|
|
@@ -62,21 +98,6 @@ export function Docs({ page, doc, site }: DocsProps) {
|
|
|
62
98
|
}
|
|
63
99
|
}, [pathname]);
|
|
64
100
|
|
|
65
|
-
if (!page || !doc) {
|
|
66
|
-
return (
|
|
67
|
-
<div className="flex min-h-full flex-col">
|
|
68
|
-
<div className="grow">
|
|
69
|
-
<NotFound site={site} />
|
|
70
|
-
</div>
|
|
71
|
-
<Footer footer={site.footer} />
|
|
72
|
-
</div>
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// API reference pages append their generated section anchors to the TOC.
|
|
77
|
-
const operation = getOperation(doc.frontmatter?.openapi);
|
|
78
|
-
const toc = operation ? [...(doc.toc || []), ...operationSections(operation)] : doc.toc;
|
|
79
|
-
|
|
80
101
|
return (
|
|
81
102
|
<div className="flex min-h-full flex-col gap-6 lg:gap-0">
|
|
82
103
|
<Sheet open={menuOpen} onOpenChange={setMenuOpen}>
|
|
@@ -128,11 +149,13 @@ export function Docs({ page, doc, site }: DocsProps) {
|
|
|
128
149
|
<div className="flex grow items-start gap-12">
|
|
129
150
|
<DocContent page={page} doc={doc} site={site} />
|
|
130
151
|
<div className="hidden min-w-0 max-w-60 basis-60 self-start lg:sticky lg:top-[calc(var(--header-height)+1.5rem)] lg:block lg:shrink-0">
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
152
|
+
{panel ?? (
|
|
153
|
+
<PageLinks
|
|
154
|
+
items={toc}
|
|
155
|
+
title={site.labels.tableOfContents}
|
|
156
|
+
navigationLabel={site.labels.tableOfContentsNavigation}
|
|
157
|
+
/>
|
|
158
|
+
)}
|
|
136
159
|
</div>
|
|
137
160
|
</div>
|
|
138
161
|
<Footer footer={site.footer} className="lg:mr-72" />
|
|
@@ -4,6 +4,7 @@ import { CodeGroup } from '@/components/docs/CodeGroup';
|
|
|
4
4
|
import { Expandable } from '@/components/docs/Expandable';
|
|
5
5
|
import { ParamField } from '@/components/docs/ParamField';
|
|
6
6
|
import { ResponseField } from '@/components/docs/ResponseField';
|
|
7
|
+
import { styles } from '@/components/docs/styles';
|
|
7
8
|
import { operationSections, statusColor } from '@/lib/openapi';
|
|
8
9
|
import type { NormalizedOperation, SchemaNode } from '@/lib/types';
|
|
9
10
|
|
|
@@ -38,6 +39,8 @@ function FieldChildren({ node }: { node: SchemaNode }) {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
function SchemaField({ node }: { node: SchemaNode }) {
|
|
42
|
+
const hasDetails = Boolean(node.description || node.enum?.length || node.children?.length);
|
|
43
|
+
|
|
41
44
|
return (
|
|
42
45
|
<ResponseField
|
|
43
46
|
name={node.name || node.type}
|
|
@@ -46,7 +49,7 @@ function SchemaField({ node }: { node: SchemaNode }) {
|
|
|
46
49
|
deprecated={node.deprecated}
|
|
47
50
|
default={node.default}
|
|
48
51
|
>
|
|
49
|
-
<FieldChildren node={node} />
|
|
52
|
+
{hasDetails ? <FieldChildren node={node} /> : null}
|
|
50
53
|
</ResponseField>
|
|
51
54
|
);
|
|
52
55
|
}
|
|
@@ -114,25 +117,26 @@ export function OpenApiOperation({ operation }: OpenApiOperationProps) {
|
|
|
114
117
|
{showParameters && (
|
|
115
118
|
<section>
|
|
116
119
|
<h2 id={sections.get('Parameters')}>Parameters</h2>
|
|
117
|
-
{
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
</ParamField>
|
|
122
|
-
)}
|
|
123
|
-
{PARAM_LOCATIONS.map(location =>
|
|
124
|
-
operation.parameters[location].map(parameter => (
|
|
125
|
-
<ParamField
|
|
126
|
-
key={`${location}-${parameter.name}`}
|
|
127
|
-
{...{ [location]: parameter.name }}
|
|
128
|
-
type={parameter.type}
|
|
129
|
-
required={parameter.required}
|
|
130
|
-
>
|
|
131
|
-
<FieldChildren node={{ ...parameter, name: undefined, description: undefined }} />
|
|
132
|
-
{parameter.description}
|
|
120
|
+
<div className={styles.fieldGroup}>
|
|
121
|
+
{operation.security.length > 0 && (
|
|
122
|
+
<ParamField header="Authorization" type="string" required>
|
|
123
|
+
Authentication credentials, e.g. <code>Bearer <token></code>.
|
|
133
124
|
</ParamField>
|
|
134
|
-
)
|
|
135
|
-
|
|
125
|
+
)}
|
|
126
|
+
{PARAM_LOCATIONS.map(location =>
|
|
127
|
+
operation.parameters[location].map(parameter => (
|
|
128
|
+
<ParamField
|
|
129
|
+
key={`${location}-${parameter.name}`}
|
|
130
|
+
{...{ [location]: parameter.name }}
|
|
131
|
+
type={parameter.type}
|
|
132
|
+
required={parameter.required}
|
|
133
|
+
>
|
|
134
|
+
<FieldChildren node={{ ...parameter, name: undefined, description: undefined }} />
|
|
135
|
+
{parameter.description}
|
|
136
|
+
</ParamField>
|
|
137
|
+
)),
|
|
138
|
+
)}
|
|
139
|
+
</div>
|
|
136
140
|
</section>
|
|
137
141
|
)}
|
|
138
142
|
{operation.requestBody && (
|
|
@@ -162,7 +166,11 @@ export function OpenApiOperation({ operation }: OpenApiOperationProps) {
|
|
|
162
166
|
<span className="text-muted-foreground text-sm">{response.description}</span>
|
|
163
167
|
)}
|
|
164
168
|
</div>
|
|
165
|
-
{response.schema &&
|
|
169
|
+
{response.schema && (
|
|
170
|
+
<div className={styles.fieldGroup}>
|
|
171
|
+
<SchemaFields node={response.schema} />
|
|
172
|
+
</div>
|
|
173
|
+
)}
|
|
166
174
|
{response.example && (
|
|
167
175
|
<HighlightedCode
|
|
168
176
|
language="json"
|
|
@@ -275,12 +275,17 @@ function NavNodes({
|
|
|
275
275
|
)}
|
|
276
276
|
>
|
|
277
277
|
{resolveIcon(icon)}
|
|
278
|
-
{label}
|
|
279
278
|
{method ? (
|
|
280
|
-
<MethodBadge
|
|
279
|
+
<MethodBadge
|
|
280
|
+
color={methodColor(method)}
|
|
281
|
+
size="xs"
|
|
282
|
+
className="w-10 shrink-0 justify-center whitespace-nowrap font-mono"
|
|
283
|
+
>
|
|
281
284
|
{method === 'DELETE' ? 'DEL' : method}
|
|
282
285
|
</MethodBadge>
|
|
283
|
-
) :
|
|
286
|
+
) : null}
|
|
287
|
+
{label}
|
|
288
|
+
{!method && tag ? (
|
|
284
289
|
<Badge
|
|
285
290
|
variant="secondary"
|
|
286
291
|
className="ml-auto h-auto rounded-sm px-[0.35rem] py-[0.05rem] text-[0.7rem] text-muted-foreground uppercase"
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { Children, Fragment, isValidElement, type ReactElement, useMemo } from 'react';
|
|
2
|
+
import { useSearchParams } from 'react-router';
|
|
3
|
+
import { Badge } from './Badge';
|
|
4
|
+
import { styles } from './styles';
|
|
5
|
+
import { Update } from './Update';
|
|
6
|
+
|
|
7
|
+
export interface ChangelogProps {
|
|
8
|
+
children?: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface UpdateChildInfo {
|
|
12
|
+
key: string;
|
|
13
|
+
tags: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Flattens fragments so <Update> entries are found however MDX nests them. */
|
|
17
|
+
function flatElements(children: React.ReactNode): ReactElement<{ tags?: unknown }>[] {
|
|
18
|
+
const flat: ReactElement<{ tags?: unknown }>[] = [];
|
|
19
|
+
|
|
20
|
+
Children.forEach(children, child => {
|
|
21
|
+
if (!isValidElement<{ tags?: unknown }>(child)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (child.type === Fragment) {
|
|
25
|
+
flat.push(...flatElements(child.props.children));
|
|
26
|
+
} else {
|
|
27
|
+
flat.push(child);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return flat;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Reads `tags` off <Update> children without rendering them. */
|
|
35
|
+
function updateInfo(children: React.ReactNode): Map<string, UpdateChildInfo> {
|
|
36
|
+
const info = new Map<string, UpdateChildInfo>();
|
|
37
|
+
|
|
38
|
+
flatElements(children).forEach((child, index) => {
|
|
39
|
+
if (child.type !== Update) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const key = String(child.key ?? `update-${index}`);
|
|
44
|
+
const raw = child.props.tags;
|
|
45
|
+
const tags = Array.isArray(raw)
|
|
46
|
+
? [...new Set(raw.filter((tag): tag is string => typeof tag === 'string' && tag.trim()))].map(
|
|
47
|
+
tag => tag.trim(),
|
|
48
|
+
)
|
|
49
|
+
: [];
|
|
50
|
+
info.set(key, { key, tags });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return info;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Groups <Update> entries with Mintlify-style tag filtering.
|
|
58
|
+
* Selected tags sync to `?tags=a,b` so filtered views are shareable.
|
|
59
|
+
* Pages without any tagged updates render as a plain list.
|
|
60
|
+
*/
|
|
61
|
+
export function Changelog({ children }: ChangelogProps) {
|
|
62
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
63
|
+
|
|
64
|
+
const allTags = useMemo(() => {
|
|
65
|
+
const seen = new Map<string, string>();
|
|
66
|
+
for (const { tags } of updateInfo(children).values()) {
|
|
67
|
+
for (const tag of tags) {
|
|
68
|
+
const key = tag.toLowerCase();
|
|
69
|
+
if (!seen.has(key)) {
|
|
70
|
+
seen.set(key, tag);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return [...seen.values()];
|
|
75
|
+
}, [children]);
|
|
76
|
+
|
|
77
|
+
const selected = useMemo(() => {
|
|
78
|
+
const param = searchParams.get('tags');
|
|
79
|
+
if (!param) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
const wanted = new Set(
|
|
83
|
+
param
|
|
84
|
+
.split(',')
|
|
85
|
+
.map(tag => tag.trim().toLowerCase())
|
|
86
|
+
.filter(Boolean),
|
|
87
|
+
);
|
|
88
|
+
return allTags.filter(tag => wanted.has(tag.toLowerCase()));
|
|
89
|
+
}, [searchParams, allTags]);
|
|
90
|
+
|
|
91
|
+
const infoByKey = useMemo(() => updateInfo(children), [children]);
|
|
92
|
+
|
|
93
|
+
if (allTags.length === 0) {
|
|
94
|
+
return <div className={styles.changelog}>{children}</div>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const toggle = (tag: string) => {
|
|
98
|
+
const lower = tag.toLowerCase();
|
|
99
|
+
const next = selected.some(item => item.toLowerCase() === lower)
|
|
100
|
+
? selected.filter(item => item.toLowerCase() !== lower)
|
|
101
|
+
: [...selected, tag];
|
|
102
|
+
|
|
103
|
+
setSearchParams(next.length ? { tags: next.join(',') } : {}, { preventScrollReset: true });
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const clear = () => {
|
|
107
|
+
setSearchParams({}, { preventScrollReset: true });
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const selectedSet = new Set(selected.map(tag => tag.toLowerCase()));
|
|
111
|
+
|
|
112
|
+
const filtered = flatElements(children).map((child, index) => {
|
|
113
|
+
if (child.type !== Update) {
|
|
114
|
+
return child;
|
|
115
|
+
}
|
|
116
|
+
const key = String(child.key ?? `update-${index}`);
|
|
117
|
+
const tags = infoByKey.get(key)?.tags ?? [];
|
|
118
|
+
if (selectedSet.size === 0) {
|
|
119
|
+
return child;
|
|
120
|
+
}
|
|
121
|
+
const lower = tags.map(tag => tag.toLowerCase());
|
|
122
|
+
const matches = [...selectedSet].every(tag => lower.includes(tag));
|
|
123
|
+
return matches ? child : null;
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const visibleUpdates = filtered.filter(
|
|
127
|
+
child => child && (child as React.ReactElement).type === Update,
|
|
128
|
+
).length;
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<div className={styles.changelog}>
|
|
132
|
+
{/* biome-ignore lint/a11y/useSemanticElements: toggle-button group labelled for the filter */}
|
|
133
|
+
<div className={styles.changelogFilters} role="group" aria-label="Filter updates by tag">
|
|
134
|
+
{allTags.map(tag => {
|
|
135
|
+
const active = selectedSet.has(tag.toLowerCase());
|
|
136
|
+
return (
|
|
137
|
+
<button
|
|
138
|
+
key={tag}
|
|
139
|
+
type="button"
|
|
140
|
+
onClick={() => toggle(tag)}
|
|
141
|
+
aria-pressed={active}
|
|
142
|
+
className={styles.changelogFilterButton}
|
|
143
|
+
data-active={active ? '' : undefined}
|
|
144
|
+
>
|
|
145
|
+
<Badge size="sm" color={active ? 'primary' : undefined}>
|
|
146
|
+
{tag}
|
|
147
|
+
</Badge>
|
|
148
|
+
</button>
|
|
149
|
+
);
|
|
150
|
+
})}
|
|
151
|
+
{selected.length > 0 ? (
|
|
152
|
+
<button type="button" onClick={clear} className={styles.changelogClear}>
|
|
153
|
+
Clear
|
|
154
|
+
</button>
|
|
155
|
+
) : null}
|
|
156
|
+
</div>
|
|
157
|
+
{visibleUpdates === 0 ? (
|
|
158
|
+
<p className={styles.changelogEmpty}>No updates match the selected tags.</p>
|
|
159
|
+
) : null}
|
|
160
|
+
{filtered}
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
}
|