@takazudo/zudo-doc 5.19.0 → 5.20.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 (76) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/asset-page/index.d.ts +3 -1
  3. package/dist/asset-page/index.js +5 -4
  4. package/dist/breadcrumb/breadcrumb.js +21 -14
  5. package/dist/chrome/derive.d.ts +23 -1
  6. package/dist/chrome/derive.js +17 -2
  7. package/dist/chrome-bindings.d.ts +6 -1
  8. package/dist/compiled.css +23 -0
  9. package/dist/config.d.ts +13 -1
  10. package/dist/config.js +1 -0
  11. package/dist/date-format-resolve/index.d.ts +10 -0
  12. package/dist/date-format-resolve/index.js +22 -0
  13. package/dist/doc-content-header/index.js +4 -2
  14. package/dist/doc-history/index.d.ts +17 -1
  15. package/dist/doc-history/index.js +19 -13
  16. package/dist/doc-history-area/index.d.ts +3 -1
  17. package/dist/doc-history-area/index.js +7 -2
  18. package/dist/doc-metainfo-area/index.js +4 -2
  19. package/dist/doc-pager/index.js +4 -2
  20. package/dist/docs-schema/index.d.ts +1 -1
  21. package/dist/eject/index.js +1 -1
  22. package/dist/factory-context/index.d.ts +3 -6
  23. package/dist/format-date/index.d.ts +15 -6
  24. package/dist/format-date/index.js +79 -15
  25. package/dist/header-with-defaults/index.js +5 -1
  26. package/dist/home-page/index.js +9 -7
  27. package/dist/nav-indexing/note-tray-index-parts/card-list.js +18 -5
  28. package/dist/nav-indexing/note-tray-index-parts/date-line.d.ts +3 -1
  29. package/dist/nav-indexing/note-tray-index-parts/date-line.js +4 -3
  30. package/dist/nav-indexing/note-tray-index-parts/index-list.js +9 -1
  31. package/dist/nav-indexing/note-tray-index-parts/timeline.js +2 -2
  32. package/dist/nav-indexing/note-tray-index.d.ts +9 -0
  33. package/dist/note-tray-index/index.d.ts +9 -0
  34. package/dist/note-tray-index/index.js +3 -1
  35. package/dist/note-tray-model/index.d.ts +13 -3
  36. package/dist/note-tray-model/index.js +9 -2
  37. package/dist/plugins/internal/claude-resources/generate.js +3 -2
  38. package/dist/plugins/internal/codex-resources/overview.js +3 -2
  39. package/dist/plugins/internal/resource-docs-shared/fs.d.ts +1 -1
  40. package/dist/plugins/internal/resource-docs-shared/fs.js +3 -2
  41. package/dist/routes/_context.d.ts +1 -4
  42. package/dist/safelist.css +1 -1
  43. package/dist/settings.d.ts +37 -0
  44. package/dist/sidebar-toggle-island/index.d.ts +16 -1
  45. package/dist/sidebar-toggle-island/index.js +6 -2
  46. package/dist/sidebar-tree-island/index.d.ts +15 -1
  47. package/dist/sidebar-tree-island/index.js +19 -8
  48. package/dist/sidebar-with-defaults/index.js +5 -2
  49. package/dist/site-tree-nav/index.d.ts +13 -0
  50. package/dist/site-tree-nav/index.js +6 -2
  51. package/dist/site-tree-nav-island/index.d.ts +9 -1
  52. package/dist/site-tree-nav-island/index.js +19 -3
  53. package/dist/theme-no-reset.css +379 -0
  54. package/dist/theme-packs/academia/pack.css +12 -2
  55. package/dist/theme-packs/blueprint/pack.css +7 -1
  56. package/dist/theme-packs/broadsheet/pack.css +7 -1
  57. package/dist/theme-packs/brutalist/pack.css +21 -3
  58. package/dist/theme-packs/drift/pack.css +5 -1
  59. package/dist/theme-packs/foundry/pack.css +13 -2
  60. package/dist/theme-packs/hearth/pack.css +7 -1
  61. package/dist/theme-packs/ledger/pack.css +12 -2
  62. package/dist/theme-packs/observatory/pack.css +5 -1
  63. package/dist/theme-packs/riso/pack.css +9 -6
  64. package/dist/theme-packs/sakura/pack.css +6 -2
  65. package/dist/theme-packs/sumi/pack.css +7 -1
  66. package/dist/theme-packs/swissgrid/pack.css +7 -1
  67. package/dist/theme-packs/tidepool/pack.css +8 -1
  68. package/dist/theme-packs/washi/pack.css +5 -2
  69. package/dist/theme.css +60 -0
  70. package/eject/breadcrumb/breadcrumb.tsx +8 -3
  71. package/eject/doc-history/index.tsx +41 -16
  72. package/eject/doc-pager/index.tsx +12 -2
  73. package/eject/sidebar-toggle-island/index.tsx +19 -0
  74. package/eject/sidebar-tree-island/index.tsx +40 -4
  75. package/eject/site-tree-nav-island/index.tsx +31 -5
  76. package/package.json +10 -9
@@ -1,4 +1,4 @@
1
- import type { Settings } from "../settings.js";
1
+ import type { LocaleConfig, Settings } from "../settings.js";
2
2
  import type { AssetManifest, ColorScheme, ThemePackRegistry } from "../route-context-payload/types.js";
3
3
  import type { UrlHelpers } from "../url-helpers/index.js";
4
4
  import type { NavSourceDocsAPI } from "../nav-source-docs/index.js";
@@ -130,11 +130,8 @@ export interface RouteContext<S = Settings> extends UrlHelpers, NavSourceDocsAPI
130
130
  defaultLocale: string;
131
131
  /** All supported locale codes, default first. */
132
132
  locales: readonly string[];
133
- /** Per-locale `{ label, dir }` config lookup. */
134
- getLocaleConfig(locale: string): {
135
- label: string;
136
- dir: string;
137
- } | undefined;
133
+ /** Per-locale configuration lookup, including an optional home description. */
134
+ getLocaleConfig(locale: string): LocaleConfig | undefined;
138
135
  /** Display label for a locale. */
139
136
  getLocaleLabel(locale: string): string;
140
137
  /**
@@ -3,14 +3,23 @@ export interface IsoDateParts {
3
3
  month: number;
4
4
  day: number;
5
5
  }
6
+ /** "locale" keeps today's Intl behaviour; anything else is a token pattern. */
7
+ export type DateFormatPattern = "locale" | (string & {});
6
8
  export declare function parseIsoDate(iso: string): IsoDateParts | undefined;
7
9
  /** Format an ISO date for display while retaining the established locale map. */
8
- export declare function formatDate(iso: string, locale: string): string;
10
+ export declare function formatDate(iso: string, locale: string, pattern?: DateFormatPattern): string;
9
11
  /** Format the localized month/day portion of an ISO date in UTC. */
10
- export declare function formatMonthDayLabel(iso: string, locale: string): string;
12
+ export declare function formatMonthDayLabel(iso: string, locale: string, pattern?: DateFormatPattern): string;
11
13
  /** Format the localized year portion of an ISO date in UTC. */
12
- export declare function formatYear(iso: string, locale: string): string;
14
+ export declare function formatYear(iso: string, locale: string, pattern?: DateFormatPattern): string;
13
15
  /** Format a year/month label with the year first in every locale. */
14
- export declare function formatYearMonth(iso: string, locale: string): string;
15
- /** Return the stable numeric month/day portion of a calendar-valid ISO date. */
16
- export declare function formatMonthDay(iso: string): string;
16
+ export declare function formatYearMonth(iso: string, locale: string, pattern?: DateFormatPattern): string;
17
+ /**
18
+ * Return the stable numeric month/day portion of a calendar-valid ISO date.
19
+ *
20
+ * The parameter order is deliberately `(iso, pattern, locale)`: this formatter
21
+ * had no locale parameter at all, so `locale` is appended last and is only
22
+ * consulted for the `MMM`/`MMMM` tokens. Input stays strictly date-only in both
23
+ * the default and the pattern path — full timestamps are returned unchanged.
24
+ */
25
+ export declare function formatMonthDay(iso: string, pattern?: DateFormatPattern, locale?: string): string;
@@ -9,29 +9,86 @@ function parseIsoDate(iso) {
9
9
  const year = Number(match[1]);
10
10
  const month = Number(match[2]);
11
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);
12
+ const date = utcDateFromParts({ year, month, day });
15
13
  if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day) {
16
14
  return void 0;
17
15
  }
18
16
  return { year, month, day };
19
17
  }
18
+ function utcDateFromParts(parts) {
19
+ const date = /* @__PURE__ */ new Date(0);
20
+ date.setUTCHours(0, 0, 0, 0);
21
+ date.setUTCFullYear(parts.year, parts.month - 1, parts.day);
22
+ return date;
23
+ }
20
24
  function toUtcDate(iso) {
21
25
  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
- }
26
+ if (parts) return utcDateFromParts(parts);
28
27
  if (!/^\d{4}-\d{2}-\d{2}T/.test(iso)) return void 0;
29
28
  const timestamp = new Date(iso);
30
29
  return Number.isNaN(timestamp.getTime()) ? void 0 : timestamp;
31
30
  }
32
- function formatDate(iso, locale) {
31
+ const PATTERN_TOKENS = ["YYYY", "YY", "MMMM", "MMM", "MM", "M", "DD", "D"];
32
+ function monthName(date, locale, width) {
33
+ return new Intl.DateTimeFormat(LOCALE_TO_BCP47[locale] ?? "en-US", {
34
+ month: width,
35
+ timeZone: "UTC"
36
+ }).format(date);
37
+ }
38
+ function renderToken(token, date, locale) {
39
+ switch (token) {
40
+ case "YYYY":
41
+ return String(date.getUTCFullYear()).padStart(4, "0");
42
+ case "YY":
43
+ return String(date.getUTCFullYear() % 100).padStart(2, "0");
44
+ case "MMMM":
45
+ return monthName(date, locale, "long");
46
+ case "MMM":
47
+ return monthName(date, locale, "short");
48
+ case "MM":
49
+ return String(date.getUTCMonth() + 1).padStart(2, "0");
50
+ case "M":
51
+ return String(date.getUTCMonth() + 1);
52
+ case "DD":
53
+ return String(date.getUTCDate()).padStart(2, "0");
54
+ case "D":
55
+ return String(date.getUTCDate());
56
+ }
57
+ }
58
+ function applyPattern(date, pattern, locale) {
59
+ let out = "";
60
+ let index = 0;
61
+ while (index < pattern.length) {
62
+ const char = pattern[index];
63
+ if (char === "[") {
64
+ const close = pattern.indexOf("]", index + 1);
65
+ if (close !== -1) {
66
+ out += pattern.slice(index + 1, close);
67
+ index = close + 1;
68
+ continue;
69
+ }
70
+ out += char;
71
+ index += 1;
72
+ continue;
73
+ }
74
+ const token = PATTERN_TOKENS.find((candidate) => pattern.startsWith(candidate, index));
75
+ if (token) {
76
+ out += renderToken(token, date, locale);
77
+ index += token.length;
78
+ continue;
79
+ }
80
+ out += char;
81
+ index += 1;
82
+ }
83
+ return out;
84
+ }
85
+ function usesPattern(pattern) {
86
+ return pattern !== void 0 && pattern !== "locale";
87
+ }
88
+ function formatDate(iso, locale, pattern) {
33
89
  const date = toUtcDate(iso);
34
90
  if (!date) return iso;
91
+ if (usesPattern(pattern)) return applyPattern(date, pattern, locale);
35
92
  return new Intl.DateTimeFormat(LOCALE_TO_BCP47[locale] ?? "en-US", {
36
93
  year: "numeric",
37
94
  month: "short",
@@ -39,26 +96,29 @@ function formatDate(iso, locale) {
39
96
  timeZone: "UTC"
40
97
  }).format(date);
41
98
  }
42
- function formatMonthDayLabel(iso, locale) {
99
+ function formatMonthDayLabel(iso, locale, pattern) {
43
100
  const date = toUtcDate(iso);
44
101
  if (!date) return iso;
102
+ if (usesPattern(pattern)) return applyPattern(date, pattern, locale);
45
103
  return new Intl.DateTimeFormat(LOCALE_TO_BCP47[locale] ?? "en-US", {
46
104
  month: "short",
47
105
  day: "numeric",
48
106
  timeZone: "UTC"
49
107
  }).format(date);
50
108
  }
51
- function formatYear(iso, locale) {
109
+ function formatYear(iso, locale, pattern) {
52
110
  const date = toUtcDate(iso);
53
111
  if (!date) return iso;
112
+ if (usesPattern(pattern)) return applyPattern(date, pattern, locale);
54
113
  return new Intl.DateTimeFormat(LOCALE_TO_BCP47[locale] ?? "en-US", {
55
114
  year: "numeric",
56
115
  timeZone: "UTC"
57
116
  }).format(date);
58
117
  }
59
- function formatYearMonth(iso, locale) {
118
+ function formatYearMonth(iso, locale, pattern) {
60
119
  const date = toUtcDate(iso.length === 7 ? `${iso}-01` : iso);
61
120
  if (!date) return iso;
121
+ if (usesPattern(pattern)) return applyPattern(date, pattern, locale);
62
122
  const year = new Intl.DateTimeFormat(LOCALE_TO_BCP47[locale] ?? "en-US", {
63
123
  year: "numeric",
64
124
  timeZone: "UTC"
@@ -69,9 +129,13 @@ function formatYearMonth(iso, locale) {
69
129
  }).format(date);
70
130
  return locale === "ja" ? `${year}${month}` : `${year} ${month}`;
71
131
  }
72
- function formatMonthDay(iso) {
132
+ function formatMonthDay(iso, pattern, locale) {
73
133
  const parts = parseIsoDate(iso);
74
- return parts ? `${String(parts.month).padStart(2, "0")}-${String(parts.day).padStart(2, "0")}` : iso;
134
+ if (!parts) return iso;
135
+ if (usesPattern(pattern)) {
136
+ return applyPattern(utcDateFromParts(parts), pattern, locale ?? "en");
137
+ }
138
+ return `${String(parts.month).padStart(2, "0")}-${String(parts.day).padStart(2, "0")}`;
75
139
  }
76
140
  export {
77
141
  formatDate,
@@ -10,6 +10,7 @@ import { SidebarToggle } from "../sidebar-toggle-island/index.js";
10
10
  import { buildGitHubRepoUrl as buildGitHubRepoUrlBase } from "../github-helpers/index.js";
11
11
  import { assertChromeContext } from "../chrome/assert-chrome-context.js";
12
12
  import {
13
+ deriveDateFormats,
13
14
  deriveGetUnavailableVersions,
14
15
  deriveNavDataPrep,
15
16
  deriveSearchWidgetSlot,
@@ -18,6 +19,7 @@ import {
18
19
  function createHeaderWithDefaults(ctx) {
19
20
  assertChromeContext(ctx, "createHeaderWithDefaults");
20
21
  const settings = ctx.settings;
22
+ const dateFormatsFor = deriveDateFormats(ctx);
21
23
  const defaultLocale = ctx.defaultLocale;
22
24
  const locales = ctx.locales;
23
25
  const t = ctx.t;
@@ -54,8 +56,10 @@ function createHeaderWithDefaults(ctx) {
54
56
  currentSlug,
55
57
  rootMenuItems,
56
58
  backToMenuLabel,
59
+ locale: lang,
57
60
  localeLinks,
58
- themeDefaultMode
61
+ themeDefaultMode,
62
+ dateFormats: dateFormatsFor(lang)
59
63
  }
60
64
  )
61
65
  });
@@ -8,7 +8,7 @@ import { TagNav } from "../nav-indexing/tag-nav.js";
8
8
  import { CategoryLinkIcon } from "../tree-nav-shared/index.js";
9
9
  import { createHeadWithDefaults } from "../head-with-defaults/index.js";
10
10
  import { resolveThemePackSsrSlug } from "../theme/theme-pack-provider.js";
11
- import { deriveComposeMetaTitle, deriveBodyEndIslands } from "../chrome/derive.js";
11
+ import { deriveComposeMetaTitle, deriveBodyEndIslands, deriveDateFormats } from "../chrome/derive.js";
12
12
  import { derivePrimaryChromeSlots } from "../chrome/primary-slots.js";
13
13
  import { assertChromeContext } from "../chrome/assert-chrome-context.js";
14
14
  import { prepareHomeData } from "./prepare-home-data.js";
@@ -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 dateFormatsFor = deriveDateFormats(ctx);
21
22
  const categoryIgnore = settings.siteTreeNavIgnore ?? [];
22
23
  const t = ctx.t;
23
24
  const withBase = ctx.withBase;
@@ -52,7 +53,7 @@ function createHomePageView(ctx) {
52
53
  const rowItems = [];
53
54
  if (primary) {
54
55
  rowItems.push(
55
- /* @__PURE__ */ jsx("a", { href: primary.href, class: "text-fg underline hover:text-accent", children: primary.label })
56
+ /* @__PURE__ */ jsx("a", { href: primary.href, class: "whitespace-nowrap text-fg underline hover:text-accent", children: primary.label })
56
57
  );
57
58
  }
58
59
  if (settings.githubUrl) {
@@ -61,7 +62,7 @@ function createHomePageView(ctx) {
61
62
  "a",
62
63
  {
63
64
  href: settings.githubUrl,
64
- class: "inline-flex items-center gap-[0.3em] text-fg underline hover:text-accent",
65
+ class: "inline-flex items-center gap-[0.3em] whitespace-nowrap text-fg underline hover:text-accent",
65
66
  target: "_blank",
66
67
  rel: "noopener noreferrer",
67
68
  children: [
@@ -116,10 +117,10 @@ function createHomePageView(ctx) {
116
117
  "aria-hidden": "true"
117
118
  }
118
119
  ) : null,
119
- /* @__PURE__ */ jsxs("div", { children: [
120
+ /* @__PURE__ */ jsxs("div", { class: "min-w-0", children: [
120
121
  /* @__PURE__ */ jsx("h1", { class: "text-heading font-bold mb-vsp-2xs break-words", children: settings.siteName }),
121
- /* @__PURE__ */ jsx("p", { class: "text-muted text-small mb-vsp-sm", children: settings.siteDescription }),
122
- /* @__PURE__ */ jsx("div", { class: "flex items-center justify-center lg:justify-start gap-hsp-md text-small", children: rowItems.map((item, index) => /* @__PURE__ */ jsxs(Fragment2, { children: [
122
+ /* @__PURE__ */ jsx("p", { class: "text-muted text-small mb-vsp-sm", children: settings.locales[locale]?.description ?? settings.siteDescription }),
123
+ /* @__PURE__ */ jsx("div", { class: "flex flex-wrap items-center justify-center lg:justify-start gap-hsp-md text-small", children: rowItems.map((item, index) => /* @__PURE__ */ jsxs(Fragment2, { children: [
123
124
  index > 0 && /* @__PURE__ */ jsx("span", { class: "text-muted", children: "/" }),
124
125
  item
125
126
  ] }, index)) })
@@ -135,7 +136,8 @@ function createHomePageView(ctx) {
135
136
  categoryIgnore,
136
137
  initiallyCollapsedCategorySlugs,
137
138
  locale,
138
- updatedLabel: t("doc.updated", locale)
139
+ updatedLabel: t("doc.updated", locale),
140
+ dateFormats: dateFormatsFor(locale)
139
141
  }
140
142
  )
141
143
  }),
@@ -17,7 +17,15 @@ function CardBody({
17
17
  /* @__PURE__ */ jsx("span", { class: "flex h-[1lh] items-center", children: /* @__PURE__ */ jsx(CategoryLinkIcon, { className: "w-icon-sm" }) }),
18
18
  item.label
19
19
  ] }) }),
20
- props.showDate && /* @__PURE__ */ jsx("span", { class: hasStamp ? "sm:hidden" : void 0, children: /* @__PURE__ */ jsx(DateLine, { item, locale: props.locale, updatedLabel: props.updatedLabel }) })
20
+ props.showDate && /* @__PURE__ */ jsx("span", { class: hasStamp ? "sm:hidden" : void 0, children: /* @__PURE__ */ jsx(
21
+ DateLine,
22
+ {
23
+ item,
24
+ locale: props.locale,
25
+ updatedLabel: props.updatedLabel,
26
+ dateFormats: props.dateFormats
27
+ }
28
+ ) })
21
29
  ] }),
22
30
  item.description && /* @__PURE__ */ jsx("p", { class: descriptionClass, children: item.description })
23
31
  ] });
@@ -27,7 +35,8 @@ function DateStamp({
27
35
  updated,
28
36
  locale,
29
37
  updatedLabel,
30
- positioned
38
+ positioned,
39
+ dateFormats
31
40
  }) {
32
41
  return /* @__PURE__ */ jsxs(
33
42
  "time",
@@ -35,12 +44,12 @@ function DateStamp({
35
44
  datetime: date,
36
45
  class: positioned ? "hidden sm:flex sm:col-start-2 sm:row-start-1 sm:row-span-2 w-[6.5rem] flex-col items-end self-stretch border-l border-muted pl-hsp-xl text-muted tabular-nums" : "hidden sm:flex w-[6.5rem] flex-col items-end self-stretch border-l border-muted pl-hsp-xl text-muted tabular-nums",
37
46
  children: [
38
- /* @__PURE__ */ jsx("span", { class: "block text-title leading-tight font-medium", children: formatMonthDayLabel(date, locale) }),
39
- /* @__PURE__ */ jsx("span", { class: "block text-caption", children: formatYear(date, locale) }),
47
+ /* @__PURE__ */ jsx("span", { class: "block text-title leading-tight font-medium", children: formatMonthDayLabel(date, locale, dateFormats?.monthDay) }),
48
+ /* @__PURE__ */ jsx("span", { class: "block text-caption", children: formatYear(date, locale, dateFormats?.year) }),
40
49
  updated && /* @__PURE__ */ jsxs("span", { class: "mt-vsp-3xs block text-micro", children: [
41
50
  updatedLabel,
42
51
  " ",
43
- formatMonthDayLabel(updated, locale)
52
+ formatMonthDayLabel(updated, locale, dateFormats?.monthDay)
44
53
  ] })
45
54
  ]
46
55
  }
@@ -74,6 +83,7 @@ function CardList(props) {
74
83
  updated: item.updated,
75
84
  locale: props.locale,
76
85
  updatedLabel: props.updatedLabel,
86
+ dateFormats: props.dateFormats,
77
87
  positioned: true
78
88
  }
79
89
  )
@@ -96,6 +106,7 @@ function CardList(props) {
96
106
  updated: item.updated,
97
107
  locale: props.locale,
98
108
  updatedLabel: props.updatedLabel,
109
+ dateFormats: props.dateFormats,
99
110
  positioned: true
100
111
  }
101
112
  )
@@ -123,6 +134,7 @@ function CardList(props) {
123
134
  updated: item.updated,
124
135
  locale: props.locale,
125
136
  updatedLabel: props.updatedLabel,
137
+ dateFormats: props.dateFormats,
126
138
  positioned: false
127
139
  }
128
140
  )
@@ -145,6 +157,7 @@ function CardList(props) {
145
157
  updated: item.updated,
146
158
  locale: props.locale,
147
159
  updatedLabel: props.updatedLabel,
160
+ dateFormats: props.dateFormats,
148
161
  positioned: false
149
162
  }
150
163
  )
@@ -1,12 +1,14 @@
1
1
  /** @jsxRuntime automatic */
2
2
  /** @jsxImportSource preact */
3
3
  import type { JSX } from "preact";
4
+ import type { ResolvedDateFormats } from "../../settings.js";
4
5
  import type { NoteTrayIndexItem } from "../note-tray-index.js";
5
6
  import type { TagNavLabels } from "../types.js";
6
- export declare function DateLine({ item, locale, updatedLabel, }: {
7
+ export declare function DateLine({ item, locale, updatedLabel, dateFormats, }: {
7
8
  item: NoteTrayIndexItem;
8
9
  locale: string;
9
10
  updatedLabel: string;
11
+ dateFormats?: ResolvedDateFormats;
10
12
  }): JSX.Element | null;
11
13
  export declare function ItemTags({ item, labels }: {
12
14
  item: NoteTrayIndexItem;
@@ -4,16 +4,17 @@ import { TagNav } from "../tag-nav.js";
4
4
  function DateLine({
5
5
  item,
6
6
  locale,
7
- updatedLabel
7
+ updatedLabel,
8
+ dateFormats
8
9
  }) {
9
10
  if (!item.date && !item.updated) return null;
10
11
  return /* @__PURE__ */ jsxs("span", { class: "tabular-nums text-caption text-muted", children: [
11
- item.date && /* @__PURE__ */ jsx("time", { datetime: item.date, children: formatDate(item.date, locale) }),
12
+ item.date && /* @__PURE__ */ jsx("time", { datetime: item.date, children: formatDate(item.date, locale, dateFormats?.full) }),
12
13
  item.date && item.updated && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: " \xB7 " }),
13
14
  item.updated && /* @__PURE__ */ jsxs("span", { children: [
14
15
  updatedLabel,
15
16
  " ",
16
- /* @__PURE__ */ jsx("time", { datetime: item.updated, children: formatDate(item.updated, locale) })
17
+ /* @__PURE__ */ jsx("time", { datetime: item.updated, children: formatDate(item.updated, locale, dateFormats?.full) })
17
18
  ] })
18
19
  ] });
19
20
  }
@@ -14,7 +14,15 @@ function IndexList(props) {
14
14
  /* @__PURE__ */ jsxs("span", { class: "min-w-0", children: [
15
15
  /* @__PURE__ */ jsx("span", { class: labelClass, children: item.label }),
16
16
  item.description && /* @__PURE__ */ jsx("span", { class: detailClass, children: item.description }),
17
- props.showDate && /* @__PURE__ */ jsx("span", { class: dateClass, children: /* @__PURE__ */ jsx(DateLine, { item, locale: props.locale, updatedLabel: props.updatedLabel }) })
17
+ props.showDate && /* @__PURE__ */ jsx("span", { class: dateClass, children: /* @__PURE__ */ jsx(
18
+ DateLine,
19
+ {
20
+ item,
21
+ locale: props.locale,
22
+ updatedLabel: props.updatedLabel,
23
+ dateFormats: props.dateFormats
24
+ }
25
+ ) })
18
26
  ] })
19
27
  ] });
20
28
  return /* @__PURE__ */ jsx("li", { class: rowClass, children: item.href ? /* @__PURE__ */ jsx("a", { href: item.href, class: "group grid grid-cols-[auto_1fr] gap-x-hsp-lg py-vsp-md", children: rowContent }) : /* @__PURE__ */ jsx("span", { class: "grid grid-cols-[auto_1fr] gap-x-hsp-lg py-vsp-md", children: rowContent }) }, item.slug);
@@ -9,7 +9,7 @@ import { ItemTags } from "./date-line.js";
9
9
  function Timeline(props) {
10
10
  const groups = groupItems(props.items, "month", props.order ?? "asc");
11
11
  return /* @__PURE__ */ jsx("div", { class: "space-y-vsp-lg", children: groups.map((group) => /* @__PURE__ */ jsxs("section", { children: [
12
- /* @__PURE__ */ jsx("h2", { class: "mb-vsp-sm text-small font-medium text-fg", children: formatYearMonthLabel(group.key, props.locale) }),
12
+ /* @__PURE__ */ jsx("h2", { class: "mb-vsp-sm text-small font-medium text-fg", children: formatYearMonthLabel(group.key, props.locale, props.dateFormats?.yearMonth) }),
13
13
  /* @__PURE__ */ jsx("ol", { class: "ml-hsp-md border-l border-muted [&_li]:mb-0", children: group.items.map((item) => /* @__PURE__ */ jsxs("li", { class: "relative grid gap-vsp-2xs pl-hsp-xl pb-vsp-lg last:pb-0", children: [
14
14
  item.href ? /* @__PURE__ */ jsx(
15
15
  "a",
@@ -26,7 +26,7 @@ function Timeline(props) {
26
26
  class: "absolute top-hsp-2xs -left-[calc(var(--spacing-icon-lg)/2)] grid size-icon-lg place-items-center rounded-full border border-muted bg-bg text-caption leading-none tabular-nums text-muted peer-hover:border-accent peer-hover:text-accent peer-focus-visible:border-accent peer-focus-visible:text-accent",
27
27
  children: [
28
28
  /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: item.date ? parseIsoDate(item.date)?.day ?? "" : "" }),
29
- /* @__PURE__ */ jsx("span", { class: "sr-only", children: item.date ? formatDate(item.date, props.locale) : "" })
29
+ /* @__PURE__ */ jsx("span", { class: "sr-only", children: item.date ? formatDate(item.date, props.locale, props.dateFormats?.full) : "" })
30
30
  ]
31
31
  }
32
32
  ),
@@ -3,6 +3,7 @@
3
3
  import type { JSX } from "preact";
4
4
  import type { TagLink, TagNavLabels } from "./types.js";
5
5
  import type { NoteTrayOrder } from "../note-tray-model/index.js";
6
+ import type { ResolvedDateFormats } from "../settings.js";
6
7
  export type NoteTrayIndexStyle = "index" | "cards" | "timeline";
7
8
  export interface NoteTrayIndexItem {
8
9
  slug: string;
@@ -25,5 +26,13 @@ export interface NoteTrayIndexProps {
25
26
  dated: boolean;
26
27
  order?: NoteTrayOrder;
27
28
  tagLabels?: TagNavLabels;
29
+ /**
30
+ * Per-role date patterns already resolved for `locale` by the SSR wrapper
31
+ * (`note-tray-index`). `NoteTrayIndex` spreads its props into the style
32
+ * parts, so `IndexList` / `CardList` / `Timeline` receive this unchanged.
33
+ * Optional and absent-safe — omitted means every role behaves as `"locale"`
34
+ * (#4075).
35
+ */
36
+ dateFormats?: ResolvedDateFormats;
28
37
  }
29
38
  export declare function NoteTrayIndex(props: NoteTrayIndexProps): JSX.Element | null;
@@ -2,6 +2,7 @@
2
2
  /** @jsxImportSource preact */
3
3
  import type { JSX } from "preact";
4
4
  import { type NoteTrayIndexItem, type NoteTrayIndexStyle } from "../nav-indexing/index.js";
5
+ import type { DateFormatSetting } from "../settings.js";
5
6
  export interface NoteTrayIndexNode extends NoteTrayIndexItem {
6
7
  shape?: "note-tray";
7
8
  sortOrder?: "asc" | "desc";
@@ -41,5 +42,13 @@ export interface NoteTrayIndexDeps {
41
42
  tagHref: (tag: string, locale: string) => string;
42
43
  t: (key: string, locale: string) => string;
43
44
  versionedDocsUrl: (slug: string, versionSlug: string, lang: string) => string;
45
+ /**
46
+ * The raw `dateFormat` setting (`ctx.settings.dateFormat`). Resolved for the
47
+ * render locale and handed to `<NoteTrayIndex>` as `dateFormats`; the
48
+ * note-tray parts are plain prop-driven components with no ChromeContext of
49
+ * their own, so this prop chain is their only route to the setting (#4075).
50
+ * Optional — omitted means every role resolves to `"locale"`.
51
+ */
52
+ dateFormat?: DateFormatSetting;
44
53
  }
45
54
  export declare function createNoteTrayIndexWrapper(deps: NoteTrayIndexDeps): (props: NoteTrayIndexWrapperProps) => JSX.Element | null;
@@ -4,6 +4,7 @@ import {
4
4
  NoteTrayIndex
5
5
  } from "../nav-indexing/index.js";
6
6
  import { findContainingNoteTray, getNoteTrayItems } from "../note-tray-model/index.js";
7
+ import { resolveDateFormats } from "../date-format-resolve/index.js";
7
8
  function createNoteTrayIndexWrapper(deps) {
8
9
  return function NoteTrayIndexWrapper({
9
10
  style = "index",
@@ -42,7 +43,8 @@ function createNoteTrayIndexWrapper(deps) {
42
43
  tagLabels: {
43
44
  tags: deps.t("doc.tags", lang),
44
45
  taggedWith: deps.t("doc.taggedWith", lang)
45
- }
46
+ },
47
+ dateFormats: resolveDateFormats(deps.dateFormat, lang)
46
48
  }
47
49
  );
48
50
  };
@@ -1,6 +1,6 @@
1
- import { formatDate, parseIsoDate, type IsoDateParts } from "../format-date/index.js";
1
+ import { formatDate, parseIsoDate, type DateFormatPattern, type IsoDateParts } from "../format-date/index.js";
2
2
  export { formatDate, parseIsoDate };
3
- export type { IsoDateParts };
3
+ export type { DateFormatPattern, IsoDateParts };
4
4
  export type NoteTrayOrder = "asc" | "desc";
5
5
  export type NoteTrayGrouping = "year" | "month";
6
6
  /** Minimal structural node accepted by every note-tray consumer. */
@@ -26,6 +26,16 @@ export declare function getNoteTrayItems<T extends NoteTrayNode>(tray: T): T[];
26
26
  export declare function rankWidth(itemsOrCount: readonly unknown[] | number): number;
27
27
  export declare function yearKey(iso: string): string;
28
28
  export declare function yearMonthKey(iso: string): string;
29
- export declare function formatYearMonthLabel(isoOrKey: string, locale: string): string;
29
+ export declare function formatYearMonthLabel(isoOrKey: string, locale: string, pattern?: DateFormatPattern): string;
30
+ /**
31
+ * Format a year-group heading from a `yearKey` ("YYYY") or a full ISO date.
32
+ *
33
+ * Without a pattern this returns the key verbatim rather than routing through
34
+ * `formatYear`: the bare year is what year headings have always rendered, and
35
+ * the default output must stay byte-identical (a JA `Intl` year would become
36
+ * "2026年"). A pattern is applied against January 1 of that year, so a
37
+ * `year` pattern carrying month/day tokens renders that boundary date.
38
+ */
39
+ export declare function formatYearLabel(isoOrKey: string, locale: string, pattern?: DateFormatPattern): string;
30
40
  /** Group dated items chronologically and keep rank order within each group. */
31
41
  export declare function groupItems<T extends NoteTrayNode>(items: readonly T[], grouping: NoteTrayGrouping, order: NoteTrayOrder): NoteTrayGroup<T>[];
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  formatDate,
3
+ formatYear,
3
4
  formatYearMonth,
4
5
  parseIsoDate
5
6
  } from "../format-date/index.js";
@@ -39,8 +40,13 @@ function yearMonthKey(iso) {
39
40
  const parts = parseIsoDate(iso);
40
41
  return parts ? `${String(parts.year).padStart(4, "0")}-${String(parts.month).padStart(2, "0")}` : "";
41
42
  }
42
- function formatYearMonthLabel(isoOrKey, locale) {
43
- return formatYearMonth(isoOrKey, locale);
43
+ function formatYearMonthLabel(isoOrKey, locale, pattern) {
44
+ return formatYearMonth(isoOrKey, locale, pattern);
45
+ }
46
+ function formatYearLabel(isoOrKey, locale, pattern) {
47
+ if (pattern === void 0 || pattern === "locale") return isoOrKey;
48
+ const iso = /^\d{4}$/.test(isoOrKey) ? `${isoOrKey}-01-01` : isoOrKey;
49
+ return formatYear(iso, locale, pattern);
44
50
  }
45
51
  function groupItems(items, grouping, order) {
46
52
  const groups = /* @__PURE__ */ new Map();
@@ -64,6 +70,7 @@ export {
64
70
  findContainingNoteTray,
65
71
  findNoteTray,
66
72
  formatDate,
73
+ formatYearLabel,
67
74
  formatYearMonthLabel,
68
75
  getNoteTrayItems,
69
76
  groupItems,
@@ -249,7 +249,8 @@ function generateOverviewIndex(config, {
249
249
  })) {
250
250
  writeGeneratedIndex(
251
251
  overviewPath,
252
- renderOverviewIndex(config, locale, categorySlugs)
252
+ renderOverviewIndex(config, locale, categorySlugs),
253
+ "/docs/claude/"
253
254
  );
254
255
  } else {
255
256
  removeGeneratedIndex(overviewPath);
@@ -341,7 +342,7 @@ function writeLocaleCategoryIndex(config, locale, localeDir, categoryDir, presen
341
342
  position,
342
343
  resourceLabel(config, locale, descriptionKey, fallbackDescription),
343
344
  formatClaudeFrontmatterString,
344
- writeGeneratedIndex
345
+ (absolutePath, content) => writeGeneratedIndex(absolutePath, content, `/docs/${categoryDir}/`)
345
346
  );
346
347
  }
347
348
  function generateClaudeResourcesDocs(config) {
@@ -37,7 +37,8 @@ function generateOverview(config, presence) {
37
37
  })) {
38
38
  writeGeneratedIndex(
39
39
  overviewPath,
40
- renderOverview(config, locale, slugs)
40
+ renderOverview(config, locale, slugs),
41
+ "/docs/codex/"
41
42
  );
42
43
  } else {
43
44
  removeGeneratedIndex(overviewPath);
@@ -105,7 +106,7 @@ function emitLocaleCategoryIndexes(config, locale, localeDir, presence) {
105
106
  fallbackLiteral: fallbackDescription
106
107
  }),
107
108
  formatFrontmatterString,
108
- writeGeneratedIndex
109
+ (absolutePath, content) => writeGeneratedIndex(absolutePath, content, `/docs/${slug}/`)
109
110
  );
110
111
  };
111
112
  write(
@@ -26,7 +26,7 @@ export declare function cleanDir(dir: string): void;
26
26
  * content. A missing file is created; an existing file is only replaced when
27
27
  * its frontmatter explicitly carries `generated: true`.
28
28
  */
29
- export declare function writeGeneratedIndex(absPath: string, mdx: string): void;
29
+ export declare function writeGeneratedIndex(absPath: string, mdx: string, routePrefix?: string): void;
30
30
  /**
31
31
  * Remove a generated locale index that is no longer backed by a source
32
32
  * category. Authored files are intentionally left untouched: locale trees
@@ -52,7 +52,7 @@ function cleanDir(dir) {
52
52
  if (!fs.existsSync(dir)) return;
53
53
  fs.rmSync(dir, { recursive: true, force: true });
54
54
  }
55
- function writeGeneratedIndex(absPath, mdx) {
55
+ function writeGeneratedIndex(absPath, mdx, routePrefix) {
56
56
  if (fs.existsSync(absPath)) {
57
57
  let existing;
58
58
  try {
@@ -64,8 +64,9 @@ function writeGeneratedIndex(absPath, mdx) {
64
64
  }
65
65
  const parsed = parseFrontmatter(existing);
66
66
  if (parsed?.data.generated !== true) {
67
+ const remedy = routePrefix === void 0 ? "add this route to defaultLocaleOnlyPrefixes" : `add "${routePrefix}" to defaultLocaleOnlyPrefixes`;
67
68
  throw new Error(
68
- `resource-docs: refusing to overwrite authored locale index "${absPath}" because its frontmatter does not contain generated: true. Remove or rename the file, then retry.`
69
+ `resource-docs: refusing to overwrite authored locale index "${absPath}" because its frontmatter does not contain generated: true. Remove or rename the file, or ${remedy} to keep this locale's file authoritative, then retry.`
69
70
  );
70
71
  }
71
72
  }
@@ -16,10 +16,7 @@ export declare const colorSchemes: Record<string, ColorScheme> | null;
16
16
  * plugin did not thread one (ADR `docs/adr/theme-packs.md`, Decision 2). */
17
17
  export declare const themePackRegistry: ThemePackRegistry | null;
18
18
  export declare const assetManifest: AssetManifest | null;
19
- export declare const i18n: import("../factory-context/index.js").FactoryI18n, defaultLocale: string, locales: readonly string[], getLocaleConfig: (locale: string) => {
20
- label: string;
21
- dir: string;
22
- } | undefined, getLocaleLabel: (locale: string) => string, t: (key: string, locale?: string) => string, urlHelpers: import("../url-helpers/index.js").UrlHelpers, withBase: (path: string) => string, stripBase: (path: string) => string, docsUrl: (slug: string, lang?: string) => string, versionedDocsUrl: (slug: string, versionSlug: string, lang?: string) => string, navHref: (path: string, lang: string | undefined, currentVersion: string | undefined, versioned?: boolean) => string, isDefaultLocaleOnlyPath: (path: string) => boolean, absoluteUrl: (pageUrl: string) => string | undefined, isExternal: (href: string) => boolean, resolveHref: (href: string) => string, buildLocaleLinks: (currentPath: string, currentLang: string) => import("../url-helpers/index.js").LocaleLink[], getCategoryOrder: () => string[], getNavSectionForSlug: (slug: string) => string | undefined, getNavSubtree: (tree: import("./_chrome.js").DocNavNode[], categoryMatch?: string) => import("./_chrome.js").DocNavNode[], extractHeadings: (body: string) => import("../extract-headings/index.js").HeadingItem[], resolveTagBound: (raw: string) => import("../tag-helpers/index.js").ResolvedTag, collectTags: (entries: ReadonlyArray<{
19
+ export declare const i18n: import("../factory-context/index.js").FactoryI18n, defaultLocale: string, locales: readonly string[], getLocaleConfig: (locale: string) => import("../settings.js").LocaleConfig | undefined, getLocaleLabel: (locale: string) => string, t: (key: string, locale?: string) => string, urlHelpers: import("../url-helpers/index.js").UrlHelpers, withBase: (path: string) => string, stripBase: (path: string) => string, docsUrl: (slug: string, lang?: string) => string, versionedDocsUrl: (slug: string, versionSlug: string, lang?: string) => string, navHref: (path: string, lang: string | undefined, currentVersion: string | undefined, versioned?: boolean) => string, isDefaultLocaleOnlyPath: (path: string) => boolean, absoluteUrl: (pageUrl: string) => string | undefined, isExternal: (href: string) => boolean, resolveHref: (href: string) => string, buildLocaleLinks: (currentPath: string, currentLang: string) => import("../url-helpers/index.js").LocaleLink[], getCategoryOrder: () => string[], getNavSectionForSlug: (slug: string) => string | undefined, getNavSubtree: (tree: import("./_chrome.js").DocNavNode[], categoryMatch?: string) => import("./_chrome.js").DocNavNode[], extractHeadings: (body: string) => import("../extract-headings/index.js").HeadingItem[], resolveTagBound: (raw: string) => import("../tag-helpers/index.js").ResolvedTag, collectTags: (entries: ReadonlyArray<{
23
20
  slug: string;
24
21
  data: {
25
22
  slug?: string;