@takazudo/zudo-doc 1.2.0 → 2.0.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 (52) hide show
  1. package/dist/chrome/derive.d.ts +68 -0
  2. package/dist/chrome/derive.js +231 -0
  3. package/dist/chrome/index.d.ts +37 -0
  4. package/dist/chrome/index.js +43 -0
  5. package/dist/content.css +25 -9
  6. package/dist/doc-body-end/index.d.ts +9 -11
  7. package/dist/doc-body-end/index.js +4 -2
  8. package/dist/doc-content-header/index.d.ts +10 -2
  9. package/dist/doc-content-header/index.js +8 -8
  10. package/dist/doc-history-area/index.d.ts +10 -25
  11. package/dist/doc-history-area/index.js +14 -10
  12. package/dist/doc-metainfo-area/index.d.ts +9 -15
  13. package/dist/doc-metainfo-area/index.js +6 -2
  14. package/dist/doc-page-renderer/index.d.ts +11 -2
  15. package/dist/doc-page-renderer/index.js +22 -15
  16. package/dist/doc-page-shell/index.d.ts +11 -3
  17. package/dist/doc-page-shell/index.js +21 -14
  18. package/dist/doc-pager/index.d.ts +6 -7
  19. package/dist/doc-pager/index.js +2 -2
  20. package/dist/doc-route-entries/index.d.ts +22 -46
  21. package/dist/doc-route-entries/index.js +15 -10
  22. package/dist/doc-tags-area/index.d.ts +9 -19
  23. package/dist/doc-tags-area/index.js +12 -2
  24. package/dist/factory-context/index.d.ts +163 -1
  25. package/dist/footer-with-defaults/index.d.ts +12 -13
  26. package/dist/footer-with-defaults/index.js +8 -10
  27. package/dist/head-with-defaults/index.d.ts +12 -16
  28. package/dist/head-with-defaults/index.js +7 -9
  29. package/dist/header-with-defaults/index.d.ts +10 -31
  30. package/dist/header-with-defaults/index.js +15 -18
  31. package/dist/integrations/claude-resources/generate.js +48 -11
  32. package/dist/nav-source-docs/index.d.ts +20 -54
  33. package/dist/nav-source-docs/index.js +9 -10
  34. package/dist/route-context/index.d.ts +24 -0
  35. package/dist/route-context/index.js +147 -0
  36. package/dist/route-enumerators/index.d.ts +25 -74
  37. package/dist/route-enumerators/index.js +17 -18
  38. package/dist/routes/_chrome.d.ts +6 -25
  39. package/dist/routes/_chrome.js +14 -387
  40. package/dist/routes/_context.d.ts +10 -43
  41. package/dist/routes/_context.js +29 -157
  42. package/dist/safelist.css +1 -1
  43. package/dist/sidebar-with-defaults/index.d.ts +8 -15
  44. package/dist/sidebar-with-defaults/index.js +6 -10
  45. package/dist/tag-pages/index.d.ts +12 -3
  46. package/dist/tag-pages/index.js +24 -19
  47. package/dist/versions-page/index.d.ts +10 -3
  48. package/dist/versions-page/index.js +18 -14
  49. package/eject/doc-pager/index.tsx +8 -10
  50. package/package.json +14 -6
  51. package/routes-src/_chrome.tsx +31 -552
  52. package/routes-src/_context.ts +37 -269
@@ -0,0 +1,68 @@
1
+ /** @jsxRuntime automatic */
2
+ /** @jsxImportSource preact */
3
+ import type { JSX, VNode } from "preact";
4
+ import type { ChromeContext, FactoryComponent } from "../factory-context/index.js";
5
+ import type { Settings } from "../settings.js";
6
+ import { type ColorScheme } from "../color-scheme-utils.js";
7
+ import { createBodyEndIslands } from "../doc-body-end-islands/index.js";
8
+ /** Package-default color scheme used when `colorSchemes` is `null` or a key is
9
+ * missing. Exported so `createChrome` keeps the identical fallback. */
10
+ export declare const DEFAULT_SCHEME: ColorScheme;
11
+ /** Package no-op DocHistory island stub — renders an empty fragment (the
12
+ * `DocHistoryComponent` contract requires a VNode, not null). */
13
+ declare function DocHistoryStub(_props: {
14
+ slug: string;
15
+ locale?: string;
16
+ basePath?: string;
17
+ }): VNode;
18
+ /** Derive the meta-title composer from the context's site name. */
19
+ export declare function deriveComposeMetaTitle(ctx: ChromeContext): (title: string) => string;
20
+ /** Derive the single-scheme / light-dark CSS-property generators bound to the
21
+ * context's `colorSchemes` payload (with the neutral grey-ramp fallback). */
22
+ export declare function deriveColorSchemeGenerators(ctx: ChromeContext): {
23
+ generateCssCustomProperties: () => string;
24
+ generateLightDarkCssProperties: () => string;
25
+ };
26
+ /** Derive the four nav data-prep builders (root menu items, locale-switcher
27
+ * links, sidebar nodes, theme default mode) bound to the context. The sidebar
28
+ * builder reads `ctx.hostBindings.sidebarsConfig` (default `{}`). */
29
+ export declare function deriveNavDataPrep(ctx: ChromeContext): {
30
+ buildRootMenuItems: (lang: string, currentVersion: string | undefined) => import("../nav-data-prep/index.js").RootMenuItem[];
31
+ buildLocaleLinksForNav: (currentPath: string, lang: string, localeCount: number) => import("../url-helpers/index.js").LocaleLink[] | undefined;
32
+ buildSidebarNodes: (lang: string, navSection: string | undefined, currentVersion: string | undefined, emptyWhenUnsectioned?: boolean) => import("../sidebar-utils/index.js").SidebarNavNode[];
33
+ getThemeDefaultMode: () => "light" | "dark" | undefined;
34
+ };
35
+ /** Derive the header SearchWidget: `ctx.hostBindings.SearchWidget` when the host
36
+ * supplies one, else the package `SearchWidget` bound to the site base. */
37
+ export declare function deriveSearchWidgetSlot(ctx: ChromeContext): (props: {
38
+ placeholderText: string;
39
+ shortcutHint: string;
40
+ resultCountTemplate: string;
41
+ searchLabel: string;
42
+ searchUnavailableText: string;
43
+ loadingIndexText: string;
44
+ noResultsText: string;
45
+ }) => JSX.Element;
46
+ /** Derive the body-end islands: `ctx.hostBindings.BodyEndIslands` when supplied,
47
+ * else the package-island subset reconstructed from `settings`. */
48
+ export declare function deriveBodyEndIslands(ctx: ChromeContext): ReturnType<typeof createBodyEndIslands>;
49
+ /** Derive the DocHistory island: `ctx.hostBindings.DocHistory` when supplied,
50
+ * else the package no-op stub. */
51
+ export declare function deriveDocHistorySlot(ctx: ChromeContext): typeof DocHistoryStub;
52
+ /** Derive the inline version-switcher builder bound to the context. */
53
+ export declare function deriveInlineVersionSwitcher(ctx: ChromeContext): (slug: string, locale: string, currentVersion?: string) => JSX.Element | undefined;
54
+ /**
55
+ * Derive the locale-aware `createMdxComponents` factory plus the
56
+ * `SiteTreeNavWrapper` (also exposed by `createChrome`). The nav wrappers
57
+ * (CategoryNav / CategoryTreeNav / SiteTreeNav) PREFER `ctx.components` — their
58
+ * documented allowlist — so a host can pass its exact wrapper functions and the
59
+ * MDX render stays byte-identical; they fall back to rebuilding from the context
60
+ * for the injected package path. The content overrides (Details / HtmlPreview /
61
+ * Island / PresetGenerator + any host extras) come from `ctx.hostBindings.mdxExtras`
62
+ * merged over the package defaults.
63
+ */
64
+ export declare function deriveMdxComponents(ctx: ChromeContext): {
65
+ createMdxComponentsBound: (lang?: string) => Record<string, unknown>;
66
+ SiteTreeNavWrapper: FactoryComponent;
67
+ };
68
+ export type { Settings };
@@ -0,0 +1,231 @@
1
+ import { Fragment, jsx } from "preact/jsx-runtime";
2
+ import { createComposeMetaTitle } from "../compose-meta-title/index.js";
3
+ import {
4
+ generateCssCustomProperties,
5
+ generateLightDarkCssProperties
6
+ } from "../color-scheme-utils.js";
7
+ import { createBodyEndIslands } from "../doc-body-end-islands/index.js";
8
+ import { SearchWidget } from "../search-widget/index.js";
9
+ import { createMdxComponents } from "../mdx-components/index.js";
10
+ import { createCategoryNavWrapper } from "../category-nav/index.js";
11
+ import { createCategoryTreeNavWrapper } from "../category-tree-nav/index.js";
12
+ import { createSiteTreeNavWrapper } from "../site-tree-nav/index.js";
13
+ import { Details } from "../details/index.js";
14
+ import {
15
+ HtmlPreviewWrapper
16
+ } from "../html-preview-wrapper/index.js";
17
+ import { createInlineVersionSwitcher } from "../inline-version-switcher/index.js";
18
+ import {
19
+ buildRootMenuItems as buildRootMenuItemsBase,
20
+ buildLocaleLinksForNav as buildLocaleLinksForNavBase,
21
+ remapVersionedHrefs,
22
+ getThemeDefaultMode as getThemeDefaultModeBase
23
+ } from "../nav-data-prep/index.js";
24
+ import { buildSidebarForSection } from "../sidebar-utils/index.js";
25
+ const GREY_RAMP = [
26
+ "#000000",
27
+ "#1a1a1a",
28
+ "#333333",
29
+ "#4d4d4d",
30
+ "#666666",
31
+ "#808080",
32
+ "#999999",
33
+ "#b3b3b3",
34
+ "#cccccc",
35
+ "#d9d9d9",
36
+ "#e6e6e6",
37
+ "#f2f2f2",
38
+ "#ff5555",
39
+ "#50fa7b",
40
+ "#f1fa8c",
41
+ "#8be9fd"
42
+ ];
43
+ const DEFAULT_SCHEME = {
44
+ background: "#000000",
45
+ foreground: "#ffffff",
46
+ cursor: "#ffffff",
47
+ selectionBg: "#444444",
48
+ selectionFg: "#ffffff",
49
+ palette: GREY_RAMP
50
+ };
51
+ function DocHistoryStub(_props) {
52
+ return /* @__PURE__ */ jsx(Fragment, {});
53
+ }
54
+ function IslandPassthrough(props) {
55
+ return props.children ?? null;
56
+ }
57
+ function deriveComposeMetaTitle(ctx) {
58
+ return createComposeMetaTitle(ctx.settings.siteName);
59
+ }
60
+ function resolveHostScheme(ctx, key) {
61
+ if (!ctx.colorSchemes) return DEFAULT_SCHEME;
62
+ return ctx.colorSchemes[key] ?? DEFAULT_SCHEME;
63
+ }
64
+ function deriveColorSchemeGenerators(ctx) {
65
+ return {
66
+ // Called only in single-scheme mode (colorMode false).
67
+ generateCssCustomProperties: () => generateCssCustomProperties(resolveHostScheme(ctx, ctx.settings.colorScheme)),
68
+ // Called only in light/dark mode (colorMode truthy).
69
+ generateLightDarkCssProperties: () => {
70
+ const cm = ctx.settings.colorMode;
71
+ if (cm) {
72
+ return generateLightDarkCssProperties(
73
+ resolveHostScheme(ctx, cm.lightScheme),
74
+ resolveHostScheme(ctx, cm.darkScheme)
75
+ );
76
+ }
77
+ return generateLightDarkCssProperties(DEFAULT_SCHEME, DEFAULT_SCHEME);
78
+ }
79
+ };
80
+ }
81
+ function deriveNavDataPrep(ctx) {
82
+ function buildRootMenuItems(lang, currentVersion) {
83
+ return buildRootMenuItemsBase(
84
+ lang,
85
+ currentVersion,
86
+ ctx.settings.headerNav,
87
+ (key, l) => ctx.t(key, l),
88
+ (path, l, v) => ctx.navHref(path, l, v)
89
+ );
90
+ }
91
+ function buildLocaleLinksForNav(currentPath, lang, localeCount) {
92
+ return buildLocaleLinksForNavBase(
93
+ currentPath,
94
+ lang,
95
+ localeCount,
96
+ (path, l) => ctx.buildLocaleLinks(path, l)
97
+ );
98
+ }
99
+ const sidebarsConfig = ctx.hostBindings.sidebarsConfig ?? {};
100
+ function buildSidebarNodes(lang, navSection, currentVersion, emptyWhenUnsectioned = true) {
101
+ if (navSection === void 0 && emptyWhenUnsectioned) return [];
102
+ const { navDocs, categoryMeta } = ctx.loadNavSourceDocs(lang, currentVersion);
103
+ const explicitPrefixes = ctx.getCategoryOrder().filter((cm) => cm !== "!");
104
+ const rawNodes = buildSidebarForSection(
105
+ navDocs,
106
+ lang,
107
+ navSection,
108
+ categoryMeta,
109
+ sidebarsConfig,
110
+ (d, l, meta) => ctx.buildNavTree(
111
+ d,
112
+ l,
113
+ meta,
114
+ (slug, loc) => ctx.docsUrl(slug, loc)
115
+ ),
116
+ explicitPrefixes
117
+ );
118
+ return currentVersion ? remapVersionedHrefs(
119
+ rawNodes,
120
+ currentVersion,
121
+ lang,
122
+ (slug, v, l) => ctx.versionedDocsUrl(slug, v, l)
123
+ ) : rawNodes;
124
+ }
125
+ function getThemeDefaultMode() {
126
+ return getThemeDefaultModeBase(ctx.settings.colorMode);
127
+ }
128
+ return { buildRootMenuItems, buildLocaleLinksForNav, buildSidebarNodes, getThemeDefaultMode };
129
+ }
130
+ function deriveSearchWidgetSlot(ctx) {
131
+ function SearchWidgetBound(props) {
132
+ return SearchWidget({ ...props, base: ctx.withBase("/") });
133
+ }
134
+ return ctx.hostBindings.SearchWidget ?? SearchWidgetBound;
135
+ }
136
+ function deriveBodyEndIslands(ctx) {
137
+ return ctx.hostBindings.BodyEndIslands ?? createBodyEndIslands({ settings: ctx.settings });
138
+ }
139
+ function deriveDocHistorySlot(ctx) {
140
+ return ctx.hostBindings.DocHistory ?? DocHistoryStub;
141
+ }
142
+ function deriveInlineVersionSwitcher(ctx) {
143
+ return createInlineVersionSwitcher({
144
+ settings: ctx.settings,
145
+ defaultLocale: ctx.defaultLocale,
146
+ t: ctx.t,
147
+ docsUrl: ctx.docsUrl,
148
+ versionedDocsUrl: ctx.versionedDocsUrl,
149
+ withBase: ctx.withBase
150
+ });
151
+ }
152
+ function deriveMdxComponents(ctx) {
153
+ const CategoryNavWrapper = ctx.components.CategoryNav ?? createCategoryNavWrapper({
154
+ defaultLocale: ctx.defaultLocale,
155
+ resolveNavSource: ctx.resolveNavSource,
156
+ buildNavTree: ((docs, locale, categoryMeta) => ctx.buildNavTree(
157
+ docs,
158
+ locale,
159
+ categoryMeta,
160
+ (slug, loc) => ctx.docsUrl(slug, loc)
161
+ )),
162
+ findNode: ctx.findNode,
163
+ firstRoutedHref: ctx.firstRoutedHref
164
+ });
165
+ const CategoryTreeNavWrapper = ctx.components.CategoryTreeNav ?? createCategoryTreeNavWrapper({
166
+ defaultLocale: ctx.defaultLocale,
167
+ resolveNavSource: ctx.resolveNavSource,
168
+ buildNavTree: ((docs, locale, categoryMeta) => ctx.buildNavTree(
169
+ docs,
170
+ locale,
171
+ categoryMeta,
172
+ (slug, loc) => ctx.docsUrl(slug, loc)
173
+ )),
174
+ groupSatelliteNodes: ctx.groupSatelliteNodes,
175
+ findNode: ctx.findNode
176
+ });
177
+ const SiteTreeNavWrapper = ctx.components.SiteTreeNav ?? createSiteTreeNavWrapper({
178
+ defaultLocale: ctx.defaultLocale,
179
+ resolveNavSource: ctx.resolveNavSource,
180
+ buildNavTree: ((docs, locale, categoryMeta) => ctx.buildNavTree(
181
+ docs,
182
+ locale,
183
+ categoryMeta,
184
+ (slug, loc) => ctx.docsUrl(slug, loc)
185
+ )),
186
+ groupSatelliteNodes: ctx.groupSatelliteNodes,
187
+ getCategoryOrder: ctx.getCategoryOrder
188
+ });
189
+ function HtmlPreviewBound(props) {
190
+ return HtmlPreviewWrapper({
191
+ globalConfig: ctx.settings.htmlPreview ?? null,
192
+ ...props
193
+ });
194
+ }
195
+ const mdxExtrasDefault = {
196
+ Details,
197
+ HtmlPreview: HtmlPreviewBound,
198
+ Island: IslandPassthrough,
199
+ // PresetGenerator stays a package stub (render nothing): it is the
200
+ // showcase's project-bound interactive island; downstream projects stub it.
201
+ PresetGenerator: (_props) => null
202
+ };
203
+ const mdxExtras = { ...mdxExtrasDefault, ...ctx.hostBindings.mdxExtras ?? {} };
204
+ function createMdxComponentsBound(lang = ctx.defaultLocale) {
205
+ return createMdxComponents({
206
+ settings: ctx.settings,
207
+ locale: lang,
208
+ navData: {
209
+ CategoryNav: CategoryNavWrapper,
210
+ CategoryTreeNav: CategoryTreeNavWrapper,
211
+ SiteTreeNav: SiteTreeNavWrapper
212
+ },
213
+ // Package-owned content components wired here so an INJECTED docs route
214
+ // renders MDX using these tags without the "MDX requires '<X>' to be
215
+ // passed via the 'components' prop" error.
216
+ extras: mdxExtras
217
+ });
218
+ }
219
+ return { createMdxComponentsBound, SiteTreeNavWrapper };
220
+ }
221
+ export {
222
+ DEFAULT_SCHEME,
223
+ deriveBodyEndIslands,
224
+ deriveColorSchemeGenerators,
225
+ deriveComposeMetaTitle,
226
+ deriveDocHistorySlot,
227
+ deriveInlineVersionSwitcher,
228
+ deriveMdxComponents,
229
+ deriveNavDataPrep,
230
+ deriveSearchWidgetSlot
231
+ };
@@ -0,0 +1,37 @@
1
+ /** @jsxRuntime automatic */
2
+ /** @jsxImportSource preact */
3
+ import type { RouteContext, ChromeHostBindings } from "../factory-context/index.js";
4
+ import type { Settings } from "../settings.js";
5
+ /**
6
+ * Build the wired page-chrome factories from a reconstructed route context plus
7
+ * the host-bound slots. Assembles a {@link ChromeContext} (route context +
8
+ * empty `components` allowlist + the passed `hostBindings`) and threads it
9
+ * through each top-level factory; every omitted `hostBindings` slot falls back
10
+ * to the package stub default inside the factories, reproducing the injected
11
+ * package-routes path byte-for-byte.
12
+ */
13
+ export declare function createChrome<S extends Settings = Settings>(context: RouteContext<S>, hostBindings?: ChromeHostBindings): {
14
+ composeMetaTitle: (title: string) => string;
15
+ HeadWithDefaults: (props: import("../head-with-defaults/index.js").HeadWithDefaultsProps) => import("preact").JSX.Element;
16
+ HeaderWithDefaults: (props: import("../header-with-defaults/index.js").HeaderWithDefaultsProps) => import("preact").JSX.Element;
17
+ FooterWithDefaults: (props: {
18
+ lang?: string;
19
+ }) => import("preact").VNode;
20
+ SidebarWithDefaults: (props: import("../sidebar-with-defaults/index.js").SidebarWithDefaultsProps) => import("preact").JSX.Element;
21
+ renderDocPage: (props: import("../doc-page-props/index.js").DocPageBaseProps, opts: import("../doc-page-renderer/index.js").RenderDocPageOptions) => import("preact").JSX.Element;
22
+ VersionsPageView: (props: import("../versions-page/index.js").VersionsPageViewProps) => import("preact").JSX.Element;
23
+ collectTagMapForLocale: (locale: string) => Map<string, import("../tag-pages/index.js").TagInfo>;
24
+ TagDetailPageView: (props: {
25
+ locale: string;
26
+ tag: string;
27
+ tagInfo: import("../tag-pages/index.js").TagInfo;
28
+ }) => import("preact").JSX.Element;
29
+ TagsIndexPageView: (props: {
30
+ locale: string;
31
+ children?: import("preact").ComponentChildren;
32
+ }) => import("preact").JSX.Element;
33
+ SiteTreeNavWrapper: import("../factory-context/index.js").FactoryComponent;
34
+ BodyEndIslands: (props: import("../doc-body-end-islands/index.js").BodyEndIslandsProps) => import("preact").JSX.Element;
35
+ };
36
+ /** The wired page-chrome surface returned by {@link createChrome}. */
37
+ export type Chrome = ReturnType<typeof createChrome>;
@@ -0,0 +1,43 @@
1
+ import { createHeadWithDefaults } from "../head-with-defaults/index.js";
2
+ import { createHeaderWithDefaults } from "../header-with-defaults/index.js";
3
+ import { createFooterWithDefaults } from "../footer-with-defaults/index.js";
4
+ import { createSidebarWithDefaults } from "../sidebar-with-defaults/index.js";
5
+ import { createRenderDocPage } from "../doc-page-renderer/index.js";
6
+ import { createVersionsPageView } from "../versions-page/index.js";
7
+ import { createTagPages } from "../tag-pages/index.js";
8
+ import {
9
+ deriveComposeMetaTitle,
10
+ deriveBodyEndIslands,
11
+ deriveMdxComponents
12
+ } from "./derive.js";
13
+ function createChrome(context, hostBindings = {}) {
14
+ const components = {};
15
+ const ctx = { ...context, components, hostBindings };
16
+ const composeMetaTitle = deriveComposeMetaTitle(ctx);
17
+ const HeadWithDefaults = createHeadWithDefaults(ctx);
18
+ const HeaderWithDefaults = createHeaderWithDefaults(ctx);
19
+ const FooterWithDefaults = createFooterWithDefaults(ctx);
20
+ const SidebarWithDefaults = createSidebarWithDefaults(ctx);
21
+ const renderDocPage = createRenderDocPage(ctx);
22
+ const VersionsPageView = createVersionsPageView(ctx);
23
+ const { collectTagMapForLocale, TagDetailPageView, TagsIndexPageView } = createTagPages(ctx);
24
+ const { SiteTreeNavWrapper } = deriveMdxComponents(ctx);
25
+ const BodyEndIslands = deriveBodyEndIslands(ctx);
26
+ return {
27
+ composeMetaTitle,
28
+ HeadWithDefaults,
29
+ HeaderWithDefaults,
30
+ FooterWithDefaults,
31
+ SidebarWithDefaults,
32
+ renderDocPage,
33
+ VersionsPageView,
34
+ collectTagMapForLocale,
35
+ TagDetailPageView,
36
+ TagsIndexPageView,
37
+ SiteTreeNavWrapper,
38
+ BodyEndIslands
39
+ };
40
+ }
41
+ export {
42
+ createChrome
43
+ };
package/dist/content.css CHANGED
@@ -271,19 +271,22 @@
271
271
  * rules must live here explicitly. Closes zudolab/zudo-doc#1357 / #1444 / #1456.
272
272
  *
273
273
  * Spacing: the box carries NO margin — the separating gap to the next block is
274
- * supplied by that block's flow-space `margin-top` (the owl rule above). The
275
- * snug bottom padding keeps the body tight inside the box without inflating
276
- * that gap. (A missing bottom margin on the box is intentional in the flow
277
- * model do NOT add one; see zudolab/zudo-doc#2188 for why the per-element
278
- * margin model that needed it was retired.)
274
+ * supplied by that block's flow-space `margin-top` (the page owl above). Blocks
275
+ * INSIDE `.admonition-body` get their own shorter flow gap (vsp-sm) via the
276
+ * nested owl below, since the page owl only reaches direct children of
277
+ * `.zd-content`; the box's vsp-sm bottom padding then gives the last block room
278
+ * so it isn't cramped against the edge. (A missing bottom margin on the box is
279
+ * intentional in the flow model — do NOT add one; see zudolab/zudo-doc#2188 for
280
+ * why the per-element margin model that needed it was retired.)
279
281
  * ======================================== */
280
282
 
281
283
  [data-admonition] {
282
284
  border-left: 4px solid var(--color-muted);
283
- /* Asymmetric padding mirrors the Astro reference (`pt-vsp-md pb-vsp-2xs`)
284
- * generous space at the top, snug at the bottom — so the title row sits
285
- * under generous breathing room without inflating the bottom gap. */
286
- padding: var(--spacing-vsp-md) var(--spacing-hsp-lg) var(--spacing-vsp-2xs);
285
+ /* Generous top padding gives the title row breathing room; the bottom uses
286
+ * the same vsp-sm as the body inner flow (below) so the last block isn't
287
+ * cramped against the box edge. (Was pb-vsp-2xs too tight once the body
288
+ * has multiple flowed blocks; zudolab/zudo-doc#2417.) */
289
+ padding: var(--spacing-vsp-md) var(--spacing-hsp-lg) var(--spacing-vsp-sm);
287
290
  background-color: color-mix(in srgb, var(--color-muted) 12%, var(--color-bg));
288
291
  border-radius: 0 var(--radius-DEFAULT) var(--radius-DEFAULT) 0;
289
292
  }
@@ -301,6 +304,19 @@
301
304
  margin-right: var(--spacing-hsp-2xs);
302
305
  }
303
306
 
307
+ /* Flow spacing between blocks INSIDE an admonition body. The page-level
308
+ * `.zd-content > * + *` owl only reaches direct children of `.zd-content`, so
309
+ * nested blocks here had no inter-element gap (zudolab/zudo-doc#2417). The
310
+ * admonition is a narrower scope than the page flow, so a shorter gap
311
+ * (vsp-sm < the page's vsp-md) reads as natural. Layered in `zd-flow` for the
312
+ * same reason as the page owl: it must beat preflight's `* { margin: 0 }` but
313
+ * lose to an unlayered `mt-*` utility on a nested block. */
314
+ @layer zd-flow {
315
+ .admonition-body > :where(* + *) {
316
+ margin-top: var(--spacing-vsp-sm);
317
+ }
318
+ }
319
+
304
320
  .admonition-body > :first-child {
305
321
  margin-top: 0;
306
322
  }
@@ -1,21 +1,19 @@
1
1
  /** @jsxRuntime automatic */
2
2
  /** @jsxImportSource preact */
3
3
  import type { JSX } from "preact";
4
+ import type { ChromeContext } from "../factory-context/index.js";
5
+ import type { Settings } from "../settings.js";
4
6
  /** Settings subset read by the DocBodyEnd factory. */
5
7
  export interface DocBodyEndSettings {
6
8
  base?: string | null;
7
9
  sidebarResizer: boolean;
8
10
  }
9
- /** Dependencies injected by the host stub. */
10
- export interface DocBodyEndDeps {
11
- settings: DocBodyEndSettings;
12
- /** The `BodyEndIslands` component (host-side — reads additional settings). */
13
- BodyEndIslands: (props: {
14
- basePath: string;
15
- }) => JSX.Element;
16
- }
17
11
  /**
18
- * Create a `DocBodyEnd` component bound to the host's settings and
19
- * BodyEndIslands component.
12
+ * Create a `DocBodyEnd` component from the unified {@link ChromeContext}
13
+ * (epic Collapse Wiring Shells #2420, FACTORIES #2424 — breaking signature).
14
+ *
15
+ * Reads `settings` directly; the `BodyEndIslands` block is a HOST-bound slot
16
+ * (`ctx.hostBindings.BodyEndIslands`) that defaults to the package-island subset
17
+ * reconstructed from `settings` (no host-only client-router / token-panel boots).
20
18
  */
21
- export declare function createDocBodyEnd(deps: DocBodyEndDeps): () => JSX.Element;
19
+ export declare function createDocBodyEnd<S extends Settings = Settings>(ctx: ChromeContext<S>): () => JSX.Element;
@@ -1,7 +1,9 @@
1
1
  import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
2
2
  import { SidebarResizerInit } from "../sidebar-resizer/index.js";
3
- function createDocBodyEnd(deps) {
4
- const { settings, BodyEndIslands } = deps;
3
+ import { deriveBodyEndIslands } from "../chrome/derive.js";
4
+ function createDocBodyEnd(ctx) {
5
+ const settings = ctx.settings;
6
+ const BodyEndIslands = deriveBodyEndIslands(ctx);
5
7
  function DocBodyEnd() {
6
8
  return /* @__PURE__ */ jsxs(Fragment, { children: [
7
9
  /* @__PURE__ */ jsx(BodyEndIslands, { basePath: settings.base ?? "/" }),
@@ -3,6 +3,8 @@
3
3
  import type { JSX } from "preact";
4
4
  import { type FrontmatterCellRenderer } from "../metainfo/index.js";
5
5
  import type { DocPageEntry } from "../doc-page-props/index.js";
6
+ import type { ChromeContext } from "../factory-context/index.js";
7
+ import type { Settings } from "../settings.js";
6
8
  export type { FrontmatterCellRenderer };
7
9
  /** Dependencies injected by the host stub. */
8
10
  export interface DocContentHeaderDeps {
@@ -64,6 +66,12 @@ interface DocContentHeaderProps {
64
66
  version?: string;
65
67
  }
66
68
  /**
67
- * Create a `DocContentHeader` component bound to the host's injected context.
69
+ * Create a `DocContentHeader` component from the unified {@link ChromeContext}
70
+ * (epic Collapse Wiring Shells #2420, FACTORIES #2424 — breaking signature).
71
+ *
72
+ * Reads `t` directly; the frontmatter-preview entry builder + per-key renderers
73
+ * are HOST-bound slots (`ctx.hostBindings.buildFrontmatterPreviewEntries` /
74
+ * `…frontmatterRenderers`, defaulting to `() => []` / `{}`). The nested
75
+ * `DocMetainfoArea` / `DocTagsArea` are rebuilt from the same context.
68
76
  */
69
- export declare function createDocContentHeader(deps: DocContentHeaderDeps): (props: DocContentHeaderProps) => JSX.Element;
77
+ export declare function createDocContentHeader<S extends Settings = Settings>(ctx: ChromeContext<S>): (props: DocContentHeaderProps) => JSX.Element;
@@ -1,13 +1,13 @@
1
1
  import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
2
2
  import { FrontmatterPreview } from "../metainfo/index.js";
3
- function createDocContentHeader(deps) {
4
- const {
5
- t,
6
- buildFrontmatterPreviewEntries,
7
- frontmatterRenderers,
8
- DocMetainfoArea,
9
- DocTagsArea
10
- } = deps;
3
+ import { createDocMetainfoArea } from "../doc-metainfo-area/index.js";
4
+ import { createDocTagsArea } from "../doc-tags-area/index.js";
5
+ function createDocContentHeader(ctx) {
6
+ const t = ctx.t;
7
+ const buildFrontmatterPreviewEntries = ctx.hostBindings.buildFrontmatterPreviewEntries ?? (() => []);
8
+ const frontmatterRenderers = ctx.hostBindings.frontmatterRenderers ?? {};
9
+ const DocMetainfoArea = createDocMetainfoArea(ctx);
10
+ const DocTagsArea = createDocTagsArea(ctx);
11
11
  function DocContentHeader({
12
12
  entry,
13
13
  slug,
@@ -1,6 +1,8 @@
1
1
  /** @jsxRuntime automatic */
2
2
  /** @jsxImportSource preact */
3
3
  import type { VNode } from "preact";
4
+ import type { ChromeContext } from "../factory-context/index.js";
5
+ import type { Settings } from "../settings.js";
4
6
  /** Per-entry metadata shape from the doc-history manifest. */
5
7
  export interface DocHistoryMetaEntry {
6
8
  author: string;
@@ -26,28 +28,6 @@ export type DocHistoryComponent = (props: {
26
28
  locale?: string;
27
29
  basePath?: string;
28
30
  }) => VNode;
29
- /** Dependencies injected by the host stub. */
30
- export interface DocHistoryAreaDeps {
31
- settings: DocHistoryAreaSettings;
32
- /** Default locale code (e.g. "en"). */
33
- defaultLocale: string;
34
- /** The parsed `#doc-history-meta` JSON manifest (host-side import). */
35
- docHistoryMeta: Record<string, DocHistoryMetaEntry>;
36
- /** Translate a UI string key for a locale. */
37
- t: (key: string, locale: string) => string;
38
- /** Convert a canonical route slug to a history slug (sentinel: "" → "index"). */
39
- toHistorySlug: (slug: string) => string;
40
- /**
41
- * Build a view-source GitHub URL, or null when githubUrl is not set.
42
- * Host passes a partially applied version binding `settings.githubUrl`.
43
- */
44
- buildGitHubSourceUrl: (contentDir: string, entryId: string) => string | null;
45
- /**
46
- * The DocHistory island component (host-side — must be imported in the stub so
47
- * zfb's scanner walks page → stub → DocHistory).
48
- */
49
- DocHistory: DocHistoryComponent;
50
- }
51
31
  export interface DocHistoryAreaProps {
52
32
  /** Page slug, e.g. "getting-started/intro". */
53
33
  slug: string;
@@ -78,7 +58,12 @@ export interface DocHistoryAreaProps {
78
58
  isFallback?: boolean;
79
59
  }
80
60
  /**
81
- * Create a `DocHistoryArea` component bound to the host's settings and
82
- * injected dependencies.
61
+ * Create a `DocHistoryArea` component from the unified {@link ChromeContext}
62
+ * (epic Collapse Wiring Shells #2420, FACTORIES #2424 — breaking signature).
63
+ *
64
+ * Reads `settings`/`defaultLocale`/`t` directly, `toHistorySlug` from the slug
65
+ * helper, and binds `buildGitHubSourceUrl` to `settings.githubUrl`. The
66
+ * DocHistory island is a HOST-bound slot (`ctx.hostBindings.DocHistory`,
67
+ * default: a no-op stub rendering an empty fragment).
83
68
  */
84
- export declare function createDocHistoryArea(deps: DocHistoryAreaDeps): (props: DocHistoryAreaProps) => VNode | null;
69
+ export declare function createDocHistoryArea<S extends Settings = Settings>(ctx: ChromeContext<S>): (props: DocHistoryAreaProps) => VNode | null;
@@ -1,16 +1,20 @@
1
1
  import { jsx, jsxs } from "preact/jsx-runtime";
2
2
  import { Island } from "@takazudo/zfb";
3
3
  import { BodyFootUtilArea } from "../body-foot-util/index.js";
4
- function createDocHistoryArea(deps) {
5
- const {
6
- settings,
7
- defaultLocale,
8
- docHistoryMeta,
9
- t,
10
- toHistorySlug,
11
- buildGitHubSourceUrl,
12
- DocHistory
13
- } = deps;
4
+ import { toHistorySlug } from "../slug/index.js";
5
+ import { buildGitHubSourceUrl as buildGitHubSourceUrlBase } from "../github-helpers/index.js";
6
+ import { deriveDocHistorySlot } from "../chrome/derive.js";
7
+ function createDocHistoryArea(ctx) {
8
+ const settings = ctx.settings;
9
+ const defaultLocale = ctx.defaultLocale;
10
+ const docHistoryMeta = ctx.hostBindings.docHistoryMeta ?? {};
11
+ const t = ctx.t;
12
+ const buildGitHubSourceUrl = (contentDir, entryId) => buildGitHubSourceUrlBase(
13
+ ctx.settings.githubUrl,
14
+ contentDir,
15
+ entryId
16
+ );
17
+ const DocHistory = deriveDocHistorySlot(ctx);
14
18
  DocHistory.displayName = "DocHistory";
15
19
  function DocHistoryArea({
16
20
  slug,
@@ -1,6 +1,8 @@
1
1
  /** @jsxRuntime automatic */
2
2
  /** @jsxImportSource preact */
3
3
  import type { VNode } from "preact";
4
+ import type { ChromeContext } from "../factory-context/index.js";
5
+ import type { Settings } from "../settings.js";
4
6
  /** Per-entry metadata shape from the doc-history manifest. */
5
7
  export interface DocHistoryMetaEntry {
6
8
  author: string;
@@ -11,18 +13,6 @@ export interface DocHistoryMetaEntry {
11
13
  export interface DocMetainfoAreaSettings {
12
14
  docMetainfo: boolean;
13
15
  }
14
- /** Dependencies injected by the host stub. */
15
- export interface DocMetainfoAreaDeps {
16
- settings: DocMetainfoAreaSettings;
17
- /** Default locale code (e.g. "en"). */
18
- defaultLocale: string;
19
- /** The parsed `#doc-history-meta` JSON manifest (host-side import). */
20
- docHistoryMeta: Record<string, DocHistoryMetaEntry>;
21
- /** Translate a UI string key for a locale. */
22
- t: (key: string, locale: string) => string;
23
- /** Convert a canonical route slug to a history slug (sentinel: "" → "index"). */
24
- toHistorySlug: (slug: string) => string;
25
- }
26
16
  export interface DocMetainfoAreaProps {
27
17
  /** Page slug, e.g. "getting-started/intro". */
28
18
  slug: string;
@@ -40,7 +30,11 @@ export interface DocMetainfoAreaProps {
40
30
  isFallback?: boolean;
41
31
  }
42
32
  /**
43
- * Create a `DocMetainfoArea` component bound to the host's settings and
44
- * injected dependencies.
33
+ * Create a `DocMetainfoArea` component from the unified {@link ChromeContext}
34
+ * (epic Collapse Wiring Shells #2420, FACTORIES #2424 — breaking signature).
35
+ *
36
+ * Reads `settings`/`defaultLocale`/`t` directly and `toHistorySlug` from the
37
+ * package slug helper; the per-page git-history manifest is a HOST-bound slot
38
+ * (`ctx.hostBindings.docHistoryMeta`, default `{}` → no Created/Updated block).
45
39
  */
46
- export declare function createDocMetainfoArea(deps: DocMetainfoAreaDeps): (props: DocMetainfoAreaProps) => VNode | null;
40
+ export declare function createDocMetainfoArea<S extends Settings = Settings>(ctx: ChromeContext<S>): (props: DocMetainfoAreaProps) => VNode | null;