@takazudo/zudo-doc 5.14.0 → 5.16.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 (101) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/asset-components/asset-card.js +3 -1
  3. package/dist/asset-components/asset-code.js +2 -0
  4. package/dist/asset-components/shared.d.ts +5 -0
  5. package/dist/asset-components/shared.js +7 -0
  6. package/dist/asset-index-page/index.d.ts +1 -0
  7. package/dist/asset-index-page/index.js +10 -9
  8. package/dist/asset-page/index.d.ts +20 -2
  9. package/dist/asset-page/index.js +43 -24
  10. package/dist/asset-path/index.d.ts +2 -0
  11. package/dist/asset-path/index.js +11 -1
  12. package/dist/chrome/derive.js +31 -6
  13. package/dist/compiled.css +12 -0
  14. package/dist/config.d.ts +15 -6
  15. package/dist/config.js +2 -0
  16. package/dist/content/content-link.d.ts +5 -1
  17. package/dist/content/content-link.js +4 -1
  18. package/dist/doc-content-header/index.js +1 -1
  19. package/dist/doc-page-shell/index.js +1 -1
  20. package/dist/factory-context/index.d.ts +7 -3
  21. package/dist/header-with-defaults/index.js +2 -0
  22. package/dist/home-page/index.js +1 -1
  23. package/dist/html-preview-wrapper/html-preview-wrapper.d.ts +41 -7
  24. package/dist/html-preview-wrapper/html-preview-wrapper.js +86 -2
  25. package/dist/html-preview-wrapper/html-preview.d.ts +29 -4
  26. package/dist/html-preview-wrapper/html-preview.js +64 -6
  27. package/dist/html-preview-wrapper/index.d.ts +1 -1
  28. package/dist/html-preview-wrapper/preview-auto-height.d.ts +24 -0
  29. package/dist/html-preview-wrapper/preview-auto-height.js +120 -0
  30. package/dist/html-preview-wrapper/preview-base.d.ts +21 -1
  31. package/dist/html-preview-wrapper/preview-base.js +99 -50
  32. package/dist/i18n-defaults/index.d.ts +5 -3
  33. package/dist/i18n-defaults/index.js +89 -0
  34. package/dist/i18n-version/language-switcher.d.ts +10 -5
  35. package/dist/i18n-version/language-switcher.js +115 -22
  36. package/dist/i18n-version/types.d.ts +1 -1
  37. package/dist/mdx-components/index.d.ts +4 -0
  38. package/dist/mdx-components/index.js +11 -2
  39. package/dist/nav-indexing/versions-page-content.js +1 -1
  40. package/dist/plugins/claude-resources.js +15 -1
  41. package/dist/plugins/codex-resources.js +15 -1
  42. package/dist/plugins/internal/asset-viewer/asset-pages.d.ts +53 -0
  43. package/dist/plugins/internal/asset-viewer/asset-pages.js +98 -0
  44. package/dist/plugins/internal/asset-viewer/scan.d.ts +18 -1
  45. package/dist/plugins/internal/asset-viewer/scan.js +34 -27
  46. package/dist/plugins/internal/claude-resources/generate.d.ts +9 -0
  47. package/dist/plugins/internal/claude-resources/generate.js +188 -18
  48. package/dist/plugins/internal/claude-resources/index.d.ts +9 -0
  49. package/dist/plugins/internal/claude-resources/index.js +13 -1
  50. package/dist/plugins/internal/codex-resources/agents-md.js +15 -2
  51. package/dist/plugins/internal/codex-resources/agents.js +15 -2
  52. package/dist/plugins/internal/codex-resources/config.js +15 -2
  53. package/dist/plugins/internal/codex-resources/generate.d.ts +9 -0
  54. package/dist/plugins/internal/codex-resources/generate.js +19 -7
  55. package/dist/plugins/internal/codex-resources/hooks.js +15 -2
  56. package/dist/plugins/internal/codex-resources/index.d.ts +9 -0
  57. package/dist/plugins/internal/codex-resources/index.js +13 -1
  58. package/dist/plugins/internal/codex-resources/overview.js +136 -7
  59. package/dist/plugins/internal/codex-resources/rules.js +15 -2
  60. package/dist/plugins/internal/codex-resources/skills.js +16 -3
  61. package/dist/plugins/internal/llms-txt/assets.d.ts +24 -0
  62. package/dist/plugins/internal/llms-txt/assets.js +65 -0
  63. package/dist/plugins/internal/llms-txt/dev-middleware.d.ts +5 -0
  64. package/dist/plugins/internal/llms-txt/dev-middleware.js +9 -1
  65. package/dist/plugins/internal/llms-txt/emit.js +21 -5
  66. package/dist/plugins/internal/llms-txt/generate.d.ts +3 -3
  67. package/dist/plugins/internal/llms-txt/generate.js +55 -2
  68. package/dist/plugins/internal/llms-txt/index.d.ts +4 -1
  69. package/dist/plugins/internal/llms-txt/index.js +4 -0
  70. package/dist/plugins/internal/llms-txt/types.d.ts +24 -0
  71. package/dist/plugins/internal/llms-txt/types.js +4 -0
  72. package/dist/plugins/internal/resource-docs-shared/fs.d.ts +33 -0
  73. package/dist/plugins/internal/resource-docs-shared/fs.js +82 -1
  74. package/dist/plugins/internal/resource-docs-shared/index.d.ts +4 -2
  75. package/dist/plugins/internal/resource-docs-shared/index.js +21 -1
  76. package/dist/plugins/internal/resource-docs-shared/labels.d.ts +24 -0
  77. package/dist/plugins/internal/resource-docs-shared/labels.js +14 -0
  78. package/dist/plugins/internal/resource-docs-shared/locale-routes.d.ts +16 -0
  79. package/dist/plugins/internal/resource-docs-shared/locale-routes.js +14 -0
  80. package/dist/plugins/internal/resource-docs-shared/mdx.d.ts +7 -1
  81. package/dist/plugins/internal/resource-docs-shared/mdx.js +2 -2
  82. package/dist/plugins/internal/search-index/collect.js +46 -1
  83. package/dist/plugins/internal/search-index/types.d.ts +6 -1
  84. package/dist/plugins/llms-txt.js +9 -1
  85. package/dist/plugins/routes.js +14 -0
  86. package/dist/plugins/search-index.js +10 -1
  87. package/dist/preset.d.ts +9 -1
  88. package/dist/preset.js +25 -4
  89. package/dist/route-context/index.js +5 -17
  90. package/dist/route-enumerators/index.d.ts +14 -1
  91. package/dist/route-enumerators/index.js +38 -0
  92. package/dist/routes/locale-files-path.d.ts +29 -0
  93. package/dist/routes/locale-files-path.js +52 -0
  94. package/dist/safelist.css +1 -1
  95. package/dist/settings.d.ts +15 -0
  96. package/dist/tag-pages/index.js +2 -2
  97. package/eject/asset-components/asset-card.tsx +2 -0
  98. package/eject/asset-components/asset-code.tsx +2 -0
  99. package/eject/asset-components/shared.tsx +16 -0
  100. package/package.json +13 -9
  101. package/routes-src/locale-files-path.tsx +73 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,44 @@ All notable changes to `@takazudo/zudo-doc` are documented in this file.
4
4
 
5
5
  The format is based on Keep a Changelog, and release notes are generated from the changelog MDX pages.
6
6
 
7
+ ## [5.16.0] - 2026-09-02
8
+
9
+ ### Features
10
+
11
+ - Added opt-in Markdown/MDX transclusion through `transclude`, plus independent `assetViewerIndexing` controls for search, llms.txt, and sitemap output. (`8145aae41`)
12
+ - Generated asset-viewer pages can now be enumerated in sitemaps and indexed in search and llms outputs, with locale, base-path, exclusion, and bounded content-extraction handling. (`5da03612e`, `b48009d7e`, `3de313585`, `76b90bc48`)
13
+
14
+ ### Bug Fixes
15
+
16
+ - Long unbroken titles now wrap across documentation, home, asset, version, and tag pages. (`f21c6b73d`)
17
+ - Asset search excerpts now use bounded reads for large files. (`6c81a13b3`)
18
+ - Asset links in llms outputs now honor the configured `siteUrl`. (`67482a1d5`)
19
+
20
+ ### Other Changes
21
+
22
+ - Raised the optional `@takazudo/zudo-doc-history-server` peer floor to the already-published 5.15.0 release. (`01bd83bc7`)
23
+
24
+ ## [5.15.0] - 2026-09-01
25
+
26
+ ### Features
27
+
28
+ - Added an accessible language dropdown that scales to every configured locale instead of assuming a two-language site. (`607121e9`)
29
+ - Generated locale-owned Claude/Codex resource indexes and Asset Viewer routes for every configured locale, with translated page chrome, locale-preserving links, and safe handling of generated versus authored indexes. (`781fb820`, `8ceca53e`, `44ae7812`, `1e558854`, `5d682bc1`)
30
+ - Localized all seven `HtmlPreview` control labels and added per-preview `labels`, `showSource`, and `showViewportControls` overrides while preserving host-provided MDX bindings. (`ffee673f`, `6538184a`)
31
+ - Added `loading="visible"` for route-bound `HtmlPreview` instances, deferring iframe markup, scripts, and external resources until the reserved preview area enters the viewport. (`df63d981`)
32
+ - Kept auto-height previews synchronized after delayed content changes and viewport reflows, including both growth and shrink. (`df3d4b56`)
33
+ - Added escaped, nonblank language and title metadata to generated preview documents, with explicit author `<title>` content retaining precedence. (`2b8b0ee3`, `55e66300`)
34
+
35
+ ### Bug Fixes
36
+
37
+ - Retained package English strings when a configured locale supplies only partial translations. (`0a3619cb`)
38
+ - Preserved an explicit empty `sandbox` attribute in generated HTML so strict opaque previews remain sandboxed as authored. (`c8b1cac7`)
39
+ - Increased interactive `HtmlPreview` controls to accessible minimum hit-target dimensions. (`a35f1573`)
40
+
41
+ ### Other Changes
42
+
43
+ - Updated the zfb peer family to 2.14.2. (`5c5bfd2f`)
44
+
7
45
  ## [5.14.0] - 2026-08-31
8
46
 
9
47
  ### Features
@@ -23,6 +61,8 @@ The format is based on Keep a Changelog, and release notes are generated from th
23
61
 
24
62
  ### Other Changes
25
63
 
64
+ - Enabled Asset Viewer routes now localize automatically for existing multi-locale consumers after a package bump, unless `/${assetViewerRoutePrefix}/` is explicitly present in `defaultLocaleOnlyPrefixes`. Existing apps that already carry the legacy Claude/Codex resource prefixes remain default-locale-only after upgrading; remove the prefixes for resource sections you want generated in every configured locale. (#3820)
65
+ - Migration: before building with 5.14.0, remove or rename any authored locale resource overview/category `index.mdx` at a generator target and move custom titles, descriptions, and labels to `ZudoDocConfig.translations`. The generator refuses to overwrite an existing file unless its frontmatter contains `generated: true`. (#3820)
26
66
  - Updated the zfb peer family to 2.14.0, `@takazudo/zdtp` to 0.4.14, and the doc-history-server peer floor to 5.13.1. (`dbe8553c7`, `038870c94`, `95a82f15f`)
27
67
 
28
68
  ## [5.13.1] - 2026-08-28
@@ -2,6 +2,7 @@ import { jsx, jsxs } from "preact/jsx-runtime";
2
2
  import { assetRawHref, assetViewerHref } from "../asset-path/index.js";
3
3
  import {
4
4
  AssetFileIcon,
5
+ assetComponentViewerLocale,
5
6
  assetComponentText,
6
7
  formatAssetBytes,
7
8
  formatAssetLanguage,
@@ -23,7 +24,8 @@ function createAssetCard(context) {
23
24
  const viewerHref = assetViewerHref({
24
25
  base: context.base,
25
26
  routePrefix: context.routePrefix,
26
- path
27
+ path,
28
+ locale: assetComponentViewerLocale(context, path)
27
29
  });
28
30
  const rawHref = assetRawHref({ base: context.base, dir: context.dir, path });
29
31
  const details = [
@@ -2,6 +2,7 @@ import { jsx, jsxs } from "preact/jsx-runtime";
2
2
  import { assetViewerHref } from "../asset-path/index.js";
3
3
  import {
4
4
  AssetFileIcon,
5
+ assetComponentViewerLocale,
5
6
  assetComponentText,
6
7
  MissingAssetWarning,
7
8
  resolveAssetEntry
@@ -43,6 +44,7 @@ function createAssetCode(context) {
43
44
  base: context.base,
44
45
  routePrefix: context.routePrefix,
45
46
  path: resolved.path,
47
+ locale: assetComponentViewerLocale(context, resolved.path),
46
48
  fragment: excerpt.viewerLineAvailable ? `L${excerpt.startLine}` : void 0
47
49
  });
48
50
  const shown = Math.max(0, excerpt.endLine - excerpt.startLine + 1);
@@ -7,9 +7,14 @@ export interface AssetComponentContext {
7
7
  assetManifest: AssetManifest | null;
8
8
  routePrefix: string;
9
9
  dir: string;
10
+ /** Non-default locale segment for viewer links. */
11
+ locale?: string;
12
+ /** Match viewer paths that intentionally remain default-locale-only. */
13
+ isDefaultLocaleOnlyPath?: (path: string) => boolean;
10
14
  /** Locale-bound package translator. Omitted callers retain English fallbacks. */
11
15
  t?: (key: string) => string;
12
16
  }
17
+ export declare function assetComponentViewerLocale(context: AssetComponentContext, path: string): string | undefined;
13
18
  export declare function assetComponentText(context: AssetComponentContext, key: string, fallback: string, values?: Record<string, string | number>): string;
14
19
  export declare function resolveAssetEntry(src: string, context: AssetComponentContext): {
15
20
  path: string;
@@ -1,5 +1,11 @@
1
1
  import { jsx, jsxs } from "preact/jsx-runtime";
2
2
  import { decodeAuthoredHref } from "../asset-path/index.js";
3
+ function assetComponentViewerLocale(context, path) {
4
+ if (context.isDefaultLocaleOnlyPath?.(`/${context.routePrefix}/${path}`)) {
5
+ return void 0;
6
+ }
7
+ return context.locale;
8
+ }
3
9
  function assetComponentText(context, key, fallback, values = {}) {
4
10
  let text = context.t?.(key) ?? fallback;
5
11
  for (const [name, value] of Object.entries(values)) {
@@ -83,6 +89,7 @@ export {
83
89
  AssetFileIcon,
84
90
  MissingAssetWarning,
85
91
  assetComponentText,
92
+ assetComponentViewerLocale,
86
93
  formatAssetBytes,
87
94
  formatAssetLanguage,
88
95
  resolveAssetEntry
@@ -14,6 +14,7 @@ export interface AssetTreeNode {
14
14
  }
15
15
  export interface AssetIndexPageViewProps {
16
16
  entries: AssetIndexEntry[];
17
+ locale?: string;
17
18
  }
18
19
  /** Build a sorted directory tree and aggregate every directory's subtree totals. */
19
20
  export declare function buildAssetTree(entries: AssetIndexEntry[]): AssetTreeNode;
@@ -86,7 +86,7 @@ function iconFor(asset) {
86
86
  function countLabel(count, plural, single) {
87
87
  return (count === 1 ? single : plural).replace("{count}", String(count));
88
88
  }
89
- function AssetTree({ node, base, routePrefix, fileCountLabel, fileCountSingleLabel, linesLabel, root = false }) {
89
+ function AssetTree({ node, base, routePrefix, locale, fileCountLabel, fileCountSingleLabel, linesLabel, root = false }) {
90
90
  return /* @__PURE__ */ jsxs("ul", { "data-zd-asset-tree": root ? true : void 0, children: [
91
91
  node.dirs.map((dir) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("details", { open: true, children: [
92
92
  /* @__PURE__ */ jsxs("summary", { class: "flex cursor-pointer items-center gap-hsp-xs rounded px-hsp-sm py-vsp-3xs text-small hover:bg-accent/10 focus-visible:bg-accent/10", children: [
@@ -103,13 +103,13 @@ function AssetTree({ node, base, routePrefix, fileCountLabel, fileCountSingleLab
103
103
  formatAssetBytes(dir.bytes)
104
104
  ] })
105
105
  ] }),
106
- /* @__PURE__ */ jsx(AssetTree, { node: dir, base, routePrefix, fileCountLabel, fileCountSingleLabel, linesLabel })
106
+ /* @__PURE__ */ jsx(AssetTree, { node: dir, base, routePrefix, locale, fileCountLabel, fileCountSingleLabel, linesLabel })
107
107
  ] }) })),
108
108
  node.files.map((asset) => {
109
109
  const Icon = iconFor(asset);
110
110
  const facet = facetLabel(asset, linesLabel);
111
111
  const meta = [kindLabel(asset), facet, formatAssetBytes(asset.bytes)].filter(Boolean).join(" \xB7 ");
112
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("a", { href: assetViewerHref({ base, routePrefix, path: asset.path }), title: asset.name, class: "flex min-w-0 items-center gap-hsp-xs rounded px-hsp-sm py-vsp-3xs text-small text-fg hover:bg-accent/10 hover:text-accent hover:underline focus-visible:bg-accent/10 focus-visible:text-accent focus-visible:underline", children: [
112
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("a", { href: assetViewerHref({ base, routePrefix, path: asset.path, locale }), title: asset.name, class: "flex min-w-0 items-center gap-hsp-xs rounded px-hsp-sm py-vsp-3xs text-small text-fg hover:bg-accent/10 hover:text-accent hover:underline focus-visible:bg-accent/10 focus-visible:text-accent focus-visible:underline", children: [
113
113
  /* @__PURE__ */ jsx(Icon, { className: "h-icon-sm w-icon-sm shrink-0 text-muted" }),
114
114
  /* @__PURE__ */ jsx("span", { class: "min-w-0 truncate font-mono", children: basename(asset.path) }),
115
115
  /* @__PURE__ */ jsx("span", { class: "ml-auto hidden whitespace-nowrap pl-hsp-lg text-caption text-muted sm:block", children: meta })
@@ -120,17 +120,18 @@ function AssetTree({ node, base, routePrefix, fileCountLabel, fileCountSingleLab
120
120
  function createAssetIndexPageView(ctx) {
121
121
  assertChromeContext(ctx, "createAssetIndexPageView");
122
122
  const settings = ctx.settings;
123
- const locale = ctx.defaultLocale;
124
123
  const t = ctx.t;
125
124
  const composeMetaTitle = deriveComposeMetaTitle(ctx);
126
125
  const HeadWithDefaults = createHeadWithDefaults(ctx);
127
126
  const { Header: HeaderWithDefaults, Footer: FooterWithDefaults, Breadcrumb: BreadcrumbWithDefaults } = derivePrimaryChromeSlots(ctx);
128
127
  const BodyEndIslands = deriveBodyEndIslands(ctx);
129
128
  const dataThemePack = resolveThemePackSsrSlug(ctx.themePackRegistry, settings);
130
- return function AssetIndexPageView({ entries }) {
129
+ return function AssetIndexPageView({ entries, locale = ctx.defaultLocale }) {
130
+ const localeSegment = locale === ctx.defaultLocale ? void 0 : locale;
131
131
  const routePrefix = ctx.assetManifest?.routePrefix ?? settings.assetViewerRoutePrefix;
132
132
  const dir = ctx.assetManifest?.dir ?? settings.assetViewerDir;
133
- const indexUrl = ctx.withBase(`/${routePrefix}/`);
133
+ const indexUrl = ctx.withBase(`/${localeSegment ? `${localeSegment}/` : ""}${routePrefix}/`);
134
+ const homeUrl = ctx.withBase(`/${localeSegment ? `${localeSegment}/` : ""}`);
134
135
  const tree = buildAssetTree(entries);
135
136
  const folders = folderCount(tree);
136
137
  const fileCountLabel = t("asset.fileCount", locale);
@@ -138,13 +139,13 @@ function createAssetIndexPageView(ctx) {
138
139
  const folderCountLabel = t("asset.folderCount", locale);
139
140
  const folderCountSingleLabel = t("asset.folderCountSingle", locale);
140
141
  const title = t("asset.crumb", locale);
141
- return /* @__PURE__ */ jsx(DocLayoutWithDefaults, { title: composeMetaTitle(title), head: /* @__PURE__ */ jsx(HeadWithDefaults, { title, description: t("asset.indexDescription", locale), canonical: ctx.absoluteUrl(indexUrl) }), lang: locale, dataThemePack, noindex: settings.noindex, hideSidebar: true, hideToc: true, sidebarOverride: false, contentWide: true, breadcrumbOverride: /* @__PURE__ */ jsx(BreadcrumbWithDefaults, { items: [{ label: "", href: ctx.withBase("/") }, { label: title }] }), headerOverride: /* @__PURE__ */ jsx(HeaderWithDefaults, { lang: locale, currentPath: indexUrl, hideSidebarToggle: true }), footerOverride: /* @__PURE__ */ jsx(FooterWithDefaults, { lang: locale }), bodyEndComponents: /* @__PURE__ */ jsx(BodyEndIslands, { basePath: settings.base ?? "/" }), enableClientRouter: settings.dynamicPageTransition, children: /* @__PURE__ */ jsxs("div", { "data-zd-asset-index-page": true, children: [
142
+ return /* @__PURE__ */ jsx(DocLayoutWithDefaults, { title: composeMetaTitle(title), head: /* @__PURE__ */ jsx(HeadWithDefaults, { title, description: t("asset.indexDescription", locale), canonical: ctx.absoluteUrl(indexUrl) }), lang: locale, dataThemePack, noindex: settings.noindex, hideSidebar: true, hideToc: true, sidebarOverride: false, contentWide: true, breadcrumbOverride: /* @__PURE__ */ jsx(BreadcrumbWithDefaults, { items: [{ label: "", href: homeUrl }, { label: title }] }), headerOverride: /* @__PURE__ */ jsx(HeaderWithDefaults, { lang: locale, currentPath: indexUrl, hideSidebarToggle: true }), footerOverride: /* @__PURE__ */ jsx(FooterWithDefaults, { lang: locale }), bodyEndComponents: /* @__PURE__ */ jsx(BodyEndIslands, { basePath: settings.base ?? "/" }), enableClientRouter: settings.dynamicPageTransition, children: /* @__PURE__ */ jsxs("div", { "data-zd-asset-index-page": true, children: [
142
143
  /* @__PURE__ */ jsxs("header", { children: [
143
144
  /* @__PURE__ */ jsxs("div", { class: "mb-vsp-xs flex flex-wrap items-center gap-hsp-xs text-micro tracking-wide uppercase", children: [
144
145
  /* @__PURE__ */ jsx("span", { class: "rounded-full border border-muted px-hsp-sm py-vsp-3xs text-fg", children: title }),
145
146
  /* @__PURE__ */ jsx("span", { class: "rounded-full border border-muted px-hsp-sm py-vsp-3xs text-muted", children: t("asset.indexBadge", locale) })
146
147
  ] }),
147
- /* @__PURE__ */ jsx("h1", { class: "mb-vsp-xs border-b border-fg pb-vsp-xs font-mono text-heading font-bold leading-tight", children: title }),
148
+ /* @__PURE__ */ jsx("h1", { class: "mb-vsp-xs border-b border-fg pb-vsp-xs font-mono text-heading font-bold leading-tight break-words", children: title }),
148
149
  /* @__PURE__ */ jsxs("div", { "data-doc-metainfo": true, class: "mb-vsp-md flex flex-wrap items-center gap-x-hsp-md gap-y-vsp-2xs text-caption text-fg", children: [
149
150
  /* @__PURE__ */ jsx("span", { children: countLabel(tree.fileCount, fileCountLabel, fileCountSingleLabel) }),
150
151
  /* @__PURE__ */ jsx("span", { children: countLabel(folders, folderCountLabel, folderCountSingleLabel) }),
@@ -163,7 +164,7 @@ function createAssetIndexPageView(ctx) {
163
164
  /* @__PURE__ */ jsx("button", { type: "button", disabled: true, "data-zd-asset-index-action": "collapse", class: "text-fg hover:text-accent focus-visible:text-accent", children: t("asset.collapseAll", locale) })
164
165
  ] })
165
166
  ] }),
166
- entries.length > 0 ? /* @__PURE__ */ jsx(AssetTree, { root: true, node: tree, base: settings.base, routePrefix, fileCountLabel, fileCountSingleLabel, linesLabel: t("asset.lines", locale) }) : /* @__PURE__ */ jsx("p", { class: "text-small text-muted", "data-zd-asset-index-empty": true, children: t("asset.indexEmpty", locale) }),
167
+ entries.length > 0 ? /* @__PURE__ */ jsx(AssetTree, { root: true, node: tree, base: settings.base, routePrefix, locale: localeSegment, fileCountLabel, fileCountSingleLabel, linesLabel: t("asset.lines", locale) }) : /* @__PURE__ */ jsx("p", { class: "text-small text-muted", "data-zd-asset-index-empty": true, children: t("asset.indexEmpty", locale) }),
167
168
  /* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: ASSET_INDEX_PAGE_SCRIPT } })
168
169
  ] }) });
169
170
  };
@@ -8,6 +8,7 @@ export { ASSET_PAGE_SCRIPT } from "./script.js";
8
8
  export type { AssetRecord } from "../plugins/internal/asset-viewer/types.js";
9
9
  export interface AssetPageViewProps {
10
10
  entry: AssetRecord;
11
+ locale?: string;
11
12
  }
12
13
  export declare function AssetEyebrow({ asset, badge }: {
13
14
  asset: AssetRecord;
@@ -36,9 +37,17 @@ export declare function AssetCodeBody({ asset, copyLabel, wrapLabel, truncatedLa
36
37
  truncatedLabel: string;
37
38
  linesLabel?: string;
38
39
  }): VNode;
39
- export declare function AssetImageStage({ asset, rawUrl }: {
40
+ export interface AssetImageStageLabels {
41
+ fit: string;
42
+ actualSize: string;
43
+ checker: string;
44
+ dark: string;
45
+ enlarge: string;
46
+ }
47
+ export declare function AssetImageStage({ asset, rawUrl, labels }: {
40
48
  asset: AssetRecord;
41
49
  rawUrl: string;
50
+ labels: AssetImageStageLabels;
42
51
  }): VNode;
43
52
  export declare function AssetVideoStage({ asset, rawUrl }: {
44
53
  asset: AssetRecord;
@@ -60,8 +69,17 @@ export declare function AssetLinkedFrom({ asset, label }: {
60
69
  asset: AssetRecord;
61
70
  label: string;
62
71
  }): VNode | null;
63
- export declare function AssetDetails({ asset }: {
72
+ export interface AssetDetailsLabels {
73
+ heading: string;
74
+ type: string;
75
+ size: string;
76
+ path: string;
77
+ dimensions: string;
78
+ updated: string;
79
+ }
80
+ export declare function AssetDetails({ asset, labels }: {
64
81
  asset: AssetRecord;
82
+ labels: AssetDetailsLabels;
65
83
  }): VNode;
66
84
  /** Build the package-owned wide asset viewer page from a chrome context. */
67
85
  export declare function createAssetPageView<S extends Settings = Settings>(ctx: ChromeContext<S>): (props: AssetPageViewProps) => JSX.Element;
@@ -42,7 +42,7 @@ function AssetHeader({ asset, locale, badge, updatedLabel, linesLabel }) {
42
42
  const facet = facetLabel(asset, linesLabel);
43
43
  return /* @__PURE__ */ jsxs("header", { children: [
44
44
  /* @__PURE__ */ jsx(AssetEyebrow, { asset, badge }),
45
- /* @__PURE__ */ jsx("h1", { class: "mb-vsp-xs border-b border-fg pb-vsp-xs font-mono text-heading font-bold leading-tight", children: asset.name }),
45
+ /* @__PURE__ */ jsx("h1", { class: "mb-vsp-xs border-b border-fg pb-vsp-xs font-mono text-heading font-bold leading-tight break-words", children: asset.name }),
46
46
  /* @__PURE__ */ jsxs("div", { "data-doc-metainfo": true, class: "mb-vsp-md flex flex-wrap items-center gap-x-hsp-md gap-y-vsp-2xs text-caption text-fg", children: [
47
47
  asset.dir && /* @__PURE__ */ jsx("span", { children: asset.dir }),
48
48
  facet && /* @__PURE__ */ jsx("span", { children: facet }),
@@ -93,21 +93,21 @@ function AssetCodeBody({ asset, copyLabel, wrapLabel, truncatedLabel, linesLabel
93
93
  function EnlargeIcon() {
94
94
  return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fill: "none", stroke: "currentColor", "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M8 3H3v5m13-5h5v5M8 21H3v-5m13 5h5v-5M3 8l6-6m12 6-6-6M3 16l6 6m12-6-6 6" }) });
95
95
  }
96
- function AssetImageStage({ asset, rawUrl }) {
96
+ function AssetImageStage({ asset, rawUrl, labels }) {
97
97
  return /* @__PURE__ */ jsxs("section", { children: [
98
98
  /* @__PURE__ */ jsxs("div", { class: "mb-vsp-xs flex flex-wrap gap-hsp-sm text-caption", children: [
99
99
  /* @__PURE__ */ jsxs("div", { class: "flex rounded border border-muted", children: [
100
- /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": "true", "data-zd-asset-action": "fit", class: "px-hsp-sm py-vsp-3xs", children: "Fit" }),
101
- /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": "false", "data-zd-asset-action": "1to1", class: "border-l border-muted px-hsp-sm py-vsp-3xs", children: "1:1" })
100
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": "true", "data-zd-asset-action": "fit", class: "px-hsp-sm py-vsp-3xs", children: labels.fit }),
101
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": "false", "data-zd-asset-action": "1to1", class: "border-l border-muted px-hsp-sm py-vsp-3xs", children: labels.actualSize })
102
102
  ] }),
103
103
  /* @__PURE__ */ jsxs("div", { class: "flex rounded border border-muted", children: [
104
- /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": "true", "data-zd-asset-action": "checker", class: "px-hsp-sm py-vsp-3xs", children: "Checker" }),
105
- /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": "false", "data-zd-asset-action": "dark", class: "border-l border-muted px-hsp-sm py-vsp-3xs", children: "Dark" })
104
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": "true", "data-zd-asset-action": "checker", class: "px-hsp-sm py-vsp-3xs", children: labels.checker }),
105
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": "false", "data-zd-asset-action": "dark", class: "border-l border-muted px-hsp-sm py-vsp-3xs", children: labels.dark })
106
106
  ] })
107
107
  ] }),
108
108
  /* @__PURE__ */ jsxs("figure", { class: "zd-enlargeable zd-asset-stage is-checker flex min-h-[20rem] items-center justify-center overflow-auto rounded border border-muted bg-surface p-hsp-lg", children: [
109
109
  /* @__PURE__ */ jsx("img", { src: rawUrl, alt: asset.description ?? asset.name, width: asset.width, height: asset.height }),
110
- /* @__PURE__ */ jsx("button", { type: "button", class: "zd-enlarge-btn", hidden: true, "aria-label": "Enlarge image", children: /* @__PURE__ */ jsx(EnlargeIcon, {}) })
110
+ /* @__PURE__ */ jsx("button", { type: "button", class: "zd-enlarge-btn", hidden: true, "aria-label": labels.enlarge, children: /* @__PURE__ */ jsx(EnlargeIcon, {}) })
111
111
  ] })
112
112
  ] });
113
113
  }
@@ -147,12 +147,12 @@ function AssetLinkedFrom({ asset, label }) {
147
147
  ] })) })
148
148
  ] });
149
149
  }
150
- function AssetDetails({ asset }) {
151
- const rows = [["Type", asset.mime], ["Size", formatAssetBytes(asset.bytes)], ["Path", asset.path]];
152
- if (asset.width !== void 0 && asset.height !== void 0) rows.splice(1, 0, ["Dimensions", `${asset.width} \xD7 ${asset.height}`]);
153
- if (asset.updatedDate) rows.push(["Updated", asset.updatedDate]);
150
+ function AssetDetails({ asset, labels }) {
151
+ const rows = [[labels.type, asset.mime], [labels.size, formatAssetBytes(asset.bytes)], [labels.path, asset.path]];
152
+ if (asset.width !== void 0 && asset.height !== void 0) rows.splice(1, 0, [labels.dimensions, `${asset.width} \xD7 ${asset.height}`]);
153
+ if (asset.updatedDate) rows.push([labels.updated, asset.updatedDate]);
154
154
  return /* @__PURE__ */ jsxs("section", { children: [
155
- /* @__PURE__ */ jsx("h2", { class: "mb-vsp-xs text-title font-bold", children: "Details" }),
155
+ /* @__PURE__ */ jsx("h2", { class: "mb-vsp-xs text-title font-bold", children: labels.heading }),
156
156
  /* @__PURE__ */ jsx("dl", { class: "grid grid-cols-[auto_1fr] gap-x-hsp-md gap-y-vsp-2xs text-caption", children: rows.map(([term, value]) => /* @__PURE__ */ jsxs(Fragment, { children: [
157
157
  /* @__PURE__ */ jsx("dt", { class: "font-medium text-muted", children: term }),
158
158
  /* @__PURE__ */ jsx("dd", { class: "min-w-0 break-words text-fg", children: value })
@@ -171,53 +171,72 @@ function MediaLayout({ stage, details, linked }) {
171
171
  function createAssetPageView(ctx) {
172
172
  assertChromeContext(ctx, "createAssetPageView");
173
173
  const settings = ctx.settings;
174
- const locale = ctx.defaultLocale;
175
174
  const t = ctx.t;
176
175
  const composeMetaTitle = deriveComposeMetaTitle(ctx);
177
176
  const HeadWithDefaults = createHeadWithDefaults(ctx);
178
177
  const { Header: HeaderWithDefaults, Footer: FooterWithDefaults, Breadcrumb: BreadcrumbWithDefaults } = derivePrimaryChromeSlots(ctx);
179
178
  const BodyEndIslands = deriveBodyEndIslands(ctx);
180
179
  const dataThemePack = resolveThemePackSsrSlug(ctx.themePackRegistry, settings);
181
- return function AssetPageView({ entry: asset }) {
180
+ return function AssetPageView({ entry: asset, locale = ctx.defaultLocale }) {
181
+ const localeSegment = locale === ctx.defaultLocale ? void 0 : locale;
182
182
  const routePrefix = ctx.assetManifest?.routePrefix ?? settings.assetViewerRoutePrefix;
183
183
  const dir = ctx.assetManifest?.dir ?? settings.assetViewerDir;
184
- const viewerUrl = assetViewerHref({ base: settings.base, routePrefix, path: asset.path });
184
+ const viewerUrl = assetViewerHref({ base: settings.base, routePrefix, path: asset.path, locale: localeSegment });
185
185
  const rawUrl = assetRawHref({ base: settings.base, dir, path: asset.path });
186
186
  const linesLabel = t("asset.lines", locale).replace(
187
187
  "{count}",
188
188
  String(asset.lines ?? 0)
189
189
  );
190
190
  const dirSegments = asset.dir.split("/").filter(Boolean);
191
- const indexUrl = ctx.withBase(`/${routePrefix}/`);
191
+ const indexUrl = ctx.withBase(`/${localeSegment ? `${localeSegment}/` : ""}${routePrefix}/`);
192
+ const homeUrl = ctx.withBase(`/${localeSegment ? `${localeSegment}/` : ""}`);
193
+ const backLink = locale === ctx.defaultLocale ? asset.linkedFrom[0] : asset.linkedFrom.find((link) => link.locale === locale) ?? asset.linkedFrom[0];
192
194
  const breadcrumbItems = [
193
- { label: "", href: ctx.withBase("/") },
195
+ { label: "", href: homeUrl },
194
196
  { label: t("asset.crumb", locale), ...settings.assetViewerIndex ? { href: indexUrl } : {} },
195
197
  ...dirSegments.map((label) => ({ label })),
196
198
  { label: asset.name }
197
199
  ];
198
200
  const linked = /* @__PURE__ */ jsx(AssetLinkedFrom, { asset, label: t("asset.linkedFrom", locale) });
199
- const details = /* @__PURE__ */ jsx(AssetDetails, { asset });
201
+ const detailsLabels = {
202
+ heading: t("asset.details", locale),
203
+ type: t("asset.type", locale),
204
+ size: t("asset.size", locale),
205
+ path: t("asset.path", locale),
206
+ dimensions: t("asset.dimensions", locale),
207
+ updated: t("doc.updated", locale)
208
+ };
209
+ const imageStageLabels = {
210
+ fit: t("asset.fit", locale),
211
+ actualSize: t("asset.actualSize", locale),
212
+ checker: t("asset.checker", locale),
213
+ dark: t("asset.dark", locale),
214
+ enlarge: t("asset.enlarge", locale)
215
+ };
216
+ const details = /* @__PURE__ */ jsx(AssetDetails, { asset, labels: detailsLabels });
200
217
  const downloadPanel = /* @__PURE__ */ jsx(AssetDownloadPanel, { asset, rawUrl, noPreview: t("asset.noPreview", locale), downloadLabel: t("asset.download", locale), copyLabel: t("asset.copy", locale) });
201
218
  let body;
202
219
  const isMedia = asset.previewable && asset.sniffOk && ["image", "video", "pdf"].includes(asset.kind);
203
220
  if (!asset.previewable || !asset.sniffOk) body = /* @__PURE__ */ jsxs(Fragment, { children: [
204
221
  downloadPanel,
205
- /* @__PURE__ */ jsx(AssetDetails, { asset }),
222
+ /* @__PURE__ */ jsx(AssetDetails, { asset, labels: detailsLabels }),
206
223
  linked
207
224
  ] });
208
- else if (asset.kind === "image") body = /* @__PURE__ */ jsx(MediaLayout, { stage: /* @__PURE__ */ jsx(AssetImageStage, { asset, rawUrl }), details, linked });
225
+ else if (asset.kind === "image") body = /* @__PURE__ */ jsx(MediaLayout, { stage: /* @__PURE__ */ jsx(AssetImageStage, { asset, rawUrl, labels: imageStageLabels }), details, linked });
209
226
  else if (asset.kind === "video") body = /* @__PURE__ */ jsx(MediaLayout, { stage: /* @__PURE__ */ jsx(AssetVideoStage, { asset, rawUrl }), details, linked });
210
227
  else if (asset.kind === "pdf") body = /* @__PURE__ */ jsx(MediaLayout, { stage: /* @__PURE__ */ jsx(AssetPdfStage, { asset, rawUrl, children: downloadPanel }), details, linked });
211
228
  else body = /* @__PURE__ */ jsxs(Fragment, { children: [
212
229
  /* @__PURE__ */ jsx(AssetCodeBody, { asset, copyLabel: t("asset.copy", locale), wrapLabel: t("asset.wrap", locale), truncatedLabel: t("asset.truncated", locale), linesLabel }),
213
- /* @__PURE__ */ jsx(AssetDetails, { asset }),
230
+ /* @__PURE__ */ jsx(AssetDetails, { asset, labels: detailsLabels }),
214
231
  linked
215
232
  ] });
216
233
  const showSource = settings.bodyFootUtilArea !== false && settings.bodyFootUtilArea.viewSourceLink !== false;
217
234
  return /* @__PURE__ */ jsx(DocLayoutWithDefaults, { title: composeMetaTitle(asset.name), head: /* @__PURE__ */ jsx(HeadWithDefaults, { title: asset.name, description: asset.description, canonical: ctx.absoluteUrl(viewerUrl) }), lang: locale, dataThemePack, noindex: settings.noindex, hideSidebar: true, hideToc: true, sidebarOverride: false, contentWide: true, breadcrumbOverride: /* @__PURE__ */ jsx(BreadcrumbWithDefaults, { items: breadcrumbItems }), headerOverride: /* @__PURE__ */ jsx(HeaderWithDefaults, { lang: locale, currentPath: viewerUrl, hideSidebarToggle: true }), footerOverride: /* @__PURE__ */ jsx(FooterWithDefaults, { lang: locale }), bodyEndComponents: /* @__PURE__ */ jsx(BodyEndIslands, { basePath: settings.base ?? "/", forceImageEnlarge: asset.kind === "image" && asset.previewable && asset.sniffOk }), enableClientRouter: settings.dynamicPageTransition, children: /* @__PURE__ */ jsxs("div", { class: "zd-asset-page", "data-zd-asset-page": true, children: [
218
- asset.linkedFrom[0] && /* @__PURE__ */ jsx("p", { class: "mb-vsp-xs text-caption", children: /* @__PURE__ */ jsxs("a", { href: asset.linkedFrom[0].href, class: "text-muted hover:text-accent focus-visible:text-accent hover:underline focus-visible:underline", children: [
219
- "\u2190 Back to ",
220
- asset.linkedFrom[0].title
235
+ backLink && /* @__PURE__ */ jsx("p", { class: "mb-vsp-xs text-caption", children: /* @__PURE__ */ jsxs("a", { href: backLink.href, class: "text-muted hover:text-accent focus-visible:text-accent hover:underline focus-visible:underline", children: [
236
+ "\u2190 ",
237
+ t("asset.backTo", locale),
238
+ " ",
239
+ backLink.title
221
240
  ] }) }),
222
241
  /* @__PURE__ */ jsx(AssetHeader, { asset, locale, badge: t("asset.badge", locale), updatedLabel: t("doc.updated", locale), linesLabel }),
223
242
  /* @__PURE__ */ jsx(AssetActions, { rawUrl, downloadLabel: t("asset.download", locale), openRawLabel: t("asset.openRaw", locale), copyLabel: t("asset.copy", locale), wrapLabel: t("asset.wrap", locale), code: !isMedia && asset.previewable && asset.sniffOk }),
@@ -9,6 +9,8 @@ export interface AssetViewerHrefOptions {
9
9
  base: string;
10
10
  routePrefix: string;
11
11
  path: string;
12
+ /** Non-default locale segment. Omit for the unprefixed default locale. */
13
+ locale?: string;
12
14
  /** A fragment with or without its leading `#`. */
13
15
  fragment?: string;
14
16
  }
@@ -59,9 +59,10 @@ function decodeAuthoredHref(href, options) {
59
59
  }
60
60
  function assetViewerHref(options) {
61
61
  const routePrefix = normalizeSettingPath(options.routePrefix, "routePrefix");
62
+ const localePrefix = options.locale === void 0 ? "" : `/${encodeURIComponent(normalizeLocaleSegment(options.locale))}`;
62
63
  const href = withBase(
63
64
  options.base,
64
- `/${routePrefix}/${encodeAssetPathForUrl(options.path)}/`
65
+ `${localePrefix}/${routePrefix}/${encodeAssetPathForUrl(options.path)}/`
65
66
  );
66
67
  if (options.fragment == null || options.fragment === "") return href;
67
68
  if (CONTROL_CHARS.test(options.fragment)) {
@@ -69,6 +70,15 @@ function assetViewerHref(options) {
69
70
  }
70
71
  return href + (options.fragment.startsWith("#") ? options.fragment : `#${options.fragment}`);
71
72
  }
73
+ function normalizeLocaleSegment(locale) {
74
+ if (locale.length === 0 || locale.includes("/") || locale.includes("\\")) {
75
+ throw new Error("Asset viewer locale must be a non-empty URL segment");
76
+ }
77
+ if (locale === "." || locale === ".." || CONTROL_CHARS.test(locale)) {
78
+ throw new Error("Asset viewer locale must be a safe URL segment");
79
+ }
80
+ return locale.normalize("NFC");
81
+ }
72
82
  function assetRawHref(options) {
73
83
  const dir = normalizeSettingPath(options.dir, "dir");
74
84
  return withBase(options.base, `/${dir}/${encodeAssetPathForUrl(options.path)}`);
@@ -314,12 +314,6 @@ function deriveMdxComponents(ctx) {
314
314
  t: (key, locale) => ctx.t(key, locale),
315
315
  versionedDocsUrl: ctx.versionedDocsUrl
316
316
  });
317
- function HtmlPreviewBound(props) {
318
- return HtmlPreviewWrapper({
319
- globalConfig: ctx.settings.htmlPreview ?? null,
320
- ...props
321
- });
322
- }
323
317
  const assetComponentContext = {
324
318
  base: ctx.settings.base,
325
319
  assetManifest: ctx.assetManifest,
@@ -329,8 +323,37 @@ function deriveMdxComponents(ctx) {
329
323
  function createMdxComponentsBound(lang = ctx.defaultLocale, currentVersion, currentSlug = "") {
330
324
  const localizedAssetContext = {
331
325
  ...assetComponentContext,
326
+ locale: lang === ctx.defaultLocale ? void 0 : lang,
327
+ isDefaultLocaleOnlyPath: ctx.isDefaultLocaleOnlyPath,
332
328
  t: (key) => ctx.t(key, lang)
333
329
  };
330
+ const localizedHtmlPreviewLabels = {
331
+ mobile: ctx.t("htmlPreview.viewport.mobile", lang),
332
+ tablet: ctx.t("htmlPreview.viewport.tablet", lang),
333
+ full: ctx.t("htmlPreview.viewport.full", lang),
334
+ viewportSize: ctx.t("htmlPreview.viewport.label", lang),
335
+ showCode: ctx.t("htmlPreview.source.show", lang),
336
+ hideCode: ctx.t("htmlPreview.source.hide", lang),
337
+ preview: ctx.t("htmlPreview.iframe.title", lang)
338
+ };
339
+ function HtmlPreviewBound(props) {
340
+ const labels = props.labels;
341
+ const effectiveLang = props.lang?.trim() ? props.lang : lang.trim() ? lang : "en";
342
+ return HtmlPreviewWrapper({
343
+ globalConfig: ctx.settings.htmlPreview ?? null,
344
+ ...props,
345
+ lang: effectiveLang,
346
+ labels: {
347
+ mobile: labels?.mobile ?? localizedHtmlPreviewLabels.mobile,
348
+ tablet: labels?.tablet ?? localizedHtmlPreviewLabels.tablet,
349
+ full: labels?.full ?? localizedHtmlPreviewLabels.full,
350
+ viewportSize: labels?.viewportSize ?? localizedHtmlPreviewLabels.viewportSize,
351
+ showCode: labels?.showCode ?? localizedHtmlPreviewLabels.showCode,
352
+ hideCode: labels?.hideCode ?? localizedHtmlPreviewLabels.hideCode,
353
+ preview: labels?.preview ?? localizedHtmlPreviewLabels.preview
354
+ }
355
+ });
356
+ }
334
357
  const mdxExtrasDefault = {
335
358
  Asset: createAssetCard(localizedAssetContext),
336
359
  AssetCode: createAssetCode(localizedAssetContext),
@@ -348,6 +371,8 @@ function deriveMdxComponents(ctx) {
348
371
  return createMdxComponents({
349
372
  settings: ctx.settings,
350
373
  assetManifest: ctx.assetManifest,
374
+ assetViewerLocale: lang === ctx.defaultLocale ? void 0 : lang,
375
+ isDefaultLocaleOnlyPath: ctx.isDefaultLocaleOnlyPath,
351
376
  locale: lang,
352
377
  currentVersion,
353
378
  currentSlug,
package/dist/compiled.css CHANGED
@@ -658,6 +658,9 @@
658
658
  .min-h-\[20rem\] {
659
659
  min-height: 20rem;
660
660
  }
661
+ .min-h-\[44px\] {
662
+ min-height: 44px;
663
+ }
661
664
  .min-h-\[60vh\] {
662
665
  min-height: 60vh;
663
666
  }
@@ -754,6 +757,9 @@
754
757
  .w-icon-xs {
755
758
  width: var(--spacing-icon-xs);
756
759
  }
760
+ .max-w-\[16rem\] {
761
+ max-width: 16rem;
762
+ }
757
763
  .max-w-\[64rem\] {
758
764
  max-width: 64rem;
759
765
  }
@@ -772,6 +778,9 @@
772
778
  .max-w-\[calc\(100vw-2rem\)\] {
773
779
  max-width: calc(100vw - 2rem);
774
780
  }
781
+ .max-w-\[calc\(100vw-var\(--spacing-hsp-xl\)\)\] {
782
+ max-width: calc(100vw - var(--spacing-hsp-xl));
783
+ }
775
784
  .max-w-\[clamp\(50rem\,75vw\,90rem\)\] {
776
785
  max-width: clamp(50rem, 75vw, 90rem);
777
786
  }
@@ -793,6 +802,9 @@
793
802
  .min-w-\[10rem\] {
794
803
  min-width: 10rem;
795
804
  }
805
+ .min-w-\[44px\] {
806
+ min-width: 44px;
807
+ }
796
808
  .flex-1 {
797
809
  flex: 1;
798
810
  }
package/dist/config.d.ts CHANGED
@@ -18,11 +18,13 @@
18
18
  * `zudoDoc()` SHALLOW-merges the user's fields over {@link DEFAULT_SETTINGS} —
19
19
  * top-level fields only (`{ ...DEFAULT_SETTINGS, ...user }`). A supplied nested
20
20
  * object (e.g. `colorMode`, `metaTags`) REPLACES the default wholesale; it is
21
- * NOT deep-merged key-by-key. This is safe because every nested config type is
22
- * all-required-fields, so a caller supplying one supplies all of its fields.
23
- * ONE exception: `FaviconConfig` is all-optional wholesale replacement IS its
24
- * semantics (only the supplied slots emit a `<link rel="icon">`), and its
25
- * default is `undefined`, so there is nothing for a partial object to clobber.
21
+ * NOT deep-merged key-by-key. Nested config types that intentionally expose
22
+ * optional keys (such as `FrontmatterPreviewConfig` and
23
+ * `AssetViewerIndexingConfig`) get their per-key defaults at the read site;
24
+ * this shallow merge never fills nested keys. `FaviconConfig` is also
25
+ * all-optional wholesale replacement IS its semantics (only the supplied
26
+ * slots emit a `<link rel="icon">`), and its default is `undefined`, so there
27
+ * is nothing for a partial object to clobber.
26
28
  * `zudoDoc()` also supplies the Wave-3 package defaults
27
29
  * (`buildDocsSchema`/`directiveVocabulary`/`translations`/`colorSchemes`/tag
28
30
  * vocabulary) unless overridden, and returns a **complete `ZfbConfig`** — the
@@ -65,7 +67,7 @@ import type { ZfbConfig, BundleConfig } from "@takazudo/zfb/config";
65
67
  import type { ZodType } from "zod";
66
68
  import type { DirectiveVocabulary, PresetTranslations, PresetTagVocabularyEntry } from "./preset.js";
67
69
  import type { ColorScheme } from "./color-scheme-utils.js";
68
- import type { Settings, ColorModeConfig, LocaleConfig, MetaTagsConfig, SiteHeadConfig, TagPlacement, TagGovernanceMode, VersionConfig, FooterConfig, HeaderNavItem, HeaderRightItem, HomeConfig, FrontmatterPreviewConfig, BodyFootUtilAreaConfig, HtmlPreviewConfig, FaviconConfig } from "./settings.js";
70
+ import type { Settings, ColorModeConfig, LocaleConfig, MetaTagsConfig, SiteHeadConfig, TagPlacement, TagGovernanceMode, VersionConfig, FooterConfig, HeaderNavItem, HeaderRightItem, HomeConfig, AssetViewerIndexingConfig, FrontmatterPreviewConfig, BodyFootUtilAreaConfig, HtmlPreviewConfig, FaviconConfig } from "./settings.js";
69
71
  /** The `settings.claudeResources` block (or `false` when disabled). */
70
72
  type ClaudeResourcesConfig = {
71
73
  claudeDir: string;
@@ -188,6 +190,11 @@ export interface ZudoDocConfig {
188
190
  * @default true
189
191
  */
190
192
  mermaid?: boolean;
193
+ /**
194
+ * Enable transclusion of other Markdown/MDX files via `:::include`.
195
+ * @default false
196
+ */
197
+ transclude?: boolean;
191
198
  /**
192
199
  * Add `noindex,nofollow` to every page (for internal docs).
193
200
  * @default false
@@ -377,6 +384,8 @@ export interface ZudoDocConfig {
377
384
  assetViewerExclude?: string[];
378
385
  /** When `true` (and `assetViewer` is `true`), generate a listing page at `/<assetViewerRoutePrefix>/` showing every managed asset as a folder tree. No effect when `assetViewer` is `false`. @default false */
379
386
  assetViewerIndex?: boolean;
387
+ /** Per-output opt-in controls for indexing generated asset-viewer pages. Omitted keys are off; `false` disables all asset-page indexing. @default false */
388
+ assetViewerIndexing?: AssetViewerIndexingConfig | false;
380
389
  /**
381
390
  * Body-foot utility area (doc-history / view-source), or `false` to disable.
382
391
  * @default false
package/dist/config.js CHANGED
@@ -53,6 +53,7 @@ const DEFAULT_SETTINGS = {
53
53
  defaultLocale: "en",
54
54
  locales: {},
55
55
  mermaid: true,
56
+ transclude: false,
56
57
  noindex: false,
57
58
  editUrl: false,
58
59
  githubUrl: false,
@@ -98,6 +99,7 @@ const DEFAULT_SETTINGS = {
98
99
  assetViewerRoutePrefix: "files",
99
100
  assetViewerExclude: [],
100
101
  assetViewerIndex: false,
102
+ assetViewerIndexing: false,
101
103
  bodyFootUtilArea: false,
102
104
  htmlPreview: void 0,
103
105
  versions: false,
@@ -9,10 +9,14 @@ export interface CreateContentLinkOptions {
9
9
  assetManifest: AssetManifest | null;
10
10
  routePrefix: string;
11
11
  dir: string;
12
+ /** Non-default locale segment used by asset-viewer links. */
13
+ locale?: string;
14
+ /** Match viewer paths that intentionally remain default-locale-only. */
15
+ isDefaultLocaleOnlyPath?: (path: string) => boolean;
12
16
  }
13
17
  /**
14
18
  * Bind ContentLink to an asset manifest. Only exact manifest entries are
15
19
  * decorated and redirected; every other anchor follows ContentLink verbatim.
16
20
  */
17
- export declare function createContentLink({ base, assetManifest, routePrefix, dir, }: CreateContentLinkOptions): (props: Props) => JSX.Element;
21
+ export declare function createContentLink({ base, assetManifest, routePrefix, dir, locale, isDefaultLocaleOnlyPath, }: CreateContentLinkOptions): (props: Props) => JSX.Element;
18
22
  export {};