Adopting the engine
How another zfb/zudo-doc host installs @takazudo/zudo-sg.
@takazudo/zudo-sg is built to be installed into a project the same way @takazudo/zudo-doc is: as a package, not as files copied by hand. This page walks through the shape of that install.
Note
This repository's own ADOPTING.md predates the engine package and describes the older, manual-copy path (copying source files like src/ directly). Once a project can depend on @takazudo/zudo-sg as a package, this page supersedes that checklist.
Quick start
For a new host, the initializer provides the shortest path to a working zudo-react + zfb styleguide:
pnpm create zudo-sg@latest my-styleguide
cd my-styleguide
pnpm install
pnpm gen-registry
pnpm gen-token-manifest
pnpm devIt creates the same package composition described below, with a small local ui/ story corpus and token CSS ready to replace.
For an existing host, keep using the manual dependency, configuration, and composition steps that follow.
Prerequisites
Module mode targets zudo-react + zfb hosts and requires a @takazudo/zudo-doc-based host with package-owned routes enabled (packageOwnedRoutes: true). The engine's route entrypoints call zudo-doc's chrome factories and use its virtual modules. Descriptor mode remains framework-independent; see the scope section below.
The host must keep @takazudo/zdtp, diff, and katex: zdtp powers the always-injected / route, while diff and katex are inherited zudo-doc route dependencies. The engine mounts zdtp's dashboard through an opaque, dynamically loaded Preact adapter inside a zudo-react island; Preact is an engine dependency, not a host peer. Keep tokens: { cssFiles,
manifestOut } in zudo-sg.config.mjs; the initializer types that block as required, and the generated token manifest feeds the dashboards.
Install steps
Add the package. Install
@takazudo/zudo-sgalongside the engine's required peers:@takazudo/,zfb ^4. 4. 0 @takazudo/, andzudo- doc ^6. 1. 1 @takazudo/. The host also needs the matching zfb runtime and Markdown WASM packages plus zudo-doc's inherited prerequisites (zdtp ^0. 8. 6 diff,katex, andzod); see the package README's copy-paste install line for the complete fixture-equivalent command.Write
zudo-sg.config.mjsat the project root — plain data, loaded by both the CLI and the build config. At minimum it needs:componentsRoots: one entry per directory of*.stories.tsxfiles to catalog ({ dir, importBase }).registryOut: where the generated registry file should be written.previewStyles: the project-root-relative CSS entry the preview iframes should compile (see Tokens and preview CSS).Optionally
categoryOrder,uiPackageName,tokens: { cssFiles, manifestOut }, androutesoverrides for the four URL patterns.
Note
componentsRootsalways points at a component package the adopting project brings itself — the engine installs no component set of its own. Thenew-componentCLI command scaffolds one component at a time into that package; it never installs or seeds a starter library. A components-only package with its stories co-located beside each component (the shape this repository's own@zudo-sg/demo-uiuses) is the recommended structure to pointcomponentsRootsat.Compose
zudoSg()/withZudoSg()intozfb.config.ts.withZudoSg(presetFragment, zudoSgConfig)appends the engine's plugin descriptors and content collections after a zudo-doc preset's own — the routes plugin fails fast at setup if it can't find the zudo-doc routes descriptor already in the list, so order matters:import { zudoDocPreset } from "@takazudo/zudo-doc/preset"; import { withZudoSg } from "@takazudo/zudo-sg/config"; import zudoSgConfig from "./zudo-sg.config.mjs"; const preset = zudoDocPreset({ settings /* … */ }); export default defineConfig({ // … ...withZudoSg(preset, zudoSgConfig), });Generate the registry. Run the engine's
gen-registryCLI command (wired here aspnpm gen:sg-registry) to discover every*.stories.tsxfile under the configuredcomponentsRootsand write the explicit-import registry fileregistryOutpoints at. Commit the generated file — it is read at both build and dev time, and a--checkmode (gen-registry --check) is meant to run in CI so a stale registry fails the build instead of silently drifting.Add the preview stylesheet. Point
previewStylesat a real CSS entry file that imports the component library's tokens and authored component styles. For example, place@layer zw-reset;before the imports listed inpackages/under “Required style imports”. The preview plugin invokes zfb'sdemo- ui/ STORIES. md csscommand with the host's wind config and component sources, then serves the scoped output at its own URL. This separate compile keeps host chrome out of the preview document.Configure the host's wind sources in
zfb.config.ts. Usewind.sources.rootsfor directories inside the project, orwind.sources.packageRootsfor installed packages outside the project root. This controls the host stylesheet scan; the preview compile scans its configured component roots independently.(Optional) Import the islands seed.
@takazudo/is a one-line side-effect import that pulls every engine island into the host's island graph. With the engine's zfb 4.4.0 peer floor, the scanner includes injected routes in dev and build, so this import is not required. It stays exported for API compatibility and is harmless if present. See The engine package.zudo- sg/ islands
Scope: zudo-react module mode, descriptor mode for other hosts
The default module-mode install path uses zfb's zudo-react runtime for its story components. A host using another framework, or no component framework, can adopt the catalog, category nav, search, and detail-page chrome by switching the story source to descriptor mode (registry: { mode: "descriptor" }) and serving its own preview document at externalPreview.url — the engine renders everything except the preview document itself, and the two sides talk over a small, versioned, framework-free postMessage contract. This is the seam that superseded the earlier idea of a host-supplied preview-renderer module invoked inside the engine's own preview route.
This page and the rest of this category stay at the concept level; the full descriptor-mode contract — the StoryDescriptor shape, externalPreview, the postMessage protocol, and a minimal framework-free preview-frame example — lives in the doc/ workspace reference (see Relationship to zudo-doc for why that split exists). Adopting the default, module-mode path today still means adopting the engine into a zudo-react/zfb host.
Recover highlighting in an existing consumer lock
An existing lock can retain two @lezer/common module identities even after installing compatible engine/editor versions. The visible symptom is missing TSX or editable CSS highlighting with a CodeMirror plugin error tags is not iterable (the variable name can differ in a minified bundle); successful editor construction or typing does not establish that highlighting works.
From the affected host, diagnose and deduplicate its own graph:
pnpm dedupe --check
pnpm dedupe --ignore-scripts
pnpm install --frozen-lockfileThe first command is diagnostic and exits nonzero when deduplication is needed. Review the resulting lock diff. Rebuild the host's generated output from scratch (the packed proof removes only dist and .zfb-build before pnpm exec zfb build), then verify TSX source tokens, editable CSS tokens, Live CSS application and clearing, preview isolation, and absence of plugin/browser errors. An ordinary or frozen install alone can preserve the split.
This recovery is scoped to pnpm 11.5.2; other package managers need their own resolution proof. It is consumer remediation, not a universal package-side fix. Published compatible Lezer ranges still admit older common modules; direct floors or a root-workspace dedupe check do not guarantee every retained consumer lock. The separate packed diagnostic pnpm verify:retained-lezer-lock in the engine repository preserves a clean control and treats the pre-recovery failure as expected evidence. Track the remaining compatibility requirement in zudo-sg#963.