@takazudo/zudo-doc 6.1.1 → 7.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.
- package/CHANGELOG.md +33 -0
- package/README.md +17 -2
- package/dist/code-syntax/mermaid-init-script.d.ts +2 -2
- package/dist/code-syntax/mermaid-init-script.js +1 -1
- package/dist/compiled.css +37 -10
- package/dist/content.css +3 -0
- package/dist/doclayout/doc-layout.js +2 -0
- package/dist/features.css +31 -56
- package/dist/page-loading/page-loading-overlay.d.ts +2 -2
- package/dist/page-loading/page-loading-overlay.js +4 -2
- package/dist/page-loading.css +13 -2
- package/dist/sidebar-toggle-island/index.js +5 -0
- package/dist/sidebar-tree-island/index.js +106 -90
- package/dist/sidebar-tree-island/sidebar-scroll-preserve.d.ts +10 -8
- package/dist/sidebar-tree-island/sidebar-scroll-preserve.js +424 -24
- package/dist/sidebar-utils/index.d.ts +8 -0
- package/dist/sidebar-utils/index.js +51 -6
- package/dist/theme-toggle/index.js +76 -27
- package/dist/transitions/chrome-transition-bootstrap.d.ts +2 -0
- package/dist/transitions/chrome-transition-bootstrap.js +23 -0
- package/dist/transitions/page-events.d.ts +4 -0
- package/dist/transitions/page-events.js +4 -0
- package/dist/wind.json +2 -0
- package/eject/page-loading/page-loading-overlay.tsx +7 -5
- package/eject/sidebar-toggle-island/index.tsx +5 -0
- package/eject/sidebar-tree-island/index.tsx +16 -2
- package/eject/sidebar-tree-island/sidebar-scroll-preserve.ts +482 -35
- package/eject/theme-toggle/index.tsx +79 -35
- package/package.json +14 -11
- package/tsconfig.base.json +1 -2
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,39 @@ The format is based on Keep a Changelog, and release notes are generated from th
|
|
|
8
8
|
|
|
9
9
|
No unreleased changes.
|
|
10
10
|
|
|
11
|
+
## [7.0.0] - 2026-10-15
|
|
12
|
+
|
|
13
|
+
### Breaking Changes
|
|
14
|
+
|
|
15
|
+
- The supported Node.js runtime moves to the current production LTS line, Node 24 (`>=24 <25`). Node 22 is no longer supported. (98c94df73)
|
|
16
|
+
- Raises the optional KaTeX peer floor to `^0.18.2 || ^0.19.0` for the GHSA-238p-pmpm-9mq7 patch; 0.16.x is no longer supported. Updates the internal KaTeX color selector while preserving server-only rendering, error fallback and math-disabled builds without KaTeX. Math-enabled hosts must install a supported version and import its local CSS. (9ee3950c9)
|
|
17
|
+
- Requires the published zfb 4.5.2 family: the `@takazudo/zfb`, `zfb-runtime` and `zfb-md-wasm` peer floors move from `^4.5.0` to `^4.5.2`. This picks up live `public/assets/` development serving, explicit and compact one-line MDX table fixes, and a `zfb dev` ready banner that waits until routes are published. Upgrade the zfb family (and `zfb-adapter-cloudflare` when used) together. (e5b7de457, d5e0d7078)
|
|
18
|
+
- The `@takazudo/zudo-doc-history-server` peer floor moves from `^6.0.0` to `^7.0.0` with this major release.
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
- Keep the header stationary during dynamic page transitions, including when destination markup replaces it, and keep the desktop sidebar stationary within the same navigation category and locale/version/tree context. Fresh destination content and existing sidebar state handling are preserved; changing articles and table-of-contents content still animate. Sidebar entry and exit also cover `hide_sidebar` pages. Loading feedback now covers the content area without dimming the header or desktop sidebar, stays out of the page animation, and clears on the content swap or navigation cancellation. (ae7152ad5, 6abeadf5f, 479309f1f)
|
|
23
|
+
- Ordinary documentation prose and navigation labels now wrap at narrow widths instead of overflowing horizontally (`overflow-wrap: anywhere` on content). Preformatted code keeps its local scrolling. (648e38da8)
|
|
24
|
+
|
|
25
|
+
## [6.2.0] - 2026-10-14
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
- Adds optional `SidebarDefinition.scopeRoot?: "configured" | "editorial"` (default `"editorial"`, unchanged behavior). `"configured"` caps Broaden, branch focus, Restore, and route reconciliation at the authored forest: empty `items` stop at the category-filtered tree, one `autogenerated` directory stops at that directory, and an explicit list stops at that list. `initialPath` remains the starting view and the Restore baseline. At the cap the scope toolbar is omitted. An unsupported value fails the build. An omitted entry or an array stays editorial. (418b2f29c)
|
|
30
|
+
|
|
31
|
+
### Bug Fixes
|
|
32
|
+
|
|
33
|
+
- The shared `tsconfig.base.json` no longer sets `baseUrl`, which TypeScript 7 rejects with TS5102 in every project that extends it. Projects that relied on the inherited `baseUrl` for bare `paths` targets should write them as explicitly relative paths (for example `"@/*": ["./src/*"]`), which TypeScript 5.9 and 7 both resolve against the declaring tsconfig. (4d5e801ed)
|
|
34
|
+
- `buildSidebarForSection` now applies `initialPath` and `initialPathMode` to an object sidebar definition with empty `items`, matching `buildSidebarNavigation`. Previously the empty-items case returned the plain category-filtered tree and ignored those options. (31fe01015)
|
|
35
|
+
- Updates the lazy browser Mermaid URL from 11.15.0 to the exact 11.17.2 release, including the advisory fixes introduced in 11.16.1. Local npm audit does not cover the separately delivered CDN graph. (76d637663)
|
|
36
|
+
- The desktop sidebar and the mobile drawer each keep their scroll offset across client navigation, a full document load, reload, and Back/Forward, including after Broaden. Restoration runs after the destination tree can hold the offset, so a replaced sidebar or a temporary empty tree does not snap back to the top. A saved zero is restored. A missing snapshot is not written as zero. A closed drawer keeps its saved offset in history until it opens again. Article scroll, focus, filtering, and disclosure are left unchanged. (418b2f29c, 2b38ccf0a)
|
|
37
|
+
- The Appearance menu is a compact list of three rows (Light, Dark, System). The visible heading and the bottom helper are removed. The System row shows the device's current Light/Dark mode inline, and that status stays truthful while a manual Light or Dark preference is selected. The full localized helper remains the System option's accessible description. The popup is sized from its natural height at its final width, so its border no longer creates a spurious inner scrollbar. It opens above the trigger near the bottom edge and repositions when its content size changes. Opening, arrow keys, and focus return no longer scroll the page, and Tab / Shift+Tab leave the menu instead of cycling its rows. (37e3b8b42, 7aba63e28)
|
|
38
|
+
|
|
39
|
+
### Other Changes
|
|
40
|
+
|
|
41
|
+
- Requires zfb 4.5.0: the `@takazudo/zfb`, `zfb-runtime` and `zfb-md-wasm` peer floors move from `^4.3.3` to `^4.5.0`. zfb 4.4.0 updates its YAML frontmatter parser and fixes fragment Back/Forward navigation. zfb 4.5.0 also restores a previously rendered same-path query entry on Back/Forward, fixes sibling radio-group state on form reset, and adds opt-in CSS compiler and `ErrorBoundary` APIs. Upgrade the zfb family (and `zfb-adapter-cloudflare` when used) to 4.5.0 together with this release. (48a224468, 4f9d0c26f)
|
|
42
|
+
- Updates the Codex resource TOML parser to smol-toml `^1.9.1`, preserving configuration dates and agent metadata while including its bounded key-scanning fix. (05e3fb5e7)
|
|
43
|
+
|
|
11
44
|
## [6.1.1] - 2026-10-12
|
|
12
45
|
|
|
13
46
|
### Other Changes
|
package/README.md
CHANGED
|
@@ -108,8 +108,8 @@ If your project renders **semi-trusted or user-submitted** HTML in a preview, ov
|
|
|
108
108
|
## 6.0 migration and peers
|
|
109
109
|
|
|
110
110
|
**6.0.0** is the coordinated major release of `@takazudo/zudo-doc`,
|
|
111
|
-
`@takazudo/zudo-doc-history-server` and `create-zudo-doc`.
|
|
112
|
-
published **zfb 4.
|
|
111
|
+
`@takazudo/zudo-doc-history-server` and `create-zudo-doc`. The current workspace
|
|
112
|
+
targets the published **zfb 4.5.2** family (peer floor **^4.5.2**).
|
|
113
113
|
[Upstream #4097](https://github.com/Takazudo/zudo-front-builder/issues/4097) is
|
|
114
114
|
resolved; its strict published 4.3.1 consumer control passed 3/3 with no skips or
|
|
115
115
|
retries in the [source-bound verification](../../docs/findings/zfb-4.3.1/verification.md).
|
|
@@ -160,3 +160,18 @@ zfb CSS generation. Do not use physical package `dist/*.css` import paths.
|
|
|
160
160
|
This package is published to npm as `@takazudo/zudo-doc` (since `0.2.0`, `latest` tracks the current line). Inside this repo the host site consumes it as a workspace package through its compiled `dist/` — `pnpm dev` at the repo root runs two watchers in parallel, `tsup --watch` for the JS and `tsc -p tsconfig.build.json --watch` for the `.d.ts`, so edits under `src/` rebuild both halves automatically; for a one-off rebuild use `pnpm --filter @takazudo/zudo-doc build`.
|
|
161
161
|
|
|
162
162
|
zfb itself comes from npm (versions pinned in the root `package.json`). To develop against a local zfb checkout, use the temporary `pnpm.overrides` link escape hatch documented in root `CONTRIBUTING.md` — do not commit the override.
|
|
163
|
+
|
|
164
|
+
### Optional math peer (unreleased compatibility floor)
|
|
165
|
+
|
|
166
|
+
Math-enabled consumers install `katex@^0.19.0` (supported peer range:
|
|
167
|
+
`^0.18.2 || ^0.19.0`) and import `katex/dist/katex.min.css` before
|
|
168
|
+
`@takazudo/zudo-doc/features.css` in their CSS entry. CSS and fonts are bundled
|
|
169
|
+
locally. The consumer matrix covers 0.18.2, 0.18.11 and 0.19.0. The old 0.16
|
|
170
|
+
branch is excluded because GHSA-238p-pmpm-9mq7 is patched from 0.18.2.
|
|
171
|
+
KaTeX 0.18's `.katex-base` selector replaces `.base` in the package color rule.
|
|
172
|
+
|
|
173
|
+
`MathBlock` remains server-rendered, with inline/display wrappers, safe defaults
|
|
174
|
+
and visible malformed-input fallback. A math-disabled consumer needs neither
|
|
175
|
+
KaTeX nor its stylesheet; module evaluation succeeds without it, and explicit
|
|
176
|
+
`MathBlock` rendering reports a useful missing-peer error. This source change
|
|
177
|
+
raises the compatibility floor and is not yet published to npm.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* resolves npm package specifiers to publishable ESM modules.
|
|
4
4
|
*
|
|
5
5
|
* Pinned to a specific minor.patch (not the bare `@11` major) so the
|
|
6
|
-
*
|
|
6
|
+
* top-level package version is stable across deploys; floating on the major
|
|
7
7
|
* would silently pull in upstream theme-API or behaviour changes. Bump
|
|
8
8
|
* policy: there is no `mermaid` package.json dependency to keep in sync
|
|
9
9
|
* with — this constant is the sole source of truth for the mermaid
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* tests can override the URL — e.g. self-hosted mirrors, version-
|
|
16
16
|
* locked deployments — without re-templating the whole script.
|
|
17
17
|
*/
|
|
18
|
-
export declare const MERMAID_CDN_MODULE_URL = "https://esm.sh/mermaid@11.
|
|
18
|
+
export declare const MERMAID_CDN_MODULE_URL = "https://esm.sh/mermaid@11.17.2";
|
|
19
19
|
/**
|
|
20
20
|
* Build the inline init script with a caller-supplied module URL.
|
|
21
21
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AFTER_NAVIGATE_EVENT } from "../transitions/page-events.js";
|
|
2
|
-
const MERMAID_CDN_MODULE_URL = "https://esm.sh/mermaid@11.
|
|
2
|
+
const MERMAID_CDN_MODULE_URL = "https://esm.sh/mermaid@11.17.2";
|
|
3
3
|
function buildMermaidInitScript(cdnUrl) {
|
|
4
4
|
const safeUrlLiteral = JSON.stringify(cdnUrl).replace(
|
|
5
5
|
/<\/(script)/gi,
|
package/dist/compiled.css
CHANGED
|
@@ -626,6 +626,7 @@ body {
|
|
|
626
626
|
color: var(--color-fg);
|
|
627
627
|
font-size: var(--text-body);
|
|
628
628
|
line-height: var(--leading-relaxed);
|
|
629
|
+
overflow-wrap: anywhere;
|
|
629
630
|
}
|
|
630
631
|
|
|
631
632
|
@layer zd-flow {
|
|
@@ -1045,6 +1046,7 @@ a.text-accent.underline {
|
|
|
1045
1046
|
|
|
1046
1047
|
.page-loading-overlay {
|
|
1047
1048
|
position: fixed;
|
|
1049
|
+
view-transition-name: zfb-loading;
|
|
1048
1050
|
inset: 0;
|
|
1049
1051
|
z-index: var(--z-index-modal, 100);
|
|
1050
1052
|
display: flex;
|
|
@@ -1053,11 +1055,12 @@ a.text-accent.underline {
|
|
|
1053
1055
|
background: var(--color-page-loading-overlay, color-mix(in oklch, var(--color-overlay, #000) 60%, transparent));
|
|
1054
1056
|
opacity: 0;
|
|
1055
1057
|
pointer-events: none;
|
|
1056
|
-
transition:
|
|
1058
|
+
transition: none;
|
|
1057
1059
|
}
|
|
1058
1060
|
|
|
1059
1061
|
.page-loading-overlay[data-visible] {
|
|
1060
1062
|
opacity: 1;
|
|
1063
|
+
transition: opacity .15s ease-out;
|
|
1061
1064
|
}
|
|
1062
1065
|
|
|
1063
1066
|
a[data-zd-nav-pending], button[data-zd-nav-pending] {
|
|
@@ -1100,6 +1103,14 @@ a[data-zd-nav-pending], button[data-zd-nav-pending] {
|
|
|
1100
1103
|
}
|
|
1101
1104
|
}
|
|
1102
1105
|
|
|
1106
|
+
::view-transition-group(zfb-loading) {
|
|
1107
|
+
animation: none;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
::view-transition-old(zfb-loading), ::view-transition-new(zfb-loading) {
|
|
1111
|
+
display: none;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1103
1114
|
header[data-header] {
|
|
1104
1115
|
background-color: var(--color-surface, var(--color-bg));
|
|
1105
1116
|
z-index: var(--z-index-toolbar, 20);
|
|
@@ -1294,7 +1305,7 @@ pre.hi-root .line .highlighted-word {
|
|
|
1294
1305
|
white-space: nowrap;
|
|
1295
1306
|
}
|
|
1296
1307
|
|
|
1297
|
-
.katex .base {
|
|
1308
|
+
.katex .katex-base {
|
|
1298
1309
|
color: var(--zd-fg);
|
|
1299
1310
|
}
|
|
1300
1311
|
|
|
@@ -1403,11 +1414,11 @@ pre.hi-root .line .highlighted-word {
|
|
|
1403
1414
|
}
|
|
1404
1415
|
}
|
|
1405
1416
|
|
|
1406
|
-
|
|
1417
|
+
body > header {
|
|
1407
1418
|
view-transition-name: zfb-header;
|
|
1408
1419
|
}
|
|
1409
1420
|
|
|
1410
|
-
|
|
1421
|
+
#desktop-sidebar:not(.sr-only) {
|
|
1411
1422
|
view-transition-name: zfb-sidebar;
|
|
1412
1423
|
}
|
|
1413
1424
|
|
|
@@ -1415,26 +1426,34 @@ pre.hi-root .line .highlighted-word {
|
|
|
1415
1426
|
view-transition-name: zfb-footer;
|
|
1416
1427
|
}
|
|
1417
1428
|
|
|
1418
|
-
|
|
1429
|
+
.zd-desktop-sidebar-toggle {
|
|
1419
1430
|
view-transition-name: zfb-sidebar-toggle;
|
|
1420
1431
|
}
|
|
1421
1432
|
|
|
1422
|
-
|
|
1433
|
+
.zd-desktop-toc-toggle {
|
|
1423
1434
|
view-transition-name: zfb-toc-toggle;
|
|
1424
1435
|
}
|
|
1425
1436
|
|
|
1426
|
-
::view-transition-old(zfb-header), ::view-transition-new(zfb-header), ::view-transition-group(zfb-header), ::view-transition-
|
|
1437
|
+
::view-transition-old(zfb-header), ::view-transition-new(zfb-header), ::view-transition-group(zfb-header), ::view-transition-group(zfb-sidebar), ::view-transition-old(zfb-footer), ::view-transition-new(zfb-footer), ::view-transition-group(zfb-footer), ::view-transition-old(zfb-sidebar-toggle), ::view-transition-new(zfb-sidebar-toggle), ::view-transition-group(zfb-sidebar-toggle), ::view-transition-old(zfb-toc-toggle), ::view-transition-new(zfb-toc-toggle), ::view-transition-group(zfb-toc-toggle) {
|
|
1427
1438
|
animation: none;
|
|
1428
1439
|
}
|
|
1429
1440
|
|
|
1430
|
-
::view-transition-
|
|
1441
|
+
::view-transition-group(zfb-header) {
|
|
1442
|
+
z-index: var(--z-index-toolbar, 20);
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
::view-transition-old(zfb-sidebar), ::view-transition-old(zfb-footer):only-child, ::view-transition-old(zfb-sidebar-toggle):only-child, ::view-transition-old(zfb-toc-toggle):only-child {
|
|
1431
1446
|
animation: var(--default-transition-duration) ease-in both contentFadeOut;
|
|
1432
1447
|
}
|
|
1433
1448
|
|
|
1434
|
-
::view-transition-new(zfb-
|
|
1449
|
+
::view-transition-new(zfb-sidebar), ::view-transition-new(zfb-footer):only-child, ::view-transition-new(zfb-sidebar-toggle):only-child, ::view-transition-new(zfb-toc-toggle):only-child {
|
|
1435
1450
|
animation: var(--zd-transition-slower) ease-out both contentFadeIn;
|
|
1436
1451
|
}
|
|
1437
1452
|
|
|
1453
|
+
:root[data-zd-sidebar-transition="stationary"]::view-transition-old(zfb-sidebar), :root[data-zd-sidebar-transition="stationary"]::view-transition-new(zfb-sidebar) {
|
|
1454
|
+
animation: none;
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1438
1457
|
::view-transition-old(root) {
|
|
1439
1458
|
animation: var(--default-transition-duration) ease-in both contentFadeOut;
|
|
1440
1459
|
}
|
|
@@ -1444,7 +1463,7 @@ pre.hi-root .line .highlighted-word {
|
|
|
1444
1463
|
}
|
|
1445
1464
|
|
|
1446
1465
|
@media (prefers-reduced-motion: reduce) {
|
|
1447
|
-
::view-transition-old(root), ::view-transition-new(root), ::view-transition-group(root), ::view-transition-old(zfb-
|
|
1466
|
+
::view-transition-old(root), ::view-transition-new(root), ::view-transition-group(root), ::view-transition-old(zfb-sidebar), ::view-transition-old(zfb-footer):only-child, ::view-transition-old(zfb-sidebar-toggle):only-child, ::view-transition-old(zfb-toc-toggle):only-child, ::view-transition-new(zfb-sidebar), ::view-transition-new(zfb-footer):only-child, ::view-transition-new(zfb-sidebar-toggle):only-child, ::view-transition-new(zfb-toc-toggle):only-child {
|
|
1448
1467
|
animation: none;
|
|
1449
1468
|
}
|
|
1450
1469
|
}
|
|
@@ -2375,6 +2394,9 @@ site-search [data-search-results] > :is([data-search-placeholder], p) {
|
|
|
2375
2394
|
.w-\[1\.125rem\] {
|
|
2376
2395
|
width: 1.125rem;
|
|
2377
2396
|
}
|
|
2397
|
+
.w-\[1\.25rem\] {
|
|
2398
|
+
width: 1.25rem;
|
|
2399
|
+
}
|
|
2378
2400
|
.w-\[1\.575rem\] {
|
|
2379
2401
|
width: 1.575rem;
|
|
2380
2402
|
}
|
|
@@ -3428,6 +3450,11 @@ site-search [data-search-results] > :is([data-search-placeholder], p) {
|
|
|
3428
3450
|
background-color: color-mix(in oklab, var(--zw-color-accent) 10%, transparent);
|
|
3429
3451
|
}
|
|
3430
3452
|
}
|
|
3453
|
+
@media (hover: hover) {
|
|
3454
|
+
.hover\:bg-accent\/5:hover {
|
|
3455
|
+
background-color: color-mix(in oklab, var(--zw-color-accent) 5%, transparent);
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3431
3458
|
@media (hover: hover) {
|
|
3432
3459
|
.hover\:bg-bg\/10:hover {
|
|
3433
3460
|
background-color: color-mix(in oklab, var(--zw-color-bg) 10%, transparent);
|
package/dist/content.css
CHANGED
|
@@ -52,6 +52,9 @@
|
|
|
52
52
|
color: var(--color-fg);
|
|
53
53
|
font-size: var(--text-body);
|
|
54
54
|
line-height: var(--leading-relaxed);
|
|
55
|
+
/* Let ordinary prose and navigation labels reflow at narrow widths (#4585).
|
|
56
|
+
* Preformatted code keeps its white-space contract and local scrolling. */
|
|
57
|
+
overflow-wrap: anywhere;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
/* ── Flow spacing (vertical rhythm) ── */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs } from "@takazudo/zfb/zudo-react/jsx-runtime";
|
|
2
2
|
import { ClientRouter } from "@takazudo/zfb-runtime";
|
|
3
|
+
import { buildChromeTransitionBootstrap } from "../transitions/chrome-transition-bootstrap.js";
|
|
3
4
|
const DESKTOP_SIDEBAR_ID = "desktop-sidebar";
|
|
4
5
|
function DocLayout(props) {
|
|
5
6
|
const {
|
|
@@ -70,6 +71,7 @@ function DocLayout(props) {
|
|
|
70
71
|
);
|
|
71
72
|
}
|
|
72
73
|
if (head !== void 0) headChildren.push(head);
|
|
74
|
+
if (enableClientRouter !== false) headChildren.push(/* @__PURE__ */ jsx("script", { rawHtml: buildChromeTransitionBootstrap() }));
|
|
73
75
|
return /* @__PURE__ */ jsxs("html", { ...htmlAttrs, children: [
|
|
74
76
|
/* @__PURE__ */ jsx("head", { children: headChildren }),
|
|
75
77
|
/* @__PURE__ */ jsxs("body", { class: "min-h-screen antialiased", children: [
|
package/dist/features.css
CHANGED
|
@@ -283,7 +283,7 @@ pre.hi-root .line .highlighted-word {
|
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
/* Make KaTeX respect our color scheme */
|
|
286
|
-
.katex .base {
|
|
286
|
+
.katex .katex-base {
|
|
287
287
|
color: var(--zd-fg);
|
|
288
288
|
}
|
|
289
289
|
|
|
@@ -457,56 +457,22 @@ html[data-sidebar-hidden] .zd-doc-content-band[data-zd-wide] {
|
|
|
457
457
|
}
|
|
458
458
|
}
|
|
459
459
|
|
|
460
|
-
/*
|
|
461
|
-
*
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
* <header>, <aside id="desktop-sidebar">, <footer>, the desktop-sidebar-toggle
|
|
468
|
-
* button, and (since #3254) the desktop-toc-toggle button each carry a
|
|
469
|
-
* data-zfb-transition-persist attribute whose value is keyed by locale and
|
|
470
|
-
* nav-section. The five attribute selectors below assign a stable
|
|
471
|
-
* view-transition-name based on the attribute value prefix, extracting those
|
|
472
|
-
* elements from the root snapshot into their own named layers:
|
|
473
|
-
* - header-{lang} → zfb-header
|
|
474
|
-
* - sidebar-{locale}-… → zfb-sidebar
|
|
475
|
-
* - footer-{lang} → zfb-footer
|
|
476
|
-
* - desktop-sidebar-toggle → zfb-sidebar-toggle
|
|
477
|
-
* - desktop-toc-toggle → zfb-toc-toggle
|
|
478
|
-
*
|
|
479
|
-
* Once extracted, the root cross-fade animates only non-chrome content (main,
|
|
480
|
-
* article, TOC, etc.). The ::view-transition-{old,new,group}(<name>) rules
|
|
481
|
-
* disable animation for all five chrome layers. The group pseudo must be
|
|
482
|
-
* neutralised too — even when old/new are static the group container can still
|
|
483
|
-
* produce a geometry-morph animation when snapshot size/position differs.
|
|
484
|
-
*
|
|
485
|
-
* Entry/exit cross-fade (zudolab/zudo-doc#2072): animation: none is only
|
|
486
|
-
* correct when the chrome element exists on BOTH pages of a navigation.
|
|
487
|
-
* When it exists on one side only (docs page with sidebar → top page
|
|
488
|
-
* without), the named group holds a single snapshot — frozen at full
|
|
489
|
-
* opacity for the whole transition, then dropped abruptly at finish. The
|
|
490
|
-
* :only-child rules below detect that one-sided case and cross-fade the
|
|
491
|
-
* lone snapshot in sync with the root content fade (spec-standard
|
|
492
|
-
* entry/exit pattern; :only-child outranks the animation: none rules via
|
|
493
|
-
* the extra pseudo-class specificity). */
|
|
494
|
-
|
|
495
|
-
/* Chrome extraction — assign view-transition-name from data-zfb-transition-persist */
|
|
496
|
-
[data-zfb-transition-persist^="header-"] { view-transition-name: zfb-header; }
|
|
497
|
-
[data-zfb-transition-persist^="sidebar-"] { view-transition-name: zfb-sidebar; }
|
|
460
|
+
/* Visual region identity is independent of persistence/remount policy. The
|
|
461
|
+
* layout's header slot is a direct body header, including consumer overrides.
|
|
462
|
+
* A sidebar keeps its name even when incoming island props require replacement.
|
|
463
|
+
* Never name an ancestor of these regions: it would move their whole layer.
|
|
464
|
+
*/
|
|
465
|
+
body > header { view-transition-name: zfb-header; }
|
|
466
|
+
#desktop-sidebar:not(.sr-only) { view-transition-name: zfb-sidebar; }
|
|
498
467
|
[data-zfb-transition-persist^="footer-"] { view-transition-name: zfb-footer; }
|
|
499
|
-
|
|
500
|
-
|
|
468
|
+
.zd-desktop-sidebar-toggle { view-transition-name: zfb-sidebar-toggle; }
|
|
469
|
+
.zd-desktop-toc-toggle { view-transition-name: zfb-toc-toggle; }
|
|
501
470
|
|
|
502
|
-
/*
|
|
503
|
-
*
|
|
504
|
-
* it doesn't animate on SPA swaps). */
|
|
471
|
+
/* Header snapshots and every chrome group stay at their authored geometry.
|
|
472
|
+
* Footer and toggle pairs keep their established stationary policy. */
|
|
505
473
|
::view-transition-old(zfb-header),
|
|
506
474
|
::view-transition-new(zfb-header),
|
|
507
475
|
::view-transition-group(zfb-header),
|
|
508
|
-
::view-transition-old(zfb-sidebar),
|
|
509
|
-
::view-transition-new(zfb-sidebar),
|
|
510
476
|
::view-transition-group(zfb-sidebar),
|
|
511
477
|
::view-transition-old(zfb-footer),
|
|
512
478
|
::view-transition-new(zfb-footer),
|
|
@@ -518,24 +484,35 @@ html[data-sidebar-hidden] .zd-doc-content-band[data-zd-wide] {
|
|
|
518
484
|
::view-transition-new(zfb-toc-toggle),
|
|
519
485
|
::view-transition-group(zfb-toc-toggle) { animation: none; }
|
|
520
486
|
|
|
521
|
-
/*
|
|
522
|
-
*
|
|
523
|
-
::view-transition-
|
|
524
|
-
|
|
487
|
+
/* A translating incoming sidebar/root must not paint over the header's lower
|
|
488
|
+
* edge. Named snapshot groups have their own stacking order above the live DOM. */
|
|
489
|
+
::view-transition-group(zfb-header) { z-index: var(--z-index-toolbar, 20); }
|
|
490
|
+
|
|
491
|
+
/* A category/context change or a one-sided sidebar uses the content animation.
|
|
492
|
+
* The header remains stationary even when replaced or present on only one side.
|
|
493
|
+
* Other chrome retains its established entry/exit behavior. */
|
|
494
|
+
::view-transition-old(zfb-sidebar),
|
|
525
495
|
::view-transition-old(zfb-footer):only-child,
|
|
526
496
|
::view-transition-old(zfb-sidebar-toggle):only-child,
|
|
527
497
|
::view-transition-old(zfb-toc-toggle):only-child {
|
|
528
498
|
animation: var(--default-transition-duration) ease-in both contentFadeOut;
|
|
529
499
|
}
|
|
530
500
|
/* Entry — lone new snapshot fades in with the content cross-fade. */
|
|
531
|
-
::view-transition-new(zfb-
|
|
532
|
-
::view-transition-new(zfb-sidebar):only-child,
|
|
501
|
+
::view-transition-new(zfb-sidebar),
|
|
533
502
|
::view-transition-new(zfb-footer):only-child,
|
|
534
503
|
::view-transition-new(zfb-sidebar-toggle):only-child,
|
|
535
504
|
::view-transition-new(zfb-toc-toggle):only-child {
|
|
536
505
|
animation: var(--zd-transition-slower) ease-out both contentFadeIn;
|
|
537
506
|
}
|
|
538
507
|
|
|
508
|
+
/* Resolved navSection AND the existing navigation context must agree. The
|
|
509
|
+
* context already isolates locale, version and custom-tree topology; article,
|
|
510
|
+
* focused subtree and DOM identity are deliberately irrelevant here. */
|
|
511
|
+
:root[data-zd-sidebar-transition="stationary"]::view-transition-old(zfb-sidebar),
|
|
512
|
+
:root[data-zd-sidebar-transition="stationary"]::view-transition-new(zfb-sidebar) {
|
|
513
|
+
animation: none;
|
|
514
|
+
}
|
|
515
|
+
|
|
539
516
|
/* Root cross-fade rules — animate only the non-chrome snapshot */
|
|
540
517
|
::view-transition-old(root) {
|
|
541
518
|
animation: var(--default-transition-duration) ease-in both contentFadeOut;
|
|
@@ -559,13 +536,11 @@ html[data-sidebar-hidden] .zd-doc-content-band[data-zd-wide] {
|
|
|
559
536
|
::view-transition-old(root),
|
|
560
537
|
::view-transition-new(root),
|
|
561
538
|
::view-transition-group(root),
|
|
562
|
-
::view-transition-old(zfb-
|
|
563
|
-
::view-transition-old(zfb-sidebar):only-child,
|
|
539
|
+
::view-transition-old(zfb-sidebar),
|
|
564
540
|
::view-transition-old(zfb-footer):only-child,
|
|
565
541
|
::view-transition-old(zfb-sidebar-toggle):only-child,
|
|
566
542
|
::view-transition-old(zfb-toc-toggle):only-child,
|
|
567
|
-
::view-transition-new(zfb-
|
|
568
|
-
::view-transition-new(zfb-sidebar):only-child,
|
|
543
|
+
::view-transition-new(zfb-sidebar),
|
|
569
544
|
::view-transition-new(zfb-footer):only-child,
|
|
570
545
|
::view-transition-new(zfb-sidebar-toggle):only-child,
|
|
571
546
|
::view-transition-new(zfb-toc-toggle):only-child {
|
|
@@ -15,13 +15,13 @@ export interface PageLoadingOverlayProps {
|
|
|
15
15
|
*/
|
|
16
16
|
export declare function buildPageLoadingOverlayBootstrap(overlayId: string): string;
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* Content loading overlay shown while a dynamic navigation is pending.
|
|
19
19
|
*
|
|
20
20
|
* Mount this once per layout (typically inside `DocLayoutWithDefaults`'s
|
|
21
21
|
* `bodyEnd` slot, alongside the existing body-end providers). It is
|
|
22
22
|
* server-rendered and self-wires its visibility — no hydration needed.
|
|
23
23
|
*
|
|
24
|
-
* CSS
|
|
24
|
+
* CSS ships in `@takazudo/zudo-doc/page-loading.css` (`.page-loading-overlay`,
|
|
25
25
|
* `.page-loading-spinner`, `[data-zd-nav-pending]` rules) rather than in an
|
|
26
26
|
* inline `<style>` block here — a `<style>` inside `<body>` violates HTML5
|
|
27
27
|
* element-permitted-content and fails html-validate (same fix applied to the
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "@takazudo/zfb/zudo-react/jsx-runtime";
|
|
2
2
|
import {
|
|
3
3
|
AFTER_NAVIGATE_EVENT,
|
|
4
|
-
BEFORE_NAVIGATE_EVENT
|
|
4
|
+
BEFORE_NAVIGATE_EVENT,
|
|
5
|
+
NAVIGATION_ABORTED_EVENT
|
|
5
6
|
} from "../transitions/page-events.js";
|
|
6
7
|
const PAGE_LOADING_OVERLAY_ID = "page-loading-overlay";
|
|
7
8
|
function buildPageLoadingOverlayBootstrap(overlayId) {
|
|
@@ -10,7 +11,7 @@ function buildPageLoadingOverlayBootstrap(overlayId) {
|
|
|
10
11
|
const after = JSON.stringify(AFTER_NAVIGATE_EVENT);
|
|
11
12
|
return `(function(){
|
|
12
13
|
var id=${id};
|
|
13
|
-
function show(){var el=document.getElementById(id);if(!el)return;el.setAttribute("data-visible","");el.setAttribute("aria-hidden","false");}
|
|
14
|
+
function show(){var el=document.getElementById(id);if(!el)return;var content=document.querySelector(".zd-sidebar-content-wrapper");var header=document.querySelector("body > header");el.style.left=Math.max(0,content?content.getBoundingClientRect().left:0)+"px";el.style.top=Math.max(0,header?header.getBoundingClientRect().bottom:0)+"px";el.setAttribute("data-visible","");el.setAttribute("aria-hidden","false");}
|
|
14
15
|
function hide(){var el=document.getElementById(id);if(!el)return;el.removeAttribute("data-visible");el.setAttribute("aria-hidden","true");}
|
|
15
16
|
function setPending(ev){document.querySelectorAll("[data-zd-nav-pending]").forEach(function(el){el.removeAttribute("data-zd-nav-pending");});var src=ev&&ev.sourceElement;if(src&&src instanceof Element)src.setAttribute("data-zd-nav-pending","");}
|
|
16
17
|
function clearPending(){document.querySelectorAll("[data-zd-nav-pending]").forEach(function(el){el.removeAttribute("data-zd-nav-pending");});}
|
|
@@ -18,6 +19,7 @@ document.addEventListener(${before},show);
|
|
|
18
19
|
document.addEventListener(${after},hide);
|
|
19
20
|
document.addEventListener(${before},setPending);
|
|
20
21
|
document.addEventListener(${after},clearPending);
|
|
22
|
+
document.addEventListener(${JSON.stringify(NAVIGATION_ABORTED_EVENT)},function(){hide();clearPending();});
|
|
21
23
|
})();`;
|
|
22
24
|
}
|
|
23
25
|
function PageLoadingOverlay({
|
package/dist/page-loading.css
CHANGED
|
@@ -31,8 +31,9 @@
|
|
|
31
31
|
|
|
32
32
|
.page-loading-overlay {
|
|
33
33
|
position: fixed;
|
|
34
|
+
view-transition-name: zfb-loading;
|
|
34
35
|
inset: 0;
|
|
35
|
-
/*
|
|
36
|
+
/* Pending-content overlay — the bootstrap excludes the layout's chrome.
|
|
36
37
|
--z-index-modal fallback (100) ensures the overlay stacks on top even for
|
|
37
38
|
bare consumers that have not defined the token. */
|
|
38
39
|
z-index: var(--z-index-modal, 100);
|
|
@@ -45,11 +46,13 @@
|
|
|
45
46
|
);
|
|
46
47
|
opacity: 0;
|
|
47
48
|
pointer-events: none;
|
|
48
|
-
|
|
49
|
+
/* Hidden loading feedback must disappear synchronously. */
|
|
50
|
+
transition: none;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
.page-loading-overlay[data-visible] {
|
|
52
54
|
opacity: 1;
|
|
55
|
+
transition: opacity 150ms ease-out;
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
a[data-zd-nav-pending],
|
|
@@ -90,3 +93,11 @@ button[data-zd-nav-pending] {
|
|
|
90
93
|
border-bottom-color: var(--color-page-loading-spinner, #fff);
|
|
91
94
|
}
|
|
92
95
|
}
|
|
96
|
+
|
|
97
|
+
/* Pending feedback belongs to the fetch, not the page cross-fade. Extract it
|
|
98
|
+
* from the root snapshot, then suppress its snapshot layer. This also avoids
|
|
99
|
+
* an unscoped after-preparation event from an older fetch hiding newer feedback.
|
|
100
|
+
*/
|
|
101
|
+
::view-transition-group(zfb-loading) { animation: none; }
|
|
102
|
+
::view-transition-old(zfb-loading),
|
|
103
|
+
::view-transition-new(zfb-loading) { display: none; }
|
|
@@ -3,6 +3,7 @@ import { Fragment, jsx, jsxs } from "@takazudo/zfb/zudo-react/jsx-runtime";
|
|
|
3
3
|
import { computed, getScope, signal } from "@takazudo/zfb/zudo-react";
|
|
4
4
|
import { AFTER_NAVIGATE_EVENT, ensureNestedIslandPropsRefresh } from "../transitions/index.js";
|
|
5
5
|
import { SidebarTree } from "../sidebar-tree-island/index.js";
|
|
6
|
+
import { notifySidebarScrollReady } from "../sidebar-tree-island/sidebar-scroll-preserve.js";
|
|
6
7
|
ensureNestedIslandPropsRefresh();
|
|
7
8
|
const cx = (...classes) => classes.filter(Boolean).join(" ");
|
|
8
9
|
const HIDDEN_ICON_STYLE = "display:none";
|
|
@@ -29,6 +30,10 @@ function SidebarToggle({
|
|
|
29
30
|
document.body.style.overflow = isOpen ? "hidden" : "";
|
|
30
31
|
if (isOpen) closeIconRef.current?.style.removeProperty("display");
|
|
31
32
|
else closeIconRef.current?.style.setProperty("display", "none");
|
|
33
|
+
if (isOpen) {
|
|
34
|
+
const scroller = document.querySelector("aside[data-zd-mobile-sidebar] .flex-1.overflow-y-auto");
|
|
35
|
+
if (scroller) notifySidebarScrollReady(scroller);
|
|
36
|
+
}
|
|
32
37
|
if (isOpen) menuIconRef.current?.style.setProperty("display", "none");
|
|
33
38
|
else menuIconRef.current?.style.removeProperty("display");
|
|
34
39
|
return () => {
|