@takazudo/zudo-doc 5.9.0 → 5.11.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.
- package/CHANGELOG.md +31 -0
- package/README.md +1 -1
- package/dist/chrome/derive.d.ts +1 -1
- package/dist/chrome/derive.js +25 -2
- package/dist/config-assertions/index.d.ts +30 -0
- package/dist/config-assertions/index.js +41 -1
- package/dist/config.d.ts +9 -0
- package/dist/config.js +1 -0
- package/dist/doc-content-header/index.js +6 -0
- package/dist/doc-metainfo-area/index.js +1 -14
- package/dist/doc-page-renderer/index.d.ts +3 -3
- package/dist/doc-page-renderer/index.js +1 -1
- package/dist/doc-page-shell/index.d.ts +6 -0
- package/dist/doc-pager/index.d.ts +6 -0
- package/dist/doc-pager/index.js +5 -2
- package/dist/docs-schema/index.d.ts +11 -0
- package/dist/docs-schema/index.js +11 -1
- package/dist/format-date/index.d.ts +12 -0
- package/dist/format-date/index.js +64 -0
- package/dist/frontmatter-preview-defaults/index.js +6 -1
- package/dist/header/header.js +9 -1
- package/dist/home-page/index.js +5 -2
- package/dist/html-preview-wrapper/highlight-runtime.d.ts +7 -7
- package/dist/html-preview-wrapper/highlight-runtime.js +1 -1
- package/dist/integrations/changelog/emit.js +19 -2
- package/dist/mdx-components/index.d.ts +4 -1
- package/dist/mdx-components/index.js +3 -1
- package/dist/nav-indexing/index.d.ts +2 -0
- package/dist/nav-indexing/index.js +2 -0
- package/dist/nav-indexing/note-tray-index.d.ts +29 -0
- package/dist/nav-indexing/note-tray-index.js +120 -0
- package/dist/note-tray-index/index.d.ts +45 -0
- package/dist/note-tray-index/index.js +52 -0
- package/dist/note-tray-model/index.d.ts +31 -0
- package/dist/note-tray-model/index.js +74 -0
- package/dist/preset.d.ts +11 -0
- package/dist/preset.js +5 -1
- package/dist/safelist.css +1 -1
- package/dist/settings.d.ts +2 -0
- package/dist/sidebar/types.d.ts +6 -0
- package/dist/sidebar-tree/build-tree.js +8 -0
- package/dist/sidebar-tree/types.d.ts +12 -0
- package/dist/sidebar-tree-island/index.js +176 -1
- package/dist/sidebar-utils/index.d.ts +6 -0
- package/dist/site-schema/doc-route-entries.js +2 -0
- package/dist/site-schema/index.d.ts +1 -0
- package/dist/site-schema/index.js +3 -1
- package/dist/site-schema/nav-tree.js +17 -2
- package/dist/site-schema/note-tray-validate.d.ts +7 -0
- package/dist/site-schema/note-tray-validate.js +94 -0
- package/dist/site-schema/types.d.ts +11 -0
- package/dist/site-tree-nav/index.d.ts +7 -0
- package/dist/site-tree-nav/index.js +3 -2
- package/dist/site-tree-nav-island/index.d.ts +5 -1
- package/dist/site-tree-nav-island/index.js +119 -12
- package/dist/theme-packs/drift/pack.css +6 -1
- package/dist/theme-packs/hearth/pack.css +5 -0
- package/dist/theme-packs/sakura/pack.css +6 -0
- package/dist/theme-packs/scandi/pack.css +7 -7
- package/dist/theme-packs/timberline/pack.css +5 -0
- package/eject/doc-pager/index.tsx +9 -0
- package/eject/header/header.tsx +26 -1
- package/eject/sidebar/types.ts +6 -0
- package/eject/sidebar-tree-island/index.tsx +241 -6
- package/eject/site-tree-nav-island/index.tsx +166 -16
- package/package.json +20 -8
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,37 @@ 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.11.0] - 2026-08-23
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- Added the `siteTreeNavIgnore` setting so selected site-tree categories can be omitted from sidebar and home-page navigation while their documents remain available. (`2d1aa78f`)
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
- Aligned grouped note-tray child items with the deeper indentation used by the corresponding expanded layout. (`5bbf365f`)
|
|
16
|
+
|
|
17
|
+
### Other Changes
|
|
18
|
+
|
|
19
|
+
- Updated the zfb package family to 2.10.0 and documented `siteTreeNavIgnore` plus quoted date frontmatter guidance in English and Japanese. (`acaa01cf`, `3f15cc2f`, `c484754b`)
|
|
20
|
+
|
|
21
|
+
## [5.10.0] - 2026-08-22
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
- Added first-class multi-package changelogs, including nested package navigation, per-package release pages, bilingual guidance, and the `create-zudo-doc --changelog-packages` option. (`91f179c8`, `302eac4f`)
|
|
26
|
+
- Upgraded the zfb package family to 2.9.0 and moved HtmlPreview to the public highlight-only WASM runtime, reducing the emitted WASM payload by about 55%. (`52291ec5`, `2911a651`)
|
|
27
|
+
|
|
28
|
+
### Bug Fixes
|
|
29
|
+
|
|
30
|
+
- Hardened nested changelog navigation and added diagnostics when an emitter targets a multi-package landing directory instead of a package release directory. (`302eac4f`, `c4266e4a`)
|
|
31
|
+
- Normalized non-pill sidebar current rows in Drift, Hearth, Sakura, Scandi, and Timberline while preserving each pack's nested active treatment. (`7c4e0444`)
|
|
32
|
+
|
|
33
|
+
### Other Changes
|
|
34
|
+
|
|
35
|
+
- Added bilingual documentation, publish-contract coverage, and positive browser detectors for the highlight-only runtime. (`b97ef30c`, `92269d49`)
|
|
36
|
+
- Added package-test hook headroom for concurrent CI load without changing subprocess deadlines. (`4d1417a9`)
|
|
37
|
+
|
|
7
38
|
## [5.9.0] - 2026-08-20
|
|
8
39
|
|
|
9
40
|
### Features
|
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ const breadcrumbs = buildBreadcrumbs(tree, "guides/color", homeHref);
|
|
|
59
59
|
|
|
60
60
|
## Optional peer dependency: `@takazudo/zfb-md-wasm`
|
|
61
61
|
|
|
62
|
-
`./html-preview-wrapper`'s `<HighlightedCode>` lazily imports the
|
|
62
|
+
`./html-preview-wrapper`'s `<HighlightedCode>` lazily imports the public `@takazudo/zfb-md-wasm/highlight` subpath and calls `highlightCode()` for client-side semantic syntax highlighting. `@takazudo/zfb-md-wasm` is declared as an **optional peerDependency** — install the same version as the rest of your zfb packages if you use that subpath:
|
|
63
63
|
|
|
64
64
|
```sh
|
|
65
65
|
pnpm add @takazudo/zfb-md-wasm
|
package/dist/chrome/derive.d.ts
CHANGED
|
@@ -152,7 +152,7 @@ export declare function deriveInlineVersionSwitcher(ctx: ChromeContext, getUnava
|
|
|
152
152
|
* happens one layer up in `createMdxComponents()`, after the wrapper choice.
|
|
153
153
|
*/
|
|
154
154
|
export declare function deriveMdxComponents(ctx: ChromeContext): {
|
|
155
|
-
createMdxComponentsBound: (lang?: string, currentVersion?: string) => Record<string, unknown>;
|
|
155
|
+
createMdxComponentsBound: (lang?: string, currentVersion?: string, currentSlug?: string) => Record<string, unknown>;
|
|
156
156
|
SiteTreeNavWrapper: FactoryComponent;
|
|
157
157
|
};
|
|
158
158
|
export type { Settings };
|
package/dist/chrome/derive.js
CHANGED
|
@@ -14,6 +14,7 @@ import { DEFAULT_THEME_PACK_SLUG } from "../theme-pack-switcher/theme-pack-sync.
|
|
|
14
14
|
import { SearchWidget } from "../search-widget/index.js";
|
|
15
15
|
import { createMdxComponents } from "../mdx-components/index.js";
|
|
16
16
|
import { createCategoryNavWrapper } from "../category-nav/index.js";
|
|
17
|
+
import { createNoteTrayIndexWrapper } from "../note-tray-index/index.js";
|
|
17
18
|
import { createCategoryTreeNavWrapper } from "../category-tree-nav/index.js";
|
|
18
19
|
import { createSiteTreeNavWrapper } from "../site-tree-nav/index.js";
|
|
19
20
|
import { Details } from "../details/index.js";
|
|
@@ -269,6 +270,26 @@ function deriveMdxComponents(ctx) {
|
|
|
269
270
|
)),
|
|
270
271
|
groupSatelliteNodes: ctx.groupSatelliteNodes,
|
|
271
272
|
getCategoryOrder: ctx.getCategoryOrder,
|
|
273
|
+
versionedDocsUrl: ctx.versionedDocsUrl,
|
|
274
|
+
categoryIgnore: ctx.settings.siteTreeNavIgnore
|
|
275
|
+
});
|
|
276
|
+
const NoteTrayIndexWrapper = createNoteTrayIndexWrapper({
|
|
277
|
+
defaultLocale: ctx.defaultLocale,
|
|
278
|
+
docTags: ctx.settings.docTags,
|
|
279
|
+
resolveNavSource: ctx.resolveNavSource,
|
|
280
|
+
buildNavTree: ((docs, locale, categoryMeta) => ctx.buildNavTree(
|
|
281
|
+
docs,
|
|
282
|
+
locale,
|
|
283
|
+
categoryMeta,
|
|
284
|
+
(slug, loc) => ctx.docsUrl(slug, loc)
|
|
285
|
+
)),
|
|
286
|
+
findNode: ctx.findNode,
|
|
287
|
+
toRouteSlug: ctx.toRouteSlug,
|
|
288
|
+
resolveTag: (raw) => ctx.resolveTagBound(raw).canonical,
|
|
289
|
+
tagHref: (tag, locale) => ctx.withBase(
|
|
290
|
+
locale === ctx.defaultLocale ? `/docs/tags/${encodeURIComponent(tag)}` : `/${locale}/docs/tags/${encodeURIComponent(tag)}`
|
|
291
|
+
),
|
|
292
|
+
t: (key, locale) => ctx.t(key, locale),
|
|
272
293
|
versionedDocsUrl: ctx.versionedDocsUrl
|
|
273
294
|
});
|
|
274
295
|
function HtmlPreviewBound(props) {
|
|
@@ -286,15 +307,17 @@ function deriveMdxComponents(ctx) {
|
|
|
286
307
|
PresetGenerator: (_props) => null
|
|
287
308
|
};
|
|
288
309
|
const mdxExtras = { ...mdxExtrasDefault, ...ctx.hostBindings.mdxExtras ?? {} };
|
|
289
|
-
function createMdxComponentsBound(lang = ctx.defaultLocale, currentVersion) {
|
|
310
|
+
function createMdxComponentsBound(lang = ctx.defaultLocale, currentVersion, currentSlug = "") {
|
|
290
311
|
return createMdxComponents({
|
|
291
312
|
settings: ctx.settings,
|
|
292
313
|
locale: lang,
|
|
293
314
|
currentVersion,
|
|
315
|
+
currentSlug,
|
|
294
316
|
navData: {
|
|
295
317
|
CategoryNav: CategoryNavWrapper,
|
|
296
318
|
CategoryTreeNav: CategoryTreeNavWrapper,
|
|
297
|
-
SiteTreeNav: SiteTreeNavWrapper
|
|
319
|
+
SiteTreeNav: SiteTreeNavWrapper,
|
|
320
|
+
NoteTrayIndex: NoteTrayIndexWrapper
|
|
298
321
|
},
|
|
299
322
|
// Package-owned content components wired here so an INJECTED docs route
|
|
300
323
|
// renders MDX using these tags without the "MDX requires '<X>' to be
|
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import type { FaviconConfig } from "../settings.js";
|
|
2
|
+
/** Structural subset of a header dropdown read by the category-match guard. */
|
|
3
|
+
export interface AmbiguousDropdownCategoryMatchItem {
|
|
4
|
+
label?: string;
|
|
5
|
+
categoryMatch?: string;
|
|
6
|
+
children?: Array<{
|
|
7
|
+
label?: string;
|
|
8
|
+
categoryMatch?: string;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
/** Logger surface accepted by the non-throwing header-nav diagnostic. */
|
|
12
|
+
export interface ConfigAssertionLogger {
|
|
13
|
+
warn(message: string): void;
|
|
14
|
+
}
|
|
2
15
|
/** Structural subset of `Settings` {@link assertNoEmptyStringFaviconOrLogo} reads. */
|
|
3
16
|
export interface EmptyStringFaviconOrLogoSubject {
|
|
4
17
|
logo?: string | false;
|
|
@@ -29,3 +42,20 @@ export interface EmptyStringFaviconOrLogoSubject {
|
|
|
29
42
|
* object is not an empty string).
|
|
30
43
|
*/
|
|
31
44
|
export declare function assertNoEmptyStringFaviconOrLogo(settings: EmptyStringFaviconOrLogoSubject): void;
|
|
45
|
+
/**
|
|
46
|
+
* Warn about `categoryMatch` values that cannot express the intended
|
|
47
|
+
* dropdown grouping.
|
|
48
|
+
*
|
|
49
|
+
* A category matcher is compared with the first slug segment by the nav-scope
|
|
50
|
+
* resolver, so a value containing `/` never matches. Likewise, duplicate
|
|
51
|
+
* child values make every matching child category-active. Children grouped
|
|
52
|
+
* under one top-level directory should omit `categoryMatch` and rely on the
|
|
53
|
+
* deepest matching child path; Learn-style children should use distinct
|
|
54
|
+
* top-level values.
|
|
55
|
+
*
|
|
56
|
+
* This is intentionally a diagnostic rather than a config assertion: malformed
|
|
57
|
+
* navigation should remain buildable, and each offending value is reported at
|
|
58
|
+
* most once per parent dropdown. The `"!"` matcher is the intentional default
|
|
59
|
+
* bucket and is excluded from both checks.
|
|
60
|
+
*/
|
|
61
|
+
export declare function warnAmbiguousDropdownCategoryMatch(headerNav: readonly AmbiguousDropdownCategoryMatchItem[] | undefined, logger?: ConfigAssertionLogger): void;
|
|
@@ -19,6 +19,46 @@ function assertNoEmptyStringFaviconOrLogo(settings) {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
+
function warnAmbiguousDropdownCategoryMatch(headerNav, logger = console) {
|
|
23
|
+
if (!headerNav) return;
|
|
24
|
+
for (const item of headerNav) {
|
|
25
|
+
const children = item.children;
|
|
26
|
+
if (!children || children.length === 0) continue;
|
|
27
|
+
const childCounts = /* @__PURE__ */ new Map();
|
|
28
|
+
for (const child of children) {
|
|
29
|
+
const value = child.categoryMatch;
|
|
30
|
+
if (value == null || value === "!") continue;
|
|
31
|
+
childCounts.set(value, (childCounts.get(value) ?? 0) + 1);
|
|
32
|
+
}
|
|
33
|
+
const offendingValues = [];
|
|
34
|
+
const addOffendingValue = (value) => {
|
|
35
|
+
if (value == null || value === "!" || offendingValues.includes(value)) return;
|
|
36
|
+
offendingValues.push(value);
|
|
37
|
+
};
|
|
38
|
+
if (item.categoryMatch?.includes("/")) {
|
|
39
|
+
addOffendingValue(item.categoryMatch);
|
|
40
|
+
}
|
|
41
|
+
for (const child of children) {
|
|
42
|
+
if (child.categoryMatch?.includes("/")) {
|
|
43
|
+
addOffendingValue(child.categoryMatch);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
for (const [value, count] of childCounts) {
|
|
47
|
+
if (count >= 2) addOffendingValue(value);
|
|
48
|
+
}
|
|
49
|
+
for (const value of offendingValues) {
|
|
50
|
+
const reasons = [
|
|
51
|
+
...value.includes("/") ? ["contains `/` and never matches a top-level slug"] : [],
|
|
52
|
+
...(childCounts.get(value) ?? 0) >= 2 ? ["is shared by multiple children"] : []
|
|
53
|
+
].join("; ");
|
|
54
|
+
const parentLabel = item.label ?? "(unnamed)";
|
|
55
|
+
logger.warn(
|
|
56
|
+
`zudo-doc: header dropdown "${parentLabel}" has categoryMatch "${value}" that ${reasons}. Children grouped under one top-level directory should omit categoryMatch (active state follows the deepest matching child path); Learn-style children should use distinct top-level values.`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
22
61
|
export {
|
|
23
|
-
assertNoEmptyStringFaviconOrLogo
|
|
62
|
+
assertNoEmptyStringFaviconOrLogo,
|
|
63
|
+
warnAmbiguousDropdownCategoryMatch
|
|
24
64
|
};
|
package/dist/config.d.ts
CHANGED
|
@@ -144,6 +144,15 @@ export interface ZudoDocConfig {
|
|
|
144
144
|
* @default { wide: false }
|
|
145
145
|
*/
|
|
146
146
|
home?: HomeConfig;
|
|
147
|
+
/**
|
|
148
|
+
* Top-level category slugs hidden from the package-owned home-page category
|
|
149
|
+
* grid on `/` and locale homes AND from the `<SiteTreeNav />` /
|
|
150
|
+
* `<SiteTreeNavDemo />` MDX tag. They still appear in header nav, sidebar,
|
|
151
|
+
* search and sitemap. Before this field the package hard-coded
|
|
152
|
+
* `["inbox", "develop"]` (#3641).
|
|
153
|
+
* @default []
|
|
154
|
+
*/
|
|
155
|
+
siteTreeNavIgnore?: string[];
|
|
147
156
|
/**
|
|
148
157
|
* Minify production HTML output from `zfb build`.
|
|
149
158
|
* @default true
|
package/dist/config.js
CHANGED
|
@@ -3,6 +3,7 @@ import { FrontmatterPreview } from "../metainfo/index.js";
|
|
|
3
3
|
import { createDocMetainfoArea } from "../doc-metainfo-area/index.js";
|
|
4
4
|
import { createDocTagsArea } from "../doc-tags-area/index.js";
|
|
5
5
|
import { assertChromeContext } from "../chrome/assert-chrome-context.js";
|
|
6
|
+
import { formatDate } from "../format-date/index.js";
|
|
6
7
|
function createDocContentHeader(ctx) {
|
|
7
8
|
assertChromeContext(ctx, "createDocContentHeader");
|
|
8
9
|
const t = ctx.t;
|
|
@@ -27,6 +28,11 @@ function createDocContentHeader(ctx) {
|
|
|
27
28
|
isFallback,
|
|
28
29
|
version
|
|
29
30
|
}),
|
|
31
|
+
(entry.data.date || entry.data.updated) && /* @__PURE__ */ jsxs("p", { class: "text-caption text-muted mb-vsp-md", "data-doc-date": true, children: [
|
|
32
|
+
entry.data.date ? formatDate(entry.data.date, locale) : null,
|
|
33
|
+
entry.data.date && entry.data.updated ? " \xB7 " : null,
|
|
34
|
+
entry.data.updated ? `${t("doc.updated", locale)} ${formatDate(entry.data.updated, locale)}` : null
|
|
35
|
+
] }),
|
|
30
36
|
!version && /* @__PURE__ */ jsx(DocMetainfoArea, { slug, locale, isFallback }),
|
|
31
37
|
!version && /* @__PURE__ */ jsx(DocTagsArea, { slug, locale, tags: entry.data.tags }),
|
|
32
38
|
isFallback && !entry.data.generated && /* @__PURE__ */ jsx(
|
|
@@ -2,20 +2,7 @@ import { jsx } from "preact/jsx-runtime";
|
|
|
2
2
|
import { DocMetainfo } from "../metainfo/index.js";
|
|
3
3
|
import { toHistorySlug } from "../slug/index.js";
|
|
4
4
|
import { assertChromeContext } from "../chrome/assert-chrome-context.js";
|
|
5
|
-
|
|
6
|
-
en: "en-US",
|
|
7
|
-
ja: "ja-JP",
|
|
8
|
-
de: "de-DE"
|
|
9
|
-
};
|
|
10
|
-
function formatDate(isoDate, locale) {
|
|
11
|
-
const d = new Date(isoDate);
|
|
12
|
-
if (isNaN(d.getTime())) return isoDate;
|
|
13
|
-
return d.toLocaleDateString(LOCALE_TO_BCP47[locale] ?? "en-US", {
|
|
14
|
-
year: "numeric",
|
|
15
|
-
month: "short",
|
|
16
|
-
day: "numeric"
|
|
17
|
-
});
|
|
18
|
-
}
|
|
5
|
+
import { formatDate } from "../format-date/index.js";
|
|
19
6
|
function createDocMetainfoArea(ctx) {
|
|
20
7
|
assertChromeContext(ctx, "createDocMetainfoArea");
|
|
21
8
|
const settings = ctx.settings;
|
|
@@ -48,10 +48,10 @@ export interface DocPageRendererDeps {
|
|
|
48
48
|
* Build locale-aware MDX components bag.
|
|
49
49
|
* Host passes `createMdxComponents` (from `pages/_mdx-components.ts`).
|
|
50
50
|
* `currentVersion` (#3218) threads the active `/v/{version}` slug into the
|
|
51
|
-
* nav
|
|
52
|
-
*
|
|
51
|
+
* nav wrappers so their data and hrefs resolve within the version. The
|
|
52
|
+
* current slug lets NoteTrayIndex infer its containing tray.
|
|
53
53
|
*/
|
|
54
|
-
createMdxComponents: (locale: string, currentVersion
|
|
54
|
+
createMdxComponents: (locale: string, currentVersion: string | undefined, currentSlug: string) => Record<string, unknown>;
|
|
55
55
|
/** Translate a UI string key for a locale. */
|
|
56
56
|
t: (key: string, locale: string) => string;
|
|
57
57
|
/**
|
|
@@ -35,7 +35,7 @@ function createRenderDocPage(ctx) {
|
|
|
35
35
|
const slug = props.kind === "autoIndex" ? props.autoIndex.slug : props.entry.data.slug ?? toRouteSlug(props.entry.slug);
|
|
36
36
|
const title = props.kind === "autoIndex" ? props.autoIndex.label : props.entry.data.title;
|
|
37
37
|
const description = props.kind === "autoIndex" ? props.autoIndex.description : props.entry.data.description;
|
|
38
|
-
const components = createMdxComponents(locale, version?.slug);
|
|
38
|
+
const components = createMdxComponents(locale, version?.slug, slug);
|
|
39
39
|
const autoIndexChildren = props.kind === "autoIndex" ? version ? props.autoIndex.children.filter((c) => c.hasPage || c.children.length > 0) : props.autoIndex.children.filter((c) => c.hasPage || c.children.length > 0).map((c) => ({
|
|
40
40
|
...c,
|
|
41
41
|
href: c.href ?? docsUrl(c.slug, locale)
|
|
@@ -22,6 +22,12 @@ export interface DocPageNavNode {
|
|
|
22
22
|
href?: string;
|
|
23
23
|
hasPage: boolean;
|
|
24
24
|
children: DocPageNavNode[];
|
|
25
|
+
shape?: "note-tray";
|
|
26
|
+
noteTrayDated?: boolean;
|
|
27
|
+
noteTraySidebar?: "index" | "year" | "month";
|
|
28
|
+
date?: string;
|
|
29
|
+
updated?: string;
|
|
30
|
+
rank?: number;
|
|
25
31
|
}
|
|
26
32
|
/** Slots and parameters that vary between the 4 doc routes. */
|
|
27
33
|
export interface DocPageShellProps {
|
|
@@ -6,6 +6,12 @@ import type { Settings } from "../settings.js";
|
|
|
6
6
|
interface PagerNode {
|
|
7
7
|
href?: string;
|
|
8
8
|
label: string;
|
|
9
|
+
shape?: "note-tray";
|
|
10
|
+
noteTrayDated?: boolean;
|
|
11
|
+
noteTraySidebar?: "index" | "year" | "month";
|
|
12
|
+
date?: string;
|
|
13
|
+
updated?: string;
|
|
14
|
+
rank?: number;
|
|
9
15
|
}
|
|
10
16
|
export interface DocPagerProps {
|
|
11
17
|
/** Previous page node (null = no previous page → renders placeholder). */
|
package/dist/doc-pager/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { ChevronLeft, ChevronRight } from "../icons/index.js";
|
|
3
3
|
import { assertChromeContext } from "../chrome/assert-chrome-context.js";
|
|
4
|
+
import { formatDate } from "../format-date/index.js";
|
|
4
5
|
function createDocPager(ctx) {
|
|
5
6
|
assertChromeContext(ctx, "createDocPager");
|
|
6
7
|
const t = ctx.t;
|
|
@@ -16,7 +17,8 @@ function createDocPager(ctx) {
|
|
|
16
17
|
/* @__PURE__ */ jsx(ChevronLeft, { className: "h-[1.125rem] w-[1.125rem]" }),
|
|
17
18
|
/* @__PURE__ */ jsx("span", { class: "no-underline", children: t("nav.previous", locale) })
|
|
18
19
|
] }),
|
|
19
|
-
/* @__PURE__ */ jsx("p", { class: "text-small font-semibold underline group-hover:text-accent", children: prev.label })
|
|
20
|
+
/* @__PURE__ */ jsx("p", { class: "text-small font-semibold underline group-hover:text-accent", children: prev.label }),
|
|
21
|
+
prev.date && /* @__PURE__ */ jsx("p", { class: "text-caption text-muted", children: formatDate(prev.date, locale) })
|
|
20
22
|
]
|
|
21
23
|
}
|
|
22
24
|
) : /* @__PURE__ */ jsx("div", {}),
|
|
@@ -30,7 +32,8 @@ function createDocPager(ctx) {
|
|
|
30
32
|
/* @__PURE__ */ jsx("span", { class: "no-underline", children: t("nav.next", locale) }),
|
|
31
33
|
/* @__PURE__ */ jsx(ChevronRight, { className: "h-[1.125rem] w-[1.125rem]" })
|
|
32
34
|
] }),
|
|
33
|
-
/* @__PURE__ */ jsx("p", { class: "text-small font-semibold underline group-hover:text-accent", children: next.label })
|
|
35
|
+
/* @__PURE__ */ jsx("p", { class: "text-small font-semibold underline group-hover:text-accent", children: next.label }),
|
|
36
|
+
next.date && /* @__PURE__ */ jsx("p", { class: "text-caption text-muted", children: formatDate(next.date, locale) })
|
|
34
37
|
]
|
|
35
38
|
}
|
|
36
39
|
) : /* @__PURE__ */ jsx("div", {})
|
|
@@ -58,6 +58,17 @@ export declare function buildDocsSchema(opts?: BuildDocsSchemaOptions): z.ZodObj
|
|
|
58
58
|
asc: "asc";
|
|
59
59
|
desc: "desc";
|
|
60
60
|
}>>;
|
|
61
|
+
category_shape: z.ZodOptional<z.ZodEnum<{
|
|
62
|
+
"note-tray": "note-tray";
|
|
63
|
+
}>>;
|
|
64
|
+
note_tray_dated: z.ZodOptional<z.ZodBoolean>;
|
|
65
|
+
note_tray_sidebar: z.ZodOptional<z.ZodEnum<{
|
|
66
|
+
index: "index";
|
|
67
|
+
year: "year";
|
|
68
|
+
month: "month";
|
|
69
|
+
}>>;
|
|
70
|
+
date: z.ZodOptional<z.ZodString>;
|
|
71
|
+
updated: z.ZodOptional<z.ZodString>;
|
|
61
72
|
}, z.core.$loose>;
|
|
62
73
|
/**
|
|
63
74
|
* TypeScript type inferred from the default docs frontmatter zod schema.
|
|
@@ -35,7 +35,17 @@ function buildDocsSchema(opts) {
|
|
|
35
35
|
// `category_sort_order` sets the child sort direction. Frontmatter wins
|
|
36
36
|
// over the sidecar.
|
|
37
37
|
category_no_page: z.boolean().optional(),
|
|
38
|
-
category_sort_order: z.enum(["asc", "desc"]).optional()
|
|
38
|
+
category_sort_order: z.enum(["asc", "desc"]).optional(),
|
|
39
|
+
category_shape: z.enum(["note-tray"]).optional(),
|
|
40
|
+
note_tray_dated: z.boolean().optional(),
|
|
41
|
+
note_tray_sidebar: z.enum(["index", "year", "month"]).optional(),
|
|
42
|
+
// zfb keeps scalar frontmatter values as strings whether quoted or not;
|
|
43
|
+
// the regex below validates those strings' YYYY-MM-DD format. Quoting is
|
|
44
|
+
// optional here and only aids interoperability with YAML 1.1 tooling,
|
|
45
|
+
// such as js-yaml, that consumes these fields and would coerce a bare
|
|
46
|
+
// date into a Date object (#3642).
|
|
47
|
+
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
|
48
|
+
updated: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
|
|
39
49
|
}).passthrough();
|
|
40
50
|
}
|
|
41
51
|
export {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface IsoDateParts {
|
|
2
|
+
year: number;
|
|
3
|
+
month: number;
|
|
4
|
+
day: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function parseIsoDate(iso: string): IsoDateParts | undefined;
|
|
7
|
+
/** Format an ISO date for display while retaining the established locale map. */
|
|
8
|
+
export declare function formatDate(iso: string, locale: string): string;
|
|
9
|
+
/** Format a year/month label with the year first in every locale. */
|
|
10
|
+
export declare function formatYearMonth(iso: string, locale: string): string;
|
|
11
|
+
/** Return the stable numeric month/day portion of a calendar-valid ISO date. */
|
|
12
|
+
export declare function formatMonthDay(iso: string): string;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const LOCALE_TO_BCP47 = {
|
|
2
|
+
en: "en-US",
|
|
3
|
+
ja: "ja-JP",
|
|
4
|
+
de: "de-DE"
|
|
5
|
+
};
|
|
6
|
+
function parseIsoDate(iso) {
|
|
7
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso);
|
|
8
|
+
if (!match) return void 0;
|
|
9
|
+
const year = Number(match[1]);
|
|
10
|
+
const month = Number(match[2]);
|
|
11
|
+
const day = Number(match[3]);
|
|
12
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
13
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
14
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
15
|
+
if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day) {
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
return { year, month, day };
|
|
19
|
+
}
|
|
20
|
+
function toUtcDate(iso) {
|
|
21
|
+
const parts = parseIsoDate(iso);
|
|
22
|
+
if (parts) {
|
|
23
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
24
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
25
|
+
date.setUTCFullYear(parts.year, parts.month - 1, parts.day);
|
|
26
|
+
return date;
|
|
27
|
+
}
|
|
28
|
+
if (!/^\d{4}-\d{2}-\d{2}T/.test(iso)) return void 0;
|
|
29
|
+
const timestamp = new Date(iso);
|
|
30
|
+
return Number.isNaN(timestamp.getTime()) ? void 0 : timestamp;
|
|
31
|
+
}
|
|
32
|
+
function formatDate(iso, locale) {
|
|
33
|
+
const date = toUtcDate(iso);
|
|
34
|
+
if (!date) return iso;
|
|
35
|
+
return new Intl.DateTimeFormat(LOCALE_TO_BCP47[locale] ?? "en-US", {
|
|
36
|
+
year: "numeric",
|
|
37
|
+
month: "short",
|
|
38
|
+
day: "numeric",
|
|
39
|
+
timeZone: "UTC"
|
|
40
|
+
}).format(date);
|
|
41
|
+
}
|
|
42
|
+
function formatYearMonth(iso, locale) {
|
|
43
|
+
const date = toUtcDate(iso.length === 7 ? `${iso}-01` : iso);
|
|
44
|
+
if (!date) return iso;
|
|
45
|
+
const year = new Intl.DateTimeFormat(LOCALE_TO_BCP47[locale] ?? "en-US", {
|
|
46
|
+
year: "numeric",
|
|
47
|
+
timeZone: "UTC"
|
|
48
|
+
}).format(date);
|
|
49
|
+
const month = new Intl.DateTimeFormat(LOCALE_TO_BCP47[locale] ?? "en-US", {
|
|
50
|
+
month: "long",
|
|
51
|
+
timeZone: "UTC"
|
|
52
|
+
}).format(date);
|
|
53
|
+
return locale === "ja" ? `${year}${month}` : `${year} ${month}`;
|
|
54
|
+
}
|
|
55
|
+
function formatMonthDay(iso) {
|
|
56
|
+
const parts = parseIsoDate(iso);
|
|
57
|
+
return parts ? `${String(parts.month).padStart(2, "0")}-${String(parts.day).padStart(2, "0")}` : iso;
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
formatDate,
|
|
61
|
+
formatMonthDay,
|
|
62
|
+
formatYearMonth,
|
|
63
|
+
parseIsoDate
|
|
64
|
+
};
|
|
@@ -18,7 +18,12 @@ const defaultFrontmatterPreviewIgnoreKeys = [
|
|
|
18
18
|
"slug",
|
|
19
19
|
"generated",
|
|
20
20
|
"category_no_page",
|
|
21
|
-
"category_sort_order"
|
|
21
|
+
"category_sort_order",
|
|
22
|
+
"category_shape",
|
|
23
|
+
"note_tray_dated",
|
|
24
|
+
"note_tray_sidebar",
|
|
25
|
+
"date",
|
|
26
|
+
"updated"
|
|
22
27
|
];
|
|
23
28
|
export {
|
|
24
29
|
defaultFrontmatterPreviewIgnoreKeys
|
package/dist/header/header.js
CHANGED
|
@@ -44,7 +44,8 @@ function Header(props) {
|
|
|
44
44
|
} = props;
|
|
45
45
|
const isNonDefaultLocale = lang != null && lang !== i18n.defaultLocale;
|
|
46
46
|
const pathWithoutBase = urlHelpers.stripBase(currentPath);
|
|
47
|
-
const
|
|
47
|
+
const pathWithoutVersion = stripCurrentVersionPrefix(pathWithoutBase, currentVersion);
|
|
48
|
+
const matchPath = pathForMatch(pathWithoutVersion, lang, i18n.defaultLocale);
|
|
48
49
|
const activeNavPath = computeActiveNavPath(headerNav, matchPath);
|
|
49
50
|
const rightItemDispatch = createRightItemDispatch(headerRightComponents);
|
|
50
51
|
return /* @__PURE__ */ jsxs(
|
|
@@ -249,6 +250,13 @@ function renderNavItem(item, activeNavPath, activeCategory, lang, currentVersion
|
|
|
249
250
|
}
|
|
250
251
|
);
|
|
251
252
|
}
|
|
253
|
+
function stripCurrentVersionPrefix(path, currentVersion) {
|
|
254
|
+
if (currentVersion == null || currentVersion === "") return path;
|
|
255
|
+
const prefix = `/v/${currentVersion}`;
|
|
256
|
+
if (path === prefix) return "/";
|
|
257
|
+
if (path.startsWith(`${prefix}/`)) return path.slice(prefix.length);
|
|
258
|
+
return path;
|
|
259
|
+
}
|
|
252
260
|
function TriggerButton({
|
|
253
261
|
index,
|
|
254
262
|
id,
|
package/dist/home-page/index.js
CHANGED
|
@@ -18,6 +18,7 @@ function cssUrl(url) {
|
|
|
18
18
|
function createHomePageView(ctx) {
|
|
19
19
|
assertChromeContext(ctx, "createHomePageView");
|
|
20
20
|
const settings = ctx.settings;
|
|
21
|
+
const categoryIgnore = settings.siteTreeNavIgnore ?? [];
|
|
21
22
|
const t = ctx.t;
|
|
22
23
|
const withBase = ctx.withBase;
|
|
23
24
|
const defaultLocale = ctx.defaultLocale;
|
|
@@ -131,8 +132,10 @@ function createHomePageView(ctx) {
|
|
|
131
132
|
{
|
|
132
133
|
tree,
|
|
133
134
|
categoryOrder,
|
|
134
|
-
categoryIgnore
|
|
135
|
-
initiallyCollapsedCategorySlugs
|
|
135
|
+
categoryIgnore,
|
|
136
|
+
initiallyCollapsedCategorySlugs,
|
|
137
|
+
locale,
|
|
138
|
+
updatedLabel: t("doc.updated", locale)
|
|
136
139
|
}
|
|
137
140
|
)
|
|
138
141
|
}),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { HighlightCodeOptions, HighlightCodeResult } from "@takazudo/zfb-md-wasm";
|
|
2
|
-
type HighlightModule = Pick<typeof import("@takazudo/zfb-md-wasm"), "highlightCode">;
|
|
1
|
+
import type { HighlightCodeOptions, HighlightCodeResult } from "@takazudo/zfb-md-wasm/highlight";
|
|
2
|
+
type HighlightModule = Pick<typeof import("@takazudo/zfb-md-wasm/highlight"), "highlightCode">;
|
|
3
3
|
export type HighlightModuleImporter = () => Promise<HighlightModule>;
|
|
4
4
|
export interface HtmlPreviewHighlightRuntime {
|
|
5
5
|
highlightCode(code: string, options: HighlightCodeOptions): Promise<HighlightCodeResult>;
|
|
@@ -7,11 +7,11 @@ export interface HtmlPreviewHighlightRuntime {
|
|
|
7
7
|
/**
|
|
8
8
|
* Build the HTML Preview's lazy zfb highlighting adapter.
|
|
9
9
|
*
|
|
10
|
-
* The cached value is only the public
|
|
11
|
-
* import is evicted so a later source-panel mount can retry a transient
|
|
12
|
-
* load. Calls to `highlightCode` are deliberately not cached: the
|
|
13
|
-
* package owns WASM initialization and trap recovery, while the
|
|
14
|
-
* request cancellation and stale-result protection.
|
|
10
|
+
* The cached value is only the public `./highlight` subpath module import. A
|
|
11
|
+
* rejected import is evicted so a later source-panel mount can retry a transient
|
|
12
|
+
* chunk load. Calls to `highlightCode` are deliberately not cached: the
|
|
13
|
+
* upstream package owns WASM initialization and trap recovery, while the
|
|
14
|
+
* component owns request cancellation and stale-result protection.
|
|
15
15
|
*/
|
|
16
16
|
export declare function createHighlightRuntime(importModule: HighlightModuleImporter): HtmlPreviewHighlightRuntime;
|
|
17
17
|
/**
|
|
@@ -20,7 +20,7 @@ function createHighlightRuntime(importModule) {
|
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
const defaultRuntime = createHighlightRuntime(
|
|
23
|
-
() => import("@takazudo/zfb-md-wasm")
|
|
23
|
+
() => import("@takazudo/zfb-md-wasm/highlight")
|
|
24
24
|
);
|
|
25
25
|
function getUsableHighlightHtml(result) {
|
|
26
26
|
if (result.html == null || result.diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, resolve } from "node:path";
|
|
1
|
+
import { mkdirSync, readdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
3
|
import { generateChangelogMarkdown } from "./generate.js";
|
|
4
4
|
import { loadChangelogEntries } from "./load.js";
|
|
5
5
|
function emitChangelogs(options) {
|
|
@@ -8,6 +8,16 @@ function emitChangelogs(options) {
|
|
|
8
8
|
const sourceDir = resolve(options.projectRoot, config.sourceDir);
|
|
9
9
|
const outputFile = resolve(options.projectRoot, config.outputFile);
|
|
10
10
|
const entries = loadChangelogEntries({ sourceDir });
|
|
11
|
+
if (entries.length === 0) {
|
|
12
|
+
const nestedMdxDirs = findNestedMdxDirs(sourceDir);
|
|
13
|
+
if (nestedMdxDirs.length > 0) {
|
|
14
|
+
options.logger?.warn?.(
|
|
15
|
+
`Changelog sourceDir "${config.sourceDir}" (${sourceDir}) yielded 0 releases but contains .mdx files in sub-directories: ${nestedMdxDirs.join(
|
|
16
|
+
", "
|
|
17
|
+
)}. In a multi-changelog layout, each changelogs[] entry must point at a per-package directory (for example "${config.sourceDir}/<name>") whose per-release files are non-index .mdx files; the loader intentionally skips index.mdx.`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
11
21
|
const markdown = generateChangelogMarkdown(entries, {
|
|
12
22
|
title: config.title,
|
|
13
23
|
packageName: config.packageName
|
|
@@ -19,6 +29,13 @@ function emitChangelogs(options) {
|
|
|
19
29
|
}
|
|
20
30
|
return { written };
|
|
21
31
|
}
|
|
32
|
+
function findNestedMdxDirs(sourceDir) {
|
|
33
|
+
return readdirSync(sourceDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).filter(
|
|
34
|
+
(entry) => readdirSync(join(sourceDir, entry.name), { withFileTypes: true }).some(
|
|
35
|
+
(nestedEntry) => nestedEntry.isFile() && nestedEntry.name.endsWith(".mdx")
|
|
36
|
+
)
|
|
37
|
+
).map((entry) => entry.name).sort();
|
|
38
|
+
}
|
|
22
39
|
export {
|
|
23
40
|
emitChangelogs
|
|
24
41
|
};
|
|
@@ -2,7 +2,7 @@ import type { Settings } from "../settings.js";
|
|
|
2
2
|
/** Any Preact-renderable component (function returning a VNode/children). */
|
|
3
3
|
type AnyComponent = (props: Record<string, unknown>) => unknown;
|
|
4
4
|
/**
|
|
5
|
-
* The
|
|
5
|
+
* The host-supplied, locale-aware nav wrappers. Each accepts a `lang` prop;
|
|
6
6
|
* the factory injects the active `locale` so the wrapper queries the correct
|
|
7
7
|
* collection. `SiteTreeNav` is reused for both the `SiteTreeNav` and
|
|
8
8
|
* `SiteTreeNavDemo` MDX tags (matching the showcase mapping).
|
|
@@ -11,6 +11,7 @@ export interface MdxNavData {
|
|
|
11
11
|
CategoryNav: AnyComponent;
|
|
12
12
|
CategoryTreeNav: AnyComponent;
|
|
13
13
|
SiteTreeNav: AnyComponent;
|
|
14
|
+
NoteTrayIndex: AnyComponent;
|
|
14
15
|
}
|
|
15
16
|
export interface CreateMdxComponentsOptions {
|
|
16
17
|
/**
|
|
@@ -35,6 +36,8 @@ export interface CreateMdxComponentsOptions {
|
|
|
35
36
|
* which wrapper `navData` holds.
|
|
36
37
|
*/
|
|
37
38
|
currentVersion?: string;
|
|
39
|
+
/** Canonical slug of the page being rendered, used to infer its containing note tray. */
|
|
40
|
+
currentSlug: string;
|
|
38
41
|
/** Host-supplied locale-aware nav wrappers. */
|
|
39
42
|
navData: MdxNavData;
|
|
40
43
|
/**
|