@takazudo/zudo-doc 7.1.0 → 8.1.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 +38 -0
- package/README.md +1 -1
- package/dist/chrome/derive.js +1 -0
- package/dist/compiled.css +25 -2
- package/dist/config-assertions/index.d.ts +13 -0
- package/dist/config-assertions/index.js +25 -0
- package/dist/config.d.ts +21 -1
- package/dist/config.js +2 -0
- package/dist/content.css +13 -2
- package/dist/doclayout/doc-layout.js +5 -1
- package/dist/features.css +24 -0
- package/dist/header/header.d.ts +9 -0
- package/dist/header/header.js +21 -2
- package/dist/header-with-defaults/index.d.ts +3 -1
- package/dist/header-with-defaults/index.js +1 -0
- package/dist/home-page/index.d.ts +1 -1
- package/dist/home-page/index.js +3 -2
- package/dist/nav-indexing/note-tray-index-parts/index-list.js +2 -2
- package/dist/preset.d.ts +4 -1
- package/dist/preset.js +3 -14
- package/dist/settings-defaults.js +2 -0
- package/dist/settings.d.ts +23 -0
- package/dist/site-tree-nav/index.d.ts +2 -0
- package/dist/site-tree-nav/index.js +2 -0
- package/dist/site-tree-nav/normalize-island-props.d.ts +1 -0
- package/dist/site-tree-nav/normalize-island-props.js +6 -0
- package/dist/site-tree-nav-island/index.d.ts +1 -1
- package/dist/site-tree-nav-island/index.js +15 -11
- package/dist/theme-packs/drift/pack.css +6 -0
- package/dist/theme-packs/foundry/pack.css +6 -0
- package/dist/theme-packs/futura-editorial/pack.css +6 -0
- package/dist/theme-packs/hollow/pack.css +3 -0
- package/dist/theme-packs/ledger/pack.css +3 -0
- package/dist/theme-packs/matcha/pack.css +3 -0
- package/dist/theme-packs/observatory/pack.css +3 -0
- package/dist/theme-packs/sakura/pack.css +3 -0
- package/dist/theme-packs/scandi/pack.css +3 -0
- package/dist/theme-packs/solar/pack.css +3 -0
- package/dist/theme-packs/tidepool/pack.css +3 -0
- package/dist/theme-packs/washi/pack.css +3 -0
- package/dist/transitions/persisted-focus-settle-bootstrap.d.ts +2 -0
- package/dist/transitions/persisted-focus-settle-bootstrap.js +22 -0
- package/dist/tree-nav-shared/index.d.ts +2 -0
- package/dist/tree-nav-shared/index.js +8 -1
- package/eject/header/header.tsx +40 -0
- package/eject/site-tree-nav-island/index.tsx +21 -12
- package/package.json +12 -16
- package/dist/plugins/optional-peers.d.ts +0 -3
- package/dist/plugins/optional-peers.js +0 -34
|
@@ -12,6 +12,12 @@ function normalizeSiteTreeNavProps(input) {
|
|
|
12
12
|
"categoryIgnore"
|
|
13
13
|
);
|
|
14
14
|
}
|
|
15
|
+
if (input.initiallyCollapsedCategorySlugs !== void 0) {
|
|
16
|
+
normalized.initiallyCollapsedCategorySlugs = normalizeStringArray(
|
|
17
|
+
input.initiallyCollapsedCategorySlugs,
|
|
18
|
+
"initiallyCollapsedCategorySlugs"
|
|
19
|
+
);
|
|
20
|
+
}
|
|
15
21
|
if (input.ariaLabel !== void 0) {
|
|
16
22
|
normalized.ariaLabel = requireString(input.ariaLabel, "ariaLabel");
|
|
17
23
|
}
|
|
@@ -5,7 +5,7 @@ export interface SiteTreeNavProps {
|
|
|
5
5
|
ariaLabel?: string;
|
|
6
6
|
categoryOrder?: string[];
|
|
7
7
|
categoryIgnore?: string[];
|
|
8
|
-
/**
|
|
8
|
+
/** Exact full category slugs at any depth that should start collapsed. */
|
|
9
9
|
initiallyCollapsedCategorySlugs?: string[];
|
|
10
10
|
/** Locale used by dated note-tray rows. */
|
|
11
11
|
locale?: string;
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
INDENT,
|
|
11
11
|
connectorLeft,
|
|
12
|
+
connectorLabelStart,
|
|
12
13
|
ConnectorLines,
|
|
13
14
|
CategoryLinkIcon
|
|
14
15
|
} from "../tree-nav-shared/index.js";
|
|
@@ -24,9 +25,11 @@ import {
|
|
|
24
25
|
import { formatMonthDay } from "../format-date/index.js";
|
|
25
26
|
import { initialCategoryOpenState, toggleCategoryOpenState } from "./state.js";
|
|
26
27
|
const SITE_BASE_PAD = "clamp(0.5rem, 0.8vw, 1rem)";
|
|
28
|
+
const CONNECTOR_WIDTH_SCALE = 2;
|
|
29
|
+
const CONNECTOR_LABEL_GAP = "var(--spacing-hsp-xs)";
|
|
27
30
|
function padLeft(depth) {
|
|
28
31
|
if (depth === 0) return SITE_BASE_PAD;
|
|
29
|
-
return `calc(${depth} * ${INDENT} + 1.25rem + 5px)`;
|
|
32
|
+
return `max(calc(${depth} * ${INDENT} + 1.25rem + 5px), ${connectorLabelStart(depth, CONNECTOR_WIDTH_SCALE, CONNECTOR_LABEL_GAP)})`;
|
|
30
33
|
}
|
|
31
34
|
function reorderTree(tree, order) {
|
|
32
35
|
const map = new Map(tree.map((node) => [node.slug, node]));
|
|
@@ -91,7 +94,7 @@ function SiteTreeNav({
|
|
|
91
94
|
node,
|
|
92
95
|
depth: 0,
|
|
93
96
|
isLast,
|
|
94
|
-
|
|
97
|
+
initiallyCollapsedCategorySlugs: initiallyCollapsed,
|
|
95
98
|
locale,
|
|
96
99
|
dateFormats,
|
|
97
100
|
updatedLabel
|
|
@@ -108,7 +111,8 @@ function SiteTreeNav({
|
|
|
108
111
|
SiteTreeNav.displayName = "SiteTreeNav";
|
|
109
112
|
function NodeList({
|
|
110
113
|
nodes,
|
|
111
|
-
depth
|
|
114
|
+
depth,
|
|
115
|
+
initiallyCollapsedCategorySlugs
|
|
112
116
|
}) {
|
|
113
117
|
return /* @__PURE__ */ jsx(For, { each: nodes, by: (node) => node.slug, children: (node, index) => {
|
|
114
118
|
const isLast = computed(() => index.value === nodes.value.length - 1);
|
|
@@ -117,7 +121,7 @@ function NodeList({
|
|
|
117
121
|
{
|
|
118
122
|
when: computed(() => node.value.children.length > 0),
|
|
119
123
|
fallback: () => /* @__PURE__ */ jsx(LeafNode, { node, depth, isLast }),
|
|
120
|
-
children: () => /* @__PURE__ */ jsx(CategoryNode, { node, depth, isLast })
|
|
124
|
+
children: () => /* @__PURE__ */ jsx(CategoryNode, { node, depth, isLast, initiallyCollapsedCategorySlugs })
|
|
121
125
|
}
|
|
122
126
|
);
|
|
123
127
|
} });
|
|
@@ -126,12 +130,12 @@ function CategoryNode({
|
|
|
126
130
|
node,
|
|
127
131
|
depth,
|
|
128
132
|
isLast,
|
|
129
|
-
|
|
133
|
+
initiallyCollapsedCategorySlugs,
|
|
130
134
|
locale = "en",
|
|
131
135
|
dateFormats,
|
|
132
136
|
updatedLabel = "Updated"
|
|
133
137
|
}) {
|
|
134
|
-
const open = signal(initialCategoryOpenState(
|
|
138
|
+
const open = signal(initialCategoryOpenState(initiallyCollapsedCategorySlugs.has(node.value.slug)));
|
|
135
139
|
const toggle = () => {
|
|
136
140
|
open.value = toggleCategoryOpenState(open.value);
|
|
137
141
|
};
|
|
@@ -157,7 +161,7 @@ function CategoryNode({
|
|
|
157
161
|
{
|
|
158
162
|
depth,
|
|
159
163
|
isLast: true,
|
|
160
|
-
widthScale:
|
|
164
|
+
widthScale: CONNECTOR_WIDTH_SCALE,
|
|
161
165
|
topPad: "calc(0.15rem + var(--spacing-vsp-xs))"
|
|
162
166
|
}
|
|
163
167
|
) }),
|
|
@@ -166,7 +170,7 @@ function CategoryNode({
|
|
|
166
170
|
{
|
|
167
171
|
depth,
|
|
168
172
|
isLast: false,
|
|
169
|
-
widthScale:
|
|
173
|
+
widthScale: CONNECTOR_WIDTH_SCALE,
|
|
170
174
|
topPad: "calc(0.15rem + var(--spacing-vsp-xs))"
|
|
171
175
|
}
|
|
172
176
|
) }),
|
|
@@ -234,7 +238,7 @@ function CategoryNode({
|
|
|
234
238
|
when: computed(
|
|
235
239
|
() => node.value.shape === "note-tray" && depth === 0
|
|
236
240
|
),
|
|
237
|
-
fallback: () => /* @__PURE__ */ jsx(NodeList, { nodes: children, depth: depth + 1 }),
|
|
241
|
+
fallback: () => /* @__PURE__ */ jsx(NodeList, { nodes: children, depth: depth + 1, initiallyCollapsedCategorySlugs }),
|
|
238
242
|
children: () => /* @__PURE__ */ jsx(
|
|
239
243
|
NoteTrayNodeList,
|
|
240
244
|
{
|
|
@@ -369,7 +373,7 @@ function LeafNode({
|
|
|
369
373
|
{
|
|
370
374
|
depth,
|
|
371
375
|
isLast: true,
|
|
372
|
-
widthScale:
|
|
376
|
+
widthScale: CONNECTOR_WIDTH_SCALE,
|
|
373
377
|
topPad
|
|
374
378
|
}
|
|
375
379
|
) }),
|
|
@@ -378,7 +382,7 @@ function LeafNode({
|
|
|
378
382
|
{
|
|
379
383
|
depth,
|
|
380
384
|
isLast: false,
|
|
381
|
-
widthScale:
|
|
385
|
+
widthScale: CONNECTOR_WIDTH_SCALE,
|
|
382
386
|
topPad
|
|
383
387
|
}
|
|
384
388
|
) }),
|
|
@@ -314,6 +314,9 @@ html[data-theme-pack="drift"] a[data-nav-active] {
|
|
|
314
314
|
html[data-theme-pack="drift"] .zd-content h1 {
|
|
315
315
|
text-shadow: 0 0 34px color-mix(in oklch, var(--zd-accent) 32%, transparent);
|
|
316
316
|
}
|
|
317
|
+
html[data-theme-pack="drift"] .zd-content h2:where(:not(.zd-home-heading)):where(:not(.zd-compact-prose *)) {
|
|
318
|
+
font-size: 1.6rem;
|
|
319
|
+
}
|
|
317
320
|
html[data-theme-pack="drift"] .zd-content h2 {
|
|
318
321
|
border-top-width: 1px;
|
|
319
322
|
border-image: linear-gradient(
|
|
@@ -323,6 +326,9 @@ html[data-theme-pack="drift"] .zd-content h2 {
|
|
|
323
326
|
)
|
|
324
327
|
1 !important;
|
|
325
328
|
}
|
|
329
|
+
html[data-theme-pack="drift"] .zd-content h3:where(:not(.zd-compact-prose *)) {
|
|
330
|
+
border-top-width: 1px;
|
|
331
|
+
}
|
|
326
332
|
|
|
327
333
|
/* links: airy underline that brightens on approach */
|
|
328
334
|
html[data-theme-pack="drift"] .zd-content a {
|
|
@@ -107,11 +107,17 @@ html[data-theme-pack="foundry"] .zd-content h1 {
|
|
|
107
107
|
padding-bottom: 0.55rem;
|
|
108
108
|
border-bottom: 1px solid color-mix(in srgb, var(--zd-muted) 42%, transparent);
|
|
109
109
|
}
|
|
110
|
+
html[data-theme-pack="foundry"] .zd-content h2:where(:not(.zd-home-heading)):where(:not(.zd-compact-prose *)) {
|
|
111
|
+
font-size: 1.6rem;
|
|
112
|
+
}
|
|
110
113
|
html[data-theme-pack="foundry"] .zd-content h2 {
|
|
111
114
|
border-image: none !important;
|
|
112
115
|
border-top-width: 1px;
|
|
113
116
|
border-top-color: color-mix(in srgb, var(--zd-muted) 42%, transparent);
|
|
114
117
|
}
|
|
118
|
+
html[data-theme-pack="foundry"] .zd-content h3:where(:not(.zd-compact-prose *)) {
|
|
119
|
+
border-top-width: 1px;
|
|
120
|
+
}
|
|
115
121
|
|
|
116
122
|
/* inline code: neutral wash that holds up in both modes */
|
|
117
123
|
html[data-theme-pack="foundry"] .zd-content code:not(pre code) {
|
|
@@ -216,11 +216,17 @@ html[data-theme-pack="futura-editorial"] .zd-content > p[data-doc-description] {
|
|
|
216
216
|
fade (signature move #2). The h2 rule needs !important to defeat the
|
|
217
217
|
SSR-inlined `style="border-image:linear-gradient(...)"` (Decision
|
|
218
218
|
6.6's sole allowlisted carve-out). */
|
|
219
|
+
html[data-theme-pack="futura-editorial"] .zd-content h2:where(:not(.zd-home-heading)):where(:not(.zd-compact-prose *)) {
|
|
220
|
+
font-size: 1.6rem;
|
|
221
|
+
}
|
|
219
222
|
html[data-theme-pack="futura-editorial"] .zd-content h2 {
|
|
220
223
|
border-image: none !important;
|
|
221
224
|
border-top-width: 1px;
|
|
222
225
|
border-top-color: var(--zd-fg);
|
|
223
226
|
}
|
|
227
|
+
html[data-theme-pack="futura-editorial"] .zd-content h3:where(:not(.zd-compact-prose *)) {
|
|
228
|
+
border-top-width: 1px;
|
|
229
|
+
}
|
|
224
230
|
html[data-theme-pack="futura-editorial"] .zd-content h3 {
|
|
225
231
|
font-family: var(--font-display);
|
|
226
232
|
letter-spacing: 0.01em;
|
|
@@ -264,6 +264,9 @@ html[data-theme-pack="hollow"] .zd-content h1 {
|
|
|
264
264
|
/* h2: the SSR-inlined gradient bar is re-pointed at a fading pink
|
|
265
265
|
rule (Decision 6.6's sole allowlisted !important carve-out —
|
|
266
266
|
border-image only) */
|
|
267
|
+
html[data-theme-pack="hollow"] .zd-content h2:where(:not(.zd-home-heading)):where(:not(.zd-compact-prose *)) {
|
|
268
|
+
font-size: 1.5rem;
|
|
269
|
+
}
|
|
267
270
|
html[data-theme-pack="hollow"] .zd-content h2 {
|
|
268
271
|
border-image: linear-gradient(
|
|
269
272
|
to right,
|
|
@@ -281,6 +281,9 @@ html[data-theme-pack="ledger"] .zd-content h2 {
|
|
|
281
281
|
font-style: italic;
|
|
282
282
|
font-size: 1.6rem;
|
|
283
283
|
}
|
|
284
|
+
html[data-theme-pack="ledger"] .zd-content h3:where(:not(.zd-compact-prose *)) {
|
|
285
|
+
border-top-width: 1px;
|
|
286
|
+
}
|
|
284
287
|
html[data-theme-pack="ledger"] .zd-content h3 {
|
|
285
288
|
font-style: italic;
|
|
286
289
|
font-size: 1.3rem;
|
|
@@ -229,6 +229,9 @@ html[data-theme-pack="matcha"] .zd-content h2 {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
/* h3: quiet, slightly tracked (weight 600 rides --zdc-doc-h3-weight) */
|
|
232
|
+
html[data-theme-pack="matcha"] .zd-content h3:where(:not(.zd-compact-prose *)) {
|
|
233
|
+
border-top-width: 1px;
|
|
234
|
+
}
|
|
232
235
|
html[data-theme-pack="matcha"] .zd-content h3 {
|
|
233
236
|
letter-spacing: 0.01em;
|
|
234
237
|
}
|
|
@@ -422,6 +422,9 @@ html[data-theme-pack="observatory"] .zd-content > p[data-doc-description] {
|
|
|
422
422
|
allowlisted carve-out) + a zeroed border-top; the node-to-line rule
|
|
423
423
|
is then painted as a top-anchored background gradient, with the two
|
|
424
424
|
glowing star nodes as pseudo-element dots. */
|
|
425
|
+
html[data-theme-pack="observatory"] .zd-content h2:where(:not(.zd-home-heading)):where(:not(.zd-compact-prose *)) {
|
|
426
|
+
font-size: 1.75rem;
|
|
427
|
+
}
|
|
425
428
|
html[data-theme-pack="observatory"] .zd-content h2 {
|
|
426
429
|
border-image: none !important;
|
|
427
430
|
border-top-width: 0;
|
|
@@ -284,6 +284,9 @@ html[data-theme-pack="sakura"] a[data-nav-active] {
|
|
|
284
284
|
into drifting transparency. Re-points the SSR-inlined
|
|
285
285
|
`style="border-image:linear-gradient(...)"` gradient — the
|
|
286
286
|
border-image !important carve-out (Decision 6.6). */
|
|
287
|
+
html[data-theme-pack="sakura"] .zd-content h2:where(:not(.zd-home-heading)):where(:not(.zd-compact-prose *)) {
|
|
288
|
+
font-size: 1.6rem;
|
|
289
|
+
}
|
|
287
290
|
html[data-theme-pack="sakura"] .zd-content h2 {
|
|
288
291
|
border-top-width: 2px;
|
|
289
292
|
border-image: linear-gradient(
|
|
@@ -225,6 +225,9 @@ html[data-theme-pack="scandi"] .zd-content h2 {
|
|
|
225
225
|
border-top-width: 1px;
|
|
226
226
|
border-top-color: color-mix(in srgb, var(--zd-accent) 38%, transparent);
|
|
227
227
|
}
|
|
228
|
+
html[data-theme-pack="scandi"] .zd-content h3:where(:not(.zd-compact-prose *)) {
|
|
229
|
+
border-top-width: 1px;
|
|
230
|
+
}
|
|
228
231
|
|
|
229
232
|
/* links: sage ink with a soft sage underline that firms on hover */
|
|
230
233
|
html[data-theme-pack="scandi"] .zd-content a {
|
|
@@ -215,6 +215,9 @@ html[data-theme-pack="solar"] main nav[aria-label="Breadcrumb"] li > span {
|
|
|
215
215
|
bar is defeated with border-image none !important (Decision 6.6's
|
|
216
216
|
sole allowlisted carve-out) + a zeroed border-top; the fade is then
|
|
217
217
|
painted as a 2px top-anchored background gradient. */
|
|
218
|
+
html[data-theme-pack="solar"] .zd-content h2:where(:not(.zd-home-heading)):where(:not(.zd-compact-prose *)) {
|
|
219
|
+
font-size: 1.5rem;
|
|
220
|
+
}
|
|
218
221
|
html[data-theme-pack="solar"] .zd-content h2 {
|
|
219
222
|
border-image: none !important;
|
|
220
223
|
border-top-width: 0;
|
|
@@ -219,6 +219,9 @@ html[data-theme-pack="tidepool"] a[data-nav-active] {
|
|
|
219
219
|
html[data-theme-pack="tidepool"] .zd-content h1 {
|
|
220
220
|
color: color-mix(in srgb, var(--zd-accent) 12%, var(--zd-fg));
|
|
221
221
|
}
|
|
222
|
+
html[data-theme-pack="tidepool"] .zd-content h2:where(:not(.zd-home-heading)):where(:not(.zd-compact-prose *)) {
|
|
223
|
+
font-size: 1.5rem;
|
|
224
|
+
}
|
|
222
225
|
html[data-theme-pack="tidepool"] .zd-content h2 {
|
|
223
226
|
border-image: none !important;
|
|
224
227
|
border-top-width: 0;
|
|
@@ -314,6 +314,9 @@ html[data-theme-pack="washi"] .zd-content > p[data-doc-description] {
|
|
|
314
314
|
SSR-inlined `style="border-image:linear-gradient(...)"` — the
|
|
315
315
|
border-image !important carve-out (Decision 6.6), here re-pointing
|
|
316
316
|
the gradient rather than clearing it. */
|
|
317
|
+
html[data-theme-pack="washi"] .zd-content h2:where(:not(.zd-home-heading)):where(:not(.zd-compact-prose *)) {
|
|
318
|
+
font-size: 1.6rem;
|
|
319
|
+
}
|
|
317
320
|
html[data-theme-pack="washi"] .zd-content h2 {
|
|
318
321
|
border-top-width: 2px;
|
|
319
322
|
border-image: linear-gradient(
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BEFORE_SWAP_EVENT } from "./page-events.js";
|
|
2
|
+
function buildPersistedFocusSettleBootstrap() {
|
|
3
|
+
return `(function(){
|
|
4
|
+
if(document.__zdPersistedFocusSettle)return;
|
|
5
|
+
document.__zdPersistedFocusSettle=true;
|
|
6
|
+
document.addEventListener(${JSON.stringify(BEFORE_SWAP_EVENT)},function(ev){
|
|
7
|
+
if(typeof ev.swap!=="function")return;
|
|
8
|
+
var originalSwap=ev.swap;
|
|
9
|
+
var wrapped=function(){
|
|
10
|
+
var active=document.activeElement;
|
|
11
|
+
var retained=!!active&&typeof active.closest==="function"&&!!active.closest("[data-zfb-transition-persist]");
|
|
12
|
+
var result=originalSwap.apply(this,arguments);
|
|
13
|
+
if(retained&&active.isConnected&&document.activeElement===active)void document.documentElement.offsetHeight;
|
|
14
|
+
return result;
|
|
15
|
+
};
|
|
16
|
+
try{ev.swap=wrapped;}catch(e){}
|
|
17
|
+
});
|
|
18
|
+
})();`;
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
buildPersistedFocusSettleBootstrap
|
|
22
|
+
};
|
|
@@ -4,6 +4,8 @@ export declare const CONNECTOR_OFFSET = "clamp(0.2rem, 0.3vw, 0.5rem)";
|
|
|
4
4
|
export declare const CONNECTOR_WIDTH = "clamp(0.4rem, 0.6vw, 1rem)";
|
|
5
5
|
export declare const BASE_PAD = "clamp(0.4rem, 0.8vw, 1.3rem)";
|
|
6
6
|
export declare function connectorLeft(depth: number): string;
|
|
7
|
+
/** Left edge a label needs so it starts `gap` after the horizontal connector's endpoint. */
|
|
8
|
+
export declare function connectorLabelStart(depth: number, widthScale: number, gap: string): string;
|
|
7
9
|
export declare function CategoryLinkIcon({ class: className }: {
|
|
8
10
|
class?: string;
|
|
9
11
|
}): import("@takazudo/zfb").Description;
|
|
@@ -6,6 +6,12 @@ const BASE_PAD = "clamp(0.4rem, 0.8vw, 1.3rem)";
|
|
|
6
6
|
function connectorLeft(depth) {
|
|
7
7
|
return `calc(${depth} * ${INDENT} + ${CONNECTOR_OFFSET})`;
|
|
8
8
|
}
|
|
9
|
+
function connectorWidth(widthScale) {
|
|
10
|
+
return widthScale === 1 ? CONNECTOR_WIDTH : `calc(${CONNECTOR_WIDTH} * ${widthScale})`;
|
|
11
|
+
}
|
|
12
|
+
function connectorLabelStart(depth, widthScale, gap) {
|
|
13
|
+
return `calc(${connectorLeft(depth)} + ${connectorWidth(widthScale)} + ${gap})`;
|
|
14
|
+
}
|
|
9
15
|
const CATEGORY_LINK_PATH = "M5.746 5.74 0 11.49l20.987 20.96C34.126 45.572 41.963 53.45 41.948 53.523c-.012.062-9.456 9.544-20.986 21.07L0 95.55l5.714 5.715c3.142 3.143 5.748 5.715 5.79 5.715s2.63-2.563 5.75-5.696l17.939-18.001c21.867-21.94 29.443-29.599 29.443-29.768 0-.114-.665-.804-5.084-5.275C51.872 40.47 11.71.125 11.565.036 11.525.01 8.906 2.578 5.746 5.74m38.345-.066c-3.132 3.13-5.696 5.71-5.696 5.732-.001.022 2.16 2.185 4.8 4.807 2.641 2.623 8.382 8.338 12.758 12.702 15.38 15.337 23.763 23.641 24.314 24.086.19.153.346.336.346.405 0 .07-1.738 1.847-3.887 3.976a17515 17515 0 0 0-20.35 20.264 19555 19555 0 0 1-17.223 17.158c-.416.409-.757.77-.757.8 0 .083 11.415 11.485 11.457 11.445.235-.22 53.542-53.528 53.542-53.543C103.395 53.472 49.891.02 49.837 0c-.028-.01-2.613 2.543-5.746 5.674";
|
|
10
16
|
function CategoryLinkIcon({ class: className }) {
|
|
11
17
|
return /* @__PURE__ */ jsx(
|
|
@@ -27,7 +33,7 @@ function ConnectorLines({
|
|
|
27
33
|
}) {
|
|
28
34
|
if (depth === 0) return null;
|
|
29
35
|
const left = connectorLeft(depth);
|
|
30
|
-
const width =
|
|
36
|
+
const width = connectorWidth(widthScale);
|
|
31
37
|
const firstLineMid = `calc(${topPad} + 0.5lh)`;
|
|
32
38
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33
39
|
/* @__PURE__ */ jsx(
|
|
@@ -61,5 +67,6 @@ export {
|
|
|
61
67
|
CategoryLinkIcon,
|
|
62
68
|
ConnectorLines,
|
|
63
69
|
INDENT,
|
|
70
|
+
connectorLabelStart,
|
|
64
71
|
connectorLeft
|
|
65
72
|
};
|
package/eject/header/header.tsx
CHANGED
|
@@ -60,6 +60,7 @@ import type {
|
|
|
60
60
|
Locale,
|
|
61
61
|
} from "./types.js";
|
|
62
62
|
import { GitHub as GitHubIcon } from "../icons/index.js";
|
|
63
|
+
import type { HeaderBrandConfig } from "../settings.js";
|
|
63
64
|
|
|
64
65
|
/**
|
|
65
66
|
* Boundary helpers the host injects into `<Header>`. These are the URL
|
|
@@ -168,6 +169,15 @@ export interface HeaderProps {
|
|
|
168
169
|
/** Site-name string shown in the logo anchor (host `settings.siteName`). */
|
|
169
170
|
siteName: string;
|
|
170
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Optional brand icon rendered before `siteName` inside the logo anchor
|
|
174
|
+
* (host `settings.headerBrand`). Omitted or `false` renders the anchor
|
|
175
|
+
* exactly as before. The icon is a decorative `currentColor` CSS mask; an
|
|
176
|
+
* `icon` starting with `/` goes through `urlHelpers.withBase()`, anything
|
|
177
|
+
* else is used verbatim.
|
|
178
|
+
*/
|
|
179
|
+
headerBrand?: false | HeaderBrandConfig;
|
|
180
|
+
|
|
171
181
|
/** Header-nav items in render order (host `settings.headerNav`). */
|
|
172
182
|
headerNav: HeaderNavItem[];
|
|
173
183
|
|
|
@@ -265,6 +275,7 @@ export function Header(props: HeaderProps): JSX.Element {
|
|
|
265
275
|
search,
|
|
266
276
|
persistKey,
|
|
267
277
|
siteName,
|
|
278
|
+
headerBrand,
|
|
268
279
|
headerNav,
|
|
269
280
|
headerRightItems,
|
|
270
281
|
headerRightComponents,
|
|
@@ -289,6 +300,16 @@ export function Header(props: HeaderProps): JSX.Element {
|
|
|
289
300
|
const matchPath = pathForMatch(pathWithoutVersion, lang, i18n.defaultLocale);
|
|
290
301
|
|
|
291
302
|
const activeNavPath = computeActiveNavPath(headerNav, matchPath);
|
|
303
|
+
// Direct `<Header>` consumers and raw route payloads bypass the config-time
|
|
304
|
+
// `assertValidHeaderBrand`, so a malformed value degrades to "no icon".
|
|
305
|
+
const brandIcon = headerBrand && typeof headerBrand.icon === "string" && headerBrand.icon !== ""
|
|
306
|
+
? headerBrand.icon
|
|
307
|
+
: null;
|
|
308
|
+
const brandIconUrl = brandIcon === null
|
|
309
|
+
? null
|
|
310
|
+
: brandIcon.startsWith("/") && !brandIcon.startsWith("//")
|
|
311
|
+
? urlHelpers.withBase(brandIcon)
|
|
312
|
+
: brandIcon;
|
|
292
313
|
const rightItemDispatch = createRightItemDispatch(headerRightComponents);
|
|
293
314
|
const rightItems = headerRightItems
|
|
294
315
|
.map((item, i) => ({
|
|
@@ -397,7 +418,17 @@ export function Header(props: HeaderProps): JSX.Element {
|
|
|
397
418
|
class="min-w-0 truncate text-title font-bold text-fg hover:underline focus:underline"
|
|
398
419
|
title={siteName}
|
|
399
420
|
data-header-logo
|
|
421
|
+
data-header-brand={brandIconUrl !== null ? true : undefined}
|
|
400
422
|
>
|
|
423
|
+
{brandIconUrl !== null && (
|
|
424
|
+
// Painted by features.css as a currentColor mask so it follows the
|
|
425
|
+
// site-name ink; decorative, the anchor's name stays the site name.
|
|
426
|
+
<span
|
|
427
|
+
aria-hidden="true"
|
|
428
|
+
data-header-brand-icon
|
|
429
|
+
style={{ "--zd-header-brand-icon": cssUrl(brandIconUrl) }}
|
|
430
|
+
/>
|
|
431
|
+
)}
|
|
401
432
|
{siteName}
|
|
402
433
|
</a>
|
|
403
434
|
|
|
@@ -908,3 +939,12 @@ function renderRightItem(
|
|
|
908
939
|
|
|
909
940
|
return null;
|
|
910
941
|
}
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* Serialize a URL as a quoted CSS `url()` token so paths with whitespace,
|
|
945
|
+
* parentheses or quotes survive inside the custom property. Kept local to
|
|
946
|
+
* this module because the header is ejectable (see `eject/index.ts`).
|
|
947
|
+
*/
|
|
948
|
+
function cssUrl(url: string): string {
|
|
949
|
+
return `url("${url.replace(/[\\"]/g, "\\$&").replace(/[\n\r\f]/g, "\\a ")}")`;
|
|
950
|
+
}
|
|
@@ -12,6 +12,7 @@ import type { ResolvedDateFormats } from "../settings.js";
|
|
|
12
12
|
import {
|
|
13
13
|
INDENT,
|
|
14
14
|
connectorLeft,
|
|
15
|
+
connectorLabelStart,
|
|
15
16
|
ConnectorLines,
|
|
16
17
|
CategoryLinkIcon,
|
|
17
18
|
} from "../tree-nav-shared/index.js";
|
|
@@ -30,9 +31,15 @@ import { initialCategoryOpenState, toggleCategoryOpenState } from "./state.js";
|
|
|
30
31
|
// site-tree-nav uses wider padding than the narrow sidebar
|
|
31
32
|
const SITE_BASE_PAD = "clamp(0.5rem, 0.8vw, 1rem)";
|
|
32
33
|
|
|
34
|
+
// The homepage draws its connectors at twice the sidebar width, so on wide
|
|
35
|
+
// viewports the branch endpoint passes the fixed label indent. The label starts
|
|
36
|
+
// at whichever is further right: that indent, or the endpoint plus a gap.
|
|
37
|
+
const CONNECTOR_WIDTH_SCALE = 2;
|
|
38
|
+
const CONNECTOR_LABEL_GAP = "var(--spacing-hsp-xs)";
|
|
39
|
+
|
|
33
40
|
function padLeft(depth: number): string {
|
|
34
41
|
if (depth === 0) return SITE_BASE_PAD;
|
|
35
|
-
return `calc(${depth} * ${INDENT} + 1.25rem + 5px)`;
|
|
42
|
+
return `max(calc(${depth} * ${INDENT} + 1.25rem + 5px), ${connectorLabelStart(depth, CONNECTOR_WIDTH_SCALE, CONNECTOR_LABEL_GAP)})`;
|
|
36
43
|
}
|
|
37
44
|
|
|
38
45
|
function reorderTree(tree: SidebarNavNode[], order: string[]): SidebarNavNode[] {
|
|
@@ -57,7 +64,7 @@ export interface SiteTreeNavProps {
|
|
|
57
64
|
ariaLabel?: string;
|
|
58
65
|
categoryOrder?: string[];
|
|
59
66
|
categoryIgnore?: string[];
|
|
60
|
-
/**
|
|
67
|
+
/** Exact full category slugs at any depth that should start collapsed. */
|
|
61
68
|
initiallyCollapsedCategorySlugs?: string[];
|
|
62
69
|
/** Locale used by dated note-tray rows. */
|
|
63
70
|
locale?: string;
|
|
@@ -129,7 +136,7 @@ export function SiteTreeNav({
|
|
|
129
136
|
node={node}
|
|
130
137
|
depth={0}
|
|
131
138
|
isLast={isLast}
|
|
132
|
-
|
|
139
|
+
initiallyCollapsedCategorySlugs={initiallyCollapsed}
|
|
133
140
|
locale={locale}
|
|
134
141
|
dateFormats={dateFormats}
|
|
135
142
|
updatedLabel={updatedLabel}
|
|
@@ -150,9 +157,11 @@ SiteTreeNav.displayName = "SiteTreeNav";
|
|
|
150
157
|
function NodeList({
|
|
151
158
|
nodes,
|
|
152
159
|
depth,
|
|
160
|
+
initiallyCollapsedCategorySlugs,
|
|
153
161
|
}: {
|
|
154
162
|
nodes: ReadonlySignal<readonly SidebarNavNode[]>;
|
|
155
163
|
depth: number;
|
|
164
|
+
initiallyCollapsedCategorySlugs: ReadonlySet<string>;
|
|
156
165
|
}) {
|
|
157
166
|
return (
|
|
158
167
|
<For each={nodes} by={(node) => node.slug}>
|
|
@@ -166,7 +175,7 @@ function NodeList({
|
|
|
166
175
|
)}
|
|
167
176
|
>
|
|
168
177
|
{() => (
|
|
169
|
-
<CategoryNode node={node} depth={depth} isLast={isLast} />
|
|
178
|
+
<CategoryNode node={node} depth={depth} isLast={isLast} initiallyCollapsedCategorySlugs={initiallyCollapsedCategorySlugs} />
|
|
170
179
|
)}
|
|
171
180
|
</Show>
|
|
172
181
|
);
|
|
@@ -179,7 +188,7 @@ function CategoryNode({
|
|
|
179
188
|
node,
|
|
180
189
|
depth,
|
|
181
190
|
isLast,
|
|
182
|
-
|
|
191
|
+
initiallyCollapsedCategorySlugs,
|
|
183
192
|
locale = "en",
|
|
184
193
|
dateFormats,
|
|
185
194
|
updatedLabel = "Updated",
|
|
@@ -187,12 +196,12 @@ function CategoryNode({
|
|
|
187
196
|
node: ReadonlySignal<SidebarNavNode>;
|
|
188
197
|
depth: number;
|
|
189
198
|
isLast: ReadonlySignal<boolean>;
|
|
190
|
-
|
|
199
|
+
initiallyCollapsedCategorySlugs: ReadonlySet<string>;
|
|
191
200
|
locale?: string;
|
|
192
201
|
dateFormats?: ResolvedDateFormats;
|
|
193
202
|
updatedLabel?: string;
|
|
194
203
|
}) {
|
|
195
|
-
const open = signal(initialCategoryOpenState(
|
|
204
|
+
const open = signal(initialCategoryOpenState(initiallyCollapsedCategorySlugs.has(node.value.slug)));
|
|
196
205
|
const toggle = () => {
|
|
197
206
|
open.value = toggleCategoryOpenState(open.value);
|
|
198
207
|
};
|
|
@@ -221,7 +230,7 @@ function CategoryNode({
|
|
|
221
230
|
<ConnectorLines
|
|
222
231
|
depth={depth}
|
|
223
232
|
isLast={true}
|
|
224
|
-
widthScale={
|
|
233
|
+
widthScale={CONNECTOR_WIDTH_SCALE}
|
|
225
234
|
topPad="calc(0.15rem + var(--spacing-vsp-xs))"
|
|
226
235
|
/>
|
|
227
236
|
)}
|
|
@@ -231,7 +240,7 @@ function CategoryNode({
|
|
|
231
240
|
<ConnectorLines
|
|
232
241
|
depth={depth}
|
|
233
242
|
isLast={false}
|
|
234
|
-
widthScale={
|
|
243
|
+
widthScale={CONNECTOR_WIDTH_SCALE}
|
|
235
244
|
topPad="calc(0.15rem + var(--spacing-vsp-xs))"
|
|
236
245
|
/>
|
|
237
246
|
)}
|
|
@@ -294,7 +303,7 @@ function CategoryNode({
|
|
|
294
303
|
() => node.value.shape === "note-tray" && depth === 0,
|
|
295
304
|
)}
|
|
296
305
|
fallback={() => (
|
|
297
|
-
<NodeList nodes={children} depth={depth + 1} />
|
|
306
|
+
<NodeList nodes={children} depth={depth + 1} initiallyCollapsedCategorySlugs={initiallyCollapsedCategorySlugs} />
|
|
298
307
|
)}
|
|
299
308
|
>
|
|
300
309
|
{() => (
|
|
@@ -493,7 +502,7 @@ function LeafNode({
|
|
|
493
502
|
<ConnectorLines
|
|
494
503
|
depth={depth}
|
|
495
504
|
isLast={true}
|
|
496
|
-
widthScale={
|
|
505
|
+
widthScale={CONNECTOR_WIDTH_SCALE}
|
|
497
506
|
topPad={topPad}
|
|
498
507
|
/>
|
|
499
508
|
)}
|
|
@@ -503,7 +512,7 @@ function LeafNode({
|
|
|
503
512
|
<ConnectorLines
|
|
504
513
|
depth={depth}
|
|
505
514
|
isLast={false}
|
|
506
|
-
widthScale={
|
|
515
|
+
widthScale={CONNECTOR_WIDTH_SCALE}
|
|
507
516
|
topPad={topPad}
|
|
508
517
|
/>
|
|
509
518
|
)}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takazudo/zudo-doc",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "zudo-doc framework primitives layer that sits on top of zfb's engine — sidebar, theme, TOC, breadcrumb, layouts, head injection, View Transitions, SSR-skip wrappers (per ADR-003).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"url": "https://github.com/zudolab/zudo-doc/issues"
|
|
16
16
|
},
|
|
17
17
|
"engines": {
|
|
18
|
-
"node": ">=24
|
|
18
|
+
"node": ">=24"
|
|
19
19
|
},
|
|
20
20
|
"keywords": [
|
|
21
21
|
"zudo-doc",
|
|
@@ -206,10 +206,6 @@
|
|
|
206
206
|
"types": "./dist/plugins/zdtp-loader.d.ts",
|
|
207
207
|
"default": "./dist/plugins/zdtp-loader.js"
|
|
208
208
|
},
|
|
209
|
-
"./plugins/optional-peers": {
|
|
210
|
-
"types": "./dist/plugins/optional-peers.d.ts",
|
|
211
|
-
"default": "./dist/plugins/optional-peers.js"
|
|
212
|
-
},
|
|
213
209
|
"./content-admonition": {
|
|
214
210
|
"types": "./dist/content-admonition/index.d.ts",
|
|
215
211
|
"default": "./dist/content-admonition/index.js"
|
|
@@ -738,12 +734,12 @@
|
|
|
738
734
|
"CHANGELOG.md"
|
|
739
735
|
],
|
|
740
736
|
"peerDependencies": {
|
|
741
|
-
"@modelcontextprotocol/sdk": "1.32.
|
|
737
|
+
"@modelcontextprotocol/sdk": "1.32.2",
|
|
742
738
|
"@takazudo/zdtp": "^0.5.2 || ^0.6.0 || ^0.7.0 || ^0.8.0",
|
|
743
|
-
"@takazudo/zfb": "^
|
|
744
|
-
"@takazudo/zfb-md-wasm": "^
|
|
745
|
-
"@takazudo/zfb-runtime": "^
|
|
746
|
-
"@takazudo/zudo-doc-history-server": "^
|
|
739
|
+
"@takazudo/zfb": "^5.1.2",
|
|
740
|
+
"@takazudo/zfb-md-wasm": "^5.1.2",
|
|
741
|
+
"@takazudo/zfb-runtime": "^5.1.2",
|
|
742
|
+
"@takazudo/zudo-doc-history-server": "^8.0.0",
|
|
747
743
|
"diff": "^8.0.0",
|
|
748
744
|
"katex": "^0.18.2 || ^0.19.0",
|
|
749
745
|
"zod": "^4.3.6"
|
|
@@ -781,11 +777,11 @@
|
|
|
781
777
|
"yaml": "^2.9.0"
|
|
782
778
|
},
|
|
783
779
|
"devDependencies": {
|
|
784
|
-
"@modelcontextprotocol/sdk": "1.32.
|
|
780
|
+
"@modelcontextprotocol/sdk": "1.32.2",
|
|
785
781
|
"@takazudo/mdx-formatter": "1.3.0",
|
|
786
|
-
"@takazudo/zfb": "
|
|
787
|
-
"@takazudo/zfb-md-wasm": "
|
|
788
|
-
"@takazudo/zfb-runtime": "
|
|
782
|
+
"@takazudo/zfb": "5.1.2",
|
|
783
|
+
"@takazudo/zfb-md-wasm": "5.1.2",
|
|
784
|
+
"@takazudo/zfb-runtime": "5.1.2",
|
|
789
785
|
"@types/fs-extra": "^11.0.4",
|
|
790
786
|
"@types/minimist": "^1.2.5",
|
|
791
787
|
"@types/node": "^25.3.5",
|
|
@@ -795,7 +791,7 @@
|
|
|
795
791
|
"typescript": "^5.0.0",
|
|
796
792
|
"vitest": "^4.1.0",
|
|
797
793
|
"zod": "^4.3.6",
|
|
798
|
-
"@takazudo/zudo-doc-history-server": "
|
|
794
|
+
"@takazudo/zudo-doc-history-server": "8.1.0"
|
|
799
795
|
},
|
|
800
796
|
"scripts": {
|
|
801
797
|
"gen:search-widget-script": "node scripts/gen-search-widget-script.mjs",
|