jamdesk 1.1.7 → 1.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/lib/deps.js +2 -2
- package/dist/lib/openapi/types.d.ts +11 -6
- package/dist/lib/openapi/types.d.ts.map +1 -1
- package/dist/lib/path-security.d.ts +3 -0
- package/dist/lib/path-security.d.ts.map +1 -1
- package/dist/lib/path-security.js +14 -1
- package/dist/lib/path-security.js.map +1 -1
- package/package.json +13 -10
- package/vendored/app/[[...slug]]/page.tsx +48 -13
- package/vendored/app/api/assets/[...path]/route.ts +2 -0
- package/vendored/components/layout/LayoutWrapper.tsx +3 -4
- package/vendored/components/mdx/ApiEndpoint.tsx +13 -2
- package/vendored/components/mdx/MDXComponents.tsx +16 -0
- package/vendored/components/mdx/OpenApiEndpoint.tsx +76 -36
- package/vendored/components/mdx/Tabs.tsx +1 -1
- package/vendored/components/mdx/Video.tsx +82 -0
- package/vendored/components/navigation/Header.tsx +3 -3
- package/vendored/components/navigation/Sidebar.tsx +3 -3
- package/vendored/components/ui/CodePanel.tsx +5 -5
- package/vendored/components/ui/CodePanelModal.tsx +3 -3
- package/vendored/components/ui/DevOnlyNotice.tsx +78 -0
- package/vendored/hooks/useChatPanel.tsx +21 -2
- package/vendored/hooks/useMediaQuery.ts +27 -0
- package/vendored/lib/build-endpoint-from-mdx.ts +66 -0
- package/vendored/lib/isr-build-executor.ts +1 -1
- package/vendored/lib/middleware-helpers.ts +6 -1
- package/vendored/lib/openapi/code-examples.ts +479 -99
- package/vendored/lib/openapi/index.ts +9 -1
- package/vendored/lib/openapi/types.ts +29 -5
- package/vendored/lib/preprocess-mdx.ts +103 -36
- package/vendored/lib/process-mdx-with-exports.ts +22 -14
- package/vendored/lib/remark-extract-param-fields.ts +134 -0
- package/vendored/lib/shiki-client.ts +12 -0
- package/vendored/lib/static-artifacts.ts +2 -0
- package/vendored/lib/url-safety.ts +122 -0
- package/vendored/next.config.js +7 -0
- package/vendored/schema/docs-schema.json +17 -4
- package/vendored/scripts/copy-files.cjs +60 -54
- package/vendored/scripts/validate-links.cjs +1 -1
- package/vendored/shared/path-security.ts +17 -1
package/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# Jamdesk CLI
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/jamdesk)
|
|
4
|
+
[](https://www.npmjs.com/package/jamdesk)
|
|
4
5
|
[](https://nodejs.org)
|
|
5
6
|
[](./LICENSE)
|
|
7
|
+
[](https://www.jamdesk.com/docs/claude-code)
|
|
6
8
|
|
|
7
9
|
CLI for [Jamdesk](https://www.jamdesk.com) — build, preview, and deploy documentation sites from MDX.
|
|
8
10
|
|
|
@@ -32,7 +34,7 @@ Your docs are at **http://localhost:3000**. That's it.
|
|
|
32
34
|
|
|
33
35
|
## Installation
|
|
34
36
|
|
|
35
|
-
### npm (recommended)
|
|
37
|
+
### [npm](https://www.npmjs.com/package/jamdesk) (recommended)
|
|
36
38
|
|
|
37
39
|
```bash
|
|
38
40
|
npm install -g jamdesk
|
|
@@ -222,7 +224,7 @@ Found 3 misspellings across 24 pages.
|
|
|
222
224
|
Tip: Run "jamdesk spellcheck --fix" to interactively fix or ignore words.
|
|
223
225
|
```
|
|
224
226
|
|
|
225
|
-
Uses an English dictionary with 150+ built-in tech terms (API, GraphQL, Kubernetes, etc.) so common jargon doesn't flag. Add project-specific words to `docs.json`:
|
|
227
|
+
Uses an English dictionary with 150+ built-in tech terms (API, GraphQL, Kubernetes, etc.) so common jargon doesn't flag. Currently English only — multi-language support is planned. Add project-specific words to `docs.json`:
|
|
226
228
|
|
|
227
229
|
```json
|
|
228
230
|
{
|
|
@@ -429,6 +431,7 @@ See the [docs.json reference](https://www.jamdesk.com/docs/config/docs-json-refe
|
|
|
429
431
|
- [Config Reference](https://www.jamdesk.com/docs/config/docs-json-reference)
|
|
430
432
|
- [OpenAPI](https://www.jamdesk.com/docs/api-reference/openapi-setup)
|
|
431
433
|
- [Deployment](https://www.jamdesk.com/docs/deploy)
|
|
434
|
+
- [npm Package](https://www.npmjs.com/package/jamdesk)
|
|
432
435
|
- [Homepage](https://www.jamdesk.com)
|
|
433
436
|
- [Pricing](https://www.jamdesk.com/pricing)
|
|
434
437
|
|
package/dist/lib/deps.js
CHANGED
|
@@ -54,7 +54,7 @@ const REQUIRED_DEPS = {
|
|
|
54
54
|
'remark-math': '^6.0.0',
|
|
55
55
|
'remark-smartypants': '^3.0.2',
|
|
56
56
|
// Math/LaTeX rendering
|
|
57
|
-
'katex': '^0.16.
|
|
57
|
+
'katex': '^0.16.44',
|
|
58
58
|
// Diagrams
|
|
59
59
|
'mermaid': '^11.12.2',
|
|
60
60
|
// YAML parsing (for OpenAPI specs)
|
|
@@ -81,7 +81,7 @@ const REQUIRED_DEPS = {
|
|
|
81
81
|
'json5': '^2.2.3',
|
|
82
82
|
'glob': '^13.0.6',
|
|
83
83
|
// TypeScript (needed for Next.js to avoid auto-install breaking symlink)
|
|
84
|
-
'typescript': '^
|
|
84
|
+
'typescript': '^6.0.2',
|
|
85
85
|
'@types/node': '^25.5.0',
|
|
86
86
|
'@types/react': '^19.2.14',
|
|
87
87
|
'@types/react-dom': '^19.0.0',
|
|
@@ -168,12 +168,17 @@ export interface ValidationResult {
|
|
|
168
168
|
error?: OpenApiValidationError;
|
|
169
169
|
}
|
|
170
170
|
/**
|
|
171
|
-
*
|
|
172
|
-
*/
|
|
173
|
-
export interface
|
|
174
|
-
curl
|
|
175
|
-
|
|
176
|
-
|
|
171
|
+
* Single code example for a specific language
|
|
172
|
+
*/
|
|
173
|
+
export interface CodeExample {
|
|
174
|
+
/** Language identifier (e.g., 'curl', 'python', 'go') */
|
|
175
|
+
id: string;
|
|
176
|
+
/** Display label for the tab (e.g., 'cURL', 'Python', 'Go') */
|
|
177
|
+
label: string;
|
|
178
|
+
/** Shiki language ID for syntax highlighting (e.g., 'bash', 'python', 'go') */
|
|
179
|
+
language: string;
|
|
180
|
+
/** Generated code string */
|
|
181
|
+
code: string;
|
|
177
182
|
}
|
|
178
183
|
/**
|
|
179
184
|
* Generated page from auto-generation
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/openapi/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/openapi/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGrE,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACxC,oBAAoB,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAC5C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAE1F;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,iBAAiB,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;QACtB,MAAM,EAAE,UAAU,CAAC;QACnB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACjE,CAAC,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QACvB,MAAM,EAAE,UAAU,CAAC;QACnB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACjE,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QACvB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,UAAU,CAAC;KACpB,CAAC,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QACzB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,eAAe,CAAC;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAElC,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAGhB,OAAO,EAAE,UAAU,EAAE,CAAC;IAGtB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAGhC,UAAU,EAAE,eAAe,EAAE,CAAC;IAG9B,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAGhC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAG1C,YAAY,CAAC,EAAE;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GACxB,gBAAgB,GAChB,aAAa,GACb,kBAAkB,GAClB,WAAW,GACX,oBAAoB,GACpB,mBAAmB,CAAC;AAExB;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC;IACvB,OAAO,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;IAChC,KAAK,CAAC,EAAE,sBAAsB,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,mBAAmB,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAC;IACtB,OAAO,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
/**
|
|
10
10
|
* Check if a path stays within the project directory.
|
|
11
11
|
* Returns true if path is within project, false otherwise.
|
|
12
|
+
*
|
|
13
|
+
* Rejects null bytes, URL-encoded sequences, and normalizes
|
|
14
|
+
* backslashes to prevent traversal via encoding tricks.
|
|
12
15
|
*/
|
|
13
16
|
export declare function isPathWithinProject(filePath: string, projectDir: string): boolean;
|
|
14
17
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-security.d.ts","sourceRoot":"","sources":["../../src/lib/path-security.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH
|
|
1
|
+
{"version":3,"file":"path-security.d.ts","sourceRoot":"","sources":["../../src/lib/path-security.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAkBjF;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAIpF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAGtF"}
|
|
@@ -10,9 +10,22 @@ import path from 'path';
|
|
|
10
10
|
/**
|
|
11
11
|
* Check if a path stays within the project directory.
|
|
12
12
|
* Returns true if path is within project, false otherwise.
|
|
13
|
+
*
|
|
14
|
+
* Rejects null bytes, URL-encoded sequences, and normalizes
|
|
15
|
+
* backslashes to prevent traversal via encoding tricks.
|
|
13
16
|
*/
|
|
14
17
|
export function isPathWithinProject(filePath, projectDir) {
|
|
15
|
-
|
|
18
|
+
// Reject null bytes (real or URL-encoded)
|
|
19
|
+
if (filePath.includes('\0') || filePath.includes('%00')) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
// Reject URL-encoded path separators and traversal sequences
|
|
23
|
+
if (/%2f/i.test(filePath) || /%5c/i.test(filePath)) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
// Normalize backslashes to forward slashes to prevent Windows-style traversal
|
|
27
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
28
|
+
const absolutePath = path.resolve(projectDir, normalized);
|
|
16
29
|
const normalizedProject = path.resolve(projectDir);
|
|
17
30
|
return absolutePath.startsWith(normalizedProject + path.sep) || absolutePath === normalizedProject;
|
|
18
31
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-security.js","sourceRoot":"","sources":["../../src/lib/path-security.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB
|
|
1
|
+
{"version":3,"file":"path-security.js","sourceRoot":"","sources":["../../src/lib/path-security.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,UAAkB;IACtE,0CAA0C;IAC1C,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,6DAA6D;IAC7D,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,8EAA8E;IAC9E,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAEhD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAC1D,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAEnD,OAAO,YAAY,CAAC,UAAU,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,YAAY,KAAK,iBAAiB,CAAC;AACrG,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAAgB,EAAE,UAAkB;IAC5E,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,mCAAmC,QAAQ,EAAE,CAAC,CAAC;IACjE,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,EAAU,EAAE,UAAkB;IAC9E,yBAAyB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC5C,yBAAyB,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;AAC5C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,36 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jamdesk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.8",
|
|
4
4
|
"description": "CLI for Jamdesk — build, preview, and deploy documentation sites from MDX. Dev server with hot reload, 50+ components, OpenAPI support, AI search, and Mintlify migration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jamdesk",
|
|
7
7
|
"documentation",
|
|
8
8
|
"documentation-generator",
|
|
9
9
|
"documentation-platform",
|
|
10
|
+
"documentation-tool",
|
|
10
11
|
"docs",
|
|
11
12
|
"docs-as-code",
|
|
13
|
+
"docs-generator",
|
|
12
14
|
"mdx",
|
|
13
15
|
"markdown",
|
|
14
16
|
"cli",
|
|
15
|
-
"dev-server",
|
|
16
|
-
"hot-reload",
|
|
17
|
-
"turbopack",
|
|
18
17
|
"developer-tools",
|
|
19
18
|
"api-documentation",
|
|
20
19
|
"openapi",
|
|
21
20
|
"api-reference",
|
|
22
|
-
"static-site",
|
|
21
|
+
"static-site-generator",
|
|
23
22
|
"nextjs",
|
|
23
|
+
"react",
|
|
24
|
+
"typescript",
|
|
24
25
|
"technical-writing",
|
|
25
26
|
"mintlify",
|
|
26
27
|
"mintlify-alternative",
|
|
28
|
+
"docusaurus-alternative",
|
|
29
|
+
"gitbook-alternative",
|
|
27
30
|
"documentation-hosting",
|
|
28
31
|
"documentation-site",
|
|
29
32
|
"developer-documentation",
|
|
30
33
|
"api-docs",
|
|
31
|
-
"react-components",
|
|
32
34
|
"docs-site-generator",
|
|
33
|
-
"ai-search"
|
|
35
|
+
"ai-search",
|
|
36
|
+
"llms-txt"
|
|
34
37
|
],
|
|
35
38
|
"homepage": "https://www.jamdesk.com",
|
|
36
39
|
"bugs": {
|
|
@@ -44,7 +47,7 @@
|
|
|
44
47
|
"license": "Apache-2.0",
|
|
45
48
|
"author": {
|
|
46
49
|
"name": "Jamdesk",
|
|
47
|
-
"email": "
|
|
50
|
+
"email": "support@jamdesk.com",
|
|
48
51
|
"url": "https://www.jamdesk.com"
|
|
49
52
|
},
|
|
50
53
|
"type": "module",
|
|
@@ -113,8 +116,8 @@
|
|
|
113
116
|
"@mdx-js/mdx": "^3.1.1",
|
|
114
117
|
"@types/fs-extra": "^11.0.0",
|
|
115
118
|
"@types/node": "^25.5.0",
|
|
116
|
-
"typescript": "^
|
|
117
|
-
"vitest": "^4.1.
|
|
119
|
+
"typescript": "^6.0.2",
|
|
120
|
+
"vitest": "^4.1.2"
|
|
118
121
|
},
|
|
119
122
|
"engines": {
|
|
120
123
|
"node": ">=20.0.0"
|
|
@@ -42,6 +42,7 @@ import { getLatexRemarkPlugins, getLatexRehypePlugins } from '@/lib/latex-config
|
|
|
42
42
|
import { getTypographyRemarkPlugins } from '@/lib/typography-config';
|
|
43
43
|
import { recmaCompoundComponents } from '@/lib/recma-compound-components';
|
|
44
44
|
import { extractInlineComponents } from '@/lib/process-mdx-with-exports';
|
|
45
|
+
import { buildEndpointFromMdx } from '@/lib/build-endpoint-from-mdx';
|
|
45
46
|
import { mdxSecurityOptions } from '@/lib/mdx-security-options';
|
|
46
47
|
import fs from 'fs';
|
|
47
48
|
import path from 'path';
|
|
@@ -67,7 +68,7 @@ import {
|
|
|
67
68
|
generateCodeExamples,
|
|
68
69
|
formatOpenApiWarning,
|
|
69
70
|
type OpenApiEndpointData,
|
|
70
|
-
type
|
|
71
|
+
type CodeExample,
|
|
71
72
|
type AuthMethod,
|
|
72
73
|
} from '@/lib/openapi';
|
|
73
74
|
import { ApiEndpoint } from '@/components/mdx/ApiEndpoint';
|
|
@@ -134,6 +135,7 @@ interface FrontmatterData {
|
|
|
134
135
|
description?: string;
|
|
135
136
|
api?: string;
|
|
136
137
|
openapi?: string;
|
|
138
|
+
playground?: string;
|
|
137
139
|
mode?: string;
|
|
138
140
|
hideFooter?: boolean;
|
|
139
141
|
rss?: boolean;
|
|
@@ -505,7 +507,7 @@ export default async function DocPage({ params }: PageProps) {
|
|
|
505
507
|
|
|
506
508
|
// Extract and compile inline component exports from MDX
|
|
507
509
|
// Only pass MDXComponents (server-compatible) to inline extraction
|
|
508
|
-
const { inlineComponents } = await extractInlineComponents(content, MDXComponents);
|
|
510
|
+
const { inlineComponents, paramFields } = await extractInlineComponents(content, MDXComponents);
|
|
509
511
|
|
|
510
512
|
// Check for component name collisions and warn
|
|
511
513
|
const overriddenComponents = Object.keys(inlineComponents).filter(
|
|
@@ -560,7 +562,7 @@ export default async function DocPage({ params }: PageProps) {
|
|
|
560
562
|
|
|
561
563
|
// Parse OpenAPI endpoint data if openapi frontmatter is present
|
|
562
564
|
let openApiEndpointData: OpenApiEndpointData | null = null;
|
|
563
|
-
let openApiCodeExamples:
|
|
565
|
+
let openApiCodeExamples: CodeExample[] | null = null;
|
|
564
566
|
|
|
565
567
|
// OpenAPI spec parsing - supports both static and ISR modes
|
|
566
568
|
if (data.openapi && typeof data.openapi === 'string') {
|
|
@@ -591,7 +593,8 @@ export default async function DocPage({ params }: PageProps) {
|
|
|
591
593
|
|
|
592
594
|
// Generate code examples
|
|
593
595
|
const authMethod = config.api?.mdx?.auth?.method as AuthMethod | undefined;
|
|
594
|
-
|
|
596
|
+
const languages = config.api?.examples?.languages;
|
|
597
|
+
openApiCodeExamples = generateCodeExamples(openApiEndpointData, { authMethod, languages });
|
|
595
598
|
} catch (err) {
|
|
596
599
|
// Log formatted warning to console (appears in CLI and build logs)
|
|
597
600
|
// Check if it's an OpenAPI validation error with our format
|
|
@@ -607,16 +610,12 @@ export default async function DocPage({ params }: PageProps) {
|
|
|
607
610
|
// Parse MDX api field for pages with api: frontmatter (but not openapi:)
|
|
608
611
|
let mdxApiMethod: HttpMethod | null = null;
|
|
609
612
|
let mdxApiPath: string | null = null;
|
|
610
|
-
let mdxApiBaseUrl: string | undefined;
|
|
611
613
|
|
|
612
614
|
if (data.api && typeof data.api === 'string' && !data.openapi) {
|
|
613
615
|
const parsed = parseMdxApiField(data.api);
|
|
614
616
|
if (parsed) {
|
|
615
617
|
mdxApiMethod = parsed.method;
|
|
616
618
|
mdxApiPath = parsed.path;
|
|
617
|
-
// Get server URL from config, with fallback
|
|
618
|
-
const serverConfig = config.api?.mdx?.server;
|
|
619
|
-
mdxApiBaseUrl = Array.isArray(serverConfig) ? serverConfig[0] : serverConfig;
|
|
620
619
|
}
|
|
621
620
|
}
|
|
622
621
|
|
|
@@ -644,6 +643,23 @@ export default async function DocPage({ params }: PageProps) {
|
|
|
644
643
|
// Prose class for MDX content styling (defined in base.css)
|
|
645
644
|
const proseClasses = 'prose max-w-none';
|
|
646
645
|
|
|
646
|
+
// Playground configuration — covers both openapi: and api: pages
|
|
647
|
+
const hasApiEndpoint = openApiEndpointData || (mdxApiMethod && mdxApiPath);
|
|
648
|
+
const playgroundDisplay = hasApiEndpoint
|
|
649
|
+
? ((data.playground as 'interactive' | 'simple' | 'none' | undefined)
|
|
650
|
+
|| config.api?.playground?.display || 'none') as 'interactive' | 'simple' | 'none'
|
|
651
|
+
: 'none';
|
|
652
|
+
const mdxServerConfig = config.api?.mdx?.server;
|
|
653
|
+
const fallbackServerUrl = Array.isArray(mdxServerConfig) ? mdxServerConfig[0] : mdxServerConfig;
|
|
654
|
+
const proxyEnabled = config.api?.playground?.proxy
|
|
655
|
+
?? (playgroundDisplay === 'interactive');
|
|
656
|
+
|
|
657
|
+
// Build endpoint data for api: pages (for playground)
|
|
658
|
+
let mdxEndpointData: OpenApiEndpointData | undefined;
|
|
659
|
+
if (!openApiEndpointData && mdxApiMethod && mdxApiPath && playgroundDisplay !== 'none') {
|
|
660
|
+
mdxEndpointData = buildEndpointFromMdx(mdxApiMethod, mdxApiPath, paramFields, fallbackServerUrl);
|
|
661
|
+
}
|
|
662
|
+
|
|
647
663
|
// For API pages, wrap the entire content area with ApiPageWrapper
|
|
648
664
|
// so code panels can be positioned as siblings at the page level
|
|
649
665
|
if (isApiPage) {
|
|
@@ -676,11 +692,24 @@ export default async function DocPage({ params }: PageProps) {
|
|
|
676
692
|
<div className={proseClasses}>
|
|
677
693
|
{/* MDX API endpoint badge (for pages with api: frontmatter) */}
|
|
678
694
|
{mdxApiMethod && mdxApiPath && (
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
695
|
+
mdxEndpointData && playgroundDisplay !== 'none' ? (
|
|
696
|
+
<OpenApiEndpoint
|
|
697
|
+
endpoint={mdxEndpointData}
|
|
698
|
+
playgroundOnly
|
|
699
|
+
playgroundDisplay={playgroundDisplay}
|
|
700
|
+
authMethod={config.api?.mdx?.auth?.method as AuthMethod | undefined}
|
|
701
|
+
authHeaderName={config.api?.mdx?.auth?.name}
|
|
702
|
+
serverUrl={fallbackServerUrl}
|
|
703
|
+
proxyEnabled={proxyEnabled}
|
|
704
|
+
languages={config.api?.examples?.languages}
|
|
705
|
+
/>
|
|
706
|
+
) : (
|
|
707
|
+
<ApiEndpoint
|
|
708
|
+
method={mdxApiMethod}
|
|
709
|
+
path={mdxApiPath}
|
|
710
|
+
baseUrl={fallbackServerUrl}
|
|
711
|
+
/>
|
|
712
|
+
)
|
|
684
713
|
)}
|
|
685
714
|
|
|
686
715
|
{/* OpenAPI endpoint documentation (auto-generated from spec) */}
|
|
@@ -688,6 +717,12 @@ export default async function DocPage({ params }: PageProps) {
|
|
|
688
717
|
<OpenApiEndpoint
|
|
689
718
|
endpoint={openApiEndpointData}
|
|
690
719
|
codeExamples={openApiCodeExamples || undefined}
|
|
720
|
+
playgroundDisplay={playgroundDisplay}
|
|
721
|
+
authMethod={config.api?.mdx?.auth?.method as AuthMethod | undefined}
|
|
722
|
+
authHeaderName={config.api?.mdx?.auth?.name}
|
|
723
|
+
serverUrl={fallbackServerUrl}
|
|
724
|
+
proxyEnabled={proxyEnabled}
|
|
725
|
+
languages={config.api?.examples?.languages}
|
|
691
726
|
/>
|
|
692
727
|
)}
|
|
693
728
|
|
|
@@ -18,6 +18,8 @@ const CACHE_DURATIONS: Record<string, number> = {
|
|
|
18
18
|
'image/svg+xml': 86400, // 1 day (may change)
|
|
19
19
|
'image/x-icon': 31536000,
|
|
20
20
|
'application/pdf': 86400,
|
|
21
|
+
'video/mp4': 31536000, // 1 year (busted by ?v= query param on new builds)
|
|
22
|
+
'video/webm': 31536000,
|
|
21
23
|
default: 3600, // 1 hour
|
|
22
24
|
};
|
|
23
25
|
|
|
@@ -37,10 +37,9 @@ function useMediaQuery(query: string): boolean {
|
|
|
37
37
|
|
|
38
38
|
export function LayoutWrapper({ config, children }: LayoutWrapperProps) {
|
|
39
39
|
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
|
40
|
-
// Chat is
|
|
41
|
-
//
|
|
42
|
-
const chatEnabled =
|
|
43
|
-
&& (config.chat?.enabled !== false);
|
|
40
|
+
// Chat button is shown in both dev and production so the layout matches.
|
|
41
|
+
// In dev mode, clicking it shows a "production only" notice instead of opening the panel.
|
|
42
|
+
const chatEnabled = config.chat?.enabled !== false;
|
|
44
43
|
|
|
45
44
|
// xl breakpoint (1280px) — chat renders inline on desktop, overlay on mobile
|
|
46
45
|
const isDesktop = useMediaQuery('(min-width: 1280px)');
|
|
@@ -6,6 +6,7 @@ interface ApiEndpointProps {
|
|
|
6
6
|
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'TRACE';
|
|
7
7
|
path: string;
|
|
8
8
|
baseUrl?: string;
|
|
9
|
+
onTryIt?: () => void;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
const methodColors: Record<string, { bg: string; text: string; border: string }> = {
|
|
@@ -51,7 +52,7 @@ const methodColors: Record<string, { bg: string; text: string; border: string }>
|
|
|
51
52
|
},
|
|
52
53
|
};
|
|
53
54
|
|
|
54
|
-
export function ApiEndpoint({ method, path, baseUrl = 'https://api.jamdesk.com/api' }: ApiEndpointProps) {
|
|
55
|
+
export function ApiEndpoint({ method, path, baseUrl = 'https://api.jamdesk.com/api', onTryIt }: ApiEndpointProps) {
|
|
55
56
|
const [copied, setCopied] = useState(false);
|
|
56
57
|
const colors = methodColors[method] || methodColors.GET;
|
|
57
58
|
const fullUrl = `${baseUrl}${path}`;
|
|
@@ -73,10 +74,20 @@ export function ApiEndpoint({ method, path, baseUrl = 'https://api.jamdesk.com/a
|
|
|
73
74
|
<span className="text-[var(--color-text-muted)]">{baseUrl}</span>
|
|
74
75
|
<span className="text-[var(--color-text-primary)]">{path}</span>
|
|
75
76
|
</span>
|
|
77
|
+
{/* Try it button */}
|
|
78
|
+
{onTryIt && (
|
|
79
|
+
<button
|
|
80
|
+
onClick={onTryIt}
|
|
81
|
+
className="px-2.5 py-1 text-xs font-medium rounded-md bg-gradient-to-r from-[var(--color-accent)] to-[var(--color-accent-hover,var(--color-accent))] text-white hover:opacity-90 transition-opacity cursor-pointer"
|
|
82
|
+
aria-label="Open API playground"
|
|
83
|
+
>
|
|
84
|
+
Try it
|
|
85
|
+
</button>
|
|
86
|
+
)}
|
|
76
87
|
{/* Copy button */}
|
|
77
88
|
<button
|
|
78
89
|
onClick={handleCopy}
|
|
79
|
-
className="p-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] rounded-md transition-colors"
|
|
90
|
+
className="p-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] rounded-md transition-colors cursor-pointer"
|
|
80
91
|
title="Copy endpoint URL"
|
|
81
92
|
aria-label="Copy endpoint URL"
|
|
82
93
|
>
|
|
@@ -30,6 +30,7 @@ import { Tree, TreeFolder, TreeFile } from './Tree';
|
|
|
30
30
|
import { Columns } from './Columns';
|
|
31
31
|
import { View, ViewProvider, ViewSelector, ViewWrapper } from './View';
|
|
32
32
|
import { YouTube } from './YouTube';
|
|
33
|
+
import { Video } from './Video';
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* Extract language from a pre element for tab label
|
|
@@ -148,6 +149,14 @@ function HttpCodeBlock({ method, url }: { method: string; url: string }) {
|
|
|
148
149
|
);
|
|
149
150
|
}
|
|
150
151
|
|
|
152
|
+
/** Fallback for raw <img src="video.mp4"> in MDX (preprocessor handles markdown syntax) */
|
|
153
|
+
const VIDEO_EXTENSIONS_IMG = ['.mp4', '.webm'];
|
|
154
|
+
function isVideoUrl(src: string | undefined): boolean {
|
|
155
|
+
if (!src) return false;
|
|
156
|
+
const pathOnly = src.split('?')[0];
|
|
157
|
+
return VIDEO_EXTENSIONS_IMG.some(ext => pathOnly.toLowerCase().endsWith(ext));
|
|
158
|
+
}
|
|
159
|
+
|
|
151
160
|
export const MDXComponents = {
|
|
152
161
|
Card,
|
|
153
162
|
// Callout components
|
|
@@ -218,6 +227,8 @@ export const MDXComponents = {
|
|
|
218
227
|
ViewWrapper,
|
|
219
228
|
// Media embeds
|
|
220
229
|
YouTube,
|
|
230
|
+
// Video player for local video files
|
|
231
|
+
Video,
|
|
221
232
|
// Sized images from preprocess-mdx ( syntax).
|
|
222
233
|
// These are output as <SizedImage> JSX so they go through component mapping
|
|
223
234
|
// (raw <img> JSX in MDX bypasses the components provider).
|
|
@@ -253,6 +264,11 @@ export const MDXComponents = {
|
|
|
253
264
|
// Destructure and ignore any other props to prevent them from being passed to DOM
|
|
254
265
|
...rest
|
|
255
266
|
}: any) => {
|
|
267
|
+
// Check if this is a video file — render <Video> instead of <ZoomableImage>
|
|
268
|
+
if (isVideoUrl(src)) {
|
|
269
|
+
return <Video src={src} title={alt || undefined} />;
|
|
270
|
+
}
|
|
271
|
+
|
|
256
272
|
// Check for data-no-zoom attribute or noZoom prop (handle both camelCase and lowercase)
|
|
257
273
|
const disableZoom = dataNoZoom === 'true' || dataNoZoom === true ||
|
|
258
274
|
noZoom === true || noZoom === 'true' ||
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { useDevOnlyNotice, DEV_FEATURE } from '../ui/DevOnlyNotice';
|
|
4
|
+
|
|
5
|
+
import { useState, useEffect, useMemo } from 'react';
|
|
4
6
|
import { ApiEndpoint } from './ApiEndpoint';
|
|
5
7
|
import { ParamField } from './ParamField';
|
|
6
8
|
import { CodeGroup } from './CodeGroup';
|
|
@@ -14,9 +16,10 @@ import type {
|
|
|
14
16
|
OpenApiEndpointData,
|
|
15
17
|
ParsedParameter,
|
|
16
18
|
ParsedResponse,
|
|
17
|
-
|
|
19
|
+
CodeExample,
|
|
18
20
|
JsonSchema,
|
|
19
21
|
SecurityRequirement,
|
|
22
|
+
AuthMethod,
|
|
20
23
|
} from '@/lib/openapi/types';
|
|
21
24
|
|
|
22
25
|
// Preload Shiki highlighter on module load
|
|
@@ -28,9 +31,25 @@ interface OpenApiEndpointProps {
|
|
|
28
31
|
/** Endpoint data from OpenAPI spec */
|
|
29
32
|
endpoint: OpenApiEndpointData;
|
|
30
33
|
/** Generated code examples */
|
|
31
|
-
codeExamples?:
|
|
34
|
+
codeExamples?: CodeExample[];
|
|
32
35
|
/** Additional content to render after endpoint info */
|
|
33
36
|
children?: React.ReactNode;
|
|
37
|
+
/** Playground display mode */
|
|
38
|
+
playgroundDisplay?: 'interactive' | 'simple' | 'none';
|
|
39
|
+
/** Auth method for playground */
|
|
40
|
+
authMethod?: AuthMethod;
|
|
41
|
+
/** Custom auth header name */
|
|
42
|
+
authHeaderName?: string;
|
|
43
|
+
/** Fallback server URL when endpoint.servers is empty */
|
|
44
|
+
serverUrl?: string;
|
|
45
|
+
/** Whether CORS proxy is enabled */
|
|
46
|
+
proxyEnabled?: boolean;
|
|
47
|
+
/** Languages for code generation */
|
|
48
|
+
languages?: string[];
|
|
49
|
+
/** When true, render only the ApiEndpoint badge (with Try it) + PlaygroundModal.
|
|
50
|
+
* Skips all auto-generated param docs, response panels, and code examples.
|
|
51
|
+
* Used for api: pages where the MDX content provides the documentation. */
|
|
52
|
+
playgroundOnly?: boolean;
|
|
34
53
|
}
|
|
35
54
|
|
|
36
55
|
/**
|
|
@@ -834,35 +853,23 @@ function ResponseFieldItem({
|
|
|
834
853
|
/**
|
|
835
854
|
* Code examples section - renders in right panel using CodePanel
|
|
836
855
|
*/
|
|
837
|
-
function CodeExamplesSection({ examples }: { examples:
|
|
856
|
+
function CodeExamplesSection({ examples }: { examples: CodeExample[] }) {
|
|
838
857
|
const codeItems = useMemo(
|
|
839
|
-
() =>
|
|
840
|
-
|
|
841
|
-
{ code: examples.python, language: 'python' },
|
|
842
|
-
{ code: examples.javascript, language: 'javascript' },
|
|
843
|
-
],
|
|
844
|
-
[examples.curl, examples.python, examples.javascript]
|
|
858
|
+
() => examples.map(ex => ({ code: ex.code, language: ex.language })),
|
|
859
|
+
[examples]
|
|
845
860
|
);
|
|
846
861
|
|
|
847
862
|
const { results: highlightedResults, isLoading } = useShikiHighlightMultiple(codeItems);
|
|
848
863
|
|
|
849
|
-
//
|
|
864
|
+
// Shiki generates HTML from trusted code strings (not user input)
|
|
850
865
|
const tabs: CodePanelTab[] = useMemo(
|
|
851
|
-
() =>
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
content: <div dangerouslySetInnerHTML={{ __html: highlightedResults[1] || '' }} />,
|
|
859
|
-
},
|
|
860
|
-
{
|
|
861
|
-
label: 'JavaScript',
|
|
862
|
-
content: <div dangerouslySetInnerHTML={{ __html: highlightedResults[2] || '' }} />,
|
|
863
|
-
},
|
|
864
|
-
],
|
|
865
|
-
[highlightedResults]
|
|
866
|
+
() => examples.map((ex, i) => ({
|
|
867
|
+
label: ex.label,
|
|
868
|
+
content: highlightedResults[i]
|
|
869
|
+
? <div dangerouslySetInnerHTML={{ __html: highlightedResults[i] }} />
|
|
870
|
+
: null,
|
|
871
|
+
})),
|
|
872
|
+
[examples, highlightedResults]
|
|
866
873
|
);
|
|
867
874
|
|
|
868
875
|
// Don't render until highlighting is complete to prevent flash of unformatted content
|
|
@@ -880,13 +887,20 @@ function CodeExamplesSection({ examples }: { examples: CodeExamples }) {
|
|
|
880
887
|
export function OpenApiEndpoint({
|
|
881
888
|
endpoint,
|
|
882
889
|
codeExamples,
|
|
883
|
-
children
|
|
890
|
+
children,
|
|
891
|
+
playgroundDisplay = 'none',
|
|
892
|
+
authMethod,
|
|
893
|
+
authHeaderName,
|
|
894
|
+
serverUrl,
|
|
895
|
+
proxyEnabled,
|
|
896
|
+
languages,
|
|
897
|
+
playgroundOnly,
|
|
884
898
|
}: OpenApiEndpointProps) {
|
|
885
|
-
const {
|
|
886
|
-
method,
|
|
887
|
-
path,
|
|
888
|
-
summary,
|
|
889
|
-
description,
|
|
899
|
+
const {
|
|
900
|
+
method,
|
|
901
|
+
path,
|
|
902
|
+
summary,
|
|
903
|
+
description,
|
|
890
904
|
deprecated,
|
|
891
905
|
parameters,
|
|
892
906
|
requestBody,
|
|
@@ -895,15 +909,33 @@ export function OpenApiEndpoint({
|
|
|
895
909
|
externalDocs,
|
|
896
910
|
servers,
|
|
897
911
|
} = endpoint;
|
|
898
|
-
|
|
912
|
+
|
|
899
913
|
// Group parameters by location
|
|
900
914
|
const pathParams = parameters.filter(p => p.in === 'path');
|
|
901
915
|
const queryParams = parameters.filter(p => p.in === 'query');
|
|
902
916
|
const headerParams = parameters.filter(p => p.in === 'header');
|
|
903
917
|
const cookieParams = parameters.filter(p => p.in === 'cookie');
|
|
904
|
-
|
|
918
|
+
|
|
905
919
|
const baseUrl = servers[0]?.url;
|
|
906
|
-
|
|
920
|
+
|
|
921
|
+
// Playground state
|
|
922
|
+
const showPlayground = playgroundDisplay !== 'none';
|
|
923
|
+
const { showNotice: showDevNotice, notice: devNotice } = useDevOnlyNotice();
|
|
924
|
+
|
|
925
|
+
if (playgroundOnly) {
|
|
926
|
+
return (
|
|
927
|
+
<div className="openapi-endpoint">
|
|
928
|
+
<ApiEndpoint
|
|
929
|
+
method={method}
|
|
930
|
+
path={path}
|
|
931
|
+
baseUrl={baseUrl}
|
|
932
|
+
onTryIt={showPlayground ? () => { showDevNotice(DEV_FEATURE.API_PLAYGROUND); } : undefined}
|
|
933
|
+
/>
|
|
934
|
+
{devNotice}
|
|
935
|
+
</div>
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
|
|
907
939
|
return (
|
|
908
940
|
<div className="openapi-endpoint">
|
|
909
941
|
{/* Deprecation Warning */}
|
|
@@ -917,7 +949,12 @@ export function OpenApiEndpoint({
|
|
|
917
949
|
)}
|
|
918
950
|
|
|
919
951
|
{/* Endpoint Badge */}
|
|
920
|
-
<ApiEndpoint
|
|
952
|
+
<ApiEndpoint
|
|
953
|
+
method={method}
|
|
954
|
+
path={path}
|
|
955
|
+
baseUrl={baseUrl}
|
|
956
|
+
onTryIt={showPlayground ? () => { showDevNotice(DEV_FEATURE.API_PLAYGROUND); } : undefined}
|
|
957
|
+
/>
|
|
921
958
|
|
|
922
959
|
{/* External Docs */}
|
|
923
960
|
{externalDocs && (
|
|
@@ -961,9 +998,12 @@ export function OpenApiEndpoint({
|
|
|
961
998
|
|
|
962
999
|
{/* Responses - field documentation in main content */}
|
|
963
1000
|
<ResponseSection responses={responses} />
|
|
1001
|
+
{devNotice}
|
|
964
1002
|
|
|
965
1003
|
{/* Additional Content */}
|
|
966
1004
|
{children}
|
|
1005
|
+
|
|
1006
|
+
{/* Playground Modal (lazy-loaded) */}
|
|
967
1007
|
</div>
|
|
968
1008
|
);
|
|
969
1009
|
}
|
|
@@ -115,7 +115,7 @@ export const Tabs = memo(function Tabs({ children, borderBottom }: TabsProps) {
|
|
|
115
115
|
<button
|
|
116
116
|
key={index}
|
|
117
117
|
onClick={() => handleTabClick(index)}
|
|
118
|
-
className={`flex items-center gap-2 px-4 py-2.5 text-sm font-medium border-b-2 transition-colors whitespace-nowrap ${
|
|
118
|
+
className={`flex items-center gap-2 px-4 py-2.5 text-sm font-medium border-b-2 transition-colors whitespace-nowrap cursor-pointer ${
|
|
119
119
|
isActive
|
|
120
120
|
? 'border-[var(--color-accent)] text-[var(--color-accent-text)]'
|
|
121
121
|
: 'border-transparent text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)] hover:border-[var(--color-border)]'
|