A version written into a manifest is written into every manifest that wants it,
which is how eslint came to exist in a hundred and four forms across this estate
and how a package came to resolve twice at run time. The catalog moves the number
out of the manifest: a manifest names the package, the workspace names the
version.
Four are held below their latest because the newer release cannot be used, and
the reason is recorded rather than the version alone: @noble/hashes 2 renamed
every subpath the web3 stack imports, @splinetool/runtime 2 depends on a package
that was never published, @dynamic-labs 5.4.1 the same, and react-resizable-panels
3 points its types condition at the bundle.
Co-authored-by: Hanzo Dev <[email protected]>
Every dependency on a @hanzo/@luxfi/@zooai package now names the same version
across the repo: a catalog entry where pnpm supports one, an exact pin where it
does not, and a range only where a peer genuinely means one.
pnpm itself is pinned too — the estate ran eighteen different versions, five of
them too old to read a catalog.
Co-authored-by: Hanzo Dev <[email protected]>
Thirty-one manifests asked for @hanzo/font ^1.9.1 while the lockfile still
resolved 1.8.0, 1.8.2 and 1.8.12, so pnpm install --frozen-lockfile — which is
every workflow under .hanzo — refused to install anything at all.
The floor is 1.9.2 rather than 1.9.1 because 1.9.1's zen.css carries the
@font-face rules and nothing else: the :root block binding --font-zen-sans and
--font-zen-mono is absent, and a sheet reading either property gets an invalid
value and the OS default without saying so. 1.9.2 publishes them again.
Thirteen shipped apps and every scaffold in packages/create-app pulled their
face from Google at request time — Inter through next/font/google in the
examples and the four Next templates, Geist and Geist_Mono in the apps, and in
the seven react-router roots a bare <link> to fonts.googleapis.com. That last
one never even applied what it fetched: nothing in those trees set a family, so
the browser downloaded Inter, spent two preconnects on it, and rendered
system-ui. So `create-app` handed a customer a new docs site whose first act was
a third-party font request for a face that is not ours.
The apps read the family back through --font-sans and --font-mono, which are
next/font's handles rather than names of anything, so those follow the binding
over to --font-zen-sans and --font-zen-mono. Eight of them had only ever bound
the mono and were leaning on Tailwind's default for the text face; they now name
both, or swapping the binding would have dropped the body copy to system-ui
without a word.
Zen and ZenMono are built faces, not constructors — subsetting, display: swap
and fallback metrics are already in them — so the call and its options go away.
Twelve example layouts imported `ZenSans` from `@hanzo/font/sans`, a name
the package stopped exporting at 1.9.0 — the family is called Zen, so the
binding is `Zen`. Every one of those layouts would fail to resolve the
import against a current install, and the `^1.8.x` ranges they pinned kept
resolving to the old export instead of surfacing it.
Twelve example apps opened with next/font/google Inter, so every developer who
copied one started their project on a face that is not ours and a request to a
font host we do not control. They use @hanzo/font's ZenSans (and ZenMono where
the example wanted a second family), with the dependency declared so it
resolves. examples/proposals bound --font-sans to var(--font-inter), a variable
nothing sets any more; it reads --font-zen-sans.
Follow-up to 903b1c38: the package-name normalization covered JS/TS imports +
package.json but missed four other surfaces that name the same phantom packages,
relocating the breakage from 'pnpm install' to 'next/vite/astro build':
- H1: 46 CSS @import strings (@hanzo/docs-{base-ui,ui,openapi}/css/*) → real pkgs
(preserved legit @hanzo/docs-{twoslash,story} CSS)
- H2: examples/astro .astro imports (base-ui/mdx→fumadocs-ui/mdx, docs-core/toc→fumadocs-core/toc)
- L1: .changeset/config.json fixed-array lockstep names
- L2: removed apps/docs/next.config.mts dead phantom→phantom self-alias + dup keys
Found by adversarial red review of 903b1c38.
The fumadocs-fork rebrand left ~13 apps + examples referencing packages under
names that don't exist in the workspace (in BOTH directions: some kept
fumadocs-*, some became @hanzo/docs-*). pnpm install failed outright. Mapped
every reference (deps + imports, 607 source + 61 package.json) to the package
that actually exists, by suffix:
@hanzo/docs-{core,mdx,ui,base-ui} → fumadocs-{core,mdx,ui} / @fumadocs/base-ui
@fumadocs/{story,cli,mdx-remote,tailwind} → @hanzo/docs-{story,cli,...}
fumadocs-{docgen,epub,twoslash,typescript} → @hanzo/docs-{...}
drop phantom eslint-config-custom
Guarded @hanzo/docs-mdx vs -mdx-remote. Also fixes a pre-existing cli bug
(createConfigSchema referenced undefined isSrc) that blocked the package build.
Verified: pnpm install resolves clean (46.8s, was broken); package dist
regenerates phantom-free from the corrected source (hanzo-docs 430→0). Full
app build-green is blocked only by a node v26 toolchain bug in fumadocs-mdx's
config loader (repo targets node >=24.14; CI node builds it).
Renames the public doc-collection method across runtime, types, examples
and create-app templates so the API carries no upstream brand. The
internal helper and all call sites move together; fumadocs-core package
imports are untouched. Pairs with the .docs default outDir change.
BREAKING CHANGE: docs.toFumadocsSource() is now docs.toDocsSource().
The plugin default outDir was 'docs' while every template and example
tsconfig aliased collections/* -> ./.source/*, so a fresh fork could not
resolve `import { docs } from 'collections/server'` without manually
passing createMDX({ outDir }). Standardize on .docs everywhere:
- packages/mdx/src/core.ts: _Defaults.outDir 'docs' -> '.docs'
- 31 template/example tsconfig: collections/* -> ./.docs/*
- align generated-dir .gitignore entries to .docs
- changeset (patch)
* feat(openapi): anchor links on schema properties
Each property rendered inline in a request body, response body, or parameter
now gets a stable id and a hover-revealed link icon, enabling deep links to
specific fields from guides. Ids follow:
- body-<prop> for request bodies
- response-<status>-<prop> for responses
- parameters-<location>-<name> for parameters
A new idPrefix option on SchemaUI opts other Schema renderings into the same
behavior. See fuma-nama/fumadocs#3322.
* feat(openapi): deep-link nested schema properties
Extend the property anchor scheme to support hashes that point inside a
popover or below an array's "Show array" collapsible. Schema-internal
segments are joined with `.` so `#body.user.address.street` resolves
unambiguously regardless of dashes in property names.
On hash match, the inline anchor scrolls into view, the matching popover
auto-opens with its breadcrumb pre-navigated, the array collapsible
expands, and oneOf/anyOf tabs switch to the variant containing the
targeted field. The deep target is consumed after the first auto-open,
so subsequent user clicks on the same trigger get the base view; a
different deep hash re-arms it.
The hover anchor is now a copy-link button (matches fumadocs heading
anchors) that writes the full URL to the clipboard and updates the
address bar via history.replaceState (no hashchange, no popover reset).
* address PR review feedback
- Clarified the changeset: split anchor ID rules by context (request/response property paths vs flat parameter anchors) so dots aren't implied to apply to parameters.
- Namespaced anchors by media type when an operation has multiple request-body or response content types: prefix becomes `<media-slug>-body` / `response-<status>-<media-slug>`, and the content-type SelectTabs auto-switches to the targeted tab when a deep link arrives. Single-content-type operations keep the simple `body` / `response-<status>` form.
- Made findUnionPropMatch validate the *full* remaining path (via new pathResolves helper) so two variants that share the first prop name no longer race; the wrong tab can't be selected for a deep hash anymore.
- Reset SchemaUIPopover breadcrumb state when initialPath changes so a fresh deep hash takes effect on an already-open popover.
- Replaced the lossy slugifyPropertyName with encodeURIComponent (plus % encoding for `.`) so property anchors stay collision-safe across case-only differences, punctuation-only differences, and non-ASCII keys, while `.` remains exclusively a path separator.
* address PR review feedback
- Fix potential hydration mismatch in useTabValueFromHash by initializing state with the fallback value and resolving from the URL hash inside useEffect; the deep-linked tab now corrects after hydration on the client.
* fix(openapi): move useTabValueFromHash into a client component
The previous hydration-mismatch fix put `useEffect`/`useState` directly in
`operation/index.tsx`, which is a Server Component — Next refused to compile
because RSC modules can't import client-only React APIs. Extract the
hash-aware tab wrapper into `operation/hash-tabs.tsx` (with `"use client"`)
and switch both body and response sites to `HashAwareSelectTabs`. The parent
tree stays server-side; only the tab wrapper hydrates on the client.
* address PR review feedback
- Wrap decodeURIComponent in try/catch in both deep-link hash parsers so a malformed percent-encoded fragment (e.g. `#body%`) is treated as "no target" instead of throwing URIError and breaking tab / popover resolution.
* address PR review feedback
- Match the URL hash against DOM ids using the raw fragment first, with the fully-decoded form as a fallback, in both DeepLinkProvider and hash-tabs. Property anchors are generated via slugifyPropertyName (which percent-encodes reserved chars like `.`), so DOM ids contain the encoded form; decoding the hash before lookup caused links like `#body.foo%2Ebar` to miss the matching element.
* redesign
* fix types
---------
Co-authored-by: Fuma Nama <[email protected]>
* OpenAPI: render deprecated badge for deprecated operations
Show a yellow "Deprecated" badge for any OpenAPI operation marked
`deprecated: true`. The badge appears in two places: inline with the
operation heading when `showTitle` is true, and as a standalone header
above the playground card when `showTitle` is false (which is the
default for individual operation pages generated by `openapiSource`).
This restores the behaviour originally added in #2417, which was lost
during the v9→v10 move of the operation renderer from
`packages/openapi/src/render/operation/index.tsx` to
`packages/openapi/src/ui/operation/index.tsx`. The parameter-level,
schema-property-level and auth-scheme-level deprecated badges all
survived the refactor; the operation-level one was silently dropped.
The example spec's `deletePlanet` operation already claimed in its
description to be deprecated but never set the flag (inherited from the
upstream Scalar Galaxy fixture). Setting it both fixes the
inconsistency and provides a visible demo for the new badge.
* OpenAPI: improve operation deprecation styles
---------
Co-authored-by: Fuma Nama <[email protected]>
Upstream changes include:
- UI: improved TOC detection, clerk variant, slot support
- Base UI: redesigned home layout navigation menu
- CLI: slot support in customise command
- OpenAPI: unsetting auth fields in playground
- Multiple version package releases
All fumadocs-* imports renamed to @hanzo/docs-* namespace.