@takazudo/zudo-doc 5.24.2 → 5.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +40 -2
  2. package/dist/asset-components/asset-card.js +2 -2
  3. package/dist/asset-components/asset-code.js +1 -1
  4. package/dist/asset-index-page/body.d.ts +80 -0
  5. package/dist/asset-index-page/body.js +116 -0
  6. package/dist/asset-index-page/index.d.ts +3 -9
  7. package/dist/asset-index-page/index.js +18 -149
  8. package/dist/asset-index-page/tree.d.ts +24 -0
  9. package/dist/asset-index-page/tree.js +43 -0
  10. package/dist/asset-page/body.d.ts +98 -0
  11. package/dist/asset-page/body.js +107 -0
  12. package/dist/asset-page/components.d.ts +94 -0
  13. package/dist/asset-page/components.js +175 -0
  14. package/dist/asset-page/index.d.ts +3 -78
  15. package/dist/asset-page/index.js +30 -240
  16. package/dist/asset-page/shared.d.ts +15 -0
  17. package/dist/asset-page/shared.js +28 -0
  18. package/dist/breadcrumb/breadcrumb.js +1 -1
  19. package/dist/compiled.css +18 -71
  20. package/dist/config/component-tokens.js +3 -1
  21. package/dist/config-assertions/index.d.ts +26 -0
  22. package/dist/config-assertions/index.js +12 -0
  23. package/dist/config.d.ts +28 -0
  24. package/dist/config.js +6 -1
  25. package/dist/doc-pager/index.js +2 -2
  26. package/dist/footer/footer.js +3 -3
  27. package/dist/header/header.js +2 -1
  28. package/dist/header-with-defaults/index.d.ts +20 -0
  29. package/dist/header-with-defaults/index.js +9 -2
  30. package/dist/home-page/index.js +49 -22
  31. package/dist/i18n-defaults/index.js +3 -0
  32. package/dist/integrations/changelog/generate.js +8 -2
  33. package/dist/integrations/changelog/links.d.ts +25 -0
  34. package/dist/integrations/changelog/links.js +280 -0
  35. package/dist/mdx-components/index.js +1 -1
  36. package/dist/nav-indexing/category-nav.js +1 -1
  37. package/dist/nav-indexing/category-tree-nav.js +1 -1
  38. package/dist/nav-indexing/doc-card-grid.js +2 -2
  39. package/dist/nav-indexing/docs-sitemap.js +2 -2
  40. package/dist/nav-indexing/nav-card-grid.d.ts +3 -2
  41. package/dist/nav-indexing/nav-card-grid.js +3 -3
  42. package/dist/nav-indexing/note-tray-index-parts/card-list.js +1 -1
  43. package/dist/nav-indexing/note-tray-index-parts/index-list.js +1 -1
  44. package/dist/nav-indexing/note-tray-index-parts/timeline.js +1 -1
  45. package/dist/nav-indexing/site-tree-nav-demo.js +2 -2
  46. package/dist/nav-indexing/versions-page-content.js +2 -2
  47. package/dist/plugins/routes.js +2 -13
  48. package/dist/plugins/zdtp-loader.js +1 -1
  49. package/dist/preset.d.ts +11 -1
  50. package/dist/preset.js +12 -5
  51. package/dist/safelist.css +1 -1
  52. package/dist/settings.d.ts +9 -0
  53. package/dist/sidebar-toggle-island/index.js +1 -1
  54. package/dist/site-tree-nav-island/index.js +3 -3
  55. package/dist/theme-packs/foundry/pack.css +4 -9
  56. package/dist/theme-packs/riso/pack.css +6 -4
  57. package/dist/theme-packs/swissgrid/pack.css +7 -4
  58. package/eject/asset-components/asset-card.tsx +2 -2
  59. package/eject/asset-components/asset-code.tsx +1 -1
  60. package/eject/breadcrumb/breadcrumb.tsx +1 -1
  61. package/eject/doc-pager/index.tsx +2 -2
  62. package/eject/footer/footer.tsx +4 -4
  63. package/eject/header/header.tsx +11 -1
  64. package/eject/sidebar-toggle-island/index.tsx +1 -1
  65. package/eject/site-tree-nav-island/index.tsx +4 -4
  66. package/package.json +37 -9
@@ -19,6 +19,16 @@ function assertNoEmptyStringFaviconOrLogo(settings) {
19
19
  }
20
20
  }
21
21
  }
22
+ function resolvesBundleZdtp(settings) {
23
+ return settings.bundleZdtp ?? settings.designTokenPanel === true;
24
+ }
25
+ function assertZdtpBundlingConsistent(settings) {
26
+ if (settings.designTokenPanel === true && settings.bundleZdtp === false) {
27
+ throw new TypeError(
28
+ "Invalid combination: designTokenPanel: true with bundleZdtp: false. The package-owned design token panel needs @takazudo/zdtp in the build, but bundleZdtp: false shadows @takazudo/zudo-doc/zdtp-loader with a throwing stub, so the panel would fail to load at runtime. Drop bundleZdtp (it follows designTokenPanel by default), or set designTokenPanel: false if you only meant to keep zdtp out of the build."
29
+ );
30
+ }
31
+ }
22
32
  function warnAmbiguousDropdownCategoryMatch(headerNav, logger = console) {
23
33
  if (!headerNav) return;
24
34
  for (const item of headerNav) {
@@ -60,5 +70,7 @@ function warnAmbiguousDropdownCategoryMatch(headerNav, logger = console) {
60
70
  }
61
71
  export {
62
72
  assertNoEmptyStringFaviconOrLogo,
73
+ assertZdtpBundlingConsistent,
74
+ resolvesBundleZdtp,
63
75
  warnAmbiguousDropdownCategoryMatch
64
76
  };
package/dist/config.d.ts CHANGED
@@ -155,6 +155,16 @@ export interface ZudoDocConfig {
155
155
  * @default []
156
156
  */
157
157
  siteTreeNavIgnore?: string[];
158
+ /**
159
+ * Top-level category slugs moved OUT of the package-owned home-page category
160
+ * grid on `/` and locale homes and rendered instead as one row of small
161
+ * links directly under the grid, in this list's order. Home page only — the
162
+ * `<SiteTreeNav />` MDX tag is unchanged. Slugs absent from the tree are
163
+ * skipped, `siteTreeNavIgnore` wins over this list, and a category with no
164
+ * page of its own stays in the grid.
165
+ * @default []
166
+ */
167
+ siteTreeNavSecondary?: string[];
158
168
  /**
159
169
  * Minify production HTML output from `zfb build`.
160
170
  * @default true
@@ -330,6 +340,24 @@ export interface ZudoDocConfig {
330
340
  * @default false
331
341
  */
332
342
  designTokenPanel?: boolean;
343
+ /**
344
+ * Bundle `@takazudo/zdtp` into the island build. Controls ONLY whether
345
+ * `@takazudo/zudo-doc/zdtp-loader` resolves to the real zdtp re-export or to
346
+ * a throwing stub — it never mounts the package panel or its header trigger
347
+ * (that stays `designTokenPanel`'s job).
348
+ *
349
+ * Set it to `true` when the host mounts its OWN panel through
350
+ * `@takazudo/zudo-doc/design-token-panel-bootstrap` while leaving
351
+ * `designTokenPanel` off (#4261); otherwise the bootstrap's `loadZdtp()`
352
+ * hits the stub and rejects at runtime. The host must then have
353
+ * `@takazudo/zdtp` installed — it is an optional peer, and the build fails
354
+ * resolving `zdtp-loader` if it is missing.
355
+ *
356
+ * `designTokenPanel: true` with `bundleZdtp: false` is rejected at config
357
+ * resolution: a mounted package panel with a stubbed loader can only throw.
358
+ * @default undefined (follows designTokenPanel)
359
+ */
360
+ bundleZdtp?: boolean;
333
361
  /**
334
362
  * Minimum heading depth included in the TOC (2–4).
335
363
  * @default 2
package/dist/config.js CHANGED
@@ -4,7 +4,10 @@ import { defaultDirectiveVocabulary } from "./directive-vocabulary-defaults/inde
4
4
  import { defaultTranslations } from "./i18n-defaults/index.js";
5
5
  import { defaultColorSchemes } from "./color-schemes-defaults/index.js";
6
6
  import { assertNoCommaInVersionSlugs } from "./version-availability/index.js";
7
- import { assertNoEmptyStringFaviconOrLogo } from "./config-assertions/index.js";
7
+ import {
8
+ assertNoEmptyStringFaviconOrLogo,
9
+ assertZdtpBundlingConsistent
10
+ } from "./config-assertions/index.js";
8
11
  import { validateAssetViewerSettings } from "./asset-path/index.js";
9
12
  function mergeDefaultTranslations(overrides) {
10
13
  if (overrides === void 0) return defaultTranslations;
@@ -47,6 +50,7 @@ const DEFAULT_SETTINGS = {
47
50
  trailingSlash: false,
48
51
  home: { wide: false, introMarkdown: "", sitemapHeading: "" },
49
52
  siteTreeNavIgnore: [],
53
+ siteTreeNavSecondary: [],
50
54
  minifyHtml: true,
51
55
  docsDir: "src/content/docs",
52
56
  entryDocSlug: "getting-started",
@@ -147,6 +151,7 @@ function zudoDoc(user = {}) {
147
151
  );
148
152
  assertNoCommaInVersionSlugs(settings.versions);
149
153
  assertNoEmptyStringFaviconOrLogo(settings);
154
+ assertZdtpBundlingConsistent(settings);
150
155
  const fragment = zudoDocPreset({
151
156
  settings,
152
157
  buildDocsSchema: userBuildDocsSchema ?? (() => defaultBuildDocsSchema({
@@ -19,7 +19,7 @@ function createDocPager(ctx) {
19
19
  /* @__PURE__ */ jsx(ChevronLeft, { className: "h-[1.125rem] w-[1.125rem]" }),
20
20
  /* @__PURE__ */ jsx("span", { class: "no-underline", children: t("nav.previous", locale) })
21
21
  ] }),
22
- /* @__PURE__ */ jsx("p", { class: "text-small font-semibold underline group-hover:text-accent", children: prev.label }),
22
+ /* @__PURE__ */ jsx("p", { class: "text-small font-semibold text-fg group-hover:text-accent group-hover:underline group-focus-visible:text-accent group-focus-visible:underline", children: prev.label }),
23
23
  prev.date && /* @__PURE__ */ jsx("p", { class: "text-caption text-muted", children: formatDate(prev.date, locale, dateFormatsFor(locale).full) })
24
24
  ]
25
25
  }
@@ -34,7 +34,7 @@ function createDocPager(ctx) {
34
34
  /* @__PURE__ */ jsx("span", { class: "no-underline", children: t("nav.next", locale) }),
35
35
  /* @__PURE__ */ jsx(ChevronRight, { className: "h-[1.125rem] w-[1.125rem]" })
36
36
  ] }),
37
- /* @__PURE__ */ jsx("p", { class: "text-small font-semibold underline group-hover:text-accent", children: next.label }),
37
+ /* @__PURE__ */ jsx("p", { class: "text-small font-semibold text-fg group-hover:text-accent group-hover:underline group-focus-visible:text-accent group-focus-visible:underline", children: next.label }),
38
38
  next.date && /* @__PURE__ */ jsx("p", { class: "text-caption text-muted", children: formatDate(next.date, locale, dateFormatsFor(locale).full) })
39
39
  ]
40
40
  }
@@ -6,7 +6,7 @@ function Footer(props) {
6
6
  const persistKey = props.persistKey;
7
7
  const hasColumns = linkColumns.length > 0 || tagColumns.length > 0;
8
8
  const hasCopyright = copyright.length > 0;
9
- const copyrightClass = hasColumns ? "text-center text-caption text-muted [&_a]:text-accent [&_a]:underline mt-vsp-lg border-t border-muted pt-vsp-md" : "text-center text-caption text-muted [&_a]:text-accent [&_a]:underline";
9
+ const copyrightClass = hasColumns ? "text-center text-caption text-muted [&_a]:underline [&_a:hover]:text-accent [&_a:focus-visible]:text-accent mt-vsp-lg border-t border-muted pt-vsp-md" : "text-center text-caption text-muted [&_a]:underline [&_a:hover]:text-accent [&_a:focus-visible]:text-accent";
10
10
  return /* @__PURE__ */ jsx(
11
11
  "footer",
12
12
  {
@@ -25,7 +25,7 @@ function Footer(props) {
25
25
  "a",
26
26
  {
27
27
  href: item.href,
28
- class: "text-caption text-muted hover:text-accent hover:underline focus-visible:underline",
28
+ class: "text-caption text-muted hover:text-accent hover:underline focus-visible:text-accent focus-visible:underline",
29
29
  ...item.isExternal ? {
30
30
  target: "_blank",
31
31
  rel: "noopener noreferrer"
@@ -47,7 +47,7 @@ function Footer(props) {
47
47
  "a",
48
48
  {
49
49
  href,
50
- class: "text-caption text-muted hover:text-accent hover:underline focus-visible:underline",
50
+ class: "text-caption text-muted hover:text-accent hover:underline focus-visible:text-accent focus-visible:underline",
51
51
  children: [
52
52
  "#",
53
53
  tag,
@@ -65,7 +65,8 @@ function Header(props) {
65
65
  "a",
66
66
  {
67
67
  href: urlHelpers.withBase(isNonDefaultLocale ? `/${lang}/` : "/"),
68
- class: "whitespace-nowrap text-title font-bold text-fg hover:underline focus:underline shrink-0",
68
+ class: "min-w-0 truncate text-title font-bold text-fg hover:underline focus:underline",
69
+ title: siteName,
69
70
  "data-header-logo": true,
70
71
  children: siteName
71
72
  }
@@ -3,6 +3,7 @@
3
3
  import type { JSX } from "preact";
4
4
  import type { ChromeContext } from "../factory-context/index.js";
5
5
  import type { Settings } from "../settings.js";
6
+ import type { SidebarNavNode } from "../sidebar/types.js";
6
7
  export interface HeaderWithDefaultsProps {
7
8
  /** Active locale; defaults to the configured defaultLocale. */
8
9
  lang?: string;
@@ -16,6 +17,25 @@ export interface HeaderWithDefaultsProps {
16
17
  navSection?: string;
17
18
  /** Omit the mobile sidebar toggle and its drawer landmark. */
18
19
  hideSidebarToggle?: boolean;
20
+ /**
21
+ * Override the mobile-drawer sidebar tree passed to `SidebarToggle`,
22
+ * resolved per render against that render's `lang`/`navSection`/
23
+ * `currentVersion`.
24
+ *
25
+ * - An array is passed to `SidebarToggle` as-is — no default build, no
26
+ * version-href remapping.
27
+ * - A callback receives the resolved `lang`/`navSection`/`currentVersion`
28
+ * plus a lazy `buildDefault()` that reproduces today's default tree
29
+ * (`buildSidebarNodes(lang, navSection, currentVersion)`) only when
30
+ * called.
31
+ * - Omitted: behavior is unchanged from before this prop existed.
32
+ */
33
+ sidebarNodes?: SidebarNavNode[] | ((args: {
34
+ lang: string;
35
+ navSection: string | undefined;
36
+ currentVersion: string | undefined;
37
+ buildDefault: () => SidebarNavNode[];
38
+ }) => SidebarNavNode[]);
19
39
  }
20
40
  /** Version config entry subset the factory reads. */
21
41
  export interface HeaderVersionEntry {
@@ -39,14 +39,21 @@ function createHeaderWithDefaults(ctx) {
39
39
  currentVersion,
40
40
  currentSlug,
41
41
  navSection,
42
- hideSidebarToggle = false
42
+ hideSidebarToggle = false,
43
+ sidebarNodes: sidebarNodesProp
43
44
  } = props;
44
45
  const lang = langProp;
45
46
  const rootMenuItems = buildRootMenuItems(lang, currentVersion);
46
47
  const backToMenuLabel = t("nav.backToMenu", lang);
47
48
  const localeLinks = buildLocaleLinksForNav(currentPath, lang, locales.length);
48
49
  const themeDefaultMode = getThemeDefaultMode();
49
- const sidebarNodes = buildSidebarNodes(lang, navSection, currentVersion);
50
+ const buildDefaultSidebarNodes = () => buildSidebarNodes(lang, navSection, currentVersion);
51
+ const sidebarNodes = sidebarNodesProp === void 0 ? buildDefaultSidebarNodes() : typeof sidebarNodesProp === "function" ? sidebarNodesProp({
52
+ lang,
53
+ navSection,
54
+ currentVersion,
55
+ buildDefault: buildDefaultSidebarNodes
56
+ }) : sidebarNodesProp;
50
57
  const sidebarToggle = hideSidebarToggle ? false : Island({
51
58
  when: "visible",
52
59
  children: /* @__PURE__ */ jsx(
@@ -16,6 +16,39 @@ import { prepareHomeData } from "./prepare-home-data.js";
16
16
  function HomeSectionHeading({ children }) {
17
17
  return /* @__PURE__ */ jsx("h2", { class: `${HOME_SECTION_HEADING_CLASS} mb-vsp-md`, children });
18
18
  }
19
+ function HomeMetaLink({ href, children }) {
20
+ return /* @__PURE__ */ jsxs(
21
+ "a",
22
+ {
23
+ href,
24
+ class: "group inline-flex items-center gap-hsp-xs text-small text-fg hover:text-accent hover:underline focus-visible:text-accent focus-visible:underline",
25
+ children: [
26
+ /* @__PURE__ */ jsx(CategoryLinkIcon, { className: "w-icon-sm text-muted group-hover:text-accent group-focus-visible:text-accent" }),
27
+ /* @__PURE__ */ jsx("span", { children })
28
+ ]
29
+ }
30
+ );
31
+ }
32
+ function selectSecondaryNodes(tree, secondary, categoryIgnore) {
33
+ const moved = [];
34
+ const seen = new Set(categoryIgnore);
35
+ for (const slug of secondary) {
36
+ if (seen.has(slug)) continue;
37
+ seen.add(slug);
38
+ const node = tree.find((n) => n.slug === slug);
39
+ if (!node) continue;
40
+ if (!node.href) {
41
+ if (typeof process !== "undefined" && process.env?.NODE_ENV !== "production") {
42
+ console.warn(
43
+ `[zudo-doc] siteTreeNavSecondary: category "${slug}" has no page (href) \u2014 left in the grid`
44
+ );
45
+ }
46
+ continue;
47
+ }
48
+ moved.push(node);
49
+ }
50
+ return moved;
51
+ }
19
52
  function cssUrl(url) {
20
53
  return `url("${url.replace(/[\\"]/g, "\\$&")}")`;
21
54
  }
@@ -24,6 +57,7 @@ function createHomePageView(ctx) {
24
57
  const settings = ctx.settings;
25
58
  const dateFormatsFor = deriveDateFormats(ctx);
26
59
  const categoryIgnore = settings.siteTreeNavIgnore ?? [];
60
+ const secondary = settings.siteTreeNavSecondary ?? [];
27
61
  const t = ctx.t;
28
62
  const withBase = ctx.withBase;
29
63
  const defaultLocale = ctx.defaultLocale;
@@ -64,6 +98,7 @@ function createHomePageView(ctx) {
64
98
  t("home.sitemapHeading", locale)
65
99
  ).sitemapHeading;
66
100
  const hasExtras = resolvedExtras != null && typeof resolvedExtras !== "boolean";
101
+ const movedNodes = selectSecondaryNodes(tree, secondary, categoryIgnore);
67
102
  const rowItems = [];
68
103
  if (primary) {
69
104
  rowItems.push(
@@ -133,7 +168,7 @@ function createHomePageView(ctx) {
133
168
  }
134
169
  ) : null,
135
170
  /* @__PURE__ */ jsxs("div", { class: "zd-home-copy min-w-0 lg:flex-1", children: [
136
- /* @__PURE__ */ jsx("h1", { class: "text-heading font-bold mb-vsp-2xs break-words", children: settings.siteName }),
171
+ /* @__PURE__ */ jsx("h1", { class: "text-heading font-bold mb-vsp-2xs wrap-anywhere", children: settings.siteName }),
137
172
  /* @__PURE__ */ jsx("p", { class: "text-muted text-small mb-vsp-sm", children: rawDescription }),
138
173
  /* @__PURE__ */ jsx("div", { class: "zd-home-links flex flex-wrap items-center justify-center lg:justify-start gap-hsp-md text-small", children: rowItems.map((item, index) => /* @__PURE__ */ jsxs(Fragment2, { children: [
139
174
  index > 0 && /* @__PURE__ */ jsx("span", { class: "text-muted", children: "/" }),
@@ -155,14 +190,23 @@ function createHomePageView(ctx) {
155
190
  {
156
191
  tree,
157
192
  categoryOrder,
158
- categoryIgnore,
193
+ categoryIgnore: [...categoryIgnore, ...movedNodes.map((n) => n.slug)],
159
194
  initiallyCollapsedCategorySlugs,
160
195
  locale,
161
196
  updatedLabel: t("doc.updated", locale),
162
197
  dateFormats: dateFormatsFor(locale)
163
198
  }
164
199
  )
165
- })
200
+ }),
201
+ movedNodes.length > 0 && /* @__PURE__ */ jsx(
202
+ "nav",
203
+ {
204
+ "aria-label": t("home.secondaryNav", locale),
205
+ "data-home-secondary-nav": true,
206
+ class: "mt-vsp-md flex flex-wrap items-center gap-x-hsp-xl gap-y-vsp-xs",
207
+ children: movedNodes.map((n) => /* @__PURE__ */ jsx(HomeMetaLink, { href: n.href, children: n.label }, n.slug))
208
+ }
209
+ )
166
210
  ] }),
167
211
  settings.docTags && tagCount > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
168
212
  /* @__PURE__ */ jsx("hr", { class: "zd-home-rule", "data-home-rule": "tags" }),
@@ -180,31 +224,14 @@ function createHomePageView(ctx) {
180
224
  }
181
225
  ),
182
226
  tags.length > tagLimit && /* @__PURE__ */ jsx("div", { class: "mt-vsp-sm text-title text-muted", "aria-hidden": "true", children: "\u2026" }),
183
- /* @__PURE__ */ jsx("div", { class: "mt-vsp-md", children: /* @__PURE__ */ jsxs(
184
- "a",
185
- {
186
- href: withBase(`${prefix}/docs/tags`),
187
- class: "group inline-flex items-center gap-hsp-xs text-accent hover:underline",
188
- children: [
189
- /* @__PURE__ */ jsx(CategoryLinkIcon, { className: "w-icon-sm text-accent" }),
190
- /* @__PURE__ */ jsx("span", { children: t("doc.seeAllTags", locale) })
191
- ]
192
- }
193
- ) })
227
+ /* @__PURE__ */ jsx("div", { class: "mt-vsp-md", children: /* @__PURE__ */ jsx(HomeMetaLink, { href: withBase(`${prefix}/docs/tags`), children: t("doc.seeAllTags", locale) }) })
194
228
  ] }) : (
195
229
  // Legacy fallback: caller passed only `tagCount` (no tag list) —
196
230
  // reproduce the pre-#3027 single "All Tags" link so existing
197
231
  // `@takazudo/zudo-doc/home-page` consumers are unaffected.
198
232
  /* @__PURE__ */ jsxs(Fragment, { children: [
199
233
  /* @__PURE__ */ jsx(HomeSectionHeading, { children: t("doc.allTags", locale) }),
200
- /* @__PURE__ */ jsx(
201
- "a",
202
- {
203
- href: withBase(`${prefix}/docs/tags`),
204
- class: "text-accent underline hover:text-accent-hover",
205
- children: t("doc.allTags", locale)
206
- }
207
- )
234
+ /* @__PURE__ */ jsx(HomeMetaLink, { href: withBase(`${prefix}/docs/tags`), children: t("doc.allTags", locale) })
208
235
  ] })
209
236
  ) })
210
237
  ] })
@@ -1,6 +1,7 @@
1
1
  const defaultTranslations = {
2
2
  en: {
3
3
  "home.sitemapHeading": "Explore the documentation",
4
+ "home.secondaryNav": "More categories",
4
5
  "nav.gettingStarted": "Getting Started",
5
6
  "nav.learn": "Learn",
6
7
  "nav.guides": "Guides",
@@ -130,6 +131,7 @@ const defaultTranslations = {
130
131
  },
131
132
  ja: {
132
133
  "home.sitemapHeading": "\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u3092\u63A2\u3059",
134
+ "home.secondaryNav": "\u305D\u306E\u4ED6\u306E\u30AB\u30C6\u30B4\u30EA",
133
135
  "nav.gettingStarted": "\u306F\u3058\u3081\u306B",
134
136
  "nav.learn": "\u5B66\u3076",
135
137
  "nav.guides": "\u30AC\u30A4\u30C9",
@@ -258,6 +260,7 @@ const defaultTranslations = {
258
260
  "version.page.docs": "\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8"
259
261
  },
260
262
  de: {
263
+ "home.secondaryNav": "Weitere Kategorien",
261
264
  "nav.gettingStarted": "Erste Schritte",
262
265
  "nav.learn": "Lernen",
263
266
  "nav.guides": "Anleitungen",
@@ -1,3 +1,8 @@
1
+ import {
2
+ buildChangelogAnchorMap,
3
+ formatChangelogEntryHeadingText,
4
+ rewriteChangelogEntryLinks
5
+ } from "./links.js";
1
6
  function generateChangelogMarkdown(entries, options = {}) {
2
7
  const title = options.title ?? "Changelog";
3
8
  const lines = [`# ${title}`, ""];
@@ -8,12 +13,13 @@ function generateChangelogMarkdown(entries, options = {}) {
8
13
  }
9
14
  lines.push("");
10
15
  lines.push("The format is based on Keep a Changelog, and release notes are generated from the changelog MDX pages.");
16
+ const anchorsByFilename = buildChangelogAnchorMap(title, entries);
11
17
  for (const entry of entries) {
12
18
  lines.push("");
13
- lines.push(entry.date ? `## [${entry.version}] - ${entry.date}` : `## [${entry.version}]`);
19
+ lines.push(`## ${formatChangelogEntryHeadingText(entry)}`);
14
20
  if (entry.content) {
15
21
  lines.push("");
16
- lines.push(entry.content);
22
+ lines.push(rewriteChangelogEntryLinks(entry.content, anchorsByFilename));
17
23
  }
18
24
  }
19
25
  lines.push("");
@@ -0,0 +1,25 @@
1
+ import type { ChangelogEntry } from "./types.js";
2
+ /**
3
+ * Heading text shared by the emitted `## [version] - date` line and the
4
+ * anchor map, so the two cannot drift out of step with each other.
5
+ */
6
+ export declare function formatChangelogEntryHeadingText(entry: Pick<ChangelogEntry, "version" | "date">): string;
7
+ /**
8
+ * Build the anchor map (exact source filename -> in-file anchor) for a full
9
+ * generated changelog document. Anchors must reflect the WHOLE document's
10
+ * heading order, not just the version headings: the `# <title>` line and any
11
+ * ATX or setext headings inside entry bodies consume slugs too, and an earlier
12
+ * entry's body heading can shift a later entry's anchor via GitHub's `-1`,
13
+ * `-2`, … de-duplication.
14
+ */
15
+ export declare function buildChangelogAnchorMap(title: string, entries: readonly ChangelogEntry[]): ReadonlyMap<string, string>;
16
+ /**
17
+ * Rewrite one entry's already-sanitized markdown content: same-directory
18
+ * `.md`/`.mdx` links to another emitted entry become an in-file anchor link
19
+ * (`#<anchor>`, original `#fragment` dropped, title preserved); any other
20
+ * relative `.md`/`.mdx` link is unlinked to its label text. Everything else
21
+ * (absolute URLs, protocol-relative, root-absolute, pure fragments, images,
22
+ * other extensions, reference-style links, and anything inside fenced or
23
+ * inline code) passes through byte-unchanged.
24
+ */
25
+ export declare function rewriteChangelogEntryLinks(content: string, anchorByFilename: ReadonlyMap<string, string>): string;