@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
@@ -6,6 +6,21 @@ export interface CodeBlockData {
6
6
  title: string;
7
7
  code: string;
8
8
  }
9
+ /**
10
+ * Localized strings rendered by the HTML preview controls.
11
+ *
12
+ * Each public layer accepts a partial bag and resolves missing values against
13
+ * the English defaults before rendering or forwarding the preview.
14
+ */
15
+ export interface HtmlPreviewLabels {
16
+ mobile: string;
17
+ tablet: string;
18
+ full: string;
19
+ viewportSize: string;
20
+ showCode: string;
21
+ hideCode: string;
22
+ preview: string;
23
+ }
9
24
  export interface PreviewBaseProps {
10
25
  title?: string;
11
26
  height?: number;
@@ -14,6 +29,11 @@ export interface PreviewBaseProps {
14
29
  syncDelay: number;
15
30
  codeBlocks: CodeBlockData[];
16
31
  defaultOpen?: boolean;
32
+ labels?: Partial<HtmlPreviewLabels>;
33
+ showSource?: boolean;
34
+ showViewportControls?: boolean;
35
+ /** Internal opt-out for documents whose height derives from the iframe. */
36
+ autoHeight?: boolean;
17
37
  }
18
38
  /**
19
39
  * Interactive preview base: iframe viewport switcher + collapsible code
@@ -22,4 +42,4 @@ export interface PreviewBaseProps {
22
42
  * JSX port of src/components/html-preview/preview-base.tsx with
23
43
  * React → Preact hook imports and `className` → `class` attribute.
24
44
  */
25
- export declare function PreviewBase({ title, height, srcdoc, sandbox, syncDelay, codeBlocks, defaultOpen, }: PreviewBaseProps): VNode;
45
+ export declare function PreviewBase({ title, height, srcdoc, sandbox, syncDelay, codeBlocks, defaultOpen, labels, showSource, showViewportControls, autoHeight, }: PreviewBaseProps): VNode;
@@ -1,11 +1,37 @@
1
1
  import { jsx, jsxs } from "preact/jsx-runtime";
2
- import { useCallback, useEffect, useRef, useState } from "preact/hooks";
2
+ import { useEffect, useRef, useState } from "preact/hooks";
3
3
  import { HighlightedCode } from "./highlighted-code.js";
4
- const VIEWPORTS = [
5
- { label: "Mobile", width: "320px" },
6
- { label: "Tablet", width: "768px" },
7
- { label: "Full", width: "100%" }
8
- ];
4
+ import {
5
+ createPreviewAutoHeightController
6
+ } from "./preview-auto-height.js";
7
+ const DEFAULT_LABELS = {
8
+ mobile: "Mobile",
9
+ tablet: "Tablet",
10
+ full: "Full",
11
+ viewportSize: "Viewport size",
12
+ showCode: "Show code",
13
+ hideCode: "Hide code",
14
+ preview: "Preview"
15
+ };
16
+ function resolveLabels(labels) {
17
+ return {
18
+ mobile: labels?.mobile ?? DEFAULT_LABELS.mobile,
19
+ tablet: labels?.tablet ?? DEFAULT_LABELS.tablet,
20
+ full: labels?.full ?? DEFAULT_LABELS.full,
21
+ viewportSize: labels?.viewportSize ?? DEFAULT_LABELS.viewportSize,
22
+ showCode: labels?.showCode ?? DEFAULT_LABELS.showCode,
23
+ hideCode: labels?.hideCode ?? DEFAULT_LABELS.hideCode,
24
+ preview: labels?.preview ?? DEFAULT_LABELS.preview
25
+ };
26
+ }
27
+ function buildViewports(labels) {
28
+ return [
29
+ { label: labels.mobile, width: "320px" },
30
+ { label: labels.tablet, width: "768px" },
31
+ { label: labels.full, width: "100%" }
32
+ ];
33
+ }
34
+ const DEFAULT_VIEWPORTS = buildViewports(DEFAULT_LABELS);
9
35
  function PreviewBase({
10
36
  title,
11
37
  height,
@@ -13,61 +39,84 @@ function PreviewBase({
13
39
  sandbox,
14
40
  syncDelay,
15
41
  codeBlocks,
16
- defaultOpen
42
+ defaultOpen,
43
+ labels,
44
+ showSource,
45
+ showViewportControls,
46
+ autoHeight
17
47
  }) {
48
+ const resolvedLabels = resolveLabels(labels);
49
+ const sourceVisible = showSource ?? true;
50
+ const viewportControlsVisible = showViewportControls ?? true;
51
+ const viewports = viewportControlsVisible ? buildViewports(resolvedLabels) : DEFAULT_VIEWPORTS;
18
52
  const [activeViewport, setActiveViewport] = useState(2);
19
- const [codeOpen, setCodeOpen] = useState(defaultOpen ?? false);
53
+ const [codeOpen, setCodeOpen] = useState(
54
+ sourceVisible && (defaultOpen ?? false)
55
+ );
20
56
  const [iframeHeight, setIframeHeight] = useState(height ?? 200);
21
57
  const iframeRef = useRef(null);
22
- const syncHeight = useCallback(() => {
58
+ const iframeHeightRef = useRef(iframeHeight);
59
+ const autoHeightControllerRef = useRef(
60
+ null
61
+ );
62
+ iframeHeightRef.current = iframeHeight;
63
+ const autoHeightEnabled = (autoHeight ?? true) && height == null;
64
+ useEffect(() => {
23
65
  const iframe = iframeRef.current;
24
- if (!iframe || height != null) return;
66
+ if (!iframe || !autoHeightEnabled) return;
67
+ const controller = createPreviewAutoHeightController({
68
+ iframe,
69
+ syncDelay,
70
+ getCurrentHeight: () => iframeHeightRef.current,
71
+ setHeight: (nextHeight) => {
72
+ iframeHeightRef.current = nextHeight;
73
+ setIframeHeight(nextHeight);
74
+ }
75
+ });
76
+ autoHeightControllerRef.current = controller;
77
+ const onLoad = () => controller.handleLoad();
78
+ iframe.addEventListener("load", onLoad);
25
79
  try {
26
- const doc = iframe.contentDocument;
27
- if (doc?.body) {
28
- const h = doc.body.scrollHeight;
29
- if (h > 0) setIframeHeight(Math.max(h + 16, 200));
80
+ if (iframe.contentDocument?.readyState === "complete") {
81
+ controller.handleLoad();
30
82
  }
31
83
  } catch {
32
84
  }
33
- }, [height]);
34
- useEffect(() => {
35
- const iframe = iframeRef.current;
36
- if (!iframe) return;
37
- let timeoutId;
38
- const onLoad = () => {
39
- if (syncDelay > 0) {
40
- timeoutId = setTimeout(syncHeight, syncDelay);
41
- } else {
42
- syncHeight();
43
- }
44
- };
45
- iframe.addEventListener("load", onLoad);
46
85
  return () => {
47
86
  iframe.removeEventListener("load", onLoad);
48
- clearTimeout(timeoutId);
87
+ controller.destroy();
88
+ if (autoHeightControllerRef.current === controller) {
89
+ autoHeightControllerRef.current = null;
90
+ }
49
91
  };
50
- }, [syncHeight, srcdoc, syncDelay]);
92
+ }, [autoHeightEnabled, srcdoc, syncDelay]);
51
93
  useEffect(() => {
52
- if (height != null) return;
53
- const id = setTimeout(syncHeight, 150);
54
- return () => clearTimeout(id);
55
- }, [activeViewport, syncHeight, height]);
56
- const containerWidth = VIEWPORTS[activeViewport]?.width ?? "100%";
94
+ if (!autoHeightEnabled) return;
95
+ autoHeightControllerRef.current?.schedule();
96
+ }, [activeViewport, autoHeightEnabled]);
97
+ const containerWidth = viewportControlsVisible ? viewports[activeViewport]?.width ?? "100%" : "100%";
57
98
  return /* @__PURE__ */ jsxs("div", { class: "border border-muted rounded-lg overflow-hidden my-vsp-md", children: [
58
- /* @__PURE__ */ jsxs("div", { class: "flex items-center justify-between px-hsp-md py-hsp-sm bg-surface border-b border-muted gap-hsp-sm flex-wrap", children: [
99
+ (viewportControlsVisible || title) && /* @__PURE__ */ jsxs("div", { class: "flex items-center justify-between px-hsp-md py-hsp-sm bg-surface border-b border-muted gap-hsp-sm flex-wrap", children: [
59
100
  title && /* @__PURE__ */ jsx("span", { class: "text-caption font-semibold text-fg", children: title }),
60
- /* @__PURE__ */ jsx("div", { class: "flex gap-hsp-2xs", role: "group", "aria-label": "Viewport size", children: VIEWPORTS.map((vp, i) => /* @__PURE__ */ jsx(
61
- "button",
101
+ viewportControlsVisible && /* @__PURE__ */ jsx(
102
+ "div",
62
103
  {
63
- type: "button",
64
- class: `px-hsp-sm py-hsp-2xs text-caption border rounded-full cursor-pointer transition-[background,color,border-color] duration-150 leading-snug ${i === activeViewport ? "bg-accent text-bg border-accent hover:bg-accent-hover hover:border-accent-hover" : "bg-transparent text-muted border-muted hover:bg-[color-mix(in_srgb,var(--color-surface)_80%,var(--color-fg)_20%)]"}`,
65
- "aria-pressed": i === activeViewport,
66
- onClick: () => setActiveViewport(i),
67
- children: vp.label
68
- },
69
- vp.label
70
- )) })
104
+ class: "flex gap-hsp-2xs",
105
+ role: "group",
106
+ "aria-label": resolvedLabels.viewportSize,
107
+ children: viewports.map((vp, i) => /* @__PURE__ */ jsx(
108
+ "button",
109
+ {
110
+ type: "button",
111
+ class: `min-h-[44px] min-w-[44px] px-hsp-sm py-hsp-2xs text-caption border rounded-full cursor-pointer transition-[background,color,border-color] duration-150 leading-snug ${i === activeViewport ? "bg-accent text-bg border-accent hover:bg-accent-hover hover:border-accent-hover" : "bg-transparent text-muted border-muted hover:bg-[color-mix(in_srgb,var(--color-surface)_80%,var(--color-fg)_20%)]"}`,
112
+ "aria-pressed": i === activeViewport,
113
+ onClick: () => setActiveViewport(i),
114
+ children: vp.label
115
+ },
116
+ vp.label
117
+ ))
118
+ }
119
+ )
71
120
  ] }),
72
121
  /* @__PURE__ */ jsx("div", { class: "bg-surface p-hsp-lg", children: /* @__PURE__ */ jsx(
73
122
  "div",
@@ -81,18 +130,18 @@ function PreviewBase({
81
130
  class: "block w-full border-none bg-[#fff] rounded shadow-[0_1px_3px_color-mix(in_srgb,var(--color-fg)_8%,transparent)]",
82
131
  srcDoc: srcdoc,
83
132
  sandbox,
84
- style: { height: iframeHeight },
85
- title: title ?? "Preview"
133
+ style: { height: height ?? iframeHeight },
134
+ title: title ?? resolvedLabels.preview
86
135
  }
87
136
  )
88
137
  }
89
138
  ) }),
90
- /* @__PURE__ */ jsxs("div", { class: "border-t border-muted", children: [
139
+ sourceVisible && /* @__PURE__ */ jsxs("div", { class: "border-t border-muted", children: [
91
140
  /* @__PURE__ */ jsxs(
92
141
  "button",
93
142
  {
94
143
  type: "button",
95
- class: "flex items-center w-full px-hsp-md py-hsp-sm text-caption font-medium text-muted bg-surface border-none cursor-pointer gap-hsp-xs hover:bg-[color-mix(in_srgb,var(--color-surface)_80%,var(--color-fg)_20%)]",
144
+ class: "flex min-h-[44px] min-w-[44px] items-center w-full px-hsp-md py-hsp-sm text-caption font-medium text-muted bg-surface border-none cursor-pointer gap-hsp-xs hover:bg-[color-mix(in_srgb,var(--color-surface)_80%,var(--color-fg)_20%)]",
96
145
  onClick: () => setCodeOpen((v) => !v),
97
146
  "aria-expanded": codeOpen,
98
147
  children: [
@@ -104,7 +153,7 @@ function PreviewBase({
104
153
  children: "\u25B6"
105
154
  }
106
155
  ),
107
- codeOpen ? "Hide code" : "Show code"
156
+ codeOpen ? resolvedLabels.hideCode : resolvedLabels.showCode
108
157
  ]
109
158
  }
110
159
  ),
@@ -8,9 +8,11 @@
8
8
  * Locale code → key → translated string. The `en` and `ja` tables carry the
9
9
  * complete package surface; `de` remains the documented partial locale. The
10
10
  * other locales in the generator's `SUPPORTED_LANGS` list
11
- * (zh-cn/zh-tw/ko/es/fr/pt) fall back to `en` at lookup time (the template's
12
- * `t()` helper does `translations[locale]?.[key] ?? translations[defaultLocale]?.[key] ?? key`)
13
- * rather than shipping their own fully-translated table here.
11
+ * (zh-cn/zh-tw/ko/es/fr/pt) fall back through the requested locale, configured
12
+ * default locale, package English, and finally the raw key at lookup time (the
13
+ * `t()` helper does `translations[locale]?.[key] ??
14
+ * translations[defaultLocale]?.[key] ?? translations.en?.[key] ?? key`) rather
15
+ * than shipping their own fully-translated table here.
14
16
  *
15
17
  * Locale label/derivation (mapping a locale code to its display label,
16
18
  * detecting the active locale from a URL, etc.) stays settings-driven and is
@@ -25,15 +25,48 @@ const defaultTranslations = {
25
25
  "code.copy": "Copy code",
26
26
  "code.copied": "Copied!",
27
27
  "code.wrapToggle": "Toggle word wrap",
28
+ "htmlPreview.viewport.mobile": "Mobile",
29
+ "htmlPreview.viewport.tablet": "Tablet",
30
+ "htmlPreview.viewport.full": "Full",
31
+ "htmlPreview.viewport.label": "Viewport size",
32
+ "htmlPreview.source.show": "Show code",
33
+ "htmlPreview.source.hide": "Hide code",
34
+ "htmlPreview.iframe.title": "Preview",
28
35
  "doc.editPage": "Edit this page",
29
36
  "doc.viewSource": "View source on GitHub",
30
37
  "header.github": "GitHub repository",
38
+ "language.switcher.label": "Language",
31
39
  "doc.tags": "Tags",
32
40
  "doc.taggedWith": "Pages tagged with",
33
41
  "doc.allTags": "All Tags",
34
42
  "doc.seeAllTags": "See all tags",
35
43
  "doc.created": "Created",
36
44
  "doc.updated": "Updated",
45
+ "resource.claudeMd.label": "CLAUDE.md",
46
+ "resource.claudeMd.description": "Project-specific instructions",
47
+ "resource.claudeCommands.label": "Commands",
48
+ "resource.claudeCommands.description": "Custom slash commands",
49
+ "resource.claudeSkills.label": "Skills",
50
+ "resource.claudeSkills.description": "Skill packages",
51
+ "resource.claudeAgents.label": "Agents",
52
+ "resource.claudeAgents.description": "Custom subagents",
53
+ "resource.codexAgentsMd.label": "AGENTS.md",
54
+ "resource.codexAgentsMd.description": "Project instructions for Codex",
55
+ "resource.codexConfig.label": "Config",
56
+ "resource.codexConfig.description": "config.toml and profiles",
57
+ "resource.codexAgents.label": "Agents",
58
+ "resource.codexAgents.description": "Custom subagents",
59
+ "resource.codexHooks.label": "Hooks",
60
+ "resource.codexHooks.description": "Lifecycle hooks",
61
+ "resource.codexRules.label": "Rules",
62
+ "resource.codexRules.description": "Command approval rules",
63
+ "resource.codexSkills.label": "Skills",
64
+ "resource.codexSkills.description": "Skill packages",
65
+ "resource.claude.title": "Claude",
66
+ "resource.claude.description": "Claude Code configuration reference.",
67
+ "resource.codex.title": "Codex",
68
+ "resource.codex.description": "OpenAI Codex configuration reference.",
69
+ "resource.resources": "Resources",
37
70
  "asset.badge": "Asset",
38
71
  "asset.crumb": "Assets",
39
72
  "asset.indexBadge": "Index",
@@ -55,6 +88,17 @@ const defaultTranslations = {
55
88
  "asset.viewFullFile": "View full file",
56
89
  "asset.showingLines": "Showing {shown} of {total} lines",
57
90
  "asset.truncated": "Preview truncated \u2014 download the file for the rest.",
91
+ "asset.details": "Details",
92
+ "asset.type": "Type",
93
+ "asset.size": "Size",
94
+ "asset.path": "Path",
95
+ "asset.dimensions": "Dimensions",
96
+ "asset.backTo": "Back to",
97
+ "asset.fit": "Fit",
98
+ "asset.actualSize": "1:1",
99
+ "asset.checker": "Checker",
100
+ "asset.dark": "Dark",
101
+ "asset.enlarge": "Enlarge image",
58
102
  "doc.noTags": "No tags found.",
59
103
  "doc.pageCount": "{count} pages",
60
104
  "doc.pageCountSingle": "{count} page",
@@ -107,15 +151,48 @@ const defaultTranslations = {
107
151
  "code.copy": "\u30B3\u30FC\u30C9\u3092\u30B3\u30D4\u30FC",
108
152
  "code.copied": "\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F\uFF01",
109
153
  "code.wrapToggle": "\u6298\u308A\u8FD4\u3057\u5207\u66FF",
154
+ "htmlPreview.viewport.mobile": "\u30E2\u30D0\u30A4\u30EB",
155
+ "htmlPreview.viewport.tablet": "\u30BF\u30D6\u30EC\u30C3\u30C8",
156
+ "htmlPreview.viewport.full": "\u30D5\u30EB",
157
+ "htmlPreview.viewport.label": "\u30D3\u30E5\u30FC\u30DD\u30FC\u30C8\u30B5\u30A4\u30BA",
158
+ "htmlPreview.source.show": "\u30B3\u30FC\u30C9\u3092\u8868\u793A",
159
+ "htmlPreview.source.hide": "\u30B3\u30FC\u30C9\u3092\u975E\u8868\u793A",
160
+ "htmlPreview.iframe.title": "\u30D7\u30EC\u30D3\u30E5\u30FC",
110
161
  "doc.editPage": "\u3053\u306E\u30DA\u30FC\u30B8\u3092\u7DE8\u96C6",
111
162
  "doc.viewSource": "GitHub \u3067\u30BD\u30FC\u30B9\u3092\u898B\u308B",
112
163
  "header.github": "GitHub \u30EA\u30DD\u30B8\u30C8\u30EA",
164
+ "language.switcher.label": "\u8A00\u8A9E",
113
165
  "doc.tags": "\u30BF\u30B0",
114
166
  "doc.taggedWith": "\u30BF\u30B0\u4ED8\u304D\u30DA\u30FC\u30B8",
115
167
  "doc.allTags": "\u3059\u3079\u3066\u306E\u30BF\u30B0",
116
168
  "doc.seeAllTags": "\u3059\u3079\u3066\u306E\u30BF\u30B0\u3092\u898B\u308B",
117
169
  "doc.created": "\u4F5C\u6210",
118
170
  "doc.updated": "\u66F4\u65B0",
171
+ "resource.claudeMd.label": "CLAUDE.md",
172
+ "resource.claudeMd.description": "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u56FA\u6709\u306E\u6307\u793A",
173
+ "resource.claudeCommands.label": "\u30B3\u30DE\u30F3\u30C9",
174
+ "resource.claudeCommands.description": "\u30AB\u30B9\u30BF\u30E0\u30B9\u30E9\u30C3\u30B7\u30E5\u30B3\u30DE\u30F3\u30C9",
175
+ "resource.claudeSkills.label": "\u30B9\u30AD\u30EB",
176
+ "resource.claudeSkills.description": "\u30B9\u30AD\u30EB\u30D1\u30C3\u30B1\u30FC\u30B8",
177
+ "resource.claudeAgents.label": "\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8",
178
+ "resource.claudeAgents.description": "\u30AB\u30B9\u30BF\u30E0\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8",
179
+ "resource.codexAgentsMd.label": "AGENTS.md",
180
+ "resource.codexAgentsMd.description": "Codex \u5411\u3051\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u6307\u793A",
181
+ "resource.codexConfig.label": "\u8A2D\u5B9A",
182
+ "resource.codexConfig.description": "config.toml \u3068\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB",
183
+ "resource.codexAgents.label": "\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8",
184
+ "resource.codexAgents.description": "\u30AB\u30B9\u30BF\u30E0\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8",
185
+ "resource.codexHooks.label": "\u30D5\u30C3\u30AF",
186
+ "resource.codexHooks.description": "\u30E9\u30A4\u30D5\u30B5\u30A4\u30AF\u30EB\u30D5\u30C3\u30AF",
187
+ "resource.codexRules.label": "\u30EB\u30FC\u30EB",
188
+ "resource.codexRules.description": "\u30B3\u30DE\u30F3\u30C9\u627F\u8A8D\u30EB\u30FC\u30EB",
189
+ "resource.codexSkills.label": "\u30B9\u30AD\u30EB",
190
+ "resource.codexSkills.description": "\u30B9\u30AD\u30EB\u30D1\u30C3\u30B1\u30FC\u30B8",
191
+ "resource.claude.title": "Claude",
192
+ "resource.claude.description": "Claude Code \u306E\u8A2D\u5B9A\u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u3002",
193
+ "resource.codex.title": "Codex",
194
+ "resource.codex.description": "OpenAI Codex \u306E\u8A2D\u5B9A\u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u3002",
195
+ "resource.resources": "\u30EA\u30BD\u30FC\u30B9",
119
196
  "asset.badge": "\u30A2\u30BB\u30C3\u30C8",
120
197
  "asset.crumb": "\u30A2\u30BB\u30C3\u30C8",
121
198
  "asset.indexBadge": "\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9",
@@ -137,6 +214,17 @@ const defaultTranslations = {
137
214
  "asset.viewFullFile": "\u30D5\u30A1\u30A4\u30EB\u5168\u4F53\u3092\u898B\u308B",
138
215
  "asset.showingLines": "{total} \u884C\u4E2D {shown} \u884C\u3092\u8868\u793A",
139
216
  "asset.truncated": "\u30D7\u30EC\u30D3\u30E5\u30FC\u306F\u7701\u7565\u3055\u308C\u3066\u3044\u307E\u3059\u3002\u5168\u4F53\u306F\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
217
+ "asset.details": "\u8A73\u7D30",
218
+ "asset.type": "\u7A2E\u985E",
219
+ "asset.size": "\u30B5\u30A4\u30BA",
220
+ "asset.path": "\u30D1\u30B9",
221
+ "asset.dimensions": "\u5BF8\u6CD5",
222
+ "asset.backTo": "\u623B\u308B",
223
+ "asset.fit": "\u5168\u4F53\u8868\u793A",
224
+ "asset.actualSize": "1:1",
225
+ "asset.checker": "\u30C1\u30A7\u30C3\u30AB\u30FC",
226
+ "asset.dark": "\u30C0\u30FC\u30AF",
227
+ "asset.enlarge": "\u753B\u50CF\u3092\u62E1\u5927",
140
228
  "doc.noTags": "\u30BF\u30B0\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002",
141
229
  "doc.pageCount": "{count}\u30DA\u30FC\u30B8",
142
230
  "doc.pageCountSingle": "{count}\u30DA\u30FC\u30B8",
@@ -192,6 +280,7 @@ const defaultTranslations = {
192
280
  "doc.editPage": "Diese Seite bearbeiten",
193
281
  "doc.viewSource": "Quellcode auf GitHub ansehen",
194
282
  "header.github": "GitHub-Repository",
283
+ "language.switcher.label": "Sprache",
195
284
  "doc.tags": "Tags",
196
285
  "doc.taggedWith": "Seiten mit Tag",
197
286
  "doc.allTags": "Alle Tags",
@@ -37,9 +37,10 @@ export declare function switchLocaleHref(pathname: string, config: LanguageSwitc
37
37
  * first load and re-runs {@link switchLocaleHref} against the live pathname —
38
38
  * the same rebind-on-navigate contract `VERSION_SWITCHER_INIT_SCRIPT` uses.
39
39
  *
40
- * `window[FLAG]` makes it idempotent: the tag may re-execute on a hard reload
41
- * or a cross-locale header repaint, but the listener is registered exactly
42
- * once per page lifetime.
40
+ * `window[FLAG]` stores the live-DOM refresh function: the tag may re-execute
41
+ * after a cross-locale header repaint, but document-level delegation is
42
+ * registered exactly once per page lifetime and the replacement markup is
43
+ * refreshed immediately.
43
44
  *
44
45
  * The pathname fed to `switchLocaleHref` comes from the embedded
45
46
  * `readCurrentPath`, which prefers the `data-zd-current-path` override over
@@ -68,13 +69,17 @@ export interface LanguageSwitcherProps {
68
69
  * script knows which locale segment to strip. Only read when `config` is set.
69
70
  */
70
71
  currentLocale?: string;
72
+ /** Localized accessible name for the disclosure trigger. */
73
+ accessibleLabel: string;
74
+ /** Optional suffix used to keep trigger/menu ids unique on the page. */
75
+ idSuffix?: string;
71
76
  }
72
77
  /**
73
- * Inline locale switcher rendered in the header / sidebar footer.
78
+ * Desktop locale disclosure rendered in the header.
74
79
  *
75
80
  * Returns `null` when there is one locale or fewer (matches the Astro
76
81
  * template's `localeLinks.length > 1 &&` guard so call-sites can mount
77
82
  * the component unconditionally).
78
83
  */
79
- export declare function LanguageSwitcher({ links, config, currentLocale, }: LanguageSwitcherProps): VNode | null;
84
+ export declare function LanguageSwitcher({ links, config, currentLocale, accessibleLabel, idSuffix, }: LanguageSwitcherProps): VNode | null;
80
85
  export type { LocaleLink };
@@ -1,5 +1,4 @@
1
1
  import { jsx, jsxs } from "preact/jsx-runtime";
2
- import { Fragment } from "preact";
3
2
  import { AFTER_NAVIGATE_EVENT } from "../transitions/index.js";
4
3
  import { CURRENT_PATH_SCRIPT_PRELUDE } from "../current-path/index.js";
5
4
  function switchLocaleHref(pathname, config, currentLang, targetLang) {
@@ -44,14 +43,26 @@ function switchLocaleHref(pathname, config, currentLang, targetLang) {
44
43
  }
45
44
  const LANGUAGE_SWITCHER_INIT_SCRIPT = `(function(){
46
45
  var FLAG="__zdLanguageSwitcherInit";
47
- if(window[FLAG])return;
48
- window[FLAG]=true;
49
46
  ${CURRENT_PATH_SCRIPT_PRELUDE}
50
47
  var switchLocaleHref=${switchLocaleHref.toString()};
51
- function rewire(){
48
+ function close(c,restoreFocus){
49
+ var toggle=c.querySelector("[data-language-toggle]");
50
+ var menu=c.querySelector("[data-language-menu]");
51
+ if(!toggle||!menu)return;
52
+ menu.classList.add("hidden");
53
+ toggle.setAttribute("aria-expanded","false");
54
+ if(restoreFocus)toggle.focus();
55
+ }
56
+ function refresh(){
52
57
  var containers=document.querySelectorAll("[data-language-switcher]");
53
58
  for(var i=0;i<containers.length;i++){
54
59
  var c=containers[i];
60
+ close(c,false);
61
+ var menu=c.querySelector("[data-language-menu]");
62
+ if(menu){
63
+ menu.classList.remove("group-hover:block","group-focus-within:block");
64
+ }
65
+ if(!c.hasAttribute("data-default-locale"))continue;
55
66
  var config={base:c.getAttribute("data-base")||"",defaultLocale:c.getAttribute("data-default-locale")||"",trailingSlash:c.getAttribute("data-trailing-slash")==="true"};
56
67
  var currentLang=c.getAttribute("data-current-locale")||config.defaultLocale;
57
68
  var anchors=c.querySelectorAll("a[lang]");
@@ -63,39 +74,121 @@ a.setAttribute("href",switchLocaleHref(readCurrentPath(CURRENT_PATH_DATASET_KEY)
63
74
  }
64
75
  }
65
76
  }
66
- rewire();
67
- document.addEventListener(${JSON.stringify(AFTER_NAVIGATE_EVENT)},rewire);
77
+ if(window[FLAG]){window[FLAG]();return;}
78
+ window[FLAG]=refresh;
79
+ document.addEventListener("click",function(e){
80
+ var target=e.target;
81
+ var toggle=target&&target.closest?target.closest("[data-language-toggle]"):null;
82
+ if(toggle){
83
+ var switcher=toggle.closest("[data-language-switcher]");
84
+ if(switcher){
85
+ var menu=switcher.querySelector("[data-language-menu]");
86
+ var willOpen=toggle.getAttribute("aria-expanded")!=="true";
87
+ document.querySelectorAll("[data-language-switcher]").forEach(function(c){if(c!==switcher)close(c,false);});
88
+ if(menu){menu.classList.toggle("hidden",!willOpen);toggle.setAttribute("aria-expanded",String(willOpen));}
89
+ }
90
+ return;
91
+ }
92
+ document.querySelectorAll("[data-language-switcher]").forEach(function(c){if(!c.contains(target))close(c,false);});
93
+ });
94
+ document.addEventListener("keydown",function(e){
95
+ if(e.key!=="Escape")return;
96
+ document.querySelectorAll('[data-language-toggle][aria-expanded="true"]').forEach(function(toggle){
97
+ var switcher=toggle.closest("[data-language-switcher]");
98
+ if(switcher)close(switcher,true);
99
+ });
100
+ });
101
+ refresh();
102
+ document.addEventListener(${JSON.stringify(AFTER_NAVIGATE_EVENT)},refresh);
68
103
  })();`;
69
104
  function LanguageSwitcher({
70
105
  links,
71
106
  config,
72
- currentLocale
107
+ currentLocale,
108
+ accessibleLabel,
109
+ idSuffix = ""
73
110
  }) {
74
111
  if (links.length <= 1) return null;
112
+ const menuId = `language-menu${idSuffix ? `-${idSuffix}` : ""}`;
113
+ const activeLink = links.find((link) => link.active) ?? links[0];
75
114
  const rewireAttrs = config ? {
76
- "data-language-switcher": true,
77
115
  "data-base": config.base,
78
116
  "data-default-locale": config.defaultLocale,
79
117
  "data-trailing-slash": String(config.trailingSlash),
80
118
  "data-current-locale": currentLocale ?? config.defaultLocale
81
119
  } : {};
82
- return /* @__PURE__ */ jsx("div", { class: "flex items-center gap-x-hsp-xs text-small", ...rewireAttrs, children: links.map((link, i) => (
83
- // Fragment is keyed via the locale code so the reconciler keeps
84
- // the active/inactive nodes paired correctly across re-renders
85
- // (e.g. when the active locale flips after navigation).
86
- /* @__PURE__ */ jsxs(Fragment, { children: [
87
- i > 0 && /* @__PURE__ */ jsx("span", { class: "text-muted", children: "/" }),
88
- link.active ? /* @__PURE__ */ jsx("span", { "aria-current": "true", class: "font-medium text-fg", children: link.label }) : /* @__PURE__ */ jsx(
89
- "a",
120
+ return /* @__PURE__ */ jsxs(
121
+ "div",
122
+ {
123
+ class: "group relative flex items-center text-small",
124
+ "data-language-switcher": true,
125
+ ...rewireAttrs,
126
+ children: [
127
+ /* @__PURE__ */ jsxs(
128
+ "button",
129
+ {
130
+ type: "button",
131
+ class: "flex max-w-[16rem] cursor-pointer items-center gap-hsp-2xs whitespace-nowrap rounded border border-muted px-hsp-sm py-vsp-3xs text-small text-muted transition-colors hover:border-accent hover:text-accent focus-visible:border-accent focus-visible:text-accent",
132
+ "aria-label": accessibleLabel,
133
+ "aria-controls": menuId,
134
+ "aria-expanded": "false",
135
+ "data-language-toggle": true,
136
+ children: [
137
+ /* @__PURE__ */ jsx("span", { class: "truncate font-medium", children: activeLink?.label }),
138
+ /* @__PURE__ */ jsx(ChevronDownIcon, {})
139
+ ]
140
+ }
141
+ ),
142
+ /* @__PURE__ */ jsx(
143
+ "ul",
144
+ {
145
+ id: menuId,
146
+ class: "absolute right-0 top-full z-dropdown mt-vsp-3xs hidden min-w-[8rem] max-w-[calc(100vw-var(--spacing-hsp-xl))] overflow-x-auto whitespace-nowrap rounded border border-muted bg-surface py-vsp-3xs shadow-lg group-hover:block group-focus-within:block",
147
+ "data-language-menu": true,
148
+ children: links.map((link) => /* @__PURE__ */ jsx("li", { children: link.active ? /* @__PURE__ */ jsx(
149
+ "span",
150
+ {
151
+ lang: link.code,
152
+ "aria-current": "page",
153
+ class: "block px-hsp-md py-vsp-2xs text-small font-bold text-accent",
154
+ children: link.label
155
+ }
156
+ ) : /* @__PURE__ */ jsx(
157
+ "a",
158
+ {
159
+ href: link.href,
160
+ lang: link.code,
161
+ class: "block px-hsp-md py-vsp-2xs text-small text-fg hover:bg-accent/10 hover:text-accent hover:underline focus-visible:text-accent focus-visible:underline",
162
+ children: link.label
163
+ }
164
+ ) }, link.code))
165
+ }
166
+ )
167
+ ]
168
+ }
169
+ );
170
+ }
171
+ function ChevronDownIcon() {
172
+ return /* @__PURE__ */ jsx(
173
+ "svg",
174
+ {
175
+ xmlns: "http://www.w3.org/2000/svg",
176
+ class: "h-icon-xs w-icon-xs shrink-0",
177
+ fill: "none",
178
+ viewBox: "0 0 24 24",
179
+ stroke: "currentColor",
180
+ "stroke-width": "2",
181
+ "aria-hidden": "true",
182
+ children: /* @__PURE__ */ jsx(
183
+ "path",
90
184
  {
91
- href: link.href,
92
- lang: link.code,
93
- class: "text-muted hover:text-fg",
94
- children: link.label
185
+ "stroke-linecap": "round",
186
+ "stroke-linejoin": "round",
187
+ d: "M19 9l-7 7-7-7"
95
188
  }
96
189
  )
97
- ] }, link.code)
98
- )) });
190
+ }
191
+ );
99
192
  }
100
193
  export {
101
194
  LANGUAGE_SWITCHER_INIT_SCRIPT,
@@ -11,7 +11,7 @@ export interface LocaleLink {
11
11
  label: string;
12
12
  /** Pre-resolved href for the locale (already prefixed with the configured base). */
13
13
  href: string;
14
- /** True for the currently-active locale — rendered as plain text, not a link. */
14
+ /** True for the currently-active locale — rendered as the disclosure's current row. */
15
15
  active: boolean;
16
16
  }
17
17
  /**
@@ -22,6 +22,10 @@ export interface CreateMdxComponentsOptions {
22
22
  settings: Pick<Settings, "base" | "imageEnlarge" | "assetViewerDir" | "assetViewerRoutePrefix">;
23
23
  /** Author-facing asset index. Null/omitted keeps all MDX overrides inert. */
24
24
  assetManifest?: AssetManifest | null;
25
+ /** Non-default locale segment used by asset-viewer links. */
26
+ assetViewerLocale?: string;
27
+ /** Match asset viewer paths that intentionally remain default-locale-only. */
28
+ isDefaultLocaleOnlyPath?: (path: string) => boolean;
25
29
  /**
26
30
  * Active locale for this render. Injected into each `navData` wrapper as the
27
31
  * `lang` prop so `/ja` pages resolve the JA collection. Load-bearing — do not