wiki-formant 0.4.1 → 0.7.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/dist/react.js ADDED
@@ -0,0 +1,470 @@
1
+ 'use client';
2
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // react.tsx — the collapsible wiki sidebar, and the table of contents inside it.
4
+ //
5
+ // This is the one part of the package that needs React, so it is the one part
6
+ // behind its own subpath: `wiki-formant/react`. React is an OPTIONAL PEER
7
+ // dependency, so a consumer that only wants the taxonomy or the MCP transport
8
+ // still installs a package with no runtime dependencies at all.
9
+ //
10
+ // What is shared here is behaviour, not appearance. Every class name is passed
11
+ // in, because the three wikis style their rails differently and always will;
12
+ // what they should not differ on is what the rail DOES. Icons are passed in for
13
+ // the same reason — and because a component library that hardcodes an icon set
14
+ // makes its consumers install that icon set.
15
+ //
16
+ // Two bugs are fixed here rather than propagated. Both were in the version this
17
+ // was lifted from:
18
+ //
19
+ // 1. `useEffect(() => setOpen(!isMobile), [isMobile])` ran on every matchMedia
20
+ // change, so resizing a window across the breakpoint silently discarded a
21
+ // collapse the reader had chosen. The breakpoint now supplies the DEFAULT
22
+ // only, and never overrides a choice already made.
23
+ // 2. The store started closed and an effect opened it on mount, so every
24
+ // desktop load flashed a closed rail before opening it. SSR cannot read
25
+ // localStorage, so no amount of React fixes this on its own: the answer is
26
+ // `sidebarBootScript`, a blocking inline script that stamps the remembered
27
+ // state on <html> before first paint. The hook keeps that attribute in
28
+ // sync afterwards, so CSS has one source of truth either side of hydration.
29
+ import { resolveSidebarOpen, SIDEBAR_ATTRIBUTE } from './sidebar.js';
30
+ import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
31
+ const readStored = (key) => {
32
+ try {
33
+ const raw = window.localStorage.getItem(key);
34
+ return raw === null ? null : raw === '1';
35
+ }
36
+ catch {
37
+ // Private browsing, or site data blocked. A rail that cannot remember is
38
+ // fine; a rail that throws on load is not.
39
+ return null;
40
+ }
41
+ };
42
+ /**
43
+ * Collapse state for a wiki rail: remembered across loads, defaulted from the
44
+ * viewport only when the reader has never chosen.
45
+ *
46
+ * The reader's choice outranks the breakpoint. Someone who collapses the rail
47
+ * on a laptop and then narrows the window has still collapsed the rail.
48
+ */
49
+ export function useCollapsibleSidebar(options = {}) {
50
+ const { storageKey = 'wiki:sidebar', breakpoint = 1024 } = options;
51
+ // Start open on the server and on the first client paint. SSR has no viewport
52
+ // and no storage, so any other guess is a guaranteed mismatch on some loads.
53
+ const [open, setOpenState] = useState(true);
54
+ const [isMobile, setIsMobile] = useState(false);
55
+ const [ready, setReady] = useState(false);
56
+ // Set as soon as the reader touches the control, so a later viewport change
57
+ // cannot overwrite what they asked for.
58
+ const chosen = useRef(false);
59
+ useEffect(() => {
60
+ const mql = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);
61
+ const stored = readStored(storageKey);
62
+ const apply = (matches) => {
63
+ setIsMobile(matches);
64
+ // The breakpoint supplies a default, never an override. This is bug 1.
65
+ setOpenState(current => resolveSidebarOpen({ chosen: chosen.current, current, stored, isMobile: matches }));
66
+ };
67
+ apply(mql.matches);
68
+ setReady(true);
69
+ const handler = (e) => apply(e.matches);
70
+ mql.addEventListener('change', handler);
71
+ return () => mql.removeEventListener('change', handler);
72
+ }, [storageKey, breakpoint]);
73
+ const setOpen = useCallback((next) => {
74
+ chosen.current = true;
75
+ setOpenState(next);
76
+ try {
77
+ window.localStorage.setItem(storageKey, next ? '1' : '0');
78
+ }
79
+ catch {
80
+ /* storage unavailable — the choice still holds for this page */
81
+ }
82
+ }, [storageKey]);
83
+ const toggle = useCallback(() => setOpen(!open), [open, setOpen]);
84
+ // One source of truth either side of hydration: the boot script writes this
85
+ // attribute before first paint, and from here on the hook owns it.
86
+ useEffect(() => {
87
+ document.documentElement.setAttribute(SIDEBAR_ATTRIBUTE, open ? 'open' : 'closed');
88
+ }, [open]);
89
+ return { open, setOpen, toggle, isMobile, ready };
90
+ }
91
+ // ---- sharing that state across the layout -----------------------------------
92
+ // The rail and the button that toggles it are rarely siblings — the button
93
+ // usually lives in the header — so the state has to span them. A context is the
94
+ // smallest thing that does, and it means a consumer needs no store of its own.
95
+ const SidebarContext = createContext(null);
96
+ /** Wrap the layout that contains both the rail and its toggle. */
97
+ export function SidebarProvider({ children, ...options }) {
98
+ const state = useCollapsibleSidebar(options);
99
+ return createElement(SidebarContext.Provider, { value: state }, children);
100
+ }
101
+ /**
102
+ * The rail's collapse state. Throws outside a provider rather than silently
103
+ * handing back a second, unconnected copy — two sources of truth for one rail
104
+ * is the failure this context exists to prevent.
105
+ */
106
+ export function useSidebar() {
107
+ const ctx = useContext(SidebarContext);
108
+ if (!ctx)
109
+ throw new Error('useSidebar must be used within a <SidebarProvider>');
110
+ return ctx;
111
+ }
112
+ const sameHeadings = (a, b) => a.length === b.length && a.every((h, i) => h.id === b[i].id && h.text === b[i].text);
113
+ /**
114
+ * An "on this page" rail with scroll-spy, read from the rendered article.
115
+ *
116
+ * Both wikis this came from had written the same thing: a debounced
117
+ * MutationObserver to survive content arriving late, an IntersectionObserver to
118
+ * track the section under the viewport top, and the same reference-stability
119
+ * trick so an unrelated DOM mutation (a price ticker, an RSS feed) does not tear
120
+ * the observer down.
121
+ */
122
+ export function TableOfContents({ containerSelector, headings: providedHeadings, resetKey, classNames = {}, icon, label = 'On this page', slug, offsetVar, offsetFallback = 64, indentRem = 0.75, empty = null, }) {
123
+ const [scanned, setScanned] = useState([]);
124
+ const [expanded, setExpanded] = useState(true);
125
+ const [activeId, setActiveId] = useState('');
126
+ const headings = providedHeadings ?? scanned;
127
+ useEffect(() => {
128
+ // Nothing to scan for when the caller already knows the headings.
129
+ if (providedHeadings || !containerSelector)
130
+ return;
131
+ let timer;
132
+ const update = () => {
133
+ clearTimeout(timer);
134
+ // Debounced: block content can arrive in bursts, and re-reading the whole
135
+ // article per mutation is what makes a rail janky.
136
+ timer = setTimeout(() => {
137
+ const root = document.querySelector(containerSelector);
138
+ if (!root)
139
+ return;
140
+ const used = new Set();
141
+ const next = [];
142
+ for (const el of Array.from(root.querySelectorAll('h1, h2, h3'))) {
143
+ const text = el.textContent?.trim() ?? '';
144
+ if (!text)
145
+ continue;
146
+ let id = el.id;
147
+ if (!id) {
148
+ if (!slug)
149
+ continue; // ids are the server's business here
150
+ const base = slug(text) || 'section';
151
+ id = base;
152
+ let n = 2;
153
+ while (used.has(id) || document.getElementById(id))
154
+ id = `${base}-${n++}`;
155
+ el.id = id;
156
+ }
157
+ used.add(id);
158
+ next.push({ id, text, level: Number(el.tagName[1]) });
159
+ }
160
+ // Keep the same array reference when nothing changed, so the scroll-spy
161
+ // effect below is not torn down by an unrelated mutation.
162
+ setScanned(prev => (sameHeadings(prev, next) ? prev : next));
163
+ }, 200);
164
+ };
165
+ update();
166
+ const root = document.querySelector(containerSelector);
167
+ const observer = new MutationObserver(update);
168
+ if (root)
169
+ observer.observe(root, { childList: true, subtree: true });
170
+ return () => {
171
+ clearTimeout(timer);
172
+ observer.disconnect();
173
+ };
174
+ }, [containerSelector, resetKey, slug, providedHeadings]);
175
+ useEffect(() => {
176
+ if (!headings.length)
177
+ return;
178
+ const order = new Map(headings.map((h, i) => [h.id, i]));
179
+ const visible = new Set();
180
+ const styles = getComputedStyle(document.documentElement);
181
+ const rem = parseFloat(styles.fontSize) || 16;
182
+ const raw = offsetVar ? styles.getPropertyValue(offsetVar).trim() : '';
183
+ // A custom property may be in rem or px; both appear across these wikis.
184
+ const parsed = parseFloat(raw);
185
+ const offset = !raw || Number.isNaN(parsed)
186
+ ? offsetFallback
187
+ : raw.endsWith('rem') ? parsed * rem : parsed;
188
+ const topOffset = Math.round(offset + rem * 0.5);
189
+ const observer = new IntersectionObserver(entries => {
190
+ for (const e of entries) {
191
+ if (e.isIntersecting)
192
+ visible.add(e.target.id);
193
+ else
194
+ visible.delete(e.target.id);
195
+ }
196
+ if (!visible.size)
197
+ return;
198
+ // The earliest visible heading in document order is the section the
199
+ // reader is in, not whichever entry fired last.
200
+ let bestId = '';
201
+ let bestIdx = Infinity;
202
+ for (const id of visible) {
203
+ const idx = order.get(id) ?? Infinity;
204
+ if (idx < bestIdx) {
205
+ bestIdx = idx;
206
+ bestId = id;
207
+ }
208
+ }
209
+ if (bestId)
210
+ setActiveId(bestId);
211
+ }, { rootMargin: `-${topOffset}px 0px -75% 0px`, threshold: 0 });
212
+ for (const h of headings) {
213
+ const el = document.getElementById(h.id);
214
+ if (el)
215
+ observer.observe(el);
216
+ }
217
+ return () => observer.disconnect();
218
+ }, [headings, offsetVar, offsetFallback]);
219
+ // Derived, not seeded from an effect: until the observer reports a section,
220
+ // the first heading is the one the reader is in.
221
+ const currentId = useMemo(() => activeId || headings[0]?.id, [activeId, headings]);
222
+ if (!headings.length)
223
+ return _jsx(_Fragment, { children: empty });
224
+ return (_jsxs("div", { className: classNames.root, children: [_jsxs("button", { type: "button", onClick: () => setExpanded(v => !v), className: classNames.button, "aria-expanded": expanded, children: [icon?.(expanded), _jsx("span", { className: classNames.label, children: label })] }), expanded && (_jsx("nav", { className: classNames.list, "aria-label": label, children: headings.map(h => (_jsx("button", { type: "button", onClick: () => {
225
+ setActiveId(h.id);
226
+ document.getElementById(h.id)?.scrollIntoView({ behavior: 'smooth' });
227
+ }, className: currentId === h.id
228
+ ? [classNames.item, classNames.itemActive].filter(Boolean).join(' ')
229
+ : classNames.item, "aria-current": currentId === h.id ? 'location' : undefined, style: { paddingLeft: `${(h.level - 1) * indentRem}rem` }, children: h.text }, h.id))) }))] }));
230
+ }
231
+ // ---- outside clicks ---------------------------------------------------------
232
+ /**
233
+ * Calls `onClose` on any mousedown outside the returned ref's element.
234
+ *
235
+ * The `offsetParent` check is the whole reason this is shared. All three wikis
236
+ * had written this hook; only one had that line, and without it a container
237
+ * that is `display: none` at the current breakpoint still claims outside-clicks
238
+ * — so a tap anywhere on a phone dismisses a popover the reader is looking at,
239
+ * because the hidden desktop copy of it answered first.
240
+ *
241
+ * `onClose` is an effect dependency: wrap it in `useCallback` or hoist it, or
242
+ * the listener is torn down and rebuilt on every render.
243
+ */
244
+ export function useClickOutside(onClose) {
245
+ const ref = useRef(null);
246
+ useEffect(() => {
247
+ const handler = (e) => {
248
+ const el = ref.current;
249
+ if (el && el.offsetParent !== null && !el.contains(e.target))
250
+ onClose();
251
+ };
252
+ document.addEventListener('mousedown', handler);
253
+ return () => document.removeEventListener('mousedown', handler);
254
+ }, [onClose]);
255
+ return ref;
256
+ }
257
+ /**
258
+ * The debounced-search state machine behind every typeahead on these sites.
259
+ *
260
+ * Five surfaces across the three wikis had three implementations, and no two
261
+ * agreed on what a search field does. This is their union, because each had a
262
+ * piece the others were missing:
263
+ *
264
+ * - a **request-id guard**, so a slow response cannot paint over a newer one.
265
+ * Two of the five surfaces had no guard at all: type fast enough and the
266
+ * results you are reading are for a query you have already replaced.
267
+ * - **keyboard navigation**. Two surfaces had none — the header dropdown of a
268
+ * wiki whose search is its primary navigation was mouse-only.
269
+ * - **abort and a per-query cache**, which only the third had. The cache is
270
+ * per mount, not module-level, so a stale result cannot outlive the page.
271
+ *
272
+ * The debounce is inlined rather than pulled from a package: this one is nine
273
+ * lines, and the point of the package is to have no runtime dependencies.
274
+ */
275
+ export function useTypeahead({ fetch, onPick, onEscape, delay = 200, minLength = 1, cache = true, }) {
276
+ const [query, setQuery] = useState('');
277
+ const [debounced, setDebounced] = useState('');
278
+ const [items, setItems] = useState([]);
279
+ const [highlight, setHighlight] = useState(0);
280
+ const [isSearching, setIsSearching] = useState(false);
281
+ const requestId = useRef(0);
282
+ const cached = useRef(new Map());
283
+ useEffect(() => {
284
+ const timer = setTimeout(() => setDebounced(query), delay);
285
+ return () => clearTimeout(timer);
286
+ }, [query, delay]);
287
+ useEffect(() => {
288
+ const q = debounced.trim();
289
+ if (q.length < minLength) {
290
+ // Bump the id so an in-flight request for a longer query cannot land
291
+ // after the field has been cleared.
292
+ requestId.current++;
293
+ setItems([]);
294
+ setIsSearching(false);
295
+ return;
296
+ }
297
+ const hit = cache ? cached.current.get(q) : undefined;
298
+ if (hit) {
299
+ requestId.current++;
300
+ setItems(hit);
301
+ setHighlight(0);
302
+ setIsSearching(false);
303
+ return;
304
+ }
305
+ const id = ++requestId.current;
306
+ const controller = new AbortController();
307
+ setIsSearching(true);
308
+ fetch(q, controller.signal).then(rows => {
309
+ if (id !== requestId.current)
310
+ return;
311
+ if (cache)
312
+ cached.current.set(q, rows);
313
+ setItems(rows);
314
+ setHighlight(0);
315
+ setIsSearching(false);
316
+ }, () => {
317
+ // An abort is the expected path for every superseded query, so it must
318
+ // not surface as an error. A real failure reads as "no results".
319
+ if (id !== requestId.current)
320
+ return;
321
+ setItems([]);
322
+ setIsSearching(false);
323
+ });
324
+ return () => controller.abort();
325
+ }, [debounced, fetch, minLength, cache]);
326
+ const clearItems = useCallback(() => setItems([]), []);
327
+ const reset = useCallback(() => {
328
+ requestId.current++;
329
+ setQuery('');
330
+ setDebounced('');
331
+ setItems([]);
332
+ setHighlight(0);
333
+ setIsSearching(false);
334
+ }, []);
335
+ const onKeyDown = (e) => {
336
+ if (e.key === 'Escape') {
337
+ setItems([]);
338
+ onEscape?.();
339
+ return;
340
+ }
341
+ if (!items.length)
342
+ return;
343
+ if (e.key === 'ArrowDown') {
344
+ e.preventDefault();
345
+ setHighlight(h => (h + 1) % items.length);
346
+ }
347
+ else if (e.key === 'ArrowUp') {
348
+ e.preventDefault();
349
+ setHighlight(h => (h - 1 + items.length) % items.length);
350
+ }
351
+ else if (e.key === 'Enter') {
352
+ e.preventDefault();
353
+ const target = items[highlight];
354
+ if (target)
355
+ onPick(target);
356
+ }
357
+ };
358
+ return {
359
+ query,
360
+ setQuery,
361
+ debouncedQuery: debounced,
362
+ items,
363
+ highlight,
364
+ setHighlight,
365
+ isSearching,
366
+ onKeyDown,
367
+ clearItems,
368
+ reset,
369
+ };
370
+ }
371
+ /**
372
+ * Wikipedia-style hover cards over a rendered article.
373
+ *
374
+ * One delegated listener on `document` rather than a component per link, which
375
+ * is what makes this viable over an article holding hundreds of anchors. Two
376
+ * wikis had written the same ~90 lines: the same 350ms intent delay, the same
377
+ * 200ms grace so the cursor can cross the gap into the card, the same
378
+ * viewport-clamp arithmetic, the same per-href cache. They differed in exactly
379
+ * three things, and those three are the options above.
380
+ */
381
+ export function useLinkPreview({ eligible, fetch, cardClassName = 'link-preview-card', cardWidth = 320, cardHeight = 240, showDelay = 350, hideDelay = 200, margin = 8, }) {
382
+ const [card, setCard] = useState(null);
383
+ const cache = useRef(new Map());
384
+ const showTimer = useRef(undefined);
385
+ const hideTimer = useRef(undefined);
386
+ const activeHref = useRef(null);
387
+ // The handlers below are rebuilt whenever these change, so they are read from
388
+ // a ref instead — a caller passing an inline `eligible` arrow would otherwise
389
+ // tear down and rebind the document listeners on every render.
390
+ const opts = useRef({ eligible, fetch });
391
+ opts.current = { eligible, fetch };
392
+ const hide = useCallback(() => {
393
+ hideTimer.current = setTimeout(() => {
394
+ activeHref.current = null;
395
+ setCard(null);
396
+ }, hideDelay);
397
+ }, [hideDelay]);
398
+ useEffect(() => {
399
+ const clearTimers = () => {
400
+ clearTimeout(showTimer.current);
401
+ clearTimeout(hideTimer.current);
402
+ };
403
+ const onOver = (e) => {
404
+ const anchor = e.target?.closest('a');
405
+ if (!anchor || !opts.current.eligible(anchor))
406
+ return;
407
+ const href = (anchor.getAttribute('href') ?? '').replace(/[#?].*$/, '');
408
+ if (!href)
409
+ return;
410
+ // Re-entering the link the card is already for: cancel the pending hide
411
+ // rather than refetching and re-placing an identical card.
412
+ if (activeHref.current === href) {
413
+ clearTimeout(hideTimer.current);
414
+ return;
415
+ }
416
+ clearTimers();
417
+ activeHref.current = href;
418
+ showTimer.current = setTimeout(async () => {
419
+ let data = cache.current.get(href);
420
+ if (data === undefined) {
421
+ try {
422
+ data = await opts.current.fetch(href);
423
+ }
424
+ catch {
425
+ data = null;
426
+ }
427
+ cache.current.set(href, data);
428
+ }
429
+ // The cursor may have moved on during the fetch.
430
+ if (activeHref.current !== href || data === null)
431
+ return;
432
+ const rect = anchor.getBoundingClientRect();
433
+ const above = rect.bottom + cardHeight > window.innerHeight && rect.top > cardHeight;
434
+ setCard({
435
+ data,
436
+ left: Math.max(margin, Math.min(rect.left, window.innerWidth - cardWidth - margin)),
437
+ top: above ? rect.top - margin : rect.bottom + margin,
438
+ above,
439
+ });
440
+ }, showDelay);
441
+ };
442
+ const onOut = (e) => {
443
+ // Moving into the card itself is not leaving.
444
+ if (e.relatedTarget?.closest(`.${cardClassName}`))
445
+ return;
446
+ clearTimers();
447
+ hide();
448
+ };
449
+ document.addEventListener('mouseover', onOver);
450
+ document.addEventListener('mouseout', onOut);
451
+ return () => {
452
+ clearTimers();
453
+ document.removeEventListener('mouseover', onOver);
454
+ document.removeEventListener('mouseout', onOut);
455
+ };
456
+ }, [cardClassName, cardWidth, cardHeight, showDelay, margin, hide]);
457
+ return {
458
+ preview: card?.data ?? null,
459
+ cardProps: {
460
+ style: {
461
+ left: card?.left,
462
+ top: card?.top,
463
+ transform: card?.above ? 'translateY(-100%)' : undefined,
464
+ },
465
+ onMouseEnter: () => clearTimeout(hideTimer.current),
466
+ onMouseLeave: hide,
467
+ },
468
+ };
469
+ }
470
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Whether the rail should be open, given what is known.
3
+ *
4
+ * The whole of both bug fixes lives in these three lines, so it is a named
5
+ * function with tests rather than a condition buried in an effect:
6
+ *
7
+ * - a choice the reader has made outranks everything, forever;
8
+ * - a remembered choice outranks the viewport;
9
+ * - the viewport is consulted only when nothing else has an opinion.
10
+ */
11
+ export declare function resolveSidebarOpen({ chosen, current, stored, isMobile, }: {
12
+ /** Has the reader touched the control this session? */
13
+ chosen: boolean;
14
+ /** What the rail is showing now. */
15
+ current: boolean;
16
+ /** What storage remembers, or null if it has never been written. */
17
+ stored: boolean | null;
18
+ /** Is the viewport below the breakpoint? */
19
+ isMobile: boolean;
20
+ }): boolean;
21
+ /** The attribute the boot script and the hook both write to `<html>`. */
22
+ export declare const SIDEBAR_ATTRIBUTE = "data-sidebar";
23
+ /**
24
+ * A blocking inline script for the document head, so the rail's first paint
25
+ * already matches what the reader last chose.
26
+ *
27
+ * Server rendering cannot see `localStorage`, so a remembered-closed rail would
28
+ * otherwise paint open and then animate shut on every single load. This is the
29
+ * same trick a theme switcher uses for exactly the same reason. Stamp it with
30
+ * `dangerouslySetInnerHTML` in `<head>`, and style against
31
+ * `html[data-sidebar='closed']` rather than against the React class alone.
32
+ *
33
+ * It is wrapped in try/catch because a browser with site data blocked throws on
34
+ * the read, and a rail that cannot remember is much better than a page that
35
+ * dies before it paints.
36
+ */
37
+ export declare function sidebarBootScript(storageKey?: string, breakpoint?: number): string;
38
+ //# sourceMappingURL=sidebar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidebar.d.ts","sourceRoot":"","sources":["../src/sidebar.ts"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,OAAO,EACP,MAAM,EACN,QAAQ,GACT,EAAE;IACD,uDAAuD;IACvD,MAAM,EAAE,OAAO,CAAC;IAChB,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,oEAAoE;IACpE,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IACvB,4CAA4C;IAC5C,QAAQ,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CAGV;AAED,yEAAyE;AACzE,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,SAAiB,EAAE,UAAU,SAAO,GAAG,MAAM,CAMxF"}
@@ -0,0 +1,43 @@
1
+ // sidebar.ts — the framework-free half of the collapsible wiki rail.
2
+ //
3
+ // Separate from `react.tsx` because that file carries `'use client'`, which
4
+ // makes every one of its exports a client reference — and `sidebarBootScript`
5
+ // has to be callable from a server component to reach the document head.
6
+ // Splitting it also lets the collapse rule be tested without a DOM.
7
+ /**
8
+ * Whether the rail should be open, given what is known.
9
+ *
10
+ * The whole of both bug fixes lives in these three lines, so it is a named
11
+ * function with tests rather than a condition buried in an effect:
12
+ *
13
+ * - a choice the reader has made outranks everything, forever;
14
+ * - a remembered choice outranks the viewport;
15
+ * - the viewport is consulted only when nothing else has an opinion.
16
+ */
17
+ export function resolveSidebarOpen({ chosen, current, stored, isMobile, }) {
18
+ if (chosen)
19
+ return current;
20
+ return stored ?? !isMobile;
21
+ }
22
+ /** The attribute the boot script and the hook both write to `<html>`. */
23
+ export const SIDEBAR_ATTRIBUTE = 'data-sidebar';
24
+ /**
25
+ * A blocking inline script for the document head, so the rail's first paint
26
+ * already matches what the reader last chose.
27
+ *
28
+ * Server rendering cannot see `localStorage`, so a remembered-closed rail would
29
+ * otherwise paint open and then animate shut on every single load. This is the
30
+ * same trick a theme switcher uses for exactly the same reason. Stamp it with
31
+ * `dangerouslySetInnerHTML` in `<head>`, and style against
32
+ * `html[data-sidebar='closed']` rather than against the React class alone.
33
+ *
34
+ * It is wrapped in try/catch because a browser with site data blocked throws on
35
+ * the read, and a rail that cannot remember is much better than a page that
36
+ * dies before it paints.
37
+ */
38
+ export function sidebarBootScript(storageKey = 'wiki:sidebar', breakpoint = 1024) {
39
+ return (`(function(){try{var v=localStorage.getItem(${JSON.stringify(storageKey)});` +
40
+ `var o=v===null?!window.matchMedia("(max-width: ${breakpoint - 1}px)").matches:v==="1";` +
41
+ `document.documentElement.setAttribute(${JSON.stringify(SIDEBAR_ATTRIBUTE)},o?"open":"closed");}catch(e){}})()`);
42
+ }
43
+ //# sourceMappingURL=sidebar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidebar.js","sourceRoot":"","sources":["../src/sidebar.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,yEAAyE;AACzE,oEAAoE;AAEpE;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAC,EACjC,MAAM,EACN,OAAO,EACP,MAAM,EACN,QAAQ,GAUT;IACC,IAAI,MAAM;QAAE,OAAO,OAAO,CAAC;IAC3B,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC;AAC7B,CAAC;AAED,yEAAyE;AACzE,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAU,GAAG,cAAc,EAAE,UAAU,GAAG,IAAI;IAC9E,OAAO,CACL,8CAA8C,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI;QAC5E,kDAAkD,UAAU,GAAG,CAAC,wBAAwB;QACxF,yCAAyC,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,qCAAqC,CAChH,CAAC;AACJ,CAAC"}
@@ -55,6 +55,39 @@ export interface MetadataRow extends MetadataKeyDefinition {
55
55
  value: string;
56
56
  href?: string;
57
57
  }
58
+ /**
59
+ * One pressable control on a browsing axis, with its destination already built.
60
+ *
61
+ * The three wikis each rendered the same taxonomy output their own way and had
62
+ * already begun to disagree about what the row CONTAINS rather than how it
63
+ * looks: one shipped no A–Z index at all, one had no way back to the unfiltered
64
+ * set, and two marked an active chip with `aria-pressed` on an `<a>` — which is
65
+ * not a toggle button and does not take that attribute. The decisions are here;
66
+ * the markup stays theirs.
67
+ */
68
+ export interface Control {
69
+ /** The value this control selects. Empty on the reset control. */
70
+ value: string;
71
+ /** What to render. Same as `value` except on the reset control. */
72
+ label: string;
73
+ /** Pages reachable through this control, counted under the sibling state. */
74
+ count: number;
75
+ /** Built through the one `href` builder, so no control drops a sibling axis. */
76
+ href: string;
77
+ active: boolean;
78
+ /**
79
+ * True on the single control that clears its axis. It leads the row rather
80
+ * than being appended, so a consumer that maps the array cannot ship a
81
+ * narrowed view with nothing to press to widen it.
82
+ */
83
+ reset?: boolean;
84
+ }
85
+ /** One facet's controls, ready to render as a labelled row. */
86
+ export interface FacetControlGroup {
87
+ key: string;
88
+ label: string;
89
+ options: Control[];
90
+ }
58
91
  /** State a category URL can carry. `href` receives this and owns the format. */
59
92
  export interface CategoryState {
60
93
  sort?: string;
@@ -99,6 +132,25 @@ export interface Taxonomy {
99
132
  filterPages: <T extends FacetablePage>(pages: T[], filters: FacetFilters, letter?: string) => T[];
100
133
  buildFacets: <T extends FacetablePage>(tagPath: string, pages: T[], filters: FacetFilters, letter?: string) => Facet[];
101
134
  alphaIndex: <T extends FacetablePage>(pages: T[], filters: FacetFilters) => FacetValue[];
135
+ /**
136
+ * `buildFacets` with every chip's destination and active state resolved.
137
+ * Prefer this to `buildFacets` in a view: the href arithmetic is the part
138
+ * that was being rebuilt, and getting it wrong drops the reader's letter or
139
+ * sort on the next press.
140
+ */
141
+ facetControls: <T extends FacetablePage>(tagPath: string, pages: T[], state?: CategoryState) => FacetControlGroup[];
142
+ /**
143
+ * The A–Z row, reset control first, or `[]` for a set that still fits on a
144
+ * screen. Folds in the `needsAlphaIndex` threshold so a consumer cannot show
145
+ * an index it did not need or, as one did, omit one it did.
146
+ */
147
+ alphaControls: <T extends FacetablePage>(tagPath: string, pages: T[], state?: CategoryState) => Control[];
148
+ /**
149
+ * A `?letter=` narrowed to a bucket that exists. A letter with no pages under
150
+ * the active filters would otherwise empty the grid with no control marked to
151
+ * explain it.
152
+ */
153
+ resolveLetter: <T extends FacetablePage>(pages: T[], filters: FacetFilters, letter?: string) => string | undefined;
102
154
  /** A page's populated metadata keys as facts-panel rows, facet values linked. */
103
155
  metadataRows: (tagPath: string, page: {
104
156
  metadata?: unknown;