jamdesk 1.1.34 → 1.1.37

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.
@@ -275,6 +275,23 @@ describe('docs-config', () => {
275
275
  expect(paths).toContain('parent-page');
276
276
  expect(paths).toContain('child-page');
277
277
  });
278
+ it('extracts paths from navigation.languages[] (per-language sub-navs)', () => {
279
+ // jamdesk-docs ships with this shape; broken-links used to report
280
+ // "checked 0 pages" because the walker skipped languages.
281
+ const config = {
282
+ name: 'Test',
283
+ navigation: {
284
+ languages: [
285
+ { language: 'en', tabs: [{ tab: 'Docs', pages: ['en/intro'] }] },
286
+ { language: 'fr', tabs: [{ tab: 'Docs', pages: ['fr/intro'] }] },
287
+ ],
288
+ },
289
+ };
290
+ const paths = getAllPagePaths(config);
291
+ expect(paths).toContain('en/intro');
292
+ expect(paths).toContain('fr/intro');
293
+ expect(paths).toHaveLength(2);
294
+ });
278
295
  it('returns empty array for empty navigation', () => {
279
296
  const config = {
280
297
  name: 'Test',
@@ -1 +1 @@
1
- {"version":3,"file":"docs-config.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/docs-config.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"docs-config.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/docs-config.test.ts"],"names":[],"mappings":"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"}
@@ -61,7 +61,10 @@ export declare function validateConfig(configPath: string): Promise<ValidationRe
61
61
  */
62
62
  export declare function loadDocsConfig(configPath: string): Promise<DocsConfig | null>;
63
63
  /**
64
- * Get all page paths from navigation config
64
+ * Get all page paths from navigation config.
65
+ * Mirrors the recursive container walker in validate-links.cjs so configs
66
+ * wrapped in `navigation.languages[]` (per-language sub-navs) enumerate the
67
+ * same set of pages the build-time link checker resolves.
65
68
  */
66
69
  export declare function getAllPagePaths(config: DocsConfig): string[];
67
70
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"docs-config.d.ts","sourceRoot":"","sources":["../../src/lib/docs-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAkBvC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACpC,UAAU,EAAE,gBAAgB,CAAC;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,eAAe,CAAC,EAAE,CAAC;IACrC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAKtC;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgCxD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAQvD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAwCvF;AA8ED;;GAEG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA0FlF;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAGnF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CA4B5D"}
1
+ {"version":3,"file":"docs-config.d.ts","sourceRoot":"","sources":["../../src/lib/docs-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAkBvC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACpC,UAAU,EAAE,gBAAgB,CAAC;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,eAAe,CAAC,EAAE,CAAC;IACrC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAKtC;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgCxD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAQvD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAwCvF;AA8ED;;GAEG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA0FlF;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAGnF;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CA0B5D"}
@@ -264,35 +264,39 @@ export async function loadDocsConfig(configPath) {
264
264
  return result.valid ? (result.config ?? null) : null;
265
265
  }
266
266
  /**
267
- * Get all page paths from navigation config
267
+ * Get all page paths from navigation config.
268
+ * Mirrors the recursive container walker in validate-links.cjs so configs
269
+ * wrapped in `navigation.languages[]` (per-language sub-navs) enumerate the
270
+ * same set of pages the build-time link checker resolves.
268
271
  */
269
272
  export function getAllPagePaths(config) {
270
273
  const paths = [];
271
- function extractPaths(items) {
272
- if (!items)
274
+ function walkContainer(c) {
275
+ if (!c || typeof c !== 'object')
273
276
  return;
274
- if (Array.isArray(items)) {
275
- for (const item of items) {
276
- if (typeof item === 'string') {
277
- paths.push(item);
278
- }
279
- else if (typeof item === 'object' && item !== null) {
280
- const obj = item;
281
- if (obj.pages)
282
- extractPaths(obj.pages);
283
- if (obj.groups)
284
- extractPaths(obj.groups);
285
- }
286
- }
277
+ const obj = c;
278
+ if (typeof obj.page === 'string')
279
+ paths.push(obj.page);
280
+ walkItems(obj.pages);
281
+ walkItems(obj.groups);
282
+ walkItems(obj.tabs);
283
+ walkItems(obj.anchors);
284
+ walkItems(obj.dropdowns);
285
+ walkItems(obj.languages);
286
+ if (obj.global)
287
+ walkContainer(obj.global);
288
+ }
289
+ function walkItems(items) {
290
+ if (!Array.isArray(items))
291
+ return;
292
+ for (const item of items) {
293
+ if (typeof item === 'string')
294
+ paths.push(item);
295
+ else if (item && typeof item === 'object')
296
+ walkContainer(item);
287
297
  }
288
298
  }
289
- const nav = config.navigation;
290
- extractPaths(nav.anchors);
291
- extractPaths(nav.tabs);
292
- extractPaths(nav.groups);
293
- extractPaths(nav.pages);
294
- extractPaths(nav.products);
295
- extractPaths(nav.dropdowns);
299
+ walkContainer(config.navigation);
296
300
  return paths;
297
301
  }
298
302
  //# sourceMappingURL=docs-config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"docs-config.js","sourceRoot":"","sources":["../../src/lib/docs-config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"docs-config.js","sourceRoot":"","sources":["../../src/lib/docs-config.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jamdesk",
3
- "version": "1.1.34",
3
+ "version": "1.1.37",
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",
@@ -8,10 +8,17 @@ import { CodeBlockCopyButton } from '@/components/CodeBlockCopyButton';
8
8
  import { HeaderLinkCopy } from '@/components/HeaderLinkCopy';
9
9
  import { FontAwesomeLoader } from '@/components/FontAwesomeLoader';
10
10
  import { JdReadySentinel } from '@/components/JdReadySentinel';
11
+ import { HtmlLangSync } from '@/components/HtmlLangSync';
11
12
  import { FA_CSS_HREF } from '@/lib/font-awesome';
12
13
  import { getDocsConfig } from '@/lib/docs';
13
14
  import { getDocsConfig as getIsrDocsConfig } from '@/lib/docs-isr';
14
- import { isIsrMode, getProjectFromRequest, getHostAtDocs } from '@/lib/page-isr-helpers';
15
+ import {
16
+ isIsrMode,
17
+ getProjectFromRequest,
18
+ getHostAtDocs,
19
+ getLanguageFromRequest,
20
+ } from '@/lib/page-isr-helpers';
21
+ import { isRTLLanguage, resolveLanguageWithFallback } from '@/lib/language-utils';
15
22
  import { getTheme, type ThemeName } from '@/themes';
16
23
  import { generateFontImports, generateFontVariables, isPreloadedFont, getPrimaryFontFamily } from '@/lib/fonts';
17
24
  import fs from 'fs';
@@ -269,8 +276,17 @@ export default async function RootLayout({
269
276
  // fetch. The unlock page owns its own visuals.
270
277
  const headersList = await headers();
271
278
  if (headersList.get('x-jd-unlock-mode') === '1') {
279
+ // Unlock mode short-circuits before we fetch project config, so we
280
+ // can't consult `config.navigation.languages` here. The middleware
281
+ // already extracts language from the original `from` path
282
+ // (proxy.ts), so the header is our only signal — fall back to "en".
283
+ const unlockLang = resolveLanguageWithFallback(
284
+ getLanguageFromRequest(headersList),
285
+ undefined,
286
+ );
287
+ const unlockDir = isRTLLanguage(unlockLang) ? 'rtl' : undefined;
272
288
  return (
273
- <html lang="en">
289
+ <html lang={unlockLang} dir={unlockDir}>
274
290
  <head>
275
291
  <meta name="viewport" content="width=device-width, initial-scale=1" />
276
292
  <meta name="robots" content="noindex, nofollow" />
@@ -371,8 +387,14 @@ export default async function RootLayout({
371
387
  ? getAnalyticsScript(resolvedProjectSlug)
372
388
  : null;
373
389
 
390
+ // Project default — what `lang` resolves to when no path locale is present.
391
+ // HtmlLangSync uses it on soft-nav back to a non-localized path (e.g. `/`).
392
+ const projectDefaultLang = resolveLanguageWithFallback(null, config.navigation?.languages);
393
+ const lang = getLanguageFromRequest(headersList) ?? projectDefaultLang;
394
+ const dir = isRTLLanguage(lang) ? 'rtl' : undefined;
395
+
374
396
  return (
375
- <html lang="en" suppressHydrationWarning data-scroll-behavior="smooth">
397
+ <html lang={lang} dir={dir} suppressHydrationWarning data-scroll-behavior="smooth">
376
398
  <head>
377
399
  {/* Add viewport meta for mobile */}
378
400
  <meta name="viewport" content="width=device-width, initial-scale=1" />
@@ -559,6 +581,7 @@ export default async function RootLayout({
559
581
  <CodeBlockCopyButton />
560
582
  <HeaderLinkCopy />
561
583
  <FontAwesomeLoader />
584
+ <HtmlLangSync defaultLanguage={projectDefaultLang} />
562
585
  </ThemeProvider>
563
586
  {/* Crisp Chat */}
564
587
  {config.integrations?.crisp?.websiteId &&
@@ -0,0 +1,38 @@
1
+ 'use client';
2
+
3
+ import { useEffect } from 'react';
4
+ import { usePathname } from 'next/navigation';
5
+ import { extractLanguageFromPath, isRTLLanguage } from '@/lib/language-utils';
6
+ import type { LanguageCode } from '@/lib/docs-types';
7
+
8
+ interface HtmlLangSyncProps {
9
+ /** Project's resolved default language — used when the path has no locale prefix. */
10
+ defaultLanguage: LanguageCode;
11
+ }
12
+
13
+ /**
14
+ * Keeps `<html lang>` and `<html dir>` in sync with the current pathname
15
+ * across client-side navigations.
16
+ *
17
+ * The root layout only renders server-side on the initial request. Soft
18
+ * navigations (`router.push`, `<Link>`) leave the html attributes stale at
19
+ * the value the server emitted, so switching locale via the dropdown left
20
+ * `lang` pointing at the previous language until a hard refresh.
21
+ */
22
+ export function HtmlLangSync({ defaultLanguage }: HtmlLangSyncProps) {
23
+ const pathname = usePathname();
24
+
25
+ useEffect(() => {
26
+ const lang = extractLanguageFromPath(pathname || '/') ?? defaultLanguage;
27
+ const html = document.documentElement;
28
+ if (html.lang !== lang) html.lang = lang;
29
+
30
+ if (isRTLLanguage(lang)) {
31
+ if (html.dir !== 'rtl') html.dir = 'rtl';
32
+ } else if (html.dir) {
33
+ html.removeAttribute('dir');
34
+ }
35
+ }, [pathname, defaultLanguage]);
36
+
37
+ return null;
38
+ }
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { ReactNode, Children, cloneElement, isValidElement, memo } from 'react';
4
4
  import { getIconClass } from '@/lib/icon-utils';
5
+ import { generateSlug } from '@/lib/heading-extractor';
5
6
 
6
7
  type TitleSize = 'p' | 'h2' | 'h3';
7
8
  type IconType = 'regular' | 'solid' | 'light' | 'thin' | 'sharp-solid' | 'duotone' | 'brands';
@@ -129,9 +130,24 @@ function StepTitle({ title, titleSize }: { title: string; titleSize: TitleSize }
129
130
 
130
131
  export const Step = memo(function Step({ title, children, stepNumber, isLast, icon, iconType, titleSize = 'p' }: StepProps) {
131
132
  const containerClassName = isLast ? 'relative pb-0' : 'relative pb-8';
133
+ const slug = title ? generateSlug(title) || undefined : undefined;
134
+
135
+ // Emit both attrs together or neither — the DOM scanner keys off
136
+ // `data-step-number`, so a label without a number would be a dangling
137
+ // attribute, and a number without a label would produce a TOC entry with
138
+ // empty text.
139
+ const stepAttrs: { 'data-step-number'?: number; 'data-step-label'?: string } = {};
140
+ if (typeof stepNumber === 'number' && title) {
141
+ stepAttrs['data-step-number'] = stepNumber;
142
+ stepAttrs['data-step-label'] = title;
143
+ }
132
144
 
133
145
  return (
134
- <div className={containerClassName}>
146
+ <div
147
+ className={containerClassName}
148
+ id={slug}
149
+ {...stepAttrs}
150
+ >
135
151
  {/* Vertical line connecting to next step - positioned absolutely */}
136
152
  {!isLast && (
137
153
  <div
@@ -164,28 +164,25 @@ export function LanguageSelector({
164
164
  ${compact ? 'px-2 py-1.5' : 'px-3 py-2 w-full'}
165
165
  `}
166
166
  >
167
- <span className="text-base" role="img" aria-label={currentLanguage?.displayName}>
168
- {currentLanguage?.flag}
169
- </span>
170
- {!compact && (
171
- <>
172
- <span className="flex-1 text-left text-sm font-medium truncate">
173
- {currentLanguage?.displayName}
174
- </span>
175
- <i
176
- className={`fa-solid fa-chevron-${position === 'up' ? 'up' : 'down'} h-3 w-3 transition-transform ${
177
- isOpen ? (position === 'up' ? 'rotate-180' : 'rotate-180') : ''
178
- }`}
179
- aria-hidden="true"
180
- />
181
- </>
182
- )}
183
- {compact && (
184
- <i
185
- className={`fa-solid fa-chevron-down h-3 w-3 transition-transform ${isOpen ? 'rotate-180' : ''}`}
186
- aria-hidden="true"
187
- />
167
+ <i
168
+ className="fa-solid fa-globe text-[12px] text-[var(--color-text-tertiary)]"
169
+ aria-hidden="true"
170
+ />
171
+ {compact ? (
172
+ <span className="text-xs font-semibold tracking-wide">
173
+ {currentLanguage?.code.split('-')[0].toUpperCase()}
174
+ </span>
175
+ ) : (
176
+ <span className="flex-1 text-left text-sm font-medium truncate">
177
+ {currentLanguage?.displayName}
178
+ </span>
188
179
  )}
180
+ <i
181
+ className={`fa-solid fa-chevron-${position} text-[10px] text-[var(--color-text-tertiary)] transition-transform ${
182
+ isOpen ? 'rotate-180' : ''
183
+ }`}
184
+ aria-hidden="true"
185
+ />
189
186
  </button>
190
187
 
191
188
  {/* Dropdown menu */}
@@ -2,12 +2,13 @@
2
2
 
3
3
  import { useEffect, useState, useRef, useCallback } from 'react';
4
4
  import { getIconClass } from '@/lib/icon-utils';
5
- import { generateSlug } from '@/lib/heading-extractor';
5
+ import { extractHeadings } from '@/lib/heading-extractor';
6
6
 
7
7
  interface TocItem {
8
8
  id: string;
9
9
  text: string;
10
10
  level: number;
11
+ stepNumber?: number;
11
12
  }
12
13
 
13
14
  interface TableOfContentsProps {
@@ -196,75 +197,128 @@ export function TableOfContents({ content, className = '' }: TableOfContentsProp
196
197
  return () => observer.disconnect();
197
198
  }, [updateThumb]);
198
199
 
199
- // Parse headings from content in source order, then scan DOM for dynamic components
200
+ // Source-order parse gives immediate headings; the MutationObserver below
201
+ // keeps the list in sync when dynamic components (Accordion, Tabs) reveal
202
+ // Steps after initial render.
200
203
  useEffect(() => {
201
- // Line-by-line pass to preserve source order and skip fenced code blocks
202
- const items: TocItem[] = [];
203
- const lines = content.split('\n');
204
- let inCodeBlock = false;
205
- let fencePattern = '';
206
-
207
- for (const line of lines) {
208
- const fenceMatch = line.match(/^(`{3,}|~{3,})/);
209
- if (fenceMatch) {
210
- if (!inCodeBlock) {
211
- inCodeBlock = true;
212
- fencePattern = fenceMatch[1];
213
- continue;
214
- } else if (line.startsWith(fencePattern)) {
215
- inCodeBlock = false;
216
- fencePattern = '';
217
- continue;
218
- }
219
- }
220
- if (inCodeBlock) continue;
221
-
222
- const headingMatch = line.match(/^(#{2,3})\s+(.+)$/);
223
- if (headingMatch) {
224
- const level = headingMatch[1].length;
225
- const text = headingMatch[2].trim();
226
- const id = generateSlug(text);
227
- if (id) items.push({ id, text, level });
228
- }
204
+ const sourceItems: TocItem[] = extractHeadings(content)
205
+ .filter(h => h.level === 2 || h.level === 3)
206
+ .map(h => ({
207
+ id: h.id,
208
+ text: h.text,
209
+ level: h.level,
210
+ stepNumber: h.stepNumber,
211
+ }));
229
212
 
230
- const updateMatch = line.match(/<Update\s+label=["']([^"']+)["']/);
231
- if (updateMatch) {
232
- const text = updateMatch[1];
233
- const id = generateSlug(text);
234
- if (id) items.push({ id, text, level: 2 });
235
- }
236
- }
237
-
238
- setHeadings(items);
213
+ setHeadings(sourceItems);
239
214
 
240
- // DOM scan: single query to get all TOC elements in document order
241
215
  const scanDomHeadings = () => {
242
- const tocElements = document.querySelectorAll('main h2[id], main h3[id], main [data-update-label]');
216
+ const tocElements = document.querySelectorAll<HTMLElement>(
217
+ 'main h2[id], main h3[id], main [data-update-label], main [data-step-number]',
218
+ );
243
219
  const newItems: TocItem[] = [];
244
220
  const seenDomIds = new Set<string>();
221
+ const collisions: string[] = [];
245
222
 
246
223
  tocElements.forEach((element) => {
247
224
  const id = element.id;
248
- if (!id || seenDomIds.has(id)) return;
225
+ if (!id) return;
226
+ if (seenDomIds.has(id)) {
227
+ collisions.push(id);
228
+ return;
229
+ }
230
+
231
+ const stepNumberAttr = element.getAttribute('data-step-number');
232
+ const isStep = stepNumberAttr !== null && stepNumberAttr !== '';
233
+ const isUpdate = !isStep && element.hasAttribute('data-update-label');
234
+
235
+ let text = '';
236
+ let level = 2;
237
+ let stepNumber: number | undefined;
238
+
239
+ if (isStep) {
240
+ text = element.getAttribute('data-step-label')?.trim() || '';
241
+ level = 3;
242
+ const parsed = parseInt(stepNumberAttr as string, 10);
243
+ stepNumber = Number.isFinite(parsed) ? parsed : undefined;
244
+ } else if (isUpdate) {
245
+ text = element.getAttribute('data-update-label')?.trim() || '';
246
+ level = 2;
247
+ } else {
248
+ text = element.textContent?.trim() || '';
249
+ level = element.tagName === 'H2' ? 2 : 3;
250
+ }
249
251
 
250
- const isUpdate = element.hasAttribute('data-update-label');
251
- const text = isUpdate
252
- ? (element.getAttribute('data-update-label') || '')
253
- : (element.textContent?.trim() || '');
254
252
  if (!text) return;
255
253
 
256
254
  seenDomIds.add(id);
257
- const level = isUpdate ? 2 : (element.tagName === 'H2' ? 2 : 3);
258
- newItems.push({ id, text, level });
255
+ newItems.push({ id, text, level, stepNumber });
259
256
  });
260
257
 
261
- if (newItems.length > 0) {
262
- setHeadings(newItems);
258
+ // Dev-mode warning for slug collisions. In production builds, stay
259
+ // silent — browsers still render correctly (first match wins).
260
+ if (collisions.length > 0 && process.env.NODE_ENV !== 'production') {
261
+ console.warn(
262
+ `[TableOfContents] Duplicate anchor id(s) detected on this page: ${[...new Set(collisions)].join(', ')}. ` +
263
+ `This usually means a <Step title="X"> collides with a heading of the same text. ` +
264
+ `Only the first element will be targetable via fragment link or TOC click.`,
265
+ );
263
266
  }
267
+
268
+ if (newItems.length === 0) return;
269
+
270
+ // Dedupe re-renders: if the new item list matches the current one
271
+ // position-for-position on (id, stepNumber), skip setState. This keeps
272
+ // the MutationObserver cheap when it fires on unrelated DOM changes.
273
+ setHeadings(prev => {
274
+ if (prev.length !== newItems.length) return newItems;
275
+ for (let i = 0; i < newItems.length; i++) {
276
+ if (
277
+ prev[i].id !== newItems[i].id ||
278
+ prev[i].stepNumber !== newItems[i].stepNumber ||
279
+ prev[i].text !== newItems[i].text
280
+ ) {
281
+ return newItems;
282
+ }
283
+ }
284
+ return prev;
285
+ });
264
286
  };
265
287
 
266
- const timer = setTimeout(scanDomHeadings, 500);
267
- return () => clearTimeout(timer);
288
+ // Coalesce scans to one per animation frame. cancelAnimationFrame in the
289
+ // cleanup below is only effective while scheduledScan is non-null — once
290
+ // the rAF callback starts, the functional setHeadings is safe on an
291
+ // unmounted tree because it returns prev when items are unchanged.
292
+ let scheduledScan: number | null = null;
293
+ const scheduleScan = () => {
294
+ if (scheduledScan !== null) return;
295
+ scheduledScan = requestAnimationFrame(() => {
296
+ scheduledScan = null;
297
+ scanDomHeadings();
298
+ });
299
+ };
300
+
301
+ scheduleScan();
302
+
303
+ const mainEl = document.querySelector('main');
304
+ const observer = mainEl ? new MutationObserver(scheduleScan) : null;
305
+ if (mainEl && observer) {
306
+ observer.observe(mainEl, {
307
+ childList: true,
308
+ subtree: true,
309
+ attributes: true,
310
+ attributeFilter: ['id', 'data-step-number', 'data-step-label', 'data-update-label'],
311
+ });
312
+ } else if (!mainEl && process.env.NODE_ENV !== 'production') {
313
+ console.warn(
314
+ '[TableOfContents] no <main> element found — late-mount TOC updates will not be observed.',
315
+ );
316
+ }
317
+
318
+ return () => {
319
+ if (scheduledScan !== null) cancelAnimationFrame(scheduledScan);
320
+ observer?.disconnect();
321
+ };
268
322
  }, [content]);
269
323
 
270
324
  // For API pages, hide TOC if there are code panels visible
@@ -306,13 +360,20 @@ export function TableOfContents({ content, className = '' }: TableOfContentsProp
306
360
  : window.innerHeight;
307
361
  const viewBottom = viewTop + viewHeight;
308
362
 
309
- // Build position list: each heading's scroll offset
363
+ // `offsetTop` is relative to `offsetParent`, so for elements inside a
364
+ // positioned ancestor (e.g. <Step> inside <Steps className="relative">)
365
+ // it bears no relation to the scroll container. Use bounding-rect math
366
+ // so positions sit in the container's content coordinate frame
367
+ // regardless of the offsetParent chain.
368
+ const containerTop = contentContainer
369
+ ? contentContainer.getBoundingClientRect().top
370
+ : 0;
310
371
  const positions: { id: string; top: number }[] = [];
311
372
  for (const h of headings) {
312
373
  const el = document.getElementById(h.id);
313
374
  if (!el) continue;
314
375
  const top = contentContainer
315
- ? el.offsetTop - contentContainer.offsetTop
376
+ ? el.getBoundingClientRect().top - containerTop + contentContainer.scrollTop
316
377
  : el.getBoundingClientRect().top + window.scrollY;
317
378
  positions.push({ id: h.id, top });
318
379
  }
@@ -344,7 +405,15 @@ export function TableOfContents({ content, className = '' }: TableOfContentsProp
344
405
  }
345
406
  }
346
407
 
347
- setActiveAnchors(active.length > 0 ? active : [headings[0].id]);
408
+ const next = active.length > 0 ? active : [headings[0].id];
409
+ // Skip state update when the active set hasn't changed — otherwise
410
+ // every scroll tick allocates a new array and cascades re-renders
411
+ // through updateThumb (which reads layout per active anchor).
412
+ setActiveAnchors(prev =>
413
+ prev.length === next.length && prev.every((id, i) => id === next[i])
414
+ ? prev
415
+ : next,
416
+ );
348
417
  };
349
418
 
350
419
  computeActive();
@@ -415,6 +484,7 @@ export function TableOfContents({ content, className = '' }: TableOfContentsProp
415
484
  <div ref={containerRef} className="relative flex flex-col">
416
485
  {headings.map((heading, index) => {
417
486
  const isActive = activeAnchors.includes(heading.id);
487
+ const hasStepNumber = typeof heading.stepNumber === 'number';
418
488
 
419
489
  const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
420
490
  e.preventDefault();
@@ -425,18 +495,45 @@ export function TableOfContents({ content, className = '' }: TableOfContentsProp
425
495
  }
426
496
  };
427
497
 
498
+ // 30px leaves room for the absolute-positioned 18px step circle
499
+ // (insetInlineStart: 1) + a gap. Non-step H3s use the same value
500
+ // so mixed step/non-step groups align visually.
501
+ const startOffset = heading.level === 3
502
+ ? 30
503
+ : getItemOffset(heading.level);
504
+
428
505
  return (
429
506
  <a
430
507
  key={`${heading.id}-${index}`}
431
508
  href={`#${heading.id}`}
432
509
  onClick={handleClick}
433
510
  data-active={isActive}
511
+ data-step={hasStepNumber || undefined}
434
512
  className={`
435
513
  relative block py-1.5 text-sm transition-colors duration-200
436
514
  ${getLinkClass(heading.level, isActive)}
437
515
  `}
438
- style={{ paddingInlineStart: getItemOffset(heading.level) }}
516
+ style={{ paddingInlineStart: startOffset }}
439
517
  >
518
+ {hasStepNumber && (
519
+ <span
520
+ aria-hidden="true"
521
+ className="absolute flex items-center justify-center rounded-full text-[11px] font-medium"
522
+ style={{
523
+ insetInlineStart: 1,
524
+ top: '50%',
525
+ transform: 'translateY(-50%)',
526
+ width: 18,
527
+ height: 18,
528
+ backgroundColor: isActive
529
+ ? 'var(--color-primary)'
530
+ : 'var(--color-bg-primary)',
531
+ color: isActive ? '#fff' : 'var(--color-text-muted)',
532
+ }}
533
+ >
534
+ {heading.stepNumber}
535
+ </span>
536
+ )}
440
537
  {heading.text}
441
538
  </a>
442
539
  );
@@ -2,7 +2,8 @@
2
2
  * Heading extractor for link validation.
3
3
  * Extracts heading slugs from MDX content to validate #fragment links.
4
4
  *
5
- * Canonical source for generateSlug — imported by TableOfContents.tsx and Update.tsx.
5
+ * Canonical source for generateSlug — imported by TableOfContents.tsx, Update.tsx,
6
+ * and Steps.tsx.
6
7
  */
7
8
 
8
9
  export interface HeadingInfo {
@@ -10,6 +11,8 @@ export interface HeadingInfo {
10
11
  text: string;
11
12
  level: number;
12
13
  line: number; // 1-indexed
14
+ /** When the entry is a <Step> inside a <Steps> block, this is its 1-based index within the block. */
15
+ stepNumber?: number;
13
16
  }
14
17
 
15
18
  /**
@@ -26,10 +29,20 @@ export function generateSlug(text: string): string {
26
29
  const HEADING_REGEX = /^(#{1,6})\s+(.+)$/;
27
30
  const FENCE_REGEX = /^(`{3,}|~{3,})/;
28
31
  const UPDATE_LABEL_REGEX = /<Update\s+label=["']([^"']+)["']/;
32
+ const STEPS_OPEN_REGEX = /<Steps(\s|>)/;
33
+ const STEPS_CLOSE_REGEX = /<\/Steps>/;
34
+ // Global flag — we iterate with matchAll so authors who inline multiple
35
+ // <Step> tags on one line get every one numbered.
36
+ // Alternation (not a character class) keeps the capture from terminating on
37
+ // apostrophes inside double-quoted attribute values (e.g. `<Step title="You're Live">`).
38
+ // MUST only be used with matchAll — direct `test`/`exec` would share
39
+ // lastIndex across calls and miscount.
40
+ const STEP_TITLE_REGEX = /<Step\s+[^>]*title=(?:"([^"]+)"|'([^']+)')/g;
29
41
 
30
42
  /**
31
43
  * Extract all heading slugs from MDX content.
32
- * Includes markdown headings and <Update label="..."> component anchors.
44
+ * Includes markdown headings, <Update label="..."> component anchors,
45
+ * and <Step title="..."> entries inside <Steps> blocks (with per-block numbering).
33
46
  * Skips content inside fenced code blocks.
34
47
  */
35
48
  export function extractHeadings(content: string): HeadingInfo[] {
@@ -37,6 +50,10 @@ export function extractHeadings(content: string): HeadingInfo[] {
37
50
  const lines = content.split('\n');
38
51
  let inCodeBlock = false;
39
52
  let fencePattern = '';
53
+ // Boolean (not depth counter) — resets on every <Steps> open so an unclosed
54
+ // block can't stick the counter across subsequent blocks.
55
+ let inStepsBlock = false;
56
+ let stepCounter = 0;
40
57
 
41
58
  for (let i = 0; i < lines.length; i++) {
42
59
  const line = lines[i];
@@ -56,6 +73,14 @@ export function extractHeadings(content: string): HeadingInfo[] {
56
73
 
57
74
  if (inCodeBlock) continue;
58
75
 
76
+ // <Steps> open resets the per-block counter unconditionally. Ordering
77
+ // matters: open-reset must run BEFORE step matching on the same line so
78
+ // `<Steps><Step title="A">...</Step>` numbers "A" as 1.
79
+ if (STEPS_OPEN_REGEX.test(line)) {
80
+ inStepsBlock = true;
81
+ stepCounter = 0;
82
+ }
83
+
59
84
  const headingMatch = line.match(HEADING_REGEX);
60
85
  if (headingMatch) {
61
86
  const level = headingMatch[1].length;
@@ -74,6 +99,28 @@ export function extractHeadings(content: string): HeadingInfo[] {
74
99
  headings.push({ id, text, level: 2, line: i + 1 });
75
100
  }
76
101
  }
102
+
103
+ if (inStepsBlock) {
104
+ for (const match of line.matchAll(STEP_TITLE_REGEX)) {
105
+ const text = match[1] ?? match[2];
106
+ const id = generateSlug(text);
107
+ if (!id) continue;
108
+ stepCounter += 1;
109
+ headings.push({
110
+ id,
111
+ text,
112
+ level: 3,
113
+ line: i + 1,
114
+ stepNumber: stepCounter,
115
+ });
116
+ }
117
+ }
118
+
119
+ // </Steps> close runs AFTER step matching so `</Steps>` on the same line
120
+ // as the last <Step> still picks up that step.
121
+ if (STEPS_CLOSE_REGEX.test(line)) {
122
+ inStepsBlock = false;
123
+ }
77
124
  }
78
125
 
79
126
  return headings;
@@ -4,7 +4,7 @@
4
4
  * Provides display names, flag emojis, and helper functions for multi-language support.
5
5
  */
6
6
 
7
- import type { LanguageCode } from './docs-types';
7
+ import type { LanguageCode, LanguageConfig } from './docs-types';
8
8
 
9
9
  /**
10
10
  * Display names for each supported language code
@@ -336,3 +336,26 @@ export function clearLanguagePreference(): void {
336
336
  // localStorage not available
337
337
  }
338
338
  }
339
+
340
+ /**
341
+ * Resolve the active locale by priority:
342
+ * 1. The path-derived language from the middleware header
343
+ * 2. The project's default language from docs.json (`default: true`,
344
+ * else first entry)
345
+ * 3. "en"
346
+ *
347
+ * Pure function — no I/O, no headers, no config-fetching. Designed to be
348
+ * called once from `app/layout.tsx` after both inputs are resolved.
349
+ */
350
+ export function resolveLanguageWithFallback(
351
+ headerLang: LanguageCode | null,
352
+ configLanguages: LanguageConfig[] | undefined,
353
+ ): LanguageCode {
354
+ if (headerLang) return headerLang;
355
+ if (configLanguages && configLanguages.length > 0) {
356
+ const explicitDefault = configLanguages.find((l) => l.default)?.language;
357
+ if (explicitDefault) return explicitDefault;
358
+ return configLanguages[0].language;
359
+ }
360
+ return 'en';
361
+ }
@@ -17,6 +17,7 @@ import { redis } from './redis';
17
17
  import { getForwardedHosts, isJamdeskDomain } from './domain-helpers';
18
18
  import { getRedirects, matchRedirect, mergeQueryStrings, isInvalidDestination } from './redirect-matcher';
19
19
  import { ASSET_PREFIX } from './docs-types';
20
+ import { extractLanguageFromPath } from './language-utils';
20
21
  import type { NextRequest } from 'next/server';
21
22
 
22
23
  /**
@@ -536,25 +537,79 @@ export function getHostAtDocsRedirect(
536
537
  return null;
537
538
  }
538
539
 
540
+ /**
541
+ * Headers we own end-to-end: written by middleware, trusted by the layout.
542
+ * Strip any inbound copy from the cloned request before we set our own,
543
+ * otherwise a client can send `x-jd-language: ar` (or `x-project-slug: evil`)
544
+ * and the layout will trust it. Same shape of bug as inbound `x-forwarded-*`.
545
+ */
546
+ const TRUSTED_PROXY_HEADERS = [
547
+ 'x-project-slug',
548
+ 'x-host-at-docs',
549
+ 'x-jd-language',
550
+ ] as const;
551
+
539
552
  /**
540
553
  * Build response headers with project context.
541
554
  *
555
+ * Sets:
556
+ * - x-project-slug — resolved project for downstream handlers
557
+ * - x-host-at-docs — whether docs are mounted at /docs
558
+ * - x-jd-language — locale code if the path starts with one (e.g. /fr/...)
559
+ *
560
+ * Strips any client-supplied copies of those headers from the inbound
561
+ * request to prevent header smuggling.
562
+ *
542
563
  * @param projectSlug - Resolved project slug
543
- * @param hostAtDocs - Whether docs are hosted at /docs
544
564
  * @param existingHeaders - Existing request headers to clone
565
+ * @param hostAtDocs - Whether docs are hosted at /docs
566
+ * @param pathname - The request pathname (request.nextUrl.pathname or, for
567
+ * the unlock direct-hit branch, the `from` query param). Used to derive
568
+ * the active locale so the root layout can emit <html lang>.
545
569
  * @returns New headers with project context added
546
570
  */
547
571
  export function buildProjectHeaders(
548
572
  projectSlug: string,
549
573
  existingHeaders: Headers,
550
- hostAtDocs = true
574
+ hostAtDocs: boolean,
575
+ pathname: string,
551
576
  ): Headers {
552
577
  const newHeaders = new Headers(existingHeaders);
578
+ for (const h of TRUSTED_PROXY_HEADERS) {
579
+ newHeaders.delete(h);
580
+ }
581
+
553
582
  newHeaders.set('x-project-slug', projectSlug);
554
583
  newHeaders.set('x-host-at-docs', hostAtDocs ? 'true' : 'false');
584
+
585
+ const language = extractLanguageFromPath(pathname);
586
+ if (language) {
587
+ newHeaders.set('x-jd-language', language);
588
+ }
589
+
555
590
  return newHeaders;
556
591
  }
557
592
 
593
+ /**
594
+ * Recover a pathname from the `from` query param on /jd/unlock.
595
+ *
596
+ * The unlock URL itself carries no locale ("/jd/unlock"), but the user's
597
+ * original path is encoded in `?from=` (e.g. `?from=%2Ffr%2Ffoo`). The
598
+ * proxy uses this to derive the active locale so the unlock page renders
599
+ * with the user's language, not the project default.
600
+ *
601
+ * Returns "" when input is null, empty, or unparseable. The caller treats
602
+ * this as "no language signal available" and falls back to project default.
603
+ */
604
+ export function pathnameFromUnlockFrom(from: string | null): string {
605
+ if (!from) return '';
606
+ try {
607
+ return new URL(from, 'http://x').pathname;
608
+ } catch {
609
+ return '';
610
+ }
611
+ }
612
+
558
613
  /**
559
614
  * Asset extensions that need to be routed to the assets API.
560
615
  */
@@ -5,6 +5,9 @@
5
5
  * These are extracted to be testable without importing Next.js components.
6
6
  */
7
7
 
8
+ import { isValidLanguageCode } from './language-utils';
9
+ import type { LanguageCode } from './docs-types';
10
+
8
11
  /**
9
12
  * Check if ISR mode is enabled.
10
13
  */
@@ -155,3 +158,20 @@ export function getBaseUrl(headers: Headers, projectSlug: string, hostAtDocs = f
155
158
  // Fallback to subdomain URL (subdomains always serve at root, never /docs)
156
159
  return `https://${projectSlug}.jamdesk.app`;
157
160
  }
161
+
162
+ /**
163
+ * Read the active locale from request headers.
164
+ *
165
+ * The middleware (proxy.ts → buildProjectHeaders) sets `x-jd-language` based
166
+ * on the URL path (e.g. `/fr/intro` → "fr"). Returns null when the header is
167
+ * absent (path had no language prefix, or middleware did not run — e.g. local
168
+ * CLI dev) or when the value is not a recognised LanguageCode.
169
+ *
170
+ * Callers should fall back to the project's default language and then "en".
171
+ */
172
+ export function getLanguageFromRequest(headers: Headers): LanguageCode | null {
173
+ const value = headers.get('x-jd-language');
174
+ if (!value) return null;
175
+ if (!isValidLanguageCode(value)) return null;
176
+ return value;
177
+ }
@@ -99,9 +99,63 @@ export interface PageFrontmatter {
99
99
  };
100
100
  }
101
101
 
102
+ /**
103
+ * Walk a navigation node (LanguageConfig, GroupConfig, etc.) and collect every
104
+ * page path declared anywhere underneath it. Pages can appear under `pages`,
105
+ * `groups`, `tabs`, `anchors`, `dropdowns`, `products`, `versions`, or `menu`,
106
+ * and groups nest. Permissive `unknown` typing handles every container shape
107
+ * with one walker — keep the key list in sync with new container types in
108
+ * docs-types.ts (a future container would silently miss pages otherwise).
109
+ */
110
+ function collectPagePathsUnder(node: unknown, out: Set<string>): void {
111
+ if (!node || typeof node !== 'object') return;
112
+ const obj = node as Record<string, unknown>;
113
+
114
+ const pages = obj.pages;
115
+ if (Array.isArray(pages)) {
116
+ for (const entry of pages) {
117
+ if (typeof entry === 'string') {
118
+ out.add(entry);
119
+ } else if (entry && typeof entry === 'object') {
120
+ const page = (entry as { page?: unknown }).page;
121
+ if (typeof page === 'string') out.add(page);
122
+ else collectPagePathsUnder(entry, out); // nested group inside `pages`
123
+ }
124
+ }
125
+ }
126
+
127
+ for (const key of ['groups', 'tabs', 'anchors', 'dropdowns', 'products', 'versions', 'menu']) {
128
+ const children = obj[key];
129
+ if (Array.isArray(children)) {
130
+ for (const child of children) collectPagePathsUnder(child, out);
131
+ }
132
+ }
133
+ }
134
+
135
+ // Same `LanguageConfig` reference is reused across every page render in a
136
+ // config's cache window (see r2-content.ts configCache + IsrContentLoader).
137
+ // Memoising avoids re-walking the nav tree on every generateMetadata call —
138
+ // a real cost for sites with many languages × many pages.
139
+ const langPagePathsCache = new WeakMap<LanguageConfig, Set<string>>();
140
+
141
+ function getLanguagePagePaths(langConfig: LanguageConfig): Set<string> {
142
+ let paths = langPagePathsCache.get(langConfig);
143
+ if (!paths) {
144
+ paths = new Set<string>();
145
+ collectPagePathsUnder(langConfig, paths);
146
+ langPagePathsCache.set(langConfig, paths);
147
+ }
148
+ return paths;
149
+ }
150
+
102
151
  /**
103
152
  * Build hreflang alternate links for multi-language pages.
104
- * Generates links for each language version of the page, plus x-default.
153
+ *
154
+ * Only emits hreflang for languages whose navigation actually declares the
155
+ * current page — Ahrefs flagged 25+ pages where we advertised translations
156
+ * (`/docs/es/ai/overview`, `/docs/fr/ai/overview`) that returned 404 because
157
+ * partially-translated sections (e.g. AI section in jamdesk-docs is en-only)
158
+ * still got hreflang for every declared language.
105
159
  *
106
160
  * @param baseUrl - The base URL (e.g., "https://docs.example.com")
107
161
  * @param pagePath - The current page path (e.g., "es/getting-started")
@@ -124,22 +178,35 @@ function buildHreflangAlternates(
124
178
  // Detect current language from path
125
179
  const currentLang = extractLanguageFromPath(pagePath) || defaultLang;
126
180
 
127
- // Build alternates for each language
128
- const alternates: Record<string, string> = {};
129
-
181
+ // Keep only languages whose navigation declares this page — capturing the
182
+ // language-relative path so the build loop and x-default branch don't have
183
+ // to recompute it via transformLanguagePath.
184
+ const supported: Array<{ lang: LanguageCode; cleanPath: string }> = [];
130
185
  for (const langConfig of languages) {
131
- const lang = langConfig.language;
132
- // Transform current path to this language's version
133
- // transformLanguagePath returns paths relative to app root (e.g., '/es/intro' or '/intro')
134
- const langPath = transformLanguagePath(pagePath, currentLang, lang, defaultLang);
135
- const cleanPath = langPath.replace(/^\//, ''); // Remove leading slash for URL building
186
+ const langPath = transformLanguagePath(pagePath, currentLang, langConfig.language, defaultLang);
187
+ const cleanPath = langPath.replace(/^\//, '');
188
+ if (getLanguagePagePaths(langConfig).has(cleanPath)) {
189
+ supported.push({ lang: langConfig.language, cleanPath });
190
+ }
191
+ }
192
+
193
+ // One or zero language versions → no useful hreflang. `<html lang>` and page
194
+ // content are sufficient signals.
195
+ if (supported.length <= 1) {
196
+ return undefined;
197
+ }
198
+
199
+ const alternates: Record<string, string> = {};
200
+ for (const { lang, cleanPath } of supported) {
136
201
  alternates[lang] = cleanPath ? `${baseUrl}/${cleanPath}` : baseUrl;
137
202
  }
138
203
 
139
- // Add x-default pointing to the default language version
140
- const defaultPath = transformLanguagePath(pagePath, currentLang, defaultLang, defaultLang);
141
- const cleanDefaultPath = defaultPath.replace(/^\//, '');
142
- alternates['x-default'] = cleanDefaultPath ? `${baseUrl}/${cleanDefaultPath}` : baseUrl;
204
+ // x-default points to the default language only when it has the page; absence
205
+ // is fine per Google, whereas pointing at a 404 is what Ahrefs flagged.
206
+ const defaultEntry = supported.find((s) => s.lang === defaultLang);
207
+ if (defaultEntry) {
208
+ alternates['x-default'] = defaultEntry.cleanPath ? `${baseUrl}/${defaultEntry.cleanPath}` : baseUrl;
209
+ }
143
210
 
144
211
  return alternates;
145
212
  }
@@ -219,16 +219,26 @@ function generateSlug(text) {
219
219
 
220
220
  /**
221
221
  * Extract heading slugs from MDX content.
222
- * Includes markdown headings and <Update label="..."> component anchors.
222
+ * Includes markdown headings, <Update label="..."> anchors, and
223
+ * <Step title="..."> anchors inside a <Steps> block.
223
224
  * Skips content inside fenced code blocks.
224
225
  *
225
- * Uses single-pass fence tracking (matches heading-extractor.ts pattern).
226
+ * Must stay semantically compatible with extractHeadings() in lib/heading-extractor.ts
227
+ * for slug collection. stepNumber tracking is intentionally omitted here — this
228
+ * function returns a Set<string>, not HeadingInfo[].
226
229
  */
227
230
  function extractHeadingSlugs(content) {
228
231
  const slugs = new Set();
229
232
  const lines = content.split('\n');
230
233
  let inCodeBlock = false;
231
234
  let fencePattern = '';
235
+ let inStepsBlock = false;
236
+
237
+ const STEPS_OPEN_REGEX = /<Steps(\s|>)/;
238
+ const STEPS_CLOSE_REGEX = /<\/Steps>/;
239
+ // Alternation, not a character class — must not terminate at apostrophes
240
+ // inside double-quoted attribute values (e.g. <Step title="You're Live">).
241
+ const STEP_TITLE_REGEX = /<Step\s+[^>]*title=(?:"([^"]+)"|'([^']+)')/g;
232
242
 
233
243
  for (let i = 0; i < lines.length; i++) {
234
244
  const line = lines[i];
@@ -248,6 +258,10 @@ function extractHeadingSlugs(content) {
248
258
 
249
259
  if (inCodeBlock) continue;
250
260
 
261
+ if (STEPS_OPEN_REGEX.test(line)) {
262
+ inStepsBlock = true;
263
+ }
264
+
251
265
  const headingMatch = line.match(/^#{1,6}\s+(.+)$/);
252
266
  if (headingMatch) {
253
267
  const slug = generateSlug(headingMatch[1].trim());
@@ -259,6 +273,17 @@ function extractHeadingSlugs(content) {
259
273
  const slug = generateSlug(updateMatch[1]);
260
274
  if (slug) slugs.add(slug);
261
275
  }
276
+
277
+ if (inStepsBlock) {
278
+ for (const match of line.matchAll(STEP_TITLE_REGEX)) {
279
+ const slug = generateSlug(match[1] ?? match[2]);
280
+ if (slug) slugs.add(slug);
281
+ }
282
+ }
283
+
284
+ if (STEPS_CLOSE_REGEX.test(line)) {
285
+ inStepsBlock = false;
286
+ }
262
287
  }
263
288
 
264
289
  return slugs;
@@ -418,79 +443,34 @@ function getNavigationPages(contentDir) {
418
443
 
419
444
  const pages = new Set();
420
445
 
421
- /**
422
- * Extract page path from a navigation entry
423
- */
424
- function getPagePath(entry) {
425
- if (typeof entry === 'string') return entry;
426
- if (entry && typeof entry === 'object' && entry.page) return entry.page;
427
- return null;
446
+ function addPage(value) {
447
+ if (typeof value === 'string') pages.add(value);
428
448
  }
429
449
 
430
- /**
431
- * Recursively collect all pages from a navigation structure
432
- */
433
- function collectPages(pagesArray) {
434
- if (!Array.isArray(pagesArray)) return;
435
-
436
- for (const entry of pagesArray) {
437
- const pagePath = getPagePath(entry);
438
-
439
- if (pagePath) {
440
- pages.add(pagePath);
441
- } else if (entry && typeof entry === 'object') {
442
- // It's a group or nested structure - recurse into it
443
- if (entry.pages) collectPages(entry.pages);
444
- if (entry.groups) {
445
- for (const group of entry.groups) {
446
- if (group.pages) collectPages(group.pages);
447
- }
448
- }
449
- }
450
- }
450
+ // Mutual recursion: walkContainer dispatches on the known container fields,
451
+ // walkItems handles arrays of strings (page ids) or nested containers.
452
+ // Covers pages/groups/tabs/anchors/dropdowns/languages/global at any depth.
453
+ function walkContainer(c) {
454
+ if (!c || typeof c !== 'object') return;
455
+ if ('page' in c) addPage(c.page);
456
+ walkItems(c.pages);
457
+ walkItems(c.groups);
458
+ walkItems(c.tabs);
459
+ walkItems(c.anchors);
460
+ walkItems(c.dropdowns);
461
+ walkItems(c.languages);
462
+ if (c.global) walkContainer(c.global);
451
463
  }
452
464
 
453
- /**
454
- * Collect pages from tabs
455
- */
456
- function collectFromTabs(tabs) {
457
- if (!Array.isArray(tabs)) return;
458
-
459
- for (const tab of tabs) {
460
- if (tab.pages) collectPages(tab.pages);
461
- if (tab.groups) {
462
- for (const group of tab.groups) {
463
- if (group.pages) collectPages(group.pages);
464
- }
465
- }
466
- }
467
- }
468
-
469
- // Collect from root-level pages
470
- if (navigation.pages) collectPages(navigation.pages);
471
-
472
- // Collect from groups
473
- if (navigation.groups) {
474
- for (const group of navigation.groups) {
475
- if (group.pages) collectPages(group.pages);
476
- }
477
- }
478
-
479
- // Collect from tabs
480
- if (navigation.tabs) collectFromTabs(navigation.tabs);
481
-
482
- // Collect from anchors
483
- if (navigation.anchors) {
484
- for (const anchor of navigation.anchors) {
485
- if (anchor.pages) collectPages(anchor.pages);
486
- if (anchor.groups) {
487
- for (const group of anchor.groups) {
488
- if (group.pages) collectPages(group.pages);
489
- }
490
- }
465
+ function walkItems(items) {
466
+ if (!Array.isArray(items)) return;
467
+ for (const item of items) {
468
+ if (typeof item === 'string') addPage(item);
469
+ else if (item && typeof item === 'object') walkContainer(item);
491
470
  }
492
471
  }
493
472
 
473
+ walkContainer(navigation);
494
474
  return pages;
495
475
  }
496
476
 
@@ -294,14 +294,14 @@
294
294
  "license": "MIT"
295
295
  },
296
296
  "node_modules/@iconify/utils": {
297
- "version": "3.1.0",
298
- "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz",
299
- "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==",
297
+ "version": "3.1.1",
298
+ "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.1.tgz",
299
+ "integrity": "sha512-MwzoDtw9rO1x+qfgLTV/IVXsHDBqeYZoMIQC8SfxfYSlaSUG+oWiAcoiB1yajAda6mqblm4/1/w2E8tRu7a7Tw==",
300
300
  "license": "MIT",
301
301
  "dependencies": {
302
302
  "@antfu/install-pkg": "^1.1.0",
303
303
  "@iconify/types": "^2.0.0",
304
- "mlly": "^1.8.0"
304
+ "mlly": "^1.8.2"
305
305
  }
306
306
  },
307
307
  "node_modules/@img/colour": {
@@ -2025,9 +2025,9 @@
2025
2025
  }
2026
2026
  },
2027
2027
  "node_modules/ajv": {
2028
- "version": "8.18.0",
2029
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
2030
- "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
2028
+ "version": "8.20.0",
2029
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
2030
+ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
2031
2031
  "license": "MIT",
2032
2032
  "dependencies": {
2033
2033
  "fast-deep-equal": "^3.1.3",
@@ -2152,9 +2152,9 @@
2152
2152
  }
2153
2153
  },
2154
2154
  "node_modules/baseline-browser-mapping": {
2155
- "version": "2.10.21",
2156
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.21.tgz",
2157
- "integrity": "sha512-Q+rUQ7Uz8AHM7DEaNdwvfFCTq7a43lNTzuS94eiWqwyxfV/wJv+oUivef51T91mmRY4d4A1u9rcSvkeufCVXlA==",
2155
+ "version": "2.10.22",
2156
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.22.tgz",
2157
+ "integrity": "sha512-6qruVrb5rse6WylFkU0FhBKKGuecWseqdpQfhkawn6ztyk2QlfwSRjsDxMCLJrkfmfN21qvhl9ABgaMeRkuwww==",
2158
2158
  "license": "Apache-2.0",
2159
2159
  "bin": {
2160
2160
  "baseline-browser-mapping": "dist/cli.cjs"
@@ -2215,9 +2215,9 @@
2215
2215
  "license": "MIT"
2216
2216
  },
2217
2217
  "node_modules/caniuse-lite": {
2218
- "version": "1.0.30001790",
2219
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001790.tgz",
2220
- "integrity": "sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw==",
2218
+ "version": "1.0.30001791",
2219
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz",
2220
+ "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==",
2221
2221
  "funding": [
2222
2222
  {
2223
2223
  "type": "opencollective",
@@ -5586,9 +5586,9 @@
5586
5586
  }
5587
5587
  },
5588
5588
  "node_modules/postcss": {
5589
- "version": "8.5.10",
5590
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
5591
- "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
5589
+ "version": "8.5.11",
5590
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.11.tgz",
5591
+ "integrity": "sha512-5dDj8+lmvA8XB78SmzGI8NlQoksv7IfutGWeVZxiixHbO+p4LDPT3wuG/D9sM/wrjZZ9I+Siy/e117vbFPxSZg==",
5592
5592
  "funding": [
5593
5593
  {
5594
5594
  "type": "opencollective",