@waveso/docs 0.10.0 → 0.12.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 +655 -0
- package/README.md +80 -3
- package/dist/code-frame.d.ts +28 -1
- package/dist/code-frame.js +36 -1
- package/dist/frontmatter.d.ts +5 -0
- package/dist/frontmatter.js +5 -0
- package/dist/nav-order.d.ts +14 -1
- package/dist/nav-order.js +15 -1
- package/dist/next.js +26 -1
- package/dist/plugins/rehype-code-frame.js +41 -11
- package/dist/react/explore.d.ts +53 -0
- package/dist/react/explore.js +86 -0
- package/dist/react/markdown-components.js +14 -0
- package/dist/react/nav.js +14 -11
- package/dist/react/search-dialog.js +87 -15
- package/dist/react/shell-labels.d.ts +8 -1
- package/dist/react/shell-labels.js +1 -0
- package/dist/styles.css +1144 -168
- package/dist/types.d.ts +31 -1
- package/package.json +5 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { docsError } from "../docs-error.js";
|
|
3
3
|
import { mergeSearchOptions } from "../search-options.js";
|
|
4
|
-
import { useCallback, useEffect, useId, useRef, useState } from "react";
|
|
4
|
+
import { Fragment, useCallback, useEffect, useId, useRef, useState } from "react";
|
|
5
5
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { createPortal } from "react-dom";
|
|
7
7
|
//#region src/react/search-dialog.tsx
|
|
@@ -51,7 +51,7 @@ function SearchDialog({ indexUrl, navigate, Link, triggerLabel = "Search", place
|
|
|
51
51
|
* not itself cause a render.
|
|
52
52
|
*/
|
|
53
53
|
const movedByKeyboard = useRef(false);
|
|
54
|
-
const [activeIndex, setActiveIndex] = useState(
|
|
54
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
55
55
|
const [status, setStatus] = useState("idle");
|
|
56
56
|
/**
|
|
57
57
|
* The trigger's shortcut, split rather than held as one string.
|
|
@@ -166,13 +166,13 @@ function SearchDialog({ indexUrl, navigate, Link, triggerLabel = "Search", place
|
|
|
166
166
|
if (isOpen) return;
|
|
167
167
|
setQuery("");
|
|
168
168
|
setHits([]);
|
|
169
|
-
setActiveIndex(
|
|
169
|
+
setActiveIndex(-1);
|
|
170
170
|
}, [isOpen]);
|
|
171
171
|
useEffect(() => {
|
|
172
172
|
const trimmed = query.trim();
|
|
173
173
|
if (trimmed.length < minQueryLength) {
|
|
174
174
|
setHits([]);
|
|
175
|
-
setActiveIndex(
|
|
175
|
+
setActiveIndex(-1);
|
|
176
176
|
setVisibleCount(pageSize);
|
|
177
177
|
return;
|
|
178
178
|
}
|
|
@@ -182,7 +182,7 @@ function SearchDialog({ indexUrl, navigate, Link, triggerLabel = "Search", place
|
|
|
182
182
|
if (isCancelled) return;
|
|
183
183
|
setStatus("ready");
|
|
184
184
|
setHits(index.search(trimmed).map(toSearchHit).filter(isSearchHit));
|
|
185
|
-
setActiveIndex(
|
|
185
|
+
setActiveIndex(-1);
|
|
186
186
|
setVisibleCount(pageSize);
|
|
187
187
|
}, () => {
|
|
188
188
|
if (!isCancelled) setStatus("error");
|
|
@@ -261,7 +261,10 @@ function SearchDialog({ indexUrl, navigate, Link, triggerLabel = "Search", place
|
|
|
261
261
|
event.preventDefault();
|
|
262
262
|
const delta = event.key === "ArrowDown" ? 1 : -1;
|
|
263
263
|
movedByKeyboard.current = true;
|
|
264
|
-
setActiveIndex((index) =>
|
|
264
|
+
setActiveIndex((index) => {
|
|
265
|
+
if (index < 0) return delta > 0 ? 0 : hits.length - 1;
|
|
266
|
+
return (index + delta + hits.length) % hits.length;
|
|
267
|
+
});
|
|
265
268
|
return;
|
|
266
269
|
}
|
|
267
270
|
if (event.key === "Enter") {
|
|
@@ -446,13 +449,16 @@ function SearchResultOption({ hit, id, isActive, setSize, posInSet, onActivate,
|
|
|
446
449
|
event.preventDefault();
|
|
447
450
|
onSelect(hit);
|
|
448
451
|
}
|
|
449
|
-
const body = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
450
|
-
className: "wave-docs-search-result-
|
|
451
|
-
children:
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
452
|
+
const body = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ResultIcon, { section: hit.href.includes("#") }), /* @__PURE__ */ jsxs("span", {
|
|
453
|
+
className: "wave-docs-search-result-text",
|
|
454
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
455
|
+
className: "wave-docs-search-result-heading",
|
|
456
|
+
children: hit.heading
|
|
457
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
458
|
+
className: "wave-docs-search-result-location",
|
|
459
|
+
"aria-hidden": "true",
|
|
460
|
+
children: toDisplaySegments(hit.href).map((segment, index, all) => /* @__PURE__ */ jsxs(Fragment, { children: [index === 0 ? null : /* @__PURE__ */ jsx(PathChevron, {}), segment] }, all.slice(0, index + 1).join("/")))
|
|
461
|
+
})]
|
|
456
462
|
})] });
|
|
457
463
|
return /* @__PURE__ */ jsx("div", {
|
|
458
464
|
id,
|
|
@@ -599,9 +605,10 @@ function isSearchHit(hit) {
|
|
|
599
605
|
* Display only. `hit.href` keeps the anchor, so the link still deep-links to
|
|
600
606
|
* the section — that is the whole point of section-scoped records.
|
|
601
607
|
*/
|
|
602
|
-
function
|
|
608
|
+
function toDisplaySegments(href) {
|
|
603
609
|
const hash = href.indexOf("#");
|
|
604
|
-
|
|
610
|
+
const segments = (hash === -1 ? href : href.slice(0, hash)).split("/").filter(Boolean);
|
|
611
|
+
return segments.length === 0 ? ["/"] : segments;
|
|
605
612
|
}
|
|
606
613
|
/**
|
|
607
614
|
* What a result is called when it is read aloud.
|
|
@@ -618,6 +625,71 @@ function toDisplayPath(href) {
|
|
|
618
625
|
* A page's own record carries `heading === title` and no ancestors, so its name
|
|
619
626
|
* is the heading alone — "Styling, Styling" is not a path, it is a stutter.
|
|
620
627
|
*/
|
|
628
|
+
/**
|
|
629
|
+
* A page or a section, as one Lucide glyph.
|
|
630
|
+
*
|
|
631
|
+
* ⚠️ THE PATHS ARE WRITTEN OUT RATHER THAN IMPORTED FROM THE SIDEBAR, WHICH
|
|
632
|
+
* HAS THE SAME PAGE ICON. `NAV_ICON_PATHS` lives in a different client
|
|
633
|
+
* component, and importing across the two would pull the whole navigation
|
|
634
|
+
* tree into the search dialog's bundle to reuse one string. The duplication is
|
|
635
|
+
* two path strings; the alternative is kilobytes.
|
|
636
|
+
*/
|
|
637
|
+
const RESULT_ICON_PATHS = {
|
|
638
|
+
page: ["M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", "M14 2v4a2 2 0 0 0 2 2h4"],
|
|
639
|
+
section: [
|
|
640
|
+
"M4 9h16",
|
|
641
|
+
"M4 15h16",
|
|
642
|
+
"m10 3-2 18",
|
|
643
|
+
"m16 3-2 18"
|
|
644
|
+
]
|
|
645
|
+
};
|
|
646
|
+
/**
|
|
647
|
+
* The separator between two route segments.
|
|
648
|
+
*
|
|
649
|
+
* The same path the sidebar's group toggle and the pager draw, at the same
|
|
650
|
+
* `1rem` they draw it. Decorative, and inside a span that is already
|
|
651
|
+
* `aria-hidden` — the route is announced as words by `spokenName`, never read
|
|
652
|
+
* out as punctuation.
|
|
653
|
+
*
|
|
654
|
+
* ⚠️ THE `viewBox` IS CROPPED HORIZONTALLY AND THAT IS NOT A DIFFERENT GLYPH.
|
|
655
|
+
* `d` is Lucide's chevron unchanged and `8 0 8 24` keeps its vertical grid, so
|
|
656
|
+
* the stroke ratio and the ink are exactly what the sidebar and the pager
|
|
657
|
+
* paint. What it drops is the padding either side that the path never reaches:
|
|
658
|
+
* on the full grid a 24-unit box held six units of chevron, so five-sixths of
|
|
659
|
+
* this element was air and the stylesheet had to subtract it back with
|
|
660
|
+
* negative margins. Cropped, the box is the ink and the spacing beside it is
|
|
661
|
+
* one positive value. See `.wave-docs-search-result-sep`.
|
|
662
|
+
*/
|
|
663
|
+
function PathChevron() {
|
|
664
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
665
|
+
className: "wave-docs-search-result-sep",
|
|
666
|
+
"aria-hidden": "true",
|
|
667
|
+
focusable: "false",
|
|
668
|
+
viewBox: "8 5 8 14",
|
|
669
|
+
fill: "none",
|
|
670
|
+
stroke: "currentColor",
|
|
671
|
+
strokeWidth: "2",
|
|
672
|
+
strokeLinecap: "round",
|
|
673
|
+
strokeLinejoin: "round",
|
|
674
|
+
children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
function ResultIcon({ section }) {
|
|
678
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
679
|
+
className: "wave-docs-search-result-icon",
|
|
680
|
+
"aria-hidden": "true",
|
|
681
|
+
focusable: "false",
|
|
682
|
+
viewBox: "0 0 24 24",
|
|
683
|
+
width: "16",
|
|
684
|
+
height: "16",
|
|
685
|
+
fill: "none",
|
|
686
|
+
stroke: "currentColor",
|
|
687
|
+
strokeWidth: "2",
|
|
688
|
+
strokeLinecap: "round",
|
|
689
|
+
strokeLinejoin: "round",
|
|
690
|
+
children: RESULT_ICON_PATHS[section ? "section" : "page"].map((d) => /* @__PURE__ */ jsx("path", { d }, d))
|
|
691
|
+
});
|
|
692
|
+
}
|
|
621
693
|
function spokenName(hit) {
|
|
622
694
|
if (hit.ancestors.length === 0 && hit.heading === hit.title) return hit.heading;
|
|
623
695
|
return [
|
|
@@ -76,6 +76,13 @@ interface DocsLabels {
|
|
|
76
76
|
* The direction, not the destination: the page's own title is the name, and
|
|
77
77
|
* these two words are what say which way it lies.
|
|
78
78
|
*/
|
|
79
|
+
/**
|
|
80
|
+
* The "where to go next" block's heading. Defaults to `'Where to go next'`.
|
|
81
|
+
*
|
|
82
|
+
* A page opts into that block with `explore` in its frontmatter; this is the one
|
|
83
|
+
* string the package supplies for it, and the questions are the author's.
|
|
84
|
+
*/
|
|
85
|
+
explore?: string | undefined;
|
|
79
86
|
previousPage?: string | undefined;
|
|
80
87
|
/** The same for the next page. Defaults to `'Next'`. */
|
|
81
88
|
nextPage?: string | undefined;
|
|
@@ -154,7 +161,7 @@ interface DocsLabels {
|
|
|
154
161
|
* failure being guarded against is a key that is declared, documented and never
|
|
155
162
|
* read, which type-checks perfectly.
|
|
156
163
|
*/
|
|
157
|
-
declare const DOCS_LABEL_KEYS: readonly ["nav", "openNav", "closeNav", "skipToContent", "expandGroup", "collapseGroup", "toc", "backToTop", "externalLink", "table", "calloutNote", "calloutTip", "calloutImportant", "calloutWarning", "calloutCaution", "youtubeTitle", "youtubePlay", "youtubeHide", "copyCode", "copyCodeFrom", "previousPage", "nextPage", "pagination", "copied", "copyFailed"];
|
|
164
|
+
declare const DOCS_LABEL_KEYS: readonly ["nav", "openNav", "closeNav", "skipToContent", "expandGroup", "collapseGroup", "toc", "backToTop", "externalLink", "table", "calloutNote", "calloutTip", "calloutImportant", "calloutWarning", "calloutCaution", "youtubeTitle", "youtubePlay", "youtubeHide", "copyCode", "copyCodeFrom", "explore", "previousPage", "nextPage", "pagination", "copied", "copyFailed"];
|
|
158
165
|
/** The four the shell renders itself, resolved centrally. */
|
|
159
166
|
type ShellLabelKey = 'nav' | 'openNav' | 'closeNav' | 'skipToContent';
|
|
160
167
|
/**
|