jamdesk 1.1.90 → 1.1.92

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 (47) hide show
  1. package/package.json +1 -1
  2. package/vendored/app/api/r2/[project]/[...path]/route.ts +14 -9
  3. package/vendored/app/layout.tsx +2 -2
  4. package/vendored/components/HtmlLangSync.tsx +3 -2
  5. package/vendored/components/mdx/Accordion.tsx +1 -1
  6. package/vendored/components/mdx/Card.tsx +1 -1
  7. package/vendored/components/mdx/CodeGroup.tsx +18 -23
  8. package/vendored/components/mdx/Color.tsx +0 -1
  9. package/vendored/components/mdx/Icon.tsx +1 -1
  10. package/vendored/components/mdx/MDXComponents.tsx +92 -66
  11. package/vendored/components/mdx/OpenApiEndpoint.tsx +0 -1
  12. package/vendored/components/mdx/ParamField.tsx +0 -1
  13. package/vendored/components/mdx/RequestExample.tsx +0 -1
  14. package/vendored/components/mdx/ResponseExample.tsx +0 -1
  15. package/vendored/components/mdx/Steps.tsx +12 -3
  16. package/vendored/components/mdx/Table.tsx +8 -2
  17. package/vendored/components/mdx/Tabs.tsx +1 -1
  18. package/vendored/components/mdx/Tree.tsx +6 -4
  19. package/vendored/components/navigation/Header.tsx +7 -5
  20. package/vendored/components/navigation/LanguageSelector.tsx +37 -10
  21. package/vendored/components/navigation/TableOfContents.tsx +1 -1
  22. package/vendored/components/navigation/TabsNav.tsx +17 -5
  23. package/vendored/components/search/SearchModal.tsx +41 -36
  24. package/vendored/components/ui/CodePanel.tsx +2 -2
  25. package/vendored/hooks/useChat.ts +1 -1
  26. package/vendored/hooks/useShikiHighlight.ts +7 -1
  27. package/vendored/lib/code-utils.ts +6 -2
  28. package/vendored/lib/health-checks.ts +2 -2
  29. package/vendored/lib/language-utils.ts +87 -15
  30. package/vendored/lib/layout-helpers.tsx +2 -1
  31. package/vendored/lib/mdx-inline-components.ts +1 -1
  32. package/vendored/lib/navigation-resolver.ts +0 -69
  33. package/vendored/lib/normalize-config.ts +1 -1
  34. package/vendored/lib/openapi/generator.ts +3 -3
  35. package/vendored/lib/openapi/parser.ts +14 -6
  36. package/vendored/lib/openapi/validator.ts +2 -2
  37. package/vendored/lib/openapi-isr.ts +4 -1
  38. package/vendored/lib/public-paths-resolver.ts +7 -6
  39. package/vendored/lib/redis.ts +2 -2
  40. package/vendored/lib/rehype-code-meta.ts +2 -2
  41. package/vendored/lib/render-doc-page.tsx +2 -2
  42. package/vendored/lib/seo.ts +22 -7
  43. package/vendored/lib/shiki-highlighter.ts +1 -1
  44. package/vendored/lib/snippet-loader-isr.ts +1 -1
  45. package/vendored/lib/static-artifacts.ts +37 -6
  46. package/vendored/lib/validate-config.ts +1 -0
  47. package/vendored/workspace-package-lock.json +10 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jamdesk",
3
- "version": "1.1.90",
3
+ "version": "1.1.92",
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",
@@ -78,20 +78,25 @@ export async function GET(
78
78
 
79
79
  if (!stream) {
80
80
  const notFoundPage = await getFileFromR2(project, '/404.html');
81
- return new NextResponse((notFoundPage as any) ?? get404Html(project), {
82
- status: 404,
83
- headers: {
84
- 'Content-Type': 'text/html; charset=utf-8',
85
- 'Cache-Control': 'no-cache',
86
- },
87
- });
81
+ // R2 SDK ReadableStream may not exactly match the DOM ReadableStream<Uint8Array>
82
+ // expected by BodyInit, but it satisfies the underlying contract at runtime.
83
+ return new NextResponse(
84
+ (notFoundPage as ReadableStream<Uint8Array> | null) ?? get404Html(project),
85
+ {
86
+ status: 404,
87
+ headers: {
88
+ 'Content-Type': 'text/html; charset=utf-8',
89
+ 'Cache-Control': 'no-cache',
90
+ },
91
+ }
92
+ );
88
93
  }
89
94
 
90
95
  // Raw MDX source: prevent indexing and embedding
91
96
  const isMdx = filePath.endsWith('.mdx');
92
97
  const vercelCdnCache = getVercelCdnCacheControl(filePath);
93
98
 
94
- return new NextResponse(stream as any, {
99
+ return new NextResponse(stream as ReadableStream<Uint8Array>, {
95
100
  status: 200,
96
101
  headers: {
97
102
  'Content-Type': getContentType(filePath),
@@ -122,7 +127,7 @@ export async function GET(
122
127
  const JAMDESK_FAVICON = "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' fill='none'><rect x='3' y='7' width='14' height='19' rx='2.5' fill='%23C4B5FD' fill-opacity='0.9'/><rect x='8' y='4' width='14' height='19' rx='2.5' fill='%23A78BFA' fill-opacity='0.85'/><rect x='13' y='9' width='14' height='19' rx='2.5' fill='%238B5CF6' fill-opacity='0.9'/></svg>";
123
128
 
124
129
  // Simple fallback 404 page if project's 404.html doesn't exist
125
- function get404Html(project: string): string {
130
+ function get404Html(_project: string): string {
126
131
  return `<!DOCTYPE html>
127
132
  <html>
128
133
  <head>
@@ -17,7 +17,7 @@ import {
17
17
  getHostAtDocs,
18
18
  getLanguageFromRequest,
19
19
  } from '@/lib/page-isr-helpers';
20
- import { isRTLLanguage, resolveLanguageWithFallback } from '@/lib/language-utils';
20
+ import { isRTLLanguage, resolveLanguageWithFallback, toHreflang } from '@/lib/language-utils';
21
21
  import type { DocsConfig } from '@/lib/docs-types';
22
22
  import { buildSiteTitle, getFaviconPath, FALLBACK_METADATA } from '@/lib/seo';
23
23
  import { withR2OpsContext, emitR2OpsSummary } from '@/lib/r2-content';
@@ -114,7 +114,7 @@ async function rootLayoutImpl({
114
114
  );
115
115
  const unlockDir = isRTLLanguage(unlockLang) ? 'rtl' : undefined;
116
116
  return (
117
- <html lang={unlockLang} dir={unlockDir} suppressHydrationWarning>
117
+ <html lang={toHreflang(unlockLang)} dir={unlockDir} suppressHydrationWarning>
118
118
  <head>
119
119
  <meta name="viewport" content="width=device-width, initial-scale=1" />
120
120
  <meta name="robots" content="noindex, nofollow" />
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { useEffect } from 'react';
4
4
  import { usePathname } from 'next/navigation';
5
- import { extractLanguageFromPath, isRTLLanguage } from '@/lib/language-utils';
5
+ import { extractLanguageFromPath, isRTLLanguage, toHreflang } from '@/lib/language-utils';
6
6
  import type { LanguageCode } from '@/lib/docs-types';
7
7
 
8
8
  interface HtmlLangSyncProps {
@@ -24,8 +24,9 @@ export function HtmlLangSync({ defaultLanguage }: HtmlLangSyncProps) {
24
24
 
25
25
  useEffect(() => {
26
26
  const lang = extractLanguageFromPath(pathname || '/') ?? defaultLanguage;
27
+ const htmlLang = toHreflang(lang);
27
28
  const html = document.documentElement;
28
- if (html.lang !== lang) html.lang = lang;
29
+ if (html.lang !== htmlLang) html.lang = htmlLang;
29
30
 
30
31
  if (isRTLLanguage(lang)) {
31
32
  if (html.dir !== 'rtl') html.dir = 'rtl';
@@ -28,7 +28,7 @@ export const Accordion = memo(function Accordion({
28
28
  description,
29
29
  defaultOpen = false,
30
30
  icon,
31
- iconType,
31
+ iconType: _iconType,
32
32
  children
33
33
  }: AccordionProps) {
34
34
  const [isOpen, setIsOpen] = useState(defaultOpen);
@@ -206,7 +206,7 @@ export const Card = memo(function Card({ title, icon, img, href, children, horiz
206
206
  role={href ? 'link' : undefined}
207
207
  aria-label={href ? (ariaLabel || (title ? `Learn about ${title}` : undefined)) : undefined}
208
208
  tabIndex={href ? 0 : undefined}
209
- onKeyDown={href ? (e) => { if (e.key === 'Enter' || e.key === ' ') handleClick(e as any); } : undefined}
209
+ onKeyDown={href ? (e) => { if (e.key === 'Enter' || e.key === ' ') handleClick(e as unknown as React.MouseEvent<HTMLDivElement>); } : undefined}
210
210
  >
211
211
  {cardContent}
212
212
  </div>
@@ -3,12 +3,23 @@
3
3
  import { ReactNode, Children, isValidElement, memo } from 'react';
4
4
  import { CodePanel, CodePanelTab } from '../ui/CodePanel';
5
5
  import { formatLanguage } from '@/lib/code-utils';
6
- import { getLanguageIcon } from '@/lib/language-icons';
7
6
 
8
7
  interface CodeGroupProps {
9
8
  children: ReactNode;
10
9
  }
11
10
 
11
+ // Loose shape of `pre`/`code` props from MDX/Shiki — only the fields we read.
12
+ type PreProps = {
13
+ 'data-language'?: string;
14
+ children?: ReactNode;
15
+ };
16
+ type CodeProps = {
17
+ className?: string;
18
+ 'data-title'?: string;
19
+ 'data-meta'?: string;
20
+ meta?: string;
21
+ };
22
+
12
23
  /**
13
24
  * Extract raw language identifier from a pre element
14
25
  * Priority: data-language > className language-*
@@ -16,7 +27,7 @@ interface CodeGroupProps {
16
27
  function getRawLanguage(child: ReactNode): string | undefined {
17
28
  if (!isValidElement(child)) return undefined;
18
29
 
19
- const childProps = child.props as any;
30
+ const childProps = child.props as PreProps;
20
31
 
21
32
  // Check for data-language on the pre element (added by Shiki transformer)
22
33
  const preLanguage = childProps['data-language'];
@@ -25,7 +36,7 @@ function getRawLanguage(child: ReactNode): string | undefined {
25
36
  const codeElement = childProps?.children;
26
37
  if (!isValidElement(codeElement)) return undefined;
27
38
 
28
- const codeProps = codeElement.props as any;
39
+ const codeProps = codeElement.props as CodeProps;
29
40
 
30
41
  // Fall back to language from className (pre-Shiki format)
31
42
  const className = codeProps?.className || '';
@@ -35,22 +46,6 @@ function getRawLanguage(child: ReactNode): string | undefined {
35
46
  return undefined;
36
47
  }
37
48
 
38
- /**
39
- * Extract custom icon from a pre element's code child
40
- * Returns data-icon attribute if present
41
- */
42
- function getCustomIcon(child: ReactNode): string | undefined {
43
- if (!isValidElement(child)) return undefined;
44
-
45
- const childProps = child.props as any;
46
- const codeElement = childProps?.children;
47
-
48
- if (!isValidElement(codeElement)) return undefined;
49
-
50
- const codeProps = codeElement.props as any;
51
- return codeProps['data-icon'];
52
- }
53
-
54
49
  /**
55
50
  * Extract title from a pre element's code child
56
51
  * Returns data-title attribute if present (e.g., title="utils.js")
@@ -58,12 +53,12 @@ function getCustomIcon(child: ReactNode): string | undefined {
58
53
  function getTitle(child: ReactNode): string | undefined {
59
54
  if (!isValidElement(child)) return undefined;
60
55
 
61
- const childProps = child.props as any;
56
+ const childProps = child.props as PreProps;
62
57
  const codeElement = childProps?.children;
63
58
 
64
59
  if (!isValidElement(codeElement)) return undefined;
65
60
 
66
- const codeProps = codeElement.props as any;
61
+ const codeProps = codeElement.props as CodeProps;
67
62
  return codeProps['data-title'];
68
63
  }
69
64
 
@@ -74,7 +69,7 @@ function getTitle(child: ReactNode): string | undefined {
74
69
  function getTabLabel(child: ReactNode): string {
75
70
  if (!isValidElement(child)) return 'Code';
76
71
 
77
- const childProps = child.props as any;
72
+ const childProps = child.props as PreProps;
78
73
 
79
74
  // Check for data-language on the pre element (added by Shiki transformer)
80
75
  const preLanguage = childProps['data-language'];
@@ -85,7 +80,7 @@ function getTabLabel(child: ReactNode): string {
85
80
  const codeElement = childProps?.children;
86
81
  if (!isValidElement(codeElement)) return 'Code';
87
82
 
88
- const codeProps = codeElement.props as any;
83
+ const codeProps = codeElement.props as CodeProps;
89
84
 
90
85
  // Check for title in data-meta or meta props (e.g., "Success Response")
91
86
  const metaString = codeProps['data-meta'] || codeProps.meta || '';
@@ -113,7 +113,6 @@ export function ColorItem({ name, value }: ColorItemProps) {
113
113
 
114
114
  const swatchColor = isThemeAware ? 'var(--color-swatch-light)' : value;
115
115
  const lightTextOnLight = isThemeAware ? shouldUseLightText(value.light) : shouldUseLightText(value as string);
116
- const lightTextOnDark = isThemeAware ? shouldUseLightText(value.dark) : lightTextOnLight;
117
116
 
118
117
  if (variant === 'table') {
119
118
  return (
@@ -77,7 +77,7 @@ function getColorStyle(color?: string): { style?: React.CSSProperties; className
77
77
  * <Icon icon={<svg viewBox="0 0 24 24"><path d="..."/></svg>} />
78
78
  *
79
79
  */
80
- export function Icon({ icon, iconType = 'solid', color, size = 16, className }: IconProps) {
80
+ export function Icon({ icon, iconType: _iconType = 'solid', color, size = 16, className }: IconProps) {
81
81
  const { style: colorStyle, className: colorClass } = getColorStyle(color);
82
82
  const combinedClassName = `inline-block ${colorClass || ''} ${className || ''}`.trim();
83
83
  const sizeStyle = { width: size, height: size };
@@ -1,4 +1,15 @@
1
- import { isValidElement, type ReactNode } from 'react';
1
+ import {
2
+ isValidElement,
3
+ type AnchorHTMLAttributes,
4
+ type BlockquoteHTMLAttributes,
5
+ type HTMLAttributes,
6
+ type ImgHTMLAttributes,
7
+ type OlHTMLAttributes,
8
+ type ReactNode,
9
+ type TableHTMLAttributes,
10
+ type TdHTMLAttributes,
11
+ type ThHTMLAttributes,
12
+ } from 'react';
2
13
  import { Card } from './Card';
3
14
  import { Note, Info, Warning, Tip, Check, Danger, Callout } from './Callouts';
4
15
  import { Accordion, AccordionGroup } from './Accordion';
@@ -41,14 +52,20 @@ import { Visibility } from './Visibility';
41
52
  * 2. language-* class on code element (standard markdown)
42
53
  * 3. language-* class on pre element (fallback for edge cases)
43
54
  */
44
- function getCodeLanguage(preProps: any, children: ReactNode): string {
55
+ type PreLikeProps = {
56
+ 'data-language'?: string;
57
+ className?: string;
58
+ };
59
+ type CodeLikeProps = { className?: string };
60
+
61
+ function getCodeLanguage(preProps: PreLikeProps, children: ReactNode): string {
45
62
  // Check data-language on pre element (added by Shiki transformer)
46
63
  if (preProps['data-language']) {
47
64
  return preProps['data-language'];
48
65
  }
49
66
  // Check code element's className for language-* class
50
67
  if (isValidElement(children)) {
51
- const codeProps = children.props as any;
68
+ const codeProps = children.props as CodeLikeProps;
52
69
  const className = codeProps?.className || '';
53
70
  const match = className.match(/language-(\w+)/);
54
71
  if (match) return match[1];
@@ -70,7 +87,7 @@ function extractTextFromChildren(children: ReactNode): string {
70
87
  if (!children) return '';
71
88
 
72
89
  if (isValidElement(children)) {
73
- const props = children.props as any;
90
+ const props = children.props as { children?: ReactNode };
74
91
  return extractTextFromChildren(props?.children);
75
92
  }
76
93
 
@@ -239,9 +256,9 @@ export const MDXComponents = {
239
256
  // Sized images from preprocess-mdx (![alt](url =WIDTHx) syntax).
240
257
  // These are output as <SizedImage> JSX so they go through component mapping
241
258
  // (raw <img> JSX in MDX bypasses the components provider).
242
- SizedImage: ({ src, alt, width, height, className, ...rest }: any) => (
259
+ SizedImage: ({ src, alt, width, height, className, ..._rest }: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src'> & { src?: string }) => (
243
260
  <ZoomableImage
244
- src={src}
261
+ src={src ?? ''}
245
262
  alt={alt || ''}
246
263
  width={width}
247
264
  height={height}
@@ -269,11 +286,20 @@ export const MDXComponents = {
269
286
  noStyle, // Explicitly destructure to prevent React warning about unknown DOM prop
270
287
  nostyle, // Also handle lowercase version from HTML parsing
271
288
  // Destructure and ignore any other props to prevent them from being passed to DOM
272
- ...rest
273
- }: any) => {
289
+ ..._rest
290
+ }: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src'> & {
291
+ src?: string;
292
+ class?: string;
293
+ 'data-no-zoom'?: string | boolean;
294
+ 'data-no-style'?: string | boolean;
295
+ noZoom?: string | boolean;
296
+ nozoom?: string | boolean;
297
+ noStyle?: string | boolean;
298
+ nostyle?: string | boolean;
299
+ }) => {
274
300
  // Check if this is a video file — render <Video> instead of <ZoomableImage>
275
301
  if (isVideoUrl(src)) {
276
- return <Video src={src} title={alt || undefined} />;
302
+ return <Video src={src ?? ''} title={alt || undefined} />;
277
303
  }
278
304
 
279
305
  // Check for data-no-zoom attribute or noZoom prop (handle both camelCase and lowercase)
@@ -295,7 +321,7 @@ export const MDXComponents = {
295
321
 
296
322
  return (
297
323
  <ZoomableImage
298
- src={src}
324
+ src={src ?? ''}
299
325
  alt={alt || ''}
300
326
  width={width}
301
327
  height={height}
@@ -308,64 +334,64 @@ export const MDXComponents = {
308
334
  />
309
335
  );
310
336
  },
311
- h1: (props: any) => (
312
- <h1
313
- className="text-3xl font-bold text-theme-text-primary mt-8 mb-4 tracking-tight"
314
- {...props}
337
+ h1: (props: HTMLAttributes<HTMLHeadingElement>) => (
338
+ <h1
339
+ className="text-3xl font-bold text-theme-text-primary mt-8 mb-4 tracking-tight"
340
+ {...props}
315
341
  />
316
342
  ),
317
- h2: (props: any) => (
318
- <h2
319
- className="text-2xl font-semibold text-theme-text-primary mt-10 mb-4 tracking-tight scroll-mt-20"
320
- {...props}
343
+ h2: (props: HTMLAttributes<HTMLHeadingElement>) => (
344
+ <h2
345
+ className="text-2xl font-semibold text-theme-text-primary mt-10 mb-4 tracking-tight scroll-mt-20"
346
+ {...props}
321
347
  />
322
348
  ),
323
- h3: (props: any) => (
324
- <h3
325
- className="text-xl font-semibold text-theme-text-primary mt-8 mb-3 scroll-mt-20"
326
- {...props}
349
+ h3: (props: HTMLAttributes<HTMLHeadingElement>) => (
350
+ <h3
351
+ className="text-xl font-semibold text-theme-text-primary mt-8 mb-3 scroll-mt-20"
352
+ {...props}
327
353
  />
328
354
  ),
329
- h4: (props: any) => (
330
- <h4
331
- className="text-lg font-semibold text-theme-text-primary mt-6 mb-2 scroll-mt-20"
332
- {...props}
355
+ h4: (props: HTMLAttributes<HTMLHeadingElement>) => (
356
+ <h4
357
+ className="text-lg font-semibold text-theme-text-primary mt-6 mb-2 scroll-mt-20"
358
+ {...props}
333
359
  />
334
360
  ),
335
- p: (props: any) => (
336
- <p
337
- className="text-theme-text-secondary leading-7 mb-4"
338
- {...props}
361
+ p: (props: HTMLAttributes<HTMLParagraphElement>) => (
362
+ <p
363
+ className="text-theme-text-secondary leading-7 mb-4"
364
+ {...props}
339
365
  />
340
366
  ),
341
- ul: ({ class: htmlClass, className, ...props }: any) => (
342
- <ul
343
- className={`list-disc list-inside text-theme-text-secondary space-y-2 mb-4 ml-4 ${htmlClass || ''} ${className || ''}`.trim()}
344
- {...props}
367
+ ul: ({ class: htmlClass, className, ...props }: HTMLAttributes<HTMLUListElement> & { class?: string }) => (
368
+ <ul
369
+ className={`list-disc list-inside text-theme-text-secondary space-y-2 mb-4 ml-4 ${htmlClass || ''} ${className || ''}`.trim()}
370
+ {...props}
345
371
  />
346
372
  ),
347
- ol: ({ class: htmlClass, className, ...props }: any) => (
348
- <ol
349
- className={`list-decimal list-inside text-theme-text-secondary space-y-2 mb-4 ml-4 ${htmlClass || ''} ${className || ''}`.trim()}
350
- {...props}
373
+ ol: ({ class: htmlClass, className, ...props }: OlHTMLAttributes<HTMLOListElement> & { class?: string }) => (
374
+ <ol
375
+ className={`list-decimal list-inside text-theme-text-secondary space-y-2 mb-4 ml-4 ${htmlClass || ''} ${className || ''}`.trim()}
376
+ {...props}
351
377
  />
352
378
  ),
353
- li: (props: any) => (
354
- <li
355
- className="text-theme-text-secondary marker:text-theme-marker"
356
- {...props}
379
+ li: (props: HTMLAttributes<HTMLLIElement>) => (
380
+ <li
381
+ className="text-theme-text-secondary marker:text-theme-marker"
382
+ {...props}
357
383
  />
358
384
  ),
359
385
  // NOTE: page.tsx overrides this for hostAtDocs sites to auto-prefix links.
360
386
  // If you change styling here, update the override in app/[[...slug]]/page.tsx too.
361
- a: ({ ariaLabel, ...props }: any) => (
387
+ a: ({ ariaLabel, ...props }: AnchorHTMLAttributes<HTMLAnchorElement> & { ariaLabel?: string }) => (
362
388
  <a
363
389
  className="text-theme-accent hover:text-theme-accent-hover transition-colors"
364
390
  aria-label={ariaLabel}
365
391
  {...props}
366
392
  />
367
393
  ),
368
- blockquote: (props: any) => (
394
+ blockquote: (props: BlockquoteHTMLAttributes<HTMLQuoteElement>) => (
369
395
  <blockquote
370
396
  className="border-l-4 pl-4 text-theme-text-tertiary my-6 not-italic"
371
397
  style={{ borderColor: 'var(--color-border)' }}
@@ -373,7 +399,7 @@ export const MDXComponents = {
373
399
  />
374
400
  ),
375
401
  // Custom pre component to wrap code blocks with titles in CodePanel
376
- pre: ({ children, 'data-title': dataTitle, ...props }: any) => {
402
+ pre: ({ children, 'data-title': dataTitle, ...props }: HTMLAttributes<HTMLPreElement> & { 'data-title'?: string }) => {
377
403
  const language = getCodeLanguage(props, children);
378
404
 
379
405
  // Check for mermaid diagrams - render with Mermaid component
@@ -411,41 +437,41 @@ export const MDXComponents = {
411
437
  );
412
438
  },
413
439
  // Inline code styling is done via CSS in base.css (.prose :not(pre) > code)
414
- table: (props: any) => (
440
+ table: (props: TableHTMLAttributes<HTMLTableElement>) => (
415
441
  <div className="overflow-x-auto my-6">
416
- <table
417
- className="w-full text-sm border-collapse"
418
- {...props}
442
+ <table
443
+ className="w-full text-sm border-collapse"
444
+ {...props}
419
445
  />
420
446
  </div>
421
447
  ),
422
- th: (props: any) => (
423
- <th
424
- className="text-left font-semibold text-theme-text-primary p-3 border-b-2 border-theme-border bg-theme-bg-secondary"
425
- {...props}
448
+ th: (props: ThHTMLAttributes<HTMLTableHeaderCellElement>) => (
449
+ <th
450
+ className="text-left font-semibold text-theme-text-primary p-3 border-b-2 border-theme-border bg-theme-bg-secondary"
451
+ {...props}
426
452
  />
427
453
  ),
428
- td: (props: any) => (
429
- <td
430
- className="p-3 border-b border-theme-border text-theme-text-secondary"
431
- {...props}
454
+ td: (props: TdHTMLAttributes<HTMLTableDataCellElement>) => (
455
+ <td
456
+ className="p-3 border-b border-theme-border text-theme-text-secondary"
457
+ {...props}
432
458
  />
433
459
  ),
434
- hr: (props: any) => (
435
- <hr
436
- className="border-theme-border my-8"
437
- {...props}
460
+ hr: (props: HTMLAttributes<HTMLHRElement>) => (
461
+ <hr
462
+ className="border-theme-border my-8"
463
+ {...props}
438
464
  />
439
465
  ),
440
466
  // Generic div handler to convert class to className
441
- div: ({ class: htmlClass, className, ...props }: any) => (
442
- <div
443
- className={`${htmlClass || ''} ${className || ''}`.trim() || undefined}
444
- {...props}
467
+ div: ({ class: htmlClass, className, ...props }: HTMLAttributes<HTMLDivElement> & { class?: string }) => (
468
+ <div
469
+ className={`${htmlClass || ''} ${className || ''}`.trim() || undefined}
470
+ {...props}
445
471
  />
446
472
  ),
447
473
  // Generic span handler to convert class to className
448
- span: ({ class: htmlClass, className, ...props }: any) => (
474
+ span: ({ class: htmlClass, className, ...props }: HTMLAttributes<HTMLSpanElement> & { class?: string }) => (
449
475
  <span
450
476
  className={`${htmlClass || ''} ${className || ''}`.trim() || undefined}
451
477
  {...props}
@@ -77,7 +77,6 @@ function renderSchemaType(schema: JsonSchema): string {
77
77
  // Shiki HTML comes from server-side highlighting of trusted code strings;
78
78
  // sanitization is unnecessary and would break the syntax-color spans.
79
79
  function renderHighlightedHtml(html: string) {
80
- // eslint-disable-next-line react/no-danger
81
80
  return <div dangerouslySetInnerHTML={{ __html: html }} />;
82
81
  }
83
82
 
@@ -30,7 +30,6 @@ export function ParamField({
30
30
  }: ParamFieldProps) {
31
31
  // Determine the parameter name from the prop
32
32
  const paramName = body || header || query || path || '';
33
- const location = body ? 'body' : header ? 'header' : query ? 'query' : path ? 'path' : '';
34
33
 
35
34
  return (
36
35
  <div className="group param-divider py-2.5 first:pt-0 not-prose">
@@ -3,7 +3,6 @@
3
3
  import { ReactNode, Children, isValidElement } from 'react';
4
4
  import { CodePanel, CodePanelTab } from '../ui/CodePanel';
5
5
  import { formatLanguage, getElementProps } from '@/lib/code-utils';
6
- import { getLanguageIcon } from '@/lib/language-icons';
7
6
 
8
7
  interface RequestExampleProps {
9
8
  children: ReactNode;
@@ -3,7 +3,6 @@
3
3
  import { ReactNode, Children, isValidElement } from 'react';
4
4
  import { CodePanel, CodePanelTab } from '../ui/CodePanel';
5
5
  import { getElementProps } from '@/lib/code-utils';
6
- import { getLanguageIcon } from '@/lib/language-icons';
7
6
 
8
7
  interface ResponseExampleProps {
9
8
  children: ReactNode;
@@ -1,6 +1,14 @@
1
1
  'use client';
2
2
 
3
- import { ReactNode, Children, Fragment, cloneElement, isValidElement, memo } from 'react';
3
+ import {
4
+ ReactNode,
5
+ Children,
6
+ Fragment,
7
+ cloneElement,
8
+ isValidElement,
9
+ memo,
10
+ type ReactElement,
11
+ } from 'react';
4
12
  import { getIconClass } from '@/lib/icon-utils';
5
13
  import { useStepSlug } from './StepSlugContext';
6
14
 
@@ -56,10 +64,11 @@ export const Steps = memo(function Steps({ children, titleSize = 'p' }: StepsPro
56
64
  <div className="relative my-8 space-y-0 not-prose">
57
65
  {childrenArray.map((child) => {
58
66
  if (isStepComponent(child)) {
59
- const childProps = (child as any).props as StepProps;
67
+ const stepEl = child as ReactElement<StepProps>;
68
+ const childProps = stepEl.props;
60
69
  const currentStepIndex = stepIndex;
61
70
  stepIndex++;
62
- return cloneElement(child as any, {
71
+ return cloneElement(stepEl, {
63
72
  ...childProps,
64
73
  stepNumber: childProps.stepNumber ?? currentStepIndex + 1,
65
74
  isLast: currentStepIndex === totalSteps - 1,
@@ -170,7 +170,10 @@ export function Table({
170
170
  <tbody>
171
171
  {sortedBodyRows.map((row, index) => {
172
172
  if (isValidElement(row)) {
173
- return cloneElement(row, { key: row.key ?? index, _rowIndex: index } as any);
173
+ return cloneElement(
174
+ row as React.ReactElement<InternalRowProps>,
175
+ { key: row.key ?? index, _rowIndex: index }
176
+ );
174
177
  }
175
178
  return row;
176
179
  })}
@@ -223,7 +226,10 @@ export function Row({
223
226
  <tr className={classes.join(' ')}>
224
227
  {Children.map(children, (child, index) => {
225
228
  if (isValidElement(child)) {
226
- return cloneElement(child, { _isHeaderRow: header, _columnIndex: index } as any);
229
+ return cloneElement(
230
+ child as React.ReactElement<InternalCellProps>,
231
+ { _isHeaderRow: header, _columnIndex: index }
232
+ );
227
233
  }
228
234
  return child;
229
235
  })}
@@ -34,7 +34,7 @@ interface TabProps {
34
34
  * Tab component - represents a single tab panel
35
35
  * Must be used inside a Tabs component
36
36
  */
37
- export const Tab = memo(function Tab({ title, icon, iconType, children }: TabProps) {
37
+ export const Tab = memo(function Tab({ title: _title, icon: _icon, iconType: _iconType, children }: TabProps) {
38
38
  // This component is rendered by Tabs, not directly
39
39
  // The props are extracted by Tabs to build the tab bar
40
40
  return <>{children}</>;
@@ -10,8 +10,6 @@ import {
10
10
  useMemo,
11
11
  useId,
12
12
  ReactNode,
13
- Children,
14
- isValidElement,
15
13
  } from 'react';
16
14
 
17
15
  // =============================================================================
@@ -158,7 +156,11 @@ function TreeRoot({ children }: TreeProps) {
158
156
 
159
157
  traverse(null);
160
158
  return items;
161
- }, [expandedIds, itemsRef.current.size]); // eslint-disable-line react-hooks/exhaustive-deps
159
+ // itemsRef.current.size is intentional — refs don't trigger re-renders, but
160
+ // size changes correlate with register/unregister so this is the best
161
+ // available recompute signal without lifting items into state.
162
+ // eslint-disable-next-line react-hooks/exhaustive-deps
163
+ }, [expandedIds, itemsRef.current.size]);
162
164
 
163
165
  // Initialize expanded state from defaultOpen props
164
166
  useEffect(() => {
@@ -174,7 +176,7 @@ function TreeRoot({ children }: TreeProps) {
174
176
  }
175
177
  setInitialized(true);
176
178
  }
177
- }, [initialized, itemsRef.current.size]); // eslint-disable-line react-hooks/exhaustive-deps
179
+ }, [initialized, itemsRef.current.size]);
178
180
 
179
181
  // Type-ahead search handler
180
182
  const handleTypeAhead = useCallback(