@myst-theme/site 0.13.3 → 0.13.5
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/package.json +9 -12
- package/src/components/Bibliography.tsx +5 -5
- package/src/components/ContentBlocks.tsx +8 -3
- package/src/components/DocumentOutline.tsx +5 -2
- package/src/components/Navigation/PrimarySidebar.tsx +1 -1
- package/src/components/Navigation/Search.tsx +69 -25
- package/src/pages/Article.tsx +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myst-theme/site",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -21,25 +21,22 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@headlessui/react": "^1.7.15",
|
|
23
23
|
"@heroicons/react": "^2.0.18",
|
|
24
|
-
"@myst-theme/common": "^0.13.
|
|
25
|
-
"@myst-theme/diagrams": "^0.13.
|
|
26
|
-
"@myst-theme/frontmatter": "^0.13.
|
|
27
|
-
"@myst-theme/jupyter": "^0.13.
|
|
28
|
-
"@myst-theme/providers": "^0.13.
|
|
29
|
-
"@myst-theme/search": "^0.13.
|
|
24
|
+
"@myst-theme/common": "^0.13.5",
|
|
25
|
+
"@myst-theme/diagrams": "^0.13.5",
|
|
26
|
+
"@myst-theme/frontmatter": "^0.13.5",
|
|
27
|
+
"@myst-theme/jupyter": "^0.13.5",
|
|
28
|
+
"@myst-theme/providers": "^0.13.5",
|
|
29
|
+
"@myst-theme/search": "^0.13.5",
|
|
30
30
|
"@radix-ui/react-collapsible": "^1.0.3",
|
|
31
31
|
"@radix-ui/react-dialog": "^1.0.3",
|
|
32
|
-
"@radix-ui/react-radio-group": "^1.2.0",
|
|
33
|
-
"@radix-ui/react-roving-focus": "^1.1.0",
|
|
34
|
-
"@radix-ui/react-slot": "^1.1.0",
|
|
35
32
|
"@radix-ui/react-visually-hidden": "^1.1.0",
|
|
36
33
|
"classnames": "^2.3.2",
|
|
37
34
|
"lodash.throttle": "^4.1.1",
|
|
38
35
|
"myst-common": "^1.7.3",
|
|
39
36
|
"myst-config": "^1.7.3",
|
|
40
|
-
"myst-demo": "^0.13.
|
|
37
|
+
"myst-demo": "^0.13.5",
|
|
41
38
|
"myst-spec-ext": "^1.7.3",
|
|
42
|
-
"myst-to-react": "^0.13.
|
|
39
|
+
"myst-to-react": "^0.13.5",
|
|
43
40
|
"nbtx": "^0.2.3",
|
|
44
41
|
"node-cache": "^5.1.2",
|
|
45
42
|
"node-fetch": "^2.6.11",
|
|
@@ -3,14 +3,14 @@ import classNames from 'classnames';
|
|
|
3
3
|
import { HashLink } from 'myst-to-react';
|
|
4
4
|
import { useState } from 'react';
|
|
5
5
|
|
|
6
|
-
const HIDE_OVER_N_REFERENCES = 5;
|
|
7
|
-
|
|
8
6
|
export function Bibliography({
|
|
9
7
|
containerClassName,
|
|
10
8
|
innerClassName,
|
|
9
|
+
hideLongBibliography = 15,
|
|
11
10
|
}: {
|
|
12
11
|
containerClassName?: string;
|
|
13
12
|
innerClassName?: string;
|
|
13
|
+
hideLongBibliography?: false | number;
|
|
14
14
|
}) {
|
|
15
15
|
const references = useReferences();
|
|
16
16
|
const grid = useGridSystemProvider();
|
|
@@ -18,14 +18,14 @@ export function Bibliography({
|
|
|
18
18
|
const filtered = order?.filter((l) => l);
|
|
19
19
|
const [hidden, setHidden] = useState(true);
|
|
20
20
|
if (!filtered || !data || filtered.length === 0) return null;
|
|
21
|
-
const refs = hidden ? filtered.slice(0,
|
|
21
|
+
const refs = hidden && hideLongBibliography ? filtered.slice(0, hideLongBibliography) : filtered;
|
|
22
22
|
return (
|
|
23
23
|
<section
|
|
24
24
|
id="references"
|
|
25
25
|
className={classNames(grid, 'subgrid-gap col-screen', containerClassName)}
|
|
26
26
|
>
|
|
27
27
|
<div className={innerClassName}>
|
|
28
|
-
{filtered.length >
|
|
28
|
+
{!!hideLongBibliography && filtered.length > hideLongBibliography && (
|
|
29
29
|
<button
|
|
30
30
|
onClick={() => setHidden(!hidden)}
|
|
31
31
|
className="float-right p-1 px-2 text-xs border rounded hover:border-blue-500 dark:hover:border-blue-400"
|
|
@@ -56,7 +56,7 @@ export function Bibliography({
|
|
|
56
56
|
/>
|
|
57
57
|
);
|
|
58
58
|
})}
|
|
59
|
-
{filtered.length >
|
|
59
|
+
{!!hideLongBibliography && filtered.length > hideLongBibliography && (
|
|
60
60
|
<li className="text-center list-none">
|
|
61
61
|
<button
|
|
62
62
|
onClick={() => setHidden(!hidden)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MyST } from 'myst-to-react';
|
|
1
|
+
import { Details, MyST } from 'myst-to-react';
|
|
2
2
|
import { SourceFileKind } from 'myst-spec-ext';
|
|
3
3
|
import type { GenericParent } from 'myst-common';
|
|
4
4
|
import classNames from 'classnames';
|
|
@@ -22,17 +22,18 @@ export function Block({
|
|
|
22
22
|
className?: string;
|
|
23
23
|
}) {
|
|
24
24
|
const grid = useGridSystemProvider();
|
|
25
|
-
const subGrid = `${grid} subgrid-gap col-screen`;
|
|
25
|
+
const subGrid = node.visibility === 'hide' ? '' : `${grid} subgrid-gap col-screen`;
|
|
26
26
|
const dataClassName = typeof node.data?.class === 'string' ? node.data?.class : undefined;
|
|
27
27
|
// Hide the subgrid if either the dataClass or the className exists and includes `col-`
|
|
28
28
|
const noSubGrid =
|
|
29
29
|
(dataClassName && dataClassName.includes('col-')) || (className && className.includes('col-'));
|
|
30
|
-
|
|
30
|
+
const block = (
|
|
31
31
|
<div
|
|
32
32
|
key={`block-${id}`}
|
|
33
33
|
id={id}
|
|
34
34
|
className={classNames('relative group/block', className, dataClassName, {
|
|
35
35
|
[subGrid]: !noSubGrid,
|
|
36
|
+
hidden: node.visibility === 'remove',
|
|
36
37
|
})}
|
|
37
38
|
>
|
|
38
39
|
{pageKind === SourceFileKind.Notebook && isACodeCell(node) && (
|
|
@@ -53,6 +54,10 @@ export function Block({
|
|
|
53
54
|
<MyST ast={node.children} />
|
|
54
55
|
</div>
|
|
55
56
|
);
|
|
57
|
+
if (node.visibility === 'hide') {
|
|
58
|
+
return <Details title="Notebook Cell">{block}</Details>;
|
|
59
|
+
}
|
|
60
|
+
return block;
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
export function ContentBlocks({
|
|
@@ -12,6 +12,7 @@ import type { RefObject } from 'react';
|
|
|
12
12
|
import { DocumentChartBarIcon } from '@heroicons/react/24/outline';
|
|
13
13
|
import { ChevronRightIcon } from '@heroicons/react/24/solid';
|
|
14
14
|
import * as Collapsible from '@radix-ui/react-collapsible';
|
|
15
|
+
import { slugToUrl } from 'myst-common';
|
|
15
16
|
|
|
16
17
|
const SELECTOR = [1, 2, 3, 4].map((n) => `main h${n}`).join(', ');
|
|
17
18
|
|
|
@@ -351,6 +352,7 @@ function useMarginOccluder() {
|
|
|
351
352
|
|
|
352
353
|
export const DocumentOutline = ({
|
|
353
354
|
outlineRef,
|
|
355
|
+
title = 'Contents',
|
|
354
356
|
top = 0,
|
|
355
357
|
className,
|
|
356
358
|
selector = SELECTOR,
|
|
@@ -359,6 +361,7 @@ export const DocumentOutline = ({
|
|
|
359
361
|
isMargin,
|
|
360
362
|
}: {
|
|
361
363
|
outlineRef?: React.RefObject<HTMLElement>;
|
|
364
|
+
title?: React.ReactNode;
|
|
362
365
|
top?: number;
|
|
363
366
|
height?: number;
|
|
364
367
|
className?: string;
|
|
@@ -405,7 +408,7 @@ export const DocumentOutline = ({
|
|
|
405
408
|
}}
|
|
406
409
|
>
|
|
407
410
|
<div className="flex flex-row gap-2 mb-4 text-sm leading-6 uppercase rounded-lg text-slate-900 dark:text-slate-100">
|
|
408
|
-
|
|
411
|
+
{title}
|
|
409
412
|
<Collapsible.Trigger asChild>
|
|
410
413
|
<button className="self-center flex-none rounded-md group hover:bg-slate-300/30 focus:outline outline-blue-200 outline-2">
|
|
411
414
|
<ChevronRightIcon
|
|
@@ -443,7 +446,7 @@ export function SupportingDocuments() {
|
|
|
443
446
|
return (
|
|
444
447
|
<li key={p.slug}>
|
|
445
448
|
<NavLink
|
|
446
|
-
to={withBaseurl(`/${p.slug}#main`, baseurl)}
|
|
449
|
+
to={withBaseurl(`/${slugToUrl(p.slug)}#main`, baseurl)}
|
|
447
450
|
prefetch="intent"
|
|
448
451
|
className={({ isActive }) =>
|
|
449
452
|
classNames('no-underline flex self-center hover:text-blue-700', {
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
createElement,
|
|
3
|
+
useEffect,
|
|
4
|
+
useState,
|
|
5
|
+
useMemo,
|
|
6
|
+
useCallback,
|
|
7
|
+
useRef,
|
|
8
|
+
forwardRef,
|
|
9
|
+
} from 'react';
|
|
2
10
|
import type { KeyboardEventHandler, Dispatch, SetStateAction, FormEvent, MouseEvent } from 'react';
|
|
3
11
|
import { useFetcher } from '@remix-run/react';
|
|
4
12
|
import {
|
|
@@ -42,10 +50,21 @@ function matchAll(text: string, pattern: RegExp) {
|
|
|
42
50
|
* Highlight a text string with an array of match words
|
|
43
51
|
*
|
|
44
52
|
* @param text - text to highlight
|
|
45
|
-
* @param
|
|
46
|
-
* @param limit - limit to the number of
|
|
53
|
+
* @param matches - regular expression patterns to match against
|
|
54
|
+
* @param limit - limit to the number of characters after first match
|
|
55
|
+
* @param classname - CSS classname to use
|
|
47
56
|
*/
|
|
48
|
-
function MarkedText({
|
|
57
|
+
function MarkedText({
|
|
58
|
+
text,
|
|
59
|
+
matches,
|
|
60
|
+
limit,
|
|
61
|
+
className,
|
|
62
|
+
}: {
|
|
63
|
+
text: string;
|
|
64
|
+
matches: string[];
|
|
65
|
+
limit?: number;
|
|
66
|
+
className?: string;
|
|
67
|
+
}) {
|
|
49
68
|
// Split by delimeter, but _keep_ delimeter!
|
|
50
69
|
const splits = matchAll(text, SPACE_OR_PUNCTUATION);
|
|
51
70
|
const tokens: string[] = [];
|
|
@@ -79,23 +98,38 @@ function MarkedText({ text, matches, limit }: { text: string; matches: string[];
|
|
|
79
98
|
lastIndex = tokens.length;
|
|
80
99
|
} else {
|
|
81
100
|
firstIndex = tokens.findIndex((token) => pattern.test(token));
|
|
82
|
-
|
|
101
|
+
let numChars = 0;
|
|
102
|
+
for (
|
|
103
|
+
lastIndex = firstIndex + 1;
|
|
104
|
+
lastIndex < tokens.length - 1 && numChars + tokens[lastIndex].length <= limit;
|
|
105
|
+
lastIndex++
|
|
106
|
+
) {
|
|
107
|
+
numChars += tokens[lastIndex].length;
|
|
108
|
+
}
|
|
83
109
|
}
|
|
84
110
|
|
|
85
111
|
if (tokens.length === 0) {
|
|
86
|
-
return
|
|
112
|
+
return <span className={className}>{...tokens}</span>;
|
|
87
113
|
} else {
|
|
88
114
|
const firstRenderer = renderToken(tokens[firstIndex]);
|
|
89
115
|
const remainingTokens = tokens.slice(firstIndex + 1, lastIndex);
|
|
90
116
|
const remainingRenderers = remainingTokens.map((token) => renderToken(token));
|
|
91
117
|
|
|
92
118
|
return (
|
|
93
|
-
|
|
94
|
-
{
|
|
119
|
+
<span
|
|
120
|
+
className={classNames(
|
|
121
|
+
className,
|
|
122
|
+
{
|
|
123
|
+
"before:content-['..._']": hasLimit,
|
|
124
|
+
"after:content-['_...']": hasLimit,
|
|
125
|
+
},
|
|
126
|
+
'truncate',
|
|
127
|
+
'w-full',
|
|
128
|
+
)}
|
|
129
|
+
>
|
|
95
130
|
{firstRenderer}
|
|
96
131
|
{...remainingRenderers}
|
|
97
|
-
|
|
98
|
-
</>
|
|
132
|
+
</span>
|
|
99
133
|
);
|
|
100
134
|
}
|
|
101
135
|
}
|
|
@@ -178,8 +212,10 @@ function SearchShortcut() {
|
|
|
178
212
|
function SearchResultItem({
|
|
179
213
|
result,
|
|
180
214
|
closeSearch,
|
|
215
|
+
charLimit,
|
|
181
216
|
}: {
|
|
182
217
|
result: RankedSearchResult;
|
|
218
|
+
charLimit?: number;
|
|
183
219
|
closeSearch?: () => void;
|
|
184
220
|
}) {
|
|
185
221
|
const { hierarchy, type, url, queries } = result;
|
|
@@ -187,14 +223,13 @@ function SearchResultItem({
|
|
|
187
223
|
const Link = useLinkProvider();
|
|
188
224
|
|
|
189
225
|
// Render the icon
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
);
|
|
226
|
+
const iconProps = useMemo(() => {
|
|
227
|
+
return { className: 'inline-block w-6 mx-2 shrink-0' };
|
|
228
|
+
}, []);
|
|
229
|
+
const iconRenderer = createElement(
|
|
230
|
+
type === 'lvl1' ? DocumentIcon : type === 'content' ? Bars3BottomLeftIcon : HashtagIcon,
|
|
231
|
+
iconProps,
|
|
232
|
+
);
|
|
198
233
|
|
|
199
234
|
// Generic "this document matched"
|
|
200
235
|
const title = result.type === 'content' ? result['content'] : hierarchy[type as HeadingLevel]!;
|
|
@@ -202,7 +237,12 @@ function SearchResultItem({
|
|
|
202
237
|
|
|
203
238
|
// Render the title, i.e. content or heading
|
|
204
239
|
const titleRenderer = (
|
|
205
|
-
<MarkedText
|
|
240
|
+
<MarkedText
|
|
241
|
+
text={title}
|
|
242
|
+
matches={matches}
|
|
243
|
+
limit={type === 'content' ? charLimit : undefined}
|
|
244
|
+
className="text-sm"
|
|
245
|
+
/>
|
|
206
246
|
);
|
|
207
247
|
|
|
208
248
|
// Render the subtitle i.e. file name
|
|
@@ -211,7 +251,7 @@ function SearchResultItem({
|
|
|
211
251
|
subtitleRenderer = undefined;
|
|
212
252
|
} else {
|
|
213
253
|
const subtitle = result.hierarchy.lvl1!;
|
|
214
|
-
subtitleRenderer = <MarkedText text={subtitle} matches={matches} />;
|
|
254
|
+
subtitleRenderer = <MarkedText text={subtitle} matches={matches} className="text-xs" />;
|
|
215
255
|
}
|
|
216
256
|
|
|
217
257
|
const enterIconRenderer = (
|
|
@@ -227,9 +267,9 @@ function SearchResultItem({
|
|
|
227
267
|
>
|
|
228
268
|
<div className="flex flex-row h-11">
|
|
229
269
|
{iconRenderer}
|
|
230
|
-
<div className="flex flex-col justify-center grow">
|
|
231
|
-
|
|
232
|
-
{subtitleRenderer
|
|
270
|
+
<div className="flex flex-col justify-center truncate grow">
|
|
271
|
+
{titleRenderer}
|
|
272
|
+
{subtitleRenderer}
|
|
233
273
|
</div>
|
|
234
274
|
{enterIconRenderer}
|
|
235
275
|
</div>
|
|
@@ -242,6 +282,7 @@ interface SearchResultsProps {
|
|
|
242
282
|
searchListID: string;
|
|
243
283
|
searchLabelID: string;
|
|
244
284
|
selectedIndex: number;
|
|
285
|
+
charLimit?: number;
|
|
245
286
|
onHoverSelect: (index: number) => void;
|
|
246
287
|
className?: string;
|
|
247
288
|
closeSearch?: () => void;
|
|
@@ -251,6 +292,7 @@ function SearchResults({
|
|
|
251
292
|
searchResults,
|
|
252
293
|
searchListID,
|
|
253
294
|
searchLabelID,
|
|
295
|
+
charLimit,
|
|
254
296
|
className,
|
|
255
297
|
selectedIndex,
|
|
256
298
|
onHoverSelect,
|
|
@@ -325,7 +367,7 @@ function SearchResults({
|
|
|
325
367
|
// Trigger selection on movement, so that scrolling doesn't trigger handler
|
|
326
368
|
onMouseMove={handleMouseMove}
|
|
327
369
|
>
|
|
328
|
-
<SearchResultItem result={result} closeSearch={closeSearch} />
|
|
370
|
+
<SearchResultItem result={result} closeSearch={closeSearch} charLimit={charLimit} />
|
|
329
371
|
</li>
|
|
330
372
|
))}
|
|
331
373
|
</ul>
|
|
@@ -541,11 +583,12 @@ const SearchPlaceholderButton = forwardRef<
|
|
|
541
583
|
|
|
542
584
|
export interface SearchProps {
|
|
543
585
|
debounceTime?: number;
|
|
586
|
+
charLimit?: number;
|
|
544
587
|
}
|
|
545
588
|
/**
|
|
546
589
|
* Component that implements a basic search interface
|
|
547
590
|
*/
|
|
548
|
-
export function Search({ debounceTime = 500 }: SearchProps) {
|
|
591
|
+
export function Search({ debounceTime = 500, charLimit = 64 }: SearchProps) {
|
|
549
592
|
const [open, setOpen] = useState(false);
|
|
550
593
|
const [searchResults, setSearchResults] = useState<RankedSearchResult[] | undefined>();
|
|
551
594
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
@@ -624,6 +667,7 @@ export function Search({ debounceTime = 500 }: SearchProps) {
|
|
|
624
667
|
selectedIndex={selectedIndex}
|
|
625
668
|
onHoverSelect={setSelectedIndex}
|
|
626
669
|
closeSearch={triggerClose}
|
|
670
|
+
charLimit={charLimit}
|
|
627
671
|
/>
|
|
628
672
|
)}
|
|
629
673
|
</Dialog.Content>
|
package/src/pages/Article.tsx
CHANGED
|
@@ -44,6 +44,9 @@ export const ArticlePage = React.memo(function ({
|
|
|
44
44
|
const keywords = article.frontmatter?.keywords ?? [];
|
|
45
45
|
const parts = extractKnownParts(tree, article.frontmatter?.parts);
|
|
46
46
|
|
|
47
|
+
const { thebe } = manifest as any;
|
|
48
|
+
const { location } = article;
|
|
49
|
+
|
|
47
50
|
return (
|
|
48
51
|
<ReferencesProvider
|
|
49
52
|
references={{ ...article.references, article: article.mdast }}
|
|
@@ -55,6 +58,8 @@ export const ArticlePage = React.memo(function ({
|
|
|
55
58
|
<FrontmatterBlock
|
|
56
59
|
kind={article.kind}
|
|
57
60
|
frontmatter={{ ...article.frontmatter, downloads }}
|
|
61
|
+
thebe={thebe}
|
|
62
|
+
location={location}
|
|
58
63
|
className="mb-8 pt-9"
|
|
59
64
|
/>
|
|
60
65
|
)}
|